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