예제 #1
0
파일: ssl_gnutls.c 프로젝트: darnir/neomutt
/**
 * tls_check_stored_hostname - Does the hostname match a stored certificate?
 * @param cert     Certificate
 * @param hostname Hostname
 * @retval 1 Hostname match found
 * @retval 0 Error, or no match
 */
static int tls_check_stored_hostname(const gnutls_datum_t *cert, const char *hostname)
{
  char *linestr = NULL;
  size_t linestrsize = 0;
  int linenum = 0;
  regex_t preg;
  regmatch_t pmatch[3];

  /* try checking against names stored in stored certs file */
  FILE *fp = fopen(C_CertificateFile, "r");
  if (fp)
  {
    if (REGCOMP(&preg, "^#H ([a-zA-Z0-9_\\.-]+) ([0-9A-F]{4}( [0-9A-F]{4}){7})[ \t]*$",
                REG_ICASE) != 0)
    {
      mutt_file_fclose(&fp);
      return 0;
    }

    char buf[80];
    buf[0] = '\0';
    tls_fingerprint(GNUTLS_DIG_MD5, buf, sizeof(buf), cert);
    while ((linestr = mutt_file_read_line(linestr, &linestrsize, fp, &linenum, 0)))
    {
      if ((linestr[0] == '#') && (linestr[1] == 'H'))
      {
        if (regexec(&preg, linestr, 3, pmatch, 0) == 0)
        {
          linestr[pmatch[1].rm_eo] = '\0';
          linestr[pmatch[2].rm_eo] = '\0';
          if ((strcmp(linestr + pmatch[1].rm_so, hostname) == 0) &&
              (strcmp(linestr + pmatch[2].rm_so, buf) == 0))
          {
            regfree(&preg);
            FREE(&linestr);
            mutt_file_fclose(&fp);
            return 1;
          }
        }
      }
    }

    regfree(&preg);
    mutt_file_fclose(&fp);
  }

  /* not found a matching name */
  return 0;
}
예제 #2
0
파일: mbox.c 프로젝트: kdave/neomutt
/**
 * mbox_mbox_open_append - Implements MxOps::mbox_open_append()
 */
static int mbox_mbox_open_append(struct Mailbox *m, OpenMailboxFlags flags)
{
  if (!m)
    return -1;

  if (init_mailbox(m) != 0)
    return -1;

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

  adata->fp = mutt_file_fopen(m->path, (flags & MUTT_NEWFOLDER) ? "w" : "a");
  if (!adata->fp)
  {
    mutt_perror(m->path);
    return -1;
  }

  if (mbox_lock_mailbox(m, true, true) != false)
  {
    mutt_error(_("Couldn't lock %s"), m->path);
    mutt_file_fclose(&adata->fp);
    return -1;
  }

  fseek(adata->fp, 0, SEEK_END);

  return 0;
}
예제 #3
0
파일: icommands.c 프로젝트: darnir/neomutt
/**
 * icmd_version - Parse 'version' command - Implements ::icommand_t
 */
static enum CommandResult icmd_version(struct Buffer *buf, struct Buffer *s,
                                       unsigned long data, struct Buffer *err)
{
  char tempfile[PATH_MAX];
  mutt_mktemp(tempfile, sizeof(tempfile));

  FILE *fp_out = mutt_file_fopen(tempfile, "w");
  if (!fp_out)
  {
    mutt_buffer_addstr(err, _("Could not create temporary file"));
    return MUTT_CMD_ERROR;
  }

  print_version(fp_out);
  mutt_file_fclose(&fp_out);

  struct Pager info = { 0 };
  if (mutt_pager("version", tempfile, 0, &info) == -1)
  {
    mutt_buffer_addstr(err, _("Could not create temporary file"));
    return MUTT_CMD_ERROR;
  }

  return MUTT_CMD_SUCCESS;
}
예제 #4
0
파일: compress.c 프로젝트: darnir/neomutt
/**
 * lock_realpath - Try to lock the ctx->realpath
 * @param m    Mailbox to lock
 * @param excl Lock exclusively?
 * @retval true  Success (locked or readonly)
 * @retval false Error (can't lock the file)
 *
 * Try to (exclusively) lock the mailbox.  If we succeed, then we mark the
 * mailbox as locked.  If we fail, but we didn't want exclusive rights, then
 * the mailbox will be marked readonly.
 */
static bool lock_realpath(struct Mailbox *m, bool excl)
{
  if (!m || !m->compress_info)
    return false;

  struct CompressInfo *ci = m->compress_info;

  if (ci->locked)
    return true;

  if (excl)
    ci->fp_lock = fopen(m->realpath, "a");
  else
    ci->fp_lock = fopen(m->realpath, "r");
  if (!ci->fp_lock)
  {
    mutt_perror(m->realpath);
    return false;
  }

  int r = mutt_file_lock(fileno(ci->fp_lock), excl, true);
  if (r == 0)
    ci->locked = true;
  else if (excl)
  {
    mutt_file_fclose(&ci->fp_lock);
    m->readonly = true;
    return true;
  }

  return r == 0;
}
예제 #5
0
파일: mbox.c 프로젝트: kdave/neomutt
/**
 * mbox_path_probe - Is this an mbox mailbox? - Implements MxOps::path_probe()
 */
enum MailboxType mbox_path_probe(const char *path, const struct stat *st)
{
  if (!path || !st)
    return MUTT_UNKNOWN;

  if (S_ISDIR(st->st_mode))
    return MUTT_UNKNOWN;

  if (st->st_size == 0)
    return MUTT_MBOX;

  FILE *fp = fopen(path, "r");
  if (!fp)
    return MUTT_UNKNOWN;

  int ch;
  while ((ch = fgetc(fp)) != EOF)
  {
    /* Some mailbox creation tools erroneously append a blank line to
     * a file before appending a mail message.  This allows neomutt to
     * detect magic for and thus open those files. */
    if ((ch != '\n') && (ch != '\r'))
    {
      ungetc(ch, fp);
      break;
    }
  }

  enum MailboxType magic = MUTT_UNKNOWN;
  char tmp[256];
  if (fgets(tmp, sizeof(tmp), fp))
  {
    if (mutt_str_startswith(tmp, "From ", CASE_MATCH))
      magic = MUTT_MBOX;
    else if (mutt_str_strcmp(tmp, MMDF_SEP) == 0)
      magic = MUTT_MMDF;
  }
  mutt_file_fclose(&fp);

