Expand leading ~s on paths and commands.
Nicholas Marriott
17 years ago
0 | 0 | 12 March 2007 |
1 | 1 | |
2 | * Expand leading ~s into home directories where appropriate. | |
2 | 3 | * Section headings are now numbered in MANUAL. |
3 | 4 | |
4 | 5 | 11 March 2007 |
222 | 222 | %anum = 57 |
223 | 223 | include "/etc/file-number-%{anum}" |
224 | 224 | |
225 | Macros are not substituted in strings specified using single-quotes. | |
226 | ||
225 | 227 | 5 Invoking fdm ================================================================= |
226 | 228 | |
227 | 229 | fdm may be invoked manually from the command line or regularly using a program |
441 | 443 | later section on regexps). |
442 | 444 | |
443 | 445 | Some accounts add additional tags, discussed below. |
444 | ||
446 | ||
447 | Tags are replaced in almost all strings (including those in single-quotes!), | |
448 | some when the configuration file is parsed and some when the string is used. In | |
449 | addition, a leading ~ or ~user is expanded to a home directory where | |
450 | appropriate. | |
451 | ||
445 | 452 | 6.2 POP3 and POP3S ------------------------------------------------------------- |
446 | 453 | |
447 | 454 | Mail may be fetched from a POP3 account. A POP3 account is defined by |
222 | 222 | %anum = 57 |
223 | 223 | include "/etc/file-number-%{anum}" |
224 | 224 | |
225 | Macros are not substituted in strings specified using single-quotes. | |
226 | ||
225 | 227 | ### Invoking fdm |
226 | 228 | |
227 | 229 | fdm may be invoked manually from the command line or regularly using a program |
441 | 443 | later section on regexps). |
442 | 444 | |
443 | 445 | Some accounts add additional tags, discussed below. |
444 | ||
446 | ||
447 | Tags are replaced in almost all strings (including those in single-quotes!), | |
448 | some when the configuration file is parsed and some when the string is used. In | |
449 | addition, a leading ~ or ~user is expanded to a home directory where | |
450 | appropriate. | |
451 | ||
445 | 452 | %%% POP3 and POP3S |
446 | 453 | |
447 | 454 | Mail may be fetched from a POP3 account. A POP3 account is defined by |
27 | 27 | - refactor & clean up privsep mail transfer code |
28 | 28 | - document mail_file, mbox_file and exec action |
29 | 29 | - document command0 etc |
30 | - expand ~s (special function on paths, check escaping) | |
31 | - matching by account section in manual | |
32 | 30 | - -q option to silence normal (info) output |
33 | 31 | - sort out so normal (info) goes to stdout and all else to stderr |
462 | 462 | |
463 | 463 | /* tag mail if needed */ |
464 | 464 | if (r->key.str != NULL) { |
465 | tkey = replace(&r->key, m->tags, m, mctx->pm_valid, | |
466 | mctx->pm); | |
467 | tvalue = replace(&r->value, m->tags, m, mctx->pm_valid, | |
468 | mctx->pm); | |
465 | tkey = replacestr(&r->key, | |
466 | m->tags, m, mctx->pm_valid, mctx->pm); | |
467 | tvalue = replacestr(&r->value, | |
468 | m->tags, m, mctx->pm_valid, mctx->pm); | |
469 | 469 | |
470 | 470 | if (tkey != NULL && *tkey != '\0' && tvalue != NULL) { |
471 | 471 | log_debug2("%s: tagging message: %s (%s)", |
561 | 561 | |
562 | 562 | for (i = 0; i < ARRAY_LENGTH(r->actions); i++) { |
563 | 563 | rs = &ARRAY_ITEM(r->actions, i, struct replstr); |
564 | s = replace(rs, m->tags, m, mctx->pm_valid, mctx->pm); | |
564 | s = replacestr(rs, m->tags, m, mctx->pm_valid, mctx->pm); | |
565 | 565 | |
566 | 566 | log_debug2("%s: looking for actions matching: %s", a->name, s); |
567 | 567 | ta = match_actions(s); |
39 | 39 | struct deliver_add_header_data *data = t->data; |
40 | 40 | char *hdr, *value; |
41 | 41 | |
42 | hdr = replace(&data->hdr, m->tags, m, *dctx->pm_valid, dctx->pm); | |
42 | hdr = replacestr(&data->hdr, m->tags, m, *dctx->pm_valid, dctx->pm); | |
43 | 43 | if (hdr == NULL || *hdr == '\0') { |
44 | 44 | if (hdr != NULL) |
45 | 45 | xfree(hdr); |
46 | 46 | log_warnx("%s: empty header", a->name); |
47 | 47 | return (DELIVER_FAILURE); |
48 | 48 | } |
49 | value = replace(&data->value, m->tags, m, *dctx->pm_valid, dctx->pm); | |
49 | value = replacestr(&data->value, m->tags, m, *dctx->pm_valid, dctx->pm); | |
50 | 50 | if (value == NULL) { |
51 | 51 | log_warnx("%s: bad value for header %s", a->name, hdr); |
52 | 52 | xfree(hdr); |
55 | 55 | size_t first, last; |
56 | 56 | gid_t gid; |
57 | 57 | |
58 | path = replace(&data->path, m->tags, m, *dctx->pm_valid, dctx->pm); | |
58 | path = replacepath(&data->path, m->tags, m, *dctx->pm_valid, dctx->pm); | |
59 | 59 | if (path == NULL || *path == '\0') { |
60 | 60 | log_warnx("%s: empty path", a->name); |
61 | 61 | goto out; |
75 | 75 | int res = DELIVER_FAILURE; |
76 | 76 | gzFile gzf = NULL; |
77 | 77 | |
78 | path = replace(&data->path, m->tags, m, *dctx->pm_valid, dctx->pm); | |
78 | path = replacepath(&data->path, m->tags, m, *dctx->pm_valid, dctx->pm); | |
79 | 79 | if (path == NULL || *path == '\0') { |
80 | 80 | if (path != NULL) |
81 | 81 | xfree(path); |
61 | 61 | char *lbuf; |
62 | 62 | size_t llen; |
63 | 63 | |
64 | s = replace(&data->cmd, m->tags, m, *dctx->pm_valid, dctx->pm); | |
64 | s = replacepath(&data->cmd, m->tags, m, *dctx->pm_valid, dctx->pm); | |
65 | 65 | if (s == NULL || *s == '\0') { |
66 | 66 | log_warnx("%s: empty command", a->name); |
67 | 67 | if (s != NULL) |
41 | 41 | size_t len, off, wrap; |
42 | 42 | u_int i; |
43 | 43 | |
44 | hdr = replace(&data->hdr, m->tags, m, *dctx->pm_valid, dctx->pm); | |
44 | hdr = replacestr(&data->hdr, m->tags, m, *dctx->pm_valid, dctx->pm); | |
45 | 45 | if (hdr == NULL || *hdr == '\0') { |
46 | 46 | if (hdr != NULL) |
47 | 47 | xfree(hdr); |
51 | 51 | char *lbuf; |
52 | 52 | size_t llen; |
53 | 53 | |
54 | s = replace(&data->cmd, m->tags, m, *dctx->pm_valid, dctx->pm); | |
54 | s = replacepath(&data->cmd, m->tags, m, *dctx->pm_valid, dctx->pm); | |
55 | 55 | if (s == NULL || *s == '\0') { |
56 | 56 | log_warnx("%s: empty command", a->name); |
57 | 57 | if (s != NULL) |
86 | 86 | if (data->to.str == NULL) |
87 | 87 | to = xstrdup(from); |
88 | 88 | else { |
89 | to = replace(&data->to, m->tags, m, *dctx->pm_valid, dctx->pm); | |
89 | to = replacestr(&data->to, m->tags, m, *dctx->pm_valid, | |
90 | dctx->pm); | |
90 | 91 | if (to == NULL || *to == '\0') { |
91 | 92 | log_warnx("%s: empty to", a->name); |
92 | 93 | goto error; |
58 | 58 | char *path; |
59 | 59 | FILE *f; |
60 | 60 | |
61 | path = replace(&data->path, m->tags, m, *dctx->pm_valid, dctx->pm); | |
61 | path = replacepath(&data->path, m->tags, m, *dctx->pm_valid, dctx->pm); | |
62 | 62 | if (path == NULL || *path == '\0') { |
63 | 63 | if (path != NULL) |
64 | 64 | xfree(path); |
70 | 70 | |
71 | 71 | /* Deliver mbox data. */ |
72 | 72 | struct deliver_mbox_data { |
73 | struct replstr path; | |
73 | struct replpath path; | |
74 | 74 | int compress; |
75 | 75 | }; |
76 | 76 | |
92 | 92 | |
93 | 93 | /* Deliver write data. */ |
94 | 94 | struct deliver_write_data { |
95 | struct replstr path; | |
95 | struct replpath path; | |
96 | 96 | }; |
97 | 97 | |
98 | 98 | /* Deliver maildir data. */ |
99 | 99 | struct deliver_maildir_data { |
100 | struct replstr path; | |
100 | struct replpath path; | |
101 | 101 | }; |
102 | 102 | |
103 | 103 | /* Deliver rewrite data. */ |
104 | 104 | struct deliver_rewrite_data { |
105 | struct replstr cmd; | |
105 | struct replpath cmd; | |
106 | 106 | }; |
107 | 107 | |
108 | 108 | /* Deliver pipe data. */ |
109 | 109 | struct deliver_pipe_data { |
110 | struct replstr cmd; | |
110 | struct replpath cmd; | |
111 | 111 | }; |
112 | 112 | |
113 | 113 | /* Deliver append-string data. */ |
250 | 250 | }; |
251 | 251 | ARRAY_DECL(replstrs, struct replstr); |
252 | 252 | |
253 | /* Similar to replstr but needs expand_path too. */ | |
254 | struct replpath { | |
255 | char *str; | |
256 | }; | |
257 | ||
253 | 258 | /* Server description. */ |
254 | 259 | struct server { |
255 | 260 | char *host; |
691 | 696 | void free_action(struct action *); |
692 | 697 | void free_rule(struct rule *); |
693 | 698 | void free_account(struct account *); |
699 | char *expand_path(char *); | |
694 | 700 | |
695 | 701 | /* fdm.c */ |
696 | 702 | double get_time(void); |
795 | 801 | const char *match_tag(struct strb *, const char *); |
796 | 802 | void default_tags(struct strb **, char *, struct account *); |
797 | 803 | void update_tags(struct strb **); |
798 | char *replace(struct replstr *, struct strb *, struct mail *, | |
799 | int, regmatch_t [NPMATCH]); | |
804 | char *replace(char *, struct strb *, struct mail *, int, | |
805 | regmatch_t [NPMATCH]); | |
806 | char *replacestr(struct replstr *, struct strb *, | |
807 | struct mail *, int, regmatch_t [NPMATCH]); | |
808 | char *replacepath(struct replpath *, struct strb *, | |
809 | struct mail *, int, regmatch_t [NPMATCH]); | |
800 | 810 | |
801 | 811 | /* io.c */ |
802 | 812 | struct io *io_create(int, SSL *, const char *, int); |
107 | 107 | |
108 | 108 | /* for any type or name matches, construct the value */ |
109 | 109 | if (data->op == ATTACHOP_ANYTYPE || data->op == ATTACHOP_ANYNAME) { |
110 | value = replace(&data->value.str, | |
110 | value = replacestr(&data->value.str, | |
111 | 111 | m->tags, m, mctx->pm_valid, mctx->pm); |
112 | 112 | } |
113 | 113 |
40 | 40 | int res; |
41 | 41 | char *s, *cause; |
42 | 42 | |
43 | s = replace(&data->str, m->tags, m, mctx->pm_valid, mctx->pm); | |
43 | s = replacestr(&data->str, m->tags, m, mctx->pm_valid, mctx->pm); | |
44 | 44 | log_debug2("%s: matching \"%s\" to \"%s\"", a->name, s, data->re.str); |
45 | 45 | |
46 | 46 | res = re_simple(&data->re, s, &cause); |
38 | 38 | struct mail *m = mctx->mail; |
39 | 39 | char *tag; |
40 | 40 | |
41 | tag = replace(&data->tag, m->tags, m, mctx->pm_valid, mctx->pm); | |
41 | tag = replacestr(&data->tag, m->tags, m, mctx->pm_valid, mctx->pm); | |
42 | 42 | if (match_tag(m->tags, tag) != NULL) { |
43 | 43 | xfree(tag); |
44 | 44 | return (MATCH_TRUE); |
96 | 96 | |
97 | 97 | /* Match command data. */ |
98 | 98 | struct match_command_data { |
99 | struct replstr cmd; | |
99 | struct replpath cmd; | |
100 | 100 | uid_t uid; |
101 | 101 | int pipe; /* pipe mail to command */ |
102 | 102 |
368 | 368 | return (0); |
369 | 369 | |
370 | 370 | /* sort out the command */ |
371 | s = replace(&data->cmd, m->tags, m, mctx->pm_valid, mctx->pm); | |
371 | s = replacepath(&data->cmd, m->tags, m, mctx->pm_valid, mctx->pm); | |
372 | 372 | if (s == NULL || *s == '\0') { |
373 | 373 | log_warnx("%s: empty command", a->name); |
374 | 374 | goto error; |
498 | 498 | xfree(a->data); |
499 | 499 | |
500 | 500 | xfree(a); |
501 | } | |
502 | ||
503 | char * | |
504 | expand_path(char *path) | |
505 | { | |
506 | char *src, *ptr; | |
507 | struct passwd *pw; | |
508 | ||
509 | src = path; | |
510 | while (isspace((int) *src)) | |
511 | src++; | |
512 | if (src[0] != '~') | |
513 | return (NULL); | |
514 | ||
515 | /* ~ */ | |
516 | if (src[1] == '\0') | |
517 | return (xstrdup(conf.info.home)); | |
518 | ||
519 | /* ~/ */ | |
520 | if (src[1] == '/') { | |
521 | xasprintf(&ptr, "%s/%s", conf.info.home, src + 2); | |
522 | return (ptr); | |
523 | } | |
524 | ||
525 | /* ~user or ~user/ */ | |
526 | ptr = strchr(src + 1, '/'); | |
527 | if (ptr != NULL) | |
528 | *ptr = '\0'; | |
529 | ||
530 | pw = getpwnam(src + 1); | |
531 | if (pw == NULL || pw->pw_dir == NULL || *pw->pw_dir == '\0') { | |
532 | endpwent(); | |
533 | return (NULL); | |
534 | } | |
535 | ||
536 | if (ptr == NULL) | |
537 | ptr = xstrdup(pw->pw_dir); | |
538 | else | |
539 | xasprintf(&ptr, "%s/%s", pw->pw_dir, ptr + 1); | |
540 | ||
541 | endpwent(); | |
542 | ||
543 | return (ptr); | |
501 | 544 | } |
502 | 545 | %} |
503 | 546 | |
576 | 619 | %type <replstrs> actions actionslist |
577 | 620 | %type <rule> perform |
578 | 621 | %type <server> server |
579 | %type <string> port to folder strv replstrv retre | |
622 | %type <string> port to folder strv replstrv retre replpathv | |
580 | 623 | %type <strings> domains domainslist headers headerslist accounts accountslist |
581 | 624 | %type <strings> maildirslist maildirs groupslist groups |
582 | 625 | %type <users> users userslist |
697 | 740 | } |
698 | 741 | |
699 | 742 | rs.str = $1; |
700 | $$ = replace(&rs, parse_tags, NULL, 0, NULL); | |
743 | $$ = replacestr(&rs, parse_tags, NULL, 0, NULL); | |
701 | 744 | xfree($1); |
702 | 745 | } |
703 | 746 | |
747 | replpathv: strv | |
748 | { | |
749 | struct replpath rp; | |
750 | ||
751 | if (parse_tags == NULL) { | |
752 | strb_create(&parse_tags); | |
753 | default_tags(&parse_tags, NULL, NULL); | |
754 | } | |
755 | ||
756 | rp.str = $1; | |
757 | $$ = replacepath(&rp, parse_tags, NULL, 0, NULL); | |
758 | xfree($1); | |
759 | } | |
760 | ||
704 | 761 | /** INCLUDE */ |
705 | include: TOKINCLUDE replstrv | |
762 | include: TOKINCLUDE replpathv | |
706 | 763 | /** [$2: replstrv (char *)] */ |
707 | 764 | { |
708 | 765 | char *path; |
837 | 894 | yyerror("fcntl and flock locking cannot be used together"); |
838 | 895 | conf.lock_types = $3; |
839 | 896 | } |
840 | | TOKSET TOKLOCKFILE replstrv | |
897 | | TOKSET TOKLOCKFILE replpathv | |
841 | 898 | /** [$3: replstrv (char *)] */ |
842 | 899 | { |
843 | 900 | if (conf.lock_file != NULL) |
1112 | 1169 | } |
1113 | 1170 | |
1114 | 1171 | /** MAILDIRSLIST: <strings> (struct strings *) */ |
1115 | maildirslist: maildirslist replstrv | |
1172 | maildirslist: maildirslist replpathv | |
1116 | 1173 | /** [$1: maildirslist (struct strings *)] [$2: replstrv (char *)] */ |
1117 | 1174 | { |
1118 | 1175 | if (*$2 == '\0') |
1121 | 1178 | $$ = $1; |
1122 | 1179 | ARRAY_ADD($$, $2, char *); |
1123 | 1180 | } |
1124 | | replstrv | |
1181 | | replpathv | |
1125 | 1182 | /** [$1: replstrv (char *)] */ |
1126 | 1183 | { |
1127 | 1184 | if (*$1 == '\0') |
1133 | 1190 | } |
1134 | 1191 | |
1135 | 1192 | /** MAILDIRS: <strings> (struct strings *) */ |
1136 | maildirs: TOKMAILDIR replstrv | |
1193 | maildirs: TOKMAILDIR replpathv | |
1137 | 1194 | /** [$2: replstrv (char *)] */ |
1138 | 1195 | { |
1139 | 1196 | if (*$2 == '\0') |
2446 | 2503 | $$.data = data; |
2447 | 2504 | data->maildirs = $1; |
2448 | 2505 | } |
2449 | | TOKNNTP server groups TOKCACHE replstrv | |
2506 | | TOKNNTP server groups TOKCACHE replpathv | |
2450 | 2507 | /** [$2: server (struct { ... } server)] */ |
2451 | 2508 | /** [$3: groups (struct strings *)] [$5: replstrv (char *)] */ |
2452 | 2509 | { |
157 | 157 | } |
158 | 158 | |
159 | 159 | char * |
160 | replace(struct replstr *rs, struct strb *tags, struct mail *m, int pm_valid, | |
160 | replacestr(struct replstr *rs, struct strb *tags, struct mail *m, | |
161 | int pm_valid, regmatch_t pm[NPMATCH]) | |
162 | { | |
163 | return (replace(rs->str, tags, m, pm_valid, pm)); | |
164 | } | |
165 | ||
166 | char * | |
167 | replacepath(struct replpath *rp, struct strb *tags, struct mail *m, | |
168 | int pm_valid, regmatch_t pm[NPMATCH]) | |
169 | { | |
170 | char *s, *ss; | |
171 | ||
172 | s = replace(rp->str, tags, m, pm_valid, pm); | |
173 | ss = expand_path(s); | |
174 | if (ss == NULL) | |
175 | return (s); | |
176 | xfree(s); | |
177 | return (ss); | |
178 | } | |
179 | ||
180 | char * | |
181 | replace(char *src, struct strb *tags, struct mail *m, int pm_valid, | |
161 | 182 | regmatch_t pm[NPMATCH]) |
162 | 183 | { |
163 | 184 | char *ptr, *tend; |
164 | 185 | const char *tptr, *alias; |
165 | char *src, *dst, ch; | |
186 | char *dst, ch; | |
166 | 187 | size_t off, len, tlen; |
167 | 188 | u_int idx; |
168 | 189 | |
169 | src = rs->str; | |
170 | 190 | if (src == NULL) |
171 | 191 | return (NULL); |
172 | 192 | if (*src == '\0') |