|
|
1.1 root 1: /*
2: * This software is Copyright (c) 1986 by Rick Adams.
3: *
4: * Permission is hereby granted to copy, reproduce, redistribute or
5: * otherwise use this software as long as: there is no monetary
6: * profit gained specifically from the use or reproduction or this
7: * software, it is not sold, rented, traded or otherwise marketed, and
8: * this copyright notice is included prominently in any copy
9: * made.
10: *
11: * The author make no claims as to the fitness or correctness of
12: * this software for any use whatsoever, and it is provided as is.
13: * Any use of this software is at the user's own risk.
14: *
15: * header.c - header functions plus some other goodies
16: */
17: /*LINTLIBRARY*/
18:
19: #ifdef SCCSID
20: static char *SccsId = "@(#)header.c 2.49 10/7/87";
21: #endif /* SCCSID */
22:
23: #include <stdio.h>
24: #include "params.h"
25: #include "patchlevel.h"
26:
27: char *hfgets();
28:
29: char *news_version = NEWS_VERSION;
30:
31: /*
32: * Read header from file fp into *hp. If wholething is FALSE,
33: * it's an incremental read, otherwise start from scratch.
34: * Return (FILE *) if header okay, else NULL.
35: */
36: FILE *
37: hread(hp, fp, wholething)
38: register struct hbuf *hp;
39: FILE *fp;
40: int wholething;
41: {
42: #ifndef GENERICPATH
43: register int len;
44: #endif /* GENERICPATH */
45: register int i;
46: #ifdef OLD
47: char *p;
48: #endif /* OLD */
49:
50: if (wholething) {
51: for(i=0;i<NUNREC;i++)
52: if (hp->unrec[i] != NULL)
53: free(hp->unrec[i]);
54: else
55: break;
56: bzero((char *)hp, sizeof (*hp));
57: for (i=0;i<NUNREC;i++)
58: hp->unrec[i] = NULL;
59: }
60:
61: /* Check that it's a B news style header. */
62: if (hfgets(bfr, PATHLEN, fp) != NULL && isalpha(bfr[0])
63: && index(bfr, ':'))
64: if (frmread(fp, hp))
65: goto strip;
66:
67: if (!nstrip(bfr+1))
68: return NULL;
69:
70: /* It's not. Try A news (begins with PROTO). */
71: if (bfr[0] != PROTO)
72: return NULL;
73: #ifndef OLD
74: logerr("Can not process A news format article without OLD defined");
75: #else /* OLD */
76: /* Read in an A news format article. */
77: p = index(bfr+1, '.');
78: if (p == NULL) {
79: (void) strcpy(hp->ident, bfr+1);
80: return NULL;
81: }
82: *p++ = '\0';
83: (void) sprintf(hp->ident, "<%s@%s%s>", p, bfr+1, ".UUCP");
84:
85: /* Newsgroup List */
86: if (hfgets(hp->nbuf, BUFLEN, fp) == NULL || !nstrip(hp->nbuf))
87: return NULL;
88: /* source path */
89: if (hfgets(hp->path, PATHLEN, fp) == NULL || !nstrip(hp->path))
90: return NULL;
91: /* date */
92: if (hfgets(hp->subdate, DATELEN, fp) == NULL || !nstrip(hp->subdate))
93: return NULL;
94: /* title */
95: if (hfgets(hp->title, BUFLEN, fp) == NULL || !nstrip(hp->title))
96: return NULL;
97: #endif /* OLD */
98:
99: strip: /* strip off sys! from front of path. */
100: #ifndef GENERICPATH
101: if (strncmp(PATHSYSNAME, hp->path, (len = strlen(PATHSYSNAME))) == 0
102: && index(NETCHRS, hp->path[len]))
103: (void) strcpy(hp->path, &(hp->path[len+1]));
104: #endif /* GENERICPATH */
105: lcase(hp->nbuf);
106:
107: /* Intuit the From: line if only a path was given. */
108: if (wholething) {
109: #ifdef OLD
110: if (hp->from[0] == '\0')
111: intuitfrom(hp);
112: else
113: #endif /* OLD */
114: fixfrom(hp);
115: }
116:
117: return fp;
118: }
119:
120:
121: /*
122: * Get header info from mail-format file.
123: * Return non-zero on success.
124: */
125: #define FROM 1
126: #define NEWSGROUP 2
127: #define TITLE 3
128: #define SUBMIT 4
129: #define RECEIVE 5
130: #define EXPIRE 6
131: #define ARTICLEID 7
132: #define MESSAGEID 8
133: #define REPLYTO 9
134: #define FOLLOWID 10
135: #define CONTROL 11
136: #define SENDER 12
137: #define FOLLOWTO 13
138: #define PATH 14
139: #define POSTVERSION 15
140: #define RELAYVERSION 16
141: #define DISTRIBUTION 17
142: #define ORGANIZATION 18
143: #define NUMLINES 19
144: #define KEYWORDS 20
145: #define APPROVED 21
146: #define NFID 22
147: #define NFFROM 23
148: #define XREF 24
149: #define SUMMARY 25
150: #define XPATH 26
151: #define SUPERSEDES 27
152: #define OTHER 99
153:
154: char *malloc();
155:
156: frmread(fp, hp)
157: register FILE *fp;
158: register struct hbuf *hp;
159: {
160: int unreccnt = 0;
161: register int i;
162:
163: i = type(bfr);
164: do {
165: switch (i) {
166: case PATH:
167: getfield(hp->path, sizeof(hp->path));
168: break;
169: case FROM:
170: getfield(hp->from, sizeof(hp->from));
171: break;
172: case NEWSGROUP:
173: getfield(hp->nbuf, sizeof(hp->nbuf));
174: break;
175: case TITLE:
176: getfield(hp->title, sizeof(hp->title));
177: break;
178: case SUBMIT:
179: getfield(hp->subdate, sizeof(hp->subdate));
180: break;
181: case EXPIRE:
182: getfield(hp->expdate, sizeof(hp->expdate));
183: break;
184: #ifdef OLD
185: case ARTICLEID:
186: /* Believe Message-ID in preference to Article-ID */
187: if (hp->ident[0] == '\0') {
188: register char *p;
189: char msgb[NAMELEN];
190: getfield(msgb, sizeof msgb);
191: p = index(msgb, '.');
192: if (p == NULL) {
193: (void) strcpy(hp->ident, msgb);
194: } else {
195: *p++ = '\0';
196: (void) sprintf(hp->ident, "<%s@%s%s>", p, msgb, ".UUCP");
197: }
198: }
199: break;
200: #endif /* OLD */
201: case MESSAGEID:
202: getfield(hp->ident, sizeof(hp->ident));
203: break;
204: case REPLYTO:
205: getfield(hp->replyto, sizeof(hp->replyto));
206: break;
207: case FOLLOWID:
208: getfield(hp->followid, sizeof(hp->followid));
209: break;
210: case SENDER:
211: getfield(hp->sender, sizeof(hp->sender));
212: break;
213: case FOLLOWTO:
214: getfield(hp->followto, sizeof(hp->followto));
215: break;
216: case CONTROL:
217: getfield(hp->ctlmsg, sizeof(hp->ctlmsg));
218: break;
219: case DISTRIBUTION:
220: getfield(hp->distribution, sizeof(hp->distribution));
221: if (strcmp(hp->distribution, "net") == 0
222: || strcmp(hp->distribution, "world") == 0)
223: hp->distribution[0] = '\0';
224: break;
225: case ORGANIZATION:
226: getfield(hp->organization, sizeof(hp->organization));
227: break;
228: case NUMLINES:
229: getfield(hp->numlines, sizeof(hp->numlines));
230: hp->intnumlines = atoi(hp->numlines);
231: break;
232: case KEYWORDS:
233: getfield(hp->keywords, sizeof(hp->keywords));
234: break;
235: case APPROVED:
236: getfield(hp->approved, sizeof(hp->approved));
237: break;
238: case NFID:
239: getfield(hp->nf_id, sizeof(hp->nf_id));
240: break;
241: case NFFROM:
242: getfield(hp->nf_from, sizeof(hp->nf_from));
243: break;
244: case SUPERSEDES:
245: getfield(hp->supersedes, sizeof(hp->supersedes));
246: break;
247: /* discard these lines */
248: case XREF:
249: case XPATH:
250: case RELAYVERSION:
251: case POSTVERSION:
252: case RECEIVE:
253: break;
254: case SUMMARY:
255: getfield(hp->summary, sizeof(hp->summary));
256: break;
257: case OTHER:
258: if (unreccnt < NUNREC) {
259: if ((hp->unrec[unreccnt] = malloc((unsigned)(strlen(bfr) + 1))) != NULL ) {
260: (void) strcpy(hp->unrec[unreccnt], bfr);
261: (void) nstrip(hp->unrec[unreccnt]);
262: unreccnt++;
263: } else
264: xerror("frmread: out of memory");
265: }
266: break;
267: }
268: } while ((i = type(hfgets(bfr, LBUFLEN, fp))) > 0);
269:
270: if ((hp->from[0] || hp->path[0]) && hp->subdate[0] && hp->ident[0])
271: return TRUE;
272: return FALSE;
273: }
274:
275: #ifdef OLD
276: /*
277: * There was no From: line in the message (because it was generated by
278: * an old news program). Guess what it should have been and create it.
279: */
280: intuitfrom(hp)
281: register struct hbuf *hp;
282: {
283: char *tp;
284: char *user, *host;
285: char *tailpath(), *rindex();
286: char *at, *dot;
287: char pathbuf[PATHLEN];
288: char fullname[BUFLEN];
289: extern char *mydomain();
290:
291: tp = tailpath(hp);
292: user = rindex(tp, '!');
293: if (user == NULL)
294: user = tp;
295: else
296: *user++ = '\0';
297:
298: /* Check for an existing Internet address on the end. */
299: at = index(user, '@');
300: if (at) {
301: dot = index(at, '.');
302: if (dot) {
303: (void) strcpy(hp->from, user);
304: return;
305: }
306: /* @ signs are illegal except for the biggie, so */
307: *at = '%';
308: }
309:
310: if (tp[0] == '.')
311: host = index(tp, '!') + 1;
312: else if (user == tp)
313: host = FROMSYSNAME;
314: else
315: host = tp;
316:
317: tp = index(host, '@');
318: if (tp != NULL)
319: *tp = 0;
320: if (index(host, '.') != NULL)
321: (void) sprintf(hp->from, "%s@%s%s", user, host, mydomain());
322: else
323: (void) sprintf(hp->from, "%s@%s", user, host);
324:
325: skin(pathbuf, fullname, hp->path); /* remove RFC822-style comments */
326: if (fullname[0] != '\0') {
327: strcat(hp->from, " (");
328: (void) strcat(hp->from, fullname);
329: strcat(hp->from, ")");
330: }
331: strcpy(hp->path, pathbuf); /* and stick it back in */
332: }
333: #endif /* OLD */
334:
335: /*
336: * Canonicalize the "From:" line into the form
337: *
338: * From: <mail-address> (full-name)
339: *
340: * RFC822 doesn't require the comment to be at the end of the string
341: * like that.
342: */
343: fixfrom(hp)
344: register struct hbuf *hp;
345: {
346: char frombuf[PATHLEN];
347: char fullname[BUFLEN];
348:
349: skin(frombuf, fullname, hp->from); /* remove RFC822-style comments */
350: if (fullname[0] != '\0') {
351: strcat(frombuf, " (");
352: strcat(frombuf, fullname);
353: strcat(frombuf, ")");
354: }
355: strcpy(hp->from, frombuf); /* stick the canonicalized "from" back in */
356: }
357:
358: skin(name, fullname, hfield)
359: char *name;
360: char *fullname;
361: char *hfield;
362: {
363: register int c;
364: register char *cp, *cp2;
365: char *bufend;
366: int gotlt, parenlev, lastsp;
367: int seenfullname = FALSE;
368:
369: *fullname = '\0'; /* no full name yet */
370: if (strpbrk(hfield, "(< ") == NULL) { /* include ',' ?? */
371: strcpy(name, hfield);
372: return;
373: }
374: gotlt = 0;
375: parenlev = 0;
376: lastsp = 0;
377: bufend = name;
378: for (cp = hfield, cp2 = bufend; c = *cp++; ) {
379: switch (c) {
380: case '(':
381: /*
382: * Start of a "comment".
383: * Ignore it, or save it in "fullname" if we haven't
384: * seen a comment yet.
385: */
386: parenlev++;
387: while ((c = *cp) != 0) {
388: cp++;
389: switch (c) {
390: case '\\':
391: if ((c = *cp) == 0)
392: goto outcm;
393: cp++;
394: break;
395: case '(':
396: parenlev++;
397: break;
398: case ')':
399: parenlev--;
400: if (parenlev == 0)
401: goto outcm;
402: break;
403: }
404: if (!seenfullname)
405: *fullname++ = c;
406: }
407: outcm:
408: parenlev = 0;
409: lastsp = 0;
410: if (!seenfullname) {
411: *fullname = '\0';
412: seenfullname = TRUE; /* only extract first comment */
413: }
414: break;
415:
416: case '"':
417: /*
418: * Start of a "quoted-string".
419: * Copy it in its entirety.
420: */
421: while ((c = *cp) != 0) {
422: cp++;
423: switch (c) {
424: case '\\':
425: if ((c = *cp) == 0)
426: goto outqs;
427: cp++;
428: break;
429: case '"':
430: goto outqs;
431: }
432: *cp2++ = c;
433: }
434: outqs:
435: lastsp = 0;
436: break;
437:
438: case ' ':
439: if (cp[0] == 'a' && cp[1] == 't' && cp[2] == ' ')
440: cp += 3, *cp2++ = '@';
441: else
442: if (cp[0] == '@' && cp[1] == ' ')
443: cp += 2, *cp2++ = '@';
444: else
445: lastsp = 1;
446: break;
447:
448: case '<':
449: if (!seenfullname) {
450: *cp2 = '\0';
451: strcpy(fullname, name);
452: seenfullname = TRUE;
453: }
454: cp2 = bufend;
455: gotlt++;
456: lastsp = 0;
457: break;
458:
459: case '>':
460: if (gotlt) {
461: gotlt = 0;
462: /*
463: * this doesn't seem reasonable, what about (,)
464: * or "," ??
465: */
466: while (*cp != ',' && *cp != 0)
467: cp++;
468: if (*cp == 0 )
469: goto done;
470: *cp2++ = ',';
471: *cp2++ = ' ';
472: bufend = cp2;
473: break;
474: }
475:
476: /* Fall into . . . */
477:
478: default:
479: if (lastsp) {
480: lastsp = 0;
481: *cp2++ = ' ';
482: }
483: *cp2++ = c;
484: break;
485: }
486: }
487: done:
488: *cp2 = 0;
489: }
490:
491:
492: #ifdef OLD
493: char *
494: oident(ident)
495: char *ident;
496: {
497: char lbuf[BUFLEN];
498: static char oidbuf[BUFLEN];
499: register char *p, *q;
500:
501: (void) strcpy(lbuf, ident);
502: p = index(lbuf, '@');
503: if (p == NULL)
504: return ident;
505: *p++ = '\0';
506: q = index(p, '.');
507: if (q == NULL)
508: q = index(p, '>');
509: if (q)
510: *q++ = '\0';
511: p[SNLN] = '\0';
512: (void) sprintf(oidbuf, "%s.%s", p, lbuf+1);
513: return oidbuf;
514: }
515: #endif /* OLD */
516:
517: /*
518: * Get the given field of a header (char * parm) from bfr, but only
519: * if there's something actually there (after the colon). Don't
520: * bother if we already have an entry for this field.
521: */
522: getfield(hpfield, size)
523: char *hpfield;
524: int size;
525: {
526: register char *ptr;
527:
528: if (hpfield[0])
529: return;
530: for (ptr = index(bfr, ':'); isspace(*++ptr); )
531: ;
532: if (*ptr != '\0') {
533: (void) strncpy(hpfield, ptr, size - 1);
534: (void) nstrip(hpfield);
535: }
536: }
537:
538:
539: #define its(type) (PREFIX(ptr, type))
540: type(ptr)
541: register char *ptr;
542: {
543: register char *colon, *space;
544:
545: if (ptr == NULL)
546: return FALSE;
547: if (its("From: "))
548: if (index(ptr, '@') || !index(ptr, '!'))
549: return FROM;
550: else
551: return PATH;
552: if (its("Path: "))
553: return PATH;
554: if (its("Newsgroups: "))
555: return NEWSGROUP;
556: if (its("Subject: "))
557: return TITLE;
558: if (its("Date: "))
559: return SUBMIT;
560: if (its("Date-Received: "))
561: return RECEIVE;
562: #ifdef OLD
563: if (its("Title: "))
564: return TITLE;
565: if (its("Posted: "))
566: return SUBMIT;
567: #endif /* OLD */
568: if (its("Received: "))
569: return RECEIVE;
570: if (its("Expires: "))
571: return EXPIRE;
572: if (its("Article-I.D.: "))
573: return ARTICLEID;
574: if (its("Message-ID: "))
575: return MESSAGEID;
576: if (its("Reply-To: "))
577: return REPLYTO;
578: if (its("References: "))
579: return FOLLOWID;
580: if (its("Control: "))
581: return CONTROL;
582: if (its("Sender: "))
583: return SENDER;
584: if (its("Followup-To: "))
585: return FOLLOWTO;
586: if (its("Distribution: "))
587: return DISTRIBUTION;
588: if (its("Organization: "))
589: return ORGANIZATION;
590: if (its("Lines: "))
591: return NUMLINES;
592: if (its("Summary: "))
593: return SUMMARY;
594: if (its("Keywords: "))
595: return KEYWORDS;
596: if (its("Approved: "))
597: return APPROVED;
598: if (its("Nf-ID: "))
599: return NFID;
600: if (its("Nf-From: "))
601: return NFFROM;
602: if (its("Supersedes: "))
603: return SUPERSEDES;
604: if (its("Xref: "))
605: return XREF;
606: if (its("Xpath: "))
607: return XPATH;
608: if (its("Posting-Version: "))
609: return POSTVERSION;
610: if (its("Relay-Version: "))
611: return RELAYVERSION;
612: if (!isalpha(*ptr))
613: return FALSE;
614: colon = index(ptr, ':');
615: space = index(ptr, ' ');
616: if (!colon || space && space < colon)
617: return FALSE;
618: return OTHER;
619: }
620:
621: /*
622: * Write header at 'hp' on stream 'fp' in B+ format. Include received date
623: * if wr is 1. Leave off sysname if wr is 2.
624: */
625: #ifndef DOXREFS
626: /*ARGSUSED*/
627: #endif /* !DOXREFS */
628: ihwrite(hp, fp, wr)
629: register struct hbuf *hp;
630: register FILE *fp;
631: int wr;
632: {
633: int iu;
634: time_t t;
635: time_t cgtdate();
636:
637: /*
638: * We're being tricky with Path/From because of upward compatibility
639: * issues. The new version considers From and Path to be separate.
640: * The old one thinks they both mean "Path" but only believes the
641: * first one it sees, so will ignore the second.
642: */
643: if (PREFIX(hp->path, PATHSYSNAME) &&
644: index(NETCHRS, hp->path[strlen(PATHSYSNAME)]))
645: fprintf(fp, "Path: %s\n", hp->path);
646: else
647: fprintf(fp, "Path: %s!%s\n", PATHSYSNAME, hp->path);
648: if (hp->from[0])
649: fprintf(fp, "From: %s\n", hp->from);
650:
651: fprintf(fp, "Newsgroups: %s\n", hp->nbuf);
652: fprintf(fp, "Subject: %s\n", hp->title);
653: if (*hp->summary)
654: fprintf(fp, "Summary: %s\n", hp->summary);
655: if (*hp->keywords)
656: fprintf(fp, "Keywords: %s\n", hp->keywords);
657: fprintf(fp, "Message-ID: %s\n", hp->ident);
658: t = cgtdate(hp->subdate);
659: fprintf(fp, "Date: %s\n", arpadate(&t));
660: #ifdef OLD
661: fprintf(fp, "Article-I.D.: %s\n", oident(hp->ident));
662: fprintf(fp, "Posted: %s", ctime(&t));
663: #endif /* OLD */
664: if (*hp->expdate)
665: fprintf(fp, "Expires: %s\n", hp->expdate);
666: if (*hp->followid) {
667: register char *dp, *cp;
668:
669: dp = cp = hp->followid;
670: while (*cp != '\0')
671: if (*cp == '<' && *(cp + 1) == '>')
672: cp += 2;
673: else
674: *dp++ = *cp++;
675: *dp = '\0';
676: if (*hp->followid)
677: fprintf(fp, "References: %s\n", hp->followid);
678: }
679: if (*hp->ctlmsg)
680: fprintf(fp, "Control: %s\n", hp->ctlmsg);
681: if (*hp->sender)
682: fprintf(fp, "Sender: %s\n", hp->sender);
683: if (*hp->replyto)
684: fprintf(fp, "Reply-To: %s\n", hp->replyto);
685: if (*hp->followto)
686: fprintf(fp, "Followup-To: %s\n", hp->followto);
687: if (*hp->distribution)
688: fprintf(fp, "Distribution: %s\n", hp->distribution);
689: if (*hp->organization)
690: fprintf(fp, "Organization: %s\n", hp->organization);
691: if (*hp->numlines)
692: fprintf(fp, "Lines: %s\n", hp->numlines);
693: if (*hp->approved)
694: fprintf(fp, "Approved: %s\n", hp->approved);
695: if (*hp->nf_id)
696: fprintf(fp, "Nf-ID: %s\n", hp->nf_id);
697: if (*hp->nf_from)
698: fprintf(fp, "Nf-From: %s\n", hp->nf_from);
699: if (*hp->supersedes)
700: fprintf(fp, "Supersedes: %s\n", hp->supersedes);
701: #ifdef DOXREFS
702: if ( wr ==1 && *hp->xref)
703: fprintf(fp, "Xref: %s\n", hp->xref);
704: #endif /* DOXREFS */
705: for (iu = 0; iu < NUNREC; iu++) {
706: if (hp->unrec[iu])
707: fprintf(fp, "%s\n", &hp->unrec[iu][0]);
708: }
709: putc('\n', fp);
710: }
711:
712:
713: #ifndef BSD4_2
714: /*
715: * Set nc bytes, starting at cp, to zero.
716: */
717: bzero(cp, nc)
718: register char *cp;
719: register int nc;
720: {
721: if (nc > 0)
722: do {
723: *cp++ = 0;
724: } while (--nc);
725: }
726: #endif /* !BSD4_2 */
727:
728: /*
729: * hfgets is like fgets, but deals with continuation lines.
730: * It also ensures that even if a line that is too long is
731: * received, the remainder of the line is thrown away
732: * instead of treated like a second line.
733: */
734: char *
735: hfgets(buf, len, fp)
736: char *buf;
737: int len;
738: FILE *fp;
739: {
740: register int c;
741: register int n = 0;
742: register char *cp;
743:
744: cp = buf;
745: while (n < len && (c = getc(fp)) != EOF) {
746: if (c == '\n')
747: break;
748: if (isprint(c) || c == '\b' || c == ' ' || c == '\t') {
749: *cp++ = c;
750: n++;
751: }
752: }
753: if (c == EOF && cp == buf)
754: return NULL;
755: *cp = '\0';
756:
757: if (c != '\n') {
758: /* Line too long - part read didn't fit into a newline */
759: while ((c = getc(fp)) != '\n' && c != EOF)
760: ;
761: } else if (cp == buf) {
762: /* Don't look for continuation of blank lines */
763: *cp++ = '\n';
764: *cp = '\0';
765: return buf;
766: }
767:
768: while ((c = getc(fp)) == ' ' || c == '\t') { /* for each cont line */
769: /* Continuation line. */
770: if ((n += 2) < len) {
771: *cp++ = '\n';
772: *cp++ = c;
773: }
774: while ((c = getc(fp)) != '\n' && c != EOF)
775: if ((isprint(c) || c == '\b' || c == ' ' || c == '\t')
776: && n++ < len)
777: *cp++ = c;
778: }
779: if (n >= len - 1)
780: cp = buf + len - 2;
781: *cp++ = '\n';
782: *cp = '\0';
783: if (c != EOF)
784: (void) ungetc(c, fp); /* push back first char of next header */
785: return buf;
786: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.