Many: use srelax() is some places
[s-mailx.git] / quit.c
blob92c1040a3b9663dd4dfacaf724973010b30a3a2d
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 srelax_hold();
102 for (mp = &message[0]; mp < &message[msgCount]; mp++)
103 if ((mp->m_flag&MPRESERVE)||(mp->m_flag&MTOUCH)==0) {
104 p++;
105 if (sendmp(mp, obuf, NULL, NULL, SEND_MBOX, NULL) < 0) {
106 perror(mailname);
107 (void)fseek(obuf, 0L, SEEK_SET);
108 srelax_rele();
109 return(-1);
111 srelax();
113 srelax_rele();
115 #ifdef APPEND
116 if (res != NULL)
117 while ((c = getc(res)) != EOF)
118 putc(c, obuf);
119 #endif
120 fflush(obuf);
121 ftrunc(obuf);
122 if (ferror(obuf)) {
123 perror(mailname);
124 (void)fseek(obuf, 0L, SEEK_SET);
125 return(-1);
127 if (res != NULL)
128 Fclose(res);
129 if (fseek(obuf, 0L, SEEK_SET) < 0)
130 return -1;
131 alter(mailname);
132 if (p == 1)
133 printf(tr(155, "Held 1 message in %s\n"), displayname);
134 else
135 printf(tr(156, "Held %d messages in %s\n"), p, displayname);
136 return 0;
140 * Save all of the undetermined messages at the top of "mbox"
141 * Save all untouched messages back in the system mailbox.
142 * Remove the system mailbox, if none saved there.
144 void
145 quit(void)
147 int p, modify, anystat;
148 FILE *fbuf, *rbuf, *abuf;
149 struct message *mp;
150 int c;
151 char *tempResid;
152 struct stat minfo;
155 * If we are read only, we can't do anything,
156 * so just return quickly. IMAP can set some
157 * flags (e.g. "\\Seen") so imap_quit must be
158 * called even then.
160 if (mb.mb_perm == 0 && mb.mb_type != MB_IMAP)
161 return;
162 /* TODO lex.c:setfile() has just called holdsigs(); before it called
163 * TODO us, but this causes uninterruptible hangs due to blocked sigs
164 * TODO anywhere except for MB_FILE (all others install their own
165 * TODO handlers, as it seems, properly); marked YYY */
166 switch (mb.mb_type) {
167 case MB_FILE:
168 break;
169 case MB_MAILDIR:
170 relsesigs(); /* YYY */
171 maildir_quit();
172 holdsigs(); /* YYY */
173 return;
174 #ifdef HAVE_POP3
175 case MB_POP3:
176 relsesigs(); /* YYY */
177 pop3_quit();
178 holdsigs(); /* YYY */
179 return;
180 #endif
181 #ifdef HAVE_IMAP
182 case MB_IMAP:
183 case MB_CACHE:
184 relsesigs(); /* YYY */
185 imap_quit();
186 holdsigs(); /* YYY */
187 return;
188 #endif
189 case MB_VOID:
190 default:
191 return;
194 * If editing (not reading system mail box), then do the work
195 * in edstop()
197 if (edit) {
198 edstop();
199 return;
203 * See if there any messages to save in mbox. If no, we
204 * can save copying mbox to /tmp and back.
206 * Check also to see if any files need to be preserved.
207 * Delete all untouched messages to keep them out of mbox.
208 * If all the messages are to be preserved, just exit with
209 * a message.
212 fbuf = Zopen(mailname, "r+", &mb.mb_compressed);
213 if (fbuf == NULL) {
214 if (errno == ENOENT)
215 return;
216 goto newmail;
218 if (fcntl_lock(fileno(fbuf), F_WRLCK) == -1) {
219 nolock:
220 perror(tr(157, "Unable to lock mailbox"));
221 Fclose(fbuf);
222 return;
224 if (dot_lock(mailname, fileno(fbuf), 1, stdout, ".") == -1)
225 goto nolock;
226 rbuf = NULL;
227 if (fstat(fileno(fbuf), &minfo) >= 0 && minfo.st_size > mailsize) {
228 printf(tr(158, "New mail has arrived.\n"));
229 rbuf = Ftemp(&tempResid, "Rq", "w", 0600, 1);
230 if (rbuf == NULL || fbuf == NULL)
231 goto newmail;
232 #ifdef APPEND
233 fseek(fbuf, (long)mailsize, SEEK_SET);
234 while ((c = getc(fbuf)) != EOF)
235 putc(c, rbuf);
236 #else
237 p = minfo.st_size - mailsize;
238 while (p-- > 0) {
239 c = getc(fbuf);
240 if (c == EOF)
241 goto newmail;
242 putc(c, rbuf);
244 #endif
245 Fclose(rbuf);
246 if ((rbuf = Fopen(tempResid, "r")) == NULL)
247 goto newmail;
248 rm(tempResid);
249 Ftfree(&tempResid);
252 anystat = holdbits();
253 modify = 0;
254 for (c = 0, p = 0, mp = &message[0]; mp < &message[msgCount]; mp++) {
255 if (mp->m_flag & MBOX)
256 c++;
257 if (mp->m_flag & MPRESERVE)
258 p++;
259 if (mp->m_flag & MODIFY)
260 modify++;
262 if (p == msgCount && !modify && !anystat) {
263 if (p == 1)
264 printf(tr(155, "Held 1 message in %s\n"), displayname);
265 else if (p > 1)
266 printf(tr(156, "Held %d messages in %s\n"),
267 p, displayname);
268 Fclose(fbuf);
269 dot_unlock(mailname);
270 return;
272 if (c == 0) {
273 if (p != 0) {
274 writeback(rbuf, fbuf);
275 Fclose(fbuf);
276 dot_unlock(mailname);
277 return;
279 goto cream;
282 if (makembox() == STOP) {
283 Fclose(fbuf);
284 dot_unlock(mailname);
285 return;
288 * Now we are ready to copy back preserved files to
289 * the system mailbox, if any were requested.
292 if (p != 0) {
293 writeback(rbuf, fbuf);
294 Fclose(fbuf);
295 dot_unlock(mailname);
296 return;
300 * Finally, remove his /usr/mail file.
301 * If new mail has arrived, copy it back.
304 cream:
305 if (rbuf != NULL) {
306 abuf = fbuf;
307 fseek(abuf, 0L, SEEK_SET);
308 while ((c = getc(rbuf)) != EOF)
309 putc(c, abuf);
310 Fclose(rbuf);
311 ftrunc(abuf);
312 alter(mailname);
313 Fclose(fbuf);
314 dot_unlock(mailname);
315 return;
317 demail();
318 Fclose(fbuf);
319 dot_unlock(mailname);
320 return;
322 newmail:
323 printf(tr(166, "Thou hast new mail.\n"));
324 if (fbuf != NULL) {
325 Fclose(fbuf);
326 dot_unlock(mailname);
331 * Adjust the message flags in each message.
333 int
334 holdbits(void)
336 struct message *mp;
337 int anystat, autohold, holdbit, nohold;
339 anystat = 0;
340 autohold = value("hold") != NULL;
341 holdbit = autohold ? MPRESERVE : MBOX;
342 nohold = MBOX|MSAVED|MDELETED|MPRESERVE;
343 if (value("keepsave") != NULL)
344 nohold &= ~MSAVED;
345 for (mp = &message[0]; mp < &message[msgCount]; mp++) {
346 if (mp->m_flag & MNEW) {
347 mp->m_flag &= ~MNEW;
348 mp->m_flag |= MSTATUS;
350 if (mp->m_flag & (MSTATUS|MFLAG|MUNFLAG|MANSWER|MUNANSWER|
351 MDRAFT|MUNDRAFT))
352 anystat++;
353 if ((mp->m_flag & MTOUCH) == 0)
354 mp->m_flag |= MPRESERVE;
355 if ((mp->m_flag & nohold) == 0)
356 mp->m_flag |= holdbit;
358 return anystat;
362 * Create another temporary file and copy user's mbox file
363 * darin. If there is no mbox, copy nothing.
364 * If he has specified "append" don't copy his mailbox,
365 * just copy saveable entries at the end.
368 enum okay
369 makembox(void)
371 struct message *mp;
372 char *mbox, *tempQuit;
373 int mcount, c;
374 FILE *ibuf = NULL, *obuf, *abuf;
375 enum protocol prot;
377 mbox = mboxname;
378 mcount = 0;
379 if (value("append") == NULL) {
380 if ((obuf = Ftemp(&tempQuit, "Rm", "w", 0600, 1)) == NULL) {
381 perror(tr(163, "temporary mail quit file"));
382 return STOP;
384 if ((ibuf = Fopen(tempQuit, "r")) == NULL) {
385 perror(tempQuit);
386 Fclose(obuf);
388 rm(tempQuit);
389 Ftfree(&tempQuit);
390 if (ibuf == NULL)
391 return STOP;
393 if ((abuf = Zopen(mbox, "r", NULL)) != NULL) {
394 while ((c = getc(abuf)) != EOF)
395 putc(c, obuf);
396 Fclose(abuf);
398 if (ferror(obuf)) {
399 perror(tr(163, "temporary mail quit file"));
400 Fclose(ibuf);
401 Fclose(obuf);
402 return STOP;
404 Fclose(obuf);
406 if ((c = open(mbox, O_CREAT|O_TRUNC|O_WRONLY, 0600)) >= 0)
407 close(c);
408 if ((obuf = Zopen(mbox, "r+", NULL)) == NULL) {
409 perror(mbox);
410 Fclose(ibuf);
411 return STOP;
414 else {
415 if ((obuf = Zopen(mbox, "a", NULL)) == NULL) {
416 perror(mbox);
417 return STOP;
419 fchmod(fileno(obuf), 0600);
422 srelax_hold();
423 prot = which_protocol(mbox);
424 for (mp = &message[0]; mp < &message[msgCount]; mp++) {
425 if (mp->m_flag & MBOX) {
426 mcount++;
427 if (prot == PROTO_IMAP &&
428 saveignore[0].i_count == 0 &&
429 saveignore[1].i_count == 0
430 #ifdef HAVE_IMAP /* TODO revisit */
431 && imap_thisaccount(mbox)
432 #endif
434 #ifdef HAVE_IMAP
435 if (imap_copy(mp, mp-message+1, mbox) == STOP)
436 #endif
437 goto jerr;
438 } else if (sendmp(mp, obuf, saveignore,
439 NULL, SEND_MBOX, NULL) < 0) {
440 perror(mbox);
441 jerr:
442 if (ibuf)
443 Fclose(ibuf);
444 Fclose(obuf);
445 srelax_rele();
446 return STOP;
448 mp->m_flag |= MBOXED;
449 srelax();
452 srelax_rele();
455 * Copy the user's old mbox contents back
456 * to the end of the stuff we just saved.
457 * If we are appending, this is unnecessary.
460 if (value("append") == NULL) {
461 rewind(ibuf);
462 c = getc(ibuf);
463 while (c != EOF) {
464 putc(c, obuf);
465 if (ferror(obuf))
466 break;
467 c = getc(ibuf);
469 Fclose(ibuf);
470 fflush(obuf);
472 ftrunc(obuf);
473 if (ferror(obuf)) {
474 perror(mbox);
475 Fclose(obuf);
476 return STOP;
478 if (Fclose(obuf) != 0) {
479 if (prot != PROTO_IMAP)
480 perror(mbox);
481 return STOP;
483 if (mcount == 1)
484 printf(tr(164, "Saved 1 message in mbox\n"));
485 else
486 printf(tr(165, "Saved %d messages in mbox\n"), mcount);
487 return OKAY;
491 * Terminate an editing session by attempting to write out the user's
492 * file from the temporary. Save any new stuff appended to the file.
494 static void
495 edstop(void)
497 int gotcha, c;
498 struct message *mp;
499 FILE *obuf, *ibuf = NULL;
500 struct stat statb;
502 if (mb.mb_perm == 0)
503 return;
504 holdsigs();
505 for (mp = &message[0], gotcha = 0; mp < &message[msgCount]; mp++) {
506 if (mp->m_flag & MNEW) {
507 mp->m_flag &= ~MNEW;
508 mp->m_flag |= MSTATUS;
510 if (mp->m_flag & (MODIFY|MDELETED|MSTATUS|MFLAG|MUNFLAG|
511 MANSWER|MUNANSWER|MDRAFT|MUNDRAFT))
512 gotcha++;
514 if (!gotcha)
515 goto done;
516 ibuf = NULL;
517 if (stat(mailname, &statb) >= 0 && statb.st_size > mailsize) {
518 char *tempname;
520 if ((obuf = Ftemp(&tempname, "edstop", "w", 0600, 1)) == NULL) {
521 perror(tr(167, "tmpfile"));
522 relsesigs();
523 reset(0);
525 if ((ibuf = Zopen(mailname, "r", &mb.mb_compressed)) == NULL) {
526 perror(mailname);
527 Fclose(obuf);
528 rm(tempname);
529 Ftfree(&tempname);
530 relsesigs();
531 reset(0);
533 fseek(ibuf, (long)mailsize, SEEK_SET);
534 while ((c = getc(ibuf)) != EOF)
535 putc(c, obuf);
536 Fclose(ibuf);
537 Fclose(obuf);
538 if ((ibuf = Fopen(tempname, "r")) == NULL) {
539 perror(tempname);
540 rm(tempname);
541 Ftfree(&tempname);
542 relsesigs();
543 reset(0);
545 rm(tempname);
546 Ftfree(&tempname);
548 printf(tr(168, "\"%s\" "), displayname);
549 fflush(stdout);
550 if ((obuf = Zopen(mailname, "r+", &mb.mb_compressed)) == NULL) {
551 perror(mailname);
552 relsesigs();
553 reset(0);
555 ftrunc(obuf);
557 srelax_hold();
558 c = 0;
559 for (mp = &message[0]; mp < &message[msgCount]; mp++) {
560 if ((mp->m_flag & MDELETED) != 0)
561 continue;
562 c++;
563 if (sendmp(mp, obuf, NULL, NULL, SEND_MBOX, NULL) < 0) {
564 perror(mailname);
565 relsesigs();
566 srelax_rele();
567 reset(0);/* XXX jump aways are terrible */
569 srelax();
571 srelax_rele();
573 gotcha = (c == 0 && ibuf == NULL);
574 if (ibuf != NULL) {
575 while ((c = getc(ibuf)) != EOF)
576 putc(c, obuf);
577 Fclose(ibuf);
579 fflush(obuf);
580 if (ferror(obuf)) {
581 perror(mailname);
582 relsesigs();
583 reset(0);
585 Fclose(obuf);
586 if (gotcha && value("emptybox") == NULL) {
587 rm(mailname);
588 printf((value("bsdcompat") || value("bsdmsgs"))
589 ? tr(169, "removed\n") : tr(211, "removed.\n"));
590 } else
591 printf((value("bsdcompat") || value("bsdmsgs"))
592 ? tr(170, "complete\n") : tr(212, "updated.\n"));
593 fflush(stdout);
595 done:
596 relsesigs();
599 enum quitflags {
600 QUITFLAG_HOLD = 001,
601 QUITFLAG_KEEPSAVE = 002,
602 QUITFLAG_APPEND = 004,
603 QUITFLAG_EMPTYBOX = 010
606 static const struct quitnames {
607 enum quitflags flag;
608 const char *name;
609 } quitnames[] = {
610 { QUITFLAG_HOLD, "hold" },
611 { QUITFLAG_KEEPSAVE, "keepsave" },
612 { QUITFLAG_APPEND, "append" },
613 { QUITFLAG_EMPTYBOX, "emptybox" },
614 { 0, NULL }
618 savequitflags(void)
620 enum quitflags qf = 0;
621 int i;
623 for (i = 0; quitnames[i].name; i++)
624 if (value(quitnames[i].name))
625 qf |= quitnames[i].flag;
626 return qf;
629 void
630 restorequitflags(int qf)
632 int i;
634 for (i = 0; quitnames[i].name; i++)
635 if (qf & quitnames[i].flag) {
636 if (value(quitnames[i].name) == NULL)
637 assign(quitnames[i].name, "");
638 } else if (value(quitnames[i].name))
639 unset_internal(quitnames[i].name);