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