pdp11v/usr/src/cmd/mail.c

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

/* @(#)mail.c	1.6 */

/*
 *	Mail to a remote machine will normally use the uux command.
 *	If available, it may be better to send mail via nusend
 *	or usend, although delivery is not as reliable as uux.
 *	Mail may be compiled to take advantage
 *	of these other networks by adding:
 *		#define USE_NUSEND  for nusend
 *	and
 *		#define USE_USEND   for usend.
 *
 *	NOTE:  If either or both defines are specified, that network
 *	will be tried before uux.
 */

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

#define LOCKON	1	/* lock set */
#define LOCKOFF	0	/* lock released */
#define C_NOACCESS	0	/* no access to file */
#define CHILD	0
#define SAME	0

#define CERROR		-1
#define CSUCCESS	0

#define TRUE	1
#define FALSE	0

#define E_FLGE	1	/* exit flge error */
#define E_FILE	2	/* exit file error */
#define E_SPACE	3	/* exit no space */
/*
 * 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		/* maximum size of a line */
#define	MAXLET		300		/* maximum number of letters */
#define FROMLEVELS	20		/* maxium number of forwards */
#define MAXFILENAME	128

#ifndef	MFMODE
#define	MFMODE		0660		/* create mode for `/usr/mail' files */
#endif

#define A_OK		0		/* return value for access */
#define A_EXIST		0		/* access check for existence */
#define A_WRITE		2		/* access check for write permission */
#define A_READ		4		/* access check for read permission */


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

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

FILE	*tmpf, *malf;

char	lettmp[] = "/tmp/maXXXXX";
char	from[] = "From ";
char	TO[] = "To: ";
char	maildir[] = "/usr/mail/";
char	sendto[256];

/*
 * fowrding address
 */
char	*mailfile;
char	maillock[] = ".lock";
char	dead[] = "/dead.letter";
char	rmtbuf[] = "/tmp/marXXXXXX";
#define RMTMSG	" remote from %s\n"
#define FORWMSG	" forwarded by %s\n"
char	frwrd[] = "Forward to ";
char	nospace[] = "mail: no space for temp file\n";
char	*thissys;
char	mbox[] = "/mbox";
char	curlock[50];
char	line[LSIZE];
char	resp[LSIZE];
char	*hmbox;
char	*hmdead;
char	*home;
char	*my_name;
char	*getlogin();
char	*malloc();
char	lfil[50];
char	*ctime();
char	*getenv();
char	*strrchr();
char	*mktemp();

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

long	ftell();
long	iop;

jmp_buf	sjbuf;

#ifdef USE_NUSEND
jmp_buf nusendfail;
int	nusendjmp = FALSE;
#endif

#ifdef USE_USEND
jmp_buf usendfail;
int	usendjmp = FALSE;
#endif

unsigned umsave;
char	*help[] = {
	"q\t\tquit\n",
	"x\t\texit without changing mail\n",
	"p\t\tprint\n",
	"s [file]\tsave (default mbox)\n",
	"w [file]\tsame without header\n",
	"-\t\tprint previous\n",
	"d\t\tdelete\n",
	"+\t\tnext (no delete)\n",
	"m [user]\tmail to user\n",
	"! cmd\t\texecute cmd\n",
	0
};

/*
 *	mail [ + ] [ -irpqet ]  [ -f file ]
 *	mail [ -t ] persons 
 *	rmail [ -t ] persons 
 */
main(argc, argv)
char	**argv;
{
	register i;

	umsave = umask(7);
	setbuf(stdout, malloc(BUFSIZ));
	uname(&utsn);
	thissys = utsn.nodename;

	/* 
	 * Use environment variables
	 * logname is used for from
	 */
	if(((home = getenv("HOME")) == NULL) || (strlen(home) == 0))
		home = ".";
	if (((my_name = getenv("LOGNAME")) == NULL) || (strlen(my_name) == 0))
		my_name = getlogin();
	if ((my_name == NULL) || (strlen(my_name) == 0))
		my_name = getpwuid(geteuid())->pw_name;
	/*
	 * Catch signals for cleanup
	 */
	if(setjmp(sjbuf)) 
		done();
	for (i=SIGINT; i<SIGCLD; i++)
		setsig(i, delete);
	setsig(SIGHUP, done);
	/*
	 * Make temporary file for letter
	 */
	mktemp(lettmp);
	unlink(lettmp);
	if((tmpf = fopen(lettmp, "w")) == NULL){
		fprintf(stderr, "mail: can't open %s for writing\n", lettmp);
		error = E_FILE;
		done();
	}
	/*
	 * Rmail always invoked to send mail
	 */
	if (argv[0][0] != 'r' &&	
		(argc == 1 || argv[1][0] == '-' || argv[1][0] == '+'))
		printmail(argc, argv);
	else
		sendmail(argc, argv);
	done();
}

