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