| /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ |
| /* config-loader-libxml.c libxml2 XML loader |
| * |
| * Copyright (C) 2003 Red Hat, Inc. |
| * |
| * Licensed under the Academic Free License version 2.1 |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #include <config.h> |
| #include "config-parser.h" |
| #include <dbus/dbus-internals.h> |
| #include <libxml/xmlreader.h> |
| #include <libxml/parser.h> |
| #include <libxml/globals.h> |
| #include <libxml/xmlmemory.h> |
| #ifdef HAVE_ERRNO_H |
| #include <errno.h> |
| #endif |
| #include <string.h> |
| |
| /* About the error handling: |
| * - setup a "structured" error handler that catches structural |
| * errors and some oom errors |
| * - assume that a libxml function returning an error code means |
| * out-of-memory |
| */ |
| #define _DBUS_MAYBE_SET_OOM(e) (dbus_error_is_set(e) ? (void)0 : _DBUS_SET_OOM(e)) |
| |
| |
| static dbus_bool_t |
| xml_text_start_element (BusConfigParser *parser, |
| xmlTextReader *reader, |
| DBusError *error) |
| { |
| const char *name; |
| int n_attributes; |
| const char **attribute_names, **attribute_values; |
| dbus_bool_t ret; |
| int i, status, is_empty; |
| |
| _DBUS_ASSERT_ERROR_IS_CLEAR (error); |
| |
| ret = FALSE; |
| attribute_names = NULL; |
| attribute_values = NULL; |
| |
| name = xmlTextReaderConstName (reader); |
| n_attributes = xmlTextReaderAttributeCount (reader); |
| is_empty = xmlTextReaderIsEmptyElement (reader); |
| |
| if (name == NULL || n_attributes < 0 || is_empty == -1) |
| { |
| _DBUS_MAYBE_SET_OOM (error); |
| goto out; |
| } |
| |
| attribute_names = dbus_new0 (const char *, n_attributes + 1); |
| attribute_values = dbus_new0 (const char *, n_attributes + 1); |
| if (attribute_names == NULL || attribute_values == NULL) |
| { |
| _DBUS_SET_OOM (error); |
| goto out; |
| } |
| i = 0; |
| while ((status = xmlTextReaderMoveToNextAttribute (reader)) == 1) |
| { |
| _dbus_assert (i < n_attributes); |
| attribute_names[i] = xmlTextReaderConstName (reader); |
| attribute_values[i] = xmlTextReaderConstValue (reader); |
| if (attribute_names[i] == NULL || attribute_values[i] == NULL) |
| { |
| _DBUS_MAYBE_SET_OOM (error); |
| goto out; |
| } |
| i++; |
| } |
| if (status == -1) |
| { |
| _DBUS_MAYBE_SET_OOM (error); |
| goto out; |
| } |
| _dbus_assert (i == n_attributes); |
| |
| ret = bus_config_parser_start_element (parser, name, |
| attribute_names, attribute_values, |
| error); |
| if (ret && is_empty == 1) |
| ret = bus_config_parser_end_element (parser, name, error); |
| |
| out: |
| dbus_free (attribute_names); |
| dbus_free (attribute_values); |
| |
| return ret; |
| } |
| |
| static void xml_shut_up (void *ctx, const char *msg, ...) |
| { |
| return; |
| } |
| |
| static void |
| xml_text_reader_error (void *arg, xmlErrorPtr xml_error) |
| { |
| DBusError *error = arg; |
| |
| #if 0 |
| _dbus_verbose ("XML_ERROR level=%d, domain=%d, code=%d, msg=%s\n", |
| xml_error->level, xml_error->domain, |
| xml_error->code, xml_error->message); |
| #endif |
| |
| if (!dbus_error_is_set (error)) |
| { |
| if (xml_error->code == XML_ERR_NO_MEMORY) |
| _DBUS_SET_OOM (error); |
| else if (xml_error->level == XML_ERR_ERROR || |
| xml_error->level == XML_ERR_FATAL) |
| dbus_set_error (error, DBUS_ERROR_FAILED, |
| "Error loading config file: '%s'", |
| xml_error->message); |
| } |
| } |
| |
| |
| BusConfigParser* |
| bus_config_load (const DBusString *file, |
| dbus_bool_t is_toplevel, |
| const BusConfigParser *parent, |
| DBusError *error) |
| |
| { |
| xmlTextReader *reader; |
| BusConfigParser *parser; |
| DBusString dirname, data; |
| DBusError tmp_error; |
| int ret; |
| |
| _DBUS_ASSERT_ERROR_IS_CLEAR (error); |
| |
| parser = NULL; |
| reader = NULL; |
| |
| if (!_dbus_string_init (&dirname)) |
| { |
| _DBUS_SET_OOM (error); |
| return NULL; |
| } |
| |
| if (!_dbus_string_init (&data)) |
| { |
| _DBUS_SET_OOM (error); |
| _dbus_string_free (&dirname); |
| return NULL; |
| } |
| |
| if (is_toplevel) |
| { |
| /* xmlMemSetup only fails if one of the functions is NULL */ |
| xmlMemSetup (dbus_free, |
| dbus_malloc, |
| dbus_realloc, |
| _dbus_strdup); |
| xmlInitParser (); |
| xmlSetGenericErrorFunc (NULL, xml_shut_up); |
| } |
| |
| if (!_dbus_string_get_dirname (file, &dirname)) |
| { |
| _DBUS_SET_OOM (error); |
| goto failed; |
| } |
| |
| parser = bus_config_parser_new (&dirname, is_toplevel, parent); |
| if (parser == NULL) |
| { |
| _DBUS_SET_OOM (error); |
| goto failed; |
| } |
| |
| if (!_dbus_file_get_contents (&data, file, error)) |
| goto failed; |
| |
| reader = xmlReaderForMemory (_dbus_string_get_const_data (&data), |
| _dbus_string_get_length (&data), |
| NULL, NULL, 0); |
| if (reader == NULL) |
| { |
| _DBUS_SET_OOM (error); |
| goto failed; |
| } |
| |
| xmlTextReaderSetParserProp (reader, XML_PARSER_SUBST_ENTITIES, 1); |
| |
| dbus_error_init (&tmp_error); |
| xmlTextReaderSetStructuredErrorHandler (reader, xml_text_reader_error, &tmp_error); |
| |
| while ((ret = xmlTextReaderRead (reader)) == 1) |
| { |
| int type; |
| |
| if (dbus_error_is_set (&tmp_error)) |
| goto reader_out; |
| |
| type = xmlTextReaderNodeType (reader); |
| if (type == -1) |
| { |
| _DBUS_MAYBE_SET_OOM (&tmp_error); |
| goto reader_out; |
| } |
| |
| switch ((xmlReaderTypes) type) { |
| case XML_READER_TYPE_ELEMENT: |
| xml_text_start_element (parser, reader, &tmp_error); |
| break; |
| |
| case XML_READER_TYPE_TEXT: |
| case XML_READER_TYPE_CDATA: |
| { |
| DBusString content; |
| const char *value; |
| value = xmlTextReaderConstValue (reader); |
| if (value != NULL) |
| { |
| _dbus_string_init_const (&content, value); |
| bus_config_parser_content (parser, &content, &tmp_error); |
| } |
| else |
| _DBUS_MAYBE_SET_OOM (&tmp_error); |
| break; |
| } |
| |
| case XML_READER_TYPE_DOCUMENT_TYPE: |
| { |
| const char *name; |
| name = xmlTextReaderConstName (reader); |
| if (name != NULL) |
| bus_config_parser_check_doctype (parser, name, &tmp_error); |
| else |
| _DBUS_MAYBE_SET_OOM (&tmp_error); |
| break; |
| } |
| |
| case XML_READER_TYPE_END_ELEMENT: |
| { |
| const char *name; |
| name = xmlTextReaderConstName (reader); |
| if (name != NULL) |
| bus_config_parser_end_element (parser, name, &tmp_error); |
| else |
| _DBUS_MAYBE_SET_OOM (&tmp_error); |
| break; |
| } |
| |
| case XML_READER_TYPE_DOCUMENT: |
| case XML_READER_TYPE_DOCUMENT_FRAGMENT: |
| case XML_READER_TYPE_PROCESSING_INSTRUCTION: |
| case XML_READER_TYPE_COMMENT: |
| case XML_READER_TYPE_ENTITY: |
| case XML_READER_TYPE_NOTATION: |
| case XML_READER_TYPE_WHITESPACE: |
| case XML_READER_TYPE_SIGNIFICANT_WHITESPACE: |
| case XML_READER_TYPE_END_ENTITY: |
| case XML_READER_TYPE_XML_DECLARATION: |
| /* nothing to do, just read on */ |
| break; |
| |
| case XML_READER_TYPE_NONE: |
| case XML_READER_TYPE_ATTRIBUTE: |
| case XML_READER_TYPE_ENTITY_REFERENCE: |
| _dbus_assert_not_reached ("unexpected nodes in XML"); |
| } |
| |
| if (dbus_error_is_set (&tmp_error)) |
| goto reader_out; |
| } |
| |
| if (ret == -1) |
| _DBUS_MAYBE_SET_OOM (&tmp_error); |
| |
| reader_out: |
| xmlFreeTextReader (reader); |
| reader = NULL; |
| if (dbus_error_is_set (&tmp_error)) |
| { |
| dbus_move_error (&tmp_error, error); |
| goto failed; |
| } |
| |
| if (!bus_config_parser_finished (parser, error)) |
| goto failed; |
| _dbus_string_free (&dirname); |
| _dbus_string_free (&data); |
| if (is_toplevel) |
| xmlCleanupParser(); |
| _DBUS_ASSERT_ERROR_IS_CLEAR (error); |
| return parser; |
| |
| failed: |
| _DBUS_ASSERT_ERROR_IS_SET (error); |
| _dbus_string_free (&dirname); |
| _dbus_string_free (&data); |
| if (is_toplevel) |
| xmlCleanupParser(); |
| if (parser) |
| bus_config_parser_unref (parser); |
| _dbus_assert (reader == NULL); /* must go to reader_out first */ |
| return NULL; |
| } |