(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");
}
memdbg_alloc (buf);
m_error = buf;
xfree (buf);
TRETURN;
}
/* Maybe a different option for this ? */
if (opt.autoretrieve)
{
ctx->setFlag("auto-key-retrieve", "1");
}
ctx->setArmor(true);
if (!m_sender.empty())
{
ctx->setSender(m_sender.c_str());
}
if (offline)
{
ctx->setOffline (true);
}
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" : anonstr (m_sender.c_str()), inputType);
if (decrypt)
{
input.seek (0, SEEK_SET);
TRACEPOINT;
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)
{
TRACEPOINT;
+ log_dbg ("Did not have combined result parsing output.");
/* 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;
TRACEPOINT;
}
else
{
verify = false;
}
TRACEPOINT;
const auto err = m_decrypt_result.error ();
if (err || m_decrypt_result.isNull () ||
err.isCanceled ())
{
m_error = format_error (m_decrypt_result, protocol);
if (err.code () == GPG_ERR_NO_DATA)
{
m_error += std::string ("") +
S_ ("If this data does not look like an encrypted message\n"
"please see the debug tab in the options on how to report\n"
"this for further improvement.") + std::string ("\n\n") +
asprintf_s (_("Debug information: Message type: %i Data type: %i"),
m_type, inputType) + std::string ("\n\n") +
S_ ("The interpreted data was:") + std::string ("\n
");
m_error += input.toString ();
m_error += "
";
}
}
}
if (verify)
{
TRACEPOINT;
GpgME::Data *sig = m_inputprovider->signature();
input.seek (0, SEEK_SET);
if (sig)
{
sig->seek (0, SEEK_SET);
TRACEPOINT;
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);
// Use a fresh output
auto provider = new MimeDataProvider ();
// Warning: The dtor of the Data object touches
// the provider. So we have to delete it after
// the assignment.
output = Data (provider);
delete m_outputprovider;
m_outputprovider = provider;
size_t nread;
while ((nread = input.read (buf, 4096)) > 0)
{
output.write (buf, nread);
}
}
else
{
TRACEPOINT;
m_verify_result = ctx->verifyOpaqueSignature(input, output);
TRACEPOINT;
const auto sigs = m_verify_result.signatures();
bool allBad = sigs.size();
for (const auto s :sigs)
{
if (!(s.summary() & Signature::Red))
{
allBad = false;
break;
}
}
#ifdef HAVE_W32_SYSTEM
if (allBad)
{
log_debug ("%s:%s:%p inline verify error trying native to utf8.",
SRCNAME, __func__, this);
/* The proper solution would be to take the encoding from
the mail / headers. Then convert the wchar body to that
encoding. Verify, and convert it after verifcation to
UTF-8 which the rest of the code expects.
Or native_body from native ACP to InternetCodepage, then
verify and convert the output back to utf8 as the rest
expects.
But as this is clearsigned and we don't really want that.
Meh.
*/
char *utf8 = native_to_utf8 (input.toString().c_str());
if (utf8)
{
// Try again after conversion.
ctx = std::unique_ptr (Context::createForProtocol (protocol));
ctx->setArmor (true);
if (!m_sender.empty())
{
ctx->setSender(m_sender.c_str());
}
input = Data (utf8, strlen (utf8));
xfree (utf8);
// Use a fresh output
auto provider = new MimeDataProvider (true);
// Warning: The dtor of the Data object touches
// the provider. So we have to delete it after
// the assignment.
output = Data (provider);
delete m_outputprovider;
m_outputprovider = provider;
// Try again
m_verify_result = ctx->verifyOpaqueSignature(input, output);
}
}
#else
(void)allBad;
#endif
}
}
log_debug ("%s:%s:%p: decrypt err: %i verify err: %i",
SRCNAME, __func__, this, m_decrypt_result.error().code(),
m_verify_result.error().code());
/* If we are called again it is the second pass */
m_second_pass = true;
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())
{
TRACEPOINT;
has_valid_encrypted_checksum = is_valid_chksum (sig);
#ifndef BUILD_TESTS
/* For TOFU we would need to update here even when offline. */
if (!offline || KeyCache::instance ()->useTofu ())
{
KeyCache::instance ()->update (sig.fingerprint (), protocol);
}
#endif
TRACEPOINT;
}
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;
}
/* Import any application/pgp-keys attachments if the option is set. */
if (opt.autoimport)
{
for (const auto &attach: get_attachments())
{
if (attach->get_content_type () == "application/pgp-keys")
{
#ifndef BUILD_TESTS
KeyCache::import_pgp_key_data (attach->get_data());
#endif
}
}
}
if (opt.enable_debug & DBG_DATA)
{
std::stringstream ss;
TRACEPOINT;
ss << m_decrypt_result << '\n' << m_verify_result;
for (const auto sig: m_verify_result.signatures())
{
const auto key = sig.key();
if (key.isNull())
{
#ifndef BUILD_TESTS
ss << '\n' << "Cached key:\n" << KeyCache::instance()->getByFpr(
sig.fingerprint(), false);
#else
get_and_print_key_test (sig.fingerprint (), protocol);
#endif
}
else
{
ss << '\n' << key;
}
}
log_debug ("Decrypt / Verify result: %s", ss.str().c_str());
}
else
{
log_debug ("%s:%s:%p Decrypt / verify done errs: %i / %i numsigs: %i.",
SRCNAME, __func__, this, m_decrypt_result.error().code(),
m_verify_result.error().code(), m_verify_result.numSignatures());
}
TRACEPOINT;
if (m_outputprovider)
{
m_outputprovider->finalize ();
}
TRETURN;
}
const std::string
ParseController::get_html_body () const
{
TSTART;
if (m_outputprovider)
{
TRETURN m_outputprovider->get_html_body ();
}
else
{
TRETURN std::string();
}
}
const std::string
ParseController::get_body () const
{
TSTART;
if (m_outputprovider)
{
TRETURN m_outputprovider->get_body ();
}
else
{
TRETURN std::string();
}
}
const std::string
ParseController::get_body_charset() const
{
TSTART;
if (m_outputprovider)
{
TRETURN m_outputprovider->get_body_charset();
}
else
{
TRETURN std::string();
}
}
const std::string
ParseController::get_html_charset() const
{
TSTART;
if (m_outputprovider)
{
TRETURN m_outputprovider->get_html_charset();
}
else
{
TRETURN std::string();
}
}
std::vector >
ParseController::get_attachments() const
{
TSTART;
if (m_outputprovider)
{
TRETURN m_outputprovider->get_attachments();
}
else
{
TRETURN std::vector >();
}
}
std::string
ParseController::get_protected_header (const std::string &which) const
{
TSTART;
if (m_outputprovider)
{
TRETURN m_outputprovider->get_protected_header (which);
}
TRETURN std::string ();
}
std::string
ParseController::get_content_type () const
{
TSTART;
if (m_outputprovider)
{
TRETURN m_outputprovider->get_content_type ();
}
else
{
TRETURN std::string();
}
}