Add keycache for located keys
authorAndre Heinecke <aheinecke@intevation.de>
Thu, 1 Mar 2018 12:39:06 +0000 (13:39 +0100)
committerAndre Heinecke <aheinecke@intevation.de>
Thu, 1 Mar 2018 12:45:24 +0000 (13:45 +0100)
* src/keycache.cpp, src/keycache.h: New.
* src/Makefile.am: Add new files.
* src/mail.cpp (in_de_vs_mode): Move to cpphelp.
Rename global maps to s_ prefix to be more clear.
(Mail::locate_keys): Use keycache.
(do_locate): Removed.
(Mail::locate_all_crypto_recipents): New.
* src/cpphelp.cpp: Add in_de_vs_mode.
* src/ribbon-callbacks.cpp (mark_mime_action): Trigger
locate on crypto toggle.

--
Every serious MUA needs a Keycache ;-)

This is needed to store the keys returned by
gpg --locate-keys if we want to use them later to automatically
send to the keys located this way.

src/Makefile.am
src/cpphelp.cpp
src/cpphelp.h
src/keycache.cpp [new file with mode: 0644]
src/keycache.h [new file with mode: 0644]
src/mail.cpp
src/mail.h
src/ribbon-callbacks.cpp

index 7ef1edd..125265d 100644 (file)
@@ -88,7 +88,8 @@ gpgol_SOURCES = \
        cryptcontroller.cpp cryptcontroller.h \
        cpphelp.cpp cpphelp.h \
        wks-helper.cpp wks-helper.h \
-       overlay.cpp overlay.h
+       overlay.cpp overlay.h \
+       keycache.cpp keycache.h
 
 
 #treeview_SOURCES = treeview.c
index 20b91d3..85c8db3 100644 (file)
 
 #include "common.h"
 
+#include <gpgme++/context.h>
+#include <gpgme++/error.h>
+#include <gpgme++/configuration.h>
+
 void
 release_cArray (char **carray)
 {
@@ -55,3 +59,55 @@ vector_to_cArray(const std::vector<std::string> &vec)
   ret[vec.size()] = NULL;
   return ret;
 }
+
+bool
+in_de_vs_mode()
+{
+/* We cache the values only once. A change requires restart.
+     This is because checking this is very expensive as gpgconf
+     spawns each process to query the settings. */
+  static bool checked;
+  static bool vs_mode;
+
+  if (checked)
+    {
+      return vs_mode;
+    }
+  GpgME::Error err;
+  const auto components = GpgME::Configuration::Component::load (err);
+  log_debug ("%s:%s: Checking for de-vs mode.",
+             SRCNAME, __func__);
+  if (err)
+    {
+      log_error ("%s:%s: Failed to get gpgconf components: %s",
+                 SRCNAME, __func__, err.asString ());
+      checked = true;
+      vs_mode = false;
+      return vs_mode;
+    }
+  for (const auto &component: components)
+    {
+      if (component.name () && !strcmp (component.name (), "gpg"))
+        {
+          for (const auto &option: component.options ())
+            {
+              if (option.name () && !strcmp (option.name (), "compliance") &&
+                  option.currentValue ().stringValue () &&
+                  !stricmp (option.currentValue ().stringValue (), "de-vs"))
+                {
+                  log_debug ("%s:%s: Detected de-vs mode",
+                             SRCNAME, __func__);
+                  checked = true;
+                  vs_mode = true;
+                  return vs_mode;
+                }
+            }
+          checked = true;
+          vs_mode = false;
+          return vs_mode;
+        }
+    }
+  checked = true;
+  vs_mode = false;
+  return false;
+}
index 0b60170..bbf68e0 100644 (file)
@@ -36,4 +36,7 @@ void rtrim(std::string &s);
 /* Convert a string vector to a null terminated char array */
 char **vector_to_cArray (const std::vector<std::string> &vec);
 
