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

Annotation of /imapfilter/file.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.49 - (hide annotations)
Sat Mar 15 18:50:23 2003 UTC (21 years ago) by lefcha
Branch: MAIN
Changes since 1.48: +7 -1 lines
File MIME type: text/plain
Check imapfilter homedir's permissions.

1 lefcha 1.1 #include <stdio.h>
2 lefcha 1.28 #include <unistd.h>
3 lefcha 1.1 #include <errno.h>
4 lefcha 1.5 #include <sys/types.h>
5 lefcha 1.1 #include <regex.h>
6     #include <string.h>
7     #include <stdlib.h>
8     #include <limits.h>
9 lefcha 1.10 #include <sys/stat.h>
10 lefcha 1.28 #include <fcntl.h>
11 lefcha 1.29 #include <time.h>
12 lefcha 1.1
13     #include "config.h"
14     #include "imapfilter.h"
15 lefcha 1.13 #include "data.h"
16 lefcha 1.1
17    
18 lefcha 1.6 extern char logfile[PATH_MAX];
19 lefcha 1.11 extern unsigned int options;
20 lefcha 1.46 extern char charset[CHARSET_LEN];
21 lefcha 1.34 extern unsigned int flags;
22     extern unsigned int interval;
23 lefcha 1.28 extern long timeout;
24 lefcha 1.29 extern char *home;
25    
26     #ifdef ENCRYPTED_PASSWORDS
27 lefcha 1.38 char *passphr; /* Master password to access the passwords
28 lefcha 1.48 * file. */
29    
30 lefcha 1.28 #endif
31 lefcha 1.27
32 lefcha 1.1
33     /*
34 lefcha 1.13 * Find the path to configuration file, open it and call parse_config().
35 lefcha 1.1 */
36 lefcha 1.48 int
37     read_config(char *cfg)
38 lefcha 1.1 {
39 lefcha 1.48 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 lefcha 1.1 #ifdef DEBUG
50 lefcha 1.48 fprintf(stderr, "debug: configuration file: '%s'\n", cfg);
51 lefcha 1.1 #endif
52 lefcha 1.13 #ifdef CHECK_PERMISSIONS
53 lefcha 1.48 check_file_perms(cfg, S_IRUSR | S_IWUSR);
54 lefcha 1.12 #endif
55 lefcha 1.48 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 lefcha 1.1
67 lefcha 1.48 fclose(fd);
68 lefcha 1.1
69 lefcha 1.11 #ifdef DEBUG
70 lefcha 1.48 fprintf(stderr, "debug: options: %0#10x '%s'\n", options, charset);
71 lefcha 1.11 #endif
72    
73 lefcha 1.48 return 0;
74 lefcha 1.1 }
75    
76 lefcha 1.6
77 lefcha 1.10 /*
78 lefcha 1.13 * Parse configuration file.
79 lefcha 1.1 */
80 lefcha 1.48 int
81     parse_config(FILE * fd)
82 lefcha 1.1 {
83 lefcha 1.48 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 lefcha 1.3
163 lefcha 1.48 /* 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 lefcha 1.18
198 lefcha 1.48 for (i = 0; i < 13; i++)
199     regfree(&creg[i]);
200 lefcha 1.6
201 lefcha 1.48 destroy_data();
202    
203     return 0;
204 lefcha 1.1 }
205    
206    
207     /*
208 lefcha 1.13 * Set other options found in config file.
209 lefcha 1.11 */
210 lefcha 1.48 void
211     set_options(char *line, regmatch_t * m)
212 lefcha 1.11 {
213 lefcha 1.48 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 lefcha 1.1 }
278 lefcha 1.29
279    
280     #ifdef ENCRYPTED_PASSWORDS
281     /*
282     * Open password file and call parse_passwords().
283     */
284 lefcha 1.48 int
285     read_passwords(void)
286 lefcha 1.29 {
287 lefcha 1.48 FILE *fd;
288     char pwfile[PATH_MAX];
289 lefcha 1.38
290 lefcha 1.48 if (!(flags & FLAG_BLANK_PASSWORD))
291     return ERROR_CONFIG_PARSE;
292 lefcha 1.38
293 lefcha 1.48 passphr = (char *)smalloc(PASSPHRASE_LEN);
294     *passphr = '\0';
295 lefcha 1.29
296 lefcha 1.48 snprintf(pwfile, PATH_MAX, "%s/%s", home, ".imapfilter/passwords");
297 lefcha 1.29 #ifdef DEBUG
298 lefcha 1.48 fprintf(stderr, "debug: passwords file: '%s'\n", pwfile);
299 lefcha 1.29 #endif
300 lefcha 1.38
301 lefcha 1.48 if (!exists_file(pwfile))
302     return ERROR_FILE_OPEN;
303 lefcha 1.38
304 lefcha 1.29 #ifdef CHECK_PERMISSIONS
305 lefcha 1.48 check_file_perms(pwfile, S_IRUSR | S_IWUSR);
306 lefcha 1.29 #endif
307    
308 lefcha 1.48 fd = fopen(pwfile, "r");
309     if (fd == NULL)
310     fatal(ERROR_FILE_OPEN, "opening passwords file %s; %s\n",
311     pwfile, strerror(errno));
312 lefcha 1.29
313 lefcha 1.48 parse_passwords(fd);
314 lefcha 1.29
315 lefcha 1.48 fclose(fd);
316 lefcha 1.29
317 lefcha 1.48 return 0;
318 lefcha 1.29 }
319    
320    
321     /*
322     * Parse unencrypted password file.
323     */
324 lefcha 1.48 int
325     parse_passwords(FILE * fd)
326 lefcha 1.29 {
327 lefcha 1.48 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 lefcha 1.29
369 lefcha 1.48 line = strtok_r(NULL, "\n", &c);
370     }
371 lefcha 1.38
372 lefcha 1.48 regfree(&creg);
373     sfree(cp);
374     sfree(buf);
375 lefcha 1.29
376 lefcha 1.48 return 0;
377 lefcha 1.29 }
378    
379    
380     /*
381     * Store encrypted passwords to file.
382     */
383 lefcha 1.48 int
384     store_passwords(account_t * accts[])
385 lefcha 1.29 {
386 lefcha 1.48 char pwfile[PATH_MAX];
387     FILE *fd;
388 lefcha 1.29
389 lefcha 1.48 snprintf(pwfile, PATH_MAX, "%s/%s", home, ".imapfilter/passwords");
390 lefcha 1.38
391 lefcha 1.48 create_file(pwfile, S_IRUSR | S_IWUSR);
392 lefcha 1.29
393 lefcha 1.48 fd = fopen(pwfile, "w");
394 lefcha 1.29
395 lefcha 1.48 if (fd == NULL)
396     fatal(ERROR_FILE_OPEN, "opening passwords file %s; %s\n",
397     pwfile, strerror(errno));
398 lefcha 1.38
399 lefcha 1.48 encrypt_passwords(fd, accts);
400 lefcha 1.38
401 lefcha 1.48 fclose(fd);
402 lefcha 1.29
403 lefcha 1.48 return 0;
404 lefcha 1.29 }
405 lefcha 1.48
406 lefcha 1.47 #endif /* ENCRYPTED_PASSWORDS */
407 lefcha 1.31
408    
409     /*
410     * Create $HOME/.imapfilter directory.
411     */
412 lefcha 1.48 int
413     create_homedir(void)
414 lefcha 1.31 {
415 lefcha 1.48 char *hdn;
416 lefcha 1.38
417 lefcha 1.48 hdn = ".imapfilter";
418 lefcha 1.31
419 lefcha 1.48 if (home != NULL)
420     if (chdir(home))
421     error("could not change directory; %s\n",
422     strerror(errno));
423    
424 lefcha 1.49 if (!exists_dir(hdn)) {
425 lefcha 1.48 if (mkdir(hdn, S_IRUSR | S_IWUSR | S_IXUSR))
426     error("could not create directory %s; %s\n", hdn,
427     strerror(errno));
428 lefcha 1.49 }
429     #ifdef CHECK_PERMISSIONS
430     else {
431     check_dir_perms(hdn, S_IRUSR | S_IWUSR | S_IXUSR);
432     }
433     #endif
434 lefcha 1.36
435 lefcha 1.48 return 0;
436 lefcha 1.31 }
437    
438    
439     /*
440     * Check if a file exists.
441     */
442 lefcha 1.48 int
443     exists_file(char *fname)
444 lefcha 1.31 {
445 lefcha 1.48 struct stat fs;
446 lefcha 1.38
447 lefcha 1.48 if (access(fname, F_OK))
448     return 0;
449 lefcha 1.38
450 lefcha 1.48 stat(fname, &fs);
451     if (!S_ISREG(fs.st_mode)) {
452     error("file %s not a regular file\n", fname);
453     return ERROR_FILE_OPEN;
454     }
455     return 1;
456 lefcha 1.31 }
457    
458    
459 lefcha 1.38 /*
460 lefcha 1.31 * Check if a directory exists.
461     */
462 lefcha 1.48 int
463     exists_dir(char *dname)
464 lefcha 1.31 {
465 lefcha 1.48 struct stat ds;
466 lefcha 1.38
467 lefcha 1.48 if (access(dname, F_OK))
468     return 0;
469 lefcha 1.38
470 lefcha 1.48 stat(dname, &ds);
471     if (!S_ISDIR(ds.st_mode)) {
472     error("file %s not a directory\n", dname);
473     return ERROR_FILE_OPEN;
474     }
475     return 1;
476 lefcha 1.31 }
477    
478    
479     /*
480 lefcha 1.36 * Create a file with the specified permissions.
481 lefcha 1.31 */
482 lefcha 1.48 int
483     create_file(char *fname, mode_t mode)
484 lefcha 1.31 {
485 lefcha 1.48 int fd;
486    
487     fd = 0;
488 lefcha 1.38
489 lefcha 1.48 if (!exists_file(fname)) {
490     fd = open(fname, O_CREAT | O_WRONLY | O_TRUNC, mode);
491     if (fd == -1) {
492     error("could not create file %s; %s\n", fname,
493     strerror(errno));
494     return ERROR_FILE_OPEN;
495     }
496     close(fd);
497     }
498     return 0;
499 lefcha 1.31 }
500    
501    
502     #ifdef CHECK_PERMISSIONS
503     /*
504     * Check the permissions of a file.
505     */
506 lefcha 1.48 int
507     check_file_perms(char *fname, mode_t mode)
508 lefcha 1.31 {
509 lefcha 1.48 struct stat fs;
510 lefcha 1.38
511 lefcha 1.48 if (stat(fname, &fs)) {
512     error("getting file %s status; %s\n", fname,
513     strerror(errno));
514     return ERROR_TRIVIAL;
515     }
516     if (!S_ISREG(fs.st_mode)) {
517     error("file %s not a regular file\n", fname);
518     return ERROR_TRIVIAL;
519     }
520     if ((fs.st_mode & 00777) != mode) {
521     error("warning: improper file %s permissions\n", fname);
522     error("warning: file's mode should be %o not %o\n", mode,
523     fs.st_mode & 00777);
524     return ERROR_TRIVIAL;
525     }
526     return 0;
527 lefcha 1.31 }
528    
529    
530     /*
531     * Check the permissions of a directory.
532     */
533 lefcha 1.48 int
534     check_dir_perms(char *dname, mode_t mode)
535 lefcha 1.31 {
536 lefcha 1.48 struct stat ds;
537 lefcha 1.31
538 lefcha 1.48 if (stat(dname, &ds)) {
539     error("getting file %s status; %s\n", dname,
540     strerror(errno));
541     return ERROR_TRIVIAL;
542     }
543     if (!S_ISDIR(ds.st_mode)) {
544     error("file %s not a directory\n", dname);
545     return ERROR_TRIVIAL;
546     }
547     if ((ds.st_mode & 00777) != mode) {
548     error("warning: improper dir %s permissions\n", dname);
549     error("warning: file's mode should be %o not %o\n", mode,
550     ds.st_mode & 00777);
551     return ERROR_TRIVIAL;
552     }
553     return 0;
554 lefcha 1.31 }
555 lefcha 1.38
556 lefcha 1.47 #endif /* CHECK_PERMISSIONS */

webmaster@linux.gr
ViewVC Help
Powered by ViewVC 1.1.26