2.10.2 header.c (path truncation fix)
Peter Gross
pag at hao.UUCP
Tue Oct 23 16:47:26 AEST 1984
/*
* header.c - header functions plus some other goodies
*
* $Log: header.c,v $
* Revision 2.29 84/10/23 00:07:58 pag
* Bug fix: getfield() made incorrect assumption about the length
* of header fields, always copying BUFLEN characters. Some header
* fields are less than this (SUBDATE, RECDATE, EXPDATE, LINES), and
* some are longer (PATH and CONTROL). All kinds of nasty results
* can result from this problem, including the "path truncation"
* bug. The fix is to add an argument to getfield indicating what
* type of header field is being copied.
*
*/
#ifndef lint
static char *RcsId = "$Header: header.c,v 2.29 84/10/23 00:07:58 pag Exp $";
#endif !lint
#include <stdio.h>
#include <sys/types.h>
#include "defs.h"
#include "header.h"
extern char bfr[], *FULLSYSNAME, *index();
extern char *strcpy(), *arpadate(), *ctime();
extern time_t defexp;
char *hfgets();
char *news_version = NEWS_VERSION;
/*
* Read header from file fp into *hp. If wholething is FALSE,
* it's an incremental read, otherwise start from scratch.
* Return (FILE *) if header okay, else NULL.
*/
FILE *
hread(hp, fp, wholething)
register struct hbuf *hp;
FILE *fp;
int wholething;
{
register int len;
register int i;
if (wholething) {
for(i=0;i<NUNREC;i++)
if (hp->unrec[i] != NULL)
free(hp->unrec[i]);
else
break;
bclear(hp, sizeof (*hp));
}
/* Check that it's a B news style header. */
if (((hfgets(bfr, PATHLEN, fp) != NULL &&
*bfr >= 'A' && *bfr <= 'Z') && index(bfr, ':')))
if (frmread(fp, hp))
goto strip;
/* It's not. Try A news (begins with PROTO). */
if (*bfr != PROTO)
return(NULL);
/* Read in an A news format article. */
strncpy(hp->oident, &(bfr[1]), NAMELEN); /* file name */
if (!nstrip(hp->oident))
return(NULL);
/* Newsgroup List */
if (hfgets(hp->nbuf, BUFLEN, fp) == NULL || !nstrip(hp->nbuf))
return(NULL);
ngcat(hp->nbuf);
/* source path */
if (hfgets(hp->path, PATHLEN, fp) == NULL || !nstrip(hp->path))
return(NULL);
/* date */
if (hfgets(hp->subdate, DATELEN, fp) == NULL || !nstrip(hp->subdate))
return(NULL);
/* title */
if (hfgets(hp->title, BUFLEN, fp) == NULL || !nstrip(hp->title))
return(NULL);
strip: /* strip off sys! from front of path. */
strcpy(bfr, FULLSYSNAME);
if (strncmp(bfr, hp->path, (len = strlen(bfr))) == 0
&& index(NETCHRS, hp->path[len]))
strcpy(hp->path, &(hp->path[len+1]));
lcase(hp->nbuf);
/* Intuit the From: line if only a path was given. */
if (wholething && hp->from[0] == '\0')
intuitfrom(hp);
/* Get old and new style message ID's. */
if (wholething)
fixid(hp);
return(fp);
}
/*
* Get header info from mail-format file.
* Return non-zero on success.
*/
#include <ctype.h>
#define FROM 1
#define NEWSGROUP 2
#define TITLE 3
#define SUBMIT 4
#define RECEIVE 5
#define EXPIRE 6
#define ARTICLEID 7
#define MESSAGEID 8
#define REPLYTO 9
#define FOLLOWID 10
#define CONTROL 11
#define SENDER 12
#define FOLLOWTO 13
#define PATH 14
#define POSTVERSION 15
#define RELAYVERSION 16
#define DISTRIBUTION 17
#define ORGANIZATION 18
#define NUMLINES 19
#define KEYWORDS 20
#define APPROVED 21
#define NFID 22
#define NFFROM 23
#define OTHER 99
char *malloc();
frmread(fp, hp)
register FILE *fp;
register struct hbuf *hp;
{
int unreccnt = 0;
register int i;
long curpos;
int hdrlineno = 0;
i = type(bfr);
do {
curpos = ftell(fp);
hdrlineno++;
switch (i) {
case PATH:
getfield(hp->path, i);
break;
case FROM:
getfield(hp->from, i);
break;
case NEWSGROUP:
getfield(hp->nbuf, i);
break;
case TITLE:
getfield(hp->title, i);
break;
case SUBMIT:
getfield(hp->subdate, i);
break;
case RECEIVE:
getfield(hp->recdate, i);
break;
case EXPIRE:
getfield(hp->expdate, i);
break;
case ARTICLEID:
getfield(hp->oident, i);
break;
case MESSAGEID:
getfield(hp->ident, i);
break;
case REPLYTO:
getfield(hp->replyto, i);
break;
case FOLLOWID:
getfield(hp->followid, i);
break;
case SENDER:
getfield(hp->sender, i);
break;
case FOLLOWTO:
getfield(hp->followto, i);
break;
case CONTROL:
getfield(hp->ctlmsg, i);
break;
case POSTVERSION:
getfield(hp->postversion, i);
break;
case DISTRIBUTION:
getfield(hp->distribution, i);
break;
case ORGANIZATION:
getfield(hp->organization, i);
break;
case NUMLINES:
getfield(hp->numlines, i);
hp->intnumlines = atoi(hp->numlines);
break;
case KEYWORDS:
getfield(hp->keywords, i);
break;
case APPROVED:
getfield(hp->approved, i);
break;
case NFID:
getfield(hp->nf_id, i);
break;
case NFFROM:
getfield(hp->nf_from, i);
break;
case RELAYVERSION:
/*
* Only believe a relay version if it's the first
* line, otherwise it probably got passed through
* by some old neighbor.
*/
if (hdrlineno == 1) {
getfield(hp->relayversion, i);
}
break;
case OTHER:
if (unreccnt < NUNREC) {
if ((hp->unrec[unreccnt] = malloc((unsigned)(strlen(bfr) + 1))) != NULL ) {
strcpy(hp->unrec[unreccnt], bfr);
unreccnt++;
} else
xerror("frmread: out of memory");
}
break;
}
} while ((i = type(hfgets(bfr, LBUFLEN, fp))) > 0);
if (*bfr != '\n')
fseek(fp, curpos, 0);
if ((hp->from[0] || hp->path[0]) && hp->subdate[0] && (hp->ident[0] || hp->oident[0]))
return TRUE;
return FALSE;
}
/*
* There was no From: line in the message (because it was generated by
* an old news program). Guess what it should have been and create it.
*/
intuitfrom(hp)
register struct hbuf *hp;
{
char *tp;
char *user, *host, *fullname;
char *tailpath(), *rindex();
char *at, *dot;
tp = tailpath(hp);
user = rindex(tp, '!');
if (user == NULL)
user = tp;
else
*user++ = '\0';
/* Check for an existing Internet address on the end. */
at = index(user, '@');
if (at) {
dot = index(at, '.');
if (dot) {
strcpy(hp->from, user);
return;
}
/* @ signs are illegal except for the biggie, so */
*at = '%';
}
if (tp[0] == '.')
host = index(tp, '!') + 1;
else if (user == tp)
host = FULLSYSNAME;
else
host = tp;
tp = index(host, '@');
if (tp != NULL)
*tp = 0;
sprintf(hp->from, "%s@%s%s", user, host, MYDOMAIN);
fullname = index(hp->path, '(');
if (fullname != NULL) {
fullname--;
strcat(hp->from, fullname);
*fullname = 0;
}
}
/*
* If the message has only one of ident/oident, guess what
* the other one should be and fill them both in.
*/
fixid(hp)
register struct hbuf *hp;
{
char lbuf[100];
register char *p;
#ifdef OLD
register char *q;
#endif OLD
if (hp->ident[0] == '\0' && hp->oident[0] != '\0') {
strcpy(lbuf, hp->oident);
p = index(lbuf, '.');
if (p == NULL) {
strcpy(hp->ident, hp->oident);
return;
}
*p++ = '\0';
/*
* It may seem strange that we hardwire ".UUCP" in
* here instead of MYDOMAIN. However, we are trying
* to guess what the domain was on the posting system,
* not the local system. Since we don't really know
* what the posting system does, we just go with the
* majority - almost everyone will be a .UUCP if they
* didn't fill in their Message-ID.
*/
sprintf(hp->ident, "<%s@%s%s>", p, lbuf, ".UUCP");
}
#ifdef OLD
if (hp->oident[0] == '\0' && hp->ident[0] != '\0') {
strcpy(lbuf, hp->ident);
p = index(lbuf, '@');
if (p == 0) {
strcpy(hp->oident, hp->ident);
return;
}
*p++ = '\0';
q = index(p, '.');
if (q == NULL)
q = index(p, '>');
if (q)
*q++ = '\0';
p[SNLN] = '\0';
sprintf(hp->oident, "%s.%s", p, lbuf+1);
}
#endif
}
/*
* Get the given field of a header (char * parm) from bfr, but only
* if there's something actually there (after the colon). Don't
* bother if we already have an entry for this field.
*/
getfield(hpfield, type)
char *hpfield;
register int type;
{
register char *ptr;
register short hcount;
if (hpfield[0])
return;
for (ptr = index(bfr, ':'); isspace(*++ptr); )
;
if (*ptr != '\0') {
switch(type){
case PATH:
case CONTROL:
hcount = PATHLEN;
break;
case SUBMIT:
case RECEIVE:
case EXPIRE:
hcount = DATELEN;
break;
case FROM:
case NEWSGROUP:
case TITLE:
case ARTICLEID:
case MESSAGEID:
case REPLYTO:
case FOLLOWID:
case SENDER:
case FOLLOWTO:
case POSTVERSION:
case DISTRIBUTION:
case ORGANIZATION:
case KEYWORDS:
case APPROVED:
case NFID:
case NFFROM:
case RELAYVERSION:
hcount = BUFLEN;
break;
case NUMLINES:
hcount = 8;
break;
default:
hcount = BUFLEN;
}
strncpy(hpfield, ptr, hcount - 1);
nstrip(hpfield);
}
return;
}
#define its(type) (prefix(ptr, type))
type(ptr)
register char *ptr;
{
register char *colon, *space;
if (ptr == NULL)
return FALSE;
if (!isalpha(*ptr) && strncmp(ptr, "From ", 5))
return FALSE;
colon = index(ptr, ':');
space = index(ptr, ' ');
if (!colon || space && space < colon)
return FALSE;
if (its("From: "))
if (index(ptr, '@') && !index(ptr, '!'))
return FROM;
else
return PATH;
if (its("Path: "))
return PATH;
if (its("Newsgroups: "))
return NEWSGROUP;
if (its("Subject: ") || its("Title: "))
return TITLE;
if (its("Posted: ") || its("Date: "))
return SUBMIT;
if (its("Date-Received: ") || its("Received: "))
return RECEIVE;
if (its("Expires: "))
return EXPIRE;
if (its("Article-I.D.: "))
return ARTICLEID;
if (its("Message-ID: "))
return MESSAGEID;
if (its("Reply-To: "))
return REPLYTO;
if (its("References: "))
return FOLLOWID;
if (its("Control: "))
return CONTROL;
if (its("Sender: "))
return SENDER;
if (its("Followup-To: "))
return FOLLOWTO;
if (its("Posting-Version: "))
return POSTVERSION;
if (its("Relay-Version: "))
return RELAYVERSION;
if (its("Distribution: "))
return DISTRIBUTION;
if (its("Organization: "))
return ORGANIZATION;
if (its("Lines: "))
return NUMLINES;
if (its("Keywords: "))
return KEYWORDS;
if (its("Approved: "))
return APPROVED;
if (its("Nf-ID: "))
return NFID;
if (its("Nf-From: "))
return NFFROM;
return OTHER;
}
/*
* Generate the current version of the news software.
*/
char *
genversion()
{
static char retbuf[32];
char rb[100];
register char *t;
strcpy(rb, news_version);
while (t = index(rb, '\t'))
*t = ' ';
/* This is B news, so we say "B", the version, and the date. */
sprintf(retbuf, "version %s; site %s%s", rb, FULLSYSNAME, MYDOMAIN);
return retbuf;
}
/*
* Write header at 'hp' on stream 'fp' in B format. This goes out to
* some other system.
*/
hwrite(hp, fp)
register struct hbuf *hp;
register FILE *fp;
{
ihwrite(hp, fp, 0);
}
/*
* Same as above, except include receival date for local usage and
* an extra \n for looks.
*/
lhwrite(hp, fp)
register struct hbuf *hp;
register FILE *fp;
{
ihwrite(hp, fp, 1);
}
/*
* Write header at 'hp' on stream 'fp' in B+ format. Include received date
* if wr is 1. Leave off sysname if wr is 2.
*/
ihwrite(hp, fp, wr)
register struct hbuf *hp;
register FILE *fp;
int wr;
{
int iu;
time_t t;
time_t cgtdate();
fprintf(fp, "Relay-Version: %s\n", genversion());
if (*hp->postversion)
fprintf(fp, "Posting-Version: %s\n", hp->postversion);
/*
* We're being tricky with Path/From because of upward compatibility
* issues. The new version considers From and Path to be separate.
* The old one thinks they both mean "Path" but only believes the
* first one it sees, so will ignore the second.
*/
if (prefix(hp->path, FULLSYSNAME))
fprintf(fp, "Path: %s\n", hp->path);
else
fprintf(fp, "Path: %s!%s\n", FULLSYSNAME, hp->path);
if (hp->from[0])
fprintf(fp, "From: %s\n", hp->from);
strcpy(bfr, hp->nbuf);
ngdel(bfr);
fprintf(fp, "Newsgroups: %s\n", bfr);
fprintf(fp, "Subject: %s\n", hp->title);
fixid(hp);
fprintf(fp, "Message-ID: %s\n", hp->ident);
t = cgtdate(hp->subdate);
fprintf(fp, "Date: %s\n", arpadate(&t));
#ifdef OLD
fprintf(fp, "Article-I.D.: %s\n", hp->oident);
fprintf(fp, "Posted: %s", ctime(&t));
#endif
if (wr == 1)
fprintf(fp, "Date-Received: %s\n", hp->recdate);
if (*hp->expdate)
fprintf(fp, "Expires: %s\n", hp->expdate);
if (*hp->followid)
fprintf(fp, "References: %s\n", hp->followid);
if (*hp->ctlmsg)
fprintf(fp, "Control: %s\n", hp->ctlmsg);
if (*hp->sender)
fprintf(fp, "Sender: %s\n", hp->sender);
if (*hp->replyto)
fprintf(fp, "Reply-To: %s\n", hp->replyto);
if (*hp->followto)
fprintf(fp, "Followup-To: %s\n", hp->followto);
if (*hp->distribution)
fprintf(fp, "Distribution: %s\n", hp->distribution);
if (*hp->organization)
fprintf(fp, "Organization: %s\n", hp->organization);
if (*hp->numlines)
fprintf(fp, "Lines: %s\n", hp->numlines);
if (*hp->keywords)
fprintf(fp, "Keywords: %s\n", hp->keywords);
if (*hp->approved)
fprintf(fp, "Approved: %s\n", hp->approved);
if (*hp->nf_id)
fprintf(fp, "Nf-ID: %s\n", hp->nf_id);
if (*hp->nf_from)
fprintf(fp, "Nf-From: %s\n", hp->nf_from);
for (iu = 0; iu < NUNREC; iu++) {
if (hp->unrec[iu])
fprintf(fp, "%s", &hp->unrec[iu][0]);
}
putc('\n', fp);
}
/*
* Set nc bytes, starting at cp, to zero.
*/
bclear(cp, nc)
register char *cp;
register int nc;
{
while (nc--)
*cp++ = 0;
}
/*
* hfgets is like fgets, but deals with continuation lines.
* It also ensures that even if a line that is too long is
* received, the remainder of the line is thrown away
* instead of treated like a second line.
*/
char *
hfgets(buf, len, fp)
char *buf;
int len;
FILE *fp;
{
register int c;
register char *cp, *tp;
cp = fgets(buf, len, fp);
if (cp == NULL)
return NULL;
tp = cp + strlen(cp);
if (tp[-1] != '\n') {
/* Line too long - part read didn't fit into a newline */
while ((c = getc(fp)) != '\n' && c != EOF)
;
} else if (tp == (cp+1))
return(cp); /* Don't look for continuation of blank lines */
else
*--tp = '\0'; /* clobber newline */
while ((c = getc(fp)) == ' ' || c == '\t') { /* for each cont line */
/* Continuation line. */
while ((c = getc(fp)) == ' ' || c == '\t') /* skip white space */
;
if (c == '\n') /* oops, it's really a blank line (I hope) */
break;
if (tp-cp < len) {*tp++ = ' '; *tp++ = c;}
while ((c = getc(fp)) != '\n' && c != EOF)
if (tp-cp < len) *tp++ = c;
}
*tp++ = '\n';
*tp++ = '\0';
if (c != EOF)
(void) ungetc(c, fp); /* push back first char of next header */
return cp;
}
More information about the Comp.sources.unix
mailing list