/*
 * Signal reset
 * signals that are not being ignored will be 
 * caught by function f
 *	i	-> signal number
 *	f	-> signal routine
 * return
 *	rc	-> former signal
 */
int (*setsig(i, f))()
int	i;
int	(*f)();
{
	register int (*rc)();

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

/*
 * Print mail entries
 *	argc	-> argument count
 *	argv	-> arguments
 */
printmail(argc, argv)
char	**argv;
{
	register int c;
	int	flg, i, j, print, aret, stret, goerr = 0;
	char	*p, *getarg();
	char	frwrdbuf[256];
	struct	stat stbuf;
	extern	char *optarg;

	/*
	 * Print in reverse order
	 */
	if (argv[1][0] == '+') {
		forward = 1;
		argc--;
		argv++;
	}
	while((c = getopt(argc, argv, "f:rpqiet")) != EOF)
		switch(c) {

		/*
		 * use file input for mail
		 */
		case 'f':
			flgf = 1;
			mailfile = optarg;
			break;
		/* 
		 * print without prompting
		 */
		case 'p':
			flgp++;
		/* 
		 * terminate on deletes
		 */
		case 'q':
			delflg = 0;
		case 'i':
			break;

		/* 
		 * print by first in, first out order
		 */
		case 'r':
			forward = 1;
			break;
		/*
		 * do  not print mail
 		 */
		case 'e':
			flge = 1;
			break;

		case 't':
			toflg = 1;
			break;
		/*
		 * bad option
		 */
		case '?':
			goerr++;
	}
	if(goerr) {
		fprintf(stderr, "usage: mail [-erpqt] [-f file] [persons]\n");
		error = E_FILE;
		done();

	}
	if (toflg == 1 && argc > 2) {
		argv++;
		sendmail(--argc, argv);
		done();
	}

	/*
	 * create working directory mbox name
	 */
	if((hmbox = malloc(strlen(home) + strlen(mbox) + 1)) == NULL){
		fprintf(stderr, "mail: can't allocate hmbox");
		error = E_FILE;
		return;
	}
	cat(hmbox, home, mbox);
	if(!flgf) {
		if(((mailfile = getenv("MAIL")) == NULL) || (strlen(mailfile) == 0)) {
			if((mailfile = malloc(strlen(maildir) + strlen(my_name) + 1)) == NULL){
				fprintf(stderr, "mail: can't allocate mailfile");
				error = E_FILE;
				return;
			}
			cat(mailfile, maildir, my_name);
		}
	}
	/*
	 * Check accessibility of mail file
	 */
	stret = stat(mailfile, &stbuf);
	if((aret=access(mailfile, A_READ)) == A_OK)
		malf = fopen(mailfile, "r");
	if (stret == CSUCCESS && aret == CERROR) {
		fprintf(stderr, "mail: permission denied!\n");
		error = E_FILE;
		return;
	}else 
	if (flgf && (aret == CERROR || (malf == NULL))) {
		fprintf(stderr, "mail: cannot open %s\n", mailfile);
		error = E_FILE;
		return;
	}else 
	if(aret == CERROR || (malf == NULL) || (stbuf.st_size == 0)) {
		if(!flge) printf("No mail.\n");
		error = E_FLGE;
		return;
	}
	lock(mailfile);
	/*
	 * See if mail is to be forwarded to 
	 * another system
	 */
	if(areforwarding(mailfile)) {
		if(flge) {
			unlock();
			error = E_FLGE;
			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;
	}
	/*
	 * copy mail to temp file and mark each
	 * letter in the let array
	 */
	copymt(malf, tmpf);
	fclose(malf);
	fclose(tmpf);
	unlock();
	if((tmpf = fopen(lettmp, "r")) == NULL) {
		fprintf(stderr, "mail: can't open %s\n", lettmp);
		error = E_FILE;
		return;
	}
	changed = 0;
	print = 1;
	for (i = 0; i < nlet; ) {

		/*
		 * reverse order ?
		 */
		j = forward ? i : nlet - i - 1;
		if( setjmp(sjbuf) == 0 && print != 0 )
				copylet(j, stdout, ORDINARY);
		
		/*
		 * print only
		 */
		if(flgp) {
			i++;
			continue;
		}
		/*
		 * Interactive
		 */
		setjmp(sjbuf);
		printf("? ");
		fflush(stdout);
		if (fgets(resp, sizeof(resp), stdin) == NULL)
			break;
		print = 1;
		switch (resp[0]) {

		default:
			printf("usage\n");

		/*
		 * help
		 */
		case '?':
			print = 0;
			{
				register i;

				for(i=0;help[i];i++)
					printf("%s", help[i]);
			}
			break;
		/*
		 * skip entry
		 */
		case '+':

		case 'n':
		case '\n':
			i++;
		case 'p':
			break;
		case 'x':
			changed = 0;
		case 'q':
			goto donep;
		/*
		 * Previous entry
		 */
		case '^':
		case '-':
			if (--i < 0)
				i = 0;
			break;
		/*
		 * Save in file without header
		 */
		case 'y':
		case 'w':
		/*
		 * Save mail with header
		 */
		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(stderr, "mail: cannot append to %s\n", lfil);
					flg++;
					continue;
				}
				if(aret==2)
					chown(lfil, geteuid(), getgid());
				if (copylet(j, malf, resp[0]=='w'? ZAP: ORDINARY) == FALSE) {
					fprintf(stderr, "mail: cannot save mail\n");
				}
				fclose(malf);
			}
			umask(7);
			if (flg)
				print = 0;
			else {
				let[j].change = 'd';
				changed++;
				i++;
			}
			break;
		/*
		 * Mail letter to someone else
		 */
		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) == FALSE)
					flg++;
			if (flg)
				print = 0;
			else {
				let[j].change = 'd';
				changed++;
				i++;
			}
			break;
		/*
		 * Escape to shell
		 */
		case '!':
			system(resp+1);
			printf("!\n");
			print = 0;
			break;
		/*
		 * Delete an entry
		 */
		case 'd':
			let[j].change = 'd';
			changed++;
			i++;
			if (resp[1] == 'q')
				goto donep;
			break;
		}
	}
	/*
	 * Copy updated mail file back
	 */
   donep:
	if (changed)
		copyback();
}

