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