  if (!C_CheckMboxSize)
  {
    /* need to restore the times here, the file was not really accessed,
     * only the type was accessed.  This is important, because detection
     * of "new mail" depends on those times set correctly.  */
#ifdef HAVE_UTIMENSAT
    struct timespec ts[2];
    mutt_file_get_stat_timespec(&ts[0], &st, MUTT_STAT_ATIME);
    mutt_file_get_stat_timespec(&ts[1], &st, MUTT_STAT_MTIME);
    utimensat(0, path, ts, 0);
#else
    struct utimbuf times;
    times.actime = st->st_atime;
    times.modtime = st->st_mtime;
    utime(path, &times);
#endif
  }

  return magic;
}
예제 #6
0
파일: mbox.c 프로젝트: kdave/neomutt
/**
 * mbox_adata_free - Free data attached to the Mailbox
 * @param[out] ptr Private mailbox data
 */
static void mbox_adata_free(void **ptr)
{
  if (!ptr || !*ptr)
    return;

  struct MboxAccountData *m = *ptr;

  mutt_file_fclose(&m->fp);
  FREE(ptr);
}
예제 #7
0
파일: mh.c 프로젝트: kdave/neomutt
/**
 * mh_read_sequences - Read a set of MH sequences
 * @param mhs  Existing sequences
 * @param path File to read from
 * @retval  0 Success
 * @retval -1 Error
 */
int mh_read_sequences(struct MhSequences *mhs, const char *path)
{
  int line = 1;
  char *buf = NULL;
  size_t sz = 0;

  MhSeqFlags flags;
  int first, last, rc = 0;

  char pathname[PATH_MAX];
  snprintf(pathname, sizeof(pathname), "%s/.mh_sequences", path);

  FILE *fp = fopen(pathname, "r");
  if (!fp)
    return 0; /* yes, ask callers to silently ignore the error */

  while ((buf = mutt_file_read_line(buf, &sz, fp, &line, 0)))
  {
    char *t = strtok(buf, " \t:");
    if (!t)
      continue;

    if (mutt_str_strcmp(t, C_MhSeqUnseen) == 0)
      flags = MH_SEQ_UNSEEN;
    else if (mutt_str_strcmp(t, C_MhSeqFlagged) == 0)
      flags = MH_SEQ_FLAGGED;
    else if (mutt_str_strcmp(t, C_MhSeqReplied) == 0)
      flags = MH_SEQ_REPLIED;
    else /* unknown sequence */
      continue;

    while ((t = strtok(NULL, " \t:")))
    {
      if (mh_read_token(t, &first, &last) < 0)
      {
        mhs_free_sequences(mhs);
        rc = -1;
        goto out;
      }
      for (; first <= last; first++)
        mhs_set(mhs, first, flags);
    }
  }

  rc = 0;

out:
  FREE(&buf);
  mutt_file_fclose(&fp);
  return rc;
}
예제 #8
0
파일: icommands.c 프로젝트: darnir/neomutt
/**
 * icmd_set - Parse 'set' command to display config - Implements ::icommand_t
 */
static enum CommandResult icmd_set(struct Buffer *buf, struct Buffer *s,
                                   unsigned long data, struct Buffer *err)
{
  char tempfile[PATH_MAX];
  mutt_mktemp(tempfile, sizeof(tempfile));

  FILE *fp_out = mutt_file_fopen(tempfile, "w");
  if (!fp_out)
  {
    mutt_buffer_addstr(err, _("Could not create temporary file"));
    return MUTT_CMD_ERROR;
  }

  if (mutt_str_strcmp(s->data, "set all") == 0)
  {
    dump_config(Config, CS_DUMP_STYLE_NEO, 0, fp_out);
  }
  else if (mutt_str_strcmp(s->data, "set") == 0)
  {
    dump_config(Config, CS_DUMP_STYLE_NEO, CS_DUMP_ONLY_CHANGED, fp_out);
  }
  else
  {
    mutt_file_fclose(&fp_out);
    return MUTT_CMD_ERROR;
  }

  mutt_file_fclose(&fp_out);

  struct Pager info = { 0 };
  if (mutt_pager("set", tempfile, 0, &info) == -1)
  {
    mutt_buffer_addstr(err, _("Could not create temporary file"));
    return MUTT_CMD_ERROR;
  }

  return MUTT_CMD_SUCCESS;
}
예제 #9
0
파일: ssl.c 프로젝트: darnir/neomutt
/**
 * check_certificate_file - Read and check a certificate file
 * @param peercert Certificate
 * @retval true  Certificate is valid
 * @retval false Error, or certificate is invalid
 */
static bool check_certificate_file(X509 *peercert)
{
  unsigned char peermd[EVP_MAX_MD_SIZE];
  unsigned int peermdlen;
  X509 *cert = NULL;
  int pass = false;
  FILE *fp = NULL;

  if (!C_CertificateFile)
    return false;

  fp = fopen(C_CertificateFile, "rt");
  if (!fp)
    return false;

  if (!X509_digest(peercert, EVP_sha256(), peermd, &peermdlen))
  {
    mutt_file_fclose(&fp);
    return false;
  }

  while (PEM_read_X509(fp, &cert, NULL, NULL))
  {
    if (compare_certificates(cert, peercert, peermd, peermdlen) &&
        check_certificate_expiration(cert, true))
    {
      pass = true;
      break;
    }
  }
  /* PEM_read_X509 sets an error on eof */
  if (!pass)
    ERR_clear_error();
  X509_free(cert);
  mutt_file_fclose(&fp);

  return pass;
}
예제 #10
0
파일: compress.c 프로젝트: darnir/neomutt
/**
 * unlock_realpath - Unlock the mailbox->realpath
 * @param m Mailbox to unlock
 *
 * Unlock a mailbox previously locked by lock_mailbox().
 */
static void unlock_realpath(struct Mailbox *m)
{
  if (!m || !m->compress_info)
    return;

  struct CompressInfo *ci = m->compress_info;

  if (!ci->locked)
    return;

  mutt_file_unlock(fileno(ci->fp_lock));

  ci->locked = false;
  mutt_file_fclose(&ci->fp_lock);
}
예제 #11
0
파일: mbox.c 프로젝트: kdave/neomutt
/**
 * mbox_test_new_folder - Test if an mbox or mmdf mailbox has new mail
 * @param path Path to the mailbox
 * @retval bool true if the folder contains new mail
 */
