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