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