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