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