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