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