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