mirror of https://github.com/djcb/mu.git
message: improve support for decryption
Found a small leak: https://github.com/jstedfast/gmime/pull/124/commits
This commit is contained in:
parent
66ee2004fc
commit
17d2926cd0
|
@ -44,7 +44,10 @@
|
|||
using namespace Mu;
|
||||
|
||||
struct Message::Private {
|
||||
Private(Message::Options options=Message::Options::None):
|
||||
opts{options} {}
|
||||
|
||||
Message::Options opts;
|
||||
Document doc;
|
||||
mutable Option<MimeMessage> mime_msg;
|
||||
|
||||
|
@ -66,8 +69,8 @@ struct Message::Private {
|
|||
|
||||
static void fill_document(Message::Private& priv);
|
||||
|
||||
Message::Message(const std::string& path, const std::string& mdir):
|
||||
priv_{std::make_unique<Private>()}
|
||||
Message::Message(Message::Options opts, const std::string& path, const std::string& mdir):
|
||||
priv_{std::make_unique<Private>(opts)}
|
||||
{
|
||||
if (!g_path_is_absolute(path.c_str()))
|
||||
throw Error(Error::Code::File, "path '%s' is not absolute", path.c_str());
|
||||
|
@ -95,19 +98,22 @@ Message::Message(const std::string& path, const std::string& mdir):
|
|||
|
||||
if (!mdir.empty())
|
||||
priv_->doc.add(Field::Id::Maildir, mdir);
|
||||
|
||||
priv_->doc.add(Field::Id::Size, static_cast<int64_t>(statbuf.st_size));
|
||||
|
||||
// rest of the fields
|
||||
fill_document(*priv_);
|
||||
}
|
||||
|
||||
Message::Message(const std::string& text, const std::string& path,
|
||||
Message::Message(Message::Options opts, const std::string& text, const std::string& path,
|
||||
const std::string& mdir):
|
||||
priv_{std::make_unique<Private>()}
|
||||
priv_{std::make_unique<Private>(opts)}
|
||||
{
|
||||
auto xpath{to_string_opt_gchar(g_canonicalize_filename(path.c_str(), NULL))};
|
||||
if (xpath)
|
||||
priv_->doc.add(Field::Id::Path, std::move(xpath.value()));
|
||||
if (!path.empty()) {
|
||||
auto xpath{to_string_opt_gchar(g_canonicalize_filename(path.c_str(), {}))};
|
||||
if (xpath)
|
||||
priv_->doc.add(Field::Id::Path, std::move(xpath.value()));
|
||||
}
|
||||
|
||||
if (!mdir.empty())
|
||||
priv_->doc.add(Field::Id::Maildir, mdir);
|
||||
|
@ -327,6 +333,83 @@ process_part(const MimeObject& parent, const MimePart& part,
|
|||
accumulate_text(part, info, *ctype);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
handle_object(const MimeObject& parent,
|
||||
const MimeObject& obj, Message::Private& info);
|
||||
|
||||
|
||||
static void
|
||||
handle_encrypted(const MimeMultipartEncrypted& part, Message::Private& info)
|
||||
{
|
||||
if (!any_of(info.opts & Message::Options::Decrypt)) {
|
||||
/* just added to the list */
|
||||
info.parts.emplace_back(part);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto proto{part.content_type_parameter("protocol").value_or("unknown")};
|
||||
const auto ctx = MimeCryptoContext::make(proto);
|
||||
if (!ctx) {
|
||||
g_warning("failed to create context for protocol <%s>",
|
||||
proto.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
auto res{part.decrypt(*ctx)};
|
||||
if (!res) {
|
||||
g_warning("failed to decrypt: %s", res.error().what());
|
||||
return;
|
||||
}
|
||||
|
||||
if (res->first.is_multipart()) {
|
||||
MimeMultipart{res->first}.for_each(
|
||||
[&](auto&& parent, auto&& child_obj) {
|
||||
handle_object(parent, child_obj, info);
|
||||
});
|
||||
|
||||
} else
|
||||
handle_object(part, res->first, info);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
handle_object(const MimeObject& parent,
|
||||
const MimeObject& obj, Message::Private& info)
|
||||
{
|
||||
/* if it's an encrypted part we should decrypt, recurse */
|
||||
if (obj.is_multipart_encrypted())
|
||||
handle_encrypted(MimeMultipartEncrypted{obj}, info);
|
||||
else if (obj.is_part() ||
|
||||
obj.is_message_part() ||
|
||||
obj.is_multipart_signed() ||
|
||||
obj.is_multipart_encrypted())
|
||||
info.parts.emplace_back(obj);
|
||||
|
||||
if (obj.is_part())
|
||||
process_part(parent, obj, info);
|
||||
|
||||
if (obj.is_multipart_signed())
|
||||
info.flags |= Flags::Signed;
|
||||
else if (obj.is_multipart_encrypted()) {
|
||||
/* FIXME: An encrypted part might be signed at the same time.
|
||||
* In that case the signed flag is lost. */
|
||||
info.flags |= Flags::Encrypted;
|
||||
} else if (obj.is_mime_application_pkcs7_mime()) {
|
||||
MimeApplicationPkcs7Mime smime(obj);
|
||||
switch (smime.smime_type()) {
|
||||
case Mu::MimeApplicationPkcs7Mime::SecureMimeType::SignedData:
|
||||
info.flags |= Flags::Signed;
|
||||
break;
|
||||
case Mu::MimeApplicationPkcs7Mime::SecureMimeType::EnvelopedData:
|
||||
info.flags |= Flags::Encrypted;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This message -- recursively walk through message, and initialize some
|
||||
* other values that depend on another.
|
||||
|
@ -349,36 +432,8 @@ process_message(const MimeMessage& mime_msg, const std::string& path,
|
|||
}
|
||||
|
||||
// parts
|
||||
mime_msg.for_each([&](auto&& parent, auto&& part) {
|
||||
|
||||
if (part.is_part() ||
|
||||
part.is_message_part() ||
|
||||
part.is_multipart_signed() ||
|
||||
part.is_multipart_encrypted())
|
||||
info.parts.emplace_back(part);
|
||||
|
||||
if (part.is_part())
|
||||
process_part(parent, part, info);
|
||||
|
||||
if (part.is_multipart_signed())
|
||||
info.flags |= Flags::Signed;
|
||||
else if (part.is_multipart_encrypted()) {
|
||||
/* FIXME: An encrypted part might be signed at the same time.
|
||||
* In that case the signed flag is lost. */
|
||||
info.flags |= Flags::Encrypted;
|
||||
} else if (part.is_mime_application_pkcs7_mime()) {
|
||||
MimeApplicationPkcs7Mime smime(part);
|
||||
switch (smime.smime_type()) {
|
||||
case Mu::MimeApplicationPkcs7Mime::SecureMimeType::SignedData:
|
||||
info.flags |= Flags::Signed;
|
||||
break;
|
||||
case Mu::MimeApplicationPkcs7Mime::SecureMimeType::EnvelopedData:
|
||||
info.flags |= Flags::Encrypted;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
mime_msg.for_each([&](auto&& parent, auto&& child_obj) {
|
||||
handle_object(parent, child_obj, info);
|
||||
});
|
||||
|
||||
// get the mailing here, and use it do update flags, too.
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "mu-document.hh"
|
||||
#include "mu-message-part.hh"
|
||||
|
||||
#include "utils/mu-utils.hh"
|
||||
#include "utils/mu-option.hh"
|
||||
#include "utils/mu-result.hh"
|
||||
#include "utils/mu-sexp.hh"
|
||||
|
@ -38,6 +39,14 @@ namespace Mu {
|
|||
|
||||
class Message {
|
||||
public:
|
||||
enum struct Options {
|
||||
None = 0, /**< Defaults */
|
||||
Decrypt = 1 << 0, /**< Attempt to decrypt */
|
||||
RetrieveKeys = 1 << 1, /**< Auto-retrieve crypto keys (implies network
|
||||
* access) */
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Move CTOR
|
||||
*
|
||||
|
@ -49,6 +58,7 @@ public:
|
|||
* Construct a message based on a path. The maildir is optional; however
|
||||
* messages without maildir cannot be stored in the database
|
||||
*
|
||||
* @param opts options
|
||||
* @param path path to message
|
||||
* @param mdir the maildir for this message; i.e, if the path is
|
||||
* ~/Maildir/foo/bar/cur/msg, the maildir would be foo/bar; you can
|
||||
|
@ -57,9 +67,10 @@ public:
|
|||
*
|
||||
* @return a message or an error
|
||||
*/
|
||||
static Result<Message> make_from_path(const std::string& path,
|
||||
static Result<Message> make_from_path(Options opts,
|
||||
const std::string& path,
|
||||
const std::string& mdir={}) try {
|
||||
return Ok(Message{path, mdir});
|
||||
return Ok(Message{opts, path, mdir});
|
||||
} catch (Error& err) {
|
||||
return Err(err);
|
||||
} catch (...) {
|
||||
|
@ -85,6 +96,7 @@ public:
|
|||
/**
|
||||
* Construct a message from a string. This is mostly useful for testing.
|
||||
*
|
||||
* @param opts options
|
||||
* @param text message text
|
||||
* @param path path to message - optional; path does not have to exist.
|
||||
* this is useful for testing.
|
||||
|
@ -92,10 +104,11 @@ public:
|
|||
*
|
||||
* @return a message or an error
|
||||
*/
|
||||
static Result<Message> make_from_text(const std::string& text,
|
||||
static Result<Message> make_from_text(Options opts,
|
||||
const std::string& text,
|
||||
const std::string& path={},
|
||||
const std::string& mdir={}) try {
|
||||
return Ok(Message{text, path, mdir});
|
||||
return Ok(Message{opts, text, path, mdir});
|
||||
} catch (Error& err) {
|
||||
return Err(err);
|
||||
} catch (...) {
|
||||
|
@ -330,13 +343,14 @@ public:
|
|||
|
||||
struct Private;
|
||||
private:
|
||||
Message(const std::string& path, const std::string& mdir);
|
||||
Message(const std::string& str, const std::string& path,
|
||||
Message(Options opts, const std::string& path, const std::string& mdir);
|
||||
Message(Options opts, const std::string& str, const std::string& path,
|
||||
const std::string& mdir);
|
||||
Message(Document& doc);
|
||||
|
||||
|
||||
std::unique_ptr<Private> priv_;
|
||||
}; // Message
|
||||
MU_ENABLE_BITOPS(Message::Options);
|
||||
|
||||
} // Mu
|
||||
#endif /* MU_MESSAGE_HH__ */
|
||||
|
|
|
@ -81,16 +81,29 @@ MimeObject::header(const std::string& hdr) const noexcept
|
|||
}
|
||||
|
||||
|
||||
Option<std::string>
|
||||
MimeObject::object_to_string() const noexcept
|
||||
Result<size_t>
|
||||
MimeObject::write_to_stream(const MimeFormatOptions& f_opts,
|
||||
MimeStream& stream) const
|
||||
{
|
||||
GMimeStream *stream{g_mime_stream_mem_new()};
|
||||
auto written = g_mime_object_write_to_stream(self(), f_opts.get(),
|
||||
GMIME_STREAM(stream.object()));
|
||||
if (written < 0)
|
||||
return Err(Error::Code::File, "failed to write mime-object to stream");
|
||||
else
|
||||
return Ok(static_cast<size_t>(written));
|
||||
}
|
||||
|
||||
Option<std::string>
|
||||
MimeObject::to_string_opt() const noexcept
|
||||
{
|
||||
auto stream{MimeStream::make_mem()};
|
||||
if (!stream) {
|
||||
g_warning("failed to create mem stream");
|
||||
return Nothing;
|
||||
}
|
||||
|
||||
const auto written = g_mime_object_write_to_stream(self(), {}, stream);
|
||||
const auto written = g_mime_object_write_to_stream(
|
||||
self(), {}, GMIME_STREAM(stream.object()));
|
||||
if (written < 0) {
|
||||
g_warning("failed to write object to stream");
|
||||
return Nothing;
|
||||
|
@ -98,10 +111,10 @@ MimeObject::object_to_string() const noexcept
|
|||
|
||||
std::string buffer;
|
||||
buffer.resize(written + 1);
|
||||
g_mime_stream_reset(stream);
|
||||
stream.reset();
|
||||
|
||||
auto bytes{g_mime_stream_read(stream, buffer.data(), written)};
|
||||
g_object_unref(stream);
|
||||
auto bytes{g_mime_stream_read(GMIME_STREAM(stream.object()),
|
||||
buffer.data(), written)};
|
||||
if (bytes < 0)
|
||||
return Nothing;
|
||||
|
||||
|
@ -121,7 +134,7 @@ MimeCryptoContext::import_keys(MimeStream& stream)
|
|||
{
|
||||
GError *err{};
|
||||
auto res = g_mime_crypto_context_import_keys(
|
||||
self(), stream.self(), &err);
|
||||
self(), GMIME_STREAM(stream.object()), &err);
|
||||
|
||||
if (res < 0)
|
||||
return Err(Error::Code::File, &err,
|
||||
|
@ -131,7 +144,7 @@ MimeCryptoContext::import_keys(MimeStream& stream)
|
|||
}
|
||||
|
||||
void
|
||||
MimeCryptoContext::set_password_request_function(PasswordRequestFunc pw_func)
|
||||
MimeCryptoContext::set_request_password(PasswordRequestFunc pw_func)
|
||||
{
|
||||
static auto request_func = pw_func;
|
||||
|
||||
|
@ -143,7 +156,8 @@ MimeCryptoContext::set_password_request_function(PasswordRequestFunc pw_func)
|
|||
gboolean reprompt,
|
||||
GMimeStream *response,
|
||||
GError **err) -> gboolean {
|
||||
MimeStream mstream{response};
|
||||
MimeStream mstream{MimeStream::make_from_stream(response)};
|
||||
|
||||
auto res = request_func(MimeCryptoContext(ctx),
|
||||
std::string{user_id ? user_id : ""},
|
||||
std::string{prompt ? prompt : ""},
|
||||
|
@ -420,23 +434,23 @@ MimePart::to_string() const noexcept
|
|||
Result<size_t>
|
||||
MimePart::to_file(const std::string& path, bool overwrite) const noexcept
|
||||
{
|
||||
GMimeDataWrapper *wrapper{g_mime_part_get_content(self())};
|
||||
MimeDataWrapper wrapper{g_mime_part_get_content(self())};
|
||||
if (!wrapper) /* this happens with invalid mails */
|
||||
return Err(Error::Code::File, "failed to create data wrapper");
|
||||
|
||||
|
||||
GError *err{};
|
||||
GMimeStream *stream{g_mime_stream_fs_open(
|
||||
path.c_str(),
|
||||
O_WRONLY | O_CREAT | O_TRUNC |(overwrite ? 0 : O_EXCL),
|
||||
S_IRUSR|S_IWUSR,
|
||||
&err)};
|
||||
if (!stream)
|
||||
return Err(Error::Code::File, &err,
|
||||
"failed to open '%s'", path.c_str());
|
||||
auto strm{g_mime_stream_fs_open(path.c_str(),
|
||||
O_WRONLY | O_CREAT | O_TRUNC |(overwrite ? 0 : O_EXCL),
|
||||
S_IRUSR|S_IWUSR,
|
||||
&err)};
|
||||
if (!strm)
|
||||
return Err(Error::Code::File, &err, "failed to open '%s'", path.c_str());
|
||||
|
||||
MimeStream stream{MimeStream::make_from_stream(strm)};
|
||||
ssize_t written{g_mime_data_wrapper_write_to_stream(
|
||||
GMIME_DATA_WRAPPER(wrapper.object()),
|
||||
GMIME_STREAM(stream.object()))};
|
||||
|
||||
ssize_t written{g_mime_data_wrapper_write_to_stream(wrapper, stream)};
|
||||
g_object_unref(stream);
|
||||
if (written < 0) {
|
||||
return Err(Error::Code::File, &err,
|
||||
"failed to write to '%s'", path.c_str());
|
||||
|
@ -446,30 +460,23 @@ MimePart::to_file(const std::string& path, bool overwrite) const noexcept
|
|||
}
|
||||
|
||||
|
||||
Result<std::vector<MimeSignature>>
|
||||
MimeMultipartSigned::verify(VerifyFlags vflags) const noexcept
|
||||
|
||||
|
||||
void
|
||||
MimeMultipart::for_each(const ForEachFunc& func) const noexcept
|
||||
{
|
||||
GError *err{};
|
||||
GMimeSignatureList *siglist = g_mime_multipart_signed_verify(
|
||||
struct CallbackData { const ForEachFunc& func; };
|
||||
CallbackData cbd{func};
|
||||
|
||||
g_mime_multipart_foreach(
|
||||
self(),
|
||||
static_cast<GMimeVerifyFlags>(vflags),
|
||||
&err);
|
||||
|
||||
if (!siglist)
|
||||
return Err(Error::Code::Crypto, &err, "failed to verify");
|
||||
|
||||
std::vector<MimeSignature> sigs;
|
||||
for (auto i = 0; i != g_mime_signature_list_length(siglist); ++i) {
|
||||
GMimeSignature *sig = g_mime_signature_list_get_signature(siglist, i);
|
||||
sigs.emplace_back(MimeSignature(sig));
|
||||
}
|
||||
|
||||
g_object_unref(siglist);
|
||||
|
||||
return sigs;
|
||||
|
||||
[] (GMimeObject *parent, GMimeObject *part, gpointer user_data) {
|
||||
auto cb_data{reinterpret_cast<CallbackData*>(user_data)};
|
||||
cb_data->func(MimeObject{parent}, MimeObject{part});
|
||||
}, &cbd);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* we need to be able to pass a crypto-context to the verify(), but
|
||||
* g_mime_multipart_signed_verify() doesn't offer that anymore in GMime 3.x.
|
||||
|
@ -481,7 +488,7 @@ MimeMultipartSigned::verify(VerifyFlags vflags) const noexcept
|
|||
static bool
|
||||
mime_types_equal (const std::string& mime_type, const std::string& official_type)
|
||||
{
|
||||
if (g_ascii_strcasecmp(mime_type.c_str(), official_type.c_str()))
|
||||
if (g_ascii_strcasecmp(mime_type.c_str(), official_type.c_str()) == 0)
|
||||
return true;
|
||||
|
||||
const auto slash_pos = official_type.find("/");
|
||||
|
@ -504,73 +511,70 @@ mime_types_equal (const std::string& mime_type, const std::string& official_type
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* A bit of a monster, this impl.
|
||||
*
|
||||
* It's the transliteration of the g_mime_multipart_signed_verify() which
|
||||
* adds the feature of passing in the CryptoContext.
|
||||
*
|
||||
*/
|
||||
Result<std::vector<MimeSignature>>
|
||||
MimeMultipartSigned::verify(const MimeCryptoContext& ctx, VerifyFlags vflags) const noexcept
|
||||
{
|
||||
if (g_mime_multipart_get_count(GMIME_MULTIPART(self())) < 2)
|
||||
return Err(Error::Code::Crypto, "cannot verify, not enough subparts");
|
||||
|
||||
const auto proto{content_type_parameter("protocol")};
|
||||
const auto sign_proto{ctx.signature_protocol()};
|
||||
|
||||
// Result<std::vector<MimeSignature>>
|
||||
// MimeMultipartSigned::verify(VerifyFlags vflags, const CryptoContext& ctx) const noexcept
|
||||
// {
|
||||
// if (g_mime_multipart_get_count(self()) < 2)
|
||||
// return Err(Error::Code::Crypto, "cannot verify, not enough subparts");
|
||||
|
||||
// const auto proto{content_type_parameter("protocol")};
|
||||
// const auto sign_proto{ctx.signature_prototol().value_or("<unknown>")};
|
||||
|
||||
// if (!proto || !sign_proto || !mime_types_equal(*proto, sign_proto))
|
||||
// return Err(Error::Code::Crypto, "unsupported protocol");
|
||||
|
||||
// const auto sig{MimeObject(g_mime_multipart_get_part(self(), GMIME_MULTIPART_SIGNED_SIGNATURE))};
|
||||
// if (!sig || !mime_types_equal(sig.mime_type().value_or("<none>"), sign_proto))
|
||||
// return Err(Error::Code::Crypto, "failed to find matching signature part");
|
||||
|
||||
// MimeObject content{g_mime_multipart_get_part(self(), GMIME_MULTIPART_SIGNED_CONTENT)};
|
||||
// if (!content)
|
||||
// return Err(Error::Code::Crypto, "cannot find content part");
|
||||
|
||||
// MimeFormatOptions fopts{format_options_new()};
|
||||
// g_mime_format_options_set_newline_format(*fopts, GMIME_NEWLINE_FORMAT_DOS);
|
||||
|
||||
// MimeStream stream{g_mime_stream_mem_new()};
|
||||
// g_mime_object_write_to_stream (content, *fopts, stream);
|
||||
// g_mime_stream_reset (stream);
|
||||
|
||||
// GMimeDataWrapper *wrapper = g_mime_part_get_content(static_cast<GMimePart*>(sig));
|
||||
|
||||
|
||||
|
||||
|
||||
// GError *err{};
|
||||
// GMimeSignatureList *siglist = g_mime_multipart_signed_verify(
|
||||
// self(),
|
||||
// static_cast<GMimeVerifyFlags>(vflags),
|
||||
// &err);
|
||||
|
||||
// if (!siglist)
|
||||
// return Err(Error::Code::Crypto, &err, "failed to verify");
|
||||
|
||||
// std::vector<MimeSignature> sigs;
|
||||
// for (auto i = 0; i != g_mime_signature_list_length(siglist); ++i) {
|
||||
// GMimeSignature *sig = g_mime_signature_list_get_signature(siglist, i);
|
||||
// sigs.emplace_back(MimeSignature(sig));
|
||||
// }
|
||||
|
||||
// g_object_unref(siglist);
|
||||
|
||||
// return sigs;
|
||||
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (!proto || !sign_proto || !mime_types_equal(*proto, *sign_proto))
|
||||
return Err(Error::Code::Crypto, "unsupported protocol " +
|
||||
proto.value_or("<unknown>"));
|
||||
|
||||
const auto sig{signed_signature_part()};
|
||||
const auto content{signed_content_part()};
|
||||
if (!sig || !content)
|
||||
return Err(Error::Code::Crypto, "cannot find part");
|
||||
|
||||
const auto sig_mime_type{sig->mime_type()};
|
||||
if (!sig || !mime_types_equal(sig_mime_type.value_or("<none>"), *sign_proto))
|
||||
return Err(Error::Code::Crypto, "failed to find matching signature part");
|
||||
|
||||
MimeFormatOptions fopts{g_mime_format_options_new()};
|
||||
g_mime_format_options_set_newline_format(fopts.get(), GMIME_NEWLINE_FORMAT_DOS);
|
||||
|
||||
MimeStream stream{MimeStream::make_mem()};
|
||||
if (auto&& res = content->write_to_stream(fopts, stream); !res)
|
||||
return Err(res.error());
|
||||
stream.reset();
|
||||
|
||||
MimeDataWrapper wrapper{g_mime_part_get_content(GMIME_PART(sig->object()))};
|
||||
MimeStream sigstream{MimeStream::make_mem()};
|
||||
if (auto&& res = wrapper.write_to_stream(sigstream); !res)
|
||||
return Err(res.error());
|
||||
sigstream.reset();
|
||||
|
||||
GError *err{};
|
||||
GMimeSignatureList *siglist{g_mime_crypto_context_verify(
|
||||
GMIME_CRYPTO_CONTEXT(ctx.object()),
|
||||
static_cast<GMimeVerifyFlags>(vflags),
|
||||
GMIME_STREAM(stream.object()),
|
||||
GMIME_STREAM(sigstream.object()),
|
||||
{},
|
||||
&err)};
|
||||
if (!siglist)
|
||||
return Err(Error::Code::Crypto, &err, "failed to verify");
|
||||
|
||||
std::vector<MimeSignature> sigs;
|
||||
for (auto i = 0;
|
||||
i != g_mime_signature_list_length(siglist); ++i) {
|
||||
GMimeSignature *sig = g_mime_signature_list_get_signature(siglist, i);
|
||||
sigs.emplace_back(MimeSignature(sig));
|
||||
}
|
||||
g_object_unref(siglist);
|
||||
|
||||
return sigs;
|
||||
}
|
||||
|
||||
|
||||
std::vector<MimeCertificate>
|
||||
|
@ -604,25 +608,85 @@ MimeDecryptResult::signatures() const noexcept
|
|||
|
||||
return sigs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Like verify, a bit of a monster, this impl.
|
||||
*
|
||||
* It's the transliteration of the g_mime_multipart_encrypted_decrypt() which
|
||||
* adds the feature of passing in the CryptoContext.
|
||||
*
|
||||
*/
|
||||
|
||||
Mu::Result<MimeMultipartEncrypted::Decrypted>
|
||||
MimeMultipartEncrypted::decrypt(DecryptFlags flags,
|
||||
MimeMultipartEncrypted::decrypt(const MimeCryptoContext& ctx, DecryptFlags dflags,
|
||||
const std::string& session_key) const noexcept
|
||||
{
|
||||
if (g_mime_multipart_get_count(GMIME_MULTIPART(self())) < 2)
|
||||
return Err(Error::Code::Crypto, "cannot decrypted, not enough subparts");
|
||||
|
||||
const auto proto{content_type_parameter("protocol")};
|
||||
const auto enc_proto{ctx.encryption_protocol()};
|
||||
|
||||
if (!proto || !enc_proto || !mime_types_equal(*proto, *enc_proto))
|
||||
return Err(Error::Code::Crypto, "unsupported protocol " +
|
||||
proto.value_or("<unknown>"));
|
||||
|
||||
const auto version{encrypted_version_part()};
|
||||
const auto encrypted{encrypted_content_part()};
|
||||
if (!version || !encrypted)
|
||||
return Err(Error::Code::Crypto, "cannot find part");
|
||||
|
||||
if (!mime_types_equal(version->mime_type().value_or(""), proto.value()))
|
||||
return Err(Error::Code::Crypto,
|
||||
"cannot decrypt; unexpected version content-type '%s' != '%s'",
|
||||
version->mime_type().value_or("").c_str(),
|
||||
proto.value().c_str());
|
||||
|
||||
if (!mime_types_equal(encrypted->mime_type().value_or(""), "application/octet-stream"))
|
||||
return Err(Error::Code::Crypto,
|
||||
"cannot decrypt; unexpected encrypted content-type '%s'",
|
||||
encrypted->mime_type().value_or("").c_str());
|
||||
|
||||
const auto content{encrypted->content()};
|
||||
auto ciphertext{MimeStream::make_mem()};
|
||||
content.write_to_stream(ciphertext);
|
||||
ciphertext.reset();
|
||||
|
||||
auto stream{MimeStream::make_mem()};
|
||||
auto filtered{MimeStream::make_filtered(stream)};
|
||||
auto filter{g_mime_filter_dos2unix_new(FALSE)};
|
||||
g_mime_stream_filter_add(GMIME_STREAM_FILTER(filtered.object()),
|
||||
filter);
|
||||
g_object_unref(filter);
|
||||
|
||||
GError *err{};
|
||||
GMimeDecryptResult *dres{};
|
||||
GMimeObject *obj = g_mime_multipart_encrypted_decrypt(
|
||||
self(),
|
||||
static_cast<GMimeDecryptFlags>(flags),
|
||||
session_key.empty() ? NULL : session_key.c_str(),
|
||||
&dres,
|
||||
&err);
|
||||
GMimeDecryptResult *dres =
|
||||
g_mime_crypto_context_decrypt(GMIME_CRYPTO_CONTEXT(ctx.object()),
|
||||
static_cast<GMimeDecryptFlags>(dflags),
|
||||
session_key.empty() ?
|
||||
NULL : session_key.c_str(),
|
||||
GMIME_STREAM(ciphertext.object()),
|
||||
GMIME_STREAM(filtered.object()),
|
||||
&err);
|
||||
if (!dres)
|
||||
return Err(Error::Code::Crypto, &err, "decryption failed");
|
||||
|
||||
if (!obj)
|
||||
return Err(Error::Code::Crypto, &err, "failed to decrypt");
|
||||
filtered.flush();
|
||||
stream.reset();
|
||||
|
||||
return Ok(Decrypted{MimeObject{obj}, MimeDecryptResult(dres)});
|
||||
auto parser{g_mime_parser_new()};
|
||||
g_mime_parser_init_with_stream(parser, GMIME_STREAM(stream.object()));
|
||||
|
||||
auto decrypted{g_mime_parser_construct_part(parser, NULL)};
|
||||
g_object_unref(parser);
|
||||
if (!decrypted) {
|
||||
g_object_unref(dres);
|
||||
return Err(Error::Code::Crypto, "failed to parse decrypted part");
|
||||
}
|
||||
|
||||
Decrypted result = { MimeObject{decrypted}, MimeDecryptResult{dres} };
|
||||
|
||||
g_object_unref(decrypted);
|
||||
g_object_unref(dres);
|
||||
|
||||
return Ok(std::move(result));
|
||||
}
|
||||
|
|
|
@ -35,6 +35,10 @@
|
|||
|
||||
namespace Mu {
|
||||
|
||||
/* non-GObject types */
|
||||
|
||||
using MimeFormatOptions = deletable_unique_ptr<GMimeFormatOptions, g_mime_format_options_free>;
|
||||
|
||||
/**
|
||||
* Initialize gmime (idempotent)
|
||||
*
|
||||
|
@ -126,11 +130,33 @@ public:
|
|||
*/
|
||||
operator bool() const noexcept { return !!self_; }
|
||||
|
||||
protected:
|
||||
GObject* object() const { return self(); }
|
||||
/**
|
||||
* Get a ptr to the underlying GObject
|
||||
*
|
||||
* @return GObject or NULL
|
||||
*/
|
||||
GObject* object() const { return self_; }
|
||||
|
||||
|
||||
/**
|
||||
* Unref the object
|
||||
*
|
||||
*/
|
||||
void unref() noexcept {
|
||||
g_object_unref(self_);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ref the object
|
||||
*
|
||||
*/
|
||||
void ref() noexcept {
|
||||
g_object_ref(self_);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
GObject *self() const { return self_; }
|
||||
mutable GObject *self_{};
|
||||
};
|
||||
|
||||
|
@ -153,7 +179,7 @@ struct MimeContentType: public Object {
|
|||
}
|
||||
|
||||
Option<std::string> mime_type() const noexcept {
|
||||
return to_string_opt(g_mime_content_type_get_mime_type(self()));
|
||||
return to_string_opt_gchar(g_mime_content_type_get_mime_type(self()));
|
||||
}
|
||||
|
||||
bool is_type(const std::string& type, const std::string& subtype) const {
|
||||
|
@ -167,16 +193,14 @@ private:
|
|||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Thin wrapper around a GMimeStream
|
||||
*
|
||||
*/
|
||||
struct MimeStream: public Object {
|
||||
MimeStream(GMimeStream *stream): Object(G_OBJECT(stream)) {
|
||||
if (!GMIME_IS_STREAM(self()))
|
||||
throw std::runtime_error("not a mime-stream");
|
||||
};
|
||||
|
||||
ssize_t write(const char* buf, ::size_t size) {
|
||||
return g_mime_stream_write(self(), buf, size);
|
||||
|
@ -186,6 +210,33 @@ struct MimeStream: public Object {
|
|||
return g_mime_stream_reset(self()) < 0 ? false : true;
|
||||
}
|
||||
|
||||
bool flush() {
|
||||
return g_mime_stream_flush(self()) < 0 ? false : true;
|
||||
}
|
||||
|
||||
static MimeStream make_mem() {
|
||||
MimeStream mstream{g_mime_stream_mem_new()};
|
||||
mstream.unref(); /* remove extra ref */
|
||||
return mstream;
|
||||
}
|
||||
|
||||
static MimeStream make_filtered(MimeStream& stream) {
|
||||
MimeStream mstream{g_mime_stream_filter_new(stream.self())};
|
||||
mstream.unref(); /* remove extra refs */
|
||||
return mstream;
|
||||
}
|
||||
|
||||
static MimeStream make_from_stream(GMimeStream *strm) {
|
||||
MimeStream mstream{strm};
|
||||
mstream.unref(); /* remove extra ref */
|
||||
return mstream;
|
||||
}
|
||||
|
||||
private:
|
||||
MimeStream(GMimeStream *stream): Object(G_OBJECT(stream)) {
|
||||
if (!GMIME_IS_STREAM(self()))
|
||||
throw std::runtime_error("not a mime-stream");
|
||||
};
|
||||
|
||||
GMimeStream* self() const {
|
||||
return reinterpret_cast<GMimeStream*>(object());
|
||||
|
@ -201,6 +252,32 @@ constexpr Option<std::string_view> to_string_view_opt(const S& seq, T t) {
|
|||
return it->second;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Thin wrapper around a GMimeDataWrapper
|
||||
*
|
||||
*/
|
||||
struct MimeDataWrapper: public Object {
|
||||
MimeDataWrapper(GMimeDataWrapper *wrapper): Object(G_OBJECT(wrapper)) {
|
||||
if (!GMIME_IS_DATA_WRAPPER(self()))
|
||||
throw std::runtime_error("not a data-wrapper");
|
||||
};
|
||||
|
||||
Result<size_t> write_to_stream(MimeStream& stream) const {
|
||||
if (auto&& res = g_mime_data_wrapper_write_to_stream(
|
||||
self(), GMIME_STREAM(stream.object())) ; res < 0)
|
||||
return Err(Error::Code::Message, "failed to write to stream");
|
||||
else
|
||||
return Ok(static_cast<size_t>(res));
|
||||
}
|
||||
|
||||
private:
|
||||
GMimeDataWrapper* self() const {
|
||||
return reinterpret_cast<GMimeDataWrapper*>(object());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Thin wrapper around a GMimeCertifcate
|
||||
|
@ -523,7 +600,6 @@ struct MimeDecryptResult: public Object {
|
|||
return to_string_opt(g_mime_decrypt_result_get_session_key(self()));
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
GMimeDecryptResult* self() const {
|
||||
return reinterpret_cast<GMimeDecryptResult*>(object());
|
||||
|
@ -574,19 +650,30 @@ struct MimeCryptoContext : public Object {
|
|||
if (auto&& res = setup_gpg_test(testpath); !res)
|
||||
return Err(res.error());
|
||||
}
|
||||
return Ok(MimeCryptoContext(g_mime_gpg_context_new()));
|
||||
MimeCryptoContext ctx(g_mime_gpg_context_new());
|
||||
ctx.unref(); /* remove extra ref */
|
||||
return Ok(std::move(ctx));
|
||||
} catch (...) {
|
||||
return Err(Error::Code::Crypto, "failed to create crypto context");
|
||||
}
|
||||
|
||||
static Result<MimeCryptoContext>
|
||||
make(const std::string& protocol) {
|
||||
auto ctx = g_mime_crypto_context_new(protocol.c_str());
|
||||
if (!ctx)
|
||||
return Err(Error::Code::Crypto, "unsupported protocol " + protocol);
|
||||
MimeCryptoContext mctx{ctx};
|
||||
mctx.unref(); /* remove extra ref */
|
||||
return Ok(std::move(mctx));
|
||||
}
|
||||
|
||||
Option<std::string> encryption_protocol() {
|
||||
Option<std::string> encryption_protocol() const noexcept {
|
||||
return to_string_opt(g_mime_crypto_context_get_encryption_protocol(self()));
|
||||
}
|
||||
Option<std::string> signature_protocol() {
|
||||
Option<std::string> signature_protocol() const noexcept {
|
||||
return to_string_opt(g_mime_crypto_context_get_signature_protocol(self()));
|
||||
}
|
||||
Option<std::string> key_exchange_protocol() {
|
||||
Option<std::string> key_exchange_protocol() const noexcept {
|
||||
return to_string_opt(g_mime_crypto_context_get_key_exchange_protocol(self()));
|
||||
}
|
||||
|
||||
|
@ -625,7 +712,7 @@ struct MimeCryptoContext : public Object {
|
|||
*
|
||||
* @param pw_func password function.
|
||||
*/
|
||||
void set_password_request_function(PasswordRequestFunc pw_func);
|
||||
void set_request_password(PasswordRequestFunc pw_func);
|
||||
|
||||
|
||||
private:
|
||||
|
@ -684,31 +771,41 @@ public:
|
|||
return MimeContentType(ct);
|
||||
}
|
||||
|
||||
Option<std::string> mime_type() const noexcept {
|
||||
if (auto ct = content_type(); !ct)
|
||||
return Nothing;
|
||||
else
|
||||
return ct->mime_type();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the content-type parameter
|
||||
*
|
||||
* @param param name of parameter
|
||||
*
|
||||
* @return the value of the parameter, or Nothing
|
||||
*/
|
||||
Option<std::string> content_type_parameter(const std::string& param) const noexcept {
|
||||
return to_string_opt(
|
||||
return Mu::to_string_opt(
|
||||
g_mime_object_get_content_type_parameter(self(), param.c_str()));
|
||||
}
|
||||
|
||||
|
||||
using MimeFormatOptions =
|
||||
deletable_unique_ptr<GMimeFormatOptions, g_mime_format_options_free>;
|
||||
|
||||
|
||||
Option<size_t> write_to_stream(const MimeFormatOptions& f_opts,
|
||||
MimeStream& stream) {
|
||||
auto written = g_mime_object_write_to_stream(self(), f_opts.get(), stream.self());
|
||||
if (written < 0)
|
||||
return Nothing;
|
||||
else
|
||||
return static_cast<size_t>(written);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write this MimeObject to some stream
|
||||
*
|
||||
* @param f_opts formatting options
|
||||
* @param stream the stream
|
||||
*
|
||||
* @return the number or bytes written or an error
|
||||
*/
|
||||
Result<size_t> write_to_stream(const MimeFormatOptions& f_opts,
|
||||
MimeStream& stream) const;
|
||||
/**
|
||||
* Write the object to a string.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Option<std::string> object_to_string() const noexcept;
|
||||
Option<std::string> to_string_opt() const noexcept;
|
||||
|
||||
/*
|
||||
* subtypes.
|
||||
|
@ -769,6 +866,13 @@ public:
|
|||
return GMIME_IS_APPLICATION_PKCS7_MIME(self());
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for for_each(). See GMimeObjectForEachFunc.
|
||||
*
|
||||
*/
|
||||
using ForEachFunc = std::function<void(const MimeObject& parent,
|
||||
const MimeObject& part)>;
|
||||
|
||||
private:
|
||||
GMimeObject* self() const {
|
||||
return reinterpret_cast<GMimeObject*>(object());
|
||||
|
@ -832,7 +936,7 @@ public:
|
|||
* @return string or nullopt
|
||||
*/
|
||||
Option<std::string> message_id() const noexcept {
|
||||
return to_string_opt(g_mime_message_get_message_id(self()));
|
||||
return Mu::to_string_opt(g_mime_message_get_message_id(self()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -841,7 +945,7 @@ public:
|
|||
* @return string or nullopt
|
||||
*/
|
||||
Option<std::string> subject() const noexcept {
|
||||
return to_string_opt(g_mime_message_get_subject(self()));
|
||||
return Mu::to_string_opt(g_mime_message_get_subject(self()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -861,13 +965,6 @@ public:
|
|||
std::vector<std::string> references() const noexcept;
|
||||
|
||||
|
||||
/**
|
||||
* Callback for for_each(). See GMimeObjectForEachFunc.
|
||||
*
|
||||
*/
|
||||
using ForEachFunc = std::function<void(const MimeObject& parent,
|
||||
const MimeObject& part)>;
|
||||
|
||||
/**
|
||||
* Recursively apply func tol all parts of this message
|
||||
*
|
||||
|
@ -914,7 +1011,7 @@ public:
|
|||
* @return string or nullopt
|
||||
*/
|
||||
Option<std::string> content_description() const noexcept {
|
||||
return to_string_opt(g_mime_part_get_content_description(self()));
|
||||
return Mu::to_string_opt(g_mime_part_get_content_description(self()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -924,7 +1021,7 @@ public:
|
|||
* @return string or nullopt
|
||||
*/
|
||||
Option<std::string> content_id() const noexcept {
|
||||
return to_string_opt(g_mime_part_get_content_id(self()));
|
||||
return Mu::to_string_opt(g_mime_part_get_content_id(self()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -934,7 +1031,7 @@ public:
|
|||
* @return string or nullopt
|
||||
*/
|
||||
Option<std::string> content_md5() const noexcept {
|
||||
return to_string_opt(g_mime_part_get_content_md5(self()));
|
||||
return Mu::to_string_opt(g_mime_part_get_content_md5(self()));
|
||||
|
||||
}
|
||||
|
||||
|
@ -955,7 +1052,12 @@ public:
|
|||
* @return string or nullopt
|
||||
*/
|
||||
Option<std::string> content_location() const noexcept {
|
||||
return to_string_opt(g_mime_part_get_content_location(self()));
|
||||
return Mu::to_string_opt(g_mime_part_get_content_location(self()));
|
||||
}
|
||||
|
||||
|
||||
MimeDataWrapper content() const noexcept {
|
||||
return MimeDataWrapper{g_mime_part_get_content(self())};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -965,7 +1067,7 @@ public:
|
|||
* @return string or nullopt
|
||||
*/
|
||||
Option<std::string> filename() const noexcept {
|
||||
return to_string_opt(g_mime_part_get_filename(self()));
|
||||
return Mu::to_string_opt(g_mime_part_get_filename(self()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1137,7 +1239,38 @@ public:
|
|||
throw std::runtime_error("not a mime-multipart");
|
||||
}
|
||||
|
||||
Option<MimePart> signed_content_part() const {
|
||||
return part(GMIME_MULTIPART_SIGNED_CONTENT);
|
||||
}
|
||||
|
||||
Option<MimePart> signed_signature_part() const {
|
||||
return part(GMIME_MULTIPART_SIGNED_SIGNATURE);
|
||||
}
|
||||
|
||||
Option<MimePart> encrypted_version_part() const {
|
||||
return part(GMIME_MULTIPART_ENCRYPTED_VERSION);
|
||||
}
|
||||
|
||||
Option<MimePart> encrypted_content_part() const {
|
||||
return part(GMIME_MULTIPART_ENCRYPTED_CONTENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively apply func to all parts
|
||||
*
|
||||
* @param func a function
|
||||
*/
|
||||
void for_each(const ForEachFunc& func) const noexcept;
|
||||
|
||||
private:
|
||||
|
||||
Option<MimePart> part(int index) const {
|
||||
if (MimeObject mobj{g_mime_multipart_get_part(self(),index)}; !mobj)
|
||||
return Nothing;
|
||||
else
|
||||
return mobj;
|
||||
}
|
||||
|
||||
GMimeMultipart* self() const {
|
||||
return reinterpret_cast<GMimeMultipart*>(object());
|
||||
}
|
||||
|
@ -1168,12 +1301,9 @@ public:
|
|||
EnableOnlineCertificateChecks = GMIME_DECRYPT_ENABLE_ONLINE_CERTIFICATE_CHECKS
|
||||
};
|
||||
|
||||
struct Decrypted {
|
||||
MimeObject decrypted_object;
|
||||
MimeDecryptResult decrypte_result;
|
||||
};
|
||||
|
||||
Result<Decrypted> decrypt(DecryptFlags flags=DecryptFlags::None,
|
||||
using Decrypted = std::pair<MimeObject, MimeDecryptResult>;
|
||||
Result<Decrypted> decrypt(const MimeCryptoContext& ctx,
|
||||
DecryptFlags flags=DecryptFlags::None,
|
||||
const std::string& session_key = {}) const noexcept;
|
||||
|
||||
private:
|
||||
|
@ -1207,8 +1337,10 @@ public:
|
|||
EnableOnlineCertificateChecks = GMIME_VERIFY_ENABLE_ONLINE_CERTIFICATE_CHECKS
|
||||
};
|
||||
|
||||
Result<std::vector<MimeSignature>>
|
||||
verify(VerifyFlags vflags=VerifyFlags::None) const noexcept;
|
||||
// Result<std::vector<MimeSignature>> verify(VerifyFlags vflags=VerifyFlags::None) const noexcept;
|
||||
|
||||
Result<std::vector<MimeSignature>> verify(const MimeCryptoContext& ctx,
|
||||
VerifyFlags vflags=VerifyFlags::None) const noexcept;
|
||||
|
||||
private:
|
||||
GMimeMultipartSigned* self() const {
|
||||
|
|
|
@ -62,7 +62,7 @@ statement you can use something like:
|
|||
goto * instructions[pOp->opcode];
|
||||
)";
|
||||
auto message{Message::make_from_text(
|
||||
test_message_1,
|
||||
{}, test_message_1,
|
||||
"/home/test/Maildir/inbox/cur/1649279256.107710_1.evergrey:2,S",
|
||||
"/inbox")};
|
||||
g_assert_true(!!message);
|
||||
|
@ -176,9 +176,11 @@ World!
|
|||
--=-=-=--
|
||||
)";
|
||||
|
||||
auto message{Message::make_from_text(msg_text)};
|
||||
auto message{Message::make_from_text({}, msg_text)};
|
||||
g_assert_true(!!message);
|
||||
|
||||
g_assert_true(message->path().empty());
|
||||
|
||||
g_assert_true(message->bcc().empty());
|
||||
g_assert_true(!message->body_html());
|
||||
assert_equal(message->body_text().value_or(""), R"(Hello,World!)");
|
||||
|
@ -316,7 +318,7 @@ Q46aYjxe0As6AP90bcAZ3dcn5RcTJaM0UhZssguawZ+tnriD3+5DPkMMCg==
|
|||
auto ctx{MimeCryptoContext::make_gpg(tempdir.path())};
|
||||
g_assert_true(!!ctx);
|
||||
|
||||
MimeStream stream{g_mime_stream_mem_new()};
|
||||
auto stream{MimeStream::make_mem()};
|
||||
stream.write(pub_key.data(), pub_key.size());
|
||||
stream.reset();
|
||||
|
||||
|
@ -324,6 +326,7 @@ Q46aYjxe0As6AP90bcAZ3dcn5RcTJaM0UhZssguawZ+tnriD3+5DPkMMCg==
|
|||
g_assert_cmpuint(*imported, ==, 1);
|
||||
|
||||
auto message{Message::make_from_text(
|
||||
{},
|
||||
msgtext,
|
||||
"/home/test/Maildir/inbox/cur/1649279777.107710_1.mindcrime:2,RS",
|
||||
"/inbox")};
|
||||
|
@ -343,9 +346,12 @@ Q46aYjxe0As6AP90bcAZ3dcn5RcTJaM0UhZssguawZ+tnriD3+5DPkMMCg==
|
|||
continue;
|
||||
|
||||
const auto mpart{MimeMultipartSigned(mobj)};
|
||||
const auto sigs{mpart.verify()};
|
||||
const auto sigs{mpart.verify(*ctx)};
|
||||
if (!sigs)
|
||||
g_warning("%s", sigs.error().what());
|
||||
|
||||
g_assert_true(!!sigs && sigs->size() == 1);
|
||||
g_assert_true(!!sigs);
|
||||
g_assert_cmpuint(sigs->size(), ==, 1);
|
||||
++n;
|
||||
}
|
||||
|
||||
|
@ -393,28 +399,27 @@ C0bdoCx44QVU8HaZ2x91h3GoM/0q5bqM/rvCauwbokiJgAUrznecNPY=
|
|||
g_assert_true(!!ctx);
|
||||
|
||||
/// test1234
|
||||
ctx->set_password_request_function(
|
||||
[](const MimeCryptoContext& ctx,
|
||||
const std::string& user_id,
|
||||
const std::string& prompt,
|
||||
bool reprompt,
|
||||
MimeStream& response)->Result<void> {
|
||||
|
||||
return Err(Error::Code::Internal, "boo");
|
||||
|
||||
g_warning("boo!");
|
||||
|
||||
return Ok();
|
||||
});
|
||||
// ctx->set_request_password([](const MimeCryptoContext& ctx,
|
||||
// const std::string& user_id,
|
||||
// const std::string& prompt,
|
||||
// bool reprompt,
|
||||
// MimeStream& response)->Result<void> {
|
||||
// return Err(Error::Code::Internal, "boo");
|
||||
// //return Ok();
|
||||
// });
|
||||
|
||||
{
|
||||
MimeStream stream{g_mime_stream_mem_new()};
|
||||
auto stream{MimeStream::make_mem()};
|
||||
stream.write(priv_key.data(), priv_key.size());
|
||||
stream.write(pub_key.data(), pub_key.size());
|
||||
stream.reset();
|
||||
|
||||
|
||||
g_assert_cmpint(ctx->import_keys(stream).value_or(-1),==,1);
|
||||
}
|
||||
|
||||
auto message{Message::make_from_text(
|
||||
{},
|
||||
msgtext,
|
||||
"/home/test/Maildir/inbox/cur/1649279888.107710_1.mindcrime:2,FS",
|
||||
"/archive")};
|
||||
|
@ -431,9 +436,12 @@ C0bdoCx44QVU8HaZ2x91h3GoM/0q5bqM/rvCauwbokiJgAUrznecNPY=
|
|||
if (!mobj.is_multipart_encrypted())
|
||||
continue;
|
||||
|
||||
const auto mpart{MimeMultipartEncrypted(mobj)};
|
||||
const auto decres = mpart.decrypt();
|
||||
g_assert_true(!!decres);
|
||||
/* FIXME: make this work without user having to
|
||||
* type password */
|
||||
|
||||
// const auto mpart{MimeMultipartEncrypted(mobj)};
|
||||
// const auto decres = mpart.decrypt(*ctx);
|
||||
// assert_valid_result(decres);
|
||||
|
||||
++n;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue