[git] GpgOL - branch, async-enc, created. gpgol-2.0.6-2-gbec715a

by Andre Heinecke cvs at cvs.gnupg.org
Tue Jan 30 11:06:15 CET 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, async-enc has been created
        at  bec715ab3f57709db1fbf4759b0b0593055b5b04 (commit)

- Log -----------------------------------------------------------------
commit bec715ab3f57709db1fbf4759b0b0593055b5b04
Author: Andre Heinecke <aheinecke at intevation.de>
Date:   Tue Jan 30 10:59:28 2018 +0100

    First steps for async encryption handling
    
    * src/Makefile.am (gpgol_SOURCES): Add new files.
    * src/common_indep.h (log_mime_data): Shorthand for DBG_MIME_DATA.
    * src/cryptcontroller.cpp, src/cryptcontroller.h: New. Similar
    to parsecontroller but the other way around.
    * src/mail.cpp (do_crypt): New. Thread helper.
    (Mail::encrypt_sign): Renamed to Mail::encrypt_sign_start. And
    changed to use cryptcontroller.
    (Mail::update_crypt_mapi): New. Dummy.
    (Mail::update_crypt_oom): New. Dummy.
    (Mail::CryptState): Helper state for encryption.
    * src/mailitem-events.cpp: Add proof of concept for new enc.
    * src/mimemaker.h (add_body_and_attachments): Export.
    * src/windowmessage.h, src/windowmessages.cpp (CRYPTO_DONE): New.
    
    --
    This shows the general possibility of async crypto by first collecting
    the data, then doing the crypto and then still to have the possibility
    to update the encrypted data both in MAPI and in OOM.

diff --git a/src/Makefile.am b/src/Makefile.am
index 961b2a5..90091cb 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -84,7 +84,8 @@ gpgol_SOURCES = \
 	addin-options.cpp addin-options.h \
 	parsecontroller.cpp parsecontroller.h \
 	mimedataprovider.cpp mimedataprovider.h \
-	explorer-events.cpp explorers-events.cpp
+	explorer-events.cpp explorers-events.cpp \
+	cryptcontroller.cpp cryptcontroller.h
 
 
 #treeview_SOURCES = treeview.c
