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