1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * Copyright (c) 2007-2013 Zmanda, Inc.  All Rights Reserved.
5  * All Rights Reserved.
6  *
7  * Permission to use, copy, modify, distribute, and sell this software and its
8  * documentation for any purpose is hereby granted without fee, provided that
9  * the above copyright notice appear in all copies and that both that
10  * copyright notice and this permission notice appear in supporting
11  * documentation, and that the name of U.M. not be used in advertising or
12  * publicity pertaining to distribution of the software without specific,
13  * written prior permission.  U.M. makes no representations about the
14  * suitability of this software for any purpose.  It is provided "as is"
15  * without express or implied warranty.
16  *
17  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
19  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23  *
24  * Authors: the Amanda Development Team.  Its members are listed in a
25  * file named AUTHORS, in the root directory of this distribution.
26  */
27 
28 /*
29  * $Id: amxml.c 5151 2007-02-06 15:41:53Z martineau $
30  *
31  * xml parsing of amanda protocol packet
32  */
33 
34 #include "amanda.h"
35 #include "util.h"
36 #include "amxml.h"
37 #include "match.h"
38 #include "glib.h"
39 #include "conffile.h"
40 #include "base64.h"
41 
42 typedef struct amgxml_s {
43     dle_t   *dles;
44     dle_t   *dle;
45     GSList  *element_names;
46     int      has_calcsize;
47     int      has_estimate;
48     int      has_record;
49     int      has_spindle;
50     int      has_compress;
51     int      has_encrypt;
52     int      has_kencrypt;
53     int      has_datapath;
54     int      has_exclude;
55     int      has_include;
56     int      has_index;
57     int      has_backup_program;
58     int      has_plugin;
59     int      has_optional;
60     char    *property_name;
61     property_t *property_data;
62     proplist_t  property;
63     script_t   *script;
64     am_level_t    *alevel;
65     char       *encoding;
66     char       *raw;
67 } amgxml_t;
68 
69 
70 dle_t *
alloc_dle(void)71 alloc_dle(void)
72 {
73     dle_t *dle;
74 
75     dle = malloc(sizeof(dle_t));
76     init_dle(dle);
77     return dle;
78 }
79 
80 void
free_dle(dle_t * dle)81 free_dle(
82     dle_t *dle)
83 {
84     scriptlist_t scriptlist;
85 
86     amfree(dle->disk);
87     amfree(dle->device);
88     amfree(dle->program);
89     g_slist_free(dle->estimatelist);
90     slist_free_full(dle->levellist, g_free);
91     amfree(dle->dumpdate);
92     amfree(dle->compprog);
93     amfree(dle->srv_encrypt);
94     amfree(dle->clnt_encrypt);
95     amfree(dle->srv_decrypt_opt);
96     amfree(dle->clnt_decrypt_opt);
97     amfree(dle->auth);
98     amfree(dle->application_client_name);
99     free_sl(dle->exclude_file);
100     free_sl(dle->exclude_list);
101     free_sl(dle->include_file);
102     free_sl(dle->include_list);
103     if (dle->property)
104 	g_hash_table_destroy(dle->property);
105     if (dle->application_property)
106 	g_hash_table_destroy(dle->application_property);
107     for(scriptlist = dle->scriptlist; scriptlist != NULL;
108 				      scriptlist = scriptlist->next) {
109 	free_script_data((script_t *)scriptlist->data);
110     }
111     slist_free_full(dle->scriptlist, g_free);
112     slist_free_full(dle->directtcp_list, g_free);
113     amfree(dle);
114 }
115 
116 void
free_script_data(script_t * script)117 free_script_data(
118     script_t *script)
119 {
120     amfree(script->plugin);
121     amfree(script->client_name);
122     if (script->property)
123 	g_hash_table_destroy(script->property);
124 }
125 
126 void
init_dle(dle_t * dle)127 init_dle(
128     dle_t *dle)
129 {
130     dle->disk = NULL;
131     dle->device = NULL;
132     dle->program_is_application_api = 0;
133     dle->program = NULL;
134     dle->estimatelist = NULL;
135     dle->record = 1;
136     dle->spindle = 0;
137     dle->compress = COMP_NONE;
138     dle->encrypt = ENCRYPT_NONE;
139     dle->kencrypt = 0;
140     dle->levellist = NULL;
141     dle->dumpdate = NULL;
142     dle->compprog = NULL;
143     dle->srv_encrypt = NULL;
144     dle->clnt_encrypt = NULL;
145     dle->srv_decrypt_opt = NULL;
146     dle->clnt_decrypt_opt = NULL;
147     dle->create_index = 0;
148     dle->auth = NULL;
149     dle->exclude_file = NULL;
150     dle->exclude_list = NULL;
151     dle->include_file = NULL;
152     dle->include_list = NULL;
153     dle->exclude_optional = 0;
154     dle->include_optional = 0;
155     dle->property = NULL;
156     dle->application_property = NULL;
157     dle->scriptlist = NULL;
158     dle->data_path = DATA_PATH_AMANDA;
159     dle->directtcp_list = NULL;
160     dle->application_client_name = NULL;
161     dle->next = NULL;
162 }
163 
164 
165 /* Called for open tags <foo bar="baz"> */
166 static void amstart_element(GMarkupParseContext *context,
167 			    const gchar         *element_name,
168 			    const gchar        **attribute_names,
169 			    const gchar        **attribute_values,
170 			    gpointer             user_data,
171 			    GError             **gerror);
172 
173 /* Called for close tags </foo> */
174 static void amend_element(GMarkupParseContext *context,
175 	                  const gchar         *element_name,
176 			  gpointer             user_data,
177 			  GError             **gerror);
178 
179 /* Called for character data */
180 /* text is not nul-terminated */
181 static void amtext(GMarkupParseContext *context,
182 		   const gchar         *text,
183 		   gsize                text_len,
184 		   gpointer             user_data,
185 		   GError             **gerror);
186 
187 /* Called for open tags <foo bar="baz"> */
188 static void
amstart_element(G_GNUC_UNUSED GMarkupParseContext * context,const gchar * element_name,G_GNUC_UNUSED const gchar ** attribute_names,G_GNUC_UNUSED const gchar ** attribute_values,gpointer user_data,GError ** gerror)189 amstart_element(
190     G_GNUC_UNUSED GMarkupParseContext *context,
191 		  const gchar         *element_name,
192     G_GNUC_UNUSED const gchar        **attribute_names,
193     G_GNUC_UNUSED const gchar        **attribute_values,
194 		  gpointer             user_data,
195 		  GError             **gerror)
196 {
197     amgxml_t *data_user = user_data;
198     dle_t    *adle;
199     GSList   *last_element = data_user->element_names;
200     char     *last_element_name = NULL;
201     dle_t    *dle = data_user->dle;
202     const gchar   **at_names, **at_values;
203 
204     if (last_element)
205 	last_element_name = last_element->data;
206 
207     data_user->raw = NULL;
208     data_user->encoding = NULL;
209 
210     if (attribute_names) {
211 	for(at_names = attribute_names, at_values = attribute_values;
212 	    *at_names != NULL && at_values != NULL;
213 	    at_names++, at_values++) {
214 	    if (strcmp(*at_names, "encoding") == 0) {
215 		data_user->encoding = stralloc(*at_values);
216 	    } else if (strcmp(*at_names, "raw") == 0) {
217 		data_user->raw = base64_decode_alloc_string((char *)*at_values);
218 	    } else {
219 		g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
220 			    "XML: Invalid attribute '%s' for %s element",
221 			    *at_names, element_name);
222 		return;
223 	    }
224 	}
225     }
226 
227     if (strcmp(element_name, "dle") == 0) {
228 	if (last_element != NULL) {
229 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
230 			"XML: Invalid dle element");
231 	    return;
232 	}
233 	for(adle = data_user->dles; adle != NULL && adle->next != NULL;
234 	    adle = adle->next);
235 	data_user->dle = alloc_dle();
236 	if (adle == NULL) {
237 	    data_user->dles = data_user->dle;
238 	} else {
239 	    adle->next = data_user->dle;
240 	}
241 	data_user->has_calcsize = 0;
242 	data_user->has_estimate = 0;
243 	data_user->has_record = 0;
244 	data_user->has_spindle = 0;
245 	data_user->has_compress = 0;
246 	data_user->has_encrypt = 0;
247 	data_user->has_kencrypt = 0;
248 	data_user->has_datapath = 0;
249 	data_user->has_exclude = 0;
250 	data_user->has_include = 0;
251 	data_user->has_index = 0;
252 	data_user->has_backup_program = 0;
253 	data_user->has_plugin = 0;
254 	data_user->has_optional = 0;
255 	data_user->property_name = NULL;
256 	data_user->property_data = NULL;
257 	data_user->property =
258 	            g_hash_table_new_full(g_str_hash, g_str_equal, &g_free, &free_property_t);
259 	data_user->script = NULL;
260 	data_user->alevel = NULL;
261 	data_user->dle->property = data_user->property;
262 	data_user->encoding = NULL;
263 	data_user->raw = NULL;
264     } else if(strcmp(element_name, "disk"          ) == 0 ||
265 	      strcmp(element_name, "diskdevice"    ) == 0 ||
266 	      strcmp(element_name, "calcsize"      ) == 0 ||
267 	      strcmp(element_name, "estimate"      ) == 0 ||
268 	      strcmp(element_name, "program"       ) == 0 ||
269 	      strcmp(element_name, "auth"          ) == 0 ||
270 	      strcmp(element_name, "index"         ) == 0 ||
271 	      strcmp(element_name, "dumpdate"      ) == 0 ||
272 	      strcmp(element_name, "level"         ) == 0 ||
273 	      strcmp(element_name, "record"        ) == 0 ||
274 	      strcmp(element_name, "spindle"       ) == 0 ||
275 	      strcmp(element_name, "compress"      ) == 0 ||
276 	      strcmp(element_name, "encrypt"       ) == 0 ||
277 	      strcmp(element_name, "kencrypt"      ) == 0 ||
278 	      strcmp(element_name, "datapath"      ) == 0 ||
279 	      strcmp(element_name, "exclude"       ) == 0 ||
280 	      strcmp(element_name, "include"       ) == 0) {
281 	if (strcmp(last_element_name, "dle") != 0) {
282 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
283 			"XML: Invalid %s element", element_name);
284 	    return;
285 	}
286 	if ((strcmp(element_name, "disk"          ) == 0 && dle->disk) ||
287 	    (strcmp(element_name, "diskdevice"    ) == 0 && dle->device) ||
288 	    (strcmp(element_name, "calcsize"      ) == 0 && data_user->has_calcsize) ||
289 	    (strcmp(element_name, "estimate"      ) == 0 && data_user->has_estimate) ||
290 	    (strcmp(element_name, "record"        ) == 0 && data_user->has_record) ||
291 	    (strcmp(element_name, "spindle"       ) == 0 && data_user->has_spindle) ||
292 	    (strcmp(element_name, "program"       ) == 0 && dle->program) ||
293 	    (strcmp(element_name, "auth"          ) == 0 && dle->auth) ||
294 	    (strcmp(element_name, "index"         ) == 0 && data_user->has_index) ||
295 	    (strcmp(element_name, "dumpdate"      ) == 0 && dle->dumpdate) ||
296 	    (strcmp(element_name, "compress"      ) == 0 && data_user->has_compress) ||
297 	    (strcmp(element_name, "encrypt"       ) == 0 && data_user->has_encrypt) ||
298 	    (strcmp(element_name, "kencrypt"      ) == 0 && data_user->has_kencrypt) ||
299 	    (strcmp(element_name, "datapath"      ) == 0 && data_user->has_datapath) ||
300 	    (strcmp(element_name, "exclude"       ) == 0 && data_user->has_exclude) ||
301 	    (strcmp(element_name, "include"       ) == 0 && data_user->has_include)) {
302 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
303 			"XML: Duplicate %s element", element_name);
304 	    return;
305 	}
306 	if (strcmp(element_name, "calcsize"      ) == 0) data_user->has_calcsize       = 1;
307 	if (strcmp(element_name, "estimate"      ) == 0) data_user->has_estimate       = 1;
308 	if (strcmp(element_name, "record"        ) == 0) data_user->has_record         = 1;
309 	if (strcmp(element_name, "spindle"       ) == 0) data_user->has_spindle        = 1;
310 	if (strcmp(element_name, "index"         ) == 0) data_user->has_index          = 1;
311 	if (strcmp(element_name, "compress"      ) == 0) data_user->has_compress       = 1;
312 	if (strcmp(element_name, "encrypt"       ) == 0) data_user->has_encrypt        = 1;
313 	if (strcmp(element_name, "kencrypt"      ) == 0) data_user->has_kencrypt       = 1;
314 	if (strcmp(element_name, "datapath"      ) == 0) data_user->has_datapath       = 1;
315 	if (strcmp(element_name, "exclude"       ) == 0) data_user->has_exclude        = 1;
316 	if (strcmp(element_name, "include"       ) == 0) data_user->has_include        = 1;
317 	if (strcmp(element_name, "exclude") == 0 || strcmp(element_name, "include") == 0)
318 	   data_user->has_optional = 0;
319 	if (strcmp(element_name, "level") == 0) {
320 	    data_user->alevel = g_new0(am_level_t, 1);
321 	}
322     } else if (strcmp(element_name, "server") == 0) {
323 	if (strcmp(last_element_name, "level") != 0) {
324 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
325 			"XML: Invalid %s element", element_name);
326 	    return;
327 	}
328     } else if(strcmp(element_name, "custom-compress-program") == 0) {
329 	if (strcmp(last_element_name, "compress") != 0) {
330 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
331 			"XML: Invalid %s element", element_name);
332 	    return;
333 	}
334 	if (dle->compprog) {
335 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
336 			"XML: Duplicate %s element", element_name);
337 	    return;
338 	}
339     } else if (strcmp(element_name, "custom-encrypt-program") == 0 ||
340 	       strcmp(element_name, "decrypt-option") == 0) {
341 	if (strcmp(last_element_name, "encrypt") != 0) {
342 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
343 			"XML: Invalid %s element", element_name);
344 	    return;
345 	}
346 	if (strcmp(element_name, "custom-encrypt-program") == 0 &&
347 		   dle->clnt_encrypt) {
348 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
349 			"XML: Duplicate %s element", element_name);
350 	    return;
351 	}
352 	if (strcmp(element_name, "decrypt-option") == 0 &&
353 		   dle->clnt_decrypt_opt) {
354 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
355 			"XML: Duplicate %s element", element_name);
356 	    return;
357 	}
358     } else if(strcmp(element_name, "plugin") == 0) {
359 	if (strcmp(last_element_name, "backup-program") != 0 &&
360 	    strcmp(last_element_name, "script") != 0) {
361 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
362 			"XML: Invalid %s element", element_name);
363 	    return;
364 	}
365 	if (data_user->has_plugin) {
366 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
367 			"XML: Duplicate %s element in '%s'", element_name,
368 			last_element_name);
369 	    return;
370 	}
371     } else if(strcmp(element_name, "property") == 0) {
372 	if (!last_element ||
373 	    (strcmp(last_element_name, "backup-program") != 0 &&
374 	     strcmp(last_element_name, "script") != 0 &&
375 	     strcmp(last_element_name, "dle") != 0)) {
376 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
377 			"XML: Invalid %s element", element_name);
378 	    return;
379 	}
380 	data_user->property_data = malloc(sizeof(property_t));
381 	data_user->property_data->append = 0;
382 	data_user->property_data->priority = 0;
383 	data_user->property_data->values = NULL;
384     } else if(strcmp(element_name, "name") == 0) {
385 	if (strcmp(last_element_name, "property") != 0) {
386 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
387 			"XML: Invalid %s element", element_name);
388 	    return;
389 	}
390 	if (data_user->property_name) {
391 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
392 			"XML: Duplicate %s element in '%s'", element_name,
393 			last_element_name);
394 	    return;
395 	}
396     } else if(strcmp(element_name, "priority") == 0) {
397 	if (strcmp(last_element_name, "property") != 0) {
398 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
399 			"XML: Invalid %s element", element_name);
400 	    return;
401 	}
402     } else if(strcmp(element_name, "value") == 0) {
403 	if (strcmp(last_element_name, "property") != 0) {
404 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
405 			"XML: Invalid %s element", element_name);
406 	    return;
407 	}
408     } else if(strcmp(element_name, "file") == 0 ||
409 	      strcmp(element_name, "list") == 0 ||
410 	      strcmp(element_name, "optional") == 0) {
411 	if (strcmp(last_element_name, "exclude") != 0 &&
412 	    strcmp(last_element_name, "include") != 0) {
413 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
414 			"XML: Invalid %s element", element_name);
415 	    return;
416 	}
417 	if (strcmp(element_name, "optional") == 0 && data_user->has_optional) {
418 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
419 			"XML: Duplicate %s element", element_name);
420 	    return;
421 	}
422 	if (strcmp(element_name, "optional") == 0) data_user->has_optional = 1;
423     } else if (strcmp(element_name, "backup-program") == 0) {
424 	if (data_user->has_backup_program) {
425 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
426 			"XML: Duplicate %s element", element_name);
427 	    return;
428 	} else {
429 	    data_user->has_backup_program = 1;
430 	    data_user->property =
431 	            g_hash_table_new_full(g_str_hash, g_str_equal, &g_free, &free_property_t);
432 	    data_user->has_plugin = 0;
433 	}
434     } else if (strcmp(element_name, "script") == 0) {
435 	data_user->property =
436 	            g_hash_table_new_full(g_str_hash, g_str_equal, &g_free, &free_property_t);
437 	data_user->script = malloc(sizeof(script_t));
438 	data_user->script->plugin = NULL;
439 	data_user->script->execute_on = 0;
440 	data_user->script->execute_where = ES_CLIENT;
441 	data_user->script->property = NULL;
442 	data_user->script->client_name = NULL;
443 	data_user->script->result = NULL;
444 	data_user->has_plugin = 0;
445     } else if (strcmp(element_name, "execute_on") == 0) {
446     } else if (strcmp(element_name, "execute_where") == 0) {
447     } else if (strcmp(element_name, "directtcp") == 0) {
448 	if (strcmp(last_element_name, "datapath") != 0) {
449 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
450 			"XML: Invalid %s element", element_name);
451 	    return;
452 	}
453     } else if (strcmp(element_name, "client_name") == 0) {
454     } else {
455 	g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
456 		    "XML: Invalid %s element", element_name);
457 	return;
458     }
459     data_user->element_names = g_slist_prepend(data_user->element_names,
460 					       stralloc(element_name));
461 }
462 
463 /* Called for close tags </foo> */
464 static void
amend_element(G_GNUC_UNUSED GMarkupParseContext * context,const gchar * element_name,gpointer user_data,GError ** gerror)465 amend_element(
466     G_GNUC_UNUSED GMarkupParseContext *context,
467 		  const gchar         *element_name,
468 		  gpointer             user_data,
469 		  GError             **gerror)
470 {
471     amgxml_t *data_user = user_data;
472     GSList   *last_element = data_user->element_names;
473     char     *last_element_name = NULL;
474     dle_t    *dle = data_user->dle;
475 
476     if (!last_element) {
477 	g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
478 		    "XML: Invalid closing tag");
479 	return;
480     }
481     last_element_name = last_element->data;
482     if (strcmp(last_element_name, element_name) != 0) {
483 	g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
484 		    "XML: Invalid closing tag '%s'", element_name);
485 	return;
486     }
487 
488     if (strcmp(element_name, "property") == 0) {
489 	g_hash_table_insert(data_user->property,
490 			    data_user->property_name,
491 			    data_user->property_data);
492 	data_user->property_name = NULL;
493 	data_user->property_data = NULL;
494     } else if (strcmp(element_name, "dle") == 0) {
495 	if (dle->program_is_application_api &&
496 	    !dle->program) {
497 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
498 		    "XML: program set to APPLICATION but no application set");
499 	    return;
500 	}
501 	if (dle->device == NULL && dle->disk)
502 	    dle->device = stralloc(dle->disk);
503 	if (dle->estimatelist == NULL)
504 	    dle->estimatelist = g_slist_append(dle->estimatelist, ES_CLIENT);
505 /* Add check of required field */
506 	data_user->property = NULL;
507 	data_user->dle = NULL;
508     } else if (strcmp(element_name, "backup-program") == 0) {
509 	if (dle->program == NULL) {
510 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
511 		    "XML: No plugin set for application");
512 	    return;
513 	}
514 	dle->application_property = data_user->property;
515 	data_user->property = dle->property;
516     } else if (strcmp(element_name, "script") == 0) {
517 	if (data_user->script->plugin == NULL) {
518 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
519 		    "XML: No plugin set for script");
520 	    return;
521 	}
522 	data_user->script->property = data_user->property;
523 	data_user->property = dle->property;
524 	dle->scriptlist = g_slist_append(dle->scriptlist, data_user->script);
525 	data_user->script = NULL;
526     } else if (strcmp(element_name, "level") == 0) {
527 	dle->levellist = g_slist_append(dle->levellist, data_user->alevel);
528 	data_user->alevel = NULL;
529     }
530     g_free(data_user->element_names->data);
531     data_user->element_names = g_slist_delete_link(data_user->element_names,
532 						   data_user->element_names);
533 }
534 
535 /* Called for character data */
536 /* text is not nul-terminated */
537 static void
amtext(G_GNUC_UNUSED GMarkupParseContext * context,const gchar * text,gsize text_len,gpointer user_data,GError ** gerror)538 amtext(
539     G_GNUC_UNUSED GMarkupParseContext *context,
540 		  const gchar         *text,
541 		  gsize                text_len,
542 		  gpointer             user_data,
543 		  GError             **gerror)
544 {
545     char     *tt;
546     amgxml_t *data_user = user_data;
547     GSList   *last_element = data_user->element_names;
548     char     *last_element_name;
549     GSList   *last_element2;
550     char     *last_element2_name;
551     dle_t    *dle = data_user->dle;
552 
553     if (!last_element) {
554 	g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
555 		    "XML: Invalid text");
556 	return;
557     }
558     last_element_name = last_element->data;
559 
560     tt = malloc(text_len + 8 + 1);
561     strncpy(tt,text,text_len);
562     tt[text_len] = '\0';
563 
564     //check if it is only space
565     if (match_no_newline("^[ \f\n\r\t\v]*$", tt)) {
566 	amfree(tt);
567 	return;
568     }
569 
570     if (data_user->raw) {
571 	amfree(tt);
572 	tt = stralloc(data_user->raw);
573     } else if (strlen(tt) > 0) {
574 	/* remove trailing space */
575 	char *ttt = tt + strlen(tt) - 1;
576 	while(*ttt == ' ') {
577 	    ttt--;
578 	}
579 	ttt++;
580 	*ttt = '\0';
581     }
582 
583     //check if it is only space
584     if (match_no_newline("^[ \f\n\r\t\v]*$", tt)) {
585 	amfree(tt);
586 	return;
587     }
588 
589     if (strcmp(last_element_name, "dle") == 0 ||
590 	strcmp(last_element_name, "backup-program") == 0 ||
591 	strcmp(last_element_name, "exclude") == 0 ||
592 	strcmp(last_element_name, "include") == 0) {
593 	g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
594 		    "XML: %s doesn't have text '%s'", last_element_name, tt);
595 	amfree(tt);
596 	return;
597     } else if(strcmp(last_element_name, "disk") == 0) {
598 	if (dle->disk != NULL) {
599 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
600 			"XML: multiple text in %s", last_element_name);
601 	    amfree(tt);
602 	    return;
603 	}
604 	dle->disk = tt;
605     } else if(strcmp(last_element_name, "diskdevice") == 0) {
606 	if (dle->device != NULL) {
607 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
608 			"XML: multiple text in %s", last_element_name);
609 	    amfree(tt);
610 	    return;
611 	}
612 	dle->device = tt;
613     } else if(strcmp(last_element_name, "calcsize") == 0) {
614 	if (strcasecmp(tt,"yes") == 0) {
615 	    dle->estimatelist = g_slist_append(dle->estimatelist,
616 					       GINT_TO_POINTER(ES_CALCSIZE));
617 	}
618 	amfree(tt);
619     } else if(strcmp(last_element_name, "estimate") == 0) {
620 	char *ttt = tt;
621 	while (strlen(ttt) > 0) {
622 	    if (BSTRNCMP(ttt,"CLIENT") == 0) {
623 		dle->estimatelist = g_slist_append(dle->estimatelist,
624 						   GINT_TO_POINTER(ES_CLIENT));
625 		ttt += strlen("client");
626 	    } else if (BSTRNCMP(ttt,"CALCSIZE") == 0) {
627 		if (!data_user->has_calcsize)
628 		    dle->estimatelist = g_slist_append(dle->estimatelist,
629 						 GINT_TO_POINTER(ES_CALCSIZE));
630 		ttt += strlen("calcsize");
631 	    } else if (BSTRNCMP(ttt,"SERVER") == 0) {
632 		dle->estimatelist = g_slist_append(dle->estimatelist,
633 						   GINT_TO_POINTER(ES_SERVER));
634 		ttt += strlen("server");
635 	    } else {
636 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
637 			"XML: bad estimate: %s", tt);
638 		return;
639 	    }
640 	    while (*ttt == ' ')
641 		ttt++;
642 	}
643 	amfree(tt);
644     } else if(strcmp(last_element_name, "program") == 0) {
645 	if (dle->program != NULL) {
646 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
647 			"XML: multiple text in %s", last_element_name);
648 	    amfree(tt);
649 	    return;
650 	}
651 	if (strcmp(tt, "APPLICATION") == 0) {
652 	    dle->program_is_application_api = 1;
653 	    dle->program = NULL;
654 	    amfree(tt);
655 	} else {
656 	    dle->program = tt;
657 	}
658     } else if(strcmp(last_element_name, "plugin") == 0) {
659 	last_element2 = g_slist_nth(data_user->element_names, 1);
660 	if (!last_element2) {
661 	    error("Invalid name text");
662 	}
663 	last_element2_name = last_element2->data;
664 	if (strcmp(last_element2_name, "backup-program") == 0) {
665 	    dle->program = tt;
666 	} else if (strcmp(last_element2_name, "script") == 0) {
667 	    data_user->script->plugin = tt;
668 	} else {
669 	    error("plugin outside of backup-program");
670 	}
671 	data_user->has_plugin = 1;
672     } else if(strcmp(last_element_name, "name") == 0) {
673 	last_element2 = g_slist_nth(data_user->element_names, 1);
674 	if (!last_element2) {
675 	    error("Invalid name text");
676 	}
677 	last_element2_name = last_element2->data;
678 	if (strcmp(last_element2_name, "property") == 0) {
679 	    data_user->property_name = tt;
680 	} else {
681 	    error("name outside of property");
682 	}
683     } else if(strcmp(last_element_name, "priority") == 0) {
684 	last_element2 = g_slist_nth(data_user->element_names, 1);
685 	if (!last_element2) {
686 	    error("Invalid priority text");
687 	}
688 	last_element2_name = last_element2->data;
689 	if (strcmp(last_element2_name, "property") == 0) {
690 	    if (strcasecmp(tt,"yes") == 0) {
691 		data_user->property_data->priority = 1;
692 	    }
693 	} else {
694 	    error("priority outside of property");
695 	}
696 	amfree(tt);
697     } else if(strcmp(last_element_name, "value") == 0) {
698 	last_element2 = g_slist_nth(data_user->element_names, 1);
699 	if (!last_element2) {
700 	    error("Invalid name text");
701 	}
702 	last_element2_name = last_element2->data;
703 	if (strcmp(last_element2_name, "property") == 0) {
704 	    data_user->property_data->values =
705 			g_slist_append(data_user->property_data->values, tt);
706 	} else {
707 	    error("value outside of property");
708 	}
709     } else if(strcmp(last_element_name, "auth") == 0) {
710 	if (dle->auth != NULL) {
711 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
712 			"XML: multiple text in %s", last_element_name);
713 	    return;
714 	}
715 	dle->auth = tt;
716     } else if(strcmp(last_element_name, "level") == 0) {
717 	data_user->alevel->level = atoi(tt);
718 	amfree(tt);
719     } else if (strcmp(last_element_name, "server") == 0) {
720 	if (strcasecmp(tt,"no") == 0) {
721 	    data_user->alevel->server = 0;
722 	} else if (strcasecmp(tt,"yes") == 0) {
723 	    data_user->alevel->server = 1;
724 	} else {
725 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
726 			"XML: Invalid %s (%s)", last_element_name, tt);
727 	    amfree(tt);
728 	    return;
729 	}
730 	amfree(tt);
731     } else if(strcmp(last_element_name, "index") == 0) {
732 	if (strcasecmp(tt,"no") == 0) {
733 	    dle->create_index = 0;
734 	} else if (strcasecmp(tt,"yes") == 0) {
735 	    dle->create_index = 1;
736 	} else {
737 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
738 			"XML: Invalid %s (%s)", last_element_name, tt);
739 	    amfree(tt);
740 	    return;
741 	}
742 	amfree(tt);
743     } else if(strcmp(last_element_name, "dumpdate") == 0) {
744 	if (dle->dumpdate != NULL) {
745 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
746 			"XML: multiple text in %s", last_element_name);
747 	    amfree(tt);
748 	    return;
749 	}
750 	dle->dumpdate = tt;
751     } else if(strcmp(last_element_name, "record") == 0) {
752 	if (strcasecmp(tt, "no") == 0) {
753 	    dle->record = 0;
754 	} else if (strcasecmp(tt, "yes") == 0) {
755 	    dle->record = 1;
756 	} else {
757 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
758 			"XML: Invalid %s (%s)", last_element_name, tt);
759 	    amfree(tt);
760 	    return;
761 	}
762 	amfree(tt);
763     } else if(strcmp(last_element_name, "spindle") == 0) {
764 	dle->spindle = atoi(tt);
765 	amfree(tt);
766     } else if(strcmp(last_element_name, "compress") == 0) {
767 	if (strcmp(tt, "FAST") == 0) {
768 	    dle->compress = COMP_FAST;
769 	} else if (strcmp(tt, "BEST") == 0) {
770 	    dle->compress = COMP_BEST;
771 	} else if (BSTRNCMP(tt, "CUSTOM") == 0) {
772 	    dle->compress = COMP_CUST;
773 	} else if (strcmp(tt, "SERVER-FAST") == 0) {
774 	    dle->compress = COMP_SERVER_FAST;
775 	} else if (strcmp(tt, "SERVER-BEST") == 0) {
776 	    dle->compress = COMP_SERVER_BEST;
777 	} else if (BSTRNCMP(tt, "SERVER-CUSTOM") == 0) {
778 	    dle->compress = COMP_SERVER_CUST;
779 	} else {
780 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
781 			"XML: Invalid %s (%s)", last_element_name, tt);
782 	    amfree(tt);
783 	    return;
784 	}
785 	amfree(tt);
786     } else if(strcmp(last_element_name, "custom-compress-program") == 0) {
787 	if (dle->compprog != NULL) {
788 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
789 			"XML: multiple text in %s", last_element_name);
790 	    amfree(tt);
791 	    return;
792 	}
793 	dle->compprog = tt;
794     } else if(strcmp(last_element_name, "encrypt") == 0) {
795 	if (BSTRNCMP(tt,"NO") == 0) {
796 	    dle->encrypt = ENCRYPT_NONE;
797 	} else if (BSTRNCMP(tt, "CUSTOM") == 0) {
798 	    dle->encrypt = ENCRYPT_CUST;
799 	} else if (BSTRNCMP(tt, "SERVER-CUSTOM") == 0) {
800 	    dle->encrypt = ENCRYPT_SERV_CUST;
801 	} else {
802 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
803 			"XML: Invalid %s (%s)", last_element_name, tt);
804 	    amfree(tt);
805 	    return;
806 	}
807 	amfree(tt);
808     } else if(strcmp(last_element_name, "kencrypt") == 0) {
809 	if (strcasecmp(tt,"no") == 0) {
810 	    dle->kencrypt = 0;
811 	} else if (strcasecmp(tt,"yes") == 0) {
812 	    dle->kencrypt = 1;
813 	} else {
814 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
815 			"XML: Invalid %s (%s)", last_element_name, tt);
816 	    amfree(tt);
817 	    return;
818 	}
819 	amfree(tt);
820     } else if(strcmp(last_element_name, "custom-encrypt-program") == 0) {
821 	last_element2 = g_slist_nth(data_user->element_names, 1);
822 	if (!last_element2) {
823 	    error("XML: optional");
824 	}
825 	last_element2_name = last_element2->data;
826 	if (dle->encrypt == ENCRYPT_SERV_CUST)
827 	    dle->srv_encrypt = tt;
828 	else
829 	    dle->clnt_encrypt = tt;
830     } else if(strcmp(last_element_name, "decrypt-option") == 0) {
831 	last_element2 = g_slist_nth(data_user->element_names, 1);
832 	if (!last_element2) {
833 	    error("XML: optional");
834 	}
835 	last_element2_name = last_element2->data;
836 	if (dle->encrypt == ENCRYPT_SERV_CUST)
837 	    dle->srv_decrypt_opt = tt;
838 	else
839 	    dle->clnt_decrypt_opt = tt;
840     } else if(strcmp(last_element_name, "exclude") == 0 ||
841 	      strcmp(last_element_name, "include") == 0) {
842 	data_user->has_optional = 0;
843 	amfree(tt);
844     } else if(strcmp(last_element_name, "file") == 0) {
845 	last_element2 = g_slist_nth(data_user->element_names, 1);
846 	if (!last_element2) {
847 	    error("XML: optional");
848 	}
849 	last_element2_name = last_element2->data;
850 	if (strcmp(last_element2_name, "exclude") == 0) {
851 	    dle->exclude_file = append_sl(dle->exclude_file, tt);
852 	} else if (strcmp(last_element2_name, "include") == 0) {
853 	    dle->include_file = append_sl(dle->include_file, tt);
854 	} else {
855 	    error("bad file");
856 	}
857     } else if(strcmp(last_element_name, "list") == 0) {
858 	last_element2 = g_slist_nth(data_user->element_names, 1);
859 	if (!last_element2) {
860 	    error("XML: optional");
861 	}
862 	last_element2_name = last_element2->data;
863 	if (strcmp(last_element2_name, "exclude") == 0) {
864 	    dle->exclude_list = append_sl(dle->exclude_list, tt);
865 	} else if (strcmp(last_element2_name, "include") == 0) {
866 	    dle->include_list = append_sl(dle->include_list, tt);
867 	} else {
868 	    error("bad list");
869 	}
870     } else if(strcmp(last_element_name, "optional") == 0) {
871 	last_element2 = g_slist_nth(data_user->element_names, 1);
872 	if (!last_element2) {
873 	    error("XML: optional");
874 	}
875 	last_element2_name = last_element2->data;
876 	if (strcmp(last_element2_name, "exclude") == 0) {
877 	    dle->exclude_optional = 1;
878 	} else if (strcmp(last_element2_name, "include") == 0) {
879 	    dle->include_optional = 1;
880 	} else {
881 	    error("bad optional");
882 	}
883 	data_user->has_optional = 1;
884 	amfree(tt);
885     } else if(strcmp(last_element_name, "script") == 0) {
886 	amfree(tt);
887     } else if(strcmp(last_element_name, "execute_on") == 0) {
888 	char *sep;
889 	char *tt1 = tt;
890 	do {
891 	    sep = strchr(tt1,',');
892 	    if (sep)
893 		*sep = '\0';
894 	    if (strcmp(tt1,"PRE-AMCHECK") == 0)
895 		data_user->script->execute_on |= EXECUTE_ON_PRE_AMCHECK;
896 	    else if (strcmp(tt1,"PRE-DLE-AMCHECK") == 0)
897 		data_user->script->execute_on |= EXECUTE_ON_PRE_DLE_AMCHECK;
898 	    else if (strcmp(tt1,"PRE-HOST-AMCHECK") == 0)
899 		data_user->script->execute_on |= EXECUTE_ON_PRE_HOST_AMCHECK;
900 	    else if (strcmp(tt1,"POST-AMCHECK") == 0)
901 		data_user->script->execute_on |= EXECUTE_ON_POST_AMCHECK;
902 	    else if (strcmp(tt1,"POST-DLE-AMCHECK") == 0)
903 		data_user->script->execute_on |= EXECUTE_ON_POST_DLE_AMCHECK;
904 	    else if (strcmp(tt1,"POST-HOST-AMCHECK") == 0)
905 		data_user->script->execute_on |= EXECUTE_ON_POST_HOST_AMCHECK;
906 	    else if (strcmp(tt1,"PRE-ESTIMATE") == 0)
907 		data_user->script->execute_on |= EXECUTE_ON_PRE_ESTIMATE;
908 	    else if (strcmp(tt1,"PRE-DLE-ESTIMATE") == 0)
909 		data_user->script->execute_on |= EXECUTE_ON_PRE_DLE_ESTIMATE;
910 	    else if (strcmp(tt1,"PRE-HOST-ESTIMATE") == 0)
911 		data_user->script->execute_on |= EXECUTE_ON_PRE_HOST_ESTIMATE;
912 	    else if (strcmp(tt1,"POST-ESTIMATE") == 0)
913 		data_user->script->execute_on |= EXECUTE_ON_POST_ESTIMATE;
914 	    else if (strcmp(tt1,"POST-DLE-ESTIMATE") == 0)
915 		data_user->script->execute_on |= EXECUTE_ON_POST_DLE_ESTIMATE;
916 	    else if (strcmp(tt1,"POST-HOST-ESTIMATE") == 0)
917 		data_user->script->execute_on |= EXECUTE_ON_POST_HOST_ESTIMATE;
918 	    else if (strcmp(tt1,"PRE-BACKUP") == 0)
919 		data_user->script->execute_on |= EXECUTE_ON_PRE_BACKUP;
920 	    else if (strcmp(tt1,"PRE-DLE-BACKUP") == 0)
921 		data_user->script->execute_on |= EXECUTE_ON_PRE_DLE_BACKUP;
922 	    else if (strcmp(tt1,"PRE-HOST-BACKUP") == 0)
923 		data_user->script->execute_on |= EXECUTE_ON_PRE_HOST_BACKUP;
924 	    else if (strcmp(tt1,"POST-BACKUP") == 0)
925 		data_user->script->execute_on |= EXECUTE_ON_POST_BACKUP;
926 	    else if (strcmp(tt1,"POST-DLE-BACKUP") == 0)
927 		data_user->script->execute_on |= EXECUTE_ON_POST_DLE_BACKUP;
928 	    else if (strcmp(tt1,"POST-HOST-BACKUP") == 0)
929 		data_user->script->execute_on |= EXECUTE_ON_POST_HOST_BACKUP;
930 	    else if (strcmp(tt1,"PRE-RECOVER") == 0)
931 		data_user->script->execute_on |= EXECUTE_ON_PRE_RECOVER;
932 	    else if (strcmp(tt1,"POST-RECOVER") == 0)
933 		data_user->script->execute_on |= EXECUTE_ON_POST_RECOVER;
934 	    else if (strcmp(tt1,"PRE-LEVEL-RECOVER") == 0)
935 		data_user->script->execute_on |= EXECUTE_ON_PRE_LEVEL_RECOVER;
936 	    else if (strcmp(tt1,"POST-LEVEL-RECOVER") == 0)
937 		data_user->script->execute_on |= EXECUTE_ON_POST_LEVEL_RECOVER;
938 	    else if (strcmp(tt1,"INTER-LEVEL-RECOVER") == 0)
939 		data_user->script->execute_on |= EXECUTE_ON_INTER_LEVEL_RECOVER;
940 	    else
941 		dbprintf("BAD EXECUTE_ON: %s\n", tt1);
942 	    if (sep)
943 		tt1 = sep+1;
944 	} while (sep);
945 	amfree(tt);
946     } else if(strcmp(last_element_name, "execute_where") == 0) {
947 	if (strcmp(tt, "CLIENT") == 0) {
948 	    data_user->script->execute_where = ES_CLIENT;
949 	} else {
950 	    data_user->script->execute_where = ES_SERVER;
951 	}
952 	amfree(tt);
953     } else if(strcmp(last_element_name, "datapath") == 0) {
954 	if (strcmp(tt, "AMANDA") == 0) {
955 	    dle->data_path = DATA_PATH_AMANDA;
956 	} else if (strcmp(tt, "DIRECTTCP") == 0) {
957 	    dle->data_path = DATA_PATH_DIRECTTCP;
958 	} else {
959 	    g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
960 			"XML: bad datapath value '%s'", tt);
961 	}
962 	amfree(tt);
963     } else if(strcmp(last_element_name, "directtcp") == 0) {
964 	dle->directtcp_list = g_slist_append(dle->directtcp_list, tt);
965     } else if(strcmp(last_element_name, "client_name") == 0) {
966 	last_element2 = g_slist_nth(data_user->element_names, 1);
967 	if (!last_element2) {
968 	    error("Invalid client_name text");
969 	}
970 	last_element2_name = last_element2->data;
971 	if (strcmp(last_element2_name, "backup-program") == 0) {
972 	    dle->application_client_name = tt;
973 g_debug("set dle->application_client_name: %s", dle->application_client_name);
974 	} else if (strcmp(last_element2_name, "script") == 0) {
975 	    data_user->script->client_name = tt;
976 g_debug("set data_user->script->client_name: %s", data_user->script->client_name);
977 	} else {
978 	    error("client_name outside of script or backup-program");
979 	}
980     } else {
981 	g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
982 		    "XML: amtext not defined for '%s'", last_element_name);
983 	amfree(tt);
984 	return;
985     }
986 }
987 
988 dle_t *
amxml_parse_node_CHAR(char * txt,char ** errmsg)989 amxml_parse_node_CHAR(
990     char *txt,
991     char **errmsg)
992 {
993     amgxml_t             amgxml = {NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
994     GMarkupParser        parser = {&amstart_element, &amend_element, &amtext,
995 				   NULL, NULL};
996     GMarkupParseFlags    flags = 0;
997     GMarkupParseContext *context;
998     GError		*gerror = NULL;
999 
1000     (void)errmsg;
1001 
1002     context = g_markup_parse_context_new(&parser, flags, &amgxml, NULL);
1003 
1004     g_markup_parse_context_parse(context, txt, strlen(txt), &gerror);
1005     if (!gerror)
1006 	g_markup_parse_context_end_parse(context, &gerror);
1007     g_markup_parse_context_free(context);
1008     if (gerror) {
1009 	if (errmsg)
1010 	    *errmsg = stralloc(gerror->message);
1011 	g_error_free(gerror);
1012     }
1013     return amgxml.dles;
1014 
1015 }
1016 
1017 dle_t *
amxml_parse_node_FILE(FILE * file,char ** errmsg)1018 amxml_parse_node_FILE(
1019     FILE *file,
1020     char **errmsg)
1021 {
1022     amgxml_t             amgxml = {NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
1023     GMarkupParser        parser = {&amstart_element, &amend_element, &amtext,
1024 				   NULL, NULL};
1025     GMarkupParseFlags    flags = 0;
1026     GMarkupParseContext *context;
1027     GError		*gerror = NULL;
1028     char                *line;
1029 
1030     (void)errmsg;
1031 
1032     context = g_markup_parse_context_new(&parser, flags, &amgxml, NULL);
1033 
1034     while ((line = agets(file)) != NULL && !gerror) {
1035 	g_markup_parse_context_parse(context, line, strlen(line), &gerror);
1036 	amfree(line);
1037     }
1038     if (!gerror)
1039 	g_markup_parse_context_end_parse(context, &gerror);
1040     g_markup_parse_context_free(context);
1041     if (gerror) {
1042 	if (errmsg)
1043 	    *errmsg = stralloc(gerror->message);
1044 	g_error_free(gerror);
1045     }
1046     return amgxml.dles;
1047 }
1048 
1049 char *
amxml_format_tag(char * tag,char * value)1050 amxml_format_tag(
1051     char *tag,
1052     char *value)
1053 {
1054     char *b64value;
1055     char *c;
1056     int   need_raw;
1057     char *result;
1058     char *quoted_value;
1059     char *q;
1060 
1061     quoted_value = malloc(strlen(value)+1);
1062     q = quoted_value;
1063     need_raw = 0;
1064     for(c=value; *c != '\0'; c++) {
1065 	// Check include negative value, with the 8th bit set.
1066 	if (*c <= ' ' ||
1067 	    (unsigned char)*c > 127 ||
1068 	    *c == '<' ||
1069 	    *c == '>' ||
1070 	    *c == '"' ||
1071 	    *c == '&' ||
1072 	    *c == '\\' ||
1073 	    *c == '\'' ||
1074 	    *c == '\t' ||
1075 	    *c == '\f' ||
1076 	    *c == '\r' ||
1077 	    *c == '\n') {
1078 	    need_raw = 1;
1079 	    *q++ = '_';
1080 	} else {
1081 	    *q++ = *c;
1082 	}
1083     }
1084     *q = '\0';
1085 
1086     if (need_raw) {
1087 	base64_encode_alloc(value, strlen(value), &b64value);
1088 	result = vstralloc("<", tag,
1089 			   " encoding=\"raw\" raw=\"", b64value, "\">",
1090 			   quoted_value,
1091 			   "</", tag, ">",
1092 			   NULL);
1093 	amfree(b64value);
1094     } else {
1095 	result = vstralloc("<", tag, ">",
1096 			   value,
1097 			   "</", tag, ">",
1098 			   NULL);
1099     }
1100     amfree(quoted_value);
1101 
1102     return result;
1103 }
1104