+/* Check if we are in de_vs mode. */
+bool in_de_vs_mode ();
+
 #endif // CPPHELP_H
diff --git a/src/keycache.cpp b/src/keycache.cpp
new file mode 100644 (file)
index 0000000..3907772
--- /dev/null
@@ -0,0 +1,366 @@
+/* @file keycache.cpp
+ * @brief Internal keycache
+ *
+ * Copyright (C) 2018 Intevation GmbH
+ *
+ * This file is part of GpgOL.
+ *
+ * GpgOL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GpgOL 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "keycache.h"
+
+#include "common.h"
+#include "cpphelp.h"
+
+#include <gpg-error.h>
+#include <gpgme++/context.h>
+#include <gpgme++/key.h>
+
+#include <windows.h>
+
+#include <map>
+
+GPGRT_LOCK_DEFINE (keycache_lock);
+static KeyCache* singleton = nullptr;
+
+class KeyCache::Private
+{
+public:
+  Private()
+  {
+
+  }
+
+  void setPgpKey(const char *mbox, const GpgME::Key &key)
+  {
+    const std::string sMbox(mbox);
+
+    gpgrt_lock_lock (&keycache_lock);
+    auto it = m_pgp_key_map.find (sMbox);
+
+    if ( it == m_pgp_key_map.end ())
+      {
+        m_pgp_key_map.insert (std::pair<std::string, GpgME::Key> (sMbox, GpgME::Key()));
+      }
+    else
+      {
+        it->second = key;
+      }
+    gpgrt_lock_unlock (&keycache_lock);
+  }
+
+  void setSmimeKey(const char *mbox, const GpgME::Key &key)
+  {
+    const std::string sMbox(mbox);
+
+    gpgrt_lock_lock (&keycache_lock);
+    auto it = m_smime_key_map.find (sMbox);
+
+    if ( it == m_smime_key_map.end ())
+      {
+        m_smime_key_map.insert (std::pair<std::string, GpgME::Key> (sMbox, GpgME::Key()));
+      }
+    else
+      {
+        it->second = key;
+      }
+    gpgrt_lock_unlock (&keycache_lock);
+  }
+
+  GpgME::Key getKey (const char *addr, GpgME::Protocol proto)
+  {
+    if (!addr)
+      {
+        return GpgME::Key();
+      }
+    auto mbox = GpgME::UserID::addrSpecFromString (addr);
+
+    if (proto == GpgME::OpenPGP)
+      {
+        gpgrt_lock_lock (&keycache_lock);
+        const auto it = m_pgp_key_map.find (mbox);
+
+        if (it == m_pgp_key_map.end ())
+          {
+            gpgrt_lock_unlock (&keycache_lock);
+            return GpgME::Key();
+          }
+        const auto ret = it->second;
+        gpgrt_lock_unlock (&keycache_lock);
+
+        return ret;
+      }
+    gpgrt_lock_lock (&keycache_lock);
+    const auto it = m_smime_key_map.find (mbox);
+
+    if (it == m_smime_key_map.end ())
+      {
+        gpgrt_lock_unlock (&keycache_lock);
+        return GpgME::Key();
+      }
+    const auto ret = it->second;
+    gpgrt_lock_unlock (&keycache_lock);
+
+    return ret;
+  }
+
+  GpgME::Key getSigningKey (const char *addr, GpgME::Protocol proto)
+  {
+    const auto key = getKey (addr, proto);
+    if (key.isNull())
+      {
+        return key;
+      }
+    if (!key.canReallySign())
+      {
+        log_mime_parser ("%s:%s: Discarding key for %s because it can't sign",
+                   SRCNAME, __func__, addr);
+        return GpgME::Key();
+      }
+    if (!key.hasSecret())
+      {
+        log_mime_parser ("%s:%s: Discarding key for %s because it has no secret",
+                   SRCNAME, __func__, addr);
+        return GpgME::Key();
+      }
+    if (in_de_vs_mode () && !key.isDeVs())
+      {
+        log_mime_parser ("%s:%s: signing key for %s is not deVS",
+                   SRCNAME, __func__, addr);
+        return GpgME::Key();
+      }
+    return key;
+  }
+
+  std::vector<GpgME::Key> getEncryptionKeys (const char **recipients,
+                                             GpgME::Protocol proto)
+  {
+    std::vector<GpgME::Key> ret;
+    if (!recipients)
+      {
+        TRACEPOINT;
+        return ret;
+      }
+    for (int i = 0; recipients[i]; i++)
+      {
+        const auto key = getKey (recipients[i], proto);
+        if (key.isNull())
+          {
+            log_mime_parser ("%s:%s: No key for %s. no internal encryption",
+                       SRCNAME, __func__, recipients[i]);
+            return std::vector<GpgME::Key>();
+          }
+
+        if (!key.canEncrypt() || key.isRevoked() ||
+            key.isExpired() || key.isDisabled() || key.isInvalid())
+          {
+            log_mime_parser ("%s:%s: Invalid key for %s. no internal encryption",
+                       SRCNAME, __func__, recipients[i]);
+            return std::vector<GpgME::Key>();
+          }
+
+        if (in_de_vs_mode () && key.isDeVs ())
+          {
+            log_mime_parser ("%s:%s: key for %s is not deVS",
+                       SRCNAME, __func__, recipients[i]);
+            return std::vector<GpgME::Key>();
+          }
+
+        bool validEnough = false;
+        /* Here we do the check if the key is valid for this recipient */
+        const auto addrSpec = GpgME::UserID::addrSpecFromString (recipients[i]);
+        for (const auto &uid: key.userIDs ())
+          {
+            if (addrSpec != uid.addrSpec())
+              {
+                // Ignore unmatching addr specs
+                continue;
+              }
+            if (uid.validity() >= GpgME::UserID::Marginal)
+              {
+                validEnough = true;
+                break;
+              }
+          }
+        if (!validEnough)
+          {
+            log_mime_parser ("%s:%s: UID for %s does not have at least marginal trust",
+                             SRCNAME, __func__, recipients[i]);
+            return std::vector<GpgME::Key>();
+          }
+        // Accepting key
+        ret.push_back (key);
+      }
+    return ret;
+  }
+
+  std::map<std::string, GpgME::Key> m_pgp_key_map;
+  std::map<std::string, GpgME::Key> m_smime_key_map;
+};
+
+KeyCache::KeyCache():
+  d(new Private)
+{
+}
+
+KeyCache *
+KeyCache::instance ()
+{
+  if (!singleton)
+    {
+      singleton = new KeyCache();
+    }
+  return singleton;
+}
+
+GpgME::Key
+KeyCache::getSigningKey (const char *addr, GpgME::Protocol proto) const
+{
+  return d->getSigningKey (addr, proto);
+}
+
+std::vector<GpgME::Key>
+KeyCache::getEncryptionKeys (const char **recipients, GpgME::Protocol proto) const
+{
+  return d->getEncryptionKeys (recipients, proto);
+}
+
+static DWORD WINAPI
+do_locate (LPVOID arg)
+{
+  char *addr = (char*) arg;
+
+  log_mime_parser ("%s:%s searching key for addr: \"%s\"",
+                   SRCNAME, __func__, addr);
+
+  const auto k = GpgME::Key::locate (addr);
+
+  if (!k.isNull ())
+    {
+      log_mime_parser ("%s:%s found key for addr: \"%s\":%s",
+                       SRCNAME, __func__, addr, k.primaryFingerprint());
+      KeyCache::instance ()->setPgpKey (addr, k);
+    }
+
+  if (opt.enable_smime)
+    {
+      auto ctx = GpgME::Context::createForProtocol (GpgME::OpenPGP);
+      if (!ctx)
+        {
+          TRACEPOINT;
+          xfree (addr);
+          return 0;
+        }
+      // We need to validate here to fetch CRL's
+      ctx->setKeyListMode (GpgME::KeyListMode::Local |
+                           GpgME::KeyListMode::Validate);
+      GpgME::Error e = ctx->startKeyListing (addr);
+      if (e)
+        {
+          TRACEPOINT;
+          xfree (addr);
+          return 0;
+        }
+
+      std::vector<GpgME::Key> keys;
+      GpgME::Error err;
+      do {
+          keys.push_back(ctx->nextKey(err));
+      } while (!err);
+      keys.pop_back();
+      delete ctx;
+
+      GpgME::Key candidate;
+      for (const auto &key: keys)
+        {
+          if (key.isRevoked() || key.isExpired() ||
+              key.isDisabled() || key.isInvalid())
+            {
+              log_mime_parser ("%s:%s: Skipping invalid S/MIME key",
+                               SRCNAME, __func__);
+              continue;
+            }
+          if (candidate.isNull() || !candidate.numUserIDs())
+            {
+              if (key.numUserIDs() &&
+                  candidate.userID(0).validity() <= key.userID(0).validity())
+                {
+                  candidate = key;
+                }
+            }
+        }
+      if (!candidate.isNull())
+        {
+          log_mime_parser ("%s:%s found SMIME key for addr: \"%s\":%s",
+                           SRCNAME, __func__, addr, candidate.primaryFingerprint());
+          KeyCache::instance()->setSmimeKey (addr, candidate);
+        }
+    }
+  xfree (addr);
+
+  log_debug ("%s:%s locator thread done",
+             SRCNAME, __func__);
+  return 0;
+}
+
+void KeyCache::startLocate (char **recipients) const
+{
+  log_debug ("%s:%s start locate",
+             SRCNAME, __func__);
+  if (!recipients)
+    {
+      TRACEPOINT;
+      return;
+    }
+  gpgrt_lock_lock (&keycache_lock);
+  log_debug ("%s:%s locked",
+             SRCNAME, __func__);
+
+  for (int i = 0; recipients[i]; i++)
+    {
+      log_debug ("%s:%s looking tat %s",
+                 SRCNAME, __func__, recipients[i]);
+      std::string recp = GpgME::UserID::addrSpecFromString (recipients[i]);
+      if (recp.empty ())
+        {
+          continue;
+        }
+      if (d->m_pgp_key_map.find (recp) == d->m_pgp_key_map.end ())
+        {
+          // It's enough to look at the PGP Key map. We marked
+          // searched keys there.
+          d->m_pgp_key_map.insert (std::pair<std::string, GpgME::Key> (recp, GpgME::Key()));
+          log_debug ("%s:%s Creating a locator thread",
+                     SRCNAME, __func__);
+          HANDLE thread = CreateThread (NULL, 0, do_locate,
+                                        (LPVOID) strdup (recp.c_str ()), 0,
+                                        NULL);
+          CloseHandle (thread);
+        }
+    }
+  gpgrt_lock_unlock (&keycache_lock);
+}
+
+void
+KeyCache::setSmimeKey(const char *mbox, const GpgME::Key &key)
+{
+  d->setSmimeKey(mbox, key);
+}
+
+void
+KeyCache::setPgpKey(const char *mbox, const GpgME::Key &key)
+{
+  d->setPgpKey(mbox, key);
+}
diff --git a/src/keycache.h b/src/keycache.h
new file mode 100644 (file)
index 0000000..7f68fe2
--- /dev/null
@@ -0,0 +1,74 @@
+#ifndef KEYCACHE_H
+#define KEYCACHE_H
+
+/* @file keycache.h
+ * @brief Internal keycache
+ *
+ * Copyright (C) 2018 Intevation GmbH
+ *
+ * This file is part of GpgOL.
+ *
+ * GpgOL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * GpgOL 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <memory>
+#include <vector>
+
+#include <gpgme++/global.h>
+
+namespace GpgME
+{
+  class Key;
+};
+
+class KeyCache
+{
+protected:
+    /** Internal ctor */
+    explicit KeyCache ();
+
+public:
+    /** Get the KeyCache */
+    static KeyCache* instance ();
+
+    /* Try to find a key for signing in the internal
+       cache. If no proper key is found a Null key is
+       returned.*/
+    GpgME::Key getSigningKey (const char *addr, GpgME::Protocol proto) const;
+
+    /* Get the keys for recipents. The keys
+       are taken from the internal cache. If
+       one recipient can't be resolved an empty
+       list is returned. */
+    std::vector<GpgME::Key> getEncryptionKeys (const char **recipients,
+                                               GpgME::Protocol proto) const;
+
+    /* Start a key location in a background thread filling
+       the key cache. cArray is a null terminated array
+       of address strings. */
+    void startLocate (char **cArray) const;
+
+
+    // Internal for thread
+    void setSmimeKey(const char *mbox, const GpgME::Key &key);
+    void setPgpKey(const char *mbox, const GpgME::Key &key);
+
+private:
+    class Private;
+    std::shared_ptr<Private> d;
+};
+
+#endif
index 818dc69..7405328 100644 (file)
@@ -38,6 +38,8 @@
 #include "windowmessages.h"
 #include "mlang-charset.h"
 #include "wks-helper.h"
