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