2 * @brief High level class to work with Outlook Mailitems.
4 * Copyright (C) 2015, 2016 Intevation GmbH
6 * This file is part of GpgOL.
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.
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.
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/>.
26 #include "eventsinks.h"
27 #include "attachment.h"
31 #include "gpgoladdin.h"
32 #include "mymapitags.h"
33 #include "parsecontroller.h"
35 #include "windowmessages.h"
36 #include "mlang-charset.h"
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>
53 #define _(a) utf8_gettext (a)
55 using namespace GpgME;
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;
61 #define COPYBUFSIZE (8 * 1024)
63 Mail::Mail (LPDISPATCH mailitem) :
68 m_crypt_successful(false),
70 m_is_smime_checked(false),
73 m_close_triggered(false),
74 m_is_html_alternative(false),
75 m_needs_encrypt(false),
78 m_type(MSGTYPE_UNKNOWN)
80 if (get_mail_for_item (mailitem))
82 log_error ("Mail object for item: %p already exists. Bug.",
87 m_event_sink = install_MailItemEvents_sink (mailitem);
90 /* Should not happen but in that case we don't add us to the list
91 and just release the Mail item. */
92 log_error ("%s:%s: Failed to install MailItemEvents sink.",
94 gpgol_release(mailitem);
97 g_mail_map.insert (std::pair<LPDISPATCH, Mail *> (mailitem, this));
100 GPGRT_LOCK_DEFINE(dtor_lock);
104 /* This should fix a race condition where the mail is
105 deleted before the parser is accessed in the decrypt
106 thread. The shared_ptr of the parser then ensures
107 that the parser is alive even if the mail is deleted
109 gpgrt_lock_lock (&dtor_lock);
110 std::map<LPDISPATCH, Mail *>::iterator it;
112 detach_MailItemEvents_sink (m_event_sink);
113 gpgol_release(m_event_sink);
115 it = g_mail_map.find(m_mailitem);
116 if (it != g_mail_map.end())
118 g_mail_map.erase (it);
123 auto it2 = g_uid_map.find(m_uuid);
124 if (it2 != g_uid_map.end())
126 g_uid_map.erase (it2);
130 gpgol_release(m_mailitem);
133 log_oom_extra ("%s:%s: destroyed: %p uuid: %s",
134 SRCNAME, __func__, this, m_uuid.c_str());
138 log_oom_extra ("%s:%s: non crypto mail: %p destroyed",
139 SRCNAME, __func__, this);
141 gpgrt_lock_unlock (&dtor_lock);
145 Mail::get_mail_for_item (LPDISPATCH mailitem)
151 std::map<LPDISPATCH, Mail *>::iterator it;
152 it = g_mail_map.find(mailitem);
153 if (it == g_mail_map.end())
161 Mail::get_mail_for_uuid (const char *uuid)
167 auto it = g_uid_map.find(std::string(uuid));
168 if (it == g_uid_map.end())
176 Mail::is_valid_ptr (const Mail *mail)
178 auto it = g_mail_map.begin();
179 while (it != g_mail_map.end())
181 if (it->second == mail)
189 Mail::pre_process_message ()
191 LPMESSAGE message = get_oom_base_message (m_mailitem);
194 log_error ("%s:%s: Failed to get base message.",
198 log_oom_extra ("%s:%s: GetBaseMessage OK.",
200 /* Change the message class here. It is important that
201 we change the message class in the before read event
202 regardless if it is already set to one of GpgOL's message
203 classes. Changing the message class (even if we set it
204 to the same value again that it already has) causes
205 Outlook to reconsider what it "knows" about a message
206 and reread data from the underlying base message. */
207 mapi_change_message_class (message, 1);
208 /* TODO: Unify this so mapi_change_message_class returns
209 a useful value already. */
210 m_type = mapi_get_message_type (message);
212 /* Create moss attachments here so that they are properly
213 hidden when the item is read into the model. */
214 m_moss_position = mapi_mark_or_create_moss_attach (message, m_type);
215 if (!m_moss_position)
217 log_error ("%s:%s: Failed to find moss attachment.",
219 m_type = MSGTYPE_UNKNOWN;
222 gpgol_release (message);
227 get_attachment (LPDISPATCH mailitem, int pos)
229 LPDISPATCH attachment;
230 LPDISPATCH attachments = get_oom_object (mailitem, "Attachments");
233 log_debug ("%s:%s: Failed to get attachments.",
238 std::string item_str;
239 int count = get_oom_int (attachments, "Count");
242 item_str = std::string("Item(") + std::to_string(pos) + ")";
246 item_str = std::string("Item(") + std::to_string(count) + ")";
250 log_debug ("%s:%s: Invalid attachment count: %i.",
251 SRCNAME, __func__, count);
252 gpgol_release (attachments);
255 attachment = get_oom_object (attachments, item_str.c_str());
256 gpgol_release (attachments);
261 /** Get the cipherstream of the mailitem. */
263 get_attachment_stream (LPDISPATCH mailitem, int pos)
267 log_debug ("%s:%s: Called with zero pos.",
271 LPDISPATCH attachment = get_attachment (mailitem, pos);
272 LPATTACH mapi_attachment = NULL;
273 LPSTREAM stream = NULL;
275 mapi_attachment = (LPATTACH) get_oom_iunknown (attachment,
277 gpgol_release (attachment);
278 if (!mapi_attachment)
280 log_debug ("%s:%s: Failed to get MapiObject of attachment: %p",
281 SRCNAME, __func__, attachment);
284 if (FAILED (mapi_attachment->OpenProperty (PR_ATTACH_DATA_BIN, &IID_IStream,
285 0, MAPI_MODIFY, (LPUNKNOWN*) &stream)))
287 log_debug ("%s:%s: Failed to open stream for mapi_attachment: %p",
288 SRCNAME, __func__, mapi_attachment);
289 gpgol_release (mapi_attachment);
296 This should work. But Outlook says no. See the comment in set_pa_variant
297 about this. I left the code here as an example how to work with
298 safearrays and how this probably should work.
301 copy_data_property(LPDISPATCH target, std::shared_ptr<Attachment> attach)
307 off_t size = attach->get_data ().seek (0, SEEK_END);
308 attach->get_data ().seek (0, SEEK_SET);
316 if (!get_pa_variant (target, PR_ATTACH_DATA_BIN_DASL, &var))
318 log_debug("Have variant. type: %x", var.vt);
322 log_debug("failed to get variant.");
325 /* Set the type to an array of unsigned chars (OLE SAFEARRAY) */
326 var.vt = VT_ARRAY | VT_UI1;
328 /* Set up the bounds structure */
329 SAFEARRAYBOUND rgsabound[1];
330 rgsabound[0].cElements = static_cast<unsigned long> (size);
331 rgsabound[0].lLbound = 0;
333 /* Create an OLE SAFEARRAY */
334 var.parray = SafeArrayCreate (VT_UI1, 1, rgsabound);
335 if (var.parray == NULL)
343 /* Get a safe pointer to the array */
344 if (SafeArrayAccessData(var.parray, &buffer) != S_OK)
351 /* Copy data to it */
352 size_t nread = attach->get_data ().read (buffer, static_cast<size_t> (size));
354 if (nread != static_cast<size_t> (size))
361 /*/ Unlock the variant data */
362 if (SafeArrayUnaccessData(var.parray) != S_OK)
369 if (set_pa_variant (target, PR_ATTACH_DATA_BIN_DASL, &var))
382 copy_attachment_to_file (std::shared_ptr<Attachment> att, HANDLE hFile)
384 char copybuf[COPYBUFSIZE];
387 /* Security considerations: Writing the data to a temporary
388 file is necessary as neither MAPI manipulation works in the
389 read event to transmit the data nor Property Accessor
390 works (see above). From a security standpoint there is a
391 short time where the temporary files are on disk. Tempdir
392 should be protected so that only the user can read it. Thus
393 we have a local attack that could also take the data out
394 of Outlook. FILE_SHARE_READ is necessary so that outlook
397 A bigger concern is that the file is manipulated
398 by another software to fake the signature state. So
399 we keep the write exlusive to us.
401 We delete the file before closing the write file handle.
404 /* Make sure we start at the beginning */
405 att->get_data ().seek (0, SEEK_SET);
406 while ((nread = att->get_data ().read (copybuf, COPYBUFSIZE)))
409 if (!WriteFile (hFile, copybuf, nread, &nwritten, NULL))
411 log_error ("%s:%s: Failed to write in tmp attachment.",
415 if (nread != nwritten)
417 log_error ("%s:%s: Write truncated.",
425 /** Sets some meta data on the last attachment atted. The meta
426 data is taken from the attachment object. */
428 fixup_last_attachment (LPDISPATCH mail, std::shared_ptr<Attachment> attachment)
430 /* Currently we only set content id */
431 if (attachment->get_content_id ().empty())
433 log_debug ("%s:%s: Content id not found.",
438 LPDISPATCH attach = get_attachment (mail, -1);
441 log_error ("%s:%s: No attachment.",
445 int ret = put_pa_string (attach,
446 PR_ATTACH_CONTENT_ID_DASL,
447 attachment->get_content_id ().c_str());
448 gpgol_release (attach);
452 /** Helper to update the attachments of a mail object in oom.
453 does not modify the underlying mapi structure. */
455 add_attachments(LPDISPATCH mail,
456 std::vector<std::shared_ptr<Attachment> > attachments)
459 for (auto att: attachments)
461 if (att->get_display_name().empty())
463 log_error ("%s:%s: Ignoring attachment without display name.",
467 wchar_t* wchar_name = utf8_to_wchar (att->get_display_name().c_str());
469 wchar_t* wchar_file = get_tmp_outfile (GpgOLStr (att->get_display_name().c_str()),
471 if (copy_attachment_to_file (att, hFile))
473 log_error ("%s:%s: Failed to copy attachment %s to temp file",
474 SRCNAME, __func__, att->get_display_name().c_str());
477 if (add_oom_attachment (mail, wchar_file, wchar_name))
479 log_error ("%s:%s: Failed to add attachment: %s",
480 SRCNAME, __func__, att->get_display_name().c_str());
483 if (!DeleteFileW (wchar_file))
485 log_error ("%s:%s: Failed to delete tmp attachment for: %s",
486 SRCNAME, __func__, att->get_display_name().c_str());
492 err = fixup_last_attachment (mail, att);
497 GPGRT_LOCK_DEFINE(parser_lock);
500 do_parsing (LPVOID arg)
502 gpgrt_lock_lock (&dtor_lock);
503 /* We lock with mail dtors so we can be sure the mail->parser
505 Mail *mail = (Mail *)arg;
506 if (!Mail::is_valid_ptr (mail))
508 log_debug ("%s:%s: canceling parsing for: %p already deleted",
509 SRCNAME, __func__, arg);
510 gpgrt_lock_unlock (&dtor_lock);
513 /* This takes a shared ptr of parser. So the parser is
514 still valid when the mail is deleted. */
515 auto parser = mail->parser();
516 gpgrt_lock_unlock (&dtor_lock);
518 gpgrt_lock_lock (&parser_lock);
519 /* We lock the parser here to avoid too many
520 decryption attempts if there are
521 multiple mailobjects which might have already
522 been deleted (e.g. by quick switches of the mailview.)
523 Let's rather be a bit slower.
525 log_debug ("%s:%s: preparing the parser for: %p",
526 SRCNAME, __func__, arg);
530 log_error ("%s:%s: no parser found for mail: %p",
531 SRCNAME, __func__, arg);
532 gpgrt_lock_unlock (&parser_lock);
536 do_in_ui_thread (PARSING_DONE, arg);
537 gpgrt_lock_unlock (&parser_lock);
542 Mail::is_crypto_mail() const
544 if (m_type == MSGTYPE_UNKNOWN || m_type == MSGTYPE_GPGOL ||
545 m_type == MSGTYPE_SMIME)
547 /* Not a message for us. */
554 Mail::decrypt_verify()
556 if (!is_crypto_mail())
562 log_error ("%s:%s: Decrypt verify called for msg that needs wipe: %p",
563 SRCNAME, __func__, m_mailitem);
568 /* Insert placeholder */
569 char *placeholder_buf;
570 if (gpgrt_asprintf (&placeholder_buf, opt.prefer_html ? decrypt_template_html :
572 is_smime() ? "S/MIME" : "OpenPGP",
573 _("Encrypted message"),
574 _("Please wait while the message is being decrypted / verified...")) == -1)
576 log_error ("%s:%s: Failed to format placeholder.",
583 if (put_oom_string (m_mailitem, "HTMLBody", placeholder_buf))
585 log_error ("%s:%s: Failed to modify html body of item.",
591 if (put_oom_string (m_mailitem, "Body", placeholder_buf))
593 log_error ("%s:%s: Failed to modify body of item.",
597 xfree (placeholder_buf);
599 /* Do the actual parsing */
600 auto cipherstream = get_attachment_stream (m_mailitem, m_moss_position);
604 log_debug ("%s:%s: Failed to get cipherstream.",
609 m_parser = std::shared_ptr <ParseController> (new ParseController (cipherstream, m_type));
610 gpgol_release (cipherstream);
612 HANDLE parser_thread = CreateThread (NULL, 0, do_parsing, (LPVOID) this, 0,
617 log_error ("%s:%s: Failed to create decrypt / verify thread.",
620 CloseHandle (parser_thread);
628 const auto error = m_parser->get_formatted_error ();
633 if (put_oom_string (m_mailitem, "HTMLBody",
636 log_error ("%s:%s: Failed to modify html body of item.",
642 if (put_oom_string (m_mailitem, "Body",
645 log_error ("%s:%s: Failed to modify html body of item.",
651 const auto html = m_parser->get_html_body();
652 if (opt.prefer_html && !html.empty())
654 char *converted = ansi_charset_to_utf8 (m_parser->get_html_charset().c_str(),
655 html.c_str(), html.size());
656 int ret = put_oom_string (m_mailitem, "HTMLBody", converted ? converted : "");
660 log_error ("%s:%s: Failed to modify html body of item.",
665 const auto body = m_parser->get_body();
666 char *converted = ansi_charset_to_utf8 (m_parser->get_body_charset().c_str(),
667 body.c_str(), body.size());
668 int ret = put_oom_string (m_mailitem, "Body", converted ? converted : "");
672 log_error ("%s:%s: Failed to modify body of item.",
682 log_oom_extra ("Mail %p Parsing done for parser: %p",
683 this, m_parser.get());
686 /* This should not happen but it happens when outlook
687 sends multiple ItemLoad events for the same Mail
688 Object. In that case it could happen that one
689 parser was already done while a second is now
690 returning for the wrong mail (as it's looked up
693 We have a check in get_uuid that the uuid was
694 not in the map before (and the parser is replaced).
695 So this really really should not happen. We
696 handle it anyway as we crash otherwise.
698 It should not happen because the parser is only
699 created in decrypt_verify which is called in the
700 read event. And even in there we check if the parser
703 log_error ("%s:%s: No parser obj. For mail: %p",
704 SRCNAME, __func__, this);
707 /* Store the results. */
708 m_decrypt_result = m_parser->decrypt_result ();
709 m_verify_result = m_parser->verify_result ();
712 if (m_decrypt_result.numRecipients())
716 if (m_verify_result.numSignatures())
725 /* Set categories according to the result. */
729 /* Update the body */
733 /* Update attachments */
734 if (add_attachments (m_mailitem, m_parser->get_attachments()))
736 log_error ("%s:%s: Failed to update attachments.",
740 /* Invalidate UI to set the correct sig status. */
742 gpgoladdin_invalidate_ui ();
748 Mail::encrypt_sign ()
752 protocol_t proto = opt.enable_smime ? PROTOCOL_UNKNOWN : PROTOCOL_OPENPGP;
757 LPMESSAGE message = get_oom_base_message (m_mailitem);
760 log_error ("%s:%s: Failed to get base message.",
764 flags = get_gpgol_draft_info_flags (message);
767 log_debug ("%s:%s: Sign / Encrypting message",
769 err = message_sign_encrypt (message, proto,
770 NULL, get_sender ().c_str (), this);
774 err = message_sign (message, proto,
775 NULL, get_sender ().c_str (), this);
779 err = message_encrypt (message, proto,
780 NULL, get_sender ().c_str (), this);
784 log_debug ("%s:%s: Unknown flags for crypto: %i",
785 SRCNAME, __func__, flags);
787 log_debug ("%s:%s: Status: %i",
788 SRCNAME, __func__, err);
789 gpgol_release (message);
790 m_crypt_successful = !err;
795 Mail::needs_crypto ()
797 LPMESSAGE message = get_oom_message (m_mailitem);
801 log_error ("%s:%s: Failed to get message.",
805 ret = get_gpgol_draft_info_flags (message);
806 gpgol_release(message);
817 log_debug ("%s:%s: Removing plaintext from mailitem: %p.",
818 SRCNAME, __func__, m_mailitem);
819 if (put_oom_string (m_mailitem, "HTMLBody",
822 if (put_oom_string (m_mailitem, "Body",
825 log_debug ("%s:%s: Failed to wipe mailitem: %p.",
826 SRCNAME, __func__, m_mailitem);
831 m_needs_wipe = false;
836 Mail::update_oom_data ()
838 LPDISPATCH sender = NULL;
839 log_debug ("%s:%s", SRCNAME, __func__);
841 /* Update the body format. */
842 m_is_html_alternative = get_oom_int (m_mailitem, "BodyFormat") > 1;
844 /* Store the body. It was not obvious for me (aheinecke) how
845 to access this through MAPI. */
846 m_html_body = get_oom_string (m_mailitem, "HTMLBody");
848 /* For some reason outlook may store the recipient address
849 in the send using account field. If we have SMTP we prefer
850 the SenderEmailAddress string. */
852 if (is_crypto_mail ())
854 /* This is the case where we are reading a mail and not composing.
855 When composing we need to use the SendUsingAccount because if
856 you send from the folder of userA but change the from to userB
857 outlook will keep the SenderEmailAddress of UserA. This is all
859 char *type = get_oom_string (m_mailitem, "SenderEmailType");
860 if (type && !strcmp ("SMTP", type))
862 char *senderMail = get_oom_string (m_mailitem, "SenderEmailAddress");
865 m_sender = senderMail;
873 sender = get_oom_object (m_mailitem, "SendUsingAccount");
876 char *buf = get_oom_string (sender, "SmtpAddress");
879 gpgol_release (sender);
882 /* Fallback to Sender object */
883 sender = get_oom_object (m_mailitem, "Sender");
886 char *buf = get_pa_string (sender, PR_SMTP_ADDRESS_DASL);
889 gpgol_release (sender);
892 /* We don't have s sender object or SendUsingAccount,
893 well, in that case fall back to the current user. */
894 sender = get_oom_object (m_mailitem, "Session.CurrentUser");
897 char *buf = get_pa_string (sender, PR_SMTP_ADDRESS_DASL);
900 gpgol_release (sender);
904 log_debug ("%s:%s: All fallbacks failed.",
912 if (m_sender.empty())
918 Mail::close_all_mails ()
921 std::map<LPDISPATCH, Mail *>::iterator it;
923 std::map<LPDISPATCH, Mail *> mail_map_copy = g_mail_map;
924 for (it = mail_map_copy.begin(); it != mail_map_copy.end(); ++it)
926 if (!it->second->is_crypto_mail())
930 if (close_inspector (it->second) || close (it->second))
932 log_error ("Failed to close mail: %p ", it->first);
933 /* Should not happen */
934 if (is_valid_ptr (it->second) && it->second->revert())
943 Mail::revert_all_mails ()
946 std::map<LPDISPATCH, Mail *>::iterator it;
947 for (it = g_mail_map.begin(); it != g_mail_map.end(); ++it)
949 if (it->second->revert ())
951 log_error ("Failed to revert mail: %p ", it->first);
956 it->second->set_needs_save (true);
957 if (!invoke_oom_method (it->first, "Save", NULL))
959 log_error ("Failed to save reverted mail: %p ", it->second);
968 Mail::wipe_all_mails ()
971 std::map<LPDISPATCH, Mail *>::iterator it;
972 for (it = g_mail_map.begin(); it != g_mail_map.end(); ++it)
974 if (it->second->wipe ())
976 log_error ("Failed to wipe mail: %p ", it->first);
992 err = gpgol_mailitem_revert (m_mailitem);
995 log_error ("%s:%s: Message revert failed falling back to wipe.",
999 /* We need to reprocess the mail next time around. */
1000 m_processed = false;
1001 m_needs_wipe = false;
1011 if (m_is_smime_checked)
1016 message = get_oom_message (m_mailitem);
1020 log_error ("%s:%s: No message?",
1025 msgtype = mapi_get_message_type (message);
1026 m_is_smime = msgtype == MSGTYPE_GPGOL_OPAQUE_ENCRYPTED ||
1027 msgtype == MSGTYPE_GPGOL_OPAQUE_SIGNED;
1029 /* Check if it is an smime mail. Multipart signed can
1031 if (!m_is_smime && msgtype == MSGTYPE_GPGOL_MULTIPART_SIGNED)
1034 char *ct = mapi_get_message_content_type (message, &proto, NULL);
1037 m_is_smime = (!strcmp (proto, "application/pkcs7-signature") ||
1038 !strcmp (proto, "application/x-pkcs7-signature"));
1042 log_error ("Protocol in multipart signed mail.");
1047 gpgol_release (message);
1048 m_is_smime_checked = true;
1053 get_string (LPDISPATCH item, const char *str)
1055 char *buf = get_oom_string (item, str);
1057 return std::string();
1058 std::string ret = buf;
1064 Mail::get_subject() const
1066 return get_string (m_mailitem, "Subject");
1070 Mail::get_body() const
1072 return get_string (m_mailitem, "Body");
1076 Mail::get_html_body() const
1078 return get_string (m_mailitem, "HTMLBody");
1082 Mail::get_recipients() const
1084 LPDISPATCH recipients = get_oom_object (m_mailitem, "Recipients");
1090 return get_oom_recipients (recipients);
1094 Mail::close_inspector (Mail *mail)
1096 LPDISPATCH inspector = get_oom_object (mail->item(), "GetInspector");
1101 log_debug ("%s:%s: No inspector.",
1106 dispid = lookup_oom_dispid (inspector, "Close");
1107 if (dispid != DISPID_UNKNOWN)
1109 VARIANT aVariant[1];
1110 DISPPARAMS dispparams;
1112 dispparams.rgvarg = aVariant;
1113 dispparams.rgvarg[0].vt = VT_INT;
1114 dispparams.rgvarg[0].intVal = 1;
1115 dispparams.cArgs = 1;
1116 dispparams.cNamedArgs = 0;
1118 hr = inspector->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
1119 DISPATCH_METHOD, &dispparams,
1123 log_debug ("%s:%s: Failed to close inspector: %#lx",
1124 SRCNAME, __func__, hr);
1133 Mail::close (Mail *mail)
1135 VARIANT aVariant[1];
1136 DISPPARAMS dispparams;
1138 dispparams.rgvarg = aVariant;
1139 dispparams.rgvarg[0].vt = VT_INT;
1140 dispparams.rgvarg[0].intVal = 1;
1141 dispparams.cArgs = 1;
1142 dispparams.cNamedArgs = 0;
1144 log_oom_extra ("%s:%s: Invoking close for: %p",
1145 SRCNAME, __func__, mail->item());
1146 mail->set_close_triggered (true);
1147 int rc = invoke_oom_method_with_parms (mail->item(), "Close",
1150 log_debug ("returned from invoke");
1155 Mail::set_close_triggered (bool value)
1157 m_close_triggered = value;
1161 Mail::get_close_triggered () const
1163 return m_close_triggered;
1167 get_uid_for_sender (const Key k, const char *sender)
1176 if (!k.numUserIDs())
1178 log_debug ("%s:%s: Key without uids",
1183 for (const auto uid: k.userIDs())
1187 log_error ("%s:%s: skipping uid without email.",
1191 auto normalized_uid = uid.addrSpec();
1192 auto normalized_sender = UserID::addrSpecFromString(sender);
1194 if (normalized_sender.empty() || normalized_uid.empty())
1196 log_error ("%s:%s: normalizing '%s' or '%s' failed.",
1197 SRCNAME, __func__, uid.email(), sender);
1200 if (normalized_sender == normalized_uid)
1209 Mail::update_sigstate ()
1211 std::string sender = get_sender();
1215 log_error ("%s:%s:%i", SRCNAME, __func__, __LINE__);
1219 if (m_verify_result.isNull())
1221 log_debug ("%s:%s: No verify result.",
1226 if (m_verify_result.error())
1228 log_debug ("%s:%s: verify error.",
1233 for (const auto sig: m_verify_result.signatures())
1236 m_uid = get_uid_for_sender (sig.key(), sender.c_str());
1237 if (!m_uid.isNull() && sig.validity() != Signature::Validity::Marginal &&
1238 sig.validity() != Signature::Validity::Full &&
1239 sig.validity() != Signature::Validity::Ultimate)
1241 /* For our category we only care about trusted sigs. And
1242 the UID needs to match.*/
1245 if (sig.validity() == Signature::Validity::Marginal)
1247 const auto tofu = m_uid.tofuInfo();
1248 if (tofu.isNull() ||
1249 (tofu.validity() != TofuInfo::Validity::BasicHistory &&
1250 tofu.validity() != TofuInfo::Validity::LargeHistory))
1252 /* Marginal is not good enough without tofu.
1253 We also wait for basic trust. */
1254 log_debug ("%s:%s: Discarding marginal signature."
1255 "With too little history.",
1260 log_debug ("%s:%s: Classified sender as verified",
1267 log_debug ("%s:%s: No signature with enough trust. Using first",
1269 m_sig = m_verify_result.signature(0);
1274 Mail::is_valid_sig ()
1280 Mail::remove_categories ()
1282 const char *decCategory = _("GpgOL: Encrypted Message");
1283 const char *verifyCategory = _("GpgOL: Trusted Sender Address");
1284 remove_category (m_mailitem, decCategory);
1285 remove_category (m_mailitem, verifyCategory);
1289 Mail::update_categories ()
1291 const char *decCategory = _("GpgOL: Encrypted Message");
1292 const char *verifyCategory = _("GpgOL: Trusted Sender Address");
1293 if (m_decrypt_result.numRecipients())
1295 /* We use the number of recipients as we don't care
1296 if decryption was successful or not for this category */
1297 add_category (m_mailitem, decCategory);
1301 /* As a small safeguard against fakes we remove our
1303 remove_category (m_mailitem, decCategory);
1308 add_category (m_mailitem, verifyCategory);
1312 remove_category (m_mailitem, verifyCategory);
1318 Mail::is_signed() const
1320 return m_verify_result.numSignatures() > 0;
1324 Mail::is_encrypted() const
1326 return m_decrypt_result.numRecipients() > 0;
1333 if (!m_uuid.empty())
1335 /* This codepath is reached by decrypt again after a
1336 close with discard changes. The close discarded
1337 the uuid on the OOM object so we have to set
1339 log_debug ("%s:%s: Resetting uuid for %p to %s",
1340 SRCNAME, __func__, this,
1342 uuid = get_unique_id (m_mailitem, 1, m_uuid.c_str());
1346 uuid = get_unique_id (m_mailitem, 1, nullptr);
1347 log_debug ("%s:%s: uuid for %p set to %s",
1348 SRCNAME, __func__, this, uuid);
1353 log_debug ("%s:%s: Failed to get/set uuid for %p",
1354 SRCNAME, __func__, m_mailitem);
1360 Mail *other = get_mail_for_uuid (uuid);
1363 /* According to documentation this should not
1364 happen as this means that multiple ItemLoad
1365 events occured for the same mailobject without
1366 unload / destruction of the mail.
1368 But it happens. If you invalidate the UI
1369 in the selection change event Outlook loads a
1370 new mailobject for the mail. Might happen in
1371 other surprising cases. We replace in that
1372 case as experiments have shown that the last
1373 mailobject is the one that is visible.
1375 Still troubling state so we log this as an error.
1377 log_error ("%s:%s: There is another mail for %p "
1378 "with uuid: %s replacing it.",
1379 SRCNAME, __func__, m_mailitem, uuid);
1382 g_uid_map.insert (std::pair<std::string, Mail *> (m_uuid, this));
1383 log_debug ("%s:%s: uuid for %p is now %s",
1384 SRCNAME, __func__, this,
1391 /* Returns -1 if the mail was signed by a uid with ownertrust
1392 ultimate and to which we have the secret key.
1393 This basically means "mail was signed by yourself"
1395 Returns the number of the signature fromt the uid that belongs
1396 made it ultimately or fully trusted if the mail was signed by some
1397 ultimate key for which we don't have the secret key.
1398 This is direct trust or CA Style PGP.
1402 level_4_check (const UserID &uid)
1404 if (uid.validity() == UserID::Validity::Ultimate)
1406 /* TODO look for the signature that caused this
1407 to be ultimate. And check if it is our own. */
1410 else if (uid.validity() == UserID::Validity::Full)
1418 Mail::get_crypto_summary ()
1420 const int level = get_signature_level ();
1422 bool enc = is_encrypted ();
1423 if (level > 3 && enc)
1425 return _("Highly Secure");
1429 return _("Highly Trustworthy");
1431 if (level >= 2 && enc)
1437 return _("Trustworthy");
1441 return _("Encrypted");
1447 return _("Insecure");
1451 Mail::get_crypto_details()
1453 /* Handle encrypt only */
1454 if (!is_encrypted () && !is_signed ())
1456 return _("You cannot be sure who sent, "
1457 "modified and read the message in transit.");
1459 else if (is_encrypted() && !is_signed ())
1461 return _("You cannot be sure who sent the message as "
1462 "it is not signed.");
1465 std::string message;
1467 bool keyFound = true;
1468 bool isOpenPGP = m_sig.key().protocol() == Protocol::OpenPGP;
1470 bool hasConflict = false;
1471 int level = get_signature_level ();
1473 log_debug ("%s:%s: Formatting sig. Validity: %x Summary: %x Level: %i",
1474 SRCNAME, __func__, m_sig.validity(), m_sig.summary(),
1479 /* level 4 check for direct trust */
1480 int four_check = level_4_check (m_uid);
1482 if (four_check == -1)
1484 message = _("And you signed this message.");
1486 else if (four_check >= 0)
1489 And you certified the identity of the sender.
1490 And <uid with ultimate trust> certified the identity
1496 log_error ("%s:%s:%i BUG: Invalid sigstate.",
1497 SRCNAME, __func__, __LINE__);
1501 else if (level == 3 && isOpenPGP)
1503 /* Level three is only reachable through web of trust and no
1504 direct signature. */
1505 message = _("And the senders identity was certified by several trusted people.");
1507 else if (level == 3 && !isOpenPGP)
1509 /* Level three is the only level for trusted S/MIME keys. */
1510 gpgrt_asprintf (&buf, _("And the senders identity is cerified by the trusted issuer:\n'%s'\n"),
1511 m_sig.key().issuerName());
1515 else if (level == 2)
1517 /* Only reachable through TOFU Trust. */
1518 if (m_uid.tofuInfo ().isNull ())
1520 log_error ("%s:%s:%i BUG: Invalid sigstate.",
1521 SRCNAME, __func__, __LINE__);
1525 unsigned long first_contact = std::max (m_uid.tofuInfo().signFirst(),
1526 m_uid.tofuInfo().encrFirst());
1527 char *time = format_date_from_gpgme (first_contact);
1528 /* i18n note signcount is always pulral because with signcount 1 we
1529 * would not be in this branch. */
1530 gpgrt_asprintf (&buf, _("And the senders address is trustworthy, because "
1531 "you have established a communication history "
1532 "with this address starting on %s.\n"
1533 "You encrypted %i times to this address and verified %i since."),
1534 time, m_uid.tofuInfo().signCount (),
1535 m_uid.tofuInfo().encrCount());
1540 else if (level == 1)
1542 /* This could be marginal trust through pgp, or tofu with little
1544 if (m_uid.tofuInfo ().validity() == TofuInfo::Validity::LittleHistory)
1546 unsigned long first_contact = std::max (m_uid.tofuInfo().signFirst(),
1547 m_uid.tofuInfo().encrFirst());
1548 char *time = format_date_from_gpgme (first_contact);
1549 gpgrt_asprintf (&buf, _("But the senders address is not trustworthy yet because "
1550 "you only verified %i messages and encrypted %i messages to "
1552 m_uid.tofuInfo().signCount (),
1553 m_uid.tofuInfo().encrCount (), time);
1558 else if (m_uid.tofuInfo ().signCount() == 1)
1560 message += _("But the senders signature was verified for the first time.");
1564 /* Marginal trust through pgp */
1565 message = _("Not enough trusted people or yourself "
1566 "have certified the senders identity.");
1571 /* Now we are in level 0, this could be a technical problem, no key
1573 message = _("But the sender address is not trustworthy because:");
1575 keyFound = !(m_sig.summary() & Signature::Summary::KeyMissing);
1577 bool general_problem = true;
1578 /* First the general stuff. */
1579 if (m_sig.summary() & Signature::Summary::Red)
1581 message += _("The signature is invalid.\n");
1583 else if (m_sig.summary() & Signature::Summary::SysError ||
1584 m_verify_result.numSignatures() < 1)
1586 message += _("There was an error verifying the signature.\n");
1588 else if (m_sig.summary() & Signature::Summary::SigExpired)
1590 message += _("The signature is expired.\n");
1594 message += isOpenPGP ? _("The used key") : _("The used certificate");
1596 general_problem = false;
1599 /* Now the key problems */
1600 if ((m_sig.summary() & Signature::Summary::KeyMissing))
1602 message += _("is not available.");
1604 else if ((m_sig.summary() & Signature::Summary::KeyRevoked))
1606 message += _("is revoked.");
1608 else if ((m_sig.summary() & Signature::Summary::KeyExpired))
1610 message += _("is expired.");
1612 else if ((m_sig.summary() & Signature::Summary::BadPolicy))
1614 message += _("is not meant for signing.");
1616 else if ((m_sig.summary() & Signature::Summary::CrlMissing))
1618 message += _("could not be checked for revocation.");
1620 else if ((m_sig.summary() & Signature::Summary::CrlTooOld))
1622 message += _("could not be checked for revocation.");
1624 else if ((m_sig.summary() & Signature::Summary::TofuConflict) ||
1625 m_uid.tofuInfo().validity() == TofuInfo::Conflict)
1627 message += _("conflicts with another key that was used in the past by the sender.");
1630 else if (m_uid.isNull())
1632 gpgrt_asprintf (&buf, _("does not claim the address: \"%s\"."),
1633 get_sender().c_str());
1637 else if (((m_sig.validity() & Signature::Validity::Undefined) ||
1638 (m_sig.validity() & Signature::Validity::Unknown) ||
1639 (m_sig.summary() == Signature::Summary::None) ||
1640 (m_sig.validity() == 0))&& !general_problem)
1642 /* Bit of a catch all for weird results. */
1643 message += _("is not certified by any trustworthy key.");
1645 else if ((m_sig.validity() & Signature::Validity::Never))
1647 message += _("is marked as not trustworthy.");
1650 message += _("You cannot be sure who wrote or modified the message.");
1654 message += _("Click here to change the key used for this address.");
1658 message += isOpenPGP ? _("Click here for details about the key.") :
1659 _("Click here for details about the certificate.");
1663 message += isOpenPGP ? _("Click here to search the key on the configured keyserver.") :
1664 _("Click here to search the certificate on the configured X509 keyserver.");
1671 Mail::get_signature_level () const
1673 if (!m_is_signed || !is_encrypted ())
1678 if (m_uid.isNull ())
1680 /* No m_uid matches our sender. */
1683 if (m_is_valid && (m_uid.validity () == UserID::Validity::Ultimate ||
1684 (m_uid.validity () == UserID::Validity::Full &&
1685 level_4_check (m_uid))))
1689 if (m_is_valid && m_uid.validity () == UserID::Validity::Full)
1697 if (m_sig.validity() == Signature::Validity::Marginal)
1701 if (m_sig.summary() & Signature::Summary::TofuConflict ||
1702 m_uid.tofuInfo().validity() == TofuInfo::Conflict)
1710 Mail::get_crypto_icon_id () const
1712 int level = get_signature_level ();
1713 int offset = is_encrypted () ? ENCRYPT_ICON_OFFSET : 0;
1714 return IDI_LEVEL_0 + level + offset;
1718 Mail::get_sig_fpr() const
1720 if (!m_is_signed || m_sig.isNull())
1724 return m_sig.fingerprint();
1729 do_locate (LPVOID arg)
1731 char *recipient = (char*) arg;
1732 log_debug ("%s:%s searching key for recipient: \"%s\"",
1733 SRCNAME, __func__, recipient);
1734 Context *ctx = Context::createForProtocol (OpenPGP);
1742 ctx->setKeyListMode (GpgME::Extern | GpgME::Local);
1743 ctx->startKeyListing (recipient, false);
1745 std::vector<Key> keys;
1748 keys.push_back (ctx->nextKey(err));
1751 ctx->endKeyListing ();
1756 log_debug ("%s:%s found key for recipient: \"%s\"",
1757 SRCNAME, __func__, recipient);
1760 do_in_ui_thread (UNKNOWN, NULL);
1764 /** Try to locate the keys for all recipients */
1765 void Mail::locate_keys()
1767 char ** recipients = get_recipients ();
1774 for (int i = 0; recipients[i]; i++)
1776 std::string recp = recipients[i];
1777 if (uids_searched.find (recp) == uids_searched.end ())
1779 uids_searched.insert (recp);
1780 HANDLE thread = CreateThread (NULL, 0, do_locate,
1781 (LPVOID) strdup(recipients[i]), 0,
1783 CloseHandle (thread);
1785 xfree (recipients[i]);
1791 Mail::is_html_alternative () const
1793 return m_is_html_alternative;
1797 Mail::get_cached_html_body () const
1803 Mail::get_crypto_flags () const
1805 return m_crypto_flags;
1809 Mail::set_needs_encrypt (bool value)
1811 m_needs_encrypt = value;
1815 Mail::needs_encrypt() const
1817 return m_needs_encrypt;