void doheaders(void) { int flaggoodfield,match; if (act == AC_DIGEST) copy(&qq,"headeradd",'H'); hdr_add2s("Mailing-List: ",MSG(TXT_MAILING_LIST)); if (listid.len > 0) hdr_add2("List-ID: ",listid.s,listid.len); hdr_datemsgid(when); hdr_from("-help"); if (!stralloc_copys(&mydtline,"Delivered-To: responder for ")) die_nomem(); if (!stralloc_catb(&mydtline,outlocal.s,outlocal.len)) die_nomem(); if (!stralloc_cats(&mydtline,"@")) die_nomem(); if (!stralloc_catb(&mydtline,outhost.s,outhost.len)) die_nomem(); if (!stralloc_cats(&mydtline,"\n")) die_nomem(); qmail_put(&qq,mydtline.s,mydtline.len); flaggoodfield = 0; if (act != AC_DIGEST) for (;;) { if (getln(&ssin,&line,&match,'\n') == -1) strerr_die2sys(111,FATAL,MSG(ERR_READ_INPUT)); if (!match) break; if (line.len == 1) break; if ((line.s[0] != ' ') && (line.s[0] != '\t')) { flaggoodfield = 0; if (case_startb(line.s,line.len,"mailing-list:")) { if (flageditor) /* we may be running from a sublist */ flaggoodfield = 0; else strerr_die2x(100,FATAL,MSG(ERR_MAILING_LIST)); } if (line.len == mydtline.len) if (byte_equal(line.s,line.len,mydtline.s)) strerr_die2x(100,FATAL,MSG(ERR_LOOPING)); if (case_startb(line.s,line.len,"delivered-to:")) flaggoodfield = 1; if (case_startb(line.s,line.len,"received:")) flaggoodfield = 1; } if (flaggoodfield) qmail_put(&qq,line.s,line.len); } }
void maketo(void) /* expects line to be a return-path line. If it is and the format is valid */ /* to is set to to the sender. Otherwise, to is left untouched. Assuming */ /* to is empty to start with, it will remain empty if no sender is found. */ { unsigned int x, y; if (case_startb(line.s,line.len,"return-path:")) { x = 12 + byte_chr(line.s + 12,line.len-12,'<'); if (x != line.len) { y = byte_rchr(line.s + x,line.len-x,'>'); if (y + x != line.len) { if (!stralloc_copyb(&to,line.s+x+1,y-1)) die_nomem(); if (!stralloc_0(&to)) die_nomem(); } /* no return path-> no addressee. A NUL in the sender */ } /* is no worse than a faked sender, so no problem */ } }
static void do_ed(char *action) { datetime_sec u; int flaggoodfield; int fd; char *x, *y; char *cp,*cplast,*cpnext,*cpafter; int flagdone; unsigned int len; const char *fname; unsigned int i; x = action + LENGTH_ED; x += scan_ulong(x,&u); if ((u > when) || (u < when - 100000)) die_cookie(); if (*x == '.') ++x; fname = x; x += str_chr(x,'.'); if (!*x) die_cookie(); *x = (char) 0; ++x; stralloc_copys(&fnedit,"text/"); stralloc_cats(&fnedit,fname); stralloc_0(&fnedit); y = fnedit.s + 5; /* after "text/" */ while (*++y) { /* Name should be guaranteed by the cookie, */ /* but better safe than sorry ... */ if (((*y > 'z') || (*y < 'a')) && (*y != '_')) strerr_die2x(100,FATAL,MSG(ERR_BAD_NAME)); if (*y == '_') *y = '-'; } lock(); /* file must not change while here */ switch (slurp(fnedit.s,&text,1024)) { case -1: strerr_die2sys(111,FATAL,MSG1(ERR_READ,fnedit.s)); case 0: strerr_die5x(100,FATAL,dir,"/",fnedit.s,MSG(ERR_NOEXIST)); } stralloc_copy(&line,&text); subst_nuls(&line); stralloc_cat(&line,&fnedit); /* including '\0' */ strnum[fmt_ulong(strnum,(unsigned long) u)] = 0; cookie(hash,key.s,key.len,strnum,line.s,"-e"); if (str_len(x) != COOKIE) die_cookie(); if (byte_diff(hash,COOKIE,x)) die_cookie(); /* cookie is ok, file exists, lock's on, new file ends in '_' */ stralloc_copys(&fneditn,fnedit.s); stralloc_append(&fneditn,'_'); stralloc_0(&fneditn); fd = open_trunc(fneditn.s); if (fd == -1) strerr_die2sys(111,FATAL,MSG1(ERR_WRITE,fneditn.s)); substdio_fdbuf(&sstext,write,fd,textbuf,sizeof(textbuf)); stralloc_copys("ed,""); /* clear */ stralloc_copys(&text,""); for (;;) { /* get message body */ if (getln(&ssin,&line,&match,'\n') == -1) strerr_die2sys(111,FATAL,MSG(ERR_READ_INPUT)); if (!match) break; stralloc_cat(&text,&line); } if (encin) { /* decode if necessary */ if (encin == 'B') decodeB(text.s,text.len,&line); else decodeQ(text.s,text.len,&line); stralloc_copy(&text,&line); } cp = text.s; cpafter = text.s+text.len; flaggoodfield = 0; flagdone = 0; len = 0; while ((cpnext = cp + byte_chr(cp,cpafter-cp,'\n')) != cpafter) { i = byte_chr(cp,cpnext-cp,'%'); if (i != (unsigned int) (cpnext - cp)) { if (!flaggoodfield) { /* MSG(TXT_EDIT_START)/END */ if (case_startb(cp+i,cpnext-cp-i,MSG(TXT_EDIT_START))) { /* start tag. Store users 'quote characters', e.g. '> ' */ stralloc_copyb("ed,cp,i); flaggoodfield = 1; cp = cpnext + 1; continue; } } else if (case_startb(cp+i,cpnext-cp-i,MSG(TXT_EDIT_END))) { flagdone = 1; break; } } if (flaggoodfield) { if ((len += cpnext - cp - quoted.len + 1) > MAXEDIT) strerr_die1x(100,MSG(ERR_EDSIZE)); if (quoted.len && cpnext-cp >= (int) quoted.len && !str_diffn(cp,quoted.s,quoted.len)) cp += quoted.len; /* skip quoting characters */ cplast = cpnext - 1; if (*cplast == '\r') /* CRLF -> '\n' for base64 encoding */ *cplast = '\n'; else ++cplast; if (substdio_put(&sstext,cp,cplast-cp+1) == -1) strerr_die2sys(111,FATAL,MSG1(ERR_WRITE,fneditn.s)); } cp = cpnext + 1; } if (!flagdone) strerr_die2x(100,FATAL,MSG(ERR_NO_MARK)); if (substdio_flush(&sstext) == -1) strerr_die2sys(111,FATAL,MSG1(ERR_WRITE,fneditn.s)); if (fsync(fd) == -1) strerr_die2sys(111,FATAL,MSG1(ERR_SYNC,fneditn.s)); if (fchmod(fd, 0600) == -1) strerr_die2sys(111,FATAL,MSG1(ERR_CHMOD,fneditn.s)); if (close(fd) == -1) strerr_die2sys(111,FATAL,MSG1(ERR_CLOSE,fneditn.s)); wrap_rename(fneditn.s,fnedit.s); unlock(); hdr_subject(MSG1(SUB_EDIT_SUCCESS,fname)); hdr_ctboundary(); copy(&qq,"text/top",flagcd); copy_act("text/edit-done"); copybottom(0); qmail_to(&qq,sender); /* not necessarily from mod */ }
void msg_headers(int act) /* Writes all the headers up to but not including subject */ { int flaggoodfield; int flaggetfrom; unsigned int pos; hdr_add2s("Mailing-List: ",MSG(TXT_MAILING_LIST)); if (listid.len > 0) hdr_add2("List-ID: ",listid.s,listid.len); quote("ed,&outlocal); /* quoted has outlocal */ qmail_puts(&qq,"List-Help: <mailto:"); /* General rfc2369 headers */ qmail_put(&qq,quoted.s,quoted.len); qmail_puts(&qq,"-help@"); qmail_put(&qq,outhost.s,outhost.len); qmail_puts(&qq,">\nList-Post: <mailto:"); qmail_put(&qq,quoted.s,quoted.len); qmail_puts(&qq,"@"); qmail_put(&qq,outhost.s,outhost.len); qmail_puts(&qq,">\nList-Subscribe: <mailto:"); qmail_put(&qq,quoted.s,quoted.len); qmail_puts(&qq,"-subscribe@"); qmail_put(&qq,outhost.s,outhost.len); qmail_puts(&qq,">\n"); hdr_datemsgid(when); /* differnt "From:" for help to break auto-responder loops */ hdr_from((act == AC_HELP) ? "-return-" : "-help"); quote2("ed,target.s); hdr_add2("To: ",quoted.s,quoted.len); stralloc_copys(&mydtline,"Delivered-To: responder for "); stralloc_catb(&mydtline,outlocal.s,outlocal.len); stralloc_cats(&mydtline,"@"); stralloc_catb(&mydtline,outhost.s,outhost.len); stralloc_cats(&mydtline,"\n"); qmail_put(&qq,mydtline.s,mydtline.len); flaggoodfield = 0; /* do it for -sc, but if the -S flag is used, do it for -subscribe */ flaggetfrom = flagstorefrom && ((act == AC_SC) || ((act == AC_SUBSCRIBE) && !flagsubconf)); for (;;) { if (gethdrln(&ssin,&line,&match,'\n') == -1) strerr_die2sys(111,FATAL,MSG(ERR_READ_INPUT)); if (!match) break; if (line.len == 1) break; flaggoodfield = 0; if (case_startb(line.s,line.len,"mailing-list:")) strerr_die2x(100,FATAL,MSG(ERR_MAILING_LIST)); if (line.len == mydtline.len) if (byte_equal(line.s,line.len,mydtline.s)) strerr_die2x(100,FATAL,MSG(ERR_LOOPING)); if (case_startb(line.s,line.len,"delivered-to:")) flaggoodfield = 1; else if (case_startb(line.s,line.len,"received:")) flaggoodfield = 1; else if (case_startb(line.s,line.len,"content-transfer-encoding:")) { pos = 26; while (line.s[pos] == ' ' || line.s[pos] == '\t') ++pos; if (case_startb(line.s+pos,line.len-pos,"base64")) encin = 'B'; else if (case_startb(line.s+pos,line.len-pos,"quoted-printable")) encin = 'Q'; } else if (flaggetfrom && case_startb(line.s,line.len,"from:")) { /* for logging subscriber data */ concatHDR(line.s+5,line.len-5,&fromline); } if (flaggoodfield) qmail_put(&qq,line.s,line.len); } hdr_mime(flagcd ? CTYPE_MULTIPART : CTYPE_TEXT); }
int main(int argc,char **argv) { int child; const char *sendargs[4]; stralloc addr = {0}; unsigned int pos = 0,pos2,poslocal,len; const char *cp; unsigned long hh = 4L; /* default time 04:12 */ unsigned long mm = 12L; const char *dow = "*"; /* day of week */ const char *qmail_inject = "/bin/qmail-inject "; char strnum[FMT_ULONG]; unsigned long uid,euid = 0; stralloc rp = {0}; stralloc user = {0}; stralloc euser = {0}; stralloc dir = {0}; stralloc listaddr = {0}; stralloc line = {0}; struct passwd *ppasswd; int match; int hostmatch; int localmatch; unsigned long dh,t; int founduser = 0; int listmatch = 0; int flagdigit = 0; int flagours; int foundlocal = 0; int foundmatch = 0; unsigned int nolists = 0; unsigned long maxlists; unsigned int lenhost,lenlocal; int fdin,fdout = -1; char *local = (char *) 0; /* list = local@host */ const char *host = (char *) 0; char *code = (char *) 0; /* digest code */ char inbuf[512]; substdio ssin; char outbuf[512]; substdio ssout; (void) umask(077); sig_pipeignore(); optind = getconfopt(argc,argv,options,0,0); if (flagt != 0) { pos = scan_ulong(flagt,&hh); if (flagt[pos++] != ':') die_usage(); (void) scan_ulong(flagt + pos,&mm); } if (flagw != 0) { dow = flagw; cp = flagw - 1; while (*(++cp)) { if (*cp >= '0' && *cp <= '7') { if (flagdigit) die_dow(); flagdigit = 1; } else if (*cp == ',') { if (!flagdigit) die_dow(); flagdigit = 0; } else die_dow(); } } if (flaglist + flagdelete + flagconfig > 1) strerr_die2x(100,FATAL,MSG(ERR_EXCLUSIVE)); uid = getuid(); if (uid && !(euid = geteuid())) strerr_die2x(100,FATAL,MSG(ERR_SUID)); if (!(ppasswd = getpwuid(uid))) strerr_die2x(100,FATAL,MSG(ERR_UID)); if (!stralloc_copys(&user,ppasswd->pw_name)) die_nomem(); if (!stralloc_0(&user)) die_nomem(); if (!(ppasswd = getpwuid(euid))) strerr_die2x(100,FATAL,MSG(ERR_EUID)); if (!stralloc_copys(&dir,ppasswd->pw_dir)) die_nomem(); if (!stralloc_0(&dir)) die_nomem(); if (!stralloc_copys(&euser,ppasswd->pw_name)) die_nomem(); if (!stralloc_0(&euser)) die_nomem(); wrap_chdir(dir.s); local = argv[optind++]; /* list address, optional for -c & -l */ if (!local) { if (!flagconfig && !flaglist) die_usage(); lenlocal = 0; lenhost = 0; } else { if (!stralloc_copys(&listaddr,local)) die_nomem(); if (!isclean(local,1)) die_argument(); pos = str_chr(local,'@'); lenlocal = pos; local[pos] = '\0'; host = local + pos + 1; lenhost = str_len(host); code = argv[optind]; if (!code) { /* ignored for -l, -c, and -d */ if (flagdelete || flaglist || flagconfig) /* get away with not putting code for delete */ code = (char*)"a"; /* a hack - so what! */ else die_usage(); } else if (!isclean(code,0)) die_argument(); } if ((fdin = open_read(TXT_EZCRONRC)) == -1) strerr_die2sys(111,FATAL,MSG1(ERR_OPEN,TXT_EZCRONRC)); /* first line is special */ substdio_fdbuf(&ssin,read,fdin,inbuf,sizeof(inbuf)); if (getln(&ssin,&line,&match,'\n') == -1) strerr_die2sys(111,FATAL,MSG1(ERR_READ,TXT_EZCRONRC)); if (!match) strerr_die2sys(111,FATAL,MSG1(ERR_READ,TXT_EZCRONRC)); /* (since we have match line.len has to be >= 1) */ line.s[line.len - 1] = '\0'; if (!isclean(line.s,0)) /* host for bounces */ strerr_die2x(100,FATAL,MSG1(ERR_CFHOST,TXT_EZCRONRC)); if (!stralloc_copys(&rp,line.s)) die_nomem(); match = 1; for(;;) { if (!match) break; /* to allow last line without '\n' */ if (getln(&ssin,&line,&match,'\n') == -1) strerr_die2sys(111,FATAL,MSG1(ERR_READ,TXT_EZCRONRC)); if (!line.len) break; line.s[line.len-1] = '\0'; if (!case_startb(line.s,line.len,user.s)) continue; pos = user.len - 1; if (pos >= line.len || line.s[pos] != ':') continue; founduser = 1; /* got user line */ break; } close(fdin); if (!founduser) strerr_die2x(100,FATAL,MSG(ERR_BADUSER)); if (flagconfig) { line.s[line.len-1] = '\n'; /* not very elegant ;-) */ substdio_fdbuf(&ssout,write,1,outbuf,sizeof(outbuf)); if (substdio_put(&ssout,line.s,line.len) == -1) strerr_die2sys(111,FATAL,MSG(ERR_WRITE_STDOUT)); if (substdio_flush(&ssout) == -1) strerr_die2sys(111,FATAL,MSG(ERR_WRITE_STDOUT)); _exit(0); } ++pos; /* points to first ':' */ len = str_chr(line.s+pos,':'); /* second ':' */ if (!line.s[pos + len]) die_syntax(&line); if (!local) { /* only -d and std left */ localmatch = 1; hostmatch = 1; } else { hostmatch = 0; if (len <= str_len(local)) if (!str_diffn(line.s+pos,local,len)) localmatch = 1; } pos += len + 1; len = str_chr(line.s + pos,':'); /* third */ if (!line.s[pos + len]) die_syntax(&line); if (local) { /* check host */ if (len == 0) /* empty host => any host */ hostmatch = 1; else if (len == str_len(host)) if (!case_diffb(line.s+pos,len,host)) hostmatch = 1; } pos += len + 1; pos += scan_ulong(line.s+pos,&maxlists); if (line.s[pos]) { /* check additional lists */ if (line.s[pos] != ':') die_syntax(&line); if (line.s[pos+1+str_chr(line.s+pos+1,':')]) die_syntax(&line); /* reminder lists are not separated by ':' */ /* otherwise a ':' or arg miscount will die */ /* silently */ if (local) { while (++pos < line.len) { len = str_chr(line.s + pos,'@'); if (len == lenlocal && !str_diffn(line.s + pos,local,len)) { pos += len; if (!line.s[pos]) break; pos++; len = str_chr(line.s+pos,','); if (len == lenhost && !case_diffb(line.s+pos,len,host)) { listmatch = 1; break; } } pos += len; } } } if (!listmatch) { if (!hostmatch) strerr_die2x(100,FATAL,MSG(ERR_BADHOST)); if (!localmatch) strerr_die2x(100,FATAL,MSG(ERR_BADLOCAL)); } /* assemble correct line */ if (!flaglist) { if (!stralloc_copyb(&addr,strnum,fmt_ulong(strnum,mm))) die_nomem(); if (!stralloc_cats(&addr," ")) die_nomem(); dh = 0L; if (deltah <= 3L) dh = deltah; else if (deltah <= 6L) dh = 6L; else if (deltah <= 12L) dh = 12L; else if (deltah <= 24L) dh = 24L; else if (deltah <= 48L) { if (dow[0] == '*') dow = "1,3,5"; } else if (deltah <= 72L) { if (dow[0] == '*') dow = "1,4"; } else if (dow[0] == '*') dow = "1"; if (!dh) { if (!stralloc_cats(&addr,"*")) die_nomem(); } else { if (!stralloc_catb(&addr,strnum,fmt_ulong(strnum,hh))) die_nomem(); for (t = hh + dh; t < hh + 24L; t+=dh) { if (!stralloc_cats(&addr,",")) die_nomem(); if (!stralloc_catb(&addr,strnum,fmt_ulong(strnum,t % 24L))) die_nomem(); } } if (!stralloc_cats(&addr," * * ")) die_nomem(); if (!stralloc_cats(&addr,dow)) die_nomem(); if (!stralloc_cats(&addr," ")) die_nomem(); if (!stralloc_cats(&addr,auto_qmail)) die_nomem(); if (!stralloc_cats(&addr,qmail_inject)) die_nomem(); if (!stralloc_cats(&addr,local)) die_nomem(); if (!stralloc_cats(&addr,"-dig-")) die_nomem(); if (!stralloc_cats(&addr,code)) die_nomem(); if (!stralloc_cats(&addr,"@")) die_nomem(); if (!stralloc_cats(&addr,host)) die_nomem(); /* feed 'Return-Path: <user@host>' to qmail-inject */ if (!stralloc_cats(&addr,"%Return-path: <")) die_nomem(); if (!stralloc_cats(&addr,user.s)) die_nomem(); if (!stralloc_cats(&addr,"@")) die_nomem(); if (!stralloc_cat(&addr,&rp)) die_nomem(); if (!stralloc_cats(&addr,">\n")) die_nomem(); } if (!stralloc_0(&addr)) die_nomem(); if (!flaglist) { /* now to rewrite crontab we need to lock */ lockfile("crontabl"); } /* if !flaglist */ if ((fdin = open_read("crontab")) == -1) { if (errno != error_noent) strerr_die2sys(111,FATAL,MSG1(ERR_READ,"crontab")); } else substdio_fdbuf(&ssin,read,fdin,inbuf,sizeof(inbuf)); if (flaglist) substdio_fdbuf(&ssout,write,1,outbuf,sizeof(outbuf)); else { if ((fdout = open_trunc("crontabn")) == -1) strerr_die2sys(111,FATAL,MSG1(ERR_WRITE,"crontabn")); substdio_fdbuf(&ssout,write,fdout,outbuf,sizeof(outbuf)); } line.len = 0; if (fdin != -1) { for (;;) { if (!flaglist && line.len) { line.s[line.len-1] = '\n'; if (substdio_put(&ssout,line.s,line.len) == -1) strerr_die2sys(111,FATAL,MSG1(ERR_WRITE,"crontabn")); } if (getln(&ssin,&line,&match,'\n') == -1) strerr_die2sys(111,FATAL,MSG1(ERR_READ,"crontab")); if (!match) break; flagours = 0; /* assume entry is not ours */ foundlocal = 0; line.s[line.len - 1] = '\0'; /* match so at least 1 char */ pos = 0; while (line.s[pos] == ' ' || line.s[pos] == '\t') ++pos; if (line.s[pos] == '#') continue; /* cron comment */ pos = str_chr(line.s,'/'); if (!str_start(line.s+pos,auto_qmail)) continue; pos += str_len(auto_qmail); if (!str_start(line.s+pos,qmail_inject)) continue; pos += str_len(qmail_inject); poslocal = pos; pos = byte_rchr(line.s,line.len,'<'); /* should be Return-Path: < */ if (pos == line.len) continue; /* not ezmlm-cron line */ pos++; len = str_chr(line.s+pos,'@'); if (len == user.len - 1 && !str_diffn(line.s+pos,user.s,len)) { flagours = 1; ++nolists; /* belongs to this user */ } if (!local) { foundlocal = 1; } else { pos = poslocal + str_chr(line.s+poslocal,'@'); if (pos + lenhost +1 >= line.len) continue; if (case_diffb(line.s+pos+1,lenhost,host)) continue; if (line.s[pos+lenhost+1] != '%') continue; /* check local */ if (poslocal + lenlocal + 5 >= line.len) continue; if (!str_start(line.s+poslocal,local)) continue; pos2 = poslocal+lenlocal; if (!str_start(line.s+pos2,"-dig-")) continue; foundlocal = 1; } if (foundlocal) { foundmatch = 1; if (flaglist && (local || flagours)) { if (substdio_put(&ssout,line.s,line.len) == -1) strerr_die2sys(111,FATAL,MSG(ERR_WRITE_STDOUT)); if (substdio_put(&ssout,"\n",1) == -1) strerr_die2sys(111,FATAL,MSG(ERR_WRITE_STDOUT)); } line.len = 0; /* same - kill line */ if (flagours) --nolists; } } close(fdin); } if (flaglist) { if (substdio_flush(&ssout) == -1) strerr_die2sys(111,FATAL,MSG1(ERR_FLUSH,"stdout")); if (foundmatch) /* means we had a match */ _exit(0); else strerr_die2x(100,FATAL,MSG(ERR_NO_MATCH)); } /* only -d and regular use left */ if (nolists >= maxlists && !flagdelete) strerr_die2x(100,FATAL,MSG(ERR_LISTNO)); if (!flagdelete) if (substdio_put(&ssout,addr.s,addr.len-1) == -1) strerr_die2sys(111,FATAL,MSG1(ERR_WRITE,"crontabn")); if (flagdelete && !foundlocal) strerr_die2x(111,FATAL,MSG(ERR_NO_MATCH)); if (substdio_flush(&ssout) == -1) strerr_die2sys(111,FATAL,MSG1(ERR_FLUSH,"crontabn")); if (fsync(fdout) == -1) strerr_die2sys(111,FATAL,MSG1(ERR_SYNC,"crontabn++")); if (close(fdout) == -1) strerr_die2sys(111,FATAL,MSG1(ERR_CLOSE,"crontabn")); wrap_rename("crontabn","crontab"); sendargs[0] = "sh"; sendargs[1] = "-c"; if (!stralloc_copys(&line,auto_cron)) die_nomem(); if (!stralloc_cats(&line,"/crontab '")) die_nomem(); if (!stralloc_cats(&line,dir.s)) die_nomem(); if (!stralloc_cats(&line,"/crontab'")) die_nomem(); if (!stralloc_0(&line)) die_nomem(); sendargs[2] = line.s; sendargs[3] = 0; if ((child = wrap_fork()) == 0) { if (setreuid(euid,euid) == -1) strerr_die2sys(100,FATAL,MSG(ERR_SETUID)); wrap_execvp(sendargs); } /* parent */ switch (wrap_waitpid(child)) { case 0: _exit(0); default: strerr_die2x(111,FATAL,MSG(ERR_CRONTAB)); } }
void main(int argc,char **argv) { char *def; char *local; const char *action = ""; char *psz; const char *err; int fd; unsigned int i,j; int flagremote; int match; int goodexit = 0; /* exit code for normal exit */ /* in manager this will be set to 0 */ unsigned long from,u,to,issue,prevmax; unsigned long mno = 0; unsigned long chunk; unsigned long subs = 0; unsigned int pos,pos1; unsigned int len; int opt; char outformat = 0; msgentry *msgtable; subentry *subtable; authentry *authtable; dateentry *datetable; struct datetime dt; char date[DATE822FMT]; (void) umask(022); sig_pipeignore(); when = now(); datetime_tai(&dt,when); opt = getconfopt(argc,argv,options,1,0); initsub(0); if (flagformat != 0) if (FORMATS[str_chr(FORMATS,flagformat[0])]) outformat = flagformat[0]; if (outformat == 0) { outformat = (getconf_line(&line,"digformat",0) && FORMATS[str_chr(FORMATS,line.s[0])]) ? line.s[0] : DEFAULT_FORMAT; } /* code to activate digest (-digest-code)*/ if ((digestcode = argv[opt]) == 0) { if (getconf_line(&digestcodefile,"digestcode",0) && digestcodefile.len > 0) { if (!stralloc_0(&digestcodefile)) die_nomem(); digestcode = digestcodefile.s; } } /* ignore any extra args */ if (!stralloc_copy(&subject,&outlocal)) die_nomem(); /* for subjects */ if (!stralloc_copy(&listname,&outlocal)) die_nomem(); /* for content disp */ local = env_get("LOCAL"); def = env_get("DEFAULT"); sender = get_sender(); if (local && *local) { /* in editor local = outlocal */ if (!sender) strerr_die2x(100,FATAL,MSG(ERR_NOSENDER)); if (!*sender) strerr_die2x(100,FATAL,MSG(ERR_BOUNCE)); if (str_equal(sender,"#@[]")) strerr_die2x(100,FATAL,MSG(ERR_BOUNCE)); if (!sender[str_chr(sender,'@')]) strerr_die2x(100,FATAL,MSG(ERR_ANONYMOUS)); if (def) { if (*def) { action = def; goodexit = 99; } else _exit(0); /* list-@host should do -help from manager */ } else { /* editor */ act = AC_DIGEST; /* on list-@host ! */ flageditor = 1; /* to avoid Mailing-list error on sublists */ /* when running out of dir/editor. */ } if (case_starts(action,"dig")) { action += 3; if (action[0] == '-' || action [0] == '.') { action++; if (!digestcode) strerr_die2x(100,FATAL,MSG(ERR_BAD_DIGCODE)); len = str_len(digestcode); if (len <= str_len(action) && case_startb(action,len,digestcode)) { if (FORMATS[str_chr(FORMATS,*(action+len))]) outformat = *(action+len); act = AC_DIGEST; } else strerr_die2x(100,FATAL,MSG(ERR_BAD_DIGCODE)); } } } else /* Command line operation */ act = AC_DIGEST; /* Things we deal with. If anything else just die with success! */ /* At the moment this is -index, -thread, and -get. */ /* If flagdo = 0 we only service -dig commands. This is to support*/ /* "secret" lists that are still archived and digested. -c on */ /* cmd line. */ if (act == AC_NONE) { if (case_equals(action,ACTION_DIGEST)) { act = AC_GET; /* list-digest@ => msg since last digest */ action = ACTION_GET; } else if (case_starts(action,ACTION_GET) || case_starts(action,ALT_GET)) act = AC_GET; else if (case_starts(action,ACTION_INDEX) || case_starts(action,ALT_INDEX)) act = AC_INDEX; else if (case_starts(action,ACTION_THREAD) || case_starts(action,ALT_THREAD)) act = AC_THREAD; } if (act == AC_NONE) /* not for us. Pass the buck. */ _exit(0); if (act != AC_INDEX) { /* need to do header processing */ if(!getconf(&digheaders,"digheaders",0)) { if(!stralloc_copys(&digheaders,digsz)) die_nomem(); if (!stralloc_0(&digheaders)) die_nomem(); psz = digheaders.s; while (*psz) { if (*psz == '\\') *psz = '\0'; ++psz; } } if (!constmap_init(&digheadersmap,digheaders.s,digheaders.len,0)) die_nomem(); } if (act != AC_DIGEST) { if (!flagdo) /* only do digests */ strerr_die2x(100,FATAL,MSG(ERR_NOCMD)); if (flagpublic < 0) flagpublic = !getconf_isset("modgetonly") && getconf_isset("public"); if (!flagpublic) { /* This all to take care of non-public lists. They should*/ /* still do digests, but do other things only for */ /* moderators that have remote access. Since this is rare*/ /* efforts have been made to keep everything that's not */ /* needed elsewhere in here. */ getconf_line(&moddir,"modsub",0); flagremote = getconf_line(&line,"remote",0); if (!flagremote) strerr_die2x(100,FATAL,MSG(ERR_NOT_PUBLIC)); if (!moddir.len) { if (line.len) { if (!stralloc_copy(&moddir,&line)) die_nomem(); } else { if (!stralloc_copys(&moddir,"mod")) die_nomem(); } } if (!stralloc_0(&moddir)) die_nomem(); ismod = issub(moddir.s,sender,&mod); if (!ismod) /* sender = moderator? */ strerr_die2x(100,FATAL,MSG(ERR_NOT_PUBLIC)); } } if (act == AC_DIGEST) { workdir = "digest"; if (!stralloc_cats(&outlocal,"-digest")) die_nomem(); if (getconf_line(&line,"chunk",0)) { if (!stralloc_0(&line)) die_nomem(); (void) scan_ulong(line.s,&chunk); /* same chunk as main list */ if (chunk == 0) /* limit range to 1-53 */ chunk = 1L; else if (chunk > 52) chunk = 52L; } else { chunk = 0L; } } else workdir = "."; if (!flagarchived) strerr_die2x(100,FATAL,MSG(ERR_NOT_ARCHIVED)); if (qmail_open(&qq) == -1) strerr_die2sys(111,FATAL,MSG(ERR_QMAIL_QUEUE)); set_cpnum(""); /* default for <#n#> replacement */ switch (act) { case AC_DIGEST: /* -dig{.|-}'digestcode'[f] returns an rfc1153 digest */ /* of messages from the archive. Messages */ /* dignum+1 through the last message received by the list are processed and */ /* dignum is updated to the last message processed. digissue is advanced. */ get_num(); /* max = last successful message */ to = max; lockup(); /* another digest could corrupt dignum */ /* but will be saved only if flagdigrange==0 */ if(getconf_line(&num,"dignum",0)) { if(!stralloc_0(&num)) die_nomem(); pos = scan_ulong(num.s,&prevmax); if (num.s[pos] == ':') pos++; pos += 1 + scan_ulong(num.s+pos,&cumsize); /* last cumsize */ if (num.s[pos] == ':') pos++; scan_ulong(num.s+pos,&digwhen); /* last reg dig */ } else { prevmax = 0L; cumsize = 0L; digwhen = 0L; } mno = prevmax + 1L; if(!max || mno > max) /* if a digest-list is "sending" the request, */ /* don't make noise: errors go to postmaster!*/ strerr_die2x(goodexit,FATAL,MSG(ERR_EMPTY_DIGEST)); szmsgnum[fmt_ulong(szmsgnum,mno)] = '\0'; set_cpnum(szmsgnum); /* for copy */ /* prepare subject to get entropy for tagmsg*/ if (!stralloc_cats(&subject," Digest ")) die_nomem(); if (!stralloc_catb(&subject,date,date822fmt(date,&dt)-1)) die_nomem(); /* skip trailing in date '\n' */ if (!stralloc_cats(&subject," Issue ")) die_nomem(); if (getconf_line(&num,"digissue",0)) { if(!stralloc_0(&num)) die_nomem(); scan_ulong(num.s,&issue); issue++; } else { issue = 1; } if (!stralloc_catb(&subject,strnum,fmt_ulong(strnum,issue))) die_nomem(); /* use the subject as entropy */ if (!stralloc_copy(&line,&subject)) die_nomem(); if (!stralloc_0(&line)) die_nomem(); if (!stralloc_ready(&seed,HASHLEN+1)) die_nomem(); seed.len = HASHLEN + 1; seed.s[HASHLEN] = '\0'; makehash(line.s,line.len,seed.s); if (chunk) { /* only if slaves are used */ qmail_puts(&qq,"Ezauth: "); qmail_put(&qq,seed.s,HASHLEN); qmail_puts(&qq,"\n"); } doheaders(); qmail_puts(&qq,"To: "); if (!quote("ed,&listname)) die_nomem(); qmail_put(&qq,quoted.s,quoted.len); qmail_puts(&qq,"@"); qmail_put(&qq,outhost.s,outhost.len); qmail_puts(&qq,"\n"); if (flagindexed && (outformat != NATIVE)) idx_mkthreads(&msgtable,&subtable,&authtable,&datetable, mno,to,max,flaglocked); else idx_mklist(&msgtable,&subtable,&authtable,mno,to); digest(msgtable,subtable,authtable,mno,to,&subject,AC_DIGEST,outformat); write_ulong(issue,0L,0L,"digissue","digissuen"); write_ulong(max,cumsizen, (unsigned long) when,"dignum","dignumn"); break; case AC_GET: /* -get[-|\.][[num].num2] copies archive num-num2. num & num2 are adjusted */ /* to be > 0 and <= last message, to num2 >= num and to num2-num <= MAXGET. */ zapnonsub(ACTION_GET); /* restrict to subs if requested */ tosender(); /* for rfc1153 */ if (!stralloc_cats(&subject," Digest of: ")) die_nomem(); if (!stralloc_cats(&subject,action)) die_nomem(); to = 0; pos = str_len(ACTION_GET); if (!case_starts(action,ACTION_GET)) pos = str_len(ALT_GET); if (FORMATS[str_chr(FORMATS,action[pos])]) { outformat = action[pos]; ++pos; } /* optional - or . after '-get' */ if (action[pos] == '-' || action[pos] == '.') pos++; get_num(); /* max = last successful message */ /* accept any separator. It may be */ /* the terminal '\n', but then */ /* scan will = 0 on the \0 so should*/ /* be safe */ if (!max) strerr_die2x(100,FATAL,MSG(ERR_EMPTY_LIST)); szmsgnum[fmt_ulong(szmsgnum,max)] = '\0'; set_cpnum(szmsgnum); /* for copy this is the latest message arch'd*/ doheaders(); if(action[pos += scan_ulong(action + pos,&u)]) scan_ulong(action + pos + 1, &to); if (u == 0 && to == 0) { /* default: messages since last */ /* digest, or last MAXGET if too many */ to= max; u = dignum(); if (u == 0) { /* no digest => last up to HISTGET msgs */ to = max; if (max > HISTGET) u = max - HISTGET; else u = 1; } if (to - u >= MAXGET) u = to - MAXGET + 1; /* max MAXGET */ } else if (u > max) { if (to) { /* -get.999999_x returns 30 and msg since last*/ to = max; /* digest 30*/ u = dignum(); if (u > HISTGET) u -= HISTGET; else u = 1; if (to - u >= MAXGET) u = to - MAXGET + 1; } else u = max; } if (u == 0) u = 1; /* -get.5 => 1-5 */ if (to < u) to = u; /* -get23_2 => 23 */ if (to >= u + MAXGET) to = u + MAXGET - 1; /* no more than MAXGET at a time */ if (to > max) to = max; if (flagindexed && (outformat != NATIVE)) /* fake out threading */ idx_mkthreads(&msgtable,&subtable,&authtable,&datetable, u,to,max,0); else idx_mklist(&msgtable,&subtable,&authtable,u,to); digest(msgtable,subtable,authtable,u,to,&subject,AC_GET,outformat); break; case AC_INDEX: /* -index[f][#|-|\.][[num][.num2] Show subject index for messages num-num2 in*/ /* sets of 100. */ /* Default last 2 sets. num and num2 are made reasonable as for get. num2 is */ /* limited to num+MAXINDEX to limit the amount of data sent. */ if (!flagindexed) strerr_die2x(100,FATAL,MSG(ERR_NOT_INDEXED)); zapnonsub(ACTION_INDEX); /* restrict to subs if requested */ to = 0; pos = str_len(ACTION_INDEX); if (!case_starts(action,ACTION_INDEX)) pos = str_len(ALT_INDEX); if (FORMATS[str_chr(FORMATS,action[pos])]) { outformat = action[pos]; /* ignored, but be nice ... */ ++pos; } get_num(); /* max = last successful message */ if (!max) strerr_die2x(100,FATAL,MSG(ERR_EMPTY_LIST)); szmsgnum[fmt_ulong(szmsgnum,max)] = '\0'; set_cpnum(szmsgnum); /* for copy this is the latest message arch'd*/ doheaders(); tosender(); if (!stralloc_cats(&subject," Result of: ")) die_nomem(); if (!stralloc_cats(&subject,action)) die_nomem(); presub(1,1,&subject,AC_INDEX,outformat); if (action[pos] == '-' || action[pos] == '.') pos++; if(action[pos += scan_ulong(action + pos,&u)]) scan_ulong(action + pos + 1, &to); if (u == 0 && to == 0) { to = max; u = max - 100; } if (u <= 0) u = 1; if (u > max) u = max; if (to < u) to = u; if (to > u + MAXINDEX) to = u+MAXINDEX; /* max MAXINDEX index files */ if (to > max) to = max; u /= 100; to /= 100; while (u <= to) { if (!stralloc_copys(&fn,"archive/")) die_nomem(); if (!stralloc_catb(&fn,strnum,fmt_ulong(strnum,u))) die_nomem(); if (!stralloc_cats(&fn,"/index")) die_nomem(); if (!stralloc_0(&fn)) die_nomem(); if (u == max/100) /* lock if last index file in archive */ lockup(); fd = open_read(fn.s); if (fd == -1) if (errno != error_noent) strerr_die2sys(111,FATAL,MSG1(ERR_OPEN,fn.s)); else code_qputs(MSG(TXT_NOINDEX)); else { substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf)); for (;;) { if (getln(&sstext,&line,&match,'\n') == -1) strerr_die2sys(111,FATAL,MSG1(ERR_READ,fn.s)); if (match) { if (line.s[0] != '\t') { /* subject line */ pos = byte_chr(line.s,line.len,' '); pos1 = 0; if (pos && pos != line.len && line.s[pos - 1] == ':') pos1 = pos + HASHLEN + 1; /* after hash */ if (pos1 >= line.len) { /* bad! */ pos = 0; pos1 = 0; /* output as is */ } if (!stralloc_copyb(&line2,line.s,pos)) die_nomem(); if (!stralloc_catb(&line2,line.s+pos1,line.len-pos1)) die_nomem(); } else { pos = byte_chr(line.s,line.len,';'); if (pos + HASHLEN + 1 < line.len && pos > 15 && line.s[pos + 1] != ' ') { if (!stralloc_copyb(&line2,line.s,pos - 15)) die_nomem(); pos++; if (!stralloc_catb(&line2,line.s + pos + HASHLEN, line.len - pos - HASHLEN)) die_nomem(); } else /* old format - no author hash */ if (!stralloc_copyb(&line2,line.s,line.len)) die_nomem(); } code_qput(line2.s,line2.len); } else break; } close(fd); } if (u == max/100) /* unlock if last index in archive file */ unlock(); u++; } normal_bottom(outformat); postmsg(outformat); break; case AC_THREAD: /* -thread[f][-|.]num returns messages with subject matching message */ /* 'num' in the subject index. If 'num' is not in[1..last_message] an error */ /* message is returned. */ if (!flagindexed) strerr_die2x(100,FATAL,MSG(ERR_NOT_INDEXED)); zapnonsub(ACTION_THREAD); /* restrict to subs if requested*/ get_num(); /* max = last successful message */ if (!max) strerr_die2x(100,FATAL,MSG(ERR_EMPTY_LIST)); szmsgnum[fmt_ulong(szmsgnum,max)] = '\0'; set_cpnum(szmsgnum); /* for copy this is the latest message arch'd*/ doheaders(); tosender(); /* for rfc1153 */ if (!stralloc_cats(&subject," Digest of: ")) die_nomem(); if (!stralloc_cats(&subject,action)) die_nomem(); to = 0; pos = str_len(ACTION_THREAD); if (!case_starts(action,ACTION_THREAD)) pos = str_len(ALT_THREAD); if (FORMATS[str_chr(FORMATS,action[pos])]) { outformat = action[pos]; ++pos; } if (action[pos] == '-' || action[pos] == '.') pos++; if(action[pos += scan_ulong(action + pos,&u)]) scan_ulong(action + pos + 1, &to); if(u == 0 || u > max) { hdr_add2("Subject: ",subject.s,subject.len); qmail_puts(&qq,"\n"); copy(&qq,"text/get-bad",flagcd); } else { /* limit range to at most u-THREAD_BEFORE to u+THREAD_AFTER */ if (u > THREAD_BEFORE) from = u-THREAD_BEFORE; else from = 1L; if (u + THREAD_AFTER > max) { idx_mkthread(&msgtable,&subtable,&authtable,from,max,u,max,0); digest(msgtable,subtable,authtable,from,max,&subject, AC_THREAD,outformat); } else { idx_mkthread(&msgtable,&subtable,&authtable, from,u+THREAD_AFTER,u,max,0); digest(msgtable,subtable,authtable,from,u+THREAD_AFTER, &subject,AC_THREAD,outformat); } } break; default: /* This happens if the initial check at the beginning of 'main' */ /* matches something that isn't matched here. Conversely, just */ /* adding an action here is not enough - it has to be added to the */ /* initial check as well. */ strerr_die2x(100,FATAL, "Program error: I'm supposed to deal with this but I didn't"); } if (!stralloc_copy(&line,&outlocal)) die_nomem(); if (act == AC_DIGEST) { if (chunk) { if (!stralloc_cats(&line,"-return-g-")) die_nomem(); } else if (!stralloc_cats(&line,"-return-")) die_nomem(); strnum[fmt_ulong(strnum,mno)] = '\0'; if (!stralloc_cats(&line,strnum)) die_nomem(); if (!stralloc_cats(&line,"-@")) die_nomem(); if (!stralloc_cat(&line,&outhost)) die_nomem(); if (!stralloc_cats(&line,"-@[]")) die_nomem(); } else { 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); if (act == AC_DIGEST) { /* Do recipients */ tagmsg(mno,seed.s,"d",hashout,qq.msgbytes,chunk); if (chunk) { if (!stralloc_copys(&line,"T")) die_nomem(); if (!stralloc_cat(&line,&outlocal)) die_nomem(); if (!stralloc_cats(&line,"-s-d-")) die_nomem(); if (!stralloc_catb(&line,hashout,COOKIE)) die_nomem(); if (!stralloc_cats(&line,"-")) die_nomem(); if (!stralloc_cats(&line,strnum)) die_nomem(); if (!stralloc_cats(&line,"-")) die_nomem(); if (!stralloc_copys(&line2,"@")) die_nomem(); if (!stralloc_cat(&line2,&outhost)) die_nomem(); if (!stralloc_0(&line2)) die_nomem(); j = 0; for (i = 0; i <= 52; i += chunk) { /* To slaves */ qmail_put(&qq,line.s,line.len); schar[0] = '0' + i / 10; schar[1] = '0' + (i % 10); qmail_put(&qq,schar,3); j += (chunk - 1); if (j > 52) j = 52; schar[0] = '0' + j / 10; schar[1] = '0' + (j % 10); qmail_put(&qq,schar,2); qmail_put(&qq,line2.s,line2.len); } } else subs = putsubs(workdir,0L,52L,&subto); } else { /* if local is set, sender is checked */ if (ismod) qmail_to(&qq,mod.s); else qmail_to(&qq,sender); } if (*(err = qmail_close(&qq)) == '\0') { /* Done. Skip rest. */ if (act == AC_DIGEST) { if (chunk) (void) logmsg(mno,0L,0L,2); else (void) logmsg(mno,0L,subs,4); } closesub(); /* close db connection */ unlock(); /* NOP if nothing locked */ strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0; strerr_die2x(goodexit,"ezmlm-get: info: qp ",strnum); } else { /* failed. Reset last msg & issue for digest */ if(act == AC_DIGEST) { issue--; write_ulong(issue,0L,0L,"digissue","digissuen"); write_ulong(prevmax,cumsize,(unsigned long) digwhen,"dignum","dignumn"); } unlock(); /* NOP if nothing locked */ strerr_die4x(111,FATAL,MSG(ERR_TMP_QMAIL_QUEUE),": ",err + 1); } }
void copymsg(int fd,char format) /* Copy archive message "msg" itself from open file handle fd, in "format" */ { int match; int flaginheader; int flagskipblanks; int flaggoodfield; switch(format) { case VIRGIN: case NATIVE: substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf)); for (;;) { if (getln(&sstext,&line,&match,'\n') == -1) strerr_die2sys(111,FATAL,MSG1(ERR_READ,line.s)); if (match) { qmail_put(&qq,line.s,line.len); msgsize += line.len; } else break; } break; case MIME: case MIXED: flaginheader = 1; flaggoodfield = 0; substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf)); for (;;) { if (getln(&sstext,&line,&match,'\n') == -1) strerr_die2sys(111,FATAL,MSG1(ERR_READ,line.s)); if (match) { if (flaginheader) { if (line.len == 1) { flaginheader = 0; flaggoodfield = 1; } else if (line.s[0] != ' ' && line.s[0] != '\t') { flaggoodfield = 0; if (constmap(&digheadersmap,line.s, byte_chr(line.s,line.len,':'))) flaggoodfield = 1; } if (flaggoodfield) { qmail_put(&qq,line.s,line.len); /* header */ msgsize += line.len; } } else { qmail_put(&qq,line.s,line.len); /* body */ msgsize += line.len; } } else break; } break; case RFC1153: /* Not worth optimizing. Rarely used */ flaginheader = 1; flagskipblanks = 1; /* must skip terminal blanks acc to rfc1153 */ archtype = ' '; /* rfc1153 requires ordered headers */ if (!stralloc_copys(&archblanklines,"")) die_nomem(); substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf)); for (;;) { if (getln(&sstext,&line,&match,'\n') == -1) strerr_die2sys(111,FATAL,MSG1(ERR_READ,line.s)); if (match) { if (flaginheader) { if (line.len == 1) { flaginheader = 0; if (archdate.len) { qmail_put(&qq,archdate.s,archdate.len); archdate.len = 0; msgsize += archdate.len; } if (archto.len) { qmail_put(&qq,archto.s,archto.len); msgsize += archto.len; archto.len = 0; } if (archfrom.len) { qmail_put(&qq,archfrom.s,archfrom.len); msgsize += archfrom.len; archfrom.len = 0; } if (archcc.len) { qmail_put(&qq,archcc.s,archcc.len); msgsize += archcc.len; archcc.len = 0; } if (archsubject.len) { qmail_put(&qq,archsubject.s,archsubject.len); msgsize += archsubject.len; archsubject.len = 0; } if (archmessageid.len) { qmail_put(&qq,archmessageid.s,archmessageid.len); msgsize += archmessageid.len; archmessageid.len = 0; } if (archkeywords.len) { qmail_put(&qq,archkeywords.s,archkeywords.len); msgsize += archkeywords.len; archkeywords.len = 0; } qmail_puts(&qq,"\n"); } else if (line.s[0] == ' ' || line.s[0] == '\t') { switch (archtype) { /* continuation lines */ case ' ': break; case 'D': if (!stralloc_cat(&archdate,&line)) die_nomem(); break; case 'F': if (!stralloc_cat(&archfrom,&line)) die_nomem(); break; case 'T': if (!stralloc_cat(&archto,&line)) die_nomem(); break; case 'C': if (!stralloc_cat(&archcc,&line)) die_nomem(); break; case 'S': if (!stralloc_cat(&archsubject,&line)) die_nomem(); break; case 'M': if (!stralloc_cat(&archmessageid,&line)) die_nomem(); break; case 'K': if (!stralloc_cat(&archkeywords,&line)) die_nomem(); break; default: strerr_die2x(111,FATAL, "Program error: Bad archive header type"); } } else { archtype = ' '; if (case_startb(line.s,line.len,"cc:")) { archtype='C'; if (!stralloc_copy(&archcc,&line)) die_nomem(); } else if (case_startb(line.s,line.len,"date:")) { archtype='D'; if (!stralloc_copy(&archdate,&line)) die_nomem(); } else if (case_startb(line.s,line.len,"from:")) { archtype='F'; if (!stralloc_copy(&archfrom,&line)) die_nomem(); } else if (case_startb(line.s,line.len,"keywords:")) { archtype='K'; if (!stralloc_copy(&archkeywords,&line)) die_nomem(); } else if (case_startb(line.s,line.len,"message-id:")) { archtype='M'; if (!stralloc_copy(&archmessageid,&line)) die_nomem(); } else if (case_startb(line.s,line.len,"subject:")) { archtype='S'; if (!stralloc_copy(&archsubject,&line)) die_nomem(); } else if (case_startb(line.s,line.len,"to:")) { archtype='T'; if (!stralloc_copy(&archto,&line)) die_nomem(); } } } else if (line.len == 1) { if (!flagskipblanks) if (!stralloc_copys(&archblanklines,"\n")) die_nomem(); } else { if (archblanklines.len) { qmail_put(&qq,archblanklines.s,archblanklines.len); archblanklines.len = 0; } flagskipblanks = 0; qmail_put(&qq,line.s,line.len); msgsize += line.len; } } else break; } break; default: strerr_die2x(100,FATAL,"Program error: bad format in copymsg()"); } }
void main(int argc,char **argv) { char *sender; char *def; char *local; char *action; int flaginheader; int flagcomment; int flaggoodfield; int flagdone; int fd, fdlock; int match; const char *err; char encin = '\0'; unsigned int start,confnum; unsigned int pos,i; int child; int opt; char *cp,*cpnext,*cpfirst,*cplast,*cpafter; (void) umask(022); sig_pipeignore(); when = now(); if (!stralloc_copys(&sendopt,"-")) die_nomem(); opt = getconfopt(argc,argv,options,1,&dir); sender = get_sender(); if (!sender) strerr_die2x(100,FATAL,MSG(ERR_NOSENDER)); local = env_get("LOCAL"); if (!local) strerr_die2x(100,FATAL,MSG(ERR_NOLOCAL)); def = env_get("DEFAULT"); if (!def) strerr_die2x(100,FATAL,MSG(ERR_NODEFAULT)); if (!*sender) strerr_die2x(100,FATAL,MSG(ERR_BOUNCE)); if (!sender[str_chr(sender,'@')]) strerr_die2x(100,FATAL,MSG(ERR_ANONYMOUS)); if (str_equal(sender,"#@[]")) strerr_die2x(100,FATAL,MSG(ERR_BOUNCE)); /* local should be >= def, but who knows ... */ cp = local + str_len(local) - str_len(def) - 2; if (cp < local) die_badformat(); action = local + byte_rchr(local,cp - local,'-'); if (action == cp) die_badformat(); action++; if (!action[0]) die_badformat(); if (!str_start(action,ACTION_ACCEPT) && !str_start(action,ACTION_REJECT)) die_badformat(); start = str_chr(action,'-'); if (!action[start]) die_badformat(); confnum = 1 + start + str_chr(action + start + 1,'.'); if (!action[confnum]) die_badformat(); confnum += 1 + str_chr(action + confnum + 1,'.'); if (!action[confnum]) die_badformat(); if (!stralloc_copyb(&fnbase,action+start+1,confnum-start-1)) die_nomem(); if (!stralloc_0(&fnbase)) die_nomem(); cookie(hash,key.s,key.len,fnbase.s,"","a"); if (byte_diff(hash,COOKIE,action+confnum+1)) die_badformat(); fdlock = lockfile("mod/lock"); switch(checkfile(fnbase.s)) { case 0: strerr_die2x(100,FATAL,MSG(ERR_MOD_TIMEOUT)); case -1: /* only error if new request != action taken */ if (str_start(action,ACTION_ACCEPT)) strerr_die2x(0,INFO,MSG(ERR_MOD_ACCEPTED)); else strerr_die2x(100,FATAL,MSG(ERR_MOD_ACCEPTED)); case -2: if (str_start(action,ACTION_REJECT)) strerr_die2x(0,INFO,MSG(ERR_MOD_REJECTED)); else strerr_die2x(100,FATAL,MSG(ERR_MOD_REJECTED)); default: break; } /* Here, we have an existing filename in fnbase with the complete path */ /* from the current dir in fnmsg. */ if (str_start(action,ACTION_REJECT)) { if (qmail_open(&qq, (stralloc *) 0) == -1) strerr_die2sys(111,FATAL,MSG(ERR_QMAIL_QUEUE)); /* Build recipient from msg return-path */ fd = open_read(fnmsg.s); if (fd == -1) { if (errno != error_noent) strerr_die2sys(111,FATAL,MSG1(ERR_OPEN,fnmsg.s)); else strerr_die2x(100,FATAL,MSG(ERR_MOD_TIMEOUT)); } substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf)); if (getln(&sstext,&line,&match,'\n') == -1 || !match) strerr_die2sys(111,FATAL,MSG(ERR_READ_INPUT)); maketo(); /* extract SENDER from return-path */ /* Build message */ hdr_add2s("Mailing-List: ",MSG(TXT_MAILING_LIST)); if (listid.len > 0) hdr_add2("List-ID: ",listid.s,listid.len); hdr_datemsgid(when); hdr_from("-owner"); if (replyto) hdr_add2s("Reply-To: ",replyto); hdr_add2s("To: ",to.s); hdr_subject(MSG(SUB_RETURNED_POST)); if (flagmime) { hdr_mime(CTYPE_MULTIPART); hdr_boundary(0); hdr_ctype(CTYPE_TEXT); hdr_transferenc(); } copy(&qq,"text/top",flagcd); copy(&qq,"text/mod-reject",flagcd); flagcomment = 0; flaginheader = 1; if (!stralloc_copys(&text,"")) die_nomem(); if (!stralloc_ready(&text,1024)) die_nomem(); for (;;) { /* copy moderator's rejection comment */ if (getln(subfdin,&line,&match,'\n') == -1) strerr_die2sys(111,FATAL,MSG(ERR_READ_INPUT)); if (!match) break; if (flaginheader) { if (case_startb(line.s,line.len,"Content-Transfer-Encoding:")) { pos = 26; while (line.s[pos] == ' ' || line.s[pos] == '\t') ++pos; if (case_startb(line.s+pos,line.len-pos,"base64")) encin = 'B'; else if (case_startb(line.s+pos,line.len-pos,"quoted-printable")) encin = 'Q'; } if (line.len == 1) flaginheader = 0; } else if (!stralloc_cat(&text,&line)) die_nomem(); } /* got body */ if (encin) { if (encin == 'B') decodeB(text.s,text.len,&line); else decodeQ(text.s,text.len,&line); if (!stralloc_copy(&text,&line)) die_nomem(); } cp = text.s; cpafter = text.s + text.len; if (!stralloc_copys(&line,"\n>>>>> -------------------- >>>>>\n")) die_nomem(); flaggoodfield = 0; flagdone = 0; while ((cpnext = cp + byte_chr(cp,cpafter-cp,'\n')) != cpafter) { i = byte_chr(cp,cpnext-cp,'%'); if (i <= 5 && cpnext-cp-i >= 3) { /* max 5 "quote characters" and space for %%% */ if (cp[i+1] == '%' && cp[i+2] == '%') { if (!flaggoodfield) { /* Start tag */ if (!stralloc_copyb("ed,cp,i)) die_nomem(); /* quote chars*/ flaggoodfield = 1; cp = cpnext + 1; cpfirst = cp; continue; } else { /* end tag */ if (flagdone) /* 0 no comment lines, 1 comment line */ flagdone = 2; /* 2 at least 1 comment line & end tag */ break; } } } if (flaggoodfield) { cplast = cpnext - 1; if (*cplast == '\r') /* CRLF -> '\n' for base64 encoding */ *cplast = '\n'; else ++cplast; /* NUL is now ok, so the test for it was removed */ flagdone = 1; i = cplast - cp + 1; if (quoted.len && quoted.len <= i && !str_diffn(cp,quoted.s,quoted.len)) { /* quote chars */ if (!stralloc_catb(&line,cp+quoted.len,i-quoted.len)) die_nomem(); } else if (!stralloc_catb(&line,cp,i)) die_nomem(); /* no quote chars */ } cp = cpnext + 1; } if (flagdone == 2) { if (!stralloc_cats(&line,"<<<<< -------------------- <<<<<\n")) die_nomem(); code_qput(line.s,line.len); } if (flagcd == 'B') { encodeB("",0,&line,2); qmail_put(&qq,line.s,line.len); } if (flagmime) { hdr_boundary(0); hdr_ctype(CTYPE_MESSAGE); } qmail_puts(&qq,"\n"); if (seek_begin(fd) == -1) strerr_die2sys(111,FATAL,MSG1(ERR_SEEK,fnmsg.s)); substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf)); if (qmail_copy(&qq,&sstext,-1) != 0) strerr_die2sys(111,FATAL,MSG1(ERR_READ,fnmsg.s)); close(fd); if (flagmime) hdr_boundary(1); 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); if (to.len) qmail_to(&qq,to.s); if (!stralloc_copys(&fnnew,"mod/rejected/")) die_nomem(); if (!stralloc_cats(&fnnew,fnbase.s)) die_nomem(); if (!stralloc_0(&fnnew)) die_nomem(); /* this is strictly to track what happended to a message to give informative */ /* messages to the 2nd-nth moderator that acts on the same message. Since */ /* this isn't vital we ignore errors. Also, it is no big ideal if unlinking */ /* the old file fails. In the worst case it gets acted on again. If we issue */ /* a temp error the reject will be redone, which is slightly worse. */ if (*(err = qmail_close(&qq)) == '\0') { fd = open_trunc(fnnew.s); if (fd != -1) close(fd); unlink(fnmsg.s); strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0; strerr_die2x(0,"ezmlm-moderate: info: qp ",strnum); } else strerr_die4x(111,FATAL,MSG(ERR_TMP_QMAIL_QUEUE),": ",err + 1); } else if (str_start(action,ACTION_ACCEPT)) { fd = open_read(fnmsg.s); if (fd == -1) { if (errno !=error_noent) strerr_die2sys(111,FATAL,MSG1(ERR_OPEN,fnmsg.s)); else /* shouldn't happen since we've got lock */ strerr_die3x(100,FATAL,fnmsg.s,MSG(ERR_MOD_TIMEOUT)); } substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf)); /* read "Return-Path:" line */ if (getln(&sstext,&line,&match,'\n') == -1 || !match) strerr_die2sys(111,FATAL,MSG(ERR_READ_INPUT)); maketo(); /* extract SENDER to "to" */ env_put2("SENDER",to.s); /* set SENDER */ if (seek_begin(fd) == -1) /* rewind, since we read an entire buffer */ strerr_die2sys(111,FATAL,MSG1(ERR_SEEK,fnmsg.s)); if ((child = wrap_fork()) == 0) { close(0); dup(fd); /* make fnmsg.s stdin */ if (argc > opt + 1) wrap_execvp((const char **)argv + opt); else if (argc > opt) wrap_execsh(argv[opt]); else wrap_execbin("/ezmlm-send", &sendopt, dir); } /* parent */ close(fd); wrap_exitcode(child); if (!stralloc_copys(&fnnew,"mod/accepted/")) die_nomem(); if (!stralloc_cats(&fnnew,fnbase.s)) die_nomem(); if (!stralloc_0(&fnnew)) die_nomem(); /* ignore errors */ fd = open_trunc(fnnew.s); if (fd != -1) close(fd); unlink(fnmsg.s); _exit(0); } }
void sendnotice(const char *d) /* sends file pointed to by d to the address in the return-path of the */ /* message. */ { unsigned int x,y; const char *err; if (qmail_open(&qq, (stralloc *) 0) == -1) strerr_die2sys(111,FATAL,MSG(ERR_QMAIL_QUEUE)); fd = open_read(d); if (fd == -1) strerr_die2sys(111,FATAL,MSG1(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(); hdr_add2s("Mailing-List: ",MSG(TXT_MAILING_LIST)); if (listid.len > 0) hdr_add2("List-ID: ",listid.s,listid.len); hdr_datemsgid(when+msgnum++); hdr_from("-help"); hdr_subject(MSG(SUB_RETURNED_POST)); hdr_add2s("To: ",to.s); if (flagmime) { hdr_mime(CTYPE_MULTIPART); hdr_boundary(0); hdr_ctype(CTYPE_TEXT); hdr_transferenc(); } else qmail_puts(&qq,"\n\n"); copy(&qq,"text/top",flagcd); copy(&qq,"text/mod-timeout",flagcd); if (flagcd == 'B') { encodeB("",0,&line,2); qmail_put(&qq,line.s,line.len); } if (flagmime) { hdr_boundary(0); hdr_ctype(CTYPE_MESSAGE); qmail_puts(&qq,"\n"); } if (seek_begin(fd) == -1) strerr_die2sys(111,FATAL,MSG1(ERR_SEEK,d)); substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf)); if (qmail_copy(&qq,&sstext,-1) != 0) die_read(); close (fd); if (flagmime) hdr_boundary(1); 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_die4x(111,FATAL,MSG(ERR_TMP_QMAIL_QUEUE),": ", err + 1); strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0; strerr_warn2("ezmlm-clean: info: qp ",strnum,0); }
int main(int argc,char **argv) { unsigned long maxmsgsize = 0L; unsigned long minmsgsize = 0L; unsigned long msgsize = 0L; char linetype = ' '; char *cp, *cpstart, *cpafter; const char *dir; const char *err; const char *sender; unsigned int len; int match; getconfopt(argc,argv,options,-1,&dir); if (dir) { startup(dir); flagparsemime = 1; /* only if dir do we have mimeremove/reject */ if (getconf_line(&line,"msgsize",0)) { if (!stralloc_0(&line)) die_nomem(); len = scan_ulong(line.s,&maxmsgsize); if (line.s[len] == ':') scan_ulong(line.s+len+1,&minmsgsize); } if (flagforward) { if (!stralloc_copys(&mydtline,"Delivered-To: command forwarder for ")) die_nomem(); if (!stralloc_catb(&mydtline,outlocal.s,outlocal.len)) die_nomem(); if (!stralloc_cats(&mydtline,"@")) die_nomem(); if (!stralloc_catb(&mydtline,outhost.s,outhost.len)) die_nomem(); if (!stralloc_cats(&mydtline,"\n")) die_nomem(); } } else { flagtook = 1; /* if no "dir" we can't get outlocal/outhost */ flagforward = 0; /* nor forward requests */ } sender = get_sender(); if (!sender) die_sender(); if (!*sender) strerr_die2x(100,FATAL,MSG(ERR_BOUNCE)); if (flagparsemime) { /* set up MIME parsing */ if (getconf(&mimeremove,"mimekeep",0)) mimeremoveflag = 1; else getconf(&mimeremove,"mimeremove",0); constmap_init(&mimeremovemap,mimeremove.s,mimeremove.len,0); getconf(&mimereject,"mimereject",0); constmap_init(&mimerejectmap,mimereject.s,mimereject.len,0); } if (flagheaderreject && dir) { getconf(&headerreject,"headerreject",0); constmap_init(&headerrejectmap,headerreject.s,headerreject.len,0); } for (;;) { if (getln(&ssin,&line,&match,'\n') == -1) strerr_die2sys(111,FATAL,MSG(ERR_READ_INPUT)); if (!match) break; if (flagheaderreject && dir) if (constmap(&headerrejectmap,line.s,byte_chr(line.s,line.len,':'))) strerr_die2x(100,FATAL,MSG(ERR_MAILING_LIST)); if (line.len == 1) break; cp = line.s; len = line.len; if ((*cp == ' ' || *cp == '\t')) { switch(linetype) { case 'T': if (!stralloc_catb(&to,cp,len-1)) die_nomem(); break; case 'S': if (!stralloc_catb(&subject,cp,len-1)) die_nomem(); break; case 'C': if (!stralloc_catb(&content,cp,len-1)) die_nomem(); break; case 'P': if (!stralloc_catb(&precd,cp,len-1)) die_nomem(); break; default: break; } } else { if (!flagtook && (case_startb(cp,len,"to:") || case_startb(cp,len,"cc:"))) { linetype = 'T'; /* cat so that To/Cc don't overwrite */ if (!stralloc_catb(&to,line.s + 3,line.len - 4)) die_nomem(); } else if ((flagneedsubject || flagrejectcommands) && case_startb(cp,len,"subject:")) { if (!stralloc_copyb(&subject,cp+8,len-9)) die_nomem(); linetype = 'S'; } else if (case_startb(cp,len,"content-type:")) { if (!stralloc_copyb(&content,cp+13,len-14)) die_nomem(); linetype = 'C'; } else if (case_startb(cp,len,"precedence:")) { if (!stralloc_copyb(&precd,cp+11,len-12)) die_nomem(); linetype = 'P'; } else { if (flagforward && line.len == mydtline.len) { if (!byte_diff(line.s,line.len,mydtline.s)) strerr_die2x(100,FATAL,MSG(ERR_LOOPING)); } linetype = ' '; } } } if (precd.len >= 4 && (!case_diffb(precd.s + precd.len - 4,4,"junk") || !case_diffb(precd.s + precd.len - 4,4,"bulk"))) strerr_die1x(99,MSG(ERR_JUNK)); /* ignore precedence junk/bulk */ cp = subject.s; len = subject.len; while (len && (cp[len-1] == ' ' || cp[len-1] == '\t')) --len; while (len && ((*cp == ' ') || (*cp == '\t'))) { ++cp; --len; } flaghavesubject = 1; if (flagbody) if ((len > 9 && case_starts(cp,"subscribe")) || (len > 11 && case_starts(cp,"unsubscribe"))) flaghavecommand = 1; switch(len) { case 0: flaghavesubject = 0; break; case 4: if (!case_diffb("help",4,cp)) flaghavecommand = 1; break; case 6: /* Why can't they just leave an empty subject empty? */ if (!case_diffb("(null)",6,cp)) flaghavesubject = 0; else if (!case_diffb("(none)",6,cp)) flaghavesubject = 0; else if (!case_diffb("remove",6,cp)) flaghavecommand = 1; break; case 9: if (!case_diffb("subscribe",9,cp)) flaghavecommand = 1; break; case 11: if (!case_diffb("unsubscribe",11,cp)) flaghavecommand = 1; break; case 12: if (!case_diffb("(no subject)",12,cp)) flaghavesubject = 0; break; default: break; } if (!flagtook && !getto(&to)) strerr_die2x(exitquiet,FATAL,MSG(ERR_NO_ADDRESS)); if (flagneedsubject && !flaghavesubject) strerr_die2x(100,FATAL,MSG(ERR_NO_SUBJECT)); if (flagrejectcommands && flaghavecommand) { if (flagforward) { /* flagforward => forward */ if (qmail_open(&qq) == -1) /* open queue */ strerr_die2sys(111,FATAL,MSG(ERR_QMAIL_QUEUE)); qmail_put(&qq,mydtline.s,mydtline.len); if (seek_begin(0) == -1) strerr_die2sys(111,FATAL,MSG(ERR_SEEK_INPUT)); if (qmail_copy(&qq,&ssin2,copylines) != 0) strerr_die2sys(111,FATAL,MSG(ERR_READ_INPUT)); if (!stralloc_copy(&to,&outlocal)) die_nomem(); if (!stralloc_cats(&to,"-request@")) die_nomem(); if (!stralloc_cat(&to,&outhost)) die_nomem(); if (!stralloc_0(&to)) die_nomem(); qmail_from(&qq,sender); qmail_to(&qq,to.s); if (*(err = qmail_close(&qq)) == '\0') { strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0; strerr_die2x(99,"ezmlm-request: info: forward qp ",strnum); } else strerr_die4x(111,FATAL,MSG(ERR_TMP_QMAIL_QUEUE),": ",err + 1); } else strerr_die2x(100,FATAL,MSG(ERR_SUBCOMMAND)); } if (content.len) { /* MIME header */ cp = content.s; len = content.len; while (len && (*cp == ' ' || *cp == '\t')) { ++cp; --len; } cpstart = cp; if (*cp == '"') { /* might be commented */ ++cp; cpstart = cp; while (len && *cp != '"') { ++cp; --len; } } else { while (len && *cp != ' ' && *cp != '\t' && *cp != ';') { ++cp; --len; } } if (flagparsemime) if ((!!constmap(&mimeremovemap,cpstart,cp-cpstart) ^ mimeremoveflag) || constmap(&mimerejectmap,cpstart,cp-cpstart)) { *(cp) = (char) 0; strerr_die2x(100,FATAL,MSG1(ERR_BAD_TYPE,cpstart)); } cpafter = content.s+content.len; while((cp += byte_chr(cp,cpafter-cp,';')) != cpafter) { ++cp; while (cp < cpafter && (*cp == ' ' || *cp == '\t')) ++cp; if (case_startb(cp,cpafter - cp,"boundary=")) { cp += 9; /* after boundary= */ if (cp < cpafter && *cp == '"') { ++cp; cpstart = cp; while (cp < cpafter && *cp != '"') ++cp; if (cp == cpafter) strerr_die1x(100,MSG(ERR_MIME_QUOTE)); } else { cpstart = cp; while (cp < cpafter && *cp != ';' && *cp != ' ' && *cp != '\t') ++cp; } if (!stralloc_copys(&boundary,"--")) die_nomem(); if (!stralloc_catb(&boundary,cpstart,cp-cpstart)) die_nomem(); break; } } /* got boundary, now parse for parts */ } for (;;) { if (getln(&ssin,&line,&match,'\n') == -1) strerr_die2sys(111,FATAL,MSG(ERR_READ_INPUT)); if (!match) break; if (line.len == 1) { flagcheck = 0; continue; /* Doesn't do continuation lines. _very_ unusual, and worst */ /* case one slips through that shouldn't have */ } else if (flagcheck && case_startb(line.s,line.len,"content-type:")) { cp = line.s + 13; len = line.len - 14; /* zap '\n' */ while (*cp == ' ' || *cp == '\t') { ++cp; --len; } cpstart = cp; if (*cp == '"') { /* quoted */ ++cp; cpstart = cp; while (len && *cp != '"') { ++cp; --len; } } else { /* not quoted */ while (len && *cp != ' ' && *cp != '\t' && *cp != ';') { ++cp; --len; } } if (flagparsemime && constmap(&mimerejectmap,cpstart,cp-cpstart)) { *cp = '\0'; strerr_die2x(100,FATAL,MSG1(ERR_BAD_PART,cpstart)); } } else if (boundary.len && *line.s == '-' && line.len > boundary.len && !str_diffn(line.s,boundary.s,boundary.len)) { flagcheck = 1; } else { if (!msgsize && flagbody) if (case_startb(line.s,line.len,"subscribe") || case_startb(line.s,line.len,"unsubscribe")) strerr_die2x(100,FATAL,MSG(ERR_BODYCOMMAND)); if (!flagcheck) { msgsize += line.len; if (maxmsgsize && msgsize > maxmsgsize) { strnum[fmt_ulong(strnum,maxmsgsize)] = 0; strerr_die2x(100,FATAL,MSG1(ERR_MAX_SIZE,strnum)); } } } } if (msgsize < minmsgsize) { strnum[fmt_ulong(strnum,minmsgsize)] = 0; strerr_die2x(100,FATAL,MSG1(ERR_MIN_SIZE,strnum)); } _exit(0); }
void main(int argc,char **argv) { char *dir; int fdlock; char *sender; int match; int flaginheader; int flagmodpost; int flagremote; const char *pmod; const char *err; int opt; unsigned int i; char szchar[2] = "-"; int child; (void) umask(022); sig_pipeignore(); if (!stralloc_copys(&sendopt,"-")) die_nomem(); while ((opt = getopt(argc,argv,"bBcCmMpPrRsSvVyY")) != opteof) switch(opt) { case 'b': flagbody = 1; break; case 'B': flagbody = 0; break; case 'm': flagmime = 1; break; case 'M': flagmime = 0; break; case 'p': flagpublic = 1; break; /* anyone can post (still moderated)*/ case 'P': flagpublic = 0; break; /* only moderators can post */ case 's': flagself = 1; break; /* modpost and DIR/mod diff fxns */ case 'S': flagself = 0; break; /* same fxn */ case 'y': flagconfirm = 1; break; /* force post confirmation */ case 'Y': flagconfirm = 0; break; /* disable post confirmation */ case 'c': /* ezmlm-send flags */ case 'C': case 'r': case 'R': szchar[0] = (char) opt & 0xff; if (!stralloc_append(&sendopt,szchar)) die_nomem(); break; case 'v': case 'V': strerr_die2x(0,"ezmlm-store version: ",auto_version); default: die_usage(); } sender = env_get("SENDER"); if (sender) { if (!*sender || str_equal(sender,"#@[]")) strerr_die2x(100,FATAL,ERR_BOUNCE); } startup(dir = argv[optind]); load_config(dir); if (flagconfirm == -1) flagconfirm = getconf_line(&confirmpost,"confirmpost",0,dir); else getconf_line(&confirmpost,"confirmpost",0,dir); flagmodpost = getconf_line(&moderators,"modpost",0,dir); flagremote = getconf_line(&line,"remote",0,dir); if (!flagmodpost && !flagconfirm) { /* not msg-mod. Pipe to ezmlm-send */ if ((child = wrap_fork()) == 0) wrap_execbin("/ezmlm-send", &sendopt, dir); /* parent */ wrap_exitcode(child); } if (!moderators.len || !(moderators.s[0] == '/')) { if (!stralloc_copys(&moderators,dir)) die_nomem(); if (!stralloc_cats(&moderators,"/mod")) die_nomem(); } if (!stralloc_0(&moderators)) die_nomem(); if (sender) { pmod = issub(moderators.s,0,sender); closesub(); /* sender = moderator? */ } else pmod = 0; if (!pmod && !flagpublic) strerr_die2x(100,FATAL,ERR_NO_POST); fdlock = lockfile("mod/lock"); if (!stralloc_copys(&mydtline, flagconfirm ? "Delivered-To: confirm to " : "Delivered-To: moderator for ")) die_nomem(); if (!stralloc_catb(&mydtline,outlocal.s,outlocal.len)) die_nomem(); if (!stralloc_append(&mydtline,"@")) die_nomem(); if (!stralloc_catb(&mydtline,outhost.s,outhost.len)) die_nomem(); if (!stralloc_cats(&mydtline,"\n")) die_nomem(); if (!stralloc_copys(&returnpath,"Return-Path: <")) die_nomem(); if (sender) { if (!stralloc_cats(&returnpath,sender)) die_nomem(); for (i = 14; i < returnpath.len;++i) if (returnpath.s[i] == '\n' || !returnpath.s[i] ) returnpath.s[i] = '_'; /* NUL and '\n' are bad, but we don't quote since this is */ /* only for ezmlm-moderate, NOT for SMTP */ } if (!stralloc_cats(&returnpath,">\n")) die_nomem(); pid = getpid(); /* unique file name */ for (i = 0;;++i) /* got lock - nobody else can add files */ { when = now(); /* when is also used later for date! */ if (!stralloc_copys(&fnmsg, flagconfirm?"mod/unconfirmed/":"mod/pending/")) die_nomem(); if (!stralloc_copyb(&fnbase,strnum,fmt_ulong(strnum,when))) die_nomem(); if (!stralloc_append(&fnbase,".")) die_nomem(); if (!stralloc_catb(&fnbase,strnum,fmt_ulong(strnum,pid))) die_nomem(); if (!stralloc_cat(&fnmsg,&fnbase)) die_nomem(); if (!stralloc_0(&fnmsg)) die_nomem(); if (stat(fnmsg.s,&st) == -1) if (errno == error_noent) break; /* really should never get to this point */ if (i == 2) strerr_die2x(111,FATAL,ERR_UNIQUE); sleep(2); } if (!stralloc_copys(&action,"-")) die_nomem(); if (!stralloc_cats(&action,flagconfirm?ACTION_DISCARD:ACTION_REJECT)) die_nomem(); if (!stralloc_cat(&action,&fnbase)) die_nomem(); if (!stralloc_0(&action)) die_nomem(); makeacthash(&action); if (!quote("ed,&outlocal)) die_nomem(); if (!stralloc_copy(&reject,"ed)) die_nomem(); if (!stralloc_cat(&reject,&action)) die_nomem(); if (!stralloc_0(&reject)) die_nomem(); if (!stralloc_copys(&action,"-")) die_nomem(); if (!stralloc_cats(&action,flagconfirm?ACTION_CONFIRM:ACTION_ACCEPT)) die_nomem(); if (!stralloc_cat(&action,&fnbase)) die_nomem(); if (!stralloc_0(&action)) die_nomem(); makeacthash(&action); if (!stralloc_copy(&accept,"ed)) die_nomem(); if (!stralloc_cat(&accept,&action)) die_nomem(); if (!stralloc_0(&accept)) die_nomem(); set_cptarget(accept.s); /* for copy () */ set_cpconfirm(reject.s,quoted.len); fdmsg = open_trunc(fnmsg.s); if (fdmsg == -1) strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fnmsg.s,": "); substdio_fdbuf(&ssmsg,write,fdmsg,msgbuf,sizeof(msgbuf)); if (qmail_open(&qq, (stralloc *) 0) == -1) /* Open mailer */ strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE); hdr_add2("Mailing-List: ",mailinglist.s,mailinglist.len); if (listid.len > 0) hdr_add2("List-ID: ",listid.s,listid.len); hdr_datemsgid(when); if (flagconfirm) hdr_from("-owner"); else hdr_add2s("From: ",reject.s); hdr_add2s("Reply-To: ",accept.s); if (!flagconfirm && !pmod && flagremote) { /* if remote admin add -allow- address */ qmail_puts(&qq,"Cc: "); /* for ezmlm-gate users */ strnum[fmt_ulong(strnum,(unsigned long) when)] = 0; cookie(hash,key.s,key.len-FLD_ALLOW,strnum,sender,"t"); if (!stralloc_copy(&line,&outlocal)) die_nomem(); if (!stralloc_cats(&line,"-allow-tc.")) die_nomem(); if (!stralloc_cats(&line,strnum)) die_nomem(); if (!stralloc_append(&line,".")) die_nomem(); if (!stralloc_catb(&line,hash,COOKIE)) die_nomem(); if (!stralloc_append(&line,"-")) die_nomem(); i = str_rchr(sender,'@'); if (!stralloc_catb(&line,sender,i)) die_nomem(); if (sender[i]) { if (!stralloc_append(&line,"=")) die_nomem(); if (!stralloc_cats(&line,sender + i + 1)) die_nomem(); } qmail_put(&qq,line.s,line.len); qmail_puts(&qq,"@"); qmail_put(&qq,outhost.s,outhost.len); qmail_puts(&qq,"\n"); } qmail_puts(&qq,"To: <"); if (flagconfirm) { if (sender) qmail_puts(&qq, sender); } else { if (!quote("ed,&outlocal)) die_nomem(); qmail_put(&qq,quoted.s,quoted.len); qmail_puts(&qq,"-moderators@"); qmail_put(&qq,outhost.s,outhost.len); } qmail_puts(&qq,">\n"); /* FIXME: Drop the custom subject hack and use hdr_listsubject1 */ if (!stralloc_copys(&subject,"Subject: ")) die_nomem(); if (flagconfirm) { if (confirmpost.len) { if (!stralloc_cat(&subject,&confirmpost)) die_nomem(); if (!stralloc_cats(&subject," ")) die_nomem(); } else { if (!stralloc_cats(&subject,TXT_CONFIRM_POST)) die_nomem(); } } else { if (!stralloc_cats(&subject,TXT_MODERATE)) die_nomem(); } if (!quote("ed,&outlocal)) die_nomem(); if (!stralloc_cat(&subject,"ed)) die_nomem(); if (!stralloc_append(&subject,"@")) die_nomem(); if (!stralloc_cat(&subject,&outhost)) die_nomem(); if (flagmime) { hdr_mime(CTYPE_MULTIPART); qmail_put(&qq,subject.s,subject.len); hdr_boundary(0); hdr_ctype(CTYPE_TEXT); hdr_transferenc(); } else { qmail_put(&qq,subject.s,subject.len); qmail_puts(&qq,"\n\n"); } copy(&qq,flagconfirm?"text/post-confirm":"text/mod-request",flagcd); if (flagcd == 'B') { encodeB("",0,&line,2); qmail_put(&qq,line.s,line.len); } if (substdio_put(&ssmsg,returnpath.s,returnpath.len) == -1) die_msg(); if (substdio_put(&ssmsg,mydtline.s,mydtline.len) == -1) die_msg(); substdio_fdbuf(&ssin,read,0,inbuf,sizeof(inbuf)); if (flagmime) { hdr_boundary(0); hdr_ctype(CTYPE_MESSAGE); qmail_puts(&qq, "\n"); } qmail_put(&qq,returnpath.s,returnpath.len); qmail_put(&qq,mydtline.s,mydtline.len); flaginheader = 1; for (;;) { if (getln(&ssin,&line,&match,'\n') == -1) strerr_die2sys(111,FATAL,ERR_READ_INPUT); if (!match) break; if (line.len == 1) flaginheader = 0; if (flaginheader) { if ((line.len == mydtline.len) && !byte_diff(line.s,line.len,mydtline.s)) { close(fdmsg); /* be nice - clean up */ unlink(fnmsg.s); strerr_die2x(100,FATAL,ERR_LOOPING); } if (case_startb(line.s,line.len,"mailing-list:")) { close(fdmsg); /* be nice - clean up */ unlink(fnmsg.s); strerr_die2x(100,FATAL,ERR_MAILING_LIST); } } if (flagbody || flaginheader) /* skip body if !flagbody */ qmail_put(&qq,line.s,line.len); if (substdio_put(&ssmsg,line.s,line.len) == -1) die_msg(); } if (flagmime) hdr_boundary(1); /* close archive before qmail. Loss of qmail will result in re-run, and */ /* worst case this results in a duplicate msg sitting orphaned until it's */ /* cleaned out. */ if (substdio_flush(&ssmsg) == -1) die_msg(); if (fsync(fdmsg) == -1) die_msg(); if (fchmod(fdmsg,MODE_MOD_MSG | 0700) == -1) die_msg(); if (close(fdmsg) == -1) die_msg(); /* NFS stupidity */ close(fdlock); if (flagconfirm) { qmail_from(&qq,reject.s); /* envelope sender */ } else { 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); /* envelope sender */ } if (flagconfirm) /* to sender */ qmail_to(&qq,sender); else if (pmod) /* to moderator only */ qmail_to(&qq,pmod); else { if (flagself) { /* to all moderators */ if (!stralloc_copys(&moderators,dir)) die_nomem(); if (!stralloc_cats(&moderators,"/mod")) die_nomem(); if (!stralloc_0(&moderators)) die_nomem(); } putsubs(moderators.s,0,0,52,subto,1); } if (*(err = qmail_close(&qq)) == '\0') { strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0; strerr_die2x(0,"ezmlm-store: info: qp ",strnum); } else strerr_die3x(111,FATAL,ERR_TMP_QMAIL_QUEUE,err+1); }
void main(void) { unsigned int i,j; for (;;) { get(&line); if (line.len == 1) break; if (flagdsn) { if (line.s[0] == ' ' || line.s[0] == '\t') /* continuation */ if (!stralloc_catb(&dsnline,line.s,line.len - 1)) die_nomem(); continue; } flagdsn = 0; if (stralloc_starts(&line,"Subject: success notice")) _exit(99); if (stralloc_starts(&line,"Subject: deferral notice")) _exit(99); if (stralloc_starts(&line,"Precedence: bulk")) _exit(99); if (stralloc_starts(&line,"Precedence: junk")) _exit(99); if (stralloc_starts(&line,"Auto-Submitted:")) _exit(99); /* Mailing list signatures */ if (stralloc_starts(&line,"Precedence: list")) _exit(99); if (stralloc_starts(&line,"Mailing-List:")) _exit(99); if (stralloc_starts(&line,"List-ID:")) _exit(99); if (stralloc_starts(&line,"X-Mailing-List:")) _exit(99); if (stralloc_starts(&line,"X-ML-Name:")) _exit(99); if (stralloc_starts(&line,"List-Help:")) _exit(99); if (stralloc_starts(&line,"List-Unsubscribe:")) _exit(99); if (stralloc_starts(&line,"List-Subscribe:")) _exit(99); if (stralloc_starts(&line,"List-Post:")) _exit(99); if (stralloc_starts(&line,"List-Owner:")) _exit(99); if (stralloc_starts(&line,"List-Archive:")) _exit(99); /* for Novell Groupwise */ if (stralloc_starts(&line,"Subject: Message status - delivered")) _exit(99); if (stralloc_starts(&line,"Subject: Message status - opened")) _exit(99); if (stralloc_starts(&line,"Subject: Out of Office AutoReply:")) _exit(99); /* Other autoresponders */ if (stralloc_starts(&line,"X-Amazon-Auto-Reply:")) _exit(99); if (stralloc_starts(&line,"X-Mailer: KANA Response")) _exit(99); if (stralloc_starts(&line,"Thread-Topic: AutoResponse")) _exit(99); if (stralloc_starts(&line,"Subject: AutoResponse -")) _exit(99); if (stralloc_starts(&line,"From: Mail Delivery Subsystem <MAILER-DAEMON@")) flagmds = 1; if (stralloc_starts(&line,"Subject: Warning: could not send message")) flagsw = 1; if (stralloc_starts(&line,"Subject: Returned mail: warning: cannot send message")) flagsr = 1; if (stralloc_starts(&line,"Auto-Submitted: auto-generated (warning")) flagas = 1; if (case_startb(line.s,line.len,"Content-type: multipart/report")) { if (!stralloc_copyb(&dsnline,line.s,line.len - 1)) die_nomem(); flagdsn = 1; } } /* end of header */ if (dsnline.len > 0) { /* always only one recipient/action */ flagdsn = 0; /* will be set for correct report type */ for (i=0; i < dsnline.len; i += 1+byte_chr(dsnline.s+i,dsnline.len-i,';')) { while (dsnline.s[i] == ' ' || dsnline.s[i] == '\t') if (++i >= dsnline.len) break; if (case_startb(dsnline.s + i,dsnline.len - i,"report-type=")) { i += 12; while (dsnline.s[i] ==' ' || dsnline.s[i] =='\t' || dsnline.s[i] =='"') if (++i >= dsnline.len) break; if (case_startb(dsnline.s + i,dsnline.len - i,"delivery-status")) flagdsn = 1; } else if (case_startb(dsnline.s + i,dsnline.len - i,"boundary=")) { i += 9; while (dsnline.s[i] ==' ' || dsnline.s[i] =='\t') if (++i >= dsnline.len) break; if (dsnline.s[i] == '"') { if (++i >= dsnline.len) break; j = i + byte_chr(dsnline.s + i,dsnline.len - i,'"'); if (j >= dsnline.len) break; } else { j = i; while (dsnline.s[j] !=' ' && dsnline.s[j] !='\t' && dsnline.s[j] !=';') if (++j >= dsnline.len) break; } /* got boundary */ if (!stralloc_copyb(&boundary,dsnline.s+i,j-i)) die_nomem(); } } } if (flagdsn && boundary.len) { /* parse DSN message */ get(&line); /* if bad format we exit(0) via get() */ for (;;) { if (isboundary()) { if (line.len == boundary.len + 5 && line.s[line.len - 1] == '-' && line.s[line.len - 2] == '-') _exit(99); /* end: not failure report */ get(&line); /* Content-type */ if (case_startb(line.s,line.len,"content-type:")) { i = 13; while (line.s[i] == ' ' || line.s[i] == '\t') if (++i >= line.len) break; if (case_startb(line.s+i,line.len-i,"message/delivery-status")) { for (;;) { get(&line); if (isboundary()) break; if (case_startb(line.s,line.len,"action:")) { i = 8; while (line.s[i] == ' ' || line.s[i] == '\t') if (++i >= line.len) break; if (case_startb(line.s + i, line.len - i,"failed")) _exit(0); /* failure notice */ else _exit(99); /* there shouldn't be more than 1 action */ } } } } } else get(&line); } } get(&line1); get(&line2); get(&line3); get(&line4); get(&line5); get(&line6); get(&line7); get(&line8); if (stralloc_starts(&line1,"This is a MIME-encapsulated message")) if (stralloc_starts(&line3,"--")) if (stralloc_starts(&line5,warn1)) if (stralloc_starts(&line6,warn2)) if (stralloc_starts(&line7,warn3)) if (stralloc_starts(&line8,warn4)) flagbw = 1; if (stralloc_starts(&line1,warn1)) if (stralloc_starts(&line2,warn2)) if (stralloc_starts(&line3,warn3)) if (stralloc_starts(&line4,warn4)) flagbw = 1; if (flagmds && flagsw && flagas && flagbw) _exit(99); if (flagmds && flagsr && flagbw) _exit(99); _exit(0); }
int ftpparse_mlsx (struct ftpparse *fp, char *x, int ll, int is_mlst) { int i; uint64 size; struct tai mtime; int flagtryretr=0; int flagtrycwd=0; char *id=0; int idlen=0; int mtimetype=FTPPARSE_MTIME_UNKNOWN; int sizetype=FTPPARSE_SIZE_UNKNOWN; int flagbrokenmlsx=0; SETUP(); if (is_mlst) if (ll>1) { ll--; x++; } if (ll<2) /* empty facts, space, one-byte-filename */ return 0; for (i=0; i<ll;i++) { int j=0,k=0; if (x[i]==' ') break; /* end of facts */ while (i+j<ll && x[i+j]!=';' && x[i+j]!=' ' && x[i+j]!='=') j++; if (i+j==ll) return 0; if (x[i+j]==' ') return 0; if (x[i+j]==';') return 0; /* x[i+j] is now '=' */ while (i+j+k<ll && x[i+j+k]!=';' && x[i+j+k]!=' ') k++; if (i+j+k==ll) return 0; /* x[i+j+k] is space or semicolon, so use of getlong is safe */ #define ISFACT(name) (j==sizeof(name)-1 && case_startb(x+i,j,name)) if (ISFACT ("size")) { get_uint64 (x + i + j + 1, k - 1,&size); sizetype=FTPPARSE_SIZE_BINARY; } else if (ISFACT ("modify")) { getmod(&mtime,x + i + j + 1, k - 1); mtimetype = FTPPARSE_MTIME_LOCAL; } else if (ISFACT ("type")) { if (k==5 && case_startb (x + i + j + 1, 4, "file")) flagtryretr = 1; else if (case_startb (x + i + j + 1, 3, "dir")) /* "current" */ flagtrycwd = 1; else if (case_startb (x + i + j + 1, 4, "pdir")) /* "parent" */ flagtrycwd = 1; else if (case_startb (x + i + j + 1, 4, "cdir")) flagtrycwd = 1; else { flagtryretr = 1; flagtrycwd = 1; } } else if (ISFACT ("unique")) { id = x + i + j + 1; idlen = k - 1; } i+=j+k; if (x[i]==' ') { flagbrokenmlsx=1; break; } } if (ll==i) return 0; i++; if (ll==i) return 0; fp->name = x + i; fp->namelen = ll - i; fp->sizetype = sizetype; fp->size=size; fp->mtimetype = mtimetype; fp->mtime=mtime; fp->flagtrycwd=flagtrycwd; fp->flagtryretr=flagtryretr; if (id) { fp->idtype = FTPPARSE_ID_FULL; fp->id=id; fp->idlen=idlen; } fp->flagbrokenmlsx=flagbrokenmlsx; fp->format=FTPPARSE_FORMAT_MLSX; return 1; }