void main(int argc,char **argv) { char *dir; char *sender; char *moddir; const char *queryext = (char *) 0; int opt; int ret = 0; int dontact = 0; unsigned int i,j; const char *program; stralloc *opts; umask(022); sig_pipeignore(); /* storeopts to ezmlm-store only. Others to both (ezmlm-store may */ /* pass them on to ezmlm-send. */ if (!stralloc_copys(&sendopt,"-")) die_nomem(); if (!stralloc_copys(&storeopt,"-")) die_nomem(); while ((opt = getopt(argc,argv, "0cCmMpPq:Q:sSrRt:T:vVyY")) != opteof) switch(opt) { /* pass on unrecognized options */ case 'c': /* ezmlm-send flags */ case 'C': case 'r': case 'Q': case 'R': szchar[0] = opt; if (!stralloc_append(&sendopt,szchar)) die_nomem(); if (!stralloc_append(&storeopt,szchar)) die_nomem(); break; case 'm': /* ezmlm-store flags */ case 'M': case 'p': case 'P': case 's': case 'S': case 'y': case 'Y': szchar[0] = opt; if (!stralloc_append(&storeopt,szchar)) die_nomem(); break; case 'q': if (optarg) queryext = optarg; break; case 'v': case 'V': strerr_die2x(0,"ezmlm-gate version: ",auto_version); case '0': dontact = 1; break; default: /* ezmlm-store flags */ die_usage(); } startup(dir = argv[optind++]); sender = env_get("SENDER"); pmod = (char *) 0; if (queryext) { getconf(&cmds,queryext,1,dir); i = 0; for (j = 0;j < cmds.len; ++j) if (!cmds.s[j]) { switch (cmds.s[i]) { case '\0': case '#': break; /* ignore blank/comment */ case '|': ret = mailprog(cmds.s + i + 1); break; default: ret = mailprog(cmds.s + i); break; } if (ret) break; i = j + 1; } if (!ret || ret == 99) /* 111 => temp error */ pmod = ""; /* 0, 99 => post */ /* other => moderate */ } moddir = argv[optind++]; if (moddir && !ret) { /* if exit 0 and moddir, add issub */ pmod = (char *) 0; while (moddir && !pmod && sender) { pmod = (moddir[0] == '/') ? issub(moddir,0,sender) : issub(dir,moddir,sender); closesub(); moddir = argv[optind++]; } } if (pmod) { program = "/ezmlm-send"; opts = &sendopt; } else { program = "/ezmlm-store"; opts = &storeopt; } if (dontact) { substdio_puts(subfderr, auto_bin); substdio_puts(subfderr, program); substdio_put(subfderr, " ", 1); substdio_put(subfderr, opts->s, opts->len); substdio_put(subfderr, " ", 1); substdio_puts(subfderr, dir); substdio_putsflush(subfderr, "\n"); _exit(0); } if ((child = wrap_fork()) == 0) wrap_execbin(program, opts, dir); /* parent */ wrap_exitcode(child); _exit(0); }
void main() { int i; int numcolons; umask(033); if (chdir(auto_qmail) == -1) die_chdir(); fd = open_read("users/assign"); if (fd == -1) die_opena(); substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf)); fdtemp = open_trunc("users/cdb.tmp"); if (fdtemp == -1) die_opent(); if (cdbmss_start(&cdbmss,fdtemp) == -1) die_writet(); if (!stralloc_copys(&wildchars,"")) die_nomem(); for (;;) { if (getln(&ssin,&line,&match,'\n') != 0) die_reada(); if (line.len && (line.s[0] == '.')) break; if (!match) die_format(); if (byte_chr(line.s,line.len,'\0') < line.len) die_format(); i = byte_chr(line.s,line.len,':'); if (i == line.len) die_format(); if (i == 0) die_format(); if (!stralloc_copys(&key,"!")) die_nomem(); if (line.s[0] == '+') { if (!stralloc_catb(&key,line.s + 1,i - 1)) die_nomem(); case_lowerb(key.s,key.len); if (i >= 2) if (byte_chr(wildchars.s,wildchars.len,line.s[i - 1]) == wildchars.len) if (!stralloc_append(&wildchars,line.s + i - 1)) die_nomem(); } else { if (!stralloc_catb(&key,line.s + 1,i - 1)) die_nomem(); if (!stralloc_0(&key)) die_nomem(); case_lowerb(key.s,key.len); } if (!stralloc_copyb(&data,line.s + i + 1,line.len - i - 1)) die_nomem(); numcolons = 0; for (i = 0;i < data.len;++i) if (data.s[i] == ':') { data.s[i] = 0; if (++numcolons == 6) break; } if (numcolons < 6) die_format(); data.len = i; if (cdbmss_add(&cdbmss,key.s,key.len,data.s,data.len) == -1) die_writet(); } if (cdbmss_add(&cdbmss,"",0,wildchars.s,wildchars.len) == -1) die_writet(); if (cdbmss_finish(&cdbmss) == -1) die_writet(); if (fsync(fdtemp) == -1) die_writet(); if (close(fdtemp) == -1) die_writet(); /* NFS stupidity */ if (rename("users/cdb.tmp","users/cdb") == -1) die_rename(); _exit(0); }
static void die_syntax(stralloc *line) { if (!stralloc_0(line)) die_nomem(); strerr_die4x(100,FATAL,MSG1(ERR_SYNTAX,TXT_EZCRONRC),": ",line->s); }
void digest(msgentry *msgtable, subentry *subtable, authentry *authtable, unsigned long from,unsigned long to, stralloc *subj,int factype,char format) /* Output digest range from-to as per msgtable/subtable (from mkthread(s)). */ /* "Subject is the subject of the _entire_ digest/set. */ { const msgentry *pmsgt; subentry *psubt; const char *cp; int ffirstmsg; unsigned int len; unsigned long msg; unsigned long subnum; psubt = subtable; presub(from,to,subj,factype,format); if (format != NATIVE) { while (psubt->sub) { ffirstmsg = 1; /* ptr to first message with this subject */ pmsgt = msgtable + psubt->firstmsg - from; subnum = (unsigned long) (psubt - subtable +1); for (msg=psubt->firstmsg; msg<=to; msg++) { if (pmsgt->subnum == subnum) { if(ffirstmsg) { ffirstmsg = 0; if (!stralloc_copys(&line,"\n")) die_nomem(); if (psubt->sublen <= HASHLEN + 2) { if (!stralloc_cats(&line,"(null)\n")) die_nomem(); } else if (!stralloc_catb(&line,psubt->sub + HASHLEN + 1, psubt->sublen - HASHLEN - 1)) die_nomem(); } else if (!stralloc_copys(&line,"")) die_nomem(); if (!stralloc_cats(&line,"\t")) die_nomem(); if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,msg))) die_nomem(); if (!stralloc_append(&line," ")) die_nomem(); if (pmsgt->authnum) { author.len = 0; cp = authtable[pmsgt->authnum - 1].auth; len = authtable[pmsgt->authnum - 1].authlen - 1; if (len > HASHLEN) { if (!stralloc_catb(&author,cp + HASHLEN + 1, len - HASHLEN - 1)) die_nomem(); } else if (!stralloc_catb(&author,cp,len)) die_nomem(); if (!stralloc_0(&author)) die_nomem(); } if (!stralloc_cats(&line,MSG1(TXT_BY,author.s))) die_nomem(); if (!stralloc_append(&line,"\n")) die_nomem(); code_qput(line.s,line.len); } pmsgt++; } psubt++; } } postsub(factype,format); psubt = subtable; while (psubt->sub) { pmsgt = msgtable + psubt->firstmsg - from; subnum = (unsigned long) (psubt - subtable +1); for (msg=psubt->firstmsg; msg<=to; msg++) { if (pmsgt->subnum == subnum) msgout(msg,format); pmsgt++; } psubt++; } postmsg(format); idx_destroythread(msgtable,subtable,authtable); }
static void entry_add(lx_s *body, struct dirent *dent) { char *dt; char *dname = dent->d_name; struct stat sb; lx_s mode = {0}; lx_s size = {0}; unsigned perms; #ifdef HAVE_D_NAMLEN /* XXX: add to configuration */ unsigned dnamlen = dent->d_namlen; #else unsigned dnamlen = strlen(dname); #endif if (!strcmp(dname, ".")) return; /* XXX: make lstat()? with stat(), symlinks could cause confusion */ if (stat(dname, &sb)) { /* XXX: should probably be debug, really, since symbolic links to * invalid filenames will trigger this */ log_warning(ERRNO, dname, "stat"); return; } if (S_ISDIR(sb.st_mode)) { dt = DTNAM_DIR; /* replace terminating 0 with '/' for our usage */ dname[dnamlen++] = '/'; } else if (S_ISREG(sb.st_mode)) { dt = DTNAM_FILE; } else { dt = DTNAM_OTHER; } /* get ascii representation of numerical mode */ perms = sb.st_mode & 07777; if (lx_setalloc(&mode, 5)) die_nomem(); if (perms >= 01000) lx_cadd(&mode, '0'); else { lx_striadd(&mode, " 0", 2); if (perms < 0100) { lx_cadd(&mode, '0'); if (perms < 010) { lx_cadd(&mode, '0'); } } } lx_straddulong(&mode, perms, 8); /* get a normalized size value */ #define kmask 0xff #define kshrink(n, type) \ ((type) (((type)n & ~(type)kmask) + (kmask+1)) >> 10) { char type = 'b'; off_t len = sb.st_size; if (lx_setalloc(&size, 5)) die_nomem(); if (len >= 4096) { len = kshrink(len, off_t); type = 'k'; } if (len >= 4096) { len = kshrink(len, off_t); type = 'm'; } if (len >= 4096) { len = kshrink(len, off_t); type = 'g'; } if (len < 1000) { lx_cadd(&size, ' '); if (len < 100) { lx_cadd(&size, ' '); if (len < 10) lx_cadd(&size, ' '); } } if (lx_straddulong(&size, len, 10)) die_nomem(); if (lx_cadd(&size, type)) die_nomem(); } if (lx_striadd(body, " ", 3) || lx_stradd(body, dt) || lx_cadd(body, ' ') || lx_strcat(body, &mode) || lx_striadd(body, " ", 2) || lx_strcat(body, &size) || lx_stradd(body, " <a href=\"") || lx_striadd(body, dname, dnamlen) || lx_striadd(body, "\">", 2) || lx_striadd(body, dname, dnamlen) || lx_stradd(body, "</a>"CRLF)) die_nomem(); lx_free(&mode); lx_free(&size); }
void util_trie_insert(struct trie *t, const char *s) { char *v = strdup(s); if (!v) die_nomem(); if (trie_insert(t, v, v) < 0) die_nomem(); }
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()"); } }
int findname(void) /* returns 1 if a matching line was found, 0 otherwise. name will contain */ /* the correct list address in either case */ { char *cpat,*cp1,*cp2,*cplast; const char *cpname; unsigned long u; unsigned char hash,hash_hi,hash_lo; unsigned int pos,pos_name,pos_hi; int fd,match; /* make case insensitive hash */ flagfound = 0; /* default */ cpname = ""; /* default */ if (!stralloc_copy(&lctarget,&target)) die_nomem(); case_lowerb(lctarget.s,lctarget.len -1); hash = subhashs(lctarget.s); /* make domain pointer */ cpat = lctarget.s + str_chr(lctarget.s,'@'); if (!*cpat) strerr_die4x(100,FATAL,ERR_ADDR_AT,": ",target.s); cplast = cpat + str_len(cpat) - 1; if (*cplast == '.') --cplast; /* annonying special case */ cp1 = cpat + byte_rchr(cpat,cplast - cpat, '.'); if (cp1 != cplast) { /* got one '.' */ if (!stralloc_copyb(&domain,cp1 + 1, cplast - cp1)) die_nomem(); cp2 = cpat + byte_rchr(cpat, cp1 - cpat,'.'); if (cp2 == cp1) cp2 = cpat; ++cp2; if (!stralloc_append(&domain,".")) die_nomem(); if (!stralloc_catb(&domain,cp2, cp1 - cp2)) die_nomem(); } else /* no '.' */ if (!stralloc_copyb(&domain,cpat + 1,cplast - cpat)) die_nomem(); if (!stralloc_0(&domain)) die_nomem(); if ((fd = open_read(split)) == -1) strerr_die4sys(111,FATAL,ERR_OPEN,split,": "); substdio_fdbuf(&sssp,read,fd,spbuf,(int) sizeof(spbuf)); lineno = 0; for (;;) { /* dom:hash_lo:hash_hi:listaddress */ if (getln(&sssp,&line,&match,'\n') == -1) strerr_die4sys(111,FATAL,ERR_READ,split,": "); lineno++; if (!match) break; if (line.s[0] == '#') continue; /* comment */ line.s[line.len - 1] = '\0'; /* no need to allow \0 in lines */ if (!line.s[pos = str_chr(line.s,':')]) continue; /* usually blank line */ line.s[pos] = '\0'; if (pos == 0 || /* no domain */ (case_starts(domain.s,line.s))) { /* or matching domain */ if (!line.s[++pos]) die_syntax(); pos_hi = pos + str_chr(line.s + pos,':'); if (!line.s[pos_hi]) die_syntax(); pos_hi++; (void) scan_ulong(line.s + pos, &u); /* scan_uint() not in ezmlm */ hash_lo = (unsigned char) u; (void) scan_ulong(line.s + pos_hi, &u); hash_hi = (unsigned char) u; pos_name = pos_hi + str_chr(line.s + pos_hi,':'); if (pos_hi == pos_name) hash_hi = 52L; /* default hi = 52 */ if (line.s[pos_name]) pos_name++; if (hash > hash_hi || hash < hash_lo) continue; /* not us */ cpname = line.s + pos_name; while (*cpname && /* isolate name */ (*cpname == ' ' || *cpname == '\t')) cpname++; pos = line.len - 2; while (pos && (line.s[pos] == '\n' || line.s[pos] == ' ' || line.s[pos] == '\t')) line.s[pos--] = '\0'; break; } } close(fd); if (*cpname) { if (!stralloc_copys(&name,cpname)) die_nomem(); if (byte_chr(name.s,name.len,'@') == name.len) { /* local sublist */ if (!stralloc_append(&name,"@")) die_nomem(); if (!stralloc_cat(&name,&outhost)) die_nomem(); } if (!stralloc_0(&name)) die_nomem(); return 1; } else { /* match without name or no match =>this list */ if (!stralloc_copy(&name,&outlocal)) die_nomem(); if (!stralloc_append(&name,"@")) die_nomem(); if (!stralloc_cat(&name,&outhost)) die_nomem(); if (!stralloc_0(&name)) die_nomem(); return 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; char *action; char *dtline; char *nhost; const char *err; unsigned int i; int match; int opt; sig_pipeignore(); while ((opt = getopt(argc,argv,"dDvV")) != opteof) { switch (opt) { case 'd': flagdo = 1; break; case 'D': flagdo = 0; break; case 'v': case 'V': strerr_die2x(0, "ezmlm-split version: ",auto_version); default: die_usage(); } } startup(dir = argv[optind++]); load_config(dir); if (!(split = argv[optind])) split = "split"; if (flagdo) { sender = env_get("SENDER"); if (!sender) strerr_die2x(100,FATAL,ERR_NOSENDER); if (!*sender) strerr_die2x(100,FATAL,ERR_BOUNCE); if (!sender[str_chr(sender,'@')]) strerr_die2x(100,FATAL,ERR_ANONYMOUS); if (str_equal(sender,"#@[]")) strerr_die2x(100,FATAL,ERR_BOUNCE); action = env_get("DEFAULT"); if (!action) strerr_die2x(100,FATAL,ERR_NODEFAULT); if (!stralloc_copys(&target,sender)) die_nomem(); if (action[0]) { i = str_chr(action,'-'); if (action[i]) { action[i] = '\0'; if (!stralloc_copys(&target,action + i + 1)) die_nomem(); i = byte_rchr(target.s,target.len,'='); if (i < target.len) target.s[i] = '@'; } } if (!stralloc_0(&target)) die_nomem(); if (case_diffs(action,ACTION_SUBSCRIBE) && case_diffs(action,ALT_SUBSCRIBE) && case_diffs(action,ACTION_UNSUBSCRIBE) && case_diffs(action,ALT_UNSUBSCRIBE)) _exit(0); /* not for us */ if (findname()) { /* new sender */ if (!stralloc_copy(&from,&outlocal)) die_nomem(); if (!stralloc_cats(&from,"-return-@")) die_nomem(); if (!stralloc_cat(&from,&outhost)) die_nomem(); if (!stralloc_0(&from)) die_nomem(); nhost = name.s + str_rchr(name.s,'@'); /* name must have '@'*/ *(nhost++) = '\0'; if (!stralloc_copys(&to,name.s)) die_nomem(); /* local */ if (!stralloc_append(&to,"-")) die_nomem(); /* - */ if (!stralloc_cats(&to,action)) die_nomem(); /* subscribe */ if (!stralloc_append(&to,"-")) die_nomem(); /* - */ if (target.s[i = str_rchr(target.s,'@')]) target.s[i] = '='; if (!stralloc_cats(&to,target.s)) die_nomem(); /* target */ if (!stralloc_append(&to,"@")) die_nomem(); /* - */ if (!stralloc_cats(&to,nhost)) die_nomem(); /* host */ if (!stralloc_0(&to)) die_nomem(); dtline = env_get("DTLINE"); if (!dtline) strerr_die2x(100,FATAL,ERR_NODTLINE); if (qmail_open(&qq,(stralloc *) 0) == -1) strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE); qmail_puts(&qq,dtline); /* delivered-to */ if (qmail_copy(&qq,subfdin,0) != 0) strerr_die2sys(111,FATAL,ERR_READ_INPUT); 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); } _exit(0); } else { for (;;) { if (getln(subfdin,&line,&match,'\n') == -1) strerr_die2sys(111,FATAL,ERR_READ_INPUT); if (!match) break; if (line.len == 1) continue; /* ignore blank lines */ if (line.s[0] == '#') continue; /* ignore comments */ if (!stralloc_copy(&target,&line)) die_nomem(); target.s[target.len - 1] = '\0'; (void) findname(); if (!stralloc_cats(&name,": ")) die_nomem(); if (!stralloc_cats(&name,target.s)) die_nomem(); if (!stralloc_append(&name,"\n")) die_nomem(); if (substdio_put(subfdout,name.s,name.len) == -1) strerr_die2sys(111,ERR_WRITE,"output: "); } if (substdio_flush(subfdout) == -1) strerr_die2sys(111,ERR_FLUSH,"output: "); _exit(0); } (void)argc; }
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); }
int main(int argc,char **argv) { const char *dir; const char *sender; const char *moddir; int opt; int ret = 0; unsigned int i,j; const char *program; stralloc *opts; umask(022); sig_pipeignore(); if (!stralloc_copys(&sendopt,"-")) die_nomem(); if (!stralloc_copys(&storeopt,"-")) die_nomem(); opt = getconfopt(argc,argv,options,1,&dir); /* storeopts to ezmlm-store only. Others to both (ezmlm-store may */ /* pass them on to ezmlm-send. */ if (!stralloc_catb(&storeopt,sendopt.s+1,sendopt.len-1)) die_nomem(); initsub(0); sender = get_sender(); ismod = 0; if (queryext) { getconf(&cmds,queryext,1); i = 0; for (j = 0;j < cmds.len; ++j) if (!cmds.s[j]) { switch (cmds.s[i]) { case '\0': case '#': break; /* ignore blank/comment */ case '|': ret = mailprog(cmds.s + i + 1); break; default: ret = mailprog(cmds.s + i); break; } if (ret) break; i = j + 1; } if (!ret || ret == 99) /* 111 => temp error */ ismod = 1; /* 0, 99 => post */ /* other => moderate */ } moddir = argv[opt++]; if (moddir && !ret) { /* if exit 0 and moddir, add issub */ ismod = 0; while (moddir && !ismod && sender) { ismod = issub(moddir,sender,0); closesub(); moddir = argv[opt++]; } } if (ismod) { program = "/ezmlm-send"; opts = &sendopt; } else { program = "/ezmlm-store"; opts = &storeopt; } if (dontact) { substdio_puts(subfderr, auto_bin()); substdio_puts(subfderr, program); substdio_put(subfderr, " ", 1); substdio_put(subfderr, opts->s, opts->len); substdio_put(subfderr, " ", 1); substdio_puts(subfderr, dir); substdio_putsflush(subfderr, "\n"); _exit(0); } if ((child = wrap_fork()) == 0) wrap_execbin(program, opts, dir); /* parent */ wrap_exitcode(child); _exit(0); }
int main(int argc,char **argv) { char strnum[FMT_ULONG]; char *action; char *dtline; char *nhost; const char *err; unsigned int i; int match; int opt; sig_pipeignore(); opt = getconfopt(argc,argv,options,1,0); if (!(split = argv[opt])) split = "split"; if (flagdo) { sender = get_sender(); if (!sender) die_sender(); 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)); action = env_get("DEFAULT"); if (!action) strerr_die2x(100,FATAL,MSG(ERR_NODEFAULT)); if (!stralloc_copys(&target,sender)) die_nomem(); if (action[0]) { i = str_chr(action,'-'); if (action[i]) { action[i] = '\0'; if (!stralloc_copys(&target,action + i + 1)) die_nomem(); i = byte_rchr(target.s,target.len,'='); if (i < target.len) target.s[i] = '@'; } } if (!stralloc_0(&target)) die_nomem(); if (case_diffs(action,ACTION_SUBSCRIBE) && case_diffs(action,ALT_SUBSCRIBE) && case_diffs(action,ACTION_UNSUBSCRIBE) && case_diffs(action,ALT_UNSUBSCRIBE)) _exit(0); /* not for us */ if (findname()) { /* new sender */ if (!stralloc_copy(&from,&outlocal)) die_nomem(); if (!stralloc_cats(&from,"-return-@")) die_nomem(); if (!stralloc_cat(&from,&outhost)) die_nomem(); if (!stralloc_0(&from)) die_nomem(); nhost = name.s + str_rchr(name.s,'@'); /* name must have '@'*/ *(nhost++) = '\0'; if (!stralloc_copys(&to,name.s)) die_nomem(); /* local */ if (!stralloc_append(&to,'-')) die_nomem(); /* - */ if (!stralloc_cats(&to,action)) die_nomem(); /* subscribe */ if (!stralloc_append(&to,'-')) die_nomem(); /* - */ if (target.s[i = str_rchr(target.s,'@')]) target.s[i] = '='; if (!stralloc_cats(&to,target.s)) die_nomem(); /* target */ if (!stralloc_append(&to,'@')) die_nomem(); /* - */ if (!stralloc_cats(&to,nhost)) die_nomem(); /* host */ if (!stralloc_0(&to)) die_nomem(); dtline = env_get("DTLINE"); if (!dtline) strerr_die2x(100,FATAL,MSG(ERR_NODTLINE)); if (qmail_open(&qq) == -1) strerr_die2sys(111,FATAL,MSG(ERR_QMAIL_QUEUE)); qmail_puts(&qq,dtline); /* delivered-to */ if (qmail_copy(&qq,subfdin,0) != 0) strerr_die2sys(111,FATAL,MSG(ERR_READ_INPUT)); qmail_from(&qq,from.s); 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_die3x(99,INFO,"qp ",strnum); } _exit(0); } else { for (;;) { if (getln(subfdin,&line,&match,'\n') == -1) strerr_die2sys(111,FATAL,MSG(ERR_READ_INPUT)); if (!match) break; if (line.len == 1) continue; /* ignore blank lines */ if (line.s[0] == '#') continue; /* ignore comments */ if (!stralloc_copy(&target,&line)) die_nomem(); target.s[target.len - 1] = '\0'; (void) findname(); if (!stralloc_cats(&name,": ")) die_nomem(); if (!stralloc_cats(&name,target.s)) die_nomem(); if (!stralloc_append(&name,'\n')) die_nomem(); if (substdio_put(subfdout,name.s,name.len) == -1) strerr_die2sys(111,FATAL,MSG(ERR_WRITE_STDOUT)); } if (substdio_flush(subfdout) == -1) strerr_die2sys(111,FATAL,MSG(ERR_FLUSH_STDOUT)); _exit(0); } (void)argc; }
void resp_sendfile(struct reqinfo *req) { int fd; struct stat sb; lx_s body = {0}, data = {0}; if (lx_check0(&req->location)) die_nomem(); fd = open(req->location.s, O_RDONLY); if (fd < 0) { if (errno == ENOENT) { die_html(HTERR_NOTFOUND|ERRNO, req->location.s, "couldn't open file for reading", err_not_found); } die_html(HTERR_FORBID|ERRNO, req->location.s, "couldn't open file for reading", err_perm_denied); } if (fstat(fd, &sb)) { close(fd); /* XXX: 500 instead? */ die_html(HTERR_FORBID|ERRNO, req->location.s, "opened file, but fstat() failed", err_perm_denied); } /* directory? send index (if config option is set) */ if (S_ISDIR(sb.st_mode) && OPT_INDICES) { index_generate(fd, &sb, req, &body); close(fd); if (lx_strset(&data, "text/html")) die_nomem(); header_setstr(blob_header_send, "Content-Type", data.s, data.len); data.len = 0; if (lx_straddulong(&data, body.len, 10)) die_nomem(); header_setstr(blob_header_send, "Content-Length", data.s, data.len); resp_sendprefix(req, HTERR_OK); resp_sendheaders(req, blob_header_send); if (!global_flags.header_only) { if (lx_gdstrput(gd_out, &body)) die_outerr(); } lx_free(&data); lx_free(&body); return; } if (!OPT_READABL) { close(fd); die_html(HTERR_FORBID, 0, "Read access forbidden", err_perm_denied); } if (!S_ISREG(sb.st_mode)) { close(fd); die_html(HTERR_FORBID, req->location.s, "not a regular file (may be a dir with 'indexes' unset)", err_perm_denied); } if (lx_strset(&data, "application/octet-stream")) die_nomem(); header_setstr(blob_header_send, "Content-Type", data.s, data.len); data.len = 0; if (lx_straddulong(&data, sb.st_size, 10)) die_nomem(); header_setstr(blob_header_send, "Content-Length", data.s, data.len); data.len = 0; header_mkdate(&data, sb.st_mtime); header_setstr(blob_header_send, "Last-Modified", data.s, data.len); resp_sendprefix(req, HTERR_OK); resp_sendheaders(req, blob_header_send); lx_free(&data); if (!global_flags.header_only) { #define READBUFSIZ 8192 char buf[READBUFSIZ]; int r; for (;;) { r = read(fd, buf, READBUFSIZ); if (r < 0) { close(fd); die_html(HTERR_SERVERR|ERRNO, req->location.s, "file opened, but read failed", err_sys_generic); } if (sb.st_size < r) { log_warning(0, req->location.s, "file longer than file size; truncating."); r = sb.st_size; /* now < READBUFSIZ by definition */ } if (lx_gdputsn(gd_out, buf, r)) die_outerr(); sb.st_size -= r; if (r < READBUFSIZ) break; } if (sb.st_size) { log_warning(0, req->location.s, "premature EOF"); /* XXX: fill with zeros? */ } } close(fd); }
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 resp_recvfile(struct reqinfo *req) { struct fsfd h; lx_s dest_dir = {0}, dest_file = {0}, data = {0}; lx_s *value; char *endlen; int length; if (!OPT_WRITABL) { die_html(HTERR_FORBID, 0, "Write access forbidden", err_perm_denied); } fs_ready(); fs_parseout(&dest_dir, &dest_file, &req->location); if (lx_check0(&dest_dir) || lx_check0(&dest_file) || lx_check0(&req->location)) die_nomem(); log_debug(DEBUG_NOISE, dest_dir.s, "destination directory"); log_debug(DEBUG_NOISE, dest_file.s, "destination file"); value = blob_get(blob_header_recv, "content-length", 14); if (!value) { die_html(HTERR_NEEDLEN, 0, "POST request without Content-Length", "Content-Length header required"); } lx_check0(value); length = strtoul(value->s, &endlen, 10); if (endlen == value->s || *endlen) { die_html(HTERR_NEEDLEN, 0, "POST request with invalid Content-Length", "Content-Length header required"); } fs_chdir_write(dest_dir.s); #if 0 if (!chdir(dest_dir.s)) { if (opt_p.v_opt.opt_toggle) { fs_mkpath(dest_dir.s); } else { die_html(HTERR_NOTFOUND|ERRNO, req->location.s, "chdir to target directory", "Invalid target directory"); } } fs_okay_write_dir(); #endif /* XXX: address if lx_check0 is modified to not increment len! */ fs_open_file_write(&h, dest_file.s, dest_file.len-1); fs_slurp_gd2h(&h, gd_in, length); log_debug(DEBUG_NOISE, h.dest, "received file from feed"); fs_close_file_write(&h); if (h.status & ST_INCOMPLETE) { die_log(0, dest_file.s, "short read from input stream", 0); } if (lx_striset(&data, "0", 1)) die_nomem(); header_setstr(blob_header_send, "Content-Length", data.s, data.len); resp_sendprefix(req, (h.status & ST_CREATED) ? HTERR_OK_CREAT : HTERR_OK_NOCNT); resp_sendheaders(req, blob_header_send); lx_free(&dest_dir); lx_free(&dest_file); lx_free(&data); }
void tagmsg(const char *dir, /* db base dir */ unsigned long msgnum, /* number of this message */ const char *seed, /* seed. NULL ok, but less entropy */ const char *action, /* to make it certain the cookie differs from*/ /* one used for a digest */ char *hashout, /* calculated hash goes here */ unsigned long bodysize, unsigned long chunk) /* This routine creates a cookie from num,seed and the */ /* list key and returns that cookie in hashout. The use of sender/num and */ /* first char of action is used to make cookie differ between messages, */ /* the key is the secret list key. The cookie will be inserted into */ /* table_cookie where table and other data is taken from dir/sql. We log */ /* arrival of the message (done=0). */ { PGresult *result; PGresult *result2; /* Need for dupicate check */ const char *table; const char *ret; std_tagmsg(msgnum,seed,action,hashout); if ((ret = opensub(dir,0,&table))) { if (*ret) strerr_die2x(111,FATAL,ret); return; /* no sql => success */ } else { if (chunk >= 53L) chunk = 0L; /* sanity */ /* INSERT INTO table_cookie (msgnum,cookie) VALUES (num,cookie) */ /* (we may have tried message before, but failed to complete, so */ /* ER_DUP_ENTRY is ok) */ if (!stralloc_copys(&line,"INSERT INTO ")) die_nomem(); if (!stralloc_cats(&line,table)) die_nomem(); if (!stralloc_cats(&line,"_cookie (msgnum,cookie,bodysize,chunk) VALUES (")) die_nomem(); if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,msgnum))) die_nomem(); if (!stralloc_cats(&line,",'")) die_nomem(); if (!stralloc_catb(&line,hashout,COOKIE)) die_nomem(); if (!stralloc_cats(&line,"',")) die_nomem(); if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,bodysize))) die_nomem(); if (!stralloc_cats(&line,",")) die_nomem(); if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,chunk))) die_nomem(); if (!stralloc_cats(&line,")")) die_nomem(); if (!stralloc_0(&line)) die_nomem(); result = PQexec(pgsql,line.s); if (result == NULL) strerr_die2x(111,FATAL,PQerrorMessage(pgsql)); if (PQresultStatus(result) != PGRES_COMMAND_OK) { /* Possible tuplicate */ if (!stralloc_copys(&line,"SELECT msgnum FROM ")) die_nomem(); if (!stralloc_cats(&line,table)) die_nomem(); if (!stralloc_cats(&line,"_cookie WHERE msgnum = ")) die_nomem(); if (!stralloc_catb(&line,strnum,fmt_ulong(strnum,msgnum))) die_nomem(); /* Query */ if (!stralloc_0(&line)) die_nomem(); result2 = PQexec(pgsql,line.s); if (result2 == NULL) strerr_die2x(111,FATAL,PQerrorMessage(pgsql)); if (PQresultStatus(result2) != PGRES_TUPLES_OK) strerr_die2x(111,FATAL,PQresultErrorMessage(result2)); /* No duplicate, return ERROR from first query */ if (PQntuples(result2)<1) strerr_die2x(111,FATAL,PQresultErrorMessage(result)); PQclear(result2); } PQclear(result); if (! (ret = logmsg(dir,msgnum,0L,0L,1))) return; /* log done=1*/ if (*ret) strerr_die2x(111,FATAL,ret); } return; }
int main() { buffer bin; buffer bout; struct prioq_elt pe; int fdoldmbox; int fdnewmbox; int fd; int match; int fdlock; umask(077); mbox = env_get("MAIL"); if (!mbox) strerr_die2x(111,FATL,"MAIL not set"); mboxtmp = env_get("MAILTMP"); if (!mboxtmp) strerr_die2x(111,FATL,"MAILTMP not set"); if (maildir_chdir() == -1) strerr_die1(111,FATAL,&maildir_chdir_err); maildir_clean(&filenames); if (maildir_scan(&pq,&filenames,1,1) == -1) strerr_die1(111,FATAL,&maildir_scan_err); if (!prioq_min(&pq,&pe)) _exit(0); /* nothing new */ fdlock = open_append(mbox); if (fdlock == -1) strerr_die4sys(111,FATL,"unable to lock ",mbox,": "); if (lock_ex(fdlock) == -1) strerr_die4sys(111,FATL,"unable to lock ",mbox,": "); fdoldmbox = open_read(mbox); if (fdoldmbox == -1) strerr_die4sys(111,FATL,"unable to read ",mbox,": "); fdnewmbox = open_trunc(mboxtmp); if (fdnewmbox == -1) strerr_die4sys(111,FATL,"unable to create ",mboxtmp,": "); buffer_init(&bin,read,fdoldmbox,inbuf,sizeof(inbuf)); buffer_init(&bout,write,fdnewmbox,outbuf,sizeof(outbuf)); switch(buffer_copy(&bout,&bin)) { case -2: strerr_die4sys(111,FATL,"unable to read ",mbox,": "); case -3: strerr_die4sys(111,FATL,"unable to write to ",mboxtmp,": "); } while (prioq_min(&pq,&pe)) { prioq_delmin(&pq); if (!prioq_insert(&pq2,&pe)) die_nomem(); fd = open_read(filenames.s + pe.id); if (fd == -1) strerr_die4sys(111,FATL,"unable to read $MAILDIR/",filenames.s + pe.id,": "); buffer_init(&bin,read,fd,inbuf,sizeof(inbuf)); if (getln(&bin,&line,&match,'\n') != 0) strerr_die4sys(111,FATL,"unable to read $MAILDIR/",filenames.s + pe.id,": "); if (!stralloc_copys(&ufline,"From XXX ")) die_nomem(); if (match) if (stralloc_starts(&line,"Return-Path: <")) { if (line.s[14] == '>') { if (!stralloc_copys(&ufline,"From MAILER-DAEMON ")) die_nomem(); } else { int i; if (!stralloc_ready(&ufline,line.len)) die_nomem(); if (!stralloc_copys(&ufline,"From ")) die_nomem(); for (i = 14;i < line.len - 2;++i) if ((line.s[i] == ' ') || (line.s[i] == '\t')) ufline.s[ufline.len++] = '-'; else { ufline.s[ufline.len++] = line.s[i]; if (!stralloc_cats(&ufline," ")) die_nomem(); } } } if (!stralloc_cats(&ufline,myctime(pe.dt))) die_nomem(); if (buffer_put(&bout,ufline.s,ufline.len) == -1) strerr_die4sys(111,FATAL,"unable to write to ",mboxtmp,": "); while (match && line.len) { if (gfrom(line.s,line.len)) if (buffer_puts(&bout,">") == -1) strerr_die4sys(111,FATL,"unable to write to ",mboxtmp,": "); if (buffer_put(&bout,line.s,line.len) == -1) strerr_die4sys(111,FATL,"unable to write to ",mboxtmp,": "); if (!match) { if (buffer_puts(&bout,"\n") == -1) strerr_die4sys(111,FATL,"unable to write to ",mboxtmp,": "); break; } if (getln(&bin,&line,&match,'\n') != 0) strerr_die4sys(111,FATL,"unable to read $MAILDIR/",filenames.s + pe.id,": "); } if (buffer_puts(&bout,"\n")) strerr_die4sys(111,FATL,"unable to write to ",mboxtmp,": "); close(fd); } if (buffer_flush(&bout) == -1) strerr_die4sys(111,FATL,"unable to write to ",mboxtmp,": "); if (fsync(fdnewmbox) == -1) strerr_die4sys(111,FATL,"unable to write to ",mboxtmp,": "); if (close(fdnewmbox) == -1) /* NFS dorks */ strerr_die4sys(111,FATL,"unable to write to ",mboxtmp,": "); if (rename(mboxtmp,mbox) == -1) strerr_die6(111,FATL,"unable to move ",mboxtmp," to ",mbox,": ",&strerr_sys); while (prioq_min(&pq2,&pe)) { prioq_delmin(&pq2); if (unlink(filenames.s + pe.id) == -1) strerr_warn4(WARNING,"$MAILDIR/",filenames.s + pe.id," will be delivered twice; unable to unlink: ",&strerr_sys); } return(0); }
void connection_accept(int c) { int ac; const char **run; const char *args[4]; char *ip =(char*)&socka.sin_addr; remote_ip[ipsvd_fmt_ip(remote_ip, ip)] =0; if (verbose) { out(INFO); out("pid "); bufnum[fmt_ulong(bufnum, getpid())] =0; out(bufnum); out(" from "); outfix(remote_ip); flush("\n"); } remote_port[ipsvd_fmt_port(remote_port, (char*)&socka.sin_port)] =0; if (lookuphost) { if (ipsvd_hostname(&remote_hostname, ip, paranoid) == -1) warn2("unable to look up hostname", remote_ip); if (! stralloc_0(&remote_hostname)) drop_nomem(); } socka_size =sizeof(socka); if (getsockname(c, (struct sockaddr*)&socka, &socka_size) == -1) drop("unable to get local address"); if (! local_hostname.len) { if (dns_name4(&local_hostname, (char*)&socka.sin_addr) == -1) drop("unable to look up local hostname"); if (! stralloc_0(&local_hostname)) die_nomem(); } local_ip[ipsvd_fmt_ip(local_ip, (char*)&socka.sin_addr)] =0; if (ucspi) ucspi_env(); if (instructs) { ac =ipsvd_check(iscdb, &inst, &match, (char*)instructs, remote_ip, remote_hostname.s, timeout); if (ac == -1) drop2("unable to check inst", remote_ip); if (ac == IPSVD_ERR) drop2("unable to read", (char*)instructs); } else ac =IPSVD_DEFAULT; if (phccmax) { if (phcc > phccmax) { ac =IPSVD_DENY; if (phccmsg) { ndelay_on(c); if (write(c, phccmsg, str_len(phccmsg)) == -1) warn("unable to write concurrency message"); } } if (verbose) { bufnum[fmt_ulong(bufnum, getpid())] =0; out(INFO); out("concurrency "); out(bufnum); out(" "); outfix(remote_ip); out(" "); bufnum[fmt_ulong(bufnum, phcc)] =0; out(bufnum); out("/"); bufnum[fmt_ulong(bufnum, phccmax)] =0; out(bufnum); out("\n"); } } if (verbose) { out(INFO); switch(ac) { case IPSVD_DENY: out("deny "); break; case IPSVD_DEFAULT: case IPSVD_INSTRUCT: out("start "); break; case IPSVD_EXEC: out("exec "); break; } bufnum[fmt_ulong(bufnum, getpid())] =0; out(bufnum); out(" "); outfix(local_hostname.s); out(":"); out(local_ip); out(" :"); outfix(remote_hostname.s); out(":"); outfix(remote_ip); out(":"); outfix(remote_port); if (instructs) { out(" "); if (iscdb) { out((char*)instructs); out("/"); } outfix(match.s); if(inst.s && inst.len && (verbose > 1)) { out(": "); outinst(&inst); } } flush("\n"); } if (ac == IPSVD_DENY) { close(c); _exit(100); } if (ac == IPSVD_EXEC) { args[0] ="/bin/sh"; args[1] ="-c"; args[2] =inst.s; args[3] =0; run =args; } else run =prog; if ((fd_move(0, c) == -1) || (fd_copy(1, 0) == -1)) drop("unable to set filedescriptor"); sig_uncatch(sig_term); sig_uncatch(sig_pipe); sig_uncatch(sig_child); sig_unblock(sig_child); #ifdef SSLSVD pid =getpid(); id[fmt_ulong(id, pid)] =0; ssl_io(0, run); #else pathexec(run); #endif drop2("unable to run", (char *)*prog); }
void msgout(unsigned long msg,char format) /* Outputs message (everything that's needed per message) */ { int fd; unsigned int len; if (!stralloc_copys(&fn,"archive/")) die_nomem(); len = fmt_ulong(strnum, msg / 100); if (!stralloc_catb(&fn,strnum,len)) die_nomem(); if (!stralloc_cats(&fn,"/")) die_nomem(); len = fmt_uint0(strnum, (unsigned int) (msg % 100),2); if (!stralloc_catb(&fn,strnum,len)) die_nomem(); if (!stralloc_0(&fn)) die_nomem(); switch(format) { case MIME: case VIRGIN: case NATIVE: case MIXED: fd = open_read(fn.s); if (fd == -1) { if (errno != error_noent) strerr_die2sys(111,FATAL,MSG1(ERR_OPEN,fn.s)); else mime_getbad(msg); } else if (fstat(fd,&st) == -1 || (!(st.st_mode & 0100))) { close(fd); mime_getbad(msg); } else { hdr_boundary(0); hdr_ctype(CTYPE_MESSAGE); qmail_puts(&qq,"Content-Disposition: inline; filename=\""); qmail_put(&qq,listname.s,listname.len); qmail_puts(&qq,"_"); qmail_put(&qq,strnum,fmt_ulong(strnum,msg)); qmail_puts(&qq,".ezm\"\n\n"); copymsg(fd,format); close(fd); } break; case RFC1153: fd = open_read(fn.s); if (fd == -1) { if (errno != error_noent) strerr_die2sys(111,FATAL,MSG1(ERR_OPEN,fn.s)); else { qmail_puts(&qq,"\n== "); qmail_put(&qq,strnum,fmt_ulong(strnum,msg)); qmail_puts(&qq," ==\n\n"); copy(&qq,"text/get-bad",flagcd); } } else { if (fstat(fd,&st) == -1 || (!(st.st_mode & 0100))) { close(fd); qmail_puts(&qq,"\n== "); qmail_put(&qq,strnum,fmt_ulong(strnum,msg)); qmail_puts(&qq," ==\n\n"); copy(&qq,"text/get-bad",flagcd); } else { copymsg(fd,format); close(fd); } } qmail_puts(&qq,"\n------------------------------\n\n"); break; default: strerr_die2x(100,FATAL,"Program error: Unrecognized format in msgout"); break; } }
int main(int argc, char **argv) { int opt; char *user =0; char *host; unsigned long port; int pid; int s; int conn; int delim; progname =*argv; phccmax =0; #ifdef SSLSVD while ((opt =getopt(argc, (const char **)argv, "c:C:i:x:u:l:Eb:hpt:vVU:/:Z:K:")) != opteof) { #else while ((opt =getopt(argc, (const char **)argv, "c:C:i:x:u:l:Eb:hpt:vV")) != opteof) { #endif switch(opt) { case 'c': scan_ulong(optarg, &cmax); if (cmax < 1) usage(); break; case 'C': delim =scan_ulong(optarg, &phccmax); if (phccmax < 1) usage(); if (optarg[delim] == ':') { if (ipsvd_fmt_msg(&msg, optarg +delim +1) == -1) die_nomem(); if (! stralloc_0(&msg)) die_nomem(); phccmsg =msg.s; } break; case 'i': if (instructs) usage(); instructs =optarg; break; case 'x': if (instructs) usage(); instructs =optarg; iscdb =1; break; case 'u': user =(char*)optarg; break; case 'l': if (! stralloc_copys(&local_hostname, optarg)) die_nomem(); if (! stralloc_0(&local_hostname)) die_nomem(); break; case 'E': ucspi =0; break; case 'b': scan_ulong(optarg, &backlog); break; case 'h': lookuphost =1; break; case 'p': lookuphost =1; paranoid =1; break; case 't': scan_ulong(optarg, &timeout); break; case 'v': ++verbose; break; #ifdef SSLSVD case 'U': ssluser =(char*)optarg; break; case '/': root =(char*)optarg; break; case 'Z': cert =(char*)optarg; break; case 'K': key =(char*)optarg; break; #endif case 'V': strerr_warn1(VERSION, 0); case '?': usage(); } } argv +=optind; if (! argv || ! *argv) usage(); host =*argv++; if (! argv || ! *argv) usage(); local_port =*argv++; if (! argv || ! *argv) usage(); prog =(const char **)argv; if (phccmax > cmax) phccmax =cmax; if (user) if (! uidgids_get(&ugid, user)) { if (errno) strerr_die4sys(111, FATAL, "unable to get user/group: ", user, ": "); strerr_die3x(100, FATAL, "unknown user/group: ", user); } #ifdef SSLSVD svuser =user; client =0; if ((getuid() == 0) && (! ssluser)) strerr_die2x(100, FATAL, "-U ssluser must be set when running as root"); if (ssluser) if (! uidgids_get(&sslugid, ssluser)) { if (errno) strerr_die4sys(111, FATAL, "unable to get user/group: ", ssluser, ": "); strerr_die3x(100, FATAL, "unknown user/group: ", ssluser); } if (! cert) cert ="./cert.pem"; if (! key) key =cert; if (matrixSslOpen() < 0) fatal("unable to initialize ssl"); if (matrixSslReadKeys(&keys, cert, key, 0, ca) < 0) { if (client) fatal("unable to read cert, key, or ca file"); fatal("unable to read cert or key file"); } if (matrixSslNewSession(&ssl, keys, 0, SSL_FLAGS_SERVER) < 0) strerr_die2x(111, FATAL, "unable to create ssl session"); #endif dns_random_init(seed); sig_block(sig_child); sig_catch(sig_child, sig_child_handler); sig_catch(sig_term, sig_term_handler); sig_ignore(sig_pipe); if (phccmax) if (ipsvd_phcc_init(cmax) == -1) die_nomem(); if (str_equal(host, "")) host ="0.0.0.0"; if (str_equal(host, "0")) host ="0.0.0.0"; if (! ipsvd_scan_port(local_port, "tcp", &port)) strerr_die3x(100, FATAL, "unknown port number or name: ", local_port); if (! stralloc_copys(&sa, host)) die_nomem(); if ((dns_ip4(&ips, &sa) == -1) || (ips.len < 4)) if (dns_ip4_qualify(&ips, &fqdn, &sa) == -1) fatal2("unable to look up ip address", host); if (ips.len < 4) strerr_die3x(100, FATAL, "unable to look up ip address: ", host); ips.len =4; if (! stralloc_0(&ips)) die_nomem(); local_ip[ipsvd_fmt_ip(local_ip, ips.s)] =0; if (! lookuphost) { if (! stralloc_copys(&remote_hostname, "")) die_nomem(); if (! stralloc_0(&remote_hostname)) die_nomem(); } if ((s =socket_tcp()) == -1) fatal("unable to create socket"); if (socket_bind4_reuse(s, ips.s, port) == -1) fatal("unable to bind socket"); if (listen(s, backlog) == -1) fatal("unable to listen"); ndelay_off(s); #ifdef SSLSVD #else if (user) { /* drop permissions */ if (setgroups(ugid.gids, ugid.gid) == -1) fatal("unable to set groups"); if (setgid(*ugid.gid) == -1) fatal("unable to set gid"); if (prot_uid(ugid.uid) == -1) fatal("unable to set uid"); } #endif close(0); if (verbose) { out(INFO); out("listening on "); outfix(local_ip); out(":"); outfix(local_port); #ifdef SSLSVD #else if (user) { bufnum[fmt_ulong(bufnum, (unsigned long)ugid.uid)] =0; out(", uid "); out(bufnum); bufnum[fmt_ulong(bufnum, (unsigned long)ugid.gid)] =0; out(", gid "); out(bufnum); } #endif flush(", starting.\n"); } for (;;) { while (cnum >= cmax) sig_pause(); socka_size =sizeof(socka); sig_unblock(sig_child); conn =accept(s, (struct sockaddr *)&socka, &socka_size); sig_block(sig_child); if (conn == -1) { if (errno != error_intr) warn("unable to accept connection"); continue; } cnum++; if (verbose) connection_status(); if (phccmax) phcc =ipsvd_phcc_add((char*)&socka.sin_addr); if ((pid =fork()) == -1) { warn2("drop connection", "unable to fork"); close(conn); continue; } if (pid == 0) { /* child */ close(s); #ifdef SSLSVD if (*progname) *progname ='\\'; #endif connection_accept(conn); } if (phccmax) ipsvd_phcc_setpid(pid); close(conn); } _exit(0); }
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 die_html(int hterr, char *arg_info, char *log_info, char *user_comment) { /* ignore all user-supplied data on error, including user-supplied req. * XXX: breaks spec? */ struct reqinfo req = { {0}, {0}, 1, 0 }; lx_s body = {0}, data = {0}; int err = (hterr & ERRNO) ? errno : 0; int hterridx = hterr & (~ERRNO); sendfmt(f_fatal, arg_info, log_info, err ? strerror(err) : 0); if (stage & SENT_PREFIX) { /* oh, well. Prefix already sent; may as well not supply user info * now, since we'd just corrupt the data already given. */ goto JUSTDIE; } header_reinit(&blob_header_send); header_set_default(hterridx); /* RFC-compliance means that we gather this regardless of whether we * send the data. I guess i care. */ if (lx_stradd(&body, "<html>\r\n" " <head>\r\n" " <title>")) die_nomem(); if (lx_stradd(&body, user_comment)) die_nomem(); if (lx_stradd(&body, "</title>\r\n" " </head>\r\n" " <body>")) die_nomem(); if (lx_stradd(&body, user_comment)) die_nomem(); if (lx_stradd(&body, "</body>\r\n" "</html>\r\n")) die_nomem(); if (lx_strset(&data, "text/html")) die_nomem(); header_setstr(blob_header_send, "Content-Type", data.s, data.len); data.len = 0; if (lx_straddulong(&data, body.len, 10)) die_nomem(); header_setstr(blob_header_send, "Content-Length", data.s, data.len); resp_sendprefix(&req, hterridx); resp_sendheaders(&req, blob_header_send); if (!global_flags.header_only) (void) lx_gdstrput(gd_out, &body); JUSTDIE: (void) lx_gdflush(gd_out); _exit(1); }
void doit(char *dir) { struct stat st; int r; int fd; const char *x; const char *fntemp; char status[40]; buffer_puts(&b,dir); buffer_puts(&b,": "); if (chdir(dir) == -1) { x = error_str(errno); buffer_puts(&b,"unable to chdir: "); buffer_puts(&b,x); return; } if (!svpath_init()) { strerr_warn4(WARNING,"unable to set up control path for ",dir,": ",&strerr_sys); return; } normallyup = 0; if (stat("down",&st) == -1) { if (errno != error_noent) { x = error_str(errno); buffer_puts(&b,"unable to stat down: "); buffer_puts(&b,x); return; } normallyup = 1; } if ((fntemp = svpath_make("/ok")) == 0) die_nomem(); fd = open_write(fntemp); if (fd == -1) { if (errno == error_nodevice) { buffer_puts(&b,"supervise not running"); return; } x = error_str(errno); buffer_puts(&b,"unable to open "); buffer_puts(&b,fntemp); buffer_puts(&b,": "); buffer_puts(&b,x); return; } close(fd); if ((fntemp = svpath_make("/status")) == 0) die_nomem(); fd = open_read(fntemp); if (fd == -1) { x = error_str(errno); buffer_puts(&b,"unable to open "); buffer_puts(&b,fntemp); buffer_puts(&b,": "); buffer_puts(&b,x); return; } r = buffer_unixread(fd,status,sizeof status); close(fd); if (r < 18) { if (r == -1) x = error_str(errno); else x = "bad format"; buffer_puts(&b,"unable to read "); buffer_puts(&b,fntemp); buffer_puts(&b,": "); buffer_puts(&b,x); return; } showstatus(status,r); if (r >= 20+18) { buffer_puts(&b,"\n"); buffer_puts(&b,dir); buffer_puts(&b," log: "); showstatus(status+20,r-20); } }
void main(int argc,char **argv) { DIR *bouncedir, *bsdir, *hdir; direntry *d, *ds; unsigned long bouncedate; unsigned long bouncetimeout = BOUNCE_TIMEOUT; unsigned long lockout = 0L; unsigned long ld; unsigned long ddir,dfile; int fdlock,fd; int opt; char ch; (void) umask(022); sig_pipeignore(); when = (unsigned long) now(); while ((opt = getopt(argc,argv,"dDl:t:vV")) != opteof) switch(opt) { case 'd': flagdig = 1; break; case 'D': flagdig = 0; break; case 'l': if (optarg) { /* lockout in seconds */ (void) scan_ulong(optarg,&lockout); } break; case 't': if (optarg) { /* bouncetimeout in days */ (void) scan_ulong(optarg,&bouncetimeout); bouncetimeout *= 3600L * 24L; } break; case 'v': case 'V': strerr_die2x(0, "ezmlm-warn version: ",auto_version); default: die_usage(); } startup(dir = argv[optind]); load_config(dir); getconf_ulong(©lines,"copylines",0,dir); if (flagdig) { if (!stralloc_copys(&digdir,dir)) die_nomem(); if (!stralloc_cats(&digdir,"/digest")) die_nomem(); if (!stralloc_0(&digdir)) die_nomem(); workdir = digdir.s; } else workdir = dir; if (!stralloc_copys(&fnlastd,workdir)) die_nomem(); if (!stralloc_cats(&fnlastd,"/bounce/lastd")) die_nomem(); if (!stralloc_0(&fnlastd)) die_nomem(); if (slurp(fnlastd.s,&lastd,16) == -1) /* last time d was scanned */ strerr_die4sys(111,FATAL,ERR_READ,fnlastd.s,": "); if (!stralloc_0(&lastd)) die_nomem(); (void) scan_ulong(lastd.s,&ld); if (!lockout) lockout = bouncetimeout / 50; /* 5.6 h for default timeout */ if (ld + lockout > when && ld < when) _exit(0); /* exit silently. Second check is to prevent lockup */ /* if lastd gets corrupted */ if (!stralloc_copy(&fnlasth,&fnlastd)) die_nomem(); fnlasth.s[fnlasth.len - 2] = 'h'; /* bad, but feels good ... */ if (flagdig) if (!stralloc_cats(&outlocal,"-digest")) die_nomem(); ddir = when / 10000; dfile = when - 10000 * ddir; if (!stralloc_copys(&line,workdir)) die_nomem(); if (!stralloc_cats(&line,"/lockbounce")) die_nomem(); if (!stralloc_0(&line)) die_nomem(); fdlock = lockfile(line.s); if (!stralloc_copys(&line,workdir)) die_nomem(); if (!stralloc_cats(&line,"/bounce/d")) die_nomem(); if (!stralloc_0(&line)) die_nomem(); bouncedir = opendir(line.s); if (!bouncedir) { if (errno != error_noent) strerr_die4sys(111,FATAL,ERR_OPEN,line.s,": "); else _exit(0); /* no bouncedir - no bounces! */ } while ((d = readdir(bouncedir))) { /* dxxx/ */ if (str_equal(d->d_name,".")) continue; if (str_equal(d->d_name,"..")) continue; scan_ulong(d->d_name,&bouncedate); /* since we do entire dir, we do files that are not old enough. */ /* to not do this and accept a delay of 10000s (2.8h) of the oldest */ /* bounce we add to bouncedate. We don't if bouncetimeout=0 so that */ /* that setting still processes _all_ bounces. */ if (bouncetimeout) ++bouncedate; if (when >= bouncedate * 10000 + bouncetimeout) { if (!stralloc_copys(&bdname,workdir)) die_nomem(); if (!stralloc_cats(&bdname,"/bounce/d/")) die_nomem(); if (!stralloc_cats(&bdname,d->d_name)) die_nomem(); if (!stralloc_0(&bdname)) die_nomem(); bsdir = opendir(bdname.s); if (!bsdir) { if (errno != error_notdir) strerr_die4sys(111,FATAL,ERR_OPEN,bdname.s,":y "); else { /* leftover nnnnn_dmmmmm file */ if (unlink(bdname.s) == -1) strerr_die4sys(111,FATAL,ERR_DELETE,bdname.s,": "); continue; } } while ((ds = readdir(bsdir))) { /* dxxxx/yyyy */ if (str_equal(ds->d_name,".")) continue; if (str_equal(ds->d_name,"..")) continue; if (!stralloc_copy(&fn,&bdname)) die_nomem(); /* '\0' at end */ fn.s[fn.len - 1] = '/'; if (!stralloc_cats(&fn,ds->d_name)) die_nomem(); if (!stralloc_0(&fn)) die_nomem(); if ((ds->d_name[0] == 'd') || (ds->d_name[0] == 'w')) doit(ds->d_name[0] == 'w'); else /* other stuff is junk */ if (unlink(fn.s) == -1) strerr_die4sys(111,FATAL,ERR_DELETE,fn.s,": "); } closedir(bsdir); if (rmdir(bdname.s) == -1) /* the directory itself */ if (errno != error_noent) strerr_die4sys(111,FATAL,ERR_DELETE,bdname.s,": "); } } closedir(bouncedir); if (!stralloc_copy(&line,&fnlastd)) die_nomem(); line.s[line.len - 2] = 'D'; fd = open_trunc(line.s); /* write lastd. Do safe */ /* since we read before lock*/ if (fd == -1) strerr_die4sys(111,FATAL,ERR_OPEN,line.s,": "); substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf)); if (substdio_put(&ssout,strnum,fmt_ulong(strnum,when)) == -1) strerr_die4sys(111,FATAL,ERR_WRITE,line.s,": "); if (substdio_put(&ssout,"\n",1) == -1) /* prettier */ strerr_die4sys(111,FATAL,ERR_WRITE,line.s,": "); if (substdio_flush(&ssout) == -1) strerr_die4sys(111,FATAL,ERR_FLUSH,line.s,": "); if (fsync(fd) == -1) strerr_die4sys(111,FATAL,ERR_SYNC,line.s,": "); if (close(fd) == -1) strerr_die4sys(111,FATAL,ERR_CLOSE,line.s,": "); if (rename(line.s,fnlastd.s) == -1) strerr_die4sys(111,FATAL,ERR_MOVE,fnlastd.s,": "); /* no need to do h dir cleaning more than */ /* once per 1-2 days (17-30 days for all) */ if (stat(fnlasth.s,&st) == -1) { if (errno != error_noent) strerr_die4sys(111,FATAL,ERR_STAT,fnlasth.s,": "); } else if (when < (unsigned long)st.st_mtime + 100000 && when > (unsigned long)st.st_mtime) _exit(0); /* 2nd comp to guard against corruption */ if (slurp(fnlasth.s,&lasth,16) == -1) /* last h cleaned */ strerr_die4sys(111,FATAL,ERR_READ,fnlasth.s,": "); if (!stralloc_0(&lasth)) die_nomem(); ch = lasth.s[0]; /* clean h */ if (ch >= 'a' && ch <= 'o') ++ch; else ch = 'a'; lasth.s[0] = ch; if (!stralloc_copys(&line,workdir)) die_nomem(); if (!stralloc_cats(&line,"/bounce/h/")) die_nomem(); if (!stralloc_catb(&line,lasth.s,1)) die_nomem(); if (!stralloc_0(&line)) die_nomem(); hdir = opendir(line.s); /* clean ./h/xxxxxx */ if (!hdir) { if (errno != error_noent) strerr_die4sys(111,FATAL,ERR_OPEN,line.s,": "); } else { while ((d = readdir(hdir))) { if (str_equal(d->d_name,".")) continue; if (str_equal(d->d_name,"..")) continue; if (!stralloc_copys(&fn,line.s)) die_nomem(); if (!stralloc_append(&fn,"/")) die_nomem(); if (!stralloc_cats(&fn,d->d_name)) die_nomem(); if (!stralloc_0(&fn)) die_nomem(); if (stat(fn.s,&st) == -1) { if (errno == error_noent) continue; strerr_die4sys(111,FATAL,ERR_STAT,fn.s,": "); } if (when > st.st_mtime + 3 * bouncetimeout) if (unlink(fn.s) == -1) strerr_die4sys(111,FATAL,ERR_DELETE,fn.s,": "); } closedir(hdir); } fd = open_trunc(fnlasth.s); /* write lasth */ if (fd == -1) strerr_die4sys(111,FATAL,ERR_OPEN,fnlasth.s,": "); substdio_fdbuf(&ssout,write,fd,outbuf,sizeof(outbuf)); if (substdio_put(&ssout,lasth.s,1) == -1) strerr_die4sys(111,FATAL,ERR_OPEN,fnlasth.s,": "); if (substdio_put(&ssout,"\n",1) == -1) /* prettier */ strerr_die4sys(111,FATAL,ERR_OPEN,fnlasth.s,": "); if (substdio_flush(&ssout) == -1) strerr_die4sys(111,FATAL,ERR_OPEN,fnlasth.s,": "); (void) close(fd); /* no big loss. No reason to flush/sync */ /* See check of ld above to guard against */ /* it being corrupted and > when */ closesub(); _exit(0); }
const char *issub(const char *dir, const char *subdir, const char *userhost) /* Returns (char *) to match if userhost is in the subscriber database */ /* dir, 0 otherwise. dir is a base directory for a list and may NOT */ /* be NULL */ /* NOTE: The returned pointer is NOT VALID after a subsequent call to issub!*/ { MYSQL_RES *result; MYSQL_ROW row; const char *ret; const char *table; unsigned long *lengths; unsigned int j; if ((ret = opensub(dir,subdir,&table))) { if (*ret) strerr_die2x(111,FATAL,ret); return std_issub(dir,subdir,userhost); } else { /* SQL version */ /* SELECT address FROM list WHERE address = 'userhost' AND hash */ /* BETWEEN 0 AND 52. Without the hash restriction, we'd make it */ /* even easier to defeat. Just faking sender to the list name would*/ /* work. Since sender checks for posts are bogus anyway, I don't */ /* know if it's worth the cost of the "WHERE ...". */ if (!stralloc_copys(&addr,userhost)) die_nomem(); j = byte_rchr(addr.s,addr.len,'@'); if (j == addr.len) return 0; case_lowerb(addr.s + j + 1,addr.len - j - 1); if (!stralloc_copys(&line,"SELECT address FROM ")) die_nomem(); if (!stralloc_cats(&line,table)) die_nomem(); if (!stralloc_cats(&line," WHERE address = '")) die_nomem(); if (!stralloc_ready("ed,2 * addr.len + 1)) die_nomem(); if (!stralloc_catb(&line,quoted.s, mysql_escape_string(quoted.s,userhost,addr.len))) die_nomem(); if (!stralloc_cats(&line,"'")) die_nomem(); if (mysql_real_query(mysql,line.s,line.len)) /* query */ strerr_die2x(111,FATAL,mysql_error(mysql)); if (!(result = mysql_use_result(mysql))) strerr_die2x(111,FATAL,mysql_error(mysql)); row = mysql_fetch_row(result); ret = (char *) 0; if (!row) { /* we need to return the actual address as other */ /* dbs may accept user-*@host, but we still want */ /* to make sure to send to e.g the correct moderator*/ /* address. */ if (!mysql_eof(result)) strerr_die2x(111,FATAL,mysql_error(mysql)); } else { if (!(lengths = mysql_fetch_lengths(result))) strerr_die2x(111,FATAL,mysql_error(mysql)); if (!stralloc_copyb(&line,row[0],lengths[0])) die_nomem(); if (!stralloc_0(&line)) die_nomem(); ret = line.s; while ((row = mysql_fetch_row(result))); /* maybe not necessary */ mysql_free_result(result); } return ret; } }
void doit(int flagw) { unsigned int i; int fd; int match; int fdhash; const char *err; fd = open_read(fn.s); if (fd == -1) die_read(); substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf)); if (getln(&ssin,&addr,&match,'\0') == -1) die_read(); if (!match) { close(fd); return; } if (!issub(workdir,0,addr.s)) { close(fd); /*XXX*/unlink(fn.s); return; } cookie(hash,"",0,"",addr.s,""); if (!stralloc_copys(&fnhash,workdir)) die_nomem(); if (!stralloc_cats(&fnhash,"/bounce/h/")) die_nomem(); if (!stralloc_catb(&fnhash,hash,1)) die_nomem(); if (!stralloc_cats(&fnhash,"/h")) die_nomem(); if (!stralloc_catb(&fnhash,hash+1,COOKIE-1)) die_nomem(); if (!stralloc_0(&fnhash)) die_nomem(); if (qmail_open(&qq, (stralloc *) 0) == -1) strerr_die2sys(111,FATAL,ERR_QMAIL_QUEUE); hdr_add2("Mailing-List: ",mailinglist.s,mailinglist.len); if (listid.len > 0) hdr_add2("\nList-ID: ",listid.s,listid.len); hdr_datemsgid(now()); if (flagcd) { if (!stralloc_0(&line)) die_nomem(); } hdr_from("-help"); if (!quote2("ed,addr.s)) die_nomem(); hdr_add2("To: ",quoted.s,quoted.len); /* to accomodate transfer-encoding */ hdr_mime(flagcd ? CTYPE_MULTIPART : CTYPE_TEXT); hdr_listsubject1(flagw ? "probe from " : "warning from "); if (flagcd) { /* first part for QP/base64 multipart msg */ hdr_boundary(0); hdr_ctype(CTYPE_TEXT); hdr_transferenc(); } else qmail_puts(&qq,"\n"); copy(&qq,"text/top",flagcd); copy(&qq,flagw ? "text/bounce-probe" : "text/bounce-warn",flagcd); if (!flagw) { if (flagdig) copy(&qq,"text/dig-bounce-num",flagcd); else copy(&qq,"text/bounce-num",flagcd); if (!flagcd) { fdhash = open_read(fnhash.s); if (fdhash == -1) { if (errno != error_noent) strerr_die4sys(111,FATAL,ERR_OPEN,fnhash.s,": "); } else { substdio_fdbuf(&sstext,read,fdhash,textbuf,sizeof(textbuf)); for(;;) { if (getln(&sstext,&line,&match,'\n') == -1) strerr_die4sys(111,FATAL,ERR_READ,fnhash.s,": "); if (!match) break; code_qput(line.s,line.len); } } close(fdhash); } else { if (!stralloc_copys(&line,"")) die_nomem(); /* slurp adds! */ if (slurp(fnhash.s,&line,256) < 0) strerr_die4sys(111,FATAL,ERR_OPEN,fnhash.s,": "); code_qput(line.s,line.len); } } copy(&qq,"text/bounce-bottom",flagcd); if (flagcd) { if (flagcd == 'B') { encodeB("",0,&line,2); qmail_put(&qq,line.s,line.len); /* flush */ } hdr_boundary(0); hdr_ctype(CTYPE_MESSAGE); qmail_puts(&qq,"\n"); } if (qmail_copy(&qq,&ssin,copylines) < 0) die_read(); close(fd); if (flagcd) /* end multipart/mixed */ hdr_boundary(1); strnum[fmt_ulong(strnum,when)] = 0; cookie(hash,key.s,key.len,strnum,addr.s,flagw ? "P" : "W"); if (!stralloc_copy(&line,&outlocal)) die_nomem(); if (!stralloc_cats(&line,flagw ? "-return-probe-" : "-return-warn-")) die_nomem(); if (!stralloc_cats(&line,strnum)) die_nomem(); if (!stralloc_cats(&line,".")) die_nomem(); if (!stralloc_catb(&line,hash,COOKIE)) die_nomem(); if (!stralloc_cats(&line,"-")) die_nomem(); i = str_chr(addr.s,'@'); if (!stralloc_catb(&line,addr.s,i)) die_nomem(); if (addr.s[i]) { if (!stralloc_cats(&line,"=")) die_nomem(); if (!stralloc_cats(&line,addr.s + i + 1)) die_nomem(); } if (!stralloc_cats(&line,"@")) die_nomem(); if (!stralloc_cat(&line,&outhost)) die_nomem(); if (!stralloc_0(&line)) die_nomem(); qmail_from(&qq,line.s); qmail_to(&qq,addr.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_warn2("ezmlm-warn: info: qp ",strnum,0); if (!flagw) { if (unlink(fnhash.s) == -1) if (errno != error_noent) strerr_die4sys(111,FATAL,ERR_DELETE,fnhash.s,": "); } if (unlink(fn.s) == -1) strerr_die4sys(111,FATAL,ERR_DELETE,fn.s,": "); }
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 doio(void) { iopause_fd x[2]; struct taia deadline; struct taia now; struct taia timeout; if (! stralloc_ready(&encinbuf, bufsizein)) die_nomem(); encin.buf =encin.start =encin.end =encinbuf.s; encin.size =bufsizein; if (! stralloc_ready(&decinbuf, bufsizein)) die_nomem(); decin.buf =decin.start =decin.end =decinbuf.s; decin.size =bufsizein; if (! stralloc_ready(&encoubuf, bufsizeou)) die_nomem(); encou.buf =encou.start =encou.end =encoubuf.s; encou.size =bufsizeou; if (! stralloc_ready(&decoubuf, bufsizeou)) die_nomem(); decou.buf =decou.start =decou.end =decoubuf.s; decou.size =bufsizeou; if (client) { rc =matrixSslEncodeClientHello(ssl, &decou, 0); if (rc != 0) fatalx("unable to encode client hello"); if (write(fdstdou, decou.start, decou.end -decou.start) != (decou.end -decou.start)) fatal("unable to send client hello"); if (verbose > 2) info("sending client hello"); if (verbose > 2) infou("write bytes: ", decou.end -decou.start); bytesou +=decou.end -decou.start; decou.start =decou.end =decou.buf; } taia_now(&now); taia_uint(&timeout, handshake_timeout); taia_add(&timeout, &now, &timeout); for (;;) { iopause_fd *xx =x; int l =2; x[0].fd =encpipe[0]; x[0].events =IOPAUSE_READ; x[0].revents =0; x[1].fd =fdstdin; x[1].events =IOPAUSE_READ; x[1].revents =0; if ((x[0].fd == -1) || handshake) { --l; ++xx; } if (x[1].fd == -1) --l; if (! l) return; taia_now(&now); if (handshake) { if (taia_less(&timeout, &now)) { if (verbose) info("ssl handshake timeout, exit."); return; } deadline.sec =timeout.sec; deadline.nano =timeout.nano; deadline.atto =timeout.atto; } else { taia_uint(&deadline, 30); taia_add(&deadline, &now, &deadline); } iopause(xx, l, &deadline, &now); if (x[0].revents) encode(); if (x[1].revents) decode(); } }