3402ae8b0406c2764f2a0f7a281040e3e92f39bf
[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 init_dle(
81     dle_t *dle)
82 {
83     dle->disk = NULL;
84     dle->device = NULL;
85     dle->program_is_application_api = 0;
86     dle->program = NULL;
87     dle->estimatelist = NULL;
88     dle->record = 1;
89     dle->spindle = 0;
90     dle->compress = COMP_NONE;
91     dle->encrypt = ENCRYPT_NONE;
92     dle->kencrypt = 0;
93     dle->levellist = NULL;
94     dle->dumpdate = NULL;
95     dle->compprog = NULL;
96     dle->srv_encrypt = NULL;
97     dle->clnt_encrypt = NULL;
98     dle->srv_decrypt_opt = NULL;
99     dle->clnt_decrypt_opt = NULL;
100     dle->create_index = 0;
101     dle->auth = NULL;
102     dle->exclude_file = NULL;
103     dle->exclude_list = NULL;
104     dle->include_file = NULL;
105     dle->include_list = NULL;
106     dle->exclude_optional = 0;
107     dle->include_optional = 0;
108     dle->application_property = NULL;
109     dle->scriptlist = NULL;
110     dle->data_path = DATA_PATH_AMANDA;
111     dle->directtcp_list = NULL;
112     dle->next = NULL;
113 }
114
115
116 /* Called for open tags <foo bar="baz"> */
117 static void amstart_element(GMarkupParseContext *context,
118                             const gchar         *element_name,
119                             const gchar        **attribute_names,
120                             const gchar        **attribute_values,
121                             gpointer             user_data,
122                             GError             **gerror);
123
124 /* Called for close tags </foo> */
125 static void amend_element(GMarkupParseContext *context,
126                           const gchar         *element_name,
127                           gpointer             user_data,
128                           GError             **gerror);
129
130 /* Called for character data */
131 /* text is not nul-terminated */
132 static void amtext(GMarkupParseContext *context,
133                    const gchar         *text,
134                    gsize                text_len,  
135                    gpointer             user_data,
136                    GError             **gerror);
137
138 /* Called for open tags <foo bar="baz"> */
139 static void
140 amstart_element(
141     G_GNUC_UNUSED GMarkupParseContext *context,
142                   const gchar         *element_name,
143     G_GNUC_UNUSED const gchar        **attribute_names,
144     G_GNUC_UNUSED const gchar        **attribute_values,
145                   gpointer             user_data,
146                   GError             **gerror)
147 {
148     amgxml_t *data_user = user_data;
149     dle_t    *adle;
150     GSList   *last_element = data_user->element_names;
151     char     *last_element_name = NULL;
152     dle_t    *dle = data_user->dle;
153     const gchar   **at_names, **at_values;
154
155     if (last_element)
156         last_element_name = last_element->data;
157
158     data_user->raw = NULL;
159     data_user->encoding = NULL;
160
161     if (attribute_names) {
162         for(at_names = attribute_names, at_values = attribute_values;
163             *at_names != NULL && at_values != NULL;
164             at_names++, at_values++) {
165             if (strcmp(*at_names, "encoding") == 0) {
166                 data_user->encoding = stralloc(*at_values);
167             } else if (strcmp(*at_names, "raw") == 0) {
168                 data_user->raw = base64_decode_alloc_string((char *)*at_values);
169             } else {
170                 g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
171                             "XML: Invalid attribute '%s' for %s element",
172                             *at_names, element_name);
173                 return;
174             }
175         }
176     }
177
178     if (strcmp(element_name, "dle") == 0) {
179         if (last_element != NULL) {
180             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
181                         "XML: Invalid dle element");
182             return;
183         }
184         for(adle = data_user->dles; adle != NULL && adle->next != NULL;
185             adle = adle->next);
186         data_user->dle = alloc_dle();
187         if (adle == NULL) {
188             data_user->dles = data_user->dle;
189         } else {
190             adle->next = data_user->dle;
191         }
192         data_user->has_calcsize = 0;
193         data_user->has_estimate = 0;
194         data_user->has_record = 0;
195         data_user->has_spindle = 0;
196         data_user->has_compress = 0;
197         data_user->has_encrypt = 0;
198         data_user->has_kencrypt = 0;
199         data_user->has_datapath = 0;
200         data_user->has_exclude = 0;
201         data_user->has_include = 0;
202         data_user->has_index = 0;
203         data_user->has_backup_program = 0;
204         data_user->has_plugin = 0;
205         data_user->has_optional = 0;
206         data_user->property_name = NULL;
207         data_user->property_data = NULL;
208         data_user->property = NULL;
209         data_user->script = NULL;
210         data_user->alevel = NULL;
211         data_user->encoding = NULL;
212         data_user->raw = NULL;
213     } else if(strcmp(element_name, "disk"          ) == 0 ||
214               strcmp(element_name, "diskdevice"    ) == 0 ||
215               strcmp(element_name, "calcsize"      ) == 0 ||
216               strcmp(element_name, "estimate"      ) == 0 ||
217               strcmp(element_name, "program"       ) == 0 ||
218               strcmp(element_name, "auth"          ) == 0 ||
219               strcmp(element_name, "index"         ) == 0 ||
220               strcmp(element_name, "dumpdate"      ) == 0 ||
221               strcmp(element_name, "level"         ) == 0 ||
222               strcmp(element_name, "record"        ) == 0 ||
223               strcmp(element_name, "spindle"       ) == 0 ||
224               strcmp(element_name, "compress"      ) == 0 ||
225               strcmp(element_name, "encrypt"       ) == 0 ||
226               strcmp(element_name, "kencrypt"      ) == 0 ||
227               strcmp(element_name, "datapath"      ) == 0 ||
228               strcmp(element_name, "exclude"       ) == 0 ||
229               strcmp(element_name, "include"       ) == 0) {
230         if (strcmp(last_element_name, "dle") != 0) {
231             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
232                         "XML: Invalid %s element", element_name);
233             return;
234         }
235         if ((strcmp(element_name, "disk"          ) == 0 && dle->disk) ||
236             (strcmp(element_name, "diskdevice"    ) == 0 && dle->device) ||
237             (strcmp(element_name, "calcsize"      ) == 0 && data_user->has_calcsize) ||
238             (strcmp(element_name, "estimate"      ) == 0 && data_user->has_estimate) ||
239             (strcmp(element_name, "record"        ) == 0 && data_user->has_record) ||
240             (strcmp(element_name, "spindle"       ) == 0 && data_user->has_spindle) ||
241             (strcmp(element_name, "program"       ) == 0 && dle->program) ||
242             (strcmp(element_name, "auth"          ) == 0 && dle->auth) ||
243             (strcmp(element_name, "index"         ) == 0 && data_user->has_index) ||
244             (strcmp(element_name, "dumpdate"      ) == 0 && dle->dumpdate) ||
245             (strcmp(element_name, "compress"      ) == 0 && data_user->has_compress) ||
246             (strcmp(element_name, "encrypt"       ) == 0 && data_user->has_encrypt) ||
247             (strcmp(element_name, "kencrypt"      ) == 0 && data_user->has_kencrypt) ||
248             (strcmp(element_name, "datapath"      ) == 0 && data_user->has_datapath) ||
249             (strcmp(element_name, "exclude"       ) == 0 && data_user->has_exclude) ||
250             (strcmp(element_name, "include"       ) == 0 && data_user->has_include)) {
251             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
252                         "XML: Duplicate %s element", element_name);
253             return;
254         }
255         if (strcmp(element_name, "calcsize"      ) == 0) data_user->has_calcsize       = 1;
256         if (strcmp(element_name, "estimate"      ) == 0) data_user->has_estimate       = 1;
257         if (strcmp(element_name, "record"        ) == 0) data_user->has_record         = 1;
258         if (strcmp(element_name, "spindle"       ) == 0) data_user->has_spindle        = 1;
259         if (strcmp(element_name, "index"         ) == 0) data_user->has_index          = 1;
260         if (strcmp(element_name, "compress"      ) == 0) data_user->has_compress       = 1;
261         if (strcmp(element_name, "encrypt"       ) == 0) data_user->has_encrypt        = 1;
262         if (strcmp(element_name, "kencrypt"      ) == 0) data_user->has_kencrypt       = 1;
263         if (strcmp(element_name, "datapath"      ) == 0) data_user->has_datapath       = 1;
264         if (strcmp(element_name, "exclude"       ) == 0) data_user->has_exclude        = 1;
265         if (strcmp(element_name, "include"       ) == 0) data_user->has_include        = 1;
266         if (strcmp(element_name, "exclude") == 0 || strcmp(element_name, "include") == 0)
267            data_user->has_optional = 0;
268         if (strcmp(element_name, "level") == 0) {
269             data_user->alevel = g_new0(level_t, 1);
270         }
271     } else if (strcmp(element_name, "server") == 0) {
272         if (strcmp(last_element_name, "level") != 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     } else if(strcmp(element_name, "custom-compress-program") == 0) {
278         if (strcmp(last_element_name, "compress") != 0) {
279             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
280                         "XML: Invalid %s element", element_name);
281             return;
282         }
283         if (dle->compprog) {
284             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
285                         "XML: Duplicate %s element", element_name);
286             return;
287         }
288     } else if (strcmp(element_name, "custom-encrypt-program") == 0 ||
289                strcmp(element_name, "decrypt-option") == 0) {
290         if (strcmp(last_element_name, "encrypt") != 0) {
291             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
292                         "XML: Invalid %s element", element_name);
293             return;
294         }
295         if (strcmp(element_name, "custom-encrypt-program") == 0 &&
296                    dle->clnt_encrypt) {
297             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
298                         "XML: Duplicate %s element", element_name);
299             return;
300         }
301         if (strcmp(element_name, "decrypt-option") == 0 &&
302                    dle->clnt_decrypt_opt) {
303             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
304                         "XML: Duplicate %s element", element_name);
305             return;
306         }
307     } else if(strcmp(element_name, "plugin") == 0) {
308         if (strcmp(last_element_name, "backup-program") != 0 &&
309             strcmp(last_element_name, "script") != 0) {
310             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
311                         "XML: Invalid %s element", element_name);
312             return;
313         }
314         if (data_user->has_plugin) {
315             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
316                         "XML: Duplicate %s element in '%s'", element_name,
317                         last_element_name);
318             return;
319         }
320     } else if(strcmp(element_name, "property") == 0) {
321         if (strcmp(last_element_name, "backup-program") != 0 &&
322             strcmp(last_element_name, "script") != 0) {
323             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
324                         "XML: Invalid %s element", element_name);
325             return;
326         }
327         data_user->property_data = malloc(sizeof(property_t));
328         data_user->property_data->append = 0;
329         data_user->property_data->priority = 0;
330         data_user->property_data->values = NULL;
331     } else if(strcmp(element_name, "name") == 0) {
332         if (strcmp(last_element_name, "property") != 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 (data_user->property_name) {
338             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
339                         "XML: Duplicate %s element in '%s'", element_name,
340                         last_element_name);
341             return;
342         }
343     } else if(strcmp(element_name, "priority") == 0) {
344         if (strcmp(last_element_name, "property") != 0) {
345             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
346                         "XML: Invalid %s element", element_name);
347             return;
348         }
349     } else if(strcmp(element_name, "value") == 0) {
350         if (strcmp(last_element_name, "property") != 0) {
351             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
352                         "XML: Invalid %s element", element_name);
353             return;
354         }
355     } else if(strcmp(element_name, "file") == 0 ||
356               strcmp(element_name, "list") == 0 ||
357               strcmp(element_name, "optional") == 0) {
358         if (strcmp(last_element_name, "exclude") != 0 &&
359             strcmp(last_element_name, "include") != 0) {
360             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
361                         "XML: Invalid %s element", element_name);
362             return;
363         }
364         if (strcmp(element_name, "optional") == 0 && data_user->has_optional) {
365             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
366                         "XML: Duplicate %s element", element_name);
367             return;
368         }
369         if (strcmp(element_name, "optional") == 0) data_user->has_optional = 1;
370     } else if (strcmp(element_name, "backup-program") == 0) {
371         if (data_user->has_backup_program) {
372             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
373                         "XML: Duplicate %s element", element_name);
374             return;
375         } else {
376             data_user->has_backup_program = 1;
377             data_user->property =
378                     g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
379             data_user->has_plugin = 0;
380         }
381     } else if (strcmp(element_name, "script") == 0) {
382         data_user->property =
383                     g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
384         data_user->script = malloc(sizeof(script_t));
385         data_user->script->plugin = NULL;
386         data_user->script->execute_on = 0;
387         data_user->script->execute_where = ES_CLIENT;
388         data_user->script->property = NULL;
389         data_user->script->result = NULL;
390         data_user->has_plugin = 0;
391     } else if (strcmp(element_name, "execute_on") == 0) {
392     } else if (strcmp(element_name, "execute_where") == 0) {
393     } else if (strcmp(element_name, "directtcp") == 0) {
394         if (strcmp(last_element_name, "datapath") != 0) {
395             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
396                         "XML: Invalid %s element", element_name);
397             return;
398         }
399     } else {
400         g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
401                     "XML: Invalid %s element", element_name);
402         return;
403     }
404     data_user->element_names = g_slist_prepend(data_user->element_names,
405                                                stralloc(element_name));
406 }
407
408 /* Called for close tags </foo> */
409 static void
410 amend_element(
411     G_GNUC_UNUSED GMarkupParseContext *context,
412                   const gchar         *element_name,
413                   gpointer             user_data,
414                   GError             **gerror)
415 {
416     amgxml_t *data_user = user_data;
417     GSList   *last_element = data_user->element_names;
418     char     *last_element_name = NULL;
419     dle_t    *dle = data_user->dle;
420
421     if (!last_element) {
422         g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
423                     "XML: Invalid closing tag");
424         return;
425     }
426     last_element_name = last_element->data;
427     if (strcmp(last_element_name, element_name) != 0) {
428         g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
429                     "XML: Invalid closing tag '%s'", element_name);
430         return;
431     }
432
433     if (strcmp(element_name, "property") == 0) {
434         g_hash_table_insert(data_user->property,
435                             data_user->property_name,
436                             data_user->property_data);
437         data_user->property_name = NULL;
438         data_user->property_data = NULL;
439     } else if (strcmp(element_name, "dle") == 0) {
440         if (dle->program_is_application_api &&
441             !dle->program) {
442             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
443                     "XML: program set to APPLICATION but no application set");
444             return;
445         }
446         if (dle->device == NULL && dle->disk)
447             dle->device = stralloc(dle->disk);
448         if (dle->estimatelist == NULL)
449             dle->estimatelist = g_slist_append(dle->estimatelist, ES_CLIENT);
450 /* Add check of required field */
451         data_user->dle = NULL;
452     } else if (strcmp(element_name, "backup-program") == 0) {
453         if (dle->program == NULL) {
454             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
455                     "XML: No plugin set for application");
456             return;
457         }
458         dle->application_property = data_user->property;
459         data_user->property = NULL;
460     } else if (strcmp(element_name, "script") == 0) {
461         if (data_user->script->plugin == NULL) {
462             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
463                     "XML: No plugin set for script");
464             return;
465         }
466         data_user->script->property = data_user->property;
467         data_user->property = NULL;
468         dle->scriptlist = g_slist_append(dle->scriptlist, data_user->script);
469         data_user->script = NULL;
470     } else if (strcmp(element_name, "level") == 0) {
471         dle->levellist = g_slist_append(dle->levellist, data_user->alevel);
472         data_user->alevel = NULL;
473     }
474     g_free(data_user->element_names->data);
475     data_user->element_names = g_slist_delete_link(data_user->element_names,
476                                                    data_user->element_names);
477 }
478
479 /* Called for character data */
480 /* text is not nul-terminated */
481 static void
482 amtext(
483     G_GNUC_UNUSED GMarkupParseContext *context,
484                   const gchar         *text,
485                   gsize                text_len,  
486                   gpointer             user_data,
487                   GError             **gerror)
488 {
489     char     *tt;
490     amgxml_t *data_user = user_data;
491     GSList   *last_element = data_user->element_names;
492     char     *last_element_name;
493     GSList   *last_element2;
494     char     *last_element2_name;
495     dle_t    *dle = data_user->dle;
496     int       i;
497
498     if (!last_element) {
499         g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
500                     "XML: Invalid text");
501         return;
502     }
503     last_element_name = last_element->data;
504
505     tt = malloc(text_len + 1);
506     strncpy(tt,text,text_len);
507     tt[text_len] = '\0';
508
509     //check if it is only space
510     if (match_no_newline("^[ \f\n\r\t\v]*$", tt)) {
511         amfree(tt);
512         return;
513     }
514
515     if (data_user->raw) {
516         amfree(tt);
517         tt = stralloc(data_user->raw);
518     } else if (strlen(tt) > 0) {
519         /* remove trailing space */
520         char *ttt = tt + strlen(tt) - 1;
521         while(*ttt == ' ') {
522             ttt--;
523         }
524         ttt++;
525         *ttt = '\0';
526     }
527
528     //check if it is only space
529     if (match_no_newline("^[ \f\n\r\t\v]*$", tt)) {
530         amfree(tt);
531         return;
532     }
533
534     if (strcmp(last_element_name, "dle") == 0 ||
535         strcmp(last_element_name, "backup-program") == 0 ||
536         strcmp(last_element_name, "exclude") == 0 ||
537         strcmp(last_element_name, "include") == 0) {
538         g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
539                     "XML: %s doesn't have text '%s'", last_element_name, tt);
540         return;
541     } else if(strcmp(last_element_name, "disk") == 0) {
542         if (dle->disk != NULL) {
543             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
544                         "XML: multiple text in %s", last_element_name);
545             return;
546         }
547         dle->disk = tt;
548     } else if(strcmp(last_element_name, "diskdevice") == 0) {
549         if (dle->device != NULL) {
550             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
551                         "XML: multiple text in %s", last_element_name);
552             return;
553         }
554         dle->device = tt;
555     } else if(strcmp(last_element_name, "calcsize") == 0) {
556         if (strcasecmp(tt,"yes") == 0) {
557             dle->estimatelist = g_slist_append(dle->estimatelist,
558                                                GINT_TO_POINTER(ES_CALCSIZE));
559         }
560         amfree(tt);
561     } else if(strcmp(last_element_name, "estimate") == 0) {
562         char *ttt = tt;
563         while (strlen(ttt) > 0) {
564             if (BSTRNCMP(ttt,"CLIENT") == 0) {
565                 dle->estimatelist = g_slist_append(dle->estimatelist,
566                                                    GINT_TO_POINTER(ES_CLIENT));
567                 ttt += strlen("client");
568             } else if (BSTRNCMP(ttt,"CALCSIZE") == 0) {
569                 if (!data_user->has_calcsize)
570                     dle->estimatelist = g_slist_append(dle->estimatelist,
571                                                  GINT_TO_POINTER(ES_CALCSIZE));
572                 ttt += strlen("calcsize");
573             } else if (BSTRNCMP(ttt,"SERVER") == 0) {
574                 dle->estimatelist = g_slist_append(dle->estimatelist,
575                                                    GINT_TO_POINTER(ES_SERVER));
576                 ttt += strlen("server");
577             } else {
578             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
579                         "XML: bad estimate: %s", tt);
580                 return;
581             }
582             while (*ttt == ' ')
583                 ttt++;
584         }
585         amfree(tt);
586     } else if(strcmp(last_element_name, "program") == 0) {
587         if (dle->program != NULL) {
588             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
589                         "XML: multiple text in %s", last_element_name);
590             return;
591         }
592         dle->program = tt;
593         if (strcmp(tt, "APPLICATION") == 0) {
594             dle->program_is_application_api = 1;
595             dle->program = NULL;
596         }
597     } else if(strcmp(last_element_name, "plugin") == 0) {
598         last_element2 = g_slist_nth(data_user->element_names, 1);
599         if (!last_element2) {
600             error("Invalid name text");
601         }
602         last_element2_name = last_element2->data;
603         if (strcmp(last_element2_name, "backup-program") == 0) {
604             dle->program = tt;
605         } else if (strcmp(last_element2_name, "script") == 0) {
606             data_user->script->plugin = tt;
607         } else {
608             error("plugin outside of backup-program");
609         }
610         data_user->has_plugin = 1;
611     } else if(strcmp(last_element_name, "name") == 0) {
612         last_element2 = g_slist_nth(data_user->element_names, 1);
613         if (!last_element2) {
614             error("Invalid name text");
615         }
616         last_element2_name = last_element2->data;
617         if (strcmp(last_element2_name, "property") == 0) {
618             data_user->property_name = tt;
619         } else {
620             error("name outside of property");
621         }
622     } else if(strcmp(last_element_name, "priority") == 0) {
623         last_element2 = g_slist_nth(data_user->element_names, 1);
624         if (!last_element2) {
625             error("Invalid priority text");
626         }
627         last_element2_name = last_element2->data;
628         if (strcmp(last_element2_name, "property") == 0) {
629             if (strcasecmp(tt,"yes") == 0) {
630                 data_user->property_data->priority = 1;
631             }
632         } else {
633             error("priority outside of property");
634         }
635     } else if(strcmp(last_element_name, "value") == 0) {
636         last_element2 = g_slist_nth(data_user->element_names, 1);
637         if (!last_element2) {
638             error("Invalid name text");
639         }
640         last_element2_name = last_element2->data;
641         if (strcmp(last_element2_name, "property") == 0) {
642             data_user->property_data->values =
643                         g_slist_append(data_user->property_data->values, tt);
644         } else {
645             error("value outside of property");
646         }
647     } else if(strcmp(last_element_name, "auth") == 0) {
648         if (dle->auth != NULL) {
649             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
650                         "XML: multiple text in %s", last_element_name);
651             return;
652         }
653         dle->auth = tt;
654     } else if(strcmp(last_element_name, "level") == 0) {
655         data_user->alevel->level = atoi(tt);
656         amfree(tt);
657     } else if (strcmp(last_element_name, "server") == 0) {
658         if (strcasecmp(tt,"no") == 0) {
659             data_user->alevel->server = 0;
660         } else if (strcasecmp(tt,"yes") == 0) {
661             data_user->alevel->server = 1;
662         } else {
663             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
664                         "XML: Invalid %s (%s)", last_element_name, tt);
665             amfree(tt);
666             return;
667         }
668         amfree(tt);
669     } else if(strcmp(last_element_name, "index") == 0) {
670         if (strcasecmp(tt,"no") == 0) {
671             dle->create_index = 0;
672         } else if (strcasecmp(tt,"yes") == 0) {
673             dle->create_index = 1;
674         } else {
675             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
676                         "XML: Invalid %s (%s)", last_element_name, tt);
677             amfree(tt);
678             return;
679         }
680         amfree(tt);
681     } else if(strcmp(last_element_name, "dumpdate") == 0) {
682         if (dle->dumpdate != NULL) {
683             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
684                         "XML: multiple text in %s", last_element_name);
685             amfree(tt);
686             return;
687         }
688         dle->dumpdate = tt;
689     } else if(strcmp(last_element_name, "record") == 0) {
690         if (strcasecmp(tt, "no") == 0) {
691             dle->record = 0;
692         } else if (strcasecmp(tt, "yes") == 0) {
693             dle->record = 1;
694         } else {
695             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
696                         "XML: Invalid %s (%s)", last_element_name, tt);
697             return;
698         }
699     } else if(strcmp(last_element_name, "spindle") == 0) {
700         dle->spindle = atoi(tt);
701     } else if(strcmp(last_element_name, "compress") == 0) {
702         if (strcmp(tt, "FAST") == 0) {
703             dle->compress = COMP_FAST;
704         } else if (strcmp(tt, "BEST") == 0) {
705             dle->compress = COMP_BEST;
706         } else if (BSTRNCMP(tt, "CUSTOM") == 0) {
707             dle->compress = COMP_CUST;
708         } else if (strcmp(tt, "SERVER-FAST") == 0) {
709             dle->compress = COMP_SERVER_FAST;
710         } else if (strcmp(tt, "SERVER-BEST") == 0) {
711             dle->compress = COMP_SERVER_BEST;
712         } else if (BSTRNCMP(tt, "SERVER-CUSTOM") == 0) {
713             dle->compress = COMP_SERVER_CUST;
714         } else {
715             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
716                         "XML: Invalid %s (%s)", last_element_name, tt);
717             amfree(tt);
718             return;
719         }
720         amfree(tt);
721     } else if(strcmp(last_element_name, "custom-compress-program") == 0) {
722         if (dle->compprog != NULL) {
723             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
724                         "XML: multiple text in %s", last_element_name);
725             amfree(tt);
726             return;
727         }
728         dle->compprog = tt;
729     } else if(strcmp(last_element_name, "encrypt") == 0) {
730         if (BSTRNCMP(tt,"NO") == 0) {
731             dle->encrypt = ENCRYPT_NONE;
732         } else if (BSTRNCMP(tt, "CUSTOM") == 0) {
733             dle->encrypt = ENCRYPT_CUST;
734         } else if (BSTRNCMP(tt, "SERVER-CUSTOM") == 0) {
735             dle->encrypt = ENCRYPT_SERV_CUST;
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, "kencrypt") == 0) {
744         if (strcasecmp(tt,"no") == 0) {
745             dle->kencrypt = 0;
746         } else if (strcasecmp(tt,"yes") == 0) {
747             dle->kencrypt = 1;
748         } else {
749             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
750                         "XML: Invalid %s (%s)", last_element_name, tt);
751             amfree(tt);
752             return;
753         }
754         amfree(tt);
755     } else if(strcmp(last_element_name, "custom-encrypt-program") == 0) {
756         last_element2 = g_slist_nth(data_user->element_names, 1);
757         if (!last_element2) {
758             error("XML: optional");
759         }
760         last_element2_name = last_element2->data;
761         dle->clnt_encrypt = tt;
762     } else if(strcmp(last_element_name, "decrypt-option") == 0) {
763         last_element2 = g_slist_nth(data_user->element_names, 1);
764         if (!last_element2) {
765             error("XML: optional");
766         }
767         last_element2_name = last_element2->data;
768         dle->clnt_decrypt_opt = tt;
769     } else if(strcmp(last_element_name, "exclude") == 0 ||
770               strcmp(last_element_name, "include") == 0) {
771         data_user->has_optional = 0;
772     } else if(strcmp(last_element_name, "file") == 0) {
773         last_element2 = g_slist_nth(data_user->element_names, 1);
774         if (!last_element2) {
775             error("XML: optional");
776         }
777         last_element2_name = last_element2->data;
778         if (strcmp(last_element2_name, "exclude") == 0) {
779             dle->exclude_file = append_sl(dle->exclude_file, tt);
780         } else if (strcmp(last_element2_name, "include") == 0) {
781             dle->include_file = append_sl(dle->include_file, tt);
782         } else {
783             error("bad file");
784         }
785     } else if(strcmp(last_element_name, "list") == 0) {
786         last_element2 = g_slist_nth(data_user->element_names, 1);
787         if (!last_element2) {
788             error("XML: optional");
789         }
790         last_element2_name = last_element2->data;
791         if (strcmp(last_element2_name, "exclude") == 0) {
792             dle->exclude_list = append_sl(dle->exclude_list, tt);
793         } else if (strcmp(last_element2_name, "include") == 0) {
794             dle->include_list = append_sl(dle->include_list, tt);
795         } else {
796             error("bad list");
797         }
798     } else if(strcmp(last_element_name, "optional") == 0) {
799         i = atoi(tt);
800         last_element2 = g_slist_nth(data_user->element_names, 1);
801         if (!last_element2) {
802             error("XML: optional");
803         }
804         last_element2_name = last_element2->data;
805         if (strcmp(last_element2_name, "exclude") == 0) {
806             dle->exclude_optional = 1;
807         } else if (strcmp(last_element2_name, "include") == 0) {
808             dle->include_optional = 1;
809         } else {
810             error("bad optional");
811         }
812         data_user->has_optional = 1;
813         amfree(tt);
814     } else if(strcmp(last_element_name, "script") == 0) {
815     } else if(strcmp(last_element_name, "execute_on") == 0) {
816         char *sep;
817         char *tt1 = tt;
818         do {
819             sep = strchr(tt1,',');
820             if (sep)
821                 *sep = '\0';
822             if (strcmp(tt1,"PRE-DLE-AMCHECK") == 0)
823                 data_user->script->execute_on |= EXECUTE_ON_PRE_DLE_AMCHECK;
824             else if (strcmp(tt1,"PRE-HOST-AMCHECK") == 0)
825                 data_user->script->execute_on |= EXECUTE_ON_PRE_HOST_AMCHECK;
826             else if (strcmp(tt1,"POST-DLE-AMCHECK") == 0)
827                 data_user->script->execute_on |= EXECUTE_ON_POST_DLE_AMCHECK;
828             else if (strcmp(tt1,"POST-HOST-AMCHECK") == 0)
829                 data_user->script->execute_on |= EXECUTE_ON_POST_HOST_AMCHECK;
830             else if (strcmp(tt1,"PRE-DLE-ESTIMATE") == 0)
831                 data_user->script->execute_on |= EXECUTE_ON_PRE_DLE_ESTIMATE;
832             else if (strcmp(tt1,"PRE-HOST-ESTIMATE") == 0)
833                 data_user->script->execute_on |= EXECUTE_ON_PRE_HOST_ESTIMATE;
834             else if (strcmp(tt1,"POST-DLE-ESTIMATE") == 0)
835                 data_user->script->execute_on |= EXECUTE_ON_POST_DLE_ESTIMATE;
836             else if (strcmp(tt1,"POST-HOST-ESTIMATE") == 0)
837                 data_user->script->execute_on |= EXECUTE_ON_POST_HOST_ESTIMATE;
838             else if (strcmp(tt1,"PRE-DLE-BACKUP") == 0)
839                 data_user->script->execute_on |= EXECUTE_ON_PRE_DLE_BACKUP;
840             else if (strcmp(tt1,"PRE-HOST-BACKUP") == 0)
841                 data_user->script->execute_on |= EXECUTE_ON_PRE_HOST_BACKUP;
842             else if (strcmp(tt1,"POST-DLE-BACKUP") == 0)
843                 data_user->script->execute_on |= EXECUTE_ON_POST_DLE_BACKUP;
844             else if (strcmp(tt1,"POST-HOST-BACKUP") == 0)
845                 data_user->script->execute_on |= EXECUTE_ON_POST_HOST_BACKUP;
846             else if (strcmp(tt1,"PRE-RECOVER") == 0)
847                 data_user->script->execute_on |= EXECUTE_ON_PRE_RECOVER;
848             else if (strcmp(tt1,"POST-RECOVER") == 0)
849                 data_user->script->execute_on |= EXECUTE_ON_POST_RECOVER;
850             else if (strcmp(tt1,"PRE-LEVEL-RECOVER") == 0)
851                 data_user->script->execute_on |= EXECUTE_ON_PRE_LEVEL_RECOVER;
852             else if (strcmp(tt1,"POST-LEVEL-RECOVER") == 0)
853                 data_user->script->execute_on |= EXECUTE_ON_POST_LEVEL_RECOVER;
854             else if (strcmp(tt1,"INTER-LEVEL-RECOVER") == 0)
855                 data_user->script->execute_on |= EXECUTE_ON_INTER_LEVEL_RECOVER;
856             else 
857                 dbprintf("BAD EXECUTE_ON: %s\n", tt1);
858             if (sep)
859                 tt1 = sep+1;
860         } while (sep);
861         amfree(tt);
862     } else if(strcmp(last_element_name, "execute_where") == 0) {
863         if (strcmp(tt, "CLIENT") == 0) {
864             data_user->script->execute_where = ES_CLIENT;
865         } else {
866             data_user->script->execute_where = ES_SERVER;
867         }
868     } else if(strcmp(last_element_name, "datapath") == 0) {
869         if (strcmp(tt, "AMANDA") == 0) {
870             dle->data_path = DATA_PATH_AMANDA;
871         } else if (strcmp(tt, "DIRECTTCP") == 0) {
872             dle->data_path = DATA_PATH_DIRECTTCP;
873         } else {
874             g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
875                         "XML: bad datapath value '%s'", tt);
876         }
877         amfree(tt);
878     } else if(strcmp(last_element_name, "directtcp") == 0) {
879         dle->directtcp_list = g_slist_append(dle->directtcp_list, tt);
880     } else {
881         g_set_error(gerror, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
882                     "XML: amtext not defined for '%s'", last_element_name);
883         return;
884     }
885 }
886
887 dle_t *
888 amxml_parse_node_CHAR(
889     char *txt,
890     char **errmsg)
891 {
892     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};
893     GMarkupParser        parser = {&amstart_element, &amend_element, &amtext,
894                                    NULL, NULL};
895     GMarkupParseFlags    flags = 0;
896     GMarkupParseContext *context;
897     GError              *gerror = NULL;
898
899     (void)errmsg;
900
901     context = g_markup_parse_context_new(&parser, flags, &amgxml, NULL);
902
903     g_markup_parse_context_parse(context, txt, strlen(txt), &gerror);
904     if (!gerror)
905         g_markup_parse_context_end_parse(context, &gerror);
906     g_markup_parse_context_free(context);
907     if (gerror) {
908         if (errmsg)
909             *errmsg = stralloc(gerror->message);
910         g_error_free(gerror);
911     }
912     return amgxml.dles;
913         
914 }
915
916 dle_t *
917 amxml_parse_node_FILE(
918     FILE *file,
919     char **errmsg)
920 {
921     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};
922     GMarkupParser        parser = {&amstart_element, &amend_element, &amtext,
923                                    NULL, NULL};
924     GMarkupParseFlags    flags = 0;
925     GMarkupParseContext *context;
926     GError              *gerror = NULL;
927     char                *line;
928
929     (void)errmsg;
930
931     context = g_markup_parse_context_new(&parser, flags, &amgxml, NULL);
932
933     while ((line = agets(file)) != NULL && !gerror) {
934         g_markup_parse_context_parse(context, line, strlen(line), &gerror);
935         amfree(line);
936     }
937     if (!gerror)
938         g_markup_parse_context_end_parse(context, &gerror);
939     g_markup_parse_context_free(context);
940     if (gerror) {
941         if (errmsg)
942             *errmsg = stralloc(gerror->message);
943         g_error_free(gerror);
944     }
945     return amgxml.dles;
946 }
947
948 char *
949 amxml_format_tag(
950     char *tag,
951     char *value)
952 {
953     char *b64value;
954     char *c;
955     int   need_raw;
956     char *result;
957     char *quoted_value;
958     char *q;
959
960     quoted_value = malloc(strlen(value)+1);
961     q = quoted_value;
962     need_raw = 0;
963     for(c=value; *c != '\0'; c++) {
964         // Check include negative value, with the 8th bit set.
965         if (*c <= ' ' ||
966             (unsigned char)*c > 127 ||
967             *c == '<' ||
968             *c == '>' ||
969             *c == '"' ||
970             *c == '&' ||
971             *c == '\\' ||
972             *c == '\'' ||
973             *c == '\t' ||
974             *c == '\f' ||
975             *c == '\r' ||
976             *c == '\n') {
977             need_raw = 1;
978             *q++ = '_';
979         } else {
980             *q++ = *c;
981         }
982     }
983     *q = '\0';
984
985     if (need_raw) {
986         base64_encode_alloc(value, strlen(value), &b64value);
987         result = vstralloc("<", tag,
988                            " encoding=\"raw\" raw=\"", b64value, "\">",
989                            quoted_value,
990                            "</", tag, ">",
991                            NULL);
992         amfree(b64value);
993     } else {
994         result = vstralloc("<", tag, ">",
995                            value,
996                            "</", tag, ">",
997                            NULL);
998     }
999     amfree(quoted_value);
1000
1001     return result;
1002 }