Add explicit decrypt as WindowMessage handler
[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
1070   auto cipherstream = get_attachment_stream_o (m_mailitem, m_moss_position);
1071   if (!cipherstream)
1072     {
1073       m_is_junk = is_junk_mail (m_mailitem);
1074       if (m_is_junk)
1075         {
1076           log_debug ("%s:%s: Detected: %p as junk",
1077                      SRCNAME, __func__, m_mailitem);
1078           auto mngr = CategoryManager::instance ();
1079           m_store_id = mngr->addCategoryToMail (this,
1080                                    CategoryManager::getJunkMailCategory (),
1081                                    3 /* peach */);
1082           installFolderEventHandler_o ();
1083           TRETURN 0;
1084         }
1085       log_debug ("%s:%s: Failed to get cipherstream. Aborting handling.",
1086                  SRCNAME, __func__);
1087       m_type = MSGTYPE_UNKNOWN;
1088       TRETURN 1;
1089     }
1090
1091   setUUID_o ();
1092   m_processed = true;
1093   m_pass_write = false;
1094
1095   /* Insert placeholder */
1096   char *placeholder_buf = nullptr;
1097   if (m_type == MSGTYPE_GPGOL_WKS_CONFIRMATION)
1098     {
1099       gpgrt_asprintf (&placeholder_buf, opt.prefer_html ? decrypt_template_html :
1100                       decrypt_template,
1101                       "OpenPGP",
1102                       _("Pubkey directory confirmation"),
1103                       _("This is a confirmation request to publish your Pubkey in the "
1104                         "directory for your domain.\n\n"
1105                         "<p>If you did not request to publish your Pubkey in your providers "
1106                         "directory, simply ignore this message.</p>\n"));
1107     }
1108   else if (gpgrt_asprintf (&placeholder_buf, opt.prefer_html ? decrypt_template_html :
1109                       decrypt_template,
1110                       isSMIME_m () ? "S/MIME" : "OpenPGP",
1111                       _("message"),
1112                       _("Please wait while the message is being decrypted / verified...")) == -1)
1113     {
1114       log_error ("%s:%s: Failed to format placeholder.",
1115                  SRCNAME, __func__);
1116       TRETURN 1;
1117     }
1118
1119   if (opt.prefer_html)
1120     {
1121       char *tmp = get_oom_string (m_mailitem, "HTMLBody");
1122       if (!tmp)
1123         {
1124           TRACEPOINT;
1125           TRETURN 1;
1126         }
1127       m_orig_body = tmp;
1128       xfree (tmp);
1129       if (put_oom_string (m_mailitem, "HTMLBody", placeholder_buf))
1130         {
1131           log_error ("%s:%s: Failed to modify html body of item.",
1132                      SRCNAME, __func__);
1133         }
1134     }
1135   else
1136     {
1137       char *tmp = get_oom_string (m_mailitem, "Body");
1138       if (!tmp)
1139         {
1140           TRACEPOINT;
1141           TRETURN 1;
1142         }
1143       m_orig_body = tmp;
1144       xfree (tmp);
1145       if (put_oom_string (m_mailitem, "Body", placeholder_buf))
1146         {
1147           log_error ("%s:%s: Failed to modify body of item.",
1148                      SRCNAME, __func__);
1149         }
1150     }
1151   memdbg_alloc (placeholder_buf);
1152   xfree (placeholder_buf);
1153
1154   if (m_type == MSGTYPE_GPGOL_WKS_CONFIRMATION)
1155     {
1156       WKSHelper::instance ()->handle_confirmation_read (this, cipherstream);
1157       TRETURN 0;
1158     }
1159
1160   m_parser = std::shared_ptr <ParseController> (new ParseController (cipherstream, m_type));
1161   m_parser->setSender(GpgME::UserID::addrSpecFromString(getSender_o ().c_str()));
1162   log_data ("%s:%s: Parser for \"%s\" is %p",
1163                    SRCNAME, __func__, getSubject_o ().c_str(), m_parser.get());
1164   gpgol_release (cipherstream);
1165
1166   if (!opt.sync_dec)
1167     {
1168       HANDLE parser_thread = CreateThread (NULL, 0, do_parsing, (LPVOID) this, 0,
1169                                            NULL);
1170
1171       if (!parser_thread)
1172         {
1173           log_error ("%s:%s: Failed to create decrypt / verify thread.",
1174                      SRCNAME, __func__);
1175         }
1176       CloseHandle (parser_thread);
1177       TRETURN 0;
1178     }
1179   else
1180     {
1181       /* Parse synchronously */
1182       do_parsing ((LPVOID) this);
1183       parsing_done ();
1184       TRETURN 0;
1185     }
1186 }
1187
1188 void find_and_replace(std::string& source, const std::string &find,
1189                       const std::string &replace)
1190 {
1191   TSTART;
1192   for(std::string::size_type i = 0; (i = source.find(find, i)) != std::string::npos;)
1193     {
1194       source.replace(i, find.length(), replace);
1195       i += replace.length();
1196     }
1197   TRETURN;
1198 }
1199
1200 void
1201 Mail::updateBody_o ()
1202 {
1203   TSTART;
1204   if (!m_parser)
1205     {
1206       TRACEPOINT;
1207       TRETURN;
1208     }
1209
1210   const auto error = m_parser->get_formatted_error ();
1211   if (!error.empty())
1212     {
1213       if (opt.prefer_html)
1214         {
1215           if (put_oom_string (m_mailitem, "HTMLBody",
1216                               error.c_str ()))
1217             {
1218               log_error ("%s:%s: Failed to modify html body of item.",
1219                          SRCNAME, __func__);
1220             }
1221           else
1222             {
1223               log_debug ("%s:%s: Set error html to: '%s'",
1224                          SRCNAME, __func__, error.c_str ());
1225             }
1226
1227         }
1228       else
1229         {
1230           if (put_oom_string (m_mailitem, "Body",
1231                               error.c_str ()))
1232             {
1233               log_error ("%s:%s: Failed to modify html body of item.",
1234                          SRCNAME, __func__);
1235             }
1236           else
1237             {
1238               log_debug ("%s:%s: Set error plain to: '%s'",
1239                          SRCNAME, __func__, error.c_str ());
1240             }
1241         }
1242       TRETURN;
1243     }
1244   if (m_verify_result.error())
1245     {
1246       log_error ("%s:%s: Verification failed. Restoring Body.",
1247                  SRCNAME, __func__);
1248       if (opt.prefer_html)
1249         {
1250           if (put_oom_string (m_mailitem, "HTMLBody", m_orig_body.c_str ()))
1251             {
1252               log_error ("%s:%s: Failed to modify html body of item.",
1253                          SRCNAME, __func__);
1254             }
1255         }
1256       else
1257         {
1258           if (put_oom_string (m_mailitem, "Body", m_orig_body.c_str ()))
1259             {
1260               log_error ("%s:%s: Failed to modify html body of item.",
1261                          SRCNAME, __func__);
1262             }
1263         }
1264       TRETURN;
1265     }
1266   // No need to carry body anymore
1267   m_orig_body = std::string();
1268   auto html = m_parser->get_html_body ();
1269   auto body = m_parser->get_body ();
1270   /** Outlook does not show newlines if \r\r\n is a newline. We replace
1271     these as apparently some other buggy MUA sends this. */
1272   find_and_replace (html, "\r\r\n", "\r\n");
1273   if (opt.prefer_html && !html.empty())
1274     {
1275       if (!m_block_html)
1276         {
1277           const auto charset = m_parser->get_html_charset();
1278
1279           int codepage = 0;
1280           if (charset.empty())
1281             {
1282               codepage = get_oom_int (m_mailitem, "InternetCodepage");
1283               log_debug ("%s:%s: Did not find html charset."
1284                          " Using internet Codepage %i.",
1285                          SRCNAME, __func__, codepage);
1286             }
1287
1288           char *converted = ansi_charset_to_utf8 (charset.c_str(), html.c_str(),
1289                                                   html.size(), codepage);
1290           TRACEPOINT;
1291           int ret = put_oom_string (m_mailitem, "HTMLBody", converted ?
1292                                                             converted : "");
1293           TRACEPOINT;
1294           xfree (converted);
1295           if (ret)
1296             {
1297               log_error ("%s:%s: Failed to modify html body of item.",
1298                          SRCNAME, __func__);
1299             }
1300
1301           TRETURN;
1302         }
1303       else if (!body.empty())
1304         {
1305           /* We had a multipart/alternative mail but html should be
1306              blocked. So we prefer the text/plain part and warn
1307              once about this so that we hopefully don't get too
1308              many bugreports about this. */
1309           if (!opt.smime_html_warn_shown)
1310             {
1311               std::string caption = _("GpgOL") + std::string (": ") +
1312                 std::string (_("HTML display disabled."));
1313               std::string buf = _("HTML content in unsigned S/MIME mails "
1314                                   "is insecure.");
1315               buf += "\n";
1316               buf += _("GpgOL will only show such mails as text.");
1317
1318               buf += "\n\n";
1319               buf += _("This message is shown only once.");
1320
1321               gpgol_message_box (getWindow (), buf.c_str(), caption.c_str(),
1322                                  MB_OK);
1323               opt.smime_html_warn_shown = true;
1324               write_options ();
1325             }
1326         }
1327     }
1328
1329   if (body.empty () && m_block_html && !html.empty())
1330     {
1331 #if 0
1332       Sadly the following code still offers to load external references
1333       it might also be too dangerous if Outlook somehow autoloads the
1334       references as soon as the Body is put into HTML
1335
1336
1337       // Fallback to show HTML as plaintext if HTML display
1338       // is blocked.
1339       log_error ("%s:%s: No text body. Putting HTML into plaintext.",
1340                  SRCNAME, __func__);
1341
1342       char *converted = ansi_charset_to_utf8 (m_parser->get_html_charset().c_str(),
1343                                               html.c_str(), html.size());
1344       int ret = put_oom_string (m_mailitem, "HTMLBody", converted ? converted : "");
1345       xfree (converted);
1346       if (ret)
1347         {
1348           log_error ("%s:%s: Failed to modify html body of item.",
1349                      SRCNAME, __func__);
1350           body = html;
1351         }
1352       else
1353         {
1354           char *plainBody = get_oom_string (m_mailitem, "Body");
1355
1356           if (!plainBody)
1357             {
1358               log_error ("%s:%s: Failed to obtain converted plain body.",
1359                          SRCNAME, __func__);
1360               body = html;
1361             }
1362           else
1363             {
1364               ret = put_oom_string (m_mailitem, "HTMLBody", plainBody);
1365               xfree (plainBody);
1366               if (ret)
1367                 {
1368                   log_error ("%s:%s: Failed to put plain into html body of item.",
1369                              SRCNAME, __func__);
1370                   body = html;
1371                 }
1372               else
1373                 {
1374                   TRETURN;
1375                 }
1376             }
1377         }
1378 #endif
1379       body = html;
1380       std::string caption = _("GpgOL") + std::string (": ") +
1381         std::string (_("HTML display disabled."));
1382       std::string buf = _("HTML content in unsigned S/MIME mails "
1383                           "is insecure.");
1384       buf += "\n";
1385       buf += _("GpgOL will only show such mails as text.");
1386
1387       buf += "\n\n";
1388       buf += _("Please ask the sender to sign the message or\n"
1389                "to send it with a plain text alternative.");
1390
1391       gpgol_message_box (getWindow (), buf.c_str(), caption.c_str(),
1392                          MB_OK);
1393     }
1394
1395   find_and_replace (body, "\r\r\n", "\r\n");
1396
1397   const auto plain_charset = m_parser->get_body_charset();
1398
1399   int codepage = 0;
1400   if (plain_charset.empty())
1401     {
1402       codepage = get_oom_int (m_mailitem, "InternetCodepage");
1403       log_debug ("%s:%s: Did not find body charset. "
1404                  "Using internet Codepage %i.",
1405                  SRCNAME, __func__, codepage);
1406     }
1407
1408   char *converted = ansi_charset_to_utf8 (plain_charset.c_str(),
1409                                           body.c_str(), body.size(),
1410                                           codepage);
1411   TRACEPOINT;
1412   int ret = put_oom_string (m_mailitem, "Body", converted ? converted : "");
1413   TRACEPOINT;
1414   xfree (converted);
1415   if (ret)
1416     {
1417       log_error ("%s:%s: Failed to modify body of item.",
1418                  SRCNAME, __func__);
1419     }
1420   TRETURN;
1421 }
1422
1423 static int parsed_count;
1424
1425 void
1426 Mail::parsing_done()
1427 {
1428   TSTART;
1429   TRACEPOINT;
1430   log_oom ("Mail %p Parsing done for parser num %i: %p",
1431                  this, parsed_count++, m_parser.get());
1432   if (!m_parser)
1433     {
1434       /* This should not happen but it happens when outlook
1435          sends multiple ItemLoad events for the same Mail
1436          Object. In that case it could happen that one
1437          parser was already done while a second is now
1438          returning for the wrong mail (as it's looked up
1439          by uuid.)
1440
1441          We have a check in get_uuid that the uuid was
1442          not in the map before (and the parser is replaced).
1443          So this really really should not happen. We
1444          handle it anyway as we crash otherwise.
1445
1446          It should not happen because the parser is only
1447          created in decrypt_verify which is called in the
1448          read event. And even in there we check if the parser
1449          was set.
1450          */
1451       log_error ("%s:%s: No parser obj. For mail: %p",
1452                  SRCNAME, __func__, this);
1453       TRETURN;
1454     }
1455   /* Store the results. */
1456   m_decrypt_result = m_parser->decrypt_result ();
1457   m_verify_result = m_parser->verify_result ();
1458
1459   m_crypto_flags = 0;
1460   if (!m_decrypt_result.isNull())
1461     {
1462       m_crypto_flags |= 1;
1463     }
1464   if (m_verify_result.numSignatures())
1465     {
1466       m_crypto_flags |= 2;
1467     }
1468
1469   TRACEPOINT;
1470   updateSigstate ();
1471   m_needs_wipe = !m_is_send_again;
1472
1473   TRACEPOINT;
1474   /* Set categories according to the result. */
1475   updateCategories_o ();
1476
1477   TRACEPOINT;
1478   m_block_html = m_parser->shouldBlockHtml ();
1479
1480   if (m_block_html)
1481     {
1482       // Just to be careful.
1483       setBlockStatus_m ();
1484     }
1485
1486   TRACEPOINT;
1487   /* Update the body */
1488   updateBody_o ();
1489   TRACEPOINT;
1490
1491   /* Check that there are no unsigned / unencrypted messages. */
1492   checkAttachments_o ();
1493
1494   /* Update attachments */
1495   if (add_attachments_o (m_mailitem, m_parser->get_attachments()))
1496     {
1497       log_error ("%s:%s: Failed to update attachments.",
1498                  SRCNAME, __func__);
1499     }
1500
1501   if (m_is_send_again)
1502     {
1503       log_debug ("%s:%s: I think that this is the send again of a crypto mail.",
1504                  SRCNAME, __func__);
1505
1506       /* We no longer want to be treated like a crypto mail. */
1507       m_type = MSGTYPE_UNKNOWN;
1508       LPMESSAGE msg = get_oom_base_message (m_mailitem);
1509       if (!msg)
1510         {
1511           TRACEPOINT;
1512         }
1513       else
1514         {
1515           set_gpgol_draft_info_flags (msg, m_crypto_flags);
1516           gpgol_release (msg);
1517         }
1518       removeOurAttachments_o ();
1519     }
1520
1521   installFolderEventHandler_o ();
1522
1523   log_debug ("%s:%s: Delayed invalidate to update sigstate.",
1524              SRCNAME, __func__);
1525   CloseHandle(CreateThread (NULL, 0, delayed_invalidate_ui, (LPVOID) 300, 0,
1526                             NULL));
1527   TRACEPOINT;
1528   TRETURN;
1529 }
1530
1531 int
1532 Mail::encryptSignStart_o ()
1533 {
1534   TSTART;
1535   if (m_crypt_state != NeedsActualCrypt)
1536     {
1537       log_debug ("%s:%s: invalid state %i",
1538                  SRCNAME, __func__, m_crypt_state);
1539       TRETURN -1;
1540     }
1541   int flags = 0;
1542   if (!needs_crypto_m ())
1543     {
1544       TRETURN 0;
1545     }
1546   LPMESSAGE message = get_oom_base_message (m_mailitem);
1547   if (!message)
1548     {
1549       log_error ("%s:%s: Failed to get base message.",
1550                  SRCNAME, __func__);
1551       TRETURN -1;
1552     }
1553   flags = get_gpgol_draft_info_flags (message);
1554   gpgol_release (message);
1555
1556   const auto window = get_active_hwnd ();
1557
1558   if (m_is_gsuite)
1559     {
1560       auto att_table = mapi_create_attach_table (message, 0);
1561       int n_att_usable = count_usable_attachments (att_table);
1562       mapi_release_attach_table (att_table);
1563       /* Check for attachments if we have some abort. */
1564
1565       if (n_att_usable)
1566         {
1567           wchar_t *w_title = utf8_to_wchar (_(
1568                                               "GpgOL: Oops, G Suite Sync account detected"));
1569           wchar_t *msg = utf8_to_wchar (
1570                       _("G Suite Sync breaks outgoing crypto mails "
1571                         "with attachments.\nUsing crypto and attachments "
1572                         "with G Suite Sync is not supported.\n\n"
1573                         "See: https://dev.gnupg.org/T3545 for details."));
1574           MessageBoxW (window,
1575                        msg,
1576                        w_title,
1577                        MB_ICONINFORMATION|MB_OK);
1578           xfree (msg);
1579           xfree (w_title);
1580           TRETURN -1;
1581         }
1582     }
1583
1584   m_do_inline = m_is_gsuite ? true : opt.inline_pgp;
1585
1586   GpgME::Protocol proto = opt.enable_smime ? GpgME::UnknownProtocol: GpgME::OpenPGP;
1587   m_crypter = std::shared_ptr <CryptController> (new CryptController (this, flags & 1,
1588                                                                       flags & 2,
1589                                                                       proto));
1590
1591   // Careful from here on we have to check every
1592   // error condition with window enabling again.
1593   setWindowEnabled_o (false);
1594   if (m_crypter->collect_data ())
1595     {
1596       log_error ("%s:%s: Crypter for mail %p failed to collect data.",
1597                  SRCNAME, __func__, this);
1598       setWindowEnabled_o (true);
1599       TRETURN -1;
1600     }
1601
1602   if (!m_async_crypt_disabled)
1603     {
1604       CloseHandle(CreateThread (NULL, 0, do_crypt,
1605                                 (LPVOID) this, 0,
1606                                 NULL));
1607     }
1608   else
1609     {
1610       do_crypt (this);
1611     }
1612   TRETURN 0;
1613 }
1614
1615 int
1616 Mail::needs_crypto_m () const
1617 {
1618   TSTART;
1619   LPMESSAGE message = get_oom_message (m_mailitem);
1620   int ret;
1621   if (!message)
1622     {
1623       log_error ("%s:%s: Failed to get message.",
1624                  SRCNAME, __func__);
1625       TRETURN false;
1626     }
1627   ret = get_gpgol_draft_info_flags (message);
1628   gpgol_release(message);
1629   TRETURN ret;
1630 }
1631
1632 int
1633 Mail::wipe_o (bool force)
1634 {
1635   TSTART;
1636   if (!m_needs_wipe && !force)
1637     {
1638       TRETURN 0;
1639     }
1640   log_debug ("%s:%s: Removing plaintext from mailitem: %p.",
1641              SRCNAME, __func__, m_mailitem);
1642   if (put_oom_string (m_mailitem, "HTMLBody",
1643                       ""))
1644     {
1645       if (put_oom_string (m_mailitem, "Body",
1646                           ""))
1647         {
1648           log_debug ("%s:%s: Failed to wipe mailitem: %p.",
1649                      SRCNAME, __func__, m_mailitem);
1650           TRETURN -1;
1651         }
1652       TRETURN -1;
1653     }
1654   else
1655     {
1656       put_oom_string (m_mailitem, "Body", "");
1657     }
1658   m_needs_wipe = false;
1659   TRETURN 0;
1660 }
1661
1662 int
1663 Mail::updateOOMData_o ()
1664 {
1665   TSTART;
1666   char *buf = nullptr;
1667   log_debug ("%s:%s", SRCNAME, __func__);
1668
1669   if (!isCryptoMail ())
1670     {
1671       /* Update the body format. */
1672       m_is_html_alternative = get_oom_int (m_mailitem, "BodyFormat") > 1;
1673
1674       /* Store the body. It was not obvious for me (aheinecke) how
1675          to access this through MAPI. */
1676       if (m_is_html_alternative)
1677         {
1678           log_debug ("%s:%s: Is html alternative mail.", SRCNAME, __func__);
1679           xfree (m_cached_html_body);
1680           m_cached_html_body = get_oom_string (m_mailitem, "HTMLBody");
1681         }
1682       xfree (m_cached_plain_body);
1683       m_cached_plain_body = get_oom_string (m_mailitem, "Body");
1684
1685       m_cached_recipients = getRecipients_o ();
1686     }
1687   /* For some reason outlook may store the recipient address
1688      in the send using account field. If we have SMTP we prefer
1689      the SenderEmailAddress string. */
1690   if (isCryptoMail ())
1691     {
1692       /* This is the case where we are reading a mail and not composing.
1693          When composing we need to use the SendUsingAccount because if
1694          you send from the folder of userA but change the from to userB
1695          outlook will keep the SenderEmailAddress of UserA. This is all
1696          so horrible. */
1697       buf = get_sender_SenderEMailAddress (m_mailitem);
1698
1699       if (!buf)
1700         {
1701           /* Try the sender Object */
1702           buf = get_sender_Sender (m_mailitem);
1703         }
1704
1705       /* We also want to cache sent representing email address so that
1706          we can use it for verification information. */
1707       char *buf2 = get_sender_SentRepresentingAddress (m_mailitem);
1708
1709       if (buf2)
1710         {
1711           m_sent_on_behalf = buf2;
1712           xfree (buf2);
1713         }
1714     }
1715
1716   if (!buf)
1717     {
1718       buf = get_sender_SendUsingAccount (m_mailitem, &m_is_gsuite);
1719     }
1720   if (!buf && !isCryptoMail ())
1721     {
1722       /* Try the sender Object */
1723       buf = get_sender_Sender (m_mailitem);
1724     }
1725   if (!buf)
1726     {
1727       /* We don't have s sender object or SendUsingAccount,
1728          well, in that case fall back to the current user. */
1729       buf = get_sender_CurrentUser (m_mailitem);
1730     }
1731   if (!buf)
1732     {
1733       log_debug ("%s:%s: All fallbacks failed.",
1734                  SRCNAME, __func__);
1735       TRETURN -1;
1736     }
1737   m_sender = buf;
1738   xfree (buf);
1739   TRETURN 0;
1740 }
1741
1742 std::string
1743 Mail::getSender_o ()
1744 {
1745   TSTART;
1746   if (m_sender.empty())
1747     updateOOMData_o ();
1748   TRETURN m_sender;
1749 }
1750
1751 std::string
1752 Mail::getSender () const
1753 {
1754   TSTART;
1755   TRETURN m_sender;
1756 }
1757
1758 int
1759 Mail::closeAllMails_o ()
1760 {
1761   TSTART;
1762   int err = 0;
1763
1764   /* Detach Folder sinks */
1765   for (auto fit = s_folder_events_map.begin(); fit != s_folder_events_map.end(); ++fit)
1766     {
1767       detach_FolderEvents_sink (fit->second);
1768       gpgol_release (fit->second);
1769     }
1770   s_folder_events_map.clear();
1771
1772
1773   std::map<LPDISPATCH, Mail *>::iterator it;
1774   TRACEPOINT;
1775   gpgol_lock (&mail_map_lock);
1776   std::map<LPDISPATCH, Mail *> mail_map_copy = s_mail_map;
1777   gpgol_unlock (&mail_map_lock);
1778   for (it = mail_map_copy.begin(); it != mail_map_copy.end(); ++it)
1779     {
1780       /* XXX For non racy code the is_valid_ptr check should not
1781          be necessary but we crashed sometimes closing a destroyed
1782          mail. */
1783       if (!isValidPtr (it->second))
1784         {
1785           log_debug ("%s:%s: Already deleted mail for %p",
1786                    SRCNAME, __func__, it->first);
1787           continue;
1788         }
1789
1790       if (!it->second->isCryptoMail ())
1791         {
1792           continue;
1793         }
1794       if (closeInspector_o (it->second) || close (it->second))
1795         {
1796           log_error ("Failed to close mail: %p ", it->first);
1797           /* Should not happen */
1798           if (isValidPtr (it->second) && it->second->revert_o ())
1799             {
1800               err++;
1801             }
1802         }
1803     }
1804   TRETURN err;
1805 }
1806 int
1807 Mail::revertAllMails_o ()
1808 {
1809   TSTART;
1810   int err = 0;
1811   std::map<LPDISPATCH, Mail *>::iterator it;
1812   gpgol_lock (&mail_map_lock);
1813   auto mail_map_copy = s_mail_map;
1814   gpgol_unlock (&mail_map_lock);
1815   for (it = mail_map_copy.begin(); it != mail_map_copy.end(); ++it)
1816     {
1817       if (it->second->revert_o ())
1818         {
1819           log_error ("Failed to revert mail: %p ", it->first);
1820           err++;
1821           continue;
1822         }
1823
1824       it->second->setNeedsSave (true);
1825       if (!invoke_oom_method (it->first, "Save", NULL))
1826         {
1827           log_error ("Failed to save reverted mail: %p ", it->second);
1828           err++;
1829           continue;
1830         }
1831     }
1832   TRETURN err;
1833 }
1834
1835 int
1836 Mail::wipeAllMails_o ()
1837 {
1838   TSTART;
1839   int err = 0;
1840   std::map<LPDISPATCH, Mail *>::iterator it;
1841   gpgol_lock (&mail_map_lock);
1842   auto mail_map_copy = s_mail_map;
1843   gpgol_unlock (&mail_map_lock);
1844   for (it = mail_map_copy.begin(); it != mail_map_copy.end(); ++it)
1845     {
1846       if (it->second->wipe_o ())
1847         {
1848           log_error ("Failed to wipe mail: %p ", it->first);
1849           err++;
1850         }
1851     }
1852   TRETURN err;
1853 }
1854
1855 int
1856 Mail::revert_o ()
1857 {
1858   TSTART;
1859   int err = 0;
1860   if (!m_processed)
1861     {
1862       TRETURN 0;
1863     }
1864
1865   m_disable_att_remove_warning = true;
1866
1867   err = gpgol_mailitem_revert (m_mailitem);
1868   if (err == -1)
1869     {
1870       log_error ("%s:%s: Message revert failed falling back to wipe.",
1871                  SRCNAME, __func__);
1872       TRETURN wipe_o ();
1873     }
1874   /* We need to reprocess the mail next time around. */
1875   m_processed = false;
1876   m_needs_wipe = false;
1877   m_disable_att_remove_warning = false;
1878   TRETURN 0;
1879 }
1880
1881 bool
1882 Mail::isSMIME_m ()
1883 {
1884   TSTART;
1885   msgtype_t msgtype;
1886   LPMESSAGE message;
1887
1888   if (m_is_smime_checked)
1889     {
1890       TRETURN m_is_smime;
1891     }
1892
1893   message = get_oom_message (m_mailitem);
1894
1895   if (!message)
1896     {
1897       log_error ("%s:%s: No message?",
1898                  SRCNAME, __func__);
1899       TRETURN false;
1900     }
1901
1902   msgtype = mapi_get_message_type (message);
1903   m_is_smime = msgtype == MSGTYPE_GPGOL_OPAQUE_ENCRYPTED ||
1904                msgtype == MSGTYPE_GPGOL_OPAQUE_SIGNED;
1905
1906   /* Check if it is an smime mail. Multipart signed can
1907      also be true. */
1908   if (!m_is_smime && msgtype == MSGTYPE_GPGOL_MULTIPART_SIGNED)
1909     {
1910       char *proto;
1911       char *ct = mapi_get_message_content_type (message, &proto, NULL);
1912       if (ct && proto)
1913         {
1914           m_is_smime = (!strcmp (proto, "application/pkcs7-signature") ||
1915                         !strcmp (proto, "application/x-pkcs7-signature"));
1916         }
1917       else
1918         {
1919           log_error ("%s:%s: No protocol in multipart / signed mail.",
1920                      SRCNAME, __func__);
1921         }
1922       xfree (proto);
1923       xfree (ct);
1924     }
1925   gpgol_release (message);
1926   m_is_smime_checked  = true;
1927   TRETURN m_is_smime;
1928 }
1929
1930 static std::string
1931 get_string_o (LPDISPATCH item, const char *str)
1932 {
1933   TSTART;
1934   char *buf = get_oom_string (item, str);
1935   if (!buf)
1936     {
1937       TRETURN std::string();
1938     }
1939   std::string ret = buf;
1940   xfree (buf);
1941   TRETURN ret;
1942 }
1943
1944 std::string
1945 Mail::getSubject_o () const
1946 {
1947   TSTART;
1948   TRETURN get_string_o (m_mailitem, "Subject");
1949 }
1950
1951 std::string
1952 Mail::getBody_o () const
1953 {
1954   TSTART;
1955   TRETURN get_string_o (m_mailitem, "Body");
1956 }
1957
1958 std::vector<std::string>
1959 Mail::getRecipients_o () const
1960 {
1961   TSTART;
1962   LPDISPATCH recipients = get_oom_object (m_mailitem, "Recipients");
1963   if (!recipients)
1964     {
1965       TRACEPOINT;
1966       std::vector<std::string>();
1967     }
1968   bool err = false;
1969   auto ret = get_oom_recipients (recipients, &err);
1970   gpgol_release (recipients);
1971
1972   if (err)
1973     {
1974       log_debug ("%s:%s: Failed to resolve recipients at this time.",
1975                  SRCNAME, __func__);
1976
1977     }
1978
1979   TRETURN ret;
1980 }
1981
1982 int
1983 Mail::closeInspector_o (Mail *mail)
1984 {
1985   TSTART;
1986   LPDISPATCH inspector = get_oom_object (mail->item(), "GetInspector");
1987   HRESULT hr;
1988   DISPID dispid;
1989   if (!inspector)
1990     {
1991       log_debug ("%s:%s: No inspector.",
1992                  SRCNAME, __func__);
1993       TRETURN -1;
1994     }
1995
1996   dispid = lookup_oom_dispid (inspector, "Close");
1997   if (dispid != DISPID_UNKNOWN)
1998     {
1999       VARIANT aVariant[1];
2000       DISPPARAMS dispparams;
2001
2002       dispparams.rgvarg = aVariant;
2003       dispparams.rgvarg[0].vt = VT_INT;
2004       dispparams.rgvarg[0].intVal = 1;
2005       dispparams.cArgs = 1;
2006       dispparams.cNamedArgs = 0;
2007
2008       hr = inspector->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
2009                               DISPATCH_METHOD, &dispparams,
2010                               NULL, NULL, NULL);
2011       if (hr != S_OK)
2012         {
2013           log_debug ("%s:%s: Failed to close inspector: %#lx",
2014                      SRCNAME, __func__, hr);
2015           gpgol_release (inspector);
2016           TRETURN -1;
2017         }
2018     }
2019   gpgol_release (inspector);
2020   TRETURN 0;
2021 }
2022
2023 /* static */
2024 int
2025 Mail::close (Mail *mail)
2026 {
2027   TSTART;
2028   VARIANT aVariant[1];
2029   DISPPARAMS dispparams;
2030
2031   dispparams.rgvarg = aVariant;
2032   dispparams.rgvarg[0].vt = VT_INT;
2033   dispparams.rgvarg[0].intVal = 1;
2034   dispparams.cArgs = 1;
2035   dispparams.cNamedArgs = 0;
2036
2037   log_oom ("%s:%s: Invoking close for: %p",
2038                  SRCNAME, __func__, mail->item());
2039   mail->setCloseTriggered (true);
2040   int rc = invoke_oom_method_with_parms (mail->item(), "Close",
2041                                        NULL, &dispparams);
2042
2043   if (!rc)
2044     {
2045       log_debug ("%s:%s: Close successful. Next write may pass.",
2046                  SRCNAME, __func__);
2047       mail->setPassWrite (true);
2048     }
2049   log_oom ("%s:%s: returned from close",
2050                  SRCNAME, __func__);
2051   TRETURN rc;
2052 }
2053
2054 void
2055 Mail::setCloseTriggered (bool value)
2056 {
2057   TSTART;
2058   m_close_triggered = value;
2059   TRETURN;
2060 }
2061
2062 bool
2063 Mail::getCloseTriggered () const
2064 {
2065   TSTART;
2066   TRETURN m_close_triggered;
2067 }
2068
2069 static const UserID
2070 get_uid_for_sender (const Key &k, const char *sender)
2071 {
2072   TSTART;
2073   UserID ret;
2074
2075   if (!sender)
2076     {
2077       TRETURN ret;
2078     }
2079
2080   if (!k.numUserIDs())
2081     {
2082       log_debug ("%s:%s: Key without uids",
2083                  SRCNAME, __func__);
2084       TRETURN ret;
2085     }
2086
2087   for (const auto uid: k.userIDs())
2088     {
2089       if (!uid.email() || !*(uid.email()))
2090         {
2091           /* This happens for S/MIME a lot */
2092           log_debug ("%s:%s: skipping uid without email.",
2093                      SRCNAME, __func__);
2094           continue;
2095         }
2096       auto normalized_uid = uid.addrSpec();
2097       auto normalized_sender = UserID::addrSpecFromString(sender);
2098
2099       if (normalized_sender.empty() || normalized_uid.empty())
2100         {
2101           log_error ("%s:%s: normalizing '%s' or '%s' failed.",
2102                      SRCNAME, __func__, anonstr (uid.email()),
2103                      anonstr (sender));
2104           continue;
2105         }
2106       if (normalized_sender == normalized_uid)
2107         {
2108           ret = uid;
2109         }
2110     }
2111   TRETURN ret;
2112 }
2113
2114 void
2115 Mail::updateSigstate ()
2116 {
2117   TSTART;
2118   std::string sender = getSender ();
2119
2120   if (sender.empty())
2121     {
2122       log_error ("%s:%s:%i", SRCNAME, __func__, __LINE__);
2123       TRETURN;
2124     }
2125
2126   if (m_verify_result.isNull())
2127     {
2128       log_debug ("%s:%s: No verify result.",
2129                  SRCNAME, __func__);
2130       TRETURN;
2131     }
2132
2133   if (m_verify_result.error())
2134     {
2135       log_debug ("%s:%s: verify error.",
2136                  SRCNAME, __func__);
2137       TRETURN;
2138     }
2139
2140   for (const auto sig: m_verify_result.signatures())
2141     {
2142       m_is_signed = true;
2143       const auto key = KeyCache::instance ()->getByFpr (sig.fingerprint(),
2144                                                         true);
2145       m_uid = get_uid_for_sender (key, sender.c_str());
2146
2147       if (m_uid.isNull() && !m_sent_on_behalf.empty ())
2148         {
2149           m_uid = get_uid_for_sender (key, m_sent_on_behalf.c_str ());
2150           if (!m_uid.isNull())
2151             {
2152               log_debug ("%s:%s: Using sent on behalf '%s' instead of '%s'",
2153                          SRCNAME, __func__, anonstr (m_sent_on_behalf.c_str()),
2154                          anonstr (sender.c_str ()));
2155             }
2156         }
2157       /* Sigsum valid or green is somehow not set in this case.
2158        * Which is strange as AFAIK this worked in the past. */
2159       if ((sig.summary() & Signature::Summary::Valid) &&
2160           m_uid.origin() == GpgME::Key::OriginWKD &&
2161           (sig.validity() == Signature::Validity::Unknown ||
2162            sig.validity() == Signature::Validity::Marginal))
2163         {
2164           // WKD is a shortcut to Level 2 trust.
2165           log_debug ("%s:%s: Unknown or marginal from WKD -> Level 2",
2166                      SRCNAME, __func__);
2167          }
2168       else if (m_uid.isNull() || (sig.validity() != Signature::Validity::Marginal &&
2169           sig.validity() != Signature::Validity::Full &&
2170           sig.validity() != Signature::Validity::Ultimate))
2171         {
2172           /* For our category we only care about trusted sigs. And
2173           the UID needs to match.*/
2174           continue;
2175         }
2176       else if (sig.validity() == Signature::Validity::Marginal)
2177         {
2178           const auto tofu = m_uid.tofuInfo();
2179           if (!tofu.isNull() &&
2180               (tofu.validity() != TofuInfo::Validity::BasicHistory &&
2181                tofu.validity() != TofuInfo::Validity::LargeHistory))
2182             {
2183               /* Marginal is only good enough without tofu.
2184                  Otherwise we wait for basic trust. */
2185               log_debug ("%s:%s: Discarding marginal signature."
2186                          "With too little history.",
2187                          SRCNAME, __func__);
2188               continue;
2189             }
2190         }
2191       log_debug ("%s:%s: Classified sender as verified uid validity: %i origin: %i",
2192                  SRCNAME, __func__, m_uid.validity(), m_uid.origin());
2193       m_sig = sig;
2194       m_is_valid = true;
2195       TRETURN;
2196     }
2197
2198   log_debug ("%s:%s: No signature with enough trust. Using first",
2199              SRCNAME, __func__);
2200   m_sig = m_verify_result.signature(0);
2201   TRETURN;
2202 }
2203
2204 bool
2205 Mail::isValidSig () const
2206 {
2207   TSTART;
2208   TRETURN m_is_valid;
2209 }
2210
2211 void
2212 Mail::removeCategories_o ()
2213 {
2214   TSTART;
2215   if (!m_store_id.empty () && !m_verify_category.empty ())
2216     {
2217       log_oom ("%s:%s: Unreffing verify category",
2218                        SRCNAME, __func__);
2219       CategoryManager::instance ()->removeCategory (this,
2220                                                     m_verify_category);
2221     }
2222   if (!m_store_id.empty () && !m_decrypt_result.isNull())
2223     {
2224       log_oom ("%s:%s: Unreffing dec category",
2225                        SRCNAME, __func__);
2226       CategoryManager::instance ()->removeCategory (this,
2227                                 CategoryManager::getEncMailCategory ());
2228     }
2229   if (m_is_junk)
2230     {
2231       log_oom ("%s:%s: Unreffing junk category",
2232                        SRCNAME, __func__);
2233       CategoryManager::instance ()->removeCategory (this,
2234                                 CategoryManager::getJunkMailCategory ());
2235     }
2236   TRETURN;
2237 }
2238
2239 /* Now for some tasty hack: Outlook sometimes does
2240    not show the new categories properly but instead
2241    does some weird scrollbar thing. This can be
2242    avoided by resizing the message a bit. But somehow
2243    this only needs to be done once.
2244
2245    Weird isn't it? But as this workaround worked let's
2246    do it programatically. Fun. Wan't some tomato sauce
2247    with this hack? */
2248 static void
2249 resize_active_window ()
2250 {
2251   TSTART;
2252   HWND wnd = get_active_hwnd ();
2253   static std::vector<HWND> resized_windows;
2254   if(std::find(resized_windows.begin(), resized_windows.end(), wnd) != resized_windows.end()) {
2255       /* We only need to do this once per window. XXX But sometimes we also
2256          need to do this once per view of the explorer. So for now this might
2257          break but we reduce the flicker. A better solution would be to find
2258          the current view and track that. */
2259       TRETURN;
2260   }
2261
2262   if (!wnd)
2263     {
2264       TRACEPOINT;
2265       TRETURN;
2266     }
2267   RECT oldpos;
2268   if (!GetWindowRect (wnd, &oldpos))
2269     {
2270       TRACEPOINT;
2271       TRETURN;
2272     }
2273
2274   if (!SetWindowPos (wnd, nullptr,
2275                      (int)oldpos.left,
2276                      (int)oldpos.top,
2277                      /* Anything smaller then 19 was ignored when the window was
2278                       * maximized on Windows 10 at least with a 1980*1024
2279                       * resolution. So I assume it's at least 1 percent.
2280                       * This is all hackish and ugly but should work for 90%...
2281                       * hopefully.
2282                       */
2283                      (int)oldpos.right - oldpos.left - 20,
2284                      (int)oldpos.bottom - oldpos.top, 0))
2285     {
2286       TRACEPOINT;
2287       TRETURN;
2288     }
2289
2290   if (!SetWindowPos (wnd, nullptr,
2291                      (int)oldpos.left,
2292                      (int)oldpos.top,
2293                      (int)oldpos.right - oldpos.left,
2294                      (int)oldpos.bottom - oldpos.top, 0))
2295     {
2296       TRACEPOINT;
2297       TRETURN;
2298     }
2299   resized_windows.push_back(wnd);
2300   TRETURN;
2301 }
2302
2303 #if 0
2304 static std::string
2305 pretty_id (const char *keyId)
2306 {
2307   /* Three spaces, four quads and a NULL */
2308   char buf[20];
2309   buf[19] = '\0';
2310   if (!keyId)
2311     {
2312       return std::string ("null");
2313     }
2314   size_t len = strlen (keyId);
2315   if (!len)
2316     {
2317       return std::string ("empty");
2318     }
2319   if (len < 16)
2320     {
2321       return std::string (_("Invalid Key"));
2322     }
2323   const char *p = keyId + (len - 16);
2324   int j = 0;
2325   for (size_t i = 0; i < 16; i++)
2326     {
2327       if (i && i % 4 == 0)
2328         {
2329           buf[j++] = ' ';
2330         }
2331       buf[j++] = *(p + i);
2332     }
2333   return std::string (buf);
2334 }
2335 #endif
2336
2337 void
2338 Mail::updateCategories_o ()
2339 {
2340   TSTART;
2341
2342   auto mngr = CategoryManager::instance ();
2343   if (isValidSig ())
2344     {
2345       char *buf;
2346       /* Resolve to the primary fingerprint */
2347 #if 0
2348       const auto sigKey = KeyCache::instance ()->getByFpr (m_sig.fingerprint (),
2349                                                            true);
2350       const char *sigFpr;
2351       if (sigKey.isNull())
2352         {
2353           sigFpr = m_sig.fingerprint ();
2354         }
2355       else
2356         {
2357           sigFpr = sigKey.primaryFingerprint ();
2358         }
2359 #endif
2360       /* If m_uid addrSpec would not return a result we would never
2361        * have gotten the UID. */
2362       int lvl = get_signature_level ();
2363       gpgrt_asprintf (&buf, "GpgOL: %s %i %s '%s'", _("Level"), lvl,
2364                       _("trust in"),
2365                       m_uid.addrSpec ().c_str ());
2366       memdbg_alloc (buf);
2367
2368       int color = 0;
2369       if (lvl == 2)
2370         {
2371           color = 7; /* Olive */
2372         }
2373       if (lvl == 3)
2374         {
2375           color = 5; /* Green */
2376         }
2377       if (lvl == 4)
2378         {
2379           color = 20; /* Dark Green */
2380         }
2381       m_store_id = mngr->addCategoryToMail (this, buf, color);
2382       m_verify_category = buf;
2383       xfree (buf);
2384     }
2385   else
2386     {
2387       remove_category (m_mailitem, "GpgOL: ", false);
2388     }
2389
2390   if (!m_decrypt_result.isNull())
2391     {
2392       const auto id = mngr->addCategoryToMail (this,
2393                                  CategoryManager::getEncMailCategory (),
2394                                  8 /* Blue */);
2395       if (m_store_id.empty())
2396         {
2397           m_store_id = id;
2398         }
2399       if (m_store_id != id)
2400         {
2401           log_error ("%s:%s unexpected store mismatch "
2402                      "between '%s' and dec cat '%s'",
2403                      SRCNAME, __func__, m_store_id.c_str(), id.c_str());
2404         }
2405     }
2406   else
2407     {
2408       /* As a small safeguard against fakes we remove our
2409          categories */
2410       remove_category (m_mailitem,
2411                        CategoryManager::getEncMailCategory ().c_str (),
2412                        true);
2413     }
2414
2415   resize_active_window();
2416
2417   TRETURN;
2418 }
2419
2420 bool
2421 Mail::isSigned () const
2422 {
2423   TSTART;
2424   TRETURN m_verify_result.numSignatures() > 0;
2425 }
2426
2427 bool
2428 Mail::isEncrypted () const
2429 {
2430   TSTART;
2431   TRETURN !m_decrypt_result.isNull();
2432 }
2433
2434 int
2435 Mail::setUUID_o ()
2436 {
2437   TSTART;
2438   char *uuid;
2439   if (!m_uuid.empty())
2440     {
2441       /* This codepath is reached by decrypt again after a
2442          close with discard changes. The close discarded
2443          the uuid on the OOM object so we have to set
2444          it again. */
2445       log_debug ("%s:%s: Resetting uuid for %p to %s",
2446                  SRCNAME, __func__, this,
2447                  m_uuid.c_str());
2448       uuid = get_unique_id (m_mailitem, 1, m_uuid.c_str());
2449     }
2450   else
2451     {
2452       uuid = get_unique_id (m_mailitem, 1, nullptr);
2453       log_debug ("%s:%s: uuid for %p set to %s",
2454                  SRCNAME, __func__, this, uuid);
2455     }
2456
2457   if (!uuid)
2458     {
2459       log_debug ("%s:%s: Failed to get/set uuid for %p",
2460                  SRCNAME, __func__, m_mailitem);
2461       TRETURN -1;
2462     }
2463   if (m_uuid.empty())
2464     {
2465       m_uuid = uuid;
2466       Mail *other = getMailForUUID (uuid);
2467       if (other)
2468         {
2469           /* According to documentation this should not
2470              happen as this means that multiple ItemLoad
2471              events occured for the same mailobject without
2472              unload / destruction of the mail.
2473
2474              But it happens. If you invalidate the UI
2475              in the selection change event Outlook loads a
2476              new mailobject for the mail. Might happen in
2477              other surprising cases. We replace in that
2478              case as experiments have shown that the last
2479              mailobject is the one that is visible.
2480
2481              Still troubling state so we log this as an error.
2482              */
2483           log_error ("%s:%s: There is another mail for %p "
2484                      "with uuid: %s replacing it.",
2485                      SRCNAME, __func__, m_mailitem, uuid);
2486           delete other;
2487         }
2488
2489       gpgol_lock (&uid_map_lock);
2490       s_uid_map.insert (std::pair<std::string, Mail *> (m_uuid, this));
2491       gpgol_unlock (&uid_map_lock);
2492       log_debug ("%s:%s: uuid for %p is now %s",
2493                  SRCNAME, __func__, this,
2494                  m_uuid.c_str());
2495     }
2496   xfree (uuid);
2497   TRETURN 0;
2498 }
2499
2500 /* TRETURNs 2 if the userid is ultimately trusted.
2501
2502    TRETURNs 1 if the userid is fully trusted but has
2503    a signature by a key for which we have a secret
2504    and which is ultimately trusted. (Direct trust)
2505
2506    0 otherwise */
2507 static int
2508 level_4_check (const UserID &uid)
2509 {
2510   TSTART;
2511   if (uid.isNull())
2512     {
2513       TRETURN 0;
2514     }
2515   if (uid.validity () == UserID::Validity::Ultimate)
2516     {
2517       TRETURN 2;
2518     }
2519   if (uid.validity () == UserID::Validity::Full)
2520     {
2521       const auto ultimate_keys = KeyCache::instance()->getUltimateKeys ();
2522       for (const auto sig: uid.signatures ())
2523         {
2524           const char *sigID = sig.signerKeyID ();
2525           if (sig.isNull() || !sigID)
2526             {
2527               /* should not happen */
2528               TRACEPOINT;
2529               continue;
2530             }
2531           /* Direct trust information is not available
2532              through gnupg so we cached the keys with ultimate
2533              trust during parsing and now see if we find a direct
2534              trust path.*/
2535           for (const auto secKey: ultimate_keys)
2536             {
2537               /* Check that the Key id of the key matches */
2538               const char *secKeyID = secKey.keyID ();
2539               if (!secKeyID || strcmp (secKeyID, sigID))
2540                 {
2541                   continue;
2542                 }
2543               /* Check that the userID of the signature is the ultimately
2544                  trusted one. */
2545               const char *sig_uid_str = sig.signerUserID();
2546               if (!sig_uid_str)
2547                 {
2548                   /* should not happen */
2549                   TRACEPOINT;
2550                   continue;
2551                 }
2552               for (const auto signer_uid: secKey.userIDs ())
2553                 {
2554                   if (signer_uid.validity() != UserID::Validity::Ultimate)
2555                     {
2556                       TRACEPOINT;
2557                       continue;
2558                     }
2559                   const char *signer_uid_str = signer_uid.id ();
2560                   if (!sig_uid_str)
2561                     {
2562                       /* should not happen */
2563                       TRACEPOINT;
2564                       continue;
2565                     }
2566                   if (!strcmp(sig_uid_str, signer_uid_str))
2567                     {
2568                       /* We have a match */
2569                       log_debug ("%s:%s: classified %s as ultimate because "
2570                                  "it was signed by uid %s of key %s",
2571                                  SRCNAME, __func__, anonstr (signer_uid_str),
2572                                  anonstr (sig_uid_str),
2573                                  anonstr (secKeyID));
2574                       TRETURN 1;
2575                     }
2576                 }
2577             }
2578         }
2579     }
2580   TRETURN 0;
2581 }
2582
2583 std::string
2584 Mail::getCryptoSummary () const
2585 {
2586   TSTART;
2587   const int level = get_signature_level ();
2588
2589   bool enc = isEncrypted ();
2590   if (level == 4 && enc)
2591     {
2592       TRETURN _("Security Level 4");
2593     }
2594   if (level == 4)
2595     {
2596       TRETURN _("Trust Level 4");
2597     }
2598   if (level == 3 && enc)
2599     {
2600       TRETURN _("Security Level 3");
2601     }
2602   if (level == 3)
2603     {
2604       TRETURN _("Trust Level 3");
2605     }
2606   if (level == 2 && enc)
2607     {
2608       TRETURN _("Security Level 2");
2609     }
2610   if (level == 2)
2611     {
2612       TRETURN _("Trust Level 2");
2613     }
2614   if (enc)
2615     {
2616       TRETURN _("Encrypted");
2617     }
2618   if (isSigned ())
2619     {
2620       /* Even if it is signed, if it is not validly
2621          signed it's still completly insecure as anyone
2622          could have signed this. So we avoid the label
2623          "signed" here as this word already implies some
2624          security. */
2625       TRETURN _("Insecure");
2626     }
2627   TRETURN _("Insecure");
2628 }
2629
2630 std::string
2631 Mail::getCryptoOneLine () const
2632 {
2633   TSTART;
2634   bool sig = isSigned ();
2635   bool enc = isEncrypted ();
2636   if (sig || enc)
2637     {
2638       if (sig && enc)
2639         {
2640           TRETURN _("Signed and encrypted message");
2641         }
2642       else if (sig)
2643         {
2644           TRETURN _("Signed message");
2645         }
2646       else if (enc)
2647         {
2648           TRETURN _("Encrypted message");
2649         }
2650     }
2651   TRETURN _("Insecure message");
2652 }
2653
2654 std::string
2655 Mail::getCryptoDetails_o ()
2656 {
2657   TSTART;
2658   std::string message;
2659
2660   /* No signature with keys but error */
2661   if (!isEncrypted () && !isSigned () && m_verify_result.error())
2662     {
2663       message = _("You cannot be sure who sent, "
2664                   "modified and read the message in transit.");
2665       message += "\n\n";
2666       message += _("The message was signed but the verification failed with:");
2667       message += "\n";
2668       message += m_verify_result.error().asString();
2669       TRETURN message;
2670     }
2671   /* No crypo, what are we doing here? */
2672   if (!isEncrypted () && !isSigned ())
2673     {
2674       TRETURN _("You cannot be sure who sent, "
2675                "modified and read the message in transit.");
2676     }
2677   /* Handle encrypt only */
2678   if (isEncrypted () && !isSigned ())
2679     {
2680       if (in_de_vs_mode ())
2681        {
2682          if (m_sig.isDeVs())
2683            {
2684              message += _("The encryption was VS-NfD-compliant.");
2685            }
2686          else
2687            {
2688              message += _("The encryption was not VS-NfD-compliant.");
2689            }
2690         }
2691       message += "\n\n";
2692       message += _("You cannot be sure who sent the message because "
2693                    "it is not signed.");
2694       TRETURN message;
2695     }
2696
2697   bool keyFound = true;
2698   const auto sigKey = KeyCache::instance ()->getByFpr (m_sig.fingerprint (),
2699                                                        true);
2700   bool isOpenPGP = sigKey.isNull() ? !isSMIME_m () :
2701                    sigKey.protocol() == Protocol::OpenPGP;
2702   char *buf;
2703   bool hasConflict = false;
2704   int level = get_signature_level ();
2705
2706   log_debug ("%s:%s: Formatting sig. Validity: %x Summary: %x Level: %i",
2707              SRCNAME, __func__, m_sig.validity(), m_sig.summary(),
2708              level);
2709
2710   if (level == 4)
2711     {
2712       /* level 4 check for direct trust */
2713       int four_check = level_4_check (m_uid);
2714
2715       if (four_check == 2 && sigKey.hasSecret ())
2716         {
2717           message = _("You signed this message.");
2718         }
2719       else if (four_check == 1)
2720         {
2721           message = _("The senders identity was certified by yourself.");
2722         }
2723       else if (four_check == 2)
2724         {
2725           message = _("The sender is allowed to certify identities for you.");
2726         }
2727       else
2728         {
2729           log_error ("%s:%s:%i BUG: Invalid sigstate.",
2730                      SRCNAME, __func__, __LINE__);
2731           TRETURN message;
2732         }
2733     }
2734   else if (level == 3 && isOpenPGP)
2735     {
2736       /* Level three is only reachable through web of trust and no
2737          direct signature. */
2738       message = _("The senders identity was certified by several trusted people.");
2739     }
2740   else if (level == 3 && !isOpenPGP)
2741     {
2742       /* Level three is the only level for trusted S/MIME keys. */
2743       gpgrt_asprintf (&buf, _("The senders identity is certified by the trusted issuer:\n'%s'\n"),
2744                       sigKey.issuerName());
2745       memdbg_alloc (buf);
2746       message = buf;
2747       xfree (buf);
2748     }
2749   else if (level == 2 && m_uid.origin () == GpgME::Key::OriginWKD)
2750     {
2751       message = _("The mail provider of the recipient served this key.");
2752     }
2753   else if (level == 2 && m_uid.tofuInfo ().isNull ())
2754     {
2755       /* Marginal trust through pgp only */
2756       message = _("Some trusted people "
2757                   "have certified the senders identity.");
2758     }
2759   else if (level == 2)
2760     {
2761       unsigned long first_contact = std::max (m_uid.tofuInfo().signFirst(),
2762                                               m_uid.tofuInfo().encrFirst());
2763       char *time = format_date_from_gpgme (first_contact);
2764       /* i18n note signcount is always pulral because with signcount 1 we
2765        * would not be in this branch. */
2766       gpgrt_asprintf (&buf, _("The senders address is trusted, because "
2767                               "you have established a communication history "
2768                               "with this address starting on %s.\n"
2769                               "You encrypted %i and verified %i messages since."),
2770                               time, m_uid.tofuInfo().encrCount(),
2771                               m_uid.tofuInfo().signCount ());
2772       memdbg_alloc (buf);
2773       xfree (time);
2774       message = buf;
2775       xfree (buf);
2776     }
2777   else if (level == 1)
2778     {
2779       /* This could be marginal trust through pgp, or tofu with little
2780          history. */
2781       if (m_uid.tofuInfo ().signCount() == 1)
2782         {
2783           message += _("The senders signature was verified for the first time.");
2784         }
2785       else if (m_uid.tofuInfo ().validity() == TofuInfo::Validity::LittleHistory)
2786         {
2787           unsigned long first_contact = std::max (m_uid.tofuInfo().signFirst(),
2788                                                   m_uid.tofuInfo().encrFirst());
2789           char *time = format_date_from_gpgme (first_contact);
2790           gpgrt_asprintf (&buf, _("The senders address is not trustworthy yet because "
2791                                   "you only verified %i messages and encrypted %i messages to "
2792                                   "it since %s."),
2793                                   m_uid.tofuInfo().signCount (),
2794                                   m_uid.tofuInfo().encrCount (), time);
2795           memdbg_alloc (buf);
2796           xfree (time);
2797           message = buf;
2798           xfree (buf);
2799         }
2800     }
2801   else
2802     {
2803       /* Now we are in level 0, this could be a technical problem, no key
2804          or just unkown. */
2805       message = isEncrypted () ? _("But the sender address is not trustworthy because:") :
2806                                   _("The sender address is not trustworthy because:");
2807       message += "\n";
2808       keyFound = !(m_sig.summary() & Signature::Summary::KeyMissing);
2809
2810       bool general_problem = true;
2811       /* First the general stuff. */
2812       if (m_sig.summary() & Signature::Summary::Red)
2813         {
2814           message += _("The signature is invalid: \n");
2815         }
2816       else if (m_sig.summary() & Signature::Summary::SysError ||
2817                m_verify_result.numSignatures() < 1)
2818         {
2819           message += _("There was an error verifying the signature.\n");
2820           const auto err = m_sig.status ();
2821           if (err)
2822             {
2823               message += err.asString () + std::string ("\n");
2824             }
2825         }
2826       else if (m_sig.summary() & Signature::Summary::SigExpired)
2827         {
2828           message += _("The signature is expired.\n");
2829         }
2830       else
2831         {
2832           message += isOpenPGP ? _("The used key") : _("The used certificate");
2833           message += " ";
2834           general_problem = false;
2835         }
2836
2837       /* Now the key problems */
2838       if ((m_sig.summary() & Signature::Summary::KeyMissing))
2839         {
2840           message += _("is not available.");
2841         }
2842       else if ((m_sig.summary() & Signature::Summary::KeyRevoked))
2843         {
2844           message += _("is revoked.");
2845         }
2846       else if ((m_sig.summary() & Signature::Summary::KeyExpired))
2847         {
2848           message += _("is expired.");
2849         }
2850       else if ((m_sig.summary() & Signature::Summary::BadPolicy))
2851         {
2852           message += _("is not meant for signing.");
2853         }
2854       else if ((m_sig.summary() & Signature::Summary::CrlMissing))
2855         {
2856           message += _("could not be checked for revocation.");
2857         }
2858       else if ((m_sig.summary() & Signature::Summary::CrlTooOld))
2859         {
2860           message += _("could not be checked for revocation.");
2861         }
2862       else if ((m_sig.summary() & Signature::Summary::TofuConflict) ||
2863                m_uid.tofuInfo().validity() == TofuInfo::Conflict)
2864         {
2865           message += _("is not the same as the key that was used "
2866                        "for this address in the past.");
2867           hasConflict = true;
2868         }
2869       else if (m_uid.isNull())
2870         {
2871           gpgrt_asprintf (&buf, _("does not claim the address: \"%s\"."),
2872                           getSender_o ().c_str());
2873           memdbg_alloc (buf);
2874           message += buf;
2875           xfree (buf);
2876         }
2877       else if (((m_sig.validity() & Signature::Validity::Undefined) ||
2878                (m_sig.validity() & Signature::Validity::Unknown) ||
2879                (m_sig.summary() == Signature::Summary::None) ||
2880                (m_sig.validity() == 0))&& !general_problem)
2881         {
2882            /* Bit of a catch all for weird results. */
2883           if (isOpenPGP)
2884             {
2885               message += _("is not certified by any trustworthy key.");
2886             }
2887           else
2888             {
2889               message += _("is not certified by a trustworthy Certificate Authority or the Certificate Authority is unknown.");
2890             }
2891         }
2892       else if (m_uid.isRevoked())
2893         {
2894           message += _("The sender marked this address as revoked.");
2895         }
2896       else if ((m_sig.validity() & Signature::Validity::Never))
2897         {
2898           message += _("is marked as not trustworthy.");
2899         }
2900     }
2901    message += "\n\n";
2902    if (in_de_vs_mode ())
2903      {
2904        if (isSigned ())
2905          {
2906            if (m_sig.isDeVs ())
2907              {
2908                message += _("The signature is VS-NfD-compliant.");
2909              }
2910            else
2911              {
2912                message += _("The signature is not VS-NfD-compliant.");
2913              }
2914            message += "\n";
2915          }
2916        if (isEncrypted ())
2917          {
2918            if (m_decrypt_result.isDeVs ())
2919              {
2920                message += _("The encryption is VS-NfD-compliant.");
2921              }
2922            else
2923              {
2924                message += _("The encryption is not VS-NfD-compliant.");
2925              }
2926            message += "\n\n";
2927          }
2928        else
2929          {
2930            message += "\n";
2931          }
2932      }
2933    if (hasConflict)
2934     {
2935       message += _("Click here to change the key used for this address.");
2936     }
2937   else if (keyFound)
2938     {
2939       message +=  isOpenPGP ? _("Click here for details about the key.") :
2940                               _("Click here for details about the certificate.");
2941     }
2942   else
2943     {
2944       message +=  isOpenPGP ? _("Click here to search the key on the configured keyserver.") :
2945                               _("Click here to search the certificate on the configured X509 keyserver.");
2946     }
2947   TRETURN message;
2948 }
2949
2950 int
2951 Mail::get_signature_level () const
2952 {
2953   TSTART;
2954   if (!m_is_signed)
2955     {
2956       TRETURN 0;
2957     }
2958
2959   if (m_uid.isNull ())
2960     {
2961       /* No m_uid matches our sender. */
2962       TRETURN 0;
2963     }
2964
2965   if (m_is_valid && (m_uid.validity () == UserID::Validity::Ultimate ||
2966       (m_uid.validity () == UserID::Validity::Full &&
2967       level_4_check (m_uid))) && (!in_de_vs_mode () || m_sig.isDeVs()))
2968     {
2969       TRETURN 4;
2970     }
2971   if (m_is_valid && m_uid.validity () == UserID::Validity::Full &&
2972       (!in_de_vs_mode () || m_sig.isDeVs()))
2973     {
2974       TRETURN 3;
2975     }
2976   if (m_is_valid)
2977     {
2978       TRETURN 2;
2979     }
2980   if (m_sig.validity() == Signature::Validity::Marginal)
2981     {
2982       TRETURN 1;
2983     }
2984   if (m_sig.summary() & Signature::Summary::TofuConflict ||
2985       m_uid.tofuInfo().validity() == TofuInfo::Conflict)
2986     {
2987       TRETURN 0;
2988     }
2989   TRETURN 0;
2990 }
2991
2992 int
2993 Mail::getCryptoIconID () const
2994 {
2995   TSTART;
2996   int level = get_signature_level ();
2997   int offset = isEncrypted () ? ENCRYPT_ICON_OFFSET : 0;
2998   TRETURN IDI_LEVEL_0 + level + offset;
2999 }
3000
3001 const char*
3002 Mail::getSigFpr () const
3003 {
3004   TSTART;
3005   if (!m_is_signed || m_sig.isNull())
3006     {
3007       TRETURN nullptr;
3008     }
3009   TRETURN m_sig.fingerprint();
3010 }
3011
3012 /** Try to locate the keys for all recipients */
3013 void
3014 Mail::locateKeys_o ()
3015 {
3016   TSTART;
3017   if (m_locate_in_progress)
3018     {
3019       /** XXX
3020         The strangest thing seems to happen here:
3021         In get_recipients the lookup for "AddressEntry" on
3022         an unresolved address might cause network traffic.
3023
3024         So Outlook somehow "detaches" this call and keeps
3025         processing window messages while the call is running.
3026
3027         So our do_delayed_locate might trigger a second locate.
3028         If we access the OOM in this call while we access the
3029         same object in the blocked "detached" call we crash.
3030         (T3931)
3031
3032         After the window message is handled outlook retunrs
3033         in the original lookup.
3034
3035         A better fix here might be a non recursive lock
3036         of the OOM. But I expect that if we lock the handling
3037         of the Windowmessage we might deadlock.
3038         */
3039       log_debug ("%s:%s: Locate for %p already in progress.",
3040                  SRCNAME, __func__, this);
3041       TRETURN;
3042     }
3043   m_locate_in_progress = true;
3044
3045   Addressbook::check_o (this);
3046
3047   if (opt.autoresolve)
3048     {
3049       // First update oom data to have recipients and sender updated.
3050       updateOOMData_o ();
3051       KeyCache::instance()->startLocateSecret (getSender_o ().c_str (), this);
3052       KeyCache::instance()->startLocate (getSender_o ().c_str (), this);
3053       KeyCache::instance()->startLocate (getCachedRecipients (), this);
3054     }
3055
3056   autosecureCheck ();
3057
3058   m_locate_in_progress = false;
3059   TRETURN;
3060 }
3061
3062 bool
3063 Mail::isHTMLAlternative () const
3064 {
3065   TSTART;
3066   TRETURN m_is_html_alternative;
3067 }
3068
3069 char *
3070 Mail::takeCachedHTMLBody ()
3071 {
3072   TSTART;
3073   char *ret = m_cached_html_body;
3074   m_cached_html_body = nullptr;
3075   TRETURN ret;
3076 }
3077
3078 char *
3079 Mail::takeCachedPlainBody ()
3080 {
3081   TSTART;
3082   char *ret = m_cached_plain_body;
3083   m_cached_plain_body = nullptr;
3084   TRETURN ret;
3085 }
3086
3087 int
3088 Mail::getCryptoFlags () const
3089 {
3090   TSTART;
3091   TRETURN m_crypto_flags;
3092 }
3093
3094 void
3095 Mail::setNeedsEncrypt (bool value)
3096 {
3097   TSTART;
3098   m_needs_encrypt = value;
3099   TRETURN;
3100 }
3101
3102 bool
3103 Mail::getNeedsEncrypt () const
3104 {
3105   TSTART;
3106   TRETURN m_needs_encrypt;
3107 }
3108
3109 std::vector<std::string>
3110 Mail::getCachedRecipients ()
3111 {
3112   TSTART;
3113   TRETURN m_cached_recipients;
3114 }
3115
3116 void
3117 Mail::appendToInlineBody (const std::string &data)
3118 {
3119   TSTART;
3120   m_inline_body += data;
3121   TRETURN;
3122 }
3123
3124 int
3125 Mail::inlineBodyToBody_o ()
3126 {
3127   TSTART;
3128   if (!m_crypter)
3129     {
3130       log_error ("%s:%s: No crypter.",
3131                  SRCNAME, __func__);
3132       TRETURN -1;
3133     }
3134
3135   const auto body = m_crypter->get_inline_data ();
3136   if (body.empty())
3137     {
3138       TRETURN 0;
3139     }
3140
3141   /* For inline we always work with UTF-8 */
3142   put_oom_int (m_mailitem, "InternetCodepage", 65001);
3143
3144   int ret = put_oom_string (m_mailitem, "Body",
3145                             body.c_str ());
3146   TRETURN ret;
3147 }
3148
3149 void
3150 Mail::updateCryptMAPI_m ()
3151 {
3152   TSTART;
3153   log_debug ("%s:%s: Update crypt mapi",
3154              SRCNAME, __func__);
3155   if (m_crypt_state != NeedsUpdateInMAPI)
3156     {
3157       log_debug ("%s:%s: invalid state %i",
3158                  SRCNAME, __func__, m_crypt_state);
3159       TRETURN;
3160     }
3161   if (!m_crypter)
3162     {
3163       if (!m_mime_data.empty())
3164         {
3165           log_debug ("%s:%s: Have override mime data creating dummy crypter",
3166                      SRCNAME, __func__);
3167           m_crypter = std::shared_ptr <CryptController> (new CryptController (this, false,
3168                                                                               false,
3169                                                                               GpgME::UnknownProtocol));
3170         }
3171       else
3172         {
3173           log_error ("%s:%s: No crypter.",
3174                      SRCNAME, __func__);
3175           m_crypt_state = NoCryptMail;
3176           TRETURN;
3177         }
3178     }
3179
3180   if (m_crypter->update_mail_mapi ())
3181     {
3182       log_error ("%s:%s: Failed to update MAPI after crypt",
3183                  SRCNAME, __func__);
3184       m_crypt_state = NoCryptMail;
3185     }
3186   else
3187     {
3188       m_crypt_state = WantsSendMIME;
3189     }
3190
3191   /** If sync we need the crypter in update_crypt_oom */
3192   if (!isAsyncCryptDisabled ())
3193     {
3194       // We don't need the crypter anymore.
3195       resetCrypter ();
3196     }
3197   TRETURN;
3198 }
3199
3200 /** Checks in OOM if the body is either
3201   empty or contains the -----BEGIN tag.
3202   pair.first -> true if body starts with -----BEGIN
3203   pair.second -> true if body is empty. */
3204 static std::pair<bool, bool>
3205 has_crypt_or_empty_body_oom (Mail *mail)
3206 {
3207   TSTART;
3208   auto body = mail->getBody_o ();
3209   std::pair<bool, bool> ret;
3210   ret.first = false;
3211   ret.second = false;
3212   ltrim (body);
3213   if (body.size() > 10 && !strncmp (body.c_str(), "-----BEGIN", 10))
3214     {
3215       ret.first = true;
3216       TRETURN ret;
3217     }
3218   if (!body.size())
3219     {
3220       ret.second = true;
3221     }
3222   else
3223     {
3224       log_data ("%s:%s: Body found in %p : \"%s\"",
3225                        SRCNAME, __func__, mail, body.c_str ());
3226     }
3227   TRETURN ret;
3228 }
3229
3230 void
3231 Mail::updateCryptOOM_o ()
3232 {
3233   TSTART;
3234   log_debug ("%s:%s: Update crypt oom for %p",
3235              SRCNAME, __func__, this);
3236   if (m_crypt_state != NeedsUpdateInOOM)
3237     {
3238       log_debug ("%s:%s: invalid state %i",
3239                  SRCNAME, __func__, m_crypt_state);
3240       resetCrypter ();
3241       TRETURN;
3242     }
3243
3244   if (getDoPGPInline ())
3245     {
3246       if (inlineBodyToBody_o ())
3247         {
3248           log_error ("%s:%s: Inline body to body failed %p.",
3249                      SRCNAME, __func__, this);
3250           gpgol_bug (get_active_hwnd(), ERR_INLINE_BODY_TO_BODY);
3251           m_crypt_state = NoCryptMail;
3252           TRETURN;
3253         }
3254     }
3255
3256   if (m_crypter->get_protocol () == GpgME::CMS && m_crypter->is_encrypter ())
3257     {
3258       /* We put the PIDNameContentType headers here for exchange
3259          because this is the only way we found to inject the
3260          smime-type. */
3261       if (put_pa_string (m_mailitem,
3262                          PR_PIDNameContentType_DASL,
3263                          "application/pkcs7-mime;smime-type=\"enveloped-data\";name=smime.p7m"))
3264         {
3265           log_debug ("%s:%s: Failed to put PIDNameContentType for %p.",
3266                      SRCNAME, __func__, this);
3267         }
3268     }
3269
3270   /** When doing async update_crypt_mapi follows and needs
3271     the crypter. */
3272   if (isAsyncCryptDisabled ())
3273     {
3274       resetCrypter ();
3275     }
3276
3277   const auto pair = has_crypt_or_empty_body_oom (this);
3278   if (pair.first)
3279     {
3280       log_debug ("%s:%s: Looks like inline body. You can pass %p.",
3281                  SRCNAME, __func__, this);
3282       m_crypt_state = WantsSendInline;
3283       TRETURN;
3284     }
3285
3286   // We are in MIME land. Wipe the body.
3287   if (wipe_o (true))
3288     {
3289       log_debug ("%s:%s: Cancel send for %p.",
3290                  SRCNAME, __func__, this);
3291       wchar_t *title = utf8_to_wchar (_("GpgOL: Encryption not possible!"));
3292       wchar_t *msg = utf8_to_wchar (_(
3293                                       "Outlook returned an error when trying to send the encrypted mail.\n\n"
3294                                       "Please restart Outlook and try again.\n\n"
3295                                       "If it still fails consider using an encrypted attachment or\n"
3296                                       "switching to PGP/Inline in GpgOL's options."));
3297       MessageBoxW (get_active_hwnd(), msg, title,
3298                    MB_ICONERROR | MB_OK);
3299       xfree (msg);
3300       xfree (title);
3301       m_crypt_state = NoCryptMail;
3302       TRETURN;
3303     }
3304   m_crypt_state = NeedsSecondAfterWrite;
3305   TRETURN;
3306 }
3307
3308 void
3309 Mail::setWindowEnabled_o (bool value)
3310 {
3311   TSTART;
3312   if (!value)
3313     {
3314       m_window = get_active_hwnd ();
3315     }
3316   log_debug ("%s:%s: enable window %p %i",
3317              SRCNAME, __func__, m_window, value);
3318
3319   EnableWindow (m_window, value ? TRUE : FALSE);
3320   TRETURN;
3321 }
3322
3323 bool
3324 Mail::check_inline_response ()
3325 {
3326   TSTART;
3327   /* Async sending is known to cause instabilities. So we keep
3328      a hidden option to disable it. */
3329   if (opt.sync_enc)
3330     {
3331       m_async_crypt_disabled = true;
3332       TRETURN m_async_crypt_disabled;
3333     }
3334
3335   m_async_crypt_disabled = false;
3336
3337   LPDISPATCH attachments = get_oom_object (m_mailitem, "Attachments");
3338   if (attachments)
3339     {
3340       /* This is horrible. But. For some kinds of attachments (we
3341          got reports about Office attachments the write in the
3342          send event triggered by our crypto done code fails with
3343          an exception. There does not appear to be a detectable
3344          pattern when this happens.
3345          As we can't be sure and do not know for which attachments
3346          this really happens we do not use async crypt for any
3347          mails with attachments. :-/
3348          Better be save (not crash) instead of nice (async).
3349
3350          TODO: Figure this out.
3351
3352          The log goes like this. We pass the send event. That triggers
3353          a write, which we pass. And then that fails. So it looks like
3354          moving to Outbox fails. Because we can save as much as we
3355          like before that.
3356
3357          Using the IMessage::SubmitMessage MAPI interface works, but
3358          as it is unstable in our current implementation we do not
3359          want to use it.
3360
3361          mailitem-events.cpp:Invoke: Passing send event for mime-encrypted message 12B7C6E0.
3362          application-events.cpp:Invoke: Unhandled Event: f002
3363          mailitem-events.cpp:Invoke: Write : 0ED4D058
3364          mailitem-events.cpp:Invoke: Passing write event.
3365          oomhelp.cpp:invoke_oom_method_with_parms_type: Method 'Send' invokation failed: 0x80020009
3366          oomhelp.cpp:dump_excepinfo: Exception:
3367          wCode: 0x1000
3368          wReserved: 0x0
3369          source: Microsoft Outlook
3370          desc: The operation failed.  The messaging interfaces have returned an unknown error. If the problem persists, restart Outlook.
3371          help: null
3372          helpCtx: 0x0
3373          deferredFill: 00000000
3374          scode: 0x80040119
3375       */
3376
3377       int count = get_oom_int (attachments, "Count");
3378       gpgol_release (attachments);
3379
3380       if (count)
3381         {
3382           m_async_crypt_disabled = true;
3383           log_debug ("%s:%s: Detected attachments. "
3384                      "Disabling async crypt due to T4131.",
3385                      SRCNAME, __func__);
3386           TRETURN m_async_crypt_disabled;
3387         }
3388    }
3389
3390   LPDISPATCH app = GpgolAddin::get_instance ()->get_application ();
3391   if (!app)
3392     {
3393       TRACEPOINT;
3394       TRETURN false;
3395     }
3396
3397   LPDISPATCH explorer = get_oom_object (app, "ActiveExplorer");
3398
3399   if (!explorer)
3400     {
3401       TRACEPOINT;
3402       TRETURN false;
3403     }
3404
3405   LPDISPATCH inlineResponse = get_oom_object (explorer, "ActiveInlineResponse");
3406   gpgol_release (explorer);
3407
3408   if (!inlineResponse)
3409     {
3410       TRETURN false;
3411     }
3412
3413   // We have inline response
3414   // Check if we are it. It's a bit naive but meh. Worst case
3415   // is that we think inline response too often and do sync
3416   // crypt where we could do async crypt.
3417   char * inlineSubject = get_oom_string (inlineResponse, "Subject");
3418   gpgol_release (inlineResponse);
3419
3420   const auto subject = getSubject_o ();
3421   if (inlineResponse && !subject.empty() && !strcmp (subject.c_str (), inlineSubject))
3422     {
3423       log_debug ("%s:%s: Detected inline response for '%p'",
3424                  SRCNAME, __func__, this);
3425       m_async_crypt_disabled = true;
3426     }
3427   xfree (inlineSubject);
3428
3429   TRETURN m_async_crypt_disabled;
3430 }
3431
3432 // static
3433 Mail *
3434 Mail::getLastMail ()
3435 {
3436   TSTART;
3437   if (!s_last_mail || !isValidPtr (s_last_mail))
3438     {
3439       s_last_mail = nullptr;
3440     }
3441   TRETURN s_last_mail;
3442 }
3443
3444 // static
3445 void
3446 Mail::clearLastMail ()
3447 {
3448   TSTART;
3449   s_last_mail = nullptr;
3450   TRETURN;
3451 }
3452
3453 // static
3454 void
3455 Mail::locateAllCryptoRecipients_o ()
3456 {
3457   TSTART;
3458   gpgol_lock (&mail_map_lock);
3459   std::map<LPDISPATCH, Mail *>::iterator it;
3460   auto mail_map_copy = s_mail_map;
3461   gpgol_unlock (&mail_map_lock);
3462   for (it = mail_map_copy.begin(); it != mail_map_copy.end(); ++it)
3463     {
3464       if (it->second->needs_crypto_m ())
3465         {
3466           it->second->locateKeys_o ();
3467         }
3468     }
3469   TRETURN;
3470 }
3471
3472 int
3473 Mail::removeAllAttachments_o ()
3474 {
3475   TSTART;
3476   int ret = 0;
3477   LPDISPATCH attachments = get_oom_object (m_mailitem, "Attachments");
3478   if (!attachments)
3479     {
3480       TRACEPOINT;
3481       TRETURN 0;
3482     }
3483   int count = get_oom_int (attachments, "Count");
3484   LPDISPATCH to_delete[count];
3485
3486   /* Populate the array so that we don't get in an index mess */
3487   for (int i = 1; i <= count; i++)
3488     {
3489       auto item_str = std::string("Item(") + std::to_string (i) + ")";
3490       to_delete[i-1] = get_oom_object (attachments, item_str.c_str());
3491     }
3492   gpgol_release (attachments);
3493
3494   /* Now delete all attachments */
3495   for (int i = 0; i < count; i++)
3496     {
3497       LPDISPATCH attachment = to_delete[i];
3498
3499       if (!attachment)
3500         {
3501           log_error ("%s:%s: No such attachment %i",
3502                      SRCNAME, __func__, i);
3503           ret = -1;
3504         }
3505
3506       /* Delete the attachments that are marked to delete */
3507       if (invoke_oom_method (attachment, "Delete", NULL))
3508         {
3509           log_error ("%s:%s: Deleting attachment %i",
3510                      SRCNAME, __func__, i);
3511           ret = -1;
3512         }
3513       gpgol_release (attachment);
3514     }
3515   TRETURN ret;
3516 }
3517
3518 int
3519 Mail::removeOurAttachments_o ()
3520 {
3521   TSTART;
3522   LPDISPATCH attachments = get_oom_object (m_mailitem, "Attachments");
3523   if (!attachments)
3524     {
3525       TRACEPOINT;
3526       TRETURN 0;
3527     }
3528   int count = get_oom_int (attachments, "Count");
3529   LPDISPATCH to_delete[count];
3530   int del_cnt = 0;
3531   for (int i = 1; i <= count; i++)
3532     {
3533       auto item_str = std::string("Item(") + std::to_string (i) + ")";
3534       LPDISPATCH attachment = get_oom_object (attachments, item_str.c_str());
3535       if (!attachment)
3536         {
3537           TRACEPOINT;
3538           continue;
3539         }
3540
3541       attachtype_t att_type;
3542       if (get_pa_int (attachment, GPGOL_ATTACHTYPE_DASL, (int*) &att_type))
3543         {
3544           /* Not our attachment. */
3545           gpgol_release (attachment);
3546           continue;
3547         }
3548
3549       if (att_type == ATTACHTYPE_PGPBODY || att_type == ATTACHTYPE_MOSS ||
3550           att_type == ATTACHTYPE_MOSSTEMPL)
3551         {
3552           /* One of ours to delete. */
3553           to_delete[del_cnt++] = attachment;
3554           /* Dont' release yet */
3555           continue;
3556         }
3557       gpgol_release (attachment);
3558     }
3559   gpgol_release (attachments);
3560
3561   int ret = 0;
3562
3563   for (int i = 0; i < del_cnt; i++)
3564     {
3565       LPDISPATCH attachment = to_delete[i];
3566
3567       /* Delete the attachments that are marked to delete */
3568       if (invoke_oom_method (attachment, "Delete", NULL))
3569         {
3570           log_error ("%s:%s: Error: deleting attachment %i",
3571                      SRCNAME, __func__, i);
3572           ret = -1;
3573         }
3574       gpgol_release (attachment);
3575     }
3576   TRETURN ret;
3577 }
3578
3579 /* We are very verbose because if we fail it might mean
3580    that we have leaked plaintext -> critical. */
3581 bool
3582 Mail::hasCryptedOrEmptyBody_o ()
3583 {
3584   TSTART;
3585   const auto pair = has_crypt_or_empty_body_oom (this);
3586
3587   if (pair.first /* encrypted marker */)
3588     {
3589       log_debug ("%s:%s: Crypt Marker detected in OOM body. Return true %p.",
3590                  SRCNAME, __func__, this);
3591       TRETURN true;
3592     }
3593
3594   if (!pair.second)
3595     {
3596       log_debug ("%s:%s: Unexpected content detected. Return false %p.",
3597                  SRCNAME, __func__, this);
3598       TRETURN false;
3599     }
3600
3601   // Pair second == true (is empty) can happen on OOM error.
3602   LPMESSAGE message = get_oom_base_message (m_mailitem);
3603   if (!message && pair.second)
3604     {
3605       if (message)
3606         {
3607           gpgol_release (message);
3608         }
3609       TRETURN true;
3610     }
3611
3612   size_t r_nbytes = 0;
3613   char *mapi_body = mapi_get_body (message, &r_nbytes);
3614   gpgol_release (message);
3615
3616   if (!mapi_body || !r_nbytes)
3617     {
3618       // Body or bytes are null. we are empty.
3619       xfree (mapi_body);
3620       log_debug ("%s:%s: MAPI error or empty message. Return true. %p.",
3621                  SRCNAME, __func__, this);
3622       TRETURN true;
3623     }
3624   if (r_nbytes > 10 && !strncmp (mapi_body, "-----BEGIN", 10))
3625     {
3626       // Body is crypt.
3627       log_debug ("%s:%s: MAPI Crypt marker detected. Return true. %p.",
3628                  SRCNAME, __func__, this);
3629       xfree (mapi_body);
3630       TRETURN true;
3631     }
3632
3633   xfree (mapi_body);
3634
3635   log_debug ("%s:%s: Found mapi body. Return false. %p.",
3636              SRCNAME, __func__, this);
3637
3638   TRETURN false;
3639 }
3640
3641 std::string
3642 Mail::getVerificationResultDump ()
3643 {
3644   TSTART;
3645   std::stringstream ss;
3646   ss << m_verify_result;
3647   TRETURN ss.str();
3648 }
3649
3650 void
3651 Mail::setBlockStatus_m ()
3652 {
3653   TSTART;
3654   SPropValue prop;
3655
3656   LPMESSAGE message = get_oom_base_message (m_mailitem);
3657
3658   prop.ulPropTag = PR_BLOCK_STATUS;
3659   prop.Value.l = 1;
3660   HRESULT hr = message->SetProps (1, &prop, NULL);
3661
3662   if (hr)
3663     {
3664       log_error ("%s:%s: can't set block value: hr=%#lx\n",
3665                  SRCNAME, __func__, hr);
3666     }
3667
3668   gpgol_release (message);
3669   TRETURN;
3670 }
3671
3672 void
3673 Mail::setBlockHTML (bool value)
3674 {
3675   TSTART;
3676   m_block_html = value;
3677   TRETURN;
3678 }
3679
3680 void
3681 Mail::incrementLocateCount ()
3682 {
3683   TSTART;
3684   m_locate_count++;
3685   TRETURN;
3686 }
3687
3688 void
3689 Mail::decrementLocateCount ()
3690 {
3691   TSTART;
3692   m_locate_count--;
3693
3694   if (m_locate_count < 0)
3695     {
3696       log_error ("%s:%s: locate count mismatch.",
3697                  SRCNAME, __func__);
3698       m_locate_count = 0;
3699     }
3700   if (!m_locate_count)
3701     {
3702       autosecureCheck ();
3703     }
3704   TRETURN;