Change diagnostic code mapping for Bib-1: "Unsupported search"
[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         sr->u.response->suggestions = 0;
771         break;
772     case Z_SRW_explain_request:
773         sr->u.explain_request = (Z_SRW_explainRequest *)
774             odr_malloc(o, sizeof(*sr->u.explain_request));
775         sr->u.explain_request->recordPacking = 0;
776         sr->u.explain_request->database = 0;
777         sr->u.explain_request->stylesheet = 0;
778         break;
779     case Z_SRW_explain_response:
780         sr->u.explain_response = (Z_SRW_explainResponse *)
781             odr_malloc(o, sizeof(*sr->u.explain_response));
782         sr->u.explain_response->record.recordData_buf = 0;
783         sr->u.explain_response->record.recordData_len = 0;
784         sr->u.explain_response->record.recordSchema = 0;
785         sr->u.explain_response->record.recordPosition = 0;
786         sr->u.explain_response->record.recordPacking =
787             Z_SRW_recordPacking_string;
788         sr->u.explain_response->diagnostics = 0;
789         sr->u.explain_response->num_diagnostics = 0;
790         sr->u.explain_response->extra_record = 0;
791         break;
792     case Z_SRW_scan_request:
793         sr->u.scan_request = (Z_SRW_scanRequest *)
794             odr_malloc(o, sizeof(*sr->u.scan_request));
795         sr->u.scan_request->database = 0;
796         sr->u.scan_request->stylesheet = 0;
797         sr->u.scan_request->maximumTerms = 0;
798         sr->u.scan_request->responsePosition = 0;
799         sr->u.scan_request->query_type = Z_SRW_query_type_cql;
800         sr->u.scan_request->scanClause.cql = 0;
801         break;
802     case Z_SRW_scan_response:
803         sr->u.scan_response = (Z_SRW_scanResponse *)
804             odr_malloc(o, sizeof(*sr->u.scan_response));
805         sr->u.scan_response->terms = 0;
806         sr->u.scan_response->num_terms = 0;
807         sr->u.scan_response->diagnostics = 0;
808         sr->u.scan_response->num_diagnostics = 0;
809         break;
810     case Z_SRW_update_request:
811         sr->u.update_request = (Z_SRW_updateRequest *)
812             odr_malloc(o, sizeof(*sr->u.update_request));
813         sr->u.update_request->database = 0;
814         sr->u.update_request->stylesheet = 0;
815         sr->u.update_request->record = 0;
816         sr->u.update_request->recordId = 0;
817         sr->u.update_request->recordVersions = 0;
818         sr->u.update_request->num_recordVersions = 0;
819         sr->u.update_request->extra_record = 0;
820         sr->u.update_request->extraRequestData_buf = 0;
821         sr->u.update_request->extraRequestData_len = 0;
822         sr->u.request->database = 0;
823         break;
824     case Z_SRW_update_response:
825         sr->u.update_response = (Z_SRW_updateResponse *)
826             odr_malloc(o, sizeof(*sr->u.update_response));
827         sr->u.update_response->operationStatus = 0;
828         sr->u.update_response->recordId = 0;
829         sr->u.update_response->recordVersions = 0;
830         sr->u.update_response->num_recordVersions = 0;
831         sr->u.update_response->record = 0;
832         sr->u.update_response->extra_record = 0;
833         sr->u.update_response->extraResponseData_buf = 0;
834         sr->u.update_response->extraResponseData_len = 0;
835         sr->u.update_response->diagnostics = 0;
836         sr->u.update_response->num_diagnostics = 0;
837     }
838     return sr;
839 }
840
841 /* bib1:srw */
842 static int bib1_srw_map[] = {
843     1, 1,
844     2, 2,
845     3, 48,
846     4, 35,
847     5, 12,
848     6, 38,
849     7, 30,
850     8, 32,
851     9, 29,
852     108, 10,  /* Malformed query : Syntax error */
853     10, 10,
854     11, 12,
855     11, 23,
856     12, 60,
857     13, 61,
858     13, 62,
859     14, 63,
860     14, 64,
861     14, 65,
862     15, 68,
863     15, 69,
864     16, 70,
865     17, 70,
866     18, 50,
867     19, 55,
868     20, 56, 
869     21, 52,
870     22, 50,
871     23, 3,
872     24, 66,
873     25, 66,
874     26, 66,
875     27, 51,
876     28, 52,
877     29, 52,
878     30, 51,
879     31, 57,
880     32, 58,
881     33, 59,
882     100, 1, /* bad map */
883     101, 3,
884     102, 3,
885     103, 3,
886     104, 3,
887     105, 3, 
888     106, 66,
889     107, 11,
890     108, 13,
891     108, 14,
892     108, 25,
893     108, 26,
894     108, 27,
895     108, 45,
896         
897     109, 2,
898     110, 37,
899     111, 1,
900     112, 58,
901     113, 10,
902     114, 16,
903     115, 16,
904     116, 16,
905     117, 19,
906     117, 20,
907     118, 22,
908     119, 32,
909     119, 31,
910     120, 28,
911     121, 15,
912     122, 32,
913     123, 22,
914     123, 17,
915     123, 18,
916     124, 24,
917     125, 36,
918     126, 36, 
919     127, 36,
920     128, 51,
921     129, 39,
922     130, 43,
923     131, 40,
924     132, 42,
925     201, 44,
926     201, 33,
927     201, 34,
928     202, 41,
929     203, 43,
930     205, 1,  /* bad map */
931     206, 1,  /* bad map */
932     207, 89,
933     208, 1,  /* bad map */
934     209, 80,
935     210, 80,
936     210, 81,
937     211, 84,
938     212, 85,
939     213, 92,
940     214, 90,
941     215, 91,
942     216, 92,
943     217, 63,
944     218, 1,  /* bad map */
945     219, 1,  /* bad map */
946     220, 1,  /* bad map */
947     221, 1,  /* bad map */
948     222, 3,
949     223, 1,  /* bad map */
950     224, 1,  /* bad map */
951     225, 1,  /* bad map */
952     226, 1,  /* bad map */
953     227, 66,
954     228, 1,  /* bad map */
955     229, 36,
956     230, 83,
957     231, 89,
958     232, 1,
959     233, 1, /* bad map */
960     234, 1, /* bad map */
961     235, 2,
962     236, 3, 
963     237, 82,
964     238, 67,
965     239, 66,
966     240, 1, /* bad map */
967     241, 1, /* bad map */
968     242, 70,
969     243, 1, /* bad map */
970     244, 66,
971     245, 10,
972     246, 10,
973     247, 10,
974     1001, 1, /* bad map */
975     1002, 1, /* bad map */
976     1003, 1, /* bad map */
977     1004, 1, /* bad map */
978     1005, 1, /* bad map */
979     1006, 1, /* bad map */
980     1007, 100,
981     1008, 1, 
982     1009, 1,
983     1010, 3,
984     1011, 3,
985     1012, 3,
986     1013, 3,
987     1014, 3,
988     1015, 3,
989     1015, 3,
990     1016, 3,
991     1017, 3,
992     1018, 2,
993     1019, 2,
994     1020, 2,
995     1021, 3,
996     1022, 3,
997     1023, 3,
998     1024, 16,
999     1025, 3,
1000     1026, 64,
1001     1027, 1,
1002     1028, 65,
1003     1029, 1,
1004     1040, 1,
1005     /* 1041-1065 */
1006     1066, 66,
1007     1066, 67,
1008     0
1009 };
1010
1011 /*
1012  * This array contains overrides for when the first occurrence of a
1013  * particular SRW error in the array above does not correspond with
1014  * the best back-translation of that SRW error.
1015  */
1016 static int srw_bib1_map[] = {
1017     66, 238,
1018     /* No doubt there are many more */
1019     0
1020 };
1021
1022
1023 int yaz_diag_bib1_to_srw (int code)
1024 {
1025     const int *p = bib1_srw_map;
1026     while (*p)
1027     {
1028         if (code == p[0])
1029             return p[1];
1030         p += 2;
1031     }
1032     return 1;
1033 }
1034
1035 int yaz_diag_srw_to_bib1(int code)
1036 {
1037     /* Check explicit reverse-map first */
1038     const int *p = srw_bib1_map;
1039     while (*p)
1040     {
1041         if (code == p[0])
1042             return p[1];
1043         p += 2;
1044     }
1045
1046     /* Fall back on reverse lookup in main map */
1047     p = bib1_srw_map;
1048     while (*p)
1049     {
1050         if (code == p[1])
1051             return p[0];
1052         p += 2;
1053     }
1054     return 1;
1055 }
1056
1057 void yaz_add_name_value_int(ODR o, char **name, char **value, int *i,
1058                             char *a_name, Odr_int *val)
1059 {
1060     if (val)
1061     {
1062         name[*i] = a_name;
1063         value[*i] = (char *) odr_malloc(o, 40);
1064         sprintf(value[*i], ODR_INT_PRINTF, *val);
1065         (*i)++;
1066     }
1067 }
1068
1069 void yaz_add_name_value_str(ODR o, char **name, char **value,  int *i,
1070                             char *a_name, char *val)
1071 {
1072     if (val)
1073     {
1074         name[*i] = a_name;
1075         value[*i] = val;
1076         (*i)++;
1077     }
1078 }
1079
1080 static int yaz_get_sru_parms(const Z_SRW_PDU *srw_pdu, ODR encode,
1081                              char **name, char **value, int max_names)
1082 {
1083     int i = 0;
1084     yaz_add_name_value_str(encode, name, value, &i, "version", srw_pdu->srw_version);
1085     name[i] = "operation";
1086     switch(srw_pdu->which)
1087     {
1088     case Z_SRW_searchRetrieve_request:
1089         value[i++] = "searchRetrieve";
1090         switch(srw_pdu->u.request->query_type)
1091         {
1092         case Z_SRW_query_type_cql:
1093             yaz_add_name_value_str(encode, name, value, &i, "query",
1094                                    srw_pdu->u.request->query.cql);
1095             break;
1096         case Z_SRW_query_type_pqf:
1097             yaz_add_name_value_str(encode, name, value, &i, "x-pquery",
1098                                    srw_pdu->u.request->query.pqf);
1099             break;
1100         case Z_SRW_query_type_xcql:
1101             yaz_add_name_value_str(encode, name, value, &i, "x-cql",
1102                                    srw_pdu->u.request->query.xcql);
1103             break;
1104         }
1105         switch(srw_pdu->u.request->sort_type)
1106         {
1107         case Z_SRW_sort_type_none:
1108             break;
1109         case Z_SRW_sort_type_sort:            
1110             yaz_add_name_value_str(encode, name, value, &i, "sortKeys",
1111                                    srw_pdu->u.request->sort.sortKeys);
1112             break;
1113         }
1114         yaz_add_name_value_int(encode, name, value, &i, "startRecord", 
1115                                srw_pdu->u.request->startRecord);
1116         yaz_add_name_value_int(encode, name, value, &i, "maximumRecords", 
1117                                srw_pdu->u.request->maximumRecords);
1118         yaz_add_name_value_str(encode, name, value, &i, "recordSchema",
1119                                srw_pdu->u.request->recordSchema);
1120         yaz_add_name_value_str(encode, name, value, &i, "recordPacking",
1121                                srw_pdu->u.request->recordPacking);
1122         yaz_add_name_value_str(encode, name, value, &i, "recordXPath",
1123                                srw_pdu->u.request->recordXPath);
1124         yaz_add_name_value_str(encode, name, value, &i, "stylesheet",
1125                                srw_pdu->u.request->stylesheet);
1126         yaz_add_name_value_int(encode, name, value, &i, "resultSetTTL", 
1127                                srw_pdu->u.request->resultSetTTL);
1128         break;
1129     case Z_SRW_explain_request:
1130         value[i++] = "explain";
1131         yaz_add_name_value_str(encode, name, value, &i, "stylesheet",
1132                                srw_pdu->u.explain_request->stylesheet);
1133         break;
1134     case Z_SRW_scan_request:
1135         value[i++] = "scan";
1136
1137         switch(srw_pdu->u.scan_request->query_type)
1138         {
1139         case Z_SRW_query_type_cql:
1140             yaz_add_name_value_str(encode, name, value, &i, "scanClause",
1141                                    srw_pdu->u.scan_request->scanClause.cql);
1142             break;
1143         case Z_SRW_query_type_pqf:
1144             yaz_add_name_value_str(encode, name, value, &i, "x-pScanClause",
1145                                    srw_pdu->u.scan_request->scanClause.pqf);
1146             break;
1147         case Z_SRW_query_type_xcql:
1148             yaz_add_name_value_str(encode, name, value, &i, "x-cqlScanClause",
1149                                    srw_pdu->u.scan_request->scanClause.xcql);
1150             break;
1151         }
1152         yaz_add_name_value_int(encode, name, value, &i, "responsePosition", 
1153                                srw_pdu->u.scan_request->responsePosition);
1154         yaz_add_name_value_int(encode, name, value, &i, "maximumTerms", 
1155                                srw_pdu->u.scan_request->maximumTerms);
1156         yaz_add_name_value_str(encode, name, value, &i, "stylesheet",
1157                                srw_pdu->u.scan_request->stylesheet);
1158         break;
1159     case Z_SRW_update_request:
1160         value[i++] = "update";
1161         break;
1162     default:
1163         return -1;
1164     }
1165     if (srw_pdu->extra_args)
1166     {
1167         Z_SRW_extra_arg *ea = srw_pdu->extra_args;
1168         for (; ea && i < max_names-1; ea = ea->next)
1169         {
1170             name[i] = ea->name;
1171             value[i] = ea->value;
1172             i++;
1173         }
1174     }
1175     name[i++] = 0;
1176
1177     return 0;
1178 }
1179
1180 int yaz_sru_get_encode(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu,
1181                        ODR encode, const char *charset)
1182 {
1183     char *name[MAX_SRU_PARAMETERS], *value[MAX_SRU_PARAMETERS]; /* definite upper limit for SRU params */
1184     char *uri_args;
1185     char *path;
1186
1187     z_HTTP_header_add_basic_auth(encode, &hreq->headers, 
1188                                  srw_pdu->username, srw_pdu->password);
1189     if (yaz_get_sru_parms(srw_pdu, encode, name, value, MAX_SRU_PARAMETERS))
1190         return -1;
1191     yaz_array_to_uri(&uri_args, encode, name, value);
1192
1193     hreq->method = "GET";
1194     
1195     path = (char *)
1196         odr_malloc(encode, strlen(hreq->path) + strlen(uri_args) + 4);
1197
1198     sprintf(path, "%s?%s", hreq->path, uri_args);
1199     yaz_log(YLOG_DEBUG, "SRU HTTP Get Request %s", path);
1200     hreq->path = path;
1201
1202     z_HTTP_header_add_content_type(encode, &hreq->headers,
1203                                    "text/xml", charset);
1204     return 0;
1205 }
1206
1207 int yaz_sru_post_encode(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu,
1208                         ODR encode, const char *charset)
1209 {
1210     char *name[MAX_SRU_PARAMETERS], *value[MAX_SRU_PARAMETERS]; /* definite upper limit for SRU params */
1211     char *uri_args;
1212
1213     z_HTTP_header_add_basic_auth(encode, &hreq->headers, 
1214                                  srw_pdu->username, srw_pdu->password);
1215     if (yaz_get_sru_parms(srw_pdu, encode, name, value, MAX_SRU_PARAMETERS))
1216         return -1;
1217
1218     yaz_array_to_uri(&uri_args, encode, name, value);
1219
1220     hreq->method = "POST";
1221     
1222     hreq->content_buf = uri_args;
1223     hreq->content_len = strlen(uri_args);
1224
1225     z_HTTP_header_add_content_type(encode, &hreq->headers,
1226                                    "application/x-www-form-urlencoded",
1227                                    charset);
1228     return 0;
1229 }
1230
1231 int yaz_sru_soap_encode(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu,
1232                         ODR odr, const char *charset)
1233 {
1234     Z_SOAP_Handler handlers[3] = {
1235 #if YAZ_HAVE_XML2
1236         {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec},
1237         {YAZ_XMLNS_UPDATE_v0_9, 0, (Z_SOAP_fun) yaz_ucp_codec},
1238 #endif
1239         {0, 0, 0}
1240     };
1241     Z_SOAP *p = (Z_SOAP*) odr_malloc(odr, sizeof(*p));
1242
1243     z_HTTP_header_add_basic_auth(odr, &hreq->headers, 
1244                                  srw_pdu->username, srw_pdu->password);
1245     z_HTTP_header_add_content_type(odr,
1246                                    &hreq->headers,
1247                                    "text/xml", charset);
1248     
1249     z_HTTP_header_add(odr, &hreq->headers,
1250                       "SOAPAction", "\"\"");
1251     p->which = Z_SOAP_generic;
1252     p->u.generic = (Z_SOAP_Generic *) odr_malloc(odr, sizeof(*p->u.generic));
1253     p->u.generic->no = 0;
1254     p->u.generic->ns = 0;
1255     p->u.generic->p = srw_pdu;
1256     p->ns = "http://schemas.xmlsoap.org/soap/envelope/";
1257
1258 #if YAZ_HAVE_XML2
1259     if (srw_pdu->which == Z_SRW_update_request ||
1260         srw_pdu->which == Z_SRW_update_response)
1261         p->u.generic->no = 1; /* second handler */
1262 #endif
1263     return z_soap_codec_enc(odr, &p,
1264                             &hreq->content_buf,
1265                             &hreq->content_len, handlers,
1266                             charset);
1267 }
1268
1269 Z_SRW_recordVersion *yaz_srw_get_record_versions(ODR odr, int num)
1270 {
1271     Z_SRW_recordVersion *ver 
1272         = (Z_SRW_recordVersion *) odr_malloc(odr,num * sizeof(*ver));
1273     int i;
1274     for (i = 0; i < num; ++i)
1275     {
1276         ver[i].versionType = 0;
1277         ver[i].versionValue = 0;
1278     }
1279     return ver;
1280 }
1281
1282 const char *yaz_srw_pack_to_str(int pack)
1283 {
1284     switch(pack)
1285     {
1286     case Z_SRW_recordPacking_string:
1287         return "string";
1288     case Z_SRW_recordPacking_XML:
1289         return "xml";
1290     case Z_SRW_recordPacking_URL:
1291         return "url";
1292     }
1293     return 0;
1294 }
1295
1296 int yaz_srw_str_to_pack(const char *str)
1297 {
1298     if (!yaz_matchstr(str, "string"))
1299         return Z_SRW_recordPacking_string;
1300     if (!yaz_matchstr(str, "xml"))
1301         return Z_SRW_recordPacking_XML;
1302     if (!yaz_matchstr(str, "url"))
1303         return Z_SRW_recordPacking_URL;
1304     return -1;
1305 }
1306
1307 void yaz_encode_sru_extra(Z_SRW_PDU *sr, ODR odr, const char *extra_args)
1308 {
1309     if (extra_args)
1310     {
1311         char **name;
1312         char **val;
1313         Z_SRW_extra_arg **ea = &sr->extra_args;
1314         yaz_uri_to_array(extra_args, odr, &name, &val);
1315
1316         while (*name)
1317         {
1318             *ea = (Z_SRW_extra_arg *) odr_malloc(odr, sizeof(**ea));
1319             (*ea)->name = *name;
1320             (*ea)->value = *val;
1321             ea = &(*ea)->next;
1322             val++;
1323             name++;
1324         }
1325         *ea = 0;
1326     }
1327 }
1328
1329
1330 /*
1331  * Local variables:
1332  * c-basic-offset: 4
1333  * c-file-style: "Stroustrup"
1334  * indent-tabs-mode: nil
1335  * End:
1336  * vim: shiftwidth=4 tabstop=8 expandtab
1337  */
1338