static char rcsid[] = "$Header:v6mail.c 12.0$";
#ifndef lint
static	char sccsid[] = "%W% %E%";
#endif
/*
 * v6mail
 */
#include <sysexits.h>

#include <sys/param.h>
#include <sys/stat.h>
#include <sys/dir.h>
#include <sys/times.h>
#include <ctype.h>
#include <errno.h>
#include <pwd.h>
#include <signal.h>

char	*ctime(), *index(), *rindex(), *ctime(), *strcpy(), *getlogin();
char	*mktemp(), *sprintf();
struct	passwd *getpwnam(), *getpwuid();
time_t	time();
struct	utmp *getutmp();
char	*getdate();
int	errno;

#include <stdio.h>

#define MAILMODE 0644
#define MSGSCMD "/usr/ucb/msgs"
#define MAILDIR "/usr/spool/mail"

char	lettmp[]  = "/tmp/MaXXXXX";	/* keep letter before sending it */
char	preptmp[] = "/tmp/mbXXXXX";	/* if prepending msg, use this file */
int	chew;				/* if true, strip extra from lines */
int	dflag;				/* if true, don't call sendmail */
char	shopcnt[30] = "0";		/* hop count parameter for rmt mail */
int	errs;				/* no of errs in sending */
char	deleteonly;			/* if true, just delete mailbox */
char 	remname[50];			/* if non-empty, from line extra */

main(argc, argv)
	int argc;
	char **argv;
{
	register int myuid;
	int delexit();
	char namebuf[128], *sn = NULL, logindir[60];
	struct passwd *pwd;

	(void) mktemp(lettmp);
	(void) mktemp(preptmp);
	(void) unlink(lettmp);
	(void) unlink(preptmp);
	myuid = getuid();
	logindir[0] = 0;
	sn = getlogin();
	if (sn == NULL || *sn == 0 || *sn == ' ') {
		pwd = getpwuid(myuid);		/* will read passwd file */
		if (pwd != NULL){
			sn = pwd->pw_name;
			(void) strcpy(logindir, pwd->pw_dir);
		}
		if (sn == NULL) {
			fprintf(stderr, "Who are you?\n");
			delexit(EX_OSFILE);
		}
	}
	(void) strcpy(namebuf, sn);
	if (argc < 2)
		goto hitit;
	for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++)
	switch (argv[0][1]) {

	case 'y':
	case 'n':
		argc++, argv--;
hitit:
		printmail(argc, argv, namebuf, logindir);
		delexit(EX_OK);

	case 'r':	/* one-arg -r--   -r addr */
		if (argc < 2)
			continue;
		/* ignore -r if not network or root */
		if (strcmp("network", namebuf) == 0 || myuid == 0 ||
/*###86 [lint] index arg. 1 used inconsistently v6mail.c(86) :: v6mail.c(244)%%%*/
/*###86 [lint] index arg. 2 used inconsistently v6mail.c(86) :: v6mail.c(244)%%%*/
		    strcmp("uucp", namebuf) == 0 || index('!', argv[1])) {
			(void) strcpy(namebuf, argv[1]);
			chew++;		/* eat From lines */
		}
		else
			(void) strcpy(remname, argv[1]);
		argc--, argv++;
		continue;

	case 'h':	/* hop count - used by network */
		if (argc < 2)
			continue;
		(void) strcpy(shopcnt, argv[1]);
		argc--, argv++;
		continue;

	case 'd':	/* really deliver this message */
		dflag++;
		continue;

	case 'D':	/* only delete the invokers mailbox */
		deleteonly++;
		goto hitit;		/* delete mail box, thats all */
	}
	/* if we are already ignoring signals, catch sigint */
	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
		(void) signal(SIGINT, delexit);
	argc++, argv--;
	bulkmail(argc, argv, namebuf);
	delexit(EX_OK);
}

