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