[git] GpgOL - branch, master, updated. gpgol-2.2.0-151-gc9e39ec

by Andre Heinecke cvs at cvs.gnupg.org
Tue Aug 28 12:16:02 CEST 2018


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "GnuPG extension for MS Outlook".

The branch, master has been updated
       via  c9e39ec1bcc258a0208d2b9a2896c4ce415ac8df (commit)
       via  ed840c43457285039006dc6b5419ed8c8970c445 (commit)
       via  d92cb5f7548c542046e9331ecaf53d05a43b57e3 (commit)
       via  8d2a2acd2e1b3714d1f48fefac1510676a71c534 (commit)
       via  14d33bbef22af1fc4c79329241d0b8b2dabae29f (commit)
       via  9e991ffe846625c7c61f6cd5b8dca32283ea44bc (commit)
      from  b09e89143d13f1d170ba2298ca412b47f2f03382 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit c9e39ec1bcc258a0208d2b9a2896c4ce415ac8df
Author: Andre Heinecke <aheinecke at intevation.de>
Date:   Tue Aug 28 12:15:24 2018 +0200

    Add another tracepoint in parsing_done
    
    * src/mail.cpp (Mail::parsing_done): Add tracepoint.

diff --git a/src/mail.cpp b/src/mail.cpp
index 7209cf8..eeddb92 100644
--- a/src/mail.cpp
+++ b/src/mail.cpp
@@ -1370,6 +1370,7 @@ Mail::parsing_done()
       m_crypto_flags |= 2;
     }
 
+  TRACEPOINT;
   updateSigstate ();
   m_needs_wipe = !m_is_send_again;
 

commit ed840c43457285039006dc6b5419ed8c8970c445
Author: Andre Heinecke <aheinecke at intevation.de>
Date:   Tue Aug 28 12:13:19 2018 +0200

    Use keycache instead of Signature::Key
    
    * src/mail.cpp (Mail::updateSigstate),
    (Mail::getCryptoDetails_o): Use keycache.
    * src/parsecontroller.cpp (Parsecontroller::parse): Use
    keycache.
    
    --
    This is a huge performance improvement as the old code
    was pretty bad. The old code did three keylistings
    after a verify to search the key, then check if the
    key is secret and then update the key.

diff --git a/src/mail.cpp b/src/mail.cpp
index fb15b1b..7209cf8 100644
--- a/src/mail.cpp
+++ b/src/mail.cpp
@@ -2009,7 +2009,9 @@ Mail::updateSigstate ()
   for (const auto sig: m_verify_result.signatures())
     {
       m_is_signed = true;
-      m_uid = get_uid_for_sender (sig.key(), sender.c_str());
+      const auto key = KeyCache::instance ()->getByFpr (sig.fingerprint(),
+                                                        true);
+      m_uid = get_uid_for_sender (key, sender.c_str());
 
       if ((sig.summary() & Signature::Summary::Valid) &&
           m_uid.origin() == GpgME::Key::OriginWKD &&
@@ -2433,8 +2435,10 @@ Mail::getCryptoDetails_o ()
     }
 
   bool keyFound = true;
-  bool isOpenPGP = m_sig.key().isNull() ? !isSMIME_m () :
-                   m_sig.key().protocol() == Protocol::OpenPGP;
+  const auto sigKey = KeyCache::instance ()->getByFpr (m_sig.fingerprint (),
+                                                       true);
+  bool isOpenPGP = sigKey.isNull() ? !isSMIME_m () :
+                   sigKey.protocol() == Protocol::OpenPGP;
   char *buf;
   bool hasConflict = false;
   int level = get_signature_level ();
@@ -2448,7 +2452,7 @@ Mail::getCryptoDetails_o ()
       /* level 4 check for direct trust */
       int four_check = level_4_check (m_uid);
 
-      if (four_check == 2 && m_sig.key().hasSecret ())
+      if (four_check == 2 && sigKey.hasSecret ())
         {
           message = _("You signed this message.");
         }
@@ -2477,7 +2481,7 @@ Mail::getCryptoDetails_o ()
     {
       /* Level three is the only level for trusted S/MIME keys. */
       gpgrt_asprintf (&buf, _("The senders identity is certified by the trusted issuer:\n'%s'\n"),
-                      m_sig.key().issuerName());
+                      sigKey.issuerName());
       memdbg_alloc (buf);
       message = buf;
       xfree (buf);
diff --git a/src/parsecontroller.cpp b/src/parsecontroller.cpp
index 04a583e..150668c 100644
--- a/src/parsecontroller.cpp
+++ b/src/parsecontroller.cpp
@@ -25,6 +25,8 @@
 #include "attachment.h"
 #include "mimedataprovider.h"
 
+#include "keycache.h"
+
 #include <gpgme++/context.h>
 #include <gpgme++/decryptionresult.h>
 #include <gpgme++/key.h>
@@ -254,6 +256,19 @@ is_valid_chksum(const GpgME::Signature &sig)
   return sum & valid_mask;
 }
 
