diff --git a/src/common_indep.h b/src/common_indep.h
index c8df4d3..b11867a 100644
--- a/src/common_indep.h
+++ b/src/common_indep.h
@@ -1,338 +1,339 @@
 #ifndef COMMON_INDEP_H
 #define COMMON_INDEP_H
 /* common_indep.h - Common, platform indepentent routines used by GpgOL
  * Copyright (C) 2005, 2007, 2008 g10 Code GmbH
  * Copyright (C) 2016 by Bundesamt für Sicherheit in der Informationstechnik
  * Software engineering by 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
  * 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/>.
  */
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
 
 #include <string.h>
 
 #include <gpgme.h>
 
 #include "xmalloc.h"
 
 #include "debug.h"
 
 #include "memdbg.h"
 
 #ifdef HAVE_W32_SYSTEM
 /* Not so independenent ;-) need this for logging HANDLE */
 # include <windows.h>
 #endif
 
 /* The Registry key used by Gpg4win.  */
 #ifdef _WIN64
 # define GPG4WIN_REGKEY_2  "Software\\Wow6432Node\\GNU\\GnuPG"
 #else
 # define GPG4WIN_REGKEY_2  "Software\\GNU\\GnuPG"
 #endif
 #ifdef _WIN64
 # define GPG4WIN_REGKEY_3  "Software\\Wow6432Node\\Gpg4win"
 #else
 # define GPG4WIN_REGKEY_3  "Software\\Gpg4win"
 #endif
 /* Identifiers for the protocol.  We use different one than those use
    by gpgme.  FIXME: We might want to define an unknown protocol to
    non-null and define such a value also in gpgme. */
 typedef enum
   {
     PROTOCOL_UNKNOWN = 0,
     PROTOCOL_OPENPGP = 1000,
     PROTOCOL_SMIME   = 1001
   }
 protocol_t;
 
 
 /* Possible options for the recipient dialog. */
 enum
   {
     OPT_FLAG_TEXT     =  2,
     OPT_FLAG_FORCE    =  4,
     OPT_FLAG_CANCEL   =  8
   };
 
 
 typedef enum
   {
     GPG_FMT_NONE = 0,       /* do not encrypt attachments */
     GPG_FMT_CLASSIC = 1,    /* encrypt attachments without any encoding */
     GPG_FMT_PGP_PEF = 2     /* use the PGP partioned encoding format (PEF) */
   }
 gpgol_format_t;
 
 /* Type of a message. */
 typedef enum
   {
     OPENPGP_NONE = 0,
     OPENPGP_MSG,
     OPENPGP_SIG,
     OPENPGP_CLEARSIG,
     OPENPGP_PUBKEY,   /* Note, that this type is only partly supported */
     OPENPGP_SECKEY    /* Note, that this type is only partly supported */
   }
 openpgp_t;
 
 /* The list of message types we support in GpgOL.  */
 typedef enum
   {
     MSGTYPE_UNKNOWN = 0,
     MSGTYPE_SMIME,         /* Original SMIME class. */
     MSGTYPE_GPGOL,
     MSGTYPE_GPGOL_MULTIPART_SIGNED,
     MSGTYPE_GPGOL_MULTIPART_ENCRYPTED,
     MSGTYPE_GPGOL_OPAQUE_SIGNED,
     MSGTYPE_GPGOL_OPAQUE_ENCRYPTED,
     MSGTYPE_GPGOL_CLEAR_SIGNED,
     MSGTYPE_GPGOL_PGP_MESSAGE,
     MSGTYPE_GPGOL_WKS_CONFIRMATION
   }
 msgtype_t;
 
 typedef enum
   {
     ATTACHTYPE_UNKNOWN = 0,
     ATTACHTYPE_MOSS = 1,         /* The original MOSS message (ie. a
                                     S/MIME or PGP/MIME message. */
     ATTACHTYPE_FROMMOSS = 2,     /* Attachment created from MOSS.  */
     ATTACHTYPE_MOSSTEMPL = 3,    /* Attachment has been created in the
                                     course of sending a message */
     ATTACHTYPE_PGPBODY = 4,      /* Attachment contains the original
                                     PGP message body of PGP inline
                                     encrypted messages.  */
     ATTACHTYPE_FROMMOSS_DEC = 5  /* A FROMMOSS attachment that has been
                                     temporarily decrypted and needs to be
                                     encrypted before it is written back
                                     into storage. */
   }
 attachtype_t;
 
 /* An object to collect information about one MAPI attachment.  */
 struct mapi_attach_item_s
 {
   int end_of_table;     /* True if this is the last plus one entry of
                            the table. */
   void *private_mapitable; /* Only for use by mapi_release_attach_table. */
 
   int mapipos;          /* The position which needs to be passed to
                            MAPI to open the attachment.  -1 means that
                            there is no valid attachment.  */
 
   int method;           /* MAPI attachment method. */
   char *filename;       /* Malloced filename of this attachment or NULL. */
 
   /* Malloced string with the MIME attrib or NULL.  Parameters are
      stripped off thus a compare against "type/subtype" is
      sufficient. */
   char *content_type;
 
   /* If not NULL the parameters of the content_type. */
   const char *content_type_parms;
 
   /* If not NULL the content_id */
   char *content_id;
 
   /* The attachment type from Property GpgOL Attach Type.  */
   attachtype_t attach_type;
 };
 typedef struct mapi_attach_item_s mapi_attach_item_t;
 
 
 /* Passphrase callback structure. */
 struct passphrase_cb_s
 {
   gpgme_key_t signer;
   gpgme_ctx_t ctx;
   char keyid[16+1];
   char *user_id;
   char *pass;
   int opts;
   int ttl;  /* TTL of the passphrase. */
   unsigned int decrypt_cmd:1; /* 1 = show decrypt dialog, otherwise secret key
 			         selection. */
   unsigned int hide_pwd:1;
   unsigned int last_was_bad:1;
 };
 
 /* Global options - initialized to default by main.c. */
 #ifdef __cplusplus
 extern "C" {
 #if 0
 }
 #endif
 #endif
 
 #ifdef __cplusplus
 extern
 #endif
 
 struct
 {
   int enable_debug;	     /* Enable extra debug options.  Values
                                 larger than 1 increases the debug log
                                 verbosity.  */
   int enable_smime;	     /* Enable S/MIME support. */
   int encrypt_default;       /* Encrypt by default. */
   int sign_default;          /* Sign by default. */
   int prefer_html;           /* Prefer html in html/text alternatives. */
   int inline_pgp;            /* Only for Addin. Use Inline PGP by default. */
   int autoresolve;           /* Autresolve keys with --locate-keys. */
   int autosecure;             /* Autmatically encrypt if locate returns enough validity. */
   int reply_crypt;           /* Only for Addin. Encrypt / Sign based on cryptostatus. */
   int automation;            /* General automation */
   int autotrust;             /* TOFU configured for GpgOL */
   int sync_enc;              /* Disabed async encryption */
   int sync_dec;              /* Disabed async decryption */
   int prefer_smime;          /* S/MIME prefered when autoresolving */
   int smime_html_warn_shown; /* Flag to save if unsigned smime warning was shown */
   int autoretrieve;           /* Use --auto-key-retrieve. */
   int search_smime_servers;  /* Search for S/MIME keys on all configured S/MIME keyservers
                                 for each new unknown mail */
   int auto_unstrusted;       /* Automatically encrypt even to untrusted keys. */
   int autoimport;            /* Automatically import keys from headers or attachments. */
+  char *draft_key;           /* Encrypt drafts with this key (fingerprint) */
 
   /* The forms revision number of the binary.  */
   int forms_revision;
 
 } opt;
 
 
 /* The state object used by b64_decode.  */
 struct b64_state_s
 {
   int idx;
   unsigned char val;
   int stop_seen;
   int invalid_encoding;
 };
 typedef struct b64_state_s b64_state_t;
 
 size_t qp_decode (char *buffer, size_t length, int *r_slbrk);
 char *qp_encode (const char *input, size_t length, size_t* outlen);
 void b64_init (b64_state_t *state);
 size_t b64_decode (b64_state_t *state, char *buffer, size_t length);
 char * b64_encode (const char *input, size_t length);
 
 char *latin1_to_utf8 (const char *string);
 
 char *mem2str (char *dest, const void *src, size_t n);
 
 char *trim_spaces (char *string);
 char *trim_trailing_spaces (char *string);
 
 /* To avoid that a compiler optimizes certain memset calls away, these
    macros may be used instead. */
 #define wipememory2(_ptr,_set,_len) do { \
               volatile char *_vptr=(volatile char *)(_ptr); \
               size_t _vlen=(_len); \
               while(_vlen) { *_vptr=(_set); _vptr++; _vlen--; } \
                   } while(0)
 #define wipememory(_ptr,_len) wipememory2(_ptr,0,_len)
 #define wipestring(_ptr) do { \
               volatile char *_vptr=(volatile char *)(_ptr); \
               while(*_vptr) { *_vptr=0; _vptr++; } \
                   } while(0)
 
 void set_default_key (const char *name);
 
 /*-- Convenience macros. -- */
 #define DIM(v)		     (sizeof(v)/sizeof((v)[0]))
 #define DIMof(type,member)   DIM(((type *)0)->member)
 
 /*-- Macros to replace ctype ones to avoid locale problems. --*/
 #define spacep(p)   (*(p) == ' ' || *(p) == '\t')
 #define digitp(p)   (*(p) >= '0' && *(p) <= '9')
 #define hexdigitp(a) (digitp (a)                     \
                       || (*(a) >= 'A' && *(a) <= 'F')  \
                       || (*(a) >= 'a' && *(a) <= 'f'))
   /* Note this isn't identical to a C locale isspace() without \f and
      \v, but works for the purposes used here. */
 #define ascii_isspace(a) ((a)==' ' || (a)=='\n' || (a)=='\r' || (a)=='\t')
 
 /* The atoi macros assume that the buffer has only valid digits. */
 #define atoi_1(p)   (*(p) - '0' )
 #define atoi_2(p)   ((atoi_1(p) * 10) + atoi_1((p)+1))
 #define atoi_4(p)   ((atoi_2(p) * 100) + atoi_2((p)+2))
 #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))
 #define xtoi_4(p)   ((xtoi_2(p) * 256) + xtoi_2((p)+2))
 
 #define tohex(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'A'))
 
 #define tohex_lower(n) ((n) < 10 ? ((n) + '0') : (((n) - 10) + 'a'))
 /***** Inline functions.  ****/
 
 /* Return true if LINE consists only of white space (up to and
    including the LF). */
 static inline int
 trailing_ws_p (const char *line)
 {
   for ( ; *line && *line != '\n'; line++)
     if (*line != ' ' && *line != '\t' && *line != '\r')
       return 0;
   return 1;
 }
 
 /* An strcmp variant with the compare ending at the end of B.  */
 static inline int
 tagcmp (const char *a, const char *b)
 {
   return strncmp (a, b, strlen (b));
 }
 
 #ifdef HAVE_W32_SYSTEM
 extern HANDLE log_mutex;
 #endif
 /*****  Missing functions.  ****/
 
 #ifndef HAVE_STPCPY
 static inline char *
 _gpgol_stpcpy (char *a, const char *b)
 {
   while (*b)
     *a++ = *b++;
   *a = 0;
   return a;
 }
 #define stpcpy(a,b) _gpgol_stpcpy ((a), (b))
 #endif /*!HAVE_STPCPY*/
 
 /* The length of the boundary - the buffer needs to be allocated one
    byte larger. */
 #define BOUNDARYSIZE 20
 char *generate_boundary (char *buffer);
 
 #ifdef __cplusplus
 }
 
 #include <string>
 #include <gpgme++/data.h>
 struct autocrypt_s
 {
   bool exists;
   std::string pref;
   std::string addr;
   GpgME::Data data;
 };
 #endif
 
 #endif // COMMON_INDEP_H
diff --git a/src/cryptcontroller.cpp b/src/cryptcontroller.cpp
index 0537bf2..a68d154 100644
--- a/src/cryptcontroller.cpp
+++ b/src/cryptcontroller.cpp
@@ -1,1240 +1,1269 @@
 /* @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 "cpphelp.h"
 #include "cryptcontroller.h"
 #include "mail.h"
 #include "mapihelp.h"
 #include "mimemaker.h"
 #include "wks-helper.h"
 #include "overlay.h"
 #include "keycache.h"
 #include "mymapitags.h"
 
 #include <gpgme++/context.h>
 #include <gpgme++/signingresult.h>
 #include <gpgme++/encryptionresult.h>
 
 #include "common.h"
 
 #include <sstream>
 
 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;
 }
 
 static int
 create_sign_attach (sink_t sink, protocol_t protocol,
                     GpgME::Data &signature,
                     GpgME::Data &signedData,
                     const char *micalg);
 
 /** 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,
                                   GpgME::Protocol proto):
     m_mail (mail),
     m_encrypt (encrypt),
     m_sign (sign),
     m_crypto_success (false),
     m_proto (proto)
 {
   TSTART;
   memdbg_ctor ("CryptController");
   log_debug ("%s:%s: CryptController ctor for %p encrypt %i sign %i inline %i.",
              SRCNAME, __func__, mail, encrypt, sign, mail->getDoPGPInline ());
   m_recipient_addrs = mail->getCachedRecipients ();
   TRETURN;
 }
 
 CryptController::~CryptController()
 {
   TSTART;
   memdbg_dtor ("CryptController");
   log_debug ("%s:%s:%p",
              SRCNAME, __func__, m_mail);
   TRETURN;
 }
 
 int
 CryptController::collect_data ()
 {
   TSTART;
   /* 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->takeCachedPlainBody ();
   if (body && !*body)
     {
       xfree (body);
       body = nullptr;
     }
 
   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)
     {
       gpgol_message_box (m_mail->getWindow (),
                          utf8_gettext ("Can't encrypt / sign an empty message."),
                          utf8_gettext ("GpgOL"), MB_OK);
       gpgol_release (message);
       mapi_release_attach_table (att_table);
       xfree (body);
       TRETURN -1;
     }
 
   bool do_inline = m_mail->getDoPGPInline ();
 
   if (n_att_usable && do_inline)
     {
       log_debug ("%s:%s: PGP Inline not supported for attachments."
                  " Using PGP MIME",
                  SRCNAME, __func__);
       do_inline = false;
       m_mail->setDoPGPInline (false);
     }
   else if (do_inline)
     {
       /* Inline. Use Body as input.
         We need to collect also our mime structure for S/MIME
         as we don't know yet if we are S/MIME or OpenPGP */
       m_bodyInput.write (body, strlen (body));
       log_debug ("%s:%s: Inline. Caching body.",
                  SRCNAME, __func__);
       /* Set the input buffer to start. */
       m_bodyInput.seek (0, SEEK_SET);
     }
 
   /* 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 */
   int err = add_body_and_attachments (sink, message, att_table, m_mail,
                                       body, n_att_usable);
   xfree (body);
 
   if (err)
     {
       log_error ("%s:%s: Collecting body and attachments failed.",
                  SRCNAME, __func__);
       gpgol_release (message);
       mapi_release_attach_table (att_table);
       TRETURN -1;
     }
 
   /* Message is no longer needed */
   gpgol_release (message);
 
   mapi_release_attach_table (att_table);
 
   /* Set the input buffer to start. */
   m_input.seek (0, SEEK_SET);
   TRETURN 0;
 }
 
 int
 CryptController::lookup_fingerprints (const std::string &sigFpr,
                                       const std::vector<std::string> recpFprs)
 {
   TSTART;
   auto ctx = std::shared_ptr<GpgME::Context> (GpgME::Context::createForProtocol (m_proto));
 
   if (!ctx)
     {
       log_error ("%s:%s: failed to create context with protocol '%s'",
                  SRCNAME, __func__,
                  m_proto == GpgME::CMS ? "smime" :
                  m_proto == GpgME::OpenPGP ? "openpgp" :
                  "unknown");
       TRETURN -1;
     }
 
   ctx->setKeyListMode (GpgME::Local);
   GpgME::Error err;
 
   if (!sigFpr.empty()) {
       m_signer_key = ctx->key (sigFpr.c_str (), err, true);
       if (err || m_signer_key.isNull ()) {
           log_error ("%s:%s: failed to lookup key for '%s' with protocol '%s'",
                      SRCNAME, __func__, anonstr (sigFpr.c_str ()),
                      m_proto == GpgME::CMS ? "smime" :
                      m_proto == GpgME::OpenPGP ? "openpgp" :
                      "unknown");
           TRETURN -1;
       }
       // reset context
       ctx = std::shared_ptr<GpgME::Context> (GpgME::Context::createForProtocol (m_proto));
       ctx->setKeyListMode (GpgME::Local);
   }
 
   if (!recpFprs.size()) {
      TRETURN 0;
   }
 
   // Convert recipient fingerprints
   char **cRecps = vector_to_cArray (recpFprs);
 
   err = ctx->startKeyListing (const_cast<const char **> (cRecps));
 
   if (err) {
       log_error ("%s:%s: failed to start recipient keylisting",
                  SRCNAME, __func__);
       release_cArray (cRecps);
       TRETURN -1;
   }
 
   do {
       m_recipients.push_back(ctx->nextKey(err));
   } while (!err);
 
   m_recipients.pop_back();
 
   release_cArray (cRecps);
 
   TRETURN 0;
 }
 
 
 int
 CryptController::parse_output (GpgME::Data &resolverOutput)
 {
   TSTART;
   // Todo: Use Data::toString
   std::istringstream ss(resolverOutput.toString());
   std::string line;
 
   std::string sigFpr;
   std::vector<std::string> recpFprs;
   while (std::getline (ss, line))
     {
       rtrim (line);
       if (line == "cancel")
         {
           log_debug ("%s:%s: resolver canceled",
                      SRCNAME, __func__);
           TRETURN -2;
         }
       if (line == "unencrypted")
         {
           log_debug ("%s:%s: FIXME resolver wants unencrypted",
                      SRCNAME, __func__);
           TRETURN -1;
         }
       std::istringstream lss (line);
 
       // First is sig or enc
       std::string what;
       std::string how;
       std::string fingerprint;
 
       std::getline (lss, what, ':');
       std::getline (lss, how, ':');
       std::getline (lss, fingerprint, ':');
 
       if (m_proto == GpgME::UnknownProtocol)
         {
           m_proto = (how == "smime") ? GpgME::CMS : GpgME::OpenPGP;
         }
 
       if (what == "sig")
         {
           if (!sigFpr.empty ())
             {
               log_error ("%s:%s: multiple signing keys not supported",
                          SRCNAME, __func__);
 
             }
           sigFpr = fingerprint;
           continue;
         }
       if (what == "enc")
         {
           recpFprs.push_back (fingerprint);
         }
     }
 
   if (m_sign && sigFpr.empty())
     {
       log_error ("%s:%s: Sign requested but no signing fingerprint - sending unsigned",
                  SRCNAME, __func__);
       m_sign = false;
     }
   if (m_encrypt && !recpFprs.size())
     {
       log_error ("%s:%s: Encrypt requested but no recipient fingerprints",
                  SRCNAME, __func__);
       gpgol_message_box (m_mail->getWindow (),
                          utf8_gettext ("No recipients for encryption selected."),
                          _("GpgOL"), MB_OK);
       TRETURN -2;
     }
 
   TRETURN lookup_fingerprints (sigFpr, recpFprs);
 }
 
 static bool
 resolve_through_protocol (const GpgME::Protocol &proto, bool sign,
                           bool encrypt, const std::string &sender,
                           const std::vector<std::string> &recps,
                           std::vector<GpgME::Key> &r_keys,
                           GpgME::Key &r_sig)
 {
   TSTART;
   bool sig_ok = true;
   bool enc_ok = true;
 
   const auto cache = KeyCache::instance();
 
   if (encrypt)
     {
       r_keys = cache->getEncryptionKeys(recps, proto);
       enc_ok = !r_keys.empty();
     }
   if (sign && enc_ok)
     {
       r_sig = cache->getSigningKey (sender.c_str (), proto);
       sig_ok = !r_sig.isNull();
     }
   TRETURN sig_ok && enc_ok;
 }
 
 int
 CryptController::resolve_keys_cached()
 {
   TSTART;
   // Prepare variables
   const auto cached_sender = m_mail->getSender ();
   auto recps = m_recipient_addrs;
 
   if (m_encrypt)
     {
       recps.push_back (cached_sender);
     }
 
   bool resolved = false;
   if (opt.enable_smime && opt.prefer_smime)
     {
       resolved = resolve_through_protocol (GpgME::CMS, m_sign, m_encrypt,
                                            cached_sender, recps, m_recipients,
                                            m_signer_key);
       if (resolved)
         {
           log_debug ("%s:%s: Resolved with CMS due to preference.",
                      SRCNAME, __func__);
           m_proto = GpgME::CMS;
         }
     }
   if (!resolved)
     {
       resolved = resolve_through_protocol (GpgME::OpenPGP, m_sign, m_encrypt,
                                            cached_sender, recps, m_recipients,
                                            m_signer_key);
       if (resolved)
         {
           log_debug ("%s:%s: Resolved with OpenPGP.",
                      SRCNAME, __func__);
           m_proto = GpgME::OpenPGP;
         }
     }
   if (!resolved && (opt.enable_smime && !opt.prefer_smime))
     {
       resolved = resolve_through_protocol (GpgME::CMS, m_sign, m_encrypt,
                                            cached_sender, recps, m_recipients,
                                            m_signer_key);
       if (resolved)
         {
           log_debug ("%s:%s: Resolved with CMS as fallback.",
                      SRCNAME, __func__);
           m_proto = GpgME::CMS;
         }
     }
 
   if (!resolved)
     {
       log_debug ("%s:%s: Failed to resolve through cache",
                  SRCNAME, __func__);
       m_recipients.clear();
       m_signer_key = GpgME::Key();
       m_proto = GpgME::UnknownProtocol;
       TRETURN 1;
     }
 
   if (!m_recipients.empty())
     {
       log_debug ("%s:%s: Encrypting with protocol %s to:",
                  SRCNAME, __func__, to_cstr (m_proto));
     }
   for (const auto &key: m_recipients)
     {
       log_debug ("%s", anonstr (key.primaryFingerprint ()));
     }
   if (!m_signer_key.isNull())
     {
       log_debug ("%s:%s: Signing key: %s:%s",
                  SRCNAME, __func__, anonstr (m_signer_key.primaryFingerprint ()),
                  to_cstr (m_signer_key.protocol()));
     }
   TRETURN 0;
 }
 
 int
 CryptController::resolve_keys ()
 {
   TSTART;
+
   m_recipients.clear();
 
+  if (m_mail->isDraftEncrypt() && opt.draft_key)
+    {
+      const auto key = KeyCache::instance()->getByFpr (opt.draft_key);
+      if (key.isNull())
+        {
+          const char *buf = utf8_gettext ("Failed to encrypt draft.\n\n"
+                                          "The configured encryption key for drafts "
+                                          "could not be found.\n"
+                                          "Please check your configuration or "
+                                          "turn off draft encryption in the settings.");
+          gpgol_message_box (get_active_hwnd (),
+                             buf,
+                             _("GpgOL"), MB_OK);
+          TRETURN -1;
+        }
+      log_debug ("%s:%s: resolved draft encryption key protocol is: %s",
+                 SRCNAME, __func__, to_cstr (key.protocol()));
+      m_recipients.push_back (key);
+      TRETURN 0;
+    }
+
   if (!m_recipient_addrs.size())
     {
       /* Should not happen. But we add it for better bug reports. */
       const char *bugmsg = utf8_gettext ("Operation failed.\n\n"
               "This is usually caused by a bug in GpgOL or an error in your setup.\n"
               "Please see https://www.gpg4win.org/reporting-bugs.html "
               "or ask your Administrator for support.");
       char *buf;
       gpgrt_asprintf (&buf, "Failed to resolve recipients.\n\n%s\n", bugmsg);
       memdbg_alloc (buf);
       gpgol_message_box (get_active_hwnd (),
                          buf,
                          _("GpgOL"), MB_OK);
       xfree(buf);
       TRETURN -1;
     }
 
   if (opt.autoresolve && !resolve_keys_cached ())
     {
       log_debug ("%s:%s: resolved keys through the cache",
                  SRCNAME, __func__);
       start_crypto_overlay();
       TRETURN 0;
     }
 
   std::vector<std::string> args;
 
   // Collect the arguments
   char *gpg4win_dir = get_gpg4win_dir ();
   if (!gpg4win_dir)
     {
       TRACEPOINT;
       TRETURN -1;
     }
   const auto resolver = std::string (gpg4win_dir) + "\\bin\\resolver.exe";
   args.push_back (resolver);
 
   log_debug ("%s:%s: resolving keys with '%s'",
              SRCNAME, __func__, resolver.c_str ());
 
   // We want debug output as OutputDebugString
   args.push_back (std::string ("--debug"));
 
   // Yes passing it as int is ok.
   auto wnd = m_mail->getWindow ();
   if (wnd)
     {
       // Pass the handle of the active window for raise / overlay.
       args.push_back (std::string ("--hwnd"));
       args.push_back (std::to_string ((int) (intptr_t) wnd));
     }
 
   // Set the overlay caption
   args.push_back (std::string ("--overlayText"));
   if (m_encrypt)
     {
       args.push_back (std::string (_("Resolving recipients...")));
     }
   else if (m_sign)
     {
       args.push_back (std::string (_("Resolving signers...")));
     }
 
   if (!opt.enable_smime)
     {
       args.push_back (std::string ("--protocol"));
       args.push_back (std::string ("pgp"));
     }
 
   if (m_sign)
     {
       args.push_back (std::string ("--sign"));
     }
   const auto cached_sender = m_mail->getSender ();
   if (cached_sender.empty())
     {
       log_error ("%s:%s: resolve keys without sender.",
                  SRCNAME, __func__);
     }
   else
     {
       args.push_back (std::string ("--sender"));
       args.push_back (cached_sender);
     }
 
   if (!opt.autoresolve)
     {
       args.push_back (std::string ("--alwaysShow"));
     }
 
   if (opt.prefer_smime)
     {
       args.push_back (std::string ("--preferred-protocol"));
       args.push_back (std::string ("cms"));
     }
 
   args.push_back (std::string ("--lang"));
   args.push_back (std::string (gettext_localename ()));
 
   if (m_encrypt)
     {
       args.push_back (std::string ("--encrypt"));
       // Get the recipients that are cached from OOM
       for (const auto &addr: m_recipient_addrs)
         {
           const auto mbox = GpgME::UserID::addrSpecFromString (addr.c_str());
           const auto overrides = KeyCache::instance ()->getOverrides (mbox);
           if (overrides.size())
             {
               std::string overrideStr = mbox + ":";
               for (const auto &key: overrides)
                 {
                   if (key.isNull())
                     {
                       TRACEPOINT;
                       continue;
                     }
                   overrideStr += key.primaryFingerprint();
                   overrideStr += ",";
                 }
               overrideStr.erase(overrideStr.size() - 1, 1);
               args.push_back (std::string ("-o"));
               args.push_back (overrideStr);
             }
           args.push_back (mbox);
         }
     }
 
   // Args are prepared. Spawn the resolver.
   auto ctx = GpgME::Context::createForEngine (GpgME::SpawnEngine);
   if (!ctx)
     {
       // can't happen
       TRACEPOINT;
       TRETURN -1;
     }
 
 
   // Convert our collected vector to c strings
   // It's a bit overhead but should be quick for such small
   // data.
   char **cargs = vector_to_cArray (args);
   log_data ("%s:%s: Spawn args:",
             SRCNAME, __func__);
   for (size_t i = 0; cargs && cargs[i]; i++)
     {
       log_data (SIZE_T_FORMAT ": '%s'", i, cargs[i]);
     }
 
   GpgME::Data mystdin (GpgME::Data::null), mystdout, mystderr;
   GpgME::Error err = ctx->spawn (cargs[0], const_cast <const char**> (cargs),
                                  mystdin, mystdout, mystderr,
                                  (GpgME::Context::SpawnFlags) (
                                   GpgME::Context::SpawnAllowSetFg |
                                   GpgME::Context::SpawnShowWindow));
   // Somehow Qt messes up which window to bring back to front.
   // So we do it manually.
   bring_to_front (wnd);
 
   // We need to create an overlay while encrypting as pinentry can take a while
   start_crypto_overlay();
 
   log_data ("Resolver stdout:\n'%s'", mystdout.toString ().c_str ());
   log_data ("Resolver stderr:\n'%s'", mystderr.toString ().c_str ());
 
   release_cArray (cargs);
 
   if (err)
     {
       log_debug ("%s:%s: Resolver spawn finished Err code: %i asString: %s",
                  SRCNAME, __func__, err.code(), err.asString());
     }
 
   int ret = parse_output (mystdout);
   if (ret == -1)
     {
       log_debug ("%s:%s: Failed to parse / resolve keys.",
                  SRCNAME, __func__);
       log_data ("Resolver stdout:\n'%s'", mystdout.toString ().c_str ());
       log_data ("Resolver stderr:\n'%s'", mystderr.toString ().c_str ());
       TRETURN -1;
     }
 
   TRETURN ret;
 }
 
 int
 CryptController::do_crypto (GpgME::Error &err, std::string &r_diag)
 {
   TSTART;
   log_debug ("%s:%s",
              SRCNAME, __func__);
 
+  if (m_mail->isDraftEncrypt ())
+    {
+      log_debug ("%s:%s Disabling sign because of draft encrypt",
+                 SRCNAME, __func__);
+      m_sign = false;
+    }
+
   /* Start a WKS check if necessary. */
   WKSHelper::instance()->start_check (m_mail->getSender ());
 
   int ret = resolve_keys ();
   if (ret == -1)
     {
       //error
       log_debug ("%s:%s: Failure to resolve keys.",
                  SRCNAME, __func__);
       TRETURN -1;
     }
   if (ret == -2)
     {
       // Cancel
       TRETURN -2;
     }
   bool do_inline = m_mail->getDoPGPInline ();
 
   if (m_proto == GpgME::CMS && do_inline)
     {
       log_debug ("%s:%s: Inline for S/MIME not supported. Switching to mime.",
                  SRCNAME, __func__);
       do_inline = false;
       m_mail->setDoPGPInline (false);
       m_bodyInput = GpgME::Data(GpgME::Data::null);
     }
 
   auto ctx = GpgME::Context::create(m_proto);
 
   if (!ctx)
     {
       log_error ("%s:%s: Failure to create context.",
                  SRCNAME, __func__);
       gpgol_message_box (m_mail->getWindow (),
                          "Failure to create context.",
                          utf8_gettext ("GpgOL"), MB_OK);
       TRETURN -1;
     }
   if (!m_signer_key.isNull())
     {
       ctx->addSigningKey (m_signer_key);
     }
 
   ctx->setTextMode (m_proto == GpgME::OpenPGP);
   ctx->setArmor (m_proto == GpgME::OpenPGP);
 
   if (m_encrypt && m_sign && do_inline)
     {
       // Sign encrypt combined
       const auto result_pair = ctx->signAndEncrypt (m_recipients,
                                                     do_inline ? m_bodyInput : m_input,
                                                     m_output,
                                                     GpgME::Context::AlwaysTrust);
       const auto err1 = result_pair.first.error();
       const auto err2 = result_pair.second.error();
 
       if (err1 || err2)
         {
           log_error ("%s:%s: Encrypt / Sign error %s %s.",
                      SRCNAME, __func__, result_pair.first.error().asString(),
                      result_pair.second.error().asString());
           err = err1 ? err1 : err2;
           GpgME::Data log;
           const auto err3 = ctx->getAuditLog (log,
                                               GpgME::Context::DiagnosticAuditLog);
           if (!err3)
             {
               r_diag = log.toString();
             }
           TRETURN -1;
         }
 
       if (err1.isCanceled() || err2.isCanceled())
         {
           err = err1.isCanceled() ? err1 : err2;
           log_debug ("%s:%s: User cancled",
                      SRCNAME, __func__);
           TRETURN -2;
         }
     }
   else if (m_encrypt && m_sign)
     {
       // First sign then encrypt
       const auto sigResult = ctx->sign (m_input, m_output,
                                         GpgME::Detached);
       err = sigResult.error();
       if (err)
         {
           log_error ("%s:%s: Signing error %s.",
                      SRCNAME, __func__, sigResult.error().asString());
           GpgME::Data log;
           const auto err3 = ctx->getAuditLog (log,
                                               GpgME::Context::DiagnosticAuditLog);
           if (!err3)
             {
               r_diag = log.toString();
             }
           TRETURN -1;
         }
       if (err.isCanceled())
         {
           log_debug ("%s:%s: User cancled",
                      SRCNAME, __func__);
           TRETURN -2;
         }
       parse_micalg (sigResult);
 
       // We now have plaintext in m_input
       // The detached signature in m_output
 
       // Set up the sink object to construct the multipart/signed
       GpgME::Data multipart;
       struct sink_s sinkmem;
       sink_t sink = &sinkmem;
       memset (sink, 0, sizeof *sink);
       sink->cb_data = &multipart;
       sink->writefnc = sink_data_write;
 
       if (create_sign_attach (sink,
                               m_proto == GpgME::CMS ?
                                          PROTOCOL_SMIME : PROTOCOL_OPENPGP,
                               m_output, m_input, m_micalg.c_str ()))
         {
           TRACEPOINT;
           TRETURN -1;
         }
 
       // Now we have the multipart throw away the rest.
       m_output = GpgME::Data ();
       m_input = GpgME::Data ();
       multipart.seek (0, SEEK_SET);
       const auto encResult = ctx->encrypt (m_recipients, multipart,
                                            m_output,
                                            GpgME::Context::AlwaysTrust);
       err = encResult.error();
       if (err)
         {
           log_error ("%s:%s: Encryption error %s.",
                      SRCNAME, __func__, err.asString());
           GpgME::Data log;
           const auto err3 = ctx->getAuditLog (log,
                                               GpgME::Context::DiagnosticAuditLog);
           if (!err3)
             {
               r_diag = log.toString();
             }
           TRETURN -1;
         }
       if (err.isCanceled())
         {
           log_debug ("%s:%s: User cancled",
                      SRCNAME, __func__);
           TRETURN -2;
         }
       // Now we have encrypted output just treat it like encrypted.
     }
   else if (m_encrypt)
     {
       const auto result = ctx->encrypt (m_recipients, do_inline ? m_bodyInput : m_input,
                                         m_output,
                                         GpgME::Context::AlwaysTrust);
       err = result.error();
       if (err)
         {
           log_error ("%s:%s: Encryption error %s.",
                      SRCNAME, __func__, err.asString());
           GpgME::Data log;
           const auto err3 = ctx->getAuditLog (log,
                                               GpgME::Context::DiagnosticAuditLog);
           if (!err3)
             {
               r_diag = log.toString();
             }
           TRETURN -1;
         }
       if (err.isCanceled())
         {
           log_debug ("%s:%s: User cancled",
                      SRCNAME, __func__);
           TRETURN -2;
         }
     }
   else if (m_sign)
     {
       const auto result = ctx->sign (do_inline ? m_bodyInput : m_input, m_output,
                                      do_inline ? GpgME::Clearsigned :
                                      GpgME::Detached);
       err = result.error();
       if (err)
         {
           log_error ("%s:%s: Signing error %s.",
                      SRCNAME, __func__, err.asString());
           GpgME::Data log;
           const auto err3 = ctx->getAuditLog (log,
                                               GpgME::Context::DiagnosticAuditLog);
           if (!err3)
             {
               r_diag = log.toString();
             }
           TRETURN -1;
         }
       if (err.isCanceled())
         {
           log_debug ("%s:%s: User cancled",
                      SRCNAME, __func__);
           TRETURN -2;
         }
       parse_micalg (result);
     }
   else
     {
       // ???
       log_error ("%s:%s: unreachable code reached.",
                  SRCNAME, __func__);
     }
 
 
   log_debug ("%s:%s: Crypto done sucessfuly.",
              SRCNAME, __func__);
   m_crypto_success = true;
 
   TRETURN 0;
 }
 
 static int
 write_data (sink_t sink, GpgME::Data &data)
 {
   TSTART;
   if (!sink || !sink->writefnc)
     {
       TRETURN -1;
     }
 
   char buf[4096];
   size_t nread;
   data.seek (0, SEEK_SET);
   while ((nread = data.read (buf, 4096)) > 0)
     {
       sink->writefnc (sink, buf, nread);
     }
 
   TRETURN 0;
 }
 
 int
 create_sign_attach (sink_t sink, protocol_t protocol,
                     GpgME::Data &signature,
                     GpgME::Data &signedData,
                     const char *micalg)
 {
   TSTART;
   char boundary[BOUNDARYSIZE+1];
   char top_header[BOUNDARYSIZE+200];
   int rc = 0;
 
   /* Write the top header.  */
   generate_boundary (boundary);
   create_top_signing_header (top_header, sizeof top_header,
                              protocol, 1, boundary,
                              micalg);
 
   if ((rc = write_string (sink, top_header)))
     {
       TRACEPOINT;
       TRETURN rc;
     }
 
   /* Write the boundary so that it is not included in the hashing.  */
   if ((rc = write_boundary (sink, boundary, 0)))
     {
       TRACEPOINT;
       TRETURN rc;
     }
 
   /* Write the signed mime structure */
   if ((rc = write_data (sink, signedData)))
     {
       TRACEPOINT;
       TRETURN rc;
     }
 
   /* Write the signature attachment */
   if ((rc = write_boundary (sink, boundary, 0)))
     {
       TRACEPOINT;
       TRETURN rc;
     }
 
   if (protocol == PROTOCOL_OPENPGP)
     {
       rc = write_string (sink,
                          "Content-Type: application/pgp-signature;\r\n"
                          "\tname=\"" OPENPGP_SIG_NAME "\"\r\n"
                          "Content-Transfer-Encoding: 7Bit\r\n");
     }
   else
     {
       rc = write_string (sink,
                          "Content-Transfer-Encoding: base64\r\n"
                          "Content-Type: application/pkcs7-signature\r\n"
                          "Content-Disposition: inline;\r\n"
                          "\tfilename=\"" SMIME_SIG_NAME "\"\r\n");
       /* rc = write_string (sink, */
       /*                    "Content-Type: application/x-pkcs7-signature\r\n" */
       /*                    "\tname=\"smime.p7s\"\r\n" */
       /*                    "Content-Transfer-Encoding: base64\r\n" */
       /*                    "Content-Disposition: attachment;\r\n" */
       /*                    "\tfilename=\"smime.p7s\"\r\n"); */
 
     }
 
   if (rc)
     {
       TRACEPOINT;
       TRETURN rc;
     }
 
   if ((rc = write_string (sink, "\r\n")))
     {
       TRACEPOINT;
       TRETURN rc;
     }
 
   // Write the signature data
   if (protocol == PROTOCOL_SMIME)
     {
       const std::string sigStr = signature.toString();
       if ((rc = write_b64 (sink, (const void *) sigStr.c_str (), sigStr.size())))
         {
           TRACEPOINT;
           TRETURN rc;
         }
     }
   else if ((rc = write_data (sink, signature)))
     {
       TRACEPOINT;
       TRETURN rc;
     }
 
   // Add an extra linefeed with should not harm.
   if ((rc = write_string (sink, "\r\n")))
     {
       TRACEPOINT;
       TRETURN rc;
     }
 
   /* Write the final boundary.  */
   if ((rc = write_boundary (sink, boundary, 1)))
     {
       TRACEPOINT;
       TRETURN rc;
     }
 
   TRETURN rc;
 }
 
 static int
 create_encrypt_attach (sink_t sink, protocol_t protocol,
                        GpgME::Data &encryptedData,
                        int exchange_major_version)
 {
   TSTART;
   char boundary[BOUNDARYSIZE+1];
   int rc = create_top_encryption_header (sink, protocol, boundary,
                                          false, exchange_major_version);
   // From here on use goto failure pattern.
   if (rc)
     {
       log_error ("%s:%s: Failed to create top header.",
                  SRCNAME, __func__);
       TRETURN rc;
     }
 
   if (protocol == PROTOCOL_OPENPGP ||
       exchange_major_version >= 15)
     {
       // With exchange 2016 we have to construct S/MIME
       // differently and write the raw data here.
       rc = write_data (sink, encryptedData);
     }
   else
     {
       const auto encStr = encryptedData.toString();
       rc = write_b64 (sink, encStr.c_str(), encStr.size());
     }
 
   if (rc)
     {
       log_error ("%s:%s: Failed to create top header.",
                  SRCNAME, __func__);
       TRETURN rc;
     }
 
   /* Write the final boundary (for OpenPGP) and finish the attachment.  */
   if (*boundary && (rc = write_boundary (sink, boundary, 1)))
     {
       log_error ("%s:%s: Failed to write boundary.",
                  SRCNAME, __func__);
     }
   TRETURN rc;
 }
 
 int
 CryptController::update_mail_mapi ()
 {
   TSTART;
   log_debug ("%s:%s", SRCNAME, __func__);
 
   LPMESSAGE message = get_oom_base_message (m_mail->item());
   if (!message)
     {
       log_error ("%s:%s: Failed to obtain message.",
                  SRCNAME, __func__);
       TRETURN -1;
     }
 
   if (m_mail->getDoPGPInline ())
     {
       // Nothing to do for inline.
       log_debug ("%s:%s: Inline mail. Setting encoding.",
                  SRCNAME, __func__);
 
       SPropValue prop;
       prop.ulPropTag = PR_INTERNET_CPID;
       prop.Value.l = 65001;
       if (HrSetOneProp (message, &prop))
         {
           log_error ("%s:%s: Failed to set CPID mapiprop.",
                      SRCNAME, __func__);
         }
 
       TRETURN 0;
     }
 
   mapi_attach_item_t *att_table = mapi_create_attach_table (message, 0);
 
   /* When we forward e.g. a crypto mail we have sent the message
      has a MOSSTEMPL. We need to remove that. T4321 */
   for (ULONG pos=0; att_table && !att_table[pos].end_of_table; pos++)
     {
       if (att_table[pos].attach_type == ATTACHTYPE_MOSSTEMPL)
         {
           log_debug ("%s:%s: Found existing moss attachment at "
                      "pos %i removing it.", SRCNAME, __func__,
                      att_table[pos].mapipos);
           if (message->DeleteAttach (att_table[pos].mapipos, 0,
                                      nullptr, 0) != S_OK)
             {
               log_error ("%s:%s: Failed to remove attachment.",
                          SRCNAME, __func__);
             }
 
         }
     }
 
   // Set up the sink object for our MSOXSMIME attachment.
   struct sink_s sinkmem;
   sink_t sink = &sinkmem;
   memset (sink, 0, sizeof *sink);
   sink->cb_data = &m_input;
   sink->writefnc = sink_data_write;
 
   // For S/MIME encrypted mails we have to use the application/pkcs7-mime
   // content type. Otherwise newer (2016) exchange servers will throw
   // an M2MCVT.StorageError.Exeption (See GnuPG-Bug-Id: T3853 )
 
   // This means that the conversion / build of the mime structure also
   // happens differently.
   int exchange_major_version = get_ex_major_version_for_addr (
                                         m_mail->getSender ().c_str ());
 
   std::string overrideMimeTag;
   if (m_proto == GpgME::CMS && m_encrypt && exchange_major_version >= 15)
     {
       log_debug ("%s:%s: CMS Encrypt with Exchange %i activating alternative.",
                  SRCNAME, __func__, exchange_major_version);
       overrideMimeTag = "application/pkcs7-mime";
     }
 
   LPATTACH attach = create_mapi_attachment (message, sink,
                                             overrideMimeTag.empty() ? nullptr :
                                             overrideMimeTag.c_str());
   if (!attach)
     {
       log_error ("%s:%s: Failed to create moss attach.",
                  SRCNAME, __func__);
       gpgol_release (message);
       TRETURN -1;
     }
 
   protocol_t protocol = m_proto == GpgME::CMS ?
                                    PROTOCOL_SMIME :
                                    PROTOCOL_OPENPGP;
 
   int rc = 0;
   /* Do we have override MIME ? */
   const auto overrideMime = m_mail->get_override_mime_data ();
   if (!overrideMime.empty())
     {
       rc = write_string (sink, overrideMime.c_str ());
     }
   else if (m_sign && m_encrypt)
     {
       rc = create_encrypt_attach (sink, protocol, m_output, exchange_major_version);
     }
   else if (m_encrypt)
     {
       rc = create_encrypt_attach (sink, protocol, m_output, exchange_major_version);
     }
   else if (m_sign)
     {
       rc = create_sign_attach (sink, protocol, m_output, m_input, m_micalg.c_str ());
     }
 
   // Close our attachment
   if (!rc)
     {
       rc = close_mapi_attachment (&attach, sink);
     }
 
   // Set message class etc.
   if (!rc)
     {
       rc = finalize_message (message, att_table, protocol, m_encrypt ? 1 : 0,
                              false);
     }
 
   // only on error.
   if (rc)
     {
       cancel_mapi_attachment (&attach, sink);
     }
 
   // cleanup
   mapi_release_attach_table (att_table);
   gpgol_release (attach);
   gpgol_release (message);
 
   TRETURN rc;
 }
 
 std::string
 CryptController::get_inline_data ()
 {
   TSTART;
   std::string ret;
   if (!m_mail->getDoPGPInline ())
     {
       TRETURN ret;
     }
   m_output.seek (0, SEEK_SET);
   char buf[4096];
   size_t nread;
   while ((nread = m_output.read (buf, 4096)) > 0)
     {
       ret += std::string (buf, nread);
     }
   TRETURN ret;
 }
 
 void
 CryptController::parse_micalg (const GpgME::SigningResult &result)
 {
   TSTART;
   if (result.isNull())
     {
       TRACEPOINT;
       TRETURN;
     }
   const auto signature = result.createdSignature(0);
   if (signature.isNull())
     {
       TRACEPOINT;
       TRETURN;
     }
 
   const char *hashAlg = signature.hashAlgorithmAsString ();
   if (!hashAlg)
     {
       TRACEPOINT;
       TRETURN;
     }
   if (m_proto == GpgME::OpenPGP)
     {
       m_micalg = std::string("pgp-") + hashAlg;
     }
   else
     {
       m_micalg = hashAlg;
     }
   std::transform(m_micalg.begin(), m_micalg.end(), m_micalg.begin(), ::tolower);
 
   log_debug ("%s:%s: micalg is: '%s'.",
              SRCNAME, __func__, m_micalg.c_str ());
   TRETURN;
 }
 
 void
 CryptController::start_crypto_overlay ()
 {
   TSTART;
   auto wid = m_mail->getWindow ();
 
   std::string text;
 
   if (m_encrypt)
     {
       text = _("Encrypting...");
     }
   else if (m_sign)
     {
       text = _("Signing...");
     }
   m_overlay = std::unique_ptr<Overlay> (new Overlay (wid, text));
   TRETURN;
 }
