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