diff --git a/src/common_indep.h b/src/common_indep.h
index 124112a..10b14d6 100644
--- a/src/common_indep.h
+++ b/src/common_indep.h
@@ -297,6 +297,7 @@ void log_hexdump (const void *buf, size_t buflen, const char *fmt,
 #define log_oom if (opt.enable_debug & DBG_OOM) log_debug
 #define log_oom_extra if (opt.enable_debug & DBG_OOM_EXTRA) log_debug
 #define log_mime_parser if (opt.enable_debug & DBG_MIME_PARSER) log_debug
+#define log_mime_data if (opt.enable_debug & DBG_MIME_DATA) log_debug
 
 #define gpgol_release(X) \
 { \
diff --git a/src/cryptcontroller.cpp b/src/cryptcontroller.cpp
new file mode 100644
index 0000000..f12957f
--- /dev/null
+++ b/src/cryptcontroller.cpp
@@ -0,0 +1,159 @@
+/* @file cryptcontroller.cpp
+ * @brief Helper to do crypto on a mail.
+ *
+ * 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 "common.h"
+#include "cryptcontroller.h"
+#include "mail.h"
+#include "mapihelp.h"
+#include "mimemaker.h"
+
+#ifdef HAVE_W32_SYSTEM
+#include "common.h"
+/* We use UTF-8 internally. */
+#undef _
+# define _(a) utf8_gettext (a)
+#else
+# define _(a) a
+#endif
+static int
+sink_data_write (sink_t sink, const void *data, size_t datalen)
+{
+  GpgME::Data *d = static_cast<GpgME::Data *>(sink->cb_data);
+  d->write (data, datalen);
+  return 0;
+}
+
+
+/** We have some C Style cruft in here as this was historically how
+  GpgOL worked directly in the MAPI data objects. To reduce the regression
+  risk the new object oriented way for crypto reused as much as possible
+  from this.
+*/
+CryptController::CryptController (Mail *mail, bool encrypt, bool sign,
+                                  bool doInline, GpgME::Protocol proto):
+    m_mail (mail),
+    m_encrypt (encrypt),
+    m_sign (sign),
+    m_inline (doInline),
+    m_proto (proto)
+{
+  log_debug ("%s:%s: CryptController ctor for %p encrypt %i sign %i inline %i.",
+             SRCNAME, __func__, mail, encrypt, sign, doInline);
+}
+
+CryptController::~CryptController()
+{
+  log_debug ("%s:%s:%p",
+             SRCNAME, __func__, m_mail);
+}
+
+int
+CryptController::collect_data ()
+{
+  /* Get the attachment info and the body.  We need to do this before
+     creating the engine's filter because sending the cancel to
+     the engine with nothing for the engine to process.  Will result
+     in an error. This is actually a bug in our engine code but
+     we better avoid triggering this bug because the engine
+     sometimes hangs.  Fixme: Needs a proper fix. */
+
+
+  /* Take the Body from the mail if possible. This is a fix for
+     GnuPG-Bug-ID: T3614 because the body is not always properly
+     updated in MAPI when sending. */
+  char *body = m_mail->take_cached_plain_body ();
+  if (body && !*body)
+    {
+      xfree (body);
+      body = NULL;
+    }
+
+  LPMESSAGE message = get_oom_base_message (m_mail->item ());
+  if (!message)
+    {
+      log_error ("%s:%s: Failed to get base message.",
+                 SRCNAME, __func__);
+    }
+
+  auto att_table = mapi_create_attach_table (message, 0);
+  int n_att_usable = count_usable_attachments (att_table);
+  if (!n_att_usable && !body)
+    {
+      log_debug ("%s:%s: encrypt empty message", SRCNAME, __func__);
+    }
+
+  if (n_att_usable && m_inline)
+    {
+      log_debug ("%s:%s: PGP Inline not supported for attachments."
+                 " Using PGP MIME",
+                 SRCNAME, __func__);
+      m_inline = false;
+    }
+  else if (m_inline)
+    {
+      // Inline. Use Body as input and be done.
+      m_input.write (body, strlen (body));
+      log_debug ("%s:%s: PGP Inline. Using cached body as input.",
+                 SRCNAME, __func__);
+      gpgol_release (message);
+      return 0;
+    }
+
+  /* Set up the sink object to collect the mime structure */
+  struct sink_s sinkmem;
+  sink_t sink = &sinkmem;
+  memset (sink, 0, sizeof *sink);
+  sink->cb_data = &m_input;
+  sink->writefnc = sink_data_write;
+
+  /* Collect the mime strucutre */
+  if (add_body_and_attachments (sink, message, att_table, m_mail,
+                                body, n_att_usable))
+    {
+      log_error ("%s:%s: Collecting body and attachments failed.",
+                 SRCNAME, __func__);
+      gpgol_release (message);
+      return -1;
+    }
+
+  /* Message is no longer needed */
+  gpgol_release (message);
+
+  /* Set the input buffer to start. */
+  m_input.seek (0, SEEK_SET);
+  return 0;
+}
+
+int
+CryptController::do_crypto ()
+{
+  log_debug ("%s:%s:",
+             SRCNAME, __func__);
+  return 0;
+}
+
+int
+CryptController::update_mail_mapi ()
+{
+  log_debug ("%s:%s:", SRCNAME, __func__);
+  return 0;
+}
diff --git a/src/cryptcontroller.h b/src/cryptcontroller.h
new file mode 100644
index 0000000..f28c567
--- /dev/null
+++ b/src/cryptcontroller.h
@@ -0,0 +1,65 @@
+/* @file cryptcontroller.h
+ * @brief Helper to handle sign and encrypt
+ *
+ * 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/>.
+ */
+#ifndef CRYPTCONTROLLER_H
+#define CRYPTCONTROLLER_H
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gpgme++/data.h>
+
+class Mail;
+
+class CryptController
+{
+public:
+  /** @brief Construct a Crypthelper for a Mail object. */
+  CryptController (Mail *mail, bool encrypt, bool sign,
+                   bool inlineCrypt, GpgME::Protocol proto);
+  ~CryptController ();
+
+  /** @brief Collect the data from the mail into the internal
+      data structures.
+
+      @returns 0 on success.
+      */
+  int collect_data ();
+
+  /** @brief Does the actual crypto work according to op.
+      Can be called in a different thread then the UI Thread.
+
+      @returns 0 on success.
+  */
+  int do_crypto ();
+
+  /** @brief Update the MAPI structure of the mail with
+    the result. */
+  int update_mail_mapi ();
+
+private:
+  Mail *m_mail;
+  GpgME::Data m_input, m_output;
+  bool m_encrypt, m_sign, m_inline;
+  GpgME::Protocol m_proto;
+};
+
+#endif
diff --git a/src/mail.cpp b/src/mail.cpp
index 39342b1..a48d1e2 100644
--- a/src/mail.cpp
+++ b/src/mail.cpp
@@ -33,6 +33,7 @@
 #include "gpgoladdin.h"
 #include "mymapitags.h"
 #include "parsecontroller.h"
+#include "cryptcontroller.h"
 #include "gpgolstr.h"
 #include "windowmessages.h"
 #include "mlang-charset.h"
@@ -135,7 +136,8 @@ Mail::Mail (LPDISPATCH mailitem) :
     m_cached_recipients(nullptr),
     m_type(MSGTYPE_UNKNOWN),
     m_do_inline(false),
-    m_is_gsuite(false)
+    m_is_gsuite(false),
+    m_crypt_state(NoCryptMail)
 {
   if (get_mail_for_item (mailitem))
     {
@@ -726,6 +728,68 @@ do_parsing (LPVOID arg)
   return 0;
 }
 
+static DWORD WINAPI
+do_crypt (LPVOID arg)
+{
+  gpgrt_lock_lock (&dtor_lock);
+  /* We lock with mail dtors so we can be sure the mail->parser
+     call is valid. */
+  Mail *mail = (Mail *)arg;
+  if (!Mail::is_valid_ptr (mail))
+    {
+      log_debug ("%s:%s: canceling crypt for: %p already deleted",
+                 SRCNAME, __func__, arg);
+      gpgrt_lock_unlock (&dtor_lock);
+      return 0;
+    }
+  if (mail->crypt_state() != Mail::NeedsActualCrypt)
+    {
+      log_debug ("%s:%s: invalid state %i",
+                 SRCNAME, __func__, mail->crypt_state ());
+      gpgrt_lock_unlock (&dtor_lock);
+      return -1;
+    }
+
+  /* This takes a shared ptr of parser. So the parser is
+     still valid when the mail is deleted. */
+  auto crypter = mail->crypter();
+  gpgrt_lock_unlock (&dtor_lock);
+
+  if (!crypter)
+    {
+      log_error ("%s:%s: no crypter found for mail: %p",
+                 SRCNAME, __func__, arg);
+      gpgrt_lock_unlock (&parser_lock);
+      return -1;
+    }
+
+  /* This can take a while */
+  int rc = crypter->do_crypto();
+
+  gpgrt_lock_lock (&dtor_lock);
+  if (!Mail::is_valid_ptr (mail))
+    {
+      log_debug ("%s:%s: aborting crypt for: %p already deleted",
+                 SRCNAME, __func__, arg);
+      gpgrt_lock_unlock (&dtor_lock);
+      return 0;
+    }
+
+  if (rc)
+    {
+      log_debug ("%s:%s: crypto failed for: %p with: %i",
+                 SRCNAME, __func__, arg, rc);
+      gpgrt_lock_unlock (&dtor_lock);
+      return rc;
+    }
+
+  mail->set_crypt_state (Mail::NeedsUpdateInMAPI);
+
+  do_in_ui_thread (CRYPTO_DONE, arg);
+  gpgrt_lock_unlock (&dtor_lock);
+  return 0;
+}
+
 bool
 Mail::is_crypto_mail() const
 {
@@ -981,11 +1045,15 @@ Mail::parsing_done()
 }
 
 int
-Mail::encrypt_sign ()
+Mail::encrypt_sign_start ()
 {
-  int err = -1,
-      flags = 0;
-  protocol_t proto = opt.enable_smime ? PROTOCOL_UNKNOWN : PROTOCOL_OPENPGP;
+  if (m_crypt_state != NeedsActualCrypt)
+    {
+      log_debug ("%s:%s: invalid state %i",
+                 SRCNAME, __func__, m_crypt_state);
+      return -1;
+    }
+  int flags = 0;
   if (!needs_crypto())
     {
       return 0;
@@ -995,9 +1063,10 @@ Mail::encrypt_sign ()
     {
       log_error ("%s:%s: Failed to get base message.",
                  SRCNAME, __func__);
-      return err;
+      return -1;
     }
   flags = get_gpgol_draft_info_flags (message);
+  gpgol_release (message);
 
   const auto window = get_active_hwnd ();
 
@@ -1068,35 +1137,29 @@ Mail::encrypt_sign ()
 
   m_do_inline = m_is_gsuite ? true : opt.inline_pgp;
 
-  EnableWindow (window, FALSE);
-  if (flags == 3)
-    {
-      log_debug ("%s:%s: Sign / Encrypting message",
-                 SRCNAME, __func__);
-      err = message_sign_encrypt (message, proto,
-                                  NULL, get_sender ().c_str (), this);
-    }
-  else if (flags == 2)
+  if (m_crypter)
     {
-      err = message_sign (message, proto,
-                          NULL, get_sender ().c_str (), this);
-    }
-  else if (flags == 1)
-    {
-      err = message_encrypt (message, proto,
-                             NULL, get_sender ().c_str (), this);
+      log_error ("%s:%s: Crypter already exists for mail %p",
+                 SRCNAME, __func__, this);
+      return -1;
     }
-  else
+
+  GpgME::Protocol proto = opt.enable_smime ? GpgME::UnknownProtocol: GpgME::OpenPGP;
+  m_crypter = std::shared_ptr <CryptController> (new CryptController (this, flags & 1,
+                                                                      flags & 2,
+                                                                      m_do_inline, proto));
+
+  if (m_crypter->collect_data ())
     {
-      log_debug ("%s:%s: Unknown flags for crypto: %i",
-                 SRCNAME, __func__, flags);
+      log_error ("%s:%s: Crypter for mail %p failed to collect data.",
+                 SRCNAME, __func__, this);
+      return -1;
     }
-  log_debug ("%s:%s: Status: %i",
-             SRCNAME, __func__, err);
-  EnableWindow (window, TRUE);
-  gpgol_release (message);
-  m_crypt_successful = !err;
-  return err;
+  //EnableWindow (window, FALSE);
+  CloseHandle(CreateThread (NULL, 0, do_crypt,
+                            (LPVOID) this, 0,
+                            NULL));
+  return 0;
 }
 
 int
@@ -2431,3 +2494,19 @@ Mail::inline_body_to_body()
   m_inline_body = std::string();
   return ret;
 }
+
+void
+Mail::update_crypt_mapi()
+{
+  log_debug ("%s:%s: Update crypt oom",
+             SRCNAME, __func__);
+  m_crypt_state = WantsSend;
+}
+
+void
+Mail::update_crypt_oom()
+{
+  log_debug ("%s:%s: Update crypt oom",
+             SRCNAME, __func__);
+  m_crypt_state = NeedsSecondAfterWrite;
+}
diff --git a/src/mail.h b/src/mail.h
index 42052e2..389c6e2 100644
--- a/src/mail.h
+++ b/src/mail.h
@@ -32,6 +32,7 @@
 #include <future>
 
 class ParseController;
+class CryptController;
 
 /** @brief Data wrapper around a mailitem.
  *
@@ -47,6 +48,16 @@ class ParseController;
 class Mail
 {
 public:
+  enum CryptState
+    {
+      NoCryptMail,
+      NeedsFirstAfterWrite,
+      NeedsActualCrypt,
+      NeedsUpdateInOOM,
+      NeedsSecondAfterWrite,
+      WantsSend
+    };
+
   /** @brief Construct a mail object for the item.
     *
     * This also installs the event sink for this item.
@@ -134,13 +145,13 @@ public:
    * @returns 0 on success. */
   int decrypt_verify ();
 
-  /** @brief do crypto operations as selected by the user.
+  /** @brief start crypto operations as selected by the user.
    *
    * Initiates the crypto operations according to the gpgol
    * draft info flags.
    *
    * @returns 0 on success. */
-  int encrypt_sign ();
+  int encrypt_sign_start ();
 
   /** @brief Necessary crypto operations were completed successfully. */
   bool crypto_successful () { return !needs_crypto() || m_crypt_successful; }
@@ -226,6 +237,10 @@ public:
     only valid while the actual parsing happens. */
   std::shared_ptr<ParseController> parser () { return m_parser; }
 
+  /** @brief get the associated cryptcontroller.
+    only valid while the crypting happens. */
+  std::shared_ptr<CryptController> crypter () { return m_crypter; }
+
   /** To be called from outside once the paser was done.
    In Qt this would be a slot that is called once it is finished
    we hack around that a bit by calling it from our windowmessages
@@ -347,6 +362,19 @@ public:
 
   /** Set the inline body as OOM body property. */
   int inline_body_to_body ();
+
+  /** Get the crypt state */
+  CryptState crypt_state () const {return m_crypt_state;}
+
+  /** Set the crypt state */
+  void set_crypt_state (CryptState state) {m_crypt_state = state;}
+
+  /** Update MAPI data after encryption. */
+  void update_crypt_mapi ();
+
+  /** Update OOM data after encryption. */
+  void update_crypt_oom ();
+
 private:
   void update_categories ();
   void update_body ();
@@ -373,6 +401,7 @@ private:
   char **m_cached_recipients;
   msgtype_t m_type; /* Our messagetype as set in mapi */
   std::shared_ptr <ParseController> m_parser;
+  std::shared_ptr <CryptController> m_crypter;
   GpgME::VerificationResult m_verify_result;
   GpgME::DecryptionResult m_decrypt_result;
   GpgME::Signature m_sig;
@@ -382,5 +411,6 @@ private:
   bool m_do_inline;
   bool m_is_gsuite; /* Are we on a gsuite account */
   std::string m_inline_body;
+  CryptState m_crypt_state;
 };
 #endif // MAIL_H
diff --git a/src/mailitem-events.cpp b/src/mailitem-events.cpp
index b3161f0..4f1d65f 100644
--- a/src/mailitem-events.cpp
+++ b/src/mailitem-events.cpp
@@ -348,11 +348,31 @@ EVENT_SINK_INVOKE(MailItemEvents)
                         SRCNAME, __func__);
              break;
            }
