Diagnostic mapping: tweak 10, 108 mappings
[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     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, 10,
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     10, 108,
1018     66, 238,
1019     /* No doubt there are many more */
1020     0
1021 };
1022
1023
1024 int yaz_diag_bib1_to_srw (int code)
1025 {
1026     const int *p = bib1_srw_map;
1027     while (*p)
1028     {
1029         if (code == p[0])
1030             return p[1];
1031         p += 2;
1032     }
1033     return 1;
1034 }
1035
1036 int yaz_diag_srw_to_bib1(int code)
1037 {
1038     /* Check explicit reverse-map first */
1039     const int *p = srw_bib1_map;
1040     while (*p)
1041     {
1042         if (code == p[0])
1043             return p[1];
1044         p += 2;
1045     }
1046
1047     /* Fall back on reverse lookup in main map */
1048     p = bib1_srw_map;
1049     while (*p)
1050     {
1051         if (code == p[1])
1052             return p[0];
1053         p += 2;
1054     }
1055     return 1;
1056 }
1057
1058 void yaz_add_name_value_int(ODR o, char **name, char **value, int *i,
1059                             char *a_name, Odr_int *val)
1060 {
1061     if (val)
1062     {
1063         name[*i] = a_name;
1064         value[*i] = (char *) odr_malloc(o, 40);
1065         sprintf(value[*i], ODR_INT_PRINTF, *val);
1066         (*i)++;
1067     }
1068 }
1069
1070 void yaz_add_name_value_str(ODR o, char **name, char **value,  int *i,
1071                             char *a_name, char *val)
1072 {
1073     if (val)
1074     {
1075         name[*i] = a_name;
1076         value[*i] = val;
1077         (*i)++;
1078     }
1079 }
1080
1081 static int yaz_get_sru_parms(const Z_SRW_PDU *srw_pdu, ODR encode,
1082                              char **name, char **value, int max_names)
1083 {
1084     int i = 0;
1085     yaz_add_name_value_str(encode, name, value, &i, "version", srw_pdu->srw_version);
1086     name[i] = "operation";
1087     switch(srw_pdu->which)
1088     {
1089     case Z_SRW_searchRetrieve_request:
1090         value[i++] = "searchRetrieve";
1091         switch(srw_pdu->u.request->query_type)
1092         {
1093         case Z_SRW_query_type_cql:
1094             yaz_add_name_value_str(encode, name, value, &i, "query",
1095                                    srw_pdu->u.request->query.cql);
1096             break;
1097         case Z_SRW_query_type_pqf:
1098             yaz_add_name_value_str(encode, name, value, &i, "x-pquery",
1099                                    srw_pdu->u.request->query.pqf);
1100             break;
1101         case Z_SRW_query_type_xcql:
1102             yaz_add_name_value_str(encode, name, value, &i, "x-cql",
1103                                    srw_pdu->u.request->query.xcql);
1104             break;
1105         }
1106         switch(srw_pdu->u.request->sort_type)
1107         {
1108         case Z_SRW_sort_type_none:
1109             break;
1110         case Z_SRW_sort_type_sort:            
1111             yaz_add_name_value_str(encode, name, value, &i, "sortKeys",
1112                                    srw_pdu->u.request->sort.sortKeys);
1113             break;
1114         }
1115         yaz_add_name_value_int(encode, name, value, &i, "startRecord", 
1116                                srw_pdu->u.request->startRecord);
1117         yaz_add_name_value_int(encode, name, value, &i, "maximumRecords", 
1118                                srw_pdu->u.request->maximumRecords);
1119         yaz_add_name_value_str(encode, name, value, &i, "recordSchema",
1120                                srw_pdu->u.request->recordSchema);
1121         yaz_add_name_value_str(encode, name, value, &i, "recordPacking",
1122                                srw_pdu->u.request->recordPacking);
1123         yaz_add_name_value_str(encode, name, value, &i, "recordXPath",
1124                                srw_pdu->u.request->recordXPath);
1125         yaz_add_name_value_str(encode, name, value, &i, "stylesheet",
1126                                srw_pdu->u.request->stylesheet);
1127         yaz_add_name_value_int(encode, name, value, &i, "resultSetTTL", 
1128                                srw_pdu->u.request->resultSetTTL);
1129         break;
1130     case Z_SRW_explain_request:
1131         value[i++] = "explain";
1132         yaz_add_name_value_str(encode, name, value, &i, "stylesheet",
1133                                srw_pdu->u.explain_request->stylesheet);
1134         break;
1135     case Z_SRW_scan_request:
1136         value[i++] = "scan";
1137
1138         switch(srw_pdu->u.scan_request->query_type)
1139         {
1140         case Z_SRW_query_type_cql:
1141             yaz_add_name_value_str(encode, name, value, &i, "scanClause",
1142                                    srw_pdu->u.scan_request->scanClause.cql);
1143             break;
1144         case Z_SRW_query_type_pqf:
1145             yaz_add_name_value_str(encode, name, value, &i, "x-pScanClause",
1146                                    srw_pdu->u.scan_request->scanClause.pqf);
1147             break;
1148         case Z_SRW_query_type_xcql:
1149             yaz_add_name_value_str(encode, name, value, &i, "x-cqlScanClause",
1150                                    srw_pdu->u.scan_request->scanClause.xcql);
1151             break;
1152         }
1153         yaz_add_name_value_int(encode, name, value, &i, "responsePosition", 
1154                                srw_pdu->u.scan_request->responsePosition);
1155         yaz_add_name_value_int(encode, name, value, &i, "maximumTerms", 
1156                                srw_pdu->u.scan_request->maximumTerms);
1157         yaz_add_name_value_str(encode, name, value, &i, "stylesheet",
1158                                srw_pdu->u.scan_request->stylesheet);
1159         break;
1160     case Z_SRW_update_request:
1161         value[i++] = "update";
1162         break;
1163     default:
1164         return -1;
1165     }
1166     if (srw_pdu->extra_args)
1167     {
1168         Z_SRW_extra_arg *ea = srw_pdu->extra_args;
1169         for (; ea && i < max_names-1; ea = ea->next)
1170         {
1171             name[i] = ea->name;
1172             value[i] = ea->value;
1173             i++;
1174         }
1175     }
1176     name[i++] = 0;
1177
1178     return 0;
1179 }
1180
1181 int yaz_sru_get_encode(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu,
1182                        ODR encode, const char *charset)
1183 {
1184     char *name[MAX_SRU_PARAMETERS], *value[MAX_SRU_PARAMETERS]; /* definite upper limit for SRU params */
1185     char *uri_args;
1186     char *path;
1187
1188     z_HTTP_header_add_basic_auth(encode, &hreq->headers, 
1189                                  srw_pdu->username, srw_pdu->password);
1190     if (yaz_get_sru_parms(srw_pdu, encode, name, value, MAX_SRU_PARAMETERS))
1191         return -1;
1192     yaz_array_to_uri(&uri_args, encode, name, value);
1193
1194     hreq->method = "GET";
1195     
1196     path = (char *)
1197         odr_malloc(encode, strlen(hreq->path) + strlen(uri_args) + 4);
1198
1199     sprintf(path, "%s?%s", hreq->path, uri_args);
1200     yaz_log(YLOG_DEBUG, "SRU HTTP Get Request %s", path);
1201     hreq->path = path;
1202
1203     z_HTTP_header_add_content_type(encode, &hreq->headers,
1204                                    "text/xml", charset);
1205     return 0;
1206 }
1207
1208 int yaz_sru_post_encode(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu,
1209                         ODR encode, const char *charset)
1210 {
1211     char *name[MAX_SRU_PARAMETERS], *value[MAX_SRU_PARAMETERS]; /* definite upper limit for SRU params */
1212     char *uri_args;
1213
1214     z_HTTP_header_add_basic_auth(encode, &hreq->headers, 
1215                                  srw_pdu->username, srw_pdu->password);
1216     if (yaz_get_sru_parms(srw_pdu, encode, name, value, MAX_SRU_PARAMETERS))
1217         return -1;
1218
1219     yaz_array_to_uri(&uri_args, encode, name, value);
1220
1221     hreq->method = "POST";
1222     
1223     hreq->content_buf = uri_args;
1224     hreq->content_len = strlen(uri_args);
1225
1226     z_HTTP_header_add_content_type(encode, &hreq->headers,
1227                                    "application/x-www-form-urlencoded",
1228                                    charset);
1229     return 0;
1230 }
1231
1232 int yaz_sru_soap_encode(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu,
1233                         ODR odr, const char *charset)
1234 {
1235     Z_SOAP_Handler handlers[3] = {
1236 #if YAZ_HAVE_XML2
1237         {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec},
1238         {YAZ_XMLNS_UPDATE_v0_9, 0, (Z_SOAP_fun) yaz_ucp_codec},
1239 #endif
1240         {0, 0, 0}
1241     };
1242     Z_SOAP *p = (Z_SOAP*) odr_malloc(odr, sizeof(*p));
1243
1244     z_HTTP_header_add_basic_auth(odr, &hreq->headers, 
1245                                  srw_pdu->username, srw_pdu->password);
1246     z_HTTP_header_add_content_type(odr,
1247                                    &hreq->headers,
1248                                    "text/xml", charset);
1249     
1250     z_HTTP_header_add(odr, &hreq->headers,
1251                       "SOAPAction", "\"\"");
1252     p->which = Z_SOAP_generic;
1253     p->u.generic = (Z_SOAP_Generic *) odr_malloc(odr, sizeof(*p->u.generic));
1254     p->u.generic->no = 0;
1255     p->u.generic->ns = 0;
1256     p->u.generic->p = srw_pdu;
1257     p->ns = "http://schemas.xmlsoap.org/soap/envelope/";
1258
1259 #if YAZ_HAVE_XML2
1260     if (srw_pdu->which == Z_SRW_update_request ||
1261         srw_pdu->which == Z_SRW_update_response)
1262         p->u.generic->no = 1; /* second handler */
1263 #endif
1264     return z_soap_codec_enc(odr, &p,
1265                             &hreq->content_buf,
1266                             &hreq->content_len, handlers,
1267                             charset);
1268 }
1269
1270 Z_SRW_recordVersion *yaz_srw_get_record_versions(ODR odr, int num)
1271 {
1272     Z_SRW_recordVersion *ver 
1273         = (Z_SRW_recordVersion *) odr_malloc(odr,num * sizeof(*ver));
1274     int i;
1275     for (i = 0; i < num; ++i)
1276     {
1277         ver[i].versionType = 0;
1278         ver[i].versionValue = 0;
1279     }
1280     return ver;
1281 }
1282
1283 const char *yaz_srw_pack_to_str(int pack)
1284 {
1285     switch(pack)
1286     {
1287     case Z_SRW_recordPacking_string:
1288         return "string";
1289     case Z_SRW_recordPacking_XML:
1290         return "xml";
1291     case Z_SRW_recordPacking_URL:
1292         return "url";
1293     }
1294     return 0;
1295 }
1296
1297 int yaz_srw_str_to_pack(const char *str)
1298 {
1299     if (!yaz_matchstr(str, "string"))
1300         return Z_SRW_recordPacking_string;
1301     if (!yaz_matchstr(str, "xml"))
1302         return Z_SRW_recordPacking_XML;
1303     if (!yaz_matchstr(str, "url"))
1304         return Z_SRW_recordPacking_URL;
1305     return -1;
1306 }
1307
1308 void yaz_encode_sru_extra(Z_SRW_PDU *sr, ODR odr, const char *extra_args)
1309 {
1310     if (extra_args)
1311     {
1312         char **name;
1313         char **val;
1314         Z_SRW_extra_arg **ea = &sr->extra_args;
1315         yaz_uri_to_array(extra_args, odr, &name, &val);
1316
1317         while (*name)
1318         {
1319             *ea = (Z_SRW_extra_arg *) odr_malloc(odr, sizeof(**ea));
1320             (*ea)->name = *name;
1321             (*ea)->value = *val;
1322             ea = &(*ea)->next;
1323             val++;
1324             name++;
1325         }
1326         *ea = 0;
1327     }
1328 }
1329
1330
1331 /*
1332  * Local variables:
1333  * c-basic-offset: 4
1334  * c-file-style: "Stroustrup"
1335  * indent-tabs-mode: nil
1336  * End:
1337  * vim: shiftwidth=4 tabstop=8 expandtab
1338  */
1339