From: Mark Wooding Date: Tue, 14 Feb 2006 15:52:22 +0000 (+0000) Subject: [PATCH] Rewrite ##X tags in headers of incoming messages X-Git-Tag: mdw/1.03-5~2^2 X-Git-Url: https://git.distorted.org.uk/~mdw/qmail/commitdiff_plain/34c57ee0a50ed7e0f8d077626ec265d915126c91 [PATCH] Rewrite ##X tags in headers of incoming messages Users of ezmlm lists sometimes do not know their subscription address when they want to unsubscribe. Not all get the "Return-Path" header from their delivery agent/MUA, and of the ones that do, many can still not decipher the information. rfc2369 provides a standard way to supply unsubscribe information. For the header to work optimally, it should contain the command adapted to the subscriber's subscription address. These patches enable qmail to replace tags with the subscribers address. This is normally done ONLY in headers to avoid the risk of message corruption. If for some reason no substitution is done, the header remains in its original form, which is harmless as far as message integrity is concerned. If any header starts with '#' this character is removed and substitution will be extented into the body. This is safe since no legal header starts with '#'. It is assumed that for messages with this flag it is desired that any tag in the message is substituted. Assume: Subscriber=user@host, list=list@listhost. and header added by ezmlm: List-Unsubscribe: Then: Header after qmail processing: List-Unsubscribe: --- diff --git a/qmail-local.c b/qmail-local.c index cd01602..ec4e5e7 100644 --- a/qmail-local.c +++ b/qmail-local.c @@ -63,6 +63,51 @@ stralloc ueo = {0}; stralloc cmds = {0}; stralloc messline = {0}; stralloc foo = {0}; +stralloc qsender = {0}; +stralloc tmpline = {0}; +char *verhhost = (char *)0; +char *verhlocal = (char *)0; +int flagheader,flagdobody; +unsigned int i; + +int verhline(sa) +stralloc *sa; +/* substitutes ##L => recipient local, ##H => recipient host if VERP sender */ +/* returns 0 normally, -1 on out-of-memory */ +{ + register char *cp; + char *cpnext,*cpafter; + + if (!verhlocal) return 0; /* no VERP SENDER */ + cp = sa->s; + cpnext = sa->s; + cpafter = cp + sa->len; + tmpline.len = 0; /* clear */ + for (;;) { + while (cp < cpafter && *cp++ != '#'); + if (cp + 1 < cpafter && *cp == '#') { /* found '##' */ + cp++; + if (*cp == 'L') { /* ##L */ + if (!stralloc_catb(&tmpline,cpnext,cp - cpnext - 2)) return -1; + cp++; + cpnext = cp; + if (!stralloc_cats(&tmpline,verhlocal)) return -1; + } else if (*cp == 'H') { /* ##H */ + if (!stralloc_catb(&tmpline,cpnext,cp - cpnext - 2)) return -1; + cp++; + cpnext = cp; + if (!stralloc_cats(&tmpline,verhhost)) return -1; + } + } + if (cp >= cpafter) { + if (tmpline.len) { /* true if we've done any substitutions */ + if (!stralloc_catb(&tmpline,cpnext,cpafter - cpnext)) return -1; + if (!stralloc_copy(sa,&tmpline)) return -1; + } + return 0; + } + } +} char buf[1024]; char outbuf[1024]; @@ -82,6 +127,7 @@ char *dir; char host[64]; char *s; int loop; + int match; struct stat st; int fd; substdio ss; @@ -117,11 +163,27 @@ char *dir; if (substdio_put(&ssout,rpline.s,rpline.len) == -1) goto fail; if (substdio_put(&ssout,dtline.s,dtline.len) == -1) goto fail; - switch(substdio_copy(&ssout,&ss)) - { - case -2: tryunlinktmp(); _exit(4); - case -3: goto fail; - } + flagheader = 1; + flagdobody = 0; + do { /* for VERH */ + if (getln(&ss,&messline,&match,'\n') != 0) + { tryunlinktmp(); _exit(4); } + if (flagheader) { + if (match && messline.len == 1) { + flagheader = 0; + if (!flagdobody) verhlocal = (char *)0; + } + if (messline.s[0] == '#') { /* continue in body */ + flagdobody = 1; /* remove leading '#' */ + for (i = 1; i < messline.len; i++) + messline.s[i - 1] = messline.s[i]; + messline.len--; /* always >= 1 from \n */ + } + } + if (verhlocal) + if (verhline(&messline) == -1) goto fail; + if (substdio_put(&ssout,messline.s,messline.len) == -1) goto fail; + } while (match); if (substdio_flush(&ssout) == -1) goto fail; if (fsync(fd) == -1) goto fail; @@ -193,6 +255,8 @@ char *fn; substdio_fdbuf(&ss,read,0,buf,sizeof(buf)); substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf)); + flagheader = 1; + flagdobody = 0; if (substdio_put(&ssout,ufline.s,ufline.len)) goto writeerrs; if (substdio_put(&ssout,rpline.s,rpline.len)) goto writeerrs; if (substdio_put(&ssout,dtline.s,dtline.len)) goto writeerrs; @@ -207,6 +271,20 @@ char *fn; if (!match && !messline.len) break; if (gfrom(messline.s,messline.len)) if (substdio_bput(&ssout,">",1)) goto writeerrs; + if (flagheader) { + if (match && messline.len == 1) { + if (!flagdobody) verhlocal = (char *)0; + flagheader = 0; + } + if (messline.s[0] == '#') { /* continue in body */ + flagdobody = 1; /* remove leading '#' */ + for (i = 1; i < messline.len; i++) + messline.s[i - 1] = messline.s[i]; + messline.len--; + } + } + if (verhlocal) + if (verhline(&messline) == -1) goto writeerrs; if (substdio_bput(&ssout,messline.s,messline.len)) goto writeerrs; if (!match) { @@ -276,9 +354,24 @@ char **recips; if (qmail_open(&qqt) == -1) temp_fork(); mailforward_qp = qmail_qp(&qqt); qmail_put(&qqt,dtline.s,dtline.len); + flagheader = 1; do { if (getln(&ss,&messline,&match,'\n') != 0) { qmail_fail(&qqt); break; } + if (flagheader) { + if (match && messline.len == 1) { + flagheader = 0; + if (!flagdobody) verhlocal = (char *)0; + } + if (messline.s[0] == '#') { /* continue in body */ + flagdobody = 1; /* remove leading '#' */ + for (i = 1; i < messline.len; i++) + messline.s[i - 1] = messline.s[i]; + messline.len--; + } + } + if (verhlocal) + if (verhline(&messline) == -1) { qmail_fail(&qqt); break; } qmail_put(&qqt,messline.s,messline.len); } while (match); @@ -458,6 +551,7 @@ char **argv; datetime_sec starttime; int flagforwardonly; char *x; + char *cplast; umask(077); sig_pipeignore(); @@ -518,9 +612,9 @@ char **argv; if (!env_put2("SENDER",sender)) temp_nomem(); - if (!quote2(&foo,sender)) temp_nomem(); + if (!quote2(&qsender,sender)) temp_nomem(); if (!stralloc_copys(&rpline,"Return-Path: <")) temp_nomem(); - if (!stralloc_cat(&rpline,&foo)) temp_nomem(); + if (!stralloc_cat(&rpline,&qsender)) temp_nomem(); for (i = 0;i < rpline.len;++i) if (rpline.s[i] == '\n') rpline.s[i] = '_'; if (!stralloc_cats(&rpline,">\n")) temp_nomem(); @@ -528,6 +622,33 @@ char **argv; if (!stralloc_0(&foo)) temp_nomem(); if (!env_put2("RPLINE",foo.s)) temp_nomem(); + i = byte_rchr(qsender.s,qsender.len,'@'); /* for VERH */ + if (i != qsender.len) { /* got @ */ + cplast = qsender.s + i; + *cplast = '\0'; + if (qsender.s[i = str_rchr(qsender.s,'=')]) { /* got = */ + qsender.s[i] = '\0'; + cplast = qsender.s + i; + verhhost = qsender.s + i + 1; + i = str_rchr(qsender.s,'-'); + if (qsender.s[i] == '-') { + for (;;) { + if (case_starts(qsender.s + i + 1,"return-")) { + verhlocal = qsender.s + i + 9 + str_chr(qsender.s + i + 8,'-'); + /* here to avoid work if not VERP */ + /* verhhost not used if verhlocal=0 */ + for (x = verhlocal; x < cplast; x++) + if (*x == '\n') *x = '_'; /* \n would ruin */ + break; + } + j = byte_rchr(qsender.s,i,'-'); + if (j == i) break; + i = j; + } + } + } + } + if (!stralloc_copys(&ufline,"From ")) temp_nomem(); if (*sender) { diff --git a/qmail-remote.c b/qmail-remote.c index 7d65473..864dee2 100644 --- a/qmail-remote.c +++ b/qmail-remote.c @@ -189,19 +189,68 @@ char *append; zerodie(); } +stralloc verh = {0}; /* quoted recipient */ +int flagverh; /* argc */ +char *vp; /* argv[3] */ + void blast() { + unsigned int posat, i; + int flagdobody,flagheader; int r; char ch; + posat = 0; /* stays 0 if no VERH */ + flagdobody = 0; /* => 0 at first blank line */ + flagheader = 1; + if (flagverh == 4) { /* only if single recipient */ + if (!quote2(&verh,vp)) temp_nomem(); /* non-canonicalized */ + for (i = 0; i < verh.len; i++) /* \n would destroy message */ + if (verh.s[i] == '\n') verh.s[i] = '_'; + posat = byte_rchr(verh.s,verh.len,'@'); /* posat=0 if no VERH */ + if (posat == verh.len) posat = 0; + } for (;;) { r = substdio_get(&ssin,&ch,1); if (r == 0) break; if (r == -1) temp_read(); if (ch == '.') substdio_put(&smtpto,".",1); + if (flagheader) { + if (ch == '\n') { /* header ends */ + flagheader = 0; + if (!flagdobody) posat = 0; + } else if (ch == '#') { /* # starting line => VERH ... */ + flagdobody = 1; /* continues in body and ... */ + continue; /* character is suppressed. */ + } + } while (ch != '\n') { - substdio_put(&smtpto,&ch,1); + if (ch == '#' && posat) { /* ... # */ + r = substdio_get(&ssin,&ch,1); + if (r == 0) perm_partialline(); + if (r == -1) temp_read(); + if (ch == '#') { /* ... ## */ + register char ch1; + ch1 = *substdio_peek(&ssin); + if (ch1 != 'L' && ch1 != 'H') { /* ... ##x x!=L x!=H */ + substdio_put(&smtpto,"#",1); + continue; + } + r = substdio_get(&ssin,&ch,1); + if (r == 0) perm_partialline(); + if (r == -1) temp_read(); + if (ch == 'L') /* ... ##L */ + substdio_put(&smtpto,verh.s,posat); + else /* ... ##H */ + substdio_put(&smtpto,verh.s + posat + 1,verh.len - posat - 1); + } else { + substdio_put(&smtpto,"#",1); + if (ch == '\n') break; + substdio_put(&smtpto,&ch,1); + } + } else + substdio_put(&smtpto,&ch,1); r = substdio_get(&ssin,&ch,1); if (r == 0) perm_partialline(); if (r == -1) temp_read(); @@ -341,6 +390,8 @@ char **argv; sig_pipeignore(); if (argc < 4) perm_usage(); + flagverh = argc; + vp = argv[3]; if (chdir(auto_qmail) == -1) temp_chdir(); getcontrols();