777 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			777 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*  mail - send/receive mail 		 Author: Peter S. Housel */
 | |
| /* Version 0.2 of September 1990: added -e, -t, * options - cwr */
 | |
| 
 | |
| /* 2003-07-18: added -s option - ASW */
 | |
| 
 | |
| #include <sys/types.h>
 | |
| #include <sys/stat.h>
 | |
| #include <errno.h>
 | |
| #undef EOF			/* temporary hack */
 | |
| #include <signal.h>
 | |
| #include <pwd.h>
 | |
| #include <time.h>
 | |
| #include <setjmp.h>
 | |
| #include <string.h>
 | |
| #include <stdlib.h>
 | |
| #include <fcntl.h>
 | |
| #include <unistd.h>
 | |
| #include <sys/wait.h>
 | |
| #include <stdio.h>
 | |
| 
 | |
| #ifdef DEBUG
 | |
| #define D(Q) (Q)
 | |
| #else
 | |
| #define D(Q)
 | |
| #endif
 | |
| 
 | |
| #define SHELL		"/bin/sh"
 | |
| 
 | |
| #define DROPNAME 	"/usr/spool/mail/%s"
 | |
| #define LOCKNAME	"/usr/spool/mail/%s.lock"
 | |
| #define LOCKWAIT	5	/* seconds to wait after collision */
 | |
| #define LOCKTRIES	4	/* maximum number of collisions */
 | |
| 
 | |
| #define MBOX		"mbox"
 | |
| 
 | |
| #define HELPFILE	"/usr/lib/mail.help"
 | |
| #define PROMPT		"? "
 | |
| #define PATHLEN		80
 | |
| #define MAXRCPT		100	/* maximum number of recipients */
 | |
| #define LINELEN		512
 | |
| 
 | |
| /* #define MAILER		"/usr/bin/smail"	*/ /* smart mailer */
 | |
| #define MAILERARGS		/* (unused) */
 | |
| 
 | |
| #define UNREAD		1	/* 'not read yet' status */
 | |
| #define DELETED		2	/* 'deleted' status */
 | |
| #define READ		3	/* 'has been read' status */
 | |
| 
 | |
| struct letter {
 | |
|   struct letter *prev, *next;	/* linked letter list */
 | |
|   int status;			/* letter status */
 | |
|   off_t location;		/* location within mailbox file */
 | |
| };
 | |
| 
 | |
| struct letter *firstlet, *lastlet;
 | |
| 
 | |
| int usemailer = 1;		/* use MAILER to deliver (if any) */
 | |
| int printmode = 0;		/* print-and-exit mode */
 | |
| int quitmode = 0;		/* take interrupts */
 | |
| int reversemode = 0;		/* print mailbox in reverse order */
 | |
| int usedrop = 1;		/* read the maildrop (no -f given) */
 | |
| int verbose = 0;		/* pass "-v" flag on to mailer */
 | |
| int needupdate = 0;		/* need to update mailbox */
 | |
| int msgstatus = 0;		/* return the mail status */
 | |
| int distlist = 0;		/* include distribution list */
 | |
| char mailbox[PATHLEN];		/* user's mailbox/maildrop */
 | |
| char tempname[PATHLEN] = "/tmp/mailXXXXXX";	/* temporary file */
 | |
| char *subject = NULL;
 | |
| FILE *boxfp = NULL;		/* mailbox file */
 | |
| jmp_buf printjump;		/* for quitting out of letters */
 | |
| unsigned oldmask;		/* saved umask() */
 | |
| 
 | |
| extern int optind;
 | |
| extern char *optarg;
 | |
| 
 | |
| _PROTOTYPE(int main, (int argc, char **argv));
 | |
| _PROTOTYPE(int deliver, (int count, char *vec []));
 | |
| _PROTOTYPE(FILE *makerewindable, (void));
 | |
| _PROTOTYPE(int copy, (FILE *fromfp, FILE *tofp));
 | |
