Added retrieval handling support in Generic Frontend Server to support
[yaz-moved-to-github.git] / src / retrieval.c
1 /*
2  * Copyright (C) 2005-2006, Index Data ApS
3  * See the file LICENSE for details.
4  *
5  * $Id: retrieval.c,v 1.3 2006-05-07 14:48:25 adam Exp $
6  */
7 /**
8  * \file retrieval.c
9  * \brief Retrieval utility
10  */
11
12 #if HAVE_CONFIG_H
13 #include <config.h>
14 #endif
15
16 #include <string.h>
17 #include <yaz/retrieval.h>
18 #include <yaz/wrbuf.h>
19 #include <yaz/xmalloc.h>
20 #include <yaz/nmem.h>
21 #include <yaz/tpath.h>
22 #include <yaz/proto.h>
23
24 #if HAVE_XSLT
25 #include <libxml/parser.h>
26 #include <libxml/tree.h>
27 #include <libxml/xinclude.h>
28 #include <libxslt/xsltutils.h>
29 #include <libxslt/transform.h>
30
31 /** \brief The internal structure for yaz_retrieval_t */
32 struct yaz_retrieval_struct {
33     /** \brief ODR memory for configuration */
34     ODR odr;
35
36     /** \brief odr's NMEM memory (odr->mem) */
37     NMEM nmem;
38
39     /** \brief string buffer for error messages */
40     WRBUF wr_error;
41
42     /** \brief path for opening files  */
43     char *path;
44
45     /** \brief retrieval list */
46     struct yaz_retrieval_elem *list;
47
48     /** \brief last pointer in retrieval list */
49     struct yaz_retrieval_elem **list_p;
50 };
51
52 /** \brief information per 'retrieval' construct */
53 struct yaz_retrieval_elem {
54     /** \brief schema identifier */
55     const char *identifier;
56     /** \brief schema short-hand (such sa "dc") */
57     const char *schema;
58     /** \brief record syntax */
59     int *syntax;
60     /** \brief backend schema */
61     const char *backend_schema;
62     /** \brief backend syntax */
63     int *backend_syntax;
64
65     /** \brief record conversion */
66     yaz_record_conv_t record_conv;
67
68     /** \brief next element in list */
69     struct yaz_retrieval_elem *next;
70 };
71
72 static void yaz_retrieval_reset(yaz_retrieval_t p);
73
74 yaz_retrieval_t yaz_retrieval_create()
75 {
76     yaz_retrieval_t p = xmalloc(sizeof(*p));
77     p->odr = odr_createmem(ODR_ENCODE);
78     p->nmem = p->odr->mem;
79     p->wr_error = wrbuf_alloc();
80     p->list = 0;
81     p->path = 0;
82     yaz_retrieval_reset(p);
83     return p;
84 }
85
86 void yaz_retrieval_destroy(yaz_retrieval_t p)
87 {
88     if (p)
89     {
90         yaz_retrieval_reset(p);
91         odr_destroy(p->odr);
92         wrbuf_free(p->wr_error, 1);
93         xfree(p->path);
94         xfree(p);
95     }
96 }
97
98 void yaz_retrieval_reset(yaz_retrieval_t p)
99 {
100     struct yaz_retrieval_elem *el = p->list;
101     for(; el; el = el->next)
102         yaz_record_conv_destroy(el->record_conv);
103
104     wrbuf_rewind(p->wr_error);
105     odr_reset(p->odr);
106
107     p->list = 0;
108     p->list_p = &p->list;
109 }
110
111 /** \brief parse retrieval XML config */
112 static int conf_retrieval(yaz_retrieval_t p, const xmlNode *ptr)
113 {
114
115     struct _xmlAttr *attr;
116     struct yaz_retrieval_elem *el = nmem_malloc(p->nmem, sizeof(*el));
117
118     el->syntax = 0;
119     el->identifier = 0;
120     el->schema = 0;
121     el->backend_schema = 0;
122     el->backend_syntax = 0;
123
124     el->next = 0;
125
126     for (attr = ptr->properties; attr; attr = attr->next)
127     {
128         if (!xmlStrcmp(attr->name, BAD_CAST "syntax") &&
129             attr->children && attr->children->type == XML_TEXT_NODE)
130         {
131             el->syntax = yaz_str_to_z3950oid(
132                 p->odr, CLASS_RECSYN,
133                 (const char *) attr->children->content);
134             if (!el->syntax)
135             {
136                 wrbuf_printf(p->wr_error, "Bad syntax '%s'",
137                              (const char *) attr->children->content);
138                 return -1;
139             }
140         }
141         else if (!xmlStrcmp(attr->name, BAD_CAST "identifier") &&
142             attr->children && attr->children->type == XML_TEXT_NODE)
143             el->identifier =
144                 nmem_strdup(p->nmem, (const char *) attr->children->content);
145         else if (!xmlStrcmp(attr->name, BAD_CAST "schema") &&
146                  attr->children && attr->children->type == XML_TEXT_NODE)
147             el->schema = 
148                 nmem_strdup(p->nmem, (const char *) attr->children->content);
149         else if (!xmlStrcmp(attr->name, BAD_CAST "backendschema") &&
150                  attr->children && attr->children->type == XML_TEXT_NODE)
151             el->backend_schema = 
152                 nmem_strdup(p->nmem, (const char *) attr->children->content);
153         else if (!xmlStrcmp(attr->name, BAD_CAST "backendsyntax") &&
154                  attr->children && attr->children->type == XML_TEXT_NODE)
155         {
156             el->backend_syntax = yaz_str_to_z3950oid(
157                 p->odr, CLASS_RECSYN,
158                 (const char *) attr->children->content);
159             if (!el->backend_syntax)
160             {
161                 wrbuf_printf(p->wr_error, "Bad backendsyntax '%s'",
162                              (const char *) attr->children->content);
163                 return -1;
164             }
165         }
166         else
167         {
168             wrbuf_printf(p->wr_error, "Bad attribute '%s'.", attr->name);
169             return -1;
170         }
171     }
172     if (!el->syntax)
173     {
174         wrbuf_printf(p->wr_error, "Missing 'syntax' attribute.", attr->name);
175         return -1;
176     }
177
178     el->record_conv = 0; /* OK to have no 'convert' sub content */
179     for (ptr = ptr->children; ptr; ptr = ptr->next)
180     {
181         if (ptr->type == XML_ELEMENT_NODE)
182         {
183             el->record_conv = yaz_record_conv_create();
184             
185             yaz_record_conv_set_path(el->record_conv, p->path);
186         
187             if (yaz_record_conv_configure(el->record_conv, ptr))
188             {
189                 wrbuf_printf(p->wr_error, "%s",
190                              yaz_record_conv_get_error(el->record_conv));
191                 yaz_record_conv_destroy(el->record_conv);
192                 return -1;
193             }
194         }
195     }
196     
197     *p->list_p = el;
198     p->list_p = &el->next;
199     return 0;
200 }
201
202 int yaz_retrieval_configure(yaz_retrieval_t p, const void *ptr_v)
203 {
204     const xmlNode *ptr = ptr_v; 
205
206     yaz_retrieval_reset(p);
207
208     if (ptr && ptr->type == XML_ELEMENT_NODE &&
209         !strcmp((const char *) ptr->name, "retrievalinfo"))
210     {
211         for (ptr = ptr->children; ptr; ptr = ptr->next)
212         {
213             if (ptr->type != XML_ELEMENT_NODE)
214                 continue;
215             if (!strcmp((const char *) ptr->name, "retrieval"))
216             {
217                 if (conf_retrieval(p, ptr))
218                     return -1;
219             }
220             else
221             {
222                 wrbuf_printf(p->wr_error, "Bad element '%s'."
223                              " Expected 'retrieval'", ptr->name);
224                 return -1;
225             }
226         }
227     }
228     else
229     {
230         wrbuf_printf(p->wr_error, "Missing 'retrievalinfo' element");
231         return -1;
232     }
233     return 0;
234 }
235
236 int yaz_retrieval_request(yaz_retrieval_t p,
237                           const char *schema, int *syntax,
238                           const char **match_schema, int **match_syntax,
239                           yaz_record_conv_t *rc,
240                           const char **backend_schema,
241                           int **backend_syntax)
242 {
243     struct yaz_retrieval_elem *el = p->list;
244
245     int syntax_matches = 0;
246     int schema_matches = 0;
247     for(; el; el = el->next)
248     {
249         int schema_ok = 0;
250         int syntax_ok = 0;
251
252         if (schema && el->schema && !strcmp(schema, el->schema))
253             schema_ok = 1;
254         if (schema && el->identifier && !strcmp(schema, el->identifier))
255             schema_ok = 1;
256         if (!schema)
257             schema_ok = 1;
258         
259         if (syntax && el->syntax && !oid_oidcmp(syntax, el->syntax))
260             syntax_ok = 1;
261         if (!syntax)
262             syntax_ok = 1;
263
264         if (syntax_ok)
265             syntax_matches++;
266         if (schema_ok)
267             schema_matches++;
268         if (syntax_ok && schema_ok)
269         {
270             *match_syntax = el->syntax;
271             *match_schema = el->schema;
272             if (backend_schema)
273                 *backend_schema = el->backend_schema;
274             if (backend_syntax)
275                 *backend_syntax = el->backend_syntax;
276             if (rc)
277                 *rc = el->record_conv;
278             return 0;
279         }
280     }
281     if (syntax_matches && !schema_matches)
282         return 1;
283     if (!syntax_matches && schema_matches)
284         return 2;
285     if (!syntax_matches && !schema_matches)
286         return 3;
287     return 4;
288 }
289
290 const char *yaz_retrieval_get_error(yaz_retrieval_t p)
291 {
292     return wrbuf_buf(p->wr_error);
293 }
294
295 void yaz_retrieval_set_path(yaz_retrieval_t p, const char *path)
296 {
297     xfree(p->path);
298     p->path = 0;
299     if (path)
300         p->path = xstrdup(path);
301 }
302
303 #endif
304
305 /*
306  * Local variables:
307  * c-basic-offset: 4
308  * indent-tabs-mode: nil
309  * End:
310  * vim: shiftwidth=4 tabstop=8 expandtab
311  */
312