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