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