1 #ifndef lint
2 static char sccsid[] = "@(#)mail.local.c	4.30 (Berkeley) 10/22/87";
3 #endif
4 
5 #include <sys/param.h>
6 #include <sys/stat.h>
7 #include <sys/file.h>
8 
9 #include <ctype.h>
10 #include <stdio.h>
11 #include <pwd.h>
12 #include <utmp.h>
13 #include <signal.h>
14 #include <setjmp.h>
15 #include <sysexits.h>
16 
17 #define SENDMAIL	"/usr/lib/sendmail"
18 
19 	/* copylet flags */
20 #define REMOTE		1		/* remote mail, add rmtmsg */
21 #define ORDINARY	2
22 #define ZAP		3		/* zap header and trailing empty line */
23 #define	FORWARD		4
24 
25 #define	LSIZE		256
26 #define	MAXLET		300		/* maximum number of letters */
27 #define	MAILMODE	0600		/* mode of created mail */
28 
29 char	line[LSIZE];
30 char	resp[LSIZE];
31 struct let {
32 	long	adr;
33 	char	change;
34 } let[MAXLET];
35 int	nlet	= 0;
36 char	lfil[50];
37 long	iop, time();
38 char	*getenv();
39 char	*index();
40 char	lettmp[] = "/tmp/maXXXXX";
41 char	maildir[] = "/usr/spool/mail/";
42 char	mailfile[] = "/usr/spool/mail/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
43 char	dead[] = "dead.letter";
44 char	forwmsg[] = " forwarded\n";
45 FILE	*tmpf;
46 FILE	*malf;
47 char	my_name[60];
48 char	*getlogin();
49 int	error;
50 int	changed;
51 int	forward;
52 char	from[] = "From ";
53 long	ftell();
54 int	delex();
55 char	*ctime();
56 int	flgf;
57 int	flgp;
58 int	delflg = 1;
59 int	hseqno;
60 jmp_buf	sjbuf;
61 int	rmail;
62 
63 main(argc, argv)
64 char **argv;
65 {
66 	register i;
67 	char *name;
68 	struct passwd *pwent;
69 
70 	name = getlogin();
71 	if (name == NULL || *name == '\0') {
72 		pwent = getpwuid(getuid());
73 		if (pwent==NULL)
74 			name = "???";
75 		else
76 			name = pwent->pw_name;
77 	}
78 	else {
79 		pwent = getpwnam(name);
80 		if (!pwent || getuid() != pwent->pw_uid) {
81 			pwent = getpwuid(getuid());
82 			name = pwent->pw_name;
83 		}
84 	}
85 	strncpy(my_name, name, sizeof(my_name)-1);
86 	if (setjmp(sjbuf))
87 		done();
88 	for (i=SIGHUP; i<=SIGTERM; i++)
89 		setsig(i, delex);
90 	i = mkstemp(lettmp);
91 	tmpf = fdopen(i, "r+w");
92 	if (i < 0 || tmpf == NULL)
93 		panic("mail: %s: cannot open for writing", lettmp);
94 	/*
95 	 * This protects against others reading mail from temp file and
96 	 * if we exit, the file will be deleted already.
97 	 */
98 	unlink(lettmp);
99 	if (argv[0][0] == 'r')
100 		rmail++;
101 	if (argv[0][0] != 'r' &&	/* no favors for rmail*/
102 	   (argc == 1 || argv[1][0] == '-' && !any(argv[1][1], "rhd")))
103 		printmail(argc, argv);
104 	else
105 		bulkmail(argc, argv);
106 	done();
107 }
108 
109 setsig(i, f)
110 int i;
111 int (*f)();
112 {
113 	if (signal(i, SIG_IGN) != SIG_IGN)
114 		signal(i, f);
115 }
116 
117 any(c, str)
118 	register int c;
119 	register char *str;
120 {
121 
122 	while (*str)
123 		if (c == *str++)
124 			return(1);
125 	return(0);
126 }
127 
128 printmail(argc, argv)
129 	char **argv;
130 {
131 	int flg, i, j, print;
132 	char *p, *getarg();
133 	struct stat statb;
134 
135 	setuid(getuid());
136 	cat(mailfile, maildir, my_name);
137 #ifdef notdef
138 	if (stat(mailfile, &statb) >= 0
139 	    && (statb.st_mode & S_IFMT) == S_IFDIR) {
140 		strcat(mailfile, "/");
141 		strcat(mailfile, my_name);
142 	}
143 #endif
144 	for (; argc > 1; argv++, argc--) {
145 		if (argv[1][0] != '-')
146 			break;
147 		switch (argv[1][1]) {
148 
149 		case 'p':
150 			flgp++;
151 			/* fall thru... */
152 		case 'q':
153 			delflg = 0;
154 			break;
155 
156 		case 'f':
157 			if (argc >= 3) {
158 				strcpy(mailfile, argv[2]);
159 				argv++, argc--;
160 			}
161 			break;
162 
163 		case 'b':
164 			forward = 1;
165 			break;
166 
167 		default:
168 			panic("unknown option %c", argv[1][1]);
169 			/*NOTREACHED*/
170 		}
171 	}
172 	malf = fopen(mailfile, "r");
173 	if (malf == NULL) {
174 		printf("No mail.\n");
175 		return;
176 	}
177 	flock(fileno(malf), LOCK_SH);
178 	copymt(malf, tmpf);
179 	fclose(malf);			/* implicit unlock */
180 	fseek(tmpf, 0, L_SET);
181 
182 	changed = 0;
183 	print = 1;
184 	for (i = 0; i < nlet; ) {
185 		j = forward ? i : nlet - i - 1;
186 		if (setjmp(sjbuf)) {
187 			print = 0;
188 		} else {
189 			if (print)
190 				copylet(j, stdout, ORDINARY);
191 			print = 1;
192 		}
193 		if (flgp) {
194 			i++;
195 			continue;
196 		}
197 		setjmp(sjbuf);
198 		fputs("? ", stdout);
199 		fflush(stdout);
200 		if (fgets(resp, LSIZE, stdin) == NULL)
201 			break;
202 		switch (resp[0]) {
203 
204 		default:
205 			printf("usage\n");
206 		case '?':
207 			print = 0;
208 			printf("q\tquit\n");
209 			printf("x\texit without changing mail\n");
210 			printf("p\tprint\n");
211 			printf("s[file]\tsave (default mbox)\n");
212 			printf("w[file]\tsame without header\n");
213 			printf("-\tprint previous\n");
214 			printf("d\tdelete\n");
215 			printf("+\tnext (no delete)\n");
216 			printf("m user\tmail to user\n");
217 			printf("! cmd\texecute cmd\n");
218 			break;
219 
220 		case '+':
221 		case 'n':
222 		case '\n':
223 			i++;
224 			break;
225 		case 'x':
226 			changed = 0;
227 		case 'q':
228 			goto donep;
229 		case 'p':
230 			break;
231 		case '^':
232 		case '-':
233 			if (--i < 0)
234 				i = 0;
235 			break;
236 		case 'y':
237 		case 'w':
238 		case 's':
239 			flg = 0;
240 			if (resp[1] != '\n' && resp[1] != ' ') {
241 				printf("illegal\n");
242 				flg++;
243 				print = 0;
244 				continue;
245 			}
246 			if (resp[1] == '\n' || resp[1] == '\0') {
247 				p = getenv("HOME");
248 				if (p != 0)
249 					cat(resp+1, p, "/mbox");
250 				else
251 					cat(resp+1, "", "mbox");
252 			}
253 			for (p = resp+1; (p = getarg(lfil, p)) != NULL; ) {
254 				malf = fopen(lfil, "a");
255 				if (malf == NULL) {
256 					printf("mail: %s: cannot append\n",
257 					    lfil);
258 					flg++;
259 					continue;
260 				}
261 				copylet(j, malf, resp[0]=='w'? ZAP: ORDINARY);
262 				fclose(malf);
263 			}
264 			if (flg)
265 				print = 0;
266 			else {
267 				let[j].change = 'd';
268 				changed++;
269 				i++;
270 			}
271 			break;
272 		case 'm':
273 			flg = 0;
274 			if (resp[1] == '\n' || resp[1] == '\0') {
275 				i++;
276 				continue;
277 			}
278 			if (resp[1] != ' ') {
279 				printf("invalid command\n");
280 				flg++;
281 				print = 0;
282 				continue;
283 			}
284 			for (p = resp+1; (p = getarg(lfil, p)) != NULL; )
285 				if (!sendmail(j, lfil, my_name))
286 					flg++;
287 			if (flg)
288 				print = 0;
289 			else {
290 				let[j].change = 'd';
291 				changed++;
292 				i++;
293 			}
294 			break;
295 		case '!':
296 			system(resp+1);
297 			printf("!\n");
298 			print = 0;
299 			break;
300 		case 'd':
301 			let[j].change = 'd';
302 			changed++;
303 			i++;
304 			if (resp[1] == 'q')
305 				goto donep;
306 			break;
307 		}
308 	}
309    donep:
310 	if (changed)
311 		copyback();
312 }
313 
314 /* copy temp or whatever back to /usr/spool/mail */
315 copyback()
316 {
317 	register i, c;
318 	int fd, new = 0, oldmask;
319 	struct stat stbuf;
320 
321 #define	mask(s)	(1 << ((s) - 1))
322 	oldmask = sigblock(mask(SIGINT)|mask(SIGHUP)|mask(SIGQUIT));
323 #undef mask
324 	fd = open(mailfile, O_RDWR | O_CREAT, MAILMODE);
325 	if (fd >= 0) {
326 		flock(fd, LOCK_EX);
327 		malf = fdopen(fd, "r+w");
328 	}
329 	if (fd < 0 || malf == NULL)
330 		panic("can't rewrite %s", lfil);
331 	fstat(fd, &stbuf);
332 	if (stbuf.st_size != let[nlet].adr) {	/* new mail has arrived */
333 		fseek(malf, let[nlet].adr, L_SET);
334 		fseek(tmpf, let[nlet].adr, L_SET);
335 		while ((c = getc(malf)) != EOF)
336 			putc(c, tmpf);
337 		let[++nlet].adr = stbuf.st_size;
338 		new = 1;
339 		fseek(malf, 0, L_SET);
340 	}
341 	ftruncate(fd, 0);
342 	for (i = 0; i < nlet; i++)
343 		if (let[i].change != 'd')
344 			copylet(i, malf, ORDINARY);
345 	fclose(malf);		/* implict unlock */
346 	if (new)
347 		printf("New mail has arrived.\n");
348 	sigsetmask(oldmask);
349 }
350 
351 /* copy mail (f1) to temp (f2) */
352 copymt(f1, f2)
353 	FILE *f1, *f2;
354 {
355 	long nextadr;
356 
357 	nlet = nextadr = 0;
358 	let[0].adr = 0;
359 	while (fgets(line, LSIZE, f1) != NULL) {
360 		if (isfrom(line))
361 			let[nlet++].adr = nextadr;
362 		nextadr += strlen(line);
363 		fputs(line, f2);
364 	}
365 	let[nlet].adr = nextadr;	/* last plus 1 */
366 }
367 
368 copylet(n, f, type)
369 	FILE *f;
370 {
371 	int ch;
372 	long k;
373 	char hostname[MAXHOSTNAMELEN];
374 
375 	fseek(tmpf, let[n].adr, L_SET);
376 	k = let[n+1].adr - let[n].adr;
377 	while (k-- > 1 && (ch = getc(tmpf)) != '\n')
378 		if (type != ZAP)
379 			putc(ch, f);
380 	switch (type) {
381 
382 	case REMOTE:
383 		gethostname(hostname, sizeof (hostname));
384 		fprintf(f, " remote from %s\n", hostname);
385 		break;
386 
387 	case FORWARD:
388 		fprintf(f, forwmsg);
389 		break;
390 
391 	case ORDINARY:
392 		putc(ch, f);
393 		break;
394 
395 	case ZAP:
396 		break;
397 
398 	default:
399 		panic("Bad letter type %d to copylet.", type);
400 	}
401 	while (k-- > 1) {
402 		ch = getc(tmpf);
403 		putc(ch, f);
404 	}
405 	if (type != ZAP || ch != '\n')
406 		putc(getc(tmpf), f);
407 }
408 
409 isfrom(lp)
410 register char *lp;
411 {
412 	register char *p;
413 
414 	for (p = from; *p; )
415 		if (*lp++ != *p++)
416 			return(0);
417 	return(1);
418 }
419 
420 bulkmail(argc, argv)
421 char **argv;
422 {
423 	char *truename;
424 	int first;
425 	register char *cp;
426 	char *newargv[1000];
427 	register char **ap;
428 	register char **vp;
429 	int dflag;
430 
431 	dflag = 0;
432 	delflg = 0;
433 	if (argc < 1) {
434 		fprintf(stderr, "puke\n");
435 		return;
436 	}
437 	for (vp = argv, ap = newargv + 1; (*ap = *vp++) != 0; ap++)
438 		if (ap[0][0] == '-' && ap[0][1] == 'd')
439 			dflag++;
440 	if (!dflag) {
441 		/* give it to sendmail, rah rah! */
442 		unlink(lettmp);
443 		ap = newargv+1;
444 		if (rmail)
445 			*ap-- = "-s";
446 		*ap = "-sendmail";
447 		setuid(getuid());
448 		execv(SENDMAIL, ap);
449 		perror(SENDMAIL);
450 		exit(EX_UNAVAILABLE);
451 	}
452 
453 	truename = 0;
454 	line[0] = '\0';
455 
456 	/*
457 	 * When we fall out of this, argv[1] should be first name,
458 	 * argc should be number of names + 1.
459 	 */
460 
461 	while (argc > 1 && *argv[1] == '-') {
462 		cp = *++argv;
463 		argc--;
464 		switch (cp[1]) {
465 		case 'r':
466 			if (argc <= 1)
467 				usage();
468 			truename = argv[1];
469 			fgets(line, LSIZE, stdin);
470 			if (strcmpn("From", line, 4) == 0)
471 				line[0] = '\0';
472 			argv++;
473 			argc--;
474 			break;
475 
476 		case 'h':
477 			if (argc <= 1)
478 				usage();
479 			hseqno = atoi(argv[1]);
480 			argv++;
481 			argc--;
482 			break;
483 
484 		case 'd':
485 			break;
486 
487 		default:
488 			usage();
489 		}
490 	}
491 	if (argc <= 1)
492 		usage();
493 	if (truename == 0)
494 		truename = my_name;
495 	time(&iop);
496 	fprintf(tmpf, "%s%s %s", from, truename, ctime(&iop));
497 	iop = ftell(tmpf);
498 	flgf = first = 1;
499 	for (;;) {
500 		if (first) {
501 			first = 0;
502 			if (*line == '\0' && fgets(line, LSIZE, stdin) == NULL)
503 				break;
504 		} else {
505 			if (fgets(line, LSIZE, stdin) == NULL)
506 				break;
507 		}
508 		if (*line == '.' && line[1] == '\n' && isatty(fileno(stdin)))
509 			break;
510 		if (isfrom(line))
511 			putc('>', tmpf);
512 		fputs(line, tmpf);
513 		flgf = 0;
514 	}
515 	putc('\n', tmpf);
516 	nlet = 1;
517 	let[0].adr = 0;
518 	let[1].adr = ftell(tmpf);
519 	if (flgf)
520 		return;
521 	while (--argc > 0)
522 		if (!sendmail(0, *++argv, truename))
523 			error++;
524 	if (error && safefile(dead)) {
525 		setuid(getuid());
526 		malf = fopen(dead, "w");
527 		if (malf == NULL) {
528 			printf("mail: cannot open %s\n", dead);
529 			fclose(tmpf);
530 			return;
531 		}
532 		copylet(0, malf, ZAP);
533 		fclose(malf);
534 		printf("Mail saved in %s\n", dead);
535 	}
536 	fclose(tmpf);
537 }
538 
539 sendrmt(n, name)
540 char *name;
541 {
542 	FILE *rmf, *popen();
543 	register char *p;
544 	char rsys[64], cmd[64];
545 	register pid;
546 	int sts;
547 
548 #ifdef notdef
549 	if (any('^', name)) {
550 		while (p = index(name, '^'))
551 			*p = '!';
552 		if (strncmp(name, "researc", 7)) {
553 			strcpy(rsys, "research");
554 			if (*name != '!')
555 				--name;
556 			goto skip;
557 		}
558 	}
559 #endif
560 	for (p=rsys; *name!='!'; *p++ = *name++)
561 		if (*name=='\0')
562 			return(0);	/* local address, no '!' */
563 	*p = '\0';
564 	if (name[1]=='\0') {
565 		printf("null name\n");
566 		return(0);
567 	}
568 skip:
569 	if ((pid = fork()) == -1) {
570 		fprintf(stderr, "mail: can't create proc for remote\n");
571 		return(0);
572 	}
573 	if (pid) {
574 		while (wait(&sts) != pid) {
575 			if (wait(&sts)==-1)
576 				return(0);
577 		}
578 		return(!sts);
579 	}
580 	setuid(getuid());
581 	if (any('!', name+1))
582 		(void)sprintf(cmd, "uux - %s!rmail \\(%s\\)", rsys, name+1);
583 	else
584 		(void)sprintf(cmd, "uux - %s!rmail %s", rsys, name+1);
585 	if ((rmf=popen(cmd, "w")) == NULL)
586 		exit(1);
587 	copylet(n, rmf, REMOTE);
588 	exit(pclose(rmf) != 0);
589 }
590 
591 usage()
592 {
593 
594 	fprintf(stderr, "Usage: mail [ -f ] people . . .\n");
595 	error = EX_USAGE;
596 	done();
597 }
598 
599 #include <sys/socket.h>
600 #include <netinet/in.h>
601 #include <netdb.h>
602 
603 notifybiff(msg)
604 	char *msg;
605 {
606 	static struct sockaddr_in addr;
607 	static int f = -1;
608 
609 	if (addr.sin_family == 0) {
610 		struct hostent *hp = gethostbyname("localhost");
611 		struct servent *sp = getservbyname("biff", "udp");
612 
613 		if (hp && sp) {
614 			addr.sin_family = hp->h_addrtype;
615 			bcopy(hp->h_addr, &addr.sin_addr, hp->h_length);
616 			addr.sin_port = sp->s_port;
617 		}
618 	}
619 	if (addr.sin_family) {
620 		if (f < 0)
621 			f = socket(AF_INET, SOCK_DGRAM, 0);
622 		if (f >= 0)
623 			sendto(f, msg, strlen(msg)+1, 0, &addr, sizeof (addr));
624 	}
625 }
626 
627 sendmail(n, name, fromaddr)
628 	int n;
629 	char *name, *fromaddr;
630 {
631 	char file[256];
632 	int mask, fd;
633 	struct passwd *pw;
634 #ifdef notdef
635 	struct stat statb;
636 #endif
637 	char buf[128];
638 
639 	if (*name=='!')
640 		name++;
641 	if (any('!', name))
642 		return (sendrmt(n, name));
643 	if ((pw = getpwnam(name)) == NULL) {
644 		printf("mail: can't send to %s\n", name);
645 		return(0);
646 	}
647 	cat(file, maildir, name);
648 #ifdef notdef
649 	if (stat(file, &statb) >= 0 && (statb.st_mode & S_IFMT) == S_IFDIR) {
650 		strcat(file, "/");
651 		strcat(file, name);
652 	}
653 #endif
654 	if (!safefile(file))
655 		return(0);
656 	fd = open(file, O_WRONLY | O_CREAT, MAILMODE);
657 	if (fd >= 0) {
658 		flock(fd, LOCK_EX);
659 		malf = fdopen(fd, "a");
660 	}
661 	if (fd < 0 || malf == NULL) {
662 		close(fd);
663 		printf("mail: %s: cannot append\n", file);
664 		return(0);
665 	}
666 	fchown(fd, pw->pw_uid, pw->pw_gid);
667 	(void)sprintf(buf, "%s@%d\n", name, ftell(malf));
668 	copylet(n, malf, ORDINARY);
669 	fclose(malf);
670 	notifybiff(buf);
671 	return(1);
672 }
673 
674 delex(i)
675 {
676 	if (i != SIGINT) {
677 		setsig(i, SIG_DFL);
678 		sigsetmask(sigblock(0) &~ sigmask(i));
679 	}
680 	putc('\n', stderr);
681 	if (delflg)
682 		longjmp(sjbuf, 1);
683 	if (error == 0)
684 		error = i;
685 	done();
686 }
687 
688 done()
689 {
690 
691 	unlink(lettmp);
692 	exit(error);
693 }
694 
695 cat(to, from1, from2)
696 	char *to, *from1, *from2;
697 {
698 	register char *cp, *dp;
699 
700 	cp = to;
701 	for (dp = from1; *cp = *dp++; cp++)
702 		;
703 	for (dp = from2; *cp++ = *dp++; )
704 		;
705 }
706 
707 /* copy p... into s, update p */
708 char *
709 getarg(s, p)
710 	register char *s, *p;
711 {
712 	while (*p == ' ' || *p == '\t')
713 		p++;
714 	if (*p == '\n' || *p == '\0')
715 		return(NULL);
716 	while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
717 		*s++ = *p++;
718 	*s = '\0';
719 	return(p);
720 }
721 
722 safefile(f)
723 	char *f;
724 {
725 	struct stat statb;
726 
727 	if (lstat(f, &statb) < 0)
728 		return (1);
729 	if (statb.st_nlink != 1 || (statb.st_mode & S_IFMT) == S_IFLNK) {
730 		fprintf(stderr,
731 		    "mail: %s has more than one link or is a symbolic link\n",
732 		    f);
733 		return (0);
734 	}
735 	return (1);
736 }
737 
738 panic(msg, a1, a2, a3)
739 	char *msg;
740 {
741 
742 	fprintf(stderr, "mail: ");
743 	fprintf(stderr, msg, a1, a2, a3);
744 	fprintf(stderr, "\n");
745 	done();
746 }
747