1 |
#include <stdio.h> |
2 |
#include <stdlib.h> |
3 |
#include <string.h> |
4 |
#include <strings.h> |
5 |
#include <errno.h> |
6 |
#include <limits.h> |
7 |
#include <sys/stat.h> |
8 |
#include <sys/types.h> /* IEEE Std 1003.1-2001 non-conformance. */ |
9 |
#include <regex.h> |
10 |
|
11 |
#include "config.h" |
12 |
#include "imapfilter.h" |
13 |
#include "pathnames.h" |
14 |
|
15 |
|
16 |
extern char logfile[PATH_MAX]; |
17 |
extern unsigned int options; |
18 |
extern char charset[CHARSET_LEN]; |
19 |
extern unsigned int flags; |
20 |
extern unsigned int interval; |
21 |
extern long timeout; |
22 |
extern char *home; |
23 |
|
24 |
#ifdef ENCRYPTED_PASSWORDS |
25 |
char *passphr = NULL; /* Master password to access the passwords |
26 |
* file. */ |
27 |
#endif |
28 |
|
29 |
|
30 |
int parse_config(FILE * fd); |
31 |
void set_options(char *line, regmatch_t * match); |
32 |
|
33 |
int parse_passwords(FILE * fd); |
34 |
|
35 |
|
36 |
/* |
37 |
* Find the path to configuration file, open it and call parse_config(). |
38 |
*/ |
39 |
int |
40 |
read_config(char *cfg) |
41 |
{ |
42 |
int r; |
43 |
FILE *fd; |
44 |
char *c; |
45 |
|
46 |
c = NULL; |
47 |
|
48 |
if (cfg == NULL) { |
49 |
cfg = c = (char *)xmalloc(PATH_MAX * sizeof(char)); |
50 |
snprintf(cfg, PATH_MAX, "%s/%s", home, PATHNAME_CONFIG_FILE); |
51 |
} |
52 |
#ifdef DEBUG |
53 |
fprintf(stderr, "debug: configuration file: '%s'\n", cfg); |
54 |
#endif |
55 |
#ifdef CHECK_PERMISSIONS |
56 |
check_file_perms(cfg, S_IRUSR | S_IWUSR); |
57 |
#endif |
58 |
fd = fopen(cfg, "r"); |
59 |
if (fd == NULL) |
60 |
fatal(ERROR_FILE_OPEN, "opening config file %s; %s\n", cfg, |
61 |
strerror(errno)); |
62 |
|
63 |
if (c != NULL) |
64 |
xfree(c); |
65 |
|
66 |
if ((r = parse_config(fd))) |
67 |
fatal(ERROR_CONFIG_PARSE, |
68 |
"parse error in config file at row %d\n", r); |
69 |
|
70 |
fclose(fd); |
71 |
|
72 |
#ifdef DEBUG |
73 |
fprintf(stderr, "debug: options: %0#10x '%s'\n", options, charset); |
74 |
#endif |
75 |
|
76 |
return 0; |
77 |
} |
78 |
|
79 |
|
80 |
/* |
81 |
* Parse configuration file. |
82 |
*/ |
83 |
int |
84 |
parse_config(FILE * fd) |
85 |
{ |
86 |
int i, r; |
87 |
unsigned int row; |
88 |
char line[LINE_MAX]; |
89 |
regex_t creg[13]; |
90 |
regmatch_t match[11]; |
91 |
const char *reg[13] = { |
92 |
"^([[:blank:]]*\n|#.*\n)$", |
93 |
|
94 |
"^[[:blank:]]*ACCOUNT[[:blank:]]+([[:alnum:]_-]+)[[:blank:]]+" |
95 |
"(([[:graph:]]+):([[:graph:]]*)|([[:graph:]]+))@" |
96 |
"([[:alnum:].-]+)(:[[:digit:]]+)?[[:blank:]]*" |
97 |
"([[:blank:]]SSL2|[[:blank:]]SSL3|[[:blank:]]TLS1)?" |
98 |
"[[:blank:]]*\n$", |
99 |
|
100 |
"^[[:blank:]]*FOLDER[[:blank:]]+([[:alnum:]_-]+)[[:blank:]]+" |
101 |
"([[:print:]]+)[[:blank:]]*\n$", |
102 |
|
103 |
"^[[:blank:]]*FILTER[[:blank:]]+([[:alnum:]_-]+)[[:blank:]]*" |
104 |
"([[:blank:]]OR|[[:blank:]]AND)?[[:blank:]]*\n$", |
105 |
|
106 |
"^[[:blank:]]*ACTION[[:blank:]]+(DELETE|" |
107 |
"COPY[[:blank:]]+(\"[[:print:]]*\"|[[:graph:]]+)|" |
108 |
"MOVE[[:blank:]]+(\"[[:print:]]*\"|[[:graph:]]+)|" |
109 |
"RCOPY[[:blank:]]+([[:alnum:]_-]+)[[:blank:]]+" |
110 |
"(\"[[:print:]]*\"|[[:graph:]]+)|" |
111 |
"RMOVE[[:blank:]]+([[:alnum:]_-]+)[[:blank:]]+" |
112 |
"(\"[[:print:]]*\"|[[:graph:]]+)|" |
113 |
"FLAG[[:blank:]]+(REPLACE|ADD|REMOVE)[[:blank:]]+" |
114 |
"([[:alpha:],]+)|" |
115 |
"LIST)[[:blank:]]*([[:graph:]]*)[[:blank:]]*\n$", |
116 |
|
117 |
"^[[:blank:]]*(MASK[[:blank:]])?[[:blank:]]*(OR[[:blank:]]|" |
118 |
"AND[[:blank:]])?[[:blank:]]*(NOT[[:blank:]])?[[:blank:]]*" |
119 |
"(ANSWERED|DELETED|DRAFT|FLAGGED|NEW|OLD|RECENT|SEEN|" |
120 |
"UNANSWERED|UNDELETED|UNDRAFT|UNFLAGGED|UNSEEN)[[:blank:]]*\n$", |
121 |
|
122 |
"^[[:blank:]]*(MASK[[:blank:]])?[[:blank:]]*(OR[[:blank:]]|" |
123 |
"AND[[:blank:]])?[[:blank:]]*(NOT[[:blank:]])?[[:blank:]]*" |
124 |
"(BCC|BODY|CC|FROM|SUBJECT|TEXT|TO)[[:blank:]]+" |
125 |
"(\"[[:print:]]*\"|[[:graph:]]+)[[:blank:]]*\n$", |
126 |
|
127 |
"^[[:blank:]]*(MASK[[:blank:]])?[[:blank:]]*(OR[[:blank:]]|" |
128 |
"AND[[:blank:]])?[[:blank:]]*(NOT[[:blank:]])?[[:blank:]]*" |
129 |
"(HEADER)[[:blank:]]+(\"[[:print:]]*\"|[[:graph:]]+)" |
130 |
"[[:blank:]]+(\"[[:print:]]*\"|[[:graph:]]+)[[:blank:]]*\n$", |
131 |
|
132 |
"^[[:blank:]]*(MASK[[:blank:]])?[[:blank:]]*(OR[[:blank:]]|" |
133 |
"AND[[:blank:]])?[[:blank:]]*(NOT[[:blank:]])?[[:blank:]]*" |
134 |
"(LARGER|SMALLER|OLDER|NEWER)[[:blank:]]+([[:digit:]]+)" |
135 |
"[[:blank:]]*\n$", |
136 |
|
137 |
"^[[:blank:]]*JOB[[:blank:]]+([[:alnum:],_-]+)[[:blank:]]+" |
138 |
"([[:alnum:],_-]+)[[:blank:]]*\n$", |
139 |
|
140 |
"^[[:blank:]]*(SET[[:blank:]])?[[:blank:]]*(LOGFILE|CHARSET)" |
141 |
"[[:blank:]]*=[[:blank:]]*(\"[[:print:]]*\"|[[:graph:]]+)" |
142 |
"[[:blank:]]*\n$", |
143 |
|
144 |
"^[[:blank:]]*(SET[[:blank:]])?[[:blank:]]*(ERRORS|EXPUNGE|" |
145 |
"HEADERS|NAMESPACE|PEEK|SUBSCRIBE)[[:blank:]]*=[[:blank:]]*" |
146 |
"(YES|NO)[[:blank:]]*\n$", |
147 |
|
148 |
"^[[:blank:]]*(SET[[:blank:]])?[[:blank:]]*(DAEMON|TIMEOUT)" |
149 |
"[[:blank:]]*=[[:blank:]]*([[:digit:]]+)\n$" |
150 |
}; |
151 |
|
152 |
r = row = 0; |
153 |
|
154 |
for (i = 0; i < 13; i++) |
155 |
regcomp(&creg[i], reg[i], REG_EXTENDED | REG_ICASE); |
156 |
|
157 |
/* First process all the variables. This is done because some |
158 |
* variables' arguments are used before the other commands are |
159 |
* processed. */ |
160 |
while (fgets(line, LINE_MAX - 1, fd)) |
161 |
if (!regexec(&creg[10], line, 4, match, 0) || |
162 |
!regexec(&creg[11], line, 4, match, 0) || |
163 |
!regexec(&creg[12], line, 4, match, 0)) |
164 |
set_options(line, match); |
165 |
|
166 |
/* Then rewind and process everything else. */ |
167 |
fseek(fd, 0L, SEEK_SET); |
168 |
while (fgets(line, LINE_MAX - 1, fd)) { |
169 |
row++; |
170 |
if (!regexec(&creg[0], line, 0, match, 0)) |
171 |
continue; |
172 |
else if (!regexec(&creg[1], line, 9, match, 0)) |
173 |
set_account(line, match); |
174 |
else if (!regexec(&creg[2], line, 3, match, 0)) |
175 |
r = set_mboxgrp(line, match); |
176 |
else if (!regexec(&creg[3], line, 3, match, 0)) |
177 |
r = set_filter(line, match); |
178 |
else if (!regexec(&creg[4], line, 11, match, 0)) |
179 |
r = set_action(line, match); |
180 |
else if (!regexec(&creg[5], line, 5, match, 0)) |
181 |
r = set_mask(line, match, MASK_MATCH_1); |
182 |
else if (!regexec(&creg[6], line, 6, match, 0)) |
183 |
r = set_mask(line, match, MASK_MATCH_2); |
184 |
else if (!regexec(&creg[7], line, 7, match, 0)) |
185 |
r = set_mask(line, match, MASK_MATCH_3); |
186 |
else if (!regexec(&creg[8], line, 6, match, 0)) |
187 |
r = set_mask(line, match, MASK_MATCH_4); |
188 |
else if (!regexec(&creg[9], line, 3, match, 0)) |
189 |
r = set_job(line, match); |
190 |
/* Skip variable processing. */ |
191 |
else if (!regexec(&creg[10], line, 4, match, 0) || |
192 |
!regexec(&creg[11], line, 4, match, 0) || |
193 |
!regexec(&creg[12], line, 4, match, 0)); |
194 |
else |
195 |
return row; |
196 |
|
197 |
if (r == ERROR_CONFIG_PARSE) |
198 |
return row; |
199 |
} |
200 |
|
201 |
for (i = 0; i < 13; i++) |
202 |
regfree(&creg[i]); |
203 |
|
204 |
destroy_unneeded(); |
205 |
|
206 |
return 0; |
207 |
} |
208 |
|
209 |
|
210 |
/* |
211 |
* Signal SIGUSR1 received, destroy all data structures and reread the |
212 |
* configuration file. |
213 |
*/ |
214 |
void |
215 |
reread_config(char *cfg) |
216 |
{ |
217 |
destroy_all(); |
218 |
read_config(cfg); |
219 |
#ifdef ENCRYPTED_PASSWORDS |
220 |
read_passwords(); |
221 |
#endif |
222 |
flags &= ~(FLAG_SIGUSR1_RECEIVED); |
223 |
} |
224 |
|
225 |
|
226 |
/* |
227 |
* Set other options found in config file. |
228 |
*/ |
229 |
void |
230 |
set_options(char *line, regmatch_t * m) |
231 |
{ |
232 |
if (!strncasecmp(line + m[2].rm_so, "logfile", 7)) { |
233 |
if (*logfile == '\0') { |
234 |
if (*(line + m[3].rm_so) == '"' && |
235 |
*(line + m[3].rm_eo - 1) == '"') |
236 |
strncat(logfile, line + m[3].rm_so + 1, |
237 |
min((m[3].rm_eo - m[3].rm_so - 2), |
238 |
PATH_MAX - 1)); |
239 |
else |
240 |
strncat(logfile, line + m[3].rm_so, |
241 |
min((m[3].rm_eo - m[3].rm_so), |
242 |
PATH_MAX - 1)); |
243 |
} |
244 |
} else if (!strncasecmp(line + m[2].rm_so, "charset", 7)) { |
245 |
if (*(line + m[3].rm_so) == '"' && |
246 |
*(line + m[3].rm_eo - 1) == '"') |
247 |
strncat(charset, line + m[3].rm_so + 1, |
248 |
min((m[3].rm_eo - m[3].rm_so - 2), |
249 |
CHARSET_LEN - 1)); |
250 |
else |
251 |
strncat(charset, line + m[3].rm_so, |
252 |
min((m[3].rm_eo - m[3].rm_so), CHARSET_LEN - 1)); |
253 |
} else if (!strncasecmp(line + m[2].rm_so, "errors", 6)) { |
254 |
if (!strncasecmp(line + m[3].rm_so, "yes", 3)) |
255 |
options |= OPTION_ERRORS; |
256 |
else |
257 |
options &= ~(OPTION_ERRORS); |
258 |
} else if (!strncasecmp(line + m[2].rm_so, "expunge", 7)) { |
259 |
if (!strncasecmp(line + m[3].rm_so, "yes", 3)) |
260 |
options |= OPTION_EXPUNGE; |
261 |
else |
262 |
options &= ~(OPTION_EXPUNGE); |
263 |
} else if (!strncasecmp(line + m[2].rm_so, "header", 6)) { |
264 |
if (!strncasecmp(line + m[3].rm_so, "yes", 3)) |
265 |
options |= OPTION_HEADERS; |
266 |
else |
267 |
options &= ~(OPTION_HEADERS); |
268 |
} else if (!strncasecmp(line + m[2].rm_so, "namespace", 9)) { |
269 |
if (!strncasecmp(line + m[3].rm_so, "yes", 3)) |
270 |
options |= OPTION_NAMESPACE; |
271 |
else |
272 |
options &= ~(OPTION_NAMESPACE); |
273 |
} else if (!strncasecmp(line + m[2].rm_so, "peek", 4)) { |
274 |
if (!strncasecmp(line + m[3].rm_so, "yes", 3)) |
275 |
options |= OPTION_PEEK; |
276 |
else |
277 |
options &= ~(OPTION_PEEK); |
278 |
} else if (!strncasecmp(line + m[2].rm_so, "subscribe", 9)) { |
279 |
if (!strncasecmp(line + m[3].rm_so, "yes", 3)) |
280 |
options |= OPTION_SUBSCRIBE; |
281 |
else |
282 |
options &= ~(OPTION_SUBSCRIBE); |
283 |
} else if (!strncasecmp(line + m[2].rm_so, "timeout", 7)) { |
284 |
errno = 0; |
285 |
timeout = strtol(line + m[3].rm_so, NULL, 10); |
286 |
if (errno) |
287 |
timeout = 0; |
288 |
} else if (!strncasecmp(line + m[2].rm_so, "daemon", 6) && |
289 |
!(options & OPTION_DAEMON_MODE)) { |
290 |
options |= OPTION_DAEMON_MODE; |
291 |
errno = 0; |
292 |
interval = strtoul(line + m[3].rm_so, NULL, 10); |
293 |
if (errno) |
294 |
interval = 0; |
295 |
} |
296 |
} |
297 |
|
298 |
|
299 |
#ifdef ENCRYPTED_PASSWORDS |
300 |
/* |
301 |
* Open password file and call parse_passwords(). |
302 |
*/ |
303 |
int |
304 |
read_passwords(void) |
305 |
{ |
306 |
FILE *fd; |
307 |
char pwfile[PATH_MAX]; |
308 |
|
309 |
if (!(flags & FLAG_BLANK_PASSWORD)) |
310 |
return ERROR_CONFIG_PARSE; |
311 |
|
312 |
if (!passphr) { |
313 |
passphr = (char *)smalloc(PASSPHRASE_LEN); |
314 |
*passphr = '\0'; |
315 |
} |
316 |
snprintf(pwfile, PATH_MAX, "%s/%s", home, PATHNAME_PASSWORD_FILE); |
317 |
#ifdef DEBUG |
318 |
fprintf(stderr, "debug: passwords file: '%s'\n", pwfile); |
319 |
#endif |
320 |
|
321 |
if (!exists_file(pwfile)) |
322 |
return ERROR_FILE_OPEN; |
323 |
|
324 |
#ifdef CHECK_PERMISSIONS |
325 |
check_file_perms(pwfile, S_IRUSR | S_IWUSR); |
326 |
#endif |
327 |
|
328 |
fd = fopen(pwfile, "r"); |
329 |
if (fd == NULL) |
330 |
fatal(ERROR_FILE_OPEN, "opening passwords file %s; %s\n", |
331 |
pwfile, strerror(errno)); |
332 |
|
333 |
parse_passwords(fd); |
334 |
|
335 |
fclose(fd); |
336 |
|
337 |
return 0; |
338 |
} |
339 |
|
340 |
|
341 |
/* |
342 |
* Parse unencrypted password file. |
343 |
*/ |
344 |
int |
345 |
parse_passwords(FILE * fd) |
346 |
{ |
347 |
int t; |
348 |
char *pe; |
349 |
char user[USERNAME_LEN], serv[SERVER_LEN]; |
350 |
unsigned char *buf; |
351 |
char *c, *cp, *line; |
352 |
regex_t creg; |
353 |
regmatch_t match[4]; |
354 |
const char *reg; |
355 |
|
356 |
t = 3; |
357 |
pe = NULL; |
358 |
reg = "([[:alnum:].-]+) ([[:graph:]]+) ([[:graph:]]+)"; |
359 |
|
360 |
if (!(flags & FLAG_DAEMON_MODE)) { |
361 |
do { |
362 |
fseek(fd, 0L, SEEK_SET); |
363 |
printf("Enter master passphrase: "); |
364 |
get_password(passphr, PASSPHRASE_LEN); |
365 |
} while (decrypt_passwords(&buf, fd) && --t != 0); |
366 |
|
367 |
if (!t) |
368 |
return ERROR_PASSPHRASE; |
369 |
} else if (decrypt_passwords(&buf, fd)) |
370 |
fatal(ERROR_DECRYPT, |
371 |
"failed decrypting passwords in daemon mode\n"); |
372 |
|
373 |
c = cp = sstrdup(buf); |
374 |
|
375 |
regcomp(&creg, reg, REG_EXTENDED | REG_ICASE); |
376 |
|
377 |
line = strtok_r(c, "\n", &c); |
378 |
while (line != NULL && !regexec(&creg, line, 4, match, 0)) { |
379 |
user[0] = serv[0] = '\0'; |
380 |
|
381 |
strncat(serv, line + match[1].rm_so, |
382 |
min(match[1].rm_eo - match[1].rm_so, SERVER_LEN - 1)); |
383 |
strncat(user, line + match[2].rm_so, |
384 |
min(match[2].rm_eo - match[2].rm_so, USERNAME_LEN - 1)); |
385 |
|
386 |
if ((pe = (char *)find_password(user, serv))) |
387 |
strncat(pe, line + match[3].rm_so, |
388 |
min(match[3].rm_eo - match[3].rm_so, |
389 |
PASSWORD_LEN - 1)); |
390 |
|
391 |
line = strtok_r(NULL, "\n", &c); |
392 |
} |
393 |
|
394 |
regfree(&creg); |
395 |
sfree(cp); |
396 |
sfree(buf); |
397 |
|
398 |
return 0; |
399 |
} |
400 |
#endif /* ENCRYPTED_PASSWORDS */ |