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