/*
 * copy temp or whatever back to /usr/mail
 */
copyback()
{
	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);
	/*
	 * Has new mail arrived?
	 */
	if (stbuf.st_size != let[nlet].adr) {
		if((malf = fopen(mailfile, "r")) == NULL) {
			fprintf(stderr, "mail: can't re-read %s\n", mailfile);
			error = E_FILE;
			done();
		}
		fseek(malf, let[nlet].adr, 0);
		fclose(tmpf);
		if((tmpf = fopen(lettmp, "a")) == NULL) {
			fprintf(stderr, "mail: can't re-write %s\n", mailfile);
			error = E_FILE;
			done();
		}

		/*
		 * append new mail
		 * assume only one
		 * new letter
		 */
		while ((c = fgetc(malf)) != EOF)
			if (fputc(c, tmpf) == EOF) {
				fclose(malf);
				fclose(tmpf);
				fprintf(stderr, nospace);
				error = E_SPACE;
				done();
			}
		fclose(malf);
		fclose(tmpf);
		if((tmpf = fopen(lettmp, "r")) == NULL) {
			fprintf(stderr, "mail: can't re-read %s\n", lettmp);
			error = E_FILE;
			done();
		}
		if(nlet == (MAXLET-2)){
			fprintf(stderr, "copyback:Too many letters\n");
			error = E_SPACE;
			done();
		}
		let[++nlet].adr = stbuf.st_size;
		new = 1;
	}
	/*
	 * Copy mail back to mail file
	 */
	if((aret=access(mailfile, A_WRITE)) == A_OK)
		malf = fopen(mailfile, "w");
	if ((malf == NULL) || (aret == CERROR)) {
		fprintf(stderr, "mail: can't rewrite %s\n", mailfile);
		error = E_FILE;
		done();
	}
	n = 0;
	for (i = 0; i < nlet; i++)
		if (let[i].change != 'd') {
			if (copylet(i, malf, ORDINARY) == FALSE) {
				fprintf(stderr, "mail: cannot copy mail back\n");
			}
			n++;
		}
	fclose(malf);
	/*
	 * Empty mailbox?
	 */
	if ((n == 0) && ((stbuf.st_mode & 0777)== MFMODE))
		unlink(mailfile);
	if (new && !flgf)
		printf("new mail arrived\n");
	unlock();
}

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

	nlet = nextadr = 0;
	let[0].adr = 0;
	while (fgets(line, sizeof(line), f1) != NULL) {

		/*
		 * bug nlet should be checked
		 */
		if(line[0] == from[0])
			if (isfrom(line)){
				if(nlet >= (MAXLET-2)){
					fclose(f1);
					fclose(f2);
					fprintf(stderr,"copymt:Too many letters\n");
					error = E_SPACE;
					done();
				}
				let[nlet++].adr = nextadr;
			}
		nextadr += strlen(line);
		if (fputs(line, f2) == EOF) {
			fclose(f1);
			fclose(f2);
			fprintf(stderr, nospace);
			error = E_SPACE;
			done();
		}
	}

	/*
	 * last plus 1
	 */
	let[nlet].adr = nextadr;
}

