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