printmail(argc, argv, name, logindir)
	int argc;
	char **argv;
	char *name, *logindir;
{
	register int c;
	FILE *fdin;
	char sfnmail[60], mbox[120];
	struct stat stb;

	(void) sprintf(sfnmail, "%s/%s", MAILDIR, name);
	if (deleteonly) {
		remove(sfnmail);
		return;
	}
	fdin = fopen(sfnmail, "r");
	if (fdin < 0 || fstat(fileno(fdin), &stb) < 0 || stb.st_size == 0) {
		printf("No mail.\n");
		return;
	}
	if (stb.st_nlink > 1) {
		printf("%s: Too many links.\n", sfnmail);
		return;
	}
	(void) getput(fdin, stdout);
	(void) fclose(fdin);
	(void) fflush(stdout);
	c = 'y';
	if (argc < 2) {
		if (isatty(0)) {
			printf("Save (y or n) ?"); (void) fflush(stdout);
			c = getchar();
		}
	} else
		c = argv[1][1];
	switch (c) {

	default:
		delexit(EX_OK);
		/*NOTREACHED*/

	case 'x':
		return;

	case 'y':
		(void) sprintf(mbox, "%s/mbox", logindir);
		if (writeable(mbox)) {
			perror(mbox);
			return;
		}
		printf("Saving mail in %s.\n", mbox);
		if (append(sfnmail, mbox, getuid(), getgid()) == 0)
			return;
		/* fall into... */

	case 'n':
		remove(sfnmail);
		return;
	}
}

bulkmail(argc, argv, from)
	char **argv, *from;
{
	char linebuf[BUFSIZ];
	FILE *fdout;

	if (dflag == 0) {
		argv[0] = "sendmail";
		argv[argc] = 0;
		execv("/usr/lib/sendmail", argv);
		perror("/usr/lib/sendmail");
		_exit(1);
	}
	fdout = fopen(lettmp, "w");
	if (fdout == NULL) {
		perror(lettmp);
		delexit(EX_OSFILE);
	}

	/*
	 * If delivering mail from the network via mail -r,
	 * Strip the leading line and throw it away, as long
	 * as it begins with "From ..." (and preserve the date if poss.)
	 */
	if (chew) {
		if (fgets(linebuf, BUFSIZ, stdin) == 0)
			goto skip;
		if (!strncmp(linebuf, "From ", 5) != 0)
			printfromline(fdout, getdate(linebuf), from);
		else {
			printfromline(fdout, (char *)0, from);
			fprintf(fdout, "%s", linebuf);
		}
	} else
		printfromline(fdout, (char *)0, from);
skip:
	if (remname[0])
		fprintf(fdout, "(from %s)\n", remname);
	if (getput(stdin, fdout) == 0)
		delexit(EX_OSERR);
	putc('\n', fdout);
	(void) fclose(fdout);
	while (--argc > 0)
		sendto(*++argv);
	delexit(errs);
}

printfromline(fdout, date, from)
	FILE *fdout;
	char *date, *from;
{
	time_t t;

	if (date == NULL) {
		t = time((time_t *)0);
		date = ctime(&t);
	}
	fprintf(fdout, "From %s  %s", from, date);
}

/* look over linebuf and return ptr to date, NULL if error */
char *
getdate(linebuf)
	char *linebuf;
{
	register char *s = linebuf;

/*###244 [lint] index arg. 2 used inconsistently v6mail.c(86) :: v6mail.c(244)%%%*/
/*###244 [lint] index arg. 1 used inconsistently v6mail.c(86) :: v6mail.c(244)%%%*/
	while (s = index(' ', s))
		if (!strncmp(s, " Sun ", 5) ||
		    !strncmp(s, " Mon ", 5) ||
		    !strncmp(s, " Tue ", 5) ||
		    !strncmp(s, " Wed ", 5) ||
		    !strncmp(s, " Thu ", 5) ||
		    !strncmp(s, " Fri ", 5) ||
		    !strncmp(s, " Sat ", 5))
			return (s + 1);
	return (0);
}

int	saved = 0;

sendto(person)
	char *person;
{
	char mailboxname[BUFSIZ];
	struct passwd *pwd;

	if (index('/', person)) {
		if (!writeable(person)) {
			perror(person);
			return;
		}
		lock(person);
		(void) append(lettmp, person, -1, -1);
		unlock();
		return;
	}
	pwd = getpwnam(person);
	if (pwd) {
		(void) sprintf(mailboxname, "%s/%s", MAILDIR, person);
		lock(mailboxname);
		(void) append(lettmp, mailboxname, pwd->pw_uid, pwd->pw_gid);
		unlock();
		return;
	}
	fprintf(stderr, "Can't send to %s.\n", person);
	errs++;
	if (!isatty(0) || saved)
		return;
	saved++;
	if (!writeable("dead.letter")) {
		perror("dead.letter");
		return;
	}
	printf("Letter saved in 'dead.letter'\n");
	(void) append(lettmp, "dead.letter", getuid(), getgid());
}

#include <sys/socket.h>
#include <net/in.h>

