2003-04-06 Havoc Pennington <hp@pobox.com>
[platform/upstream/dbus.git] / bus / config-parser.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* config-parser.c  XML-library-agnostic configuration file parser
3  *
4  * Copyright (C) 2003 Red Hat, Inc.
5  *
6  * Licensed under the Academic Free License version 1.2
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23 #include "config-parser.h"
24 #include "test.h"
25 #include "utils.h"
26 #include <dbus/dbus-list.h>
27 #include <dbus/dbus-internals.h>
28 #include <string.h>
29
30 typedef enum
31 {
32   ELEMENT_NONE,
33   ELEMENT_BUSCONFIG,
34   ELEMENT_INCLUDE,
35   ELEMENT_USER,
36   ELEMENT_LISTEN,
37   ELEMENT_AUTH,
38   ELEMENT_POLICY,
39   ELEMENT_LIMIT,
40   ELEMENT_ALLOW,
41   ELEMENT_DENY,
42   ELEMENT_FORK,
43   ELEMENT_PIDFILE,
44   ELEMENT_SERVICEDIR,
45   ELEMENT_INCLUDEDIR,
46   ELEMENT_TYPE
47 } ElementType;
48
49 typedef struct
50 {
51   ElementType type;
52
53   unsigned int had_content : 1;
54
55   union
56   {
57     struct
58     {
59       unsigned int ignore_missing : 1;
60     } include;
61
62     struct
63     {
64       char *context;
65       char *user;
66       char *group;
67       DBusList *rules;
68     } policy;
69
70     struct
71     {
72       int foo;
73     } limit;
74
75   } d;
76
77 } Element;
78
79 struct BusConfigParser
80 {
81   int refcount;
82
83   DBusString basedir;  /**< Directory we resolve paths relative to */
84   
85   DBusList *stack;     /**< stack of Element */
86
87   char *user;          /**< user to run as */
88
89   char *bus_type;          /**< Message bus type */
90   
91   DBusList *listen_on; /**< List of addresses to listen to */
92
93   DBusList *mechanisms; /**< Auth mechanisms */
94
95   DBusList *service_dirs; /**< Directories to look for services in */
96   
97   unsigned int fork : 1; /**< TRUE to fork into daemon mode */
98
99   char *pidfile;
100 };
101
102 static const char*
103 element_type_to_name (ElementType type)
104 {
105   switch (type)
106     {
107     case ELEMENT_NONE:
108       return NULL;
109     case ELEMENT_BUSCONFIG:
110       return "busconfig";
111     case ELEMENT_INCLUDE:
112       return "include";
113     case ELEMENT_USER:
114       return "user";
115     case ELEMENT_LISTEN:
116       return "listen";
117     case ELEMENT_AUTH:
118       return "auth";
119     case ELEMENT_POLICY:
120       return "policy";
121     case ELEMENT_LIMIT:
122       return "limit";
123     case ELEMENT_ALLOW:
124       return "allow";
125     case ELEMENT_DENY:
126       return "deny";
127     case ELEMENT_FORK:
128       return "fork";
129     case ELEMENT_PIDFILE:
130       return "pidfile";
131     case ELEMENT_SERVICEDIR:
132       return "servicedir";
133     case ELEMENT_INCLUDEDIR:
134       return "includedir";
135     case ELEMENT_TYPE:
136       return "type";
137     }
138
139   _dbus_assert_not_reached ("bad element type");
140
141   return NULL;
142 }
143
144 static Element*
145 push_element (BusConfigParser *parser,
146               ElementType      type)
147 {
148   Element *e;
149
150   _dbus_assert (type != ELEMENT_NONE);
151   
152   e = dbus_new0 (Element, 1);
153   if (e == NULL)
154     return NULL;
155
156   if (!_dbus_list_append (&parser->stack, e))
157     {
158       dbus_free (e);
159       return NULL;
160     }
161   
162   e->type = type;
163
164   return e;
165 }
166
167 static void
168 element_free (Element *e)
169 {
170
171   dbus_free (e);
172 }
173
174 static void
175 pop_element (BusConfigParser *parser)
176 {
177   Element *e;
178
179   e = _dbus_list_pop_last (&parser->stack);
180   
181   element_free (e);
182 }
183
184 static Element*
185 peek_element (BusConfigParser *parser)
186 {
187   Element *e;
188
189   e = _dbus_list_get_last (&parser->stack);
190
191   return e;
192 }
193
194 static ElementType
195 top_element_type (BusConfigParser *parser)
196 {
197   Element *e;
198
199   e = _dbus_list_get_last (&parser->stack);
200
201   if (e)
202     return e->type;
203   else
204     return ELEMENT_NONE;
205 }
206
207 static dbus_bool_t
208 merge_included (BusConfigParser *parser,
209                 BusConfigParser *included,
210                 DBusError       *error)
211 {
212   DBusList *link;
213
214   if (included->user != NULL)
215     {
216       dbus_free (parser->user);
217       parser->user = included->user;
218       included->user = NULL;
219     }
220
221   if (included->bus_type != NULL)
222     {
223       dbus_free (parser->bus_type);
224       parser->bus_type = included->bus_type;
225       included->bus_type = NULL;
226     }
227   
228   if (included->fork)
229     parser->fork = TRUE;
230
231   if (included->pidfile != NULL)
232     {
233       dbus_free (parser->pidfile);
234       parser->pidfile = included->pidfile;
235       included->pidfile = NULL;
236     }
237   
238   while ((link = _dbus_list_pop_first_link (&included->listen_on)))
239     _dbus_list_append_link (&parser->listen_on, link);
240
241   while ((link = _dbus_list_pop_first_link (&included->mechanisms)))
242     _dbus_list_append_link (&parser->mechanisms, link);
243
244   while ((link = _dbus_list_pop_first_link (&included->service_dirs)))
245     _dbus_list_append_link (&parser->service_dirs, link);
246   
247   return TRUE;
248 }
249
250 BusConfigParser*
251 bus_config_parser_new (const DBusString *basedir)
252 {
253   BusConfigParser *parser;
254
255   parser = dbus_new0 (BusConfigParser, 1);
256   if (parser == NULL)
257     return NULL;
258
259   if (!_dbus_string_init (&parser->basedir))
260     {
261       dbus_free (parser);
262       return NULL;
263     }
264
265   if (!_dbus_string_copy (basedir, 0, &parser->basedir, 0))
266     {
267       _dbus_string_free (&parser->basedir);
268       dbus_free (parser);
269       return NULL;
270     }
271   
272   parser->refcount = 1;
273
274   return parser;
275 }
276
277 void
278 bus_config_parser_ref (BusConfigParser *parser)
279 {
280   _dbus_assert (parser->refcount > 0);
281
282   parser->refcount += 1;
283 }
284
285 void
286 bus_config_parser_unref (BusConfigParser *parser)
287 {
288   _dbus_assert (parser->refcount > 0);
289
290   parser->refcount -= 1;
291
292   if (parser->refcount == 0)
293     {
294       while (parser->stack != NULL)
295         pop_element (parser);
296
297       dbus_free (parser->user);
298       dbus_free (parser->bus_type);
299       dbus_free (parser->pidfile);
300       
301       _dbus_list_foreach (&parser->listen_on,
302                           (DBusForeachFunction) dbus_free,
303                           NULL);
304
305       _dbus_list_clear (&parser->listen_on);
306
307       _dbus_list_foreach (&parser->service_dirs,
308                           (DBusForeachFunction) dbus_free,
309                           NULL);
310
311       _dbus_list_clear (&parser->service_dirs);
312
313       _dbus_list_foreach (&parser->mechanisms,
314                           (DBusForeachFunction) dbus_free,
315                           NULL);
316
317       _dbus_list_clear (&parser->mechanisms);
318       
319       _dbus_string_free (&parser->basedir);
320       
321       dbus_free (parser);
322     }
323 }
324
325 dbus_bool_t
326 bus_config_parser_check_doctype (BusConfigParser   *parser,
327                                  const char        *doctype,
328                                  DBusError         *error)
329 {
330   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
331
332   if (strcmp (doctype, "busconfig") != 0)
333     {
334       dbus_set_error (error,
335                       DBUS_ERROR_FAILED,
336                       "Configuration file has the wrong document type %s",
337                       doctype);
338       return FALSE;
339     }
340   else
341     return TRUE;
342 }
343
344 typedef struct
345 {
346   const char  *name;
347   const char **retloc;
348 } LocateAttr;
349
350 static dbus_bool_t
351 locate_attributes (BusConfigParser  *parser,
352                    const char       *element_name,
353                    const char      **attribute_names,
354                    const char      **attribute_values,
355                    DBusError        *error,
356                    const char       *first_attribute_name,
357                    const char      **first_attribute_retloc,
358                    ...)
359 {
360   va_list args;
361   const char *name;
362   const char **retloc;
363   int n_attrs;
364 #define MAX_ATTRS 24
365   LocateAttr attrs[MAX_ATTRS];
366   dbus_bool_t retval;
367   int i;
368
369   _dbus_assert (first_attribute_name != NULL);
370   _dbus_assert (first_attribute_retloc != NULL);
371
372   retval = TRUE;
373
374   n_attrs = 1;
375   attrs[0].name = first_attribute_name;
376   attrs[0].retloc = first_attribute_retloc;
377   *first_attribute_retloc = NULL;
378
379   va_start (args, first_attribute_retloc);
380
381   name = va_arg (args, const char*);
382   retloc = va_arg (args, const char**);
383
384   while (name != NULL)
385     {
386       _dbus_assert (retloc != NULL);
387       _dbus_assert (n_attrs < MAX_ATTRS);
388
389       attrs[n_attrs].name = name;
390       attrs[n_attrs].retloc = retloc;
391       n_attrs += 1;
392       *retloc = NULL;
393
394       name = va_arg (args, const char*);
395       retloc = va_arg (args, const char**);
396     }
397
398   va_end (args);
399
400   if (!retval)
401     return retval;
402
403   i = 0;
404   while (attribute_names[i])
405     {
406       int j;
407       dbus_bool_t found;
408       
409       found = FALSE;
410       j = 0;
411       while (j < n_attrs)
412         {
413           if (strcmp (attrs[j].name, attribute_names[i]) == 0)
414             {
415               retloc = attrs[j].retloc;
416
417               if (*retloc != NULL)
418                 {
419                   dbus_set_error (error, DBUS_ERROR_FAILED,
420                                   "Attribute \"%s\" repeated twice on the same <%s> element",
421                                   attrs[j].name, element_name);
422                   retval = FALSE;
423                   goto out;
424                 }
425
426               *retloc = attribute_values[i];
427               found = TRUE;
428             }
429
430           ++j;
431         }
432
433       if (!found)
434         {
435           dbus_set_error (error, DBUS_ERROR_FAILED,
436                           "Attribute \"%s\" is invalid on <%s> element in this context",
437                           attribute_names[i], element_name);
438           retval = FALSE;
439           goto out;
440         }
441
442       ++i;
443     }
444
445  out:
446   return retval;
447 }
448
449 static dbus_bool_t
450 check_no_attributes (BusConfigParser  *parser,
451                      const char       *element_name,
452                      const char      **attribute_names,
453                      const char      **attribute_values,
454                      DBusError        *error)
455 {
456   if (attribute_names[0] != NULL)
457     {
458       dbus_set_error (error, DBUS_ERROR_FAILED,
459                       "Attribute \"%s\" is invalid on <%s> element in this context",
460                       attribute_names[0], element_name);
461       return FALSE;
462     }
463
464   return TRUE;
465 }
466
467 static dbus_bool_t
468 start_busconfig_child (BusConfigParser   *parser,
469                        const char        *element_name,
470                        const char       **attribute_names,
471                        const char       **attribute_values,
472                        DBusError         *error)
473 {
474   if (strcmp (element_name, "user") == 0)
475     {
476       if (!check_no_attributes (parser, "user", attribute_names, attribute_values, error))
477         return FALSE;
478
479       if (push_element (parser, ELEMENT_USER) == NULL)
480         {
481           BUS_SET_OOM (error);
482           return FALSE;
483         }
484
485       return TRUE;
486     }
487   else if (strcmp (element_name, "type") == 0)
488     {
489       if (!check_no_attributes (parser, "type", attribute_names, attribute_values, error))
490         return FALSE;
491
492       if (push_element (parser, ELEMENT_TYPE) == NULL)
493         {
494           BUS_SET_OOM (error);
495           return FALSE;
496         }
497
498       return TRUE;
499     }
500   else if (strcmp (element_name, "fork") == 0)
501     {
502       if (!check_no_attributes (parser, "fork", attribute_names, attribute_values, error))
503         return FALSE;
504
505       if (push_element (parser, ELEMENT_FORK) == NULL)
506         {
507           BUS_SET_OOM (error);
508           return FALSE;
509         }
510
511       parser->fork = TRUE;
512       
513       return TRUE;
514     }
515   else if (strcmp (element_name, "pidfile") == 0)
516     {
517       if (!check_no_attributes (parser, "pidfile", attribute_names, attribute_values, error))
518         return FALSE;
519
520       if (push_element (parser, ELEMENT_PIDFILE) == NULL)
521         {
522           BUS_SET_OOM (error);
523           return FALSE;
524         }
525
526       return TRUE;
527     }
528   else if (strcmp (element_name, "listen") == 0)
529     {
530       if (!check_no_attributes (parser, "listen", attribute_names, attribute_values, error))
531         return FALSE;
532
533       if (push_element (parser, ELEMENT_LISTEN) == NULL)
534         {
535           BUS_SET_OOM (error);
536           return FALSE;
537         }
538
539       return TRUE;
540     }
541   else if (strcmp (element_name, "auth") == 0)
542     {
543       if (!check_no_attributes (parser, "auth", attribute_names, attribute_values, error))
544         return FALSE;
545
546       if (push_element (parser, ELEMENT_AUTH) == NULL)
547         {
548           BUS_SET_OOM (error);
549           return FALSE;
550         }
551
552       return TRUE;
553     }
554   else if (strcmp (element_name, "includedir") == 0)
555     {
556       if (!check_no_attributes (parser, "includedir", attribute_names, attribute_values, error))
557         return FALSE;
558
559       if (push_element (parser, ELEMENT_INCLUDEDIR) == NULL)
560         {
561           BUS_SET_OOM (error);
562           return FALSE;
563         }
564
565       return TRUE;
566     }
567   else if (strcmp (element_name, "servicedir") == 0)
568     {
569       if (!check_no_attributes (parser, "servicedir", attribute_names, attribute_values, error))
570         return FALSE;
571
572       if (push_element (parser, ELEMENT_SERVICEDIR) == NULL)
573         {
574           BUS_SET_OOM (error);
575           return FALSE;
576         }
577
578       return TRUE;
579     }
580   else if (strcmp (element_name, "include") == 0)
581     {
582       Element *e;
583       const char *ignore_missing;
584
585       if ((e = push_element (parser, ELEMENT_INCLUDE)) == NULL)
586         {
587           BUS_SET_OOM (error);
588           return FALSE;
589         }
590
591       e->d.include.ignore_missing = FALSE;
592
593       if (!locate_attributes (parser, "include",
594                               attribute_names,
595                               attribute_values,
596                               error,
597                               "ignore_missing", &ignore_missing,
598                               NULL))
599         return FALSE;
600
601       if (ignore_missing != NULL)
602         {
603           if (strcmp (ignore_missing, "yes") == 0)
604             e->d.include.ignore_missing = TRUE;
605           else if (strcmp (ignore_missing, "no") == 0)
606             e->d.include.ignore_missing = FALSE;
607           else
608             {
609               dbus_set_error (error, DBUS_ERROR_FAILED,
610                               "ignore_missing attribute must have value \"yes\" or \"no\"");
611               return FALSE;
612             }
613         }
614
615       return TRUE;
616     }
617   else if (strcmp (element_name, "policy") == 0)
618     {
619       Element *e;
620       const char *context;
621       const char *user;
622       const char *group;
623
624       if ((e = push_element (parser, ELEMENT_POLICY)) == NULL)
625         {
626           BUS_SET_OOM (error);
627           return FALSE;
628         }
629
630       if (!locate_attributes (parser, "include",
631                               attribute_names,
632                               attribute_values,
633                               error,
634                               "context", &context,
635                               "user", &user,
636                               "group", &group,
637                               NULL))
638         return FALSE;
639
640       /* FIXME */
641       
642       return TRUE;
643     }
644   else
645     {
646       dbus_set_error (error, DBUS_ERROR_FAILED,
647                       "Element <%s> not allowed inside <%s> in configuration file",
648                       element_name, "busconfig");
649       return FALSE;
650     }
651 }
652
653 static dbus_bool_t
654 start_policy_child (BusConfigParser   *parser,
655                     const char        *element_name,
656                     const char       **attribute_names,
657                     const char       **attribute_values,
658                     DBusError         *error)
659 {
660   if (strcmp (element_name, "allow") == 0)
661     {
662       if (push_element (parser, ELEMENT_ALLOW) == NULL)
663         {
664           BUS_SET_OOM (error);
665           return FALSE;
666         }
667       
668       return TRUE;
669     }
670   else if (strcmp (element_name, "deny") == 0)
671     {
672       if (push_element (parser, ELEMENT_DENY) == NULL)
673         {
674           BUS_SET_OOM (error);
675           return FALSE;
676         }
677       
678       return TRUE;
679     }
680   else
681     {
682       dbus_set_error (error, DBUS_ERROR_FAILED,
683                       "Element <%s> not allowed inside <%s> in configuration file",
684                       element_name, "policy");
685       return FALSE;
686     }
687 }
688
689 dbus_bool_t
690 bus_config_parser_start_element (BusConfigParser   *parser,
691                                  const char        *element_name,
692                                  const char       **attribute_names,
693                                  const char       **attribute_values,
694                                  DBusError         *error)
695 {
696   ElementType t;
697
698   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
699
700   /* printf ("START: %s\n", element_name); */
701   
702   t = top_element_type (parser);
703
704   if (t == ELEMENT_NONE)
705     {
706       if (strcmp (element_name, "busconfig") == 0)
707         {
708           if (!check_no_attributes (parser, "busconfig", attribute_names, attribute_values, error))
709             return FALSE;
710           
711           if (push_element (parser, ELEMENT_BUSCONFIG) == NULL)
712             {
713               BUS_SET_OOM (error);
714               return FALSE;
715             }
716
717           return TRUE;
718         }
719       else
720         {
721           dbus_set_error (error, DBUS_ERROR_FAILED,
722                           "Unknown element <%s> at root of configuration file",
723                           element_name);
724           return FALSE;
725         }
726     }
727   else if (t == ELEMENT_BUSCONFIG)
728     {
729       return start_busconfig_child (parser, element_name,
730                                     attribute_names, attribute_values,
731                                     error);
732     }
733   else if (t == ELEMENT_POLICY)
734     {
735       return start_policy_child (parser, element_name,
736                                  attribute_names, attribute_values,
737                                  error);
738     }
739   else
740     {
741       dbus_set_error (error, DBUS_ERROR_FAILED,
742                       "Element <%s> is not allowed in this context",
743                       element_name);
744       return FALSE;
745     }  
746 }
747
748 dbus_bool_t
749 bus_config_parser_end_element (BusConfigParser   *parser,
750                                const char        *element_name,
751                                DBusError         *error)
752 {
753   ElementType t;
754   const char *n;
755   Element *e;
756
757   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
758
759   /* printf ("END: %s\n", element_name); */
760   
761   t = top_element_type (parser);
762
763   if (t == ELEMENT_NONE)
764     {
765       /* should probably be an assertion failure but
766        * being paranoid about XML parsers
767        */
768       dbus_set_error (error, DBUS_ERROR_FAILED,
769                       "XML parser ended element with no element on the stack");
770       return FALSE;
771     }
772
773   n = element_type_to_name (t);
774   _dbus_assert (n != NULL);
775   if (strcmp (n, element_name) != 0)
776     {
777       /* should probably be an assertion failure but
778        * being paranoid about XML parsers
779        */
780       dbus_set_error (error, DBUS_ERROR_FAILED,
781                       "XML element <%s> ended but topmost element on the stack was <%s>",
782                       element_name, n);
783       return FALSE;
784     }
785
786   e = peek_element (parser);
787   _dbus_assert (e != NULL);
788
789   switch (e->type)
790     {
791     case ELEMENT_NONE:
792       _dbus_assert_not_reached ("element in stack has no type");
793       break;
794
795     case ELEMENT_INCLUDE:
796     case ELEMENT_USER:
797     case ELEMENT_TYPE:
798     case ELEMENT_LISTEN:
799     case ELEMENT_PIDFILE:
800     case ELEMENT_AUTH:
801     case ELEMENT_SERVICEDIR:
802     case ELEMENT_INCLUDEDIR:
803       if (!e->had_content)
804         {
805           dbus_set_error (error, DBUS_ERROR_FAILED,
806                           "XML element <%s> was expected to have content inside it",
807                           element_type_to_name (e->type));
808           return FALSE;
809         }
810       break;
811
812     case ELEMENT_BUSCONFIG:
813     case ELEMENT_POLICY:
814     case ELEMENT_LIMIT:
815     case ELEMENT_ALLOW:
816     case ELEMENT_DENY:
817     case ELEMENT_FORK:
818       break;
819     }
820
821   pop_element (parser);
822
823   return TRUE;
824 }
825
826 static dbus_bool_t
827 all_whitespace (const DBusString *str)
828 {
829   int i;
830
831   _dbus_string_skip_white (str, 0, &i);
832
833   return i == _dbus_string_get_length (str);
834 }
835
836 static dbus_bool_t
837 make_full_path (const DBusString *basedir,
838                 const DBusString *filename,
839                 DBusString       *full_path)
840 {
841   if (_dbus_path_is_absolute (filename))
842     {
843       return _dbus_string_copy (filename, 0, full_path, 0);
844     }
845   else
846     {
847       if (!_dbus_string_copy (basedir, 0, full_path, 0))
848         return FALSE;
849       
850       if (!_dbus_concat_dir_and_file (full_path, filename))
851         return FALSE;
852
853       return TRUE;
854     }
855 }
856
857 static dbus_bool_t
858 include_file (BusConfigParser   *parser,
859               const DBusString  *filename,
860               dbus_bool_t        ignore_missing,
861               DBusError         *error)
862 {
863   /* FIXME good test case for this would load each config file in the
864    * test suite both alone, and as an include, and check
865    * that the result is the same
866    */
867   BusConfigParser *included;
868   DBusError tmp_error;
869         
870   dbus_error_init (&tmp_error);
871   included = bus_config_load (filename, &tmp_error);
872   if (included == NULL)
873     {
874       _DBUS_ASSERT_ERROR_IS_SET (&tmp_error);
875
876       if (dbus_error_has_name (&tmp_error, DBUS_ERROR_FILE_NOT_FOUND) &&
877           ignore_missing)
878         {
879           dbus_error_free (&tmp_error);
880           return TRUE;
881         }
882       else
883         {
884           dbus_move_error (&tmp_error, error);
885           return FALSE;
886         }
887     }
888   else
889     {
890       _DBUS_ASSERT_ERROR_IS_CLEAR (&tmp_error);
891
892       if (!merge_included (parser, included, error))
893         {
894           bus_config_parser_unref (included);
895           return FALSE;
896         }
897
898       bus_config_parser_unref (included);
899       return TRUE;
900     }
901 }
902
903 static dbus_bool_t
904 include_dir (BusConfigParser   *parser,
905              const DBusString  *dirname,
906              DBusError         *error)
907 {
908   DBusString filename;
909   dbus_bool_t retval;
910   DBusError tmp_error;
911   DBusDirIter *dir;
912   
913   if (!_dbus_string_init (&filename))
914     {
915       BUS_SET_OOM (error);
916       return FALSE;
917     }
918
919   retval = FALSE;
920   
921   dir = _dbus_directory_open (dirname, error);
922
923   if (dir == NULL)
924     goto failed;
925
926   dbus_error_init (&tmp_error);
927   while (_dbus_directory_get_next_file (dir, &filename, &tmp_error))
928     {
929       DBusString full_path;
930
931       if (!_dbus_string_init (&full_path))
932         {
933           BUS_SET_OOM (error);
934           goto failed;
935         }
936
937       if (!_dbus_string_copy (dirname, 0, &full_path, 0))
938         {
939           BUS_SET_OOM (error);
940           _dbus_string_free (&full_path);
941           goto failed;
942         }      
943
944       if (!_dbus_concat_dir_and_file (&full_path, &filename))
945         {
946           BUS_SET_OOM (error);
947           _dbus_string_free (&full_path);
948           goto failed;
949         }
950       
951       if (_dbus_string_ends_with_c_str (&full_path, ".conf"))
952         {
953           if (!include_file (parser, &full_path, TRUE, error))
954             {
955               _dbus_string_free (&full_path);
956               goto failed;
957             }
958         }
959
960       _dbus_string_free (&full_path);
961     }
962
963   if (dbus_error_is_set (&tmp_error))
964     {
965       dbus_move_error (&tmp_error, error);
966       goto failed;
967     }
968   
969   retval = TRUE;
970   
971  failed:
972   _dbus_string_free (&filename);
973   
974   if (dir)
975     _dbus_directory_close (dir);
976
977   return retval;
978 }
979
980 dbus_bool_t
981 bus_config_parser_content (BusConfigParser   *parser,
982                            const DBusString  *content,
983                            DBusError         *error)
984 {
985   Element *e;
986
987   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
988
989 #if 0
990   {
991     const char *c_str;
992     
993     _dbus_string_get_const_data (content, &c_str);
994
995     printf ("CONTENT %d bytes: %s\n", _dbus_string_get_length (content), c_str);
996   }
997 #endif
998   
999   e = peek_element (parser);
1000   if (e == NULL)
1001     {
1002       dbus_set_error (error, DBUS_ERROR_FAILED,
1003                       "Text content outside of any XML element in configuration file");
1004       return FALSE;
1005     }
1006   else if (e->had_content)
1007     {
1008       _dbus_assert_not_reached ("Element had multiple content blocks");
1009       return FALSE;
1010     }
1011
1012   switch (top_element_type (parser))
1013     {
1014     case ELEMENT_NONE:
1015       _dbus_assert_not_reached ("element at top of stack has no type");
1016       return FALSE;
1017
1018     case ELEMENT_BUSCONFIG:
1019     case ELEMENT_POLICY:
1020     case ELEMENT_LIMIT:
1021     case ELEMENT_ALLOW:
1022     case ELEMENT_DENY:
1023     case ELEMENT_FORK:
1024       if (all_whitespace (content))
1025         return TRUE;
1026       else
1027         {
1028           dbus_set_error (error, DBUS_ERROR_FAILED,
1029                           "No text content expected inside XML element %s in configuration file",
1030                           element_type_to_name (top_element_type (parser)));
1031           return FALSE;
1032         }
1033
1034     case ELEMENT_PIDFILE:
1035       {
1036         char *s;
1037
1038         e->had_content = TRUE;
1039         
1040         if (!_dbus_string_copy_data (content, &s))
1041           goto nomem;
1042           
1043         dbus_free (parser->pidfile);
1044         parser->pidfile = s;
1045       }
1046       break;
1047
1048     case ELEMENT_INCLUDE:
1049       {
1050         DBusString full_path;
1051         
1052         e->had_content = TRUE;
1053
1054         if (!_dbus_string_init (&full_path))
1055           goto nomem;
1056         
1057         if (!make_full_path (&parser->basedir, content, &full_path))
1058           {
1059             _dbus_string_free (&full_path);
1060             goto nomem;
1061           }
1062         
1063         if (!include_file (parser, &full_path,
1064                            e->d.include.ignore_missing, error))
1065           {
1066             _dbus_string_free (&full_path);
1067             return FALSE;
1068           }
1069
1070         _dbus_string_free (&full_path);
1071       }
1072       break;
1073
1074     case ELEMENT_INCLUDEDIR:
1075       {
1076         DBusString full_path;
1077         
1078         e->had_content = TRUE;
1079
1080         if (!_dbus_string_init (&full_path))
1081           goto nomem;
1082         
1083         if (!make_full_path (&parser->basedir, content, &full_path))
1084           {
1085             _dbus_string_free (&full_path);
1086             goto nomem;
1087           }
1088         
1089         if (!include_dir (parser, &full_path, error))
1090           {
1091             _dbus_string_free (&full_path);
1092             return FALSE;
1093           }
1094
1095         _dbus_string_free (&full_path);
1096       }
1097       break;
1098       
1099     case ELEMENT_USER:
1100       {
1101         char *s;
1102
1103         e->had_content = TRUE;
1104         
1105         if (!_dbus_string_copy_data (content, &s))
1106           goto nomem;
1107           
1108         dbus_free (parser->user);
1109         parser->user = s;
1110       }
1111       break;
1112
1113     case ELEMENT_TYPE:
1114       {
1115         char *s;
1116
1117         e->had_content = TRUE;
1118
1119         if (!_dbus_string_copy_data (content, &s))
1120           goto nomem;
1121         
1122         dbus_free (parser->bus_type);
1123         parser->bus_type = s;
1124       }
1125       break;
1126       
1127     case ELEMENT_LISTEN:
1128       {
1129         char *s;
1130
1131         e->had_content = TRUE;
1132         
1133         if (!_dbus_string_copy_data (content, &s))
1134           goto nomem;
1135
1136         if (!_dbus_list_append (&parser->listen_on,
1137                                 s))
1138           {
1139             dbus_free (s);
1140             goto nomem;
1141           }
1142       }
1143       break;
1144
1145     case ELEMENT_AUTH:
1146       {
1147         char *s;
1148         
1149         e->had_content = TRUE;
1150
1151         if (!_dbus_string_copy_data (content, &s))
1152           goto nomem;
1153
1154         if (!_dbus_list_append (&parser->mechanisms,
1155                                 s))
1156           {
1157             dbus_free (s);
1158             goto nomem;
1159           }
1160       }
1161       break;
1162
1163     case ELEMENT_SERVICEDIR:
1164       {
1165         char *s;
1166         DBusString full_path;
1167         
1168         e->had_content = TRUE;
1169
1170         if (!_dbus_string_init (&full_path))
1171           goto nomem;
1172         
1173         if (!make_full_path (&parser->basedir, content, &full_path))
1174           {
1175             _dbus_string_free (&full_path);
1176             goto nomem;
1177           }
1178         
1179         if (!_dbus_string_copy_data (&full_path, &s))
1180           {
1181             _dbus_string_free (&full_path);
1182             goto nomem;
1183           }
1184
1185         if (!_dbus_list_append (&parser->service_dirs, s))
1186           {
1187             _dbus_string_free (&full_path);
1188             dbus_free (s);
1189             goto nomem;
1190           }
1191
1192         _dbus_string_free (&full_path);
1193       }
1194       break;
1195     }
1196
1197   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
1198   return TRUE;
1199
1200  nomem:
1201   BUS_SET_OOM (error);
1202   return FALSE;
1203 }
1204
1205 dbus_bool_t
1206 bus_config_parser_finished (BusConfigParser   *parser,
1207                             DBusError         *error)
1208 {
1209   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
1210
1211   if (parser->stack != NULL)
1212     {
1213       dbus_set_error (error, DBUS_ERROR_FAILED,
1214                       "Element <%s> was not closed in configuration file",
1215                       element_type_to_name (top_element_type (parser)));
1216
1217       return FALSE;
1218     }
1219
1220   if (parser->listen_on == NULL)
1221     {
1222       dbus_set_error (error, DBUS_ERROR_FAILED,
1223                       "Configuration file needs one or more <listen> elements giving addresses"); 
1224       return FALSE;
1225     }
1226   
1227   return TRUE;
1228 }
1229
1230 const char*
1231 bus_config_parser_get_user (BusConfigParser *parser)
1232 {
1233   return parser->user;
1234 }
1235
1236 const char*
1237 bus_config_parser_get_type (BusConfigParser *parser)
1238 {
1239   return parser->bus_type;
1240 }
1241
1242 DBusList**
1243 bus_config_parser_get_addresses (BusConfigParser *parser)
1244 {
1245   return &parser->listen_on;
1246 }
1247
1248 DBusList**
1249 bus_config_parser_get_mechanisms (BusConfigParser *parser)
1250 {
1251   return &parser->mechanisms;
1252 }
1253
1254 DBusList**
1255 bus_config_parser_get_service_dirs (BusConfigParser *parser)
1256 {
1257   return &parser->service_dirs;
1258 }
1259
1260 dbus_bool_t
1261 bus_config_parser_get_fork (BusConfigParser   *parser)
1262 {
1263   return parser->fork;
1264 }
1265
1266 const char *
1267 bus_config_parser_get_pidfile (BusConfigParser   *parser)
1268 {
1269   return parser->pidfile;
1270 }
1271
1272 #ifdef DBUS_BUILD_TESTS
1273 #include <stdio.h>
1274
1275 typedef enum
1276 {
1277   VALID,
1278   INVALID,
1279   UNKNOWN
1280 } Validity;
1281
1282 static dbus_bool_t
1283 do_load (const DBusString *full_path,
1284          Validity          validity,
1285          dbus_bool_t       oom_possible)
1286 {
1287   BusConfigParser *parser;
1288   DBusError error;
1289
1290   dbus_error_init (&error);
1291
1292   parser = bus_config_load (full_path, &error);
1293   if (parser == NULL)
1294     {
1295       _DBUS_ASSERT_ERROR_IS_SET (&error);
1296
1297       if (oom_possible &&
1298           dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
1299         {
1300           _dbus_verbose ("Failed to load valid file due to OOM\n");
1301           dbus_error_free (&error);
1302           return TRUE;
1303         }
1304       else if (validity == VALID)
1305         {
1306           _dbus_warn ("Failed to load valid file but still had memory: %s\n",
1307                       error.message);
1308
1309           dbus_error_free (&error);
1310           return FALSE;
1311         }
1312       else
1313         {
1314           dbus_error_free (&error);
1315           return TRUE;
1316         }
1317     }
1318   else
1319     {
1320       _DBUS_ASSERT_ERROR_IS_CLEAR (&error);
1321
1322       bus_config_parser_unref (parser);
1323
1324       if (validity == INVALID)
1325         {
1326           _dbus_warn ("Accepted invalid file\n");
1327           return FALSE;
1328         }
1329
1330       return TRUE;
1331     }
1332 }
1333
1334 typedef struct
1335 {
1336   const DBusString *full_path;
1337   Validity          validity;
1338 } LoaderOomData;
1339
1340 static dbus_bool_t
1341 check_loader_oom_func (void *data)
1342 {
1343   LoaderOomData *d = data;
1344
1345   return do_load (d->full_path, d->validity, TRUE);
1346 }
1347
1348 static dbus_bool_t
1349 process_test_subdir (const DBusString *test_base_dir,
1350                      const char       *subdir,
1351                      Validity          validity)
1352 {
1353   DBusString test_directory;
1354   DBusString filename;
1355   DBusDirIter *dir;
1356   dbus_bool_t retval;
1357   DBusError error;
1358
1359   retval = FALSE;
1360   dir = NULL;
1361
1362   if (!_dbus_string_init (&test_directory))
1363     _dbus_assert_not_reached ("didn't allocate test_directory\n");
1364
1365   _dbus_string_init_const (&filename, subdir);
1366
1367   if (!_dbus_string_copy (test_base_dir, 0,
1368                           &test_directory, 0))
1369     _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory");
1370
1371   if (!_dbus_concat_dir_and_file (&test_directory, &filename))
1372     _dbus_assert_not_reached ("couldn't allocate full path");
1373
1374   _dbus_string_free (&filename);
1375   if (!_dbus_string_init (&filename))
1376     _dbus_assert_not_reached ("didn't allocate filename string\n");
1377
1378   dbus_error_init (&error);
1379   dir = _dbus_directory_open (&test_directory, &error);
1380   if (dir == NULL)
1381     {
1382       _dbus_warn ("Could not open %s: %s\n",
1383                   _dbus_string_get_const_data (&test_directory),
1384                   error.message);
1385       dbus_error_free (&error);
1386       goto failed;
1387     }
1388
1389   printf ("Testing:\n");
1390
1391  next:
1392   while (_dbus_directory_get_next_file (dir, &filename, &error))
1393     {
1394       DBusString full_path;
1395       LoaderOomData d;
1396
1397       if (!_dbus_string_init (&full_path))
1398         _dbus_assert_not_reached ("couldn't init string");
1399
1400       if (!_dbus_string_copy (&test_directory, 0, &full_path, 0))
1401         _dbus_assert_not_reached ("couldn't copy dir to full_path");
1402
1403       if (!_dbus_concat_dir_and_file (&full_path, &filename))
1404         _dbus_assert_not_reached ("couldn't concat file to dir");
1405
1406       if (!_dbus_string_ends_with_c_str (&full_path, ".conf"))
1407         {
1408           _dbus_verbose ("Skipping non-.conf file %s\n",
1409                          _dbus_string_get_const_data (&filename));
1410           _dbus_string_free (&full_path);
1411           goto next;
1412         }
1413
1414       printf ("    %s\n", _dbus_string_get_const_data (&filename));
1415
1416       _dbus_verbose (" expecting %s\n",
1417                      validity == VALID ? "valid" :
1418                      (validity == INVALID ? "invalid" :
1419                       (validity == UNKNOWN ? "unknown" : "???")));
1420
1421       d.full_path = &full_path;
1422       d.validity = validity;
1423       if (!_dbus_test_oom_handling ("config-loader", check_loader_oom_func, &d))
1424         _dbus_assert_not_reached ("test failed");
1425
1426       _dbus_string_free (&full_path);
1427     }
1428
1429   if (dbus_error_is_set (&error))
1430     {
1431       _dbus_warn ("Could not get next file in %s: %s\n",
1432                   _dbus_string_get_const_data (&test_directory),
1433                   error.message);
1434       dbus_error_free (&error);
1435       goto failed;
1436     }
1437
1438   retval = TRUE;
1439
1440  failed:
1441
1442   if (dir)
1443     _dbus_directory_close (dir);
1444   _dbus_string_free (&test_directory);
1445   _dbus_string_free (&filename);
1446
1447   return retval;
1448 }
1449
1450 dbus_bool_t
1451 bus_config_parser_test (const DBusString *test_data_dir)
1452 {
1453   if (test_data_dir == NULL ||
1454       _dbus_string_get_length (test_data_dir) == 0)
1455     {
1456       printf ("No test data\n");
1457       return TRUE;
1458     }
1459
1460   if (!process_test_subdir (test_data_dir, "valid-config-files", VALID))
1461     return FALSE;
1462
1463   return TRUE;
1464 }
1465
1466 #endif /* DBUS_BUILD_TESTS */
1467