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