Merge branch 'dbus-1.2'
[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_start_element (BusConfigParser   *parser,
136                                  const char        *element_name,
137                                  const char       **attribute_names,
138                                  const char       **attribute_values,
139                                  DBusError         *error)
140 {
141   /* we don't do processing of attribute names, we don't need to */
142   parser->type = bus_config_parser_element_name_to_type (element_name);
143
144   switch (parser->type)
145     {
146     case ELEMENT_SERVICEHELPER:
147     case ELEMENT_USER:
148     case ELEMENT_TYPE:
149       /* content about to be handled */
150       break;
151
152     case ELEMENT_STANDARD_SYSTEM_SERVICEDIRS:
153       {
154         DBusList *link;
155         DBusList *dirs;
156         dirs = NULL;
157
158         if (!_dbus_get_standard_system_servicedirs (&dirs))
159           {
160             BUS_SET_OOM (error);
161             return FALSE;
162           }
163
164           while ((link = _dbus_list_pop_first_link (&dirs)))
165             service_dirs_append_link_unique_or_free (&parser->service_dirs, link);
166         break;
167       }
168
169     default:
170       {
171         /* we really don't care about the others... */
172         _dbus_verbose (" START We dont care about '%s' type '%i'\n", element_name, parser->type);
173         break;
174       }
175     }
176   return TRUE;
177 }
178
179 dbus_bool_t
180 bus_config_parser_end_element (BusConfigParser   *parser,
181                                const char               *element_name,
182                                DBusError                *error)
183 {
184   /* we don't care */
185   return TRUE;
186 }
187
188 dbus_bool_t
189 bus_config_parser_content (BusConfigParser   *parser,
190                            const DBusString  *content,
191                            DBusError         *error)
192 {
193   DBusString content_sane;
194   dbus_bool_t retval;
195
196   retval = FALSE;
197
198   if (!_dbus_string_init (&content_sane))
199     {
200       BUS_SET_OOM (error);
201       goto out;
202     }
203   if (!_dbus_string_copy (content, 0, &content_sane, 0))
204     {
205       BUS_SET_OOM (error);
206       goto out_content;
207     }
208
209   /* rip out white space */
210   _dbus_string_chop_white (&content_sane);
211   if (_dbus_string_get_length (&content_sane) == 0)
212     {
213       /* optimise, there is no content */
214       retval = TRUE;
215       goto out_content;
216     }
217
218   switch (parser->type)
219     {
220     case ELEMENT_SERVICEDIR:
221       {
222         char *cpath;
223
224         /* copy the sane data into a char array */
225         if (!_dbus_string_copy_data(&content_sane, &cpath))
226           {
227             BUS_SET_OOM (error);
228             goto out_content;
229           }
230
231         /* append the dynamic char string to service dirs */
232         if (!_dbus_list_append (&parser->service_dirs, cpath))
233           {
234             dbus_free (cpath);
235             BUS_SET_OOM (error);
236             goto out_content;
237           }
238       }
239       break;
240
241     case ELEMENT_SERVICEHELPER:
242       {
243         if (!_dbus_string_copy (&content_sane, 0, &parser->service_helper, 0))
244           {
245             BUS_SET_OOM (error);
246             goto out_content;
247           }
248       }
249       break;
250
251     case ELEMENT_USER:
252       {
253         if (!_dbus_string_copy (&content_sane, 0, &parser->user, 0))
254           {
255             BUS_SET_OOM (error);
256             goto out_content;
257           }
258       }
259       break;
260
261     case ELEMENT_TYPE:
262       {
263         if (!_dbus_string_copy (&content_sane, 0, &parser->bus_type, 0))
264           {
265             BUS_SET_OOM (error);
266             goto out_content;
267           }
268       }
269       break;
270     default:
271       {
272         /* we don't care about the others... really */
273         _dbus_verbose (" CONTENTS We dont care '%s' type '%i'\n", _dbus_string_get_const_data (&content_sane), parser->type);
274         break;
275       }
276     }
277
278   /* woot! */
279   retval = TRUE;
280
281 out_content:
282   _dbus_string_free (&content_sane);
283 out:
284   return retval;
285 }
286
287 dbus_bool_t
288 bus_config_parser_finished (BusConfigParser   *parser,
289                             DBusError         *error)
290 {
291   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
292   _dbus_verbose ("finished scanning!\n");
293   return TRUE;
294 }
295
296 const char*
297 bus_config_parser_get_user (BusConfigParser *parser)
298 {
299   return _dbus_string_get_const_data (&parser->user);
300 }
301
302 const char*
303 bus_config_parser_get_type (BusConfigParser *parser)
304 {
305   return _dbus_string_get_const_data (&parser->bus_type);
306 }
307
308 DBusList**
309 bus_config_parser_get_service_dirs (BusConfigParser *parser)
310 {
311   return &parser->service_dirs;
312 }
313
314 #ifdef DBUS_BUILD_TESTS
315 #include <stdio.h>
316 #include "test.h"
317
318 typedef enum
319 {
320   VALID,
321   INVALID,
322   UNKNOWN
323 } Validity;
324
325 static dbus_bool_t
326 check_return_values (const DBusString *full_path)
327 {
328   BusConfigParser *parser;
329   DBusError error;
330   dbus_bool_t retval;
331   const char *user;
332   const char *type;
333   DBusList **dirs;
334
335   dbus_error_init (&error);
336   retval = FALSE;
337
338   printf ("Testing values from: %s\n", _dbus_string_get_const_data (full_path));
339
340   parser = bus_config_load (full_path, TRUE, NULL, &error);
341   if (parser == NULL)
342     {
343       _DBUS_ASSERT_ERROR_IS_SET (&error);
344       if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
345         _dbus_verbose ("Failed to load valid file due to OOM\n");
346       goto finish;
347     }
348   _DBUS_ASSERT_ERROR_IS_CLEAR (&error);
349
350   /* check user return value is okay */
351   user = bus_config_parser_get_user (parser);
352   if (user == NULL)
353     {
354       _dbus_warn ("User was NULL!\n");
355       goto finish;
356     }
357 #if 0
358   /* the username can be configured in configure.in so this test doesn't work */
359   if (strcmp (user, "dbus") != 0)
360     {
361       _dbus_warn ("User was invalid; '%s'!\n", user);
362       goto finish;
363     }
364   printf ("    <user>dbus</user> OKAY!\n");  
365 #endif
366   
367   /* check type return value is okay */
368   type = bus_config_parser_get_type (parser);
369   if (type == NULL)
370     {
371       _dbus_warn ("Type was NULL!\n");
372       goto finish;
373     }
374   if (strcmp (type, "system") != 0)
375     {
376       _dbus_warn ("Type was invalid; '%s'!\n", user);
377       goto finish;
378     }
379   printf ("    <type>system</type> OKAY!\n");
380
381   /* check dirs return value is okay */
382   dirs = bus_config_parser_get_service_dirs (parser);
383   if (dirs == NULL)
384     {
385       _dbus_warn ("Service dirs are NULL!\n");
386       goto finish;
387     }
388   printf ("    <standard_system_service_dirs/> OKAY!\n");
389   /* NOTE: We tested the specific return values in the config-parser tests */
390
391   /* woohoo! */
392   retval = TRUE;
393 finish:
394   if (parser != NULL)
395     bus_config_parser_unref (parser);
396   dbus_error_free (&error);
397   return retval;
398 }
399
400 static dbus_bool_t
401 do_load (const DBusString *full_path,
402          Validity          validity,
403          dbus_bool_t       oom_possible)
404 {
405   BusConfigParser *parser;
406   DBusError error;
407
408   dbus_error_init (&error);
409
410   parser = bus_config_load (full_path, TRUE, NULL, &error);
411   if (parser == NULL)
412     {
413       _DBUS_ASSERT_ERROR_IS_SET (&error);
414
415       if (oom_possible &&
416           dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
417         {
418           _dbus_verbose ("Failed to load valid file due to OOM\n");
419           dbus_error_free (&error);
420           return TRUE;
421         }
422       else if (validity == VALID)
423         {
424           _dbus_warn ("Failed to load valid file but still had memory: %s\n",
425                       error.message);
426
427           dbus_error_free (&error);
428           return FALSE;
429         }
430       else
431         {
432           dbus_error_free (&error);
433           return TRUE;
434         }
435     }
436   else
437     {
438       _DBUS_ASSERT_ERROR_IS_CLEAR (&error);
439
440       bus_config_parser_unref (parser);
441
442       if (validity == INVALID)
443         {
444           _dbus_warn ("Accepted invalid file\n");
445           return FALSE;
446         }
447
448       return TRUE;
449     }
450 }
451
452 typedef struct
453 {
454   const DBusString *full_path;
455   Validity          validity;
456 } LoaderOomData;
457
458 static dbus_bool_t
459 check_loader_oom_func (void *data)
460 {
461   LoaderOomData *d = data;
462
463   return do_load (d->full_path, d->validity, TRUE);
464 }
465
466 static dbus_bool_t
467 process_test_valid_subdir (const DBusString *test_base_dir,
468                            const char       *subdir,
469                            Validity          validity)
470 {
471   DBusString test_directory;
472   DBusString filename;
473   DBusDirIter *dir;
474   dbus_bool_t retval;
475   DBusError error;
476
477   retval = FALSE;
478   dir = NULL;
479
480   if (!_dbus_string_init (&test_directory))
481     _dbus_assert_not_reached ("didn't allocate test_directory\n");
482
483   _dbus_string_init_const (&filename, subdir);
484
485   if (!_dbus_string_copy (test_base_dir, 0,
486                           &test_directory, 0))
487     _dbus_assert_not_reached ("couldn't copy test_base_dir to test_directory");
488
489   if (!_dbus_concat_dir_and_file (&test_directory, &filename))
490     _dbus_assert_not_reached ("couldn't allocate full path");
491
492   _dbus_string_free (&filename);
493   if (!_dbus_string_init (&filename))
494     _dbus_assert_not_reached ("didn't allocate filename string\n");
495
496   dbus_error_init (&error);
497   dir = _dbus_directory_open (&test_directory, &error);
498   if (dir == NULL)
499     {
500       _dbus_warn ("Could not open %s: %s\n",
501                   _dbus_string_get_const_data (&test_directory),
502                   error.message);
503       dbus_error_free (&error);
504       goto failed;
505     }
506
507   if (validity == VALID)
508     printf ("Testing valid files:\n");
509   else if (validity == INVALID)
510     printf ("Testing invalid files:\n");
511   else
512     printf ("Testing unknown files:\n");
513
514  next:
515   while (_dbus_directory_get_next_file (dir, &filename, &error))
516     {
517       DBusString full_path;
518       LoaderOomData d;
519
520       if (!_dbus_string_init (&full_path))
521         _dbus_assert_not_reached ("couldn't init string");
522
523       if (!_dbus_string_copy (&test_directory, 0, &full_path, 0))
524         _dbus_assert_not_reached ("couldn't copy dir to full_path");
525
526       if (!_dbus_concat_dir_and_file (&full_path, &filename))
527         _dbus_assert_not_reached ("couldn't concat file to dir");
528
529       if (!_dbus_string_ends_with_c_str (&full_path, ".conf"))
530         {
531           _dbus_verbose ("Skipping non-.conf file %s\n",
532                          _dbus_string_get_const_data (&filename));
533           _dbus_string_free (&full_path);
534           goto next;
535         }
536
537       printf ("    %s\n", _dbus_string_get_const_data (&filename));
538
539       _dbus_verbose (" expecting %s\n",
540                      validity == VALID ? "valid" :
541                      (validity == INVALID ? "invalid" :
542                       (validity == UNKNOWN ? "unknown" : "???")));
543
544       d.full_path = &full_path;
545       d.validity = validity;
546
547       /* FIXME hackaround for an expat problem, see
548        * https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=124747
549        * http://freedesktop.org/pipermail/dbus/2004-May/001153.html
550        */
551       /* if (!_dbus_test_oom_handling ("config-loader", check_loader_oom_func, &d)) */
552       if (!check_loader_oom_func (&d))
553         _dbus_assert_not_reached ("test failed");
554       
555       _dbus_string_free (&full_path);
556     }
557
558   if (dbus_error_is_set (&error))
559     {
560       _dbus_warn ("Could not get next file in %s: %s\n",
561                   _dbus_string_get_const_data (&test_directory),
562                   error.message);
563       dbus_error_free (&error);
564       goto failed;
565     }
566
567   retval = TRUE;
568
569  failed:
570
571   if (dir)
572     _dbus_directory_close (dir);
573   _dbus_string_free (&test_directory);
574   _dbus_string_free (&filename);
575
576   return retval;
577 }
578
579 /* convenience function, do not reuse outside of TEST */
580 static dbus_bool_t
581 make_full_path (const DBusString *test_data_dir,
582                                   const char       *subdir,
583                                   const char       *file,
584                                   DBusString       *full_path)
585 {
586   DBusString filename;
587   dbus_bool_t retval;
588
589   retval = FALSE;
590
591   if (!_dbus_string_init (full_path))
592     {
593       _dbus_warn ("couldn't allocate full path");
594       goto finish;
595     }
596
597   if (!_dbus_string_copy (test_data_dir, 0, full_path, 0))
598     {
599       _dbus_warn ("couldn't allocate full path");
600       goto finish;
601     }
602
603   _dbus_string_init_const (&filename, subdir);
604   if (!_dbus_concat_dir_and_file (full_path, &filename))
605     {
606       _dbus_warn ("couldn't allocate full path");
607       goto finish;
608     }
609   _dbus_string_free (&filename);
610
611   _dbus_string_init_const (&filename, file);
612   if (!_dbus_concat_dir_and_file (full_path, &filename))
613     {
614       _dbus_warn ("couldn't allocate full path");
615       goto finish;
616     }
617
618   /* woot! */
619   retval = TRUE;
620
621 finish:
622   _dbus_string_free (&filename);
623   return retval;
624 }
625
626 static dbus_bool_t
627 check_file_valid (DBusString *full_path,
628                                     Validity    validity)
629 {
630   dbus_bool_t retval;
631
632   if (validity == VALID)
633     printf ("Testing valid file:\n");
634   else if (validity == INVALID)
635     printf ("Testing invalid file:\n");
636   else
637     printf ("Testing unknown file:\n");
638
639   /* print the filename, just so we match the other output */
640   printf ("    %s\n", _dbus_string_get_const_data (full_path));
641
642   /* only test one file */
643   retval = do_load (full_path, validity, TRUE);
644
645   return retval;
646 }
647
648 dbus_bool_t
649 bus_config_parser_trivial_test (const DBusString *test_data_dir)
650 {
651   DBusString full_path;
652   dbus_bool_t retval;
653
654   retval = FALSE;
655
656   if (test_data_dir == NULL ||
657       _dbus_string_get_length (test_data_dir) == 0)
658     {
659       printf ("No test data\n");
660       return TRUE;
661     }
662   
663   /* We already test default_session_servicedirs and default_system_servicedirs
664    * in bus_config_parser_test() */
665   if (!process_test_valid_subdir (test_data_dir, "valid-config-files", VALID))
666     goto finish;
667
668   /* we don't process all the invalid files, as the trivial parser can't hope
669    * to validate them all for all different syntaxes. We just check one broken
670    * file to see if junk is received */
671   if (!make_full_path (test_data_dir, "invalid-config-files", "not-well-formed.conf", &full_path))
672     goto finish;
673   if (!check_file_valid (&full_path, INVALID))
674     goto finish;
675   _dbus_string_free (&full_path);
676
677   /* just test if the check_file_valid works okay and we got sane values */
678   if (!make_full_path (test_data_dir, "valid-config-files", "system.conf", &full_path))
679     goto finish;
680   if (!check_file_valid (&full_path, VALID))
681     goto finish;
682   /* check to see if we got the correct values from the parser */
683   if (!check_return_values (&full_path))
684     goto finish;
685
686   /* woot! */
687   retval = TRUE;
688
689 finish:
690   _dbus_string_free (&full_path);
691
692   /* we don't process equiv-config-files as we don't handle <include> */
693   return retval;
694 }
695
696 #endif /* DBUS_BUILD_TESTS */
697