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