bool mbox_test_new_folder(const char *path)
{
  bool rc = false;

  enum MailboxType magic = mx_path_probe(path, NULL);

  if ((magic != MUTT_MBOX) && (magic != MUTT_MMDF))
    return false;

  FILE *fp = fopen(path, "rb");
  if (fp)
  {
    rc = test_last_status_new(fp);
    mutt_file_fclose(&fp);
  }

  return rc;
}
예제 #12
0
파일: crypt.c 프로젝트: darnir/neomutt
/**
 * crypt_write_signed - Write the message body/part
 * @param a        Body to write
 * @param s        State to use
 * @param tempfile File to write to
 * @retval  0 Success
 * @retval -1 Error
 *
 * Body/part A described by state S to the given TEMPFILE.
 */
int crypt_write_signed(struct Body *a, struct State *s, const char *tempfile)
{
  FILE *fp = NULL;
  bool hadcr;
  size_t bytes;

  if (!WithCrypto)
    return -1;

  fp = mutt_file_fopen(tempfile, "w");
  if (!fp)
  {
    mutt_perror(tempfile);
    return -1;
  }

  fseeko(s->fp_in, a->hdr_offset, SEEK_SET);
  bytes = a->length + a->offset - a->hdr_offset;
  hadcr = false;
  while (bytes > 0)
  {
    const int c = fgetc(s->fp_in);
    if (c == EOF)
      break;

    bytes--;

    if (c == '\r')
      hadcr = true;
    else
    {
      if ((c == '\n') && !hadcr)
        fputc('\r', fp);

      hadcr = false;
    }

    fputc(c, fp);
  }
  mutt_file_fclose(&fp);

  return 0;
}
예제 #13
0
파일: ssl.c 프로젝트: darnir/neomutt
/**
 * ssl_load_certificates - Load certificates and filter out the expired ones
 * @param ctx SSL context
 * @retval 1 Success
 * @retval 0 Error
 *
 * ssl certificate verification can behave strangely if there are expired certs
 * loaded into the trusted store.  This function filters out expired certs.
 *
 * Previously the code used this form:
 *     SSL_CTX_load_verify_locations (ssldata->ctx, #C_CertificateFile, NULL);
 */
static int ssl_load_certificates(SSL_CTX *ctx)
{
  FILE *fp = NULL;
  X509 *cert = NULL;
  X509_STORE *store = NULL;
  int rc = 1;
  char buf[256];

  mutt_debug(LL_DEBUG2, "loading trusted certificates\n");
  store = SSL_CTX_get_cert_store(ctx);
  if (!store)
  {
    store = X509_STORE_new();
    SSL_CTX_set_cert_store(ctx, store);
  }

  fp = fopen(C_CertificateFile, "rt");
  if (!fp)
    return 0;

  while (NULL != PEM_read_X509(fp, &cert, NULL, NULL))
  {
    if ((X509_cmp_current_time(X509_get0_notBefore(cert)) >= 0) ||
        (X509_cmp_current_time(X509_get0_notAfter(cert)) <= 0))
    {
      mutt_debug(LL_DEBUG2, "filtering expired cert: %s\n",
                 X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)));
    }
    else
    {
      X509_STORE_add_cert(store, cert);
    }
  }
  /* PEM_read_X509 sets the error NO_START_LINE on eof */
  if (ERR_GET_REASON(ERR_peek_last_error()) != PEM_R_NO_START_LINE)
    rc = 0;
  ERR_clear_error();

  X509_free(cert);
  mutt_file_fclose(&fp);

  return rc;
}
예제 #14
0
파일: compress.c 프로젝트: darnir/neomutt
/**
 * setup_paths - Set the mailbox paths
 * @param m Mailbox to modify
 * @retval  0 Success
 * @retval -1 Error
 *
 * Save the compressed filename in mailbox->realpath.
 * Create a temporary filename and put its name in mailbox->path.
 * The temporary file is created to prevent symlink attacks.
 */
static int setup_paths(struct Mailbox *m)
{
  if (!m)
    return -1;

  char tmp[PATH_MAX];

  /* Setup the right paths */
  mutt_str_strfcpy(m->realpath, m->path, sizeof(m->realpath));

  /* We will uncompress to /tmp */
  mutt_mktemp(tmp, sizeof(tmp));
  mutt_str_strfcpy(m->path, tmp, sizeof(m->path));

  FILE *fp = mutt_file_fopen(m->path, "w");
  if (!fp)
    return -1;

  mutt_file_fclose(&fp);
  return 0;
}
예제 #15
0
파일: mbox.c 프로젝트: 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;
}
예제 #16
0
파일: icommands.c 프로젝트: darnir/neomutt
/**
 * icmd_bind - Parse 'bind' and 'macro' commands - Implements ::icommand_t
 */
static enum CommandResult icmd_bind(struct Buffer *buf, struct Buffer *s,
                                    unsigned long data, struct Buffer *err)
{
  FILE *fp_out = NULL;
  char tempfile[PATH_MAX];
  bool dump_all = false, bind = (data == 0);

  if (!MoreArgs(s))
    dump_all = true;
  else
    mutt_extract_token(buf, s, 0);

  if (MoreArgs(s))
  {
    /* More arguments potentially means the user is using the
     * ::command_t :bind command thus we delegate the task. */
    return MUTT_CMD_ERROR;
  }

  struct Buffer *filebuf = mutt_buffer_alloc(4096);
  if (dump_all || (mutt_str_strcasecmp(buf->data, "all") == 0))
  {
    dump_all_menus(filebuf, bind);
  }
  else
  {
    const int menu_index = mutt_map_get_value(buf->data, Menus);
    if (menu_index == -1)
    {
      mutt_buffer_printf(err, _("%s: no such menu"), buf->data);
      mutt_buffer_free(&filebuf);
      return MUTT_CMD_ERROR;
    }

    struct Mapping menu = { buf->data, menu_index };
    dump_menu(filebuf, &menu, bind);
  }

  if (mutt_buffer_is_empty(filebuf))
  {
    mutt_buffer_printf(err, _("%s: no %s for this menu"),
                       dump_all ? "all" : buf->data, bind ? "binds" : "macros");
    mutt_buffer_free(&filebuf);
    return MUTT_CMD_ERROR;
  }

  mutt_mktemp(tempfile, sizeof(tempfile));
  fp_out = mutt_file_fopen(tempfile, "w");
  if (!fp_out)
  {
    mutt_buffer_printf(err, _("Could not create temporary file %s"), tempfile);
    mutt_buffer_free(&filebuf);
    return MUTT_CMD_ERROR;
  }
  fputs(filebuf->data, fp_out);

  mutt_file_fclose(&fp_out);
  mutt_buffer_free(&filebuf);

  struct Pager info = { 0 };
  if (mutt_pager((bind) ? "bind" : "macro", tempfile, 0, &info) == -1)
  {
    mutt_buffer_printf(err, _("Could not create temporary file %s"), tempfile);
    return MUTT_CMD_ERROR;
  }

  return MUTT_CMD_SUCCESS;
}
예제 #17
0
파일: ssl.c 프로젝트: darnir/neomutt
/**
 * interactive_check_cert - Ask the user if a certificate is valid
 * @param cert         Certificate
 * @param idx          Place of certificate in the chain
 * @param len          Length of the certificate chain
 * @param ssl          SSL state
 * @param allow_always If certificate may be always allowed
 * @retval true  User selected 'skip'
 * @retval false Otherwise
 */
