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