Пример #1
0
/* 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;
}
Пример #2
0
Файл: mh.c Проект: hww3/pexts
int mh_check_mailbox(CONTEXT *ctx, int *index_hint)
{
  char buf[_POSIX_PATH_MAX], b1[LONG_STRING], b2[LONG_STRING];
  struct stat st, st_cur;
  short modified = 0, have_new = 0, occult = 0;
  struct maildir *md, *p;
  struct maildir **last;
  HASH *fnames;
  int i, j;
  
  if(!option (OPTCHECKNEW))
    return 0;
  
  if(ctx->magic == M_MH)
  {
    strfcpy(buf, ctx->path, sizeof(buf));
    if(stat(buf, &st) == -1)
      return -1;

    /* create .mh_sequences when there isn't one. */
    snprintf (buf, sizeof (buf), "%s/.mh_sequences", ctx->path);
    if (stat (buf, &st_cur) == -1)
    {
      if (errno == ENOENT)
      {
	char *tmp;
	FILE *fp = NULL;

	if (mh_mkstemp (ctx, &fp, &tmp) == 0)
	{
	  safe_fclose (&fp);
	  if (safe_rename (tmp, buf) == -1)
	    unlink (tmp);
	  safe_free ((void **) &tmp);
	}
	
	if (stat (buf, &st_cur) == -1)
	  modified = 1;
      }
      else
	modified = 1;
    }
  }
  else if(ctx->magic == M_MAILDIR)
  {
    snprintf(buf, sizeof(buf), "%s/new", ctx->path);
    if(stat(buf, &st) == -1)
      return -1;
    
    snprintf(buf, sizeof(buf), "%s/cur", ctx->path);
    if(stat(buf, &st_cur) == -1)			/* XXX - name is bad. */
      modified = 1;

  }
  
  if(!modified && ctx->magic == M_MAILDIR && st_cur.st_mtime > ctx->mtime_cur)
    modified = 1;
  
  if(!modified && ctx->magic == M_MH && (st.st_mtime > ctx->mtime || st_cur.st_mtime > ctx->mtime_cur))
    modified = 1;
  
  if(modified || (ctx->magic == M_MAILDIR && st.st_mtime > ctx->mtime))
    have_new = 1;
  
  if(!modified && !have_new)
    return 0;

  ctx->mtime_cur = st_cur.st_mtime;
  ctx->mtime = st.st_mtime;

#if 0
  if(Sort != SORT_ORDER)
  {
    short old_sort;
    
    old_sort = Sort;
    Sort = SORT_ORDER;
    mutt_sort_headers(ctx, 1);
    Sort = old_sort;
  }
