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