From 026a19bcfae84286161c1c592ef3fca4d12d1ae0 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Wed, 5 Apr 2023 00:05:10 +0300 Subject: [PATCH] message: allow extracting message parts to file And add unit-test. Fixes #2467 --- lib/message/mu-message-part.cc | 16 +++++++++------- lib/message/mu-message-part.hh | 3 +-- lib/message/mu-mime-object.cc | 24 ++++++++++++++++-------- lib/message/mu-mime-object.hh | 12 ++++++++++-- lib/message/test-mu-message.cc | 6 +++++- 5 files changed, 41 insertions(+), 20 deletions(-) diff --git a/lib/message/mu-message-part.cc b/lib/message/mu-message-part.cc index 0d59c580..73316cb5 100644 --- a/lib/message/mu-message-part.cc +++ b/lib/message/mu-message-part.cc @@ -1,5 +1,5 @@ /* -** Copyright (C) 2022 Dirk-Jan C. Binnema +** Copyright (C) 2023 Dirk-Jan C. Binnema ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the @@ -156,16 +156,18 @@ MessagePart::to_string() const noexcept return mime_object().to_string_opt(); } - - Result MessagePart::to_file(const std::string& path, bool overwrite) const noexcept { - if (!mime_object().is_part()) - return Err(Error::Code::InvalidArgument, - "not a part"); - else + if (mime_object().is_part()) return MimePart{mime_object()}.to_file(path, overwrite); + else if (mime_object().is_message_part()) { + if (auto&& msg{MimeMessagePart{mime_object()}.get_message()}; !msg) + return Err(Error::Code::Message, "failed to get message-part"); + else + return msg->to_file(path, overwrite); + } else + return mime_object().to_file(path, overwrite); } bool diff --git a/lib/message/mu-message-part.hh b/lib/message/mu-message-part.hh index 53367233..1d31e0eb 100644 --- a/lib/message/mu-message-part.hh +++ b/lib/message/mu-message-part.hh @@ -1,5 +1,5 @@ /* -** Copyright (C) 2022 Dirk-Jan C. Binnema +** Copyright (C) 2023 Dirk-Jan C. Binnema ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the @@ -53,7 +53,6 @@ public: */ ~MessagePart(); - /** * Get the underlying MimeObject; you need to include mu-mime-object.hh * to do anything useful with it. diff --git a/lib/message/mu-mime-object.cc b/lib/message/mu-mime-object.cc index e5879de4..c1cc8e56 100644 --- a/lib/message/mu-mime-object.cc +++ b/lib/message/mu-mime-object.cc @@ -125,8 +125,6 @@ MimeObject::headers() const noexcept return hdrs; } - - Result MimeObject::write_to_stream(const MimeFormatOptions& f_opts, MimeStream& stream) const @@ -139,6 +137,22 @@ MimeObject::write_to_stream(const MimeFormatOptions& f_opts, return Ok(static_cast(written)); } +Result +MimeObject::to_file(const std::string& path, bool overwrite) const noexcept +{ + GError *err{}; + 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)}; + return write_to_stream({}, stream); +} + + Option MimeObject::to_string_opt() const noexcept { @@ -525,11 +539,8 @@ MimePart::to_string() const noexcept buffer.resize(buflen); return buffer; - } - - Result MimePart::to_file(const std::string& path, bool overwrite) const noexcept { @@ -558,9 +569,6 @@ MimePart::to_file(const std::string& path, bool overwrite) const noexcept return Ok(static_cast(written)); } - - - void MimeMultipart::for_each(const ForEachFunc& func) const noexcept { diff --git a/lib/message/mu-mime-object.hh b/lib/message/mu-mime-object.hh index 56a4b56c..af0e23f7 100644 --- a/lib/message/mu-mime-object.hh +++ b/lib/message/mu-mime-object.hh @@ -829,6 +829,16 @@ public: */ Option to_string_opt() const noexcept; + /** + * Write object to a file + * + * @param path path to file + * @param overwrite if true, overwrite existing file, if it bqexists + * + * @return size of the wrtten file, or an error. + */ + Result to_file(const std::string& path, bool overwrite) const noexcept; + /* * subtypes. */ @@ -1101,7 +1111,6 @@ public: */ Option to_string() const noexcept; - /** * Write part to a file * @@ -1113,7 +1122,6 @@ public: Result to_file(const std::string& path, bool overwrite) const noexcept; - /** * Types of Content Encoding. * diff --git a/lib/message/test-mu-message.cc b/lib/message/test-mu-message.cc index 124b2645..68e2e68a 100644 --- a/lib/message/test-mu-message.cc +++ b/lib/message/test-mu-message.cc @@ -1,5 +1,5 @@ /* -** Copyright (C) 2022 Dirk-Jan C. Binnema +** Copyright (C) 2023 Dirk-Jan C. Binnema ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the @@ -249,6 +249,10 @@ World! { auto&& part{message->parts().at(3)}; g_assert_true(part.mime_type() == "message/rfc822"); + + const auto fname{*cache_path + "/msgpart"}; + g_assert_cmpuint(part.to_file(fname, true).value_or(123), ==, 139); + g_assert_true(::access(fname.c_str(), F_OK) == 0); } {