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