/** * mbox_mbox_open - Implements MxOps::mbox_open() */ static int mbox_mbox_open(struct Mailbox *m) { if (init_mailbox(m) != 0) return -1; struct MboxAccountData *adata = mbox_adata_get(m); if (!adata) return -1; adata->fp = fopen(m->path, "r+"); if (!adata->fp) { mutt_perror(m->path); return -1; } mutt_sig_block(); if (mbox_lock_mailbox(m, false, true) == -1) { mutt_sig_unblock(); return -1; } int rc; if (m->magic == MUTT_MBOX) rc = mbox_parse_mailbox(m); else if (m->magic == MUTT_MMDF) rc = mmdf_parse_mailbox(m); else rc = -1; mutt_file_touch_atime(fileno(adata->fp)); mbox_unlock_mailbox(m); mutt_sig_unblock(); return rc; }
/* open a mbox or mmdf style mailbox */ int mbox_open_mailbox(CONTEXT *ctx) { int rc; if ((ctx->fp = fopen(ctx->path, "r")) == NULL) { mutt_perror(ctx->path); return -1; } mutt_block_signals(); if (mbox_lock_mailbox(ctx, 0, 1) == -1) { mutt_unblock_signals(); return -1; } if (ctx->magic == M_MBOX) rc = mbox_parse_mailbox(ctx); else if (ctx->magic == M_MMDF) rc = mmdf_parse_mailbox(ctx); else rc = -1; mbox_unlock_mailbox(ctx); mutt_unblock_signals(); return rc; }
/** * mbox_mbox_check - Implements MxOps::mbox_check() * @param[in] m Mailbox * @param[out] index_hint Keep track of current index selection * @retval #MUTT_REOPENED Mailbox has been reopened * @retval #MUTT_NEW_MAIL New mail has arrived * @retval #MUTT_LOCKED Couldn't lock the file * @retval 0 No change * @retval -1 Error */ static int mbox_mbox_check(struct Mailbox *m, int *index_hint) { if (!m) return -1; struct MboxAccountData *adata = mbox_adata_get(m); if (!adata) return -1; if (!adata->fp) { if (mbox_mbox_open(m) < 0) return -1; mutt_mailbox_changed(m, MBN_INVALID); } struct stat st; bool unlock = false; bool modified = false; if (stat(m->path, &st) == 0) { if ((mutt_file_stat_timespec_compare(&st, MUTT_STAT_MTIME, &m->mtime) == 0) && (st.st_size == m->size)) { return 0; } if (st.st_size == m->size) { /* the file was touched, but it is still the same length, so just exit */ mutt_file_get_stat_timespec(&m->mtime, &st, MUTT_STAT_MTIME); return 0; } if (st.st_size > m->size) { /* lock the file if it isn't already */ if (!adata->locked) { mutt_sig_block(); if (mbox_lock_mailbox(m, false, false) == -1) { mutt_sig_unblock(); /* we couldn't lock the mailbox, but nothing serious happened: * probably the new mail arrived: no reason to wait till we can * parse it: we'll get it on the next pass */ return MUTT_LOCKED; } unlock = 1; } /* Check to make sure that the only change to the mailbox is that * message(s) were appended to this file. My heuristic is that we should * see the message separator at *exactly* what used to be the end of the * folder. */ char buf[1024]; if (fseeko(adata->fp, m->size, SEEK_SET) != 0) mutt_debug(LL_DEBUG1, "#1 fseek() failed\n"); if (fgets(buf, sizeof(buf), adata->fp)) { if (((m->magic == MUTT_MBOX) && mutt_str_startswith(buf, "From ", CASE_MATCH)) || ((m->magic == MUTT_MMDF) && (mutt_str_strcmp(buf, MMDF_SEP) == 0))) { if (fseeko(adata->fp, m->size, SEEK_SET) != 0) mutt_debug(LL_DEBUG1, "#2 fseek() failed\n"); int old_msg_count = m->msg_count; if (m->magic == MUTT_MBOX) mbox_parse_mailbox(m); else mmdf_parse_mailbox(m); if (m->msg_count > old_msg_count) mutt_mailbox_changed(m, MBN_INVALID); /* Only unlock the folder if it was locked inside of this routine. * It may have been locked elsewhere, like in * mutt_checkpoint_mailbox(). */ if (unlock) { mbox_unlock_mailbox(m); mutt_sig_unblock(); } return MUTT_NEW_MAIL; /* signal that new mail arrived */ } else modified = true; } else { mutt_debug(LL_DEBUG1, "fgets returned NULL\n"); modified = true; } } else modified = true; } if (modified) { if (reopen_mailbox(m, index_hint) != -1) { mutt_mailbox_changed(m, MBN_INVALID); if (unlock) { mbox_unlock_mailbox(m); mutt_sig_unblock(); } return MUTT_REOPENED; } } /* fatal error */ mbox_unlock_mailbox(m); mx_fastclose_mailbox(m); mutt_sig_unblock(); mutt_error(_("Mailbox was corrupted")); return -1; }
/** * reopen_mailbox - Close and reopen a mailbox * @param m Mailbox * @param index_hint Current email * @retval >0 Success, e.g. #MUTT_REOPENED, #MUTT_NEW_MAIL * @retval -1 Error */ static int reopen_mailbox(struct Mailbox *m, int *index_hint) { if (!m) return -1; struct MboxAccountData *adata = mbox_adata_get(m); if (!adata) return -1; bool (*cmp_headers)(const struct Email *, const struct Email *) = NULL; struct Email **old_hdrs = NULL; int old_msg_count; bool msg_mod = false; int rc = -1; /* silent operations */ m->quiet = true; if (!m->quiet) mutt_message(_("Reopening mailbox...")); /* our heuristics require the old mailbox to be unsorted */ if (C_Sort != SORT_ORDER) { short old_sort = C_Sort; C_Sort = SORT_ORDER; mutt_mailbox_changed(m, MBN_RESORT); C_Sort = old_sort; } old_hdrs = NULL; old_msg_count = 0; /* simulate a close */ mutt_mailbox_changed(m, MBN_CLOSED); mutt_hash_free(&m->id_hash); mutt_hash_free(&m->subj_hash); mutt_hash_free(&m->label_hash); FREE(&m->v2r); if (m->readonly) { for (int i = 0; i < m->msg_count; i++) mutt_email_free(&(m->emails[i])); /* nothing to do! */ FREE(&m->emails); } else { /* save the old headers */ old_msg_count = m->msg_count; old_hdrs = m->emails; m->emails = NULL; } m->email_max = 0; /* force allocation of new headers */ m->msg_count = 0; m->vcount = 0; m->msg_tagged = 0; m->msg_deleted = 0; m->msg_new = 0; m->msg_unread = 0; m->msg_flagged = 0; m->changed = false; m->id_hash = NULL; m->subj_hash = NULL; mutt_make_label_hash(m); switch (m->magic) { case MUTT_MBOX: case MUTT_MMDF: cmp_headers = mutt_email_cmp_strict; mutt_file_fclose(&adata->fp); adata->fp = mutt_file_fopen(m->path, "r"); if (!adata->fp) rc = -1; else if (m->magic == MUTT_MBOX) rc = mbox_parse_mailbox(m); else rc = mmdf_parse_mailbox(m); break; default: rc = -1; break; } if (rc == -1) { /* free the old headers */ for (int i = 0; i < old_msg_count; i++) mutt_email_free(&(old_hdrs[i])); FREE(&old_hdrs); m->quiet = false; return -1; } mutt_file_touch_atime(fileno(adata->fp)); /* now try to recover the old flags */ if (!m->readonly) { for (int i = 0; i < m->msg_count; i++) { bool found = false; /* some messages have been deleted, and new messages have been * appended at the end; the heuristic is that old messages have then * "advanced" towards the beginning of the folder, so we begin the * search at index "i" */ int j; for (j = i; j < old_msg_count; j++) { if (!old_hdrs[j]) continue; if (cmp_headers(m->emails[i], old_hdrs[j])) { found = true; break; } } if (!found) { for (j = 0; (j < i) && (j < old_msg_count); j++) { if (!old_hdrs[j]) continue; if (cmp_headers(m->emails[i], old_hdrs[j])) { found = true; break; } } } if (found) { /* this is best done here */ if (index_hint && (*index_hint == j)) *index_hint = i; if (old_hdrs[j]->changed) { /* Only update the flags if the old header was changed; * otherwise, the header may have been modified externally, * and we don't want to lose _those_ changes */ mutt_set_flag(m, m->emails[i], MUTT_FLAG, old_hdrs[j]->flagged); mutt_set_flag(m, m->emails[i], MUTT_REPLIED, old_hdrs[j]->replied); mutt_set_flag(m, m->emails[i], MUTT_OLD, old_hdrs[j]->old); mutt_set_flag(m, m->emails[i], MUTT_READ, old_hdrs[j]->read); } mutt_set_flag(m, m->emails[i], MUTT_DELETE, old_hdrs[j]->deleted); mutt_set_flag(m, m->emails[i], MUTT_PURGE, old_hdrs[j]->purge); mutt_set_flag(m, m->emails[i], MUTT_TAG, old_hdrs[j]->tagged); /* we don't need this header any more */ mutt_email_free(&(old_hdrs[j])); } } /* free the remaining old headers */ for (int j = 0; j < old_msg_count; j++) { if (old_hdrs[j]) { mutt_email_free(&(old_hdrs[j])); msg_mod = true; } } FREE(&old_hdrs); } m->quiet = false; return (m->changed || msg_mod) ? MUTT_REOPENED : MUTT_NEW_MAIL; }
/* check to see if the mailbox has changed on disk. * * return values: * M_REOPENED mailbox has been reopened * M_NEW_MAIL new mail has arrived! * M_LOCKED couldn't lock the file * 0 no change * -1 error */ int mbox_check_mailbox(CONTEXT *ctx, int *index_hint) { struct stat st; char buffer[LONG_STRING]; int unlock = 0; int modified = 0; if (stat(ctx->path, &st) == 0) { if ((st.st_mtime == ctx->mtime) && (st.st_size == ctx->size)) return 0; if (st.st_size == ctx->size) { /* the file was touched, but it is still the same length, so just exit */ ctx->mtime = st.st_mtime; return 0; } if (st.st_size > ctx->size) { /* lock the file if it isn't already */ if (!ctx->locked) { mutt_block_signals(); if (mbox_lock_mailbox(ctx, 0, 0) == -1) { mutt_unblock_signals(); /* we couldn't lock the mailbox, but nothing serious happened: * probably the new mail arrived: no reason to wait till we *can * parse it: we'll get it on the next pass */ return M_LOCKED; } unlock = 1; } /* * Check to make sure that the only change to the mailbox is that * message(s) were appended to this file. My heuristic is that we *should * see the message separator at *exactly* what used to be the end of *the * folder. */ if (fseeko(ctx->fp, ctx->size, SEEK_SET) != 0) dprint(1, "mbox_check_mailbox: fseek() failed\n"); if (fgets(buffer, sizeof(buffer), ctx->fp) != NULL) { if (((ctx->magic == M_MBOX) && (mutt_strncmp("From ", buffer, 5) == 0)) || ((ctx->magic == M_MMDF) && (mutt_strcmp(MMDF_SEP, buffer) == 0))) { if (fseeko(ctx->fp, ctx->size, SEEK_SET) != 0) dprint(1, "mbox_check_mailbox: fseek() failed\n"); if (ctx->magic == M_MBOX) mbox_parse_mailbox(ctx); else mmdf_parse_mailbox(ctx); /* Only unlock the folder if it was locked inside of this routine. * It may have been locked elsewhere, like in * mutt_checkpoint_mailbox(). */ if (unlock) { mbox_unlock_mailbox(ctx); mutt_unblock_signals(); } return M_NEW_MAIL; /* signal that new mail arrived */ } else modified = 1; } else { dprint(1, "mbox_check_mailbox: fgets returned NULL.\n"); modified = 1; } } else modified = 1; } if (modified) { if (mutt_reopen_mailbox(ctx, index_hint) != -1) { if (unlock) { mbox_unlock_mailbox(ctx); mutt_unblock_signals(); } return M_REOPENED; } } /* fatal error */ mbox_unlock_mailbox(ctx); mx_fastclose_mailbox(ctx); mutt_unblock_signals(); mutt_error _("Mailbox was corrupted!"); return -1; }