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