Added support for SRW scan in yaz-client
[yaz-moved-to-github.git] / src / srwutil.c
1 /*
2  * Copyright (c) 2002-2004, Index Data.
3  * See the file LICENSE for details.
4  *
5  * $Id: srwutil.c,v 1.20 2005-01-08 01:20:19 adam Exp $
6  */
7 /**
8  * \file srwutil.c
9  * \brief Implements SRW/SRU utilities.
10  */
11
12 #include <yaz/srw.h>
13 #include <yaz/yaz-iconv.h>
14
15 static int hex_digit (int ch)
16 {
17     if (ch >= '0' && ch <= '9')
18         return ch - '0';
19     else if (ch >= 'a' && ch <= 'f')
20         return ch - 'a'+10;
21     else if (ch >= 'A' && ch <= 'F')
22         return ch - 'A'+10;
23     return 0;
24 }
25
26 int yaz_uri_array(const char *path, ODR o, char ***name, char ***val)
27 {
28     int no = 2;
29     const char *cp;
30     *name = 0;
31     if (*path != '?')
32         return no;
33     path++;
34     cp = path;
35     while ((cp = strchr(cp, '&')))
36     {
37         cp++;
38         no++;
39     }
40     *name = odr_malloc(o, no * sizeof(char**));
41     *val = odr_malloc(o, no * sizeof(char**));
42
43     for (no = 0; *path; no++)
44     {
45         const char *p1 = strchr(path, '=');
46         size_t i = 0;
47         char *ret;
48         if (!p1)
49             break;
50
51         (*name)[no] = odr_malloc(o, (p1-path)+1);
52         memcpy((*name)[no], path, p1-path);
53         (*name)[no][p1-path] = '\0';
54
55         path = p1 + 1;
56         p1 = strchr(path, '&');
57         if (!p1)
58             p1 = strlen(path) + path;
59         (*val)[no] = ret = odr_malloc(o, p1 - path + 1);
60         while (*path && *path != '&')
61         {
62             if (*path == '+')
63             {
64                 ret[i++] = ' ';
65                 path++;
66             }
67             else if (*path == '%' && path[1] && path[2])
68             {
69                 ret[i++] = hex_digit (path[1])*16 + hex_digit (path[2]);
70                 path = path + 3;
71             }
72             else
73                 ret[i++] = *path++;
74         }
75         ret[i] = '\0';
76
77         if (*path)
78             path++;
79     }
80     (*name)[no] = 0;
81     (*val)[no] = 0;
82     return no;
83 }
84
85 char *yaz_uri_val(const char *path, const char *name, ODR o)
86 {
87     size_t nlen = strlen(name);
88     if (*path != '?')
89         return 0;
90     path++;
91     while (path && *path)
92     {
93         const char *p1 = strchr(path, '=');
94         if (!p1)
95             break;
96         if ((size_t)(p1 - path) == nlen && !memcmp(path, name, nlen))
97         {
98             size_t i = 0;
99             char *ret;
100             
101             path = p1 + 1;
102             p1 = strchr(path, '&');
103             if (!p1)
104                 p1 = strlen(path) + path;
105             ret = (char *) odr_malloc(o, p1 - path + 1);
106             while (*path && *path != '&')
107             {
108                 if (*path == '+')
109                 {
110                     ret[i++] = ' ';
111                     path++;
112                 }
113                 else if (*path == '%' && path[1] && path[2])
114                 {
115                     ret[i++] = hex_digit (path[1])*16 + hex_digit (path[2]);
116                     path = path + 3;
117                 }
118                 else
119                     ret[i++] = *path++;
120             }
121             ret[i] = '\0';
122             return ret;
123         }
124         path = strchr(p1, '&');
125         if (path)
126             path++;
127     }
128     return 0;
129 }
130
131 void yaz_uri_val_int(const char *path, const char *name, ODR o, int **intp)
132 {
133     const char *v = yaz_uri_val(path, name, o);
134     if (v)
135         *intp = odr_intdup(o, atoi(v));
136 }
137
138 void yaz_mk_std_diagnostic(ODR o, Z_SRW_diagnostic *d, 
139                            int code, const char *details)
140 {
141     d->uri = (char *) odr_malloc(o, 50);
142     sprintf(d->uri, "info:srw/diagnostic/1/%d", code);
143     d->message = 0;
144     if (details)
145         d->details = odr_strdup(o, details);
146     else
147         d->details = 0;
148 }
149
150 void yaz_add_srw_diagnostic(ODR o, Z_SRW_diagnostic **d,
151                             int *num, int code, const char *addinfo)
152 {
153     Z_SRW_diagnostic *d_new;
154     d_new = (Z_SRW_diagnostic *) odr_malloc (o, (*num + 1)* sizeof(**d));
155     if (*num)
156         memcpy (d_new, *d, *num *sizeof(**d));
157     *d = d_new;
158
159     yaz_mk_std_diagnostic(o, *d + *num, code, addinfo);
160     (*num)++;
161 }
162
163 int yaz_srw_decode(Z_HTTP_Request *hreq, Z_SRW_PDU **srw_pdu,
164                    Z_SOAP **soap_package, ODR decode, char **charset)
165 {
166     if (!strcmp(hreq->method, "POST"))
167     {
168         const char *content_type = z_HTTP_header_lookup(hreq->headers,
169                                                         "Content-Type");
170         if (content_type && !yaz_strcmp_del("text/xml", content_type, "; "))
171         {
172             char *db = "Default";
173             const char *p0 = hreq->path, *p1;
174             int ret = -1;
175             const char *charset_p = 0;
176             
177             static Z_SOAP_Handler soap_handlers[3] = {
178 #if HAVE_XML2
179                 {"http://www.loc.gov/zing/srw/", 0,
180                  (Z_SOAP_fun) yaz_srw_codec},
181                 {"http://www.loc.gov/zing/srw/v1.0/", 0,
182                  (Z_SOAP_fun) yaz_srw_codec},
183 #endif
184                 {0, 0, 0}
185             };
186             
187             if (*p0 == '/')
188                 p0++;
189             p1 = strchr(p0, '?');
190             if (!p1)
191                 p1 = p0 + strlen(p0);
192             if (p1 != p0)
193             {
194                 db = (char*) odr_malloc(decode, p1 - p0 + 1);
195                 memcpy (db, p0, p1 - p0);
196                 db[p1 - p0] = '\0';
197             }
198
199             if (charset && (charset_p = strstr(content_type, "; charset=")))
200             {
201                 int i = 0;
202                 charset_p += 10;
203                 while (i < 20 && charset_p[i] &&
204                        !strchr("; \n\r", charset_p[i]))
205                     i++;
206                 *charset = (char*) odr_malloc(decode, i+1);
207                 memcpy(*charset, charset_p, i);
208                 (*charset)[i] = '\0';
209             }
210             ret = z_soap_codec(decode, soap_package, 
211                                &hreq->content_buf, &hreq->content_len,
212                                soap_handlers);
213             if (ret == 0 && (*soap_package)->which == Z_SOAP_generic)
214             {
215                 *srw_pdu = (Z_SRW_PDU*) (*soap_package)->u.generic->p;
216                 
217                 if ((*srw_pdu)->which == Z_SRW_searchRetrieve_request &&
218                     (*srw_pdu)->u.request->database == 0)
219                     (*srw_pdu)->u.request->database = db;
220
221                 if ((*srw_pdu)->which == Z_SRW_explain_request &&
222                     (*srw_pdu)->u.explain_request->database == 0)
223                     (*srw_pdu)->u.explain_request->database = db;
224
225                 if ((*srw_pdu)->which == Z_SRW_scan_request &&
226                     (*srw_pdu)->u.scan_request->database == 0)
227                     (*srw_pdu)->u.scan_request->database = db;
228
229                 return 0;
230             }
231             return 1;
232         }
233     }
234     return 2;
235 }
236
237 int yaz_sru_decode(Z_HTTP_Request *hreq, Z_SRW_PDU **srw_pdu,
238                    Z_SOAP **soap_package, ODR decode, char **charset,
239                    Z_SRW_diagnostic **diag, int *num_diag)
240 {
241 #if HAVE_XML2
242     static Z_SOAP_Handler soap_handlers[2] = {
243         {"http://www.loc.gov/zing/srw/", 0,
244          (Z_SOAP_fun) yaz_srw_codec},
245         {0, 0, 0}
246     };
247 #endif
248     if (!strcmp(hreq->method, "GET"))
249     {
250         char *db = "Default";
251         const char *p0 = hreq->path, *p1;
252         const char *operation = 0;
253         char *version = 0;
254         char *query = 0;
255         char *pQuery = 0;
256         char *sortKeys = 0;
257         char *stylesheet = 0;
258         char *scanClause = 0;
259         char *pScanClause = 0;
260         char *recordXPath = 0;
261         char *recordSchema = 0;
262         char *recordPacking = "xml";  /* xml packing is default for SRU */
263         char *maximumRecords = 0;
264         char *startRecord = 0;
265         char **uri_name;
266         char **uri_val;
267
268         if (charset)
269             *charset = 0;
270         if (*p0 == '/')
271             p0++;
272         p1 = strchr(p0, '?');
273         if (!p1)
274             p1 = p0 + strlen(p0);
275         if (p1 != p0)
276         {
277             db = (char*) odr_malloc(decode, p1 - p0 + 1);
278             memcpy (db, p0, p1 - p0);
279             db[p1 - p0] = '\0';
280         }
281         yaz_uri_array(p1, decode, &uri_name, &uri_val);
282 #if HAVE_XML2
283         if (uri_name)
284         {
285             int i;
286             for (i = 0; uri_name[i]; i++)
287             {
288                 char *n = uri_name[i];
289                 char *v = uri_val[i];
290                 if (!strcmp(n, "query"))
291                     query = v;
292                 else if (!strcmp(n, "x-pquery"))
293                     pQuery = v;
294                 else if (!strcmp(n, "operation"))
295                     operation = v;
296                 else if (!strcmp(n, "stylesheet"))
297                     stylesheet = v;
298                 else if (!strcmp(n, "sortKeys"))
299                     sortKeys = v;
300                 else if (!strcmp(n, "recordXPath"))
301                     recordXPath = v;
302                 else if (!strcmp(n, "recordSchema"))
303                     recordSchema = v;
304                 else if (!strcmp(n, "recordPacking"))
305                     recordPacking = v;
306                 else if (!strcmp(n, "version"))
307                     version = v;
308                 else if (!strcmp(n, "scanClause"))
309                     scanClause = v;
310                 else if (!strcmp(n, "x-ScanClause"))
311                     pScanClause = v;
312                 else if (!strcmp(n, "maximumRecords"))
313                     maximumRecords = v;
314                 else if (!strcmp(n, "startRecord"))
315                     startRecord = v;
316                 else
317                     yaz_add_srw_diagnostic(decode, diag, num_diag, 8, n);
318             }
319         }
320         if (!version)
321         {
322             if (uri_name)
323                 yaz_add_srw_diagnostic(decode, diag, num_diag, 7, "version");
324             version = "1.1";
325         }
326         if (strcmp(version, "1.1"))
327             yaz_add_srw_diagnostic(decode, diag, num_diag, 5, "1.1");
328         if (!operation)
329         {
330             if (uri_name)
331                 yaz_add_srw_diagnostic(decode, diag, num_diag, 7, "operation");
332             operation = "explain";
333         }
334         if (!strcmp(operation, "searchRetrieve"))
335         {
336             Z_SRW_PDU *sr = yaz_srw_get(decode, Z_SRW_searchRetrieve_request);
337
338             sr->srw_version = version;
339             *srw_pdu = sr;
340             if (query)
341             {
342                 sr->u.request->query_type = Z_SRW_query_type_cql;
343                 sr->u.request->query.cql = query;
344             }
345             else if (pQuery)
346             {
347                 sr->u.request->query_type = Z_SRW_query_type_pqf;
348                 sr->u.request->query.pqf = pQuery;
349             }
350             else
351                 yaz_add_srw_diagnostic(decode, diag, num_diag, 7, "query");
352
353             if (sortKeys)
354             {
355                 sr->u.request->sort_type = Z_SRW_sort_type_sort;
356                 sr->u.request->sort.sortKeys = sortKeys;
357             }
358             sr->u.request->recordXPath = recordXPath;
359             sr->u.request->recordSchema = recordSchema;
360             sr->u.request->recordPacking = recordPacking;
361             sr->u.request->stylesheet = stylesheet;
362
363             if (maximumRecords)
364                 sr->u.request->maximumRecords =
365                     odr_intdup(decode, atoi(maximumRecords));
366             if (startRecord)
367                 sr->u.request->startRecord =
368                     odr_intdup(decode, atoi(startRecord));
369
370             sr->u.request->database = db;
371
372             (*soap_package) = odr_malloc(decode, sizeof(**soap_package));
373             (*soap_package)->which = Z_SOAP_generic;
374             
375             (*soap_package)->u.generic =
376                 odr_malloc(decode, sizeof(*(*soap_package)->u.generic));
377             
378             (*soap_package)->u.generic->p = sr;
379             (*soap_package)->u.generic->ns = soap_handlers[0].ns;
380             (*soap_package)->u.generic->no = 0;
381             
382             (*soap_package)->ns = "SRU";
383
384             return 0;
385         }
386         else if (!strcmp(operation, "explain"))
387         {
388             Z_SRW_PDU *sr = yaz_srw_get(decode, Z_SRW_explain_request);
389
390             sr->srw_version = version;
391             *srw_pdu = sr;
392             sr->u.explain_request->recordPacking = recordPacking;
393             sr->u.explain_request->database = db;
394
395             sr->u.explain_request->stylesheet = stylesheet;
396
397             (*soap_package) = odr_malloc(decode, sizeof(**soap_package));
398             (*soap_package)->which = Z_SOAP_generic;
399             
400             (*soap_package)->u.generic =
401                 odr_malloc(decode, sizeof(*(*soap_package)->u.generic));
402             
403             (*soap_package)->u.generic->p = sr;
404             (*soap_package)->u.generic->ns = soap_handlers[0].ns;
405             (*soap_package)->u.generic->no = 0;
406             
407             (*soap_package)->ns = "SRU";
408
409             return 0;
410         }
411         else if (!strcmp(operation, "scan"))
412         {
413             Z_SRW_PDU *sr = yaz_srw_get(decode, Z_SRW_scan_request);
414
415             if (scanClause)
416             {
417                 sr->u.scan_request->query_type = Z_SRW_query_type_cql;
418                 sr->u.scan_request->scanClause.cql = scanClause;
419             }
420             else if (pScanClause)
421             {
422                 sr->u.scan_request->query_type = Z_SRW_query_type_pqf;
423                 sr->u.scan_request->scanClause.pqf = pScanClause;
424             }
425             else
426                 yaz_add_srw_diagnostic(decode, diag, num_diag, 7,
427                                        "scanClause");
428             sr->srw_version = version;
429             *srw_pdu = sr;
430             sr->u.scan_request->database = db;
431             sr->u.scan_request->stylesheet = stylesheet;
432
433             (*soap_package) = odr_malloc(decode, sizeof(**soap_package));
434             (*soap_package)->which = Z_SOAP_generic;
435             
436             (*soap_package)->u.generic =
437                 odr_malloc(decode, sizeof(*(*soap_package)->u.generic));
438             
439             (*soap_package)->u.generic->p = sr;
440             (*soap_package)->u.generic->ns = soap_handlers[0].ns;
441             (*soap_package)->u.generic->no = 0;
442             
443             (*soap_package)->ns = "SRU";
444
445             return 0;
446         }
447         else
448         {
449             /* unsupported operation ... */
450             /* Act as if we received a explain request and throw diagnostic. */
451
452             Z_SRW_PDU *sr = yaz_srw_get(decode, Z_SRW_explain_request);
453
454             sr->srw_version = version;
455             *srw_pdu = sr;
456             sr->u.explain_request->recordPacking = recordPacking;
457             sr->u.explain_request->database = db;
458
459             sr->u.explain_request->stylesheet = stylesheet;
460
461             (*soap_package) = odr_malloc(decode, sizeof(**soap_package));
462             (*soap_package)->which = Z_SOAP_generic;
463             
464             (*soap_package)->u.generic =
465                 odr_malloc(decode, sizeof(*(*soap_package)->u.generic));
466             
467             (*soap_package)->u.generic->p = sr;
468             (*soap_package)->u.generic->ns = soap_handlers[0].ns;
469             (*soap_package)->u.generic->no = 0;
470             
471             (*soap_package)->ns = "SRU";
472
473             yaz_add_srw_diagnostic(decode, diag, num_diag, 4, operation);
474             return 0;
475         }
476 #endif
477         return 1;
478     }
479     return 2;
480 }
481
482 Z_SRW_PDU *yaz_srw_get(ODR o, int which)
483 {
484     Z_SRW_PDU *sr = (Z_SRW_PDU *) odr_malloc(o, sizeof(*o));
485
486     sr->srw_version = odr_strdup(o, "1.1");
487     sr->which = which;
488     switch(which)
489     {
490     case Z_SRW_searchRetrieve_request:
491         sr->u.request = (Z_SRW_searchRetrieveRequest *)
492             odr_malloc(o, sizeof(*sr->u.request));
493         sr->u.request->query_type = Z_SRW_query_type_cql;
494         sr->u.request->query.cql = 0;
495         sr->u.request->sort_type = Z_SRW_sort_type_none;
496         sr->u.request->sort.none = 0;
497         sr->u.request->startRecord = 0;
498         sr->u.request->maximumRecords = 0;
499         sr->u.request->recordSchema = 0;
500         sr->u.request->recordPacking = 0;
501         sr->u.request->recordXPath = 0;
502         sr->u.request->database = 0;
503         sr->u.request->resultSetTTL = 0;
504         sr->u.request->stylesheet = 0;
505         break;
506     case Z_SRW_searchRetrieve_response:
507         sr->u.response = (Z_SRW_searchRetrieveResponse *)
508             odr_malloc(o, sizeof(*sr->u.response));
509         sr->u.response->numberOfRecords = 0;
510         sr->u.response->resultSetId = 0;
511         sr->u.response->resultSetIdleTime = 0;
512         sr->u.response->records = 0;
513         sr->u.response->num_records = 0;
514         sr->u.response->diagnostics = 0;
515         sr->u.response->num_diagnostics = 0;
516         sr->u.response->nextRecordPosition = 0;
517         break;
518     case Z_SRW_explain_request:
519         sr->u.explain_request = (Z_SRW_explainRequest *)
520             odr_malloc(o, sizeof(*sr->u.explain_request));
521         sr->u.explain_request->recordPacking = 0;
522         sr->u.explain_request->database = 0;
523         sr->u.explain_request->stylesheet = 0;
524         break;
525     case Z_SRW_explain_response:
526         sr->u.explain_response = (Z_SRW_explainResponse *)
527             odr_malloc(o, sizeof(*sr->u.explain_response));
528         sr->u.explain_response->record.recordData_buf = 0;
529         sr->u.explain_response->record.recordData_len = 0;
530         sr->u.explain_response->record.recordSchema = 0;
531         sr->u.explain_response->record.recordPosition = 0;
532         sr->u.explain_response->record.recordPacking =
533             Z_SRW_recordPacking_string;
534         sr->u.explain_response->diagnostics = 0;
535         sr->u.explain_response->num_diagnostics = 0;
536         break;
537     case Z_SRW_scan_request:
538         sr->u.scan_request = (Z_SRW_scanRequest *)
539             odr_malloc(o, sizeof(*sr->u.scan_request));
540         sr->u.scan_request->database = 0;
541         sr->u.scan_request->stylesheet = 0;
542         sr->u.scan_request->maximumTerms = 0;
543         sr->u.scan_request->responsePosition = 0;
544         sr->u.scan_request->query_type = Z_SRW_query_type_cql;
545         sr->u.scan_request->scanClause.cql = 0;
546         break;
547     case Z_SRW_scan_response:
548         sr->u.scan_response = (Z_SRW_scanResponse *)
549             odr_malloc(o, sizeof(*sr->u.scan_response));
550         sr->u.scan_response->terms = 0;
551         sr->u.scan_response->num_terms = 0;
552         sr->u.scan_response->diagnostics = 0;
553         sr->u.scan_response->num_diagnostics = 0;
554     }
555     return sr;
556 }
557
558
559
560 /* bib1:srw */
561 static int srw_bib1_map[] = {
562     1, 1,
563     2, 2,
564     3, 11,
565     4, 35,
566     5, 12,
567     6, 38,
568     7, 30,
569     8, 32,
570     9, 29,
571     108, 10,  /* Malformed query : Syntax error */
572     10, 10,
573     11, 12,
574     11, 23,
575     12, 60,
576     13, 61,
577     13, 62,
578     14, 63,
579     14, 64,
580     14, 65,
581     15, 68,
582     15, 69,
583     16, 70,
584     17, 70,
585     18, 50,
586     19, 55,
587     20, 56, 
588     21, 52,
589     22, 50,
590     23, 3,
591     24, 66,
592     25, 66,
593     26, 66,
594     27, 51,
595     28, 52,
596     29, 52,
597     30, 51,
598     31, 57,
599     32, 58,
600     33, 59,
601     100, 1, /* bad map */
602     101, 3,
603     102, 3,
604     103, 3,
605     104, 3,
606     105, 3, 
607     106, 66,
608     107, 11,
609     108, 13,
610     108, 14,
611     108, 25,
612     108, 26,
613     108, 27,
614     108, 45,
615         
616     109, 2,
617     110, 37,
618     111, 1,
619     112, 58,
620     113, 10,
621     114, 16,
622     115, 16,
623     116, 16,
624     117, 19,
625     117, 20,
626     118, 22,
627     119, 32,
628     119, 31,
629     120, 28,
630     121, 15,
631     122, 32,
632     123, 22,
633     123, 17,
634     123, 18,
635     124, 24,
636     125, 36,
637     126, 36, 
638     127, 36,
639     128, 51,
640     129, 39,
641     130, 43,
642     131, 40,
643     132, 42,
644     201, 44,
645     201, 33,
646     201, 34,
647     202, 41,
648     203, 43,
649     205, 1,  /* bad map */
650     206, 1,  /* bad map */
651     207, 89,
652     208, 1,  /* bad map */
653     209, 80,
654     210, 80,
655     210, 81,
656     211, 84,
657     212, 85,
658     213, 92,
659     214, 90,
660     215, 91,
661     216, 92,
662     217, 63,
663     218, 1,  /* bad map */
664     219, 1,  /* bad map */
665     220, 1,  /* bad map */
666     221, 1,  /* bad map */
667     222, 1,  /* bad map */
668     223, 1,  /* bad map */
669     224, 1,  /* bad map */
670     225, 1,  /* bad map */
671     226, 1,  /* bad map */
672     227, 66,
673     228, 1,  /* bad map */
674     229, 36,
675     230, 83,
676     231, 89,
677     232, 1,
678     233, 1, /* bad map */
679     234, 1, /* bad map */
680     235, 2,
681     236, 3, 
682     237, 82,
683     238, 67,
684     239, 66,
685     240, 1, /* bad map */
686     241, 1, /* bad map */
687     242, 70,
688     243, 1, /* bad map */
689     244, 66,
690     245, 10,
691     246, 10,
692     247, 10,
693     1001, 1, /* bad map */
694     1002, 1, /* bad map */
695     1003, 1, /* bad map */
696     1004, 1, /* bad map */
697     1005, 1, /* bad map */
698     1006, 1, /* bad map */
699     1007, 100,
700     1008, 1, 
701     1009, 1,
702     1010, 3,
703     1011, 3,
704     1012, 3,
705     1013, 3,
706     1014, 3,
707     1015, 3,
708     1015, 3,
709     1016, 3,
710     1017, 3,
711     1018, 2,
712     1019, 2,
713     1020, 2,
714     1021, 3,
715     1022, 3,
716     1023, 3,
717     1024, 16,
718     1025, 3,
719     1026, 64,
720     1027, 1,
721     1028, 65,
722     1029, 1,
723     1040, 1,
724     /* 1041-1065 */
725     1066, 66,
726     1066, 67,
727     0
728 };
729
730 int yaz_diag_bib1_to_srw (int code)
731 {
732     const int *p = srw_bib1_map;
733     while (*p)
734     {
735         if (code == p[0])
736             return p[1];
737         p += 2;
738     }
739     return 1;
740 }
741
742 int yaz_diag_srw_to_bib1(int code)
743 {
744     const int *p = srw_bib1_map;
745     while (*p)
746     {
747         if (code == p[1])
748             return p[0];
749         p += 2;
750     }
751     return 1;
752 }
753