1239275Sgonzo/*-
2239275Sgonzo * SPDX-License-Identifier: BSD-3-Clause
3239275Sgonzo *
4239275Sgonzo * Copyright (c) 1980, 1993
5239275Sgonzo *	The Regents of the University of California.  All rights reserved.
6239275Sgonzo *
7239275Sgonzo * Redistribution and use in source and binary forms, with or without
8239275Sgonzo * modification, are permitted provided that the following conditions
9239275Sgonzo * are met:
10239275Sgonzo * 1. Redistributions of source code must retain the above copyright
11239275Sgonzo *    notice, this list of conditions and the following disclaimer.
12239275Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
13239275Sgonzo *    notice, this list of conditions and the following disclaimer in the
14239275Sgonzo *    documentation and/or other materials provided with the distribution.
15239275Sgonzo * 3. Neither the name of the University nor the names of its contributors
16239275Sgonzo *    may be used to endorse or promote products derived from this software
17239275Sgonzo *    without specific prior written permission.
18239275Sgonzo *
19239275Sgonzo * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20239275Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21239275Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22239275Sgonzo * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23239275Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24239275Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25239275Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26239275Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27239275Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28239275Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29239275Sgonzo * SUCH DAMAGE.
30239275Sgonzo */
31239275Sgonzo
32239275Sgonzo#include "rcv.h"
33239275Sgonzo#include <fcntl.h>
34239275Sgonzo#include "extern.h"
35239275Sgonzo
36239275Sgonzo/*
37239275Sgonzo * Rcv -- receive mail rationally.
38239275Sgonzo *
39239275Sgonzo * Termination processing.
40239275Sgonzo */
41239275Sgonzo
42239275Sgonzo/*
43239275Sgonzo * The "quit" command.
44239275Sgonzo */
45239275Sgonzoint
46239275Sgonzoquitcmd(void *arg __unused)
47239275Sgonzo{
48239275Sgonzo	/*
49239275Sgonzo	 * If we are sourcing, then return 1 so execute() can handle it.
50239275Sgonzo	 * Otherwise, return -1 to abort command loop.
51239275Sgonzo	 */
52239275Sgonzo	if (sourcing)
53239275Sgonzo		return (1);
54239275Sgonzo	return (-1);
55239275Sgonzo}
56239275Sgonzo
57239275Sgonzo/*
58239275Sgonzo * Save all of the undetermined messages at the top of "mbox"
59239275Sgonzo * Save all untouched messages back in the system mailbox.
60239275Sgonzo * Remove the system mailbox, if none saved there.
61239275Sgonzo */
62239275Sgonzovoid
63239275Sgonzoquit(void)
64239275Sgonzo{
65239275Sgonzo	int mcount, p, modify, autohold, anystat, holdbit, nohold;
66239275Sgonzo	FILE *ibuf, *obuf, *fbuf, *rbuf, *readstat, *abuf;
67239275Sgonzo	struct message *mp;
68239275Sgonzo	int c, fd;
69239275Sgonzo	struct stat minfo;
70239275Sgonzo	char *mbox, tempname[PATHSIZE];
71239275Sgonzo
72239275Sgonzo	/*
73239275Sgonzo	 * If we are read only, we can't do anything,
74239275Sgonzo	 * so just return quickly.
75239275Sgonzo	 */
76239275Sgonzo	if (readonly)
77239275Sgonzo		return;
78239275Sgonzo	/*
79239275Sgonzo	 * If editing (not reading system mail box), then do the work
80239275Sgonzo	 * in edstop()
81239275Sgonzo	 */
82239275Sgonzo	if (edit) {
83239275Sgonzo		edstop();
84239275Sgonzo		return;
85239275Sgonzo	}
86239275Sgonzo
87239275Sgonzo	/*
88239275Sgonzo	 * See if there any messages to save in mbox.  If no, we
89239275Sgonzo	 * can save copying mbox to /tmp and back.
90239275Sgonzo	 *
91239275Sgonzo	 * Check also to see if any files need to be preserved.
92239275Sgonzo	 * Delete all untouched messages to keep them out of mbox.
93239275Sgonzo	 * If all the messages are to be preserved, just exit with
94239275Sgonzo	 * a message.
95239275Sgonzo	 */
96239275Sgonzo
97239275Sgonzo	fbuf = Fopen(mailname, "r");
98239275Sgonzo	if (fbuf == NULL)
99239275Sgonzo		goto newmail;
100239275Sgonzo	(void)flock(fileno(fbuf), LOCK_EX);
101239275Sgonzo	rbuf = NULL;
102239275Sgonzo	if (fstat(fileno(fbuf), &minfo) >= 0 && minfo.st_size > mailsize) {
103239275Sgonzo		printf("New mail has arrived.\n");
104239275Sgonzo		(void)snprintf(tempname, sizeof(tempname),
105239275Sgonzo		    "%s/mail.RqXXXXXXXXXX", tmpdir);
106239275Sgonzo		if ((fd = mkstemp(tempname)) == -1 ||
107239275Sgonzo		    (rbuf = Fdopen(fd, "w")) == NULL)
108239275Sgonzo			goto newmail;
109239275Sgonzo#ifdef APPEND
110239275Sgonzo		(void)fseeko(fbuf, mailsize, SEEK_SET);
111239275Sgonzo		while ((c = getc(fbuf)) != EOF)
112239275Sgonzo			(void)putc(c, rbuf);
113239275Sgonzo#else
114239275Sgonzo		p = minfo.st_size - mailsize;
115239275Sgonzo		while (p-- > 0) {
116239275Sgonzo			c = getc(fbuf);
117239275Sgonzo			if (c == EOF)
118239275Sgonzo				goto newmail;
119239275Sgonzo			(void)putc(c, rbuf);
120239275Sgonzo		}
121239275Sgonzo#endif
122239275Sgonzo		(void)Fclose(rbuf);
123239275Sgonzo		if ((rbuf = Fopen(tempname, "r")) == NULL)
124239275Sgonzo			goto newmail;
125239275Sgonzo		(void)rm(tempname);
126239275Sgonzo	}
127239275Sgonzo
128239275Sgonzo	/*
129239275Sgonzo	 * Adjust the message flags in each message.
130239275Sgonzo	 */
131239275Sgonzo
132239275Sgonzo	anystat = 0;
133239275Sgonzo	autohold = value("hold") != NULL;
134239275Sgonzo	holdbit = autohold ? MPRESERVE : MBOX;
135239275Sgonzo	nohold = MBOX|MSAVED|MDELETED|MPRESERVE;
136239275Sgonzo	if (value("keepsave") != NULL)
137239275Sgonzo		nohold &= ~MSAVED;
138239275Sgonzo	for (mp = &message[0]; mp < &message[msgCount]; mp++) {
139239275Sgonzo		if (mp->m_flag & MNEW) {
140239275Sgonzo			mp->m_flag &= ~MNEW;
141239275Sgonzo			mp->m_flag |= MSTATUS;
142239275Sgonzo		}
143239275Sgonzo		if (mp->m_flag & MSTATUS)
144239275Sgonzo			anystat++;
145239275Sgonzo		if ((mp->m_flag & MTOUCH) == 0)
146239275Sgonzo			mp->m_flag |= MPRESERVE;
147239275Sgonzo		if ((mp->m_flag & nohold) == 0)
148239275Sgonzo			mp->m_flag |= holdbit;
149239275Sgonzo	}
150239275Sgonzo	modify = 0;
151239275Sgonzo	if (Tflag != NULL) {
152239275Sgonzo		if ((readstat = Fopen(Tflag, "w")) == NULL)
153239275Sgonzo			Tflag = NULL;
154239275Sgonzo	}
155239275Sgonzo	for (c = 0, p = 0, mp = &message[0]; mp < &message[msgCount]; mp++) {
156239275Sgonzo		if (mp->m_flag & MBOX)
157239275Sgonzo			c++;
158239275Sgonzo		if (mp->m_flag & MPRESERVE)
159239275Sgonzo			p++;
160239275Sgonzo		if (mp->m_flag & MODIFY)
161239275Sgonzo			modify++;
162239275Sgonzo		if (Tflag != NULL && (mp->m_flag & (MREAD|MDELETED)) != 0) {
163239275Sgonzo			char *id;
164239275Sgonzo
165239275Sgonzo			if ((id = hfield("article-id", mp)) != NULL)
166239275Sgonzo				fprintf(readstat, "%s\n", id);
167239275Sgonzo		}
168239275Sgonzo	}
169239275Sgonzo	if (Tflag != NULL)
170239275Sgonzo		(void)Fclose(readstat);
171239275Sgonzo	if (p == msgCount && !modify && !anystat) {
172239275Sgonzo		printf("Held %d message%s in %s\n",
173239275Sgonzo			p, p == 1 ? "" : "s", mailname);
174239275Sgonzo		(void)Fclose(fbuf);
175239275Sgonzo		return;
176239275Sgonzo	}
177239275Sgonzo	if (c == 0) {
178239275Sgonzo		if (p != 0) {
179239275Sgonzo			writeback(rbuf);
180239275Sgonzo			(void)Fclose(fbuf);
181239275Sgonzo			return;
182239275Sgonzo		}
183239275Sgonzo		goto cream;
184239275Sgonzo	}
185239275Sgonzo
186239275Sgonzo	/*
187239275Sgonzo	 * Create another temporary file and copy user's mbox file
188239275Sgonzo	 * darin.  If there is no mbox, copy nothing.
189239275Sgonzo	 * If he has specified "append" don't copy his mailbox,
190239275Sgonzo	 * just copy saveable entries at the end.
191239275Sgonzo	 */
192239275Sgonzo
193239275Sgonzo	mbox = expand("&");
194239275Sgonzo	mcount = c;
195239275Sgonzo	if (value("append") == NULL) {
196239275Sgonzo		(void)snprintf(tempname, sizeof(tempname),
197239275Sgonzo		    "%s/mail.RmXXXXXXXXXX", tmpdir);
198239275Sgonzo		if ((fd = mkstemp(tempname)) == -1 ||
199239275Sgonzo		    (obuf = Fdopen(fd, "w")) == NULL) {
200239275Sgonzo			warn("%s", tempname);
201239275Sgonzo			(void)Fclose(fbuf);
202239275Sgonzo			return;
203239275Sgonzo		}
204239275Sgonzo		if ((ibuf = Fopen(tempname, "r")) == NULL) {
205239275Sgonzo			warn("%s", tempname);
206239275Sgonzo			(void)rm(tempname);
207239275Sgonzo			(void)Fclose(obuf);
208239275Sgonzo			(void)Fclose(fbuf);
209239275Sgonzo			return;
210239275Sgonzo		}
211239275Sgonzo		(void)rm(tempname);
212239275Sgonzo		if ((abuf = Fopen(mbox, "r")) != NULL) {
213239275Sgonzo			while ((c = getc(abuf)) != EOF)
214239275Sgonzo				(void)putc(c, obuf);
215239275Sgonzo			(void)Fclose(abuf);
216239275Sgonzo		}
217239275Sgonzo		if (ferror(obuf)) {
218239275Sgonzo			warnx("%s", tempname);
219239275Sgonzo			(void)Fclose(ibuf);
220239275Sgonzo			(void)Fclose(obuf);
221239275Sgonzo			(void)Fclose(fbuf);
222239275Sgonzo			return;
223239275Sgonzo		}
224239275Sgonzo		(void)Fclose(obuf);
225239275Sgonzo		if ((fd = open(mbox, O_CREAT | O_TRUNC | O_WRONLY, 0600)) >= 0)
226239275Sgonzo			(void)close(fd);
227239275Sgonzo		if ((obuf = Fopen(mbox, "r+")) == NULL) {
228239275Sgonzo			warn("%s", mbox);
229239275Sgonzo			(void)Fclose(ibuf);
230239275Sgonzo			(void)Fclose(fbuf);
231239275Sgonzo			return;
232239275Sgonzo		}
233239275Sgonzo	}
234239275Sgonzo	if (value("append") != NULL) {
235239275Sgonzo		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