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