/*
 * check to see if mail is being forwarded
 *	s	-> mail file
 * returns
 *	TRUE	-> forwarding
 *	FALSE	-> local
 */
areforwarding(s)
char *s;
{
	register char *p;
	register int c;
	FILE *fd;
	char fbuf[256];
	if((fd = fopen(s, "r")) == NULL) 
		return(FALSE);
	fbuf[0] = '\0';
	fread(fbuf, sizeof(frwrd) - 1, 1, fd);
	if(strncmp(fbuf, frwrd, sizeof(frwrd) - 1) == SAME) {
		for(p = sendto; (c = getc(fd)) != EOF && c != '\n';)
			if(c != ' ') 
			*p++ = c;
		*p = '\0';
		fclose(fd);
		return(TRUE);
	}
	fclose(fd);
	return(FALSE);
}

/*
 * copy letter 
 *	ln	-> index into: letter table
 *	f	-> file descrptor to copy file to
 *	type	-> copy type
 */
copylet(ln, f, type) 
register FILE *f;
{
	register int i;
	register char *s;
	char	buf[512], lastc, ch;
	int	k, n, j;
	int	num;

	fseek(tmpf, let[ln].adr, 0);
	k = let[ln+1].adr - let[ln].adr;
	if(k)
		k--;
	for(i=0;i<k;){
		s = buf;
		num = ((k-i) > sizeof(buf))?sizeof(buf):(k-i);
		if((n = read(tmpf->_file, buf, num)) < 0){
		}
		lastc = buf[n-1];
		if(i == 0){
			for(j=0;j<n;j++,s++){
				if(*s == '\n')
					break;
			}
			if(type != ZAP)
				if(write(f->_file,buf,j) == CERROR){
				return(FALSE);
				}
			i += j+1;
			n -= j+1;
			ch = *s++;
			switch(type){
			case REMOTE:
				fprintf(f, RMTMSG, thissys);
				break;
			case ORDINARY:
				fprintf(f, "%c", ch);
				break;
			case FORWARD:
				fprintf(f, FORWMSG, my_name);
				break;
			}
			fflush(f);
		}
		if(write(f->_file, s, n) != n){
			return(FALSE);
		}
		i += n;
	}
	if(type != ZAP || lastc != '\n'){
		read(tmpf->_file, buf, 1);
		write(f->_file, buf, 1);
	}
	return(TRUE);
}

/*
 * check for "from" string
 *	lp	-> string to be checked
 * returns
 *	TRUE	-> match
 *	FALSE	-> no match
 */
isfrom(lp)
register char *lp;

{
	register char *p;

	for (p = from; *p; )
		if (*lp++ != *p++)
			return(FALSE);
	return(TRUE);
}

/*
 * Send mail
 *	argc	-> argument count
 *	argv	-> argument list
 */
