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