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