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