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