+
+/* Note on stability:
+
+   Experiments have shown that we can have a crash if parse
+   returns at time that is not good for the state of Outlook.
+
+   This happend in my test instance after a delay of > 1s < 3s
+   with a < 1% chance :-/
+
+   So if you have really really bad luck this might still crash
+   although it usually should be either much quicker or much slower
+   (slower e.g. when pinentry is requrired).
+*/
 void
 ParseController::parse()
 {
@@ -459,17 +474,16 @@ ParseController::parse()
     {
       has_valid_encrypted_checksum = is_valid_chksum (sig);
 
-      /* FIXME: This is very expensive. We need some caching here
-         or reduce the information */
-      TRACEPOINT;
-      sig.key(true, true);
-      TRACEPOINT;
+      KeyCache::instance ()->update (sig.fingerprint (), protocol);
+
       if (!ultimate_keys_queried &&
           (sig.validity() == Signature::Validity::Full ||
           sig.validity() == Signature::Validity::Ultimate))
         {
           /* Ensure that we have the keys with ultimate
              trust cached for the ui. */
+
+          // TODO this is something for the keycache
           get_ultimate_keys ();
           ultimate_keys_queried = true;
         }
@@ -489,7 +503,16 @@ ParseController::parse()
        ss << m_decrypt_result << '\n' << m_verify_result;
       for (const auto sig: m_verify_result.signatures())
         {
-          ss << '\n' << sig.key();
+          const auto key = sig.key();
+          if (key.isNull())
+            {
+              ss << '\n' << "Cached key:\n" << KeyCache::instance()->getByFpr(
+                  sig.fingerprint(), false);
+            }
+          else
+            {
+              ss << '\n' << key;
+            }
         }
        log_debug ("Decrypt / Verify result: %s", ss.str().c_str());
     }

commit d92cb5f7548c542046e9331ecaf53d05a43b57e3
Author: Andre Heinecke <aheinecke at intevation.de>
Date:   Tue Aug 28 12:11:23 2018 +0200

    Extend keycache for fingerprint lookup
    
    * src/keycache.cpp, src/keycache.h (KeyCache::update),
    (KeyCache::getByFpr): New.
    
    --
    This extends the keycache with a generic fingerprint
    to key mapping. Update inserts or updates a key
    in the keycache, getByFpr is the main accessor.

diff --git a/src/keycache.cpp b/src/keycache.cpp
index a0eadba..95c27fe 100644
--- a/src/keycache.cpp
+++ b/src/keycache.cpp
@@ -32,9 +32,12 @@
 #include <windows.h>
 
 #include <map>
+#include <unordered_map>
 #include <sstream>
 
 GPGRT_LOCK_DEFINE (keycache_lock);
+GPGRT_LOCK_DEFINE (fpr_map_lock);
+GPGRT_LOCK_DEFINE (update_lock);
 static KeyCache* singleton = nullptr;
 
 /** At some point we need to set a limit. There
@@ -84,6 +87,53 @@ namespace
     };
 } // namespace
 
+typedef std::pair<std::string, GpgME::Protocol> update_arg_t;
+
+static DWORD WINAPI
+do_update (LPVOID arg)
+{
+  auto args = std::unique_ptr<update_arg_t> ((update_arg_t*) arg);
+
+  log_mime_parser ("%s:%s updating: \"%s\" with protocol %s",
+                   SRCNAME, __func__, args->first.c_str (),
+                   to_cstr (args->second));
+
+  auto ctx = std::unique_ptr<GpgME::Context> (GpgME::Context::createForProtocol
+                                              (args->second));
+
+  if (!ctx)
+    {
+      TRACEPOINT;
+      KeyCache::instance ()->onUpdateJobDone (args->first.c_str(),
+                                              GpgME::Key ());
+      return 0;
+    }
+
+  ctx->setKeyListMode (GpgME::KeyListMode::Local |
+                       GpgME::KeyListMode::Signatures |
+                       GpgME::KeyListMode::Validate |
+                       GpgME::KeyListMode::WithTofu);
+  GpgME::Error err;
+  const auto newKey = ctx->key (args->first.c_str (), err, false);
+  TRACEPOINT;
+
+  if (newKey.isNull())
+    {
+      log_debug ("%s:%s Failed to find key for %s",
+                 SRCNAME, __func__, args->first.c_str ());
+    }
+  if (err)
+    {
+      log_debug ("%s:%s Failed to find key for %s err: ",
+                 SRCNAME, __func__, err.asString ());
+    }
+  KeyCache::instance ()->onUpdateJobDone (args->first.c_str(),
+                                          newKey);
+  log_debug ("%s:%s Update job done",
+             SRCNAME, __func__);
+  return 0;
+}
+
 class KeyCache::Private
 {
 public:
@@ -105,6 +155,7 @@ public:
       {
         it->second = key;
       }
+    insertOrUpdateInFprMap (key);
     gpgrt_lock_unlock (&keycache_lock);
   }
 
@@ -121,6 +172,7 @@ public:
       {
         it->second = key;
       }
+    insertOrUpdateInFprMap (key);
     gpgrt_lock_unlock (&keycache_lock);
   }
 
@@ -137,6 +189,7 @@ public:
       {
         it->second = key;
       }
+    insertOrUpdateInFprMap (key);
     gpgrt_lock_unlock (&keycache_lock);
   }
 
@@ -153,6 +206,7 @@ public:
       {
         it->second = key;
       }
+    insertOrUpdateInFprMap (key);
     gpgrt_lock_unlock (&keycache_lock);
   }
 
@@ -260,7 +314,8 @@ public:
     return key;
   }
 
-  std::vector<GpgME::Key> getEncryptionKeys (const std::vector<std::string> &recipients,
+  std::vector<GpgME::Key> getEncryptionKeys (const std::vector<std::string>
+                                             &recipients,
                                              GpgME::Protocol proto)
   {
     std::vector<GpgME::Key> ret;
@@ -323,10 +378,219 @@ public:
     return ret;
   }
 
+  void insertOrUpdateInFprMap (const GpgME::Key &key)
+    {
+      if (key.isNull() || !key.primaryFingerprint())
+        {
+          TRACEPOINT;
+          return;
+        }
+      gpgrt_lock_lock (&fpr_map_lock);
+
+      /* First ensure that we have the subkeys mapped to the primary
+         fpr */
+      const char *primaryFpr = key.primaryFingerprint ();
+
+      for (const auto &sub: key.subkeys())
+        {
+          const char *subFpr = sub.fingerprint();
+          auto it = m_sub_fpr_map.find (subFpr);
+          if (it == m_sub_fpr_map.end ())
+            {
+              m_sub_fpr_map.insert (std::make_pair(
+                                     std::string (subFpr),
+                                     std::string (primaryFpr)));
+            }
+        }
+
+      auto it = m_fpr_map.find (primaryFpr);
+
+      log_mime_parser ("%s:%s \"%s\" updated.",
+                       SRCNAME, __func__, primaryFpr);
+      if (it == m_fpr_map.end ())
+        {
+          m_fpr_map.insert (std::make_pair (primaryFpr, key));
+
+          gpgrt_lock_unlock (&fpr_map_lock);
+          return;
+        }
+
+      if (it->second.hasSecret () && !key.hasSecret())
+        {
+          log_debug ("%s:%s Lost secret info on update. Merging.",
+                     SRCNAME, __func__);
+          auto merged = key;
+          merged.mergeWith (it->second);
+          it->second = merged;
+        }
+      else
+        {
+          it->second = key;
+        }
+      gpgrt_lock_unlock (&fpr_map_lock);
+      return;
+    }
+
+  GpgME::Key getFromMap (const char *fpr) const
+  {
+    if (!fpr)
+      {
+        TRACEPOINT;
+        return GpgME::Key();
+      }
+
+    gpgrt_lock_lock (&fpr_map_lock);
+    std::string primaryFpr;
+    const auto it = m_sub_fpr_map.find (fpr);
+    if (it != m_sub_fpr_map.end ())
+      {
+        log_debug ("%s:%s using \"%s\" for \"%s\"",
+                   SRCNAME, __func__, it->second.c_str(), fpr);
+        primaryFpr = it->second;
+      }
+    else
+      {
+        primaryFpr = fpr;
+      }
+
+    const auto keyIt = m_fpr_map.find (primaryFpr);
+    if (keyIt != m_fpr_map.end ())
+      {
+        gpgrt_lock_unlock (&fpr_map_lock);
+        return keyIt->second;
+      }
+    gpgrt_lock_unlock (&fpr_map_lock);
+    return GpgME::Key();
+  }
+
+  GpgME::Key getByFpr (const char *fpr, bool block) const
+    {
+      if (!fpr)
+        {
+          TRACEPOINT;
+          return GpgME::Key ();
+        }
+
+      TRACEPOINT;
+      const auto ret = getFromMap (fpr);
+      if (ret.isNull())
+        {
+          // If the key was not found we need to check if there is
+          // an update running.
+          if (block)
+            {
+              const std::string sFpr (fpr);
+              int i = 0;
+
+              gpgrt_lock_lock (&update_lock);
+              while (std::find (m_update_jobs.begin(), m_update_jobs.end(),
+                                sFpr)
+                     != m_update_jobs.end ())
+                {
+                  i++;
+                  if (i % 100 == 0)
+                    {
+                      log_debug ("%s:%s Waiting on update for \"%s\"",
+                                 SRCNAME, __func__, fpr);
+                    }
+                  gpgrt_lock_unlock (&update_lock);
+                  Sleep (10);
+                  gpgrt_lock_lock (&update_lock);
+                  if (i == 3000)
+                    {
+                      /* Just to be on the save side */
+                      log_error ("%s:%s Waiting on update for \"%s\" "
+                                 "failed! Bug!",
+                                 SRCNAME, __func__, fpr);
+                      break;
+                    }
+                }
+              gpgrt_lock_unlock (&update_lock);
+
+              TRACEPOINT;
+              const auto ret2 = getFromMap (fpr);
+              if (ret2.isNull ())
+                {
+                  log_debug ("%s:%s Cache miss after blocking check %s.",
+                             SRCNAME, __func__, fpr);
+                }
+              else
+                {
+                  log_debug ("%s:%s Cache hit after wait for %s.",
+                             SRCNAME, __func__, fpr);
+                  return ret2;
+                }
+            }
+          log_debug ("%s:%s Cache miss for %s.",
+                     SRCNAME, __func__, fpr);
+          return GpgME::Key();
+        }
+
+      log_debug ("%s:%s Cache hit for %s.",
+                 SRCNAME, __func__, fpr);
+      return ret;
+    }
+
+  void update (const char *fpr, GpgME::Protocol proto)
+     {
+       if (!fpr)
+         {
+           return;
+         }
+       const std::string sFpr (fpr);
+       gpgrt_lock_lock (&update_lock);
+       if (std::find (m_update_jobs.begin(), m_update_jobs.end(), sFpr)
+                      != m_update_jobs.end ())
+         {
+           log_debug ("%s:%s Update for \"%s\" already in progress.",
+                      SRCNAME, __func__, fpr);
+           gpgrt_lock_unlock (&update_lock);
+         }
+
+       m_update_jobs.push_back (sFpr);
+       gpgrt_lock_unlock (&update_lock);
+       update_arg_t * args = new update_arg_t;
+       args->first = sFpr;
+       args->second = proto;
+       CloseHandle (CreateThread (NULL, 0, do_update,
+                                  (LPVOID) args, 0,
+                                  NULL));
+     }
+
+  void onUpdateJobDone (const char *fpr, const GpgME::Key &key)
+    {
+      if (!fpr)
+        {
+          return;
+        }
+      TRACEPOINT;
+      insertOrUpdateInFprMap (key);
+      const std::string sFpr (fpr);
+      gpgrt_lock_lock (&update_lock);
+      const auto it = std::find (m_update_jobs.begin(),
+                                 m_update_jobs.end(),
+                                 sFpr);
+
+      if (it == m_update_jobs.end())
+        {
+          log_error ("%s:%s Update for \"%s\" already finished.",
+                     SRCNAME, __func__, fpr);
+          gpgrt_lock_unlock (&update_lock);
+          return;
+        }
+      m_update_jobs.erase (it);
+      gpgrt_lock_unlock (&update_lock);
+      TRACEPOINT;
+      return;
+    }
+
   std::map<std::string, GpgME::Key> m_pgp_key_map;
   std::map<std::string, GpgME::Key> m_smime_key_map;
   std::map<std::string, GpgME::Key> m_pgp_skey_map;
   std::map<std::string, GpgME::Key> m_smime_skey_map;
