+ } else {
+ doconfirm(ACTION_UC);
+ copy(&qq,"text/unsub-bad",flagcd,FATAL);
+ copybottom();
+ qmail_to(&qq,target.s);
+ }
+
+ } else if (str_start(action,ACTION_VC)) {
+ if (hashok(action,ACTION_VC)) {
+ r = getoff(action);
+ if (!r && flagmod)
+ strerr_die2x(0,INFO,ERR_UNSUB_NOP);
+ mod_bottom();
+ if (r) { /* success to target */
+ qmail_to(&qq,target.s);
+ if (flagverbose > 1) to_owner();
+ } else /* NOP to sender = admin. Will take */
+ qmail_to(&qq,sender); /* care of it. No need to tell owner */
+ /* if list is moderated skip - otherwise bad with > 1 mod */
+ } else {
+ if (!pmod || !flagremote) /* else anyone can get a good -vc. */
+ die_cookie();
+ doconfirm(ACTION_VC);
+ copy(&qq,"text/unsub-bad",flagcd,FATAL);
+ copybottom();
+ qmail_to(&qq,pmod);
+ }
+
+ } else if (act == AC_LIST || act == AC_LISTN) {
+
+ if (!flaglist || (!flagmod && !flagremote))
+ strerr_die2x(100,FATAL,ERR_NOT_AVAILABLE);
+ if (!pmod)
+ strerr_die2x(100,FATAL,ERR_NOT_ALLOWED);
+ qmail_puts(&qq,TXT_EZMLM_RESPONSE);
+ transferenc();
+ copy(&qq,"text/top",flagcd,FATAL);
+
+ if (act == AC_LIST) {
+ (void) code_qput(TXT_LISTMEMBERS,str_len(TXT_LISTMEMBERS));
+ i = putsubs(workdir,0L,52L,code_subto,1,FATAL);
+ } else /* listn */
+ i = putsubs(workdir,0L,52L,dummy_to,1,FATAL);
+
+ (void) code_qput("\n ======> ",11);
+ (void) code_qput(strnum,fmt_ulong(strnum,i));
+ (void) code_qput("\n",1);
+ copybottom();
+ qmail_to(&qq,pmod);
+
+ } else if (act == AC_LOG) {
+ action += actlen;
+ if (*action == '.' || *action == '_') ++action;
+ if (!flaglist || !flagremote)
+ strerr_die2x(100,FATAL,ERR_NOT_AVAILABLE);
+ if (!pmod)
+ strerr_die2x(100,FATAL,ERR_NOT_ALLOWED);
+ qmail_puts(&qq,TXT_EZMLM_RESPONSE);
+ transferenc();
+ searchlog(workdir,action,code_subto,FATAL);
+ copybottom();
+ qmail_to(&qq,pmod);
+
+ } else if (act == AC_EDIT) {
+ /* only remote admins and only if -e is specified may edit */
+ if (!flagedit || !flagremote)
+ strerr_die2x(100,FATAL,ERR_NOT_AVAILABLE);
+ if (!pmod)
+ strerr_die2x(100,FATAL,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,ERR_BAD_REQUEST);
+ if (!stralloc_copys(&fnedit,"text/")) die_nomem();
+ if (!stralloc_cats(&fnedit,action+len+1)) die_nomem();
+ if (!stralloc_0(&fnedit)) die_nomem();
+ 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,ERR_BAD_NAME);
+ if (ch == '_') fnedit.s[i-1] = '-';
+ }
+ switch(slurp(fnedit.s,&text,1024)) { /* entire file! */
+ case -1:
+ strerr_die6sys(111,FATAL,ERR_READ,dir,"/",fnedit.s,": ");
+ case 0:
+ strerr_die5x(100,FATAL,dir,"/",fnedit.s,ERR_NOEXIST);
+ }
+ if (!stralloc_copy(&line,&text)) die_nomem();
+ { /* get rid of nulls to use cookie */
+ register char *s; register unsigned int n;
+ s = line.s; n = line.len;
+ while(n--) { if (!*s) *s = '_'; ++s; }
+ }
+ if (!stralloc_cat(&line,&fnedit)) die_nomem(); /* including '\0' */
+ strnum[fmt_ulong(strnum,(unsigned long) when)] = 0;
+ cookie(hash,key.s,key.len,strnum,line.s,"-e");
+ if (!stralloc_copy(&confirm,&outlocal)) die_nomem();
+ if (!stralloc_append(&confirm,"-")) die_nomem();
+ if (!stralloc_catb(&confirm,ACTION_ED,LENGTH_ED)) die_nomem();
+ if (!stralloc_cats(&confirm,strnum)) die_nomem();
+ if (!stralloc_append(&confirm,".")) die_nomem();
+ /* action part has been checked for bad chars */
+ if (!stralloc_cats(&confirm,action + len + 1)) die_nomem();
+ if (!stralloc_append(&confirm,".")) die_nomem();
+ if (!stralloc_catb(&confirm,hash,COOKIE)) die_nomem();
+ if (!stralloc_append(&confirm,"@")) die_nomem();
+ if (!stralloc_cat(&confirm,&outhost)) die_nomem();
+ if (!stralloc_0(&confirm)) die_nomem();
+ set_cpconfirm(confirm.s);
+
+ qmail_puts(&qq,"Reply-To: ");
+ if (!quote2("ed,confirm.s)) die_nomem();
+ qmail_put(&qq,quoted.s,quoted.len);
+ qmail_puts(&qq,"\n");
+ if (!stralloc_0(&confirm)) die_nomem();
+
+ qmail_puts(&qq,TXT_EDIT_RESPONSE);
+ qmail_puts(&qq,action+len+1); /* has the '_' not '-' */
+ qmail_puts(&qq,TXT_EDIT_FOR);
+ if (!quote("ed,&outlocal)) die_nomem();
+ qmail_put(&qq,quoted.s,quoted.len);
+ qmail_puts(&qq,"@");
+ qmail_put(&qq,outhost.s,outhost.len);
+ qmail_puts(&qq,"\n");
+ transferenc();
+ copy(&qq,"text/top",flagcd,FATAL);
+ copy(&qq,"text/edit-do",flagcd,FATAL);
+ (void) code_qput(TXT_EDIT_START,str_len(TXT_EDIT_START));
+ (void) code_qput("\n",1);
+ (void) code_qput(text.s,text.len);
+ (void) code_qput(TXT_EDIT_END,str_len(TXT_EDIT_END));
+ (void) code_qput("\n",1);
+
+ } else { /* -edit only, so output list of editable files */
+ qmail_puts(&qq,TXT_EDIT_LIST);
+ transferenc();
+ copy(&qq,"text/top",flagcd,FATAL);
+ copy(&qq,"text/edit-list",flagcd,FATAL);
+ }
+ qmail_puts(&qq,"\n\n");
+ copybottom();
+ qmail_to(&qq,pmod);
+
+ } else if (str_start(action,ACTION_ED)) {
+ datetime_sec u;
+ int flaggoodfield;
+ 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;
+ if (!stralloc_copys(&fnedit,"text/")) die_nomem();
+ if (!stralloc_cats(&fnedit,fname)) die_nomem();
+ if (!stralloc_0(&fnedit)) die_nomem();
+ 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,ERR_BAD_NAME);
+ if (*y == '_') *y = '-';
+ }
+
+ lock(); /* file must not change while here */
+
+ switch (slurp(fnedit.s,&text,1024)) {
+ case -1:
+ strerr_die6sys(111,FATAL,ERR_READ,dir,"/",fnedit.s,": ");
+ case 0:
+ strerr_die5x(100,FATAL,dir,"/",fnedit.s,ERR_NOEXIST);
+ }
+ if (!stralloc_copy(&line,&text)) die_nomem();
+ { /* get rid of nulls to use cookie */
+ register char *s; register unsigned int n;
+ s = line.s; n = line.len;
+ while(n--) { if (!*s) *s = '_'; ++s; }
+ }
+ if (!stralloc_cat(&line,&fnedit)) die_nomem(); /* 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 '_' */
+ if (!stralloc_copys(&fneditn,fnedit.s)) die_nomem();
+ if (!stralloc_append(&fneditn,"_")) die_nomem();
+ if (!stralloc_0(&fneditn)) die_nomem();
+ fd = open_trunc(fneditn.s);
+ if (fd == -1)
+ strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fneditn.s,": ");
+ substdio_fdbuf(&sstext,write,fd,textbuf,sizeof(textbuf));
+ if (!stralloc_copys("ed,"")) die_nomem(); /* clear */
+ if (!stralloc_copys(&text,"")) die_nomem();
+
+ for (;;) { /* get message body */
+ if (getln(&ssin,&line,&match,'\n') == -1)
+ strerr_die2sys(111,FATAL,ERR_READ_INPUT);
+ if (!match) break;
+ if (!stralloc_cat(&text,&line)) die_nomem();
+ }
+ if (encin) { /* decode if necessary */
+ if (encin == 'B')
+ decodeB(text.s,text.len,&line,FATAL);
+ else
+ decodeQ(text.s,text.len,&line,FATAL);
+ if (!stralloc_copy(&text,&line)) die_nomem();
+ }
+ 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) { /* TXT_EDIT_START/END */
+ if (case_startb(cp+i,cpnext-cp-i,TXT_EDIT_START)) {
+ /* start tag. Store users 'quote characters', e.g. '> ' */
+ if (!stralloc_copyb("ed,cp,i)) die_nomem();
+ flaggoodfield = 1;
+ cp = cpnext + 1;
+ cpfirst = cp;
+ continue;
+ }
+ } else
+ if (case_startb(cp+i,cpnext-cp-i,TXT_EDIT_END)) {
+ flagdone = 1;
+ break;
+ }
+ }
+ if (flaggoodfield) {
+ if ((len += cpnext - cp - quoted.len + 1) > MAXEDIT)
+ strerr_die1x(100,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_die6sys(111,FATAL,ERR_WRITE,dir,"/",fneditn.s,": ");
+ }
+ cp = cpnext + 1;
+ }
+ if (!flagdone)
+ strerr_die2x(100,FATAL,ERR_NO_MARK);
+ if (substdio_flush(&sstext) == -1)
+ strerr_die6sys(111,FATAL,ERR_WRITE,dir,"/",fneditn.s,": ");
+ if (fsync(fd) == -1)
+ strerr_die6sys(111,FATAL,ERR_SYNC,dir,"/",fneditn.s,": ");
+ if (fchmod(fd, 0600) == -1)
+ strerr_die6sys(111,FATAL,ERR_CHMOD,dir,"/",fneditn.s,": ");
+ if (close(fd) == -1)
+ strerr_die6sys(111,FATAL,ERR_CLOSE,dir,"/",fneditn.s,": ");
+ if (rename(fneditn.s,fnedit.s) == -1)
+ strerr_die6sys(111,FATAL,ERR_MOVE,dir,"/",fneditn.s,": ");
+
+ unlock();
+ qmail_puts(&qq,TXT_EDIT_SUCCESS);
+ qmail_puts(&qq,fname);
+ qmail_puts(&qq,TXT_EDIT_FOR);
+ if (!quote("ed,&outlocal)) die_nomem();
+ qmail_put(&qq,quoted.s,quoted.len);
+ qmail_puts(&qq,"@");
+ qmail_put(&qq,outhost.s,outhost.len);
+ qmail_puts(&qq,"\n");
+ transferenc();
+ copy(&qq,"text/top",flagcd,FATAL);
+ copy(&qq,"text/edit-done",flagcd,FATAL);
+ copybottom();
+ qmail_to(&qq,sender); /* not necessarily from mod */
+
+ } else if (act == AC_GET) {
+