WANT_AMALGAMATION will henceforth work through main.c
[s-mailx.git] / quit.c
blob64cca8113a3c5dc6aae49c7ab87e226ebe7d9dd2
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 - 2015 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.
39 #undef n_FILE
40 #define n_FILE quit
42 #ifndef HAVE_AMALGAMATION
43 # include "nail.h"
44 #endif
46 #include <utime.h>
48 enum quitflags {
49 QUITFLAG_HOLD = 1<<0,
50 QUITFLAG_KEEP = 1<<1,
51 QUITFLAG_KEEPSAVE = 1<<2,
52 QUITFLAG_APPEND = 1<<3,
53 QUITFLAG_EMPTYBOX = 1<<4
56 struct quitnames {
57 enum quitflags flag;
58 enum okeys okey;
61 static struct quitnames const _quitnames[] = {
62 {QUITFLAG_HOLD, ok_b_hold},
63 {QUITFLAG_KEEP, ok_b_keep},
64 {QUITFLAG_KEEPSAVE, ok_b_keepsave},
65 {QUITFLAG_APPEND, ok_b_append},
66 {QUITFLAG_EMPTYBOX, ok_b_emptybox} /* TODO obsolete emptybox */
69 static char _mboxname[PATH_MAX]; /* Name of mbox */
71 /* Touch the indicated file */
72 static void _alter(char const *name);
74 /* Preserve all the appropriate messages back in the system mailbox, and print
75 * a nice message indicated how many were saved. On any error, just return -1.
76 * Else return 0. Incorporate the any new mail that we found */
77 static int writeback(FILE *res, FILE *obuf);
79 /* Terminate an editing session by attempting to write out the user's file from
80 * the temporary. Save any new stuff appended to the file */
81 static void edstop(void);
83 /* Remove "mailname", unless *keep* says otherwise; force truncation, then */
84 static void _demail(void);
86 static void
87 _alter(char const *name)
89 struct stat sb;
90 struct utimbuf utb;
91 NYD_ENTER;
93 if (!stat(name, &sb)) {
94 utb.actime = time(NULL) + 1;
95 utb.modtime = sb.st_mtime;
96 utime(name, &utb);
98 NYD_LEAVE;
101 static int
102 writeback(FILE *res, FILE *obuf)
104 struct message *mp;
105 int rv = -1, p, c;
106 NYD_ENTER;
108 if (fseek(obuf, 0L, SEEK_SET) == -1)
109 goto jleave;
111 #ifndef APPEND
112 if (res != NULL)
113 while ((c = getc(res)) != EOF)
114 putc(c, obuf);
115 #endif
116 srelax_hold();
117 for (p = 0, mp = message; PTRCMP(mp, <, message + msgCount); ++mp)
118 if ((mp->m_flag & MPRESERVE) || !(mp->m_flag & MTOUCH)) {
119 ++p;
120 if (sendmp(mp, obuf, NULL, NULL, SEND_MBOX, NULL) < 0) {
121 n_perr(mailname, 0);
122 srelax_rele();
123 goto jerror;
125 srelax();
127 srelax_rele();
128 #ifdef APPEND
129 if (res != NULL)
130 while ((c = getc(res)) != EOF)
131 putc(c, obuf);
132 #endif
133 ftrunc(obuf);
135 if (ferror(obuf)) {
136 n_perr(mailname, 0);
137 jerror:
138 fseek(obuf, 0L, SEEK_SET);
139 goto jleave;
141 if (fseek(obuf, 0L, SEEK_SET) == -1)
142 goto jleave;
144 _alter(mailname);
145 if (p == 1)
146 printf(_("Held 1 message in %s\n"), displayname);
147 else
148 printf(_("Held %d messages in %s\n"), p, displayname);
149 rv = 0;
150 jleave:
151 if (res != NULL)
152 Fclose(res);
153 NYD_LEAVE;
154 return rv;
157 static void
158 edstop(void) /* TODO oh my god - and REMOVE that CRAPPY reset(0) jump!! */
160 int gotcha, c;
161 struct message *mp;
162 FILE *obuf = NULL, *ibuf = NULL;
163 struct stat statb;
164 bool_t doreset;
165 NYD_ENTER;
167 hold_sigs();
168 doreset = FAL0;
170 if (mb.mb_perm == 0)
171 goto jleave;
173 for (mp = message, gotcha = 0; PTRCMP(mp, <, message + msgCount); ++mp) {
174 if (mp->m_flag & MNEW) {
175 mp->m_flag &= ~MNEW;
176 mp->m_flag |= MSTATUS;
178 if (mp->m_flag & (MODIFY | MDELETED | MSTATUS | MFLAG | MUNFLAG |
179 MANSWER | MUNANSWER | MDRAFT | MUNDRAFT))
180 ++gotcha;
182 if (!gotcha)
183 goto jleave;
185 doreset = TRU1;
187 /* TODO This is too simple minded? We should regenerate an index file
188 * TODO to be able to truly tell wether *anything* has changed!
189 * TODO (Or better: only come here.. then! It is an *object method!* */
190 /* TODO Ignoring stat error is easy, huh? */
191 if (!stat(mailname, &statb) && statb.st_size > mailsize) {
192 if ((obuf = Ftmp(NULL, "edstop", OF_RDWR | OF_UNLINK | OF_REGISTER,
193 0600)) == NULL) {
194 n_perr(_("tmpfile"), 0);
195 goto jleave;
197 if ((ibuf = Zopen(mailname, "r")) == NULL) {
198 n_perr(mailname, 0);
199 Fclose(obuf);
200 goto jleave;
203 file_lock(fileno(ibuf), FLT_READ, 0,0, 1); /* TODO ignoring lock error! */
204 fseek(ibuf, (long)mailsize, SEEK_SET);
205 while ((c = getc(ibuf)) != EOF) /* xxx bytewise??? TODO ... I/O error? */
206 putc(c, obuf);
207 Fclose(ibuf);
208 ibuf = obuf;
209 fflush_rewind(obuf);
212 printf(_("\"%s\" "), displayname);
213 fflush(stdout);
214 if ((obuf = Zopen(mailname, "r+")) == NULL) {
215 n_perr(mailname, 0);
216 goto jleave;
219 file_lock(fileno(obuf), FLT_WRITE, 0,0, 1); /* TODO ignoring lock error! */
220 ftrunc(obuf);
222 srelax_hold();
223 c = 0;
224 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp) {
225 if (mp->m_flag & MDELETED)
226 continue;
227 ++c;
228 if (sendmp(mp, obuf, NULL, NULL, SEND_MBOX, NULL) < 0) {
229 n_perr(mailname, 0);
230 srelax_rele();
231 goto jleave;
233 srelax();
235 srelax_rele();
237 gotcha = (c == 0 && ibuf == NULL);
238 if (ibuf != NULL) {
239 while ((c = getc(ibuf)) != EOF)
240 putc(c, obuf);
242 fflush(obuf);
243 if (ferror(obuf)) {
244 n_perr(mailname, 0);
245 goto jleave;
247 Fclose(obuf);
249 doreset = FAL0;
251 if (gotcha && !ok_blook(keep) && !ok_blook(emptybox)/* TODO obsolete eb*/) {
252 rm(mailname);
253 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
254 ? _("removed\n") : _("removed.\n"));
255 } else
256 printf((ok_blook(bsdcompat) || ok_blook(bsdmsgs))
257 ? _("complete\n") : _("updated.\n"));
258 fflush(stdout);
259 jleave:
260 if (ibuf != NULL)
261 Fclose(ibuf);
262 rele_sigs();
263 NYD_LEAVE;
264 if (doreset)
265 reset(0);
268 static void
269 _demail(void)
271 NYD2_ENTER;
272 if (ok_blook(keep) || rm(mailname) < 0) {
273 /* TODO demail(): try use f?truncate(2) instead?! */
274 int fd = open(mailname, O_WRONLY | O_CREAT | O_TRUNC, 0600);
275 if (fd >= 0)
276 close(fd);
278 NYD2_LEAVE;
281 FL void
282 quit(void)
284 int p, modify, anystat, c;
285 FILE *fbuf = NULL, *lckfp = NULL, *rbuf, *abuf;
286 struct message *mp;
287 struct stat minfo;
288 NYD_ENTER;
290 temporary_localopts_folder_hook_unroll();
292 /* If we are read only, we can't do anything, so just return quickly */
293 /* TODO yet we cannot return quickly if resources have to be released!
294 * TODO somewhen it'll be mailbox->quit() anyway, for now do it by hand
295 *if (mb.mb_perm == 0)
296 * goto jleave;*/
297 p = (mb.mb_perm == 0);
299 /* TODO lex.c:setfile() has just called hold_sigs(); before it called
300 * TODO us, but this causes uninterruptible hangs due to blocked sigs
301 * TODO anywhere except for MB_FILE (all others install their own
302 * TODO handlers, as it seems, properly); marked YYY */
303 switch (mb.mb_type) {
304 case MB_FILE:
305 break;
306 case MB_MAILDIR:
307 rele_sigs(); /* YYY */
308 maildir_quit();
309 hold_sigs(); /* YYY */
310 goto jleave;
311 #ifdef HAVE_POP3
312 case MB_POP3:
313 rele_sigs(); /* YYY */
314 pop3_quit();
315 hold_sigs(); /* YYY */
316 goto jleave;
317 #endif
318 #ifdef HAVE_IMAP
319 case MB_IMAP:
320 case MB_CACHE:
321 rele_sigs(); /* YYY */
322 imap_quit();
323 hold_sigs(); /* YYY */
324 goto jleave;
325 #endif
326 case MB_VOID:
327 default:
328 goto jleave;
330 if (p) goto jleave; /* TODO */
332 /* If editing (not reading system mail box), then do the work in edstop() */
333 if (pstate & PS_EDIT) {
334 edstop();
335 goto jleave;
338 /* See if there any messages to save in mbox. If no, we
339 * can save copying mbox to /tmp and back.
341 * Check also to see if any files need to be preserved.
342 * Delete all untouched messages to keep them out of mbox.
343 * If all the messages are to be preserved, just exit with
344 * a message */
345 fbuf = Zopen(mailname, "r+");
346 if (fbuf == NULL) {
347 if (errno == ENOENT)
348 goto jleave;
349 jnewmail:
350 printf(_("Thou hast new mail.\n"));
351 goto jleave;
354 if ((lckfp = dot_lock(mailname, fileno(fbuf), FLT_WRITE, 0,0, 1)) == NULL) {
355 n_perr(_("Unable to (dot) lock mailbox, aborting operation"), 0);
356 Fclose(fbuf);
357 fbuf = NULL;
358 goto jleave;
361 rbuf = NULL;
362 if (!fstat(fileno(fbuf), &minfo) && minfo.st_size > mailsize) {
363 printf(_("New mail has arrived.\n"));
364 rbuf = Ftmp(NULL, "quit", OF_RDWR | OF_UNLINK | OF_REGISTER, 0600);
365 if (rbuf == NULL || fbuf == NULL)
366 goto jnewmail;
367 #ifdef APPEND
368 fseek(fbuf, (long)mailsize, SEEK_SET);
369 while ((c = getc(fbuf)) != EOF)
370 putc(c, rbuf);
371 #else
372 p = minfo.st_size - mailsize;
373 while (p-- > 0) {
374 c = getc(fbuf);
375 if (c == EOF)
376 goto jnewmail;
377 putc(c, rbuf);
379 #endif
380 fflush_rewind(rbuf);
383 anystat = holdbits();
384 modify = 0;
385 for (c = 0, p = 0, mp = message; PTRCMP(mp, <, message + msgCount); ++mp) {
386 if (mp->m_flag & MBOX)
387 c++;
388 if (mp->m_flag & MPRESERVE)
389 p++;
390 if (mp->m_flag & MODIFY)
391 modify++;
393 if (p == msgCount && !modify && !anystat) {
394 if (p == 1)
395 printf(_("Held 1 message in %s\n"), displayname);
396 else if (p > 1)
397 printf(_("Held %d messages in %s\n"), p, displayname);
398 goto jleave;
400 if (c == 0) {
401 if (p != 0) {
402 writeback(rbuf, fbuf);
403 goto jleave;
405 goto jcream;
408 if (makembox() == STOP)
409 goto jleave;
411 /* Now we are ready to copy back preserved files to the system mailbox, if
412 * any were requested */
413 if (p != 0) {
414 writeback(rbuf, fbuf);
415 goto jleave;
418 /* Finally, remove his file. If new mail has arrived, copy it back */
419 jcream:
420 if (rbuf != NULL) {
421 abuf = fbuf;
422 fseek(abuf, 0L, SEEK_SET);
423 while ((c = getc(rbuf)) != EOF)
424 putc(c, abuf);
425 Fclose(rbuf);
426 ftrunc(abuf);
427 _alter(mailname);
428 goto jleave;
430 _demail();
431 jleave:
432 if (fbuf != NULL) {
433 Fclose(fbuf);
434 if (lckfp != NULL && lckfp != (FILE*)-1)
435 Pclose(lckfp, FAL0);
437 NYD_LEAVE;
440 FL int
441 holdbits(void)
443 struct message *mp;
444 int anystat, autohold, holdbit, nohold;
445 NYD_ENTER;
447 anystat = 0;
448 autohold = ok_blook(hold);
449 holdbit = autohold ? MPRESERVE : MBOX;
450 nohold = MBOX | MSAVED | MDELETED | MPRESERVE;
451 if (ok_blook(keepsave))
452 nohold &= ~MSAVED;
453 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp) {
454 if (mp->m_flag & MNEW) {
455 mp->m_flag &= ~MNEW;
456 mp->m_flag |= MSTATUS;
458 if (mp->m_flag & (MSTATUS | MFLAG | MUNFLAG | MANSWER | MUNANSWER |
459 MDRAFT | MUNDRAFT))
460 ++anystat;
461 if (!(mp->m_flag & MTOUCH))
462 mp->m_flag |= MPRESERVE;
463 if (!(mp->m_flag & nohold))
464 mp->m_flag |= holdbit;
466 NYD_LEAVE;
467 return anystat;
470 FL enum okay
471 makembox(void) /* TODO oh my god */
473 struct message *mp;
474 char *mbox, *tempQuit;
475 int mcount, c;
476 FILE *ibuf = NULL, *obuf, *abuf;
477 enum protocol prot;
478 enum okay rv = STOP;
479 NYD_ENTER;
481 mbox = _mboxname;
482 mcount = 0;
483 if (!ok_blook(append)) {
484 if ((obuf = Ftmp(&tempQuit, "makembox",
485 OF_WRONLY | OF_HOLDSIGS | OF_REGISTER, 0600)) == NULL) {
486 n_perr(_("temporary mail quit file"), 0);
487 goto jleave;
489 if ((ibuf = Fopen(tempQuit, "r")) == NULL)
490 n_perr(tempQuit, 0);
491 Ftmp_release(&tempQuit);
492 if (ibuf == NULL) {
493 Fclose(obuf);
494 goto jleave;
497 if ((abuf = Zopen(mbox, "r")) != NULL) {
498 while ((c = getc(abuf)) != EOF)
499 putc(c, obuf);
500 Fclose(abuf);
502 if (ferror(obuf)) {
503 n_perr(_("temporary mail quit file"), 0);
504 Fclose(ibuf);
505 Fclose(obuf);
506 goto jleave;
508 Fclose(obuf);
510 if ((c = open(mbox, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1)
511 close(c);
512 if ((obuf = Zopen(mbox, "r+")) == NULL) {
513 n_perr(mbox, 0);
514 Fclose(ibuf);
515 goto jleave;
517 } else {
518 if ((obuf = Zopen(mbox, "a")) == NULL) {
519 n_perr(mbox, 0);
520 goto jleave;
522 fchmod(fileno(obuf), 0600);
525 srelax_hold();
526 prot = which_protocol(mbox);
527 for (mp = message; PTRCMP(mp, <, message + msgCount); ++mp) {
528 if (mp->m_flag & MBOX) {
529 ++mcount;
530 if (prot == PROTO_IMAP &&
531 saveignore[0].i_count == 0 && saveignore[1].i_count == 0
532 #ifdef HAVE_IMAP /* TODO revisit */
533 && imap_thisaccount(mbox)
534 #endif
536 #ifdef HAVE_IMAP
537 if (imap_copy(mp, PTR2SIZE(mp - message + 1), mbox) == STOP)
538 #endif
539 goto jerr;
540 } else if (sendmp(mp, obuf, saveignore, NULL, SEND_MBOX, NULL) < 0) {
541 n_perr(mbox, 0);
542 jerr:
543 srelax_rele();
544 if (ibuf != NULL)
545 Fclose(ibuf);
546 Fclose(obuf);
547 goto jleave;
549 mp->m_flag |= MBOXED;
550 srelax();
553 srelax_rele();
555 /* Copy the user's old mbox contents back to the end of the stuff we just
556 * saved. If we are appending, this is unnecessary */
557 if (!ok_blook(append)) {
558 rewind(ibuf);
559 c = getc(ibuf);
560 while (c != EOF) {
561 putc(c, obuf);
562 if (ferror(obuf))
563 break;
564 c = getc(ibuf);
566 Fclose(ibuf);
567 fflush(obuf);
569 ftrunc(obuf);
570 if (ferror(obuf)) {
571 n_perr(mbox, 0);
572 Fclose(obuf);
573 goto jleave;
575 if (Fclose(obuf) != 0) {
576 if (prot != PROTO_IMAP)
577 n_perr(mbox, 0);
578 goto jleave;
580 if (mcount == 1)
581 printf(_("Saved 1 message in mbox\n"));
582 else
583 printf(_("Saved %d messages in mbox\n"), mcount);
584 rv = OKAY;
585 jleave:
586 NYD_LEAVE;
587 return rv;
590 FL void
591 save_mbox_for_possible_quitstuff(void) /* TODO try to get rid of that */
593 char const *cp;
594 NYD_ENTER;
596 if ((cp = expand("&")) == NULL)
597 cp = "";
598 n_strlcpy(_mboxname, cp, sizeof _mboxname);
599 NYD_LEAVE;
602 FL int
603 savequitflags(void)
605 enum quitflags qf = 0;
606 size_t i;
607 NYD_ENTER;
609 for (i = 0; i < NELEM(_quitnames); ++i)
610 if (_var_oklook(_quitnames[i].okey) != NULL)
611 qf |= _quitnames[i].flag;
612 NYD_LEAVE;
613 return qf;
616 FL void
617 restorequitflags(int qf)
619 size_t i;
620 NYD_ENTER;
622 for (i = 0; i < NELEM(_quitnames); ++i) {
623 char *x = _var_oklook(_quitnames[i].okey);
624 if (qf & _quitnames[i].flag) {
625 if (x == NULL)
626 _var_okset(_quitnames[i].okey, TRU1);
627 } else if (x != NULL)
628 _var_okclear(_quitnames[i].okey);
630 NYD_LEAVE;
633 /* s-it-mode */