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