Home | History | Annotate | Download | only in bus
      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 }
    322