2004-04-12 Jon Trowbridge <trow@ximian.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 2.0
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     struct
82     {
83       char *name;
84       long value;
85     } limit;
86     
87   } d;
88
89 } Element;
90
91 /**
92  * Parser for bus configuration file. 
93  */
94 struct BusConfigParser
95 {
96   int refcount;        /**< Reference count */
97
98   DBusString basedir;  /**< Directory we resolve paths relative to */
99   
100   DBusList *stack;     /**< stack of Element */
101
102   char *user;          /**< user to run as */
103
104   char *bus_type;          /**< Message bus type */
105   
106   DBusList *listen_on; /**< List of addresses to listen to */
107
108   DBusList *mechanisms; /**< Auth mechanisms */
109
110   DBusList *service_dirs; /**< Directories to look for services in */
111
112   BusPolicy *policy;     /**< Security policy */
113
114   BusLimits limits;      /**< Limits */
115
116   char *pidfile;         /**< PID file */
117
118   DBusList *included_files;  /**< Included files stack */
119
120   unsigned int fork : 1; /**< TRUE to fork into daemon mode */
121
122   unsigned int is_toplevel : 1; /**< FALSE if we are a sub-config-file inside another one */
123 };
124
125 static const char*
126 element_type_to_name (ElementType type)
127 {
128   switch (type)
129     {
130     case ELEMENT_NONE:
131       return NULL;
132     case ELEMENT_BUSCONFIG:
133       return "busconfig";
134     case ELEMENT_INCLUDE:
135       return "include";
136     case ELEMENT_USER:
137       return "user";
138     case ELEMENT_LISTEN:
139       return "listen";
140     case ELEMENT_AUTH:
141       return "auth";
142     case ELEMENT_POLICY:
143       return "policy";
144     case ELEMENT_LIMIT:
145       return "limit";
146     case ELEMENT_ALLOW:
147       return "allow";
148     case ELEMENT_DENY:
149       return "deny";
150     case ELEMENT_FORK:
151       return "fork";
152     case ELEMENT_PIDFILE:
153       return "pidfile";
154     case ELEMENT_SERVICEDIR:
155       return "servicedir";
156     case ELEMENT_INCLUDEDIR:
157       return "includedir";
158     case ELEMENT_TYPE:
159       return "type";
160     }
161
162   _dbus_assert_not_reached ("bad element type");
163
164   return NULL;
165 }
166
167 static Element*
168 push_element (BusConfigParser *parser,
169               ElementType      type)
170 {
171   Element *e;
172
173   _dbus_assert (type != ELEMENT_NONE);
174   
175   e = dbus_new0 (Element, 1);
176   if (e == NULL)
177     return NULL;
178
179   if (!_dbus_list_append (&parser->stack, e))
180     {
181       dbus_free (e);
182       return NULL;
183     }
184   
185   e->type = type;
186
187   return e;
188 }
189
190 static void
191 element_free (Element *e)
192 {
193   if (e->type == ELEMENT_LIMIT)
194     dbus_free (e->d.limit.name);
195   
196   dbus_free (e);
197 }
198
199 static void
200 pop_element (BusConfigParser *parser)
201 {
202   Element *e;
203
204   e = _dbus_list_pop_last (&parser->stack);
205   
206   element_free (e);
207 }
208
209 static Element*
210 peek_element (BusConfigParser *parser)
211 {
212   Element *e;
213
214   e = _dbus_list_get_last (&parser->stack);
215
216   return e;
217 }
218
219 static ElementType
220 top_element_type (BusConfigParser *parser)
221 {
222   Element *e;
223
224   e = _dbus_list_get_last (&parser->stack);
225
226   if (e)
227     return e->type;
228   else
229     return ELEMENT_NONE;
230 }
231
232 static dbus_bool_t
233 merge_included (BusConfigParser *parser,
234                 BusConfigParser *included,
235                 DBusError       *error)
236 {
237   DBusList *link;
238
239   if (!bus_policy_merge (parser->policy,
240                          included->policy))
241     {
242       BUS_SET_OOM (error);
243       return FALSE;
244     }
245   
246   if (included->user != NULL)
247     {
248       dbus_free (parser->user);
249       parser->user = included->user;
250       included->user = NULL;
251     }
252
253   if (included->bus_type != NULL)
254     {
255       dbus_free (parser->bus_type);
256       parser->bus_type = included->bus_type;
257       included->bus_type = NULL;
258     }
259   
260   if (included->fork)
261     parser->fork = TRUE;
262
263   if (included->pidfile != NULL)
264     {
265       dbus_free (parser->pidfile);
266       parser->pidfile = included->pidfile;
267       included->pidfile = NULL;
268     }
269   
270   while ((link = _dbus_list_pop_first_link (&included->listen_on)))
271     _dbus_list_append_link (&parser->listen_on, link);
272
273   while ((link = _dbus_list_pop_first_link (&included->mechanisms)))
274     _dbus_list_append_link (&parser->mechanisms, link);
275
276   while ((link = _dbus_list_pop_first_link (&included->service_dirs)))
277     _dbus_list_append_link (&parser->service_dirs, link);
278   
279   return TRUE;
280 }
281
282 static dbus_bool_t
283 seen_include (BusConfigParser  *parser,
284               const DBusString *file)
285 {
286   DBusList *iter;
287
288   iter = parser->included_files;
289   while (iter != NULL)
290     {
291       if (! strcmp (_dbus_string_get_const_data (file), iter->data))
292         return TRUE;
293
294       iter = _dbus_list_get_next_link (&parser->included_files, iter);
295     }
296
297   return FALSE;
298 }
299
300 BusConfigParser*
301 bus_config_parser_new (const DBusString      *basedir,
302                        dbus_bool_t            is_toplevel,
303                        const BusConfigParser *parent)
304 {
305   BusConfigParser *parser;
306
307   parser = dbus_new0 (BusConfigParser, 1);
308   if (parser == NULL)
309     return NULL;
310
311   parser->is_toplevel = !!is_toplevel;
312   
313   if (!_dbus_string_init (&parser->basedir))
314     {
315       dbus_free (parser);
316       return NULL;
317     }
318
319   if (((parser->policy = bus_policy_new ()) == NULL) ||
320       !_dbus_string_copy (basedir, 0, &parser->basedir, 0))
321     {
322       if (parser->policy)
323         bus_policy_unref (parser->policy);
324       
325       _dbus_string_free (&parser->basedir);
326       dbus_free (parser);
327       return NULL;
328     }
329
330   if (parent != NULL)
331     {
332       /* Initialize the parser's limits from the parent. */
333       parser->limits = parent->limits;
334
335       /* Use the parent's list of included_files to avoid
336          circular inclusions. */
337       parser->included_files = parent->included_files;
338     }
339   else
340     {
341
342       /* Make up some numbers! woot! */
343       parser->limits.max_incoming_bytes = _DBUS_ONE_MEGABYTE * 63;
344       parser->limits.max_outgoing_bytes = _DBUS_ONE_MEGABYTE * 63;
345       parser->limits.max_message_size = _DBUS_ONE_MEGABYTE * 32;
346       
347       /* Making this long means the user has to wait longer for an error
348        * message if something screws up, but making it too short means
349        * they might see a false failure.
350        */
351       parser->limits.activation_timeout = 25000; /* 25 seconds */
352
353       /* Making this long risks making a DOS attack easier, but too short
354        * and legitimate auth will fail.  If interactive auth (ask user for
355        * password) is allowed, then potentially it has to be quite long.
356        */
357       parser->limits.auth_timeout = 30000; /* 30 seconds */
358       
359       parser->limits.max_incomplete_connections = 32;
360       parser->limits.max_connections_per_user = 128;
361       
362       /* Note that max_completed_connections / max_connections_per_user
363        * is the number of users that would have to work together to
364        * DOS all the other users.
365        */
366       parser->limits.max_completed_connections = 1024;
367       
368       parser->limits.max_pending_activations = 256;
369       parser->limits.max_services_per_connection = 256;
370       
371       parser->limits.max_match_rules_per_connection = 128;
372       
373       parser->limits.reply_timeout = 5 * 60 * 1000; /* 5 minutes */
374       parser->limits.max_replies_per_connection = 32;
375     }
376       
377   parser->refcount = 1;
378       
379   return parser;
380 }
381
382 BusConfigParser *
383 bus_config_parser_ref (BusConfigParser *parser)
384 {
385   _dbus_assert (parser->refcount > 0);
386
387   parser->refcount += 1;
388
389   return parser;
390 }
391
392 void
393 bus_config_parser_unref (BusConfigParser *parser)
394 {
395   _dbus_assert (parser->refcount > 0);
396
397   parser->refcount -= 1;
398
399   if (parser->refcount == 0)
400     {
401       while (parser->stack != NULL)
402         pop_element (parser);
403
404       dbus_free (parser->user);
405       dbus_free (parser->bus_type);
406       dbus_free (parser->pidfile);
407       
408       _dbus_list_foreach (&parser->listen_on,
409                           (DBusForeachFunction) dbus_free,
410                           NULL);
411
412       _dbus_list_clear (&parser->listen_on);
413
414       _dbus_list_foreach (&parser->service_dirs,
415                           (DBusForeachFunction) dbus_free,
416                           NULL);
417
418       _dbus_list_clear (&parser->service_dirs);
419
420       _dbus_list_foreach (&parser->mechanisms,
421                           (DBusForeachFunction) dbus_free,
422                           NULL);
423
424       _dbus_list_clear (&parser->mechanisms);
425       
426       _dbus_string_free (&parser->basedir);
427
428       if (parser->policy)
429         bus_policy_unref (parser->policy);
430
431       dbus_free (parser);
432     }
433 }
434
435 dbus_bool_t
436 bus_config_parser_check_doctype (BusConfigParser   *parser,
437                                  const char        *doctype,
438                                  DBusError         *error)
439 {
440   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
441
442   if (strcmp (doctype, "busconfig") != 0)
443     {
444       dbus_set_error (error,
445                       DBUS_ERROR_FAILED,
446                       "Configuration file has the wrong document type %s",
447                       doctype);
448       return FALSE;
449     }
450   else
451     return TRUE;
452 }
453
454 typedef struct
455 {
456   const char  *name;
457   const char **retloc;
458 } LocateAttr;
459
460 static dbus_bool_t
461 locate_attributes (BusConfigParser  *parser,
462                    const char       *element_name,
463                    const char      **attribute_names,
464                    const char      **attribute_values,
465                    DBusError        *error,
466                    const char       *first_attribute_name,
467                    const char      **first_attribute_retloc,
468                    ...)
469 {
470   va_list args;
471   const char *name;
472   const char **retloc;
473   int n_attrs;
474 #define MAX_ATTRS 24
475   LocateAttr attrs[MAX_ATTRS];
476   dbus_bool_t retval;
477   int i;
478
479   _dbus_assert (first_attribute_name != NULL);
480   _dbus_assert (first_attribute_retloc != NULL);
481
482   retval = TRUE;
483
484   n_attrs = 1;
485   attrs[0].name = first_attribute_name;
486   attrs[0].retloc = first_attribute_retloc;
487   *first_attribute_retloc = NULL;
488
489   va_start (args, first_attribute_retloc);
490
491   name = va_arg (args, const char*);
492   retloc = va_arg (args, const char**);
493
494   while (name != NULL)
495     {
496       _dbus_assert (retloc != NULL);
497       _dbus_assert (n_attrs < MAX_ATTRS);
498
499       attrs[n_attrs].name = name;
500       attrs[n_attrs].retloc = retloc;
501       n_attrs += 1;
502       *retloc = NULL;
503
504       name = va_arg (args, const char*);
505       retloc = va_arg (args, const char**);
506     }
507
508   va_end (args);
509
510   if (!retval)
511     return retval;
512
513   i = 0;
514   while (attribute_names[i])
515     {
516       int j;
517       dbus_bool_t found;
518       
519       found = FALSE;
520       j = 0;
521       while (j < n_attrs)
522         {
523           if (strcmp (attrs[j].name, attribute_names[i]) == 0)
524             {
525               retloc = attrs[j].retloc;
526
527               if (*retloc != NULL)
528                 {
529                   dbus_set_error (error, DBUS_ERROR_FAILED,
530                                   "Attribute \"%s\" repeated twice on the same <%s> element",
531                                   attrs[j].name, element_name);
532                   retval = FALSE;
533                   goto out;
534                 }
535
536               *retloc = attribute_values[i];
537               found = TRUE;
538             }
539
540           ++j;
541         }
542
543       if (!found)
544         {
545           dbus_set_error (error, DBUS_ERROR_FAILED,
546                           "Attribute \"%s\" is invalid on <%s> element in this context",
547                           attribute_names[i], element_name);
548           retval = FALSE;
549           goto out;
550         }
551
552       ++i;
553     }
554
555  out:
556   return retval;
557 }
558
559 static dbus_bool_t
560 check_no_attributes (BusConfigParser  *parser,
561                      const char       *element_name,
562                      const char      **attribute_names,
563                      const char      **attribute_values,
564                      DBusError        *error)
565 {
566   if (attribute_names[0] != NULL)
567     {
568       dbus_set_error (error, DBUS_ERROR_FAILED,
569                       "Attribute \"%s\" is invalid on <%s> element in this context",
570                       attribute_names[0], element_name);
571       return FALSE;
572     }
573
574   return TRUE;
575 }
576
577 static dbus_bool_t
578 start_busconfig_child (BusConfigParser   *parser,
579                        const char        *element_name,
580                        const char       **attribute_names,
581                        const char       **attribute_values,
582                        DBusError         *error)
583 {
584   if (strcmp (element_name, "user") == 0)
585     {
586       if (!check_no_attributes (parser, "user", attribute_names, attribute_values, error))
587         return FALSE;
588
589       if (push_element (parser, ELEMENT_USER) == NULL)
590         {
591           BUS_SET_OOM (error);
592           return FALSE;
593         }
594
595       return TRUE;
596     }
597   else if (strcmp (element_name, "type") == 0)
598     {
599       if (!check_no_attributes (parser, "type", attribute_names, attribute_values, error))
600         return FALSE;
601
602       if (push_element (parser, ELEMENT_TYPE) == NULL)
603         {
604           BUS_SET_OOM (error);
605           return FALSE;
606         }
607
608       return TRUE;
609     }
610   else if (strcmp (element_name, "fork") == 0)
611     {
612       if (!check_no_attributes (parser, "fork", attribute_names, attribute_values, error))
613         return FALSE;
614
615       if (push_element (parser, ELEMENT_FORK) == NULL)
616         {
617           BUS_SET_OOM (error);
618           return FALSE;
619         }
620
621       parser->fork = TRUE;
622       
623       return TRUE;
624     }
625   else if (strcmp (element_name, "pidfile") == 0)
626     {
627       if (!check_no_attributes (parser, "pidfile", attribute_names, attribute_values, error))
628         return FALSE;
629
630       if (push_element (parser, ELEMENT_PIDFILE) == NULL)
631         {
632           BUS_SET_OOM (error);
633           return FALSE;
634         }
635
636       return TRUE;
637     }
638   else if (strcmp (element_name, "listen") == 0)
639     {
640       if (!check_no_attributes (parser, "listen", attribute_names, attribute_values, error))
641         return FALSE;
642
643       if (push_element (parser, ELEMENT_LISTEN) == NULL)
644         {
645           BUS_SET_OOM (error);
646           return FALSE;
647         }
648
649       return TRUE;
650     }
651   else if (strcmp (element_name, "auth") == 0)
652     {
653       if (!check_no_attributes (parser, "auth", attribute_names, attribute_values, error))
654         return FALSE;
655
656       if (push_element (parser, ELEMENT_AUTH) == NULL)
657         {
658           BUS_SET_OOM (error);
659           return FALSE;
660         }
661
662       return TRUE;
663     }
664   else if (strcmp (element_name, "includedir") == 0)
665     {
666       if (!check_no_attributes (parser, "includedir", attribute_names, attribute_values, error))
667         return FALSE;
668
669       if (push_element (parser, ELEMENT_INCLUDEDIR) == NULL)
670         {
671           BUS_SET_OOM (error);
672           return FALSE;
673         }
674
675       return TRUE;
676     }
677   else if (strcmp (element_name, "servicedir") == 0)
678     {
679       if (!check_no_attributes (parser, "servicedir", attribute_names, attribute_values, error))
680         return FALSE;
681
682       if (push_element (parser, ELEMENT_SERVICEDIR) == NULL)
683         {
684           BUS_SET_OOM (error);
685           return FALSE;
686         }
687
688       return TRUE;
689     }
690   else if (strcmp (element_name, "include") == 0)
691     {
692       Element *e;
693       const char *ignore_missing;
694
695       if ((e = push_element (parser, ELEMENT_INCLUDE)) == NULL)
696         {
697           BUS_SET_OOM (error);
698           return FALSE;
699         }
700
701       e->d.include.ignore_missing = FALSE;
702
703       if (!locate_attributes (parser, "include",
704                               attribute_names,
705                               attribute_values,
706                               error,
707                               "ignore_missing", &ignore_missing,
708                               NULL))
709         return FALSE;
710
711       if (ignore_missing != NULL)
712         {
713           if (strcmp (ignore_missing, "yes") == 0)
714             e->d.include.ignore_missing = TRUE;
715           else if (strcmp (ignore_missing, "no") == 0)
716             e->d.include.ignore_missing = FALSE;
717           else
718             {
719               dbus_set_error (error, DBUS_ERROR_FAILED,
720                               "ignore_missing attribute must have value \"yes\" or \"no\"");
721               return FALSE;
722             }
723         }
724
725       return TRUE;
726     }
727   else if (strcmp (element_name, "policy") == 0)
728     {
729       Element *e;
730       const char *context;
731       const char *user;
732       const char *group;
733
734       if ((e = push_element (parser, ELEMENT_POLICY)) == NULL)
735         {
736           BUS_SET_OOM (error);
737           return FALSE;
738         }
739
740       e->d.policy.type = POLICY_IGNORED;
741       
742       if (!locate_attributes (parser, "policy",
743                               attribute_names,
744                               attribute_values,
745                               error,
746                               "context", &context,
747                               "user", &user,
748                               "group", &group,
749                               NULL))
750         return FALSE;
751
752       if (((context && user) ||
753            (context && group)) ||
754           (user && group) ||
755           !(context || user || group))
756         {
757           dbus_set_error (error, DBUS_ERROR_FAILED,
758                           "<policy> element must have exactly one of (context|user|group) attributes");
759           return FALSE;
760         }
761
762       if (context != NULL)
763         {
764           if (strcmp (context, "default") == 0)
765             {
766               e->d.policy.type = POLICY_DEFAULT;
767             }
768           else if (strcmp (context, "mandatory") == 0)
769             {
770               e->d.policy.type = POLICY_MANDATORY;
771             }
772           else
773             {
774               dbus_set_error (error, DBUS_ERROR_FAILED,
775                               "context attribute on <policy> must have the value \"default\" or \"mandatory\", not \"%s\"",
776                               context);
777               return FALSE;
778             }
779         }
780       else if (user != NULL)
781         {
782           DBusString username;
783           _dbus_string_init_const (&username, user);
784
785           if (_dbus_get_user_id (&username,
786                                  &e->d.policy.gid_or_uid))
787             e->d.policy.type = POLICY_USER;
788           else
789             _dbus_warn ("Unknown username \"%s\" in message bus configuration file\n",
790                         user);
791         }
792       else if (group != NULL)
793         {
794           DBusString group_name;
795           _dbus_string_init_const (&group_name, group);
796
797           if (_dbus_get_group_id (&group_name,
798                                   &e->d.policy.gid_or_uid))
799             e->d.policy.type = POLICY_GROUP;
800           else
801             _dbus_warn ("Unknown group \"%s\" in message bus configuration file\n",
802                         group);          
803         }
804       else
805         {
806           _dbus_assert_not_reached ("all <policy> attributes null and we didn't set error");
807         }
808       
809       return TRUE;
810     }
811   else if (strcmp (element_name, "limit") == 0)
812     {
813       Element *e;
814       const char *name;
815
816       if ((e = push_element (parser, ELEMENT_LIMIT)) == NULL)
817         {
818           BUS_SET_OOM (error);
819           return FALSE;
820         }
821       
822       if (!locate_attributes (parser, "limit",
823                               attribute_names,
824                               attribute_values,
825                               error,
826                               "name", &name,
827                               NULL))
828         return FALSE;
829
830       if (name == NULL)
831         {
832           dbus_set_error (error, DBUS_ERROR_FAILED,
833                           "<limit> element must have a \"name\" attribute");
834           return FALSE;
835         }
836
837       e->d.limit.name = _dbus_strdup (name);
838       if (e->d.limit.name == NULL)
839         {
840           BUS_SET_OOM (error);
841           return FALSE;
842         }
843
844       return TRUE;
845     }
846   else
847     {
848       dbus_set_error (error, DBUS_ERROR_FAILED,
849                       "Element <%s> not allowed inside <%s> in configuration file",
850                       element_name, "busconfig");
851       return FALSE;
852     }
853 }
854
855 static dbus_bool_t
856 append_rule_from_element (BusConfigParser   *parser,
857                           const char        *element_name,
858                           const char       **attribute_names,
859                           const char       **attribute_values,
860                           dbus_bool_t        allow,
861                           DBusError         *error)
862 {
863   const char *send_interface;
864   const char *send_member;
865   const char *send_error;
866   const char *send_destination;
867   const char *send_path;
868   const char *send_type;
869   const char *receive_interface;
870   const char *receive_member;
871   const char *receive_error;
872   const char *receive_sender;
873   const char *receive_path;
874   const char *receive_type;
875   const char *eavesdrop;
876   const char *requested_reply;
877   const char *own;
878   const char *user;
879   const char *group;
880   BusPolicyRule *rule;
881   
882   if (!locate_attributes (parser, element_name,
883                           attribute_names,
884                           attribute_values,
885                           error,
886                           "send_interface", &send_interface,
887                           "send_member", &send_member,
888                           "send_error", &send_error,
889                           "send_destination", &send_destination,
890                           "send_path", &send_path,
891                           "send_type", &send_type,
892                           "receive_interface", &receive_interface,
893                           "receive_member", &receive_member,
894                           "receive_error", &receive_error,
895                           "receive_sender", &receive_sender,
896                           "receive_path", &receive_path,
897                           "receive_type", &receive_type,
898                           "eavesdrop", &eavesdrop,
899                           "requested_reply", &requested_reply,
900                           "own", &own,
901                           "user", &user,
902                           "group", &group,
903                           NULL))
904     return FALSE;
905
906   if (!(send_interface || send_member || send_error || send_destination ||
907         send_type || send_path ||
908         receive_interface || receive_member || receive_error || receive_sender ||
909         receive_type || receive_path || eavesdrop || requested_reply ||
910         own || user || group))
911     {
912       dbus_set_error (error, DBUS_ERROR_FAILED,
913                       "Element <%s> must have one or more attributes",
914                       element_name);
915       return FALSE;
916     }
917
918   if ((send_member && (send_interface == NULL && send_path == NULL)) ||
919       (receive_member && (receive_interface == NULL && receive_path == NULL)))
920     {
921       dbus_set_error (error, DBUS_ERROR_FAILED,
922                       "On element <%s>, if you specify a member you must specify an interface or a path. Keep in mind that not all messages have an interface field.",
923                       element_name);
924       return FALSE;
925     }
926   
927   /* Allowed combinations of elements are:
928    *
929    *   base, must be all send or all receive:
930    *     nothing
931    *     interface
932    *     interface + member
933    *     error
934    * 
935    *   base send_ can combine with send_destination, send_path, send_type
936    *   base receive_ with receive_sender, receive_path, receive_type, eavesdrop, requested_reply
937    *
938    *   user, group, own must occur alone
939    *
940    * Pretty sure the below stuff is broken, FIXME think about it more.
941    */
942
943   if (((send_interface && send_error) ||
944        (send_interface && receive_interface) ||
945        (send_interface && receive_member) ||
946        (send_interface && receive_error) ||
947        (send_interface && receive_sender) ||
948        (send_interface && eavesdrop) ||
949        (send_interface && requested_reply) ||
950        (send_interface && own) ||
951        (send_interface && user) ||
952        (send_interface && group)) ||
953
954       ((send_member && send_error) ||
955        (send_member && receive_interface) ||
956        (send_member && receive_member) ||
957        (send_member && receive_error) ||
958        (send_member && receive_sender) ||
959        (send_member && eavesdrop) ||
960        (send_member && requested_reply) ||
961        (send_member && own) ||
962        (send_member && user) ||
963        (send_member && group)) ||
964       
965       ((send_error && receive_interface) ||
966        (send_error && receive_member) ||
967        (send_error && receive_error) ||
968        (send_error && receive_sender) ||
969        (send_error && eavesdrop) ||
970        (send_error && requested_reply) ||
971        (send_error && own) ||
972        (send_error && user) ||
973        (send_error && group)) ||
974
975       ((send_destination && receive_interface) ||
976        (send_destination && receive_member) ||
977        (send_destination && receive_error) ||
978        (send_destination && receive_sender) ||
979        (send_destination && eavesdrop) ||
980        (send_destination && requested_reply) ||
981        (send_destination && own) ||
982        (send_destination && user) ||
983        (send_destination && group)) ||
984
985       ((send_type && receive_interface) ||
986        (send_type && receive_member) ||
987        (send_type && receive_error) ||
988        (send_type && receive_sender) ||
989        (send_type && eavesdrop) ||
990        (send_type && requested_reply) ||
991        (send_type && own) ||
992        (send_type && user) ||
993        (send_type && group)) ||
994
995       ((send_path && receive_interface) ||
996        (send_path && receive_member) ||
997        (send_path && receive_error) ||
998        (send_path && receive_sender) ||
999        (send_path && eavesdrop) ||
1000        (send_path && requested_reply) ||
1001        (send_path && own) ||
1002        (send_path && user) ||
1003        (send_path && group)) ||
1004       
1005       ((receive_interface && receive_error) ||
1006        (receive_interface && own) ||
1007        (receive_interface && user) ||
1008        (receive_interface && group)) ||
1009
1010       ((receive_member && receive_error) ||
1011        (receive_member && own) ||
1012        (receive_member && user) ||
1013        (receive_member && group)) ||
1014       
1015       ((receive_error && own) ||
1016        (receive_error && user) ||
1017        (receive_error && group)) ||
1018
1019       ((eavesdrop && own) ||
1020        (eavesdrop && user) ||
1021        (eavesdrop && group)) ||
1022
1023       ((requested_reply && own) ||
1024        (requested_reply && user) ||
1025        (requested_reply && group)) ||
1026       
1027       ((own && user) ||
1028        (own && group)) ||
1029
1030       ((user && group)))
1031     {
1032       dbus_set_error (error, DBUS_ERROR_FAILED,
1033                       "Invalid combination of attributes on element <%s>",
1034                       element_name);
1035       return FALSE;
1036     }
1037   
1038   rule = NULL;
1039
1040   /* In BusPolicyRule, NULL represents wildcard.
1041    * In the config file, '*' represents it.
1042    */
1043 #define IS_WILDCARD(str) ((str) && ((str)[0]) == '*' && ((str)[1]) == '\0')
1044
1045   if (send_interface || send_member || send_error || send_destination ||
1046       send_path || send_type)
1047     {
1048       int message_type;
1049       
1050       if (IS_WILDCARD (send_interface))
1051         send_interface = NULL;
1052       if (IS_WILDCARD (send_member))
1053         send_member = NULL;
1054       if (IS_WILDCARD (send_error))
1055         send_error = NULL;
1056       if (IS_WILDCARD (send_destination))
1057         send_destination = NULL;
1058       if (IS_WILDCARD (send_path))
1059         send_path = NULL;
1060       if (IS_WILDCARD (send_type))
1061         send_type = NULL;
1062
1063       message_type = DBUS_MESSAGE_TYPE_INVALID;
1064       if (send_type != NULL)
1065         {
1066           message_type = dbus_message_type_from_string (send_type);
1067           if (message_type == DBUS_MESSAGE_TYPE_INVALID)
1068             {
1069               dbus_set_error (error, DBUS_ERROR_FAILED,
1070                               "Bad message type \"%s\"",
1071                               send_type);
1072               return FALSE;
1073             }
1074         }
1075       
1076       rule = bus_policy_rule_new (BUS_POLICY_RULE_SEND, allow); 
1077       if (rule == NULL)
1078         goto nomem;
1079       
1080       rule->d.send.message_type = message_type;
1081       rule->d.send.path = _dbus_strdup (send_path);
1082       rule->d.send.interface = _dbus_strdup (send_interface);
1083       rule->d.send.member = _dbus_strdup (send_member);
1084       rule->d.send.error = _dbus_strdup (send_error);
1085       rule->d.send.destination = _dbus_strdup (send_destination);
1086       if (send_path && rule->d.send.path == NULL)
1087         goto nomem;
1088       if (send_interface && rule->d.send.interface == NULL)
1089         goto nomem;
1090       if (send_member && rule->d.send.member == NULL)
1091         goto nomem;
1092       if (send_error && rule->d.send.error == NULL)
1093         goto nomem;
1094       if (send_destination && rule->d.send.destination == NULL)
1095         goto nomem;
1096     }
1097   else if (receive_interface || receive_member || receive_error || receive_sender ||
1098            receive_path || receive_type || eavesdrop || requested_reply)
1099     {
1100       int message_type;
1101       
1102       if (IS_WILDCARD (receive_interface))
1103         receive_interface = NULL;
1104       if (IS_WILDCARD (receive_member))
1105         receive_member = NULL;
1106       if (IS_WILDCARD (receive_error))
1107         receive_error = NULL;
1108       if (IS_WILDCARD (receive_sender))
1109         receive_sender = NULL;
1110       if (IS_WILDCARD (receive_path))
1111         receive_path = NULL;
1112       if (IS_WILDCARD (receive_type))
1113         receive_type = NULL;
1114
1115       message_type = DBUS_MESSAGE_TYPE_INVALID;
1116       if (receive_type != NULL)
1117         {
1118           message_type = dbus_message_type_from_string (receive_type);
1119           if (message_type == DBUS_MESSAGE_TYPE_INVALID)
1120             {
1121               dbus_set_error (error, DBUS_ERROR_FAILED,
1122                               "Bad message type \"%s\"",
1123                               receive_type);
1124               return FALSE;
1125             }
1126         }
1127
1128
1129       if (eavesdrop &&
1130           !(strcmp (eavesdrop, "true") == 0 ||
1131             strcmp (eavesdrop, "false") == 0))
1132         {
1133           dbus_set_error (error, DBUS_ERROR_FAILED,
1134                           "Bad value \"%s\" for %s attribute, must be true or false",
1135                           "eavesdrop", eavesdrop);
1136           return FALSE;
1137         }
1138
1139       if (requested_reply &&
1140           !(strcmp (requested_reply, "true") == 0 ||
1141             strcmp (requested_reply, "false") == 0))
1142         {
1143           dbus_set_error (error, DBUS_ERROR_FAILED,
1144                           "Bad value \"%s\" for %s attribute, must be true or false",
1145                           "requested_reply", requested_reply);
1146           return FALSE;
1147         }
1148       
1149       rule = bus_policy_rule_new (BUS_POLICY_RULE_RECEIVE, allow); 
1150       if (rule == NULL)
1151         goto nomem;
1152
1153       if (eavesdrop)
1154         rule->d.receive.eavesdrop = (strcmp (eavesdrop, "true") == 0);
1155
1156       if (requested_reply)
1157         rule->d.receive.requested_reply = (strcmp (requested_reply, "true") == 0);
1158       
1159       rule->d.receive.message_type = message_type;
1160       rule->d.receive.path = _dbus_strdup (receive_path);
1161       rule->d.receive.interface = _dbus_strdup (receive_interface);
1162       rule->d.receive.member = _dbus_strdup (receive_member);
1163       rule->d.receive.error = _dbus_strdup (receive_error);
1164       rule->d.receive.origin = _dbus_strdup (receive_sender);
1165
1166       if (receive_path && rule->d.receive.path == NULL)
1167         goto nomem;
1168       if (receive_interface && rule->d.receive.interface == NULL)
1169         goto nomem;
1170       if (receive_member && rule->d.receive.member == NULL)
1171         goto nomem;
1172       if (receive_error && rule->d.receive.error == NULL)
1173         goto nomem;
1174       if (receive_sender && rule->d.receive.origin == NULL)
1175         goto nomem;
1176     }
1177   else if (own)
1178     {
1179       rule = bus_policy_rule_new (BUS_POLICY_RULE_OWN, allow); 
1180       if (rule == NULL)
1181         goto nomem;
1182
1183       if (IS_WILDCARD (own))
1184         own = NULL;
1185       
1186       rule->d.own.service_name = _dbus_strdup (own);
1187       if (own && rule->d.own.service_name == NULL)
1188         goto nomem;
1189     }
1190   else if (user)
1191     {      
1192       if (IS_WILDCARD (user))
1193         {
1194           rule = bus_policy_rule_new (BUS_POLICY_RULE_USER, allow); 
1195           if (rule == NULL)
1196             goto nomem;
1197
1198           rule->d.user.uid = DBUS_UID_UNSET;
1199         }
1200       else
1201         {
1202           DBusString username;
1203           dbus_uid_t uid;
1204           
1205           _dbus_string_init_const (&username, user);
1206       
1207           if (_dbus_get_user_id (&username, &uid))
1208             {
1209               rule = bus_policy_rule_new (BUS_POLICY_RULE_USER, allow); 
1210               if (rule == NULL)
1211                 goto nomem;
1212
1213               rule->d.user.uid = uid;
1214             }
1215           else
1216             {
1217               _dbus_warn ("Unknown username \"%s\" on element <%s>\n",
1218                           user, element_name);
1219             }
1220         }
1221     }
1222   else if (group)
1223     {
1224       if (IS_WILDCARD (group))
1225         {
1226           rule = bus_policy_rule_new (BUS_POLICY_RULE_GROUP, allow); 
1227           if (rule == NULL)
1228             goto nomem;
1229
1230           rule->d.group.gid = DBUS_GID_UNSET;
1231         }
1232       else
1233         {
1234           DBusString groupname;
1235           dbus_gid_t gid;
1236           
1237           _dbus_string_init_const (&groupname, group);
1238           
1239           if (_dbus_get_user_id (&groupname, &gid))
1240             {
1241               rule = bus_policy_rule_new (BUS_POLICY_RULE_GROUP, allow); 
1242               if (rule == NULL)
1243                 goto nomem;
1244
1245               rule->d.group.gid = gid;
1246             }
1247           else
1248             {
1249               _dbus_warn ("Unknown group \"%s\" on element <%s>\n",
1250                           group, element_name);
1251             }
1252         }
1253     }
1254   else
1255     _dbus_assert_not_reached ("Did not handle some combination of attributes on <allow> or <deny>");
1256
1257   if (rule != NULL)
1258     {
1259       Element *pe;
1260       
1261       pe = peek_element (parser);      
1262       _dbus_assert (pe != NULL);
1263       _dbus_assert (pe->type == ELEMENT_POLICY);
1264
1265       switch (pe->d.policy.type)
1266         {
1267         case POLICY_IGNORED:
1268           /* drop the rule on the floor */
1269           break;
1270           
1271         case POLICY_DEFAULT:
1272           if (!bus_policy_append_default_rule (parser->policy, rule))
1273             goto nomem;
1274           break;
1275         case POLICY_MANDATORY:
1276           if (!bus_policy_append_mandatory_rule (parser->policy, rule))
1277             goto nomem;
1278           break;
1279         case POLICY_USER:
1280           if (!BUS_POLICY_RULE_IS_PER_CLIENT (rule))
1281             {
1282               dbus_set_error (error, DBUS_ERROR_FAILED,
1283                               "<%s> rule cannot be per-user because it has bus-global semantics",
1284                               element_name);
1285               goto failed;
1286             }
1287           
1288           if (!bus_policy_append_user_rule (parser->policy, pe->d.policy.gid_or_uid,
1289                                             rule))
1290             goto nomem;
1291           break;
1292         case POLICY_GROUP:
1293           if (!BUS_POLICY_RULE_IS_PER_CLIENT (rule))
1294             {
1295               dbus_set_error (error, DBUS_ERROR_FAILED,
1296                               "<%s> rule cannot be per-group because it has bus-global semantics",
1297                               element_name);
1298               goto failed;
1299             }
1300           
1301           if (!bus_policy_append_group_rule (parser->policy, pe->d.policy.gid_or_uid,
1302                                              rule))
1303             goto nomem;
1304           break;
1305         }
1306       
1307       bus_policy_rule_unref (rule);
1308       rule = NULL;
1309     }
1310   
1311   return TRUE;
1312
1313  nomem:
1314   BUS_SET_OOM (error);
1315  failed:
1316   if (rule)
1317     bus_policy_rule_unref (rule);
1318   return FALSE;
1319 }
1320
1321 static dbus_bool_t
1322 start_policy_child (BusConfigParser   *parser,
1323                     const char        *element_name,
1324                     const char       **attribute_names,
1325                     const char       **attribute_values,
1326                     DBusError         *error)
1327 {
1328   if (strcmp (element_name, "allow") == 0)
1329     {
1330       if (!append_rule_from_element (parser, element_name,
1331                                      attribute_names, attribute_values,
1332                                      TRUE, error))
1333         return FALSE;
1334       
1335       if (push_element (parser, ELEMENT_ALLOW) == NULL)
1336         {
1337           BUS_SET_OOM (error);
1338           return FALSE;
1339         }
1340       
1341       return TRUE;
1342     }
1343   else if (strcmp (element_name, "deny") == 0)
1344     {
1345       if (!append_rule_from_element (parser, element_name,
1346                                      attribute_names, attribute_values,
1347                                      FALSE, error))
1348         return FALSE;
1349       
1350       if (push_element (parser, ELEMENT_DENY) == NULL)
1351         {
1352           BUS_SET_OOM (error);
1353           return FALSE;
1354         }
1355       
1356       return TRUE;
1357     }
1358   else
1359     {
1360       dbus_set_error (error, DBUS_ERROR_FAILED,
1361                       "Element <%s> not allowed inside <%s> in configuration file",
1362                       element_name, "policy");
1363       return FALSE;
1364     }
1365 }
1366
1367 dbus_bool_t
1368 bus_config_parser_start_element (BusConfigParser   *parser,
1369                                  const char        *element_name,
1370                                  const char       **attribute_names,
1371                                  const char       **attribute_values,
1372                                  DBusError         *error)
1373 {
1374   ElementType t;
1375
1376   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
1377
1378   /* printf ("START: %s\n", element_name); */
1379   
1380   t = top_element_type (parser);
1381
1382   if (t == ELEMENT_NONE)
1383     {
1384       if (strcmp (element_name, "busconfig") == 0)
1385         {
1386           if (!check_no_attributes (parser, "busconfig", attribute_names, attribute_values, error))
1387             return FALSE;
1388           
1389           if (push_element (parser, ELEMENT_BUSCONFIG) == NULL)
1390             {
1391               BUS_SET_OOM (error);
1392               return FALSE;
1393             }
1394
1395           return TRUE;
1396         }
1397       else
1398         {
1399           dbus_set_error (error, DBUS_ERROR_FAILED,
1400                           "Unknown element <%s> at root of configuration file",
1401                           element_name);
1402           return FALSE;
1403         }
1404     }
1405   else if (t == ELEMENT_BUSCONFIG)
1406     {
1407       return start_busconfig_child (parser, element_name,
1408                                     attribute_names, attribute_values,
1409                                     error);
1410     }
1411   else if (t == ELEMENT_POLICY)
1412     {
1413       return start_policy_child (parser, element_name,
1414                                  attribute_names, attribute_values,
1415                                  error);
1416     }
1417   else
1418     {
1419       dbus_set_error (error, DBUS_ERROR_FAILED,
1420                       "Element <%s> is not allowed in this context",
1421                       element_name);
1422       return FALSE;
1423     }  
1424 }
1425
1426 static dbus_bool_t
1427 set_limit (BusConfigParser *parser,
1428            const char      *name,
1429            long             value,
1430            DBusError       *error)
1431 {
1432   dbus_bool_t must_be_positive;
1433   dbus_bool_t must_be_int;
1434
1435   must_be_int = FALSE;
1436   must_be_positive = FALSE;
1437   
1438   if (strcmp (name, "max_incoming_bytes") == 0)
1439     {
1440       must_be_positive = TRUE;
1441       parser->limits.max_incoming_bytes = value;
1442     }
1443   else if (strcmp (name, "max_outgoing_bytes") == 0)
1444     {
1445       must_be_positive = TRUE;
1446       parser->limits.max_outgoing_bytes = value;
1447     }
1448   else if (strcmp (name, "max_message_size") == 0)
1449     {
1450       must_be_positive = TRUE;
1451       parser->limits.max_message_size = value;
1452     }
1453   else if (strcmp (name, "activation_timeout") == 0)
1454     {
1455       must_be_positive = TRUE;
1456       must_be_int = TRUE;
1457       parser->limits.activation_timeout = value;
1458     }
1459   else if (strcmp (name, "auth_timeout") == 0)
1460     {
1461       must_be_positive = TRUE;
1462       must_be_int = TRUE;
1463       parser->limits.auth_timeout = value;
1464     }
1465   else if (strcmp (name, "reply_timeout") == 0)
1466     {
1467       must_be_positive = TRUE;
1468       must_be_int = TRUE;
1469       parser->limits.reply_timeout = value;
1470     }
1471   else if (strcmp (name, "max_completed_connections") == 0)
1472     {
1473       must_be_positive = TRUE;
1474       must_be_int = TRUE;
1475       parser->limits.max_completed_connections = value;
1476     }
1477   else if (strcmp (name, "max_incomplete_connections") == 0)
1478     {
1479       must_be_positive = TRUE;
1480       must_be_int = TRUE;
1481       parser->limits.max_incomplete_connections = value;
1482     }
1483   else if (strcmp (name, "max_connections_per_user") == 0)
1484     {
1485       must_be_positive = TRUE;
1486       must_be_int = TRUE;
1487       parser->limits.max_connections_per_user = value;
1488     }
1489   else if (strcmp (name, "max_pending_activations") == 0)
1490     {
1491       must_be_positive = TRUE;
1492       must_be_int = TRUE;
1493       parser->limits.max_pending_activations = value;
1494     }
1495   else if (strcmp (name, "max_services_per_connection") == 0)
1496     {
1497       must_be_positive = TRUE;
1498       must_be_int = TRUE;
1499       parser->limits.max_services_per_connection = value;
1500     }
1501   else if (strcmp (name, "max_replies_per_connection") == 0)
1502     {
1503       must_be_positive = TRUE;
1504       must_be_int = TRUE;
1505       parser->limits.max_replies_per_connection = value;
1506     }
1507   else
1508     {
1509       dbus_set_error (error, DBUS_ERROR_FAILED,
1510                       "There is no limit called \"%s\"\n",
1511                       name);
1512       return FALSE;
1513     }
1514   
1515   if (must_be_positive && value < 0)
1516     {
1517       dbus_set_error (error, DBUS_ERROR_FAILED,
1518                       "<limit name=\"%s\"> must be a positive number\n",
1519                       name);
1520       return FALSE;
1521     }
1522
1523   if (must_be_int &&
1524       (value < _DBUS_INT_MIN || value > _DBUS_INT_MAX))
1525     {
1526       dbus_set_error (error, DBUS_ERROR_FAILED,
1527                       "<limit name=\"%s\"> value is too large\n",
1528                       name);
1529       return FALSE;
1530     }
1531
1532   return TRUE;  
1533 }
1534
1535 dbus_bool_t
1536 bus_config_parser_end_element (BusConfigParser   *parser,
1537                                const char        *element_name,
1538                                DBusError         *error)
1539 {
1540   ElementType t;
1541   const char *n;
1542   Element *e;
1543
1544   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
1545
1546   /* printf ("END: %s\n", element_name); */
1547   
1548   t = top_element_type (parser);
1549
1550   if (t == ELEMENT_NONE)
1551     {
1552       /* should probably be an assertion failure but
1553        * being paranoid about XML parsers
1554        */
1555       dbus_set_error (error, DBUS_ERROR_FAILED,
1556                       "XML parser ended element with no element on the stack");
1557       return FALSE;
1558     }
1559
1560   n = element_type_to_name (t);
1561   _dbus_assert (n != NULL);
1562   if (strcmp (n, element_name) != 0)
1563     {
1564       /* should probably be an assertion failure but
1565        * being paranoid about XML parsers
1566        */
1567       dbus_set_error (error, DBUS_ERROR_FAILED,
1568                       "XML element <%s> ended but topmost element on the stack was <%s>",
1569                       element_name, n);
1570       return FALSE;
1571     }
1572
1573   e = peek_element (parser);
1574   _dbus_assert (e != NULL);
1575
1576   switch (e->type)
1577     {
1578     case ELEMENT_NONE:
1579       _dbus_assert_not_reached ("element in stack has no type");
1580       break;
1581
1582     case ELEMENT_INCLUDE:
1583     case ELEMENT_USER:
1584     case ELEMENT_TYPE:
1585     case ELEMENT_LISTEN:
1586     case ELEMENT_PIDFILE:
1587     case ELEMENT_AUTH:
1588     case ELEMENT_SERVICEDIR:
1589     case ELEMENT_INCLUDEDIR:
1590     case ELEMENT_LIMIT:
1591       if (!e->had_content)
1592         {
1593           dbus_set_error (error, DBUS_ERROR_FAILED,
1594                           "XML element <%s> was expected to have content inside it",
1595                           element_type_to_name (e->type));
1596           return FALSE;
1597         }
1598
1599       if (e->type == ELEMENT_LIMIT)
1600         {
1601           if (!set_limit (parser, e->d.limit.name, e->d.limit.value,
1602                           error))
1603             return FALSE;
1604         }
1605       break;
1606
1607     case ELEMENT_BUSCONFIG:
1608     case ELEMENT_POLICY:
1609     case ELEMENT_ALLOW:
1610     case ELEMENT_DENY:
1611     case ELEMENT_FORK:
1612       break;
1613     }
1614
1615   pop_element (parser);
1616
1617   return TRUE;
1618 }
1619
1620 static dbus_bool_t
1621 all_whitespace (const DBusString *str)
1622 {
1623   int i;
1624
1625   _dbus_string_skip_white (str, 0, &i);
1626
1627   return i == _dbus_string_get_length (str);
1628 }
1629
1630 static dbus_bool_t
1631 make_full_path (const DBusString *basedir,
1632                 const DBusString *filename,
1633                 DBusString       *full_path)
1634 {
1635   if (_dbus_path_is_absolute (filename))
1636     {
1637       return _dbus_string_copy (filename, 0, full_path, 0);
1638     }
1639   else
1640     {
1641       if (!_dbus_string_copy (basedir, 0, full_path, 0))
1642         return FALSE;
1643       
1644       if (!_dbus_concat_dir_and_file (full_path, filename))
1645         return FALSE;
1646
1647       return TRUE;
1648     }
1649 }
1650
1651 static dbus_bool_t
1652 include_file (BusConfigParser   *parser,
1653               const DBusString  *filename,
1654               dbus_bool_t        ignore_missing,
1655               DBusError         *error)
1656 {
1657   /* FIXME good test case for this would load each config file in the
1658    * test suite both alone, and as an include, and check
1659    * that the result is the same
1660    */
1661   BusConfigParser *included;
1662   const char *filename_str;
1663   DBusError tmp_error;
1664         
1665   dbus_error_init (&tmp_error);
1666
1667   filename_str = _dbus_string_get_const_data (filename);
1668
1669   /* Check to make sure this file hasn't already been included. */
1670   if (seen_include (parser, filename))
1671     {
1672       dbus_set_error (error, DBUS_ERROR_FAILED,
1673                       "Circular inclusion of file '%s'",
1674                       filename_str);
1675       return FALSE;
1676     }
1677   
1678   if (! _dbus_list_append (&parser->included_files, (void *) filename_str))
1679     {
1680       BUS_SET_OOM (error);
1681       return FALSE;
1682     }
1683
1684   /* Since parser is passed in as the parent, included
1685      inherits parser's limits. */
1686   included = bus_config_load (filename, FALSE, parser, &tmp_error);
1687
1688   _dbus_list_pop_last (&parser->included_files);
1689
1690   if (included == NULL)
1691     {
1692       _DBUS_ASSERT_ERROR_IS_SET (&tmp_error);
1693
1694       if (dbus_error_has_name (&tmp_error, DBUS_ERROR_FILE_NOT_FOUND) &&
1695           ignore_missing)
1696         {
1697           dbus_error_free (&tmp_error);
1698           return TRUE;
1699         }
1700       else
1701         {
1702           dbus_move_error (&tmp_error, error);
1703           return FALSE;
1704         }
1705     }
1706   else
1707     {
1708       _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error);
1709
1710       if (!merge_included (parser, included, error))
1711         {
1712           bus_config_parser_unref (included);
1713           return FALSE;
1714         }
1715
1716       /* Copy included's limits back to parser. */
1717       parser->limits = included->limits;
1718
1719       bus_config_parser_unref (included);
1720       return TRUE;
1721     }
1722 }
1723
1724 static dbus_bool_t
1725 include_dir (BusConfigParser   *parser,
1726              const DBusString  *dirname,
1727              DBusError         *error)
1728 {
1729   DBusString filename;
1730   dbus_bool_t retval;
1731   DBusError tmp_error;
1732   DBusDirIter *dir;
1733   
1734   if (!_dbus_string_init (&filename))
1735     {
1736       BUS_SET_OOM (error);
1737       return FALSE;
1738     }
1739
1740   retval = FALSE;
1741   
1742   dir = _dbus_directory_open (dirname, error);
1743
1744   if (dir == NULL)
1745     goto failed;
1746
1747   dbus_error_init (&tmp_error);
1748   while (_dbus_directory_get_next_file (dir, &filename, &tmp_error))
1749     {
1750       DBusString full_path;
1751
1752       if (!_dbus_string_init (&full_path))
1753         {
1754           BUS_SET_OOM (error);
1755           goto failed;
1756         }
1757
1758       if (!_dbus_string_copy (dirname, 0, &full_path, 0))
1759         {
1760           BUS_SET_OOM (error);
1761           _dbus_string_free (&full_path);
1762           goto failed;
1763         }      
1764
1765       if (!_dbus_concat_dir_and_file (&full_path, &filename))
1766         {
1767           BUS_SET_OOM (error);
1768           _dbus_string_free (&full_path);
1769           goto failed;
1770         }
1771       
1772       if (_dbus_string_ends_with_c_str (&full_path, ".conf"))
1773         {
1774           if (!include_file (parser, &full_path, TRUE, error))
1775             {
1776               _dbus_string_free (&full_path);
1777               goto failed;
1778             }
1779         }
1780
1781       _dbus_string_free (&full_path);
1782     }
1783
1784   if (dbus_error_is_set (&tmp_error))
1785     {
1786       dbus_move_error (&tmp_error, error);
1787       goto failed;
1788     }
1789   
1790   retval = TRUE;
1791   
1792  failed:
1793   _dbus_string_free (&filename);
1794   
1795   if (dir)
1796     _dbus_directory_close (dir);
1797
1798   return retval;
1799 }
1800
1801 dbus_bool_t
1802 bus_config_parser_content (BusConfigParser   *parser,
1803                            const DBusString  *content,
1804                            DBusError         *error)
1805 {
1806   Element *e;
1807
1808   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
1809
1810 #if 0
1811   {
1812     const char *c_str;
1813     
1814     _dbus_string_get_const_data (content, &c_str);
1815
1816     printf ("CONTENT %d bytes: %s\n", _dbus_string_get_length (content), c_str);
1817   }
1818 #endif
1819   
1820   e = peek_element (parser);
1821   if (e == NULL)
1822     {
1823       dbus_set_error (error, DBUS_ERROR_FAILED,
1824                       "Text content outside of any XML element in configuration file");
1825       return FALSE;
1826     }
1827   else if (e->had_content)
1828     {
1829       _dbus_assert_not_reached ("Element had multiple content blocks");
1830       return FALSE;
1831     }
1832
1833   switch (top_element_type (parser))
1834     {
1835     case ELEMENT_NONE:
1836       _dbus_assert_not_reached ("element at top of stack has no type");
1837       return FALSE;
1838
1839     case ELEMENT_BUSCONFIG:
1840     case ELEMENT_POLICY:
1841     case ELEMENT_ALLOW:
1842     case ELEMENT_DENY:
1843     case ELEMENT_FORK:
1844       if (all_whitespace (content))
1845         return TRUE;
1846       else
1847         {
1848           dbus_set_error (error, DBUS_ERROR_FAILED,
1849                           "No text content expected inside XML element %s in configuration file",
1850                           element_type_to_name (top_element_type (parser)));
1851           return FALSE;
1852         }
1853
1854     case ELEMENT_PIDFILE:
1855       {
1856         char *s;
1857
1858         e->had_content = TRUE;
1859         
1860         if (!_dbus_string_copy_data (content, &s))
1861           goto nomem;
1862           
1863         dbus_free (parser->pidfile);
1864         parser->pidfile = s;
1865       }
1866       break;
1867
1868     case ELEMENT_INCLUDE:
1869       {
1870         DBusString full_path;
1871         
1872         e->had_content = TRUE;
1873
1874         if (!_dbus_string_init (&full_path))
1875           goto nomem;
1876         
1877         if (!make_full_path (&parser->basedir, content, &full_path))
1878           {
1879             _dbus_string_free (&full_path);
1880             goto nomem;
1881           }
1882         
1883         if (!include_file (parser, &full_path,
1884                            e->d.include.ignore_missing, error))
1885           {
1886             _dbus_string_free (&full_path);
1887             return FALSE;
1888           }
1889
1890         _dbus_string_free (&full_path);
1891       }
1892       break;
1893
1894     case ELEMENT_INCLUDEDIR:
1895       {
1896         DBusString full_path;
1897         
1898         e->had_content = TRUE;
1899
1900         if (!_dbus_string_init (&full_path))
1901           goto nomem;
1902         
1903         if (!make_full_path (&parser->basedir, content, &full_path))
1904           {
1905             _dbus_string_free (&full_path);
1906             goto nomem;
1907           }
1908         
1909         if (!include_dir (parser, &full_path, error))
1910           {
1911             _dbus_string_free (&full_path);
1912             return FALSE;
1913           }
1914
1915         _dbus_string_free (&full_path);
1916       }
1917       break;
1918       
1919     case ELEMENT_USER:
1920       {
1921         char *s;
1922
1923         e->had_content = TRUE;
1924         
1925         if (!_dbus_string_copy_data (content, &s))
1926           goto nomem;
1927           
1928         dbus_free (parser->user);
1929         parser->user = s;
1930       }
1931       break;
1932
1933     case ELEMENT_TYPE:
1934       {
1935         char *s;
1936
1937         e->had_content = TRUE;
1938
1939         if (!_dbus_string_copy_data (content, &s))
1940           goto nomem;
1941         
1942         dbus_free (parser->bus_type);
1943         parser->bus_type = s;
1944       }
1945       break;
1946       
1947     case ELEMENT_LISTEN:
1948       {
1949         char *s;
1950
1951         e->had_content = TRUE;
1952         
1953         if (!_dbus_string_copy_data (content, &s))
1954           goto nomem;
1955
1956         if (!_dbus_list_append (&parser->listen_on,
1957                                 s))
1958           {
1959             dbus_free (s);
1960             goto nomem;
1961           }
1962       }
1963       break;
1964
1965     case ELEMENT_AUTH:
1966       {
1967         char *s;
1968         
1969         e->had_content = TRUE;
1970
1971         if (!_dbus_string_copy_data (content, &s))
1972           goto nomem;
1973
1974         if (!_dbus_list_append (&parser->mechanisms,
1975                                 s))
1976           {
1977             dbus_free (s);
1978             goto nomem;
1979           }
1980       }
1981       break;
1982
1983     case ELEMENT_SERVICEDIR:
1984       {
1985         char *s;
1986         DBusString full_path;
1987         
1988         e->had_content = TRUE;
1989
1990         if (!_dbus_string_init (&full_path))
1991           goto nomem;
1992         
1993         if (!make_full_path (&parser->basedir, content, &full_path))
1994           {
1995             _dbus_string_free (&full_path);
1996             goto nomem;
1997           }
1998         
1999         if (!_dbus_string_copy_data (&full_path, &s))
2000           {
2001             _dbus_string_free (&full_path);
2002             goto nomem;
2003           }
2004
2005         if (!_dbus_list_append (&parser->service_dirs, s))
2006           {
2007             _dbus_string_free (&full_path);
2008             dbus_free (s);
2009             goto nomem;
2010           }
2011
2012         _dbus_string_free (&full_path);
2013       }
2014       break;
2015
2016     case ELEMENT_LIMIT:
2017       {
2018         long val;
2019
2020         e->had_content = TRUE;
2021
2022         val = 0;
2023         if (!_dbus_string_parse_int (content, 0, &val, NULL))
2024           {
2025             dbus_set_error (error, DBUS_ERROR_FAILED,
2026                             "<limit name=\"%s\"> element has invalid value (could not parse as integer)",
2027                             e->d.limit.name);
2028             return FALSE;
2029           }
2030
2031         e->d.limit.value = val;
2032
2033         _dbus_verbose ("Loaded value %ld for limit %s\n",
2034                        e->d.limit.value,
2035                        e->d.limit.name);
2036       }
2037       break;
2038     }
2039
2040   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
2041   return TRUE;
2042
2043  nomem:
2044   BUS_SET_OOM (error);
2045   return FALSE;
2046 }
2047
2048 dbus_bool_t
2049 bus_config_parser_finished (BusConfigParser   *parser,
2050                             DBusError         *error)
2051 {
2052   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
2053
2054   if (parser->stack != NULL)
2055     {
2056       dbus_set_error (error, DBUS_ERROR_FAILED,
2057                       "Element <%s> was not closed in configuration file",
2058                       element_type_to_name (top_element_type (parser)));
2059
2060       return FALSE;
2061     }
2062
2063   if (parser->is_toplevel && parser->listen_on == NULL)
2064     {
2065       dbus_set_error (error, DBUS_ERROR_FAILED,
2066                       "Configuration file needs one or more <listen> elements giving addresses"); 
2067       return FALSE;
2068     }
2069   
2070   return TRUE;
2071 }
2072
2073 const char*
2074 bus_config_parser_get_user (BusConfigParser *parser)
2075 {
2076   return parser->user;
2077 }
2078
2079 const char*
2080 bus_config_parser_get_type (BusConfigParser *parser)
2081 {
2082   return parser->bus_type;
2083 }
2084
2085 DBusList**
2086 bus_config_parser_get_addresses (BusConfigParser *parser)
2087 {
2088   return &parser->listen_on;
2089 }
2090
2091 DBusList**
2092 bus_config_parser_get_mechanisms (BusConfigParser *parser)
2093 {
2094   return &parser->mechanisms;
2095 }
2096
2097 DBusList**
2098 bus_config_parser_get_service_dirs (BusConfigParser *parser)
2099 {
2100   return &parser->service_dirs;
2101 }
2102
2103 dbus_bool_t
2104 bus_config_parser_get_fork (BusConfigParser   *parser)
2105 {
2106   return parser->fork;
2107 }
2108
2109 const char *
2110 bus_config_parser_get_pidfile (BusConfigParser   *parser)
2111 {
2112   return parser->pidfile;
2113 }
2114
2115 BusPolicy*
2116 bus_config_parser_steal_policy (BusConfigParser *parser)
2117 {
2118   BusPolicy *policy;
2119
2120   _dbus_assert (parser->policy != NULL); /* can only steal the policy 1 time */
2121   
2122   policy = parser->policy;
2123
2124   parser->policy = NULL;
2125
2126   return policy;
2127 }
2128
2129 /* Overwrite any limits that were set in the configuration file */
2130 void
2131 bus_config_parser_get_limits (BusConfigParser *parser,
2132                               BusLimits       *limits)
2133 {
2134   *limits = parser->limits;
2135 }
2136
2137 #ifdef DBUS_BUILD_TESTS
2138 #include <stdio.h>
2139
2140 typedef enum
2141 {
2142   VALID,
2143   INVALID,
2144   UNKNOWN
2145 } Validity;
2146
2147 static dbus_bool_t
2148 do_load (const DBusString *full_path,
2149          Validity          validity,
2150          dbus_bool_t       oom_possible)
2151 {
2152   BusConfigParser *parser;
2153   DBusError error;
2154
2155   dbus_error_init (&error);
2156
2157   parser = bus_config_load (full_path, TRUE, NULL, &error);
2158   if (parser == NULL)
2159     {
2160       _DBUS_ASSERT_ERROR_IS_SET (&error);
2161
2162       if (oom_possible &&
2163           dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
2164         {
2165           _dbus_verbose ("Failed to load valid file due to OOM\n");
2166           dbus_error_free (&error);
2167           return TRUE;
2168         }
2169       else if (validity == VALID)
2170         {
2171           _dbus_warn ("Failed to load valid file but still had memory: %s\n",
2172                       error.message);
2173
2174           dbus_error_free (&error);
2175           return FALSE;
2176         }
2177       else
2178         {
2179           dbus_error_free (&error);
2180           return TRUE;
2181         }
2182     }
2183   else
2184     {
2185       _DBUS_ASSERT_ERROR_IS_CLEAR (&error);
2186
2187       bus_config_parser_unref (parser);
2188
2189       if (validity == INVALID)
2190         {
2191           _dbus_warn ("Accepted invalid file\n");
2192           return FALSE;
2193         }
2194
2195       return TRUE;
2196     }
2197 }
2198
2199 typedef struct
2200 {
2201   const DBusString *full_path;
2202   Validity          validity;
2203 } LoaderOomData;
2204
2205 static dbus_bool_t
2206 check_loader_oom_func (void *data)
2207 {
2208   LoaderOomData *d = data;
2209
2210   return do_load (d->full_path, d->validity, TRUE);
2211 }
2212
2213 static dbus_bool_t
2214 process_test_valid_subdir (const DBusString *test_base_dir,
2215                            const char       *subdir,
2216                            Validity          validity)
2217 {
2218   DBusString test_directory;
2219   DBusString filename;
2220   DBusDirIter *dir;
2221   dbus_bool_t retval;
2222   DBusError error;
2223
2224   retval = FALSE;
2225   dir = NULL;
2226
2227   if (!_dbus_string_init (&test_directory))
2228     _dbus_assert_not_reached ("didn't allocate test_directory\n");
2229
2230   _dbus_string_init_const (&filename, subdir);
2231
2232   if (!_dbus_string_copy (test_base_dir, 0,
2233                           &test_directory, 0))
2234     _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory");
2235
2236   if (!_dbus_concat_dir_and_file (&test_directory, &filename))
2237     _dbus_assert_not_reached ("couldn't allocate full path");
2238
2239   _dbus_string_free (&filename);
2240   if (!_dbus_string_init (&filename))
2241     _dbus_assert_not_reached ("didn't allocate filename string\n");
2242
2243   dbus_error_init (&error);
2244   dir = _dbus_directory_open (&test_directory, &error);
2245   if (dir == NULL)
2246     {
2247       _dbus_warn ("Could not open %s: %s\n",
2248                   _dbus_string_get_const_data (&test_directory),
2249                   error.message);
2250       dbus_error_free (&error);
2251       goto failed;
2252     }
2253
2254   if (validity == VALID)
2255     printf ("Testing valid files:\n");
2256   else if (validity == INVALID)
2257     printf ("Testing invalid files:\n");
2258   else
2259     printf ("Testing unknown files:\n");
2260
2261  next:
2262   while (_dbus_directory_get_next_file (dir, &filename, &error))
2263     {
2264       DBusString full_path;
2265       LoaderOomData d;
2266
2267       if (!_dbus_string_init (&full_path))
2268         _dbus_assert_not_reached ("couldn't init string");
2269
2270       if (!_dbus_string_copy (&test_directory, 0, &full_path, 0))
2271         _dbus_assert_not_reached ("couldn't copy dir to full_path");
2272
2273       if (!_dbus_concat_dir_and_file (&full_path, &filename))
2274         _dbus_assert_not_reached ("couldn't concat file to dir");
2275
2276       if (!_dbus_string_ends_with_c_str (&full_path, ".conf"))
2277         {
2278           _dbus_verbose ("Skipping non-.conf file %s\n",
2279                          _dbus_string_get_const_data (&filename));
2280           _dbus_string_free (&full_path);
2281           goto next;
2282         }
2283
2284       printf ("    %s\n", _dbus_string_get_const_data (&filename));
2285
2286       _dbus_verbose (" expecting %s\n",
2287                      validity == VALID ? "valid" :
2288                      (validity == INVALID ? "invalid" :
2289                       (validity == UNKNOWN ? "unknown" : "???")));
2290
2291       d.full_path = &full_path;
2292       d.validity = validity;
2293       if (!_dbus_test_oom_handling ("config-loader", check_loader_oom_func, &d))
2294         _dbus_assert_not_reached ("test failed");
2295
2296       _dbus_string_free (&full_path);
2297     }
2298
2299   if (dbus_error_is_set (&error))
2300     {
2301       _dbus_warn ("Could not get next file in %s: %s\n",
2302                   _dbus_string_get_const_data (&test_directory),
2303                   error.message);
2304       dbus_error_free (&error);
2305       goto failed;
2306     }
2307
2308   retval = TRUE;
2309
2310  failed:
2311
2312   if (dir)
2313     _dbus_directory_close (dir);
2314   _dbus_string_free (&test_directory);
2315   _dbus_string_free (&filename);
2316
2317   return retval;
2318 }
2319
2320 static dbus_bool_t
2321 bools_equal (dbus_bool_t a,
2322              dbus_bool_t b)
2323 {
2324   return a ? b : !b;
2325 }
2326
2327 static dbus_bool_t
2328 strings_equal_or_both_null (const char *a,
2329                             const char *b)
2330 {
2331   if (a == NULL || b == NULL)
2332     return a == b;
2333   else
2334     return !strcmp (a, b);
2335 }
2336
2337 static dbus_bool_t
2338 elements_equal (const Element *a,
2339                 const Element *b)
2340 {
2341   if (a->type != b->type)
2342     return FALSE;
2343
2344   if (!bools_equal (a->had_content, b->had_content))
2345     return FALSE;
2346
2347   switch (a->type)
2348     {
2349
2350     case ELEMENT_INCLUDE:
2351       if (!bools_equal (a->d.include.ignore_missing,
2352                         b->d.include.ignore_missing))
2353         return FALSE;
2354       break;
2355
2356     case ELEMENT_POLICY:
2357       if (a->d.policy.type != b->d.policy.type)
2358         return FALSE;
2359       if (a->d.policy.gid_or_uid != b->d.policy.gid_or_uid)
2360         return FALSE;
2361       break;
2362
2363     case ELEMENT_LIMIT:
2364       if (strcmp (a->d.limit.name, b->d.limit.name))
2365         return FALSE;
2366       if (a->d.limit.value != b->d.limit.value)
2367         return FALSE;
2368       break;
2369
2370     default:
2371       /* do nothing */
2372       break;
2373     }
2374
2375   return TRUE;
2376
2377 }
2378
2379 static dbus_bool_t
2380 lists_of_elements_equal (DBusList *a,
2381                          DBusList *b)
2382 {
2383   DBusList *ia;
2384   DBusList *ib;
2385
2386   ia = a;
2387   ib = b;
2388   
2389   while (ia != NULL && ib != NULL)
2390     {
2391       if (elements_equal (ia->data, ib->data))
2392         return FALSE;
2393       ia = _dbus_list_get_next_link (&a, ia);
2394       ib = _dbus_list_get_next_link (&b, ib);
2395     }
2396
2397   return ia == NULL && ib == NULL;
2398 }
2399
2400 static dbus_bool_t
2401 lists_of_c_strings_equal (DBusList *a,
2402                           DBusList *b)
2403 {
2404   DBusList *ia;
2405   DBusList *ib;
2406
2407   ia = a;
2408   ib = b;
2409   
2410   while (ia != NULL && ib != NULL)
2411     {
2412       if (strcmp (ia->data, ib->data))
2413         return FALSE;
2414       ia = _dbus_list_get_next_link (&a, ia);
2415       ib = _dbus_list_get_next_link (&b, ib);
2416     }
2417
2418   return ia == NULL && ib == NULL;
2419 }
2420
2421 static dbus_bool_t
2422 limits_equal (const BusLimits *a,
2423               const BusLimits *b)
2424 {
2425   return
2426     (a->max_incoming_bytes == b->max_incoming_bytes
2427      || a->max_outgoing_bytes == b->max_outgoing_bytes
2428      || a->max_message_size == b->max_message_size
2429      || a->activation_timeout == b->activation_timeout
2430      || a->auth_timeout == b->auth_timeout
2431      || a->max_completed_connections == b->max_completed_connections
2432      || a->max_incomplete_connections == b->max_incomplete_connections
2433      || a->max_connections_per_user == b->max_connections_per_user
2434      || a->max_pending_activations == b->max_pending_activations
2435      || a->max_services_per_connection == b->max_services_per_connection
2436      || a->max_match_rules_per_connection == b->max_match_rules_per_connection
2437      || a->max_replies_per_connection == b->max_replies_per_connection
2438      || a->reply_timeout == b->reply_timeout);
2439 }
2440
2441 static dbus_bool_t
2442 config_parsers_equal (const BusConfigParser *a,
2443                       const BusConfigParser *b)
2444 {
2445   if (!_dbus_string_equal (&a->basedir, &b->basedir))
2446     return FALSE;
2447
2448   if (!lists_of_elements_equal (a->stack, b->stack))
2449     return FALSE;
2450
2451   if (!strings_equal_or_both_null (a->user, b->user))
2452     return FALSE;
2453
2454   if (!lists_of_c_strings_equal (a->listen_on, b->listen_on))
2455     return FALSE;
2456
2457   if (!lists_of_c_strings_equal (a->mechanisms, b->mechanisms))
2458     return FALSE;
2459
2460   if (!lists_of_c_strings_equal (a->service_dirs, b->service_dirs))
2461     return FALSE;
2462   
2463   /* FIXME: compare policy */
2464
2465   if (! limits_equal (&a->limits, &b->limits))
2466     return FALSE;
2467
2468   if (!strings_equal_or_both_null (a->pidfile, b->pidfile))
2469     return FALSE;
2470
2471   if (! bools_equal (a->fork, b->fork))
2472     return FALSE;
2473
2474   if (! bools_equal (a->is_toplevel, b->is_toplevel))
2475     return FALSE;
2476
2477   return TRUE;
2478 }
2479
2480 static dbus_bool_t
2481 all_are_equiv (const DBusString *target_directory)
2482 {
2483   DBusString filename;
2484   DBusDirIter *dir;
2485   BusConfigParser *first_parser;
2486   BusConfigParser *parser;
2487   DBusError error;
2488   dbus_bool_t equal;
2489   dbus_bool_t retval;
2490
2491   dir = NULL;
2492   first_parser = NULL;
2493   parser = NULL;
2494   retval = FALSE;
2495
2496   if (!_dbus_string_init (&filename))
2497     _dbus_assert_not_reached ("didn't allocate filename string");
2498
2499   dbus_error_init (&error);
2500   dir = _dbus_directory_open (target_directory, &error);
2501   if (dir == NULL)
2502     {
2503       _dbus_warn ("Could not open %s: %s\n",
2504                   _dbus_string_get_const_data (target_directory),
2505                   error.message);
2506       dbus_error_free (&error);
2507       goto finished;
2508     }
2509
2510   printf ("Comparing equivalent files:\n");
2511
2512  next:
2513   while (_dbus_directory_get_next_file (dir, &filename, &error))
2514     {
2515       DBusString full_path;
2516
2517       if (!_dbus_string_init (&full_path))
2518         _dbus_assert_not_reached ("couldn't init string");
2519
2520       if (!_dbus_string_copy (target_directory, 0, &full_path, 0))
2521         _dbus_assert_not_reached ("couldn't copy dir to full_path");
2522
2523       if (!_dbus_concat_dir_and_file (&full_path, &filename))
2524         _dbus_assert_not_reached ("couldn't concat file to dir");
2525
2526       if (!_dbus_string_ends_with_c_str (&full_path, ".conf"))
2527         {
2528           _dbus_verbose ("Skipping non-.conf file %s\n",
2529                          _dbus_string_get_const_data (&filename));
2530           _dbus_string_free (&full_path);
2531           goto next;
2532         }
2533
2534       printf ("    %s\n", _dbus_string_get_const_data (&filename));
2535
2536       parser = bus_config_load (&full_path, TRUE, NULL, &error);
2537       _dbus_string_free (&full_path);
2538
2539       if (parser == NULL)
2540         {
2541           _dbus_warn ("Could not load file %s: %s\n",
2542                       _dbus_string_get_const_data (&full_path),
2543                       error.message);
2544           dbus_error_free (&error);
2545           goto finished;
2546         }
2547       else if (first_parser == NULL)
2548         {
2549           first_parser = parser;
2550         }
2551       else
2552         {
2553           equal = config_parsers_equal (first_parser, parser);
2554           bus_config_parser_unref (parser);
2555           if (! equal)
2556             goto finished;
2557         }
2558
2559     }
2560
2561   retval = TRUE;
2562
2563  finished:
2564   _dbus_string_free (&filename);
2565   if (first_parser)
2566     bus_config_parser_unref (first_parser);
2567   if (dir)
2568     _dbus_directory_close (dir);
2569
2570   return retval;
2571   
2572 }
2573
2574 static dbus_bool_t
2575 process_test_equiv_subdir (const DBusString *test_base_dir,
2576                            const char       *subdir)
2577 {
2578   DBusString test_directory;
2579   DBusString filename;
2580   DBusDirIter *dir;
2581   DBusError error;
2582   dbus_bool_t equal;
2583   dbus_bool_t retval;
2584
2585   dir = NULL;
2586   retval = FALSE;
2587
2588   if (!_dbus_string_init (&test_directory))
2589     _dbus_assert_not_reached ("didn't allocate test_directory");
2590
2591   _dbus_string_init_const (&filename, subdir);
2592
2593   if (!_dbus_string_copy (test_base_dir, 0,
2594                           &test_directory, 0))
2595     _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory");
2596
2597   if (!_dbus_concat_dir_and_file (&test_directory, &filename))
2598     _dbus_assert_not_reached ("couldn't allocate full path");
2599
2600   _dbus_string_free (&filename);
2601   if (!_dbus_string_init (&filename))
2602     _dbus_assert_not_reached ("didn't allocate filename string");
2603
2604   dbus_error_init (&error);
2605   dir = _dbus_directory_open (&test_directory, &error);
2606   if (dir == NULL)
2607     {
2608       _dbus_warn ("Could not open %s: %s\n",
2609                   _dbus_string_get_const_data (&test_directory),
2610                   error.message);
2611       dbus_error_free (&error);
2612       goto finished;
2613     }
2614
2615   while (_dbus_directory_get_next_file (dir, &filename, &error))
2616     {
2617       DBusString full_path;
2618
2619       /* Skip CVS's magic directories! */
2620       if (_dbus_string_equal_c_str (&filename, "CVS"))
2621         continue;
2622
2623       if (!_dbus_string_init (&full_path))
2624         _dbus_assert_not_reached ("couldn't init string");
2625
2626       if (!_dbus_string_copy (&test_directory, 0, &full_path, 0))
2627         _dbus_assert_not_reached ("couldn't copy dir to full_path");
2628
2629       if (!_dbus_concat_dir_and_file (&full_path, &filename))
2630         _dbus_assert_not_reached ("couldn't concat file to dir");
2631       
2632       equal = all_are_equiv (&full_path);
2633       _dbus_string_free (&full_path);
2634
2635       if (!equal)
2636         goto finished;
2637     }
2638
2639   retval = TRUE;
2640
2641  finished:
2642   _dbus_string_free (&test_directory);
2643   _dbus_string_free (&filename);
2644   if (dir)
2645     _dbus_directory_close (dir);
2646
2647   return retval;
2648   
2649 }
2650                            
2651 dbus_bool_t
2652 bus_config_parser_test (const DBusString *test_data_dir)
2653 {
2654   if (test_data_dir == NULL ||
2655       _dbus_string_get_length (test_data_dir) == 0)
2656     {
2657       printf ("No test data\n");
2658       return TRUE;
2659     }
2660
2661   if (!process_test_valid_subdir (test_data_dir, "valid-config-files", VALID))
2662     return FALSE;
2663
2664   if (!process_test_valid_subdir (test_data_dir, "invalid-config-files", INVALID))
2665     return FALSE;
2666
2667   if (!process_test_equiv_subdir (test_data_dir, "equiv-config-files"))
2668     return FALSE;
2669
2670   return TRUE;
2671 }
2672
2673 #endif /* DBUS_BUILD_TESTS */
2674