Implement doctype check in the trivial parser
[platform/upstream/dbus.git] / bus / config-parser-trivial.c
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /* config-parser-trivial.c  XML-library-agnostic configuration file parser
3  *                  Does not do includes or anything remotely complicated.
4  *
5  * Copyright (C) 2003, 2004, 2007 Red Hat, Inc.
6  *
7  * Licensed under the Academic Free License version 2.1
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22  *
23  */
24  
25 #include "config-parser-common.h"
26 #include "config-parser-trivial.h"
27 #include "utils.h"
28 #include <dbus/dbus-list.h>
29 #include <dbus/dbus-internals.h>
30 #include <string.h>
31
32 /**
33  * TRIVIAL parser for bus configuration file.
34  */
35 struct BusConfigParser
36 {
37   ElementType type;
38   DBusString user;                  /**< User the dbus-daemon runs as */
39   DBusString bus_type;              /**< Message bus type */
40   DBusString service_helper;        /**< Location of the setuid helper */
41   DBusList *service_dirs;           /**< Directories to look for services in */
42 };
43
44 static dbus_bool_t
45 service_dirs_find_dir (DBusList **service_dirs,
46                        const char *dir)
47 {
48   DBusList *link;
49
50   _dbus_assert (dir != NULL);
51
52   for (link = *service_dirs; link; link = _dbus_list_get_next_link(service_dirs, link))
53     {
54       const char *link_dir;
55
56       link_dir = (const char *)link->data;
57       if (strcmp (dir, link_dir) == 0)
58         return TRUE;
59     }
60
61   return FALSE;
62 }
63
64 static void
65 service_dirs_append_link_unique_or_free (DBusList **service_dirs,
66                                          DBusList *dir_link)
67 {
68   if (!service_dirs_find_dir (service_dirs, dir_link->data))
69     {
70       _dbus_list_append_link (service_dirs, dir_link);
71     }
72   else
73     {
74       dbus_free (dir_link->data);
75       _dbus_list_free_link (dir_link);
76     }
77 }
78
79 BusConfigParser*
80 bus_config_parser_new (const DBusString             *basedir,
81                        dbus_bool_t                   is_toplevel,
82                        const BusConfigParser        *parent)
83 {
84   BusConfigParser *parser;
85
86   parser = dbus_new0 (BusConfigParser, 1);
87   if (parser == NULL)
88     goto failed;
89
90   parser->type = ELEMENT_NONE;
91
92   /* init the lists */
93   parser->service_dirs = NULL;
94
95   /* init the strings */
96   if (!_dbus_string_init (&parser->user))
97     goto failed_parser;
98   if (!_dbus_string_init (&parser->bus_type))
99     goto failed_type;
100   if (!_dbus_string_init (&parser->service_helper))
101     goto failed_helper;
102
103   /* woot! */
104   return parser;
105
106 /* argh. we have do do this carefully because of OOM */
107 failed_helper:
108   _dbus_string_free (&parser->bus_type);
109 failed_type:
110   _dbus_string_free (&parser->user);
111 failed_parser:
112   dbus_free (parser);
113 failed:
114   return NULL;
115 }
116
117 void
118 bus_config_parser_unref (BusConfigParser *parser)
119 {
120   _dbus_string_free (&parser->user);
121   _dbus_string_free (&parser->service_helper);
122   _dbus_string_free (&parser->bus_type);
123
124   _dbus_list_foreach (&parser->service_dirs,
125                       (DBusForeachFunction) dbus_free,
126                       NULL);
127
128   _dbus_list_clear (&parser->service_dirs);
129
130   dbus_free (parser);
131 }
132
133 dbus_bool_t
134 bus_config_parser_check_doctype (BusConfigParser   *parser,
135                                  const char        *doctype,
136                                  DBusError         *error)
137 {
138   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
139
140   if (strcmp (doctype, "busconfig") != 0)
141     {
142       dbus_set_error (error,
143                       DBUS_ERROR_FAILED,
144                       "Configuration file has the wrong document type %s",
145                       doctype);
146       return FALSE;
147     }
148   else
149     return TRUE;
150 }
151
152 dbus_bool_t
153 bus_config_parser_start_element (BusConfigParser   *parser,
154                                  const char        *element_name,
155                                  const char       **attribute_names,
156                                  const char       **attribute_values,
157                                  DBusError         *error)
158 {
159   /* we don't do processing of attribute names, we don't need to */
160   parser->type = bus_config_parser_element_name_to_type (element_name);
161
162   switch (parser->type)
163     {
164     case ELEMENT_SERVICEHELPER:
165     case ELEMENT_USER:
166     case ELEMENT_TYPE:
167       /* content about to be handled */
168       break;
169
170     case ELEMENT_STANDARD_SYSTEM_SERVICEDIRS:
171       {
172         DBusList *link;
173         DBusList *dirs;
174         dirs = NULL;
175
176         if (!_dbus_get_standard_system_servicedirs (&dirs))
177           {
178             BUS_SET_OOM (error);
179             return FALSE;
180           }
181
182           while ((link = _dbus_list_pop_first_link (&dirs)))
183             service_dirs_append_link_unique_or_free (&parser->service_dirs, link);
184         break;
185       }
186
187     default:
188       {
189         /* we really don't care about the others... */
190         _dbus_verbose (" START We dont care about '%s' type '%i'\n", element_name, parser->type);
191         break;
192       }
193     }
194   return TRUE;
195 }
196
197 dbus_bool_t
198 bus_config_parser_end_element (BusConfigParser   *parser,
199                                const char               *element_name,
200                                DBusError                *error)
201 {
202   /* we don't care */
203   return TRUE;
204 }
205
206 dbus_bool_t
207 bus_config_parser_content (BusConfigParser   *parser,
208                            const DBusString  *content,
209                            DBusError         *error)
210 {
211   DBusString content_sane;
212   dbus_bool_t retval;
213
214   retval = FALSE;
215
216   if (!_dbus_string_init (&content_sane))
217     {
218       BUS_SET_OOM (error);
219       goto out;
220     }
221   if (!_dbus_string_copy (content, 0, &content_sane, 0))
222     {
223       BUS_SET_OOM (error);
224       goto out_content;
225     }
226
227   /* rip out white space */
228   _dbus_string_chop_white (&content_sane);
229   if (_dbus_string_get_length (&content_sane) == 0)
230     {
231       /* optimise, there is no content */
232       retval = TRUE;
233       goto out_content;
234     }
235
236   switch (parser->type)
237     {
238     case ELEMENT_SERVICEDIR:
239       {
240         char *cpath;
241
242         /* copy the sane data into a char array */
243         if (!_dbus_string_copy_data(&content_sane, &cpath))
244           {
245             BUS_SET_OOM (error);
246             goto out_content;
247           }
248
249         /* append the dynamic char string to service dirs */
250         if (!_dbus_list_append (&parser->service_dirs, cpath))
251           {
252             dbus_free (cpath);
253             BUS_SET_OOM (error);
254             goto out_content;
255           }
256       }
257       break;
258
259     case ELEMENT_SERVICEHELPER:
260       {
261         if (!_dbus_string_copy (&content_sane, 0, &parser->service_helper, 0))
262           {
263             BUS_SET_OOM (error);
264             goto out_content;
265           }
266       }
267       break;
268
269     case ELEMENT_USER:
270       {
271         if (!_dbus_string_copy (&content_sane, 0, &parser->user, 0))
272           {
273             BUS_SET_OOM (error);
274             goto out_content;
275           }
276       }
277       break;
278
279     case ELEMENT_TYPE:
280       {
281         if (!_dbus_string_copy (&content_sane, 0, &parser->bus_type, 0))
282           {
283             BUS_SET_OOM (error);
284             goto out_content;
285           }
286       }
287       break;
288     default:
289       {
290         /* we don't care about the others... really */
291         _dbus_verbose (" CONTENTS We dont care '%s' type '%i'\n", _dbus_string_get_const_data (&content_sane), parser->type);
292         break;
293       }
294     }
295
296   /* woot! */
297   retval = TRUE;
298
299 out_content:
300   _dbus_string_free (&content_sane);
301 out:
302   return retval;
303 }
304
305 dbus_bool_t
306 bus_config_parser_finished (BusConfigParser   *parser,
307                             DBusError         *error)
308 {
309   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
310   _dbus_verbose ("finished scanning!\n");
311   return TRUE;
312 }
313
314 const char*
315 bus_config_parser_get_user (BusConfigParser *parser)
316 {
317   return _dbus_string_get_const_data (&parser->user);
318 }
319
320 const char*
321 bus_config_parser_get_type (BusConfigParser *parser)
322 {
323   return _dbus_string_get_const_data (&parser->bus_type);
324 }
325
326 DBusList**
327 bus_config_parser_get_service_dirs (BusConfigParser *parser)
328 {
329   return &parser->service_dirs;
330 }
331
332 #ifdef DBUS_BUILD_TESTS
333 #include <stdio.h>
334 #include "test.h"
335
336 typedef enum
337 {
338   VALID,
339   INVALID,
340   UNKNOWN
341 } Validity;
342
343 static dbus_bool_t
344 check_return_values (const DBusString *full_path)
345 {
346   BusConfigParser *parser;
347   DBusError error;
348   dbus_bool_t retval;
349   const char *user;
350   const char *type;
351   DBusList **dirs;
352
353   dbus_error_init (&error);
354   retval = FALSE;
355
356   printf ("Testing values from: %s\n", _dbus_string_get_const_data (full_path));
357
358   parser = bus_config_load (full_path, TRUE, NULL, &error);
359   if (parser == NULL)
360     {
361       _DBUS_ASSERT_ERROR_IS_SET (&error);
362       if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
363         _dbus_verbose ("Failed to load valid file due to OOM\n");
364       goto finish;
365     }
366   _DBUS_ASSERT_ERROR_IS_CLEAR (&error);
367
368   /* check user return value is okay */
369   user = bus_config_parser_get_user (parser);
370   if (user == NULL)
371     {
372       _dbus_warn ("User was NULL!\n");
373       goto finish;
374     }
375 #if 0
376   /* the username can be configured in configure.in so this test doesn't work */
377   if (strcmp (user, "dbus") != 0)
378     {
379       _dbus_warn ("User was invalid; '%s'!\n", user);
380       goto finish;
381     }
382   printf ("    <user>dbus</user> OKAY!\n");  
383 #endif
384   
385   /* check type return value is okay */
386   type = bus_config_parser_get_type (parser);
387   if (type == NULL)
388     {
389       _dbus_warn ("Type was NULL!\n");
390       goto finish;
391     }
392   if (strcmp (type, "system") != 0)
393     {
394       _dbus_warn ("Type was invalid; '%s'!\n", user);
395       goto finish;
396     }
397   printf ("    <type>system</type> OKAY!\n");
398
399   /* check dirs return value is okay */
400   dirs = bus_config_parser_get_service_dirs (parser);
401   if (dirs == NULL)
402     {
403       _dbus_warn ("Service dirs are NULL!\n");
404       goto finish;
405     }
406   printf ("    <standard_system_service_dirs/> OKAY!\n");
407   /* NOTE: We tested the specific return values in the config-parser tests */
408
409   /* woohoo! */
410   retval = TRUE;
411 finish:
412   if (parser != NULL)
413     bus_config_parser_unref (parser);
414   dbus_error_free (&error);
415   return retval;
416 }
417
418 static dbus_bool_t
419 do_load (const DBusString *full_path,
420          Validity          validity,
421          dbus_bool_t       oom_possible)
422 {
423   BusConfigParser *parser;
424   DBusError error;
425
426   dbus_error_init (&error);
427
428   parser = bus_config_load (full_path, TRUE, NULL, &error);
429   if (parser == NULL)
430     {
431       _DBUS_ASSERT_ERROR_IS_SET (&error);
432
433       if (oom_possible &&
434           dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
435         {
436           _dbus_verbose ("Failed to load valid file due to OOM\n");
437           dbus_error_free (&error);
438           return TRUE;
439         }
440       else if (validity == VALID)
441         {
442           _dbus_warn ("Failed to load valid file but still had memory: %s\n",
443                       error.message);
444
445           dbus_error_free (&error);
446           return FALSE;
447         }
448       else
449         {
450           dbus_error_free (&error);
451           return TRUE;
452         }
453     }
454   else
455     {
456       _DBUS_ASSERT_ERROR_IS_CLEAR (&error);
457
458       bus_config_parser_unref (parser);
459
460       if (validity == INVALID)
461         {
462           _dbus_warn ("Accepted invalid file\n");
463           return FALSE;
464         }
465
466       return TRUE;
467     }
468 }
469
470 typedef struct
471 {
472   const DBusString *full_path;
473   Validity          validity;
474 } LoaderOomData;
475
476 static dbus_bool_t
477 check_loader_oom_func (void *data)
478 {
479   LoaderOomData *d = data;
480
481   return do_load (d->full_path, d->validity, TRUE);
482 }
483
484 static dbus_bool_t
485 process_test_valid_subdir (const DBusString *test_base_dir,
486                            const char       *subdir,
487                            Validity          validity)
488 {
489   DBusString test_directory;
490   DBusString filename;
491   DBusDirIter *dir;
492   dbus_bool_t retval;
493   DBusError error;
494
495   retval = FALSE;
496   dir = NULL;
497
498   if (!_dbus_string_init (&test_directory))
499     _dbus_assert_not_reached ("didn't allocate test_directory\n");
500
501   _dbus_string_init_const (&filename, subdir);
502
503   if (!_dbus_string_copy (test_base_dir, 0,
504                           &test_directory, 0))
505     _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory");
506
507   if (!_dbus_concat_dir_and_file (&test_directory, &filename))
508     _dbus_assert_not_reached ("couldn't allocate full path");
509
510   _dbus_string_free (&filename);
511   if (!_dbus_string_init (&filename))
512     _dbus_assert_not_reached ("didn't allocate filename string\n");
513
514   dbus_error_init (&error);
515   dir = _dbus_directory_open (&test_directory, &error);
516   if (dir == NULL)
517     {
518       _dbus_warn ("Could not open %s: %s\n",
519                   _dbus_string_get_const_data (&test_directory),
520                   error.message);
521       dbus_error_free (&error);
522       goto failed;
523     }
524
525   if (validity == VALID)
526     printf ("Testing valid files:\n");
527   else if (validity == INVALID)
528     printf ("Testing invalid files:\n");
529   else
530     printf ("Testing unknown files:\n");
531
532  next:
533   while (_dbus_directory_get_next_file (dir, &filename, &error))
534     {
535       DBusString full_path;
536       LoaderOomData d;
537
538       if (!_dbus_string_init (&full_path))
539         _dbus_assert_not_reached ("couldn't init string");
540
541       if (!_dbus_string_copy (&test_directory, 0, &full_path, 0))
542         _dbus_assert_not_reached ("couldn't copy dir to full_path");
543
544       if (!_dbus_concat_dir_and_file (&full_path, &filename))
545         _dbus_assert_not_reached ("couldn't concat file to dir");
546
547       if (!_dbus_string_ends_with_c_str (&full_path, ".conf"))
548         {
549           _dbus_verbose ("Skipping non-.conf file %s\n",
550                          _dbus_string_get_const_data (&filename));
551           _dbus_string_free (&full_path);
552           goto next;
553         }
554
555       printf ("    %s\n", _dbus_string_get_const_data (&filename));
556
557       _dbus_verbose (" expecting %s\n",
558                      validity == VALID ? "valid" :
559                      (validity == INVALID ? "invalid" :
560                       (validity == UNKNOWN ? "unknown" : "???")));
561
562       d.full_path = &full_path;
563       d.validity = validity;
564
565       /* FIXME hackaround for an expat problem, see
566        * https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=124747
567        * http://freedesktop.org/pipermail/dbus/2004-May/001153.html
568        */
569       /* if (!_dbus_test_oom_handling ("config-loader", check_loader_oom_func, &d)) */
570       if (!check_loader_oom_func (&d))
571         _dbus_assert_not_reached ("test failed");
572       
573       _dbus_string_free (&full_path);
574     }
575
576   if (dbus_error_is_set (&error))
577     {
578       _dbus_warn ("Could not get next file in %s: %s\n",
579                   _dbus_string_get_const_data (&test_directory),
580                   error.message);
581       dbus_error_free (&error);
582       goto failed;
583     }
584
585   retval = TRUE;
586
587  failed:
588
589   if (dir)
590     _dbus_directory_close (dir);
591   _dbus_string_free (&test_directory);
592   _dbus_string_free (&filename);
593
594   return retval;
595 }
596
597 /* convenience function, do not reuse outside of TEST */
598 static dbus_bool_t
599 make_full_path (const DBusString *test_data_dir,
600                                   const char       *subdir,
601                                   const char       *file,
602                                   DBusString       *full_path)
603 {
604   DBusString filename;
605   dbus_bool_t retval;
606
607   retval = FALSE;
608
609   if (!_dbus_string_init (full_path))
610     {
611       _dbus_warn ("couldn't allocate full path");
612       goto finish;
613     }
614
615   if (!_dbus_string_copy (test_data_dir, 0, full_path, 0))
616     {
617       _dbus_warn ("couldn't allocate full path");
618       goto finish;
619     }
620
621   _dbus_string_init_const (&filename, subdir);
622   if (!_dbus_concat_dir_and_file (full_path, &filename))
623     {
624       _dbus_warn ("couldn't allocate full path");
625       goto finish;
626     }
627   _dbus_string_free (&filename);
628
629   _dbus_string_init_const (&filename, file);
630   if (!_dbus_concat_dir_and_file (full_path, &filename))
631     {
632       _dbus_warn ("couldn't allocate full path");
633       goto finish;
634     }
635
636   /* woot! */
637   retval = TRUE;
638
639 finish:
640   _dbus_string_free (&filename);
641   return retval;
642 }
643
644 static dbus_bool_t
645 check_file_valid (DBusString *full_path,
646                                     Validity    validity)
647 {
648   dbus_bool_t retval;
649
650   if (validity == VALID)
651     printf ("Testing valid file:\n");
652   else if (validity == INVALID)
653     printf ("Testing invalid file:\n");
654   else
655     printf ("Testing unknown file:\n");
656
657   /* print the filename, just so we match the other output */
658   printf ("    %s\n", _dbus_string_get_const_data (full_path));
659
660   /* only test one file */
661   retval = do_load (full_path, validity, TRUE);
662
663   return retval;
664 }
665
666 dbus_bool_t
667 bus_config_parser_trivial_test (const DBusString *test_data_dir)
668 {
669   DBusString full_path;
670   dbus_bool_t retval;
671
672   retval = FALSE;
673
674   if (test_data_dir == NULL ||
675       _dbus_string_get_length (test_data_dir) == 0)
676     {
677       printf ("No test data\n");
678       return TRUE;
679     }
680   
681   /* We already test default_session_servicedirs and default_system_servicedirs
682    * in bus_config_parser_test() */
683   if (!process_test_valid_subdir (test_data_dir, "valid-config-files", VALID))
684     goto finish;
685
686   /* we don't process all the invalid files, as the trivial parser can't hope
687    * to validate them all for all different syntaxes. We just check one broken
688    * file to see if junk is received */
689   if (!make_full_path (test_data_dir, "invalid-config-files", "not-well-formed.conf", &full_path))
690     goto finish;
691   if (!check_file_valid (&full_path, INVALID))
692     goto finish;
693   _dbus_string_free (&full_path);
694
695   /* just test if the check_file_valid works okay and we got sane values */
696   if (!make_full_path (test_data_dir, "valid-config-files", "system.conf", &full_path))
697     goto finish;
698   if (!check_file_valid (&full_path, VALID))
699     goto finish;
700   /* check to see if we got the correct values from the parser */
701   if (!check_return_values (&full_path))
702     goto finish;
703
704   /* woot! */
705   retval = TRUE;
706
707 finish:
708   _dbus_string_free (&full_path);
709
710   /* we don't process equiv-config-files as we don't handle <include> */
711   return retval;
712 }
713
714 #endif /* DBUS_BUILD_TESTS */
715