40c8039e4edeaea2c79421cf8bcd680c05cc512b
[yaz-moved-to-github.git] / src / srwutil.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2011 Index Data
3  * See the file LICENSE for details.
4  */
5 /**
6  * \file srwutil.c
7  * \brief Implements SRW/SRU utilities.
8  */
9 #if HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include <stdlib.h>
14 #include <assert.h>
15 #include <yaz/srw.h>
16 #include <yaz/matchstr.h>
17 #include <yaz/base64.h>
18 #include <yaz/yaz-iconv.h>
19 #include "sru-p.h"
20
21 #define MAX_SRU_PARAMETERS 30
22
23 static char *yaz_decode_sru_dbpath_odr(ODR n, const char *uri, size_t len)
24 {
25     return odr_strdupn(n, uri, len);
26 }
27
28 void yaz_encode_sru_dbpath_buf(char *dst, const char *db)
29 {
30     assert(db);
31     *dst = '/';
32     strcpy(dst+1, db);
33 }
34
35 char *yaz_encode_sru_dbpath_odr(ODR out, const char *db)
36 {
37     char *dst = odr_malloc(out, 3 * strlen(db) + 2);
38     yaz_encode_sru_dbpath_buf(dst, db);
39     return dst;
40 }
41
42 Z_AttributeList *yaz_use_attribute_create(ODR o, const char *name)
43 {
44     Z_AttributeList *attributes= (Z_AttributeList *)
45         odr_malloc(o, sizeof(*attributes));
46     Z_AttributeElement ** elements;
47     attributes->num_attributes = 1;
48     elements = (Z_AttributeElement**)
49         odr_malloc(o, attributes->num_attributes * sizeof(*elements));
50     elements[0] = (Z_AttributeElement*) odr_malloc(o,sizeof(**elements));
51     elements[0]->attributeType = odr_intdup(o, 1);
52     elements[0]->attributeSet = odr_nullval();
53     elements[0]->which = Z_AttributeValue_complex;
54     elements[0]->value.complex = (Z_ComplexAttribute *)
55         odr_malloc(o, sizeof(Z_ComplexAttribute));
56     elements[0]->value.complex->num_list = 1;
57     elements[0]->value.complex->list = (Z_StringOrNumeric **)
58         odr_malloc(o, 1 * sizeof(Z_StringOrNumeric *));
59     elements[0]->value.complex->list[0] = (Z_StringOrNumeric *)
60         odr_malloc(o, sizeof(Z_StringOrNumeric));
61     elements[0]->value.complex->list[0]->which = Z_StringOrNumeric_string;
62     elements[0]->value.complex->list[0]->u.string = odr_strdup(o, name);
63     elements[0]->value.complex->semanticAction = 0;
64     elements[0]->value.complex->num_semanticAction = 0;
65     attributes->attributes = elements;
66     return attributes;
67 }
68
69 #if YAZ_HAVE_XML2
70 const char *yaz_element_attribute_value_get(xmlNodePtr ptr,
71                                             const char *node_name,
72                                             const char *attribute_name)
73 {
74     struct _xmlAttr *attr;
75     // check if the node name matches
76     if (strcmp((const char*) ptr->name, node_name))
77         return 0;
78     // check if the attribute name and return the value
79     for (attr = ptr->properties; attr; attr = attr->next)
80         if (attr->children && attr->children->type == XML_TEXT_NODE)
81         {
82             if (!strcmp((const char *) attr->name, attribute_name))
83                 return (const char *) attr->children->content;
84         }
85     return 0;
86 }
87 #endif
88
89 int yaz_srw_check_content_type(Z_HTTP_Response *hres)
90 {
91     const char *content_type = z_HTTP_header_lookup(hres->headers,
92                                                     "Content-Type");
93     if (content_type)
94     {
95         if (!yaz_strcmp_del("text/xml", content_type, "; "))
96             return 1;
97         if (!yaz_strcmp_del("application/xml", content_type, "; "))
98             return 1;
99     }
100     return 0;
101 }
102
103 /**
104  * Look for authentication tokens in HTTP Basic parameters or in x-username/x-password
105  * parameters. Added by SH.
106  */
107 static void yaz_srw_decodeauth(Z_SRW_PDU *sr, Z_HTTP_Request *hreq,
108                                char *username, char *password, ODR decode)
109 {
110     const char *basic = z_HTTP_header_lookup(hreq->headers, "Authorization");
111
112     if (username)
113         sr->username = username;
114     if (password)
115         sr->password = password;
116
117     if (basic)
118     {
119         int len;
120         char out[256];
121         char ubuf[256] = "", pbuf[256] = "", *p;
122         if (strncmp(basic, "Basic ", 6))
123             return;
124         basic += 6;
125         len = strlen(basic);
126         if (!len || len > 256)
127             return;
128         yaz_base64decode(basic, out);
129         /* Format of out should be username:password at this point */
130         strcpy(ubuf, out);
131         if ((p = strchr(ubuf, ':')))
132         {
133             *(p++) = '\0';
134             if (*p)
135                 strcpy(pbuf, p);
136         }
137         if (*ubuf)
138             sr->username = odr_strdup(decode, ubuf);
139         if (*pbuf)
140             sr->password = odr_strdup(decode, pbuf);
141     }
142 }
143
144 void yaz_uri_val_int(const char *path, const char *name, ODR o, Odr_int **intp)
145 {
146     const char *v = yaz_uri_val(path, name, o);
147     if (v)
148         *intp = odr_intdup(o, atoi(v));
149 }
150
151 void yaz_mk_srw_diagnostic(ODR o, Z_SRW_diagnostic *d, 
152                            const char *uri, const char *message,
153                            const char *details)
154 {
155     d->uri = odr_strdup(o, uri);
156     if (message)
157         d->message = odr_strdup(o, message);
158     else
159         d->message = 0;
160     if (details)
161         d->details = odr_strdup(o, details);
162     else
163         d->details = 0;
164 }
165
166 void yaz_mk_std_diagnostic(ODR o, Z_SRW_diagnostic *d, 
167                            int code, const char *details)
168 {
169     char uri[40];
170     
171     sprintf(uri, "info:srw/diagnostic/1/%d", code);
172     yaz_mk_srw_diagnostic(o, d, uri, 0, details);
173 }
174
175 void yaz_add_srw_diagnostic_uri(ODR o, Z_SRW_diagnostic **d,
176                                 int *num, const char *uri,
177                                 const char *message, const char *details)
178 {
179     Z_SRW_diagnostic *d_new;
180     d_new = (Z_SRW_diagnostic *) odr_malloc(o, (*num + 1)* sizeof(**d));
181     if (*num)
182         memcpy(d_new, *d, *num *sizeof(**d));
183     *d = d_new;
184
185     yaz_mk_srw_diagnostic(o, *d + *num, uri, message, details);
186     (*num)++;
187 }
188
189 void yaz_add_srw_diagnostic(ODR o, Z_SRW_diagnostic **d,
190                             int *num, int code, const char *addinfo)
191 {
192     char uri[40];
193     
194     sprintf(uri, "info:srw/diagnostic/1/%d", code);
195     yaz_add_srw_diagnostic_uri(o, d, num, uri, 0, addinfo);
196 }
197
198
199 void yaz_add_sru_update_diagnostic(ODR o, Z_SRW_diagnostic **d,
200                                    int *num, int code, const char *addinfo)
201 {
202     char uri[40];
203     
204     sprintf(uri, "info:srw/diagnostic/12/%d", code);
205     yaz_add_srw_diagnostic_uri(o, d, num, uri, 0, addinfo);
206 }
207
208
209 void yaz_mk_sru_surrogate(ODR o, Z_SRW_record *record, int pos,
210                           int code, const char *details)
211 {
212     const char *message = yaz_diag_srw_str(code);
213     int len = 200;
214     if (message)
215         len += strlen(message);
216     if (details)
217         len += strlen(details);
218     
219     record->recordData_buf = (char *) odr_malloc(o, len);
220     
221     sprintf(record->recordData_buf, "<diagnostic "
222             "xmlns=\"http://www.loc.gov/zing/srw/diagnostic/\">\n"
223             " <uri>info:srw/diagnostic/1/%d</uri>\n", code);
224     if (details)
225         sprintf(record->recordData_buf + strlen(record->recordData_buf),
226                 " <details>%s</details>\n", details);
227     if (message)
228         sprintf(record->recordData_buf + strlen(record->recordData_buf),
229                 " <message>%s</message>\n", message);
230     sprintf(record->recordData_buf + strlen(record->recordData_buf),
231             "</diagnostic>\n");
232     record->recordData_len = strlen(record->recordData_buf);
233     record->recordPosition = odr_intdup(o, pos);
234     record->recordSchema = "info:srw/schema/1/diagnostics-v1.1";
235 }
236
237 static void grab_charset(ODR o, const char *content_type, char **charset)
238 {
239     if (charset)
240     { 
241         const char *charset_p = 0;
242         if (content_type && (charset_p = strstr(content_type, "; charset=")))
243         {
244             int i = 0;
245             charset_p += 10;
246             while (i < 20 && charset_p[i] &&
247                    !strchr("; \n\r", charset_p[i]))
248                 i++;
249             *charset = (char*) odr_malloc(o, i+1);
250             memcpy(*charset, charset_p, i);
251             (*charset)[i] = '\0';
252         }
253     }
254 }
255
256 int yaz_srw_decode(Z_HTTP_Request *hreq, Z_SRW_PDU **srw_pdu,
257                    Z_SOAP **soap_package, ODR decode, char **charset)
258 {
259     if (!strcmp(hreq->method, "POST"))
260     {
261         const char *content_type = z_HTTP_header_lookup(hreq->headers,
262                                                         "Content-Type");
263         if (content_type && 
264             (!yaz_strcmp_del("text/xml", content_type, "; ") ||
265              !yaz_strcmp_del("application/soap+xml", content_type, "; ") ||
266              !yaz_strcmp_del("text/plain", content_type, "; ")))
267         {
268             char *db = "Default";
269             const char *p0 = hreq->path, *p1;
270             int ret = -1;
271             
272             static Z_SOAP_Handler soap_handlers[4] = {
273 #if YAZ_HAVE_XML2
274                 { YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec },
275                 { YAZ_XMLNS_SRU_v1_0, 0, (Z_SOAP_fun) yaz_srw_codec },
276                 { YAZ_XMLNS_UPDATE_v0_9, 0, (Z_SOAP_fun) yaz_ucp_codec },
277 #endif
278                 {0, 0, 0}
279             };
280             
281             if (*p0 == '/')
282                 p0++;
283             p1 = strchr(p0, '?');
284             if (!p1)
285                 p1 = p0 + strlen(p0);
286             if (p1 != p0)
287                 db = yaz_decode_sru_dbpath_odr(decode, p0, p1 - p0);
288             grab_charset(decode, content_type, charset);
289
290             ret = z_soap_codec(decode, soap_package, 
291                                &hreq->content_buf, &hreq->content_len,
292                                soap_handlers);
293             if (ret == 0 && (*soap_package)->which == Z_SOAP_generic)
294             {
295                 *srw_pdu = (Z_SRW_PDU*) (*soap_package)->u.generic->p;
296                 yaz_srw_decodeauth(*srw_pdu, hreq, 0, 0, decode);
297                 
298                 if ((*srw_pdu)->which == Z_SRW_searchRetrieve_request &&
299                     (*srw_pdu)->u.request->database == 0)
300                     (*srw_pdu)->u.request->database = db;
301
302                 if ((*srw_pdu)->which == Z_SRW_explain_request &&
303                     (*srw_pdu)->u.explain_request->database == 0)
304                     (*srw_pdu)->u.explain_request->database = db;
305
306                 if ((*srw_pdu)->which == Z_SRW_scan_request &&
307                     (*srw_pdu)->u.scan_request->database == 0)
308                     (*srw_pdu)->u.scan_request->database = db;
309
310                 if ((*srw_pdu)->which == Z_SRW_update_request &&
311                     (*srw_pdu)->u.update_request->database == 0)
312                     (*srw_pdu)->u.update_request->database = db;
313
314                 return 0;
315             }
316             return 1;
317         }
318     }
319     return 2;
320 }
321
322 #if YAZ_HAVE_XML2
323 static int yaz_sru_decode_integer(ODR odr, const char *pname, 
324                                   const char *valstr, Odr_int **valp,
325                                   Z_SRW_diagnostic **diag, int *num_diag,
326                                   int min_value)
327 {
328     int ival;
329     if (!valstr)
330         return 0;
331     if (sscanf(valstr, "%d", &ival) != 1)
332     {
333         yaz_add_srw_diagnostic(odr, diag, num_diag,
334                                YAZ_SRW_UNSUPP_PARAMETER_VALUE, pname);
335         return 0;
336     }
337     if (min_value >= 0 && ival < min_value)
338     {
339         yaz_add_srw_diagnostic(odr, diag, num_diag,
340                                YAZ_SRW_UNSUPP_PARAMETER_VALUE, pname);
341         return 0;
342     }
343     *valp = odr_intdup(odr, ival);
344     return 1;
345 }
346 #endif
347
348 /**
349    http://www.loc.gov/z3950/agency/zing/srw/service.html
350 */ 
351 int yaz_sru_decode(Z_HTTP_Request *hreq, Z_SRW_PDU **srw_pdu,
352                    Z_SOAP **soap_package, ODR decode, char **charset,
353                    Z_SRW_diagnostic **diag, int *num_diag)
354 {
355 #if YAZ_HAVE_XML2
356     static Z_SOAP_Handler soap_handlers[2] = {
357         {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec},
358         {0, 0, 0}
359     };
360 #endif
361     const char *content_type = z_HTTP_header_lookup(hreq->headers,
362                                                     "Content-Type");
363
364     /*
365       SRU GET: ignore content type.
366       SRU POST: we support "application/x-www-form-urlencoded";
367       not  "multipart/form-data" .
368     */
369     if (!strcmp(hreq->method, "GET")
370         || 
371         (!strcmp(hreq->method, "POST") && content_type &&
372          !yaz_strcmp_del("application/x-www-form-urlencoded",
373                          content_type, "; ")))
374     {
375         char *db = "Default";
376         const char *p0 = hreq->path, *p1;
377 #if YAZ_HAVE_XML2
378         const char *operation = 0;
379         char *version = 0;
380         char *query = 0;
381         char *pQuery = 0;
382         char *username = 0;
383         char *password = 0;
384         char *sortKeys = 0;
385         char *stylesheet = 0;
386         char *scanClause = 0;
387         char *pScanClause = 0;
388         char *recordXPath = 0;
389         char *recordSchema = 0;
390         char *recordPacking = "xml";  /* xml packing is default for SRU */
391         char *maximumRecords = 0;
392         char *startRecord = 0;
393         char *maximumTerms = 0;
394         char *responsePosition = 0;
395         Z_SRW_extra_arg *extra_args = 0;
396 #endif
397         char **uri_name;
398         char **uri_val;
399
400         grab_charset(decode, content_type, charset);
401         if (charset && *charset == 0 && !strcmp(hreq->method, "GET"))
402             *charset = "UTF-8";
403
404         if (*p0 == '/')
405             p0++;
406         p1 = strchr(p0, '?');
407         if (!p1)
408             p1 = p0 + strlen(p0);
409         if (p1 != p0)
410             db = yaz_decode_sru_dbpath_odr(decode, p0, p1 - p0);
411         if (!strcmp(hreq->method, "POST"))
412             p1 = hreq->content_buf;
413         yaz_uri_to_array(p1, decode, &uri_name, &uri_val);
414 #if YAZ_HAVE_XML2
415         if (uri_name)
416         {
417             int i;
418             for (i = 0; uri_name[i]; i++)
419             {
420                 char *n = uri_name[i];
421                 char *v = uri_val[i];
422                 if (!strcmp(n, "query"))
423                     query = v;
424                 else if (!strcmp(n, "x-pquery"))
425                     pQuery = v;
426                 else if (!strcmp(n, "x-username"))
427                     username = v;
428                 else if (!strcmp(n, "x-password"))
429                     password = v;
430                 else if (!strcmp(n, "operation"))
431                     operation = v;
432                 else if (!strcmp(n, "stylesheet"))
433                     stylesheet = v;
434                 else if (!strcmp(n, "sortKeys"))
435                     sortKeys = v;
436                 else if (!strcmp(n, "recordXPath"))
437                     recordXPath = v;
438                 else if (!strcmp(n, "recordSchema"))
439                     recordSchema = v;
440                 else if (!strcmp(n, "recordPacking"))
441                     recordPacking = v;
442                 else if (!strcmp(n, "version"))
443                     version = v;
444                 else if (!strcmp(n, "scanClause"))
445                     scanClause = v;
446                 else if (!strcmp(n, "x-pScanClause"))
447                     pScanClause = v;
448                 else if (!strcmp(n, "maximumRecords"))
449                     maximumRecords = v;
450                 else if (!strcmp(n, "startRecord"))
451                     startRecord = v;
452                 else if (!strcmp(n, "maximumTerms"))
453                     maximumTerms = v;
454                 else if (!strcmp(n, "responsePosition"))
455                     responsePosition = v;
456                 else if (!strcmp(n, "extraRequestData"))
457                     ; /* ignoring extraRequestData */
458                 else if (n[0] == 'x' && n[1] == '-')
459                 {
460                     Z_SRW_extra_arg **l = &extra_args;
461                     while (*l)
462                         l = &(*l)->next;
463                     *l = (Z_SRW_extra_arg *) odr_malloc(decode, sizeof(**l));
464                     (*l)->name = odr_strdup(decode, n);
465                     (*l)->value = odr_strdup(decode, v);
466                     (*l)->next = 0;
467                 }
468                 else
469                     yaz_add_srw_diagnostic(decode, diag, num_diag,
470                                            YAZ_SRW_UNSUPP_PARAMETER, n);
471             }
472         }
473         if (!version)
474         {
475             if (uri_name)
476                 yaz_add_srw_diagnostic(
477                     decode, diag, num_diag,
478                     YAZ_SRW_MANDATORY_PARAMETER_NOT_SUPPLIED, "version");
479             version = "1.1";
480         }
481
482         version = yaz_negotiate_sru_version(version);
483
484         if (!version)
485         {   /* negotiation failed. */
486             yaz_add_srw_diagnostic(decode, diag, num_diag,
487                                    YAZ_SRW_UNSUPP_VERSION, "1.2");
488             version = "1.2";
489         }
490         
491         if (!operation)
492         {
493             if (uri_name)
494                 yaz_add_srw_diagnostic(
495                     decode, diag, num_diag, 
496                     YAZ_SRW_MANDATORY_PARAMETER_NOT_SUPPLIED, "operation");
497             operation = "explain";
498         }
499         if (!strcmp(operation, "searchRetrieve"))
500         {
501             Z_SRW_PDU *sr = yaz_srw_get(decode, Z_SRW_searchRetrieve_request);
502
503             sr->srw_version = version;
504             sr->extra_args = extra_args;
505             *srw_pdu = sr;
506             yaz_srw_decodeauth(sr, hreq, username, password, decode);
507             if (query)
508             {
509                 sr->u.request->query_type = Z_SRW_query_type_cql;
510                 sr->u.request->query.cql = query;
511             }
512             else if (pQuery)
513             {
514                 sr->u.request->query_type = Z_SRW_query_type_pqf;
515                 sr->u.request->query.pqf = pQuery;
516             }
517             else
518                 yaz_add_srw_diagnostic(
519                     decode, diag, num_diag, 
520                     YAZ_SRW_MANDATORY_PARAMETER_NOT_SUPPLIED, "query");
521
522             if (sortKeys)
523             {
524                 sr->u.request->sort_type = Z_SRW_sort_type_sort;
525                 sr->u.request->sort.sortKeys = sortKeys;
526             }
527             sr->u.request->recordXPath = recordXPath;
528             sr->u.request->recordSchema = recordSchema;
529             sr->u.request->recordPacking = recordPacking;
530             sr->u.request->stylesheet = stylesheet;
531
532             yaz_sru_decode_integer(decode, "maximumRecords", maximumRecords, 
533                                    &sr->u.request->maximumRecords, 
534                                    diag, num_diag, 0);
535             
536             yaz_sru_decode_integer(decode, "startRecord", startRecord, 
537                                    &sr->u.request->startRecord,
538                                    diag, num_diag, 1);
539
540             sr->u.request->database = db;
541
542             (*soap_package) = (Z_SOAP *)
543                 odr_malloc(decode, sizeof(**soap_package));
544             (*soap_package)->which = Z_SOAP_generic;
545             
546             (*soap_package)->u.generic = (Z_SOAP_Generic *)
547                 odr_malloc(decode, sizeof(*(*soap_package)->u.generic));
548             
549             (*soap_package)->u.generic->p = sr;
550             (*soap_package)->u.generic->ns = soap_handlers[0].ns;
551             (*soap_package)->u.generic->no = 0;
552             
553             (*soap_package)->ns = "SRU";
554
555             return 0;
556         }
557         else if (!strcmp(operation, "explain"))
558         {
559             /* Transfer SRU explain parameters to common struct */
560             /* http://www.loc.gov/z3950/agency/zing/srw/explain.html */
561             Z_SRW_PDU *sr = yaz_srw_get(decode, Z_SRW_explain_request);
562
563             sr->srw_version = version;
564             sr->extra_args = extra_args;
565             yaz_srw_decodeauth(sr, hreq, username, password, decode);
566             *srw_pdu = sr;
567             sr->u.explain_request->recordPacking = recordPacking;
568             sr->u.explain_request->database = db;
569
570             sr->u.explain_request->stylesheet = stylesheet;
571
572             (*soap_package) = (Z_SOAP *)
573                 odr_malloc(decode, sizeof(**soap_package));
574             (*soap_package)->which = Z_SOAP_generic;
575             
576             (*soap_package)->u.generic = (Z_SOAP_Generic *)
577                 odr_malloc(decode, sizeof(*(*soap_package)->u.generic));
578             
579             (*soap_package)->u.generic->p = sr;
580             (*soap_package)->u.generic->ns = soap_handlers[0].ns;
581             (*soap_package)->u.generic->no = 0;
582             
583             (*soap_package)->ns = "SRU";
584
585             return 0;
586         }
587         else if (!strcmp(operation, "scan"))
588         {
589             /* Transfer SRU scan parameters to common struct */
590             /* http://www.loc.gov/z3950/agency/zing/srw/scan.html */
591             Z_SRW_PDU *sr = yaz_srw_get(decode, Z_SRW_scan_request);
592
593             sr->srw_version = version;
594             sr->extra_args = extra_args;
595             *srw_pdu = sr;
596             yaz_srw_decodeauth(sr, hreq, username, password, decode);
597
598             if (scanClause)
599             {
600                 sr->u.scan_request->query_type = Z_SRW_query_type_cql;
601                 sr->u.scan_request->scanClause.cql = scanClause;
602             }
603             else if (pScanClause)
604             {
605                 sr->u.scan_request->query_type = Z_SRW_query_type_pqf;
606                 sr->u.scan_request->scanClause.pqf = pScanClause;
607             }
608             else
609                 yaz_add_srw_diagnostic(
610                     decode, diag, num_diag, 
611                     YAZ_SRW_MANDATORY_PARAMETER_NOT_SUPPLIED, "scanClause");
612             sr->u.scan_request->database = db;
613             
614             yaz_sru_decode_integer(decode, "maximumTerms",
615                                    maximumTerms, 
616                                    &sr->u.scan_request->maximumTerms,
617                                    diag, num_diag, 0);
618             
619             yaz_sru_decode_integer(decode, "responsePosition",
620                                    responsePosition, 
621                                    &sr->u.scan_request->responsePosition,
622                                    diag, num_diag, 0);
623
624             sr->u.scan_request->stylesheet = stylesheet;
625
626             (*soap_package) = (Z_SOAP *)
627                 odr_malloc(decode, sizeof(**soap_package));
628             (*soap_package)->which = Z_SOAP_generic;
629             
630             (*soap_package)->u.generic = (Z_SOAP_Generic *)
631                 odr_malloc(decode, sizeof(*(*soap_package)->u.generic));
632             
633             (*soap_package)->u.generic->p = sr;
634             (*soap_package)->u.generic->ns = soap_handlers[0].ns;
635             (*soap_package)->u.generic->no = 0;
636             
637             (*soap_package)->ns = "SRU";
638
639             return 0;
640         }
641         else
642         {
643             /* unsupported operation ... */
644             /* Act as if we received a explain request and throw diagnostic. */
645
646             Z_SRW_PDU *sr = yaz_srw_get(decode, Z_SRW_explain_request);
647
648             sr->srw_version = version;
649             *srw_pdu = sr;
650             sr->u.explain_request->recordPacking = recordPacking;
651             sr->u.explain_request->database = db;
652
653             sr->u.explain_request->stylesheet = stylesheet;
654
655             (*soap_package) = (Z_SOAP *)
656                 odr_malloc(decode, sizeof(**soap_package));
657             (*soap_package)->which = Z_SOAP_generic;
658             
659             (*soap_package)->u.generic = (Z_SOAP_Generic *)
660                 odr_malloc(decode, sizeof(*(*soap_package)->u.generic));
661             
662             (*soap_package)->u.generic->p = sr;
663             (*soap_package)->u.generic->ns = soap_handlers[0].ns;
664             (*soap_package)->u.generic->no = 0;
665             
666             (*soap_package)->ns = "SRU";
667
668             yaz_add_srw_diagnostic(decode, diag, num_diag, 
669                                    YAZ_SRW_UNSUPP_OPERATION, operation);
670             return 0;
671         }
672 #endif
673         return 1;
674     }
675     return 2;
676 }
677
678 Z_SRW_extra_record *yaz_srw_get_extra_record(ODR o)
679 {
680     Z_SRW_extra_record *res = (Z_SRW_extra_record *)
681         odr_malloc(o, sizeof(*res));
682
683     res->extraRecordData_buf = 0;
684     res->extraRecordData_len = 0;
685     res->recordIdentifier = 0;
686     return res;
687 }
688
689
690 Z_SRW_record *yaz_srw_get_records(ODR o, int n)
691 {
692     Z_SRW_record *res = (Z_SRW_record *) odr_malloc(o, n * sizeof(*res));
693     int i;
694
695     for (i = 0; i<n; i++)
696     {
697         res[i].recordSchema = 0;
698         res[i].recordPacking = Z_SRW_recordPacking_string;
699         res[i].recordData_buf = 0;
700         res[i].recordData_len = 0;
701         res[i].recordPosition = 0;
702     }
703     return res;
704 }
705
706 Z_SRW_record *yaz_srw_get_record(ODR o)
707 {
708     return yaz_srw_get_records(o, 1);
709 }
710
711 static Z_SRW_PDU *yaz_srw_get_core_ver(ODR o, const char *version)
712 {
713     Z_SRW_PDU *p = (Z_SRW_PDU *) odr_malloc(o, sizeof(*p));
714     p->srw_version = odr_strdup(o, version);
715     p->username = 0;
716     p->password = 0;
717     p->extra_args = 0;
718     p->extraResponseData_buf = 0;
719     p->extraResponseData_len = 0;
720     return p;
721 }
722
723 Z_SRW_PDU *yaz_srw_get_core_v_1_1(ODR o)
724 {
725     return yaz_srw_get_core_ver(o, "1.1");
726 }
727
728 Z_SRW_PDU *yaz_srw_get(ODR o, int which)
729 {
730     return yaz_srw_get_pdu(o, which, "1.1");
731 }
732
733 Z_SRW_PDU *yaz_srw_get_pdu(ODR o, int which, const char *version)
734 {
735     Z_SRW_PDU *sr = yaz_srw_get_core_ver(o, version);
736
737     sr->which = which;
738     switch(which)
739     {
740     case Z_SRW_searchRetrieve_request:
741         sr->u.request = (Z_SRW_searchRetrieveRequest *)
742             odr_malloc(o, sizeof(*sr->u.request));
743         sr->u.request->query_type = Z_SRW_query_type_cql;
744         sr->u.request->query.cql = 0;
745         sr->u.request->sort_type = Z_SRW_sort_type_none;
746         sr->u.request->sort.none = 0;
747         sr->u.request->startRecord = 0;
748         sr->u.request->maximumRecords = 0;
749         sr->u.request->recordSchema = 0;
750         sr->u.request->recordPacking = 0;
751         sr->u.request->recordXPath = 0;
752         sr->u.request->database = 0;
753         sr->u.request->resultSetTTL = 0;
754         sr->u.request->stylesheet = 0;
755         sr->u.request->facetList = 0;
756         break;
757     case Z_SRW_searchRetrieve_response:
758         sr->u.response = (Z_SRW_searchRetrieveResponse *)
759             odr_malloc(o, sizeof(*sr->u.response));
760         sr->u.response->numberOfRecords = 0;
761         sr->u.response->resultSetId = 0;
762         sr->u.response->resultSetIdleTime = 0;
763         sr->u.response->records = 0;
764         sr->u.response->num_records = 0;
765         sr->u.response->diagnostics = 0;
766         sr->u.response->num_diagnostics = 0;
767         sr->u.response->nextRecordPosition = 0;
768         sr->u.response->extra_records = 0;
769         sr->u.response->facetList = 0;
770         break;
771     case Z_SRW_explain_request:
772         sr->u.explain_request = (Z_SRW_explainRequest *)
773             odr_malloc(o, sizeof(*sr->u.explain_request));
774         sr->u.explain_request->recordPacking = 0;
775         sr->u.explain_request->database = 0;
776         sr->u.explain_request->stylesheet = 0;
777         break;
778     case Z_SRW_explain_response:
779         sr->u.explain_response = (Z_SRW_explainResponse *)
780             odr_malloc(o, sizeof(*sr->u.explain_response));
781         sr->u.explain_response->record.recordData_buf = 0;
782         sr->u.explain_response->record.recordData_len = 0;
783         sr->u.explain_response->record.recordSchema = 0;
784         sr->u.explain_response->record.recordPosition = 0;
785         sr->u.explain_response->record.recordPacking =
786             Z_SRW_recordPacking_string;
787         sr->u.explain_response->diagnostics = 0;
788         sr->u.explain_response->num_diagnostics = 0;
789         sr->u.explain_response->extra_record = 0;
790         break;
791     case Z_SRW_scan_request:
792         sr->u.scan_request = (Z_SRW_scanRequest *)
793             odr_malloc(o, sizeof(*sr->u.scan_request));
794         sr->u.scan_request->database = 0;
795         sr->u.scan_request->stylesheet = 0;
796         sr->u.scan_request->maximumTerms = 0;
797         sr->u.scan_request->responsePosition = 0;
798         sr->u.scan_request->query_type = Z_SRW_query_type_cql;
799         sr->u.scan_request->scanClause.cql = 0;
800         break;
801     case Z_SRW_scan_response:
802         sr->u.scan_response = (Z_SRW_scanResponse *)
803             odr_malloc(o, sizeof(*sr->u.scan_response));
804         sr->u.scan_response->terms = 0;
805         sr->u.scan_response->num_terms = 0;
806         sr->u.scan_response->diagnostics = 0;
807         sr->u.scan_response->num_diagnostics = 0;
808         break;
809     case Z_SRW_update_request:
810         sr->u.update_request = (Z_SRW_updateRequest *)
811             odr_malloc(o, sizeof(*sr->u.update_request));
812         sr->u.update_request->database = 0;
813         sr->u.update_request->stylesheet = 0;
814         sr->u.update_request->record = 0;
815         sr->u.update_request->recordId = 0;
816         sr->u.update_request->recordVersions = 0;
817         sr->u.update_request->num_recordVersions = 0;
818         sr->u.update_request->extra_record = 0;
819         sr->u.update_request->extraRequestData_buf = 0;
820         sr->u.update_request->extraRequestData_len = 0;
821         sr->u.request->database = 0;
822         break;
823     case Z_SRW_update_response:
824         sr->u.update_response = (Z_SRW_updateResponse *)
825             odr_malloc(o, sizeof(*sr->u.update_response));
826         sr->u.update_response->operationStatus = 0;
827         sr->u.update_response->recordId = 0;
828         sr->u.update_response->recordVersions = 0;
829         sr->u.update_response->num_recordVersions = 0;
830         sr->u.update_response->record = 0;
831         sr->u.update_response->extra_record = 0;
832         sr->u.update_response->extraResponseData_buf = 0;
833         sr->u.update_response->extraResponseData_len = 0;
834         sr->u.update_response->diagnostics = 0;
835         sr->u.update_response->num_diagnostics = 0;
836     }
837     return sr;
838 }
839
840 /* bib1:srw */
841 static int bib1_srw_map[] = {
842     1, 1,
843     2, 2,
844     3, 11,
845     4, 35,
846     5, 12,
847     6, 38,
848     7, 30,
849     8, 32,
850     9, 29,
851     108, 10,  /* Malformed query : Syntax error */
852     10, 10,
853     11, 12,
854     11, 23,
855     12, 60,
856     13, 61,
857     13, 62,
858     14, 63,
859     14, 64,
860     14, 65,
861     15, 68,
862     15, 69,
863     16, 70,
864     17, 70,
865     18, 50,
866     19, 55,
867     20, 56, 
868     21, 52,
869     22, 50,
870     23, 3,
871     24, 66,
872     25, 66,
873     26, 66,
874     27, 51,
875     28, 52,
876     29, 52,
877     30, 51,
878     31, 57,
879     32, 58,
880     33, 59,
881     100, 1, /* bad map */
882     101, 3,
883     102, 3,
884     103, 3,
885     104, 3,
886     105, 3, 
887     106, 66,
888     107, 11,
889     108, 13,
890     108, 14,
891     108, 25,
892     108, 26,
893     108, 27,
894     108, 45,
895         
896     109, 2,
897     110, 37,
898     111, 1,
899     112, 58,
900     113, 10,
901     114, 16,
902     115, 16,
903     116, 16,
904     117, 19,
905     117, 20,
906     118, 22,
907     119, 32,
908     119, 31,
909     120, 28,
910     121, 15,
911     122, 32,
912     123, 22,
913     123, 17,
914     123, 18,
915     124, 24,
916     125, 36,
917     126, 36, 
918     127, 36,
919     128, 51,
920     129, 39,
921     130, 43,
922     131, 40,
923     132, 42,
924     201, 44,
925     201, 33,
926     201, 34,
927     202, 41,
928     203, 43,
929     205, 1,  /* bad map */
930     206, 1,  /* bad map */
931     207, 89,
932     208, 1,  /* bad map */
933     209, 80,
934     210, 80,
935     210, 81,
936     211, 84,
937     212, 85,
938     213, 92,
939     214, 90,
940     215, 91,
941     216, 92,
942     217, 63,
943     218, 1,  /* bad map */
944     219, 1,  /* bad map */
945     220, 1,  /* bad map */
946     221, 1,  /* bad map */
947     222, 3,
948     223, 1,  /* bad map */
949     224, 1,  /* bad map */
950     225, 1,  /* bad map */
951     226, 1,  /* bad map */
952     227, 66,
953     228, 1,  /* bad map */
954     229, 36,
955     230, 83,
956     231, 89,
957     232, 1,
958     233, 1, /* bad map */
959     234, 1, /* bad map */
960     235, 2,
961     236, 3, 
962     237, 82,
963     238, 67,
964     239, 66,
965     240, 1, /* bad map */
966     241, 1, /* bad map */
967     242, 70,
968     243, 1, /* bad map */
969     244, 66,
970     245, 10,
971     246, 10,
972     247, 10,
973     1001, 1, /* bad map */
974     1002, 1, /* bad map */
975     1003, 1, /* bad map */
976     1004, 1, /* bad map */
977     1005, 1, /* bad map */
978     1006, 1, /* bad map */
979     1007, 100,
980     1008, 1, 
981     1009, 1,
982     1010, 3,
983     1011, 3,
984     1012, 3,
985     1013, 3,
986     1014, 3,
987     1015, 3,
988     1015, 3,
989     1016, 3,
990     1017, 3,
991     1018, 2,
992     1019, 2,
993     1020, 2,
994     1021, 3,
995     1022, 3,
996     1023, 3,
997     1024, 16,
998     1025, 3,
999     1026, 64,
1000     1027, 1,
1001     1028, 65,
1002     1029, 1,
1003     1040, 1,
1004     /* 1041-1065 */
1005     1066, 66,
1006     1066, 67,
1007     0
1008 };
1009
1010 /*
1011  * This array contains overrides for when the first occurrence of a
1012  * particular SRW error in the array above does not correspond with
1013  * the best back-translation of that SRW error.
1014  */
1015 static int srw_bib1_map[] = {
1016     66, 238,
1017     /* No doubt there are many more */
1018     0
1019 };
1020
1021
1022 int yaz_diag_bib1_to_srw (int code)
1023 {
1024     const int *p = bib1_srw_map;
1025     while (*p)
1026     {
1027         if (code == p[0])
1028             return p[1];
1029         p += 2;
1030     }
1031     return 1;
1032 }
1033
1034 int yaz_diag_srw_to_bib1(int code)
1035 {
1036     /* Check explicit reverse-map first */
1037     const int *p = srw_bib1_map;
1038     while (*p)
1039     {
1040         if (code == p[0])
1041             return p[1];
1042         p += 2;
1043     }
1044
1045     /* Fall back on reverse lookup in main map */
1046     p = bib1_srw_map;
1047     while (*p)
1048     {
1049         if (code == p[1])
1050             return p[0];
1051         p += 2;
1052     }
1053     return 1;
1054 }
1055
1056 void yaz_add_name_value_int(ODR o, char **name, char **value, int *i,
1057                             char *a_name, Odr_int *val)
1058 {
1059     if (val)
1060     {
1061         name[*i] = a_name;
1062         value[*i] = (char *) odr_malloc(o, 40);
1063         sprintf(value[*i], ODR_INT_PRINTF, *val);
1064         (*i)++;
1065     }
1066 }
1067
1068 void yaz_add_name_value_str(ODR o, char **name, char **value,  int *i,
1069                             char *a_name, char *val)
1070 {
1071     if (val)
1072     {
1073         name[*i] = a_name;
1074         value[*i] = val;
1075         (*i)++;
1076     }
1077 }
1078
1079 static int yaz_get_sru_parms(const Z_SRW_PDU *srw_pdu, ODR encode,
1080                              char **name, char **value, int max_names)
1081 {
1082     int i = 0;
1083     yaz_add_name_value_str(encode, name, value, &i, "version", srw_pdu->srw_version);
1084     name[i] = "operation";
1085     switch(srw_pdu->which)
1086     {
1087     case Z_SRW_searchRetrieve_request:
1088         value[i++] = "searchRetrieve";
1089         switch(srw_pdu->u.request->query_type)
1090         {
1091         case Z_SRW_query_type_cql:
1092             yaz_add_name_value_str(encode, name, value, &i, "query",
1093                                    srw_pdu->u.request->query.cql);
1094             break;
1095         case Z_SRW_query_type_pqf:
1096             yaz_add_name_value_str(encode, name, value, &i, "x-pquery",
1097                                    srw_pdu->u.request->query.pqf);
1098             break;
1099         case Z_SRW_query_type_xcql:
1100             yaz_add_name_value_str(encode, name, value, &i, "x-cql",
1101                                    srw_pdu->u.request->query.xcql);
1102             break;
1103         }
1104         switch(srw_pdu->u.request->sort_type)
1105         {
1106         case Z_SRW_sort_type_none:
1107             break;
1108         case Z_SRW_sort_type_sort:            
1109             yaz_add_name_value_str(encode, name, value, &i, "sortKeys",
1110                                    srw_pdu->u.request->sort.sortKeys);
1111             break;
1112         }
1113         yaz_add_name_value_int(encode, name, value, &i, "startRecord", 
1114                                srw_pdu->u.request->startRecord);
1115         yaz_add_name_value_int(encode, name, value, &i, "maximumRecords", 
1116                                srw_pdu->u.request->maximumRecords);
1117         yaz_add_name_value_str(encode, name, value, &i, "recordSchema",
1118                                srw_pdu->u.request->recordSchema);
1119         yaz_add_name_value_str(encode, name, value, &i, "recordPacking",
1120                                srw_pdu->u.request->recordPacking);
1121         yaz_add_name_value_str(encode, name, value, &i, "recordXPath",
1122                                srw_pdu->u.request->recordXPath);
1123         yaz_add_name_value_str(encode, name, value, &i, "stylesheet",
1124                                srw_pdu->u.request->stylesheet);
1125         yaz_add_name_value_int(encode, name, value, &i, "resultSetTTL", 
1126                                srw_pdu->u.request->resultSetTTL);
1127         break;
1128     case Z_SRW_explain_request:
1129         value[i++] = "explain";
1130         yaz_add_name_value_str(encode, name, value, &i, "stylesheet",
1131                                srw_pdu->u.explain_request->stylesheet);
1132         break;
1133     case Z_SRW_scan_request:
1134         value[i++] = "scan";
1135
1136         switch(srw_pdu->u.scan_request->query_type)
1137         {
1138         case Z_SRW_query_type_cql:
1139             yaz_add_name_value_str(encode, name, value, &i, "scanClause",
1140                                    srw_pdu->u.scan_request->scanClause.cql);
1141             break;
1142         case Z_SRW_query_type_pqf:
1143             yaz_add_name_value_str(encode, name, value, &i, "x-pScanClause",
1144                                    srw_pdu->u.scan_request->scanClause.pqf);
1145             break;
1146         case Z_SRW_query_type_xcql:
1147             yaz_add_name_value_str(encode, name, value, &i, "x-cqlScanClause",
1148                                    srw_pdu->u.scan_request->scanClause.xcql);
1149             break;
1150         }
1151         yaz_add_name_value_int(encode, name, value, &i, "responsePosition", 
1152                                srw_pdu->u.scan_request->responsePosition);
1153         yaz_add_name_value_int(encode, name, value, &i, "maximumTerms", 
1154                                srw_pdu->u.scan_request->maximumTerms);
1155         yaz_add_name_value_str(encode, name, value, &i, "stylesheet",
1156                                srw_pdu->u.scan_request->stylesheet);
1157         break;
1158     case Z_SRW_update_request:
1159         value[i++] = "update";
1160         break;
1161     default:
1162         return -1;
1163     }
1164     if (srw_pdu->extra_args)
1165     {
1166         Z_SRW_extra_arg *ea = srw_pdu->extra_args;
1167         for (; ea && i < max_names-1; ea = ea->next)
1168         {
1169             name[i] = ea->name;
1170             value[i] = ea->value;
1171             i++;
1172         }
1173     }
1174     name[i++] = 0;
1175
1176     return 0;
1177 }
1178
1179 int yaz_sru_get_encode(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu,
1180                        ODR encode, const char *charset)
1181 {
1182     char *name[MAX_SRU_PARAMETERS], *value[MAX_SRU_PARAMETERS]; /* definite upper limit for SRU params */
1183     char *uri_args;
1184     char *path;
1185
1186     z_HTTP_header_add_basic_auth(encode, &hreq->headers, 
1187                                  srw_pdu->username, srw_pdu->password);
1188     if (yaz_get_sru_parms(srw_pdu, encode, name, value, MAX_SRU_PARAMETERS))
1189         return -1;
1190     yaz_array_to_uri(&uri_args, encode, name, value);
1191
1192     hreq->method = "GET";
1193     
1194     path = (char *)
1195         odr_malloc(encode, strlen(hreq->path) + strlen(uri_args) + 4);
1196
1197     sprintf(path, "%s?%s", hreq->path, uri_args);
1198     yaz_log(YLOG_DEBUG, "SRU HTTP Get Request %s", path);
1199     hreq->path = path;
1200
1201     z_HTTP_header_add_content_type(encode, &hreq->headers,
1202                                    "text/xml", charset);
1203     return 0;
1204 }
1205
1206 int yaz_sru_post_encode(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu,
1207                         ODR encode, const char *charset)
1208 {
1209     char *name[MAX_SRU_PARAMETERS], *value[MAX_SRU_PARAMETERS]; /* definite upper limit for SRU params */
1210     char *uri_args;
1211
1212     z_HTTP_header_add_basic_auth(encode, &hreq->headers, 
1213                                  srw_pdu->username, srw_pdu->password);
1214     if (yaz_get_sru_parms(srw_pdu, encode, name, value, MAX_SRU_PARAMETERS))
1215         return -1;
1216
1217     yaz_array_to_uri(&uri_args, encode, name, value);
1218
1219     hreq->method = "POST";
1220     
1221     hreq->content_buf = uri_args;
1222     hreq->content_len = strlen(uri_args);
1223
1224     z_HTTP_header_add_content_type(encode, &hreq->headers,
1225                                    "application/x-www-form-urlencoded",
1226                                    charset);
1227     return 0;
1228 }
1229
1230 int yaz_sru_soap_encode(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu,
1231                         ODR odr, const char *charset)
1232 {
1233     Z_SOAP_Handler handlers[3] = {
1234 #if YAZ_HAVE_XML2
1235         {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec},
1236         {YAZ_XMLNS_UPDATE_v0_9, 0, (Z_SOAP_fun) yaz_ucp_codec},
1237 #endif
1238         {0, 0, 0}
1239     };
1240     Z_SOAP *p = (Z_SOAP*) odr_malloc(odr, sizeof(*p));
1241
1242     z_HTTP_header_add_basic_auth(odr, &hreq->headers, 
1243                                  srw_pdu->username, srw_pdu->password);
1244     z_HTTP_header_add_content_type(odr,
1245                                    &hreq->headers,
1246                                    "text/xml", charset);
1247     
1248     z_HTTP_header_add(odr, &hreq->headers,
1249                       "SOAPAction", "\"\"");
1250     p->which = Z_SOAP_generic;
1251     p->u.generic = (Z_SOAP_Generic *) odr_malloc(odr, sizeof(*p->u.generic));
1252     p->u.generic->no = 0;
1253     p->u.generic->ns = 0;
1254     p->u.generic->p = srw_pdu;
1255     p->ns = "http://schemas.xmlsoap.org/soap/envelope/";
1256
1257 #if YAZ_HAVE_XML2
1258     if (srw_pdu->which == Z_SRW_update_request ||
1259         srw_pdu->which == Z_SRW_update_response)
1260         p->u.generic->no = 1; /* second handler */
1261 #endif
1262     return z_soap_codec_enc(odr, &p,
1263                             &hreq->content_buf,
1264                             &hreq->content_len, handlers,
1265                             charset);
1266 }
1267
1268 Z_SRW_recordVersion *yaz_srw_get_record_versions(ODR odr, int num)
1269 {
1270     Z_SRW_recordVersion *ver 
1271         = (Z_SRW_recordVersion *) odr_malloc(odr,num * sizeof(*ver));
1272     int i;
1273     for (i = 0; i < num; ++i)
1274     {
1275         ver[i].versionType = 0;
1276         ver[i].versionValue = 0;
1277     }
1278     return ver;
1279 }
1280
1281 const char *yaz_srw_pack_to_str(int pack)
1282 {
1283     switch(pack)
1284     {
1285     case Z_SRW_recordPacking_string:
1286         return "string";
1287     case Z_SRW_recordPacking_XML:
1288         return "xml";
1289     case Z_SRW_recordPacking_URL:
1290         return "url";
1291     }
1292     return 0;
1293 }
1294
1295 int yaz_srw_str_to_pack(const char *str)
1296 {
1297     if (!yaz_matchstr(str, "string"))
1298         return Z_SRW_recordPacking_string;
1299     if (!yaz_matchstr(str, "xml"))
1300         return Z_SRW_recordPacking_XML;
1301     if (!yaz_matchstr(str, "url"))
1302         return Z_SRW_recordPacking_URL;
1303     return -1;
1304 }
1305
1306 void yaz_encode_sru_extra(Z_SRW_PDU *sr, ODR odr, const char *extra_args)
1307 {
1308     if (extra_args)
1309     {
1310         char **name;
1311         char **val;
1312         Z_SRW_extra_arg **ea = &sr->extra_args;
1313         yaz_uri_to_array(extra_args, odr, &name, &val);
1314
1315         while (*name)
1316         {
1317             *ea = (Z_SRW_extra_arg *) odr_malloc(odr, sizeof(**ea));
1318             (*ea)->name = *name;
1319             (*ea)->value = *val;
1320             ea = &(*ea)->next;
1321             val++;
1322             name++;
1323         }
1324         *ea = 0;
1325     }
1326 }
1327
1328
1329 /*
1330  * Local variables:
1331  * c-basic-offset: 4
1332  * c-file-style: "Stroustrup"
1333  * indent-tabs-mode: nil
1334  * End:
1335  * vim: shiftwidth=4 tabstop=8 expandtab
1336  */
1337