Added debug logging to verify ZOOM_EVENT_RECV_SEARCH
[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         sr->u.request->facetList = 0;
751         break;
752     case Z_SRW_searchRetrieve_response:
753         sr->u.response = (Z_SRW_searchRetrieveResponse *)
754             odr_malloc(o, sizeof(*sr->u.response));
755         sr->u.response->numberOfRecords = 0;
756         sr->u.response->resultSetId = 0;
757         sr->u.response->resultSetIdleTime = 0;
758         sr->u.response->records = 0;
759         sr->u.response->num_records = 0;
760         sr->u.response->diagnostics = 0;
761         sr->u.response->num_diagnostics = 0;
762         sr->u.response->nextRecordPosition = 0;
763         sr->u.response->extra_records = 0;
764         sr->u.response->facetList = 0;
765         break;
766     case Z_SRW_explain_request:
767         sr->u.explain_request = (Z_SRW_explainRequest *)
768             odr_malloc(o, sizeof(*sr->u.explain_request));
769         sr->u.explain_request->recordPacking = 0;
770         sr->u.explain_request->database = 0;
771         sr->u.explain_request->stylesheet = 0;
772         break;
773     case Z_SRW_explain_response:
774         sr->u.explain_response = (Z_SRW_explainResponse *)
775             odr_malloc(o, sizeof(*sr->u.explain_response));
776         sr->u.explain_response->record.recordData_buf = 0;
777         sr->u.explain_response->record.recordData_len = 0;
778         sr->u.explain_response->record.recordSchema = 0;
779         sr->u.explain_response->record.recordPosition = 0;
780         sr->u.explain_response->record.recordPacking =
781             Z_SRW_recordPacking_string;
782         sr->u.explain_response->diagnostics = 0;
783         sr->u.explain_response->num_diagnostics = 0;
784         sr->u.explain_response->extra_record = 0;
785         break;
786     case Z_SRW_scan_request:
787         sr->u.scan_request = (Z_SRW_scanRequest *)
788             odr_malloc(o, sizeof(*sr->u.scan_request));
789         sr->u.scan_request->database = 0;
790         sr->u.scan_request->stylesheet = 0;
791         sr->u.scan_request->maximumTerms = 0;
792         sr->u.scan_request->responsePosition = 0;
793         sr->u.scan_request->query_type = Z_SRW_query_type_cql;
794         sr->u.scan_request->scanClause.cql = 0;
795         break;
796     case Z_SRW_scan_response:
797         sr->u.scan_response = (Z_SRW_scanResponse *)
798             odr_malloc(o, sizeof(*sr->u.scan_response));
799         sr->u.scan_response->terms = 0;
800         sr->u.scan_response->num_terms = 0;
801         sr->u.scan_response->diagnostics = 0;
802         sr->u.scan_response->num_diagnostics = 0;
803         break;
804     case Z_SRW_update_request:
805         sr->u.update_request = (Z_SRW_updateRequest *)
806             odr_malloc(o, sizeof(*sr->u.update_request));
807         sr->u.update_request->database = 0;
808         sr->u.update_request->stylesheet = 0;
809         sr->u.update_request->record = 0;
810         sr->u.update_request->recordId = 0;
811         sr->u.update_request->recordVersions = 0;
812         sr->u.update_request->num_recordVersions = 0;
813         sr->u.update_request->extra_record = 0;
814         sr->u.update_request->extraRequestData_buf = 0;
815         sr->u.update_request->extraRequestData_len = 0;
816         sr->u.request->database = 0;
817         break;
818     case Z_SRW_update_response:
819         sr->u.update_response = (Z_SRW_updateResponse *)
820             odr_malloc(o, sizeof(*sr->u.update_response));
821         sr->u.update_response->operationStatus = 0;
822         sr->u.update_response->recordId = 0;
823         sr->u.update_response->recordVersions = 0;
824         sr->u.update_response->num_recordVersions = 0;
825         sr->u.update_response->record = 0;
826         sr->u.update_response->extra_record = 0;
827         sr->u.update_response->extraResponseData_buf = 0;
828         sr->u.update_response->extraResponseData_len = 0;
829         sr->u.update_response->diagnostics = 0;
830         sr->u.update_response->num_diagnostics = 0;
831     }
832     return sr;
833 }
834
835 /* bib1:srw */
836 static int bib1_srw_map[] = {
837     1, 1,
838     2, 2,
839     3, 11,
840     4, 35,
841     5, 12,
842     6, 38,
843     7, 30,
844     8, 32,
845     9, 29,
846     108, 10,  /* Malformed query : Syntax error */
847     10, 10,
848     11, 12,
849     11, 23,
850     12, 60,
851     13, 61,
852     13, 62,
853     14, 63,
854     14, 64,
855     14, 65,
856     15, 68,
857     15, 69,
858     16, 70,
859     17, 70,
860     18, 50,
861     19, 55,
862     20, 56, 
863     21, 52,
864     22, 50,
865     23, 3,
866     24, 66,
867     25, 66,
868     26, 66,
869     27, 51,
870     28, 52,
871     29, 52,
872     30, 51,
873     31, 57,
874     32, 58,
875     33, 59,
876     100, 1, /* bad map */
877     101, 3,
878     102, 3,
879     103, 3,
880     104, 3,
881     105, 3, 
882     106, 66,
883     107, 11,
884     108, 13,
885     108, 14,
886     108, 25,
887     108, 26,
888     108, 27,
889     108, 45,
890         
891     109, 2,
892     110, 37,
893     111, 1,
894     112, 58,
895     113, 10,
896     114, 16,
897     115, 16,
898     116, 16,
899     117, 19,
900     117, 20,
901     118, 22,
902     119, 32,
903     119, 31,
904     120, 28,
905     121, 15,
906     122, 32,
907     123, 22,
908     123, 17,
909     123, 18,
910     124, 24,
911     125, 36,
912     126, 36, 
913     127, 36,
914     128, 51,
915     129, 39,
916     130, 43,
917     131, 40,
918     132, 42,
919     201, 44,
920     201, 33,
921     201, 34,
922     202, 41,
923     203, 43,
924     205, 1,  /* bad map */
925     206, 1,  /* bad map */
926     207, 89,
927     208, 1,  /* bad map */
928     209, 80,
929     210, 80,
930     210, 81,
931     211, 84,
932     212, 85,
933     213, 92,
934     214, 90,
935     215, 91,
936     216, 92,
937     217, 63,
938     218, 1,  /* bad map */
939     219, 1,  /* bad map */
940     220, 1,  /* bad map */
941     221, 1,  /* bad map */
942     222, 3,
943     223, 1,  /* bad map */
944     224, 1,  /* bad map */
945     225, 1,  /* bad map */
946     226, 1,  /* bad map */
947     227, 66,
948     228, 1,  /* bad map */
949     229, 36,
950     230, 83,
951     231, 89,
952     232, 1,
953     233, 1, /* bad map */
954     234, 1, /* bad map */
955     235, 2,
956     236, 3, 
957     237, 82,
958     238, 67,
959     239, 66,
960     240, 1, /* bad map */
961     241, 1, /* bad map */
962     242, 70,
963     243, 1, /* bad map */
964     244, 66,
965     245, 10,
966     246, 10,
967     247, 10,
968     1001, 1, /* bad map */
969     1002, 1, /* bad map */
970     1003, 1, /* bad map */
971     1004, 1, /* bad map */
972     1005, 1, /* bad map */
973     1006, 1, /* bad map */
974     1007, 100,
975     1008, 1, 
976     1009, 1,
977     1010, 3,
978     1011, 3,
979     1012, 3,
980     1013, 3,
981     1014, 3,
982     1015, 3,
983     1015, 3,
984     1016, 3,
985     1017, 3,
986     1018, 2,
987     1019, 2,
988     1020, 2,
989     1021, 3,
990     1022, 3,
991     1023, 3,
992     1024, 16,
993     1025, 3,
994     1026, 64,
995     1027, 1,
996     1028, 65,
997     1029, 1,
998     1040, 1,
999     /* 1041-1065 */
1000     1066, 66,
1001     1066, 67,
1002     0
1003 };
1004
1005 /*
1006  * This array contains overrides for when the first occurrence of a
1007  * particular SRW error in the array above does not correspond with
1008  * the best back-translation of that SRW error.
1009  */
1010 static int srw_bib1_map[] = {
1011     66, 238,
1012     /* No doubt there are many more */
1013     0
1014 };
1015
1016
1017 int yaz_diag_bib1_to_srw (int code)
1018 {
1019     const int *p = bib1_srw_map;
1020     while (*p)
1021     {
1022         if (code == p[0])
1023             return p[1];
1024         p += 2;
1025     }
1026     return 1;
1027 }
1028
1029 int yaz_diag_srw_to_bib1(int code)
1030 {
1031     /* Check explicit reverse-map first */
1032     const int *p = srw_bib1_map;
1033     while (*p)
1034     {
1035         if (code == p[0])
1036             return p[1];
1037         p += 2;
1038     }
1039
1040     /* Fall back on reverse lookup in main map */
1041     p = bib1_srw_map;
1042     while (*p)
1043     {
1044         if (code == p[1])
1045             return p[0];
1046         p += 2;
1047     }
1048     return 1;
1049 }
1050
1051 void yaz_add_name_value_int(ODR o, char **name, char **value, int *i,
1052                             char *a_name, Odr_int *val)
1053 {
1054     if (val)
1055     {
1056         name[*i] = a_name;
1057         value[*i] = (char *) odr_malloc(o, 40);
1058         sprintf(value[*i], ODR_INT_PRINTF, *val);
1059         (*i)++;
1060     }
1061 }
1062
1063 void yaz_add_name_value_str(ODR o, char **name, char **value,  int *i,
1064                             char *a_name, char *val)
1065 {
1066     if (val)
1067     {
1068         name[*i] = a_name;
1069         value[*i] = val;
1070         (*i)++;
1071     }
1072 }
1073
1074 static int yaz_get_sru_parms(const Z_SRW_PDU *srw_pdu, ODR encode,
1075                              char **name, char **value, int max_names)
1076 {
1077     int i = 0;
1078     yaz_add_name_value_str(encode, name, value, &i, "version", srw_pdu->srw_version);
1079     name[i] = "operation";
1080     switch(srw_pdu->which)
1081     {
1082     case Z_SRW_searchRetrieve_request:
1083         value[i++] = "searchRetrieve";
1084         switch(srw_pdu->u.request->query_type)
1085         {
1086         case Z_SRW_query_type_cql:
1087             yaz_add_name_value_str(encode, name, value, &i, "query",
1088                                    srw_pdu->u.request->query.cql);
1089             break;
1090         case Z_SRW_query_type_pqf:
1091             yaz_add_name_value_str(encode, name, value, &i, "x-pquery",
1092                                    srw_pdu->u.request->query.pqf);
1093             break;
1094         case Z_SRW_query_type_xcql:
1095             yaz_add_name_value_str(encode, name, value, &i, "x-cql",
1096                                    srw_pdu->u.request->query.xcql);
1097             break;
1098         }
1099         switch(srw_pdu->u.request->sort_type)
1100         {
1101         case Z_SRW_sort_type_none:
1102             break;
1103         case Z_SRW_sort_type_sort:            
1104             yaz_add_name_value_str(encode, name, value, &i, "sortKeys",
1105                                    srw_pdu->u.request->sort.sortKeys);
1106             break;
1107         }
1108         yaz_add_name_value_int(encode, name, value, &i, "startRecord", 
1109                                srw_pdu->u.request->startRecord);
1110         yaz_add_name_value_int(encode, name, value, &i, "maximumRecords", 
1111                                srw_pdu->u.request->maximumRecords);
1112         yaz_add_name_value_str(encode, name, value, &i, "recordSchema",
1113                                srw_pdu->u.request->recordSchema);
1114         yaz_add_name_value_str(encode, name, value, &i, "recordPacking",
1115                                srw_pdu->u.request->recordPacking);
1116         yaz_add_name_value_str(encode, name, value, &i, "recordXPath",
1117                                srw_pdu->u.request->recordXPath);
1118         yaz_add_name_value_str(encode, name, value, &i, "stylesheet",
1119                                srw_pdu->u.request->stylesheet);
1120         yaz_add_name_value_int(encode, name, value, &i, "resultSetTTL", 
1121                                srw_pdu->u.request->resultSetTTL);
1122         break;
1123     case Z_SRW_explain_request:
1124         value[i++] = "explain";
1125         yaz_add_name_value_str(encode, name, value, &i, "stylesheet",
1126                                srw_pdu->u.explain_request->stylesheet);
1127         break;
1128     case Z_SRW_scan_request:
1129         value[i++] = "scan";
1130
1131         switch(srw_pdu->u.scan_request->query_type)
1132         {
1133         case Z_SRW_query_type_cql:
1134             yaz_add_name_value_str(encode, name, value, &i, "scanClause",
1135                                    srw_pdu->u.scan_request->scanClause.cql);
1136             break;
1137         case Z_SRW_query_type_pqf:
1138             yaz_add_name_value_str(encode, name, value, &i, "x-pScanClause",
1139                                    srw_pdu->u.scan_request->scanClause.pqf);
1140             break;
1141         case Z_SRW_query_type_xcql:
1142             yaz_add_name_value_str(encode, name, value, &i, "x-cqlScanClause",
1143                                    srw_pdu->u.scan_request->scanClause.xcql);
1144             break;
1145         }
1146         yaz_add_name_value_int(encode, name, value, &i, "responsePosition", 
1147                                srw_pdu->u.scan_request->responsePosition);
1148         yaz_add_name_value_int(encode, name, value, &i, "maximumTerms", 
1149                                srw_pdu->u.scan_request->maximumTerms);
1150         yaz_add_name_value_str(encode, name, value, &i, "stylesheet",
1151                                srw_pdu->u.scan_request->stylesheet);
1152         break;
1153     case Z_SRW_update_request:
1154         value[i++] = "update";
1155         break;
1156     default:
1157         return -1;
1158     }
1159     if (srw_pdu->extra_args)
1160     {
1161         Z_SRW_extra_arg *ea = srw_pdu->extra_args;
1162         for (; ea && i < max_names-1; ea = ea->next)
1163         {
1164             name[i] = ea->name;
1165             value[i] = ea->value;
1166             i++;
1167         }
1168     }
1169     name[i++] = 0;
1170
1171     return 0;
1172 }
1173
1174 int yaz_sru_get_encode(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu,
1175                        ODR encode, const char *charset)
1176 {
1177     char *name[30], *value[30]; /* definite upper limit for SRU params */
1178     char *uri_args;
1179     char *path;
1180
1181     z_HTTP_header_add_basic_auth(encode, &hreq->headers, 
1182                                  srw_pdu->username, srw_pdu->password);
1183     if (yaz_get_sru_parms(srw_pdu, encode, name, value, 30))
1184         return -1;
1185     yaz_array_to_uri(&uri_args, encode, name, value);
1186
1187     hreq->method = "GET";
1188     
1189     path = (char *)
1190         odr_malloc(encode, strlen(hreq->path) + strlen(uri_args) + 4);
1191
1192     sprintf(path, "%s?%s", hreq->path, uri_args);
1193     hreq->path = path;
1194
1195     z_HTTP_header_add_content_type(encode, &hreq->headers,
1196                                    "text/xml", charset);
1197     return 0;
1198 }
1199
1200 int yaz_sru_post_encode(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu,
1201                         ODR encode, const char *charset)
1202 {
1203     char *name[30], *value[30]; /* definite upper limit for SRU params */
1204     char *uri_args;
1205
1206     z_HTTP_header_add_basic_auth(encode, &hreq->headers, 
1207                                  srw_pdu->username, srw_pdu->password);
1208     if (yaz_get_sru_parms(srw_pdu, encode, name, value, 30))
1209         return -1;
1210
1211     yaz_array_to_uri(&uri_args, encode, name, value);
1212
1213     hreq->method = "POST";
1214     
1215     hreq->content_buf = uri_args;
1216     hreq->content_len = strlen(uri_args);
1217
1218     z_HTTP_header_add_content_type(encode, &hreq->headers,
1219                                    "application/x-www-form-urlencoded",
1220                                    charset);
1221     return 0;
1222 }
1223
1224 int yaz_sru_soap_encode(Z_HTTP_Request *hreq, Z_SRW_PDU *srw_pdu,
1225                         ODR odr, const char *charset)
1226 {
1227     Z_SOAP_Handler handlers[3] = {
1228 #if YAZ_HAVE_XML2
1229         {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec},
1230         {YAZ_XMLNS_UPDATE_v0_9, 0, (Z_SOAP_fun) yaz_ucp_codec},
1231 #endif
1232         {0, 0, 0}
1233     };
1234     Z_SOAP *p = (Z_SOAP*) odr_malloc(odr, sizeof(*p));
1235
1236     z_HTTP_header_add_basic_auth(odr, &hreq->headers, 
1237                                  srw_pdu->username, srw_pdu->password);
1238     z_HTTP_header_add_content_type(odr,
1239                                    &hreq->headers,
1240                                    "text/xml", charset);
1241     
1242     z_HTTP_header_add(odr, &hreq->headers,
1243                       "SOAPAction", "\"\"");
1244     p->which = Z_SOAP_generic;
1245     p->u.generic = (Z_SOAP_Generic *) odr_malloc(odr, sizeof(*p->u.generic));
1246     p->u.generic->no = 0;
1247     p->u.generic->ns = 0;
1248     p->u.generic->p = srw_pdu;
1249     p->ns = "http://schemas.xmlsoap.org/soap/envelope/";
1250
1251 #if YAZ_HAVE_XML2
1252     if (srw_pdu->which == Z_SRW_update_request ||
1253         srw_pdu->which == Z_SRW_update_response)
1254         p->u.generic->no = 1; /* second handler */
1255 #endif
1256     return z_soap_codec_enc(odr, &p,
1257                             &hreq->content_buf,
1258                             &hreq->content_len, handlers,
1259                             charset);
1260 }
1261
1262 Z_SRW_recordVersion *yaz_srw_get_record_versions(ODR odr, int num )
1263 {
1264     Z_SRW_recordVersion *ver 
1265         = (Z_SRW_recordVersion *) odr_malloc( odr, num * sizeof(*ver) );
1266     int i;
1267     for ( i=0; i < num; ++i ){
1268         ver[i].versionType = 0;
1269         ver[i].versionValue = 0;
1270     }
1271     return ver;
1272 }
1273
1274 const char *yaz_srw_pack_to_str(int pack)
1275 {
1276     switch(pack)
1277     {
1278     case Z_SRW_recordPacking_string:
1279         return "string";
1280     case Z_SRW_recordPacking_XML:
1281         return "xml";
1282     case Z_SRW_recordPacking_URL:
1283         return "url";
1284     }
1285     return 0;
1286 }
1287
1288 int yaz_srw_str_to_pack(const char *str)
1289 {
1290     if (!yaz_matchstr(str, "string"))
1291         return Z_SRW_recordPacking_string;
1292     if (!yaz_matchstr(str, "xml"))
1293         return Z_SRW_recordPacking_XML;
1294     if (!yaz_matchstr(str, "url"))
1295         return Z_SRW_recordPacking_URL;
1296     return -1;
1297 }
1298
1299 void yaz_encode_sru_extra(Z_SRW_PDU *sr, ODR odr, const char *extra_args)
1300 {
1301     if (extra_args)
1302     {
1303         char **name;
1304         char **val;
1305         Z_SRW_extra_arg **ea = &sr->extra_args;
1306         yaz_uri_to_array(extra_args, odr, &name, &val);
1307
1308         while (*name)
1309         {
1310             *ea = (Z_SRW_extra_arg *) odr_malloc(odr, sizeof(**ea));
1311             (*ea)->name = *name;
1312             (*ea)->value = *val;
1313             ea = &(*ea)->next;
1314             val++;
1315             name++;
1316         }
1317         *ea = 0;
1318     }
1319 }
1320
1321
1322 /*
1323  * Local variables:
1324  * c-basic-offset: 4
1325  * c-file-style: "Stroustrup"
1326  * indent-tabs-mode: nil
1327  * End:
1328  * vim: shiftwidth=4 tabstop=8 expandtab
1329  */
1330