1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ 2 /* config-loader-expat.c expat 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 <expat.h> 28 29 static XML_Memory_Handling_Suite memsuite; 30 31 typedef struct 32 { 33 BusConfigParser *parser; 34 const char *filename; 35 DBusString content; 36 DBusError *error; 37 dbus_bool_t failed; 38 } ExpatParseContext; 39 40 static dbus_bool_t 41 process_content (ExpatParseContext *context) 42 { 43 if (context->failed) 44 return FALSE; 45 46 if (_dbus_string_get_length (&context->content) > 0) 47 { 48 if (!bus_config_parser_content (context->parser, 49 &context->content, 50 context->error)) 51 { 52 context->failed = TRUE; 53 return FALSE; 54 } 55 _dbus_string_set_length (&context->content, 0); 56 } 57 58 return TRUE; 59 } 60 61 static void 62 expat_StartElementHandler (void *userData, 63 const XML_Char *name, 64 const XML_Char **atts) 65 { 66 ExpatParseContext *context = userData; 67 int i; 68 char **names; 69 char **values; 70 71 /* Expat seems to suck and can't abort the parse if we 72 * throw an error. Expat 2.0 is supposed to fix this. 73 */ 74 if (context->failed) 75 return; 76 77 if (!process_content (context)) 78 return; 79 80 /* "atts" is key, value, key, value, NULL */ 81 for (i = 0; atts[i] != NULL; ++i) 82 ; /* nothing */ 83 84 _dbus_assert (i % 2 == 0); 85 names = dbus_new0 (char *, i / 2 + 1); 86 values = dbus_new0 (char *, i / 2 + 1); 87 88 if (names == NULL || values == NULL) 89 { 90 dbus_set_error (context->error, DBUS_ERROR_NO_MEMORY, NULL); 91 context->failed = TRUE; 92 dbus_free (names); 93 dbus_free (values); 94 return; 95 } 96 97 i = 0; 98 while (atts[i] != NULL) 99 { 100 _dbus_assert (i % 2 == 0); 101 names [i / 2] = (char*) atts[i]; 102 values[i / 2] = (char*) atts[i+1]; 103 104 i += 2; 105 } 106 107 if (!bus_config_parser_start_element (context->parser, 108 name, 109 (const char **) names, 110 (const char **) values, 111 context->error)) 112 { 113 dbus_free (names); 114 dbus_free (values); 115 context->failed = TRUE; 116 return; 117 } 118 119 dbus_free (names); 120 dbus_free (values); 121 } 122 123 static void 124 expat_EndElementHandler (void *userData, 125 const XML_Char *name) 126 { 127 ExpatParseContext *context = userData; 128 129 if (!process_content (context)) 130 return; 131 132 if (!bus_config_parser_end_element (context->parser, 133 name, 134 context->error)) 135 { 136 context->failed = TRUE; 137 return; 138 } 139 } 140 141 /* s is not 0 terminated. */ 142 static void 143 expat_CharacterDataHandler (void *userData, 144 const XML_Char *s, 145 int len) 146 { 147 ExpatParseContext *context = userData; 148 if (context->failed) 149 return; 150 151 if (!_dbus_string_append_len (&context->content, 152 s, len)) 153 { 154 dbus_set_error (context->error, DBUS_ERROR_NO_MEMORY, NULL); 155 context->failed = TRUE; 156 return; 157 } 158 } 159 160 161 BusConfigParser* 162 bus_config_load (const DBusString *file, 163 dbus_bool_t is_toplevel, 164 const BusConfigParser *parent, 165 DBusError *error) 166 { 167 XML_Parser expat; 168 const char *filename; 169 BusConfigParser *parser; 170 ExpatParseContext context; 171 DBusString dirname; 172 173 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 174 175 parser = NULL; 176 expat = NULL; 177 context.error = error; 178 context.failed = FALSE; 179 180 filename = _dbus_string_get_const_data (file); 181 182 if (!_dbus_string_init (&context.content)) 183 { 184 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 185 return NULL; 186 } 187 188 if (!_dbus_string_init (&dirname)) 189 { 190 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 191 _dbus_string_free (&context.content); 192 return NULL; 193 } 194 195 memsuite.malloc_fcn = dbus_malloc; 196 memsuite.realloc_fcn = dbus_realloc; 197 memsuite.free_fcn = dbus_free; 198 199 expat = XML_ParserCreate_MM ("UTF-8", &memsuite, NULL); 200 if (expat == NULL) 201 { 202 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 203 goto failed; 204 } 205 206 if (!_dbus_string_get_dirname (file, &dirname)) 207 { 208 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 209 goto failed; 210 } 211 212 parser = bus_config_parser_new (&dirname, is_toplevel, parent); 213 if (parser == NULL) 214 { 215 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 216 goto failed; 217 } 218 context.parser = parser; 219 220 XML_SetUserData (expat, &context); 221 XML_SetElementHandler (expat, 222 expat_StartElementHandler, 223 expat_EndElementHandler); 224 XML_SetCharacterDataHandler (expat, 225 expat_CharacterDataHandler); 226 227 { 228 DBusString data; 229 const char *data_str; 230 231 if (!_dbus_string_init (&data)) 232 { 233 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 234 goto failed; 235 } 236 237 if (!_dbus_file_get_contents (&data, file, error)) 238 { 239 _dbus_string_free (&data); 240 goto failed; 241 } 242 243 data_str = _dbus_string_get_const_data (&data); 244 245 if (!XML_Parse (expat, data_str, _dbus_string_get_length (&data), TRUE)) 246 { 247 if (context.error != NULL && 248 !dbus_error_is_set (context.error)) 249 { 250 enum XML_Error e; 251 252 e = XML_GetErrorCode (expat); 253 if (e == XML_ERROR_NO_MEMORY) 254 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); 255 else 256 dbus_set_error (error, DBUS_ERROR_FAILED, 257 "Error in file %s, line %d, column %d: %s\n", 258 filename, 259 XML_GetCurrentLineNumber (expat), 260 XML_GetCurrentColumnNumber (expat), 261 XML_ErrorString (e)); 262 } 263 264 _dbus_string_free (&data); 265 goto failed; 266 } 267 268 _dbus_string_free (&data); 269 270 if (context.failed) 271 goto failed; 272 } 273 274 if (!bus_config_parser_finished (parser, error)) 275 goto failed; 276 277 _dbus_string_free (&dirname); 278 _dbus_string_free (&context.content); 279 XML_ParserFree (expat); 280 281 _DBUS_ASSERT_ERROR_IS_CLEAR (error); 282 return parser; 283 284 failed: 285 _DBUS_ASSERT_ERROR_IS_SET (error); 286 287 _dbus_string_free (&dirname); 288 _dbus_string_free (&context.content); 289 if (expat) 290 XML_ParserFree (expat); 291 if (parser) 292 bus_config_parser_unref (parser); 293 return NULL; 294 } 295