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