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>.
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. 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
40 #ifndef HAVE_AMALGAMATION
47 static char _mboxname
[MAXPATHLEN
]; /* Name of mbox */
49 /* Touch the indicated file */
50 static void alter(char const *name
);
52 static int writeback(FILE *res
, FILE *obuf
);
53 static void edstop(void);
56 alter(char const *name
)
63 utb
.actime
= time((time_t *)0) + 1;
64 utb
.modtime
= sb
.st_mtime
;
77 * If we are sourcing, then return 1 so execute() can handle it.
78 * Otherwise, return -1 to abort command loop.
86 * Preserve all the appropriate messages back in the system
87 * mailbox, and print a nice message indicated how many were
88 * saved. On any error, just return -1. Else return 0.
89 * Incorporate the any new mail that we found.
92 writeback(FILE *res
, FILE *obuf
)
98 if (fseek(obuf
, 0L, SEEK_SET
) < 0)
102 while ((c
= getc(res
)) != EOF
)
106 for (mp
= &message
[0]; mp
< &message
[msgCount
]; mp
++)
107 if ((mp
->m_flag
&MPRESERVE
)||(mp
->m_flag
&MTOUCH
)==0) {
109 if (sendmp(mp
, obuf
, NULL
, NULL
, SEND_MBOX
, NULL
) < 0) {
111 (void)fseek(obuf
, 0L, SEEK_SET
);
121 while ((c
= getc(res
)) != EOF
)
128 (void)fseek(obuf
, 0L, SEEK_SET
);
133 if (fseek(obuf
, 0L, SEEK_SET
) < 0)
137 printf(tr(155, "Held 1 message in %s\n"), displayname
);
139 printf(tr(156, "Held %d messages in %s\n"), p
, displayname
);
144 * Save all of the undetermined messages at the top of "mbox"
145 * Save all untouched messages back in the system mailbox.
146 * Remove the system mailbox, if none saved there.
151 int p
, modify
, anystat
;
152 FILE *fbuf
, *rbuf
, *abuf
;
159 * If we are read only, we can't do anything,
160 * so just return quickly. IMAP can set some
161 * flags (e.g. "\\Seen") so imap_quit must be
164 if (mb
.mb_perm
== 0 && mb
.mb_type
!= MB_IMAP
)
166 /* TODO lex.c:setfile() has just called hold_sigs(); before it called
167 * TODO us, but this causes uninterruptible hangs due to blocked sigs
168 * TODO anywhere except for MB_FILE (all others install their own
169 * TODO handlers, as it seems, properly); marked YYY */
170 switch (mb
.mb_type
) {
174 rele_sigs(); /* YYY */
176 hold_sigs(); /* YYY */
180 rele_sigs(); /* YYY */
182 hold_sigs(); /* YYY */
188 rele_sigs(); /* YYY */
190 hold_sigs(); /* YYY */
198 * If editing (not reading system mail box), then do the work
207 * See if there any messages to save in mbox. If no, we
208 * can save copying mbox to /tmp and back.
210 * Check also to see if any files need to be preserved.
211 * Delete all untouched messages to keep them out of mbox.
212 * If all the messages are to be preserved, just exit with
216 fbuf
= Zopen(mailname
, "r+", &mb
.mb_compressed
);
222 if (fcntl_lock(fileno(fbuf
), F_WRLCK
) == -1) {
224 perror(tr(157, "Unable to lock mailbox"));
228 if (dot_lock(mailname
, fileno(fbuf
), 1, stdout
, ".") == -1)
231 if (fstat(fileno(fbuf
), &minfo
) >= 0 && minfo
.st_size
> mailsize
) {
232 printf(tr(158, "New mail has arrived.\n"));
233 rbuf
= Ftemp(&tempResid
, "Rq", "w", 0600, 1);
234 if (rbuf
== NULL
|| fbuf
== NULL
)
237 fseek(fbuf
, (long)mailsize
, SEEK_SET
);
238 while ((c
= getc(fbuf
)) != EOF
)
241 p
= minfo
.st_size
- mailsize
;
250 if ((rbuf
= Fopen(tempResid
, "r")) == NULL
)
256 anystat
= holdbits();
258 for (c
= 0, p
= 0, mp
= &message
[0]; mp
< &message
[msgCount
]; mp
++) {
259 if (mp
->m_flag
& MBOX
)
261 if (mp
->m_flag
& MPRESERVE
)
263 if (mp
->m_flag
& MODIFY
)
266 if (p
== msgCount
&& !modify
&& !anystat
) {
268 printf(tr(155, "Held 1 message in %s\n"), displayname
);
270 printf(tr(156, "Held %d messages in %s\n"),
273 dot_unlock(mailname
);
278 writeback(rbuf
, fbuf
);
280 dot_unlock(mailname
);
286 if (makembox() == STOP
) {
288 dot_unlock(mailname
);
292 * Now we are ready to copy back preserved files to
293 * the system mailbox, if any were requested.
297 writeback(rbuf
, fbuf
);
299 dot_unlock(mailname
);
304 * Finally, remove his /usr/mail file.
305 * If new mail has arrived, copy it back.
311 fseek(abuf
, 0L, SEEK_SET
);
312 while ((c
= getc(rbuf
)) != EOF
)
318 dot_unlock(mailname
);
323 dot_unlock(mailname
);
327 printf(tr(166, "Thou hast new mail.\n"));
330 dot_unlock(mailname
);
335 * Adjust the message flags in each message.
341 int anystat
, autohold
, holdbit
, nohold
;
344 autohold
= value("hold") != NULL
;
345 holdbit
= autohold
? MPRESERVE
: MBOX
;
346 nohold
= MBOX
|MSAVED
|MDELETED
|MPRESERVE
;
347 if (value("keepsave") != NULL
)
349 for (mp
= &message
[0]; mp
< &message
[msgCount
]; mp
++) {
350 if (mp
->m_flag
& MNEW
) {
352 mp
->m_flag
|= MSTATUS
;
354 if (mp
->m_flag
& (MSTATUS
|MFLAG
|MUNFLAG
|MANSWER
|MUNANSWER
|
357 if ((mp
->m_flag
& MTOUCH
) == 0)
358 mp
->m_flag
|= MPRESERVE
;
359 if ((mp
->m_flag
& nohold
) == 0)
360 mp
->m_flag
|= holdbit
;
366 save_mbox_for_possible_quitstuff(void) /* TODO try to get rid of that */
370 if ((cp
= expand("&")) == NULL
)
372 n_strlcpy(_mboxname
, cp
, sizeof _mboxname
);
376 * Create another temporary file and copy user's mbox file
377 * darin. If there is no mbox, copy nothing.
378 * If he has specified "append" don't copy his mailbox,
379 * just copy saveable entries at the end.
385 char *mbox
, *tempQuit
;
387 FILE *ibuf
= NULL
, *obuf
, *abuf
;
392 if (value("append") == NULL
) {
393 if ((obuf
= Ftemp(&tempQuit
, "Rm", "w", 0600, 1)) == NULL
) {
394 perror(tr(163, "temporary mail quit file"));
397 if ((ibuf
= Fopen(tempQuit
, "r")) == NULL
) {
406 if ((abuf
= Zopen(mbox
, "r", NULL
)) != NULL
) {
407 while ((c
= getc(abuf
)) != EOF
)
412 perror(tr(163, "temporary mail quit file"));
419 if ((c
= open(mbox
, O_CREAT
|O_TRUNC
|O_WRONLY
, 0600)) >= 0)
421 if ((obuf
= Zopen(mbox
, "r+", NULL
)) == NULL
) {
428 if ((obuf
= Zopen(mbox
, "a", NULL
)) == NULL
) {
432 fchmod(fileno(obuf
), 0600);
436 prot
= which_protocol(mbox
);
437 for (mp
= &message
[0]; mp
< &message
[msgCount
]; mp
++) {
438 if (mp
->m_flag
& MBOX
) {
440 if (prot
== PROTO_IMAP
&&
441 saveignore
[0].i_count
== 0 &&
442 saveignore
[1].i_count
== 0
443 #ifdef HAVE_IMAP /* TODO revisit */
444 && imap_thisaccount(mbox
)
448 if (imap_copy(mp
, mp
-message
+1, mbox
) == STOP
)
451 } else if (sendmp(mp
, obuf
, saveignore
,
452 NULL
, SEND_MBOX
, NULL
) < 0) {
461 mp
->m_flag
|= MBOXED
;
468 * Copy the user's old mbox contents back
469 * to the end of the stuff we just saved.
470 * If we are appending, this is unnecessary.
473 if (value("append") == NULL
) {
491 if (Fclose(obuf
) != 0) {
492 if (prot
!= PROTO_IMAP
)
497 printf(tr(164, "Saved 1 message in mbox\n"));
499 printf(tr(165, "Saved %d messages in mbox\n"), mcount
);
504 * Terminate an editing session by attempting to write out the user's
505 * file from the temporary. Save any new stuff appended to the file.
512 FILE *obuf
, *ibuf
= NULL
;
518 for (mp
= &message
[0], gotcha
= 0; mp
< &message
[msgCount
]; mp
++) {
519 if (mp
->m_flag
& MNEW
) {
521 mp
->m_flag
|= MSTATUS
;
523 if (mp
->m_flag
& (MODIFY
|MDELETED
|MSTATUS
|MFLAG
|MUNFLAG
|
524 MANSWER
|MUNANSWER
|MDRAFT
|MUNDRAFT
))
530 if (stat(mailname
, &statb
) >= 0 && statb
.st_size
> mailsize
) {
533 if ((obuf
= Ftemp(&tempname
, "edstop", "w", 0600, 1)) == NULL
) {
534 perror(tr(167, "tmpfile"));
538 if ((ibuf
= Zopen(mailname
, "r", &mb
.mb_compressed
)) == NULL
) {
546 fseek(ibuf
, (long)mailsize
, SEEK_SET
);
547 while ((c
= getc(ibuf
)) != EOF
)
551 if ((ibuf
= Fopen(tempname
, "r")) == NULL
) {
561 printf(tr(168, "\"%s\" "), displayname
);
563 if ((obuf
= Zopen(mailname
, "r+", &mb
.mb_compressed
)) == NULL
) {
572 for (mp
= &message
[0]; mp
< &message
[msgCount
]; mp
++) {
573 if ((mp
->m_flag
& MDELETED
) != 0)
576 if (sendmp(mp
, obuf
, NULL
, NULL
, SEND_MBOX
, NULL
) < 0) {
580 reset(0);/* XXX jump aways are terrible */
586 gotcha
= (c
== 0 && ibuf
== NULL
);
588 while ((c
= getc(ibuf
)) != EOF
)
599 if (gotcha
&& value("emptybox") == NULL
) {
601 printf((value("bsdcompat") || value("bsdmsgs"))
602 ? tr(169, "removed\n") : tr(211, "removed.\n"));
604 printf((value("bsdcompat") || value("bsdmsgs"))
605 ? tr(170, "complete\n") : tr(212, "updated.\n"));
614 QUITFLAG_KEEPSAVE
= 002,
615 QUITFLAG_APPEND
= 004,
616 QUITFLAG_EMPTYBOX
= 010
619 static const struct quitnames
{
623 { QUITFLAG_HOLD
, "hold" },
624 { QUITFLAG_KEEPSAVE
, "keepsave" },
625 { QUITFLAG_APPEND
, "append" },
626 { QUITFLAG_EMPTYBOX
, "emptybox" },
633 enum quitflags qf
= 0;
636 for (i
= 0; quitnames
[i
].name
; i
++)
637 if (value(quitnames
[i
].name
))
638 qf
|= quitnames
[i
].flag
;
643 restorequitflags(int qf
)
647 for (i
= 0; quitnames
[i
].name
; i
++)
648 if (qf
& quitnames
[i
].flag
) {
649 if (value(quitnames
[i
].name
) == NULL
)
650 assign(quitnames
[i
].name
, "");
651 } else if (value(quitnames
[i
].name
))
652 unset_internal(quitnames
[i
].name
);