/[imapfilter]/imapfilter/file.c
ViewVC logotype

Contents of /imapfilter/file.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.45 - (show annotations)
Mon Aug 26 20:34:25 2002 UTC (21 years, 7 months ago) by lefcha
Branch: MAIN
Changes since 1.44: +4 -2 lines
File MIME type: text/plain
Replace strsep() with POSIX strtok_r().

1 #include <stdio.h>
2 #include <unistd.h>
3 #include <errno.h>
4 #include <sys/types.h>
5 #include <regex.h>
6 #include <string.h>
7 #include <stdlib.h>
8 #include <limits.h>
9 #include <sys/stat.h>
10 #include <fcntl.h>
11 #include <time.h>
12
13 #include "config.h"
14 #include "imapfilter.h"
15 #include "data.h"
16
17
18 extern char logfile[PATH_MAX];
19 extern unsigned int options;
20 extern unsigned int flags;
21 extern unsigned int interval;
22 extern long timeout;
23 extern char *home;
24
25 #ifdef ENCRYPTED_PASSWORDS
26 char *passphr; /* Master password to access the passwords
27 file. */
28 #endif
29
30
31 /*
32 * Find the path to configuration file, open it and call parse_config().
33 */
34 int read_config(char *cfg)
35 {
36 int r;
37 FILE *fd;
38 char *c = NULL;
39
40 if (!cfg) {
41 cfg = c = (char *)xmalloc(PATH_MAX * sizeof(char));
42 snprintf(cfg, PATH_MAX, "%s/%s", home, ".imapfilterrc");
43 }
44 #ifdef DEBUG
45 printf("debug: configuration file: '%s'\n", cfg);
46 #endif
47 #ifdef CHECK_PERMISSIONS
48 check_file_perms(cfg, S_IRUSR | S_IWUSR);
49 #endif
50 fd = fopen(cfg, "r");
51
52 if (!fd)
53 fatal(ERROR_FILE_OPEN, "imapfilter: opening config file %s; %s\n",
54 cfg, strerror(errno));
55
56 if (c)
57 xfree(c);
58
59 if ((r = parse_config(fd)))
60 fatal(ERROR_CONFIG_PARSE,
61 "imapfilter: parse error in config file at row %d\n", r);
62
63 fclose(fd);
64
65 #ifdef DEBUG
66 printf("debug: options: %0#10x\n", options);
67 #endif
68
69 return 0;
70 }
71
72
73 /*
74 * Parse configuration file.
75 */
76 int parse_config(FILE * fd)
77 {
78 int i, r = 0;
79 unsigned int row = 0;
80 char line[LINE_MAX];
81 regex_t creg[13];
82 regmatch_t match[11];
83 const char *reg[13] = {
84 "^([[:blank:]]*\n|#.*\n)$",
85
86 "^[[:blank:]]*ACCOUNT[[:blank:]]+([[:alnum:]_-]+)[[:blank:]]+"
87 "(([[:graph:]]+):([[:graph:]]*)|([[:graph:]]+))@([[:alnum:].-]+)"
88 "(:[[:digit:]]+)?[[:blank:]]*([[:blank:]]SSL|[[:blank:]]SSL2|"
89 "[[:blank:]]SSL3|[[:blank:]]TLS1)?[[:blank:]]*\n$",
90
91 "^[[:blank:]]*FOLDER[[:blank:]]+([[:alnum:]_-]+)[[:blank:]]+"
92 "([[:print:]]+)[[:blank:]]*\n$",
93
94 "^[[:blank:]]*FILTER[[:blank:]]+([[:alnum:]_-]+)[[:blank:]]*"
95 "([[:blank:]]OR|[[:blank:]]AND)?[[:blank:]]*\n$",
96
97 "^[[:blank:]]*ACTION[[:blank:]]+(DELETE|"
98 "COPY[[:blank:]]+(\"[[:print:]]*\"|[[:graph:]]+)|"
99 "MOVE[[:blank:]]+(\"[[:print:]]*\"|[[:graph:]]+)|"
100 "RCOPY[[:blank:]]+([[:alnum:]_-]+)[[:blank:]]+"
101 "(\"[[:print:]]*\"|[[:graph:]]+)|"
102 "RMOVE[[:blank:]]+([[:alnum:]_-]+)[[:blank:]]+"
103 "(\"[[:print:]]*\"|[[:graph:]]+)|"
104 "FLAG[[:blank:]]+(REPLACE|ADD|REMOVE)[[:blank:]]+"
105 "([[:alpha:],]+)|"
106 "LIST)[[:blank:]]*([[:graph:]]*)[[:blank:]]*\n$",
107
108 "^[[:blank:]]*(MASK[[:blank:]])?[[:blank:]]*(OR[[:blank:]]|"
109 "AND[[:blank:]])?[[:blank:]]*(NOT[[:blank:]])?[[:blank:]]*"
110 "(ANSWERED|DELETED|DRAFT|FLAGGED|NEW|OLD|RECENT|SEEN|UNANSWERED|"
111 "UNDELETED|UNDRAFT|UNFLAGGED|UNSEEN)[[:blank:]]*\n$",
112
113 "^[[:blank:]]*(MASK[[:blank:]])?[[:blank:]]*(OR[[:blank:]]|"
114 "AND[[:blank:]])?[[:blank:]]*(NOT[[:blank:]])?[[:blank:]]*"
115 "(BCC|BODY|CC|FROM|SUBJECT|TEXT|TO)[[:blank:]]+(\"[[:print:]]*\"|"
116 "[[:graph:]]+)[[:blank:]]*\n$",
117
118 "^[[:blank:]]*(MASK[[:blank:]])?[[:blank:]]*(OR[[:blank:]]|"
119 "AND[[:blank:]])?[[:blank:]]*(NOT[[:blank:]])?[[:blank:]]*"
120 "(HEADER)[[:blank:]]+(\"[[:print:]]*\"|[[:graph:]]+)[[:blank:]]+"
121 "(\"[[:print:]]*\"|[[:graph:]]+)[[:blank:]]*\n$",
122
123 "^[[:blank:]]*(MASK[[:blank:]])?[[:blank:]]*(OR[[:blank:]]|"
124 "AND[[:blank:]])?[[:blank:]]*(NOT[[:blank:]])?[[:blank:]]*"
125 "(LARGER|SMALLER|OLDER|NEWER)[[:blank:]]+([[:digit:]]+)"
126 "[[:blank:]]*\n$",
127
128 "^[[:blank:]]*JOB[[:blank:]]+([[:alnum:],_-]+)[[:blank:]]+"
129 "([[:alnum:],_-]+)[[:blank:]]*\n$",
130
131 "^[[:blank:]]*(SET[[:blank:]])?[[:blank:]]*(LOGFILE)"
132 "[[:blank:]]*=[[:blank:]]*([[:print:]]+)\n$",
133
134 "^[[:blank:]]*(SET[[:blank:]])?[[:blank:]]*(ERRORS|EXPUNGE|HEADERS|"
135 "NAMESPACE|SUBSCRIBE|WARNING)[[:blank:]]*=[[:blank:]]*(YES|NO)"
136 "[[:blank:]]*\n$",
137
138 "^[[:blank:]]*(SET[[:blank:]])?[[:blank:]]*(DAEMON|TIMEOUT)"
139 "[[:blank:]]*=[[:blank:]]*([[:digit:]]+)\n$"
140 };
141
142 for (i = 0; i < 13; i++)
143 regcomp(&creg[i], reg[i], REG_EXTENDED | REG_ICASE);
144
145 /* First process all the variables. This is done because some variables'
146 arguments are used before the other commands are processed. */
147 while (fgets(line, LINE_MAX - 1, fd))
148 if (!regexec(&creg[10], line, 4, match, 0) ||
149 !regexec(&creg[11], line, 4, match, 0) ||
150 !regexec(&creg[12], line, 4, match, 0))
151 set_options(line, match);
152
153 /* Then rewind and process everything else. */
154 fseek(fd, 0L, SEEK_SET);
155 while (fgets(line, LINE_MAX - 1, fd)) {
156 row++;
157 if (!regexec(&creg[0], line, 0, match, 0))
158 continue;
159 else if (!regexec(&creg[1], line, 9, match, 0))
160 set_account(line, match);
161 else if (!regexec(&creg[2], line, 3, match, 0))
162 r = set_mboxgrp(line, match);
163 else if (!regexec(&creg[3], line, 3, match, 0))
164 r = set_filter(line, match);
165 else if (!regexec(&creg[4], line, 11, match, 0))
166 r = set_action(line, match);
167 else if (!regexec(&creg[5], line, 5, match, 0))
168 r = set_mask(line, match, MASK_MATCH_1);
169 else if (!regexec(&creg[6], line, 6, match, 0))
170 r = set_mask(line, match, MASK_MATCH_2);
171 else if (!regexec(&creg[7], line, 7, match, 0))
172 r = set_mask(line, match, MASK_MATCH_3);
173 else if (!regexec(&creg[8], line, 6, match, 0))
174 r = set_mask(line, match, MASK_MATCH_4);
175 else if (!regexec(&creg[9], line, 3, match, 0))
176 r = set_job(line, match);
177 /* Skip variable processing. */
178 else if (!regexec(&creg[10], line, 4, match, 0) ||
179 !regexec(&creg[11], line, 4, match, 0) ||
180 !regexec(&creg[12], line, 4, match, 0));
181 else
182 return row;
183
184 if (r == ERROR_CONFIG_PARSE)
185 return row;
186 }
187
188 for (i = 0; i < 13; i++)
189 regfree(&creg[i]);
190
191 destroy_data();
192
193 return 0;
194 }
195
196
197 /*
198 * Set other options found in config file.
199 */
200 void set_options(char *line, regmatch_t * match)
201 {
202 if (!strncasecmp(line + match[2].rm_so, "logfile", 7)) {
203 if (!*logfile)
204 strncat(logfile, line + match[3].rm_so,
205 min((match[3].rm_eo - match[3].rm_so), PATH_MAX - 1));
206 } else if (!strncasecmp(line + match[2].rm_so, "errors", 6)) {
207 if (!strncasecmp(line + match[3].rm_so, "yes", 3))
208 options |= OPTION_ERRORS;
209 else
210 options &= ~(OPTION_ERRORS);
211 } else if (!strncasecmp(line + match[2].rm_so, "expunge", 7)) {
212 if (!strncasecmp(line + match[3].rm_so, "yes", 3))
213 options |= OPTION_EXPUNGE;
214 else
215 options &= ~(OPTION_EXPUNGE);
216 } else if (!strncasecmp(line + match[2].rm_so, "header", 6)) {
217 if (!strncasecmp(line + match[3].rm_so, "yes", 3))
218 options |= OPTION_HEADERS;
219 else
220 options &= ~(OPTION_HEADERS);
221 } else if (!strncasecmp(line + match[2].rm_so, "namespace", 9)) {
222 if (!strncasecmp(line + match[3].rm_so, "yes", 3))
223 options |= OPTION_NAMESPACE;
224 else
225 options &= ~(OPTION_NAMESPACE);
226 } else if (!strncasecmp(line + match[2].rm_so, "subscribe", 9)) {
227 if (!strncasecmp(line + match[3].rm_so, "yes", 3))
228 options |= OPTION_SUBSCRIBE;
229 else
230 options &= ~(OPTION_SUBSCRIBE);
231 } else if (!strncasecmp(line + match[2].rm_so, "warning", 7)) {
232 if (!strncasecmp(line + match[3].rm_so, "yes", 3))
233 options |= OPTION_WARNING;
234 else
235 options &= ~(OPTION_WARNING);
236 } else if (!strncasecmp(line + match[2].rm_so, "timeout", 7)) {
237 errno = 0;
238 timeout = strtol(line + match[3].rm_so, NULL, 10);
239 if (errno)
240 timeout = 0;
241 } else if (!strncasecmp(line + match[2].rm_so, "daemon", 6) &&
242 !(options & OPTION_DAEMON_MODE)) {
243 options |= OPTION_DAEMON_MODE;
244 errno = 0;
245 interval = strtoul(line + match[3].rm_so, NULL, 10);
246 if (errno)
247 interval = 0;
248 }
249 }
250
251
252 #ifdef ENCRYPTED_PASSWORDS
253 /*
254 * Open password file and call parse_passwords().
255 */
256 int read_passwords(void)
257 {
258 FILE *fd;
259 char pwfile[PATH_MAX];
260
261 if (!(flags & FLAG_BLANK_PASSWORD))
262 return ERROR_CONFIG_PARSE;
263
264 passphr = (char *)smalloc(PASSPHRASE_LEN);
265 *passphr = 0;
266
267 snprintf(pwfile, PATH_MAX, "%s/%s", home, ".imapfilter/passwords");
268 #ifdef DEBUG
269 printf("debug: passwords file: '%s'\n", pwfile);
270 #endif
271
272 if (!exists_file(pwfile))
273 return ERROR_FILE_OPEN;
274
275 #ifdef CHECK_PERMISSIONS
276 check_file_perms(pwfile, S_IRUSR | S_IWUSR);
277 #endif
278
279 fd = fopen(pwfile, "r");
280
281 if (!fd)
282 fatal(ERROR_FILE_OPEN, "imapfilter: opening passwords file %s; %s\n",
283 pwfile, strerror(errno));
284
285 parse_passwords(fd);
286
287 fclose(fd);
288
289 return 0;
290 }
291
292
293 /*
294 * Parse unencrypted password file.
295 */
296 int parse_passwords(FILE * fd)
297 {
298 int t = 3;
299 char *pe = NULL;
300 char user[USERNAME_LEN], serv[SERVER_LEN];
301 unsigned char *buf;
302 char *c, *cp, *line;
303 regex_t creg;
304 regmatch_t match[4];
305 const char *reg = "([[:alnum:].-]+) ([[:graph:]]+) ([[:graph:]]+)";
306 int r;
307
308 do {
309 fseek(fd, 0L, SEEK_SET);
310 printf("Enter master passphrase: ");
311 get_password(passphr, PASSPHRASE_LEN);
312 } while ((r = decrypt_passwords(&buf, fd)) && --t);
313
314 if (!t)
315 return ERROR_PASSPHRASE;
316
317 c = cp = sstrdup(buf);
318
319 regcomp(&creg, reg, REG_EXTENDED | REG_ICASE);
320
321 line = strtok_r(c, "\n", &c);
322 while (line && !regexec(&creg, line, 4, match, 0)) {
323 user[0] = serv[0] = 0;
324
325 strncat(serv, line + match[1].rm_so,
326 min(match[1].rm_eo - match[1].rm_so, SERVER_LEN - 1));
327 strncat(user, line + match[2].rm_so,
328 min(match[2].rm_eo - match[2].rm_so, USERNAME_LEN - 1));
329
330 if ((pe = (char *)find_password(user, serv)))
331 strncat(pe, line + match[3].rm_so,
332 min(match[3].rm_eo - match[3].rm_so, PASSWORD_LEN - 1));
333
334 line = strtok_r(NULL, "\n", &c);
335 }
336
337 regfree(&creg);
338 sfree(cp);
339 sfree(buf);
340
341 return 0;
342 }
343
344
345 /*
346 * Store encrypted passwords to file.
347 */
348 int store_passwords(account_t * accts[])
349 {
350 char pwfile[PATH_MAX];
351 FILE *fd;
352
353 snprintf(pwfile, PATH_MAX, "%s/%s", home, ".imapfilter/passwords");
354
355 create_file(pwfile, S_IRUSR | S_IWUSR);
356
357 fd = fopen(pwfile, "w");
358
359 if (!fd)
360 fatal(ERROR_FILE_OPEN, "imapfilter: opening passwords file %s; %s\n",
361 pwfile, strerror(errno));
362
363 encrypt_passwords(fd, accts);
364
365 fclose(fd);
366
367 return 0;
368 }
369
370 #endif
371
372
373 /*
374 * Create $HOME/.imapfilter directory.
375 */
376 int create_homedir(void)
377 {
378 char *hdn = ".imapfilter";
379
380 if (home)
381 if (chdir(home))
382 error("imapfilter: could not change directory; %s\n",
383 strerror(errno));
384
385 if (!exists_dir(hdn))
386 if (mkdir(hdn, S_IRUSR | S_IWUSR | S_IXUSR))
387 error("imapfilter: could not create directory %s; %s\n", hdn,
388 strerror(errno));
389
390 return 0;
391 }
392
393
394 /*
395 * Check if a file exists.
396 */
397 int exists_file(char *fname)
398 {
399 struct stat fs;
400
401 if (access(fname, F_OK))
402 return 0;
403
404 stat(fname, &fs);
405 if (!S_ISREG(fs.st_mode)) {
406 error("imapfilter: file %s not a regular file\n", fname);
407 return ERROR_FILE_OPEN;
408 }
409 return 1;
410 }
411
412
413 /*
414 * Check if a directory exists.
415 */
416 int exists_dir(char *dname)
417 {
418 struct stat ds;
419
420 if (access(dname, F_OK))
421 return 0;
422
423 stat(dname, &ds);
424 if (!S_ISDIR(ds.st_mode)) {
425 error("imapfilter: file %s not a directory\n", dname);
426 return ERROR_FILE_OPEN;
427 }
428 return 1;
429 }
430
431
432 /*
433 * Create a file with the specified permissions.
434 */
435 int create_file(char *fname, mode_t mode)
436 {
437 int fd = 0;
438
439 if (!exists_file(fname)) {
440 fd = open(fname, O_CREAT | O_WRONLY | O_TRUNC, mode);
441 if (fd == -1) {
442 error("imapfilter: could not create file %s; %s\n", fname,
443 strerror(errno));
444 return ERROR_FILE_OPEN;
445 }
446 close(fd);
447 }
448 return 0;
449 }
450
451
452 #ifdef CHECK_PERMISSIONS
453 /*
454 * Check the permissions of a file.
455 */
456 int check_file_perms(char *fname, mode_t mode)
457 {
458 struct stat fs;
459
460 if (stat(fname, &fs)) {
461 error("imapfilter: getting file %s status; %s\n", fname,
462 strerror(errno));
463 return ERROR_TRIVIAL;
464 }
465 if (!S_ISREG(fs.st_mode)) {
466 error("imapfilter: file %s not a regular file\n", fname);
467 return ERROR_TRIVIAL;
468 }
469 if ((fs.st_mode & 00777) != mode) {
470 error("imapfilter: warning: improper file %s permissions\n"
471 "imapfilter: warning: file's mode should be %o not %o\n",
472 fname, mode, fs.st_mode & 00777);
473 return ERROR_TRIVIAL;
474 }
475 return 0;
476 }
477
478
479 /*
480 * Check the permissions of a directory.
481 */
482 int check_dir_perms(char *dname, mode_t mode)
483 {
484 struct stat ds;
485
486 if (stat(dname, &ds)) {
487 error("imapfilter: getting file %s status; %s\n", dname,
488 strerror(errno));
489 return ERROR_TRIVIAL;
490 }
491 if (!S_ISDIR(ds.st_mode)) {
492 error("imapfilter: file %s not a directory\n", dname);
493 return ERROR_TRIVIAL;
494 }
495 if ((ds.st_mode & 00777) != mode) {
496 error("imapfilter: warning: improper dir %s permissions\n"
497 "imapfilter: warning: file's mode should be %o not %o\n",
498 dname, mode, ds.st_mode & 00777);
499 return ERROR_TRIVIAL;
500 }
501 return 0;
502 }
503
504 #endif

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26