* bus/config-loader-libxml.c: complete the implementation of libxml
[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.0
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       (xml_error->level == XML_ERR_ERROR ||
127        xml_error->level == XML_ERR_FATAL))
128     {
129       if (xml_error->code != XML_ERR_NO_MEMORY)
130         _DBUS_SET_OOM (error);
131       else
132         dbus_set_error (error, DBUS_ERROR_FAILED,
133                         "Error loading config file: '%s'",
134                         xml_error->message);
135     }
136 }
137
138
139 BusConfigParser*
140 bus_config_load (const DBusString      *file,
141                  dbus_bool_t            is_toplevel,
142                  const BusConfigParser *parent,
143                  DBusError             *error)
144
145 {
146   xmlTextReader *reader;
147   BusConfigParser *parser;
148   DBusString dirname, data;
149   const char *data_str;
150   DBusError tmp_error;
151   int ret;
152   
153   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
154   
155   parser = NULL;
156   reader = NULL;
157
158   if (is_toplevel)
159     {
160       /* xmlMemSetup only fails if one of the functions is NULL */
161       xmlMemSetup (dbus_free,
162                    dbus_malloc,
163                    dbus_realloc,
164                    _dbus_strdup);
165       xmlInitParser ();
166       xmlSetGenericErrorFunc (NULL, xml_shut_up);
167     }
168
169   if (!_dbus_string_init (&dirname))
170     {
171       _DBUS_SET_OOM (error);
172       return NULL;
173     }
174
175   if (!_dbus_string_init (&data))
176     {
177       _DBUS_SET_OOM (error);
178       _dbus_string_free (&dirname);
179       return NULL;
180     }
181
182   if (!_dbus_string_get_dirname (file, &dirname))
183     {
184       _DBUS_SET_OOM (error);
185       goto failed;
186     }
187   
188   parser = bus_config_parser_new (&dirname, is_toplevel, parent);
189   if (parser == NULL)
190     {
191       _DBUS_SET_OOM (error);
192       goto failed;
193     }
194   
195   if (!_dbus_file_get_contents (&data, file, error))
196     goto failed;
197
198   data_str = _dbus_string_get_const_data (&data);
199
200   reader = xmlReaderForMemory (data_str, _dbus_string_get_length (&data),
201                                NULL, NULL, 0);
202   if (reader == NULL)
203     {
204       _DBUS_SET_OOM (error);
205       goto failed;
206     }
207
208   xmlTextReaderSetParserProp (reader, XML_PARSER_SUBST_ENTITIES, 1);
209
210   dbus_error_init (&tmp_error);
211   xmlTextReaderSetStructuredErrorHandler (reader, xml_text_reader_error, &tmp_error);
212
213   while ((ret = xmlTextReaderRead (reader)) == 1)
214     {
215       int type;
216       
217       if (dbus_error_is_set (&tmp_error))
218         goto reader_out;
219
220       type = xmlTextReaderNodeType (reader);
221       if (type == -1)
222         {
223           _DBUS_MAYBE_SET_OOM (&tmp_error);
224           goto reader_out;
225         }
226
227       switch ((xmlReaderTypes) type) {
228       case XML_READER_TYPE_ELEMENT:
229         xml_text_start_element (parser, reader, &tmp_error);
230         break;
231
232       case XML_READER_TYPE_TEXT:
233       case XML_READER_TYPE_CDATA:
234         {
235           DBusString content;
236           const char *value;
237           value = xmlTextReaderConstValue (reader);
238           if (value != NULL)
239             {
240               _dbus_string_init_const (&content, value);
241               bus_config_parser_content (parser, &content, &tmp_error);
242             }
243           else
244             _DBUS_MAYBE_SET_OOM (&tmp_error);
245           break;
246         }
247
248       case XML_READER_TYPE_DOCUMENT_TYPE:
249         {
250           const char *name;
251           name = xmlTextReaderConstName (reader);
252           if (name != NULL)
253             bus_config_parser_check_doctype (parser, name, &tmp_error);
254           else
255             _DBUS_MAYBE_SET_OOM (&tmp_error);
256           break;
257         }
258
259       case XML_READER_TYPE_END_ELEMENT:
260         {
261           const char *name;
262           name = xmlTextReaderConstName (reader);
263           if (name != NULL)
264             bus_config_parser_end_element (parser, name, &tmp_error);
265           else
266             _DBUS_MAYBE_SET_OOM (&tmp_error);
267           break;
268         }
269
270       case XML_READER_TYPE_DOCUMENT:
271       case XML_READER_TYPE_DOCUMENT_FRAGMENT:
272       case XML_READER_TYPE_PROCESSING_INSTRUCTION:
273       case XML_READER_TYPE_COMMENT:
274       case XML_READER_TYPE_ENTITY:
275       case XML_READER_TYPE_NOTATION:
276       case XML_READER_TYPE_WHITESPACE:
277       case XML_READER_TYPE_SIGNIFICANT_WHITESPACE:
278       case XML_READER_TYPE_END_ENTITY:
279       case XML_READER_TYPE_XML_DECLARATION:
280         /* nothing to do, just read on */
281         break;
282
283       case XML_READER_TYPE_NONE:
284       case XML_READER_TYPE_ATTRIBUTE:
285       case XML_READER_TYPE_ENTITY_REFERENCE:
286         _dbus_assert_not_reached ("unexpected nodes in XML");
287       }
288
289       if (dbus_error_is_set (&tmp_error))
290         goto reader_out;
291     }
292
293   if (ret == -1)
294     _DBUS_MAYBE_SET_OOM (&tmp_error);
295
296  reader_out:
297   xmlFreeTextReader (reader);
298   if (is_toplevel)
299     xmlCleanupParser(); 
300   reader = NULL;
301   if (dbus_error_is_set (&tmp_error))
302     {
303       dbus_move_error (&tmp_error, error);
304       goto failed;
305     }
306   
307   if (!bus_config_parser_finished (parser, error))
308     goto failed;
309   _dbus_string_free (&dirname);
310   _dbus_string_free (&data);
311   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
312   return parser;
313   
314  failed:
315   _DBUS_ASSERT_ERROR_IS_SET (error);
316   _dbus_string_free (&dirname);
317   _dbus_string_free (&data);
318   if (parser)
319     bus_config_parser_unref (parser);
320   _dbus_assert (reader == NULL); /* must go to reader_out first */
321   return NULL;
322 }