Functional settings system. At this point, they control the CCL map only
[pazpar2-moved-to-github.git] / src / database.c
1 /* $Id: database.c,v 1.6 2007-03-30 02:45:07 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 = 0;
131     struct conf_retrievalprofile *retrieval;
132     struct database *db;
133     struct host *host;
134     char hostport[256];
135     char *dbname;
136
137     if (!nmem)
138         nmem = nmem_create();
139     if (doc)
140     {
141         explain = zr_read_xml(nmem, xmlDocGetRootElement(doc));
142         if (!explain)
143             return 0;
144     }
145     if (!(retrieval = database_retrievalprofile(id)))
146     {
147         xmlFree(doc);
148         return 0;
149     }
150     if (strlen(id) > 255)
151         return 0;
152     strcpy(hostport, id);
153     if ((dbname = strchr(hostport, '/')))
154         *(dbname++) = '\0';
155     else
156         dbname = "Default";
157     if (!(host = find_host(hostport)))
158         return 0;
159     db = nmem_malloc(nmem, sizeof(*db));
160     memset(db, 0, sizeof(*db));
161     db->host = host;
162     db->url = nmem_strdup(nmem, id);
163     db->name = 0;
164     db->databases = xmalloc(2 * sizeof(char *));
165     db->databases[0] = nmem_strdup(nmem, dbname);
166     db->databases[1] = 0;
167     db->errors = 0;
168     db->explain = explain;
169     db->rprofile = retrieval;
170     db->settings = 0;
171     db->next = databases;
172     db->ccl_map = 0;
173     databases = db;
174
175     return db;
176 }
177
178 // Return a database structure by ID. Load and add to list if necessary
179 // new==1 just means we know it's not in the list
180 struct database *find_database(const char *id, int new)
181 {
182     struct database *p;
183     if (!new)
184     {
185         for (p = databases; p; p = p->next)
186             if (!strcmp(p->url, id))
187                 return p;
188     }
189     return load_database(id);
190 }
191
192 static int match_zurl(const char *zurl, const char *pattern)
193 {
194     if (!strcmp(pattern, "*"))
195         return 1;
196     else if (!strncmp(pattern, "*/", 2))
197     {
198         char *db = strchr(zurl, '/');
199         if (!db)
200             return 0;
201         if (!strcmp(pattern + 2, db))
202             return 1;
203         else
204             return 0;
205     }
206     else if (!strcmp(pattern, zurl))
207         return 1;
208     else
209         return 0;
210 }
211
212 // This will be generalized at some point
213 static int match_criterion(struct database *db, struct database_criterion *c)
214 {
215     if (!strcmp(c->name, "id"))
216     {
217         struct database_criterion_value *v;
218         for (v = c->values; v; v = v->next)
219             if (match_zurl(db->url, v->value))
220                 return 1;
221         return 0;
222     }
223     else
224         return 0;
225 }
226
227 int database_match_criteria(struct database *db, struct database_criterion *cl)
228 {
229     for (; cl; cl = cl->next)
230         if (!match_criterion(db, cl))
231             break;
232     if (cl) // one of the criteria failed to match -- skip this db
233         return 0;
234     else
235         return 1;
236 }
237
238 // Cycles through databases, calling a handler function on the ones for
239 // which all criteria matched.
240 int grep_databases(void *context, struct database_criterion *cl,
241         void (*fun)(void *context, struct database *db))
242 {
243     struct database *p;
244     int i;
245
246     for (p = databases; p; p = p->next)
247     {
248         if (database_match_criteria(p, cl))
249         {
250             (*fun)(context, p);
251             i++;
252         }
253     }
254     return i;
255 }
256
257 // This function will most likely vanish when a proper target profile mechanism is
258 // introduced.
259 void load_simpletargets(const char *fn)
260 {
261     FILE *f = fopen(fn, "r");
262     char line[256];
263
264     if (!f)
265     {
266         yaz_log(YLOG_WARN|YLOG_ERRNO, "open %s", fn);
267         exit(1);
268     }
269
270     while (fgets(line, 255, f))
271     {
272         char *url;
273         char *name;
274         struct database *db;
275
276         if (strncmp(line, "target ", 7))
277             continue;
278         line[strlen(line) - 1] = '\0';
279
280         if ((name = strchr(line, ';')))
281             *(name++) = '\0';
282
283         url = line + 7;
284
285         if (!(db = find_database(url, 0)))
286             yaz_log(YLOG_WARN, "Unable to load database %s", url);
287         if (name && db)
288             db->name = nmem_strdup(nmem, name);
289     }
290     fclose(f);
291 }
292
293
294 /*
295  * Local variables:
296  * c-basic-offset: 4
297  * indent-tabs-mode: nil
298  * End:
299  * vim: shiftwidth=4 tabstop=8 expandtab
300  */