]> granicus.if.org Git - mutt/commitdiff
gpgme integration. See documentation for $crypt_use_gpgme,
authorWerner Koch <wk@gnupg.org>
Fri, 28 Jan 2005 13:17:22 +0000 (13:17 +0000)
committerWerner Koch <wk@gnupg.org>
Fri, 28 Jan 2005 13:17:22 +0000 (13:17 +0000)
and http://www.gnupg.org/aegypten2/.

Makefile.am
configure.in
crypt-gpgme.c [new file with mode: 0644]
crypt-gpgme.h [new file with mode: 0644]
crypt-mod-pgp-gpgme.c [new file with mode: 0644]
crypt-mod-smime-gpgme.c [new file with mode: 0644]
cryptglue.c
functions.h

index 69585c53f2b040e196f903fe1dea5b07188f3da8..d1d740d5b9293137e4c6aaaa92e9d882447f80d6 100644 (file)
@@ -31,7 +31,7 @@ mutt_SOURCES = $(BUILT_SOURCES) \
        url.c ascii.c mutt_idna.c crypt-mod.c crypt-mod.h
 
 mutt_LDADD = @MUTT_LIB_OBJECTS@ @LIBOBJS@ $(LIBIMAP) $(MUTTLIBS) \
-       $(INTLLIBS) $(LIBICONV) 
+       $(INTLLIBS) $(LIBICONV)  $(LIBGPGME_LIBS)
 
 mutt_DEPENDENCIES = @MUTT_LIB_OBJECTS@ @LIBOBJS@ $(LIBIMAPDEPS) \
        $(INTLDEPS)
@@ -53,7 +53,7 @@ DEFS=-DPKGDATADIR=\"$(pkgdatadir)\" -DSYSCONFDIR=\"$(sysconfdir)\" \
        -DBINDIR=\"$(bindir)\" -DMUTTLOCALEDIR=\"$(datadir)/locale\" \
        -DHAVE_CONFIG_H=1
 
-INCLUDES=-I. -I$(top_srcdir) $(IMAP_INCLUDES) -Iintl
+INCLUDES=-I. -I$(top_srcdir) $(IMAP_INCLUDES) $(LIBGPGME_CFLAGS) -Iintl
 
 CPPFLAGS=@CPPFLAGS@ -I$(includedir)
 
@@ -63,7 +63,8 @@ EXTRA_mutt_SOURCES = account.c md5c.c mutt_sasl.c mutt_socket.c mutt_ssl.c \
        pgplib.c sha1.c pgpmicalg.c gnupgparse.c resize.c dotlock.c remailer.c \
        browser.h mbyte.h remailer.h url.h mutt_ssl_nss.c \
        crypt-mod-pgp-classic.c crypt-mod-smime-classic.c \
-       pgppacket.c mutt_idna.h hcache.c mutt_ssl_gnutls.c
+       pgppacket.c mutt_idna.h hcache.c mutt_ssl_gnutls.c \
+       crypt-gpgme.c crypt-mod-pgp-gpgme.c crypt-mod-smime-gpgme.c
 
 EXTRA_DIST = COPYRIGHT GPL OPS OPS.PGP OPS.CRYPT OPS.SMIME TODO \
        configure acconfig.h account.h \
index 791f1eaf6f9d90d9f1a1824d8154fd7ca510afc5..445125246b3819deaa86ce782f580ee7934cdec6 100644 (file)
@@ -42,6 +42,8 @@ AC_CHECK_TOOL(AR, ar, ar)
 AC_C_INLINE
 AC_C_CONST
 
+AC_SYS_LARGEFILE
+
 AC_PATH_PROG(DBX, dbx, no)
 AC_PATH_PROG(GDB, gdb, no)
 AC_PATH_PROG(SDB, sdb, no)
@@ -96,6 +98,33 @@ if test -f $srcdir/EXPORTABLE ; then
 else
         SUBVERSION="i"
 
