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