# neomutt
NEOMUTT= neomutt$(EXEEXT)
NEOMUTTOBJS= account.o addrbook.o alias.o bcache.o browser.o color.o commands.o \
- complete.o compose.o compress.o conststrings.o copy.o \
+ complete.o compose.o compress.o conststrings.o context.o copy.o \
curs_lib.o edit.o editmsg.o enriched.o enter.o \
filter.o flags.o git_ver.o handler.o hdrline.o help.o hook.o \
index.o init.o keymap.o mailbox.o main.o menu.o muttlib.o \
else
{
mx_fastclose_mailbox(Context->mailbox);
- mutt_context_free(&Context);
+ ctx_free(&Context);
}
/* go back to the folder we started from */
--- /dev/null
+/**
+ * @file
+ * The "currently-open" mailbox
+ *
+ * @authors
+ * Copyright (C) 2018 Richard Russon <rich@flatcap.org>
+ *
+ * @copyright
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @page ctx The "currently-open" mailbox
+ *
+ * The "currently-open" mailbox
+ */
+
+#include "config.h"
+#include <string.h>
+#include "mutt/mutt.h"
+#include "email/lib.h"
+#include "context.h"
+#include "globals.h"
+#include "mutt_header.h"
+#include "mutt_thread.h"
+#include "mx.h"
+#include "ncrypt/ncrypt.h"
+#include "pattern.h"
+#include "score.h"
+#include "sort.h"
+
+/**
+ * ctx_free - Free a Context
+ * @param ctx Context to free
+ */
+void ctx_free(struct Context **ctx)
+{
+ if (!ctx || !*ctx)
+ return;
+
+ mailbox_free(&(*ctx)->mailbox);
+ FREE(ctx);
+}
+
+/**
+ * ctx_cleanup - Release memory and initialize a Context object
+ * @param ctx Context to cleanup
+ */
+void ctx_cleanup(struct Context *ctx)
+{
+ FREE(&ctx->pattern);
+ mutt_pattern_free(&ctx->limit_pattern);
+ memset(ctx, 0, sizeof(struct Context));
+}
+
+/**
+ * ctx_update - Update the Context's message counts
+ * @param ctx Mailbox
+ *
+ * this routine is called to update the counts in the context structure
+ */
+void ctx_update(struct Context *ctx)
+{
+ if (!ctx || !ctx->mailbox)
+ return;
+
+ struct Mailbox *m = ctx->mailbox;
+
+ mutt_hash_free(&m->subj_hash);
+ mutt_hash_free(&m->id_hash);
+
+ /* reset counters */
+ m->msg_unread = 0;
+ m->msg_flagged = 0;
+ m->msg_new = 0;
+ m->msg_deleted = 0;
+ m->msg_tagged = 0;
+ m->vcount = 0;
+ m->changed = false;
+
+ mutt_clear_threads(ctx);
+
+ struct Email *e = NULL;
+ for (int msgno = 0; msgno < m->msg_count; msgno++)
+ {
+ e = m->emails[msgno];
+
+ if (WithCrypto)
+ {
+ /* NOTE: this _must_ be done before the check for mailcap! */
+ e->security = crypt_query(e->content);
+ }
+
+ if (!ctx->pattern)
+ {
+ m->v2r[m->vcount] = msgno;
+ e->virtual = m->vcount++;
+ }
+ else
+ e->virtual = -1;
+ e->msgno = msgno;
+
+ if (e->env->supersedes)
+ {
+ struct Email *e2 = NULL;
+
+ if (!m->id_hash)
+ m->id_hash = mutt_make_id_hash(m);
+
+ e2 = mutt_hash_find(m->id_hash, e->env->supersedes);
+ if (e2)
+ {
+ e2->superseded = true;
+ if (Score)
+ mutt_score_message(ctx->mailbox, e2, true);
+ }
+ }
+
+ /* add this message to the hash tables */
+ if (m->id_hash && e->env->message_id)
+ mutt_hash_insert(m->id_hash, e->env->message_id, e);
+ if (m->subj_hash && e->env->real_subj)
+ mutt_hash_insert(m->subj_hash, e->env->real_subj, e);
+ mutt_label_hash_add(m, e);
+
+ if (Score)
+ mutt_score_message(ctx->mailbox, e, false);
+
+ if (e->changed)
+ m->changed = true;
+ if (e->flagged)
+ m->msg_flagged++;
+ if (e->deleted)
+ m->msg_deleted++;
+ if (!e->read)
+ {
+ m->msg_unread++;
+ if (!e->old)
+ m->msg_new++;
+ }
+ }
+
+ mutt_sort_headers(ctx, true); /* rethread from scratch */
+}
+
+/**
+ * ctx_update_tables - Update a Context structure's internal tables
+ * @param ctx Mailbox
+ * @param committing Commit the changes?
+ */
+void ctx_update_tables(struct Context *ctx, bool committing)
+{
+ if (!ctx || !ctx->mailbox)
+ return;
+
+ struct Mailbox *m = ctx->mailbox;
+
+ int i, j, padding;
+
+ /* update memory to reflect the new state of the mailbox */
+ m->vcount = 0;
+ ctx->vsize = 0;
+ m->msg_tagged = 0;
+ m->msg_deleted = 0;
+ m->msg_new = 0;
+ m->msg_unread = 0;
+ m->changed = false;
+ m->msg_flagged = 0;
+ padding = mx_msg_padding_size(m);
+ for (i = 0, j = 0; i < m->msg_count; i++)
+ {
+ if (!m->emails[i]->quasi_deleted &&
+ ((committing && (!m->emails[i]->deleted || (m->magic == MUTT_MAILDIR && MaildirTrash))) ||
+ (!committing && m->emails[i]->active)))
+ {
+ if (i != j)
+ {
+ m->emails[j] = m->emails[i];
+ m->emails[i] = NULL;
+ }
+ m->emails[j]->msgno = j;
+ if (m->emails[j]->virtual != -1)
+ {
+ m->v2r[m->vcount] = j;
+ m->emails[j]->virtual = m->vcount++;
+ struct Body *b = m->emails[j]->content;
+ ctx->vsize += b->length + b->offset - b->hdr_offset + padding;
+ }
+
+ if (committing)
+ m->emails[j]->changed = false;
+ else if (m->emails[j]->changed)
+ m->changed = true;
+
+ if (!committing || (m->magic == MUTT_MAILDIR && MaildirTrash))
+ {
+ if (m->emails[j]->deleted)
+ m->msg_deleted++;
+ }
+
+ if (m->emails[j]->tagged)
+ m->msg_tagged++;
+ if (m->emails[j]->flagged)
+ m->msg_flagged++;
+ if (!m->emails[j]->read)
+ {
+ m->msg_unread++;
+ if (!m->emails[j]->old)
+ m->msg_new++;
+ }
+
+ j++;
+ }
+ else
+ {
+ if (m->magic == MUTT_MH || m->magic == MUTT_MAILDIR)
+ {
+ m->size -= (m->emails[i]->content->length + m->emails[i]->content->offset -
+ m->emails[i]->content->hdr_offset);
+ }
+ /* remove message from the hash tables */
+ if (m->subj_hash && m->emails[i]->env->real_subj)
+ mutt_hash_delete(m->subj_hash, m->emails[i]->env->real_subj, m->emails[i]);
+ if (m->id_hash && m->emails[i]->env->message_id)
+ mutt_hash_delete(m->id_hash, m->emails[i]->env->message_id, m->emails[i]);
+ mutt_label_hash_remove(m, m->emails[i]);
+ /* The path mx_mbox_check() -> imap_check_mailbox() ->
+ * imap_expunge_mailbox() -> ctx_update_tables()
+ * can occur before a call to mx_mbox_sync(), resulting in
+ * last_tag being stale if it's not reset here.
+ */
+ if (ctx->last_tag == m->emails[i])
+ ctx->last_tag = NULL;
+ mutt_email_free(&m->emails[i]);
+ }
+ }
+ m->msg_count = j;
+}
+
+/**
+ * ctx_mailbox_changed - Act on a Mailbox change notification
+ * @param m Mailbox
+ * @param action Event occurring
+ * @param ndata Private notification data
+ */
+void ctx_mailbox_changed(struct Mailbox *m, enum MailboxNotification action)
+{
+ if (!m || !m->ndata)
+ return;
+
+ struct Context *ctx = m->ndata;
+
+ switch (action)
+ {
+ case MBN_CLOSED:
+ mutt_clear_threads(ctx);
+ ctx_cleanup(ctx);
+ break;
+ case MBN_INVALID:
+ ctx_update(ctx);
+ break;
+ case MBN_RESORT:
+ ctx_update_tables(ctx, false);
+ mutt_sort_headers(ctx, true);
+ break;
+ }
+}
+
#include <stdio.h>
#include <sys/types.h>
#include <time.h>
-
-struct Mailbox;
+#include "mailbox.h"
/**
* struct Context - The "current" mailbox
struct Mailbox *mailbox;
};
+void ctx_free(struct Context **ctx);
+void ctx_mailbox_changed(struct Mailbox *m, enum MailboxNotification action);
+void ctx_update(struct Context *ctx);
+void ctx_update_tables(struct Context *ctx, bool committing);
+
#endif /* MUTT_CONTEXT_H */
* flag becomes set (e.g. a flag update to a modified header),
* this function will be called by imap_cmd_finish().
*
- * The mx_update_tables() will free and remove these "inactive" headers,
+ * The ctx_update_tables() will free and remove these "inactive" headers,
* despite that an EXPUNGE was not received for them.
* This would result in memory leaks and segfaults due to dangling
* pointers in the msn_index and uid_hash.
imap_expunge_mailbox(m);
/* undo expunge count updates.
- * mx_update_context() will do this at the end of the header fetch. */
+ * ctx_update() will do this at the end of the header fetch. */
m->vcount = 0;
m->msg_tagged = 0;
m->msg_deleted = 0;
/* keepalive failure in mutt_enter_fname may kill connection. #3028 */
if (Context && (Context->mailbox->path[0] == '\0'))
- mutt_context_free(&Context);
+ ctx_free(&Context);
if (Context)
{
if (!Context->mailbox || Context->mailbox->path[0] == '\0')
{
/* fatal error occurred */
- mutt_context_free(&Context);
+ ctx_free(&Context);
menu->redraw = REDRAW_FULL;
}
/* check for a fatal error, or all messages deleted */
if (Context->mailbox->path[0] == '\0')
- mutt_context_free(&Context);
+ ctx_free(&Context);
/* if we were in the pager, redisplay the message */
if (menu->menu == MENU_PAGER)
if (Context)
{
mx_fastclose_mailbox(Context->mailbox);
- mutt_context_free(&Context);
+ ctx_free(&Context);
}
done = true;
}
mutt_mailbox_changed(*m, MBN_CLOSED);
- if (Context && Context->mailbox && Context->mailbox == *m)
- {
- mx_cleanup_context(Context);
- FREE(&Context);
- }
-
FREE(&(*m)->desc);
if ((*m)->mdata && (*m)->free_mdata)
(*m)->free_mdata(&(*m)->mdata);
*s = '\0';
}
-/**
- * mutt_context_free - Free a Context
- * @param ctx Context to free
- */
-void mutt_context_free(struct Context **ctx)
-{
- if (!ctx || !*ctx)
- return;
-
- mailbox_free(&(*ctx)->mailbox);
- FREE(ctx);
-}
-
/**
* mutt_mailbox_changed - Notify listeners of a change to a Mailbox
* @param m Mailbox
#include "where.h"
struct Buffer;
-struct Context;
struct Account;
struct stat;
struct Mailbox *mailbox_new(void);
void mailbox_free(struct Mailbox **m);
-void mutt_context_free(struct Context **ctx);
struct Mailbox *mutt_find_mailbox(const char *path);
struct Mailbox *mutt_find_mailbox_desc(const char *desc);
m->emails[i]->index = j++;
}
- mx_update_tables(ctx, false);
+ ctx_update_tables(ctx, false);
mutt_clear_threads(ctx);
}
mutt_sb_set_open_mailbox();
#endif
mutt_index_menu();
- mutt_context_free(&Context);
+ ctx_free(&Context);
}
#ifdef USE_IMAP
imap_logout_all();
return rc;
}
-/**
- * mx_mailbox_changed - Act on a Mailbox change notification
- * @param m Mailbox
- * @param action Event occurring
- * @param ndata Private notification data
- */
-void mx_mailbox_changed(struct Mailbox *m, enum MailboxNotification action)
-{
- if (!m || !m->ndata)
- return;
-
- struct Context *ctx = m->ndata;
-
- switch (action)
- {
- case MBN_CLOSED:
- mutt_clear_threads(ctx);
- mx_cleanup_context(ctx);
- break;
- case MBN_INVALID:
- mx_update_context(ctx);
- break;
- case MBN_RESORT:
- mx_update_tables(ctx, false);
- mutt_sort_headers(ctx, true);
- break;
- }
-}
-
/**
* mx_mbox_open - Open a mailbox and parse it
* @param m Mailbox to open
/* int rc = */ mx_path_canon2(m, Folder);
}
- m->notify = mx_mailbox_changed;
+ m->notify = ctx_mailbox_changed;
m->ndata = ctx;
if ((m->magic == MUTT_UNKNOWN) && (flags & MUTT_NEWFOLDER))
if (mx_open_mailbox_append(ctx->mailbox, flags) != 0)
{
mx_fastclose_mailbox(m);
- mutt_context_free(&ctx);
+ ctx_free(&ctx);
return NULL;
}
return ctx;
mutt_error(_("%s is not a mailbox"), path);
mx_fastclose_mailbox(m);
- mutt_context_free(&ctx);
+ ctx_free(&ctx);
return NULL;
}
int rc = m->mx_ops->mbox_open(ctx->mailbox);
m->opened++;
if (rc == 0)
- mx_update_context(ctx);
+ ctx_update(ctx);
if ((rc == 0) || (rc == -2))
{
else
{
mx_fastclose_mailbox(m);
- mutt_context_free(&ctx);
+ ctx_free(&ctx);
}
OptForceRefresh = false;
return 0;
}
-/**
- * mx_update_tables - Update a Context structure's internal tables
- * @param ctx Mailbox
- * @param committing Commit the changes?
- */
-void mx_update_tables(struct Context *ctx, bool committing)
-{
- if (!ctx || !ctx->mailbox)
- return;
-
- struct Mailbox *m = ctx->mailbox;
-
- int i, j, padding;
-
- /* update memory to reflect the new state of the mailbox */
- m->vcount = 0;
- ctx->vsize = 0;
- m->msg_tagged = 0;
- m->msg_deleted = 0;
- m->msg_new = 0;
- m->msg_unread = 0;
- m->changed = false;
- m->msg_flagged = 0;
- padding = mx_msg_padding_size(m);
- for (i = 0, j = 0; i < m->msg_count; i++)
- {
- if (!m->emails[i]->quasi_deleted &&
- ((committing && (!m->emails[i]->deleted || (m->magic == MUTT_MAILDIR && MaildirTrash))) ||
- (!committing && m->emails[i]->active)))
- {
- if (i != j)
- {
- m->emails[j] = m->emails[i];
- m->emails[i] = NULL;
- }
- m->emails[j]->msgno = j;
- if (m->emails[j]->virtual != -1)
- {
- m->v2r[m->vcount] = j;
- m->emails[j]->virtual = m->vcount++;
- struct Body *b = m->emails[j]->content;
- ctx->vsize += b->length + b->offset - b->hdr_offset + padding;
- }
-
- if (committing)
- m->emails[j]->changed = false;
- else if (m->emails[j]->changed)
- m->changed = true;
-
- if (!committing || (m->magic == MUTT_MAILDIR && MaildirTrash))
- {
- if (m->emails[j]->deleted)
- m->msg_deleted++;
- }
-
- if (m->emails[j]->tagged)
- m->msg_tagged++;
- if (m->emails[j]->flagged)
- m->msg_flagged++;
- if (!m->emails[j]->read)
- {
- m->msg_unread++;
- if (!m->emails[j]->old)
- m->msg_new++;
- }
-
- j++;
- }
- else
- {
- if (m->magic == MUTT_MH || m->magic == MUTT_MAILDIR)
- {
- m->size -= (m->emails[i]->content->length + m->emails[i]->content->offset -
- m->emails[i]->content->hdr_offset);
- }
- /* remove message from the hash tables */
- if (m->subj_hash && m->emails[i]->env->real_subj)
- mutt_hash_delete(m->subj_hash, m->emails[i]->env->real_subj, m->emails[i]);
- if (m->id_hash && m->emails[i]->env->message_id)
- mutt_hash_delete(m->id_hash, m->emails[i]->env->message_id, m->emails[i]);
- mutt_label_hash_remove(m, m->emails[i]);
- /* The path mx_mbox_check() -> imap_check_mailbox() ->
- * imap_expunge_mailbox() -> mx_update_tables()
- * can occur before a call to mx_mbox_sync(), resulting in
- * last_tag being stale if it's not reset here.
- */
- if (ctx->last_tag == m->emails[i])
- ctx->last_tag = NULL;
- mutt_email_free(&m->emails[i]);
- }
- }
- m->msg_count = j;
-}
-
/**
* mx_mbox_sync - Save changes to mailbox
* @param[in] ctx Context
}
/* really only for IMAP - imap_sync_mailbox results in a call to
- * mx_update_tables, so m->msg_deleted is 0 when it comes back */
+ * ctx_update_tables, so m->msg_deleted is 0 when it comes back */
msgcount = m->msg_count;
deleted = m->msg_deleted;
/* IMAP does this automatically after handling EXPUNGE */
if (m->magic != MUTT_IMAP)
{
- mx_update_tables(ctx, true);
+ ctx_update_tables(ctx, true);
mutt_sort_headers(ctx, true); /* rethread from scratch */
}
}
}
}
-/**
- * mx_update_context - Update the Context's message counts
- * @param ctx Mailbox
- *
- * this routine is called to update the counts in the context structure
- */
-void mx_update_context(struct Context *ctx)
-{
- if (!ctx || !ctx->mailbox)
- return;
-
- struct Mailbox *m = ctx->mailbox;
-
- mutt_hash_free(&m->subj_hash);
- mutt_hash_free(&m->id_hash);
-
- /* reset counters */
- m->msg_unread = 0;
- m->msg_flagged = 0;
- m->msg_new = 0;
- m->msg_deleted = 0;
- m->msg_tagged = 0;
- m->vcount = 0;
- m->changed = false;
-
- mutt_clear_threads(ctx);
-
- struct Email *e = NULL;
- for (int msgno = 0; msgno < m->msg_count; msgno++)
- {
- e = m->emails[msgno];
-
- if (WithCrypto)
- {
- /* NOTE: this _must_ be done before the check for mailcap! */
- e->security = crypt_query(e->content);
- }
-
- if (!ctx->pattern)
- {
- m->v2r[m->vcount] = msgno;
- e->virtual = m->vcount++;
- }
- else
- e->virtual = -1;
- e->msgno = msgno;
-
- if (e->env->supersedes)
- {
- struct Email *e2 = NULL;
-
- if (!m->id_hash)
- m->id_hash = mutt_make_id_hash(m);
-
- e2 = mutt_hash_find(m->id_hash, e->env->supersedes);
- if (e2)
- {
- e2->superseded = true;
- if (Score)
- mutt_score_message(ctx->mailbox, e2, true);
- }
- }
-
- /* add this message to the hash tables */
- if (m->id_hash && e->env->message_id)
- mutt_hash_insert(m->id_hash, e->env->message_id, e);
- if (m->subj_hash && e->env->real_subj)
- mutt_hash_insert(m->subj_hash, e->env->real_subj, e);
- mutt_label_hash_add(m, e);
-
- if (Score)
- mutt_score_message(ctx->mailbox, e, false);
-
- if (e->changed)
- m->changed = true;
- if (e->flagged)
- m->msg_flagged++;
- if (e->deleted)
- m->msg_deleted++;
- if (!e->read)
- {
- m->msg_unread++;
- if (!e->old)
- m->msg_new++;
- }
- }
-
- mutt_sort_headers(ctx, true); /* rethread from scratch */
-}
-
/**
* mx_check_empty - Is the mailbox empty
* @param path Mailbox to check
return 0;
}
-/**
- * mx_cleanup_context - Release memory and initialize a Context object
- * @param ctx Context to cleanup
- */
-void mx_cleanup_context(struct Context *ctx)
-{
- FREE(&ctx->pattern);
- mutt_pattern_free(&ctx->limit_pattern);
- memset(ctx, 0, sizeof(struct Context));
-}
-
/**
* mx_mbox_check_stats - Check the statistics for a mailbox - Wrapper for MxOps::mbox_check_stats
*/
void mx_fastclose_mailbox(struct Mailbox *m);
const struct MxOps *mx_get_ops (enum MailboxType magic);
bool mx_tags_is_supported(struct Mailbox *m);
-void mx_update_context (struct Context *ctx);
-void mx_update_tables (struct Context *ctx, bool committing);
-void mx_cleanup_context (struct Context *ctx);
#endif /* MUTT_MX_H */
{
if ((anum >= mdata->newsrc_ent[i].first) && (anum <= mdata->newsrc_ent[i].last))
{
- /* can't use mutt_set_flag() because mx_update_context()
+ /* can't use mutt_set_flag() because ctx_update()
didn't get called yet */
e->read = true;
return;
e->changed = true;
e->received = e->date_sent;
e->index = m->msg_count++;
- mx_update_context(ctx);
+ ctx_update(ctx);
return 0;
}
break;
}
if (m->msg_count > old_msg_count)
- mx_update_context(ctx);
+ ctx_update(ctx);
#ifdef USE_HCACHE
mutt_hcache_close(hc);
nntp_edata_get(e)->parsed = true;
mutt_parse_mime_message(m, e);
- /* these would normally be updated in mx_update_context(), but the
+ /* these would normally be updated in ctx_update(), but the
* full headers aren't parsed with overview, so the information wasn't
* available then */
if (WithCrypto)
rc = 0;
if (m->msg_count > mdata->oldmsgcount)
- mx_update_context(ctx);
+ ctx_update(ctx);
done:
if (q)
notmuch_query_destroy(q);
if (!Context->mailbox || Context->mailbox->path[0] == '\0')
{
/* fatal error occurred */
- mutt_context_free(&Context);
+ ctx_free(&Context);
pager_menu->redraw = REDRAW_FULL;
break;
}
else
PostCount = ctx->mailbox->msg_count;
mx_fastclose_mailbox(ctx->mailbox);
- mutt_context_free(&ctx);
+ ctx_free(&ctx);
#ifdef USE_NNTP
if (optnews)
OptNews = true;