1/*++
2/* NAME
3/*	cfg_parser 3
4/* SUMMARY
5/*	configuration parser utilities
6/* SYNOPSIS
7/*	#include "cfg_parser.h"
8/*
9/*	CFG_PARSER *cfg_parser_alloc(pname)
10/*	const char *pname;
11/*
12/*	CFG_PARSER *cfg_parser_free(parser)
13/*	CFG_PARSER *parser;
14/*
15/*	char *cfg_get_str(parser, name, defval, min, max)
16/*	const CFG_PARSER *parser;
17/*	const char *name;
18/*	const char *defval;
19/*	int min;
20/*	int max;
21/*
22/*	int cfg_get_int(parser, name, defval, min, max)
23/*	const CFG_PARSER *parser;
24/*	const char *name;
25/*	int defval;
26/*	int min;
27/*	int max;
28/*
29/*	int cfg_get_bool(parser, name, defval)
30/*	const CFG_PARSER *parser;
31/*	const char *name;
32/*	int defval;
33/*
34/*	DICT_OWNER cfg_get_owner(parser)
35/*	const CFG_PARSER *parser;
36/* DESCRIPTION
37/*	This module implements utilities for parsing parameters defined
38/*	either as "\fIname\fR = \fBvalue\fR" in a file pointed to by
39/*	\fIpname\fR (the old MySQL style), or as "\fIpname\fR_\fIname\fR =
40/*	\fBvalue\fR" in main.cf (the old LDAP style).  It unifies the
41/*	two styles and provides support for range checking.
42/*
43/*	\fIcfg_parser_alloc\fR initializes the parser. The result
44/*	is NULL if a configuration file could not be opened.
45/*
46/*	\fIcfg_parser_free\fR releases the parser.
47/*
48/*	\fIcfg_get_str\fR looks up a string.
49/*
50/*	\fIcfg_get_int\fR looks up an integer.
51/*
52/*	\fIcfg_get_bool\fR looks up a boolean value.
53/*
54/*	\fIdefval\fR is returned when no value was found. \fImin\fR is
55/*	zero or specifies a lower limit on the integer value or string
56/*	length; \fImax\fR is zero or specifies an upper limit on the
57/*	integer value or string length.
58/*
59/*	Conveniently, \fIcfg_get_str\fR returns \fBNULL\fR if
60/*	\fIdefval\fR is \fBNULL\fR and no value was found.  The returned
61/*	string has to be freed by the caller if not \fBNULL\fR.
62/*
63/*	cfg_get_owner() looks up the configuration file owner.
64/* DIAGNOSTICS
65/*	Fatal errors: bad string length, malformed numerical value, malformed
66/*	boolean value.
67/* SEE ALSO
68/*	mail_conf_str(3) string-valued global configuration parameter support
69/*	mail_conf_int(3) integer-valued configuration parameter support
70/*	mail_conf_bool(3) boolean-valued configuration parameter support
71/* LICENSE
72/* .ad
73/* .fi
74/*	The Secure Mailer license must be distributed with this software.
75/* AUTHOR(S)
76/*	Wietse Venema
77/*	IBM T.J. Watson Research
78/*	P.O. Box 704
79/*	Yorktown Heights, NY 10598, USA
80/*
81/*	Liviu Daia
82/*	Institute of Mathematics of the Romanian Academy
83/*	P.O. BOX 1-764
84/*	RO-014700 Bucharest, ROMANIA
85/*--*/
86
87/* System library. */
88
89#include "sys_defs.h"
90
91#include <stdlib.h>
92#include <errno.h>
93#include <string.h>
94
95#ifdef STRCASECMP_IN_STRINGS_H
96#include <strings.h>
97#endif
98
99/* Utility library. */
100
101#include "msg.h"
102#include "mymalloc.h"
103#include "vstring.h"
104#include "dict.h"
105
106/* Global library. */
107
108#include "mail_conf.h"
109
110/* Application-specific. */
111
112#include "cfg_parser.h"
113
114/* get string from file */
115
116static char *get_dict_str(const struct CFG_PARSER *parser,
117			          const char *name, const char *defval,
118			          int min, int max)
119{
120    const char *strval;
121    int     len;
122
123    if ((strval = (char *) dict_lookup(parser->name, name)) == 0)
124	strval = defval;
125
126    len = strlen(strval);
127    if (min && len < min)
128	msg_fatal("%s: bad string length %d < %d: %s = %s",
129		  parser->name, len, min, name, strval);
130    if (max && len > max)
131	msg_fatal("%s: bad string length %d > %d: %s = %s",
132		  parser->name, len, max, name, strval);
133    return (mystrdup(strval));
134}
135
136/* get string from main.cf */
137
138static char *get_main_str(const struct CFG_PARSER *parser,
139			          const char *name, const char *defval,
140			          int min, int max)
141{
142    static VSTRING *buf = 0;
143
144    if (buf == 0)
145	buf = vstring_alloc(15);
146    vstring_sprintf(buf, "%s_%s", parser->name, name);
147    return ((char *) get_mail_conf_str(vstring_str(buf), defval, min, max));
148}
149
150/* get integer from file */
151
152static int get_dict_int(const struct CFG_PARSER *parser,
153		             const char *name, int defval, int min, int max)
154{
155    const char *strval;
156    char   *end;
157    int     intval;
158    long    longval;
159
160    if ((strval = (char *) dict_lookup(parser->name, name)) != 0) {
161	errno = 0;
162	intval = longval = strtol(strval, &end, 10);
163	if (*strval == 0 || *end != 0 || errno == ERANGE || longval != intval)
164	    msg_fatal("%s: bad numerical configuration: %s = %s",
165		      parser->name, name, strval);
166    } else
167	intval = defval;
168    if (min && intval < min)
169	msg_fatal("%s: invalid %s parameter value %d < %d",
170		  parser->name, name, intval, min);
171    if (max && intval > max)
172	msg_fatal("%s: invalid %s parameter value %d > %d",
173		  parser->name, name, intval, max);
174    return (intval);
175}
176
177/* get integer from main.cf */
178
179static int get_main_int(const struct CFG_PARSER *parser,
180		             const char *name, int defval, int min, int max)
181{
182    static VSTRING *buf = 0;
183
184    if (buf == 0)
185	buf = vstring_alloc(15);
186    vstring_sprintf(buf, "%s_%s", parser->name, name);
187    return (get_mail_conf_int(vstring_str(buf), defval, min, max));
188}
189
190/* get boolean option from file */
191
192static int get_dict_bool(const struct CFG_PARSER *parser,
193			         const char *name, int defval)
194{
195    const char *strval;
196    int     intval;
197
198    if ((strval = (char *) dict_lookup(parser->name, name)) != 0) {
199	if (strcasecmp(strval, CONFIG_BOOL_YES) == 0) {
200	    intval = 1;
201	} else if (strcasecmp(strval, CONFIG_BOOL_NO) == 0) {
202	    intval = 0;
203	} else {
204	    msg_fatal("%s: bad boolean configuration: %s = %s",
205		      parser->name, name, strval);
206	}
207    } else
208	intval = defval;
209    return (intval);
210}
211
212/* get boolean option from main.cf */
213
214static int get_main_bool(const struct CFG_PARSER *parser,
215			         const char *name, int defval)
216{
217    static VSTRING *buf = 0;
218
219    if (buf == 0)
220	buf = vstring_alloc(15);
221    vstring_sprintf(buf, "%s_%s", parser->name, name);
222    return (get_mail_conf_bool(vstring_str(buf), defval));
223}
224
225/* initialize parser */
226
227CFG_PARSER *cfg_parser_alloc(const char *pname)
228{
229    const char *myname = "cfg_parser_alloc";
230    CFG_PARSER *parser;
231    DICT   *dict;
232
233    if (pname == 0 || *pname == 0)
234	msg_fatal("%s: null parser name", myname);
235    parser = (CFG_PARSER *) mymalloc(sizeof(*parser));
236    parser->name = mystrdup(pname);
237    if (*parser->name == '/' || *parser->name == '.') {
238	if (dict_load_file_xt(parser->name, parser->name) == 0) {
239	    myfree(parser->name);
240	    myfree((char *) parser);
241	    return (0);
242	}
243	parser->get_str = get_dict_str;
244	parser->get_int = get_dict_int;
245	parser->get_bool = get_dict_bool;
246	dict = dict_handle(parser->name);
247    } else {
248	parser->get_str = get_main_str;
249	parser->get_int = get_main_int;
250	parser->get_bool = get_main_bool;
251	dict = dict_handle(CONFIG_DICT);	/* XXX Use proper API */
252    }
253    if (dict == 0)
254	msg_panic("%s: dict_handle failed", myname);
255    parser->owner = dict->owner;
256    return (parser);
257}
258
259/* get string */
260
261char   *cfg_get_str(const CFG_PARSER *parser, const char *name,
262		            const char *defval, int min, int max)
263{
264    const char *myname = "cfg_get_str";
265    char   *strval;
266
267    strval = parser->get_str(parser, name, (defval ? defval : ""), min, max);
268    if (defval == 0 && *strval == 0) {
269	/* the caller wants NULL instead of "" */
270	myfree(strval);
271	strval = 0;
272    }
273    if (msg_verbose)
274	msg_info("%s: %s: %s = %s", myname, parser->name, name,
275		 (strval ? strval : "<NULL>"));
276    return (strval);
277}
278
279/* get integer */
280
281int     cfg_get_int(const CFG_PARSER *parser, const char *name, int defval,
282		            int min, int max)
283{
284    const char *myname = "cfg_get_int";
285    int     intval;
286
287    intval = parser->get_int(parser, name, defval, min, max);
288    if (msg_verbose)
289	msg_info("%s: %s: %s = %d", myname, parser->name, name, intval);
290    return (intval);
291}
292
293/* get boolean option */
294
295int     cfg_get_bool(const CFG_PARSER *parser, const char *name, int defval)
296{
297    const char *myname = "cfg_get_bool";
298    int     intval;
299
300    intval = parser->get_bool(parser, name, defval);
301    if (msg_verbose)
302	msg_info("%s: %s: %s = %s", myname, parser->name, name,
303		 (intval ? "on" : "off"));
304    return (intval);
305}
306
307/* release parser */
308
309CFG_PARSER *cfg_parser_free(CFG_PARSER *parser)
310{
311    const char *myname = "cfg_parser_free";
312
313    if (parser->name == 0 || *parser->name == 0)
314	msg_panic("%s: null parser name", myname);
315    if (*parser->name == '/' || *parser->name == '.') {
316	if (dict_handle(parser->name))
317	    dict_unregister(parser->name);
318    }
319    myfree(parser->name);
320    myfree((char *) parser);
321    return (0);
322}
323