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