Localize cmdtab[] in lex.c..
[s-mailx.git] / quit.c
blobf7fca6a8db6bbffb14e4ac63aa6eb526af3a7f34
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Termination processing.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2013 Steffen "Daode" Nurpmeso <sdaoden@users.sf.net>.
6 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the University of
22 * California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 * may be used to endorse or promote products derived from this software
25 * without specific prior written permission.
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
40 #include "nail.h"
42 #include <fcntl.h>
43 #include <utime.h>
45 /* Touch the indicated file */
46 static void alter(char const *name);
48 static int writeback(FILE *res, FILE *obuf);
49 static void edstop(void);
51 static void
52 alter(char const *name)
54 struct stat sb;
55 struct utimbuf utb;
57 if (stat(name, &sb))
58 return;
59 utb.actime = time((time_t *)0) + 1;
60 utb.modtime = sb.st_mtime;
61 utime(name, &utb);
65 * The "quit" command.
67 /*ARGSUSED*/
68 int
69 quitcmd(void *v)
71 (void)v;
73 * If we are sourcing, then return 1 so execute() can handle it.
74 * Otherwise, return -1 to abort command loop.
76 if (sourcing)
77 return 1;
78 return -1;
82 * Preserve all the appropriate messages back in the system
83 * mailbox, and print a nice message indicated how many were
84 * saved. On any error, just return -1. Else return 0.
85 * Incorporate the any new mail that we found.
87 static int
88 writeback(FILE *res, FILE *obuf)
90 struct message *mp;
91 int p, c;
93 p = 0;
94 if (fseek(obuf, 0L, SEEK_SET) < 0)
95 return -1;
96 #ifndef APPEND
97 if (res != NULL)
98 while ((c = getc(res)) != EOF)
99 putc(c, obuf);
100 #endif
101 for (mp = &message[0]; mp < &message[msgCount]; mp++)
102 if ((mp->m_flag&MPRESERVE)||(mp->m_flag&MTOUCH)==0) {
103 p++;
104 if (sendmp(mp, obuf, NULL, NULL, SEND_MBOX, NULL) < 0) {
105 perror(mailname);
106 (void)fseek(obuf, 0L, SEEK_SET);
107 return(-1);
110 #ifdef APPEND
111 if (res != NULL)
112 while ((c = getc(res)) != EOF)
113 putc(c, obuf);
114 #endif
115 fflush(obuf);
116 ftrunc(obuf);
117 if (ferror(obuf)) {
118 perror(mailname);
119 (void)fseek(obuf, 0L, SEEK_SET);
120 return(-1);
122 if (res != NULL)
123 Fclose(res);
124 if (fseek(obuf, 0L, SEEK_SET) < 0)
125 return -1;
126 alter(mailname);
127 if (p == 1)
128 printf(tr(155, "Held 1 message in %s\n"), displayname);
129 else
130 printf(tr(156, "Held %d messages in %s\n"), p, displayname);
131 return 0;
135 * Save all of the undetermined messages at the top of "mbox"
136 * Save all untouched messages back in the system mailbox.
137 * Remove the system mailbox, if none saved there.
139 void
140 quit(void)
142 int p, modify, anystat;
143 FILE *fbuf, *rbuf, *abuf;
144 struct message *mp;
145 int c;
146 char *tempResid;
147 struct stat minfo;
150 * If we are read only, we can't do anything,
151 * so just return quickly. IMAP can set some
152 * flags (e.g. "\\Seen") so imap_quit must be
153 * called even then.
155 if (mb.mb_perm == 0 && mb.mb_type != MB_IMAP)
156 return;
157 /* TODO lex.c:setfile() has just called holdsigs(); before it called
158 * TODO us, but this causes uninterruptible hangs due to blocked sigs
159 * TODO anywhere except for MB_FILE (all others install their own
160 * TODO handlers, as it seems, properly); marked YYY */
161 switch (mb.mb_type) {
162 case MB_FILE:
163 break;
164 case MB_MAILDIR:
165 relsesigs(); /* YYY */
166 maildir_quit();
167 holdsigs(); /* YYY */
168 return;
169 #ifdef HAVE_POP3
170 case MB_POP3:
171 relsesigs(); /* YYY */
172 pop3_quit();
173 holdsigs(); /* YYY */
174 return;
175 #endif
176 #ifdef HAVE_IMAP
177 case MB_IMAP:
178 case MB_CACHE:
179 relsesigs(); /* YYY */
180 imap_quit();
181 holdsigs(); /* YYY */
182 return;
183 #endif
184 case MB_VOID:
185 default:
186 return;
189 * If editing (not reading system mail box), then do the work
190 * in edstop()
192 if (edit) {
193 edstop();
194 return;
198 * See if there any messages to save in mbox. If no, we
199 * can save copying mbox to /tmp and back.
201 * Check also to see if any files need to be preserved.
202 * Delete all untouched messages to keep them out of mbox.
203 * If all the messages are to be preserved, just exit with
204 * a message.
207 fbuf = Zopen(mailname, "r+", &mb.mb_compressed);
208 if (fbuf == NULL) {
209 if (errno == ENOENT)
210 return;
211 goto newmail;
213 if (fcntl_lock(fileno(fbuf), F_WRLCK) == -1) {
214 nolock:
215 perror(tr(157, "Unable to lock mailbox"));
216 Fclose(fbuf);
217 return;
219 if (dot_lock(mailname, fileno(fbuf), 1, stdout, ".") == -1)
220 goto nolock;
221 rbuf = NULL;
222 if (fstat(fileno(fbuf), &minfo) >= 0 && minfo.st_size > mailsize) {
223 printf(tr(158, "New mail has arrived.\n"));
224 rbuf = Ftemp(&tempResid, "Rq", "w", 0600, 1);
225 if (rbuf == NULL || fbuf == NULL)
226 goto newmail;
227 #ifdef APPEND
228 fseek(fbuf, (long)mailsize, SEEK_SET);
229 while ((c = getc(fbuf)) != EOF)
230 putc(c, rbuf);
231 #else
232 p = minfo.st_size - mailsize;
233 while (p-- > 0) {
234 c = getc(fbuf);
235 if (c == EOF)
236 goto newmail;
237 putc(c, rbuf);
239 #endif
240 Fclose(rbuf);
241 if ((rbuf = Fopen(tempResid, "r")) == NULL)
242 goto newmail;
243 rm(tempResid);
244 Ftfree(&tempResid);
247 anystat = holdbits();
248 modify = 0;
249 for (c = 0, p = 0, mp = &message[0]; mp < &message[msgCount]; mp++) {
250 if (mp->m_flag & MBOX)
251 c++;
252 if (mp->m_flag & MPRESERVE)
253 p++;
254 if (mp->m_flag & MODIFY)
255 modify++;
257 if (p == msgCount && !modify && !anystat) {
258 if (p == 1)
259 printf(tr(155, "Held 1 message in %s\n"), displayname);
260 else if (p > 1)
261 printf(tr(156, "Held %d messages in %s\n"),
262 p, displayname);
263 Fclose(fbuf);
264 dot_unlock(mailname);
265 return;
267 if (c == 0) {
268 if (p != 0) {
269 writeback(rbuf, fbuf);
270 Fclose(fbuf);
271 dot_unlock(mailname);
272 return;
274 goto cream;
277 if (makembox() == STOP) {
278 Fclose(fbuf);
279 dot_unlock(mailname);
280 return;
283 * Now we are ready to copy back preserved files to
284 * the system mailbox, if any were requested.
287 if (p != 0) {
288 writeback(rbuf, fbuf);
289 Fclose(fbuf);
290 dot_unlock(mailname);
291 return;
295 * Finally, remove his /usr/mail file.
296 * If new mail has arrived, copy it back.
299 cream:
300 if (rbuf != NULL) {
301 abuf = fbuf;
302 fseek(abuf, 0L, SEEK_SET);
303 while ((c = getc(rbuf)) != EOF)
304 putc(c, abuf);
305 Fclose(rbuf);
306 ftrunc(abuf);
307 alter(mailname);
308 Fclose(fbuf);
309 dot_unlock(mailname);
310 return;
312 demail();
313 Fclose(fbuf);
314 dot_unlock(mailname);
315 return;
317 newmail:
318 printf(tr(166, "Thou hast new mail.\n"));
319 if (fbuf != NULL) {
320 Fclose(fbuf);
321 dot_unlock(mailname);
326 * Adjust the message flags in each message.
328 int
329 holdbits(void)
331 struct message *mp;
332 int anystat, autohold, holdbit, nohold;
334 anystat = 0;
335 autohold = value("hold") != NULL;
336 holdbit = autohold ? MPRESERVE : MBOX;
337 nohold = MBOX|MSAVED|MDELETED|MPRESERVE;
338 if (value("keepsave") != NULL)
339 nohold &= ~MSAVED;
340 for (mp = &message[0]; mp < &message[msgCount]; mp++) {
341 if (mp->m_flag & MNEW) {
342 mp->m_flag &= ~MNEW;
343 mp->m_flag |= MSTATUS;
345 if (mp->m_flag & (MSTATUS|MFLAG|MUNFLAG|MANSWER|MUNANSWER|
346 MDRAFT|MUNDRAFT))
347 anystat++;
348 if ((mp->m_flag & MTOUCH) == 0)
349 mp->m_flag |= MPRESERVE;
350 if ((mp->m_flag & nohold) == 0)
351 mp->m_flag |= holdbit;
353 return anystat;
357 * Create another temporary file and copy user's mbox file
358 * darin. If there is no mbox, copy nothing.
359 * If he has specified "append" don't copy his mailbox,
360 * just copy saveable entries at the end.
363 enum okay
364 makembox(void)
366 struct message *mp;
367 char *mbox, *tempQuit;
368 int mcount, c;
369 FILE *ibuf = NULL, *obuf, *abuf;
370 enum protocol prot;
372 mbox = mboxname;
373 mcount = 0;
374 if (value("append") == NULL) {
375 if ((obuf = Ftemp(&tempQuit, "Rm", "w", 0600, 1)) == NULL) {
376 perror(tr(163, "temporary mail quit file"));
377 return STOP;
379 if ((ibuf = Fopen(tempQuit, "r")) == NULL) {
380 perror(tempQuit);
381 Fclose(obuf);
383 rm(tempQuit);
384 Ftfree(&tempQuit);
385 if (ibuf == NULL)
386 return STOP;
388 if ((abuf = Zopen(mbox, "r", NULL)) != NULL) {
389 while ((c = getc(abuf)) != EOF)
390 putc(c, obuf);
391 Fclose(abuf);
393 if (ferror(obuf)) {
394 perror(tr(163, "temporary mail quit file"));
395 Fclose(ibuf);
396 Fclose(obuf);
397 return STOP;
399 Fclose(obuf);
401 if ((c = open(mbox, O_CREAT|O_TRUNC|O_WRONLY, 0600)) >= 0)
402 close(c);
403 if ((obuf = Zopen(mbox, "r+", NULL)) == NULL) {
404 perror(mbox);
405 Fclose(ibuf);
406 return STOP;
409 else {
410 if ((obuf = Zopen(mbox, "a", NULL)) == NULL) {
411 perror(mbox);
412 return STOP;
414 fchmod(fileno(obuf), 0600);
416 prot = which_protocol(mbox);
417 for (mp = &message[0]; mp < &message[msgCount]; mp++)
418 if (mp->m_flag & MBOX) {
419 mcount++;
420 if (prot == PROTO_IMAP &&
421 saveignore[0].i_count == 0 &&
422 saveignore[1].i_count == 0
423 #ifdef HAVE_IMAP /* TODO revisit */
424 && imap_thisaccount(mbox)
425 #endif
427 #ifdef HAVE_IMAP
428 if (imap_copy(mp, mp-message+1, mbox) == STOP)
429 #endif
430 goto err;
431 } else if (sendmp(mp, obuf, saveignore,
432 NULL, SEND_MBOX, NULL) < 0) {
433 perror(mbox);
434 err: if (ibuf)
435 Fclose(ibuf);
436 Fclose(obuf);
437 return STOP;
439 mp->m_flag |= MBOXED;
443 * Copy the user's old mbox contents back
444 * to the end of the stuff we just saved.
445 * If we are appending, this is unnecessary.
448 if (value("append") == NULL) {
449 rewind(ibuf);
450 c = getc(ibuf);
451 while (c != EOF) {
452 putc(c, obuf);
453 if (ferror(obuf))
454 break;
455 c = getc(ibuf);
457 Fclose(ibuf);
458 fflush(obuf);
460 ftrunc(obuf);
461 if (ferror(obuf)) {
462 perror(mbox);
463 Fclose(obuf);
464 return STOP;
466 if (Fclose(obuf) != 0) {
467 if (prot != PROTO_IMAP)
468 perror(mbox);
469 return STOP;
471 if (mcount == 1)
472 printf(tr(164, "Saved 1 message in mbox\n"));
473 else
474 printf(tr(165, "Saved %d messages in mbox\n"), mcount);
475 return OKAY;
479 * Terminate an editing session by attempting to write out the user's
480 * file from the temporary. Save any new stuff appended to the file.
482 static void
483 edstop(void)
485 int gotcha, c;
486 struct message *mp;
487 FILE *obuf, *ibuf = NULL;
488 struct stat statb;
490 if (mb.mb_perm == 0)
491 return;
492 holdsigs();
493 for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++) {
494 if (mp->m_flag & MNEW) {
495 mp->m_flag &= ~MNEW;
496 mp->m_flag |= MSTATUS;
498 if (mp->m_flag & (MODIFY|MDELETED|MSTATUS|MFLAG|MUNFLAG|
499 MANSWER|MUNANSWER|MDRAFT|MUNDRAFT))
500 gotcha++;
502 if (!gotcha)
503 goto done;
504 ibuf = NULL;
505 if (stat(mailname, &statb) >= 0 && statb.st_size > mailsize) {
506 char *tempname;
508 if ((obuf = Ftemp(&tempname, "edstop", "w", 0600, 1)) == NULL) {
509 perror(tr(167, "tmpfile"));
510 relsesigs();
511 reset(0);
513 if ((ibuf = Zopen(mailname, "r", &mb.mb_compressed)) == NULL) {
514 perror(mailname);
515 Fclose(obuf);
516 rm(tempname);
517 Ftfree(&tempname);
518 relsesigs();
519 reset(0);
521 fseek(ibuf, (long)mailsize, SEEK_SET);
522 while ((c = getc(ibuf)) != EOF)
523 putc(c, obuf);
524 Fclose(ibuf);
525 Fclose(obuf);
526 if ((ibuf = Fopen(tempname, "r")) == NULL) {
527 perror(tempname);
528 rm(tempname);
529 Ftfree(&tempname);
530 relsesigs();
531 reset(0);
533 rm(tempname);
534 Ftfree(&tempname);
536 printf(tr(168, "\"%s\" "), displayname);
537 fflush(stdout);
538 if ((obuf = Zopen(mailname, "r+", &mb.mb_compressed)) == NULL) {
539 perror(mailname);
540 relsesigs();
541 reset(0);
543 ftrunc(obuf);
544 c = 0;
545 for (mp = &message[0]; mp < &message[msgCount]; mp++) {
546 if ((mp->m_flag & MDELETED) != 0)
547 continue;
548 c++;
549 if (sendmp(mp, obuf, NULL, NULL, SEND_MBOX, NULL) < 0) {
550 perror(mailname);
551 relsesigs();
552 reset(0);
555 gotcha = (c == 0 && ibuf == NULL);
556 if (ibuf != NULL) {
557 while ((c = getc(ibuf)) != EOF)
558 putc(c, obuf);
559 Fclose(ibuf);
561 fflush(obuf);
562 if (ferror(obuf)) {
563 perror(mailname);
564 relsesigs();
565 reset(0);
567 Fclose(obuf);
568 if (gotcha && value("emptybox") == NULL) {
569 rm(mailname);
570 printf((value("bsdcompat") || value("bsdmsgs"))
571 ? tr(169, "removed\n") : tr(211, "removed.\n"));
572 } else
573 printf((value("bsdcompat") || value("bsdmsgs"))
574 ? tr(170, "complete\n") : tr(212, "updated.\n"));
575 fflush(stdout);
577 done:
578 relsesigs();
581 enum quitflags {
582 QUITFLAG_HOLD = 001,
583 QUITFLAG_KEEPSAVE = 002,
584 QUITFLAG_APPEND = 004,
585 QUITFLAG_EMPTYBOX = 010
588 static const struct quitnames {
589 enum quitflags flag;
590 const char *name;
591 } quitnames[] = {
592 { QUITFLAG_HOLD, "hold" },
593 { QUITFLAG_KEEPSAVE, "keepsave" },
594 { QUITFLAG_APPEND, "append" },
595 { QUITFLAG_EMPTYBOX, "emptybox" },
596 { 0, NULL }
600 savequitflags(void)
602 enum quitflags qf = 0;
603 int i;
605 for (i = 0; quitnames[i].name; i++)
606 if (value(quitnames[i].name))
607 qf |= quitnames[i].flag;
608 return qf;
611 void
612 restorequitflags(int qf)
614 int i;
616 for (i = 0; quitnames[i].name; i++)
617 if (qf & quitnames[i].flag) {
618 if (value(quitnames[i].name) == NULL)
619 assign(quitnames[i].name, "");
620 } else if (value(quitnames[i].name))
621 unset_internal(quitnames[i].name);