1/*
2 * Copyright (c) 1980, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 4. Neither the name of the University nor the names of its contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#ifndef lint
31#if 0
32static char sccsid[] = "@(#)quit.c	8.2 (Berkeley) 4/28/95";
33#endif
34#endif /* not lint */
35#include <sys/cdefs.h>
36__FBSDID("$FreeBSD$");
37
38#include "rcv.h"
39#include <fcntl.h>
40#include "extern.h"
41
42/*
43 * Rcv -- receive mail rationally.
44 *
45 * Termination processing.
46 */
47
48/*
49 * The "quit" command.
50 */
51int
52quitcmd(void)
53{
54	/*
55	 * If we are sourcing, then return 1 so execute() can handle it.
56	 * Otherwise, return -1 to abort command loop.
57	 */
58	if (sourcing)
59		return (1);
60	return (-1);
61}
62
63/*
64 * Save all of the undetermined messages at the top of "mbox"
65 * Save all untouched messages back in the system mailbox.
66 * Remove the system mailbox, if none saved there.
67 */
68void
69quit(void)
70{
71	int mcount, p, modify, autohold, anystat, holdbit, nohold;
72	FILE *ibuf, *obuf, *fbuf, *rbuf, *readstat, *abuf;
73	struct message *mp;
74	int c, fd;
75	struct stat minfo;
76	char *mbox, tempname[PATHSIZE];
77
78	/*
79	 * If we are read only, we can't do anything,
80	 * so just return quickly.
81	 */
82	if (readonly)
83		return;
84	/*
85	 * If editing (not reading system mail box), then do the work
86	 * in edstop()
87	 */
88	if (edit) {
89		edstop();
90		return;
91	}
92
93	/*
94	 * See if there any messages to save in mbox.  If no, we
95	 * can save copying mbox to /tmp and back.
96	 *
97	 * Check also to see if any files need to be preserved.
98	 * Delete all untouched messages to keep them out of mbox.
99	 * If all the messages are to be preserved, just exit with
100	 * a message.
101	 */
102
103	fbuf = Fopen(mailname, "r");
104	if (fbuf == NULL)
105		goto newmail;
106	(void)flock(fileno(fbuf), LOCK_EX);
107	rbuf = NULL;
108	if (fstat(fileno(fbuf), &minfo) >= 0 && minfo.st_size > mailsize) {
109		printf("New mail has arrived.\n");
110		(void)snprintf(tempname, sizeof(tempname),
111		    "%s/mail.RqXXXXXXXXXX", tmpdir);
112		if ((fd = mkstemp(tempname)) == -1 ||
113		    (rbuf = Fdopen(fd, "w")) == NULL)
114			goto newmail;
115#ifdef APPEND
116		(void)fseeko(fbuf, mailsize, SEEK_SET);
117		while ((c = getc(fbuf)) != EOF)
118			(void)putc(c, rbuf);
119#else
120		p = minfo.st_size - mailsize;
121		while (p-- > 0) {
122			c = getc(fbuf);
123			if (c == EOF)
124				goto newmail;
125			(void)putc(c, rbuf);
126		}
127#endif
128		(void)Fclose(rbuf);
129		if ((rbuf = Fopen(tempname, "r")) == NULL)
130			goto newmail;
131		(void)rm(tempname);
132	}
133
134	/*
135	 * Adjust the message flags in each message.
136	 */
137
138	anystat = 0;
139	autohold = value("hold") != NULL;
140	holdbit = autohold ? MPRESERVE : MBOX;
141	nohold = MBOX|MSAVED|MDELETED|MPRESERVE;
142	if (value("keepsave") != NULL)
143		nohold &= ~MSAVED;
144	for (mp = &message[0]; mp < &message[msgCount]; mp++) {
145		if (mp->m_flag & MNEW) {
146			mp->m_flag &= ~MNEW;
147			mp->m_flag |= MSTATUS;
148		}
149		if (mp->m_flag & MSTATUS)
150			anystat++;
151		if ((mp->m_flag & MTOUCH) == 0)
152			mp->m_flag |= MPRESERVE;
153		if ((mp->m_flag & nohold) == 0)
154			mp->m_flag |= holdbit;
155	}
156	modify = 0;
157	if (Tflag != NULL) {
158		if ((readstat = Fopen(Tflag, "w")) == NULL)
159			Tflag = NULL;
160	}
161	for (c = 0, p = 0, mp = &message[0]; mp < &message[msgCount]; mp++) {
162		if (mp->m_flag & MBOX)
163			c++;
164		if (mp->m_flag & MPRESERVE)
165			p++;
166		if (mp->m_flag & MODIFY)
167			modify++;
168		if (Tflag != NULL && (mp->m_flag & (MREAD|MDELETED)) != 0) {
169			char *id;
170
171			if ((id = hfield("article-id", mp)) != NULL)
172				fprintf(readstat, "%s\n", id);
173		}
174	}
175	if (Tflag != NULL)
176		(void)Fclose(readstat);
177	if (p == msgCount && !modify && !anystat) {
178		printf("Held %d message%s in %s\n",
179			p, p == 1 ? "" : "s", mailname);
180		(void)Fclose(fbuf);
181		return;
182	}
183	if (c == 0) {
184		if (p != 0) {
185			writeback(rbuf);
186			(void)Fclose(fbuf);
187			return;
188		}
189		goto cream;
190	}
191
192	/*
193	 * Create another temporary file and copy user's mbox file
194	 * darin.  If there is no mbox, copy nothing.
195	 * If he has specified "append" don't copy his mailbox,
196	 * just copy saveable entries at the end.
197	 */
198
199	mbox = expand("&");
200	mcount = c;
201	if (value("append") == NULL) {
202		(void)snprintf(tempname, sizeof(tempname),
203		    "%s/mail.RmXXXXXXXXXX", tmpdir);
204		if ((fd = mkstemp(tempname)) == -1 ||
205		    (obuf = Fdopen(fd, "w")) == NULL) {
206			warn("%s", tempname);
207			(void)Fclose(fbuf);
208			return;
209		}
210		if ((ibuf = Fopen(tempname, "r")) == NULL) {
211			warn("%s", tempname);
212			(void)rm(tempname);
213			(void)Fclose(obuf);
214			(void)Fclose(fbuf);
215			return;
216		}
217		(void)rm(tempname);
218		if ((abuf = Fopen(mbox, "r")) != NULL) {
219			while ((c = getc(abuf)) != EOF)
220				(void)putc(c, obuf);
221			(void)Fclose(abuf);
222		}
223		if (ferror(obuf)) {
224			warnx("%s", tempname);
225			(void)Fclose(ibuf);
226			(void)Fclose(obuf);
227			(void)Fclose(fbuf);
228			return;
229		}
230		(void)Fclose(obuf);
231		if ((fd = open(mbox, O_CREAT | O_TRUNC | O_WRONLY, 0600)) >= 0)
232			(void)close(fd);
233		if ((obuf = Fopen(mbox, "r+")) == NULL) {
234			warn("%s", mbox);
235			(void)Fclose(ibuf);
236			(void)Fclose(fbuf);
237			return;
238		}
239	}
240	if (value("append") != NULL) {
241		if ((obuf = Fopen(mbox, "a")) == NULL) {
242			warn("%s", mbox);
243			(void)Fclose(fbuf);
244			return;
245		}
246		(void)fchmod(fileno(obuf), 0600);
247	}
248	for (mp = &message[0]; mp < &message[msgCount]; mp++)
249		if (mp->m_flag & MBOX)
250			if (sendmessage(mp, obuf, saveignore, NULL) < 0) {
251				warnx("%s", mbox);
252				(void)Fclose(ibuf);
253				(void)Fclose(obuf);
254				(void)Fclose(fbuf);
255				return;
256			}
257
258	/*
259	 * Copy the user's old mbox contents back
260	 * to the end of the stuff we just saved.
261	 * If we are appending, this is unnecessary.
262	 */
263
264	if (value("append") == NULL) {
265		rewind(ibuf);
266		c = getc(ibuf);
267		while (c != EOF) {
268			(void)putc(c, obuf);
269			if (ferror(obuf))
270				break;
271			c = getc(ibuf);
272		}
273		(void)Fclose(ibuf);
274	}
275	(void)fflush(obuf);
276	trunc(obuf);
277	if (ferror(obuf)) {
278		warn("%s", mbox);
279		(void)Fclose(obuf);
280		(void)Fclose(fbuf);
281		return;
282	}
283	(void)Fclose(obuf);
284	if (mcount == 1)
285		printf("Saved 1 message in mbox\n");
286	else
287		printf("Saved %d messages in mbox\n", mcount);
288
289	/*
290	 * Now we are ready to copy back preserved files to
291	 * the system mailbox, if any were requested.
292	 */
293
294	if (p != 0) {
295		writeback(rbuf);
296		(void)Fclose(fbuf);
297		return;
298	}
299
300	/*
301	 * Finally, remove his /var/mail file.
302	 * If new mail has arrived, copy it back.
303	 */
304
305cream:
306	if (rbuf != NULL) {
307		abuf = Fopen(mailname, "r+");
308		if (abuf == NULL)
309			goto newmail;
310		while ((c = getc(rbuf)) != EOF)
311			(void)putc(c, abuf);
312		(void)Fclose(rbuf);
313		trunc(abuf);
314		(void)Fclose(abuf);
315		alter(mailname);
316		(void)Fclose(fbuf);
317		return;
318	}
319	demail();
320	(void)Fclose(fbuf);
321	return;
322
323newmail:
324	printf("Thou hast new mail.\n");
325	if (fbuf != NULL)
326		(void)Fclose(fbuf);
327}
328
329/*
330 * Preserve all the appropriate messages back in the system
331 * mailbox, and print a nice message indicated how many were
332 * saved.  On any error, just return -1.  Else return 0.
333 * Incorporate the any new mail that we found.
334 */
335int
336writeback(FILE *res)
337{
338	struct message *mp;
339	int p, c;
340	FILE *obuf;
341
342	p = 0;
343	if ((obuf = Fopen(mailname, "r+")) == NULL) {
344		warn("%s", mailname);
345		return (-1);
346	}
347#ifndef APPEND
348	if (res != NULL)
349		while ((c = getc(res)) != EOF)
350			(void)putc(c, obuf);
351#endif
352	for (mp = &message[0]; mp < &message[msgCount]; mp++)
353		if ((mp->m_flag&MPRESERVE)||(mp->m_flag&MTOUCH)==0) {
354			p++;
355			if (sendmessage(mp, obuf, NULL, NULL) < 0) {
356				warnx("%s", mailname);
357				(void)Fclose(obuf);
358				return (-1);
359			}
360		}
361#ifdef APPEND
362	if (res != NULL)
363		while ((c = getc(res)) != EOF)
364			(void)putc(c, obuf);
365#endif
366	(void)fflush(obuf);
367	trunc(obuf);
368	if (ferror(obuf)) {
369		warn("%s", mailname);
370		(void)Fclose(obuf);
371		return (-1);
372	}
373	if (res != NULL)
374		(void)Fclose(res);
375	(void)Fclose(obuf);
376	alter(mailname);
377	if (p == 1)
378		printf("Held 1 message in %s\n", mailname);
379	else
380		printf("Held %d messages in %s\n", p, mailname);
381	return (0);
382}
383
384/*
385 * Terminate an editing session by attempting to write out the user's
386 * file from the temporary.  Save any new stuff appended to the file.
387 */
388void
389edstop(void)
390{
391	int gotcha, c;
392	struct message *mp;
393	FILE *obuf, *ibuf, *readstat;
394	struct stat statb;
395	char tempname[PATHSIZE];
396
397	if (readonly)
398		return;
399	holdsigs();
400	if (Tflag != NULL) {
401		if ((readstat = Fopen(Tflag, "w")) == NULL)
402			Tflag = NULL;
403	}
404	for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++) {
405		if (mp->m_flag & MNEW) {
406			mp->m_flag &= ~MNEW;
407			mp->m_flag |= MSTATUS;
408		}
409		if (mp->m_flag & (MODIFY|MDELETED|MSTATUS))
410			gotcha++;
411		if (Tflag != NULL && (mp->m_flag & (MREAD|MDELETED)) != 0) {
412			char *id;
413
414			if ((id = hfield("article-id", mp)) != NULL)
415				fprintf(readstat, "%s\n", id);
416		}
417	}
418	if (Tflag != NULL)
419		(void)Fclose(readstat);
420	if (!gotcha || Tflag != NULL)
421		goto done;
422	ibuf = NULL;
423	if (stat(mailname, &statb) >= 0 && statb.st_size > mailsize) {
424		int fd;
425
426		(void)snprintf(tempname, sizeof(tempname),
427		    "%s/mbox.XXXXXXXXXX", tmpdir);
428		if ((fd = mkstemp(tempname)) == -1 ||
429		    (obuf = Fdopen(fd, "w")) == NULL) {
430			warn("%s", tempname);
431			relsesigs();
432			reset(0);
433		}
434		if ((ibuf = Fopen(mailname, "r")) == NULL) {
435			warn("%s", mailname);
436			(void)Fclose(obuf);
437			(void)rm(tempname);
438			relsesigs();
439			reset(0);
440		}
441		(void)fseeko(ibuf, mailsize, SEEK_SET);
442		while ((c = getc(ibuf)) != EOF)
443			(void)putc(c, obuf);
444		(void)Fclose(ibuf);
445		(void)Fclose(obuf);
446		if ((ibuf = Fopen(tempname, "r")) == NULL) {
447			warn("%s", tempname);
448			(void)rm(tempname);
449			relsesigs();
450			reset(0);
451		}
452		(void)rm(tempname);
453	}
454	printf("\"%s\" ", mailname);
455	(void)fflush(stdout);
456	if ((obuf = Fopen(mailname, "r+")) == NULL) {
457		warn("%s", mailname);
458		relsesigs();
459		reset(0);
460	}
461	trunc(obuf);
462	c = 0;
463	for (mp = &message[0]; mp < &message[msgCount]; mp++) {
464		if ((mp->m_flag & MDELETED) != 0)
465			continue;
466		c++;
467		if (sendmessage(mp, obuf, NULL, NULL) < 0) {
468			warnx("%s", mailname);
469			relsesigs();
470			reset(0);
471		}
472	}
473	gotcha = (c == 0 && ibuf == NULL);
474	if (ibuf != NULL) {
475		while ((c = getc(ibuf)) != EOF)
476			(void)putc(c, obuf);
477		(void)Fclose(ibuf);
478	}
479	(void)fflush(obuf);
480	if (ferror(obuf)) {
481		warn("%s", mailname);
482		relsesigs();
483		reset(0);
484	}
485	(void)Fclose(obuf);
486	if (gotcha) {
487		(void)rm(mailname);
488		printf("removed\n");
489	} else
490		printf("complete\n");
491	(void)fflush(stdout);
492
493done:
494	relsesigs();
495}
496