bc5c723dfd9ff5f0486e1f474f9b5a5dca108cfb
[pazpar2-moved-to-github.git] / src / database.c
1 /* $Id: database.c,v 1.30 2008-02-16 04:28:55 quinn Exp $
2    Copyright (c) 2006-2007, Index Data.
3
4 This file is part of Pazpar2.
5
6 Pazpar2 is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2, or (at your option) any later
9 version.
10
11 Pazpar2 is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Pazpar2; see the file LICENSE.  If not, write to the
18 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19 02111-1307, USA.
20  */
21
22 #include <libxml/parser.h>
23 #include <libxml/tree.h>
24 #include <libxslt/xslt.h>
25 #include <libxslt/transform.h>
26 #include <libxslt/xsltutils.h>
27 #include <assert.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30
31 #include "pazpar2.h"
32 #include "host.h"
33 #include "config.h"
34 #include "settings.h"
35 #include "http.h"
36 #include "zeerex.h"
37
38 #include <sys/types.h>
39 #include <sys/socket.h>
40 #include <netdb.h>
41 #include <netinet/in.h>
42
43 static struct host *hosts = 0;  // The hosts we know about 
44 static struct database *databases = 0; // The databases we know about
45 static NMEM nmem = 0;
46
47 static xmlDoc *get_explain_xml(const char *id)
48 {
49     struct stat st;
50     char *dir;
51     char path[256];
52     char ide[256];
53     if (!config || !config->targetprofiles)
54     {
55         yaz_log(YLOG_WARN, "Config must be loaded and specify targetprofiles");
56         return 0;
57     }
58     if (config->targetprofiles->type != Targetprofiles_local)
59     {
60         yaz_log(YLOG_FATAL, "Only supports local type");
61         return 0;
62     }
63     dir = config->targetprofiles->src;
64     urlencode(id, ide);
65     sprintf(path, "%s/%s", dir, ide);
66     if (!stat(path, &st))
67         return xmlParseFile(path);
68     else
69         return 0;
70 }
71
72 // Create a new host structure for hostport
73 static struct host *create_host(const char *hostport)
74 {
75     struct host *host;
76
77     host = xmalloc(sizeof(struct host));
78     host->hostport = xstrdup(hostport);
79     host->connections = 0;
80     host->ipport = 0;
81
82     if (host_getaddrinfo(host))
83     {
84         xfree(host->hostport);
85         xfree(host);
86         return 0;
87     }
88     host->next = hosts;
89     hosts = host;
90     return host;
91 }
92
93 static struct host *find_host(const char *hostport)
94 {
95     struct host *p;
96     for (p = hosts; p; p = p->next)
97         if (!strcmp(p->hostport, hostport))
98             return p;
99     return create_host(hostport);
100 }
101
102 static struct database *load_database(const char *id)
103 {
104     xmlDoc *doc = 0;
105     struct zr_explain *explain = 0;
106     struct database *db;
107     struct host *host;
108     char hostport[256];
109     char *dbname;
110     struct setting *idset;
111
112     yaz_log(YLOG_LOG, "New database: %s", id);
113     if (!nmem)
114         nmem = nmem_create();
115
116     if (config && config->targetprofiles 
117         && (doc = get_explain_xml(id)))
118     {
119         explain = zr_read_xml(nmem, xmlDocGetRootElement(doc));
120         if (!explain)
121             return 0;
122     }
123
124     if (strlen(id) > 255)
125         return 0;
126     strcpy(hostport, id);
127     if ((dbname = strchr(hostport, '/')))
128         *(dbname++) = '\0';
129     else
130         dbname = "Default";
131     if (!(host = find_host(hostport)))
132         return 0;
133     db = nmem_malloc(nmem, sizeof(*db));
134     memset(db, 0, sizeof(*db));
135     db->host = host;
136     db->url = nmem_strdup(nmem, id);
137     db->databases = xmalloc(2 * sizeof(char *));
138     db->databases[0] = nmem_strdup(nmem, dbname);
139     db->databases[1] = 0;
140     db->errors = 0;
141     db->explain = explain;
142
143     db->settings = 0;
144
145     db->settings = nmem_malloc(nmem, sizeof(struct settings*) * settings_num());
146     memset(db->settings, 0, sizeof(struct settings*) * settings_num());
147     idset = nmem_malloc(nmem, sizeof(*idset));
148     idset->precedence = 0;
149     idset->name = "pz:id";
150     idset->target = idset->value = db->url;
151     idset->next = 0;
152     db->settings[PZ_ID] = idset;
153
154     db->next = databases;
155     databases = db;
156
157     return db;
158 }
159
160 // Return a database structure by ID. Load and add to list if necessary
161 // new==1 just means we know it's not in the list
162 struct database *find_database(const char *id, int new)
163 {
164     struct database *p;
165     if (!new)
166     {
167         for (p = databases; p; p = p->next)
168             if (!strcmp(p->url, id))
169                 return p;
170     }
171     return load_database(id);
172 }
173
174 // This whole session_grep database thing should be moved elsewhere
175
176 int match_zurl(const char *zurl, const char *pattern)
177 {
178     int len;
179
180     if (!strcmp(pattern, "*"))
181         return 1;
182     else if (!strncmp(pattern, "*/", 2))   // host wildcard.. what the heck is that for?
183     {
184         char *db = strchr(zurl, '/');
185         if (!db)
186             return 0;
187         if (!strcmp(pattern + 2, db))
188             return 1;
189         else
190             return 0;
191     }
192     else if (*(pattern + (len = strlen(pattern) - 1)) == '*')  // db wildcard
193     {
194         if (!strncmp(pattern, zurl, len))
195             return 1;
196         else
197             return 2;
198     }
199     else if (!strcmp(pattern, zurl))
200         return 1;
201     else
202         return 0;
203 }
204
205 // This will be generalized at some point
206 static int match_criterion(struct setting **settings, struct database_criterion *c)
207 {
208     int offset = settings_offset(c->name);
209     struct database_criterion_value *v;
210
211     if (offset < 0)
212     {
213         yaz_log(YLOG_WARN, "Criterion not found: %s", c->name);
214         return 0;
215     }
216     if (!settings[offset])
217         return 0;
218     for (v = c->values; v; v = v->next)
219     {
220         if (offset == PZ_ID)
221         {
222             if (match_zurl(settings[offset]->value, v->value))
223                 break;
224         }
225         else 
226         {
227             if (!strcmp(settings[offset]->value, v->value))
228                 break;
229         }
230     }
231     if (v)
232         return 1;
233     else
234         return 0;
235 }
236
237 int database_match_criteria(struct setting **settings, struct database_criterion *cl)
238 {
239     for (; cl; cl = cl->next)
240         if (!match_criterion(settings, cl))
241             break;
242     if (cl) // one of the criteria failed to match -- skip this db
243         return 0;
244     else
245         return 1;
246 }
247
248 // Cycles through databases, calling a handler function on the ones for
249 // which all criteria matched.
250 int session_grep_databases(struct session *se, struct database_criterion *cl,
251         void (*fun)(void *context, struct session_database *db))
252 {
253     struct session_database *p;
254     int i = 0;
255
256     for (p = se->databases; p; p = p->next)
257     {
258         if (p->settings && p->settings[PZ_ALLOW] && *p->settings[PZ_ALLOW]->value == '0')
259             continue;
260         if (!p->settings[PZ_NAME])
261             continue;
262         if (database_match_criteria(p->settings, cl))
263         {
264             (*fun)(se, p);
265             i++;
266         }
267     }
268     return i;
269 }
270
271 int predef_grep_databases(void *context, struct database_criterion *cl,
272                           void (*fun)(void *context, struct database *db))
273 {
274     struct database *p;
275     int i = 0;
276
277     for (p = databases; p; p = p->next)
278         if (database_match_criteria(p->settings, cl))
279         {
280             (*fun)(context, p);
281             i++;
282         }
283     return i;
284 }
285
286 /*
287  * Local variables:
288  * c-basic-offset: 4
289  * indent-tabs-mode: nil
290  * End:
291  * vim: shiftwidth=4 tabstop=8 expandtab
292  */