#endif

  md = NULL;
  last = &md;

  if(ctx->magic == M_MAILDIR)
  {
    if(have_new)
      maildir_parse_dir(ctx, &last, "new", NULL);
    if(modified)
      maildir_parse_dir(ctx, &last, "cur", NULL);
  }
  else if(ctx->magic == M_MH)
  {
    struct mh_sequences mhs;
    memset (&mhs, 0, sizeof (mhs));
    maildir_parse_dir (ctx, &last, NULL, NULL);
    mh_read_sequences (&mhs, ctx->path);
    mh_update_maildir (md, &mhs);
    mhs_free_sequences (&mhs);
  }

  /* check for modifications and adjust flags */

  fnames = hash_create (1031);
  
  for(p = md; p; p = p->next)
  {
    if(ctx->magic == M_MAILDIR)
    {
      maildir_canon_filename(b2, p->h->path, sizeof(b2));
      p->canon_fname = safe_strdup(b2);
    }
    else
      p->canon_fname = safe_strdup(p->h->path);
    
    hash_insert(fnames, p->canon_fname, p, 0);
  }

  
  for(i = 0; i < ctx->msgcount; i++)
  {
    ctx->hdrs[i]->active = 0;

    if(ctx->magic == M_MAILDIR)
      maildir_canon_filename(b1, ctx->hdrs[i]->path, sizeof(b1));
    else
      strfcpy(b1, ctx->hdrs[i]->path, sizeof(b1));

    dprint(2, (debugfile, "%s:%d: mh_check_mailbox(): Looking for %s.\n", __FILE__, __LINE__, b1));
    
    if((p = hash_find(fnames, b1)) && p->h &&
       mbox_strict_cmp_headers(ctx->hdrs[i], p->h))
    {
      /* found the right message */

      dprint(2, (debugfile, "%s:%d: Found.  Flags before: %s%s%s%s%s\n", __FILE__, __LINE__,
		 ctx->hdrs[i]->flagged ? "f" : "",
		 ctx->hdrs[i]->deleted ? "D" : "",
		 ctx->hdrs[i]->replied ? "r" : "",
		 ctx->hdrs[i]->old     ? "O" : "",
		 ctx->hdrs[i]->read    ? "R" : ""));

      if(mutt_strcmp(ctx->hdrs[i]->path, p->h->path))
	mutt_str_replace (&ctx->hdrs[i]->path, p->h->path);

      if(modified)
      {
	if(!ctx->hdrs[i]->changed)
	{
	  mutt_set_flag (ctx, ctx->hdrs[i], M_FLAG, p->h->flagged);
	  mutt_set_flag (ctx, ctx->hdrs[i], M_REPLIED, p->h->replied);
	  mutt_set_flag (ctx, ctx->hdrs[i], M_READ, p->h->read);
	}

	mutt_set_flag(ctx, ctx->hdrs[i], M_OLD, p->h->old);
      }

      ctx->hdrs[i]->active = 1;

      dprint(2, (debugfile, "%s:%d:         Flags after: %s%s%s%s%s\n", __FILE__, __LINE__,
		 ctx->hdrs[i]->flagged ? "f" : "",
		 ctx->hdrs[i]->deleted ? "D" : "",
		 ctx->hdrs[i]->replied ? "r" : "",
		 ctx->hdrs[i]->old     ? "O" : "",
		 ctx->hdrs[i]->read    ? "R" : ""));

      mutt_free_header(&p->h);
    }
    else if (ctx->magic == M_MAILDIR && !modified && !strncmp("cur/", ctx->hdrs[i]->path, 4))
    {
      /* If the cur/ part wasn't externally modified for a maildir
       * type folder, assume the message is still active. Actually,
       * we simply don't know.
       */

      ctx->hdrs[i]->active = 1;
    }
    else if (modified || (ctx->magic == M_MAILDIR && !strncmp("new/", ctx->hdrs[i]->path, 4)))
    {
      
      /* Mailbox was modified, or a new message vanished. */

      /* Note: This code will _not_ apply for a new message which
       * is just moved to cur/, as this would modify cur's time
       * stamp and lead to modified == 1.  Thus, we'd have parsed
       * the complete folder above, and the message would have
       * been found in the look-up table.
       */
      
      dprint(2, (debugfile, "%s:%d: Not found.  Flags were: %s%s%s%s%s\n", __FILE__, __LINE__,
		 ctx->hdrs[i]->flagged ? "f" : "",
		 ctx->hdrs[i]->deleted ? "D" : "",
		 ctx->hdrs[i]->replied ? "r" : "",
		 ctx->hdrs[i]->old     ? "O" : "",
		 ctx->hdrs[i]->read    ? "R" : ""));
      
      occult = 1;

    }
  }

  /* destroy the file name hash */

  hash_destroy(&fnames, NULL);

  /* If we didn't just get new mail, update the tables. */
  
  if(modified || occult)
  {
    short old_sort;
    int old_count;

#ifndef LIBMUTT
    if (Sort != SORT_ORDER)
    {
      old_sort = Sort;
      Sort = SORT_ORDER;
      mutt_sort_headers (ctx, 1);
      Sort = old_sort;
    }
#endif
  
    old_count = ctx->msgcount;
    for (i = 0, j = 0; i < old_count; i++)
    {
      if (ctx->hdrs[i]->active && index_hint && *index_hint == i)
	*index_hint = j;

      if (ctx->hdrs[i]->active)
	ctx->hdrs[i]->index = j++;
    }
    mx_update_tables(ctx, 0);
  }

  /* Incorporate new messages */

  maildir_move_to_context(ctx, &md);

  return (modified || occult) ? M_REOPENED : have_new ? M_NEW_MAIL : 0;
}
Пример #3
0
int mutt_reopen_mailbox(CONTEXT *ctx, int *index_hint)
{
    int (*cmp_headers) (const HEADER *,
                        const HEADER *) = NULL;
    HEADER **old_hdrs;
    int old_msgcount;
    int msg_mod = 0;
    int index_hint_set;
    int i, j;
    int rc = -1;

    /* silent operations */
    ctx->quiet = 1;

    if (!ctx->quiet)
        mutt_message _("Reopening mailbox...");

    /* our heuristics require the old mailbox to be unsorted */
    if (Sort != SORT_ORDER) {
        short old_sort;

        old_sort = Sort;
        Sort = SORT_ORDER;
        mutt_sort_headers(ctx, 1);
        Sort = old_sort;
    }

    old_hdrs = NULL;
    old_msgcount = 0;

    /* simulate a close */
    if (ctx->id_hash)
        hash_destroy(&ctx->id_hash, NULL);

    if (ctx->subj_hash)
        hash_destroy(&ctx->subj_hash, NULL);
    mutt_clear_threads(ctx);
    safe_free(&ctx->v2r);

    if (ctx->readonly) {
        for (i = 0; i < ctx->msgcount; i++)
            mutt_free_header(&(ctx->hdrs[i]));  /* nothing to do! */
        safe_free(&ctx->hdrs);
    } else {
        /* save the old headers */
        old_msgcount = ctx->msgcount;
        old_hdrs = ctx->hdrs;
        ctx->hdrs = NULL;
    }

    ctx->hdrmax = 0; /* force allocation of new headers */
    ctx->msgcount = 0;
    ctx->vcount = 0;
    ctx->tagged = 0;
    ctx->deleted = 0;
    ctx->new_messages = 0;
    ctx->unread = 0;
    ctx->flagged = 0;
    ctx->changed = 0;
    ctx->id_hash = NULL;
    ctx->subj_hash = NULL;

    switch (ctx->magic) {
    case M_MBOX:
    case M_MMDF:
        cmp_headers = mbox_strict_cmp_headers;
        safe_fclose(&ctx->fp);

        if (!(ctx->fp = safe_fopen(ctx->path, "r")))
            rc = -1;
        else
            rc = ((ctx->magic == M_MBOX) ? mbox_parse_mailbox
                  : mmdf_parse_mailbox)(ctx);
        break;

    default:
        rc = -1;
        break;
    }

    if (rc == -1) {
        /* free the old headers */
        for (j = 0; j < old_msgcount; j++)
            mutt_free_header(&(old_hdrs[j]));
        safe_free(&old_hdrs);

        ctx->quiet = 0;
        return -1;
    }

    /* now try to recover the old flags */

    index_hint_set = (index_hint == NULL);

    if (!ctx->readonly) {
        for (i = 0; i < ctx->msgcount; i++) {
            int found = 0;

            /* 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"
             */
            for (j = i; j < old_msgcount; j++) {
                if (old_hdrs[j] == NULL)
                    continue;

                if (cmp_headers(ctx->hdrs[i], old_hdrs[j])) {
                    found = 1;
                    break;
                }
            }

            if (!found) {
                for (j = 0; j < i
                     && j < old_msgcount; j++) {
                    if (old_hdrs[j] == NULL)
                        continue;

                    if (cmp_headers(ctx->hdrs[i], old_hdrs[j])) {
                        found = 1;
                        break;
                    }
                }
            }

            if (found) {
                /* this is best done here */
                if (!index_hint_set
                    && (*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(ctx, ctx->hdrs[i], M_FLAG, old_hdrs[j]->flagged);
                    mutt_set_flag(ctx, ctx->hdrs[i], M_REPLIED, old_hdrs[j]->replied);
                    mutt_set_flag(ctx, ctx->hdrs[i], M_OLD, old_hdrs[j]->old);
                    mutt_set_flag(ctx, ctx->hdrs[i], M_READ, old_hdrs[j]->read);
                }
                mutt_set_flag(ctx, ctx->hdrs[i], M_DELETE, old_hdrs[j]->deleted);
                mutt_set_flag(ctx, ctx->hdrs[i], M_TAG, old_hdrs[j]->tagged);

                /* we don't need this header any more */
                mutt_free_header(&(old_hdrs[j]));
            }
        }

        /* free the remaining old headers */
        for (j = 0; j < old_msgcount; j++) {
            if (old_hdrs[j]) {
                mutt_free_header(&(old_hdrs[j]));
                msg_mod = 1;
            }
        }
        safe_free(&old_hdrs);
    }

    ctx->quiet = 0;

    return (ctx->changed
            || msg_mod) ? M_REOPENED : M_NEW_MAIL;
}