/*$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); } }