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