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