2004-08-09 Havoc Pennington <hp@redhat.com>
[platform/upstream/dbus.git] / bus / config-loader-libxml.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* config-loader-libxml.c  libxml2 XML loader
3  *
4  * Copyright (C) 2003 Red Hat, Inc.
5  *
6  * Licensed under the Academic Free License version 2.1
7  * 
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  * 
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23
24 #include "config-parser.h"
25 #include <dbus/dbus-internals.h>
26 #include <libxml/xmlreader.h>
27 #include <libxml/parser.h>
28 #include <libxml/globals.h>
29 #include <libxml/xmlmemory.h>
30 #include <errno.h>
31 #include <string.h>
32
33 /* About the error handling: 
34  *  - setup a "structured" error handler that catches structural
35  *    errors and some oom errors 
36  *  - assume that a libxml function returning an error code means
37  *    out-of-memory
38  */
39 #define _DBUS_MAYBE_SET_OOM(e) (dbus_error_is_set(e) ? (void)0 : _DBUS_SET_OOM(e))
40
41
42 static dbus_bool_t
43 xml_text_start_element (BusConfigParser   *parser,
44                         xmlTextReader     *reader,
45                         DBusError         *error)
46 {
47   const char *name;
48   int n_attributes;
49   const char **attribute_names, **attribute_values;
50   dbus_bool_t ret;
51   int i, status, is_empty;
52
53   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
54
55   ret = FALSE;
56   attribute_names = NULL;
57   attribute_values = NULL;
58
59   name = xmlTextReaderConstName (reader);
60   n_attributes = xmlTextReaderAttributeCount (reader);
61   is_empty = xmlTextReaderIsEmptyElement (reader);
62
63   if (name == NULL || n_attributes < 0 || is_empty == -1)
64     {
65       _DBUS_MAYBE_SET_OOM (error);
66       goto out;
67     }
68
69   attribute_names = dbus_new0 (const char *, n_attributes + 1);
70   attribute_values = dbus_new0 (const char *, n_attributes + 1);
71   if (attribute_names == NULL || attribute_values == NULL)
72     {
73       _DBUS_SET_OOM (error);
74       goto out;
75     }
76   i = 0;
77   while ((status = xmlTextReaderMoveToNextAttribute (reader)) == 1)
78     {
79       _dbus_assert (i < n_attributes);
80       attribute_names[i] = xmlTextReaderConstName (reader);
81       attribute_values[i] = xmlTextReaderConstValue (reader);
82       if (attribute_names[i] == NULL || attribute_values[i] == NULL)
83         { 
84           _DBUS_MAYBE_SET_OOM (error);
85           goto out;
86         }
87       i++;
88     }
89   if (status == -1)
90     {
91       _DBUS_MAYBE_SET_OOM (error);
92       goto out;
93     }
94   _dbus_assert (i == n_attributes);
95
96   ret = bus_config_parser_start_element (parser, name,
97                                          attribute_names, attribute_values,
98                                          error);
99   if (ret && is_empty == 1)
100     ret = bus_config_parser_end_element (parser, name, error);
101
102  out:
103   dbus_free (attribute_names);
104   dbus_free (attribute_values);
105
106   return ret;
107 }
108
109 static void xml_shut_up (void *ctx, const char *msg, ...)
110 {
111     return;
112 }
113
114 static void
115 xml_text_reader_error (void *arg, xmlErrorPtr xml_error)
116 {
117   DBusError *error = arg;
118
119 #if 0
120   _dbus_verbose ("XML_ERROR level=%d, domain=%d, code=%d, msg=%s\n",
121                  xml_error->level, xml_error->domain,
122                  xml_error->code, xml_error->message);
123 #endif
124
125   if (!dbus_error_is_set (error))
126     {
127       if (xml_error->code == XML_ERR_NO_MEMORY)
128         _DBUS_SET_OOM (error);
129       else if (xml_error->level == XML_ERR_ERROR ||
130                xml_error->level == XML_ERR_FATAL)
131         dbus_set_error (error, DBUS_ERROR_FAILED,
132                         "Error loading config file: '%s'",
133                         xml_error->message);
134     }
135 }
136
137
138 BusConfigParser*
139 bus_config_load (const DBusString      *file,
140                  dbus_bool_t            is_toplevel,
141                  const BusConfigParser *parent,
142                  DBusError             *error)
143
144 {
145   xmlTextReader *reader;
146   BusConfigParser *parser;
147   DBusString dirname, data;
148   DBusError tmp_error;
149   int ret;
150   
151   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
152   
153   parser = NULL;
154   reader = NULL;
155
156   if (!_dbus_string_init (&dirname))
157     {
158       _DBUS_SET_OOM (error);
159       return NULL;
160     }
161
162   if (!_dbus_string_init (&data))
163     {
164       _DBUS_SET_OOM (error);
165       _dbus_string_free (&dirname);
166       return NULL;
167     }
168
169   if (is_toplevel)
170     {
171       /* xmlMemSetup only fails if one of the functions is NULL */
172       xmlMemSetup (dbus_free,
173                    dbus_malloc,
174                    dbus_realloc,
175                    _dbus_strdup);
176       xmlInitParser ();
177       xmlSetGenericErrorFunc (NULL, xml_shut_up);
178     }
179
180   if (!_dbus_string_get_dirname (file, &dirname))
181     {
182       _DBUS_SET_OOM (error);
183       goto failed;
184     }
185   
186   parser = bus_config_parser_new (&dirname, is_toplevel, parent);
187   if (parser == NULL)
188     {
189       _DBUS_SET_OOM (error);
190       goto failed;
191     }
192   
193   if (!_dbus_file_get_contents (&data, file, error))
194     goto failed;
195
196   reader = xmlReaderForMemory (_dbus_string_get_const_data (&data), 
197                                _dbus_string_get_length (&data),
198                                NULL, NULL, 0);
199   if (reader == NULL)
200     {
201       _DBUS_SET_OOM (error);
202       goto failed;
203     }
204
205   xmlTextReaderSetParserProp (reader, XML_PARSER_SUBST_ENTITIES, 1);
206
207   dbus_error_init (&tmp_error);
208   xmlTextReaderSetStructuredErrorHandler (reader, xml_text_reader_error, &tmp_error);
209
210   while ((ret = xmlTextReaderRead (reader)) == 1)
211     {
212       int type;
213       
214       if (dbus_error_is_set (&tmp_error))
215         goto reader_out;
216
217       type = xmlTextReaderNodeType (reader);
218       if (type == -1)
219         {
220           _DBUS_MAYBE_SET_OOM (&tmp_error);
221           goto reader_out;
222         }
223
224       switch ((xmlReaderTypes) type) {
225       case XML_READER_TYPE_ELEMENT:
226         xml_text_start_element (parser, reader, &tmp_error);
227         break;
228
229       case XML_READER_TYPE_TEXT:
230       case XML_READER_TYPE_CDATA:
231         {
232           DBusString content;
233           const char *value;
234           value = xmlTextReaderConstValue (reader);
235           if (value != NULL)
236             {
237               _dbus_string_init_const (&content, value);
238               bus_config_parser_content (parser, &content, &tmp_error);
239             }
240           else
241             _DBUS_MAYBE_SET_OOM (&tmp_error);
242           break;
243         }
244
245       case XML_READER_TYPE_DOCUMENT_TYPE:
246         {
247           const char *name;
248           name = xmlTextReaderConstName (reader);
249           if (name != NULL)
250             bus_config_parser_check_doctype (parser, name, &tmp_error);
251           else
252             _DBUS_MAYBE_SET_OOM (&tmp_error);
253           break;
254         }
255
256       case XML_READER_TYPE_END_ELEMENT:
257         {
258           const char *name;
259           name = xmlTextReaderConstName (reader);
260           if (name != NULL)
261             bus_config_parser_end_element (parser, name, &tmp_error);
262           else
263             _DBUS_MAYBE_SET_OOM (&tmp_error);
264           break;
265         }
266
267       case XML_READER_TYPE_DOCUMENT:
268       case XML_READER_TYPE_DOCUMENT_FRAGMENT:
269       case XML_READER_TYPE_PROCESSING_INSTRUCTION:
270       case XML_READER_TYPE_COMMENT:
271       case XML_READER_TYPE_ENTITY:
272       case XML_READER_TYPE_NOTATION:
273       case XML_READER_TYPE_WHITESPACE:
274       case XML_READER_TYPE_SIGNIFICANT_WHITESPACE:
275       case XML_READER_TYPE_END_ENTITY:
276       case XML_READER_TYPE_XML_DECLARATION:
277         /* nothing to do, just read on */
278         break;
279
280       case XML_READER_TYPE_NONE:
281       case XML_READER_TYPE_ATTRIBUTE:
282       case XML_READER_TYPE_ENTITY_REFERENCE:
283         _dbus_assert_not_reached ("unexpected nodes in XML");
284       }
285
286       if (dbus_error_is_set (&tmp_error))
287         goto reader_out;
288     }
289
290   if (ret == -1)
291     _DBUS_MAYBE_SET_OOM (&tmp_error);
292
293  reader_out:
294   xmlFreeTextReader (reader);
295   reader = NULL;
296   if (dbus_error_is_set (&tmp_error))
297     {
298       dbus_move_error (&tmp_error, error);
299       goto failed;
300     }
301   
302   if (!bus_config_parser_finished (parser, error))
303     goto failed;
304   _dbus_string_free (&dirname);
305   _dbus_string_free (&data);
306   if (is_toplevel)
307     xmlCleanupParser();
308   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
309   return parser;
310   
311  failed:
312   _DBUS_ASSERT_ERROR_IS_SET (error);
313   _dbus_string_free (&dirname);
314   _dbus_string_free (&data);
315   if (is_toplevel)
316     xmlCleanupParser();
317   if (parser)
318     bus_config_parser_unref (parser);
319   _dbus_assert (reader == NULL); /* must go to reader_out first */
320   return NULL;
321 }