1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* config-parser.c XML-library-agnostic configuration file parser
4 * Copyright (C) 2003 Red Hat, Inc.
6 * Licensed under the Academic Free License version 1.2
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include "config-parser.h"
25 #include <dbus/dbus-list.h>
26 #include <dbus/dbus-internals.h>
47 unsigned int had_content : 1;
53 unsigned int ignore_missing : 1;
78 struct BusConfigParser
82 DBusList *stack; /**< stack of Element */
84 char *user; /**< user to run as */
86 DBusList *listen_on; /**< List of addresses to listen to */
90 element_type_to_name (ElementType type)
96 case ELEMENT_BUSCONFIG:
116 _dbus_assert_not_reached ("bad element type");
122 push_element (BusConfigParser *parser,
127 _dbus_assert (type != ELEMENT_NONE);
129 e = dbus_new0 (Element, 1);
133 if (!_dbus_list_append (&parser->stack, e))
145 element_free (Element *e)
152 pop_element (BusConfigParser *parser)
156 e = _dbus_list_pop_last (&parser->stack);
162 peek_element (BusConfigParser *parser)
166 e = _dbus_list_get_last (&parser->stack);
172 top_element_type (BusConfigParser *parser)
176 e = _dbus_list_get_last (&parser->stack);
185 merge_included (BusConfigParser *parser,
186 BusConfigParser *included,
191 if (included->user != NULL)
193 dbus_free (parser->user);
194 parser->user = included->user;
195 included->user = NULL;
198 while ((link = _dbus_list_pop_first_link (&included->listen_on)))
199 _dbus_list_append_link (&parser->listen_on, link);
205 bus_config_parser_new (void)
207 BusConfigParser *parser;
209 parser = dbus_new0 (BusConfigParser, 1);
213 parser->refcount = 1;
219 bus_config_parser_ref (BusConfigParser *parser)
221 _dbus_assert (parser->refcount > 0);
223 parser->refcount += 1;
227 bus_config_parser_unref (BusConfigParser *parser)
229 _dbus_assert (parser->refcount > 0);
231 parser->refcount -= 1;
233 if (parser->refcount == 0)
235 while (parser->stack != NULL)
236 pop_element (parser);
238 dbus_free (parser->user);
240 _dbus_list_foreach (&parser->listen_on,
241 (DBusForeachFunction) dbus_free,
244 _dbus_list_clear (&parser->listen_on);
251 bus_config_parser_check_doctype (BusConfigParser *parser,
255 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
257 if (strcmp (doctype, "busconfig") != 0)
259 dbus_set_error (error,
261 "Configuration file has the wrong document type %s",
276 locate_attributes (BusConfigParser *parser,
277 const char *element_name,
278 const char **attribute_names,
279 const char **attribute_values,
281 const char *first_attribute_name,
282 const char **first_attribute_retloc,
290 LocateAttr attrs[MAX_ATTRS];
294 _dbus_assert (first_attribute_name != NULL);
295 _dbus_assert (first_attribute_retloc != NULL);
300 attrs[0].name = first_attribute_name;
301 attrs[0].retloc = first_attribute_retloc;
302 *first_attribute_retloc = NULL;
304 va_start (args, first_attribute_retloc);
306 name = va_arg (args, const char*);
307 retloc = va_arg (args, const char**);
311 _dbus_assert (retloc != NULL);
312 _dbus_assert (n_attrs < MAX_ATTRS);
314 attrs[n_attrs].name = name;
315 attrs[n_attrs].retloc = retloc;
319 name = va_arg (args, const char*);
320 retloc = va_arg (args, const char**);
329 while (attribute_names[i])
338 if (strcmp (attrs[j].name, attribute_names[i]) == 0)
340 retloc = attrs[j].retloc;
344 dbus_set_error (error, DBUS_ERROR_FAILED,
345 "Attribute \"%s\" repeated twice on the same <%s> element",
346 attrs[j].name, element_name);
351 *retloc = attribute_values[i];
360 dbus_set_error (error, DBUS_ERROR_FAILED,
361 "Attribute \"%s\" is invalid on <%s> element in this context",
362 attribute_names[i], element_name);
375 check_no_attributes (BusConfigParser *parser,
376 const char *element_name,
377 const char **attribute_names,
378 const char **attribute_values,
381 if (attribute_names[0] != NULL)
383 dbus_set_error (error, DBUS_ERROR_FAILED,
384 "Attribute \"%s\" is invalid on <%s> element in this context",
385 attribute_names[0], element_name);
393 start_busconfig_child (BusConfigParser *parser,
394 const char *element_name,
395 const char **attribute_names,
396 const char **attribute_values,
399 if (strcmp (element_name, "user") == 0)
401 if (!check_no_attributes (parser, "user", attribute_names, attribute_values, error))
404 if (push_element (parser, ELEMENT_USER) == NULL)
406 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
412 else if (strcmp (element_name, "listen") == 0)
414 if (!check_no_attributes (parser, "listen", attribute_names, attribute_values, error))
417 if (push_element (parser, ELEMENT_LISTEN) == NULL)
419 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
425 else if (strcmp (element_name, "include") == 0)
428 const char *ignore_missing;
430 if ((e = push_element (parser, ELEMENT_INCLUDE)) == NULL)
432 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
436 e->d.include.ignore_missing = FALSE;
438 if (!locate_attributes (parser, "include",
442 "ignore_missing", &ignore_missing,
446 if (ignore_missing != NULL)
448 if (strcmp (ignore_missing, "yes") == 0)
449 e->d.include.ignore_missing = TRUE;
450 else if (strcmp (ignore_missing, "no") == 0)
451 e->d.include.ignore_missing = FALSE;
454 dbus_set_error (error, DBUS_ERROR_FAILED,
455 "ignore_missing attribute must have value \"yes\" or \"no\"");
462 else if (strcmp (element_name, "policy") == 0)
469 if ((e = push_element (parser, ELEMENT_POLICY)) == NULL)
471 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
475 if (!locate_attributes (parser, "include",
491 dbus_set_error (error, DBUS_ERROR_FAILED,
492 "Element <%s> not allowed inside <%s> in configuration file",
493 element_name, "busconfig");
499 start_policy_child (BusConfigParser *parser,
500 const char *element_name,
501 const char **attribute_names,
502 const char **attribute_values,
505 if (strcmp (element_name, "allow") == 0)
507 if (push_element (parser, ELEMENT_ALLOW) == NULL)
509 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
515 else if (strcmp (element_name, "deny") == 0)
517 if (push_element (parser, ELEMENT_DENY) == NULL)
519 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
527 dbus_set_error (error, DBUS_ERROR_FAILED,
528 "Element <%s> not allowed inside <%s> in configuration file",
529 element_name, "policy");
535 bus_config_parser_start_element (BusConfigParser *parser,
536 const char *element_name,
537 const char **attribute_names,
538 const char **attribute_values,
543 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
545 /* printf ("START: %s\n", element_name); */
547 t = top_element_type (parser);
549 if (t == ELEMENT_NONE)
551 if (strcmp (element_name, "busconfig") == 0)
553 if (!check_no_attributes (parser, "busconfig", attribute_names, attribute_values, error))
556 if (push_element (parser, ELEMENT_BUSCONFIG) == NULL)
558 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
566 dbus_set_error (error, DBUS_ERROR_FAILED,
567 "Unknown element <%s> at root of configuration file",
572 else if (t == ELEMENT_BUSCONFIG)
574 return start_busconfig_child (parser, element_name,
575 attribute_names, attribute_values,
578 else if (t == ELEMENT_POLICY)
580 return start_policy_child (parser, element_name,
581 attribute_names, attribute_values,
586 dbus_set_error (error, DBUS_ERROR_FAILED,
587 "Element <%s> is not allowed in this context",
594 bus_config_parser_end_element (BusConfigParser *parser,
595 const char *element_name,
602 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
604 /* printf ("END: %s\n", element_name); */
606 t = top_element_type (parser);
608 if (t == ELEMENT_NONE)
610 /* should probably be an assertion failure but
611 * being paranoid about XML parsers
613 dbus_set_error (error, DBUS_ERROR_FAILED,
614 "XML parser ended element with no element on the stack");
618 n = element_type_to_name (t);
619 _dbus_assert (n != NULL);
620 if (strcmp (n, element_name) != 0)
622 /* should probably be an assertion failure but
623 * being paranoid about XML parsers
625 dbus_set_error (error, DBUS_ERROR_FAILED,
626 "XML element ended which was not the topmost element on the stack");
630 e = peek_element (parser);
631 _dbus_assert (e != NULL);
636 _dbus_assert_not_reached ("element in stack has no type");
639 case ELEMENT_INCLUDE:
645 dbus_set_error (error, DBUS_ERROR_FAILED,
646 "XML element <%s> was expected to have content inside it",
647 element_type_to_name (e->type));
652 case ELEMENT_BUSCONFIG:
660 pop_element (parser);
666 all_whitespace (const DBusString *str)
670 _dbus_string_skip_white (str, 0, &i);
672 return i == _dbus_string_get_length (str);
676 bus_config_parser_content (BusConfigParser *parser,
677 const DBusString *content,
682 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
688 _dbus_string_get_const_data (content, &c_str);
690 printf ("CONTENT %d bytes: %s\n", _dbus_string_get_length (content), c_str);
694 e = peek_element (parser);
697 dbus_set_error (error, DBUS_ERROR_FAILED,
698 "Text content outside of any XML element in configuration file");
701 else if (e->had_content)
703 _dbus_assert_not_reached ("Element had multiple content blocks");
707 switch (top_element_type (parser))
710 _dbus_assert_not_reached ("element at top of stack has no type");
713 case ELEMENT_BUSCONFIG:
718 if (all_whitespace (content))
722 dbus_set_error (error, DBUS_ERROR_FAILED,
723 "No text content expected inside XML element %s in configuration file",
724 element_type_to_name (top_element_type (parser)));
728 case ELEMENT_INCLUDE:
730 /* FIXME good test case for this would load each config file in the
731 * test suite both alone, and as an include, and check
732 * that the result is the same
734 BusConfigParser *included;
737 e->had_content = TRUE;
739 dbus_error_init (&tmp_error);
740 included = bus_config_load (content, &tmp_error);
741 if (included == NULL)
743 _DBUS_ASSERT_ERROR_IS_SET (&tmp_error);
744 if (dbus_error_has_name (&tmp_error, DBUS_ERROR_FILE_NOT_FOUND) &&
745 e->d.include.ignore_missing)
747 dbus_error_free (&tmp_error);
752 dbus_move_error (&tmp_error, error);
758 _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error);
760 if (!merge_included (parser, included, error))
763 bus_config_parser_unref (included);
773 e->had_content = TRUE;
775 if (!_dbus_string_copy_data (content, &s))
778 dbus_free (parser->user);
787 e->had_content = TRUE;
789 if (!_dbus_string_copy_data (content, &s))
792 if (!_dbus_list_append (&parser->listen_on,
803 e->had_content = TRUE;
809 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
813 dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
818 bus_config_parser_finished (BusConfigParser *parser,
821 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
823 if (parser->stack != NULL)
825 dbus_set_error (error, DBUS_ERROR_FAILED,
826 "Element <%s> was not closed in configuration file",
827 element_type_to_name (top_element_type (parser)));
832 if (parser->listen_on == NULL)
834 dbus_set_error (error, DBUS_ERROR_FAILED,
835 "Configuration file needs one or more <listen> elements giving addresses");
843 bus_config_parser_get_user (BusConfigParser *parser)
849 bus_config_parser_get_addresses (BusConfigParser *parser)
851 return &parser->listen_on;
854 #ifdef DBUS_BUILD_TESTS
865 do_load (const DBusString *full_path,
867 dbus_bool_t oom_possible)
869 BusConfigParser *parser;
872 dbus_error_init (&error);
874 parser = bus_config_load (full_path, &error);
877 _DBUS_ASSERT_ERROR_IS_SET (&error);
880 dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
882 _dbus_verbose ("Failed to load valid file due to OOM\n");
883 dbus_error_free (&error);
886 else if (validity == VALID)
888 _dbus_warn ("Failed to load valid file but still had memory: %s\n",
891 dbus_error_free (&error);
896 dbus_error_free (&error);
902 _DBUS_ASSERT_ERROR_IS_CLEAR (&error);
904 bus_config_parser_unref (parser);
906 if (validity == INVALID)
908 _dbus_warn ("Accepted invalid file\n");
918 const DBusString *full_path;
923 check_loader_oom_func (void *data)
925 LoaderOomData *d = data;
927 return do_load (d->full_path, d->validity, TRUE);
931 process_test_subdir (const DBusString *test_base_dir,
935 DBusString test_directory;
944 if (!_dbus_string_init (&test_directory, _DBUS_INT_MAX))
945 _dbus_assert_not_reached ("didn't allocate test_directory\n");
947 _dbus_string_init_const (&filename, subdir);
949 if (!_dbus_string_copy (test_base_dir, 0,
951 _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory");
953 if (!_dbus_concat_dir_and_file (&test_directory, &filename))
954 _dbus_assert_not_reached ("couldn't allocate full path");
956 _dbus_string_free (&filename);
957 if (!_dbus_string_init (&filename, _DBUS_INT_MAX))
958 _dbus_assert_not_reached ("didn't allocate filename string\n");
960 dbus_error_init (&error);
961 dir = _dbus_directory_open (&test_directory, &error);
965 _dbus_string_get_const_data (&test_directory, &s);
966 _dbus_warn ("Could not open %s: %s\n", s,
968 dbus_error_free (&error);
972 printf ("Testing:\n");
975 while (_dbus_directory_get_next_file (dir, &filename, &error))
977 DBusString full_path;
980 if (!_dbus_string_init (&full_path, _DBUS_INT_MAX))
981 _dbus_assert_not_reached ("couldn't init string");
983 if (!_dbus_string_copy (&test_directory, 0, &full_path, 0))
984 _dbus_assert_not_reached ("couldn't copy dir to full_path");
986 if (!_dbus_concat_dir_and_file (&full_path, &filename))
987 _dbus_assert_not_reached ("couldn't concat file to dir");
989 if (!_dbus_string_ends_with_c_str (&full_path, ".conf"))
991 const char *filename_c;
992 _dbus_string_get_const_data (&filename, &filename_c);
993 _dbus_verbose ("Skipping non-.conf file %s\n",
995 _dbus_string_free (&full_path);
1001 _dbus_string_get_const_data (&filename, &s);
1002 printf (" %s\n", s);
1005 _dbus_verbose (" expecting %s\n",
1006 validity == VALID ? "valid" :
1007 (validity == INVALID ? "invalid" :
1008 (validity == UNKNOWN ? "unknown" : "???")));
1010 d.full_path = &full_path;
1011 d.validity = validity;
1012 if (!_dbus_test_oom_handling ("config-loader", check_loader_oom_func, &d))
1013 _dbus_assert_not_reached ("test failed");
1015 _dbus_string_free (&full_path);
1018 if (dbus_error_is_set (&error))
1021 _dbus_string_get_const_data (&test_directory, &s);
1022 _dbus_warn ("Could not get next file in %s: %s\n",
1024 dbus_error_free (&error);
1033 _dbus_directory_close (dir);
1034 _dbus_string_free (&test_directory);
1035 _dbus_string_free (&filename);
1041 bus_config_parser_test (const DBusString *test_data_dir)
1043 if (test_data_dir == NULL ||
1044 _dbus_string_get_length (test_data_dir) == 0)
1046 printf ("No test data\n");
1050 if (!process_test_subdir (test_data_dir, "valid-config-files", VALID))
1056 #endif /* DBUS_BUILD_TESTS */