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