/* * header.c - header functions plus some other goodies */ static char *SccsId = "@(#)header.c 2.20 6/24/83"; #include #include #include "defs.h" #include "header.h" extern char bfr[], FULLSYSNAME[], SYSNAME[], *index(); extern time_t defexp; char *hfgets(); static int seenrelay; /* * 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; if (wholething) bclear((char *) hp, sizeof (*hp)); seenrelay = 0; /* 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); hfgets(hp->nbuf, BUFLEN, fp); /* newsgroup list */ if (!nstrip(hp->nbuf)) return(NULL); ngcat(hp->nbuf); hfgets(hp->path, PATHLEN, fp); /* source path */ if (!nstrip(hp->path)) return(NULL); hfgets(hp->subdate, DATELEN, fp); /* date */ if (!nstrip(hp->subdate)) return(NULL); hfgets(hp->title, BUFLEN, fp); /* title */ if (!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 #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 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; int iu; for (iu=0; iuunrec[iu] = NULL; i = type(bfr); do { curpos = ftell(fp); hdrlineno++; switch (i) { case PATH: getfield(hp->path); break; case FROM: getfield(hp->from); break; case NEWSGROUP: getfield(hp->nbuf); break; case TITLE: getfield(hp->title); break; case SUBMIT: getfield(hp->subdate); break; case RECEIVE: getfield(hp->recdate); break; case EXPIRE: getfield(hp->expdate); break; case ARTICLEID: getfield(hp->oident); break; case MESSAGEID: getfield(hp->ident); break; case REPLYTO: getfield(hp->replyto); break; case FOLLOWID: getfield(hp->followid); break; case SENDER: getfield(hp->sender); break; case FOLLOWTO: getfield(hp->followto); break; case CONTROL: getfield(hp->ctlmsg); break; case POSTVERSION: getfield(hp->postversion); break; case DISTRIBUTION: getfield(hp->distribution); break; case ORGANIZATION: getfield(hp->organization); break; case NUMLINES: getfield(hp->numlines); hp->intnumlines = atoi(hp->numlines); break; case KEYWORDS: getfield(hp->keywords); break; case APPROVED: getfield(hp->approved); 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); seenrelay = 1; } break; case OTHER: if (unreccnt < NUNREC) { hp->unrec[unreccnt] = malloc(strlen(bfr) + 1); strcpy(hp->unrec[unreccnt], bfr); unreccnt++; } 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]; char *p, *q; if (hp->ident[0] == '\0' && hp->oident[0] != '\0') { strcpy(lbuf, hp->oident); p = index(lbuf, '.'); if (p == 0) { 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) 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) char *hpfield; { char *ptr; if (hpfield[0]) return; for (ptr = index(bfr, ':'); isspace(*++ptr); ) ; if (*ptr != '\0') { strcpy(hpfield, ptr); nstrip(hpfield); } return; } #define its(type) (prefix(ptr, type)) type(ptr) char *ptr; { char *colon, *space; if (!isalpha(*ptr) && strncmp(ptr, "From ", 5)) return FALSE; colon = index(ptr, ':'); space = index(ptr, ' '); if (!colon || colon + 1 != space) return FALSE; if (its("From: ")) if (index(ptr, '@') && !index(ptr, '!') && seenrelay) 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; 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); ngdel(strcpy(bfr, hp->nbuf)); 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); 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 (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) ungetc(c, fp); /* push back first char of next header */ return cp; }