int qldap_get_mailstore(qldap *q, stralloc *hd, stralloc *ms) { int r; /* * get and check the mailstores. * Both homedir and maildir are set from the three * values ~control/ldapmessagestore, homedirectory * and mailmessagestore. * ms is only filled with a value if both homedir * and maildir is used. */ r = qldap_get_attr(q, LDAP_HOMEDIR, hd, SINGLE_VALUE); if (r == NOSUCH) { if (!stralloc_copys(hd, "")) return ERRNO; } else if (r != OK) return r; if (0 < hd->len) { if (hd->s[0] != '/' || check_paths(hd->s) == 0) { /* probably some log warning would be good */ return ILLVAL; } } r = qldap_get_attr(q, LDAP_MAILSTORE, ms, SINGLE_VALUE); if (r == NOSUCH) { if (!stralloc_copys(ms, "")) return ERRNO; } else if (r != OK) return r; if (ms->len > 0) if (check_paths(ms->s) == 0) { /* probably some log warning would be good */ return ILLVAL; } if (hd->len > 0 && ms->len > 0) return OK; if (hd->len > 0) return OK; if (ms->len > 0) { if (ms->s[0] != '/') { if (default_messagestore.s == 0 || default_messagestore.len == 0) return ILLVAL; if (!stralloc_cat(hd, &default_messagestore)) return ERRNO; } if (!stralloc_cat(hd, ms)) return ERRNO; if (!stralloc_copys(ms, "")) return ERRNO; return OK; } return NEEDED; }
/* Searches the subscriber log and outputs via subwrite(s,len) any entry * that matches search. A '_' is search is a wildcard. Any other * non-alphanum/'.' char is replaced by a '_'. */ void sub_sql_searchlog(struct subdbinfo *info, const char *table, char *search, /* search string */ int subwrite()) /* output fxn */ { void *result; datetime_sec when; struct datetime dt; char date[DATE822FMT]; int nparams; char strnum[FMT_ULONG]; make_name(info,table?"_":0,table,0); /* SELECT (*) FROM list_slog WHERE fromline LIKE '%search%' OR address */ /* LIKE '%search%' ORDER BY tai; */ /* The '*' is formatted to look like the output of the non-mysql version */ /* This requires reading the entire table, since search fields are not */ /* indexed, but this is a rare query and time is not of the essence. */ stralloc_copys(&query,"SELECT "); stralloc_cats(&query,sql_searchlog_select_defn); stralloc_cats(&query," FROM "); stralloc_cat(&query,&name); stralloc_cats(&query,"_slog"); if (*search) { /* We can afford to wait for LIKE '%xx%' */ stralloc_copys(¶ms[0],search); stralloc_copys(¶ms[1],search); nparams = 2; stralloc_cats(&query," WHERE "); stralloc_cats(&query,sql_searchlog_where_defn); } else nparams = 0; /* ordering by tai which is an index */ stralloc_cats(&query," ORDER by tai"); result = sql_select(info,&query,nparams,params); while (sql_fetch_row(info,result,2,params)) { stralloc_0(¶ms[0]); (void)scan_ulong(params[0].s,&when); datetime_tai(&dt,when); stralloc_copyb(¶ms[0],date,date822fmt(date,&dt)-1); stralloc_cats(¶ms[0],": "); stralloc_catb(¶ms[0],strnum,fmt_ulong(strnum,when)); stralloc_cats(¶ms[0]," "); stralloc_cat(¶ms[0],¶ms[1]); if (subwrite(params[0].s,params[0].len) == -1) die_write(); } sql_free_result(info,result); }
/* Outputs all addresses in the table through subwrite. subwrite must be * a function returning >=0 on success, -1 on error, and taking * arguments (char* string, unsigned int length). It will be called once * per address and should take care of newline or whatever needed for * the output form. */ unsigned long sub_sql_putsubs(struct subdbinfo *info, const char *table, unsigned long hash_lo, unsigned long hash_hi, int subwrite()) /* write function. */ { void *result; unsigned long no = 0L; char strnum[FMT_ULONG]; stralloc_copyb(¶ms[0],strnum,fmt_ulong(strnum,hash_lo)); stralloc_copyb(¶ms[1],strnum,fmt_ulong(strnum,hash_hi)); make_name(info,table?"_":0,table,0); /* main query */ stralloc_copys(&query,"SELECT address FROM "); stralloc_cat(&query,&name); stralloc_cats(&query," WHERE "); stralloc_cats(&query,sql_putsubs_where_defn); result = sql_select(info,&query,2,params); no = 0; while (sql_fetch_row(info,result,1,&addr)) { if (subwrite(addr.s,addr.len) == -1) die_write(); no++; /* count for list-list fxn */ } sql_free_result(info,result); return no; }
int stralloc_copy(stralloc *x, stralloc *y) { if (!x || !y) { errno = EINVAL; return 0; } x->len = 0; return stralloc_cat(x, y); }
/* take an username and create a filename from it by prepending datadir, return it in \0 terminated tmpname */ void create_datafilename(stralloc *tmpname, stralloc *username) { /* create the filename in our datastructure */ if(!stralloc_copys(tmpname, datadir)) die_nomem(); if(!stralloc_cats(tmpname, "/")) die_nomem(); if(!stralloc_cat(tmpname, username)) die_nomem(); if(!stralloc_0(tmpname)) die_nomem(); }
void to_owner(void) { stralloc_copy(&owner,&outlocal); stralloc_cats(&owner,"-owner@"); stralloc_cat(&owner,&outhost); stralloc_0(&owner); qmail_to(&qq,owner.s); }
int main(int argc, const char ** argv ) { cmd = (char*) *argv++; --argc; section = (char*) *argv++; --argc; manual = (char*) *argv++; --argc; stralloc tempCMD1 = {0}; tempCMD1 = generateSearchCMD( manual,tempCMD1); int fd[2]; pipe(fd); int status; pid_t child; if ( (child = fork() ) < 0 ) { return 0; } else if( child == 0 ) { dup2(fd[1],1); close(fd[0]); close(fd[1]); system(tempCMD1.s); stralloc_free(&tempCMD1); exit(255); } else { stralloc tempCMD2 = {0}; ssize_t nbytes; char buff[64]; close(fd[1]); while(( nbytes = read(fd[0] , buff , sizeof buff )) > 0 ) { stralloc_catb( &tempCMD2 , buff , nbytes ); }; stralloc temp = {0}; stralloc_copys( &temp , cmdCAT ); stralloc_cat( &temp , &tempCMD2 ); char *t = temp.s; printManual(t); stralloc_free(&tempCMD2); stralloc_free(&temp); } close(fd[0]); close(fd[1]); wait(&status); return 0; }
int geton(const char *action) { const char *fl; int r; unsigned int i; unsigned char ch; fl = get_from(target.s,action); /* try to match up */ r = subscribe(workdir,target.s,1,fl,(*action == ACTION_RC[0]) ? "+mod" : "+",-1); if (flagdig == FLD_DENY || flagdig == FLD_ALLOW) strerr_die2x(0,INFO,MSG1(ERR_EXTRA_SUB,target.s)); switch (r) { case 1: qmail_puts(&qq,"List-Unsubscribe: <mailto:"); /*rfc2369 */ qmail_put(&qq,outlocal.s,outlocal.len); qmail_puts(&qq,"-unsubscribe-"); /* url-encode since verptarget is controlled by sender */ /* note &verptarget ends in '\0', hence len - 1! */ for (i = 0; i < verptarget.len - 1; i++) { ch = verptarget.s[i]; if (str_chr("\"?;<>&/:%+#",ch) < 10 || (ch <= ' ') || (ch & 0x80)) { urlstr[1] = hex[ch / 16]; urlstr[2] = hex[ch & 0xf]; qmail_put(&qq,urlstr,3); } else { qmail_put(&qq,verptarget.s + i, 1); } } qmail_puts(&qq,"@"); qmail_put(&qq,outhost.s,outhost.len); /* safe */ qmail_puts(&qq,">\n"); hdr_subject(MSG(SUB_WELCOME)); hdr_ctboundary(); stralloc_copy(&confirm,&outlocal); stralloc_cats(&confirm,"-unsubscribe-"); stralloc_cats(&confirm,verptarget.s); stralloc_append(&confirm,'@'); stralloc_cat(&confirm,&outhost); stralloc_0(&confirm); set_cpconfirm(confirm.s,outlocal.len); /* for !R in copy */ copy(&qq,"text/top",flagcd); copy_act("text/sub-ok"); break; default: if (str_start(action,ACTION_TC)) strerr_die2x(0,INFO,MSG(ERR_SUB_NOP)); hdr_subject(MSG(SUB_SUBSCRIBE_NOP)); hdr_ctboundary(); copy(&qq,"text/top",flagcd); copy_act("text/sub-nop"); break; } return r; }
int pathexec_env(const char *s,const char *t) { if (!s) return 1; if (!stralloc_copys(&tmp,s)) return 0; if (t) { if (!stralloc_cats(&tmp,"=")) return 0; if (!stralloc_cats(&tmp,t)) return 0; } if (!stralloc_0(&tmp)) return 0; return stralloc_cat(&plus,&tmp); }
/* usage: rellink "http://www.nytimes.com/2005/10/06/international/middleeast/06cnd-prexy.html?ex=1129262400&en=30e300dafe83d0fc&ei=5065&partner=MYWAY" downloaded-data.html */ int main(int argc,char* argv[]) { char* baseurl; char* map,* max,* x; struct stat ss; static stralloc sa; if (argc!=3) die(0,"usage: rellink http://base/url downloaded-data.html"); errmsg_iam("rellink"); baseurl=argv[1]; map=mmap_read_stat(argv[2],&ss); if (map==0) diesys(111,"open \"",argv[2],"\" failed"); max=map+ss.st_size; for (x=map; x<max; ) { stralloc tag; /* copy non-tag */ for (; x<max && *x!='<'; ++x) if (stralloc_append(&sa,x)==0) nomem: die(111,"out of memory"); if (x>=max) break; stralloc_copys(&tag,""); { int indq,insq,ok; indq=insq=ok=0; for (; x<max; ++x) { if (*x == '\'') insq^=1; else if (*x == '"') indq^=1; if (stralloc_append(&tag,x)==0) goto nomem; if (*x == '>' && !insq && !indq) { ok=1; ++x; break; } } if (ok) if (mangleurl(&tag,baseurl)==-1) goto nomem; } if (stralloc_cat(&sa,&tag)==0) goto nomem; } if (sa.len == ss.st_size && byte_equal(sa.s,ss.st_size,map)) return 0; munmap(map,ss.st_size); { struct utimbuf utb; int fd=open(argv[2],O_WRONLY|O_TRUNC,0600); if (fd==-1) die(111,"open(\"",argv[2],"\")"); write(fd,sa.s,sa.len); close(fd); utb.actime=ss.st_atime; utb.modtime=ss.st_mtime; utime(argv[2],&utb); } return 0; }
static int _issub(struct subdbinfo *info, const char *table, const char *userhost, stralloc *recorded) { sqlite3_stmt *stmt; unsigned int j; int res; /* 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_cat_table(&line,info,table)) die_nomem(); if (!stralloc_cats(&line," WHERE address LIKE '")) die_nomem(); if (!stralloc_cat(&line,&addr)) die_nomem(); if (!stralloc_cats(&line,"'")) die_nomem(); if (!stralloc_0(&line)) die_nomem(); if ((stmt = _sqlquery(info, &line)) == NULL) /* select */ strerr_die2x(111,FATAL,sqlite3_errmsg((sqlite3*)info->conn)); /* No data returned in QUERY */ res = sqlite3_step(stmt); if (res != SQLITE_ROW) { if (res != SQLITE_DONE) strerr_die2x(111,FATAL,sqlite3_errmsg((sqlite3*)info->conn)); sqlite3_finalize(stmt); return 0; } if (recorded) { if (!stralloc_copyb(recorded, (const char*)sqlite3_column_text(stmt, 0), sqlite3_column_bytes(stmt, 0))) die_nomem(); if (!stralloc_0(recorded)) die_nomem(); } sqlite3_finalize(stmt); return 1; }
void makeacthash(stralloc *act) /* act is expected to be -reject-ddddd.ttttt or -accept-ddddd.ttttt and * has to be 0-terminated. */ /* The routine will add .hash@outhost to act. act will NOT be 0-terminated */ { int d; d = 2 + str_chr(act->s + 1,'-'); cookie(hash,key.s,key.len,act->s + d,"","a"); *(act->s + act->len - 1) = '.'; /* we put a '.' Bad, but works */ if (!stralloc_catb(act,hash,COOKIE)) die_nomem(); if (!stralloc_cats(act,"@")) die_nomem(); if (!stralloc_cat(act,&outhost)) die_nomem(); }
int filter_start(stralloc *filter) { if (!stralloc_copys(filter, "")) return 0; if (objectclass.s != (char *)0 && objectclass.len != 0) { /* (&(objectclass=...)%searchfilter%) */ if (!stralloc_copys(filter, "(&(") || !stralloc_cats(filter, LDAP_OBJECTCLASS) || !stralloc_cats(filter, "=") || !stralloc_cat(filter, &objectclass) || !stralloc_cats(filter, ")")) return 0; } return 1; }
int sub_sql_issub(struct subdbinfo *info, const char *table, const char *userhost, stralloc *recorded) { unsigned int j; void *result; int ret; /* 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 ...". */ make_name(info,table?"_":0,table,0); /* Lower-case the domain portion */ stralloc_copys(&addr,userhost); j = byte_rchr(addr.s,addr.len,'@'); if (j == addr.len) return 0; case_lowerb(addr.s + j + 1,addr.len - j - 1); stralloc_copys(&query,"SELECT address FROM "); stralloc_cat(&query,&name); stralloc_cats(&query," WHERE "); stralloc_cats(&query,sql_issub_where_defn); result = sql_select(info,&query,1,&addr); if (!sql_fetch_row(info,result,1,&addr)) ret = 0; else { /* 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 (recorded != 0) { stralloc_copy(recorded,&addr); stralloc_0(recorded); } ret = 1; } sql_free_result(info,result); return ret; }
char * filter_objectclass(char *searchfilter) { if (searchfilter == (char *)0) return 0; if (objectclass.s == (char *)0 || objectclass.len == 0) return searchfilter; /* (&(objectclass=...)%searchfilter%) */ if (!stralloc_copys(&ocfilter, "(&(") || !stralloc_cats(&ocfilter, LDAP_OBJECTCLASS) || !stralloc_cats(&ocfilter, "=") || !stralloc_cat(&ocfilter, &objectclass) || !stralloc_cats(&ocfilter, ")") || !stralloc_cats(&ocfilter, searchfilter) || !stralloc_cats(&ocfilter, ")") || !stralloc_0(&ocfilter)) return 0; return ocfilter.s; }
int control_readfile(stralloc *sa,char *fn,int flagme) { buffer b; int fd; int match; if (!stralloc_copys(sa,"")) return -1; fd = open_read(fn); if (fd == -1) { if (errno == ENOENT) { if (flagme && meok) { if (!stralloc_copy(sa,&me)) return -1; if (!stralloc_0(sa)) return -1; return 1; } return 0; } return -1; } buffer_init(&b,read,fd,inbuf,sizeof(inbuf)); for (;;) { if (getln(&b,&line,&match,'\n') == -1) break; if (!match && !line.len) { close(fd); return 1; } striptrailingwhitespace(&line); if (!stralloc_0(&line)) break; if (line.s[0]) if (line.s[0] != '#') if (!stralloc_cat(sa,&line)) break; if (!match) { close(fd); return 1; } } close(fd); return -1; }
void doconfirm(const char *act) /* This should only be called with valid act for sub/unsub confirms. If act */ /* is not ACTION_[RST]C, it is assumed to be an unsubscribe conf.*/ /* "act" is the first letter of desired confirm request only as STRING! */ { strnum[fmt_ulong(strnum,when)] = 0; cookie(hash,key.s,key.len-flagdig,strnum,target.s,act); stralloc_copy(&confirm,&outlocal); stralloc_append(&confirm,'-'); stralloc_catb(&confirm,act,1); stralloc_cats(&confirm,"c."); stralloc_cats(&confirm,strnum); stralloc_append(&confirm,'.'); stralloc_catb(&confirm,hash,COOKIE); stralloc_append(&confirm,'-'); stralloc_cats(&confirm,verptarget.s); stralloc_append(&confirm,'@'); stralloc_cat(&confirm,&outhost); stralloc_0(&confirm); set_cpconfirm(confirm.s,outlocal.len); /* for copy */ set_cpaction(act); set_cphash(hash); set_cpwhen(when); qmail_puts(&qq,"Reply-To: "); quote2("ed,confirm.s); qmail_put(&qq,quoted.s,quoted.len); qmail_puts(&qq,"\n"); hdr_subject((*act == ACTION_SC[0]) ? MSG(SUB_USR_SUBSCRIBE) : (*act == ACTION_UC[0]) ? MSG(SUB_USR_UNSUBSCRIBE) : (*act == ACTION_TC[0] || *act == ACTION_RC[0]) ? MSG(SUB_MOD_SUBSCRIBE) : MSG(SUB_MOD_UNSUBSCRIBE)); hdr_ctboundary(); copy(&qq,"text/top",flagcd); }
/* convert a query to a filename mainly by adding slashes instead of dots (this is an abstraction) and removing anything that is not a valid in DNS, which means A-Z and '-' */ int query2filename(char *q, stralloc *s) { int i; char *p; stralloc tmp = { 0 }; p = q; /* XXX: handling of uppercase/lowercase is missing */ while(*p) { stralloc_copy(&tmp, s); stralloc_copyb(s, "/", 1); stralloc_catb(s, p+1, (int) *p); /* Clean up string by changing all characters not allowed in dnsnames to '+'. We need to do tis to keep strange meta characters from slipping into syscalls when opening files changing directorys */ for(i = 1; i < s->len; i++) { if(!(((s->s[i] >= 'a') && (s->s[i] <= 'z')) || ((s->s[i] >= 'A') && (s->s[i] <= 'Z')) || ((s->s[i] >= '0') && (s->s[i] <= '9')) || s->s[i] == '-')) { s->s[i] = '+'; } } stralloc_cat(s, &tmp); p += ((int) *p) + 1; } stralloc_0(s); return s->len; }
static int mangleurl(stralloc* tag,const char* baseurl) { char* x; const char* y; static stralloc before,arg,after,tmp; int found; struct stat ss; found=0; if (stralloc_istag(tag,"a") || stralloc_istag(tag,"link")) found=1; else if (stralloc_istag(tag,"img") || stralloc_istag(tag,"frame")) found=2; if (!found) return 0; if (extractparam(tag,found==1?"href":"src",&before,&arg,&after)) { if (stralloc_starts(&arg,"/") || stralloc_starts(&arg,"http://") || stralloc_starts(&arg,"https://")) { canonicalize(&arg,baseurl); } else return 0; /* url was already relative */ if (stralloc_0(&arg)==0) return -1; stralloc_chop(&arg); x=arg.s+7; if (*x=='/') ++x; y=baseurl+7; if (*y=='/') ++y; /* now x is something like * "www.spiegel.de/img/0,1020,525770,00.jpg" * and baseurl is something like * "www.spiegel.de/panorama/0,1518,378421,00.html" * and we want to change x into "../img/0,1020,525770,00.jpg" */ if (stat(x,&ss)!=0) return 0; for (;;) { int i=str_chr(x,'/'); int j=str_chr(y,'/'); if (i>0 && i==j && byte_equal(x,i,y)) { x+=i+1; y+=i+1; while (*x=='/') ++x; while (*y=='/') ++y; } else break; } stralloc_zero(&tmp); for (;;) { int i=str_chr(y,'/'); if (y[i]=='/') { y+=i+1; while (*y=='/') ++y; if (stralloc_cats(&tmp,"../")==0) return -1; } else break; } { int i,needquote; for (i=needquote=0; x[i]; ++i) if (!isalnum(x[i]) && x[i]!='/' && x[i]!='_' && x[i]!='.') needquote=1; if (needquote) { if (stralloc_cats(&before,"\"")==0 || stralloc_cat(&before,&tmp)==0 || stralloc_cats(&before,x)==0 || stralloc_cats(&before,"\"")==0) return -1; } else if (stralloc_cat(&before,&tmp)==0 || stralloc_cats(&before,x)==0) return -1; } if (stralloc_cat(&before,&after)==0) return -1; if (stralloc_copy(tag,&before)==0) return -1; } return 0; }
static void do_edit(const char *action) { unsigned int i; unsigned int len; char ch; /* only remote admins and only if -e is specified may edit */ if (!flagedit || remote.s == 0) strerr_die2x(100,FATAL,MSG(ERR_NOT_AVAILABLE)); if (!ismod) strerr_die2x(100,FATAL,MSG(ERR_NOT_ALLOWED)); len = str_len(ACTION_EDIT); if (!case_starts(action,ACTION_EDIT)) len = str_len(ALT_EDIT); if (action[len]) { /* -edit.file, not just -edit */ if (action[len] != '.') strerr_die2x(100,FATAL,MSG(ERR_BAD_REQUEST)); showsend2("edit ",action+len+1); stralloc_copys(&fnedit,"text/"); stralloc_cats(&fnedit,action+len+1); stralloc_0(&fnedit); case_lowerb(fnedit.s,fnedit.len); i = 5; /* after the "text/" */ while ((ch = fnedit.s[i++])) { if (((ch > 'z') || (ch < 'a')) && (ch != '_')) strerr_die2x(100,FATAL,MSG(ERR_BAD_NAME)); if (ch == '_') fnedit.s[i-1] = '-'; } switch(slurp(fnedit.s,&text,1024)) { /* entire file! */ case -1: strerr_die2sys(111,FATAL,MSG1(ERR_READ,fnedit.s)); case 0: strerr_die5x(100,FATAL,dir,"/",fnedit.s,MSG(ERR_NOEXIST)); } stralloc_copy(&line,&text); subst_nuls(&line); stralloc_cat(&line,&fnedit); /* including '\0' */ strnum[fmt_ulong(strnum,(unsigned long) when)] = 0; cookie(hash,key.s,key.len,strnum,line.s,"-e"); stralloc_copy(&confirm,&outlocal); stralloc_append(&confirm,'-'); stralloc_catb(&confirm,ACTION_ED,LENGTH_ED); stralloc_cats(&confirm,strnum); stralloc_append(&confirm,'.'); /* action part has been checked for bad chars */ stralloc_cats(&confirm,action + len + 1); stralloc_append(&confirm,'.'); stralloc_catb(&confirm,hash,COOKIE); stralloc_append(&confirm,'@'); stralloc_cat(&confirm,&outhost); stralloc_0(&confirm); set_cpconfirm(confirm.s,outlocal.len); set_cpaction(ACTION_ED); set_cphash(hash); set_cpwhen(when); qmail_puts(&qq,"Reply-To: "); quote2("ed,confirm.s); qmail_put(&qq,quoted.s,quoted.len); qmail_puts(&qq,"\n"); hdr_subject(MSG1(SUB_EDIT_REQUEST,action+len+1)); hdr_ctboundary(); copy(&qq,"text/top",flagcd); copy(&qq,"text/edit-do",flagcd); (void) code_qputs(MSG(TXT_EDIT_START)); (void) code_qput(text.s,text.len); (void) code_qputs(MSG(TXT_EDIT_END)); } else { /* -edit only, so output list of editable files */ hdr_subject(MSG(SUB_EDIT_LIST)); hdr_ctboundary(); copy(&qq,"text/top",flagcd); copy_act("text/edit-list"); } qmail_puts(&qq,"\n\n"); copybottom(0); qmail_to(&qq,mod.s); }
static void do_ed(char *action) { datetime_sec u; int flaggoodfield; int fd; char *x, *y; char *cp,*cplast,*cpnext,*cpafter; int flagdone; unsigned int len; const char *fname; unsigned int i; x = action + LENGTH_ED; x += scan_ulong(x,&u); if ((u > when) || (u < when - 100000)) die_cookie(); if (*x == '.') ++x; fname = x; x += str_chr(x,'.'); if (!*x) die_cookie(); *x = (char) 0; ++x; stralloc_copys(&fnedit,"text/"); stralloc_cats(&fnedit,fname); stralloc_0(&fnedit); y = fnedit.s + 5; /* after "text/" */ while (*++y) { /* Name should be guaranteed by the cookie, */ /* but better safe than sorry ... */ if (((*y > 'z') || (*y < 'a')) && (*y != '_')) strerr_die2x(100,FATAL,MSG(ERR_BAD_NAME)); if (*y == '_') *y = '-'; } lock(); /* file must not change while here */ switch (slurp(fnedit.s,&text,1024)) { case -1: strerr_die2sys(111,FATAL,MSG1(ERR_READ,fnedit.s)); case 0: strerr_die5x(100,FATAL,dir,"/",fnedit.s,MSG(ERR_NOEXIST)); } stralloc_copy(&line,&text); subst_nuls(&line); stralloc_cat(&line,&fnedit); /* including '\0' */ strnum[fmt_ulong(strnum,(unsigned long) u)] = 0; cookie(hash,key.s,key.len,strnum,line.s,"-e"); if (str_len(x) != COOKIE) die_cookie(); if (byte_diff(hash,COOKIE,x)) die_cookie(); /* cookie is ok, file exists, lock's on, new file ends in '_' */ stralloc_copys(&fneditn,fnedit.s); stralloc_append(&fneditn,'_'); stralloc_0(&fneditn); fd = open_trunc(fneditn.s); if (fd == -1) strerr_die2sys(111,FATAL,MSG1(ERR_WRITE,fneditn.s)); substdio_fdbuf(&sstext,write,fd,textbuf,sizeof(textbuf)); stralloc_copys("ed,""); /* clear */ stralloc_copys(&text,""); for (;;) { /* get message body */ if (getln(&ssin,&line,&match,'\n') == -1) strerr_die2sys(111,FATAL,MSG(ERR_READ_INPUT)); if (!match) break; stralloc_cat(&text,&line); } if (encin) { /* decode if necessary */ if (encin == 'B') decodeB(text.s,text.len,&line); else decodeQ(text.s,text.len,&line); stralloc_copy(&text,&line); } cp = text.s; cpafter = text.s+text.len; flaggoodfield = 0; flagdone = 0; len = 0; while ((cpnext = cp + byte_chr(cp,cpafter-cp,'\n')) != cpafter) { i = byte_chr(cp,cpnext-cp,'%'); if (i != (unsigned int) (cpnext - cp)) { if (!flaggoodfield) { /* MSG(TXT_EDIT_START)/END */ if (case_startb(cp+i,cpnext-cp-i,MSG(TXT_EDIT_START))) { /* start tag. Store users 'quote characters', e.g. '> ' */ stralloc_copyb("ed,cp,i); flaggoodfield = 1; cp = cpnext + 1; continue; } } else if (case_startb(cp+i,cpnext-cp-i,MSG(TXT_EDIT_END))) { flagdone = 1; break; } } if (flaggoodfield) { if ((len += cpnext - cp - quoted.len + 1) > MAXEDIT) strerr_die1x(100,MSG(ERR_EDSIZE)); if (quoted.len && cpnext-cp >= (int) quoted.len && !str_diffn(cp,quoted.s,quoted.len)) cp += quoted.len; /* skip quoting characters */ cplast = cpnext - 1; if (*cplast == '\r') /* CRLF -> '\n' for base64 encoding */ *cplast = '\n'; else ++cplast; if (substdio_put(&sstext,cp,cplast-cp+1) == -1) strerr_die2sys(111,FATAL,MSG1(ERR_WRITE,fneditn.s)); } cp = cpnext + 1; } if (!flagdone) strerr_die2x(100,FATAL,MSG(ERR_NO_MARK)); if (substdio_flush(&sstext) == -1) strerr_die2sys(111,FATAL,MSG1(ERR_WRITE,fneditn.s)); if (fsync(fd) == -1) strerr_die2sys(111,FATAL,MSG1(ERR_SYNC,fneditn.s)); if (fchmod(fd, 0600) == -1) strerr_die2sys(111,FATAL,MSG1(ERR_CHMOD,fneditn.s)); if (close(fd) == -1) strerr_die2sys(111,FATAL,MSG1(ERR_CLOSE,fneditn.s)); wrap_rename(fneditn.s,fnedit.s); unlock(); hdr_subject(MSG1(SUB_EDIT_SUCCESS,fname)); hdr_ctboundary(); copy(&qq,"text/top",flagcd); copy_act("text/edit-done"); copybottom(0); qmail_to(&qq,sender); /* not necessarily from mod */ }
void main(int argc,char **argv) { char *def; char *local; const char *action = ""; char *psz; const char *err; int fd; unsigned int i,j; int flagremote; int match; int goodexit = 0; /* exit code for normal exit */ /* in manager this will be set to 0 */ unsigned long from,u,to,issue,prevmax; unsigned long mno = 0; unsigned long chunk; unsigned long subs = 0; unsigned int pos,pos1; unsigned int len; int opt; char outformat = 0; msgentry *msgtable; subentry *subtable; authentry *authtable; dateentry *datetable; struct datetime dt; char date[DATE822FMT]; (void) umask(022); sig_pipeignore(); when = now(); datetime_tai(&dt,when); opt = getconfopt(argc,argv,options,1,0); initsub(0); if (flagformat != 0) if (FORMATS[str_chr(FORMATS,flagformat[0])]) outformat = flagformat[0]; if (outformat == 0) { outformat = (getconf_line(&line,"digformat",0) && FORMATS[str_chr(FORMATS,line.s[0])]) ? line.s[0] : DEFAULT_FORMAT; } /* code to activate digest (-digest-code)*/ if ((digestcode = argv[opt]) == 0) { if (getconf_line(&digestcodefile,"digestcode",0) && digestcodefile.len > 0) { if (!stralloc_0(&digestcodefile)) die_nomem(); digestcode = digestcodefile.s; } } /* ignore any extra args */ if (!stralloc_copy(&subject,&outlocal)) die_nomem(); /* for subjects */ if (!stralloc_copy(&listname,&outlocal)) die_nomem(); /* for content disp */ local = env_get("LOCAL"); def = env_get("DEFAULT"); sender = get_sender(); if (local && *local) { /* in editor local = outlocal */ if (!sender) strerr_die2x(100,FATAL,MSG(ERR_NOSENDER)); if (!*sender) strerr_die2x(100,FATAL,MSG(ERR_BOUNCE)); if (str_equal(sender,"#@[]")) strerr_die2x(100,FATAL,MSG(ERR_BOUNCE)); if (!sender[str_chr(sender,'@')]) strerr_die2x(100,FATAL,MSG(ERR_ANONYMOUS)); if (def) { if (*def) { action = def; goodexit = 99; } else _exit(0); /* list-@host should do -help from manager */ } else { /* editor */ act = AC_DIGEST; /* on list-@host ! */ flageditor = 1; /* to avoid Mailing-list error on sublists */ /* when running out of dir/editor. */ } if (case_starts(action,"dig")) { action += 3; if (action[0] == '-' || action [0] == '.') { action++; if (!digestcode) strerr_die2x(100,FATAL,MSG(ERR_BAD_DIGCODE)); len = str_len(digestcode); if (len <= str_len(action) && case_startb(action,len,digestcode)) { if (FORMATS[str_chr(FORMATS,*(action+len))]) outformat = *(action+len); act = AC_DIGEST; } else strerr_die2x(100,FATAL,MSG(ERR_BAD_DIGCODE)); } } } else /* Command line operation */ act = AC_DIGEST; /* Things we deal with. If anything else just die with success! */ /* At the moment this is -index, -thread, and -get. */ /* If flagdo = 0 we only service -dig commands. This is to support*/ /* "secret" lists that are still archived and digested. -c on */ /* cmd line. */ if (act == AC_NONE) { if (case_equals(action,ACTION_DIGEST)) { act = AC_GET; /* list-digest@ => msg since last digest */ action = ACTION_GET; } else if (case_starts(action,ACTION_GET) || case_starts(action,ALT_GET)) act = AC_GET; else if (case_starts(action,ACTION_INDEX) || case_starts(action,ALT_INDEX)) act = AC_INDEX; else if (case_starts(action,ACTION_THREAD) || case_starts(action,ALT_THREAD)) act = AC_THREAD; } if (act == AC_NONE) /* not for us. Pass the buck. */ _exit(0); if (act != AC_INDEX) { /* need to do header processing */ if(!getconf(&digheaders,"digheaders",0)) { if(!stralloc_copys(&digheaders,digsz)) die_nomem(); if (!stralloc_0(&digheaders)) die_nomem(); psz = digheaders.s; while (*psz) { if (*psz == '\\') *psz = '\0'; ++psz; } } if (!constmap_init(&digheadersmap,digheaders.s,digheaders.len,0)) die_nomem(); } if (act != AC_DIGEST) { if (!flagdo) /* only do digests */ strerr_die2x(100,FATAL,MSG(ERR_NOCMD)); if (flagpublic < 0) flagpublic = !getconf_isset("modgetonly") && getconf_isset("public"); if (!flagpublic) { /* This all to take care of non-public lists. They should*/ /* still do digests, but do other things only for */ /* moderators that have remote access. Since this is rare*/ /* efforts have been made to keep everything that's not */ /* needed elsewhere in here. */ getconf_line(&moddir,"modsub",0); flagremote = getconf_line(&line,"remote",0); if (!flagremote) strerr_die2x(100,FATAL,MSG(ERR_NOT_PUBLIC)); if (!moddir.len) { if (line.len) { if (!stralloc_copy(&moddir,&line)) die_nomem(); } else { if (!stralloc_copys(&moddir,"mod")) die_nomem(); } } if (!stralloc_0(&moddir)) die_nomem(); ismod = issub(moddir.s,sender,&mod); if (!ismod) /* sender = moderator? */ strerr_die2x(100,FATAL,MSG(ERR_NOT_PUBLIC)); } } if (act == AC_DIGEST) { workdir = "digest"; if (!stralloc_cats(&outlocal,"-digest")) die_nomem(); if (getconf_line(&line,"chunk",0)) { if (!stralloc_0(&line)) die_nomem(); (void) scan_ulong(line.s,&chunk); /* same chunk as main list */ if (chunk == 0) /* limit range to 1-53 */ chunk = 1L; else if (chunk > 52) chunk = 52L; } else { chunk = 0L; } } else workdir = "."; if (!flagarchived) strerr_die2x(100,FATAL,MSG(ERR_NOT_ARCHIVED)); if (qmail_open(&qq) == -1) strerr_die2sys(111,FATAL,MSG(ERR_QMAIL_QUEUE)); set_cpnum(""); /* default for <#n#> replacement */ switch (act) { case AC_DIGEST: /* -dig{.|-}'digestcode'[f] returns an rfc1153 digest */ /* of messages from the archive. Messages */ /* dignum+1 through the last message received by the list are processed and */ /* dignum is updated to the last message processed. digissue is advanced. */ get_num(); /* max = last successful message */ to = max; lockup(); /* another digest could corrupt dignum */ /* but will be saved only if flagdigrange==0 */ if(getconf_line(&num,"dignum",0)) { if(!stralloc_0(&num)) die_nomem(); pos = scan_ulong(num.s,&prevmax); if (num.s[pos] == ':') pos++; pos += 1 + scan_ulong(num.s+pos,&cumsize); /* last cumsize */ if (num.s[pos] == ':') pos++; scan_ulong(num.s+pos,&digwhen); /* last reg dig */ } else { prevmax = 0L; cumsize = 0L; digwhen = 0L; } mno = prevmax + 1L; if(!max || mno > max) /* if a digest-list is "sending" the request, */ /* don't make noise: errors go to postmaster!*/ strerr_die2x(goodexit,FATAL,MSG(ERR_EMPTY_DIGEST)); szmsgnum[fmt_ulong(szmsgnum,mno)] = '\0'; set_cpnum(szmsgnum); /* for copy */ /* prepare subject to get entropy for tagmsg*/ if (!stralloc_cats(&subject," Digest ")) die_nomem(); if (!stralloc_catb(&subject,date,date822fmt(date,&dt)-1)) die_nomem(); /* skip trailing in date '\n' */ if (!stralloc_cats(&subject," Issue ")) die_nomem(); if (getconf_line(&num,"digissue",0)) { if(!stralloc_0(&num)) die_nomem(); scan_ulong(num.s,&issue); issue++; } else { issue = 1; } if (!stralloc_catb(&subject,strnum,fmt_ulong(strnum,issue))) die_nomem(); /* use the subject as entropy */ if (!stralloc_copy(&line,&subject)) die_nomem(); if (!stralloc_0(&line)) die_nomem(); if (!stralloc_ready(&seed,HASHLEN+1)) die_nomem(); seed.len = HASHLEN + 1; seed.s[HASHLEN] = '\0'; makehash(line.s,line.len,seed.s); if (chunk) { /* only if slaves are used */ qmail_puts(&qq,"Ezauth: "); qmail_put(&qq,seed.s,HASHLEN); qmail_puts(&qq,"\n"); } doheaders(); qmail_puts(&qq,"To: "); if (!quote("ed,&listname)) die_nomem(); qmail_put(&qq,quoted.s,quoted.len); qmail_puts(&qq,"@"); qmail_put(&qq,outhost.s,outhost.len); qmail_puts(&qq,"\n"); if (flagindexed && (outformat != NATIVE)) idx_mkthreads(&msgtable,&subtable,&authtable,&datetable, mno,to,max,flaglocked); else idx_mklist(&msgtable,&subtable,&authtable,mno,to); digest(msgtable,subtable,authtable,mno,to,&subject,AC_DIGEST,outformat); write_ulong(issue,0L,0L,"digissue","digissuen"); write_ulong(max,cumsizen, (unsigned long) when,"dignum","dignumn"); break; case AC_GET: /* -get[-|\.][[num].num2] copies archive num-num2. num & num2 are adjusted */ /* to be > 0 and <= last message, to num2 >= num and to num2-num <= MAXGET. */ zapnonsub(ACTION_GET); /* restrict to subs if requested */ tosender(); /* for rfc1153 */ if (!stralloc_cats(&subject," Digest of: ")) die_nomem(); if (!stralloc_cats(&subject,action)) die_nomem(); to = 0; pos = str_len(ACTION_GET); if (!case_starts(action,ACTION_GET)) pos = str_len(ALT_GET); if (FORMATS[str_chr(FORMATS,action[pos])]) { outformat = action[pos]; ++pos; } /* optional - or . after '-get' */ if (action[pos] == '-' || action[pos] == '.') pos++; get_num(); /* max = last successful message */ /* accept any separator. It may be */ /* the terminal '\n', but then */ /* scan will = 0 on the \0 so should*/ /* be safe */ if (!max) strerr_die2x(100,FATAL,MSG(ERR_EMPTY_LIST)); szmsgnum[fmt_ulong(szmsgnum,max)] = '\0'; set_cpnum(szmsgnum); /* for copy this is the latest message arch'd*/ doheaders(); if(action[pos += scan_ulong(action + pos,&u)]) scan_ulong(action + pos + 1, &to); if (u == 0 && to == 0) { /* default: messages since last */ /* digest, or last MAXGET if too many */ to= max; u = dignum(); if (u == 0) { /* no digest => last up to HISTGET msgs */ to = max; if (max > HISTGET) u = max - HISTGET; else u = 1; } if (to - u >= MAXGET) u = to - MAXGET + 1; /* max MAXGET */ } else if (u > max) { if (to) { /* -get.999999_x returns 30 and msg since last*/ to = max; /* digest 30*/ u = dignum(); if (u > HISTGET) u -= HISTGET; else u = 1; if (to - u >= MAXGET) u = to - MAXGET + 1; } else u = max; } if (u == 0) u = 1; /* -get.5 => 1-5 */ if (to < u) to = u; /* -get23_2 => 23 */ if (to >= u + MAXGET) to = u + MAXGET - 1; /* no more than MAXGET at a time */ if (to > max) to = max; if (flagindexed && (outformat != NATIVE)) /* fake out threading */ idx_mkthreads(&msgtable,&subtable,&authtable,&datetable, u,to,max,0); else idx_mklist(&msgtable,&subtable,&authtable,u,to); digest(msgtable,subtable,authtable,u,to,&subject,AC_GET,outformat); break; case AC_INDEX: /* -index[f][#|-|\.][[num][.num2] Show subject index for messages num-num2 in*/ /* sets of 100. */ /* Default last 2 sets. num and num2 are made reasonable as for get. num2 is */ /* limited to num+MAXINDEX to limit the amount of data sent. */ if (!flagindexed) strerr_die2x(100,FATAL,MSG(ERR_NOT_INDEXED)); zapnonsub(ACTION_INDEX); /* restrict to subs if requested */ to = 0; pos = str_len(ACTION_INDEX); if (!case_starts(action,ACTION_INDEX)) pos = str_len(ALT_INDEX); if (FORMATS[str_chr(FORMATS,action[pos])]) { outformat = action[pos]; /* ignored, but be nice ... */ ++pos; } get_num(); /* max = last successful message */ if (!max) strerr_die2x(100,FATAL,MSG(ERR_EMPTY_LIST)); szmsgnum[fmt_ulong(szmsgnum,max)] = '\0'; set_cpnum(szmsgnum); /* for copy this is the latest message arch'd*/ doheaders(); tosender(); if (!stralloc_cats(&subject," Result of: ")) die_nomem(); if (!stralloc_cats(&subject,action)) die_nomem(); presub(1,1,&subject,AC_INDEX,outformat); if (action[pos] == '-' || action[pos] == '.') pos++; if(action[pos += scan_ulong(action + pos,&u)]) scan_ulong(action + pos + 1, &to); if (u == 0 && to == 0) { to = max; u = max - 100; } if (u <= 0) u = 1; if (u > max) u = max; if (to < u) to = u; if (to > u + MAXINDEX) to = u+MAXINDEX; /* max MAXINDEX index files */ if (to > max) to = max; u /= 100; to /= 100; while (u <= to) { if (!stralloc_copys(&fn,"archive/")) die_nomem(); if (!stralloc_catb(&fn,strnum,fmt_ulong(strnum,u))) die_nomem(); if (!stralloc_cats(&fn,"/index")) die_nomem(); if (!stralloc_0(&fn)) die_nomem(); if (u == max/100) /* lock if last index file in archive */ lockup(); fd = open_read(fn.s); if (fd == -1) if (errno != error_noent) strerr_die2sys(111,FATAL,MSG1(ERR_OPEN,fn.s)); else code_qputs(MSG(TXT_NOINDEX)); else { substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf)); for (;;) { if (getln(&sstext,&line,&match,'\n') == -1) strerr_die2sys(111,FATAL,MSG1(ERR_READ,fn.s)); if (match) { if (line.s[0] != '\t') { /* subject line */ pos = byte_chr(line.s,line.len,' '); pos1 = 0; if (pos && pos != line.len && line.s[pos - 1] == ':') pos1 = pos + HASHLEN + 1; /* after hash */ if (pos1 >= line.len) { /* bad! */ pos = 0; pos1 = 0; /* output as is */ } if (!stralloc_copyb(&line2,line.s,pos)) die_nomem(); if (!stralloc_catb(&line2,line.s+pos1,line.len-pos1)) die_nomem(); } else { pos = byte_chr(line.s,line.len,';'); if (pos + HASHLEN + 1 < line.len && pos > 15 && line.s[pos + 1] != ' ') { if (!stralloc_copyb(&line2,line.s,pos - 15)) die_nomem(); pos++; if (!stralloc_catb(&line2,line.s + pos + HASHLEN, line.len - pos - HASHLEN)) die_nomem(); } else /* old format - no author hash */ if (!stralloc_copyb(&line2,line.s,line.len)) die_nomem(); } code_qput(line2.s,line2.len); } else break; } close(fd); } if (u == max/100) /* unlock if last index in archive file */ unlock(); u++; } normal_bottom(outformat); postmsg(outformat); break; case AC_THREAD: /* -thread[f][-|.]num returns messages with subject matching message */ /* 'num' in the subject index. If 'num' is not in[1..last_message] an error */ /* message is returned. */ if (!flagindexed) strerr_die2x(100,FATAL,MSG(ERR_NOT_INDEXED)); zapnonsub(ACTION_THREAD); /* restrict to subs if requested*/ get_num(); /* max = last successful message */ if (!max) strerr_die2x(100,FATAL,MSG(ERR_EMPTY_LIST)); szmsgnum[fmt_ulong(szmsgnum,max)] = '\0'; set_cpnum(szmsgnum); /* for copy this is the latest message arch'd*/ doheaders(); tosender(); /* for rfc1153 */ if (!stralloc_cats(&subject," Digest of: ")) die_nomem(); if (!stralloc_cats(&subject,action)) die_nomem(); to = 0; pos = str_len(ACTION_THREAD); if (!case_starts(action,ACTION_THREAD)) pos = str_len(ALT_THREAD); if (FORMATS[str_chr(FORMATS,action[pos])]) { outformat = action[pos]; ++pos; } if (action[pos] == '-' || action[pos] == '.') pos++; if(action[pos += scan_ulong(action + pos,&u)]) scan_ulong(action + pos + 1, &to); if(u == 0 || u > max) { hdr_add2("Subject: ",subject.s,subject.len); qmail_puts(&qq,"\n"); copy(&qq,"text/get-bad",flagcd); } else { /* limit range to at most u-THREAD_BEFORE to u+THREAD_AFTER */ if (u > THREAD_BEFORE) from = u-THREAD_BEFORE; else from = 1L; if (u + THREAD_AFTER > max) { idx_mkthread(&msgtable,&subtable,&authtable,from,max,u,max,0); digest(msgtable,subtable,authtable,from,max,&subject, AC_THREAD,outformat); } else { idx_mkthread(&msgtable,&subtable,&authtable, from,u+THREAD_AFTER,u,max,0); digest(msgtable,subtable,authtable,from,u+THREAD_AFTER, &subject,AC_THREAD,outformat); } } break; default: /* This happens if the initial check at the beginning of 'main' */ /* matches something that isn't matched here. Conversely, just */ /* adding an action here is not enough - it has to be added to the */ /* initial check as well. */ strerr_die2x(100,FATAL, "Program error: I'm supposed to deal with this but I didn't"); } if (!stralloc_copy(&line,&outlocal)) die_nomem(); if (act == AC_DIGEST) { if (chunk) { if (!stralloc_cats(&line,"-return-g-")) die_nomem(); } else if (!stralloc_cats(&line,"-return-")) die_nomem(); strnum[fmt_ulong(strnum,mno)] = '\0'; if (!stralloc_cats(&line,strnum)) die_nomem(); if (!stralloc_cats(&line,"-@")) die_nomem(); if (!stralloc_cat(&line,&outhost)) die_nomem(); if (!stralloc_cats(&line,"-@[]")) die_nomem(); } else { if (!stralloc_cats(&line,"-return-@")) die_nomem(); if (!stralloc_cat(&line,&outhost)) die_nomem(); } if (!stralloc_0(&line)) die_nomem(); qmail_from(&qq,line.s); if (act == AC_DIGEST) { /* Do recipients */ tagmsg(mno,seed.s,"d",hashout,qq.msgbytes,chunk); if (chunk) { if (!stralloc_copys(&line,"T")) die_nomem(); if (!stralloc_cat(&line,&outlocal)) die_nomem(); if (!stralloc_cats(&line,"-s-d-")) die_nomem(); if (!stralloc_catb(&line,hashout,COOKIE)) die_nomem(); if (!stralloc_cats(&line,"-")) die_nomem(); if (!stralloc_cats(&line,strnum)) die_nomem(); if (!stralloc_cats(&line,"-")) die_nomem(); if (!stralloc_copys(&line2,"@")) die_nomem(); if (!stralloc_cat(&line2,&outhost)) die_nomem(); if (!stralloc_0(&line2)) die_nomem(); j = 0; for (i = 0; i <= 52; i += chunk) { /* To slaves */ qmail_put(&qq,line.s,line.len); schar[0] = '0' + i / 10; schar[1] = '0' + (i % 10); qmail_put(&qq,schar,3); j += (chunk - 1); if (j > 52) j = 52; schar[0] = '0' + j / 10; schar[1] = '0' + (j % 10); qmail_put(&qq,schar,2); qmail_put(&qq,line2.s,line2.len); } } else subs = putsubs(workdir,0L,52L,&subto); } else { /* if local is set, sender is checked */ if (ismod) qmail_to(&qq,mod.s); else qmail_to(&qq,sender); } if (*(err = qmail_close(&qq)) == '\0') { /* Done. Skip rest. */ if (act == AC_DIGEST) { if (chunk) (void) logmsg(mno,0L,0L,2); else (void) logmsg(mno,0L,subs,4); } closesub(); /* close db connection */ unlock(); /* NOP if nothing locked */ strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0; strerr_die2x(goodexit,"ezmlm-get: info: qp ",strnum); } else { /* failed. Reset last msg & issue for digest */ if(act == AC_DIGEST) { issue--; write_ulong(issue,0L,0L,"digissue","digissuen"); write_ulong(prevmax,cumsize,(unsigned long) digwhen,"dignum","dignumn"); } unlock(); /* NOP if nothing locked */ strerr_die4x(111,FATAL,MSG(ERR_TMP_QMAIL_QUEUE),": ",err + 1); } }
void copymsg(int fd,char format) /* Copy archive message "msg" itself from open file handle fd, in "format" */ { int match; int flaginheader; int flagskipblanks; int flaggoodfield; switch(format) { case VIRGIN: case NATIVE: substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf)); for (;;) { if (getln(&sstext,&line,&match,'\n') == -1) strerr_die2sys(111,FATAL,MSG1(ERR_READ,line.s)); if (match) { qmail_put(&qq,line.s,line.len); msgsize += line.len; } else break; } break; case MIME: case MIXED: flaginheader = 1; flaggoodfield = 0; substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf)); for (;;) { if (getln(&sstext,&line,&match,'\n') == -1) strerr_die2sys(111,FATAL,MSG1(ERR_READ,line.s)); if (match) { if (flaginheader) { if (line.len == 1) { flaginheader = 0; flaggoodfield = 1; } else if (line.s[0] != ' ' && line.s[0] != '\t') { flaggoodfield = 0; if (constmap(&digheadersmap,line.s, byte_chr(line.s,line.len,':'))) flaggoodfield = 1; } if (flaggoodfield) { qmail_put(&qq,line.s,line.len); /* header */ msgsize += line.len; } } else { qmail_put(&qq,line.s,line.len); /* body */ msgsize += line.len; } } else break; } break; case RFC1153: /* Not worth optimizing. Rarely used */ flaginheader = 1; flagskipblanks = 1; /* must skip terminal blanks acc to rfc1153 */ archtype = ' '; /* rfc1153 requires ordered headers */ if (!stralloc_copys(&archblanklines,"")) die_nomem(); substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf)); for (;;) { if (getln(&sstext,&line,&match,'\n') == -1) strerr_die2sys(111,FATAL,MSG1(ERR_READ,line.s)); if (match) { if (flaginheader) { if (line.len == 1) { flaginheader = 0; if (archdate.len) { qmail_put(&qq,archdate.s,archdate.len); archdate.len = 0; msgsize += archdate.len; } if (archto.len) { qmail_put(&qq,archto.s,archto.len); msgsize += archto.len; archto.len = 0; } if (archfrom.len) { qmail_put(&qq,archfrom.s,archfrom.len); msgsize += archfrom.len; archfrom.len = 0; } if (archcc.len) { qmail_put(&qq,archcc.s,archcc.len); msgsize += archcc.len; archcc.len = 0; } if (archsubject.len) { qmail_put(&qq,archsubject.s,archsubject.len); msgsize += archsubject.len; archsubject.len = 0; } if (archmessageid.len) { qmail_put(&qq,archmessageid.s,archmessageid.len); msgsize += archmessageid.len; archmessageid.len = 0; } if (archkeywords.len) { qmail_put(&qq,archkeywords.s,archkeywords.len); msgsize += archkeywords.len; archkeywords.len = 0; } qmail_puts(&qq,"\n"); } else if (line.s[0] == ' ' || line.s[0] == '\t') { switch (archtype) { /* continuation lines */ case ' ': break; case 'D': if (!stralloc_cat(&archdate,&line)) die_nomem(); break; case 'F': if (!stralloc_cat(&archfrom,&line)) die_nomem(); break; case 'T': if (!stralloc_cat(&archto,&line)) die_nomem(); break; case 'C': if (!stralloc_cat(&archcc,&line)) die_nomem(); break; case 'S': if (!stralloc_cat(&archsubject,&line)) die_nomem(); break; case 'M': if (!stralloc_cat(&archmessageid,&line)) die_nomem(); break; case 'K': if (!stralloc_cat(&archkeywords,&line)) die_nomem(); break; default: strerr_die2x(111,FATAL, "Program error: Bad archive header type"); } } else { archtype = ' '; if (case_startb(line.s,line.len,"cc:")) { archtype='C'; if (!stralloc_copy(&archcc,&line)) die_nomem(); } else if (case_startb(line.s,line.len,"date:")) { archtype='D'; if (!stralloc_copy(&archdate,&line)) die_nomem(); } else if (case_startb(line.s,line.len,"from:")) { archtype='F'; if (!stralloc_copy(&archfrom,&line)) die_nomem(); } else if (case_startb(line.s,line.len,"keywords:")) { archtype='K'; if (!stralloc_copy(&archkeywords,&line)) die_nomem(); } else if (case_startb(line.s,line.len,"message-id:")) { archtype='M'; if (!stralloc_copy(&archmessageid,&line)) die_nomem(); } else if (case_startb(line.s,line.len,"subject:")) { archtype='S'; if (!stralloc_copy(&archsubject,&line)) die_nomem(); } else if (case_startb(line.s,line.len,"to:")) { archtype='T'; if (!stralloc_copy(&archto,&line)) die_nomem(); } } } else if (line.len == 1) { if (!flagskipblanks) if (!stralloc_copys(&archblanklines,"\n")) die_nomem(); } else { if (archblanklines.len) { qmail_put(&qq,archblanklines.s,archblanklines.len); archblanklines.len = 0; } flagskipblanks = 0; qmail_put(&qq,line.s,line.len); msgsize += line.len; } } else break; } break; default: strerr_die2x(100,FATAL,"Program error: bad format in copymsg()"); } }
void 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) { char *action; const char *err; unsigned int i; int act = AC_NONE; /* desired action */ unsigned int actlen = 0;/* str_len of above */ (void) umask(022); sig_pipeignore(); when = now(); getconfopt(argc,argv,options,1,&dir); initsub(0); sender = get_sender(); if (!sender) die_sender(); action = env_get("DEFAULT"); if (!action) 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)); action = set_workdir(action); stralloc_copys(&target,sender); if (action[0]) { i = str_chr(action,'-'); if (action[i]) { action[i] = 0; stralloc_copys(&target,action + i + 1); i = byte_rchr(target.s,target.len,'='); if (i < target.len) target.s[i] = '@'; } } stralloc_0(&target); set_cptarget(target.s); /* for copy() */ make_verptarget(); act = get_act_ismod(action,&actlen); stralloc_copy(&from,&outlocal); stralloc_cats(&from,"-return-@"); stralloc_cat(&from,&outhost); stralloc_0(&from); if (qmail_open(&qq) == -1) strerr_die2sys(111,FATAL,MSG(ERR_QMAIL_QUEUE)); msg_headers(act); if (act == AC_SUBSCRIBE) do_subscribe(action); else if (act == AC_SC) do_sc(action); else if (str_start(action,ACTION_RC)) do_rc_tc(action,ACTION_RC); else if(str_start(action,ACTION_TC)) do_rc_tc(action,ACTION_TC); else if (act == AC_UNSUBSCRIBE) do_unsubscribe(action); else if (str_start(action,ACTION_UC)) do_uc(action); else if (str_start(action,ACTION_VC)) do_vc_wc(action,ACTION_VC); else if (str_start(action,ACTION_WC)) do_vc_wc(action,ACTION_WC); else if (act == AC_LIST || act == AC_LISTN) do_list(act); else if (act == AC_LOG) do_log(action,actlen); else if (act == AC_EDIT) do_edit(action); else if (str_start(action,ACTION_ED)) do_ed(action); else if (act == AC_GET) do_get(action); else if (case_starts(action,ACTION_QUERY) || case_starts(action,ALT_QUERY)) do_query(); else if (case_starts(action,ACTION_INFO) || case_starts(action,ALT_INFO)) do_info(); else if (case_starts(action,ACTION_FAQ) || case_starts(action,ALT_FAQ)) do_faq(); else if (ismod && (act == AC_HELP)) do_mod_help(); else do_help(); err = qmail_close(&qq); closesub(); if (*err != '\0') strerr_die4x(111,FATAL,MSG(ERR_TMP_QMAIL_QUEUE),": ",err + 1); strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0; strerr_die3x(0,INFO,"qp ",strnum); }
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,": "); }
const char *parsesql(const char *dir, const char *subdir, struct sqlinfo *info) /* reads the file dbname/sql, and if the file exists, parses it into the */ /* components. The string should be host:port:user:pw:db:table. If */ /* the file does not exists, returns "". On success returns NULL. On error */ /* returns error string for temporary error. */ /* Note that myp is static and all pointers point to it.*/ { unsigned int j; info->db = "ezmlm"; std_makepath(&fn,dir,subdir,"/sql",0); /* host:port:db:table:user:pw:name */ myp.len = 0; switch (slurp(fn.s,&myp,128)) { case -1: if (!stralloc_copys(&ers,ERR_READ)) return ERR_NOMEM; if (!stralloc_cat(&ers,&fn)) return ERR_NOMEM; if (!stralloc_0(&ers)) return ERR_NOMEM; return ers.s; case 0: return ""; } if (!stralloc_append(&myp,"\n")) return ERR_NOMEM; if (myp.s[j = str_chr(myp.s,'\n')]) myp.s[j] = '\0'; /* get connection parameters */ if (!stralloc_0(&myp)) return ERR_NOMEM; info->host = myp.s; if (myp.s[j = str_chr(myp.s,':')]) { myp.s[j++] = '\0'; info->port = myp.s + j; if (myp.s[j += str_chr(myp.s+j,':')]) { myp.s[j++] = '\0'; info->user = myp.s + j; if (myp.s[j += str_chr(myp.s+j,':')]) { myp.s[j++] = '\0'; info->pw = myp.s + j; if (myp.s[j += str_chr(myp.s+j,':')]) { myp.s[j++] = '\0'; info->db = myp.s + j; if (myp.s[j += str_chr(myp.s+j,':')]) { myp.s[j++] = '\0'; info->table = myp.s + j; } } } } } if (info->host && !*info->host) info->host = (char *) 0; if (info->user && !*info->user) info->user = (char *) 0; if (info->pw && !*info->pw) info->pw = (char *) 0; if (info->db && !*info->db) info->db = (char *) 0; if (!info->table || !*info->table) return ERR_NO_TABLE; return (char *) 0; }
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; } }
int main(int argc,char **argv) { int child; const char *sendargs[4]; stralloc addr = {0}; unsigned int pos = 0,pos2,poslocal,len; const char *cp; unsigned long hh = 4L; /* default time 04:12 */ unsigned long mm = 12L; const char *dow = "*"; /* day of week */ const char *qmail_inject = "/bin/qmail-inject "; char strnum[FMT_ULONG]; unsigned long uid,euid = 0; stralloc rp = {0}; stralloc user = {0}; stralloc euser = {0}; stralloc dir = {0}; stralloc listaddr = {0}; stralloc line = {0}; struct passwd *ppasswd; int match; int hostmatch; int localmatch; unsigned long dh,t; int founduser = 0; int listmatch = 0; int flagdigit = 0; int flagours; int foundlocal = 0; int foundmatch = 0; unsigned int nolists = 0; unsigned long maxlists; unsigned int lenhost,lenlocal; int fdin,fdout = -1; char *local = (char *) 0; /* list = local@host */ const char *host = (char *) 0; char *code = (char *) 0; /* digest code */ char inbuf[512]; substdio ssin; char outbuf[512]; substdio ssout; (void) umask(077); sig_pipeignore(); optind = getconfopt(argc,argv,options,0,0); if (flagt != 0) { pos = scan_ulong(flagt,&hh); if (flagt[pos++] != ':') die_usage(); (void) scan_ulong(flagt + pos,&mm); } if (flagw != 0) { dow = flagw; cp = flagw - 1; while (*(++cp)) { if (*cp >= '0' && *cp <= '7') { if (flagdigit) die_dow(); flagdigit = 1; } else if (*cp == ',') { if (!flagdigit) die_dow(); flagdigit = 0; } else die_dow(); } } if (flaglist + flagdelete + flagconfig > 1) strerr_die2x(100,FATAL,MSG(ERR_EXCLUSIVE)); uid = getuid(); if (uid && !(euid = geteuid())) strerr_die2x(100,FATAL,MSG(ERR_SUID)); if (!(ppasswd = getpwuid(uid))) strerr_die2x(100,FATAL,MSG(ERR_UID)); if (!stralloc_copys(&user,ppasswd->pw_name)) die_nomem(); if (!stralloc_0(&user)) die_nomem(); if (!(ppasswd = getpwuid(euid))) strerr_die2x(100,FATAL,MSG(ERR_EUID)); if (!stralloc_copys(&dir,ppasswd->pw_dir)) die_nomem(); if (!stralloc_0(&dir)) die_nomem(); if (!stralloc_copys(&euser,ppasswd->pw_name)) die_nomem(); if (!stralloc_0(&euser)) die_nomem(); wrap_chdir(dir.s); local = argv[optind++]; /* list address, optional for -c & -l */ if (!local) { if (!flagconfig && !flaglist) die_usage(); lenlocal = 0; lenhost = 0; } else { if (!stralloc_copys(&listaddr,local)) die_nomem(); if (!isclean(local,1)) die_argument(); pos = str_chr(local,'@'); lenlocal = pos; local[pos] = '\0'; host = local + pos + 1; lenhost = str_len(host); code = argv[optind]; if (!code) { /* ignored for -l, -c, and -d */ if (flagdelete || flaglist || flagconfig) /* get away with not putting code for delete */ code = (char*)"a"; /* a hack - so what! */ else die_usage(); } else if (!isclean(code,0)) die_argument(); } if ((fdin = open_read(TXT_EZCRONRC)) == -1) strerr_die2sys(111,FATAL,MSG1(ERR_OPEN,TXT_EZCRONRC)); /* first line is special */ substdio_fdbuf(&ssin,read,fdin,inbuf,sizeof(inbuf)); if (getln(&ssin,&line,&match,'\n') == -1) strerr_die2sys(111,FATAL,MSG1(ERR_READ,TXT_EZCRONRC)); if (!match) strerr_die2sys(111,FATAL,MSG1(ERR_READ,TXT_EZCRONRC)); /* (since we have match line.len has to be >= 1) */ line.s[line.len - 1] = '\0'; if (!isclean(line.s,0)) /* host for bounces */ strerr_die2x(100,FATAL,MSG1(ERR_CFHOST,TXT_EZCRONRC)); if (!stralloc_copys(&rp,line.s)) die_nomem(); match = 1; for(;;) { if (!match) break; /* to allow last line without '\n' */ if (getln(&ssin,&line,&match,'\n') == -1) strerr_die2sys(111,FATAL,MSG1(ERR_READ,TXT_EZCRONRC)); if (!line.len) break; line.s[line.len-1] = '\0'; if (!case_startb(line.s,line.len,user.s)) continue; pos = user.len - 1; if (pos >= line.len || line.s[pos] != ':') continue; founduser = 1; /* got user line */ break; } close(fdin); if (!founduser) strerr_die2x(100,FATAL,MSG(ERR_BADUSER)); if (flagconfig) { line.s[line.len-1] = '\n'; /* not very elegant ;-) */ substdio_fdbuf(&ssout,write,1,outbuf,sizeof(outbuf)); if (substdio_put(&ssout,line.s,line.len) == -1) strerr_die2sys(111,FATAL,MSG(ERR_WRITE_STDOUT)); if (substdio_flush(&ssout) == -1) strerr_die2sys(111,FATAL,MSG(ERR_WRITE_STDOUT)); _exit(0); } ++pos; /* points to first ':' */ len = str_chr(line.s+pos,':'); /* second ':' */ if (!line.s[pos + len]) die_syntax(&line); if (!local) { /* only -d and std left */ localmatch = 1; hostmatch = 1; } else { hostmatch = 0; if (len <= str_len(local)) if (!str_diffn(line.s+pos,local,len)) localmatch = 1; } pos += len + 1; len = str_chr(line.s + pos,':'); /* third */ if (!line.s[pos + len]) die_syntax(&line); if (local) { /* check host */ if (len == 0) /* empty host => any host */ hostmatch = 1; else if (len == str_len(host)) if (!case_diffb(line.s+pos,len,host)) hostmatch = 1; } pos += len + 1; pos += scan_ulong(line.s+pos,&maxlists); if (line.s[pos]) { /* check additional lists */ if (line.s[pos] != ':') die_syntax(&line); if (line.s[pos+1+str_chr(line.s+pos+1,':')]) die_syntax(&line); /* reminder lists are not separated by ':' */ /* otherwise a ':' or arg miscount will die */ /* silently */ if (local) { while (++pos < line.len) { len = str_chr(line.s + pos,'@'); if (len == lenlocal && !str_diffn(line.s + pos,local,len)) { pos += len; if (!line.s[pos]) break; pos++; len = str_chr(line.s+pos,','); if (len == lenhost && !case_diffb(line.s+pos,len,host)) { listmatch = 1; break; } } pos += len; } } } if (!listmatch) { if (!hostmatch) strerr_die2x(100,FATAL,MSG(ERR_BADHOST)); if (!localmatch) strerr_die2x(100,FATAL,MSG(ERR_BADLOCAL)); } /* assemble correct line */ if (!flaglist) { if (!stralloc_copyb(&addr,strnum,fmt_ulong(strnum,mm))) die_nomem(); if (!stralloc_cats(&addr," ")) die_nomem(); dh = 0L; if (deltah <= 3L) dh = deltah; else if (deltah <= 6L) dh = 6L; else if (deltah <= 12L) dh = 12L; else if (deltah <= 24L) dh = 24L; else if (deltah <= 48L) { if (dow[0] == '*') dow = "1,3,5"; } else if (deltah <= 72L) { if (dow[0] == '*') dow = "1,4"; } else if (dow[0] == '*') dow = "1"; if (!dh) { if (!stralloc_cats(&addr,"*")) die_nomem(); } else { if (!stralloc_catb(&addr,strnum,fmt_ulong(strnum,hh))) die_nomem(); for (t = hh + dh; t < hh + 24L; t+=dh) { if (!stralloc_cats(&addr,",")) die_nomem(); if (!stralloc_catb(&addr,strnum,fmt_ulong(strnum,t % 24L))) die_nomem(); } } if (!stralloc_cats(&addr," * * ")) die_nomem(); if (!stralloc_cats(&addr,dow)) die_nomem(); if (!stralloc_cats(&addr," ")) die_nomem(); if (!stralloc_cats(&addr,auto_qmail)) die_nomem(); if (!stralloc_cats(&addr,qmail_inject)) die_nomem(); if (!stralloc_cats(&addr,local)) die_nomem(); if (!stralloc_cats(&addr,"-dig-")) die_nomem(); if (!stralloc_cats(&addr,code)) die_nomem(); if (!stralloc_cats(&addr,"@")) die_nomem(); if (!stralloc_cats(&addr,host)) die_nomem(); /* feed 'Return-Path: <user@host>' to qmail-inject */ if (!stralloc_cats(&addr,"%Return-path: <")) die_nomem(); if (!stralloc_cats(&addr,user.s)) die_nomem(); if (!stralloc_cats(&addr,"@")) die_nomem(); if (!stralloc_cat(&addr,&rp)) die_nomem(); if (!stralloc_cats(&addr,">\n")) die_nomem(); } if (!stralloc_0(&addr)) die_nomem(); if (!flaglist) { /* now to rewrite crontab we need to lock */ lockfile("crontabl"); } /* if !flaglist */ if ((fdin = open_read("crontab")) == -1) { if (errno != error_noent) strerr_die2sys(111,FATAL,MSG1(ERR_READ,"crontab")); } else substdio_fdbuf(&ssin,read,fdin,inbuf,sizeof(inbuf)); if (flaglist) substdio_fdbuf(&ssout,write,1,outbuf,sizeof(outbuf)); else { if ((fdout = open_trunc("crontabn")) == -1) strerr_die2sys(111,FATAL,MSG1(ERR_WRITE,"crontabn")); substdio_fdbuf(&ssout,write,fdout,outbuf,sizeof(outbuf)); } line.len = 0; if (fdin != -1) { for (;;) { if (!flaglist && line.len) { line.s[line.len-1] = '\n'; if (substdio_put(&ssout,line.s,line.len) == -1) strerr_die2sys(111,FATAL,MSG1(ERR_WRITE,"crontabn")); } if (getln(&ssin,&line,&match,'\n') == -1) strerr_die2sys(111,FATAL,MSG1(ERR_READ,"crontab")); if (!match) break; flagours = 0; /* assume entry is not ours */ foundlocal = 0; line.s[line.len - 1] = '\0'; /* match so at least 1 char */ pos = 0; while (line.s[pos] == ' ' || line.s[pos] == '\t') ++pos; if (line.s[pos] == '#') continue; /* cron comment */ pos = str_chr(line.s,'/'); if (!str_start(line.s+pos,auto_qmail)) continue; pos += str_len(auto_qmail); if (!str_start(line.s+pos,qmail_inject)) continue; pos += str_len(qmail_inject); poslocal = pos; pos = byte_rchr(line.s,line.len,'<'); /* should be Return-Path: < */ if (pos == line.len) continue; /* not ezmlm-cron line */ pos++; len = str_chr(line.s+pos,'@'); if (len == user.len - 1 && !str_diffn(line.s+pos,user.s,len)) { flagours = 1; ++nolists; /* belongs to this user */ } if (!local) { foundlocal = 1; } else { pos = poslocal + str_chr(line.s+poslocal,'@'); if (pos + lenhost +1 >= line.len) continue; if (case_diffb(line.s+pos+1,lenhost,host)) continue; if (line.s[pos+lenhost+1] != '%') continue; /* check local */ if (poslocal + lenlocal + 5 >= line.len) continue; if (!str_start(line.s+poslocal,local)) continue; pos2 = poslocal+lenlocal; if (!str_start(line.s+pos2,"-dig-")) continue; foundlocal = 1; } if (foundlocal) { foundmatch = 1; if (flaglist && (local || flagours)) { if (substdio_put(&ssout,line.s,line.len) == -1) strerr_die2sys(111,FATAL,MSG(ERR_WRITE_STDOUT)); if (substdio_put(&ssout,"\n",1) == -1) strerr_die2sys(111,FATAL,MSG(ERR_WRITE_STDOUT)); } line.len = 0; /* same - kill line */ if (flagours) --nolists; } } close(fdin); } if (flaglist) { if (substdio_flush(&ssout) == -1) strerr_die2sys(111,FATAL,MSG1(ERR_FLUSH,"stdout")); if (foundmatch) /* means we had a match */ _exit(0); else strerr_die2x(100,FATAL,MSG(ERR_NO_MATCH)); } /* only -d and regular use left */ if (nolists >= maxlists && !flagdelete) strerr_die2x(100,FATAL,MSG(ERR_LISTNO)); if (!flagdelete) if (substdio_put(&ssout,addr.s,addr.len-1) == -1) strerr_die2sys(111,FATAL,MSG1(ERR_WRITE,"crontabn")); if (flagdelete && !foundlocal) strerr_die2x(111,FATAL,MSG(ERR_NO_MATCH)); if (substdio_flush(&ssout) == -1) strerr_die2sys(111,FATAL,MSG1(ERR_FLUSH,"crontabn")); if (fsync(fdout) == -1) strerr_die2sys(111,FATAL,MSG1(ERR_SYNC,"crontabn++")); if (close(fdout) == -1) strerr_die2sys(111,FATAL,MSG1(ERR_CLOSE,"crontabn")); wrap_rename("crontabn","crontab"); sendargs[0] = "sh"; sendargs[1] = "-c"; if (!stralloc_copys(&line,auto_cron)) die_nomem(); if (!stralloc_cats(&line,"/crontab '")) die_nomem(); if (!stralloc_cats(&line,dir.s)) die_nomem(); if (!stralloc_cats(&line,"/crontab'")) die_nomem(); if (!stralloc_0(&line)) die_nomem(); sendargs[2] = line.s; sendargs[3] = 0; if ((child = wrap_fork()) == 0) { if (setreuid(euid,euid) == -1) strerr_die2sys(100,FATAL,MSG(ERR_SETUID)); wrap_execvp(sendargs); } /* parent */ switch (wrap_waitpid(child)) { case 0: _exit(0); default: strerr_die2x(111,FATAL,MSG(ERR_CRONTAB)); } }
void main(int argc,char **argv) { char *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); } }