diff --git a/src/mail.cpp b/src/mail.cpp
index 0f07c79..c2211d5 100644
--- a/src/mail.cpp
+++ b/src/mail.cpp
@@ -1,4015 +1,4060 @@
 /* @file mail.h
  * @brief High level class to work with Outlook Mailitems.
  *
  * Copyright (C) 2015, 2016 by Bundesamt für Sicherheit in der Informationstechnik
  * Software engineering by 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 "categorymanager.h"
 #include "dialogs.h"
 #include "common.h"
 #include "mail.h"
 #include "eventsinks.h"
 #include "attachment.h"
 #include "mapihelp.h"
 #include "mimemaker.h"
 #include "revert.h"
 #include "gpgoladdin.h"
 #include "mymapitags.h"
 #include "parsecontroller.h"
 #include "cryptcontroller.h"
 #include "windowmessages.h"
 #include "mlang-charset.h"
 #include "wks-helper.h"
 #include "keycache.h"
 #include "cpphelp.h"
 #include "addressbook.h"
 
 #include <gpgme++/configuration.h>
 #include <gpgme++/tofuinfo.h>
 #include <gpgme++/verificationresult.h>
 #include <gpgme++/decryptionresult.h>
 #include <gpgme++/key.h>
 #include <gpgme++/context.h>
 #include <gpgme++/keylistresult.h>
 #include <gpg-error.h>
 
 #include <map>
 #include <unordered_map>
 #include <set>
 #include <vector>
 #include <memory>
 #include <sstream>
 
 #undef _
 #define _(a) utf8_gettext (a)
 
 using namespace GpgME;
 
 static std::map<LPDISPATCH, Mail*> s_mail_map;
 static std::map<std::string, Mail*> s_uid_map;
 static std::map<std::string, LPDISPATCH> s_folder_events_map;
 static std::set<std::string> uids_searched;
 
 GPGRT_LOCK_DEFINE (mail_map_lock);
 GPGRT_LOCK_DEFINE (uid_map_lock);
 
 static Mail *s_last_mail;
 
 #define COPYBUFSIZE (8 * 1024)
 
 Mail::Mail (LPDISPATCH mailitem) :
     m_mailitem(mailitem),
     m_currentItemRef(nullptr),
     m_processed(false),
     m_needs_wipe(false),
     m_needs_save(false),
     m_crypt_successful(false),
     m_is_smime(false),
     m_is_smime_checked(false),
     m_is_signed(false),
     m_is_valid(false),
     m_close_triggered(false),
     m_is_html_alternative(false),
     m_needs_encrypt(false),
     m_moss_position(0),
     m_crypto_flags(0),
     m_cached_html_body(nullptr),
     m_cached_plain_body(nullptr),
     m_type(MSGTYPE_UNKNOWN),
     m_do_inline(false),
     m_is_gsuite(false),
     m_crypt_state(NoCryptMail),
     m_window(nullptr),
     m_async_crypt_disabled(false),
     m_is_forwarded_crypto_mail(false),
     m_is_reply_crypto_mail(false),
     m_is_send_again(false),
     m_disable_att_remove_warning(false),
     m_manual_crypto_opts(false),
     m_first_autosecure_check(true),
     m_locate_count(0),
     m_pass_write(false),
     m_locate_in_progress(false),
-    m_is_junk(false)
+    m_is_junk(false),
+    m_is_draft_encrypt(false)
 {
   TSTART;
   if (getMailForItem (mailitem))
     {
       log_error ("Mail object for item: %p already exists. Bug.",
                  mailitem);
       TRETURN;
     }
 
   m_event_sink = install_MailItemEvents_sink (mailitem);
   if (!m_event_sink)
     {
       /* Should not happen but in that case we don't add us to the list
          and just release the Mail item. */
       log_error ("%s:%s: Failed to install MailItemEvents sink.",
                  SRCNAME, __func__);
       gpgol_release(mailitem);
       TRETURN;
     }
   gpgol_lock (&mail_map_lock);
   s_mail_map.insert (std::pair<LPDISPATCH, Mail *> (mailitem, this));
   gpgol_unlock (&mail_map_lock);
   s_last_mail = this;
   memdbg_ctor ("Mail");
   TRETURN;
 }
 
 GPGRT_LOCK_DEFINE(dtor_lock);
 
 // static
 void
 Mail::lockDelete ()
 {
   TSTART;
   gpgol_lock (&dtor_lock);
   TRETURN;
 }
 
 // static
 void
 Mail::unlockDelete ()
 {
   TSTART;
   gpgol_unlock (&dtor_lock);
   TRETURN;
 }
 
 Mail::~Mail()
 {
   TSTART;
   /* This should fix a race condition where the mail is
      deleted before the parser is accessed in the decrypt
      thread. The shared_ptr of the parser then ensures
      that the parser is alive even if the mail is deleted
      while parsing. */
   gpgol_lock (&dtor_lock);
   memdbg_dtor ("Mail");
   log_oom ("%s:%s: dtor: Mail: %p item: %p",
                  SRCNAME, __func__, this, m_mailitem);
   std::map<LPDISPATCH, Mail *>::iterator it;
 
   log_oom ("%s:%s: Detaching event sink",
                  SRCNAME, __func__);
   detach_MailItemEvents_sink (m_event_sink);
   gpgol_release(m_event_sink);
 
   log_oom ("%s:%s: Erasing mail",
                  SRCNAME, __func__);
   gpgol_lock (&mail_map_lock);
   it = s_mail_map.find(m_mailitem);
   if (it != s_mail_map.end())
     {
       s_mail_map.erase (it);
     }
   gpgol_unlock (&mail_map_lock);
 
   if (!m_uuid.empty())
     {
       gpgol_lock (&uid_map_lock);
       auto it2 = s_uid_map.find(m_uuid);
       if (it2 != s_uid_map.end())
         {
           s_uid_map.erase (it2);
         }
       gpgol_unlock (&uid_map_lock);
     }
 
   log_oom ("%s:%s: removing categories",
                  SRCNAME, __func__);
   removeCategories_o ();
 
   log_oom ("%s:%s: releasing mailitem",
                  SRCNAME, __func__);
   gpgol_release(m_mailitem);
   xfree (m_cached_html_body);
   xfree (m_cached_plain_body);
   if (!m_uuid.empty())
     {
       log_oom ("%s:%s: destroyed: %p uuid: %s",
                      SRCNAME, __func__, this, m_uuid.c_str());
     }
   else
     {
       log_oom ("%s:%s: non crypto (or sent) mail: %p destroyed",
                      SRCNAME, __func__, this);
     }
   log_oom ("%s:%s: nulling shared pointer",
                  SRCNAME, __func__);
   m_parser = nullptr;
   m_crypter = nullptr;
 
   releaseCurrentItem();
   gpgol_unlock (&dtor_lock);
   log_oom ("%s:%s: returning",
                  SRCNAME, __func__);
   TRETURN;
 }
 
 //static
 Mail *
 Mail::getMailForItem (LPDISPATCH mailitem)
 {
   TSTART;
   if (!mailitem)
     {
       TRETURN NULL;
     }
   std::map<LPDISPATCH, Mail *>::iterator it;
   gpgol_lock (&mail_map_lock);
   it = s_mail_map.find(mailitem);
   gpgol_unlock (&mail_map_lock);
   if (it == s_mail_map.end())
     {
       TRETURN NULL;
     }
   TRETURN it->second;
 }
 
 //static
 Mail *
 Mail::getMailForUUID (const char *uuid)
 {
   TSTART;
   if (!uuid)
     {
       TRETURN NULL;
     }
   gpgol_lock (&uid_map_lock);
   auto it = s_uid_map.find(std::string(uuid));
   gpgol_unlock (&uid_map_lock);
   if (it == s_uid_map.end())
     {
       TRETURN NULL;
     }
   TRETURN it->second;
 }
 
 //static
 bool
 Mail::isValidPtr (const Mail *mail)
 {
   TSTART;
   gpgol_lock (&mail_map_lock);
   auto it = s_mail_map.begin();
   while (it != s_mail_map.end())
     {
       if (it->second == mail)
         {
           gpgol_unlock (&mail_map_lock);
           TRETURN true;
         }
       ++it;
     }
   gpgol_unlock (&mail_map_lock);
   TRETURN false;
 }
 
 int
 Mail::preProcessMessage_m ()
 {
   TSTART;
   LPMESSAGE message = get_oom_base_message (m_mailitem);
   if (!message)
     {
       log_error ("%s:%s: Failed to get base message.",
                  SRCNAME, __func__);
       TRETURN 0;
     }
   log_oom ("%s:%s: GetBaseMessage OK for %p.",
                  SRCNAME, __func__, m_mailitem);
   /* Change the message class here. It is important that
      we change the message class in the before read event
      regardless if it is already set to one of GpgOL's message
      classes. Changing the message class (even if we set it
      to the same value again that it already has) causes
      Outlook to reconsider what it "knows" about a message
      and reread data from the underlying base message. */
   mapi_change_message_class (message, 1, &m_type);
 
   if (m_type == MSGTYPE_UNKNOWN)
     {
       /* For unknown messages we still need to check for autocrypt
          headers. If the mails are crypto messages the autocrypt
          stuff is handled in the parsecontroller. */
       autocrypt_s ac;
       if (mapi_get_header_info (message, ac))
         {
           if (ac.exists)
             {
               log_debug ("%s:%s: Importing autocrypt header from unencrypted "
                          "mail.", SRCNAME, __func__);
               KeyCache::import_pgp_key_data (ac.data);
             }
         }
       gpgol_release (message);
       TRETURN 0;
     }
 
   /* Create moss attachments here so that they are properly
      hidden when the item is read into the model. */
   LPMESSAGE parsed_message = get_oom_message (m_mailitem);
   m_moss_position = mapi_mark_or_create_moss_attach (message, parsed_message,
                                                      m_type);
   gpgol_release (parsed_message);
   if (!m_moss_position)
     {
       log_error ("%s:%s: Failed to find moss attachment.",
                  SRCNAME, __func__);
       m_type = MSGTYPE_UNKNOWN;
     }
 
   gpgol_release (message);
   TRETURN 0;
 }
 
 static LPDISPATCH
 get_attachment_o (LPDISPATCH mailitem, int pos)
 {
   TSTART;
   LPDISPATCH attachment;
   LPDISPATCH attachments = get_oom_object (mailitem, "Attachments");
   if (!attachments)
     {
       log_debug ("%s:%s: Failed to get attachments.",
                  SRCNAME, __func__);
       TRETURN NULL;
     }
 
   std::string item_str;
   int count = get_oom_int (attachments, "Count");
   if (count < 1)
     {
       log_debug ("%s:%s: Invalid attachment count: %i.",
                  SRCNAME, __func__, count);
       gpgol_release (attachments);
       TRETURN NULL;
     }
   if (pos > 0)
     {
       item_str = std::string("Item(") + std::to_string(pos) + ")";
     }
   else
     {
       item_str = std::string("Item(") + std::to_string(count) + ")";
     }
   attachment = get_oom_object (attachments, item_str.c_str());
   gpgol_release (attachments);
 
   TRETURN attachment;
 }
 
 /** Helper to check that all attachments are hidden, to be
   called before crypto. */
 int
 Mail::checkAttachments_o () const
 {
   TSTART;
   LPDISPATCH attachments = get_oom_object (m_mailitem, "Attachments");
   if (!attachments)
     {
       log_debug ("%s:%s: Failed to get attachments.",
                  SRCNAME, __func__);
       TRETURN 1;
     }
   int count = get_oom_int (attachments, "Count");
   if (!count)
     {
       gpgol_release (attachments);
       TRETURN 0;
     }
 
   std::string message;
 
   if (isEncrypted () && isSigned ())
     {
       message += _("Not all attachments were encrypted or signed.\n"
                    "The unsigned / unencrypted attachments are:\n\n");
     }
   else if (isSigned ())
     {
       message += _("Not all attachments were signed.\n"
                    "The unsigned attachments are:\n\n");
     }
   else if (isEncrypted ())
     {
       message += _("Not all attachments were encrypted.\n"
                    "The unencrypted attachments are:\n\n");
     }
   else
     {
       gpgol_release (attachments);
       TRETURN 0;
     }
 
   bool foundOne = false;
 
   for (int i = 1; i <= count; i++)
     {
       std::string item_str;
       item_str = std::string("Item(") + std::to_string (i) + ")";
       LPDISPATCH oom_attach = get_oom_object (attachments, item_str.c_str ());
       if (!oom_attach)
         {
           log_error ("%s:%s: Failed to get attachment.",
                      SRCNAME, __func__);
           continue;
         }
       VARIANT var;
       VariantInit (&var);
       if (get_pa_variant (oom_attach, PR_ATTACHMENT_HIDDEN_DASL, &var) ||
           (var.vt == VT_BOOL && var.boolVal == VARIANT_FALSE))
         {
           foundOne = true;
           char *dispName = get_oom_string (oom_attach, "DisplayName");
           message += dispName ? dispName : "Unknown";
           xfree (dispName);
           message += "\n";
         }
       VariantClear (&var);
       gpgol_release (oom_attach);
     }
   gpgol_release (attachments);
   if (foundOne)
     {
       message += "\n";
       message += _("Note: The attachments may be encrypted or signed "
                     "on a file level but the GpgOL status does not apply to them.");
       wchar_t *wmsg = utf8_to_wchar (message.c_str ());
       wchar_t *wtitle = utf8_to_wchar (_("GpgOL Warning"));
       MessageBoxW (get_active_hwnd (), wmsg, wtitle,
                    MB_ICONWARNING|MB_OK);
       xfree (wmsg);
       xfree (wtitle);
     }
   TRETURN 0;
 }
 
 /** Get the cipherstream of the mailitem. */
 static LPSTREAM
 get_attachment_stream_o (LPDISPATCH mailitem, int pos)
 {
   TSTART;
   if (!pos)
     {
       log_debug ("%s:%s: Called with zero pos.",
                  SRCNAME, __func__);
       TRETURN NULL;
     }
   LPDISPATCH attachment = get_attachment_o (mailitem, pos);
   LPSTREAM stream = NULL;
 
   if (!attachment)
     {
       // For opened messages that have ms-tnef type we
       // create the moss attachment but don't find it
       // in the OOM. Try to find it through MAPI.
       HRESULT hr;
       log_debug ("%s:%s: Failed to find MOSS Attachment. "
                  "Fallback to MAPI.", SRCNAME, __func__);
       LPMESSAGE message = get_oom_message (mailitem);
       if (!message)
         {
           log_debug ("%s:%s: Failed to get MAPI Interface.",
                      SRCNAME, __func__);
           TRETURN NULL;
         }
       hr = gpgol_openProperty (message, PR_BODY_A, &IID_IStream, 0, 0,
                                (LPUNKNOWN*)&stream);
       gpgol_release (message);
       if (hr)
         {
           log_debug ("%s:%s: OpenProperty failed: hr=%#lx",
                      SRCNAME, __func__, hr);
           TRETURN NULL;
         }
       TRETURN stream;
     }
 
   LPATTACH mapi_attachment = NULL;
 
   mapi_attachment = (LPATTACH) get_oom_iunknown (attachment,
                                                  "MapiObject");
   gpgol_release (attachment);
   if (!mapi_attachment)
     {
       log_debug ("%s:%s: Failed to get MapiObject of attachment: %p",
                  SRCNAME, __func__, attachment);
       TRETURN NULL;
     }
   if (FAILED (gpgol_openProperty (mapi_attachment, PR_ATTACH_DATA_BIN,
                                   &IID_IStream, 0, MAPI_MODIFY,
                                   (LPUNKNOWN*) &stream)))
     {
       log_debug ("%s:%s: Failed to open stream for mapi_attachment: %p",
                  SRCNAME, __func__, mapi_attachment);
       gpgol_release (mapi_attachment);
     }
   gpgol_release (mapi_attachment);
   TRETURN stream;
 }
 
 #if 0
 
 This should work. But Outlook says no. See the comment in set_pa_variant
 about this. I left the code here as an example how to work with
 safearrays and how this probably should work.
 
 static int
 copy_data_property(LPDISPATCH target, std::shared_ptr<Attachment> attach)
 {
   TSTART;
   VARIANT var;
   VariantInit (&var);
 
   /* Get the size */
   off_t size = attach->get_data ().seek (0, SEEK_END);
   attach->get_data ().seek (0, SEEK_SET);
 
   if (!size)
     {
       TRACEPOINT;
       TRETURN 1;
     }
 
   if (!get_pa_variant (target, PR_ATTACH_DATA_BIN_DASL, &var))
     {
       log_debug("Have variant. type: %x", var.vt);
     }
   else
     {
       log_debug("failed to get variant.");
     }
 
   /* Set the type to an array of unsigned chars (OLE SAFEARRAY) */
   var.vt = VT_ARRAY | VT_UI1;
 
   /* Set up the bounds structure */
   SAFEARRAYBOUND rgsabound[1];
   rgsabound[0].cElements = static_cast<unsigned long> (size);
   rgsabound[0].lLbound = 0;
 
   /* Create an OLE SAFEARRAY */
   var.parray = SafeArrayCreate (VT_UI1, 1, rgsabound);
   if (var.parray == NULL)
     {
       TRACEPOINT;
       VariantClear(&var);
       TRETURN 1;
     }
 
   void *buffer = NULL;
   /* Get a safe pointer to the array */
   if (SafeArrayAccessData(var.parray, &buffer) != S_OK)
     {
       TRACEPOINT;
       VariantClear(&var);
       TRETURN 1;
     }
 
   /* Copy data to it */
   size_t nread = attach->get_data ().read (buffer, static_cast<size_t> (size));
 
   if (nread != static_cast<size_t> (size))
     {
       TRACEPOINT;
       VariantClear(&var);
       TRETURN 1;
     }
 
   /*/ Unlock the variant data */
   if (SafeArrayUnaccessData(var.parray) != S_OK)
     {
       TRACEPOINT;
       VariantClear(&var);
       TRETURN 1;
     }
 
   if (set_pa_variant (target, PR_ATTACH_DATA_BIN_DASL, &var))
     {
       TRACEPOINT;
       VariantClear(&var);
       TRETURN 1;
     }
 
   VariantClear(&var);
   TRETURN 0;
 }
 #endif
 
 static int
 copy_attachment_to_file (std::shared_ptr<Attachment> att, HANDLE hFile)
 {
   TSTART;
   char copybuf[COPYBUFSIZE];
   size_t nread;
 
   /* Security considerations: Writing the data to a temporary
      file is necessary as neither MAPI manipulation works in the
      read event to transmit the data nor Property Accessor
      works (see above). From a security standpoint there is a
      short time where the temporary files are on disk. Tempdir
      should be protected so that only the user can read it. Thus
      we have a local attack that could also take the data out
      of Outlook. FILE_SHARE_READ is necessary so that outlook
      can read the file.
 
      A bigger concern is that the file is manipulated
      by another software to fake the signature state. So
      we keep the write exlusive to us.
 
      We delete the file before closing the write file handle.
   */
 
   /* Make sure we start at the beginning */
   att->get_data ().seek (0, SEEK_SET);
   while ((nread = att->get_data ().read (copybuf, COPYBUFSIZE)))
     {
       DWORD nwritten;
       if (!WriteFile (hFile, copybuf, nread, &nwritten, NULL))
         {
           log_error ("%s:%s: Failed to write in tmp attachment.",
                      SRCNAME, __func__);
           TRETURN 1;
         }
       if (nread != nwritten)
         {
           log_error ("%s:%s: Write truncated.",
                      SRCNAME, __func__);
           TRETURN 1;
         }
     }
   TRETURN 0;
 }
 
 /** Sets some meta data on the last attachment added. The meta
   data is taken from the attachment object. */
 static int
 fixup_last_attachment_o (LPDISPATCH mail,
                          std::shared_ptr<Attachment> attachment)
 {
   TSTART;
   /* Currently we only set content id */
   if (attachment->get_content_id ().empty())
     {
       log_debug ("%s:%s: Content id not found.",
                  SRCNAME, __func__);
       TRETURN 0;
     }
 
   LPDISPATCH attach = get_attachment_o (mail, -1);
   if (!attach)
     {
       log_error ("%s:%s: No attachment.",
                  SRCNAME, __func__);
       TRETURN 1;
     }
   const std::string cid = attachment->get_content_id ();
   int ret = put_pa_string (attach,
                            PR_ATTACH_CONTENT_ID_DASL,
                            cid.c_str());
 
   log_debug ("%s:%s: Set attachment content id to: '%s'",
              SRCNAME, __func__, anonstr (cid.c_str()));
   if (ret)
     {
       log_error ("%s:%s: Failed.", SRCNAME, __func__);
       gpgol_release (attach);
     }
 #if 0
 
   The following was an experiement to delete the ATTACH_FLAGS values
   so that we are not hiding attachments.
 
   LPATTACH mapi_attach = (LPATTACH) get_oom_iunknown (attach, "MAPIOBJECT");
   if (mapi_attach)
     {
       SPropTagArray proparray;
       HRESULT hr;
 
       proparray.cValues = 1;
       proparray.aulPropTag[0] = 0x37140003;
       hr = mapi_attach->DeleteProps (&proparray, NULL);
       if (hr)
         {
           log_error ("%s:%s: can't delete property attach flags: hr=%#lx\n",
                      SRCNAME, __func__, hr);
           ret = -1;
         }
       gpgol_release (mapi_attach);
     }
   else
     {
       log_error ("%s:%s: Failed to get mapi attachment.",
                  SRCNAME, __func__);
     }
 #endif
   gpgol_release (attach);
   TRETURN ret;
 }
 
 /** Helper to update the attachments of a mail object in oom.
   does not modify the underlying mapi structure. */
 static int
 add_attachments_o(LPDISPATCH mail,
                 std::vector<std::shared_ptr<Attachment> > attachments)
 {
   TSTART;
   bool anyError = false;
   for (auto att: attachments)
     {
       int err = 0;
       const auto dispName = att->get_display_name ();
       if (dispName.empty())
         {
           log_error ("%s:%s: Ignoring attachment without display name.",
                      SRCNAME, __func__);
           continue;
         }
       wchar_t* wchar_name = utf8_to_wchar (dispName.c_str());
       if (!wchar_name)
         {
           log_error ("%s:%s: Failed to convert '%s' to wchar.",
                      SRCNAME, __func__, anonstr (dispName.c_str()));
           continue;
         }
 
       HANDLE hFile;
       wchar_t* wchar_file = get_tmp_outfile (wchar_name,
                                              &hFile);
       if (!wchar_file)
         {
           log_error ("%s:%s: Failed to obtain a tmp filename for: %s",
                      SRCNAME, __func__, anonstr (dispName.c_str()));
           err = 1;
         }
       if (!err && copy_attachment_to_file (att, hFile))
         {
           log_error ("%s:%s: Failed to copy attachment %s to temp file",
                      SRCNAME, __func__, anonstr (dispName.c_str()));
           err = 1;
         }
       if (!err && add_oom_attachment (mail, wchar_file, wchar_name))
         {
           log_error ("%s:%s: Failed to add attachment: %s",
                      SRCNAME, __func__, anonstr (dispName.c_str()));
           err = 1;
         }
       if (hFile && hFile != INVALID_HANDLE_VALUE)
         {
           CloseHandle (hFile);
         }
       if (wchar_file && !DeleteFileW (wchar_file))
         {
           log_error ("%s:%s: Failed to delete tmp attachment for: %s",
                      SRCNAME, __func__, anonstr (dispName.c_str()));
           err = 1;
         }
       xfree (wchar_file);
       xfree (wchar_name);
 
       if (!err)
         {
           log_debug ("%s:%s: Added attachment '%s'",
                      SRCNAME, __func__, anonstr (dispName.c_str()));
           err = fixup_last_attachment_o (mail, att);
         }
       if (err)
         {
           anyError = true;
         }
     }
   TRETURN anyError;
 }
 
 GPGRT_LOCK_DEFINE(parser_lock);
 
 static DWORD WINAPI
 do_parsing (LPVOID arg)
 {
   TSTART;
   gpgol_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::isValidPtr (mail))
     {
       log_debug ("%s:%s: canceling parsing for: %p already deleted",
                  SRCNAME, __func__, arg);
       gpgol_unlock (&dtor_lock);
       TRETURN 0;
     }
 
   blockInv ();
   /* This takes a shared ptr of parser. So the parser is
      still valid when the mail is deleted. */
   auto parser = mail->parser ();
   gpgol_unlock (&dtor_lock);
 
   gpgol_lock (&parser_lock);
   /* We lock the parser here to avoid too many
      decryption attempts if there are
      multiple mailobjects which might have already
      been deleted (e.g. by quick switches of the mailview.)
      Let's rather be a bit slower.
      */
   log_debug ("%s:%s: preparing the parser for: %p",
              SRCNAME, __func__, arg);
 
   if (!Mail::isValidPtr (mail))
     {
       log_debug ("%s:%s: cancel for: %p already deleted",
                  SRCNAME, __func__, arg);
       gpgol_unlock (&parser_lock);
       unblockInv();
       TRETURN 0;
     }
 
   if (!parser)
     {
       log_error ("%s:%s: no parser found for mail: %p",
                  SRCNAME, __func__, arg);
       gpgol_unlock (&parser_lock);
       unblockInv();
       TRETURN -1;
     }
   parser->parse();
   if (!opt.sync_dec)
     {
       do_in_ui_thread (PARSING_DONE, arg);
     }
   gpgol_unlock (&parser_lock);
   unblockInv();
   TRETURN 0;
 }
 
 /* How encryption is done:
 
    There are two modes of encryption. Synchronous and Async.
    If async is used depends on the value of mail->async_crypt_disabled.
 
    Synchronous crypto:
 
    > Send Event < | State NoCryptMail
    Needs Crypto ? (get_gpgol_draft_info_flags != 0)
 
    -> No:
       Pass send -> unencrypted mail.
 
    -> Yes:
       mail->update_oom_data
       State = Mail::NeedsFirstAfterWrite
       check_inline_response
       invoke_oom_method (m_object, "Save", NULL);
 
       > Write Event <
       Pass because is_crypto_mail is false (not a decrypted mail)
 
       > AfterWrite Event < | State NeedsFirstAfterWrite
       State = NeedsActualCrypo
       encrypt_sign_start
         collect_input_data
         -> Check if Inline PGP should be used
         do_crypt
           -> Resolve keys / do crypto
 
           State = NeedsUpdateInMAPI
           update_crypt_mapi
           crypter->update_mail_mapi
            if (inline) (Meaning PGP/Inline)
           <-- do nothing.
            else
             build MSOXSMIME attachment and clear body / attachments.
 
           State = NeedsUpdateInOOM
       <- Back to Send Event
       update_crypt_oom
         -> Cleans body or sets PGP/Inline body. (inline_body_to_body)
       State = WantsSendMIME or WantsSendInline
 
       -> Saftey check "has_crypted_or_empty_body"
       -> If MIME Mail do the T3656 check.
 
     Send.
 
     State order for "inline_response" (sync) Mails.
     NoCryptMail
     NeedsFirstAfterWrite
     NeedsActualCrypto
     NeedsUpdateInMAPI
     NeedsUpdateInOOM
     WantsSendMIME (or inline for PGP Inline)
     -> Send.
 
     State order for async Mails
     NoCryptMail
     NeedsFirstAfterWrite
     NeedsActualCrypto
     -> Cancel Send.
     Windowmessages -> Crypto Done
     NeedsUpdateInOOM
     NeedsSecondAfterWrite
     trigger Save.
     NeedsUpdateInMAPI
     WantsSendMIME
     trigger Send.
 */
 static DWORD WINAPI
 do_crypt (LPVOID arg)
 {
   TSTART;
   gpgol_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::isValidPtr (mail))
     {
       log_debug ("%s:%s: canceling crypt for: %p already deleted",
                  SRCNAME, __func__, arg);
       gpgol_unlock (&dtor_lock);
       TRETURN 0;
     }
   if (mail->cryptState () != Mail::NeedsActualCrypt)
     {
       log_debug ("%s:%s: invalid state %i",
                  SRCNAME, __func__, mail->cryptState ());
       mail->enableWindow ();
       gpgol_unlock (&dtor_lock);
       TRETURN -1;
     }
 
   /* This takes a shared ptr of crypter. So the crypter is
      still valid when the mail is deleted. */
   auto crypter = mail->cryper ();
   gpgol_unlock (&dtor_lock);
 
   if (!crypter)
     {
       log_error ("%s:%s: no crypter found for mail: %p",
                  SRCNAME, __func__, arg);
       gpgol_unlock (&parser_lock);
       mail->enableWindow ();
       TRETURN -1;
     }
 
   GpgME::Error err;
   std::string diag;
   int rc = crypter->do_crypto(err, diag);
 
   gpgol_lock (&dtor_lock);
   if (!Mail::isValidPtr (mail))
     {
       log_debug ("%s:%s: aborting crypt for: %p already deleted",
                  SRCNAME, __func__, arg);
       gpgol_unlock (&dtor_lock);
       TRETURN 0;
     }
 
   mail->enableWindow ();
 
   if (rc == -1 || err)
     {
       mail->resetCrypter ();
       crypter = nullptr;
       if (err)
         {
           char *buf = nullptr;
           gpgrt_asprintf (&buf, _("Crypto operation failed:\n%s"),
                           err.asString());
           std::string msg = buf;
           memdbg_alloc (buf);
           xfree (buf);
           if (!diag.empty())
             {
               msg += "\n\n";
               msg += _("Diagnostics");
               msg += ":\n";
               msg += diag;
             }
           gpgol_message_box (mail->getWindow (), msg.c_str (),
                              _("GpgOL"), MB_OK);
         }
       else
         {
           gpgol_bug (mail->getWindow (),
                      ERR_CRYPT_RESOLVER_FAILED);
         }
     }
 
   if (rc || err.isCanceled())
     {
       log_debug ("%s:%s: crypto failed for: %p with: %i err: %i",
                  SRCNAME, __func__, arg, rc, err.code());
       mail->setCryptState (Mail::NoCryptMail);
       mail->resetCrypter ();
       crypter = nullptr;
       gpgol_unlock (&dtor_lock);
       TRETURN rc;
     }
 
   if (!mail->isAsyncCryptDisabled ())
     {
       mail->setCryptState (Mail::NeedsUpdateInOOM);
       gpgol_unlock (&dtor_lock);
       // This deletes the Mail in Outlook 2010
       do_in_ui_thread (CRYPTO_DONE, arg);
       log_debug ("%s:%s: UI thread finished for %p",
                  SRCNAME, __func__, arg);
     }
