/* binmail - the dumb mail handling program.     Author: Peter S. Housel */

/*
 * binmail - 	standard UNIX mail reading program.
 *
 * Usage:	mail [-tv] user [...]
 *		mail [-epqr] [-f file]
 *
 * Version:	1.8	04/28/90
 *
 * Authors:	Peter S. Housel
 *		Fred van Kempen
 *
 * Revisions:
 *
 * 	08/16/88 1.0	Peter S. Housel		Initial release.
 *	01/06/89 1.1	Peter S. Housel
 *	08/03/89 1.2	Peter S. Housel
 *	11/09/89 1.3	Fred van Kempen		Edited for the MSS.
 *						Implemented -e option.
 *						Implemented -t option.
 *						Hacked the MAILER code.
 *	12/04/89 1.4	Fred van Kempen		Fixed all known bugs.
 *	12/16/89 1.5	Fred van Kempen		Cleanup (lint).
 *	12/30/89 1.6	Fred van Kempen		Cleanup for POSIX (MINIX 1.5)
 *	02/17/90 1.7	Fred van Kempen		Cleanup for release.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <pwd.h>
#include <setjmp.h>
#include <signal.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <stdio.h>


#ifdef _UNIX
#	define LOCKNAME	   "/usr/mail/%s.lock"
#	define DROPNAME    "/usr/mail/%s"
#	define MAILER	   "/usr/bin/mailx"	/* "smart" mailer" */
#endif

#ifdef _MINIX
#	define LOCKNAME	   "/usr/spool/mail/%s.lock"
#	define DROPNAME    "/usr/spool/mail/%s"
#	define MAILER	   "/usr/bin/mailx"	/* "smart" mailer" */
#endif

#define DEADLETTER	"dead.letter"		/* emergency file */
#define SHELL		"/bin/sh"
#define MBOX		"mbox"
#define PROMPT		"? "
#define LOCKWAIT	  5		/* seconds to wait after collision */
#define LOCKTRIES	  4			/* maximum # of collisions */
#define PATHLEN		 80
#define MAXRCPT		100			/* maximum # of recipients */
#define LINELEN		512
#define UNREAD		  1			/* 'not read yet' status */
#define DELETED		  2			/* 'deleted' status */
#define READ		  3			/* 'has been read' status */
#define FALSE		  0
#define TRUE		  1


typedef struct _letter {
  struct _letter *prev, *next;		/* linked letter list */
  int status;				/* letter status */
  off_t location;			/* location within mailbox file */
} LET;
#define NIL_LET (LET *)NULL


static char *Version = "@(#) binmail 1.8 (04/28/90)";


char tempname[PATHLEN] = "/tmp/mailXXXXXX";	/* temporary file */
char mailbox[PATHLEN];			/* user's mailbox/maildrop */
jmp_buf printjump;			/* for quitting out of letters */
LET *firstlet, *lastlet;		/* letter pointers */
FILE *boxfp = NULL;			/* mailbox file */
unsigned oldmask;			/* saved umask() */
int sayall = FALSE;			/* add list of addressees */
int usemailer = TRUE;			/* use MAILER to deliver (if any) */
int printmode = FALSE;			/* print-and-exit mode */
int quitmode = FALSE;			/* take interrupts */
int reversemode = FALSE;		/* print mailbox in reverse order */
int usedrop = TRUE;			/* read the maildrop (no -f given) */
int verbose = FALSE;			/* pass "-v" flag on to mailer */
int needupdate = FALSE;			/* need to update mailbox */
int old_uid, old_gid;			/* for security-reasons */


extern int getopt(), optind;			/* from getopt(3) */
extern char *optarg;

extern FILE *freopen(), *fdopen();
extern struct passwd *getpwnam(), *getpwuid();
extern char *getenv();

extern int errno;				/* from libc.a */


int onint()
{
  longjmp(printjump, 1);
}


/*
 * Get the caller's user name.
 */
char *whoami()
{
  struct passwd *pw;

  if ((pw = getpwuid(old_uid)) == (struct passwd *)NULL) return("nobody");
    else return(pw->pw_name);
}


/*
 * Strip off the directories from a pathname.
 */
char *basename(name)
char *name;
{
  char *p;

  if ((p = strrchr(name, '/')) == (char *)NULL) return(name);
    else return(++p);
}


/*
 * Show the contents of letter 'let' onto file 'fp'.
 */
