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