+  std::unordered_map<std::string, GpgME::Key> m_fpr_map;
+  std::unordered_map<std::string, std::string> m_sub_fpr_map;
+  std::vector<std::string> m_update_jobs;
 };
 
 KeyCache::KeyCache():
@@ -392,7 +656,8 @@ do_locate (LPVOID arg)
         }
       // We need to validate here to fetch CRL's
       ctx->setKeyListMode (GpgME::KeyListMode::Local |
-                           GpgME::KeyListMode::Validate);
+                           GpgME::KeyListMode::Validate |
+                           GpgME::KeyListMode::Signatures);
       GpgME::Error e = ctx->startKeyListing (addr.c_str());
       if (e)
         {
@@ -663,3 +928,21 @@ KeyCache::isMailResolvable(Mail *mail)
 
   return !encKeys.empty() && !sigKey.isNull();
 }
+
+void
+KeyCache::update (const char *fpr, GpgME::Protocol proto)
+{
+  d->update (fpr, proto);
+}
+
+GpgME::Key
+KeyCache::getByFpr (const char *fpr, bool block) const
+{
+  return d->getByFpr (fpr, block);
+}
+
+void
+KeyCache::onUpdateJobDone (const char *fpr, const GpgME::Key &key)
+{
+  return d->onUpdateJobDone (fpr, key);
+}
diff --git a/src/keycache.h b/src/keycache.h
index 9cdf499..ea90863 100644
--- a/src/keycache.h
+++ b/src/keycache.h
@@ -80,11 +80,29 @@ public:
      **/
     bool isMailResolvable (Mail *mail);
 