+#include "keycache.h"
+#include "cpphelp.h"
 
 #include <gpgme++/configuration.h>
 #include <gpgme++/tofuinfo.h>
 
 using namespace GpgME;
 
-static std::map<LPDISPATCH, Mail*> g_mail_map;
-static std::map<std::string, Mail*> g_uid_map;
+static std::map<LPDISPATCH, Mail*> s_mail_map;
+static std::map<std::string, Mail*> s_uid_map;
 static std::set<std::string> uids_searched;
 
 static Mail *s_last_mail;
 
-static bool
-in_de_vs_mode()
-{
-  /* We cache the values only once. A change requires restart.
-     This is because checking this is very expensive as gpgconf
-     spawns each process to query the settings. */
-  static bool checked;
-  static bool vs_mode;
-
-  if (checked)
-    {
-      return vs_mode;
-    }
-  Error err;
-  const auto components = Configuration::Component::load (err);
-  log_debug ("%s:%s: Checking for de-vs mode.",
-             SRCNAME, __func__);
-  if (err)
-    {
-      log_error ("%s:%s: Failed to get gpgconf components: %s",
-                 SRCNAME, __func__, err.asString ());
-      checked = true;
-      vs_mode = false;
-      return vs_mode;
-    }
-  for (const auto &component: components)
-    {
-      if (component.name () && !strcmp (component.name (), "gpg"))
-        {
-          for (const auto &option: component.options ())
-            {
-              if (option.name () && !strcmp (option.name (), "compliance") &&
-                  option.currentValue ().stringValue () &&
-                  !stricmp (option.currentValue ().stringValue (), "de-vs"))
-                {
-                  log_debug ("%s:%s: Detected de-vs mode",
-                             SRCNAME, __func__);
-                  checked = true;
-                  vs_mode = true;
-                  return vs_mode;
-                }
-            }
-          checked = true;
-          vs_mode = false;
-          return vs_mode;
-        }
-    }
-  checked = true;
-  vs_mode = false;
-  return false;
-}
-
 #define COPYBUFSIZE (8 * 1024)
 
 Mail::Mail (LPDISPATCH mailitem) :