-          m_mail->update_oom_data ();
-          m_mail->set_needs_encrypt (true);
 
-          invoke_oom_method (m_object, "Save", NULL);
-          if (m_mail->crypto_successful ())
+          if (m_mail->crypt_state () == Mail::NoCryptMail &&
+              m_mail->needs_crypto ())
+            {
+              // First contact with a mail to encrypt update
+              // state and oom data.
+              m_mail->update_oom_data ();
+              m_mail->set_crypt_state (Mail::NeedsFirstAfterWrite);
+
+              // Save the Mail
+              invoke_oom_method (m_object, "Save", NULL);
+
+              // The afterwrite in the save should have triggered
+              // the encryption. We cancel send for our asyncness.
+
+              // Cancel send
+              *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE;
+            }
+
+          if (m_mail->crypt_state () == Mail::NeedsUpdateInOOM)
+            {
+              m_mail->update_crypt_oom ();
+            }
+
+          if (m_mail->crypt_state () == Mail::WantsSend)
             {
               /* Fishy behavior catcher: Sometimes the save does not clean
                  out the body. Weird. Happened at least for one user.
@@ -515,9 +535,17 @@ EVENT_SINK_INVOKE(MailItemEvents)
         {
           log_oom_extra ("%s:%s: AfterWrite : %p",
                          SRCNAME, __func__, m_mail);
-          if (m_mail->needs_encrypt ())
+          if (m_mail->crypt_state () == Mail::NeedsFirstAfterWrite)
+            {
+              /* Seen the first after write. Advance the state */
+              m_mail->set_crypt_state (Mail::NeedsActualCrypt);
+              m_mail->encrypt_sign_start ();
+              return S_OK;
+            }
+          if (m_mail->crypt_state () == Mail::NeedsSecondAfterWrite)
             {
-              m_mail->encrypt_sign ();
+              m_mail->set_crypt_state (Mail::NeedsUpdateInMAPI);
+              m_mail->update_crypt_mapi ();
               return S_OK;
             }
           break;
