SysIII/usr/src/cmd/mail.c

Compare this file to the similar file:
Show the results in this format:

/*
**	mail [ person ]
**	mail -f file
*/

#include	<stdio.h>
#include	<pwd.h>
#include	<utmp.h>
#include	<signal.h>
#include	<sys/types.h>
#include	<sys/stat.h>
#include	<setjmp.h>
#include	<sys/utsname.h>

/*copylet flags */
#define	REMOTE		1		/* remote mail, add rmtmsg */
#define ORDINARY	2
#define ZAP		3		/* zap header and trailing empty line */
#define FORWARD		4
#define	LSIZE		256
#define	MAXLET		300		/* maximum number of letters */
#ifndef	MFMODE
#define	MFMODE		0664		/* create mode for `/usr/mail' files */
#endif

struct	let	{
	long	adr;
	char	change;
} let[MAXLET];

struct	passwd	*getpwuid(), getpwent();
struct	utsname utsn;

char	lettmp[] = "/tmp/maXXXXX";
char	from[] = "From ";
char	TO[] = "To: ";
char	maildir[] = "/usr/mail/";
char	sendto[256];
char	*mailfile;
char	maillock[] = ".lock";
char	dead[] = "dead.letter";
char	rmtbuf[] = "/tmp/marXXXXXX";
char	*rmtmsg = " remote from %s\n";
char	*forwmsg = " forwarded by %s\n";
char	frwrd[] = "Forward to ";
char	*thissys;
char	mbox[] = "/mbox";
char	curlock[50];
char	line[LSIZE];
char	resp[LSIZE];
char	*hmbox;
char	*home;
char	*email;
char	*my_name;
char	*getlogin();
char	*malloc();
char	lfil[50];
char	*ctime();
char	*getenv();
char	*strrchr();

FILE	*tmpf;
FILE	*malf;
FILE	*rmtf;

int	error;
int	nlet	= 0;
int	locked;
int	changed;
int	forward;
int	delete();
int	flgf = 0;
int	flgp = 0;
int	flge = 0;
int	delflg = 1;
int	savdead();
int	(*saveint)();
int	(*setsig())();

long	ftell();
long	iop;

jmp_buf	sjbuf;

unsigned umsave;

main(argc, argv)
char **argv;
{
	register i;

	umsave = umask(0);
	setbuf(stdout, malloc(BUFSIZ));
	mktemp(lettmp);
	unlink(lettmp);
	uname(&utsn);
	thissys = utsn.nodename;
	my_name = getenv("LOGNAME");
	if ((my_name == NULL) || (strlen(my_name) == 0))
		my_name = getlogin();
	if ((my_name == NULL) || (strlen(my_name) == 0))
		my_name = getpwuid(geteuid())->pw_name;
	if(setjmp(sjbuf)) done();
	for (i=2; i<18; i++)
		setsig(i, delete);
	setsig(1, done);
	tmpf = fopen(lettmp, "w");
	if (tmpf == NULL) {
		fprintf(stderr, "mail: cannot open %s for writing\n", lettmp);
		error = 2;
		done();
	}
	if (argv[0][0] != 'r' &&	/* no favors for rmail */
	   (argc == 1 || argv[1][0] == '-' || argv[1][0] == '+'))
		printmail(argc, argv);
	else
		sendmail(argc, argv);
	done();
}

int (*setsig(i, f))()
int i;
int (*f)();
{
	register int (*rc)();

	if((rc=signal(i, SIG_IGN))!=SIG_IGN)
		signal(i, f);
	return(rc);
}