@@ -161,7 +111,7 @@ Mail::Mail (LPDISPATCH mailitem) :
       gpgol_release(mailitem);
       return;
     }
-  g_mail_map.insert (std::pair<LPDISPATCH, Mail *> (mailitem, this));
+  s_mail_map.insert (std::pair<LPDISPATCH, Mail *> (mailitem, this));
   s_last_mail = this;
 }
 
@@ -180,18 +130,18 @@ Mail::~Mail()
   detach_MailItemEvents_sink (m_event_sink);
   gpgol_release(m_event_sink);
 
-  it = g_mail_map.find(m_mailitem);
-  if (it != g_mail_map.end())
+  it = s_mail_map.find(m_mailitem);
+  if (it != s_mail_map.end())
     {
-      g_mail_map.erase (it);
+      s_mail_map.erase (it);
     }
 
   if (!m_uuid.empty())
     {
-      auto it2 = g_uid_map.find(m_uuid);
-      if (it2 != g_uid_map.end())
+      auto it2 = s_uid_map.find(m_uuid);
+      if (it2 != s_uid_map.end())
         {
-          g_uid_map.erase (it2);
+          s_uid_map.erase (it2);
         }
     }
 
@@ -222,8 +172,8 @@ Mail::get_mail_for_item (LPDISPATCH mailitem)
       return NULL;
     }
   std::map<LPDISPATCH, Mail *>::iterator it;
