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