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;
|
using namespace Mu;
|
||||||
|
|
||||||
struct Message::Private {
|
struct Message::Private {
|
||||||
|
Private(Message::Options options=Message::Options::None):
|
||||||
|
opts{options} {}
|
||||||
|
|
||||||
|
Message::Options opts;
|
||||||
Document doc;
|
Document doc;
|
||||||
mutable Option<MimeMessage> mime_msg;
|
mutable Option<MimeMessage> mime_msg;
|
||||||
|
|
||||||
|
@ -66,8 +69,8 @@ struct Message::Private {
|
||||||
|
|
||||||
static void fill_document(Message::Private& priv);
|
static void fill_document(Message::Private& priv);
|
||||||
|
|
||||||
Message::Message(const std::string& path, const std::string& mdir):
|
Message::Message(Message::Options opts, const std::string& path, const std::string& mdir):
|
||||||
priv_{std::make_unique<Private>()}
|
priv_{std::make_unique<Private>(opts)}
|
||||||
{
|
{
|
||||||
if (!g_path_is_absolute(path.c_str()))
|
if (!g_path_is_absolute(path.c_str()))
|
||||||
throw Error(Error::Code::File, "path '%s' is not 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())
|
if (!mdir.empty())
|
||||||
priv_->doc.add(Field::Id::Maildir, mdir);
|
priv_->doc.add(Field::Id::Maildir, mdir);
|
||||||
|
|
||||||
priv_->doc.add(Field::Id::Size, static_cast<int64_t>(statbuf.st_size));
|
priv_->doc.add(Field::Id::Size, static_cast<int64_t>(statbuf.st_size));
|
||||||
|
|
||||||
// rest of the fields
|
// rest of the fields
|
||||||
fill_document(*priv_);
|
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):
|
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 (!path.empty()) {
|
||||||
if (xpath)
|
auto xpath{to_string_opt_gchar(g_canonicalize_filename(path.c_str(), {}))};
|
||||||
priv_->doc.add(Field::Id::Path, std::move(xpath.value()));
|
if (xpath)
|
||||||
|
priv_->doc.add(Field::Id::Path, std::move(xpath.value()));
|
||||||
|
}
|
||||||
|
|
||||||
if (!mdir.empty())
|
if (!mdir.empty())
|
||||||
priv_->doc.add(Field::Id::Maildir, mdir);
|
priv_->doc.add(Field::Id::Maildir, mdir);
|
||||||
|
@ -327,6 +333,83 @@ process_part(const MimeObject& parent, const MimePart& part,
|
||||||
accumulate_text(part, info, *ctype);
|
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
|
* This message -- recursively walk through message, and initialize some
|
||||||
* other values that depend on another.
|
* other values that depend on another.
|
||||||
|
@ -349,36 +432,8 @@ process_message(const MimeMessage& mime_msg, const std::string& path,
|
||||||
}
|
}
|
||||||
|
|
||||||
// parts
|
// parts
|
||||||
mime_msg.for_each([&](auto&& parent, auto&& part) {
|
mime_msg.for_each([&](auto&& parent, auto&& child_obj) {
|
||||||
|
handle_object(parent, child_obj, info);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// get the mailing here, and use it do update flags, too.
|
// get the mailing here, and use it do update flags, too.
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include "mu-document.hh"
|
#include "mu-document.hh"
|
||||||
#include "mu-message-part.hh"
|
#include "mu-message-part.hh"
|
||||||
|
|
||||||
|
#include "utils/mu-utils.hh"
|
||||||
#include "utils/mu-option.hh"
|
#include "utils/mu-option.hh"
|
||||||
#include "utils/mu-result.hh"
|
#include "utils/mu-result.hh"
|
||||||
#include "utils/mu-sexp.hh"
|
#include "utils/mu-sexp.hh"
|
||||||
|
@ -38,6 +39,14 @@ namespace Mu {
|
||||||
|
|
||||||
class Message {
|
class Message {
|
||||||
public:
|
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
|
* Move CTOR
|
||||||
*
|
*
|
||||||
|
@ -49,6 +58,7 @@ public:
|
||||||
* Construct a message based on a path. The maildir is optional; however
|
* Construct a message based on a path. The maildir is optional; however
|
||||||
* messages without maildir cannot be stored in the database
|
* messages without maildir cannot be stored in the database
|
||||||
*
|
*
|
||||||
|
* @param opts options
|
||||||
* @param path path to message
|
* @param path path to message
|
||||||
* @param mdir the maildir for this message; i.e, if the path is
|
* @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
|
* ~/Maildir/foo/bar/cur/msg, the maildir would be foo/bar; you can
|
||||||
|
@ -57,9 +67,10 @@ public:
|
||||||
*
|
*
|
||||||
* @return a message or an error
|
* @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 {
|
const std::string& mdir={}) try {
|
||||||
return Ok(Message{path, mdir});
|
return Ok(Message{opts, path, mdir});
|
||||||
} catch (Error& err) {
|
} catch (Error& err) {
|
||||||
return Err(err);
|
return Err(err);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
|
@ -85,6 +96,7 @@ public:
|
||||||
/**
|
/**
|
||||||
* Construct a message from a string. This is mostly useful for testing.
|
* Construct a message from a string. This is mostly useful for testing.
|
||||||
*
|
*
|
||||||
|
* @param opts options
|
||||||
* @param text message text
|
* @param text message text
|
||||||
* @param path path to message - optional; path does not have to exist.
|
* @param path path to message - optional; path does not have to exist.
|
||||||
* this is useful for testing.
|
* this is useful for testing.
|
||||||
|
@ -92,10 +104,11 @@ public:
|
||||||
*
|
*
|
||||||
* @return a message or an error
|
* @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& path={},
|
||||||
const std::string& mdir={}) try {
|
const std::string& mdir={}) try {
|
||||||
return Ok(Message{text, path, mdir});
|
return Ok(Message{opts, text, path, mdir});
|
||||||
} catch (Error& err) {
|
} catch (Error& err) {
|
||||||
return Err(err);
|
return Err(err);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
|
@ -330,13 +343,14 @@ public:
|
||||||
|
|
||||||
struct Private;
|
struct Private;
|
||||||
private:
|
private:
|
||||||
Message(const std::string& path, const std::string& mdir);
|
Message(Options opts, const std::string& path, const std::string& mdir);
|
||||||
Message(const std::string& str, const std::string& path,
|
Message(Options opts, const std::string& str, const std::string& path,
|
||||||
const std::string& mdir);
|
const std::string& mdir);
|
||||||
Message(Document& doc);
|
Message(Document& doc);
|
||||||
|
|
||||||
|
|
||||||
std::unique_ptr<Private> priv_;
|
std::unique_ptr<Private> priv_;
|
||||||
}; // Message
|
}; // Message
|
||||||
|
MU_ENABLE_BITOPS(Message::Options);
|
||||||
|
|
||||||
} // Mu
|
} // Mu
|
||||||
#endif /* MU_MESSAGE_HH__ */
|
#endif /* MU_MESSAGE_HH__ */
|
||||||
|
|
|
@ -81,16 +81,29 @@ MimeObject::header(const std::string& hdr) const noexcept
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Option<std::string>
|
Result<size_t>
|
||||||
MimeObject::object_to_string() const noexcept
|
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) {
|
if (!stream) {
|
||||||
g_warning("failed to create mem stream");
|
g_warning("failed to create mem stream");
|
||||||
return Nothing;
|
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) {
|
if (written < 0) {
|
||||||
g_warning("failed to write object to stream");
|
g_warning("failed to write object to stream");
|
||||||
return Nothing;
|
return Nothing;
|
||||||
|
@ -98,10 +111,10 @@ MimeObject::object_to_string() const noexcept
|
||||||
|
|
||||||
std::string buffer;
|
std::string buffer;
|
||||||
buffer.resize(written + 1);
|
buffer.resize(written + 1);
|
||||||
g_mime_stream_reset(stream);
|
stream.reset();
|
||||||
|
|
||||||
auto bytes{g_mime_stream_read(stream, buffer.data(), written)};
|
auto bytes{g_mime_stream_read(GMIME_STREAM(stream.object()),
|
||||||
g_object_unref(stream);
|
buffer.data(), written)};
|
||||||
if (bytes < 0)
|
if (bytes < 0)
|
||||||
return Nothing;
|
return Nothing;
|
||||||
|
|
||||||
|
@ -121,7 +134,7 @@ MimeCryptoContext::import_keys(MimeStream& stream)
|
||||||
{
|
{
|
||||||
GError *err{};
|
GError *err{};
|
||||||
auto res = g_mime_crypto_context_import_keys(
|
auto res = g_mime_crypto_context_import_keys(
|
||||||
self(), stream.self(), &err);
|
self(), GMIME_STREAM(stream.object()), &err);
|
||||||
|
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
return Err(Error::Code::File, &err,
|
return Err(Error::Code::File, &err,
|
||||||
|
@ -131,7 +144,7 @@ MimeCryptoContext::import_keys(MimeStream& stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MimeCryptoContext::set_password_request_function(PasswordRequestFunc pw_func)
|
MimeCryptoContext::set_request_password(PasswordRequestFunc pw_func)
|
||||||
{
|
{
|
||||||
static auto request_func = pw_func;
|
static auto request_func = pw_func;
|
||||||
|
|
||||||
|
@ -143,7 +156,8 @@ MimeCryptoContext::set_password_request_function(PasswordRequestFunc pw_func)
|
||||||
gboolean reprompt,
|
gboolean reprompt,
|
||||||
GMimeStream *response,
|
GMimeStream *response,
|
||||||
GError **err) -> gboolean {
|
GError **err) -> gboolean {
|
||||||
MimeStream mstream{response};
|
MimeStream mstream{MimeStream::make_from_stream(response)};
|
||||||
|
|
||||||
auto res = request_func(MimeCryptoContext(ctx),
|
auto res = request_func(MimeCryptoContext(ctx),
|
||||||
std::string{user_id ? user_id : ""},
|
std::string{user_id ? user_id : ""},
|
||||||
std::string{prompt ? prompt : ""},
|
std::string{prompt ? prompt : ""},
|
||||||
|
@ -420,23 +434,23 @@ MimePart::to_string() const noexcept
|
||||||
Result<size_t>
|
Result<size_t>
|
||||||
MimePart::to_file(const std::string& path, bool overwrite) const noexcept
|
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 */
|
if (!wrapper) /* this happens with invalid mails */
|
||||||
return Err(Error::Code::File, "failed to create data wrapper");
|
return Err(Error::Code::File, "failed to create data wrapper");
|
||||||
|
|
||||||
|
|
||||||
GError *err{};
|
GError *err{};
|
||||||
GMimeStream *stream{g_mime_stream_fs_open(
|
auto strm{g_mime_stream_fs_open(path.c_str(),
|
||||||
path.c_str(),
|
O_WRONLY | O_CREAT | O_TRUNC |(overwrite ? 0 : O_EXCL),
|
||||||
O_WRONLY | O_CREAT | O_TRUNC |(overwrite ? 0 : O_EXCL),
|
S_IRUSR|S_IWUSR,
|
||||||
S_IRUSR|S_IWUSR,
|
&err)};
|
||||||
&err)};
|
if (!strm)
|
||||||
if (!stream)
|
return Err(Error::Code::File, &err, "failed to open '%s'", path.c_str());
|
||||||
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) {
|
if (written < 0) {
|
||||||
return Err(Error::Code::File, &err,
|
return Err(Error::Code::File, &err,
|
||||||
"failed to write to '%s'", path.c_str());
|
"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{};
|
struct CallbackData { const ForEachFunc& func; };
|
||||||
GMimeSignatureList *siglist = g_mime_multipart_signed_verify(
|
CallbackData cbd{func};
|
||||||
|
|
||||||
|
g_mime_multipart_foreach(
|
||||||
self(),
|
self(),
|
||||||
static_cast<GMimeVerifyFlags>(vflags),
|
[] (GMimeObject *parent, GMimeObject *part, gpointer user_data) {
|
||||||
&err);
|
auto cb_data{reinterpret_cast<CallbackData*>(user_data)};
|
||||||
|
cb_data->func(MimeObject{parent}, MimeObject{part});
|
||||||
if (!siglist)
|
}, &cbd);
|
||||||
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;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* we need to be able to pass a crypto-context to the verify(), but
|
* 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.
|
* 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
|
static bool
|
||||||
mime_types_equal (const std::string& mime_type, const std::string& official_type)
|
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;
|
return true;
|
||||||
|
|
||||||
const auto slash_pos = official_type.find("/");
|
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>>
|
if (!proto || !sign_proto || !mime_types_equal(*proto, *sign_proto))
|
||||||
// MimeMultipartSigned::verify(VerifyFlags vflags, const CryptoContext& ctx) const noexcept
|
return Err(Error::Code::Crypto, "unsupported protocol " +
|
||||||
// {
|
proto.value_or("<unknown>"));
|
||||||
// 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;
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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>
|
std::vector<MimeCertificate>
|
||||||
|
@ -604,25 +608,85 @@ MimeDecryptResult::signatures() const noexcept
|
||||||
|
|
||||||
return sigs;
|
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>
|
Mu::Result<MimeMultipartEncrypted::Decrypted>
|
||||||
MimeMultipartEncrypted::decrypt(DecryptFlags flags,
|
MimeMultipartEncrypted::decrypt(const MimeCryptoContext& ctx, DecryptFlags dflags,
|
||||||
const std::string& session_key) const noexcept
|
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{};
|
GError *err{};
|
||||||
GMimeDecryptResult *dres{};
|
GMimeDecryptResult *dres =
|
||||||
GMimeObject *obj = g_mime_multipart_encrypted_decrypt(
|
g_mime_crypto_context_decrypt(GMIME_CRYPTO_CONTEXT(ctx.object()),
|
||||||
self(),
|
static_cast<GMimeDecryptFlags>(dflags),
|
||||||
static_cast<GMimeDecryptFlags>(flags),
|
session_key.empty() ?
|
||||||
session_key.empty() ? NULL : session_key.c_str(),
|
NULL : session_key.c_str(),
|
||||||
&dres,
|
GMIME_STREAM(ciphertext.object()),
|
||||||
&err);
|
GMIME_STREAM(filtered.object()),
|
||||||
|
&err);
|
||||||
|
if (!dres)
|
||||||
|
return Err(Error::Code::Crypto, &err, "decryption failed");
|
||||||
|
|
||||||
if (!obj)
|
filtered.flush();
|
||||||
return Err(Error::Code::Crypto, &err, "failed to decrypt");
|
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 {
|
namespace Mu {
|
||||||
|
|
||||||
|
/* non-GObject types */
|
||||||
|
|
||||||
|
using MimeFormatOptions = deletable_unique_ptr<GMimeFormatOptions, g_mime_format_options_free>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize gmime (idempotent)
|
* Initialize gmime (idempotent)
|
||||||
*
|
*
|
||||||
|
@ -126,11 +130,33 @@ public:
|
||||||
*/
|
*/
|
||||||
operator bool() const noexcept { return !!self_; }
|
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:
|
private:
|
||||||
GObject *self() const { return self_; }
|
|
||||||
mutable GObject *self_{};
|
mutable GObject *self_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -153,7 +179,7 @@ struct MimeContentType: public Object {
|
||||||
}
|
}
|
||||||
|
|
||||||
Option<std::string> mime_type() const noexcept {
|
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 {
|
bool is_type(const std::string& type, const std::string& subtype) const {
|
||||||
|
@ -167,16 +193,14 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thin wrapper around a GMimeStream
|
* Thin wrapper around a GMimeStream
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
struct MimeStream: public Object {
|
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) {
|
ssize_t write(const char* buf, ::size_t size) {
|
||||||
return g_mime_stream_write(self(), buf, 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;
|
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 {
|
GMimeStream* self() const {
|
||||||
return reinterpret_cast<GMimeStream*>(object());
|
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;
|
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
|
* 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()));
|
return to_string_opt(g_mime_decrypt_result_get_session_key(self()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GMimeDecryptResult* self() const {
|
GMimeDecryptResult* self() const {
|
||||||
return reinterpret_cast<GMimeDecryptResult*>(object());
|
return reinterpret_cast<GMimeDecryptResult*>(object());
|
||||||
|
@ -574,19 +650,30 @@ struct MimeCryptoContext : public Object {
|
||||||
if (auto&& res = setup_gpg_test(testpath); !res)
|
if (auto&& res = setup_gpg_test(testpath); !res)
|
||||||
return Err(res.error());
|
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 (...) {
|
} catch (...) {
|
||||||
return Err(Error::Code::Crypto, "failed to create crypto context");
|
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()));
|
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()));
|
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()));
|
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.
|
* @param pw_func password function.
|
||||||
*/
|
*/
|
||||||
void set_password_request_function(PasswordRequestFunc pw_func);
|
void set_request_password(PasswordRequestFunc pw_func);
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -684,31 +771,41 @@ public:
|
||||||
return MimeContentType(ct);
|
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 {
|
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()));
|
g_mime_object_get_content_type_parameter(self(), param.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
using MimeFormatOptions =
|
* Write this MimeObject to some stream
|
||||||
deletable_unique_ptr<GMimeFormatOptions, g_mime_format_options_free>;
|
*
|
||||||
|
* @param f_opts formatting options
|
||||||
|
* @param stream the stream
|
||||||
Option<size_t> write_to_stream(const MimeFormatOptions& f_opts,
|
*
|
||||||
MimeStream& stream) {
|
* @return the number or bytes written or an error
|
||||||
auto written = g_mime_object_write_to_stream(self(), f_opts.get(), stream.self());
|
*/
|
||||||
if (written < 0)
|
Result<size_t> write_to_stream(const MimeFormatOptions& f_opts,
|
||||||
return Nothing;
|
MimeStream& stream) const;
|
||||||
else
|
|
||||||
return static_cast<size_t>(written);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write the object to a string.
|
* Write the object to a string.
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
Option<std::string> object_to_string() const noexcept;
|
Option<std::string> to_string_opt() const noexcept;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* subtypes.
|
* subtypes.
|
||||||
|
@ -769,6 +866,13 @@ public:
|
||||||
return GMIME_IS_APPLICATION_PKCS7_MIME(self());
|
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:
|
private:
|
||||||
GMimeObject* self() const {
|
GMimeObject* self() const {
|
||||||
return reinterpret_cast<GMimeObject*>(object());
|
return reinterpret_cast<GMimeObject*>(object());
|
||||||
|
@ -832,7 +936,7 @@ public:
|
||||||
* @return string or nullopt
|
* @return string or nullopt
|
||||||
*/
|
*/
|
||||||
Option<std::string> message_id() const noexcept {
|
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
|
* @return string or nullopt
|
||||||
*/
|
*/
|
||||||
Option<std::string> subject() const noexcept {
|
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;
|
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
|
* Recursively apply func tol all parts of this message
|
||||||
*
|
*
|
||||||
|
@ -914,7 +1011,7 @@ public:
|
||||||
* @return string or nullopt
|
* @return string or nullopt
|
||||||
*/
|
*/
|
||||||
Option<std::string> content_description() const noexcept {
|
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
|
* @return string or nullopt
|
||||||
*/
|
*/
|
||||||
Option<std::string> content_id() const noexcept {
|
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
|
* @return string or nullopt
|
||||||
*/
|
*/
|
||||||
Option<std::string> content_md5() const noexcept {
|
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
|
* @return string or nullopt
|
||||||
*/
|
*/
|
||||||
Option<std::string> content_location() const noexcept {
|
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
|
* @return string or nullopt
|
||||||
*/
|
*/
|
||||||
Option<std::string> filename() const noexcept {
|
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");
|
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:
|
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 {
|
GMimeMultipart* self() const {
|
||||||
return reinterpret_cast<GMimeMultipart*>(object());
|
return reinterpret_cast<GMimeMultipart*>(object());
|
||||||
}
|
}
|
||||||
|
@ -1168,12 +1301,9 @@ public:
|
||||||
EnableOnlineCertificateChecks = GMIME_DECRYPT_ENABLE_ONLINE_CERTIFICATE_CHECKS
|
EnableOnlineCertificateChecks = GMIME_DECRYPT_ENABLE_ONLINE_CERTIFICATE_CHECKS
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Decrypted {
|
using Decrypted = std::pair<MimeObject, MimeDecryptResult>;
|
||||||
MimeObject decrypted_object;
|
Result<Decrypted> decrypt(const MimeCryptoContext& ctx,
|
||||||
MimeDecryptResult decrypte_result;
|
DecryptFlags flags=DecryptFlags::None,
|
||||||
};
|
|
||||||
|
|
||||||
Result<Decrypted> decrypt(DecryptFlags flags=DecryptFlags::None,
|
|
||||||
const std::string& session_key = {}) const noexcept;
|
const std::string& session_key = {}) const noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -1207,8 +1337,10 @@ public:
|
||||||
EnableOnlineCertificateChecks = GMIME_VERIFY_ENABLE_ONLINE_CERTIFICATE_CHECKS
|
EnableOnlineCertificateChecks = GMIME_VERIFY_ENABLE_ONLINE_CERTIFICATE_CHECKS
|
||||||
};
|
};
|
||||||
|
|
||||||
Result<std::vector<MimeSignature>>
|
// Result<std::vector<MimeSignature>> verify(VerifyFlags vflags=VerifyFlags::None) const noexcept;
|
||||||
verify(VerifyFlags vflags=VerifyFlags::None) const noexcept;
|
|
||||||
|
Result<std::vector<MimeSignature>> verify(const MimeCryptoContext& ctx,
|
||||||
|
VerifyFlags vflags=VerifyFlags::None) const noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GMimeMultipartSigned* self() const {
|
GMimeMultipartSigned* self() const {
|
||||||
|
|
|
@ -62,7 +62,7 @@ statement you can use something like:
|
||||||
goto * instructions[pOp->opcode];
|
goto * instructions[pOp->opcode];
|
||||||
)";
|
)";
|
||||||
auto message{Message::make_from_text(
|
auto message{Message::make_from_text(
|
||||||
test_message_1,
|
{}, test_message_1,
|
||||||
"/home/test/Maildir/inbox/cur/1649279256.107710_1.evergrey:2,S",
|
"/home/test/Maildir/inbox/cur/1649279256.107710_1.evergrey:2,S",
|
||||||
"/inbox")};
|
"/inbox")};
|
||||||
g_assert_true(!!message);
|
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);
|
||||||
|
|
||||||
|
g_assert_true(message->path().empty());
|
||||||
|
|
||||||
g_assert_true(message->bcc().empty());
|
g_assert_true(message->bcc().empty());
|
||||||
g_assert_true(!message->body_html());
|
g_assert_true(!message->body_html());
|
||||||
assert_equal(message->body_text().value_or(""), R"(Hello,World!)");
|
assert_equal(message->body_text().value_or(""), R"(Hello,World!)");
|
||||||
|
@ -316,7 +318,7 @@ Q46aYjxe0As6AP90bcAZ3dcn5RcTJaM0UhZssguawZ+tnriD3+5DPkMMCg==
|
||||||
auto ctx{MimeCryptoContext::make_gpg(tempdir.path())};
|
auto ctx{MimeCryptoContext::make_gpg(tempdir.path())};
|
||||||
g_assert_true(!!ctx);
|
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.write(pub_key.data(), pub_key.size());
|
||||||
stream.reset();
|
stream.reset();
|
||||||
|
|
||||||
|
@ -324,6 +326,7 @@ Q46aYjxe0As6AP90bcAZ3dcn5RcTJaM0UhZssguawZ+tnriD3+5DPkMMCg==
|
||||||
g_assert_cmpuint(*imported, ==, 1);
|
g_assert_cmpuint(*imported, ==, 1);
|
||||||
|
|
||||||
auto message{Message::make_from_text(
|
auto message{Message::make_from_text(
|
||||||
|
{},
|
||||||
msgtext,
|
msgtext,
|
||||||
"/home/test/Maildir/inbox/cur/1649279777.107710_1.mindcrime:2,RS",
|
"/home/test/Maildir/inbox/cur/1649279777.107710_1.mindcrime:2,RS",
|
||||||
"/inbox")};
|
"/inbox")};
|
||||||
|
@ -343,9 +346,12 @@ Q46aYjxe0As6AP90bcAZ3dcn5RcTJaM0UhZssguawZ+tnriD3+5DPkMMCg==
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const auto mpart{MimeMultipartSigned(mobj)};
|
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;
|
++n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,28 +399,27 @@ C0bdoCx44QVU8HaZ2x91h3GoM/0q5bqM/rvCauwbokiJgAUrznecNPY=
|
||||||
g_assert_true(!!ctx);
|
g_assert_true(!!ctx);
|
||||||
|
|
||||||
/// test1234
|
/// test1234
|
||||||
ctx->set_password_request_function(
|
// ctx->set_request_password([](const MimeCryptoContext& ctx,
|
||||||
[](const MimeCryptoContext& ctx,
|
// const std::string& user_id,
|
||||||
const std::string& user_id,
|
// const std::string& prompt,
|
||||||
const std::string& prompt,
|
// bool reprompt,
|
||||||
bool reprompt,
|
// MimeStream& response)->Result<void> {
|
||||||
MimeStream& response)->Result<void> {
|
// return Err(Error::Code::Internal, "boo");
|
||||||
|
// //return Ok();
|
||||||
return Err(Error::Code::Internal, "boo");
|
// });
|
||||||
|
|
||||||
g_warning("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(priv_key.data(), priv_key.size());
|
||||||
|
stream.write(pub_key.data(), pub_key.size());
|
||||||
stream.reset();
|
stream.reset();
|
||||||
|
|
||||||
|
|
||||||
g_assert_cmpint(ctx->import_keys(stream).value_or(-1),==,1);
|
g_assert_cmpint(ctx->import_keys(stream).value_or(-1),==,1);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto message{Message::make_from_text(
|
auto message{Message::make_from_text(
|
||||||
|
{},
|
||||||
msgtext,
|
msgtext,
|
||||||
"/home/test/Maildir/inbox/cur/1649279888.107710_1.mindcrime:2,FS",
|
"/home/test/Maildir/inbox/cur/1649279888.107710_1.mindcrime:2,FS",
|
||||||
"/archive")};
|
"/archive")};
|
||||||
|
@ -431,9 +436,12 @@ C0bdoCx44QVU8HaZ2x91h3GoM/0q5bqM/rvCauwbokiJgAUrznecNPY=
|
||||||
if (!mobj.is_multipart_encrypted())
|
if (!mobj.is_multipart_encrypted())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const auto mpart{MimeMultipartEncrypted(mobj)};
|
/* FIXME: make this work without user having to
|
||||||
const auto decres = mpart.decrypt();
|
* type password */
|
||||||
g_assert_true(!!decres);
|
|
||||||
|
// const auto mpart{MimeMultipartEncrypted(mobj)};
|
||||||
|
// const auto decres = mpart.decrypt(*ctx);
|
||||||
|
// assert_valid_result(decres);
|
||||||
|
|
||||||
++n;
|
++n;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue