Added settings
[pazpar2-moved-to-github.git] / src / database.c
1 /* $Id: database.c,v 1.5 2007-03-29 13:44:38 quinn Exp $ */
2
3 #include <libxml/parser.h>
4 #include <libxml/tree.h>
5 #include <libxslt/xslt.h>
6 #include <libxslt/transform.h>
7 #include <libxslt/xsltutils.h>
8 #include <assert.h>
9 #include <sys/types.h>
10 #include <sys/stat.h>
11
12 #include "pazpar2.h"
13 #include "config.h"
14 #include "http.h"
15 #include "zeerex.h"
16
17 #include <sys/types.h>
18 #include <sys/socket.h>
19 #include <netdb.h>
20 #include <netinet/in.h>
21
22 static struct host *hosts = 0;  // The hosts we know about 
23 static struct database *databases = 0; // The databases we know about
24 static NMEM nmem = 0;
25
26 // This needs to be extended with selection criteria
27 static struct conf_retrievalprofile *database_retrievalprofile(const char *id)
28 {
29     if (!config)
30     {
31         yaz_log(YLOG_FATAL, "Must load configuration (-f)");
32         exit(1);
33     }
34     if (!config->retrievalprofiles)
35     {
36         yaz_log(YLOG_FATAL, "No retrieval profiles defined");
37     }
38     return config->retrievalprofiles;
39 }
40
41 static struct conf_queryprofile *database_queryprofile(const char *id)
42 {
43     return (struct conf_queryprofile*) 1;
44 }
45
46 static xmlDoc *get_explain_xml(const char *id)
47 {
48     struct stat st;
49     char *dir;
50     char path[256];
51     char ide[256];
52     if (!config || !config->targetprofiles)
53     {
54         yaz_log(YLOG_WARN, "Config must be loaded and specify targetprofiles");
55         return 0;
56     }
57     if (config->targetprofiles->type != Targetprofiles_local)
58     {
59         yaz_log(YLOG_FATAL, "Only supports local type");
60         return 0;
61     }
62     dir = config->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 addrinfo *addrinfo, hints;
75     struct host *host;
76     char *port;
77     char ipport[128];
78     unsigned char addrbuf[4];
79     int res;
80
81     host = xmalloc(sizeof(struct host));
82     host->hostport = xstrdup(hostport);
83     host->connections = 0;
84
85     if ((port = strchr(hostport, ':')))
86         *(port++) = '\0';
87     else
88         port = "210";
89
90     hints.ai_flags = 0;
91     hints.ai_family = PF_INET;
92     hints.ai_socktype = SOCK_STREAM;
93     hints.ai_protocol = IPPROTO_TCP;
94     hints.ai_addrlen = 0;
95     hints.ai_addr = 0;
96     hints.ai_canonname = 0;
97     hints.ai_next = 0;
98     // This is not robust code. It assumes that getaddrinfo always
99     // returns AF_INET address.
100     if ((res = getaddrinfo(hostport, port, &hints, &addrinfo)))
101     {
102         yaz_log(YLOG_WARN, "Failed to resolve %s: %s", hostport, gai_strerror(res));
103         xfree(host->hostport);
104         xfree(host);
105         return 0;
106     }
107     assert(addrinfo->ai_family == PF_INET);
108     memcpy(addrbuf, &((struct sockaddr_in*)addrinfo->ai_addr)->sin_addr.s_addr, 4);
109     sprintf(ipport, "%u.%u.%u.%u:%s",
110             addrbuf[0], addrbuf[1], addrbuf[2], addrbuf[3], port);
111     host->ipport = xstrdup(ipport);
112     freeaddrinfo(addrinfo);
113     host->next = hosts;
114     hosts = host;
115     return host;
116 }
117
118 static struct host *find_host(const char *hostport)
119 {
120     struct host *p;
121     for (p = hosts; p; p = p->next)
122         if (!strcmp(p->hostport, hostport))
123             return p;
124     return create_host(hostport);
125 }
126
127 static struct database *load_database(const char *id)
128 {
129     xmlDoc *doc = get_explain_xml(id);
130     struct zr_explain *explain;
131     struct conf_retrievalprofile *retrieval;
132     struct conf_queryprofile *query;
133     struct database *db;
134     struct host *host;
135     char hostport[256];
136     char *dbname;
137
138     if (!nmem)
139         nmem = nmem_create();
140     if (doc)
141     {
142         explain = zr_read_xml(nmem, xmlDocGetRootElement(doc));
143         if (!explain)
144             return 0;
145     }
146     if (!(retrieval = database_retrievalprofile(id)) ||
147             !(query = database_queryprofile(id)))
148     {
149         xmlFree(doc);
150         return 0;
151     }
152     if (strlen(id) > 255)
153         return 0;
154     strcpy(hostport, id);
155     if ((dbname = strchr(hostport, '/')))
156         *(dbname++) = '\0';
157     else
158         dbname = "Default";
159     if (!(host = find_host(hostport)))
160         return 0;
161     db = nmem_malloc(nmem, sizeof(*db));
162     memset(db, 0, sizeof(*db));
163     db->host = host;
164     db->url = nmem_strdup(nmem, id);
165     db->name = 0;
166     db->databases = xmalloc(2 * sizeof(char *));
167     db->databases[0] = nmem_strdup(nmem, dbname);
168     db->databases[1] = 0;
169     db->errors = 0;
170     db->explain = explain;
171     db->qprofile = query;
172     db->rprofile = retrieval;
173     db->settings = 0;
174     db->next = databases;
175     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 {
184     struct database *p;
185     if (!new)
186     {
187         for (p = databases; p; p = p->next)
188             if (!strcmp(p->url, id))
189                 return p;
190     }
191     return load_database(id);
192 }
193
194 static int match_zurl(const char *zurl, const char *pattern)
195 {
196     if (!strcmp(pattern, "*"))
197         return 1;
198     else if (!strncmp(pattern, "*/", 2))
199     {
200         char *db = strchr(zurl, '/');
201         if (!db)
202             return 0;
203         if (!strcmp(pattern + 2, db))
204             return 1;
205         else
206             return 0;
207     }
208     else if (!strcmp(pattern, zurl))
209         return 1;
210     else
211         return 0;
212 }
213
214 // This will be generalized at some point
215 static int match_criterion(struct database *db, struct database_criterion *c)
216 {
217     if (!strcmp(c->name, "id"))
218     {
219         struct database_criterion_value *v;
220         for (v = c->values; v; v = v->next)
221             if (match_zurl(db->url, v->value))
222                 return 1;
223         return 0;
224     }
225     else
226         return 0;
227 }
228
229 int database_match_criteria(struct database *db, struct database_criterion *cl)
230 {
231     for (; cl; cl = cl->next)
232         if (!match_criterion(db, cl))
233             break;
234     if (cl) // one of the criteria failed to match -- skip this db
235         return 0;
236     else
237         return 1;
238 }
239
240 // Cycles through databases, calling a handler function on the ones for
241 // which all criteria matched.
242 int grep_databases(void *context, struct database_criterion *cl,
243         void (*fun)(void *context, struct database *db))
244 {
245     struct database *p;
246     int i;
247
248     for (p = databases; p; p = p->next)
249     {
250         if (database_match_criteria(p, cl))
251         {
252             (*fun)(context, p);
253             i++;
254         }
255     }
256     return i;
257 }
258
259 // This function will most likely vanish when a proper target profile mechanism is
260 // introduced.
261 void load_simpletargets(const char *fn)
262 {
263     FILE *f = fopen(fn, "r");
264     char line[256];
265
266     if (!f)
267     {
268         yaz_log(YLOG_WARN|YLOG_ERRNO, "open %s", fn);
269         exit(1);
270     }
271
272     while (fgets(line, 255, f))
273     {
274         char *url;
275         char *name;
276         struct database *db;
277
278         if (strncmp(line, "target ", 7))
279             continue;
280         line[strlen(line) - 1] = '\0';
281
282         if ((name = strchr(line, ';')))
283             *(name++) = '\0';
284
285         url = line + 7;
286
287         if (!(db = find_database(url, 0)))
288             yaz_log(YLOG_WARN, "Unable to load database %s", url);
289         if (name && db)
290             db->name = nmem_strdup(nmem, name);
291     }
292     fclose(f);
293 }
294
295
296 /*
297  * Local variables:
298  * c-basic-offset: 4
299  * indent-tabs-mode: nil
300  * End:
301  * vim: shiftwidth=4 tabstop=8 expandtab
302  */