| _PROTOTYPE(void readbox, (void));
 | |
| _PROTOTYPE(void printall, (void));
 | |
| _PROTOTYPE(void interact, (void));
 | |
| _PROTOTYPE(void onint, (int dummy));
 | |
| _PROTOTYPE(void savelet, (struct letter *let, char *savefile));
 | |
| _PROTOTYPE(void updatebox, (void));
 | |
| _PROTOTYPE(void printlet, (struct letter *let, FILE *tofp));
 | |
| _PROTOTYPE(void doshell, (char *command));
 | |
| _PROTOTYPE(void usage, (void));
 | |
| _PROTOTYPE(char *basename, (char *name));
 | |
| _PROTOTYPE(char *whoami, (void));
 | |
| _PROTOTYPE(void dohelp, (void));
 | |
| _PROTOTYPE(int filesize, (char *name));
 | |
| 
 | |
| int main(argc, argv)
 | |
| int argc;
 | |
| char *argv[];
 | |
| {
 | |
|   int c;
 | |
| 
 | |
|   if ('l' == (basename(argv[0]))[0])	/* 'lmail' link? */
 | |
| 	usemailer = 0;		/* yes, let's deliver it */
 | |
| 
 | |
|   (void) mktemp(tempname);	/* name the temp file */
 | |
| 
 | |
|   oldmask = umask(022);		/* change umask for security */
 | |
| 
 | |
|   while (EOF != (c = getopt(argc, argv, "epqrf:tdvs:"))) switch (c) {
 | |
| 	    case 'e':	++msgstatus;	break;	
 | |
| 
 | |
| 	    case 't':	++distlist;	break;
 | |
| 
 | |
| 	    case 'p':	++printmode;	break;
 | |
| 
 | |
| 	    case 'q':	++quitmode;	break;
 | |
| 
 | |
| 	    case 'r':	++reversemode;	break;
 | |
| 
 | |
| 	    case 'f':
 | |
| 		setuid(getuid());	/* won't need to lock */
 | |
| 		usedrop = 0;
 | |
| 		strncpy(mailbox, optarg, (size_t)(PATHLEN - 1));
 | |
| 		break;
 | |
| 
 | |
| 	    case 'd':	usemailer = 0;	break;
 | |
| 
 | |
| 	    case 'v':	++verbose;	break;
 | |
| 
 | |
| 	    case 's':	subject = optarg; break;
 | |
| 
 | |
| 	    default:
 | |
| 		usage();
 | |
| 		exit(1);
 | |
| 	}
 | |
| 
 | |
|   if (optind < argc) {
 | |
| 	if (deliver(argc - optind, argv + optind) < 0)
 | |
| 		exit(1);
 | |
| 	else
 | |
| 		exit(0);
 | |
|   }
 | |
|   if (usedrop) sprintf(mailbox, DROPNAME, whoami());
 | |
| 
 | |
|   D(printf("mailbox=%s\n", mailbox));
 | |
| 
 | |
|   if (msgstatus) {
 | |
| 	if (filesize(mailbox))
 | |
| 		exit(0);
 | |
| 	else
 | |
| 		exit(1);
 | |
|   }
 | |
| 
 | |
|   readbox();
 | |
| 
 | |
|   if (printmode)
 | |
| 	printall();
 | |
|   else
 | |
| 	interact();
 | |
| 
 | |
|   if (needupdate) updatebox();
 | |
| 
 | |
|   return(0);
 | |
| }
 | |
| 
 | |
| int deliver(count, vec)
 | |
| int count;
 | |
| char *vec[];
 | |
