a75225f2658969bef43959d75e10164ad5cfeff4
[gpgol.git] / src / mail.cpp
1 /* @file mail.h
2  * @brief High level class to work with Outlook Mailitems.
3  *
4  * Copyright (C) 2015, 2016 by Bundesamt für Sicherheit in der Informationstechnik
5  * Software engineering by Intevation GmbH
6  *
7  * This file is part of GpgOL.
8  *
9  * GpgOL is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * GpgOL is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, see <http://www.gnu.org/licenses/>.
21  */
22
23 #include "config.h"
24
25 #include "categorymanager.h"
26 #include "dialogs.h"
27 #include "common.h"
28 #include "mail.h"
29 #include "eventsinks.h"
30 #include "attachment.h"
31 #include "mapihelp.h"
32 #include "mimemaker.h"
33 #include "revert.h"
34 #include "gpgoladdin.h"
35 #include "mymapitags.h"
36 #include "parsecontroller.h"
37 #include "cryptcontroller.h"
38 #include "windowmessages.h"
39 #include "mlang-charset.h"
40 #include "wks-helper.h"
41 #include "keycache.h"
42 #include "cpphelp.h"
43 #include "addressbook.h"
44
45 #include <gpgme++/configuration.h>
46 #include <gpgme++/tofuinfo.h>
47 #include <gpgme++/verificationresult.h>
48 #include <gpgme++/decryptionresult.h>
49 #include <gpgme++/key.h>
50 #include <gpgme++/context.h>
51 #include <gpgme++/keylistresult.h>
52 #include <gpg-error.h>
53
54 #include <map>
55 #include <unordered_map>
56 #include <set>
57 #include <vector>
58 #include <memory>
59 #include <sstream>
60
61 #undef _
62 #define _(a) utf8_gettext (a)
63
64 using namespace GpgME;
65
66 static std::map<LPDISPATCH, Mail*> s_mail_map;
67 static std::map<std::string, Mail*> s_uid_map;
68 static std::map<std::string, LPDISPATCH> s_folder_events_map;
69 static std::set<std::string> uids_searched;
70
71 GPGRT_LOCK_DEFINE (mail_map_lock);
72 GPGRT_LOCK_DEFINE (uid_map_lock);
73
74 static Mail *s_last_mail;
75
76 #define COPYBUFSIZE (8 * 1024)
77
78 Mail::Mail (LPDISPATCH mailitem) :
79     m_mailitem(mailitem),
80     m_currentItemRef(nullptr),
81     m_processed(false),
82     m_needs_wipe(false),
83     m_needs_save(false),
84     m_crypt_successful(false),
85     m_is_smime(false),
86     m_is_smime_checked(false),
87     m_is_signed(false),
88     m_is_valid(false),
89     m_close_triggered(false),
90     m_is_html_alternative(false),
91     m_needs_encrypt(false),
92     m_moss_position(0),
93     m_crypto_flags(0),
94     m_cached_html_body(nullptr),
95     m_cached_plain_body(nullptr),
96     m_type(MSGTYPE_UNKNOWN),
97     m_do_inline(false),
98     m_is_gsuite(false),
99     m_crypt_state(NoCryptMail),
100     m_window(nullptr),
101     m_async_crypt_disabled(false),
102     m_is_forwarded_crypto_mail(false),
103     m_is_reply_crypto_mail(false),
104     m_is_send_again(false),
105     m_disable_att_remove_warning(false),
106     m_manual_crypto_opts(false),
107     m_first_autosecure_check(true),
108     m_locate_count(0),
109     m_pass_write(false),
110     m_locate_in_progress(false),
111     m_is_junk(false)
112 {
113   TSTART;
114   if (getMailForItem (mailitem))
115     {
116       log_error ("Mail object for item: %p already exists. Bug.",
117                  mailitem);
118       TRETURN;
119     }
120
121   m_event_sink = install_MailItemEvents_sink (mailitem);
122   if (!m_event_sink)
123     {
124       /* Should not happen but in that case we don't add us to the list
125          and just release the Mail item. */
126       log_error ("%s:%s: Failed to install MailItemEvents sink.",
127                  SRCNAME, __func__);
128       gpgol_release(mailitem);
129       TRETURN;
130     }
131   gpgol_lock (&mail_map_lock);
132   s_mail_map.insert (std::pair<LPDISPATCH, Mail *> (mailitem, this));
133   gpgol_unlock (&mail_map_lock);
134   s_last_mail = this;
135   memdbg_ctor ("Mail");
136   TRETURN;
137 }
138
139 GPGRT_LOCK_DEFINE(dtor_lock);
140
141 // static
142 void
143 Mail::lockDelete ()
144 {
145   TSTART;
146   gpgol_lock (&dtor_lock);
147   TRETURN;
148 }
149
150 // static
151 void
152 Mail::unlockDelete ()
153 {
154   TSTART;
155   gpgol_unlock (&dtor_lock);
156   TRETURN;
157 }
158
159 Mail::~Mail()
160 {
161   TSTART;
162   /* This should fix a race condition where the mail is
163      deleted before the parser is accessed in the decrypt
164      thread. The shared_ptr of the parser then ensures
165      that the parser is alive even if the mail is deleted
166      while parsing. */
167   gpgol_lock (&dtor_lock);
168   memdbg_dtor ("Mail");
169   log_oom ("%s:%s: dtor: Mail: %p item: %p",
170                  SRCNAME, __func__, this, m_mailitem);
171   std::map<LPDISPATCH, Mail *>::iterator it;
172
173   log_oom ("%s:%s: Detaching event sink",
174                  SRCNAME, __func__);
175   detach_MailItemEvents_sink (m_event_sink);
176   gpgol_release(m_event_sink);
177
178   log_oom ("%s:%s: Erasing mail",
179                  SRCNAME, __func__);
180   gpgol_lock (&mail_map_lock);
181   it = s_mail_map.find(m_mailitem);
182   if (it != s_mail_map.end())
183     {
184       s_mail_map.erase (it);
185     }
186   gpgol_unlock (&mail_map_lock);
187
188   if (!m_uuid.empty())
189     {
190       gpgol_lock (&uid_map_lock);
191       auto it2 = s_uid_map.find(m_uuid);
192       if (it2 != s_uid_map.end())
193         {
194           s_uid_map.erase (it2);
195         }
196       gpgol_unlock (&uid_map_lock);
197     }
198
199   log_oom ("%s:%s: removing categories",
200                  SRCNAME, __func__);
201   removeCategories_o ();
202
203   log_oom ("%s:%s: releasing mailitem",
204                  SRCNAME, __func__);
205   gpgol_release(m_mailitem);
206   xfree (m_cached_html_body);
207   xfree (m_cached_plain_body);
208   if (!m_uuid.empty())
209     {
210       log_oom ("%s:%s: destroyed: %p uuid: %s",
211                      SRCNAME, __func__, this, m_uuid.c_str());
212     }
213   else
214     {
215       log_oom ("%s:%s: non crypto (or sent) mail: %p destroyed",
216                      SRCNAME, __func__, this);
217     }
218   log_oom ("%s:%s: nulling shared pointer",
219                  SRCNAME, __func__);
220   m_parser = nullptr;
221   m_crypter = nullptr;
222
223   releaseCurrentItem();
224   gpgol_unlock (&dtor_lock);
225   log_oom ("%s:%s: returning",
226                  SRCNAME, __func__);
227   TRETURN;
228 }
229
230 //static
231 Mail *
232 Mail::getMailForItem (LPDISPATCH mailitem)
233 {
234   TSTART;
235   if (!mailitem)
236     {
237       TRETURN NULL;
238     }
239   std::map<LPDISPATCH, Mail *>::iterator it;
240   gpgol_lock (&mail_map_lock);
241   it = s_mail_map.find(mailitem);
242   gpgol_unlock (&mail_map_lock);
243   if (it == s_mail_map.end())
244     {
245       TRETURN NULL;
246     }
247   TRETURN it->second;
248 }
249
250 //static
251 Mail *
252 Mail::getMailForUUID (const char *uuid)
253 {
254   TSTART;
255   if (!uuid)
256     {
257       TRETURN NULL;
258     }
259   gpgol_lock (&uid_map_lock);
260   auto it = s_uid_map.find(std::string(uuid));
261   gpgol_unlock (&uid_map_lock);
262   if (it == s_uid_map.end())
263     {
264       TRETURN NULL;
265     }
266   TRETURN it->second;
267 }
268
269 //static
270 bool
271 Mail::isValidPtr (const Mail *mail)
272 {
273   TSTART;
274   gpgol_lock (&mail_map_lock);
275   auto it = s_mail_map.begin();
276   while (it != s_mail_map.end())
277     {
278       if (it->second == mail)
279         {
280           gpgol_unlock (&mail_map_lock);
281           TRETURN true;
282         }
283       ++it;
284     }
285   gpgol_unlock (&mail_map_lock);
286   TRETURN false;
287 }
288
289 int
290 Mail::preProcessMessage_m ()
291 {
292   TSTART;
293   LPMESSAGE message = get_oom_base_message (m_mailitem);
294   if (!message)
295     {
296       log_error ("%s:%s: Failed to get base message.",
297                  SRCNAME, __func__);
298       TRETURN 0;
299     }
300   log_oom ("%s:%s: GetBaseMessage OK for %p.",
301                  SRCNAME, __func__, m_mailitem);
302   /* Change the message class here. It is important that
303      we change the message class in the before read event
304      regardless if it is already set to one of GpgOL's message
305      classes. Changing the message class (even if we set it
306      to the same value again that it already has) causes
307      Outlook to reconsider what it "knows" about a message
308      and reread data from the underlying base message. */
309   mapi_change_message_class (message, 1, &m_type);
310
311   if (m_type == MSGTYPE_UNKNOWN)
312     {
313       gpgol_release (message);
314       TRETURN 0;
315     }
316
317   /* Create moss attachments here so that they are properly
318      hidden when the item is read into the model. */
319   m_moss_position = mapi_mark_or_create_moss_attach (message, m_type);
320   if (!m_moss_position)
321     {
322       log_error ("%s:%s: Failed to find moss attachment.",
323                  SRCNAME, __func__);
324       m_type = MSGTYPE_UNKNOWN;
325     }
326
327   gpgol_release (message);
328   TRETURN 0;
329 }
330
331 static LPDISPATCH
332 get_attachment_o (LPDISPATCH mailitem, int pos)
333 {
334   TSTART;
335   LPDISPATCH attachment;
336   LPDISPATCH attachments = get_oom_object (mailitem, "Attachments");
337   if (!attachments)
338     {
339       log_debug ("%s:%s: Failed to get attachments.",
340                  SRCNAME, __func__);
341       TRETURN NULL;
342     }
343
344   std::string item_str;
345   int count = get_oom_int (attachments, "Count");
346   if (count < 1)
347     {
348       log_debug ("%s:%s: Invalid attachment count: %i.",
349                  SRCNAME, __func__, count);
350       gpgol_release (attachments);
351       TRETURN NULL;
352     }
353   if (pos > 0)
354     {
355       item_str = std::string("Item(") + std::to_string(pos) + ")";
356     }
357   else
358     {
359       item_str = std::string("Item(") + std::to_string(count) + ")";
360     }
361   attachment = get_oom_object (attachments, item_str.c_str());
362   gpgol_release (attachments);
363
364   TRETURN attachment;
365 }
366
367 /** Helper to check that all attachments are hidden, to be
368   called before crypto. */
369 int
370 Mail::checkAttachments_o () const
371 {
372   TSTART;
373   LPDISPATCH attachments = get_oom_object (m_mailitem, "Attachments");
374   if (!attachments)
375     {
376       log_debug ("%s:%s: Failed to get attachments.",
377                  SRCNAME, __func__);
378       TRETURN 1;
379     }
380   int count = get_oom_int (attachments, "Count");
381   if (!count)
382     {
383       gpgol_release (attachments);
384       TRETURN 0;
385     }
386
387   std::string message;
388
389   if (isEncrypted () && isSigned ())
390     {
391       message += _("Not all attachments were encrypted or signed.\n"
392                    "The unsigned / unencrypted attachments are:\n\n");
393     }
394   else if (isSigned ())
395     {
396       message += _("Not all attachments were signed.\n"
397                    "The unsigned attachments are:\n\n");
398     }
399   else if (isEncrypted ())
400     {
401       message += _("Not all attachments were encrypted.\n"
402                    "The unencrypted attachments are:\n\n");
403     }
404   else
405     {
406       gpgol_release (attachments);
407       TRETURN 0;
408     }
409
410   bool foundOne = false;
411
412   for (int i = 1; i <= count; i++)
413     {
414       std::string item_str;
415       item_str = std::string("Item(") + std::to_string (i) + ")";
416       LPDISPATCH oom_attach = get_oom_object (attachments, item_str.c_str ());
417       if (!oom_attach)
418         {
419           log_error ("%s:%s: Failed to get attachment.",
420                      SRCNAME, __func__);
421           continue;
422         }
423       VARIANT var;
424       VariantInit (&var);
425       if (get_pa_variant (oom_attach, PR_ATTACHMENT_HIDDEN_DASL, &var) ||
426           (var.vt == VT_BOOL && var.boolVal == VARIANT_FALSE))
427         {
428           foundOne = true;
429           char *dispName = get_oom_string (oom_attach, "DisplayName");
430           message += dispName ? dispName : "Unknown";
431           xfree (dispName);
432           message += "\n";
433         }
434       VariantClear (&var);
435       gpgol_release (oom_attach);
436     }
437   gpgol_release (attachments);
438   if (foundOne)
439     {
440       message += "\n";
441       message += _("Note: The attachments may be encrypted or signed "
442                     "on a file level but the GpgOL status does not apply to them.");
443       wchar_t *wmsg = utf8_to_wchar (message.c_str ());
444       wchar_t *wtitle = utf8_to_wchar (_("GpgOL Warning"));
445       MessageBoxW (get_active_hwnd (), wmsg, wtitle,
446                    MB_ICONWARNING|MB_OK);
447       xfree (wmsg);
448       xfree (wtitle);
449     }
450   TRETURN 0;
451 }
452
453 /** Get the cipherstream of the mailitem. */
454 static LPSTREAM
455 get_attachment_stream_o (LPDISPATCH mailitem, int pos)
456 {
457   TSTART;
458   if (!pos)
459     {
460       log_debug ("%s:%s: Called with zero pos.",
461                  SRCNAME, __func__);
462       TRETURN NULL;
463     }
464   LPDISPATCH attachment = get_attachment_o (mailitem, pos);
465   LPSTREAM stream = NULL;
466
467   if (!attachment)
468     {
469       // For opened messages that have ms-tnef type we
470       // create the moss attachment but don't find it
471       // in the OOM. Try to find it through MAPI.
472       HRESULT hr;
473       log_debug ("%s:%s: Failed to find MOSS Attachment. "
474                  "Fallback to MAPI.", SRCNAME, __func__);
475       LPMESSAGE message = get_oom_message (mailitem);
476       if (!message)
477         {
478           log_debug ("%s:%s: Failed to get MAPI Interface.",
479                      SRCNAME, __func__);
480           TRETURN NULL;
481         }
482       hr = gpgol_openProperty (message, PR_BODY_A, &IID_IStream, 0, 0,
483                                (LPUNKNOWN*)&stream);
484       gpgol_release (message);
485       if (hr)
486         {
487           log_debug ("%s:%s: OpenProperty failed: hr=%#lx",
488                      SRCNAME, __func__, hr);
489           TRETURN NULL;
490         }
491       TRETURN stream;
492     }
493
494   LPATTACH mapi_attachment = NULL;
495
496   mapi_attachment = (LPATTACH) get_oom_iunknown (attachment,
497                                                  "MapiObject");
498   gpgol_release (attachment);
499   if (!mapi_attachment)
500     {
501       log_debug ("%s:%s: Failed to get MapiObject of attachment: %p",
502                  SRCNAME, __func__, attachment);
503       TRETURN NULL;
504     }
505   if (FAILED (gpgol_openProperty (mapi_attachment, PR_ATTACH_DATA_BIN,
506                                   &IID_IStream, 0, MAPI_MODIFY,
507                                   (LPUNKNOWN*) &stream)))
508     {
509       log_debug ("%s:%s: Failed to open stream for mapi_attachment: %p",
510                  SRCNAME, __func__, mapi_attachment);
511       gpgol_release (mapi_attachment);
512     }
513   gpgol_release (mapi_attachment);
514   TRETURN stream;
515 }
516
517 #if 0
518
519 This should work. But Outlook says no. See the comment in set_pa_variant
520 about this. I left the code here as an example how to work with
521 safearrays and how this probably should work.
522
523 static int
524 copy_data_property(LPDISPATCH target, std::shared_ptr<Attachment> attach)
525 {
526   TSTART;
527   VARIANT var;
528   VariantInit (&var);
529
530   /* Get the size */
531   off_t size = attach->get_data ().seek (0, SEEK_END);
532   attach->get_data ().seek (0, SEEK_SET);
533
534   if (!size)
535     {
536       TRACEPOINT;
537       TRETURN 1;
538     }
539
540   if (!get_pa_variant (target, PR_ATTACH_DATA_BIN_DASL, &var))
541     {
542       log_debug("Have variant. type: %x", var.vt);
543     }
544   else
545     {
546       log_debug("failed to get variant.");
547     }
548
549   /* Set the type to an array of unsigned chars (OLE SAFEARRAY) */
550   var.vt = VT_ARRAY | VT_UI1;
551
552   /* Set up the bounds structure */
553   SAFEARRAYBOUND rgsabound[1];
554   rgsabound[0].cElements = static_cast<unsigned long> (size);
555   rgsabound[0].lLbound = 0;
556
557   /* Create an OLE SAFEARRAY */
558   var.parray = SafeArrayCreate (VT_UI1, 1, rgsabound);
559   if (var.parray == NULL)
560     {
561       TRACEPOINT;
562       VariantClear(&var);
563       TRETURN 1;
564     }
565
566   void *buffer = NULL;
567   /* Get a safe pointer to the array */
568   if (SafeArrayAccessData(var.parray, &buffer) != S_OK)
569     {
570       TRACEPOINT;
571       VariantClear(&var);
572       TRETURN 1;
573     }
574
575   /* Copy data to it */
576   size_t nread = attach->get_data ().read (buffer, static_cast<size_t> (size));
577
578   if (nread != static_cast<size_t> (size))
579     {
580       TRACEPOINT;
581       VariantClear(&var);
582       TRETURN 1;
583     }
584
585   /*/ Unlock the variant data */
586   if (SafeArrayUnaccessData(var.parray) != S_OK)
587     {
588       TRACEPOINT;
589       VariantClear(&var);
590       TRETURN 1;
591     }
592
593   if (set_pa_variant (target, PR_ATTACH_DATA_BIN_DASL, &var))
594     {
595       TRACEPOINT;
596       VariantClear(&var);
597       TRETURN 1;
598     }
599
600   VariantClear(&var);
601   TRETURN 0;
602 }
603 #endif
604
605 static int
606 copy_attachment_to_file (std::shared_ptr<Attachment> att, HANDLE hFile)
607 {
608   TSTART;
609   char copybuf[COPYBUFSIZE];
610   size_t nread;
611
612   /* Security considerations: Writing the data to a temporary
613      file is necessary as neither MAPI manipulation works in the
614      read event to transmit the data nor Property Accessor
615      works (see above). From a security standpoint there is a
616      short time where the temporary files are on disk. Tempdir
617      should be protected so that only the user can read it. Thus
618      we have a local attack that could also take the data out
619      of Outlook. FILE_SHARE_READ is necessary so that outlook
620      can read the file.
621
622      A bigger concern is that the file is manipulated
623      by another software to fake the signature state. So
624      we keep the write exlusive to us.
625
626      We delete the file before closing the write file handle.
627   */
628
629   /* Make sure we start at the beginning */
630   att->get_data ().seek (0, SEEK_SET);
631   while ((nread = att->get_data ().read (copybuf, COPYBUFSIZE)))
632     {
633       DWORD nwritten;
634       if (!WriteFile (hFile, copybuf, nread, &nwritten, NULL))
635         {
636           log_error ("%s:%s: Failed to write in tmp attachment.",
637                      SRCNAME, __func__);
638           TRETURN 1;
639         }
640       if (nread != nwritten)
641         {
642           log_error ("%s:%s: Write truncated.",
643                      SRCNAME, __func__);
644           TRETURN 1;
645         }
646     }
647   TRETURN 0;
648 }
649
650 /** Sets some meta data on the last attachment added. The meta
651   data is taken from the attachment object. */
652 static int
653 fixup_last_attachment_o (LPDISPATCH mail,
654                          std::shared_ptr<Attachment> attachment)
655 {
656   TSTART;
657   /* Currently we only set content id */
658   if (attachment->get_content_id ().empty())
659     {
660       log_debug ("%s:%s: Content id not found.",
661                  SRCNAME, __func__);
662       TRETURN 0;
663     }
664
665   LPDISPATCH attach = get_attachment_o (mail, -1);
666   if (!attach)
667     {
668       log_error ("%s:%s: No attachment.",
669                  SRCNAME, __func__);
670       TRETURN 1;
671     }
672   const std::string cid = attachment->get_content_id ();
673   int ret = put_pa_string (attach,
674                            PR_ATTACH_CONTENT_ID_DASL,
675                            cid.c_str());
676
677   log_debug ("%s:%s: Set attachment content id to: '%s'",
678              SRCNAME, __func__, anonstr (cid.c_str()));
679   if (ret)
680     {
681       log_error ("%s:%s: Failed.", SRCNAME, __func__);
682       gpgol_release (attach);
683     }
684 #if 0
685
686   The following was an experiement to delete the ATTACH_FLAGS values
687   so that we are not hiding attachments.
688
689   LPATTACH mapi_attach = (LPATTACH) get_oom_iunknown (attach, "MAPIOBJECT");
690   if (mapi_attach)
691     {
692       SPropTagArray proparray;
693       HRESULT hr;
694
695       proparray.cValues = 1;
696       proparray.aulPropTag[0] = 0x37140003;
697       hr = mapi_attach->DeleteProps (&proparray, NULL);
698       if (hr)
699         {
700           log_error ("%s:%s: can't delete property attach flags: hr=%#lx\n",
701                      SRCNAME, __func__, hr);
702           ret = -1;
703         }
704       gpgol_release (mapi_attach);
705     }
706   else
707     {
708       log_error ("%s:%s: Failed to get mapi attachment.",
709                  SRCNAME, __func__);
710     }
711 #endif
712   gpgol_release (attach);
713   TRETURN ret;
714 }
715
716 /** Helper to update the attachments of a mail object in oom.
717   does not modify the underlying mapi structure. */
718 static int
719 add_attachments_o(LPDISPATCH mail,
720                 std::vector<std::shared_ptr<Attachment> > attachments)
721 {
722   TSTART;
723   bool anyError = false;
724   for (auto att: attachments)
725     {
726       int err = 0;
727       const auto dispName = att->get_display_name ();
728       if (dispName.empty())
729         {
730           log_error ("%s:%s: Ignoring attachment without display name.",
731                      SRCNAME, __func__);
732           continue;
733         }
734       wchar_t* wchar_name = utf8_to_wchar (dispName.c_str());
735       if (!wchar_name)
736         {
737           log_error ("%s:%s: Failed to convert '%s' to wchar.",
738                      SRCNAME, __func__, anonstr (dispName.c_str()));
739           continue;
740         }
741
742       HANDLE hFile;
743       wchar_t* wchar_file = get_tmp_outfile (wchar_name,
744                                              &hFile);
745       if (!wchar_file)
746         {
747           log_error ("%s:%s: Failed to obtain a tmp filename for: %s",
748                      SRCNAME, __func__, anonstr (dispName.c_str()));
749           err = 1;
750         }
751       if (!err && copy_attachment_to_file (att, hFile))
752         {
753           log_error ("%s:%s: Failed to copy attachment %s to temp file",
754                      SRCNAME, __func__, anonstr (dispName.c_str()));
755           err = 1;
756         }
757       if (!err && add_oom_attachment (mail, wchar_file, wchar_name))
758         {
759           log_error ("%s:%s: Failed to add attachment: %s",
760                      SRCNAME, __func__, anonstr (dispName.c_str()));
761           err = 1;
762         }
763       if (hFile && hFile != INVALID_HANDLE_VALUE)
764         {
765           CloseHandle (hFile);
766         }
767       if (wchar_file && !DeleteFileW (wchar_file))
768         {
769           log_error ("%s:%s: Failed to delete tmp attachment for: %s",
770                      SRCNAME, __func__, anonstr (dispName.c_str()));
771           err = 1;
772         }
773       xfree (wchar_file);
774       xfree (wchar_name);
775
776       if (!err)
777         {
778           log_debug ("%s:%s: Added attachment '%s'",
779                      SRCNAME, __func__, anonstr (dispName.c_str()));
780           err = fixup_last_attachment_o (mail, att);
781         }
782       if (err)
783         {
784           anyError = true;
785         }
786     }
787   TRETURN anyError;
788 }
789
790 GPGRT_LOCK_DEFINE(parser_lock);
791
792 static DWORD WINAPI
793 do_parsing (LPVOID arg)
794 {
795   TSTART;
796   gpgol_lock (&dtor_lock);
797   /* We lock with mail dtors so we can be sure the mail->parser
798      call is valid. */
799   Mail *mail = (Mail *)arg;
800   if (!Mail::isValidPtr (mail))
801     {
802       log_debug ("%s:%s: canceling parsing for: %p already deleted",
803                  SRCNAME, __func__, arg);
804       gpgol_unlock (&dtor_lock);
805       TRETURN 0;
806     }
807
808   blockInv ();
809   /* This takes a shared ptr of parser. So the parser is
810      still valid when the mail is deleted. */
811   auto parser = mail->parser ();
812   gpgol_unlock (&dtor_lock);
813
814   gpgol_lock (&parser_lock);
815   /* We lock the parser here to avoid too many
816      decryption attempts if there are
817      multiple mailobjects which might have already
818      been deleted (e.g. by quick switches of the mailview.)
819      Let's rather be a bit slower.
820      */
821   log_debug ("%s:%s: preparing the parser for: %p",
822              SRCNAME, __func__, arg);
823
824   if (!Mail::isValidPtr (mail))
825     {
826       log_debug ("%s:%s: cancel for: %p already deleted",
827                  SRCNAME, __func__, arg);
828       gpgol_unlock (&parser_lock);
829       unblockInv();
830       TRETURN 0;
831     }
832
833   if (!parser)
834     {
835       log_error ("%s:%s: no parser found for mail: %p",
836                  SRCNAME, __func__, arg);
837       gpgol_unlock (&parser_lock);
838       unblockInv();
839       TRETURN -1;
840     }
841   parser->parse();
842   if (!opt.sync_dec)
843     {
844       do_in_ui_thread (PARSING_DONE, arg);
845     }
846   gpgol_unlock (&parser_lock);
847   unblockInv();
848   TRETURN 0;
849 }
850
851 /* How encryption is done:
852
853    There are two modes of encryption. Synchronous and Async.
854    If async is used depends on the value of mail->async_crypt_disabled.
855
856    Synchronous crypto:
857
858    > Send Event < | State NoCryptMail
859    Needs Crypto ? (get_gpgol_draft_info_flags != 0)
860
861    -> No:
862       Pass send -> unencrypted mail.
863
864    -> Yes:
865       mail->update_oom_data
866       State = Mail::NeedsFirstAfterWrite
867       check_inline_response
868       invoke_oom_method (m_object, "Save", NULL);
869
870       > Write Event <
871       Pass because is_crypto_mail is false (not a decrypted mail)
872
873       > AfterWrite Event < | State NeedsFirstAfterWrite
874       State = NeedsActualCrypo
875       encrypt_sign_start
876         collect_input_data
877         -> Check if Inline PGP should be used
878         do_crypt
879           -> Resolve keys / do crypto
880
881           State = NeedsUpdateInMAPI
882           update_crypt_mapi
883           crypter->update_mail_mapi
884            if (inline) (Meaning PGP/Inline)
885           <-- do nothing.
886            else
887             build MSOXSMIME attachment and clear body / attachments.
888
889           State = NeedsUpdateInOOM
890       <- Back to Send Event
891       update_crypt_oom
892         -> Cleans body or sets PGP/Inline body. (inline_body_to_body)
893       State = WantsSendMIME or WantsSendInline
894
895       -> Saftey check "has_crypted_or_empty_body"
896       -> If MIME Mail do the T3656 check.
897
898     Send.
899
900     State order for "inline_response" (sync) Mails.
901     NoCryptMail
902     NeedsFirstAfterWrite
903     NeedsActualCrypto
904     NeedsUpdateInMAPI
905     NeedsUpdateInOOM
906     WantsSendMIME (or inline for PGP Inline)
907     -> Send.
908
909     State order for async Mails
910     NoCryptMail
911     NeedsFirstAfterWrite
912     NeedsActualCrypto
913     -> Cancel Send.
914     Windowmessages -> Crypto Done
915     NeedsUpdateInOOM
916     NeedsSecondAfterWrite
917     trigger Save.
918     NeedsUpdateInMAPI
919     WantsSendMIME
920     trigger Send.
921 */
922 static DWORD WINAPI
923 do_crypt (LPVOID arg)
924 {
925   TSTART;
926   gpgol_lock (&dtor_lock);
927   /* We lock with mail dtors so we can be sure the mail->parser
928      call is valid. */
929   Mail *mail = (Mail *)arg;
930   if (!Mail::isValidPtr (mail))
931     {
932       log_debug ("%s:%s: canceling crypt for: %p already deleted",
933                  SRCNAME, __func__, arg);
934       gpgol_unlock (&dtor_lock);
935       TRETURN 0;
936     }
937   if (mail->cryptState () != Mail::NeedsActualCrypt)
938     {
939       log_debug ("%s:%s: invalid state %i",
940                  SRCNAME, __func__, mail->cryptState ());
941       mail->setWindowEnabled_o (true);
942       gpgol_unlock (&dtor_lock);
943       TRETURN -1;
944     }
945
946   /* This takes a shared ptr of crypter. So the crypter is
947      still valid when the mail is deleted. */
948   auto crypter = mail->cryper ();
949   gpgol_unlock (&dtor_lock);
950
951   if (!crypter)
952     {
953       log_error ("%s:%s: no crypter found for mail: %p",
954                  SRCNAME, __func__, arg);
955       gpgol_unlock (&parser_lock);
956       mail->setWindowEnabled_o (true);
957       TRETURN -1;
958     }
959
960   GpgME::Error err;
961   int rc = crypter->do_crypto(err);
962
963   gpgol_lock (&dtor_lock);
964   if (!Mail::isValidPtr (mail))
965     {
966       log_debug ("%s:%s: aborting crypt for: %p already deleted",
967                  SRCNAME, __func__, arg);
968       gpgol_unlock (&dtor_lock);
969       TRETURN 0;
970     }
971
972   mail->setWindowEnabled_o (true);
973
974   if (rc == -1 || err)
975     {
976       mail->resetCrypter ();
977       crypter = nullptr;
978       if (err)
979         {
980           char *buf = nullptr;
981           gpgrt_asprintf (&buf, _("Crypto operation failed:\n%s"),
982                           err.asString());
983           memdbg_alloc (buf);
984           gpgol_message_box (mail->getWindow (), buf, _("GpgOL"), MB_OK);
985           xfree (buf);
986         }
987       else
988         {
989           gpgol_bug (mail->getWindow (),
990                      ERR_CRYPT_RESOLVER_FAILED);
991         }
992     }
993
994   if (rc || err.isCanceled())
995     {
996       log_debug ("%s:%s: crypto failed for: %p with: %i err: %i",
997                  SRCNAME, __func__, arg, rc, err.code());
998       mail->setCryptState (Mail::NoCryptMail);
999       mail->resetCrypter ();
1000       crypter = nullptr;
1001       gpgol_unlock (&dtor_lock);
1002       TRETURN rc;
1003     }
1004
1005   if (!mail->isAsyncCryptDisabled ())
1006     {
1007       mail->setCryptState (Mail::NeedsUpdateInOOM);
1008       gpgol_unlock (&dtor_lock);
1009       // This deletes the Mail in Outlook 2010
1010       do_in_ui_thread (CRYPTO_DONE, arg);
1011       log_debug ("%s:%s: UI thread finished for %p",
1012                  SRCNAME, __func__, arg);
1013     }
1014   else
1015     {
1016       mail->setCryptState (Mail::NeedsUpdateInMAPI);
1017       mail->updateCryptMAPI_m ();
1018       if (mail->cryptState () == Mail::WantsSendMIME)
1019         {
1020           // For sync crypto we need to switch this.
1021           mail->setCryptState (Mail::NeedsUpdateInOOM);
1022         }
1023       else
1024         {
1025           // A bug!
1026           log_debug ("%s:%s: Resetting crypter because of state mismatch. %p",
1027                      SRCNAME, __func__, arg);
1028           crypter = nullptr;
1029           mail->resetCrypter ();
1030         }
1031       gpgol_unlock (&dtor_lock);
1032     }
1033   /* This works around a bug in pinentry that it might
1034      bring the wrong window to front. So after encryption /
1035      signing we bring outlook back to front.
1036
1037      See GnuPG-Bug-Id: T3732
1038      */
1039   do_in_ui_thread_async (BRING_TO_FRONT, nullptr, 250);
1040   log_debug ("%s:%s: crypto thread for %p finished",
1041              SRCNAME, __func__, arg);
1042   TRETURN 0;
1043 }
1044
1045 bool
1046 Mail::isCryptoMail () const
1047 {
1048   TSTART;
1049   if (m_type == MSGTYPE_UNKNOWN || m_type == MSGTYPE_GPGOL ||
1050       m_type == MSGTYPE_SMIME)
1051     {
1052       /* Not a message for us. */
1053       TRETURN false;
1054     }
1055   TRETURN true;
1056 }
1057
1058 int
1059 Mail::decryptVerify_o ()
1060 {
1061   TSTART;
1062
1063   if (!isCryptoMail ())
1064     {
1065       log_debug ("%s:%s: Decrypt Verify for non crypto mail: %p.",
1066                  SRCNAME, __func__, m_mailitem);
1067       TRETURN 0;
1068     }
1069   if (m_needs_wipe)
1070     {
1071       log_error ("%s:%s: Decrypt verify called for msg that needs wipe: %p",
1072                  SRCNAME, __func__, m_mailitem);
1073       TRETURN 1;
1074     }
1075
1076   auto cipherstream = get_attachment_stream_o (m_mailitem, m_moss_position);
1077   if (!cipherstream)
1078     {
1079       m_is_junk = is_junk_mail (m_mailitem);
1080       if (m_is_junk)
1081         {
1082           log_debug ("%s:%s: Detected: %p as junk",
1083                      SRCNAME, __func__, m_mailitem);
1084           auto mngr = CategoryManager::instance ();
1085           m_store_id = mngr->addCategoryToMail (this,
1086                                    CategoryManager::getJunkMailCategory (),
1087                                    3 /* peach */);
1088           installFolderEventHandler_o ();
1089           TRETURN 0;
1090         }
1091       log_debug ("%s:%s: Failed to get cipherstream. Aborting handling.",
1092                  SRCNAME, __func__);
1093       m_type = MSGTYPE_UNKNOWN;
1094       TRETURN 1;
1095     }
1096
1097   setUUID_o ();
1098   m_processed = true;
1099   m_pass_write = false;
1100
1101   /* Insert placeholder */
1102   char *placeholder_buf = nullptr;
1103   if (m_type == MSGTYPE_GPGOL_WKS_CONFIRMATION)
1104     {
1105       gpgrt_asprintf (&placeholder_buf, opt.prefer_html ? decrypt_template_html :
1106                       decrypt_template,
1107                       "OpenPGP",
1108                       _("Pubkey directory confirmation"),
1109                       _("This is a confirmation request to publish your Pubkey in the "
1110                         "directory for your domain.\n\n"
1111                         "<p>If you did not request to publish your Pubkey in your providers "
1112                         "directory, simply ignore this message.</p>\n"));
1113     }
1114   else if (gpgrt_asprintf (&placeholder_buf, opt.prefer_html ? decrypt_template_html :
1115                       decrypt_template,
1116                       isSMIME_m () ? "S/MIME" : "OpenPGP",
1117                       _("message"),
1118                       _("Please wait while the message is being decrypted / verified...")) == -1)
1119     {
1120       log_error ("%s:%s: Failed to format placeholder.",
1121                  SRCNAME, __func__);
1122       TRETURN 1;
1123     }
1124
1125   if (opt.prefer_html)
1126     {
1127       char *tmp = get_oom_string (m_mailitem, "HTMLBody");
1128       if (!tmp)
1129         {
1130           TRACEPOINT;
1131           TRETURN 1;
1132         }
1133       m_orig_body = tmp;
1134       xfree (tmp);
1135       if (put_oom_string (m_mailitem, "HTMLBody", placeholder_buf))
1136         {
1137           log_error ("%s:%s: Failed to modify html body of item.",
1138                      SRCNAME, __func__);
1139         }
1140     }
1141   else
1142     {
1143       char *tmp = get_oom_string (m_mailitem, "Body");
1144       if (!tmp)
1145         {
1146           TRACEPOINT;
1147           TRETURN 1;
1148         }
1149       m_orig_body = tmp;
1150       xfree (tmp);
1151       if (put_oom_string (m_mailitem, "Body", placeholder_buf))
1152         {
1153           log_error ("%s:%s: Failed to modify body of item.",
1154                      SRCNAME, __func__);
1155         }
1156     }
1157   memdbg_alloc (placeholder_buf);
1158   xfree (placeholder_buf);
1159
1160   if (m_type == MSGTYPE_GPGOL_WKS_CONFIRMATION)
1161     {
1162       WKSHelper::instance ()->handle_confirmation_read (this, cipherstream);
1163       TRETURN 0;
1164     }
1165
1166   m_parser = std::shared_ptr <ParseController> (new ParseController (cipherstream, m_type));
1167   m_parser->setSender(GpgME::UserID::addrSpecFromString(getSender_o ().c_str()));
1168   log_data ("%s:%s: Parser for \"%s\" is %p",
1169                    SRCNAME, __func__, getSubject_o ().c_str(), m_parser.get());
1170   gpgol_release (cipherstream);
1171
1172   if (!opt.sync_dec)
1173     {
1174       HANDLE parser_thread = CreateThread (NULL, 0, do_parsing, (LPVOID) this, 0,
1175                                            NULL);
1176
1177       if (!parser_thread)
1178         {
1179           log_error ("%s:%s: Failed to create decrypt / verify thread.",
1180                      SRCNAME, __func__);
1181         }
1182       CloseHandle (parser_thread);
1183       TRETURN 0;
1184     }
1185   else
1186     {
1187       /* Parse synchronously */
1188       do_parsing ((LPVOID) this);
1189       parsing_done ();
1190       TRETURN 0;
1191     }
1192 }
1193
1194 void find_and_replace(std::string& source, const std::string &find,
1195                       const std::string &replace)
1196 {
1197   TSTART;
1198   for(std::string::size_type i = 0; (i = source.find(find, i)) != std::string::npos;)
1199     {
1200       source.replace(i, find.length(), replace);
1201       i += replace.length();
1202     }
1203   TRETURN;
1204 }
1205
1206 void
1207 Mail::updateBody_o ()
1208 {
1209   TSTART;
1210   if (!m_parser)
1211     {
1212       TRACEPOINT;
1213       TRETURN;
1214     }
1215
1216   const auto error = m_parser->get_formatted_error ();
1217   if (!error.empty())
1218     {
1219       if (opt.prefer_html)
1220         {
1221           if (put_oom_string (m_mailitem, "HTMLBody",
1222                               error.c_str ()))
1223             {
1224               log_error ("%s:%s: Failed to modify html body of item.",
1225                          SRCNAME, __func__);
1226             }
1227           else
1228             {
1229               log_debug ("%s:%s: Set error html to: '%s'",
1230                          SRCNAME, __func__, error.c_str ());
1231             }
1232
1233         }
1234       else
1235         {
1236           if (put_oom_string (m_mailitem, "Body",
1237                               error.c_str ()))
1238             {
1239               log_error ("%s:%s: Failed to modify html body of item.",
1240                          SRCNAME, __func__);
1241             }
1242           else
1243             {
1244               log_debug ("%s:%s: Set error plain to: '%s'",
1245                          SRCNAME, __func__, error.c_str ());
1246             }
1247         }
1248       TRETURN;
1249     }
1250   if (m_verify_result.error())
1251     {
1252       log_error ("%s:%s: Verification failed. Restoring Body.",
1253                  SRCNAME, __func__);
1254       if (opt.prefer_html)
1255         {
1256           if (put_oom_string (m_mailitem, "HTMLBody", m_orig_body.c_str ()))
1257             {
1258               log_error ("%s:%s: Failed to modify html body of item.",
1259                          SRCNAME, __func__);
1260             }
1261         }
1262       else
1263         {
1264           if (put_oom_string (m_mailitem, "Body", m_orig_body.c_str ()))
1265             {
1266               log_error ("%s:%s: Failed to modify html body of item.",
1267                          SRCNAME, __func__);
1268             }
1269         }
1270       TRETURN;
1271     }
1272   // No need to carry body anymore
1273   m_orig_body = std::string();
1274   auto html = m_parser->get_html_body ();
1275   auto body = m_parser->get_body ();
1276   /** Outlook does not show newlines if \r\r\n is a newline. We replace
1277     these as apparently some other buggy MUA sends this. */
1278   find_and_replace (html, "\r\r\n", "\r\n");
1279   if (opt.prefer_html && !html.empty())
1280     {
1281       if (!m_block_html)
1282         {
1283           const auto charset = m_parser->get_html_charset();
1284
1285           int codepage = 0;
1286           if (charset.empty())
1287             {
1288               codepage = get_oom_int (m_mailitem, "InternetCodepage");
1289               log_debug ("%s:%s: Did not find html charset."
1290                          " Using internet Codepage %i.",
1291                          SRCNAME, __func__, codepage);
1292             }
1293
1294           char *converted = ansi_charset_to_utf8 (charset.c_str(), html.c_str(),
1295                                                   html.size(), codepage);
1296           TRACEPOINT;
1297           int ret = put_oom_string (m_mailitem, "HTMLBody", converted ?
1298                                                             converted : "");
1299           TRACEPOINT;
1300           xfree (converted);
1301           if (ret)
1302             {
1303               log_error ("%s:%s: Failed to modify html body of item.",
1304                          SRCNAME, __func__);
1305             }
1306
1307           TRETURN;
1308         }
1309       else if (!body.empty())
1310         {
1311           /* We had a multipart/alternative mail but html should be
1312              blocked. So we prefer the text/plain part and warn
1313              once about this so that we hopefully don't get too
1314              many bugreports about this. */
1315           if (!opt.smime_html_warn_shown)
1316             {
1317               std::string caption = _("GpgOL") + std::string (": ") +
1318                 std::string (_("HTML display disabled."));
1319               std::string buf = _("HTML content in unsigned S/MIME mails "
1320                                   "is insecure.");
1321               buf += "\n";
1322               buf += _("GpgOL will only show such mails as text.");
1323
1324               buf += "\n\n";
1325               buf += _("This message is shown only once.");
1326
1327               gpgol_message_box (getWindow (), buf.c_str(), caption.c_str(),
1328                                  MB_OK);
1329               opt.smime_html_warn_shown = true;
1330               write_options ();
1331             }
1332         }
1333     }
1334
1335   if (body.empty () && m_block_html && !html.empty())
1336     {
1337 #if 0
1338       Sadly the following code still offers to load external references
1339       it might also be too dangerous if Outlook somehow autoloads the
1340       references as soon as the Body is put into HTML
1341
1342
1343       // Fallback to show HTML as plaintext if HTML display
1344       // is blocked.
1345       log_error ("%s:%s: No text body. Putting HTML into plaintext.",
1346                  SRCNAME, __func__);
1347
1348       char *converted = ansi_charset_to_utf8 (m_parser->get_html_charset().c_str(),
1349                                               html.c_str(), html.size());
1350       int ret = put_oom_string (m_mailitem, "HTMLBody", converted ? converted : "");
1351       xfree (converted);
1352       if (ret)
1353         {
1354           log_error ("%s:%s: Failed to modify html body of item.",
1355                      SRCNAME, __func__);
1356           body = html;
1357         }
1358       else
1359         {
1360           char *plainBody = get_oom_string (m_mailitem, "Body");
1361
1362           if (!plainBody)
1363             {
1364               log_error ("%s:%s: Failed to obtain converted plain body.",
1365                          SRCNAME, __func__);
1366               body = html;
1367             }
1368           else
1369             {
1370               ret = put_oom_string (m_mailitem, "HTMLBody", plainBody);
1371               xfree (plainBody);
1372               if (ret)
1373                 {
1374                   log_error ("%s:%s: Failed to put plain into html body of item.",
1375                              SRCNAME, __func__);
1376                   body = html;
1377                 }
1378               else
1379                 {
1380                   TRETURN;
1381                 }
1382             }
1383         }
1384 #endif
1385       body = html;
1386       std::string caption = _("GpgOL") + std::string (": ") +
1387         std::string (_("HTML display disabled."));
1388       std::string buf = _("HTML content in unsigned S/MIME mails "
1389                           "is insecure.");
1390       buf += "\n";
1391       buf += _("GpgOL will only show such mails as text.");
1392
1393       buf += "\n\n";
1394       buf += _("Please ask the sender to sign the message or\n"
1395                "to send it with a plain text alternative.");
1396
1397       gpgol_message_box (getWindow (), buf.c_str(), caption.c_str(),
1398                          MB_OK);
1399     }
1400
1401   find_and_replace (body, "\r\r\n", "\r\n");
1402
1403   const auto plain_charset = m_parser->get_body_charset();
1404
1405   int codepage = 0;
1406   if (plain_charset.empty())
1407     {
1408       codepage = get_oom_int (m_mailitem, "InternetCodepage");
1409       log_debug ("%s:%s: Did not find body charset. "
1410                  "Using internet Codepage %i.",
1411                  SRCNAME, __func__, codepage);
1412     }
1413
1414   char *converted = ansi_charset_to_utf8 (plain_charset.c_str(),
1415                                           body.c_str(), body.size(),
1416                                           codepage);
1417   TRACEPOINT;
1418   int ret = put_oom_string (m_mailitem, "Body", converted ? converted : "");
1419   TRACEPOINT;
1420   xfree (converted);
1421   if (ret)
1422     {
1423       log_error ("%s:%s: Failed to modify body of item.",
1424                  SRCNAME, __func__);
1425     }
1426   TRETURN;
1427 }
1428
1429 static int parsed_count;
1430
1431 void
1432 Mail::parsing_done()
1433 {
1434   TSTART;
1435   TRACEPOINT;
1436   log_oom ("Mail %p Parsing done for parser num %i: %p",
1437                  this, parsed_count++, m_parser.get());
1438   if (!m_parser)
1439     {
1440       /* This should not happen but it happens when outlook
1441          sends multiple ItemLoad events for the same Mail
1442          Object. In that case it could happen that one
1443          parser was already done while a second is now
1444          returning for the wrong mail (as it's looked up
1445          by uuid.)
1446
1447          We have a check in get_uuid that the uuid was
1448          not in the map before (and the parser is replaced).
1449          So this really really should not happen. We
1450          handle it anyway as we crash otherwise.
1451
1452          It should not happen because the parser is only
1453          created in decrypt_verify which is called in the
1454          read event. And even in there we check if the parser
1455          was set.
1456          */
1457       log_error ("%s:%s: No parser obj. For mail: %p",
1458                  SRCNAME, __func__, this);
1459       TRETURN;
1460     }
1461   /* Store the results. */
1462   m_decrypt_result = m_parser->decrypt_result ();
1463   m_verify_result = m_parser->verify_result ();
1464
1465   m_crypto_flags = 0;
1466   if (!m_decrypt_result.isNull())
1467     {
1468       m_crypto_flags |= 1;
1469     }
1470   if (m_verify_result.numSignatures())
1471     {
1472       m_crypto_flags |= 2;
1473     }
1474
1475   TRACEPOINT;
1476   updateSigstate ();
1477   m_needs_wipe = !m_is_send_again;
1478
1479   TRACEPOINT;
1480   /* Set categories according to the result. */
1481   updateCategories_o ();
1482
1483   TRACEPOINT;
1484   m_block_html = m_parser->shouldBlockHtml ();
1485
1486   if (m_block_html)
1487     {
1488       // Just to be careful.
1489       setBlockStatus_m ();
1490     }
1491
1492   TRACEPOINT;
1493   /* Update the body */
1494   updateBody_o ();
1495   TRACEPOINT;
1496
1497   /* Check that there are no unsigned / unencrypted messages. */
1498   checkAttachments_o ();
1499
1500   /* Update attachments */
1501   if (add_attachments_o (m_mailitem, m_parser->get_attachments()))
1502     {
1503       log_error ("%s:%s: Failed to update attachments.",
1504                  SRCNAME, __func__);
1505     }
1506
1507   if (m_is_send_again)
1508     {
1509       log_debug ("%s:%s: I think that this is the send again of a crypto mail.",
1510                  SRCNAME, __func__);
1511
1512       /* We no longer want to be treated like a crypto mail. */
1513       m_type = MSGTYPE_UNKNOWN;
1514       LPMESSAGE msg = get_oom_base_message (m_mailitem);
1515       if (!msg)
1516         {
1517           TRACEPOINT;
1518         }
1519       else
1520         {
1521           set_gpgol_draft_info_flags (msg, m_crypto_flags);
1522           gpgol_release (msg);
1523         }
1524       removeOurAttachments_o ();
1525     }
1526
1527   installFolderEventHandler_o ();
1528
1529   log_debug ("%s:%s: Delayed invalidate to update sigstate.",
1530              SRCNAME, __func__);
1531   CloseHandle(CreateThread (NULL, 0, delayed_invalidate_ui, (LPVOID) 300, 0,
1532                             NULL));
1533   TRACEPOINT;
1534   TRETURN;
1535 }
1536
1537 int
1538 Mail::encryptSignStart_o ()
1539 {
1540   TSTART;
1541   if (m_crypt_state != NeedsActualCrypt)
1542     {
1543       log_debug ("%s:%s: invalid state %i",
1544                  SRCNAME, __func__, m_crypt_state);
1545       TRETURN -1;
1546     }
1547   int flags = 0;
1548   if (!needs_crypto_m ())
1549     {
1550       TRETURN 0;
1551     }
1552   LPMESSAGE message = get_oom_base_message (m_mailitem);
1553   if (!message)
1554     {
1555       log_error ("%s:%s: Failed to get base message.",
1556                  SRCNAME, __func__);
1557       TRETURN -1;
1558     }
1559   flags = get_gpgol_draft_info_flags (message);
1560   gpgol_release (message);
1561
1562   const auto window = get_active_hwnd ();
1563
1564   if (m_is_gsuite)
1565     {
1566       auto att_table = mapi_create_attach_table (message, 0);
1567       int n_att_usable = count_usable_attachments (att_table);
1568       mapi_release_attach_table (att_table);
1569       /* Check for attachments if we have some abort. */
1570
1571       if (n_att_usable)
1572         {
1573           wchar_t *w_title = utf8_to_wchar (_(
1574                                               "GpgOL: Oops, G Suite Sync account detected"));
1575           wchar_t *msg = utf8_to_wchar (
1576                       _("G Suite Sync breaks outgoing crypto mails "
1577                         "with attachments.\nUsing crypto and attachments "
1578                         "with G Suite Sync is not supported.\n\n"
1579                         "See: https://dev.gnupg.org/T3545 for details."));
1580           MessageBoxW (window,
1581                        msg,
1582                        w_title,
1583                        MB_ICONINFORMATION|MB_OK);
1584           xfree (msg);
1585           xfree (w_title);
1586           TRETURN -1;
1587         }
1588     }
1589
1590   m_do_inline = m_is_gsuite ? true : opt.inline_pgp;
1591
1592   GpgME::Protocol proto = opt.enable_smime ? GpgME::UnknownProtocol: GpgME::OpenPGP;
1593   m_crypter = std::shared_ptr <CryptController> (new CryptController (this, flags & 1,
1594                                                                       flags & 2,
1595                                                                       proto));
1596
1597   // Careful from here on we have to check every
1598   // error condition with window enabling again.
1599   setWindowEnabled_o (false);
1600   if (m_crypter->collect_data ())
1601     {
1602       log_error ("%s:%s: Crypter for mail %p failed to collect data.",
1603                  SRCNAME, __func__, this);
1604       setWindowEnabled_o (true);
1605       TRETURN -1;
1606     }
1607
1608   if (!m_async_crypt_disabled)
1609     {
1610       CloseHandle(CreateThread (NULL, 0, do_crypt,
1611                                 (LPVOID) this, 0,
1612                                 NULL));
1613     }
1614   else
1615     {
1616       do_crypt (this);
1617     }
1618   TRETURN 0;
1619 }
1620
1621 int
1622 Mail::needs_crypto_m () const
1623 {
1624   TSTART;
1625   LPMESSAGE message = get_oom_message (m_mailitem);
1626   int ret;
1627   if (!message)
1628     {
1629       log_error ("%s:%s: Failed to get message.",
1630                  SRCNAME, __func__);
1631       TRETURN false;
1632     }
1633   ret = get_gpgol_draft_info_flags (message);
1634   gpgol_release(message);
1635   TRETURN ret;
1636 }
1637
1638 int
1639 Mail::wipe_o (bool force)
1640 {
1641   TSTART;
1642   if (!m_needs_wipe && !force)
1643     {
1644       TRETURN 0;
1645     }
1646   log_debug ("%s:%s: Removing plaintext from mailitem: %p.",
1647              SRCNAME, __func__, m_mailitem);
1648   if (put_oom_string (m_mailitem, "HTMLBody",
1649                       ""))
1650     {
1651       if (put_oom_string (m_mailitem, "Body",
1652                           ""))
1653         {
1654           log_debug ("%s:%s: Failed to wipe mailitem: %p.",
1655                      SRCNAME, __func__, m_mailitem);
1656           TRETURN -1;
1657         }
1658       TRETURN -1;
1659     }
1660   else
1661     {
1662       put_oom_string (m_mailitem, "Body", "");
1663     }
1664   m_needs_wipe = false;
1665   TRETURN 0;
1666 }
1667
1668 int
1669 Mail::updateOOMData_o ()
1670 {
1671   TSTART;
1672   char *buf = nullptr;
1673   log_debug ("%s:%s", SRCNAME, __func__);
1674
1675   if (!isCryptoMail ())
1676     {
1677       /* Update the body format. */
1678       m_is_html_alternative = get_oom_int (m_mailitem, "BodyFormat") > 1;
1679
1680       /* Store the body. It was not obvious for me (aheinecke) how
1681          to access this through MAPI. */
1682       if (m_is_html_alternative)
1683         {
1684           log_debug ("%s:%s: Is html alternative mail.", SRCNAME, __func__);
1685           xfree (m_cached_html_body);
1686           m_cached_html_body = get_oom_string (m_mailitem, "HTMLBody");
1687         }
1688       xfree (m_cached_plain_body);
1689       m_cached_plain_body = get_oom_string (m_mailitem, "Body");
1690
1691       m_cached_recipients = getRecipients_o ();
1692     }
1693   /* For some reason outlook may store the recipient address
1694      in the send using account field. If we have SMTP we prefer
1695      the SenderEmailAddress string. */
1696   if (isCryptoMail ())
1697     {
1698       /* This is the case where we are reading a mail and not composing.
1699          When composing we need to use the SendUsingAccount because if
1700          you send from the folder of userA but change the from to userB
1701          outlook will keep the SenderEmailAddress of UserA. This is all
1702          so horrible. */
1703       buf = get_sender_SenderEMailAddress (m_mailitem);
1704
1705       if (!buf)
1706         {
1707           /* Try the sender Object */
1708           buf = get_sender_Sender (m_mailitem);
1709         }
1710
1711       /* We also want to cache sent representing email address so that
1712          we can use it for verification information. */
1713       char *buf2 = get_sender_SentRepresentingAddress (m_mailitem);
1714
1715       if (buf2)
1716         {
1717           m_sent_on_behalf = buf2;
1718           xfree (buf2);
1719         }
1720     }
1721
1722   if (!buf)
1723     {
1724       buf = get_sender_SendUsingAccount (m_mailitem, &m_is_gsuite);
1725     }
1726   if (!buf && !isCryptoMail ())
1727     {
1728       /* Try the sender Object */
1729       buf = get_sender_Sender (m_mailitem);
1730     }
1731   if (!buf)
1732     {
1733       /* We don't have s sender object or SendUsingAccount,
1734          well, in that case fall back to the current user. */
1735       buf = get_sender_CurrentUser (m_mailitem);
1736     }
1737   if (!buf)
1738     {
1739       log_debug ("%s:%s: All fallbacks failed.",
1740                  SRCNAME, __func__);
1741       TRETURN -1;
1742     }
1743   m_sender = buf;
1744   xfree (buf);
1745   TRETURN 0;
1746 }
1747
1748 std::string
1749 Mail::getSender_o ()
1750 {
1751   TSTART;
1752   if (m_sender.empty())
1753     updateOOMData_o ();
1754   TRETURN m_sender;
1755 }
1756
1757 std::string
1758 Mail::getSender () const
1759 {
1760   TSTART;
1761   TRETURN m_sender;
1762 }
1763
1764 int
1765 Mail::closeAllMails_o ()
1766 {
1767   TSTART;
1768   int err = 0;
1769
1770   /* Detach Folder sinks */
1771   for (auto fit = s_folder_events_map.begin(); fit != s_folder_events_map.end(); ++fit)
1772     {
1773       detach_FolderEvents_sink (fit->second);
1774       gpgol_release (fit->second);
1775     }
1776   s_folder_events_map.clear();
1777
1778
1779   std::map<LPDISPATCH, Mail *>::iterator it;
1780   TRACEPOINT;
1781   gpgol_lock (&mail_map_lock);
1782   std::map<LPDISPATCH, Mail *> mail_map_copy = s_mail_map;
1783   gpgol_unlock (&mail_map_lock);
1784   for (it = mail_map_copy.begin(); it != mail_map_copy.end(); ++it)
1785     {
1786       /* XXX For non racy code the is_valid_ptr check should not
1787          be necessary but we crashed sometimes closing a destroyed
1788          mail. */
1789       if (!isValidPtr (it->second))
1790         {
1791           log_debug ("%s:%s: Already deleted mail for %p",
1792                    SRCNAME, __func__, it->first);
1793           continue;
1794         }
1795
1796       if (!it->second->isCryptoMail ())
1797         {
1798           continue;
1799         }
1800       if (closeInspector_o (it->second) || close (it->second))
1801         {
1802           log_error ("Failed to close mail: %p ", it->first);
1803           /* Should not happen */
1804           if (isValidPtr (it->second) && it->second->revert_o ())
1805             {
1806               err++;
1807             }
1808         }
1809     }
1810   TRETURN err;
1811 }
1812 int
1813 Mail::revertAllMails_o ()
1814 {
1815   TSTART;
1816   int err = 0;
1817   std::map<LPDISPATCH, Mail *>::iterator it;
1818   gpgol_lock (&mail_map_lock);
1819   auto mail_map_copy = s_mail_map;
1820   gpgol_unlock (&mail_map_lock);
1821   for (it = mail_map_copy.begin(); it != mail_map_copy.end(); ++it)
1822     {
1823       if (it->second->revert_o ())
1824         {
1825           log_error ("Failed to revert mail: %p ", it->first);
1826           err++;
1827           continue;
1828         }
1829
1830       it->second->setNeedsSave (true);
1831       if (!invoke_oom_method (it->first, "Save", NULL))
1832         {
1833           log_error ("Failed to save reverted mail: %p ", it->second);
1834           err++;
1835           continue;
1836         }
1837     }
1838   TRETURN err;
1839 }
1840
1841 int
1842 Mail::wipeAllMails_o ()
1843 {
1844   TSTART;
1845   int err = 0;
1846   std::map<LPDISPATCH, Mail *>::iterator it;
1847   gpgol_lock (&mail_map_lock);
1848   auto mail_map_copy = s_mail_map;
1849   gpgol_unlock (&mail_map_lock);
1850   for (it = mail_map_copy.begin(); it != mail_map_copy.end(); ++it)
1851     {
1852       if (it->second->wipe_o ())
1853         {
1854           log_error ("Failed to wipe mail: %p ", it->first);
1855           err++;
1856         }
1857     }
1858   TRETURN err;
1859 }
1860
1861 int
1862 Mail::revert_o ()
1863 {
1864   TSTART;
1865   int err = 0;
1866   if (!m_processed)
1867     {
1868       TRETURN 0;
1869     }
1870
1871   m_disable_att_remove_warning = true;
1872
1873   err = gpgol_mailitem_revert (m_mailitem);
1874   if (err == -1)
1875     {
1876       log_error ("%s:%s: Message revert failed falling back to wipe.",
1877                  SRCNAME, __func__);
1878       TRETURN wipe_o ();
1879     }
1880   /* We need to reprocess the mail next time around. */
1881   m_processed = false;
1882   m_needs_wipe = false;
1883   m_disable_att_remove_warning = false;
1884   TRETURN 0;
1885 }
1886
1887 bool
1888 Mail::isSMIME_m ()
1889 {
1890   TSTART;
1891   msgtype_t msgtype;
1892   LPMESSAGE message;
1893
1894   if (m_is_smime_checked)
1895     {
1896       TRETURN m_is_smime;
1897     }
1898
1899   message = get_oom_message (m_mailitem);
1900
1901   if (!message)
1902     {
1903       log_error ("%s:%s: No message?",
1904                  SRCNAME, __func__);
1905       TRETURN false;
1906     }
1907
1908   msgtype = mapi_get_message_type (message);
1909   m_is_smime = msgtype == MSGTYPE_GPGOL_OPAQUE_ENCRYPTED ||
1910                msgtype == MSGTYPE_GPGOL_OPAQUE_SIGNED;
1911
1912   /* Check if it is an smime mail. Multipart signed can
1913      also be true. */
1914   if (!m_is_smime && msgtype == MSGTYPE_GPGOL_MULTIPART_SIGNED)
1915     {
1916       char *proto;
1917       char *ct = mapi_get_message_content_type (message, &proto, NULL);
1918       if (ct && proto)
1919         {
1920           m_is_smime = (!strcmp (proto, "application/pkcs7-signature") ||
1921                         !strcmp (proto, "application/x-pkcs7-signature"));
1922         }
1923       else
1924         {
1925           log_error ("%s:%s: No protocol in multipart / signed mail.",
1926                      SRCNAME, __func__);
1927         }
1928       xfree (proto);
1929       xfree (ct);
1930     }
1931   gpgol_release (message);
1932   m_is_smime_checked  = true;
1933   TRETURN m_is_smime;
1934 }
1935
1936 static std::string
1937 get_string_o (LPDISPATCH item, const char *str)
1938 {
1939   TSTART;
1940   char *buf = get_oom_string (item, str);
1941   if (!buf)
1942     {
1943       TRETURN std::string();
1944     }
1945   std::string ret = buf;
1946   xfree (buf);
1947   TRETURN ret;
1948 }
1949
1950 std::string
1951 Mail::getSubject_o () const
1952 {
1953   TSTART;
1954   TRETURN get_string_o (m_mailitem, "Subject");
1955 }
1956
1957 std::string
1958 Mail::getBody_o () const
1959 {
1960   TSTART;
1961   TRETURN get_string_o (m_mailitem, "Body");
1962 }
1963
1964 std::vector<std::string>
1965 Mail::getRecipients_o () const
1966 {
1967   TSTART;
1968   LPDISPATCH recipients = get_oom_object (m_mailitem, "Recipients");
1969   if (!recipients)
1970     {
1971       TRACEPOINT;
1972       std::vector<std::string>();
1973     }
1974   bool err = false;
1975   auto ret = get_oom_recipients (recipients, &err);
1976   gpgol_release (recipients);
1977
1978   if (err)
1979     {
1980       log_debug ("%s:%s: Failed to resolve recipients at this time.",
1981                  SRCNAME, __func__);
1982
1983     }
1984
1985   TRETURN ret;
1986 }
1987
1988 int
1989 Mail::closeInspector_o (Mail *mail)
1990 {
1991   TSTART;
1992   LPDISPATCH inspector = get_oom_object (mail->item(), "GetInspector");
1993   HRESULT hr;
1994   DISPID dispid;
1995   if (!inspector)
1996     {
1997       log_debug ("%s:%s: No inspector.",
1998                  SRCNAME, __func__);
1999       TRETURN -1;
2000     }
2001
2002   dispid = lookup_oom_dispid (inspector, "Close");
2003   if (dispid != DISPID_UNKNOWN)
2004     {
2005       VARIANT aVariant[1];
2006       DISPPARAMS dispparams;
2007
2008       dispparams.rgvarg = aVariant;
2009       dispparams.rgvarg[0].vt = VT_INT;
2010       dispparams.rgvarg[0].intVal = 1;
2011       dispparams.cArgs = 1;
2012       dispparams.cNamedArgs = 0;
2013
2014       hr = inspector->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
2015                               DISPATCH_METHOD, &dispparams,
2016                               NULL, NULL, NULL);
2017       if (hr != S_OK)
2018         {
2019           log_debug ("%s:%s: Failed to close inspector: %#lx",
2020                      SRCNAME, __func__, hr);
2021           gpgol_release (inspector);
2022           TRETURN -1;
2023         }
2024     }
2025   gpgol_release (inspector);
2026   TRETURN 0;
2027 }
2028
2029 /* static */
2030 int
2031 Mail::close (Mail *mail)
2032 {
2033   TSTART;
2034   VARIANT aVariant[1];
2035   DISPPARAMS dispparams;
2036
2037   dispparams.rgvarg = aVariant;
2038   dispparams.rgvarg[0].vt = VT_INT;
2039   dispparams.rgvarg[0].intVal = 1;
2040   dispparams.cArgs = 1;
2041   dispparams.cNamedArgs = 0;
2042
2043   log_oom ("%s:%s: Invoking close for: %p",
2044                  SRCNAME, __func__, mail->item());
2045   mail->setCloseTriggered (true);
2046   int rc = invoke_oom_method_with_parms (mail->item(), "Close",
2047                                        NULL, &dispparams);
2048
2049   if (!rc)
2050     {
2051       log_debug ("%s:%s: Close successful. Next write may pass.",
2052                  SRCNAME, __func__);
2053       mail->setPassWrite (true);
2054     }
2055   log_oom ("%s:%s: returned from close",
2056                  SRCNAME, __func__);
2057   TRETURN rc;
2058 }
2059
2060 void
2061 Mail::setCloseTriggered (bool value)
2062 {
2063   TSTART;
2064   m_close_triggered = value;
2065   TRETURN;
2066 }
2067
2068 bool
2069 Mail::getCloseTriggered () const
2070 {
2071   TSTART;
2072   TRETURN m_close_triggered;
2073 }
2074
2075 static const UserID
2076 get_uid_for_sender (const Key &k, const char *sender)
2077 {
2078   TSTART;
2079   UserID ret;
2080
2081   if (!sender)
2082     {
2083       TRETURN ret;
2084     }
2085
2086   if (!k.numUserIDs())
2087     {
2088       log_debug ("%s:%s: Key without uids",
2089                  SRCNAME, __func__);
2090       TRETURN ret;
2091     }
2092
2093   for (const auto uid: k.userIDs())
2094     {
2095       if (!uid.email() || !*(uid.email()))
2096         {
2097           /* This happens for S/MIME a lot */
2098           log_debug ("%s:%s: skipping uid without email.",
2099                      SRCNAME, __func__);
2100           continue;
2101         }
2102       auto normalized_uid = uid.addrSpec();
2103       auto normalized_sender = UserID::addrSpecFromString(sender);
2104
2105       if (normalized_sender.empty() || normalized_uid.empty())
2106         {
2107           log_error ("%s:%s: normalizing '%s' or '%s' failed.",
2108                      SRCNAME, __func__, anonstr (uid.email()),
2109                      anonstr (sender));
2110           continue;
2111         }
2112       if (normalized_sender == normalized_uid)
2113         {
2114           ret = uid;
2115         }
2116     }
2117   TRETURN ret;
2118 }
2119
2120 void
2121 Mail::updateSigstate ()
2122 {
2123   TSTART;
2124   std::string sender = getSender ();
2125
2126   if (sender.empty())
2127     {
2128       log_error ("%s:%s:%i", SRCNAME, __func__, __LINE__);
2129       TRETURN;
2130     }
2131
2132   if (m_verify_result.isNull())
2133     {
2134       log_debug ("%s:%s: No verify result.",
2135                  SRCNAME, __func__);
2136       TRETURN;
2137     }
2138
2139   if (m_verify_result.error())
2140     {
2141       log_debug ("%s:%s: verify error.",
2142                  SRCNAME, __func__);
2143       TRETURN;
2144     }
2145
2146   for (const auto sig: m_verify_result.signatures())
2147     {
2148       m_is_signed = true;
2149       const auto key = KeyCache::instance ()->getByFpr (sig.fingerprint(),
2150                                                         true);
2151       m_uid = get_uid_for_sender (key, sender.c_str());
2152
2153       if (m_uid.isNull() && !m_sent_on_behalf.empty ())
2154         {
2155           m_uid = get_uid_for_sender (key, m_sent_on_behalf.c_str ());
2156           if (!m_uid.isNull())
2157             {
2158               log_debug ("%s:%s: Using sent on behalf '%s' instead of '%s'",
2159                          SRCNAME, __func__, anonstr (m_sent_on_behalf.c_str()),
2160                          anonstr (sender.c_str ()));
2161             }
2162         }
2163       /* Sigsum valid or green is somehow not set in this case.
2164        * Which is strange as AFAIK this worked in the past. */
2165       if ((sig.summary() & Signature::Summary::Valid) &&
2166           m_uid.origin() == GpgME::Key::OriginWKD &&
2167           (sig.validity() == Signature::Validity::Unknown ||
2168            sig.validity() == Signature::Validity::Marginal))
2169         {
2170           // WKD is a shortcut to Level 2 trust.
2171           log_debug ("%s:%s: Unknown or marginal from WKD -> Level 2",
2172                      SRCNAME, __func__);
2173          }
2174       else if (m_uid.isNull() || (sig.validity() != Signature::Validity::Marginal &&
2175           sig.validity() != Signature::Validity::Full &&
2176           sig.validity() != Signature::Validity::Ultimate))
2177         {
2178           /* For our category we only care about trusted sigs. And
2179           the UID needs to match.*/
2180           continue;
2181         }
2182       else if (sig.validity() == Signature::Validity::Marginal)
2183         {
2184           const auto tofu = m_uid.tofuInfo();
2185           if (!tofu.isNull() &&
2186               (tofu.validity() != TofuInfo::Validity::BasicHistory &&
2187                tofu.validity() != TofuInfo::Validity::LargeHistory))
2188             {
2189               /* Marginal is only good enough without tofu.
2190                  Otherwise we wait for basic trust. */
2191               log_debug ("%s:%s: Discarding marginal signature."
2192                          "With too little history.",
2193                          SRCNAME, __func__);
2194               continue;
2195             }
2196         }
2197       log_debug ("%s:%s: Classified sender as verified uid validity: %i origin: %i",
2198                  SRCNAME, __func__, m_uid.validity(), m_uid.origin());
2199       m_sig = sig;
2200       m_is_valid = true;
2201       TRETURN;
2202     }
2203
2204   log_debug ("%s:%s: No signature with enough trust. Using first",
2205              SRCNAME, __func__);
2206   m_sig = m_verify_result.signature(0);
2207   TRETURN;
2208 }
2209
2210 bool
2211 Mail::isValidSig () const
2212 {
2213   TSTART;
2214   TRETURN m_is_valid;
2215 }
2216
2217 void
2218 Mail::removeCategories_o ()
2219 {
2220   TSTART;
2221   if (!m_store_id.empty () && !m_verify_category.empty ())
2222     {
2223       log_oom ("%s:%s: Unreffing verify category",
2224                        SRCNAME, __func__);
2225       CategoryManager::instance ()->removeCategory (this,
2226                                                     m_verify_category);
2227     }
2228   if (!m_store_id.empty () && !m_decrypt_result.isNull())
2229     {
2230       log_oom ("%s:%s: Unreffing dec category",
2231                        SRCNAME, __func__);
2232       CategoryManager::instance ()->removeCategory (this,
2233                                 CategoryManager::getEncMailCategory ());
2234     }
2235   if (m_is_junk)
2236     {
2237       log_oom ("%s:%s: Unreffing junk category",
2238                        SRCNAME, __func__);
2239       CategoryManager::instance ()->removeCategory (this,
2240                                 CategoryManager::getJunkMailCategory ());
2241     }
2242   TRETURN;
2243 }
2244
2245 /* Now for some tasty hack: Outlook sometimes does
2246    not show the new categories properly but instead
2247    does some weird scrollbar thing. This can be
2248    avoided by resizing the message a bit. But somehow
2249    this only needs to be done once.
2250
2251    Weird isn't it? But as this workaround worked let's
2252    do it programatically. Fun. Wan't some tomato sauce
2253    with this hack? */
2254 static void
2255 resize_active_window ()
2256 {
2257   TSTART;
2258   HWND wnd = get_active_hwnd ();
2259   static std::vector<HWND> resized_windows;
2260   if(std::find(resized_windows.begin(), resized_windows.end(), wnd) != resized_windows.end()) {
2261       /* We only need to do this once per window. XXX But sometimes we also
2262          need to do this once per view of the explorer. So for now this might
2263          break but we reduce the flicker. A better solution would be to find
2264          the current view and track that. */
2265       TRETURN;
2266   }
2267
2268   if (!wnd)
2269     {
2270       TRACEPOINT;
2271       TRETURN;
2272     }
2273   RECT oldpos;
2274   if (!GetWindowRect (wnd, &oldpos))
2275     {
2276       TRACEPOINT;
2277       TRETURN;
2278     }
2279
2280   if (!SetWindowPos (wnd, nullptr,
2281                      (int)oldpos.left,
2282                      (int)oldpos.top,
2283                      /* Anything smaller then 19 was ignored when the window was
2284                       * maximized on Windows 10 at least with a 1980*1024
2285                       * resolution. So I assume it's at least 1 percent.
2286                       * This is all hackish and ugly but should work for 90%...
2287                       * hopefully.
2288                       */
2289                      (int)oldpos.right - oldpos.left - 20,
2290                      (int)oldpos.bottom - oldpos.top, 0))
2291     {
2292       TRACEPOINT;
2293       TRETURN;
2294     }
2295
2296   if (!SetWindowPos (wnd, nullptr,
2297                      (int)oldpos.left,
2298                      (int)oldpos.top,
2299                      (int)oldpos.right - oldpos.left,
2300                      (int)oldpos.bottom - oldpos.top, 0))
2301     {
2302       TRACEPOINT;
2303       TRETURN;
2304     }
2305   resized_windows.push_back(wnd);
2306   TRETURN;
2307 }
2308
2309 #if 0
2310 static std::string
2311 pretty_id (const char *keyId)
2312 {
2313   /* Three spaces, four quads and a NULL */
2314   char buf[20];
2315   buf[19] = '\0';
2316   if (!keyId)
2317     {
2318       return std::string ("null");
2319     }
2320   size_t len = strlen (keyId);
2321   if (!len)
2322     {
2323       return std::string ("empty");
2324     }
2325   if (len < 16)
2326     {
2327       return std::string (_("Invalid Key"));
2328     }
2329   const char *p = keyId + (len - 16);
2330   int j = 0;
2331   for (size_t i = 0; i < 16; i++)
2332     {
2333       if (i && i % 4 == 0)
2334         {
2335           buf[j++] = ' ';
2336         }
2337       buf[j++] = *(p + i);
2338     }
2339   return std::string (buf);
2340 }
2341 #endif
2342
2343 void
2344 Mail::updateCategories_o ()
2345 {
2346   TSTART;
2347
2348   auto mngr = CategoryManager::instance ();
2349   if (isValidSig ())
2350     {
2351       char *buf;
2352       /* Resolve to the primary fingerprint */
2353 #if 0
2354       const auto sigKey = KeyCache::instance ()->getByFpr (m_sig.fingerprint (),
2355                                                            true);
2356       const char *sigFpr;
2357       if (sigKey.isNull())
2358         {
2359           sigFpr = m_sig.fingerprint ();
2360         }
2361       else
2362         {
2363           sigFpr = sigKey.primaryFingerprint ();
2364         }
2365 #endif
2366       /* If m_uid addrSpec would not return a result we would never
2367        * have gotten the UID. */
2368       int lvl = get_signature_level ();
2369       gpgrt_asprintf (&buf, "GpgOL: %s %i %s '%s'", _("Level"), lvl,
2370                       _("trust in"),
2371                       m_uid.addrSpec ().c_str ());
2372       memdbg_alloc (buf);
2373
2374       int color = 0;
2375       if (lvl == 2)
2376         {
2377           color = 7; /* Olive */
2378         }
2379       if (lvl == 3)
2380         {
2381           color = 5; /* Green */
2382         }
2383       if (lvl == 4)
2384         {
2385           color = 20; /* Dark Green */
2386         }
2387       m_store_id = mngr->addCategoryToMail (this, buf, color);
2388       m_verify_category = buf;
2389       xfree (buf);
2390     }
2391   else
2392     {
2393       remove_category (m_mailitem, "GpgOL: ", false);
2394     }
2395
2396   if (!m_decrypt_result.isNull())
2397     {
2398       const auto id = mngr->addCategoryToMail (this,
2399                                  CategoryManager::getEncMailCategory (),
2400                                  8 /* Blue */);
2401       if (m_store_id.empty())
2402         {
2403           m_store_id = id;
2404         }
2405       if (m_store_id != id)
2406         {
2407           log_error ("%s:%s unexpected store mismatch "
2408                      "between '%s' and dec cat '%s'",
2409                      SRCNAME, __func__, m_store_id.c_str(), id.c_str());
2410         }
2411     }
2412   else
2413     {
2414       /* As a small safeguard against fakes we remove our
2415          categories */
2416       remove_category (m_mailitem,
2417                        CategoryManager::getEncMailCategory ().c_str (),
2418                        true);
2419     }
2420
2421   resize_active_window();
2422
2423   TRETURN;
2424 }
2425
2426 bool
2427 Mail::isSigned () const
2428 {
2429   TSTART;
2430   TRETURN m_verify_result.numSignatures() > 0;
2431 }
2432
2433 bool
2434 Mail::isEncrypted () const
2435 {
2436   TSTART;
2437   TRETURN !m_decrypt_result.isNull();
2438 }
2439
2440 int
2441 Mail::setUUID_o ()
2442 {
2443   TSTART;
2444   char *uuid;
2445   if (!m_uuid.empty())
2446     {
2447       /* This codepath is reached by decrypt again after a
2448          close with discard changes. The close discarded
2449          the uuid on the OOM object so we have to set
2450          it again. */
2451       log_debug ("%s:%s: Resetting uuid for %p to %s",
2452                  SRCNAME, __func__, this,
2453                  m_uuid.c_str());
2454       uuid = get_unique_id (m_mailitem, 1, m_uuid.c_str());
2455     }
2456   else
2457     {
2458       uuid = get_unique_id (m_mailitem, 1, nullptr);
2459       log_debug ("%s:%s: uuid for %p set to %s",
2460                  SRCNAME, __func__, this, uuid);
2461     }
2462
2463   if (!uuid)
2464     {
2465       log_debug ("%s:%s: Failed to get/set uuid for %p",
2466                  SRCNAME, __func__, m_mailitem);
2467       TRETURN -1;
2468     }
2469   if (m_uuid.empty())
2470     {
2471       m_uuid = uuid;
2472       Mail *other = getMailForUUID (uuid);
2473       if (other)
2474         {
2475           /* According to documentation this should not
2476              happen as this means that multiple ItemLoad
2477              events occured for the same mailobject without
2478              unload / destruction of the mail.
2479
2480              But it happens. If you invalidate the UI
2481              in the selection change event Outlook loads a
2482              new mailobject for the mail. Might happen in
2483              other surprising cases. We replace in that
2484              case as experiments have shown that the last
2485              mailobject is the one that is visible.
2486
2487              Still troubling state so we log this as an error.
2488              */
2489           log_error ("%s:%s: There is another mail for %p "
2490                      "with uuid: %s replacing it.",
2491                      SRCNAME, __func__, m_mailitem, uuid);
2492           delete other;
2493         }
2494
2495       gpgol_lock (&uid_map_lock);
2496       s_uid_map.insert (std::pair<std::string, Mail *> (m_uuid, this));
2497       gpgol_unlock (&uid_map_lock);
2498       log_debug ("%s:%s: uuid for %p is now %s",
2499                  SRCNAME, __func__, this,
2500                  m_uuid.c_str());
2501     }
2502   xfree (uuid);
2503   TRETURN 0;
2504 }
2505
2506 /* TRETURNs 2 if the userid is ultimately trusted.
2507
2508    TRETURNs 1 if the userid is fully trusted but has
2509    a signature by a key for which we have a secret
2510    and which is ultimately trusted. (Direct trust)
2511
2512    0 otherwise */
2513 static int
2514 level_4_check (const UserID &uid)
2515 {
2516   TSTART;
2517   if (uid.isNull())
2518     {
2519       TRETURN 0;
2520     }
2521   if (uid.validity () == UserID::Validity::Ultimate)
2522     {
2523       TRETURN 2;
2524     }
2525   if (uid.validity () == UserID::Validity::Full)
2526     {
2527       const auto ultimate_keys = KeyCache::instance()->getUltimateKeys ();
2528       for (const auto sig: uid.signatures ())
2529         {
2530           const char *sigID = sig.signerKeyID ();
2531           if (sig.isNull() || !sigID)
2532             {
2533               /* should not happen */
2534               TRACEPOINT;
2535               continue;
2536             }
2537           /* Direct trust information is not available
2538              through gnupg so we cached the keys with ultimate
2539              trust during parsing and now see if we find a direct
2540              trust path.*/
2541           for (const auto secKey: ultimate_keys)
2542             {
2543               /* Check that the Key id of the key matches */
2544               const char *secKeyID = secKey.keyID ();
2545               if (!secKeyID || strcmp (secKeyID, sigID))
2546                 {
2547                   continue;
2548                 }
2549               /* Check that the userID of the signature is the ultimately
2550                  trusted one. */
2551               const char *sig_uid_str = sig.signerUserID();
2552               if (!sig_uid_str)
2553                 {
2554                   /* should not happen */
2555                   TRACEPOINT;
2556                   continue;
2557                 }
2558               for (const auto signer_uid: secKey.userIDs ())
2559                 {
2560                   if (signer_uid.validity() != UserID::Validity::Ultimate)
2561                     {
2562                       TRACEPOINT;
2563                       continue;
2564                     }
2565                   const char *signer_uid_str = signer_uid.id ();
2566                   if (!sig_uid_str)
2567                     {
2568                       /* should not happen */
2569                       TRACEPOINT;
2570                       continue;
2571                     }
2572                   if (!strcmp(sig_uid_str, signer_uid_str))
2573                     {
2574                       /* We have a match */
2575                       log_debug ("%s:%s: classified %s as ultimate because "
2576                                  "it was signed by uid %s of key %s",
2577                                  SRCNAME, __func__, anonstr (signer_uid_str),
2578                                  anonstr (sig_uid_str),
2579                                  anonstr (secKeyID));
2580                       TRETURN 1;
2581                     }
2582                 }
2583             }
2584         }
2585     }
2586   TRETURN 0;
2587 }
2588
2589 std::string
2590 Mail::getCryptoSummary () const
2591 {
2592   TSTART;
2593   const int level = get_signature_level ();
2594
2595   bool enc = isEncrypted ();
2596   if (level == 4 && enc)
2597     {
2598       TRETURN _("Security Level 4");
2599     }
2600   if (level == 4)
2601     {
2602       TRETURN _("Trust Level 4");
2603     }
2604   if (level == 3 && enc)
2605     {
2606       TRETURN _("Security Level 3");
2607     }
2608   if (level == 3)
2609     {
2610       TRETURN _("Trust Level 3");
2611     }
2612   if (level == 2 && enc)
2613     {
2614       TRETURN _("Security Level 2");
2615     }
2616   if (level == 2)
2617     {
2618       TRETURN _("Trust Level 2");
2619     }
2620   if (enc)
2621     {
2622       TRETURN _("Encrypted");
2623     }
2624   if (isSigned ())
2625     {
2626       /* Even if it is signed, if it is not validly
2627          signed it's still completly insecure as anyone
2628          could have signed this. So we avoid the label
2629          "signed" here as this word already implies some
2630          security. */
2631       TRETURN _("Insecure");
2632     }
2633   TRETURN _("Insecure");
2634 }
2635
2636 std::string
2637 Mail::getCryptoOneLine () const
2638 {
2639   TSTART;
2640   bool sig = isSigned ();
2641   bool enc = isEncrypted ();
2642   if (sig || enc)
2643     {
2644       if (sig && enc)
2645         {
2646           TRETURN _("Signed and encrypted message");
2647         }
2648       else if (sig)
2649         {
2650           TRETURN _("Signed message");
2651         }
2652       else if (enc)
2653         {
2654           TRETURN _("Encrypted message");
2655         }
2656     }
2657   TRETURN _("Insecure message");
2658 }
2659
2660 std::string
2661 Mail::getCryptoDetails_o ()
2662 {
2663   TSTART;
2664   std::string message;
2665
2666   /* No signature with keys but error */
2667   if (!isEncrypted () && !isSigned () && m_verify_result.error())
2668     {
2669       message = _("You cannot be sure who sent, "
2670                   "modified and read the message in transit.");
2671       message += "\n\n";
2672       message += _("The message was signed but the verification failed with:");
2673       message += "\n";
2674       message += m_verify_result.error().asString();
2675       TRETURN message;
2676     }
2677   /* No crypo, what are we doing here? */
2678   if (!isEncrypted () && !isSigned ())
2679     {
2680       TRETURN _("You cannot be sure who sent, "
2681                "modified and read the message in transit.");
2682     }
2683   /* Handle encrypt only */
2684   if (isEncrypted () && !isSigned ())
2685     {
2686       if (in_de_vs_mode ())
2687        {
2688          if (m_sig.isDeVs())
2689            {
2690              message += _("The encryption was VS-NfD-compliant.");
2691            }
2692          else
2693            {
2694              message += _("The encryption was not VS-NfD-compliant.");
2695            }
2696         }
2697       message += "\n\n";
2698       message += _("You cannot be sure who sent the message because "
2699                    "it is not signed.");
2700       TRETURN message;
2701     }
2702
2703   bool keyFound = true;
2704   const auto sigKey = KeyCache::instance ()->getByFpr (m_sig.fingerprint (),
2705                                                        true);
2706   bool isOpenPGP = sigKey.isNull() ? !isSMIME_m () :
2707                    sigKey.protocol() == Protocol::OpenPGP;
2708   char *buf;
2709   bool hasConflict = false;
2710   int level = get_signature_level ();
2711
2712   log_debug ("%s:%s: Formatting sig. Validity: %x Summary: %x Level: %i",
2713              SRCNAME, __func__, m_sig.validity(), m_sig.summary(),
2714              level);
2715
2716   if (level == 4)
2717     {
2718       /* level 4 check for direct trust */
2719       int four_check = level_4_check (m_uid);
2720
2721       if (four_check == 2 && sigKey.hasSecret ())
2722         {
2723           message = _("You signed this message.");
2724         }
2725       else if (four_check == 1)
2726         {
2727           message = _("The senders identity was certified by yourself.");
2728         }
2729       else if (four_check == 2)
2730         {
2731           message = _("The sender is allowed to certify identities for you.");
2732         }
2733       else
2734         {
2735           log_error ("%s:%s:%i BUG: Invalid sigstate.",
2736                      SRCNAME, __func__, __LINE__);
2737           TRETURN message;
2738         }
2739     }
2740   else if (level == 3 && isOpenPGP)
2741     {
2742       /* Level three is only reachable through web of trust and no
2743          direct signature. */
2744       message = _("The senders identity was certified by several trusted people.");
2745     }
2746   else if (level == 3 && !isOpenPGP)
2747     {
2748       /* Level three is the only level for trusted S/MIME keys. */
2749       gpgrt_asprintf (&buf, _("The senders identity is certified by the trusted issuer:\n'%s'\n"),
2750                       sigKey.issuerName());
2751       memdbg_alloc (buf);
2752       message = buf;
2753       xfree (buf);
2754     }
2755   else if (level == 2 && m_uid.origin () == GpgME::Key::OriginWKD)
2756     {
2757       message = _("The mail provider of the recipient served this key.");
2758     }
2759   else if (level == 2 && m_uid.tofuInfo ().isNull ())
2760     {
2761       /* Marginal trust through pgp only */
2762       message = _("Some trusted people "
2763                   "have certified the senders identity.");
2764     }
2765   else if (level == 2)
2766     {
2767       unsigned long first_contact = std::max (m_uid.tofuInfo().signFirst(),
2768                                               m_uid.tofuInfo().encrFirst());
2769       char *time = format_date_from_gpgme (first_contact);
2770       /* i18n note signcount is always pulral because with signcount 1 we
2771        * would not be in this branch. */
2772       gpgrt_asprintf (&buf, _("The senders address is trusted, because "
2773                               "you have established a communication history "
2774                               "with this address starting on %s.\n"
2775                               "You encrypted %i and verified %i messages since."),
2776                               time, m_uid.tofuInfo().encrCount(),
2777                               m_uid.tofuInfo().signCount ());
2778       memdbg_alloc (buf);
2779       xfree (time);
2780       message = buf;
2781       xfree (buf);
2782     }
2783   else if (level == 1)
2784     {
2785       /* This could be marginal trust through pgp, or tofu with little
2786          history. */
2787       if (m_uid.tofuInfo ().signCount() == 1)
2788         {
2789           message += _("The senders signature was verified for the first time.");
2790         }
2791       else if (m_uid.tofuInfo ().validity() == TofuInfo::Validity::LittleHistory)
2792         {
2793           unsigned long first_contact = std::max (m_uid.tofuInfo().signFirst(),
2794                                                   m_uid.tofuInfo().encrFirst());
2795           char *time = format_date_from_gpgme (first_contact);
2796           gpgrt_asprintf (&buf, _("The senders address is not trustworthy yet because "
2797                                   "you only verified %i messages and encrypted %i messages to "
2798                                   "it since %s."),
2799                                   m_uid.tofuInfo().signCount (),
2800                                   m_uid.tofuInfo().encrCount (), time);
2801           memdbg_alloc (buf);
2802           xfree (time);
2803           message = buf;
2804           xfree (buf);
2805         }
2806     }
2807   else
2808     {
2809       /* Now we are in level 0, this could be a technical problem, no key
2810          or just unkown. */
2811       message = isEncrypted () ? _("But the sender address is not trustworthy because:") :
2812                                   _("The sender address is not trustworthy because:");
2813       message += "\n";
2814       keyFound = !(m_sig.summary() & Signature::Summary::KeyMissing);
2815
2816       bool general_problem = true;
2817       /* First the general stuff. */
2818       if (m_sig.summary() & Signature::Summary::Red)
2819         {
2820           message += _("The signature is invalid: \n");
2821         }
2822       else if (m_sig.summary() & Signature::Summary::SysError ||
2823                m_verify_result.numSignatures() < 1)
2824         {
2825           message += _("There was an error verifying the signature.\n");
2826           const auto err = m_sig.status ();
2827           if (err)
2828             {
2829               message += err.asString () + std::string ("\n");
2830             }
2831         }
2832       else if (m_sig.summary() & Signature::Summary::SigExpired)
2833         {
2834           message += _("The signature is expired.\n");
2835         }
2836       else
2837         {
2838           message += isOpenPGP ? _("The used key") : _("The used certificate");
2839           message += " ";
2840           general_problem = false;
2841         }
2842
2843       /* Now the key problems */
2844       if ((m_sig.summary() & Signature::Summary::KeyMissing))
2845         {
2846           message += _("is not available.");
2847         }
2848       else if ((m_sig.summary() & Signature::Summary::KeyRevoked))
2849         {
2850           message += _("is revoked.");
2851         }
2852       else if ((m_sig.summary() & Signature::Summary::KeyExpired))
2853         {
2854           message += _("is expired.");
2855         }
2856       else if ((m_sig.summary() & Signature::Summary::BadPolicy))
2857         {
2858           message += _("is not meant for signing.");
2859         }
2860       else if ((m_sig.summary() & Signature::Summary::CrlMissing))
2861         {
2862           message += _("could not be checked for revocation.");
2863         }
2864       else if ((m_sig.summary() & Signature::Summary::CrlTooOld))
2865         {
2866           message += _("could not be checked for revocation.");
2867         }
2868       else if ((m_sig.summary() & Signature::Summary::TofuConflict) ||
2869                m_uid.tofuInfo().validity() == TofuInfo::Conflict)
2870         {
2871           message += _("is not the same as the key that was used "
2872                        "for this address in the past.");
2873           hasConflict = true;
2874         }
2875       else if (m_uid.isNull())
2876         {
2877           gpgrt_asprintf (&buf, _("does not claim the address: \"%s\"."),
2878                           getSender_o ().c_str());
2879           memdbg_alloc (buf);
2880           message += buf;
2881           xfree (buf);
2882         }
2883       else if (((m_sig.validity() & Signature::Validity::Undefined) ||
2884                (m_sig.validity() & Signature::Validity::Unknown) ||
2885                (m_sig.summary() == Signature::Summary::None) ||
2886                (m_sig.validity() == 0))&& !general_problem)
2887         {
2888            /* Bit of a catch all for weird results. */
2889           if (isOpenPGP)
2890             {
2891               message += _("is not certified by any trustworthy key.");
2892             }
2893           else
2894             {
2895               message += _("is not certified by a trustworthy Certificate Authority or the Certificate Authority is unknown.");
2896             }
2897         }
2898       else if (m_uid.isRevoked())
2899         {
2900           message += _("The sender marked this address as revoked.");
2901         }
2902       else if ((m_sig.validity() & Signature::Validity::Never))
2903         {
2904           message += _("is marked as not trustworthy.");
2905         }
2906     }
2907    message += "\n\n";
2908    if (in_de_vs_mode ())
2909      {
2910        if (isSigned ())
2911          {
2912            if (m_sig.isDeVs ())
2913              {
2914                message += _("The signature is VS-NfD-compliant.");
2915              }
2916            else
2917              {
2918                message += _("The signature is not VS-NfD-compliant.");
2919              }
2920            message += "\n";
2921          }
2922        if (isEncrypted ())
2923          {
2924            if (m_decrypt_result.isDeVs ())
2925              {
2926                message += _("The encryption is VS-NfD-compliant.");
2927              }
2928            else
2929              {
2930                message += _("The encryption is not VS-NfD-compliant.");
2931              }
2932            message += "\n\n";
2933          }
2934        else
2935          {
2936            message += "\n";
2937          }
2938      }
2939    if (hasConflict)
2940     {
2941       message += _("Click here to change the key used for this address.");
2942     }
2943   else if (keyFound)
2944     {
2945       message +=  isOpenPGP ? _("Click here for details about the key.") :
2946                               _("Click here for details about the certificate.");
2947     }
2948   else
2949     {
2950       message +=  isOpenPGP ? _("Click here to search the key on the configured keyserver.") :
2951                               _("Click here to search the certificate on the configured X509 keyserver.");
2952     }
2953   TRETURN message;
2954 }
2955
2956 int
2957 Mail::get_signature_level () const
2958 {
2959   TSTART;
2960   if (!m_is_signed)
2961     {
2962       TRETURN 0;
2963     }
2964
2965   if (m_uid.isNull ())
2966     {
2967       /* No m_uid matches our sender. */
2968       TRETURN 0;
2969     }
2970
2971   if (m_is_valid && (m_uid.validity () == UserID::Validity::Ultimate ||
2972       (m_uid.validity () == UserID::Validity::Full &&
2973       level_4_check (m_uid))) && (!in_de_vs_mode () || m_sig.isDeVs()))
2974     {
2975       TRETURN 4;
2976     }
2977   if (m_is_valid && m_uid.validity () == UserID::Validity::Full &&
2978       (!in_de_vs_mode () || m_sig.isDeVs()))
2979     {
2980       TRETURN 3;
2981     }
2982   if (m_is_valid)
2983     {
2984       TRETURN 2;
2985     }
2986   if (m_sig.validity() == Signature::Validity::Marginal)
2987     {
2988       TRETURN 1;
2989     }
2990   if (m_sig.summary() & Signature::Summary::TofuConflict ||
2991       m_uid.tofuInfo().validity() == TofuInfo::Conflict)
2992     {
2993       TRETURN 0;
2994     }
2995   TRETURN 0;
2996 }
2997
2998 int
2999 Mail::getCryptoIconID () const
3000 {
3001   TSTART;
3002   int level = get_signature_level ();
3003   int offset = isEncrypted () ? ENCRYPT_ICON_OFFSET : 0;
3004   TRETURN IDI_LEVEL_0 + level + offset;
3005 }
3006
3007 const char*
3008 Mail::getSigFpr () const
3009 {
3010   TSTART;
3011   if (!m_is_signed || m_sig.isNull())
3012     {
3013       TRETURN nullptr;
3014     }
3015   TRETURN m_sig.fingerprint();
3016 }
3017
3018 /** Try to locate the keys for all recipients */
3019 void
3020 Mail::locateKeys_o ()
3021 {
3022   TSTART;
3023   if (m_locate_in_progress)
3024     {
3025       /** XXX
3026         The strangest thing seems to happen here:
3027         In get_recipients the lookup for "AddressEntry" on
3028         an unresolved address might cause network traffic.
3029
3030         So Outlook somehow "detaches" this call and keeps
3031         processing window messages while the call is running.
3032
3033         So our do_delayed_locate might trigger a second locate.
3034         If we access the OOM in this call while we access the
3035         same object in the blocked "detached" call we crash.
3036         (T3931)
3037
3038         After the window message is handled outlook retunrs
3039         in the original lookup.
3040
3041         A better fix here might be a non recursive lock
3042         of the OOM. But I expect that if we lock the handling
3043         of the Windowmessage we might deadlock.
3044         */
3045       log_debug ("%s:%s: Locate for %p already in progress.",
3046                  SRCNAME, __func__, this);
3047       TRETURN;
3048     }
3049   m_locate_in_progress = true;
3050
3051   Addressbook::check_o (this);
3052
3053   if (opt.autoresolve)
3054     {
3055       // First update oom data to have recipients and sender updated.
3056       updateOOMData_o ();
3057       KeyCache::instance()->startLocateSecret (getSender_o ().c_str (), this);
3058       KeyCache::instance()->startLocate (getSender_o ().c_str (), this);
3059       KeyCache::instance()->startLocate (getCachedRecipients (), this);
3060     }
3061
3062   autosecureCheck ();
3063
3064   m_locate_in_progress = false;
3065   TRETURN;
3066 }
3067
3068 bool
3069 Mail::isHTMLAlternative () const
3070 {
3071   TSTART;
3072   TRETURN m_is_html_alternative;
3073 }
3074
3075 char *
3076 Mail::takeCachedHTMLBody ()
3077 {
3078   TSTART;
3079   char *ret = m_cached_html_body;
3080   m_cached_html_body = nullptr;
3081   TRETURN ret;
3082 }
3083
3084 char *
3085 Mail::takeCachedPlainBody ()
3086 {
3087   TSTART;
3088   char *ret = m_cached_plain_body;
3089   m_cached_plain_body = nullptr;
3090   TRETURN ret;
3091 }
3092
3093 int
3094 Mail::getCryptoFlags () const
3095 {
3096   TSTART;
3097   TRETURN m_crypto_flags;
3098 }
3099
3100 void
3101 Mail::setNeedsEncrypt (bool value)
3102 {
3103   TSTART;
3104   m_needs_encrypt = value;
3105   TRETURN;
3106 }
3107
3108 bool
3109 Mail::getNeedsEncrypt () const
3110 {
3111   TSTART;
3112   TRETURN m_needs_encrypt;
3113 }
3114
3115 std::vector<std::string>
3116 Mail::getCachedRecipients ()
3117 {
3118   TSTART;
3119   TRETURN m_cached_recipients;
3120 }
3121
3122 void
3123 Mail::appendToInlineBody (const std::string &data)
3124 {
3125   TSTART;
3126   m_inline_body += data;
3127   TRETURN;
3128 }
3129
3130 int
3131 Mail::inlineBodyToBody_o ()
3132 {
3133   TSTART;
3134   if (!m_crypter)
3135     {
3136       log_error ("%s:%s: No crypter.",
3137                  SRCNAME, __func__);
3138       TRETURN -1;
3139     }
3140
3141   const auto body = m_crypter->get_inline_data ();
3142   if (body.empty())
3143     {
3144       TRETURN 0;
3145     }
3146
3147   /* For inline we always work with UTF-8 */
3148   put_oom_int (m_mailitem, "InternetCodepage", 65001);
3149
3150   int ret = put_oom_string (m_mailitem, "Body",
3151                             body.c_str ());
3152   TRETURN ret;
3153 }
3154
3155 void
3156 Mail::updateCryptMAPI_m ()
3157 {
3158   TSTART;
3159   log_debug ("%s:%s: Update crypt mapi",
3160              SRCNAME, __func__);
3161   if (m_crypt_state != NeedsUpdateInMAPI)
3162     {
3163       log_debug ("%s:%s: invalid state %i",
3164                  SRCNAME, __func__, m_crypt_state);
3165       TRETURN;
3166     }
3167   if (!m_crypter)
3168     {
3169       if (!m_mime_data.empty())
3170         {
3171           log_debug ("%s:%s: Have override mime data creating dummy crypter",
3172                      SRCNAME, __func__);
3173           m_crypter = std::shared_ptr <CryptController> (new CryptController (this, false,
3174                                                                               false,
3175                                                                               GpgME::UnknownProtocol));
3176         }
3177       else
3178         {
3179           log_error ("%s:%s: No crypter.",
3180                      SRCNAME, __func__);
3181           m_crypt_state = NoCryptMail;
3182           TRETURN;
3183         }
3184     }
3185
3186   if (m_crypter->update_mail_mapi ())
3187     {
3188       log_error ("%s:%s: Failed to update MAPI after crypt",
3189                  SRCNAME, __func__);
3190       m_crypt_state = NoCryptMail;
3191     }
3192   else
3193     {
3194       m_crypt_state = WantsSendMIME;
3195     }
3196
3197   /** If sync we need the crypter in update_crypt_oom */
3198   if (!isAsyncCryptDisabled ())
3199     {
3200       // We don't need the crypter anymore.
3201       resetCrypter ();
3202     }
3203   TRETURN;
3204 }
3205
3206 /** Checks in OOM if the body is either
3207   empty or contains the -----BEGIN tag.
3208   pair.first -> true if body starts with -----BEGIN
3209   pair.second -> true if body is empty. */
3210 static std::pair<bool, bool>
3211 has_crypt_or_empty_body_oom (Mail *mail)
3212 {
3213   TSTART;
3214   auto body = mail->getBody_o ();
3215   std::pair<bool, bool> ret;
3216   ret.first = false;
3217   ret.second = false;
3218   ltrim (body);
3219   if (body.size() > 10 && !strncmp (body.c_str(), "-----BEGIN", 10))
3220     {
3221       ret.first = true;
3222       TRETURN ret;
3223     }
3224   if (!body.size())
3225     {
3226       ret.second = true;
3227     }
3228   else
3229     {
3230       log_data ("%s:%s: Body found in %p : \"%s\"",
3231                        SRCNAME, __func__, mail, body.c_str ());
3232     }
3233   TRETURN ret;
3234 }
3235
3236 void
3237 Mail::updateCryptOOM_o ()
3238 {
3239   TSTART;
3240   log_debug ("%s:%s: Update crypt oom for %p",
3241              SRCNAME, __func__, this);
3242   if (m_crypt_state != NeedsUpdateInOOM)
3243     {
3244       log_debug ("%s:%s: invalid state %i",
3245                  SRCNAME, __func__, m_crypt_state);
3246       resetCrypter ();
3247       TRETURN;
3248     }
3249
3250   if (getDoPGPInline ())
3251     {
3252       if (inlineBodyToBody_o ())
3253         {
3254           log_error ("%s:%s: Inline body to body failed %p.",
3255                      SRCNAME, __func__, this);
3256           gpgol_bug (get_active_hwnd(), ERR_INLINE_BODY_TO_BODY);
3257           m_crypt_state = NoCryptMail;
3258           TRETURN;
3259         }
3260     }
3261
3262   if (m_crypter->get_protocol () == GpgME::CMS && m_crypter->is_encrypter ())
3263     {
3264       /* We put the PIDNameContentType headers here for exchange
3265          because this is the only way we found to inject the
3266          smime-type. */
3267       if (put_pa_string (m_mailitem,
3268                          PR_PIDNameContentType_DASL,
3269                          "application/pkcs7-mime;smime-type=\"enveloped-data\";name=smime.p7m"))
3270         {
3271           log_debug ("%s:%s: Failed to put PIDNameContentType for %p.",
3272                      SRCNAME, __func__, this);
3273         }
3274     }
3275
3276   /** When doing async update_crypt_mapi follows and needs
3277     the crypter. */
3278   if (isAsyncCryptDisabled ())
3279     {
3280       resetCrypter ();
3281     }
3282
3283   const auto pair = has_crypt_or_empty_body_oom (this);
3284   if (pair.first)
3285     {
3286       log_debug ("%s:%s: Looks like inline body. You can pass %p.",
3287                  SRCNAME, __func__, this);
3288       m_crypt_state = WantsSendInline;
3289       TRETURN;
3290     }
3291
3292   // We are in MIME land. Wipe the body.
3293   if (wipe_o (true))
3294     {
3295       log_debug ("%s:%s: Cancel send for %p.",
3296                  SRCNAME, __func__, this);
3297       wchar_t *title = utf8_to_wchar (_("GpgOL: Encryption not possible!"));
3298       wchar_t *msg = utf8_to_wchar (_(
3299                                       "Outlook returned an error when trying to send the encrypted mail.\n\n"
3300                                       "Please restart Outlook and try again.\n\n"
3301                                       "If it still fails consider using an encrypted attachment or\n"
3302                                       "switching to PGP/Inline in GpgOL's options."));
3303       MessageBoxW (get_active_hwnd(), msg, title,
3304                    MB_ICONERROR | MB_OK);
3305       xfree (msg);
3306       xfree (title);
3307       m_crypt_state = NoCryptMail;
3308       TRETURN;
3309     }
3310   m_crypt_state = NeedsSecondAfterWrite;
3311   TRETURN;
3312 }
3313
3314 void
3315 Mail::setWindowEnabled_o (bool value)
3316 {
3317   TSTART;
3318   if (!value)
3319     {
3320       m_window = get_active_hwnd ();
3321     }
3322   log_debug ("%s:%s: enable window %p %i",
3323              SRCNAME, __func__, m_window, value);
3324
3325   EnableWindow (m_window, value ? TRUE : FALSE);
3326   TRETURN;
3327 }
3328
3329 bool
3330 Mail::check_inline_response ()
3331 {
3332   TSTART;
3333   /* Async sending is known to cause instabilities. So we keep
3334      a hidden option to disable it. */
3335   if (opt.sync_enc)
3336     {
3337       m_async_crypt_disabled = true;
3338       TRETURN m_async_crypt_disabled;
3339     }
3340
3341   m_async_crypt_disabled = false;
3342
3343   LPDISPATCH attachments = get_oom_object (m_mailitem, "Attachments");
3344   if (attachments)
3345     {
3346       /* This is horrible. But. For some kinds of attachments (we
3347          got reports about Office attachments the write in the
3348          send event triggered by our crypto done code fails with
3349          an exception. There does not appear to be a detectable
3350          pattern when this happens.
3351          As we can't be sure and do not know for which attachments
3352          this really happens we do not use async crypt for any
3353          mails with attachments. :-/
3354          Better be save (not crash) instead of nice (async).
3355
3356          TODO: Figure this out.
3357
3358          The log goes like this. We pass the send event. That triggers
3359          a write, which we pass. And then that fails. So it looks like
3360          moving to Outbox fails. Because we can save as much as we
3361          like before that.
3362
3363          Using the IMessage::SubmitMessage MAPI interface works, but
3364          as it is unstable in our current implementation we do not
3365          want to use it.
3366
3367          mailitem-events.cpp:Invoke: Passing send event for mime-encrypted message 12B7C6E0.
3368          application-events.cpp:Invoke: Unhandled Event: f002
3369          mailitem-events.cpp:Invoke: Write : 0ED4D058
3370          mailitem-events.cpp:Invoke: Passing write event.
3371          oomhelp.cpp:invoke_oom_method_with_parms_type: Method 'Send' invokation failed: 0x80020009
3372          oomhelp.cpp:dump_excepinfo: Exception:
3373          wCode: 0x1000
3374          wReserved: 0x0
3375          source: Microsoft Outlook
3376          desc: The operation failed.  The messaging interfaces have returned an unknown error. If the problem persists, restart Outlook.
3377          help: null
3378          helpCtx: 0x0
3379          deferredFill: 00000000
3380          scode: 0x80040119
3381       */
3382
3383       int count = get_oom_int (attachments, "Count");
3384       gpgol_release (attachments);
3385
3386       if (count)
3387         {
3388           m_async_crypt_disabled = true;
3389           log_debug ("%s:%s: Detected attachments. "
3390                      "Disabling async crypt due to T4131.",
3391                      SRCNAME, __func__);
3392           TRETURN m_async_crypt_disabled;
3393         }
3394    }
3395
3396   LPDISPATCH app = GpgolAddin::get_instance ()->get_application ();
3397   if (!app)
3398     {
3399       TRACEPOINT;
3400       TRETURN false;
3401     }
3402
3403   LPDISPATCH explorer = get_oom_object (app, "ActiveExplorer");
3404
3405   if (!explorer)
3406     {
3407       TRACEPOINT;
3408       TRETURN false;
3409     }
3410
3411   LPDISPATCH inlineResponse = get_oom_object (explorer, "ActiveInlineResponse");
3412   gpgol_release (explorer);
3413
3414   if (!inlineResponse)
3415     {
3416       TRETURN false;
3417     }
3418
3419   // We have inline response
3420   // Check if we are it. It's a bit naive but meh. Worst case
3421   // is that we think inline response too often and do sync
3422   // crypt where we could do async crypt.
3423   char * inlineSubject = get_oom_string (inlineResponse, "Subject");
3424   gpgol_release (inlineResponse);
3425
3426   const auto subject = getSubject_o ();
3427   if (inlineResponse && !subject.empty() && !strcmp (subject.c_str (), inlineSubject))
3428     {
3429       log_debug ("%s:%s: Detected inline response for '%p'",
3430                  SRCNAME, __func__, this);
3431       m_async_crypt_disabled = true;
3432     }
3433   xfree (inlineSubject);
3434
3435   TRETURN m_async_crypt_disabled;
3436 }
3437
3438 // static
3439 Mail *
3440 Mail::getLastMail ()
3441 {
3442   TSTART;
3443   if (!s_last_mail || !isValidPtr (s_last_mail))
3444     {
3445       s_last_mail = nullptr;
3446     }
3447   TRETURN s_last_mail;
3448 }
3449
3450 // static
3451 void
3452 Mail::clearLastMail ()
3453 {
3454   TSTART;
3455   s_last_mail = nullptr;
3456   TRETURN;
3457 }
3458
3459 // static
3460 void
3461 Mail::locateAllCryptoRecipients_o ()
3462 {
3463   TSTART;
3464   gpgol_lock (&mail_map_lock);
3465   std::map<LPDISPATCH, Mail *>::iterator it;
3466   auto mail_map_copy = s_mail_map;
3467   gpgol_unlock (&mail_map_lock);
3468   for (it = mail_map_copy.begin(); it != mail_map_copy.end(); ++it)
3469     {
3470       if (it->second->needs_crypto_m ())
3471         {
3472           it->second->locateKeys_o ();
3473         }
3474     }
3475   TRETURN;
3476 }
3477
3478 int
3479 Mail::removeAllAttachments_o ()
3480 {
3481   TSTART;
3482   int ret = 0;
3483   LPDISPATCH attachments = get_oom_object (m_mailitem, "Attachments");
3484   if (!attachments)
3485     {
3486       TRACEPOINT;
3487       TRETURN 0;
3488     }
3489   int count = get_oom_int (attachments, "Count");
3490   LPDISPATCH to_delete[count];
3491
3492   /* Populate the array so that we don't get in an index mess */
3493   for (int i = 1; i <= count; i++)
3494     {
3495       auto item_str = std::string("Item(") + std::to_string (i) + ")";
3496       to_delete[i-1] = get_oom_object (attachments, item_str.c_str());
3497     }
3498   gpgol_release (attachments);
3499
3500   /* Now delete all attachments */
3501   for (int i = 0; i < count; i++)
3502     {
3503       LPDISPATCH attachment = to_delete[i];
3504
3505       if (!attachment)
3506         {
3507           log_error ("%s:%s: No such attachment %i",
3508                      SRCNAME, __func__, i);
3509           ret = -1;
3510         }
3511
3512       /* Delete the attachments that are marked to delete */
3513       if (invoke_oom_method (attachment, "Delete", NULL))
3514         {
3515           log_error ("%s:%s: Deleting attachment %i",
3516                      SRCNAME, __func__, i);
3517           ret = -1;
3518         }
3519       gpgol_release (attachment);
3520     }
3521   TRETURN ret;
3522 }
3523
3524 int
3525 Mail::removeOurAttachments_o ()
3526 {
3527   TSTART;
3528   LPDISPATCH attachments = get_oom_object (m_mailitem, "Attachments");
3529   if (!attachments)
3530     {
3531       TRACEPOINT;
3532       TRETURN 0;
3533     }
3534   int count = get_oom_int (attachments, "Count");
3535   LPDISPATCH to_delete[count];
3536   int del_cnt = 0;
3537   for (int i = 1; i <= count; i++)
3538     {
3539       auto item_str = std::string("Item(") + std::to_string (i) + ")";
3540       LPDISPATCH attachment = get_oom_object (attachments, item_str.c_str());
3541       if (!attachment)
3542         {
3543           TRACEPOINT;
3544           continue;
3545         }
3546
3547       attachtype_t att_type;
3548       if (get_pa_int (attachment, GPGOL_ATTACHTYPE_DASL, (int*) &att_type))
3549         {
3550           /* Not our attachment. */
3551           gpgol_release (attachment);
3552           continue;
3553         }
3554
3555       if (att_type == ATTACHTYPE_PGPBODY || att_type == ATTACHTYPE_MOSS ||
3556           att_type == ATTACHTYPE_MOSSTEMPL)
3557         {
3558           /* One of ours to delete. */
3559           to_delete[del_cnt++] = attachment;
3560           /* Dont' release yet */
3561           continue;
3562         }
3563       gpgol_release (attachment);
3564     }
3565   gpgol_release (attachments);
3566
3567   int ret = 0;
3568
3569   for (int i = 0; i < del_cnt; i++)
3570     {
3571       LPDISPATCH attachment = to_delete[i];
3572
3573       /* Delete the attachments that are marked to delete */
3574       if (invoke_oom_method (attachment, "Delete", NULL))
3575         {
3576           log_error ("%s:%s: Error: deleting attachment %i",
3577                      SRCNAME, __func__, i);
3578           ret = -1;
3579         }
3580       gpgol_release (attachment);
3581     }
3582   TRETURN ret;
3583 }
3584
3585 /* We are very verbose because if we fail it might mean
3586    that we have leaked plaintext -> critical. */
3587 bool
3588 Mail::hasCryptedOrEmptyBody_o ()
3589 {
3590   TSTART;
3591   const auto pair = has_crypt_or_empty_body_oom (this);
3592
3593   if (pair.first /* encrypted marker */)
3594     {
3595       log_debug ("%s:%s: Crypt Marker detected in OOM body. Return true %p.",
3596                  SRCNAME, __func__, this);
3597       TRETURN true;
3598     }
3599
3600   if (!pair.second)
3601     {
3602       log_debug ("%s:%s: Unexpected content detected. Return false %p.",
3603                  SRCNAME, __func__, this);
3604       TRETURN false;
3605     }
3606
3607   // Pair second == true (is empty) can happen on OOM error.
3608   LPMESSAGE message = get_oom_base_message (m_mailitem);
3609   if (!message && pair.second)
3610     {
3611       if (message)
3612         {
3613           gpgol_release (message);
3614         }
3615       TRETURN true;
3616     }
3617
3618   size_t r_nbytes = 0;
3619   char *mapi_body = mapi_get_body (message, &r_nbytes);
3620   gpgol_release (message);
3621
3622   if (!mapi_body || !r_nbytes)
3623     {
3624       // Body or bytes are null. we are empty.
3625       xfree (mapi_body);
3626       log_debug ("%s:%s: MAPI error or empty message. Return true. %p.",
3627                  SRCNAME, __func__, this);
3628       TRETURN true;
3629     }
3630   if (r_nbytes > 10 && !strncmp (mapi_body, "-----BEGIN", 10))
3631     {
3632       // Body is crypt.
3633       log_debug ("%s:%s: MAPI Crypt marker detected. Return true. %p.",
3634                  SRCNAME, __func__, this);
3635       xfree (mapi_body);
3636       TRETURN true;
3637     }
3638
3639   xfree (mapi_body);
3640
3641   log_debug ("%s:%s: Found mapi body. Return false. %p.",
3642              SRCNAME, __func__, this);
3643
3644   TRETURN false;
3645 }
3646
3647 std::string
3648 Mail::getVerificationResultDump ()
3649 {
3650   TSTART;
3651   std::stringstream ss;
3652   ss << m_verify_result;
3653   TRETURN ss.str();
3654 }
3655
3656 void
3657 Mail::setBlockStatus_m ()
3658 {
3659   TSTART;
3660   SPropValue prop;
3661
3662   LPMESSAGE message = get_oom_base_message (m_mailitem);
3663
3664   prop.ulPropTag = PR_BLOCK_STATUS;
3665   prop.Value.l = 1;
3666   HRESULT hr = message->SetProps (1, &prop, NULL);
3667
3668   if (hr)
3669     {
3670       log_error ("%s:%s: can't set block value: hr=%#lx\n",
3671                  SRCNAME, __func__, hr);
3672     }
3673
3674   gpgol_release (message);
3675   TRETURN;
3676 }
3677
3678 void
3679 Mail::setBlockHTML (bool value)
3680 {
3681   TSTART;
3682   m_block_html = value;
3683   TRETURN;
3684 }
3685
3686 void
3687 Mail::incrementLocateCount ()
3688 {
3689   TSTART;
3690   m_locate_count++;
3691   TRETURN;
3692 }
3693
3694 void
3695 Mail::decrementLocateCount ()
3696 {
3697   TSTART;
3698   m_locate_count--;
3699
3700   if (m_locate_count < 0)
3701     {
3702       log_error ("%s:%s: locate count mismatch.",
3703                  SRCNAME, __func__);
3704       m_locate_count = 0;
3705     }
3706   if (!m_locate_count)
3707     {
3708       autosecureCheck ();
3709     }
3710   TRETURN;
3711 }
3712
3713 void
3714 Mail::autosecureCheck ()
3715 {
3716   TSTART;
3717   if (!opt.autosecure || !opt.autoresolve || m_manual_crypto_opts ||
3718       m_locate_count)
3719     {
3720       TRETURN;
3721     }
3722   bool ret = KeyCache::instance()->isMailResolvable (this);
3723
3724   log_debug ("%s:%s: status %i",
3725              SRCNAME, __func__, ret);
3726
3727   /* As we are safe to call at any time, because we need
3728    * to be triggered by the locator threads finishing we
3729    * need to actually set the draft info flags in
3730    * the ui thread. */
3731   do_in_ui_thread_async (ret ? DO_AUTO_SECURE : DONT_AUTO_SECURE,
3732                          this);
3733   TRETURN;
3734 }
3735
3736 void
3737 Mail::setDoAutosecure_m (bool value)
3738 {
3739   TSTART;
3740   TRACEPOINT;
3741   LPMESSAGE msg = get_oom_base_message (m_mailitem);
3742
3743   if (!msg)
3744     {
3745       TRACEPOINT;
3746       TRETURN;
3747     }
3748   /* We need to set a uuid so that autosecure can
3749      be disabled manually */
3750   setUUID_o ();
3751
3752   int old_flags = get_gpgol_draft_info_flags (msg);
3753   if (old_flags && m_first_autosecure_check &&
3754       /* Someone with always sign and autosecure active
3755        * will want to get autoencryption. */
3756       !(old_flags == 2 && opt.sign_default))
3757     {
3758       /* They were set explicily before us. This can be
3759        * because they were a draft (which is bad) or
3760        * because they are a reply/forward to a crypto mail
3761        * or because there are conflicting settings. */
3762       log_debug ("%s:%s: Mail %p had already flags set.",
3763                  SRCNAME, __func__, m_mailitem);
3764       m_first_autosecure_check = false;
3765       m_manual_crypto_opts = true;
3766       gpgol_release (msg);
3767       TRETURN;
3768     }
3769   m_first_autosecure_check = false;
3770   set_gpgol_draft_info_flags (msg, value ? 3 : opt.sign_default ? 2 : 0);
3771   gpgol_release (msg);
3772   gpgoladdin_invalidate_ui();
3773   TRETURN;
3774 }
3775
3776 void
3777 Mail::installFolderEventHandler_o()
3778 {
3779   TSTART;
3780   TRACEPOINT;
3781   LPDISPATCH folder = get_oom_object (m_mailitem, "Parent");
3782
3783   if (!folder)
3784     {
3785       TRACEPOINT;
3786       TRETURN;
3787     }
3788
3789   char *objName = get_object_name (folder);
3790   if (!objName || strcmp (objName, "MAPIFolder"))
3791     {
3792       log_debug ("%s:%s: Mail %p parent is not a mapi folder.",
3793                  SRCNAME, __func__, m_mailitem);
3794       xfree (objName);
3795       gpgol_release (folder);
3796       TRETURN;
3797     }
3798   xfree (objName);
3799
3800   char *path = get_oom_string (folder, "FullFolderPath");
3801   if (!path)
3802     {
3803       TRACEPOINT;
3804       path = get_oom_string (folder, "FolderPath");
3805     }
3806   if (!path)
3807     {
3808       log_error ("%s:%s: Mail %p parent has no folder path.",
3809                  SRCNAME, __func__, m_mailitem);
3810       gpgol_release (folder);
3811       TRETURN;
3812     }
3813
3814   std::string strPath (path);
3815   xfree (path);
3816
3817   if (s_folder_events_map.find (strPath) == s_folder_events_map.end())
3818     {
3819       log_debug ("%s:%s: Install folder events watcher for %s.",
3820                  SRCNAME, __func__, anonstr (strPath.c_str()));
3821       const auto sink = install_FolderEvents_sink (folder);
3822       s_folder_events_map.insert (std::make_pair (strPath, sink));
3823     }
3824
3825   /* Folder already registered */
3826   gpgol_release (folder);
3827   TRETURN;
3828 }
3829
3830 void
3831 Mail::refCurrentItem()
3832 {
3833   TSTART;
3834   if (m_currentItemRef)
3835     {
3836       log_debug ("%s:%s: Current item multi ref. Bug?",
3837                  SRCNAME, __func__);
3838       TRETURN;
3839     }
3840   /* This prevents a crash in Outlook 2013 when sending a mail as it
3841    * would unload too early.
3842    *
3843    * As it didn't crash when the mail was opened in Outlook Spy this
3844    * mimics that the mail is inspected somewhere else. */
3845   m_currentItemRef = get_oom_object (m_mailitem, "GetInspector.CurrentItem");
3846   TRETURN;
3847 }
3848
3849 void
3850 Mail::releaseCurrentItem()
3851 {
3852   TSTART;
3853   if (!m_currentItemRef)
3854     {
3855       TRETURN;
3856     }
3857   log_oom ("%s:%s: releasing CurrentItem ref %p",
3858                  SRCNAME, __func__, m_currentItemRef);
3859   LPDISPATCH tmp = m_currentItemRef;
3860   m_currentItemRef = nullptr;
3861   /* This can cause our destruction */
3862   gpgol_release (tmp);
3863   TRETURN;
3864 }