+  else if (mail->isDraftEncrypt ())
+    {
+      mail->setCryptState (Mail::NeedsUpdateInMAPI);
+      mail->updateCryptMAPI_m ();
+      mail->setIsDraftEncrypt (false);
+      mail->setCryptState (Mail::NoCryptMail);
+    }
   else
     {
       mail->setCryptState (Mail::NeedsUpdateInMAPI);
       mail->updateCryptMAPI_m ();
       if (mail->cryptState () == Mail::WantsSendMIME)
         {
           // For sync crypto we need to switch this.
           mail->setCryptState (Mail::NeedsUpdateInOOM);
         }
       else
         {
           // A bug!
           log_debug ("%s:%s: Resetting crypter because of state mismatch. %p",
                      SRCNAME, __func__, arg);
           crypter = nullptr;
           mail->resetCrypter ();
         }
       gpgol_unlock (&dtor_lock);
     }
   /* This works around a bug in pinentry that it might
      bring the wrong window to front. So after encryption /
      signing we bring outlook back to front.
 
      See GnuPG-Bug-Id: T3732
      */
   do_in_ui_thread_async (BRING_TO_FRONT, nullptr, 250);
   log_debug ("%s:%s: crypto thread for %p finished",
              SRCNAME, __func__, arg);
   TRETURN 0;
 }
 
 bool
 Mail::isCryptoMail () const
 {
   TSTART;
   if (m_type == MSGTYPE_UNKNOWN || m_type == MSGTYPE_GPGOL ||
       m_type == MSGTYPE_SMIME)
     {
       /* Not a message for us. */
       TRETURN false;
     }
   TRETURN true;
 }
 
 int
 Mail::decryptVerify_o ()
 {
   TSTART;
 
   if (!isCryptoMail ())
     {
       log_debug ("%s:%s: Decrypt Verify for non crypto mail: %p.",
                  SRCNAME, __func__, m_mailitem);
       TRETURN 0;
     }
 
   auto cipherstream = get_attachment_stream_o (m_mailitem, m_moss_position);
   if (!cipherstream)
     {
       m_is_junk = is_junk_mail (m_mailitem);
       if (m_is_junk)
         {
           log_debug ("%s:%s: Detected: %p as junk",
                      SRCNAME, __func__, m_mailitem);
           auto mngr = CategoryManager::instance ();
           m_store_id = mngr->addCategoryToMail (this,
                                    CategoryManager::getJunkMailCategory (),
                                    3 /* peach */);
           installFolderEventHandler_o ();
           TRETURN 0;
         }
       log_debug ("%s:%s: Failed to get cipherstream. Aborting handling.",
                  SRCNAME, __func__);
       m_type = MSGTYPE_UNKNOWN;
       TRETURN 1;
     }
 
   setUUID_o ();
   m_processed = true;
   m_pass_write = false;
 
   /* Insert placeholder */
   char *placeholder_buf = nullptr;
   if (m_type == MSGTYPE_GPGOL_WKS_CONFIRMATION)
     {
       gpgrt_asprintf (&placeholder_buf, opt.prefer_html ? decrypt_template_html :
                       decrypt_template,
                       "OpenPGP",
                       _("Pubkey directory confirmation"),
                       _("This is a confirmation request to publish your Pubkey in the "
                         "directory for your domain.\n\n"
                         "<p>If you did not request to publish your Pubkey in your providers "
                         "directory, simply ignore this message.</p>\n"));
     }
   else if (gpgrt_asprintf (&placeholder_buf, opt.prefer_html ? decrypt_template_html :
                       decrypt_template,
                       isSMIME_m () ? "S/MIME" : "OpenPGP",
                       _("message"),
                       _("Please wait while the message is being decrypted / verified...")) == -1)
     {
       log_error ("%s:%s: Failed to format placeholder.",
                  SRCNAME, __func__);
       TRETURN 1;
     }
 
   if (opt.prefer_html)
     {
       char *tmp = get_oom_string (m_mailitem, "HTMLBody");
       if (!tmp)
         {
           TRACEPOINT;
           TRETURN 1;
         }
       m_orig_body = tmp;
       xfree (tmp);
       if (put_oom_string (m_mailitem, "HTMLBody", placeholder_buf))
         {
           log_error ("%s:%s: Failed to modify html body of item.",
                      SRCNAME, __func__);
         }
     }
   else
     {
       char *tmp = get_oom_string (m_mailitem, "Body");
       if (!tmp)
         {
           TRACEPOINT;
           TRETURN 1;
         }
       m_orig_body = tmp;
       xfree (tmp);
       if (put_oom_string (m_mailitem, "Body", placeholder_buf))
         {
           log_error ("%s:%s: Failed to modify body of item.",
                      SRCNAME, __func__);
         }
     }
   memdbg_alloc (placeholder_buf);
   xfree (placeholder_buf);
 
   if (m_type == MSGTYPE_GPGOL_WKS_CONFIRMATION)
     {
       WKSHelper::instance ()->handle_confirmation_read (this, cipherstream);
       TRETURN 0;
     }
 
   m_parser = std::shared_ptr <ParseController> (new ParseController (cipherstream, m_type));
   m_parser->setSender(GpgME::UserID::addrSpecFromString(getSender_o ().c_str()));
 
   if (opt.autoimport)
     {
       /* Handle autocrypt header. As we want to have the import
          of the header in the same thread as the parser we leave it
          to the parser. */
       auto message = MAKE_SHARED (get_oom_message (m_mailitem));
       if (!message)
         {
           /* Hmmm */
           STRANGEPOINT;
         }
       else
         {
           autocrypt_s ainfo;
           if (!mapi_get_header_info ((LPMESSAGE)message.get(), ainfo))
             {
               STRANGEPOINT;
             }
           else if (ainfo.exists)
             {
               m_parser->setAutocryptInfo (ainfo);
             }
         }
     }
 
   log_data ("%s:%s: Parser for \"%s\" is %p",
                    SRCNAME, __func__, getSubject_o ().c_str(), m_parser.get());
   gpgol_release (cipherstream);
 
   if (!opt.sync_dec)
     {
       HANDLE parser_thread = CreateThread (NULL, 0, do_parsing, (LPVOID) this, 0,
                                            NULL);
 
       if (!parser_thread)
         {
           log_error ("%s:%s: Failed to create decrypt / verify thread.",
                      SRCNAME, __func__);
         }
       CloseHandle (parser_thread);
       TRETURN 0;
     }
   else
     {
       /* Parse synchronously */
       do_parsing ((LPVOID) this);
       parsing_done ();
       TRETURN 0;
     }
 }
 
 void find_and_replace(std::string& source, const std::string &find,
                       const std::string &replace)
 {
   TSTART;
   for(std::string::size_type i = 0; (i = source.find(find, i)) != std::string::npos;)
     {
       source.replace(i, find.length(), replace);
       i += replace.length();
     }
   TRETURN;
 }
 
 void
 Mail::updateBody_o ()
 {
   TSTART;
   if (!m_parser)
     {
       TRACEPOINT;
       TRETURN;
     }
 
   const auto error = m_parser->get_formatted_error ();
   if (!error.empty())
     {
       if (opt.prefer_html)
         {
           if (put_oom_string (m_mailitem, "HTMLBody",
                               error.c_str ()))
             {
               log_error ("%s:%s: Failed to modify html body of item.",
                          SRCNAME, __func__);
             }
           else
             {
               log_debug ("%s:%s: Set error html to: '%s'",
                          SRCNAME, __func__, error.c_str ());
             }
 
         }
       else
         {
           if (put_oom_string (m_mailitem, "Body",
                               error.c_str ()))
             {
               log_error ("%s:%s: Failed to modify html body of item.",
                          SRCNAME, __func__);
             }
           else
             {
               log_debug ("%s:%s: Set error plain to: '%s'",
                          SRCNAME, __func__, error.c_str ());
             }
         }
       TRETURN;
     }
   if (m_verify_result.error())
     {
       log_error ("%s:%s: Verification failed. Restoring Body.",
                  SRCNAME, __func__);
       if (opt.prefer_html)
         {
           if (put_oom_string (m_mailitem, "HTMLBody", m_orig_body.c_str ()))
             {
               log_error ("%s:%s: Failed to modify html body of item.",
                          SRCNAME, __func__);
             }
         }
       else
         {
           if (put_oom_string (m_mailitem, "Body", m_orig_body.c_str ()))
             {
               log_error ("%s:%s: Failed to modify html body of item.",
                          SRCNAME, __func__);
             }
         }
       TRETURN;
     }
   // No need to carry body anymore
   m_orig_body = std::string();
   auto html = m_parser->get_html_body ();
   auto body = m_parser->get_body ();
   /** Outlook does not show newlines if \r\r\n is a newline. We replace
     these as apparently some other buggy MUA sends this. */
   find_and_replace (html, "\r\r\n", "\r\n");
   if (opt.prefer_html && !html.empty())
     {
       if (!m_block_html)
         {
           const auto charset = m_parser->get_html_charset();
 
           int codepage = 0;
           if (charset.empty())
             {
               codepage = get_oom_int (m_mailitem, "InternetCodepage");
               log_debug ("%s:%s: Did not find html charset."
                          " Using internet Codepage %i.",
                          SRCNAME, __func__, codepage);
             }
 
           char *converted = ansi_charset_to_utf8 (charset.c_str(), html.c_str(),
                                                   html.size(), codepage);
           TRACEPOINT;
           int ret = put_oom_string (m_mailitem, "HTMLBody", converted ?
                                                             converted : "");
           TRACEPOINT;
           xfree (converted);
           if (ret)
             {
               log_error ("%s:%s: Failed to modify html body of item.",
                          SRCNAME, __func__);
             }
 
           TRETURN;
         }
       else if (!body.empty())
         {
           /* We had a multipart/alternative mail but html should be
              blocked. So we prefer the text/plain part and warn
              once about this so that we hopefully don't get too
              many bugreports about this. */
           if (!opt.smime_html_warn_shown)
             {
               std::string caption = _("GpgOL") + std::string (": ") +
                 std::string (_("HTML display disabled."));
               std::string buf = _("HTML content in unsigned S/MIME mails "
                                   "is insecure.");
               buf += "\n";
               buf += _("GpgOL will only show such mails as text.");
 
               buf += "\n\n";
               buf += _("This message is shown only once.");
 
               gpgol_message_box (getWindow (), buf.c_str(), caption.c_str(),
                                  MB_OK);
               opt.smime_html_warn_shown = true;
               write_options ();
             }
         }
     }
 
   if (body.empty () && m_block_html && !html.empty())
     {
 #if 0
       Sadly the following code still offers to load external references
       it might also be too dangerous if Outlook somehow autoloads the
       references as soon as the Body is put into HTML
 
 
       // Fallback to show HTML as plaintext if HTML display
       // is blocked.
       log_error ("%s:%s: No text body. Putting HTML into plaintext.",
                  SRCNAME, __func__);
 
       char *converted = ansi_charset_to_utf8 (m_parser->get_html_charset().c_str(),
                                               html.c_str(), html.size());
       int ret = put_oom_string (m_mailitem, "HTMLBody", converted ? converted : "");
       xfree (converted);
       if (ret)
         {
           log_error ("%s:%s: Failed to modify html body of item.",
                      SRCNAME, __func__);
           body = html;
         }
       else
         {
           char *plainBody = get_oom_string (m_mailitem, "Body");
 
           if (!plainBody)
             {
               log_error ("%s:%s: Failed to obtain converted plain body.",
                          SRCNAME, __func__);
               body = html;
             }
           else
             {
               ret = put_oom_string (m_mailitem, "HTMLBody", plainBody);
               xfree (plainBody);
               if (ret)
                 {
                   log_error ("%s:%s: Failed to put plain into html body of item.",
                              SRCNAME, __func__);
                   body = html;
                 }
               else
                 {
                   TRETURN;
                 }
             }
         }
 #endif
       body = html;
       std::string caption = _("GpgOL") + std::string (": ") +
         std::string (_("HTML display disabled."));
       std::string buf = _("HTML content in unsigned S/MIME mails "
                           "is insecure.");
       buf += "\n";
       buf += _("GpgOL will only show such mails as text.");
 
       buf += "\n\n";
       buf += _("Please ask the sender to sign the message or\n"
                "to send it with a plain text alternative.");
 
       gpgol_message_box (getWindow (), buf.c_str(), caption.c_str(),
                          MB_OK);
     }
 
   find_and_replace (body, "\r\r\n", "\r\n");
 
   const auto plain_charset = m_parser->get_body_charset();
 
   int codepage = 0;
   if (plain_charset.empty())
     {
       codepage = get_oom_int (m_mailitem, "InternetCodepage");
       log_debug ("%s:%s: Did not find body charset. "
                  "Using internet Codepage %i.",
                  SRCNAME, __func__, codepage);
     }
 
   char *converted = ansi_charset_to_utf8 (plain_charset.c_str(),
                                           body.c_str(), body.size(),
                                           codepage);
   TRACEPOINT;
   int ret = put_oom_string (m_mailitem, "Body", converted ? converted : "");
   TRACEPOINT;
   xfree (converted);
   if (ret)
     {
       log_error ("%s:%s: Failed to modify body of item.",
                  SRCNAME, __func__);
     }
   TRETURN;
 }
 
 static int parsed_count;
 
 void
 Mail::parsing_done()
 {
   TSTART;
   TRACEPOINT;
   log_oom ("Mail %p Parsing done for parser num %i: %p",
                  this, parsed_count++, m_parser.get());
   if (!m_parser)
     {
       /* This should not happen but it happens when outlook
          sends multiple ItemLoad events for the same Mail
          Object. In that case it could happen that one
          parser was already done while a second is now
          returning for the wrong mail (as it's looked up
          by uuid.)
 
          We have a check in get_uuid that the uuid was
          not in the map before (and the parser is replaced).
          So this really really should not happen. We
          handle it anyway as we crash otherwise.
 
          It should not happen because the parser is only
          created in decrypt_verify which is called in the
          read event. And even in there we check if the parser
          was set.
          */
       log_error ("%s:%s: No parser obj. For mail: %p",
                  SRCNAME, __func__, this);
       TRETURN;
     }
   /* Store the results. */
   m_decrypt_result = m_parser->decrypt_result ();
   m_verify_result = m_parser->verify_result ();
 
   const auto subject = m_parser->get_internal_subject ();
   if (!subject.empty ())
     {
       put_oom_string (m_mailitem, "Subject", subject.c_str ());
     }
 
   m_crypto_flags = 0;
   if (!m_decrypt_result.isNull())
     {
       m_crypto_flags |= 1;
     }
   if (m_verify_result.numSignatures())
     {
       m_crypto_flags |= 2;
     }
 
   TRACEPOINT;
   updateSigstate ();
   m_needs_wipe = !m_is_send_again;
 
   TRACEPOINT;
   /* Set categories according to the result. */
   updateCategories_o ();
 
   TRACEPOINT;
   m_block_html = m_parser->shouldBlockHtml ();
 
   if (m_block_html)
     {
       // Just to be careful.
       setBlockStatus_m ();
     }
 
   TRACEPOINT;
   /* Update the body */
   updateBody_o ();
   TRACEPOINT;
 
   /* Check that there are no unsigned / unencrypted messages. */
   checkAttachments_o ();
 
   /* Update attachments */
   if (add_attachments_o (m_mailitem, m_parser->get_attachments()))
     {
       log_error ("%s:%s: Failed to update attachments.",
                  SRCNAME, __func__);
     }
 
   if (m_is_send_again)
     {
       log_debug ("%s:%s: I think that this is the send again of a crypto mail.",
                  SRCNAME, __func__);
 
       /* We no longer want to be treated like a crypto mail. */
       m_type = MSGTYPE_UNKNOWN;
       LPMESSAGE msg = get_oom_base_message (m_mailitem);
       if (!msg)
         {
           TRACEPOINT;
         }
       else
         {
           set_gpgol_draft_info_flags (msg, m_crypto_flags);
           gpgol_release (msg);
         }
       removeOurAttachments_o ();
     }
 
   installFolderEventHandler_o ();
 
   log_debug ("%s:%s: Delayed invalidate to update sigstate.",
              SRCNAME, __func__);
   CloseHandle(CreateThread (NULL, 0, delayed_invalidate_ui, (LPVOID) 300, 0,
                             NULL));
   TRACEPOINT;
   TRETURN;
 }
 
 int
 Mail::encryptSignStart_o ()
 {
   TSTART;
   if (m_crypt_state != NeedsActualCrypt)
     {
       log_debug ("%s:%s: invalid state %i",
                  SRCNAME, __func__, m_crypt_state);
       TRETURN -1;
     }
   int flags = 0;
   if (!needs_crypto_m ())
     {
       TRETURN 0;
     }
   LPMESSAGE message = get_oom_base_message (m_mailitem);
   if (!message)
     {
       log_error ("%s:%s: Failed to get base message.",
                  SRCNAME, __func__);
       TRETURN -1;
     }
   flags = get_gpgol_draft_info_flags (message);
   gpgol_release (message);
 
   const auto window = get_active_hwnd ();
 
   if (m_is_gsuite)
     {
       auto att_table = mapi_create_attach_table (message, 0);
       int n_att_usable = count_usable_attachments (att_table);
       mapi_release_attach_table (att_table);
       /* Check for attachments if we have some abort. */
 
       if (n_att_usable)
         {
           wchar_t *w_title = utf8_to_wchar (_(
                                               "GpgOL: Oops, G Suite Sync account detected"));
           wchar_t *msg = utf8_to_wchar (
                       _("G Suite Sync breaks outgoing crypto mails "
                         "with attachments.\nUsing crypto and attachments "
                         "with G Suite Sync is not supported.\n\n"
                         "See: https://dev.gnupg.org/T3545 for details."));
           MessageBoxW (window,
                        msg,
                        w_title,
                        MB_ICONINFORMATION|MB_OK);
           xfree (msg);
           xfree (w_title);
           TRETURN -1;
         }
     }
 
-  m_do_inline = m_is_gsuite ? true : opt.inline_pgp;
+  m_do_inline = m_is_draft_encrypt ? false :
+                m_is_gsuite ? true : opt.inline_pgp;
 
   GpgME::Protocol proto = opt.enable_smime ? GpgME::UnknownProtocol: GpgME::OpenPGP;
   m_crypter = std::shared_ptr <CryptController> (new CryptController (this, flags & 1,
                                                                       flags & 2,
                                                                       proto));
 
   // Careful from here on we have to check every
   // error condition with window enabling again.
   disableWindow_o ();
   if (m_crypter->collect_data ())
     {
       log_error ("%s:%s: Crypter for mail %p failed to collect data.",
                  SRCNAME, __func__, this);
       enableWindow ();
       TRETURN -1;
     }
 
   if (!m_async_crypt_disabled)
     {
       CloseHandle(CreateThread (NULL, 0, do_crypt,
                                 (LPVOID) this, 0,
                                 NULL));
     }
   else
     {
       do_crypt (this);
     }
   TRETURN 0;
 }
 
 int
 Mail::needs_crypto_m () const
 {
   TSTART;
   LPMESSAGE message = get_oom_message (m_mailitem);
   int ret;
   if (!message)
     {
       log_error ("%s:%s: Failed to get message.",
                  SRCNAME, __func__);
       TRETURN false;
     }
   ret = get_gpgol_draft_info_flags (message);
   gpgol_release(message);
   TRETURN ret;
 }
 
 int
 Mail::wipe_o (bool force)
 {
   TSTART;
   if (!m_needs_wipe && !force)
     {
       TRETURN 0;
     }
   log_debug ("%s:%s: Removing plaintext from mailitem: %p.",
              SRCNAME, __func__, m_mailitem);
   if (put_oom_string (m_mailitem, "HTMLBody",
                       ""))
     {
       if (put_oom_string (m_mailitem, "Body",
                           ""))
         {
           log_debug ("%s:%s: Failed to wipe mailitem: %p.",
                      SRCNAME, __func__, m_mailitem);
           TRETURN -1;
         }
       TRETURN -1;
     }
   else
     {
       put_oom_string (m_mailitem, "Body", "");
     }
   m_needs_wipe = false;
   TRETURN 0;
 }
 
 int
 Mail::updateOOMData_o ()
 {
   TSTART;
   char *buf = nullptr;
   log_debug ("%s:%s", SRCNAME, __func__);
 
   if (!isCryptoMail ())
     {
       /* Update the body format. */
       m_is_html_alternative = get_oom_int (m_mailitem, "BodyFormat") > 1;
 
       /* Store the body. It was not obvious for me (aheinecke) how
          to access this through MAPI. */
       if (m_is_html_alternative)
         {
           log_debug ("%s:%s: Is html alternative mail.", SRCNAME, __func__);
           xfree (m_cached_html_body);
           m_cached_html_body = get_oom_string (m_mailitem, "HTMLBody");
         }
       xfree (m_cached_plain_body);
       m_cached_plain_body = get_oom_string (m_mailitem, "Body");
 
       m_cached_recipients = getRecipients_o ();
     }
   /* For some reason outlook may store the recipient address
      in the send using account field. If we have SMTP we prefer
      the SenderEmailAddress string. */
   if (isCryptoMail ())
     {
       /* This is the case where we are reading a mail and not composing.
          When composing we need to use the SendUsingAccount because if
          you send from the folder of userA but change the from to userB
          outlook will keep the SenderEmailAddress of UserA. This is all
          so horrible. */
       buf = get_sender_SenderEMailAddress (m_mailitem);
 
       if (!buf)
         {
           /* Try the sender Object */
           buf = get_sender_Sender (m_mailitem);
         }
 
       /* We also want to cache sent representing email address so that
          we can use it for verification information. */
       char *buf2 = get_sender_SentRepresentingAddress (m_mailitem);
 
       if (buf2)
         {
           m_sent_on_behalf = buf2;
           xfree (buf2);
         }
     }
 
   if (!buf)
     {
       buf = get_sender_SendUsingAccount (m_mailitem, &m_is_gsuite);
     }
   if (!buf && !isCryptoMail ())
     {
       /* Try the sender Object */
       buf = get_sender_Sender (m_mailitem);
     }
   if (!buf)
     {
       /* We don't have s sender object or SendUsingAccount,
          well, in that case fall back to the current user. */
       buf = get_sender_CurrentUser (m_mailitem);
     }
   if (!buf)
     {
       log_debug ("%s:%s: All fallbacks failed.",
                  SRCNAME, __func__);
       TRETURN -1;
     }
   m_sender = buf;
   xfree (buf);
   TRETURN 0;
 }
 
 std::string
 Mail::getSender_o ()
 {
   TSTART;
   if (m_sender.empty())
     updateOOMData_o ();
   TRETURN m_sender;
 }
 
 std::string
 Mail::getSender () const
 {
   TSTART;
   TRETURN m_sender;
 }
 
 int
 Mail::closeAllMails_o ()
 {
   TSTART;
   int err = 0;
 
   /* Detach Folder sinks */
   for (auto fit = s_folder_events_map.begin(); fit != s_folder_events_map.end(); ++fit)
     {
       detach_FolderEvents_sink (fit->second);
       gpgol_release (fit->second);
     }
   s_folder_events_map.clear();
 
 
   std::map<LPDISPATCH, Mail *>::iterator it;
   TRACEPOINT;
   gpgol_lock (&mail_map_lock);
   std::map<LPDISPATCH, Mail *> mail_map_copy = s_mail_map;
   gpgol_unlock (&mail_map_lock);
   for (it = mail_map_copy.begin(); it != mail_map_copy.end(); ++it)
     {
       /* XXX For non racy code the is_valid_ptr check should not
          be necessary but we crashed sometimes closing a destroyed
          mail. */
       if (!isValidPtr (it->second))
         {
           log_debug ("%s:%s: Already deleted mail for %p",
                    SRCNAME, __func__, it->first);
           continue;
         }
 
       if (!it->second->isCryptoMail ())
         {
           continue;
         }
       if (closeInspector_o (it->second) || close (it->second))
         {
           log_error ("Failed to close mail: %p ", it->first);
           /* Should not happen */
           if (isValidPtr (it->second) && it->second->revert_o ())
             {
               err++;
             }
         }
     }
   TRETURN err;
 }
 int
 Mail::revertAllMails_o ()
 {
   TSTART;
   int err = 0;
   std::map<LPDISPATCH, Mail *>::iterator it;
   gpgol_lock (&mail_map_lock);
   auto mail_map_copy = s_mail_map;
   gpgol_unlock (&mail_map_lock);
   for (it = mail_map_copy.begin(); it != mail_map_copy.end(); ++it)
     {
       if (it->second->revert_o ())
         {
           log_error ("Failed to revert mail: %p ", it->first);
           err++;
           continue;
         }
 
       it->second->setNeedsSave (true);
       if (!invoke_oom_method (it->first, "Save", NULL))
         {
           log_error ("Failed to save reverted mail: %p ", it->second);
           err++;
           continue;
         }
     }
   TRETURN err;
 }
 
 int
 Mail::wipeAllMails_o ()
 {
   TSTART;
   int err = 0;
   std::map<LPDISPATCH, Mail *>::iterator it;
   gpgol_lock (&mail_map_lock);
   auto mail_map_copy = s_mail_map;
   gpgol_unlock (&mail_map_lock);
   for (it = mail_map_copy.begin(); it != mail_map_copy.end(); ++it)
     {
       if (it->second->wipe_o ())
         {
           log_error ("Failed to wipe mail: %p ", it->first);
           err++;
         }
     }
   TRETURN err;
 }
 
 int
 Mail::revert_o ()
 {
   TSTART;
   int err = 0;
   if (!m_processed)
     {
       TRETURN 0;
     }
 
   m_disable_att_remove_warning = true;
 
   err = gpgol_mailitem_revert (m_mailitem);
   if (err == -1)
     {
       log_error ("%s:%s: Message revert failed falling back to wipe.",
                  SRCNAME, __func__);
       TRETURN wipe_o ();
     }
   /* We need to reprocess the mail next time around. */
   m_processed = false;
   m_needs_wipe = false;
   m_disable_att_remove_warning = false;
   TRETURN 0;
 }
 
 bool
 Mail::isSMIME_m ()
 {
   TSTART;
   msgtype_t msgtype;
   LPMESSAGE message;
 
   if (m_is_smime_checked)
     {
       TRETURN m_is_smime;
     }
 
   message = get_oom_message (m_mailitem);
 
   if (!message)
     {
       log_error ("%s:%s: No message?",
                  SRCNAME, __func__);
       TRETURN false;
     }
 
   msgtype = mapi_get_message_type (message);
   m_is_smime = msgtype == MSGTYPE_GPGOL_OPAQUE_ENCRYPTED ||
                msgtype == MSGTYPE_GPGOL_OPAQUE_SIGNED ||
                msgtype == MSGTYPE_SMIME;
 
   /* Check if it is an smime mail. Multipart signed can
      also be true. */
   if (!m_is_smime && msgtype == MSGTYPE_GPGOL_MULTIPART_SIGNED)
     {
       char *proto;
       char *ct = mapi_get_message_content_type (message, &proto, NULL);
       if (ct && proto)
         {
           m_is_smime = (!strcmp (proto, "application/pkcs7-signature") ||
                         !strcmp (proto, "application/x-pkcs7-signature"));
         }
       else
         {
           log_error ("%s:%s: No protocol in multipart / signed mail.",
                      SRCNAME, __func__);
         }
       xfree (proto);
       xfree (ct);
     }
   gpgol_release (message);
   m_is_smime_checked  = true;
 
   log_debug ("%s:%s: Detected %s mail",
              SRCNAME, __func__,
              m_is_smime ? "S/MIME" : "not S/MIME");
 
   TRETURN m_is_smime;
 }
 
 static std::string
 get_string_o (LPDISPATCH item, const char *str)
 {
   TSTART;
   char *buf = get_oom_string (item, str);
   if (!buf)
     {
       TRETURN std::string();
     }
   std::string ret = buf;
   xfree (buf);
   TRETURN ret;
 }
 
 std::string
 Mail::getSubject_o () const
 {
   TSTART;
   TRETURN get_string_o (m_mailitem, "Subject");
 }
 
 std::string
 Mail::getBody_o () const
 {
   TSTART;
   TRETURN get_string_o (m_mailitem, "Body");
 }
 
 std::vector<std::string>
 Mail::getRecipients_o () const
 {
   TSTART;
   LPDISPATCH recipients = get_oom_object (m_mailitem, "Recipients");
   if (!recipients)
     {
       TRACEPOINT;
       std::vector<std::string>();
     }
   bool err = false;
   auto ret = get_oom_recipients (recipients, &err);
   gpgol_release (recipients);
 
   if (err)
     {
       log_debug ("%s:%s: Failed to resolve recipients at this time.",
                  SRCNAME, __func__);
 
     }
 
   TRETURN ret;
 }
 
 int
 Mail::closeInspector_o (Mail *mail)
 {
   TSTART;
   LPDISPATCH inspector = get_oom_object (mail->item(), "GetInspector");
   HRESULT hr;
   DISPID dispid;
   if (!inspector)
     {
       log_debug ("%s:%s: No inspector.",
                  SRCNAME, __func__);
       TRETURN -1;
     }
 
   dispid = lookup_oom_dispid (inspector, "Close");
   if (dispid != DISPID_UNKNOWN)
     {
       VARIANT aVariant[1];
       DISPPARAMS dispparams;
 
       dispparams.rgvarg = aVariant;
       dispparams.rgvarg[0].vt = VT_INT;
       dispparams.rgvarg[0].intVal = 1;
       dispparams.cArgs = 1;
       dispparams.cNamedArgs = 0;
 
       hr = inspector->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
                               DISPATCH_METHOD, &dispparams,
                               NULL, NULL, NULL);
       if (hr != S_OK)
         {
           log_debug ("%s:%s: Failed to close inspector: %#lx",
                      SRCNAME, __func__, hr);
           gpgol_release (inspector);
           TRETURN -1;
         }
     }
   gpgol_release (inspector);
   TRETURN 0;
 }
 
 /* static */
 int
 Mail::close (Mail *mail)
 {
   TSTART;
   VARIANT aVariant[1];
   DISPPARAMS dispparams;
 
   dispparams.rgvarg = aVariant;
   dispparams.rgvarg[0].vt = VT_INT;
   dispparams.rgvarg[0].intVal = 1;
   dispparams.cArgs = 1;
   dispparams.cNamedArgs = 0;
 
   log_oom ("%s:%s: Invoking close for: %p",
                  SRCNAME, __func__, mail->item());
   mail->setCloseTriggered (true);
   int rc = invoke_oom_method_with_parms (mail->item(), "Close",
                                        NULL, &dispparams);
 
   if (!rc)
     {
       log_debug ("%s:%s: Close successful. Next write may pass.",
                  SRCNAME, __func__);
       mail->setPassWrite (true);
     }
   log_oom ("%s:%s: returned from close",
                  SRCNAME, __func__);
   TRETURN rc;
 }
 
 void
 Mail::setCloseTriggered (bool value)
 {
   TSTART;
   m_close_triggered = value;
   TRETURN;
 }
 
 bool
 Mail::getCloseTriggered () const
 {
   TSTART;
   TRETURN m_close_triggered;
 }
 
 static const UserID
 get_uid_for_sender (const Key &k, const char *sender)
 {
   TSTART;
   UserID ret;
 
   if (!sender)
     {
       TRETURN ret;
     }
 
   if (!k.numUserIDs())
     {
       log_debug ("%s:%s: Key without uids",
                  SRCNAME, __func__);
       TRETURN ret;
     }
 
   for (const auto uid: k.userIDs())
     {
       if (!uid.email() || !*(uid.email()))
         {
           /* This happens for S/MIME a lot */
           log_debug ("%s:%s: skipping uid without email.",
                      SRCNAME, __func__);
           continue;
         }
       auto normalized_uid = uid.addrSpec();
       auto normalized_sender = UserID::addrSpecFromString(sender);
 
       if (normalized_sender.empty() || normalized_uid.empty())
         {
           log_error ("%s:%s: normalizing '%s' or '%s' failed.",
                      SRCNAME, __func__, anonstr (uid.email()),
                      anonstr (sender));
           continue;
         }
       if (normalized_sender == normalized_uid)
         {
           ret = uid;
         }
     }
   TRETURN ret;
 }
 
 void
 Mail::updateSigstate ()
 {
   TSTART;
   std::string sender = getSender ();
 
   if (sender.empty())
     {
       log_error ("%s:%s:%i", SRCNAME, __func__, __LINE__);
       TRETURN;
     }
 
   if (m_verify_result.isNull())
     {
       log_debug ("%s:%s: No verify result.",
                  SRCNAME, __func__);
       TRETURN;
     }
 
   if (m_verify_result.error())
     {
       log_debug ("%s:%s: verify error.",
                  SRCNAME, __func__);
       TRETURN;
     }
 
   for (const auto sig: m_verify_result.signatures())
     {
       m_is_signed = true;
       const auto key = KeyCache::instance ()->getByFpr (sig.fingerprint(),
                                                         true);
       m_uid = get_uid_for_sender (key, sender.c_str());
 
       if (m_uid.isNull() && !m_sent_on_behalf.empty ())
         {
           m_uid = get_uid_for_sender (key, m_sent_on_behalf.c_str ());
           if (!m_uid.isNull())
             {
               log_debug ("%s:%s: Using sent on behalf '%s' instead of '%s'",
                          SRCNAME, __func__, anonstr (m_sent_on_behalf.c_str()),
                          anonstr (sender.c_str ()));
             }
         }
       /* Sigsum valid or green is somehow not set in this case.
        * Which is strange as AFAIK this worked in the past. */
       if ((sig.summary() & Signature::Summary::Valid) &&
           m_uid.origin() == GpgME::Key::OriginWKD &&
           (sig.validity() == Signature::Validity::Unknown ||
            sig.validity() == Signature::Validity::Marginal))
         {
           // WKD is a shortcut to Level 2 trust.
           log_debug ("%s:%s: Unknown or marginal from WKD -> Level 2",
                      SRCNAME, __func__);
          }
       else if (m_uid.isNull() || (sig.validity() != Signature::Validity::Marginal &&
           sig.validity() != Signature::Validity::Full &&
           sig.validity() != Signature::Validity::Ultimate))
         {
           /* For our category we only care about trusted sigs. And
           the UID needs to match.*/
           continue;
         }
       else if (sig.validity() == Signature::Validity::Marginal)
         {
           const auto tofu = m_uid.tofuInfo();
           if (!tofu.isNull() &&
               (tofu.validity() != TofuInfo::Validity::BasicHistory &&
                tofu.validity() != TofuInfo::Validity::LargeHistory))
             {
               /* Marginal is only good enough without tofu.
                  Otherwise we wait for basic trust. */
               log_debug ("%s:%s: Discarding marginal signature."
                          "With too little history.",
                          SRCNAME, __func__);
               continue;
             }
         }
       log_debug ("%s:%s: Classified sender as verified uid validity: %i origin: %i",
                  SRCNAME, __func__, m_uid.validity(), m_uid.origin());
       m_sig = sig;
       m_is_valid = true;
       TRETURN;
     }
 
   log_debug ("%s:%s: No signature with enough trust. Using first",
              SRCNAME, __func__);
   m_sig = m_verify_result.signature(0);
   TRETURN;
 }
 
 bool
 Mail::isValidSig () const
 {
   TSTART;
   TRETURN m_is_valid;
 }
 
 void
 Mail::removeCategories_o ()
 {
   TSTART;
   if (!m_store_id.empty () && !m_verify_category.empty ())
     {
       log_oom ("%s:%s: Unreffing verify category",
                        SRCNAME, __func__);
       CategoryManager::instance ()->removeCategory (this,
                                                     m_verify_category);
     }
   if (!m_store_id.empty () && !m_decrypt_result.isNull())
     {
       log_oom ("%s:%s: Unreffing dec category",
                        SRCNAME, __func__);
       CategoryManager::instance ()->removeCategory (this,
                                 CategoryManager::getEncMailCategory ());
     }
   if (m_is_junk)
     {
       log_oom ("%s:%s: Unreffing junk category",
                        SRCNAME, __func__);
       CategoryManager::instance ()->removeCategory (this,
                                 CategoryManager::getJunkMailCategory ());
     }
   TRETURN;
 }
 
 /* Now for some tasty hack: Outlook sometimes does
    not show the new categories properly but instead
    does some weird scrollbar thing. This can be
    avoided by resizing the message a bit. But somehow
    this only needs to be done once.
 
    Weird isn't it? But as this workaround worked let's
    do it programatically. Fun. Wan't some tomato sauce
    with this hack? */
 static void
 resize_active_window ()
 {
   TSTART;
   HWND wnd = get_active_hwnd ();
   static std::vector<HWND> resized_windows;
   if(std::find(resized_windows.begin(), resized_windows.end(), wnd) != resized_windows.end()) {
       /* We only need to do this once per window. XXX But sometimes we also
          need to do this once per view of the explorer. So for now this might
          break but we reduce the flicker. A better solution would be to find
          the current view and track that. */
       TRETURN;
   }
 
   if (!wnd)
     {
       TRACEPOINT;
       TRETURN;
     }
   RECT oldpos;
   if (!GetWindowRect (wnd, &oldpos))
     {
       TRACEPOINT;
       TRETURN;
     }
 
   if (!SetWindowPos (wnd, nullptr,
                      (int)oldpos.left,
                      (int)oldpos.top,
                      /* Anything smaller then 19 was ignored when the window was
                       * maximized on Windows 10 at least with a 1980*1024
                       * resolution. So I assume it's at least 1 percent.
                       * This is all hackish and ugly but should work for 90%...
                       * hopefully.
                       */
                      (int)oldpos.right - oldpos.left - 20,
                      (int)oldpos.bottom - oldpos.top, 0))
     {
       TRACEPOINT;
       TRETURN;
     }
 
   if (!SetWindowPos (wnd, nullptr,
                      (int)oldpos.left,
                      (int)oldpos.top,
                      (int)oldpos.right - oldpos.left,
                      (int)oldpos.bottom - oldpos.top, 0))
     {
       TRACEPOINT;
       TRETURN;
     }
   resized_windows.push_back(wnd);
   TRETURN;
 }
 
 #if 0
 static std::string
 pretty_id (const char *keyId)
 {
   /* Three spaces, four quads and a NULL */
   char buf[20];
   buf[19] = '\0';
   if (!keyId)
     {
       return std::string ("null");
     }
   size_t len = strlen (keyId);
   if (!len)
     {
       return std::string ("empty");
     }
   if (len < 16)
     {
       return std::string (_("Invalid Key"));
     }
   const char *p = keyId + (len - 16);
   int j = 0;
   for (size_t i = 0; i < 16; i++)
     {
       if (i && i % 4 == 0)
         {
           buf[j++] = ' ';
         }
       buf[j++] = *(p + i);
     }
   return std::string (buf);
 }
 #endif
 
 void
 Mail::updateCategories_o ()
 {
   TSTART;
 
   auto mngr = CategoryManager::instance ();
   if (isValidSig ())
     {
       char *buf;
       /* Resolve to the primary fingerprint */
 #if 0
       const auto sigKey = KeyCache::instance ()->getByFpr (m_sig.fingerprint (),
                                                            true);
       const char *sigFpr;
       if (sigKey.isNull())
         {
           sigFpr = m_sig.fingerprint ();
         }
       else
         {
           sigFpr = sigKey.primaryFingerprint ();
         }
 #endif
       /* If m_uid addrSpec would not return a result we would never
        * have gotten the UID. */
       int lvl = get_signature_level ();
 
       /* TRANSLATORS: The first placeholder is for tranlsation of "Level".
          The second one is for the level number. The third is for the
          translation of "trust in" and the last one is for the mail
          address used for verification. The result is used as the
          text on the green bar for signed mails. e.g.:
          "GpgOL: Level 3 trust in 'john.doe@example.org'" */
       gpgrt_asprintf (&buf, "GpgOL: %s %i %s '%s'", _("Level"), lvl,
                       _("trust in"),
                       m_uid.addrSpec ().c_str ());
       memdbg_alloc (buf);
 
       int color = 0;
       if (lvl == 2)
         {
           color = 7; /* Olive */
         }
       if (lvl == 3)
         {
           color = 5; /* Green */
         }
       if (lvl == 4)
         {
           color = 20; /* Dark Green */
         }
       m_store_id = mngr->addCategoryToMail (this, buf, color);
       m_verify_category = buf;
       xfree (buf);
     }
   else
     {
       remove_category (m_mailitem, "GpgOL: ", false);
     }
 
   if (!m_decrypt_result.isNull())
     {
       const auto id = mngr->addCategoryToMail (this,
                                  CategoryManager::getEncMailCategory (),
                                  8 /* Blue */);
       if (m_store_id.empty())
         {
           m_store_id = id;
         }
       if (m_store_id != id)
         {
           log_error ("%s:%s unexpected store mismatch "
                      "between '%s' and dec cat '%s'",
                      SRCNAME, __func__, m_store_id.c_str(), id.c_str());
         }
     }
   else
     {
       /* As a small safeguard against fakes we remove our
          categories */
       remove_category (m_mailitem,
                        CategoryManager::getEncMailCategory ().c_str (),
                        true);
     }
 
   resize_active_window();
 
   TRETURN;
 }
 
 bool
 Mail::isSigned () const
 {
   TSTART;
   TRETURN m_verify_result.numSignatures() > 0;
 }
 
 bool
 Mail::isEncrypted () const
 {
   TSTART;
   TRETURN !m_decrypt_result.isNull();
 }
 
 int
 Mail::setUUID_o ()
 {
   TSTART;
   char *uuid;
   if (!m_uuid.empty())
     {
       /* This codepath is reached by decrypt again after a
          close with discard changes. The close discarded
          the uuid on the OOM object so we have to set
          it again. */
       log_debug ("%s:%s: Resetting uuid for %p to %s",
                  SRCNAME, __func__, this,
                  m_uuid.c_str());
       uuid = get_unique_id (m_mailitem, 1, m_uuid.c_str());
     }
   else
     {
       uuid = get_unique_id (m_mailitem, 1, nullptr);
       log_debug ("%s:%s: uuid for %p set to %s",
                  SRCNAME, __func__, this, uuid);
     }
 
   if (!uuid)
     {
       log_debug ("%s:%s: Failed to get/set uuid for %p",
                  SRCNAME, __func__, m_mailitem);
       TRETURN -1;
     }
   if (m_uuid.empty())
     {
       m_uuid = uuid;
       Mail *other = getMailForUUID (uuid);
       if (other)
         {
           /* According to documentation this should not
              happen as this means that multiple ItemLoad
              events occured for the same mailobject without
              unload / destruction of the mail.
 
              But it happens. If you invalidate the UI
              in the selection change event Outlook loads a
              new mailobject for the mail. Might happen in
              other surprising cases. We replace in that
              case as experiments have shown that the last
              mailobject is the one that is visible.
 
              Still troubling state so we log this as an error.
              */
           log_error ("%s:%s: There is another mail for %p "
                      "with uuid: %s replacing it.",
                      SRCNAME, __func__, m_mailitem, uuid);
           delete other;
         }
 
       gpgol_lock (&uid_map_lock);
       s_uid_map.insert (std::pair<std::string, Mail *> (m_uuid, this));
       gpgol_unlock (&uid_map_lock);
       log_debug ("%s:%s: uuid for %p is now %s",
                  SRCNAME, __func__, this,
                  m_uuid.c_str());
     }
   xfree (uuid);
   TRETURN 0;
 }
 
 /* TRETURNs 2 if the userid is ultimately trusted.
 
    TRETURNs 1 if the userid is fully trusted but has
    a signature by a key for which we have a secret
    and which is ultimately trusted. (Direct trust)
 
    0 otherwise */
 static int
 level_4_check (const UserID &uid)
 {
   TSTART;
   if (uid.isNull())
     {
       TRETURN 0;
     }
   if (uid.validity () == UserID::Validity::Ultimate)
     {
       TRETURN 2;
     }
   if (uid.validity () == UserID::Validity::Full)
     {
       const auto ultimate_keys = KeyCache::instance()->getUltimateKeys ();
       for (const auto sig: uid.signatures ())
         {
           const char *sigID = sig.signerKeyID ();
           if (sig.isNull() || !sigID)
             {
               /* should not happen */
               TRACEPOINT;
               continue;
             }
           /* Direct trust information is not available
              through gnupg so we cached the keys with ultimate
              trust during parsing and now see if we find a direct
              trust path.*/
           for (const auto secKey: ultimate_keys)
             {
               /* Check that the Key id of the key matches */
               const char *secKeyID = secKey.keyID ();
               if (!secKeyID || strcmp (secKeyID, sigID))
                 {
                   continue;
                 }
               /* Check that the userID of the signature is the ultimately
                  trusted one. */
               const char *sig_uid_str = sig.signerUserID();
               if (!sig_uid_str)
                 {
                   /* should not happen */
                   TRACEPOINT;
                   continue;
                 }
               for (const auto signer_uid: secKey.userIDs ())
                 {
                   if (signer_uid.validity() != UserID::Validity::Ultimate)
                     {
                       TRACEPOINT;
                       continue;
                     }
                   const char *signer_uid_str = signer_uid.id ();
                   if (!sig_uid_str)
                     {
                       /* should not happen */
                       TRACEPOINT;
                       continue;
                     }
                   if (!strcmp(sig_uid_str, signer_uid_str))
                     {
                       /* We have a match */
                       log_debug ("%s:%s: classified %s as ultimate because "
                                  "it was signed by uid %s of key %s",
                                  SRCNAME, __func__, anonstr (signer_uid_str),
                                  anonstr (sig_uid_str),
                                  anonstr (secKeyID));
                       TRETURN 1;
                     }
                 }
             }
         }
     }
   TRETURN 0;
 }
 
 std::string
 Mail::getCryptoSummary () const
 {
   TSTART;
   const int level = get_signature_level ();
 
   bool enc = isEncrypted ();
   if (level == 4 && enc)
     {
       TRETURN _("Security Level 4");
     }
   if (level == 4)
     {
       TRETURN _("Trust Level 4");
     }
   if (level == 3 && enc)
     {
       TRETURN _("Security Level 3");
     }
   if (level == 3)
     {
       TRETURN _("Trust Level 3");
     }
   if (level == 2 && enc)
     {
       TRETURN _("Security Level 2");
     }
   if (level == 2)
     {
       TRETURN _("Trust Level 2");
     }
   if (enc)
     {
       TRETURN _("Encrypted");
     }
   if (isSigned ())
     {
       /* Even if it is signed, if it is not validly
          signed it's still completly insecure as anyone
          could have signed this. So we avoid the label
          "signed" here as this word already implies some
          security. */
       TRETURN _("Insecure");
     }
   TRETURN _("Insecure");
 }
 
 std::string
 Mail::getCryptoOneLine () const
 {
   TSTART;
   bool sig = isSigned ();
   bool enc = isEncrypted ();
   if (sig || enc)
     {
       if (sig && enc)
         {
           TRETURN _("Signed and encrypted message");
         }
       else if (sig)
         {
           TRETURN _("Signed message");
         }
       else if (enc)
         {
           TRETURN _("Encrypted message");
         }
     }
   TRETURN _("Insecure message");
 }
 
 std::string
 Mail::getCryptoDetails_o ()
 {
   TSTART;
   std::string message;
 
   /* No signature with keys but error */
   if (!isEncrypted () && !isSigned () && m_verify_result.error())
     {
       message = _("You cannot be sure who sent, "
                   "modified and read the message in transit.");
       message += "\n\n";
       message += _("The message was signed but the verification failed with:");
       message += "\n";
       message += m_verify_result.error().asString();
       TRETURN message;
     }
   /* No crypo, what are we doing here? */
   if (!isEncrypted () && !isSigned ())
     {
       TRETURN _("You cannot be sure who sent, "
                "modified and read the message in transit.");
     }
   /* Handle encrypt only */
   if (isEncrypted () && !isSigned ())
     {
       if (in_de_vs_mode ())
        {
          if (m_sig.isDeVs())
            {
              message += _("The encryption was VS-NfD-compliant.");
            }
          else
            {
              message += _("The encryption was not VS-NfD-compliant.");
            }
         }
       message += "\n\n";
       message += _("You cannot be sure who sent the message because "
                    "it is not signed.");
       TRETURN message;
     }
 
   bool keyFound = true;
   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 ();
 
   log_debug ("%s:%s: Formatting sig. Validity: %x Summary: %x Level: %i",
              SRCNAME, __func__, m_sig.validity(), m_sig.summary(),
              level);
 
   if (level == 4)
     {
       /* level 4 check for direct trust */
       int four_check = level_4_check (m_uid);
 
       if (four_check == 2 && sigKey.hasSecret ())
         {
           message = _("You signed this message.");
         }
       else if (four_check == 1)
         {
           message = _("The senders identity was certified by yourself.");
         }
       else if (four_check == 2)
         {
           message = _("The sender is allowed to certify identities for you.");
         }
       else
         {
           log_error ("%s:%s:%i BUG: Invalid sigstate.",
                      SRCNAME, __func__, __LINE__);
           TRETURN message;
         }
     }
   else if (level == 3 && isOpenPGP)
     {
       /* Level three is only reachable through web of trust and no
          direct signature. */
       message = _("The senders identity was certified by several trusted people.");
     }
   else if (level == 3 && !isOpenPGP)
     {
       /* 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"),
                       sigKey.issuerName());
       memdbg_alloc (buf);
       message = buf;
       xfree (buf);
     }
   else if (level == 2 && m_uid.origin () == GpgME::Key::OriginWKD)
     {
       message = _("The mail provider of the recipient served this key.");
     }
   else if (level == 2 && m_uid.tofuInfo ().isNull ())
     {
       /* Marginal trust through pgp only */
       message = _("Some trusted people "
                   "have certified the senders identity.");
     }
   else if (level == 2)
     {
       unsigned long first_contact = std::max (m_uid.tofuInfo().signFirst(),
                                               m_uid.tofuInfo().encrFirst());
       char *time = format_date_from_gpgme (first_contact);
       /* i18n note signcount is always pulral because with signcount 1 we
        * would not be in this branch. */
       gpgrt_asprintf (&buf, _("The senders address is trusted, because "
                               "you have established a communication history "
                               "with this address starting on %s.\n"
                               "You encrypted %i and verified %i messages since."),
                               time, m_uid.tofuInfo().encrCount(),
                               m_uid.tofuInfo().signCount ());
       memdbg_alloc (buf);
       xfree (time);
       message = buf;
       xfree (buf);
     }
   else if (level == 1)
     {
       /* This could be marginal trust through pgp, or tofu with little
          history. */
       if (m_uid.tofuInfo ().signCount() == 1)
         {
           message += _("The senders signature was verified for the first time.");
         }
       else if (m_uid.tofuInfo ().validity() == TofuInfo::Validity::LittleHistory)
         {
           unsigned long first_contact = std::max (m_uid.tofuInfo().signFirst(),
                                                   m_uid.tofuInfo().encrFirst());
           char *time = format_date_from_gpgme (first_contact);
           gpgrt_asprintf (&buf, _("The senders address is not trustworthy yet because "
                                   "you only verified %i messages and encrypted %i messages to "
                                   "it since %s."),
                                   m_uid.tofuInfo().signCount (),
                                   m_uid.tofuInfo().encrCount (), time);
           memdbg_alloc (buf);
           xfree (time);
           message = buf;
           xfree (buf);
         }
     }
   else
     {
       /* Now we are in level 0, this could be a technical problem, no key
          or just unkown. */
       message = isEncrypted () ? _("But the sender address is not trustworthy because:") :
                                   _("The sender address is not trustworthy because:");
       message += "\n";
       keyFound = !(m_sig.summary() & Signature::Summary::KeyMissing);
 
       bool general_problem = true;
       /* First the general stuff. */
       if (m_sig.summary() & Signature::Summary::Red)
         {
             message += _("The signature is invalid: \n");
             if (m_sig.status().code() == GPG_ERR_BAD_SIGNATURE)
               {
                 message += std::string("\n") + _("The signature does not match.");
                 return message;
               }
         }
       else if (m_sig.summary() & Signature::Summary::SysError ||
                m_verify_result.numSignatures() < 1)
         {
           message += _("There was an error verifying the signature.\n");
           const auto err = m_sig.status ();
           if (err)
             {
               message += err.asString () + std::string ("\n");
             }
         }
       else if (m_sig.summary() & Signature::Summary::SigExpired)
         {
           message += _("The signature is expired.\n");
         }
       else
         {
           message += isOpenPGP ? _("The used key") : _("The used certificate");
           message += " ";
           general_problem = false;
         }
 
       /* Now the key problems */
       if ((m_sig.summary() & Signature::Summary::KeyMissing))
         {
           message += _("is not available.");
         }
       else if ((m_sig.summary() & Signature::Summary::KeyRevoked))
         {
           message += _("is revoked.");
         }
       else if ((m_sig.summary() & Signature::Summary::KeyExpired))
         {
           message += _("is expired.");
         }
       else if ((m_sig.summary() & Signature::Summary::BadPolicy))
         {
           message += _("is not meant for signing.");
         }
       else if ((m_sig.summary() & Signature::Summary::CrlMissing))
         {
           message += _("could not be checked for revocation.");
         }
       else if ((m_sig.summary() & Signature::Summary::CrlTooOld))
         {
           message += _("could not be checked for revocation.");
         }
       else if ((m_sig.summary() & Signature::Summary::TofuConflict) ||
                m_uid.tofuInfo().validity() == TofuInfo::Conflict)
         {
           message += _("is not the same as the key that was used "
                        "for this address in the past.");
           hasConflict = true;
         }
       else if (m_uid.isNull())
         {
           gpgrt_asprintf (&buf, _("does not claim the address: \"%s\"."),
                           getSender_o ().c_str());
           memdbg_alloc (buf);
           message += buf;
           xfree (buf);
         }
       else if (((m_sig.validity() & Signature::Validity::Undefined) ||
                (m_sig.validity() & Signature::Validity::Unknown) ||
                (m_sig.summary() == Signature::Summary::None) ||
                (m_sig.validity() == 0))&& !general_problem)
         {
            /* Bit of a catch all for weird results. */
           if (isOpenPGP)
             {
               message += _("is not certified by any trustworthy key.");
             }
           else
             {
               message += _("is not certified by a trustworthy Certificate Authority or the Certificate Authority is unknown.");
             }
         }
       else if (m_uid.isRevoked())
         {
           message += _("The sender marked this address as revoked.");
         }
       else if ((m_sig.validity() & Signature::Validity::Never))
         {
           message += _("is marked as not trustworthy.");
         }
     }
    message += "\n\n";
    if (in_de_vs_mode ())
      {
        if (isSigned ())
          {
            if (m_sig.isDeVs ())
              {
                message += _("The signature is VS-NfD-compliant.");
              }
            else
              {
                message += _("The signature is not VS-NfD-compliant.");
              }
            message += "\n";
          }
        if (isEncrypted ())
          {
            if (m_decrypt_result.isDeVs ())
              {
                message += _("The encryption is VS-NfD-compliant.");
              }
            else
              {
                message += _("The encryption is not VS-NfD-compliant.");
              }
            message += "\n\n";
          }
        else
          {
            message += "\n";
          }
      }
    if (hasConflict)
     {
       message += _("Click here to change the key used for this address.");
     }
   else if (keyFound)
     {
       message +=  isOpenPGP ? _("Click here for details about the key.") :
                               _("Click here for details about the certificate.");
     }
   else
     {
       message +=  isOpenPGP ? _("Click here to search the key on the configured keyserver.") :
                               _("Click here to search the certificate on the configured X509 keyserver.");
     }
   TRETURN message;
 }
 
 int
 Mail::get_signature_level () const
 {
   TSTART;
   if (!m_is_signed)
     {
       TRETURN 0;
     }
 
   if (m_uid.isNull ())
     {
       /* No m_uid matches our sender. */
       TRETURN 0;
     }
 
   if (m_is_valid && (m_uid.validity () == UserID::Validity::Ultimate ||
       (m_uid.validity () == UserID::Validity::Full &&
       level_4_check (m_uid))) && (!in_de_vs_mode () || m_sig.isDeVs()))
     {
       TRETURN 4;
     }
   if (m_is_valid && m_uid.validity () == UserID::Validity::Full &&
       (!in_de_vs_mode () || m_sig.isDeVs()))
     {
       TRETURN 3;
     }
   if (m_is_valid)
     {
       TRETURN 2;
     }
   if (m_sig.validity() == Signature::Validity::Marginal)
     {
       TRETURN 1;
     }
   if (m_sig.summary() & Signature::Summary::TofuConflict ||
       m_uid.tofuInfo().validity() == TofuInfo::Conflict)
     {
       TRETURN 0;
     }
   TRETURN 0;
 }
 
 int
 Mail::getCryptoIconID () const
 {
   TSTART;
   int level = get_signature_level ();
   int offset = isEncrypted () ? ENCRYPT_ICON_OFFSET : 0;
   TRETURN IDI_LEVEL_0 + level + offset;
 }
 
 const char*
 Mail::getSigFpr () const
 {
   TSTART;
   if (!m_is_signed || m_sig.isNull())
     {
       TRETURN nullptr;
     }
   TRETURN m_sig.fingerprint();
 }
 
 /** Try to locate the keys for all recipients */
 void
 Mail::locateKeys_o ()
 {
   TSTART;
   if (m_locate_in_progress)
     {
       /** XXX
         The strangest thing seems to happen here:
         In get_recipients the lookup for "AddressEntry" on
         an unresolved address might cause network traffic.
 
         So Outlook somehow "detaches" this call and keeps
         processing window messages while the call is running.
 
         So our do_delayed_locate might trigger a second locate.
         If we access the OOM in this call while we access the
         same object in the blocked "detached" call we crash.
         (T3931)
 
         After the window message is handled outlook retunrs
         in the original lookup.
 
         A better fix here might be a non recursive lock
         of the OOM. But I expect that if we lock the handling
         of the Windowmessage we might deadlock.
         */
       log_debug ("%s:%s: Locate for %p already in progress.",
                  SRCNAME, __func__, this);
       TRETURN;
     }
   m_locate_in_progress = true;
 
   Addressbook::check_o (this);
 
   if (opt.autoresolve)
     {
       // First update oom data to have recipients and sender updated.
       updateOOMData_o ();
       KeyCache::instance()->startLocateSecret (getSender_o ().c_str (), this);
       KeyCache::instance()->startLocate (getSender_o ().c_str (), this);
       KeyCache::instance()->startLocate (getCachedRecipients (), this);
     }
 
   autosecureCheck ();
 
   m_locate_in_progress = false;
   TRETURN;
 }
 
 bool
 Mail::isHTMLAlternative () const
 {
   TSTART;
   TRETURN m_is_html_alternative;
 }
 
 char *
 Mail::takeCachedHTMLBody ()
 {
   TSTART;
   char *ret = m_cached_html_body;
   m_cached_html_body = nullptr;
   TRETURN ret;
 }
 
 char *
 Mail::takeCachedPlainBody ()
 {
   TSTART;
   char *ret = m_cached_plain_body;
   m_cached_plain_body = nullptr;
   TRETURN ret;
 }
 
 int
 Mail::getCryptoFlags () const
 {
   TSTART;
   TRETURN m_crypto_flags;
 }
 
 void
 Mail::setNeedsEncrypt (bool value)
 {
   TSTART;
   m_needs_encrypt = value;
   TRETURN;
 }
 
 bool
 Mail::getNeedsEncrypt () const
 {
   TSTART;
   TRETURN m_needs_encrypt;
 }
 
 std::vector<std::string>
 Mail::getCachedRecipients ()
 {
   TSTART;
   TRETURN m_cached_recipients;
 }
 
 void
 Mail::appendToInlineBody (const std::string &data)
 {
   TSTART;
   m_inline_body += data;
   TRETURN;
 }
 
 int
 Mail::inlineBodyToBody_o ()
 {
   TSTART;
   if (!m_crypter)
     {
       log_error ("%s:%s: No crypter.",
                  SRCNAME, __func__);
       TRETURN -1;
     }
 
   const auto body = m_crypter->get_inline_data ();
   if (body.empty())
     {
       TRETURN 0;
     }
 
   /* For inline we always work with UTF-8 */
   put_oom_int (m_mailitem, "InternetCodepage", 65001);
 
   int ret = put_oom_string (m_mailitem, "Body",
                             body.c_str ());
   TRETURN ret;
 }
 
 void
 Mail::updateCryptMAPI_m ()
 {
   TSTART;
   log_debug ("%s:%s: Update crypt mapi",
              SRCNAME, __func__);
   if (m_crypt_state != NeedsUpdateInMAPI)
     {
       log_debug ("%s:%s: invalid state %i",
                  SRCNAME, __func__, m_crypt_state);
       TRETURN;
     }
   if (!m_crypter)
     {
       if (!m_mime_data.empty())
         {
           log_debug ("%s:%s: Have override mime data creating dummy crypter",
                      SRCNAME, __func__);
           m_crypter = std::shared_ptr <CryptController> (new CryptController (this, false,
                                                                               false,
                                                                               GpgME::UnknownProtocol));
         }
       else
         {
           log_error ("%s:%s: No crypter.",
                      SRCNAME, __func__);
           m_crypt_state = NoCryptMail;
           TRETURN;
         }
     }
 
   if (m_crypter->update_mail_mapi ())
     {
       log_error ("%s:%s: Failed to update MAPI after crypt",
                  SRCNAME, __func__);
       m_crypt_state = NoCryptMail;
     }
   else
     {
       m_crypt_state = WantsSendMIME;
     }
 
   /** If sync we need the crypter in update_crypt_oom */
   if (!isAsyncCryptDisabled ())
     {
       // We don't need the crypter anymore.
       resetCrypter ();
     }
   TRETURN;
 }
 
 /** Checks in OOM if the body is either
   empty or contains the -----BEGIN tag.
   pair.first -> true if body starts with -----BEGIN
   pair.second -> true if body is empty. */
 static std::pair<bool, bool>
 has_crypt_or_empty_body_oom (Mail *mail)
 {
   TSTART;
   auto body = mail->getBody_o ();
   std::pair<bool, bool> ret;
   ret.first = false;
   ret.second = false;
   ltrim (body);
   if (body.size() > 10 && !strncmp (body.c_str(), "-----BEGIN", 10))
     {
       ret.first = true;
       TRETURN ret;
     }
   if (!body.size())
     {
       ret.second = true;
     }
   else
     {
       log_data ("%s:%s: Body found in %p : \"%s\"",
                        SRCNAME, __func__, mail, body.c_str ());
     }
   TRETURN ret;
 }
 
 void
 Mail::updateCryptOOM_o ()
 {
   TSTART;
   log_debug ("%s:%s: Update crypt oom for %p",
              SRCNAME, __func__, this);
   if (m_crypt_state != NeedsUpdateInOOM)
     {
       log_debug ("%s:%s: invalid state %i",
                  SRCNAME, __func__, m_crypt_state);
       resetCrypter ();
       TRETURN;
     }
 
   if (getDoPGPInline ())
     {
       if (inlineBodyToBody_o ())
         {
           log_error ("%s:%s: Inline body to body failed %p.",
                      SRCNAME, __func__, this);
           gpgol_bug (get_active_hwnd(), ERR_INLINE_BODY_TO_BODY);
           m_crypt_state = NoCryptMail;
           TRETURN;
         }
     }
 
   if (m_crypter->get_protocol () == GpgME::CMS && m_crypter->is_encrypter ())
     {
       /* We put the PIDNameContentType headers here for exchange
          because this is the only way we found to inject the
          smime-type. */
       if (put_pa_string (m_mailitem,
                          PR_PIDNameContentType_DASL,
                          "application/pkcs7-mime;smime-type=\"enveloped-data\";name=smime.p7m"))
         {
           log_debug ("%s:%s: Failed to put PIDNameContentType for %p.",
                      SRCNAME, __func__, this);
         }
     }
 
   /** When doing async update_crypt_mapi follows and needs
     the crypter. */
   if (isAsyncCryptDisabled ())
     {
       resetCrypter ();
     }
 
   const auto pair = has_crypt_or_empty_body_oom (this);
   if (pair.first)
     {
       log_debug ("%s:%s: Looks like inline body. You can pass %p.",
                  SRCNAME, __func__, this);
       m_crypt_state = WantsSendInline;
       TRETURN;
     }
 
+  /* Draft encryption we do not want to wipe the oom but we have
+     to modify it to trigger the wirte / second after write. */
+  if (m_is_draft_encrypt)
+    {
+      char *subject = get_oom_string (m_mailitem, "Subject");
+      put_oom_string (m_mailitem, "Subject", subject ? subject : "");
+      xfree (subject);
+    }
+
   // We are in MIME land. Wipe the body.
-  if (wipe_o (true))
+  if (!m_is_draft_encrypt && wipe_o (true))
     {
       log_debug ("%s:%s: Cancel send for %p.",
                  SRCNAME, __func__, this);
       wchar_t *title = utf8_to_wchar (_("GpgOL: Encryption not possible!"));
       wchar_t *msg = utf8_to_wchar (_(
                                       "Outlook returned an error when trying to send the encrypted mail.\n\n"
                                       "Please restart Outlook and try again.\n\n"
                                       "If it still fails consider using an encrypted attachment or\n"
                                       "switching to PGP/Inline in GpgOL's options."));
       MessageBoxW (get_active_hwnd(), msg, title,
                    MB_ICONERROR | MB_OK);
       xfree (msg);
       xfree (title);
       m_crypt_state = NoCryptMail;
       TRETURN;
     }
   m_crypt_state = NeedsSecondAfterWrite;
   TRETURN;
 }
 
 void
 Mail::enableWindow ()
 {
   TSTART;
   if (!m_window)
     {
       log_error ("%s:%s:enable window which was not disabled",
                  SRCNAME, __func__);
     }
   log_debug ("%s:%s: enable window %p",
              SRCNAME, __func__, m_window);
 
   EnableWindow (m_window, TRUE);
   TRETURN;
 }
 
 void
 Mail::disableWindow_o ()
 {
   TSTART;
   m_window = get_active_hwnd ();
   log_debug ("%s:%s: disable window %p",
              SRCNAME, __func__, m_window);
 
   EnableWindow (m_window, FALSE);
   TRETURN;
 }
 
 bool
 Mail::check_inline_response ()
 {
   TSTART;
   /* Async sending is known to cause instabilities. So we keep
      a hidden option to disable it. */
   if (opt.sync_enc)
     {
       m_async_crypt_disabled = true;
       TRETURN m_async_crypt_disabled;
     }
 
   m_async_crypt_disabled = false;
 
   const auto subject = getSubject_o ();
 
   /* Check for an empty subject. Otherwise the question for it
      might be hidden behind our overlay. */
   if (subject.empty())
     {
       log_debug ("%s:%s: Detected empty subject. "
                  "Disabling async crypt due to T4150.",
                  SRCNAME, __func__);
       m_async_crypt_disabled = true;
       TRETURN m_async_crypt_disabled;
     }
 
   LPDISPATCH attachments = get_oom_object (m_mailitem, "Attachments");
   if (attachments)
     {
       /* This is horrible. But. For some kinds of attachments (we
          got reports about Office attachments the write in the
          send event triggered by our crypto done code fails with
          an exception. There does not appear to be a detectable
          pattern when this happens.
          As we can't be sure and do not know for which attachments
          this really happens we do not use async crypt for any
          mails with attachments. :-/
          Better be save (not crash) instead of nice (async).
 
          TODO: Figure this out.
 
          The log goes like this. We pass the send event. That triggers
          a write, which we pass. And then that fails. So it looks like
          moving to Outbox fails. Because we can save as much as we
          like before that.
 
          Using the IMessage::SubmitMessage MAPI interface works, but
          as it is unstable in our current implementation we do not
          want to use it.
 
          mailitem-events.cpp:Invoke: Passing send event for mime-encrypted message 12B7C6E0.
          application-events.cpp:Invoke: Unhandled Event: f002
          mailitem-events.cpp:Invoke: Write : 0ED4D058
          mailitem-events.cpp:Invoke: Passing write event.
          oomhelp.cpp:invoke_oom_method_with_parms_type: Method 'Send' invokation failed: 0x80020009
          oomhelp.cpp:dump_excepinfo: Exception:
          wCode: 0x1000
          wReserved: 0x0
          source: Microsoft Outlook
          desc: The operation failed.  The messaging interfaces have returned an unknown error. If the problem persists, restart Outlook.
          help: null
          helpCtx: 0x0
          deferredFill: 00000000
          scode: 0x80040119
       */
 
       int count = get_oom_int (attachments, "Count");
       gpgol_release (attachments);
 
       if (count)
         {
           m_async_crypt_disabled = true;
           log_debug ("%s:%s: Detected attachments. "
                      "Disabling async crypt due to T4131.",
                      SRCNAME, __func__);
           TRETURN m_async_crypt_disabled;
         }
    }
 
   LPDISPATCH app = GpgolAddin::get_instance ()->get_application ();
   if (!app)
     {
       TRACEPOINT;
       TRETURN false;
     }
 
   LPDISPATCH explorer = get_oom_object (app, "ActiveExplorer");
 
   if (!explorer)
     {
       TRACEPOINT;
       TRETURN false;
     }
 
   LPDISPATCH inlineResponse = get_oom_object (explorer, "ActiveInlineResponse");
   gpgol_release (explorer);
 
   if (!inlineResponse)
     {
       TRETURN false;
     }
 
   // We have inline response
   // Check if we are it. It's a bit naive but meh. Worst case
   // is that we think inline response too often and do sync
   // crypt where we could do async crypt.
   char * inlineSubject = get_oom_string (inlineResponse, "Subject");
   gpgol_release (inlineResponse);
 
   if (inlineResponse && !subject.empty() && !strcmp (subject.c_str (), inlineSubject))
     {
       log_debug ("%s:%s: Detected inline response for '%p'",
                  SRCNAME, __func__, this);
       m_async_crypt_disabled = true;
     }
   xfree (inlineSubject);
 
   TRETURN m_async_crypt_disabled;
 }
 
 // static
 Mail *
 Mail::getLastMail ()
 {
   TSTART;
   if (!s_last_mail || !isValidPtr (s_last_mail))
     {
       s_last_mail = nullptr;
     }
   TRETURN s_last_mail;
 }
 
 // static
 void
 Mail::clearLastMail ()
 {
   TSTART;
   s_last_mail = nullptr;
   TRETURN;
 }
 
 // static
 void
 Mail::locateAllCryptoRecipients_o ()
 {
   TSTART;
   gpgol_lock (&mail_map_lock);
   std::map<LPDISPATCH, Mail *>::iterator it;
   auto mail_map_copy = s_mail_map;
   gpgol_unlock (&mail_map_lock);
   for (it = mail_map_copy.begin(); it != mail_map_copy.end(); ++it)
     {
       if (it->second->needs_crypto_m ())
         {
           it->second->locateKeys_o ();
         }
     }
   TRETURN;
 }
 
 int
 Mail::removeAllAttachments_o ()
 {
   TSTART;
   int ret = 0;
   LPDISPATCH attachments = get_oom_object (m_mailitem, "Attachments");
   if (!attachments)
     {
       TRACEPOINT;
       TRETURN 0;
     }
   int count = get_oom_int (attachments, "Count");
   LPDISPATCH to_delete[count];
 
   /* Populate the array so that we don't get in an index mess */
   for (int i = 1; i <= count; i++)
     {
       auto item_str = std::string("Item(") + std::to_string (i) + ")";
       to_delete[i-1] = get_oom_object (attachments, item_str.c_str());
     }
   gpgol_release (attachments);
 
   /* Now delete all attachments */
   for (int i = 0; i < count; i++)
     {
       LPDISPATCH attachment = to_delete[i];
 
       if (!attachment)
         {
           log_error ("%s:%s: No such attachment %i",
                      SRCNAME, __func__, i);
           ret = -1;
         }
 
       /* Delete the attachments that are marked to delete */
       if (invoke_oom_method (attachment, "Delete", NULL))
         {
           log_error ("%s:%s: Deleting attachment %i",
                      SRCNAME, __func__, i);
           ret = -1;
         }
       gpgol_release (attachment);
     }
   TRETURN ret;
 }
 
 int
 Mail::removeOurAttachments_o ()
 {
   TSTART;
   LPDISPATCH attachments = get_oom_object (m_mailitem, "Attachments");
   if (!attachments)
     {
       TRACEPOINT;
       TRETURN 0;
     }
   int count = get_oom_int (attachments, "Count");
   LPDISPATCH to_delete[count];
   int del_cnt = 0;
   for (int i = 1; i <= count; i++)
     {
       auto item_str = std::string("Item(") + std::to_string (i) + ")";
       LPDISPATCH attachment = get_oom_object (attachments, item_str.c_str());
       if (!attachment)
         {
           TRACEPOINT;
           continue;
         }
 
       attachtype_t att_type;
       if (get_pa_int (attachment, GPGOL_ATTACHTYPE_DASL, (int*) &att_type))
         {
           /* Not our attachment. */
           gpgol_release (attachment);
           continue;
         }
 
       if (att_type == ATTACHTYPE_PGPBODY || att_type == ATTACHTYPE_MOSS ||
           att_type == ATTACHTYPE_MOSSTEMPL)
         {
           /* One of ours to delete. */
           to_delete[del_cnt++] = attachment;
           /* Dont' release yet */
           continue;
         }
       gpgol_release (attachment);
     }
   gpgol_release (attachments);
 
   int ret = 0;
 
   for (int i = 0; i < del_cnt; i++)
     {
       LPDISPATCH attachment = to_delete[i];
 
       /* Delete the attachments that are marked to delete */
       if (invoke_oom_method (attachment, "Delete", NULL))
         {
           log_error ("%s:%s: Error: deleting attachment %i",
                      SRCNAME, __func__, i);
           ret = -1;
         }
       gpgol_release (attachment);
     }
   TRETURN ret;
 }
 
 /* We are very verbose because if we fail it might mean
    that we have leaked plaintext -> critical. */
 bool
 Mail::hasCryptedOrEmptyBody_o ()
 {
   TSTART;
   const auto pair = has_crypt_or_empty_body_oom (this);
 
   if (pair.first /* encrypted marker */)
     {
       log_debug ("%s:%s: Crypt Marker detected in OOM body. Return true %p.",
                  SRCNAME, __func__, this);
       TRETURN true;
     }
 
   if (!pair.second)
     {
       log_debug ("%s:%s: Unexpected content detected. Return false %p.",
                  SRCNAME, __func__, this);
       TRETURN false;
     }
 
   // Pair second == true (is empty) can happen on OOM error.
   LPMESSAGE message = get_oom_base_message (m_mailitem);
   if (!message && pair.second)
     {
       if (message)
         {
           gpgol_release (message);
         }
       TRETURN true;
     }
 
   size_t r_nbytes = 0;
   char *mapi_body = mapi_get_body (message, &r_nbytes);
   gpgol_release (message);
 
   if (!mapi_body || !r_nbytes)
     {
       // Body or bytes are null. we are empty.
       xfree (mapi_body);
       log_debug ("%s:%s: MAPI error or empty message. Return true. %p.",
                  SRCNAME, __func__, this);
       TRETURN true;
     }
   if (r_nbytes > 10 && !strncmp (mapi_body, "-----BEGIN", 10))
     {
       // Body is crypt.
       log_debug ("%s:%s: MAPI Crypt marker detected. Return true. %p.",
                  SRCNAME, __func__, this);
       xfree (mapi_body);
       TRETURN true;
     }
 
   xfree (mapi_body);
 
   log_debug ("%s:%s: Found mapi body. Return false. %p.",
              SRCNAME, __func__, this);
 
   TRETURN false;
 }
 
 std::string
 Mail::getVerificationResultDump ()
 {
   TSTART;
   std::stringstream ss;
   ss << m_verify_result;
   TRETURN ss.str();
 }
 
 void
 Mail::setBlockStatus_m ()
 {
   TSTART;
   SPropValue prop;
 
   LPMESSAGE message = get_oom_base_message (m_mailitem);
 
   prop.ulPropTag = PR_BLOCK_STATUS;
   prop.Value.l = 1;
   HRESULT hr = message->SetProps (1, &prop, NULL);
 
   if (hr)
     {
       log_error ("%s:%s: can't set block value: hr=%#lx\n",
                  SRCNAME, __func__, hr);
     }
 
   gpgol_release (message);
   TRETURN;
 }
 
 void
 Mail::setBlockHTML (bool value)
 {
   TSTART;
   m_block_html = value;
   TRETURN;
 }
 
 void
 Mail::incrementLocateCount ()
 {
   TSTART;
   m_locate_count++;
   TRETURN;
 }
 
 void
 Mail::decrementLocateCount ()
 {
   TSTART;
   m_locate_count--;
 
   if (m_locate_count < 0)
     {
       log_error ("%s:%s: locate count mismatch.",
                  SRCNAME, __func__);
       m_locate_count = 0;
     }
   if (!m_locate_count)
     {
       autosecureCheck ();
     }
   TRETURN;
 }
 
 void
 Mail::autosecureCheck ()
 {
   TSTART;
   if (!opt.autosecure || !opt.autoresolve || m_manual_crypto_opts ||
       m_locate_count)
     {
       TRETURN;
     }
   bool ret = KeyCache::instance()->isMailResolvable (this);
 
   log_debug ("%s:%s: status %i",
              SRCNAME, __func__, ret);
 
   /* As we are safe to call at any time, because we need
    * to be triggered by the locator threads finishing we
    * need to actually set the draft info flags in
    * the ui thread. */
   do_in_ui_thread_async (ret ? DO_AUTO_SECURE : DONT_AUTO_SECURE,
                          this);
   TRETURN;
 }
 
 void
 Mail::setDoAutosecure_m (bool value)
 {
   TSTART;
   TRACEPOINT;
   LPMESSAGE msg = get_oom_base_message (m_mailitem);
 
   if (!msg)
     {
       TRACEPOINT;
       TRETURN;
     }
   /* We need to set a uuid so that autosecure can
      be disabled manually */
   setUUID_o ();
 
   int old_flags = get_gpgol_draft_info_flags (msg);
   if (old_flags && m_first_autosecure_check &&
       /* Someone with always sign and autosecure active
        * will want to get autoencryption. */
       !(old_flags == 2 && opt.sign_default))
     {
       /* They were set explicily before us. This can be
        * because they were a draft (which is bad) or
        * because they are a reply/forward to a crypto mail
        * or because there are conflicting settings. */
       log_debug ("%s:%s: Mail %p had already flags set.",
                  SRCNAME, __func__, m_mailitem);
       m_first_autosecure_check = false;
       m_manual_crypto_opts = true;
       gpgol_release (msg);
       TRETURN;
     }
   m_first_autosecure_check = false;
   set_gpgol_draft_info_flags (msg, value ? 3 : opt.sign_default ? 2 : 0);
   gpgol_release (msg);
   gpgoladdin_invalidate_ui();
   TRETURN;
 }
 
 void
 Mail::installFolderEventHandler_o()
 {
   TSTART;
   TRACEPOINT;
   LPDISPATCH folder = get_oom_object (m_mailitem, "Parent");
 
   if (!folder)
     {
       TRACEPOINT;
       TRETURN;
     }
 
   char *objName = get_object_name (folder);
   if (!objName || strcmp (objName, "MAPIFolder"))
     {
       log_debug ("%s:%s: Mail %p parent is not a mapi folder.",
                  SRCNAME, __func__, m_mailitem);
       xfree (objName);
       gpgol_release (folder);
       TRETURN;
     }
   xfree (objName);
 
   char *path = get_oom_string (folder, "FullFolderPath");
   if (!path)
     {
       TRACEPOINT;
       path = get_oom_string (folder, "FolderPath");
     }
   if (!path)
     {
       log_error ("%s:%s: Mail %p parent has no folder path.",
                  SRCNAME, __func__, m_mailitem);
       gpgol_release (folder);
       TRETURN;
     }
 
   std::string strPath (path);
   xfree (path);
 
   if (s_folder_events_map.find (strPath) == s_folder_events_map.end())
     {
       log_debug ("%s:%s: Install folder events watcher for %s.",
                  SRCNAME, __func__, anonstr (strPath.c_str()));
       const auto sink = install_FolderEvents_sink (folder);
       s_folder_events_map.insert (std::make_pair (strPath, sink));
     }
 
   /* Folder already registered */
   gpgol_release (folder);
   TRETURN;
 }
 
 void
 Mail::refCurrentItem()
 {
   TSTART;
   if (m_currentItemRef)
     {
       log_debug ("%s:%s: Current item multi ref. Bug?",
                  SRCNAME, __func__);
       TRETURN;
     }
   /* This prevents a crash in Outlook 2013 when sending a mail as it
    * would unload too early.
    *
    * As it didn't crash when the mail was opened in Outlook Spy this
    * mimics that the mail is inspected somewhere else. */
   m_currentItemRef = get_oom_object (m_mailitem, "GetInspector.CurrentItem");
   TRETURN;
 }
 
 void
 Mail::releaseCurrentItem()
 {
   TSTART;
   if (!m_currentItemRef)
     {
       TRETURN;
     }
   log_oom ("%s:%s: releasing CurrentItem ref %p",
                  SRCNAME, __func__, m_currentItemRef);
   LPDISPATCH tmp = m_currentItemRef;
   m_currentItemRef = nullptr;
   /* This can cause our destruction */
   gpgol_release (tmp);
   TRETURN;
 }
 
 void
 Mail::decryptPermanently_o()
 {
   TSTART;
   if (!m_needs_wipe)
     {
       log_debug ("%s:%s: Mail does not yet need wipe. Called to early?",
                  SRCNAME, __func__);
       TRETURN;
     }
 
   if (!m_decrypt_result.isNull() && m_decrypt_result.error())
     {
       log_debug ("%s:%s: Decrypt result had error. Can't decrypt permanently.",
                  SRCNAME, __func__);
       TRETURN;
     }
 
   /* Remove the existing categories */
   removeCategories_o ();
 
   /* Drop our state variables */
   m_decrypt_result = GpgME::DecryptionResult();
   m_verify_result = GpgME::VerificationResult();
   m_needs_wipe = false;
   m_processed = false;
   m_is_smime = false;
   m_type = MSGTYPE_UNKNOWN;
 
   /* Remove our own attachments */
   removeOurAttachments_o ();
 
   updateSigstate();
 
   auto msg = MAKE_SHARED (get_oom_base_message (m_mailitem));
   if (!msg)
     {
       STRANGEPOINT;
       TRETURN;
     }
   mapi_delete_gpgol_tags ((LPMESSAGE)msg.get());
 
   mapi_set_mesage_class ((LPMESSAGE)msg.get(), "IPM.Note");
 
   if (invoke_oom_method (m_mailitem, "Save", NULL))
     {
       log_error ("Failed to save decrypted mail: %p ", m_mailitem);
     }
 
   log_debug ("%s:%s: Delayed invalidate to update sigstate after perm dec.",
              SRCNAME, __func__);
   CloseHandle(CreateThread (NULL, 0, delayed_invalidate_ui, (LPVOID) 300, 0,
                             NULL));
   TRETURN;
 }