void printlet(let, tofp)
LET *let;
register FILE *tofp;
{
  off_t current, limit;
  register int c;

  fseek(boxfp, (current = let->location), SEEK_SET);
  limit = (let->next != NIL_LET) ? let->next->location : -1L;

  while (current != limit && (c = getc(boxfp)) != EOF) {
	fputc(c, tofp);
	++current;
  }
}


/*
 * Execute a command.
 * Do this as a child process, so we can turn off
 * the SetUID ROOT bit first...
 */
void doshell(command)
char *command;
{
  int waitstat, pid;
  char *shell;

  if ((shell = getenv("SHELL")) == (char *)NULL) shell = SHELL;

  if ((pid = fork()) < 0) {
	perror("mail: couldn't fork");
	return;
  } else if (pid != 0) {	/* parent */
	wait(&waitstat);
	return;
  } else {			/* child */
	  setgid(old_gid);
	  setuid(old_uid);
	  umask(oldmask);

	  execl(shell, shell, "-c", command, (char *)NULL);
	  fprintf(stderr, "can't exec shell");
	  exit(127);
  }
}


/*
 * Check if we may perform operation 'mode' on
 * file 'name'. System Security!
 * If the error is 'ENOENT', then test the parent
 * directory for the desired access.
 */
int allowed(name, mode)
char *name;			/* name of file to be checked */
unsigned short mode;		/* mode to check (R=4, W=2, X=1) */
{
  char abuf[1024];		/* temp. buf for filename */
  struct stat stb;
  char *p;

  /* Is this 'The Master' calling? */
  if (old_uid == 0) return(TRUE);

  if (stat(name, &stb) < 0) {
	if (errno == ENOENT) {			/* file does not exist */
		strcpy(abuf, name);		/* so check its parent dir */
		p = strrchr(abuf, '/');	
		if (p == (char *)NULL)		/* plain filename, */
			getcwd(abuf, 1023);	/* get current dir */
		  else *p = '\0';		/* strip 'file' part */
		if (stat(abuf, &stb) < 0) return(FALSE);	/* error? */
	} else return(FALSE);			/* it exists, other error! */
  }

  /* We now have the status of the file or its parent dir. */
  if (stb.st_uid == old_uid) {			/* we are owner! */
	if ((stb.st_mode >> 6) & mode)
				 return(TRUE);	/* OK, we may do it. */
  	  else return(FALSE);			/* Alas... */
  } else if (stb.st_uid == old_gid) {		/* are we the same group? */
	if ((stb.st_mode >>3) & mode)
				 return(TRUE);	/* OK, we may do it. */
  	  else return(FALSE);			/* Alas... */
  } else if (stb.st_mode & mode)		/* we are 'others' */
				 return(TRUE);	/* OK, we may do it. */
  return(FALSE);				/* Alas... */
}


/*
 * 'stdin' isn't rewindable. Make a temp file that is.
 * Note that if one wanted to catch SIGINT and write a
 * '~/dead.letter' for interactive mails, this might be
 * the place to do it (though the case where a MAILER is
 * being used would also need to be handled).
 */
FILE *makerewindable(void)
{
  register FILE *tempfp;		/* temp file used for copy */
  register int c;			/* character being copied */
  register int state;			/* ".\n" detection state */

  if ((tempfp = fopen(tempname, "w")) == (FILE *)NULL) {
	fprintf(stderr, "mail: can't create temporary file");
	return((FILE *)NULL);
  }

  /*
   * Here we copy until we reach the end of the letter (end
   * of file or a line containing only a '.'), painstakingly
   * avoiding setting a line length limit.
   */
  state = '\n';

  while ((c = getc(stdin)) != EOF) switch(state) {
	case '\n':
		if (c == '.') state = '.';
		  else {
			if (c != '\n') state = '\0';
			fputc(c, tempfp);
		}
		break;
	case '.':
		if (c == '\n') goto done1;
		state = '\0';
		fputc('.', tempfp);
		fputc(c, tempfp);
		break;
	default:
		state = (c == '\n') ? '\n' : '\0';
		fputc(c, tempfp);
  }

done1:

  if (ferror(tempfp) || fclose(tempfp)) {
	fprintf(stderr, "mail: couldn't copy letter to temporary file\n");
	return((FILE *)NULL);
  }
  tempfp = freopen(tempname, "r", stdin);
  unlink(tempname);	/* unlink name; file lingers on in limbo */
  return(tempfp);
}


