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