| {
 | |
|   int i, j;
 | |
|   int errs = 0;			/* count of errors */
 | |
|   int dropfd;			/* file descriptor for user's drop */
 | |
|   int created = 0;		/* true if we created the maildrop */
 | |
|   FILE *mailfp;			/* fp for mail */
 | |
|   struct stat stb;		/* for checking drop modes, owners */
 | |
| #ifdef __STDC__
 | |
|   void (*sigint)(int), (*sighup)(int), (*sigquit)(int);/* saving signal state */
 | |
| #else
 | |
|   void (*sigint) (), (*sighup) (), (*sigquit) ();      /* saving signal state */
 | |
| #endif
 | |
|   time_t now;			/* for datestamping the postmark */
 | |
|   char sender[32];		/* sender's login name */
 | |
|   char lockname[PATHLEN];	/* maildrop lock */
 | |
|   int locktries;		/* tries when box is locked */
 | |
|   struct passwd *pw;		/* sender and recipent */
 | |
|   int to_console;		/* deliver to console if everything fails */
 | |
| 
 | |
|   if (count > MAXRCPT) {
 | |
| 	fprintf(stderr, "mail: too many recipients\n");
 | |
| 	return -1;
 | |
|   }
 | |
| #ifdef MAILER
 | |
|   if (usemailer) {
 | |
| 	char *argvec[MAXRCPT + 3];
 | |
| 	char **argp;
 | |
| 
 | |
| 	setuid(getuid());
 | |
| 
 | |
| 	argp = argvec;
 | |
| 	*argp++ = "send-mail";
 | |
| 	if (verbose) *argp++ = "-v";
 | |
| 
 | |
| 	for (i = 0; i < count; ++i) *argp++ = vec[i];
 | |
| 
 | |
| 	*argp = NULL;
 | |
| 	execv(MAILER, argvec);
 | |
| 	fprintf(stderr, "mail: couldn't exec %s\n", MAILER);
 | |
| 	return -1;
 | |
|   }
 | |
| #endif /* MAILER */
 | |
| 
 | |
|   if (NULL == (pw = getpwuid(getuid()))) {
 | |
| 	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, 0) == (off_t) -1)) {
 | |
| 	mailfp = makerewindable();
 | |
|   } else
 | |
| 	mailfp = stdin;
 | |
| 
 | |
|   /* Shut off signals during the delivery */
 | |
|   sigint = signal(SIGINT, SIG_IGN);
 | |
|   sighup = signal(SIGHUP, SIG_IGN);
 | |
|   sigquit = signal(SIGQUIT, SIG_IGN);
 | |
| 
 | |