/*
 * Copy a message from file 'fromfp' to 'tofp'.
 */
copy(fromfp, tofp)
FILE *fromfp, *tofp;
{
  static char *postmark = "From ";
  register char *p, *q;		/* fast scanners */
  register int c;		/* character being copied */
  register int state;		/* ".\n" and postmark detection state */
  int blankline = FALSE;	/* was most recently line a blank? */

  /*
   * Here we copy until we reach the end of the letter (end of file
   * or a line containing only a '.'). Postmarks (lines beginning
   * with "From ") are copied with a ">" prepended. Here we also
   * complicate things by not setting a line limit.
   */
  state = '\n';
  p = postmark;

  while ((c = getc(fromfp)) != EOF) {
	switch(state) {
		case '\n':
			if (c == '.') state = '.';	/* '.' at BOL */
			  else if (c == *p) {	/* start of postmark */
					++p;
					state = 'P';
			} else {			/* anything else */
				if (c == '\n') blankline = TRUE;
				  else {
					state = '\0';
					blankline = FALSE;
				}
				fputc(c, tofp);
			}
			break;
		case '.':
			if (c == '\n') goto done2;
			state = '\0';
			fputc('.', tofp);
			fputc(c, tofp);
			break;
		case 'P':
			if (c == *p) {
				/* Successfully reached end of PM. */
				if (*++p == '\0') {
					p = postmark;
					fputc('>', tofp);
					fputs(postmark, tofp);
					state = '\0';
					break;
				}
				break;		/* not there yet */
			}
			state = (c == '\n') ? '\n' : '\0';
			for (q = postmark; q < p; ++q)
						fputc(*q, tofp);
			fputc(c, tofp);
			blankline = FALSE;
			p = postmark;
			break;
		default:
			state = ('\n' == c) ? '\n' : '\0';
			fputc(c, tofp);
	}
  }
  if (state != '\n') fputc('\n', tofp);

done2:

  if (!blankline) fputc('\n', tofp);
  if (ferror(tofp)) return(-1);
  return(0);
}


/*
 * Read the contents of the mailbox, and create
 * a linked list of message frames (headers).
 */
void readbox()
{
  char linebuf[512];
  LET *let;
  off_t current;
 
  firstlet = lastlet = NIL_LET;

  if (allowed(mailbox, 04) == FALSE) {
	fprintf(stderr, "mail: can't access mailbox ");
	perror(mailbox);
	exit(1);
  }
  boxfp = fopen(mailbox, "r");

  current = 0L;
  while(TRUE) {
	if (fgets(linebuf, sizeof linebuf, boxfp) == (char *)NULL) break;
       
	if (strncmp(linebuf, "From ", 5)==0) {
		if ( (let = (LET *)malloc(sizeof(LET))) == NIL_LET) {
			fprintf(stderr, "Out of memory.\n");
			exit(1);
		}
        	if (lastlet == NIL_LET) {
			firstlet = let;
			let->prev = NIL_LET;
		} else {
			let->prev = lastlet;
			lastlet->next = let;
		}
		lastlet = let;
		let->next = NIL_LET;

		let->status = UNREAD;
		let->location = current;
	}
	current += strlen(linebuf);
  }
}


/*
 * Write the contents of letter 'let' to file 'savefile'.
 */
void savelet(let, savefile)
LET *let;
char *savefile;
{
  FILE *savefp;

  if (allowed(savefile, 04) == TRUE) {
	if ((savefp = fopen(savefile, "a")) == (FILE *)NULL) {
		perror(savefile);
		exit(0);
	}
	printlet(let, savefp);
	if ((ferror(savefp) != 0) || (fclose(savefp) != 0)) {
		fprintf(stderr, "savefile write error:");
		perror(savefile);
	}
  } else {
	fprintf(stderr, "mail: cannot write to file \"%s\"\n", savefile);
  }
}


