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