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