\n");
+ if (*banner == '<') oputs(banner);
+ else {
+ substdio_flush(&ssout);
+ sig_pipeignore();
+ bannerargs[0] = banner;
+ bannerargs[1] = host;
+ bannerargs[2] = local;
+ bannerargs[3] = 0;
+ /* We log errors but just complete the page anyway, since we're */
+ /* already committed to output something. */
+ switch(child = fork()) {
+ case -1:
+ strerr_warn3(FATAL,ERR_FORK,"banner program: ",&strerr_sys);
+ break;
+ case 0:
+ execv(*bannerargs,bannerargs);
+ strerr_die3x(100,FATAL,ERR_EXECUTE,"banner program: ");
+ break;
+ }
+ /* parent */
+ wait_pid(&wstat,child);
+ if (wait_crashed(wstat))
+ strerr_warn2(FATAL,ERR_CHILD_CRASHED,(struct strerr *) 0);
+ if (wait_exitcode(wstat))
+ strerr_warn2(FATAL,ERR_CHILD_UNKNOWN,(struct strerr *) 0);
+ }
+ oputs("
\n");
+ }
+ oputs("\n\n");
+ substdio_flush(&ssout);
+}
+
+/* DATE functions */
+
+void datelink(struct msginfo *infop,unsigned long d,char direction)
+/* output a date with link back to thread index */
+{
+ oput(url.s,url.len);
+ cmdstr[0] = ITEM[ITEM_DATE];
+ cmdstr[1] = ITEM[ITEM_DATE];
+ cmdstr[2] = DIRECT[direction + 1];
+ oputs(cmdstr);
+ if (direction == DIRECT_LAST)
+ oput("0",1); /* suppress msgnum to avoid going there */
+ else
+ oput(strnum,fmt_ulong(strnum,infop->target));
+ oputs(":");
+ oput(strnum,fmt_ulong(strnum,d));
+ oputs("#b\">");
+ switch (direction) {
+ case DIRECT_SAME:
+ if (dateline(&dtline,d) < 0) die_nomem();
+ oput(dtline.s,dtline.len);
+ break;
+ case DIRECT_PREV:
+ oputs("[<-]");
+ break;
+ case DIRECT_NEXT:
+ oputs("[->]");
+ break;
+ case DIRECT_FIRST:
+ oputs("[<<-]");
+ break;
+ case DIRECT_LAST:
+ oputs("[->>]");
+ break;
+ }
+ oputs("");
+}
+
+void finddate(struct msginfo *infop)
+/* DIRECT_SAME works as DIRECT_PREV, dvs returns previous date or last date */
+{
+ DIR *archivedir;
+ direntry *d;
+ unsigned long ddate, startdate;
+ unsigned long below, above;
+
+ below = 0L;
+ above = MAXULONG; /* creating a Y 0xffffff problem */
+ startdate = infop->date;
+ archivedir = opendir("archive/threads/");
+ if (!archivedir)
+ if (errno != error_noent)
+ strerr_die4sys(111,FATAL,ERR_OPEN,dir,"/archive/threads: ");
+ else
+ strerr_die4sys(100,FATAL,ERR_OPEN,dir,"/archive/threads: ");
+
+ while ((d = readdir(archivedir))) { /* dxxx/ */
+ if (str_equal(d->d_name,".")) continue;
+ if (str_equal(d->d_name,"..")) continue;
+ scan_ulong(d->d_name,&ddate);
+ if (!ddate) continue; /* just in case some smart guy ... */
+ if (startdate) {
+ if (ddate > startdate && ddate < above) above = ddate;
+ if (ddate < startdate && ddate > below) below = ddate;
+ } else {
+ if (ddate < above) above = ddate;
+ if (ddate > below) below = ddate;
+ }
+ }
+ closedir(archivedir);
+
+ if (infop->direction == DIRECT_NEXT && above != MAXULONG || !below)
+ /* we always give a valid date as long as there is at least one */
+ infop->date = above;
+ else
+ infop->date = below;
+ return;
+}
+
+void latestdate(struct msginfo *infop,int flagfail)
+{
+ if (!flagfail) {
+ datetime_tai(&dt,now());
+ infop->date = ((unsigned long) dt.year + 1900) * 100 + dt.mon + 1;
+ } else {
+ infop->date = 0;
+ infop->direction = DIRECT_PREV;
+ finddate(infop);
+ }
+}
+
+void firstdate(struct msginfo *infop,int flagfail)
+{
+ infop->date = 0;
+ infop->direction = DIRECT_NEXT;
+ finddate(infop);
+}
+
+void getdate(struct msginfo *infop,int flagfail)
+/* infop->date has to be 0 or valid on entry. Month outside of [1-12] on */
+/* entry causes GIGO */
+{
+ if (!flagfail) { /* guess */
+ if (infop->direction == DIRECT_NEXT) {
+ infop->date++;
+ if (infop->date % 100 > 12) infop->date += (100 - 12);
+ } else if (infop->direction == DIRECT_PREV) {
+ infop->date--;
+ if (!infop->date % 100) infop->date -= (100 - 12);
+ }
+ } else
+ finddate(infop);
+ return;
+}
+
+indexlinks(struct msginfo *infop)
+{
+ unsigned long tmpmsg;
+
+ tmpmsg = infop->target;
+ infop->target = 1;
+ oputs("\n");
+ for (;;) {
+ if (getln(&ssin,&line,&match,'\n') == -1)
+ strerr_die4sys(111,FATAL,ERR_READ,fn.s,": ");
+ if (!match)
+ break;
+ pos = scan_ulong(line.s,&thismsg);
+ l = pos;
+ ch = line.s[pos++];
+ pos++;
+ if (line.len < pos + 1 + HASHLEN)
+ strerr_die2x(100,FATAL,"index line with truncated subject entry");
+ if (!stralloc_copyb(&subject,line.s+pos,HASHLEN)) die_nomem();
+ if (!stralloc_0(&subject)) die_nomem();
+ infop->axis = ITEM_SUBJECT;
+ infop->subject = subject.s;
+ oput(strnum,fmt_uint0(strnum,(unsigned int) thismsg % 100,2));
+ oputs(": ");
+ link(infop,ITEM_MESSAGE,ITEM_SUBJECT,thismsg,line.s+pos,line.len - pos - 1);
+ oputs("\n");
+ if (ch == ':') {
+ if (getln(&ssin,&line,&match,'\n') == -1)
+ strerr_die4sys(111,FATAL,ERR_READ,fn.s,": ");
+ if (!match)
+ break;
+ pos = byte_chr(line.s,line.len,';');
+ if (pos != line.len) {
+ infop->date = date2yyyymm(line.s);
+ oputs("(");
+ link(infop,ITEM_AUTHOR,ITEM_AUTHOR,thismsg,line.s+pos+1,
+ line.len - pos - 2);
+ oputs(")
\n");
+ }
+ }
+ }
+ close(fd);
+ oputs("\n
with internal resources as well as internal */
+/* ones. One might make other-charset messages external resources as well*/
+/* Now, the problem is that we need to "preview" MIME info _before_ */
+/* seeing the start boundary. */
+{
+ if (!stralloc_copyb(&decline,strnum,fmt_ulong(strnum,infop->target)))
+ die_nomem();
+ if (!stralloc_cats(&decline,":")) die_nomem();
+ if (!stralloc_0(&decline)) die_nomem();
+ decodeHDR(hdr[HDR_SUBJECT - 1].s,hdr[HDR_SUBJECT - 1].len,&line,"",FATAL);
+ if (!mime_current)
+ new_mime(); /* allocate */
+ else
+ clear_mime();
+ decode_mime_type(hdr[HDR_CT - 1].s,hdr[HDR_CT - 1].len,
+ hdr[HDR_VERSION - 1].len);
+ html_header(decline.s,line.s,line.len - 1,
+ "msgbody",SPC_BASE);
+ msglinks(infop);
+ oputs("
\n");
+}
+
+void show_part(struct msginfo *infop,int flagshowheaders,
+ int flagskip,int flagstartseen)
+/* if flagshowheaders we display headers, otherwise not */
+/* if flagstartseen we've already see the start boundary for this part, */
+/* if not we'll ignore what's there up to it */
+/* if flagskip we skip this part */
+{
+ char *cp;
+ int flaginheader;
+ int whatheader;
+ int flaggoodfield;
+ int flaghtml;
+ int btype,i;
+ unsigned int colpos,l,pos;
+ char linetype;
+
+ flaginheader = 1;
+ for (i = 0; i < NO_HDRS; i++) hdr[i].len = 0;
+ flaggoodfield = 1;
+ match = 1;
+ recursion_level++; /* one up */
+ for (;;) {
+ if (!match) return;
+ if (getln(&ssin,&line,&match,'\n') == -1)
+ strerr_die4sys(111,FATAL,ERR_READ,fn.s,": ");
+ if (!match) return;
+ if ((btype = check_boundary())) {
+ if (flagpre) {
+ oputs("");
+ toggle_flagpre(0);
+ }
+ if (mime_current->level < recursion_level) {
+ return;
+ }
+ if (btype == 1) {
+ flagstartseen = 1;
+ flaggoodfield = 1;
+ flaginheader = 1;
+ } else
+ flagstartseen = 0;
+ continue;
+ }
+ if (!flagstartseen) continue; /* skip to start */
+ if (flaginheader) {
+ if (line.len == 1) {
+ if (flagshowheaders) { /* rfc822hdr only */
+ if (flagtoplevel)
+ start_message_page(infop); /* so we can put subj in TITLE */
+ oputs("
\n");
+ for (i = 0; i < NO_HDRS; i++) {
+ if (!hdr[i].len || !headers_shown[i]) continue;
+ if (i == HDR_SUBJECT - 1 && flagtoplevel)
+ oputs("
");
+ oputs("");
+ oputs(constmap_get(&headermap,i + 1));
+ oputs(":");
+ decodeHDR(hdr[i].s,hdr[i].len,&line,"",FATAL);
+ if (i == HDR_SUBJECT - 1 && flagtoplevel) {
+ oputs("");
+ }
+ if (flagobscure && i == HDR_FROM - 1) {
+ oputs(" ");
+ decodeHDR(cp,author_name(&cp,line.s,line.len),&decline,"",FATAL);
+ htmlencode_put(decline.s,decline.len);
+ } else {
+ decodeHDR(hdr[i].s,hdr[i].len,&decline,"",FATAL);
+ htmlencode_put(decline.s,decline.len - 1);
+ }
+ if (i == HDR_SUBJECT - 1 && flagtoplevel)
+ oputs("");
+ oputs("\n
");
+ }
+ oputs("
\n");
+ }
+ flaginheader = 0;
+ flagtoplevel = 0;
+ flaggoodfield = 1;
+ flaghtml = 0;
+ if (!flagmime)
+ flagmime = hdr[HDR_VERSION - 1].len; /* MIME-Version header */
+ decode_mime_type(hdr[HDR_CT - 1].s,hdr[HDR_CT - 1].len,flagmime);
+ decode_transfer_encoding(hdr[HDR_CTENC - 1].s,hdr[HDR_CTENC - 1].len);
+ content.len = 0; encoding.len = 0;
+ switch (mime_current->mimetype) {
+ case MIME_MULTI_SIGNED:
+ case MIME_MULTI_MIXED:
+ case MIME_MULTI_ALTERNATIVE:
+ case MIME_MULTI_DIGEST:
+ show_part(infop,0,0,0);
+ recursion_level--;
+ flagstartseen = 0;
+ flaginheader = 1;
+ continue;
+ case MIME_MESSAGE_RFC822:
+ oputs("\n
");
+ toggle_flagpre(1);
+ flagshowheaders = 1;
+ flaginheader = 1;
+ flagmime = 0; /* need new MIME-Version header */
+ continue;
+ case MIME_TEXT_HTML:
+ if (flagshowhtml) {
+ oputs("
\n");
+ flaghtml = 1;
+ } else {
+ oputs("[\"");
+ oput(mime_current->ctype.s,mime_current->ctype.len);
+ oputs("\" not shown]\n");
+ flaggoodfield = 0; /* hide */
+ }
+ continue;
+ case MIME_TEXT_PLAIN:
+ case MIME_TEXT: /* in honor of Phil using "text" on */
+ case MIME_NONE: /* the qmail list and rfc2045:5.2 */
+ oputs("
\n\n");
+ toggle_flagpre(1);
+ continue;
+ case MIME_TEXT_VCARD:
+ default: /* application/octetstream...*/
+ oputs("
[\"");
+ oput(mime_current->ctype.s,mime_current->ctype.len);
+ oputs("\" not shown]\n");
+ flaggoodfield = 0; /* hide */
+ continue;
+ }
+ } else if (line.s[0] != ' ' && line.s[0] != '\t') {
+ linetype = ' ';
+ flaggoodfield = 0;
+ colpos = byte_chr(line.s,line.len,':');
+ if ((whatheader = constmap_index(&headermap,line.s,colpos))) {
+ flaggoodfield = 1;
+ if (!stralloc_copyb(&hdr[whatheader - 1],line.s + colpos + 1,
+ line.len - colpos - 1)) die_nomem();
+ }
+ } else {
+ if (whatheader)
+ if (!stralloc_catb(&hdr[whatheader - 1],line.s,line.len))
+ die_nomem();
+ }
+ } else {
+ if (flaggoodfield) {
+ if (mime_current->ctenc) {
+ if (!stralloc_copy(&decline,&line)) die_nomem();
+ line.len = 0;
+ if (mime_current->ctenc == CTENC_QP)
+ decodeQ(decline.s,decline.len,&line);
+ else
+ decodeB(decline.s,decline.len,&line);
+ }
+ if (flaghtml)
+ oput(line.s,line.len);
+ else {
+ htmlencode_put(line.s,line.len); /* body */
+ }
+ }
+ }
+ }
+}
+
+int show_message(struct msginfo *infop)
+{
+ char *psz;
+
+ if(!stralloc_copys(&headers,(char *) headers_used)) die_nomem();
+ if (!stralloc_0(&headers)) die_nomem();
+ psz = headers.s;
+ while (*psz) {
+ if (*psz == '\\') *psz = '\0';
+ ++psz;
+ }
+ if (!constmap_init(&headermap,headers.s,headers.len,0))
+ die_nomem();
+
+ (void) makefn(&fn,ITEM_MESSAGE,msginfo.target,"");
+ if ((fd = open_read(fn.s)) == -1)
+ if (errno == error_noent)
+ return 0;
+ else
+ strerr_die4sys(111,FATAL,ERR_OPEN,fn.s,": ");
+ substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
+ toggle_flagpre(0);
+ recursion_level = 0; /* recursion level for show_part */
+ flagmime = 0; /* no active mime */
+ flagtoplevel = 1; /* top message/rfc822 get special rx */
+ new_mime(); /* initiate a MIME info storage slot */
+
+ show_part(infop,1,0,1); /* do real work, including html header etc */
+ if (flagpre)
+ oputs("
\n");
+ close(fd);
+ oputs("
\n");
+ msglinks(infop);
+ html_footer(0);
+ return 1;
+}
+
+char decode_item(char ch)
+{
+ switch (ch) {
+ case 'm': return ITEM_MESSAGE;
+ case 'a': return ITEM_AUTHOR ;
+ case 's': return ITEM_SUBJECT;
+ case 'd': return ITEM_DATE ;
+ case 'i': return ITEM_INDEX ;
+ default: cgierr("Navigation command contains ",
+ "illegal item code","");
+ }
+ return 0; /* never reached */
+}
+
+char decode_direction(char ch)
+{
+ switch (ch) {
+ case 's': return DIRECT_SAME;
+ case 'n': return DIRECT_NEXT;
+ case 'p': return DIRECT_PREV;
+ default: cgierr("Navigation command contains ",
+ "illegal direction code","");
+ }
+ return 0; /* never reached */
+}
+
+int decode_cmd(char *s,struct msginfo *infop)
+/* decodes s into infop. Assures that no security problems slip through by */
+/* checking everything */
+/* commands xyd:123[:abc]. x what we want, y is the axis, d the direction. */
+/* 123 is the current message number. abc is a date/subject/author hash, */
+/* depending on axis, or empty if not available. */
+/* returns: 0 no command+msgnum. */
+/* 1 empty or at least cmd + msgnum. */
+/* Guarantee: Only legal values accepted */
+{
+ register char ch;
+
+ infop->source = 0L;
+ infop->date = 0L;
+ infop->author = (char *)0;
+ infop->subject = (char *)0;
+ infop->cgiarg = (char *)0;
+
+ if (!s || !*s) { /* main index */
+ infop->item = ITEM_DATE;
+ infop->axis = ITEM_DATE;
+ infop->direction = DIRECT_SAME;
+ latestdate(&msginfo,0);
+ infop->target = MAXULONG;
+ return 1;
+ }
+ ch = *(s++);
+ if (ch >= '0' && ch <= '9') { /* numeric - simplified cmd: msgnum ... */
+ s--;
+ infop->item = ITEM_MESSAGE;
+ infop->axis = ITEM_MESSAGE;
+ infop->direction = DIRECT_SAME;
+ } else { /* what:axis:direction:msgnum ... */
+ infop->item = decode_item(ch);
+ ch = *(s++);
+ infop->axis = decode_item(ch);
+ ch = *(s++);
+ infop->direction = decode_direction(ch);
+ if (*(s++) != ':') return 0;
+ }
+ s+= scan_ulong(s,&(infop->source));
+ if (*(s++) != ':') return 0;
+ if (*s >= '0' && *s <= '9') { /* numeric nav hint [date] */
+ s+= scan_ulong(s,&(infop->date));
+ if (!*s++) return 1; /* skip any char - should be ':' unless NUL */
+ }
+ if (checkhash(s)) { /* Ignore if illegal rather than complaining*/
+ if (!stralloc_copyb(&charg,s,HASHLEN)) die_nomem();
+ if (!stralloc_0(&charg)) die_nomem();
+ infop->cgiarg = charg.s;
+ }
+ return 1;
+}
+
+int msg2hash(struct msginfo *infop)
+{
+ unsigned int pos;
+ unsigned long tmpmsg;
+
+ if (!infop->source) die_prog("source is 0 in msg2hash");
+ (void) makefn(&fn,ITEM_INDEX,infop->source,"");
+ if ((fd = open_read(fn.s)) == -1) {
+ if (errno == error_noent)
+ return 0;
+ else
+ strerr_die4sys(111,FATAL,ERR_OPEN,fn.s,": ");
+ }
+ substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
+ for (;;) {
+ if (getln(&ssin,&line,&match,'\n') == -1)
+ strerr_die3sys(111,FATAL,ERR_READ,"index: ");
+ if (!match)
+ return 0; /* didn't find message */
+ if (*line.s == '\t') continue; /* author line */
+ pos = scan_ulong(line.s,&tmpmsg);
+ if (tmpmsg == infop->source) {
+ if (line.s[pos++] != ':' || line.s[pos++] != ' ')
+ strerr_die3x(100,ERR_SYNTAX,fn.s,": missing subject separator");
+ if (line.len < HASHLEN + pos)
+ strerr_die3x(100,ERR_SYNTAX,fn.s,": missing subject hash");
+ if (!stralloc_copyb(&subject,line.s+pos,HASHLEN)) die_nomem();
+ if (!stralloc_0(&subject)) die_nomem();
+ infop->subject = subject.s;
+ if (getln(&ssin,&line,&match,'\n') == -1)
+ strerr_die3sys(111,FATAL,ERR_READ,"index: ");
+ if (!match)
+ strerr_die3x(100,ERR_SYNTAX,fn.s,
+ ": author info missing. Truncated?");
+ pos = byte_chr(line.s,line.len,';');
+ if (pos == line.len)
+ strerr_die3x(100,ERR_SYNTAX,fn.s,"missing ';' after date");
+ if (pos > 1)
+ infop->date = date2yyyymm(line.s+1); /* ';' marks end ok */
+ pos++;
+ if (line.len < HASHLEN + pos)
+ strerr_die3x(100,ERR_SYNTAX,fn.s,": missing author hash");
+ if (!stralloc_copyb(&author,line.s+pos,HASHLEN)) die_nomem();
+ if (!stralloc_0(&author)) die_nomem();
+ infop->author = author.s;
+ close(fd);
+ return 1; /* success */
+ }
+ }
+ close(fd);
+ return 0; /* failed to match */
+}
+
+void setmsg(struct msginfo *infop)
+/* Reads the file corresponding to infop->axis and assumes fn.s is set */
+/* correctly for this. Sets up a msgnav structure and links it in */
+/* correction for axis=author/subject. For axis=date it supports also */
+/* direction=DIRECT_FIRST which will return the first message of the */
+/* first thread in the date file. DIRECT_LAST is not supported. */
+/* DIRECT_FIRST is supported ONLY for date. */
+{
+ if (infop->direction == DIRECT_SAME) {
+ infop->target = infop->source;
+ return;
+ }
+ if ((fd = open_read(fn.s)) == -1) {
+ if (errno == error_noent)
+ strerr_die4x(100,FATAL,ERR_OPEN,fn.s,
+ " in listmsgs. Rerun ezmlm-archive!");
+ else
+ strerr_die4sys(111,FATAL,ERR_OPEN,fn.s,": ");
+ }
+ substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf));
+ if (infop->source != ITEM_DATE) {
+ if (getln(&ssin,&line,&match,'\n') == -1) /* first line */
+ strerr_die4sys(111,FATAL,ERR_READ,fn.s,": ");
+ if (!match)
+ strerr_die3x(100,ERR_SYNTAX,fn.s,": first line missing");
+ }
+ msgnav[3] = 0L; /* next */
+ msgnav[4] = 0L; /* after */
+ infop->target = 0L;
+ for (;;) {
+ if (getln(&ssin,&line,&match,'\n') == -1)
+ strerr_die4sys(111,FATAL,ERR_READ,fn.s,": ");
+ if (!match) break;
+ msgnav[0] = msgnav[1];
+ msgnav[1] = msgnav[2];
+ (void) scan_ulong(line.s,&(msgnav[2]));
+ if (infop->direction == DIRECT_FIRST) break;
+ if (msgnav[2] == infop->source) {
+ if (getln(&ssin,&line,&match,'\n') == -1)
+ strerr_die4sys(111,FATAL,ERR_READ,fn.s,": ");
+ if (!match) break;
+ (void) scan_ulong(line.s,&(msgnav[3]));
+ if (getln(&ssin,&line,&match,'\n') == -1)
+ strerr_die4sys(111,FATAL,ERR_READ,fn.s,": ");
+ if (!match) break;
+ (void) scan_ulong(line.s,&(msgnav[4]));
+ break;
+ }
+ }
+ close(fd);
+ switch (infop->axis) {
+ case ITEM_AUTHOR:
+ infop->authnav = msgnav + 2 + infop->direction;
+ infop->target = *(infop->authnav);
+ infop->subject = (char *)0; /* what we know is not for this msg */
+ infop->date = 0;
+ break;
+ case ITEM_SUBJECT:
+ infop->subjnav = msgnav + 2 + infop->direction;
+ infop->target = *(infop->subjnav);
+ infop->author = (char *)0; /* what we know is not for this msg */
+ infop->date = 0;
+ break;
+ case ITEM_DATE:
+ infop->target = msgnav[2];
+ infop->subject = (char *)0; /* what we know is not for this msg */
+ infop->author = (char *)0; /* what we know is not for this msg */
+ default:
+ die_prog("Bad item in setmsg");
+ }
+ return;
+}
+
+void auth2msg(struct msginfo *infop)
+{
+ if (!infop->author) die_prog("no such author in authmsg");
+ if (!makefn(&fn,ITEM_AUTHOR,0L,infop->author)) die_prog("auth2msg");
+ setmsg(infop);
+}
+
+void subj2msg(struct msginfo *infop)
+{
+ if (!infop->subject) die_prog("no such subject in subj2msg");
+ if (!makefn(&fn,ITEM_SUBJECT,0L,infop->subject)) die_prog("subj2msg");
+ setmsg(infop);
+}
+
+void date2msg(struct msginfo *infop)
+{
+ (void) makefn(&fn,ITEM_DATE,infop->date,"");
+ setmsg(infop);
+}
+
+void findlastmsg(struct msginfo *infop)
+{
+ if (!getconf_line(&line,"num",dir,0,FATAL))
+ cgierr("Sorry, there are no messages in the archive","","");
+ if (!stralloc_0(&line)) die_nomem();
+ (void) scan_ulong(line.s,&(infop->target));
+}
+
+int do_cmd(struct msginfo *infop)
+/* interprets msginfo to create msginfo. Upon return, msginfo can be trusted */
+/* to have all info needed, and that all info is correct. There may be more */
+/* info than needed. This can be used to build more specific links. NOTE: */
+/* there is no guarantee that a message meeting the criteria actually exists*/
+{
+ infop->target = infop->source;
+
+ switch (infop->item) {
+ case ITEM_MESSAGE: /* we want to get a message back */
+ {
+ switch (infop->axis) {
+ case ITEM_MESSAGE:
+ if (infop->direction == DIRECT_SAME)
+ break;
+ else if (infop->direction == DIRECT_NEXT)
+ (infop->target)++;
+ else { /* previous */
+ cache = 2;
+ if (infop->target >= 2)
+ (infop->target)--;
+ else
+ infop->target = 1;
+ }
+ break;
+ case ITEM_AUTHOR:
+ infop->author = infop->cgiarg;
+ if (!infop->author) /* we don't know author hash */
+ if (!msg2hash(infop)) return 0;
+ auth2msg(infop);
+ break;
+ case ITEM_SUBJECT:
+ infop->subject = infop->cgiarg;
+ if (!infop->subject) /* we don't know Subject hash */
+ if (!msg2hash(infop)) return 0;
+ subj2msg(infop);
+ break;
+ }
+ break;
+ }
+ case ITEM_AUTHOR:
+ switch (infop->axis) {
+ case ITEM_MESSAGE:
+ if (!infop->author)
+ if (!msg2hash(infop)) return 0;
+ break;
+ case ITEM_AUTHOR:
+ infop->author = infop->cgiarg;
+ if (!infop->author)
+ if (!msg2hash(infop)) return 0;
+ auth2msg(infop);
+ break;
+ case ITEM_SUBJECT:
+ infop->subject = infop->cgiarg;
+ if (!infop->subject) /* we don't know Subject hash */
+ if (!msg2hash(infop)) return 0;
+ subj2msg(infop);
+ break;
+ }
+ break;
+ case ITEM_SUBJECT:
+ switch (infop->axis) {
+ case ITEM_MESSAGE:
+ if (!msg2hash(infop)) return 0;
+ break;
+ case ITEM_AUTHOR:
+ infop->author = infop->cgiarg;
+ if (!infop->author)
+ if (!msg2hash(infop)) return 0;
+ auth2msg(infop);
+ break;
+ case ITEM_SUBJECT:
+ infop->subject = infop->cgiarg;
+ if (!infop->subject) /* we don't know Subject hash */
+ if (!msg2hash(infop)) return 0;
+ subj2msg(infop);
+ break;
+ }
+ break;
+ case ITEM_DATE: /* want a date reference */
+ switch (infop->axis) {
+ case ITEM_MESSAGE:
+ case ITEM_AUTHOR:
+ case ITEM_SUBJECT:
+ case ITEM_DATE:
+ if (!infop->date && infop->source)
+ if (!msg2hash(infop)) return 0;
+ getdate(infop,0);
+ break;
+ }
+ break;
+ case ITEM_INDEX: /* ignore direction etc - only for index */
+ if (!infop->target)
+ infop->target = infop->source;
+ if (!infop->target)
+ findlastmsg(infop);
+ break;
+ }
+ return 1;
+}
+
+void list_lists()
+{
+ unsigned long lno;
+ cache = 2;
+ flagrobot = 2;
+ html_header("Robot index of lists",0,0,0,0);
+ for (;;) {
+ if (getln(&ssin,&cfline,&match,'\n') == -1) /* read line */
+ strerr_die4sys(111,FATAL,ERR_READ,fn.s,": ");
+ if (!match)
+ break;
+ if (cfline.s[0] == '#') continue; /* skip comment */
+ cfline.s[cfline.len - 1] = '\0'; /* so all are sz */
+ (void) scan_ulong(cfline.s,&lno); /* listno for line */
+ if (lno) { /* don't expose default list */
+ oputs("
[link]\n");
+ }
+ }
+ html_footer(0);
+}
+
+void list_list(unsigned long listno)
+/* Make one link [for list_set()] per set of 100 archive messages. */
+/* Assumption: Any directory DIR/archive/xxx where 'xxx' is a numeric,*/
+/* is part of the list archive and has in it an index file and one */
+/* or more messages. */
+{
+ DIR *archivedir;
+ direntry *d;
+ unsigned long msgset;
+
+ flagrobot = 2;
+ strnum[fmt_ulong(strnum,listno)] = '\0';
+ archivedir = opendir("archive/");
+ if (!archivedir)
+ if (errno != error_noent)
+ strerr_die4sys(111,FATAL,ERR_OPEN,dir,"/archive: ");
+ else
+ strerr_die4sys(100,FATAL,ERR_OPEN,dir,"/archive: ");
+
+ cache = 1;
+ html_header("Robot index for message sets in list",0,0,0,0);
+
+ while ((d = readdir(archivedir))) {
+ if (d->d_name[scan_ulong(d->d_name,&msgset)])
+ continue; /* not numeric */
+ oputs("
d_name);
+ oputs("\">[link]\n");
+ }
+ closedir(archivedir);
+ html_footer(0);
+}
+
+void list_set(unsigned long listno,unsigned long msgset)
+{
+ unsigned int msgfirst,msgmax;
+ unsigned long lastset;
+
+ flagrobot = 2;
+ findlastmsg(&msginfo);
+ if (!stralloc_copys(&line,"
lastset) { /* assure empty list */
+ msgmax = 0;
+ msgfirst = 1;
+ } else if (msgset == lastset) {
+ cache = 0; /* still changing */
+ msgmax = msginfo.target % 100;
+ }
+ html_header("Robot index for messages in set",0,0,0,0);
+ while (msgfirst <= msgmax) {
+ oput(line.s,line.len);
+ oput(strnum,fmt_uint0(strnum,msgfirst,2));
+ oputs("\">[link]\n");
+ msgfirst++;
+ }
+ html_footer(0);
+}
+
+/**************** MAY BE SUID ROOT HERE ****************************/
+void drop_priv(int flagchroot)
+{
+ if (!uid) strerr_die2x(100,FATAL,ERR_SUID); /* not as root */
+ if (!euid) {
+ if (flagchroot)
+ if (chroot(dir) == -1) /* chroot listdir */
+ strerr_die4sys(111,FATAL,"failed to chroot ",dir,": ");
+ if (setuid(uid) == -1) /* setuid */
+ strerr_die2sys(111,FATAL,ERR_SETUID);
+ }
+ euid = (unsigned long) geteuid();
+ if (!euid) strerr_die2x(100,FATAL,ERR_SUID); /* setuid didn't do it*/
+}
+/*******************************************************************/
+
+int main(argc,argv)
+int argc;
+char **argv;
+{
+ char *cp,*cppath;
+ unsigned long listno,thislistno,tmpuid,msgset;
+ unsigned long msgnum = 0;
+ unsigned long port = 0L;
+ unsigned long tmptarget;
+ unsigned int pos,l;
+ int flagindex = 0;
+ int flagchroot = 1; /* chroot listdir if SUID root */
+ int ret;
+ char sep;
+
+/******************** we may be SUID ROOT ******************************/
+ uid = (unsigned long) getuid(); /* should be http */
+ euid = (unsigned long) geteuid(); /* chroot only if 0 */
+
+ if (!euid) {
+ if ((fd = open_read(EZ_CGIRC)) == -1) /* open config */
+ strerr_die4sys(111,FATAL,ERR_OPEN,EZ_CGIRC,": ");
+ } else {
+ if ((fd = open_read(EZ_CGIRC_LOC)) == -1) /* open local config */
+ strerr_die4sys(111,FATAL,ERR_OPEN,EZ_CGIRC_LOC,": ");
+ }
+
+ substdio_fdbuf(&ssin,read,fd,inbuf,sizeof(inbuf)); /* set up buffer */
+ /* ##### tainted info #####*/
+
+ cmd = env_get("QUERY_STRING"); /* get command */
+ cppath = env_get("PATH_INFO"); /* get path_info */
+
+ if (!cppath || !*cppath) {
+ if (cmd && *cmd) {
+ cmd += scan_ulong(cmd,&thislistno);
+ if (*cmd == ':') cmd++; /* allow ':' after ln*/
+ } else
+ thislistno = 0L;
+ } else {
+ cppath++;
+ cppath += scan_ulong(cppath,&thislistno); /* this listno */
+ if (!thislistno || *cppath++ == '/') {
+ if (str_start(cppath,"index")) {
+ cppath += 5;
+ flagindex = 1;
+ if (!thislistno) { /* list index */
+ drop_priv(0); /* <---- dropping privs */
+ list_lists();
+ close(fd);
+ _exit(0);
+ }
+ }
+ } /* rest done per list */
+ }
+
+ for (;;) {
+ if (getln(&ssin,&cfline,&match,'\n') == -1) /* read line */
+ strerr_die4sys(111,FATAL,ERR_READ,fn.s,": ");
+ if (!match)
+ break;
+ if (*cfline.s == '#' || cfline.len == 1) continue; /* skip comment/blank */
+ cfline.s[cfline.len - 1] = '\0'; /* so all are sz */
+ pos = scan_ulong(cfline.s,&listno); /* listno for line */
+ if (thislistno != listno) continue;
+ sep = cfline.s[pos++];
+ if (cfline.s[pos] == '-') { /* no chroot if -uid*/
+ flagchroot = 0;
+ pos++;
+ }
+ pos += scan_ulong(cfline.s+pos,&tmpuid); /* listno for line */
+ if (tmpuid) uid = tmpuid; /* override default */
+ if (!cfline.s[pos++] == sep)
+ die_syntax("missing separator after user id");
+ if (cfline.s[pos] != '/')
+ die_syntax("dir"); /* absolute path */
+ l = byte_chr(cfline.s + pos, cfline.len - pos,sep);
+ if (l == cfline.len - pos) /* listno:path:...*/
+ die_syntax("missing separator after path");
+ dir = cfline.s + pos;
+ pos += l;
+ cfline.s[pos++] = '\0'; /* .../dir\0 */
+ break; /* do rest after dropping priv */
+ }
+ close(fd); /* don't accept uid 0*/
+ if (!dir) {
+ drop_priv(0); /* don't trust cgierr. No dir, no chroot */
+ cgierr("list ",ERR_NOEXIST,"");
+ }
+ if (chdir(dir) == -1) /* chdir listdir */
+ strerr_die4sys(111,FATAL,ERR_SWITCH,dir,": ");
+ drop_priv(flagchroot);
+
+/******************************* RELAX **********************************/
+
+/********************* continue to process config line ******************/
+
+ flagrobot = 0;
+ if (cfline.s[pos] == '-') {
+ flagobscure = 1;
+ pos++;
+ }
+ local = cfline.s + pos;
+ l = byte_chr(cfline.s + pos, cfline.len - pos,sep); /* ... home */
+ if (l < cfline.len - pos) { /* optional */
+ pos += l;
+ cfline.s[pos++] = '\0';
+ home = cfline.s + pos;
+ l = byte_chr(cfline.s + pos, cfline.len - pos,sep); /* ... charset */
+ if (l < cfline.len - pos) { /* optional */
+ pos += l;
+ cfline.s[pos++] = '\0';
+ charset = cfline.s + pos;
+ l = byte_chr(cfline.s+pos,cfline.len - pos,sep); /* ... stylesheet */
+ if (l < cfline.len - pos) { /* optional */
+ pos += l;
+ cfline.s[pos++] = '\0';
+ stylesheet = cfline.s + pos;
+ l = byte_chr(cfline.s+pos,cfline.len-pos,sep); /* ... bannerURL */
+ if (l < cfline.len - pos) { /* optional */
+ pos += l;
+ cfline.s[pos++] = '\0';
+ banner = cfline.s + pos;
+ }
+ }
+ }
+ }
+ if (!charset || !*charset) /* rfc822 default */
+ charset = EZ_CHARSET;
+ if (!stralloc_copys(&curcharset,charset)) die_nomem();
+ csbase = decode_charset(curcharset.s,curcharset.len);
+ if (csbase == CS_BAD) csbase = CS_NONE;
+ cs = csbase;
+ pos = + str_rchr(local,'@');
+ if (!local[pos])
+ die_syntax("listaddress lacks '@'"); /* require host */
+ local[pos++] = '\0';
+ host = local + pos;
+
+/********************* Accomodate robots and PATH_INFO ****************/
+
+ if (flagindex) {
+ if (*(cppath++) == '/') { /* /2/index/123 */
+ cppath += scan_ulong(cppath,&msgset);
+ list_set(thislistno,msgset);
+ } else /* /2/index */
+ list_list(thislistno);
+ _exit(0);
+ }
+
+ if (cppath && *cppath) { /* /2/msgnum */
+ flagrobot = 1; /* allow index, but "nofollow" */
+ scan_ulong(cppath,&msgnum);
+ } /* dealt with normally */
+
+/********************* Get info from server on BASE etc ****************/
+
+ if (!stralloc_copys(&base,"
\n")) die_nomem();
+ if (!stralloc_copys(&url,"
tmptarget) {
+ cache = 2; /* won't change */
+ firstdate(&msginfo,1); /* first thread index */
+ msginfo.direction = DIRECT_FIRST;
+ date2msg(&msginfo); /* (may not be 1 if parts removed) */
+ }
+ ret = show_index(&msginfo);
+ }
+ break;
+ default:
+ strerr_die2x(100,FATAL,"bad item in main");
+ }
+ if (!ret) {
+ findlastmsg(&msginfo); /* as last resort; last msgindex */
+ cache = 0;
+ ret = show_message(&msginfo);
+ }
+
+ _exit(0);
+}