struct sockaddr_in biffaddr = { AF_INET, IPPORT_BIFFUDP };

append(from, to, uid, gid)
	char *from, *to;
	int uid, gid;
{
	register FILE *fdin, *fdout;
	int ret;
	struct stat stb;
	char *cp, buf[100]; int f;

	if (stat(to, &stb) >= 0 && (stb.st_mode&S_IFMT) != S_IFREG) {
		fprintf(stderr, "Not a plain file: %s\n", to);
		goto fail;
	}
	fdout = fopen(to, "a");
	if (fdout == NULL) {
		perror(to);
		goto fail;
	}
	if (uid != -1) {
		(void) chown(to, uid, gid);
		(void) chmod(to, MAILMODE);
	}
	if ((fdin = fopen(from, "r")) == NULL) {
		perror(from);
		return (0);
	}
	cp = rindex(to, '/');
	if (cp) {
		char *host = "localhost";
		biffaddr.sin_addr.s_addr = rhost(&host);
#if vax || pdp11
		biffaddr.sin_port =
		    (biffaddr.sin_port<<8) | ((biffaddr.sin_port>>8) & 0xff);
#endif
		f = socket(SOCK_DGRAM, 0, 0, 0);
		(void) sprintf(buf, "%s@%d\n", cp+1, ftell(fdout)); 
	}
	ret = getput(fdin, fdout);
	(void) fclose(fdin);
	(void) fclose(fdout);
	if (cp && f >= 0) {
		send(f, &biffaddr, buf, strlen(buf)+1);
		(void) close(f);
	}
	return (ret);
fail:
	errs++;
	return (0);
}

delexit(status)
	int status;
{

	(void) unlink(lettmp);
	(void) unlink(preptmp);
	exit(status);
}

getput(fdin, fdout)
	register FILE *fdin, *fdout;
{
	register int c;

	while ((c = getc(fdin)) != EOF) {
		errno = 0;
		putc(c, fdout);
		if (errno) {
			perror("mail");
			return (0);
		}
	}
	return (1);
}

writeable(name)
	char *name;
{
	struct stat stb;
	char *cp;
	int ok;

	if (stat(name, &stb) < 0) {
		cp = rindex(name, '/');
		if (cp)
			*cp = 0;
		ok = access(cp ? "." : name, 2) == 0;
		if (cp)
			*cp = '/';
		return (ok);
	}
	return (access(name, 2) == 0);
}

char	locktmp[30];				/* Usable lock temporary */
char	curlock[50];				/* Last used name of lock */
int	locked;					/* To note that we locked it */

/*
 * Lock the specified mail file by setting the file mailfile.lock.
 * We must, of course, be careful to unlink the lock file by a call
 * to unlock before we stop.  The algorithm used here is to see if
 * the lock exists, and if it does, to check its modify time.  If it
 * is older than 30 seconds, we assume error and set our own file.
 * Otherwise, we wait for 5 seconds and try again.
 */
lock(file)
	char *file;
{
	register int f;
	struct stat statbuf;
	time_t curtime;

	if (locked)
		return;
	(void) sprintf(curlock, "%s%s", file, ".lock");
	(void) sprintf(locktmp, "%s/tmXXXXXX", MAILDIR);
	(void) mktemp(locktmp);
	(void) unlink(locktmp);
	for (;;) {
		f = lock1(locktmp, curlock);
		if (f == 0) {
			locked = 1;
			return;
		}
		if (stat(curlock, &statbuf) < 0)
			return;
		(void) time(&curtime);
		if (curtime < statbuf.st_mtime + 30) {
			sleep(5);
			continue;
		}
		(void) unlink(curlock);
	}
}

unlock()
{

	if (locked)
		(void) unlink(curlock);
	locked = 0;
}

/*
 * Attempt to set the lock by creating the temporary file,
 * then doing a link/unlink.  If it fails, return -1 else 0
 */
lock1(tempfile, name)
	char tempfile[], name[];
{
	int fno;

	fno = creat(tempfile, 0400);
	if (fno < 0)
		return (-1);
	(void) close(fno);
	if (link(tempfile, name) < 0) {
		(void) unlink(tempfile);
		return (-1);
	}
	(void) unlink(tempfile);
	return (0);
}

remove(sfn)
	char *sfn;
{
	int i;

	if (unlink(sfn) < 0) {
		i = creat(sfn, MAILMODE);
		if (i >= 0)
			(void) close(i);
	}
}
