xref: /minix3/minix/commands/mail/mail.c (revision b88949904f55e7591b64919f3c92207f41f5b478)
1433d6423SLionel Sambuc /*  mail - send/receive mail 		 Author: Peter S. Housel */
2433d6423SLionel Sambuc /* Version 0.2 of September 1990: added -e, -t, * options - cwr */
3433d6423SLionel Sambuc 
4433d6423SLionel Sambuc /* 2003-07-18: added -s option - ASW */
5433d6423SLionel Sambuc 
6433d6423SLionel Sambuc #include <sys/types.h>
7433d6423SLionel Sambuc #include <sys/stat.h>
8433d6423SLionel Sambuc #include <errno.h>
9433d6423SLionel Sambuc #undef EOF			/* temporary hack */
10433d6423SLionel Sambuc #include <signal.h>
11433d6423SLionel Sambuc #include <pwd.h>
12433d6423SLionel Sambuc #include <time.h>
13433d6423SLionel Sambuc #include <setjmp.h>
14433d6423SLionel Sambuc #include <string.h>
15433d6423SLionel Sambuc #include <stdlib.h>
16433d6423SLionel Sambuc #include <fcntl.h>
17433d6423SLionel Sambuc #include <unistd.h>
18433d6423SLionel Sambuc #include <sys/wait.h>
19433d6423SLionel Sambuc #include <stdio.h>
20433d6423SLionel Sambuc 
21433d6423SLionel Sambuc #ifdef DEBUG
22433d6423SLionel Sambuc #define D(Q) (Q)
23433d6423SLionel Sambuc #else
24433d6423SLionel Sambuc #define D(Q)
25433d6423SLionel Sambuc #endif
26433d6423SLionel Sambuc 
27433d6423SLionel Sambuc #define SHELL		"/bin/sh"
28433d6423SLionel Sambuc 
29433d6423SLionel Sambuc #define DROPNAME 	"/var/mail/%s"
30433d6423SLionel Sambuc #define LOCKNAME	"/var/mail/%s.lock"
31433d6423SLionel Sambuc #define LOCKWAIT	5	/* seconds to wait after collision */
32433d6423SLionel Sambuc #define LOCKTRIES	4	/* maximum number of collisions */
33433d6423SLionel Sambuc 
34433d6423SLionel Sambuc #define MBOX		"mbox"
35433d6423SLionel Sambuc 
36433d6423SLionel Sambuc #define HELPFILE	"/usr/lib/mail.help"
37433d6423SLionel Sambuc #define PROMPT		"? "
38433d6423SLionel Sambuc #define PATHLEN		80
39433d6423SLionel Sambuc #define MAXRCPT		100	/* maximum number of recipients */
40433d6423SLionel Sambuc #define LINELEN		512
41433d6423SLionel Sambuc 
42433d6423SLionel Sambuc /* #define MAILER		"/usr/bin/smail"	*/ /* smart mailer */
43433d6423SLionel Sambuc #define MAILERARGS		/* (unused) */
44433d6423SLionel Sambuc 
45433d6423SLionel Sambuc #define UNREAD		1	/* 'not read yet' status */
46433d6423SLionel Sambuc #define DELETED		2	/* 'deleted' status */
47433d6423SLionel Sambuc #define READ		3	/* 'has been read' status */
48433d6423SLionel Sambuc 
49433d6423SLionel Sambuc struct letter {
50433d6423SLionel Sambuc   struct letter *prev, *next;	/* linked letter list */
51433d6423SLionel Sambuc   int status;			/* letter status */
52433d6423SLionel Sambuc   off_t location;		/* location within mailbox file */
53433d6423SLionel Sambuc };
54433d6423SLionel Sambuc 
55433d6423SLionel Sambuc struct letter *firstlet, *lastlet;
56433d6423SLionel Sambuc 
57433d6423SLionel Sambuc int usemailer = 1;		/* use MAILER to deliver (if any) */
58433d6423SLionel Sambuc int printmode = 0;		/* print-and-exit mode */
59433d6423SLionel Sambuc int quitmode = 0;		/* take interrupts */
60433d6423SLionel Sambuc int reversemode = 0;		/* print mailbox in reverse order */
61433d6423SLionel Sambuc int usedrop = 1;		/* read the maildrop (no -f given) */
62433d6423SLionel Sambuc int verbose = 0;		/* pass "-v" flag on to mailer */
63433d6423SLionel Sambuc int needupdate = 0;		/* need to update mailbox */
64433d6423SLionel Sambuc int msgstatus = 0;		/* return the mail status */
65433d6423SLionel Sambuc int distlist = 0;		/* include distribution list */
66433d6423SLionel Sambuc char mailbox[PATHLEN];		/* user's mailbox/maildrop */
67433d6423SLionel Sambuc char tempname[PATHLEN] = "/tmp/mailXXXXXX";	/* temporary file */
68433d6423SLionel Sambuc char *subject = NULL;
69433d6423SLionel Sambuc FILE *boxfp = NULL;		/* mailbox file */
70433d6423SLionel Sambuc jmp_buf printjump;		/* for quitting out of letters */
71433d6423SLionel Sambuc unsigned oldmask;		/* saved umask() */
72433d6423SLionel Sambuc 
73433d6423SLionel Sambuc extern int optind;
74433d6423SLionel Sambuc extern char *optarg;
75433d6423SLionel Sambuc 
76433d6423SLionel Sambuc int main(int argc, char **argv);
77433d6423SLionel Sambuc int deliver(int count, char *vec []);
78433d6423SLionel Sambuc FILE *makerewindable(void);
79433d6423SLionel Sambuc int copy(FILE *fromfp, FILE *tofp);
80433d6423SLionel Sambuc void readbox(void);
81433d6423SLionel Sambuc void printall(void);
82433d6423SLionel Sambuc void interact(void);
83433d6423SLionel Sambuc void onint(int dummy);
84433d6423SLionel Sambuc void savelet(struct letter *let, char *savefile);
85433d6423SLionel Sambuc void updatebox(void);
86433d6423SLionel Sambuc void printlet(struct letter *let, FILE *tofp);
87433d6423SLionel Sambuc void doshell(char *command);
88433d6423SLionel Sambuc void usage(void);
89433d6423SLionel Sambuc char *basename(char *name);
90433d6423SLionel Sambuc char *whoami(void);
91433d6423SLionel Sambuc void dohelp(void);
92433d6423SLionel Sambuc int filesize(char *name);
93433d6423SLionel Sambuc 
main(argc,argv)94433d6423SLionel Sambuc int main(argc, argv)
95433d6423SLionel Sambuc int argc;
96433d6423SLionel Sambuc char *argv[];
97433d6423SLionel Sambuc {
98433d6423SLionel Sambuc   int c;
99433d6423SLionel Sambuc 
100433d6423SLionel Sambuc   if ('l' == (basename(argv[0]))[0])	/* 'lmail' link? */
101433d6423SLionel Sambuc 	usemailer = 0;		/* yes, let's deliver it */
102433d6423SLionel Sambuc 
103433d6423SLionel Sambuc   (void) mktemp(tempname);	/* name the temp file */
104433d6423SLionel Sambuc 
105433d6423SLionel Sambuc   oldmask = umask(022);		/* change umask for security */
106433d6423SLionel Sambuc 
107433d6423SLionel Sambuc   while (EOF != (c = getopt(argc, argv, "epqrf:tdvs:"))) switch (c) {
108433d6423SLionel Sambuc 	    case 'e':	++msgstatus;	break;
109433d6423SLionel Sambuc 
110433d6423SLionel Sambuc 	    case 't':	++distlist;	break;
111433d6423SLionel Sambuc 
112433d6423SLionel Sambuc 	    case 'p':	++printmode;	break;
113433d6423SLionel Sambuc 
114433d6423SLionel Sambuc 	    case 'q':	++quitmode;	break;
115433d6423SLionel Sambuc 
116433d6423SLionel Sambuc 	    case 'r':	++reversemode;	break;
117433d6423SLionel Sambuc 
118433d6423SLionel Sambuc 	    case 'f':
119433d6423SLionel Sambuc 		setuid(getuid());	/* won't need to lock */
120433d6423SLionel Sambuc 		usedrop = 0;
121433d6423SLionel Sambuc 		strncpy(mailbox, optarg, (size_t)(PATHLEN - 1));
122433d6423SLionel Sambuc 		break;
123433d6423SLionel Sambuc 
124433d6423SLionel Sambuc 	    case 'd':	usemailer = 0;	break;
125433d6423SLionel Sambuc 
126433d6423SLionel Sambuc 	    case 'v':	++verbose;	break;
127433d6423SLionel Sambuc 
128433d6423SLionel Sambuc 	    case 's':	subject = optarg; break;
129433d6423SLionel Sambuc 
130433d6423SLionel Sambuc 	    default:
131433d6423SLionel Sambuc 		usage();
132433d6423SLionel Sambuc 		exit(1);
133433d6423SLionel Sambuc 	}
134433d6423SLionel Sambuc 
135433d6423SLionel Sambuc   if (optind < argc) {
136433d6423SLionel Sambuc 	if (deliver(argc - optind, argv + optind) < 0)
137433d6423SLionel Sambuc 		exit(1);
138433d6423SLionel Sambuc 	else
139433d6423SLionel Sambuc 		exit(0);
140433d6423SLionel Sambuc   }
141433d6423SLionel Sambuc   if (usedrop) sprintf(mailbox, DROPNAME, whoami());
142433d6423SLionel Sambuc 
143433d6423SLionel Sambuc   D(printf("mailbox=%s\n", mailbox));
144433d6423SLionel Sambuc 
145433d6423SLionel Sambuc   if (msgstatus) {
146433d6423SLionel Sambuc 	if (filesize(mailbox))
147433d6423SLionel Sambuc 		exit(0);
148433d6423SLionel Sambuc 	else
149433d6423SLionel Sambuc 		exit(1);
150433d6423SLionel Sambuc   }
151433d6423SLionel Sambuc 
152433d6423SLionel Sambuc   readbox();
153433d6423SLionel Sambuc 
154433d6423SLionel Sambuc   if (printmode)
155433d6423SLionel Sambuc 	printall();
156433d6423SLionel Sambuc   else
157433d6423SLionel Sambuc 	interact();
158433d6423SLionel Sambuc 
159433d6423SLionel Sambuc   if (needupdate) updatebox();
160433d6423SLionel Sambuc 
161433d6423SLionel Sambuc   return(0);
162433d6423SLionel Sambuc }
163433d6423SLionel Sambuc 
deliver(count,vec)164433d6423SLionel Sambuc int deliver(count, vec)
165433d6423SLionel Sambuc int count;
166433d6423SLionel Sambuc char *vec[];
167433d6423SLionel Sambuc {
168433d6423SLionel Sambuc   int i, j;
169433d6423SLionel Sambuc   int errs = 0;			/* count of errors */
170433d6423SLionel Sambuc   int dropfd;			/* file descriptor for user's drop */
171433d6423SLionel Sambuc   int created = 0;		/* true if we created the maildrop */
172433d6423SLionel Sambuc   FILE *mailfp;			/* fp for mail */
173433d6423SLionel Sambuc   struct stat stb;		/* for checking drop modes, owners */
174433d6423SLionel Sambuc #ifdef __STDC__
175433d6423SLionel Sambuc   void (*sigint)(int), (*sighup)(int), (*sigquit)(int);/* saving signal state */
176433d6423SLionel Sambuc #else
177433d6423SLionel Sambuc   void (*sigint) (), (*sighup) (), (*sigquit) ();      /* saving signal state */
178433d6423SLionel Sambuc #endif
179433d6423SLionel Sambuc   time_t now;			/* for datestamping the postmark */
180433d6423SLionel Sambuc   char sender[32];		/* sender's login name */
181433d6423SLionel Sambuc   char lockname[PATHLEN];	/* maildrop lock */
182433d6423SLionel Sambuc   int locktries;		/* tries when box is locked */
183433d6423SLionel Sambuc   struct passwd *pw;		/* sender and recipent */
184433d6423SLionel Sambuc   int to_console;		/* deliver to console if everything fails */
185433d6423SLionel Sambuc 
186433d6423SLionel Sambuc   if (count > MAXRCPT) {
187433d6423SLionel Sambuc 	fprintf(stderr, "mail: too many recipients\n");
188433d6423SLionel Sambuc 	return -1;
189433d6423SLionel Sambuc   }
190433d6423SLionel Sambuc #ifdef MAILER
191433d6423SLionel Sambuc   if (usemailer) {
192433d6423SLionel Sambuc 	char *argvec[MAXRCPT + 3];
193433d6423SLionel Sambuc 	char **argp;
194433d6423SLionel Sambuc 
195433d6423SLionel Sambuc 	setuid(getuid());
196433d6423SLionel Sambuc 
197433d6423SLionel Sambuc 	argp = argvec;
198433d6423SLionel Sambuc 	*argp++ = "send-mail";
199433d6423SLionel Sambuc 	if (verbose) *argp++ = "-v";
200433d6423SLionel Sambuc 
201433d6423SLionel Sambuc 	for (i = 0; i < count; ++i) *argp++ = vec[i];
202433d6423SLionel Sambuc 
203433d6423SLionel Sambuc 	*argp = NULL;
204433d6423SLionel Sambuc 	execv(MAILER, argvec);
205433d6423SLionel Sambuc 	fprintf(stderr, "mail: couldn't exec %s\n", MAILER);
206433d6423SLionel Sambuc 	return -1;
207433d6423SLionel Sambuc   }
208433d6423SLionel Sambuc #endif /* MAILER */
209433d6423SLionel Sambuc 
210433d6423SLionel Sambuc   if (NULL == (pw = getpwuid(getuid()))) {
211433d6423SLionel Sambuc 	fprintf(stderr, "mail: unknown sender\n");
212433d6423SLionel Sambuc 	return -1;
213433d6423SLionel Sambuc   }
214433d6423SLionel Sambuc   strcpy(sender, pw->pw_name);
215433d6423SLionel Sambuc 
216433d6423SLionel Sambuc   /* If we need to rewind stdin and it isn't rewindable, make a copy */
217433d6423SLionel Sambuc   if (isatty(0) || (count > 1 && lseek(0, 0L, 0) == (off_t) -1)) {
218433d6423SLionel Sambuc 	mailfp = makerewindable();
219433d6423SLionel Sambuc   } else
220433d6423SLionel Sambuc 	mailfp = stdin;
221433d6423SLionel Sambuc 
222433d6423SLionel Sambuc   /* Shut off signals during the delivery */
223433d6423SLionel Sambuc   sigint = signal(SIGINT, SIG_IGN);
224433d6423SLionel Sambuc   sighup = signal(SIGHUP, SIG_IGN);
225433d6423SLionel Sambuc   sigquit = signal(SIGQUIT, SIG_IGN);
226433d6423SLionel Sambuc 
227433d6423SLionel Sambuc   for (i = 0; i < count; ++i) {
228433d6423SLionel Sambuc 	if (count > 1) rewind(mailfp);
229433d6423SLionel Sambuc 
230433d6423SLionel Sambuc 	D(printf("deliver to %s\n", vec[i]));
231433d6423SLionel Sambuc 
232433d6423SLionel Sambuc 	if (NULL == (pw = getpwnam(vec[i]))) {
233433d6423SLionel Sambuc 		fprintf(stderr, "mail: user %s not known\n", vec[i]);
234433d6423SLionel Sambuc 		++errs;
235433d6423SLionel Sambuc 		continue;
236433d6423SLionel Sambuc 	}
237433d6423SLionel Sambuc 	sprintf(mailbox, DROPNAME, pw->pw_name);
238433d6423SLionel Sambuc 	sprintf(lockname, LOCKNAME, pw->pw_name);
239433d6423SLionel Sambuc 
240433d6423SLionel Sambuc 	D(printf("maildrop='%s', lock='%s'\n", mailbox, lockname));
241433d6423SLionel Sambuc 
242433d6423SLionel Sambuc 	/* Lock the maildrop while we're messing with it. Races are
243433d6423SLionel Sambuc 	 * possible (though not very likely) when we have to create
244433d6423SLionel Sambuc 	 * the maildrop, but not otherwise. If the box is already
245433d6423SLionel Sambuc 	 * locked, wait awhile and try again. */
246433d6423SLionel Sambuc 	locktries = created = to_console = 0;
247433d6423SLionel Sambuc trylock:
248433d6423SLionel Sambuc 	if (link(mailbox, lockname) != 0) {
249433d6423SLionel Sambuc 		if (ENOENT == errno) {	/* user doesn't have a drop yet */
250433d6423SLionel Sambuc 			dropfd = creat(mailbox, 0600);
251433d6423SLionel Sambuc 			if (dropfd < 0 && errno == ENOENT) {
252433d6423SLionel Sambuc 				/* Probably missing spool dir; to console. */
253433d6423SLionel Sambuc 				boxfp = fopen("/dev/console", "w");
254433d6423SLionel Sambuc 				if (boxfp != NULL) {
255433d6423SLionel Sambuc 					to_console = 1;
256433d6423SLionel Sambuc 					goto nobox;
257433d6423SLionel Sambuc 				}
258433d6423SLionel Sambuc 			}
259433d6423SLionel Sambuc 			if (dropfd < 0) {
260433d6423SLionel Sambuc 				fprintf(stderr, "mail: couln't create a maildrop for user %s\n",
261433d6423SLionel Sambuc 					vec[i]);
262433d6423SLionel Sambuc 				++errs;
263433d6423SLionel Sambuc 				continue;
264433d6423SLionel Sambuc 			}
265433d6423SLionel Sambuc 			++created;
266433d6423SLionel Sambuc 			goto trylock;
267433d6423SLionel Sambuc 		} else {	/* somebody else has it locked, it seems -
268433d6423SLionel Sambuc 			 * wait */
269433d6423SLionel Sambuc 			if (++locktries >= LOCKTRIES) {
270433d6423SLionel Sambuc 				fprintf(stderr, "mail: couldn't lock maildrop for user %s\n",
271433d6423SLionel Sambuc 					vec[i]);
272433d6423SLionel Sambuc 				++errs;
273433d6423SLionel Sambuc 				continue;
274433d6423SLionel Sambuc 			}
275433d6423SLionel Sambuc 			sleep(LOCKWAIT);
276433d6423SLionel Sambuc 			goto trylock;
277433d6423SLionel Sambuc 		}
278433d6423SLionel Sambuc 	}
279433d6423SLionel Sambuc 	if (created) {
280433d6423SLionel Sambuc 		(void) chown(mailbox, pw->pw_uid, pw->pw_gid);
281433d6423SLionel Sambuc 		boxfp = fdopen(dropfd, "a");
282433d6423SLionel Sambuc 	} else
283433d6423SLionel Sambuc 		boxfp = fopen(mailbox, "a");
284433d6423SLionel Sambuc 
285433d6423SLionel Sambuc 	if (NULL == boxfp || stat(mailbox, &stb) < 0) {
286433d6423SLionel Sambuc 		fprintf(stderr, "mail: serious maildrop problems for %s\n", vec[i]);
287433d6423SLionel Sambuc 		unlink(lockname);
288433d6423SLionel Sambuc 		++errs;
289433d6423SLionel Sambuc 		continue;
290433d6423SLionel Sambuc 	}
291433d6423SLionel Sambuc 	if (stb.st_uid != pw->pw_uid || (stb.st_mode & S_IFMT) != S_IFREG) {
292433d6423SLionel Sambuc 		fprintf(stderr, "mail: mailbox for user %s is illegal\n", vec[i]);
293433d6423SLionel Sambuc 		unlink(lockname);
294433d6423SLionel Sambuc 		++errs;
295433d6423SLionel Sambuc 		continue;
296433d6423SLionel Sambuc 	}
297433d6423SLionel Sambuc nobox:
298433d6423SLionel Sambuc 	if (to_console) {
299433d6423SLionel Sambuc 		fprintf(boxfp,
300433d6423SLionel Sambuc 			"-------------\n| Mail from %s to %s\n-------------\n",
301433d6423SLionel Sambuc 			sender, vec[i]);
302433d6423SLionel Sambuc 	} else {
303433d6423SLionel Sambuc 		(void) time(&now);
304433d6423SLionel Sambuc 		fprintf(boxfp, "From %s %24.24s\n", sender, ctime(&now));
305433d6423SLionel Sambuc 	}
306433d6423SLionel Sambuc 
307433d6423SLionel Sambuc 	/* Add the To: header line */
308433d6423SLionel Sambuc 	fprintf(boxfp, "To: %s\n", vec[i]);
309433d6423SLionel Sambuc 
310433d6423SLionel Sambuc 	if (distlist) {
311433d6423SLionel Sambuc 		fprintf(boxfp, "Dist: ");
312433d6423SLionel Sambuc 		for (j = 0; j < count; ++j)
313433d6423SLionel Sambuc 			if (getpwnam(vec[j]) != NULL && j != i)
314433d6423SLionel Sambuc 				fprintf(boxfp, "%s ", vec[j]) ;
315433d6423SLionel Sambuc 		fprintf(boxfp, "\n");
316433d6423SLionel Sambuc 	}
317433d6423SLionel Sambuc 
318433d6423SLionel Sambuc 	/* Add the Subject: header line */
319433d6423SLionel Sambuc 	if (subject != NULL) fprintf(boxfp, "Subject: %s\n", subject);
320433d6423SLionel Sambuc 
321433d6423SLionel Sambuc 	fprintf(boxfp, "\n");
322433d6423SLionel Sambuc 
323433d6423SLionel Sambuc 	if ((copy(mailfp, boxfp) < 0) || (fclose(boxfp) != 0)) {
324433d6423SLionel Sambuc 		fprintf(stderr, "mail: error delivering to user %s", vec[i]);
325433d6423SLionel Sambuc 		perror(" ");
326433d6423SLionel Sambuc 		++errs;
327433d6423SLionel Sambuc 	}
328433d6423SLionel Sambuc 	unlink(lockname);
329433d6423SLionel Sambuc   }
330433d6423SLionel Sambuc 
331433d6423SLionel Sambuc   fclose(mailfp);
332433d6423SLionel Sambuc 
333433d6423SLionel Sambuc   /* Put signals back the way they were */
334433d6423SLionel Sambuc   signal(SIGINT, sigint);
335433d6423SLionel Sambuc   signal(SIGHUP, sighup);
336433d6423SLionel Sambuc   signal(SIGQUIT, sigquit);
337433d6423SLionel Sambuc 
338433d6423SLionel Sambuc   return(0 == errs) ? 0 : -1;
339433d6423SLionel Sambuc }
340433d6423SLionel Sambuc 
341433d6423SLionel Sambuc /* 'stdin' isn't rewindable. Make a temp file that is.
342433d6423SLionel Sambuc  * Note that if one wanted to catch SIGINT and write a '~/dead.letter'
343433d6423SLionel Sambuc  * for interactive mails, this might be the place to do it (though the
344433d6423SLionel Sambuc  * case where a MAILER is being used would also need to be handled).
345433d6423SLionel Sambuc  */
makerewindable()346433d6423SLionel Sambuc FILE *makerewindable()
347433d6423SLionel Sambuc {
348433d6423SLionel Sambuc   FILE *tempfp;			/* temp file used for copy */
349433d6423SLionel Sambuc   int c;			/* character being copied */
350433d6423SLionel Sambuc   int state;			/* ".\n" detection state */
351433d6423SLionel Sambuc 
352433d6423SLionel Sambuc   if (NULL == (tempfp = fopen(tempname, "w"))) {
353433d6423SLionel Sambuc 	fprintf(stderr, "mail: can't create temporary file\n");
354433d6423SLionel Sambuc 	return NULL;
355433d6423SLionel Sambuc   }
356433d6423SLionel Sambuc 
357433d6423SLionel Sambuc   /* Here we copy until we reach the end of the letter (end of file or
358433d6423SLionel Sambuc    * a line containing only a '.'), painstakingly avoiding setting a
359433d6423SLionel Sambuc    * line length limit. */
360433d6423SLionel Sambuc   state = '\n';
361433d6423SLionel Sambuc   while (EOF != (c = getc(stdin))) switch (state) {
362433d6423SLionel Sambuc 	    case '\n':
363433d6423SLionel Sambuc 		if ('.' == c)
364433d6423SLionel Sambuc 			state = '.';
365433d6423SLionel Sambuc 		else {
366433d6423SLionel Sambuc 			if ('\n' != c) state = '\0';
367433d6423SLionel Sambuc 			putc(c, tempfp);
368433d6423SLionel Sambuc 		}
369433d6423SLionel Sambuc 		break;
370433d6423SLionel Sambuc 	    case '.':
371433d6423SLionel Sambuc 		if ('\n' == c) goto done;
372433d6423SLionel Sambuc 		state = '\0';
373433d6423SLionel Sambuc 		putc('.', tempfp);
374433d6423SLionel Sambuc 		putc(c, tempfp);
375433d6423SLionel Sambuc 		break;
376433d6423SLionel Sambuc 	    default:
377433d6423SLionel Sambuc 		state = ('\n' == c) ? '\n' : '\0';
378433d6423SLionel Sambuc 		putc(c, tempfp);
379433d6423SLionel Sambuc 	}
380433d6423SLionel Sambuc done:
381433d6423SLionel Sambuc   if (ferror(tempfp) || fclose(tempfp)) {
382433d6423SLionel Sambuc 	fprintf(stderr, "mail: couldn't copy letter to temporary file\n");
383433d6423SLionel Sambuc 	return NULL;
384433d6423SLionel Sambuc   }
385433d6423SLionel Sambuc   tempfp = freopen(tempname, "r", stdin);
386433d6423SLionel Sambuc   unlink(tempname);		/* unlink name; file lingers on in limbo */
387433d6423SLionel Sambuc   return tempfp;
388433d6423SLionel Sambuc }
389433d6423SLionel Sambuc 
copy(fromfp,tofp)390433d6423SLionel Sambuc int copy(fromfp, tofp)
391433d6423SLionel Sambuc FILE *fromfp, *tofp;
392433d6423SLionel Sambuc {
393433d6423SLionel Sambuc   int c;			/* character being copied */
394433d6423SLionel Sambuc   int state;			/* ".\n" and postmark detection state */
395433d6423SLionel Sambuc   int blankline = 0;		/* was most recent line completely blank? */
396433d6423SLionel Sambuc   static char postmark[] = "From ";
397433d6423SLionel Sambuc   char *p, *q;
398433d6423SLionel Sambuc 
399433d6423SLionel Sambuc   /* Here we copy until we reach the end of the letter (end of file or
400433d6423SLionel Sambuc    * a line containing only a '.'). Postmarks (lines beginning with
401433d6423SLionel Sambuc    * "From ") are copied with a ">" prepended. Here we also complicate
402433d6423SLionel Sambuc    * things by not setting a line limit. */
403433d6423SLionel Sambuc   state = '\n';
404433d6423SLionel Sambuc   p = postmark;
405433d6423SLionel Sambuc   while (EOF != (c = getc(fromfp))) {
406433d6423SLionel Sambuc 	switch (state) {
407433d6423SLionel Sambuc 	    case '\n':
408433d6423SLionel Sambuc 		if ('.' == c)	/* '.' at BOL */
409433d6423SLionel Sambuc 			state = '.';
410433d6423SLionel Sambuc 		else if (*p == c) {	/* start of postmark */
411433d6423SLionel Sambuc 			++p;
412433d6423SLionel Sambuc 			state = 'P';
413433d6423SLionel Sambuc 		} else {	/* anything else */
414433d6423SLionel Sambuc 			if ('\n' == c)
415433d6423SLionel Sambuc 				blankline = 1;
416433d6423SLionel Sambuc 			else {
417433d6423SLionel Sambuc 				state = '\0';
418433d6423SLionel Sambuc 				blankline = 0;
419433d6423SLionel Sambuc 			}
420433d6423SLionel Sambuc 			putc(c, tofp);
421433d6423SLionel Sambuc 		}
422433d6423SLionel Sambuc 		break;
423433d6423SLionel Sambuc 	    case '.':
424433d6423SLionel Sambuc 		if ('\n' == c) goto done;
425433d6423SLionel Sambuc 		state = '\0';
426433d6423SLionel Sambuc 		putc('.', tofp);
427433d6423SLionel Sambuc 		putc(c, tofp);
428433d6423SLionel Sambuc 		break;
429433d6423SLionel Sambuc 	    case 'P':
430433d6423SLionel Sambuc 		if (*p == c) {
431433d6423SLionel Sambuc 			if (*++p == '\0') {	/* successfully reached end */
432433d6423SLionel Sambuc 				p = postmark;
433433d6423SLionel Sambuc 				putc('>', tofp);
434433d6423SLionel Sambuc 				fputs(postmark, tofp);
435433d6423SLionel Sambuc 				state = '\0';
436433d6423SLionel Sambuc 				break;
437433d6423SLionel Sambuc 			}
438433d6423SLionel Sambuc 			break;	/* not there yet */
439433d6423SLionel Sambuc 		}
440433d6423SLionel Sambuc 		state = ('\n' == c) ? '\n' : '\0';
441433d6423SLionel Sambuc 		for (q = postmark; q < p; ++q) putc(*q, tofp);
442433d6423SLionel Sambuc 		putc(c, tofp);
443433d6423SLionel Sambuc 		blankline = 0;
444433d6423SLionel Sambuc 		p = postmark;
445433d6423SLionel Sambuc 		break;
446433d6423SLionel Sambuc 	    default:
447433d6423SLionel Sambuc 		state = ('\n' == c) ? '\n' : '\0';
448433d6423SLionel Sambuc 		putc(c, tofp);
449433d6423SLionel Sambuc 	}
450433d6423SLionel Sambuc   }
451433d6423SLionel Sambuc   if ('\n' != state) putc('\n', tofp);
452433d6423SLionel Sambuc done:
453433d6423SLionel Sambuc   if (!blankline) putc('\n', tofp);
454433d6423SLionel Sambuc   if (ferror(tofp)) return -1;
455433d6423SLionel Sambuc   return 0;
456433d6423SLionel Sambuc }
457433d6423SLionel Sambuc 
readbox()458433d6423SLionel Sambuc void readbox()
459433d6423SLionel Sambuc {
460433d6423SLionel Sambuc   char linebuf[512];
461433d6423SLionel Sambuc   struct letter *let;
462433d6423SLionel Sambuc   off_t current;
463433d6423SLionel Sambuc 
464433d6423SLionel Sambuc   firstlet = lastlet = NULL;
465433d6423SLionel Sambuc 
466433d6423SLionel Sambuc   if (access(mailbox, 4) < 0 || NULL == (boxfp = fopen(mailbox, "r"))) {
467433d6423SLionel Sambuc 	if (usedrop && ENOENT == errno) return;
468433d6423SLionel Sambuc 	fprintf(stderr, "can't access mailbox ");
469433d6423SLionel Sambuc 	perror(mailbox);
470433d6423SLionel Sambuc 	exit(1);
471433d6423SLionel Sambuc   }
472433d6423SLionel Sambuc   current = 0L;
473433d6423SLionel Sambuc   while (1) {
474433d6423SLionel Sambuc 	if (NULL == fgets(linebuf, sizeof linebuf, boxfp)) break;
475433d6423SLionel Sambuc 
476433d6423SLionel Sambuc 	if (!strncmp(linebuf, "From ", (size_t)5)) {
477433d6423SLionel Sambuc 		if (NULL == (let = (struct letter *) malloc(sizeof(struct letter)))) {
478433d6423SLionel Sambuc 			fprintf(stderr, "Out of memory.\n");
479433d6423SLionel Sambuc 			exit(1);
480433d6423SLionel Sambuc 		}
481433d6423SLionel Sambuc 		if (NULL == lastlet) {
482433d6423SLionel Sambuc 			firstlet = let;
483433d6423SLionel Sambuc 			let->prev = NULL;
484433d6423SLionel Sambuc 		} else {
485433d6423SLionel Sambuc 			let->prev = lastlet;
486433d6423SLionel Sambuc 			lastlet->next = let;
487433d6423SLionel Sambuc 		}
488433d6423SLionel Sambuc 		lastlet = let;
489433d6423SLionel Sambuc 		let->next = NULL;
490433d6423SLionel Sambuc 
491433d6423SLionel Sambuc 		let->status = UNREAD;
492433d6423SLionel Sambuc 		let->location = current;
493433d6423SLionel Sambuc 		D(printf("letter at %ld\n", current));
494433d6423SLionel Sambuc 	}
495433d6423SLionel Sambuc 	current += strlen(linebuf);
496433d6423SLionel Sambuc   }
497433d6423SLionel Sambuc }
498433d6423SLionel Sambuc 
printall()499433d6423SLionel Sambuc void printall()
500433d6423SLionel Sambuc {
501433d6423SLionel Sambuc   struct letter *let;
502433d6423SLionel Sambuc 
503433d6423SLionel Sambuc   let = reversemode ? firstlet : lastlet;
504433d6423SLionel Sambuc 
505433d6423SLionel Sambuc   if (NULL == let) {
506433d6423SLionel Sambuc 	printf("No mail.\n");
507433d6423SLionel Sambuc 	return;
508433d6423SLionel Sambuc   }
509433d6423SLionel Sambuc   while (NULL != let) {
510433d6423SLionel Sambuc 	printlet(let, stdout);
511433d6423SLionel Sambuc 	let = reversemode ? let->next : let->prev;
512433d6423SLionel Sambuc   }
513433d6423SLionel Sambuc }
514433d6423SLionel Sambuc 
interact()515433d6423SLionel Sambuc void interact()
516433d6423SLionel Sambuc {
517433d6423SLionel Sambuc   char linebuf[512];		/* user input line */
518433d6423SLionel Sambuc   struct letter *let, *next;	/* current and next letter */
519433d6423SLionel Sambuc   int interrupted = 0;		/* SIGINT hit during letter print */
520433d6423SLionel Sambuc   int needprint = 1;		/* need to print this letter */
521433d6423SLionel Sambuc   char *savefile;		/* filename to save into */
522433d6423SLionel Sambuc 
523433d6423SLionel Sambuc   if (NULL == firstlet) {
524433d6423SLionel Sambuc 	printf("No mail.\n");
525433d6423SLionel Sambuc 	return;
526433d6423SLionel Sambuc   }
527433d6423SLionel Sambuc   let = reversemode ? firstlet : lastlet;
528433d6423SLionel Sambuc 
529433d6423SLionel Sambuc   while (1) {
530433d6423SLionel Sambuc 	next = reversemode ? let->next : let->prev;
531433d6423SLionel Sambuc 	if (NULL == next) next = let;
532433d6423SLionel Sambuc 
533433d6423SLionel Sambuc 	if (!quitmode) {
534433d6423SLionel Sambuc 		interrupted = setjmp(printjump);
535433d6423SLionel Sambuc 		signal(SIGINT, onint);
536433d6423SLionel Sambuc 	}
537433d6423SLionel Sambuc 	if (!interrupted && needprint) {
538433d6423SLionel Sambuc 		if (DELETED != let->status) let->status = READ;
539433d6423SLionel Sambuc 		printlet(let, stdout);
540433d6423SLionel Sambuc 	}
541433d6423SLionel Sambuc 	if (interrupted) putchar('\n');
542433d6423SLionel Sambuc 	needprint = 0;
543433d6423SLionel Sambuc 	fputs(PROMPT, stdout);
544433d6423SLionel Sambuc 	fflush(stdout);
545433d6423SLionel Sambuc 
546433d6423SLionel Sambuc 	if (fgets(linebuf, sizeof linebuf, stdin) == NULL) break;
547433d6423SLionel Sambuc 
548433d6423SLionel Sambuc 	if (!quitmode) signal(SIGINT, SIG_IGN);
549433d6423SLionel Sambuc 
550433d6423SLionel Sambuc 	switch (linebuf[0]) {
551433d6423SLionel Sambuc 	    case '\n':
552433d6423SLionel Sambuc 		let = next;
553433d6423SLionel Sambuc 		needprint = 1;
554433d6423SLionel Sambuc 		continue;
555433d6423SLionel Sambuc 	    case 'd':
556433d6423SLionel Sambuc 		let->status = DELETED;
557433d6423SLionel Sambuc 		if (next != let)/* look into this */
558433d6423SLionel Sambuc 			needprint = 1;
559433d6423SLionel Sambuc 		needupdate = 1;
560433d6423SLionel Sambuc 		let = next;
561433d6423SLionel Sambuc 		continue;
562433d6423SLionel Sambuc 	    case 'p':
563433d6423SLionel Sambuc 		needprint = 1;
564433d6423SLionel Sambuc 		continue;
565433d6423SLionel Sambuc 	    case '-':
566433d6423SLionel Sambuc 		next = reversemode ? let->prev : let->next;
567433d6423SLionel Sambuc 		if (NULL == next) next = let;
568433d6423SLionel Sambuc 		let = next;
569433d6423SLionel Sambuc 		needprint = 1;
570433d6423SLionel Sambuc 		continue;
571433d6423SLionel Sambuc 	    case 's':
572433d6423SLionel Sambuc 		for (savefile = strtok(linebuf + 1, " \t\n");
573433d6423SLionel Sambuc 		     savefile != NULL;
574433d6423SLionel Sambuc 		     savefile = strtok((char *) NULL, " \t\n")) {
575433d6423SLionel Sambuc 			savelet(let, savefile);
576433d6423SLionel Sambuc 		}
577433d6423SLionel Sambuc 		continue;
578433d6423SLionel Sambuc 	    case '!':
579433d6423SLionel Sambuc 		doshell(linebuf + 1);
580433d6423SLionel Sambuc 		continue;
581433d6423SLionel Sambuc 	    case '*':
582433d6423SLionel Sambuc 		dohelp();
583433d6423SLionel Sambuc 		continue;
584433d6423SLionel Sambuc 	    case 'q':
585433d6423SLionel Sambuc 		return;
586433d6423SLionel Sambuc 	    case 'x':
587433d6423SLionel Sambuc 		exit(0);
588433d6423SLionel Sambuc 	    default:
589433d6423SLionel Sambuc 		fprintf(stderr, "Illegal command\n");
590433d6423SLionel Sambuc 		continue;
591433d6423SLionel Sambuc 	}
592433d6423SLionel Sambuc   }
593433d6423SLionel Sambuc }
594433d6423SLionel Sambuc 
onint(dummy)595433d6423SLionel Sambuc void onint(dummy)
596433d6423SLionel Sambuc int dummy;	/* to satisfy ANSI compilers */
597433d6423SLionel Sambuc {
598433d6423SLionel Sambuc   longjmp(printjump, 1);
599433d6423SLionel Sambuc }
600433d6423SLionel Sambuc 
savelet(let,savefile)601433d6423SLionel Sambuc void savelet(let, savefile)
602433d6423SLionel Sambuc struct letter *let;
603433d6423SLionel Sambuc char *savefile;
604433d6423SLionel Sambuc {
605433d6423SLionel Sambuc   int waitstat, pid;
606433d6423SLionel Sambuc   FILE *savefp;
607433d6423SLionel Sambuc 
608433d6423SLionel Sambuc   if ((pid = fork()) < 0) {
609433d6423SLionel Sambuc 	perror("mail: couldn't fork");
610433d6423SLionel Sambuc 	return;
611433d6423SLionel Sambuc   } else if (pid != 0) {	/* parent */
612433d6423SLionel Sambuc 	wait(&waitstat);
613433d6423SLionel Sambuc 	return;
614433d6423SLionel Sambuc   }
615433d6423SLionel Sambuc 
616433d6423SLionel Sambuc   /* Child */
617433d6423SLionel Sambuc   setgid(getgid());
618433d6423SLionel Sambuc   setuid(getuid());
619433d6423SLionel Sambuc   if ((savefp = fopen(savefile, "a")) == NULL) {
620433d6423SLionel Sambuc 	perror(savefile);
621433d6423SLionel Sambuc 	exit(0);
622433d6423SLionel Sambuc   }
623433d6423SLionel Sambuc   printlet(let, savefp);
624433d6423SLionel Sambuc   if ((ferror(savefp) != 0) | (fclose(savefp) != 0)) {
625433d6423SLionel Sambuc 	fprintf(stderr, "savefile write error:");
626433d6423SLionel Sambuc 	perror(savefile);
627433d6423SLionel Sambuc   }
628433d6423SLionel Sambuc   exit(0);
629433d6423SLionel Sambuc }
630433d6423SLionel Sambuc 
updatebox()631433d6423SLionel Sambuc void updatebox()
632433d6423SLionel Sambuc {
633433d6423SLionel Sambuc   FILE *tempfp;			/* fp for tempfile */
634433d6423SLionel Sambuc   char lockname[PATHLEN];	/* maildrop lock */
635433d6423SLionel Sambuc   int locktries = 0;		/* tries when box is locked */
636433d6423SLionel Sambuc   struct letter *let;		/* current letter */
637433d6423SLionel Sambuc   int c;
638433d6423SLionel Sambuc 
639433d6423SLionel Sambuc   sprintf(lockname, LOCKNAME, whoami());
640433d6423SLionel Sambuc 
641433d6423SLionel Sambuc   if (NULL == (tempfp = fopen(tempname, "w"))) {
642433d6423SLionel Sambuc 	perror("mail: can't create temporary file");
643433d6423SLionel Sambuc 	return;
644433d6423SLionel Sambuc   }
645433d6423SLionel Sambuc   for (let = firstlet; let != NULL; let = let->next) {
646433d6423SLionel Sambuc 	if (let->status != DELETED) {
647433d6423SLionel Sambuc 		printlet(let, tempfp);
648433d6423SLionel Sambuc 		D(printf("printed letter at %ld\n", let->location));
649433d6423SLionel Sambuc 	}
650433d6423SLionel Sambuc   }
651433d6423SLionel Sambuc 
652433d6423SLionel Sambuc   if (ferror(tempfp) || NULL == (tempfp = freopen(tempname, "r", tempfp))) {
653433d6423SLionel Sambuc 	perror("mail: temporary file write error");
654433d6423SLionel Sambuc 	unlink(tempname);
655433d6423SLionel Sambuc 	return;
656433d6423SLionel Sambuc   }
657433d6423SLionel Sambuc 
658433d6423SLionel Sambuc   /* Shut off signals during the update */
659433d6423SLionel Sambuc   signal(SIGINT, SIG_IGN);
660433d6423SLionel Sambuc   signal(SIGHUP, SIG_IGN);
661433d6423SLionel Sambuc   signal(SIGQUIT, SIG_IGN);
662433d6423SLionel Sambuc 
663433d6423SLionel Sambuc   if (usedrop) while (link(mailbox, lockname) != 0) {
664433d6423SLionel Sambuc 		if (++locktries >= LOCKTRIES) {
665433d6423SLionel Sambuc 			fprintf(stderr, "mail: couldn't lock maildrop for update\n");
666433d6423SLionel Sambuc 			return;
667433d6423SLionel Sambuc 		}
668433d6423SLionel Sambuc 		sleep(LOCKWAIT);
669433d6423SLionel Sambuc 	}
670433d6423SLionel Sambuc 
671433d6423SLionel Sambuc   if (NULL == (boxfp = freopen(mailbox, "w", boxfp))) {
672433d6423SLionel Sambuc 	perror("mail: couldn't reopen maildrop");
673433d6423SLionel Sambuc 	fprintf(stderr, "mail may have been lost; look in %s\n", tempname);
674433d6423SLionel Sambuc 	if (usedrop) unlink(lockname);
675433d6423SLionel Sambuc 	return;
676433d6423SLionel Sambuc   }
677433d6423SLionel Sambuc   unlink(tempname);
678433d6423SLionel Sambuc 
679433d6423SLionel Sambuc   while ((c = getc(tempfp)) != EOF) putc(c, boxfp);
680433d6423SLionel Sambuc 
681433d6423SLionel Sambuc   fclose(boxfp);
682433d6423SLionel Sambuc 
683433d6423SLionel Sambuc   if (usedrop) unlink(lockname);
684433d6423SLionel Sambuc }
685433d6423SLionel Sambuc 
printlet(let,tofp)686433d6423SLionel Sambuc void printlet(let, tofp)
687433d6423SLionel Sambuc struct letter *let;
688433d6423SLionel Sambuc FILE *tofp;
689433d6423SLionel Sambuc {
690433d6423SLionel Sambuc   off_t current, limit;
691433d6423SLionel Sambuc   int c;
692433d6423SLionel Sambuc 
693433d6423SLionel Sambuc   fseek(boxfp, (current = let->location), 0);
694433d6423SLionel Sambuc   limit = (NULL != let->next) ? let->next->location : -1;
695433d6423SLionel Sambuc 
696433d6423SLionel Sambuc   while (current != limit && (c = getc(boxfp)) != EOF) {
697433d6423SLionel Sambuc 	putc(c, tofp);
698433d6423SLionel Sambuc 	++current;
699433d6423SLionel Sambuc   }
700433d6423SLionel Sambuc }
701433d6423SLionel Sambuc 
doshell(command)702433d6423SLionel Sambuc void doshell(command)
703433d6423SLionel Sambuc char *command;
704433d6423SLionel Sambuc {
705433d6423SLionel Sambuc   int waitstat, pid;
706433d6423SLionel Sambuc   char *shell;
707433d6423SLionel Sambuc 
708433d6423SLionel Sambuc   if (NULL == (shell = getenv("SHELL"))) shell = SHELL;
709433d6423SLionel Sambuc 
710433d6423SLionel Sambuc   if ((pid = fork()) < 0) {
711433d6423SLionel Sambuc 	perror("mail: couldn't fork");
712433d6423SLionel Sambuc 	return;
713433d6423SLionel Sambuc   } else if (pid != 0) {	/* parent */
714433d6423SLionel Sambuc 	wait(&waitstat);
715433d6423SLionel Sambuc 	return;
716433d6423SLionel Sambuc   }
717433d6423SLionel Sambuc 
718433d6423SLionel Sambuc   /* Child */
719433d6423SLionel Sambuc   setgid(getgid());
720433d6423SLionel Sambuc   setuid(getuid());
721433d6423SLionel Sambuc   umask(oldmask);
722433d6423SLionel Sambuc 
723433d6423SLionel Sambuc   execl(shell, shell, "-c", command, (char *) NULL);
724433d6423SLionel Sambuc   fprintf(stderr, "can't exec shell\n");
725433d6423SLionel Sambuc   exit(127);
726433d6423SLionel Sambuc }
727433d6423SLionel Sambuc 
usage()728433d6423SLionel Sambuc void usage()
729433d6423SLionel Sambuc {
730433d6423SLionel Sambuc   fprintf(stderr, "usage: mail [-epqr] [-f file]\n");
731433d6423SLionel Sambuc   fprintf(stderr, "       mail [-dtv] [-s subject] user [...]\n");
732433d6423SLionel Sambuc }
733433d6423SLionel Sambuc 
basename(name)734433d6423SLionel Sambuc char *basename(name)
735433d6423SLionel Sambuc char *name;
736433d6423SLionel Sambuc {
737433d6423SLionel Sambuc   char *p;
738433d6423SLionel Sambuc 
739433d6423SLionel Sambuc   if (NULL == (p = rindex(name, '/')))
740433d6423SLionel Sambuc 	return name;
741433d6423SLionel Sambuc   else
742433d6423SLionel Sambuc 	return p + 1;
743433d6423SLionel Sambuc }
744433d6423SLionel Sambuc 
whoami()745433d6423SLionel Sambuc char *whoami()
746433d6423SLionel Sambuc {
747433d6423SLionel Sambuc   struct passwd *pw;
748433d6423SLionel Sambuc 
749433d6423SLionel Sambuc   if (NULL != (pw = getpwuid(getuid())))
750433d6423SLionel Sambuc 	return pw->pw_name;
751433d6423SLionel Sambuc   else
752433d6423SLionel Sambuc 	return "nobody";
753433d6423SLionel Sambuc }
754433d6423SLionel Sambuc 
dohelp()755433d6423SLionel Sambuc void dohelp()
756433d6423SLionel Sambuc {
757433d6423SLionel Sambuc   FILE *fp;
758433d6423SLionel Sambuc   char buffer[80];
759433d6423SLionel Sambuc 
760*b8894990SThomas Cort   if ( (fp = fopen(HELPFILE, "r")) == NULL) {
761433d6423SLionel Sambuc 	fprintf(stdout, "can't open helpfile %s\n", HELPFILE);
762*b8894990SThomas Cort 	return;
763*b8894990SThomas Cort   }
764*b8894990SThomas Cort 
765433d6423SLionel Sambuc   while (fgets(buffer, 80, fp))
766433d6423SLionel Sambuc 	fputs(buffer, stdout);
767*b8894990SThomas Cort 
768*b8894990SThomas Cort   fclose(fp);
769433d6423SLionel Sambuc }
770433d6423SLionel Sambuc 
filesize(name)771433d6423SLionel Sambuc int filesize(name)
772433d6423SLionel Sambuc char *name ;
773433d6423SLionel Sambuc {
774433d6423SLionel Sambuc   struct stat buf;
775433d6423SLionel Sambuc 
776433d6423SLionel Sambuc   if (stat(name, &buf) == -1)
777433d6423SLionel Sambuc 	buf.st_size = 0L;
778433d6423SLionel Sambuc 
779433d6423SLionel Sambuc   return (buf.st_size ? 1 : 0);
780433d6423SLionel Sambuc }
781