+
+void
+Mail::prepareCrypto_o ()
+{
+  TSTART;
+
+  if (!isAsyncCryptDisabled())
+    {
+      /* Obtain a reference of the current item. This prevents
+       * an early unload which would crash Outlook 2013
+       *
+       * As it didn't crash when the mail was opened in Outlook Spy this
+       * mimics that the mail is inspected somewhere else. */
+      refCurrentItem ();
+    }
+
+  // First contact with a mail to encrypt update
+  // state and oom data.
+  updateOOMData_o ();
+
+  setCryptState (Mail::NeedsFirstAfterWrite);
+
+  // Check inline response state before the write.
+  check_inline_response ();
+
+  TRETURN;
+}
diff --git a/src/mail.h b/src/mail.h
index 339bb5f..2b6a912 100644
--- a/src/mail.h
+++ b/src/mail.h
@@ -1,662 +1,669 @@
 /* @file mail.h
  * @brief High level class to work with Outlook Mailitems.
  *
  * Copyright (C) 2015, 2016 by Bundesamt für Sicherheit in der Informationstechnik
  * Software engineering by 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 MAIL_H
 #define MAIL_H
 
 #include "oomhelp.h"
 #include "mapihelp.h"
 #include "gpgme++/verificationresult.h"
 #include "gpgme++/decryptionresult.h"
 #include "gpgme++/key.h"
 
 #include <string>
 #include <future>
 
 class ParseController;
 class CryptController;
 
 /** @brief Data wrapper around a mailitem.
  *
  * This class is intended to bundle all that we know about
  * a Mail. Due to the restrictions in Outlook we sometimes may
  * need additional information that is not available at the time
  * like the sender address of an exchange account in the afterWrite
  * event.
  *
  * This class bundles such information and also provides a way to
  * access the event handler of a mail.
  *
  * Naming conventions of the suffixes:
  *  _o functions that work on OOM and possibly also MAPI.
  *  _m functions that work on MAPI.
  *  _s functions that only work on internal data and are safe to call
  *     from any thread.
  *
  * O and M functions _must_ only be called from the main thread. Use
  * a WindowMessage to signal the Main thread. But be wary. A WindowMessage
  * might be handled while an OOM call in the main thread waits for completion.
  *
  * An example for this is how update_oom_data can work:
  *
  * Main Thread:
  *   call update_oom_data
  *    └> internally invokes an OOM function that might do network access e.g.
  *       to connect to the exchange server to fetch the address.
  *
  *   Counterintutively the Main thread does not return from that function or
  *   blocks for it's completion but handles windowmessages.
  *
  *   After a windowmessage was handled and if the OOM invocation is
  *   completed the invocation returns and normal execution continues.
  *
  *   So if the window message handler's includes for example
  *   also a call to lookup recipients we crash. Note that it's usually
  *   safe to do OOM / MAPI calls from a window message.
  *
  *
  * While this seems impossible, remember that we do not work directly
  * with functions but everything is handled through COM. Without this
  * logic Outlook would probably become unusable because as any long running
  * call to the OOM would block it completely and freeze the UI.
  * (no windowmessages handled).
  *
  * So be wary when accessing the OOM from a Window Message.
  */
 class Mail
 {
 public:
   enum CryptState
     {
       NoCryptMail,
       NeedsFirstAfterWrite,
       NeedsActualCrypt,
       NeedsUpdateInOOM,
       NeedsSecondAfterWrite,
       NeedsUpdateInMAPI,
       WantsSendInline,
       WantsSendMIME,
     };
 
   /** @brief Construct a mail object for the item.
     *
     * This also installs the event sink for this item.
     *
     * The mail object takes ownership of the mailitem
     * reference. Do not Release it! */
   Mail (LPDISPATCH mailitem);
 
   ~Mail ();
 
   /** @brief looks for existing Mail objects for the OOM mailitem.
 
     @returns A reference to an existing mailitem or NULL in case none
     could be found.
   */
   static Mail* getMailForItem (LPDISPATCH mailitem);
 
   /** @brief looks for existing Mail objects in the uuid map.
     Only objects for which set_uid has been called can be found
     in the uid map. Get the Unique ID of a mailitem thorugh get_unique_id
 
     @returns A reference to an existing mailitem or NULL in case none
     could be found.
   */
   static Mail* getMailForUUID (const char *uuid);
 
   /** @brief Get the last created mail.
 
     @returns A reference to the last created mail or null.
   */
   static Mail* getLastMail ();
 
   /** @brief voids the last mail variable. */
   static void clearLastMail ();
 
   /** @brief Lock mail deletion.
 
     Mails are heavily accessed multi threaded. E.g. when locating
     keys. Due to bad timing it would be possible that between
     a check for "is_valid_ptr" to see if a map is still valid
     and the usage of the mail a delete would happen.
 
     This lock can be used to prevent that. Changes made to the
     mail will of course have no effect as the mail is already in
     the process of beeing unloaded. And calls that access MAPI
     or OOM still might crash. But this at least gurantees that
     the member variables of the mail exist while the lock is taken.
 
     Use it in your thread like:
 
       Mail::lockDelete ();
       Mail::isValidPtr (mail);
       mail->set_or_check_something ();
       Mail::unlockDelete ();
 
       Still be carefull when it is a mapi or oom function.
   */
   static void lockDelete ();
   static void unlockDelete ();
 
   /** @brief looks for existing Mail objects.
 
     @returns A reference to an existing mailitem or NULL in case none
     could be found. Can be used to check if a mail object was destroyed.
   */
   static bool isValidPtr (const Mail *mail);
 
   /** @brief wipe the plaintext from all known Mail objects.
     *
     * This is intended as a "cleanup" call to be done on unload
     * to avoid leaking plaintext in case we are deactivated while
     * some mails still have their plaintext inserted.
     *
     * @returns the number of errors that occured.
     */
   static int wipeAllMails_o ();
 
   /** @brief revert all known Mail objects.
     *
     * Similar to wipe but works on MAPI to revert our attachment
     * dance and restore an original MIME mail.
     *
     * @returns the number of errors that occured.
     */
   static int revertAllMails_o ();
 
   /** @brief close all known Mail objects.
     *
     * Close our mail with discard changes set to true.
     * This discards the plaintext / attachments. Afterwards
     * it calls save if neccessary to sync back the collected
     * property changes.
     *
     * This is the nicest of our three "Clean plaintext"
     * functions. Will fallback to revert if closing fails.
     * Closed mails are deleted.
     *
     * @returns the number of errors that occured.
     */
   static int closeAllMails_o ();
 
   /** @brief closes the inspector for a mail
     *
     * @returns true on success.
   */
   static int closeInspector_o (Mail *mail);
 
   /** Call close with discard changes to discard
       plaintext. returns the value of the oom close
       call. This may have delete the mail if the close
       triggers an unload.
   */
   static int close (Mail *mail);
 
   /** @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 locateAllCryptoRecipients_o ();
 
   /** @brief Reference to the mailitem. Do not Release! */
   LPDISPATCH item () { return m_mailitem; }
 
   /** @brief Pre process the message. Ususally to be called from BeforeRead.
    *
    * This function assumes that the base message interface can be accessed
    * and calles the MAPI Message handling which changes the message class
    * to enable our own handling.
    *
    * @returns 0 on success.
    */
   int preProcessMessage_m ();
 
   /** @brief Decrypt / Verify the mail.
    *
    * Sets the needs_wipe and was_encrypted variable.
    *
    * @returns 0 on success. */
   int decryptVerify_o ();
 
   /** @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 encryptSignStart_o ();
 
   /** @brief Necessary crypto operations were completed successfully. */
   bool wasCryptoSuccessful_m () { return m_crypt_successful || !needs_crypto_m (); }
 
   /** @brief Message should be encrypted and or signed.
     0: No
     1: Encrypt
     2: Sign
     3: Encrypt & Sign
   */
   int needs_crypto_m () const;
 
   /** @brief wipe the plaintext from the message and encrypt attachments.
    *
    * @returns 0 on success; */
   int wipe_o (bool force = false);
 
   /** @brief revert the message to the original mail before our changes.
    *
    * @returns 0 on success; */
   int revert_o ();
 
   /** @brief update some data collected from the oom
    *
    * This updates cached values from the OOM that are not available
    * in MAPI events like after Write.
    *
    * For Exchange 2013 at least we don't have any other way to get the
    * senders SMTP address then through the object model. So we have to
    * store the sender address for later events that do not allow us to
    * access the OOM but enable us to work with the underlying MAPI structure.
    *
    * It also updated the is_html_alternative value.
    *
    * @returns 0 on success */
   int updateOOMData_o ();
 
   /** @brief get sender SMTP address (UTF-8 encoded).
    *
    * If the sender address has not been set through update_sender this
    * calls update_sender before returning the sender.
    *
    * @returns A reference to the utf8 sender address. Or an empty string. */
   std::string getSender_o ();
 
   /** @brief get sender SMTP address (UTF-8 encoded).
    *
    * Like get_sender but ensures not to touch oom or mapi
    *
    * @returns A reference to the utf8 sender address. Or an empty string. */
   std::string getSender () const;
 
   /** @brief get the subject string (UTF-8 encoded).
     *
     * @returns the subject or an empty string. */
   std::string getSubject_o () const;
 
   /** @brief Is this a crypto mail handled by gpgol.
   *
   * Calling this is only valid after a message has been processed.
   *
   * @returns true if the mail was either signed or encrypted and we processed
   * it.
   */
   bool isCryptoMail () const;
 
   /** @brief This mail needs to be actually written.
   *
   * @returns true if the next write event should not be canceled.
   */
   bool needsSave () { return m_needs_save; }
 
   /** @brief set the needs save state.
   */
   void setNeedsSave (bool val) { m_needs_save = val; }
 
   /** @brief is this mail an S/MIME mail.
     *
     * @returns true for smime messages.
     */
   bool isSMIME_m ();
 
   /** @brief get the associated parser.
     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> cryper () { 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
    handler.
   */
   void parsing_done ();
 
   /** Returns true if the mail was verified and has at least one
     signature. Regardless of the validity of the mail */
   bool isSigned () const;
 
   /** Returns true if the mail is encrypted to at least one
     recipient. Regardless if it could be decrypted. */
   bool isEncrypted () const;
 
   /** Are we "green" */
   bool isValidSig () const;
 
   /** Get UID gets UniqueID property of this mail. Returns
     an empty string if the uid was not set with set uid.*/
   const std::string & getUUID () const { return m_uuid; }
 
   /** Returns 0 on success if the mail has a uid alrady or sets
     the uid. Setting only succeeds if the OOM is currently
     accessible. Returns -1 on error. */
   int setUUID_o ();
 
   /** Returns a localized string describing in one or two
     words the crypto status of this mail. */
   std::string getCryptoSummary () const;
 
   /** Returns a localized string describing the detailed
     crypto state of this mail. */
   std::string getCryptoDetails_o ();
 
   /** Returns a localized string describing a one line
     summary of the crypto state. */
   std::string getCryptoOneLine () const;
 
   /** Get the icon id of the appropiate icon for this mail */
   int getCryptoIconID () const;
 
   /** Get the fingerprint of an associated signature or null
       if it is not signed. */
   const char *getSigFpr () const;
 
   /** Remove all categories of this mail */
   void removeCategories_o ();
 
   /** Get the body of the mail */
   std::string getBody_o () const;
 
   /** Get the recipients. */
   std::vector<std::string> getRecipients_o () const;
 
   /** Try to locate the keys for all recipients.
       This also triggers the Addressbook integration, which we
       treat as locate jobs. */
   void locateKeys_o ();
 
   /** State variable to check if a close was triggerd by us. */
   void setCloseTriggered (bool value);
   bool getCloseTriggered () const;
 
   /** Check if the mail should be sent as html alternative mail.
     Only valid if update_oom_data was called before. */
   bool isHTMLAlternative () const;
 
   /** Get the html body. It is updated in update_oom_data.
       Caller takes ownership of the string and has to free it.
   */
   char *takeCachedHTMLBody ();
 
   /** Get the plain body. It is updated in update_oom_data.
       Caller takes ownership of the string and has to free it.
   */
   char *takeCachedPlainBody ();
 
   /** Get the cached recipients. It is updated in update_oom_data.*/
   std::vector<std::string> getCachedRecipients ();
 
   /** Returns 1 if the mail was encrypted, 2 if signed, 3 if both.
       Only valid after decrypt_verify.
   */
   int getCryptoFlags () const;
 
   /** Returns true if the mail should be encrypted in the
       after write event. */
   bool getNeedsEncrypt () const;
   void setNeedsEncrypt (bool val);
 
   /** Gets the level of the signature. See:
     https://wiki.gnupg.org/EasyGpg2016/AutomatedEncryption for
     a definition of the levels. */
   int get_signature_level () const;
 
   /** Check if all attachments are hidden and show a warning
     message appropiate to the crypto state if necessary. */
   int checkAttachments_o () const;
 
   /** Check if the mail should be encrypted "inline" */
   bool getDoPGPInline () const {return m_do_inline;}
 
   /** Check if the mail should be encrypted "inline" */
   void setDoPGPInline (bool value) {m_do_inline = value;}
 
   /** Append data to a cached inline body. Helper to do this
      on MAPI level and later add it through OOM */
   void appendToInlineBody (const std::string &data);
 
   /** Set the inline body as OOM body property. */
   int inlineBodyToBody_o ();
 
   /** Get the crypt state */
   CryptState cryptState () const {return m_crypt_state;}
 
   /** Set the crypt state */
   void setCryptState (CryptState state) {m_crypt_state = state;}
 
   /** Update MAPI data after encryption. */
   void updateCryptMAPI_m ();
 
   /** Update OOM data after encryption.
 
     Checks for plain text leaks and
     does not advance crypt state if body can't be cleaned.
   */
   void updateCryptOOM_o ();
 
   /** Enable / Disable the window of this mail.
 
     When the window gets disabled the
     handle is stored for a later enable. */
   void disableWindow_o ();
   void enableWindow ();
 
   /** Determine if the mail is an inline response.
 
     Call check_inline_response first to update the state
     from the OOM.
 
     We need synchronous encryption for inline responses. */
   bool isAsyncCryptDisabled () { return m_async_crypt_disabled; }
 
   /** Check through OOM if the current mail is an inline
     response.
 
     Caches the state which can then be queried through
     async_crypt_disabled
   */
   bool check_inline_response ();
 
   /** Get the window for the mail. Caution! This is only
     really valid in the time that the window is disabled.
     Use with care and can be null or invalid.
   */
   HWND getWindow () { return m_window; }
 
   /** Cleanup any attached crypter object. Useful
     on error. */
   void resetCrypter () { m_crypter = nullptr; }
 
   /** Set special crypto mime data that should be used as the
     mime structure when sending. */
   void setOverrideMIMEData (const std::string &data) {m_mime_data = data;}
 
   /** Get the mime data that should be used when sending. */
   std::string get_override_mime_data () const { return m_mime_data; }
   bool hasOverrideMimeData() const { return !m_mime_data.empty(); }
 
   /** Set if this is a forward of a crypto mail. */
   void setIsForwardedCryptoMail (bool value) { m_is_forwarded_crypto_mail = value; }
   bool is_forwarded_crypto_mail () { return m_is_forwarded_crypto_mail; }
 
   /** Set if this is a reply of a crypto mail. */
   void setIsReplyCryptoMail (bool value) { m_is_reply_crypto_mail = value; }
   bool is_reply_crypto_mail () { return m_is_reply_crypto_mail; }
 
   /** Remove the hidden GpgOL attachments. This is needed when forwarding
     without encryption so that our attachments are not included in the forward.
     Returns 0 on success. Works in OOM. */
   int removeOurAttachments_o ();
 
   /** Remove all attachments. Including our own. This is needed for
     forwarding of unsigned S/MIME mails (Efail).
     Returns 0 on success. Works in OOM. */
   int removeAllAttachments_o ();
 
   /** Check both OOM and MAPI if the body is either empty or
     encrypted. Won't abort on OOM or MAPI errors, so it can be
     used in both states. But will return false if a body
     was detected or in the OOM the MAPI Base Message. This
     is intended as a saveguard before sending a mail.
 
     This function should not be used to detected the necessity
     of encryption and is only an extra check to catch unexpected
     errors.
     */
   bool hasCryptedOrEmptyBody_o ();
 
   void updateBody_o ();
 
   /** Set if this mail looks like the send again of a crypto mail.
       This will mean that after it is decrypted it is treated
       like an unencrypted mail so that it can be encrypted again
       or sent unencrypted.
       */
   void setIsSendAgain (bool value) { m_is_send_again = value; }
 
 
   /* Attachment removal state variables. */
   bool attachmentRemoveWarningDisabled () { return m_disable_att_remove_warning; }
 
   /* Gets the string dump of the verification result. */
   std::string getVerificationResultDump ();
 
   /* Block loading HTML content */
   void setBlockHTML (bool value);
   bool isBlockHTML () const { return m_block_html; }
 
   /* Remove automatic loading of HTML references setting. */
   void setBlockStatus_m ();
 
   /* Crypto options (sign/encrypt) have been set manually. */
   void setCryptoSelectedManually (bool v) { m_manual_crypto_opts = v; }
   // bool is_crypto_selected_manually () const { return m_manual_crypto_opts; }
 
   /* Reference that a resolver thread is running for this mail. */
   void incrementLocateCount ();
 
   /* To be called when a resolver thread is done. If there are no running
      resolver threads we can check the recipients to see if we should
      toggle / untoggle the secure state.
      */
   void decrementLocateCount ();
 
   /* Check if the keys can be resolved automatically and trigger
    * setting the crypto flags accordingly.
    */
   void autosecureCheck ();
 
   /* Set if a mail should be secured (encrypted and signed)
    *
    * Only save to call from a place that may access mapi.
    */
   void setDoAutosecure_m (bool value);
 
   /* Install an event handler for the folder of this mail. */
   void installFolderEventHandler_o ();
 
   /* Marker for a "Move" of this mail */
   bool passWrite () { return m_pass_write; }
   void setPassWrite(bool value) { m_pass_write = value; }
 
   /* Releases the current item ref obtained in update oom data */
   void releaseCurrentItem ();
   /* Gets an additional reference for GetInspector.CurrentItem */
   void refCurrentItem ();
 
   /* Get the storeID for this mail */
   std::string storeID() { return m_store_id; }
 
   /* Remove encryption permanently. */
   void decryptPermanently_o ();
 
+  /* Prepare for encrypt / sign. Updates data. */
+  void prepareCrypto_o ();
+
+  /* State variable to check if we are about to encrypt a draft. */
+  void setIsDraftEncrypt (bool value) { m_is_draft_encrypt = value; }
+  bool isDraftEncrypt () { return m_is_draft_encrypt; }
 private:
   void updateCategories_o ();
   void updateSigstate ();
 
   LPDISPATCH m_mailitem;
   LPDISPATCH m_event_sink;
   LPDISPATCH m_currentItemRef;
   bool m_processed,    /* The message has been porcessed by us.  */
        m_needs_wipe,   /* We have added plaintext to the mesage. */
        m_needs_save,   /* A property was changed but not by us. */
        m_crypt_successful, /* We successfuly performed crypto on the item. */
        m_is_smime, /* This is an smime mail. */
        m_is_smime_checked, /* it was checked if this is an smime mail */
        m_is_signed, /* Mail is signed */
        m_is_valid, /* Mail is valid signed. */
        m_close_triggered, /* We have programtically triggered a close */
        m_is_html_alternative, /* Body Format is not plain text */
        m_needs_encrypt; /* Send was triggered we want to encrypt. */
   int m_moss_position; /* The number of the original message attachment. */
   int m_crypto_flags;
   std::string m_sender;
   std::string m_sent_on_behalf;
   char *m_cached_html_body; /* Cached html body. */
   char *m_cached_plain_body; /* Cached plain body. */
   std::vector<std::string> 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;
   GpgME::UserID m_uid;
   std::string m_uuid;
   std::string m_orig_body;
   bool m_do_inline;
   bool m_is_gsuite; /* Are we on a gsuite account */
   std::string m_inline_body;
   CryptState m_crypt_state;
   HWND m_window;
   bool m_async_crypt_disabled;
   std::string m_mime_data;
   bool m_is_forwarded_crypto_mail; /* Is this a forward of a crypto mail */
   bool m_is_reply_crypto_mail; /* Is this a reply to a crypto mail */
   bool m_is_send_again; /* Is this a send again of a crypto mail */
   bool m_disable_att_remove_warning; /* Should not warn about attachment removal. */
   bool m_block_html; /* Force blocking of html content. e.g for unsigned S/MIME mails. */
   bool m_manual_crypto_opts; /* Crypto options (sign/encrypt) have been set manually. */
   bool m_first_autosecure_check; /* This is the first autoresolve check */
   int m_locate_count; /* The number of key locates pending for this mail. */
   bool m_pass_write; /* Danger the next write will be passed. This is for closed mails */
   bool m_locate_in_progress; /* Simplified state variable for locate */
   std::string m_store_id; /* Store id for categories */
   std::string m_verify_category; /* The category string for the verify result */
   bool m_is_junk; /* Mail is in the junk folder */
+  bool m_is_draft_encrypt; /* Mail is a draft that should be encrypted */
 };
 #endif // MAIL_H