void updatebox()
{
  char lockname[PATHLEN];		/* maildrop lock */
  LET *let;				/* current letter */
  register FILE *tempfp;		/* fp for tempfile */
  int locktries = 0;			/* tries when box is locked */
  register int c;			/* copying character */

  sprintf(lockname, LOCKNAME, whoami());

  if ((tempfp = fopen(tempname, "w")) == (FILE *)NULL) {
	perror("mail: can't create temporary file");
	return;
  }

  for (let = firstlet; let != NIL_LET; let = let->next) {
	if (let->status != DELETED) printlet(let, tempfp);
  }

  if (ferror(tempfp) || (tempfp = freopen(tempname, "r", tempfp))
							== (FILE *)NULL) {
	perror("mail: temporary file write error");
	unlink(tempname);
	return;
  }

  /* Shut off signals during the update. */
  signal(SIGINT, SIG_IGN);
  signal(SIGHUP, SIG_IGN);
  signal(SIGQUIT, SIG_IGN);

  if (usedrop) while(link(mailbox, lockname) != 0) {
	if (++locktries >= LOCKTRIES) {
		fprintf(stderr, "mail: couldn't lock maildrop for update\n");
		return;
	}
	sleep(LOCKWAIT);
  }

  if ((boxfp = freopen(mailbox, "w", boxfp)) == (FILE *)NULL) {
	perror("mail: couldn't reopen maildrop");
	fprintf(stderr, "mail may have been lost; look in %s\n", tempname);
	if (usedrop) unlink(lockname);
	return;
  }

  unlink(tempname);

  while ((c = getc(tempfp)) != EOF) fputc(c, boxfp);

  fclose(boxfp);

  if (usedrop) unlink(lockname);
}


/*
 * This is the interactive command interpreter.
 */
void interact()
{
  char linebuf[512];		/* user input line */
  LET *let, *next;		/* current and next letter */
  int interrupted = FALSE;	/* SIGINT hit during letter print */
  int needprint = TRUE;		/* need to print this letter */
  char *savefile;		/* filename to save into */

  if (firstlet == NIL_LET) {
	printf("No mail.\n");
	return;
  }

  let = reversemode ? firstlet : lastlet;

  while(TRUE) {
	next = reversemode ? let->next : let->prev;
	if (next == NIL_LET) next = let;

	if (!quitmode) {
		interrupted = setjmp(printjump);
		signal(SIGINT, onint);
	}

	if (!interrupted && needprint) {
		if (DELETED != let->status) let->status = READ;
		printlet(let, stdout);
	}

	if (interrupted) putchar('\n');
	needprint = FALSE;
	fputs(PROMPT, stdout);
	fflush(stdout);

	if (fgets(linebuf, sizeof linebuf, stdin) == (char *)NULL) break;

	if (!quitmode) signal(SIGINT, SIG_IGN);

	switch(linebuf[0]) {
		case '\n':
			let = next;
			needprint = TRUE;
			continue;
		case 'd':
			let->status = DELETED;
			if (next != let) needprint = TRUE;
			needupdate = TRUE;
			let = next;
			continue;
		case 'p':
			needprint = TRUE;
			continue;
		case '-':
			next = reversemode ? let->prev : let->next;
			if (next == NIL_LET) next = let;
			let = next;
			needprint = TRUE;
			continue;
		case 's':
			for (savefile = strtok(linebuf + 1, " \t\n");
				savefile != (char *)NULL;
				savefile = strtok((char *)NULL, " \t\n")) {
					savelet(let, savefile);
			}
			continue;
		case '!':
			doshell(&linebuf[1]);
			continue;
		case 'q':
			return;
		case 'x':
			exit(0);
		default:
			fprintf(stderr, "Illegal command\n");
			continue;
	}
  }   
}


#ifdef MAILER
/*
 * Send a message to a remote (non-local) user
 * through the MAILER agent.
 */
void sendremote(who)
char *who;
{
  char *argvec[5];		/* string array for execv() */
  char **argp;			/* index into array */
  int waitstat, pid;

  if ((pid = fork()) < 0) {
	perror("mail: couldn't fork");
	return;
  } else if (pid != 0) {		/* parent */
	wait(&waitstat);
	return;
  } else {				/* child */
	  setgid(old_gid);
	  setuid(old_uid);

	  argp = argvec;		/* create the arguments for exec() */
	  *argp++ = "send-mail";
	  if (verbose) *argp++ = "-v";	/* Pass the -v option ... */
	  *argp++ = who;		/* the recipient */

	  *argp = (char *)NULL;		/* terminate the array with a NULL */

	  execv(MAILER, argvec);	/* execute the mailer and exit */

	  fprintf(stderr, "mail: couldn't exec %s\n", MAILER);
	  exit(-1);
  }
}
#endif MAILER


/* 
 * Save the current message to file 'dead.letter'.
 */
