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