--- /dev/null
+/*$Id: ezmlm-request.c,v 1.34 1999/08/18 01:50:04 lindberg Exp $*/
+/*$Name: ezmlm-idx-040 $*/
+#include "stralloc.h"
+#include "subfd.h"
+#include "strerr.h"
+#include "error.h"
+#include "qmail.h"
+#include "env.h"
+#include "sig.h"
+#include "open.h"
+#include "getln.h"
+#include "case.h"
+#include "str.h"
+#include "datetime.h"
+#include "date822fmt.h"
+#include "now.h"
+#include "quote.h"
+#include "readwrite.h"
+#include "exit.h"
+#include "substdio.h"
+#include "getconf.h"
+#include "constmap.h"
+#include "fmt.h"
+#include "sgetopt.h"
+#include "byte.h"
+#include "seek.h"
+#include "errtxt.h"
+#include "copy.h"
+#include "idx.h"
+
+#define FATAL "ezmlm-request: fatal: "
+#define INFO "ezmlm-request: info: "
+
+void die_usage()
+{
+ strerr_die1x(100,"ezmlm-request: usage: ezmlm-request [-f lists.cfg] dir");
+}
+
+void die_nomem()
+{
+ strerr_die2x(111,FATAL,ERR_NOMEM);
+}
+
+void die_badaddr()
+{
+ strerr_die2x(100,FATAL,ERR_BAD_ADDRESS);
+}
+
+char strnum[FMT_ULONG];
+
+void *psql = (void *) 0;
+
+char *userlocal = (char *) 0;
+char *userhost = (char *) 0;
+char *listlocal = (char *) 0;
+char *listhost = (char *) 0;
+char *cfname = (char *) 0;
+char *command = "help";
+stralloc line = {0};
+stralloc qline = {0};
+stralloc usr = {0};
+stralloc lhost = {0};
+stralloc subject = {0};
+stralloc inlocal = {0};
+stralloc outlocal = {0};
+stralloc listname = {0};
+stralloc hostname = {0};
+stralloc outhost = {0};
+stralloc headerremove = {0};
+stralloc mailinglist = {0};
+stralloc cmds = {0};
+stralloc from = {0};
+stralloc to = {0};
+stralloc charset = {0};
+char *boundary = "zxcaeedrqcrtrvthbdty"; /* cheap "rnd" MIME boundary */
+int flagcd = '\0'; /* no encoding by default */
+
+struct constmap headerremovemap;
+struct constmap commandmap;
+int flaggotsub = 0; /* Found a subject */
+ /* cmdstring has all commands seperated by '\'. cmdxlate maps each */
+ /* command alias to the basic command, which is used to construct */
+ /* the command address (positive numbers) or handled by this */
+ /* program (negative numbers). Note: Any command not matched is */
+ /* used to make a command address, so ezmlm request can handle */
+ /* ("transmit") user-added commands. */
+const char *cmdstring =
+ "system\\help\\" /* 1,2 */
+ "subscribe\\unsubscribe\\index\\" /* 3,4,5 */
+ "info\\list\\query\\" /* 6,7,8 */
+ "sub\\unsub\\remove\\signoff\\" /* 9,10,11,12 */
+ "lists\\which\\" /* 13,14 */
+ "ind\\rev\\review\\recipients\\" /* 15,16,17,18 */
+ "who\\showdist\\" /* 19,20 */
+ "put\\set"; /* 21,22 */
+
+ /* map aliases. -> 0 not recognized. -> 1 recognized will be made */
+ /* help and arguments scrapped. < 0 handled locally. HELP without */
+ /* args also handled locally */
+ /* the last are not supported -> help */
+const int cmdxlate[] = { 0,1,2,3,4,5,6,7,8,3,4,4,4,-13,-14,5,7,7,7,7,7,
+ 1,1 };
+
+ /* If there are no arguments (listlocal = 0) then commands are mapped*/
+ /* through this. This way, help, list, query, ... can mean something */
+ /* here even though they have local funcions at the lists if used */
+ /* with arguments. (Made same lengh as cmdxlate in case of bugs.) */
+ /* Note: This is used ONLY for the global interface */
+const int noargsxlate[] = { 0,1,-2,3,4,5,-2,-13,-14,9,10,11,12,13,14,15,16,17,
+ 18,19,20,21,22 };
+
+ /* these need to be defined as the index of the corresponding */
+ /* commands. They are handled by ezmlm-request. NOTE: Help is >0! */
+#define EZREQ_LISTS 13
+#define EZREQ_WHICH 14
+#define EZREQ_HELP 2
+#define EZREQ_BAD 1
+
+substdio sstext;
+char textbuf[1024];
+datetime_sec when;
+struct datetime dt;
+char date[DATE822FMT];
+
+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,(int) sizeof(qqbuf));
+
+char inbuf[1024];
+substdio ssin = SUBSTDIO_FDBUF(read,0,inbuf,(int) sizeof(inbuf));
+substdio ssin2 = SUBSTDIO_FDBUF(read,0,inbuf,(int) sizeof(inbuf));
+
+substdio ssout;
+char outbuf[1];
+
+stralloc mydtline = {0};
+
+void transferenc()
+{
+ if (flagcd) {
+ qmail_puts(&qq,"\n--");
+ qmail_puts(&qq,boundary);
+ qmail_puts(&qq,"\nContent-Type: text/plain; charset=");
+ qmail_puts(&qq,charset.s);
+ qmail_puts(&qq,"\nContent-Transfer-Encoding: ");
+ if (flagcd == 'Q')
+ qmail_puts(&qq,"quoted-printable\n\n");
+ else
+ qmail_puts(&qq,"base64\n\n");
+ }
+}
+
+int 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);
+ }
+ return 0; /* always succeeds */
+}
+
+/* Checks the argument. Only us-ascii letters, numbers, ".+-_" are ok. */
+/* NOTE: For addresses this is more restrictive than rfc821/822. */
+void checkarg(s)
+char *s;
+{
+ register char *cp;
+ register char ch;
+ cp = s;
+ if (!cp) return; /* undef is ok */
+ while ((ch = *cp++)) {
+ if (ch >= 'a' && ch <= 'z')
+ continue; /* lc letters */
+ if (ch >= '0' && ch <='9') /* digits */
+ continue;
+ if (ch == '.' || ch == '-' || ch == '_' || ch == '+')
+ continue; /* ok chars */
+ if (ch >= 'A' && ch <= 'Z') continue; /* UC LETTERS */
+ strerr_die4x(100,ERR_NOT_CLEAN,": \"",s,"\"");
+ }
+ return;
+}
+
+/* parses line poited to by cp into sz:s as per: */
+/* 1. listlocal-command-userlocal=userhost@listhost */
+/* 2. command userlocal@userhost */
+/* 3. command userlocal@userhost listlocal@listhost */
+/* 4. command listlocal@listhost */
+/* 5. command listlocal[@listhost] userlocal@userhost */
+/* 6. which [userlocal@userhost] */
+/* The first 3 are valid only if !cfname, i.e. -request operation and */
+/* listlocal and listhost are always set to outlocal@outhost. Options */
+/* 4-5 are for the global address (cfname is set). Here listhost is */
+/* taken from the first list in *cfname matching listlocal, or set to */
+/* outhost, if not specified. If specified, it's accepted if it matches */
+/* a list in *cfname and silently set to outhost otherwise. Pointers to */
+/* unspecified parts are set to NULL in this routine to be dealt with */
+/* elsewhere. "Which" special argument order (6) is fixed elsewhere. */
+/* If listhost is not given, "@outhost" is added. Absence of 'userhost' */
+/* is accepted to allow commands that take arguments that are not */
+/* addresses (e.g. -get12-34). */
+
+void parseline(cp)
+char *cp;
+
+{
+ register char *cp1, *cp2;
+ char *cp3;
+
+ cp1 = cp;
+ while (*cp1) { /* make tabs into spaces */
+ if (*cp1 == '\t') *cp1 = ' ';
+ ++cp1;
+ }
+ /* NOTE: outlocal has '\0' added! */
+ if (outlocal.len < str_len(cp) && cp[outlocal.len -1] == '-' &&
+ case_starts(cp,outlocal.s)) { /* normal ezmlm cmd */
+ command = cp + outlocal.len; /* after the '-' */
+ listlocal = outlocal.s;
+ listhost = outhost.s;
+ cp1 = command;
+ while (*cp1 && *cp1 != '-') ++cp1; /* find next '-' */
+ if (*cp1) {
+ *cp1 = '\0';
+ userlocal = ++cp1; /* after '-' */
+ cp1 = cp1 + str_rchr(cp1,'@'); /* @ _or_ end */
+ *cp1 = '\0'; /* last '=' in userlocal */
+ cp1 = userlocal + str_rchr(userlocal,'=');
+ if (*cp1) { /* found '=' */
+ *cp1 = '\0'; /* zap */
+ userhost = cp1 + 1; /* char after '=' */
+ }
+ }
+ } else { /* '@' before ' ' means complete cmd */
+ if (str_chr(cp,'@') < str_chr(cp,' ')) /* addr where inlocal failed */
+ strerr_die2x(100,FATAL,ERR_REQ_LOCAL);
+ /* to match */
+ command = cp;
+ cp1 = cp + str_chr(cp,' ');
+ if (*cp1) {
+ *cp1++ = '\0';
+ while (*cp1 && *cp1 == ' ') ++cp1; /* skip spaces */
+ }
+ cp2 = 0;
+ if (*cp1) { /* argument */
+ cp2 = cp1 + str_chr(cp1,' ');
+ cp3 = cp2;
+ while (*cp2 && *cp2 == ' ') ++cp2; /* skip spaces */
+ *cp3 = '\0';
+
+ if (!*cp2)
+ cp2 = 0;
+ else {
+ cp3 = cp2 + str_chr(cp2,' ');
+ *cp3 = '\0';
+ }
+ } else
+ cp1 = 0;
+
+ if (!cfname && !cp2) { /* the single arg is user if we serve a */
+ cp2 = cp1; /* list. It's list if we serve "domo@" */
+ cp1 = 0;
+ }
+ if (cp2) {
+ userlocal = cp2;
+ cp2 += str_chr(cp2,'@');
+ if (*cp2) {
+ *cp2++ = '\0';
+ userhost = cp2;
+ }
+ }
+ if (cp1) {
+ listlocal = cp1;
+ cp1 += str_chr(cp1,'@');
+ if (*cp1) {
+ *cp1++ = '\0';
+ listhost = cp1;
+ }
+ }
+ }
+ checkarg(command); /* better safe than sorry */
+ checkarg(userlocal); checkarg(userhost);
+ checkarg(listlocal); checkarg(listhost);
+}
+
+void main(argc,argv)
+int argc;
+char **argv;
+{
+ char *dir;
+ char *local;
+ char *action;
+ char *def;
+ char *sender;
+ char *psz;
+ char *err;
+ int cmdidx;
+ int flagsub;
+ int flagok;
+ int flagnosubject;
+ int match;
+ int flaginheader;
+ int flagbadfield;
+ int flagmultipart = 0;
+ int fd;
+ int opt;
+ unsigned int pos,pos1,len,last;
+
+ (void)umask(022);
+ sig_pipeignore();
+
+ while ((opt = getopt(argc,argv,"f:F:vV")) != opteof)
+ switch(opt) {
+ case 'F':
+ case 'f': if (optarg) cfname = optarg; break;
+ case 'v':
+ case 'V': strerr_die2x(0,"ezmlm-request version: ",EZIDX_VERSION);
+ default:
+ die_usage();
+ }
+
+ dir = argv[optind];
+ if (!dir) die_usage();
+
+ if (chdir(dir) == -1)
+ strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": ");
+
+ /* do minimum to identify request for this program in case */
+ /* it's invoked in line with e.g. ezmlm-manage */
+
+ def = env_get("DEFAULT");
+ if (def) { /* qmail>=1.02 */
+ action = def;
+ } else if (cfname) { /* older qmail OR just list-mdomo */
+ local = env_get("LOCAL");
+ if (!local) strerr_die2x(100,FATAL,ERR_NOLOCAL);
+ len = str_len(local);
+ if (len >= 8 && !case_diffb(local + len - 8,8,"-return-")) {
+ action = "return-"; /* our bounce with qmail<1.02 */
+ } else
+ action = ""; /* list-mdomo-xxx won't work for older lists */
+ } else { /* older qmail versions */
+ local = env_get("LOCAL");
+ if (!local) strerr_die2x(100,FATAL,ERR_NOLOCAL);
+ 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 (*action)
+ if (*(action++) != '-') die_badaddr(); /* check anyway */
+ }
+ /* at this point action = "request" or "request-..." for std use; */
+ /* "" for majordomo@ */
+ if (!cfname) { /* expect request */
+ if (case_starts(action,ACTION_REQUEST))
+ action += str_len(ACTION_REQUEST);
+ else if (case_starts(action,ALT_REQUEST))
+ action += str_len(ALT_REQUEST);
+ else
+ _exit(0); /* not for us */
+ }
+ getconf_line(&outlocal,"outlocal",1,FATAL,dir);
+ getconf_line(&outhost,"outhost",1,FATAL,dir);
+
+ if (!stralloc_copy(&listname,&outlocal)) die_nomem();
+ if (!stralloc_copy(&hostname,&outhost)) die_nomem();
+ if (!stralloc_0(&outlocal)) die_nomem();
+ if (!stralloc_0(&outhost)) die_nomem();
+
+ sender = env_get("SENDER");
+ if (!sender) strerr_die2x(99,INFO,ERR_NOSENDER);
+ if (!*sender)
+ strerr_die2x(99,INFO,ERR_BOUNCE);
+ if (!sender[str_chr(sender,'@')])
+ strerr_die2x(99,INFO,ERR_ANONYMOUS);
+ if (str_equal(sender,"#@[]"))
+ strerr_die2x(99,INFO,ERR_BOUNCE);
+
+ getconf(&headerremove,"headerremove",1,FATAL,dir);
+ constmap_init(&headerremovemap,headerremove.s,headerremove.len,0);
+
+ if (!stralloc_copys(&mydtline,
+ "Delivered-To: request processor for ")) die_nomem();
+ if (!stralloc_cats(&mydtline,outlocal.s)) die_nomem();
+ if (!stralloc_cats(&mydtline,"@")) die_nomem();
+ if (!stralloc_cats(&mydtline,outhost.s)) die_nomem();
+ if (!stralloc_cats(&mydtline,"\n")) die_nomem();
+
+ flagnosubject = 1;
+ if (action[0]) { /* mainly to allow ezmlm-lists or ezmlm-which with */
+ flagnosubject = 0; /* a command address rather than a complete msg */
+ command = action;
+ if (str_start(action,"return")) /* kill bounces */
+ strerr_die2x(0,INFO,ERR_BOUNCE);
+ pos = 1 + str_chr(action + 1,'-');
+ if (action[pos]) { /* start of target */
+ action[pos] = '\0';
+ userlocal = action + pos + 1;
+ pos = str_rchr(userlocal,'='); /* the "pseudo-@" */
+ if (userlocal[pos]) {
+ userlocal[pos] = '\0';
+ userhost = userlocal + pos + 1;
+ }
+ }
+ } else {
+ for (;;) { /* Get Subject: */
+ if (getln(&ssin,&line,&match,'\n') == -1)
+ strerr_die2sys(111,FATAL,ERR_READ_INPUT);
+ if (line.len == 1)
+ break;
+ if ((line.s[0] != ' ') && (line.s[0] != '\t')) {
+ flagsub = 0;
+
+ if (case_startb(line.s,line.len,"mailing-list:"))
+ strerr_die2x(100,FATAL,ERR_MAILING_LIST);
+ else if (case_startb(line.s,line.len,"Subject:")) {
+ flaggotsub = 1;
+ pos = 8;
+ last = line.len - 2; /* skip terminal '\n' */
+ while (line.s[last] == ' ' || line.s[last] == '\t') --last;
+ while (pos <= last &&
+ (line.s[pos] == ' ' || line.s[pos] == '\t')) ++pos;
+ if (!stralloc_copyb(&subject,line.s+pos,last-pos+1)) die_nomem();
+ } else if (case_startb(line.s,line.len,"content-type:")) {
+ pos = 13; last = line.len - 2; /* not cont-line - ok */
+ while (pos <= last &&
+ (line.s[pos] == ' ' || line.s[pos] == '\t')) ++pos;
+ if (case_startb(line.s+pos,line.len - pos,"multipart/"))
+ flagmultipart = 1;
+ } else if (line.len == mydtline.len)
+ if (!byte_diff(line.s,line.len,mydtline.s))
+ strerr_die2x(100,FATAL,ERR_LOOPING);
+ } else if (flagsub) { /* Continuation line */
+ pos = 1;
+ len = line.len - 2; /* skip terminal '\n' */
+ while (line.s[len] == ' ' || line.s[len] == '\t') --len;
+ while (pos < len &&
+ (line.s[pos] == ' ' || line.s[pos] == '\t')) ++pos;
+ if (!stralloc_append(&subject," ")) die_nomem();
+ if (!stralloc_copy(&subject,line.s+pos,len-pos+1)) die_nomem();
+ }
+ if (!match)
+ break;
+ }
+ if (!cfname) { /* listserv@/majordomo@ ignore */
+ register char ch;
+ if (!stralloc_0(&subject)) die_nomem();
+ ch = *subject.s; /* valid commands/list names start w letter */
+ if ((ch <= 'z' && ch >= 'a') || (ch <= 'Z' && ch >= 'A')) {
+ parseline(subject.s);
+ flagnosubject = 0;
+ }
+ }
+ if (cfname || flagnosubject) {
+ for (;;) { /* parse body */
+ if (getln(&ssin,&line,&match,'\n') == -1)
+ strerr_die2sys(111,FATAL,ERR_READ_INPUT);
+ if (!match) break;
+ if (line.len == 1 && flagmultipart != 2) continue;
+ /* lazy MIME cludge assumes first '--...' is start border */
+ /* which is virtually always true */
+ if (flagmultipart == 1) { /* skip to first border */
+ if (*line.s != '-' || line.s[1] != '-') continue;
+ flagmultipart = 2;
+ continue;
+ } else if (flagmultipart == 2) { /* skip content info */
+ if (line.len != 1) continue;
+ flagmultipart = 3; /* may be part within part */
+ continue; /* and blank line */
+ } else if (flagmultipart == 3) {
+ if (*line.s == '-' && line.s[1] == '-') {
+ flagmultipart = 2; /* part within part */
+ continue;
+ }
+ }
+ {
+ register char ch;
+ ch = *line.s;
+ if (line.len == 1 ||
+ !((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')))
+ continue; /* skip if not letter pos 1 */
+ }
+ /* Here we have a body line with something */
+ if (!stralloc_copy(&subject,&line)) die_nomem(); /* save it */
+ subject.s[subject.len-1] = '\0';
+ parseline(subject.s);
+ break;
+ }
+ }
+ }
+ /* Do command substitution */
+ if (!stralloc_copys(&cmds,cmdstring)) die_nomem();
+ if (!stralloc_0(&cmds)) die_nomem();
+ psz = cmds.s;
+ while (*psz) {
+ if (*psz == '\\') *psz = '\0';
+ ++psz;
+ }
+ if (!constmap_init(&commandmap,cmds.s,cmds.len,0)) die_nomem();
+ cmdidx = cmdxlate[constmap_index(&commandmap,command,str_len(command))];
+ if (cmdidx == EZREQ_BAD) { /* recognized, but not supported -> help */
+ listlocal = 0; /* needed 'cause arguments are who-knows-what */
+ listhost = 0;
+ userlocal = 0;
+ userhost = 0;
+ cmdidx = EZREQ_HELP;
+ }
+ if (cfname && !listlocal && !userlocal && cmdidx > 0)
+ cmdidx = noargsxlate[cmdidx]; /* some done differently if no args */
+
+ /* =0 not found. This is treated as a list command! */
+ if (cmdidx < 0 && !cfname) {
+ cmdidx = EZREQ_HELP;
+ }
+ if (qmail_open(&qq,(stralloc *) 0) == -1)
+ strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE);
+
+ if (cmdidx >= 0) {
+ /* Things handled elsewhere. We do want to handle a simple HELP */
+ /* without arguments for e.g. majordomo@ from our own help file */
+
+ if (!stralloc_copys(&from,sender)) die_nomem();
+ if (!stralloc_0(&from)) die_nomem();
+ if (!listlocal) {
+ if (cfname)
+ strerr_die1x(100,ERR_REQ_LISTNAME);
+ else
+ listlocal = outlocal.s; /* This is at the -request address */
+ }
+ /* if !cfname listhost is made outhost. If cfname, listhost=outhost */
+ /* is ok. listhost=0 => first match in config. Other listhost is ok */
+ /* only if match is found. Otherwise it's set to outhost. */
+
+ if (!cfname || (listhost && !case_diffs(listhost,outhost.s)))
+ listhost = outhost.s;
+ else { /* Check listhost against config file */
+ pos = str_len(listlocal);
+ fd = open_read(cfname);
+ if (fd == -1)
+ strerr_die4sys(111,FATAL,ERR_OPEN,cfname,": ");
+ substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
+ flagok = 0; /* got listhost match */
+ for (;;) {
+ if (getln(&sstext,&line,&match,'\n') == -1)
+ strerr_die3sys(111,FATAL,ERR_READ,cfname);
+ if (!match)
+ break;
+ if (line.len <= 1 || line.s[0] == '#')
+ continue;
+ if ((pos < line.len) && (line.s[pos] == '@') &&
+ !byte_diff(line.s,pos,listlocal)) {
+ last = byte_chr(line.s,line.len,':');
+ if (!stralloc_copyb(&lhost,line.s+pos+1,last-pos-1)) die_nomem();
+ if (!stralloc_0(&lhost)) die_nomem();
+ if (listhost) {
+ if (!case_diffs(listhost,lhost.s)) {
+ flagok = 1;
+ break; /* host did match */
+ } else
+ continue; /* host didn't match */
+ } else { /* none given - grab first */
+ listhost = lhost.s;
+ flagok = 1;
+ break;
+ }
+ }
+ }
+ if (!flagok)
+ listhost = outhost.s;
+ close(fd);
+ }
+ if (!listhost)
+ listhost = outhost.s;
+ if (!userlocal) {
+ if (!stralloc_copys(&usr,sender)) die_nomem();
+ if (!stralloc_0(&usr)) die_nomem();
+ userlocal = usr.s;
+ userhost = usr.s + byte_rchr(usr.s,usr.len-1,'@');
+ if (!*userhost)
+ userhost = 0;
+ else {
+ *userhost = '\0';
+ ++userhost;
+ }
+ }
+
+ if (!stralloc_copys(&to,listlocal)) die_nomem();
+ if (!stralloc_cats(&to,"-")) die_nomem();
+ if (cmdidx) { /* recognized - substitute */
+ if (!stralloc_cats(&to,constmap_get(&commandmap,cmdidx)))
+ die_nomem();
+ } else /* not recognized - use as is */
+ if (!stralloc_cats(&to,command)) die_nomem();
+
+ if (!stralloc_cats(&to,"-")) die_nomem();
+ if (!stralloc_cats(&to,userlocal)) die_nomem();
+ if (userhost) { /* doesn't exist for e.g. -get */
+ if (!stralloc_cats(&to,"=")) die_nomem();
+ if (!stralloc_cats(&to,userhost)) die_nomem();
+ }
+ if (!stralloc_cats(&to,"@")) die_nomem();
+ if (!stralloc_cats(&to,listhost)) die_nomem();
+ if (!stralloc_0(&to)) die_nomem();
+
+ qmail_put(&qq,mydtline.s,mydtline.len);
+
+ flaginheader = 1;
+ flagbadfield = 0;
+
+ if (seek_begin(0) == -1)
+ strerr_die2sys(111,FATAL,ERR_SEEK_INPUT);
+ substdio_fdbuf(&ssin,read,0,inbuf,sizeof(inbuf));
+
+ for (;;) {
+ if (getln(&ssin,&line,&match,'\n') == -1)
+ strerr_die2sys(111,FATAL,ERR_READ_INPUT);
+
+ if (flaginheader && match) {
+ if (line.len == 1)
+ flaginheader = 0;
+ if ((line.s[0] != ' ') && (line.s[0] != '\t')) {
+ flagbadfield = 0;
+ if (constmap(&headerremovemap,line.s,byte_chr(line.s,line.len,':')))
+ flagbadfield = 1;
+ }
+ }
+ if (!(flaginheader && flagbadfield))
+ qmail_put(&qq,line.s,line.len);
+ if (!match)
+ break;
+ }
+ } else { /* commands we deal with */
+ cmdidx = - cmdidx; /* now positive */
+ if (cmdidx == EZREQ_WHICH) { /* arg is user, not list */
+ userlocal = listlocal; listlocal = 0;
+ userhost = listhost; listhost = 0;
+ }
+ if (!stralloc_copys(&from,outlocal.s)) die_nomem();
+ if (!stralloc_cats(&from,"-return-@")) die_nomem();
+ if (!stralloc_cats(&from,outhost.s)) die_nomem();
+ if (!stralloc_0(&from)) die_nomem();
+
+ if (userlocal) {
+ if (!stralloc_copys(&to,userlocal)) die_nomem();
+ if (!stralloc_cats(&to,"@")) die_nomem();
+ if (userhost) {
+ if (!stralloc_cats(&to,userhost)) die_nomem();
+ } else {
+ if (!stralloc_cats(&to,outhost.s)) die_nomem();
+ }
+ } else
+ if (!stralloc_copys(&to,sender)) die_nomem();
+ if (!stralloc_0(&to)) die_nomem();
+
+ /* now we need to look for charset and set flagcd appropriately */
+
+ 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();
+ set_cpoutlocal(&listname); /* necessary in case there are <#l#> */
+ set_cpouthost(&hostname); /* necessary in case there are <#h#> */
+ /* we don't want to be send to a list*/
+ qmail_puts(&qq,"Mailing-List: ezmlm-request");
+ if (getconf(&line,"listid",0,FATAL)) {
+ qmail_puts(&qq,"List-ID: ");
+ qmail_put(&qq,line.s,line.len);
+ }
+ qmail_puts(&qq,"\nDate: ");
+ when = now();
+ 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)))
+ 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_cats(&line,outhost.s)) die_nomem();
+ if (!stralloc_0(&line)) die_nomem();
+ qmail_puts(&qq,line.s);
+ qmail_puts(&qq,">\nFrom: ");
+ if (!quote2(&line,outlocal.s)) die_nomem();
+ qmail_put(&qq,line.s,line.len);
+ if (cmdidx == EZREQ_HELP)
+ qmail_puts(&qq,"-return-@");
+ else
+ qmail_puts(&qq,"-help@");
+ qmail_puts(&qq,outhost.s);
+ qmail_puts(&qq,"\n");
+ qmail_put(&qq,mydtline.s,mydtline.len);
+ qmail_puts(&qq,"To: ");
+ if (!quote2(&line,to.s)) die_nomem();
+ qmail_put(&qq,line.s,line.len);
+ qmail_puts(&qq,"\n");
+ qmail_puts(&qq,"MIME-Version: 1.0\n");
+ if (flagcd) {
+ qmail_puts(&qq,"Content-Type: multipart/mixed; charset=");
+ qmail_puts(&qq,charset.s);
+ qmail_puts(&qq,";\n\tboundary=");
+ qmail_puts(&qq,boundary);
+ } else {
+ qmail_puts(&qq,"Content-type: text/plain; charset=");
+ qmail_puts(&qq,charset.s);
+ }
+ qmail_puts(&qq,"\nSubject: ");
+ if (!quote2(&line,outlocal.s)) die_nomem();
+ qmail_put(&qq,line.s,line.len);
+ qmail_puts(&qq,TXT_RESULTS);
+ transferenc();
+ copy(&qq,"text/top",flagcd,FATAL);
+ if (cmdidx == EZREQ_LISTS || cmdidx == EZREQ_WHICH) {
+ switch (cmdidx) {
+ case EZREQ_LISTS:
+ code_qput("LISTS:",6);
+ break;
+ case EZREQ_WHICH:
+ code_qput("WHICH (",7);
+ code_qput(to.s,to.len - 1);
+ code_qput("):\n\n",4);
+ break;
+ default: break;
+ }
+ fd = open_read(cfname);
+ if (fd == -1)
+ strerr_die4sys(111,FATAL,ERR_OPEN,cfname,": ");
+ substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf));
+ for (;;) {
+ if (getln(&sstext,&line,&match,'\n') == -1)
+ strerr_die3sys(111,FATAL,ERR_READ,cfname);
+ if (!match)
+ break;
+ if (line.len <= 1 || line.s[0] == '#')
+ continue;
+ if (!stralloc_0(&line)) die_nomem();
+ pos = str_chr(line.s,':');
+ if (!line.s[pos])
+ break;
+ line.s[pos] = '\0';
+ ++pos;
+ pos1 = pos + str_chr(line.s + pos,':');
+ if (line.s[pos1]) {
+ line.s[pos1] = '\0';
+ ++pos1;
+ } else
+ pos1 = 0;
+
+ switch (cmdidx) {
+ case EZREQ_LISTS:
+ code_qput("\n\n\t",3);
+ code_qput(line.s,pos-1);
+ code_qput("\n",1);
+ if (pos1) {
+ code_qput(line.s+pos1,line.len-2-pos1);
+ }
+ break;
+ case EZREQ_WHICH:
+ if (issub(line.s+pos,to.s,(char *) 0,FATAL)) {
+ code_qput(line.s,pos-1);
+ code_qput("\n",1);
+ }
+ closesql(); /* likely different dbs for different lists */
+ break;
+ }
+ }
+ code_qput("\n",1);
+ close(fd);
+ } else
+ copy(&qq,"text/help",flagcd,FATAL);
+
+ copy(&qq,"text/bottom",flagcd,FATAL);
+ if (flagcd) {
+ if (flagcd == 'B') {
+ encodeB("",0,&line,2,FATAL); /* flush */
+ qmail_put(&qq,line.s,line.len);
+ }
+ qmail_puts(&qq,"\n--");
+ qmail_puts(&qq,boundary);
+ 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(&line,sender)) die_nomem();
+ qmail_put(&qq,line.s,line.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);
+ if (flagcd) {
+ qmail_puts(&qq,"\n--");
+ qmail_puts(&qq,boundary);
+ qmail_puts(&qq,"--\n");
+ }
+ }
+ qmail_from(&qq,from.s);
+ 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_die3x(99,INFO, "qp ",strnum);
+}