Exemple #1
0
/**
 * mutt_is_multipart_signed - Is a message signed?
 * @param b Body of email
 * @retval num Message is signed, see #SecurityFlags
 * @retval   0 Message is not signed (#SEC_NO_FLAGS)
 */
SecurityFlags mutt_is_multipart_signed(struct Body *b)
{
  if (!b || (b->type != TYPE_MULTIPART) || !b->subtype ||
      (mutt_str_strcasecmp(b->subtype, "signed") != 0))
  {
    return SEC_NO_FLAGS;
  }

  char *p = mutt_param_get(&b->parameter, "protocol");
  if (!p)
    return SEC_NO_FLAGS;

  if (!(mutt_str_strcasecmp(p, "multipart/mixed") != 0))
    return SEC_SIGN;

  if (((WithCrypto & APPLICATION_PGP) != 0) &&
      !(mutt_str_strcasecmp(p, "application/pgp-signature") != 0))
  {
    return PGP_SIGN;
  }

  if (((WithCrypto & APPLICATION_SMIME) != 0) &&
      !(mutt_str_strcasecmp(p, "application/x-pkcs7-signature") != 0))
  {
    return SMIME_SIGN;
  }
  if (((WithCrypto & APPLICATION_SMIME) != 0) &&
      !(mutt_str_strcasecmp(p, "application/pkcs7-signature") != 0))
  {
    return SMIME_SIGN;
  }

  return SEC_NO_FLAGS;
}
Exemple #2
0
/**
 * alias_sort_address - Compare two Addresses
 * @param a First  Address to compare
 * @param b Second Address to compare
 * @retval -1 a precedes b
 * @retval  0 a and b are identical
 * @retval  1 b precedes a
 */
static int alias_sort_address(const void *a, const void *b)
{
  struct Address *pa = (*(struct Alias **) a)->addr;
  struct Address *pb = (*(struct Alias **) b)->addr;
  int r;

  if (pa == pb)
    r = 0;
  else if (!pa)
    r = -1;
  else if (!pb)
    r = 1;
  else if (pa->personal)
  {
    if (pb->personal)
      r = mutt_str_strcasecmp(pa->personal, pb->personal);
    else
      r = 1;
  }
  else if (pb->personal)
    r = -1;
  else
    r = mutt_str_strcasecmp(pa->mailbox, pb->mailbox);
  return RSORT(r);
}
Exemple #3
0
/**
 * mutt_regexlist_add - Compile a regex string and add it to a list
 * @param rl    RegexList to add to
 * @param str   String to compile into a regex
 * @param flags Flags, e.g. REG_ICASE
 * @param err   Buffer for error messages
 * @retval 0  Success, Regex compiled and added to the list
 * @retval -1 Error, see message in 'err'
 */
int mutt_regexlist_add(struct RegexList *rl, const char *str, int flags, struct Buffer *err)
{
  if (!str || !*str)
    return 0;

  struct Regex *rx = mutt_regex_compile(str, flags);
  if (!rx)
  {
    mutt_buffer_printf(err, "Bad regex: %s\n", str);
    return -1;
  }

  /* check to make sure the item is not already on this rl */
  struct RegexListNode *np = NULL;
  STAILQ_FOREACH(np, rl, entries)
  {
    if (mutt_str_strcasecmp(rx->pattern, np->regex->pattern) == 0)
      break; /* already on the rl */
  }

  if (np)
  {
    mutt_regex_free(&rx);
  }
  else
  {
    np = mutt_regexlist_new();
    np->regex = rx;
    STAILQ_INSERT_TAIL(rl, np, entries);
  }

  return 0;
}
Exemple #4
0
/**
 * crypt_convert_to_7bit - Convert an email to 7bit encoding
 * @param a Body of email to convert
 */
