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