1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 * 22 */ 23 24 #include <config.h> 25 #include "config-parser.h" 26 #include <dbus/dbus-internals.h> 27 #include <libxml/xmlreader.h> 28 #include <libxml/parser.h> 29 #include <libxml/globals.h> 30 #include <libxml/xmlmemory.h> 31 #ifdef HAVE_ERRNO_H 32 #include <errno.h> 33 #endif 34 #include <string.h> 35 36 /* About the error handling: 37 * - setup a "structured" error handler that catches structural 38 * errors and some oom errors 39 * - assume that a libxml function returning an error code means 40 * out-of-memory 41 */ 42 #define _DBUS_MAYBE_SET_OOM(e) (dbus_error_is_set(e) ? (void)0 : _DBUS_SET_OOM(e)) 43 44 45 static dbus_bool_t 46 xml_text_start_element (BusConfigParser *parser, 47 xmlTextReader *reader, 48 DBusError *error) 49 { 50 const char *name; 51 int n_attributes; 52 const char **attribute_names, **attribute_values; 53 dbus_bool_t ret; 54 int i, status, is_empty; 55 56 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 57 58 ret = FALSE; 59 attribute_names = NULL; 60 attribute_values = NULL; 61 62 name = xmlTextReaderConstName (reader); 63 n_attributes = xmlTextReaderAttributeCount (reader); 64 is_empty = xmlTextReaderIsEmptyElement (reader); 65 66 if (name == NULL || n_attributes < 0 || is_empty == -1) 67 { 68 _DBUS_MAYBE_SET_OOM (error); 69 goto out; 70 } 71 72 attribute_names = dbus_new0 (const char *, n_attributes + 1); 73 attribute_values = dbus_new0 (const char *, n_attributes + 1); 74 if (attribute_names == NULL || attribute_values == NULL) 75 { 76 _DBUS_SET_OOM (error); 77 goto out; 78 } 79 i = 0; 80 while ((status = xmlTextReaderMoveToNextAttribute (reader)) == 1) 81 { 82 _dbus_assert (i < n_attributes); 83 attribute_names[i] = xmlTextReaderConstName (reader); 84 attribute_values[i] = xmlTextReaderConstValue (reader); 85 if (attribute_names[i] == NULL || attribute_values[i] == NULL) 86 { 87 _DBUS_MAYBE_SET_OOM (error); 88 goto out; 89 } 90 i++; 91 } 92 if (status == -1) 93 { 94 _DBUS_MAYBE_SET_OOM (error); 95 goto out; 96 } 97 _dbus_assert (i == n_attributes); 98 99 ret = bus_config_parser_start_element (parser, name, 100 attribute_names, attribute_values, 101 error); 102 if (ret && is_empty == 1) 103 ret = bus_config_parser_end_element (parser, name, error); 104 105 out: 106 dbus_free (attribute_names); 107 dbus_free (attribute_values); 108 109 return ret; 110 } 111 112 static void xml_shut_up (void *ctx, const char *msg, ...) 113 { 114 return; 115 } 116 117 static void 118 xml_text_reader_error (void *arg, xmlErrorPtr xml_error) 119 { 120 DBusError *error = arg; 121 122 #if 0 123 _dbus_verbose ("XML_ERROR level=%d, domain=%d, code=%d, msg=%s\n", 124 xml_error->level, xml_error->domain, 125 xml_error->code, xml_error->message); 126 #endif 127 128 if (!dbus_error_is_set (error)) 129 { 130 if (xml_error->code == XML_ERR_NO_MEMORY) 131 _DBUS_SET_OOM (error); 132 else if (xml_error->level == XML_ERR_ERROR || 133 xml_error->level == XML_ERR_FATAL) 134 dbus_set_error (error, DBUS_ERROR_FAILED, 135 "Error loading config file: '%s'", 136 xml_error->message); 137 } 138 } 139 140 141 BusConfigParser* 142 bus_config_load (const DBusString *file, 143 dbus_bool_t is_toplevel, 144 const BusConfigParser *parent, 145 DBusError *error) 146 147 { 148 xmlTextReader *reader; 149 BusConfigParser *parser; 150 DBusString dirname, data; 151 DBusError tmp_error; 152 int ret; 153 154 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 155 156 parser = NULL; 157 reader = NULL; 158 159 if (!_dbus_string_init (&dirname)) 160 { 161 _DBUS_SET_OOM (error); 162 return NULL; 163 } 164 165 if (!_dbus_string_init (&data)) 166 { 167 _DBUS_SET_OOM (error); 168 _dbus_string_free (&dirname); 169 return NULL; 170 } 171 172 if (is_toplevel) 173 { 174 /* xmlMemSetup only fails if one of the functions is NULL */ 175 xmlMemSetup (dbus_free, 176 dbus_malloc, 177 dbus_realloc, 178 _dbus_strdup); 179 xmlInitParser (); 180 xmlSetGenericErrorFunc (NULL, xml_shut_up); 181 } 182 183 if (!_dbus_string_get_dirname (file, &dirname)) 184 { 185 _DBUS_SET_OOM (error); 186 goto failed; 187 } 188 189 parser = bus_config_parser_new (&dirname, is_toplevel, parent); 190 if (parser == NULL) 191 { 192 _DBUS_SET_OOM (error); 193 goto failed; 194 } 195 196 if (!_dbus_file_get_contents (&data, file, error)) 197 goto failed; 198 199 reader = xmlReaderForMemory (_dbus_string_get_const_data (&data), 200 _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 reader = NULL; 299 if (dbus_error_is_set (&tmp_error)) 300 { 301 dbus_move_error (&tmp_error, error); 302 goto failed; 303 } 304 305 if (!bus_config_parser_finished (parser, error)) 306 goto failed; 307 _dbus_string_free (&dirname); 308 _dbus_string_free (&data); 309 if (is_toplevel) 310 xmlCleanupParser(); 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 (is_toplevel) 319 xmlCleanupParser(); 320 if (parser) 321 bus_config_parser_unref (parser); 322 _dbus_assert (reader == NULL); /* must go to reader_out first */ 323 return NULL; 324 } 325