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