static bool interactive_check_cert(X509 *cert, int idx, size_t len, SSL *ssl, bool allow_always)
{
  static const int part[] = {
    NID_commonName,             /* CN */
    NID_pkcs9_emailAddress,     /* Email */
    NID_organizationName,       /* O */
    NID_organizationalUnitName, /* OU */
    NID_localityName,           /* L */
    NID_stateOrProvinceName,    /* ST */
    NID_countryName,            /* C */
  };
  X509_NAME *x509_subject = NULL;
  X509_NAME *x509_issuer = NULL;
  char helpstr[1024];
  char buf[256];
  char title[256];
  struct Menu *menu = mutt_menu_new(MENU_GENERIC);
  int done, row;
  FILE *fp = NULL;
  int ALLOW_SKIP = 0; /* All caps tells Coverity that this is effectively a preproc condition */

  mutt_menu_push_current(menu);

  menu->max = mutt_array_size(part) * 2 + 11;
  menu->dialog = mutt_mem_calloc(1, menu->max * sizeof(char *));
  for (int i = 0; i < menu->max; i++)
    menu->dialog[i] = mutt_mem_calloc(1, dialog_row_len * sizeof(char));

  row = 0;
  mutt_str_strfcpy(menu->dialog[row], _("This certificate belongs to:"), dialog_row_len);
  row++;
  x509_subject = X509_get_subject_name(cert);
  for (unsigned int u = 0; u < mutt_array_size(part); u++)
  {
    snprintf(menu->dialog[row++], dialog_row_len, "   %s",
             x509_get_part(x509_subject, part[u]));
  }

  row++;
  mutt_str_strfcpy(menu->dialog[row], _("This certificate was issued by:"), dialog_row_len);
  row++;
  x509_issuer = X509_get_issuer_name(cert);
  for (unsigned int u = 0; u < mutt_array_size(part); u++)
  {
    snprintf(menu->dialog[row++], dialog_row_len, "   %s",
             x509_get_part(x509_issuer, part[u]));
  }

  row++;
  snprintf(menu->dialog[row++], dialog_row_len, "%s", _("This certificate is valid"));
  snprintf(menu->dialog[row++], dialog_row_len, _("   from %s"),
           asn1time_to_string(X509_getm_notBefore(cert)));
  snprintf(menu->dialog[row++], dialog_row_len, _("     to %s"),
           asn1time_to_string(X509_getm_notAfter(cert)));

  row++;
  buf[0] = '\0';
  x509_fingerprint(buf, sizeof(buf), cert, EVP_sha1);
  snprintf(menu->dialog[row++], dialog_row_len, _("SHA1 Fingerprint: %s"), buf);
  buf[0] = '\0';
  buf[40] = '\0'; /* Ensure the second printed line is null terminated */
  x509_fingerprint(buf, sizeof(buf), cert, EVP_sha256);
  buf[39] = '\0'; /* Divide into two lines of output */
  snprintf(menu->dialog[row++], dialog_row_len, "%s%s", _("SHA256 Fingerprint: "), buf);
  snprintf(menu->dialog[row++], dialog_row_len, "%*s%s",
           (int) mutt_str_strlen(_("SHA256 Fingerprint: ")), "", buf + 40);

  snprintf(title, sizeof(title),
           _("SSL Certificate check (certificate %zu of %zu in chain)"), len - idx, len);
  menu->title = title;

/* The leaf/host certificate can't be skipped. */
#ifdef HAVE_SSL_PARTIAL_CHAIN
  if ((idx != 0) && C_SslVerifyPartialChains)
    ALLOW_SKIP = 1;
#endif

  /* Inside ssl_verify_callback(), this function is guarded by a call to
   * check_certificate_by_digest().  This means if check_certificate_expiration() is
   * true, then check_certificate_file() must be false.  Therefore we don't need
   * to also scan the certificate file here.  */
  allow_always = allow_always && C_CertificateFile &&
                 check_certificate_expiration(cert, true);

  /* L10N: These four letters correspond to the choices in the next four strings:
     (r)eject, accept (o)nce, (a)ccept always, (s)kip.
     These prompts are the interactive certificate confirmation prompts for
     an OpenSSL connection. */
  menu->keys = _("roas");
  if (allow_always)
  {
    if (ALLOW_SKIP)
      menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always, (s)kip");
    else
      menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always");
  }
  else
  {
    if (ALLOW_SKIP)
      menu->prompt = _("(r)eject, accept (o)nce, (s)kip");
    else
      menu->prompt = _("(r)eject, accept (o)nce");
  }

  helpstr[0] = '\0';
  mutt_make_help(buf, sizeof(buf), _("Exit  "), MENU_GENERIC, OP_EXIT);
  mutt_str_strcat(helpstr, sizeof(helpstr), buf);
  mutt_make_help(buf, sizeof(buf), _("Help"), MENU_GENERIC, OP_HELP);
  mutt_str_strcat(helpstr, sizeof(helpstr), buf);
  menu->help = helpstr;

  done = 0;
  OptIgnoreMacroEvents = true;
  while (done == 0)
  {
    switch (mutt_menu_loop(menu))
    {
      case -1:         /* abort */
      case OP_MAX + 1: /* reject */
      case OP_EXIT:
        done = 1;
        break;
      case OP_MAX + 3: /* accept always */
        if (!allow_always)
          break;
        done = 0;
        fp = fopen(C_CertificateFile, "a");
        if (fp)
        {
          if (PEM_write_X509(fp, cert))
            done = 1;
          mutt_file_fclose(&fp);
        }
        if (done == 0)
        {
          mutt_error(_("Warning: Couldn't save certificate"));
        }
        else
        {
          mutt_message(_("Certificate saved"));
          mutt_sleep(0);
        }
      /* fallthrough */
      case OP_MAX + 2: /* accept once */
        done = 2;
        SSL_set_ex_data(ssl, SkipModeExDataIndex, NULL);
        ssl_cache_trusted_cert(cert);
        break;
      case OP_MAX + 4: /* skip */
        if (!ALLOW_SKIP)
          break;
        done = 2;
        SSL_set_ex_data(ssl, SkipModeExDataIndex, &SkipModeExDataIndex);
        break;
    }
  }
  OptIgnoreMacroEvents = false;
  mutt_menu_pop_current(menu);
  mutt_menu_destroy(&menu);
  mutt_debug(LL_DEBUG2, "done=%d\n", done);
  return done == 2;
}
예제 #18
0
파일: ssl_gnutls.c 프로젝트: darnir/neomutt
/**
 * tls_check_one_certificate - Check a GnuTLS certificate
 * @param certdata List of GnuTLS certificates
 * @param certstat GnuTLS certificate status
 * @param hostname Hostname
 * @param idx      Index into certificate list
 * @param len      Length of certificate list
 * @retval 0  Failure
 * @retval >0 Success
 */
