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