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