diff --git a/src/mailitem-events.cpp b/src/mailitem-events.cpp
index c8e7125..6cc5a80 100644
--- a/src/mailitem-events.cpp
+++ b/src/mailitem-events.cpp
@@ -1,941 +1,961 @@
 /* mailitem-events.h - Event handling for mails.
  * Copyright (C) 2015 by Bundesamt für Sicherheit in der Informationstechnik
  * Software engineering by 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 "eventsink.h"
 #include "eventsinks.h"
 #include "mymapi.h"
 #include "mymapitags.h"
 #include "oomhelp.h"
 #include "ocidl.h"
 #include "windowmessages.h"
 #include "mail.h"
 #include "mapihelp.h"
 #include "gpgoladdin.h"
 #include "wks-helper.h"
 
 #undef _
 #define _(a) utf8_gettext (a)
 
 const wchar_t *prop_blacklist[] = {
   L"Body",
   L"HTMLBody",
   L"To", /* Somehow this is done when a mail is opened */
   L"CC", /* Ditto */
   L"BCC", /* Ditto */
   L"Categories",
   L"UnRead",
   L"OutlookVersion",
   L"OutlookInternalVersion",
   L"ReceivedTime",
   L"InternetCodepage",
   L"ConversationIndex",
   L"Subject",
   NULL };
 
 typedef enum
   {
     AfterWrite = 0xFC8D,
     AttachmentAdd = 0xF00B,
     AttachmentRead = 0xF00C,
     AttachmentRemove = 0xFBAE,
     BeforeAttachmentAdd = 0xFBB0,
     BeforeAttachmentPreview = 0xFBAF,
     BeforeAttachmentRead = 0xFBAB,
     BeforeAttachmentSave = 0xF00D,
     BeforeAttachmentWriteToTempFile = 0xFBB2,
     BeforeAutoSave = 0xFC02,
     BeforeCheckNames = 0xF00A,
     BeforeDelete = 0xFA75,
     BeforeRead = 0xFC8C,
     Close = 0xF004,
     CustomAction = 0xF006,
     CustomPropertyChange = 0xF008,
     Forward = 0xF468,
     Open = 0xF003,
     PropertyChange = 0xF009,
     Read = 0xF001,
     ReadComplete = 0xFC8F,
     Reply = 0xF466,
     ReplyAll = 0xF467,
     Send = 0xF005,
     Unload = 0xFBAD,
     Write = 0xF002
   } MailEvent;
 
 /* Mail Item Events */
 BEGIN_EVENT_SINK(MailItemEvents, IDispatch)
 /* We are still in the class declaration */
 
 private:
   Mail * m_mail; /* The mail object related to this mailitem */
 };
 
 MailItemEvents::MailItemEvents() :
     m_object(NULL),
     m_pCP(NULL),
     m_cookie(0),
     m_ref(1),
     m_mail(NULL)
 {
 }
 
 MailItemEvents::~MailItemEvents()
 {
   if (m_pCP)
     m_pCP->Unadvise(m_cookie);
   if (m_object)
     gpgol_release (m_object);
 }
 
 static bool propchangeWarnShown = false;
 static bool attachRemoveWarnShown = false;
 static bool addinsLogged = false;
 
 static DWORD WINAPI
 do_delayed_locate (LPVOID arg)
 {
   Sleep(100);
   do_in_ui_thread (RECIPIENT_ADDED, arg);
   return 0;
 }
 
 /* The main Invoke function. The return value of this
    function does not appear to have any effect on outlook
    although I have read in an example somewhere that you
    should return S_OK so that outlook continues to handle
    the event I have not yet seen any effect by returning
    error values here and no MSDN documentation about the
    return values.
 */
 EVENT_SINK_INVOKE(MailItemEvents)
 {
   USE_INVOKE_ARGS
   TSTART;
   if (!m_mail)
     {
       m_mail = Mail::getMailForItem (m_object);
       if (!m_mail)
         {
           log_error ("%s:%s: mail event without mail object known. Bug.",
                      SRCNAME, __func__);
           TRETURN S_OK;
         }
     }
 
   bool is_reply = false;
   switch(dispid)
     {
+      case BeforeAutoSave:
+        {
+          log_oom ("%s:%s: BeforeAutoSave : %p",
+                   SRCNAME, __func__, m_mail);
+          if (opt.draft_key && (m_mail->needs_crypto_m () & 1) &&
+              !m_mail->isDraftEncrypt())
+            {
+              log_debug ("%s:%s: Draft encryption for autosave starting now.",
+                         SRCNAME, __func__);
+              m_mail->setIsDraftEncrypt (true);
+              m_mail->prepareCrypto_o ();
+            }
+          TRETURN S_OK;
+        }
       case Open:
         {
           log_oom ("%s:%s: Open : %p",
                          SRCNAME, __func__, m_mail);
           int draft_flags = 0;
           if (!opt.encrypt_default && !opt.sign_default)
             {
               TRETURN S_OK;
             }
           LPMESSAGE message = get_oom_base_message (m_object);
           if (!message)
             {
               log_error ("%s:%s: Failed to get message.",
                          SRCNAME, __func__);
               TBREAK;
             }
           if (opt.encrypt_default)
             {
               draft_flags = 1;
             }
           if (opt.sign_default)
             {
               draft_flags += 2;
             }
           set_gpgol_draft_info_flags (message, draft_flags);
           gpgol_release (message);
           TBREAK;
         }
       case BeforeRead:
         {
           log_oom ("%s:%s: BeforeRead : %p",
                          SRCNAME, __func__, m_mail);
           if (GpgolAddin::get_instance ()->isShutdown())
             {
               log_debug ("%s:%s: Ignoring read after shutdown.",
                          SRCNAME, __func__);
               TBREAK;
             }
 
           if (m_mail->preProcessMessage_m ())
             {
               log_error ("%s:%s: Pre process message failed.",
                          SRCNAME, __func__);
             }
           TBREAK;
         }
       case Read:
         {
           log_oom ("%s:%s: Read : %p",
                          SRCNAME, __func__, m_mail);
           if (!addinsLogged)
             {
               // We do it here as this nearly always comes and we want to remove
               // as much as possible from the startup time.
               log_addins ();
               addinsLogged = true;
             }
           if (!m_mail->isCryptoMail ())
             {
               log_debug ("%s:%s: Non crypto mail %p opened. Updating sigstatus.",
                          SRCNAME, __func__, m_mail);
               /* Ensure that no wrong sigstatus is shown */
               CloseHandle(CreateThread (NULL, 0, delayed_invalidate_ui, (LPVOID) 300, 0,
                                         NULL));
               TBREAK;
             }
           if (m_mail->setUUID_o ())
             {
               log_debug ("%s:%s: Failed to set uuid.",
                          SRCNAME, __func__);
               delete m_mail; /* deletes this, too */
               TRETURN S_OK;
             }
           if (m_mail->decryptVerify_o ())
             {
               log_error ("%s:%s: Decrypt message failed.",
                          SRCNAME, __func__);
             }
           if (!opt.enable_smime && m_mail->isSMIME_m ())
             {
               /* We want to save the mail when it's an smime mail and smime
                  is disabled to revert it. */
               log_debug ("%s:%s: S/MIME mail but S/MIME is disabled."
                          " Need save.",
                          SRCNAME, __func__);
               m_mail->setNeedsSave (true);
             }
           TBREAK;
         }
       case PropertyChange:
         {
           if (!parms || parms->cArgs != 1 ||
               parms->rgvarg[0].vt != VT_BSTR ||
               !parms->rgvarg[0].bstrVal)
             {
               log_error ("%s:%s: Unexpected params.",
                          SRCNAME, __func__);
               TBREAK;
             }
           const wchar_t *prop_name = parms->rgvarg[0].bstrVal;
           if (!m_mail->isCryptoMail ())
             {
               if (m_mail->hasOverrideMimeData())
                 {
                   /* This is a mail created by us. Ignore propchanges. */
                   TBREAK;
                 }
               if (!wcscmp (prop_name, L"To") /* ||
                   !wcscmp (prop_name, L"BCC") ||
                   !wcscmp (prop_name, L"CC")
                   Testing shows that Outlook always sends these three in a row
                   */)
                 {
                   if (opt.autosecure || (m_mail->needs_crypto_m () & 1))
                     {
                       /* XXX Racy race. This is a fix for crashes
                          that happend if a resolved recipient is copied an pasted.
                          If we then access the recipients object in the Property
                          Change event we crash. Thus we do the delay dance. */
                       HANDLE thread = CreateThread (NULL, 0, do_delayed_locate,
                                                     (LPVOID) m_mail, 0,
                                                     NULL);
                       CloseHandle(thread);
                     }
                 }
               TBREAK;
             }
           for (const wchar_t **cur = prop_blacklist; *cur; cur++)
             {
               if (!wcscmp (prop_name, *cur))
                 {
                   log_oom ("%s:%s: Message %p propchange: %ls discarded.",
                            SRCNAME, __func__, m_object, prop_name);
                   TRETURN S_OK;
                 }
             }
           log_oom ("%s:%s: Message %p propchange: %ls.",
                    SRCNAME, __func__, m_object, prop_name);
 
           if (!wcscmp (prop_name, L"SendUsingAccount"))
             {
               bool sent = get_oom_bool (m_object, "Sent");
               if (sent)
                 {
                   log_debug ("%s:%s: Ignoring SendUsingAccount change for sent %p ",
                              SRCNAME, __func__, m_object);
                   TRETURN S_OK;
                 }
               log_debug ("%s:%s: Message %p looks like send again.",
                         SRCNAME, __func__, m_object);
               m_mail->setIsSendAgain (true);
               TRETURN S_OK;
             }
 
           /* We have tried several scenarios to handle propery changes.
              Only save the property in MAPI and call MAPI SaveChanges
              worked and did not leak plaintext but this caused outlook
              still to break the attachments of PGP/MIME Mails into two
              attachments and add them as winmail.dat so other clients
              are broken.
 
              Alternatively reverting the mail, saving the property and
              then decrypt again also worked a bit but there were some
              weird side effects and breakages. But this has the usual
              problem of a revert that the mail is created by outlook and
              e.g. multipart/signed signatures from most MUA's are broken.
 
              Some things to try out might be the close approach and then
              another open or a selection change. But for now we just warn.
 
              As a workardound a user should make property changes when
              the mail was not read by us. */
           if (propchangeWarnShown)
             {
               TRETURN S_OK;
             }
 
           wchar_t *title = utf8_to_wchar (_("Sorry, that's not possible, yet"));
           char *fmt;
           gpgrt_asprintf (&fmt, _("GpgOL has prevented the change to the \"%s\" property.\n"
                                   "Property changes are not yet handled for crypto messages.\n\n"
                                   "To workaround this limitation please change the property when the "
                                   "message is not open in any window and not selected in the "
                                   "messagelist.\n\nFor example by right clicking but not selecting the message.\n"),
                           wchar_to_utf8(prop_name));
           memdbg_alloc (fmt);
           wchar_t *msg = utf8_to_wchar (fmt);
           xfree (fmt);
           MessageBoxW (get_active_hwnd(), msg, title,
                        MB_ICONINFORMATION | MB_OK);
           xfree (msg);
           xfree (title);
           propchangeWarnShown = true;
           TRETURN S_OK;
         }
       case CustomPropertyChange:
         {
           log_oom ("%s:%s: CustomPropertyChange : %p",
                          SRCNAME, __func__, m_mail);
           /* TODO */
           TBREAK;
         }
       case Send:
         {
           /* This is the only event where we can cancel the send of a
              mailitem. But it is too early for us to encrypt as the MAPI
              structures are not yet filled. Crypto based on the
              Outlook Object Model data did not work as the messages
              were only sent out empty. See 2b376a48 for a try of
              this.
 
              This is why we store send_seen and invoke a save which
              may result in an error but only after triggering all the
              behavior we need -> filling mapi structures and invoking the
              AfterWrite handler where we encrypt.
 
              If this encryption is successful and we pass the send
              as then the encrypted data is sent.
            */
           log_oom ("%s:%s: Send : %p",
                          SRCNAME, __func__, m_mail);
           if (!m_mail->needs_crypto_m () && m_mail->cryptState () == Mail::NoCryptMail)
             {
              log_debug ("%s:%s: No crypto neccessary. Passing send for %p obj %p",
                         SRCNAME, __func__, m_mail, m_object);
              TBREAK;
             }
 
           if (parms->cArgs != 1 || parms->rgvarg[0].vt != (VT_BOOL | VT_BYREF))
            {
              log_debug ("%s:%s: Uncancellable send event.",
                         SRCNAME, __func__);
              TBREAK;
            }
 
           if (m_mail->cryptState () == Mail::NoCryptMail &&
               m_mail->needs_crypto_m ())
             {
               log_debug ("%s:%s: Send event for crypto mail %p saving and starting.",
                          SRCNAME, __func__, m_mail);
 
-              if (!m_mail->isAsyncCryptDisabled())
-                {
-                  /* Obtain a reference of the current item. This prevents
-                   * an early unload which would crash Outlook 2013
-                   *
-                   * As it didn't crash when the mail was opened in Outlook Spy this
-                   * mimics that the mail is inspected somewhere else. */
-                  m_mail->refCurrentItem ();
-                }
-
-              // First contact with a mail to encrypt update
-              // state and oom data.
-              m_mail->updateOOMData_o ();
+              m_mail->prepareCrypto_o ();
 
-              m_mail->setCryptState (Mail::NeedsFirstAfterWrite);
-
-              // Check inline response state before the write.
-              m_mail->check_inline_response ();
               // Save the Mail
               invoke_oom_method (m_object, "Save", NULL);
 
               if (!m_mail->isAsyncCryptDisabled ())
                 {
                   // The afterwrite in the save should have triggered
                   // the encryption. We cancel send for our asyncness.
                   // Cancel send
                   *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE;
                   TBREAK;
                 }
               else
                 {
                   if (m_mail->cryptState () == Mail::NoCryptMail)
                     {
                       // Crypto failed or was canceled
                       log_debug ("%s:%s: Message %p mail %p cancelling send - "
                                  "Crypto failed or canceled.",
                                  SRCNAME, __func__, m_object, m_mail);
                       *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE;
                       TBREAK;
                     }
                   // For inline response we can't trigger send programatically
                   // so we do the encryption in sync.
                   if (m_mail->cryptState () == Mail::NeedsUpdateInOOM)
                     {
                       m_mail->updateCryptOOM_o ();
                     }
                   if (m_mail->cryptState () == Mail::NeedsSecondAfterWrite)
                     {
                       m_mail->setCryptState (Mail::WantsSendMIME);
                     }
                   if (m_mail->getDoPGPInline () && m_mail->cryptState () != Mail::WantsSendInline)
                     {
                       log_debug ("%s:%s: Message %p mail %p cancelling send - "
                                  "Invalid state.",
                                  SRCNAME, __func__, m_object, m_mail);
                       gpgol_bug (m_mail->getWindow (),
                                  ERR_INLINE_BODY_INV_STATE);
                       *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE;
                       TBREAK;
                     }
                 }
             }
 
           if (m_mail->cryptState () == Mail::WantsSendInline)
             {
               if (!m_mail->hasCryptedOrEmptyBody_o ())
                 {
                   log_debug ("%s:%s: Message %p mail %p cancelling send - "
                              "not encrypted or not empty body detected.",
                              SRCNAME, __func__, m_object, m_mail);
                   gpgol_bug (m_mail->getWindow (),
                              ERR_WANTS_SEND_INLINE_BODY);
                   m_mail->setCryptState (Mail::NoCryptMail);
                   *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE;
                   TBREAK;
                 }
               log_debug ("%s:%s: Passing send event for no-mime message %p.",
                          SRCNAME, __func__, m_object);
               WKSHelper::instance()->allow_notify (1000);
               TBREAK;
             }
 
           if (m_mail->cryptState () == Mail::WantsSendMIME)
             {
               if (!m_mail->hasCryptedOrEmptyBody_o ())
                 {
 /* The safety checks here trigger too often. Somehow for some
    users the body is not empty after the encryption but when
    it is sent it is still sent with the crypto content because
    the encrypted MIME Structure is used because it is
    correct in MAPI land.
 
    For safety reasons enabling the checks might be better but
    until we figure out why for some users the body replacement
    does not work we have to disable them. Otherwise GpgOL
    is unusuable for such users. GnuPG-Bug-Id: T3875
 */
 #define DISABLE_SAFTEY_CHECKS
 #ifndef DISABLE_SAFTEY_CHECKS
                   gpgol_bug (m_mail->getWindow (),
                              ERR_WANTS_SEND_MIME_BODY);
                   log_debug ("%s:%s: Message %p mail %p cancelling send mime - "
                              "not encrypted or not empty body detected.",
                              SRCNAME, __func__, m_object, m_mail);
                   m_mail->setCryptState (Mail::NoCryptMail);
                   *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE;
                   TBREAK;
 #else
                   log_debug ("%s:%s: Message %p mail %p - "
                              "not encrypted or not empty body detected - MIME.",
                              SRCNAME, __func__, m_object, m_mail);
 #endif
                 }
               /* Now we adress T3656 if Outlooks internal S/MIME is somehow
                * mixed in (even if it is enabled and then disabled) it might
                * cause strange behavior in that it sends the plain message
                * and not the encrypted message. Tests have shown that we can
                * bypass that by calling submit message on our base
                * message.
                *
                * We do this conditionally as our other way of using OOM
                * to send is proven to work and we don't want to mess
                * with it.
                */
               // Get the Message class.
               HRESULT hr;
               LPSPropValue propval = NULL;
 
               // It's important we use the _not_ base message here.
               LPMESSAGE message = get_oom_message (m_object);
               hr = HrGetOneProp ((LPMAPIPROP)message, PR_MESSAGE_CLASS_A, &propval);
               gpgol_release (message);
               if (FAILED (hr))
                 {
                   log_error ("%s:%s: HrGetOneProp() failed: hr=%#lx\n",
                              SRCNAME, __func__, hr);
                   gpgol_release (message);
                   TBREAK;
                 }
               if (propval->Value.lpszA && !strstr (propval->Value.lpszA, "GpgOL"))
                 {
                   // Does not have a message class by us.
                   log_debug ("%s:%s: Message %p - No GpgOL Message class after encryption. cls is: '%s'",
                              SRCNAME, __func__, m_object, propval->Value.lpszA);
                   log_debug ("%s:%s: Message %p - Activating T3656 Workaround",
                              SRCNAME, __func__, m_object);
                   message = get_oom_base_message (m_object);
                   if (message)
                     {
                       // It's important we use the _base_ message here.
                       mapi_save_changes (message, FORCE_SAVE);
                       message->SubmitMessage(0);
                       gpgol_release (message);
                       // Close the composer and trigger unloads
                       CloseHandle(CreateThread (NULL, 0, close_mail, (LPVOID) m_mail, 0,
                                                 NULL));
                     }
                   else
                     {
                       gpgol_bug (nullptr,
                                  ERR_GET_BASE_MSG_FAILED);
                     }
                   // Cancel send
                   *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE;
                 }
               MAPIFreeBuffer (propval);
               if (*(parms->rgvarg[0].pboolVal) == VARIANT_TRUE)
                 {
                   TBREAK;
                 }
               log_debug ("%s:%s: Passing send event for mime-encrypted message %p.",
                          SRCNAME, __func__, m_object);
               WKSHelper::instance()->allow_notify (1000);
               TBREAK;
             }
           else
             {
               log_debug ("%s:%s: Message %p cancelling send - "
                          "crypto or second save failed.",
                          SRCNAME, __func__, m_object);
               *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE;
             }
           TRETURN S_OK;
         }
       case Write:
         {
           log_oom ("%s:%s: Write : %p",
                          SRCNAME, __func__, m_mail);
           /* This is a bit strange. We sometimes get multiple write events
              without a read in between. When we access the message in
              the second event it fails and if we cancel the event outlook
              crashes. So we have keep the m_needs_wipe state variable
              to keep track of that. */
           if (parms->cArgs != 1 || parms->rgvarg[0].vt != (VT_BOOL | VT_BYREF))
            {
              /* This happens in the weird case */
              log_debug ("%s:%s: Uncancellable write event.",
                         SRCNAME, __func__);
              TBREAK;
            }
 
           if (m_mail->passWrite())
             {
               log_debug ("%s:%s: Passing write because passNextWrite was set for %p",
                          SRCNAME, __func__, m_mail);
               TBREAK;
             }
 
           if (m_mail->isCryptoMail () && !m_mail->needsSave ())
             {
               Mail *last_mail = Mail::getLastMail ();
               if (Mail::isValidPtr (last_mail))
                 {
                   /* We want to identify here if there was a mail created that
                      should receive the contents of this mail. For this we check
                      for a write in the same loop as a mail creation.
                      Now when switching from one mail to another this is also what
                      happens. The new mail is loaded and the old mail is written.
                      To distinguish the two we check that the new mail does not have
                      an entryID, a Subject and No Size. Maybe just size or entryID
                      would be enough but better save then sorry.
 
                      Security consideration: Worst case we pass the write here but
                      an unload follows before we get the scheduled revert. This
                      would leak plaintext. But does not happen in our tests.
 
                      Similarly if we crash or Outlook is closed before we see this
                      revert. But as we immediately revert after the write this should
                      also not happen. */
                   const std::string lastSubject = last_mail->getSubject_o ();
                   char *lastEntryID = get_oom_string (last_mail->item (), "EntryID");
                   int lastSize = get_oom_int (last_mail->item (), "Size");
                   std::string lastEntryStr;
                   if (lastEntryID)
                     {
                       lastEntryStr = lastEntryID;
                       xfree (lastEntryID);
                     }
 
                   if (!lastSize && !lastEntryStr.size () && !lastSubject.size ())
                     {
                       log_debug ("%s:%s: Write in the same loop as empty load."
                                  " Pass but schedule revert.",
                                  SRCNAME, __func__);
 
                       /* This might be a forward. So don't invalidate yet. */
 
                       // Mail::clearLastMail ();
 
                       do_in_ui_thread_async (REVERT_MAIL, m_mail);
                       TRETURN S_OK;
                     }
                 }
               /* We cancel the write event to stop outlook from excessively
                  syncing our changes.
                  if smime support is disabled and we still have an smime
                  mail we also don't want to cancel the write event
                  to enable reverting this mails.
                  */
               *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE;
               log_debug ("%s:%s: Canceling write event.",
                          SRCNAME, __func__);
               TRETURN S_OK;
             }
 
           if (m_mail->isCryptoMail () && m_mail->needsSave () &&
               m_mail->revert_o ())
             {
               /* An error cleaning the mail should not happen normally.
                  But just in case there is an error we cancel the
                  write here. */
               log_debug ("%s:%s: Failed to remove plaintext.",
                          SRCNAME, __func__);
               *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE;
             }
 
           if (!m_mail->isCryptoMail () && m_mail->is_forwarded_crypto_mail () &&
               !m_mail->needs_crypto_m () && m_mail->cryptState () == Mail::NoCryptMail)
             {
               /* We are sure now that while this is a forward of an encrypted
                * mail that the forward should not be signed or encrypted. So
                * it's not constructed by us. We need to remove our attachments
                * though so that they are not included in the forward. */
               log_debug ("%s:%s: Writing unencrypted forward of crypt mail. "
                          "Removing attachments. mail: %p item: %p",
                          SRCNAME, __func__, m_mail, m_object);
               if (m_mail->removeOurAttachments_o ())
                 {
                   // Worst case we forward some encrypted data here not
                   // a security problem, so let it pass.
                   log_error ("%s:%s: Failed to remove our attachments.",
                              SRCNAME, __func__);
                 }
               /* Remove marker because we did this now. */
               m_mail->setIsForwardedCryptoMail (false);
             }
 
-          log_debug ("%s:%s: Passing write event.",
-                     SRCNAME, __func__);
+          if (m_mail->isDraftEncrypt () &&
+              m_mail->cryptState () != Mail::NeedsFirstAfterWrite &&
+              m_mail->cryptState () != Mail::NeedsSecondAfterWrite)
+            {
+              log_debug ("%s:%s: Canceling write because draft encrypt is on"
+                         " progress.",
+                         SRCNAME, __func__);
+              *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE;
+              TRETURN S_OK;
+            }
+
+          if (opt.draft_key && (m_mail->needs_crypto_m () & 1) &&
+              !m_mail->isDraftEncrypt())
+            {
+              log_debug ("%s:%s: Draft encryption starting now.",
+                         SRCNAME, __func__);
+              m_mail->setIsDraftEncrypt (true);
+              m_mail->prepareCrypto_o ();
+            }
+
+          log_debug ("%s:%s: Passing write event. %i %i",
+                     SRCNAME, __func__, m_mail->isDraftEncrypt(), m_mail->cryptState());
           m_mail->setNeedsSave (false);
           TBREAK;
         }
       case AfterWrite:
         {
           log_oom ("%s:%s: AfterWrite : %p",
                          SRCNAME, __func__, m_mail);
           if (m_mail->cryptState () == Mail::NeedsFirstAfterWrite)
             {
               /* Seen the first after write. Advance the state */
               m_mail->setCryptState (Mail::NeedsActualCrypt);
               if (m_mail->encryptSignStart_o ())
                 {
                   log_debug ("%s:%s: Encrypt sign start failed.",
                              SRCNAME, __func__);
                   m_mail->setCryptState (Mail::NoCryptMail);
                 }
               TRETURN S_OK;
             }
           if (m_mail->cryptState () == Mail::NeedsSecondAfterWrite)
             {
               m_mail->setCryptState (Mail::NeedsUpdateInMAPI);
               m_mail->updateCryptMAPI_m ();
+              log_debug ("%s:%s: Second after write done.",
+                         SRCNAME, __func__);
               TRETURN S_OK;
             }
           TBREAK;
         }
       case Close:
         {
           log_oom ("%s:%s: Close : %p",
                          SRCNAME, __func__, m_mail);
           if (m_mail->isCryptoMail ())
             {
               /* Close. This happens when an Opened mail is closed.
                  To prevent the question of wether or not to save the changes
                  (Which would save the decrypted data without an event to
                  prevent it) we cancel the close and then either close it
                  with discard changes or revert / save it.
                  Contrary to documentation we can invoke close from
                  close.
                  */
               if (parms->cArgs != 1 || parms->rgvarg[0].vt != (VT_BOOL | VT_BYREF))
                 {
                   /* This happens in the weird case */
                   log_debug ("%s:%s: Uncancellable close event.",
                              SRCNAME, __func__);
                   TBREAK;
                 }
               if (m_mail->getCloseTriggered ())
                 {
                   /* Our close with discard changes, pass through */
                   m_mail->setCloseTriggered (false);
                   TRETURN S_OK;
                 }
               *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE;
               log_oom ("%s:%s: Canceling close event.",
                              SRCNAME, __func__);
               if (Mail::close(m_mail))
                 {
                   log_debug ("%s:%s: Close request failed.",
                              SRCNAME, __func__);
                 }
             }
           TRETURN S_OK;
         }
       case Unload:
         {
           log_oom ("%s:%s: Unload : %p",
                          SRCNAME, __func__, m_mail);
           log_debug ("%s:%s: Removing Mail for message: %p.",
                      SRCNAME, __func__, m_object);
           delete m_mail;
           log_oom ("%s:%s: deletion done",
                          SRCNAME, __func__);
           memdbg_dump ();
           TRETURN S_OK;
         }
       case ReplyAll:
       case Reply:
           is_reply = true;
           /* fall through */
       case Forward:
         {
           log_oom ("%s:%s: %s : %p",
                          SRCNAME, __func__, is_reply ? "reply" : "forward", m_mail);
           int draft_flags = 0;
           if (opt.encrypt_default)
             {
               draft_flags = 1;
             }
           if (opt.sign_default)
             {
               draft_flags += 2;
             }
           bool is_crypto_mail = m_mail->isCryptoMail ();
 
           /* If it is a crypto mail and the settings should not be taken
            * from the crypto mail and always encrypt / sign is on. Or
            * If it is not a crypto mail and we have automaticalls sign_encrypt. */
           if ((is_crypto_mail && !opt.reply_crypt && draft_flags) ||
               (!is_crypto_mail && draft_flags))
             {
               /* Check if we can use the dispval */
                 if (parms->cArgs == 2 && parms->rgvarg[1].vt == (VT_DISPATCH) &&
                     parms->rgvarg[0].vt == (VT_BOOL | VT_BYREF))
                 {
                   LPMESSAGE msg = get_oom_base_message (parms->rgvarg[1].pdispVal);
                   if (msg)
                     {
                       set_gpgol_draft_info_flags (msg, draft_flags);
                       gpgol_release (msg);
                     }
                   else
                     {
                       log_error ("%s:%s: Failed to get base message.",
                                  SRCNAME, __func__);
                     }
                 }
               else
                 {
                   log_error ("%s:%s: Unexpected parameters.",
                              SRCNAME, __func__);
                 }
             }
 
           if (!is_crypto_mail)
             {
               /* Replys to non crypto mails do not interest us anymore. */
               TBREAK;
             }
 
           Mail *last_mail = Mail::getLastMail ();
           if (Mail::isValidPtr (last_mail))
             {
               /* We want to identify here if there was a mail created that
                  should receive the contents of this mail. For this we check
                  for a forward in the same loop as a mail creation.
 
                  We need to do it this complicated and can't just use
                  get_mail_for_item because the mailitem pointer we get here
                  is a different one then the one with which the mail was loaded.
               */
               char *lastEntryID = get_oom_string (last_mail->item (), "EntryID");
               int lastSize = get_oom_int (last_mail->item (), "Size");
               std::string lastEntryStr;
               if (lastEntryID)
                 {
                   lastEntryStr = lastEntryID;
                   xfree (lastEntryID);
                 }
 
               if (!lastSize && !lastEntryStr.size ())
                 {
                   if (!is_reply)
                     {
                       log_debug ("%s:%s: Forward in the same loop as empty "
                                  "load Marking %p (item %p) as forwarded.",
                                  SRCNAME, __func__, last_mail,
                                  last_mail->item ());
 
                       last_mail->setIsForwardedCryptoMail (true);
                     }
                   else
                     {
                       log_debug ("%s:%s: Reply in the same loop as empty "
                                  "load Marking %p (item %p) as reply.",
                                  SRCNAME, __func__, last_mail,
                                  last_mail->item ());
                     }
                   if (m_mail->isBlockHTML ())
                     {
                       std::string buf;
                       /** TRANSLATORS: Part of a warning dialog that disallows
                         reply and forward with contents */
                       buf = is_reply ? _("You are replying to an unsigned S/MIME "
                                          "email.") :
                                        _("You are forwarding an unsigned S/MIME "
                                          "email.");
                       buf +="\n\n";
                       buf += _("In this version of S/MIME an attacker could "
                                "use the missing signature to have you "
                                "decrypt contents from a different, otherwise "
                                "completely unrelated email and place it in the "
                                "quote so they can get hold of it.\n"
                                "This is why we only allow quoting to be done manually.");
                       buf += "\n\n";
                       buf += _("Please copy the relevant contents and insert "
                                "them into the new email.");
 
                       gpgol_message_box (get_active_hwnd (), buf.c_str(),
                                          _("GpgOL"), MB_OK);
 
                       do_in_ui_thread_async (CLEAR_REPLY_FORWARD, last_mail, 1000);
                     }
                 }
               // We can now invalidate the last mail
               Mail::clearLastMail ();
             }
 
           log_oom ("%s:%s: Reply Forward ReplyAll: %p",
                          SRCNAME, __func__, m_mail);
           if (!opt.reply_crypt)
             {
               TBREAK;
             }
           int crypto_flags = 0;
           if (!(crypto_flags = m_mail->getCryptoFlags ()))
             {
               TBREAK;
             }
           if (parms->cArgs != 2 || parms->rgvarg[1].vt != (VT_DISPATCH) ||
               parms->rgvarg[0].vt != (VT_BOOL | VT_BYREF))
             {
               /* This happens in the weird case */
               log_debug ("%s:%s: Unexpected args %i %x %x named: %i",
                          SRCNAME, __func__, parms->cArgs, parms->rgvarg[0].vt, parms->rgvarg[1].vt,
                          parms->cNamedArgs);
               TBREAK;
             }
           LPMESSAGE msg = get_oom_base_message (parms->rgvarg[1].pdispVal);
           if (!msg)
             {
               log_debug ("%s:%s: Failed to get base message",
                          SRCNAME, __func__);
               TBREAK;
             }
           set_gpgol_draft_info_flags (msg, crypto_flags);
           gpgol_release (msg);
           TBREAK;
         }
       case AttachmentRemove:
         {
           log_oom ("%s:%s: AttachmentRemove: %p",
                          SRCNAME, __func__, m_mail);
           if (!m_mail->isCryptoMail () || attachRemoveWarnShown ||
               m_mail->attachmentRemoveWarningDisabled ())
             {
               TRETURN S_OK;
             }
           gpgol_message_box (get_active_hwnd (),
                              _("Attachments are part of the crypto message.\nThey "
                                "can't be permanently removed and will be shown again the next "
                                "time this message is opened."),
                              _("Sorry, that's not possible, yet"), MB_OK);
           attachRemoveWarnShown = true;
           TRETURN S_OK;
         }
 
       default:
         log_oom ("%s:%s: Message:%p Unhandled Event: %lx \n",
                        SRCNAME, __func__, m_object, dispid);
     }
   TRETURN S_OK;
 }
 END_EVENT_SINK(MailItemEvents, IID_MailItemEvents)