|   for (i = 0; i < count; ++i) {
 | |
| 	if (count > 1) rewind(mailfp);
 | |
| 
 | |
| 	D(printf("deliver to %s\n", vec[i]));
 | |
| 
 | |
| 	if (NULL == (pw = getpwnam(vec[i]))) {
 | |
| 		fprintf(stderr, "mail: user %s not known\n", vec[i]);
 | |
| 		++errs;
 | |
| 		continue;
 | |
| 	}
 | |
| 	sprintf(mailbox, DROPNAME, pw->pw_name);
 | |
| 	sprintf(lockname, LOCKNAME, pw->pw_name);
 | |
| 
 | |
| 	D(printf("maildrop='%s', lock='%s'\n", mailbox, lockname));
 | |
| 
 | |
| 	/* 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 = to_console = 0;
 | |
| trylock:
 | |
| 	if (link(mailbox, lockname) != 0) {
 | |
| 		if (ENOENT == errno) {	/* user doesn't have a drop yet */
 | |
| 			dropfd = creat(mailbox, 0600);
 | |
| 			if (dropfd < 0 && errno == ENOENT) {
 | |
| 				/* Probably missing spool dir; to console. */
 | |
| 				boxfp = fopen("/dev/console", "w");
 | |
| 				if (boxfp != NULL) {
 | |
| 					to_console = 1;
 | |
| 					goto nobox;
 | |
| 				}
 | |
| 			}
 | |
| 			if (dropfd < 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 (NULL == boxfp || 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;
 | |
| 	}
 | |
| nobox:
 | |
| 	if (to_console) {
 | |
| 		fprintf(boxfp,
 | |
| 			"-------------\n| Mail from %s to %s\n-------------\n",
 | |
| 			sender, vec[i]);
 | |
| 	} else {
 | |
| 		(void) time(&now);
 | |
| 		fprintf(boxfp, "From %s %24.24s\n", sender, ctime(&now));
 | |
| 	}
 | |
| 
 | |
| 	/* Add the To: header line */
 | |
| 	fprintf(boxfp, "To: %s\n", vec[i]);
 | |
| 
 | |
| 	if (distlist) {
 | |
| 		fprintf(boxfp, "Dist: ");
 | |
| 		for (j = 0; j < count; ++j)
 | |
| 			if (getpwnam(vec[j]) != NULL && j != i)
 | |
| 				fprintf(boxfp, "%s ", vec[j]) ;
 | |
| 		fprintf(boxfp, "\n");
 | |
| 	}
 | |
| 
 | |
| 	/* Add the Subject: header line */
 | |
| 	if (subject != NULL) fprintf(boxfp, "Subject: %s\n", subject);
 | |
| 
 | |
| 	fprintf(boxfp, "\n");
 | |
|  
 | |
| 	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(0 == errs) ? 0 : -1;
 | |
| }
 | |
| 
 | |
| /* '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()
 | |
| {
 | |
|   FILE *tempfp;			/* temp file used for copy */
 | |
|   int c;			/* character being copied */
 | |
|   int state;			/* ".\n" detection state */
 | |
| 
 | |
|   if (NULL == (tempfp = fopen(tempname, "w"))) {
 | |
| 	fprintf(stderr, "mail: can't create temporary file\n");
 | |
| 	return 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 (EOF != (c = getc(stdin))) switch (state) {
 | |
| 	    case '\n':
 | |
| 		if ('.' == c)
 | |
| 			state = '.';
 | |
| 		else {
 | |
| 			if ('\n' != c) state = '\0';
 | |
| 			putc(c, tempfp);
 | |
| 		}
 | |
| 		break;
 | |
| 	    case '.':
 | |
| 		if ('\n' == c) goto done;
 | |
| 		state = '\0';
 | |
| 		putc('.', tempfp);
 | |
| 		putc(c, tempfp);
 | |
| 		break;
 | |
| 	    default:
 | |
| 		state = ('\n' == c) ? '\n' : '\0';
 | |
| 		putc(c, tempfp);
 | |
| 	}
 | |
| done:
 | |
|   if (ferror(tempfp) || fclose(tempfp)) {
 | |
| 	fprintf(stderr, "mail: couldn't copy letter to temporary file\n");
 | |
| 	return NULL;
 | |
|   }
 | |
|   tempfp = freopen(tempname, "r", stdin);
 | |
|   unlink(tempname);		/* unlink name; file lingers on in limbo */
 | |
|   return tempfp;
 | |
| }
 | |
| 
 | |
| int copy(fromfp, tofp)
 | |
| FILE *fromfp, *tofp;
 | |
| {
 | |
|   int c;			/* character being copied */
 | |
|   int state;			/* ".\n" and postmark detection state */
 | |
|   int blankline = 0;		/* was most recent line completely blank? */
 | |
|   static char postmark[] = "From ";
 | |
|   char *p, *q;
 | |
| 
 | |
|   /* 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 (EOF != (c = getc(fromfp))) {
 | |
| 	switch (state) {
 | |
| 	    case '\n':
 | |
| 		if ('.' == c)	/* '.' at BOL */
 | |
| 			state = '.';
 | |
| 		else if (*p == c) {	/* start of postmark */
 | |
| 			++p;
 | |
| 			state = 'P';
 | |
| 		} else {	/* anything else */
 | |
| 			if ('\n' == c)
 | |
| 				blankline = 1;
 | |
| 			else {
 | |
| 				state = '\0';
 | |
| 				blankline = 0;
 | |
| 			}
 | |
| 			putc(c, tofp);
 | |
| 		}
 | |
| 		break;
 | |
| 	    case '.':
 | |
| 		if ('\n' == c) goto done;
 | |
| 		state = '\0';
 | |
| 		putc('.', tofp);
 | |
| 		putc(c, tofp);
 | |
| 		break;
 | |
| 	    case 'P':
 | |
| 		if (*p == c) {
 | |
| 			if (*++p == '\0') {	/* successfully reached end */
 | |
| 				p = postmark;
 | |
| 				putc('>', tofp);
 | |
| 				fputs(postmark, tofp);
 | |
| 				state = '\0';
 | |
| 				break;
 | |
| 			}
 | |
| 			break;	/* not there yet */
 | |
| 		}
 | |
| 		state = ('\n' == c) ? '\n' : '\0';
 | |
| 		for (q = postmark; q < p; ++q) putc(*q, tofp);
 | |
| 		putc(c, tofp);
 | |
| 		blankline = 0;
 | |
| 		p = postmark;
 | |
| 		break;
 | |
| 	    default:
 | |
| 		state = ('\n' == c) ? '\n' : '\0';
 | |
| 		putc(c, tofp);
 | |
| 	}
 | |
|   }
 | |