+    /* Search / Update a key in the cache. This is meant to be
+       called e.g. after a verify to update the key.
+
+       A known issue is that a get right after it might
+       still return an outdated key but the get after that
+       would return the updated one. This is acceptable as
+       it only poses a minor problem with TOFU while we
+       can show the correct state in the tooltip. */
+    void update (const char *fpr, GpgME::Protocol proto);
+
+    /* Get a cached key. If block is true it will block
+       if the key is currently searched for.
+
+       This function will not search a key. Call update
+       to insert keys into the cache */
+    GpgME::Key getByFpr (const char *fpr, bool block = true) const;
+
     // Internal for thread
     void setSmimeKey(const std::string &mbox, const GpgME::Key &key);
     void setPgpKey(const std::string &mbox, const GpgME::Key &key);
     void setSmimeKeySecret(const std::string &mbox, const GpgME::Key &key);
     void setPgpKeySecret(const std::string &mbox, const GpgME::Key &key);
+    void onUpdateJobDone (const char *fpr, const GpgME::Key &key);
 
 private:
 

commit 8d2a2acd2e1b3714d1f48fefac1510676a71c534
Author: Andre Heinecke <aheinecke at intevation.de>
Date:   Tue Aug 28 12:10:18 2018 +0200

    Fix confusing debug output
    
    * src/windowmessages.cpp (delayed_invalidate_ui): Print
    waiting before sleep, not after.