diff --git a/src/mimemaker.cpp b/src/mimemaker.cpp
index 53e6049..1f9de4e 100644
--- a/src/mimemaker.cpp
+++ b/src/mimemaker.cpp
@@ -1403,7 +1403,7 @@ add_body (Mail *mail, const char *boundary, sink_t sink,
 }
 
 /* Add the body and attachments. Does multipart handling. */
-static int
+int
 add_body_and_attachments (sink_t sink, LPMESSAGE message,
                           mapi_attach_item_t *att_table, Mail *mail,
                           const char *body, int n_att_usable)
diff --git a/src/mimemaker.h b/src/mimemaker.h
index c89a495..2896a60 100644
--- a/src/mimemaker.h
+++ b/src/mimemaker.h
@@ -73,6 +73,9 @@ int restore_msg_from_moss (LPMESSAGE message, LPDISPATCH moss_att,
 
 int count_usable_attachments (mapi_attach_item_t *table);
 
+int add_body_and_attachments (sink_t sink, LPMESSAGE message,
+                              mapi_attach_item_t *att_table, Mail *mail,
+                              const char *body, int n_att_usable);
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/windowmessages.cpp b/src/windowmessages.cpp
index f44b9df..815c217 100644
--- a/src/windowmessages.cpp
+++ b/src/windowmessages.cpp
@@ -88,6 +88,32 @@ gpgol_window_proc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
               Mail::close (mail);
               break;
             }
