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