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

Contents of /imapfilter/file.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.46 - (show annotations)
Mon Feb 3 20:22:01 2003 UTC (21 years, 2 months ago) by lefcha
Branch: MAIN
Changes since 1.45: +25 -6 lines
File MIME type: text/plain
Added i18n support, variable charset to declare character set to the server.

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

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26