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