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