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