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