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