sendmail(argc, argv)
char **argv;
{
	int	aret;
	char	**args;
	int	fromflg = 0;
	char	*asctime();
	struct	tm *bp, *localtime();
	char	*tp, *zp;

	/*
	 * Format time
	 */
	time(&iop);
	bp = localtime(&iop);
	tp = asctime(bp);
	zp = tzname[bp->tm_isdst];
	fprintf(tmpf, "%s%s %.16s %.3s %.5s", from, my_name, tp, zp, tp+20);
	/*
	 * Copy to list in mail entry?
	 */
	if (toflg == 1 && argc > 1) {
		aret = argc;
		args = argv;
		fprintf(tmpf,"%s ",TO);
		while(--aret > 0)
			fprintf(tmpf,"%s ",*++args);
		fputs("\n",tmpf);
	}
	iop = ftell(tmpf);
	flgf = 1;
	/*
	 * Read mail message
	 */
	saveint = setsig(SIGINT, savdead);
	fromlevel = 0;
	while (fgets(line, sizeof(line), stdin) != NULL) {
		if (line[0] == '.' && line[1] == '\n')
			break;
		/*
		 * If "from" string is present prepend with
		 * a ">" so it is no longer interpreted as
		 * the last system fowarding the mail.
		 */
		if(line[0] == from[0])
			if (isfrom(line))
				fputs(">", tmpf);
		/*
		 * Find out how many "from" lines
		 */
		if (fromflg == 0 && (strncmp(line, "From", 4) == SAME || strncmp(line, ">From", 5) == SAME))
			fromlevel++;
		else
			fromflg = 1;
		if (fputs(line, tmpf) == EOF) {
			fclose(tmpf);
			fprintf(stderr, nospace);
			error = E_SPACE;
			return;
		}
		flgf = 0;
	}
	setsig(SIGINT, saveint);
	fputs("\n", tmpf);
	/*
	 * In order to use some of the subroutines that
	 * are used to read mail, the let array must be set up
	 */
	nlet = 1;
	let[0].adr = 0;
	let[1].adr = ftell(tmpf);
	fclose(tmpf);
	if (flgf)
		return;
	if((tmpf = fopen(lettmp, "r")) == NULL) {
		fprintf(stderr, "mail: cannot reopen %s for reading\n", lettmp);
		error = E_FILE;
		return;
	}
	/*
	 * Send a copy of the letter to the specified users
	 */
	if (error == 0)
		while (--argc > 0)
			if (send(0, *++argv, 0) == FALSE) 
				error++;
	/*
	 * Save a copy of the letter in dead.letter if
	 * any letters can't be sent
	 */
	if (error) {
		/*
		 * Try to create dead letter in current directory
		 * or in home directory
		 */
		umask(umsave);
		if((aret=legal(&dead[1])))
			malf = fopen(&dead[1], "w");
		if ((malf == NULL) || (aret == 0)) {

			/*
			 * try to create in $HOME
			 */
			if((hmdead = malloc(strlen(home) + strlen(dead) + 1)) == NULL) {
				fprintf(stderr, "mail: can't malloc\n");
				goto out;
			}
			cat(hmdead, home, dead);
			if((aret=legal(hmdead)))
				malf = fopen(hmdead, "w");
			if ((malf == NULL) || (aret == 0)) {
				fprintf(stderr, "mail: cannot create %s\n", &dead[1]);
			out:
				fclose(tmpf);
				error = E_FILE;
				umask(7);
				return;
			}  else {
				chmod(hmdead, 0600);
				printf("Mail saved in %s\n", hmdead);
			}
		} else {
			chmod(&dead[1], 0600);
			printf("Mail saved in %s\n", &dead[1]);
		}
		/*
		 * Copy letter into dead letter box
		 */
		umask(7);
		if (copylet(0, malf, ZAP) == FALSE) {
			fprintf(stderr, "mail: cannot save in dead letter\n");
		}
		fclose(malf);
	}
	fclose(tmpf);
}

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

/*
 * send mail to remote system taking fowarding into account
 *	n	-> index into mail table
 *	name	-> mail destination
 * returns
 *	TRUE	-> sent mail
 *	FALSE	-> can't send mail
 */
