diff --git a/lib/message/mu-message.cc b/lib/message/mu-message.cc index a480ac9e..231214db 100644 --- a/lib/message/mu-message.cc +++ b/lib/message/mu-message.cc @@ -1,5 +1,5 @@ /* - ** Copyright (C) 2022 Dirk-Jan C. Binnema +** Copyright (C) 2022 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 @@ -66,7 +66,7 @@ struct Message::Private { static void fill_document(Message::Private& priv); -Message::Message(const std::string& path, Option mdir): +Message::Message(const std::string& path, const std::string& mdir): priv_{std::make_unique()} { if (!g_path_is_absolute(path.c_str())) @@ -91,20 +91,29 @@ Message::Message(const std::string& path, Option mdir): priv_->doc.add(Field::Id::Path, Mu::from_gchars(g_canonicalize_filename(path.c_str(), NULL))); - priv_->doc.add(Field::Id::Maildir, mdir); + if (!mdir.empty()) + priv_->doc.add(Field::Id::Maildir, mdir); priv_->doc.add(Field::Id::Size, static_cast(statbuf.st_size)); // rest of the fields fill_document(*priv_); } -Message::Message(const std::string& text): +Message::Message(const std::string& text, const std::string& path, + const std::string& mdir): priv_{std::make_unique()} { + if (!path.empty()) + priv_->doc.add(Field::Id::Path, + Mu::from_gchars( + g_canonicalize_filename(path.c_str(), NULL))); + if (!mdir.empty()) + priv_->doc.add(Field::Id::Maildir, mdir); + priv_->doc.add(Field::Id::Size, static_cast(text.size())); init_gmime(); - if (auto msg{MimeMessage::make_from_string(text)}; !msg) + if (auto msg{MimeMessage::make_from_text(text)}; !msg) throw msg.error(); else priv_->mime_msg = std::move(msg.value()); @@ -318,17 +327,21 @@ static void process_message(const MimeMessage& mime_msg, const std::string& path, Message::Private& info) { - info.flags = flags_from_path(path).value_or(Flags::None); - - /* pseudo-flag --> unread means either NEW or NOT SEEN, just - * for searching convenience */ - if (any_of(info.flags & Flags::New) || none_of(info.flags & Flags::Seen)) - info.flags |= Flags::Unread; + /* only have file-flags when there's a path. */ + if (!path.empty()) { + info.flags = flags_from_path(path).value_or(Flags::None); + /* pseudo-flag --> unread means either NEW or NOT SEEN, just + * for searching convenience */ + if (any_of(info.flags & Flags::New) || none_of(info.flags & Flags::Seen)) + info.flags |= Flags::Unread; + } // parts mime_msg.for_each([&](auto&& parent, auto&& part) { - if (part.is_part() || part.is_message_part()) + if (part.is_part() || + part.is_message_part() || + part.is_multipart_signed()) info.parts.emplace_back(part); if (part.is_part()) @@ -388,20 +401,24 @@ calculate_sha256(const std::string& path) return Ok(g_checksum_get_string(checksum)); } - +/** + * Get a fake-message-id for a message without one. + * + * @param path message path + * + * @return a fake message-id + */ static std::string -fake_message_id(const std::string path) +fake_message_id(const std::string& path) { constexpr auto mu_suffix{"@mu.id"}; - if (path.empty()) - return format("12345@%s", mu_suffix); - else if (const auto sha256_res{calculate_sha256(path)}; !sha256_res) { - g_warning("failed to get sha-256: %s", sha256_res.error().what()); - // fallback... not a very good message-id, but should - // not happen in practice. + // not a very good message-id, only for testing. + if (path.empty() || ::access(path.c_str(), R_OK) != 0) return format("%08x%s", g_str_hash(path.c_str()), mu_suffix); - } else + if (const auto sha256_res{calculate_sha256(path)}; !sha256_res) + return format("%08x%s", g_str_hash(path.c_str()), mu_suffix); + else return format("%s%s", sha256_res.value().c_str(), mu_suffix); } diff --git a/lib/message/mu-message.hh b/lib/message/mu-message.hh index f263faed..e61ee895 100644 --- a/lib/message/mu-message.hh +++ b/lib/message/mu-message.hh @@ -52,15 +52,13 @@ public: * @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 - * pass NULL for this parameter, in which case some maildir-specific + * pass Nothing for this parameter, in which case some maildir-specific * information is not available. * - * - * * @return a message or an error */ static Result make_from_path(const std::string& path, - Option mdir={}) try { + const std::string& mdir={}) try { return Ok(Message{path, mdir}); } catch (Error& err) { return Err(err); @@ -88,15 +86,21 @@ public: * Construct a message from a string. This is mostly useful for testing. * * @param text message text + * @param path path to message - optional; path does not have to exist. + * this is useful for testing. + * @param mdir the maildir for this message; optional, useful for testing. * * @return a message or an error */ - static Result make_from_string(const std::string& text) try { - return Ok(Message{text}); + static Result make_from_text(const std::string& text, + const std::string& path={}, + const std::string& mdir={}) try { + return Ok(Message{text, path, mdir}); } catch (Error& err) { return Err(err); } catch (...) { - return Err(Mu::Error(Error::Code::Message, "failed to create message")); + return Err(Mu::Error(Error::Code::Message, + "failed to create message from text")); } /** @@ -166,7 +170,11 @@ public: * Get the Message-Id of this message * * @return the Message-Id of this message (without the enclosing <>), or - * a fake message-id for messages that don't have them + * a fake message-id for messages that don't have them. + * + * For file-backed message, this fake message-id is based on a hash of the + * message contents. For non-file-backed (test) messages, some other value + * is concocted. */ std::string message_id() const { return document().string_value(Field::Id::MessageId);} @@ -188,7 +196,7 @@ public: ::time_t date() const { return static_cast(document().integer_value(Field::Id::Date)); } /** - * get the flags for this message + * get the flags for this message. * * @return the file/content flags */ @@ -322,8 +330,9 @@ public: struct Private; private: - Message(const std::string& path, Option mdir); - Message(const std::string& str); + Message(const std::string& path, const std::string& mdir); + Message(const std::string& str, const std::string& path, + const std::string& mdir); Message(Document& doc);