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