renamed record filter module sources
[idzebra-moved-to-github.git] / index / mod_grs_xml.c
1 /* This file is part of the Zebra server.
2    Copyright (C) 1995-2008 Index Data
3
4 Zebra is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
18 */
19
20 #if HAVE_EXPAT_H
21
22 #include <assert.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #if HAVE_ICONV_H
26 #include <errno.h>
27 #include <iconv.h>
28 #endif
29
30 #include <yaz/log.h>
31
32 #include <idzebra/recgrs.h>
33
34 #include <yaz/log.h>
35 #include <yaz/xmalloc.h>
36
37 #include <expat.h>
38
39 #define XML_CHUNK 1024
40
41 struct user_info {
42     data1_node *d1_stack[256];
43     int level;
44     data1_handle dh;
45     NMEM nmem;
46     int loglevel;
47 };
48
49 static void cb_start (void *user, const char *el, const char **attr)
50 {
51     struct user_info *ui = (struct user_info*) user;
52     if (ui->level == 1)
53         data1_set_root (ui->dh, ui->d1_stack[0], ui->nmem, el);
54     ui->d1_stack[ui->level] = data1_mk_tag (ui->dh, ui->nmem, el, attr,
55                                                 ui->d1_stack[ui->level-1]);
56     ui->level++;
57     yaz_log (ui->loglevel, "cb_start %s", el);
58 }
59
60 static void cb_end (void *user, const char *el)
61 {
62     struct user_info *ui = (struct user_info*) user;
63
64     ui->level--;
65     yaz_log (ui->loglevel, "cb_end %s", el);
66 }
67
68 static void cb_chardata (void *user, const char *s, int len)
69 {
70     struct user_info *ui = (struct user_info*) user;
71 #if 0
72     yaz_log (ui->loglevel, "cb_chardata %.*s", len, s);
73 #endif
74     ui->d1_stack[ui->level] = data1_mk_text_n (ui->dh, ui->nmem, s, len,
75                                                    ui->d1_stack[ui->level -1]);
76 }
77
78 static void cb_decl (void *user, const char *version, const char *encoding,
79                      int standalone)
80 {
81     struct user_info *ui = (struct user_info*) user;
82     const char *attr_list[7];
83
84     attr_list[0] = "version";
85     attr_list[1] = version;
86
87     attr_list[2] = "encoding";
88     attr_list[3] = "UTF-8"; /* internally it's always UTF-8 */
89
90     attr_list[4] = "standalone";
91     attr_list[5] = standalone  ? "yes" : "no";
92
93     attr_list[6] = 0;
94     
95     data1_mk_preprocess (ui->dh, ui->nmem, "xml", attr_list,
96                              ui->d1_stack[ui->level-1]);
97 #if 0
98     yaz_log (YLOG_LOG, "decl version=%s encoding=%s",
99              version ? version : "null",
100              encoding ? encoding : "null");
101 #endif
102 }
103     
104 static void cb_processing (void *user, const char *target,
105                            const char *data)
106 {
107     struct user_info *ui = (struct user_info*) user;
108     data1_node *res =
109         data1_mk_preprocess (ui->dh, ui->nmem, target, 0,
110                              ui->d1_stack[ui->level-1]);
111     data1_mk_text_nf (ui->dh, ui->nmem, data, strlen(data), res);
112     
113     yaz_log (ui->loglevel, "decl processing target=%s data=%s",
114              target ? target : "null",
115              data ? data : "null");
116 }
117
118 static void cb_comment (void *user, const char *data)
119 {
120     struct user_info *ui = (struct user_info*) user;
121     yaz_log (ui->loglevel, "decl comment data=%s", data ? data : "null");
122     data1_mk_comment (ui->dh, ui->nmem, data, ui->d1_stack[ui->level-1]);
123 }
124
125 static void cb_doctype_start (void *userData, const char *doctypeName,
126                               const char *sysid, const char *pubid,
127                               int has_internal_subset)
128 {
129     struct user_info *ui = (struct user_info*) userData;
130     yaz_log (ui->loglevel, "doctype start doctype=%s sysid=%s pubid=%s",
131              doctypeName, sysid, pubid);
132 }
133
134 static void cb_doctype_end (void *userData)
135 {
136     struct user_info *ui = (struct user_info*) userData;
137     yaz_log (ui->loglevel, "doctype end");
138 }
139
140
141 static void cb_entity_decl (void *userData, const char *entityName,
142                             int is_parameter_entity,
143                             const char *value, int value_length,
144                             const char *base, const char *systemId,
145                             const char *publicId, const char *notationName)
146 {
147     struct user_info *ui = (struct user_info*) userData;
148     yaz_log (ui->loglevel,
149              "entity decl %s is_para_entry=%d value=%.*s base=%s systemId=%s"
150              " publicId=%s notationName=%s",
151              entityName, is_parameter_entity, value_length, value,
152              base, systemId, publicId, notationName);
153     
154 }
155
156 static int cb_external_entity(XML_Parser pparser,
157                               const char *context,
158                               const char *base,
159                               const char *systemId,
160                               const char *publicId)
161 {
162     struct user_info *ui = (struct user_info*) XML_GetUserData(pparser);
163     FILE *inf;
164     int done = 0;
165     XML_Parser parser;
166
167     yaz_log (ui->loglevel,
168              "external entity context=%s base=%s systemid=%s publicid=%s",
169              context, base, systemId, publicId);
170     if (!systemId)
171         return 1;
172
173     if (!(inf = fopen (systemId, "rb")))
174     {
175         yaz_log (YLOG_WARN|YLOG_ERRNO, "fopen %s", systemId);
176         return 0;
177     }
178
179     parser = XML_ExternalEntityParserCreate (pparser, "", 0);
180     while (!done)
181     {
182         int r;
183         void *buf = XML_GetBuffer (parser, XML_CHUNK);
184         if (!buf)
185         {
186             yaz_log (YLOG_WARN, "XML_GetBuffer fail");
187             break;
188         }
189         r = fread (buf, 1, XML_CHUNK, inf);
190         if (r == 0)
191         {
192             if (ferror(inf))
193             {
194                 yaz_log (YLOG_WARN|YLOG_ERRNO, "fread %s", systemId);
195                 break;
196             }
197             done = 1;
198         }
199         if (!XML_ParseBuffer (parser, r, done))
200         {
201             done = 1;
202             yaz_log (YLOG_WARN, "%s:%d:%d:XML error: %s",
203                      systemId,
204                      XML_GetCurrentLineNumber(parser),
205                      XML_GetCurrentColumnNumber(parser),
206                      XML_ErrorString(XML_GetErrorCode(parser)));
207         }
208     }
209     fclose (inf);
210     XML_ParserFree (parser);
211     return done;
212 }
213
214
215 #if HAVE_ICONV_H
216 static int cb_encoding_convert (void *data, const char *s)
217 {
218     iconv_t t = (iconv_t) data;
219     size_t ret;
220     size_t outleft = 2;
221     char outbuf_[2], *outbuf = outbuf_;
222     size_t inleft = 4;
223     char *inbuf = (char *) s;
224     unsigned short code;
225
226 #if 1
227     yaz_log(YLOG_LOG, "------------------------- cb_encoding_convert --- ");
228 #endif
229     ret = iconv (t, &inbuf, &inleft, &outbuf, &outleft);
230     if (ret == (size_t) (-1) && errno != E2BIG)
231     {
232         iconv (t, 0, 0, 0, 0);
233         return -1;
234     }
235     if (outleft != 0)
236         return -1;
237     memcpy (&code, outbuf_, sizeof(short));
238     return code;
239 }
240
241 static void cb_encoding_release (void *data)
242 {
243     iconv_t t = (iconv_t) data;
244     iconv_close (t);
245 }
246
247 static int cb_encoding_handler (void *userData, const char *name,
248                                 XML_Encoding *info)
249 {
250     int i = 0;
251     int no_ok = 0;
252     struct user_info *ui = (struct user_info*) userData;
253
254     iconv_t t = iconv_open ("UNICODE", name);
255     if (t == (iconv_t) (-1))
256         return 0;
257    
258     info->data = 0;  /* signal that multibyte is not in use */
259     yaz_log (ui->loglevel, "Encoding handler of %s", name);
260     for (i = 0; i<256; i++)
261     {
262         size_t ret;
263         char outbuf_[5];
264         char inbuf_[5];
265         char *inbuf = inbuf_;
266         char *outbuf = outbuf_;
267         size_t inleft = 1;
268         size_t outleft = 2;
269         inbuf_[0] = i;
270
271         iconv (t, 0, 0, 0, 0);  /* reset iconv */
272
273         ret = iconv(t, &inbuf, &inleft, &outbuf, &outleft);
274         if (ret == (size_t) (-1))
275         {
276             if (errno == EILSEQ)
277             {
278                 yaz_log (ui->loglevel, "Encoding %d: invalid sequence", i);
279                 info->map[i] = -1;  /* invalid sequence */
280             }
281             if (errno == EINVAL)
282             {                       /* multi byte input */
283                 int len = 2;
284                 int j = 0;
285                 info->map[i] = -1;
286                 
287                 while (len <= 4)
288                 {
289                     char sbuf[80];
290                     int k;
291                     inbuf = inbuf_;
292                     inleft = len;
293                     outbuf = outbuf_;
294                     outleft = 2;
295
296                     inbuf_[len-1] = j;
297                     iconv (t, 0,0,0,0);
298
299                     assert (i >= 0 && i<255);
300
301                     *sbuf = 0;
302                     for (k = 0; k<len; k++)
303                     {
304                         sprintf (sbuf+strlen(sbuf), "%d ", inbuf_[k]&255);
305                     }
306                     ret = iconv (t, &inbuf, &inleft, &outbuf, &outleft);
307                     if (ret == (size_t) (-1))
308                     {
309                         if (errno == EILSEQ || errno == E2BIG)
310                         {
311                             j++;
312                             if (j > 255)
313                                 break;
314                         }
315                         else if (errno == EINVAL)
316                         {
317                             len++;
318                             j = 7;
319                         }
320                     }
321                     else if (outleft == 0)
322                     {
323                         info->map[i] = -len;
324                         info->data = t;  /* signal that multibyte is in use */
325                         break;
326                     }
327                     else
328                     {
329                         break;
330                     }
331                 }
332                 if (info->map[i] < -1)
333                     yaz_log (ui->loglevel, "Encoding %d: multibyte input %d",
334                              i, -info->map[i]);
335                 else
336                     yaz_log (ui->loglevel, "Encoding %d: multibyte input failed",
337                              i);
338             }
339             if (errno == E2BIG)
340             {
341                 info->map[i] = -1;  /* no room for output */
342                 if (i != 0)
343                     yaz_log (YLOG_WARN, "Encoding %d: no room for output",
344                              i);
345             }
346         }
347         else if (outleft == 0)
348         {
349             unsigned short code;
350             memcpy (&code, outbuf_, sizeof(short));
351             info->map[i] = code;
352             no_ok++;
353         }
354         else
355         {   /* should never happen */
356             info->map[i] = -1;
357             yaz_log (YLOG_DEBUG, "Encoding %d: bad state", i);
358         }
359     }
360     if (info->data)
361     {   /* at least one multi byte */
362         info->convert = cb_encoding_convert;
363         info->release = cb_encoding_release;
364     }
365     else
366     {
367         /* no multi byte - we no longer need iconv handler */
368         iconv_close(t);
369         info->convert = 0;
370         info->release = 0;
371     }
372     if (!no_ok)
373         return 0;
374     return 1;
375 }
376 /* HAVE_ICONV_H */
377 #endif
378
379 static void cb_ns_start(void *userData, const char *prefix, const char *uri)
380 {
381     struct user_info *ui = (struct user_info*) userData;
382     if (prefix && uri)
383         yaz_log(ui->loglevel, "cb_ns_start %s %s", prefix, uri);
384 }
385
386 static void cb_ns_end(void *userData, const char *prefix)
387 {
388     struct user_info *ui = (struct user_info*) userData;
389     if (prefix)
390         yaz_log(ui->loglevel, "cb_ns_end %s", prefix);
391 }
392
393 data1_node *zebra_read_xml(data1_handle dh,
394                            struct ZebraRecStream *stream,
395                            NMEM m)
396 {
397     XML_Parser parser;
398     struct user_info uinfo;
399     int done = 0;
400     data1_node *first_node;
401     int no_read = 0;
402
403     uinfo.loglevel = YLOG_DEBUG;
404     uinfo.level = 1;
405     uinfo.dh = dh;
406     uinfo.nmem = m;
407     uinfo.d1_stack[0] = data1_mk_node2 (dh, m, DATA1N_root, 0);
408     uinfo.d1_stack[1] = 0; /* indicate no children (see end of routine) */
409     
410     parser = XML_ParserCreate (0 /* encoding */);
411     
412     XML_SetElementHandler (parser, cb_start, cb_end);
413     XML_SetCharacterDataHandler (parser, cb_chardata);
414     XML_SetXmlDeclHandler (parser, cb_decl);
415     XML_SetProcessingInstructionHandler (parser, cb_processing);
416     XML_SetUserData (parser, &uinfo);
417     XML_SetCommentHandler (parser, cb_comment);
418     XML_SetDoctypeDeclHandler (parser, cb_doctype_start, cb_doctype_end);
419     XML_SetEntityDeclHandler (parser, cb_entity_decl);
420     XML_SetExternalEntityRefHandler (parser, cb_external_entity);
421     XML_SetNamespaceDeclHandler(parser, cb_ns_start, cb_ns_end);
422 #if HAVE_ICONV_H
423     XML_SetUnknownEncodingHandler (parser, cb_encoding_handler, &uinfo);
424 #endif
425     while (!done)
426     {
427         int r;
428         void *buf = XML_GetBuffer (parser, XML_CHUNK);
429         if (!buf)
430         {
431             /* error */
432             yaz_log (YLOG_WARN, "XML_GetBuffer fail");
433             break;
434         }
435         r = stream->readf(stream, buf, XML_CHUNK);
436         if (r < 0)
437         {
438             /* error */
439             yaz_log (YLOG_WARN, "XML read fail");
440             break;
441         }
442         else if (r == 0)
443             done = 1;
444         else
445             no_read += r;
446         if (no_read && !XML_ParseBuffer (parser, r, done))
447         {
448             done = 1;
449             yaz_log (YLOG_WARN, "%d:%d:XML error: %s",
450                      XML_GetCurrentLineNumber(parser),
451                      XML_GetCurrentColumnNumber(parser),
452                      XML_ErrorString(XML_GetErrorCode(parser)));
453         }
454     }
455     XML_ParserFree (parser);
456     if (no_read == 0)
457         return 0;
458     if (!uinfo.d1_stack[1] || !done)
459         return 0;
460     /* insert XML header if not present .. */
461     first_node = uinfo.d1_stack[0]->child;
462     if (first_node->which != DATA1N_preprocess ||
463         strcmp(first_node->u.preprocess.target, "xml"))
464     {
465         const char *attr_list[5];
466
467         attr_list[0] = "version";
468         attr_list[1] = "1.0";
469
470         attr_list[2] = "encoding";
471         attr_list[3] = "UTF-8"; /* encoding */
472
473         attr_list[4] = 0;
474     
475         data1_insert_preprocess (uinfo.dh, uinfo.nmem, "xml", attr_list,
476                                  uinfo.d1_stack[0]);
477     }
478     return uinfo.d1_stack[0];
479 }
480
481 struct xml_info {
482     XML_Expat_Version expat_version;
483 };
484
485 static data1_node *grs_read_xml(struct grs_read_info *p)
486 {
487     return zebra_read_xml(p->dh, p->stream, p->mem);
488 }
489
490 static void *filter_init(Res res, RecType recType)
491 {
492     struct xml_info *p = (struct xml_info *) xmalloc (sizeof(*p));
493
494     p->expat_version = XML_ExpatVersionInfo();
495
496     return p;
497 }
498
499 static void filter_destroy(void *clientData)
500 {
501     struct xml_info *p = (struct xml_info *) clientData;
502
503     xfree (p);
504 }
505
506 static int filter_extract(void *clientData, struct recExtractCtrl *ctrl)
507 {
508     return zebra_grs_extract(clientData, ctrl, grs_read_xml);
509 }
510
511 static int filter_retrieve(void *clientData, struct recRetrieveCtrl *ctrl)
512 {
513     return zebra_grs_retrieve(clientData, ctrl, grs_read_xml);
514 }
515
516 static struct recType filter_type = {
517     0,
518     "grs.xml",
519     filter_init,
520     0,
521     filter_destroy,
522     filter_extract,
523     filter_retrieve,
524 };
525
526 RecType
527 #ifdef IDZEBRA_STATIC_GRS_XML
528 idzebra_filter_grs_xml
529 #else
530 idzebra_filter
531 #endif
532
533 [] = {
534     &filter_type,
535     0,
536 };
537     
538 #endif
539
540 /*
541  * Local variables:
542  * c-basic-offset: 4
543  * indent-tabs-mode: nil
544  * End:
545  * vim: shiftwidth=4 tabstop=8 expandtab
546  */
547