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