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