|
|
1.1 root 1: /*
2: ** message spooing, header and address parsing and completion
3: ** functions for smail/rmail
4: */
5:
6: #ifndef lint
7: static char *sccsid="@(#)headers.c 2.5 (smail) 9/15/87";
8: #endif
9:
10: # include <stdio.h>
11: # include <sys/types.h>
12: # include <time.h>
13: # include <ctype.h>
14: # include <pwd.h>
15: # include "defs.h"
16:
17: extern enum edebug debug; /* how verbose we are */
18: extern char hostname[]; /* */
19: extern char hostdomain[]; /* */
20: extern char *spoolfile; /* file name of spooled message */
21: extern FILE *spoolfp; /* file ptr to spooled message */
22: extern int spoolmaster; /* set if creator of spoolfile */
23: extern time_t now; /* time */
24: extern char nows[], arpanows[]; /* time strings */
25: extern struct tm *gmt, *loc; /* time structs */
26: extern char *from_addr; /* replacement fromaddr with -F */
27:
28: static char toline[SMLBUF];
29: static char fromline[SMLBUF];
30: static char dateline[SMLBUF];
31: static char midline[SMLBUF];
32: static char *ieof = "NOTNULL";
33:
34: struct reqheaders {
35: char *name;
36: char *field;
37: char have;
38: };
39:
40: static struct reqheaders reqtab[] = {
41: "Message-Id:" , midline , 'N' ,
42: "Date:" , dateline , 'N' ,
43: "From:" , fromline , 'N' ,
44: "To:" , toline , 'N' ,
45: NULL , NULL , 'N'
46: };
47:
48:
49: /*
50: **
51: ** parse(): parse <address> into <domain, user, form>.
52: **
53: ** input form
54: ** ----- ----
55: ** user LOCAL
56: ** domain!user DOMAIN
57: ** user@domain DOMAIN
58: ** @domain,address LOCAL (just for sendmail)
59: ** host!address UUCP
60: **
61: */
62:
63: enum eform
64: parse(address, domain, user)
65: char *address; /* input address */
66: char *domain; /* output domain */
67: char *user; /* output user */
68: {
69: int parts;
70: char *partv[MAXPATH]; /* to crack address */
71:
72: /*
73: ** If this is route address form @domain_a,@domain_b:user@domain_c, ...
74: */
75: if(*address == '@')
76: #ifdef SENDMAIL
77: /*
78: ** hand it to sendmail
79: */
80: {
81: goto local;
82: }
83: #else
84: /*
85: ** no sendmail, convert it into a bang path: domain_a!domain_b!domain_c!user
86: */
87: {
88: char buf[SMLBUF], *p;
89: char t_dom[SMLBUF], t_user[SMLBUF];
90:
91: (void) strcpy(buf, address+1); /* elide leading '@' */
92:
93: for(p=buf; *p != '\0' ; p++) { /* search for ',' or ':' */
94: if(*p == ':') { /* reached end of route */
95: break;
96: }
97: if(*p == ',') { /* elide ','s */
98: (void) strcpy(p, p+1);
99: }
100: if(*p == '@') { /* convert '@' to '!' */
101: *p = '!';
102: }
103: }
104:
105: if(*p != ':') { /* bad syntax - punt */
106: goto local;
107: }
108: *p = '\0';
109:
110: if(parse(p+1, t_dom, t_user) != LOCAL) {
111: (void) strcat(buf, "!");
112: (void) strcat(buf, t_dom);
113: }
114: (void) strcat(buf, "!");
115: (void) strcat(buf, t_user);
116:
117: /* munge the address (yuk)
118: ** it's OK to copy into 'address', because the machinations
119: ** above don't increase the string length of the address.
120: */
121:
122: (void) strcpy(address, buf);
123:
124: /* re-parse the address */
125: return(parse(address, domain, user));
126: }
127: #endif
128: /*
129: ** Try splitting at @. If it works, this is user@domain, form DOMAIN.
130: ** Prefer the righthand @ in a@b@c.
131: */
132: if ((parts = ssplit(address, '@', partv)) >= 2) {
133: (void) strcpy(domain, partv[parts-1]);
134: (void) strncpy(user, partv[0], partv[parts-1]-partv[0]-1);
135: user[partv[parts-1]-partv[0]-1] = '\0';
136: return (DOMAIN);
137: }
138: /*
139: ** Try splitting at !. If it works, see if the piece before the ! has
140: ** a . in it (domain!user, form DOMAIN) or not (host!user, form UUCP).
141: */
142: if (ssplit(address, '!', partv) > 1) {
143: (void) strcpy(user, partv[1]);
144: (void) strncpy(domain, partv[0], partv[1]-partv[0]-1);
145: domain[partv[1]-partv[0]-1] = '\0';
146:
147: if((parts = ssplit(domain, '.', partv)) < 2) {
148: return(UUCP);
149: }
150:
151: if(partv[parts-1][0] == '\0') {
152: partv[parts-1][-1] = '\0'; /* strip trailing . */
153: }
154: return (DOMAIN);
155: }
156: /*
157: ** Done trying. This must be just a user name, form LOCAL.
158: */
159: local:
160: (void) strcpy(user, address);
161: (void) strcpy(domain, "");
162: return(LOCAL); /* user */
163: }
164:
165: build(domain, user, form, result)
166: char *domain;
167: char *user;
168: enum eform form;
169: char *result;
170: {
171: switch((int) form) {
172: case LOCAL:
173: (void) sprintf(result, "%s", user);
174: break;
175: case UUCP:
176: (void) sprintf(result, "%s!%s", domain, user);
177: break;
178: case DOMAIN:
179: (void) sprintf(result, "%s@%s", user, domain);
180: break;
181: }
182: }
183:
184: /*
185: ** ssplit(): split a line into array pointers.
186: **
187: ** Each pointer wordv[i] points to the first character after the i'th
188: ** occurence of c in buf. Note that each wordv[i] includes wordv[i+1].
189: **
190: */
191:
192: ssplit(buf, c, ptr)
193: register char *buf; /* line to split up */
194: char c; /* character to split on */
195: char **ptr; /* the resultant vector */
196: {
197: int count = 0;
198: int wasword = 0;
199:
200: for(; *buf; buf++) {
201: if (!wasword) {
202: count++;
203: *ptr++ = buf;
204: }
205: wasword = (c != *buf);
206: }
207: if (!wasword) {
208: count++;
209: *ptr++ = buf;
210: }
211: *ptr = NULL;
212: return(count);
213: }
214:
215: /*
216: ** Determine whether an address is a local address
217: */
218:
219: islocal(addr, domain, user)
220: char *addr, *domain, *user;
221: {
222: enum eform form, parse();
223: extern char hostuucp[];
224:
225: /*
226: ** parse the address
227: */
228:
229: form = parse(addr, domain, user);
230:
231: if((form == LOCAL) /* user */
232: ||(strcmpic(domain, hostdomain) == 0) /* user@hostdomain */
233: ||(strcmpic(domain, hostname) == 0) /* user@hostname */
234: #ifdef DOMGATE
235: ||(strcmpic(domain, &MYDOM[0]) == 0) /* user@MYDOM w/ dot */
236: ||(strcmpic(domain, &MYDOM[1]) == 0) /* user@MYDOM no dot */
237: #endif
238: ||(strcmpic(domain, hostuucp) == 0)) {/* user@hostuucp */
239: return(1);
240: }
241: return(0);
242: }
243:
244: /*
245: ** spool - message spooling module
246: **
247: ** (1) get dates for headers, etc.
248: ** (2) if the message is on the standard input (no '-f')
249: ** (a) create a temp file for spooling the message.
250: ** (b) collapse the From_ headers into a path.
251: ** (c) if the mail originated locally, then
252: ** (i) establish default headers
253: ** (ii) scan the message headers for required header fields
254: ** (iii) add any required message headers that are absent
255: ** (d) copy rest of the message to the spool file
256: ** (e) close the spool file
257: ** (3) open the spool file for reading
258: */
259:
260: void
261: spool(argc, argv)
262: int argc;
263: char **argv;
264: {
265: static char *tmpf = "/tmp/rmXXXXXX"; /* temp file name */
266: char *mktemp();
267: char buf[SMLBUF];
268: static char splbuf[SMLBUF];
269: char from[SMLBUF], domain[SMLBUF], user[SMLBUF];
270: void rline(), scanheaders(), compheaders();
271:
272: /*
273: ** if the mail has already been spooled by
274: ** a previous invocation of smail don't respool.
275: ** check the file name to prevent things like
276: ** rmail -f /etc/passwd badguy@dreadfuldomain
277: */
278:
279: if((spoolfile != NULL)
280: && (strncmp(spoolfile, tmpf, strlen(tmpf) - 6) != 0)) {
281: error(EX_TEMPFAIL, "spool: bad file name '%s'\n", spoolfile);
282: }
283:
284: /*
285: ** set dates in local, arpa, and gmt forms
286: */
287: setdates();
288:
289: /*
290: ** If necessary, copy stdin to a temp file.
291: */
292:
293: if(spoolfile == NULL) {
294: spoolfile = strcpy(splbuf, tmpf);
295: (void) mktemp(spoolfile);
296:
297: if((spoolfp = fopen(spoolfile, "w")) == NULL) {
298: error(EX_CANTCREAT, "can't create %s.\n", spoolfile);
299: }
300:
301: spoolmaster = 1;
302:
303: /*
304: ** rline reads the standard input,
305: ** collapsing the From_ and >From_
306: ** lines into a single uucp path.
307: ** first non-from_ line is in buf[];
308: */
309:
310: rline(from, buf);
311:
312: /*
313: ** if the mail originated here, we parse the header
314: ** and add any required headers that are missing.
315: */
316:
317: if(islocal(from, domain, user) || (from_addr != NULL)) {
318: /*
319: ** initialize default headers
320: */
321: def_headers(argc, argv, from);
322:
323: /*
324: ** buf has first, non-from_ line
325: */
326: scanheaders(buf);
327: /*
328: ** buf has first, non-header line,
329: */
330:
331: compheaders();
332:
333: if(buf[0] != '\n') {
334: (void) fputs("\n", spoolfp);
335: }
336: }
337:
338: /*
339: ** now, copy the rest of the letter into the spool file
340: ** terminate on either EOF or '^.$'
341: */
342:
343: while(ieof != NULL) {
344: (void) fputs(buf, spoolfp);
345: if((fgets(buf, SMLBUF, stdin) == NULL)
346: || (buf[0] == '.' && buf[1] == '\n')) {
347: ieof = NULL;
348: }
349: }
350:
351: /*
352: ** close the spool file, and the standard input.
353: */
354:
355: (void) fclose(spoolfp);
356: (void) fclose(stdin); /* you don't see this too often! */
357: }
358:
359: if((spoolfp = fopen(spoolfile, "r")) == NULL) {
360: error(EX_TEMPFAIL, "can't open %s.\n", spoolfile);
361: }
362: }
363:
364: /*
365: **
366: ** rline(): collapse From_ and >From_ lines.
367: **
368: ** Same idea as the old rmail, but also turns user@domain to domain!user.
369: **
370: */
371:
372: void
373: rline(from, retbuf)
374: char *from;
375: char *retbuf;
376: {
377: int parts; /* for cracking From_ lines ... */
378: char *partv[16]; /* ... apart using ssplit() */
379: char user[SMLBUF]; /* for rewriting user@host */
380: char domain[SMLBUF]; /* " " " */
381: char addr[SMLBUF]; /* " " " */
382: enum eform form, parse(); /* " " " */
383: extern build(); /* " " " */
384: char *c;
385: int nhops, i;
386: char buf[SMLBUF], tmp[SMLBUF], *hop[128], *e, *b;
387: char *pwuid();
388:
389: if(spoolmaster == 0) return;
390:
391: buf[0] = from[0] = addr[0] = '\0';
392: /*
393: ** Read each line until we hit EOF or a line not beginning with "From "
394: ** or ">From " (called From_ lines), accumulating the new path in from
395: ** and stuffing the actual sending user (the user name on the last From_
396: ** line) in addr.
397: */
398: for(;;) {
399: (void) strcpy(retbuf, buf);
400: if(ieof == NULL) {
401: break;
402: }
403: if((fgets(buf, sizeof(buf), stdin) == NULL)
404: || (buf[0] == '.' && buf[1] == '\n')) {
405: ieof = NULL;
406: break;
407: }
408: if (strncmp("From ", buf, 5)
409: && strncmp(">From ", buf, 6)) {
410: break;
411: }
412: /*
413: ** Crack the line apart using ssplit.
414: */
415: if(c = index(buf, '\n')) {
416: *c = '\0';
417: }
418: parts = ssplit(buf, ' ', partv);
419: /*
420: ** Tack host! onto the from argument if "remote from host" is present.
421: */
422:
423: if((parts > 3)
424: && (strncmp("remote from ", partv[parts-3], 12) == 0)) {
425: (void) strcat(from, partv[parts-1]);
426: (void) strcat(from, "!");
427: }
428: /*
429: ** Stuff user name into addr, overwriting the user name from previous
430: ** From_ lines, since only the last one counts. Then rewrite user@host
431: ** into host!user, since @'s don't belong in the From_ argument.
432: */
433: if(parts < 2) {
434: break;
435: } else {
436: char *x = partv[1];
437: char *q = index(x, ' ');
438: if(q != NULL) {
439: *q = '\0';
440: }
441: (void) strcpy(addr, x);
442: }
443:
444: (void) parse(addr, domain, user);
445: if(*domain == '\0') {
446: form = LOCAL;
447: } else {
448: form = UUCP;
449: }
450:
451: build(domain, user, form, addr);
452: }
453: /*
454: ** Now tack the user name onto the from argument.
455: */
456: (void) strcat(from, addr);
457: /*
458: ** If we still have no from argument, we have junk headers, but we try
459: ** to get the user's name using /etc/passwd.
460: */
461:
462: if (from[0] == '\0') {
463: char *login;
464: if ((login = pwuid(getuid())) == NULL) {
465: (void) strcpy(from, "nobody"); /* bad news */
466: } else {
467: (void) strcpy(from, login);
468: }
469: }
470:
471: /* split the from line on '!'s */
472: nhops = ssplit(from, '!', hop);
473:
474: for(i = 0; i < (nhops - 1); i++) {
475: b = hop[i];
476: if(*b == '\0') {
477: continue;
478: }
479: e = hop[i+1];
480: e-- ;
481: *e = '\0'; /* null terminate each path segment */
482: e++;
483:
484: #ifdef HIDDENHOSTS
485: /*
486: ** Strip hidden hosts: anything.hostname.MYDOM -> hostname.MYDOM
487: */
488: for(p = b;(p = index(p, '.')) != NULL; p++) {
489: if(strcmpic(hostdomain, p+1) == 0) {
490: (void) strcpy(b, hostdomain);
491: break;
492: }
493: }
494: #endif
495:
496: /*
497: ** Strip useless MYDOM: hostname.MYDOM -> hostname
498: */
499: if(strcmpic(hop[i], hostdomain) == 0) {
500: (void) strcpy(hop[i], hostname);
501: }
502: }
503:
504: /*
505: ** Now strip out any redundant information in the From_ line
506: ** a!b!c!c!d => a!b!c!d
507: */
508:
509: for(i = 0; i < (nhops - 2); i++) {
510: b = hop[i];
511: e = hop[i+1];
512: if(strcmpic(b, e) == 0) {
513: *b = '\0';
514: }
515: }
516: /*
517: ** Reconstruct the From_ line
518: */
519: tmp[0] = '\0'; /* empty the tmp buffer */
520:
521: for(i = 0; i < (nhops - 1); i++) {
522: if((hop[i][0] == '\0') /* deleted this hop */
523: ||((tmp[0] == '\0') /* first hop == hostname */
524: &&(strcmpic(hop[i], hostname) == 0))) {
525: continue;
526: }
527: (void) strcat(tmp, hop[i]);
528: (void) strcat(tmp, "!");
529: }
530: (void) strcat(tmp, hop[i]);
531: (void) strcpy(from, tmp);
532: (void) strcpy(retbuf, buf);
533: (void) fprintf(spoolfp, "%s\n", from);
534: }
535:
536: void
537: scanheaders(buf)
538: char *buf;
539: {
540: int inheader = 0;
541:
542: while(ieof != NULL) {
543: if(buf[0] == '\n') {
544: break; /* end of headers */
545: }
546:
547: /*
548: ** header lines which begin with whitespace
549: ** are continuation lines
550: */
551: if((inheader == 0)
552: || ((buf[0] != ' ' && buf[0] != '\t'))) {
553: /* not a continuation line
554: ** check for header
555: */
556: if(isheader(buf) == 0) {
557: /*
558: ** not a header
559: */
560: break;
561: }
562: inheader = 1;
563: haveheaders(buf);
564: }
565: (void) fputs(buf, spoolfp);
566: if((fgets(buf, SMLBUF, stdin) == NULL)
567: || (buf[0] == '.' && buf[1] == '\n')) {
568: ieof = NULL;
569: }
570: }
571:
572: if(isheader(buf)) {
573: buf[0] = '\0';
574: }
575: }
576:
577: /*
578: ** complete headers - add any required headers that are not in the message
579: */
580: void
581: compheaders()
582: {
583: struct reqheaders *i;
584:
585: /*
586: ** look at the table of required headers and
587: ** add those that are missing to the spooled message.
588: */
589: for(i = reqtab; i->name != NULL; i++) {
590: if(i->have != 'Y') {
591: (void) fprintf(spoolfp, "%s\n", i->field);
592: }
593: }
594: }
595:
596: /*
597: ** look at a string and determine
598: ** whether or not it is a valid header.
599: */
600: isheader(s)
601: char *s;
602: {
603: char *p;
604:
605: /*
606: ** header field names must terminate with a colon
607: ** and may not be null.
608: */
609: if(((p = index(s, ':')) == NULL) || (s == p)) {
610: return(0);
611:
612: }
613: /*
614: ** header field names must consist entirely of
615: ** printable ascii characters.
616: */
617: while(s != p) {
618: if((*s < '!') || (*s > '~')) {
619: return(0);
620: }
621: s++;
622: }
623: /*
624: ** we hit the ':', so the field may be a header
625: */
626: return(1);
627: }
628:
629: /*
630: ** compare the header field to those in the required header table.
631: ** if it matches, then mark the required header as being present
632: ** in the message.
633: */
634: haveheaders(s)
635: char *s;
636: {
637: struct reqheaders *i;
638:
639: for(i = reqtab; i->name != NULL; i++) {
640: if(strncmpic(i->name, s, strlen(i->name)) == 0) {
641: if((strncmpic("From:", s, 5) == 0)
642: && (from_addr != NULL)) {
643: (void) sprintf(s, "From: %s\n", from_addr);
644: }
645: i->have = 'Y';
646: break;
647: }
648: }
649: }
650:
651: /*
652: ** create default headers for the message.
653: */
654: def_headers(argc, argv, from)
655: int argc;
656: char **argv;
657: char *from;
658: {
659: def_to(argc, argv); /* default To: */
660: def_date(); /* default Date: */
661: def_from(from); /* default From: */
662: def_mid(); /* default Message-Id: */
663: }
664:
665: /*
666: ** default Date: in arpa format
667: */
668: def_date()
669: {
670: (void) strcpy(dateline, "Date: ");
671: (void) strcat(dateline, arpanows);
672: }
673:
674: /*
675: ** default Message-Id
676: ** Message-Id: <yymmddhhmm.AAppppp@hostdomain>
677: **
678: ** yy year
679: ** mm month
680: ** dd day
681: ** hh hour
682: ** mm minute
683: ** ppppp process-id
684: **
685: ** date and time are set by GMT
686: */
687: def_mid()
688: {
689: (void) sprintf(midline, "Message-Id: <%02d%02d%02d%02d%02d.AA%05d@%s>",
690: gmt->tm_year,
691: gmt->tm_mon+1,
692: gmt->tm_mday,
693: gmt->tm_hour,
694: gmt->tm_min,
695: getpid(),
696: hostdomain);
697: }
698:
699: /*
700: ** default From:
701: ** From: user@hostdomain (Full Name)
702: */
703: def_from(from)
704: char *from;
705: {
706:
707: char *nameptr;
708: char name[SMLBUF];
709: char *getenv(), *login;
710: char *pwfnam(), *pwuid();
711:
712: if (from_addr != NULL) {
713: (void) sprintf(fromline, "From: %s", from_addr);
714: return;
715: }
716:
717: name[0] = '\0';
718: if((nameptr = getenv("NAME")) != NULL) {
719: (void) strcpy(name, nameptr);
720: } else if((login = pwuid(getuid())) != NULL) {
721: if((nameptr = pwfnam(login)) != NULL) {
722: (void) strcpy(name, nameptr);
723: }
724: }
725: if(name[0] != '\0') {
726: (void) sprintf(fromline,
727: "From: %s@%s (%s)", from, hostdomain, name);
728: } else {
729: (void) sprintf(fromline,
730: "From: %s@%s", from, hostdomain);
731: }
732: }
733:
734: /*
735: ** default To:
736: ** To: recip1, recip2, ...
737: **
738: ** lines longer than 50 chars are continued on another line.
739: */
740: def_to(argc, argv)
741: int argc;
742: char **argv;
743: {
744: int i, n;
745: char *bol;
746:
747: bol = toline;
748: (void) strcpy(bol, "To: ");
749: for(n = i = 0; i < argc; i++) {
750: (void) strcat(bol, argv[i]);
751:
752: if((index(argv[i], '!') == NULL)
753: && (index(argv[i], '@') == NULL)) {
754: (void) strcat(bol, "@");
755: (void) strcat(bol, hostdomain);
756: }
757: if(i+1 < argc) {
758: n = strlen(bol);
759: if(n > 50) {
760: (void) strcat(bol, ",\n\t");
761: bol = bol + strlen(bol);
762: *bol = '\0';
763: n = 8;
764: } else {
765: (void) strcat(bol, ", ");
766: }
767: }
768: }
769: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.