void deadletter(void)
{
  char *fname = DEADLETTER;
  register FILE *inf, *outf;

  if (allowed(fname, 02) == TRUE) {
	inf = fopen(tempname, "r");
	if (inf == (FILE *)NULL) {
		fprintf(stderr, "mail: cannot open \"%s\"\n", tempname);
		return;
	}

	outf = fopen(fname, "w");
	if (outf == (FILE *)NULL) {
		fprintf(stderr, "mail: cannot create \"%s\"\n", fname);
		return;
	}

	/* Copy temp. file to dead.letter. */
	copy(inf, outf);

	fclose(inf);
	fclose(outf);

	setuid(getuid());
	chown(fname, getuid(), getgid());
	fprintf(stderr, "mail: dumped message on file \"%s\"\n", fname);
  } else {
	fprintf(stderr, "mail: cannot write to file \"%s\"\n", fname);
  }
}


/*
 * Deliver a message in old-style (V6/V7) format.
 */
int deliver(count, vec)
int count; char *vec[];
{
  int (*sigint)(), (*sighup)(), (*sigquit)();	/* saving signal state */
  char lockname[PATHLEN];		/* maildrop lock */
  char addressees[1024];			/* list of addressees */
  char sender[32];			/* sender's login name */
  struct stat stb;			/* for checking drop modes, owners */
  time_t now;				/* for datestamping the postmark */
  struct passwd *pw;			/* sender and recipent */
  FILE *mailfp;				/* fp for mail */
  int errs = 0;				/* count of errors */
  int dropfd;				/* file descriptor for user's drop */
  int created = 0;			/* true if we created the maildrop */
  int locktries;			/* tries when box is locked */
  int i;

  if (count > MAXRCPT) {
	fprintf(stderr, "mail: too many recipients\n");
	return(-1);
  }

  if ((pw = getpwuid(old_uid)) == (struct passwd *)NULL) {
	fprintf(stderr, "mail: unknown sender\n");
	return(-1);
  }
  strcpy(sender, pw->pw_name);

  /* If we need to rewind stdin and it isn't rewindable, make a copy. */
  if (isatty(0) || (count > 1 && lseek(0, 0L, SEEK_SET) < 0L)) {
	mailfp = makerewindable();
  } else mailfp = stdin;

  /* Create a list of addressees if necessary. */
  if (sayall) {
	strcpy(addressees, "");
	for (i=0; i<count; i++) {
		strcat(addressees, vec[i]);
		/* RFC-822: separate with comma */
		strcat(addressees, ", ");
  	}
  	addressees[strlen(addressees) -2] = '\0';  /* kill last comma */
  }

  /* Shut off signals during the delivery. */
  sigint = signal(SIGINT, SIG_IGN);
  sighup = signal(SIGHUP, SIG_IGN);
  sigquit = signal(SIGQUIT, SIG_IGN);

  /* Deliver the messages. Do this on a per-user basis. */
  for (i = 0; i < count; ++i) {
	if (count > 1) rewind(mailfp);

#ifdef MAILER
	if ((strchr(vec[i], '!') || strchr(vec[i], '@')) && usemailer) {
		sendremote(vec[i]);
		continue;
	}
#endif MAILER

	if ((pw = getpwnam(vec[i])) == (struct passwd *)NULL) {
		fprintf(stderr, "mail: user %s not known\n", vec[i]);
		++errs;
		continue;
	}

	sprintf(mailbox, DROPNAME, pw->pw_name);
	sprintf(lockname, LOCKNAME, pw->pw_name);

	/*
	 * Lock the maildrop while we're messing with it.
	 * Races are possible (though not very likely) when
	 * we have to create the maildrop, but not otherwise.
	 * If the box is already locked, wait awhile and try
	 * again.
	 */
	locktries = created = 0;
trylock:
	if (link(mailbox, lockname) != 0) {
		if (errno == ENOENT) {	/* user doesn't have a drop yet */
			if ((dropfd = creat(mailbox, 0600)) < 0) {
				fprintf(stderr,
				"mail: couln't create a maildrop for user %s\n",
								vec[i]);
				++errs;
				continue;
			}
			++created;
			goto trylock;
		} else {    /* somebody else has it locked, it seems - wait */
			if (++locktries >= LOCKTRIES) {
				fprintf(stderr,
				"mail: couldn't lock maildrop for user %s\n",
								vec[i]);
				++errs;
				continue;
			}
	   		sleep(LOCKWAIT);
	  		goto trylock;
		}
	}

	if (created) {
		(void) chown(mailbox, pw->pw_uid, pw->pw_gid);
		boxfp = fdopen(dropfd, "a");
	} else boxfp = fopen(mailbox, "a");

	if (boxfp == (FILE *)NULL || stat(mailbox, &stb) < 0) {
		fprintf(stderr, "mail: serious maildrop problems for %s\n",
								vec[i]);
		unlink(lockname);
		++errs;
		continue;
       	}

	if (stb.st_uid != pw->pw_uid || (stb.st_mode & S_IFMT) != S_IFREG) {
		fprintf(stderr, "mail: mailbox for user %s is illegal\n",
								vec[i]);
		unlink(lockname);
		++errs;
		continue;
       	}

	(void) time(&now);
	fprintf(boxfp, "From %s %24.24s\n", sender, ctime(&now));
	if (sayall) fprintf(boxfp, "To: %s\n", addressees);
	fprintf(boxfp, "\n");	/* empty line marks end-of-header! */

	if ((copy(mailfp, boxfp) < 0) | (fclose(boxfp) != 0)) {
		fprintf(stderr, "mail: error delivering to user %s", vec[i]);
		perror("");
		++errs;
       	}
	unlink(lockname);
  }

  fclose(mailfp);

  /* Put signals back the way they were. */
  signal(SIGINT, sigint);
  signal(SIGHUP, sighup);
  signal(SIGQUIT, sigquit);

  return((errs == 0) ? 0 : -1);
}