diff --git a/src/main.c b/src/main.c
index cb95efe..c0a31a0 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1,394 +1,411 @@
 /* main.c - DLL entry point
  * Copyright (C) 2005, 2007, 2008 g10 Code 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
  * 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 <windows.h>
 #include <wincrypt.h>
 #include <ctype.h>
 #include <winnls.h>
 #include <unistd.h>
 
 #include "mymapi.h"
 #include "mymapitags.h"
 
 #include "common.h"
 #include "mymapi.h"
 
 /* Local function prototypes. */
 static char *get_locale_dir (void);
 static void drop_locale_dir (char *locale_dir);
 
 /* The major version of Outlook we are attached to */
 int g_ol_version_major;
 
 
 void
 i18n_init (void)
 {
   char *locale_dir;
 
 #ifdef ENABLE_NLS
 # ifdef HAVE_LC_MESSAGES
   setlocale (LC_TIME, "");
   setlocale (LC_MESSAGES, "");
 # else
   setlocale (LC_ALL, "" );
 # endif
 #endif
 
   locale_dir = get_locale_dir ();
   if (locale_dir)
     {
       bindtextdomain (PACKAGE_GT, locale_dir);
       drop_locale_dir (locale_dir);
     }
   textdomain (PACKAGE_GT);
 }
 
 static char *
 get_gpgme_w32_inst_dir (void)
 {
   char *gpg4win_dir = get_gpg4win_dir ();
   char *tmp;
   gpgrt_asprintf (&tmp, "%s\\bin\\gpgme-w32spawn.exe", gpg4win_dir);
   memdbg_alloc (tmp);
 
   if (!access(tmp, R_OK))
     {
       xfree (tmp);
       gpgrt_asprintf (&tmp, "%s\\bin", gpg4win_dir);
       memdbg_alloc (tmp);
       xfree (gpg4win_dir);
       return tmp;
     }
   xfree (tmp);
   gpgrt_asprintf (&tmp, "%s\\gpgme-w32spawn.exe", gpg4win_dir);
   memdbg_alloc (tmp);
 
   if (!access(tmp, R_OK))
     {
       xfree (tmp);
       return gpg4win_dir;
     }
   OutputDebugString("Failed to find gpgme-w32spawn.exe!");
   return NULL;
 }
 
 /* Entry point called by DLL loader. */
 int WINAPI
 DllMain (HINSTANCE hinst, DWORD reason, LPVOID reserved)
 {
   (void)reserved;
 
   if (reason == DLL_PROCESS_ATTACH)
     {
       set_global_hinstance (hinst);
 
       gpg_err_init ();
 
       /* Set the installation directory for GpgME so that
          it can find tools like gpgme-w32-spawn correctly. */
       char *instdir = get_gpgme_w32_inst_dir();
       gpgme_set_global_flag ("w32-inst-dir", instdir);
       xfree (instdir);
 
       /* The next call initializes subsystems of gpgme and should be
          done as early as possible.  The actual return value (the
          version string) is not used here.  It may be called at any
          time later for this. */
       gpgme_check_version (NULL);
     }
   else if (reason == DLL_PROCESS_DETACH)
     {
       gpg_err_deinit (0);
     }
 
   return TRUE;
 }
 
 static char *
 get_locale_dir (void)
 {
   char *instdir;
   char *p;
   char *dname;
 
   instdir = get_gpg4win_dir();
   if (!instdir)
     return NULL;
 
   /* Build the key: "<instdir>/share/locale".  */
 #define SLDIR "\\share\\locale"
   dname = xmalloc (strlen (instdir) + strlen (SLDIR) + 1);
   if (!dname)
     {
       xfree (instdir);
       return NULL;
     }
   p = dname;
   strcpy (p, instdir);
   p += strlen (instdir);
   strcpy (p, SLDIR);
 
   xfree (instdir);
 
   return dname;
 }
 
 
 static void
 drop_locale_dir (char *locale_dir)
 {
   xfree (locale_dir);
 }
 
 
 static int
 get_conf_bool (const char *name, int defaultVal)
 {
   char *val = NULL;
   int ret;
   load_extension_value (name, &val);
   ret = val == NULL ? defaultVal : *val != '1' ? 0 : 1;
   xfree (val);
   return ret;
 }
 
 static int
 dbg_compat (int oldval)
 {
   // We broke the debug levels at some point
   // This is cmpatibility code with the old
   // levels.
 
 #define DBG_MEMORY_OLD     (1<<5) // 32
 #define DBG_MIME_PARSER_OLD (1<<7) // 128 Unified as DBG_DATA
 #define DBG_MIME_DATA_OLD   (1<<8) // 256 Unified in read_options
 #define DBG_OOM_OLD        (1<<9) // 512 Unified as DBG_OOM
 #define DBG_OOM_EXTRA_OLD  (1<<10)// 1024 Unified in read_options
   int new_dbg = oldval;
   if ((oldval & DBG_MEMORY_OLD))
     {
       new_dbg |= DBG_MEMORY;
       new_dbg -= DBG_MEMORY_OLD;
     }
   if ((oldval & DBG_OOM_OLD))
     {
       new_dbg |= DBG_OOM;
       new_dbg -= DBG_OOM_OLD;
     }
   if ((oldval & DBG_MIME_PARSER_OLD))
     {
       new_dbg |= DBG_DATA;
       new_dbg -= DBG_MIME_PARSER_OLD;
     }
   if ((oldval & DBG_MIME_DATA_OLD))
     {
       new_dbg |= DBG_DATA;
       new_dbg -= DBG_MIME_DATA_OLD;
     }
   if ((oldval & DBG_OOM_OLD))
     {
       new_dbg |= DBG_OOM;
       new_dbg -= DBG_OOM_OLD;
     }
   if ((oldval & DBG_OOM_EXTRA_OLD))
     {
       new_dbg |= DBG_OOM;
       new_dbg -= DBG_OOM_EXTRA_OLD;
     }
 #undef DBG_MEMORY_OLD
 #undef DBG_MIME_PARSER_OLD
 #undef DBG_MIME_DATA_OLD
 #undef DBG_OOM_OLD
 #undef DBG_OOM_EXTRA_OLD
   return new_dbg;
 }
 
 /* Read option settings from the Registry. */
 void
 read_options (void)
 {
   char *val = NULL;
 
   /* Set the log file first so that output from this function is
      logged too.  */
   load_extension_value ("logFile", &val);
   set_log_file (val);
   xfree (val); val = NULL;
 
   /* Parse the debug flags.  */
   load_extension_value ("enableDebug", &val);
   opt.enable_debug = 0;
   if (val)
     {
       char *p, *pend;
 
       trim_spaces (val);
       for (p = val; p; p = pend)
         {
           pend = strpbrk (p, ", \t\n\r\f");
           if (pend)
             {
               *pend++ = 0;
               pend += strspn (pend, ", \t\n\r\f");
             }
           if (isascii (*p) && isdigit (*p))
             {
               opt.enable_debug |= dbg_compat (strtoul (p, NULL, 0));
 
             }
           else if (!strcmp (p, "memory"))
             opt.enable_debug |= DBG_MEMORY;
           else if (!strcmp (p, "mime-parser"))
             opt.enable_debug |= DBG_DATA;
           else if (!strcmp (p, "mime-data"))
             opt.enable_debug |= DBG_DATA;
           else if (!strcmp (p, "oom"))
             opt.enable_debug |= DBG_OOM;
           else if (!strcmp (p, "oom-extra"))
             opt.enable_debug |= DBG_OOM;
           else
             log_debug ("invalid debug flag `%s' ignored", p);
         }
     }
   else
     {
       /* To help the user enable debugging make sure that the registry
          key exists.  Note that the other registry keys are stored
          after using the configuration dialog.  */
       store_extension_value ("enableDebug", "0");
     }
   /* Yes we use free here because memtracing did not track the alloc
      as the option for debuging was not read before. */
   free (val); val = NULL;
   if (opt.enable_debug)
     log_debug ("enabled debug flags:%s%s%s%s\n",
                (opt.enable_debug & DBG_MEMORY)? " memory":"",
                (opt.enable_debug & DBG_DATA)? " data":"",
                (opt.enable_debug & DBG_OOM)? " oom":"",
                (opt.enable_debug & DBG_TRACE)? " trace":""
                );
 
   opt.enable_smime = get_conf_bool ("enableSmime", 0);
   opt.encrypt_default = get_conf_bool ("encryptDefault", 0);
   opt.sign_default = get_conf_bool ("signDefault", 0);
   opt.inline_pgp = get_conf_bool ("inlinePGP", 0);
   opt.reply_crypt = get_conf_bool ("replyCrypt", 1);
   opt.prefer_smime = get_conf_bool ("preferSmime", 0);
   opt.autoresolve = get_conf_bool ("autoresolve", 1);
   opt.autoretrieve = get_conf_bool ("autoretrieve", 0);
   opt.automation = get_conf_bool ("automation", 1);
   opt.autosecure = get_conf_bool ("autosecure", 1);
   opt.autotrust = get_conf_bool ("autotrust", 0);
   opt.search_smime_servers = get_conf_bool ("searchSmimeServers", 0);
   opt.smime_html_warn_shown = get_conf_bool ("smimeHtmlWarnShown", 0);
   opt.auto_unstrusted = get_conf_bool ("autoencryptUntrusted", 0);
   opt.autoimport = get_conf_bool ("autoimport", 0);
 
   if (!opt.automation)
     {
       // Disabling automation is a shorthand to disable the
       // others, too.
       opt.autosecure = 0;
       opt.autoresolve = 0;
       opt.autotrust = 0;
       opt.autoretrieve = 0;
       opt.autoimport = 0;
       opt.auto_unstrusted = 0;
     }
 
+  /* Draft encryption handling. */
+  if (get_conf_bool ("draftEnc", 0))
+    {
+      load_extension_value ("draftKey", &val);
+      if (val)
+        {
+          xfree (opt.draft_key);
+          opt.draft_key = val;
+          val = NULL;
+        }
+    }
+  else
+    {
+      xfree (opt.draft_key);
+      opt.draft_key = NULL;
+    }
+
   /* Hidden options  */
   opt.sync_enc = get_conf_bool ("syncEnc", 0);
   opt.sync_dec = get_conf_bool ("syncDec", 0);
 }
 
 
 /* Write current options back to the Registry. */
 int
 write_options (void)
 {
   struct
   {
     const char *name;
     int  mode;
     int  value;
     char *s_val;
   } table[] = {
     {"smimeHtmlWarnShown",       0, opt.smime_html_warn_shown, NULL},
     {NULL, 0, 0, NULL}
   };
   char buf[32];
   int rc, i;
   const char *string;
 
   for (i=0; table[i].name; i++)
     {
       switch (table[i].mode)
         {
         case 0:
           string = table[i].value? "1": "0";
           log_debug ("storing option `%s' value=`%s'\n",
                      table[i].name, string);
           rc = store_extension_value (table[i].name, string);
           break;
         case 1:
           sprintf (buf, "%d", table[i].value);
           log_debug ("storing option `%s' value=`%s'\n",
                      table[i].name, buf);
           rc = store_extension_value (table[i].name, buf);
           break;
         case 2:
           string = table[i].s_val? table[i].s_val : "";
           log_debug ("storing option `%s' value=`%s'\n",
                      table[i].name, string);
           rc = store_extension_value (table[i].name, string);
           break;
 /*         case 3: */
 /*           buf[0] = '0'; */
 /*           buf[1] = 0; */
 /*           switch (opt.default_protocol) */
 /*             { */
 /*             case PROTOCOL_UNKNOWN: buf[0] = '0'; /\* auto *\/ break; */
 /*             case PROTOCOL_OPENPGP: buf[0] = '1'; break; */
 /*             case PROTOCOL_SMIME:   buf[0] = '2'; break; */
 /*             } */
 /*           log_debug ("storing option `%s' value=`%s'\n", */
 /*                      table[i].name, buf); */
 /*           rc = store_extension_value (table[i].name, buf); */
 /*           break;   */
 
         case 4:
           sprintf (buf, "0x%x", table[i].value);
           log_debug ("storing option `%s' value=`%s'\n",
                      table[i].name, buf);
           rc = store_extension_value (table[i].name, buf);
           break;
 
         default:
           rc = -1;
           break;
         }
       if (rc)
         log_error ("error storing option `%s': rc = %d\n", table[i].name, rc);
     }
 
   return 0;
 }
