pag@hao.UUCP (Peter Gross) (10/23/84)
/* * 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; }