X-Git-Url: https://git.distorted.org.uk/~mdw/ezmlm/blobdiff_plain/5b62e993b0af39700031c2875d7f6654e6a02850..f8beb284087c279acfb30506f5bb32baa4949b44:/ezmlm-request.c diff --git a/ezmlm-request.c b/ezmlm-request.c new file mode 100644 index 0000000..661616d --- /dev/null +++ b/ezmlm-request.c @@ -0,0 +1,826 @@ +/*$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); +}