printmail(argc, argv)
char **argv;
{
	int	flg, i, j, print, aret, stret, goerr = 0;
	char	c, *p, *getarg();
	char	frwrdbuf[256];
	struct	stat stbuf;
	extern	char *optarg;

	if (argv[1][0] == '+') {
		forward = 1;
		argc--;
		argv++;
	}
	while((c=getopt(argc, argv, "f:rpqie")) != EOF) switch(c) {
		case 'f':
			flgf = 1;
			mailfile = optarg;
			break;
		case 'p':
			flgp++;
		case 'q':
			delflg = 0;
		case 'i':
			break;
		case 'r':
			forward = 1;
			break;
		case 'e':
			flge = 1;
			break;
		case '?':
			goerr++;
	}
	if(goerr) {
		fprintf(stderr, "usage: mail [-rpq] [-f file] [persons]\n");
		error = 2;
		done();
	}
	home = getenv("HOME");
	if((home == NULL) || (strlen(home) == 0))
		home = ".";
	hmbox = malloc(strlen(home) + strlen(mbox) + 1);
	cat(hmbox, home, mbox);
	if(!flgf) {
		mailfile = getenv("MAIL");
		if((mailfile == NULL) || (strlen(mailfile) == 0)) {
			mailfile = malloc(strlen(maildir) + strlen(my_name) + 1);
			cat(mailfile, maildir, my_name);
		}
	}
	stret = stat(mailfile, &stbuf);
	if((aret=access(mailfile, 4)) == 0)
		malf = fopen(mailfile, "r");
	if (!stret && aret) {
		fprintf(stderr, "mail: permission denied!\n");
		error = 2;
		return;
	}
	else if (flgf && (aret || (malf == NULL))) {
		fprintf(stderr, "mail: cannot open %s\n", mailfile);
		error = 2;
		return;
	}
	else if(aret || (malf == NULL) || (stbuf.st_size == 0)) {
		if(!flge) fprintf(stdout, "No mail.\n");
		error = 1;
		return;
	}
	lock(mailfile);
	if(areforwarding(mailfile)) {
		if(flge) {
			unlock();
			error = 1;
			return;
		}
		printf("Your mail is being forwarded to ");
		fseek(malf, (long)(sizeof(frwrd) - 1), 0);
		fgets(frwrdbuf, sizeof(frwrdbuf), malf);
		printf("%s", frwrdbuf);
		if(getc(malf) != EOF)
			printf("and your mailbox contains extra stuff\n");
		unlock();
		return;
	}
	if(flge) {
		unlock();
		return;
	}
	copymt(malf, tmpf);
	fclose(malf);
	fclose(tmpf);
	unlock();
	tmpf = fopen(lettmp, "r");
	changed = 0;
	print = 1;
	for (i = 0; i < nlet; ) {
		j = forward ? i : nlet - i - 1;
		if( setjmp(sjbuf) == 0 && print != 0 )
				copylet(j, stdout, ORDINARY);
		if(flgp) {
			i++;
			continue;
		}
		setjmp(sjbuf);
		fprintf(stdout, "? ");
		fflush(stdout);
		if (fgets(resp, LSIZE, stdin) == NULL)
			break;
		switch (resp[0]) {

		default:
			fprintf(stderr, "usage\n");
		case '?':
			print = 0;
			fprintf(stderr, "q\t\tquit\n");
			fprintf(stderr, "x\t\texit without changing mail\n");
			fprintf(stderr, "p\t\tprint\n");
			fprintf(stderr, "s [file]\tsave (default mbox)\n");
			fprintf(stderr, "w [file]\tsame without header\n");
			fprintf(stderr, "-\t\tprint previous\n");
			fprintf(stderr, "d\t\tdelete\n");
			fprintf(stderr, "+\t\tnext (no delete)\n");
			fprintf(stderr, "m [user]\tmail to user\n");
			fprintf(stderr, "! cmd\t\texecute cmd\n");
			break;

		case '+':
		case 'n':
		case '\n':
			i++;
		case 'p':
			print = 1;
			break;
		case 'x':
			changed = 0;
		case 'q':
			goto donep;
		case '^':
		case '-':
			if (--i < 0)
				i = 0;
			print = 1;
			break;
		case 'y':
		case 'w':
		case 's':
			if (resp[1] == '\n' || resp[1] == '\0')
				cat(resp+1, hmbox, "");
			else if(resp[1] != ' ') {
				printf("invalid command\n");
				print = 0;
				continue;
			}
			umask(umsave);
			flg = 0;
			for (p = resp+1; (p = getarg(lfil, p)) != NULL; ) {
				if((aret=legal(lfil)))
					malf = fopen(lfil, "a");
				if ((malf == NULL) || (aret == 0)) {
					fprintf(stdout, "mail: cannot append to %s\n", lfil);
					flg++;
					continue;
				}
				if(aret==2)
					chown(lfil, geteuid(), getgid());
				copylet(j, malf, resp[0]=='w'? ZAP: ORDINARY);
				fclose(malf);
			}
			umask(0);
			if (flg)
				print = 0;
			else {
				let[j].change = 'd';
				changed++;
				i++;
			}
			break;
		case 'm':
			if (resp[1] == '\n' || resp[1] == '\0') {
				i++;
				continue;
			}
			if (resp[1] != ' ') {
				printf("invalid command\n");
				print = 0;
				continue;
			}
			flg = 0;
			for (p = resp+1; (p = getarg(lfil, p)) != NULL; )
				if (!sendrmt(j, lfil))	/* couldn't send it */
					flg++;
			if (flg)
				print = 0;
			else {
				let[j].change = 'd';
				changed++;
				i++;
			}
			break;
		case '!':
			system(resp+1);
			printf("!\n");
			print = 0;
			break;
		case 'd':
			let[j].change = 'd';
			changed++;
			i++;
			if (resp[1] == 'q')
				goto donep;
			print = 1;
			break;
		}
	}
   donep:
	if (changed)
		copyback();
}

