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