void crypt_convert_to_7bit(struct Body *a)
{
  if (!WithCrypto)
    return;

  while (a)
  {
    if (a->type == TYPE_MULTIPART)
    {
      if (a->encoding != ENC_7BIT)
      {
        a->encoding = ENC_7BIT;
        crypt_convert_to_7bit(a->parts);
      }
      else if (((WithCrypto & APPLICATION_PGP) != 0) && C_PgpStrictEnc)
        crypt_convert_to_7bit(a->parts);
    }
    else if ((a->type == TYPE_MESSAGE) &&
             (mutt_str_strcasecmp(a->subtype, "delivery-status") != 0))
    {
      if (a->encoding != ENC_7BIT)
        mutt_message_to_7bit(a, NULL);
    }
    else if (a->encoding == ENC_8BIT)
      a->encoding = ENC_QUOTED_PRINTABLE;
    else if (a->encoding == ENC_BINARY)
      a->encoding = ENC_BASE64;
    else if (a->content && (a->encoding != ENC_BASE64) &&
             (a->content->from || (a->content->space && C_PgpStrictEnc)))
    {
      a->encoding = ENC_QUOTED_PRINTABLE;
    }
    a = a->next;
  }
}
Exemple #5
0
/**
 * mutt_is_multipart_encrypted - Does the message have encrypted parts?
 * @param b Body of email
 * @retval num Message has got encrypted parts, see #SecurityFlags
 * @retval   0 Message hasn't got encrypted parts (#SEC_NO_FLAGS)
 */
SecurityFlags mutt_is_multipart_encrypted(struct Body *b)
{
  if ((WithCrypto & APPLICATION_PGP) == 0)
    return SEC_NO_FLAGS;

  char *p = NULL;

  if (!b || (b->type != TYPE_MULTIPART) || !b->subtype ||
      (mutt_str_strcasecmp(b->subtype, "encrypted") != 0) ||
      !(p = mutt_param_get(&b->parameter, "protocol")) ||
      (mutt_str_strcasecmp(p, "application/pgp-encrypted") != 0))
  {
    return SEC_NO_FLAGS;
  }

  return PGP_ENCRYPT;
}
Exemple #6
0
/**
 * alias_sort_alias - Compare two Aliases
 * @param a First  Alias to compare
 * @param b Second Alias to compare
 * @retval -1 a precedes b
 * @retval  0 a and b are identical
 * @retval  1 b precedes a
 */
static int alias_sort_alias(const void *a, const void *b)
{
  struct Alias *pa = *(struct Alias **) a;
  struct Alias *pb = *(struct Alias **) b;
  int r = mutt_str_strcasecmp(pa->name, pb->name);

  return RSORT(r);
}
Exemple #7
0
/**
 * elem_list_sort - Sort two HashElem pointers to config
 * @param a First HashElem
 * @param b Second HashElem
 * @retval -1 a precedes b
 * @retval  0 a and b are identical
 * @retval  1 b precedes a
 */
int elem_list_sort(const void *a, const void *b)
{
  if (!a || !b)
    return 0;

  const struct HashElem *hea = *(struct HashElem **) a;
  const struct HashElem *heb = *(struct HashElem **) b;

  return mutt_str_strcasecmp(hea->key.strkey, heb->key.strkey);
}
Exemple #8
0
/**
 * mutt_is_application_pgp - Does the message use PGP?
 * @param m Body of email
 * @retval >0 Message uses PGP, e.g. #PGP_ENCRYPT
 * @retval  0 Message doesn't use PGP, (#SEC_NO_FLAGS)
 */
SecurityFlags mutt_is_application_pgp(struct Body *m)
{
  SecurityFlags t = SEC_NO_FLAGS;
  char *p = NULL;

  if (m->type == TYPE_APPLICATION)
  {
    if ((mutt_str_strcasecmp(m->subtype, "pgp") == 0) ||
        (mutt_str_strcasecmp(m->subtype, "x-pgp-message") == 0))
    {
      p = mutt_param_get(&m->parameter, "x-action");
      if (p && ((mutt_str_strcasecmp(p, "sign") == 0) ||
                (mutt_str_strcasecmp(p, "signclear") == 0)))
      {
        t |= PGP_SIGN;
      }

      p = mutt_param_get(&m->parameter, "format");
      if (p && (mutt_str_strcasecmp(p, "keys-only") == 0))
      {
        t |= PGP_KEY;
      }

      if (!t)
        t |= PGP_ENCRYPT; /* not necessarily correct, but... */
    }

    if (mutt_str_strcasecmp(m->subtype, "pgp-signed") == 0)
      t |= PGP_SIGN;

    if (mutt_str_strcasecmp(m->subtype, "pgp-keys") == 0)
      t |= PGP_KEY;
  }
  else if ((m->type == TYPE_TEXT) && (mutt_str_strcasecmp("plain", m->subtype) == 0))
  {
    if (((p = mutt_param_get(&m->parameter, "x-mutt-action")) ||
         (p = mutt_param_get(&m->parameter, "x-action")) ||
         (p = mutt_param_get(&m->parameter, "action"))) &&
        mutt_str_startswith(p, "pgp-sign", CASE_IGNORE))
    {
      t |= PGP_SIGN;
    }
    else if (p && mutt_str_startswith(p, "pgp-encrypt", CASE_IGNORE))
      t |= PGP_ENCRYPT;
    else if (p && mutt_str_startswith(p, "pgp-keys", CASE_IGNORE))
      t |= PGP_KEY;
  }
  if (t)
    t |= PGP_INLINE;

  return t;
}
Exemple #9
0
/**
 * mutt_is_valid_multipart_pgp_encrypted - Is this a valid multi-part encrypted message?
 * @param b Body of email
 * @retval >0 Message is valid, with encrypted parts, e.g. #PGP_ENCRYPT
 * @retval  0 Message hasn't got encrypted parts
 */
