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