changeset: 6726:20499921a4e5 user: Kevin McCarthy <ke...@8t8.us> date: Sun Jul 17 19:31:09 2016 -0700 link: http://dev.mutt.org/hg/mutt/rev/20499921a4e5
Add the trash folder patch. This is based on the trash folder patch by Cedric Duval. Modifications to the original patch are: * Use a flag called M_PURGE instead of M_APPENDED. The same flag is then used in the following "purge" patch instead of adding a different flag. * Removed the counter in context. The existing context->deleted is all that's needed. * Removed the "auto unset M_PURGE" when M_DELETED is unset inside _mutt_set_flag(), although this is convenient, it easily leads to header->purge not being reset in a few situations. * Reset purge flag along with the deleted flag if $delete is answered no. * Set M_PURGE on an edited message. (edit_one_message()) * Preserve purge flag in mutt_reopen_mailbox() * Turn off OPTCONFIRMAPPEND when saving to the trash, rather than hardcoding it off in mutt_save_confirm(). That way, normal save to the folder will respect the option. changeset: 6727:280f9b195192 user: Kevin McCarthy <ke...@8t8.us> date: Sun Jul 17 19:31:13 2016 -0700 link: http://dev.mutt.org/hg/mutt/rev/280f9b195192 Add purge-message patch. This is based on the patch by Cedric Duval. Modifications are: * Use the exising M_PURGE flag from the trash folder patch, rather than adding a separate flag. * Undelete operations are already handled by the trash folder patch. changeset: 6728:f1d5a884ffed user: Kevin McCarthy <ke...@8t8.us> date: Sun Jul 17 19:31:16 2016 -0700 link: http://dev.mutt.org/hg/mutt/rev/f1d5a884ffed Add imap-fast-trash patch. This is based on the patch by Paul Miller. Modifications are: * Create a new flag, MUTT_TRASH for imap_make_msg_set(), rather than use MUTT_EXPIRED. * Change imap_make_msg_set(MUTT_TRASH) to only look at hdrs[n]->deleted && !hdrs[n]->purge, behaving like MUTT_TAG, rather than looking at the HEADER_DATA. * Reimplement imap_fast_trash() based on imap_copy_message(). It looks the old version was too, but it lacked handling of TRYCREATE and also queued the UID COPY but didn't exec it. (Presumably this happened in the subsequent sync). * Move the Context magic and mx_is_imap() checks outside of imap_fast_trash() diffs (568 lines): diff -r b2cb7a38c1ed -r f1d5a884ffed OPS --- a/OPS Sat Jul 16 14:04:29 2016 -0700 +++ b/OPS Sun Jul 17 19:31:16 2016 -0700 @@ -142,6 +142,7 @@ OP_PREV_LINE "scroll up one line" OP_PREV_PAGE "move to the previous page" OP_PRINT "print the current entry" +OP_PURGE_MESSAGE "really delete the current entry, bypassing the trash folder" OP_QUERY "query external program for addresses" OP_QUERY_APPEND "append new query results to current results" OP_QUIT "save changes to mailbox and quit" diff -r b2cb7a38c1ed -r f1d5a884ffed commands.c --- a/commands.c Sat Jul 16 14:04:29 2016 -0700 +++ b/commands.c Sun Jul 17 19:31:16 2016 -0700 @@ -717,6 +717,7 @@ if (delete) { mutt_set_flag (Context, h, MUTT_DELETE, 1); + mutt_set_flag (Context, h, MUTT_PURGE, 1); if (option (OPTDELETEUNTAG)) mutt_set_flag (Context, h, MUTT_TAG, 0); } diff -r b2cb7a38c1ed -r f1d5a884ffed curs_main.c --- a/curs_main.c Sat Jul 16 14:04:29 2016 -0700 +++ b/curs_main.c Sun Jul 17 19:31:16 2016 -0700 @@ -1950,6 +1950,7 @@ MAYBE_REDRAW (menu->redraw); break; + case OP_PURGE_MESSAGE: case OP_DELETE: CHECK_MSGCOUNT; @@ -1961,6 +1962,7 @@ if (tag) { mutt_tag_set_flag (MUTT_DELETE, 1); + mutt_tag_set_flag (MUTT_PURGE, (op == OP_PURGE_MESSAGE)); if (option (OPTDELETEUNTAG)) mutt_tag_set_flag (MUTT_TAG, 0); menu->redraw = REDRAW_INDEX; @@ -1968,6 +1970,7 @@ else { mutt_set_flag (Context, CURHDR, MUTT_DELETE, 1); + mutt_set_flag (Context, CURHDR, MUTT_PURGE, (op == OP_PURGE_MESSAGE)); if (option (OPTDELETEUNTAG)) mutt_set_flag (Context, CURHDR, MUTT_TAG, 0); if (option (OPTRESOLVE)) @@ -2273,11 +2276,13 @@ if (tag) { mutt_tag_set_flag (MUTT_DELETE, 0); + mutt_tag_set_flag (MUTT_PURGE, 0); menu->redraw = REDRAW_INDEX; } else { mutt_set_flag (Context, CURHDR, MUTT_DELETE, 0); + mutt_set_flag (Context, CURHDR, MUTT_PURGE, 0); if (option (OPTRESOLVE) && menu->current < Context->vcount - 1) { menu->current++; @@ -2300,7 +2305,9 @@ rc = mutt_thread_set_flag (CURHDR, MUTT_DELETE, 0, op == OP_UNDELETE_THREAD ? 0 : 1); - + if (rc != -1) + rc = mutt_thread_set_flag (CURHDR, MUTT_PURGE, 0, + op == OP_UNDELETE_THREAD ? 0 : 1); if (rc != -1) { if (option (OPTRESOLVE)) diff -r b2cb7a38c1ed -r f1d5a884ffed editmsg.c --- a/editmsg.c Sat Jul 16 14:04:29 2016 -0700 +++ b/editmsg.c Sun Jul 17 19:31:16 2016 -0700 @@ -200,6 +200,7 @@ if (rc == 0) { mutt_set_flag (Context, cur, MUTT_DELETE, 1); + mutt_set_flag (Context, cur, MUTT_PURGE, 1); mutt_set_flag (Context, cur, MUTT_READ, 1); if (option (OPTDELETEUNTAG)) diff -r b2cb7a38c1ed -r f1d5a884ffed flags.c --- a/flags.c Sat Jul 16 14:04:29 2016 -0700 +++ b/flags.c Sun Jul 17 19:31:16 2016 -0700 @@ -87,6 +87,20 @@ } break; + case MUTT_PURGE: + + if (!mutt_bit_isset(ctx->rights,MUTT_ACL_DELETE)) + return; + + if (bf) + { + if (!h->purge && !ctx->readonly) + h->purge = 1; + } + else if (h->purge) + h->purge = 0; + break; + case MUTT_NEW: if (!mutt_bit_isset(ctx->rights,MUTT_ACL_SEEN)) @@ -345,6 +359,13 @@ { case 'd': case 'D': + if (!bf) + { + if (h) + mutt_set_flag (Context, h, MUTT_PURGE, bf); + else + mutt_tag_set_flag (MUTT_PURGE, bf); + } flag = MUTT_DELETE; break; diff -r b2cb7a38c1ed -r f1d5a884ffed functions.h --- a/functions.h Sat Jul 16 14:04:29 2016 -0700 +++ b/functions.h Sun Jul 17 19:31:16 2016 -0700 @@ -121,6 +121,7 @@ { "toggle-write", OP_TOGGLE_WRITE, "%" }, { "next-thread", OP_MAIN_NEXT_THREAD, "\016" }, { "next-subthread", OP_MAIN_NEXT_SUBTHREAD, "\033n" }, + { "purge-message", OP_PURGE_MESSAGE, NULL }, { "query", OP_QUERY, "Q" }, { "quit", OP_QUIT, "q" }, { "reply", OP_REPLY, "r" }, @@ -223,6 +224,7 @@ { "print-message", OP_PRINT, "p" }, { "previous-thread", OP_MAIN_PREV_THREAD, "\020" }, { "previous-subthread",OP_MAIN_PREV_SUBTHREAD, "\033p" }, + { "purge-message", OP_PURGE_MESSAGE, NULL }, { "quit", OP_QUIT, "Q" }, { "exit", OP_EXIT, "q" }, { "reply", OP_REPLY, "r" }, diff -r b2cb7a38c1ed -r f1d5a884ffed globals.h --- a/globals.h Sat Jul 16 14:04:29 2016 -0700 +++ b/globals.h Sun Jul 17 19:31:16 2016 -0700 @@ -147,6 +147,7 @@ WHERE char *Status; WHERE char *Tempdir; WHERE char *Tochars; +WHERE char *TrashPath; WHERE char *TSStatusFormat; WHERE char *TSIconFormat; WHERE short TSSupported; diff -r b2cb7a38c1ed -r f1d5a884ffed imap/imap.c --- a/imap/imap.c Sat Jul 16 14:04:29 2016 -0700 +++ b/imap/imap.c Sun Jul 17 19:31:16 2016 -0700 @@ -924,6 +924,10 @@ if (hdrs[n]->tagged) match = 1; break; + case MUTT_TRASH: + if (hdrs[n]->deleted && !hdrs[n]->purge) + match = 1; + break; } if (match && (!changed || hdrs[n]->changed)) @@ -2073,6 +2077,101 @@ return -1; } +/* imap_fast_trash: use server COPY command to copy deleted + * messages to the trash folder. + * Return codes: + * -1: error + * 0: success + * 1: non-fatal error - try fetch/append */ +int imap_fast_trash (CONTEXT* ctx, char* dest) +{ + IMAP_DATA* idata; + char mbox[LONG_STRING]; + char mmbox[LONG_STRING]; + char prompt[LONG_STRING]; + int rc; + IMAP_MBOX mx; + int triedcreate = 0; + + idata = (IMAP_DATA*) ctx->data; + + if (imap_parse_path (dest, &mx)) + { + dprint (1, (debugfile, "imap_fast_trash: bad destination %s\n", dest)); + return -1; + } + + /* check that the save-to folder is in the same account */ + if (!mutt_account_match (&(CTX_DATA->conn->account), &(mx.account))) + { + dprint (3, (debugfile, "imap_fast_trash: %s not same server as %s\n", + dest, ctx->path)); + return 1; + } + + imap_fix_path (idata, mx.mbox, mbox, sizeof (mbox)); + if (!*mbox) + strfcpy (mbox, "INBOX", sizeof (mbox)); + imap_munge_mbox_name (idata, mmbox, sizeof (mmbox), mbox); + + /* loop in case of TRYCREATE */ + do + { + rc = imap_exec_msgset (idata, "UID COPY", mmbox, MUTT_TRASH, 0, 0); + if (!rc) + { + dprint (1, (debugfile, "imap_fast_trash: No messages to trash\n")); + rc = -1; + goto out; + } + else if (rc < 0) + { + dprint (1, (debugfile, "could not queue copy\n")); + goto out; + } + else + mutt_message (_("Copying %d messages to %s..."), rc, mbox); + + /* let's get it on */ + rc = imap_exec (idata, NULL, IMAP_CMD_FAIL_OK); + if (rc == -2) + { + if (triedcreate) + { + dprint (1, (debugfile, "Already tried to create mailbox %s\n", mbox)); + break; + } + /* bail out if command failed for reasons other than nonexistent target */ + if (ascii_strncasecmp (imap_get_qualifier (idata->buf), "[TRYCREATE]", 11)) + break; + dprint (3, (debugfile, "imap_fast_trash: server suggests TRYCREATE\n")); + snprintf (prompt, sizeof (prompt), _("Create %s?"), mbox); + if (option (OPTCONFIRMCREATE) && mutt_yesorno (prompt, 1) < 1) + { + mutt_clear_error (); + goto out; + } + if (imap_create_mailbox (idata, mbox) < 0) + break; + triedcreate = 1; + } + } + while (rc == -2); + + if (rc != 0) + { + imap_error ("imap_fast_trash", idata->buf); + goto out; + } + + rc = 0; + + out: + FREE (&mx.mbox); + + return rc < 0 ? -1 : rc; +} + struct mx_ops mx_imap_ops = { .open = imap_open_mailbox, .close = imap_close_mailbox, diff -r b2cb7a38c1ed -r f1d5a884ffed imap/imap.h --- a/imap/imap.h Sat Jul 16 14:04:29 2016 -0700 +++ b/imap/imap.h Sun Jul 17 19:31:16 2016 -0700 @@ -43,6 +43,7 @@ int imap_search (CONTEXT* ctx, const pattern_t* pat); int imap_subscribe (char *path, int subscribe); int imap_complete (char* dest, size_t dlen, char* path); +int imap_fast_trash (CONTEXT* ctx, char* dest); void imap_allow_reopen (CONTEXT *ctx); void imap_disallow_reopen (CONTEXT *ctx); diff -r b2cb7a38c1ed -r f1d5a884ffed imap/message.c --- a/imap/message.c Sat Jul 16 14:04:29 2016 -0700 +++ b/imap/message.c Sun Jul 17 19:31:16 2016 -0700 @@ -902,6 +902,7 @@ if (ctx->hdrs[n]->tagged) { mutt_set_flag (ctx, ctx->hdrs[n], MUTT_DELETE, 1); + mutt_set_flag (ctx, ctx->hdrs[n], MUTT_PURGE, 1); if (option (OPTDELETEUNTAG)) mutt_set_flag (ctx, ctx->hdrs[n], MUTT_TAG, 0); } @@ -909,6 +910,7 @@ else { mutt_set_flag (ctx, h, MUTT_DELETE, 1); + mutt_set_flag (ctx, h, MUTT_PURGE, 1); if (option (OPTDELETEUNTAG)) mutt_set_flag (ctx, h, MUTT_TAG, 0); } diff -r b2cb7a38c1ed -r f1d5a884ffed init.h --- a/init.h Sat Jul 16 14:04:29 2016 -0700 +++ b/init.h Sun Jul 17 19:31:16 2016 -0700 @@ -3553,6 +3553,16 @@ ** by \fIyou\fP. The sixth character is used to indicate when a mail ** was sent to a mailing-list you subscribe to. */ + { "trash", DT_PATH, R_NONE, UL &TrashPath, 0 }, + /* + ** .pp + ** If set, this variable specifies the path of the trash folder where the + ** mails marked for deletion will be moved, instead of being irremediably + ** purged. + ** .pp + ** NOTE: When you delete a message in the trash folder, it is really + ** deleted, so that you have a way to clean the trash. + */ {"ts_icon_format", DT_STR, R_BOTH, UL &TSIconFormat, UL "M%?n?AIL&ail?"}, /* ** .pp diff -r b2cb7a38c1ed -r f1d5a884ffed mbox.c --- a/mbox.c Sat Jul 16 14:04:29 2016 -0700 +++ b/mbox.c Sun Jul 17 19:31:16 2016 -0700 @@ -1265,6 +1265,7 @@ mutt_set_flag (ctx, ctx->hdrs[i], MUTT_READ, old_hdrs[j]->read); } mutt_set_flag (ctx, ctx->hdrs[i], MUTT_DELETE, old_hdrs[j]->deleted); + mutt_set_flag (ctx, ctx->hdrs[i], MUTT_PURGE, old_hdrs[j]->purge); mutt_set_flag (ctx, ctx->hdrs[i], MUTT_TAG, old_hdrs[j]->tagged); /* we don't need this header any more */ diff -r b2cb7a38c1ed -r f1d5a884ffed mutt.h --- a/mutt.h Sat Jul 16 14:04:29 2016 -0700 +++ b/mutt.h Sun Jul 17 19:31:16 2016 -0700 @@ -181,6 +181,7 @@ MUTT_UNREAD, MUTT_DELETE, MUTT_UNDELETE, + MUTT_PURGE, MUTT_DELETED, MUTT_FLAG, MUTT_TAG, @@ -188,6 +189,7 @@ MUTT_LIMIT, MUTT_EXPIRED, MUTT_SUPERSEDED, + MUTT_TRASH, /* actions for mutt_pattern_comp/mutt_pattern_exec */ MUTT_AND, @@ -728,6 +730,7 @@ unsigned int flagged : 1; /* marked important? */ unsigned int tagged : 1; unsigned int deleted : 1; + unsigned int purge : 1; /* skip trash folder when deleting */ unsigned int changed : 1; unsigned int attach_del : 1; /* has an attachment marked for deletion */ unsigned int old : 1; diff -r b2cb7a38c1ed -r f1d5a884ffed mx.c --- a/mx.c Sat Jul 16 14:04:29 2016 -0700 +++ b/mx.c Sun Jul 17 19:31:16 2016 -0700 @@ -797,6 +797,73 @@ return rc; } +/* move deleted mails to the trash folder */ +static int trash_append (CONTEXT *ctx) +{ + CONTEXT *ctx_trash; + int i; + struct stat st, stc; + int opt_confappend, rc; + + if (!TrashPath || !ctx->deleted || + (ctx->magic == MUTT_MAILDIR && option (OPTMAILDIRTRASH))) + return 0; + + for (i = 0; i < ctx->msgcount; i++) + if (ctx->hdrs[i]->deleted && (!ctx->hdrs[i]->purge)) + break; + if (i == ctx->msgcount) + return 0; /* nothing to be done */ + + /* avoid the "append messages" prompt */ + opt_confappend = option (OPTCONFIRMAPPEND); + if (opt_confappend) + unset_option (OPTCONFIRMAPPEND); + rc = mutt_save_confirm (TrashPath, &st); + if (opt_confappend) + set_option (OPTCONFIRMAPPEND); + if (rc != 0) + { + mutt_error _("message(s) not deleted"); + return -1; + } + + if (lstat (ctx->path, &stc) == 0 && stc.st_ino == st.st_ino + && stc.st_dev == st.st_dev && stc.st_rdev == st.st_rdev) + return 0; /* we are in the trash folder: simple sync */ + +#ifdef USE_IMAP + if (Context->magic == MUTT_IMAP && mx_is_imap (TrashPath)) + { + if (!imap_fast_trash (Context, TrashPath)) + return 0; + } +#endif + + if ((ctx_trash = mx_open_mailbox (TrashPath, MUTT_APPEND, NULL)) != NULL) + { + /* continue from initial scan above */ + for (; i < ctx->msgcount ; i++) + if (ctx->hdrs[i]->deleted && (!ctx->hdrs[i]->purge)) + { + if (mutt_append_message (ctx_trash, ctx, ctx->hdrs[i], 0, 0) == -1) + { + mx_close_mailbox (ctx_trash, NULL); + return -1; + } + } + + mx_close_mailbox (ctx_trash, NULL); + } + else + { + mutt_error _("Can't open trash folder"); + return -1; + } + + return 0; +} + /* save changes and close mailbox */ int mx_close_mailbox (CONTEXT *ctx, int *index_hint) { @@ -939,6 +1006,7 @@ if (mutt_append_message (&f, ctx, ctx->hdrs[i], 0, CH_UPDATE_LEN) == 0) { mutt_set_flag (ctx, ctx->hdrs[i], MUTT_DELETE, 1); + mutt_set_flag (ctx, ctx->hdrs[i], MUTT_PURGE, 1); } else { @@ -962,7 +1030,17 @@ mx_fastclose_mailbox (ctx); return 0; } - + + /* copy mails to the trash before expunging */ + if (purge && ctx->deleted && mutt_strcmp (ctx->path, TrashPath)) + { + if (trash_append (ctx) != 0) + { + ctx->closing = 0; + return -1; + } + } + #ifdef USE_IMAP /* allow IMAP to preserve the deleted flag across sessions */ if (ctx->magic == MUTT_IMAP) @@ -979,7 +1057,10 @@ if (!purge) { for (i = 0; i < ctx->msgcount; i++) + { ctx->hdrs[i]->deleted = 0; + ctx->hdrs[i]->purge = 0; + } ctx->deleted = 0; } @@ -1159,7 +1240,10 @@ if (ctx->magic != MUTT_IMAP) { for (i = 0 ; i < ctx->msgcount ; i++) + { ctx->hdrs[i]->deleted = 0; + ctx->hdrs[i]->purge = 0; + } ctx->deleted = 0; } } @@ -1172,6 +1256,12 @@ msgcount = ctx->msgcount; deleted = ctx->deleted; + if (purge && ctx->deleted && mutt_strcmp (ctx->path, TrashPath)) + { + if (trash_append (ctx) != 0) + return -1; + } + #ifdef USE_IMAP if (ctx->magic == MUTT_IMAP) rc = imap_sync_mailbox (ctx, purge, index_hint); diff -r b2cb7a38c1ed -r f1d5a884ffed pager.c --- a/pager.c Sat Jul 16 14:04:29 2016 -0700 +++ b/pager.c Sun Jul 17 19:31:16 2016 -0700 @@ -2406,6 +2406,7 @@ MAYBE_REDRAW (redraw); break; + case OP_PURGE_MESSAGE: case OP_DELETE: CHECK_MODE(IsHeader (extra)); CHECK_READONLY; @@ -2413,6 +2414,7 @@ CHECK_ACL(MUTT_ACL_DELETE, _("Cannot delete message")); mutt_set_flag (Context, extra->hdr, MUTT_DELETE, 1); + mutt_set_flag (Context, extra->hdr, MUTT_PURGE, (ch == OP_PURGE_MESSAGE)); if (option (OPTDELETEUNTAG)) mutt_set_flag (Context, extra->hdr, MUTT_TAG, 0); redraw = REDRAW_STATUS | REDRAW_INDEX; @@ -2743,6 +2745,7 @@ CHECK_ACL(MUTT_ACL_DELETE, _("Cannot undelete message")); mutt_set_flag (Context, extra->hdr, MUTT_DELETE, 0); + mutt_set_flag (Context, extra->hdr, MUTT_PURGE, 0); redraw = REDRAW_STATUS | REDRAW_INDEX; if (option (OPTRESOLVE)) { @@ -2760,7 +2763,9 @@ r = mutt_thread_set_flag (extra->hdr, MUTT_DELETE, 0, ch == OP_UNDELETE_THREAD ? 0 : 1); - + if (r != -1) + r = mutt_thread_set_flag (extra->hdr, MUTT_PURGE, 0, + ch == OP_UNDELETE_THREAD ? 0 : 1); if (r != -1) { if (option (OPTRESOLVE)) diff -r b2cb7a38c1ed -r f1d5a884ffed pattern.c --- a/pattern.c Sat Jul 16 14:04:29 2016 -0700 +++ b/pattern.c Sun Jul 17 19:31:16 2016 -0700 @@ -1367,8 +1367,10 @@ { switch (op) { + case MUTT_UNDELETE: + mutt_set_flag (Context, Context->hdrs[Context->v2r[i]], MUTT_PURGE, + 0); case MUTT_DELETE: - case MUTT_UNDELETE: mutt_set_flag (Context, Context->hdrs[Context->v2r[i]], MUTT_DELETE, (op == MUTT_DELETE)); break; diff -r b2cb7a38c1ed -r f1d5a884ffed postpone.c --- a/postpone.c Sat Jul 16 14:04:29 2016 -0700 +++ b/postpone.c Sun Jul 17 19:31:16 2016 -0700 @@ -178,6 +178,7 @@ { case OP_DELETE: case OP_UNDELETE: + /* should deleted draft messages be saved in the trash folder? */ mutt_set_flag (PostContext, PostContext->hdrs[menu->current], MUTT_DELETE, (i == OP_DELETE) ? 1 : 0); PostCount = PostContext->msgcount - PostContext->deleted; if (option (OPTRESOLVE) && menu->current < menu->max - 1) @@ -276,6 +277,7 @@ /* finished with this message, so delete it. */ mutt_set_flag (PostContext, h, MUTT_DELETE, 1); + mutt_set_flag (PostContext, h, MUTT_PURGE, 1); /* update the count for the status display */ PostCount = PostContext->msgcount - PostContext->deleted;