Improve display of MARC records with multi-byte subfield IDs YAZ-695
[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 = (char*) odr_malloc(o, i+1);
250             memcpy(*charset, charset_p, i);
251             (*charset)[i] = '\0';
252         }
253     }
254 }
255
256 int yaz_srw_decode(Z_HTTP_Request *hreq, Z_SRW_PDU **srw_pdu,
257                    Z_SOAP **soap_package, ODR decode, char **charset)
258 {
259     if (!strcmp(hreq->method, "POST"))
260     {
261         const char *content_type = z_HTTP_header_lookup(hreq->headers,
262                                                         "Content-Type");
263         if (content_type &&
264             (!yaz_strcmp_del("text/xml", content_type, "; ") ||
265              !yaz_strcmp_del("application/soap+xml", content_type, "; ") ||
266              !yaz_strcmp_del("text/plain", content_type, "; ")))
267         {
268             char *db = "Default";
269             const char *p0 = hreq->path, *p1;
270             int ret = -1;
271
272             static Z_SOAP_Handler soap_handlers[4] = {
273 #if YAZ_HAVE_XML2
274                 { YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec },
275                 { YAZ_XMLNS_SRU_v1_0, 0, (Z_SOAP_fun) yaz_srw_codec },
276                 { YAZ_XMLNS_UPDATE_v0_9, 0, (Z_SOAP_fun) yaz_ucp_codec },
277 #endif
278                 {0, 0, 0}
279             };
280
281             if (*p0 == '/')
282                 p0++;
283             p1 = strchr(p0, '?');
284             if (!p1)
285                 p1 = p0 + strlen(p0);
286             if (p1 != p0)
287                 db = yaz_decode_sru_dbpath_odr(decode, p0, p1 - p0);
288             grab_charset(decode, content_type, charset);
289
290             ret = z_soap_codec(decode, soap_package,
291                                &hreq->content_buf, &hreq->content_len,
292                                soap_handlers);
293             if (ret == 0 && (*soap_package)->which == Z_SOAP_generic)
294             {
295                 *srw_pdu = (Z_SRW_PDU*) (*soap_package)->u.generic->p;
296                 yaz_srw_decodeauth(*srw_pdu, hreq, 0, 0, decode);
297
298                 if ((*srw_pdu)->which == Z_SRW_searchRetrieve_request &&
299                     (*srw_pdu)->u.request->database == 0)
300                     (*srw_pdu)->u.request->database = db;
301
302                 if ((*srw_pdu)->which == Z_SRW_explain_request &&
303                     (*srw_pdu)->u.explain_request->database == 0)
304                     (*srw_pdu)->u.explain_request->database = db;
305
306                 if ((*srw_pdu)->which == Z_SRW_scan_request &&
307                     (*srw_pdu)->u.scan_request->database == 0)
308                     (*srw_pdu)->u.scan_request->database = db;
309
310                 if ((*srw_pdu)->which == Z_SRW_update_request &&
311                     (*srw_pdu)->u.update_request->database == 0)
312                     (*srw_pdu)->u.update_request->database = db;
313
314                 return 0;
315             }
316             return 1;
317         }
318     }
319     return 2;
320 }
321
322 #if YAZ_HAVE_XML2
323 static int yaz_sru_decode_integer(ODR odr, const char *pname,
324                                   const char *valstr, Odr_int **valp,
325                                   Z_SRW_diagnostic **diag, int *num_diag,
326                                   int min_value)
327 {
328     int ival;
329     if (!valstr)
330         return 0;
331     if (sscanf(valstr, "%d", &ival) != 1)
332     {
333         yaz_add_srw_diagnostic(odr, diag, num_diag,
334                                YAZ_SRW_UNSUPP_PARAMETER_VALUE, pname);
335         return 0;
336     }
337     if (min_value >= 0 && ival < min_value)
338     {
339         yaz_add_srw_diagnostic(odr, diag, num_diag,
340                                YAZ_SRW_UNSUPP_PARAMETER_VALUE, pname);
341         return 0;
342     }
343     *valp = odr_intdup(odr, ival);
344     return 1;
345 }
346 #endif
347
348 /**
349    http://www.loc.gov/z3950/agency/zing/srw/service.html
350 */
351 int yaz_sru_decode(Z_HTTP_Request *hreq, Z_SRW_PDU **srw_pdu,
352                    Z_SOAP **soap_package, ODR decode, char **charset,
353                    Z_SRW_diagnostic **diag, int *num_diag)
354 {
355 #if YAZ_HAVE_XML2
356     static Z_SOAP_Handler soap_handlers[2] = {
357         {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec},
358         {0, 0, 0}
359     };
360 #endif
361     const char *content_type = z_HTTP_header_lookup(hreq->headers,
362                                                     "Content-Type");
363
364     /*
365       SRU GET: ignore content type.
366       SRU POST: we support "application/x-www-form-urlencoded";
367       not  "multipart/form-data" .
368     */
369     if (!strcmp(hreq->method, "GET")
370         ||
371         (!strcmp(hreq->method, "POST") && content_type &&
372          !yaz_strcmp_del("application/x-www-form-urlencoded",
373                          content_type, "; ")))
374     {
375         char *db = "Default";
376         const char *p0 = hreq->path, *p1;
377 #if YAZ_HAVE_XML2
378         const char *operation = 0;
379         char *version = 0;
380         char *query = 0;
381         char *pQuery = 0;
382         char *username = 0;
383         char *password = 0;
384         char *sortKeys = 0;
385         char *stylesheet = 0;
386         char *scanClause = 0;
387         char *pScanClause = 0;
388         char *recordXPath = 0;
389         char *recordSchema = 0;
390         char *recordPacking = "xml";  /* xml packing is default for SRU */
391         char *maximumRecords = 0;
392         char *startRecord = 0;
393         char *maximumTerms = 0;
394         char *responsePosition = 0;
395         Z_SRW_extra_arg *extra_args = 0;
396 #endif
397         char **uri_name;
398         char **uri_val;
399
400         grab_charset(decode, content_type, charset);
401         if (charset && *charset == 0 && !strcmp(hreq->method, "GET"))
402             *charset = "UTF-8";
403
404         if (*p0 == '/')
405             p0++;
406         p1 = strchr(p0, '?');
407         if (!p1)
408             p1 = p0 + strlen(p0);
409         if (p1 != p0)
410             db = yaz_decode_sru_dbpath_odr(decode, p0, p1 - p0);
411         if (!strcmp(hreq->method, "POST"))
412             p1 = hreq->content_buf;
413         yaz_uri_to_array(p1, decode, &uri_name, &uri_val);
414 #if YAZ_HAVE_XML2
415         if (uri_name)
416         {
417             int i;
418             for (i = 0; uri_name[i]; i++)
419             {
420                 char *n = uri_name[i];
421                 char *v = uri_val[i];
422                 if (!strcmp(n, "query"))
423                     query = v;
424                 else if (!strcmp(n, "x-pquery"))
425                     pQuery = v;
426                 else if (!strcmp(n, "x-username"))
427                     username = v;
428                 else if (!strcmp(n, "x-password"))
429                     password = v;
430                 else if (!strcmp(n, "operation"))
431                     operation = v;
432                 else if (!strcmp(n, "stylesheet"))
433                     stylesheet = v;
434                 else if (!strcmp(n, "sortKeys"))
435                     sortKeys = v;
436                 else if (!strcmp(n, "recordXPath"))
437                     recordXPath = v;
438                 else if (!strcmp(n, "recordSchema"))
439                     recordSchema = v;
440                 else if (!strcmp(n, "recordPacking"))
441                     recordPacking = v;
442                 else if (!strcmp(n, "version"))
443                     version = v;
444                 else if (!strcmp(n, "scanClause"))
445                     scanClause = v;
446                 else if (!strcmp(n, "x-pScanClause"))
447                     pScanClause = v;
448                 else if (!strcmp(n, "maximumRecords"))
449                     maximumRecords = v;
450                 else if (!strcmp(n, "startRecord"))
451                     startRecord = v;
452                 else if (!strcmp(n, "maximumTerms"))
453                     maximumTerms = v;
454                 else if (!strcmp(n, "responsePosition"))
455                     responsePosition = v;
456                 else if (!strcmp(n, "extraRequestData"))
457                     ; /* ignoring extraRequestData */
458                 else if (n[0] == 'x' && n[1] == '-')
459                 {
460                     Z_SRW_extra_arg **l = &extra_args;
461                     while (*l)
462                         l = &(*l)->next;
463                     *l = (Z_SRW_extra_arg *) odr_malloc(decode, sizeof(**l));
464                     (*l)->name = odr_strdup(decode, n);
465                     (*l)->value = odr_strdup(decode, v);
466                     (*l)->next = 0;
467                 }
468                 else
469                 {
470                     if (*num_diag < 10)
471                         yaz_add_srw_diagnostic(decode, diag, num_diag,
472                                                YAZ_SRW_UNSUPP_PARAMETER, n);
473                 }
474             }
475         }
476         if (!version)
477         {
478             if (uri_name)
479                 yaz_add_srw_diagnostic(
480                     decode, diag, num_diag,
481                     YAZ_SRW_MANDATORY_PARAMETER_NOT_SUPPLIED, "version");
482             version = "1.1";
483         }
484
485         version = yaz_negotiate_sru_version(version);
486
487         if (!version)
488         {   /* negotiation failed. */
489             yaz_add_srw_diagnostic(decode, diag, num_diag,
490                                    YAZ_SRW_UNSUPP_VERSION, "1.2");
491             version = "1.2";
492         }
493
494         if (!operation)
495         {
496             if (uri_name)
497                 yaz_add_srw_diagnostic(
498                     decode, diag, num_diag,
499                     YAZ_SRW_MANDATORY_PARAMETER_NOT_SUPPLIED, "operation");
500             operation = "explain";
501         }
502         if (!strcmp(operation, "searchRetrieve"))
503         {
504             Z_SRW_PDU *sr = yaz_srw_get(decode, Z_SRW_searchRetrieve_request);
505
506             sr->srw_version = version;
507             sr->extra_args = extra_args;
508             *srw_pdu = sr;
509             yaz_srw_decodeauth(sr, hreq, username, password, decode);
510             if (query)
511             {
512                 sr->u.request->query_type = Z_SRW_query_type_cql;
513                 sr->u.request->query.cql = query;
514             }
515             else if (pQuery)
516             {
517                 sr->u.request->query_type = Z_SRW_query_type_pqf;
518                 sr->u.request->query.pqf = pQuery;
519             }
520             else
521                 yaz_add_srw_diagnostic(
522                     decode, diag, num_diag,
523                     YAZ_SRW_MANDATORY_PARAMETER_NOT_SUPPLIED, "query");
524
525             if (sortKeys)
526             {
527                 sr->u.request->sort_type = Z_SRW_sort_type_sort;
528                 sr->u.request->sort.sortKeys = sortKeys;
529             }
530             sr->u.request->recordXPath = recordXPath;
531             sr->u.request->recordSchema = recordSchema;
532             sr->u.request->recordPacking = recordPacking;
533             sr->u.request->stylesheet = stylesheet;
534
535             yaz_sru_decode_integer(decode, "maximumRecords", maximumRecords,
536                                    &sr->u.request->maximumRecords,
537                                    diag, num_diag, 0);
538
539             yaz_sru_decode_integer(decode, "startRecord", startRecord,
540                                    &sr->u.request->startRecord,
541                                    diag, num_diag, 1);
542
543             sr->u.request->database = db;
544
545             (*soap_package) = (Z_SOAP *)
546                 odr_malloc(decode, sizeof(**soap_package));
547             (*soap_package)->which = Z_SOAP_generic;
548
549             (*soap_package)->u.generic = (Z_SOAP_Generic *)
550                 odr_malloc(decode, sizeof(*(*soap_package)->u.generic));
551
552             (*soap_package)->u.generic->p = sr;
553             (*soap_package)->u.generic->ns = soap_handlers[0].ns;
554             (*soap_package)->u.generic->no = 0;
555
556             (*soap_package)->ns = "SRU";
557
558             return 0;
559         }
560         else if (!strcmp(operation, "explain"))
561         {
562             /* Transfer SRU explain parameters to common struct */
563             /* http://www.loc.gov/z3950/agency/zing/srw/explain.html */
564             Z_SRW_PDU *sr = yaz_srw_get(decode, Z_SRW_explain_request);
565
566             sr->srw_version = version;
567             sr->extra_args = extra_args;
568             yaz_srw_decodeauth(sr, hreq, username, password, decode);
569             *srw_pdu = sr;
570             sr->u.explain_request->recordPacking = recordPacking;
571             sr->u.explain_request->database = db;
572
573             sr->u.explain_request->stylesheet = stylesheet;
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, "scan"))
591         {
592             /* Transfer SRU scan parameters to common struct */
593             /* http://www.loc.gov/z3950/agency/zing/srw/scan.html */
594             Z_SRW_PDU *sr = yaz_srw_get(decode, Z_SRW_scan_request);
595
596             sr->srw_version = version;
597             sr->extra_args = extra_args;
598             *srw_pdu = sr;
599             yaz_srw_decodeauth(sr, hreq, username, password, decode);
600
601             if (scanClause)
602             {
603                 sr->u.scan_request->query_type = Z_SRW_query_type_cql;
604                 sr->u.scan_request->scanClause.cql = scanClause;
605             }
606             else if (pScanClause)
607             {
608                 sr->u.scan_request->query_type = Z_SRW_query_type_pqf;
609                 sr->u.scan_request->scanClause.pqf = pScanClause;
610             }
611             else
612                 yaz_add_srw_diagnostic(
613                     decode, diag, num_diag,
614                     YAZ_SRW_MANDATORY_PARAMETER_NOT_SUPPLIED, "scanClause");
615             sr->u.scan_request->database = db;
616
617             yaz_sru_decode_integer(decode, "maximumTerms",
618                                    maximumTerms,
619                                    &sr->u.scan_request->maximumTerms,
620                                    diag, num_diag, 0);
621
622             yaz_sru_decode_integer(decode, "responsePosition",
623                                    responsePosition,
624                                    &sr->u.scan_request->responsePosition,
625                                    diag, num_diag, 0);
626
627             sr->u.scan_request->stylesheet = stylesheet;
628
629             (*soap_package) = (Z_SOAP *)
630                 odr_malloc(decode, sizeof(**soap_package));
631             (*soap_package)->which = Z_SOAP_generic;
632
633             (*soap_package)->u.generic = (Z_SOAP_Generic *)
634                 odr_malloc(decode, sizeof(*(*soap_package)->u.generic));
635
636             (*soap_package)->u.generic->p = sr;
637             (*soap_package)->u.generic->ns = soap_handlers[0].ns;
638             (*soap_package)->u.generic->no = 0;
639
640             (*soap_package)->ns = "SRU";
641
642             return 0;
643         }
644         else
645         {
646             /* unsupported operation ... */
647             /* Act as if we received a explain request and throw diagnostic. */
648
649             Z_SRW_PDU *sr = yaz_srw_get(decode, Z_SRW_explain_request);
650
651             sr->srw_version = version;
652             *srw_pdu = sr;
653             sr->u.explain_request->recordPacking = recordPacking;
654             sr->u.explain_request->database = db;
655
656             sr->u.explain_request->stylesheet = stylesheet;
657
658             (*soap_package) = (Z_SOAP *)
659                 odr_malloc(decode, sizeof(**soap_package));
660             (*soap_package)->which = Z_SOAP_generic;
661
662             (*soap_package)->u.generic = (Z_SOAP_Generic *)
663                 odr_malloc(decode, sizeof(*(*soap_package)->u.generic));
664
665             (*soap_package)->u.generic->p = sr;
666             (*soap_package)->u.generic->ns = soap_handlers[0].ns;
667             (*soap_package)->u.generic->no = 0;
668
669             (*soap_package)->ns = "SRU";
670
671             yaz_add_srw_diagnostic(decode, diag, num_diag,
672                                    YAZ_SRW_UNSUPP_OPERATION, operation);
673             return 0;
674         }
675 #else
676         return 1;
677 #endif
678     }
679     return 2;
680 }
681
682 Z_SRW_extra_record *yaz_srw_get_extra_record(ODR o)
683 {
684     Z_SRW_extra_record *res = (Z_SRW_extra_record *)
685         odr_malloc(o, sizeof(*res));
686
687     res->extraRecordData_buf = 0;
688     res->extraRecordData_len = 0;
689     res->recordIdentifier = 0;
690     return res;
691 }
692
693
694 Z_SRW_record *yaz_srw_get_records(ODR o, int n)
695 {
696     Z_SRW_record *res = (Z_SRW_record *) odr_malloc(o, n * sizeof(*res));
697     int i;
698
699     for (i = 0; i<n; i++)
700     {
701         res[i].recordSchema = 0;
702         res[i].recordPacking = Z_SRW_recordPacking_string;
703         res[i].recordData_buf = 0;
704         res[i].recordData_len = 0;
705         res[i].recordPosition = 0;
706     }
707     return res;
708 }
709
710 Z_SRW_record *yaz_srw_get_record(ODR o)
711 {
712     return yaz_srw_get_records(o, 1);
713 }
714
715 static Z_SRW_PDU *yaz_srw_get_core_ver(ODR o, const char *version)
716 {
717     Z_SRW_PDU *p = (Z_SRW_PDU *) odr_malloc(o, sizeof(*p));
718     p->srw_version = odr_strdup(o, version);
719     p->username = 0;
720     p->password = 0;
721     p->extra_args = 0;
722     p->extraResponseData_buf = 0;
723     p->extraResponseData_len = 0;
724     return p;
725 }
726
727 Z_SRW_PDU *yaz_srw_get_core_v_1_1(ODR o)
728 {
729     return yaz_srw_get_core_ver(o, "1.1");
730 }
731
732 Z_SRW_PDU *yaz_srw_get(ODR o, int which)
733 {
734     return yaz_srw_get_pdu(o, which, "1.1");
735 }
736
737 Z_SRW_PDU *yaz_srw_get_pdu(ODR o, int which, const char *version)
738 {
739     Z_SRW_PDU *sr = yaz_srw_get_core_ver(o, version);
740
741     sr->which = which;
742     switch(which)
743     {
744     case Z_SRW_searchRetrieve_request:
745         sr->u.request = (Z_SRW_searchRetrieveRequest *)
746             odr_malloc(o, sizeof(*sr->u.request));
747         sr->u.request->query_type = Z_SRW_query_type_cql;
748         sr->u.request->query.cql = 0;
749         sr->u.request->sort_type = Z_SRW_sort_type_none;
750         sr->u.request->sort.none = 0;
751         sr->u.request->startRecord = 0;
752         sr->u.request->maximumRecords = 0;
753         sr->u.request->recordSchema = 0;
754         sr->u.request->recordPacking = 0;
755         sr->u.request->recordXPath = 0;
756         sr->u.request->database = 0;
757         sr->u.request->resultSetTTL = 0;
758         sr->u.request->stylesheet = 0;
759         sr->u.request->facetList = 0;
760         break;
761     case Z_SRW_searchRetrieve_response:
762         sr->u.response = (Z_SRW_searchRetrieveResponse *)
763             odr_malloc(o, sizeof(*sr->u.response));
764         sr->u.response->numberOfRecords = 0;
765         sr->u.response->resultSetId = 0;
766         sr->u.response->resultSetIdleTime = 0;
767         sr->u.response->records = 0;
768         sr->u.response->num_records = 0;
769         sr->u.response->diagnostics = 0;
770         sr->u.response->num_diagnostics = 0;
771         sr->u.response->nextRecordPosition = 0;
772         sr->u.response->extra_records = 0;
773         sr->u.response->facetList = 0;
774         sr->u.response->suggestions = 0;
775         break;
776     case Z_SRW_explain_request:
777         sr->u.explain_request = (Z_SRW_explainRequest *)
778             odr_malloc(o, sizeof(*sr->u.explain_request));
779         sr->u.explain_request->recordPacking = 0;
780         sr->u.explain_request->database = 0;
781         sr->u.explain_request->stylesheet = 0;
782         break;
783     case Z_SRW_explain_response:
784         sr->u.explain_response = (Z_SRW_explainResponse *)
785             odr_malloc(o, sizeof(*sr->u.explain_response));
786         sr->u.explain_response->record.recordData_buf = 0;
787         sr->u.explain_response->record.recordData_len = 0;
788         sr->u.explain_response->record.recordSchema = 0;
789         sr->u.explain_response->record.recordPosition = 0;
790         sr->u.explain_response->record.recordPacking =
791             Z_SRW_recordPacking_string;
792         sr->u.explain_response->diagnostics = 0;
793         sr->u.explain_response->num_diagnostics = 0;
794         sr->u.explain_response->extra_record = 0;
795         break;
796     case Z_SRW_scan_request:
797         sr->u.scan_request = (Z_SRW_scanRequest *)
798             odr_malloc(o, sizeof(*sr->u.scan_request));
799         sr->u.scan_request->database = 0;
800         sr->u.scan_request->stylesheet = 0;
801         sr->u.scan_request->maximumTerms = 0;
802         sr->u.scan_request->responsePosition = 0;
803         sr->u.scan_request->query_type = Z_SRW_query_type_cql;
804         sr->u.scan_request->scanClause.cql = 0;
805         break;
806     case Z_SRW_scan_response:
807         sr->u.scan_response = (Z_SRW_scanResponse *)
808             odr_malloc(o, sizeof(*sr->u.scan_response));
809         sr->u.scan_response->terms = 0;
810         sr->u.scan_response->num_terms = 0;
811         sr->u.scan_response->diagnostics = 0;
812         sr->u.scan_response->num_diagnostics = 0;
813         break;
814     case Z_SRW_update_request:
815         sr->u.update_request = (Z_SRW_updateRequest *)
816             odr_malloc(o, sizeof(*sr->u.update_request));
817         sr->u.update_request->database = 0;
818         sr->u.update_request->stylesheet = 0;
819         sr->u.update_request->record = 0;
820         sr->u.update_request->recordId = 0;
821         sr->u.update_request->recordVersions = 0;
822         sr->u.update_request->num_recordVersions = 0;
823         sr->u.update_request->extra_record = 0;
824         sr->u.update_request->extraRequestData_buf = 0;
825         sr->u.update_request->extraRequestData_len = 0;
826         sr->u.request->database = 0;
827         break;
828     case Z_SRW_update_response:
829         sr->u.update_response = (Z_SRW_updateResponse *)
830             odr_malloc(o, sizeof(*sr->u.update_response));
831         sr->u.update_response->operationStatus = 0;
832         sr->u.update_response->recordId = 0;
833         sr->u.update_response->recordVersions = 0;
834         sr->u.update_response->num_recordVersions = 0;
835         sr->u.update_response->record = 0;
836         sr->u.update_response->extra_record = 0;
837         sr->u.update_response->extraResponseData_buf = 0;
838         sr->u.update_response->extraResponseData_len = 0;
839         sr->u.update_response->diagnostics = 0;
840         sr->u.update_response->num_diagnostics = 0;
841     }
842     return sr;
843 }
844
845 void yaz_add_name_value_int(ODR o, char **name, char **value, int *i,
846                             char *a_name, Odr_int *val)
847 {
848     if (val)
849     {
850         name[*i] = a_name;
851         value[*i] = (char *) odr_malloc(o, 40);
852         sprintf(value[*i], ODR_INT_PRINTF, *val);
853         (*i)++;
854     }
855 }
856
857 void yaz_add_name_value_str(ODR o, char **name, char **value,  int *i,
858                             char *a_name, char *val)
859 {
860     if (val)
861     {
862         name[*i] = a_name;
863         value[*i] = val;
864         (*i)++;
865     }
866 }
867
868 static int yaz_get_sru_parms(const Z_SRW_PDU *srw_pdu, ODR encode,
869                              char **name, char **value, int max_names)
870 {
871     int i = 0;
872     yaz_add_name_value_str(encode, name, value, &i, "version", srw_pdu->srw_version);
873     name[i] = "operation";
874     switch(srw_pdu->which)
875     {
876     case Z_SRW_searchRetrieve_request:
877         value[i++] = "searchRetrieve";
878         switch(srw_pdu->u.request->query_type)
879         {
880         case Z_SRW_query_type_cql:
881             yaz_add_name_value_str(encode, name, value, &i, "query",
882                                    srw_pdu->u.request->query.cql);
883             break;
884         case Z_SRW_query_type_pqf:
885             yaz_add_name_value_str(encode, name, value, &i, "x-pquery",
886                                    srw_pdu->u.request->query.pqf);
887             break;
888         case Z_SRW_query_type_xcql:
889             yaz_add_name_value_str(encode, name, value, &i, "x-cql",
890                                    srw_pdu->u.request->query.xcql);
891             break;
892         }
893         switch(srw_pdu->u.request->sort_type)
894         {
895         case Z_SRW_sort_type_none:
896             break;
897         case Z_SRW_sort_type_sort:
898             yaz_add_name_value_str(encode, name, value, &i, "sortKeys",
899                                    srw_pdu->u.request->sort.sortKeys);
900             break;
901         }
902         yaz_add_name_value_int(encode, name, value, &i, "startRecord",
903                                srw_pdu->u.request->startRecord);
904         yaz_add_name_value_int(encode, name, value, &i, "maximumRecords",
905                                srw_pdu->u.request->maximumRecords);
906         yaz_add_name_value_str(encode, name, value, &i, "recordSchema",
907                                srw_pdu->u.request->recordSchema);
908         yaz_add_name_value_str(encode, name, value, &i, "recordPacking",
909                                srw_pdu->u.request->recordPacking);
910         yaz_add_name_value_str(encode, name, value, &i, "recordXPath",
911                                srw_pdu->u.request->recordXPath);
912         yaz_add_name_value_str(encode, name, value, &i, "stylesheet",
913                                srw_pdu->u.request->stylesheet);
914         yaz_add_name_value_int(encode, name, value, &i, "resultSetTTL",
915                                srw_pdu->u.request->resultSetTTL);
916         break;
917     case Z_SRW_explain_request:
918         value[i++] = "explain";
919         yaz_add_name_value_str(encode, name, value, &i, "stylesheet",
920                                srw_pdu->u.explain_request->stylesheet);
921         break;
922     case Z_SRW_scan_request:
923         value[i++] = "scan";
924
925         switch(srw_pdu->u.scan_request->query_type)
926         {
927         case Z_SRW_query_type_cql:
928             yaz_add_name_value_str(encode, name, value, &i, "scanClause",
929                                    srw_pdu->u.scan_request->scanClause.cql);
930             break;
931         case Z_SRW_query_type_pqf:
932             yaz_add_name_value_str(encode, name, value, &i, "x-pScanClause",
933                                    srw_pdu->u.scan_request->scanClause.pqf);
934             break;
935         case Z_SRW_query_type_xcql:
936             yaz_add_name_value_str(encode, name, value, &i, "x-cqlScanClause",
937                                    srw_pdu->u.scan_request->scanClause.xcql);
938             break;
939         }
940         yaz_add_name_value_int(encode, name, value, &i, "responsePosition",
941                                srw_pdu->u.scan_request->responsePosition);
942         yaz_add_name_value_int(encode, name, value, &i, "maximumTerms",
943                                srw_pdu->u.scan_request->maximumTerms);
944         yaz_add_name_value_str(encode, name, value, &i, "stylesheet",
945                                srw_pdu->u.scan_request->stylesheet);
946         break;
947     case Z_SRW_update_request:
948         value[i++] = "update";
949         break;
950     default:
951         return -1;
952     }
953     if (srw_pdu->extra_args)
954     {
955         Z_SRW_extra_arg *ea = srw_pdu->extra_args;
956         for (; ea && i < max_names-1; ea = ea->next)
957         {
958             name[i] = ea->name;
959             value[i] = ea->value;
960             i++;
961         }
962     }
963     name[i++] = 0;
964
965     return 0;
966 }
967
968 int yaz_sru_get_encode(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu,
969                        ODR encode, const char *charset)
970 {
971     char *name[MAX_SRU_PARAMETERS], *value[MAX_SRU_PARAMETERS]; /* definite upper limit for SRU params */
972     char *uri_args;
973     char *path;
974
975     z_HTTP_header_add_basic_auth(encode, &hreq->headers,
976                                  srw_pdu->username, srw_pdu->password);
977     if (yaz_get_sru_parms(srw_pdu, encode, name, value, MAX_SRU_PARAMETERS))
978         return -1;
979     yaz_array_to_uri(&uri_args, encode, name, value);
980
981     hreq->method = "GET";
982
983     path = (char *)
984         odr_malloc(encode, strlen(hreq->path) + strlen(uri_args) + 4);
985
986     sprintf(path, "%s?%s", hreq->path, uri_args);
987     yaz_log(YLOG_DEBUG, "SRU HTTP Get Request %s", path);
988     hreq->path = path;
989
990     z_HTTP_header_add_content_type(encode, &hreq->headers,
991                                    "text/xml", charset);
992     return 0;
993 }
994
995 int yaz_sru_post_encode(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu,
996                         ODR encode, const char *charset)
997 {
998     char *name[MAX_SRU_PARAMETERS], *value[MAX_SRU_PARAMETERS]; /* definite upper limit for SRU params */
999     char *uri_args;
1000
1001     z_HTTP_header_add_basic_auth(encode, &hreq->headers,
1002                                  srw_pdu->username, srw_pdu->password);
1003     if (yaz_get_sru_parms(srw_pdu, encode, name, value, MAX_SRU_PARAMETERS))
1004         return -1;
1005
1006     yaz_array_to_uri(&uri_args, encode, name, value);
1007
1008     hreq->method = "POST";
1009
1010     hreq->content_buf = uri_args;
1011     hreq->content_len = strlen(uri_args);
1012
1013     z_HTTP_header_add_content_type(encode, &hreq->headers,
1014                                    "application/x-www-form-urlencoded",
1015                                    charset);
1016     return 0;
1017 }
1018
1019 int yaz_sru_soap_encode(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu,
1020                         ODR odr, const char *charset)
1021 {
1022     Z_SOAP_Handler handlers[3] = {
1023 #if YAZ_HAVE_XML2
1024         {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec},
1025         {YAZ_XMLNS_UPDATE_v0_9, 0, (Z_SOAP_fun) yaz_ucp_codec},
1026 #endif
1027         {0, 0, 0}
1028     };
1029     Z_SOAP *p = (Z_SOAP*) odr_malloc(odr, sizeof(*p));
1030
1031     z_HTTP_header_add_basic_auth(odr, &hreq->headers,
1032                                  srw_pdu->username, srw_pdu->password);
1033     z_HTTP_header_add_content_type(odr,
1034                                    &hreq->headers,
1035                                    "text/xml", charset);
1036
1037     z_HTTP_header_add(odr, &hreq->headers,
1038                       "SOAPAction", "\"\"");
1039     p->which = Z_SOAP_generic;
1040     p->u.generic = (Z_SOAP_Generic *) odr_malloc(odr, sizeof(*p->u.generic));
1041     p->u.generic->no = 0;
1042     p->u.generic->ns = 0;
1043     p->u.generic->p = srw_pdu;
1044     p->ns = "http://schemas.xmlsoap.org/soap/envelope/";
1045
1046 #if YAZ_HAVE_XML2
1047     if (srw_pdu->which == Z_SRW_update_request ||
1048         srw_pdu->which == Z_SRW_update_response)
1049         p->u.generic->no = 1; /* second handler */
1050 #endif
1051     return z_soap_codec_enc(odr, &p,
1052                             &hreq->content_buf,
1053                             &hreq->content_len, handlers,
1054                             charset);
1055 }
1056
1057 Z_SRW_recordVersion *yaz_srw_get_record_versions(ODR odr, int num)
1058 {
1059     Z_SRW_recordVersion *ver
1060         = (Z_SRW_recordVersion *) odr_malloc(odr,num * sizeof(*ver));
1061     int i;
1062     for (i = 0; i < num; ++i)
1063     {
1064         ver[i].versionType = 0;
1065         ver[i].versionValue = 0;
1066     }
1067     return ver;
1068 }
1069
1070 const char *yaz_srw_pack_to_str(int pack)
1071 {
1072     switch(pack)
1073     {
1074     case Z_SRW_recordPacking_string:
1075         return "string";
1076     case Z_SRW_recordPacking_XML:
1077         return "xml";
1078     case Z_SRW_recordPacking_URL:
1079         return "url";
1080     }
1081     return 0;
1082 }
1083
1084 int yaz_srw_str_to_pack(const char *str)
1085 {
1086     if (!yaz_matchstr(str, "string"))
1087         return Z_SRW_recordPacking_string;
1088     if (!yaz_matchstr(str, "xml"))
1089         return Z_SRW_recordPacking_XML;
1090     if (!yaz_matchstr(str, "url"))
1091         return Z_SRW_recordPacking_URL;
1092     return -1;
1093 }
1094
1095 void yaz_encode_sru_extra(Z_SRW_PDU *sr, ODR odr, const char *extra_args)
1096 {
1097     if (extra_args)
1098     {
1099         char **name;
1100         char **val;
1101         Z_SRW_extra_arg **ea = &sr->extra_args;
1102         yaz_uri_to_array(extra_args, odr, &name, &val);
1103
1104         /** append rather than override */
1105         while (*ea)
1106             ea = &(*ea)->next;
1107         while (*name)
1108         {
1109             *ea = (Z_SRW_extra_arg *) odr_malloc(odr, sizeof(**ea));
1110             (*ea)->name = *name;
1111             (*ea)->value = *val;
1112             ea = &(*ea)->next;
1113             val++;
1114             name++;
1115         }
1116         *ea = 0;
1117     }
1118 }
1119
1120
1121 /*
1122  * Local variables:
1123  * c-basic-offset: 4
1124  * c-file-style: "Stroustrup"
1125  * indent-tabs-mode: nil
1126  * End:
1127  * vim: shiftwidth=4 tabstop=8 expandtab
1128  */
1129