|
|
1.1 root 1: /*
2: * header.c - header functions plus some other goodies
3: */
4:
5: static char *SccsId = "@(#)header.c 2.20 6/24/83";
6:
7: #include <stdio.h>
8: #include <sys/types.h>
9: #include "defs.h"
10: #include "header.h"
11:
12: extern char bfr[], FULLSYSNAME[], SYSNAME[], *index();
13: extern time_t defexp;
14:
15: char *hfgets();
16:
17: static int seenrelay;
18:
19: /*
20: * Read header from file fp into *hp. If wholething is FALSE,
21: * it's an incremental read, otherwise start from scratch.
22: * Return (FILE *) if header okay, else NULL.
23: */
24: FILE *
25: hread(hp, fp, wholething)
26: register struct hbuf *hp;
27: FILE *fp;
28: int wholething;
29: {
30: register int len;
31:
32: if (wholething)
33: bclear((char *) hp, sizeof (*hp));
34:
35: seenrelay = 0;
36:
37: /* Check that it's a B news style header. */
38: if (((hfgets(bfr, PATHLEN, fp) != NULL &&
39: *bfr >= 'A' && *bfr <= 'Z') && index(bfr, ':')))
40: if (frmread(fp, hp))
41: goto strip;
42:
43: /* It's not. Try A news (begins with PROTO). */
44: if (*bfr != PROTO)
45: return(NULL);
46:
47: /* Read in an A news format article. */
48: strncpy(hp->oident, &(bfr[1]), NAMELEN); /* file name */
49: if (!nstrip(hp->oident))
50: return(NULL);
51: hfgets(hp->nbuf, BUFLEN, fp); /* newsgroup list */
52: if (!nstrip(hp->nbuf))
53: return(NULL);
54: ngcat(hp->nbuf);
55: hfgets(hp->path, PATHLEN, fp); /* source path */
56: if (!nstrip(hp->path))
57: return(NULL);
58: hfgets(hp->subdate, DATELEN, fp); /* date */
59: if (!nstrip(hp->subdate))
60: return(NULL);
61: hfgets(hp->title, BUFLEN, fp); /* title */
62: if (!nstrip(hp->title))
63: return(NULL);
64:
65: strip: /* strip off sys! from front of path. */
66: strcpy(bfr, FULLSYSNAME);
67: if (strncmp(bfr, hp->path, (len = strlen(bfr))) == 0
68: && index(NETCHRS, hp->path[len]))
69: strcpy(hp->path, &(hp->path[len+1]));
70: lcase(hp->nbuf);
71:
72: /* Intuit the From: line if only a path was given. */
73: if (wholething && hp->from[0] == '\0')
74: intuitfrom(hp);
75:
76: /* Get old and new style message ID's. */
77: if (wholething)
78: fixid(hp);
79:
80: return(fp);
81: }
82:
83:
84: /*
85: * Get header info from mail-format file.
86: * Return non-zero on success.
87: */
88: #include <ctype.h>
89: #define FROM 1
90: #define NEWSGROUP 2
91: #define TITLE 3
92: #define SUBMIT 4
93: #define RECEIVE 5
94: #define EXPIRE 6
95: #define ARTICLEID 7
96: #define MESSAGEID 8
97: #define REPLYTO 9
98: #define FOLLOWID 10
99: #define CONTROL 11
100: #define SENDER 12
101: #define FOLLOWTO 13
102: #define PATH 14
103: #define POSTVERSION 15
104: #define RELAYVERSION 16
105: #define DISTRIBUTION 17
106: #define ORGANIZATION 18
107: #define NUMLINES 19
108: #define KEYWORDS 20
109: #define APPROVED 21
110: #define OTHER 99
111:
112: char *malloc();
113:
114: frmread(fp, hp)
115: register FILE *fp;
116: register struct hbuf *hp;
117: {
118: int unreccnt = 0;
119: register int i;
120: long curpos;
121: int hdrlineno = 0;
122: int iu;
123:
124: for (iu=0; iu<NUNREC; iu++)
125: hp->unrec[iu] = NULL;
126:
127: i = type(bfr);
128: do {
129: curpos = ftell(fp);
130: hdrlineno++;
131: switch (i) {
132: case PATH:
133: getfield(hp->path);
134: break;
135: case FROM:
136: getfield(hp->from);
137: break;
138: case NEWSGROUP:
139: getfield(hp->nbuf);
140: break;
141: case TITLE:
142: getfield(hp->title);
143: break;
144: case SUBMIT:
145: getfield(hp->subdate);
146: break;
147: case RECEIVE:
148: getfield(hp->recdate);
149: break;
150: case EXPIRE:
151: getfield(hp->expdate);
152: break;
153: case ARTICLEID:
154: getfield(hp->oident);
155: break;
156: case MESSAGEID:
157: getfield(hp->ident);
158: break;
159: case REPLYTO:
160: getfield(hp->replyto);
161: break;
162: case FOLLOWID:
163: getfield(hp->followid);
164: break;
165: case SENDER:
166: getfield(hp->sender);
167: break;
168: case FOLLOWTO:
169: getfield(hp->followto);
170: break;
171: case CONTROL:
172: getfield(hp->ctlmsg);
173: break;
174: case POSTVERSION:
175: getfield(hp->postversion);
176: break;
177: case DISTRIBUTION:
178: getfield(hp->distribution);
179: break;
180: case ORGANIZATION:
181: getfield(hp->organization);
182: break;
183: case NUMLINES:
184: getfield(hp->numlines);
185: hp->intnumlines = atoi(hp->numlines);
186: break;
187: case KEYWORDS:
188: getfield(hp->keywords);
189: break;
190: case APPROVED:
191: getfield(hp->approved);
192: break;
193: case RELAYVERSION:
194: /*
195: * Only believe a relay version if it's the first
196: * line, otherwise it probably got passed through
197: * by some old neighbor.
198: */
199: if (hdrlineno == 1) {
200: getfield(hp->relayversion);
201: seenrelay = 1;
202: }
203: break;
204: case OTHER:
205: if (unreccnt < NUNREC) {
206: hp->unrec[unreccnt] = malloc(strlen(bfr) + 1);
207: strcpy(hp->unrec[unreccnt], bfr);
208: unreccnt++;
209: }
210: break;
211: }
212: } while ((i = type(hfgets(bfr, LBUFLEN, fp))) > 0);
213:
214: if (*bfr != '\n')
215: fseek(fp, curpos, 0);
216: if ((hp->from[0] || hp->path[0]) && hp->subdate[0] && (hp->ident[0] || hp->oident[0]))
217: return TRUE;
218: return FALSE;
219: }
220:
221: /*
222: * There was no From: line in the message (because it was generated by
223: * an old news program). Guess what it should have been and create it.
224: */
225: intuitfrom(hp)
226: register struct hbuf *hp;
227: {
228: char *tp;
229: char *user, *host, *fullname;
230: char *tailpath(), *rindex();
231: char *at, *dot;
232:
233: tp = tailpath(hp);
234: user = rindex(tp, '!');
235: if (user == NULL)
236: user = tp;
237: else
238: *user++ = '\0';
239:
240: /* Check for an existing Internet address on the end. */
241: at = index(user, '@');
242: if (at) {
243: dot = index(at, '.');
244: if (dot) {
245: strcpy(hp->from, user);
246: return;
247: }
248: /* @ signs are illegal except for the biggie, so */
249: *at = '%';
250: }
251:
252: if (tp[0] == '.')
253: host = index(tp, '!') + 1;
254: else if (user == tp)
255: host = FULLSYSNAME;
256: else
257: host = tp;
258:
259: tp = index(host, '@');
260: if (tp != NULL)
261: *tp = 0;
262: sprintf(hp->from, "%s@%s%s", user, host, MYDOMAIN);
263:
264: fullname = index(hp->path, '(');
265: if (fullname != NULL) {
266: fullname--;
267: strcat(hp->from, fullname);
268: *fullname = 0;
269: }
270: }
271:
272: /*
273: * If the message has only one of ident/oident, guess what
274: * the other one should be and fill them both in.
275: */
276: fixid(hp)
277: register struct hbuf *hp;
278: {
279: char lbuf[100];
280: char *p, *q;
281:
282: if (hp->ident[0] == '\0' && hp->oident[0] != '\0') {
283: strcpy(lbuf, hp->oident);
284: p = index(lbuf, '.');
285: if (p == 0) {
286: strcpy(hp->ident, hp->oident);
287: return;
288: }
289: *p++ = '\0';
290: /*
291: * It may seem strange that we hardwire ".UUCP" in
292: * here instead of MYDOMAIN. However, we are trying
293: * to guess what the domain was on the posting system,
294: * not the local system. Since we don't really know
295: * what the posting system does, we just go with the
296: * majority - almost everyone will be a .UUCP if they
297: * didn't fill in their Message-ID.
298: */
299: sprintf(hp->ident, "<%s@%s%s>", p, lbuf, ".UUCP");
300: }
301:
302: #ifdef OLD
303: if (hp->oident[0] == '\0' && hp->ident[0] != '\0') {
304: strcpy(lbuf, hp->ident);
305: p = index(lbuf, '@');
306: if (p == 0) {
307: strcpy(hp->oident, hp->ident);
308: return;
309: }
310: *p++ = '\0';
311: q = index(p, '.');
312: if (!q)
313: q = index(p, '>');
314: if (q)
315: *q++ = '\0';
316: p[SNLN] = '\0';
317: sprintf(hp->oident, "%s.%s", p, lbuf+1);
318: }
319: #endif
320: }
321:
322: /*
323: * Get the given field of a header (char * parm) from bfr, but only
324: * if there's something actually there (after the colon). Don't
325: * bother if we already have an entry for this field.
326: */
327: getfield(hpfield)
328: char *hpfield;
329: {
330: char *ptr;
331:
332: if (hpfield[0])
333: return;
334: for (ptr = index(bfr, ':'); isspace(*++ptr); )
335: ;
336: if (*ptr != '\0') {
337: strcpy(hpfield, ptr);
338: nstrip(hpfield);
339: }
340: return;
341: }
342:
343:
344: #define its(type) (prefix(ptr, type))
345: type(ptr)
346: char *ptr;
347: {
348: char *colon, *space;
349:
350: if (!isalpha(*ptr) && strncmp(ptr, "From ", 5))
351: return FALSE;
352: colon = index(ptr, ':');
353: space = index(ptr, ' ');
354: if (!colon || colon + 1 != space)
355: return FALSE;
356: if (its("From: "))
357: if (index(ptr, '@') && !index(ptr, '!') && seenrelay)
358: return FROM;
359: else
360: return PATH;
361: if (its("Path: "))
362: return PATH;
363: if (its("Newsgroups: "))
364: return NEWSGROUP;
365: if (its("Subject: ") || its("Title: "))
366: return TITLE;
367: if (its("Posted: ") || its("Date: "))
368: return SUBMIT;
369: if (its("Date-Received: ") || its("Received: "))
370: return RECEIVE;
371: if (its("Expires: "))
372: return EXPIRE;
373: if (its("Article-I.D.: "))
374: return ARTICLEID;
375: if (its("Message-ID: "))
376: return MESSAGEID;
377: if (its("Reply-To: "))
378: return REPLYTO;
379: if (its("References: "))
380: return FOLLOWID;
381: if (its("Control: "))
382: return CONTROL;
383: if (its("Sender: "))
384: return SENDER;
385: if (its("Followup-To: "))
386: return FOLLOWTO;
387: if (its("Posting-Version: "))
388: return POSTVERSION;
389: if (its("Relay-Version: "))
390: return RELAYVERSION;
391: if (its("Distribution: "))
392: return DISTRIBUTION;
393: if (its("Organization: "))
394: return ORGANIZATION;
395: if (its("Lines: "))
396: return NUMLINES;
397: if (its("Keywords: "))
398: return KEYWORDS;
399: if (its("Approved: "))
400: return APPROVED;
401: return OTHER;
402: }
403:
404: /*
405: * Generate the current version of the news software.
406: */
407: char *
408: genversion()
409: {
410: static char retbuf[32];
411: char rb[100];
412: register char *t;
413:
414: strcpy(rb, news_version);
415: while (t = index(rb, '\t'))
416: *t = ' ';
417: /* This is B news, so we say "B", the version, and the date. */
418: sprintf(retbuf, "version %s; site %s%s", rb, FULLSYSNAME, MYDOMAIN);
419: return retbuf;
420: }
421:
422: /*
423: * Write header at 'hp' on stream 'fp' in B format. This goes out to
424: * some other system.
425: */
426: hwrite(hp, fp)
427: register struct hbuf *hp;
428: register FILE *fp;
429: {
430: ihwrite(hp, fp, 0);
431: }
432:
433:
434: /*
435: * Same as above, except include receival date for local usage and
436: * an extra \n for looks.
437: */
438: lhwrite(hp, fp)
439: register struct hbuf *hp;
440: register FILE *fp;
441: {
442: ihwrite(hp, fp, 1);
443: }
444:
445:
446: /*
447: * Write header at 'hp' on stream 'fp' in B+ format. Include received date
448: * if wr is 1. Leave off sysname if wr is 2.
449: */
450: ihwrite(hp, fp, wr)
451: register struct hbuf *hp;
452: register FILE *fp;
453: int wr;
454: {
455: int iu;
456: time_t t;
457: time_t cgtdate();
458:
459: fprintf(fp, "Relay-Version: %s\n", genversion());
460: if (*hp->postversion)
461: fprintf(fp, "Posting-Version: %s\n", hp->postversion);
462: /*
463: * We're being tricky with Path/From because of upward compatibility
464: * issues. The new version considers From and Path to be separate.
465: * The old one thinks they both mean "Path" but only believes the
466: * first one it sees, so will ignore the second.
467: */
468: if (prefix(hp->path, FULLSYSNAME))
469: fprintf(fp, "Path: %s\n", hp->path);
470: else
471: fprintf(fp, "Path: %s!%s\n", FULLSYSNAME, hp->path);
472: if (hp->from[0])
473: fprintf(fp, "From: %s\n", hp->from);
474:
475: ngdel(strcpy(bfr, hp->nbuf));
476: fprintf(fp, "Newsgroups: %s\n", bfr);
477: fprintf(fp, "Subject: %s\n", hp->title);
478: fixid(hp);
479: fprintf(fp, "Message-ID: %s\n", hp->ident);
480: t = cgtdate(hp->subdate);
481: fprintf(fp, "Date: %s\n", arpadate(&t));
482: #ifdef OLD
483: fprintf(fp, "Article-I.D.: %s\n", hp->oident);
484: fprintf(fp, "Posted: %s", ctime(&t));
485: #endif
486: if (wr == 1)
487: fprintf(fp, "Date-Received: %s\n", hp->recdate);
488: if (*hp->expdate)
489: fprintf(fp, "Expires: %s\n", hp->expdate);
490: if (*hp->followid)
491: fprintf(fp, "References: %s\n", hp->followid);
492: if (*hp->ctlmsg)
493: fprintf(fp, "Control: %s\n", hp->ctlmsg);
494: if (*hp->sender)
495: fprintf(fp, "Sender: %s\n", hp->sender);
496: if (*hp->replyto)
497: fprintf(fp, "Reply-To: %s\n", hp->replyto);
498: if (*hp->followto)
499: fprintf(fp, "Followup-To: %s\n", hp->followto);
500: if (*hp->distribution)
501: fprintf(fp, "Distribution: %s\n", hp->distribution);
502: if (*hp->organization)
503: fprintf(fp, "Organization: %s\n", hp->organization);
504: if (*hp->numlines)
505: fprintf(fp, "Lines: %s\n", hp->numlines);
506: if (*hp->keywords)
507: fprintf(fp, "Keywords: %s\n", hp->keywords);
508: if (*hp->approved)
509: fprintf(fp, "Approved: %s\n", hp->approved);
510: for (iu = 0; iu < NUNREC; iu++) {
511: if (hp->unrec[iu])
512: fprintf(fp, "%s", &hp->unrec[iu][0]);
513: }
514: putc('\n', fp);
515: }
516:
517:
518: /*
519: * Set nc bytes, starting at cp, to zero.
520: */
521: bclear(cp, nc)
522: register char *cp;
523: register int nc;
524: {
525: while (nc--)
526: *cp++ = 0;
527: }
528:
529: /*
530: * hfgets is like fgets, but deals with continuation lines.
531: * It also ensures that even if a line that is too long is
532: * received, the remainder of the line is thrown away
533: * instead of treated like a second line.
534: */
535: char *
536: hfgets(buf, len, fp)
537: char *buf;
538: int len;
539: FILE *fp;
540: {
541: register int c;
542: register char *cp, *tp;
543:
544: cp = fgets(buf, len, fp);
545: if (cp == NULL)
546: return NULL;
547:
548: tp = cp + strlen(cp);
549: if (tp[-1] != '\n') {
550: /* Line too long - part read didn't fit into a newline */
551: while ((c = getc(fp)) != '\n' && c != EOF)
552: ;
553: } else
554: *--tp = '\0'; /* clobber newline */
555:
556: while ((c = getc(fp)) == ' ' || c == '\t') { /* for each cont line */
557: /* Continuation line. */
558: while ((c = getc(fp)) == ' ' || c == 't') /* skip white space */
559: ;
560: if (tp-cp < len) {*tp++ = ' '; *tp++ = c;}
561: while ((c = getc(fp)) != '\n' && c != EOF)
562: if (tp-cp < len) *tp++ = c;
563: }
564: *tp++ = '\n';
565: *tp++ = '\0';
566: if (c != EOF)
567: ungetc(c, fp); /* push back first char of next header */
568: return cp;
569: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.