remove old lib.[hc]
[apps/madmutt.git] / crypt.c
diff --git a/crypt.c b/crypt.c
index 2fc0924..e137f2c 100644 (file)
--- a/crypt.c
+++ b/crypt.c
@@ -22,7 +22,6 @@
 #include <lib-mx/mx.h>
 
 #include "crypt.h"
-#include "lib.h"
 #include "alias.h"
 #include "handler.h"
 #include "copy.h"
 #define CRYPT_KV_STRONGID 8
 #define CRYPT_KV_MATCH (CRYPT_KV_ADDR|CRYPT_KV_STRING)
 
-/*
- * Type definitions.
- */
-
-struct crypt_cache {
-  char *what;
-  char *dflt;
-  struct crypt_cache *next;
-};
-
 struct dn_array_s {
-  char *key;
-  char *value;
+    char *key;
+    char *value;
 };
 
 /* We work based on user IDs, getting from a user ID to the key is
    check and does not need any memory (gpgme uses reference counting). */
-typedef struct crypt_keyinfo {
-  struct crypt_keyinfo *next;
-  gpgme_key_t kobj;
-  int idx;                      /* and the user ID at this index */
-  const char *uid;              /* and for convenience point to this user ID */
-  unsigned int flags;           /* global and per uid flags (for convenience) */
-} crypt_key_t;
+typedef struct cryptkey_t {
+    struct cryptkey_t *next;
+    int idx;                      /* and the user ID at this index */
+    int flags;                    /* global and per uid flags (for convenience) */
+    gpgme_key_t kobj;
+    const char *uid;              /* and for convenience point to this user ID */
+} cryptkey_t;
+
+DO_INIT(cryptkey_t, cryptkey);
+static void cryptkey_wipe(cryptkey_t *key) {
+    gpgme_key_release(key->kobj);
+}
+DO_NEW(cryptkey_t, cryptkey);
+DO_DELETE(cryptkey_t, cryptkey);
+DO_SLIST(cryptkey_t, key, cryptkey_delete);
+
+static cryptkey_t *cryptkey_dup(const cryptkey_t *k)
+{
+    cryptkey_t *res = cryptkey_new();
+    *res = *k;
+    res->next = NULL;
+    gpgme_key_ref(k->kobj);
+    return res;
+}
 
 typedef struct crypt_entry {
-  ssize_t num;
-  crypt_key_t *key;
+    ssize_t num;
+    cryptkey_t *key;
 } crypt_entry_t;
 
-
-static struct crypt_cache *id_defaults = NULL;
 static gpgme_key_t signature_key = NULL;
 
