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