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

Contents of /imapfilter/file.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.48 - (show annotations)
Sat Feb 22 16:06:41 2003 UTC (21 years, 2 months ago) by lefcha
Branch: MAIN
Changes since 1.47: +388 -360 lines
File MIME type: text/plain
Coding style to KNF and some code cleanup.

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

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26