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