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