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