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