int mutt_is_valid_multipart_pgp_encrypted(struct Body *b)
{
  if (mutt_is_multipart_encrypted(b) == SEC_NO_FLAGS)
    return 0;

  b = b->parts;
  if (!b || (b->type != TYPE_APPLICATION) || !b->subtype ||
      (mutt_str_strcasecmp(b->subtype, "pgp-encrypted") != 0))
  {
    return 0;
  }

  b = b->next;
  if (!b || (b->type != TYPE_APPLICATION) || !b->subtype ||
      (mutt_str_strcasecmp(b->subtype, "octet-stream") != 0))
  {
    return 0;
  }

  return PGP_ENCRYPT;
}
Exemple #10
0
/**
 * mutt_is_malformed_multipart_pgp_encrypted - Check for malformed layout
 * @param b Body of email
 * @retval num Success, see #SecurityFlags
 * @retval   0 Error, (#SEC_NO_FLAGS)
 *
 * This checks for the malformed layout caused by MS Exchange in
 * some cases:
 * ```
 *  <multipart/mixed>
 *     <text/plain>
 *     <application/pgp-encrypted> [BASE64-encoded]
 *     <application/octet-stream> [BASE64-encoded]
 * ```
 * See ticket #3742
 */
SecurityFlags mutt_is_malformed_multipart_pgp_encrypted(struct Body *b)
{
  if (!(WithCrypto & APPLICATION_PGP))
    return SEC_NO_FLAGS;

  if (!b || (b->type != TYPE_MULTIPART) || !b->subtype ||
      (mutt_str_strcasecmp(b->subtype, "mixed") != 0))
  {
    return SEC_NO_FLAGS;
  }

  b = b->parts;
  if (!b || (b->type != TYPE_TEXT) || !b->subtype ||
      (mutt_str_strcasecmp(b->subtype, "plain") != 0) || (b->length != 0))
  {
    return SEC_NO_FLAGS;
  }

  b = b->next;
  if (!b || (b->type != TYPE_APPLICATION) || !b->subtype ||
      (mutt_str_strcasecmp(b->subtype, "pgp-encrypted") != 0))
  {
    return SEC_NO_FLAGS;
  }

  b = b->next;
  if (!b || (b->type != TYPE_APPLICATION) || !b->subtype ||
      (mutt_str_strcasecmp(b->subtype, "octet-stream") != 0))
  {
    return SEC_NO_FLAGS;
  }

  b = b->next;
  if (b)
    return SEC_NO_FLAGS;

  return PGP_ENCRYPT;
}
Exemple #11
0
/**
 * 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;
}
Exemple #12
0
/**
 * mutt_is_application_smime - Does the message use S/MIME?
 * @param m Body of email
 * @retval >0 Message uses S/MIME, e.g. #SMIME_ENCRYPT
 * @retval  0 Message doesn't use S/MIME, (#SEC_NO_FLAGS)
 */
