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