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