1 /* ====================================================================
2  * The Kannel Software License, Version 1.0
3  *
4  * Copyright (c) 2001-2014 Kannel Group
5  * Copyright (c) 1998-2001 WapIT Ltd.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  *    if any, must include the following acknowledgment:
22  *       "This product includes software developed by the
23  *        Kannel Group (http://www.kannel.org/)."
24  *    Alternately, this acknowledgment may appear in the software itself,
25  *    if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Kannel" and "Kannel Group" must not be used to
28  *    endorse or promote products derived from this software without
29  *    prior written permission. For written permission, please
30  *    contact org@kannel.org.
31  *
32  * 5. Products derived from this software may not be called "Kannel",
33  *    nor may "Kannel" appear in their name, without prior written
34  *    permission of the Kannel Group.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED.  IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS
40  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
41  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
42  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
43  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
44  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
45  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
46  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47  * ====================================================================
48  *
49  * This software consists of voluntary contributions made by many
50  * individuals on behalf of the Kannel Group.  For more information on
51  * the Kannel Group, please see <http://www.kannel.org/>.
52  *
53  * Portions of this software are based upon software originally written at
54  * WapIT Ltd., Helsinki, Finland for the Kannel project.
55  */
56 
57 /*
58  * cfg.c - configuration file handling
59  *
60  * Lars Wirzenius
61  */
62 
63 
64 #include "gwlib/gwlib.h"
65 
66 /* for include dir */
67 #include <sys/types.h>
68 #include <sys/stat.h>
69 #include <unistd.h>
70 #include <dirent.h>
71 #include <errno.h>
72 
73 struct CfgGroup {
74     Octstr *name;
75     Dict *vars;
76     Octstr *configfile;
77     long line;
78 };
79 
80 
create_group(void)81 static CfgGroup *create_group(void)
82 {
83     CfgGroup *grp;
84 
85     grp = gw_malloc(sizeof(*grp));
86     grp->name = NULL;
87     grp->vars = dict_create(64, octstr_destroy_item);
88     grp->configfile = NULL;
89     grp->line = 0;
90     return grp;
91 }
92 
destroy_group(void * arg)93 static void destroy_group(void *arg)
94 {
95     CfgGroup *grp;
96 
97     if (arg != NULL) {
98 	grp = arg;
99 	octstr_destroy(grp->name);
100 	octstr_destroy(grp->configfile);
101 	dict_destroy(grp->vars);
102 	gw_free(grp);
103     }
104 }
105 
106 
107 struct CfgLoc {
108     Octstr *filename;
109     long line_no;
110     Octstr *line;
111 };
112 
113 
cfgloc_create(Octstr * filename)114 static CfgLoc *cfgloc_create(Octstr *filename)
115 {
116     CfgLoc *cfgloc;
117 
118     cfgloc = gw_malloc(sizeof(*cfgloc));
119     cfgloc->filename = octstr_duplicate(filename);
120     cfgloc->line_no = 0;
121     cfgloc->line = NULL;
122     return cfgloc;
123 }
124 
125 
cfgloc_destroy(CfgLoc * cfgloc)126 static void cfgloc_destroy(CfgLoc *cfgloc)
127 {
128     if (cfgloc != NULL) {
129 	octstr_destroy(cfgloc->filename);
130 	octstr_destroy(cfgloc->line);
131 	gw_free(cfgloc);
132     }
133 }
134 
135 
destroy_group_list(void * arg)136 static void destroy_group_list(void *arg)
137 {
138     gwlist_destroy(arg, destroy_group);
139 }
140 
141 
set_group_name(CfgGroup * grp,Octstr * name)142 static void set_group_name(CfgGroup *grp, Octstr *name)
143 {
144     octstr_destroy(grp->name);
145     grp->name = octstr_duplicate(name);
146 }
147 
148 
149 struct Cfg {
150     Octstr *filename;
151     Dict *single_groups;
152     Dict *multi_groups;
153 };
154 
155 
156 /********************************************************************
157  * Section providing hooks to external modules to apply their specific
158  * is_allowed_in_group() and is_single_group() with their own
159  * foobar-cfg.def.
160  */
161 
162 static List *allowed_hooks;
163 static List *single_hooks;
164 
core_is_allowed_in_group(Octstr * group,Octstr * variable)165 static int core_is_allowed_in_group(Octstr *group, Octstr *variable)
166 {
167     Octstr *groupstr;
168 
169     groupstr = octstr_imm("group");
170 
171     #define OCTSTR(name) \
172     	if (octstr_compare(octstr_imm(#name), variable) == 0) \
173 	    return 1;
174     #define SINGLE_GROUP(name, fields) \
175     	if (octstr_compare(octstr_imm(#name), group) == 0) { \
176 	    if (octstr_compare(groupstr, variable) == 0) \
177 		return 1; \
178 	    fields \
179 	    return 0; \
180 	}
181     #define MULTI_GROUP(name, fields) \
182     	if (octstr_compare(octstr_imm(#name), group) == 0) { \
183 	    if (octstr_compare(groupstr, variable) == 0) \
184 		return 1; \
185 	    fields \
186 	    return 0; \
187 	}
188     #include "cfg.def"
189 
190     /* unknown group identifier */
191     return -1;
192 }
193 
194 
core_is_single_group(Octstr * query)195 static int core_is_single_group(Octstr *query)
196 {
197     #define OCTSTR(name)
198     #define SINGLE_GROUP(name, fields) \
199     	if (octstr_compare(octstr_imm(#name), query) == 0) \
200 	    return 1;
201     #define MULTI_GROUP(name, fields) \
202     	if (octstr_compare(octstr_imm(#name), query) == 0) \
203 	    return 0;
204     #include "cfg.def"
205     return 0;
206 }
207 
208 
is_allowed_in_group(Octstr * group,Octstr * variable)209 static int is_allowed_in_group(Octstr *group, Octstr *variable)
210 {
211     long i;
212     int x, r = -1;
213 
214     for (i = 0; i < gwlist_len(allowed_hooks); ++i) {
215         x = ((int(*)(Octstr *, Octstr *))
216             gwlist_get(allowed_hooks, i))(group, variable);
217         r = (x == -1 ? (r == -1 ? x : r) : (r == -1 ? x : r + x));
218     }
219 
220     return r;
221 }
222 
223 
is_single_group(Octstr * query)224 static int is_single_group(Octstr *query)
225 {
226     long i;
227     int r = 0;
228 
229     for (i = 0; i < gwlist_len(single_hooks); ++i) {
230         r += ((int(*)(Octstr *))
231             gwlist_get(single_hooks, i))(query);
232     }
233 
234     return (r > 0);
235 }
236 
237 
cfg_add_hooks(void * allowed,void * single)238 void cfg_add_hooks(void *allowed, void *single)
239 {
240     gwlist_append(allowed_hooks, allowed);
241     gwlist_append(single_hooks, single);
242 }
243 
244 
add_group(Cfg * cfg,CfgGroup * grp)245 static int add_group(Cfg *cfg, CfgGroup *grp)
246 {
247     Octstr *groupname;
248     Octstr *name;
249     List *names;
250     List *list;
251 
252     groupname = cfg_get(grp, octstr_imm("group"));
253     if (groupname == NULL) {
254         error(0, "Group does not contain variable 'group'.");
255         return -1;
256     }
257     set_group_name(grp, groupname);
258 
259     names = dict_keys(grp->vars);
260 
261     while ((name = gwlist_extract_first(names)) != NULL) {
262         int a = is_allowed_in_group(groupname, name);
263         switch (a) {
264             case 0:
265                 error(0, "Group '%s' may not contain field '%s'.",
266                       octstr_get_cstr(groupname), octstr_get_cstr(name));
267                 octstr_destroy(name);
268                 octstr_destroy(groupname);
269                 gwlist_destroy(names, octstr_destroy_item);
270                 return -1;
271                 break;
272             case -1:
273                 error(0, "Group '%s' is no valid group identifier.",
274                       octstr_get_cstr(groupname));
275                 octstr_destroy(name);
276                 octstr_destroy(groupname);
277                 gwlist_destroy(names, octstr_destroy_item);
278                 return -1;
279                 break;
280             default:
281                 octstr_destroy(name);
282                 break;
283         }
284     }
285     gwlist_destroy(names, NULL);
286 
287     if (is_single_group(groupname)) {
288         dict_put(cfg->single_groups, groupname, grp);
289     } else {
290         list = dict_get(cfg->multi_groups, groupname);
291         if (list == NULL) {
292             list = gwlist_create();
293             dict_put(cfg->multi_groups, groupname, list);
294         }
295         gwlist_append(list, grp);
296     }
297 
298     octstr_destroy(groupname);
299     return 0;
300 }
301 
302 
cfg_create(Octstr * filename)303 Cfg *cfg_create(Octstr *filename)
304 {
305     Cfg *cfg;
306 
307     cfg = gw_malloc(sizeof(*cfg));
308     cfg->filename = octstr_duplicate(filename);
309     cfg->single_groups = dict_create(64, destroy_group);
310     cfg->multi_groups = dict_create(64, destroy_group_list);
311 
312     return cfg;
313 }
314 
315 
cfg_destroy(Cfg * cfg)316 void cfg_destroy(Cfg *cfg)
317 {
318     if (cfg != NULL) {
319         octstr_destroy(cfg->filename);
320         dict_destroy(cfg->single_groups);
321         dict_destroy(cfg->multi_groups);
322         gw_free(cfg);
323     }
324 }
325 
326 
parse_value(Octstr * value)327 static void parse_value(Octstr *value)
328 {
329     Octstr *temp;
330     long len;
331     int c;
332 
333     octstr_strip_blanks(value);
334 
335     len = octstr_len(value);
336     if (octstr_get_char(value, 0) != '"' ||
337         octstr_get_char(value, len - 1) != '"')
338 	return;
339 
340     octstr_delete(value, len - 1, 1);
341     octstr_delete(value, 0, 1);
342 
343     temp = octstr_duplicate(value);
344     octstr_truncate(value, 0);
345 
346     while (octstr_len(temp) > 0) {
347 	c = octstr_get_char(temp, 0);
348 	octstr_delete(temp, 0, 1);
349 
350     	if (c != '\\' || octstr_len(temp) == 0)
351 	    octstr_append_char(value, c);
352 	else {
353 	    c = octstr_get_char(temp, 0);
354 	    octstr_delete(temp, 0, 1);
355 
356 	    switch (c) {
357     	    case '\\':
358     	    case '"':
359 	    	octstr_append_char(value, c);
360 	    	break;
361 
362     	    default:
363 	    	octstr_append_char(value, '\\');
364 	    	octstr_append_char(value, c);
365 		break;
366 	    }
367 	}
368     }
369 
370     octstr_destroy(temp);
371 }
372 
373 
expand_file(Octstr * file,int forward)374 static List *expand_file(Octstr *file, int forward)
375 {
376     Octstr *os;
377     Octstr *line;
378     List *lines;
379     List *expand;
380     long lineno;
381     CfgLoc *loc = NULL;
382 
383     os = octstr_read_file(octstr_get_cstr(file));
384     if (os == NULL)
385     	return NULL;
386 
387     lines = octstr_split(os, octstr_imm("\n"));
388     lineno = 0;
389     expand = gwlist_create();
390 
391     while ((line = gwlist_extract_first(lines)) != NULL) {
392     	if (loc == NULL) {
393             ++lineno;
394             loc = cfgloc_create(file);
395             loc->line_no = lineno;
396             loc->line = octstr_create("");
397             if (forward)
398                 gwlist_append(expand, loc);
399             else
400                 gwlist_insert(expand, 0, loc);
401         }
402         /* check for escape and then add to existing loc */
403         if (octstr_get_char(line, octstr_len(line) - 1) == '\\') {
404             octstr_delete(line, octstr_len(line) - 1, 1);
405             octstr_append(loc->line, line);
406             /* check for second escape */
407             if (octstr_get_char(line, octstr_len(line) - 1) == '\\')
408                 loc = NULL;
409         } else {
410             octstr_append(loc->line, line);
411             loc = NULL;
412         }
413         octstr_destroy(line);
414     }
415 
416     /*
417      * add newline at each end of included files to avoid
418      * concatenating different groups by mistake
419      */
420     if (lineno > 0) {
421         loc = cfgloc_create(file);
422         loc->line_no = lineno;
423         loc->line = octstr_create("\n");
424         if (forward)
425             gwlist_append(expand, loc);
426         else
427             gwlist_insert(expand, 0, loc);
428     }
429 
430     gwlist_destroy(lines, octstr_destroy_item);
431     octstr_destroy(os);
432 
433     return expand;
434 }
435 
436 
cfg_read(Cfg * cfg)437 int cfg_read(Cfg *cfg)
438 {
439     CfgLoc *loc;
440     CfgLoc *loc_inc;
441     List *lines;
442     List *expand;
443     List *stack;
444     Octstr *name;
445     Octstr *value;
446     Octstr *filename;
447     CfgGroup *grp;
448     long equals;
449     long error_lineno;
450 
451     loc = loc_inc = NULL;
452 
453     /*
454      * expand initial main config file and add it to the recursion
455      * stack to protect against cycling
456      */
457     if ((lines = expand_file(cfg->filename, 1)) == NULL) {
458         panic(0, "Failed to load main configuration file `%s'. Aborting!",
459               octstr_get_cstr(cfg->filename));
460     }
461     stack = gwlist_create();
462     gwlist_insert(stack, 0, octstr_duplicate(cfg->filename));
463 
464     grp = NULL;
465     error_lineno = 0;
466     while (error_lineno == 0 && (loc = gwlist_extract_first(lines)) != NULL) {
467         octstr_strip_blanks(loc->line);
468         if (octstr_len(loc->line) == 0) {
469             if (grp != NULL && add_group(cfg, grp) == -1) {
470                 error_lineno = loc->line_no;
471                 destroy_group(grp);
472             }
473             grp = NULL;
474         } else if (octstr_get_char(loc->line, 0) != '#') {
475             equals = octstr_search_char(loc->line, '=', 0);
476             if (equals == -1) {
477                 error(0, "An equals sign ('=') is missing on line %ld of file %s.",
478                       loc->line_no, octstr_get_cstr(loc->filename));
479                 error_lineno = loc->line_no;
480             } else
481 
482             /*
483              * check for special config directives, like include or conditional
484              * directives here
485              */
486             if (octstr_search(loc->line, octstr_imm("include"), 0) != -1) {
487                 filename = octstr_copy(loc->line, equals + 1, octstr_len(loc->line));
488                 parse_value(filename);
489 
490                 /* check if we are cycling */
491                 if (gwlist_search(stack, filename, octstr_item_match) != NULL) {
492                     panic(0, "Recursive include for config file `%s' detected "
493                              "(on line %ld of file %s).",
494                           octstr_get_cstr(filename), loc->line_no,
495                           octstr_get_cstr(loc->filename));
496                 } else {
497                     List *files = gwlist_create();
498                     Octstr *file;
499                     struct stat filestat;
500 
501                     /* check if included file is a directory */
502                     if (lstat(octstr_get_cstr(filename), &filestat) != 0) {
503                         error(errno, "lstat failed: couldn't stat `%s'",
504                               octstr_get_cstr(filename));
505                         panic(0, "Failed to include `%s' "
506                               "(on line %ld of file %s). Aborting!",
507                               octstr_get_cstr(filename), loc->line_no,
508                               octstr_get_cstr(loc->filename));
509                     }
510 
511                     /*
512                      * is a directory, create a list with files of
513                      * this directory and load all as part of the
514                      * whole configuration.
515                      */
516                     if (S_ISDIR(filestat.st_mode)) {
517                         DIR *dh;
518                         struct dirent *diritem;
519 
520                         debug("gwlib.cfg", 0, "Loading include dir `%s' "
521                               "(on line %ld of file %s).",
522                               octstr_get_cstr(filename), loc->line_no,
523                               octstr_get_cstr(loc->filename));
524 
525                         dh = opendir(octstr_get_cstr(filename));
526                         while ((diritem = readdir(dh))) {
527                             Octstr *fileitem;
528 
529                             fileitem = octstr_duplicate(filename);
530                             octstr_append_cstr(fileitem, "/");
531                             octstr_append_cstr(fileitem, diritem->d_name);
532 
533                             lstat(octstr_get_cstr(fileitem), &filestat);
534                             if (!S_ISDIR(filestat.st_mode)) {
535                                 gwlist_insert(files, 0, fileitem);
536                             } else {
537                             	octstr_destroy(fileitem);
538                             }
539                         }
540                         closedir(dh);
541                     }
542 
543                     /* is a file, create a list with it */
544                     else {
545                         gwlist_insert(files, 0, octstr_duplicate(filename));
546                     }
547 
548                     /* include files */
549                     while ((file = gwlist_extract_first(files)) != NULL) {
550 
551                         gwlist_insert(stack, 0, octstr_duplicate(file));
552                         debug("gwlib.cfg", 0, "Loading include file `%s' (on line %ld of file %s).",
553                               octstr_get_cstr(file), loc->line_no,
554                               octstr_get_cstr(loc->filename));
555 
556                         /*
557                          * expand the given include file and add it to the current
558                          * processed main while loop
559                          */
560                         if ((expand = expand_file(file, 0)) != NULL) {
561                             while ((loc_inc = gwlist_extract_first(expand)) != NULL)
562                                 gwlist_insert(lines, 0, loc_inc);
563                         } else {
564                             panic(0, "Failed to load whole configuration. Aborting!");
565                         }
566 
567                         gwlist_destroy(expand, NULL);
568                         cfgloc_destroy(loc_inc);
569                         octstr_destroy(file);
570                     }
571                     gwlist_destroy(files, octstr_destroy_item);
572                 }
573                 octstr_destroy(filename);
574             }
575 
576             /*
577              * this is a "normal" line, so process it accodingly
578              */
579             else  {
580                 name = octstr_copy(loc->line, 0, equals);
581                 octstr_strip_blanks(name);
582                 value = octstr_copy(loc->line, equals + 1, octstr_len(loc->line));
583                 parse_value(value);
584 
585     	    	if (grp == NULL)
586                     grp = create_group();
587 
588                 if (grp->configfile != NULL) {
589                     octstr_destroy(grp->configfile);
590                     grp->configfile = NULL;
591                 }
592                 grp->configfile = octstr_duplicate(cfg->filename);
593 
594                 cfg_set(grp, name, value);
595                 octstr_destroy(name);
596                 octstr_destroy(value);
597             }
598         }
599 
600         cfgloc_destroy(loc);
601     }
602 
603     if (grp != NULL && add_group(cfg, grp) == -1) {
604         error_lineno = 1;
605         destroy_group(grp);
606     }
607 
608     gwlist_destroy(lines, NULL);
609     gwlist_destroy(stack, octstr_destroy_item);
610 
611     if (error_lineno != 0) {
612         error(0, "Error found on line %ld of file `%s'.",
613 	          error_lineno, octstr_get_cstr(cfg->filename));
614         return -1;
615     }
616 
617     return 0;
618 }
619 
620 
cfg_get_single_group(Cfg * cfg,Octstr * name)621 CfgGroup *cfg_get_single_group(Cfg *cfg, Octstr *name)
622 {
623     return dict_get(cfg->single_groups, name);
624 }
625 
626 
cfg_get_multi_group(Cfg * cfg,Octstr * name)627 List *cfg_get_multi_group(Cfg *cfg, Octstr *name)
628 {
629     List *list, *copy;
630     long i;
631 
632     list = dict_get(cfg->multi_groups, name);
633     if (list == NULL)
634     	return NULL;
635 
636     copy = gwlist_create();
637     for (i = 0; i < gwlist_len(list); ++i)
638     	gwlist_append(copy, gwlist_get(list, i));
639     return copy;
640 }
641 
642 
cfg_get_group_name(CfgGroup * grp)643 Octstr *cfg_get_group_name(CfgGroup *grp)
644 {
645     return octstr_duplicate(grp->name);
646 }
647 
cfg_get_configfile(CfgGroup * grp)648 Octstr *cfg_get_configfile(CfgGroup *grp)
649 {
650     return octstr_duplicate(grp->configfile);
651 }
652 
653 
cfg_get_real(CfgGroup * grp,Octstr * varname,const char * file,long line,const char * func)654 Octstr *cfg_get_real(CfgGroup *grp, Octstr *varname, const char *file,
655     	    	     long line, const char *func)
656 {
657     Octstr *os;
658 
659     if(grp == NULL)
660     	panic(0, "Trying to fetch variable `%s' in non-existing group",
661 	      octstr_get_cstr(varname));
662 
663     if (grp->name != NULL && !is_allowed_in_group(grp->name, varname))
664     	panic(0, "Trying to fetch variable `%s' in group `%s', not allowed.",
665 	      octstr_get_cstr(varname), octstr_get_cstr(grp->name));
666 
667     os = dict_get(grp->vars, varname);
668     if (os == NULL)
669     	return NULL;
670     return gw_claim_area_for(octstr_duplicate(os), file, line, func);
671 }
672 
673 
cfg_get_integer(long * n,CfgGroup * grp,Octstr * varname)674 int cfg_get_integer(long *n, CfgGroup *grp, Octstr *varname)
675 {
676     Octstr *os;
677     int ret;
678 
679     os = cfg_get(grp, varname);
680     if (os == NULL)
681     	return -1;
682     if (octstr_parse_long(n, os, 0, 0) == -1)
683     	ret = -1;
684     else
685     	ret = 0;
686     octstr_destroy(os);
687     return ret;
688 }
689 
690 
cfg_get_bool(int * n,CfgGroup * grp,Octstr * varname)691 int cfg_get_bool(int *n, CfgGroup *grp, Octstr *varname)
692 {
693     Octstr *os;
694 
695     os = cfg_get(grp, varname);
696     if (os == NULL) {
697 	*n = 0;
698     	return -1;
699     }
700     if (octstr_case_compare(os, octstr_imm("true")) == 0
701 	|| octstr_case_compare(os, octstr_imm("yes")) == 0
702 	|| octstr_case_compare(os, octstr_imm("on")) == 0
703 	|| octstr_case_compare(os, octstr_imm("1")) == 0)
704     {
705 	*n = 1;
706     } else if (octstr_case_compare(os, octstr_imm("false")) == 0
707 	|| octstr_case_compare(os, octstr_imm("no")) == 0
708 	|| octstr_case_compare(os, octstr_imm("off")) == 0
709 	|| octstr_case_compare(os, octstr_imm("0")) == 0)
710     {
711 	*n = 0;
712     }
713     else {
714 	*n = 1;
715 	warning(0, "bool variable set to strange value, assuming 'true'");
716     }
717     octstr_destroy(os);
718     return 0;
719 }
720 
721 
cfg_get_list(CfgGroup * grp,Octstr * varname)722 List *cfg_get_list(CfgGroup *grp, Octstr *varname)
723 {
724     Octstr *os;
725     List *list;
726 
727     os = cfg_get(grp, varname);
728     if (os == NULL)
729     	return NULL;
730 
731     list = octstr_split_words(os);
732     octstr_destroy(os);
733     return list;
734 }
735 
736 
cfg_set(CfgGroup * grp,Octstr * varname,Octstr * value)737 void cfg_set(CfgGroup *grp, Octstr *varname, Octstr *value)
738 {
739     dict_put(grp->vars, varname, octstr_duplicate(value));
740 }
741 
742 
grp_dump(CfgGroup * grp)743 void grp_dump(CfgGroup *grp)
744 {
745     List *names;
746     Octstr *name;
747     Octstr *value;
748 
749     if (grp->name == NULL)
750 	debug("gwlib.cfg", 0, "  dumping group (name not set):");
751     else
752 	debug("gwlib.cfg", 0, "  dumping group (%s):",
753 	      octstr_get_cstr(grp->name));
754     names = dict_keys(grp->vars);
755     while ((name = gwlist_extract_first(names)) != NULL) {
756 	value = cfg_get(grp, name);
757 	debug("gwlib.cfg", 0, "    <%s> = <%s>",
758 	      octstr_get_cstr(name),
759 	      octstr_get_cstr(value));
760     	octstr_destroy(value);
761     	octstr_destroy(name);
762     }
763     gwlist_destroy(names, NULL);
764 }
765 
766 
cfg_dump(Cfg * cfg)767 void cfg_dump(Cfg *cfg)
768 {
769     CfgGroup *grp;
770     List *list;
771     List *names;
772     Octstr *name;
773 
774     debug("gwlib.cfg", 0, "Dumping Cfg %p", (void *) cfg);
775     debug("gwlib.cfg", 0, "  filename = <%s>",
776     	  octstr_get_cstr(cfg->filename));
777 
778     names = dict_keys(cfg->single_groups);
779     while ((name = gwlist_extract_first(names)) != NULL) {
780 	grp = cfg_get_single_group(cfg, name);
781 	if (grp != NULL)
782 	    grp_dump(grp);
783     	octstr_destroy(name);
784     }
785     gwlist_destroy(names, NULL);
786 
787     names = dict_keys(cfg->multi_groups);
788     while ((name = gwlist_extract_first(names)) != NULL) {
789 	list = cfg_get_multi_group(cfg, name);
790 	while ((grp = gwlist_extract_first(list)) != NULL)
791 	    grp_dump(grp);
792 	gwlist_destroy(list, NULL);
793     	octstr_destroy(name);
794     }
795     gwlist_destroy(names, NULL);
796 
797     debug("gwlib.cfg", 0, "Dump ends.");
798 }
799 
800 
cfg_dump_all(void)801 void cfg_dump_all(void)
802 {
803     #define OCTSTR(name) \
804         printf("%s = <please consult user doc>\n", #name);
805     #define SINGLE_GROUP(name, fields) \
806         printf("#\n#  Single Group\n#\n"); \
807         printf("group = %s\n", #name); \
808         fields; \
809         printf("\n\n");
810     #define MULTI_GROUP(name, fields) \
811         printf("#\n#  Multi Group\n#\n"); \
812         printf("group = %s\n", #name); \
813         fields; \
814         printf("\n\n");
815     #include "cfg.def"
816 }
817 
818 
cfg_init(void)819 void cfg_init(void)
820 {
821     /* make sure we put our own core hooks into the lists */
822     allowed_hooks = gwlist_create();
823     single_hooks = gwlist_create();
824 
825     gwlist_append(allowed_hooks, &core_is_allowed_in_group);
826     gwlist_append(single_hooks, &core_is_single_group);
827 }
828 
829 
cfg_shutdown(void)830 void cfg_shutdown(void)
831 {
832     gwlist_destroy(allowed_hooks, NULL);
833     gwlist_destroy(single_hooks, NULL);
834     allowed_hooks = single_hooks = NULL;
835 }
836