copyback()	/* copy temp or whatever back to /usr/mail */
{
	register i, n, c;
	int new = 0, aret;
	struct stat stbuf;

	signal(SIGINT, SIG_IGN);
	signal(SIGHUP, SIG_IGN);
	signal(SIGQUIT, SIG_IGN);
	lock(mailfile);
	stat(mailfile, &stbuf);
	if (stbuf.st_size != let[nlet].adr) {	/* new mail has arrived */
		malf = fopen(mailfile, "r");
		if (malf == NULL) {
			fprintf(stdout, "mail: can't re-read %s\n", mailfile);
			error = 2;
			done();
		}
		fseek(malf, let[nlet].adr, 0);
		fclose(tmpf);
		tmpf = fopen(lettmp, "a");
		fseek(tmpf, let[nlet].adr, 0);
		while ((c = fgetc(malf)) != EOF)
			fputc(c, tmpf);
		fclose(malf);
		fclose(tmpf);
		tmpf = fopen(lettmp, "r");
		let[++nlet].adr = stbuf.st_size;
		new = 1;
	}
	if((aret=access(mailfile, 2)) == 0)
		malf = fopen(mailfile, "w");
	if ((malf == NULL) || (aret)) {
		fprintf(stderr, "mail: can't rewrite %s\n", mailfile);
		error = 2;
		done();
	}
	n = 0;
	for (i = 0; i < nlet; i++)
		if (let[i].change != 'd') {
			copylet(i, malf, ORDINARY);
			n++;
		}
	fclose(malf);
	if ((n == 0) && ((stbuf.st_mode & 0777)== MFMODE)) /* empty mailbox */
		unlink(mailfile);
	if (new && !flgf)
		fprintf(stdout, "new mail arrived\n");
	unlock();
}

copymt(f1, f2)	/* copy mail (f1) to temp (f2) */
FILE *f1, *f2;
{
	long nextadr;

	nlet = nextadr = 0;
	let[0].adr = 0;
	while (fgets(line, LSIZE, f1) != NULL) {
		if (isfrom(line))
			let[nlet++].adr = nextadr;
		nextadr += strlen(line);
		fputs(line, f2);
	}
	let[nlet].adr = nextadr;	/* last plus 1 */
}