-/*
- * General helper functions.
- */
-
 static void convert_to_7bit (BODY * a)
 {
-  while (a) {
-    if (a->type == TYPEMULTIPART) {
-      if (a->encoding != ENC7BIT) {
-        a->encoding = ENC7BIT;
-        convert_to_7bit (a->parts);
-      } else {
-        convert_to_7bit (a->parts);
-      }
-    }
-    else if (a->type == TYPEMESSAGE &&
-             m_strcasecmp(a->subtype, "delivery-status")) {
-      if (a->encoding != ENC7BIT)
-        mutt_message_to_7bit (a, NULL);
+    for (; a; a = a->next) {
+        int tok = mime_which_token(a->subtype, -1);
+
+        if (a->type == TYPEMULTIPART) {
+            a->encoding = ENC7BIT;
+            convert_to_7bit(a->parts);
+        } else if (a->type == TYPEMESSAGE && tok == MIME_DELIVERY_STATUS) {
+            if (a->encoding != ENC7BIT)
+                mutt_message_to_7bit(a, NULL);
+        } else if (a->encoding == ENC8BIT) {
+            a->encoding = ENCQUOTEDPRINTABLE;
+        } else if (a->encoding == ENCBINARY) {
+            a->encoding = ENCBASE64;
+        } else if (a->content && a->encoding != ENCBASE64
+               && (a->content->from || a->content->space))
+        {
+            a->encoding = ENCQUOTEDPRINTABLE;
+        }
     }
-    else if (a->encoding == ENC8BIT)
-      a->encoding = ENCQUOTEDPRINTABLE;
-    else if (a->encoding == ENCBINARY)
-      a->encoding = ENCBASE64;
-    else if (a->content && a->encoding != ENCBASE64 &&
-             (a->content->from || a->content->space))
-      a->encoding = ENCQUOTEDPRINTABLE;
-    a = a->next;
-  }
 }
 
 /* Print the utf-8 encoded string BUF of length LEN bytes to stream
    FP. Convert the character set. */
 static void print_utf8 (FILE * fp, const char *buf, ssize_t len)
 {
-  char *tstr;
+    char *tstr;
 
-  tstr = p_dupstr(buf, len);
-  mutt_convert_string (&tstr, "utf-8", MCharset.charset, M_ICONV_HOOK_FROM);
-  fputs (tstr, fp);
-  p_delete(&tstr);
+    tstr = p_dupstr(buf, len);
+    mutt_convert_string(&tstr, "utf-8", MCharset.charset, M_ICONV_HOOK_FROM);
+    fputs(tstr, fp);
+    p_delete(&tstr);
 }
 
-
-/*
- * Key management.
- */
-
 /* Return the keyID for the key K.  Note that this string is valid as
    long as K is valid */
-static const char *crypt_keyid (crypt_key_t * k)
+static const char *crypt_keyid (cryptkey_t * k)
 {
-  const char *s = "????????";
-
-  if (k->kobj && k->kobj->subkeys) {
-    s = k->kobj->subkeys->keyid;
-    if ((!option (OPTPGPLONGIDS)) && (m_strlen(s) == 16))
-      /* Return only the short keyID.  */
-      s += 8;
-  }
+    if (k->kobj && k->kobj->subkeys) {
+        const char *s = k->kobj->subkeys->keyid;
+        return m_strlen(s) == 16 ? s + 8 : s;
+    }
 
-  return s;
+    return "????????";
 }
 
 /* Return the hexstring fingerprint from the key K. */
-static const char *crypt_fpr (crypt_key_t * k)
+static const char *crypt_fpr (cryptkey_t * k)
 {
-  const char *s = "";
-
-  if (k->kobj && k->kobj->subkeys)
-    s = k->kobj->subkeys->fpr;
-
-  return s;
+    return k->kobj && k->kobj->subkeys ? k->kobj->subkeys->fpr : "";
 }
 
 /* Parse FLAGS and return a statically allocated(!) string with them. */
 static char *crypt_key_abilities (int flags)
 {
-  static char buff[3];
-
-  if (!(flags & KEYFLAG_CANENCRYPT))
-    buff[0] = '-';
-  else if (flags & KEYFLAG_PREFER_SIGNING)
-    buff[0] = '.';
-  else
-    buff[0] = 'e';
+    static char buff[3] = "es";
 
-  if (!(flags & KEYFLAG_CANSIGN))
-    buff[1] = '-';
-  else if (flags & KEYFLAG_PREFER_ENCRYPTION)
-    buff[1] = '.';
-  else
-    buff[1] = 's';
+    if (!(flags & KEYFLAG_CANENCRYPT))
+        buff[0] = '-';
+    else if (flags & KEYFLAG_PREFER_SIGNING)
+        buff[0] = '.';
 
-  buff[2] = '\0';
+    if (!(flags & KEYFLAG_CANSIGN))
+        buff[1] = '-';
+    else if (flags & KEYFLAG_PREFER_ENCRYPTION)
+        buff[1] = '.';
 
-  return buff;
+    return buff;
 }
 
 /* Parse FLAGS and return a character describing the most important flag. */
-static char crypt_flags (int flags)
-{
-  if (flags & KEYFLAG_REVOKED)
-    return 'R';
-  else if (flags & KEYFLAG_EXPIRED)
-    return 'X';
-  else if (flags & KEYFLAG_DISABLED)
-    return 'd';
-  else if (flags & KEYFLAG_CRITICAL)
-    return 'c';
-  else
+static char crypt_flags(int flags)
+{
+    if (flags & KEYFLAG_REVOKED)
+        return 'R';
+    if (flags & KEYFLAG_EXPIRED)
+        return 'X';
+    if (flags & KEYFLAG_DISABLED)
+        return 'd';
+    if (flags & KEYFLAG_CRITICAL)
+        return 'c';
     return ' ';
 }
 
-/* Return a copy of KEY. */
-static crypt_key_t *crypt_copy_key (crypt_key_t *key)
-{
-  crypt_key_t *k;
-
-  k = p_new(crypt_key_t, 1);
-  k->kobj = key->kobj;
-  gpgme_key_ref (key->kobj);
-  k->idx = key->idx;
-  k->uid = key->uid;
-  k->flags = key->flags;
-
-  return k;
-}
-
-/* Release all the keys at the address of KEYLIST and set the address
-   to NULL. */
-static void crypt_free_key (crypt_key_t ** keylist)
-{
-  while (*keylist) {
-    crypt_key_t *k = (*keylist)->next;
-
-    p_delete(&k);
-    *keylist = k;
-  }
-}
-
-/* Return trute when key K is valid. */
-static int crypt_key_is_valid (crypt_key_t * k)
-{
-  if (k->flags & KEYFLAG_CANTUSE)
-    return 0;
-  return 1;
-}
-
 /* Return true whe validity of KEY is sufficient. */
-static int crypt_id_is_strong (crypt_key_t * key)
+static int crypt_id_is_strong (cryptkey_t * key)
 {
-  gpgme_validity_t val = GPGME_VALIDITY_UNKNOWN;
-  gpgme_user_id_t uid = NULL;
-  int is_strong = 0;
-  int i = 0;
-
-  if ((key->flags & KEYFLAG_ISX509))
-    return 1;
-
-  for (i = 0, uid = key->kobj->uids; (i < key->idx) && uid;
-       i++, uid = uid->next);
-  if (uid)
-    val = uid->validity;
-
-  switch (val) {
-  case GPGME_VALIDITY_UNKNOWN:
-  case GPGME_VALIDITY_UNDEFINED:
-  case GPGME_VALIDITY_NEVER:
-  case GPGME_VALIDITY_MARGINAL:
-    is_strong = 0;
-    break;
+    gpgme_user_id_t uid;
+    int i;
 
-  case GPGME_VALIDITY_FULL:
-  case GPGME_VALIDITY_ULTIMATE:
-    is_strong = 1;
-    break;
-  }
+    if (key->flags & KEYFLAG_ISX509)
+        return 1;
 
-  return is_strong;
-}
+    for (i = 0, uid = key->kobj->uids; uid; i++, uid = uid->next) {
+        if (i == key->idx) {
+            return uid->validity == GPGME_VALIDITY_FULL
+                || uid->validity == GPGME_VALIDITY_ULTIMATE;
+        }
+    }
 
-/* Return true when the KEY is valid, i.e. not marked as unusable. */
-static int crypt_id_is_valid (crypt_key_t * key)
-{
-  return !(key->flags & KEYFLAG_CANTUSE);
+    return 0;
 }
 
 /* Return a bit vector describing how well the addresses ADDR and
    U_ADDR match and whether KEY is valid. */
-static int crypt_id_matches_addr (address_t * addr, address_t * u_addr,
-                                  crypt_key_t * key)
+static int
+crypt_id_matches_addr(address_t *addr, address_t *u_addr, cryptkey_t *key)
 {
-  int rv = 0;
+    int rv = 0;
 
-  if (crypt_id_is_valid (key))
-    rv |= CRYPT_KV_VALID;
+    if (!(key->flags & KEYFLAG_CANTUSE))
+        rv |= CRYPT_KV_VALID;
 
-  if (crypt_id_is_strong (key))
-    rv |= CRYPT_KV_STRONGID;
+    if (crypt_id_is_strong(key))
+        rv |= CRYPT_KV_STRONGID;
 
-  if (addr->mailbox && u_addr->mailbox
-      && m_strcasecmp(addr->mailbox, u_addr->mailbox) == 0)
-    rv |= CRYPT_KV_ADDR;
+    if (addr->mailbox && !m_strcasecmp(addr->mailbox, u_addr->mailbox))
+        rv |= CRYPT_KV_ADDR;
 
-  if (addr->personal && u_addr->personal
-      && m_strcasecmp(addr->personal, u_addr->personal) == 0)
-    rv |= CRYPT_KV_STRING;
+    if (addr->personal && m_strcasecmp(addr->personal, u_addr->personal))
+        rv |= CRYPT_KV_STRING;
 
-  return rv;
+    return rv;
 }
 
 
-/*
- * GPGME convenient functions.
- */
-
 /* Create a new gpgme context and return it.  With FOR_SMIME set to
    true, the protocol of the context is set to CMS. */
-static gpgme_ctx_t create_gpgme_context (int for_smime)
+static gpgme_ctx_t create_gpgme_context(int for_smime)
 {
-  gpgme_error_t err;
-  gpgme_ctx_t ctx;
-
-  err = gpgme_new (&ctx);
-  if (err) {
-    mutt_error (_("error creating gpgme context: %s\n"), gpgme_strerror (err));
-    sleep (2);
-    mutt_exit (1);
-  }
+    gpgme_error_t err;
+    gpgme_ctx_t ctx;
 
-  if (for_smime) {
-    err = gpgme_set_protocol (ctx, GPGME_PROTOCOL_CMS);
+    err = gpgme_new (&ctx);
     if (err) {
-      mutt_error (_("error enabling CMS protocol: %s\n"), gpgme_strerror (err));
-      sleep (2);
-      mutt_exit (1);
+        mutt_error(_("error creating gpgme context: %s\n"),
+                   gpgme_strerror(err));
+        sleep(2);
+        mutt_exit(1);
     }
-  }
+    if (!for_smime)
+        return ctx;
 
-  return ctx;
+    err = gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS);
+    if (err) {
+        mutt_error(_("error enabling CMS protocol: %s\n"), gpgme_strerror(err));
+        sleep(2);
+        mutt_exit(1);
+    }
+    return ctx;
 }
 
 /* Create a new gpgme data object.  This is a wrapper to die on
    error. */
-static gpgme_data_t create_gpgme_data (void)
+static gpgme_data_t create_gpgme_data(void)
 {
-  gpgme_error_t err;
-  gpgme_data_t data;
+    gpgme_error_t err;
+    gpgme_data_t data;
 
-  err = gpgme_data_new (&data);
-  if (err) {
-    mutt_error (_("error creating gpgme data object: %s\n"),
-                gpgme_strerror (err));
-    sleep (2);
-    mutt_exit (1);
-  }
-  return data;
+    err = gpgme_data_new(&data);
+    if (err) {
+        mutt_error(_("error creating gpgme data object: %s\n"),
+                   gpgme_strerror(err));
+        sleep(2);
+        mutt_exit(1);
+    }
+    return data;
 }
 
 /* Create a new GPGME Data object from the mail body A.  With CONVERT
    passed as true, the lines are converted to CR,LF if required.
    Return NULL on error or the gpgme_data_t object on success. */
-static gpgme_data_t body_to_data_object (BODY * a, int convert)
+static gpgme_data_t body_to_data_object(BODY *a, int convert)
 {
-  char tempfile[_POSIX_PATH_MAX];
-  FILE *fptmp;
-  int err = 0;
-  gpgme_data_t data;
+    gpgme_data_t data;
+    FILE *fptmp;
+    int err = 0;
 
-  fptmp = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
-  if (!fptmp) {
-    mutt_perror (_("Can't create temporary file"));
-    return NULL;
-  }
+    if (!(fptmp = tmpfile())) {
+        mutt_perror (_("Can't create temporary file"));
+        return NULL;
+    }
 
-  mutt_write_mime_header (a, fptmp);
-  fputc ('\n', fptmp);
-  mutt_write_mime_body (a, fptmp);
+    mutt_write_mime_header(a, fptmp);
+    fputc('\n', fptmp);
+    mutt_write_mime_body(a, fptmp);
+    rewind(fptmp);
 
-  if (convert) {
-    int c, hadcr = 0;
-    unsigned char buf[1];
+    if (convert) {
+        char buf[STRING];
+        int spare = 0;
 
-    data = create_gpgme_data ();
-    rewind (fptmp);
-    while ((c = fgetc (fptmp)) != EOF) {
-      if (c == '\r')
-        hadcr = 1;
-      else {
-        if (c == '\n' && !hadcr) {
-          buf[0] = '\r';
-          gpgme_data_write (data, buf, 1);
-        }
+        data = create_gpgme_data();
 
-        hadcr = 0;
-      }
-      /* FIXME: This is quite suboptimal */
-      buf[0] = c;
-      gpgme_data_write (data, buf, 1);
+        while (fgets(buf + spare, sizeof(buf) - 1, fptmp)) {
+            int l = m_strlen(buf);
+
+            spare = buf[l - 1] != '\n';
+            if (!spare && (l <= 1 || buf[l - 2] != '\r')) {
+                buf[l - 1] = '\r';
+                buf[l++]   = '\n';
+            }
+            gpgme_data_write(data, buf, l - spare);
+            if (spare)
+                buf[0] = buf[l - 1];
+        }
+        if (spare)
+            gpgme_data_write(data, buf, 1);
+        gpgme_data_seek(data, 0, SEEK_SET);
+        m_fclose(&fptmp);
+        return data;
     }
-    gpgme_data_seek (data, 0, SEEK_SET);
-  } else {
-    err = gpgme_data_new_from_file (&data, tempfile, 1);
-  }
-  m_fclose(&fptmp);
-  unlink (tempfile);
-  if (err) {
-    mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
-    return NULL;
-  }
 
-  return data;
+    err = gpgme_data_new_from_stream(&data, fptmp);
+    m_fclose(&fptmp);
+    if (err) {
+        mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
+        return NULL;
+    }
+    return data;
 }
 
 /* Create a GPGME data object from the stream FP but limit the object
    to LENGTH bytes starting at OFFSET bytes from the beginning of the
    file. */
-static gpgme_data_t file_to_data_object (FILE * fp, long offset, long length)
+static gpgme_data_t file_to_data_object(FILE *fp, long offset, long length)
 {
-  int err = 0;
-  gpgme_data_t data;
+    int err = 0;
+    gpgme_data_t data;
 
-  err = gpgme_data_new_from_filepart (&data, NULL, fp, offset, length);
-  if (err) {
-    mutt_error (_("error allocating data object: %s\n"), gpgme_strerror (err));
-    return NULL;
-  }
+    err = gpgme_data_new_from_filepart(&data, NULL, fp, offset, length);
+    if (err) {
+        mutt_error(_("error allocating data object: %s\n"),
+                   gpgme_strerror(err));
+        return NULL;
+    }
 
-  return data;
+    return data;
 }
 
 /* Write a GPGME data object to the stream FP. */
-static int data_object_to_stream (gpgme_data_t data, FILE * fp)
+static int data_object_to_stream(gpgme_data_t data, FILE *fp)
 {
-  int err;
-  char buf[4096], *p;
-  ssize_t nread;
-
-  err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
-         ? gpgme_error_from_errno (errno) : 0);
-  if (err) {
-    mutt_error (_("error rewinding data object: %s\n"), gpgme_strerror (err));
-    return -1;
-  }
+    int err;
+    char buf[4096], *p;
+    ssize_t nread;
 
-  while ((nread = gpgme_data_read (data, buf, sizeof (buf)))) {
-    /* fixme: we are not really converting CRLF to LF but just
-       skipping CR. Doing it correctly needs a more complex logic */
-    for (p = buf; nread; p++, nread--) {
-      if (*p != '\r')
-        putc (*p, fp);
+    err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
+           ? gpgme_error_from_errno (errno) : 0);
+    if (err) {
+        mutt_error (_("error rewinding data object: %s\n"),
+                    gpgme_strerror(err));
+        return -1;
     }
 
-    if (ferror (fp)) {
-      mutt_perror ("[tempfile]");
-      return -1;
+    while ((nread = gpgme_data_read(data, buf, sizeof(buf)))) {
+        /* fixme: we are not really converting CRLF to LF but just
+           skipping CR. Doing it correctly needs a more complex logic */
+        for (p = buf; nread; p++, nread--) {
+            if (*p != '\r')
+                putc (*p, fp);
+        }
+
+        if (ferror(fp)) {
+            mutt_perror ("[tempfile]");
+            return -1;
+        }
     }
-  }
-  if (nread == -1) {
-    mutt_error (_("error reading data object: %s\n"), strerror (errno));
-    return -1;
-  }
-  return 0;
+    if (nread == -1) {
+        mutt_error(_("error reading data object: %s\n"), strerror(errno));
+        return -1;
+    }
+    return 0;
 }
 
 /* Copy a data object to a newly created temporay file and return that
    filename. Caller must free.  With RET_FP not NULL, don't close the
    stream but return it there. */
-static char *data_object_to_tempfile (gpgme_data_t data, FILE ** ret_fp)
+static char *data_object_to_tempfile(gpgme_data_t data, FILE **ret_fp)
 {
-  int err;
-  char tempfile[_POSIX_PATH_MAX];
-  FILE *fp;
-  ssize_t nread = 0;
+    int err;
+    char tempfile[_POSIX_PATH_MAX];
+    FILE *fp;
+    ssize_t nread = 0;
 
-  fp = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
-  if (!fp) {
-    mutt_perror (_("Can't create temporary file"));
-    return NULL;
-  }
+    fp = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
+    if (!fp) {
+        mutt_perror (_("Can't create temporary file"));
+        return NULL;
+    }
 
-  err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
-         ? gpgme_error_from_errno (errno) : 0);
-  if (!err) {
-    char buf[4096];
+    err = ((gpgme_data_seek (data, 0, SEEK_SET) == -1)
+           ? gpgme_error_from_errno (errno) : 0);
+    if (!err) {
+        char buf[4096];
+
+        while ((nread = gpgme_data_read(data, buf, sizeof(buf)))) {
+            if (fwrite (buf, nread, 1, fp) != 1) {
+                mutt_perror (_("Can't create temporary file"));
+                m_fclose(&fp);
+                unlink (tempfile);
+                return NULL;
+            }
+        }
+    }
 
-    while ((nread = gpgme_data_read (data, buf, sizeof (buf)))) {
-      if (fwrite (buf, nread, 1, fp) != 1) {
-        mutt_perror (_("Can't create temporary file"));
-        m_fclose(&fp);
+    if (nread == -1) {
+        mutt_error (_("error reading data object: %s\n"), gpgme_strerror (err));
         unlink (tempfile);
+        m_fclose(&fp);
         return NULL;
-      }
     }
-  }
-  if (ret_fp)
-    rewind (fp);
-  else
-    m_fclose(&fp);
-  if (nread == -1) {
-    mutt_error (_("error reading data object: %s\n"), gpgme_strerror (err));
-    unlink (tempfile);
-    m_fclose(&fp);
-    return NULL;
-  }
-  if (ret_fp)
-    *ret_fp = fp;
-  return m_strdup(tempfile);
+    if (ret_fp) {
+        rewind(fp);
+        *ret_fp = fp;
+    } else {
+        m_fclose(&fp);
+    }
+    return m_strdup(tempfile);
 }
 
 
@@ -512,110 +427,80 @@ gpgme_get_key2 (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key,
 
 /* Create a GpgmeRecipientSet from the keys in the string KEYLIST.
    The keys must be space delimited. */
-static gpgme_key_t *create_recipient_set (const char *keylist,
-                                          gpgme_protocol_t protocol)
-{
-  int err;
-  const char *s;
-  char buf[100];
-  int i;
-  gpgme_key_t *rset = NULL;
-  unsigned int rset_n = 0;
-  gpgme_key_t key = NULL;
-  gpgme_ctx_t context = NULL;
-
-  err = gpgme_new (&context);
-  if (!err)
-    err = gpgme_set_protocol (context, protocol);
-
-  if (!err) {
-    s = keylist;
-    do {
-      while (*s == ' ')
-        s++;
-      for (i = 0; *s && *s != ' ' && i < ssizeof(buf) - 1;)
-        buf[i++] = *s++;
-      buf[i] = 0;
-      if (*buf) {
-        if (i > 1 && buf[i - 1] == '!') {
-          /* The user selected to override the valididy of that
-             key. */
-          buf[i - 1] = 0;
-
-          err = gpgme_get_key2 (context, buf, &key, 0);
-          if (!err)
-            key->uids->validity = GPGME_VALIDITY_FULL;
-          buf[i - 1] = '!';
+static gpgme_key_t *create_recipient_set(const char *s, int smime)
+{
+    gpgme_ctx_t  ctx  = create_gpgme_context(smime);
+    gpgme_key_t *rset = NULL;
+    int rset_n = 0;
+
+    s = skipspaces(s);
+    while (*s) {
+        gpgme_error_t err;
+        gpgme_key_t key;
+        const char *p = m_strnextsp(s);
+        char buf[100];
+
+        m_strncpy(buf, sizeof(buf), s, p - s);
+        if (p - s > 1 && p[-1] == '!') {
+            /* user wants to override the valididy of that key. */
+
+            buf[p - s - 1] = '\0';
+            err = gpgme_get_key2(ctx, buf, &key, 0);
+            if (!err)
+                key->uids->validity = GPGME_VALIDITY_FULL;
+        } else {
+            err = gpgme_get_key2(ctx, buf, &key, 0);
         }
-        else
-          err = gpgme_get_key2 (context, buf, &key, 0);
 
-        if (!err) {
-          p_realloc(&rset, rset_n + 1);
-          rset[rset_n++] = key;
-        }
-        else {
-          mutt_error (_("error adding recipient `%s': %s\n"),
-                      buf, gpgme_strerror (err));
-          p_delete(&rset);
-          return NULL;
+        if (err) {
+            mutt_error(_("error adding recipient `%.*s': %s\n"),
+                       (int)(p - s), s, gpgme_strerror(err));
+            p_delete(&rset);
+            break;
         }
-      }
-    } while (*s);
-  }
 
-  /* NULL terminate.  */
-  p_realloc(&rset, rset_n + 1);
-  rset[rset_n++] = NULL;
+        p_realloc(&rset, rset_n + 1);
+        rset[rset_n++] = key;
+        s = skipspaces(p);
+    }
 
-  if (context)
-    gpgme_release (context);
+    if (rset) {
+        /* NULL terminate.  */
+        p_realloc(&rset, rset_n + 1);
+        rset[rset_n++] = NULL;
+    }
 
-  return rset;
+    gpgme_release(ctx);
+    return rset;
 }
 
 
 /* Make sure that the correct signer is set. Returns 0 on success. */
-static int set_signer (gpgme_ctx_t ctx, int for_smime)
+static int set_signer(gpgme_ctx_t ctx, int for_smime)
 {
-  char *signid = for_smime ? SmimeDefaultKey : PgpSignAs;
-  gpgme_error_t err;
-  gpgme_ctx_t listctx;
-  gpgme_key_t key, key2;
+    const char *signid = for_smime ? SmimeDefaultKey : PgpSignAs;
+    gpgme_error_t err;
+    gpgme_key_t key;
 
-  if (!signid || !*signid)
-    return 0;
+    if (m_strisempty(signid))
+        return 0;
 
-  listctx = create_gpgme_context (for_smime);
-  err = gpgme_op_keylist_start (listctx, signid, 1);
-  if (!err)
-    err = gpgme_op_keylist_next (listctx, &key);
-  if (err) {
-    gpgme_release (listctx);
-    mutt_error (_("secret key `%s' not found: %s\n"),
-                signid, gpgme_strerror (err));
-    return -1;
-  }
-  err = gpgme_op_keylist_next (listctx, &key2);
-  if (!err) {
-    gpgme_key_release (key);
-    gpgme_key_release (key2);
-    gpgme_release (listctx);
-    mutt_error (_("ambiguous specification of secret key `%s'\n"), signid);
-    return -1;
-  }
-  gpgme_op_keylist_end (listctx);
-  gpgme_release (listctx);
+    err = gpgme_get_key(ctx, signid, &key, 1);
+    if (err) {
+        mutt_error(_("error getting secret key `%s': %s\n"), signid,
+                   gpgme_strerror(err));
+        return -1;
+    }
 
-  gpgme_signers_clear (ctx);
-  err = gpgme_signers_add (ctx, key);
-  gpgme_key_release (key);
-  if (err) {
-    mutt_error (_("error setting secret key `%s': %s\n"),
-                signid, gpgme_strerror (err));
-    return -1;
-  }
-  return 0;
+    gpgme_signers_clear(ctx);
+    err = gpgme_signers_add(ctx, key);
+    gpgme_key_unref(key);
+    if (err) {
+        mutt_error(_("error setting secret key `%s': %s\n"), signid,
+                   gpgme_strerror(err));
+        return -1;
+    }
+    return 0;
 }
 
 
@@ -624,45 +509,46 @@ static int set_signer (gpgme_ctx_t ctx, int for_smime)
    enciphered text.  With USE_SMIME set to true, the smime backend is
    used.  With COMBINED_SIGNED a PGP message is signed and
    encrypted.  Returns NULL in case of error */
-static char *encrypt_gpgme_object (gpgme_data_t plaintext, gpgme_key_t * rset,
-                                   int use_smime, int combined_signed)
+static char *encrypt_gpgme_object(gpgme_data_t plaintext, gpgme_key_t *rset,
+                                  int use_smime, int combined_signed)
 {
-  int err;
-  gpgme_ctx_t ctx;
-  gpgme_data_t ciphertext;
-  char *outfile;
+    int err;
+    gpgme_ctx_t ctx;
+    gpgme_data_t ciphertext;
+    char *outfile;
 
-  ctx = create_gpgme_context (use_smime);
-  if (!use_smime)
-    gpgme_set_armor (ctx, 1);
+    ctx = create_gpgme_context (use_smime);
+    if (!use_smime)
+        gpgme_set_armor (ctx, 1);
 
-  ciphertext = create_gpgme_data ();
+    ciphertext = create_gpgme_data ();
 
-  if (combined_signed) {
-    if (set_signer (ctx, use_smime)) {
-      gpgme_data_release (ciphertext);
-      gpgme_release (ctx);
-      return NULL;
+    if (combined_signed) {
+        if (set_signer(ctx, use_smime)) {
+            gpgme_data_release(ciphertext);
+            gpgme_release(ctx);
+            return NULL;
+        }
+        err = gpgme_op_encrypt_sign(ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
+                                    plaintext, ciphertext);
+    } else {
+        err = gpgme_op_encrypt(ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
+                               plaintext, ciphertext);
     }
-    err = gpgme_op_encrypt_sign (ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
-                                 plaintext, ciphertext);
-  }
-  else
-    err = gpgme_op_encrypt (ctx, rset, GPGME_ENCRYPT_ALWAYS_TRUST,
-                            plaintext, ciphertext);
-  mutt_need_hard_redraw ();
-  if (err) {
-    mutt_error (_("error encrypting data: %s\n"), gpgme_strerror (err));
-    gpgme_data_release (ciphertext);
-    gpgme_release (ctx);
-    return NULL;
-  }
 
-  gpgme_release (ctx);
+    mutt_need_hard_redraw();
+    if (err) {
+        mutt_error (_("error encrypting data: %s\n"), gpgme_strerror (err));
+        gpgme_data_release (ciphertext);
+        gpgme_release (ctx);
+        return NULL;
+    }
 
-  outfile = data_object_to_tempfile (ciphertext, NULL);
-  gpgme_data_release (ciphertext);
-  return outfile;
+    gpgme_release(ctx);
+
+    outfile = data_object_to_tempfile(ciphertext, NULL);
+    gpgme_data_release(ciphertext);
+    return outfile;
 }
 
 /* Find the "micalg" parameter from the last Gpgme operation on
@@ -671,38 +557,35 @@ static char *encrypt_gpgme_object (gpgme_data_t plaintext, gpgme_key_t * rset,
    which must have been allocated by the caller with size BUFLEN.
    Returns 0 on success or -1 in case of an error.  The return string
    is truncted to BUFLEN - 1. */
-static int get_micalg (gpgme_ctx_t ctx, char *buf, ssize_t buflen)
+static int get_micalg(gpgme_ctx_t ctx, char *buf, ssize_t buflen)
 {
-  gpgme_sign_result_t result = NULL;
-  const char *algorithm_name = NULL;
+    gpgme_sign_result_t result = NULL;
+    const char *algorithm_name = NULL;
 
-  if (!buflen)
-    return -1;
-
-  *buf = 0;
-  result = gpgme_op_sign_result (ctx);
-  if (result) {
-    algorithm_name = gpgme_hash_algo_name (result->signatures->hash_algo);
-    if (algorithm_name) {
-      m_strcpy(buf, buflen, algorithm_name);
+    *buf = '\0';
+    result = gpgme_op_sign_result(ctx);
+    if (result) {
+        algorithm_name = gpgme_hash_algo_name (result->signatures->hash_algo);
+        if (algorithm_name) {
+            m_strcpy(buf, buflen, algorithm_name);
+        }
     }
-  }
 
-  return *buf ? 0 : -1;
+    return *buf ? 0 : -1;
 }
 
-static void print_time (time_t t, STATE * s)
+static void print_time(time_t t, STATE *s)
 {
-  char p[STRING];
+    char p[STRING];
 
-  setlocale (LC_TIME, "");
+    setlocale(LC_TIME, "");
 #ifdef HAVE_LANGINFO_D_T_FMT
-  strftime (p, sizeof (p), nl_langinfo (D_T_FMT), localtime (&t));
+    strftime(p, sizeof(p), nl_langinfo(D_T_FMT), localtime(&t));
 #else
-  strftime (p, sizeof (p), "%c", localtime (&t));
+    strftime(p, sizeof(p), "%c", localtime(&t));
 #endif
-  setlocale (LC_TIME, "C");
-  state_attach_puts (p, s);
+    setlocale(LC_TIME, "C");
+    state_attach_puts(p, s);
 }
 
 /* Implementation of `sign_message'. */
@@ -710,7 +593,7 @@ static void print_time (time_t t, STATE * s)
 /* Sign the MESSAGE in body A either using OpenPGP or S/MIME when
    USE_SMIME is passed as true.  Returns the new body or NULL on
    error. */
-static BODY *sign_message (BODY * a, int use_smime)
+static BODY *sign_message(BODY * a, int use_smime)
 {
   BODY *t;
   char *sigfile;
@@ -721,7 +604,7 @@ static BODY *sign_message (BODY * a, int use_smime)
 
   convert_to_7bit (a);          /* Signed data _must_ be in 7-bit format. */
 
-  message = body_to_data_object (a, 1);
+  message = body_to_data_object(a, 1);
   if (!message)
     return NULL;
   signature = create_gpgme_data ();
@@ -746,7 +629,7 @@ static BODY *sign_message (BODY * a, int use_smime)
     return NULL;
   }
 
-  sigfile = data_object_to_tempfile (signature, NULL);
+  sigfile = data_object_to_tempfile(signature, NULL);
   gpgme_data_release (signature);
   if (!sigfile) {
     gpgme_release (ctx);
@@ -798,10 +681,6 @@ static BODY *sign_message (BODY * a, int use_smime)
   return a;
 }
 
-/*
- * Implementation of `encrypt_message'.
- */
-
 /* Encrypt the mail body A to all keys given as space separated keyids
    or fingerprints in KEYLIST and return the encrypted body.  */
 static BODY *crypt_pgp_encrypt_message (BODY * a, char *keylist, int sign)
@@ -811,13 +690,13 @@ static BODY *crypt_pgp_encrypt_message (BODY * a, char *keylist, int sign)
   gpgme_key_t *rset = NULL;
   gpgme_data_t plaintext;
 
-  rset = create_recipient_set (keylist, GPGME_PROTOCOL_OpenPGP);
+  rset = create_recipient_set(keylist, 0);
   if (!rset)
     return NULL;
 
   if (sign)
     convert_to_7bit (a);
-  plaintext = body_to_data_object (a, 0);
+  plaintext = body_to_data_object(a, 0);
   if (!plaintext) {
     p_delete(&rset);
     return NULL;
@@ -852,16 +731,11 @@ static BODY *crypt_pgp_encrypt_message (BODY * a, char *keylist, int sign)
   t->parts->next->use_disp = 1;
   t->parts->next->disposition = DISPINLINE;
   t->parts->next->unlink = 1;   /* delete after sending the message */
-  t->parts->next->d_filename = m_strdup("msg.asc"); /* non pgp/mime
-                                                           can save */
+  t->parts->next->d_filename = m_strdup("msg.asc");
 
   return t;
 }
 
-/*
- * Implementation of `smime_build_smime_entity'.
- */
-
 /* Encrypt the mail body A to all keys given as space separated
    fingerprints in KEYLIST and return the S/MIME encrypted body.  */
 static BODY *crypt_smime_build_smime_entity (BODY * a, char *keylist)
@@ -871,11 +745,11 @@ static BODY *crypt_smime_build_smime_entity (BODY * a, char *keylist)
   gpgme_key_t *rset = NULL;
   gpgme_data_t plaintext;
 
-  rset = create_recipient_set (keylist, GPGME_PROTOCOL_CMS);
+  rset = create_recipient_set(keylist, 1);
   if (!rset)
     return NULL;
 
-  plaintext = body_to_data_object (a, 0);
+  plaintext = body_to_data_object(a, 0);
   if (!plaintext) {
     p_delete(&rset);
     return NULL;
@@ -904,9 +778,6 @@ static BODY *crypt_smime_build_smime_entity (BODY * a, char *keylist)
   return t;
 }
 
-
-/* Implementation of `verify_one'. */
-
 /* Display the common attributes of the signature summary SUM.
    Return 1 if there is is a severe warning.
  */
@@ -1116,7 +987,7 @@ static int show_one_sig_status (gpgme_ctx_t ctx, int idx, STATE * s)
       return -1;                /* Signature not found.  */
 
     if (signature_key) {
-      gpgme_key_release (signature_key);
+      gpgme_key_unref(signature_key);
       signature_key = NULL;
     }
 
@@ -1196,7 +1067,7 @@ static int show_one_sig_status (gpgme_ctx_t ctx, int idx, STATE * s)
     }
 
     if (key != signature_key)
-      gpgme_key_release (key);
+      gpgme_key_unref(key);
   }
 
   return anybad ? 1 : anywarn ? 2 : 0;
@@ -1248,7 +1119,7 @@ static int crypt_verify_one(BODY *sigbdy, STATE *s, FILE *fp, int is_smime)
     int anybad = 0;
 
     if (signature_key) {
-      gpgme_key_release (signature_key);
+      gpgme_key_unref(signature_key);
       signature_key = NULL;
     }
 
@@ -1301,18 +1172,14 @@ static int crypt_verify_one(BODY *sigbdy, STATE *s, FILE *fp, int is_smime)
   return badsig ? 1 : anywarn ? 2 : 0;
 }
 
-/*
- * Implementation of `decrypt_part'.
- */
-
 /* Decrypt a PGP or SMIME message (depending on the boolean flag
    IS_SMIME) with body A described further by state S.  Write
    plaintext out to file FPOUT and return a new body.  For PGP returns
    a flag in R_IS_SIGNED to indicate whether this is a combined
    encrypted and signed message, for S/MIME it returns true when it is
    not a encrypted but a signed message.  */
-static BODY *decrypt_part (BODY * a, STATE * s, FILE * fpout, int is_smime,
-                           int *r_is_signed)
+static BODY *
+decrypt_part(BODY *a, STATE *s, FILE *fpout, int is_smime, int *r_is_signed)
 {
   struct stat info;
   BODY *tattach;
@@ -1441,48 +1308,39 @@ restart:
 
 /* Decrypt a PGP/MIME message in FPIN and B and return a new body and
    the stream in CUR and FPOUT.  Returns 0 on success. */
-int crypt_pgp_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b, BODY ** cur)
+int crypt_pgp_decrypt_mime (FILE * fpin, FILE **fpout, BODY *b, BODY **cur)
 {
-  char tempfile[_POSIX_PATH_MAX];
-  STATE s;
-  BODY *first_part = b;
-  int is_signed;
-
-  first_part->goodsig = 0;
-  first_part->warnsig = 0;
+    STATE s;
+    BODY *first_part = b;
+    int is_signed;
 
-  if (!mutt_is_multipart_encrypted (b))
-    return -1;
+    first_part->goodsig = 0;
+    first_part->warnsig = 0;
 
-  if (!b->parts || !b->parts->next)
-    return -1;
+    if (!mutt_is_multipart_encrypted(b) || !b->parts || !b->parts->next)
+        return -1;
 
-  b = b->parts->next;
+    b = b->parts->next;
 
-  p_clear(&s, 1);
-  s.fpin = fpin;
-  *fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
-  if (!*fpout) {
-    mutt_perror (_("Can't create temporary file"));
-    return -1;
-  }
-  unlink (tempfile);
-
-  *cur = decrypt_part (b, &s, *fpout, 0, &is_signed);
-  rewind (*fpout);
-  if (is_signed > 0)
-    first_part->goodsig = 1;
+    p_clear(&s, 1);
+    s.fpin = fpin;
+    *fpout = tmpfile();
+    if (!*fpout) {
+        mutt_perror (_("Can't create temporary file"));
+        return -1;
+    }
 
-  return *cur ? 0 : -1;
+    *cur = decrypt_part(b, &s, *fpout, 0, &is_signed);
+    rewind(*fpout);
+    first_part->goodsig = is_signed > 0;
+    return *cur ? 0 : -1;
 }
 
 
 /* Decrypt a S/MIME message in FPIN and B and return a new body and
    the stream in CUR and FPOUT.  Returns 0 on success. */
-int crypt_smime_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b,
-                              BODY ** cur)
+int crypt_smime_decrypt_mime(FILE *fpin, FILE **fpout, BODY *b, BODY **cur)
 {
-  char tempfile[_POSIX_PATH_MAX];
   STATE s;
   FILE *tmpfp = NULL;
   int is_signed;
@@ -1506,12 +1364,11 @@ int crypt_smime_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b,
   p_clear(&s, 1);
   s.fpin = fpin;
   fseeko (s.fpin, b->offset, 0);
-  tmpfp = m_tempfile (tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
+  tmpfp = tmpfile();
   if (!tmpfp) {
     mutt_perror (_("Can't create temporary file"));
     return -1;
   }
-  mutt_unlink (tempfile);
 
   s.fpout = tmpfp;
   mutt_decode_attachment (b, &s);
@@ -1523,12 +1380,11 @@ int crypt_smime_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b,
   p_clear(&s, 1);
   s.fpin = tmpfp;
   s.fpout = 0;
-  *fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
+  *fpout = tmpfile();
   if (!*fpout) {
     mutt_perror (_("Can't create temporary file"));
     return -1;
   }
-  mutt_unlink (tempfile);
 
   *cur = decrypt_part (b, &s, *fpout, 1, &is_signed);
   if (*cur)
@@ -1558,12 +1414,11 @@ int crypt_smime_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b,
     p_clear(&s, 1);
     s.fpin = *fpout;
     fseeko (s.fpin, bb->offset, 0);
-    tmpfp = m_tempfile (tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
+    tmpfp = tmpfile();
     if (!tmpfp) {
       mutt_perror (_("Can't create temporary file"));
       return -1;
     }
-    mutt_unlink (tempfile);
 
     s.fpout = tmpfp;
     mutt_decode_attachment (bb, &s);
@@ -1576,12 +1431,11 @@ int crypt_smime_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b,
     p_clear(&s, 1);
     s.fpin = tmpfp;
     s.fpout = 0;
-    *fpout = m_tempfile(tempfile, sizeof(tempfile), NONULL(MCore.tmpdir), NULL);
+    *fpout = tmpfile();
     if (!*fpout) {
       mutt_perror (_("Can't create temporary file"));
       return -1;
     }
-    mutt_unlink (tempfile);
 
     tmp_b = decrypt_part (bb, &s, *fpout, 1, &is_signed);
     if (tmp_b)
@@ -1598,12 +1452,8 @@ int crypt_smime_decrypt_mime (FILE * fpin, FILE ** fpout, BODY * b,
 }
 
 
-/*
- * Implementation of `pgp_check_traditional'.
- */
-
-static int pgp_check_traditional_one_body (FILE * fp, BODY * b,
-                                           int tagged_only)
+static int
+pgp_check_traditional_one_body(FILE *fp, BODY *b, int tagged_only)
 {
   char tempfile[_POSIX_PATH_MAX];
   char buf[HUGE_STRING];
@@ -1652,26 +1502,25 @@ static int pgp_check_traditional_one_body (FILE * fp, BODY * b,
   return 1;
 }
 
-int crypt_pgp_check_traditional (FILE * fp, BODY * b, int tagged_only)
+int crypt_pgp_check_traditional(FILE *fp, BODY *b, int tagged_only)
 {
-  int rv = 0;
-  int r;
+    int rv = 0;
 
-  for (; b; b = b->next) {
-    if (is_multipart (b))
-      rv = (crypt_pgp_check_traditional (fp, b->parts, tagged_only) || rv);
-    else if (b->type == TYPETEXT) {
-      if ((r = mutt_is_application_pgp (b)))
-        rv = (rv || r);
-      else
-        rv = (pgp_check_traditional_one_body (fp, b, tagged_only) || rv);
+    for (; b; b = b->next) {
+        if (is_multipart(b))
+            rv |= crypt_pgp_check_traditional(fp, b->parts, tagged_only);
+        if (b->type == TYPETEXT) {
+            int r;
+            if ((r = mutt_is_application_pgp(b))) {
+                rv |= r;
+            } else {
+                rv |= pgp_check_traditional_one_body(fp, b, tagged_only);
+            }
+        }
     }
-  }
-  return rv;
-}
 
-
-/* Implementation of `application_handler'. */
+    return rv;
+}
 
 /*
   Copy a clearsigned message, and strip the signature and PGP's
@@ -1684,8 +1533,7 @@ int crypt_pgp_check_traditional (FILE * fp, BODY * b, int tagged_only)
   (Note that we aren't worse than Outlook & Cie in this, and also
   note that we can successfully handle anything produced by any
   existing versions of mutt.)  */
-
-static void copy_clearsigned (gpgme_data_t data, STATE * s, char *charset)
+static void copy_clearsigned(gpgme_data_t data, STATE * s, char *charset)
 {
   char buf[HUGE_STRING];
   short complete, armor_header;
@@ -1693,7 +1541,7 @@ static void copy_clearsigned (gpgme_data_t data, STATE * s, char *charset)
   char *fname;
   FILE *fp;
 
-  fname = data_object_to_tempfile (data, &fp);
+  fname = data_object_to_tempfile(data, &fp);
   if (!fname)
     return;
   unlink (fname);
@@ -1732,9 +1580,8 @@ static void copy_clearsigned (gpgme_data_t data, STATE * s, char *charset)
   m_fclose(&fp);
 }
 
-
 /* Support for classic_application/pgp */
-int crypt_pgp_application_pgp_handler (BODY * m, STATE * s)
+int crypt_pgp_application_pgp_handler(BODY *m, STATE *s)
 {
   int needpass = -1, pgp_keyblock = 0;
   int clearsign = 0;
@@ -1779,8 +1626,7 @@ int crypt_pgp_application_pgp_handler (BODY * m, STATE * s)
         clearsign = 1;
         needpass = 0;
       }
-      else if (!option (OPTDONTHANDLEPGPKEYS) &&
-               !m_strcmp("PUBLIC KEY BLOCK-----\n", buf + 15)) {
+      else if (!m_strcmp("PUBLIC KEY BLOCK-----\n", buf + 15)) {
         needpass = 0;
         pgp_keyblock = 1;
       }
@@ -1881,7 +1727,7 @@ int crypt_pgp_application_pgp_handler (BODY * m, STATE * s)
                                  "information --]\n\n"), s);
           }
 
-          tmpfname = data_object_to_tempfile (plaintext, &pgpout);
+          tmpfname = data_object_to_tempfile(plaintext, &pgpout);
           if (!tmpfname) {
             pgpout = NULL;
             state_attach_puts (_("Error: copy data failed\n"), s);
@@ -1958,8 +1804,6 @@ int crypt_pgp_application_pgp_handler (BODY * m, STATE * s)
   return (err);
 }
 
-/* Implementation of `encrypted_handler'. */
-
 /* MIME handler for pgp/mime encrypted messages. */
 int crypt_pgp_encrypted_handler (BODY * a, STATE * s)
 {
@@ -2122,7 +1966,7 @@ crypt_entry_fmt (char *dest, ssize_t destlen, char op,
 {
   char fmt[16];
   crypt_entry_t *entry;
-  crypt_key_t *key;
+  cryptkey_t *key;
   int kflags = 0;
   int optional = (flags & M_FORMAT_OPTIONAL);
   const char *s = NULL;
@@ -2308,10 +2152,10 @@ crypt_entry_fmt (char *dest, ssize_t destlen, char op,
 /* Used by the display fucntion to format a line. */
 static void crypt_entry (char *s, ssize_t l, MUTTMENU * menu, int num)
 {
-  crypt_key_t **key_table = (crypt_key_t **) menu->data;
+  cryptkey_t **cryptkey_table = (cryptkey_t **) menu->data;
   crypt_entry_t entry;
 
-  entry.key = key_table[num];
+  entry.key = cryptkey_table[num];
   entry.num = num + 1;
 
   m_strformat(s, l, COLS - SW, PgpEntryFormat, crypt_entry_fmt, &entry,
@@ -2321,8 +2165,8 @@ static void crypt_entry (char *s, ssize_t l, MUTTMENU * menu, int num)
 /* Compare two addresses and the keyid to be used for sorting. */
 static int _crypt_compare_address (const void *a, const void *b)
 {
-  crypt_key_t **s = (crypt_key_t **) a;
-  crypt_key_t **t = (crypt_key_t **) b;
+  cryptkey_t **s = (cryptkey_t **) a;
+  cryptkey_t **t = (cryptkey_t **) b;
   int r;
 
   if ((r = m_strcasecmp((*s)->uid, (*t)->uid)))
@@ -2341,8 +2185,8 @@ static int crypt_compare_address (const void *a, const void *b)
 /* Compare two key IDs and the addresses to be used for sorting. */
 static int _crypt_compare_keyid (const void *a, const void *b)
 {
-  crypt_key_t **s = (crypt_key_t **) a;
-  crypt_key_t **t = (crypt_key_t **) b;
+  cryptkey_t **s = (cryptkey_t **) a;
+  cryptkey_t **t = (cryptkey_t **) b;
   int r;
 
   if ((r = m_strcasecmp(crypt_keyid (*s), crypt_keyid (*t))))
@@ -2360,8 +2204,8 @@ static int crypt_compare_keyid (const void *a, const void *b)
 /* Compare 2 creation dates and the addresses.  For sorting. */
 static int _crypt_compare_date (const void *a, const void *b)
 {
-  crypt_key_t **s = (crypt_key_t **) a;
-  crypt_key_t **t = (crypt_key_t **) b;
+  cryptkey_t **s = (cryptkey_t **) a;
+  cryptkey_t **t = (cryptkey_t **) b;
   unsigned long ts = 0, tt = 0;
 
   if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
@@ -2387,8 +2231,8 @@ static int crypt_compare_date (const void *a, const void *b)
    addresses and the key IDs.  For sorting. */
 static int _crypt_compare_trust (const void *a, const void *b)
 {
-  crypt_key_t **s = (crypt_key_t **) a;
-  crypt_key_t **t = (crypt_key_t **) b;
+  cryptkey_t **s = (cryptkey_t **) a;
+  cryptkey_t **t = (cryptkey_t **) b;
   unsigned long ts = 0, tt = 0;
   int r;
 
@@ -2649,12 +2493,12 @@ static void parse_and_print_user_id(FILE * fp, const char *userid)
 }
 
 typedef enum {
-  KEY_CAP_CAN_ENCRYPT,
-  KEY_CAP_CAN_SIGN,
-  KEY_CAP_CAN_CERTIFY
+    KEY_CAP_CAN_ENCRYPT,
+    KEY_CAP_CAN_SIGN,
+    KEY_CAP_CAN_CERTIFY
 } key_cap_t;
 
-static unsigned int key_check_cap (gpgme_key_t key, key_cap_t cap)
+static unsigned int key_check_cap(gpgme_key_t key, key_cap_t cap)
 {
   gpgme_subkey_t subkey = NULL;
   unsigned int ret = 0;
@@ -2905,7 +2749,7 @@ static void print_key_info (gpgme_key_t key, FILE * fp)
 
 
 /* Show detailed information about the selected key */
-static void verify_key (crypt_key_t * key)
+static void verify_key (cryptkey_t * key)
 {
   FILE *fp;
   char cmd[LONG_STRING], tempfile[_POSIX_PATH_MAX];
@@ -2938,7 +2782,7 @@ static void verify_key (crypt_key_t * key)
   while ((s = k->chain_id) && k->subkeys && m_strcmp(s, k->subkeys->fpr)) {
     putc ('\n', fp);
     err = gpgme_op_keylist_start (listctx, s, 0);
-    gpgme_key_release (k);
+    gpgme_key_unref(k);
     k = NULL;
     if (!err)
       err = gpgme_op_keylist_next (listctx, &k);
@@ -2957,221 +2801,132 @@ static void verify_key (crypt_key_t * key)
   }
 
 leave:
-  gpgme_key_release (k);
+  gpgme_key_unref(k);
   gpgme_release (listctx);
   m_fclose(&fp);
   mutt_clear_error ();
   snprintf (cmd, sizeof (cmd), _("Key ID: 0x%s"), crypt_keyid (key));
-  mutt_do_pager (cmd, tempfile, 0, NULL);
+  mutt_pager(cmd, tempfile, 0, NULL);
 }
 
 /* Implementation of `findkeys'. */
 
-/* Convert string_list_t into a pattern string suitable to be passed to GPGME.
-   We need to convert spaces in an item into a '+' and '%' into
-   "%25". */
-static char *list_to_pattern (string_list_t * list)
+static void add_hints(string_array *arr, const char *s)
 {
-  string_list_t *l;
-  char *pattern, *p;
-  const char *s;
-  ssize_t n;
+    if (!s)
+        return;
 
-  n = 0;
-  for (l = list; l; l = l->next) {
-    for (s = l->data; *s; s++) {
-      if (*s == '%')
-        n += 2;
-      n++;
+    while (*s) {
+        int l = strcspn(s, " ,.:\"()<>\n");
+        string_array_append(arr, p_dupstr(s, l));
+        s += l;
+        s += strspn(s, " ,.:\"()<>\n");
     }
-    n++;                        /* delimiter or end of string */
-  }
-  n++;                          /* make sure to allocate at least one byte */
-  pattern = p = p_new(char, n);
-  for (l = list; l; l = l->next) {
-    s = l->data;
-    if (*s) {
-      if (l != list)
-        *p++ = ' ';
-      for (s = l->data; *s; s++) {
-        if (*s == '%') {
-          *p++ = '%';
-          *p++ = '2';
-          *p++ = '5';
-        }
-        else if (*s == '+') {
-          *p++ = '%';
-          *p++ = '2';
-          *p++ = 'B';
-        }
-        else if (*s == ' ')
-          *p++ = '+';
-        else
-          *p++ = *s;
-      }
-    }
-  }
-  *p = 0;
-  return pattern;
 }
 
-/* Return a list of keys which are candidates for the selection.
-   Select by looking at the HINTS list. */
-static crypt_key_t *get_candidates (string_list_t * hints, unsigned int app,
-                                    int secret)
+/* Return a list of keys which are candidates for the selection. */
+static cryptkey_t *
+get_candidates(string_array *hints, unsigned int app, int secret)
 {
-  crypt_key_t *db, *k, **kend;
-  char *pattern;
-  gpgme_error_t err;
-  gpgme_ctx_t ctx;
-  gpgme_key_t key;
-  int idx;
-  gpgme_user_id_t uid = NULL;
-
-  pattern = list_to_pattern (hints);
-  if (!pattern)
-    return NULL;
-
-  err = gpgme_new (&ctx);
-  if (err) {
-    mutt_error (_("gpgme_new failed: %s"), gpgme_strerror (err));
-    p_delete(&pattern);
-    return NULL;
-  }
-
-  db = NULL;
-  kend = &db;
+    cryptkey_t  *res = NULL, **kend = &res;
+    gpgme_error_t err;
+    gpgme_ctx_t   ctx;
+    gpgme_key_t   key;
 
-  if ((app & APPLICATION_PGP)) {
-    /* Its all a mess.  That old GPGME expects different things
-       depending on the protocol.  For gpg we don' t need percent
-       escaped pappert but simple strings passed in an array to the
-       keylist_ext_start function. */
-    string_list_t *l;
-    ssize_t n;
-    char **patarr;
+    if (hints->len <= 0)
+        return NULL;
+    string_array_append(hints, NULL);
+    ctx = create_gpgme_context(0);
 
-    for (l = hints, n = 0; l; l = l->next) {
-      if (l->data && *l->data)
-        n++;
-    }
-    if (!n)
-      goto no_pgphints;
+    if ((app & APPLICATION_PGP)) {
+        err = gpgme_op_keylist_ext_start(ctx, (const char **)hints->arr,
+                                         secret, 0);
+        if (err) {
+            mutt_error(_("gpgme_op_keylist_start failed: %s"),
+                       gpgme_strerror(err));
+            gpgme_release(ctx);
+            return NULL;
+        }
 
-    patarr = p_new(char *, n + 1);
-    for (l = hints, n = 0; l; l = l->next) {
-      if (l->data && *l->data)
-        patarr[n++] = m_strdup(l->data);
-    }
-    patarr[n] = NULL;
-    err = gpgme_op_keylist_ext_start (ctx, (const char **) patarr, secret, 0);
-    for (n = 0; patarr[n]; n++)
-      p_delete(&patarr[n]);
-    p_delete(&patarr);
-    if (err) {
-      mutt_error (_("gpgme_op_keylist_start failed: %s"), gpgme_strerror (err));
-      gpgme_release (ctx);
-      p_delete(&pattern);
-      return NULL;
+        while (!(err = gpgme_op_keylist_next(ctx, &key))) {
+            gpgme_user_id_t uid = NULL;
+            unsigned int flags = 0;
+            int idx;
+
+            if (key_check_cap(key, KEY_CAP_CAN_ENCRYPT))
+                flags |= KEYFLAG_CANENCRYPT;
+            if (key_check_cap(key, KEY_CAP_CAN_SIGN))
+                flags |= KEYFLAG_CANSIGN;
+
+            for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
+                cryptkey_t *k = p_new(cryptkey_t, 1);
+                k->kobj = key;
+                k->idx = idx;
+                k->uid = uid->uid;
+                k->flags = flags;
+                *kend = k;
+                kend = &k->next;
+            }
+        }
+        if (gpg_err_code(err) != GPG_ERR_EOF)
+            mutt_error(_("gpgme_op_keylist_next failed: %s"), gpgme_strerror(err));
+        gpgme_op_keylist_end(ctx);
     }
 
-    while (!(err = gpgme_op_keylist_next (ctx, &key))) {
-      unsigned int flags = 0;
-
-      if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT))
-        flags |= KEYFLAG_CANENCRYPT;
-      if (key_check_cap (key, KEY_CAP_CAN_SIGN))
-        flags |= KEYFLAG_CANSIGN;
-
-      for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
-        k = p_new(crypt_key_t, 1);
-        k->kobj = key;
-        k->idx = idx;
-        k->uid = uid->uid;
-        k->flags = flags;
-        *kend = k;
-        kend = &k->next;
-      }
-    }
-    if (gpg_err_code (err) != GPG_ERR_EOF)
-      mutt_error (_("gpgme_op_keylist_next failed: %s"), gpgme_strerror (err));
-    gpgme_op_keylist_end (ctx);
-  no_pgphints:
-    ;
-  }
+    if ((app & APPLICATION_SMIME)) {
+        gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS);
+        err = gpgme_op_keylist_ext_start(ctx, (const char **)hints->arr,
+                                         secret, 0);
+        if (err) {
+            mutt_error(_("gpgme_op_keylist_start failed: %s"),
+                       gpgme_strerror(err));
+            gpgme_release(ctx);
+            return NULL;
+        }
 
-  if ((app & APPLICATION_SMIME)) {
-    /* and now look for x509 certificates */
-    gpgme_set_protocol (ctx, GPGME_PROTOCOL_CMS);
-    err = gpgme_op_keylist_start (ctx, pattern, 0);
-    if (err) {
-      mutt_error (_("gpgme_op_keylist_start failed: %s"), gpgme_strerror (err));
-      gpgme_release (ctx);
-      p_delete(&pattern);
-      return NULL;
+        while (!(err = gpgme_op_keylist_next(ctx, &key))) {
+            gpgme_user_id_t uid = NULL;
+            unsigned int flags = KEYFLAG_ISX509;
+            int idx;
+
+            if (key_check_cap(key, KEY_CAP_CAN_ENCRYPT))
+                flags |= KEYFLAG_CANENCRYPT;
+            if (key_check_cap(key, KEY_CAP_CAN_SIGN))
+                flags |= KEYFLAG_CANSIGN;
+
+            for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
+                cryptkey_t *k = p_new(cryptkey_t, 1);
+                k->kobj = key;
+                k->idx = idx;
+                k->uid = uid->uid;
+                k->flags = flags;
+                *kend = k;
+                kend = &k->next;
+            }
+        }
+        if (gpg_err_code(err) != GPG_ERR_EOF)
+            mutt_error(_("gpgme_op_keylist_next failed: %s"),
+                       gpgme_strerror(err));
+        gpgme_op_keylist_end(ctx);
     }
 
-    while (!(err = gpgme_op_keylist_next (ctx, &key))) {
-      unsigned int flags = KEYFLAG_ISX509;
-
-      if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT))
-        flags |= KEYFLAG_CANENCRYPT;
-      if (key_check_cap (key, KEY_CAP_CAN_SIGN))
-        flags |= KEYFLAG_CANSIGN;
-
-      for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next) {
-        k = p_new(crypt_key_t, 1);
-        k->kobj = key;
-        k->idx = idx;
-        k->uid = uid->uid;
-        k->flags = flags;
-        *kend = k;
-        kend = &k->next;
-      }
-    }
-    if (gpg_err_code (err) != GPG_ERR_EOF)
-      mutt_error (_("gpgme_op_keylist_next failed: %s"), gpgme_strerror (err));
-    gpgme_op_keylist_end (ctx);
-  }
-
-  gpgme_release (ctx);
-  p_delete(&pattern);
-  return db;
-}
-
-/* Add the string STR to the list HINTS.  This list is later used to
-   match addresses. */
-static string_list_t *crypt_add_string_to_hints (string_list_t * hints, const char *str)
-{
-  char *scratch;
-  char *t;
-
-  if ((scratch = m_strdup(str)) == NULL)
-    return hints;
-
-  for (t = strtok (scratch, " ,.:\"()<>\n"); t;
-       t = strtok (NULL, " ,.:\"()<>\n")) {
-    if (m_strlen(t) > 3)
-      hints = mutt_add_list(hints, t);
-  }
-
-  p_delete(&scratch);
-  return hints;
+    gpgme_release(ctx);
+    return res;
 }
 
 /* Display a menu to select a key from the array KEYS. FORCED_VALID
    will be set to true on return if the user did override the the
    key's validity. */
-static crypt_key_t *crypt_select_key (crypt_key_t * keys,
+static cryptkey_t *crypt_select_key (cryptkey_t * keys,
                                       address_t * p, const char *s,
                                       unsigned int app, int *forced_valid)
 {
   int keymax;
-  crypt_key_t **key_table;
+  cryptkey_t **cryptkey_table;
   MUTTMENU *menu;
   int i, done = 0;
   char helpstr[STRING], buf[LONG_STRING];
-  crypt_key_t *k;
+  cryptkey_t *k;
   int (*f) (const void *, const void *);
   int menu_to_use = 0;
   int unusable = 0;
@@ -3180,7 +2935,7 @@ static crypt_key_t *crypt_select_key (crypt_key_t * keys,
 
   /* build the key table */
   keymax = i = 0;
-  key_table = NULL;
+  cryptkey_table = NULL;
   for (k = keys; k; k = k->next) {
       if (!option (OPTPGPSHOWUNUSABLE) && (k->flags & KEYFLAG_CANTUSE)) {
           unusable = 1;
@@ -3189,10 +2944,10 @@ static crypt_key_t *crypt_select_key (crypt_key_t * keys,
 
       if (i == keymax) {
           keymax += 20;
-          p_realloc(&key_table, keymax);
+          p_realloc(&cryptkey_table, keymax);
       }
 
-      key_table[i++] = k;
+      cryptkey_table[i++] = k;
   }
 
   if (!i && unusable) {
@@ -3217,7 +2972,7 @@ static crypt_key_t *crypt_select_key (crypt_key_t * keys,
       f = crypt_compare_trust;
       break;
   }
-  qsort (key_table, i, sizeof (crypt_key_t *), f);
+  qsort (cryptkey_table, i, sizeof (cryptkey_t *), f);
 
   if (app & APPLICATION_PGP)
       menu_to_use = MENU_KEY_SELECT_PGP;
@@ -3241,7 +2996,7 @@ static crypt_key_t *crypt_select_key (crypt_key_t * keys,
   menu->make_entry = crypt_entry;
   menu->menu = menu_to_use;
   menu->help = helpstr;
-  menu->data = key_table;
+  menu->data = cryptkey_table;
 
   {
       const char *ts;
@@ -3268,32 +3023,32 @@ static crypt_key_t *crypt_select_key (crypt_key_t * keys,
       *forced_valid = 0;
       switch (mutt_menuLoop (menu)) {
         case OP_VERIFY_KEY:
-          verify_key (key_table[menu->current]);
+          verify_key (cryptkey_table[menu->current]);
           menu->redraw = REDRAW_FULL;
           break;
 
         case OP_VIEW_ID:
-          mutt_message ("%s", key_table[menu->current]->uid);
+          mutt_message ("%s", cryptkey_table[menu->current]->uid);
           break;
 
         case OP_GENERIC_SELECT_ENTRY:
           /* FIXME make error reporting more verbose - this should be
              easy because gpgme provides more information */
           if (option (OPTPGPCHECKTRUST)) {
-              if (!crypt_key_is_valid (key_table[menu->current])) {
-                  mutt_error _("This key can't be used: "
-                               "expired/disabled/revoked.");
+              if (cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE ) {
+                  mutt_error(_("This key can't be used: "
+                               "expired/disabled/revoked."));
                   break;
               }
           }
 
           if (option (OPTPGPCHECKTRUST) &&
-              (!crypt_id_is_valid (key_table[menu->current])
-               || !crypt_id_is_strong (key_table[menu->current]))) {
+              ((cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE)
+               || !crypt_id_is_strong (cryptkey_table[menu->current]))) {
               const char *warn_s;
               char buff[LONG_STRING];
 
-              if (key_table[menu->current]->flags & KEYFLAG_CANTUSE)
+              if (cryptkey_table[menu->current]->flags & KEYFLAG_CANTUSE)
                   s = N_("ID is expired/disabled/revoked.");
               else {
                   gpgme_validity_t val = GPGME_VALIDITY_UNKNOWN;
@@ -3302,8 +3057,8 @@ static crypt_key_t *crypt_select_key (crypt_key_t * keys,
 
                   warn_s = "??";
 
-                  uid = key_table[menu->current]->kobj->uids;
-                  for (j = 0; (j < key_table[menu->current]->idx) && uid;
+                  uid = cryptkey_table[menu->current]->kobj->uids;
+                  for (j = 0; (j < cryptkey_table[menu->current]->idx) && uid;
                        j++, uid = uid->next);
                   if (uid)
                       val = uid->validity;
@@ -3335,7 +3090,7 @@ static crypt_key_t *crypt_select_key (crypt_key_t * keys,
               }
           }
 
-          k = crypt_copy_key (key_table[menu->current]);
+          k = cryptkey_dup(cryptkey_table[menu->current]);
           done = 1;
           break;
 
@@ -3347,18 +3102,17 @@ static crypt_key_t *crypt_select_key (crypt_key_t * keys,
   }
 
   mutt_menuDestroy (&menu);
-  p_delete(&key_table);
+  p_delete(&cryptkey_table);
 
   set_option (OPTNEEDREDRAW);
 
   return k;
 }
 
-static crypt_key_t *crypt_getkeybyaddr (address_t * a, short abilities,
-                                        unsigned int app, int *forced_valid)
+static cryptkey_t *
+crypt_getkeybyaddr(address_t * a, int abilities, int app, int *forced_valid)
 {
     address_t *r, *p;
-    string_list_t *hints = NULL;
 
     int weak = 0;
     int invalid = 0;
@@ -3368,22 +3122,23 @@ static crypt_key_t *crypt_getkeybyaddr (address_t * a, short abilities,
     int this_key_has_invalid;
     int match;
 
-    crypt_key_t *keys, *k;
-    crypt_key_t *the_valid_key = NULL;
-    crypt_key_t *matches = NULL;
-    crypt_key_t **matches_endp = &matches;
+    cryptkey_t *keys, *k;
+    cryptkey_t *the_valid_key = NULL;
+    cryptkey_t *matches = NULL;
+    cryptkey_t **matches_endp = &matches;
 
     *forced_valid = 0;
 
-    if (a && a->mailbox)
-        hints = crypt_add_string_to_hints (hints, a->mailbox);
-    if (a && a->personal)
-        hints = crypt_add_string_to_hints (hints, a->personal);
-
-    mutt_message (_("Looking for keys matching \"%s\"..."), a->mailbox);
-    keys = get_candidates (hints, app, (abilities & KEYFLAG_CANSIGN));
+    {
+        string_array hints;
+        string_array_init(&hints); 
+        add_hints(&hints, a->mailbox);
+        add_hints(&hints, a->personal);
 
-    string_list_wipe(&hints);
+        mutt_message (_("Looking for keys matching \"%s\"..."), a->mailbox);
+        keys = get_candidates(&hints, app, (abilities & KEYFLAG_CANSIGN));
+        string_array_wipe(&hints);
+    }
 
     if (!keys)
         return NULL;
@@ -3425,408 +3180,352 @@ static crypt_key_t *crypt_getkeybyaddr (address_t * a, short abilities,
         address_list_wipe(&r);
 
         if (match) {
-            crypt_key_t *tmp;
+            cryptkey_t *tmp;
 
             if (!this_key_has_strong && this_key_has_invalid)
                 invalid = 1;
             if (!this_key_has_strong && this_key_has_weak)
                 weak = 1;
 
-            *matches_endp = tmp = crypt_copy_key (k);
+            *matches_endp = tmp = cryptkey_dup(k);
             matches_endp = &tmp->next;
             the_valid_key = tmp;
         }
     }
-
-    crypt_free_key (&keys);
+    key_list_wipe(&keys);
 
     if (matches) {
         if (the_valid_key && !multi && !weak
             && !(invalid && option (OPTPGPSHOWUNUSABLE))) {
-      /*
-       * There was precisely one strong match on a valid ID, there
-       * were no valid keys with weak matches, and we aren't
-       * interested in seeing invalid keys.
-       *
-       * Proceed without asking the user.
-       */
-      k = crypt_copy_key (the_valid_key);
-    }
-    else {
-      /*
-       * Else: Ask the user.
-       */
-      k = crypt_select_key (matches, a, NULL, app, forced_valid);
+            /*
+             * There was precisely one strong match on a valid ID, there
+             * were no valid keys with weak matches, and we aren't
+             * interested in seeing invalid keys.
+             *
+             * Proceed without asking the user.
+             */
+            k = cryptkey_dup(the_valid_key);
+        } else {
+            /*
+             * Else: Ask the user.
+             */
+            k = crypt_select_key (matches, a, NULL, app, forced_valid);
+        }
+        key_list_wipe(&matches);
+    } else {
+        k = NULL;
     }
-    crypt_free_key (&matches);
-  }
-  else
-    k = NULL;
 
-  return k;
+    return k;
 }
 
 
-static crypt_key_t *crypt_getkeybystr (const char *p, short abilities,
-                                       unsigned int app, int *forced_valid)
+static cryptkey_t *
+crypt_getkeybystr(const char *p, int abilities, int app, int *forced_valid)
 {
-  string_list_t *hints = NULL;
-  crypt_key_t *keys;
-  crypt_key_t *matches = NULL;
-  crypt_key_t **matches_endp = &matches;
-  crypt_key_t *k;
+  cryptkey_t *keys;
+  cryptkey_t *matches = NULL;
+  cryptkey_t **matches_endp = &matches;
+  cryptkey_t *k;
   int match;
 
   mutt_message (_("Looking for keys matching \"%s\"..."), p);
 
   *forced_valid = 0;
 
-  hints = crypt_add_string_to_hints (hints, p);
-  keys = get_candidates (hints, app, (abilities & KEYFLAG_CANSIGN));
-  string_list_wipe(&hints);
+  {
+      string_array hints;
+      string_array_init(&hints);
+      add_hints(&hints, p);
+      keys = get_candidates(&hints, app, (abilities & KEYFLAG_CANSIGN));
+      string_array_wipe(&hints);
+  }
 
   if (!keys)
     return NULL;
 
   for (k = keys; k; k = k->next) {
+    const char *s = crypt_keyid(k);
+
     if (abilities && !(k->flags & abilities))
       continue;
 
     match = 0;
 
-    if (!*p || !m_strcasecmp(p, crypt_keyid (k))
-        || (!m_strncasecmp(p, "0x", 2)
-            && !m_strcasecmp(p + 2, crypt_keyid (k)))
-        || (option (OPTPGPLONGIDS)
-            && !m_strncasecmp(p, "0x", 2)
-            && !m_strcasecmp(p + 2, crypt_keyid (k) + 8))
-        || m_stristr(k->uid, p)) {
-      crypt_key_t *tmp;
+    if (!*p || !m_strcasecmp(p, s)
+        || (!m_strncasecmp(p, "0x", 2) && !m_strcasecmp(p + 2, s))
+        || m_stristr(k->uid, p))
+    {
+      cryptkey_t *tmp;
 
-      *matches_endp = tmp = crypt_copy_key (k);
+      *matches_endp = tmp = cryptkey_dup(k);
       matches_endp = &tmp->next;
     }
   }
-
-  crypt_free_key (&keys);
+  key_list_wipe(&keys);
 
   if (matches) {
     k = crypt_select_key (matches, NULL, p, app, forced_valid);
-    crypt_free_key (&matches);
+    key_list_wipe(&matches);
     return k;
   }
 
   return NULL;
 }
 
-/* Display TAG as a prompt to ask for a key.  If WHATFOR is not null
-   use it as default and store it under that label as the next
-   default.  ABILITIES describe the required key abilities (sign,
-   encrypt) and APP the type of the requested key; ether S/MIME or
-   PGP.  Return a copy of the key or NULL if not found. */
-static crypt_key_t *
-crypt_ask_for_key(char *tag, char *whatfor, short abilities,
-                  unsigned int app, int *forced_valid)
+/* Display TAG as a prompt to ask for a key.
+ * ABILITIES describe the required key abilities (sign, encrypt) and APP the
+ * type of the requested key; ether S/MIME or PGP.
+ * Return a copy of the key or NULL if not found. */
+static cryptkey_t *
+crypt_ask_for_key(const char *tag, int abilities, int app, int *forced_valid)
 {
-  crypt_key_t *key;
-  char resp[STRING];
-  struct crypt_cache *l = NULL;
-  int dummy;
+    cryptkey_t *key;
+    char resp[STRING];
+    int dummy;
 
-  if (!forced_valid)
-    forced_valid = &dummy;
+    if (!forced_valid)
+        forced_valid = &dummy;
+    *forced_valid = 0;
 
-  mutt_clear_error ();
+    mutt_clear_error();
+    for (;;) {
+        resp[0] = 0;
+        if (mutt_get_field(tag, resp, sizeof(resp), M_CLEAR) != 0)
+            return NULL;
 
-  *forced_valid = 0;
-  resp[0] = 0;
-  if (whatfor) {
-    for (l = id_defaults; l; l = l->next)
-      if (!m_strcasecmp(whatfor, l->what)) {
-        m_strcpy(resp, sizeof(resp), NONULL(l->dflt));
-        break;
-      }
-  }
+        if (m_strisempty(resp))
+            return NULL;
 
-  for (;;) {
-    resp[0] = 0;
-    if (mutt_get_field (tag, resp, sizeof (resp), M_CLEAR) != 0)
-      return NULL;
+        if ((key = crypt_getkeybystr(resp, abilities, app, forced_valid)))
+            return key;
 
-    if (whatfor) {
-      if (l)
-        m_strreplace(&l->dflt, resp);
-      else {
-        l = p_new(struct crypt_cache, 1);
-        l->next = id_defaults;
-        id_defaults = l;
-        l->what = m_strdup(whatfor);
-        l->dflt = m_strdup(resp);
-      }
+        BEEP ();
     }
-
-    if ((key = crypt_getkeybystr (resp, abilities, app, forced_valid)))
-      return key;
-
-    BEEP ();
-  }
-  /* not reached */
 }
 
 /* This routine attempts to find the keyids of the recipients of a
    message.  It returns NULL if any of the keys can not be found.  */
-static char *find_keys (address_t * to, address_t * cc, address_t * bcc,
-                        unsigned int app)
+static char *find_keys(ENVELOPE *env, unsigned int app)
 {
-  char *keylist = NULL, *t;
-  const char *keyID;
-  ssize_t keylist_size = 0;
-  ssize_t keylist_used = 0;
-  address_t *tmp = NULL, *addr = NULL;
-  address_t **last = &tmp;
-  address_t *p, *q;
-  int i;
-  crypt_key_t *k_info, *key;
-  const char *fqdn = mutt_fqdn (1);
+    address_t *lst = NULL, *addr;
+    buffer_t *keylist = buffer_new();
 
-#if 0
-  *r_application = APPLICATION_PGP | APPLICATION_SMIME;
-#endif
-
-  for (i = 0; i < 3; i++) {
-    switch (i) {
-    case 0:
-      p = to;
-      break;
-    case 1:
-      p = cc;
-      break;
-    case 2:
-      p = bcc;
-      break;
-    default:
-      abort ();
-    }
-
-    *last = address_list_dup (p);
-    while (*last)
-      last = &((*last)->next);
-  }
-
-  rfc822_qualify(tmp, fqdn);
-  address_list_uniq(tmp);
-
-  for (p = tmp; p; p = p->next) {
-    char buf[LONG_STRING];
-    int forced_valid = 0;
-
-    q = p;
-    k_info = NULL;
+    {
+        address_t **last = &lst;
+        *last = address_list_dup(env->to);
+        last  = address_list_last(last);
+        *last = address_list_dup(env->cc);
+        last  = address_list_last(last);
+        *last = address_list_dup(env->bcc);
+
+        rfc822_qualify(lst, mutt_fqdn(1));
+        address_list_uniq(lst);
+    }
+
+    while ((addr = address_list_pop(&lst))) {
+        char buf[STRING];
+        int forced_valid = 0;
+        const char *keyID;
+        cryptkey_t *key = NULL;
+
+        if ((keyID = mutt_crypt_hook(addr))) {
+            int r;
+
+            snprintf(buf, sizeof(buf), _("Use keyID = \"%s\" for %s?"), keyID,
+                     addr->mailbox);
+            r = mutt_yesorno(buf, M_YES);
+
+            if (r == -1) {
+                address_list_wipe(&lst);
+                address_list_wipe(&addr);
+                buffer_delete(&keylist);
+                return NULL;
+            }
 
-    if ((keyID = mutt_crypt_hook (p)) != NULL) {
-      int r;
+            if (r == M_YES) {
+                address_t *a;
+                /* check for e-mail address */
+                if (strchr(keyID, '@') && (a = rfc822_parse_adrlist(NULL, keyID))) {
+                    rfc822_qualify(a, mutt_fqdn(1));
+                    address_list_wipe(&addr);
+                    addr = a;
+                } else {
+                    key = crypt_getkeybystr(keyID, KEYFLAG_CANENCRYPT, app,
+                                            &forced_valid);
+                }
+            }
+        }
 
-      snprintf (buf, sizeof (buf), _("Use keyID = \"%s\" for %s?"),
-                keyID, p->mailbox);
-      if ((r = mutt_yesorno (buf, M_YES)) == M_YES) {
-        /* check for e-mail address */
-        if ((t = strchr (keyID, '@')) &&
-            (addr = rfc822_parse_adrlist (NULL, keyID))) {
-          rfc822_qualify(addr, fqdn);
-          q = addr;
+        if (!key) {
+            key = crypt_getkeybyaddr(addr, KEYFLAG_CANENCRYPT, app, &forced_valid);
         }
-        else {
-          k_info = crypt_getkeybystr (keyID, KEYFLAG_CANENCRYPT,
-                                      app, &forced_valid);
+        if (!key) {
+            snprintf(buf, sizeof(buf), _("Enter keyID for %s: "), addr->mailbox);
+            key = crypt_ask_for_key(buf, KEYFLAG_CANENCRYPT, app,
+                                    &forced_valid);
+            if (!key) {
+                address_list_wipe(&lst);
+                address_list_wipe(&addr);
+                buffer_delete(&keylist);
+                return NULL;
+            }
         }
-      }
-      else if (r == -1) {
-        p_delete(&keylist);
-        address_list_wipe(&tmp);
-        address_list_wipe(&addr);
-        return NULL;
-      }
-    }
 
-    if (k_info == NULL
-        && (k_info = crypt_getkeybyaddr (q, KEYFLAG_CANENCRYPT,
-                                         app, &forced_valid)) == NULL) {
-      snprintf (buf, sizeof (buf), _("Enter keyID for %s: "), q->mailbox);
+        if (keylist->len)
+            buffer_addch(keylist, ' ');
+        buffer_addstr(keylist, "0x");
+        buffer_addstr(keylist, crypt_fpr(key));
+        if (forced_valid)
+            buffer_addch(keylist, '!');
 
-      if ((key = crypt_ask_for_key (buf, q->mailbox, KEYFLAG_CANENCRYPT, app,
-                                    &forced_valid)) == NULL)
-      {
-        p_delete(&keylist);
-        address_list_wipe(&tmp);
+        key_list_wipe(&key);
         address_list_wipe(&addr);
-        return NULL;
-      }
-    }
-    else
-      key = k_info;
-
-    {
-      const char *s = crypt_fpr (key);
-
-      keylist_size += m_strlen(s) + 4 + 1;
-      p_realloc(&keylist, keylist_size);
-      sprintf (keylist + keylist_used, "%s0x%s%s",
-               keylist_used ? " " : "", s, forced_valid ? "!" : "");
     }
-    keylist_used = m_strlen(keylist);
 
-    crypt_free_key (&key);
-    address_list_wipe(&addr);
-  }
-  address_list_wipe(&tmp);
-  return (keylist);
+    address_list_wipe(&lst);
+    return buffer_unwrap(&keylist);
 }
 
-int crypt_get_keys (HEADER * msg, char **keylist)
+int crypt_get_keys(HEADER *msg, char **keylist)
 {
-  /* Do a quick check to make sure that we can find all of the encryption
-   * keys if the user has requested this service.
-   */
+    /* Do a quick check to make sure that we can find all of the encryption
+     * keys if the user has requested this service.
+     */
 
-  *keylist = NULL;
+    *keylist = NULL;
 
-  if (msg->security & ENCRYPT) {
-    if (msg->security & APPLICATION_PGP) {
-      set_option(OPTPGPCHECKTRUST);
-      *keylist = find_keys(msg->env->to, msg->env->cc, msg->env->bcc,
-                           APPLICATION_PGP);
-      unset_option(OPTPGPCHECKTRUST);
-      if (!*keylist)
-          return -1;
-    }
+    if (msg->security & ENCRYPT) {
+        if (msg->security & APPLICATION_PGP) {
+            set_option(OPTPGPCHECKTRUST);
+            *keylist = find_keys(msg->env, APPLICATION_PGP);
+            unset_option(OPTPGPCHECKTRUST);
+            if (!*keylist)
+                return -1;
+        }
 
-    if (msg->security & APPLICATION_SMIME) {
-      *keylist = find_keys(msg->env->to, msg->env->cc, msg->env->bcc,
-                           APPLICATION_SMIME);
-      if (!*keylist)
-          return -1;
+        if (msg->security & APPLICATION_SMIME) {
+            *keylist = find_keys(msg->env, APPLICATION_SMIME);
+            if (!*keylist)
+                return -1;
+        }
     }
-  }
 
-  return (0);
+    return 0;
 }
 
 
 int crypt_send_menu (HEADER * msg, int *redraw, int is_smime)
 {
-  crypt_key_t *p;
-  char input_signas[STRING];
-  int choice;
+    cryptkey_t *p;
+    char buf[STRING];
+    int choice;
+
+    if (msg->security & APPLICATION_SMIME)
+        is_smime = 1;
+    if (msg->security & APPLICATION_PGP)
+        is_smime = 0;
+
+    choice = is_smime
+        ? mutt_multi_choice(_("S/MIME (e)ncrypt, (s)ign, sign (a)s, (b)oth, (p)gp or (c)lear?"),
+                            _("esabpc"))
+        : mutt_multi_choice(_("PGP (e)ncrypt, (s)ign, sign (a)s, (b)oth, s/(m)ime or (c)lear?"),
+                            _("esabmc"));
+
+    switch (choice) {
+      case 1:                      /* (e)ncrypt */
+        msg->security |= (is_smime ? SMIMEENCRYPT : PGPENCRYPT);
+        msg->security &= ~(is_smime ? SMIMESIGN : PGPSIGN);
+        break;
 
-  if (msg->security & APPLICATION_PGP)
-    is_smime = 0;
-  else if (msg->security & APPLICATION_SMIME)
-    is_smime = 1;
+      case 2:                      /* (s)ign */
+        msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
+        msg->security &= ~(is_smime ? SMIMEENCRYPT : PGPENCRYPT);
+        break;
 
-  if (is_smime)
-    choice =
-      mutt_multi_choice (_
-                         ("S/MIME (e)ncrypt, (s)ign, sign (a)s, (b)oth, (p)gp or (c)lear?"),
-                         _("esabpc"));
-  else
-    choice =
-      mutt_multi_choice (_
-                         ("PGP (e)ncrypt, (s)ign, sign (a)s, (b)oth, s/(m)ime or (c)lear?"),
-                         _("esabmc"));
-
-  switch (choice) {
-  case 1:                      /* (e)ncrypt */
-    msg->security |= (is_smime ? SMIMEENCRYPT : PGPENCRYPT);
-    msg->security &= ~(is_smime ? SMIMESIGN : PGPSIGN);
-    break;
+      case 3:                      /* sign (a)s */
+        p = crypt_ask_for_key(_("Sign as: "), KEYFLAG_CANSIGN,
+                              is_smime ?  APPLICATION_SMIME : APPLICATION_PGP,
+                              NULL);
+        if (p) {
+            snprintf(buf, sizeof(buf), "0x%s", crypt_keyid(p));
+            m_strreplace(is_smime ? &SmimeDefaultKey : &PgpSignAs, buf);
+            key_list_wipe(&p);
+            msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
+        }
+        *redraw = REDRAW_FULL;
+        break;
 
-  case 2:                      /* (s)ign */
-    msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
-    msg->security &= ~(is_smime ? SMIMEENCRYPT : PGPENCRYPT);
-    break;
+      case 4:                      /* (b)oth */
+        if (is_smime) {
+            msg->security = SMIMEENCRYPT | SMIMESIGN;
+        } else {
+            msg->security = PGPENCRYPT | PGPSIGN;
+        }
+        break;
 
-  case 3:                      /* sign (a)s */
-    if ((p = crypt_ask_for_key (_("Sign as: "), NULL, KEYFLAG_CANSIGN,
-                                is_smime ? APPLICATION_SMIME :
-                                APPLICATION_PGP, NULL)))
-    {
-      snprintf (input_signas, sizeof (input_signas), "0x%s", crypt_keyid (p));
-      m_strreplace(is_smime ? &SmimeDefaultKey : &PgpSignAs,
-                        input_signas);
-      crypt_free_key (&p);
+      case 5:                      /* (p)gp or s/(m)ime */
+        is_smime = !is_smime;
+        break;
 
-      msg->security |= (is_smime ? SMIMESIGN : PGPSIGN);
+      case 6:                      /* (c)lear */
+        return msg->security = 0;
     }
-    *redraw = REDRAW_FULL;
-    break;
-
-  case 4:                      /* (b)oth */
-    msg->security =
-      (is_smime ? (SMIMEENCRYPT | SMIMESIGN) : (PGPENCRYPT | PGPSIGN));
-    break;
 
-  case 5:                      /* (p)gp or s/(m)ime */
-    is_smime = !is_smime;
-    break;
-
-  case 6:                      /* (c)lear */
-    return msg->security = 0;
-  }
-
-  if (is_smime) {
-    msg->security &= ~APPLICATION_PGP;
-    msg->security |= APPLICATION_SMIME;
-  } else {
-    msg->security &= ~APPLICATION_SMIME;
-    msg->security |= APPLICATION_PGP;
-  }
+    if (is_smime) {
+        msg->security &= ~APPLICATION_PGP;
+        msg->security |= APPLICATION_SMIME;
+    } else {
+        msg->security &= ~APPLICATION_SMIME;
+        msg->security |= APPLICATION_PGP;
+    }
 
-  return msg->security;
+    return msg->security;
 }
 
-int crypt_smime_verify_sender (HEADER * h)
+int crypt_smime_verify_sender(HEADER *h)
 {
-  address_t *sender = NULL;
-  unsigned int ret = 1;
+    address_t *sender = NULL;
+    unsigned int ret = 1;
 
-  if (h->env->from) {
-    h->env->from = mutt_expand_aliases (h->env->from);
-    sender = h->env->from;
-  }
-  else if (h->env->sender) {
-    h->env->sender = mutt_expand_aliases (h->env->sender);
-    sender = h->env->sender;
-  }
+    if (h->env->from) {
+        h->env->from = mutt_expand_aliases(h->env->from);
+        sender = h->env->from;
+    } else if (h->env->sender) {
+        h->env->sender = mutt_expand_aliases (h->env->sender);
+        sender = h->env->sender;
+    }
 
-  if (sender) {
-    if (signature_key) {
-      gpgme_key_t key = signature_key;
-      gpgme_user_id_t uid = NULL;
-      int sender_length = 0;
-      int uid_length = 0;
-
-      sender_length = m_strlen(sender->mailbox);
-      for (uid = key->uids; uid && ret; uid = uid->next) {
-        uid_length = m_strlen(uid->email);
-        if (1 && (uid->email[0] == '<')
-            && (uid->email[uid_length - 1] == '>')
-            && (uid_length == sender_length + 2)
-            && (!m_strncmp (uid->email + 1, sender->mailbox, sender_length)))
-          ret = 0;
-      }
+    if (!sender) {
+        mutt_any_key_to_continue ("Failed to figure out sender");
+        goto end;
     }
-    else
-      mutt_any_key_to_continue ("Failed to verify sender");
-  }
-  else
-    mutt_any_key_to_continue ("Failed to figure out sender");
 
-  if (signature_key) {
-    gpgme_key_release (signature_key);
-    signature_key = NULL;
-  }
+    if (signature_key) {
+        gpgme_key_t key = signature_key;
+        gpgme_user_id_t uid = NULL;
+        int sender_length = 0;
+        int uid_length = 0;
+
+        sender_length = m_strlen(sender->mailbox);
+        for (uid = key->uids; uid && ret; uid = uid->next) {
+            uid_length = m_strlen(uid->email);
+            if (1 && (uid->email[0] == '<')
+                && (uid->email[uid_length - 1] == '>')
+                && (uid_length == sender_length + 2)
+                && (!m_strncmp (uid->email + 1, sender->mailbox, sender_length)))
+                ret = 0;
+        }
+    } else {
+        mutt_any_key_to_continue ("Failed to verify sender");
+    }
 
-  return ret;
+  end:
+    if (signature_key) {
+        gpgme_key_unref(signature_key);
+        signature_key = NULL;
+    }
+    return ret;
 }
 
 static void crypt_invoke_import(FILE *stream, int smime)
@@ -3877,43 +3576,17 @@ static void pgp_extract_keys_from_attachment(FILE * fp, BODY * top)
 
 void crypt_pgp_extract_keys_from_attachment_list(FILE * fp, int tag, BODY * top)
 {
-  mutt_endwin (NULL);
-  set_option (OPTDONTHANDLEPGPKEYS);
-
-  for (; top; top = top->next) {
-    if (!tag || top->tagged)
-      pgp_extract_keys_from_attachment (fp, top);
-
-    if (!tag)
-      break;
-  }
-
-  unset_option (OPTDONTHANDLEPGPKEYS);
-}
-
-
-/* TODO */
-
-/* fixme: needs documentation. */
-void crypt_pgp_invoke_getkeys (address_t * addr)
-{
-}
-
-/* Generate a PGP public key attachment. */
-BODY *crypt_pgp_make_key_attachment (char *tempf)
-{
-    return NULL;
-}
+    mutt_endwin (NULL);
 
-/* S/MIME */
+    for (; top; top = top->next) {
+        if (!tag || top->tagged)
+            pgp_extract_keys_from_attachment (fp, top);
 
-/* fixme: Needs documentation. */
-void crypt_smime_getkeys (ENVELOPE * env)
-{
+        if (!tag)
+            break;
+    }
 }
 
-/***************************************************************************/
-
 void crypt_invoke_message (int type)
 {
     if (type & APPLICATION_PGP) {
@@ -4125,7 +3798,6 @@ void crypt_extract_keys_from_messages(HEADER * h)
         return;
     }
 
-    set_option(OPTDONTHANDLEPGPKEYS);
     if (!h) {
         int i;
         for (i = 0; i < Context->vcount; i++) {
@@ -4136,35 +3808,27 @@ void crypt_extract_keys_from_messages(HEADER * h)
     } else {
         extract_keys_aux(tmpfp, h);
     }
-    unset_option(OPTDONTHANDLEPGPKEYS);
     m_fclose(&tmpfp);
 
     if (isendwin())
         mutt_any_key_to_continue(NULL);
 }
 
-
-
-static void crypt_fetch_signatures (BODY ***signatures, BODY * a, int *n)
+static void crypt_fetch_signatures(BODY ***signatures, BODY * a, int *n)
 {
-  for (; a; a = a->next) {
-    if (a->type == TYPEMULTIPART)
-      crypt_fetch_signatures (signatures, a->parts, n);
-    else {
-      if ((*n % 5) == 0)
-        p_realloc(signatures, *n + 6);
+    for (; a; a = a->next) {
+        if (a->type == TYPEMULTIPART) {
+            crypt_fetch_signatures(signatures, a->parts, n);
+        } else {
+            if ((*n % 5) == 0)
+                p_realloc(signatures, *n + 6);
 
-      (*signatures)[(*n)++] = a;
+            (*signatures)[(*n)++] = a;
+        }
     }
-  }
 }
 
-
-/*
- * This routine verifies a  "multipart/signed"  body.
- */
-
-int mutt_signed_handler (BODY * a, STATE * s)
+int mutt_signed_handler(BODY *a, STATE *s)
 {
   unsigned major, minor;
   char *protocol;