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