static int tls_check_one_certificate(const gnutls_datum_t *certdata,
                                     gnutls_certificate_status_t certstat,
                                     const char *hostname, int idx, size_t len)
{
  int certerr, savedcert;
  gnutls_x509_crt_t cert;
  char buf[128];
  char fpbuf[128];
  size_t buflen;
  char dn_common_name[128];
  char dn_email[128];
  char dn_organization[128];
  char dn_organizational_unit[128];
  char dn_locality[128];
  char dn_province[128];
  char dn_country[128];
  time_t t;
  char datestr[30];
  struct Menu *menu = NULL;
  char helpstr[1024];
  char title[256];
  FILE *fp = NULL;
  gnutls_datum_t pemdata;
  int row, done, ret;

  if (tls_check_preauth(certdata, certstat, hostname, idx, &certerr, &savedcert) == 0)
    return 1;

  /* interactive check from user */
  if (gnutls_x509_crt_init(&cert) < 0)
  {
    mutt_error(_("Error initialising gnutls certificate data"));
    return 0;
  }

  if (gnutls_x509_crt_import(cert, certdata, GNUTLS_X509_FMT_DER) < 0)
  {
    mutt_error(_("Error processing certificate data"));
    gnutls_x509_crt_deinit(cert);
    return 0;
  }

  menu = mutt_menu_new(MENU_GENERIC);
  menu->max = 27;
  menu->dialog = mutt_mem_calloc(1, menu->max * sizeof(char *));
  for (int i = 0; i < menu->max; i++)
    menu->dialog[i] = mutt_mem_calloc(1, dialog_row_len * sizeof(char));
  mutt_menu_push_current(menu);

  row = 0;
  mutt_str_strfcpy(menu->dialog[row], _("This certificate belongs to:"), dialog_row_len);
  row++;

  buflen = sizeof(dn_common_name);
  if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0,
                                    dn_common_name, &buflen) != 0)
  {
    dn_common_name[0] = '\0';
  }
  buflen = sizeof(dn_email);
  if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0, dn_email, &buflen) != 0)
    dn_email[0] = '\0';
  buflen = sizeof(dn_organization);
  if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATION_NAME, 0,
                                    0, dn_organization, &buflen) != 0)
  {
    dn_organization[0] = '\0';
  }
  buflen = sizeof(dn_organizational_unit);
  if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME,
                                    0, 0, dn_organizational_unit, &buflen) != 0)
  {
    dn_organizational_unit[0] = '\0';
  }
  buflen = sizeof(dn_locality);
  if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0,
                                    dn_locality, &buflen) != 0)
  {
    dn_locality[0] = '\0';
  }
  buflen = sizeof(dn_province);
  if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME,
                                    0, 0, dn_province, &buflen) != 0)
  {
    dn_province[0] = '\0';
  }
  buflen = sizeof(dn_country);
  if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0,
                                    dn_country, &buflen) != 0)
  {
    dn_country[0] = '\0';
  }

  snprintf(menu->dialog[row++], dialog_row_len, "   %s  %s", dn_common_name, dn_email);
  snprintf(menu->dialog[row++], dialog_row_len, "   %s", dn_organization);
  snprintf(menu->dialog[row++], dialog_row_len, "   %s", dn_organizational_unit);
  snprintf(menu->dialog[row++], dialog_row_len, "   %s  %s  %s", dn_locality,
           dn_province, dn_country);
  row++;

  mutt_str_strfcpy(menu->dialog[row], _("This certificate was issued by:"), dialog_row_len);
  row++;

  buflen = sizeof(dn_common_name);
  if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0,
                                           0, dn_common_name, &buflen) != 0)
  {
    dn_common_name[0] = '\0';
  }
  buflen = sizeof(dn_email);
  if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_PKCS9_EMAIL, 0, 0,
                                           dn_email, &buflen) != 0)
  {
    dn_email[0] = '\0';
  }
  buflen = sizeof(dn_organization);
  if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATION_NAME,
                                           0, 0, dn_organization, &buflen) != 0)
  {
    dn_organization[0] = '\0';
  }
  buflen = sizeof(dn_organizational_unit);
  if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME,
                                           0, 0, dn_organizational_unit, &buflen) != 0)
  {
    dn_organizational_unit[0] = '\0';
  }
  buflen = sizeof(dn_locality);
  if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_LOCALITY_NAME,
                                           0, 0, dn_locality, &buflen) != 0)
  {
    dn_locality[0] = '\0';
  }
  buflen = sizeof(dn_province);
  if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME,
                                           0, 0, dn_province, &buflen) != 0)
  {
    dn_province[0] = '\0';
  }
  buflen = sizeof(dn_country);
  if (gnutls_x509_crt_get_issuer_dn_by_oid(cert, GNUTLS_OID_X520_COUNTRY_NAME,
                                           0, 0, dn_country, &buflen) != 0)
  {
    dn_country[0] = '\0';
  }

  snprintf(menu->dialog[row++], dialog_row_len, "   %s  %s", dn_common_name, dn_email);
  snprintf(menu->dialog[row++], dialog_row_len, "   %s", dn_organization);
  snprintf(menu->dialog[row++], dialog_row_len, "   %s", dn_organizational_unit);
  snprintf(menu->dialog[row++], dialog_row_len, "   %s  %s  %s", dn_locality,
           dn_province, dn_country);
  row++;

  snprintf(menu->dialog[row++], dialog_row_len, _("This certificate is valid"));

  t = gnutls_x509_crt_get_activation_time(cert);
  mutt_date_make_tls(datestr, sizeof(datestr), t);
  snprintf(menu->dialog[row++], dialog_row_len, _("   from %s"), datestr);

  t = gnutls_x509_crt_get_expiration_time(cert);
  mutt_date_make_tls(datestr, sizeof(datestr), t);
  snprintf(menu->dialog[row++], dialog_row_len, _("     to %s"), datestr);

  fpbuf[0] = '\0';
  tls_fingerprint(GNUTLS_DIG_SHA, fpbuf, sizeof(fpbuf), certdata);
  snprintf(menu->dialog[row++], dialog_row_len, _("SHA1 Fingerprint: %s"), fpbuf);
  fpbuf[0] = '\0';
  fpbuf[40] = '\0'; /* Ensure the second printed line is null terminated */
  tls_fingerprint(GNUTLS_DIG_SHA256, fpbuf, sizeof(fpbuf), certdata);
  fpbuf[39] = '\0'; /* Divide into two lines of output */
  snprintf(menu->dialog[row++], dialog_row_len, "%s%s", _("SHA256 Fingerprint: "), fpbuf);
  snprintf(menu->dialog[row++], dialog_row_len, "%*s%s",
           (int) mutt_str_strlen(_("SHA256 Fingerprint: ")), "", fpbuf + 40);

  if (certerr & CERTERR_NOTYETVALID)
  {
    row++;
    mutt_str_strfcpy(menu->dialog[row],
                     _("WARNING: Server certificate is not yet valid"), dialog_row_len);
  }
  if (certerr & CERTERR_EXPIRED)
  {
    row++;
    mutt_str_strfcpy(menu->dialog[row],
                     _("WARNING: Server certificate has expired"), dialog_row_len);
  }
  if (certerr & CERTERR_REVOKED)
  {
    row++;
    mutt_str_strfcpy(menu->dialog[row],
                     _("WARNING: Server certificate has been revoked"), dialog_row_len);
  }
  if (certerr & CERTERR_HOSTNAME)
  {
    row++;
    mutt_str_strfcpy(menu->dialog[row],
                     _("WARNING: Server hostname does not match certificate"),
                     dialog_row_len);
  }
  if (certerr & CERTERR_SIGNERNOTCA)
  {
    row++;
    mutt_str_strfcpy(menu->dialog[row],
                     _("WARNING: Signer of server certificate is not a CA"), dialog_row_len);
  }

  snprintf(title, sizeof(title),
           _("SSL Certificate check (certificate %zu of %zu in chain)"), len - idx, len);
  menu->title = title;
  /* certificates with bad dates, or that are revoked, must be
   * accepted manually each and every time */
  if (C_CertificateFile && !savedcert &&
      !(certerr & (CERTERR_EXPIRED | CERTERR_NOTYETVALID | CERTERR_REVOKED)))
  {
    menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always");
    /* L10N: These three letters correspond to the choices in the string:
       (r)eject, accept (o)nce, (a)ccept always.
       This is an interactive certificate confirmation prompt for
       a GNUTLS connection. */
    menu->keys = _("roa");
  }
  else
  {
    menu->prompt = _("(r)eject, accept (o)nce");
    /* L10N: These two letters correspond to the choices in the string:
       (r)eject, accept (o)nce.
       These is an interactive certificate confirmation prompt for
       a GNUTLS connection. */
    menu->keys = _("ro");
  }

  helpstr[0] = '\0';
  mutt_make_help(buf, sizeof(buf), _("Exit  "), MENU_GENERIC, OP_EXIT);
  mutt_str_strcat(helpstr, sizeof(helpstr), buf);
  mutt_make_help(buf, sizeof(buf), _("Help"), MENU_GENERIC, OP_HELP);
  mutt_str_strcat(helpstr, sizeof(helpstr), buf);
  menu->help = helpstr;

  done = 0;
  OptIgnoreMacroEvents = true;
  while (done == 0)
  {
    switch (mutt_menu_loop(menu))
    {
      case -1:         /* abort */
      case OP_MAX + 1: /* reject */
      case OP_EXIT:
        done = 1;
        break;
      case OP_MAX + 3: /* accept always */
        done = 0;
        fp = mutt_file_fopen(C_CertificateFile, "a");
        if (fp)
        {
          /* save hostname if necessary */
          if (certerr & CERTERR_HOSTNAME)
          {
            fpbuf[0] = '\0';
            tls_fingerprint(GNUTLS_DIG_MD5, fpbuf, sizeof(fpbuf), certdata);
            fprintf(fp, "#H %s %s\n", hostname, fpbuf);
            done = 1;
          }
          /* Save the cert for all other errors */
          if (certerr ^ CERTERR_HOSTNAME)
          {
            done = 0;
            ret = gnutls_pem_base64_encode_alloc("CERTIFICATE", certdata, &pemdata);
            if (ret == 0)
            {
              if (fwrite(pemdata.data, pemdata.size, 1, fp) == 1)
              {
                done = 1;
              }
              gnutls_free(pemdata.data);
            }
          }
          mutt_file_fclose(&fp);
        }
        if (done == 0)
        {
          mutt_error(_("Warning: Couldn't save certificate"));
        }
        else
        {
          mutt_message(_("Certificate saved"));
          mutt_sleep(0);
        }
      /* fallthrough */
      case OP_MAX + 2: /* accept once */
        done = 2;
        break;
    }
  }
  OptIgnoreMacroEvents = false;
  mutt_menu_pop_current(menu);
  mutt_menu_destroy(&menu);
  gnutls_x509_crt_deinit(cert);

  return done == 2;
}
예제 #19
0
파일: mbox.c 프로젝트: 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;
}
예제 #20
0
파일: crypt.c 프로젝트: darnir/neomutt
/**
 * crypt_extract_keys_from_messages - Extract keys from a message
 * @param el List of Emails to process
 *
 * The extracted keys will be added to the user's keyring.
 */
