X-Git-Url: https://git.distorted.org.uk/~mdw/ezmlm/blobdiff_plain/5b62e993b0af39700031c2875d7f6654e6a02850..f8beb284087c279acfb30506f5bb32baa4949b44:/ezmlm-get.c diff --git a/ezmlm-get.c b/ezmlm-get.c new file mode 100644 index 0000000..f8146ac --- /dev/null +++ b/ezmlm-get.c @@ -0,0 +1,1459 @@ +/*$Id: ezmlm-get.c,v 1.113 1999/11/22 01:47:45 lindberg Exp $*/ +/*$Name: ezmlm-idx-040 $*/ + +#include +#include +#include "alloc.h" +#include "error.h" +#include "stralloc.h" +#include "str.h" +#include "env.h" +#include "sig.h" +#include "slurp.h" +#include "getconf.h" +#include "strerr.h" +#include "byte.h" +#include "getln.h" +#include "case.h" +#include "qmail.h" +#include "substdio.h" +#include "readwrite.h" +#include "seek.h" +#include "quote.h" +#include "datetime.h" +#include "now.h" +#include "date822fmt.h" +#include "fmt.h" +#include "sgetopt.h" +#include "cookie.h" +#include "makehash.h" +#include "copy.h" +#include "constmap.h" +#include "subscribe.h" +#include "idxthread.h" +#include "idx.h" +#include "mime.h" +#include "errtxt.h" + +int flagdo = 1; /* React to commands (doesn't affect -dig)*/ +int flagbottom = 1; /* copy text/bottom + request */ +int flagpublic = 2; /* 0 = non-public, 1 = public, 2 = respect*/ + /* dir/public. */ +char flagcd = '\0'; /* default: don't use quoted-printable */ +int flagsub = 0; /* =1 subscribers only for get/index/thread */ +char *digsz = + "from\\to\\subject\\reply-to\\date\\message-id\\cc\\" + "mime-version\\content-type\\content-transfer-encoding"; + +#define FATAL "ezmlm-get: fatal: " + +void die_usage() { + strerr_die1x(100, + "ezmlm-get: usage: " + "ezmlm-get [-bBcClLpPsSvV] [-f fmt] [digestcode]"); +} + +void die_nomem() { strerr_die2x(111,FATAL,ERR_NOMEM); } + +void die_badaddr() +{ + strerr_die2x(100,FATAL,ERR_BAD_ADDRESS); +} + +stralloc inhost = {0}; +stralloc outhost = {0}; +stralloc inlocal = {0}; +stralloc outlocal = {0}; +stralloc listname = {0}; +stralloc mailinglist = {0}; +stralloc qmqpservers = {0}; +stralloc fn = {0}; +stralloc moddir = {0}; +stralloc charset = {0}; +stralloc mydtline = {0}; +stralloc digheaders = {0}; +stralloc seed = {0}; +struct constmap digheadersmap; + +char schar[] = "00_"; +stralloc listno = {0}; +void *psql = (void *) 0; + +datetime_sec when; +struct datetime dt; +unsigned long cumsize = 0L; /* cumulative msgs / 256 */ +unsigned long cumsizen = 0L; /* new cumulative msgs / 256 */ +unsigned long max = 0L; /* Last message in archive */ +unsigned long msgsize = 0L; /* for digest accounting */ +datetime_sec digwhen; /* last digest */ + +char strnum[FMT_ULONG]; +char szmsgnum[FMT_ULONG]; +char date[DATE822FMT]; +char boundary[COOKIE]; +char hashout[COOKIE]; +stralloc line = {0}; +stralloc line2 = {0}; +stralloc qline = {0}; +stralloc quoted = {0}; +stralloc msgnum = {0}; +stralloc num = {0}; +stralloc subject = {0}; + +/* for copy archive */ +stralloc archdate = {0}; +stralloc archfrom = {0}; +stralloc archto = {0}; +stralloc archcc = {0}; +stralloc archsubject = {0}; +stralloc archmessageid = {0}; +stralloc archkeywords = {0}; +stralloc archblanklines = {0}; +char archtype=' '; + +/* for mods on non-public lists (needed for future fuzzy sub dbs) */ +stralloc mod = {0}; /* moderator addr for non-public lists */ +char *pmod = (char *) 0; /* pointer to above */ + +/* for digest */ +stralloc ddir = {0}; +stralloc edir = {0}; + +int act = AC_NONE; /* Action we do */ +int flageditor = 0; /* if we're invoked for within dir/editor */ +struct stat st; + +int flaglocked = 0; /* if directory is locked */ +int flagarchived; /* if list is archived */ +int flagindexed; /* if list is indexed */ +int flagq = 0; /* don't use 'quoted-printable' */ + +char *dir; +char *workdir; +char *sender; +char *digestcode; + +struct qmail qq; +int qqwrite(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; +} + +char qqbuf[1]; +substdio ssqq = SUBSTDIO_FDBUF(qqwrite,-1,qqbuf,sizeof(qqbuf)); + +char inbuf[1024]; +substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,sizeof(inbuf)); +substdio ssin2 = SUBSTDIO_FDBUF(read,0,inbuf,sizeof(inbuf)); + +substdio ssnum; +char numbuf[16]; + +substdio sstext; +char textbuf[1024]; + +substdio ssindex; +char indexbuf[1024]; + +int fdlock; + +void lockup() +/* lock unless locked */ +{ + if(!flaglocked) { + fdlock = open_append("lock"); + if (fdlock == -1) + strerr_die2sys(111,FATAL,ERR_OPEN_LOCK); + if (lock_ex(fdlock) == -1) { + close(fdlock); + strerr_die2sys(111,FATAL,ERR_OBTAIN_LOCK); + } + flaglocked = 1; + } +} + +void unlock() +/* unlock if locked */ +{ + if (flaglocked) { + close(fdlock); + flaglocked = 0; + } +} + +void code_qput(s,n) +char *s; +unsigned int n; +{ + if (!flagcd) + qmail_put(&qq,s,n); + else { + if (flagcd == 'B') + encodeB(s,n,&qline,0,FATAL); + else + encodeQ(s,n,&qline,FATAL); + qmail_put(&qq,qline.s,qline.len); + msgsize += qline.len; + } +} + +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 zapnonsub(szerr) +/* fatal error if flagsub is set and sender is not a subscriber */ +/* expects the current dir to be the list dir. Error is szerr */ +/* added check for undefined sender as a precaution */ +char *szerr; +{ + if (sender && *sender) { /* "no sender" is not a subscriber */ + if (!flagsub) + return; + if (issub(dir,sender,(char *) 0,FATAL)) + return; /* subscriber */ + if (issub(ddir.s,sender,(char *) 0,FATAL)) + return; /* digest subscriber */ + if (issub(edir.s,sender,(char *) 0,FATAL)) + return; /* allow addresses */ + } + strerr_die4x(100,FATAL,ERR_SUBSCRIBER_CAN,szerr,ERR_571); +} + +void tosender() +{ + qmail_puts(&qq,"To: "); + if (!quote2("ed,sender)) die_nomem(); + qmail_put(&qq,quoted.s,quoted.len); + qmail_puts(&qq,"\n"); +} + +void get_num() +{ +/* read dir/num -> max. max/cumsizen left alone if not present */ +/* Both of these should have been initialized to 0L */ + + unsigned int pos; + if (getconf_line(&num,"num",0,FATAL,dir)) { + if(!stralloc_0(&num)) die_nomem(); + pos = scan_ulong(num.s,&max); + if (num.s[pos] == ':') pos++; + scan_ulong(num.s+pos,&cumsizen); + } +} + +unsigned long dignum() +{ +/* return dignum if exists, 0 otherwise. */ + + unsigned long retval; + if (!stralloc_copys(&num,"")) die_nomem(); /* zap */ + getconf_line(&num,"dignum",0,FATAL,dir); + if(!stralloc_0(&num)) die_nomem(); + scan_ulong(num.s,&retval); + return retval; +} + +void write_ulong(num,cum,dat,fn,fnn) +/* write num to "fnn" add ':' & cum if cum <>0, then move "fnn" to "fn" */ +char *fn, *fnn; +unsigned long num,cum,dat; +{ + int fd; + + fd = open_trunc(fnn); + if (fd == -1) + strerr_die6sys(111,FATAL,ERR_CREATE,dir,"/",fnn,": "); + substdio_fdbuf(&ssnum,write,fd,numbuf,sizeof(numbuf)); + if (substdio_put(&ssnum,strnum,fmt_ulong(strnum,num)) == -1) + strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn,": "); + if (substdio_puts(&ssnum,":") == -1) + strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn,": "); + if (substdio_put(&ssnum,strnum,fmt_ulong(strnum,cum)) == -1) + strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn,": "); + if (dat) { + if (substdio_puts(&ssnum,":") == -1) + strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn,": "); + if (substdio_put(&ssnum,strnum,fmt_ulong(strnum,dat)) == -1) + strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn,": "); + } + if (substdio_puts(&ssnum,"\n") == -1) + strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnn,": "); + if (substdio_flush(&ssnum) == -1) + strerr_die6sys(111,FATAL,ERR_FLUSH,dir,"/",fnn,": "); + if (fsync(fd) == -1) + strerr_die6sys(111,FATAL,ERR_SYNC,dir,"/",fnn,": "); + if (close(fd) == -1) + strerr_die6sys(111,FATAL,ERR_CLOSE,dir,"/",fnn,": "); + if (rename(fnn,fn) == -1) + strerr_die4sys(111,FATAL,ERR_MOVE,fnn,": "); +} + +void normal_bottom(format) +char format; +/* Copies bottom text and the original message to the new message */ +{ + if (flagbottom) { + copy(&qq,"text/bottom",flagcd,FATAL); + if (flagcd && format != RFC1153) { + if (flagcd == 'B') { + encodeB("",0,&line,2,FATAL); /* flush */ + qmail_put(&qq,line.s,line.len); + } + qmail_puts(&qq,"\n--"); + qmail_put(&qq,boundary,COOKIE); + qmail_puts(&qq,"\nContent-Type: message/rfc822"); + qmail_puts(&qq,"\nContent-Disposition: inline; filename=request.msg\n\n"); + } + qmail_puts(&qq,"Return-Path: <"); + if (!quote2("ed,sender)) die_nomem(); + qmail_put(&qq,quoted.s,quoted.len); + qmail_puts(&qq,">\n"); + if (seek_begin(0) == -1) + strerr_die2sys(111,FATAL,ERR_SEEK_INPUT); + if (substdio_copy(&ssqq,&ssin2) != 0) + strerr_die2sys(111,FATAL,ERR_READ_INPUT); + } else { + if (flagcd == 'B' && format != RFC1153) { + encodeB("",0,&line,2,FATAL); /* flush */ + qmail_put(&qq,line.s,line.len); + } + } +} + +void presub(from,to,subject,factype,format) +/* Starts within header, outputs "subject" and optional headers, terminates*/ +/* header and handles output before table-of-contents */ +unsigned long from,to; +stralloc *subject; +int factype; /* action type (AC_THREAD, AC_GET, AC_DIGEST) */ +char format; /* output format type (see idx.h) */ +{ + qmail_puts(&qq,"MIME-Version: 1.0\n"); + switch(format) { + case MIME: + case VIRGIN: + case NATIVE: + case MIXED: + qmail_puts(&qq,"Content-Type: multipart/"); + if (format == MIXED) + qmail_puts(&qq,"mixed"); + else + qmail_puts(&qq,"digest"); + qmail_puts(&qq,"; boundary="); + qmail_put(&qq,boundary,COOKIE); + qmail_puts(&qq,"\nSubject: "); + qmail_put(&qq,subject->s,subject->len); + qmail_puts(&qq,"\n\n\n--"); + qmail_put(&qq,boundary,COOKIE); + qmail_puts(&qq,"\nContent-Type: text/plain; charset="); + qmail_puts(&qq,charset.s); + transferenc(); /* content-transfer-enc header if needed */ + qmail_puts(&qq,"\n"); + break; + case RFC1153: + qmail_puts(&qq,"Content-type: text/plain; charset="); + qmail_puts(&qq,charset.s); + qmail_puts(&qq,"\nSubject: "); + qmail_put(&qq,subject->s,subject->len); + qmail_puts(&qq,"\n\n"); + flagcd = '\0'; /* We make 8-bit messages, not QP/bas64 for rfc1153 */ + break; /* Since messages themselves aren't encoded */ + } + if (!stralloc_cats(subject,"\n\n")) die_nomem(); + code_qput(subject->s,subject->len); + if (format != NATIVE && factype != AC_THREAD && factype != AC_INDEX) { + if (!stralloc_copys(&line,TXT_TOP_TOPICS)) die_nomem(); + if (!stralloc_cats(&line,TXT_TOP_MESSAGES)) die_nomem(); + if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,from))) die_nomem(); + if (!stralloc_cats(&line,TXT_TOP_THROUGH)) die_nomem(); + if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,to))) die_nomem(); + if (!stralloc_cats(&line,TXT_TOP_LAST)) die_nomem(); + code_qput(line.s,line.len); + } +} + +void postsub(factype,format) +/* output after TOC and before first message. */ +int factype; /* action type (AC_THREAD, AC_GET, AC_DIGEST) */ +char format; /* output format type (see idx.h) */ +{ + code_qput(TXT_ADMINISTRIVIA,str_len(TXT_ADMINISTRIVIA)); + if(factype == AC_DIGEST) { + copy(&qq,"text/digest",flagcd,FATAL); + if (flagcd == 'B') { + encodeB("",0,&line,2,FATAL); /* flush */ + qmail_put(&qq,line.s,line.len); + } + } else + normal_bottom(format); + if (!flagcd || format == RFC1153) + qmail_puts(&qq,"\n----------------------------------------------------------------------\n"); + else + qmail_puts(&qq,"\n"); +} + +void postmsg(format) +char format; +{ + switch(format) { + case MIME: + case VIRGIN: + case NATIVE: + case MIXED: + qmail_puts(&qq,"\n--"); + qmail_put(&qq,boundary,COOKIE); /* digest boundary */ + qmail_puts(&qq,"--\n"); + break; + case RFC1153: + qmail_puts(&qq,"End of "); + qmail_put(&qq,listname.s,listname.len); + qmail_puts(&qq," Digest"); + qmail_puts(&qq,"\n***********************************\n"); + break; + } +} + +void copymsg(msg,fd,format) +/* Copy archive message "msg" itself from open file handle fd, in "format" */ +unsigned long msg; int fd; char format; +{ + int match; + int flaginheader; + int flagskipblanks; + int flaggoodfield; + + switch(format) { + case VIRGIN: + case NATIVE: + substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf)); + for (;;) { + if (getln(&sstext,&line,&match,'\n') == -1) + strerr_die4sys(111,FATAL,ERR_READ,line.s,": "); + if (match) { + qmail_put(&qq,line.s,line.len); + msgsize += line.len; + } else + break; + } + break; + case MIME: + case MIXED: + flaginheader = 1; + flaggoodfield = 0; + substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf)); + for (;;) { + if (getln(&sstext,&line,&match,'\n') == -1) + strerr_die4sys(111,FATAL,ERR_READ,line.s,": "); + if (match) { + if (flaginheader) { + if (line.len == 1) { + flaginheader = 0; + flaggoodfield = 1; + } else if (line.s[0] != ' ' && line.s[0] != '\t') { + flaggoodfield = 0; + if (constmap(&digheadersmap,line.s, + byte_chr(line.s,line.len,':'))) + flaggoodfield = 1; + } + if (flaggoodfield) { + qmail_put(&qq,line.s,line.len); /* header */ + msgsize += line.len; + } + } else { + qmail_put(&qq,line.s,line.len); /* body */ + msgsize += line.len; + } + } else + break; + } + break; + case RFC1153: /* Not worth optimizing. Rarely used */ + flaginheader = 1; + flagskipblanks = 1; /* must skip terminal blanks acc to rfc1153 */ + archtype = ' '; /* rfc1153 requires ordered headers */ + if (!stralloc_copys(&archblanklines,"")) die_nomem(); + substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf)); + for (;;) { + if (getln(&sstext,&line,&match,'\n') == -1) + strerr_die4sys(111,FATAL,ERR_READ,line.s,": "); + if (match) { + if (flaginheader) { + if (line.len == 1) { + flaginheader = 0; + if (archdate.len) { + qmail_put(&qq,archdate.s,archdate.len); + archdate.len = 0; + msgsize += archdate.len; + } + if (archto.len) { + qmail_put(&qq,archto.s,archto.len); + msgsize += archto.len; + archto.len = 0; + } + if (archfrom.len) { + qmail_put(&qq,archfrom.s,archfrom.len); + msgsize += archfrom.len; + archfrom.len = 0; + } + if (archcc.len) { + qmail_put(&qq,archcc.s,archcc.len); + msgsize += archcc.len; + archcc.len = 0; + } + if (archsubject.len) { + qmail_put(&qq,archsubject.s,archsubject.len); + msgsize += archsubject.len; + archsubject.len = 0; + } + if (archmessageid.len) { + qmail_put(&qq,archmessageid.s,archmessageid.len); + msgsize += archmessageid.len; + archmessageid.len = 0; + } + if (archkeywords.len) { + qmail_put(&qq,archkeywords.s,archkeywords.len); + msgsize += archkeywords.len; + archkeywords.len = 0; + } + qmail_puts(&qq,"\n"); + } else if (line.s[0] == ' ' || line.s[0] == '\t') { + switch (archtype) { /* continuation lines */ + case ' ': + break; + case 'D': + if (!stralloc_cat(&archdate,&line)) die_nomem(); break; + case 'F': + if (!stralloc_cat(&archfrom,&line)) die_nomem(); break; + case 'T': + if (!stralloc_cat(&archto,&line)) die_nomem(); break; + case 'C': + if (!stralloc_cat(&archcc,&line)) die_nomem(); break; + case 'S': + if (!stralloc_cat(&archsubject,&line)) die_nomem(); break; + case 'M': + if (!stralloc_cat(&archmessageid,&line)) die_nomem(); break; + case 'K': + if (!stralloc_cat(&archkeywords,&line)) die_nomem(); break; + default: + strerr_die2x(111,FATAL, + "Program error: Bad archive header type"); + } + } else { + archtype = ' '; + if (case_startb(line.s,line.len,"cc:")) { + archtype='C'; + if (!stralloc_copy(&archcc,&line)) die_nomem(); + } + else if (case_startb(line.s,line.len,"date:")) { + archtype='D'; + if (!stralloc_copy(&archdate,&line)) die_nomem(); + } + else if (case_startb(line.s,line.len,"from:")) { + archtype='F'; + if (!stralloc_copy(&archfrom,&line)) die_nomem(); + } + else if (case_startb(line.s,line.len,"keywords:")) { + archtype='K'; + if (!stralloc_copy(&archkeywords,&line)) die_nomem(); + } + else if (case_startb(line.s,line.len,"message-id:")) { + archtype='M'; + if (!stralloc_copy(&archmessageid,&line)) die_nomem(); + } + else if (case_startb(line.s,line.len,"subject:")) { + archtype='S'; + if (!stralloc_copy(&archsubject,&line)) die_nomem(); + } + else if (case_startb(line.s,line.len,"to:")) { + archtype='T'; + if (!stralloc_copy(&archto,&line)) die_nomem(); + } + } + } else if (line.len == 1) { + if (!flagskipblanks) + if (!stralloc_copys(&archblanklines,"\n")) die_nomem(); + } else { + if (archblanklines.len) { + qmail_put(&qq,archblanklines.s,archblanklines.len); + archblanklines.len = 0; + } + flagskipblanks = 0; + qmail_put(&qq,line.s,line.len); + msgsize += line.len; + } + } else + break; + } + break; + default: + strerr_die2x(100,FATAL,"Program error: bad format in copymsg()"); + } +} + +void mime_getbad(msg) +/* Message not found as a MIME multipart */ +unsigned long msg; +{ + qmail_puts(&qq,"\n--"); + qmail_put(&qq,boundary,COOKIE); + qmail_puts(&qq,"\nContent-Type: text/plain; charset="); + qmail_puts(&qq,charset.s); + qmail_puts(&qq,"\nContent-Disposition: inline; filename=\""); + qmail_put(&qq,listname.s,listname.len); + qmail_puts(&qq,"_"); + qmail_put(&qq,strnum,fmt_ulong(strnum,msg)); + qmail_puts(&qq,".ezm\"\n"); + transferenc(); + copy(&qq,"text/get-bad",flagcd,FATAL); +} + +void msgout(msg,format) +/* Outputs message (everything that's needed per message) */ +unsigned long msg; char format; +{ + int fd; + unsigned int len; + + if (!stralloc_copys(&fn,"archive/")) die_nomem(); + + len = fmt_ulong(strnum, msg / 100); + if (!stralloc_catb(&fn,strnum,len)) die_nomem(); + if (!stralloc_cats(&fn,"/")) die_nomem(); + len = fmt_uint0(strnum, (unsigned int) (msg % 100),2); + if (!stralloc_catb(&fn,strnum,len)) die_nomem(); + if (!stralloc_0(&fn)) die_nomem(); + + switch(format) { + case MIME: + case VIRGIN: + case NATIVE: + case MIXED: + fd = open_read(fn.s); + if (fd == -1) { + if (errno != error_noent) + strerr_die4sys(111,FATAL,ERR_OPEN,fn.s,": "); + else + mime_getbad(msg); + } else if (fstat(fd,&st) == -1 || (!(st.st_mode & 0100))) { + close(fd); + mime_getbad(msg); + } else { + qmail_puts(&qq,"\n--"); + qmail_put(&qq,boundary,COOKIE); + qmail_puts(&qq,"\nContent-Type: message/rfc822"); + qmail_puts(&qq,"\nContent-Disposition: inline; filename=\""); + qmail_put(&qq,listname.s,listname.len); + qmail_puts(&qq,"_"); + qmail_put(&qq,strnum,fmt_ulong(strnum,msg)); + qmail_puts(&qq,".ezm\"\n\n"); + copymsg(msg,fd,format); + close(fd); + } + break; + case RFC1153: + fd = open_read(fn.s); + if (fd == -1) { + if (errno != error_noent) + strerr_die4sys(111,FATAL,ERR_OPEN,fn.s,": "); + else { + qmail_puts(&qq,"\n== "); + qmail_put(&qq,strnum,fmt_ulong(strnum,msg)); + qmail_puts(&qq," ==\n\n"); + copy(&qq,"text/get-bad",flagcd,FATAL); + } + } else { + if (fstat(fd,&st) == -1 || (!(st.st_mode & 0100))) { + close(fd); + qmail_puts(&qq,"\n== "); + qmail_put(&qq,strnum,fmt_ulong(strnum,msg)); + qmail_puts(&qq," ==\n\n"); + copy(&qq,"text/get-bad",flagcd,FATAL); + } else { + copymsg(msg,fd,format); + close(fd); + } + } + qmail_puts(&qq,"\n------------------------------\n\n"); + break; + default: + strerr_die2x(100,FATAL,"Program error: Unrecognized format in msgout"); + break; + } +} + +void digest(msgtable,subtable,authtable,from,to,subj,factype,format) +/* Output digest range from-to as per msgtable/subtable (from mkthread(s)). */ +/* "Subject is the subject of the _entire_ digest/set. */ +msgentry *msgtable; subentry *subtable; authentry *authtable; +unsigned long from,to; stralloc *subj; int factype; char format; +{ + msgentry *pmsgt; + subentry *psubt; + char *cp; + int ffirstmsg; + unsigned int len; + unsigned long msg; + unsigned long subnum; + + psubt = subtable; + presub(from,to,subj,factype,format); + + if (format != NATIVE) { + while (psubt->sub) { + ffirstmsg = 1; + /* ptr to first message with this subject */ + pmsgt = msgtable + psubt->firstmsg - from; + subnum = (unsigned long) (psubt - subtable +1); + for (msg=psubt->firstmsg; msg<=to; msg++) { + if (pmsgt->subnum == subnum) { + if(ffirstmsg) { + ffirstmsg = 0; + if (!stralloc_copys(&line,"\n")) die_nomem(); + if (psubt->sublen <= HASHLEN + 2) { + if (!stralloc_cats(&line,"(null)\n")) die_nomem(); + } else + if (!stralloc_catb(&line,psubt->sub + HASHLEN + 1, + psubt->sublen - HASHLEN - 1)) die_nomem(); + } else + if (!stralloc_copys(&line,"")) die_nomem(); + if (!stralloc_cats(&line,"\t")) die_nomem(); + if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,msg))) die_nomem(); + if (!stralloc_cats(&line,TXT_BY)) die_nomem(); + if (pmsgt->authnum) { + cp = authtable[pmsgt->authnum - 1].auth; + len = authtable[pmsgt->authnum - 1].authlen; + if (len > HASHLEN) { + if (!stralloc_catb(&line,cp + HASHLEN + 1, + len - HASHLEN - 1)) die_nomem(); + } else + if (!stralloc_catb(&line,cp,len)) die_nomem(); + } else + if (!stralloc_cats(&line,"\n")) die_nomem(); + code_qput(line.s,line.len); + } + pmsgt++; + } + psubt++; + } + } + postsub(factype,format); + + psubt = subtable; + while (psubt->sub) { + pmsgt = msgtable + psubt->firstmsg - from; + subnum = (unsigned long) (psubt - subtable +1); + for (msg=psubt->firstmsg; msg<=to; msg++) { + if (pmsgt->subnum == subnum) + msgout(msg,format); + pmsgt++; + } + psubt++; + } + postmsg(format); + idx_destroythread(msgtable,subtable,authtable); +} + +void doheaders() +{ + int flaggoodfield,match; + + if (act == AC_DIGEST) + copy(&qq,"headeradd",'H',FATAL); + + qmail_puts(&qq,"Mailing-List: "); + qmail_put(&qq,mailinglist.s,mailinglist.len); + if (getconf_line(&line,"listid",0,FATAL,dir)) { + qmail_puts(&qq,"\nList-ID: "); + qmail_put(&qq,line.s,line.len); + } + qmail_puts(&qq,"\nDate: "); + qmail_put(&qq,date,date822fmt(date,&dt)); + qmail_puts(&qq,"Message-ID: <"); + if (!stralloc_copyb(&line,strnum,fmt_ulong(strnum,(unsigned long) when))) + die_nomem(); + if (!stralloc_append(&line,".")) die_nomem(); + if (!stralloc_catb(&line,strnum, + fmt_ulong(strnum,(unsigned long) getpid()))) die_nomem(); + if (!stralloc_cats(&line,".ezmlm@")) die_nomem(); + if (!stralloc_cat(&line,&outhost)) die_nomem(); + if (!stralloc_0(&line)) die_nomem(); + qmail_puts(&qq,line.s); + /* "unique" MIME boundary as hash of messageid */ + makehash(line.s,line.len,boundary); + qmail_puts(&qq,">\nFrom: "); + if (!quote("ed,&outlocal)) die_nomem(); + qmail_put(&qq,quoted.s,quoted.len); + qmail_puts(&qq,"-help@"); + qmail_put(&qq,outhost.s,outhost.len); + qmail_puts(&qq,"\n"); + if (!stralloc_copys(&mydtline,"Delivered-To: responder for ")) 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(); + + qmail_put(&qq,mydtline.s,mydtline.len); + + flaggoodfield = 0; + if (act != AC_DIGEST) + for (;;) { + if (getln(&ssin,&line,&match,'\n') == -1) + strerr_die2sys(111,FATAL,ERR_READ_INPUT); + if (!match) break; + if (line.len == 1) break; + if ((line.s[0] != ' ') && (line.s[0] != '\t')) { + flaggoodfield = 0; + if (case_startb(line.s,line.len,"mailing-list:")) + if (flageditor) /* we may be running from a sublist */ + flaggoodfield = 0; + else + strerr_die2x(100,FATAL,ERR_MAILING_LIST); + if (line.len == mydtline.len) + if (byte_equal(line.s,line.len,mydtline.s)) + strerr_die2x(100,FATAL,ERR_LOOPING); + if (case_startb(line.s,line.len,"delivered-to:")) + flaggoodfield = 1; + if (case_startb(line.s,line.len,"received:")) + flaggoodfield = 1; + } + if (flaggoodfield) + qmail_put(&qq,line.s,line.len); + } +} + + +void main(argc,argv) +int argc; +char **argv; +{ + char *def; + char *local; + char *action = ""; + char *psz; + char *err; + int fd; + unsigned int i,j; + int flagremote; + int flagqmqp = 0; + int match; + int goodexit = 0; /* exit code for normal exit */ + /* in manager this will be set to 0 */ + unsigned long from,u,to,issue,prevmax,mno; + unsigned long chunk; + unsigned long subs; + unsigned int pos,pos1; + unsigned int len; + int opt; + char outformat = DEFAULT_FORMAT; /* default output format */ + msgentry *msgtable; + subentry *subtable; + authentry *authtable; + dateentry *datetable; + + (void) umask(022); + sig_pipeignore(); + when = now(); + datetime_tai(&dt,when); + + while ((opt = getopt(argc,argv,"bBcCf:pPsSt:vV")) != opteof) + switch (opt) { + case 'b': flagbottom = 1; break; /* add text/bottom (default) */ + case 'B': flagbottom = 0; break; /* suppress text/bottom */ + case 'c': flagdo = 1; break; /* do commands */ + case 'C': flagdo = 0; break; /* ignore commands X dig */ + case 'f': if (FORMATS[str_chr(FORMATS,optarg[0])]) + outformat = optarg[0]; + break; + case 'p': flagpublic = 1; break; /* always public */ + case 'P': flagpublic = 0; break; /* never public = only mods do cmd*/ + /* def = 2: respect DIR/public */ + case 's': flagsub = 1; break; /* only subs have archive access */ + case 'S': flagsub = 0; break; /* everyone has archive access */ + case 'v': + case 'V': strerr_die2x(0,"ezmlm-get version: ",EZIDX_VERSION); + default: + die_usage(); + } + + dir = argv[optind++]; + if (!dir) die_usage(); + if (chdir(dir) == -1) + strerr_die4x(111,FATAL,ERR_SWITCH,dir,": "); + + digestcode = argv[optind]; /* code to activate digest (-digest-code)*/ + /* ignore any extra args */ + + getconf_line(&outlocal,"outlocal",1,FATAL,dir); + if (!stralloc_copy(&subject,&outlocal)) die_nomem(); /* for subjects */ + if (!stralloc_copy(&listname,&outlocal)) die_nomem(); /* for content disp */ + + local = env_get("LOCAL"); + def = env_get("DEFAULT"); + sender = env_get("SENDER"); + if (local && *local) { /* in editor local = inlocal */ + if (!sender) strerr_die2x(100,FATAL,ERR_NOSENDER); + if (!*sender) + strerr_die2x(100,FATAL,ERR_BOUNCE); + if (str_equal(sender,"#@[]")) + strerr_die2x(100,FATAL,ERR_BOUNCE); + if (!sender[str_chr(sender,'@')]) + strerr_die2x(100,FATAL,ERR_ANONYMOUS); + if (def) { /* qmail>=1.02 support only */ + if (*def) { + action = def; + goodexit = 99; + } else + _exit(0); /* list-@host should do -help from manager */ + } else { /* editor */ + act = AC_DIGEST; /* on list-@host ! */ + flageditor = 1; /* to avoid Mailing-list error on sublists */ + /* when running out of dir/editor. */ + } + if (case_starts(action,"dig")) { + action += 3; + if (action[0] == '-' || action [0] == '.') { + action++; + if (!digestcode) + strerr_die2x(100,FATAL,ERR_BAD_DIGCODE); + len = str_len(digestcode); + if (len <= str_len(action) && case_startb(action,len,digestcode)) { + if (FORMATS[str_chr(FORMATS,*(action+len))]) + outformat = *(action+len); + act = AC_DIGEST; + } else + strerr_die2x(100,FATAL,ERR_BAD_DIGCODE); + } + } + } else /* Command line operation */ + act = AC_DIGEST; + + /* Things we deal with. If anything else just die with success! */ + /* At the moment this is -index, -thread, and -get. */ + /* If flagdo = 0 we only service -dig commands. This is to support*/ + /* "secret" lists that are still archived and digested. -c on */ + /* cmd line. */ + + if (act == AC_NONE) { + if (case_equals(action,ACTION_DIGEST)) { + act = AC_GET; /* list-digest@ => msg since last digest */ + action = ACTION_GET; + } else if (case_starts(action,ACTION_GET) || case_starts(action,ALT_GET)) + act = AC_GET; + else if (case_starts(action,ACTION_INDEX) || case_starts(action,ALT_INDEX)) + act = AC_INDEX; + else if (case_starts(action,ACTION_THREAD) || + case_starts(action,ALT_THREAD)) + act = AC_THREAD; + } + if (act == AC_NONE) /* not for us. Pass the buck. */ + _exit(0); + if (act != AC_INDEX) { /* need to do header processing */ + if(!getconf(&digheaders,"digheaders",0,FATAL,dir)) { + if(!stralloc_copys(&digheaders,digsz)) die_nomem(); + if (!stralloc_0(&digheaders)) die_nomem(); + psz = digheaders.s; + while (*psz) { + if (*psz == '\\') *psz = '\0'; + ++psz; + } + } + if (!constmap_init(&digheadersmap,digheaders.s,digheaders.len,0)) + die_nomem(); + } + if (act != AC_DIGEST) { + if (!flagdo) /* only do digests */ + strerr_die2x(100,FATAL,ERR_NOCMD); + if (flagpublic == 2) + flagpublic = getconf_line(&line,"public",0,FATAL,dir); + if (!flagpublic) { + /* This all to take care of non-public lists. They should*/ + /* still do digests, but do other things only for */ + /* moderators that have remote access. Since this is rare*/ + /* efforts have been made to keep everything that's not */ + /* needed elsewhere in here. */ + getconf_line(&moddir,"modsub",0,FATAL,dir); + flagremote = getconf_line(&line,"remote",0,FATAL,dir); + if (!flagremote) + strerr_die2x(100,FATAL,ERR_NOT_PUBLIC); + if (!moddir.len || moddir.s[0] != '/') { + if (line.len && line.s[0] == '/') { + if (!stralloc_copy(&moddir,&line)) die_nomem(); + } else { + if (!stralloc_copys(&moddir,dir)) die_nomem(); + if (!stralloc_cats(&moddir,"/mod")) die_nomem(); + } + } + if (!stralloc_0(&moddir)) die_nomem(); + pmod = issub(moddir.s,sender,(char *) 0,FATAL); + if (!pmod) /* sender = moderator? */ + strerr_die2x(100,FATAL,ERR_NOT_PUBLIC); + else { + if (!stralloc_copys(&mod,pmod)) die_nomem(); + if (!stralloc_0(&mod)) die_nomem(); + pmod = mod.s; /* send to address in list not matching bait */ + } + } + } + + flagindexed = getconf_line(&line,"indexed",0,FATAL,dir); + flagarchived = getconf_line(&line,"archived",0,FATAL,dir); + + 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(); + getconf_line(&mailinglist,"mailinglist",1,FATAL,dir); + getconf_line(&outhost,"outhost",1,FATAL,dir); + set_cpouthost(&outhost); + + if (!stralloc_copys(&ddir,dir)) die_nomem(); + if (!stralloc_cats(&ddir,"/digest")) die_nomem(); + if (!stralloc_0(&ddir)) die_nomem(); + if (act == AC_DIGEST) { + workdir = ddir.s; + if (!stralloc_cats(&outlocal,"-digest")) die_nomem(); + if (getconf_line(&line,"chunk",0,FATAL,dir)) { + if (!stralloc_0(&line)) die_nomem(); + (void) scan_ulong(line.s,&chunk); /* same chunk as main list */ + if (chunk == 0) /* limit range to 1-53 */ + chunk = 1L; + else if (chunk > 52) + chunk = 52L; + } else { + chunk = 0L; /* maybe direct qmqp? */ + if (!stralloc_copys(&line,QMQPSERVERS)) die_nomem(); + if (!stralloc_cats(&line,"/0")) die_nomem(); + if (!stralloc_0(&line)) die_nomem(); + flagqmqp = getconf(&qmqpservers,line.s,0,FATAL,dir); + } + } else + workdir = dir; + + if (!stralloc_copys(&edir,dir)) die_nomem(); /* not needed for -dig, but */ + if (!stralloc_cats(&edir,"/allow")) die_nomem(); /* be safe */ + if (!stralloc_0(&edir)) die_nomem(); + set_cpoutlocal(&outlocal); /* needed for copy */ + + if (flagqmqp) { + if (qmail_open(&qq,&qmqpservers) == -1) /* open qmail */ + strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE); + } else if (qmail_open(&qq,(stralloc *) 0) == -1) /* open qmail */ + strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE); + + set_cpnum(""); /* default for <#n#> replacement */ + + if (act == AC_DIGEST) { +/* -dig{.|-}'digestcode'[f] returns an rfc1153 digest */ +/* of messages from the archive. Messages */ +/* dignum+1 through the last message received by the list are processed and */ +/* dignum is updated to the last message processed. digissue is advanced. */ + + if (!flagarchived) + strerr_die2x(100,FATAL,ERR_NOT_ARCHIVED); + + get_num(); /* max = last successful message */ + to = max; + lockup(); /* another digest could corrupt dignum */ + /* but will be saved only if flagdigrange==0 */ + if(getconf_line(&num,"dignum",0,FATAL,dir)) { + if(!stralloc_0(&num)) die_nomem(); + pos = scan_ulong(num.s,&prevmax); + if (num.s[pos] == ':') pos++; + pos += 1 + scan_ulong(num.s+pos,&cumsize); /* last cumsize */ + if (num.s[pos] == ':') pos++; + scan_ulong(num.s+pos,&digwhen); /* last reg dig */ + } else { + prevmax = 0L; + cumsize = 0L; + digwhen = 0L; + } + mno = prevmax + 1L; + if(!max || mno > max) /* if a digest-list is "sending" the request, */ + /* don't make noise: errors go to postmaster!*/ + strerr_die2x(goodexit,FATAL,ERR_EMPTY_DIGEST); + szmsgnum[fmt_ulong(szmsgnum,mno)] = '\0'; + set_cpnum(szmsgnum); /* for copy */ + /* prepare subject to get entropy for tagmsg*/ + if (!stralloc_cats(&subject," Digest ")) die_nomem(); + if (!stralloc_catb(&subject,date,date822fmt(date,&dt)-1)) + die_nomem(); /* skip trailing in date '\n' */ + if (!stralloc_cats(&subject," Issue ")) die_nomem(); + if (getconf_line(&num,"digissue",0,FATAL,dir)) { + if(!stralloc_0(&num)) die_nomem(); + scan_ulong(num.s,&issue); + issue++; + } else { + issue = 1; + } + if (!stralloc_catb(&subject,strnum,fmt_ulong(strnum,issue))) + die_nomem(); + /* use the subject as entropy */ + if (!stralloc_copy(&line,&subject)) die_nomem(); + if (!stralloc_0(&line)) die_nomem(); + + if (!stralloc_ready(&seed,HASHLEN+1)) die_nomem(); + seed.len = HASHLEN + 1; + seed.s[HASHLEN] = '\0'; + makehash(line.s,line.len,seed.s); + if (chunk) { /* only if slaves are used */ + qmail_puts(&qq,"Ezauth: "); + qmail_put(&qq,seed.s,HASHLEN); + qmail_puts(&qq,"\n"); + } + + doheaders(); + qmail_puts(&qq,"To: "); + if (!quote("ed,&listname)) die_nomem(); + qmail_put(&qq,quoted.s,quoted.len); + qmail_puts(&qq,"@"); + qmail_put(&qq,outhost.s,outhost.len); + qmail_puts(&qq,"\n"); + if (flagindexed && (outformat != NATIVE)) + idx_mkthreads(&msgtable,&subtable,&authtable,&datetable, + mno,to,max,flaglocked,FATAL); + else + idx_mklist(&msgtable,&subtable,&authtable,mno,to,FATAL); + digest(msgtable,subtable,authtable,mno,to,&subject,AC_DIGEST,outformat); + + write_ulong(issue,0L,0L,"digissue","digissuen"); + write_ulong(max,cumsizen, (unsigned long) when,"dignum","dignumn"); + } + + else if (act == AC_GET) { + +/* -get[-|\.][[num].num2] copies archive num-num2. num & num2 are adjusted */ +/* to be > 0 and <= last message, to num2 >= num and to num2-num <= MAXGET. */ + + if (!flagarchived) + strerr_die2x(100,FATAL,ERR_NOT_ARCHIVED); + zapnonsub(ACTION_GET); /* restrict to subs if requested */ + tosender(); + /* for rfc1153 */ + if (!stralloc_cats(&subject," Digest of: ")) die_nomem(); + if (!stralloc_cats(&subject,action)) die_nomem(); + + to = 0; + pos = str_len(ACTION_GET); + if (!case_starts(action,ACTION_GET)) + pos = str_len(ALT_GET); + if (FORMATS[str_chr(FORMATS,action[pos])]) { + outformat = action[pos]; + ++pos; + } + /* optional - or . after '-get' */ + if (action[pos] == '-' || action[pos] == '.') pos++; + get_num(); /* max = last successful message */ + /* accept any separator. It may be */ + /* the terminal '\n', but then */ + /* scan will = 0 on the \0 so should*/ + /* be safe */ + if (!max) + strerr_die2x(100,FATAL,ERR_EMPTY_LIST); + szmsgnum[fmt_ulong(szmsgnum,max)] = '\0'; + set_cpnum(szmsgnum); /* for copy this is the latest message arch'd*/ + doheaders(); + if(action[pos += scan_ulong(action + pos,&u)]) + scan_ulong(action + pos + 1, &to); + if (u == 0 && to == 0) { /* default: messages since last */ + /* digest, or last MAXGET if too many */ + to= max; + u = dignum(); + if (u == 0) { /* no digest => last up to HISTGET msgs */ + to = max; + if (max > HISTGET) u = max - HISTGET; else u = 1; + } + if (to - u >= MAXGET) u = to - MAXGET + 1; /* max MAXGET */ + } else if (u > max) { + if (to) { /* -get.999999_x returns 30 and msg since last*/ + to = max; /* digest 30*/ + u = dignum(); + if (u > HISTGET) u -= HISTGET; else u = 1; + if (to - u >= MAXGET) u = to - MAXGET + 1; + } else + u = max; + } + if (u == 0) u = 1; /* -get.5 => 1-5 */ + if (to < u) to = u; /* -get23_2 => 23 */ + if (to >= u + MAXGET) to = u + MAXGET - 1; + /* no more than MAXGET at a time */ + if (to > max) to = max; + if (flagindexed && (outformat != NATIVE)) /* fake out threading */ + idx_mkthreads(&msgtable,&subtable,&authtable,&datetable, + u,to,max,0,FATAL); + else + idx_mklist(&msgtable,&subtable,&authtable,u,to,FATAL); + digest(msgtable,subtable,authtable,u,to,&subject,AC_GET,outformat); + } + + else if (act == AC_INDEX) { + +/* -index[f][#|-|\.][[num][.num2] Show subject index for messages num-num2 in*/ +/* sets of 100. */ +/* Default last 2 sets. num and num2 are made reasonable as for get. num2 is */ +/* limited to num+MAXINDEX to limit the amount of data sent. */ + + if (!flagindexed) + strerr_die2x(100,FATAL,ERR_NOT_INDEXED); + if (flagsub) + zapnonsub(ACTION_INDEX); /* restrict to subs if requested */ + to = 0; + pos = str_len(ACTION_INDEX); + if (!case_starts(action,ACTION_INDEX)) + pos = str_len(ALT_INDEX); + if (FORMATS[str_chr(FORMATS,action[pos])]) { + outformat = action[pos]; /* ignored, but be nice ... */ + ++pos; + } + get_num(); /* max = last successful message */ + if (!max) + strerr_die2x(100,FATAL,ERR_EMPTY_LIST); + szmsgnum[fmt_ulong(szmsgnum,max)] = '\0'; + set_cpnum(szmsgnum); /* for copy this is the latest message arch'd*/ + + doheaders(); + tosender(); + if (!stralloc_cats(&subject," Result of: ")) die_nomem(); + if (!stralloc_cats(&subject,action)) die_nomem(); + presub(1,1,&subject,AC_INDEX,outformat); + + if (action[pos] == '-' || action[pos] == '.') pos++; + if(action[pos += scan_ulong(action + pos,&u)]) + scan_ulong(action + pos + 1, &to); + + if (u == 0 && to == 0) { to = max; u = max - 100; } + if (u <= 0) u = 1; + if (u > max) u = max; + if (to < u) to = u; + if (to > u + MAXINDEX) to = u+MAXINDEX; /* max MAXINDEX index files */ + if (to > max) to = max; + u /= 100; + to /= 100; + while (u <= to) { + if (!stralloc_copys(&fn,"archive/")) die_nomem(); + if (!stralloc_catb(&fn,strnum,fmt_ulong(strnum,u))) die_nomem(); + if (!stralloc_cats(&fn,"/index")) die_nomem(); + if (!stralloc_0(&fn)) die_nomem(); + + if (u == max/100) /* lock if last index file in archive */ + lockup(); + + fd = open_read(fn.s); + if (fd == -1) + if (errno != error_noent) + strerr_die4sys(111,FATAL,ERR_OPEN,fn.s,": "); + else + code_qput(TXT_NOINDEX,str_len(TXT_NOINDEX)); + else { + substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf)); + for (;;) { + if (getln(&sstext,&line,&match,'\n') == -1) + strerr_die4sys(111,FATAL,ERR_READ,fn.s,": "); + if (match) { + if (line.s[0] != '\t') { /* subject line */ + pos = byte_chr(line.s,line.len,' '); + if (pos && pos != line.len && line.s[pos - 1] == ':') + pos1 = pos + HASHLEN + 1; /* after hash */ + if (pos1 >= line.len) { /* bad! */ + pos = 0; + pos1 = 0; /* output as is */ + } + if (!stralloc_copyb(&line2,line.s,pos)) die_nomem(); + if (!stralloc_catb(&line2,line.s+pos1,line.len-pos1)) die_nomem(); + } else { + pos = byte_chr(line.s,line.len,';'); + if (pos + HASHLEN + 1 < line.len && pos > 15 && + line.s[pos + 1] != ' ') { + if (!stralloc_copyb(&line2,line.s,pos - 15)) die_nomem(); + pos++; + if (!stralloc_catb(&line2,line.s + pos + HASHLEN, + line.len - pos - HASHLEN)) die_nomem(); + } else /* old format - no author hash */ + if (!stralloc_copyb(&line2,line.s,line.len)) die_nomem(); + } + code_qput(line2.s,line2.len); + } else + break; + } + close(fd); + } + + if (u == max/100) /* unlock if last index in archive file */ + unlock(); + + u++; + } + normal_bottom(outformat); + postmsg(outformat); + } + + else if (act == AC_THREAD) { + +/* -thread[f][-|.]num returns messages with subject matching message */ +/* 'num' in the subject index. If 'num' is not in[1..last_message] an error */ +/* message is returned. */ + + if (!flagarchived) + strerr_die2x(100,FATAL,ERR_NOT_ARCHIVED); + if (!flagindexed) + strerr_die2x(100,FATAL,ERR_NOT_INDEXED); + + zapnonsub(ACTION_THREAD); /* restrict to subs if requested*/ + + get_num(); /* max = last successful message */ + if (!max) + strerr_die2x(100,FATAL,ERR_EMPTY_LIST); + szmsgnum[fmt_ulong(szmsgnum,max)] = '\0'; + set_cpnum(szmsgnum); /* for copy this is the latest message arch'd*/ + + doheaders(); + tosender(); + /* for rfc1153 */ + if (!stralloc_cats(&subject," Digest of: ")) die_nomem(); + if (!stralloc_cats(&subject,action)) die_nomem(); + + to = 0; + pos = str_len(ACTION_THREAD); + if (!case_starts(action,ACTION_THREAD)) + pos = str_len(ALT_THREAD); + if (FORMATS[str_chr(FORMATS,action[pos])]) { + outformat = action[pos]; + ++pos; + } + if (action[pos] == '-' || action[pos] == '.') pos++; + if(action[pos += scan_ulong(action + pos,&u)]) + scan_ulong(action + pos + 1, &to); + + if(u == 0 || u > max) { + if (!stralloc_cats(&subject,"\n\n")) die_nomem(); + qmail_puts(&qq,"Subject: "); + qmail_put(&qq,subject.s,subject.len); + copy(&qq,"text/get-bad",flagcd,FATAL); + } else { /* limit range to at most u-THREAD_BEFORE to u+THREAD_AFTER */ + if (u > THREAD_BEFORE) + from = u-THREAD_BEFORE; + else + from = 1L; + if (u + THREAD_AFTER > max) { + idx_mkthread(&msgtable,&subtable,&authtable,from,max,u,max,0,FATAL); + digest(msgtable,subtable,authtable,from,max,&subject, + AC_THREAD,outformat); + } else { + idx_mkthread(&msgtable,&subtable,&authtable, + from,u+THREAD_AFTER,u,max,0,FATAL); + digest(msgtable,subtable,authtable,from,u+THREAD_AFTER, + &subject,AC_THREAD,outformat); + } + } + } + + else + /* This happens if the initial check at the beginning of 'main' */ + /* matches something that isn't matched here. Conversely, just */ + /* adding an action here is not enough - it has to be added to the */ + /* initial check as well. */ + + strerr_die2x(100,FATAL, + "Program error: I'm supposed to deal with this but I didn't"); + + if (!stralloc_copy(&line,&outlocal)) die_nomem(); + if (act == AC_DIGEST) { + if (chunk) { + if (!stralloc_cats(&line,"-return-g-")) die_nomem(); + } else + if (!stralloc_cats(&line,"-return-")) die_nomem(); + strnum[fmt_ulong(strnum,mno)] = '\0'; + if (!stralloc_cats(&line,strnum)) die_nomem(); + if (!stralloc_cats(&line,"-@")) die_nomem(); + + if (!stralloc_cat(&line,&outhost)) die_nomem(); + if (!stralloc_cats(&line,"-@[]")) die_nomem(); + } else { + if (!stralloc_cats(&line,"-return-@")) die_nomem(); + if (!stralloc_cat(&line,&outhost)) die_nomem(); + } + if (!stralloc_0(&line)) die_nomem(); + + qmail_from(&qq,line.s); + if (act == AC_DIGEST) { /* Do recipients */ + tagmsg(workdir,mno,seed.s,"d",hashout,qq.msgbytes,chunk,FATAL); + if (chunk) { + if (!stralloc_copys(&line,"T")) die_nomem(); + if (!stralloc_cat(&line,&outlocal)) die_nomem(); + if (!stralloc_cats(&line,"-s-d-")) die_nomem(); + if (!stralloc_catb(&line,hashout,COOKIE)) die_nomem(); + if (!stralloc_cats(&line,"-")) die_nomem(); + if (!stralloc_cats(&line,strnum)) die_nomem(); + if (!stralloc_cats(&line,"-")) die_nomem(); + if (!stralloc_copys(&line2,"@")) die_nomem(); + if (!stralloc_cat(&line2,&outhost)) die_nomem(); + if (!stralloc_0(&line2)) die_nomem(); + j = 0; + for (i = 0; i <= 52; i += chunk) { /* To slaves */ + qmail_put(&qq,line.s,line.len); + schar[0] = '0' + i / 10; + schar[1] = '0' + (i % 10); + qmail_put(&qq,schar,3); + j += (chunk - 1); + if (j > 52) j = 52; + schar[0] = '0' + j / 10; + schar[1] = '0' + (j % 10); + qmail_put(&qq,schar,2); + qmail_put(&qq,line2.s,line2.len); + } + } else + subs = putsubs(workdir,0L,52L,&subto,1,FATAL); + } else { /* if local is set, sender is checked */ + if (pmod) + qmail_to(&qq,pmod); + else + qmail_to(&qq,sender); + } + + if (*(err = qmail_close(&qq)) == '\0') { /* Done. Skip rest. */ + if (act == AC_DIGEST) { + if (chunk) + (void) logmsg(workdir,mno,0L,0L,2); + else + (void) logmsg(workdir,mno,0L,subs,4); + } + closesql(); /* close db connection */ + unlock(); /* NOP if nothing locked */ + strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0; + strerr_die2x(goodexit,"ezmlm-get: info: qp ",strnum); + } else { /* failed. Reset last msg & issue for digest */ + if(act == AC_DIGEST) { + issue--; + write_ulong(issue,0L,0L,"digissue","digissuen"); + write_ulong(prevmax,cumsize,(unsigned long) digwhen,"dignum","dignumn"); + } + unlock(); /* NOP if nothing locked */ + strerr_die3x(111,FATAL,ERR_TMP_QMAIL_QUEUE,err + 1); + } +}