quit.c revision 1.19 1 /* $NetBSD: quit.c,v 1.19 2003/08/07 11:14:41 agc 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.19 2003/08/07 11:14:41 agc 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 extern char *tmpdir;
51
52 /*
53 * The "quit" command.
54 */
55 int
56 quitcmd(void *v)
57 {
58 /*
59 * If we are sourcing, then return 1 so execute() can handle it.
60 * Otherwise, return -1 to abort command loop.
61 */
62 if (sourcing)
63 return 1;
64 return -1;
65 }
66
67 /*
68 * Save all of the undetermined messages at the top of "mbox"
69 * Save all untouched messages back in the system mailbox.
70 * Remove the system mailbox, if none saved there.
71 */
72 void
73 quit(void)
74 {
75 int mcount, p, modify, autohold, anystat, holdbit, nohold;
76 FILE *ibuf = NULL, *obuf, *fbuf, *rbuf, *readstat = NULL, *abuf;
77 struct message *mp;
78 int c, fd;
79 struct stat minfo;
80 char *mbox;
81 char tempname[PATHSIZE];
82
83 #ifdef __GNUC__
84 obuf = NULL; /* XXX gcc -Wuninitialized */
85 #endif
86
87 /*
88 * If we are read only, we can't do anything,
89 * so just return quickly.
90 */
91 if (readonly)
92 return;
93 /*
94 * If editing (not reading system mail box), then do the work
95 * in edstop()
96 */
97 if (edit) {
98 edstop();
99 return;
100 }
101
102 /*
103 * See if there any messages to save in mbox. If no, we
104 * can save copying mbox to /tmp and back.
105 *
106 * Check also to see if any files need to be preserved.
107 * Delete all untouched messages to keep them out of mbox.
108 * If all the messages are to be preserved, just exit with
109 * a message.
110 */
111
112 fbuf = Fopen(mailname, "r");
113 if (fbuf == NULL)
114 goto newmail;
115 if (flock(fileno(fbuf), LOCK_EX) == -1) {
116 nolock:
117 warn("Unable to lock mailbox");
118 Fclose(fbuf);
119 return;
120 }
121 if (dot_lock(mailname, 1, stdout, ".") == -1)
122 goto nolock;
123 rbuf = NULL;
124 if (fstat(fileno(fbuf), &minfo) >= 0 && minfo.st_size > mailsize) {
125 printf("New mail has arrived.\n");
126 (void)snprintf(tempname, sizeof(tempname),
127 "%s/mail.RqXXXXXXXXXX", tmpdir);
128 if ((fd = mkstemp(tempname)) == -1 ||
129 (rbuf = Fdopen(fd, "w")) == NULL) {
130 if (fd != -1)
131 close(fd);
132 goto newmail;
133 }
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 warn("%s", tempname);
150 Fclose(rbuf);
151 Fclose(fbuf);
152 dot_unlock(mailname);
153 return;
154 }
155 Fclose(rbuf);
156 if ((rbuf = Fopen(tempname, "r")) == NULL)
157 goto newmail;
158 rm(tempname);
159 }
160
161 /*
162 * Adjust the message flags in each message.
163 */
164
165 anystat = 0;
166 autohold = value("hold") != NULL;
167 holdbit = autohold ? MPRESERVE : MBOX;
168 nohold = MBOX|MSAVED|MDELETED|MPRESERVE;
169 if (value("keepsave") != NULL)
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 != NULL) {
185 if ((readstat = Fopen(Tflag, "w")) == NULL)
186 Tflag = NULL;
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 != NULL && (mp->m_flag & (MREAD|MDELETED)) != 0) {
196 char *id;
197
198 if ((id = hfield("article-id", mp)) != NULL)
199 fprintf(readstat, "%s\n", id);
200 }
201 }
202 if (Tflag != NULL)
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") == NULL) {
231 (void)snprintf(tempname, sizeof(tempname),
232 "%s/mail.RmXXXXXXXXXX", tmpdir);
233 if ((fd = mkstemp(tempname)) == -1 ||
234 (obuf = Fdopen(fd, "w")) == NULL) {
235 warn("%s", tempname);
236 if (fd != -1)
237 close(fd);
238 Fclose(fbuf);
239 dot_unlock(mailname);
240 return;
241 }
242 if ((ibuf = Fopen(tempname, "r")) == NULL) {
243 warn("%s", tempname);
244 rm(tempname);
245 Fclose(obuf);
246 Fclose(fbuf);
247 dot_unlock(mailname);
248 return;
249 }
250 rm(tempname);
251 if ((abuf = Fopen(mbox, "r")) != NULL) {
252 while ((c = getc(abuf)) != EOF)
253 (void)putc(c, obuf);
254 Fclose(abuf);
255 }
256 if (ferror(obuf)) {
257 warn("%s", tempname);
258 Fclose(ibuf);
259 Fclose(obuf);
260 Fclose(fbuf);
261 dot_unlock(mailname);
262 return;
263 }
264 Fclose(obuf);
265 close(creat(mbox, 0600));
266 if ((obuf = Fopen(mbox, "r+")) == NULL) {
267 warn("%s", mbox);
268 Fclose(ibuf);
269 Fclose(fbuf);
270 dot_unlock(mailname);
271 return;
272 }
273 }
274 else {
275 if ((obuf = Fopen(mbox, "a")) == NULL) {
276 warn("%s", mbox);
277 Fclose(fbuf);
278 dot_unlock(mailname);
279 return;
280 }
281 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 Fclose(ibuf);
288 Fclose(obuf);
289 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 Fclose(ibuf);
310 }
311 fflush(obuf);
312 if (!ferror(obuf))
313 trunc(obuf); /* XXX or should we truncate? */
314 if (ferror(obuf)) {
315 warn("%s", mbox);
316 Fclose(obuf);
317 Fclose(fbuf);
318 dot_unlock(mailname);
319 return;
320 }
321 Fclose(obuf);
322 if (mcount == 1)
323 printf("Saved 1 message in mbox\n");
324 else
325 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 writeback(rbuf);
334 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 Fclose(abuf);
355 Fclose(fbuf);
356 dot_unlock(mailname);
357 return;
358 }
359 Fclose(rbuf);
360 trunc(abuf);
361 Fclose(abuf);
362 alter(mailname);
363 Fclose(fbuf);
364 dot_unlock(mailname);
365 return;
366 }
367 demail();
368 Fclose(fbuf);
369 dot_unlock(mailname);
370 return;
371
372 newmail:
373 printf("Thou hast new mail.\n");
374 if (fbuf != NULL) {
375 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 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 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 fflush(obuf);
425 if (!ferror(obuf))
426 trunc(obuf); /* XXX or should we truncate? */
427 if (ferror(obuf)) {
428 warn("%s", mailname);
429 Fclose(obuf);
430 return(-1);
431 }
432 if (res != NULL)
433 Fclose(res);
434 Fclose(obuf);
435 alter(mailname);
436 if (p == 1)
437 printf("Held 1 message in %s\n", mailname);
438 else
439 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 fprintf(readstat, "%s\n", id);
476 }
477 }
478 if (Tflag != NULL)
479 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 close(fd);
491 relsesigs();
492 reset(0);
493 }
494 if ((ibuf = Fopen(mailname, "r")) == NULL) {
495 warn("%s", mailname);
496 Fclose(obuf);
497 rm(tempname);
498 relsesigs();
499 reset(0);
500 }
501 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 Fclose(obuf);
508 Fclose(ibuf);
509 rm(tempname);
510 relsesigs();
511 reset(0);
512 }
513 Fclose(ibuf);
514 Fclose(obuf);
515 if ((ibuf = Fopen(tempname, "r")) == NULL) {
516 warn("%s", tempname);
517 rm(tempname);
518 relsesigs();
519 reset(0);
520 }
521 rm(tempname);
522 }
523 printf("\"%s\" ", mailname);
524 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 Fclose(ibuf);
547 }
548 fflush(obuf);
549 if (ferror(obuf)) {
550 warn("%s", mailname);
551 relsesigs();
552 reset(0);
553 }
554 Fclose(obuf);
555 if (gotcha) {
556 rm(mailname);
557 printf("removed\n");
558 } else
559 printf("complete\n");
560 fflush(stdout);
561
562 done:
563 relsesigs();
564 }
565