+       AC_ARG_ENABLE(gpgme, [  --enable-gpgme           Enable GPGME support],
+       [       if test x$enableval = xyes; then
+                       have_gpgme=yes
+               fi
+        ])
+       AC_ARG_WITH(gpgme-prefix, [  --with-gpgme-prefix=PFX    prefix where GPGME is installed (optional)],
+       gpgme_config_prefix="$withval", gpgme_config_prefix="")
+
+        if test x$have_gpgme = xyes; then
+               if test x$gpgme_config_prefix != x; then
+                       GPGME_CONFIG="$gpgme_config_prefix/bin/gpgme-config"
+               else
+                       AC_PATH_PROG(GPGME_CONFIG, gpgme-config, no)
+               fi
+               if test "x$GPGME_CONFIG" = "xno"; then
+                       AC_MSG_ERROR([GPGME not found])
+               else
+                       LIBGPGME_CFLAGS=`$GPGME_CONFIG --cflags`
+                       LIBGPGME_LIBS=`$GPGME_CONFIG --libs`
+                       MUTT_LIB_OBJECTS="$MUTT_LIB_OBJECTS crypt-gpgme.o crypt-mod-pgp-gpgme.o crypt-mod-smime-gpgme.o"
+                       AC_DEFINE(CRYPT_BACKEND_GPGME, 1,
+                                 [Defined, if GPGME support is enabled])
+               fi
+       fi
+       AC_SUBST(LIBGPGME_CFLAGS)
+       AC_SUBST(LIBGPGME_LIBS)
+
         AC_ARG_ENABLE(pgp, [  --disable-pgp              Disable PGP support],
         [       if test x$enableval = xno ; then
                         have_pgp=no
diff --git a/crypt-gpgme.c b/crypt-gpgme.c
new file mode 100644 (file)
index 0000000..65c0582
--- /dev/null
@@ -0,0 +1,4252 @@
+/* crypt-gpgme.c - GPGME based crypto operations
+ * Copyright (C) 1996,1997 Michael R. Elkins <me@cs.hmc.edu>
+ * Copyright (C) 1998,1999,2000 Thomas Roessler <roessler@guug.de>
+ * Copyright (C) 2001  Thomas Roessler <roessler@guug.de>
+ *                     Oliver Ehli <elmy@acm.org>
+ * Copyright (C) 2002, 2003, 2004 g10 Code GmbH
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ * 
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ * 
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ */
+
+#include <config.h>
+
+#ifdef CRYPT_BACKEND_GPGME
+
+#include "mutt.h"
+#include "mutt_crypt.h"
+#include "mutt_menu.h"
+#include "mutt_curses.h"
+#include "mime.h"
+#include "copy.h"
+#include "pager.h"
+#include "sort.h"
+
+#include <sys/wait.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <gpgme.h>
+
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+#ifdef HAVE_LANGINFO_D_T_FMT
+#include <langinfo.h>
+#endif
+
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+
+#ifdef HAVE_SYS_RESOURCE_H
+# include <sys/resource.h>
+#endif
+
+/*
+ * Helper macros.
+ */
+#define digitp(p)   (*(p) >= '0' && *(p) <= '9')
+#define hexdigitp(a) (digitp (a)                     \
+                      || (*(a) >= 'A' && *(a) <= 'F')  \
+                      || (*(a) >= 'a' && *(a) <= 'f'))
+#define xtoi_1(p)   (*(p) <= '9'? (*(p)- '0'): \
+                     *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
+#define xtoi_2(p)   ((xtoi_1(p) * 16) + xtoi_1((p)+1))
+
+/* Values used for comparing addresses. */
+#define CRYPT_KV_VALID    1
+#define CRYPT_KV_ADDR     2
+#define CRYPT_KV_STRING   4
+#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;
+};
+
+/* 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 crypt_entry
+{
+  size_t num;
+  crypt_key_t *key;
+} crypt_entry_t;
+
+
+static struct crypt_cache *id_defaults = NULL;
+static gpgme_key_t signature_key = NULL;
+
+/*
+ * General helper functions.
+ */
+
+/* return true when S pints to a didgit or letter. */
+static int
+digit_or_letter (const unsigned char *s)
+{
+  return ( (*s >= '0' && *s < '9')
+           || (*s >= 'A' && *s <= 'Z')
+           || (*s >= 'a' && *s <= 'z'));
+}
+
+
+/* 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, size_t len)
+{
+  char *tstr;
+
+  tstr = safe_malloc (len+1);
+  memcpy (tstr, buf, len);
+  tstr[len] = 0;
+  mutt_convert_string (&tstr, "utf-8", Charset, M_ICONV_HOOK_FROM);
+  fputs (tstr, fp);
+  FREE (&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)
+{
+  const char *s = "????????";
+
+  if (k->kobj && k->kobj->subkeys)
+    {
+      s = k->kobj->subkeys->keyid;
+      if ((! option (OPTPGPLONGIDS)) && (strlen (s) == 16))
+       /* Return only the short keyID.  */
+       s += 8;
+    }
+
+  return s;
+}
+
+/* Return the hexstring fingerprint from the key K. */
+static const char *crypt_fpr (crypt_key_t *k)
+{
+  const char *s = "";
+
+  if (k->kobj && k->kobj->subkeys)
+    s = k->kobj->subkeys->fpr;
+
+  return s;
+}
+
+/* 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';
+
+  if (!(flags & KEYFLAG_CANSIGN))
+    buff[1] = '-';
+  else if (flags & KEYFLAG_PREFER_ENCRYPTION)
+    buff[1] = '.';
+  else
+    buff[1] = 's';
+
+  buff[2] = '\0';
+
+  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 
+    return ' ';
+}
+
+/* Return a copy of KEY. */
+static crypt_key_t *crypt_copy_key (crypt_key_t *key)
+{
+  crypt_key_t *k;
+
+  k = safe_calloc (1, sizeof *k);
+  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;
+      FREE (&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)
+{
+  gpgme_validity_t val = GPGME_VALIDITY_UNKNOWN;
+  gpgme_user_id_t uid = NULL;
+  unsigned int is_strong = 0;
+  unsigned 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;
+
+    case GPGME_VALIDITY_FULL:
+    case GPGME_VALIDITY_ULTIMATE:
+      is_strong = 1;
+      break;
+    }
+
+  return is_strong;
+}
+
+/* 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 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 *addr, ADDRESS *u_addr,
+                                  crypt_key_t *key)
+{
+  int rv = 0;
+  
+  if (crypt_id_is_valid (key))
+    rv |= CRYPT_KV_VALID;
+
+  if (crypt_id_is_strong (key))
+    rv |= CRYPT_KV_STRONGID;
+  
+  if (addr->mailbox && u_addr->mailbox
+      && mutt_strcasecmp (addr->mailbox, u_addr->mailbox) == 0)
+    rv |= CRYPT_KV_ADDR;
+    
+  if (addr->personal && u_addr->personal
+      && mutt_strcasecmp (addr->personal, u_addr->personal) == 0)
+    rv |= CRYPT_KV_STRING;
+  
+  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)
+{
+  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);
+    }
+
+  if (for_smime)
+    {
+      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)
+{
+  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;
+}
+
+/* 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)
+{
+  char tempfile[_POSIX_PATH_MAX];
+  FILE *fptmp;
+  int err = 0;
+  gpgme_data_t data;
+  
+  mutt_mktemp (tempfile);
+  fptmp = safe_fopen (tempfile, "w+");
+  if (!fptmp)
+    {
+      mutt_perror (tempfile);
+      return NULL;
+    }
+
+  mutt_write_mime_header (a, fptmp);
+  fputc ('\n', fptmp);
+  mutt_write_mime_body (a, fptmp);
+
+  if (convert)
+    {
+      int c, hadcr = 0;
+      unsigned char buf[1];
+
+      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);
+                }
+                  
+              hadcr = 0;
+            }
+          /* FIXME: This is quite suboptimal */
+          buf[0] = c;
+          gpgme_data_write (data, buf, 1);
+        }
+      fclose(fptmp);
+      gpgme_data_seek (data, 0, SEEK_SET);
+    }
+  else
+    {
+      fclose(fptmp);
+      err = gpgme_data_new_from_file (&data, tempfile, 1);
+    }
+  unlink (tempfile);
+  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)
+{
+  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;
+    }
+
+  return data;
+}
+
+/* Write a GPGME data object to the stream 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;
+    }
+
+  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;
+}
+
+/* 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)
+{
+  int err;
+  char tempfile[_POSIX_PATH_MAX];
+  FILE *fp;
+  size_t nread = 0;
+
+  mutt_mktemp (tempfile);
+  fp = safe_fopen (tempfile, "w+");
+  if (!fp)
+    {
+      mutt_perror (tempfile);
+      return NULL;
+    }
+
+  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 (tempfile);
+              fclose (fp);
+              unlink (tempfile);
+              return NULL;
+            }
+        }
+    }
+  if (ret_fp)
+    rewind (fp);
+  else
+    fclose (fp);
+  if (nread == -1)
+    {
+      mutt_error ("error reading data object: %s\n", gpgme_strerror (err));
+      unlink (tempfile);
+      fclose (fp);
+      return NULL;
+    }
+  if (ret_fp)
+    *ret_fp = fp;
+  return safe_strdup (tempfile);
+}
+
+
+/* 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 < sizeof(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_key (context, buf, &key, 0);
+               if (! err)
+                 key->uids->validity = GPGME_VALIDITY_FULL;
+               buf[i-1] = '!';
+             }
+           else
+             err = gpgme_get_key (context, buf, &key, 0);
+
+           if (! err)
+             {
+               safe_realloc (&rset, sizeof (*rset) * (rset_n + 1));
+               rset[rset_n++] = key;
+             }
+           else
+             {
+               mutt_error ("error adding recipient `%s': %s\n",
+                           buf, gpgme_strerror (err));
+               FREE (&rset);
+               return NULL;
+             }
+         }
+      } while (*s);
+    }
+
+  /* NULL terminate.  */
+  safe_realloc (&rset, sizeof (*rset) * (rset_n + 1));
+  rset[rset_n++] = NULL;
+  
+  if (context)
+    gpgme_release (context);
+
+  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)
+{
+  char *signid = for_smime ? SmimeDefaultKey: PgpSignAs;
+  gpgme_error_t err;
+  gpgme_ctx_t listctx;
+  gpgme_key_t key, key2;
+
+  if (!signid || !*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 specfication of secret key `%s'\n"),
+                  signid);
+      return -1;
+    }
+  gpgme_op_keylist_end (listctx);
+  gpgme_release (listctx);
+
+  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;
+}
+
+
+/* Encrypt the gpgme data object PLAINTEXT to the recipients in RSET
+   and return an allocated filename to a temporary file containing the
+   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)
+{
+  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);
+
+  ciphertext = create_gpgme_data ();
+
+  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);
+  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);
+
+  outfile = data_object_to_tempfile (ciphertext, NULL);
+  gpgme_data_release (ciphertext);
+  return outfile;
+}
+
+/* Find the "micalg" parameter from the last Gpgme operation on
+   context CTX.  It is expected that this operation was a sign
+   operation.  Return the algorithm name as a C string in buffer BUF
+   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, size_t buflen)
+{
+  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)
+       {
+         strncpy (buf, algorithm_name, buflen - 1);
+         buf[buflen - 1] = 0;
+       }
+    }
+
+  return *buf? 0:-1;
+}
+
+static void print_time(time_t t, STATE *s)
+{
+  char p[STRING];
+
+  setlocale (LC_TIME, "");
+#ifdef HAVE_LANGINFO_D_T_FMT
+  strftime (p, sizeof (p), nl_langinfo (D_T_FMT), localtime (&t));
+#else
+  strftime (p, sizeof (p), "%c", localtime (&t));
+#endif
+  setlocale (LC_TIME, "C");
+  state_attach_puts (p, s);
+}
+
+/* 
+ * Implementation of `sign_message'.
+ */
+
+/* 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)
+{
+  BODY *t;
+  char *sigfile;
+  int err = 0;
+  char buf[100];
+  gpgme_ctx_t ctx;
+  gpgme_data_t message, signature;
+
+  convert_to_7bit (a); /* Signed data _must_ be in 7-bit format. */
+
+  message = body_to_data_object (a, 1);
+  if (!message)
+     return NULL;
+  signature = create_gpgme_data ();
+
+  ctx = create_gpgme_context (use_smime);
+  if (!use_smime)
+    gpgme_set_armor (ctx, 1);
+
+  if (set_signer (ctx, use_smime))
+    {
+      gpgme_data_release (signature);
+      gpgme_release (ctx);
+      return NULL;
+    }
+
+  err = gpgme_op_sign (ctx, message, signature, GPGME_SIG_MODE_DETACH );
+  mutt_need_hard_redraw ();
+  gpgme_data_release (message);
+  if (err)
+    {
+      gpgme_data_release (signature);
+      gpgme_release (ctx);
+      mutt_error ("error signing data: %s\n", gpgme_strerror (err));
+      return NULL;
+    }
+
+  sigfile = data_object_to_tempfile (signature, NULL);
+  gpgme_data_release (signature);
+  if (!sigfile)
+    {
+      gpgme_release (ctx);
+      return NULL;
+    }
+
+  t = mutt_new_body ();
+  t->type = TYPEMULTIPART;
+  t->subtype = safe_strdup ("signed");
+  t->encoding = ENC7BIT;
+  t->use_disp = 0;
+  t->disposition = DISPINLINE;
+
+  mutt_generate_boundary (&t->parameter);
+  mutt_set_parameter ("protocol",
+                      use_smime? "application/pkcs7-signature"
+                               : "application/pgp-signature",
+                      &t->parameter);
+  /* Get the micalg from gpgme.  Old gpgme versions don't support this
+     for S/MIME so we assume sha-1 in this case. */
+  if (!get_micalg (ctx, buf, sizeof buf))
+    mutt_set_parameter ("micalg", buf, &t->parameter);
+  else if (use_smime)
+    mutt_set_parameter ("micalg", "sha1", &t->parameter);
+  gpgme_release (ctx);
+
+  t->parts = a;
+  a = t;
+
+  t->parts->next = mutt_new_body ();
+  t = t->parts->next;
+  t->type = TYPEAPPLICATION;
+  if (use_smime)
+    {
+      t->subtype = safe_strdup ("pkcs7-signature");
+      mutt_set_parameter ("name", "smime.p7s", &t->parameter);
+      t->encoding = ENCBASE64; 
+      t->use_disp = 1;
+      t->disposition = DISPATTACH;
+      t->d_filename = safe_strdup ("smime.p7s");
+    }
+  else
+    {
+      t->subtype = safe_strdup ("pgp-signature");
+      t->use_disp = 0;
+      t->disposition = DISPINLINE;
+      t->encoding = ENC7BIT;
+    }
+  t->filename = sigfile;
+  t->unlink = 1; /* ok to remove this file after sending. */
+
+  return a;
+}
+
+
+BODY *pgp_gpgme_sign_message (BODY *a)
+{
+  return sign_message (a, 0);
+}
+
+BODY *smime_gpgme_sign_message (BODY *a)
+{
+  return sign_message (a, 1);
+}
+
+/*
+ * 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.  */
+BODY *pgp_gpgme_encrypt_message (BODY *a, char *keylist, int sign)
+{
+  char *outfile = NULL;
+  BODY *t;
+  gpgme_key_t *rset = NULL;
+  gpgme_data_t plaintext;
+  
+  rset = create_recipient_set (keylist, GPGME_PROTOCOL_OpenPGP);
+  if (!rset)
+    return NULL;
+  
+  if (sign)
+    convert_to_7bit (a);
+  plaintext = body_to_data_object (a, 0);
+  if (!plaintext)
+    {
+      FREE (&rset);
+      return NULL;
+    }
+  
+  outfile = encrypt_gpgme_object (plaintext, rset, 0, sign);
+  gpgme_data_release (plaintext);
+  FREE (&rset);
+  if (!outfile)
+      return NULL;
+
+  t = mutt_new_body ();
+  t->type = TYPEMULTIPART;
+  t->subtype = safe_strdup ("encrypted");
+  t->encoding = ENC7BIT;
+  t->use_disp = 0;
+  t->disposition = DISPINLINE;
+
+  mutt_generate_boundary(&t->parameter);
+  mutt_set_parameter("protocol", "application/pgp-encrypted", &t->parameter);
+  
+  t->parts = mutt_new_body ();
+  t->parts->type = TYPEAPPLICATION;
+  t->parts->subtype = safe_strdup ("pgp-encrypted");
+  t->parts->encoding = ENC7BIT;
+
+  t->parts->next = mutt_new_body ();
+  t->parts->next->type = TYPEAPPLICATION;
+  t->parts->next->subtype = safe_strdup ("octet-stream");
+  t->parts->next->encoding = ENC7BIT;
+  t->parts->next->filename = outfile;
+  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 = safe_strdup ("msg.asc"); /* non pgp/mime
+                                                           can save */
+
+  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.  */
+BODY *smime_gpgme_build_smime_entity (BODY *a, char *keylist)
+{
+  char *outfile = NULL;
+  BODY *t;
+  gpgme_key_t *rset = NULL;
+  gpgme_data_t plaintext;
+
+  rset = create_recipient_set (keylist, GPGME_PROTOCOL_CMS);
+  if (!rset)
+    return NULL;
+
+  plaintext = body_to_data_object (a, 0);
+  if (!plaintext)
+    {
+      FREE (&rset);
+      return NULL;
+    }
+
+  outfile = encrypt_gpgme_object (plaintext, rset, 1, 0);
+  gpgme_data_release (plaintext);
+  FREE (&rset);
+  if (!outfile) 
+      return NULL;
+
+  t = mutt_new_body ();
+  t->type = TYPEAPPLICATION;
+  t->subtype = safe_strdup ("pkcs7-mime");
+  mutt_set_parameter ("name", "smime.p7m", &t->parameter);
+  mutt_set_parameter ("smime-type", "enveloped-data", &t->parameter);
+  t->encoding = ENCBASE64;  /* The output of OpenSSL SHOULD be binary */
+  t->use_disp = 1;
+  t->disposition = DISPATTACH;
+  t->d_filename = safe_strdup ("smime.p7m");
+  t->filename = outfile;
+  t->unlink = 1; /*delete after sending the message */
+  t->parts=0;
+  t->next=0;
+  
+  return t;
+}
+
+
+/* 
+ * Implementation of `verify_one'.
+ */
+
+/* Display the common attributes of the signature summary SUM.
+   Return 1 if there is is a severe warning.
+ */
+static int show_sig_summary (unsigned long sum,
+                              gpgme_ctx_t ctx, gpgme_key_t key, int idx,
+                              STATE *s)
+{
+  int severe = 0;
+
+  if ((sum & GPGME_SIGSUM_KEY_REVOKED))
+    {
+      state_attach_puts (_("Warning: One of the keys has been revoked\n"),s);
+      severe = 1;
+    }
+
+  if ((sum & GPGME_SIGSUM_KEY_EXPIRED))
+    {
+      time_t at = key->subkeys->expires ? key->subkeys->expires : 0;
+      if (at)
+        {
+          state_attach_puts (_("Warning: The key used to create the "
+                               "signature expired at: "), s);
+          print_time (at , s);
+          state_attach_puts ("\n", s);
+        }
+      else
+        state_attach_puts (_("Warning: At least one certification key "
+                             "has expired\n"), s);
+    }
+
+  if ((sum & GPGME_SIGSUM_SIG_EXPIRED))
+    {
+      gpgme_verify_result_t result;
+      gpgme_signature_t sig;
+      unsigned int i;
+      
+      result = gpgme_op_verify_result (ctx);
+
+      for (sig = result->signatures, i = 0; sig && (i < idx);
+           sig = sig->next, i++)
+        ;
+      
+      state_attach_puts (_("Warning: The signature expired at: "), s);
+      print_time (sig ? sig->exp_timestamp : 0, s);
+      state_attach_puts ("\n", s);
+    }
+
+  if ((sum & GPGME_SIGSUM_KEY_MISSING))
+    state_attach_puts (_("Can't verify due to a missing "
+                         "key or certificate\n"), s);
+
+  if ((sum & GPGME_SIGSUM_CRL_MISSING))
+    {
+      state_attach_puts (_("The CRL is not available\n"), s);
+      severe = 1;
+    }
+
+  if ((sum & GPGME_SIGSUM_CRL_TOO_OLD))
+    {
+      state_attach_puts (_("Available CRL is too old\n"), s);
+      severe = 1;
+    }
+
+  if ((sum & GPGME_SIGSUM_BAD_POLICY))
+    state_attach_puts (_("A policy requirement was not met\n"), s);
+
+  if ((sum & GPGME_SIGSUM_SYS_ERROR))
+    {
+      const char *t0 = NULL, *t1 = NULL;
+      gpgme_verify_result_t result;
+      gpgme_signature_t sig;
+      unsigned int i;
+
+      state_attach_puts (_("A system error occured"), s );
+
+      /* Try to figure out some more detailed system error information. */
+      result = gpgme_op_verify_result (ctx);
+      for (sig = result->signatures, i = 0; sig && (i < idx);
+           sig = sig->next, i++)
+        ;
+      if (sig)
+       {
+         t0 = "";
+         t1 = sig->wrong_key_usage ? "Wrong_Key_Usage" : "";
+       }
+
+      if (t0 || t1)
+        {
+          state_attach_puts (": ", s);
+          if (t0)
+              state_attach_puts (t0, s);
+          if (t1 && !(t0 && !strcmp (t0, t1)))
+            {
+              if (t0)
+                state_attach_puts (",", s);
+              state_attach_puts (t1, s);
+            }
+        }
+      state_attach_puts ("\n", s);
+    }
+
+  return severe;
+}
+
+
+static void show_fingerprint (gpgme_key_t key, STATE *state)
+{
+  const char *s;
+  int i, is_pgp;
+  char *buf, *p;
+  const char *prefix = _("Fingerprint: ");
+
+  if (!key)
+    return;
+  s = key->subkeys ? key->subkeys->fpr : NULL;
+  if (!s)
+    return;
+  is_pgp = (key->protocol == GPGME_PROTOCOL_OpenPGP);
+
+  buf = safe_malloc ( strlen (prefix) + strlen(s) * 4 + 2 );
+  strcpy (buf, prefix); /* __STRCPY_CHECKED__ */
+  p = buf + strlen (buf);
+  if (is_pgp && strlen (s) == 40)
+    {  /* PGP v4 style formatted. */
+      for (i=0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++)
+        {
+          *p++ = s[0];
+          *p++ = s[1];
+          *p++ = s[2];
+          *p++ = s[3];
+          *p++ = ' ';
+          if (i == 4)
+            *p++ = ' ';
+        }
+    }
+  else
+    {
+      for (i=0; *s && s[1] && s[2]; s += 2, i++)
+        {
+          *p++ = s[0];
+          *p++ = s[1];
+          *p++ = is_pgp? ' ':':';
+          if (is_pgp && i == 7)
+            *p++ = ' ';
+        }
+    }
+
+  /* just in case print remaining odd digits */
+  for (; *s; s++)
+    *p++ = *s;
+  *p++ = '\n';
+  *p = 0;
+  state_attach_puts (buf, state);
+  FREE (&buf);
+}
+
+/* Show the valididy of a key used for one signature. */
+static void show_one_sig_validity (gpgme_ctx_t ctx, int idx, STATE *s)
+{
+  gpgme_verify_result_t result = NULL;
+  gpgme_signature_t sig = NULL;
+  const char *txt = NULL;
+
+  result = gpgme_op_verify_result (ctx);
+  if (result)
+    for (sig = result->signatures; sig && (idx > 0); sig = sig->next, idx--);
+
+  switch (sig ? sig->validity : 0)
+      {
+      case GPGME_VALIDITY_UNKNOWN:
+       txt = _("WARNING: We have NO indication whether "
+               "the key belongs to the person named "
+               "as shown above\n");
+       break;
+      case GPGME_VALIDITY_UNDEFINED:
+       break;
+      case GPGME_VALIDITY_NEVER:
+       txt = _("WARNING: The key does NOT BELONG to "
+               "the person named as shown above\n");
+       break;
+      case GPGME_VALIDITY_MARGINAL:
+       txt = _("WARNING: It is NOT certain that the key "
+               "belongs to the person named as shown above\n");
+       break;
+      case GPGME_VALIDITY_FULL:
+      case GPGME_VALIDITY_ULTIMATE:
+       txt = NULL;
+       break;
+      }
+  if (txt)
+    state_attach_puts (txt, s);
+}
+
+/* Show information about one signature.  This fucntion is called with
+   the context CTX of a sucessful verification operation and the
+   enumerator IDX which should start at 0 and incremete for each
+   call/signature. 
+
+   Return values are: 0 for normal procession, 1 for a bad signature,
+   2 for a signature with a warning or -1 for no more signature.  */
+static int show_one_sig_status (gpgme_ctx_t ctx, int idx, STATE *s)
+{
+  time_t created;
+  const char *fpr, *uid;
+  gpgme_key_t key = NULL;
+  int i, anybad = 0, anywarn = 0;
+  unsigned int sum;
+  gpgme_user_id_t uids = NULL;
+  gpgme_verify_result_t result;
+  gpgme_signature_t sig;
+  gpgme_error_t err = GPG_ERR_NO_ERROR;
+
+  result = gpgme_op_verify_result (ctx);
+  if (result)
+    {
+      /* FIXME: this code should use a static variable and remember
+        the current position in the list of signatures, IMHO.
+        -moritz.  */
+
+      for (i = 0, sig = result->signatures; sig && (i < idx);
+           i++, sig = sig->next)
+        ;
+      if (! sig)
+       return -1;              /* Signature not found.  */
+
+      if (signature_key)
+       {
+         gpgme_key_release (signature_key);
+         signature_key = NULL;
+       }
+      
+      created = sig->timestamp;
+      fpr = sig->fpr;
+      sum = sig->summary;
+
+      if (gpg_err_code (sig->status) != GPG_ERR_NO_ERROR)
+       anybad = 1;
+
+      err = gpgme_get_key (ctx, fpr, &key, 0); /* secret key?  */
+      if (! err)
+       {
+         uid = (key->uids && key->uids->uid) ? key->uids->uid : "[?]";
+         if (! signature_key)
+           signature_key = key;
+       }
+      else
+       {
+          key = NULL; /* Old gpgme versions did not set KEY to NULL on
+                         error.   Do it here to avoid a double free. */
+          uid = "[?]";
+       }
+
+      if (!s || !s->fpout || !(s->flags & M_DISPLAY))
+       ; /* No state information so no way to print anything. */
+      else if (err)
+       {
+         state_attach_puts (_("Error getting key information: "), s);
+         state_attach_puts ( gpg_strerror (err), s );
+         state_attach_puts ("\n", s);
+         anybad = 1;
+       }
+      else if ((sum & GPGME_SIGSUM_GREEN))
+       {
+         state_attach_puts (_("Good signature from: "), s);
+         state_attach_puts (uid, s);
+         state_attach_puts ("\n", s);
+         for (i = 1, uids = key->uids; uids; i++, uids = uids->next)
+           {
+             if (i == 1)
+               /* Skip primary UID.  */
+               continue;
+             if (uids->revoked)
+               continue;
+             state_attach_puts (_("                aka: "), s);
+             state_attach_puts (uids->uid, s);
+             state_attach_puts ("\n", s);
+           }
+         state_attach_puts (_("            created: "), s);
+         print_time (created, s);
+         state_attach_puts ("\n", s);
+         if (show_sig_summary (sum, ctx, key, idx, s))
+           anywarn = 1;
+         show_one_sig_validity (ctx, idx, s);
+       }
+      else if ((sum & GPGME_SIGSUM_RED))
+       {
+         state_attach_puts (_("*BAD* signature claimed to be from: "), s);
+         state_attach_puts (uid, s);
+         state_attach_puts ("\n", s);
+         show_sig_summary (sum, ctx, key, idx, s);
+       }
+      else if (!anybad && key && (key->protocol == GPGME_PROTOCOL_OpenPGP))
+       { /* We can't decide (yellow) but this is a PGP key with a good
+            signature, so we display what a PGP user expects: The name,
+            fingerprint and the key validity (which is neither fully or
+            ultimate). */
+         state_attach_puts (_("Good signature from: "), s);
+         state_attach_puts (uid, s);
+         state_attach_puts ("\n", s);
+         state_attach_puts (_("            created: "), s);
+         print_time (created, s);
+         state_attach_puts ("\n", s);
+         show_one_sig_validity (ctx, idx, s);
+         show_fingerprint (key,s);
+         if (show_sig_summary (sum, ctx, key, idx, s))
+           anywarn = 1;
+       }
+      else /* can't decide (yellow) */
+       {
+         state_attach_puts (_("Error checking signature"), s);
+         state_attach_puts ("\n", s);
+         show_sig_summary (sum, ctx, key, idx, s);
+       }
+
+      if (key != signature_key)
+       gpgme_key_release (key);
+    }
+
+  return anybad ? 1 : anywarn ? 2 : 0;
+}
+
+/* Do the actual verification step. With IS_SMIME set to true we
+   assume S/MIME (surprise!) */
+static int verify_one (BODY *sigbdy, STATE *s,
+                       const char *tempfile, int is_smime)
+{
+  int badsig = -1;
+  int anywarn = 0;
+  int err;
+  gpgme_ctx_t ctx;
+  gpgme_data_t signature, message;
+
+  signature = file_to_data_object (s->fpin, sigbdy->offset, sigbdy->length);
+  if (!signature)
+    return -1;
+
+  /* We need to tell gpgme about the encoding because the backend can't
+     auto-detect plain base-64 encoding which is used by S/MIME. */
+  if (is_smime)
+    gpgme_data_set_encoding (signature, GPGME_DATA_ENCODING_BASE64);
+
+  err = gpgme_data_new_from_file (&message, tempfile, 1);
+  if (err) 
+    {
+      gpgme_data_release (signature);
+      mutt_error ("error allocating data object: %s\n", gpgme_strerror (err));
+      return -1;
+    }
+  ctx = create_gpgme_context (is_smime);
+
+  /* Note: We don't need a current time output because GPGME avoids
+     such an attack by separating the meta information from the
+     data. */
+  state_attach_puts (_("[-- Begin signature information --]\n"), s);
+
+  err = gpgme_op_verify (ctx, signature, message, NULL);
+  mutt_need_hard_redraw ();
+  if (err)
+    {
+      char buf[200];
+      
+      snprintf (buf, sizeof(buf)-1, 
+                _("Error: verification failed: %s\n"),
+                gpgme_strerror (err));
+      state_attach_puts (buf, s);
+    }
+  else
+    { /* Verification succeeded, see what the result is. */
+      int res, idx;
+      int anybad = 0;
+
+      if (signature_key)
+       {
+         gpgme_key_release (signature_key);
+         signature_key = NULL;
+       }
+
+      for(idx=0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++)
+        {
+          if (res == 1)
+            anybad = 1;
+          else if (res == 2)
+            anywarn = 2;
+        }
+      if (!anybad)
+        badsig = 0;
+    }
+
+  if (!badsig)
+    {
+      gpgme_verify_result_t result;
+      gpgme_sig_notation_t notation;
+      gpgme_signature_t signature;
+
+      result = gpgme_op_verify_result (ctx);
+      if (result)
+      {
+       for (signature = result->signatures; signature;
+             signature = signature->next)
+       {
+         if (signature->notations)
+         {
+           state_attach_puts ("*** Begin Notation (signature by: ", s);
+           state_attach_puts (signature->fpr, s);
+           state_attach_puts (") ***\n", s);
+           for (notation = signature->notations; notation;
+                 notation = notation->next)
+           {
+             if (notation->name)
+             {
+               state_attach_puts (notation->name, s);
+               state_attach_puts ("=", s);
+             }
+             if (notation->value)
+             {
+               state_attach_puts (notation->value, s);
+               if (!(*notation->value
+                      && (notation->value[strlen (notation->value)-1]=='\n')))
+                 state_attach_puts ("\n", s);
+             }
+           }
+           state_attach_puts ("*** End Notation ***\n", s);
+         }
+       }
+      }
+    }
+
+  gpgme_release (ctx);
+  
+  state_attach_puts (_("[-- End signature information --]\n\n"), s);
+  dprint (1, (debugfile, "verify_one: returning %d.\n", badsig));
+  
+  return badsig? 1: anywarn? 2 : 0;
+}
+
+int pgp_gpgme_verify_one (BODY *sigbdy, STATE *s, const char *tempfile)
+{
+  return verify_one (sigbdy, s, tempfile, 0);
+}
+
+int smime_gpgme_verify_one (BODY *sigbdy, STATE *s, const char *tempfile)
+{
+  return verify_one (sigbdy, s, tempfile, 1);
+}
+
+/*
+ * 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)
+{
+  struct stat info;
+  BODY *tattach;
+  int err;
+  gpgme_ctx_t ctx;
+  gpgme_data_t ciphertext, plaintext;
+  int maybe_signed = 0;
+  int anywarn = 0;
+  int sig_stat = 0;
+
+  if (r_is_signed)
+    *r_is_signed = 0;
+
+  ctx = create_gpgme_context (is_smime);
+
+ restart:
+  /* Make a data object from the body, create context etc. */
+  ciphertext = file_to_data_object (s->fpin, a->offset, a->length);
+  if (!ciphertext)
+    return NULL;
+  plaintext = create_gpgme_data ();
+
+  /* Do the decryption or the verification in case of the S/MIME hack. */
+  if ((! is_smime) || maybe_signed)
+    {
+      if (! is_smime)
+       err = gpgme_op_decrypt_verify (ctx, ciphertext, plaintext);
+      else if (maybe_signed)
+       err = gpgme_op_verify (ctx, ciphertext, NULL, plaintext);
+
+      {
+       /* Check wether signatures have been verified.  */
+       gpgme_verify_result_t verify_result = gpgme_op_verify_result (ctx);
+       if (verify_result->signatures)
+         sig_stat = 1;
+      }
+    }
+  else
+    err = gpgme_op_decrypt (ctx, ciphertext, plaintext);
+  gpgme_data_release (ciphertext);
+  if (err)
+    {
+      if (is_smime && !maybe_signed
+          && gpg_err_code (err) == GPG_ERR_NO_DATA)
+        {
+         /* Check whether this might be a signed message despite what
+             the mime header told us.  Retry then.  gpgsm returns the
+             error information "unsupported Algorithm '?'" but gpgme
+             will not store this unknown algorithm, thus we test that
+             it has not been set. */
+         gpgme_decrypt_result_t result;
+
+         result = gpgme_op_decrypt_result (ctx);
+         if (!result->unsupported_algorithm)
+            {
+              maybe_signed = 1;
+              gpgme_data_release (plaintext);
+              goto restart;
+            }
+        }
+      mutt_need_hard_redraw ();
+      if ((s->flags & M_DISPLAY))
+        {
+          char buf[200];
+          
+          snprintf (buf, sizeof(buf)-1, 
+                    _("[-- Error: decryption failed: %s --]\n\n"),
+                    gpgme_strerror (err));
+          state_attach_puts (buf, s);
+        }
+      gpgme_data_release (plaintext);
+      gpgme_release (ctx);
+      return NULL;
+  }
+  mutt_need_hard_redraw ();
+
+  /* Read the output from GPGME, and make sure to change CRLF to LF,
+     otherwise read_mime_header has a hard time parsing the message.  */
+  if (data_object_to_stream (plaintext, fpout))
+    {
+      gpgme_data_release (plaintext);
+      gpgme_release (ctx);
+      return NULL;
+    }
+  gpgme_data_release (plaintext);
+
+  a->is_signed_data = 0;
+  if (sig_stat)
+    {
+      int res, idx;
+      int anybad = 0;
+      
+      if (maybe_signed)
+        a->is_signed_data = 1;
+      if(r_is_signed)
+        *r_is_signed = -1; /* A signature exists. */
+
+      if ((s->flags & M_DISPLAY))
+        state_attach_puts (_("[-- Begin signature "
+                             "information --]\n"), s);
+      for(idx = 0; (res = show_one_sig_status (ctx, idx, s)) != -1; idx++)
+        {
+          if (res == 1)
+            anybad = 1;
+          else if (res == 2)
+            anywarn = 1;
+        }
+      if (!anybad && idx && r_is_signed && *r_is_signed)
+        *r_is_signed = anywarn? 2:1; /* Good signature. */
+      
+      if ((s->flags & M_DISPLAY))
+        state_attach_puts (_("[-- End signature "
+                             "information --]\n\n"), s);
+    }
+  gpgme_release (ctx); ctx = NULL;
+
+  fflush (fpout);
+  rewind (fpout);
+  tattach = mutt_read_mime_header (fpout, 0);
+  if (tattach)
+    {
+      /*
+       * Need to set the length of this body part.
+       */
+      fstat (fileno (fpout), &info);
+      tattach->length = info.st_size - tattach->offset;
+      
+      tattach->warnsig = anywarn;
+
+      /* See if we need to recurse on this MIME part.  */
+      mutt_parse_part (fpout, tattach);
+    }
+
+  return tattach;
+}
+
+/* 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 pgp_gpgme_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;
+
+  if(!mutt_is_multipart_encrypted(b))
+    return -1;
+
+  if(!b->parts || !b->parts->next)
+    return -1;
+  
+  b = b->parts->next;
+  
+  memset (&s, 0, sizeof (s));
+  s.fpin = fpin;
+  mutt_mktemp (tempfile);
+  if (!(*fpout = safe_fopen (tempfile, "w+")))
+  {
+    mutt_perror (tempfile);
+    return -1;
+  }
+  unlink (tempfile);
+
+  *cur = decrypt_part (b, &s, *fpout, 0, &is_signed);
+  rewind (*fpout);
+  if (is_signed > 0)
+    first_part->goodsig = 1;
+  
+  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 smime_gpgme_decrypt_mime (FILE *fpin, FILE **fpout, BODY *b, BODY **cur)
+{
+  char tempfile[_POSIX_PATH_MAX];
+  STATE s;
+  FILE *tmpfp=NULL;
+  int is_signed;
+  long saved_b_offset;
+  size_t saved_b_length;
+  int saved_b_type;
+
+  if (!mutt_is_application_smime (b))
+    return -1;
+
+  if (b->parts)
+    return -1;
+  
+  /* Decode the body - we need to pass binary CMS to the
+     backend.  The backend allows for Base64 encoded data but it does
+     not allow for QP which I have seen in some messages.  So better
+     do it here. */
+  saved_b_type = b->type;
+  saved_b_offset = b->offset;
+  saved_b_length = b->length;
+  memset (&s, 0, sizeof (s));
+  s.fpin = fpin;
+  fseek (s.fpin, b->offset, 0); 
+  mutt_mktemp (tempfile);
+  if (!(tmpfp = safe_fopen (tempfile, "w+")))
+    {
+      mutt_perror (tempfile);
+      return -1;
+    }
+  mutt_unlink (tempfile);
+
+  s.fpout = tmpfp;
+  mutt_decode_attachment (b, &s);
+  fflush (tmpfp);
+  b->length = ftell (s.fpout);
+  b->offset = 0;
+  rewind (tmpfp);
+
+  memset (&s, 0, sizeof (s));
+  s.fpin = tmpfp;
+  s.fpout = 0;
+  mutt_mktemp (tempfile);
+  if (!(*fpout = safe_fopen (tempfile, "w+")))
+    {
+      mutt_perror (tempfile);
+      return -1;
+    }
+  mutt_unlink (tempfile);
+
+  *cur = decrypt_part (b, &s, *fpout, 1, &is_signed);
+  if (*cur)
+    (*cur)->goodsig = is_signed > 0;
+  b->type = saved_b_type;
+  b->length = saved_b_length;
+  b->offset = saved_b_offset;
+  fclose (tmpfp);
+  rewind (*fpout);
+  if (*cur && !is_signed && !(*cur)->parts && mutt_is_application_smime (*cur))
+    {
+      /* Assume that this is a opaque signed s/mime message.  This is
+         an ugly way of doing it but we have anyway a problem with
+         arbitrary encoded S/MIME messages: Only the outer part may be
+         encrypted.  The entire mime parsing should be revamped,
+         probably by keeping the temportary files so that we don't
+         need to decrypt them all the time.  Inner parts of an
+         encrypted part can then pint into this file and tehre won't
+         never be a need to decrypt again.  This needs a partial
+         rewrite of the MIME engine. */
+      BODY *bb = *cur;
+      BODY *tmp_b;
+
+      saved_b_type = bb->type;
+      saved_b_offset = bb->offset;
+      saved_b_length = bb->length;
+      memset (&s, 0, sizeof (s));
+      s.fpin = *fpout;
+      fseek (s.fpin, bb->offset, 0); 
+      mutt_mktemp (tempfile);
+      if (!(tmpfp = safe_fopen (tempfile, "w+")))
+        {
+          mutt_perror (tempfile);
+          return -1;
+        }
+      mutt_unlink (tempfile);
+
+      s.fpout = tmpfp;
+      mutt_decode_attachment (bb, &s);
+      fflush (tmpfp);
+      bb->length = ftell (s.fpout);
+      bb->offset = 0;
+      rewind (tmpfp);
+      fclose (*fpout); 
+
+      memset (&s, 0, sizeof (s));
+      s.fpin = tmpfp;
+      s.fpout = 0;
+      mutt_mktemp (tempfile);
+      if (!(*fpout = safe_fopen (tempfile, "w+")))
+        {
+          mutt_perror (tempfile);
+          return -1;
+        }
+      mutt_unlink (tempfile);
+
+      tmp_b = decrypt_part (bb, &s, *fpout, 1, &is_signed);
+      if (tmp_b)
+        tmp_b->goodsig = is_signed > 0;
+      bb->type = saved_b_type;
+      bb->length = saved_b_length;
+      bb->offset = saved_b_offset;
+      fclose (tmpfp);
+      rewind (*fpout);
+      mutt_free_body (cur);
+      *cur = tmp_b;
+    }
+  return *cur? 0:-1;
+}
+
+
+/* 
+ * Implementation of `pgp_check_traditional'.
+ */
+
+static int pgp_check_traditional_one_body (FILE *fp, BODY *b, int tagged_only)
+{
+  char tempfile[_POSIX_PATH_MAX];
+  char buf[HUGE_STRING];
+  FILE *tfp;
+  
+  short sgn = 0;
+  short enc = 0;
+  
+  if (b->type != TYPETEXT)
+    return 0;
+
+  if (tagged_only && !b->tagged)
+    return 0;
+
+  mutt_mktemp (tempfile);
+  if (mutt_decode_save_attachment (fp, b, tempfile, 0, 0) != 0)
+  {
+    unlink (tempfile);
+    return 0;
+  }
+  
+  if ((tfp = fopen (tempfile, "r")) == NULL)
+  {
+    unlink (tempfile);
+    return 0;
+  }
+  
+  while (fgets (buf, sizeof (buf), tfp))
+  {
+    if (!mutt_strncmp ("-----BEGIN PGP ", buf, 15))
+    {
+      if (!mutt_strcmp ("MESSAGE-----\n", buf + 15))
+       enc = 1;
+      else if (!mutt_strcmp ("SIGNED MESSAGE-----\n", buf + 15))
+       sgn = 1;
+    }
+  }
+  safe_fclose (&tfp);
+  unlink (tempfile);
+
+  if (!enc && !sgn)
+    return 0;
+
+  /* fix the content type */
+  
+  mutt_set_parameter ("format", "fixed", &b->parameter);
+  mutt_set_parameter ("x-action", enc ? "pgp-encrypted" : "pgp-signed",
+                      &b->parameter);
+  
+  return 1;
+}
+
+int pgp_gpgme_check_traditional (FILE *fp, BODY *b, int tagged_only)
+{
+  int rv = 0;
+  int r;
+  for (; b; b = b->next)
+  {
+    if (is_multipart (b))
+      rv = (pgp_gpgme_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);
+    }
+  }
+  return rv;
+}
+
+
+/* 
+ * Implementation of `application_handler'.
+ */
+
+/* 
+  Copy a clearsigned message, and strip the signature and PGP's
+  dash-escaping.
+  
+  XXX - charset handling: We assume that it is safe to do
+  character set decoding first, dash decoding second here, while
+  we do it the other way around in the main handler.
+  
+  (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)
+{
+  char buf[HUGE_STRING];
+  short complete, armor_header;
+  FGETCONV *fc;
+  char *fname;
+  FILE *fp;
+
+  fname = data_object_to_tempfile (data, &fp);
+  if (!fname)
+    return;
+  unlink (fname);
+  FREE (&fname);
+  
+  fc = fgetconv_open (fp, charset, Charset, M_ICONV_HOOK_FROM);
+  
+  for (complete = 1, armor_header = 1;
+       fgetconvs (buf, sizeof (buf), fc) != NULL;
+       complete = strchr (buf, '\n') != NULL)
+  {
+    if (!complete)
+    {
+      if (!armor_header)
+       state_puts (buf, s);
+      continue;
+    }
+
+    if (!mutt_strcmp (buf, "-----BEGIN PGP SIGNATURE-----\n"))
+      break;
+    
+    if (armor_header)
+    {
+      if (buf[0] == '\n') 
+       armor_header = 0;
+      continue;
+    }
+    
+    if (s->prefix) 
+      state_puts (s->prefix, s);
+    
+    if (buf[0] == '-' && buf[1] == ' ')
+      state_puts (buf + 2, s);
+    else
+      state_puts (buf, s);
+  }
+  
+  fgetconv_close (&fc);
+  fclose (fp);
+}
+
+
+/* Support for classic_application/pgp */
+void pgp_gpgme_application_handler (BODY *m, STATE *s)
+{
+  int needpass = -1, pgp_keyblock = 0;
+  int clearsign = 0;
+  long start_pos = 0;
+  long bytes, last_pos, offset;
+  char buf[HUGE_STRING];
+  FILE *pgpout = NULL;
+
+  gpgme_error_t err;
+  gpgme_data_t armored_data = NULL;
+
+  short maybe_goodsig = 1;
+  short have_any_sigs = 0;
+
+  char body_charset[STRING];  /* Only used for clearsigned messages. */
+
+  dprint (2, (debugfile, "Entering pgp_application_pgp handler\n"));
+
+  /* For clearsigned messages we won't be able to get a character set
+     but we know that this may only be text thus we assume Latin-1
+     here. */
+  if (!mutt_get_body_charset (body_charset, sizeof (body_charset), m))
+    strfcpy (body_charset, "iso-8859-1", sizeof body_charset);
+
+  fseek (s->fpin, m->offset, 0);
+  last_pos = m->offset;
+  
+  for (bytes = m->length; bytes > 0;)
+    {
+      if (fgets (buf, sizeof (buf), s->fpin) == NULL)
+        break;
+      
+      offset = ftell (s->fpin);
+      bytes -= (offset - last_pos); /* don't rely on mutt_strlen(buf) */
+      last_pos = offset;
+      
+      if (!mutt_strncmp ("-----BEGIN PGP ", buf, 15))
+        {
+          clearsign = 0;
+          start_pos = last_pos;
+          
+          if (!mutt_strcmp ("MESSAGE-----\n", buf + 15))
+            needpass = 1;
+          else if (!mutt_strcmp ("SIGNED MESSAGE-----\n", buf + 15))
+            {
+              clearsign = 1;
+              needpass = 0;
+            }
+          else if (!option (OPTDONTHANDLEPGPKEYS) &&
+                   !mutt_strcmp ("PUBLIC KEY BLOCK-----\n", buf + 15))
+            {
+              needpass = 0;
+              pgp_keyblock =1;
+            } 
+          else
+            {
+              /* XXX - we may wish to recode here */
+              if (s->prefix)
+                state_puts (s->prefix, s);
+              state_puts (buf, s);
+              continue;
+            }
+          
+          have_any_sigs = (have_any_sigs
+                           || (clearsign && (s->flags & M_VERIFY)));
+          
+          /* Copy PGP material to an data container */
+          armored_data = create_gpgme_data ();
+          gpgme_data_write (armored_data, buf, strlen (buf));
+          while (bytes > 0 && fgets (buf, sizeof (buf) - 1, s->fpin) != NULL)
+            {
+              offset = ftell (s->fpin);
+              bytes -= (offset - last_pos); /* don't rely on mutt_strlen(buf)*/
+              last_pos = offset;
+              
+              gpgme_data_write (armored_data, buf, strlen (buf));
+              
+              if ((needpass
+                   && !mutt_strcmp ("-----END PGP MESSAGE-----\n", buf)) 
+                  || (!needpass 
+                      && (!mutt_strcmp ("-----END PGP SIGNATURE-----\n", buf)
+                          || !mutt_strcmp (
+                                "-----END PGP PUBLIC KEY BLOCK-----\n",buf))))
+                break;
+            }
+          
+          /* Invoke PGP if needed */
+          if (!clearsign || (s->flags & M_VERIFY))
+            {
+              unsigned int sig_stat = 0;
+              gpgme_data_t plaintext;
+              gpgme_ctx_t ctx;
+
+              plaintext = create_gpgme_data ();
+              ctx = create_gpgme_context (0);
+
+              if (clearsign)
+                err = gpgme_op_verify (ctx, armored_data, NULL, plaintext);
+              else
+                {
+                  err = gpgme_op_decrypt_verify (ctx, armored_data, plaintext);
+                  if (gpg_err_code (err) == GPG_ERR_NO_DATA)
+                    {
+                      /* Decrypt verify can't handle signed only messages. */
+                     err = (gpgme_data_seek (armored_data, 0, SEEK_SET) == -1)
+                       ? gpgme_error_from_errno (errno) : 0;
+                      /* Must release plaintext so that we supply an
+                         uninitialized object. */
+                      gpgme_data_release (plaintext);
+                      plaintext = create_gpgme_data ();
+                      err = gpgme_op_verify (ctx, armored_data,
+                                             NULL, plaintext);
+                    }
+                }
+
+              if (err)
+                {
+                  char errbuf[200];
+                  
+                  snprintf (errbuf, sizeof(errbuf)-1, 
+                            _("Error: decryption/verification failed: %s\n"),
+                            gpgme_strerror (err));
+                  state_attach_puts (errbuf, s);
+                }
+              else
+                { /* Decryption/Verification succeeded */
+                  char *tmpfname;
+
+                 {
+                   /* Check wether signatures have been verified.  */
+                   gpgme_verify_result_t verify_result;
+
+                    verify_result = gpgme_op_verify_result (ctx);
+                   if (verify_result->signatures)
+                     sig_stat = 1;
+                 }
+
+                  have_any_sigs = 0;
+                  maybe_goodsig = 0;
+                  if ((s->flags & M_DISPLAY) && sig_stat)
+                    {
+                      int res, idx;
+                      int anybad = 0;
+                      int anywarn = 0;
+
+                      state_attach_puts (_("[-- Begin signature "
+                                           "information --]\n"), s);
+                      have_any_sigs = 1;
+                      for(idx=0;
+                          (res = show_one_sig_status (ctx, idx, s)) != -1;
+                          idx++)
+                        {
+                          if (res == 1)
+                            anybad = 1;
+                          else if (res == 2)
+                            anywarn = 1;
+                        }
+                      if (!anybad && idx)
+                        maybe_goodsig = 1;
+                      
+                      state_attach_puts (_("[-- End signature "
+                                           "information --]\n\n"), s);
+                    }
+
+                  tmpfname = data_object_to_tempfile (plaintext, &pgpout);
+                  if (!tmpfname)
+                    {
+                      pgpout = NULL;
+                      state_attach_puts (_("Error: copy data failed\n"), s);
+                    }
+                  else
+                    {
+                      unlink (tmpfname);
+                      FREE (&tmpfname);
+                    }
+                }
+              gpgme_release (ctx);
+            }
+      
+          /*
+           * Now, copy cleartext to the screen.  NOTE - we expect that PGP
+           * outputs utf-8 cleartext.  This may not always be true, but it 
+           * seems to be a reasonable guess.
+           */
+          
+          if(s->flags & M_DISPLAY)
+            {
+              if (needpass)
+            state_attach_puts (_("[-- BEGIN PGP MESSAGE --]\n\n"), s);
+              else if (pgp_keyblock)
+                state_attach_puts (_("[-- BEGIN PGP PUBLIC KEY BLOCK --]\n"),
+                                   s);
+              else
+                state_attach_puts (_("[-- BEGIN PGP SIGNED MESSAGE --]\n\n"),
+                                   s);
+            }
+          
+          if (clearsign)
+            {
+              copy_clearsigned (armored_data, s, body_charset);
+            }
+          else if (pgpout)
+            {
+              FGETCONV *fc;
+              int c;
+              rewind (pgpout);
+              fc = fgetconv_open (pgpout, "utf-8", Charset, 0);
+              while ((c = fgetconv (fc)) != EOF)
+                {
+                  state_putc (c, s);
+                  if (c == '\n' && s->prefix)
+                    state_puts (s->prefix, s);
+                }
+              fgetconv_close (&fc);
+            }
+          
+          if (s->flags & M_DISPLAY)
+            {
+              state_putc ('\n', s);
+              if (needpass)
+                state_attach_puts (_("[-- END PGP MESSAGE --]\n"), s);
+              else if (pgp_keyblock)
+                state_attach_puts (_("[-- END PGP PUBLIC KEY BLOCK --]\n"), s);
+              else
+                state_attach_puts (_("[-- END PGP SIGNED MESSAGE --]\n"), s);
+            }
+          
+          if (pgpout)
+            {
+              safe_fclose (&pgpout);
+            }
+        }
+      else
+        {
+          /* XXX - we may wish to recode here */
+          if (s->prefix)
+            state_puts (s->prefix, s);
+          state_puts (buf, s);
+        }
+    }
+
+  m->goodsig = (maybe_goodsig && have_any_sigs);
+  
+  if (needpass == -1)
+    {
+      state_attach_puts (_("[-- Error: could not find beginning"
+                           " of PGP message! --]\n\n"), s);
+      return;
+    }
+  dprint (2, (debugfile, "Leaving pgp_application_pgp handler\n"));
+}
+
+/* 
+ * Implementation of `encrypted_handler'.
+ */
+
+/* MIME handler for pgp/mime encrypted messages. */
+void pgp_gpgme_encrypted_handler (BODY *a, STATE *s)
+{
+  char tempfile[_POSIX_PATH_MAX];
+  FILE *fpout;
+  BODY *tattach;
+  BODY *orig_body = a;
+  int is_signed;
+  
+  dprint (2, (debugfile, "Entering pgp_encrypted handler\n"));
+  a = a->parts;
+  if (!a || a->type != TYPEAPPLICATION || !a->subtype
+      || ascii_strcasecmp ("pgp-encrypted", a->subtype) 
+      || !a->next || a->next->type != TYPEAPPLICATION || !a->next->subtype
+      || ascii_strcasecmp ("octet-stream", a->next->subtype) )
+    {
+      if (s->flags & M_DISPLAY)
+        state_attach_puts (_("[-- Error: malformed PGP/MIME message! --]\n\n"),
+                           s);
+      return;
+    }
+
+  /* Move forward to the application/pgp-encrypted body. */
+  a = a->next;
+
+  mutt_mktemp (tempfile);
+  if (!(fpout = safe_fopen (tempfile, "w+")))
+    {
+      if (s->flags & M_DISPLAY)
+        state_attach_puts (_("[-- Error: could not create temporary file! "
+                             "--]\n"), s);
+      return;
+    }
+
+  tattach = decrypt_part (a, s, fpout, 0, &is_signed);
+  if (tattach)
+    {
+      tattach->goodsig = is_signed > 0;
+
+      if (s->flags & M_DISPLAY)
+        state_attach_puts (is_signed?
+          _("[-- The following data is PGP/MIME signed and encrypted --]\n\n"):
+          _("[-- The following data is PGP/MIME encrypted --]\n\n"),
+                           s);
+      
+      {
+        FILE *savefp = s->fpin;
+        s->fpin = fpout;
+        mutt_body_handler (tattach, s);
+        s->fpin = savefp;
+      }
+
+      /* 
+       * if a multipart/signed is the _only_ sub-part of a
+       * multipart/encrypted, cache signature verification
+       * status.
+       */
+      if (mutt_is_multipart_signed (tattach) && !tattach->next)
+        orig_body->goodsig |= tattach->goodsig;
+    
+      if (s->flags & M_DISPLAY)
+        {
+          state_puts ("\n", s);
+          state_attach_puts (is_signed?
+             _("[-- End of PGP/MIME signed and encrypted data --]\n"):
+             _("[-- End of PGP/MIME encrypted data --]\n"),
+                             s);
+        }
+
+      mutt_free_body (&tattach);
+    }
+  
+  fclose (fpout);
+  mutt_unlink(tempfile);
+  dprint (2, (debugfile, "Leaving pgp_encrypted handler\n"));
+}
+
+/* Support for application/smime */
+void smime_gpgme_application_handler (BODY *a, STATE *s)
+{
+  char tempfile[_POSIX_PATH_MAX];
+  FILE *fpout;
+  BODY *tattach;
+  int is_signed;
+
+
+  dprint (2, (debugfile, "Entering smime_encrypted handler\n"));
+  
+  a->warnsig = 0;
+  mutt_mktemp (tempfile);
+  if (!(fpout = safe_fopen (tempfile, "w+")))
+    {
+      if (s->flags & M_DISPLAY)
+        state_attach_puts (_("[-- Error: could not create temporary file! "
+                             "--]\n"), s);
+      return;
+    }
+
+  tattach = decrypt_part (a, s, fpout, 1, &is_signed);
+  if (tattach)
+    {
+      tattach->goodsig = is_signed > 0;
+
+      if (s->flags & M_DISPLAY)
+        state_attach_puts (is_signed?
+          _("[-- The following data is S/MIME signed --]\n\n"):
+          _("[-- The following data is S/MIME encrypted --]\n\n"),
+                           s);
+      
+      {
+        FILE *savefp = s->fpin;
+        s->fpin = fpout;
+        mutt_body_handler (tattach, s);
+        s->fpin = savefp;
+      }
+
+      /* 
+       * if a multipart/signed is the _only_ sub-part of a
+       * multipart/encrypted, cache signature verification
+       * status.
+       */
+      if (mutt_is_multipart_signed (tattach) && !tattach->next)
+        {
+          if (!(a->goodsig = tattach->goodsig))
+            a->warnsig = tattach->warnsig;
+        }
+      else if (tattach->goodsig)
+        {
+          a->goodsig = 1;
+          a->warnsig = tattach->warnsig;
+        }
+
+      if (s->flags & M_DISPLAY)
+        {
+          state_puts ("\n", s);
+          state_attach_puts (is_signed?
+             _("[-- End of S/MIME signed data --]\n"):
+             _("[-- End of S/MIME encrypted data --]\n"),
+                             s);
+        }
+
+      mutt_free_body (&tattach);
+    }
+  
+  fclose (fpout);
+  mutt_unlink(tempfile);
+  dprint (2, (debugfile, "Leaving smime_encrypted handler\n"));
+}
+
+
+/*
+ * Format an entry on the CRYPT key selection menu.
+ * 
+ * %n  number
+ * %k  key id          %K      key id of the principal key
+ * %u  user id
+ * %a  algorithm       %A      algorithm of the princ. key
+ * %l  length          %L      length of the princ. key
+ * %f  flags           %F      flags of the princ. key
+ * %c  capabilities    %C      capabilities of the princ. key
+ * %t  trust/validity of the key-uid association
+ * %p           protocol
+ * %[...] date of key using strftime(3)
+ */
+
+static const char *crypt_entry_fmt (char *dest,
+                                    size_t destlen,
+                                    char op,
+                                    const char *src,
+                                    const char *prefix,
+                                    const char *ifstring,
+                                    const char *elsestring,
+                                    unsigned long data,
+                                    format_flag flags)
+{
+  char fmt[16];
+  crypt_entry_t *entry;
+  crypt_key_t *key;
+  int kflags = 0;
+  int optional = (flags & M_FORMAT_OPTIONAL);
+  const char *s = NULL;
+  unsigned long val;
+
+  entry = (crypt_entry_t *) data;
+  key   = entry->key;
+
+/*    if (isupper ((unsigned char) op)) */
+/*      key = pkey; */
+
+  kflags = (key->flags /*| (pkey->flags & KEYFLAG_RESTRICTIONS)
+                         | uid->flags*/);
+  
+  switch (ascii_tolower (op))
+    {
+    case '[':
+      {
+       const char *cp;
+       char buf2[SHORT_STRING], *p;
+       int do_locales;
+       struct tm *tm;
+       size_t len;
+
+       p = dest;
+
+       cp = src;
+       if (*cp == '!')
+       {
+         do_locales = 0;
+         cp++;
+       }
+       else
+         do_locales = 1;
+
+       len = destlen - 1;
+       while (len > 0 && *cp != ']')
+       {
+         if (*cp == '%')
+         {
+           cp++;
+           if (len >= 2)
+           {
+             *p++ = '%';
+             *p++ = *cp;
+             len -= 2;
+           }
+           else
+             break; /* not enough space */
+           cp++;
+         }
+         else
+         {
+           *p++ = *cp++;
+           len--;
+         }
+       }
+       *p = 0;
+
+       if (do_locales && Locale)
+         setlocale (LC_TIME, Locale);
+        
+        {
+         time_t tt = 0;
+
+         if (key->kobj->subkeys && (key->kobj->subkeys->timestamp > 0))
+           tt = key->kobj->subkeys->timestamp;
+
+          tm = localtime (&tt);
+        }
+       strftime (buf2, sizeof (buf2), dest, tm);
+
+       if (do_locales)
+         setlocale (LC_TIME, "C");
+        
+       snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
+       snprintf (dest, destlen, fmt, buf2);
+       if (len > 0)
+         src = cp + 1;
+      }
+      break;
+    case 'n':
+      if (!optional)
+      {
+       snprintf (fmt, sizeof (fmt), "%%%sd", prefix);
+       snprintf (dest, destlen, fmt, entry->num);
+      }
+      break;
+    case 'k':
+      if (!optional)
+      {
+        /* fixme: we need a way to distinguish between main and subkeys.
+           Store the idx in entry? */
+       snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
+       snprintf (dest, destlen, fmt, crypt_keyid (key));
+      }
+      break;
+    case 'u':
+      if (!optional)
+      {
+       snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
+       snprintf (dest, destlen, fmt, key->uid);
+      }
+      break;
+    case 'a':
+      if (!optional)
+      {
+       snprintf (fmt, sizeof (fmt), "%%%s.3s", prefix);
+       if (key->kobj->subkeys)
+         s = gpgme_pubkey_algo_name (key->kobj->subkeys->pubkey_algo);
+       else
+          s = "?";
+       snprintf (dest, destlen, fmt, s);
+      }
+      break;
+    case 'l':
+      if (!optional)
+      {
+       snprintf (fmt, sizeof (fmt), "%%%slu", prefix);
+       if (key->kobj->subkeys)
+         val = key->kobj->subkeys->length;
+       else
+         val = 0;
+       snprintf (dest, destlen, fmt, val);
+      }
+      break;
+    case 'f':
+      if (!optional)
+      {
+       snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
+       snprintf (dest, destlen, fmt, crypt_flags (kflags));
+      }
+      else if (!(kflags & (KEYFLAG_RESTRICTIONS)))
+        optional = 0;
+      break;
+    case 'c':
+      if (!optional)
+      {
+       snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
+       snprintf (dest, destlen, fmt, crypt_key_abilities (kflags));
+      }
+      else if (!(kflags & (KEYFLAG_ABILITIES)))
+        optional = 0;
+      break;
+    case 't':
+      if ((kflags & KEYFLAG_ISX509))
+        s = "x";
+      else
+       {
+         gpgme_user_id_t uid = NULL;
+         unsigned int i = 0;
+
+         for (i = 0, uid = key->kobj->uids; uid && (i < key->idx);
+               i++, uid = uid->next)
+            ;
+         if (uid)
+           switch (uid->validity)
+             {
+             case GPGME_VALIDITY_UNDEFINED:
+               s = "q";
+               break;
+             case GPGME_VALIDITY_NEVER:
+               s = "n";
+               break;
+             case GPGME_VALIDITY_MARGINAL:
+               s = "m";
+               break;
+             case GPGME_VALIDITY_FULL:
+               s = "f";
+               break;
+             case GPGME_VALIDITY_ULTIMATE:
+               s = "u";
+               break;
+             case GPGME_VALIDITY_UNKNOWN:
+             default:
+               s = "?";
+               break;
+             }
+       }
+      snprintf (fmt, sizeof (fmt), "%%%sc", prefix);
+      snprintf (dest, destlen, fmt, s? *s: 'B');
+      break;
+    case 'p':
+      snprintf (fmt, sizeof (fmt), "%%%ss", prefix);
+      snprintf (dest, destlen, fmt,
+                gpgme_get_protocol_name (key->kobj->protocol));
+      break;
+
+    default:
+      *dest = '\0';
+  }
+
+  if (optional)
+    mutt_FormatString (dest, destlen, ifstring, mutt_attach_fmt, data, 0);
+  else if (flags & M_FORMAT_OPTIONAL)
+    mutt_FormatString (dest, destlen, elsestring, mutt_attach_fmt, data, 0);
+  return (src);
+}
+      
+/* Used by the display fucntion to format a line. */
+static void crypt_entry (char *s, size_t l, MUTTMENU * menu, int num)
+{
+  crypt_key_t **key_table = (crypt_key_t **) menu->data;
+  crypt_entry_t entry;
+  
+  entry.key = key_table[num];
+  entry.num = num + 1;
+
+  mutt_FormatString (s, l, NONULL (PgpEntryFormat), crypt_entry_fmt, 
+                    (unsigned long) &entry, M_FORMAT_ARROWCURSOR);
+}
+
+/* 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;
+  int r;
+
+  if ((r = mutt_strcasecmp ((*s)->uid, (*t)->uid)))
+    return r > 0;
+  else
+    return mutt_strcasecmp (crypt_keyid (*s), crypt_keyid (*t)) > 0;
+}
+
+static int crypt_compare_address (const void *a, const void *b)
+{
+  return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_address (a, b)
+                                         :  _crypt_compare_address (a, 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;
+  int r;
+
+  if ((r = mutt_strcasecmp (crypt_keyid (*s), crypt_keyid (*t))))
+    return r > 0;
+  else
+    return mutt_strcasecmp ((*s)->uid, (*t)->uid) > 0;
+}
+
+static int crypt_compare_keyid (const void *a, const void *b)
+{
+  return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_keyid (a, b)
+                                        :  _crypt_compare_keyid (a, 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;
+  unsigned long ts = 0, tt = 0;
+
+  if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
+    ts = (*s)->kobj->subkeys->timestamp;
+  if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
+    tt = (*t)->kobj->subkeys->timestamp;
+
+  if (ts > tt)
+    return 1;
+  if (ts < tt)
+    return 0;
+
+  return mutt_strcasecmp ((*s)->uid, (*t)->uid) > 0;
+}
+
+static int crypt_compare_date (const void *a, const void *b)
+{
+  return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_date (a, b)
+                                         :  _crypt_compare_date (a, b));
+}
+
+/* Compare two trust values, the key length, the creation dates. the
+   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;
+  unsigned long ts = 0, tt = 0;
+  int r;
+
+  if ((r = (((*s)->flags & (KEYFLAG_RESTRICTIONS))
+           - ((*t)->flags & (KEYFLAG_RESTRICTIONS)))))
+    return r > 0;
+
+  if ((*s)->kobj->uids)
+    ts = (*s)->kobj->uids->validity;
+  if ((*t)->kobj->uids)
+    tt = (*t)->kobj->uids->validity;
+  if ((r = (tt - ts)))
+    return r < 0;
+
+  if ((*s)->kobj->subkeys)
+    ts = (*s)->kobj->subkeys->length;
+  if ((*t)->kobj->subkeys)
+    tt = (*t)->kobj->subkeys->length;
+  if (ts != tt)
+    return ts > tt;
+
+  if ((*s)->kobj->subkeys && ((*s)->kobj->subkeys->timestamp > 0))
+    ts = (*s)->kobj->subkeys->timestamp;
+  if ((*t)->kobj->subkeys && ((*t)->kobj->subkeys->timestamp > 0))
+    tt = (*t)->kobj->subkeys->timestamp;
+  if (ts > tt)
+    return 1;
+  if (ts < tt)
+    return 0;
+
+  if ((r = mutt_strcasecmp ((*s)->uid, (*t)->uid)))
+    return r > 0;
+  return (mutt_strcasecmp (crypt_keyid ((*s)), crypt_keyid ((*t)))) > 0;
+}
+
+static int crypt_compare_trust (const void *a, const void *b)
+{
+  return ((PgpSortKeys & SORT_REVERSE) ? !_crypt_compare_trust (a, b)
+                                      : _crypt_compare_trust (a, b));
+}
+
+/* Print the X.500 Distinguished Name part KEY from the array of parts
+   DN to FP. */
+static int
+print_dn_part (FILE *fp, struct dn_array_s *dn, const char *key)
+{
+  int any = 0;
+
+  for (; dn->key; dn++)
+    {
+      if (!strcmp (dn->key, key))
+        {
+          if (any)
+            fputs (" + ", fp);
+          print_utf8 (fp, dn->value, strlen (dn->value));
+          any = 1;
+        }
+    }
+  return any;
+}
+
+/* Print all parts of a DN in a standard sequence. */
+static void
+print_dn_parts (FILE *fp, struct dn_array_s *dn)
+{
+  const char *stdpart[] = {
+    "CN", "OU", "O", "STREET", "L", "ST", "C", NULL 
+  };
+  int any=0, any2=0, i;
+  
+  for (i=0; stdpart[i]; i++)
+    {
+      if (any)
+        fputs (", ", fp);
+      any = print_dn_part (fp, dn, stdpart[i]);
+    }
+  /* now print the rest without any specific ordering */
+  for (; dn->key; dn++)
+    {
+      for (i=0; stdpart[i]; i++)
+        {
+          if (!strcmp (dn->key, stdpart[i]))
+            break;
+        }
+      if (!stdpart[i])
+        {
+          if (any)
+            fputs (", ", fp);
+          if (!any2)
+            fputs ("(", fp);
+          any = print_dn_part (fp, dn, dn->key);
+          any2 = 1;
+        }
+    }
+  if (any2)
+    fputs (")", fp);
+}
+
+
+/* Parse an RDN; this is a helper to parse_dn(). */
+static const unsigned char *
+parse_dn_part (struct dn_array_s *array, const unsigned char *string)
+{
+  const unsigned char *s, *s1;
+  size_t n;
+  unsigned char *p;
+
+  /* parse attributeType */
+  for (s = string+1; *s && *s != '='; s++)
+    ;
+  if (!*s)
+    return NULL; /* error */
+  n = s - string;
+  if (!n)
+    return NULL; /* empty key */
+  array->key = safe_malloc (n+1);
+  p = (unsigned char *)array->key;
+  memcpy (p, string, n); /* fixme: trim trailing spaces */
+  p[n] = 0;
+  string = s + 1;
+
+  if (*string == '#')
+    { /* hexstring */
+      string++;
+      for (s=string; hexdigitp (s); s++)
+        s++;
+      n = s - string;
+      if (!n || (n & 1))
+        return NULL; /* empty or odd number of digits */
+      n /= 2;
+      p = safe_malloc (n+1);
+      array->value = (char*)p;
+      for (s1=string; n; s1 += 2, n--)
+        *p++ = xtoi_2 (s1);
+      *p = 0;
+   }
+  else
+    { /* regular v3 quoted string */
+      for (n=0, s=string; *s; s++)
+        {
+          if (*s == '\\')
+            { /* pair */
+              s++;
+              if (*s == ',' || *s == '=' || *s == '+'
+                  || *s == '<' || *s == '>' || *s == '#' || *s == ';' 
+                  || *s == '\\' || *s == '\"' || *s == ' ')
+                n++;
+              else if (hexdigitp (s) && hexdigitp (s+1))
+                {
+                  s++;
+                  n++;
+                }
+              else
+                return NULL; /* invalid escape sequence */
+            }
+          else if (*s == '\"')
+            return NULL; /* invalid encoding */
+          else if (*s == ',' || *s == '=' || *s == '+'
+                   || *s == '<' || *s == '>' || *s == '#' || *s == ';' )
+            break; 
+          else
+            n++;
+        }
+
+      p = safe_malloc (n+1);
+      array->value = (char*)p;
+      for (s=string; n; s++, n--)
+        {
+          if (*s == '\\')
+            { 
+              s++;
+              if (hexdigitp (s))
+                {
+                  *p++ = xtoi_2 (s);
+                  s++;
+                }
+              else
+                *p++ = *s;
+            }
+          else
+            *p++ = *s;
+        }
+      *p = 0;
+    }
+  return s;
+}
+
+
+/* Parse a DN and return an array-ized one.  This is not a validating
+   parser and it does not support any old-stylish syntax; gpgme is
+   expected to return only rfc2253 compatible strings. */
+static struct dn_array_s *
+parse_dn (const unsigned char *string)
+{
+  struct dn_array_s *array;
+  size_t arrayidx, arraysize;
+  int i;
+
+  arraysize = 7; /* C,ST,L,O,OU,CN,email */
+  array = safe_malloc ((arraysize+1) * sizeof *array);
+  arrayidx = 0;
+  while (*string)
+    {
+      while (*string == ' ')
+        string++;
+      if (!*string)
+        break; /* ready */
+      if (arrayidx >= arraysize)
+        { /* mutt lacks a real safe_realoc - so we need to copy */
+          struct dn_array_s *a2;
+
+          arraysize += 5;
+          a2 = safe_malloc ((arraysize+1) * sizeof *array);
+          for (i=0; i < arrayidx; i++)
+            {
+              a2[i].key = array[i].key;
+              a2[i].value = array[i].value;
+            }
+          FREE (&array);
+          array = a2;
+        }
+      array[arrayidx].key = NULL;
+      array[arrayidx].value = NULL;
+      string = parse_dn_part (array+arrayidx, string);
+      arrayidx++;
+      if (!string)
+        goto failure;
+      while (*string == ' ')
+        string++;
+      if (*string && *string != ',' && *string != ';' && *string != '+')
+        goto failure; /* invalid delimiter */
+      if (*string)
+        string++;
+    }
+  array[arrayidx].key = NULL;
+  array[arrayidx].value = NULL;
+  return array;
+
+ failure:
+  for (i=0; i < arrayidx; i++)
+    {
+      FREE (&array[i].key);
+      FREE (&array[i].value);
+    }
+  FREE (&array);
+  return NULL;
+}
+
+
+/* Print a nice representation of the USERID and make sure it is
+   displayed in a proper way, which does mean to reorder some parts
+   for S/MIME's DNs.  USERID is a string as returned by the gpgme key
+   functions.  It is utf-8 encoded. */
+static void
+parse_and_print_user_id (FILE *fp, const char *userid)
+{
+  const char *s;
+  int i;
+
+  if (*userid == '<')
+    {
+      s = strchr (userid+1, '>');
+      if (s)
+        print_utf8 (fp, userid+1, s-userid-1);
+    }
+  else if (*userid == '(')
+    fputs (_("[Can't display this user ID (unknown encoding)]"), fp);
+  else if (!digit_or_letter ((const unsigned char *)userid))
+    fputs (_("[Can't display this user ID (invalid encoding)]"), fp);
+  else
+    {
+      struct dn_array_s *dn = parse_dn ((const unsigned char *)userid);
+      if (!dn)
+        fputs (_("[Can't display this user ID (invalid DN)]"), fp);
+      else 
+        {
+          print_dn_parts (fp, dn);          
+          for (i=0; dn[i].key; i++)
+            {
+              FREE (&dn[i].key);
+              FREE (&dn[i].value);
+            }
+          FREE (&dn);
+        }
+    }
+}
+
+typedef enum
+  {
+    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)
+{
+  gpgme_subkey_t subkey = NULL;
+  unsigned int ret = 0;
+
+  switch (cap)
+    {
+    case KEY_CAP_CAN_ENCRYPT:
+      if (! (ret = key->can_encrypt))
+       for (subkey = key->subkeys; subkey; subkey = subkey->next)
+         if ((ret = subkey->can_encrypt))
+           break;
+      break;
+    case KEY_CAP_CAN_SIGN:
+      if (! (ret = key->can_sign))
+       for (subkey = key->subkeys; subkey; subkey = subkey->next)
+         if ((ret = subkey->can_sign))
+           break;
+      break;
+    case KEY_CAP_CAN_CERTIFY:
+      if (! (ret = key->can_certify))
+       for (subkey = key->subkeys; subkey; subkey = subkey->next)
+         if ((ret = subkey->can_certify))
+           break;
+      break;
+    }
+
+  return ret;
+}
+
+
+/* Print verbose information about a key or certificate to FP. */
+static void print_key_info (gpgme_key_t key, FILE *fp)
+{
+  int idx;
+  const char *s = NULL, *s2 = NULL;
+  time_t tt = 0;
+  struct tm *tm;
+  char shortbuf[SHORT_STRING];
+  unsigned long aval = 0;
+  const char *delim;
+  int is_pgp = 0;
+  int i;
+  gpgme_user_id_t uid = NULL;
+
+  if (Locale)
+    setlocale (LC_TIME, Locale);
+
+  is_pgp = key->protocol == GPGME_PROTOCOL_OpenPGP;
+
+  for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next)
+    {
+      if (uid->revoked)
+        continue;
+
+      s = uid->uid;
+      fprintf (fp, "%s ......: ", idx ? _(" aka") :_("Name"));
+      if (uid->invalid)
+        {
+          fputs (_("[Invalid]"), fp);
+          putc (' ', fp);
+        }
+      if (is_pgp)
+        print_utf8 (fp, s, strlen(s));
+      else
+        parse_and_print_user_id (fp, s);
+      putc ('\n', fp);
+    }
+
+  if (key->subkeys && (key->subkeys->timestamp > 0))
+    {
+      tt = key->subkeys->timestamp;
+
+      tm = localtime (&tt);
+#ifdef HAVE_LANGINFO_D_T_FMT
+      strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
+#else
+      strftime (shortbuf, sizeof shortbuf, "%c", tm);
+#endif
+      fprintf (fp, "Valid From : %s\n", shortbuf);
+    }
+  
+  if (key->subkeys && (key->subkeys->expires > 0))
+    {
+      tt = key->subkeys->expires;
+      
+      tm = localtime (&tt);
+#ifdef HAVE_LANGINFO_D_T_FMT
+      strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
+#else
+      strftime (shortbuf, sizeof shortbuf, "%c", tm);
+#endif
+      fprintf (fp, "Valid To ..: %s\n", shortbuf);
+    }
+
+  if (key->subkeys)
+    s = gpgme_pubkey_algo_name (key->subkeys->pubkey_algo);
+  else
+    s = "?";
+
+  s2 = is_pgp ? "PGP" : "X.509";
+
+  if (key->subkeys)
+    aval = key->subkeys->length;
+
+  fprintf (fp, "Key Type ..: %s, %lu bit %s\n", s2, aval, s);
+
+  fprintf (fp, "Key Usage .: ");
+  delim = "";
+
+  if (key_check_cap (key, KEY_CAP_CAN_ENCRYPT))
+    {
+      fprintf (fp, "%s%s", delim, _("encryption"));
+      delim = ", ";
+    }
+  if (key_check_cap (key, KEY_CAP_CAN_SIGN))
+    {
+      fprintf (fp, "%s%s", delim, _("signing"));
+      delim = ", ";
+    }
+  if (key_check_cap (key, KEY_CAP_CAN_CERTIFY))
+    {
+      fprintf (fp, "%s%s", delim, _("certification"));
+      delim = ", ";
+    }
+  putc ('\n', fp);
+
+  if (key->subkeys)
+    {
+      s = key->subkeys->fpr;
+      fputs (_("Fingerprint: "), fp);
+      if (is_pgp && strlen (s) == 40)
+        {
+          for (i=0; *s && s[1] && s[2] && s[3] && s[4]; s += 4, i++)
+            {
+              putc (*s, fp);
+              putc (s[1], fp);
+              putc (s[2], fp);
+              putc (s[3], fp);
+              putc (is_pgp? ' ':':', fp);
+              if (is_pgp && i == 4)
+                putc (' ', fp);
+            }
+        }
+      else
+        {
+          for (i=0; *s && s[1] && s[2]; s += 2, i++)
+            {
+              putc (*s, fp);
+              putc (s[1], fp);
+              putc (is_pgp? ' ':':', fp);
+              if (is_pgp && i == 7)
+                putc (' ', fp);
+            }
+        }
+      fprintf (fp, "%s\n", s);
+    }
+
+  if (key->issuer_serial)
+    {
+      s = key->issuer_serial;
+      if (s)
+       fprintf (fp, "Serial-No .: 0x%s\n", s);
+    }
+
+  if (key->issuer_name)
+    {
+      s = key->issuer_name;
+      if (s)
+       {
+         fprintf (fp, "Issued By .: ");
+         parse_and_print_user_id (fp, s);
+         putc ('\n', fp);
+       }
+    }
+
+  /* For PGP we list all subkeys. */
+  if (is_pgp)
+    {
+      gpgme_subkey_t subkey = NULL;
+
+      for (idx = 1, subkey = key->subkeys; subkey;
+           idx++, subkey = subkey->next)
+        {
+         s = subkey->keyid;
+         
+          putc ('\n', fp);
+          if ( strlen (s) == 16)
+            s += 8; /* display only the short keyID */
+          fprintf (fp, "Subkey ....: 0x%s", s);
+         if (subkey->revoked)
+            {
+              putc (' ', fp);
+              fputs (_("[Revoked]"), fp);
+            }
+         if (subkey->invalid)
+            {
+              putc (' ', fp);
+              fputs (_("[Invalid]"), fp);
+            }
+         if (subkey->expired)
+            {
+              putc (' ', fp);
+              fputs (_("[Expired]"), fp);
+            }
+         if (subkey->disabled)
+            {
+              putc (' ', fp);
+              fputs (_("[Disabled]"), fp);
+            }
+          putc ('\n', fp);
+
+         if (subkey->timestamp > 0)
+           {
+             tt = subkey->timestamp;
+
+              tm = localtime (&tt);
+#ifdef HAVE_LANGINFO_D_T_FMT
+              strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
+#else
+              strftime (shortbuf, sizeof shortbuf, "%c", tm);
+#endif
+              fprintf (fp, "Valid From : %s\n", shortbuf);
+            }
+
+         if (subkey->expires > 0)
+           {
+             tt = subkey->expires;
+
+              tm = localtime (&tt);
+#ifdef HAVE_LANGINFO_D_T_FMT
+              strftime (shortbuf, sizeof shortbuf, nl_langinfo (D_T_FMT), tm);
+#else
+              strftime (shortbuf, sizeof shortbuf, "%c", tm);
+#endif
+              fprintf (fp, "Valid To ..: %s\n", shortbuf);
+            }
+
+         if (subkey)
+           s = gpgme_pubkey_algo_name (subkey->pubkey_algo);
+         else
+            s = "?";
+
+         if (subkey)
+           aval = subkey->length;
+         else
+           aval = 0;
+
+          fprintf (fp, "Key Type ..: %s, %lu bit %s\n", "PGP", aval, s);
+
+          fprintf (fp, "Key Usage .: ");
+          delim = "";
+
+         if (subkey->can_encrypt)
+            {
+              fprintf (fp, "%s%s", delim, _("encryption"));
+              delim = ", ";
+            }
+          if (subkey->can_sign)
+            {
+              fprintf (fp, "%s%s", delim, _("signing"));
+              delim = ", ";
+            }
+          if (subkey->can_certify)
+            {
+              fprintf (fp, "%s%s", delim, _("certification"));
+              delim = ", ";
+            }
+          putc ('\n', fp);
+        }
+    }
+
+  if (Locale)
+    setlocale (LC_TIME, "C");
+}
+
+
+/* Show detailed information about the selected key */
+static void 
+verify_key (crypt_key_t *key)
+{
+  FILE *fp;
+  char cmd[LONG_STRING], tempfile[_POSIX_PATH_MAX];
+  const char *s;
+  gpgme_ctx_t listctx = NULL;
+  gpgme_error_t err;
+  gpgme_key_t k = NULL;
+  int maxdepth = 100;
+
+  mutt_mktemp (tempfile);
+  if (!(fp = safe_fopen (tempfile, "w")))
+    {
+      mutt_perror _("Can't create temporary file");
+      return;
+    }
+  mutt_message _("Collecting data...");
+
+  print_key_info (key->kobj, fp);
+
+  err = gpgme_new (&listctx);
+  if (err)
+    {
+      fprintf (fp, "Internal error: can't create gpgme context: %s\n",
+               gpgme_strerror (err));
+      goto leave;
+    }
+  if ((key->flags & KEYFLAG_ISX509))
+      gpgme_set_protocol (listctx, GPGME_PROTOCOL_CMS);
+
+  k = key->kobj;
+  gpgme_key_ref (k);
+  while ((s = k->chain_id) && k->subkeys && strcmp (s, k->subkeys->fpr) )
+    {
+      putc ('\n', fp);
+      err = gpgme_op_keylist_start (listctx, s, 0);
+      gpgme_key_release (k);
+      k = NULL;
+      if (!err)
+       err = gpgme_op_keylist_next (listctx, &k);
+      if (err)
+        {
+          fprintf (fp, _("Error finding issuer key: %s\n"),
+                   gpgme_strerror (err));
+          goto leave;
+        }
+      gpgme_op_keylist_end (listctx);
+      
+      print_key_info (k, fp);
+      if (!--maxdepth)
+        {
+          putc ('\n', fp);
+          fputs (_("Error: certification chain to long - stopping here\n"),
+                 fp);
+          break;
+        }
+    }
+
+ leave:
+  gpgme_key_release (k);
+  gpgme_release (listctx);
+  fclose (fp);
+  mutt_clear_error ();
+  snprintf (cmd, sizeof (cmd), _("Key ID: 0x%s"),  crypt_keyid (key));
+  mutt_do_pager (cmd, tempfile, 0, NULL);
+}
+
+/* 
+ * Implementation of `findkeys'.
+ */
+
+
+/* Convert LIST 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 (LIST *list)
+{
+  LIST *l;
+  char *pattern, *p;
+  const char *s;
+  size_t n;
+
+  n = 0;
+  for(l=list; l; l = l->next)
+    {
+      for(s = l->data; *s; s++)
+        {
+          if (*s == '%')
+            n += 2;
+          n++;
+        }
+      n++; /* delimiter or end of string */
+    }
+  n++; /* make sure to allocate at least one byte */
+  pattern = p = safe_calloc (1,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 (LIST * 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));
+      FREE (&pattern);
+      return NULL;
+    }
+
+  db = NULL;
+  kend = &db;
+  
+  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. */
+      LIST *l;
+      size_t n;
+      char **patarr;
+
+      for(l=hints, n=0; l; l = l->next)
+        {
+          if (l->data && *l->data)
+            n++;
+        }
+      if (!n)
+        goto no_pgphints;
+
+      patarr = safe_calloc (n+1, sizeof *patarr);
+      for(l=hints, n=0; l; l = l->next)
+        {
+          if (l->data && *l->data)
+            patarr[n++] = safe_strdup (l->data);
+        }
+      patarr[n] = NULL;
+      err = gpgme_op_keylist_ext_start (ctx, (const char**)patarr, secret, 0);
+      for (n=0; patarr[n]; n++)
+        FREE (&patarr[n]);
+      FREE (&patarr);
+      if (err) 
+        {
+          mutt_error ("gpgme_op_keylist_start failed: %s",
+                      gpgme_strerror (err));
+          gpgme_release (ctx);
+          FREE (&pattern);
+          return NULL;
+        }
+
+      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;
+
+#if 0 /* DISABLED code */
+          if (!flags)
+            {
+             /* Bug in gpg.  Capabilities are not listed for secret
+                keys.  Try to deduce them from the algorithm. */
+
+             switch (key->subkeys[0].pubkey_algo)
+                {
+                case GPGME_PK_RSA:
+                  flags |= KEYFLAG_CANENCRYPT;
+                  flags |= KEYFLAG_CANSIGN;
+                  break;
+                case GPGME_PK_ELG_E:
+                  flags |= KEYFLAG_CANENCRYPT;
+                  break;
+                case GPGME_PK_DSA:
+                  flags |= KEYFLAG_CANSIGN;
+                  break;
+                }
+            }
+#endif /* DISABLED code */
+
+         for (idx = 0, uid = key->uids; uid; idx++, uid = uid->next)
+            {
+              k = safe_calloc (1, sizeof *k);
+              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))
+    {
+      /* 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);
+          FREE (&pattern);
+          return NULL;
+        }
+
+      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 = safe_calloc (1, sizeof *k);
+              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);
+  FREE (&pattern);
+  return db;
+}
+
+/* Add the string STR to the list HINTS.  This list is later used to
+   match addresses. */
+static LIST *crypt_add_string_to_hints (LIST *hints, const char *str)
+{
+  char *scratch;
+  char *t;
+
+  if ((scratch = safe_strdup (str)) == NULL)
+    return hints;
+
+  for (t = strtok (scratch, " ,.:\"()<>\n"); t;
+       t = strtok (NULL, " ,.:\"()<>\n"))
+    {
+      if (strlen (t) > 3)
+        hints = mutt_add_list (hints, t);
+    }
+  
+  FREE (&scratch);
+  return hints;
+}
+
+/* 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,
+                                      ADDRESS * p, const char *s, 
+                                      unsigned int app, int *forced_valid)
+{
+  int keymax;
+  crypt_key_t **key_table;
+  MUTTMENU *menu;
+  int i, done = 0;
+  char helpstr[SHORT_STRING], buf[LONG_STRING];
+  crypt_key_t *k;
+  int (*f) (const void *, const void *);
+  int menu_to_use = 0;
+  int unusable = 0;
+
+  *forced_valid = 0;
+
+  /* build the key table */
+  keymax = i = 0;
+  key_table = NULL;
+  for (k = keys; k; k = k->next)
+    {
+      if (!option (OPTPGPSHOWUNUSABLE) && (k->flags & KEYFLAG_CANTUSE))
+        {
+          unusable = 1;
+          continue;
+        }
+      
+      if (i == keymax)
+        {
+          keymax += 20;
+          safe_realloc (&key_table, sizeof (crypt_key_t*)*keymax);
+        }
+      
+      key_table[i++] = k;
+    }
+
+  if (!i && unusable)
+    {
+      mutt_error _("All matching keys are marked expired/revoked.");
+      mutt_sleep (1);
+      return NULL;
+    }
+
+  switch (PgpSortKeys & SORT_MASK)
+  {
+    case SORT_DATE:
+      f = crypt_compare_date;
+      break;
+    case SORT_KEYID:
+      f = crypt_compare_keyid;
+      break;
+    case SORT_ADDRESS:
+      f = crypt_compare_address;
+      break;
+    case SORT_TRUST:
+    default:
+      f = crypt_compare_trust;
+      break;
+  }
+  qsort (key_table, i, sizeof (crypt_key_t*), f);
+
+  if (app & APPLICATION_PGP)
+    menu_to_use = MENU_KEY_SELECT_PGP;
+  else if (app & APPLICATION_SMIME)
+    menu_to_use = MENU_KEY_SELECT_SMIME;
+
+  helpstr[0] = 0;
+  mutt_make_help (buf, sizeof (buf), _("Exit  "), menu_to_use, OP_EXIT);
+  strcat (helpstr, buf);       /* __STRCAT_CHECKED__ */
+  mutt_make_help (buf, sizeof (buf), _("Select  "), menu_to_use,
+                 OP_GENERIC_SELECT_ENTRY);
+  strcat (helpstr, buf);       /* __STRCAT_CHECKED__ */
+  mutt_make_help (buf, sizeof (buf), _("Check key  "),
+                  menu_to_use, OP_VERIFY_KEY);
+  strcat (helpstr, buf);       /* __STRCAT_CHECKED__ */
+  mutt_make_help (buf, sizeof (buf), _("Help"), menu_to_use, OP_HELP);
+  strcat (helpstr, buf);       /* __STRCAT_CHECKED__ */
+
+  menu = mutt_new_menu ();
+  menu->max = i;
+  menu->make_entry = crypt_entry;
+  menu->menu = menu_to_use;
+  menu->help = helpstr;
+  menu->data = key_table;
+
+  {
+    const char *ts;
+
+    if ((app & APPLICATION_PGP) && (app &  APPLICATION_SMIME))
+      ts = _("PGP and S/MIME keys matching");
+    else if ((app & APPLICATION_PGP))
+      ts = _("PGP keys matching");
+    else if ((app & APPLICATION_SMIME))
+      ts = _("S/MIME keys matching");
+    else
+      ts = _("keys matching");
+
+    if (p)
+      snprintf (buf, sizeof (buf), _("%s <%s>."), ts, p->mailbox);
+    else
+      snprintf (buf, sizeof (buf), _("%s \"%s\"."), ts, s);
+    menu->title = buf; 
+  }
+
+  mutt_clear_error ();
+  k = NULL;
+  while (!done)
+    {
+      *forced_valid = 0;
+      switch (mutt_menuLoop (menu))
+        {
+        case OP_VERIFY_KEY:
+          verify_key (key_table[menu->current]);
+          menu->redraw = REDRAW_FULL;
+          break;
+          
+        case OP_VIEW_ID:
+          mutt_message ("%s", key_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.");
+                break;
+              }
+            }
+          
+          if (option (OPTPGPCHECKTRUST) &&
+              (!crypt_id_is_valid (key_table[menu->current])
+               || !crypt_id_is_strong (key_table[menu->current])))
+            {
+              const char *warn_s;
+              char buff[LONG_STRING];
+              
+              if (key_table[menu->current]->flags & KEYFLAG_CANTUSE)
+                s = N_("ID is expired/disabled/revoked.");
+              else 
+                {
+                 gpgme_validity_t val = GPGME_VALIDITY_UNKNOWN;
+                 gpgme_user_id_t uid = NULL;
+                 unsigned int j = 0;
+
+                  warn_s = "??";
+
+                 uid = key_table[menu->current]->kobj->uids;
+                 for (j = 0; (j < key_table[menu->current]->idx) && uid;
+                       j++, uid = uid->next)
+                    ;
+                 if (uid)
+                   val = uid->validity;
+
+                  switch (val)
+                    {
+                    case GPGME_VALIDITY_UNKNOWN:   
+                    case GPGME_VALIDITY_UNDEFINED: 
+                      warn_s = N_("ID has undefined validity.");
+                      break;
+                    case GPGME_VALIDITY_NEVER:     
+                      warn_s = N_("ID is not valid.");
+                      break;
+                    case GPGME_VALIDITY_MARGINAL:  
+                      warn_s = N_("ID is only marginally valid.");
+                      break;
+                    case GPGME_VALIDITY_FULL:      
+                    case GPGME_VALIDITY_ULTIMATE:  
+                      break;
+                    }
+
+                  snprintf (buff, sizeof (buff),
+                            _("%s Do you really want to use the key?"),
+                            _(warn_s));
+                 
+                  if (mutt_yesorno (buff, 0) != 1)
+                    {
+                      mutt_clear_error ();
+                      break;
+                    }
+                  *forced_valid = 1;
+                }
+            }  
+
+          k = crypt_copy_key (key_table[menu->current]);
+          done = 1;
+          break;
+          
+        case OP_EXIT:
+          k = NULL;
+          done = 1;
+          break;
+        }
+    }
+  
+  mutt_menuDestroy (&menu);
+  FREE (&key_table);
+
+  set_option (OPTNEEDREDRAW);
+  
+  return k;
+}
+
+static crypt_key_t *crypt_getkeybyaddr (ADDRESS * a, short abilities,
+                                       unsigned int app, int *forced_valid)
+{
+  ADDRESS *r, *p;
+  LIST *hints = NULL;
+
+  int weak    = 0;
+  int invalid = 0;
+  int multi   = 0;
+  int this_key_has_strong;
+  int this_key_has_weak;
+  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;
+  
+  *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) );
+
+  mutt_free_list (&hints);
+  
+  if (!keys)
+    return NULL;
+  
+  dprint (5, (debugfile, "crypt_getkeybyaddr: looking for %s <%s>.",
+             a->personal, a->mailbox));
+
+  for (k = keys; k; k = k->next)
+    {
+      dprint (5, (debugfile, "  looking at key: %s `%.15s'\n",
+                  crypt_keyid (k), k->uid));
+      
+      if (abilities && !(k->flags & abilities))
+        {
+          dprint (5, (debugfile, "  insufficient abilities: Has %x, want %x\n",
+                      k->flags, abilities));
+          continue;
+        }
+
+      this_key_has_weak    = 0;        /* weak but valid match   */
+      this_key_has_invalid = 0;   /* invalid match          */
+      this_key_has_strong  = 0;        /* strong and valid match */
+      match                = 0;   /* any match                   */
+
+      r = rfc822_parse_adrlist (NULL, k->uid);
+      for (p = r; p; p = p->next)
+        {
+          int validity = crypt_id_matches_addr (a, p, k);
+              
+          if (validity & CRYPT_KV_MATCH)       /* something matches */
+            match = 1;
+
+          /* is this key a strong candidate? */
+          if ((validity & CRYPT_KV_VALID)
+              && (validity & CRYPT_KV_STRONGID) 
+              && (validity & CRYPT_KV_ADDR))
+            {
+              if (the_valid_key && the_valid_key != k)
+                multi             = 1;
+              the_valid_key       = k;
+              this_key_has_strong = 1;
+            }
+          else if ((validity & CRYPT_KV_MATCH)
+                   && !(validity & CRYPT_KV_VALID))
+            this_key_has_invalid = 1;
+          else if ((validity & CRYPT_KV_MATCH) 
+                   && (!(validity & CRYPT_KV_STRONGID)
+                       || !(validity & CRYPT_KV_ADDR)))
+            this_key_has_weak    = 1;
+        }
+      rfc822_free_address (&r);
+      
+      if (match)
+        {
+          crypt_key_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->next;
+         the_valid_key = tmp;
+        }
+    }
+  
+  crypt_free_key (&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);
+        }
+      crypt_free_key (&matches);
+    }
+  else 
+    k = NULL;
+  
+  return k;
+}
+
+
+static crypt_key_t *crypt_getkeybystr (char *p, short abilities,
+                                      unsigned int app, int *forced_valid)
+{
+  LIST *hints = NULL;
+  crypt_key_t *keys;
+  crypt_key_t *matches = NULL;
+  crypt_key_t **matches_endp = &matches;
+  crypt_key_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));
+  mutt_free_list (&hints);
+
+  if (!keys)
+    return NULL;
+  
+  for (k = keys; k; k = k->next)
+    {
+      if (abilities && !(k->flags & abilities))
+        continue;
+
+      match = 0;
+      dprint (5, (debugfile, "crypt_getkeybystr: matching \"%s\" against "
+                  "key %s, \"%s\": ",  p, crypt_keyid (k), k->uid));
+
+      if (!*p
+          || !mutt_strcasecmp (p, crypt_keyid (k))
+          || (!mutt_strncasecmp (p, "0x", 2)
+              && !mutt_strcasecmp (p + 2, crypt_keyid (k)))
+          || (option (OPTPGPLONGIDS)
+              && !mutt_strncasecmp (p, "0x", 2) 
+              && !mutt_strcasecmp (p + 2, crypt_keyid (k) + 8))
+          || mutt_stristr (k->uid, p))
+        {
+          crypt_key_t *tmp;
+
+          dprint (5, (debugfile, "match.\n"));
+
+          *matches_endp = tmp = crypt_copy_key (k);
+          matches_endp = &tmp->next;
+        }
+    }
+  
+  crypt_free_key (&keys);
+  
+  if (matches)
+    {
+      k = crypt_select_key (matches, NULL, p, app, forced_valid);
+      crypt_free_key (&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)
+{
+  crypt_key_t *key;
+  char resp[SHORT_STRING];
+  struct crypt_cache *l = NULL;
+  int dummy;
+
+  if (!forced_valid)
+    forced_valid = &dummy;
+
+  mutt_clear_error ();
+
+  *forced_valid = 0;
+  resp[0] = 0;
+  if (whatfor)
+    {
+      
+      for (l = id_defaults; l; l = l->next)
+        if (!mutt_strcasecmp (whatfor, l->what))
+          {
+            strfcpy (resp, NONULL (l->dflt), sizeof (resp));
+            break;
+          }
+    }
+
+
+  for (;;)
+    {
+      resp[0] = 0;
+      if (mutt_get_field (tag, resp, sizeof (resp), M_CLEAR) != 0)
+        return NULL;
+      
+      if (whatfor)
+        {
+          if (l)
+            mutt_str_replace (&l->dflt, resp);
+          else
+            {
+              l = safe_malloc (sizeof (struct crypt_cache));
+              l->next = id_defaults;
+              id_defaults = l;
+              l->what = safe_strdup (whatfor);
+              l->dflt = safe_strdup (resp);
+            }
+        }
+      
+      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 *to, ADDRESS *cc, ADDRESS *bcc,
+                        unsigned int app)
+{
+  char *keyID, *keylist = NULL, *t;
+  size_t keylist_size = 0;
+  size_t keylist_used = 0;
+  ADDRESS *tmp = NULL, *addr = NULL;
+  ADDRESS **last = &tmp;
+  ADDRESS *p, *q;
+  int i;
+  crypt_key_t *k_info, *key;
+  const char *fqdn = mutt_fqdn (1);
+
+#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 = rfc822_cpy_adr (p);
+      while (*last)
+        last = &((*last)->next);
+    }
+  
+  if (fqdn)
+    rfc822_qualify (tmp, fqdn);
+  
+  tmp = mutt_remove_duplicates (tmp);
+  
+  for (p = tmp; p ; p = p->next)
+    {
+      char buf[LONG_STRING];
+      int forced_valid = 0;
+      
+      q = p;
+      k_info = NULL;
+      
+      if ((keyID = mutt_crypt_hook (p)) != NULL)
+        {
+          int r;
+          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)))
+                {
+                  if (fqdn)
+                    rfc822_qualify (addr, fqdn);
+                  q = addr;
+                }
+              else
+               {
+#if 0            
+                 k_info = crypt_getkeybystr (keyID, KEYFLAG_CANENCRYPT, 
+                                             *r_application, &forced_valid);
+#else
+                 k_info = crypt_getkeybystr (keyID, KEYFLAG_CANENCRYPT, 
+                                             app, &forced_valid);
+#endif
+               }
+            }
+          else if (r == -1)
+            {
+              FREE (&keylist);
+              rfc822_free_address (&tmp);
+              rfc822_free_address (&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 ((key = crypt_ask_for_key (buf, q->mailbox,
+                                        KEYFLAG_CANENCRYPT,
+#if 0
+                                        *r_application,
+#else
+                                       app,
+#endif
+                                       &forced_valid)) == NULL)
+            {
+              FREE (&keylist);
+              rfc822_free_address (&tmp);
+              rfc822_free_address (&addr);
+              return NULL;
+            }
+        }
+      else
+        key = k_info;
+
+      {
+        const char *s = crypt_fpr (key);
+
+#if 0
+        if (key->flags & KEYFLAG_ISX509)
+          *r_application &= ~APPLICATION_PGP;
+        if (!(key->flags & KEYFLAG_ISX509))
+          *r_application &= ~APPLICATION_SMIME;
+#endif
+      
+        keylist_size += mutt_strlen (s) + 4 + 1;
+        safe_realloc (&keylist, keylist_size);
+        sprintf (keylist + keylist_used, "%s0x%s%s", /* __SPRINTF_CHECKED__ */
+                 keylist_used ? " " : "",  s,
+                 forced_valid? "!":"");
+      }
+      keylist_used = mutt_strlen (keylist);
+        
+      crypt_free_key (&key);
+      rfc822_free_address (&addr);
+    }
+  rfc822_free_address (&tmp);
+  return (keylist);
+}
+
+char *pgp_gpgme_findkeys (ADDRESS *to, ADDRESS *cc, ADDRESS *bcc)
+{
+  return find_keys (to, cc, bcc, APPLICATION_PGP);
+}
+
+char *smime_gpgme_findkeys (ADDRESS *to, ADDRESS *cc, ADDRESS *bcc)
+{
+  return find_keys (to, cc, bcc, APPLICATION_SMIME);
+}
+
+/*
+ * Implementation of `init'.
+ */
+
+/* Initialization.  */
+static void init_gpgme (void)
+{
+  /* Make sure that gpg-agent is running.  */
+  if (! getenv ("GPG_AGENT_INFO"))
+    {
+      mutt_error ("\nUsing GPGME backend, although no gpg-agent is running");
+      if (mutt_any_key_to_continue (NULL) == -1)
+       mutt_exit(1);
+    }
+}
+
+void pgp_gpgme_init (void)
+{
+  init_gpgme ();
+}
+
+void smime_gpgme_init (void)
+{
+}
+
+static int gpgme_send_menu (HEADER *msg, int *redraw, int is_smime)
+{
+  crypt_key_t *p;
+  char input_signas[SHORT_STRING];
+  int choice;
+
+  if (msg->security & APPLICATION_PGP)
+    is_smime = 0;
+  else if (msg->security & APPLICATION_SMIME)
+    is_smime = 1;
+
+  if (is_smime)
+    choice = mutt_multi_choice (
+    _("S/MIME (e)ncrypt, (s)ign, sign (a)s, (b)oth, (t)oggle or (f)orget it?"),
+             _("esabtf"));
+  else 
+    choice = mutt_multi_choice (
+    _("PGP (e)ncrypt, (s)ign, sign (a)s, (b)oth, (t)oggle or (f)orget it?"),
+                _("esabtf"));
+
+  switch (choice)
+  {
+  case 1: /* (e)ncrypt */
+    msg->security |= (is_smime ? SMIMEENCRYPT : PGPENCRYPT);
+    break;
+
+  case 2: /* (s)ign */
+    msg->security |= (is_smime? SMIMESIGN :PGPSIGN);
+    break;
+
+  case 3: /* sign (a)s */
+/*      unset_option(OPTCRYPTCHECKTRUST); */
+    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));
+      mutt_str_replace (is_smime? &SmimeDefaultKey : &PgpSignAs, input_signas);
+      crypt_free_key (&p); 
+      
+      msg->security |= (is_smime? SMIMESIGN:PGPSIGN);
+    }
+    else
+    {
+      msg->security &= (is_smime? ~SMIMESIGN : ~PGPSIGN);
+    }
+    *redraw = REDRAW_FULL;
+    break;
+
+  case 4: /* (b)oth */
+    msg->security = (is_smime? (SMIMEENCRYPT|SMIMESIGN):(PGPENCRYPT|PGPSIGN));
+    break;
+
+  case 5: /* (t)oggle */
+    is_smime = !is_smime;
+    break;
+
+  case 6: /* (f)orget it */
+    msg->security = 0;
+    break;
+  }
+
+  if (choice == 6)
+    ;
+  else if (is_smime)
+    {
+      msg->security &= ~APPLICATION_PGP;
+      msg->security |= APPLICATION_SMIME;
+    }
+  else
+    {
+      msg->security &= ~APPLICATION_SMIME;
+      msg->security |= APPLICATION_PGP;
+    }
+  
+  return (msg->security);
+}
+
+int pgp_gpgme_send_menu (HEADER *msg, int *redraw)
+{
+  return gpgme_send_menu (msg, redraw, 0);
+}
+
+int smime_gpgme_send_menu (HEADER *msg, int *redraw)
+{
+  return gpgme_send_menu (msg, redraw, 1);
+}
+
+static int verify_sender (HEADER *h, gpgme_protocol_t protocol)
+{
+  ADDRESS *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 (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 = strlen (sender->mailbox);
+         for (uid = key->uids; uid && ret; uid = uid->next)
+           {
+             uid_length = strlen (uid->email);
+             if (1
+                 && (uid->email[0] == '<')
+                 && (uid->email[uid_length - 1] == '>')
+                 && (uid_length == sender_length + 2)
+                 && (! strncmp (uid->email + 1, sender->mailbox, sender_length)))
+               ret = 0;
+           }
+       }
+      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;
+    }
+
+  return ret;
+}
+
+int smime_gpgme_verify_sender (HEADER *h)
+{
+  return verify_sender (h, GPGME_PROTOCOL_CMS);
+}
+
+#endif
diff --git a/crypt-gpgme.h b/crypt-gpgme.h
new file mode 100644 (file)
index 0000000..4901351
--- /dev/null
@@ -0,0 +1,53 @@
+/* 
+ * Copyright (C) 2004 g10 Code GmbH
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ * 
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ * 
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ */
+
+#ifndef CRYPT_GPGME_H
+#define CRYPT_GPGME_H
+
+#include "mutt_crypt.h"
+
+void pgp_gpgme_init (void);
+void smime_gpgme_init (void);
+
+char *pgp_gpgme_findkeys (ADDRESS *to, ADDRESS *cc, ADDRESS *bcc);
+char *smime_gpgme_findkeys (ADDRESS *to, ADDRESS *cc, ADDRESS *bcc);
+
+BODY *pgp_gpgme_encrypt_message (BODY *a, char *keylist, int sign);
+BODY *smime_gpgme_build_smime_entity (BODY *a, char *keylist);
+
+int pgp_gpgme_decrypt_mime (FILE *fpin, FILE **fpout, BODY *b, BODY **cur);
+int smime_gpgme_decrypt_mime (FILE *fpin, FILE **fpout, BODY *b, BODY **cur);
+
+int pgp_gpgme_check_traditional (FILE *fp, BODY *b, int tagged_only);
+
+void pgp_gpgme_application_handler (BODY *m, STATE *s);
+void smime_gpgme_application_handler (BODY *a, STATE *s);
+void pgp_gpgme_encrypted_handler (BODY *a, STATE *s);
+
+BODY *pgp_gpgme_make_key_attachment (char *tempf);
+
+BODY *pgp_gpgme_sign_message (BODY *a);
+BODY *smime_gpgme_sign_message (BODY *a);
+
+int pgp_gpgme_verify_one (BODY *sigbdy, STATE *s, const char *tempfile);
+int smime_gpgme_verify_one (BODY *sigbdy, STATE *s, const char *tempfile);
+
+int pgp_gpgme_send_menu (HEADER *msg, int *redraw);
+int smime_gpgme_send_menu (HEADER *msg, int *redraw);
+
+#endif
diff --git a/crypt-mod-pgp-gpgme.c b/crypt-mod-pgp-gpgme.c
new file mode 100644 (file)
index 0000000..05e1699
--- /dev/null
@@ -0,0 +1,122 @@
+/* 
+ * Copyright (C) 2004 g10 Code GmbH
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ * 
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ * 
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ */
+
+/* 
+    This is a crytpo module wrapping the gpgme based pgp code.
+ */
+
+#include <config.h>
+
+#ifdef CRYPT_BACKEND_GPGME
+
+#include "crypt-mod.h"
+#include "crypt-gpgme.h"
+
+static void crypt_mod_pgp_init (void)
+{
+  pgp_gpgme_init ();
+}
+
+static void crypt_mod_pgp_void_passphrase (void)
+{
+  /* Handled by gpg-agent.  */
+}
+
+static int crypt_mod_pgp_valid_passphrase (void)
+{
+  /* Handled by gpg-agent.  */
+  return 1;
+}
+
+static int crypt_mod_pgp_decrypt_mime (FILE *a, FILE **b, BODY *c, BODY **d)
+{
+  return pgp_gpgme_decrypt_mime (a, b, c, d);
+}
+
+static void crypt_mod_pgp_application_handler (BODY *m, STATE *s)
+{
+  pgp_gpgme_application_handler (m, s);
+}
+
+static void crypt_mod_pgp_encrypted_handler (BODY *m, STATE *s)
+{
+  pgp_gpgme_encrypted_handler (m, s);
+}
+
+static int crypt_mod_pgp_check_traditional (FILE *fp, BODY *b, int tagged_only)
+{
+  return pgp_gpgme_check_traditional (fp, b, tagged_only);
+}
+
+static char *crypt_mod_pgp_findkeys (ADDRESS *to, ADDRESS *cc, ADDRESS *bcc)
+{
+  return pgp_gpgme_findkeys (to, cc, bcc);
+}
+
+static BODY *crypt_mod_pgp_sign_message (BODY *a)
+{
+  return pgp_gpgme_sign_message (a);
+}
+
+static int crypt_mod_pgp_verify_one (BODY *sigbdy, STATE *s, const char *tempf)
+{
+  return pgp_gpgme_verify_one (sigbdy, s, tempf);
+}
+
+static int crypt_mod_pgp_send_menu (HEADER *msg, int *redraw)
+{
+  return pgp_gpgme_send_menu (msg, redraw);
+}
+
+static BODY *crypt_mod_pgp_encrypt_message (BODY *a, char *keylist, int sign)
+{
+  return pgp_gpgme_encrypt_message (a, keylist, sign);
+}
+
+struct crypt_module_specs crypt_mod_pgp_gpgme =
+  { APPLICATION_PGP,
+    {
+      /* Common.  */
+      crypt_mod_pgp_init,
+      crypt_mod_pgp_void_passphrase,
+      crypt_mod_pgp_valid_passphrase,
+      crypt_mod_pgp_decrypt_mime,
+      crypt_mod_pgp_application_handler,
+      crypt_mod_pgp_encrypted_handler,
+      crypt_mod_pgp_findkeys,
+      crypt_mod_pgp_sign_message,
+      crypt_mod_pgp_verify_one,
+      crypt_mod_pgp_send_menu,
+
+      /* PGP specific.  */
+      crypt_mod_pgp_encrypt_message,
+      NULL,                    /* pgp_make_key_attachment, */
+      crypt_mod_pgp_check_traditional,
+      NULL,                    /* pgp_traditional_encryptsign  */
+      NULL, /* pgp_invoke_getkeys  */
+      NULL, /* pgp_invoke_import  */
+      NULL, /* pgp_extract_keys_from_attachment_list  */
+
+      NULL,                    /* smime_getkeys */
+      NULL,                    /* smime_verify_sender */
+      NULL,                    /* smime_build_smime_entity */
+      NULL,                    /* smime_invoke_import */
+    }
+  };
+
+#endif
diff --git a/crypt-mod-smime-gpgme.c b/crypt-mod-smime-gpgme.c
new file mode 100644 (file)
index 0000000..263b94f
--- /dev/null
@@ -0,0 +1,115 @@
+/* 
+ * Copyright (C) 2004 g10 Code GmbH
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ * 
+ *     This program is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *     GNU General Public License for more details.
+ * 
+ *     You should have received a copy of the GNU General Public License
+ *     along with this program; if not, write to the Free Software
+ *     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
+ */
+
+/* 
+    This is a crytpo module wrapping the gpgme based smime code.
+ */
+
+#include <config.h>
+
+#ifdef CRYPT_BACKEND_GPGME
+
+#include "crypt-mod.h"
+#include "crypt-gpgme.h"
+
+static void crypt_mod_smime_init (void)
+{
+  smime_gpgme_init ();
+}
+
+static void crypt_mod_smime_void_passphrase (void)
+{
+  /* Handled by gpg-agent.  */
+}
+
+static int crypt_mod_smime_valid_passphrase (void)
+{
+  /* Handled by gpg-agent.  */
+  return 1;
+}
+
+static int crypt_mod_smime_decrypt_mime (FILE *a, FILE **b, BODY *c, BODY **d)
+{
+  return smime_gpgme_decrypt_mime (a, b, c, d);
+}
+
+static void crypt_mod_smime_application_handler (BODY *m, STATE *s)
+{
+  smime_gpgme_application_handler (m, s);
+}
+
+static char *crypt_mod_smime_findkeys (ADDRESS *to, ADDRESS *cc, ADDRESS *bcc)
+{
+  return smime_gpgme_findkeys (to, cc, bcc);
+}
+
+static BODY *crypt_mod_smime_sign_message (BODY *a)
+{
+  return smime_gpgme_sign_message (a);
+}
+
+static int crypt_mod_smime_verify_one (BODY *sigbdy, STATE *s, const char *tempf)
+{
+  return smime_gpgme_verify_one (sigbdy, s, tempf);
+}
+
+static int crypt_mod_smime_send_menu (HEADER *msg, int *redraw)
+{
+  return smime_gpgme_send_menu (msg, redraw);
+}
+
+static BODY *crypt_mod_smime_build_smime_entity (BODY *a, char *certlist)
+{
+  return smime_gpgme_build_smime_entity (a, certlist);
+}
+
+static int crypt_mod_smime_verify_sender (HEADER *h)
+{
+  return smime_gpgme_verify_sender (h);
+}
+
+struct crypt_module_specs crypt_mod_smime_gpgme =
+  { APPLICATION_SMIME,
+    {
+      crypt_mod_smime_init,
+      crypt_mod_smime_void_passphrase,
+      crypt_mod_smime_valid_passphrase,
+      crypt_mod_smime_decrypt_mime,
+      crypt_mod_smime_application_handler,
+      NULL,                    /* encrypted_handler */
+      crypt_mod_smime_findkeys,
+      crypt_mod_smime_sign_message,
+      crypt_mod_smime_verify_one,
+      crypt_mod_smime_send_menu,
+
+      NULL,                    /* pgp_encrypt_message */
+      NULL,                    /* pgp_make_key_attachment */
+      NULL,                    /* pgp_check_traditional */
+      NULL,                    /* pgp_traditional_encryptsign */
+      NULL,                    /* pgp_invoke_getkeys */
+      NULL,                    /* pgp_invoke_import */
+      NULL,                    /* pgp_extract_keys_from_attachment_list */
+      
+      NULL,                    /* smime_getkeys */
+      crypt_mod_smime_verify_sender,
+      crypt_mod_smime_build_smime_entity,
+      NULL,                    /* smime_invoke_import */
+    }
+  };
+
+#endif
index f8ff4793a6130f58835f207e357cf79c7ba33422..ae25b11db0af62a2210910ec033967f6c922b581 100644 (file)
@@ -84,6 +84,8 @@ void crypt_init (void)
 #else
       mutt_message (_("\"crypt_use_gpgme\" set"
                       " but not build with GPGME support."));
+      if (mutt_any_key_to_continue (NULL) == -1)
+       mutt_exit(1);
 #endif
     }
 
index a302112613946bbf0fed73948ef8e35061065405..d1a7ca1051cf15967796b7c44b056d5aa728fcc9 100644 (file)
@@ -400,11 +400,20 @@ struct binding_t OpPgp[] = {
   { NULL,              0,                              NULL }
 };
 
-/* Don't know an useful key binding yet. But. just in case, adding this already */
+
+
+/* When using the GPGME based backend we have some useful functions
+   for the SMIME menu.  */
 struct binding_t OpSmime[] = {
+#ifdef CRYPT_BACKEND_GPGME
+  { "verify-key",    OP_VERIFY_KEY,             "c" },
+  { "view-name",     OP_VIEW_ID,               "%" },
+#endif
   { NULL,      0,      NULL }
 };
 
+
+
 #ifdef MIXMASTER
 struct binding_t OpMix[] = {
   { "accept",          OP_MIX_USE,     M_ENTER_S },