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