1 /*****************************************************************************
2 
3 NAME:
4    configfile.c -- process config file parameters
5 
6    2003-02-12 - split out from config.c so bogolexer use the code.
7 
8 AUTHOR:
9    David Relson <relson@osagesoftware.com>
10 
11 ******************************************************************************/
12 
13 #include "common.h"
14 
15 #include <ctype.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <errno.h>
19 
20 #include "getopt.h"
21 
22 #include "bogoconfig.h"
23 #include "bogofilter.h"
24 #include "bool.h"
25 #include "maint.h"
26 #include "error.h"
27 #include "find_home.h"
28 #include "format.h"
29 #include "lexer.h"
30 #include "longoptions.h"
31 #include "maint.h"
32 #include "system.h"
33 #include "wordlists.h"
34 #include "xatox.h"
35 #include "xmalloc.h"
36 #include "xstrdup.h"
37 
38 #ifndef	DEBUG_CONFIG
39 #define DEBUG_CONFIG(level)	(verbose > level)
40 #endif
41 
42 /*---------------------------------------------------------------------------*/
43 
44 /* NOTE: MAXBUFFLEN _MUST_ _NOT_ BE LARGER THAN INT_MAX! */
45 #define	MAXBUFFLEN	((int)200)
46 
47 /*---------------------------------------------------------------------------*/
48 
49 /* Global variables */
50 
51 #ifndef __riscos__
52 const char *user_config_file   = "~/.bogofilter.cf";
53 #else
54 const char *user_config_file   = "Choices:bogofilter.bogofilter/cf";
55 #endif
56 
57 bool	stats_in_header = true;
58 char 	*config_file_name;
59 
60 /*---------------------------------------------------------------------------*/
61 
62 /*  remove trailing comment from the line.
63  */
remove_comment(char * line)64 void remove_comment(char *line)
65 {
66     char *tmp = strchr(line, '#');
67     if (tmp != NULL) {
68 	tmp -= 1;
69 	while (tmp > line && isspace((unsigned char)*tmp))
70 	    tmp -= 1;
71 	*(tmp+1) = '\0';
72     }
73     return;
74 }
75 
process_config_option(const char * arg,bool warn_on_error,priority_t precedence,struct option * longopts)76 bool process_config_option(const char *arg, bool warn_on_error, priority_t precedence, struct option *longopts)
77 {
78     uint pos;
79     bool ok = true;
80 
81     char *val = NULL;
82     const char *opt = arg;
83     char *dupl;
84     const char delim[] = " \t=";
85 
86     while (isspace((unsigned char)*opt))		/* ignore leading whitespace */
87 	opt += 1;
88 
89     dupl = xstrdup(opt);
90     pos = strcspn(dupl, delim);
91     if (pos < strlen(dupl)) { 		/* if delimiter present */
92 	val = dupl + pos;
93 	*val++ = '\0';
94 	val += strspn(val, delim);
95     }
96 
97     if (val == NULL ||
98 	!process_config_option_as_arg(dupl, val, precedence, longopts)) {
99 	ok = false;
100 	if (warn_on_error)
101 	    fprintf(stderr, "Error - bad parameter '%s'\n", arg);
102     }
103 
104     xfree(dupl);
105     return ok;
106 }
107 
108 /* option_compare()
109 **
110 ** Returns true if options are equal, without regard to case and
111 ** allows underscore from config file to match hyphen
112 */
113 
option_compare(const char * opt,const char * name)114 static bool option_compare(const char *opt, const char *name)
115 {
116     char co, cn;
117     if (strlen(opt) != strlen(name))
118 	return false;
119     while (((co = *opt++) != '\0') && ((cn = *name++) != '\0')) {
120 	if ((co == cn) || (tolower((unsigned char)co) == tolower((unsigned char)cn)))
121 	    continue;
122 	if (co != '_' || cn != '-')
123 	    return false;
124     }
125     return true;
126 }
127 
process_config_option_as_arg(const char * opt,const char * val,priority_t precedence,struct option * long_options)128 bool process_config_option_as_arg(const char *opt, const char *val, priority_t precedence, struct option *long_options)
129 {
130     struct option *option;
131 
132     for (option = long_options; option->name; option += 1) {
133 	if (!option_compare(opt, option->name))
134 	    continue;
135 	if (strcmp(val, "''") == 0)
136 	    val = "";
137 	process_arg(option->val, option->name, val, precedence, PASS_2_CFG);
138 	return true;
139     }
140 
141     return false;
142 }
143 
read_config_file(const char * fname,bool tilde_expand,bool warn_on_error,priority_t precedence,struct option * longopts)144 bool read_config_file(const char *fname, bool tilde_expand, bool warn_on_error, priority_t precedence, struct option *longopts)
145 {
146     bool ok = true;
147     int lineno = 0;
148     FILE *fp;
149 
150     if (config_file_name != NULL)
151 	xfree(config_file_name);
152 
153     if (!tilde_expand)
154 	config_file_name = xstrdup(fname);
155     else
156 	config_file_name = tildeexpand(fname);
157 
158     fp = fopen(config_file_name, "r");
159 
160     if (fp == NULL) {
161 	xfree(config_file_name);
162 	config_file_name = NULL;
163 	return false;
164     }
165 
166     if (DEBUG_CONFIG(0))
167 	fprintf(dbgout, "Reading %s\n", config_file_name);
168 
169     while (!feof(fp))
170     {
171 	size_t len;
172 	char buff[MAXBUFFLEN];
173 
174 	lineno += 1;
175 	if (fgets(buff, sizeof(buff), fp) == NULL)
176 	    break;
177 	len = strlen(buff);
178 	if ( buff[0] == '#' || buff[0] == ';' || buff[0] == '\n' )
179 	    continue;
180 	while (len >= 1
181 		&& (iscntrl((unsigned char)buff[len-1])
182 		    || isspace((unsigned char)buff[len-1])))
183 	    buff[--len] = '\0';
184 
185 	if (DEBUG_CONFIG(1))
186 	    fprintf(dbgout, "Testing:  %s\n", buff);
187 
188 	if (!process_config_option(buff, warn_on_error, precedence, longopts))
189 	    ok = false;
190     }
191 
192     if (ferror(fp)) {
193 	fprintf(stderr, "Error reading file \"%s\"\n.", config_file_name);
194 	ok = false;
195     }
196 
197     (void)fclose(fp); /* we're just reading, so fclose should succeed */
198 
199     return ok;
200 }
201 
202 /* exported */
process_config_files(bool warn_on_error,struct option * longopts)203 bool process_config_files(bool warn_on_error, struct option *longopts)
204 {
205     bool ok = true;
206     const char *env = getenv("BOGOTEST");
207 
208     if (!suppress_config_file) {
209 	if (!read_config_file(system_config_file, false, warn_on_error, PR_CFG_SITE, longopts))
210 	    ok = false;
211 	if (!read_config_file(user_config_file, true, warn_on_error, PR_CFG_USER, longopts))
212 	    ok = false;
213     }
214 
215     if (env)
216 	set_bogotest(env);
217 
218     return ok;
219 }
220