/** * 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; }
/** * mbox_mbox_sync - Implements MxOps::mbox_sync() */ static int mbox_mbox_sync(struct Mailbox *m, int *index_hint) { if (!m) return -1; struct MboxAccountData *adata = mbox_adata_get(m); if (!adata) return -1; char tempfile[PATH_MAX]; char buf[32]; int i, j; enum SortType save_sort = SORT_ORDER; int rc = -1; int need_sort = 0; /* flag to resort mailbox if new mail arrives */ int first = -1; /* first message to be written */ LOFF_T offset; /* location in mailbox to write changed messages */ struct stat statbuf; struct MUpdate *new_offset = NULL; struct MUpdate *old_offset = NULL; FILE *fp = NULL; struct Progress progress; char msgbuf[PATH_MAX + 64]; /* sort message by their position in the mailbox on disk */ if (C_Sort != SORT_ORDER) { save_sort = C_Sort; C_Sort = SORT_ORDER; mutt_mailbox_changed(m, MBN_RESORT); C_Sort = save_sort; need_sort = 1; } /* need to open the file for writing in such a way that it does not truncate * the file, so use read-write mode. */ adata->fp = freopen(m->path, "r+", adata->fp); if (!adata->fp) { mx_fastclose_mailbox(m); mutt_error(_("Fatal error! Could not reopen mailbox!")); return -1; } mutt_sig_block(); if (mbox_lock_mailbox(m, true, true) == -1) { mutt_sig_unblock(); mutt_error(_("Unable to lock mailbox")); goto bail; } /* Check to make sure that the file hasn't changed on disk */ i = mbox_mbox_check(m, index_hint); if ((i == MUTT_NEW_MAIL) || (i == MUTT_REOPENED)) { /* new mail arrived, or mailbox reopened */ rc = i; goto bail; } else if (i < 0) { /* fatal error */ return -1; } /* Create a temporary file to write the new version of the mailbox in. */ mutt_mktemp(tempfile, sizeof(tempfile)); int fd = open(tempfile, O_WRONLY | O_EXCL | O_CREAT, 0600); if ((fd == -1) || !(fp = fdopen(fd, "w"))) { if (fd != -1) { close(fd); unlink(tempfile); } mutt_error(_("Could not create temporary file")); goto bail; } /* find the first deleted/changed message. we save a lot of time by only * rewriting the mailbox from the point where it has actually changed. */ for (i = 0; (i < m->msg_count) && !m->emails[i]->deleted && !m->emails[i]->changed && !m->emails[i]->attach_del; i++) { } if (i == m->msg_count) { /* this means ctx->changed or m->msg_deleted was set, but no * messages were found to be changed or deleted. This should * never happen, is we presume it is a bug in neomutt. */ mutt_error( _("sync: mbox modified, but no modified messages (report this bug)")); mutt_debug(LL_DEBUG1, "no modified messages\n"); unlink(tempfile); goto bail; } /* save the index of the first changed/deleted message */ first = i; /* where to start overwriting */ offset = m->emails[i]->offset; /* the offset stored in the header does not include the MMDF_SEP, so make * sure we seek to the correct location */ if (m->magic == MUTT_MMDF) offset -= (sizeof(MMDF_SEP) - 1); /* allocate space for the new offsets */ new_offset = mutt_mem_calloc(m->msg_count - first, sizeof(struct MUpdate)); old_offset = mutt_mem_calloc(m->msg_count - first, sizeof(struct MUpdate)); if (!m->quiet) { snprintf(msgbuf, sizeof(msgbuf), _("Writing %s..."), m->path); mutt_progress_init(&progress, msgbuf, MUTT_PROGRESS_MSG, C_WriteInc, m->msg_count); } for (i = first, j = 0; i < m->msg_count; i++) { if (!m->quiet) mutt_progress_update(&progress, i, (int) (ftello(adata->fp) / (m->size / 100 + 1))); /* back up some information which is needed to restore offsets when * something fails. */ old_offset[i - first].valid = true; old_offset[i - first].hdr = m->emails[i]->offset; old_offset[i - first].body = m->emails[i]->content->offset; old_offset[i - first].lines = m->emails[i]->lines; old_offset[i - first].length = m->emails[i]->content->length; if (!m->emails[i]->deleted) { j++; if (m->magic == MUTT_MMDF) { if (fputs(MMDF_SEP, fp) == EOF) { mutt_perror(tempfile); unlink(tempfile); goto bail; } } /* save the new offset for this message. we add 'offset' because the * temporary file only contains saved message which are located after * 'offset' in the real mailbox */ new_offset[i - first].hdr = ftello(fp) + offset; if (mutt_copy_message_ctx(fp, m, m->emails[i], MUTT_CM_UPDATE, CH_FROM | CH_UPDATE | CH_UPDATE_LEN) != 0) { mutt_perror(tempfile); unlink(tempfile); goto bail; } /* Since messages could have been deleted, the offsets stored in memory * will be wrong, so update what we can, which is the offset of this * message, and the offset of the body. If this is a multipart message, * we just flush the in memory cache so that the message will be reparsed * if the user accesses it later. */ new_offset[i - first].body = ftello(fp) - m->emails[i]->content->length + offset; mutt_body_free(&m->emails[i]->content->parts); switch (m->magic) { case MUTT_MMDF: if (fputs(MMDF_SEP, fp) == EOF) { mutt_perror(tempfile); unlink(tempfile); goto bail; } break; default: if (fputs("\n", fp) == EOF) { mutt_perror(tempfile); unlink(tempfile); goto bail; } } } } if (fclose(fp) != 0) { fp = NULL; mutt_debug(LL_DEBUG1, "mutt_file_fclose (&) returned non-zero\n"); unlink(tempfile); mutt_perror(tempfile); goto bail; } fp = NULL; /* Save the state of this folder. */ if (stat(m->path, &statbuf) == -1) { mutt_perror(m->path); unlink(tempfile); goto bail; } fp = fopen(tempfile, "r"); if (!fp) { mutt_sig_unblock(); mx_fastclose_mailbox(m); mutt_debug(LL_DEBUG1, "unable to reopen temp copy of mailbox!\n"); mutt_perror(tempfile); FREE(&new_offset); FREE(&old_offset); return -1; } if ((fseeko(adata->fp, offset, SEEK_SET) != 0) || /* seek the append location */ /* do a sanity check to make sure the mailbox looks ok */ !fgets(buf, sizeof(buf), adata->fp) || ((m->magic == MUTT_MBOX) && !mutt_str_startswith(buf, "From ", CASE_MATCH)) || ((m->magic == MUTT_MMDF) && (mutt_str_strcmp(MMDF_SEP, buf) != 0))) { mutt_debug(LL_DEBUG1, "message not in expected position\n"); mutt_debug(LL_DEBUG1, "\tLINE: %s\n", buf); i = -1; } else { if (fseeko(adata->fp, offset, SEEK_SET) != 0) /* return to proper offset */ { i = -1; mutt_debug(LL_DEBUG1, "fseek() failed\n"); } else { /* copy the temp mailbox back into place starting at the first * change/deleted message */ if (!m->quiet) mutt_message(_("Committing changes...")); i = mutt_file_copy_stream(fp, adata->fp); if (ferror(adata->fp)) i = -1; } if (i == 0) { m->size = ftello(adata->fp); /* update the mailbox->size of the mailbox */ if ((m->size < 0) || (ftruncate(fileno(adata->fp), m->size) != 0)) { i = -1; mutt_debug(LL_DEBUG1, "ftruncate() failed\n"); } } } mutt_file_fclose(&fp); fp = NULL; mbox_unlock_mailbox(m); if ((mutt_file_fclose(&adata->fp) != 0) || (i == -1)) { /* error occurred while writing the mailbox back, so keep the temp copy around */ char savefile[PATH_MAX]; snprintf(savefile, sizeof(savefile), "%s/neomutt.%s-%s-%u", NONULL(C_Tmpdir), NONULL(Username), NONULL(ShortHostname), (unsigned int) getpid()); rename(tempfile, savefile); mutt_sig_unblock(); mx_fastclose_mailbox(m); mutt_pretty_mailbox(savefile, sizeof(savefile)); mutt_error(_("Write failed! Saved partial mailbox to %s"), savefile); FREE(&new_offset); FREE(&old_offset); return -1; } /* Restore the previous access/modification times */ mbox_reset_atime(m, &statbuf); /* reopen the mailbox in read-only mode */ adata->fp = fopen(m->path, "r"); if (!adata->fp) { unlink(tempfile); mutt_sig_unblock(); mx_fastclose_mailbox(m); mutt_error(_("Fatal error! Could not reopen mailbox!")); FREE(&new_offset); FREE(&old_offset); return -1; } /* update the offsets of the rewritten messages */ for (i = first, j = first; i < m->msg_count; i++) { if (!m->emails[i]->deleted) { m->emails[i]->offset = new_offset[i - first].hdr; m->emails[i]->content->hdr_offset = new_offset[i - first].hdr; m->emails[i]->content->offset = new_offset[i - first].body; m->emails[i]->index = j++; } } FREE(&new_offset); FREE(&old_offset); unlink(tempfile); /* remove partial copy of the mailbox */ mutt_sig_unblock(); if (C_CheckMboxSize) { struct Mailbox *tmp = mutt_find_mailbox(m->path); if (tmp && !tmp->has_new) mutt_update_mailbox(tmp); } return 0; /* signal success */ bail: /* Come here in case of disaster */ mutt_file_fclose(&fp); /* restore offsets, as far as they are valid */ if ((first >= 0) && old_offset) { for (i = first; (i < m->msg_count) && old_offset[i - first].valid; i++) { m->emails[i]->offset = old_offset[i - first].hdr; m->emails[i]->content->hdr_offset = old_offset[i - first].hdr; m->emails[i]->content->offset = old_offset[i - first].body; m->emails[i]->lines = old_offset[i - first].lines; m->emails[i]->content->length = old_offset[i - first].length; } } /* this is ok to call even if we haven't locked anything */ mbox_unlock_mailbox(m); mutt_sig_unblock(); FREE(&new_offset); FREE(&old_offset); adata->fp = freopen(m->path, "r", adata->fp); if (!adata->fp) { mutt_error(_("Could not reopen mailbox")); mx_fastclose_mailbox(m); return -1; } if (need_sort) { /* if the mailbox was reopened, the thread tree will be invalid so make * sure to start threading from scratch. */ mutt_mailbox_changed(m, MBN_RESORT); } return rc; }
/* return values: * 0 success * -1 failure */ int mbox_sync_mailbox(CONTEXT *ctx, int *index_hint) { char tempfile[_POSIX_PATH_MAX]; char buf[32]; int i, j, save_sort = SORT_ORDER; int rc = -1; int need_sort = 0; /* flag to resort mailbox if new mail arrives */ int first = -1; /* first message to be written */ LOFF_T offset; /* location in mailbox to write changed messages */ struct stat statbuf; struct m_update_t *newOffset = NULL; struct m_update_t *oldOffset = NULL; FILE *fp = NULL; progress_t progress; char msgbuf[STRING]; /* sort message by their position in the mailbox on disk */ if (Sort != SORT_ORDER) { save_sort = Sort; Sort = SORT_ORDER; mutt_sort_headers(ctx, 0); Sort = save_sort; need_sort = 1; } /* need to open the file for writing in such a way that it does not truncate * the file, so use read-write mode. */ if ((ctx->fp = freopen(ctx->path, "r+", ctx->fp)) == NULL) { mx_fastclose_mailbox(ctx); mutt_error _("Fatal error! Could not reopen mailbox!"); return -1; } mutt_block_signals(); if (mbox_lock_mailbox(ctx, 1, 1) == -1) { mutt_unblock_signals(); mutt_error _("Unable to lock mailbox!"); goto bail; } /* Check to make sure that the file hasn't changed on disk */ if (((i = mbox_check_mailbox(ctx, index_hint)) == M_NEW_MAIL) || (i == M_REOPENED)) { /* new mail arrived, or mailbox reopened */ need_sort = i; rc = i; goto bail; } else if (i < 0) /* fatal error */ return -1; /* Create a temporary file to write the new version of the mailbox in. */ mutt_mktemp(tempfile, sizeof(tempfile)); if (((i = open(tempfile, O_WRONLY | O_EXCL | O_CREAT, 0600)) == -1) || ((fp = fdopen(i, "w")) == NULL)) { if (-1 != i) { close(i); unlink(tempfile); } mutt_error _("Could not create temporary file!"); mutt_sleep(5); goto bail; } /* find the first deleted/changed message. we save a lot of time by only * rewriting the mailbox from the point where it has actually changed. */ for (i = 0; i < ctx->msgcount && !ctx->hdrs[i]->deleted && !ctx->hdrs[i]->changed && !ctx->hdrs[i]->attach_del; i++) ; if (i == ctx->msgcount) { /* this means ctx->changed or ctx->deleted was set, but no * messages were found to be changed or deleted. This should * never happen, is we presume it is a bug in mutt. */ mutt_error _( "sync: mbox modified, but no modified messages! (report this bug)"); mutt_sleep(5); /* the mutt_error /will/ get cleared! */ dprint(1, "mbox_sync_mailbox(): no modified messages.\n"); unlink(tempfile); goto bail; } /* save the index of the first changed/deleted message */ first = i; /* where to start overwriting */ offset = ctx->hdrs[i]->offset; /* the offset stored in the header does not include the MMDF_SEP, so make * sure we seek to the correct location */ if (ctx->magic == M_MMDF) offset -= (sizeof MMDF_SEP - 1); /* allocate space for the new offsets */ newOffset = (__typeof__(newOffset)) safe_calloc(ctx->msgcount - first, sizeof(struct m_update_t)); oldOffset = (__typeof__(oldOffset)) safe_calloc(ctx->msgcount - first, sizeof(struct m_update_t)); if (!ctx->quiet) { snprintf(msgbuf, sizeof(msgbuf), _("Writing %s..."), ctx->path); mutt_progress_init(&progress, msgbuf, M_PROGRESS_MSG, WriteInc, ctx->msgcount); } for (i = first, j = 0; i < ctx->msgcount; i++) { if (!ctx->quiet) mutt_progress_update(&progress, i, (int)(ftello(ctx->fp) / (ctx->size / 100 + 1))); /* * back up some information which is needed to restore offsets when * something fails. */ oldOffset[i - first].valid = 1; oldOffset[i - first].hdr = ctx->hdrs[i]->offset; oldOffset[i - first].body = ctx->hdrs[i]->content->offset; oldOffset[i - first].lines = ctx->hdrs[i]->lines; oldOffset[i - first].length = ctx->hdrs[i]->content->length; if (!ctx->hdrs[i]->deleted) { j++; if (ctx->magic == M_MMDF) { if (fputs(MMDF_SEP, fp) == EOF) { mutt_perror(tempfile); mutt_sleep(5); unlink(tempfile); goto bail; } } /* save the new offset for this message. we add `offset' because the * temporary file only contains saved message which are located *after * `offset' in the real mailbox */ newOffset[i - first].hdr = ftello(fp) + offset; if (mutt_copy_message(fp, ctx, ctx->hdrs[i], M_CM_UPDATE, CH_FROM | CH_UPDATE | CH_UPDATE_LEN) != 0) { mutt_perror(tempfile); mutt_sleep(5); unlink(tempfile); goto bail; } /* Since messages could have been deleted, the offsets stored in memory * will be wrong, so update what we can, which is the offset of this * message, and the offset of the body. If this is a multipart *message, * we just flush the in memory cache so that the message will be *reparsed * if the user accesses it later. */ newOffset[i - first].body = ftello(fp) - ctx->hdrs[i]->content->length + offset; mutt_free_body(&ctx->hdrs[i]->content->parts); switch (ctx->magic) { case M_MMDF: if (fputs(MMDF_SEP, fp) == EOF) { mutt_perror(tempfile); mutt_sleep(5); unlink(tempfile); goto bail; } break; default: if (fputs("\n", fp) == EOF) { mutt_perror(tempfile); mutt_sleep(5); unlink(tempfile); goto bail; } } } } if (fclose(fp) != 0) { fp = NULL; dprint(1, "mbox_sync_mailbox: safe_fclose (&) returned non-zero.\n"); unlink(tempfile); mutt_perror(tempfile); mutt_sleep(5); goto bail; } fp = NULL; /* Save the state of this folder. */ if (stat(ctx->path, &statbuf) == -1) { mutt_perror(ctx->path); mutt_sleep(5); unlink(tempfile); goto bail; } if ((fp = fopen(tempfile, "r")) == NULL) { mutt_unblock_signals(); mx_fastclose_mailbox(ctx); dprint(1, "mbox_sync_mailbox: unable to reopen temp copy of mailbox!\n"); mutt_perror(tempfile); mutt_sleep(5); return -1; } if ((fseeko(ctx->fp, offset, SEEK_SET) != 0) /* seek the append location */ ||/* do a sanity check to make sure the mailbox looks ok */ (fgets(buf, sizeof(buf), ctx->fp) == NULL) || ((ctx->magic == M_MBOX) && (mutt_strncmp("From ", buf, 5) != 0)) || ((ctx->magic == M_MMDF) && (mutt_strcmp(MMDF_SEP, buf) != 0))) { dprint(1, "mbox_sync_mailbox: message not in expected position."); dprint(1, "\tLINE: %s\n", buf); i = -1; } else { if (fseeko(ctx->fp, offset, SEEK_SET) != 0) { /* return to proper offset */ i = -1; dprint(1, "mbox_sync_mailbox: fseek() failed\n"); } else { /* copy the temp mailbox back into place starting at the first * change/deleted message */ if (!ctx->quiet) mutt_message _("Committing changes..."); i = mutt_copy_stream(fp, ctx->fp); if (ferror(ctx->fp)) i = -1; } if (i == 0) { ctx->size = ftello(ctx->fp); /* update the size of the mailbox */ ftruncate(fileno(ctx->fp), ctx->size); } } safe_fclose(&fp); fp = NULL; mbox_unlock_mailbox(ctx); if ((fclose(ctx->fp) != 0) || (i == -1)) { /* error occurred while writing the mailbox back, so keep the temp copy * around */ char savefile[_POSIX_PATH_MAX]; snprintf(savefile, sizeof(savefile), "%s/mutt.%s-%s-%u", NONULL(Tempdir), NONULL(Username), NONULL(Hostname), (unsigned int)getpid()); rename(tempfile, savefile); mutt_unblock_signals(); mx_fastclose_mailbox(ctx); mutt_pretty_mailbox(savefile, sizeof(savefile)); mutt_error(_("Write failed! Saved partial mailbox to %s"), savefile); mutt_sleep(5); return -1; } /* Restore the previous access/modification times */ mbox_reset_atime(ctx, &statbuf); /* reopen the mailbox in read-only mode */ if ((ctx->fp = fopen(ctx->path, "r")) == NULL) { unlink(tempfile); mutt_unblock_signals(); mx_fastclose_mailbox(ctx); mutt_error _("Fatal error! Could not reopen mailbox!"); return -1; } /* update the offsets of the rewritten messages */ for (i = first, j = first; i < ctx->msgcount; i++) { if (!ctx->hdrs[i]->deleted) { ctx->hdrs[i]->offset = newOffset[i - first].hdr; ctx->hdrs[i]->content->hdr_offset = newOffset[i - first].hdr; ctx->hdrs[i]->content->offset = newOffset[i - first].body; ctx->hdrs[i]->index = j++; } } safe_free(&newOffset); safe_free(&oldOffset); unlink(tempfile); /* remove partial copy of the mailbox */ mutt_unblock_signals(); return 0; /* signal success */ bail: /* Come here in case of disaster */ safe_fclose(&fp); /* restore offsets, as far as they are valid */ if ((first >= 0) && oldOffset) { for (i = first; i < ctx->msgcount && oldOffset[i - first].valid; i++) { ctx->hdrs[i]->offset = oldOffset[i - first].hdr; ctx->hdrs[i]->content->hdr_offset = oldOffset[i - first].hdr; ctx->hdrs[i]->content->offset = oldOffset[i - first].body; ctx->hdrs[i]->lines = oldOffset[i - first].lines; ctx->hdrs[i]->content->length = oldOffset[i - first].length; } } /* this is ok to call even if we haven't locked anything */ mbox_unlock_mailbox(ctx); mutt_unblock_signals(); safe_free(&newOffset); safe_free(&oldOffset); if ((ctx->fp = freopen(ctx->path, "r", ctx->fp)) == NULL) { mutt_error _("Could not reopen mailbox!"); mx_fastclose_mailbox(ctx); return -1; } if (need_sort) /* if the mailbox was reopened, the thread tree will be invalid so make * sure to start threading from scratch. */ mutt_sort_headers(ctx, (need_sort == M_REOPENED)); return rc; }
/* 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; }