+          case (CRYPTO_DONE):
+            {
+              auto mail = (Mail*) ctx->data;
+              if (!Mail::is_valid_ptr (mail))
+                {
+                  log_debug ("%s:%s: Crypto done for mail which is gone.",
+                             SRCNAME, __func__);
+                  break;
+                }
+              // modify the mail.
+              if (mail->crypt_state (Mail::NeedsUpdateInOOM))
+                {
+                  mail->update_crypt_oom();
+                }
+              if (mail->crypt_state (Mail::NeedsSecondAfterWrite))
+                {
+                  // Save the Mail
+                  log_debug ("%s:%s: Crypto done for %p Invoking second save.",
+                             SRCNAME, __func__, mail);
+                  invoke_oom_method (mail->item (), "Save", NULL);
+                  log_debug ("%s:%s: Second save done for %p Invoking second send.",
+                             SRCNAME, __func__, mail);
+                }
+              // Finaly this should pass.
+              invoke_oom_method (mail->item (), "Send", NULL);
+            }
           default:
             log_debug ("Unknown msg");
         }
diff --git a/src/windowmessages.h b/src/windowmessages.h
index f53ecd4..a278675 100644
--- a/src/windowmessages.h
+++ b/src/windowmessages.h
@@ -42,7 +42,8 @@ typedef enum _gpgol_wmsg_type
                       to the mail object. */
   RECIPIENT_ADDED = 3, /* A recipient was added. Data should be ptr
                           to mail */
-  CLOSE = 4, /* Send the message in the next event loop. */
+  CLOSE = 4, /* Close the message in the next event loop. */
+  CRYPTO_DONE = 5, /* Sign / Encrypt done. */
 } gpgol_wmsg_type;
 
 typedef struct

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


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




More information about the Gnupg-commits mailing list