|   if ('\n' != state) putc('\n', tofp);
 | |
| done:
 | |
|   if (!blankline) putc('\n', tofp);
 | |
|   if (ferror(tofp)) return -1;
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| void readbox()
 | |
| {
 | |
|   char linebuf[512];
 | |
|   struct letter *let;
 | |
|   off_t current;
 | |
| 
 | |
|   firstlet = lastlet = NULL;
 | |
| 
 | |
|   if (access(mailbox, 4) < 0 || NULL == (boxfp = fopen(mailbox, "r"))) {
 | |
| 	if (usedrop && ENOENT == errno) return;
 | |
| 	fprintf(stderr, "can't access mailbox ");
 | |
| 	perror(mailbox);
 | |
| 	exit(1);
 | |
|   }
 | |
|   current = 0L;
 | |
|   while (1) {
 | |
| 	if (NULL == fgets(linebuf, sizeof linebuf, boxfp)) break;
 | |
| 
 | |
| 	if (!strncmp(linebuf, "From ", (size_t)5)) {
 | |
| 		if (NULL == (let = (struct letter *) malloc(sizeof(struct letter)))) {
 | |
| 			fprintf(stderr, "Out of memory.\n");
 | |
| 			exit(1);
 | |
| 		}
 | |
| 		if (NULL == lastlet) {
 | |
| 			firstlet = let;
 | |
| 			let->prev = NULL;
 | |
| 		} else {
 | |
| 			let->prev = lastlet;
 | |
| 			lastlet->next = let;
 | |
| 		}
 | |
| 		lastlet = let;
 | |
| 		let->next = NULL;
 | |
| 
 | |
| 		let->status = UNREAD;
 | |
| 		let->location = current;
 | |
| 		D(printf("letter at %ld\n", current));
 | |
| 	}
 | |
| 	current += strlen(linebuf);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void printall()
 | |
| {
 | |
|   struct letter *let;
 | |
| 
 | |
|   let = reversemode ? firstlet : lastlet;
 | |
| 
 | |
|   if (NULL == let) {
 | |
| 	printf("No mail.\n");
 | |
| 	return;
 | |
|   }
 | |
|   while (NULL != let) {
 | |
| 	printlet(let, stdout);
 | |
| 	let = reversemode ? let->next : let->prev;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void interact()
 | |
| {
 | |
|   char linebuf[512];		/* user input line */
 | |
|   struct letter *let, *next;	/* current and next letter */
 | |
|   int interrupted = 0;		/* SIGINT hit during letter print */
 | |
|   int needprint = 1;		/* need to print this letter */
 | |
|   char *savefile;		/* filename to save into */
 | |
| 
 | |
|   if (NULL == firstlet) {
 | |
| 	printf("No mail.\n");
 | |
| 	return;
 | |
|   }
 | |
|   let = reversemode ? firstlet : lastlet;
 | |
| 
 | |
|   while (1) {
 | |
| 	next = reversemode ? let->next : let->prev;
 | |
| 	if (NULL == next) 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 = 0;
 | |
| 	fputs(PROMPT, stdout);
 | |
| 	fflush(stdout);
 | |
| 
 | |
| 	if (fgets(linebuf, sizeof linebuf, stdin) == NULL) break;
 | |
| 
 | |
| 	if (!quitmode) signal(SIGINT, SIG_IGN);
 | |
| 
 | |
| 	switch (linebuf[0]) {
 | |
| 	    case '\n':
 | |
| 		let = next;
 | |
| 		needprint = 1;
 | |
| 		continue;
 | |
| 	    case 'd':
 | |
| 		let->status = DELETED;
 | |
| 		if (next != let)/* look into this */
 | |
| 			needprint = 1;
 | |
| 		needupdate = 1;
 | |
| 		let = next;
 | |
| 		continue;
 | |
| 	    case 'p':
 | |
| 		needprint = 1;
 | |
| 		continue;
 | |
| 	    case '-':
 | |
| 		next = reversemode ? let->prev : let->next;
 | |
| 		if (NULL == next) next = let;
 | |
| 		let = next;
 | |
| 		needprint = 1;
 | |
| 		continue;
 | |
| 	    case 's':
 | |
| 		for (savefile = strtok(linebuf + 1, " \t\n");
 | |
| 		     savefile != NULL;
 | |
| 		     savefile = strtok((char *) NULL, " \t\n")) {
 | |
| 			savelet(let, savefile);
 | |
| 		}
 | |
| 		continue;
 | |
| 	    case '!':
 | |
| 		doshell(linebuf + 1);
 | |
| 		continue;
 | |
| 	    case '*':
 | |
| 		dohelp();
 | |
| 		continue;
 | |
| 	    case 'q':
 | |
| 		return;
 | |
| 	    case 'x':
 | |
| 		exit(0);
 | |
| 	    default:
 | |
| 		fprintf(stderr, "Illegal command\n");
 | |
| 		continue;
 | |
| 	}
 | |
|   }
 | |
| }
 | |
| 
 | |
| void onint(dummy)
 | |
| int dummy;	/* to satisfy ANSI compilers */
 | |
| {
 | |
|   longjmp(printjump, 1);
 | |
| }
 | |
| 
 | |
| void savelet(let, savefile)
 | |
| struct letter *let;
 | |
| char *savefile;
 | |
| {
 | |
|   int waitstat, pid;
 | |
|   FILE *savefp;
 | |
| 
 | |
|   if ((pid = fork()) < 0) {
 | |
| 	perror("mail: couldn't fork");
 | |
| 	return;
 | |
|   } else if (pid != 0) {	/* parent */
 | |
| 	wait(&waitstat);
 | |
| 	return;
 | |
|   }
 | |
| 
 | |
|   /* Child */
 | |
|   setgid(getgid());
 | |
|   setuid(getuid());
 | |
|   if ((savefp = fopen(savefile, "a")) == NULL) {
 | |
| 	perror(savefile);
 | |
| 	exit(0);
 | |
|   }
 | |
|   printlet(let, savefp);
 | |
|   if ((ferror(savefp) != 0) | (fclose(savefp) != 0)) {
 | |
| 	fprintf(stderr, "savefile write error:");
 | |
| 	perror(savefile);
 | |
|   }
 | |
|   exit(0);
 | |
| }
 | |
| 
 | |
| void updatebox()
 | |
| {
 | |
|   FILE *tempfp;			/* fp for tempfile */
 | |
|   char lockname[PATHLEN];	/* maildrop lock */
 | |
|   int locktries = 0;		/* tries when box is locked */
 | |
|   struct letter *let;		/* current letter */
 | |
|   int c;
 | |
| 
 | |
|   sprintf(lockname, LOCKNAME, whoami());
 | |
| 
 | |
|   if (NULL == (tempfp = fopen(tempname, "w"))) {
 | |
| 	perror("mail: can't create temporary file");
 | |
| 	return;
 | |
|   }
 | |
|   for (let = firstlet; let != NULL; let = let->next) {
 | |
| 	if (let->status != DELETED) {
 | |
| 		printlet(let, tempfp);
 | |
| 		D(printf("printed letter at %ld\n", let->location));
 | |
| 	}
 | |
|   }
 | |
| 
 | |
|   if (ferror(tempfp) || NULL == (tempfp = freopen(tempname, "r", tempfp))) {
 | |
| 	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 (NULL == (boxfp = freopen(mailbox, "w", boxfp))) {
 | |
| 	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) putc(c, boxfp);
 | |
| 
 | |
|   fclose(boxfp);
 | |
| 
 | |
|   if (usedrop) unlink(lockname);
 | |
| }
 | |
| 
 | |
| void printlet(let, tofp)
 | |
| struct letter *let;
 | |
| FILE *tofp;
 | |
| {
 | |
|   off_t current, limit;
 | |
|   int c;
 | |
| 
 | |
|   fseek(boxfp, (current = let->location), 0);
 | |
|   limit = (NULL != let->next) ? let->next->location : -1;
 | |
| 
 | |
|   while (current != limit && (c = getc(boxfp)) != EOF) {
 | |
| 	putc(c, tofp);
 | |
| 	++current;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void doshell(command)
 | |
| char *command;
 | |
| {
 | |
|   int waitstat, pid;
 | |
|   char *shell;
 | |
| 
 | |
|   if (NULL == (shell = getenv("SHELL"))) shell = SHELL;
 | |
| 
 | |
|   if ((pid = fork()) < 0) {
 | |
| 	perror("mail: couldn't fork");
 | |
| 	return;
 | |
|   } else if (pid != 0) {	/* parent */
 | |
| 	wait(&waitstat);
 | |
| 	return;
 | |
|   }
 | |
| 
 | |
|   /* Child */
 | |
|   setgid(getgid());
 | |
|   setuid(getuid());
 | |
|   umask(oldmask);
 | |
| 
 | |
|   execl(shell, shell, "-c", command, (char *) NULL);
 | |
|   fprintf(stderr, "can't exec shell\n");
 | |
|   exit(127);
 | |
| }
 | |
| 
 | |
| void usage()
 | |
| {
 | |
|   fprintf(stderr, "usage: mail [-epqr] [-f file]\n");
 | |
|   fprintf(stderr, "       mail [-dtv] [-s subject] user [...]\n");
 | |
| }
 | |
| 
 | |
| char *basename(name)
 | |
| char *name;
 | |
| {
 | |
|   char *p;
 | |
| 
 | |
|   if (NULL == (p = rindex(name, '/')))
 | |
| 	return name;
 | |
|   else
 | |
| 	return p + 1;
 | |
| }
 | |
| 
 | |
| char *whoami()
 | |
| {
 | |
|   struct passwd *pw;
 | |
| 
 | |
|   if (NULL != (pw = getpwuid(getuid())))
 | |
| 	return pw->pw_name;
 | |
|   else
 | |
| 	return "nobody";
 | |
| }
 | |
| 
 | |
| void dohelp()
 | |
| {
 | |
|   FILE *fp;
 | |
|   char buffer[80];
 | |
| 
 | |
|   if ( (fp = fopen(HELPFILE, "r")) == NULL)
 | |
| 	fprintf(stdout, "can't open helpfile %s\n", HELPFILE);
 | |
|   else
 | |
| 	while (fgets(buffer, 80, fp))
 | |
| 		fputs(buffer, stdout);
 | |
| }
 | |
| 
 | |
| int filesize(name)
 | |
| char *name ;
 | |
| {
 | |
|   struct stat buf;
 | |
|  
 | |
|   if (stat(name, &buf) == -1)
 | |
| 	buf.st_size = 0L;
 | |
| 
 | |
|   return (buf.st_size ? 1 : 0);
 | |
| }
 | 
