+/* $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 "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];
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;
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;
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)
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;
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");
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();
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);
}
}