|
|
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: goto end; /* djikstra is a wimp! */
451: break;
452: case '.':
453: case '@': /* should be a domain spec */
454: atdot = TRUE;
455: break;
456: default:
457: if (!isascii(*id) || iscntrl(*id) || isspace(*id))
458: return(FALSE); /* quit immediately */
459: break;
460: }
461: }
462: end:
463: return(atdot && closure);
464: }
465:
466: /*
467: ** hfgets is like fgets, but deals with continuation lines.
468: ** It also ensures that even if a line that is too long is
469: ** received, the remainder of the line is thrown away
470: ** instead of treated like a second line.
471: */
472: char *
473: hfgets(buf, len, fp)
474: char *buf;
475: int len;
476: FILE *fp;
477: {
478: register int c;
479: register int n = 0;
480: register char *cp;
481:
482: cp = buf;
483: while (n < len && (c = getc(fp)) != EOF) {
484: if (c == '\n')
485: break;
486: if (!iscntrl(c) || c == '\b' || c == '\t') {
487: *cp++ = c;
488: n++;
489: }
490: }
491: if (c == EOF && cp == buf)
492: return NULL;
493: *cp = '\0';
494:
495: if (c != '\n') {
496: /* Line too long - part read didn't fit into a newline */
497: while ((c = getc(fp)) != '\n' && c != EOF)
498: ;
499: } else if (cp == buf) {
500: /* Don't look for continuation of blank lines */
501: *cp++ = '\n';
502: *cp = '\0';
503: return buf;
504: }
505:
506: while ((c = getc(fp)) == ' ' || c == '\t') { /* for each cont line */
507: /* Continuation line. */
508: if ((n += 2) < len) {
509: *cp++ = '\n';
510: *cp++ = c;
511: }
512: while ((c = getc(fp)) != '\n' && c != EOF)
513: if ((!iscntrl(c) || c == '\b' || c == '\t') && n++ < len)
514: *cp++ = c;
515: }
516: if (n >= len - 1)
517: cp = buf + len - 2;
518: *cp++ = '\n';
519: *cp = '\0';
520: if (c != EOF)
521: (void) ungetc(c, fp); /* push back first char of next header */
522: return buf;
523: }
524:
525: /*
526: * arpadate is like ctime(3) except that the time is returned in
527: * an acceptable ARPANET time format instead of ctime format.
528: */
529: char *
530: arpadate(longtime)
531: time_t *longtime;
532: {
533: register char *p, *q, *ud;
534: register int i;
535: static char b[40];
536: extern struct tm *gmtime();
537: extern char *asctime();
538:
539: /* Get current time. This will be used resolve the timezone. */
540: ud = asctime(gmtime(longtime));
541:
542: /* Crack the UNIX date line in a singularly unoriginal way. */
543: q = b;
544:
545: #ifdef notdef
546: /* until every site installs the fix to getdate.y, the day
547: of the week can cause time warps */
548: p = &ud[0]; /* Mon */
549: *q++ = *p++;
550: *q++ = *p++;
551: *q++ = *p++;
552: *q++ = ','; *q++ = ' ';
553: #endif
554:
555: p = &ud[8]; /* 16 */
556: if (*p == ' ')
557: p++;
558: else
559: *q++ = *p++;
560: *q++ = *p++; *q++ = ' ';
561:
562: p = &ud[4]; /* Sep */
563: *q++ = *p++; *q++ = *p++; *q++ = *p++; *q++ = ' ';
564:
565: p = &ud[22]; /* 1979 */
566: *q++ = *p++; *q++ = *p++; *q++ = ' ';
567:
568: p = &ud[11]; /* 01:03:52 */
569: for (i = 8; i > 0; i--)
570: *q++ = *p++;
571:
572: *q++ = ' ';
573: *q++ = 'G'; /* GMT */
574: *q++ = 'M';
575: *q++ = 'T';
576: *q = '\0';
577:
578: return b;
579: }
580:
581: time_t
582: cgtdate(datestr)
583: char *datestr;
584: {
585: char junk[40], month[40], day[30], tod[60], year[50], buf[BUFLEN];
586: static time_t lasttime;
587: static char lastdatestr[BUFLEN] = "";
588: extern time_t getdate();
589:
590: if (lastdatestr[0] && strcmp(datestr, lastdatestr) == 0)
591: return(lasttime);
592: lasttime = getdate(datestr, (struct timeb *)NULL);
593: if (lasttime < 0 &&
594: sscanf(datestr, "%s %s %s %s %s", junk, month, day, tod, year) == 5) {
595: (void) sprintf(buf, "%s %s, %s %s", month, day, year, tod);
596: lasttime = getdate(buf, (struct timeb *)NULL);
597: }
598: strncpy(lastdatestr, datestr, BUFLEN);
599: return(lasttime);
600: }
601:
602: char *
603: errmsg(code)
604: int code;
605: {
606: extern int sys_nerr;
607: extern char *sys_errlist[];
608: static char ebuf[6+5+1];
609:
610: if (code > sys_nerr) {
611: (void) sprintf(ebuf, "Error %d", code);
612: return ebuf;
613: } else
614: return sys_errlist[code];
615: }
616:
617: /*
618: * Strip trailing newlines, blanks, and tabs from 's'.
619: * Return TRUE if newline was found, else FALSE.
620: */
621: nstrip(s)
622: register char *s;
623: {
624: register char *p;
625: register int rc;
626:
627: rc = FALSE;
628: p = s;
629: while (*p)
630: if (*p++ == '\n')
631: rc = TRUE;
632: while (--p >= s && (*p == '\n' || *p == ' ' || *p == '\t'));
633: *++p = '\0';
634: return rc;
635: }
636:
637: prefix(full, pref)
638: register char *full, *pref;
639: {
640: register char fc, pc;
641:
642: while ((pc = *pref++) != '\0') {
643: fc = *full++;
644: if (isupper(fc))
645: fc = tolower(fc);
646: if (isupper(pc))
647: pc = tolower(pc);
648: if (fc != pc)
649: return FALSE;
650: }
651: return TRUE;
652: }
653:
654: #ifndef USG
655: char *
656: strpbrk(str, chars)
657: register char *str, *chars;
658: {
659: register char *cp;
660:
661: do {
662: cp = chars - 1;
663: while (*++cp) {
664: if (*str == *cp)
665: return str;
666: }
667: } while (*str++);
668: return NULL;
669: }
670: #endif /* !USG */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.