xref: /netbsd-src/usr.bin/mail/quit.c (revision 1ca5c1b28139779176bd5c13ad7c5f25c0bcd5f8)
1 /*	$NetBSD: quit.c,v 1.12 2001/02/05 02:07:53 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. 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.12 2001/02/05 02:07:53 christos 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 extern char *tempQuit, *tempResid;
56 
57 /*
58  * The "quit" command.
59  */
60 int
61 quitcmd(v)
62 	void *v;
63 {
64 	/*
65 	 * If we are sourcing, then return 1 so execute() can handle it.
66 	 * Otherwise, return -1 to abort command loop.
67 	 */
68 	if (sourcing)
69 		return 1;
70 	return -1;
71 }
72 
73 /*
74  * Save all of the undetermined messages at the top of "mbox"
75  * Save all untouched messages back in the system mailbox.
76  * Remove the system mailbox, if none saved there.
77  */
78 void
79 quit()
80 {
81 	int mcount, p, modify, autohold, anystat, holdbit, nohold;
82 	FILE *ibuf = NULL, *obuf, *fbuf, *rbuf, *readstat = NULL, *abuf;
83 	struct message *mp;
84 	int c;
85 	struct stat minfo;
86 	char *mbox;
87 
88 #ifdef __GNUC__
89 	obuf = NULL;		/* XXX gcc -Wuninitialized */
90 #endif
91 
92 	/*
93 	 * If we are read only, we can't do anything,
94 	 * so just return quickly.
95 	 */
96 	if (readonly)
97 		return;
98 	/*
99 	 * If editing (not reading system mail box), then do the work
100 	 * in edstop()
101 	 */
102 	if (edit) {
103 		edstop();
104 		return;
105 	}
106 
107 	/*
108 	 * See if there any messages to save in mbox.  If no, we
109 	 * can save copying mbox to /tmp and back.
110 	 *
111 	 * Check also to see if any files need to be preserved.
112 	 * Delete all untouched messages to keep them out of mbox.
113 	 * If all the messages are to be preserved, just exit with
114 	 * a message.
115 	 */
116 
117 	fbuf = Fopen(mailname, "r");
118 	if (fbuf == NULL)
119 		goto newmail;
120 	if (flock(fileno(fbuf), LOCK_EX) == -1) {
121 nolock:
122 		perror("Unable to lock mailbox");
123 		Fclose(fbuf);
124 		return;
125 	}
126 	if (dot_lock(mailname, 1, stdout, ".") == -1)
127 		goto nolock;
128 	rbuf = NULL;
129 	if (fstat(fileno(fbuf), &minfo) >= 0 && minfo.st_size > mailsize) {
130 		printf("New mail has arrived.\n");
131 		rbuf = Fopen(tempResid, "w");
132 		if (rbuf == NULL || fbuf == NULL)
133 			goto newmail;
134 #ifdef APPEND
135 		fseek(fbuf, (long)mailsize, 0);
136 		while ((c = getc(fbuf)) != EOF)
137 			(void) putc(c, rbuf);
138 #else
139 		p = minfo.st_size - mailsize;
140 		while (p-- > 0) {
141 			c = getc(fbuf);
142 			if (c == EOF)
143 				goto newmail;
144 			(void) putc(c, rbuf);
145 		}
146 #endif
147 		(void) fflush(rbuf);
148 		if (ferror(rbuf)) {
149 			perror(tempResid);
150 			Fclose(rbuf);
151 			Fclose(fbuf);
152 			dot_unlock(mailname);
153 			return;
154 		}
155 		Fclose(rbuf);
156 		if ((rbuf = Fopen(tempResid, "r")) == NULL)
157 			goto newmail;
158 		rm(tempResid);
159 	}
160 
161 	/*
162 	 * Adjust the message flags in each message.
163 	 */
164 
165 	anystat = 0;
166 	autohold = value("hold") != NOSTR;
167 	holdbit = autohold ? MPRESERVE : MBOX;
168 	nohold = MBOX|MSAVED|MDELETED|MPRESERVE;
169 	if (value("keepsave") != NOSTR)
170 		nohold &= ~MSAVED;
171 	for (mp = &message[0]; mp < &message[msgCount]; mp++) {
172 		if (mp->m_flag & MNEW) {
173 			mp->m_flag &= ~MNEW;
174 			mp->m_flag |= MSTATUS;
175 		}
176 		if (mp->m_flag & MSTATUS)
177 			anystat++;
178 		if ((mp->m_flag & MTOUCH) == 0)
179 			mp->m_flag |= MPRESERVE;
180 		if ((mp->m_flag & nohold) == 0)
181 			mp->m_flag |= holdbit;
182 	}
183 	modify = 0;
184 	if (Tflag != NOSTR) {
185 		if ((readstat = Fopen(Tflag, "w")) == NULL)
186 			Tflag = NOSTR;
187 	}
188 	for (c = 0, p = 0, mp = &message[0]; mp < &message[msgCount]; mp++) {
189 		if (mp->m_flag & MBOX)
190 			c++;
191 		if (mp->m_flag & MPRESERVE)
192 			p++;
193 		if (mp->m_flag & MODIFY)
194 			modify++;
195 		if (Tflag != NOSTR && (mp->m_flag & (MREAD|MDELETED)) != 0) {
196 			char *id;
197 
198 			if ((id = hfield("article-id", mp)) != NOSTR)
199 				fprintf(readstat, "%s\n", id);
200 		}
201 	}
202 	if (Tflag != NOSTR)
203 		Fclose(readstat);
204 	if (p == msgCount && !modify && !anystat) {
205 		printf("Held %d message%s in %s\n",
206 			p, p == 1 ? "" : "s", mailname);
207 		Fclose(fbuf);
208 		dot_unlock(mailname);
209 		return;
210 	}
211 	if (c == 0) {
212 		if (p != 0) {
213 			writeback(rbuf);
214 			Fclose(fbuf);
215 			dot_unlock(mailname);
216 			return;
217 		}
218 		goto cream;
219 	}
220 
221 	/*
222 	 * Create another temporary file and copy user's mbox file
223 	 * darin.  If there is no mbox, copy nothing.
224 	 * If he has specified "append" don't copy his mailbox,
225 	 * just copy saveable entries at the end.
226 	 */
227 
228 	mbox = expand("&");
229 	mcount = c;
230 	if (value("append") == NOSTR) {
231 		if ((obuf = Fopen(tempQuit, "w")) == NULL) {
232 			perror(tempQuit);
233 			Fclose(fbuf);
234 			dot_unlock(mailname);
235 			return;
236 		}
237 		if ((ibuf = Fopen(tempQuit, "r")) == NULL) {
238 			perror(tempQuit);
239 			rm(tempQuit);
240 			Fclose(obuf);
241 			Fclose(fbuf);
242 			dot_unlock(mailname);
243 			return;
244 		}
245 		rm(tempQuit);
246 		if ((abuf = Fopen(mbox, "r")) != NULL) {
247 			while ((c = getc(abuf)) != EOF)
248 				(void) putc(c, obuf);
249 			Fclose(abuf);
250 		}
251 		if (ferror(obuf)) {
252 			perror(tempQuit);
253 			Fclose(ibuf);
254 			Fclose(obuf);
255 			Fclose(fbuf);
256 			dot_unlock(mailname);
257 			return;
258 		}
259 		Fclose(obuf);
260 		close(creat(mbox, 0600));
261 		if ((obuf = Fopen(mbox, "r+")) == NULL) {
262 			perror(mbox);
263 			Fclose(ibuf);
264 			Fclose(fbuf);
265 			dot_unlock(mailname);
266 			return;
267 		}
268 	}
269 	else {
270 		if ((obuf = Fopen(mbox, "a")) == NULL) {
271 			perror(mbox);
272 			Fclose(fbuf);
273 			dot_unlock(mailname);
274 			return;
275 		}
276 		fchmod(fileno(obuf), 0600);
277 	}
278 	for (mp = &message[0]; mp < &message[msgCount]; mp++)
279 		if (mp->m_flag & MBOX)
280 			if (sendmessage(mp, obuf, saveignore, NOSTR) < 0) {
281 				perror(mbox);
282 				Fclose(ibuf);
283 				Fclose(obuf);
284 				Fclose(fbuf);
285 				dot_unlock(mailname);
286 				return;
287 			}
288 
289 	/*
290 	 * Copy the user's old mbox contents back
291 	 * to the end of the stuff we just saved.
292 	 * If we are appending, this is unnecessary.
293 	 */
294 
295 	if (value("append") == NOSTR) {
296 		rewind(ibuf);
297 		c = getc(ibuf);
298 		while (c != EOF) {
299 			(void) putc(c, obuf);
300 			if (ferror(obuf))
301 				break;
302 			c = getc(ibuf);
303 		}
304 		Fclose(ibuf);
305 	}
306 	fflush(obuf);
307 	if (!ferror(obuf))
308 		trunc(obuf);	/* XXX or should we truncate? */
309 	if (ferror(obuf)) {
310 		perror(mbox);
311 		Fclose(obuf);
312 		Fclose(fbuf);
313 		dot_unlock(mailname);
314 		return;
315 	}
316 	Fclose(obuf);
317 	if (mcount == 1)
318 		printf("Saved 1 message in mbox\n");
319 	else
320 		printf("Saved %d messages in mbox\n", mcount);
321 
322 	/*
323 	 * Now we are ready to copy back preserved files to
324 	 * the system mailbox, if any were requested.
325 	 */
326 
327 	if (p != 0) {
328 		writeback(rbuf);
329 		Fclose(fbuf);
330 		dot_unlock(mailname);
331 		return;
332 	}
333 
334 	/*
335 	 * Finally, remove his /usr/mail file.
336 	 * If new mail has arrived, copy it back.
337 	 */
338 
339 cream:
340 	if (rbuf != NULL) {
341 		abuf = Fopen(mailname, "r+");
342 		if (abuf == NULL)
343 			goto newmail;
344 		while ((c = getc(rbuf)) != EOF)
345 			(void) putc(c, abuf);
346 		(void) fflush(abuf);
347 		if (ferror(abuf)) {
348 			perror(mailname);
349 			Fclose(abuf);
350 			Fclose(fbuf);
351 			dot_unlock(mailname);
352 			return;
353 		}
354 		Fclose(rbuf);
355 		trunc(abuf);
356 		Fclose(abuf);
357 		alter(mailname);
358 		Fclose(fbuf);
359 		dot_unlock(mailname);
360 		return;
361 	}
362 	demail();
363 	Fclose(fbuf);
364 	dot_unlock(mailname);
365 	return;
366 
367 newmail:
368 	printf("Thou hast new mail.\n");
369 	if (fbuf != NULL) {
370 		Fclose(fbuf);
371 		dot_unlock(mailname);
372 	}
373 }
374 
375 /*
376  * Preserve all the appropriate messages back in the system
377  * mailbox, and print a nice message indicated how many were
378  * saved.  On any error, just return -1.  Else return 0.
379  * Incorporate the any new mail that we found.
380  */
381 int
382 writeback(res)
383 	FILE *res;
384 {
385 	struct message *mp;
386 	int p, c;
387 	FILE *obuf;
388 
389 	p = 0;
390 	if ((obuf = Fopen(mailname, "r+")) == NULL) {
391 		perror(mailname);
392 		return(-1);
393 	}
394 #ifndef APPEND
395 	if (res != NULL) {
396 		while ((c = getc(res)) != EOF)
397 			(void) putc(c, obuf);
398 		(void) fflush(obuf);
399 		if (ferror(obuf)) {
400 			perror(mailname);
401 			Fclose(obuf);
402 			return(-1);
403 		}
404 	}
405 #endif
406 	for (mp = &message[0]; mp < &message[msgCount]; mp++)
407 		if ((mp->m_flag&MPRESERVE)||(mp->m_flag&MTOUCH)==0) {
408 			p++;
409 			if (sendmessage(mp, obuf, (struct ignoretab *)0, NOSTR) < 0) {
410 				perror(mailname);
411 				Fclose(obuf);
412 				return(-1);
413 			}
414 		}
415 #ifdef APPEND
416 	if (res != NULL)
417 		while ((c = getc(res)) != EOF)
418 			(void) putc(c, obuf);
419 #endif
420 	fflush(obuf);
421 	if (!ferror(obuf))
422 		trunc(obuf);	/* XXX or should we truncate? */
423 	if (ferror(obuf)) {
424 		perror(mailname);
425 		Fclose(obuf);
426 		return(-1);
427 	}
428 	if (res != NULL)
429 		Fclose(res);
430 	Fclose(obuf);
431 	alter(mailname);
432 	if (p == 1)
433 		printf("Held 1 message in %s\n", mailname);
434 	else
435 		printf("Held %d messages in %s\n", p, mailname);
436 	return(0);
437 }
438 
439 /*
440  * Terminate an editing session by attempting to write out the user's
441  * file from the temporary.  Save any new stuff appended to the file.
442  */
443 void
444 edstop()
445 {
446 	int gotcha, c;
447 	struct message *mp;
448 	FILE *obuf, *ibuf, *readstat = NULL;
449 	struct stat statb;
450 	char *tempname;
451 
452 	if (readonly)
453 		return;
454 	holdsigs();
455 	if (Tflag != NOSTR) {
456 		if ((readstat = Fopen(Tflag, "w")) == NULL)
457 			Tflag = NOSTR;
458 	}
459 	for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++) {
460 		if (mp->m_flag & MNEW) {
461 			mp->m_flag &= ~MNEW;
462 			mp->m_flag |= MSTATUS;
463 		}
464 		if (mp->m_flag & (MODIFY|MDELETED|MSTATUS))
465 			gotcha++;
466 		if (Tflag != NOSTR && (mp->m_flag & (MREAD|MDELETED)) != 0) {
467 			char *id;
468 
469 			if ((id = hfield("article-id", mp)) != NOSTR)
470 				fprintf(readstat, "%s\n", id);
471 		}
472 	}
473 	if (Tflag != NOSTR)
474 		Fclose(readstat);
475 	if (!gotcha || Tflag != NOSTR)
476 		goto done;
477 	ibuf = NULL;
478 	if (stat(mailname, &statb) >= 0 && statb.st_size > mailsize) {
479 		tempname = tempnam(tmpdir, "mbox");
480 
481 		if ((obuf = Fopen(tempname, "w")) == NULL) {
482 			perror(tempname);
483 			relsesigs();
484 			reset(0);
485 		}
486 		if ((ibuf = Fopen(mailname, "r")) == NULL) {
487 			perror(mailname);
488 			Fclose(obuf);
489 			rm(tempname);
490 			relsesigs();
491 			reset(0);
492 		}
493 		fseek(ibuf, (long)mailsize, 0);
494 		while ((c = getc(ibuf)) != EOF)
495 			(void) putc(c, obuf);
496 		(void) fflush(obuf);
497 		if (ferror(obuf)) {
498 			perror(tempname);
499 			Fclose(obuf);
500 			Fclose(ibuf);
501 			rm(tempname);
502 			relsesigs();
503 			reset(0);
504 		}
505 		Fclose(ibuf);
506 		Fclose(obuf);
507 		if ((ibuf = Fopen(tempname, "r")) == NULL) {
508 			perror(tempname);
509 			rm(tempname);
510 			relsesigs();
511 			reset(0);
512 		}
513 		rm(tempname);
514 		free(tempname);
515 	}
516 	printf("\"%s\" ", mailname);
517 	fflush(stdout);
518 	if ((obuf = Fopen(mailname, "r+")) == NULL) {
519 		perror(mailname);
520 		relsesigs();
521 		reset(0);
522 	}
523 	trunc(obuf);
524 	c = 0;
525 	for (mp = &message[0]; mp < &message[msgCount]; mp++) {
526 		if ((mp->m_flag & MDELETED) != 0)
527 			continue;
528 		c++;
529 		if (sendmessage(mp, obuf, (struct ignoretab *) NULL, NOSTR) < 0) {
530 			perror(mailname);
531 			relsesigs();
532 			reset(0);
533 		}
534 	}
535 	gotcha = (c == 0 && ibuf == NULL);
536 	if (ibuf != NULL) {
537 		while ((c = getc(ibuf)) != EOF)
538 			(void) putc(c, obuf);
539 		Fclose(ibuf);
540 	}
541 	fflush(obuf);
542 	if (ferror(obuf)) {
543 		perror(mailname);
544 		relsesigs();
545 		reset(0);
546 	}
547 	Fclose(obuf);
548 	if (gotcha) {
549 		rm(mailname);
550 		printf("removed\n");
551 	} else
552 		printf("complete\n");
553 	fflush(stdout);
554 
555 done:
556 	relsesigs();
557 }
558