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