-  it = g_mail_map.find(mailitem);
-  if (it == g_mail_map.end())
+  it = s_mail_map.find(mailitem);
+  if (it == s_mail_map.end())
     {
       return NULL;
     }
@@ -237,8 +187,8 @@ Mail::get_mail_for_uuid (const char *uuid)
     {
       return NULL;
     }
-  auto it = g_uid_map.find(std::string(uuid));
-  if (it == g_uid_map.end())
+  auto it = s_uid_map.find(std::string(uuid));
+  if (it == s_uid_map.end())
     {
       return NULL;
     }
@@ -248,8 +198,8 @@ Mail::get_mail_for_uuid (const char *uuid)
 bool
 Mail::is_valid_ptr (const Mail *mail)
 {
-  auto it = g_mail_map.begin();
-  while (it != g_mail_map.end())
+  auto it = s_mail_map.begin();
+  while (it != s_mail_map.end())
     {
       if (it->second == mail)
         return true;
@@ -1415,7 +1365,7 @@ Mail::close_all_mails ()
   int err = 0;
   std::map<LPDISPATCH, Mail *>::iterator it;
   TRACEPOINT;
-  std::map<LPDISPATCH, Mail *> mail_map_copy = g_mail_map;
+  std::map<LPDISPATCH, Mail *> mail_map_copy = s_mail_map;
   for (it = mail_map_copy.begin(); it != mail_map_copy.end(); ++it)
     {
       /* XXX For non racy code the is_valid_ptr check should not
@@ -1449,7 +1399,7 @@ Mail::revert_all_mails ()
 {
   int err = 0;
   std::map<LPDISPATCH, Mail *>::iterator it;
-  for (it = g_mail_map.begin(); it != g_mail_map.end(); ++it)
+  for (it = s_mail_map.begin(); it != s_mail_map.end(); ++it)
     {
       if (it->second->revert ())
         {
@@ -1474,7 +1424,7 @@ Mail::wipe_all_mails ()
 {
   int err = 0;
   std::map<LPDISPATCH, Mail *>::iterator it;
-  for (it = g_mail_map.begin(); it != g_mail_map.end(); ++it)
+  for (it = s_mail_map.begin(); it != s_mail_map.end(); ++it)
     {
       if (it->second->wipe ())
         {
@@ -1953,7 +1903,7 @@ Mail::set_uuid()
                      SRCNAME, __func__, m_mailitem, uuid);
           delete other;
         }
-      g_uid_map.insert (std::pair<std::string, Mail *> (m_uuid, this));
+      s_uid_map.insert (std::pair<std::string, Mail *> (m_uuid, this));
       log_debug ("%s:%s: uuid for %p is now %s",
                  SRCNAME, __func__, this,
                  m_uuid.c_str());
@@ -2450,71 +2400,12 @@ Mail::get_sig_fpr() const
   return m_sig.fingerprint();
 }
 
-
-static DWORD WINAPI
-do_locate (LPVOID arg)
-{
-  char *recipient = (char*) arg;
-  log_debug ("%s:%s searching key for recipient: \"%s\"",
-             SRCNAME, __func__, recipient);
-  Context *ctx = Context::createForProtocol (OpenPGP);
-
-  if (!ctx)
-    {
-      TRACEPOINT;
-      return 0;
-    }
-
-  ctx->setKeyListMode (GpgME::Extern | GpgME::Local);
-  ctx->startKeyListing (recipient, false);
-
-  std::vector<Key> keys;
-  Error err;
-  do {
-      keys.push_back (ctx->nextKey(err));
-    } while (!err);
-  keys.pop_back ();
-  ctx->endKeyListing ();
-  delete ctx;
-
-  if (keys.size ())
-    {
-      log_debug ("%s:%s found key for recipient: \"%s\"",
-                 SRCNAME, __func__, recipient);
-    }
-  xfree (recipient);
-  // do_in_ui_thread (UNKNOWN, NULL);
-  return 0;
-}
-
-GPGRT_LOCK_DEFINE(uids_searched_lock);
-
 /** Try to locate the keys for all recipients */
 void Mail::locate_keys()
 {
-  gpgrt_lock_lock (&uids_searched_lock);
   char ** recipients = get_recipients ();
-
-  if (!recipients)
-    {
-      TRACEPOINT;
-      return;
-    }
-  for (int i = 0; recipients[i]; i++)
-    {
-      std::string recp = recipients[i];
-      if (uids_searched.find (recp) == uids_searched.end ())
-        {
-          uids_searched.insert (recp);
-          HANDLE thread = CreateThread (NULL, 0, do_locate,
-                                        (LPVOID) strdup(recipients[i]), 0,
-                                        NULL);
-          CloseHandle (thread);
-        }
-      xfree (recipients[i]);
-    }
-  xfree (recipients);
-  gpgrt_lock_unlock (&uids_searched_lock);
+  KeyCache::instance()->startLocate (recipients);
+  release_cArray (recipients);
 }
 
 bool
@@ -2756,3 +2647,22 @@ Mail::get_last_mail ()
     }
   return s_last_mail;
 }
+
+// static
+void
+Mail::locate_all_crypto_recipients()
+{
+  if (!opt.autoresolve)
+    {
+      return;
+    }
+
+  std::map<LPDISPATCH, Mail *>::iterator it;
+  for (it = s_mail_map.begin(); it != s_mail_map.end(); ++it)
+    {
+      if (it->second->needs_crypto ())
+        {
+          it->second->locate_keys ();
+        }
+    }
+}
index 53c0e23..f8cdbab 100644 (file)
@@ -133,6 +133,18 @@ public:
     */
   static int close_all_mails ();
 
+  /** @brief locate recipients for all crypto mails
+    *
+    * To avoid lookups of recipients for non crypto mails we only
+    * locate keys when a crypto action is already selected.
+    *
+    * As the user can do this after recipients were added but
+    * we don't know for which mail the crypt button was triggered.
+    * we march over all mails and if they are crypto mails we check
+    * that the recipents were located.
+    */
+  static void locate_all_crypto_recipients ();
+
   /** @brief Reference to the mailitem. Do not Release! */
   LPDISPATCH item () { return m_mailitem; }
 
index e579026..4f7d0a9 100644 (file)
@@ -1357,6 +1357,11 @@ mark_mime_action (LPDISPATCH ctrl, int flags, bool is_explorer)
       we invalidate a lot *sigh* */
   gpgoladdin_invalidate_ui ();
 
+  if (newflags & 1)
+    {
+      Mail::locate_all_crypto_recipients ();
+    }
+
 done:
   gpgol_release (context);
   gpgol_release (mailitem);