--- /dev/null
+/*$Id: ezmlm-receipt.c,v 1.10 1999/02/05 04:57:44 lindberg Exp $*/
+/*$Name: ezmlm-idx-0324 $*/
+/* Handles receipts and bounces from sublists at the main list */
+/* Set up instead of ezmlm-return in DIR/bouncer of main list */
+
+#include <sys/types.h>
+#include "direntry.h"
+#include "stralloc.h"
+#include "str.h"
+#include "env.h"
+#include "slurp.h"
+#include "getconf.h"
+#include "strerr.h"
+#include "byte.h"
+#include "case.h"
+#include "quote.h"
+#include "getln.h"
+#include "substdio.h"
+#include "error.h"
+#include "readwrite.h"
+#include "fmt.h"
+#include "now.h"
+#include "seek.h"
+#include "idx.h"
+#include "errtxt.h"
+
+#define FATAL "ezmlm-receipt: fatal: "
+#define INFO "ezmlm-receipt: info: "
+
+void die_usage()
+{
+ strerr_die1x(100,"ezmlm-receipt: usage: ezmlm-receipt [-dD] dir");
+}
+
+void die_nomem() { strerr_die2x(111,FATAL,ERR_NOMEM); }
+
+void die_badaddr()
+{
+ strerr_die2x(100,FATAL,ERR_BAD_ADDRESS);
+}
+void die_trash()
+{
+ strerr_die2x(0,INFO,"trash address");
+}
+
+stralloc line = {0};
+stralloc quoted = {0};
+stralloc intro = {0};
+stralloc bounce = {0};
+stralloc header = {0};
+stralloc failure = {0};
+stralloc paragraph = {0};
+stralloc ddir = {0};
+stralloc outhost = {0};
+stralloc outlocal = {0};
+stralloc inlocal = {0};
+stralloc tagline = {0};
+stralloc listaddr = {0};
+stralloc fndate = {0};
+stralloc fndir = {0};
+stralloc fndatenew = {0};
+
+void die_datenew()
+{ strerr_die4sys(111,FATAL,ERR_WRITE,fndatenew.s,": "); }
+void die_msgin()
+{ strerr_die2sys(111,FATAL,ERR_READ_INPUT); }
+
+char strnum[FMT_ULONG];
+char inbuf[1024];
+substdio ssin;
+
+char outbuf[256]; /* small - rarely used */
+substdio ssout;
+
+unsigned long when;
+unsigned long addrno = 0L;
+
+char *sender;
+char *dir;
+char *workdir;
+void **psql = (void **) 0;
+stralloc listno = {0};
+
+
+void doit(addr,msgnum,when,bounce)
+/* Just stores address\0nsgnum\0 followed by bounce. File name is */
+/* dttt.ppp[.n], where 'ttt' is a time stamp, 'ppp' the pid, and 'n' the */
+/* number when there are more than 1 addresses in a pre-VERP bounce. In */
+/* this case, the first one is just dttt.ppp, the decond dttt.ppp.2, etc. */
+/* For a main list, bounces come from sublists. They are rare and serious. */
+char *addr;
+unsigned long msgnum;
+unsigned long when;
+stralloc *bounce;
+{
+ int fd;
+ unsigned int pos;
+ DIR *bouncedir;
+ direntry *d;
+ unsigned int no;
+
+ if (!stralloc_copys(&fndir,workdir)) die_nomem();
+ if (!stralloc_cats(&fndir,"/bounce")) die_nomem();
+ if (!stralloc_0(&fndir)) die_nomem();
+ bouncedir = opendir(fndir.s);
+ if (!bouncedir)
+ if (errno != error_noent)
+ strerr_die4sys(111,FATAL,ERR_OPEN,line.s,": ");
+ else
+ strerr_die3x(111,FATAL,fndir.s,ERR_NOEXIST);
+
+ no = MAX_MAIN_BOUNCES; /* no more than this many allowed */
+ while (no && (d = readdir(bouncedir))) {
+ if (str_equal(d->d_name,".")) continue;
+ if (str_equal(d->d_name,"..")) continue;
+ --no;
+ }
+ closedir(bouncedir);
+ if (!no) /* max no of bounces exceeded */
+ strerr_die2x(0,INFO,ERR_MAX_BOUNCE);
+ /* save bounce */
+ if (!stralloc_copys(&fndate,workdir)) die_nomem();
+ if (!stralloc_cats(&fndate,"/bounce/d")) die_nomem();
+ pos = fndate.len - 1;
+ if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,when))) die_nomem();
+ if (!stralloc_cats(&fndate,".")) die_nomem();
+ if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,(unsigned long) getpid())))
+ die_nomem();
+ if (addrno) { /* so that pre-VERP bounces make a d... file per address */
+ /* for the first one we use the std-style fname */
+ if (!stralloc_cats(&fndate,".")) die_nomem();
+ if (!stralloc_catb(&fndate,strnum,fmt_ulong(strnum,addrno))) die_nomem();
+ }
+ addrno++; /* get ready for next */
+ if (!stralloc_0(&fndate)) die_nomem();
+ if (!stralloc_copy(&fndatenew,&fndate)) die_nomem();
+ fndatenew.s[pos] = 'D';
+
+ fd = open_trunc(fndatenew.s);
+ if (fd == -1) die_datenew();
+ substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf));
+ if (substdio_puts(&ssout,addr) == -1) die_datenew();
+ if (substdio_put(&ssout,"",1) == -1) die_datenew();
+ if (substdio_put(&ssout,strnum,fmt_ulong(strnum,msgnum)) == -1)
+ die_datenew();
+ if (substdio_put(&ssout,"",1) == -1) die_datenew();
+
+ if (substdio_puts(&ssout,"Return-Path: <") == -1) die_datenew();
+ if (!quote2("ed,sender)) die_nomem();
+ if (substdio_put(&ssout,quoted.s,quoted.len) == -1) die_datenew();
+ if (substdio_puts(&ssout,">\n") == -1) die_datenew();
+ if (substdio_put(&ssout,bounce->s,bounce->len) == -1) die_datenew();
+ if (substdio_flush(&ssout) == -1) die_datenew();
+ if (fsync(fd) == -1) die_datenew();
+ if (close(fd) == -1) die_datenew(); /* NFS stupidity */
+ if (rename(fndatenew.s,fndate.s) == -1)
+ strerr_die6sys(111,FATAL,ERR_MOVE,fndatenew.s," to ",fndate.s,": ");
+}
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ char *local;
+ char *host;
+ char *action;
+ char *def;
+ int flagdig = 1;
+ int flaghaveintro;
+ int flaghaveheader;
+ int match;
+ unsigned long msgnum;
+ unsigned int i;
+ unsigned int len;
+ char *cp;
+
+ umask(022);
+ sig_pipeignore();
+
+ when = (unsigned long) now();
+
+ dir = argv[1];
+ if (!dir) die_usage();
+ if (*dir == '-') {
+ if (dir[1] == 'd') {
+ flagdig = 2;
+ } else if (dir[1] == 'D') {
+ flagdig = 0;
+ } else
+ die_usage();
+ dir = argv[2];
+ if (!dir) die_usage();
+ }
+ if (chdir(dir) == -1)
+ strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": ");
+
+ sender = env_get("SENDER");
+ def = env_get("DEFAULT");
+ local = env_get("LOCAL");
+
+ getconf_line(&outhost,"outhost",1,FATAL,dir);
+ getconf_line(&outlocal,"outlocal",1,FATAL,dir);
+ workdir = dir;
+ if (def) { /* qmail>=1.02 */
+ action = def; /* now see if -digest-return- */
+ if (flagdig == 1) {
+ flagdig = 0;
+ if (str_len(local) >= str_len(def) + 14)
+ if (str_start(local + str_len(local) - 14 - str_len(def),"digest-"))
+ flagdig = 2;
+ }
+ } else { /* older version of qmail */
+ getconf_line(&inlocal,"inlocal",1,FATAL,dir);
+ if (inlocal.len > str_len(local)) die_badaddr();
+ if (case_diffb(inlocal.s,inlocal.len,local)) die_badaddr();
+ action = local + inlocal.len;
+ if (flagdig == 1) {
+ flagdig = 0;
+ if (str_start(action,"-digest")) {
+ flagdig = 2;
+ action += 7;
+ }
+ }
+ if (!str_start(action,"-return-")) die_badaddr();
+ action += 8;
+ }
+ if (flagdig) {
+ if (!stralloc_copys(&ddir,dir)) die_nomem();
+ if (!stralloc_cats(&ddir,"/digest")) die_nomem();
+ if (!stralloc_0(&ddir)) die_nomem();
+ workdir = ddir.s;
+ if (!stralloc_cats(&outlocal,"-digest")) die_nomem();
+ }
+ if (!*action) die_trash();
+
+ substdio_fdbuf(&ssin,read,0,inbuf,sizeof(inbuf));
+
+ if (!case_diffs(action,"receipt")) {
+ host = sender + str_rchr(sender,'@');
+ if (*host)
+ *(host++) = '\0';
+ cp = sender;
+ /* check recipient in case it's a bounce*/
+ while (*(cp++)) { /* decode sender */
+ cp += str_chr(cp,'-');
+ if (case_starts(cp,"-return-")) {
+ if (!scan_ulong(cp + 8,&msgnum))
+ strerr_die2x(100,FATAL,"bad VERP format for receipt");
+ *cp = '\0';
+ if (!stralloc_copys(&listaddr,sender)) die_nomem();
+ if (!stralloc_append(&listaddr,"@")) die_nomem();
+ if (!stralloc_cats(&listaddr,host)) die_nomem();
+ if (!stralloc_0(&listaddr)) die_nomem();
+ break;
+ }
+ }
+ for(;;) { /* Get X-tag from hdr*/
+ if (getln(&ssin,&line,&match,'\n') == -1) die_msgin();
+ if (!match)
+ break;
+
+ if (line.len == 1) break;
+ if (case_startb(line.s,line.len,TXT_TAG)) {
+ len = str_len(TXT_TAG);
+ if (!stralloc_catb(&tagline,line.s +len,line.len - len -1)) die_nomem();
+ /* NOTE: tagline is dirty! We quote it for sql and rely on */
+ /* std log clean for maillog */
+ break;
+ }
+ }
+ /* feedback ok even if not sub. Will be filtered by subreceipt*/
+ /* For instance, main list feedback is ok, but !issub. */
+ subreceipt(workdir,msgnum,&tagline,listaddr.s,2,INFO,FATAL);
+ closesql();
+ _exit(0);
+ }
+ /* not receipt - maybe bounce */
+ /* no need to lock. dttt.pid can be assumed */
+ /* to be unique and if not would be over- */
+ /* written even with lock */
+ action += scan_ulong(action,&msgnum);
+ if (*action != '-') die_badaddr();
+ ++action;
+ /* scan bounce for tag. It'll be in the BODY! */
+ for (;;) {
+ if (getln(&ssin,&line,&match,'\n') == -1)
+ strerr_die2sys(111,FATAL,ERR_READ_INPUT);
+ if (!match) break;
+ if (case_startb(line.s,line.len,TXT_TAG)) {
+ len = str_len(TXT_TAG);
+ if (!stralloc_catb(&tagline,line.s +len,line.len - len -1)) die_nomem();
+ /* NOTE: tagline is dirty! We quote it for sql and rely on */
+ /* std log clean for maillog */
+ break;
+ }
+ }
+ if (seek_begin(0) == -1)
+ strerr_die2sys(111,FATAL,ERR_SEEK_INPUT);
+ substdio_fdbuf(&ssin,read,0,inbuf,sizeof(inbuf));
+
+ if (*action) { /* normal bounce */
+
+ if (slurpclose(0,&bounce,1024) == -1) die_msgin();
+ i = str_rchr(action,'=');
+ if (!stralloc_copyb(&listaddr,action,i)) die_nomem();
+ if (action[i]) {
+ if (!stralloc_cats(&listaddr,"@")) die_nomem();
+ if (!stralloc_cats(&listaddr,action + i + 1)) die_nomem();
+ }
+ if (!stralloc_0(&listaddr)) die_nomem();
+ /* don't check for sub, since issub() doesn't see sublists */
+ switch (subreceipt(workdir,msgnum,&tagline,listaddr.s,-1,INFO,FATAL)) {
+ case -1: strerr_die2x(0,INFO,ERR_COOKIE);
+ case -2: strerr_die2x(0,INFO,ERR_NOT_ACTIVE);
+ default: doit(listaddr.s,msgnum,when,&bounce);
+ }
+ closesql();
+ _exit(0);
+ } /* pre-VERP bounce, in QSBMF format */
+
+ flaghaveheader = 0;
+ flaghaveintro = 0;
+
+ for (;;) {
+ if (!stralloc_copys(¶graph,"")) die_nomem();
+ for (;;) {
+ if (getln(&ssin,&line,&match,'\n') == -1)
+ strerr_die2sys(111,FATAL,ERR_READ_INPUT);
+ if (!match) die_trash();
+ if (!stralloc_cat(¶graph,&line)) die_nomem();
+ if (line.len <= 1) break;
+ }
+
+ if (!flaghaveheader) {
+ if (!stralloc_copy(&header,¶graph)) die_nomem();
+ flaghaveheader = 1;
+ continue;
+ }
+
+ if (!flaghaveintro) {
+ if (paragraph.s[0] == '-' && paragraph.s[1] == '-')
+ continue; /* skip MIME boundary if it exists */
+ if (paragraph.len < 15) die_trash();
+ if (str_diffn(paragraph.s,"Hi. This is the",15)) die_trash();
+ if (!stralloc_copy(&intro,¶graph)) die_nomem();
+ flaghaveintro = 1;
+ continue;
+ }
+
+ if (paragraph.s[0] == '-')
+ break;
+
+ if (paragraph.s[0] == '<') { /* find address */
+ if (!stralloc_copy(&failure,¶graph)) die_nomem();
+
+ if (!stralloc_copy(&bounce,&header)) die_nomem();
+ if (!stralloc_cat(&bounce,&intro)) die_nomem();
+ if (!stralloc_cat(&bounce,&failure)) die_nomem();
+
+ i = byte_chr(failure.s,failure.len,'\n');
+ if (i < 3) die_trash();
+
+ if (!stralloc_copyb(&listaddr,failure.s + 1,i - 3)) die_nomem();
+ if (byte_chr(listaddr.s,listaddr.len,'\0') == listaddr.len) {
+ if (!stralloc_0(&listaddr)) die_nomem();
+ if (subreceipt(workdir,msgnum,&tagline,listaddr.s,-1,INFO,FATAL) == 0)
+ doit(listaddr.s,msgnum,when,&bounce);
+ }
+ }
+ }
+ closesql();
+ _exit(0);
+}
+