\n");
if (*banner == '<') oputs(banner);
else
strerr_die2x(100,FATAL,"Sorry - banner programs not supported");
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)
{
infop->date = 0;
infop->direction = DIRECT_NEXT;
finddate(infop);
}
void gtdate(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(" 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);
decline.len = 0; /* reset */
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 (decline.len) { /* flush last line that doesn't */
if (flaghtml) /* end in \n for QP/base64 */
oput(decline.s,decline.len);
else
anchor_put(decline.s,decline.len);
decline.len = 0;
}
if (flagpre) { /* ending part was
*/
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);
html_put(decline.s,decline.len);
} else {
decodeHDR(hdr[i].s,hdr[i].len,&decline,"",FATAL);
html_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 (mime_current->ctenc == CTENC_QP)
decodeQ(line.s,line.len,&decline);
else
decodeB(line.s,line.len,&decline);
if (decline.s[decline.len - 1] == '\n') { /* complete line */
if (!stralloc_copy(&line,&decline)) die_nomem();
decline.len = 0;
} else /* incomplete - wait for next */
line.len = 0; /* in case URL is split */
}
if (flaghtml)
oput(line.s,line.len);
else {
anchor_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->axis != 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];
pos = scan_ulong(line.s,&(msgnav[2]));
if (infop->direction == DIRECT_FIRST && infop->axis == ITEM_DATE) {
if (pos + HASHLEN + 1 < line.len)
if (!stralloc_copyb(&subject,line.s+pos+1,HASHLEN)) die_nomem();
if (!stralloc_0(&subject)) die_nomem();
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:
if (infop->direction == DIRECT_FIRST)
infop->target = msgnav[2];
else {
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 */
break;
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)
/* this is all a terrible hack */
{
(void) makefn(&fn,ITEM_DATE,infop->date,"");
infop->direction = DIRECT_FIRST;
infop->axis = ITEM_DATE;
setmsg(infop); /* got first thread */
infop->subject = subject.s;
infop->axis = ITEM_SUBJECT;
subj2msg(infop); /* get 1st message no in that thread */
}
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;
gtdate(infop,0);
break;
}
break;
case ITEM_INDEX: /* ignore direction etc - only for index */
if (!infop->target)
infop->target = infop->source;
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 (!cmd && !cppath)
cmd = argv[1];
if (!cppath || !*cppath) {
if (cmd && *cmd) {
cmd += scan_ulong(cmd,&thislistno);
if (*cmd == ':') cmd++; /* allow ':' after ln*/
} else
thislistno = 0L;
} else {
if (*cppath == '/') 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(&url,"
\n")) die_nomem();
if (!stralloc_cats(&url,"?")) die_nomem();
if (thislistno) {
if (!stralloc_catb(&url,strnum,fmt_ulong(strnum,thislistno))) die_nomem();
if (!stralloc_cats(&url,":")) die_nomem();
}
cache = 1; /* don't know if we want to cache */
/****************************** Get command ****************************/
if (msgnum) { /* to support /listno/msgno */
msginfo.target = msgnum;
msginfo.item = ITEM_MESSAGE;
cache = 2;
} else {
(void) decode_cmd(cmd,&msginfo);
if (!do_cmd(&msginfo))
cgierr("I'm sorry, Dave ... I can't do that, Dave ...","","");
}
switch (msginfo.item) {
case ITEM_MESSAGE:
if (!(ret = show_message(&msginfo))) { /* assume next exists ... */
cache = 0; /* border cond. - no cache */
msginfo.target = msginfo.source; /* show same */
msginfo.subjnav = 0;
msginfo.authnav = 0;
ret = show_message(&msginfo);
}
break;
case ITEM_AUTHOR:
if (!(ret = show_object(&msginfo,ITEM_AUTHOR)))
cgierr ("I couldn't find the author for that message","","");
break;
case ITEM_SUBJECT:
if (!(ret = show_object(&msginfo,ITEM_SUBJECT)))
cgierr ("I couldn't find the subject for that message","","");
break;
case ITEM_DATE:
if (!(ret = show_object(&msginfo,ITEM_DATE))) {
finddate(&msginfo);
ret = show_object(&msginfo,ITEM_DATE);
}
break;
case ITEM_INDEX:
ret = 1;
if (show_index(&msginfo)) break;/* msgnumber valid */
tmptarget = msginfo.target;
findlastmsg(&msginfo);
cache = 0; /* latest one - no cache */
if (msginfo.target > tmptarget) {
cache = 2; /* first one won't change */
msginfo.target = 1; /* try */
if (show_index(&msginfo)) break;
msginfo.date = 0; /* first indexes missing */
firstdate(&msginfo); /* instead get first msg of first */
date2msg(&msginfo); /* thread. */
if (show_index(&msginfo)) break;
} else
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);
}