void crypt_extract_keys_from_messages(struct EmailList *el)
{
  char tempfname[PATH_MAX], *mbox = NULL;
  struct Address *tmp = NULL;

  if (!WithCrypto)
    return;

  mutt_mktemp(tempfname, sizeof(tempfname));
  FILE *fp_out = mutt_file_fopen(tempfname, "w");
  if (!fp_out)
  {
    mutt_perror(tempfname);
    return;
  }

  if (WithCrypto & APPLICATION_PGP)
    OptDontHandlePgpKeys = true;

  struct EmailNode *en = NULL;
  STAILQ_FOREACH(en, el, entries)
  {
    struct Email *e = en->email;

    mutt_parse_mime_message(Context->mailbox, e);
    if (e->security & SEC_ENCRYPT && !crypt_valid_passphrase(e->security))
    {
      mutt_file_fclose(&fp_out);
      break;
    }

    if (((WithCrypto & APPLICATION_PGP) != 0) && (e->security & APPLICATION_PGP))
    {
      mutt_copy_message_ctx(fp_out, Context->mailbox, e,
                            MUTT_CM_DECODE | MUTT_CM_CHARCONV, 0);
      fflush(fp_out);

      mutt_endwin();
      puts(_("Trying to extract PGP keys...\n"));
      crypt_pgp_invoke_import(tempfname);
    }

    if (((WithCrypto & APPLICATION_SMIME) != 0) && (e->security & APPLICATION_SMIME))
    {
      if (e->security & SEC_ENCRYPT)
      {
        mutt_copy_message_ctx(fp_out, Context->mailbox, e,
                              MUTT_CM_NOHEADER | MUTT_CM_DECODE_CRYPT | MUTT_CM_DECODE_SMIME,
                              0);
      }
      else
        mutt_copy_message_ctx(fp_out, Context->mailbox, e, 0, 0);
      fflush(fp_out);

      if (e->env->from)
        tmp = mutt_expand_aliases(e->env->from);
      else if (e->env->sender)
        tmp = mutt_expand_aliases(e->env->sender);
      mbox = tmp ? tmp->mailbox : NULL;
      if (mbox)
      {
        mutt_endwin();
        puts(_("Trying to extract S/MIME certificates..."));
        crypt_smime_invoke_import(tempfname, mbox);
        tmp = NULL;
      }
    }

    rewind(fp_out);
  }

  mutt_file_fclose(&fp_out);
  if (isendwin())
    mutt_any_key_to_continue(NULL);

  mutt_file_unlink(tempfname);

  if (WithCrypto & APPLICATION_PGP)
    OptDontHandlePgpKeys = false;
}
예제 #21
0
파일: ssl_gnutls.c 프로젝트: darnir/neomutt
/**
 * tls_compare_certificates - Compare certificates against #C_CertificateFile
 * @param peercert Certificate
 * @retval 1 Certificate matches file
 * @retval 0 Error, or no match
 */
