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
process_content(ExpatParseContext * context)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
expat_StartElementHandler(void * userData,const XML_Char * name,const XML_Char ** atts)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
expat_EndElementHandler(void * userData,const XML_Char * name)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
expat_CharacterDataHandler(void * userData,const XML_Char * s,int len)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*
bus_config_load(const DBusString * file,dbus_bool_t is_toplevel,const BusConfigParser * parent,DBusError * error)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