X-Git-Url: https://git.distorted.org.uk/~mdw/ezmlm/blobdiff_plain/5b62e993b0af39700031c2875d7f6654e6a02850..f8beb284087c279acfb30506f5bb32baa4949b44:/ezmlm-clean.c diff --git a/ezmlm-clean.c b/ezmlm-clean.c new file mode 100644 index 0000000..ce8c04f --- /dev/null +++ b/ezmlm-clean.c @@ -0,0 +1,355 @@ +/*$Id: ezmlm-clean.c,v 1.30 1999/05/12 22:15:26 lindberg Exp $*/ +/*$Name: ezmlm-idx-040 $*/ +#include +#include +#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 "direntry.h" +#include "cookie.h" +#include "sgetopt.h" +#include "fmt.h" +#include "errtxt.h" +#include "copy.h" +#include "idx.h" +#include "mime.h" + +int flagmime = MOD_MIME; /* default is message as attachment */ +int flagreturn = 1; /* default return timed-out messages */ +char flagcd = '\0'; /* default: no transferencoding */ +stralloc fnmsg = {0}; + +/* When ezmlm-clean is run, messages and message stubs in pending/ */ +/* rejected/accepted are erased if they are older than delay hours. */ +/* Timeouts in h for messages. If modtime has a number, it is made to be*/ +/* in the range DELAY_MIN..DELAY_MAX. If the number is 0 or there is no */ +/* number, DELAY_DEFAULT is used. Messages that are read-only are */ +/* ignored. Messages in 'pending' that have the execute bit set result */ +/* in an informative reply to the poster. Any defects in the message */ +/* format, inability to open the file, etc, result in a maillog entry */ +/* whereafter the message is erased. */ + +/* The defines are in "idx.h" */ + +#define FATAL "ezmlm-clean: fatal: " + +void die_read() +{ + strerr_die4x(111,FATAL,ERR_READ,fnmsg.s,": "); +} + +void die_usage() +{ + strerr_die1x(100,"ezmlm-clean: usage: ezmlm-clean [-mMrRvV] dir"); +} + +void die_nomem() { strerr_die2x(111,FATAL,ERR_NOMEM); } + +datetime_sec when; +unsigned int older; +struct datetime dt; + +char textbuf[1024]; +substdio sstext; + +struct qmail qq; +int qqwrite(fd,buf,len) int fd; char *buf; unsigned int len; +{ + qmail_put(&qq,buf,len); + return len; +} +char qqbuf[1]; +substdio ssqq = SUBSTDIO_FDBUF(qqwrite,-1,qqbuf,sizeof(qqbuf)); + +char *dir; +char strnum[FMT_ULONG]; +char date[DATE822FMT]; +char boundary[COOKIE]; +datetime_sec hashdate; + +stralloc outhost = {0}; +stralloc outlocal = {0}; +stralloc mailinglist = {0}; +stralloc listid = {0}; +stralloc quoted = {0}; +stralloc line = {0}; +stralloc modtime = {0}; +stralloc to = {0}; +stralloc charset = {0}; + +int flagconf; +int fd; +int match; +unsigned long msgnum = 0; + /* counter to make message-id unique, since we may */ + /* send out several msgs. This is not bullet-proof.*/ + /* Duplication occurs if we do x>1 msg && another */ + /* ezmlm started within x seconds, and with the */ + /* same pid. Very unlikely. */ + +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 readconfigs() +/* gets outlocal, outhost, etc. This is done only if there are any timed-out*/ +/* messages found, that merit a reply to the author. */ +{ + + getconf_line(&mailinglist,"mailinglist",1,FATAL,dir); + getconf_line(&listid,"listid",0,FATAL,dir); + getconf_line(&outhost,"outhost",1,FATAL,dir); + getconf_line(&outlocal,"outlocal",1,FATAL,dir); + set_cpouthost(&outlocal); + set_cpoutlocal(&outlocal); +} + +void sendnotice(d) +char *d; +/* sends file pointed to by d to the address in the return-path of the */ +/* message. */ +{ + unsigned int x,y; + char *err; + + if (!flagconf) { + readconfigs(); + } + if (qmail_open(&qq, (stralloc *) 0) == -1) + strerr_die2x(111,FATAL,ERR_QMAIL_QUEUE); + + fd = open_read(d); + if (fd == -1) + strerr_die4sys(111,FATAL,ERR_OPEN,d,": "); + substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf)); + if (getln(&sstext,&line,&match,'\n') == -1) die_read(); + if (!match) die_read(); + if (!case_startb(line.s,line.len,"return-path:")) die_read(); + x = 12 + byte_chr(line.s + 12,line.len-12,'<'); + y = byte_rchr(line.s + x,line.len-x,'>'); + if (x != line.len && x+y != line.len) { + if (!stralloc_copyb(&to,line.s+x+1, y-1)) die_nomem(); + if (!stralloc_0(&to)) die_nomem(); + } else + die_read(); + qmail_puts(&qq,"Mailing-List: "); + qmail_put(&qq,mailinglist.s,mailinglist.len); + qmail_puts(&qq,"\nList-ID: "); + qmail_put(&qq,listid.s,listid.len); + qmail_puts(&qq,"\nDate: "); + datetime_tai(&dt,when); + qmail_put(&qq,date,date822fmt(date,&dt)); + qmail_puts(&qq,"Message-ID: <"); + if (!stralloc_copyb(&line,strnum,fmt_ulong(strnum, + (unsigned long) when + msgnum++))) 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 */ + cookie(boundary,"",0,"",line.s,""); + 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,"\nSubject: "); + qmail_puts(&qq,TXT_RETURNED_POST); + qmail_put(&qq,quoted.s,quoted.len); + qmail_puts(&qq,"@"); + qmail_put(&qq,outhost.s,outhost.len); + qmail_puts(&qq, "\nTo: "); + qmail_puts(&qq,to.s); + if (flagmime) { + 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(); + qmail_puts(&qq,"\nMIME-Version: 1.0\n"); + qmail_puts(&qq,"Content-Type: multipart/mixed;\n\tboundary="); + qmail_put(&qq,boundary,COOKIE); + qmail_puts(&qq,"\n\n--"); + qmail_put(&qq,boundary,COOKIE); + qmail_puts(&qq,"\nContent-Type: text/plain; charset="); + qmail_puts(&qq,charset.s); + transferenc(); + } else + qmail_puts(&qq,"\n\n"); + + copy(&qq,"text/top",flagcd,FATAL); + copy(&qq,"text/mod-timeout",flagcd,FATAL); + if (flagcd == 'B') { + encodeB("",0,&line,2,FATAL); + qmail_put(&qq,line.s,line.len); + } + + if (flagmime) { + qmail_puts(&qq,"\n--"); + qmail_put(&qq,boundary,COOKIE); + qmail_puts(&qq,"\nContent-Type: message/rfc822\n\n"); + } + + if (seek_begin(fd) == -1) + strerr_die4sys(111,FATAL,ERR_SEEK,d,": "); + + substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf)); + if (substdio_copy(&ssqq,&sstext) != 0) die_read(); + close (fd); + + if (flagmime) { + qmail_puts(&qq,"\n--"); + qmail_put(&qq,boundary,COOKIE); + qmail_puts(&qq,"--\n"); + } + + if (!stralloc_copy(&line,&outlocal)) die_nomem(); + 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); /* sender */ + qmail_to(&qq,to.s); + + if (*(err = qmail_close(&qq)) != '\0') + strerr_die3x(111,FATAL,ERR_TMP_QMAIL_QUEUE, err + 1); + + strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0; + strerr_warn2("ezmlm-clean: info: qp ",strnum,0); +} + +void dodir(dirname,reply) +char *dirname; int reply; +/* parses file names in directory 'dirname'. Files that are not owner */ +/* writable (w) are ignored. If the files are older (by name!) than */ +/* now-delay, action is taken: */ +/* If the owner x bit is not set, the file is erased. */ +/* If it is set and reply is not set, the file is erased. If both are */ +/* set, a notice about the timeout is sent to the poster. If this */ +/* fails due to a message-related error (format, etc) the file is */ +/* erased even though no notice is sent. For temporary errors (like */ +/* out-of-memory) the message is left intact for the next run. If the */ +/* notice is sent successfully, the file is erased. All this is to */ +/* do the best possible without risking a rerun of the .qmail file, */ +/* which could result in a redelivery of the action request and a */ +/* second (incorrect) reply to the moderator's request. */ + +/* NOTE: ALL non-hidden files in this dir are processed and merci- */ +/* lessly deleted. No checks for proper file name. E.g. 'HELLO' */ +/* => time 0 => will be deleted on the next ezmlm-clean run. */ +{ + DIR *moddir; + direntry *d; + unsigned long modtime; + struct stat st; + + moddir = opendir(dirname); + if (!moddir) + strerr_die6sys(0,FATAL,ERR_OPEN,dir,"/",dirname,": "); + while ((d = readdir(moddir))) { + if (d->d_name[0] == '.') continue; + scan_ulong(d->d_name,&modtime); + if (modtime < older) { + if (!stralloc_copys(&fnmsg,dirname)) die_nomem(); + if (!stralloc_cats(&fnmsg,d->d_name)) die_nomem(); + if (!stralloc_0(&fnmsg)) die_nomem(); + if((stat(fnmsg.s,&st) != -1) && (st.st_mode & 0200)) { + if(reply && (st.st_mode & 0100)) { + /* unlink unless there was a TEMPORARY */ + /* not message-related error notifying */ + /* poster and msg x bit set. Leave r/o*/ + /* messages alone. Non-x bit msg are */ + /* trash. Just unlink, don't notify */ + sendnotice(fnmsg.s); + unlink(fnmsg.s); + } else + unlink(fnmsg.s); + } + } + } + closedir(moddir); +} + + +void main(argc,argv) +int argc; +char **argv; +{ + int fdlock; + int delay; + int opt; + (void) umask(022); + sig_pipeignore(); + when = now(); + + while ((opt = getopt(argc,argv,"mMrRvV")) != opteof) + switch(opt) { + case 'm': flagmime = 1; break; + case 'M': flagmime = 0; break; + case 'r': flagreturn = 1; break; + case 'R': flagreturn = 0; break; + case 'v': + case 'V': strerr_die2x(0,"ezmlm-clean version: ", EZIDX_VERSION); + /* not reached */ + default: + die_usage(); + } + + dir = argv[optind]; + if (!dir) die_usage(); + + if (chdir(dir) == -1) + strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": "); + + getconf_line(&modtime,"modtime",0,FATAL,dir); + if (!stralloc_0(&modtime)) die_nomem(); + scan_ulong(modtime.s,&delay); + if (!delay) delay = DELAY_DEFAULT; + else if (delay < DELAY_MIN) delay = DELAY_MIN; + else if (delay > DELAY_MAX) delay = DELAY_MAX; + older = (unsigned long) when - 3600L * delay; /* delay is in hours */ + + fdlock = open_append("mod/lock"); + if (fdlock == -1) + strerr_die4sys(0,FATAL,ERR_OPEN,dir,"/mod/lock: "); + if (lock_ex(fdlock) == -1) + strerr_die4sys(0,FATAL,ERR_OBTAIN,dir,"/mod/lock: "); + + flagconf = 0; + dodir("mod/pending/",flagreturn); + dodir("mod/accepted/",0); + dodir("mod/rejected/",0); + _exit(0); +} +