2003-04-12 Havoc Pennington <hp@pobox.com>
[platform/upstream/dbus.git] / bus / config-parser.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* config-parser.c  XML-library-agnostic configuration file parser
3  *
4  * Copyright (C) 2003 Red Hat, Inc.
5  *
6  * Licensed under the Academic Free License version 1.2
7  *
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.
12  *
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.
17  *
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
21  *
22  */
23 #include "config-parser.h"
24 #include "test.h"
25 #include "utils.h"
26 #include "policy.h"
27 #include <dbus/dbus-list.h>
28 #include <dbus/dbus-internals.h>
29 #include <string.h>
30
31 typedef enum
32 {
33   ELEMENT_NONE,
34   ELEMENT_BUSCONFIG,
35   ELEMENT_INCLUDE,
36   ELEMENT_USER,
37   ELEMENT_LISTEN,
38   ELEMENT_AUTH,
39   ELEMENT_POLICY,
40   ELEMENT_LIMIT,
41   ELEMENT_ALLOW,
42   ELEMENT_DENY,
43   ELEMENT_FORK,
44   ELEMENT_PIDFILE,
45   ELEMENT_SERVICEDIR,
46   ELEMENT_INCLUDEDIR,
47   ELEMENT_TYPE
48 } ElementType;
49
50 typedef struct
51 {
52   ElementType type;
53
54   unsigned int had_content : 1;
55
56   union
57   {
58     struct
59     {
60       unsigned int ignore_missing : 1;
61     } include;
62
63     struct
64     {
65       char *context;
66       char *user;
67       char *group;
68       DBusList *rules;
69     } policy;
70
71     struct
72     {
73       int foo;
74     } limit;
75
76   } d;
77
78 } Element;
79
80 struct BusConfigParser
81 {
82   int refcount;
83
84   DBusString basedir;  /**< Directory we resolve paths relative to */
85   
86   DBusList *stack;     /**< stack of Element */
87
88   char *user;          /**< user to run as */
89
90   char *bus_type;          /**< Message bus type */
91   
92   DBusList *listen_on; /**< List of addresses to listen to */
93
94   DBusList *mechanisms; /**< Auth mechanisms */
95
96   DBusList *service_dirs; /**< Directories to look for services in */
97
98   BusPolicy *policy;     /**< Security policy */
99   
100   unsigned int fork : 1; /**< TRUE to fork into daemon mode */
101
102   char *pidfile;
103 };
104
105 static const char*
106 element_type_to_name (ElementType type)
107 {
108   switch (type)
109     {
110     case ELEMENT_NONE:
111       return NULL;
112     case ELEMENT_BUSCONFIG:
113       return "busconfig";
114     case ELEMENT_INCLUDE:
115       return "include";
116     case ELEMENT_USER:
117       return "user";
118     case ELEMENT_LISTEN:
119       return "listen";
120     case ELEMENT_AUTH:
121       return "auth";
122     case ELEMENT_POLICY:
123       return "policy";
124     case ELEMENT_LIMIT:
125       return "limit";
126     case ELEMENT_ALLOW:
127       return "allow";
128     case ELEMENT_DENY:
129       return "deny";
130     case ELEMENT_FORK:
131       return "fork";
132     case ELEMENT_PIDFILE:
133       return "pidfile";
134     case ELEMENT_SERVICEDIR:
135       return "servicedir";
136     case ELEMENT_INCLUDEDIR:
137       return "includedir";
138     case ELEMENT_TYPE:
139       return "type";
140     }
141
142   _dbus_assert_not_reached ("bad element type");
143
144   return NULL;
145 }
146
147 static Element*
148 push_element (BusConfigParser *parser,
149               ElementType      type)
150 {
151   Element *e;
152
153   _dbus_assert (type != ELEMENT_NONE);
154   
155   e = dbus_new0 (Element, 1);
156   if (e == NULL)
157     return NULL;
158
159   if (!_dbus_list_append (&parser->stack, e))
160     {
161       dbus_free (e);
162       return NULL;
163     }
164   
165   e->type = type;
166
167   return e;
168 }
169
170 static void
171 element_free (Element *e)
172 {
173
174   dbus_free (e);
175 }
176
177 static void
178 pop_element (BusConfigParser *parser)
179 {
180   Element *e;
181
182   e = _dbus_list_pop_last (&parser->stack);
183   
184   element_free (e);
185 }
186
187 static Element*
188 peek_element (BusConfigParser *parser)
189 {
190   Element *e;
191
192   e = _dbus_list_get_last (&parser->stack);
193
194   return e;
195 }
196
197 static ElementType
198 top_element_type (BusConfigParser *parser)
199 {
200   Element *e;
201
202   e = _dbus_list_get_last (&parser->stack);
203
204   if (e)
205     return e->type;
206   else
207     return ELEMENT_NONE;
208 }
209
210 static dbus_bool_t
211 merge_included (BusConfigParser *parser,
212                 BusConfigParser *included,
213                 DBusError       *error)
214 {
215   DBusList *link;
216
217   if (included->user != NULL)
218     {
219       dbus_free (parser->user);
220       parser->user = included->user;
221       included->user = NULL;
222     }
223
224   if (included->bus_type != NULL)
225     {
226       dbus_free (parser->bus_type);
227       parser->bus_type = included->bus_type;
228       included->bus_type = NULL;
229     }
230   
231   if (included->fork)
232     parser->fork = TRUE;
233
234   if (included->pidfile != NULL)
235     {
236       dbus_free (parser->pidfile);
237       parser->pidfile = included->pidfile;
238       included->pidfile = NULL;
239     }
240   
241   while ((link = _dbus_list_pop_first_link (&included->listen_on)))
242     _dbus_list_append_link (&parser->listen_on, link);
243
244   while ((link = _dbus_list_pop_first_link (&included->mechanisms)))
245     _dbus_list_append_link (&parser->mechanisms, link);
246
247   while ((link = _dbus_list_pop_first_link (&included->service_dirs)))
248     _dbus_list_append_link (&parser->service_dirs, link);
249   
250   return TRUE;
251 }
252
253 BusConfigParser*
254 bus_config_parser_new (const DBusString *basedir)
255 {
256   BusConfigParser *parser;
257
258   parser = dbus_new0 (BusConfigParser, 1);
259   if (parser == NULL)
260     return NULL;
261
262   if (!_dbus_string_init (&parser->basedir))
263     {
264       dbus_free (parser);
265       return NULL;
266     }
267
268   if (((parser->policy = bus_policy_new ()) == NULL) ||
269       !_dbus_string_copy (basedir, 0, &parser->basedir, 0))
270     {
271       _dbus_string_free (&parser->basedir);
272       dbus_free (parser);
273       return NULL;
274     }
275   
276   parser->refcount = 1;
277
278   return parser;
279 }
280
281 void
282 bus_config_parser_ref (BusConfigParser *parser)
283 {
284   _dbus_assert (parser->refcount > 0);
285
286   parser->refcount += 1;
287 }
288
289 void
290 bus_config_parser_unref (BusConfigParser *parser)
291 {
292   _dbus_assert (parser->refcount > 0);
293
294   parser->refcount -= 1;
295
296   if (parser->refcount == 0)
297     {
298       while (parser->stack != NULL)
299         pop_element (parser);
300
301       dbus_free (parser->user);
302       dbus_free (parser->bus_type);
303       dbus_free (parser->pidfile);
304       
305       _dbus_list_foreach (&parser->listen_on,
306                           (DBusForeachFunction) dbus_free,
307                           NULL);
308
309       _dbus_list_clear (&parser->listen_on);
310
311       _dbus_list_foreach (&parser->service_dirs,
312                           (DBusForeachFunction) dbus_free,
313                           NULL);
314
315       _dbus_list_clear (&parser->service_dirs);
316
317       _dbus_list_foreach (&parser->mechanisms,
318                           (DBusForeachFunction) dbus_free,
319                           NULL);
320
321       _dbus_list_clear (&parser->mechanisms);
322       
323       _dbus_string_free (&parser->basedir);
324
325       if (parser->policy)
326         bus_policy_unref (parser->policy);
327       
328       dbus_free (parser);
329     }
330 }
331
332 dbus_bool_t
333 bus_config_parser_check_doctype (BusConfigParser   *parser,
334                                  const char        *doctype,
335                                  DBusError         *error)
336 {
337   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
338
339   if (strcmp (doctype, "busconfig") != 0)
340     {
341       dbus_set_error (error,
342                       DBUS_ERROR_FAILED,
343                       "Configuration file has the wrong document type %s",
344                       doctype);
345       return FALSE;
346     }
347   else
348     return TRUE;
349 }
350
351 typedef struct
352 {
353   const char  *name;
354   const char **retloc;
355 } LocateAttr;
356
357 static dbus_bool_t
358 locate_attributes (BusConfigParser  *parser,
359                    const char       *element_name,
360                    const char      **attribute_names,
361                    const char      **attribute_values,
362                    DBusError        *error,
363                    const char       *first_attribute_name,
364                    const char      **first_attribute_retloc,
365                    ...)
366 {
367   va_list args;
368   const char *name;
369   const char **retloc;
370   int n_attrs;
371 #define MAX_ATTRS 24
372   LocateAttr attrs[MAX_ATTRS];
373   dbus_bool_t retval;
374   int i;
375
376   _dbus_assert (first_attribute_name != NULL);
377   _dbus_assert (first_attribute_retloc != NULL);
378
379   retval = TRUE;
380
381   n_attrs = 1;
382   attrs[0].name = first_attribute_name;
383   attrs[0].retloc = first_attribute_retloc;
384   *first_attribute_retloc = NULL;
385
386   va_start (args, first_attribute_retloc);
387
388   name = va_arg (args, const char*);
389   retloc = va_arg (args, const char**);
390
391   while (name != NULL)
392     {
393       _dbus_assert (retloc != NULL);
394       _dbus_assert (n_attrs < MAX_ATTRS);
395
396       attrs[n_attrs].name = name;
397       attrs[n_attrs].retloc = retloc;
398       n_attrs += 1;
399       *retloc = NULL;
400
401       name = va_arg (args, const char*);
402       retloc = va_arg (args, const char**);
403     }
404
405   va_end (args);
406
407   if (!retval)
408     return retval;
409
410   i = 0;
411   while (attribute_names[i])
412     {
413       int j;
414       dbus_bool_t found;
415       
416       found = FALSE;
417       j = 0;
418       while (j < n_attrs)
419         {
420           if (strcmp (attrs[j].name, attribute_names[i]) == 0)
421             {
422               retloc = attrs[j].retloc;
423
424               if (*retloc != NULL)
425                 {
426                   dbus_set_error (error, DBUS_ERROR_FAILED,
427                                   "Attribute \"%s\" repeated twice on the same <%s> element",
428                                   attrs[j].name, element_name);
429                   retval = FALSE;
430                   goto out;
431                 }
432
433               *retloc = attribute_values[i];
434               found = TRUE;
435             }
436
437           ++j;
438         }
439
440       if (!found)
441         {
442           dbus_set_error (error, DBUS_ERROR_FAILED,
443                           "Attribute \"%s\" is invalid on <%s> element in this context",
444                           attribute_names[i], element_name);
445           retval = FALSE;
446           goto out;
447         }
448
449       ++i;
450     }
451
452  out:
453   return retval;
454 }
455
456 static dbus_bool_t
457 check_no_attributes (BusConfigParser  *parser,
458                      const char       *element_name,
459                      const char      **attribute_names,
460                      const char      **attribute_values,
461                      DBusError        *error)
462 {
463   if (attribute_names[0] != NULL)
464     {
465       dbus_set_error (error, DBUS_ERROR_FAILED,
466                       "Attribute \"%s\" is invalid on <%s> element in this context",
467                       attribute_names[0], element_name);
468       return FALSE;
469     }
470
471   return TRUE;
472 }
473
474 static dbus_bool_t
475 start_busconfig_child (BusConfigParser   *parser,
476                        const char        *element_name,
477                        const char       **attribute_names,
478                        const char       **attribute_values,
479                        DBusError         *error)
480 {
481   if (strcmp (element_name, "user") == 0)
482     {
483       if (!check_no_attributes (parser, "user", attribute_names, attribute_values, error))
484         return FALSE;
485
486       if (push_element (parser, ELEMENT_USER) == NULL)
487         {
488           BUS_SET_OOM (error);
489           return FALSE;
490         }
491
492       return TRUE;
493     }
494   else if (strcmp (element_name, "type") == 0)
495     {
496       if (!check_no_attributes (parser, "type", attribute_names, attribute_values, error))
497         return FALSE;
498
499       if (push_element (parser, ELEMENT_TYPE) == NULL)
500         {
501           BUS_SET_OOM (error);
502           return FALSE;
503         }
504
505       return TRUE;
506     }
507   else if (strcmp (element_name, "fork") == 0)
508     {
509       if (!check_no_attributes (parser, "fork", attribute_names, attribute_values, error))
510         return FALSE;
511
512       if (push_element (parser, ELEMENT_FORK) == NULL)
513         {
514           BUS_SET_OOM (error);
515           return FALSE;
516         }
517
518       parser->fork = TRUE;
519       
520       return TRUE;
521     }
522   else if (strcmp (element_name, "pidfile") == 0)
523     {
524       if (!check_no_attributes (parser, "pidfile", attribute_names, attribute_values, error))
525         return FALSE;
526
527       if (push_element (parser, ELEMENT_PIDFILE) == NULL)
528         {
529           BUS_SET_OOM (error);
530           return FALSE;
531         }
532
533       return TRUE;
534     }
535   else if (strcmp (element_name, "listen") == 0)
536     {
537       if (!check_no_attributes (parser, "listen", attribute_names, attribute_values, error))
538         return FALSE;
539
540       if (push_element (parser, ELEMENT_LISTEN) == NULL)
541         {
542           BUS_SET_OOM (error);
543           return FALSE;
544         }
545
546       return TRUE;
547     }
548   else if (strcmp (element_name, "auth") == 0)
549     {
550       if (!check_no_attributes (parser, "auth", attribute_names, attribute_values, error))
551         return FALSE;
552
553       if (push_element (parser, ELEMENT_AUTH) == NULL)
554         {
555           BUS_SET_OOM (error);
556           return FALSE;
557         }
558
559       return TRUE;
560     }
561   else if (strcmp (element_name, "includedir") == 0)
562     {
563       if (!check_no_attributes (parser, "includedir", attribute_names, attribute_values, error))
564         return FALSE;
565
566       if (push_element (parser, ELEMENT_INCLUDEDIR) == NULL)
567         {
568           BUS_SET_OOM (error);
569           return FALSE;
570         }
571
572       return TRUE;
573     }
574   else if (strcmp (element_name, "servicedir") == 0)
575     {
576       if (!check_no_attributes (parser, "servicedir", attribute_names, attribute_values, error))
577         return FALSE;
578
579       if (push_element (parser, ELEMENT_SERVICEDIR) == NULL)
580         {
581           BUS_SET_OOM (error);
582           return FALSE;
583         }
584
585       return TRUE;
586     }
587   else if (strcmp (element_name, "include") == 0)
588     {
589       Element *e;
590       const char *ignore_missing;
591
592       if ((e = push_element (parser, ELEMENT_INCLUDE)) == NULL)
593         {
594           BUS_SET_OOM (error);
595           return FALSE;
596         }
597
598       e->d.include.ignore_missing = FALSE;
599
600       if (!locate_attributes (parser, "include",
601                               attribute_names,
602                               attribute_values,
603                               error,
604                               "ignore_missing", &ignore_missing,
605                               NULL))
606         return FALSE;
607
608       if (ignore_missing != NULL)
609         {
610           if (strcmp (ignore_missing, "yes") == 0)
611             e->d.include.ignore_missing = TRUE;
612           else if (strcmp (ignore_missing, "no") == 0)
613             e->d.include.ignore_missing = FALSE;
614           else
615             {
616               dbus_set_error (error, DBUS_ERROR_FAILED,
617                               "ignore_missing attribute must have value \"yes\" or \"no\"");
618               return FALSE;
619             }
620         }
621
622       return TRUE;
623     }
624   else if (strcmp (element_name, "policy") == 0)
625     {
626       Element *e;
627       const char *context;
628       const char *user;
629       const char *group;
630
631       if ((e = push_element (parser, ELEMENT_POLICY)) == NULL)
632         {
633           BUS_SET_OOM (error);
634           return FALSE;
635         }
636
637       if (!locate_attributes (parser, "policy",
638                               attribute_names,
639                               attribute_values,
640                               error,
641                               "context", &context,
642                               "user", &user,
643                               "group", &group,
644                               NULL))
645         return FALSE;
646
647       if (((context && user) ||
648            (context && group)) ||
649           (user && group) ||
650           !(context || user || group))
651         {
652           dbus_set_error (error, DBUS_ERROR_FAILED,
653                           "<policy> element must have exactly one of (context|user|group) attributes");
654           return FALSE;
655         }
656
657       if (context != NULL)
658         {
659           if (strcmp (context, "default") == 0)
660             {
661
662             }
663           else if (strcmp (context, "mandatory") == 0)
664             {
665
666             }
667           else
668             {
669               dbus_set_error (error, DBUS_ERROR_FAILED,
670                               "context attribute on <policy> must have the value \"default\" or \"mandatory\", not \"%s\"",
671                               context);
672               return FALSE;
673             }
674           
675           /* FIXME */
676
677         }
678       else if (user != NULL)
679         {
680           /* FIXME */
681
682         }
683       else if (group != NULL)
684         {
685           /* FIXME */
686
687         }
688       else
689         {
690           _dbus_assert_not_reached ("all <policy> attributes null and we didn't set error");
691         }
692       
693       return TRUE;
694     }
695   else
696     {
697       dbus_set_error (error, DBUS_ERROR_FAILED,
698                       "Element <%s> not allowed inside <%s> in configuration file",
699                       element_name, "busconfig");
700       return FALSE;
701     }
702 }
703
704 static dbus_bool_t
705 start_policy_child (BusConfigParser   *parser,
706                     const char        *element_name,
707                     const char       **attribute_names,
708                     const char       **attribute_values,
709                     DBusError         *error)
710 {
711   if (strcmp (element_name, "allow") == 0)
712     {
713       if (push_element (parser, ELEMENT_ALLOW) == NULL)
714         {
715           BUS_SET_OOM (error);
716           return FALSE;
717         }
718       
719       return TRUE;
720     }
721   else if (strcmp (element_name, "deny") == 0)
722     {
723       if (push_element (parser, ELEMENT_DENY) == NULL)
724         {
725           BUS_SET_OOM (error);
726           return FALSE;
727         }
728       
729       return TRUE;
730     }
731   else
732     {
733       dbus_set_error (error, DBUS_ERROR_FAILED,
734                       "Element <%s> not allowed inside <%s> in configuration file",
735                       element_name, "policy");
736       return FALSE;
737     }
738 }
739
740 dbus_bool_t
741 bus_config_parser_start_element (BusConfigParser   *parser,
742                                  const char        *element_name,
743                                  const char       **attribute_names,
744                                  const char       **attribute_values,
745                                  DBusError         *error)
746 {
747   ElementType t;
748
749   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
750
751   /* printf ("START: %s\n", element_name); */
752   
753   t = top_element_type (parser);
754
755   if (t == ELEMENT_NONE)
756     {
757       if (strcmp (element_name, "busconfig") == 0)
758         {
759           if (!check_no_attributes (parser, "busconfig", attribute_names, attribute_values, error))
760             return FALSE;
761           
762           if (push_element (parser, ELEMENT_BUSCONFIG) == NULL)
763             {
764               BUS_SET_OOM (error);
765               return FALSE;
766             }
767
768           return TRUE;
769         }
770       else
771         {
772           dbus_set_error (error, DBUS_ERROR_FAILED,
773                           "Unknown element <%s> at root of configuration file",
774                           element_name);
775           return FALSE;
776         }
777     }
778   else if (t == ELEMENT_BUSCONFIG)
779     {
780       return start_busconfig_child (parser, element_name,
781                                     attribute_names, attribute_values,
782                                     error);
783     }
784   else if (t == ELEMENT_POLICY)
785     {
786       return start_policy_child (parser, element_name,
787                                  attribute_names, attribute_values,
788                                  error);
789     }
790   else
791     {
792       dbus_set_error (error, DBUS_ERROR_FAILED,
793                       "Element <%s> is not allowed in this context",
794                       element_name);
795       return FALSE;
796     }  
797 }
798
799 dbus_bool_t
800 bus_config_parser_end_element (BusConfigParser   *parser,
801                                const char        *element_name,
802                                DBusError         *error)
803 {
804   ElementType t;
805   const char *n;
806   Element *e;
807
808   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
809
810   /* printf ("END: %s\n", element_name); */
811   
812   t = top_element_type (parser);
813
814   if (t == ELEMENT_NONE)
815     {
816       /* should probably be an assertion failure but
817        * being paranoid about XML parsers
818        */
819       dbus_set_error (error, DBUS_ERROR_FAILED,
820                       "XML parser ended element with no element on the stack");
821       return FALSE;
822     }
823
824   n = element_type_to_name (t);
825   _dbus_assert (n != NULL);
826   if (strcmp (n, element_name) != 0)
827     {
828       /* should probably be an assertion failure but
829        * being paranoid about XML parsers
830        */
831       dbus_set_error (error, DBUS_ERROR_FAILED,
832                       "XML element <%s> ended but topmost element on the stack was <%s>",
833                       element_name, n);
834       return FALSE;
835     }
836
837   e = peek_element (parser);
838   _dbus_assert (e != NULL);
839
840   switch (e->type)
841     {
842     case ELEMENT_NONE:
843       _dbus_assert_not_reached ("element in stack has no type");
844       break;
845
846     case ELEMENT_INCLUDE:
847     case ELEMENT_USER:
848     case ELEMENT_TYPE:
849     case ELEMENT_LISTEN:
850     case ELEMENT_PIDFILE:
851     case ELEMENT_AUTH:
852     case ELEMENT_SERVICEDIR:
853     case ELEMENT_INCLUDEDIR:
854       if (!e->had_content)
855         {
856           dbus_set_error (error, DBUS_ERROR_FAILED,
857                           "XML element <%s> was expected to have content inside it",
858                           element_type_to_name (e->type));
859           return FALSE;
860         }
861       break;
862
863     case ELEMENT_BUSCONFIG:
864     case ELEMENT_POLICY:
865     case ELEMENT_LIMIT:
866     case ELEMENT_ALLOW:
867     case ELEMENT_DENY:
868     case ELEMENT_FORK:
869       break;
870     }
871
872   pop_element (parser);
873
874   return TRUE;
875 }
876
877 static dbus_bool_t
878 all_whitespace (const DBusString *str)
879 {
880   int i;
881
882   _dbus_string_skip_white (str, 0, &i);
883
884   return i == _dbus_string_get_length (str);
885 }
886
887 static dbus_bool_t
888 make_full_path (const DBusString *basedir,
889                 const DBusString *filename,
890                 DBusString       *full_path)
891 {
892   if (_dbus_path_is_absolute (filename))
893     {
894       return _dbus_string_copy (filename, 0, full_path, 0);
895     }
896   else
897     {
898       if (!_dbus_string_copy (basedir, 0, full_path, 0))
899         return FALSE;
900       
901       if (!_dbus_concat_dir_and_file (full_path, filename))
902         return FALSE;
903
904       return TRUE;
905     }
906 }
907
908 static dbus_bool_t
909 include_file (BusConfigParser   *parser,
910               const DBusString  *filename,
911               dbus_bool_t        ignore_missing,
912               DBusError         *error)
913 {
914   /* FIXME good test case for this would load each config file in the
915    * test suite both alone, and as an include, and check
916    * that the result is the same
917    */
918   BusConfigParser *included;
919   DBusError tmp_error;
920         
921   dbus_error_init (&tmp_error);
922   included = bus_config_load (filename, &tmp_error);
923   if (included == NULL)
924     {
925       _DBUS_ASSERT_ERROR_IS_SET (&tmp_error);
926
927       if (dbus_error_has_name (&tmp_error, DBUS_ERROR_FILE_NOT_FOUND) &&
928           ignore_missing)
929         {
930           dbus_error_free (&tmp_error);
931           return TRUE;
932         }
933       else
934         {
935           dbus_move_error (&tmp_error, error);
936           return FALSE;
937         }
938     }
939   else
940     {
941       _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error);
942
943       if (!merge_included (parser, included, error))
944         {
945           bus_config_parser_unref (included);
946           return FALSE;
947         }
948
949       bus_config_parser_unref (included);
950       return TRUE;
951     }
952 }
953
954 static dbus_bool_t
955 include_dir (BusConfigParser   *parser,
956              const DBusString  *dirname,
957              DBusError         *error)
958 {
959   DBusString filename;
960   dbus_bool_t retval;
961   DBusError tmp_error;
962   DBusDirIter *dir;
963   
964   if (!_dbus_string_init (&filename))
965     {
966       BUS_SET_OOM (error);
967       return FALSE;
968     }
969
970   retval = FALSE;
971   
972   dir = _dbus_directory_open (dirname, error);
973
974   if (dir == NULL)
975     goto failed;
976
977   dbus_error_init (&tmp_error);
978   while (_dbus_directory_get_next_file (dir, &filename, &tmp_error))
979     {
980       DBusString full_path;
981
982       if (!_dbus_string_init (&full_path))
983         {
984           BUS_SET_OOM (error);
985           goto failed;
986         }
987
988       if (!_dbus_string_copy (dirname, 0, &full_path, 0))
989         {
990           BUS_SET_OOM (error);
991           _dbus_string_free (&full_path);
992           goto failed;
993         }      
994
995       if (!_dbus_concat_dir_and_file (&full_path, &filename))
996         {
997           BUS_SET_OOM (error);
998           _dbus_string_free (&full_path);
999           goto failed;
1000         }
1001       
1002       if (_dbus_string_ends_with_c_str (&full_path, ".conf"))
1003         {
1004           if (!include_file (parser, &full_path, TRUE, error))
1005             {
1006               _dbus_string_free (&full_path);
1007               goto failed;
1008             }
1009         }
1010
1011       _dbus_string_free (&full_path);
1012     }
1013
1014   if (dbus_error_is_set (&tmp_error))
1015     {
1016       dbus_move_error (&tmp_error, error);
1017       goto failed;
1018     }
1019   
1020   retval = TRUE;
1021   
1022  failed:
1023   _dbus_string_free (&filename);
1024   
1025   if (dir)
1026     _dbus_directory_close (dir);
1027
1028   return retval;
1029 }
1030
1031 dbus_bool_t
1032 bus_config_parser_content (BusConfigParser   *parser,
1033                            const DBusString  *content,
1034                            DBusError         *error)
1035 {
1036   Element *e;
1037
1038   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
1039
1040 #if 0
1041   {
1042     const char *c_str;
1043     
1044     _dbus_string_get_const_data (content, &c_str);
1045
1046     printf ("CONTENT %d bytes: %s\n", _dbus_string_get_length (content), c_str);
1047   }
1048 #endif
1049   
1050   e = peek_element (parser);
1051   if (e == NULL)
1052     {
1053       dbus_set_error (error, DBUS_ERROR_FAILED,
1054                       "Text content outside of any XML element in configuration file");
1055       return FALSE;
1056     }
1057   else if (e->had_content)
1058     {
1059       _dbus_assert_not_reached ("Element had multiple content blocks");
1060       return FALSE;
1061     }
1062
1063   switch (top_element_type (parser))
1064     {
1065     case ELEMENT_NONE:
1066       _dbus_assert_not_reached ("element at top of stack has no type");
1067       return FALSE;
1068
1069     case ELEMENT_BUSCONFIG:
1070     case ELEMENT_POLICY:
1071     case ELEMENT_LIMIT:
1072     case ELEMENT_ALLOW:
1073     case ELEMENT_DENY:
1074     case ELEMENT_FORK:
1075       if (all_whitespace (content))
1076         return TRUE;
1077       else
1078         {
1079           dbus_set_error (error, DBUS_ERROR_FAILED,
1080                           "No text content expected inside XML element %s in configuration file",
1081                           element_type_to_name (top_element_type (parser)));
1082           return FALSE;
1083         }
1084
1085     case ELEMENT_PIDFILE:
1086       {
1087         char *s;
1088
1089         e->had_content = TRUE;
1090         
1091         if (!_dbus_string_copy_data (content, &s))
1092           goto nomem;
1093           
1094         dbus_free (parser->pidfile);
1095         parser->pidfile = s;
1096       }
1097       break;
1098
1099     case ELEMENT_INCLUDE:
1100       {
1101         DBusString full_path;
1102         
1103         e->had_content = TRUE;
1104
1105         if (!_dbus_string_init (&full_path))
1106           goto nomem;
1107         
1108         if (!make_full_path (&parser->basedir, content, &full_path))
1109           {
1110             _dbus_string_free (&full_path);
1111             goto nomem;
1112           }
1113         
1114         if (!include_file (parser, &full_path,
1115                            e->d.include.ignore_missing, error))
1116           {
1117             _dbus_string_free (&full_path);
1118             return FALSE;
1119           }
1120
1121         _dbus_string_free (&full_path);
1122       }
1123       break;
1124
1125     case ELEMENT_INCLUDEDIR:
1126       {
1127         DBusString full_path;
1128         
1129         e->had_content = TRUE;
1130
1131         if (!_dbus_string_init (&full_path))
1132           goto nomem;
1133         
1134         if (!make_full_path (&parser->basedir, content, &full_path))
1135           {
1136             _dbus_string_free (&full_path);
1137             goto nomem;
1138           }
1139         
1140         if (!include_dir (parser, &full_path, error))
1141           {
1142             _dbus_string_free (&full_path);
1143             return FALSE;
1144           }
1145
1146         _dbus_string_free (&full_path);
1147       }
1148       break;
1149       
1150     case ELEMENT_USER:
1151       {
1152         char *s;
1153
1154         e->had_content = TRUE;
1155         
1156         if (!_dbus_string_copy_data (content, &s))
1157           goto nomem;
1158           
1159         dbus_free (parser->user);
1160         parser->user = s;
1161       }
1162       break;
1163
1164     case ELEMENT_TYPE:
1165       {
1166         char *s;
1167
1168         e->had_content = TRUE;
1169
1170         if (!_dbus_string_copy_data (content, &s))
1171           goto nomem;
1172         
1173         dbus_free (parser->bus_type);
1174         parser->bus_type = s;
1175       }
1176       break;
1177       
1178     case ELEMENT_LISTEN:
1179       {
1180         char *s;
1181
1182         e->had_content = TRUE;
1183         
1184         if (!_dbus_string_copy_data (content, &s))
1185           goto nomem;
1186
1187         if (!_dbus_list_append (&parser->listen_on,
1188                                 s))
1189           {
1190             dbus_free (s);
1191             goto nomem;
1192           }
1193       }
1194       break;
1195
1196     case ELEMENT_AUTH:
1197       {
1198         char *s;
1199         
1200         e->had_content = TRUE;
1201
1202         if (!_dbus_string_copy_data (content, &s))
1203           goto nomem;
1204
1205         if (!_dbus_list_append (&parser->mechanisms,
1206                                 s))
1207           {
1208             dbus_free (s);
1209             goto nomem;
1210           }
1211       }
1212       break;
1213
1214     case ELEMENT_SERVICEDIR:
1215       {
1216         char *s;
1217         DBusString full_path;
1218         
1219         e->had_content = TRUE;
1220
1221         if (!_dbus_string_init (&full_path))
1222           goto nomem;
1223         
1224         if (!make_full_path (&parser->basedir, content, &full_path))
1225           {
1226             _dbus_string_free (&full_path);
1227             goto nomem;
1228           }
1229         
1230         if (!_dbus_string_copy_data (&full_path, &s))
1231           {
1232             _dbus_string_free (&full_path);
1233             goto nomem;
1234           }
1235
1236         if (!_dbus_list_append (&parser->service_dirs, s))
1237           {
1238             _dbus_string_free (&full_path);
1239             dbus_free (s);
1240             goto nomem;
1241           }
1242
1243         _dbus_string_free (&full_path);
1244       }
1245       break;
1246     }
1247
1248   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
1249   return TRUE;
1250
1251  nomem:
1252   BUS_SET_OOM (error);
1253   return FALSE;
1254 }
1255
1256 dbus_bool_t
1257 bus_config_parser_finished (BusConfigParser   *parser,
1258                             DBusError         *error)
1259 {
1260   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
1261
1262   if (parser->stack != NULL)
1263     {
1264       dbus_set_error (error, DBUS_ERROR_FAILED,
1265                       "Element <%s> was not closed in configuration file",
1266                       element_type_to_name (top_element_type (parser)));
1267
1268       return FALSE;
1269     }
1270
1271   if (parser->listen_on == NULL)
1272     {
1273       dbus_set_error (error, DBUS_ERROR_FAILED,
1274                       "Configuration file needs one or more <listen> elements giving addresses"); 
1275       return FALSE;
1276     }
1277   
1278   return TRUE;
1279 }
1280
1281 const char*
1282 bus_config_parser_get_user (BusConfigParser *parser)
1283 {
1284   return parser->user;
1285 }
1286
1287 const char*
1288 bus_config_parser_get_type (BusConfigParser *parser)
1289 {
1290   return parser->bus_type;
1291 }
1292
1293 DBusList**
1294 bus_config_parser_get_addresses (BusConfigParser *parser)
1295 {
1296   return &parser->listen_on;
1297 }
1298
1299 DBusList**
1300 bus_config_parser_get_mechanisms (BusConfigParser *parser)
1301 {
1302   return &parser->mechanisms;
1303 }
1304
1305 DBusList**
1306 bus_config_parser_get_service_dirs (BusConfigParser *parser)
1307 {
1308   return &parser->service_dirs;
1309 }
1310
1311 dbus_bool_t
1312 bus_config_parser_get_fork (BusConfigParser   *parser)
1313 {
1314   return parser->fork;
1315 }
1316
1317 const char *
1318 bus_config_parser_get_pidfile (BusConfigParser   *parser)
1319 {
1320   return parser->pidfile;
1321 }
1322
1323 BusPolicy*
1324 bus_config_parser_steal_policy (BusConfigParser *parser)
1325 {
1326   BusPolicy *policy;
1327
1328   _dbus_assert (parser->policy != NULL); /* can only steal the policy 1 time */
1329   
1330   policy = parser->policy;
1331
1332   parser->policy = NULL;
1333
1334   return policy;
1335 }
1336
1337 #ifdef DBUS_BUILD_TESTS
1338 #include <stdio.h>
1339
1340 typedef enum
1341 {
1342   VALID,
1343   INVALID,
1344   UNKNOWN
1345 } Validity;
1346
1347 static dbus_bool_t
1348 do_load (const DBusString *full_path,
1349          Validity          validity,
1350          dbus_bool_t       oom_possible)
1351 {
1352   BusConfigParser *parser;
1353   DBusError error;
1354
1355   dbus_error_init (&error);
1356
1357   parser = bus_config_load (full_path, &error);
1358   if (parser == NULL)
1359     {
1360       _DBUS_ASSERT_ERROR_IS_SET (&error);
1361
1362       if (oom_possible &&
1363           dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
1364         {
1365           _dbus_verbose ("Failed to load valid file due to OOM\n");
1366           dbus_error_free (&error);
1367           return TRUE;
1368         }
1369       else if (validity == VALID)
1370         {
1371           _dbus_warn ("Failed to load valid file but still had memory: %s\n",
1372                       error.message);
1373
1374           dbus_error_free (&error);
1375           return FALSE;
1376         }
1377       else
1378         {
1379           dbus_error_free (&error);
1380           return TRUE;
1381         }
1382     }
1383   else
1384     {
1385       _DBUS_ASSERT_ERROR_IS_CLEAR (&error);
1386
1387       bus_config_parser_unref (parser);
1388
1389       if (validity == INVALID)
1390         {
1391           _dbus_warn ("Accepted invalid file\n");
1392           return FALSE;
1393         }
1394
1395       return TRUE;
1396     }
1397 }
1398
1399 typedef struct
1400 {
1401   const DBusString *full_path;
1402   Validity          validity;
1403 } LoaderOomData;
1404
1405 static dbus_bool_t
1406 check_loader_oom_func (void *data)
1407 {
1408   LoaderOomData *d = data;
1409
1410   return do_load (d->full_path, d->validity, TRUE);
1411 }
1412
1413 static dbus_bool_t
1414 process_test_subdir (const DBusString *test_base_dir,
1415                      const char       *subdir,
1416                      Validity          validity)
1417 {
1418   DBusString test_directory;
1419   DBusString filename;
1420   DBusDirIter *dir;
1421   dbus_bool_t retval;
1422   DBusError error;
1423
1424   retval = FALSE;
1425   dir = NULL;
1426
1427   if (!_dbus_string_init (&test_directory))
1428     _dbus_assert_not_reached ("didn't allocate test_directory\n");
1429
1430   _dbus_string_init_const (&filename, subdir);
1431
1432   if (!_dbus_string_copy (test_base_dir, 0,
1433                           &test_directory, 0))
1434     _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory");
1435
1436   if (!_dbus_concat_dir_and_file (&test_directory, &filename))
1437     _dbus_assert_not_reached ("couldn't allocate full path");
1438
1439   _dbus_string_free (&filename);
1440   if (!_dbus_string_init (&filename))
1441     _dbus_assert_not_reached ("didn't allocate filename string\n");
1442
1443   dbus_error_init (&error);
1444   dir = _dbus_directory_open (&test_directory, &error);
1445   if (dir == NULL)
1446     {
1447       _dbus_warn ("Could not open %s: %s\n",
1448                   _dbus_string_get_const_data (&test_directory),
1449                   error.message);
1450       dbus_error_free (&error);
1451       goto failed;
1452     }
1453
1454   printf ("Testing:\n");
1455
1456  next:
1457   while (_dbus_directory_get_next_file (dir, &filename, &error))
1458     {
1459       DBusString full_path;
1460       LoaderOomData d;
1461
1462       if (!_dbus_string_init (&full_path))
1463         _dbus_assert_not_reached ("couldn't init string");
1464
1465       if (!_dbus_string_copy (&test_directory, 0, &full_path, 0))
1466         _dbus_assert_not_reached ("couldn't copy dir to full_path");
1467
1468       if (!_dbus_concat_dir_and_file (&full_path, &filename))
1469         _dbus_assert_not_reached ("couldn't concat file to dir");
1470
1471       if (!_dbus_string_ends_with_c_str (&full_path, ".conf"))
1472         {
1473           _dbus_verbose ("Skipping non-.conf file %s\n",
1474                          _dbus_string_get_const_data (&filename));
1475           _dbus_string_free (&full_path);
1476           goto next;
1477         }
1478
1479       printf ("    %s\n", _dbus_string_get_const_data (&filename));
1480
1481       _dbus_verbose (" expecting %s\n",
1482                      validity == VALID ? "valid" :
1483                      (validity == INVALID ? "invalid" :
1484                       (validity == UNKNOWN ? "unknown" : "???")));
1485
1486       d.full_path = &full_path;
1487       d.validity = validity;
1488       if (!_dbus_test_oom_handling ("config-loader", check_loader_oom_func, &d))
1489         _dbus_assert_not_reached ("test failed");
1490
1491       _dbus_string_free (&full_path);
1492     }
1493
1494   if (dbus_error_is_set (&error))
1495     {
1496       _dbus_warn ("Could not get next file in %s: %s\n",
1497                   _dbus_string_get_const_data (&test_directory),
1498                   error.message);
1499       dbus_error_free (&error);
1500       goto failed;
1501     }
1502
1503   retval = TRUE;
1504
1505  failed:
1506
1507   if (dir)
1508     _dbus_directory_close (dir);
1509   _dbus_string_free (&test_directory);
1510   _dbus_string_free (&filename);
1511
1512   return retval;
1513 }
1514
1515 dbus_bool_t
1516 bus_config_parser_test (const DBusString *test_data_dir)
1517 {
1518   if (test_data_dir == NULL ||
1519       _dbus_string_get_length (test_data_dir) == 0)
1520     {
1521       printf ("No test data\n");
1522       return TRUE;
1523     }
1524
1525   if (!process_test_subdir (test_data_dir, "valid-config-files", VALID))
1526     return FALSE;
1527
1528   return TRUE;
1529 }
1530
1531 #endif /* DBUS_BUILD_TESTS */
1532