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