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