SecurityFlags mutt_is_application_smime(struct Body *m)
{
  if (!m)
    return SEC_NO_FLAGS;

  if (((m->type & TYPE_APPLICATION) == 0) || !m->subtype)
    return SEC_NO_FLAGS;

  char *t = NULL;
  bool complain = false;
  /* S/MIME MIME types don't need x- anymore, see RFC2311 */
  if ((mutt_str_strcasecmp(m->subtype, "x-pkcs7-mime") == 0) ||
      (mutt_str_strcasecmp(m->subtype, "pkcs7-mime") == 0))
  {
    t = mutt_param_get(&m->parameter, "smime-type");
    if (t)
    {
      if (mutt_str_strcasecmp(t, "enveloped-data") == 0)
        return SMIME_ENCRYPT;
      else if (mutt_str_strcasecmp(t, "signed-data") == 0)
        return SMIME_SIGN | SMIME_OPAQUE;
      else
        return SEC_NO_FLAGS;
    }
    /* Netscape 4.7 uses
     * Content-Description: S/MIME Encrypted Message
     * instead of Content-Type parameter */
    if (mutt_str_strcasecmp(m->description, "S/MIME Encrypted Message") == 0)
      return SMIME_ENCRYPT;
    complain = true;
  }
  else if (mutt_str_strcasecmp(m->subtype, "octet-stream") != 0)
    return SEC_NO_FLAGS;

  t = mutt_param_get(&m->parameter, "name");

  if (!t)
    t = m->d_filename;
  if (!t)
    t = m->filename;
  if (!t)
  {
    if (complain)
    {
      mutt_message(
          _("S/MIME messages with no hints on content are unsupported"));
    }
    return SEC_NO_FLAGS;
  }

  /* no .p7c, .p10 support yet. */

  int len = mutt_str_strlen(t) - 4;
  if ((len > 0) && (*(t + len) == '.'))
  {
    len++;
    if (mutt_str_strcasecmp((t + len), "p7m") == 0)
    {
      /* Not sure if this is the correct thing to do, but
       * it's required for compatibility with Outlook */
      return SMIME_SIGN | SMIME_OPAQUE;
    }
    else if (mutt_str_strcasecmp((t + len), "p7s") == 0)
      return SMIME_SIGN | SMIME_OPAQUE;
  }

  return SEC_NO_FLAGS;
}
Exemple #13
0
/**
 * mutt_protect - Encrypt and/or sign a message
 * @param msg     Header of the message
 * @param keylist List of keys to encrypt to (space-separated)
 * @retval  0 Success
 * @retval -1 Error
 */