diff --git a/src/windowmessages.cpp b/src/windowmessages.cpp
index e3dfd45..e9ec1e5 100644
--- a/src/windowmessages.cpp
+++ b/src/windowmessages.cpp
@@ -460,15 +460,14 @@ delayed_invalidate_ui (LPVOID minsleep)
   int i = 0;
   while (invalidation_blocked)
     {
-      Sleep (100);
       i++;
-
       if (i % 10 == 0)
         {
           log_debug ("%s:%s: Waiting for invalidation.",
                      SRCNAME, __func__);
         }
 
+      Sleep (100);
       /* Do we need an abort statement here? */
     }
   do_in_ui_thread (INVALIDATE_UI, nullptr);

commit 14d33bbef22af1fc4c79329241d0b8b2dabae29f
Author: Andre Heinecke <aheinecke at intevation.de>
Date:   Thu Aug 23 09:21:41 2018 +0200

    Change mime data log category
    
    * src/mimedataprovider.cpp: Change data logging from
    log_mime_parser to log_mime_data.

diff --git a/src/mimedataprovider.cpp b/src/mimedataprovider.cpp
index 52c7834..e869cc5 100644
--- a/src/mimedataprovider.cpp
+++ b/src/mimedataprovider.cpp
@@ -561,18 +561,18 @@ MimeDataProvider::read(void *buffer, size_t size)
   log_mime_parser ("%s:%s: Reading: " SIZE_T_FORMAT "Bytes",
                  SRCNAME, __func__, size);
   ssize_t bRead = m_crypto_data.read (buffer, size);
