|
|
1.1 root 1: /*
2: ** Get header info from mail-format file.
3: ** Return non-zero on success.
4: */
5:
6: #include "defs.h"
7: #include <ctype.h>
8: #include <sys/types.h>
9: #include <stdio.h>
10: #include "header.h"
11: #include "llist.h"
12:
13: #ifndef isblank
14: #define isblank(c) ((c) == ' ' || (c) == '\t')
15: #endif
16:
17: char *hfgets();
18: char *arpadate();
19: char *errmsg();
20: char *strpbrk();
21: time_t cgtdate();
22: extern char *index();
23: extern unsigned strlen();
24: extern time_t time();
25:
26: #define FROM 1
27: #define NEWSGROUP 2
28: #define TITLE 3
29: #define SUBMIT 4
30: #define RECEIVE 5
31: #define EXPIRE 6
32: #define ARTICLEID 7
33: #define MESSAGEID 8
34: #define REPLYTO 9
35: #define FOLLOWID 10
36: #define CONTROL 11
37: #define SENDER 12
38: #define FOLLOWTO 13
39: #define PATH 14
40: #define POSTVERSION 15
41: #define RELAYVERSION 16
42: #define DISTRIBUTION 17
43: #define ORGANIZATION 18
44: #define NUMLINES 19
45: #define KEYWORDS 20
46: #define APPROVED 21
47: #define NFID 22
48: #define NFFROM 23
49: #define XREF 24
50: #define SUMMARY 25
51: #define FULLNAME 26
52: #define OTHER 99
53:
54: /*
55: ** This is the list of headers we recognize.
56: ** All others get stripped before they get to inews.
57: */
58: struct htype {
59: char *hname;
60: int hid;
61: } htype[] = {
62: {"Approved:", APPROVED},
63: {"Article-I.D.:", ARTICLEID},
64: {"Control:", CONTROL},
65: {"Date-Received:", RECEIVE},
66: {"Date:", SUBMIT},
67: {"Distribution:", DISTRIBUTION},
68: {"Expires:", EXPIRE},
69: {"Followup-To:", FOLLOWTO},
70: {"From:", FROM},
71: /* {"Full-Name:", FULLNAME}, */
72: {"In-Reply-To:", FOLLOWID},
73: {"Keywords:", KEYWORDS},
74: {"Lines:", NUMLINES},
75: {"Message-ID:", MESSAGEID},
76: {"Newsgroups:", NEWSGROUP},
77: {"Nf-From:", NFFROM},
78: {"Nf-ID:", NFID},
79: {"Organization:", ORGANIZATION},
80: {"Path:", PATH},
81: {"Posted:", SUBMIT},
82: {"Posting-Version:", POSTVERSION},
83: /* {"Received:", RECEIVE}, a bad name w.r.t. RFC822 */
84: {"References:", FOLLOWID},
85: {"Relay-Version:", RELAYVERSION},
86: {"Reply-To:", REPLYTO},
87: {"Sender:", SENDER},
88: {"Subject:", TITLE},
89: {"Summary:", SUMMARY},
90: {"Title:", TITLE},
91: {"Xref:", XREF},
92: {(char *)NULL, NULL}
93: };
94:
95: char *malloc();
96:
97: rfc822read(hp, fp, bfr)
98: register struct hbuf *hp;
99: register FILE *fp;
100: char *bfr;
101: {
102: register int i = type(bfr);
103: long curpos;
104:
105: do {
106: curpos = ftell(fp);
107: switch (i) {
108: case FROM:
109: getfield(bfr, hp->from, sizeof(hp->from));
110: break;
111: case NEWSGROUP:
112: getfield(bfr, hp->nbuf, sizeof(hp->nbuf));
113: break;
114: case TITLE:
115: getfield(bfr, hp->title, sizeof(hp->title));
116: break;
117: case SUBMIT:
118: getfield(bfr, hp->subdate, sizeof(hp->subdate));
119: break;
120: case EXPIRE:
121: getfield(bfr, hp->expdate, sizeof(hp->expdate));
122: break;
123: case MESSAGEID:
124: getfield(bfr, hp->ident, sizeof(hp->ident));
125: break;
126: case REPLYTO:
127: getfield(bfr, hp->replyto, sizeof(hp->replyto));
128: break;
129: case FOLLOWID:
130: getfield(bfr, hp->followid, sizeof(hp->followid));
131: break;
132: case SENDER:
133: getfield(bfr, hp->sender, sizeof(hp->sender));
134: break;
135: case FOLLOWTO:
136: getfield(bfr, hp->followto, sizeof(hp->followto));
137: break;
138: case CONTROL:
139: getfield(bfr, hp->ctlmsg, sizeof(hp->ctlmsg));
140: break;
141: case DISTRIBUTION:
142: getfield(bfr, hp->distribution, sizeof(hp->distribution));
143: break;
144: case ORGANIZATION:
145: getfield(bfr, hp->organization, sizeof(hp->organization));
146: break;
147: case KEYWORDS:
148: getfield(bfr, hp->keywords, sizeof(hp->keywords));
149: break;
150: case APPROVED:
151: getfield(bfr, hp->approved, sizeof(hp->approved));
152: break;
153: case SUMMARY:
154: getfield(bfr, hp->summary, sizeof(hp->summary));
155: break;
156: }
157: } while ((i = type(hfgets(bfr, LBUFLEN, fp))) > 0);
158:
159: if (*bfr != '\n')
160: fseek(fp, curpos, 0);
161: return(TRUE);
162: }
163:
164: #define its(type) (prefix(ptr, type))
165:
166: type(ptr)
167: register char *ptr;
168: {
169: register struct htype *hp;
170: register char *colon, *space;
171: static int lasthdr = FALSE; /* for continuation headers */
172:
173: /*
174: ** some consistency checks (i.e. is this really a header line?)
175: */
176: if ((ptr == NULL) || !isascii(*ptr) || (*ptr == '\n'))
177: return(lasthdr = FALSE);
178:
179: if (isblank(*ptr)) /* continuation line? */
180: return(lasthdr);
181:
182: colon = index(ptr, ':');
183: space = index(ptr, ' ');
184: if (!colon || space && space < colon)
185: return(lasthdr = FALSE);
186:
187: for(hp = htype; hp->hname; hp++) {
188: if (its(hp->hname))
189: return(lasthdr = hp->hid);
190: }
191: return(lasthdr = OTHER);
192: }
193:
194: /*
195: ** Get the contents of the field of the header line, appending it,
196: ** with a space delimeter if it's a continuation line.
197: ** If there is already something in the header storage, skip this
198: ** header line and the continuations.
199: */
200: getfield(src, dest, size)
201: register char *src, *dest;
202: int size; /* of dest (total bytes) */
203: {
204: static int skip = FALSE; /* skip the continuation lines */
205:
206: if (src == (char *)NULL || dest == (char *)NULL)
207: return(FALSE);
208:
209: if (isblank(*src)) { /* continuation line? */
210: if (skip)
211: return(TRUE);
212: if ((size -= strlen(dest)) <= 0) /* any space left? */
213: return(FALSE);
214: while(*src && isblank(*src)) /* eat whitespace */
215: src++;
216: *--src = ' '; /* backup & add one */
217: (void) strncat(dest, src, size - 1); /* append to hdr */
218: } else {
219: skip = FALSE;
220: if (*dest) /* already got one? */
221: return(skip = TRUE); /* skip continuation */
222: if ((src = index(src, ':')) == (char *)NULL) /* impossible */
223: return(FALSE);
224: src++; /* skip colon */
225: while(*src && isblank(*src)) /* eat whitespace */
226: src++;
227: (void) strncpy(dest, src, size - 1);
228: }
229: (void) nstrip(dest);
230: return(TRUE);
231: }
232:
233: /*
234: ** Write out an RFC822 header, paying no attention to line limits.
235: ** Ideally, we should do continuations in here...
236: */
237: rfc822write(hp, fp)
238: register struct hbuf *hp;
239: register FILE *fp;
240: {
241: time_t t;
242:
243: if (hp->path[0])
244: fprintf(fp, "Path: %s\n", hp->path);
245: if (hp->from[0])
246: fprintf(fp, "From: %s\n", hp->from);
247: if (hp->nbuf[0])
248: fprintf(fp, "Newsgroups: %s\n", hp->nbuf);
249: if (hp->title[0])
250: fprintf(fp, "Subject: %s\n", hp->title);
251: if (hp->ident[0])
252: fprintf(fp, "Message-ID: %s\n", hp->ident);
253: if (hp->subdate[0])
254: t = cgtdate(hp->subdate);
255: else
256: time(&t);
257: fprintf(fp, "Date: %s\n", arpadate(&t));
258: if (*hp->expdate)
259: fprintf(fp, "Expires: %s\n", hp->expdate);
260: if (*hp->followid)
261: fprintf(fp, "References: %s\n", hp->followid);
262: if (*hp->ctlmsg)
263: fprintf(fp, "Control: %s\n", hp->ctlmsg);
264: if (*hp->sender)
265: fprintf(fp, "Sender: %s\n", hp->sender);
266: if (*hp->replyto)
267: fprintf(fp, "Reply-To: %s\n", hp->replyto);
268: if (*hp->followto)
269: fprintf(fp, "Followup-To: %s\n", hp->followto);
270: if (*hp->distribution)
271: fprintf(fp, "Distribution: %s\n", hp->distribution);
272: if (*hp->organization)
273: fprintf(fp, "Organization: %s\n", hp->organization);
274: if (*hp->keywords)
275: fprintf(fp, "Keywords: %s\n", hp->keywords);
276: if (*hp->summary)
277: fprintf(fp, "Summary: %s\n", hp->summary);
278: if (*hp->approved)
279: fprintf(fp, "Approved: %s\n", hp->approved);
280: putc('\n', fp);
281: return(!ferror(fp));
282: }
283:
284: /*
285: ** strip leading and trailing spaces
286: */
287: char *
288: sp_strip(s)
289: register char *s;
290: {
291: register char *cp;
292:
293: if (s == NULL)
294: return(NULL);
295:
296: if (*s == '\0')
297: return(s);
298:
299: cp = &s[strlen(s) - 1];
300: while(cp > s && isspace(*cp))
301: cp--;
302:
303: *++cp = '\0'; /* zap trailing spaces */
304:
305: for(cp = s; *cp && isspace(*cp); cp++)
306: continue;
307:
308: return(cp); /* return pointer to first non-space */
309: }
310:
311: /*
312: ** crack an RFC822 from header field into address and fullname.
313: */
314: crackfrom(addr,name,field)
315: char *addr, *name, *field;
316: {
317: char commbuf[LBUFLEN];
318: char addrbuf[LBUFLEN];
319: register char *p;
320: register char *ap = addrbuf;
321: register char *np;
322: short commfound = 0, comment = 0;
323: short addrfound = 0, address = 0;
324: struct llist comm, *cp = &comm;
325:
326: *name = '\0'; /* just make sure */
327: *addr = '\0';
328:
329: if ((p = field) == NULL)
330: return(FALSE);
331:
332: while(*p && isspace(*p)) /* eat leading white space */
333: p++;
334:
335: while(*p) {
336: switch(*p) {
337: case '(':
338: if (comment == 0) {
339: np = commbuf;
340: *np = '\0';
341: }
342: comment++;
343: break;
344: case ')':
345: if (comment > 0 && --comment == 0) {
346: *np++ = *p++; /* note incr; skip `)' */
347: *np++ = '\0';
348: if ((cp = l_alloc(cp, commbuf, strlen(commbuf) + 1)) == NULL) {
349: if (commfound)
350: l_free(comm);
351: return(FALSE);
352: }
353: commfound++;
354: np = NULL;
355: continue;
356: }
357: break;
358: case '<':
359: if (address) {
360: if (commfound)
361: l_free(comm);
362: return(FALSE); /* AWK! Abort! */
363: }
364: if (!comment) {
365: address++;
366: *ap = '\0';
367: ap = addr;
368: }
369: break;
370: case '>':
371: if (!comment && address) {
372: address--;
373: addrfound++;
374: *ap = '\0';
375: ap = &addrbuf[strlen(addrbuf)];
376: p++; /* skip the `>' */
377: }
378: break;
379: }
380:
381: if (comment) {
382: *np++ = *p;
383: } else if (address) {
384: if (*p != '<')
385: *ap++ = *p;
386: } else {
387: *ap++ = *p;
388: }
389: if (*p)
390: p++;
391: else
392: break;
393: }
394:
395: *ap++ = '\0';
396:
397: if (addrfound) {
398: (void) strcpy(name, sp_strip(addrbuf));
399: (void) strcpy(addr, strcpy(commbuf, sp_strip(addr)));
400: } else {
401: (void) strcpy(addr, sp_strip(addrbuf));
402: *name = '\0';
403: }
404: /*
405: ** Just to be sure that we got the full name,
406: ** we'll take all of the comments!
407: */
408: if (commfound) {
409: register int len;
410: register int flag = (*name != '\0' ? TRUE : FALSE);
411:
412: for(cp = &comm; cp->l_item; cp = cp->l_next) {
413: if (flag)
414: (void)strcat(name, ", ");
415: flag = TRUE;
416: if (cp->l_item[cp->l_len - 2] == ')')
417: cp->l_item[cp->l_len - 2] = '\0';
418: (void)strcat(name, sp_strip(&cp->l_item[1]));
419: }
420: l_free(comm);
421: }
422: return(TRUE);
423: }
424:
425: /*
426: ** Check on the validity of an RFC822 message-id.
427: ** Check for enclosing `<>', an `@', and a `.' after
428: ** the `@'. Also make sure that everything is ASCII,
429: ** and non-control characters.
430: */
431: msgid_ok(id)
432: register char *id;
433: {
434: register atdot = FALSE;
435: register closure = FALSE;
436:
437: if (id == NULL)
438: return(FALSE); /* don't waste my time! */
439:
440: if (*id != '<')
441: return(FALSE);
442:
443: /* skip the first `<', cause we check for more */
444: for(id++; *id; id++) {
445: switch(*id) {
446: case '<':
447: return(FALSE); /* we've already got one */
448: case '>':
449: closure = TRUE;
450: break;
451: case '.':
452: case '@': /* should be a domain spec */
453: atdot = TRUE;
454: break;
455: default:
456: if (!isascii(*id) || iscntrl(*id) || isspace(*id))
457: return(FALSE); /* quit immediately */
458: break;
459: }
460: }
461: return(atdot && closure);
462: }
463:
464: /*
465: ** hfgets is like fgets, but deals with continuation lines.
466: ** It also ensures that even if a line that is too long is
467: ** received, the remainder of the line is thrown away
468: ** instead of treated like a second line.
469: */
470: char *
471: hfgets(buf, len, fp)
472: char *buf;
473: int len;
474: FILE *fp;
475: {
476: register int c;
477: register int n = 0;
478: register char *cp;
479:
480: cp = buf;
481: while (n < len && (c = getc(fp)) != EOF) {
482: if (c == '\n')
483: break;
484: if (!iscntrl(c) || c == '\b' || c == '\t') {
485: *cp++ = c;
486: n++;
487: }
488: }
489: if (c == EOF && cp == buf)
490: return NULL;
491: *cp = '\0';
492:
493: if (c != '\n') {
494: /* Line too long - part read didn't fit into a newline */
495: while ((c = getc(fp)) != '\n' && c != EOF)
496: ;
497: } else if (cp == buf) {
498: /* Don't look for continuation of blank lines */
499: *cp++ = '\n';
500: *cp = '\0';
501: return buf;
502: }
503:
504: while ((c = getc(fp)) == ' ' || c == '\t') { /* for each cont line */
505: /* Continuation line. */
506: if ((n += 2) < len) {
507: *cp++ = '\n';
508: *cp++ = c;
509: }
510: while ((c = getc(fp)) != '\n' && c != EOF)
511: if ((!iscntrl(c) || c == '\b' || c == '\t') && n++ < len)
512: *cp++ = c;
513: }
514: if (n >= len - 1)
515: cp = buf + len - 2;
516: *cp++ = '\n';
517: *cp = '\0';
518: if (c != EOF)
519: (void) ungetc(c, fp); /* push back first char of next header */
520: return buf;
521: }
522:
523: /*
524: * arpadate is like ctime(3) except that the time is returned in
525: * an acceptable ARPANET time format instead of ctime format.
526: */
527: char *
528: arpadate(longtime)
529: time_t *longtime;
530: {
531: register char *p, *q, *ud;
532: register int i;
533: static char b[40];
534: extern struct tm *gmtime();
535: extern char *asctime();
536:
537: /* Get current time. This will be used resolve the timezone. */
538: ud = asctime(gmtime(longtime));
539:
540: /* Crack the UNIX date line in a singularly unoriginal way. */
541: q = b;
542:
543: #ifdef notdef
544: /* until every site installs the fix to getdate.y, the day
545: of the week can cause time warps */
546: p = &ud[0]; /* Mon */
547: *q++ = *p++;
548: *q++ = *p++;
549: *q++ = *p++;
550: *q++ = ','; *q++ = ' ';
551: #endif
552:
553: p = &ud[8]; /* 16 */
554: if (*p == ' ')
555: p++;
556: else
557: *q++ = *p++;
558: *q++ = *p++; *q++ = ' ';
559:
560: p = &ud[4]; /* Sep */
561: *q++ = *p++; *q++ = *p++; *q++ = *p++; *q++ = ' ';
562:
563: p = &ud[22]; /* 1979 */
564: *q++ = *p++; *q++ = *p++; *q++ = ' ';
565:
566: p = &ud[11]; /* 01:03:52 */
567: for (i = 8; i > 0; i--)
568: *q++ = *p++;
569:
570: *q++ = ' ';
571: *q++ = 'G'; /* GMT */
572: *q++ = 'M';
573: *q++ = 'T';
574: *q = '\0';
575:
576: return b;
577: }
578:
579: time_t
580: cgtdate(datestr)
581: char *datestr;
582: {
583: char junk[40], month[40], day[30], tod[60], year[50], buf[BUFLEN];
584: static time_t lasttime;
585: static char lastdatestr[BUFLEN] = "";
586: extern time_t getdate();
587:
588: if (lastdatestr[0] && strcmp(datestr, lastdatestr) == 0)
589: return(lasttime);
590: lasttime = getdate(datestr, (struct timeb *)NULL);
591: if (lasttime < 0 &&
592: sscanf(datestr, "%s %s %s %s %s", junk, month, day, tod, year) == 5) {
593: (void) sprintf(buf, "%s %s, %s %s", month, day, year, tod);
594: lasttime = getdate(buf, (struct timeb *)NULL);
595: }
596: strncpy(lastdatestr, datestr, BUFLEN);
597: return(lasttime);
598: }
599:
600: char *
601: errmsg(code)
602: int code;
603: {
604: extern int sys_nerr;
605: extern char *sys_errlist[];
606: static char ebuf[6+5+1];
607:
608: if (code > sys_nerr) {
609: (void) sprintf(ebuf, "Error %d", code);
610: return ebuf;
611: } else
612: return sys_errlist[code];
613: }
614:
615: /*
616: * Strip trailing newlines, blanks, and tabs from 's'.
617: * Return TRUE if newline was found, else FALSE.
618: */
619: nstrip(s)
620: register char *s;
621: {
622: register char *p;
623: register int rc;
624:
625: rc = FALSE;
626: p = s;
627: while (*p)
628: if (*p++ == '\n')
629: rc = TRUE;
630: while (--p >= s && (*p == '\n' || *p == ' ' || *p == '\t'));
631: *++p = '\0';
632: return rc;
633: }
634:
635: prefix(full, pref)
636: register char *full, *pref;
637: {
638: register char fc, pc;
639:
640: while ((pc = *pref++) != '\0') {
641: fc = *full++;
642: if (isupper(fc))
643: fc = tolower(fc);
644: if (isupper(pc))
645: pc = tolower(pc);
646: if (fc != pc)
647: return FALSE;
648: }
649: return TRUE;
650: }
651:
652: #ifndef USG
653: char *
654: strpbrk(str, chars)
655: register char *str, *chars;
656: {
657: register char *cp;
658:
659: do {
660: cp = chars - 1;
661: while (*++cp) {
662: if (*str == *cp)
663: return str;
664: }
665: } while (*str++);
666: return NULL;
667: }
668: #endif /* !USG */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.