Exemplo n.º 1
0
Arquivo: mbox.c Projeto: kdave/neomutt
/**
 * 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;
}
Exemplo n.º 2
0
/**
 * execute_command - Run a system command
 * @param m        Mailbox to work with
 * @param command  Command string to execute
 * @param progress Message to show the user
 * @retval 1 Success
 * @retval 0 Failure
 *
 * Run the supplied command, taking care of all the NeoMutt requirements,
 * such as locking files and blocking signals.
 */
static int execute_command(struct Mailbox *m, const char *command, const char *progress)
{
  int rc = 1;
  char sys_cmd[STR_COMMAND];

  if (!m || !command || !progress)
    return 0;

  if (!m->quiet)
  {
    mutt_message(progress, m->realpath);
  }

  mutt_sig_block();
  endwin();
  fflush(stdout);

  expand_command_str(m, command, sys_cmd, sizeof(sys_cmd));

  if (mutt_system(sys_cmd) != 0)
  {
    rc = 0;
    mutt_any_key_to_continue(NULL);
    mutt_error(_("Error running \"%s\""), sys_cmd);
  }

  mutt_sig_unblock();

  return rc;
}
Exemplo n.º 3
0
Arquivo: mbox.c Projeto: kdave/neomutt
/**
 * mbox_mbox_close - Implements MxOps::mbox_close()
 */
static int mbox_mbox_close(struct Mailbox *m)
{
  if (!m)
    return -1;

  struct MboxAccountData *adata = mbox_adata_get(m);
  if (!adata)
    return -1;

  if (!adata->fp)
    return 0;

  if (adata->append)
  {
    mutt_file_unlock(fileno(adata->fp));
    mutt_sig_unblock();
  }

  mutt_file_fclose(&adata->fp);

  /* fix up the times so mailbox won't get confused */
  if (m->peekonly && (m->path[0] != '\0') &&
      (mutt_file_timespec_compare(&m->mtime, &adata->atime) > 0))
  {
#ifdef HAVE_UTIMENSAT
    struct timespec ts[2];
    ts[0] = adata->atime;
    ts[1] = m->mtime;
    utimensat(0, m->path, ts, 0);
#else
    struct utimbuf ut;
    ut.actime = adata->atime.tv_sec;
    ut.modtime = m->mtime.tv_sec;
    utime(m->path, &ut);
#endif
  }

  return 0;
}
Exemplo n.º 4
0
Arquivo: mbox.c Projeto: kdave/neomutt
/**
 * 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;
}
Exemplo n.º 5
0
Arquivo: mbox.c Projeto: kdave/neomutt
/**
 * 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;
}