-  if (opt.enable_debug & DBG_MIME_PARSER && bRead)
+  if (opt.enable_debug & DBG_MIME_DATA && bRead)
     {
       std::string buf ((char *)buffer, bRead);
 
       if (!is_binary (buf))
         {
-          log_mime_parser ("%s:%s: Data: \n------\n%s\n------",
+          log_mime_data ("%s:%s: Data: \n------\n%s\n------",
                            SRCNAME, __func__, buf.c_str());
         }
       else
         {
-          log_mime_parser ("%s:%s: Hex Data: \n------\n%s\n------",
+          log_mime_data ("%s:%s: Hex Data: \n------\n%s\n------",
                            SRCNAME, __func__,
                            string_to_hex (buf).c_str ());
         }
@@ -614,8 +614,8 @@ MimeDataProvider::collect_input_lines(const char *input, size_t insize)
               pos--;
             }
 
-          log_mime_parser("%s:%s: Parsing line=`%.*s'\n",
-                          SRCNAME, __func__, (int)pos, linebuf);
+          log_mime_data ("%s:%s: Parsing line=`%.*s'\n",
+                         SRCNAME, __func__, (int)pos, linebuf);
           /* Check the next state */
           if (rfc822parse_insert (m_mime_ctx->msg,
                                   (unsigned char*) linebuf,
@@ -653,7 +653,7 @@ MimeDataProvider::collect_input_lines(const char *input, size_t insize)
                 {
                   m_crypto_data.write ("\r\n", 2);
                 }
-              log_mime_parser ("Writing raw crypto data: %.*s",
+              log_mime_data ("Writing raw crypto data: %.*s",
                                (int)pos, linebuf);
               m_crypto_data.write (linebuf, pos);
               m_mime_ctx->collect_crypto_data = 2;

commit 9e991ffe846625c7c61f6cd5b8dca32283ea44bc
Author: Andre Heinecke <aheinecke at intevation.de>
Date:   Wed Aug 22 13:33:11 2018 +0200

    Ignore disable async crypto option
    
    * src/mail.cpp (Mail::isAsyncCryptDisabled): We assume
    that Async crypto is now stable. So ignore this option.
    
    --
    We assume that T3838 is fixed.
    
    GnuPG-Bug-Id: T3838

diff --git a/src/mail.cpp b/src/mail.cpp
index 26fa366..fb15b1b 100644
--- a/src/mail.cpp
+++ b/src/mail.cpp
@@ -3033,6 +3033,8 @@ Mail::setWindowEnabled_o (bool value)
 bool
 Mail::check_inline_response ()
 {
+#if 0 // Should be fixed
+
 /* Async sending might lead to crashes when the send invocation is done.
  * For now we treat every mail as an inline response to disable async
  * encryption. :-( For more details see: T3838 */
@@ -3042,6 +3044,7 @@ Mail::check_inline_response ()
       m_async_crypt_disabled = true;
       return m_async_crypt_disabled;
     }
+#endif
 
   m_async_crypt_disabled = false;
   LPDISPATCH app = GpgolAddin::get_instance ()->get_application ();

-----------------------------------------------------------------------

Summary of changes:
 src/keycache.cpp         | 287 ++++++++++++++++++++++++++++++++++++++++++++++-
 src/keycache.h           |  18 +++
 src/mail.cpp             |  18 ++-
 src/mimedataprovider.cpp |  12 +-
 src/parsecontroller.cpp  |  35 +++++-
 src/windowmessages.cpp   |   3 +-
 6 files changed, 352 insertions(+), 21 deletions(-)


hooks/post-receive
-- 
GnuPG extension for MS Outlook
http://git.gnupg.org




More information about the Gnupg-commits mailing list