areforwarding(s) char *s;
{	FILE *fd;
	char fbuf[256], *p;
	int c;
	fd = fopen(s, "r");
	if(fd == NULL)
		return(0);
	fread(fbuf, sizeof(frwrd) - 1, 1, fd);
	if(strncmp(fbuf, frwrd, sizeof(frwrd) - 1) == 0) {
		for(p = sendto; (c = getc(fd)) != EOF && c != '\n';)
			if(c != ' ') *p++ = c;
		*p = 0;
		fclose(fd);
		return(1);
	}
	fclose(fd);
	return(0);
}

copylet(n, f, type) FILE *f;
{	int ch, k;
	fseek(tmpf, let[n].adr, 0);
	k = let[n+1].adr - let[n].adr;
	while(k-- > 1 && (ch=fgetc(tmpf))!='\n')
		if(type!=ZAP)
			fputc(ch,f);
	if(type==REMOTE)
		fprintf(f, rmtmsg, thissys);
	else if(type==FORWARD)
		fprintf(f, forwmsg, my_name);
	else if(type==ORDINARY)
		fputc(ch,f);
	while(k-->1)
		fputc(ch=fgetc(tmpf), f);
	if(type!=ZAP || ch!= '\n')
		fputc(fgetc(tmpf), f);
}

isfrom(lp)
register char *lp;
{
	register char *p;

	for (p = from; *p; )
		if (*lp++ != *p++)
			return(0);
	return(1);
}

sendmail(argc, argv)
char **argv;
{

	int	aret;
	char	**args;
	time(&iop);
	fprintf(tmpf, "%s%s %s", from, my_name, ctime(&iop));
	if(argc > 2) {		/* send Copy to message */
		aret = argc;
		args = argv;
		fprintf(tmpf,"%s ",TO);
		while(--aret > 0)
			fprintf(tmpf,"%s ",*++args);
		fputs("\n",tmpf);
	}
	iop = ftell(tmpf);
	flgf = 1;
	saveint = setsig(SIGINT, savdead);
	while (fgets(line, LSIZE, stdin) != NULL) {
		if (line[0] == '.' && line[1] == '\n')
			break;
		if (isfrom(line))
			fputs(">", tmpf);
		fputs(line, tmpf);
		flgf = 0;
	}
	setsig(SIGINT, saveint);
	fputs("\n", tmpf);
	nlet = 1;
	let[0].adr = 0;
	let[1].adr = ftell(tmpf);
	fclose(tmpf);
	if (flgf)
		return;
	tmpf = fopen(lettmp, "r");
	if (tmpf == NULL) {
		fprintf(stderr, "mail: cannot reopen %s for reading\n", lettmp);
		error = 2;
		return;
	}
	if (error == 0)
		while (--argc > 0)
			if (!send(0, *++argv, 0))	/* couldn't send to him */
				error++;
	if (error) {
		umask(umsave);
		if((aret=legal(dead)))
			malf = fopen(dead, "w");
		if ((malf == NULL) || (aret == 0)) {
			fprintf(stdout, "mail: cannot create %s\n", dead);
			fclose(tmpf);
			error = 2;
			umask(0);
			return;
		}
		umask(0);
		copylet(0, malf, ZAP);
		fclose(malf);
		fprintf(stdout, "Mail saved in %s\n", dead);
	}
	fclose(tmpf);
}

savdead()
{
	setsig(SIGINT, saveint);
	error++;
}

sendrmt(n, name)
char *name;
{
	FILE *rmf, *popen();
	register char *p;
	char rsys[64], cmd[64];
	register local;

	local = 0;
	if (*name=='!')
		name++;
	for(p=rsys; *name!='!'; *p++ = *name++)
		if (*name=='\0') {
			local++;
			break;
		}
	*p = '\0';
	if ((!local && *name=='\0') || (local && *rsys=='\0')) {
		fprintf(stdout, "null name\n");
		return(0);
	}
	if (local)
		sprintf(cmd, "mail %s", rsys);
	else {
		if (strchr(name+1, '!'))
			sprintf(cmd, "uux - %s!rmail \\(%s\\)", rsys, name+1);
		else
			sprintf(cmd, "uux - %s!rmail %s", rsys, name+1);
	}
	if ((rmf=popen(cmd, "w")) == NULL)
		return(0);
	copylet(n, rmf, local? FORWARD: REMOTE);
	return(pclose(rmf)==0 ? 1 : 0);
}