int mutt_protect(struct Email *msg, char *keylist)
{
  struct Body *pbody = NULL, *tmp_pbody = NULL;
  struct Body *tmp_smime_pbody = NULL;
  struct Body *tmp_pgp_pbody = NULL;
  int flags = (WithCrypto & APPLICATION_PGP) ? msg->security : 0;

  if (!WithCrypto)
    return -1;

  if (!(msg->security & (SEC_ENCRYPT | SEC_SIGN)))
    return 0;

  if ((msg->security & SEC_SIGN) && !crypt_valid_passphrase(msg->security))
    return -1;

  if (((WithCrypto & APPLICATION_PGP) != 0) && ((msg->security & PGP_INLINE) == PGP_INLINE))
  {
    if ((msg->content->type != TYPE_TEXT) ||
        (mutt_str_strcasecmp(msg->content->subtype, "plain") != 0))
    {
      if (query_quadoption(C_PgpMimeAuto,
                           _("Inline PGP can't be used with attachments.  "
                             "Revert to PGP/MIME?")) != MUTT_YES)
      {
        mutt_error(
            _("Mail not sent: inline PGP can't be used with attachments"));
        return -1;
      }
    }
    else if (mutt_str_strcasecmp("flowed", mutt_param_get(&msg->content->parameter, "format")) == 0)
    {
      if ((query_quadoption(C_PgpMimeAuto,
                            _("Inline PGP can't be used with format=flowed.  "
                              "Revert to PGP/MIME?"))) != MUTT_YES)
      {
        mutt_error(
            _("Mail not sent: inline PGP can't be used with format=flowed"));
        return -1;
      }
    }
    else
    {
      /* they really want to send it inline... go for it */
      if (!isendwin())
      {
        mutt_endwin();
        puts(_("Invoking PGP..."));
      }
      pbody = crypt_pgp_traditional_encryptsign(msg->content, flags, keylist);
      if (pbody)
      {
        msg->content = pbody;
        return 0;
      }

      /* otherwise inline won't work...ask for revert */
      if (query_quadoption(
              C_PgpMimeAuto,
              _("Message can't be sent inline.  Revert to using PGP/MIME?")) != MUTT_YES)
      {
        mutt_error(_("Mail not sent"));
        return -1;
      }
    }

    /* go ahead with PGP/MIME */
  }

  if (!isendwin())
    mutt_endwin();

  if (WithCrypto & APPLICATION_SMIME)
    tmp_smime_pbody = msg->content;
  if (WithCrypto & APPLICATION_PGP)
    tmp_pgp_pbody = msg->content;

  if (C_CryptUsePka && (msg->security & SEC_SIGN))
  {
    /* Set sender (necessary for e.g. PKA).  */
    const char *mailbox = NULL;
    struct Address *from = msg->env->from;

    if (!from)
      from = mutt_default_from();

    mailbox = from->mailbox;
    if (!mailbox && C_EnvelopeFromAddress)
      mailbox = C_EnvelopeFromAddress->mailbox;

    if (((WithCrypto & APPLICATION_SMIME) != 0) && (msg->security & APPLICATION_SMIME))
      crypt_smime_set_sender(mailbox);
    else if (((WithCrypto & APPLICATION_PGP) != 0) && (msg->security & APPLICATION_PGP))
      crypt_pgp_set_sender(mailbox);

    if (!msg->env->from)
      mutt_addr_free(&from);
  }

  if (C_CryptProtectedHeadersWrite)
  {
    struct Envelope *protected_headers = mutt_env_new();
    mutt_str_replace(&protected_headers->subject, msg->env->subject);
    /* Note: if other headers get added, such as to, cc, then a call to
     * mutt_env_to_intl() will need to be added here too. */
    mutt_prepare_envelope(protected_headers, 0);

    mutt_env_free(&msg->content->mime_headers);
    msg->content->mime_headers = protected_headers;
  }

  if (msg->security & SEC_SIGN)
  {
    if (((WithCrypto & APPLICATION_SMIME) != 0) && (msg->security & APPLICATION_SMIME))
    {
      tmp_pbody = crypt_smime_sign_message(msg->content);
      if (!tmp_pbody)
        goto bail;
      pbody = tmp_pbody;
      tmp_smime_pbody = tmp_pbody;
    }

    if (((WithCrypto & APPLICATION_PGP) != 0) && (msg->security & APPLICATION_PGP) &&
        (!(flags & SEC_ENCRYPT) || C_PgpRetainableSigs))
    {
      tmp_pbody = crypt_pgp_sign_message(msg->content);
      if (!tmp_pbody)
        goto bail;

      flags &= ~SEC_SIGN;
      pbody = tmp_pbody;
      tmp_pgp_pbody = tmp_pbody;
    }

    if ((WithCrypto != 0) && (msg->security & APPLICATION_SMIME) &&
        (msg->security & APPLICATION_PGP))
    {
      /* here comes the draft ;-) */
    }
  }

  if (msg->security & SEC_ENCRYPT)
  {
    if (((WithCrypto & APPLICATION_SMIME) != 0) && (msg->security & APPLICATION_SMIME))
    {
      tmp_pbody = crypt_smime_build_smime_entity(tmp_smime_pbody, keylist);
      if (!tmp_pbody)
      {
        /* signed ? free it! */
        goto bail;
      }
      /* free tmp_body if messages was signed AND encrypted ... */
      if ((tmp_smime_pbody != msg->content) && (tmp_smime_pbody != tmp_pbody))
      {
        /* detach and don't delete msg->content,
         * which tmp_smime_pbody->parts after signing. */
        tmp_smime_pbody->parts = tmp_smime_pbody->parts->next;
        msg->content->next = NULL;
        mutt_body_free(&tmp_smime_pbody);
      }
      pbody = tmp_pbody;
    }

    if (((WithCrypto & APPLICATION_PGP) != 0) && (msg->security & APPLICATION_PGP))
    {
      pbody = crypt_pgp_encrypt_message(tmp_pgp_pbody, keylist, (flags & SEC_SIGN));
      if (!pbody)
      {
        /* did we perform a retainable signature? */
        if (flags != msg->security)
        {
          /* remove the outer multipart layer */
          tmp_pgp_pbody = mutt_remove_multipart(tmp_pgp_pbody);
          /* get rid of the signature */
          mutt_body_free(&tmp_pgp_pbody->next);
        }

        goto bail;
      }

      /* destroy temporary signature envelope when doing retainable
       * signatures.

       */
      if (flags != msg->security)
      {
        tmp_pgp_pbody = mutt_remove_multipart(tmp_pgp_pbody);
        mutt_body_free(&tmp_pgp_pbody->next);
      }
    }
  }

  if (pbody)
  {
    msg->content = pbody;
    return 0;
  }

bail:
  mutt_env_free(&msg->content->mime_headers);
  return -1;
}
Exemple #14
0
/**
 * mutt_signed_handler - Verify a "multipart/signed" body - Implements ::handler_t
 */
