X-Git-Url: https://git.distorted.org.uk/~mdw/ezmlm/blobdiff_plain/5b62e993b0af39700031c2875d7f6654e6a02850..f8beb284087c279acfb30506f5bb32baa4949b44:/ezmlm-send.c diff --git a/ezmlm-send.c b/ezmlm-send.c index 5748891..fa2913e 100644 --- a/ezmlm-send.c +++ b/ezmlm-send.c @@ -1,3 +1,5 @@ +/* $Id: ezmlm-send.c,v 1.77 1999/10/29 02:49:14 lindberg Exp $*/ +/* $Name: ezmlm-idx-040 $*/ #include "stralloc.h" #include "subfd.h" #include "strerr.h" @@ -17,27 +19,89 @@ #include "substdio.h" #include "getconf.h" #include "constmap.h" - +#include "byte.h" +#include "sgetopt.h" +#include "quote.h" +#include "subscribe.h" +#include "mime.h" +#include "errtxt.h" +#include "makehash.h" +#include "cookie.h" +#include "idx.h" +#include "copy.h" + +int flagnoreceived = 1; /* suppress received headers by default. They*/ + /* are still archived. =0 => archived and */ + /* copied. */ +int flaglog = 1; /* for lists with mysql support, use tags */ + /* and log traffic to the database */ #define FATAL "ezmlm-send: fatal: " void die_usage() { - strerr_die1x(100,"ezmlm-send: usage: ezmlm-send dir"); + strerr_die1x(100,"ezmlm-send: usage: ezmlm-send [-cClLqQrR] [-h header] dir"); } void die_nomem() { - strerr_die2x(111,FATAL,"out of memory"); + strerr_die2x(111,FATAL,ERR_NOMEM); } + /* for writing new index file indexn later moved to index. */ +substdio ssindexn; +char indexnbuf[1024]; + char strnum[FMT_ULONG]; +char szmsgnum[FMT_ULONG]; +char hash[HASHLEN]; stralloc fnadir = {0}; stralloc fnaf = {0}; +stralloc fnif = {0}; +stralloc fnifn = {0}; stralloc fnsub = {0}; stralloc line = {0}; +stralloc qline = {0}; +stralloc lines = {0}; +stralloc subject = {0}; +stralloc from = {0}; +stralloc received = {0}; +stralloc prefix = {0}; +stralloc content = {0}; +stralloc boundary = {0}; +stralloc charset = {0}; +stralloc dcprefix = {0}; +stralloc dummy = {0}; +stralloc qmqpservers = {0}; + +void die_indexn() +{ + strerr_die4x(111,FATAL,ERR_WRITE,fnifn.s,": "); +} +void *psql = (void *) 0; + +unsigned long innum; +unsigned long outnum; +unsigned long msgnum; +unsigned long hash_lo = 0L; +unsigned long hash_hi = 52L; +unsigned long msgsize = 0L; +unsigned long cumsize = 0L; /* cumulative archive size bytes / 256 */ +char flagcd = '\0'; /* no transfer-encoding for trailer */ +char encin = '\0'; +int flagindexed; +int flagfoundokpart; /* Found something to pass on. If multipart */ + /* we set to 0 and then set to 1 for any */ + /* acceptable mime part. If 0 -> reject */ +int flagreceived; +int flagprefixed; +unsigned int serial = 0; int flagarchived; int fdarchive; +int fdindex; +int fdindexn; +char hashout[COOKIE+1]; + substdio ssarchive; char archivebuf[1024]; @@ -48,7 +112,9 @@ stralloc outlocal = {0}; stralloc outhost = {0}; stralloc headerremove = {0}; struct constmap headerremovemap; -stralloc headeradd = {0}; +stralloc mimeremove = {0}; +struct constmap mimeremovemap; +char *dir; struct qmail qq; substdio ssin; @@ -56,7 +122,10 @@ char inbuf[1024]; substdio ssout; char outbuf[1]; -int mywrite(fd,buf,len) +char textbuf[512]; +substdio sstext; + +unsigned int mywrite(fd,buf,len) int fd; char *buf; unsigned int len; @@ -65,23 +134,33 @@ unsigned int len; return len; } +int subto(s,l) +char *s; +unsigned int l; +{ + qmail_put(&qq,"T",1); + qmail_put(&qq,s,l); + qmail_put(&qq,"",1); + return (int) l; +} + void die_archive() { - strerr_die4sys(111,FATAL,"unable to write to ",fnaf.s,": "); + strerr_die4sys(111,FATAL,ERR_WRITE,fnaf.s,": "); } void die_numnew() { - strerr_die2sys(111,FATAL,"unable to create numnew: "); + strerr_die3sys(111,FATAL,ERR_CREATE,"numnew: "); } -void put(buf,len) char *buf; int len; +void qa_put(buf,len) char *buf; unsigned int len; { qmail_put(&qq,buf,len); if (flagarchived) if (substdio_put(&ssarchive,buf,len) == -1) die_archive(); } -void puts(buf) char *buf; +void qa_puts(buf) char *buf; { qmail_puts(&qq,buf); if (flagarchived) @@ -91,8 +170,8 @@ void puts(buf) char *buf; int sublistmatch(sender) char *sender; { - int i; - int j; + unsigned int i; + unsigned int j; j = str_len(sender); if (j < sublist.len) return 0; @@ -109,14 +188,12 @@ char *sender; substdio ssnumnew; char numnewbuf[16]; -unsigned long msgnum; -stralloc num = {0}; char buf0[256]; substdio ss0 = SUBSTDIO_FDBUF(read,0,buf0,sizeof(buf0)); void numwrite() -{ +{ /* this one deals with msgnum, not outnum! */ int fd; fd = open_trunc("numnew"); @@ -124,70 +201,277 @@ void numwrite() substdio_fdbuf(&ssnumnew,write,fd,numnewbuf,sizeof(numnewbuf)); if (substdio_put(&ssnumnew,strnum,fmt_ulong(strnum,msgnum)) == -1) die_numnew(); + if (substdio_puts(&ssnumnew,":") == -1) die_numnew(); + if (substdio_put(&ssnumnew,strnum,fmt_ulong(strnum,cumsize)) == -1) + die_numnew(); + if (substdio_puts(&ssnumnew,"\n") == -1) die_numnew(); if (substdio_flush(&ssnumnew) == -1) die_numnew(); if (fsync(fd) == -1) die_numnew(); if (close(fd) == -1) die_numnew(); /* NFS stupidity */ if (rename("numnew","num") == -1) - strerr_die2sys(111,FATAL,"unable to move numnew to num: "); + strerr_die3sys(111,FATAL,ERR_MOVE,"numnew: "); } stralloc mydtline = {0}; +int idx_copy_insertsubject() +/* copies old index file up to but not including msg, then adds a line with */ +/* 'sub' trimmed of reply indicators, then closes the new index and moves it*/ +/* to the name 'index'. Errors are dealt with directly, and if the routine */ +/* returns, it was successful. 'fatal' points to a program-specific error */ +/* string. Sub is not destroyed, but from is!!! */ +/* returns 1 if reply-indicators were found, 0 otherwise. */ +/* no terminal \n or \0 in any of the strallocs! */ +{ + char *cp; + unsigned long idx; + int match; + int r; + unsigned int pos; + + if (!stralloc_copys(&fnadir,"archive/")) die_nomem(); + if (!stralloc_catb(&fnadir,strnum,fmt_ulong(strnum,outnum / 100))) + die_nomem(); + if (!stralloc_copy(&fnif,&fnadir)) die_nomem(); + if (!stralloc_copy(&fnifn,&fnif)) die_nomem(); + if (!stralloc_cats(&fnif,"/index")) die_nomem(); + if (!stralloc_cats(&fnifn,"/indexn")) die_nomem(); + if (!stralloc_0(&fnif)) die_nomem(); + if (!stralloc_0(&fnifn)) die_nomem(); + if (!stralloc_0(&fnadir)) die_nomem(); + + /* may not exists since we run before ezmlm-send */ + if (mkdir(fnadir.s,0755) == -1) + if (errno != error_exist) + strerr_die4x(111,FATAL,ERR_CREATE,fnadir.s,": "); + + /* Open indexn */ + fdindexn = open_trunc(fnifn.s); + if (fdindexn == -1) + strerr_die4x(111,FATAL,ERR_WRITE,fnifn.s,": "); + + /* set up buffers for indexn */ + substdio_fdbuf(&ssindexn,write,fdindexn,indexnbuf,sizeof(indexnbuf)); + + concatHDR(subject.s,subject.len,&lines,FATAL); /* make 1 line */ + decodeHDR(lines.s,lines.len,&qline,charset.s,FATAL); /* decode mime */ + r = unfoldHDR(qline.s,qline.len,&lines,charset.s,&dcprefix,1,FATAL); + /* trim mime */ + + fdindex = open_read(fnif.s); + if (fdindex == -1) { + if (errno != error_noent) + strerr_die4x(111,FATAL,ERR_OPEN, fnif.s, ": "); + } else { + substdio_fdbuf(&ssin,read,fdindex,inbuf,sizeof(inbuf)); + for(;;) { + if (getln(&ssin,&qline,&match,'\n') == -1) + strerr_die4sys(111,FATAL,ERR_READ, fnif.s, ": "); + if (!match) + break; + pos = scan_ulong(qline.s,&idx); + if (!idx) /* "impossible!" */ + strerr_die2x(111,FATAL,ERR_BAD_INDEX); + if (idx >= outnum) + break; /* messages always come in order */ + if (substdio_put(&ssindexn,qline.s,qline.len) == -1) + die_indexn(); + if (qline.s[pos] == ':') { /* has author line */ + if (getln(&ssin,&qline,&match,'\n') == -1) + strerr_die4x(111,FATAL,ERR_READ, fnif.s, ": "); + if (!match && qline.s[0] != '\t') /* "impossible! */ + strerr_die2x(111,FATAL,ERR_BAD_INDEX); + if (substdio_put(&ssindexn,qline.s,qline.len) == -1) + die_indexn(); + } + } + close(fdindex); + } + if (!stralloc_copyb(&qline,strnum,fmt_ulong(strnum,outnum))) die_nomem(); + if (!stralloc_cats(&qline,": ")) die_nomem(); /* ':' for new ver */ + makehash(lines.s,lines.len,hash); + if (!stralloc_catb(&qline,hash,HASHLEN)) die_nomem(); + if (!stralloc_cats(&qline," ")) die_nomem(); + if (r & 1) /* reply */ + if (!stralloc_cats(&qline,"Re: ")) die_nomem(); + if (!stralloc_cat(&qline,&lines)) die_nomem(); + if (!stralloc_cats(&qline,"\n\t")) die_nomem(); + if (!stralloc_cat(&qline,&received)) die_nomem(); + if (!stralloc_cats(&qline,";")) die_nomem(); + + concatHDR(from.s,from.len,&lines,FATAL); + mkauthhash(lines.s,lines.len,hash); + + if (!stralloc_catb(&qline,hash,HASHLEN)) die_nomem(); + if (!stralloc_cats(&qline," ")) die_nomem(); + + decodeHDR(cp,author_name(&cp,lines.s,lines.len),&from,charset.s,FATAL); + (void) unfoldHDR(from.s,from.len,&lines,charset.s,&dcprefix,0,FATAL); + if (!stralloc_cat(&qline,&lines)) die_nomem(); + + if (!stralloc_cats(&qline,"\n")) die_nomem(); + if (substdio_put(&ssindexn,qline.s,qline.len) == -1) die_indexn(); + if (substdio_flush(&ssindexn) == -1) die_indexn(); + if (fsync(fdindexn) == -1) die_indexn(); + if (fchmod(fdindexn,MODE_ARCHIVE | 0700) == -1) die_indexn(); + if (close(fdindexn) == -1) die_indexn(); /* NFS stupidity */ + if (rename(fnifn.s,fnif.s) == -1) + strerr_die4x(111,FATAL,ERR_MOVE,fnifn.s,": "); + return r; +} + +void transferenc() +{ + if (flagcd) { + qmail_puts(&qq,"\nContent-Transfer-Encoding: "); + if (flagcd == 'Q') + qmail_puts(&qq,"Quoted-printable\n\n"); + else + qmail_puts(&qq,"base64\n\n"); + } else + qmail_puts(&qq,"\n\n"); +} + +void getcharset() +{ + if (getconf_line(&charset,"charset",0,FATAL,dir)) { + if (charset.len >= 2 && charset.s[charset.len - 2] == ':') { + if (charset.s[charset.len - 1] == 'B' || + charset.s[charset.len - 1] == 'Q') { + flagcd = charset.s[charset.len - 1]; + charset.s[charset.len - 2] = '\0'; + } + } + } else + if (!stralloc_copys(&charset,TXT_DEF_CHARSET)) die_nomem(); + + if (!stralloc_0(&charset)) die_nomem(); +} + void main(argc,argv) int argc; char **argv; { - int fd; - char *dir; + unsigned long subs; int fdlock; char *sender; + char *mlheader = (char *) 0; + char *ret; + char *err; int flagmlwasthere; + int flagqmqp = 0; /* don't use qmqp by default */ + int flaglistid = 0; /* no listid header added */ int match; - int i; - char ch; + unsigned int i; + int r,fd; int flaginheader; int flagbadfield; + int flagbadpart; + int flagseenext; + int flagsubline; + int flagfromline; + int flagcontline; + int flagarchiveonly; + int flagtrailer; + unsigned int pos; + int opt; + char *cp, *cpstart, *cpafter; umask(022); sig_pipeignore(); - dir = argv[1]; + while ((opt = getopt(argc,argv,"cCh:H:lLrRqQs:S:vV")) != opteof) + switch(opt) { + case 'c': case 'C': break; /* ignore for backwards compat */ + case 'h': + case 'H': mlheader = optarg; /* Alternative sublist check header */ + mlheader[str_chr(mlheader,':')] = '\0'; + break; + case 'l': flaglog = 1; break; + case 'L': flaglog = 0; break; + case 'r': flagnoreceived = 0; break; + case 'R': flagnoreceived = 1; break; + case 's': + case 'S': pos = scan_ulong(optarg,&hash_lo); + if (!optarg[pos++]) break; + (void) scan_ulong(optarg+pos,&hash_hi); + if (hash_hi > 52L) hash_hi = 52L; + if (hash_lo > hash_hi) hash_lo = hash_hi; + + break; + case 'q': flagqmqp = 0; break; + case 'Q': flagqmqp = 1; break; + case 'v': + case 'V': strerr_die2x(0, + "ezmlm-send version: ezmlm-0.53+",EZIDX_VERSION); + default: + die_usage(); + } + + + dir = argv[optind++]; if (!dir) die_usage(); sender = env_get("SENDER"); if (chdir(dir) == -1) - strerr_die4sys(111,FATAL,"unable to switch to ",dir,": "); + strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": "); fdlock = open_append("lock"); if (fdlock == -1) - strerr_die4sys(111,FATAL,"unable to open ",dir,"/lock: "); + strerr_die4sys(111,FATAL,ERR_OPEN,dir,"/lock: "); if (lock_ex(fdlock) == -1) - strerr_die4sys(111,FATAL,"unable to obtain ",dir,"/lock: "); - - if (qmail_open(&qq) == -1) - strerr_die2sys(111,FATAL,"unable to run qmail-queue: "); + strerr_die4sys(111,FATAL,ERR_OBTAIN,dir,"/lock: "); flagarchived = getconf_line(&line,"archived",0,FATAL,dir); + flagindexed = getconf_line(&line,"indexed",0,FATAL,dir); + getcharset(); + flagprefixed = getconf_line(&prefix,"prefix",0,FATAL,dir); + if (prefix.len) { /* encoding and serial # support */ + /* no sanity checks - you put '\n' or '\0' */ + /* into the coded string, you pay */ + + decodeHDR(prefix.s,prefix.len,&line,charset.s,FATAL); + (void) unfoldHDR(line.s,line.len,&dcprefix,charset.s,&dummy,0,FATAL); + if (!stralloc_copy(&dcprefix,&line)) die_nomem(); + serial = byte_rchr(prefix.s,prefix.len,'#'); + } + if ((fd = open_read("text/trailer")) == -1) { /* see if there is a trailer */ + if (errno == error_noent) flagtrailer = 0; + else strerr_die2sys(111,ERR_OPEN,"text/trailer: "); + } else { + close(fd); + flagtrailer = 1; + } - getconf_line(&num,"num",1,FATAL,dir); - if (!stralloc_0(&num)) die_nomem(); - scan_ulong(num.s,&msgnum); - ++msgnum; + getconf(&mimeremove,"mimeremove",0,FATAL,dir); + + if (getconf_line(&line,"num",0,FATAL,dir)) { /* Now non-FATAL, def=0 */ + if (!stralloc_0(&line)) die_nomem(); + cp = line.s + scan_ulong(line.s,&msgnum); + ++msgnum; + if (*cp++ == ':') + scan_ulong(cp,&cumsize); + } else + msgnum = 1L; /* if num not there */ getconf_line(&outhost,"outhost",1,FATAL,dir); getconf_line(&outlocal,"outlocal",1,FATAL,dir); - getconf_line(&mailinglist,"mailinglist",1,FATAL,dir); + set_cpoutlocal(&outlocal); + set_cpouthost(&outhost); flagsublist = getconf_line(&sublist,"sublist",0,FATAL,dir); - getconf(&headerremove,"headerremove",1,FATAL,dir); - constmap_init(&headerremovemap,headerremove.s,headerremove.len,0); + if (flagqmqp) { /* forward compatibility ;-) */ + if (!stralloc_copys(&line,QMQPSERVERS)) die_nomem(); + if (!stralloc_cats(&line,"/0")) die_nomem(); + if (!stralloc_0(&line)) die_nomem(); + (void) getconf_line(&qmqpservers,line.s,0,FATAL,dir); + } - getconf(&headeradd,"headeradd",1,FATAL,dir); - for (i = 0;i < headeradd.len;++i) - if (!headeradd.s[i]) - headeradd.s[i] = '\n'; + getconf(&headerremove,"headerremove",1,FATAL,dir); + if (!constmap_init(&headerremovemap,headerremove.s,headerremove.len,0)) + die_nomem(); if (!stralloc_copys(&mydtline,"Delivered-To: mailing list ")) die_nomem(); if (!stralloc_catb(&mydtline,outlocal.s,outlocal.len)) die_nomem(); @@ -197,123 +481,326 @@ char **argv; if (sender) { if (!*sender) - strerr_die2x(100,FATAL,"I don't distribute bounce messages (#5.7.2)"); + strerr_die2x(100,FATAL,ERR_BOUNCE); if (str_equal(sender,"#@[]")) - strerr_die2x(100,FATAL,"I don't distribute bounce messages (#5.7.2)"); + strerr_die2x(100,FATAL,ERR_BOUNCE); if (flagsublist) if (!sublistmatch(sender)) - strerr_die2x(100,FATAL,"this message is not from my parent list (#5.7.2)"); + strerr_die2x(100,FATAL,ERR_NOT_PARENT); } + innum = msgnum; /* innum = incoming */ + outnum = msgnum; /* outnum = outgoing */ + if (flagsublist && !flagarchived) { /* msgnum = archive */ + pos = byte_rchr(sublist.s,sublist.len,'@'); /* checked in sublistmatch */ + if (str_start(sender+pos,"-return-")) + pos += 8; + pos += scan_ulong(sender+pos,&innum); + if (!flagarchived && innum && sender[pos] == '-') + outnum = innum; + } + szmsgnum[fmt_ulong(szmsgnum,outnum)] = '\0'; + set_cpnum(szmsgnum); /* for copy */ if (flagarchived) { if (!stralloc_copys(&fnadir,"archive/")) die_nomem(); - if (!stralloc_catb(&fnadir,strnum,fmt_ulong(strnum,msgnum / 100))) die_nomem(); + if (!stralloc_catb(&fnadir,strnum, + fmt_ulong(strnum,outnum / 100))) die_nomem(); if (!stralloc_copy(&fnaf,&fnadir)) die_nomem(); if (!stralloc_cats(&fnaf,"/")) die_nomem(); - if (!stralloc_catb(&fnaf,strnum,fmt_uint0(strnum,(unsigned int) (msgnum % 100),2))) die_nomem(); + if (!stralloc_catb(&fnaf,strnum,fmt_uint0(strnum, + (unsigned int) (outnum % 100),2))) die_nomem(); if (!stralloc_0(&fnadir)) die_nomem(); if (!stralloc_0(&fnaf)) die_nomem(); if (mkdir(fnadir.s,0755) == -1) if (errno != error_exist) - strerr_die4sys(111,FATAL,"unable to create ",fnadir.s,": "); + strerr_die4sys(111,FATAL,ERR_CREATE,fnadir.s,": "); fdarchive = open_trunc(fnaf.s); if (fdarchive == -1) - strerr_die4sys(111,FATAL,"unable to write ",fnaf.s,": "); + strerr_die4sys(111,FATAL,ERR_WRITE,fnaf.s,": "); substdio_fdbuf(&ssarchive,write,fdarchive,archivebuf,sizeof(archivebuf)); + /* return-path to archive */ + if (!stralloc_copys(&line,"Return-Path: <")) die_nomem(); + if (sender) { /* same as qmail-local */ + if (!quote2(&qline,sender)) die_nomem(); + for (i = 0;i < qline.len;++i) if (qline.s[i] == '\n') qline.s[i] = '_'; + if (!stralloc_cat(&line,&qline)) die_nomem(); + } + if (!stralloc_cats(&line,">\n")) die_nomem(); + if (substdio_put(&ssarchive,line.s,line.len) == -1) die_archive(); } + if (flagqmqp) { + if (qmail_open(&qq,&qmqpservers) == -1) /* open qmqp */ + strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE); + } else if (qmail_open(&qq,(stralloc *) 0) == -1) /* open queue */ + strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE); + if (!flagsublist) { - puts("Mailing-List: "); - put(mailinglist.s,mailinglist.len); - puts("\n"); + getconf_line(&mailinglist,"mailinglist",1,FATAL,dir); + qa_puts("Mailing-List: "); + qa_put(mailinglist.s,mailinglist.len); + if (getconf_line(&line,"listid",0,FATAL,dir)) { + flaglistid = 1; + qmail_puts(&qq,"\nList-ID: "); + qmail_put(&qq,line.s,line.len); + } + qa_puts("\n"); } - put(headeradd.s,headeradd.len); - put(mydtline.s,mydtline.len); + copy(&qq,"headeradd",'H',FATAL); + qa_put(mydtline.s,mydtline.len); flagmlwasthere = 0; flaginheader = 1; + flagfoundokpart = 1; flagbadfield = 0; - + flagbadpart = 0; + flagseenext = 0; + flagsubline = 0; + flagfromline = 0; + flagreceived = 0; for (;;) { if (getln(&ss0,&line,&match,'\n') == -1) - strerr_die2sys(111,FATAL,"unable to read input: "); - + strerr_die2sys(111,FATAL,ERR_READ_INPUT); if (flaginheader && match) { - if (line.len == 1) + if (line.len == 1) { /* end of header */ flaginheader = 0; - if ((line.s[0] != ' ') && (line.s[0] != '\t')) { - flagbadfield = 0; + if (flagindexed) /* std entry */ + r = idx_copy_insertsubject(); /* all indexed lists */ + if (flagprefixed && !flagsublist) { + qa_puts("Subject:"); + if (!flagindexed) { /* non-indexed prefixed lists */ + concatHDR(subject.s,subject.len,&lines,FATAL); + decodeHDR(lines.s,lines.len,&qline,charset.s,FATAL); + r = unfoldHDR(qline.s,qline.len,&lines, + charset.s,&dcprefix,1,FATAL); + } + if (!(r & 2)) { + qmail_puts(&qq," "); + if (serial == prefix.len) + qmail_put(&qq,prefix.s,prefix.len); + else { + qmail_put(&qq,prefix.s,serial); + qmail_puts(&qq,szmsgnum); + qmail_put(&qq,prefix.s+serial+1,prefix.len-serial-1); + } + } + qa_put(subject.s,subject.len); + } + /* do other stuff to do with post header processing here */ + if (content.len) { /* get MIME boundary, if exists */ + concatHDR(content.s,content.len,&qline,FATAL); + if (!stralloc_copy(&content,&qline)) die_nomem(); + + if (flagtrailer && /* trailer only for some multipart */ + case_startb(content.s,content.len,"multipart/")) + if (!case_startb(content.s+10,content.len-10,"mixed") && + !case_startb(content.s+10,content.len-10,"digest") && + !case_startb(content.s+10,content.len-10,"parallel")) + flagtrailer = 0; + + cp = content.s; + cpafter = cp + content.len; /* check after each ';' */ + while ((cp += byte_chr(cp,cpafter-cp,';')) != cpafter) { + ++cp; + while (cp < cpafter && + (*cp == ' ' || *cp == '\t' || *cp == '\n')) ++cp; + if (case_startb(cp,cpafter-cp,"boundary=")) { + cp += 9; /* after boundary= */ + if (*cp == '"') { /* quoted boundary */ + ++cp; + cpstart = cp; + while (cp < cpafter && *cp != '"') ++cp; + if (cp == cpafter) + strerr_die1x(100,ERR_MIME_QUOTE); + } else { /* non-quoted boundary */ + cpstart = cp; /* find terminator of boundary */ + while (cp < cpafter && *cp != ';' && + *cp != ' ' && *cp != '\t' && *cp != '\n') ++cp; + } + if (!stralloc_copys(&boundary,"--")) die_nomem(); + if (!stralloc_catb(&boundary,cpstart,cp-cpstart)) + die_nomem(); + flagfoundokpart = 0; + if (!constmap_init(&mimeremovemap,mimeremove.s,mimeremove.len,0)) + die_nomem(); + flagbadpart = 1; /* skip before first boundary */ + qa_puts("\n"); /* to make up for the lost '\n' */ + } + } + } + } else if ((*line.s != ' ') && (*line.s != '\t')) { + flagsubline = 0; + flagfromline = 0; + flagbadfield = 0; + flagarchiveonly = 0; + flagcontline = 0; if (constmap(&headerremovemap,line.s,byte_chr(line.s,line.len,':'))) flagbadfield = 1; - if (case_startb(line.s,line.len,"mailing-list:")) - flagmlwasthere = 1; - if (line.len == mydtline.len) + if ((flagnoreceived || !flagreceived) && + case_startb(line.s,line.len,"Received:")) { + if (!flagreceived) { /* get date from first rec'd */ + flagreceived = 1; /* line (done by qmail) */ + pos = byte_chr(line.s,line.len,';'); + if (pos != line.len) /* has '\n' */ + if (!stralloc_copyb(&received,line.s+pos+2,line.len - pos - 3)) + die_nomem(); + } else { /* suppress, but archive */ + flagarchiveonly = 1; /* but do not suppress the */ + flagbadfield = 1; /* top one added by qmail */ + } + } else if (case_startb(line.s,line.len,"Mailing-List:")) + flagmlwasthere = 1; /* sublists always ok ezmlm masters */ + else if (mlheader && case_startb(line.s,line.len,mlheader)) + flagmlwasthere = 1; /* mlheader treated as ML */ + else if ((mimeremove.len || flagtrailer) && /* else no MIME need*/ + case_startb(line.s,line.len,"Content-Type:")) { + if (!stralloc_copyb(&content,line.s+13,line.len-13)) die_nomem(); + flagcontline = 1; + } else if (case_startb(line.s,line.len,"Subject:")) { + if (!stralloc_copyb(&subject,line.s+8,line.len-8)) die_nomem(); + flagsubline = 1; + if (flagprefixed && !flagsublist) /* don't prefix for sublists */ + flagbadfield = 1; /* we'll print our own */ + } else if (flagtrailer && + case_startb(line.s,line.len,"Content-Transfer-Encoding:")) { + cp = line.s + 26; + cpafter = cp + line.len; + while (cp < cpafter && (*cp == ' ' || *cp == '\t')) ++cp; + if (case_startb(cp,cpafter-cp,"base64")) encin = 'B'; + else if (case_startb(cp,cpafter-cp,"Quoted-Printable")) encin = 'Q'; + } else if (flaglistid && case_startb(line.s,line.len,"list-id:")) + flagbadfield = 1; /* suppress if we added our own */ + else if (flagindexed) { + + if (case_startb(line.s,line.len,"From:")) { + flagfromline = 1; + if (!stralloc_copyb(&from,line.s+5,line.len-5)) die_nomem(); + } + } else if (line.len == mydtline.len) if (!byte_diff(line.s,line.len,mydtline.s)) - strerr_die2x(100,FATAL,"this message is looping: it already has my Delivered-To line (#5.4.6)"); + strerr_die2x(100,FATAL,ERR_LOOPING); + } else { /* continuation lines */ + if (flagsubline) { + if (!stralloc_cat(&subject,&line)) die_nomem(); + } else if (flagfromline) { + if (!stralloc_cat(&from,&line)) die_nomem(); + } else if (flagcontline) { + if (!stralloc_cat(&content,&line)) die_nomem(); + } } - } - - if (!(flaginheader && flagbadfield)) - put(line.s,line.len); + } else /* body */ + msgsize += line.len; /* always for tstdig support */ + + if (!(flaginheader && flagbadfield)) { + if (boundary.len && line.len > boundary.len && + !str_diffn(line.s,boundary.s,boundary.len)) { + if (line.s[boundary.len] == '-' && line.s[boundary.len+1] == '-') { + flagbadpart = 0; /* end boundary should be output */ + if (flagtrailer) { + qmail_puts(&qq,"\n"); + qmail_put(&qq,boundary.s,boundary.len); + qmail_puts(&qq,"\nContent-Type: text/plain; charset="); + qmail_puts(&qq,charset.s); + transferenc(); /* trailer for multipart message */ + copy(&qq,"text/trailer",flagcd,FATAL); + if (flagcd == 'B') { /* need to do our own flushing */ + encodeB("",0,&qline,2,FATAL); + qmail_put(&qq,qline.s,qline.len); + } + } + } else { /* new part */ + flagbadpart = 1; /* skip lines */ + if (!stralloc_copy(&lines,&line)) die_nomem(); /* but save */ + flagseenext = 1; /* need to check Cont-type */ + } + } else if (flagseenext) { /* last was boundary, now stored */ + if (case_startb(line.s,line.len,"content-type:")) { + flagseenext = 0; /* done thinking about it */ + cp = line.s + 13; /* start of type */ + while (*cp == ' ' || *cp == '\t') ++cp; + cpstart = cp; /* end of type */ + while (*cp != '\n' && *cp != '\t' && *cp != ' ' && *cp != ';') ++cp; + if (constmap(&mimeremovemap,cpstart,cp-cpstart)) { + flagbadpart = 1; + } else { + flagfoundokpart = 1; + qa_put(lines.s,lines.len); /* saved lines */ + flagbadpart = 0; /* do this part */ + } + } else if (line.len == 1) { /* end of content desc */ + flagbadpart = 0; /* default type, so ok */ + flagseenext = 0; /* done thinking about it */ + } else /* save line in cont desc */ + if (!stralloc_cat(&lines,&line)) die_nomem(); + } + if (!flagbadpart) + qa_put(line.s,line.len); + } else if (flagarchiveonly && flagarchived) /* received headers */ + if (substdio_put(&ssarchive,line.s,line.len) == -1) die_archive(); if (!match) break; } + if (!boundary.len && flagtrailer) { + qmail_puts(&qq,"\n"); /* trailer for non-multipart message */ + if (!encin || encin == 'Q') { /* can add for QP, but not for base64 */ + copy(&qq,"text/trailer",encin,FATAL); + qmail_puts(&qq,"\n"); /* no need to flush for plain/QP */ + } + } - if (flagsublist) - if (!flagmlwasthere) - strerr_die2x(100,FATAL,"sublist messages must have Mailing-List (#5.7.2)"); - if (!flagsublist) + cumsize += (msgsize + 128L) >> 8; /* round to 256 byte 'records' */ + /* check message tag */ + if (flagsublist) { /* sublists need tag if selected/suppt*/ + if (flaglog) + if ((ret = checktag(dir,innum,hash_lo+1L,"m",(char *) 0,hashout))) { + if (*ret) strerr_die2x(111,FATAL,ret); + else strerr_die2x(100,FATAL,ERR_NOT_PARENT); + } + if (!flagmlwasthere) /* sublists need ML header */ + strerr_die2x(100,FATAL,ERR_SUBLIST); + } else /* others are not allowed to have one */ if (flagmlwasthere) - strerr_die2x(100,FATAL,"message already has Mailing-List (#5.7.2)"); + strerr_die2x(100,FATAL,ERR_MAILING_LIST); + if (!flagfoundokpart) /* all parts were on the strip list */ + strerr_die2x(100,FATAL,ERR_BAD_ALL); if (flagarchived) { if (substdio_flush(&ssarchive) == -1) die_archive(); if (fsync(fdarchive) == -1) die_archive(); - if (fchmod(fdarchive,0744) == -1) die_archive(); + if (fchmod(fdarchive,MODE_ARCHIVE | 0700) == -1) die_archive(); if (close(fdarchive) == -1) die_archive(); /* NFS stupidity */ } - numwrite(); + if (flaglog) { + tagmsg(dir,innum,sender,"m",hashout,qq.msgbytes,53L,FATAL); + hashout[COOKIE] = '\0'; + } + numwrite(); if (!stralloc_copy(&line,&outlocal)) die_nomem(); if (!stralloc_cats(&line,"-return-")) die_nomem(); - if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,msgnum))) die_nomem(); + if (!stralloc_cats(&line,szmsgnum)) die_nomem(); if (!stralloc_cats(&line,"-@")) die_nomem(); if (!stralloc_cat(&line,&outhost)) die_nomem(); if (!stralloc_cats(&line,"-@[]")) die_nomem(); if (!stralloc_0(&line)) die_nomem(); - - qmail_from(&qq,line.s); - - for (i = 0;i < 53;++i) { - ch = 64 + i; - if (!stralloc_copys(&fnsub,"subscribers/")) die_nomem(); - if (!stralloc_catb(&fnsub,&ch,1)) strerr_die2x(111,FATAL,"out of memory"); - if (!stralloc_0(&fnsub)) strerr_die2x(111,FATAL,"out of memory"); - fd = open_read(fnsub.s); - if (fd == -1) { - if (errno != error_noent) - strerr_die4sys(111,FATAL,"unable to read ",fnsub.s,": "); - } - else { - substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf)); - substdio_fdbuf(&ssout,mywrite,-1,outbuf,sizeof(outbuf)); - if (substdio_copy(&ssout,&ssin) != 0) - strerr_die4sys(111,FATAL,"unable to read ",fnsub.s,": "); - close(fd); - } - } - - switch(qmail_close(&qq)) { - case 0: + qmail_from(&qq,line.s); /* envelope sender */ + subs = putsubs(dir,hash_lo,hash_hi,subto,1,FATAL); /* subscribers */ + if (flagsublist) hash_lo++; + + if (*(err = qmail_close(&qq)) == '\0') { + if (flaglog) /* mysql logging */ + (void) logmsg(dir,outnum,hash_lo,subs,flagsublist ? 3 : 4); + closesql(); strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0; strerr_die2x(0,"ezmlm-send: info: qp ",strnum); - default: + } else { --msgnum; + cumsize -= (msgsize + 128L) >> 8; numwrite(); - strerr_die2x(111,FATAL,"temporary qmail-queue error"); + strerr_die3x(111,FATAL,ERR_TMP_QMAIL_QUEUE,err + 1); } }