Read recordXPath for SRU
[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.7 2004-01-07 22:27:41 adam Exp $
6  */
7
8 #include <yaz/srw.h>
9 #include <yaz/yaz-iconv.h>
10
11 static int hex_digit (int ch)
12 {
13     if (ch >= '0' && ch <= '9')
14         return ch - '0';
15     else if (ch >= 'a' && ch <= 'f')
16         return ch - 'a'+10;
17     else if (ch >= 'A' && ch <= 'F')
18         return ch - 'A'+10;
19     return 0;
20 }
21
22 char *yaz_uri_val(const char *path, const char *name, ODR o)
23 {
24     size_t nlen = strlen(name);
25     if (*path != '?')
26         return 0;
27     path++;
28     while (path && *path)
29     {
30         const char *p1 = strchr(path, '=');
31         if (!p1)
32             break;
33         if ((size_t)(p1 - path) == nlen && !memcmp(path, name, nlen))
34         {
35             size_t i = 0;
36             char *ret;
37             
38             path = p1 + 1;
39             p1 = strchr(path, '&');
40             if (!p1)
41                 p1 = strlen(path) + path;
42             ret = odr_malloc(o, p1 - path + 1);
43             while (*path && *path != '&')
44             {
45                 if (*path == '+')
46                 {
47                     ret[i++] = ' ';
48                     path++;
49                 }
50                 else if (*path == '%' && path[1] && path[2])
51                 {
52                     ret[i++] = hex_digit (path[1])*16 + hex_digit (path[2]);
53                     path = path + 3;
54                 }
55                 else
56                     ret[i++] = *path++;
57             }
58             ret[i] = '\0';
59             return ret;
60         }
61         path = strchr(p1, '&');
62         if (path)
63             path++;
64     }
65     return 0;
66 }
67
68 void yaz_uri_val_int(const char *path, const char *name, ODR o, int **intp)
69 {
70     const char *v = yaz_uri_val(path, name, o);
71     if (v)
72         *intp = odr_intdup(o, atoi(v));
73 }
74
75 int yaz_srw_decode(Z_HTTP_Request *hreq, Z_SRW_PDU **srw_pdu,
76                    Z_SOAP **soap_package, ODR decode, char **charset)
77 {
78     if (!strcmp(hreq->method, "POST"))
79     {
80         const char *content_type = z_HTTP_header_lookup(hreq->headers,
81                                                         "Content-Type");
82         if (content_type && !yaz_strcmp_del("text/xml", content_type, "; "))
83         {
84             char *db = "Default";
85             const char *p0 = hreq->path, *p1;
86             int ret = -1;
87             const char *charset_p = 0;
88             
89             static Z_SOAP_Handler soap_handlers[3] = {
90 #if HAVE_XML2
91                 {"http://www.loc.gov/zing/srw/", 0,
92                  (Z_SOAP_fun) yaz_srw_codec},
93                 {"http://www.loc.gov/zing/srw/v1.0/", 0,
94                  (Z_SOAP_fun) yaz_srw_codec},
95 #endif
96                 {0, 0, 0}
97             };
98             
99             if (*p0 == '/')
100                 p0++;
101             p1 = strchr(p0, '?');
102             if (!p1)
103                 p1 = p0 + strlen(p0);
104             if (p1 != p0)
105             {
106                 db = (char*) odr_malloc(decode, p1 - p0 + 1);
107                 memcpy (db, p0, p1 - p0);
108                 db[p1 - p0] = '\0';
109             }
110
111             if (charset && (charset_p = strstr(content_type, "; charset=")))
112             {
113                 int i = 0;
114                 charset_p += 10;
115                 while (i < 20 && charset_p[i] &&
116                        !strchr("; \n\r", charset_p[i]))
117                     i++;
118                 *charset = (char*) odr_malloc(decode, i+1);
119                 memcpy(*charset, charset_p, i);
120                 (*charset)[i] = '\0';
121             }
122             ret = z_soap_codec(decode, soap_package, 
123                                &hreq->content_buf, &hreq->content_len,
124                                soap_handlers);
125             if (ret == 0 && (*soap_package)->which == Z_SOAP_generic)
126             {
127                 *srw_pdu = (Z_SRW_PDU*) (*soap_package)->u.generic->p;
128                 
129                 if ((*srw_pdu)->which == Z_SRW_searchRetrieve_request &&
130                     (*srw_pdu)->u.request->database == 0)
131                     (*srw_pdu)->u.request->database = db;
132
133                 if ((*srw_pdu)->which == Z_SRW_explain_request &&
134                     (*srw_pdu)->u.explain_request->database == 0)
135                     (*srw_pdu)->u.explain_request->database = db;
136
137                 if ((*srw_pdu)->which == Z_SRW_scan_request &&
138                     (*srw_pdu)->u.scan_request->database == 0)
139                     (*srw_pdu)->u.scan_request->database = db;
140
141                 return 0;
142             }
143             return 1;
144         }
145     }
146     return 2;
147 }
148
149 int yaz_sru_decode(Z_HTTP_Request *hreq, Z_SRW_PDU **srw_pdu,
150                    Z_SOAP **soap_package, ODR decode, char **charset)
151 {
152 #if HAVE_XML2
153     static Z_SOAP_Handler soap_handlers[2] = {
154         {"http://www.loc.gov/zing/srw/", 0,
155          (Z_SOAP_fun) yaz_srw_codec},
156         {0, 0, 0}
157     };
158 #endif
159     if (!strcmp(hreq->method, "GET"))
160     {
161         char *db = "Default";
162         const char *p0 = hreq->path, *p1;
163         const char *operation = 0;
164         char *query = 0;
165         char *pQuery = 0;
166         char *stylesheet = 0;
167         
168         if (charset)
169             *charset = 0;
170         if (*p0 == '/')
171             p0++;
172         p1 = strchr(p0, '?');
173         if (!p1)
174             p1 = p0 + strlen(p0);
175         if (p1 != p0)
176         {
177             db = (char*) odr_malloc(decode, p1 - p0 + 1);
178             memcpy (db, p0, p1 - p0);
179             db[p1 - p0] = '\0';
180         }
181 #if HAVE_XML2
182         query = yaz_uri_val(p1, "query", decode);
183         pQuery = yaz_uri_val(p1, "pQuery", decode);
184         operation = yaz_uri_val(p1, "operation", decode);
185         stylesheet = yaz_uri_val(p1, "stylesheet", decode);
186         if (!operation)
187             operation = "explain";
188         if ((operation && !strcmp(operation, "searchRetrieve"))
189             || pQuery || query)
190         {
191             Z_SRW_PDU *sr = yaz_srw_get(decode, Z_SRW_searchRetrieve_request);
192             char *sortKeys = yaz_uri_val(p1, "sortKeys", decode);
193             
194             *srw_pdu = sr;
195             if (query)
196             {
197                 sr->u.request->query_type = Z_SRW_query_type_cql;
198                 sr->u.request->query.cql = query;
199             }
200             if (pQuery)
201             {
202                 sr->u.request->query_type = Z_SRW_query_type_pqf;
203                 sr->u.request->query.pqf = pQuery;
204             }
205             if (sortKeys)
206             {
207                 sr->u.request->sort_type = Z_SRW_sort_type_sort;
208                 sr->u.request->sort.sortKeys = sortKeys;
209             }
210             sr->u.request->recordXPath = yaz_uri_val(p1, "recordXPath", decode);
211             sr->u.request->recordSchema = yaz_uri_val(p1, "recordSchema", decode);
212             sr->u.request->recordPacking = yaz_uri_val(p1, "recordPacking", decode);
213             sr->u.request->stylesheet = stylesheet;
214
215             if (!sr->u.request->recordPacking)
216                 sr->u.request->recordPacking = "xml";
217             yaz_uri_val_int(p1, "maximumRecords", decode, 
218                         &sr->u.request->maximumRecords);
219             yaz_uri_val_int(p1, "startRecord", decode,
220                         &sr->u.request->startRecord);
221
222             sr->u.request->database = db;
223
224             (*soap_package) = odr_malloc(decode, sizeof(**soap_package));
225             (*soap_package)->which = Z_SOAP_generic;
226             
227             (*soap_package)->u.generic =
228                 odr_malloc(decode, sizeof(*(*soap_package)->u.generic));
229             
230             (*soap_package)->u.generic->p = sr;
231             (*soap_package)->u.generic->ns = soap_handlers[0].ns;
232             (*soap_package)->u.generic->no = 0;
233             
234             (*soap_package)->ns = "SRU";
235
236             return 0;
237         }
238         else if (p1 && !strcmp(operation, "explain"))
239         {
240             Z_SRW_PDU *sr = yaz_srw_get(decode, Z_SRW_explain_request);
241
242             *srw_pdu = sr;
243             sr->u.explain_request->recordPacking =
244                 yaz_uri_val(p1, "recordPacking", decode);
245             if (!sr->u.explain_request->recordPacking)
246                 sr->u.explain_request->recordPacking = "xml";
247             sr->u.explain_request->database = db;
248
249             sr->u.explain_request->stylesheet = stylesheet;
250
251             (*soap_package) = odr_malloc(decode, sizeof(**soap_package));
252             (*soap_package)->which = Z_SOAP_generic;
253             
254             (*soap_package)->u.generic =
255                 odr_malloc(decode, sizeof(*(*soap_package)->u.generic));
256             
257             (*soap_package)->u.generic->p = sr;
258             (*soap_package)->u.generic->ns = soap_handlers[0].ns;
259             (*soap_package)->u.generic->no = 0;
260             
261             (*soap_package)->ns = "SRU";
262
263             return 0;
264         }
265 #endif
266         return 1;
267     }
268     return 2;
269 }
270
271 Z_SRW_PDU *yaz_srw_get(ODR o, int which)
272 {
273     Z_SRW_PDU *sr = odr_malloc(o, sizeof(*o));
274
275     sr->srw_version = odr_strdup(o, "1.1");
276     sr->which = which;
277     switch(which)
278     {
279     case Z_SRW_searchRetrieve_request:
280         sr->u.request = (Z_SRW_searchRetrieveRequest *)
281             odr_malloc(o, sizeof(*sr->u.request));
282         sr->u.request->query_type = Z_SRW_query_type_cql;
283         sr->u.request->query.cql = 0;
284         sr->u.request->sort_type = Z_SRW_sort_type_none;
285         sr->u.request->sort.none = 0;
286         sr->u.request->startRecord = 0;
287         sr->u.request->maximumRecords = 0;
288         sr->u.request->recordSchema = 0;
289         sr->u.request->recordPacking = 0;
290         sr->u.request->recordXPath = 0;
291         sr->u.request->database = 0;
292         sr->u.request->resultSetTTL = 0;
293         sr->u.request->stylesheet = 0;
294         break;
295     case Z_SRW_searchRetrieve_response:
296         sr->u.response = (Z_SRW_searchRetrieveResponse *)
297             odr_malloc(o, sizeof(*sr->u.response));
298         sr->u.response->numberOfRecords = 0;
299         sr->u.response->resultSetId = 0;
300         sr->u.response->resultSetIdleTime = 0;
301         sr->u.response->records = 0;
302         sr->u.response->num_records = 0;
303         sr->u.response->diagnostics = 0;
304         sr->u.response->num_diagnostics = 0;
305         sr->u.response->nextRecordPosition = 0;
306         break;
307     case Z_SRW_explain_request:
308         sr->u.explain_request = (Z_SRW_explainRequest *)
309             odr_malloc(o, sizeof(*sr->u.explain_request));
310         sr->u.explain_request->recordPacking = 0;
311         sr->u.explain_request->database = 0;
312         sr->u.explain_request->stylesheet = 0;
313         break;
314     case Z_SRW_explain_response:
315         sr->u.explain_response = (Z_SRW_explainResponse *)
316             odr_malloc(o, sizeof(*sr->u.explain_response));
317         sr->u.explain_response->record.recordData_buf = 0;
318         sr->u.explain_response->record.recordData_len = 0;
319         sr->u.explain_response->record.recordSchema = 0;
320         sr->u.explain_response->record.recordPosition = 0;
321         sr->u.explain_response->record.recordPacking =
322             Z_SRW_recordPacking_string;
323         sr->u.explain_response->diagnostics = 0;
324         sr->u.explain_response->num_diagnostics = 0;
325     }
326     return sr;
327 }
328
329
330 static struct {
331     int code;
332     const char *msg;
333 } yaz_srw_codes [] = {
334 {1, "Permanent system error"}, 
335 {2, "System temporarily unavailable"}, 
336 {3, "Authentication error"}, 
337 /* Diagnostics Relating to CQL */
338 {10, "Query syntax error"}, 
339 {11, "Unsupported query type"}, 
340 {12, "Too many characters in query"}, 
341 {13, "Unbalanced or illegal use of parentheses"}, 
342 {14, "Unbalanced or illegal use of quotes"}, 
343 {15, "Illegal or unsupported context set"}, 
344 {16, "Illegal or unsupported index"}, 
345 {17, "Illegal or unsupported combination of index and context set"}, 
346 {18, "Illegal or unsupported combination of indexes"}, 
347 {19, "Illegal or unsupported relation"}, 
348 {20, "Illegal or unsupported relation modifier"}, 
349 {21, "Illegal or unsupported combination of relation modifers"}, 
350 {22, "Illegal or unsupported combination of relation and index"}, 
351 {23, "Too many characters in term"}, 
352 {24, "Illegal combination of relation and term"}, 
353 {25, "Special characters not quoted in term"}, 
354 {26, "Non special character escaped in term"}, 
355 {27, "Empty term unsupported"}, 
356 {28, "Masking character not supported"}, 
357 {29, "Masked words too short"}, 
358 {30, "Too many masking characters in term"}, 
359 {31, "Anchoring character not supported"}, 
360 {32, "Anchoring character in illegal or unsupported position"}, 
361 {33, "Combination of proximity/adjacency and masking characters not supported"}, 
362 {34, "Combination of proximity/adjacency and anchoring characters not supported"}, 
363 {35, "Terms only exclusion (stop) words"}, 
364 {36, "Term in invalid format for index or relation"}, 
365 {37, "Illegal or unsupported boolean operator"}, 
366 {38, "Too many boolean operators in query"}, 
367 {39, "Proximity not supported"}, 
368 {40, "Illegal or unsupported proximity relation"}, 
369 {41, "Illegal or unsupported proximity distance"}, 
370 {42, "Illegal or unsupported proximity unit"}, 
371 {43, "Illegal or unsupported proximity ordering"}, 
372 {44, "Illegal or unsupported combination of proximity modifiers"}, 
373 {45, "context set name (prefix) assigned to multiple identifiers"}, 
374 /* Diagnostics Relating to Result Sets */
375 {50, "Result sets not supported"}, 
376 {51, "Result set does not exist"}, 
377 {52, "Result set temporarily unavailable"}, 
378 {53, "Result sets only supported for retrieval"}, 
379 {54, "Retrieval may only occur from an existing result set"}, 
380 {55, "Combination of result sets with search terms not supported"}, 
381 {56, "Only combination of single result set with search terms supported"}, 
382 {57, "Result set created but no records available"}, 
383 {58, "Result set created with unpredictable partial results available"}, 
384 {59, "Result set created with valid partial results available"}, 
385 /* Diagnostics Relating to Records */
386 {60, "Too many records retrieved"}, 
387 {61, "First record position out of range"}, 
388 {62, "Negative number of records requested"}, 
389 {63, "System error in retrieving records"}, 
390 {64, "Record temporarily unavailable"}, 
391 {65, "Record does not exist"}, 
392 {66, "Unknown schema for retrieval"}, 
393 {67, "Record not available in this schema"}, 
394 {68, "Not authorised to send record"}, 
395 {69, "Not authorised to send record in this schema"}, 
396 {70, "Record too large to send"}, 
397 /* Diagnostics Relating to Sorting */
398 {80, "Sort not supported"}, 
399 {81, "Unsupported sort type (sortKeys vs xSortKeys)"}, 
400 {82, "Illegal or unsupported sort sequence"}, 
401 {83, "Too many records"}, 
402 {84, "Too many sort keys"}, 
403 {85, "Duplicate sort keys"}, 
404 {86, "Incompatible record formats"}, 
405 {87, "Unsupported schema for sort"}, 
406 {88, "Unsupported tag path for sort"}, 
407 {89, "Tag path illegal or unsupported for schema"}, 
408 {90, "Illegal or unsupported direction value"}, 
409 {91, "Illegal or unsupported case value"}, 
410 {92, "Illegal or unsupported missing value action"}, 
411 /* Diagnostics Relating to Explain */
412 {100, "Explain not supported"}, 
413 {101, "Explain request type not supported (SOAP vs GET)"}, 
414 {102, "Explain record temporarily unavailable"},
415 {0, 0}
416 };
417
418 const char *yaz_diag_srw_str (int code)
419 {
420     int i;
421     for (i = 0; yaz_srw_codes[i].code; i++)
422         if (yaz_srw_codes[i].code == code)
423             return yaz_srw_codes[i].msg;
424     return 0;
425 }
426
427
428 /* bib1:srw */
429 static int srw_bib1_map[] = {
430     1, 1,
431     2, 2,
432     3, 11,
433     4, 35,
434     5, 12,
435     6, 38,
436     7, 30,
437     8, 32,
438     9, 29,
439     108, 10,  /* Malformed query : Syntax error */
440     10, 10,
441     11, 12,
442     11, 23,
443     12, 60,
444     13, 61,
445     13, 62,
446     14, 63,
447     14, 64,
448     14, 65,
449     15, 68,
450     15, 69,
451     16, 70,
452     17, 70,
453     18, 50,
454     19, 55,
455     20, 56, 
456     21, 52,
457     22, 50,
458     23, 3,
459     24, 66,
460     25, 66,
461     26, 66,
462     27, 51,
463     28, 52,
464     29, 52,
465     30, 51,
466     31, 57,
467     32, 58,
468     33, 59,
469     100, 1, /* bad map */
470     101, 3,
471     102, 3,
472     103, 3,
473     104, 3,
474     105, 3, 
475     106, 66,
476     107, 11,
477     108, 10,
478     108, 13,
479     108, 14,
480     108, 25,
481     108, 26,
482     108, 27,
483     108, 45,
484         
485     109, 1,
486     110, 37,
487     111, 1,
488     112, 58,
489     113, 10,
490     114, 16,
491     115, 16,
492     116, 16,
493     117, 19,
494     118, 22,
495     119, 32,
496     119, 31,
497     120, 28,
498     121, 15,
499     122, 32,
500     123, 22,
501     123, 17,
502     123, 18,
503     124, 24,
504     125, 36,
505     126, 36, 
506     127, 36,
507     128, 51,
508     129, 39,
509     130, 43,
510     131, 40,
511     132, 42,
512     201, 44,
513     201, 33,
514     201, 34,
515     202, 41,
516     203, 43,
517     205, 1,  /* bad map */
518     206, 1,  /* bad map */
519     207, 89,
520     208, 1,  /* bad map */
521     209, 80,
522     210, 80,
523     210, 81,
524     211, 84,
525     212, 85,
526     213, 92,
527     214, 90,
528     215, 91,
529     216, 92,
530     217, 63,
531     218, 1,  /* bad map */
532     219, 1,  /* bad map */
533     220, 1,  /* bad map */
534     221, 1,  /* bad map */
535     222, 1,  /* bad map */
536     223, 1,  /* bad map */
537     224, 1,  /* bad map */
538     225, 1,  /* bad map */
539     226, 1,  /* bad map */
540     227, 66,
541     228, 1,  /* bad map */
542     229, 36,
543     230, 83,
544     231, 89,
545     232, 1,
546     233, 1, /* bad map */
547     234, 1, /* bad map */
548     235, 2,
549     236, 3, 
550     237, 82,
551     238, 67,
552     239, 66,
553     240, 1, /* bad map */
554     241, 1, /* bad map */
555     242, 70,
556     243, 1, /* bad map */
557     244, 66,
558     245, 10,
559     246, 10,
560     247, 10,
561     1001, 1, /* bad map */
562     1002, 1, /* bad map */
563     1003, 1, /* bad map */
564     1004, 1, /* bad map */
565     1005, 1, /* bad map */
566     1006, 1, /* bad map */
567     1007, 100,
568     1008, 1, 
569     1009, 1,
570     1010, 3,
571     1011, 3,
572     1012, 3,
573     1013, 3,
574     1014, 3,
575     1015, 3,
576     1015, 3,
577     1016, 3,
578     1017, 3,
579     1018, 2,
580     1019, 2,
581     1020, 2,
582     1021, 3,
583     1022, 3,
584     1023, 3,
585     1024, 16,
586     1025, 3,
587     1026, 64,
588     1027, 1,
589     1028, 65,
590     1029, 1,
591     1040, 1,
592     /* 1041-1065 */
593     1066, 66,
594     1066, 67,
595     0
596 };
597
598 int yaz_diag_bib1_to_srw (int code)
599 {
600     const int *p = srw_bib1_map;
601     while (*p)
602     {
603         if (code == p[0])
604             return p[1];
605         p += 2;
606     }
607     return 1;
608 }
609
610 int yaz_diag_srw_to_bib1(int code)
611 {
612     const int *p = srw_bib1_map;
613     while (*p)
614     {
615         if (code == p[1])
616             return p[0];
617         p += 2;
618     }
619     return 1;
620 }
621