Add minimalistic protected-headers support
[gpgol.git] / src / mailitem-events.cpp
1 /* mailitem-events.h - Event handling for mails.
2  * Copyright (C) 2015 by Bundesamt für Sicherheit in der Informationstechnik
3  * Software engineering by Intevation GmbH
4  *
5  * This file is part of GpgOL.
6  *
7  * GpgOL is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * GpgOL is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this program; if not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include "config.h"
22 #include "common.h"
23 #include "eventsink.h"
24 #include "eventsinks.h"
25 #include "mymapi.h"
26 #include "mymapitags.h"
27 #include "oomhelp.h"
28 #include "ocidl.h"
29 #include "windowmessages.h"
30 #include "mail.h"
31 #include "mapihelp.h"
32 #include "gpgoladdin.h"
33 #include "wks-helper.h"
34
35 #undef _
36 #define _(a) utf8_gettext (a)
37
38 const wchar_t *prop_blacklist[] = {
39   L"Body",
40   L"HTMLBody",
41   L"To", /* Somehow this is done when a mail is opened */
42   L"CC", /* Ditto */
43   L"BCC", /* Ditto */
44   L"Categories",
45   L"UnRead",
46   L"OutlookVersion",
47   L"OutlookInternalVersion",
48   L"ReceivedTime",
49   L"InternetCodepage",
50   L"ConversationIndex",
51   L"Subject",
52   NULL };
53
54 typedef enum
55   {
56     AfterWrite = 0xFC8D,
57     AttachmentAdd = 0xF00B,
58     AttachmentRead = 0xF00C,
59     AttachmentRemove = 0xFBAE,
60     BeforeAttachmentAdd = 0xFBB0,
61     BeforeAttachmentPreview = 0xFBAF,
62     BeforeAttachmentRead = 0xFBAB,
63     BeforeAttachmentSave = 0xF00D,
64     BeforeAttachmentWriteToTempFile = 0xFBB2,
65     BeforeAutoSave = 0xFC02,
66     BeforeCheckNames = 0xF00A,
67     BeforeDelete = 0xFA75,
68     BeforeRead = 0xFC8C,
69     Close = 0xF004,
70     CustomAction = 0xF006,
71     CustomPropertyChange = 0xF008,
72     Forward = 0xF468,
73     Open = 0xF003,
74     PropertyChange = 0xF009,
75     Read = 0xF001,
76     ReadComplete = 0xFC8F,
77     Reply = 0xF466,
78     ReplyAll = 0xF467,
79     Send = 0xF005,
80     Unload = 0xFBAD,
81     Write = 0xF002
82   } MailEvent;
83
84 /* Mail Item Events */
85 BEGIN_EVENT_SINK(MailItemEvents, IDispatch)
86 /* We are still in the class declaration */
87
88 private:
89   Mail * m_mail; /* The mail object related to this mailitem */
90 };
91
92 MailItemEvents::MailItemEvents() :
93     m_object(NULL),
94     m_pCP(NULL),
95     m_cookie(0),
96     m_ref(1),
97     m_mail(NULL)
98 {
99 }
100
101 MailItemEvents::~MailItemEvents()
102 {
103   if (m_pCP)
104     m_pCP->Unadvise(m_cookie);
105   if (m_object)
106     gpgol_release (m_object);
107 }
108
109 static bool propchangeWarnShown = false;
110 static bool attachRemoveWarnShown = false;
111 static bool addinsLogged = false;
112
113 static DWORD WINAPI
114 do_delayed_locate (LPVOID arg)
115 {
116   Sleep(100);
117   do_in_ui_thread (RECIPIENT_ADDED, arg);
118   return 0;
119 }
120
121 /* The main Invoke function. The return value of this
122    function does not appear to have any effect on outlook
123    although I have read in an example somewhere that you
124    should return S_OK so that outlook continues to handle
125    the event I have not yet seen any effect by returning
126    error values here and no MSDN documentation about the
127    return values.
128 */
129 EVENT_SINK_INVOKE(MailItemEvents)
130 {
131   USE_INVOKE_ARGS
132   TSTART;
133   if (!m_mail)
134     {
135       m_mail = Mail::getMailForItem (m_object);
136       if (!m_mail)
137         {
138           log_error ("%s:%s: mail event without mail object known. Bug.",
139                      SRCNAME, __func__);
140           TRETURN S_OK;
141         }
142     }
143
144   bool is_reply = false;
145   switch(dispid)
146     {
147       case Open:
148         {
149           log_oom ("%s:%s: Open : %p",
150                          SRCNAME, __func__, m_mail);
151           int draft_flags = 0;
152           if (!opt.encrypt_default && !opt.sign_default)
153             {
154               TRETURN S_OK;
155             }
156           LPMESSAGE message = get_oom_base_message (m_object);
157           if (!message)
158             {
159               log_error ("%s:%s: Failed to get message.",
160                          SRCNAME, __func__);
161               TBREAK;
162             }
163           if (opt.encrypt_default)
164             {
165               draft_flags = 1;
166             }
167           if (opt.sign_default)
168             {
169               draft_flags += 2;
170             }
171           set_gpgol_draft_info_flags (message, draft_flags);
172           gpgol_release (message);
173           TBREAK;
174         }
175       case BeforeRead:
176         {
177           log_oom ("%s:%s: BeforeRead : %p",
178                          SRCNAME, __func__, m_mail);
179           if (GpgolAddin::get_instance ()->isShutdown())
180             {
181               log_debug ("%s:%s: Ignoring read after shutdown.",
182                          SRCNAME, __func__);
183               TBREAK;
184             }
185
186           if (m_mail->preProcessMessage_m ())
187             {
188               log_error ("%s:%s: Pre process message failed.",
189                          SRCNAME, __func__);
190             }
191           TBREAK;
192         }
193       case Read:
194         {
195           log_oom ("%s:%s: Read : %p",
196                          SRCNAME, __func__, m_mail);
197           if (!addinsLogged)
198             {
199               // We do it here as this nearly always comes and we want to remove
200               // as much as possible from the startup time.
201               log_addins ();
202               addinsLogged = true;
203             }
204           if (!m_mail->isCryptoMail ())
205             {
206               log_debug ("%s:%s: Non crypto mail %p opened. Updating sigstatus.",
207                          SRCNAME, __func__, m_mail);
208               /* Ensure that no wrong sigstatus is shown */
209               CloseHandle(CreateThread (NULL, 0, delayed_invalidate_ui, (LPVOID) 300, 0,
210                                         NULL));
211               TBREAK;
212             }
213           if (m_mail->setUUID_o ())
214             {
215               log_debug ("%s:%s: Failed to set uuid.",
216                          SRCNAME, __func__);
217               delete m_mail; /* deletes this, too */
218               TRETURN S_OK;
219             }
220           if (m_mail->decryptVerify_o ())
221             {
222               log_error ("%s:%s: Decrypt message failed.",
223                          SRCNAME, __func__);
224             }
225           if (!opt.enable_smime && m_mail->isSMIME_m ())
226             {
227               /* We want to save the mail when it's an smime mail and smime
228                  is disabled to revert it. */
229               log_debug ("%s:%s: S/MIME mail but S/MIME is disabled."
230                          " Need save.",
231                          SRCNAME, __func__);
232               m_mail->setNeedsSave (true);
233             }
234           TBREAK;
235         }
236       case PropertyChange:
237         {
238           if (!parms || parms->cArgs != 1 ||
239               parms->rgvarg[0].vt != VT_BSTR ||
240               !parms->rgvarg[0].bstrVal)
241             {
242               log_error ("%s:%s: Unexpected params.",
243                          SRCNAME, __func__);
244               TBREAK;
245             }
246           const wchar_t *prop_name = parms->rgvarg[0].bstrVal;
247           if (!m_mail->isCryptoMail ())
248             {
249               if (m_mail->hasOverrideMimeData())
250                 {
251                   /* This is a mail created by us. Ignore propchanges. */
252                   TBREAK;
253                 }
254               if (!wcscmp (prop_name, L"To") /* ||
255                   !wcscmp (prop_name, L"BCC") ||
256                   !wcscmp (prop_name, L"CC")
257                   Testing shows that Outlook always sends these three in a row
258                   */)
259                 {
260                   if (opt.autosecure || (m_mail->needs_crypto_m () & 1))
261                     {
262                       /* XXX Racy race. This is a fix for crashes
263                          that happend if a resolved recipient is copied an pasted.
264                          If we then access the recipients object in the Property
265                          Change event we crash. Thus we do the delay dance. */
266                       HANDLE thread = CreateThread (NULL, 0, do_delayed_locate,
267                                                     (LPVOID) m_mail, 0,
268                                                     NULL);
269                       CloseHandle(thread);
270                     }
271                 }
272               TBREAK;
273             }
274           for (const wchar_t **cur = prop_blacklist; *cur; cur++)
275             {
276               if (!wcscmp (prop_name, *cur))
277                 {
278                   log_oom ("%s:%s: Message %p propchange: %ls discarded.",
279                            SRCNAME, __func__, m_object, prop_name);
280                   TRETURN S_OK;
281                 }
282             }
283           log_oom ("%s:%s: Message %p propchange: %ls.",
284                    SRCNAME, __func__, m_object, prop_name);
285
286           if (!wcscmp (prop_name, L"SendUsingAccount"))
287             {
288               bool sent = get_oom_bool (m_object, "Sent");
289               if (sent)
290                 {
291                   log_debug ("%s:%s: Ignoring SendUsingAccount change for sent %p ",
292                              SRCNAME, __func__, m_object);
293                   TRETURN S_OK;
294                 }
295               log_debug ("%s:%s: Message %p looks like send again.",
296                         SRCNAME, __func__, m_object);
297               m_mail->setIsSendAgain (true);
298               TRETURN S_OK;
299             }
300
301           /* We have tried several scenarios to handle propery changes.
302              Only save the property in MAPI and call MAPI SaveChanges
303              worked and did not leak plaintext but this caused outlook
304              still to break the attachments of PGP/MIME Mails into two
305              attachments and add them as winmail.dat so other clients
306              are broken.
307
308              Alternatively reverting the mail, saving the property and
309              then decrypt again also worked a bit but there were some
310              weird side effects and breakages. But this has the usual
311              problem of a revert that the mail is created by outlook and
312              e.g. multipart/signed signatures from most MUA's are broken.
313
314              Some things to try out might be the close approach and then
315              another open or a selection change. But for now we just warn.
316
317              As a workardound a user should make property changes when
318              the mail was not read by us. */
319           if (propchangeWarnShown)
320             {
321               TRETURN S_OK;
322             }
323
324           wchar_t *title = utf8_to_wchar (_("Sorry, that's not possible, yet"));
325           char *fmt;
326           gpgrt_asprintf (&fmt, _("GpgOL has prevented the change to the \"%s\" property.\n"
327                                   "Property changes are not yet handled for crypto messages.\n\n"
328                                   "To workaround this limitation please change the property when the "
329                                   "message is not open in any window and not selected in the "
330                                   "messagelist.\n\nFor example by right clicking but not selecting the message.\n"),
331                           wchar_to_utf8(prop_name));
332           memdbg_alloc (fmt);
333           wchar_t *msg = utf8_to_wchar (fmt);
334           xfree (fmt);
335           MessageBoxW (get_active_hwnd(), msg, title,
336                        MB_ICONINFORMATION | MB_OK);
337           xfree (msg);
338           xfree (title);
339           propchangeWarnShown = true;
340           TRETURN S_OK;
341         }
342       case CustomPropertyChange:
343         {
344           log_oom ("%s:%s: CustomPropertyChange : %p",
345                          SRCNAME, __func__, m_mail);
346           /* TODO */
347           TBREAK;
348         }
349       case Send:
350         {
351           /* This is the only event where we can cancel the send of a
352              mailitem. But it is too early for us to encrypt as the MAPI
353              structures are not yet filled. Crypto based on the
354              Outlook Object Model data did not work as the messages
355              were only sent out empty. See 2b376a48 for a try of
356              this.
357
358              This is why we store send_seen and invoke a save which
359              may result in an error but only after triggering all the
360              behavior we need -> filling mapi structures and invoking the
361              AfterWrite handler where we encrypt.
362
363              If this encryption is successful and we pass the send
364              as then the encrypted data is sent.
365            */
366           log_oom ("%s:%s: Send : %p",
367                          SRCNAME, __func__, m_mail);
368           if (!m_mail->needs_crypto_m () && m_mail->cryptState () == Mail::NoCryptMail)
369             {
370              log_debug ("%s:%s: No crypto neccessary. Passing send for %p obj %p",
371                         SRCNAME, __func__, m_mail, m_object);
372              TBREAK;
373             }
374
375           if (parms->cArgs != 1 || parms->rgvarg[0].vt != (VT_BOOL | VT_BYREF))
376            {
377              log_debug ("%s:%s: Uncancellable send event.",
378                         SRCNAME, __func__);
379              TBREAK;
380            }
381
382           if (m_mail->cryptState () == Mail::NoCryptMail &&
383               m_mail->needs_crypto_m ())
384             {
385               log_debug ("%s:%s: Send event for crypto mail %p saving and starting.",
386                          SRCNAME, __func__, m_mail);
387
388               if (!m_mail->isAsyncCryptDisabled())
389                 {
390                   /* Obtain a reference of the current item. This prevents
391                    * an early unload which would crash Outlook 2013
392                    *
393                    * As it didn't crash when the mail was opened in Outlook Spy this
394                    * mimics that the mail is inspected somewhere else. */
395                   m_mail->refCurrentItem ();
396                 }
397
398               // First contact with a mail to encrypt update
399               // state and oom data.
400               m_mail->updateOOMData_o ();
401
402               m_mail->setCryptState (Mail::NeedsFirstAfterWrite);
403
404               // Check inline response state before the write.
405               m_mail->check_inline_response ();
406               // Save the Mail
407               invoke_oom_method (m_object, "Save", NULL);
408
409               if (!m_mail->isAsyncCryptDisabled ())
410                 {
411                   // The afterwrite in the save should have triggered
412                   // the encryption. We cancel send for our asyncness.
413                   // Cancel send
414                   *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE;
415                   TBREAK;
416                 }
417               else
418                 {
419                   if (m_mail->cryptState () == Mail::NoCryptMail)
420                     {
421                       // Crypto failed or was canceled
422                       log_debug ("%s:%s: Message %p mail %p cancelling send - "
423                                  "Crypto failed or canceled.",
424                                  SRCNAME, __func__, m_object, m_mail);
425                       *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE;
426                       TBREAK;
427                     }
428                   // For inline response we can't trigger send programatically
429                   // so we do the encryption in sync.
430                   if (m_mail->cryptState () == Mail::NeedsUpdateInOOM)
431                     {
432                       m_mail->updateCryptOOM_o ();
433                     }
434                   if (m_mail->cryptState () == Mail::NeedsSecondAfterWrite)
435                     {
436                       m_mail->setCryptState (Mail::WantsSendMIME);
437                     }
438                   if (m_mail->getDoPGPInline () && m_mail->cryptState () != Mail::WantsSendInline)
439                     {
440                       log_debug ("%s:%s: Message %p mail %p cancelling send - "
441                                  "Invalid state.",
442                                  SRCNAME, __func__, m_object, m_mail);
443                       gpgol_bug (m_mail->getWindow (),
444                                  ERR_INLINE_BODY_INV_STATE);
445                       *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE;
446                       TBREAK;
447                     }
448                 }
449             }
450
451           if (m_mail->cryptState () == Mail::WantsSendInline)
452             {
453               if (!m_mail->hasCryptedOrEmptyBody_o ())
454                 {
455                   log_debug ("%s:%s: Message %p mail %p cancelling send - "
456                              "not encrypted or not empty body detected.",
457                              SRCNAME, __func__, m_object, m_mail);
458                   gpgol_bug (m_mail->getWindow (),
459                              ERR_WANTS_SEND_INLINE_BODY);
460                   m_mail->setCryptState (Mail::NoCryptMail);
461                   *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE;
462                   TBREAK;
463                 }
464               log_debug ("%s:%s: Passing send event for no-mime message %p.",
465                          SRCNAME, __func__, m_object);
466               WKSHelper::instance()->allow_notify (1000);
467               TBREAK;
468             }
469
470           if (m_mail->cryptState () == Mail::WantsSendMIME)
471             {
472               if (!m_mail->hasCryptedOrEmptyBody_o ())
473                 {
474 /* The safety checks here trigger too often. Somehow for some
475    users the body is not empty after the encryption but when
476    it is sent it is still sent with the crypto content because
477    the encrypted MIME Structure is used because it is
478    correct in MAPI land.
479
480    For safety reasons enabling the checks might be better but
481    until we figure out why for some users the body replacement
482    does not work we have to disable them. Otherwise GpgOL
483    is unusuable for such users. GnuPG-Bug-Id: T3875
484 */
485 #define DISABLE_SAFTEY_CHECKS
486 #ifndef DISABLE_SAFTEY_CHECKS
487                   gpgol_bug (m_mail->getWindow (),
488                              ERR_WANTS_SEND_MIME_BODY);
489                   log_debug ("%s:%s: Message %p mail %p cancelling send mime - "
490                              "not encrypted or not empty body detected.",
491                              SRCNAME, __func__, m_object, m_mail);
492                   m_mail->setCryptState (Mail::NoCryptMail);
493                   *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE;
494                   TBREAK;
495 #else
496                   log_debug ("%s:%s: Message %p mail %p - "
497                              "not encrypted or not empty body detected - MIME.",
498                              SRCNAME, __func__, m_object, m_mail);
499 #endif
500                 }
501               /* Now we adress T3656 if Outlooks internal S/MIME is somehow
502                * mixed in (even if it is enabled and then disabled) it might
503                * cause strange behavior in that it sends the plain message
504                * and not the encrypted message. Tests have shown that we can
505                * bypass that by calling submit message on our base
506                * message.
507                *
508                * We do this conditionally as our other way of using OOM
509                * to send is proven to work and we don't want to mess
510                * with it.
511                */
512               // Get the Message class.
513               HRESULT hr;
514               LPSPropValue propval = NULL;
515
516               // It's important we use the _not_ base message here.
517               LPMESSAGE message = get_oom_message (m_object);
518               hr = HrGetOneProp ((LPMAPIPROP)message, PR_MESSAGE_CLASS_A, &propval);
519               gpgol_release (message);
520               if (FAILED (hr))
521                 {
522                   log_error ("%s:%s: HrGetOneProp() failed: hr=%#lx\n",
523                              SRCNAME, __func__, hr);
524                   gpgol_release (message);
525                   TBREAK;
526                 }
527               if (propval->Value.lpszA && !strstr (propval->Value.lpszA, "GpgOL"))
528                 {
529                   // Does not have a message class by us.
530                   log_debug ("%s:%s: Message %p - No GpgOL Message class after encryption. cls is: '%s'",
531                              SRCNAME, __func__, m_object, propval->Value.lpszA);
532                   log_debug ("%s:%s: Message %p - Activating T3656 Workaround",
533                              SRCNAME, __func__, m_object);
534                   message = get_oom_base_message (m_object);
535                   if (message)
536                     {
537                       // It's important we use the _base_ message here.
538                       mapi_save_changes (message, FORCE_SAVE);
539                       message->SubmitMessage(0);
540                       gpgol_release (message);
541                       // Close the composer and trigger unloads
542                       CloseHandle(CreateThread (NULL, 0, close_mail, (LPVOID) m_mail, 0,
543                                                 NULL));
544                     }
545                   else
546                     {
547                       gpgol_bug (nullptr,
548                                  ERR_GET_BASE_MSG_FAILED);
549                     }
550                   // Cancel send
551                   *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE;
552                 }
553               MAPIFreeBuffer (propval);
554               if (*(parms->rgvarg[0].pboolVal) == VARIANT_TRUE)
555                 {
556                   TBREAK;
557                 }
558               log_debug ("%s:%s: Passing send event for mime-encrypted message %p.",
559                          SRCNAME, __func__, m_object);
560               WKSHelper::instance()->allow_notify (1000);
561               TBREAK;
562             }
563           else
564             {
565               log_debug ("%s:%s: Message %p cancelling send - "
566                          "crypto or second save failed.",
567                          SRCNAME, __func__, m_object);
568               *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE;
569             }
570           TRETURN S_OK;
571         }
572       case Write:
573         {
574           log_oom ("%s:%s: Write : %p",
575                          SRCNAME, __func__, m_mail);
576           /* This is a bit strange. We sometimes get multiple write events
577              without a read in between. When we access the message in
578              the second event it fails and if we cancel the event outlook
579              crashes. So we have keep the m_needs_wipe state variable
580              to keep track of that. */
581           if (parms->cArgs != 1 || parms->rgvarg[0].vt != (VT_BOOL | VT_BYREF))
582            {
583              /* This happens in the weird case */
584              log_debug ("%s:%s: Uncancellable write event.",
585                         SRCNAME, __func__);
586              TBREAK;
587            }
588
589           if (m_mail->passWrite())
590             {
591               log_debug ("%s:%s: Passing write because passNextWrite was set for %p",
592                          SRCNAME, __func__, m_mail);
593               TBREAK;
594             }
595
596           if (m_mail->isCryptoMail () && !m_mail->needsSave ())
597             {
598               Mail *last_mail = Mail::getLastMail ();
599               if (Mail::isValidPtr (last_mail))
600                 {
601                   /* We want to identify here if there was a mail created that
602                      should receive the contents of this mail. For this we check
603                      for a write in the same loop as a mail creation.
604                      Now when switching from one mail to another this is also what
605                      happens. The new mail is loaded and the old mail is written.
606                      To distinguish the two we check that the new mail does not have
607                      an entryID, a Subject and No Size. Maybe just size or entryID
608                      would be enough but better save then sorry.
609
610                      Security consideration: Worst case we pass the write here but
611                      an unload follows before we get the scheduled revert. This
612                      would leak plaintext. But does not happen in our tests.
613
614                      Similarly if we crash or Outlook is closed before we see this
615                      revert. But as we immediately revert after the write this should
616                      also not happen. */
617                   const std::string lastSubject = last_mail->getSubject_o ();
618                   char *lastEntryID = get_oom_string (last_mail->item (), "EntryID");
619                   int lastSize = get_oom_int (last_mail->item (), "Size");
620                   std::string lastEntryStr;
621                   if (lastEntryID)
622                     {
623                       lastEntryStr = lastEntryID;
624                       xfree (lastEntryID);
625                     }
626
627                   if (!lastSize && !lastEntryStr.size () && !lastSubject.size ())
628                     {
629                       log_debug ("%s:%s: Write in the same loop as empty load."
630                                  " Pass but schedule revert.",
631                                  SRCNAME, __func__);
632
633                       /* This might be a forward. So don't invalidate yet. */
634
635                       // Mail::clearLastMail ();
636
637                       do_in_ui_thread_async (REVERT_MAIL, m_mail);
638                       TRETURN S_OK;
639                     }
640                 }
641               /* We cancel the write event to stop outlook from excessively
642                  syncing our changes.
643                  if smime support is disabled and we still have an smime
644                  mail we also don't want to cancel the write event
645                  to enable reverting this mails.
646                  */
647               *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE;
648               log_debug ("%s:%s: Canceling write event.",
649                          SRCNAME, __func__);
650               TRETURN S_OK;
651             }
652
653           if (m_mail->isCryptoMail () && m_mail->needsSave () &&
654               m_mail->revert_o ())
655             {
656               /* An error cleaning the mail should not happen normally.
657                  But just in case there is an error we cancel the
658                  write here. */
659               log_debug ("%s:%s: Failed to remove plaintext.",
660                          SRCNAME, __func__);
661               *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE;
662             }
663
664           if (!m_mail->isCryptoMail () && m_mail->is_forwarded_crypto_mail () &&
665               !m_mail->needs_crypto_m () && m_mail->cryptState () == Mail::NoCryptMail)
666             {
667               /* We are sure now that while this is a forward of an encrypted
668                * mail that the forward should not be signed or encrypted. So
669                * it's not constructed by us. We need to remove our attachments
670                * though so that they are not included in the forward. */
671               log_debug ("%s:%s: Writing unencrypted forward of crypt mail. "
672                          "Removing attachments. mail: %p item: %p",
673                          SRCNAME, __func__, m_mail, m_object);
674               if (m_mail->removeOurAttachments_o ())
675                 {
676                   // Worst case we forward some encrypted data here not
677                   // a security problem, so let it pass.
678                   log_error ("%s:%s: Failed to remove our attachments.",
679                              SRCNAME, __func__);
680                 }
681               /* Remove marker because we did this now. */
682               m_mail->setIsForwardedCryptoMail (false);
683             }
684
685           log_debug ("%s:%s: Passing write event.",
686                      SRCNAME, __func__);
687           m_mail->setNeedsSave (false);
688           TBREAK;
689         }
690       case AfterWrite:
691         {
692           log_oom ("%s:%s: AfterWrite : %p",
693                          SRCNAME, __func__, m_mail);
694           if (m_mail->cryptState () == Mail::NeedsFirstAfterWrite)
695             {
696               /* Seen the first after write. Advance the state */
697               m_mail->setCryptState (Mail::NeedsActualCrypt);
698               if (m_mail->encryptSignStart_o ())
699                 {
700                   log_debug ("%s:%s: Encrypt sign start failed.",
701                              SRCNAME, __func__);
702                   m_mail->setCryptState (Mail::NoCryptMail);
703                 }
704               TRETURN S_OK;
705             }
706           if (m_mail->cryptState () == Mail::NeedsSecondAfterWrite)
707             {
708               m_mail->setCryptState (Mail::NeedsUpdateInMAPI);
709               m_mail->updateCryptMAPI_m ();
710               TRETURN S_OK;
711             }
712           TBREAK;
713         }
714       case Close:
715         {
716           log_oom ("%s:%s: Close : %p",
717                          SRCNAME, __func__, m_mail);
718           if (m_mail->isCryptoMail ())
719             {
720               /* Close. This happens when an Opened mail is closed.
721                  To prevent the question of wether or not to save the changes
722                  (Which would save the decrypted data without an event to
723                  prevent it) we cancel the close and then either close it
724                  with discard changes or revert / save it.
725                  Contrary to documentation we can invoke close from
726                  close.
727                  */
728               if (parms->cArgs != 1 || parms->rgvarg[0].vt != (VT_BOOL | VT_BYREF))
729                 {
730                   /* This happens in the weird case */
731                   log_debug ("%s:%s: Uncancellable close event.",
732                              SRCNAME, __func__);
733                   TBREAK;
734                 }
735               if (m_mail->getCloseTriggered ())
736                 {
737                   /* Our close with discard changes, pass through */
738                   m_mail->setCloseTriggered (false);
739                   TRETURN S_OK;
740                 }
741               *(parms->rgvarg[0].pboolVal) = VARIANT_TRUE;
742               log_oom ("%s:%s: Canceling close event.",
743                              SRCNAME, __func__);
744               if (Mail::close(m_mail))
745                 {
746                   log_debug ("%s:%s: Close request failed.",
747                              SRCNAME, __func__);
748                 }
749             }
750           TRETURN S_OK;
751         }
752       case Unload:
753         {
754           log_oom ("%s:%s: Unload : %p",
755                          SRCNAME, __func__, m_mail);
756           log_debug ("%s:%s: Removing Mail for message: %p.",
757                      SRCNAME, __func__, m_object);
758           delete m_mail;
759           log_oom ("%s:%s: deletion done",
760                          SRCNAME, __func__);
761           memdbg_dump ();
762           TRETURN S_OK;
763         }
764       /* Fallthrough */
765       case ReplyAll:
766       case Reply:
767           is_reply = true;
768           __attribute__ ((fallthrough));
769       case Forward:
770         {
771           log_oom ("%s:%s: %s : %p",
772                          SRCNAME, __func__, is_reply ? "reply" : "forward", m_mail);
773           int draft_flags = 0;
774           if (opt.encrypt_default)
775             {
776               draft_flags = 1;
777             }
778           if (opt.sign_default)
779             {
780               draft_flags += 2;
781             }
782           bool is_crypto_mail = m_mail->isCryptoMail ();
783
784           /* If it is a crypto mail and the settings should not be taken
785            * from the crypto mail and always encrypt / sign is on. Or
786            * If it is not a crypto mail and we have automaticalls sign_encrypt. */
787           if ((is_crypto_mail && !opt.reply_crypt && draft_flags) ||
788               (!is_crypto_mail && draft_flags))
789             {
790               /* Check if we can use the dispval */
791                 if (parms->cArgs == 2 && parms->rgvarg[1].vt == (VT_DISPATCH) &&
792                     parms->rgvarg[0].vt == (VT_BOOL | VT_BYREF))
793                 {
794                   LPMESSAGE msg = get_oom_base_message (parms->rgvarg[1].pdispVal);
795                   if (msg)
796                     {
797                       set_gpgol_draft_info_flags (msg, draft_flags);
798                       gpgol_release (msg);
799                     }
800                   else
801                     {
802                       log_error ("%s:%s: Failed to get base message.",
803                                  SRCNAME, __func__);
804                     }
805                 }
806               else
807                 {
808                   log_error ("%s:%s: Unexpected parameters.",
809                              SRCNAME, __func__);
810                 }
811             }
812
813           if (!is_crypto_mail)
814             {
815               /* Replys to non crypto mails do not interest us anymore. */
816               TBREAK;
817             }
818
819           Mail *last_mail = Mail::getLastMail ();
820           if (Mail::isValidPtr (last_mail))
821             {
822               /* We want to identify here if there was a mail created that
823                  should receive the contents of this mail. For this we check
824                  for a forward in the same loop as a mail creation.
825
826                  We need to do it this complicated and can't just use
827                  get_mail_for_item because the mailitem pointer we get here
828                  is a different one then the one with which the mail was loaded.
829               */
830               char *lastEntryID = get_oom_string (last_mail->item (), "EntryID");
831               int lastSize = get_oom_int (last_mail->item (), "Size");
832               std::string lastEntryStr;
833               if (lastEntryID)
834                 {
835                   lastEntryStr = lastEntryID;
836                   xfree (lastEntryID);
837                 }
838
839               if (!lastSize && !lastEntryStr.size ())
840                 {
841                   if (!is_reply)
842                     {
843                       log_debug ("%s:%s: Forward in the same loop as empty "
844                                  "load Marking %p (item %p) as forwarded.",
845                                  SRCNAME, __func__, last_mail,
846                                  last_mail->item ());
847
848                       last_mail->setIsForwardedCryptoMail (true);
849                     }
850                   else
851                     {
852                       log_debug ("%s:%s: Reply in the same loop as empty "
853                                  "load Marking %p (item %p) as reply.",
854                                  SRCNAME, __func__, last_mail,
855                                  last_mail->item ());
856                     }
857                   if (m_mail->isBlockHTML ())
858                     {
859                       std::string buf;
860                       /** TRANSLATORS: Part of a warning dialog that disallows
861                         reply and forward with contents */
862                       buf = is_reply ? _("You are replying to an unsigned S/MIME "
863                                          "email.") :
864                                        _("You are forwarding an unsigned S/MIME "
865                                          "email.");
866                       buf +="\n\n";
867                       buf += _("In this version of S/MIME an attacker could "
868                                "use the missing signature to have you "
869                                "decrypt contents from a different, otherwise "
870                                "completely unrelated email and place it in the "
871                                "quote so they can get hold of it.\n"
872                                "This is why we only allow quoting to be done manually.");
873                       buf += "\n\n";
874                       buf += _("Please copy the relevant contents and insert "
875                                "them into the new email.");
876
877                       gpgol_message_box (get_active_hwnd (), buf.c_str(),
878                                          _("GpgOL"), MB_OK);
879
880                       do_in_ui_thread_async (CLEAR_REPLY_FORWARD, last_mail, 1000);
881                     }
882                 }
883               // We can now invalidate the last mail
884               Mail::clearLastMail ();
885             }
886
887           log_oom ("%s:%s: Reply Forward ReplyAll: %p",
888                          SRCNAME, __func__, m_mail);
889           if (!opt.reply_crypt)
890             {
891               TBREAK;
892             }
893           int crypto_flags = 0;
894           if (!(crypto_flags = m_mail->getCryptoFlags ()))
895             {
896               TBREAK;
897             }
898           if (parms->cArgs != 2 || parms->rgvarg[1].vt != (VT_DISPATCH) ||
899               parms->rgvarg[0].vt != (VT_BOOL | VT_BYREF))
900             {
901               /* This happens in the weird case */
902               log_debug ("%s:%s: Unexpected args %i %x %x named: %i",
903                          SRCNAME, __func__, parms->cArgs, parms->rgvarg[0].vt, parms->rgvarg[1].vt,
904                          parms->cNamedArgs);
905               TBREAK;
906             }
907           LPMESSAGE msg = get_oom_base_message (parms->rgvarg[1].pdispVal);
908           if (!msg)
909             {
910               log_debug ("%s:%s: Failed to get base message",
911                          SRCNAME, __func__);
912               TBREAK;
913             }
914           set_gpgol_draft_info_flags (msg, crypto_flags);
915           gpgol_release (msg);
916           TBREAK;
917         }
918       case AttachmentRemove:
919         {
920           log_oom ("%s:%s: AttachmentRemove: %p",
921                          SRCNAME, __func__, m_mail);
922           if (!m_mail->isCryptoMail () || attachRemoveWarnShown ||
923               m_mail->attachmentRemoveWarningDisabled ())
924             {
925               TRETURN S_OK;
926             }
927           gpgol_message_box (get_active_hwnd (),
928                              _("Attachments are part of the crypto message.\nThey "
929                                "can't be permanently removed and will be shown again the next "
930                                "time this message is opened."),
931                              _("Sorry, that's not possible, yet"), MB_OK);
932           attachRemoveWarnShown = true;
933           TRETURN S_OK;
934         }
935
936       default:
937         log_oom ("%s:%s: Message:%p Unhandled Event: %lx \n",
938                        SRCNAME, __func__, m_object, dispid);
939     }
940   TRETURN S_OK;
941 }
942 END_EVENT_SINK(MailItemEvents, IID_MailItemEvents)