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