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