sendrmt(n, name)
register char *name;
{
# define NSCCONS	"/usr/nsc/cons/"
	register char *p;
	register local;
	FILE *rmf, *popen();
	char rsys[64], cmd[200];
	char remote[30];

	/*
	 * assume mail is for remote
	 * look for bang to confirm that
	 * assumption
	 */
	local = 0;
	while (*name=='!')
		name++;
	for(p=rsys; *name!='!'; *p++ = *name++)
		if (*name=='\0') {
			local++;
			break;
		}
	*p = '\0';
	if ((!local && *name=='\0') || (local && *rsys=='\0')) {
		fprintf(stderr, "null name\n");
		return(FALSE);
	}
	if (local)
		sprintf(cmd, "mail %s", rsys);
	if (strcmp(thissys, rsys) == SAME) {
		local++;
		sprintf(cmd, "mail %s", name+1);
	}

	/*
	 * send local mail or remote via uux
	 */
	if (!local) {
		if (fromlevel > FROMLEVELS)
			return(FALSE);

#ifdef USE_NUSEND
		/*
		 * If mail can't be sent over NSC network
		 * use uucp.
		 */
		if (setjmp(nusendfail) == 0) {
			nusendjmp = TRUE;
			sprintf(remote, "%s%s", NSCCONS, rsys);
			if (access(remote, A_EXIST) != CERROR) {
				/*
				 * Send mail over NSC network
				 */
				sprintf(cmd, "nusend -d %s -s -e -!'rmail %s' - 2>/dev/null",
					rsys, name);
#ifdef DEBUG
printf("%s\n", cmd);
#endif
				if ((rmf=popen(cmd, "w")) != NULL) {
					copylet(n, rmf, local? FORWARD: REMOTE);
					if (pclose(rmf) == 0) {
						nusendjmp = FALSE;
						return(TRUE);
					}
				}
			}
		}
		nusendjmp = FALSE;
#endif

#ifdef USE_USEND
		if (setjmp(usendfail) == 0) {
			usendjmp = TRUE;
			sprintf(cmd, "usend -s -d%s -uNoLogin -!'rmail %s' - 2>/dev/null",
				rsys, name);
#ifdef DEBUG
printf("%s\n", cmd);
#endif
			if ((rmf=popen(cmd, "w")) != NULL) {
				copylet(n, rmf, local? FORWARD: REMOTE);
				if (pclose(rmf) == 0) {
					usendjmp = FALSE;
					return(TRUE);
				}
			}
		}
		usendjmp = FALSE;
#endif

		/*
		 * Use uux to send mail
		 */
		if (strchr(name+1, '!'))
			sprintf(cmd, "uux - %s!rmail \\(%s\\)", rsys, name+1);
		else
			sprintf(cmd, "uux - %s!rmail %s", rsys, name+1);
	}
#ifdef DEBUG
printf("%s\n", cmd);
#endif
	/*
	 * copy letter to pipe
	 */
	if ((rmf=popen(cmd, "w")) == NULL)
		return(FALSE);
	if (copylet(n, rmf, local? FORWARD: REMOTE) == FALSE) {
		fprintf(stderr, "mail: cannot pipe to mail command\n");
		pclose(rmf);
		return(FALSE);
	}

	/*
	 * check status
	 */
	return(pclose(rmf)==0 ? TRUE : FALSE);
}

/*
 * send letter n to name
 *	n	-> letter number
 *	name	-> mail destination
 *	level	-> depth of recursion for forwarding
 * returns
 *	TRUE	-> mail sent
 *	FALSE	-> can't send mail
 */
