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>.
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
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. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38 #ifndef HAVE_AMALGAMATION
47 QUITFLAG_KEEPSAVE
= 1<<2,
48 QUITFLAG_APPEND
= 1<<3,
49 QUITFLAG_EMPTYBOX
= 1<<4
57 static struct quitnames
const _quitnames
[] = {
58 {QUITFLAG_HOLD
, ok_b_hold
},
59 {QUITFLAG_KEEP
, ok_b_keep
},
60 {QUITFLAG_KEEPSAVE
, ok_b_keepsave
},
61 {QUITFLAG_APPEND
, ok_b_append
},
62 {QUITFLAG_EMPTYBOX
, ok_b_emptybox
} /* TODO obsolete emptybox */
65 static char _mboxname
[PATH_MAX
]; /* Name of mbox */
67 /* Touch the indicated file */
68 static void _alter(char const *name
);
70 /* Preserve all the appropriate messages back in the system mailbox, and print
71 * a nice message indicated how many were saved. On any error, just return -1.
72 * Else return 0. Incorporate the any new mail that we found */
73 static int writeback(FILE *res
, FILE *obuf
);
75 /* Terminate an editing session by attempting to write out the user's file from
76 * the temporary. Save any new stuff appended to the file */
77 static void edstop(void);
79 /* Remove "mailname", unless *keep* says otherwise; force truncation, then */
80 static void _demail(void);
83 _alter(char const *name
)
86 struct timespec tsa
[2];
94 tsa
[0].tv_sec
= n_time_epoch() + 1;
96 tsa
[1].tv_nsec
= UTIME_OMIT
;
97 utimensat(AT_FDCWD
, name
, tsa
, 0);
99 if (!stat(name
, &sb
)) {
100 utb
.actime
= n_time_epoch() + 1;
101 utb
.modtime
= sb
.st_mtime
;
109 writeback(FILE *res
, FILE *obuf
)
115 if (fseek(obuf
, 0L, SEEK_SET
) == -1)
120 while ((c
= getc(res
)) != EOF
)
124 for (p
= 0, mp
= message
; PTRCMP(mp
, <, message
+ msgCount
); ++mp
)
125 if ((mp
->m_flag
& MPRESERVE
) || !(mp
->m_flag
& MTOUCH
)) {
127 if (sendmp(mp
, obuf
, NULL
, NULL
, SEND_MBOX
, NULL
) < 0) {
137 while ((c
= getc(res
)) != EOF
)
145 fseek(obuf
, 0L, SEEK_SET
);
148 if (fseek(obuf
, 0L, SEEK_SET
) == -1)
153 printf(_("Held 1 message in %s\n"), displayname
);
155 printf(_("Held %d messages in %s\n"), p
, displayname
);
165 edstop(void) /* TODO oh my god - and REMOVE that CRAPPY reset(0) jump!! */
169 FILE *obuf
= NULL
, *ibuf
= NULL
;
180 for (mp
= message
, gotcha
= 0; PTRCMP(mp
, <, message
+ msgCount
); ++mp
) {
181 if (mp
->m_flag
& MNEW
) {
183 mp
->m_flag
|= MSTATUS
;
185 if (mp
->m_flag
& (MODIFY
| MDELETED
| MSTATUS
| MFLAG
| MUNFLAG
|
186 MANSWER
| MUNANSWER
| MDRAFT
| MUNDRAFT
))
194 /* TODO This is too simple minded? We should regenerate an index file
195 * TODO to be able to truly tell wether *anything* has changed!
196 * TODO (Or better: only come here.. then! It is an *object method!* */
197 /* TODO Ignoring stat error is easy, huh? */
198 if (!stat(mailname
, &statb
) && statb
.st_size
> mailsize
) {
199 if ((obuf
= Ftmp(NULL
, "edstop", OF_RDWR
| OF_UNLINK
| OF_REGISTER
)) ==
201 n_perr(_("tmpfile"), 0);
204 if ((ibuf
= Zopen(mailname
, "r")) == NULL
) {
210 file_lock(fileno(ibuf
), FLT_READ
, 0,0, 1); /* TODO ignoring lock error! */
211 fseek(ibuf
, (long)mailsize
, SEEK_SET
);
212 while ((c
= getc(ibuf
)) != EOF
) /* xxx bytewise??? TODO ... I/O error? */
219 printf(_("\"%s\" "), displayname
);
221 if ((obuf
= Zopen(mailname
, "r+")) == NULL
) {
226 file_lock(fileno(obuf
), FLT_WRITE
, 0,0, 1); /* TODO ignoring lock error! */
231 for (mp
= message
; PTRCMP(mp
, <, message
+ msgCount
); ++mp
) {
232 if (mp
->m_flag
& MDELETED
)
235 if (sendmp(mp
, obuf
, NULL
, NULL
, SEND_MBOX
, NULL
) < 0) {
244 gotcha
= (c
== 0 && ibuf
== NULL
);
246 while ((c
= getc(ibuf
)) != EOF
)
258 if (gotcha
&& !ok_blook(keep
) && !ok_blook(emptybox
)/* TODO obsolete eb*/) {
260 printf((ok_blook(bsdcompat
) || ok_blook(bsdmsgs
))
261 ? _("removed\n") : _("removed.\n"));
263 printf((ok_blook(bsdcompat
) || ok_blook(bsdmsgs
))
264 ? _("complete\n") : _("updated.\n"));
279 if (ok_blook(keep
) || rm(mailname
) < 0) {
280 /* TODO demail(): try use f?truncate(2) instead?! */
281 int fd
= open(mailname
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0600);
291 int p
, modify
, anystat
, c
;
292 FILE *fbuf
= NULL
, *lckfp
= NULL
, *rbuf
, *abuf
;
297 temporary_localopts_folder_hook_unroll();
299 /* If we are read only, we can't do anything, so just return quickly */
300 /* TODO yet we cannot return quickly if resources have to be released!
301 * TODO somewhen it'll be mailbox->quit() anyway, for now do it by hand
302 *if (mb.mb_perm == 0)
304 p
= (mb
.mb_perm
== 0);
306 /* TODO lex.c:setfile() has just called hold_sigs(); before it called
307 * TODO us, but this causes uninterruptible hangs due to blocked sigs
308 * TODO anywhere except for MB_FILE (all others install their own
309 * TODO handlers, as it seems, properly); marked YYY */
310 switch (mb
.mb_type
) {
314 rele_sigs(); /* YYY */
316 hold_sigs(); /* YYY */
320 rele_sigs(); /* YYY */
322 hold_sigs(); /* YYY */
329 if (p
) goto jleave
; /* TODO */
331 /* If editing (not reading system mail box), then do the work in edstop() */
332 if (pstate
& PS_EDIT
) {
337 /* See if there any messages to save in mbox. If no, we
338 * can save copying mbox to /tmp and back.
340 * Check also to see if any files need to be preserved.
341 * Delete all untouched messages to keep them out of mbox.
342 * If all the messages are to be preserved, just exit with
344 fbuf
= Zopen(mailname
, "r+");
349 printf(_("Thou hast new mail.\n"));
353 if ((lckfp
= dot_lock(mailname
, fileno(fbuf
), FLT_WRITE
, 0,0, 1)) == NULL
) {
354 n_perr(_("Unable to (dot) lock mailbox, aborting operation"), 0);
361 if (!fstat(fileno(fbuf
), &minfo
) && minfo
.st_size
> mailsize
) {
362 printf(_("New mail has arrived.\n"));
363 rbuf
= Ftmp(NULL
, "quit", OF_RDWR
| OF_UNLINK
| OF_REGISTER
);
364 if (rbuf
== NULL
|| fbuf
== NULL
)
367 fseek(fbuf
, (long)mailsize
, SEEK_SET
);
368 while ((c
= getc(fbuf
)) != EOF
)
371 p
= minfo
.st_size
- mailsize
;
382 anystat
= holdbits();
384 for (c
= 0, p
= 0, mp
= message
; PTRCMP(mp
, <, message
+ msgCount
); ++mp
) {
385 if (mp
->m_flag
& MBOX
)
387 if (mp
->m_flag
& MPRESERVE
)
389 if (mp
->m_flag
& MODIFY
)
392 if (p
== msgCount
&& !modify
&& !anystat
) {
394 printf(_("Held 1 message in %s\n"), displayname
);
396 printf(_("Held %d messages in %s\n"), p
, displayname
);
401 writeback(rbuf
, fbuf
);
407 if (makembox() == STOP
)
410 /* Now we are ready to copy back preserved files to the system mailbox, if
411 * any were requested */
413 writeback(rbuf
, fbuf
);
417 /* Finally, remove his file. If new mail has arrived, copy it back */
421 fseek(abuf
, 0L, SEEK_SET
);
422 while ((c
= getc(rbuf
)) != EOF
)
433 if (lckfp
!= NULL
&& lckfp
!= (FILE*)-1)
443 int anystat
, autohold
, holdbit
, nohold
;
447 autohold
= ok_blook(hold
);
448 holdbit
= autohold
? MPRESERVE
: MBOX
;
449 nohold
= MBOX
| MSAVED
| MDELETED
| MPRESERVE
;
450 if (ok_blook(keepsave
))
452 for (mp
= message
; PTRCMP(mp
, <, message
+ msgCount
); ++mp
) {
453 if (mp
->m_flag
& MNEW
) {
455 mp
->m_flag
|= MSTATUS
;
457 if (mp
->m_flag
& (MSTATUS
| MFLAG
| MUNFLAG
| MANSWER
| MUNANSWER
|
460 if (!(mp
->m_flag
& MTOUCH
))
461 mp
->m_flag
|= MPRESERVE
;
462 if (!(mp
->m_flag
& nohold
))
463 mp
->m_flag
|= holdbit
;
470 makembox(void) /* TODO oh my god */
473 char *mbox
, *tempQuit
;
475 FILE *ibuf
= NULL
, *obuf
, *abuf
;
481 if (!ok_blook(append
)) {
482 if ((obuf
= Ftmp(&tempQuit
, "makembox",
483 OF_WRONLY
| OF_HOLDSIGS
| OF_REGISTER
)) == NULL
) {
484 n_perr(_("temporary mail quit file"), 0);
487 if ((ibuf
= Fopen(tempQuit
, "r")) == NULL
)
489 Ftmp_release(&tempQuit
);
495 if ((abuf
= Zopen(mbox
, "r")) != NULL
) {
496 while ((c
= getc(abuf
)) != EOF
)
501 n_perr(_("temporary mail quit file"), 0);
508 if ((c
= open(mbox
, O_CREAT
| O_TRUNC
| O_WRONLY
, 0600)) != -1)
510 if ((obuf
= Zopen(mbox
, "r+")) == NULL
) {
516 if ((obuf
= Zopen(mbox
, "a")) == NULL
) {
520 fchmod(fileno(obuf
), 0600);
524 for (mp
= message
; PTRCMP(mp
, <, message
+ msgCount
); ++mp
) {
525 if (mp
->m_flag
& MBOX
) {
527 if (sendmp(mp
, obuf
, saveignore
, NULL
, SEND_MBOX
, NULL
) < 0) {
535 mp
->m_flag
|= MBOXED
;
541 /* Copy the user's old mbox contents back to the end of the stuff we just
542 * saved. If we are appending, this is unnecessary */
543 if (!ok_blook(append
)) {
561 if (Fclose(obuf
) != 0) {
566 printf(_("Saved 1 message in mbox\n"));
568 printf(_("Saved %d messages in mbox\n"), mcount
);
576 save_mbox_for_possible_quitstuff(void) /* TODO try to get rid of that */
581 if ((cp
= expand("&")) == NULL
)
583 n_strscpy(_mboxname
, cp
, sizeof _mboxname
);
590 enum quitflags qf
= 0;
594 for (i
= 0; i
< NELEM(_quitnames
); ++i
)
595 if (_var_oklook(_quitnames
[i
].okey
) != NULL
)
596 qf
|= _quitnames
[i
].flag
;
602 restorequitflags(int qf
)
607 for (i
= 0; i
< NELEM(_quitnames
); ++i
) {
608 char *x
= _var_oklook(_quitnames
[i
].okey
);
609 if (qf
& _quitnames
[i
].flag
) {
611 _var_okset(_quitnames
[i
].okey
, TRU1
);
612 } else if (x
!= NULL
)
613 _var_okclear(_quitnames
[i
].okey
);