xref: /netbsd/usr.bin/mail/quit.c (revision 6550d01e)
1 /*	$NetBSD: quit.c,v 1.27 2009/04/10 13:08:25 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
35 static char sccsid[] = "@(#)quit.c	8.2 (Berkeley) 4/28/95";
36 #else
37 __RCSID("$NetBSD: quit.c,v 1.27 2009/04/10 13:08:25 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*/
56 PUBLIC int
57 quitcmd(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  */
74 static int
75 writeback(FILE *res)
76 {
77 	struct message *mp;
78 	int p, c;
79 	FILE *obuf;
80 
81 	p = 0;
82 	if ((obuf = Fopen(mailname, "r+")) == 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  */
135 static void
136 edstop(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, "w")) == 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, "w")) == 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, "r")) == 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, "r")) == 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, "r+")) == 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 
254 done:
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  */
264 PUBLIC void
265 quit(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, "r");
311 	if (fbuf == NULL)
312 		goto newmail;
313 	if (flock(fileno(fbuf), LOCK_EX) == -1) {
314 nolock:
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, "w")) == 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, "r")) == 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, "w")) == 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, "w")) == 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, "r")) == 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, "r")) != 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, "r+")) == 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, "a")) == 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 
545 cream:
546 	if (rbuf != NULL) {
547 		abuf = Fopen(mailname, "r+");
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 
573 newmail:
574 	(void)printf("Thou hast new mail.\n");
575 	if (fbuf != NULL) {
576 		(void)Fclose(fbuf);
577 		dot_unlock(mailname);
578 	}
579 }
580