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