Support displaying application/pgp-keys with GPGME.
authorBrendan Cully <brendan@kublai.com>
Wed, 2 Jul 2008 00:20:02 +0000 (17:20 -0700)
committerBrendan Cully <brendan@kublai.com>
Wed, 2 Jul 2008 00:20:02 +0000 (17:20 -0700)
This was pretty convoluted because GPGME provides no way to examine a
key block without importing it. This code creates a temporary GPG home
in which to import the key in order to display it.

crypt-gpgme.c
lib.c
lib.h

index 288217a82a19e3ef2949ae6ef948b4f80082ab08..9f696d292acd978d48d25f368ae0d605c76d8f09 100644 (file)
@@ -1838,6 +1838,119 @@ int smime_gpgme_decrypt_mime (FILE *fpin, FILE **fpout, BODY *b, BODY **cur)
   return *cur? 0:-1;
 }
 
+static int pgp_gpgme_extract_keys (gpgme_data_t keydata, FILE** fp)
+{
+  /* there's no side-effect free way to view key data in GPGME,
+   * so we import the key into a temporary keyring */
+  char tmpdir[_POSIX_PATH_MAX];
+  char tmpfile[_POSIX_PATH_MAX];
+  gpgme_ctx_t tmpctx;
+  gpgme_error_t err;
+  gpgme_engine_info_t engineinfo;
+  gpgme_key_t key;
+  gpgme_user_id_t uid;
+  gpgme_subkey_t subkey;
+  const char* shortid;
+  int len;
+  char date[STRING];
+  int more;
+  int rc = -1;
+
+  snprintf (tmpdir, sizeof(tmpdir), "%s/mutt-gpgme-XXXXXX", Tempdir);
+  if (!mkdtemp (tmpdir))
+  {
+    dprint (1, (debugfile, "Error creating temporary GPGME home\n"));
+    return rc;
+  }
+
+  if ((err = gpgme_new (&tmpctx)) != GPG_ERR_NO_ERROR)
+  {
+    dprint (1, (debugfile, "Error creating GPGME context\n"));
+    goto err_tmpdir;
+  }
+
+  engineinfo = gpgme_ctx_get_engine_info (tmpctx);
+  while (engineinfo && engineinfo->protocol != GPGME_PROTOCOL_OpenPGP)
+    engineinfo = engineinfo->next;
+  if (!engineinfo)
+  {
+    dprint (1, (debugfile, "Error finding GPGME PGP engine\n"));
+    goto err_ctx;
+  }
+  
+  err = gpgme_ctx_set_engine_info (tmpctx, GPGME_PROTOCOL_OpenPGP,
+                                   engineinfo->file_name, tmpdir);
+  if (err != GPG_ERR_NO_ERROR)
+  {
+    dprint (1, (debugfile, "Error setting GPGME context home\n"));
+    goto err_ctx;
+  }
+
+  if ((err = gpgme_op_import (tmpctx, keydata)) != GPG_ERR_NO_ERROR)
+  {
+    dprint (1, (debugfile, "Error importing key\n"));
+    goto err_ctx;
+  }
+
+  mutt_mktemp (tmpfile);
+  *fp = safe_fopen (tmpfile, "w+");
+  if (!*fp)
+  {
+    mutt_perror (tmpfile);
+    goto err_ctx;
+  }
+  unlink (tmpfile);
+
+  err = gpgme_op_keylist_start (tmpctx, NULL, 0);
+  while (!err)
+  {
+    if ((err = gpgme_op_keylist_next (tmpctx, &key)))
+      break;
+    uid = key->uids;
+    subkey = key->subkeys;
+    more = 0;
+    while (subkey)
+    {
+      shortid = subkey->keyid;
+      len = mutt_strlen (subkey->keyid);
+      if (len > 8)
+        shortid += len - 8;
+      strftime (date, sizeof (date), "%Y-%m-%d", localtime (&subkey->timestamp));
+
+      if (!more)
+        fprintf (*fp, "%s %5.5s %d/%8s %s %s\n", more ? "sub" : "pub",
+                 gpgme_pubkey_algo_name (subkey->pubkey_algo), subkey->length,
+                 shortid, date, uid->uid);
+      else
+        fprintf (*fp, "%s %5.5s %d/%8s %s\n", more ? "sub" : "pub",
+                 gpgme_pubkey_algo_name (subkey->pubkey_algo), subkey->length,
+                 shortid, date);      
+      subkey = subkey->next;
+      more = 1;
+    }
+    gpgme_key_release (key);
+  }
+  if (gpg_err_code (err) != GPG_ERR_EOF)
+  {
+    dprint (1, (debugfile, "Error listing keys\n"));
+    goto err_fp;
+  }
+
+  rc = 0;
+
+err_fp:
+  if (rc)
+  {
+    fclose (*fp);
+    *fp = NULL;
+  }
+err_ctx:
+  gpgme_release (tmpctx);
+err_tmpdir:
+  mutt_rmtree (tmpdir);
+
+  return rc;
+}
 
 /* 
  * Implementation of `pgp_check_traditional'.
@@ -2064,7 +2177,11 @@ int pgp_gpgme_application_handler (BODY *m, STATE *s)
           /* Copy PGP material to an data container */
          armored_data = file_to_data_object (s->fpin, m->offset, m->length);
           /* Invoke PGP if needed */
