> attachments)
{
int err = 0;
for (auto att: attachments)
{
if (att->get_display_name().empty())
{
log_error ("%s:%s: Ignoring attachment without display name.",
SRCNAME, __func__);
continue;
}
wchar_t* wchar_name = utf8_to_wchar (att->get_display_name().c_str());
HANDLE hFile;
wchar_t* wchar_file = get_tmp_outfile (wchar_name,
&hFile);
if (copy_attachment_to_file (att, hFile))
{
log_error ("%s:%s: Failed to copy attachment %s to temp file",
SRCNAME, __func__, att->get_display_name().c_str());
err = 1;
}
if (add_oom_attachment (mail, wchar_file, wchar_name))
{
log_error ("%s:%s: Failed to add attachment: %s",
SRCNAME, __func__, att->get_display_name().c_str());
err = 1;
}
CloseHandle (hFile);
if (!DeleteFileW (wchar_file))
{
log_error ("%s:%s: Failed to delete tmp attachment for: %s",
SRCNAME, __func__, att->get_display_name().c_str());
err = 1;
}
xfree (wchar_file);
xfree (wchar_name);
err = fixup_last_attachment (mail, att);
}
return err;
}
GPGRT_LOCK_DEFINE(parser_lock);
static DWORD WINAPI
do_parsing (LPVOID arg)
{
gpgrt_lock_lock (&dtor_lock);
/* We lock with mail dtors so we can be sure the mail->parser
call is valid. */
Mail *mail = (Mail *)arg;
if (!Mail::is_valid_ptr (mail))
{
log_debug ("%s:%s: canceling parsing for: %p already deleted",
SRCNAME, __func__, arg);
gpgrt_lock_unlock (&dtor_lock);
return 0;
}
/* This takes a shared ptr of parser. So the parser is
still valid when the mail is deleted. */
auto parser = mail->parser();
gpgrt_lock_unlock (&dtor_lock);
gpgrt_lock_lock (&parser_lock);
gpgrt_lock_lock (&invalidate_lock);
/* We lock the parser here to avoid too many
decryption attempts if there are
multiple mailobjects which might have already
been deleted (e.g. by quick switches of the mailview.)
Let's rather be a bit slower.
*/
log_debug ("%s:%s: preparing the parser for: %p",
SRCNAME, __func__, arg);
if (!Mail::is_valid_ptr (mail))
{
log_debug ("%s:%s: cancel for: %p already deleted",
SRCNAME, __func__, arg);
gpgrt_lock_unlock (&invalidate_lock);
gpgrt_lock_unlock (&parser_lock);
return 0;
}
if (!parser)
{
log_error ("%s:%s: no parser found for mail: %p",
SRCNAME, __func__, arg);
gpgrt_lock_unlock (&invalidate_lock);
gpgrt_lock_unlock (&parser_lock);
return -1;
}
parser->parse();
do_in_ui_thread (PARSING_DONE, arg);
gpgrt_lock_unlock (&invalidate_lock);
gpgrt_lock_unlock (&parser_lock);
return 0;
}
/* How encryption is done:
There are two modes of encryption. Synchronous and Async.
If async is used depends on the value of mail->async_crypt_disabled.
Synchronous crypto:
> Send Event < | State NoCryptMail
Needs Crypto ? (get_gpgol_draft_info_flags != 0)
-> No:
Pass send -> unencrypted mail.
-> Yes:
mail->update_oom_data
State = Mail::NeedsFirstAfterWrite
check_inline_response
invoke_oom_method (m_object, "Save", NULL);
> Write Event <
Pass because is_crypto_mail is false (not a decrypted mail)
> AfterWrite Event < | State NeedsFirstAfterWrite
State = NeedsActualCrypo
encrypt_sign_start
collect_input_data
-> Check if Inline PGP should be used
do_crypt
-> Resolve keys / do crypto
State = NeedsUpdateInMAPI
update_crypt_mapi
crypter->update_mail_mapi
if (inline) (Meaning PGP/Inline)
<-- do nothing.
else
build MSOXSMIME attachment and clear body / attachments.
State = NeedsUpdateInOOM
<- Back to Send Event
update_crypt_oom
-> Cleans body or sets PGP/Inline body. (inline_body_to_body)
State = WantsSendMIME or WantsSendInline
-> Saftey check "has_crypted_or_empty_body"
-> If MIME Mail do the T3656 check.
Send.
State order for "inline_response" (sync) Mails.
NoCryptMail
NeedsFirstAfterWrite
NeedsActualCrypto
NeedsUpdateInMAPI
NeedsUpdateInOOM
WantsSendMIME (or inline for PGP Inline)
-> Send.
State order for async Mails
NoCryptMail
NeedsFirstAfterWrite
NeedsActualCrypto
-> Cancel Send.
Windowmessages -> Crypto Done
NeedsUpdateInOOM
NeedsSecondAfterWrite
trigger Save.
NeedsUpdateInMAPI
WantsSendMIME
trigger Send.
*/
static DWORD WINAPI
do_crypt (LPVOID arg)
{
gpgrt_lock_lock (&dtor_lock);
/* We lock with mail dtors so we can be sure the mail->parser
call is valid. */
Mail *mail = (Mail *)arg;
if (!Mail::is_valid_ptr (mail))
{
log_debug ("%s:%s: canceling crypt for: %p already deleted",
SRCNAME, __func__, arg);
gpgrt_lock_unlock (&dtor_lock);
return 0;
}
if (mail->crypt_state() != Mail::NeedsActualCrypt)
{
log_debug ("%s:%s: invalid state %i",
SRCNAME, __func__, mail->crypt_state ());
mail->set_window_enabled (true);
gpgrt_lock_unlock (&dtor_lock);
return -1;
}
/* This takes a shared ptr of crypter. So the crypter is
still valid when the mail is deleted. */
auto crypter = mail->crypter();
gpgrt_lock_unlock (&dtor_lock);
if (!crypter)
{
log_error ("%s:%s: no crypter found for mail: %p",
SRCNAME, __func__, arg);
gpgrt_lock_unlock (&parser_lock);
mail->set_window_enabled (true);
return -1;
}
GpgME::Error err;
int rc = crypter->do_crypto(err);
gpgrt_lock_lock (&dtor_lock);
if (!Mail::is_valid_ptr (mail))
{
log_debug ("%s:%s: aborting crypt for: %p already deleted",
SRCNAME, __func__, arg);
gpgrt_lock_unlock (&dtor_lock);
return 0;
}
mail->set_window_enabled (true);
if (rc == -1 || err)
{
mail->reset_crypter ();
crypter = nullptr;
if (err)
{
char *buf = nullptr;
gpgrt_asprintf (&buf, _("Crypto operation failed:\n%s"),
err.asString());
gpgol_message_box (mail->get_window(), buf, _("GpgOL"), MB_OK);
xfree (buf);
}
else
{
gpgol_bug (mail->get_window (),
ERR_CRYPT_RESOLVER_FAILED);
}
}
if (rc || err.isCanceled())
{
log_debug ("%s:%s: crypto failed for: %p with: %i err: %i",
SRCNAME, __func__, arg, rc, err.code());
mail->set_crypt_state (Mail::NoCryptMail);
mail->reset_crypter ();
crypter = nullptr;
gpgrt_lock_unlock (&dtor_lock);
return rc;
}
if (!mail->async_crypt_disabled ())
{
mail->set_crypt_state (Mail::NeedsUpdateInOOM);
gpgrt_lock_unlock (&dtor_lock);
// This deletes the Mail in Outlook 2010
do_in_ui_thread (CRYPTO_DONE, arg);
log_debug ("%s:%s: UI thread finished for %p",
SRCNAME, __func__, arg);
}
else
{
mail->set_crypt_state (Mail::NeedsUpdateInMAPI);
mail->update_crypt_mapi ();
if (mail->crypt_state () == Mail::WantsSendMIME)
{
// For sync crypto we need to switch this.
mail->set_crypt_state (Mail::NeedsUpdateInOOM);
}
else
{
// A bug!
log_debug ("%s:%s: Resetting crypter because of state mismatch. %p",
SRCNAME, __func__, arg);
crypter = nullptr;
mail->reset_crypter ();
}
gpgrt_lock_unlock (&dtor_lock);
}
/* This works around a bug in pinentry that it might
bring the wrong window to front. So after encryption /
signing we bring outlook back to front.
See GnuPG-Bug-Id: T3732
*/
do_in_ui_thread_async (BRING_TO_FRONT, nullptr);
log_debug ("%s:%s: crypto thread for %p finished",
SRCNAME, __func__, arg);
return 0;
}
bool
Mail::is_crypto_mail() const
{
if (m_type == MSGTYPE_UNKNOWN || m_type == MSGTYPE_GPGOL ||
m_type == MSGTYPE_SMIME)
{
/* Not a message for us. */
return false;
}
return true;
}
int
Mail::decrypt_verify()
{
if (!is_crypto_mail())
{
log_debug ("%s:%s: Decrypt Verify for non crypto mail: %p.",
SRCNAME, __func__, m_mailitem);
return 0;
}
if (m_needs_wipe)
{
log_error ("%s:%s: Decrypt verify called for msg that needs wipe: %p",
SRCNAME, __func__, m_mailitem);
return 1;
}
set_uuid ();
m_processed = true;
/* Insert placeholder */
char *placeholder_buf;
if (m_type == MSGTYPE_GPGOL_WKS_CONFIRMATION)
{
gpgrt_asprintf (&placeholder_buf, opt.prefer_html ? decrypt_template_html :
decrypt_template,
"OpenPGP",
_("Pubkey directory confirmation"),
_("This is a confirmation request to publish your Pubkey in the "
"directory for your domain.\n\n"
"If you did not request to publish your Pubkey in your providers "
"directory, simply ignore this message.
\n"));
}
else if (gpgrt_asprintf (&placeholder_buf, opt.prefer_html ? decrypt_template_html :
decrypt_template,
is_smime() ? "S/MIME" : "OpenPGP",
_("message"),
_("Please wait while the message is being decrypted / verified...")) == -1)
{
log_error ("%s:%s: Failed to format placeholder.",
SRCNAME, __func__);
return 1;
}
if (opt.prefer_html)
{
m_orig_body = get_oom_string (m_mailitem, "HTMLBody");
if (put_oom_string (m_mailitem, "HTMLBody", placeholder_buf))
{
log_error ("%s:%s: Failed to modify html body of item.",
SRCNAME, __func__);
}
}
else
{
m_orig_body = get_oom_string (m_mailitem, "Body");
if (put_oom_string (m_mailitem, "Body", placeholder_buf))
{
log_error ("%s:%s: Failed to modify body of item.",
SRCNAME, __func__);
}
}
xfree (placeholder_buf);
/* Do the actual parsing */
auto cipherstream = get_attachment_stream (m_mailitem, m_moss_position);
if (m_type == MSGTYPE_GPGOL_WKS_CONFIRMATION)
{
WKSHelper::instance ()->handle_confirmation_read (this, cipherstream);
return 0;
}
if (!cipherstream)
{
log_debug ("%s:%s: Failed to get cipherstream.",
SRCNAME, __func__);
return 1;
}
m_parser = std::shared_ptr (new ParseController (cipherstream, m_type));
m_parser->setSender(GpgME::UserID::addrSpecFromString(get_sender().c_str()));
log_mime_parser ("%s:%s: Parser for \"%s\" is %p",
SRCNAME, __func__, get_subject ().c_str(), m_parser.get());
gpgol_release (cipherstream);
HANDLE parser_thread = CreateThread (NULL, 0, do_parsing, (LPVOID) this, 0,
NULL);
if (!parser_thread)
{
log_error ("%s:%s: Failed to create decrypt / verify thread.",
SRCNAME, __func__);
}
CloseHandle (parser_thread);
return 0;
}
void find_and_replace(std::string& source, const std::string &find,
const std::string &replace)
{
for(std::string::size_type i = 0; (i = source.find(find, i)) != std::string::npos;)
{
source.replace(i, find.length(), replace);
i += replace.length();
}
}
void
Mail::update_body()
{
if (!m_parser)
{
TRACEPOINT;
return;
}
const auto error = m_parser->get_formatted_error ();
if (!error.empty())
{
if (opt.prefer_html)
{
if (put_oom_string (m_mailitem, "HTMLBody",
error.c_str ()))
{
log_error ("%s:%s: Failed to modify html body of item.",
SRCNAME, __func__);
}
else
{
log_debug ("%s:%s: Set error html to: '%s'",
SRCNAME, __func__, error.c_str ());
}
}
else
{
if (put_oom_string (m_mailitem, "Body",
error.c_str ()))
{
log_error ("%s:%s: Failed to modify html body of item.",
SRCNAME, __func__);
}
else
{
log_debug ("%s:%s: Set error plain to: '%s'",
SRCNAME, __func__, error.c_str ());
}
}
return;
}
if (m_verify_result.error())
{
log_error ("%s:%s: Verification failed. Restoring Body.",
SRCNAME, __func__);
if (opt.prefer_html)
{
if (put_oom_string (m_mailitem, "HTMLBody", m_orig_body.c_str ()))
{
log_error ("%s:%s: Failed to modify html body of item.",
SRCNAME, __func__);
}
}
else
{
if (put_oom_string (m_mailitem, "Body", m_orig_body.c_str ()))
{
log_error ("%s:%s: Failed to modify html body of item.",
SRCNAME, __func__);
}
}
return;
}
// No need to carry body anymore
m_orig_body = std::string();
auto html = m_parser->get_html_body ();
/** Outlook does not show newlines if \r\r\n is a newline. We replace
these as apparently some other buggy MUA sends this. */
find_and_replace (html, "\r\r\n", "\r\n");
- if (opt.prefer_html && !html.empty())
+ if (opt.prefer_html && !html.empty() && !m_block_html)
{
char *converted = ansi_charset_to_utf8 (m_parser->get_html_charset().c_str(),
html.c_str(), html.size());
int ret = put_oom_string (m_mailitem, "HTMLBody", converted ? converted : "");
xfree (converted);
if (ret)
{
log_error ("%s:%s: Failed to modify html body of item.",
SRCNAME, __func__);
}
+
return;
}
auto body = m_parser->get_body ();
+
+ if (body.empty () && m_block_html && !html.empty())
+ {
+#if 0
+ Sadly the following code still offers to load external references
+ it might also be too dangerous if Outlook somehow autoloads the
+ references as soon as the Body is put into HTML
+
+
+ // Fallback to show HTML as plaintext if HTML display
+ // is blocked.
+ log_error ("%s:%s: No text body. Putting HTML into plaintext.",
+ SRCNAME, __func__);
+
+ char *converted = ansi_charset_to_utf8 (m_parser->get_html_charset().c_str(),
+ html.c_str(), html.size());
+ int ret = put_oom_string (m_mailitem, "HTMLBody", converted ? converted : "");
+ xfree (converted);
+ if (ret)
+ {
+ log_error ("%s:%s: Failed to modify html body of item.",
+ SRCNAME, __func__);
+ body = html;
+ }
+ else
+ {
+ char *plainBody = get_oom_string (m_mailitem, "Body");
+
+ if (!plainBody)
+ {
+ log_error ("%s:%s: Failed to obtain converted plain body.",
+ SRCNAME, __func__);
+ body = html;
+ }
+ else
+ {
+ ret = put_oom_string (m_mailitem, "HTMLBody", plainBody);
+ xfree (plainBody);
+ if (ret)
+ {
+ log_error ("%s:%s: Failed to put plain into html body of item.",
+ SRCNAME, __func__);
+ body = html;
+ }
+ else
+ {
+ return;
+ }
+ }
+ }
+#endif
+ body = html;
+ std::string buf = _("HTML display disabled.");
+ buf += "\n\n";
+ buf += _("For security reasons HTML content in unsigned, encrypted\n"
+ "S/MIME mails cannot be displayed.\n\n"
+ "Please ask the sender to sign the message or to send it as plain text.");
+
+ gpgol_message_box (get_window(), buf.c_str() , _("GpgOL"), MB_OK);
+ }
+
find_and_replace (body, "\r\r\n", "\r\n");
char *converted = ansi_charset_to_utf8 (m_parser->get_body_charset().c_str(),
body.c_str(), body.size());
int ret = put_oom_string (m_mailitem, "Body", converted ? converted : "");
xfree (converted);
if (ret)
{
log_error ("%s:%s: Failed to modify body of item.",
SRCNAME, __func__);
}
return;
}
void
Mail::parsing_done()
{
TRACEPOINT;
log_oom_extra ("Mail %p Parsing done for parser: %p",
this, m_parser.get());
if (!m_parser)
{
/* This should not happen but it happens when outlook
sends multiple ItemLoad events for the same Mail
Object. In that case it could happen that one
parser was already done while a second is now
returning for the wrong mail (as it's looked up
by uuid.)
We have a check in get_uuid that the uuid was
not in the map before (and the parser is replaced).
So this really really should not happen. We
handle it anyway as we crash otherwise.
It should not happen because the parser is only
created in decrypt_verify which is called in the
read event. And even in there we check if the parser
was set.
*/
log_error ("%s:%s: No parser obj. For mail: %p",
SRCNAME, __func__, this);
return;
}
/* Store the results. */
m_decrypt_result = m_parser->decrypt_result ();
m_verify_result = m_parser->verify_result ();
m_crypto_flags = 0;
if (!m_decrypt_result.isNull())
{
m_crypto_flags |= 1;
}
if (m_verify_result.numSignatures())
{
m_crypto_flags |= 2;
}
update_sigstate ();
m_needs_wipe = !m_is_send_again;
TRACEPOINT;
/* Set categories according to the result. */
- update_categories();
+ update_categories ();
+
+ TRACEPOINT;
+ m_block_html = m_parser->shouldBlockHtml ();
+
+ if (m_block_html)
+ {
+ // Just to be careful.
+ set_block_status ();
+ }
TRACEPOINT;
/* Update the body */
update_body();
TRACEPOINT;
/* Check that there are no unsigned / unencrypted messages. */
check_attachments ();
/* Update attachments */
if (add_attachments (m_mailitem, m_parser->get_attachments()))
{
log_error ("%s:%s: Failed to update attachments.",
SRCNAME, __func__);
}
if (m_is_send_again)
{
log_debug ("%s:%s: I think that this is the send again of a crypto mail.",
SRCNAME, __func__);
/* We no longer want to be treated like a crypto mail. */
m_type = MSGTYPE_UNKNOWN;
LPMESSAGE msg = get_oom_base_message (m_mailitem);
if (!msg)
{
TRACEPOINT;
}
else
{
set_gpgol_draft_info_flags (msg, m_crypto_flags);
gpgol_release (msg);
}
remove_our_attachments ();
}
log_debug ("%s:%s: Delayed invalidate to update sigstate.",
SRCNAME, __func__);
CloseHandle(CreateThread (NULL, 0, delayed_invalidate_ui, (LPVOID) this, 0,
NULL));
TRACEPOINT;
return;
}
int
Mail::encrypt_sign_start ()
{
if (m_crypt_state != NeedsActualCrypt)
{
log_debug ("%s:%s: invalid state %i",
SRCNAME, __func__, m_crypt_state);
return -1;
}
int flags = 0;
if (!needs_crypto())
{
return 0;
}
LPMESSAGE message = get_oom_base_message (m_mailitem);
if (!message)
{
log_error ("%s:%s: Failed to get base message.",
SRCNAME, __func__);
return -1;
}
flags = get_gpgol_draft_info_flags (message);
gpgol_release (message);
const auto window = get_active_hwnd ();
if (m_is_gsuite)
{
auto att_table = mapi_create_attach_table (message, 0);
int n_att_usable = count_usable_attachments (att_table);
mapi_release_attach_table (att_table);
/* Check for attachments if we have some abort. */
if (n_att_usable)
{
wchar_t *w_title = utf8_to_wchar (_(
"GpgOL: Oops, G Suite Sync account detected"));
wchar_t *msg = utf8_to_wchar (
_("G Suite Sync breaks outgoing crypto mails "
"with attachments.\nUsing crypto and attachments "
"with G Suite Sync is not supported.\n\n"
"See: https://dev.gnupg.org/T3545 for details."));
MessageBoxW (window,
msg,
w_title,
MB_ICONINFORMATION|MB_OK);
xfree (msg);
xfree (w_title);
return -1;
}
}
m_do_inline = m_is_gsuite ? true : opt.inline_pgp;
GpgME::Protocol proto = opt.enable_smime ? GpgME::UnknownProtocol: GpgME::OpenPGP;
m_crypter = std::shared_ptr (new CryptController (this, flags & 1,
flags & 2,
proto));
// Careful from here on we have to check every
// error condition with window enabling again.
set_window_enabled (false);
if (m_crypter->collect_data ())
{
log_error ("%s:%s: Crypter for mail %p failed to collect data.",
SRCNAME, __func__, this);
set_window_enabled (true);
return -1;
}
if (!m_async_crypt_disabled)
{
CloseHandle(CreateThread (NULL, 0, do_crypt,
(LPVOID) this, 0,
NULL));
}
else
{
do_crypt (this);
}
return 0;
}
int
Mail::needs_crypto ()
{
LPMESSAGE message = get_oom_message (m_mailitem);
bool ret;
if (!message)
{
log_error ("%s:%s: Failed to get message.",
SRCNAME, __func__);
return false;
}
ret = get_gpgol_draft_info_flags (message);
gpgol_release(message);
return ret;
}
int
Mail::wipe (bool force)
{
if (!m_needs_wipe && !force)
{
return 0;
}
log_debug ("%s:%s: Removing plaintext from mailitem: %p.",
SRCNAME, __func__, m_mailitem);
if (put_oom_string (m_mailitem, "HTMLBody",
""))
{
if (put_oom_string (m_mailitem, "Body",
""))
{
log_debug ("%s:%s: Failed to wipe mailitem: %p.",
SRCNAME, __func__, m_mailitem);
return -1;
}
return -1;
}
else
{
put_oom_string (m_mailitem, "Body", "");
}
m_needs_wipe = false;
return 0;
}
int
Mail::update_oom_data ()
{
char *buf = nullptr;
log_debug ("%s:%s", SRCNAME, __func__);
if (!is_crypto_mail())
{
/* Update the body format. */
m_is_html_alternative = get_oom_int (m_mailitem, "BodyFormat") > 1;
/* Store the body. It was not obvious for me (aheinecke) how
to access this through MAPI. */
if (m_is_html_alternative)
{
log_debug ("%s:%s: Is html alternative mail.", SRCNAME, __func__);
xfree (m_cached_html_body);
m_cached_html_body = get_oom_string (m_mailitem, "HTMLBody");
}
xfree (m_cached_plain_body);
m_cached_plain_body = get_oom_string (m_mailitem, "Body");
release_cArray (m_cached_recipients);
m_cached_recipients = get_recipients ();
}
/* For some reason outlook may store the recipient address
in the send using account field. If we have SMTP we prefer
the SenderEmailAddress string. */
if (is_crypto_mail ())
{
/* This is the case where we are reading a mail and not composing.
When composing we need to use the SendUsingAccount because if
you send from the folder of userA but change the from to userB
outlook will keep the SenderEmailAddress of UserA. This is all
so horrible. */
buf = get_sender_SenderEMailAddress (m_mailitem);
if (!buf)
{
/* Try the sender Object */
buf = get_sender_Sender (m_mailitem);
}
}
if (!buf)
{
buf = get_sender_SendUsingAccount (m_mailitem, &m_is_gsuite);
}
if (!buf && !is_crypto_mail ())
{
/* Try the sender Object */
buf = get_sender_Sender (m_mailitem);
}
if (!buf)
{
/* We don't have s sender object or SendUsingAccount,
well, in that case fall back to the current user. */
buf = get_sender_CurrentUser (m_mailitem);
}
if (!buf)
{
log_debug ("%s:%s: All fallbacks failed.",
SRCNAME, __func__);
return -1;
}
m_sender = buf;
xfree (buf);
return 0;
}
std::string
Mail::get_sender ()
{
if (m_sender.empty())
update_oom_data();
return m_sender;
}
std::string
Mail::get_cached_sender ()
{
return m_sender;
}
int
Mail::close_all_mails ()
{
int err = 0;
std::map::iterator it;
TRACEPOINT;
std::map mail_map_copy = s_mail_map;
for (it = mail_map_copy.begin(); it != mail_map_copy.end(); ++it)
{
/* XXX For non racy code the is_valid_ptr check should not
be necessary but we crashed sometimes closing a destroyed
mail. */
if (!is_valid_ptr (it->second))
{
log_debug ("%s:%s: Already deleted mail for %p",
SRCNAME, __func__, it->first);
continue;
}
if (!it->second->is_crypto_mail())
{
continue;
}
if (close_inspector (it->second) || close (it->second))
{
log_error ("Failed to close mail: %p ", it->first);
/* Should not happen */
if (is_valid_ptr (it->second) && it->second->revert())
{
err++;
}
}
}
return err;
}
int
Mail::revert_all_mails ()
{
int err = 0;
std::map::iterator it;
for (it = s_mail_map.begin(); it != s_mail_map.end(); ++it)
{
if (it->second->revert ())
{
log_error ("Failed to revert mail: %p ", it->first);
err++;
continue;
}
it->second->set_needs_save (true);
if (!invoke_oom_method (it->first, "Save", NULL))
{
log_error ("Failed to save reverted mail: %p ", it->second);
err++;
continue;
}
}
return err;
}
int
Mail::wipe_all_mails ()
{
int err = 0;
std::map::iterator it;
for (it = s_mail_map.begin(); it != s_mail_map.end(); ++it)
{
if (it->second->wipe ())
{
log_error ("Failed to wipe mail: %p ", it->first);
err++;
}
}
return err;
}
int
Mail::revert ()
{
int err = 0;
if (!m_processed)
{
return 0;
}
m_disable_att_remove_warning = true;
err = gpgol_mailitem_revert (m_mailitem);
if (err == -1)
{
log_error ("%s:%s: Message revert failed falling back to wipe.",
SRCNAME, __func__);
return wipe ();
}
/* We need to reprocess the mail next time around. */
m_processed = false;
m_needs_wipe = false;
m_disable_att_remove_warning = false;
return 0;
}
bool
Mail::is_smime ()
{
msgtype_t msgtype;
LPMESSAGE message;
if (m_is_smime_checked)
{
return m_is_smime;
}
message = get_oom_message (m_mailitem);
if (!message)
{
log_error ("%s:%s: No message?",
SRCNAME, __func__);
return false;
}
msgtype = mapi_get_message_type (message);
m_is_smime = msgtype == MSGTYPE_GPGOL_OPAQUE_ENCRYPTED ||
msgtype == MSGTYPE_GPGOL_OPAQUE_SIGNED;
/* Check if it is an smime mail. Multipart signed can
also be true. */
if (!m_is_smime && msgtype == MSGTYPE_GPGOL_MULTIPART_SIGNED)
{
char *proto;
char *ct = mapi_get_message_content_type (message, &proto, NULL);
if (ct && proto)
{
m_is_smime = (!strcmp (proto, "application/pkcs7-signature") ||
!strcmp (proto, "application/x-pkcs7-signature"));
}
else
{
log_error ("%s:%s: No protocol in multipart / signed mail.",
SRCNAME, __func__);
}
xfree (proto);
xfree (ct);
}
gpgol_release (message);
m_is_smime_checked = true;
return m_is_smime;
}
static std::string
get_string (LPDISPATCH item, const char *str)
{
char *buf = get_oom_string (item, str);
if (!buf)
return std::string();
std::string ret = buf;
xfree (buf);
return ret;
}
std::string
Mail::get_subject() const
{
return get_string (m_mailitem, "Subject");
}
std::string
Mail::get_body() const
{
return get_string (m_mailitem, "Body");
}
std::string
Mail::get_html_body() const
{
return get_string (m_mailitem, "HTMLBody");
}
char **
Mail::get_recipients() const
{
LPDISPATCH recipients = get_oom_object (m_mailitem, "Recipients");
if (!recipients)
{
TRACEPOINT;
return nullptr;
}
auto ret = get_oom_recipients (recipients);
gpgol_release (recipients);
return ret;
}
int
Mail::close_inspector (Mail *mail)
{
LPDISPATCH inspector = get_oom_object (mail->item(), "GetInspector");
HRESULT hr;
DISPID dispid;
if (!inspector)
{
log_debug ("%s:%s: No inspector.",
SRCNAME, __func__);
return -1;
}
dispid = lookup_oom_dispid (inspector, "Close");
if (dispid != DISPID_UNKNOWN)
{
VARIANT aVariant[1];
DISPPARAMS dispparams;
dispparams.rgvarg = aVariant;
dispparams.rgvarg[0].vt = VT_INT;
dispparams.rgvarg[0].intVal = 1;
dispparams.cArgs = 1;
dispparams.cNamedArgs = 0;
hr = inspector->Invoke (dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT,
DISPATCH_METHOD, &dispparams,
NULL, NULL, NULL);
if (hr != S_OK)
{
log_debug ("%s:%s: Failed to close inspector: %#lx",
SRCNAME, __func__, hr);
gpgol_release (inspector);
return -1;
}
}
gpgol_release (inspector);
return 0;
}
/* static */
int
Mail::close (Mail *mail)
{
VARIANT aVariant[1];
DISPPARAMS dispparams;
dispparams.rgvarg = aVariant;
dispparams.rgvarg[0].vt = VT_INT;
dispparams.rgvarg[0].intVal = 1;
dispparams.cArgs = 1;
dispparams.cNamedArgs = 0;
log_oom_extra ("%s:%s: Invoking close for: %p",
SRCNAME, __func__, mail->item());
mail->set_close_triggered (true);
int rc = invoke_oom_method_with_parms (mail->item(), "Close",
NULL, &dispparams);
log_oom_extra ("%s:%s: Returned from close",
SRCNAME, __func__);
return rc;
}
void
Mail::set_close_triggered (bool value)
{
m_close_triggered = value;
}
bool
Mail::get_close_triggered () const
{
return m_close_triggered;
}
static const UserID
get_uid_for_sender (const Key &k, const char *sender)
{
UserID ret;
if (!sender)
{
return ret;
}
if (!k.numUserIDs())
{
log_debug ("%s:%s: Key without uids",
SRCNAME, __func__);
return ret;
}
for (const auto uid: k.userIDs())
{
if (!uid.email())
{
log_error ("%s:%s: skipping uid without email.",
SRCNAME, __func__);
continue;
}
auto normalized_uid = uid.addrSpec();
auto normalized_sender = UserID::addrSpecFromString(sender);
if (normalized_sender.empty() || normalized_uid.empty())
{
log_error ("%s:%s: normalizing '%s' or '%s' failed.",
SRCNAME, __func__, uid.email(), sender);
continue;
}
if (normalized_sender == normalized_uid)
{
ret = uid;
}
}
return ret;
}
void
Mail::update_sigstate ()
{
std::string sender = get_sender();
if (sender.empty())
{
log_error ("%s:%s:%i", SRCNAME, __func__, __LINE__);
return;
}
if (m_verify_result.isNull())
{
log_debug ("%s:%s: No verify result.",
SRCNAME, __func__);
return;
}
if (m_verify_result.error())
{
log_debug ("%s:%s: verify error.",
SRCNAME, __func__);
return;
}
for (const auto sig: m_verify_result.signatures())
{
m_is_signed = true;
m_uid = get_uid_for_sender (sig.key(), sender.c_str());
if (m_uid.isNull() || (sig.validity() != Signature::Validity::Marginal &&
sig.validity() != Signature::Validity::Full &&
sig.validity() != Signature::Validity::Ultimate))
{
/* For our category we only care about trusted sigs. And
the UID needs to match.*/
continue;
}
if (sig.validity() == Signature::Validity::Marginal)
{
const auto tofu = m_uid.tofuInfo();
if (!tofu.isNull() &&
(tofu.validity() != TofuInfo::Validity::BasicHistory &&
tofu.validity() != TofuInfo::Validity::LargeHistory))
{
/* Marginal is only good enough without tofu.
Otherwise we wait for basic trust. */
log_debug ("%s:%s: Discarding marginal signature."
"With too little history.",
SRCNAME, __func__);
continue;
}
}
log_debug ("%s:%s: Classified sender as verified uid validity: %i",
SRCNAME, __func__, m_uid.validity());
m_sig = sig;
m_is_valid = true;
return;
}
log_debug ("%s:%s: No signature with enough trust. Using first",
SRCNAME, __func__);
m_sig = m_verify_result.signature(0);
return;
}
bool
Mail::is_valid_sig ()
{
return m_is_valid;
}
void
Mail::remove_categories ()
{
const char *decCategory = _("GpgOL: Encrypted Message");
const char *verifyCategory = _("GpgOL: Trusted Sender Address");
remove_category (m_mailitem, decCategory);
remove_category (m_mailitem, verifyCategory);
}
/* Now for some tasty hack: Outlook sometimes does
not show the new categories properly but instead
does some weird scrollbar thing. This can be
avoided by resizing the message a bit. But somehow
this only needs to be done once.
Weird isn't it? But as this workaround worked let's
do it programatically. Fun. Wan't some tomato sauce
with this hack? */
static void
resize_active_window ()
{
HWND wnd = get_active_hwnd ();
static std::vector resized_windows;
if(std::find(resized_windows.begin(), resized_windows.end(), wnd) != resized_windows.end()) {
/* We only need to do this once per window. XXX But sometimes we also
need to do this once per view of the explorer. So for now this might
break but we reduce the flicker. A better solution would be to find
the current view and track that. */
return;
}
if (!wnd)
{
TRACEPOINT;
return;
}
RECT oldpos;
if (!GetWindowRect (wnd, &oldpos))
{
TRACEPOINT;
return;
}
if (!SetWindowPos (wnd, nullptr,
(int)oldpos.left,
(int)oldpos.top,
/* Anything smaller then 19 was ignored when the window was
* maximized on Windows 10 at least with a 1980*1024
* resolution. So I assume it's at least 1 percent.
* This is all hackish and ugly but should work for 90%...
* hopefully.
*/
(int)oldpos.right - oldpos.left - 20,
(int)oldpos.bottom - oldpos.top, 0))
{
TRACEPOINT;
return;
}
if (!SetWindowPos (wnd, nullptr,
(int)oldpos.left,
(int)oldpos.top,
(int)oldpos.right - oldpos.left,
(int)oldpos.bottom - oldpos.top, 0))
{
TRACEPOINT;
return;
}
resized_windows.push_back(wnd);
}
void
Mail::update_categories ()
{
const char *decCategory = _("GpgOL: Encrypted Message");
const char *verifyCategory = _("GpgOL: Trusted Sender Address");
if (is_valid_sig())
{
add_category (m_mailitem, verifyCategory);
}
else
{
remove_category (m_mailitem, verifyCategory);
}
if (!m_decrypt_result.isNull())
{
add_category (m_mailitem, decCategory);
}
else
{
/* As a small safeguard against fakes we remove our
categories */
remove_category (m_mailitem, decCategory);
}
resize_active_window();
return;
}
bool
Mail::is_signed() const
{
return m_verify_result.numSignatures() > 0;
}
bool
Mail::is_encrypted() const
{
return !m_decrypt_result.isNull();
}
int
Mail::set_uuid()
{
char *uuid;
if (!m_uuid.empty())
{
/* This codepath is reached by decrypt again after a
close with discard changes. The close discarded
the uuid on the OOM object so we have to set
it again. */
log_debug ("%s:%s: Resetting uuid for %p to %s",
SRCNAME, __func__, this,
m_uuid.c_str());
uuid = get_unique_id (m_mailitem, 1, m_uuid.c_str());
}
else
{
uuid = get_unique_id (m_mailitem, 1, nullptr);
log_debug ("%s:%s: uuid for %p set to %s",
SRCNAME, __func__, this, uuid);
}
if (!uuid)
{
log_debug ("%s:%s: Failed to get/set uuid for %p",
SRCNAME, __func__, m_mailitem);
return -1;
}
if (m_uuid.empty())
{
m_uuid = uuid;
Mail *other = get_mail_for_uuid (uuid);
if (other)
{
/* According to documentation this should not
happen as this means that multiple ItemLoad
events occured for the same mailobject without
unload / destruction of the mail.
But it happens. If you invalidate the UI
in the selection change event Outlook loads a
new mailobject for the mail. Might happen in
other surprising cases. We replace in that
case as experiments have shown that the last
mailobject is the one that is visible.
Still troubling state so we log this as an error.
*/
log_error ("%s:%s: There is another mail for %p "
"with uuid: %s replacing it.",
SRCNAME, __func__, m_mailitem, uuid);
delete other;
}
s_uid_map.insert (std::pair (m_uuid, this));
log_debug ("%s:%s: uuid for %p is now %s",
SRCNAME, __func__, this,
m_uuid.c_str());
}
xfree (uuid);
return 0;
}
/* Returns 2 if the userid is ultimately trusted.
Returns 1 if the userid is fully trusted but has
a signature by a key for which we have a secret
and which is ultimately trusted. (Direct trust)
0 otherwise */
static int
level_4_check (const UserID &uid)
{
if (uid.isNull())
{
return 0;
}
if (uid.validity () == UserID::Validity::Ultimate)
{
return 2;
}
if (uid.validity () == UserID::Validity::Full)
{
for (const auto sig: uid.signatures ())
{
const char *sigID = sig.signerKeyID ();
if (sig.isNull() || !sigID)
{
/* should not happen */
TRACEPOINT;
continue;
}
/* Direct trust information is not available
through gnupg so we cached the keys with ultimate
trust during parsing and now see if we find a direct
trust path.*/
for (const auto secKey: ParseController::get_ultimate_keys ())
{
/* Check that the Key id of the key matches */
const char *secKeyID = secKey.keyID ();
if (!secKeyID || strcmp (secKeyID, sigID))
{
continue;
}
/* Check that the userID of the signature is the ultimately
trusted one. */
const char *sig_uid_str = sig.signerUserID();
if (!sig_uid_str)
{
/* should not happen */
TRACEPOINT;
continue;
}
for (const auto signer_uid: secKey.userIDs ())
{
if (signer_uid.validity() != UserID::Validity::Ultimate)
{
TRACEPOINT;
continue;
}
const char *signer_uid_str = signer_uid.id ();
if (!sig_uid_str)
{
/* should not happen */
TRACEPOINT;
continue;
}
if (!strcmp(sig_uid_str, signer_uid_str))
{
/* We have a match */
log_debug ("%s:%s: classified %s as ultimate because "
"it was signed by uid %s of key %s",
SRCNAME, __func__, signer_uid_str, sig_uid_str,
secKeyID);
return 1;
}
}
}
}
}
return 0;
}
std::string
Mail::get_crypto_summary ()
{
const int level = get_signature_level ();
bool enc = is_encrypted ();
if (level == 4 && enc)
{
return _("Security Level 4");
}
if (level == 4)
{
return _("Trust Level 4");
}
if (level == 3 && enc)
{
return _("Security Level 3");
}
if (level == 3)
{
return _("Trust Level 3");
}
if (level == 2 && enc)
{
return _("Security Level 2");
}
if (level == 2)
{
return _("Trust Level 2");
}
if (enc)
{
return _("Encrypted");
}
if (is_signed ())
{
/* Even if it is signed, if it is not validly
signed it's still completly insecure as anyone
could have signed this. So we avoid the label
"signed" here as this word already implies some
security. */
return _("Insecure");
}
return _("Insecure");
}
std::string
Mail::get_crypto_one_line()
{
bool sig = is_signed ();
bool enc = is_encrypted ();
if (sig || enc)
{
if (sig && enc)
{
return _("Signed and encrypted message");
}
else if (sig)
{
return _("Signed message");
}
else if (enc)
{
return _("Encrypted message");
}
}
return _("Insecure message");
}
std::string
Mail::get_crypto_details()
{
std::string message;
/* No signature with keys but error */
if (!is_encrypted() && !is_signed () && m_verify_result.error())
{
message = _("You cannot be sure who sent, "
"modified and read the message in transit.");
message += "\n\n";
message += _("The message was signed but the verification failed with:");
message += "\n";
message += m_verify_result.error().asString();
return message;
}
/* No crypo, what are we doing here? */
if (!is_encrypted () && !is_signed ())
{
return _("You cannot be sure who sent, "
"modified and read the message in transit.");
}
/* Handle encrypt only */
if (is_encrypted() && !is_signed ())
{
if (in_de_vs_mode ())
{
if (m_sig.isDeVs())
{
message += _("The encryption was VS-NfD-compliant.");
}
else
{
message += _("The encryption was not VS-NfD-compliant.");
}
}
message += "\n\n";
message += _("You cannot be sure who sent the message because "
"it is not signed.");
return message;
}
bool keyFound = true;
bool isOpenPGP = m_sig.key().isNull() ? !is_smime() :
m_sig.key().protocol() == Protocol::OpenPGP;
char *buf;
bool hasConflict = false;
int level = get_signature_level ();
log_debug ("%s:%s: Formatting sig. Validity: %x Summary: %x Level: %i",
SRCNAME, __func__, m_sig.validity(), m_sig.summary(),
level);
if (level == 4)
{
/* level 4 check for direct trust */
int four_check = level_4_check (m_uid);
if (four_check == 2 && m_sig.key().hasSecret ())
{
message = _("You signed this message.");
}
else if (four_check == 1)
{
message = _("The senders identity was certified by yourself.");
}
else if (four_check == 2)
{
message = _("The sender is allowed to certify identities for you.");
}
else
{
log_error ("%s:%s:%i BUG: Invalid sigstate.",
SRCNAME, __func__, __LINE__);
return message;
}
}
else if (level == 3 && isOpenPGP)
{
/* Level three is only reachable through web of trust and no
direct signature. */
message = _("The senders identity was certified by several trusted people.");
}
else if (level == 3 && !isOpenPGP)
{
/* Level three is the only level for trusted S/MIME keys. */
gpgrt_asprintf (&buf, _("The senders identity is certified by the trusted issuer:\n'%s'\n"),
m_sig.key().issuerName());
message = buf;
xfree (buf);
}
else if (level == 2 && m_uid.tofuInfo ().isNull ())
{
/* Marginal trust through pgp only */
message = _("Some trusted people "
"have certified the senders identity.");
}
else if (level == 2)
{
unsigned long first_contact = std::max (m_uid.tofuInfo().signFirst(),
m_uid.tofuInfo().encrFirst());
char *time = format_date_from_gpgme (first_contact);
/* i18n note signcount is always pulral because with signcount 1 we
* would not be in this branch. */
gpgrt_asprintf (&buf, _("The senders address is trusted, because "
"you have established a communication history "
"with this address starting on %s.\n"
"You encrypted %i and verified %i messages since."),
time, m_uid.tofuInfo().encrCount(),
m_uid.tofuInfo().signCount ());
xfree (time);
message = buf;
xfree (buf);
}
else if (level == 1)
{
/* This could be marginal trust through pgp, or tofu with little
history. */
if (m_uid.tofuInfo ().signCount() == 1)
{
message += _("The senders signature was verified for the first time.");
}
else if (m_uid.tofuInfo ().validity() == TofuInfo::Validity::LittleHistory)
{
unsigned long first_contact = std::max (m_uid.tofuInfo().signFirst(),
m_uid.tofuInfo().encrFirst());
char *time = format_date_from_gpgme (first_contact);
gpgrt_asprintf (&buf, _("The senders address is not trustworthy yet because "
"you only verified %i messages and encrypted %i messages to "
"it since %s."),
m_uid.tofuInfo().signCount (),
m_uid.tofuInfo().encrCount (), time);
xfree (time);
message = buf;
xfree (buf);
}
}
else
{
/* Now we are in level 0, this could be a technical problem, no key
or just unkown. */
message = is_encrypted () ? _("But the sender address is not trustworthy because:") :
_("The sender address is not trustworthy because:");
message += "\n";
keyFound = !(m_sig.summary() & Signature::Summary::KeyMissing);
bool general_problem = true;
/* First the general stuff. */
if (m_sig.summary() & Signature::Summary::Red)
{
message += _("The signature is invalid: \n");
}
else if (m_sig.summary() & Signature::Summary::SysError ||
m_verify_result.numSignatures() < 1)
{
message += _("There was an error verifying the signature.\n");
}
else if (m_sig.summary() & Signature::Summary::SigExpired)
{
message += _("The signature is expired.\n");
}
else
{
message += isOpenPGP ? _("The used key") : _("The used certificate");
message += " ";
general_problem = false;
}
/* Now the key problems */
if ((m_sig.summary() & Signature::Summary::KeyMissing))
{
message += _("is not available.");
}
else if ((m_sig.summary() & Signature::Summary::KeyRevoked))
{
message += _("is revoked.");
}
else if ((m_sig.summary() & Signature::Summary::KeyExpired))
{
message += _("is expired.");
}
else if ((m_sig.summary() & Signature::Summary::BadPolicy))
{
message += _("is not meant for signing.");
}
else if ((m_sig.summary() & Signature::Summary::CrlMissing))
{
message += _("could not be checked for revocation.");
}
else if ((m_sig.summary() & Signature::Summary::CrlTooOld))
{
message += _("could not be checked for revocation.");
}
else if ((m_sig.summary() & Signature::Summary::TofuConflict) ||
m_uid.tofuInfo().validity() == TofuInfo::Conflict)
{
message += _("is not the same as the key that was used "
"for this address in the past.");
hasConflict = true;
}
else if (m_uid.isNull())
{
gpgrt_asprintf (&buf, _("does not claim the address: \"%s\"."),
get_sender().c_str());
message += buf;
xfree (buf);
}
else if (((m_sig.validity() & Signature::Validity::Undefined) ||
(m_sig.validity() & Signature::Validity::Unknown) ||
(m_sig.summary() == Signature::Summary::None) ||
(m_sig.validity() == 0))&& !general_problem)
{
/* Bit of a catch all for weird results. */
if (isOpenPGP)
{
message += _("is not certified by any trustworthy key.");
}
else
{
message += _("is not certified by a trustworthy Certificate Authority or the Certificate Authority is unknown.");
}
}
else if (m_uid.isRevoked())
{
message += _("The sender marked this address as revoked.");
}
else if ((m_sig.validity() & Signature::Validity::Never))
{
message += _("is marked as not trustworthy.");
}
}
message += "\n\n";
if (in_de_vs_mode ())
{
if (is_signed ())
{
if (m_sig.isDeVs ())
{
message += _("The signature is VS-NfD-compliant.");
}
else
{
message += _("The signature is not VS-NfD-compliant.");
}
message += "\n";
}
if (is_encrypted ())
{
if (m_decrypt_result.isDeVs ())
{
message += _("The encryption is VS-NfD-compliant.");
}
else
{
message += _("The encryption is not VS-NfD-compliant.");
}
message += "\n\n";
}
else
{
message += "\n";
}
}
if (hasConflict)
{
message += _("Click here to change the key used for this address.");
}
else if (keyFound)
{
message += isOpenPGP ? _("Click here for details about the key.") :
_("Click here for details about the certificate.");
}
else
{
message += isOpenPGP ? _("Click here to search the key on the configured keyserver.") :
_("Click here to search the certificate on the configured X509 keyserver.");
}
return message;
}
int
Mail::get_signature_level () const
{
if (!m_is_signed)
{
return 0;
}
if (m_uid.isNull ())
{
/* No m_uid matches our sender. */
return 0;
}
if (m_is_valid && (m_uid.validity () == UserID::Validity::Ultimate ||
(m_uid.validity () == UserID::Validity::Full &&
level_4_check (m_uid))) && (!in_de_vs_mode () || m_sig.isDeVs()))
{
return 4;
}
if (m_is_valid && m_uid.validity () == UserID::Validity::Full &&
(!in_de_vs_mode () || m_sig.isDeVs()))
{
return 3;
}
if (m_is_valid)
{
return 2;
}
if (m_sig.validity() == Signature::Validity::Marginal)
{
return 1;
}
if (m_sig.summary() & Signature::Summary::TofuConflict ||
m_uid.tofuInfo().validity() == TofuInfo::Conflict)
{
return 0;
}
return 0;
}
int
Mail::get_crypto_icon_id () const
{
int level = get_signature_level ();
int offset = is_encrypted () ? ENCRYPT_ICON_OFFSET : 0;
return IDI_LEVEL_0 + level + offset;
}
const char*
Mail::get_sig_fpr() const
{
if (!m_is_signed || m_sig.isNull())
{
return nullptr;
}
return m_sig.fingerprint();
}
/** Try to locate the keys for all recipients */
void
Mail::locate_keys()
{
static bool locate_in_progress;
if (locate_in_progress)
{
/** XXX
The strangest thing seems to happen here:
In get_recipients the lookup for "AddressEntry" on
an unresolved address might cause network traffic.
So Outlook somehow "detaches" this call and keeps
processing window messages while the call is running.
So our do_delayed_locate might trigger a second locate.
If we access the OOM in this call while we access the
same object in the blocked "detached" call we crash.
(T3931)
After the window message is handled outlook retunrs
in the original lookup.
A better fix here might be a non recursive lock
of the OOM. But I expect that if we lock the handling
of the Windowmessage we might deadlock.
*/
log_debug ("%s:%s: Locate for %p already in progress.",
SRCNAME, __func__, this);
return;
}
locate_in_progress = true;
// First update oom data to have recipients and sender updated.
update_oom_data ();
char ** recipients = take_cached_recipients ();
KeyCache::instance()->startLocateSecret (get_sender ().c_str ());
KeyCache::instance()->startLocate (get_sender ().c_str ());
KeyCache::instance()->startLocate (recipients);
release_cArray (recipients);
locate_in_progress = false;
}
bool
Mail::is_html_alternative () const
{
return m_is_html_alternative;
}
char *
Mail::take_cached_html_body ()
{
char *ret = m_cached_html_body;
m_cached_html_body = nullptr;
return ret;
}
char *
Mail::take_cached_plain_body ()
{
char *ret = m_cached_plain_body;
m_cached_plain_body = nullptr;
return ret;
}
int
Mail::get_crypto_flags () const
{
return m_crypto_flags;
}
void
Mail::set_needs_encrypt (bool value)
{
m_needs_encrypt = value;
}
bool
Mail::needs_encrypt() const
{
return m_needs_encrypt;
}
char **
Mail::take_cached_recipients()
{
char **ret = m_cached_recipients;
m_cached_recipients = nullptr;
return ret;
}
void
Mail::append_to_inline_body (const std::string &data)
{
m_inline_body += data;
}
int
Mail::inline_body_to_body()
{
if (!m_crypter)
{
log_error ("%s:%s: No crypter.",
SRCNAME, __func__);
return -1;
}
const auto body = m_crypter->get_inline_data ();
if (body.empty())
{
return 0;
}
int ret = put_oom_string (m_mailitem, "Body",
body.c_str ());
return ret;
}
void
Mail::update_crypt_mapi()
{
log_debug ("%s:%s: Update crypt mapi",
SRCNAME, __func__);
if (m_crypt_state != NeedsUpdateInMAPI)
{
log_debug ("%s:%s: invalid state %i",
SRCNAME, __func__, m_crypt_state);
return;
}
if (!m_crypter)
{
if (!m_mime_data.empty())
{
log_debug ("%s:%s: Have override mime data creating dummy crypter",
SRCNAME, __func__);
m_crypter = std::shared_ptr (new CryptController (this, false,
false,
GpgME::UnknownProtocol));
}
else
{
log_error ("%s:%s: No crypter.",
SRCNAME, __func__);
m_crypt_state = NoCryptMail;
return;
}
}
if (m_crypter->update_mail_mapi ())
{
log_error ("%s:%s: Failed to update MAPI after crypt",
SRCNAME, __func__);
m_crypt_state = NoCryptMail;
}
else
{
m_crypt_state = WantsSendMIME;
}
/** If sync we need the crypter in update_crypt_oom */
if (!async_crypt_disabled ())
{
// We don't need the crypter anymore.
reset_crypter ();
}
}
/** Checks in OOM if the body is either
empty or contains the -----BEGIN tag.
pair.first -> true if body starts with -----BEGIN
pair.second -> true if body is empty. */
static std::pair
has_crypt_or_empty_body_oom (Mail *mail)
{
auto body = mail->get_body();
std::pair ret;
ret.first = false;
ret.second = false;
ltrim (body);
if (body.size() > 10 && !strncmp (body.c_str(), "-----BEGIN", 10))
{
ret.first = true;
return ret;
}
if (!body.size())
{
ret.second = true;
}
else
{
log_mime_parser ("%s:%s: Body found in %p : \"%s\"",
SRCNAME, __func__, mail, body.c_str ());
}
return ret;
}
void
Mail::update_crypt_oom()
{
log_debug ("%s:%s: Update crypt oom for %p",
SRCNAME, __func__, this);
if (m_crypt_state != NeedsUpdateInOOM)
{
log_debug ("%s:%s: invalid state %i",
SRCNAME, __func__, m_crypt_state);
reset_crypter ();
return;
}
if (do_pgp_inline ())
{
if (inline_body_to_body ())
{
log_error ("%s:%s: Inline body to body failed %p.",
SRCNAME, __func__, this);
gpgol_bug (get_active_hwnd(), ERR_INLINE_BODY_TO_BODY);
m_crypt_state = NoCryptMail;
return;
}
}
if (m_crypter->get_protocol () == GpgME::CMS && m_crypter->is_encrypter ())
{
/* We put the PIDNameContentType headers here for exchange
because this is the only way we found to inject the
smime-type. */
if (put_pa_string (m_mailitem,
PR_PIDNameContentType_DASL,
"application/pkcs7-mime;smime-type=\"enveloped-data\";name=smime.p7m"))
{
log_debug ("%s:%s: Failed to put PIDNameContentType for %p.",
SRCNAME, __func__, this);
}
}
/** When doing async update_crypt_mapi follows and needs
the crypter. */
if (async_crypt_disabled ())
{
reset_crypter ();
}
const auto pair = has_crypt_or_empty_body_oom (this);
if (pair.first)
{
log_debug ("%s:%s: Looks like inline body. You can pass %p.",
SRCNAME, __func__, this);
m_crypt_state = WantsSendInline;
return;
}
// We are in MIME land. Wipe the body.
if (wipe (true))
{
log_debug ("%s:%s: Cancel send for %p.",
SRCNAME, __func__, this);
wchar_t *title = utf8_to_wchar (_("GpgOL: Encryption not possible!"));
wchar_t *msg = utf8_to_wchar (_(
"Outlook returned an error when trying to send the encrypted mail.\n\n"
"Please restart Outlook and try again.\n\n"
"If it still fails consider using an encrypted attachment or\n"
"switching to PGP/Inline in GpgOL's options."));
MessageBoxW (get_active_hwnd(), msg, title,
MB_ICONERROR | MB_OK);
xfree (msg);
xfree (title);
m_crypt_state = NoCryptMail;
return;
}
m_crypt_state = NeedsSecondAfterWrite;
return;
}
void
Mail::set_window_enabled (bool value)
{
if (!value)
{
m_window = get_active_hwnd ();
}
log_debug ("%s:%s: enable window %p %i",
SRCNAME, __func__, m_window, value);
EnableWindow (m_window, value ? TRUE : FALSE);
}
bool
Mail::check_inline_response ()
{
/* Async sending might lead to crashes when the send invocation is done.
* For now we treat every mail as an inline response to disable async
* encryption. :-( For more details see: T3838 */
#ifdef DO_ASYNC_CRYPTO
m_async_crypt_disabled = false;
LPDISPATCH app = GpgolAddin::get_instance ()->get_application ();
if (!app)
{
TRACEPOINT;
return false;
}
LPDISPATCH explorer = get_oom_object (app, "ActiveExplorer");
if (!explorer)
{
TRACEPOINT;
return false;
}
LPDISPATCH inlineResponse = get_oom_object (explorer, "ActiveInlineResponse");
gpgol_release (explorer);
if (!inlineResponse)
{
return false;
}
// We have inline response
// Check if we are it. It's a bit naive but meh. Worst case
// is that we think inline response too often and do sync
// crypt where we could do async crypt.
char * inlineSubject = get_oom_string (inlineResponse, "Subject");
gpgol_release (inlineResponse);
const auto subject = get_subject ();
if (inlineResponse && !subject.empty() && !strcmp (subject.c_str (), inlineSubject))
{
log_debug ("%s:%s: Detected inline response for '%p'",
SRCNAME, __func__, this);
m_async_crypt_disabled = true;
}
xfree (inlineSubject);
#else
m_async_crypt_disabled = true;
#endif
return m_async_crypt_disabled;
}
// static
Mail *
Mail::get_last_mail ()
{
if (!s_last_mail || !is_valid_ptr (s_last_mail))
{
s_last_mail = nullptr;
}
return s_last_mail;
}
// static
void
Mail::invalidate_last_mail ()
{
s_last_mail = nullptr;
}
// static
void
Mail::locate_all_crypto_recipients()
{
if (!opt.autoresolve)
{
return;
}
std::map::iterator it;
for (it = s_mail_map.begin(); it != s_mail_map.end(); ++it)
{
if (it->second->needs_crypto ())
{
it->second->locate_keys ();
}
}
}
int
Mail::remove_our_attachments ()
{
LPDISPATCH attachments = get_oom_object (m_mailitem, "Attachments");
if (!attachments)
{
TRACEPOINT;
return 0;
}
int count = get_oom_int (attachments, "Count");
LPDISPATCH to_delete[count];
int del_cnt = 0;
for (int i = 1; i <= count; i++)
{
auto item_str = std::string("Item(") + std::to_string (i) + ")";
LPDISPATCH attachment = get_oom_object (attachments, item_str.c_str());
if (!attachment)
{
TRACEPOINT;
continue;
}
attachtype_t att_type;
if (get_pa_int (attachment, GPGOL_ATTACHTYPE_DASL, (int*) &att_type))
{
/* Not our attachment. */
gpgol_release (attachment);
continue;
}
if (att_type == ATTACHTYPE_PGPBODY || att_type == ATTACHTYPE_MOSS ||
att_type == ATTACHTYPE_MOSSTEMPL)
{
/* One of ours to delete. */
to_delete[del_cnt++] = attachment;
/* Dont' release yet */
continue;
}
gpgol_release (attachment);
}
gpgol_release (attachments);
int ret = 0;
for (int i = 0; i < del_cnt; i++)
{
LPDISPATCH attachment = to_delete[i];
/* Delete the attachments that are marked to delete */
if (invoke_oom_method (attachment, "Delete", NULL))
{
log_error ("%s:%s: Error: deleting attachment %i",
SRCNAME, __func__, i);
ret = -1;
}
gpgol_release (attachment);
}
return ret;
}
/* We are very verbose because if we fail it might mean
that we have leaked plaintext -> critical. */
bool
Mail::has_crypted_or_empty_body ()
{
const auto pair = has_crypt_or_empty_body_oom (this);
if (pair.first /* encrypted marker */)
{
log_debug ("%s:%s: Crypt Marker detected in OOM body. Return true %p.",
SRCNAME, __func__, this);
return true;
}
if (!pair.second)
{
log_debug ("%s:%s: Unexpected content detected. Return false %p.",
SRCNAME, __func__, this);
return false;
}
// Pair second == true (is empty) can happen on OOM error.
LPMESSAGE message = get_oom_base_message (m_mailitem);
if (!message && pair.second)
{
if (message)
{
gpgol_release (message);
}
return true;
}
size_t r_nbytes = 0;
char *mapi_body = mapi_get_body (message, &r_nbytes);
gpgol_release (message);
if (!mapi_body || !r_nbytes)
{
// Body or bytes are null. we are empty.
xfree (mapi_body);
log_debug ("%s:%s: MAPI error or empty message. Return true. %p.",
SRCNAME, __func__, this);
return true;
}
if (r_nbytes > 10 && !strncmp (mapi_body, "-----BEGIN", 10))
{
// Body is crypt.
log_debug ("%s:%s: MAPI Crypt marker detected. Return true. %p.",
SRCNAME, __func__, this);
xfree (mapi_body);
return true;
}
xfree (mapi_body);
log_debug ("%s:%s: Found mapi body. Return false. %p.",
SRCNAME, __func__, this);
return false;
}
std::string
Mail::get_verification_result_dump()
{
std::stringstream ss;
ss << m_verify_result;
return ss.str();
}
+
+void
+Mail::set_block_status()
+{
+ SPropValue prop;
+
+ LPMESSAGE message = get_oom_base_message (m_mailitem);
+
+ prop.ulPropTag = PR_BLOCK_STATUS;
+ prop.Value.l = 1;
+ HRESULT hr = message->SetProps (1, &prop, NULL);
+
+ if (hr)
+ {
+ log_error ("%s:%s: can't set block value: hr=%#lx\n",
+ SRCNAME, __func__, hr);
+ }
+
+ gpgol_release (message);
+ return;
+}
+
+void
+Mail::set_block_html(bool value)
+{
+ m_block_html = value;
+}
diff --git a/src/mail.h b/src/mail.h
index 677ac7b..20e737a 100644
--- a/src/mail.h
+++ b/src/mail.h
@@ -1,533 +1,541 @@
/* @file mail.h
* @brief High level class to work with Outlook Mailitems.
*
* Copyright (C) 2015, 2016 by Bundesamt für Sicherheit in der Informationstechnik
* Software engineering by Intevation GmbH
*
* This file is part of GpgOL.
*
* GpgOL is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* GpgOL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, see .
*/
#ifndef MAIL_H
#define MAIL_H
#include "oomhelp.h"
#include "mapihelp.h"
#include "gpgme++/verificationresult.h"
#include "gpgme++/decryptionresult.h"
#include "gpgme++/key.h"
#include
#include
class ParseController;
class CryptController;
/** @brief Data wrapper around a mailitem.
*
* This class is intended to bundle all that we know about
* a Mail. Due to the restrictions in Outlook we sometimes may
* need additional information that is not available at the time
* like the sender address of an exchange account in the afterWrite
* event.
*
* This class bundles such information and also provides a way to
* access the event handler of a mail.
*/
class Mail
{
public:
enum CryptState
{
NoCryptMail,
NeedsFirstAfterWrite,
NeedsActualCrypt,
NeedsUpdateInOOM,
NeedsSecondAfterWrite,
NeedsUpdateInMAPI,
WantsSendInline,
WantsSendMIME,
};
/** @brief Construct a mail object for the item.
*
* This also installs the event sink for this item.
*
* The mail object takes ownership of the mailitem
* reference. Do not Release it! */
Mail (LPDISPATCH mailitem);
~Mail ();
/** @brief looks for existing Mail objects for the OOM mailitem.
@returns A reference to an existing mailitem or NULL in case none
could be found.
*/
static Mail* get_mail_for_item (LPDISPATCH mailitem);
/** @brief looks for existing Mail objects in the uuid map.
Only objects for which set_uid has been called can be found
in the uid map. Get the Unique ID of a mailitem thorugh get_unique_id
@returns A reference to an existing mailitem or NULL in case none
could be found.
*/
static Mail* get_mail_for_uuid (const char *uuid);
/** @brief Get the last created mail.
@returns A reference to the last created mail or null.
*/
static Mail* get_last_mail ();
static void invalidate_last_mail ();
/** @brief looks for existing Mail objects.
@returns A reference to an existing mailitem or NULL in case none
could be found. Can be used to check if a mail object was destroyed.
*/
static bool is_valid_ptr (const Mail *mail);
/** @brief wipe the plaintext from all known Mail objects.
*
* This is intended as a "cleanup" call to be done on unload
* to avoid leaking plaintext in case we are deactivated while
* some mails still have their plaintext inserted.
*
* @returns the number of errors that occured.
*/
static int wipe_all_mails ();
/** @brief revert all known Mail objects.
*
* Similar to wipe but works on MAPI to revert our attachment
* dance and restore an original MIME mail.
*
* @returns the number of errors that occured.
*/
static int revert_all_mails ();
/** @brief close all known Mail objects.
*
* Close our mail with discard changes set to true.
* This discards the plaintext / attachments. Afterwards
* it calls save if neccessary to sync back the collected
* property changes.
*
* This is the nicest of our three "Clean plaintext"
* functions. Will fallback to revert if closing fails.
* Closed mails are deleted.
*
* @returns the number of errors that occured.
*/
static int close_all_mails ();
/** @brief locate recipients for all crypto mails
*
* To avoid lookups of recipients for non crypto mails we only
* locate keys when a crypto action is already selected.
*
* As the user can do this after recipients were added but
* we don't know for which mail the crypt button was triggered.
* we march over all mails and if they are crypto mails we check
* that the recipents were located.
*/
static void locate_all_crypto_recipients ();
/** @brief Reference to the mailitem. Do not Release! */
LPDISPATCH item () { return m_mailitem; }
/** @brief Pre process the message. Ususally to be called from BeforeRead.
*
* This function assumes that the base message interface can be accessed
* and calles the MAPI Message handling which changes the message class
* to enable our own handling.
*
* @returns 0 on success.
*/
int pre_process_message ();
/** @brief Decrypt / Verify the mail.
*
* Sets the needs_wipe and was_encrypted variable.
*
* @returns 0 on success. */
int decrypt_verify ();
/** @brief start crypto operations as selected by the user.
*
* Initiates the crypto operations according to the gpgol
* draft info flags.
*
* @returns 0 on success. */
int encrypt_sign_start ();
/** @brief Necessary crypto operations were completed successfully. */
bool crypto_successful () { return !needs_crypto() || m_crypt_successful; }
/** @brief Message should be encrypted and or signed.
0: No
1: Encrypt
2: Sign
3: Encrypt & Sign
*/
int needs_crypto ();
/** @brief wipe the plaintext from the message and encrypt attachments.
*
* @returns 0 on success; */
int wipe (bool force = false);
/** @brief revert the message to the original mail before our changes.
*
* @returns 0 on success; */
int revert ();
/** @brief update some data collected from the oom
*
* This updates cached values from the OOM that are not available
* in MAPI events like after Write.
*
* For Exchange 2013 at least we don't have any other way to get the
* senders SMTP address then through the object model. So we have to
* store the sender address for later events that do not allow us to
* access the OOM but enable us to work with the underlying MAPI structure.
*
* It also updated the is_html_alternative value.
*
* @returns 0 on success */
int update_oom_data ();
/** @brief get sender SMTP address (UTF-8 encoded).
*
* If the sender address has not been set through update_sender this
* calls update_sender before returning the sender.
*
* @returns A reference to the utf8 sender address. Or an empty string. */
std::string get_sender ();
/** @brief get sender SMTP address (UTF-8 encoded).
*
* Like get_sender but ensures not to touch oom or mapi
*
* @returns A reference to the utf8 sender address. Or an empty string. */
std::string get_cached_sender ();
/** @brief get the subject string (UTF-8 encoded).
*
* @returns the subject or an empty string. */
std::string get_subject () const;
/** @brief Is this a crypto mail handled by gpgol.
*
* Calling this is only valid after a message has been processed.
*
* @returns true if the mail was either signed or encrypted and we processed
* it.
*/
bool is_crypto_mail () const;
/** @brief This mail needs to be actually written.
*
* @returns true if the next write event should not be canceled.
*/
bool needs_save () { return m_needs_save; }
/** @brief set the needs save state.
*/
void set_needs_save (bool val) { m_needs_save = val; }
/** @brief is this mail an S/MIME mail.
*
* @returns true for smime messages.
*/
bool is_smime ();
/** @brief closes the inspector for a mail
*
* @returns true on success.
*/
static int close_inspector (Mail *mail);
/** @brief get the associated parser.
only valid while the actual parsing happens. */
std::shared_ptr parser () { return m_parser; }
/** @brief get the associated cryptcontroller.
only valid while the crypting happens. */
std::shared_ptr crypter () { return m_crypter; }
/** To be called from outside once the paser was done.
In Qt this would be a slot that is called once it is finished
we hack around that a bit by calling it from our windowmessages
handler.
*/
void parsing_done ();
/** Returns true if the mail was verified and has at least one
signature. Regardless of the validity of the mail */
bool is_signed () const;
/** Returns true if the mail is encrypted to at least one
recipient. Regardless if it could be decrypted. */
bool is_encrypted () const;
/** Are we "green" */
bool is_valid_sig ();
/** Get UID gets UniqueID property of this mail. Returns
an empty string if the uid was not set with set uid.*/
const std::string & get_uuid () const { return m_uuid; }
/** Returns 0 on success if the mail has a uid alrady or sets
the uid. Setting only succeeds if the OOM is currently
accessible. Returns -1 on error. */
int set_uuid ();
/** Returns a localized string describing in one or two
words the crypto status of this mail. */
std::string get_crypto_summary ();
/** Returns a localized string describing the detailed
crypto state of this mail. */
std::string get_crypto_details ();
/** Returns a localized string describing a one line
summary of the crypto state. */
std::string get_crypto_one_line ();
/** Get the icon id of the appropiate icon for this mail */
int get_crypto_icon_id () const;
/** Get the fingerprint of an associated signature or null
if it is not signed. */
const char *get_sig_fpr() const;
/** Remove all categories of this mail */
void remove_categories ();
/** Get the body of the mail */
std::string get_body () const;
/** Get the html of the mail */
std::string get_html_body () const;
/** Get the recipients recipients is a null
terminated array of strings. Needs to be freed
by the caller. */
char ** get_recipients () const;
/** Call close with discard changes to discard
plaintext. returns the value of the oom close
call. This may have delete the mail if the close
triggers an unload.
*/
static int close (Mail *mail);
/** Try to locate the keys for all recipients */
void locate_keys();
/** State variable to check if a close was triggerd by us. */
void set_close_triggered (bool value);
bool get_close_triggered () const;
/** Check if the mail should be sent as html alternative mail.
Only valid if update_oom_data was called before. */
bool is_html_alternative () const;
/** Get the html body. It is updated in update_oom_data.
Caller takes ownership of the string and has to free it.
*/
char *take_cached_html_body ();
/** Get the plain body. It is updated in update_oom_data.
Caller takes ownership of the string and has to free it.
*/
char *take_cached_plain_body ();
/** Get the cached recipients. It is updated in update_oom_data.
Caller takes ownership of the list and has to free it.
*/
char **take_cached_recipients ();
/** Returns 1 if the mail was encrypted, 2 if signed, 3 if both.
Only valid after decrypt_verify.
*/
int get_crypto_flags () const;
/** Returns true if the mail should be encrypted in the
after write event. */
bool needs_encrypt () const;
void set_needs_encrypt (bool val);
/** Gets the level of the signature. See:
https://wiki.gnupg.org/EasyGpg2016/AutomatedEncryption for
a definition of the levels. */
int get_signature_level () const;
/** Check if all attachments are hidden and show a warning
message appropiate to the crypto state if necessary. */
int check_attachments () const;
/** Check if the mail should be encrypted "inline" */
bool do_pgp_inline () const {return m_do_inline;}
/** Check if the mail should be encrypted "inline" */
void set_do_pgp_inline (bool value) {m_do_inline = value;}
/** Append data to a cached inline body. Helper to do this
on MAPI level and later add it through OOM */
void append_to_inline_body (const std::string &data);
/** Set the inline body as OOM body property. */
int inline_body_to_body ();
/** Get the crypt state */
CryptState crypt_state () const {return m_crypt_state;}
/** Set the crypt state */
void set_crypt_state (CryptState state) {m_crypt_state = state;}
/** Update MAPI data after encryption. */
void update_crypt_mapi ();
/** Update OOM data after encryption.
Checks for plain text leaks and
does not advance crypt state if body can't be cleaned.
*/
void update_crypt_oom ();
/** Enable / Disable the window of this mail.
When value is false the active window will
be disabled and the handle stored for a later
enable. */
void set_window_enabled (bool value);
/** Determine if the mail is an inline response.
Call check_inline_response first to update the state
from the OOM.
We need synchronous encryption for inline responses. */
bool async_crypt_disabled () { return m_async_crypt_disabled; }
/** Check through OOM if the current mail is an inline
response.
Caches the state which can then be queried through
async_crypt_disabled
*/
bool check_inline_response ();
/** Get the window for the mail. Caution! This is only
really valid in the time that the window is disabled.
Use with care and can be null or invalid.
*/
HWND get_window () { return m_window; }
/** Cleanup any attached crypter object. Useful
on error. */
void reset_crypter () { m_crypter = nullptr; }
/** Set special crypto mime data that should be used as the
mime structure when sending. */
void set_override_mime_data (const std::string &data) {m_mime_data = data;}
/** Get the mime data that should be used when sending. */
std::string get_override_mime_data () const { return m_mime_data; }
/** Set if this is a forward of a crypto mail. */
void set_is_forwarded_crypto_mail (bool value) { m_is_forwarded_crypto_mail = value; }
bool is_forwarded_crypto_mail () { return m_is_forwarded_crypto_mail; }
/** Remove the hidden GpgOL attachments. This is needed when forwarding
without encryption so that our attachments are not included in the forward.
Returns 0 on success. Works in OOM. */
int remove_our_attachments ();
/** Check both OOM and MAPI if the body is either empty or
encrypted. Won't abort on OOM or MAPI errors, so it can be
used in both states. But will return false if a body
was detected or in the OOM the MAPI Base Message. This
is intended as a saveguard before sending a mail.
This function should not be used to detected the necessity
of encryption and is only an extra check to catch unexpected
errors.
*/
bool has_crypted_or_empty_body ();
void update_body ();
/** Set if this mail looks like the send again of a crypto mail.
This will mean that after it is decrypted it is treated
like an unencrypted mail so that it can be encrypted again
or sent unencrypted.
*/
void set_is_send_again (bool value) { m_is_send_again = value; }
/* Attachment removal state variables. */
bool attachment_remove_warning_disabled () { return m_disable_att_remove_warning; }
/* Gets the string dump of the verification result. */
std::string get_verification_result_dump ();
+
+ /* Block loading HTML content */
+ void set_block_html (bool value);
+
+ /* Remove automatic loading of HTML references setting. */
+ void set_block_status ();
+
private:
void update_categories ();
void update_sigstate ();
LPDISPATCH m_mailitem;
LPDISPATCH m_event_sink;
bool m_processed, /* The message has been porcessed by us. */
m_needs_wipe, /* We have added plaintext to the mesage. */
m_needs_save, /* A property was changed but not by us. */
m_crypt_successful, /* We successfuly performed crypto on the item. */
m_is_smime, /* This is an smime mail. */
m_is_smime_checked, /* it was checked if this is an smime mail */
m_is_signed, /* Mail is signed */
m_is_valid, /* Mail is valid signed. */
m_close_triggered, /* We have programtically triggered a close */
m_is_html_alternative, /* Body Format is not plain text */
m_needs_encrypt; /* Send was triggered we want to encrypt. */
int m_moss_position; /* The number of the original message attachment. */
int m_crypto_flags;
std::string m_sender;
char *m_cached_html_body; /* Cached html body. */
char *m_cached_plain_body; /* Cached plain body. */
char **m_cached_recipients;
msgtype_t m_type; /* Our messagetype as set in mapi */
std::shared_ptr m_parser;
std::shared_ptr m_crypter;
GpgME::VerificationResult m_verify_result;
GpgME::DecryptionResult m_decrypt_result;
GpgME::Signature m_sig;
GpgME::UserID m_uid;
std::string m_uuid;
std::string m_orig_body;
bool m_do_inline;
bool m_is_gsuite; /* Are we on a gsuite account */
std::string m_inline_body;
CryptState m_crypt_state;
HWND m_window;
bool m_async_crypt_disabled;
std::string m_mime_data;
bool m_is_forwarded_crypto_mail; /* Is this a forward of a crypto mail */
bool m_is_send_again; /* Is this a send again of a crypto mail */
bool m_disable_att_remove_warning; /* Should not warn about attachment removal. */
+ bool m_block_html; /* Force blocking of html content. e.g for unsigned S/MIME mails. */
};
#endif // MAIL_H
diff --git a/src/mymapitags.h b/src/mymapitags.h
index 9232c37..1458102 100644
--- a/src/mymapitags.h
+++ b/src/mymapitags.h
@@ -1,1077 +1,1078 @@
/* mymapitags.h - MAPI definitions
*
* This file defines constants as used by MAPI. This interface
* definition has been compiled from similar Python code by g10 Code
* GmbH.
*
* Revisions:
* 2005-07-26 Initial version.
*
*/
#ifndef MAPITAGS_H
#define MAPITAGS_H 1
#define PT_UNSPECIFIED 0
#define PT_NULL 1
#define PT_I2 2
#define PT_LONG 3
#define PT_R4 4
#define PT_DOUBLE 5
#define PT_CURRENCY 6
#define PT_APPTIME 7
#define PT_ERROR 10
#define PT_BOOLEAN 11
#define PT_OBJECT 13
#define PT_I8 20
#define PT_STRING8 30
#define PT_UNICODE 31
#define PT_SYSTIME 64
#define PT_CLSID 72
#define PT_BINARY 258
#define PT_SHORT PT_I2
#define PT_I4 PT_LONG
#define PT_FLOAT PT_R4
#define PT_R8 PT_DOUBLE
#define PT_LONGLONG PT_I8
#define MV_FLAG 0x1000
#define PT_MV_I2 (MV_FLAG|PT_I2)
#define PT_MV_LONG (MV_FLAG|PT_LONG)
#define PT_MV_R4 (MV_FLAG|PT_R4)
#define PT_MV_DOUBLE (MV_FLAG|PT_DOUBLE)
#define PT_MV_CURRENCY (MV_FLAG|PT_CURRENCY)
#define PT_MV_APPTIME (MV_FLAG|PT_APPTIME)
#define PT_MV_SYSTIME (MV_FLAG|PT_SYSTIME)
#define PT_MV_STRING8 (MV_FLAG|PT_STRING8)
#define PT_MV_BINARY (MV_FLAG|PT_BINARY)
#define PT_MV_UNICODE (MV_FLAG|PT_UNICODE)
#define PT_MV_CLSID (MV_FLAG|PT_CLSID)
#define PT_MV_I8 (MV_FLAG|PT_I8)
#define PT_MV_SHORT PT_MV_I2
#define PT_MV_I4 PT_MV_LONG
#define PT_MV_FLOAT PT_MV_R4
#define PT_MV_R8 PT_MV_DOUBLE
#define PT_MV_LONGLONG PT_MV_I8
#define PT_TSTRING PT_UNICODE
#define PT_MV_TSTRING (MV_FLAG|PT_UNICODE)
#define PROP_TYPE_MASK 0x0000FFFF
#define PROP_TYPE(t) ((t) & PROP_TYPE_MASK)
#define PROP_ID(t) ((t)>>16)
#define PROP_TAG(t,i) (((i)<<16)|(t))
#define PROP_ID_NULL 0
#define PROP_ID_INVALID 0xFFFF
#define PR_NULL PROP_TAG(PT_NULL, PROP_ID_NULL)
#define PR_ACKNOWLEDGEMENT_MODE PROP_TAG( PT_LONG, 0x0001)
#define PR_ACKNOWLEDGEMENT_MODE PROP_TAG( PT_LONG, 0x0001)
#define PR_ALTERNATE_RECIPIENT_ALLOWED PROP_TAG( PT_BOOLEAN, 0x0002)
#define PR_AUTHORIZING_USERS PROP_TAG( PT_BINARY, 0x0003)
#define PR_AUTO_FORWARD_COMMENT PROP_TAG( PT_TSTRING, 0x0004)
#define PR_AUTO_FORWARD_COMMENT_W PROP_TAG( PT_UNICODE, 0x0004)
#define PR_AUTO_FORWARD_COMMENT_W PROP_TAG( PT_UNICODE, 0x0004)
#define PR_AUTO_FORWARD_COMMENT_A PROP_TAG( PT_STRING8, 0x0004)
#define PR_AUTO_FORWARDED PROP_TAG( PT_BOOLEAN, 0x0005)
#define PR_CONTENT_TYPE_A PROP_TAG( PT_STRING8, 0x8095)
#define PR_CONTENT_CONFIDENTIALITY_ALGORITHM_ID PROP_TAG( PT_BINARY, 0x0006)
#define PR_CONTENT_CORRELATOR PROP_TAG( PT_BINARY, 0x0007)
#define PR_CONTENT_IDENTIFIER PROP_TAG( PT_TSTRING, 0x0008)
#define PR_CONTENT_IDENTIFIER_W PROP_TAG( PT_UNICODE, 0x0008)
#define PR_CONTENT_IDENTIFIER_A PROP_TAG( PT_STRING8, 0x0008)
#define PR_CONTENT_LENGTH PROP_TAG( PT_LONG, 0x0009)
#define PR_CONTENT_RETURN_REQUESTED PROP_TAG( PT_BOOLEAN, 0x000A)
#define PR_CONVERSATION_KEY PROP_TAG( PT_BINARY, 0x000B)
#define PR_CONVERSION_EITS PROP_TAG( PT_BINARY, 0x000C)
#define PR_CONVERSION_WITH_LOSS_PROHIBITED PROP_TAG( PT_BOOLEAN, 0x000D)
#define PR_CONVERTED_EITS PROP_TAG( PT_BINARY, 0x000E)
#define PR_DEFERRED_DELIVERY_TIME PROP_TAG( PT_SYSTIME, 0x000F)
#define PR_DELIVER_TIME PROP_TAG( PT_SYSTIME, 0x0010)
#define PR_DISCARD_REASON PROP_TAG( PT_LONG, 0x0011)
#define PR_DISCLOSURE_OF_RECIPIENTS PROP_TAG( PT_BOOLEAN, 0x0012)
#define PR_DL_EXPANSION_HISTORY PROP_TAG( PT_BINARY, 0x0013)
#define PR_DL_EXPANSION_PROHIBITED PROP_TAG( PT_BOOLEAN, 0x0014)
#define PR_EXPIRY_TIME PROP_TAG( PT_SYSTIME, 0x0015)
#define PR_IMPLICIT_CONVERSION_PROHIBITED PROP_TAG( PT_BOOLEAN, 0x0016)
#define PR_IMPORTANCE PROP_TAG( PT_LONG, 0x0017)
#define PR_IPM_ID PROP_TAG( PT_BINARY, 0x0018)
#define PR_LATEST_DELIVERY_TIME PROP_TAG( PT_SYSTIME, 0x0019)
#define PR_MESSAGE_CLASS PROP_TAG( PT_TSTRING, 0x001A)
#define PR_MESSAGE_CLASS_W PROP_TAG( PT_UNICODE, 0x001A)
#define PR_MESSAGE_CLASS_A PROP_TAG( PT_STRING8, 0x001A)
#define PR_MESSAGE_DELIVERY_ID PROP_TAG( PT_BINARY, 0x001B)
#define PR_MESSAGE_SECURITY_LABEL PROP_TAG( PT_BINARY, 0x001E)
#define PR_OBSOLETED_IPMS PROP_TAG( PT_BINARY, 0x001F)
#define PR_ORIGINALLY_INTENDED_RECIPIENT_NAME PROP_TAG( PT_BINARY, 0x0020)
#define PR_ORIGINAL_EITS PROP_TAG( PT_BINARY, 0x0021)
#define PR_ORIGINATOR_CERTIFICATE PROP_TAG( PT_BINARY, 0x0022)
#define PR_ORIGINATOR_DELIVERY_REPORT_REQUESTED PROP_TAG( PT_BOOLEAN, 0x0023)
#define PR_ORIGINATOR_RETURN_ADDRESS PROP_TAG( PT_BINARY, 0x0024)
#define PR_PARENT_KEY PROP_TAG( PT_BINARY, 0x0025)
#define PR_PRIORITY PROP_TAG( PT_LONG, 0x0026)
#define PR_ORIGIN_CHECK PROP_TAG( PT_BINARY, 0x0027)
#define PR_PROOF_OF_SUBMISSION_REQUESTED PROP_TAG( PT_BOOLEAN, 0x0028)
#define PR_READ_RECEIPT_REQUESTED PROP_TAG( PT_BOOLEAN, 0x0029)
#define PR_RECEIPT_TIME PROP_TAG( PT_SYSTIME, 0x002A)
#define PR_RECIPIENT_REASSIGNMENT_PROHIBITED PROP_TAG( PT_BOOLEAN, 0x002B)
#define PR_REDIRECTION_HISTORY PROP_TAG( PT_BINARY, 0x002C)
#define PR_RELATED_IPMS PROP_TAG( PT_BINARY, 0x002D)
#define PR_ORIGINAL_SENSITIVITY PROP_TAG( PT_LONG, 0x002E)
#define PR_LANGUAGES PROP_TAG( PT_TSTRING, 0x002F)
#define PR_LANGUAGES_W PROP_TAG( PT_UNICODE, 0x002F)
#define PR_LANGUAGES_A PROP_TAG( PT_STRING8, 0x002F)
#define PR_REPLY_TIME PROP_TAG( PT_SYSTIME, 0x0030)
#define PR_REPORT_TAG PROP_TAG( PT_BINARY, 0x0031)
#define PR_REPORT_TIME PROP_TAG( PT_SYSTIME, 0x0032)
#define PR_RETURNED_IPM PROP_TAG( PT_BOOLEAN, 0x0033)
#define PR_SECURITY PROP_TAG( PT_LONG, 0x0034)
#define PR_INCOMPLETE_COPY PROP_TAG( PT_BOOLEAN, 0x0035)
#define PR_SENSITIVITY PROP_TAG( PT_LONG, 0x0036)
#define PR_SUBJECT PROP_TAG( PT_TSTRING, 0x0037)
#define PR_SUBJECT_W PROP_TAG( PT_UNICODE, 0x0037)
#define PR_SUBJECT_A PROP_TAG( PT_STRING8, 0x0037)
#define PR_SUBJECT_IPM PROP_TAG( PT_BINARY, 0x0038)
#define PR_CLIENT_SUBMIT_TIME PROP_TAG( PT_SYSTIME, 0x0039)
#define PR_REPORT_NAME PROP_TAG( PT_TSTRING, 0x003A)
#define PR_REPORT_NAME_W PROP_TAG( PT_UNICODE, 0x003A)
#define PR_REPORT_NAME_A PROP_TAG( PT_STRING8, 0x003A)
#define PR_SENT_REPRESENTING_SEARCH_KEY PROP_TAG( PT_BINARY, 0x003B)
#define PR_X400_CONTENT_TYPE PROP_TAG( PT_BINARY, 0x003C)
#define PR_SUBJECT_PREFIX PROP_TAG( PT_TSTRING, 0x003D)
#define PR_SUBJECT_PREFIX_W PROP_TAG( PT_UNICODE, 0x003D)
#define PR_SUBJECT_PREFIX_A PROP_TAG( PT_STRING8, 0x003D)
#define PR_NON_RECEIPT_REASON PROP_TAG( PT_LONG, 0x003E)
#define PR_RECEIVED_BY_ENTRYID PROP_TAG( PT_BINARY, 0x003F)
#define PR_RECEIVED_BY_NAME PROP_TAG( PT_TSTRING, 0x0040)
#define PR_RECEIVED_BY_NAME_W PROP_TAG( PT_UNICODE, 0x0040)
#define PR_RECEIVED_BY_NAME_A PROP_TAG( PT_STRING8, 0x0040)
#define PR_SENT_REPRESENTING_ENTRYID PROP_TAG( PT_BINARY, 0x0041)
#define PR_SENT_REPRESENTING_NAME PROP_TAG( PT_TSTRING, 0x0042)
#define PR_SENT_REPRESENTING_NAME_W PROP_TAG( PT_UNICODE, 0x0042)
#define PR_SENT_REPRESENTING_NAME_A PROP_TAG( PT_STRING8, 0x0042)
#define PR_RCVD_REPRESENTING_ENTRYID PROP_TAG( PT_BINARY, 0x0043)
#define PR_RCVD_REPRESENTING_NAME PROP_TAG( PT_TSTRING, 0x0044)
#define PR_RCVD_REPRESENTING_NAME_W PROP_TAG( PT_UNICODE, 0x0044)
#define PR_RCVD_REPRESENTING_NAME_A PROP_TAG( PT_STRING8, 0x0044)
#define PR_REPORT_ENTRYID PROP_TAG( PT_BINARY, 0x0045)
#define PR_READ_RECEIPT_ENTRYID PROP_TAG( PT_BINARY, 0x0046)
#define PR_MESSAGE_SUBMISSION_ID PROP_TAG( PT_BINARY, 0x0047)
#define PR_PROVIDER_SUBMIT_TIME PROP_TAG( PT_SYSTIME, 0x0048)
#define PR_ORIGINAL_SUBJECT PROP_TAG( PT_TSTRING, 0x0049)
#define PR_ORIGINAL_SUBJECT_W PROP_TAG( PT_UNICODE, 0x0049)
#define PR_ORIGINAL_SUBJECT_A PROP_TAG( PT_STRING8, 0x0049)
#define PR_DISC_VAL PROP_TAG( PT_BOOLEAN, 0x004A)
#define PR_ORIG_MESSAGE_CLASS PROP_TAG( PT_TSTRING, 0x004B)
#define PR_ORIG_MESSAGE_CLASS_W PROP_TAG( PT_UNICODE, 0x004B)
#define PR_ORIG_MESSAGE_CLASS_A PROP_TAG( PT_STRING8, 0x004B)
#define PR_ORIGINAL_AUTHOR_ENTRYID PROP_TAG( PT_BINARY, 0x004C)
#define PR_ORIGINAL_AUTHOR_NAME PROP_TAG( PT_TSTRING, 0x004D)
#define PR_ORIGINAL_AUTHOR_NAME_W PROP_TAG( PT_UNICODE, 0x004D)
#define PR_ORIGINAL_AUTHOR_NAME_A PROP_TAG( PT_STRING8, 0x004D)
#define PR_ORIGINAL_SUBMIT_TIME PROP_TAG( PT_SYSTIME, 0x004E)
#define PR_REPLY_RECIPIENT_ENTRIES PROP_TAG( PT_BINARY, 0x004F)
#define PR_REPLY_RECIPIENT_NAMES PROP_TAG( PT_TSTRING, 0x0050)
#define PR_REPLY_RECIPIENT_NAMES_W PROP_TAG( PT_UNICODE, 0x0050)
#define PR_REPLY_RECIPIENT_NAMES_A PROP_TAG( PT_STRING8, 0x0050)
#define PR_RECEIVED_BY_SEARCH_KEY PROP_TAG( PT_BINARY, 0x0051)
#define PR_RCVD_REPRESENTING_SEARCH_KEY PROP_TAG( PT_BINARY, 0x0052)
#define PR_READ_RECEIPT_SEARCH_KEY PROP_TAG( PT_BINARY, 0x0053)
#define PR_REPORT_SEARCH_KEY PROP_TAG( PT_BINARY, 0x0054)
#define PR_ORIGINAL_DELIVERY_TIME PROP_TAG( PT_SYSTIME, 0x0055)
#define PR_ORIGINAL_AUTHOR_SEARCH_KEY PROP_TAG( PT_BINARY, 0x0056)
#define PR_MESSAGE_TO_ME PROP_TAG( PT_BOOLEAN, 0x0057)
#define PR_MESSAGE_CC_ME PROP_TAG( PT_BOOLEAN, 0x0058)
#define PR_MESSAGE_RECIP_ME PROP_TAG( PT_BOOLEAN, 0x0059)
#define PR_ORIGINAL_SENDER_NAME PROP_TAG( PT_TSTRING, 0x005A)
#define PR_ORIGINAL_SENDER_NAME_W PROP_TAG( PT_UNICODE, 0x005A)
#define PR_ORIGINAL_SENDER_NAME_A PROP_TAG( PT_STRING8, 0x005A)
#define PR_ORIGINAL_SENDER_ENTRYID PROP_TAG( PT_BINARY, 0x005B)
#define PR_ORIGINAL_SENDER_SEARCH_KEY PROP_TAG( PT_BINARY, 0x005C)
#define PR_ORIGINAL_SENT_REPRESENTING_NAME PROP_TAG( PT_TSTRING, 0x005D)
#define PR_ORIGINAL_SENT_REPRESENTING_NAME_W PROP_TAG( PT_UNICODE, 0x005D)
#define PR_ORIGINAL_SENT_REPRESENTING_NAME_A PROP_TAG( PT_STRING8, 0x005D)
#define PR_ORIGINAL_SENT_REPRESENTING_ENTRYID PROP_TAG( PT_BINARY, 0x005E)
#define PR_ORIGINAL_SENT_REPRESENTING_SEARCH_KEY PROP_TAG( PT_BINARY, 0x005F)
#define PR_START_DATE PROP_TAG( PT_SYSTIME, 0x0060)
#define PR_END_DATE PROP_TAG( PT_SYSTIME, 0x0061)
#define PR_OWNER_APPT_ID PROP_TAG( PT_LONG, 0x0062)
#define PR_RESPONSE_REQUESTED PROP_TAG( PT_BOOLEAN, 0x0063)
#define PR_SENT_REPRESENTING_ADDRTYPE PROP_TAG( PT_TSTRING, 0x0064)
#define PR_SENT_REPRESENTING_ADDRTYPE_W PROP_TAG( PT_UNICODE, 0x0064)
#define PR_SENT_REPRESENTING_ADDRTYPE_A PROP_TAG( PT_STRING8, 0x0064)
#define PR_SENT_REPRESENTING_EMAIL_ADDRESS PROP_TAG( PT_TSTRING, 0x0065)
#define PR_SENT_REPRESENTING_EMAIL_ADDRESS_W PROP_TAG( PT_UNICODE, 0x0065)
#define PR_SENT_REPRESENTING_EMAIL_ADDRESS_A PROP_TAG( PT_STRING8, 0x0065)
#define PR_ORIGINAL_SENDER_ADDRTYPE PROP_TAG( PT_TSTRING, 0x0066)
#define PR_ORIGINAL_SENDER_ADDRTYPE_W PROP_TAG( PT_UNICODE, 0x0066)
#define PR_ORIGINAL_SENDER_ADDRTYPE_A PROP_TAG( PT_STRING8, 0x0066)
#define PR_ORIGINAL_SENDER_EMAIL_ADDRESS PROP_TAG( PT_TSTRING, 0x0067)
#define PR_ORIGINAL_SENDER_EMAIL_ADDRESS_W PROP_TAG( PT_UNICODE, 0x0067)
#define PR_ORIGINAL_SENDER_EMAIL_ADDRESS_A PROP_TAG( PT_STRING8, 0x0067)
#define PR_ORIGINAL_SENT_REPRESENTING_ADDRTYPE PROP_TAG( PT_TSTRING, 0x0068)
#define PR_ORIGINAL_SENT_REPRESENTING_ADDRTYPE_W PROP_TAG( PT_UNICODE, 0x0068)
#define PR_ORIGINAL_SENT_REPRESENTING_ADDRTYPE_A PROP_TAG( PT_STRING8, 0x0068)
#define PR_ORIGINAL_SENT_REPRESENTING_EMAIL_ADDRESS \
PROP_TAG( PT_TSTRING, 0x0069)
#define PR_ORIGINAL_SENT_REPRESENTING_EMAIL_ADDRESS_W \
PROP_TAG( PT_UNICODE, 0x0069)
#define PR_ORIGINAL_SENT_REPRESENTING_EMAIL_ADDRESS_A \
PROP_TAG( PT_STRING8, 0x0069)
#define PR_CONVERSATION_TOPIC PROP_TAG( PT_TSTRING, 0x0070)
#define PR_CONVERSATION_TOPIC_W PROP_TAG( PT_UNICODE, 0x0070)
#define PR_CONVERSATION_TOPIC_A PROP_TAG( PT_STRING8, 0x0070)
#define PR_CONVERSATION_INDEX PROP_TAG( PT_BINARY, 0x0071)
#define PR_ORIGINAL_DISPLAY_BCC PROP_TAG( PT_TSTRING, 0x0072)
#define PR_ORIGINAL_DISPLAY_BCC_W PROP_TAG( PT_UNICODE, 0x0072)
#define PR_ORIGINAL_DISPLAY_BCC_A PROP_TAG( PT_STRING8, 0x0072)
#define PR_ORIGINAL_DISPLAY_CC PROP_TAG( PT_TSTRING, 0x0073)
#define PR_ORIGINAL_DISPLAY_CC_W PROP_TAG( PT_UNICODE, 0x0073)
#define PR_ORIGINAL_DISPLAY_CC_A PROP_TAG( PT_STRING8, 0x0073)
#define PR_ORIGINAL_DISPLAY_TO PROP_TAG( PT_TSTRING, 0x0074)
#define PR_ORIGINAL_DISPLAY_TO_W PROP_TAG( PT_UNICODE, 0x0074)
#define PR_ORIGINAL_DISPLAY_TO_A PROP_TAG( PT_STRING8, 0x0074)
#define PR_RECEIVED_BY_ADDRTYPE PROP_TAG( PT_TSTRING, 0x0075)
#define PR_RECEIVED_BY_ADDRTYPE_W PROP_TAG( PT_UNICODE, 0x0075)
#define PR_RECEIVED_BY_ADDRTYPE_A PROP_TAG( PT_STRING8, 0x0075)
#define PR_RECEIVED_BY_EMAIL_ADDRESS PROP_TAG( PT_TSTRING, 0x0076)
#define PR_RECEIVED_BY_EMAIL_ADDRESS_W PROP_TAG( PT_UNICODE, 0x0076)
#define PR_RECEIVED_BY_EMAIL_ADDRESS_A PROP_TAG( PT_STRING8, 0x0076)
#define PR_RCVD_REPRESENTING_ADDRTYPE PROP_TAG( PT_TSTRING, 0x0077)
#define PR_RCVD_REPRESENTING_ADDRTYPE_W PROP_TAG( PT_UNICODE, 0x0077)
#define PR_RCVD_REPRESENTING_ADDRTYPE_A PROP_TAG( PT_STRING8, 0x0077)
#define PR_RCVD_REPRESENTING_EMAIL_ADDRESS PROP_TAG( PT_TSTRING, 0x0078)
#define PR_RCVD_REPRESENTING_EMAIL_ADDRESS_W PROP_TAG( PT_UNICODE, 0x0078)
#define PR_RCVD_REPRESENTING_EMAIL_ADDRESS_A PROP_TAG( PT_STRING8, 0x0078)
#define PR_ORIGINAL_AUTHOR_ADDRTYPE PROP_TAG( PT_TSTRING, 0x0079)
#define PR_ORIGINAL_AUTHOR_ADDRTYPE_W PROP_TAG( PT_UNICODE, 0x0079)
#define PR_ORIGINAL_AUTHOR_ADDRTYPE_A PROP_TAG( PT_STRING8, 0x0079)
#define PR_ORIGINAL_AUTHOR_EMAIL_ADDRESS PROP_TAG( PT_TSTRING, 0x007A)
#define PR_ORIGINAL_AUTHOR_EMAIL_ADDRESS_W PROP_TAG( PT_UNICODE, 0x007A)
#define PR_ORIGINAL_AUTHOR_EMAIL_ADDRESS_A PROP_TAG( PT_STRING8, 0x007A)
#define PR_ORIGINALLY_INTENDED_RECIP_ADDRTYPE PROP_TAG( PT_TSTRING, 0x007B)
#define PR_ORIGINALLY_INTENDED_RECIP_ADDRTYPE_W PROP_TAG( PT_UNICODE, 0x007B)
#define PR_ORIGINALLY_INTENDED_RECIP_ADDRTYPE_A PROP_TAG( PT_STRING8, 0x007B)
#define PR_ORIGINALLY_INTENDED_RECIP_EMAIL_ADDRESS \
PROP_TAG( PT_TSTRING, 0x007C)
#define PR_ORIGINALLY_INTENDED_RECIP_EMAIL_ADDRESS_W \
PROP_TAG( PT_UNICODE, 0x007C)
#define PR_ORIGINALLY_INTENDED_RECIP_EMAIL_ADDRESS_A \
PROP_TAG( PT_STRING8, 0x007C)
#define PR_TRANSPORT_MESSAGE_HEADERS PROP_TAG(PT_TSTRING, 0x007D)
#define PR_TRANSPORT_MESSAGE_HEADERS_W PROP_TAG(PT_UNICODE, 0x007D)
#define PR_TRANSPORT_MESSAGE_HEADERS_A PROP_TAG(PT_STRING8, 0x007D)
#define PR_DELEGATION PROP_TAG(PT_BINARY, 0x007E)
#define PR_TNEF_CORRELATION_KEY PROP_TAG(PT_BINARY, 0x007F)
#define PR_BODY PROP_TAG( PT_TSTRING, 0x1000)
#define PR_BODY_W PROP_TAG( PT_UNICODE, 0x1000)
#define PR_BODY_A PROP_TAG( PT_STRING8, 0x1000)
#define PR_REPORT_TEXT PROP_TAG( PT_TSTRING, 0x1001)
#define PR_REPORT_TEXT_W PROP_TAG( PT_UNICODE, 0x1001)
#define PR_REPORT_TEXT_A PROP_TAG( PT_STRING8, 0x1001)
#define PR_ORIGINATOR_AND_DL_EXPANSION_HISTORY PROP_TAG( PT_BINARY, 0x1002)
#define PR_REPORTING_DL_NAME PROP_TAG( PT_BINARY, 0x1003)
#define PR_REPORTING_MTA_CERTIFICATE PROP_TAG( PT_BINARY, 0x1004)
#define PR_RTF_SYNC_BODY_CRC PROP_TAG( PT_LONG, 0x1006)
#define PR_RTF_SYNC_BODY_COUNT PROP_TAG( PT_LONG, 0x1007)
#define PR_RTF_SYNC_BODY_TAG PROP_TAG( PT_TSTRING, 0x1008)
#define PR_RTF_SYNC_BODY_TAG_W PROP_TAG( PT_UNICODE, 0x1008)
#define PR_RTF_SYNC_BODY_TAG_A PROP_TAG( PT_STRING8, 0x1008)
#define PR_RTF_COMPRESSED PROP_TAG( PT_BINARY, 0x1009)
#define PR_RTF_SYNC_PREFIX_COUNT PROP_TAG( PT_LONG, 0x1010)
#define PR_RTF_SYNC_TRAILING_COUNT PROP_TAG( PT_LONG, 0x1011)
#define PR_ORIGINALLY_INTENDED_RECIP_ENTRYID PROP_TAG( PT_BINARY, 0x1012)
#define PR_BODY_HTML PROP_TAG( PT_TSTRING, 0x1013)
#define PR_BODY_HTML_W PROP_TAG( PT_UNICODE, 0x1013)
#define PR_BODY_HTML_A PROP_TAG( PT_STRING8, 0x1013)
#define PR_CONTENT_INTEGRITY_CHECK PROP_TAG( PT_BINARY, 0x0C00)
#define PR_EXPLICIT_CONVERSION PROP_TAG( PT_LONG, 0x0C01)
#define PR_IPM_RETURN_REQUESTED PROP_TAG( PT_BOOLEAN, 0x0C02)
#define PR_MESSAGE_TOKEN PROP_TAG( PT_BINARY, 0x0C03)
#define PR_NDR_REASON_CODE PROP_TAG( PT_LONG, 0x0C04)
#define PR_NDR_DIAG_CODE PROP_TAG( PT_LONG, 0x0C05)
#define PR_NON_RECEIPT_NOTIFICATION_REQUESTED PROP_TAG( PT_BOOLEAN, 0x0C06)
#define PR_DELIVERY_POINT PROP_TAG( PT_LONG, 0x0C07)
#define PR_ORIGINATOR_NON_DELIVERY_REPORT_REQUESTED \
PROP_TAG( PT_BOOLEAN, 0x0C08)
#define PR_ORIGINATOR_REQUESTED_ALTERNATE_RECIPIENT \
PROP_TAG( PT_BINARY, 0x0C09)
#define PR_PHYSICAL_DELIVERY_BUREAU_FAX_DELIVERY \
PROP_TAG( PT_BOOLEAN, 0x0C0A)
#define PR_PHYSICAL_DELIVERY_MODE PROP_TAG( PT_LONG, 0x0C0B)
#define PR_PHYSICAL_DELIVERY_REPORT_REQUEST PROP_TAG( PT_LONG, 0x0C0C)
#define PR_PHYSICAL_FORWARDING_ADDRESS PROP_TAG( PT_BINARY, 0x0C0D)
#define PR_PHYSICAL_FORWARDING_ADDRESS_REQUESTED \
PROP_TAG( PT_BOOLEAN, 0x0C0E)
#define PR_PHYSICAL_FORWARDING_PROHIBITED PROP_TAG( PT_BOOLEAN, 0x0C0F)
#define PR_PHYSICAL_RENDITION_ATTRIBUTES PROP_TAG( PT_BINARY, 0x0C10)
#define PR_PROOF_OF_DELIVERY PROP_TAG( PT_BINARY, 0x0C11)
#define PR_PROOF_OF_DELIVERY_REQUESTED PROP_TAG( PT_BOOLEAN, 0x0C12)
#define PR_RECIPIENT_CERTIFICATE PROP_TAG( PT_BINARY, 0x0C13)
#define PR_RECIPIENT_NUMBER_FOR_ADVICE PROP_TAG( PT_TSTRING, 0x0C14)
#define PR_RECIPIENT_NUMBER_FOR_ADVICE_W PROP_TAG( PT_UNICODE, 0x0C14)
#define PR_RECIPIENT_NUMBER_FOR_ADVICE_A PROP_TAG( PT_STRING8, 0x0C14)
#define PR_RECIPIENT_TYPE PROP_TAG( PT_LONG, 0x0C15)
#define PR_REGISTERED_MAIL_TYPE PROP_TAG( PT_LONG, 0x0C16)
#define PR_REPLY_REQUESTED PROP_TAG( PT_BOOLEAN, 0x0C17)
#define PR_REQUESTED_DELIVERY_METHOD PROP_TAG( PT_LONG, 0x0C18)
#define PR_SENDER_ENTRYID PROP_TAG( PT_BINARY, 0x0C19)
#define PR_SENDER_NAME PROP_TAG( PT_TSTRING, 0x0C1A)
#define PR_SENDER_NAME_W PROP_TAG( PT_UNICODE, 0x0C1A)
#define PR_SENDER_NAME_A PROP_TAG( PT_STRING8, 0x0C1A)
#define PR_SUPPLEMENTARY_INFO PROP_TAG( PT_TSTRING, 0x0C1B)
#define PR_SUPPLEMENTARY_INFO_W PROP_TAG( PT_UNICODE, 0x0C1B)
#define PR_SUPPLEMENTARY_INFO_A PROP_TAG( PT_STRING8, 0x0C1B)
#define PR_TYPE_OF_MTS_USER PROP_TAG( PT_LONG, 0x0C1C)
#define PR_SENDER_SEARCH_KEY PROP_TAG( PT_BINARY, 0x0C1D)
#define PR_SENDER_ADDRTYPE PROP_TAG( PT_TSTRING, 0x0C1E)
#define PR_SENDER_ADDRTYPE_W PROP_TAG( PT_UNICODE, 0x0C1E)
#define PR_SENDER_ADDRTYPE_A PROP_TAG( PT_STRING8, 0x0C1E)
#define PR_SENDER_EMAIL_ADDRESS PROP_TAG( PT_TSTRING, 0x0C1F)
#define PR_SENDER_EMAIL_ADDRESS_W PROP_TAG( PT_UNICODE, 0x0C1F)
#define PR_SENDER_EMAIL_ADDRESS_A PROP_TAG( PT_STRING8, 0x0C1F)
#define PR_CURRENT_VERSION PROP_TAG( PT_I8, 0x0E00)
#define PR_DELETE_AFTER_SUBMIT PROP_TAG( PT_BOOLEAN, 0x0E01)
#define PR_DISPLAY_BCC PROP_TAG( PT_TSTRING, 0x0E02)
#define PR_DISPLAY_BCC_W PROP_TAG( PT_UNICODE, 0x0E02)
#define PR_DISPLAY_BCC_A PROP_TAG( PT_STRING8, 0x0E02)
#define PR_DISPLAY_CC PROP_TAG( PT_TSTRING, 0x0E03)
#define PR_DISPLAY_CC_W PROP_TAG( PT_UNICODE, 0x0E03)
#define PR_DISPLAY_CC_A PROP_TAG( PT_STRING8, 0x0E03)
#define PR_DISPLAY_TO PROP_TAG( PT_TSTRING, 0x0E04)
#define PR_DISPLAY_TO_W PROP_TAG( PT_UNICODE, 0x0E04)
#define PR_DISPLAY_TO_A PROP_TAG( PT_STRING8, 0x0E04)
#define PR_PARENT_DISPLAY PROP_TAG( PT_TSTRING, 0x0E05)
#define PR_PARENT_DISPLAY_W PROP_TAG( PT_UNICODE, 0x0E05)
#define PR_PARENT_DISPLAY_A PROP_TAG( PT_STRING8, 0x0E05)
#define PR_MESSAGE_DELIVERY_TIME PROP_TAG( PT_SYSTIME, 0x0E06)
#define PR_MESSAGE_FLAGS PROP_TAG( PT_LONG, 0x0E07)
#define PR_MESSAGE_SIZE PROP_TAG( PT_LONG, 0x0E08)
#define PR_PARENT_ENTRYID PROP_TAG( PT_BINARY, 0x0E09)
#define PR_SENTMAIL_ENTRYID PROP_TAG( PT_BINARY, 0x0E0A)
#define PR_CORRELATE PROP_TAG( PT_BOOLEAN, 0x0E0C)
#define PR_CORRELATE_MTSID PROP_TAG( PT_BINARY, 0x0E0D)
#define PR_DISCRETE_VALUES PROP_TAG( PT_BOOLEAN, 0x0E0E)
#define PR_RESPONSIBILITY PROP_TAG( PT_BOOLEAN, 0x0E0F)
#define PR_SPOOLER_STATUS PROP_TAG( PT_LONG, 0x0E10)
#define PR_TRANSPORT_STATUS PROP_TAG( PT_LONG, 0x0E11)
#define PR_MESSAGE_RECIPIENTS PROP_TAG( PT_OBJECT, 0x0E12)
#define PR_MESSAGE_ATTACHMENTS PROP_TAG( PT_OBJECT, 0x0E13)
#define PR_SUBMIT_FLAGS PROP_TAG( PT_LONG, 0x0E14)
#define PR_RECIPIENT_STATUS PROP_TAG( PT_LONG, 0x0E15)
#define PR_TRANSPORT_KEY PROP_TAG( PT_LONG, 0x0E16)
#define PR_MSG_STATUS PROP_TAG( PT_LONG, 0x0E17)
#define PR_MESSAGE_DOWNLOAD_TIME PROP_TAG( PT_LONG, 0x0E18)
#define PR_CREATION_VERSION PROP_TAG( PT_I8, 0x0E19)
#define PR_MODIFY_VERSION PROP_TAG( PT_I8, 0x0E1A)
#define PR_HASATTACH PROP_TAG( PT_BOOLEAN, 0x0E1B)
#define PR_BODY_CRC PROP_TAG( PT_LONG, 0x0E1C)
#define PR_NORMALIZED_SUBJECT PROP_TAG( PT_TSTRING, 0x0E1D)
#define PR_NORMALIZED_SUBJECT_W PROP_TAG( PT_UNICODE, 0x0E1D)
#define PR_NORMALIZED_SUBJECT_A PROP_TAG( PT_STRING8, 0x0E1D)
#define PR_RTF_IN_SYNC PROP_TAG( PT_BOOLEAN, 0x0E1F)
#define PR_ATTACH_SIZE PROP_TAG( PT_LONG, 0x0E20)
#define PR_ATTACH_NUM PROP_TAG( PT_LONG, 0x0E21)
#define PR_PREPROCESS PROP_TAG( PT_BOOLEAN, 0x0E22)
#define PR_ORIGINATING_MTA_CERTIFICATE PROP_TAG( PT_BINARY, 0x0E25)
#define PR_PROOF_OF_SUBMISSION PROP_TAG( PT_BINARY, 0x0E26)
#define PR_PRIMARY_SEND_ACCT PROP_TAG( PT_UNICODE, 0x0E28)
#define PR_ENTRYID PROP_TAG( PT_BINARY, 0x0FFF)
#define PR_OBJECT_TYPE PROP_TAG( PT_LONG, 0x0FFE)
#define PR_ICON PROP_TAG( PT_BINARY, 0x0FFD)
#define PR_MINI_ICON PROP_TAG( PT_BINARY, 0x0FFC)
#define PR_STORE_ENTRYID PROP_TAG( PT_BINARY, 0x0FFB)
#define PR_STORE_RECORD_KEY PROP_TAG( PT_BINARY, 0x0FFA)
#define PR_RECORD_KEY PROP_TAG( PT_BINARY, 0x0FF9)
#define PR_MAPPING_SIGNATURE PROP_TAG( PT_BINARY, 0x0FF8)
#define PR_ACCESS_LEVEL PROP_TAG( PT_LONG, 0x0FF7)
#define PR_INSTANCE_KEY PROP_TAG( PT_BINARY, 0x0FF6)
#define PR_ROW_TYPE PROP_TAG( PT_LONG, 0x0FF5)
#define PR_ACCESS PROP_TAG( PT_LONG, 0x0FF4)
#define PR_ROWID PROP_TAG( PT_LONG, 0x3000)
#define PR_DISPLAY_NAME PROP_TAG( PT_TSTRING, 0x3001)
#define PR_DISPLAY_NAME_W PROP_TAG( PT_UNICODE, 0x3001)
#define PR_DISPLAY_NAME_A PROP_TAG( PT_STRING8, 0x3001)
#define PR_ADDRTYPE PROP_TAG( PT_TSTRING, 0x3002)
#define PR_ADDRTYPE_W PROP_TAG( PT_UNICODE, 0x3002)
#define PR_ADDRTYPE_A PROP_TAG( PT_STRING8, 0x3002)
#define PR_EMAIL_ADDRESS PROP_TAG( PT_TSTRING, 0x3003)
#define PR_EMAIL_ADDRESS_W PROP_TAG( PT_UNICODE, 0x3003)
#define PR_EMAIL_ADDRESS_A PROP_TAG( PT_STRING8, 0x3003)
#define PR_COMMENT PROP_TAG( PT_TSTRING, 0x3004)
#define PR_COMMENT_W PROP_TAG( PT_UNICODE, 0x3004)
#define PR_COMMENT_A PROP_TAG( PT_STRING8, 0x3004)
#define PR_DEPTH PROP_TAG( PT_LONG, 0x3005)
#define PR_PROVIDER_DISPLAY PROP_TAG( PT_TSTRING, 0x3006)
#define PR_PROVIDER_DISPLAY_W PROP_TAG( PT_UNICODE, 0x3006)
#define PR_PROVIDER_DISPLAY_A PROP_TAG( PT_STRING8, 0x3006)
#define PR_CREATION_TIME PROP_TAG( PT_SYSTIME, 0x3007)
#define PR_LAST_MODIFICATION_TIME PROP_TAG( PT_SYSTIME, 0x3008)
#define PR_RESOURCE_FLAGS PROP_TAG( PT_LONG, 0x3009)
#define PR_PROVIDER_DLL_NAME PROP_TAG( PT_TSTRING, 0x300A)
#define PR_PROVIDER_DLL_NAME_W PROP_TAG( PT_UNICODE, 0x300A)
#define PR_PROVIDER_DLL_NAME_A PROP_TAG( PT_STRING8, 0x300A)
#define PR_SEARCH_KEY PROP_TAG( PT_BINARY, 0x300B)
#define PR_PROVIDER_UID PROP_TAG( PT_BINARY, 0x300C)
#define PR_PROVIDER_ORDINAL PROP_TAG( PT_LONG, 0x300D)
#define PR_FORM_VERSION PROP_TAG(PT_TSTRING, 0x3301)
#define PR_FORM_VERSION_W PROP_TAG(PT_UNICODE, 0x3301)
#define PR_FORM_VERSION_A PROP_TAG(PT_STRING8, 0x3301)
#define PR_FORM_CLSID PROP_TAG(PT_CLSID, 0x3302)
#define PR_FORM_CONTACT_NAME PROP_TAG(PT_TSTRING, 0x3303)
#define PR_FORM_CONTACT_NAME_W PROP_TAG(PT_UNICODE, 0x3303)
#define PR_FORM_CONTACT_NAME_A PROP_TAG(PT_STRING8, 0x3303)
#define PR_FORM_CATEGORY PROP_TAG(PT_TSTRING, 0x3304)
#define PR_FORM_CATEGORY_W PROP_TAG(PT_UNICODE, 0x3304)
#define PR_FORM_CATEGORY_A PROP_TAG(PT_STRING8, 0x3304)
#define PR_FORM_CATEGORY_SUB PROP_TAG(PT_TSTRING, 0x3305)
#define PR_FORM_CATEGORY_SUB_W PROP_TAG(PT_UNICODE, 0x3305)
#define PR_FORM_CATEGORY_SUB_A PROP_TAG(PT_STRING8, 0x3305)
#define PR_FORM_HOST_MAP PROP_TAG(PT_MV_LONG, 0x3306)
#define PR_FORM_HIDDEN PROP_TAG(PT_BOOLEAN, 0x3307)
#define PR_FORM_DESIGNER_NAME PROP_TAG(PT_TSTRING, 0x3308)
#define PR_FORM_DESIGNER_NAME_W PROP_TAG(PT_UNICODE, 0x3308)
#define PR_FORM_DESIGNER_NAME_A PROP_TAG(PT_STRING8, 0x3308)
#define PR_FORM_DESIGNER_GUID PROP_TAG(PT_CLSID, 0x3309)
#define PR_FORM_MESSAGE_BEHAVIOR PROP_TAG(PT_LONG, 0x330A)
#define PR_DEFAULT_STORE PROP_TAG( PT_BOOLEAN, 0x3400)
#define PR_STORE_SUPPORT_MASK PROP_TAG( PT_LONG, 0x340D)
#define PR_STORE_STATE PROP_TAG( PT_LONG, 0x340E)
#define PR_IPM_SUBTREE_SEARCH_KEY PROP_TAG( PT_BINARY, 0x3410)
#define PR_IPM_OUTBOX_SEARCH_KEY PROP_TAG( PT_BINARY, 0x3411)
#define PR_IPM_WASTEBASKET_SEARCH_KEY PROP_TAG( PT_BINARY, 0x3412)
#define PR_IPM_SENTMAIL_SEARCH_KEY PROP_TAG( PT_BINARY, 0x3413)
#define PR_MDB_PROVIDER PROP_TAG( PT_BINARY, 0x3414)
#define PR_RECEIVE_FOLDER_SETTINGS PROP_TAG( PT_OBJECT, 0x3415)
#define PR_VALID_FOLDER_MASK PROP_TAG( PT_LONG, 0x35DF)
#define PR_IPM_SUBTREE_ENTRYID PROP_TAG( PT_BINARY, 0x35E0)
#define PR_IPM_OUTBOX_ENTRYID PROP_TAG( PT_BINARY, 0x35E2)
#define PR_IPM_WASTEBASKET_ENTRYID PROP_TAG( PT_BINARY, 0x35E3)
#define PR_IPM_SENTMAIL_ENTRYID PROP_TAG( PT_BINARY, 0x35E4)
#define PR_VIEWS_ENTRYID PROP_TAG( PT_BINARY, 0x35E5)
#define PR_COMMON_VIEWS_ENTRYID PROP_TAG( PT_BINARY, 0x35E6)
#define PR_FINDER_ENTRYID PROP_TAG( PT_BINARY, 0x35E7)
#define PR_CONTAINER_FLAGS PROP_TAG( PT_LONG, 0x3600)
#define PR_FOLDER_TYPE PROP_TAG( PT_LONG, 0x3601)
#define PR_CONTENT_COUNT PROP_TAG( PT_LONG, 0x3602)
#define PR_CONTENT_UNREAD PROP_TAG( PT_LONG, 0x3603)
#define PR_CREATE_TEMPLATES PROP_TAG( PT_OBJECT, 0x3604)
#define PR_DETAILS_TABLE PROP_TAG( PT_OBJECT, 0x3605)
#define PR_SEARCH PROP_TAG( PT_OBJECT, 0x3607)
#define PR_SELECTABLE PROP_TAG( PT_BOOLEAN, 0x3609)
#define PR_SUBFOLDERS PROP_TAG( PT_BOOLEAN, 0x360A)
#define PR_STATUS PROP_TAG( PT_LONG, 0x360B)
#define PR_ANR PROP_TAG( PT_TSTRING, 0x360C)
#define PR_ANR_W PROP_TAG( PT_UNICODE, 0x360C)
#define PR_ANR_A PROP_TAG( PT_STRING8, 0x360C)
#define PR_CONTENTS_SORT_ORDER PROP_TAG( PT_MV_LONG, 0x360D)
#define PR_CONTAINER_HIERARCHY PROP_TAG( PT_OBJECT, 0x360E)
#define PR_CONTAINER_CONTENTS PROP_TAG( PT_OBJECT, 0x360F)
#define PR_FOLDER_ASSOCIATED_CONTENTS PROP_TAG( PT_OBJECT, 0x3610)
#define PR_DEF_CREATE_DL PROP_TAG( PT_BINARY, 0x3611)
#define PR_DEF_CREATE_MAILUSER PROP_TAG( PT_BINARY, 0x3612)
#define PR_CONTAINER_CLASS PROP_TAG( PT_TSTRING, 0x3613)
#define PR_CONTAINER_CLASS_W PROP_TAG( PT_UNICODE, 0x3613)
#define PR_CONTAINER_CLASS_A PROP_TAG( PT_STRING8, 0x3613)
#define PR_CONTAINER_MODIFY_VERSION PROP_TAG( PT_I8, 0x3614)
#define PR_AB_PROVIDER_ID PROP_TAG( PT_BINARY, 0x3615)
#define PR_DEFAULT_VIEW_ENTRYID PROP_TAG( PT_BINARY, 0x3616)
#define PR_ASSOC_CONTENT_COUNT PROP_TAG( PT_LONG, 0x3617)
#define PR_ATTACHMENT_X400_PARAMETERS PROP_TAG( PT_BINARY, 0x3700)
#define PR_ATTACH_DATA_OBJ PROP_TAG( PT_OBJECT, 0x3701)
#define PR_ATTACH_DATA_BIN PROP_TAG( PT_BINARY, 0x3701)
#define PR_ATTACH_ENCODING PROP_TAG( PT_BINARY, 0x3702)
#define PR_ATTACH_EXTENSION PROP_TAG( PT_TSTRING, 0x3703)
#define PR_ATTACH_EXTENSION_W PROP_TAG( PT_UNICODE, 0x3703)
#define PR_ATTACH_EXTENSION_A PROP_TAG( PT_STRING8, 0x3703)
#define PR_ATTACH_FILENAME PROP_TAG( PT_TSTRING, 0x3704)
#define PR_ATTACH_FILENAME_W PROP_TAG( PT_UNICODE, 0x3704)
#define PR_ATTACH_FILENAME_A PROP_TAG( PT_STRING8, 0x3704)
#define PR_ATTACH_METHOD PROP_TAG( PT_LONG, 0x3705)
#define PR_ATTACH_LONG_FILENAME PROP_TAG( PT_TSTRING, 0x3707)
#define PR_ATTACH_LONG_FILENAME_W PROP_TAG( PT_UNICODE, 0x3707)
#define PR_ATTACH_LONG_FILENAME_A PROP_TAG( PT_STRING8, 0x3707)
#define PR_ATTACH_PATHNAME PROP_TAG( PT_TSTRING, 0x3708)
#define PR_ATTACH_PATHNAME_W PROP_TAG( PT_UNICODE, 0x3708)
#define PR_ATTACH_PATHNAME_A PROP_TAG( PT_STRING8, 0x3708)
#define PR_ATTACH_RENDERING PROP_TAG( PT_BINARY, 0x3709)
#define PR_ATTACH_TAG PROP_TAG( PT_BINARY, 0x370A)
#define PR_RENDERING_POSITION PROP_TAG( PT_LONG, 0x370B)
#define PR_ATTACH_TRANSPORT_NAME PROP_TAG( PT_TSTRING, 0x370C)
#define PR_ATTACH_TRANSPORT_NAME_W PROP_TAG( PT_UNICODE, 0x370C)
#define PR_ATTACH_TRANSPORT_NAME_A PROP_TAG( PT_STRING8, 0x370C)
#define PR_ATTACH_LONG_PATHNAME PROP_TAG( PT_TSTRING, 0x370D)
#define PR_ATTACH_LONG_PATHNAME_W PROP_TAG( PT_UNICODE, 0x370D)
#define PR_ATTACH_LONG_PATHNAME_A PROP_TAG( PT_STRING8, 0x370D)
#define PR_ATTACH_MIME_TAG PROP_TAG( PT_TSTRING, 0x370E)
#define PR_ATTACH_MIME_TAG_W PROP_TAG( PT_UNICODE, 0x370E)
#define PR_ATTACH_MIME_TAG_A PROP_TAG( PT_STRING8, 0x370E)
#define PR_ATTACH_ADDITIONAL_INFO PROP_TAG( PT_BINARY, 0x370F)
#define PR_ATTACH_CONTENT_ID PROP_TAG( PT_UNICODE, 0x3712)
#define PR_DISPLAY_TYPE PROP_TAG( PT_LONG, 0x3900)
#define PR_TEMPLATEID PROP_TAG( PT_BINARY, 0x3902)
#define PR_PRIMARY_CAPABILITY PROP_TAG( PT_BINARY, 0x3904)
#define PR_7BIT_DISPLAY_NAME PROP_TAG( PT_STRING8, 0x39FF)
#define PR_ACCOUNT PROP_TAG( PT_TSTRING, 0x3A00)
#define PR_ACCOUNT_W PROP_TAG( PT_UNICODE, 0x3A00)
#define PR_ACCOUNT_A PROP_TAG( PT_STRING8, 0x3A00)
#define PR_ALTERNATE_RECIPIENT PROP_TAG( PT_BINARY, 0x3A01)
#define PR_CALLBACK_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A02)
#define PR_CALLBACK_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A02)
#define PR_CALLBACK_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A02)
#define PR_CONVERSION_PROHIBITED PROP_TAG( PT_BOOLEAN, 0x3A03)
#define PR_DISCLOSE_RECIPIENTS PROP_TAG( PT_BOOLEAN, 0x3A04)
#define PR_GENERATION PROP_TAG( PT_TSTRING, 0x3A05)
#define PR_GENERATION_W PROP_TAG( PT_UNICODE, 0x3A05)
#define PR_GENERATION_A PROP_TAG( PT_STRING8, 0x3A05)
#define PR_GIVEN_NAME PROP_TAG( PT_TSTRING, 0x3A06)
#define PR_GIVEN_NAME_W PROP_TAG( PT_UNICODE, 0x3A06)
#define PR_GIVEN_NAME_A PROP_TAG( PT_STRING8, 0x3A06)
#define PR_GOVERNMENT_ID_NUMBER PROP_TAG( PT_TSTRING, 0x3A07)
#define PR_GOVERNMENT_ID_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A07)
#define PR_GOVERNMENT_ID_NUMBER_A PROP_TAG( PT_STRING8, 0x3A07)
#define PR_BUSINESS_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A08)
#define PR_BUSINESS_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A08)
#define PR_BUSINESS_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A08)
#define PR_OFFICE_TELEPHONE_NUMBER PR_BUSINESS_TELEPHONE_NUMBER
#define PR_OFFICE_TELEPHONE_NUMBER_W PR_BUSINESS_TELEPHONE_NUMBER_W
#define PR_OFFICE_TELEPHONE_NUMBER_A PR_BUSINESS_TELEPHONE_NUMBER_A
#define PR_HOME_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A09)
#define PR_HOME_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A09)
#define PR_HOME_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A09)
#define PR_INITIALS PROP_TAG( PT_TSTRING, 0x3A0A)
#define PR_INITIALS_W PROP_TAG( PT_UNICODE, 0x3A0A)
#define PR_INITIALS_A PROP_TAG( PT_STRING8, 0x3A0A)
#define PR_KEYWORD PROP_TAG( PT_TSTRING, 0x3A0B)
#define PR_KEYWORD_W PROP_TAG( PT_UNICODE, 0x3A0B)
#define PR_KEYWORD_A PROP_TAG( PT_STRING8, 0x3A0B)
#define PR_LANGUAGE PROP_TAG( PT_TSTRING, 0x3A0C)
#define PR_LANGUAGE_W PROP_TAG( PT_UNICODE, 0x3A0C)
#define PR_LANGUAGE_A PROP_TAG( PT_STRING8, 0x3A0C)
#define PR_LOCATION PROP_TAG( PT_TSTRING, 0x3A0D)
#define PR_LOCATION_W PROP_TAG( PT_UNICODE, 0x3A0D)
#define PR_LOCATION_A PROP_TAG( PT_STRING8, 0x3A0D)
#define PR_MAIL_PERMISSION PROP_TAG( PT_BOOLEAN, 0x3A0E)
#define PR_MHS_COMMON_NAME PROP_TAG( PT_TSTRING, 0x3A0F)
#define PR_MHS_COMMON_NAME_W PROP_TAG( PT_UNICODE, 0x3A0F)
#define PR_MHS_COMMON_NAME_A PROP_TAG( PT_STRING8, 0x3A0F)
#define PR_ORGANIZATIONAL_ID_NUMBER PROP_TAG( PT_TSTRING, 0x3A10)
#define PR_ORGANIZATIONAL_ID_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A10)
#define PR_ORGANIZATIONAL_ID_NUMBER_A PROP_TAG( PT_STRING8, 0x3A10)
#define PR_SURNAME PROP_TAG( PT_TSTRING, 0x3A11)
#define PR_SURNAME_W PROP_TAG( PT_UNICODE, 0x3A11)
#define PR_SURNAME_A PROP_TAG( PT_STRING8, 0x3A11)
#define PR_ORIGINAL_ENTRYID PROP_TAG( PT_BINARY, 0x3A12)
#define PR_ORIGINAL_DISPLAY_NAME PROP_TAG( PT_TSTRING, 0x3A13)
#define PR_ORIGINAL_DISPLAY_NAME_W PROP_TAG( PT_UNICODE, 0x3A13)
#define PR_ORIGINAL_DISPLAY_NAME_A PROP_TAG( PT_STRING8, 0x3A13)
#define PR_ORIGINAL_SEARCH_KEY PROP_TAG( PT_BINARY, 0x3A14)
#define PR_POSTAL_ADDRESS PROP_TAG( PT_TSTRING, 0x3A15)
#define PR_POSTAL_ADDRESS_W PROP_TAG( PT_UNICODE, 0x3A15)
#define PR_POSTAL_ADDRESS_A PROP_TAG( PT_STRING8, 0x3A15)
#define PR_COMPANY_NAME PROP_TAG( PT_TSTRING, 0x3A16)
#define PR_COMPANY_NAME_W PROP_TAG( PT_UNICODE, 0x3A16)
#define PR_COMPANY_NAME_A PROP_TAG( PT_STRING8, 0x3A16)
#define PR_TITLE PROP_TAG( PT_TSTRING, 0x3A17)
#define PR_TITLE_W PROP_TAG( PT_UNICODE, 0x3A17)
#define PR_TITLE_A PROP_TAG( PT_STRING8, 0x3A17)
#define PR_DEPARTMENT_NAME PROP_TAG( PT_TSTRING, 0x3A18)
#define PR_DEPARTMENT_NAME_W PROP_TAG( PT_UNICODE, 0x3A18)
#define PR_DEPARTMENT_NAME_A PROP_TAG( PT_STRING8, 0x3A18)
#define PR_OFFICE_LOCATION PROP_TAG( PT_TSTRING, 0x3A19)
#define PR_OFFICE_LOCATION_W PROP_TAG( PT_UNICODE, 0x3A19)
#define PR_OFFICE_LOCATION_A PROP_TAG( PT_STRING8, 0x3A19)
#define PR_PRIMARY_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A1A)
#define PR_PRIMARY_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A1A)
#define PR_PRIMARY_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A1A)
#define PR_BUSINESS2_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A1B)
#define PR_BUSINESS2_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A1B)
#define PR_BUSINESS2_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A1B)
#define PR_OFFICE2_TELEPHONE_NUMBER PR_BUSINESS2_TELEPHONE_NUMBER
#define PR_OFFICE2_TELEPHONE_NUMBER_W PR_BUSINESS2_TELEPHONE_NUMBER_W
#define PR_OFFICE2_TELEPHONE_NUMBER_A PR_BUSINESS2_TELEPHONE_NUMBER_A
#define PR_MOBILE_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A1C)
#define PR_MOBILE_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A1C)
#define PR_MOBILE_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A1C)
#define PR_CELLULAR_TELEPHONE_NUMBER PR_MOBILE_TELEPHONE_NUMBER
#define PR_CELLULAR_TELEPHONE_NUMBER_W PR_MOBILE_TELEPHONE_NUMBER_W
#define PR_CELLULAR_TELEPHONE_NUMBER_A PR_MOBILE_TELEPHONE_NUMBER_A
#define PR_RADIO_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A1D)
#define PR_RADIO_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A1D)
#define PR_RADIO_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A1D)
#define PR_CAR_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A1E)
#define PR_CAR_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A1E)
#define PR_CAR_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A1E)
#define PR_OTHER_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A1F)
#define PR_OTHER_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A1F)
#define PR_OTHER_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A1F)
#define PR_TRANSMITABLE_DISPLAY_NAME PROP_TAG( PT_TSTRING, 0x3A20)
#define PR_TRANSMITABLE_DISPLAY_NAME_W PROP_TAG( PT_UNICODE, 0x3A20)
#define PR_TRANSMITABLE_DISPLAY_NAME_A PROP_TAG( PT_STRING8, 0x3A20)
#define PR_PAGER_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A21)
#define PR_PAGER_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A21)
#define PR_PAGER_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A21)
#define PR_BEEPER_TELEPHONE_NUMBER PR_PAGER_TELEPHONE_NUMBER
#define PR_BEEPER_TELEPHONE_NUMBER_W PR_PAGER_TELEPHONE_NUMBER_W
#define PR_BEEPER_TELEPHONE_NUMBER_A PR_PAGER_TELEPHONE_NUMBER_A
#define PR_USER_CERTIFICATE PROP_TAG( PT_BINARY, 0x3A22)
#define PR_PRIMARY_FAX_NUMBER PROP_TAG( PT_TSTRING, 0x3A23)
#define PR_PRIMARY_FAX_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A23)
#define PR_PRIMARY_FAX_NUMBER_A PROP_TAG( PT_STRING8, 0x3A23)
#define PR_BUSINESS_FAX_NUMBER PROP_TAG( PT_TSTRING, 0x3A24)
#define PR_BUSINESS_FAX_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A24)
#define PR_BUSINESS_FAX_NUMBER_A PROP_TAG( PT_STRING8, 0x3A24)
#define PR_HOME_FAX_NUMBER PROP_TAG( PT_TSTRING, 0x3A25)
#define PR_HOME_FAX_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A25)
#define PR_HOME_FAX_NUMBER_A PROP_TAG( PT_STRING8, 0x3A25)
#define PR_COUNTRY PROP_TAG( PT_TSTRING, 0x3A26)
#define PR_COUNTRY_W PROP_TAG( PT_UNICODE, 0x3A26)
#define PR_COUNTRY_A PROP_TAG( PT_STRING8, 0x3A26)
#define PR_BUSINESS_ADDRESS_COUNTRY PR_COUNTRY
#define PR_BUSINESS_ADDRESS_COUNTRY_W PR_COUNTRY_W
#define PR_BUSINESS_ADDRESS_COUNTRY_A PR_COUNTRY_A
#define PR_LOCALITY PROP_TAG( PT_TSTRING, 0x3A27)
#define PR_LOCALITY_W PROP_TAG( PT_UNICODE, 0x3A27)
#define PR_LOCALITY_A PROP_TAG( PT_STRING8, 0x3A27)
#define PR_BUSINESS_ADDRESS_CITY PR_LOCALITY
#define PR_BUSINESS_ADDRESS_CITY_W PR_LOCALITY_W
#define PR_BUSINESS_ADDRESS_CITY_A PR_LOCALITY_A
#define PR_STATE_OR_PROVINCE PROP_TAG( PT_TSTRING, 0x3A28)
#define PR_STATE_OR_PROVINCE_W PROP_TAG( PT_UNICODE, 0x3A28)
#define PR_STATE_OR_PROVINCE_A PROP_TAG( PT_STRING8, 0x3A28)
#define PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE PR_STATE_OR_PROVINCE
#define PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE_W PR_STATE_OR_PROVINCE_W
#define PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE_A PR_STATE_OR_PROVINCE_A
#define PR_STREET_ADDRESS PROP_TAG( PT_TSTRING, 0x3A29)
#define PR_STREET_ADDRESS_W PROP_TAG( PT_UNICODE, 0x3A29)
#define PR_STREET_ADDRESS_A PROP_TAG( PT_STRING8, 0x3A29)
#define PR_BUSINESS_ADDRESS_STREET PR_STREET_ADDRESS
#define PR_BUSINESS_ADDRESS_STREET_W PR_STREET_ADDRESS_W
#define PR_BUSINESS_ADDRESS_STREET_A PR_STREET_ADDRESS_A
#define PR_POSTAL_CODE PROP_TAG( PT_TSTRING, 0x3A2A)
#define PR_POSTAL_CODE_W PROP_TAG( PT_UNICODE, 0x3A2A)
#define PR_POSTAL_CODE_A PROP_TAG( PT_STRING8, 0x3A2A)
#define PR_BUSINESS_ADDRESS_POSTAL_CODE PR_POSTAL_CODE
#define PR_BUSINESS_ADDRESS_POSTAL_CODE_W PR_POSTAL_CODE_W
#define PR_BUSINESS_ADDRESS_POSTAL_CODE_A PR_POSTAL_CODE_A
#define PR_POST_OFFICE_BOX PROP_TAG( PT_TSTRING, 0x3A2B)
#define PR_POST_OFFICE_BOX_W PROP_TAG( PT_UNICODE, 0x3A2B)
#define PR_POST_OFFICE_BOX_A PROP_TAG( PT_STRING8, 0x3A2B)
#define PR_BUSINESS_ADDRESS_POST_OFFICE_BOX PR_POST_OFFICE_BOX
#define PR_BUSINESS_ADDRESS_POST_OFFICE_BOX_W PR_POST_OFFICE_BOX_W
#define PR_BUSINESS_ADDRESS_POST_OFFICE_BOX_A PR_POST_OFFICE_BOX_A
#define PR_TELEX_NUMBER PROP_TAG( PT_TSTRING, 0x3A2C)
#define PR_TELEX_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A2C)
#define PR_TELEX_NUMBER_A PROP_TAG( PT_STRING8, 0x3A2C)
#define PR_ISDN_NUMBER PROP_TAG( PT_TSTRING, 0x3A2D)
#define PR_ISDN_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A2D)
#define PR_ISDN_NUMBER_A PROP_TAG( PT_STRING8, 0x3A2D)
#define PR_ASSISTANT_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A2E)
#define PR_ASSISTANT_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A2E)
#define PR_ASSISTANT_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A2E)
#define PR_HOME2_TELEPHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A2F)
#define PR_HOME2_TELEPHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A2F)
#define PR_HOME2_TELEPHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A2F)
#define PR_ASSISTANT PROP_TAG( PT_TSTRING, 0x3A30)
#define PR_ASSISTANT_W PROP_TAG( PT_UNICODE, 0x3A30)
#define PR_ASSISTANT_A PROP_TAG( PT_STRING8, 0x3A30)
#define PR_SEND_RICH_INFO PROP_TAG( PT_BOOLEAN, 0x3A40)
#define PR_WEDDING_ANNIVERSARY PROP_TAG( PT_SYSTIME, 0x3A41)
#define PR_BIRTHDAY PROP_TAG( PT_SYSTIME, 0x3A42)
#define PR_HOBBIES PROP_TAG( PT_TSTRING, 0x3A43)
#define PR_HOBBIES_W PROP_TAG( PT_UNICODE, 0x3A43)
#define PR_HOBBIES_A PROP_TAG( PT_STRING8, 0x3A43)
#define PR_MIDDLE_NAME PROP_TAG( PT_TSTRING, 0x3A44)
#define PR_MIDDLE_NAME_W PROP_TAG( PT_UNICODE, 0x3A44)
#define PR_MIDDLE_NAME_A PROP_TAG( PT_STRING8, 0x3A44)
#define PR_DISPLAY_NAME_PREFIX PROP_TAG( PT_TSTRING, 0x3A45)
#define PR_DISPLAY_NAME_PREFIX_W PROP_TAG( PT_UNICODE, 0x3A45)
#define PR_DISPLAY_NAME_PREFIX_A PROP_TAG( PT_STRING8, 0x3A45)
#define PR_PROFESSION PROP_TAG( PT_TSTRING, 0x3A46)
#define PR_PROFESSION_W PROP_TAG( PT_UNICODE, 0x3A46)
#define PR_PROFESSION_A PROP_TAG( PT_STRING8, 0x3A46)
#define PR_PREFERRED_BY_NAME PROP_TAG( PT_TSTRING, 0x3A47)
#define PR_PREFERRED_BY_NAME_W PROP_TAG( PT_UNICODE, 0x3A47)
#define PR_PREFERRED_BY_NAME_A PROP_TAG( PT_STRING8, 0x3A47)
#define PR_SPOUSE_NAME PROP_TAG( PT_TSTRING, 0x3A48)
#define PR_SPOUSE_NAME_W PROP_TAG( PT_UNICODE, 0x3A48)
#define PR_SPOUSE_NAME_A PROP_TAG( PT_STRING8, 0x3A48)
#define PR_COMPUTER_NETWORK_NAME PROP_TAG( PT_TSTRING, 0x3A49)
#define PR_COMPUTER_NETWORK_NAME_W PROP_TAG( PT_UNICODE, 0x3A49)
#define PR_COMPUTER_NETWORK_NAME_A PROP_TAG( PT_STRING8, 0x3A49)
#define PR_CUSTOMER_ID PROP_TAG( PT_TSTRING, 0x3A4A)
#define PR_CUSTOMER_ID_W PROP_TAG( PT_UNICODE, 0x3A4A)
#define PR_CUSTOMER_ID_A PROP_TAG( PT_STRING8, 0x3A4A)
#define PR_TTYTDD_PHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A4B)
#define PR_TTYTDD_PHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A4B)
#define PR_TTYTDD_PHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A4B)
#define PR_FTP_SITE PROP_TAG( PT_TSTRING, 0x3A4C)
#define PR_FTP_SITE_W PROP_TAG( PT_UNICODE, 0x3A4C)
#define PR_FTP_SITE_A PROP_TAG( PT_STRING8, 0x3A4C)
#define PR_GENDER PROP_TAG( PT_SHORT, 0x3A4D)
#define PR_MANAGER_NAME PROP_TAG( PT_TSTRING, 0x3A4E)
#define PR_MANAGER_NAME_W PROP_TAG( PT_UNICODE, 0x3A4E)
#define PR_MANAGER_NAME_A PROP_TAG( PT_STRING8, 0x3A4E)
#define PR_NICKNAME PROP_TAG( PT_TSTRING, 0x3A4F)
#define PR_NICKNAME_W PROP_TAG( PT_UNICODE, 0x3A4F)
#define PR_NICKNAME_A PROP_TAG( PT_STRING8, 0x3A4F)
#define PR_PERSONAL_HOME_PAGE PROP_TAG( PT_TSTRING, 0x3A50)
#define PR_PERSONAL_HOME_PAGE_W PROP_TAG( PT_UNICODE, 0x3A50)
#define PR_PERSONAL_HOME_PAGE_A PROP_TAG( PT_STRING8, 0x3A50)
#define PR_BUSINESS_HOME_PAGE PROP_TAG( PT_TSTRING, 0x3A51)
#define PR_BUSINESS_HOME_PAGE_W PROP_TAG( PT_UNICODE, 0x3A51)
#define PR_BUSINESS_HOME_PAGE_A PROP_TAG( PT_STRING8, 0x3A51)
#define PR_CONTACT_VERSION PROP_TAG( PT_CLSID, 0x3A52)
#define PR_CONTACT_ENTRYIDS PROP_TAG( PT_MV_BINARY, 0x3A53)
#define PR_CONTACT_ADDRTYPES PROP_TAG( PT_MV_TSTRING, 0x3A54)
#define PR_CONTACT_ADDRTYPES_W PROP_TAG( PT_MV_UNICODE, 0x3A54)
#define PR_CONTACT_ADDRTYPES_A PROP_TAG( PT_MV_STRING8, 0x3A54)
#define PR_CONTACT_DEFAULT_ADDRESS_INDEX PROP_TAG( PT_LONG, 0x3A55)
#define PR_CONTACT_EMAIL_ADDRESSES PROP_TAG( PT_MV_TSTRING, 0x3A56)
#define PR_CONTACT_EMAIL_ADDRESSES_W PROP_TAG( PT_MV_UNICODE, 0x3A56)
#define PR_CONTACT_EMAIL_ADDRESSES_A PROP_TAG( PT_MV_STRING8, 0x3A56)
#define PR_COMPANY_MAIN_PHONE_NUMBER PROP_TAG( PT_TSTRING, 0x3A57)
#define PR_COMPANY_MAIN_PHONE_NUMBER_W PROP_TAG( PT_UNICODE, 0x3A57)
#define PR_COMPANY_MAIN_PHONE_NUMBER_A PROP_TAG( PT_STRING8, 0x3A57)
#define PR_CHILDRENS_NAMES PROP_TAG( PT_MV_TSTRING, 0x3A58)
#define PR_CHILDRENS_NAMES_W PROP_TAG( PT_MV_UNICODE, 0x3A58)
#define PR_CHILDRENS_NAMES_A PROP_TAG( PT_MV_STRING8, 0x3A58)
#define PR_HOME_ADDRESS_CITY PROP_TAG( PT_TSTRING, 0x3A59)
#define PR_HOME_ADDRESS_CITY_W PROP_TAG( PT_UNICODE, 0x3A59)
#define PR_HOME_ADDRESS_CITY_A PROP_TAG( PT_STRING8, 0x3A59)
#define PR_HOME_ADDRESS_COUNTRY PROP_TAG( PT_TSTRING, 0x3A5A)
#define PR_HOME_ADDRESS_COUNTRY_W PROP_TAG( PT_UNICODE, 0x3A5A)
#define PR_HOME_ADDRESS_COUNTRY_A PROP_TAG( PT_STRING8, 0x3A5A)
#define PR_HOME_ADDRESS_POSTAL_CODE PROP_TAG( PT_TSTRING, 0x3A5B)
#define PR_HOME_ADDRESS_POSTAL_CODE_W PROP_TAG( PT_UNICODE, 0x3A5B)
#define PR_HOME_ADDRESS_POSTAL_CODE_A PROP_TAG( PT_STRING8, 0x3A5B)
#define PR_HOME_ADDRESS_STATE_OR_PROVINCE PROP_TAG( PT_TSTRING, 0x3A5C)
#define PR_HOME_ADDRESS_STATE_OR_PROVINCE_W PROP_TAG( PT_UNICODE, 0x3A5C)
#define PR_HOME_ADDRESS_STATE_OR_PROVINCE_A PROP_TAG( PT_STRING8, 0x3A5C)
#define PR_HOME_ADDRESS_STREET PROP_TAG( PT_TSTRING, 0x3A5D)
#define PR_HOME_ADDRESS_STREET_W PROP_TAG( PT_UNICODE, 0x3A5D)
#define PR_HOME_ADDRESS_STREET_A PROP_TAG( PT_STRING8, 0x3A5D)
#define PR_HOME_ADDRESS_POST_OFFICE_BOX PROP_TAG( PT_TSTRING, 0x3A5E)
#define PR_HOME_ADDRESS_POST_OFFICE_BOX_W PROP_TAG( PT_UNICODE, 0x3A5E)
#define PR_HOME_ADDRESS_POST_OFFICE_BOX_A PROP_TAG( PT_STRING8, 0x3A5E)
#define PR_OTHER_ADDRESS_CITY PROP_TAG( PT_TSTRING, 0x3A5F)
#define PR_OTHER_ADDRESS_CITY_W PROP_TAG( PT_UNICODE, 0x3A5F)
#define PR_OTHER_ADDRESS_CITY_A PROP_TAG( PT_STRING8, 0x3A5F)
#define PR_OTHER_ADDRESS_COUNTRY PROP_TAG( PT_TSTRING, 0x3A60)
#define PR_OTHER_ADDRESS_COUNTRY_W PROP_TAG( PT_UNICODE, 0x3A60)
#define PR_OTHER_ADDRESS_COUNTRY_A PROP_TAG( PT_STRING8, 0x3A60)
#define PR_OTHER_ADDRESS_POSTAL_CODE PROP_TAG( PT_TSTRING, 0x3A61)
#define PR_OTHER_ADDRESS_POSTAL_CODE_W PROP_TAG( PT_UNICODE, 0x3A61)
#define PR_OTHER_ADDRESS_POSTAL_CODE_A PROP_TAG( PT_STRING8, 0x3A61)
#define PR_OTHER_ADDRESS_STATE_OR_PROVINCE PROP_TAG( PT_TSTRING, 0x3A62)
#define PR_OTHER_ADDRESS_STATE_OR_PROVINCE_W PROP_TAG( PT_UNICODE, 0x3A62)
#define PR_OTHER_ADDRESS_STATE_OR_PROVINCE_A PROP_TAG( PT_STRING8, 0x3A62)
#define PR_OTHER_ADDRESS_STREET PROP_TAG( PT_TSTRING, 0x3A63)
#define PR_OTHER_ADDRESS_STREET_W PROP_TAG( PT_UNICODE, 0x3A63)
#define PR_OTHER_ADDRESS_STREET_A PROP_TAG( PT_STRING8, 0x3A63)
#define PR_OTHER_ADDRESS_POST_OFFICE_BOX PROP_TAG( PT_TSTRING, 0x3A64)
#define PR_OTHER_ADDRESS_POST_OFFICE_BOX_W PROP_TAG( PT_UNICODE, 0x3A64)
#define PR_OTHER_ADDRESS_POST_OFFICE_BOX_A PROP_TAG( PT_STRING8, 0x3A64)
#define PR_STORE_PROVIDERS PROP_TAG( PT_BINARY, 0x3D00)
#define PR_AB_PROVIDERS PROP_TAG( PT_BINARY, 0x3D01)
#define PR_TRANSPORT_PROVIDERS PROP_TAG( PT_BINARY, 0x3D02)
#define PR_DEFAULT_PROFILE PROP_TAG( PT_BOOLEAN, 0x3D04)
#define PR_AB_SEARCH_PATH PROP_TAG( PT_MV_BINARY, 0x3D05)
#define PR_AB_DEFAULT_DIR PROP_TAG( PT_BINARY, 0x3D06)
#define PR_AB_DEFAULT_PAB PROP_TAG( PT_BINARY, 0x3D07)
#define PR_FILTERING_HOOKS PROP_TAG( PT_BINARY, 0x3D08)
#define PR_SERVICE_NAME PROP_TAG( PT_TSTRING, 0x3D09)
#define PR_SERVICE_NAME_W PROP_TAG( PT_UNICODE, 0x3D09)
#define PR_SERVICE_NAME_A PROP_TAG( PT_STRING8, 0x3D09)
#define PR_SERVICE_DLL_NAME PROP_TAG( PT_TSTRING, 0x3D0A)
#define PR_SERVICE_DLL_NAME_W PROP_TAG( PT_UNICODE, 0x3D0A)
#define PR_SERVICE_DLL_NAME_A PROP_TAG( PT_STRING8, 0x3D0A)
#define PR_SERVICE_ENTRY_NAME PROP_TAG( PT_STRING8, 0x3D0B)
#define PR_SERVICE_UID PROP_TAG( PT_BINARY, 0x3D0C)
#define PR_SERVICE_EXTRA_UIDS PROP_TAG( PT_BINARY, 0x3D0D)
#define PR_SERVICES PROP_TAG( PT_BINARY, 0x3D0E)
#define PR_SERVICE_SUPPORT_FILES PROP_TAG( PT_MV_TSTRING, 0x3D0F)
#define PR_SERVICE_SUPPORT_FILES_W PROP_TAG( PT_MV_UNICODE, 0x3D0F)
#define PR_SERVICE_SUPPORT_FILES_A PROP_TAG( PT_MV_STRING8, 0x3D0F)
#define PR_SERVICE_DELETE_FILES PROP_TAG( PT_MV_TSTRING, 0x3D10)
#define PR_SERVICE_DELETE_FILES_W PROP_TAG( PT_MV_UNICODE, 0x3D10)
#define PR_SERVICE_DELETE_FILES_A PROP_TAG( PT_MV_STRING8, 0x3D10)
#define PR_AB_SEARCH_PATH_UPDATE PROP_TAG( PT_BINARY, 0x3D11)
#define PR_PROFILE_NAME PROP_TAG( PT_TSTRING, 0x3D12)
#define PR_PROFILE_NAME_A PROP_TAG( PT_STRING8, 0x3D12)
#define PR_PROFILE_NAME_W PROP_TAG( PT_UNICODE, 0x3D12)
#define PR_IDENTITY_DISPLAY PROP_TAG( PT_TSTRING, 0x3E00)
#define PR_IDENTITY_DISPLAY_W PROP_TAG( PT_UNICODE, 0x3E00)
#define PR_IDENTITY_DISPLAY_A PROP_TAG( PT_STRING8, 0x3E00)
#define PR_IDENTITY_ENTRYID PROP_TAG( PT_BINARY, 0x3E01)
#define PR_RESOURCE_METHODS PROP_TAG( PT_LONG, 0x3E02)
#define PR_RESOURCE_TYPE PROP_TAG( PT_LONG, 0x3E03)
#define PR_STATUS_CODE PROP_TAG( PT_LONG, 0x3E04)
#define PR_IDENTITY_SEARCH_KEY PROP_TAG( PT_BINARY, 0x3E05)
#define PR_OWN_STORE_ENTRYID PROP_TAG( PT_BINARY, 0x3E06)
#define PR_RESOURCE_PATH PROP_TAG( PT_TSTRING, 0x3E07)
#define PR_RESOURCE_PATH_W PROP_TAG( PT_UNICODE, 0x3E07)
#define PR_RESOURCE_PATH_A PROP_TAG( PT_STRING8, 0x3E07)
#define PR_STATUS_STRING PROP_TAG( PT_TSTRING, 0x3E08)
#define PR_STATUS_STRING_W PROP_TAG( PT_UNICODE, 0x3E08)
#define PR_STATUS_STRING_A PROP_TAG( PT_STRING8, 0x3E08)
#define PR_X400_DEFERRED_DELIVERY_CANCEL PROP_TAG( PT_BOOLEAN, 0x3E09)
#define PR_HEADER_FOLDER_ENTRYID PROP_TAG( PT_BINARY, 0x3E0A)
#define PR_REMOTE_PROGRESS PROP_TAG( PT_LONG, 0x3E0B)
#define PR_REMOTE_PROGRESS_TEXT PROP_TAG( PT_TSTRING, 0x3E0C)
#define PR_REMOTE_PROGRESS_TEXT_W PROP_TAG( PT_UNICODE, 0x3E0C)
#define PR_REMOTE_PROGRESS_TEXT_A PROP_TAG( PT_STRING8, 0x3E0C)
#define PR_REMOTE_VALIDATE_OK PROP_TAG( PT_BOOLEAN, 0x3E0D)
#define PR_CONTROL_FLAGS PROP_TAG( PT_LONG, 0x3F00)
#define PR_CONTROL_STRUCTURE PROP_TAG( PT_BINARY, 0x3F01)
#define PR_CONTROL_TYPE PROP_TAG( PT_LONG, 0x3F02)
#define PR_DELTAX PROP_TAG( PT_LONG, 0x3F03)
#define PR_DELTAY PROP_TAG( PT_LONG, 0x3F04)
#define PR_XPOS PROP_TAG( PT_LONG, 0x3F05)
#define PR_YPOS PROP_TAG( PT_LONG, 0x3F06)
#define PR_CONTROL_ID PROP_TAG( PT_BINARY, 0x3F07)
#define PR_INITIAL_DETAILS_PANE PROP_TAG( PT_LONG, 0x3F08)
#define PR_MSG_EDITOR_FORMAT PROP_TAG( PT_LONG, 0x5903)
#define PR_ATTACHMENT_HIDDEN PROP_TAG( PT_BOOLEAN, 0x7ffe)
#define PR_SMTP_ADDRESS PROP_TAG( PT_TSTRING, 0x39fe)
#define PR_SMTP_ADDRESS_W PROP_TAG( PT_UNICODE, 0x39fe)
#define PR_SMTP_ADDRESS_A PROP_TAG( PT_STRING8, 0x39fe)
#define PR_SENT_REPRESENTING_SMTP_ADDRESS PROP_TAG( PT_TSTRING, 0x5d02)
#define PR_SENT_REPRESENTING_SMTP_ADDRESS_A PROP_TAG( PT_STRING8, 0x5d02)
#define PR_SENT_REPRESENTING_SMTP_ADDRESS_W PROP_TAG( PT_UNICODE, 0x5d02)
#define PidTagSenderSmtpAddress_W PROP_TAG( PT_UNICODE, 0x5d01)
+#define PR_BLOCK_STATUS PROP_TAG( PT_LONG, 0x1096)
#define PROP_ID_SECURE_MIN 0x67F0
#define PROP_ID_SECURE_MAX 0x67FF
#define pidExchangeXmitReservedMin 0x3FE0
#define pidExchangeNonXmitReservedMin 0x65E0
#define pidProfileMin 0x6600
#define pidStoreMin 0x6618
#define pidFolderMin 0x6638
#define pidMessageReadOnlyMin 0x6640
#define pidMessageWriteableMin 0x6658
#define pidAttachReadOnlyMin 0x666C
#define pidSpecialMin 0x6670
#define pidAdminMin 0x6690
#define pidSecureProfileMin PROP_ID_SECURE_MIN
#define PR_PROFILE_VERSION PROP_TAG( PT_LONG, pidProfileMin+0x00)
#define PR_PROFILE_CONFIG_FLAGS PROP_TAG( PT_LONG, pidProfileMin+0x01)
#define PR_PROFILE_HOME_SERVER PROP_TAG( PT_STRING8, pidProfileMin+0x02)
#define PR_PROFILE_HOME_SERVER_DN PROP_TAG( PT_STRING8, pidProfileMin+0x12)
#define PR_PROFILE_HOME_SERVER_ADDRS PROP_TAG( PT_MV_STRING8, \
pidProfileMin+0x13)
#define PR_PROFILE_USER PROP_TAG( PT_STRING8, pidProfileMin+0x03)
#define PR_PROFILE_CONNECT_FLAGS PROP_TAG( PT_LONG, pidProfileMin+0x04)
#define PR_PROFILE_TRANSPORT_FLAGS PROP_TAG( PT_LONG, pidProfileMin+0x05)
#define PR_PROFILE_UI_STATE PROP_TAG( PT_LONG, pidProfileMin+0x06)
#define PR_PROFILE_UNRESOLVED_NAME PROP_TAG( PT_STRING8, pidProfileMin+0x07)
#define PR_PROFILE_UNRESOLVED_SERVER PROP_TAG( PT_STRING8, pidProfileMin+0x08)
#define PR_PROFILE_BINDING_ORDER PROP_TAG( PT_STRING8, pidProfileMin+0x09)
#define PR_PROFILE_MAX_RESTRICT PROP_TAG( PT_LONG, pidProfileMin+0x0D)
#define PR_PROFILE_AB_FILES_PATH PROP_TAG( PT_STRING8, pidProfileMin+0xE)
#define PR_PROFILE_OFFLINE_STORE_PATH PROP_TAG( PT_STRING8,pidProfileMin+0x10)
#define PR_PROFILE_OFFLINE_INFO PROP_TAG( PT_BINARY, pidProfileMin+0x11)
#define PR_PROFILE_ADDR_INFO PROP_TAG( PT_BINARY, pidSpecialMin+0x17)
#define PR_PROFILE_OPTIONS_DATA PROP_TAG( PT_BINARY, pidSpecialMin+0x19)
#define PR_PROFILE_SECURE_MAILBOX PROP_TAG( PT_BINARY, \
pidSecureProfileMin + 0)
#define PR_DISABLE_WINSOCK PROP_TAG( PT_LONG, pidProfileMin+0x18)
#define PR_OST_ENCRYPTION PROP_TAG( PT_LONG, 0x6702)
#define PR_PROFILE_OPEN_FLAGS PROP_TAG( PT_LONG, pidProfileMin+0x09)
#define PR_PROFILE_TYPE PROP_TAG( PT_LONG, pidProfileMin+0x0A)
#define PR_PROFILE_MAILBOX PROP_TAG( PT_STRING8, pidProfileMin+0x0B)
#define PR_PROFILE_SERVER PROP_TAG( PT_STRING8, pidProfileMin+0x0C)
#define PR_PROFILE_SERVER_DN PROP_TAG( PT_STRING8, pidProfileMin+0x14)
#define PR_PROFILE_FAVFLD_DISPLAY_NAME PROP_TAG(PT_STRING8,pidProfileMin+0x0F)
#define PR_PROFILE_FAVFLD_COMMENT PROP_TAG(PT_STRING8, pidProfileMin+0x15)
#define PR_PROFILE_ALLPUB_DISPLAY_NAME PROP_TAG(PT_STRING8,pidProfileMin+0x16)
#define PR_PROFILE_ALLPUB_COMMENT PROP_TAG(PT_STRING8, pidProfileMin+0x17)
#define OSTF_NO_ENCRYPTION 0x80000000
#define OSTF_COMPRESSABLE_ENCRYPTION 0x40000000
#define OSTF_BEST_ENCRYPTION 0x20000000
#define PR_NON_IPM_SUBTREE_ENTRYID PROP_TAG( PT_BINARY, pidStoreMin+0x08)
#define PR_EFORMS_REGISTRY_ENTRYID PROP_TAG( PT_BINARY, pidStoreMin+0x09)
#define PR_SPLUS_FREE_BUSY_ENTRYID PROP_TAG( PT_BINARY, pidStoreMin+0x0A)
#define PR_OFFLINE_ADDRBOOK_ENTRYID PROP_TAG( PT_BINARY, pidStoreMin+0x0B)
#define PR_EFORMS_FOR_LOCALE_ENTRYID PROP_TAG( PT_BINARY, pidStoreMin+0x0C)
#define PR_FREE_BUSY_FOR_LOCAL_SITE_ENTRYID \
PROP_TAG( PT_BINARY, pidStoreMin+0x0D)
#define PR_ADDRBOOK_FOR_LOCAL_SITE_ENTRYID \
PROP_TAG( PT_BINARY, pidStoreMin+0x0E)
#define PR_OFFLINE_MESSAGE_ENTRYID PROP_TAG( PT_BINARY, pidStoreMin+0x0F)
#define PR_IPM_FAVORITES_ENTRYID PROP_TAG( PT_BINARY, pidStoreMin+0x18)
#define PR_IPM_PUBLIC_FOLDERS_ENTRYID PROP_TAG( PT_BINARY, pidStoreMin+0x19)
#define PR_GW_MTSIN_ENTRYID PROP_TAG( PT_BINARY, pidStoreMin+0x10)
#define PR_GW_MTSOUT_ENTRYID PROP_TAG( PT_BINARY, pidStoreMin+0x11)
#define PR_TRANSFER_ENABLED PROP_TAG( PT_BOOLEAN,pidStoreMin+0x12)
#define PR_TEST_LINE_SPEED PROP_TAG( PT_BINARY, pidStoreMin+0x13)
#define PR_HIERARCHY_SYNCHRONIZER PROP_TAG( PT_OBJECT, pidStoreMin+0x14)
#define PR_CONTENTS_SYNCHRONIZER PROP_TAG( PT_OBJECT, pidStoreMin+0x15)
#define PR_COLLECTOR PROP_TAG( PT_OBJECT, pidStoreMin+0x16)
#define PR_FAST_TRANSFER PROP_TAG( PT_OBJECT, pidStoreMin+0x17)
#define PR_STORE_OFFLINE PROP_TAG( PT_BOOLEAN,pidStoreMin+0x1A)
#define PR_IN_TRANSIT PROP_TAG( PT_BOOLEAN,pidStoreMin)
#define PR_REPLICATION_STYLE PROP_TAG( PT_LONG, pidAdminMin)
#define PR_REPLICATION_SCHEDULE PROP_TAG( PT_BINARY, pidAdminMin+0x01)
#define PR_REPLICATION_MESSAGE_PRIORITY PROP_TAG( PT_LONG, pidAdminMin+0x02)
#define PR_OVERALL_MSG_AGE_LIMIT PROP_TAG( PT_LONG, pidAdminMin+0x03 )
#define PR_REPLICATION_ALWAYS_INTERVAL PROP_TAG( PT_LONG, pidAdminMin+0x04 )
#define PR_REPLICATION_MSG_SIZE PROP_TAG( PT_LONG, pidAdminMin+0x05 )
#define REPLICATION_MESSAGE_SIZE_LIMIT_DEFAULT 100
#define STYLE_DEFAULT (-1)
#define STYLE_NEVER 0
#define STYLE_NORMAL 1
#define STYLE_ALWAYS 2
#define STYLE_ALWAYS_INTERVAL_DEFAULT 15
#define PR_SOURCE_KEY PROP_TAG( PT_BINARY, pidExchangeNonXmitReservedMin+0x0)
#define PR_PARENT_SOURCE_KEY \
PROP_TAG( PT_BINARY, pidExchangeNonXmitReservedMin+0x1)
#define PR_CHANGE_KEY PROP_TAG( PT_BINARY, pidExchangeNonXmitReservedMin+0x2)
#define PR_PREDECESSOR_CHANGE_LIST \
PROP_TAG( PT_BINARY, pidExchangeNonXmitReservedMin+0x3)
#define PR_FOLDER_CHILD_COUNT PROP_TAG( PT_LONG, pidFolderMin)
#define PR_RIGHTS PROP_TAG( PT_LONG, pidFolderMin+1)
#define PR_HAS_RULES PROP_TAG( PT_BOOLEAN, pidFolderMin+2)
#define PR_ACL_TABLE PROP_TAG( PT_OBJECT, pidExchangeXmitReservedMin)
#define PR_RULES_TABLE PROP_TAG( PT_OBJECT, pidExchangeXmitReservedMin+1)
#define PR_ADDRESS_BOOK_ENTRYID PROP_TAG( PT_BINARY, pidFolderMin+0x03)
#define PR_ACL_DATA PROP_TAG( PT_BINARY, pidExchangeXmitReservedMin)
#define PR_RULES_DATA PROP_TAG( PT_BINARY, pidExchangeXmitReservedMin+0x1)
#define PR_FOLDER_DESIGN_FLAGS \
PROP_TAG( PT_LONG, pidExchangeXmitReservedMin+0x2)
#define PR_DESIGN_IN_PROGRESS \
PROP_TAG( PT_BOOLEAN, pidExchangeXmitReservedMin+0x4)
#define PR_SECURE_ORIGINATION \
PROP_TAG( PT_BOOLEAN, pidExchangeXmitReservedMin+0x5)
#define PR_PUBLISH_IN_ADDRESS_BOOK \
PROP_TAG( PT_BOOLEAN, pidExchangeXmitReservedMin+0x6)
#define PR_RESOLVE_METHOD PROP_TAG( PT_LONG, pidExchangeXmitReservedMin+0x7)
#define PR_ADDRESS_BOOK_DISPLAY_NAME \
PROP_TAG( PT_TSTRING, pidExchangeXmitReservedMin+0x8)
#define PR_EFORMS_LOCALE_ID PROP_TAG( PT_LONG, pidExchangeXmitReservedMin+0x9)
#define PR_REPLICA_LIST PROP_TAG( PT_BINARY, pidAdminMin+0x8)
#define PR_OVERALL_AGE_LIMIT PROP_TAG( PT_LONG, pidAdminMin+0x9)
#define RESOLVE_METHOD_DEFAULT 0
#define RESOLVE_METHOD_LAST_WRITER_WINS 1
#define RESOLVE_METHOD_NO_CONFLICT_NOTIFICATION 2
#define PR_PUBLIC_FOLDER_ENTRYID PROP_TAG( PT_BINARY, pidFolderMin+0x04)
#define PR_HAS_NAMED_PROPERTIES \
PROP_TAG(PT_BOOLEAN, pidMessageReadOnlyMin+0x0A)
#define PR_CREATOR_NAME \
PROP_TAG(PT_TSTRING, pidExchangeXmitReservedMin+0x18)
#define PR_CREATOR_ENTRYID \
PROP_TAG(PT_BINARY, pidExchangeXmitReservedMin+0x19)
#define PR_LAST_MODIFIER_NAME \
PROP_TAG(PT_TSTRING, pidExchangeXmitReservedMin+0x1A)
#define PR_LAST_MODIFIER_ENTRYID \
PROP_TAG(PT_BINARY, pidExchangeXmitReservedMin+0x1B)
#define PR_HAS_DAMS \
PROP_TAG( PT_BOOLEAN, pidExchangeXmitReservedMin+0xA)
#define PR_RULE_TRIGGER_HISTORY \
PROP_TAG( PT_BINARY, pidExchangeXmitReservedMin+0x12)
#define PR_MOVE_TO_STORE_ENTRYID \
PROP_TAG( PT_BINARY, pidExchangeXmitReservedMin+0x13)
#define PR_MOVE_TO_FOLDER_ENTRYID \
PROP_TAG( PT_BINARY, pidExchangeXmitReservedMin+0x14)
#define PR_REPLICA_SERVER \
PROP_TAG(PT_TSTRING, pidMessageReadOnlyMin+0x4)
#define PR_DEFERRED_SEND_NUMBER \
PROP_TAG( PT_LONG, pidExchangeXmitReservedMin+0xB)
#define PR_DEFERRED_SEND_UNITS \
PROP_TAG( PT_LONG, pidExchangeXmitReservedMin+0xC)
#define PR_EXPIRY_NUMBER \
PROP_TAG( PT_LONG, pidExchangeXmitReservedMin+0xD)
#define PR_EXPIRY_UNITS \
PROP_TAG( PT_LONG, pidExchangeXmitReservedMin+0xE)
#define PR_DEFERRED_SEND_TIME \
PROP_TAG( PT_SYSTIME, pidExchangeXmitReservedMin+0xF)
#define PR_GW_ADMIN_OPERATIONS PROP_TAG( PT_LONG, pidMessageWriteableMin)
#define PR_P1_CONTENT PROP_TAG( PT_BINARY, 0x1100)
#define PR_P1_CONTENT_TYPE PROP_TAG( PT_BINARY, 0x1101)
#define PR_CLIENT_ACTIONS PROP_TAG(PT_BINARY, pidMessageReadOnlyMin+0x5)
#define PR_DAM_ORIGINAL_ENTRYID PROP_TAG(PT_BINARY, pidMessageReadOnlyMin+0x6)
#define PR_DAM_BACK_PATCHED PROP_TAG(PT_BOOLEAN, pidMessageReadOnlyMin+0x7)
#define PR_RULE_ERROR PROP_TAG(PT_LONG, pidMessageReadOnlyMin+0x8)
#define PR_RULE_ACTION_TYPE PROP_TAG(PT_LONG, pidMessageReadOnlyMin+0x9)
#define PR_RULE_ACTION_NUMBER PROP_TAG(PT_LONG, pidMessageReadOnlyMin+0x10)
#define PR_RULE_FOLDER_ENTRYID PROP_TAG(PT_BINARY, pidMessageReadOnlyMin+0x11)
#define PR_CONFLICT_ENTRYID \
PROP_TAG(PT_BINARY, pidExchangeXmitReservedMin+0x10)
#define PR_MESSAGE_LOCALE_ID \
PROP_TAG(PT_LONG, pidExchangeXmitReservedMin+0x11)
#define PR_STORAGE_QUOTA_LIMIT \
PROP_TAG(PT_LONG, pidExchangeXmitReservedMin+0x15)
#define PR_EXCESS_STORAGE_USED \
PROP_TAG(PT_LONG, pidExchangeXmitReservedMin+0x16)
#define PR_SVR_GENERATING_QUOTA_MSG \
PROP_TAG(PT_TSTRING, pidExchangeXmitReservedMin+0x17)
#define PR_DELEGATED_BY_RULE \
PROP_TAG( PT_BOOLEAN, pidExchangeXmitReservedMin+0x3)
#define MSGSTATUS_IN_CONFLICT 0x800
#define PR_IN_CONFLICT PROP_TAG(PT_BOOLEAN, pidAttachReadOnlyMin)
#define PR_LONGTERM_ENTRYID_FROM_TABLE PROP_TAG(PT_BINARY, pidSpecialMin)
#define PR_ORIGINATOR_NAME PROP_TAG( PT_TSTRING, pidMessageWriteableMin+0x3)
#define PR_ORIGINATOR_ADDR PROP_TAG( PT_TSTRING, pidMessageWriteableMin+0x4)
#define PR_ORIGINATOR_ADDRTYPE \
PROP_TAG( PT_TSTRING, pidMessageWriteableMin+0x5)
#define PR_ORIGINATOR_ENTRYID PROP_TAG( PT_BINARY, pidMessageWriteableMin+0x6)
#define PR_ARRIVAL_TIME PROP_TAG( PT_SYSTIME, pidMessageWriteableMin+0x7)
#define PR_TRACE_INFO PROP_TAG( PT_BINARY, pidMessageWriteableMin+0x8)
#define PR_INTERNAL_TRACE_INFO \
PROP_TAG( PT_BINARY, pidMessageWriteableMin+0x12)
#define PR_SUBJECT_TRACE_INFO PROP_TAG( PT_BINARY, pidMessageWriteableMin+0x9)
#define PR_RECIPIENT_NUMBER PROP_TAG( PT_LONG, pidMessageWriteableMin+0xA)
#define PR_MTS_SUBJECT_ID PROP_TAG(PT_BINARY, pidMessageWriteableMin+0xB)
#define PR_REPORT_DESTINATION_NAME \
PROP_TAG(PT_TSTRING, pidMessageWriteableMin+0xC)
#define PR_REPORT_DESTINATION_ENTRYID \
PROP_TAG(PT_BINARY, pidMessageWriteableMin+0xD)
#define PR_CONTENT_SEARCH_KEY PROP_TAG(PT_BINARY, pidMessageWriteableMin+0xE)
#define PR_FOREIGN_ID PROP_TAG(PT_BINARY, pidMessageWriteableMin+0xF)
#define PR_FOREIGN_REPORT_ID PROP_TAG(PT_BINARY, pidMessageWriteableMin+0x10)
#define PR_FOREIGN_SUBJECT_ID PROP_TAG(PT_BINARY, pidMessageWriteableMin+0x11)
#define PR_MTS_ID PR_MESSAGE_SUBMISSION_ID
#define PR_MTS_REPORT_ID PR_MESSAGE_SUBMISSION_ID
#define PR_FOLDER_FLAGS PROP_TAG( PT_LONG, pidAdminMin+0x18 )
#define PR_LAST_ACCESS_TIME PROP_TAG( PT_SYSTIME,pidAdminMin+0x19)
#define PR_RESTRICTION_COUNT PROP_TAG( PT_LONG, pidAdminMin+0x1A )
#define PR_CATEG_COUNT PROP_TAG( PT_LONG, pidAdminMin+0x1B )
#define PR_CACHED_COLUMN_COUNT PROP_TAG( PT_LONG, pidAdminMin+0x1C )
#define PR_NORMAL_MSG_W_ATTACH_COUNT PROP_TAG( PT_LONG, pidAdminMin+0x1D )
#define PR_ASSOC_MSG_W_ATTACH_COUNT PROP_TAG( PT_LONG, pidAdminMin+0x1E )
#define PR_RECIPIENT_ON_NORMAL_MSG_COUNT PROP_TAG( PT_LONG, pidAdminMin+0x1F )
#define PR_RECIPIENT_ON_ASSOC_MSG_COUNT PROP_TAG( PT_LONG, pidAdminMin+0x20 )
#define PR_ATTACH_ON_NORMAL_MSG_COUNT PROP_TAG( PT_LONG, pidAdminMin+0x21 )
#define PR_ATTACH_ON_ASSOC_MSG_COUNT PROP_TAG( PT_LONG, pidAdminMin+0x22 )
#define PR_NORMAL_MESSAGE_SIZE PROP_TAG( PT_LONG, pidAdminMin+0x23 )
#define PR_NORMAL_MESSAGE_SIZE_EXTENDED PROP_TAG( PT_I8, pidAdminMin+0x23 )
#define PR_ASSOC_MESSAGE_SIZE PROP_TAG( PT_LONG, pidAdminMin+0x24 )
#define PR_ASSOC_MESSAGE_SIZE_EXTENDED PROP_TAG( PT_I8, pidAdminMin+0x24 )
#define PR_FOLDER_PATHNAME PROP_TAG(PT_TSTRING, pidAdminMin+0x25 )
#define PR_OWNER_COUNT PROP_TAG( PT_LONG, pidAdminMin+0x26 )
#define PR_CONTACT_COUNT PROP_TAG( PT_LONG, pidAdminMin+0x27 )
#define PR_MESSAGE_SIZE_EXTENDED PROP_TAG(PT_I8, PROP_ID(PR_MESSAGE_SIZE))
#endif /*MAPITAGS_H*/
diff --git a/src/oomhelp.h b/src/oomhelp.h
index ccc8236..c2afd4a 100644
--- a/src/oomhelp.h
+++ b/src/oomhelp.h
@@ -1,355 +1,357 @@
/* oomhelp.h - Defs for helper functions for the Outlook Object Model
* Copyright (C) 2009 g10 Code GmbH
* Copyright (C) 2015 by Bundesamt für Sicherheit in der Informationstechnik
* Software engineering by Intevation GmbH
*
* This file is part of GpgOL.
*
* GpgOL is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* GpgOL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, see .
*/
#ifndef OOMHELP_H
#define OOMHELP_H
#include
#include "mymapi.h"
#define MSOCONTROLBUTTON 1
#define MSOCONTROLEDIT 2
#define MSOCONTROLDROPDOWN 3
#define MSOCONTROLCOMBOBOX 4
#define MSOCONTROLPOPUP 10
enum
{
msoButtonAutomatic = 0,
msoButtonIcon = 1,
msoButtonCaption = 2,
msoButtonIconAndCaption = 3,
msoButtonIconAndWrapCaption = 7,
msoButtonIconAndCaptionBelow = 11,
msoButtonWrapCaption = 14,
msoButtonIconAndWrapCaptionBelow = 15
};
enum
{
msoButtonDown = -1,
msoButtonUp = 0,
msoButtonMixed = 2
};
DEFINE_GUID(GUID_NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
DEFINE_GUID(IID_IConnectionPoint,
0xb196b286, 0xbab4, 0x101a,
0xb6, 0x9c, 0x00, 0xaa, 0x00, 0x34, 0x1d, 0x07);
DEFINE_GUID(IID_IConnectionPointContainer,
0xb196b284, 0xbab4, 0x101a,
0xb6, 0x9c, 0x00, 0xaa, 0x00, 0x34, 0x1d, 0x07);
DEFINE_GUID(IID_IPictureDisp,
0x7bf80981, 0xbf32, 0x101a,
0x8b, 0xbb, 0x00, 0xaa, 0x00, 0x30, 0x0c, 0xab);
DEFINE_GUID(IID_ApplicationEvents, 0x0006304E, 0x0000, 0x0000,
0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
DEFINE_GUID(IID_ExplorerEvents, 0x0006300F, 0x0000, 0x0000,
0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
DEFINE_GUID(IID_ExplorersEvents, 0x00063078, 0x0000, 0x0000,
0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
DEFINE_GUID(IID_MailItemEvents, 0x0006302B, 0x0000, 0x0000,
0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
DEFINE_GUID(IID_MailItem, 0x00063034, 0x0000, 0x0000,
0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46);
DEFINE_GUID(IID_IMAPISecureMessage, 0x253cc320, 0xeab6, 0x11d0,
0x82, 0x22, 0, 0x60, 0x97, 0x93, 0x87, 0xea);
DEFINE_OLEGUID(IID_IUnknown, 0x00000000, 0, 0);
DEFINE_OLEGUID(IID_IDispatch, 0x00020400, 0, 0);
DEFINE_OLEGUID(IID_IOleWindow, 0x00000114, 0, 0);
#ifndef PR_SMTP_ADDRESS_DASL
#define PR_SMTP_ADDRESS_DASL \
"http://schemas.microsoft.com/mapi/proptag/0x39FE001E"
#endif
#ifndef PR_EMS_AB_PROXY_ADDRESSES_DASL
#define PR_EMS_AB_PROXY_ADDRESSES_DASL \
"http://schemas.microsoft.com/mapi/proptag/0x800F101E"
#endif
#ifndef PR_ATTACHMENT_HIDDEN_DASL
#define PR_ATTACHMENT_HIDDEN_DASL \
"http://schemas.microsoft.com/mapi/proptag/0x7FFE000B"
#endif
#define PR_MESSAGE_CLASS_W_DASL \
"http://schemas.microsoft.com/mapi/proptag/0x001A001F"
#define GPGOL_ATTACHTYPE_DASL \
"http://schemas.microsoft.com/mapi/string/" \
"{31805AB8-3E92-11DC-879C-00061B031004}/GpgOL Attach Type/0x00000003"
#define GPGOL_UID_DASL \
"http://schemas.microsoft.com/mapi/string/" \
"{31805AB8-3E92-11DC-879C-00061B031004}/GpgOL UID/0x0000001F"
#define PR_ATTACH_DATA_BIN_DASL \
"http://schemas.microsoft.com/mapi/proptag/0x37010102"
#define PR_BODY_W_DASL \
"http://schemas.microsoft.com/mapi/proptag/0x1000001F"
#define PR_ATTACHMENT_HIDDEN_DASL \
"http://schemas.microsoft.com/mapi/proptag/0x7FFE000B"
#define PR_ATTACH_MIME_TAG_DASL \
"http://schemas.microsoft.com/mapi/proptag/0x370E001F"
#define PR_ATTACH_CONTENT_ID_DASL \
"http://schemas.microsoft.com/mapi/proptag/0x3712001F"
#define PR_TAG_SENDER_SMTP_ADDRESS \
"http://schemas.microsoft.com/mapi/proptag/0x5D01001F"
#define PR_TAG_RECEIVED_REPRESENTING_SMTP_ADDRESS \
"http://schemas.microsoft.com/mapi/proptag/0x5D08001F"
#define PR_PIDNameContentType_DASL \
"http://schemas.microsoft.com/mapi/string/{00020386-0000-0000-C000-000000000046}/content-type/0x0000001F"
+#define PR_BLOCK_STATUS_DASL \
+ "http://schemas.microsoft.com/mapi/proptag/0x10960003"
#ifdef __cplusplus
extern "C" {
#if 0
}
#endif
#endif
/* Return the malloced name of an COM+ object. */
char *get_object_name (LPUNKNOWN obj);
/* Helper to lookup a dispid. */
DISPID lookup_oom_dispid (LPDISPATCH pDisp, const char *name);
/* Return the OOM object's IDispatch interface described by FULLNAME. */
LPDISPATCH get_oom_object (LPDISPATCH pStart, const char *fullname);
/* Set the Icon of a CommandBarControl. */
int put_oom_icon (LPDISPATCH pDisp, int rsource_id, int size);
/* Set the boolean property NAME to VALUE. */
int put_oom_bool (LPDISPATCH pDisp, const char *name, int value);
/* Set the property NAME to VALUE. */
int put_oom_int (LPDISPATCH pDisp, const char *name, int value);
/* Set the property NAME to STRING. */
int put_oom_string (LPDISPATCH pDisp, const char *name, const char *string);
/* Set the property NAME to DISP. */
int put_oom_disp (LPDISPATCH pDisp, const char *name, LPDISPATCH value);
/* Get the boolean property NAME of the object PDISP. */
int get_oom_bool (LPDISPATCH pDisp, const char *name);
/* Get the integer property NAME of the object PDISP. */
int get_oom_int (LPDISPATCH pDisp, const char *name);
/* Get the string property NAME of the object PDISP. */
char *get_oom_string (LPDISPATCH pDisp, const char *name);
/* Get an IUnknown object from property NAME of PDISP. */
LPUNKNOWN get_oom_iunknown (LPDISPATCH pDisp, const char *name);
/* Return the control object with tag property value TAG. */
LPDISPATCH get_oom_control_bytag (LPDISPATCH pObj, const char *tag);
/* Add a new button to an object which supports the add method.
Returns the new object or NULL on error. */
LPDISPATCH add_oom_button (LPDISPATCH pObj);
/* Delete a button. */
void del_oom_button (LPDISPATCH button);
/* Get the HWND of the active window in the current context */
HWND get_oom_context_window (LPDISPATCH context);
/* Get the address of the recipients as string list */
char ** get_oom_recipients (LPDISPATCH recipients);
/* Add an attachment to a dispatcher */
int
add_oom_attachment (LPDISPATCH disp, const wchar_t* inFile,
const wchar_t *displayName);
/* Look up a string with the propertyAccessor interface */
char *
get_pa_string (LPDISPATCH pDisp, const char *property);
/* Look up a long with the propertyAccessor interface.
returns -1 on error.*/
int
get_pa_int (LPDISPATCH pDisp, const char *property, int *rInt);
/* Set a variant with the propertyAccessor interface.
This is tested to work at least vor BSTR variants. Trying
to set PR_ATTACH_DATA_BIN_DASL with this failed with
hresults 0x80020005 type mismatch or 0x80020008 vad
variable type for:
VT_ARRAY | VT_UI1 | VT_BYREF
VT_SAFEARRAY | VT_UI1 | VT_BYREF
VT_BSTR | VT_BYREF
VT_BSTR
VT_ARRAY | VT_UI1
VT_SAFEARRAY | VT_UI1
No idea whats wrong there. Needs more experiments. The
Type is only documented as "Binary". Outlookspy also
fails with the same error when trying to modify the
property.
*/
int
put_pa_string (LPDISPATCH pDisp, const char *dasl_id, const char *value);
int
put_pa_variant (LPDISPATCH pDisp, const char *dasl_id, VARIANT *value);
int
put_pa_int (LPDISPATCH pDisp, const char *dasl_id, int value);
/* Look up a variant with the propertyAccessor interface */
int
get_pa_variant (LPDISPATCH pDisp, const char *dasl_id, VARIANT *rVariant);
/* Look up a LONG with the propertyAccessor interface */
LONG
get_pa_long (LPDISPATCH pDisp, const char *dasl_id);
/* Queries the interface of the dispatcher for the id
id. Returns NULL on error. The returned Object
must be released.
Mainly useful to check if an object is what
it appears to be. */
LPDISPATCH
get_object_by_id (LPDISPATCH pDisp, REFIID id);
/* Obtain the MAPI Message corresponding to the
Mailitem. Returns NULL on error.
The returned Message needs to be released by the
caller */
LPMESSAGE
get_oom_message (LPDISPATCH mailitem);
/* Obtain the Base MAPI Message of a MailItem.
The parameter should be a pointer to a MailItem.
returns NULL on error.
The returned Message needs to be released by the
caller.
*/
LPMESSAGE
get_oom_base_message (LPDISPATCH mailitem);
/* Get a strong reference for a mail object by calling
Application.GetObjectReference with type strong. The
documentation is unclear what this acutally does.
This function is left over from experiments about
strong references. Maybe there is a use for them.
The reference we use in the Mail object is documented
as a Weak reference. But changing that does not appear
to make a difference.
*/
LPDISPATCH
get_strong_reference (LPDISPATCH mail);
/* Invoke a method of an outlook object.
returns true on success false otherwise.
rVariant should either point to a propery initialized
variant (initinalized wiht VariantInit) to hold
the return value or a pointer to NULL.
*/
int
invoke_oom_method (LPDISPATCH pDisp, const char *name, VARIANT *rVariant);
/* Invoke a method of an outlook object.
returns true on success false otherwise.
rVariant should either point to a propery initialized
variant (initinalized wiht VariantInit) to hold
the return value or a pointer to NULL.
parms can optionally be used to provide a DISPPARAMS structure
with parameters for the function.
*/
int
invoke_oom_method_with_parms (LPDISPATCH pDisp, const char *name,
VARIANT *rVariant, DISPPARAMS *params);
/* Try to obtain the mapisession through the Application.
returns NULL on error.*/
LPMAPISESSION
get_oom_mapi_session (void);
/* Ensure a category of the name name exists in
the session for the Mail mail.
Creates the category with the specified color if required.
returns 0 on success. */
void
ensure_category_exists (LPDISPATCH mail, const char *category, int color);
/* Add a category to a mail if it is not already added. */
int
add_category (LPDISPATCH mail, const char *category);
/* Remove a category from a mail if it was added. */
int
remove_category (LPDISPATCH mail, const char *category);
/* Get a unique identifier for a mail object. The
uuid is a custom property. If create is set
a new uuid will be added if none exists and the
value of that uuid returned.
The optinal uuid value can be set to be used
as uuid instead of a generated one.
Return value has to be freed by the caller.
*/
char *
get_unique_id (LPDISPATCH mail, int create, const char* uuid);
/* Uses the Application->ActiveWindow to determine the hwnd
through FindWindow and the caption. Does not use IOleWindow
because that was unreliable somhow. */
HWND get_active_hwnd (void);
/* Create a new mailitem and return it */
LPDISPATCH create_mail (void);
LPDISPATCH get_account_for_mail (const char *mbox);
/* Sender fallbacks. All return either null or a malloced address. */
char *get_sender_CurrentUser (LPDISPATCH mailitem);
char *get_sender_Sender (LPDISPATCH mailitem);
char *get_sender_SenderEMailAddress (LPDISPATCH mailitem);
/* Get the body of the active inline response */
char *get_inline_body (void);
/* Get the major version of the exchange server of the account for the
mail address "mbox". Returns -1 if no version could be detected
or exchange is not used.*/
int get_ex_major_version_for_addr (const char *mbox);
#ifdef __cplusplus
char *get_sender_SendUsingAccount (LPDISPATCH mailitem, bool *r_is_GSuite);
}
#endif
#endif /*OOMHELP_H*/
diff --git a/src/parsecontroller.cpp b/src/parsecontroller.cpp
index 3cd31a7..0db7734 100644
--- a/src/parsecontroller.cpp
+++ b/src/parsecontroller.cpp
@@ -1,526 +1,560 @@
/* @file parsecontroller.cpp
* @brief Parse a mail and decrypt / verify accordingly
*
* Copyright (C) 2016 by Bundesamt für Sicherheit in der Informationstechnik
* Software engineering by Intevation GmbH
*
* This file is part of GpgOL.
*
* GpgOL is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* GpgOL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, see .
*/
#include "config.h"
#include "parsecontroller.h"
#include "attachment.h"
#include "mimedataprovider.h"
#include
#include
#include
#include
#ifdef HAVE_W32_SYSTEM
#include "common.h"
/* We use UTF-8 internally. */
#undef _
# define _(a) utf8_gettext (a)
#else
# define _(a) a
#endif
const char decrypt_template_html[] = {
""
""
""
""
" %s %s |
"
""
""
""
" %s"
" |
"
"
"};
const char decrypt_template[] = {"%s %s\n\n%s"};
using namespace GpgME;
static bool
expect_no_headers (msgtype_t type)
{
return type != MSGTYPE_GPGOL_MULTIPART_SIGNED;
}
static bool
expect_no_mime (msgtype_t type)
{
return type == MSGTYPE_GPGOL_PGP_MESSAGE ||
type == MSGTYPE_GPGOL_CLEAR_SIGNED;
}
#ifdef HAVE_W32_SYSTEM
ParseController::ParseController(LPSTREAM instream, msgtype_t type):
m_inputprovider (new MimeDataProvider(instream,
expect_no_headers(type))),
m_outputprovider (new MimeDataProvider(expect_no_mime(type))),
- m_type (type)
+ m_type (type),
+ m_block_html (false)
{
log_mime_parser ("%s:%s: Creating parser for stream: %p of type %i"
" expect no headers: %i expect no mime: %i",
SRCNAME, __func__, instream, type,
expect_no_headers (type), expect_no_mime (type));
}
#endif
ParseController::ParseController(FILE *instream, msgtype_t type):
m_inputprovider (new MimeDataProvider(instream,
expect_no_headers(type))),
m_outputprovider (new MimeDataProvider(expect_no_mime(type))),
- m_type (type)
+ m_type (type),
+ m_block_html (false)
{
log_mime_parser ("%s:%s: Creating parser for stream: %p of type %i",
SRCNAME, __func__, instream, type);
}
ParseController::~ParseController()
{
log_debug ("%s:%s", SRCNAME, __func__);
delete m_inputprovider;
delete m_outputprovider;
}
static void
operation_for_type(msgtype_t type, bool *decrypt,
bool *verify)
{
*decrypt = false;
*verify = false;
switch (type)
{
case MSGTYPE_GPGOL_MULTIPART_ENCRYPTED:
case MSGTYPE_GPGOL_PGP_MESSAGE:
*decrypt = true;
break;
case MSGTYPE_GPGOL_MULTIPART_SIGNED:
case MSGTYPE_GPGOL_CLEAR_SIGNED:
*verify = true;
break;
case MSGTYPE_GPGOL_OPAQUE_SIGNED:
*verify = true;
break;
case MSGTYPE_GPGOL_OPAQUE_ENCRYPTED:
*decrypt = true;
break;
default:
log_error ("%s:%s: Unknown data type: %i",
SRCNAME, __func__, type);
}
}
static bool
is_smime (Data &data)
{
data.seek (0, SEEK_SET);
auto id = data.type();
data.seek (0, SEEK_SET);
return id == Data::CMSSigned || id == Data::CMSEncrypted;
}
static std::string
format_recipients(GpgME::DecryptionResult result, Protocol protocol)
{
std::string msg;
for (const auto recipient: result.recipients())
{
auto ctx = Context::createForProtocol(protocol);
Error e;
if (!ctx) {
/* Can't happen */
TRACEPOINT;
continue;
}
const auto key = ctx->key(recipient.keyID(), e, false);
delete ctx;
if (!key.isNull() && key.numUserIDs() && !e) {
msg += std::string("
") + key.userIDs()[0].id() + " (0x" + recipient.keyID() + ")";
continue;
}
msg += std::string("
") + _("Unknown Key:") + " 0x" + recipient.keyID();
}
return msg;
}
static std::string
format_error(GpgME::DecryptionResult result, Protocol protocol)
{
char *buf;
bool no_sec = false;
std::string msg;
if (result.error ().isCanceled () ||
result.error ().code () == GPG_ERR_NO_SECKEY)
{
msg = _("Decryption canceled or timed out.");
}
if (result.error ().code () == GPG_ERR_DECRYPT_FAILED ||
result.error ().code () == GPG_ERR_NO_SECKEY)
{
no_sec = true;
for (const auto &recipient: result.recipients ()) {
no_sec &= (recipient.status ().code () == GPG_ERR_NO_SECKEY);
}
}
if (no_sec)
{
msg = _("No secret key found to decrypt the message. "
"It is encrypted to the following keys:");
msg += format_recipients (result, protocol);
}
else
{
msg = _("Could not decrypt the data: ");
if (result.isNull ())
{
msg += _("Failed to parse the mail.");
}
else
{
msg += result.error().asString();
}
}
if (gpgrt_asprintf (&buf, opt.prefer_html ? decrypt_template_html :
decrypt_template,
protocol == OpenPGP ? "OpenPGP" : "S/MIME",
_("Encrypted message (decryption not possible)"),
msg.c_str()) == -1)
{
log_error ("%s:%s:Failed to Format error.",
SRCNAME, __func__);
return "Failed to Format error.";
}
msg = buf;
return msg;
}
void
ParseController::setSender(const std::string &sender)
{
m_sender = sender;
}
+static bool
+is_valid_chksum(const GpgME::Signature &sig)
+{
+ switch (sig.summary())
+ {
+ case GpgME::Signature::Valid:
+ case GpgME::Signature::Green:
+ case GpgME::Signature::KeyRevoked:
+ case GpgME::Signature::KeyExpired:
+ case GpgME::Signature::SigExpired:
+ case GpgME::Signature::CrlMissing:
+ case GpgME::Signature::CrlTooOld:
+ case GpgME::Signature::TofuConflict:
+ return true;
+ default:
+ return false;
+ }
+}
+
void
ParseController::parse()
{
// Wrap the input stream in an attachment / GpgME Data
Protocol protocol;
bool decrypt, verify;
Data input (m_inputprovider);
auto inputType = input.type ();
if (inputType == Data::Type::PGPSigned)
{
verify = true;
decrypt = false;
}
else
{
operation_for_type (m_type, &decrypt, &verify);
}
if ((m_inputprovider->signature() && is_smime (*m_inputprovider->signature())) ||
is_smime (input))
{
protocol = Protocol::CMS;
}
else
{
protocol = Protocol::OpenPGP;
}
auto ctx = Context::createForProtocol (protocol);
if (!ctx)
{
log_error ("%s:%s:Failed to create context. Installation broken.",
SRCNAME, __func__);
char *buf;
const char *proto = protocol == OpenPGP ? "OpenPGP" : "S/MIME";
if (gpgrt_asprintf (&buf, opt.prefer_html ? decrypt_template_html :
decrypt_template,
proto,
_("Encrypted message (decryption not possible)"),
_("Failed to find GnuPG please ensure that GnuPG or "
"Gpg4win is properly installed.")) == -1)
{
log_error ("%s:%s:Failed format error.",
SRCNAME, __func__);
/* Should never happen */
m_error = std::string("Bad installation");
}
m_error = buf;
xfree (buf);
return;
}
ctx->setArmor(true);
if (!m_sender.empty())
{
ctx->setSender(m_sender.c_str());
}
Data output (m_outputprovider);
log_debug ("%s:%s:%p decrypt: %i verify: %i with protocol: %s sender: %s type: %i",
SRCNAME, __func__, this,
decrypt, verify,
protocol == OpenPGP ? "OpenPGP" :
protocol == CMS ? "CMS" : "Unknown",
m_sender.empty() ? "none" : m_sender.c_str(), inputType);
if (decrypt)
{
input.seek (0, SEEK_SET);
auto combined_result = ctx->decryptAndVerify(input, output);
log_debug ("%s:%s:%p decrypt / verify done.",
SRCNAME, __func__, this);
m_decrypt_result = combined_result.first;
m_verify_result = combined_result.second;
if ((!m_decrypt_result.error () &&
m_verify_result.signatures ().empty() &&
m_outputprovider->signature ()) ||
is_smime (output) ||
output.type() == Data::Type::PGPSigned)
{
/* There is a signature in the output. So we have
to verify it now as an extra step. */
input = Data (m_outputprovider);
delete m_inputprovider;
m_inputprovider = m_outputprovider;
m_outputprovider = new MimeDataProvider();
output = Data(m_outputprovider);
verify = true;
}
else
{
verify = false;
}
if (m_decrypt_result.error () || m_decrypt_result.isNull () ||
m_decrypt_result.error ().isCanceled ())
{
m_error = format_error (m_decrypt_result, protocol);
}
}
if (verify)
{
const auto sig = m_inputprovider->signature();
input.seek (0, SEEK_SET);
if (sig)
{
sig->seek (0, SEEK_SET);
m_verify_result = ctx->verifyDetachedSignature(*sig, input);
log_debug ("%s:%s:%p verify done.",
SRCNAME, __func__, this);
/* Copy the input to output to do a mime parsing. */
char buf[4096];
input.seek (0, SEEK_SET);
output.seek (0, SEEK_SET);
size_t nread;
while ((nread = input.read (buf, 4096)) > 0)
{
output.write (buf, nread);
}
}
else
{
m_verify_result = ctx->verifyOpaqueSignature(input, output);
}
}
delete ctx;
log_debug ("%s:%s:%p: decrypt err: %i verify err: %i",
SRCNAME, __func__, this, m_decrypt_result.error().code(),
m_verify_result.error().code());
TRACEPOINT;
- /* Ensure that the Keys for the signatures are available */
+
+ bool has_valid_encrypted_checksum = false;
+ /* Ensure that the Keys for the signatures are available
+ and if it has a valid encrypted checksum. */
for (const auto sig: m_verify_result.signatures())
{
+ has_valid_encrypted_checksum = is_valid_chksum (sig);
+
sig.key(true, true);
if (sig.validity() == Signature::Validity::Full ||
sig.validity() == Signature::Validity::Ultimate)
{
/* Ensure that we have the keys with ultimate
trust cached for the ui. */
get_ultimate_keys ();
}
}
+ if (protocol == Protocol::CMS && decrypt && !m_decrypt_result.error() &&
+ !has_valid_encrypted_checksum)
+ {
+ log_debug ("%s:%s:%p Encrypted S/MIME without checksum. Block HTML.",
+ SRCNAME, __func__, this);
+ m_block_html = true;
+ }
+
if (opt.enable_debug)
{
std::stringstream ss;
ss << m_decrypt_result << '\n' << m_verify_result;
for (const auto sig: m_verify_result.signatures())
{
ss << '\n' << sig.key();
}
log_debug ("Decrypt / Verify result: %s", ss.str().c_str());
}
TRACEPOINT;
return;
}
const std::string
ParseController::get_html_body() const
{
if (m_outputprovider)
{
return m_outputprovider->get_html_body();
}
else
{
return std::string();
}
}
const std::string
ParseController::get_body() const
{
if (m_outputprovider)
{
return m_outputprovider->get_body();
}
else
{
return std::string();
}
}
const std::string
ParseController::get_body_charset() const
{
if (m_outputprovider)
{
return m_outputprovider->get_body_charset();
}
else
{
return std::string();
}
}
const std::string
ParseController::get_html_charset() const
{
if (m_outputprovider)
{
return m_outputprovider->get_html_charset();
}
else
{
return std::string();
}
}
std::vector >
ParseController::get_attachments() const
{
if (m_outputprovider)
{
return m_outputprovider->get_attachments();
}
else
{
return std::vector >();
}
}
GPGRT_LOCK_DEFINE(keylist_lock);
/* static */
std::vector
ParseController::get_ultimate_keys()
{
static bool s_keys_listed;
static std::vector s_ultimate_keys;
gpgrt_lock_lock (&keylist_lock);
if (s_keys_listed)
{
gpgrt_lock_unlock (&keylist_lock);
return s_ultimate_keys;
}
log_debug ("%s:%s: Starting keylisting.",
SRCNAME, __func__);
auto ctx = Context::createForProtocol (OpenPGP);
if (!ctx)
{
/* Maybe PGP broken and not S/MIME */
log_error ("%s:%s: broken installation no ctx.",
SRCNAME, __func__);
gpgrt_lock_unlock (&keylist_lock);
return s_ultimate_keys;
}
ctx->setKeyListMode (KeyListMode::Local);
Error err;
TRACEPOINT;
if ((err = ctx->startKeyListing ()))
{
log_error ("%s:%s: Failed to start keylisting err: %i: %s",
SRCNAME, __func__, err.code (), err.asString());
delete ctx;
gpgrt_lock_unlock (&keylist_lock);
return s_ultimate_keys;
}
TRACEPOINT;
while (!err)
{
const auto key = ctx->nextKey(err);
if (err || key.isNull())
{
TRACEPOINT;
break;
}
if (key.isInvalid ())
{
log_debug ("%s:%s: skipping invalid key.",
SRCNAME, __func__);
continue;
}
for (const auto uid: key.userIDs())
{
if (uid.validity() == UserID::Validity::Ultimate &&
uid.id())
{
s_ultimate_keys.push_back (key);
log_debug ("%s:%s: Adding ultimate uid.",
SRCNAME, __func__);
log_mime_parser ("%s:%s: Added uid %s.",
SRCNAME, __func__, uid.id());
break;
}
}
}
TRACEPOINT;
delete ctx;
log_debug ("%s:%s: keylisting done.",
SRCNAME, __func__);
s_keys_listed = true;
gpgrt_lock_unlock (&keylist_lock);
return s_ultimate_keys;
}
diff --git a/src/parsecontroller.h b/src/parsecontroller.h
index d3c67c8..c318b9d 100644
--- a/src/parsecontroller.h
+++ b/src/parsecontroller.h
@@ -1,116 +1,120 @@
/* @file parsecontroller.h
* @brief Controll the parsing and decrypt / verify of a mail
*
* Copyright (C) 2016 by Bundesamt für Sicherheit in der Informationstechnik
* Software engineering by Intevation GmbH
*
* This file is part of GpgOL.
*
* GpgOL is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* GpgOL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, see .
*/
#ifndef PARSECONTROLLER_H
#define PARSECONTROLLER_H
#include
#include
#include
#ifdef HAVE_CONFIG_H
#include
#endif
#include "common_indep.h"
#include
#include
#include
class Attachment;
class MimeDataProvider;
#ifdef HAVE_W32_SYSTEM
#include "oomhelp.h"
#endif
/* A template for decryption errors / status message. */
extern const char decrypt_template[];
extern const char decrypt_template_html[];
class ParseController
{
public:
#ifdef HAVE_W32_SYSTEM
/** Construct a new ParseController for the stream instream.
instream is expected to point to a mime mail.
Adds a reference to the stream and releases it on
destruction. */
ParseController(LPSTREAM instream, msgtype_t type);
#endif
ParseController(FILE *instream, msgtype_t type);
~ParseController();
/* Get a list of ultimately keys where at least one
userid has ultimate trust. This list
will be initialized only once when the first signature
is validity full or ultimate is encountered. */
static std::vector get_ultimate_keys();
/** Main entry point. After execution getters will become
valid. */
void parse();
/** Get the Body. Call parse first. */
const std::string get_body() const;
/** Get the charset of the body. Call parse first.
*
* That is a bit of a clunky API to make testing
* without outlook easier as we use mlang for Charset
* conversion which is not available on GNU/Linux.
*/
const std::string get_body_charset() const;
const std::string get_html_charset() const;
/** Get an alternative? HTML Body. Call parse first. */
const std::string get_html_body() const;
/** Get the decrypted / verified attachments. Call parse first.
*/
std::vector > get_attachments() const;
const GpgME::DecryptionResult decrypt_result() const
{ return m_decrypt_result; }
const GpgME::VerificationResult verify_result() const
{ return m_verify_result; }
const std::string get_formatted_error() const
{ return m_error; }
void setSender(const std::string &sender);
+ bool shouldBlockHtml() const
+ { return m_block_html; }
+
private:
/* State variables */
MimeDataProvider *m_inputprovider;
MimeDataProvider *m_outputprovider;
msgtype_t m_type;
std::string m_error;
GpgME::DecryptionResult m_decrypt_result;
GpgME::VerificationResult m_verify_result;
std::string m_sender;
+ bool m_block_html;
};
#endif /* PARSECONTROLLER_H */