1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23/*	  All Rights Reserved  	*/
24/*
25 * Copyright (c) 2016 by Delphix. All rights reserved.
26 */
27
28/*
29 * University Copyright- Copyright (c) 1982, 1986, 1988
30 * The Regents of the University of California
31 * All Rights Reserved
32 *
33 * University Acknowledgment- Portions of this document are derived from
34 * software developed by the University of California, Berkeley, and its
35 * contributors.
36 */
37
38#pragma ident	"%Z%%M%	%I%	%E% SMI"
39
40#include "rcv.h"
41#include <locale.h>
42
43/*
44 * mailx -- a modified version of a University of California at Berkeley
45 *	mail program
46 *
47 * Rcv -- receive mail rationally.
48 *
49 * Termination processing.
50 */
51
52static void		writeback(int noremove);
53
54#define PRIV(x)		setgid(myegid), (x), setgid(myrgid);
55
56/*
57 * Save all of the undetermined messages at the top of "mbox"
58 * Save all untouched messages back in the system mailbox.
59 * Remove the system mailbox, if none saved there.
60 */
61
62void
63quit(
64    int noremove	/* don't remove system mailbox, trunc it instead */
65)
66{
67	int mcount, p, modify, autohold, anystat, holdbit, nohold, fd;
68	FILE *ibuf, *obuf, *fbuf, *readstat;
69	register struct message *mp;
70	register int c;
71	char *id;
72	int appending;
73	char *mbox = Getf("MBOX");
74
75	/*
76	 * If we are read only, we can't do anything,
77	 * so just return quickly.
78	 */
79
80	mcount = 0;
81	if (readonly)
82		return;
83	/*
84	 * See if there any messages to save in mbox.  If no, we
85	 * can save copying mbox to /tmp and back.
86	 *
87	 * Check also to see if any files need to be preserved.
88	 * Delete all untouched messages to keep them out of mbox.
89	 * If all the messages are to be preserved, just exit with
90	 * a message.
91	 *
92	 * If the luser has sent mail to himself, refuse to do
93	 * anything with the mailbox, unless mail locking works.
94	 */
95
96#ifndef CANLOCK
97	if (selfsent) {
98		printf(gettext("You have new mail.\n"));
99		return;
100	}
101#endif
102
103	/*
104	 * Adjust the message flags in each message.
105	 */
106
107	anystat = 0;
108	autohold = value("hold") != NOSTR;
109	appending = value("append") != NOSTR;
110	holdbit = autohold ? MPRESERVE : MBOX;
111	nohold = MBOXED|MBOX|MSAVED|MDELETED|MPRESERVE;
112	if (value("keepsave") != NOSTR)
113		nohold &= ~MSAVED;
114	for (mp = &message[0]; mp < &message[msgCount]; mp++) {
115		if (mp->m_flag & MNEW) {
116			receipt(mp);
117			mp->m_flag &= ~MNEW;
118			mp->m_flag |= MSTATUS;
119		}
120		if (mp->m_flag & MSTATUS)
121			anystat++;
122		if ((mp->m_flag & MTOUCH) == 0)
123			mp->m_flag |= MPRESERVE;
124		if ((mp->m_flag & nohold) == 0)
125			mp->m_flag |= holdbit;
126	}
127	modify = 0;
128	if (Tflag != NOSTR) {
129		if ((readstat = fopen(Tflag, "w")) == NULL)
130			Tflag = NOSTR;
131	}
132	for (c = 0, p = 0, mp = &message[0]; mp < &message[msgCount]; mp++) {
133		if (mp->m_flag & MBOX)
134			c++;
135		if (mp->m_flag & MPRESERVE)
136			p++;
137		if (mp->m_flag & MODIFY)
138			modify++;
139		if (Tflag != NOSTR && (mp->m_flag & (MREAD|MDELETED)) != 0) {
140			id = hfield("message-id", mp, addone);
141			if (id != NOSTR)
142				fprintf(readstat, "%s\n", id);
143			else {
144				id = hfield("article-id", mp, addone);
145				if (id != NOSTR)
146					fprintf(readstat, "%s\n", id);
147			}
148		}
149	}
150	if (Tflag != NOSTR)
151		fclose(readstat);
152	if (p == msgCount && !modify && !anystat) {
153		if (p == 1)
154			printf(gettext("Held 1 message in %s\n"), mailname);
155		else
156			printf(gettext("Held %d messages in %s\n"), p,
157			    mailname);
158		return;
159	}
160	if (c == 0) {
161		writeback(noremove);
162		return;
163	}
164
165	/*
166	 * Create another temporary file and copy user's mbox file
167	 * therein.  If there is no mbox, copy nothing.
168	 * If they have specified "append" don't copy the mailbox,
169	 * just copy saveable entries at the end.
170	 */
171
172	mcount = c;
173	if (!appending) {
174		if ((fd = open(tempQuit, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 ||
175		(obuf = fdopen(fd, "w")) == NULL) {
176			perror(tempQuit);
177			return;
178		}
179		if ((ibuf = fopen(tempQuit, "r")) == NULL) {
180			perror(tempQuit);
181			removefile(tempQuit);
182			fclose(obuf);
183			return;
184		}
185		removefile(tempQuit);
186		if ((fbuf = fopen(mbox, "r")) != NULL) {
187			while ((c = getc(fbuf)) != EOF)
188				putc(c, obuf);
189			fclose(fbuf);
190		}
191		fflush(obuf);
192		if (fferror(obuf)) {
193			perror(tempQuit);
194			fclose(ibuf);
195			fclose(obuf);
196			return;
197		}
198		fclose(obuf);
199		if ((fd = open(mbox, O_RDWR|O_CREAT|O_TRUNC, MBOXPERM)) < 0 ||
200		    (obuf = fdopen(fd, "r+")) == NULL) {
201			perror(mbox);
202			fclose(ibuf);
203			return;
204		}
205		if (issysmbox)
206			touchlock();
207	} else {	/* we are appending */
208		if ((fd = open(mbox, O_RDWR|O_CREAT, MBOXPERM)) < 0 ||
209		    (obuf = fdopen(fd, "a")) == NULL) {
210			perror(mbox);
211			return;
212		}
213	}
214	for (mp = &message[0]; mp < &message[msgCount]; mp++)
215		if (mp->m_flag & MBOX) {
216			if (msend(mp, obuf, (int)value("alwaysignore") ?
217			    M_IGNORE|M_SAVING : M_SAVING, fputs) < 0) {
218				perror(mbox);
219				if (!appending)
220					fclose(ibuf);
221				fclose(obuf);
222				return;
223			}
224			mp->m_flag &= ~MBOX;
225			mp->m_flag |= MBOXED;
226			if (issysmbox)
227				touchlock();
228		}
229
230	/*
231	 * Copy the user's old mbox contents back
232	 * to the end of the stuff we just saved.
233	 * If we are appending, this is unnecessary.
234	 */
235
236	if (!appending) {
237		rewind(ibuf);
238		c = getc(ibuf);
239		while (c != EOF) {
240			putc(c, obuf);
241			if (ferror(obuf))
242				break;
243			c = getc(ibuf);
244		}
245		fclose(ibuf);
246		fflush(obuf);
247	}
248	trunc(obuf);
249	if (fferror(obuf)) {
250		perror(mbox);
251		fclose(obuf);
252		return;
253	}
254	fclose(obuf);
255	if (mcount == 1)
256		printf(gettext("Saved 1 message in %s\n"), mbox);
257	else
258		printf(gettext("Saved %d messages in %s\n"), mcount, mbox);
259
260	/*
261	 * Now we are ready to copy back preserved files to
262	 * the system mailbox, if any were requested.
263	 */
264	writeback(noremove);
265}
266
267/*
268 * Preserve all the appropriate messages back in the system
269 * mailbox, and print a nice message indicating how many were
270 * saved.  Incorporate any new mail that we found.
271 */
272static void
273writeback(int noremove)
274{
275	register struct message *mp;
276	register int p, c;
277	struct stat st;
278	FILE *obuf = 0, *fbuf = 0, *rbuf = 0;
279	void (*fhup)(int), (*fint)(int), (*fquit)(int);
280	int fd = -1;
281
282	fhup = sigset(SIGHUP, SIG_IGN);
283	fint = sigset(SIGINT, SIG_IGN);
284	fquit = sigset(SIGQUIT, SIG_IGN);
285
286	if (issysmbox)
287		lockmail();
288	if ((fbuf = fopen(mailname, "r+")) == NULL) {
289		perror(mailname);
290		goto die;
291	}
292	if (!issysmbox)
293		lock(fbuf, "r+", 1);
294	fstat(fileno(fbuf), &st);
295	if (st.st_size > mailsize) {
296		printf(gettext("New mail has arrived.\n"));
297		snprintf(tempResid, PATHSIZE, "%s/:saved/%s", maildir, myname);
298		PRIV(rbuf = fopen(tempResid, "w+"));
299		if (rbuf == NULL) {
300			snprintf(tempResid, PATHSIZE, "/tmp/Rq%-ld", mypid);
301			fd = open(tempResid,O_RDWR|O_CREAT|O_EXCL, 0600);
302			PRIV(rbuf = fdopen(fd, "w+"));
303			if (rbuf == NULL) {
304				snprintf(tempResid, PATHSIZE,
305					"%s/:saved/%s", maildir,
306				    myname);
307				perror(tempResid);
308				fclose(fbuf);
309				goto die;
310			}
311		}
312#ifdef APPEND
313		fseek(fbuf, mailsize, 0);
314		while ((c = getc(fbuf)) != EOF)
315			putc(c, rbuf);
316#else
317		p = st.st_size - mailsize;
318		while (p-- > 0) {
319			c = getc(fbuf);
320			if (c == EOF) {
321				perror(mailname);
322				fclose(fbuf);
323				goto die;
324			}
325			putc(c, rbuf);
326		}
327#endif
328		fclose(fbuf);
329		fseek(rbuf, 0L, 0);
330		if (issysmbox)
331			touchlock();
332	}
333
334	if ((obuf = fopen(mailname, "r+")) == NULL) {
335		perror(mailname);
336		goto die;
337	}
338#ifndef APPEND
339	if (rbuf != NULL)
340		while ((c = getc(rbuf)) != EOF)
341			putc(c, obuf);
342#endif
343	p = 0;
344	for (mp = &message[0]; mp < &message[msgCount]; mp++)
345		if ((mp->m_flag&MPRESERVE)||(mp->m_flag&MTOUCH)==0) {
346			p++;
347			if (msend(mp, obuf, 0, fputs) < 0) {
348				perror(mailname);
349				goto die;
350			}
351			if (issysmbox)
352				touchlock();
353		}
354#ifdef APPEND
355	if (rbuf != NULL)
356		while ((c = getc(rbuf)) != EOF)
357			putc(c, obuf);
358#endif
359	fflush(obuf);
360	trunc(obuf);
361	if (fferror(obuf)) {
362		perror(mailname);
363		goto die;
364	}
365	alter(mailname);
366	if (p) {
367		if (p == 1)
368			printf(gettext("Held 1 message in %s\n"), mailname);
369		else
370			printf(gettext("Held %d messages in %s\n"), p,
371			    mailname);
372	}
373
374	if (!noremove && (fsize(obuf) == 0) && (value("keep") == NOSTR)) {
375		if (stat(mailname, &st) >= 0)
376			PRIV(delempty(st.st_mode, mailname));
377	}
378
379die:
380	if (rbuf) {
381		fclose(rbuf);
382		PRIV(removefile(tempResid));
383	}
384	if (obuf)
385		fclose(obuf);
386	if (issysmbox)
387		unlockmail();
388	sigset(SIGHUP, fhup);
389	sigset(SIGINT, fint);
390	sigset(SIGQUIT, fquit);
391}
392
393void
394lockmail(void)
395{
396    PRIV(maillock(lockname,10));
397}
398
399void
400unlockmail(void)
401{
402    PRIV(mailunlock());
403}
404