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