-          if (!clearsign || (s->flags & M_VERIFY))
+          if (pgp_keyblock)
+          {
+            pgp_gpgme_extract_keys (armored_data, &pgpout);
+          }
+          else if (!clearsign || (s->flags & M_VERIFY))
             {
               unsigned int sig_stat = 0;
               gpgme_data_t plaintext;
diff --git a/lib.c b/lib.c
index 13417862f0b7a8e74001080a85d96c1b5ba46713..baf016c4847b19c0bb9adae8d7f445cfd244213f 100644 (file)
--- a/lib.c
+++ b/lib.c
@@ -41,6 +41,7 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <pwd.h>
+#include <dirent.h>
 
 #ifdef HAVE_SYSEXITS_H
 #include <sysexits.h>
@@ -576,6 +577,38 @@ int mutt_mkwrapdir (const char *path, char *newfile, size_t nflen,
   return 0;  
 }
 
+/* remove a directory and everything under it */
+int mutt_rmtree (const char* path)
+{
+  DIR* dirp;
+  struct dirent* de;
+  char cur[_POSIX_PATH_MAX];
+  int rc = 0;
+
+  if (!(dirp = opendir (path)))
+  {
+    dprint (1, (debugfile, "mutt_rmtree: error opening directory %s\n", path));
+    return -1;
+  }
+  while ((de = readdir (dirp)))
+  {
+    if (!strcmp (".", de->d_name) || !strcmp ("..", de->d_name))
+      continue;
+
+    snprintf (cur, sizeof (cur), "%s/%s", path, de->d_name);
+    /* XXX make nonrecursive version */
+    if (de->d_type == DT_DIR)
+      rc |= mutt_rmtree (cur);
+    else
+      rc |= unlink (cur);
+  }
+  closedir (dirp);
+
+  rc |= rmdir (path);
+
+  return rc;
+}
+
 int mutt_put_file_in_place (const char *path, const char *safe_file, const char *safe_dir)
 {
   int rv;
diff --git a/lib.h b/lib.h
index 8acab0b6ea16a380883f71dca495ff309d8218b1..4c5a9d065325e1efe4d2c35696287e3a836507ef 100644 (file)
--- a/lib.h
+++ b/lib.h
@@ -171,6 +171,7 @@ int safe_rename (const char *, const char *);
 int safe_symlink (const char *, const char *);
 int safe_fclose (FILE **);
 int safe_fsync_close (FILE **);
+int mutt_rmtree (const char *);
 
 size_t mutt_quote_filename (char *, size_t, const char *);
 size_t mutt_strlen (const char *);