send(n, name, level)	/* send letter n to name */
int n;
char *name;
{
	char	file[50], *p;
	struct	passwd	*pwd, *getpwname();

	if(level > 20) {
		fprintf(stdout, "unbounded forwarding\n");
		return(0);
	}
	if (strcmp(name, "-") == 0)
		return(1);
	for(p=name; *p!='!' &&*p!='\0'; p++)
		;
	if (*p == '!')
		return(sendrmt(n, name));
	cat(file, maildir, name);
	if(areforwarding(file))
		return(send(n, sendto, level+1));
	setpwent();	/* rewind password file -- dumb design */
	pwd = getpwnam(name);
	if (pwd == NULL) {
		fprintf(stdout, "mail: can't send to %s\n", name);
		return(0);
	}
	cat(file, maildir, name);
	lock(file);
	if(access(file, 0) < 0) {
		close(creat(file, MFMODE));
		chown(file, pwd->pw_uid, getegid());
	}
	malf = fopen(file, "a");
	if (malf == NULL) {
		fprintf(stdout, "mail: cannot append to %s\n", file);
		unlock();
		return(0);
	}
	copylet(n, malf, ORDINARY);
	fclose(malf);
	unlock();
	return(1);
}

delete(i)
{
	setsig(i, delete);
	if(i>3)
		fprintf(stderr, "mail: error signal %d\n", i);
	else
		fprintf(stderr, "\n");
	if(delflg)
		longjmp(sjbuf, 1);
	done();
}

done()
{
	unlock();
	unlink(lettmp);
	unlink(rmtbuf);
	exit(error);
}

lock(file)
char *file;
{
	int f, i;

	if (locked)
		return;
	cat(curlock, file, maillock);
	for (i=0; i<10; i++) {
		f = creat(curlock, 0);
		if (f >= 0) {
			close(f);
			locked = 1;
			return;
		}
		sleep(2);
	}
	fprintf(stderr, "mail: %s not creatable after %d tries\n", curlock, i);
	error = 2;
	done();
}

unlock()
{
	unlink(curlock);
	locked = 0;
}

cat(to, from1, from2)
char *to, *from1, *from2;
{
	int i, j;

	j = 0;
	for (i=0; from1[i]; i++)
		to[j++] = from1[i];
	for (i=0; from2[i]; i++)
		to[j++] = from2[i];
	to[j] = 0;
}

char *getarg(s, p)	/* copy p... into s, update p */
register char *s, *p;
{
	while (*p == ' ' || *p == '\t')
		p++;
	if (*p == '\n' || *p == '\0')
		return(NULL);
	while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
		*s++ = *p++;
	*s = '\0';
	return(p);
}

legal(file)
char *file;
{
	char	*sp, dfile[100];
	if(!access(file, 0))
		if(!access(file, 2))
			return(1);
		else	return(0);
	else {
		if((sp=strrchr(file, '/')) == NULL)
			cat(dfile, ".", "");
		else {
			strncpy(dfile, file, sp - file);
			dfile[sp - file] = '\0';
		}
		if(access(dfile, 2)) return(0);
		return(2);
	}
}

system(s)
char *s;
{
	int status, pid, w;
	register int (*istat)(), (*qstat)();

	if ((pid = fork()) == 0) {
		setgid(getgid());
		execl("/bin/sh", "sh", "-c", s, 0);
		_exit(127);
	}
	istat = signal(SIGINT, SIG_IGN);
	qstat = signal(SIGQUIT, SIG_IGN);
	while ((w = wait(&status)) != pid && w != -1)
		;
	if (w == -1)
		status = -1;
	signal(SIGINT, istat);
	signal(SIGQUIT, qstat);
	return(status);
}