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