send(n, name, level)
int	n;
char	*name;
{
	register char *p;
	char	file[MAXFILENAME];
	struct	passwd	*pwd, *getpwname();

	if(level > 20) {
		fprintf(stderr, "unbounded forwarding\n");
		return(FALSE);
	}
	if (strcmp(name, "-") == SAME)
		return(TRUE);
	/*
	 * See if mail is to be fowarded
	 */
	for(p=name; *p!='!' &&*p!='\0'; p++)
		;
	if (*p == '!')
		return(sendrmt(n, name));
	/*
	 * See if user has specified that mail is to be fowarded
	 */
	cat(file, maildir, name);
	if(areforwarding(file))
		return(send(n, sendto, level+1));
	/*
	 * see if user exists on this system
	 */
	setpwent();	
	if((pwd = getpwnam(name)) == NULL){
		fprintf(stderr, "mail: can't send to %s\n", name);
		return(FALSE);
	}
	cat(file, maildir, name);
	lock(file);
	/*
	 * If mail file does not exist create it
	 * with the correct uid and gid
	 */
	if(access(file, A_EXIST) == CERROR) {
		umask(0);
		close(creat(file, MFMODE));
		umask(7);
		chown(file, pwd->pw_uid, getegid());
	}
	/*
	 * Append letter to mail box
	 */
	if((malf = fopen(file, "a")) == NULL){
		fprintf(stderr, "mail: cannot append to %s\n", file);
		unlock();
		return(FALSE);
	}
	if (copylet(n, malf, ORDINARY) == FALSE) {
		fprintf(stderr, "mail: cannot append to %s\n", file);
		unlock();
		return(FALSE);
	}
	fclose(malf);
	unlock();
	return(TRUE);
}

/*
 * signal catching routine
 * reset signals on quits and interupts
 * exit on other signals
 *	i	-> signal #
 */
delete(i)
register int i;
{
	setsig(i, delete);

#ifdef USE_NUSEND
	if (i == SIGPIPE && nusendjmp == TRUE)
		longjmp(nusendfail, 1);
#endif

#ifdef USE_USEND
	if (i == SIGPIPE && usendjmp == TRUE)
		longjmp(usendfail, 1);
#endif

	if(i>SIGQUIT){
		fprintf(stderr, "mail: error signal %d\n", i);
	}else
		fprintf(stderr, "\n");
	if(delflg && (i==SIGINT || i==SIGQUIT))
		longjmp(sjbuf, 1);
	done();
}

/*
 * clean up lock files and exit
 */
done()
{
	unlock();
	unlink(lettmp);
	unlink(rmtbuf);
	exit(error);
}

/*
 * create mail lock file
 *	file	-> lock file name
 * returns:
 *	none
 */
lock(file)
char *file;
{
	register int f, i;

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

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

/*
 * concatenate from1 and from2 to to
 *	to	-> destination string
 *	from1	-> source string
 *	from2	-> source string
 * return:
 *	none
 */
cat(to, from1, from2)
register char *to, *from1, *from2;
{

	for (; *from1; )
		*to++ = *from1++;
	for (; *from2; )
		*to++ = *from2++;
	*to = '\0';
}

/*
 * get next token
 *	p	-> string to be searched
 *	s	-> area to return token
 * returns:
 *	p	-> updated string pointer
 *	s	-> token
 *	NULL	-> no token
 */
char *getarg(s, 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);
}

/*
 * check existence of file
 *	file	-> file to check
 * returns:
 *	0	-> exists unwriteable
 *	1	-> exists writeable
 *	2	-> does not exist
 */
legal(file)
register char *file;
{
	register char *sp;
	char dfile[MAXFILENAME];

	/*
	 * If file does not exist then
	 * try "." if file name has no "/"
	 * For file names that have a "/", try check
	 * for existence of previous directory
	 */
	if(access(file, A_EXIST) == A_OK)
		if(access(file, A_WRITE) == A_OK)
			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, A_WRITE) == CERROR) 
			return(0);
		return(2);
	}
}

/*
 * invok shell to execute command waiting for
 * command to terminate
 *	s	-> command string
 * return:
 *	status	-> command exit status
 */
system(s)
char *s;
{
	register int pid, w;
	int status;
	int (*istat)(), (*qstat)();

	/*
	 * Spawn the shell to execute command, however,
	 * since the mail command runs setgid mode
	 * reset the effective group id to the real
	 * group id so that the command does not
	 * acquire any special privileges
	 */
	if ((pid = fork()) == CHILD) {
		setuid(getuid());
		setgid(getgid());
		execl("/bin/sh", "sh", "-c", s, NULL);
		_exit(127);
	}

	/*
	 * Parent temporarily ignores signals so it 
	 * will remain around for command to finish
	 */
	istat = signal(SIGINT, SIG_IGN);
	qstat = signal(SIGQUIT, SIG_IGN);
	while ((w = wait(&status)) != pid && w != CERROR)
		;
	if (w == CERROR)
		status = CERROR;
	signal(SIGINT, istat);
	signal(SIGQUIT, qstat);
	return(status);
}