Pazpar2 compiles on Windows. But does not yet work
[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 #if HAVE_SYS_SOCKET_H
37 #include <sys/socket.h>
38 #endif
39 #if HAVE_NETDB_H
40 #include <netdb.h>
41 #endif
42 #if HAVE_NETINET_IN_H
43 #include <netinet/in.h>
44 #endif
45
46 static struct host *hosts = 0;  // The hosts we know about 
47 static struct database *databases = 0; // The databases we know about
48 static NMEM nmem = 0;
49
50 static xmlDoc *get_explain_xml(const char *id)
51 {
52     struct stat st;
53     char *dir;
54     char path[256];
55     char ide[256];
56     if (!config || !config->targetprofiles)
57     {
58         yaz_log(YLOG_WARN, "Config must be loaded and specify targetprofiles");
59         return 0;
60     }
61     if (config->targetprofiles->type != Targetprofiles_local)
62     {
63         yaz_log(YLOG_FATAL, "Only supports local type");
64         return 0;
65     }
66     dir = config->targetprofiles->src;
67     urlencode(id, ide);
68     sprintf(path, "%s/%s", dir, ide);
69     if (!stat(path, &st))
70         return xmlParseFile(path);
71     else
72         return 0;
73 }
74
75 // Create a new host structure for hostport
76 static struct host *create_host(const char *hostport)
77 {
78     struct host *host;
79
80     host = xmalloc(sizeof(struct host));
81     host->hostport = xstrdup(hostport);
82     host->connections = 0;
83     host->ipport = 0;
84
85     if (host_getaddrinfo(host))
86     {
87         xfree(host->hostport);
88         xfree(host);
89         return 0;
90     }
91     host->next = hosts;
92     hosts = host;
93     return host;
94 }
95
96 static struct host *find_host(const char *hostport)
97 {
98     struct host *p;
99     for (p = hosts; p; p = p->next)
100         if (!strcmp(p->hostport, hostport))
101             return p;
102     return create_host(hostport);
103 }
104
105 static struct database *load_database(const char *id)
106 {
107     xmlDoc *doc = 0;
108     struct zr_explain *explain = 0;
109     struct database *db;
110     struct host *host;
111     char hostport[256];
112     char *dbname;
113     struct setting *idset;
114
115     yaz_log(YLOG_LOG, "New database: %s", id);
116     if (!nmem)
117         nmem = nmem_create();
118
119     if (config && config->targetprofiles 
120         && (doc = get_explain_xml(id)))
121     {
122         explain = zr_read_xml(nmem, xmlDocGetRootElement(doc));
123         if (!explain)
124             return 0;
125     }
126
127     if (strlen(id) > 255)
128         return 0;
129     strcpy(hostport, id);
130     if ((dbname = strchr(hostport, '/')))
131         *(dbname++) = '\0';
132     else
133         dbname = "Default";
134     if (!(host = find_host(hostport)))
135         return 0;
136     db = nmem_malloc(nmem, sizeof(*db));
137     memset(db, 0, sizeof(*db));
138     db->host = host;
139     db->url = nmem_strdup(nmem, id);
140     db->databases = xmalloc(2 * sizeof(char *));
141     db->databases[0] = nmem_strdup(nmem, dbname);
142     db->databases[1] = 0;
143     db->errors = 0;
144     db->explain = explain;
145
146     db->settings = 0;
147
148     db->settings = nmem_malloc(nmem, sizeof(struct settings*) * settings_num());
149     memset(db->settings, 0, sizeof(struct settings*) * settings_num());
150     idset = nmem_malloc(nmem, sizeof(*idset));
151     idset->precedence = 0;
152     idset->name = "pz:id";
153     idset->target = idset->value = db->url;
154     idset->next = 0;
155     db->settings[PZ_ID] = idset;
156
157     db->next = databases;
158     databases = db;
159
160     return db;
161 }
162
163 // Return a database structure by ID. Load and add to list if necessary
164 // new==1 just means we know it's not in the list
165 struct database *find_database(const char *id, int new)
166 {
167     struct database *p;
168     if (!new)
169     {
170         for (p = databases; p; p = p->next)
171             if (!strcmp(p->url, id))
172                 return p;
173     }
174     return load_database(id);
175 }
176
177 // This whole session_grep database thing should be moved elsewhere
178
179 int match_zurl(const char *zurl, const char *pattern)
180 {
181     int len;
182
183     if (!strcmp(pattern, "*"))
184         return 1;
185     else if (!strncmp(pattern, "*/", 2))   // host wildcard.. what the heck is that for?
186     {
187         char *db = strchr(zurl, '/');
188         if (!db)
189             return 0;
190         if (!strcmp(pattern + 2, db))
191             return 1;
192         else
193             return 0;
194     }
195     else if (*(pattern + (len = strlen(pattern) - 1)) == '*')  // db wildcard
196     {
197         if (!strncmp(pattern, zurl, len))
198             return 1;
199         else
200             return 2;
201     }
202     else if (!strcmp(pattern, zurl))
203         return 1;
204     else
205         return 0;
206 }
207
208 // This will be generalized at some point
209 static int match_criterion(struct setting **settings, struct database_criterion *c)
210 {
211     int offset = settings_offset(c->name);
212     struct database_criterion_value *v;
213
214     if (offset < 0)
215     {
216         yaz_log(YLOG_WARN, "Criterion not found: %s", c->name);
217         return 0;
218     }
219     if (!settings[offset])
220         return 0;
221     for (v = c->values; v; v = v->next)
222     {
223         if (offset == PZ_ID)
224         {
225             if (match_zurl(settings[offset]->value, v->value))
226                 break;
227         }
228         else 
229         {
230             if (!strcmp(settings[offset]->value, v->value))
231                 break;
232         }
233     }
234     if (v)
235         return 1;
236     else
237         return 0;
238 }
239
240 int database_match_criteria(struct setting **settings, struct database_criterion *cl)
241 {
242     for (; cl; cl = cl->next)
243         if (!match_criterion(settings, cl))
244             break;
245     if (cl) // one of the criteria failed to match -- skip this db
246         return 0;
247     else
248         return 1;
249 }
250
251 // Cycles through databases, calling a handler function on the ones for
252 // which all criteria matched.
253 int session_grep_databases(struct session *se, struct database_criterion *cl,
254         void (*fun)(void *context, struct session_database *db))
255 {
256     struct session_database *p;
257     int i = 0;
258
259     for (p = se->databases; p; p = p->next)
260     {
261         if (p->settings && p->settings[PZ_ALLOW] && *p->settings[PZ_ALLOW]->value == '0')
262             continue;
263         if (!p->settings[PZ_NAME])
264             continue;
265         if (database_match_criteria(p->settings, cl))
266         {
267             (*fun)(se, p);
268             i++;
269         }
270     }
271     return i;
272 }
273
274 int predef_grep_databases(void *context, struct database_criterion *cl,
275                           void (*fun)(void *context, struct database *db))
276 {
277     struct database *p;
278     int i = 0;
279
280     for (p = databases; p; p = p->next)
281         if (database_match_criteria(p->settings, cl))
282         {
283             (*fun)(context, p);
284             i++;
285         }
286     return i;
287 }
288
289 /*
290  * Local variables:
291  * c-basic-offset: 4
292  * indent-tabs-mode: nil
293  * End:
294  * vim: shiftwidth=4 tabstop=8 expandtab
295  */