int mutt_signed_handler(struct Body *a, struct State *s)
{
  bool inconsistent = false;
  struct Body *b = a;
  struct Body **signatures = NULL;
  int sigcnt = 0;
  int rc = 0;

  if (!WithCrypto)
    return -1;

  a = a->parts;
  SecurityFlags signed_type = mutt_is_multipart_signed(b);
  if (signed_type == SEC_NO_FLAGS)
  {
    /* A null protocol value is already checked for in mutt_body_handler() */
    state_printf(s,
                 _("[-- Error: "
                   "Unknown multipart/signed protocol %s --]\n\n"),
                 mutt_param_get(&b->parameter, "protocol"));
    return mutt_body_handler(a, s);
  }

  if (!(a && a->next))
    inconsistent = true;
  else
  {
    switch (signed_type)
    {
      case SEC_SIGN:
        if ((a->next->type != TYPE_MULTIPART) ||
            (mutt_str_strcasecmp(a->next->subtype, "mixed") != 0))
        {
          inconsistent = true;
        }
        break;
      case PGP_SIGN:
        if ((a->next->type != TYPE_APPLICATION) ||
            (mutt_str_strcasecmp(a->next->subtype, "pgp-signature") != 0))
        {
          inconsistent = true;
        }
        break;
      case SMIME_SIGN:
        if ((a->next->type != TYPE_APPLICATION) ||
            ((mutt_str_strcasecmp(a->next->subtype, "x-pkcs7-signature") != 0) &&
             (mutt_str_strcasecmp(a->next->subtype, "pkcs7-signature") != 0)))
        {
          inconsistent = true;
        }
        break;
      default:
        inconsistent = true;
    }
  }
  if (inconsistent)
  {
    state_attach_puts(_("[-- Error: "
                        "Missing or bad-format multipart/signed signature"
                        " --]\n\n"),
                      s);
    return mutt_body_handler(a, s);
  }

  if (s->flags & MUTT_DISPLAY)
  {
    crypt_fetch_signatures(&signatures, a->next, &sigcnt);

    if (sigcnt)
    {
      char tempfile[PATH_MAX];
      mutt_mktemp(tempfile, sizeof(tempfile));
      bool goodsig = true;
      if (crypt_write_signed(a, s, tempfile) == 0)
      {
        for (int i = 0; i < sigcnt; i++)
        {
          if (((WithCrypto & APPLICATION_PGP) != 0) &&
              (signatures[i]->type == TYPE_APPLICATION) &&
              (mutt_str_strcasecmp(signatures[i]->subtype, "pgp-signature") == 0))
          {
            if (crypt_pgp_verify_one(signatures[i], s, tempfile) != 0)
              goodsig = false;

            continue;
          }

          if (((WithCrypto & APPLICATION_SMIME) != 0) &&
              (signatures[i]->type == TYPE_APPLICATION) &&
              ((mutt_str_strcasecmp(signatures[i]->subtype,
                                    "x-pkcs7-signature") == 0) ||
               (mutt_str_strcasecmp(signatures[i]->subtype,
                                    "pkcs7-signature") == 0)))
          {
            if (crypt_smime_verify_one(signatures[i], s, tempfile) != 0)
              goodsig = false;

            continue;
          }

          state_printf(s,
                       _("[-- Warning: "
                         "We can't verify %s/%s signatures. --]\n\n"),
                       TYPE(signatures[i]), signatures[i]->subtype);
        }
      }

      mutt_file_unlink(tempfile);

      b->goodsig = goodsig;
      b->badsig = !goodsig;

      /* Now display the signed body */
      state_attach_puts(_("[-- The following data is signed --]\n\n"), s);

      mutt_protected_headers_handler(a, s);

      FREE(&signatures);
    }
    else
      state_attach_puts(_("[-- Warning: Can't find any signatures. --]\n\n"), s);
  }

  rc = mutt_body_handler(a, s);

  if (s->flags & MUTT_DISPLAY && sigcnt)
    state_attach_puts(_("\n[-- End of signed data --]\n"), s);

  return rc;
}