Add HTTP Basic authentication to SRU/GET, SRU/POST and SRW requests.
[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.63 2007-09-06 17:10:35 mike 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
598         version = yaz_negotiate_sru_version(version);
599
600         if (!version)
601         {   /* negotiation failed. */
602             yaz_add_srw_diagnostic(decode, diag, num_diag,
603                                    YAZ_SRW_UNSUPP_VERSION, "1.2");
604             version = "1.2";
605         }
606         
607         if (!operation)
608         {
609             if (uri_name)
610                 yaz_add_srw_diagnostic(
611                     decode, diag, num_diag, 
612                     YAZ_SRW_MANDATORY_PARAMETER_NOT_SUPPLIED, "operation");
613             operation = "explain";
614         }
615         if (!strcmp(operation, "searchRetrieve"))
616         {
617             Z_SRW_PDU *sr = yaz_srw_get(decode, Z_SRW_searchRetrieve_request);
618
619             sr->srw_version = version;
620             *srw_pdu = sr;
621             yaz_srw_decodeauth(sr, hreq, username, password, decode);
622             if (query)
623             {
624                 sr->u.request->query_type = Z_SRW_query_type_cql;
625                 sr->u.request->query.cql = query;
626             }
627             else if (pQuery)
628             {
629                 sr->u.request->query_type = Z_SRW_query_type_pqf;
630                 sr->u.request->query.pqf = pQuery;
631             }
632             else
633                 yaz_add_srw_diagnostic(
634                     decode, diag, num_diag, 
635                     YAZ_SRW_MANDATORY_PARAMETER_NOT_SUPPLIED, "query");
636
637             if (sortKeys)
638             {
639                 sr->u.request->sort_type = Z_SRW_sort_type_sort;
640                 sr->u.request->sort.sortKeys = sortKeys;
641             }
642             sr->u.request->recordXPath = recordXPath;
643             sr->u.request->recordSchema = recordSchema;
644             sr->u.request->recordPacking = recordPacking;
645             sr->u.request->stylesheet = stylesheet;
646
647             yaz_sru_decode_integer(decode, "maximumRecords", maximumRecords, 
648                                    &sr->u.request->maximumRecords, 
649                                    diag, num_diag, 0);
650             
651             yaz_sru_decode_integer(decode, "startRecord", startRecord, 
652                                    &sr->u.request->startRecord,
653                                    diag, num_diag, 1);
654
655             sr->u.request->database = db;
656
657             (*soap_package) = (Z_SOAP *)
658                 odr_malloc(decode, sizeof(**soap_package));
659             (*soap_package)->which = Z_SOAP_generic;
660             
661             (*soap_package)->u.generic = (Z_SOAP_Generic *)
662                 odr_malloc(decode, sizeof(*(*soap_package)->u.generic));
663             
664             (*soap_package)->u.generic->p = sr;
665             (*soap_package)->u.generic->ns = soap_handlers[0].ns;
666             (*soap_package)->u.generic->no = 0;
667             
668             (*soap_package)->ns = "SRU";
669
670             return 0;
671         }
672         else if (!strcmp(operation, "explain"))
673         {
674             /* Transfer SRU explain parameters to common struct */
675             /* http://www.loc.gov/z3950/agency/zing/srw/explain.html */
676             Z_SRW_PDU *sr = yaz_srw_get(decode, Z_SRW_explain_request);
677
678             sr->srw_version = version;
679             yaz_srw_decodeauth(sr, hreq, username, password, decode);
680             *srw_pdu = sr;
681             sr->u.explain_request->recordPacking = recordPacking;
682             sr->u.explain_request->database = db;
683
684             sr->u.explain_request->stylesheet = stylesheet;
685
686             (*soap_package) = (Z_SOAP *)
687                 odr_malloc(decode, sizeof(**soap_package));
688             (*soap_package)->which = Z_SOAP_generic;
689             
690             (*soap_package)->u.generic = (Z_SOAP_Generic *)
691                 odr_malloc(decode, sizeof(*(*soap_package)->u.generic));
692             
693             (*soap_package)->u.generic->p = sr;
694             (*soap_package)->u.generic->ns = soap_handlers[0].ns;
695             (*soap_package)->u.generic->no = 0;
696             
697             (*soap_package)->ns = "SRU";
698
699             return 0;
700         }
701         else if (!strcmp(operation, "scan"))
702         {
703             /* Transfer SRU scan parameters to common struct */
704             /* http://www.loc.gov/z3950/agency/zing/srw/scan.html */
705             Z_SRW_PDU *sr = yaz_srw_get(decode, Z_SRW_scan_request);
706
707             sr->srw_version = version;
708             *srw_pdu = sr;
709             yaz_srw_decodeauth(sr, hreq, username, password, decode);
710
711             if (scanClause)
712             {
713                 sr->u.scan_request->query_type = Z_SRW_query_type_cql;
714                 sr->u.scan_request->scanClause.cql = scanClause;
715             }
716             else if (pScanClause)
717             {
718                 sr->u.scan_request->query_type = Z_SRW_query_type_pqf;
719                 sr->u.scan_request->scanClause.pqf = pScanClause;
720             }
721             else
722                 yaz_add_srw_diagnostic(
723                     decode, diag, num_diag, 
724                     YAZ_SRW_MANDATORY_PARAMETER_NOT_SUPPLIED, "scanClause");
725             sr->u.scan_request->database = db;
726             
727             yaz_sru_decode_integer(decode, "maximumTerms",
728                                    maximumTerms, 
729                                    &sr->u.scan_request->maximumTerms,
730                                    diag, num_diag, 0);
731             
732             yaz_sru_decode_integer(decode, "responsePosition",
733                                    responsePosition, 
734                                    &sr->u.scan_request->responsePosition,
735                                    diag, num_diag, 0);
736
737             sr->u.scan_request->stylesheet = stylesheet;
738
739             (*soap_package) = (Z_SOAP *)
740                 odr_malloc(decode, sizeof(**soap_package));
741             (*soap_package)->which = Z_SOAP_generic;
742             
743             (*soap_package)->u.generic = (Z_SOAP_Generic *)
744                 odr_malloc(decode, sizeof(*(*soap_package)->u.generic));
745             
746             (*soap_package)->u.generic->p = sr;
747             (*soap_package)->u.generic->ns = soap_handlers[0].ns;
748             (*soap_package)->u.generic->no = 0;
749             
750             (*soap_package)->ns = "SRU";
751
752             return 0;
753         }
754         else
755         {
756             /* unsupported operation ... */
757             /* Act as if we received a explain request and throw diagnostic. */
758
759             Z_SRW_PDU *sr = yaz_srw_get(decode, Z_SRW_explain_request);
760
761             sr->srw_version = version;
762             *srw_pdu = sr;
763             sr->u.explain_request->recordPacking = recordPacking;
764             sr->u.explain_request->database = db;
765
766             sr->u.explain_request->stylesheet = stylesheet;
767
768             (*soap_package) = (Z_SOAP *)
769                 odr_malloc(decode, sizeof(**soap_package));
770             (*soap_package)->which = Z_SOAP_generic;
771             
772             (*soap_package)->u.generic = (Z_SOAP_Generic *)
773                 odr_malloc(decode, sizeof(*(*soap_package)->u.generic));
774             
775             (*soap_package)->u.generic->p = sr;
776             (*soap_package)->u.generic->ns = soap_handlers[0].ns;
777             (*soap_package)->u.generic->no = 0;
778             
779             (*soap_package)->ns = "SRU";
780
781             yaz_add_srw_diagnostic(decode, diag, num_diag, 
782                                    YAZ_SRW_UNSUPP_OPERATION, operation);
783             return 0;
784         }
785 #endif
786         return 1;
787     }
788     return 2;
789 }
790
791 Z_SRW_extra_record *yaz_srw_get_extra_record(ODR o)
792 {
793     Z_SRW_extra_record *res = (Z_SRW_extra_record *)
794         odr_malloc(o, sizeof(*res));
795
796     res->extraRecordData_buf = 0;
797     res->extraRecordData_len = 0;
798     res->recordIdentifier = 0;
799     return res;
800 }
801
802
803 Z_SRW_record *yaz_srw_get_records(ODR o, int n)
804 {
805     Z_SRW_record *res = (Z_SRW_record *) odr_malloc(o, n * sizeof(*res));
806     int i;
807
808     for (i = 0; i<n; i++)
809     {
810         res[i].recordSchema = 0;
811         res[i].recordPacking = Z_SRW_recordPacking_string;
812         res[i].recordData_buf = 0;
813         res[i].recordData_len = 0;
814         res[i].recordPosition = 0;
815     }
816     return res;
817 }
818
819 Z_SRW_record *yaz_srw_get_record(ODR o)
820 {
821     return yaz_srw_get_records(o, 1);
822 }
823
824 static Z_SRW_PDU *yaz_srw_get_core_ver(ODR o, const char *version)
825 {
826     Z_SRW_PDU *p = (Z_SRW_PDU *) odr_malloc(o, sizeof(*p));
827     p->srw_version = odr_strdup(o, version);
828     p->username = 0;
829     p->password = 0;
830     p->extra_args = 0;
831     return p;
832 }
833
834 Z_SRW_PDU *yaz_srw_get_core_v_1_1(ODR o)
835 {
836     return yaz_srw_get_core_ver(o, "1.1");
837 }
838
839 Z_SRW_PDU *yaz_srw_get(ODR o, int which)
840 {
841     return yaz_srw_get_pdu(o, which, "1.1");
842 }
843
844 Z_SRW_PDU *yaz_srw_get_pdu(ODR o, int which, const char *version)
845 {
846     Z_SRW_PDU *sr = yaz_srw_get_core_ver(o, version);
847
848     sr->which = which;
849     switch(which)
850     {
851     case Z_SRW_searchRetrieve_request:
852         sr->u.request = (Z_SRW_searchRetrieveRequest *)
853             odr_malloc(o, sizeof(*sr->u.request));
854         sr->u.request->query_type = Z_SRW_query_type_cql;
855         sr->u.request->query.cql = 0;
856         sr->u.request->sort_type = Z_SRW_sort_type_none;
857         sr->u.request->sort.none = 0;
858         sr->u.request->startRecord = 0;
859         sr->u.request->maximumRecords = 0;
860         sr->u.request->recordSchema = 0;
861         sr->u.request->recordPacking = 0;
862         sr->u.request->recordXPath = 0;
863         sr->u.request->database = 0;
864         sr->u.request->resultSetTTL = 0;
865         sr->u.request->stylesheet = 0;
866         break;
867     case Z_SRW_searchRetrieve_response:
868         sr->u.response = (Z_SRW_searchRetrieveResponse *)
869             odr_malloc(o, sizeof(*sr->u.response));
870         sr->u.response->numberOfRecords = 0;
871         sr->u.response->resultSetId = 0;
872         sr->u.response->resultSetIdleTime = 0;
873         sr->u.response->records = 0;
874         sr->u.response->num_records = 0;
875         sr->u.response->diagnostics = 0;
876         sr->u.response->num_diagnostics = 0;
877         sr->u.response->nextRecordPosition = 0;
878         sr->u.response->extra_records = 0;
879         break;
880     case Z_SRW_explain_request:
881         sr->u.explain_request = (Z_SRW_explainRequest *)
882             odr_malloc(o, sizeof(*sr->u.explain_request));
883         sr->u.explain_request->recordPacking = 0;
884         sr->u.explain_request->database = 0;
885         sr->u.explain_request->stylesheet = 0;
886         break;
887     case Z_SRW_explain_response:
888         sr->u.explain_response = (Z_SRW_explainResponse *)
889             odr_malloc(o, sizeof(*sr->u.explain_response));
890         sr->u.explain_response->record.recordData_buf = 0;
891         sr->u.explain_response->record.recordData_len = 0;
892         sr->u.explain_response->record.recordSchema = 0;
893         sr->u.explain_response->record.recordPosition = 0;
894         sr->u.explain_response->record.recordPacking =
895             Z_SRW_recordPacking_string;
896         sr->u.explain_response->diagnostics = 0;
897         sr->u.explain_response->num_diagnostics = 0;
898         sr->u.explain_response->extra_record = 0;
899         break;
900     case Z_SRW_scan_request:
901         sr->u.scan_request = (Z_SRW_scanRequest *)
902             odr_malloc(o, sizeof(*sr->u.scan_request));
903         sr->u.scan_request->database = 0;
904         sr->u.scan_request->stylesheet = 0;
905         sr->u.scan_request->maximumTerms = 0;
906         sr->u.scan_request->responsePosition = 0;
907         sr->u.scan_request->query_type = Z_SRW_query_type_cql;
908         sr->u.scan_request->scanClause.cql = 0;
909         break;
910     case Z_SRW_scan_response:
911         sr->u.scan_response = (Z_SRW_scanResponse *)
912             odr_malloc(o, sizeof(*sr->u.scan_response));
913         sr->u.scan_response->terms = 0;
914         sr->u.scan_response->num_terms = 0;
915         sr->u.scan_response->diagnostics = 0;
916         sr->u.scan_response->num_diagnostics = 0;
917         break;
918     case Z_SRW_update_request:
919         sr->u.update_request = (Z_SRW_updateRequest *)
920             odr_malloc(o, sizeof(*sr->u.update_request));
921         sr->u.update_request->database = 0;
922         sr->u.update_request->stylesheet = 0;
923         sr->u.update_request->record = 0;
924         sr->u.update_request->recordId = 0;
925         sr->u.update_request->recordVersions = 0;
926         sr->u.update_request->num_recordVersions = 0;
927         sr->u.update_request->extra_record = 0;
928         sr->u.update_request->extraRequestData_buf = 0;
929         sr->u.update_request->extraRequestData_len = 0;
930         sr->u.request->database = 0;
931         break;
932     case Z_SRW_update_response:
933         sr->u.update_response = (Z_SRW_updateResponse *)
934             odr_malloc(o, sizeof(*sr->u.update_response));
935         sr->u.update_response->operationStatus = 0;
936         sr->u.update_response->recordId = 0;
937         sr->u.update_response->recordVersions = 0;
938         sr->u.update_response->num_recordVersions = 0;
939         sr->u.update_response->record = 0;
940         sr->u.update_response->extra_record = 0;
941         sr->u.update_response->extraResponseData_buf = 0;
942         sr->u.update_response->extraResponseData_len = 0;
943         sr->u.update_response->diagnostics = 0;
944         sr->u.update_response->num_diagnostics = 0;
945     }
946     return sr;
947 }
948
949 /* bib1:srw */
950 static int srw_bib1_map[] = {
951     1, 1,
952     2, 2,
953     3, 11,
954     4, 35,
955     5, 12,
956     6, 38,
957     7, 30,
958     8, 32,
959     9, 29,
960     108, 10,  /* Malformed query : Syntax error */
961     10, 10,
962     11, 12,
963     11, 23,
964     12, 60,
965     13, 61,
966     13, 62,
967     14, 63,
968     14, 64,
969     14, 65,
970     15, 68,
971     15, 69,
972     16, 70,
973     17, 70,
974     18, 50,
975     19, 55,
976     20, 56, 
977     21, 52,
978     22, 50,
979     23, 3,
980     24, 66,
981     25, 66,
982     26, 66,
983     27, 51,
984     28, 52,
985     29, 52,
986     30, 51,
987     31, 57,
988     32, 58,
989     33, 59,
990     100, 1, /* bad map */
991     101, 3,
992     102, 3,
993     103, 3,
994     104, 3,
995     105, 3, 
996     106, 66,
997     107, 11,
998     108, 13,
999     108, 14,
1000     108, 25,
1001     108, 26,
1002     108, 27,
1003     108, 45,
1004         
1005     109, 2,
1006     110, 37,
1007     111, 1,
1008     112, 58,
1009     113, 10,
1010     114, 16,
1011     115, 16,
1012     116, 16,
1013     117, 19,
1014     117, 20,
1015     118, 22,
1016     119, 32,
1017     119, 31,
1018     120, 28,
1019     121, 15,
1020     122, 32,
1021     123, 22,
1022     123, 17,
1023     123, 18,
1024     124, 24,
1025     125, 36,
1026     126, 36, 
1027     127, 36,
1028     128, 51,
1029     129, 39,
1030     130, 43,
1031     131, 40,
1032     132, 42,
1033     201, 44,
1034     201, 33,
1035     201, 34,
1036     202, 41,
1037     203, 43,
1038     205, 1,  /* bad map */
1039     206, 1,  /* bad map */
1040     207, 89,
1041     208, 1,  /* bad map */
1042     209, 80,
1043     210, 80,
1044     210, 81,
1045     211, 84,
1046     212, 85,
1047     213, 92,
1048     214, 90,
1049     215, 91,
1050     216, 92,
1051     217, 63,
1052     218, 1,  /* bad map */
1053     219, 1,  /* bad map */
1054     220, 1,  /* bad map */
1055     221, 1,  /* bad map */
1056     222, 3,
1057     223, 1,  /* bad map */
1058     224, 1,  /* bad map */
1059     225, 1,  /* bad map */
1060     226, 1,  /* bad map */
1061     227, 66,
1062     228, 1,  /* bad map */
1063     229, 36,
1064     230, 83,
1065     231, 89,
1066     232, 1,
1067     233, 1, /* bad map */
1068     234, 1, /* bad map */
1069     235, 2,
1070     236, 3, 
1071     237, 82,
1072     238, 67,
1073     239, 66,
1074     240, 1, /* bad map */
1075     241, 1, /* bad map */
1076     242, 70,
1077     243, 1, /* bad map */
1078     244, 66,
1079     245, 10,
1080     246, 10,
1081     247, 10,
1082     1001, 1, /* bad map */
1083     1002, 1, /* bad map */
1084     1003, 1, /* bad map */
1085     1004, 1, /* bad map */
1086     1005, 1, /* bad map */
1087     1006, 1, /* bad map */
1088     1007, 100,
1089     1008, 1, 
1090     1009, 1,
1091     1010, 3,
1092     1011, 3,
1093     1012, 3,
1094     1013, 3,
1095     1014, 3,
1096     1015, 3,
1097     1015, 3,
1098     1016, 3,
1099     1017, 3,
1100     1018, 2,
1101     1019, 2,
1102     1020, 2,
1103     1021, 3,
1104     1022, 3,
1105     1023, 3,
1106     1024, 16,
1107     1025, 3,
1108     1026, 64,
1109     1027, 1,
1110     1028, 65,
1111     1029, 1,
1112     1040, 1,
1113     /* 1041-1065 */
1114     1066, 66,
1115     1066, 67,
1116     0
1117 };
1118
1119 int yaz_diag_bib1_to_srw (int code)
1120 {
1121     const int *p = srw_bib1_map;
1122     while (*p)
1123     {
1124         if (code == p[0])
1125             return p[1];
1126         p += 2;
1127     }
1128     return 1;
1129 }
1130
1131 int yaz_diag_srw_to_bib1(int code)
1132 {
1133     const int *p = srw_bib1_map;
1134     while (*p)
1135     {
1136         if (code == p[1])
1137             return p[0];
1138         p += 2;
1139     }
1140     return 1;
1141 }
1142
1143 static void add_val_int(ODR o, char **name, char **value,  int *i,
1144                         char *a_name, int *val)
1145 {
1146     if (val)
1147     {
1148         name[*i] = a_name;
1149         value[*i] = (char *) odr_malloc(o, 30);
1150         sprintf(value[*i], "%d", *val);
1151         (*i)++;
1152     }
1153 }
1154
1155 static void add_val_str(ODR o, char **name, char **value,  int *i,
1156                         char *a_name, char *val)
1157 {
1158     if (val)
1159     {
1160         name[*i] = a_name;
1161         value[*i] = val;
1162         (*i)++;
1163     }
1164 }
1165
1166 static int yaz_get_sru_parms(const Z_SRW_PDU *srw_pdu, ODR encode,
1167                               char **name, char **value)
1168 {
1169     int i = 0;
1170     add_val_str(encode, name, value, &i, "version", srw_pdu->srw_version);
1171     name[i] = "operation";
1172     switch(srw_pdu->which)
1173     {
1174     case Z_SRW_searchRetrieve_request:
1175         value[i++] = "searchRetrieve";
1176         switch(srw_pdu->u.request->query_type)
1177         {
1178         case Z_SRW_query_type_cql:
1179             add_val_str(encode, name, value, &i, "query",
1180                         srw_pdu->u.request->query.cql);
1181             break;
1182         case Z_SRW_query_type_pqf:
1183             add_val_str(encode, name, value, &i, "x-pquery",
1184                         srw_pdu->u.request->query.pqf);
1185             break;
1186         case Z_SRW_query_type_xcql:
1187             add_val_str(encode, name, value, &i, "x-cql",
1188                         srw_pdu->u.request->query.xcql);
1189             break;
1190         }
1191         switch(srw_pdu->u.request->sort_type)
1192         {
1193         case Z_SRW_sort_type_none:
1194             break;
1195         case Z_SRW_sort_type_sort:            
1196             add_val_str(encode, name, value, &i, "sortKeys",
1197                         srw_pdu->u.request->sort.sortKeys);
1198             break;
1199         }
1200         add_val_int(encode, name, value, &i, "startRecord", 
1201                     srw_pdu->u.request->startRecord);
1202         add_val_int(encode, name, value, &i, "maximumRecords", 
1203                     srw_pdu->u.request->maximumRecords);
1204         add_val_str(encode, name, value, &i, "recordSchema",
1205                     srw_pdu->u.request->recordSchema);
1206         add_val_str(encode, name, value, &i, "recordPacking",
1207                     srw_pdu->u.request->recordPacking);
1208         add_val_str(encode, name, value, &i, "recordXPath",
1209                     srw_pdu->u.request->recordXPath);
1210         add_val_str(encode, name, value, &i, "stylesheet",
1211                     srw_pdu->u.request->stylesheet);
1212         add_val_int(encode, name, value, &i, "resultSetTTL", 
1213                     srw_pdu->u.request->resultSetTTL);
1214         break;
1215     case Z_SRW_explain_request:
1216         value[i++] = "explain";
1217         add_val_str(encode, name, value, &i, "stylesheet",
1218                     srw_pdu->u.explain_request->stylesheet);
1219         break;
1220     case Z_SRW_scan_request:
1221         value[i++] = "scan";
1222
1223         switch(srw_pdu->u.scan_request->query_type)
1224         {
1225         case Z_SRW_query_type_cql:
1226             add_val_str(encode, name, value, &i, "scanClause",
1227                         srw_pdu->u.scan_request->scanClause.cql);
1228             break;
1229         case Z_SRW_query_type_pqf:
1230             add_val_str(encode, name, value, &i, "x-pScanClause",
1231                         srw_pdu->u.scan_request->scanClause.pqf);
1232             break;
1233         case Z_SRW_query_type_xcql:
1234             add_val_str(encode, name, value, &i, "x-cqlScanClause",
1235                         srw_pdu->u.scan_request->scanClause.xcql);
1236             break;
1237         }
1238         add_val_int(encode, name, value, &i, "responsePosition", 
1239                     srw_pdu->u.scan_request->responsePosition);
1240         add_val_int(encode, name, value, &i, "maximumTerms", 
1241                     srw_pdu->u.scan_request->maximumTerms);
1242         add_val_str(encode, name, value, &i, "stylesheet",
1243                     srw_pdu->u.scan_request->stylesheet);
1244         break;
1245     case Z_SRW_update_request:
1246         value[i++] = "update";
1247         break;
1248     default:
1249         return -1;
1250     }
1251     name[i++] = 0;
1252     return 0;
1253 }
1254
1255 int yaz_sru_get_encode(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu,
1256                        ODR encode, const char *charset)
1257 {
1258     char *name[30], *value[30]; /* definite upper limit for SRU params */
1259     char *uri_args;
1260     char *path;
1261
1262     z_HTTP_header_add_basic_auth(encode, &hreq->headers, 
1263                                  srw_pdu->username, srw_pdu->password);
1264     if (yaz_get_sru_parms(srw_pdu, encode, name, value))
1265         return -1;
1266     yaz_array_to_uri_ex(&uri_args, encode, name, value, srw_pdu->extra_args);
1267
1268     hreq->method = "GET";
1269     
1270     path = (char *)
1271         odr_malloc(encode, strlen(hreq->path) + strlen(uri_args) + 4
1272                    +(srw_pdu->extra_args ? strlen(srw_pdu->extra_args) : 0));
1273
1274     sprintf(path, "%s?%s", hreq->path, uri_args);
1275     hreq->path = path;
1276
1277     z_HTTP_header_add_content_type(encode, &hreq->headers,
1278                                    "text/xml", charset);
1279     return 0;
1280 }
1281
1282 int yaz_sru_post_encode(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu,
1283                         ODR encode, const char *charset)
1284 {
1285     char *name[30], *value[30]; /* definite upper limit for SRU params */
1286     char *uri_args;
1287
1288     z_HTTP_header_add_basic_auth(encode, &hreq->headers, 
1289                                  srw_pdu->username, srw_pdu->password);
1290     if (yaz_get_sru_parms(srw_pdu, encode, name, value))
1291         return -1;
1292
1293     yaz_array_to_uri_ex(&uri_args, encode, name, value, srw_pdu->extra_args);
1294
1295     hreq->method = "POST";
1296     
1297     hreq->content_buf = uri_args;
1298     hreq->content_len = strlen(uri_args);
1299
1300     z_HTTP_header_add_content_type(encode, &hreq->headers,
1301                                    "application/x-www-form-urlencoded",
1302                                    charset);
1303     return 0;
1304 }
1305
1306 int yaz_sru_soap_encode(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu,
1307                         ODR odr, const char *charset)
1308 {
1309     Z_SOAP_Handler handlers[3] = {
1310 #if YAZ_HAVE_XML2
1311         {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec},
1312         {YAZ_XMLNS_UPDATE_v0_9, 0, (Z_SOAP_fun) yaz_ucp_codec},
1313 #endif
1314         {0, 0, 0}
1315     };
1316     Z_SOAP *p = (Z_SOAP*) odr_malloc(odr, sizeof(*p));
1317
1318     z_HTTP_header_add_basic_auth(odr, &hreq->headers, 
1319                                  srw_pdu->username, srw_pdu->password);
1320     z_HTTP_header_add_content_type(odr,
1321                                    &hreq->headers,
1322                                    "text/xml", charset);
1323     
1324     z_HTTP_header_add(odr, &hreq->headers,
1325                       "SOAPAction", "\"\"");
1326     p->which = Z_SOAP_generic;
1327     p->u.generic = (Z_SOAP_Generic *) odr_malloc(odr, sizeof(*p->u.generic));
1328     p->u.generic->no = 0;
1329     p->u.generic->ns = 0;
1330     p->u.generic->p = srw_pdu;
1331     p->ns = "http://schemas.xmlsoap.org/soap/envelope/";
1332
1333 #if YAZ_HAVE_XML2
1334     if (srw_pdu->which == Z_SRW_update_request ||
1335         srw_pdu->which == Z_SRW_update_response)
1336         p->u.generic->no = 1; /* second handler */
1337 #endif
1338     return z_soap_codec_enc(odr, &p,
1339                             &hreq->content_buf,
1340                             &hreq->content_len, handlers,
1341                             charset);
1342 }
1343
1344 Z_SRW_recordVersion *yaz_srw_get_record_versions(ODR odr, int num )
1345 {
1346     Z_SRW_recordVersion *ver 
1347         = (Z_SRW_recordVersion *) odr_malloc( odr, num * sizeof(*ver) );
1348     int i;
1349     for ( i=0; i < num; ++i ){
1350         ver[i].versionType = 0;
1351         ver[i].versionValue = 0;
1352     }
1353     return ver;
1354 }
1355
1356 const char *yaz_srw_pack_to_str(int pack)
1357 {
1358     switch(pack)
1359     {
1360     case Z_SRW_recordPacking_string:
1361         return "string";
1362     case Z_SRW_recordPacking_XML:
1363         return "xml";
1364     case Z_SRW_recordPacking_URL:
1365         return "url";
1366     }
1367     return 0;
1368 }
1369
1370 int yaz_srw_str_to_pack(const char *str)
1371 {
1372     if (!yaz_matchstr(str, "string"))
1373         return Z_SRW_recordPacking_string;
1374     if (!yaz_matchstr(str, "xml"))
1375         return Z_SRW_recordPacking_XML;
1376     if (!yaz_matchstr(str, "url"))
1377         return Z_SRW_recordPacking_URL;
1378     return -1;
1379 }
1380
1381 /*
1382  * Local variables:
1383  * c-basic-offset: 4
1384  * indent-tabs-mode: nil
1385  * End:
1386  * vim: shiftwidth=4 tabstop=8 expandtab
1387  */
1388