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