395c75aa3b13901f14390ce290a36f4a94bc104c
[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 Intevation GmbH
5  *
6  * This file is part of GpgOL.
7  *
8  * GpgOL is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * GpgOL is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include "config.h"
23 #include "dialogs.h"
24 #include "common.h"
25 #include "mail.h"
26 #include "eventsinks.h"
27 #include "attachment.h"
28 #include "mapihelp.h"
29 #include "message.h"
30 #include "revert.h"
31 #include "gpgoladdin.h"
32 #include "mymapitags.h"
33 #include "parsecontroller.h"
34 #include "gpgolstr.h"
35 #include "windowmessages.h"
36 #include "mlang-charset.h"
37
38 #include <gpgme++/tofuinfo.h>
39 #include <gpgme++/verificationresult.h>
40 #include <gpgme++/decryptionresult.h>
41 #include <gpgme++/key.h>
42 #include <gpgme++/context.h>
43 #include <gpgme++/keylistresult.h>
44 #include <gpg-error.h>
45
46 #include <map>
47 #include <set>
48 #include <vector>
49 #include <memory>
50
51
52 #undef _
53 #define _(a) utf8_gettext (a)
54
55 using namespace GpgME;
56
57 static std::map<LPDISPATCH, Mail*> g_mail_map;
58 static std::map<std::string, Mail*> g_uid_map;
59 static std::set<std::string> uids_searched;
60
61 #define COPYBUFSIZE (8 * 1024)
62
63 /* Our own basic trust level for tofu.
64    GnuPG's can't be trusted. See comment
65    in get_valid_sig why.*/
66 #define GPGOL_BASIC_TOFU_TRUST 10
67
68 Mail::Mail (LPDISPATCH mailitem) :
69     m_mailitem(mailitem),
70     m_processed(false),
71     m_needs_wipe(false),
72     m_needs_save(false),
73     m_crypt_successful(false),
74     m_is_smime(false),
75     m_is_smime_checked(false),
76     m_is_signed(false),
77     m_is_valid(false),
78     m_close_triggered(false),
79     m_is_html_alternative(false),
80     m_needs_encrypt(false),
81     m_moss_position(0),
82     m_crypto_flags(0),
83     m_type(MSGTYPE_UNKNOWN)
84 {
85   if (get_mail_for_item (mailitem))
86     {
87       log_error ("Mail object for item: %p already exists. Bug.",
88                  mailitem);
89       return;
90     }
91
92   m_event_sink = install_MailItemEvents_sink (mailitem);
93   if (!m_event_sink)
94     {
95       /* Should not happen but in that case we don't add us to the list
96          and just release the Mail item. */
97       log_error ("%s:%s: Failed to install MailItemEvents sink.",
98                  SRCNAME, __func__);
99       gpgol_release(mailitem);
100       return;
101     }
102   g_mail_map.insert (std::pair<LPDISPATCH, Mail *> (mailitem, this));
103 }
104
105 GPGRT_LOCK_DEFINE(dtor_lock);
106
107 Mail::~Mail()
108 {
109   /* This should fix a race condition where the mail is
110      deleted before the parser is accessed in the decrypt
111      thread. The shared_ptr of the parser then ensures
112      that the parser is alive even if the mail is deleted
113      while parsing. */
114   gpgrt_lock_lock (&dtor_lock);
115   std::map<LPDISPATCH, Mail *>::iterator it;
116
117   detach_MailItemEvents_sink (m_event_sink);
118   gpgol_release(m_event_sink);
119
120   it = g_mail_map.find(m_mailitem);
121   if (it != g_mail_map.end())
122     {
123       g_mail_map.erase (it);
124     }
125
126   if (!m_uuid.empty())
127     {
128       auto it2 = g_uid_map.find(m_uuid);
129       if (it2 != g_uid_map.end())
130         {
131           g_uid_map.erase (it2);
132         }
133     }
134
135   gpgol_release(m_mailitem);
136   if (!m_uuid.empty())
137     {
138       log_oom_extra ("%s:%s: destroyed: %p uuid: %s",
139                      SRCNAME, __func__, this, m_uuid.c_str());
140     }
141   else
142     {
143       log_oom_extra ("%s:%s: non crypto mail: %p destroyed",
144                      SRCNAME, __func__, this);
145     }
146   gpgrt_lock_unlock (&dtor_lock);
147 }
148
149 Mail *
150 Mail::get_mail_for_item (LPDISPATCH mailitem)
151 {
152   if (!mailitem)
153     {
154       return NULL;
155     }
156   std::map<LPDISPATCH, Mail *>::iterator it;
157   it = g_mail_map.find(mailitem);
158   if (it == g_mail_map.end())
159     {
160       return NULL;
161     }
162   return it->second;
163 }
164
165 Mail *
166 Mail::get_mail_for_uuid (const char *uuid)
167 {
168   if (!uuid)
169     {
170       return NULL;
171     }
172   auto it = g_uid_map.find(std::string(uuid));
173   if (it == g_uid_map.end())
174     {
175       return NULL;
176     }
177   return it->second;
178 }
179
180 bool
181 Mail::is_valid_ptr (const Mail *mail)
182 {
183   auto it = g_mail_map.begin();
184   while (it != g_mail_map.end())
185     {
186       if (it->second == mail)
187         return true;
188       ++it;
189     }
190   return false;
191 }
192
193 int
194 Mail::pre_process_message ()
195 {
196   LPMESSAGE message = get_oom_base_message (m_mailitem);
197   if (!message)
198     {
199       log_error ("%s:%s: Failed to get base message.",
200                  SRCNAME, __func__);
201       return 0;
202     }
203   log_oom_extra ("%s:%s: GetBaseMessage OK.",
204                  SRCNAME, __func__);
205   /* Change the message class here. It is important that
206      we change the message class in the before read event
207      regardless if it is already set to one of GpgOL's message
208      classes. Changing the message class (even if we set it
209      to the same value again that it already has) causes
210      Outlook to reconsider what it "knows" about a message
211      and reread data from the underlying base message. */
212   mapi_change_message_class (message, 1);
213   /* TODO: Unify this so mapi_change_message_class returns
214      a useful value already. */
215   m_type = mapi_get_message_type (message);
216
217   /* Create moss attachments here so that they are properly
218      hidden when the item is read into the model. */
219   m_moss_position = mapi_mark_or_create_moss_attach (message, m_type);
220   if (!m_moss_position)
221     {
222       log_error ("%s:%s: Failed to find moss attachment.",
223                  SRCNAME, __func__);
224       m_type = MSGTYPE_UNKNOWN;
225     }
226
227   gpgol_release (message);
228   return 0;
229 }
230
231 static LPDISPATCH
232 get_attachment (LPDISPATCH mailitem, int pos)
233 {
234   LPDISPATCH attachment;
235   LPDISPATCH attachments = get_oom_object (mailitem, "Attachments");
236   if (!attachments)
237     {
238       log_debug ("%s:%s: Failed to get attachments.",
239                  SRCNAME, __func__);
240       return NULL;
241     }
242
243   std::string item_str;
244   int count = get_oom_int (attachments, "Count");
245   if (pos > 0)
246     {
247       item_str = std::string("Item(") + std::to_string(pos) + ")";
248     }
249   else
250     {
251       item_str = std::string("Item(") + std::to_string(count) + ")";
252     }
253   if (count < 1)
254     {
255       log_debug ("%s:%s: Invalid attachment count: %i.",
256                  SRCNAME, __func__, count);
257       gpgol_release (attachments);
258       return NULL;
259     }
260   attachment = get_oom_object (attachments, item_str.c_str());
261   gpgol_release (attachments);
262
263   return attachment;
264 }
265
266 /** Get the cipherstream of the mailitem. */
267 static LPSTREAM
268 get_attachment_stream (LPDISPATCH mailitem, int pos)
269 {
270   if (!pos)
271     {
272       log_debug ("%s:%s: Called with zero pos.",
273                  SRCNAME, __func__);
274       return NULL;
275     }
276   LPDISPATCH attachment = get_attachment (mailitem, pos);
277   LPATTACH mapi_attachment = NULL;
278   LPSTREAM stream = NULL;
279
280   mapi_attachment = (LPATTACH) get_oom_iunknown (attachment,
281                                                  "MapiObject");
282   gpgol_release (attachment);
283   if (!mapi_attachment)
284     {
285       log_debug ("%s:%s: Failed to get MapiObject of attachment: %p",
286                  SRCNAME, __func__, attachment);
287       return NULL;
288     }
289   if (FAILED (mapi_attachment->OpenProperty (PR_ATTACH_DATA_BIN, &IID_IStream,
290                                              0, MAPI_MODIFY, (LPUNKNOWN*) &stream)))
291     {
292       log_debug ("%s:%s: Failed to open stream for mapi_attachment: %p",
293                  SRCNAME, __func__, mapi_attachment);
294       gpgol_release (mapi_attachment);
295     }
296   return stream;
297 }
298
299 #if 0
300
301 This should work. But Outlook says no. See the comment in set_pa_variant
302 about this. I left the code here as an example how to work with
303 safearrays and how this probably should work.
304
305 static int
306 copy_data_property(LPDISPATCH target, std::shared_ptr<Attachment> attach)
307 {
308   VARIANT var;
309   VariantInit (&var);
310
311   /* Get the size */
312   off_t size = attach->get_data ().seek (0, SEEK_END);
313   attach->get_data ().seek (0, SEEK_SET);
314
315   if (!size)
316     {
317       TRACEPOINT;
318       return 1;
319     }
320
321   if (!get_pa_variant (target, PR_ATTACH_DATA_BIN_DASL, &var))
322     {
323       log_debug("Have variant. type: %x", var.vt);
324     }
325   else
326     {
327       log_debug("failed to get variant.");
328     }
329
330   /* Set the type to an array of unsigned chars (OLE SAFEARRAY) */
331   var.vt = VT_ARRAY | VT_UI1;
332
333   /* Set up the bounds structure */
334   SAFEARRAYBOUND rgsabound[1];
335   rgsabound[0].cElements = static_cast<unsigned long> (size);
336   rgsabound[0].lLbound = 0;
337
338   /* Create an OLE SAFEARRAY */
339   var.parray = SafeArrayCreate (VT_UI1, 1, rgsabound);
340   if (var.parray == NULL)
341     {
342       TRACEPOINT;
343       VariantClear(&var);
344       return 1;
345     }
346
347   void *buffer = NULL;
348   /* Get a safe pointer to the array */
349   if (SafeArrayAccessData(var.parray, &buffer) != S_OK)
350     {
351       TRACEPOINT;
352       VariantClear(&var);
353       return 1;
354     }
355
356   /* Copy data to it */
357   size_t nread = attach->get_data ().read (buffer, static_cast<size_t> (size));
358
359   if (nread != static_cast<size_t> (size))
360     {
361       TRACEPOINT;
362       VariantClear(&var);
363       return 1;
364     }
365
366   /*/ Unlock the variant data */
367   if (SafeArrayUnaccessData(var.parray) != S_OK)
368     {
369       TRACEPOINT;
370       VariantClear(&var);
371       return 1;
372     }
373
374   if (set_pa_variant (target, PR_ATTACH_DATA_BIN_DASL, &var))
375     {
376       TRACEPOINT;
377       VariantClear(&var);
378       return 1;
379     }
380
381   VariantClear(&var);
382   return 0;
383 }
384 #endif
385
386 static int
387 copy_attachment_to_file (std::shared_ptr<Attachment> att, HANDLE hFile)
388 {
389   char copybuf[COPYBUFSIZE];
390   size_t nread;
391
392   /* Security considerations: Writing the data to a temporary
393      file is necessary as neither MAPI manipulation works in the
394      read event to transmit the data nor Property Accessor
395      works (see above). From a security standpoint there is a
396      short time where the temporary files are on disk. Tempdir
397      should be protected so that only the user can read it. Thus
398      we have a local attack that could also take the data out
399      of Outlook. FILE_SHARE_READ is necessary so that outlook
400      can read the file.
401
402      A bigger concern is that the file is manipulated
403      by another software to fake the signature state. So
404      we keep the write exlusive to us.
405
406      We delete the file before closing the write file handle.
407   */
408
409   /* Make sure we start at the beginning */
410   att->get_data ().seek (0, SEEK_SET);
411   while ((nread = att->get_data ().read (copybuf, COPYBUFSIZE)))
412     {
413       DWORD nwritten;
414       if (!WriteFile (hFile, copybuf, nread, &nwritten, NULL))
415         {
416           log_error ("%s:%s: Failed to write in tmp attachment.",
417                      SRCNAME, __func__);
418           return 1;
419         }
420       if (nread != nwritten)
421         {
422           log_error ("%s:%s: Write truncated.",
423                      SRCNAME, __func__);
424           return 1;
425         }
426     }
427   return 0;
428 }
429
430 /** Sets some meta data on the last attachment atted. The meta
431   data is taken from the attachment object. */
432 static int
433 fixup_last_attachment (LPDISPATCH mail, std::shared_ptr<Attachment> attachment)
434 {
435   /* Currently we only set content id */
436   if (attachment->get_content_id ().empty())
437     {
438       log_debug ("%s:%s: Content id not found.",
439                  SRCNAME, __func__);
440       return 0;
441     }
442
443   LPDISPATCH attach = get_attachment (mail, -1);
444   if (!attach)
445     {
446       log_error ("%s:%s: No attachment.",
447                  SRCNAME, __func__);
448       return 1;
449     }
450   int ret = put_pa_string (attach,
451                            PR_ATTACH_CONTENT_ID_DASL,
452                            attachment->get_content_id ().c_str());
453   gpgol_release (attach);
454   return ret;
455 }
456
457 /** Helper to update the attachments of a mail object in oom.
458   does not modify the underlying mapi structure. */
459 static int
460 add_attachments(LPDISPATCH mail,
461                 std::vector<std::shared_ptr<Attachment> > attachments)
462 {
463   int err = 0;
464   for (auto att: attachments)
465     {
466       if (att->get_display_name().empty())
467         {
468           log_error ("%s:%s: Ignoring attachment without display name.",
469                      SRCNAME, __func__);
470           continue;
471         }
472       wchar_t* wchar_name = utf8_to_wchar (att->get_display_name().c_str());
473       HANDLE hFile;
474       wchar_t* wchar_file = get_tmp_outfile (GpgOLStr (att->get_display_name().c_str()),
475                                              &hFile);
476       if (copy_attachment_to_file (att, hFile))
477         {
478           log_error ("%s:%s: Failed to copy attachment %s to temp file",
479                      SRCNAME, __func__, att->get_display_name().c_str());
480           err = 1;
481         }
482       if (add_oom_attachment (mail, wchar_file, wchar_name))
483         {
484           log_error ("%s:%s: Failed to add attachment: %s",
485                      SRCNAME, __func__, att->get_display_name().c_str());
486           err = 1;
487         }
488       if (!DeleteFileW (wchar_file))
489         {
490           log_error ("%s:%s: Failed to delete tmp attachment for: %s",
491                      SRCNAME, __func__, att->get_display_name().c_str());
492           err = 1;
493         }
494       xfree (wchar_file);
495       xfree (wchar_name);
496
497       err = fixup_last_attachment (mail, att);
498     }
499   return err;
500 }
501
502 GPGRT_LOCK_DEFINE(parser_lock);
503
504 static DWORD WINAPI
505 do_parsing (LPVOID arg)
506 {
507   gpgrt_lock_lock (&dtor_lock);
508   /* We lock with mail dtors so we can be sure the mail->parser
509      call is valid. */
510   Mail *mail = (Mail *)arg;
511   if (!Mail::is_valid_ptr (mail))
512     {
513       log_debug ("%s:%s: canceling parsing for: %p already deleted",
514                  SRCNAME, __func__, arg);
515       gpgrt_lock_unlock (&dtor_lock);
516       return 0;
517     }
518   /* This takes a shared ptr of parser. So the parser is
519      still valid when the mail is deleted. */
520   auto parser = mail->parser();
521   gpgrt_lock_unlock (&dtor_lock);
522
523   gpgrt_lock_lock (&parser_lock);
524   /* We lock the parser here to avoid too many
525      decryption attempts if there are
526      multiple mailobjects which might have already
527      been deleted (e.g. by quick switches of the mailview.)
528      Let's rather be a bit slower.
529      */
530   log_debug ("%s:%s: preparing the parser for: %p",
531              SRCNAME, __func__, arg);
532
533   if (!parser)
534     {
535       log_error ("%s:%s: no parser found for mail: %p",
536                  SRCNAME, __func__, arg);
537       gpgrt_lock_unlock (&parser_lock);
538       return -1;
539     }
540   parser->parse();
541   do_in_ui_thread (PARSING_DONE, arg);
542   gpgrt_lock_unlock (&parser_lock);
543   return 0;
544 }
545
546 bool
547 Mail::is_crypto_mail() const
548 {
549   if (m_type == MSGTYPE_UNKNOWN || m_type == MSGTYPE_GPGOL ||
550       m_type == MSGTYPE_SMIME)
551     {
552       /* Not a message for us. */
553       return false;
554     }
555   return true;
556 }
557
558 int
559 Mail::decrypt_verify()
560 {
561   if (!is_crypto_mail())
562     {
563       return 0;
564     }
565   if (m_needs_wipe)
566     {
567       log_error ("%s:%s: Decrypt verify called for msg that needs wipe: %p",
568                  SRCNAME, __func__, m_mailitem);
569       return 1;
570     }
571   set_uuid ();
572   m_processed = true;
573   /* Insert placeholder */
574   char *placeholder_buf;
575   if (gpgrt_asprintf (&placeholder_buf, opt.prefer_html ? decrypt_template_html :
576                       decrypt_template,
577                       is_smime() ? "S/MIME" : "OpenPGP",
578                       _("Encrypted message"),
579                       _("Please wait while the message is being decrypted / verified...")) == -1)
580     {
581       log_error ("%s:%s: Failed to format placeholder.",
582                  SRCNAME, __func__);
583       return 1;
584     }
585
586   if (opt.prefer_html)
587     {
588       if (put_oom_string (m_mailitem, "HTMLBody", placeholder_buf))
589         {
590           log_error ("%s:%s: Failed to modify html body of item.",
591                      SRCNAME, __func__);
592         }
593     }
594   else
595     {
596       if (put_oom_string (m_mailitem, "Body", placeholder_buf))
597         {
598           log_error ("%s:%s: Failed to modify body of item.",
599                      SRCNAME, __func__);
600         }
601     }
602   xfree (placeholder_buf);
603
604   /* Do the actual parsing */
605   auto cipherstream = get_attachment_stream (m_mailitem, m_moss_position);
606
607   if (!cipherstream)
608     {
609       log_debug ("%s:%s: Failed to get cipherstream.",
610                  SRCNAME, __func__);
611       return 1;
612     }
613
614   m_parser = std::shared_ptr <ParseController> (new ParseController (cipherstream, m_type));
615   gpgol_release (cipherstream);
616
617   HANDLE parser_thread = CreateThread (NULL, 0, do_parsing, (LPVOID) this, 0,
618                                        NULL);
619
620   if (!parser_thread)
621     {
622       log_error ("%s:%s: Failed to create decrypt / verify thread.",
623                  SRCNAME, __func__);
624     }
625   CloseHandle (parser_thread);
626
627   return 0;
628 }
629
630 void
631 Mail::update_body()
632 {
633   const auto error = m_parser->get_formatted_error ();
634   if (!error.empty())
635     {
636       if (opt.prefer_html)
637         {
638           if (put_oom_string (m_mailitem, "HTMLBody",
639                               error.c_str ()))
640             {
641               log_error ("%s:%s: Failed to modify html body of item.",
642                          SRCNAME, __func__);
643             }
644         }
645       else
646         {
647           if (put_oom_string (m_mailitem, "Body",
648                               error.c_str ()))
649             {
650               log_error ("%s:%s: Failed to modify html body of item.",
651                          SRCNAME, __func__);
652             }
653         }
654       return;
655     }
656   const auto html = m_parser->get_html_body();
657   if (opt.prefer_html && !html.empty())
658     {
659       char *converted = ansi_charset_to_utf8 (m_parser->get_html_charset().c_str(),
660                                               html.c_str(), html.size());
661       int ret = put_oom_string (m_mailitem, "HTMLBody", converted ? converted : "");
662       xfree (converted);
663       if (ret)
664         {
665           log_error ("%s:%s: Failed to modify html body of item.",
666                      SRCNAME, __func__);
667         }
668       return;
669     }
670   const auto body = m_parser->get_body();
671   char *converted = ansi_charset_to_utf8 (m_parser->get_body_charset().c_str(),
672                                           body.c_str(), body.size());
673   int ret = put_oom_string (m_mailitem, "Body", converted ? converted : "");
674   xfree (converted);
675   if (ret)
676     {
677       log_error ("%s:%s: Failed to modify body of item.",
678                  SRCNAME, __func__);
679     }
680   return;
681 }
682
683 void
684 Mail::parsing_done()
685 {
686   TRACEPOINT;
687   log_oom_extra ("Mail %p Parsing done for parser: %p",
688                  this, m_parser.get());
689   if (!m_parser)
690     {
691       /* This should not happen but it happens when outlook
692          sends multiple ItemLoad events for the same Mail
693          Object. In that case it could happen that one
694          parser was already done while a second is now
695          returning for the wrong mail (as it's looked up
696          by uuid.)
697
698          We have a check in get_uuid that the uuid was
699          not in the map before (and the parser is replaced).
700          So this really really should not happen. We
701          handle it anyway as we crash otherwise.
702
703          It should not happen because the parser is only
704          created in decrypt_verify which is called in the
705          read event. And even in there we check if the parser
706          was set.
707          */
708       log_error ("%s:%s: No parser obj. For mail: %p",
709                  SRCNAME, __func__, this);
710       return;
711     }
712   /* Store the results. */
713   m_decrypt_result = m_parser->decrypt_result ();
714   m_verify_result = m_parser->verify_result ();
715
716   m_crypto_flags = 0;
717   if (m_decrypt_result.numRecipients())
718     {
719       m_crypto_flags |= 1;
720     }
721   if (m_verify_result.numSignatures())
722     {
723       m_crypto_flags |= 2;
724     }
725
726   update_sigstate ();
727   m_needs_wipe = true;
728
729   TRACEPOINT;
730   /* Set categories according to the result. */
731   update_categories();
732
733   TRACEPOINT;
734   /* Update the body */
735   update_body();
736   TRACEPOINT;
737
738   /* Update attachments */
739   if (add_attachments (m_mailitem, m_parser->get_attachments()))
740     {
741       log_error ("%s:%s: Failed to update attachments.",
742                  SRCNAME, __func__);
743     }
744
745   /* Invalidate UI to set the correct sig status. */
746   m_parser = nullptr;
747   gpgoladdin_invalidate_ui ();
748   TRACEPOINT;
749   return;
750 }
751
752 int
753 Mail::encrypt_sign ()
754 {
755   int err = -1,
756       flags = 0;
757   protocol_t proto = opt.enable_smime ? PROTOCOL_UNKNOWN : PROTOCOL_OPENPGP;
758   if (!needs_crypto())
759     {
760       return 0;
761     }
762   LPMESSAGE message = get_oom_base_message (m_mailitem);
763   if (!message)
764     {
765       log_error ("%s:%s: Failed to get base message.",
766                  SRCNAME, __func__);
767       return err;
768     }
769   flags = get_gpgol_draft_info_flags (message);
770   if (flags == 3)
771     {
772       log_debug ("%s:%s: Sign / Encrypting message",
773                  SRCNAME, __func__);
774       err = message_sign_encrypt (message, proto,
775                                   NULL, get_sender ().c_str (), this);
776     }
777   else if (flags == 2)
778     {
779       err = message_sign (message, proto,
780                           NULL, get_sender ().c_str (), this);
781     }
782   else if (flags == 1)
783     {
784       err = message_encrypt (message, proto,
785                              NULL, get_sender ().c_str (), this);
786     }
787   else
788     {
789       log_debug ("%s:%s: Unknown flags for crypto: %i",
790                  SRCNAME, __func__, flags);
791     }
792   log_debug ("%s:%s: Status: %i",
793              SRCNAME, __func__, err);
794   gpgol_release (message);
795   m_crypt_successful = !err;
796   return err;
797 }
798
799 int
800 Mail::needs_crypto ()
801 {
802   LPMESSAGE message = get_oom_message (m_mailitem);
803   bool ret;
804   if (!message)
805     {
806       log_error ("%s:%s: Failed to get message.",
807                  SRCNAME, __func__);
808       return false;
809     }
810   ret = get_gpgol_draft_info_flags (message);
811   gpgol_release(message);
812   return ret;
813 }
814
815 int
816 Mail::wipe ()
817 {
818   if (!m_needs_wipe)
819     {
820       return 0;
821     }
822   log_debug ("%s:%s: Removing plaintext from mailitem: %p.",
823              SRCNAME, __func__, m_mailitem);
824   if (put_oom_string (m_mailitem, "HTMLBody",
825                       ""))
826     {
827       if (put_oom_string (m_mailitem, "Body",
828                           ""))
829         {
830           log_debug ("%s:%s: Failed to wipe mailitem: %p.",
831                      SRCNAME, __func__, m_mailitem);
832           return -1;
833         }
834       return -1;
835     }
836   m_needs_wipe = false;
837   return 0;
838 }
839
840 int
841 Mail::update_oom_data ()
842 {
843   LPDISPATCH sender = NULL;
844   log_debug ("%s:%s", SRCNAME, __func__);
845
846   /* Update the body format. */
847   m_is_html_alternative = get_oom_int (m_mailitem, "BodyFormat") > 1;
848
849   /* Store the body. It was not obvious for me (aheinecke) how
850      to access this through MAPI. */
851   m_html_body = get_oom_string (m_mailitem, "HTMLBody");
852
853   /* For some reason outlook may store the recipient address
854      in the send using account field. If we have SMTP we prefer
855      the SenderEmailAddress string. */
856
857   if (is_crypto_mail ())
858     {
859       /* This is the case where we are reading a mail and not composing.
860          When composing we need to use the SendUsingAccount because if
861          you send from the folder of userA but change the from to userB
862          outlook will keep the SenderEmailAddress of UserA. This is all
863          so horrible. */
864       char *type = get_oom_string (m_mailitem, "SenderEmailType");
865       if (type && !strcmp ("SMTP", type))
866         {
867           char *senderMail = get_oom_string (m_mailitem, "SenderEmailAddress");
868           if (senderMail)
869             {
870               m_sender = senderMail;
871               xfree (senderMail);
872               xfree (type);
873               return 0;
874             }
875         }
876       xfree (type);
877     }
878   sender = get_oom_object (m_mailitem, "SendUsingAccount");
879   if (sender)
880     {
881       char *buf = get_oom_string (sender, "SmtpAddress");
882       m_sender = buf;
883       xfree (buf);
884       gpgol_release (sender);
885       return 0;
886     }
887   /* Fallback to Sender object */
888   sender = get_oom_object (m_mailitem, "Sender");
889   if (sender)
890     {
891       char *buf = get_pa_string (sender, PR_SMTP_ADDRESS_DASL);
892       m_sender = buf;
893       xfree (buf);
894       gpgol_release (sender);
895       return 0;
896     }
897   /* We don't have s sender object or SendUsingAccount,
898      well, in that case fall back to the current user. */
899   sender = get_oom_object (m_mailitem, "Session.CurrentUser");
900   if (sender)
901     {
902       char *buf = get_pa_string (sender, PR_SMTP_ADDRESS_DASL);
903       m_sender = buf;
904       xfree (buf);
905       gpgol_release (sender);
906       return 0;
907     }
908
909   log_debug ("%s:%s: All fallbacks failed.",
910              SRCNAME, __func__);
911   return -1;
912 }
913
914 std::string
915 Mail::get_sender ()
916 {
917   if (m_sender.empty())
918     update_oom_data();
919   return m_sender;
920 }
921
922 int
923 Mail::close_all_mails ()
924 {
925   int err = 0;
926   std::map<LPDISPATCH, Mail *>::iterator it;
927   TRACEPOINT;
928   std::map<LPDISPATCH, Mail *> mail_map_copy = g_mail_map;
929   for (it = mail_map_copy.begin(); it != mail_map_copy.end(); ++it)
930     {
931       if (!it->second->is_crypto_mail())
932         {
933           continue;
934         }
935       if (close_inspector (it->second) || close (it->second))
936         {
937           log_error ("Failed to close mail: %p ", it->first);
938           /* Should not happen */
939           if (is_valid_ptr (it->second) && it->second->revert())
940             {
941               err++;
942             }
943         }
944     }
945   return err;
946 }
947 int
948 Mail::revert_all_mails ()
949 {
950   int err = 0;
951   std::map<LPDISPATCH, Mail *>::iterator it;
952   for (it = g_mail_map.begin(); it != g_mail_map.end(); ++it)
953     {
954       if (it->second->revert ())
955         {
956           log_error ("Failed to revert mail: %p ", it->first);
957           err++;
958           continue;
959         }
960
961       it->second->set_needs_save (true);
962       if (!invoke_oom_method (it->first, "Save", NULL))
963         {
964           log_error ("Failed to save reverted mail: %p ", it->second);
965           err++;
966           continue;
967         }
968     }
969   return err;
970 }
971
972 int
973 Mail::wipe_all_mails ()
974 {
975   int err = 0;
976   std::map<LPDISPATCH, Mail *>::iterator it;
977   for (it = g_mail_map.begin(); it != g_mail_map.end(); ++it)
978     {
979       if (it->second->wipe ())
980         {
981           log_error ("Failed to wipe mail: %p ", it->first);
982           err++;
983         }
984     }
985   return err;
986 }
987
988 int
989 Mail::revert ()
990 {
991   int err = 0;
992   if (!m_processed)
993     {
994       return 0;
995     }
996
997   err = gpgol_mailitem_revert (m_mailitem);
998   if (err == -1)
999     {
1000       log_error ("%s:%s: Message revert failed falling back to wipe.",
1001                  SRCNAME, __func__);
1002       return wipe ();
1003     }
1004   /* We need to reprocess the mail next time around. */
1005   m_processed = false;
1006   m_needs_wipe = false;
1007   return 0;
1008 }
1009
1010 bool
1011 Mail::is_smime ()
1012 {
1013   msgtype_t msgtype;
1014   LPMESSAGE message;
1015
1016   if (m_is_smime_checked)
1017     {
1018       return m_is_smime;
1019     }
1020
1021   message = get_oom_message (m_mailitem);
1022
1023   if (!message)
1024     {
1025       log_error ("%s:%s: No message?",
1026                  SRCNAME, __func__);
1027       return false;
1028     }
1029
1030   msgtype = mapi_get_message_type (message);
1031   m_is_smime = msgtype == MSGTYPE_GPGOL_OPAQUE_ENCRYPTED ||
1032                msgtype == MSGTYPE_GPGOL_OPAQUE_SIGNED;
1033
1034   /* Check if it is an smime mail. Multipart signed can
1035      also be true. */
1036   if (!m_is_smime && msgtype == MSGTYPE_GPGOL_MULTIPART_SIGNED)
1037     {
1038       char *proto;
1039       char *ct = mapi_get_message_content_type (message, &proto, NULL);
1040       if (ct && proto)
1041         {
1042           m_is_smime = (!strcmp (proto, "application/pkcs7-signature") ||
1043                         !strcmp (proto, "application/x-pkcs7-signature"));
1044         }
1045       else
1046         {
1047           log_error ("Protocol in multipart signed mail.");
1048         }
1049       xfree (proto);
1050       xfree (ct);
1051     }
1052   gpgol_release (message);
1053   m_is_smime_checked  = true;
1054   return m_is_smime;
1055 }
1056
1057 static std::string
1058 get_string (LPDISPATCH item, const char *str)
1059 {
1060   char *buf = get_oom_string (item, str);
1061   if (!buf)
1062     return std::string();
1063   std::string ret = buf;
1064   xfree (buf);
1065   return ret;
1066 }
1067
1068 std::string
1069 Mail::get_subject() const
1070 {
1071   return get_string (m_mailitem, "Subject");
1072 }
1073
1074 std::string
1075 Mail::get_body() const
1076 {
1077   return get_string (m_mailitem, "Body");
1078 }
1079
1080 std::string
1081 Mail::get_html_body() const
1082 {
1083   return get_string (m_mailitem, "HTMLBody");
1084 }
1085
1086 char **
1087 Mail::get_recipients() const
1088 {
1089   LPDISPATCH recipients = get_oom_object (m_mailitem, "Recipients");
1090   if (!recipients)
1091     {
1092       TRACEPOINT;
1093       return nullptr;
1094     }
1095   return get_oom_recipients (recipients);
1096 }
1097
1098 int
1099 Mail::close_inspector (Mail *mail)
1100 {
1101   LPDISPATCH inspector = get_oom_object (mail->item(), "GetInspector");
1102   HRESULT hr;
1103   DISPID dispid;
1104   if (!inspector)
1105     {
1106       log_debug ("%s:%s: No inspector.",
1107                  SRCNAME, __func__);
1108       return -1;
1109     }
1110
1111   dispid = lookup_oom_dispid (inspector, "Close");
1112   if (dispid != DISPID_UNKNOWN)
1113     {
1114       VARIANT aVariant[1];
1115       DISPPARAMS dispparams;
1116
1117       dispparams.rgvarg = aVariant;
1118       dispparams.rgvarg[0].vt = VT_INT;
1119       dispparams.rgvarg[0].intVal = 1;
1120       dispparams.cArgs = 1;
1121       dispparams.cNamedArgs = 0;
1122
1123       hr = inspector->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
1124                               DISPATCH_METHOD, &dispparams,
1125                               NULL, NULL, NULL);
1126       if (hr != S_OK)
1127         {
1128           log_debug ("%s:%s: Failed to close inspector: %#lx",
1129                      SRCNAME, __func__, hr);
1130           return -1;
1131         }
1132     }
1133   return 0;
1134 }
1135
1136 /* static */
1137 int
1138 Mail::close (Mail *mail)
1139 {
1140   VARIANT aVariant[1];
1141   DISPPARAMS dispparams;
1142
1143   dispparams.rgvarg = aVariant;
1144   dispparams.rgvarg[0].vt = VT_INT;
1145   dispparams.rgvarg[0].intVal = 1;
1146   dispparams.cArgs = 1;
1147   dispparams.cNamedArgs = 0;
1148
1149   log_oom_extra ("%s:%s: Invoking close for: %p",
1150                  SRCNAME, __func__, mail->item());
1151   mail->set_close_triggered (true);
1152   int rc = invoke_oom_method_with_parms (mail->item(), "Close",
1153                                        NULL, &dispparams);
1154
1155   log_debug ("returned from invoke");
1156   return rc;
1157 }
1158
1159 void
1160 Mail::set_close_triggered (bool value)
1161 {
1162   m_close_triggered = value;
1163 }
1164
1165 bool
1166 Mail::get_close_triggered () const
1167 {
1168   return m_close_triggered;
1169 }
1170
1171 static const UserID
1172 get_uid_for_sender (const Key k, const char *sender)
1173 {
1174   UserID ret;
1175
1176   if (!sender)
1177     {
1178       return ret;
1179     }
1180
1181   if (!k.numUserIDs())
1182     {
1183       log_debug ("%s:%s: Key without uids",
1184                  SRCNAME, __func__);
1185       return ret;
1186     }
1187
1188   for (const auto uid: k.userIDs())
1189     {
1190       if (!uid.email())
1191         {
1192           log_error ("%s:%s: skipping uid without email.",
1193                      SRCNAME, __func__);
1194           continue;
1195         }
1196       auto normalized_uid = uid.addrSpec();
1197       auto normalized_sender = UserID::addrSpecFromString(sender);
1198
1199       if (normalized_sender.empty() || normalized_uid.empty())
1200         {
1201           log_error ("%s:%s: normalizing '%s' or '%s' failed.",
1202                      SRCNAME, __func__, uid.email(), sender);
1203           continue;
1204         }
1205       if (normalized_sender == normalized_uid)
1206         {
1207           ret = uid;
1208         }
1209     }
1210   return ret;
1211 }
1212
1213 void
1214 Mail::update_sigstate ()
1215 {
1216   std::string sender = get_sender();
1217
1218   if (sender.empty())
1219     {
1220       log_error ("%s:%s:%i", SRCNAME, __func__, __LINE__);
1221       return;
1222     }
1223
1224   if (m_verify_result.isNull())
1225     {
1226       log_debug ("%s:%s: No verify result.",
1227                  SRCNAME, __func__);
1228       return;
1229     }
1230
1231   if (m_verify_result.error())
1232     {
1233       log_debug ("%s:%s: verify error.",
1234                  SRCNAME, __func__);
1235       return;
1236     }
1237
1238   for (const auto sig: m_verify_result.signatures())
1239     {
1240       m_is_signed = true;
1241       if (sig.validity() != Signature::Validity::Marginal &&
1242           sig.validity() != Signature::Validity::Full &&
1243           sig.validity() != Signature::Validity::Ultimate)
1244         {
1245           /* For our category we only care about trusted sigs. */
1246           continue;
1247         }
1248       const auto uid = get_uid_for_sender (sig.key(), sender.c_str());
1249       if (sig.validity() == Signature::Validity::Marginal)
1250         {
1251           const auto tofu = uid.tofuInfo();
1252           if (tofu.isNull() ||
1253               (tofu.validity() != TofuInfo::Validity::LittleHistory &&
1254                tofu.validity() != TofuInfo::Validity::BasicHistory &&
1255                tofu.validity() != TofuInfo::Validity::LargeHistory))
1256             {
1257               /* Marginal is not good enough without tofu.
1258                  We also wait for basic trust. */
1259               log_debug ("%s:%s: Discarding marginal signature.",
1260                          SRCNAME, __func__);
1261               continue;
1262             }
1263           /* GnuPG uses the encrypt count to determine validity.
1264              This does not make sense for us. E.g. Drafts may have
1265              been encrypted and encryption is no communication so
1266              it does not track communication history or consistency.
1267              So basically "our" tofu validity is that more then 10 messages
1268              have been exchanged. Which was the original code in GnuPG */
1269           if (!tofu.isNull() && tofu.signCount() <= GPGOL_BASIC_TOFU_TRUST) {
1270               log_debug ("%s:%s: Tofu signcount too small.",
1271                          SRCNAME, __func__);
1272               continue;
1273           }
1274         }
1275       log_debug ("%s:%s: Classified sender as verified",
1276                  SRCNAME, __func__);
1277       m_sig = sig;
1278       m_uid = uid;
1279       m_is_valid = true;
1280       return;
1281     }
1282
1283   log_debug ("%s:%s: No signature with enough trust. Using first",
1284              SRCNAME, __func__);
1285   m_sig = m_verify_result.signature(0);
1286   return;
1287 }
1288
1289 const std::pair <Signature, UserID>
1290 Mail::get_valid_sig ()
1291 {
1292   std::pair <Signature, UserID> ret;
1293   if (!m_is_valid)
1294     {
1295       return ret;
1296     }
1297   return std::pair<Signature, UserID> (m_sig, m_uid);
1298 }
1299
1300 bool
1301 Mail::is_valid_sig ()
1302 {
1303    return m_is_valid;
1304 }
1305
1306 void
1307 Mail::remove_categories ()
1308 {
1309   const char *decCategory = _("GpgOL: Encrypted Message");
1310   const char *verifyCategory = _("GpgOL: Trusted Sender Address");
1311   remove_category (m_mailitem, decCategory);
1312   remove_category (m_mailitem, verifyCategory);
1313 }
1314
1315 void
1316 Mail::update_categories ()
1317 {
1318   const char *decCategory = _("GpgOL: Encrypted Message");
1319   const char *verifyCategory = _("GpgOL: Trusted Sender Address");
1320   if (m_decrypt_result.numRecipients())
1321     {
1322       /* We use the number of recipients as we don't care
1323          if decryption was successful or not for this category */
1324       add_category (m_mailitem, decCategory);
1325     }
1326   else
1327     {
1328       /* As a small safeguard against fakes we remove our
1329          categories */
1330       remove_category (m_mailitem, decCategory);
1331     }
1332
1333   if (is_valid_sig())
1334     {
1335       add_category (m_mailitem, verifyCategory);
1336     }
1337   else
1338     {
1339       remove_category (m_mailitem, verifyCategory);
1340     }
1341   return;
1342 }
1343
1344 bool
1345 Mail::is_signed()
1346 {
1347   return m_verify_result.numSignatures() > 0;
1348 }
1349
1350 int
1351 Mail::set_uuid()
1352 {
1353   char *uuid;
1354   if (!m_uuid.empty())
1355     {
1356       /* This codepath is reached by decrypt again after a
1357          close with discard changes. The close discarded
1358          the uuid on the OOM object so we have to set
1359          it again. */
1360       log_debug ("%s:%s: Resetting uuid for %p to %s",
1361                  SRCNAME, __func__, this,
1362                  m_uuid.c_str());
1363       uuid = get_unique_id (m_mailitem, 1, m_uuid.c_str());
1364     }
1365   else
1366     {
1367       uuid = get_unique_id (m_mailitem, 1, nullptr);
1368       log_debug ("%s:%s: uuid for %p set to %s",
1369                  SRCNAME, __func__, this, uuid);
1370     }
1371
1372   if (!uuid)
1373     {
1374       log_debug ("%s:%s: Failed to get/set uuid for %p",
1375                  SRCNAME, __func__, m_mailitem);
1376       return -1;
1377     }
1378   if (m_uuid.empty())
1379     {
1380       m_uuid = uuid;
1381       Mail *other = get_mail_for_uuid (uuid);
1382       if (other)
1383         {
1384           /* According to documentation this should not
1385              happen as this means that multiple ItemLoad
1386              events occured for the same mailobject without
1387              unload / destruction of the mail.
1388
1389              But it happens. If you invalidate the UI
1390              in the selection change event Outlook loads a
1391              new mailobject for the mail. Might happen in
1392              other surprising cases. We replace in that
1393              case as experiments have shown that the last
1394              mailobject is the one that is visible.
1395
1396              Still troubling state so we log this as an error.
1397              */
1398           log_error ("%s:%s: There is another mail for %p "
1399                      "with uuid: %s replacing it.",
1400                      SRCNAME, __func__, m_mailitem, uuid);
1401           delete other;
1402         }
1403       g_uid_map.insert (std::pair<std::string, Mail *> (m_uuid, this));
1404       log_debug ("%s:%s: uuid for %p is now %s",
1405                  SRCNAME, __func__, this,
1406                  m_uuid.c_str());
1407     }
1408   xfree (uuid);
1409   return 0;
1410 }
1411
1412 std::string
1413 Mail::get_signature_status()
1414 {
1415   std::string message;
1416   if (!is_signed())
1417     {
1418       message =_("This message is not signed.\n");
1419       message += _("You cannot be sure who wrote the message.");
1420       return message;
1421     }
1422
1423   const auto pair = get_valid_sig ();
1424   bool keyFound = false;
1425   bool isOpenPGP = pair.first.key().protocol() == Protocol::OpenPGP;
1426   char *buf;
1427   bool hasConflict = false;
1428   if (!pair.first.isNull () && !pair.second.isNull ())
1429     {
1430       const auto sig = pair.first;
1431       const auto uid = pair.second;
1432       /* We are valid */
1433       keyFound = true;
1434       if (sig.validity() == Signature::Validity::Full ||
1435           sig.validity() == Signature::Validity::Ultimate)
1436         {
1437           message += _("The sender address is fully trusted because:");
1438         }
1439       else
1440         {
1441           message += _("The sender address is trusted because:");
1442         }
1443       message += "\n\n";
1444       message += isOpenPGP ? _("The used key") : _("The used certificate");
1445       message += " ";
1446       message += sig.validity() == Signature::Validity::Ultimate ?
1447                       _("is marked as your own.") :
1448                       sig.validity() == Signature::Validity::Full && isOpenPGP &&
1449                       uid.tofuInfo().policy() == TofuInfo::PolicyGood ?
1450                       _("was marked to be the right key for this address") :
1451                       sig.validity() == Signature::Validity::Full && isOpenPGP ?
1452                       _("was certified by enough trusted keys or yourself.") :
1453                       "";
1454       if (sig.validity() == Signature::Validity::Full && !isOpenPGP)
1455         {
1456           gpgrt_asprintf (&buf, _("is cerified by the trusted issuer:\n'%s'\n"),
1457                           sig.key().issuerName());
1458           message += buf;
1459           xfree (buf);
1460         }
1461       else if (sig.validity() == Signature::Validity::Marginal)
1462         {
1463           char *time = format_date_from_gpgme (uid.tofuInfo().signFirst());
1464           /* i18n note signcount is always pulral because with signcount 1 we
1465            * would not be in this branch. */
1466           gpgrt_asprintf (&buf, _("was used for %i messages since %s."),
1467                           uid.tofuInfo().signCount (), time);
1468           xfree (time);
1469           message += buf;
1470           xfree (buf);
1471         }
1472     }
1473   else
1474     {
1475       if (m_verify_result.numSignatures() > 1)
1476         {
1477           log_debug ("%s:%s: More then one signature found on %p",
1478                      SRCNAME, __func__, m_mailitem);
1479         }
1480       /* We only handle the first signature. */
1481       const auto sig = m_verify_result.signature (0);
1482       isOpenPGP = !is_smime();
1483       keyFound = !(sig.summary() & Signature::Summary::KeyMissing);
1484
1485       log_debug ("%s:%s: Formatting sig. Validity: %x Summary: %x",
1486                  SRCNAME, __func__, sig.validity(), sig.summary());
1487
1488       /* There is a signature but we don't accepted it as fully valid. */
1489       message += _("The sender address is not trusted because:");
1490       message += "\n\n";
1491
1492       bool general_problem = true;
1493       /* First the general stuff. */
1494       if (sig.summary() & Signature::Summary::Red)
1495         {
1496           message += _("The signature is invalid.\n");
1497           message += _("You cannot be sure who wrote the message.");
1498         }
1499       else if (sig.summary() & Signature::Summary::SysError ||
1500                m_verify_result.numSignatures() < 1)
1501         {
1502           message += _("There was an error verifying the signature.\n");
1503           message += _("You cannot be sure who wrote the message.");
1504         }
1505       else if (sig.summary() & Signature::Summary::SigExpired)
1506         {
1507           message += _("The signature is expired.\n");
1508         }
1509       else
1510         {
1511           message += isOpenPGP ? _("The used key") : _("The used certificate");
1512           message += " ";
1513           general_problem = false;
1514         }
1515
1516       const auto uid = get_uid_for_sender (sig.key(), get_sender().c_str());
1517       /* Now the key problems */
1518       if ((sig.summary() & Signature::Summary::KeyMissing))
1519         {
1520           message += _("is not available for verification.");
1521         }
1522       else if ((sig.summary() & Signature::Summary::KeyRevoked))
1523         {
1524           message += _("is revoked.");
1525         }
1526       else if ((sig.summary() & Signature::Summary::KeyExpired))
1527         {
1528           message += _("is expired.");
1529         }
1530       else if ((sig.summary() & Signature::Summary::BadPolicy))
1531         {
1532           message += _("is not meant for signing.");
1533         }
1534       else if ((sig.summary() & Signature::Summary::CrlMissing))
1535         {
1536           message += _("could not be checked for revocation.");
1537         }
1538       else if ((sig.summary() & Signature::Summary::CrlTooOld))
1539         {
1540           message += _("could not be checked for revocation.");
1541         }
1542       else if ((sig.summary() & Signature::Summary::TofuConflict) ||
1543                uid.tofuInfo().validity() == TofuInfo::Conflict)
1544         {
1545           message += _("conflicts with another key that was used in the past by the sender.");
1546           hasConflict = true;
1547         }
1548       else if (uid.isNull())
1549         {
1550           gpgrt_asprintf (&buf, _("does not claim the address: \"%s\"."),
1551                           get_sender().c_str());
1552           message += buf;
1553           xfree (buf);
1554         }
1555       else if ((sig.validity() & Signature::Validity::Marginal))
1556         {
1557           const auto tofuInfo = uid.tofuInfo();
1558           if (tofuInfo.isNull() || !tofuInfo.signCount())
1559             {
1560               message += _("is not certified by enough trusted keys.");
1561             }
1562           else if (tofuInfo.signCount() == 1)
1563             {
1564               message += _("is seen for the first time.");
1565             }
1566           else
1567             {
1568               gpgrt_asprintf (&buf, "was only used for %i messages.",
1569                               tofuInfo.signCount());
1570               message += buf;
1571               xfree (buf);
1572             }
1573         }
1574       else if (((sig.validity() & Signature::Validity::Undefined) ||
1575                (sig.validity() & Signature::Validity::Unknown) ||
1576                (sig.summary() == Signature::Summary::None) ||
1577                (sig.validity() == 0))&& !general_problem)
1578         {
1579            /* Bit of a catch all for weird results. */
1580           message += _("is not certified by any trusted key.");
1581         }
1582       else if ((sig.validity() & Signature::Validity::Never))
1583         {
1584           message += _("is explicitly marked as invalid.");
1585         }
1586     }
1587   message += "\n\n";
1588   if (hasConflict)
1589     {
1590       message += _("Click here to resolve the conflict.");
1591     }
1592   else if (keyFound)
1593     {
1594       message +=  isOpenPGP ? _("Click here for details about the key.") :
1595                               _("Click here for details about the certificate.");
1596     }
1597   else
1598     {
1599       message +=  isOpenPGP ? _("Click here to search the key on the configured keyserver.") :
1600                               _("Click here to search the certificate on the configured X509 keyserver.");
1601     }
1602   return message;
1603 }
1604
1605 int
1606 Mail::get_signature_icon_id () const
1607 {
1608   if (!m_is_signed)
1609     {
1610       return IDI_EMBLEM_INFORMATION_64_PNG;
1611     }
1612   const auto sig = m_verify_result.signature (0);
1613   if ((sig.summary() & Signature::Summary::KeyMissing))
1614     {
1615       return IDI_EMBLEM_QUESTION_64_PNG;
1616     }
1617   if (m_is_valid && sig.validity() == Signature::Validity::Full)
1618     {
1619       return IDI_EMBLEM_SUCCESS_64_PNG;
1620     }
1621   else if (m_is_valid)
1622     {
1623       return IDI_EMBLEM_SUCCESS_YELLOW_64_PNG;
1624     }
1625   const auto uid = get_uid_for_sender (sig.key(), m_sender.c_str());
1626   if (sig.summary() & Signature::Summary::TofuConflict ||
1627       uid.tofuInfo().validity() == TofuInfo::Conflict)
1628     {
1629       return IDI_EMBLEM_WARNING_64_PNG;
1630     }
1631   return IDI_EMBLEM_INFORMATION_64_PNG;
1632 }
1633
1634 const char*
1635 Mail::get_sig_fpr() const
1636 {
1637   if (!m_is_signed || m_sig.isNull())
1638     {
1639       return nullptr;
1640     }
1641   return m_sig.fingerprint();
1642 }
1643
1644
1645 static DWORD WINAPI
1646 do_locate (LPVOID arg)
1647 {
1648   char *recipient = (char*) arg;
1649   log_debug ("%s:%s searching key for recipient: \"%s\"",
1650              SRCNAME, __func__, recipient);
1651   Context *ctx = Context::createForProtocol (OpenPGP);
1652
1653   if (!ctx)
1654     {
1655       TRACEPOINT;
1656       return 0;
1657     }
1658
1659   ctx->setKeyListMode (GpgME::Extern | GpgME::Local);
1660   ctx->startKeyListing (recipient, false);
1661
1662   std::vector<Key> keys;
1663   Error err;
1664   do {
1665       keys.push_back (ctx->nextKey(err));
1666     } while (!err);
1667   keys.pop_back ();
1668   ctx->endKeyListing ();
1669   delete ctx;
1670
1671   if (keys.size ())
1672     {
1673       log_debug ("%s:%s found key for recipient: \"%s\"",
1674                  SRCNAME, __func__, recipient);
1675     }
1676   xfree (recipient);
1677   do_in_ui_thread (UNKNOWN, NULL);
1678   return 0;
1679 }
1680
1681 /** Try to locate the keys for all recipients */
1682 void Mail::locate_keys()
1683 {
1684   char ** recipients = get_recipients ();
1685
1686   if (!recipients)
1687     {
1688       TRACEPOINT;
1689       return;
1690     }
1691   for (int i = 0; recipients[i]; i++)
1692     {
1693       std::string recp = recipients[i];
1694       if (uids_searched.find (recp) == uids_searched.end ())
1695         {
1696           uids_searched.insert (recp);
1697           HANDLE thread = CreateThread (NULL, 0, do_locate,
1698                                         (LPVOID) strdup(recipients[i]), 0,
1699                                         NULL);
1700           CloseHandle (thread);
1701         }
1702       xfree (recipients[i]);
1703     }
1704   xfree (recipients);
1705 }
1706
1707 bool
1708 Mail::is_html_alternative () const
1709 {
1710   return m_is_html_alternative;
1711 }
1712
1713 const std::string &
1714 Mail::get_cached_html_body () const
1715 {
1716   return m_html_body;
1717 }
1718
1719 int
1720 Mail::get_crypto_flags () const
1721 {
1722   return m_crypto_flags;
1723 }
1724
1725 void
1726 Mail::set_needs_encrypt (bool value)
1727 {
1728   m_needs_encrypt = value;
1729 }
1730
1731 bool
1732 Mail::needs_encrypt() const
1733 {
1734   return m_needs_encrypt;
1735 }