INSTALL: update for v14.6
[s-mailx.git] / quit.c
blob27bcac6fa9b759b2cd764ecbc10c973390c23d30
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 - 2014 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 #ifndef HAVE_AMALGAMATION
41 # include "nail.h"
42 #endif
44 #include <fcntl.h>
45 #include <utime.h>
47 enum quitflags {
48 QUITFLAG_HOLD = 001,
49 QUITFLAG_KEEPSAVE = 002,
50 QUITFLAG_APPEND = 004,
51 QUITFLAG_EMPTYBOX = 010
54 struct quitnames {
55 enum quitflags flag;
56 enum okeys okey;
59 static struct quitnames const _quitnames[] = {
60 {QUITFLAG_HOLD, ok_b_hold},
61 {QUITFLAG_KEEPSAVE, ok_b_keepsave},
62 {QUITFLAG_APPEND, ok_b_append},
63 {QUITFLAG_EMPTYBOX, ok_b_emptybox}
66 static char _mboxname[PATH_MAX]; /* Name of mbox */
68 /* Touch the indicated file */
69 static void _alter(char const *name);
71 /* Preserve all the appropriate messages back in the system mailbox, and print
72 * a nice message indicated how many were saved. On any error, just return -1.
73 * Else return 0. Incorporate the any new mail that we found */
74 static int writeback(FILE *res, FILE *obuf);
76 /* Terminate an editing session by attempting to write out the user's file from
77 * the temporary. Save any new stuff appended to the file */
78 static void edstop(void);
80 static void
81 _alter(char const *name)
83 struct stat sb;
84 struct utimbuf utb;
85 NYD_ENTER;
87 if (!stat(name, &sb)) {
88 utb.actime = time(NULL) + 1;
89 utb.modtime = sb.st_mtime;
90 utime(name, &utb);
92 NYD_LEAVE;
95 static int
96 writeback(FILE *res, FILE *obuf)
98 struct message *mp;
99 int rv = -1, p, c;
100 NYD_ENTER;
102 if (fseek(obuf, 0L, SEEK_SET) == -1)
103 goto jleave;
105 #ifndef APPEND
106 if (res != NULL)
107 while ((c = getc(res)) != EOF)
108 putc(c, obuf);
109 #endif
110 srelax_hold();
111 for (p = 0, mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
112 if ((mp->m_flag & MPRESERVE) || !(mp->m_flag & MTOUCH)) {
113 ++p;
114 if (sendmp(mp, obuf, NULL, NULL, SEND_MBOX, NULL) < 0) {
115 srelax_rele();
116 goto jerror;
118 srelax();
120 srelax_rele();
121 #ifdef APPEND
122 if (res != NULL)
123 while ((c = getc(res)) != EOF)
124 putc(c, obuf);
125 #endif
126 fflush(obuf);
127 ftrunc(obuf);
129 if (ferror(obuf)) {
130 jerror:
131 perror(mailname);
132 fseek(obuf, 0L, SEEK_SET);
133 goto jleave;
135 if (fseek(obuf, 0L, SEEK_SET) == -1)
136 goto jleave;
138 _alter(mailname);
139 if (p == 1)
140 printf(tr(155, "Held 1 message in %s\n"), displayname);
141 else
142 printf(tr(156, "Held %d messages in %s\n"), p, displayname);
143 rv = 0;
144 jleave:
145 if (res != NULL)
146 Fclose(res);
147 NYD_LEAVE;
148 return rv;
151 static void
152 edstop(void) /* TODO oh my god - and REMOVE that CRAPPY reset(0) jump!! */
154 int gotcha, c;
155 struct message *mp;
156 FILE *obuf = NULL, *ibuf = NULL;
157 struct stat statb;
158 bool_t doreset;
159 NYD_ENTER;
161 hold_sigs();
162 doreset = FAL0;
164 if (mb.mb_perm == 0)
165 goto jleave;
167 for (mp = message, gotcha = 0; PTRCMP(mp, <, message + msgCount); ++mp) {
168 if (mp->m_flag & MNEW) {
169 mp->m_flag &= ~MNEW;
170 mp->m_flag |= MSTATUS;
172 if (mp->m_flag & (MODIFY | MDELETED | MSTATUS | MFLAG | MUNFLAG |
173 MANSWER | MUNANSWER | MDRAFT | MUNDRAFT))
174 ++gotcha;
176 if (!gotcha)
177 goto jleave;
179 doreset = TRU1;
181 if (stat(mailname, &statb) >= 0 && statb.st_size > mailsize) {
182 if ((obuf = Ftmp(NULL, "edstop", OF_RDWR | OF_UNLINK | OF_REGISTER,
183 0600)) == NULL) {
184 perror(tr(167, "tmpfile"));
185 goto jleave;
187 if ((ibuf = Zopen(mailname, "r", &mb.mb_compressed)) == NULL) {
188 perror(mailname);
189 Fclose(obuf);
190 goto jleave;
192 fseek(ibuf, (long)mailsize, SEEK_SET);
193 while ((c = getc(ibuf)) != EOF)
194 putc(c, obuf);
195 Fclose(ibuf);
196 ibuf = obuf;
197 fflush_rewind(obuf);
200 printf(tr(168, "\"%s\" "), displayname);
201 fflush(stdout);
202 if ((obuf = Zopen(mailname, "r+", &mb.mb_compressed)) == NULL) {
203 perror(mailname);
204 goto jleave;
206 ftrunc(obuf);
208 srelax_hold();
209 c = 0;
210 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp) {
211 if ((mp->m_flag & MDELETED) != 0)
212 continue;
213 ++c;
214 if (sendmp(mp, obuf, NULL, NULL, SEND_MBOX, NULL) < 0) {
215 perror(mailname);
216 srelax_rele();
217 goto jleave;
219 srelax();
221 srelax_rele();
223 gotcha = (c == 0 && ibuf == NULL);
224 if (ibuf != NULL) {
225 while ((c = getc(ibuf)) != EOF)
226 putc(c, obuf);
227 Fclose(ibuf);
229 fflush(obuf);
230 if (ferror(obuf)) {
231 perror(mailname);
232 goto jleave;
234 Fclose(obuf);
236 doreset = FAL0;
238 if (gotcha && !ok_blook(emptybox)) {
239 rm(mailname);
240 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
241 ? tr(169, "removed\n") : tr(211, "removed.\n"));
242 } else
243 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
244 ? tr(170, "complete\n") : tr(212, "updated.\n"));
245 fflush(stdout);
246 jleave:
247 if (ibuf != NULL)
248 Fclose(ibuf);
249 rele_sigs();
250 NYD_LEAVE;
251 if (doreset)
252 reset(0);
255 FL int
256 c_quit(void *v)
258 int rv;
259 NYD_ENTER;
260 UNUSED(v);
262 /* If we are sourcing, then return 1 so evaluate() can handle it.
263 * Otherwise return -1 to abort command loop */
264 rv = sourcing ? 1 : -1;
265 NYD_LEAVE;
266 return rv;
269 FL void
270 quit(void)
272 int p, modify, anystat, c;
273 FILE *fbuf = NULL, *rbuf, *abuf;
274 struct message *mp;
275 char *tempResid;
276 struct stat minfo;
277 NYD_ENTER;
279 /* If we are read only, we can't do anything, so just return quickly. IMAP
280 * can set some flags (e.g. "\\Seen") so imap_quit must be called anyway */
281 if (mb.mb_perm == 0 && mb.mb_type != MB_IMAP)
282 goto jleave;
284 /* TODO lex.c:setfile() has just called hold_sigs(); before it called
285 * TODO us, but this causes uninterruptible hangs due to blocked sigs
286 * TODO anywhere except for MB_FILE (all others install their own
287 * TODO handlers, as it seems, properly); marked YYY */
288 switch (mb.mb_type) {
289 case MB_FILE:
290 break;
291 case MB_MAILDIR:
292 rele_sigs(); /* YYY */
293 maildir_quit();
294 hold_sigs(); /* YYY */
295 goto jleave;
296 #ifdef HAVE_POP3
297 case MB_POP3:
298 rele_sigs(); /* YYY */
299 pop3_quit();
300 hold_sigs(); /* YYY */
301 goto jleave;
302 #endif
303 #ifdef HAVE_IMAP
304 case MB_IMAP:
305 case MB_CACHE:
306 rele_sigs(); /* YYY */
307 imap_quit();
308 hold_sigs(); /* YYY */
309 goto jleave;
310 #endif
311 case MB_VOID:
312 default:
313 goto jleave;
316 /* If editing (not reading system mail box), then do the work in edstop() */
317 if (edit) {
318 edstop();
319 goto jleave;
322 /* See if there any messages to save in mbox. If no, we
323 * can save copying mbox to /tmp and back.
325 * Check also to see if any files need to be preserved.
326 * Delete all untouched messages to keep them out of mbox.
327 * If all the messages are to be preserved, just exit with
328 * a message */
329 fbuf = Zopen(mailname, "r+", &mb.mb_compressed);
330 if (fbuf == NULL) {
331 if (errno == ENOENT)
332 goto jleave;
333 jnewmail:
334 printf(tr(166, "Thou hast new mail.\n"));
335 goto jleave;
338 if (fcntl_lock(fileno(fbuf), FLOCK_WRITE) == -1 ||
339 dot_lock(mailname, fileno(fbuf), 1, stdout, ".") == -1) {
340 perror(tr(157, "Unable to lock mailbox"));
341 Fclose(fbuf);
342 fbuf = NULL;
343 goto jleave;
346 rbuf = NULL;
347 if (fstat(fileno(fbuf), &minfo) >= 0 && minfo.st_size > mailsize) {
348 printf(tr(158, "New mail has arrived.\n"));
349 rbuf = Ftmp(&tempResid, "quit", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600);
350 if (rbuf == NULL || fbuf == NULL)
351 goto jnewmail;
352 #ifdef APPEND
353 fseek(fbuf, (long)mailsize, SEEK_SET);
354 while ((c = getc(fbuf)) != EOF)
355 putc(c, rbuf);
356 #else
357 p = minfo.st_size - mailsize;
358 while (p-- > 0) {
359 c = getc(fbuf);
360 if (c == EOF)
361 goto jnewmail;
362 putc(c, rbuf);
364 #endif
365 fflush_rewind(rbuf);
368 anystat = holdbits();
369 modify = 0;
370 for (c = 0, p = 0, mp = message; PTRCMP(mp, <, message + msgCount); ++mp) {
371 if (mp->m_flag & MBOX)
372 c++;
373 if (mp->m_flag & MPRESERVE)
374 p++;
375 if (mp->m_flag & MODIFY)
376 modify++;
378 if (p == msgCount && !modify && !anystat) {
379 if (p == 1)
380 printf(tr(155, "Held 1 message in %s\n"), displayname);
381 else if (p > 1)
382 printf(tr(156, "Held %d messages in %s\n"), p, displayname);
383 goto jleave;
385 if (c == 0) {
386 if (p != 0) {
387 writeback(rbuf, fbuf);
388 goto jleave;
390 goto jcream;
393 if (makembox() == STOP)
394 goto jleave;
396 /* Now we are ready to copy back preserved files to the system mailbox, if
397 * any were requested */
398 if (p != 0) {
399 writeback(rbuf, fbuf);
400 goto jleave;
403 /* Finally, remove his file. If new mail has arrived, copy it back */
404 jcream:
405 if (rbuf != NULL) {
406 abuf = fbuf;
407 fseek(abuf, 0L, SEEK_SET);
408 while ((c = getc(rbuf)) != EOF)
409 putc(c, abuf);
410 Fclose(rbuf);
411 ftrunc(abuf);
412 _alter(mailname);
413 goto jleave;
415 demail();
416 jleave:
417 if (fbuf != NULL) {
418 Fclose(fbuf);
419 dot_unlock(mailname);
421 NYD_LEAVE;
424 FL int
425 holdbits(void)
427 struct message *mp;
428 int anystat, autohold, holdbit, nohold;
429 NYD_ENTER;
431 anystat = 0;
432 autohold = ok_blook(hold);
433 holdbit = autohold ? MPRESERVE : MBOX;
434 nohold = MBOX | MSAVED | MDELETED | MPRESERVE;
435 if (ok_blook(keepsave))
436 nohold &= ~MSAVED;
437 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp) {
438 if (mp->m_flag & MNEW) {
439 mp->m_flag &= ~MNEW;
440 mp->m_flag |= MSTATUS;
442 if (mp->m_flag & (MSTATUS | MFLAG | MUNFLAG | MANSWER | MUNANSWER |
443 MDRAFT | MUNDRAFT))
444 ++anystat;
445 if (!(mp->m_flag & MTOUCH))
446 mp->m_flag |= MPRESERVE;
447 if (!(mp->m_flag & nohold))
448 mp->m_flag |= holdbit;
450 NYD_LEAVE;
451 return anystat;
454 FL enum okay
455 makembox(void) /* TODO oh my god */
457 struct message *mp;
458 char *mbox, *tempQuit;
459 int mcount, c;
460 FILE *ibuf = NULL, *obuf, *abuf;
461 enum protocol prot;
462 enum okay rv = STOP;
463 NYD_ENTER;
465 mbox = _mboxname;
466 mcount = 0;
467 if (!ok_blook(append)) {
468 if ((obuf = Ftmp(&tempQuit, "makembox",
469 OF_WRONLY | OF_HOLDSIGS | OF_REGISTER, 0600)) == NULL) {
470 perror(tr(163, "temporary mail quit file"));
471 goto jleave;
473 if ((ibuf = Fopen(tempQuit, "r")) == NULL)
474 perror(tempQuit);
475 Ftmp_release(&tempQuit);
476 if (ibuf == NULL) {
477 Fclose(obuf);
478 goto jleave;
481 if ((abuf = Zopen(mbox, "r", NULL)) != NULL) {
482 while ((c = getc(abuf)) != EOF)
483 putc(c, obuf);
484 Fclose(abuf);
486 if (ferror(obuf)) {
487 perror(tr(163, "temporary mail quit file"));
488 Fclose(ibuf);
489 Fclose(obuf);
490 goto jleave;
492 Fclose(obuf);
494 if ((c = open(mbox, O_CREAT | O_TRUNC | O_WRONLY, 0600)) >= 0)
495 close(c);
496 if ((obuf = Zopen(mbox, "r+", NULL)) == NULL) {
497 perror(mbox);
498 Fclose(ibuf);
499 goto jleave;
501 } else {
502 if ((obuf = Zopen(mbox, "a", NULL)) == NULL) {
503 perror(mbox);
504 goto jleave;
506 fchmod(fileno(obuf), 0600);
509 srelax_hold();
510 prot = which_protocol(mbox);
511 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp) {
512 if (mp->m_flag & MBOX) {
513 ++mcount;
514 if (prot == PROTO_IMAP &&
515 saveignore[0].i_count == 0 && saveignore[1].i_count == 0
516 #ifdef HAVE_IMAP /* TODO revisit */
517 && imap_thisaccount(mbox)
518 #endif
520 #ifdef HAVE_IMAP
521 if (imap_copy(mp, PTR2SIZE(mp - message + 1), mbox) == STOP)
522 #endif
523 goto jerr;
524 } else if (sendmp(mp, obuf, saveignore, NULL, SEND_MBOX, NULL) < 0) {
525 perror(mbox);
526 jerr:
527 if (ibuf != NULL)
528 Fclose(ibuf);
529 Fclose(obuf);
530 srelax_rele();
531 goto jleave;
533 mp->m_flag |= MBOXED;
534 srelax();
537 srelax_rele();
539 /* Copy the user's old mbox contents back to the end of the stuff we just
540 * saved. If we are appending, this is unnecessary */
541 if (!ok_blook(append)) {
542 rewind(ibuf);
543 c = getc(ibuf);
544 while (c != EOF) {
545 putc(c, obuf);
546 if (ferror(obuf))
547 break;
548 c = getc(ibuf);
550 Fclose(ibuf);
551 fflush(obuf);
553 ftrunc(obuf);
554 if (ferror(obuf)) {
555 perror(mbox);
556 Fclose(obuf);
557 goto jleave;
559 if (Fclose(obuf) != 0) {
560 if (prot != PROTO_IMAP)
561 perror(mbox);
562 goto jleave;
564 if (mcount == 1)
565 printf(tr(164, "Saved 1 message in mbox\n"));
566 else
567 printf(tr(165, "Saved %d messages in mbox\n"), mcount);
568 rv = OKAY;
569 jleave:
570 NYD_LEAVE;
571 return rv;
574 FL void
575 save_mbox_for_possible_quitstuff(void) /* TODO try to get rid of that */
577 char const *cp;
578 NYD_ENTER;
580 if ((cp = expand("&")) == NULL)
581 cp = "";
582 n_strlcpy(_mboxname, cp, sizeof _mboxname);
583 NYD_LEAVE;
586 FL int
587 savequitflags(void)
589 enum quitflags qf = 0;
590 size_t i;
591 NYD_ENTER;
593 for (i = 0; i < NELEM(_quitnames); ++i)
594 if (_var_oklook(_quitnames[i].okey) != NULL)
595 qf |= _quitnames[i].flag;
596 NYD_LEAVE;
597 return qf;
600 FL void
601 restorequitflags(int qf)
603 size_t i;
604 NYD_ENTER;
606 for (i = 0; i < NELEM(_quitnames); ++i) {
607 char *x = _var_oklook(_quitnames[i].okey);
608 if (qf & _quitnames[i].flag) {
609 if (x == NULL)
610 _var_okset(_quitnames[i].okey, TRU1);
611 } else if (x != NULL)
612 _var_okclear(_quitnames[i].okey);
614 NYD_LEAVE;
617 /* vim:set fenc=utf-8:s-it-mode */