static int tls_compare_certificates(const gnutls_datum_t *peercert)
{
  gnutls_datum_t cert;
  unsigned char *ptr = NULL;
  gnutls_datum_t b64_data;
  unsigned char *b64_data_data = NULL;
  struct stat filestat;

  if (stat(C_CertificateFile, &filestat) == -1)
    return 0;

  b64_data.size = filestat.st_size + 1;
  b64_data_data = mutt_mem_calloc(1, b64_data.size);
  b64_data_data[b64_data.size - 1] = '\0';
  b64_data.data = b64_data_data;

  FILE *fp = fopen(C_CertificateFile, "r");
  if (!fp)
    return 0;

  b64_data.size = fread(b64_data.data, 1, b64_data.size, fp);
  mutt_file_fclose(&fp);

  do
  {
    const int ret = gnutls_pem_base64_decode_alloc(NULL, &b64_data, &cert);
    if (ret != 0)
    {
      FREE(&b64_data_data);
      return 0;
    }

    /* find start of cert, skipping junk */
    ptr = (unsigned char *) strstr((char *) b64_data.data, CERT_SEP);
    if (!ptr)
    {
      gnutls_free(cert.data);
      FREE(&b64_data_data);
      return 0;
    }
    /* find start of next cert */
    ptr = (unsigned char *) strstr((char *) ptr + 1, CERT_SEP);

    b64_data.size = b64_data.size - (ptr - b64_data.data);
    b64_data.data = ptr;

    if (cert.size == peercert->size)
    {
      if (memcmp(cert.data, peercert->data, cert.size) == 0)
      {
        /* match found */
        gnutls_free(cert.data);
        FREE(&b64_data_data);
        return 1;
      }
    }

    gnutls_free(cert.data);
  } while (ptr);

  /* no match found */
  FREE(&b64_data_data);
  return 0;
}
예제 #22
0
파일: mbox.c 프로젝트: kdave/neomutt
/**
 * 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;
}
예제 #23
0
파일: mh.c 프로젝트: kdave/neomutt
/**
 * mh_update_sequences - Update sequence numbers
 * @param m Mailbox
 *
 * XXX we don't currently remove deleted messages from sequences we don't know.
 * Should we?
 */