diff --git a/src/windowmessages.cpp b/src/windowmessages.cpp
index 58a42cc..6ef4060 100644
--- a/src/windowmessages.cpp
+++ b/src/windowmessages.cpp
@@ -1,649 +1,661 @@
 /* @file windowmessages.h
  * @brief Helper class to work with the windowmessage handler thread.
  *
  * Copyright (C) 2015, 2016 by Bundesamt für Sicherheit in der Informationstechnik
  * Software engineering by 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 "windowmessages.h"
 
 #include "common.h"
 #include "oomhelp.h"
 #include "mail.h"
 #include "gpgoladdin.h"
 #include "wks-helper.h"
 #include "addressbook.h"
 
 #include <stdio.h>
 
 #define RESPONDER_CLASS_NAME "GpgOLResponder"
 
 /* Singleton window */
 static HWND g_responder_window = NULL;
 static int invalidation_blocked = 0;
 
 LONG_PTR WINAPI
 gpgol_window_proc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
 {
 //  log_debug ("WMG: %x", (unsigned int) message);
   if (message == WM_USER + 42)
     {
       TSTART;
 
       if (!lParam)
         {
           log_error ("%s:%s: Recieved user msg without lparam",
                      SRCNAME, __func__);
           TRETURN DefWindowProc(hWnd, message, wParam, lParam);
         }
       wm_ctx_t *ctx = (wm_ctx_t *) lParam;
       log_debug ("%s:%s: Recieved user msg: %i",
                  SRCNAME, __func__, ctx->wmsg_type);
       switch (ctx->wmsg_type)
         {
           case (PARSING_DONE):
             {
               auto mail = (Mail*) ctx->data;
               if (!Mail::isValidPtr (mail))
                 {
                   log_debug ("%s:%s: Parsing done for mail which is gone.",
                              SRCNAME, __func__);
                   TBREAK;
                 }
               mail->parsing_done();
               TBREAK;
             }
           case (RECIPIENT_ADDED):
             {
               auto mail = (Mail*) ctx->data;
               if (!Mail::isValidPtr (mail))
                 {
                   log_debug ("%s:%s: Recipient add for mail which is gone.",
                              SRCNAME, __func__);
                   TBREAK;
                 }
               mail->locateKeys_o ();
               TBREAK;
             }
           case (REVERT_MAIL):
             {
               auto mail = (Mail*) ctx->data;
               if (!Mail::isValidPtr (mail))
                 {
                   log_debug ("%s:%s: Revert mail for mail which is gone.",
                              SRCNAME, __func__);
                   TBREAK;
                 }
 
               mail->setNeedsSave (true);
               /* Some magic here. Accessing any existing inline body cements
                  it. Otherwise updating the body through the revert also changes
                  the body of a inline mail. */
               char *inlineBody = get_inline_body ();
               xfree (inlineBody);
 
               // Does the revert.
               log_debug ("%s:%s: Revert mail. Invoking save.",
                          SRCNAME, __func__);
               invoke_oom_method (mail->item (), "Save", NULL);
               log_debug ("%s:%s: Revert mail. Save done. Updating body..",
                          SRCNAME, __func__);
               mail->updateBody_o ();
               log_debug ("%s:%s: Revert mail done.",
                          SRCNAME, __func__);
               TBREAK;
             }
           case (INVALIDATE_UI):
             {
               if (!invalidation_blocked)
                 {
                   log_debug ("%s:%s: Invalidating UI",
                              SRCNAME, __func__);
                   gpgoladdin_invalidate_ui();
                   log_debug ("%s:%s: Invalidation done",
                              SRCNAME, __func__);
                 }
               else
                 {
                   log_debug ("%s:%s: Received invalidation msg while blocked."
                              " Ignoring it",
                              SRCNAME, __func__);
                 }
               TBREAK;
             }
           case (INVALIDATE_LAST_MAIL):
             {
               log_debug ("%s:%s: clearing last mail",
                          SRCNAME, __func__);
               Mail::clearLastMail ();
               TBREAK;
             }
           case (CLOSE):
             {
               auto mail = (Mail*) ctx->data;
               if (!Mail::isValidPtr (mail))
                 {
                   log_debug ("%s:%s: Close for mail which is gone.",
                              SRCNAME, __func__);
                   TBREAK;
                 }
               mail->refCurrentItem();
               Mail::closeInspector_o (mail);
               TRACEPOINT;
               Mail::close (mail);
               log_debug ("%s:%s: Close finished.",
                          SRCNAME, __func__);
               mail->releaseCurrentItem();
               TBREAK;
             }
           case (CRYPTO_DONE):
             {
               auto mail = (Mail*) ctx->data;
               if (!Mail::isValidPtr (mail))
                 {
                   log_debug ("%s:%s: Crypto done for mail which is gone.",
                              SRCNAME, __func__);
                   TBREAK;
                 }
+              if (mail->isDraftEncrypt())
+                {
+                  mail->updateCryptOOM_o ();
+                  invoke_oom_method (mail->item (), "Save", NULL);
+                  log_debug ("%s:%s: Second save done for %p finished draft "
+                             "encryption.",
+                             SRCNAME, __func__, mail);
+                  mail->setIsDraftEncrypt (false);
+                  mail->setCryptState (Mail::NoCryptMail);
+                  mail->releaseCurrentItem ();
+                  TBREAK;
+                }
               // modify the mail.
               if (mail->cryptState () == Mail::NeedsUpdateInOOM)
                 {
                   // Save the Mail
                   log_debug ("%s:%s: Crypto done for %p updating oom.",
                              SRCNAME, __func__, mail);
                   mail->updateCryptOOM_o ();
                 }
               if (mail->cryptState () == Mail::NeedsSecondAfterWrite)
                 {
                   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.
               if (invoke_oom_method (mail->item (), "Send", NULL))
                 {
                   log_error ("%s:%s: Send failed for %p. "
                              "Trying SubmitMessage instead.\n"
                              "This will likely crash.",
                              SRCNAME, __func__, mail);
 
                   /* What we do here is similar to the T3656 workaround
                      in mailitem-events.cpp. In our tests this works but
                      is unstable. So we only use it as a very very last
                      resort. */
                   auto mail_message = get_oom_base_message (mail->item());
                   if (!mail_message)
                     {
                       gpgol_bug (mail->getWindow (),
                                  ERR_GET_BASE_MSG_FAILED);
                       TBREAK;
                     }
                   // It's important we use the _base_ message here.
                   mapi_save_changes (mail_message, FORCE_SAVE);
                   HRESULT hr = mail_message->SubmitMessage(0);
                   gpgol_release (mail_message);
 
                   if (hr == S_OK)
                     {
                       do_in_ui_thread_async (CLOSE, (LPVOID) mail);
                     }
                   else
                     {
                       log_error ("%s:%s: SubmitMessage Failed hr=0x%lx.",
                                  SRCNAME, __func__, hr);
                       gpgol_bug (mail->getWindow (),
                                  ERR_SEND_FALLBACK_FAILED);
                     }
                 }
               else
                 {
                   mail->releaseCurrentItem ();
                 }
               log_debug ("%s:%s:  Send for %p completed.",
                          SRCNAME, __func__, mail);
               TBREAK;
             }
           case (BRING_TO_FRONT):
             {
               HWND wnd = get_active_hwnd ();
               if (wnd)
                 {
                   log_debug ("%s:%s: Bringing window %p to front.",
                              SRCNAME, __func__, wnd);
                   bring_to_front (wnd);
                 }
               else
                 {
                   log_debug ("%s:%s: No active window found for bring to front.",
                              SRCNAME, __func__);
                 }
               TBREAK;
             }
           case (WKS_NOTIFY):
             {
               WKSHelper::instance ()->notify ((const char *) ctx->data);
               xfree (ctx->data);
               TBREAK;
             }
           case (CLEAR_REPLY_FORWARD):
             {
               auto mail = (Mail*) ctx->data;
               if (!Mail::isValidPtr (mail))
                 {
                   log_debug ("%s:%s: Clear reply forward for mail which is gone.",
                              SRCNAME, __func__);
                   TBREAK;
                 }
               mail->wipe_o (true);
               mail->removeAllAttachments_o ();
               TBREAK;
             }
           case (DO_AUTO_SECURE):
             {
               auto mail = (Mail*) ctx->data;
               if (!Mail::isValidPtr (mail))
                 {
                   log_debug ("%s:%s: DO_AUTO_SECURE for mail which is gone.",
                              SRCNAME, __func__);
                   TBREAK;
                 }
               mail->setDoAutosecure_m (true);
               TBREAK;
             }
           case (DONT_AUTO_SECURE):
             {
               auto mail = (Mail*) ctx->data;
               if (!Mail::isValidPtr (mail))
                 {
                   log_debug ("%s:%s: DO_AUTO_SECURE for mail which is gone.",
                              SRCNAME, __func__);
                   TBREAK;
                 }
               mail->setDoAutosecure_m (false);
               TBREAK;
             }
           case (CONFIG_KEY_DONE):
             {
               log_debug ("%s:%s: Key configuration done.",
                          SRCNAME, __func__);
 
               Addressbook::update_key_o (ctx->data);
               TBREAK;
             }
           default:
             log_debug ("%s:%s: Unknown msg %x",
                        SRCNAME, __func__, ctx->wmsg_type);
         }
         TRETURN 0;
     }
   else if (message == WM_USER)
     {
       switch (wParam)
         {
           case (EXT_API_CLOSE_ALL):
             {
               log_debug ("%s:%s: Closing all mails.",
                          SRCNAME, __func__);
               Mail::closeAllMails_o ();
               TRETURN 0;
             }
           default:
             log_debug ("%s:%s: Unknown external msg %i",
                        SRCNAME, __func__, (int) wParam);
         }
     }
   else if (message == WM_COPYDATA)
     {
       log_debug ("%s:%s Received copydata message.",
                  SRCNAME, __func__);
       if (!lParam)
         {
           STRANGEPOINT;
           TRETURN DefWindowProc(hWnd, message, wParam, lParam);
         }
       const COPYDATASTRUCT *cds = (COPYDATASTRUCT*)lParam;
 
       if (cds->dwData == EXT_API_CLOSE)
         {
           log_debug ("%s:%s CopyData with external close. Payload: %.*s",
                      SRCNAME, __func__, (int)cds->cbData, (char *) cds->lpData);
 
           std::string uid ((char*) cds->lpData, cds->cbData);
           auto mail = Mail::getMailForUUID (uid.c_str ());
           if (!mail)
             {
               log_error ("%s:%s Failed to find mail for: %s",
                          SRCNAME, __func__, uid.c_str() );
               TRETURN DefWindowProc(hWnd, message, wParam, lParam);
             }
 
           mail->refCurrentItem ();
           Mail::closeInspector_o (mail);
           TRACEPOINT;
           Mail::close (mail);
           log_debug ("%s:%s: Close for %p uid: %s finished.",
                      SRCNAME, __func__, mail, uid.c_str ());
           mail->releaseCurrentItem();
           TRETURN 0;
         }
       else if (cds->dwData == EXT_API_DECRYPT)
         {
           log_debug ("%s:%s CopyData with external decrypt. Payload: %.*s",
                      SRCNAME, __func__, (int)cds->cbData, (char *) cds->lpData);
 
           std::string uid ((char*) cds->lpData, cds->cbData);
           auto mail = Mail::getMailForUUID (uid.c_str ());
           if (!mail)
             {
               log_error ("%s:%s Failed to find mail for: %s",
                          SRCNAME, __func__, uid.c_str() );
               TRETURN DefWindowProc(hWnd, message, wParam, lParam);
             }
 
           log_debug ("%s:%s: Decrypting %p again.",
                      SRCNAME, __func__, mail);
           mail->preProcessMessage_m ();
           mail->decryptVerify_o ();
           TRETURN 0;
         }
 
     }
   return DefWindowProc(hWnd, message, wParam, lParam);
 }
 
 HWND
 create_responder_window ()
 {
   TSTART;
   size_t cls_name_len = strlen(RESPONDER_CLASS_NAME) + 1;
   char cls_name[cls_name_len];
   if (g_responder_window)
     {
       TRETURN g_responder_window;
     }
   /* Create Window wants a mutable string as the first parameter */
   snprintf (cls_name, cls_name_len, "%s", RESPONDER_CLASS_NAME);
 
   WNDCLASS windowClass;
   windowClass.style = CS_GLOBALCLASS | CS_DBLCLKS;
   windowClass.lpfnWndProc = gpgol_window_proc;
   windowClass.cbClsExtra = 0;
   windowClass.cbWndExtra = 0;
   windowClass.hInstance = (HINSTANCE) GetModuleHandle(NULL);
   windowClass.hIcon = 0;
   windowClass.hCursor = 0;
   windowClass.hbrBackground = 0;
   windowClass.lpszMenuName  = 0;
   windowClass.lpszClassName = cls_name;
   RegisterClass(&windowClass);
   g_responder_window = CreateWindow (cls_name, RESPONDER_CLASS_NAME, 0, 0, 0,
                                      0, 0, 0, (HMENU) 0,
                                      (HINSTANCE) GetModuleHandle(NULL), 0);
   TRETURN g_responder_window;
 }
 
 static int
 send_msg_to_ui_thread (wm_ctx_t *ctx)
 {
   TSTART;
   size_t cls_name_len = strlen(RESPONDER_CLASS_NAME) + 1;
   char cls_name[cls_name_len];
   snprintf (cls_name, cls_name_len, "%s", RESPONDER_CLASS_NAME);
 
   HWND responder = FindWindow (cls_name, RESPONDER_CLASS_NAME);
   if (!responder)
   {
     log_error ("%s:%s: Failed to find responder window.",
                SRCNAME, __func__);
     TRETURN -1;
   }
   SendMessage (responder, WM_USER + 42, 0, (LPARAM) ctx);
   TRETURN 0;
 }
 
 int
 do_in_ui_thread (gpgol_wmsg_type type, void *data)
 {
   TSTART;
   wm_ctx_t ctx = {NULL, UNKNOWN, 0, 0};
   ctx.wmsg_type = type;
   ctx.data = data;
 
   log_debug ("%s:%s: Sending message of type %i",
              SRCNAME, __func__, type);
 
   if (send_msg_to_ui_thread (&ctx))
     {
       TRETURN -1;
     }
   TRETURN ctx.err;
 }
 
 static DWORD WINAPI
 do_async (LPVOID arg)
 {
   TSTART;
   wm_ctx_t *ctx = (wm_ctx_t*) arg;
   log_debug ("%s:%s: Do async with type %i after %i ms",
              SRCNAME, __func__, ctx ? ctx->wmsg_type : -1,
              ctx->delay);
   if (ctx->delay)
     {
       Sleep (ctx->delay);
     }
   send_msg_to_ui_thread (ctx);
   xfree (ctx);
   TRETURN 0;
 }
 
 void
 do_in_ui_thread_async (gpgol_wmsg_type type, void *data, int delay)
 {
   TSTART;
   wm_ctx_t *ctx = (wm_ctx_t *) xcalloc (1, sizeof (wm_ctx_t));
   ctx->wmsg_type = type;
   ctx->data = data;
   ctx->delay = delay;
 
   CloseHandle (CreateThread (NULL, 0, do_async, (LPVOID) ctx, 0, NULL));
   TRETURN;
 }
 
 LRESULT CALLBACK
 gpgol_hook(int code, WPARAM wParam, LPARAM lParam)
 {
 /* Once we are in the close events we don't have enough
    control to revert all our changes so we have to do it
    with this nice little hack by catching the WM_CLOSE message
    before it reaches outlook. */
   LPCWPSTRUCT cwp = (LPCWPSTRUCT) lParam;
 
   /* What we do here is that we catch all WM_CLOSE messages that
      get to Outlook. Then we check if the last open Explorer
      is the target of the close. In set case we start our shutdown
      routine before we pass the WM_CLOSE to outlook */
   switch (cwp->message)
     {
       case WM_CLOSE:
       {
         HWND lastChild = NULL;
         log_debug ("%s:%s: Got WM_CLOSE",
                    SRCNAME, __func__);
         if (!GpgolAddin::get_instance() || !GpgolAddin::get_instance ()->get_application())
           {
             TRACEPOINT;
             TBREAK;
           }
         LPDISPATCH explorers = get_oom_object (GpgolAddin::get_instance ()->get_application(),
                                                "Explorers");
 
         if (!explorers)
           {
             log_error ("%s:%s: No explorers object",
                        SRCNAME, __func__);
             TBREAK;
           }
         int count = get_oom_int (explorers, "Count");
 
         if (count != 1)
           {
             log_debug ("%s:%s: More then one explorer. Not shutting down.",
                        SRCNAME, __func__);
             gpgol_release (explorers);
             TBREAK;
           }
 
         LPDISPATCH explorer = get_oom_object (explorers, "Item(1)");
         gpgol_release (explorers);
 
         if (!explorer)
           {
             TRACEPOINT;
             TBREAK;
           }
 
         /* Casting to LPOLEWINDOW and calling GetWindow
            succeeded in Outlook 2016 but always TRETURNed
            the number 1. So we need this hack. */
         char *caption = get_oom_string (explorer, "Caption");
         gpgol_release (explorer);
         if (!caption)
           {
             log_debug ("%s:%s: No caption.",
                        SRCNAME, __func__);
             TBREAK;
           }
         /* rctrl_renwnd32 is the window class of outlook. */
         HWND hwnd = FindWindowExA(NULL, lastChild, "rctrl_renwnd32",
                                   caption);
         xfree (caption);
         lastChild = hwnd;
         if (hwnd == cwp->hwnd)
           {
             log_debug ("%s:%s: WM_CLOSE windowmessage for explorer. "
                        "Shutting down.",
                        SRCNAME, __func__);
             GpgolAddin::get_instance ()->shutdown();
             TBREAK;
           }
         TBREAK;
       }
      case WM_SYSCOMMAND:
         /*
          This comes to often and when we are closed from the icon
          we also get WM_CLOSE
        if (cwp->wParam == SC_CLOSE)
         {
           log_debug ("%s:%s: SC_CLOSE syscommand. Closing all mails.",
                      SRCNAME, __func__);
           GpgolAddin::get_instance ()->shutdown();
         } */
        break;
      default:
 //       log_debug ("WM: %x", (unsigned int) cwp->message);
        break;
     }
   return CallNextHookEx (NULL, code, wParam, lParam);
 }
 
 /* Create the message hook for outlook's windowmessages
    we are especially interested in WM_QUIT to do cleanups
    and prevent the "Item has changed" question. */
 HHOOK
 create_message_hook()
 {
   TSTART;
   TRETURN SetWindowsHookEx (WH_CALLWNDPROC,
                            gpgol_hook,
                            NULL,
                            GetCurrentThreadId());
 }
 
 GPGRT_LOCK_DEFINE (invalidate_lock);
 
 static bool invalidation_in_progress;
 
 DWORD WINAPI
 delayed_invalidate_ui (LPVOID minsleep)
 {
   TSTART;
   if (invalidation_in_progress)
     {
       log_debug ("%s:%s: Invalidation canceled as it is in progress.",
                  SRCNAME, __func__);
       TRETURN 0;
     }
   TRACEPOINT;
   invalidation_in_progress = true;
   gpgol_lock(&invalidate_lock);
 
   int sleep_ms = (intptr_t)minsleep;
   Sleep (sleep_ms);
   int i = 0;
   while (invalidation_blocked)
     {
       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);
   TRACEPOINT;
   invalidation_in_progress = false;
   gpgol_unlock(&invalidate_lock);
   TRETURN 0;
 }
 
 DWORD WINAPI
 close_mail (LPVOID mail)
 {
   TSTART;
   do_in_ui_thread (CLOSE, mail);
   TRETURN 0;
 }
 
 void
 blockInv()
 {
   TSTART;
   invalidation_blocked++;
   log_oom ("%s:%s: Invalidation block count %i",
                  SRCNAME, __func__, invalidation_blocked);
   TRETURN;
 }
 
 void
 unblockInv()
 {
   TSTART;
   invalidation_blocked--;
   log_oom ("%s:%s: Invalidation block count %i",
                  SRCNAME, __func__, invalidation_blocked);
 
   if (invalidation_blocked < 0)
     {
       log_error ("%s:%s: Invalidation block mismatch",
                  SRCNAME, __func__);
       invalidation_blocked = 0;
     }
   TRETURN;
 }