/*
 * Print all letters and exit.
 */
void printall()
{
  register LET *let;

  let = reversemode ? firstlet : lastlet;

  if (let == NIL_LET) {
	printf("No mail.\n");
	return;
  }

  while (let != NIL_LET) {
	printlet(let, stdout);
	let = reversemode ? let->next : let->prev;
  }
}


/*
 * Check if there is any mail for the calling user.
 * Return 0 if there is mail, or 1 for NO MAIL.
 */
int chk_mail(void)
{
  char temp[512];
  FILE *fp;

  if ((fp = fopen(mailbox, "r")) != (FILE *)NULL) {
	if (fgets(temp, sizeof(temp), fp) == (char *)NULL) {
		fclose(fp);	/* empty mailbox, no mail! */
		return(1);
	}
      	if (!strncmp(temp, "Forward to ", 11)) {
		fclose(fp);	/* FORWARD line in mailbox */
		return(2);	/* so no mail. */
	}
	fclose(fp);	/* another line, so we have mail! */
	return(0);
  }
  return(1);
}


void usage()
{
  fprintf(stderr, "usage: mail [-v] user [...]\n");
  fprintf(stderr, "       mail [-epqr] [-f file] [-t arg]\n");
}


int main(argc, argv)
int argc;
char *argv[];
{
  int c;

  /* If we are a link to 'lmail', let's deliver it ourself. */
  if ((basename(argv[0]))[0] == 'l') usemailer = FALSE;

  mktemp(tempname);		/* name the temp file */

  oldmask = umask(022);			/* change umask for security */
  old_uid = getuid();
  old_gid = getgid();
  setgid(getegid());
  setuid(geteuid());

  while ((c = getopt(argc, argv, "def:pqrtv")) != EOF) switch(c) {
	case 'd':	/* local delivery only */
		usemailer = FALSE;
		break;
	case 'e':	/* check if any mail present */
		exit(chk_mail());
	case 'f':	/* use other mailbox */
		setuid(getuid());	/* won't need to lock */
		setgid(getgid());	/* won't need to lock */
		usedrop = FALSE;
		strncpy(mailbox, optarg, PATHLEN - 1);
		break;
	case 'p':	/* print all mail and exit */
		printmode++;
		break;
	case 'q':	/* abort if SIGINT received */
		quitmode++;
		break;
	case 'r':	/* REVERSE mode */
		reversemode++;
		break;
	case 't':	/* include list of all addressees in "To:" */
		sayall++;
		break;
	case 'v':	/* verbose mode */
		verbose++;
		break;
	default:
		usage();
		exit(2);
  }

  if (optind < argc) {
	if (deliver(argc - optind, argv + optind) < 0) {
		deadletter();
		exit(1);
	} else exit(0);
  }

  if (usedrop) sprintf(mailbox, DROPNAME, whoami());

  readbox();

  if (printmode) printall();
    else interact();

  if (needupdate) updatebox();

  exit(1);
}