void mh_update_sequences(struct Mailbox *m)
{
  char sequences[PATH_MAX];
  char *tmpfname = NULL;
  char *buf = NULL;
  char *p = NULL;
  size_t s;
  int l = 0;
  int i;

  int unseen = 0;
  int flagged = 0;
  int replied = 0;

  char seq_unseen[256];
  char seq_replied[256];
  char seq_flagged[256];

  struct MhSequences mhs = { 0 };

  snprintf(seq_unseen, sizeof(seq_unseen), "%s:", NONULL(C_MhSeqUnseen));
  snprintf(seq_replied, sizeof(seq_replied), "%s:", NONULL(C_MhSeqReplied));
  snprintf(seq_flagged, sizeof(seq_flagged), "%s:", NONULL(C_MhSeqFlagged));

  FILE *fp_new = NULL;
  if (mh_mkstemp(m, &fp_new, &tmpfname) != 0)
  {
    /* error message? */
    return;
  }

  snprintf(sequences, sizeof(sequences), "%s/.mh_sequences", m->path);

  /* first, copy unknown sequences */
  FILE *fp_old = fopen(sequences, "r");
  if (fp_old)
  {
    while ((buf = mutt_file_read_line(buf, &s, fp_old, &l, 0)))
    {
      if (mutt_str_startswith(buf, seq_unseen, CASE_MATCH) ||
          mutt_str_startswith(buf, seq_flagged, CASE_MATCH) ||
          mutt_str_startswith(buf, seq_replied, CASE_MATCH))
        continue;

      fprintf(fp_new, "%s\n", buf);
    }
  }
  mutt_file_fclose(&fp_old);

  /* now, update our unseen, flagged, and replied sequences */
  for (l = 0; l < m->msg_count; l++)
  {
    if (m->emails[l]->deleted)
      continue;

    p = strrchr(m->emails[l]->path, '/');
    if (p)
      p++;
    else
      p = m->emails[l]->path;

    if (mutt_str_atoi(p, &i) < 0)
      continue;

    if (!m->emails[l]->read)
    {
      mhs_set(&mhs, i, MH_SEQ_UNSEEN);
      unseen++;
    }
    if (m->emails[l]->flagged)
    {
      mhs_set(&mhs, i, MH_SEQ_FLAGGED);
      flagged++;
    }
    if (m->emails[l]->replied)
    {
      mhs_set(&mhs, i, MH_SEQ_REPLIED);
      replied++;
    }
  }

  /* write out the new sequences */
  if (unseen)
    mhs_write_one_sequence(fp_new, &mhs, MH_SEQ_UNSEEN, NONULL(C_MhSeqUnseen));
  if (flagged)
    mhs_write_one_sequence(fp_new, &mhs, MH_SEQ_FLAGGED, NONULL(C_MhSeqFlagged));
  if (replied)
    mhs_write_one_sequence(fp_new, &mhs, MH_SEQ_REPLIED, NONULL(C_MhSeqReplied));

  mhs_free_sequences(&mhs);

  /* try to commit the changes - no guarantee here */
  mutt_file_fclose(&fp_new);

  unlink(sequences);
  if (mutt_file_safe_rename(tmpfname, sequences) != 0)
  {
    /* report an error? */
    unlink(tmpfname);
  }

  FREE(&tmpfname);
}
예제 #24
0
파일: mh.c 프로젝트: kdave/neomutt
/**
 * mh_mbox_check - Implements MxOps::mbox_check()
 *
 * This function handles arrival of new mail and reopening of mh/maildir
 * folders. Things are getting rather complex because we don't have a
 * well-defined "mailbox order", so the tricks from mbox.c and mx.c won't work
 * here.
 *
 * Don't change this code unless you _really_ understand what happens.
 */
int mh_mbox_check(struct Mailbox *m, int *index_hint)
{
  if (!m)
    return -1;

  char buf[PATH_MAX];
  struct stat st, st_cur;
  bool modified = false, occult = false, flags_changed = false;
  int num_new = 0;
  struct Maildir *md = NULL, *p = NULL;
  struct Maildir **last = NULL;
  struct MhSequences mhs = { 0 };
  int count = 0;
  struct Hash *fnames = NULL;
  struct MaildirMboxData *mdata = maildir_mdata_get(m);

  if (!C_CheckNew)
    return 0;

  mutt_str_strfcpy(buf, m->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", m->path);
  int i = stat(buf, &st_cur);
  if ((i == -1) && (errno == ENOENT))
  {
    char *tmp = NULL;
    FILE *fp = NULL;

    if (mh_mkstemp(m, &fp, &tmp) == 0)
    {
      mutt_file_fclose(&fp);
      if (mutt_file_safe_rename(tmp, buf) == -1)
        unlink(tmp);
      FREE(&tmp);
    }
  }

  if ((i == -1) && (stat(buf, &st_cur) == -1))
    modified = true;

  if ((mutt_file_stat_timespec_compare(&st, MUTT_STAT_MTIME, &m->mtime) > 0) ||
      (mutt_file_stat_timespec_compare(&st_cur, MUTT_STAT_MTIME, &mdata->mtime_cur) > 0))
  {
    modified = true;
  }

  if (!modified)
    return 0;

    /* Update the modification times on the mailbox.
     *
     * The monitor code notices changes in the open mailbox too quickly.
     * In practice, this sometimes leads to all the new messages not being
     * noticed during the SAME group of mtime stat updates.  To work around
     * the problem, don't update the stat times for a monitor caused check. */
#ifdef USE_INOTIFY
  if (MonitorContextChanged)
    MonitorContextChanged = 0;
  else
#endif
  {
    mutt_file_get_stat_timespec(&mdata->mtime_cur, &st_cur, MUTT_STAT_MTIME);
    mutt_file_get_stat_timespec(&m->mtime, &st, MUTT_STAT_MTIME);
  }

  md = NULL;
  last = &md;

  maildir_parse_dir(m, &last, NULL, &count, NULL);
  maildir_delayed_parsing(m, &md, NULL);

  if (mh_read_sequences(&mhs, m->path) < 0)
    return -1;
  mh_update_maildir(md, &mhs);
  mhs_free_sequences(&mhs);

  /* check for modifications and adjust flags */
  fnames = mutt_hash_new(count, MUTT_HASH_NO_FLAGS);

  for (p = md; p; p = p->next)
  {
    /* the hash key must survive past the header, which is freed below. */
    p->canon_fname = mutt_str_strdup(p->email->path);
    mutt_hash_insert(fnames, p->canon_fname, p);
  }

  for (i = 0; i < m->msg_count; i++)
  {
    m->emails[i]->active = false;

    p = mutt_hash_find(fnames, m->emails[i]->path);
    if (p && p->email && mutt_email_cmp_strict(m->emails[i], p->email))
    {
      m->emails[i]->active = true;
      /* found the right message */
      if (!m->emails[i]->changed)
        if (maildir_update_flags(m, m->emails[i], p->email))
          flags_changed = true;

      mutt_email_free(&p->email);
    }
    else /* message has disappeared */
      occult = true;
  }

  /* destroy the file name hash */

  mutt_hash_free(&fnames);

  /* If we didn't just get new mail, update the tables. */
  if (occult)
    mutt_mailbox_changed(m, MBN_RESORT);

  /* Incorporate new messages */
  num_new = maildir_move_to_mailbox(m, &md);
  if (num_new > 0)
  {
    mutt_mailbox_changed(m, MBN_INVALID);
    m->changed = true;
  }

  if (occult)
    return MUTT_REOPENED;
  if (num_new > 0)
    return MUTT_NEW_MAIL;
  if (flags_changed)
    return MUTT_FLAGS;
  return 0;
}