/* $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" #include "error.h" #include "qmail.h" #include "env.h" #include "lock.h" #include "sig.h" #include "open.h" #include "getln.h" #include "case.h" #include "scan.h" #include "str.h" #include "fmt.h" #include "readwrite.h" #include "exit.h" #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" #include "seek.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 [-aAcClLqQrR] [-h header] dir"); } void die_nomem() { 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]; int flagsublist; stralloc sublist = {0}; stralloc mailinglist = {0}; stralloc outlocal = {0}; stralloc outhost = {0}; stralloc headerremove = {0}; struct constmap headerremovemap; stralloc mimeremove = {0}; struct constmap mimeremovemap; char *dir; struct qmail qq; substdio ssin; char inbuf[1024]; substdio ssout; char outbuf[1]; char textbuf[512]; substdio sstext; unsigned int mywrite(fd,buf,len) int fd; char *buf; unsigned int len; { qmail_put(&qq,buf,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,ERR_WRITE,fnaf.s,": "); } void die_numnew() { strerr_die3sys(111,FATAL,ERR_CREATE,"numnew: "); } 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 qa_puts(buf) char *buf; { qmail_puts(&qq,buf); if (flagarchived) if (substdio_puts(&ssarchive,buf) == -1) die_archive(); } int sublistmatch(sender) char *sender; { unsigned int i; unsigned int j; j = str_len(sender); if (j < sublist.len) return 0; i = byte_rchr(sublist.s,sublist.len,'@'); if (i == sublist.len) return 1; if (byte_diff(sublist.s,i,sender)) return 0; if (case_diffb(sublist.s + i,sublist.len - i,sender + j - (sublist.len - i))) return 0; return 1; } substdio ssnumnew; char numnewbuf[16]; char buf0[256]; substdio ss0 = SUBSTDIO_FDBUF(read,0,buf0,sizeof(buf0)); substdio ss1 = SUBSTDIO_FDBUF(read,0,buf0,sizeof(buf0)); void numwrite() { /* this one deals with msgnum, not outnum! */ int fd; fd = open_trunc("numnew"); if (fd == -1) die_numnew(); 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_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; int k; 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(); k = author_name(&cp,lines.s,lines.len); decodeHDR(cp,k,&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; { 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; unsigned int i; int r,fd; int flaginheader; int flagbadfield; int flagbadpart; int flagseenext; int flagsubline; int flagfromline; int flagcontline; int flagarchiveonly; int flagtrailer = 1; int flagalttrailer = 0; /* std trailer if at all except if -a */ unsigned int pos; int opt; char *cp, *cpstart, *cpafter; umask(022); sig_pipeignore(); while ((opt = getopt(argc,argv,"aAcCh:H:lLrRqQs:S:vV")) != opteof) switch(opt) { case 'a': flagalttrailer++; break; case 'A': flagalttrailer = 0; break; 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,ERR_SWITCH,dir,": "); fdlock = open_append("lock"); if (fdlock == -1) strerr_die4sys(111,FATAL,ERR_OPEN,dir,"/lock: "); if (lock_ex(fdlock) == -1) 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 (!flagalttrailer) { /* skip test if -a */ if ((fd = open_read("text/trailer")) == -1) { /* see if trailer */ if (errno == error_noent) flagtrailer = 0; else strerr_die2sys(111,ERR_OPEN,"text/trailer: "); } else { close(fd); } } 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); set_cpoutlocal(&outlocal); set_cpouthost(&outhost); flagsublist = getconf_line(&sublist,"sublist",0,FATAL,dir); 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(&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(); if (!stralloc_cats(&mydtline,"@")) die_nomem(); if (!stralloc_catb(&mydtline,outhost.s,outhost.len)) die_nomem(); if (!stralloc_cats(&mydtline,"\n")) die_nomem(); if (sender) { if (!*sender) strerr_die2x(100,FATAL,ERR_BOUNCE); if (str_equal(sender,"#@[]")) strerr_die2x(100,FATAL,ERR_BOUNCE); if (flagsublist) if (!sublistmatch(sender)) 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 (flagalttrailer == 1) { while (flagalttrailer) { /* verify no '##L/##H */ if (getln(&ss1,&line,&match,'\n') == -1) strerr_die2sys(111,FATAL,ERR_READ_INPUT); if (!match) break; pos = 0; while ((pos += byte_chr(line.s + pos,line.len - pos,'#')) < line.len) { if (++pos + 1 < line.len) { if (line.s[pos] == '#') { if (line.s[pos + 1] == 'H' || line.s[pos + 1] == 'L') { flagalttrailer = 0; break; } } } } } if (seek_begin(0) == -1) /* rewind */ strerr_die2sys(111,FATAL,ERR_SEEK_INPUT); } if (flagarchived) { if (!stralloc_copys(&fnadir,"archive/")) 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) (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,ERR_CREATE,fnadir.s,": "); fdarchive = open_trunc(fnaf.s); if (fdarchive == -1) 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) { 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"); } if (flagalttrailer) qmail_put(&qq,"#",1); /* VERH body signal */ 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,ERR_READ_INPUT); if (flaginheader && match) { if (line.len == 1) { /* end of header */ flaginheader = 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 ((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,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(); } } } 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 */ if (flagalttrailer) copy(&qq,"text/alttrailer",flagcd,FATAL); else 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 */ flagfoundokpart = 1; /* this is part of a multipart msg */ flagseenext = 0; /* done thinking about it */ qa_put(lines.s,lines.len); /* saved lines */ } 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 */ if (flagalttrailer) copy(&qq,"text/alttrailer",encin,FATAL); else copy(&qq,"text/trailer",encin,FATAL); qmail_puts(&qq,"\n"); /* no need to flush for plain/QP */ } } 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,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,MODE_ARCHIVE | 0700) == -1) die_archive(); if (close(fdarchive) == -1) die_archive(); /* NFS stupidity */ } 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_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); /* 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); } else { --msgnum; cumsize -= (msgsize + 128L) >> 8; numwrite(); strerr_die3x(111,FATAL,ERR_TMP_QMAIL_QUEUE,err + 1); } }