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