diff --git a/lib/message/mu-fields.hh b/lib/message/mu-fields.hh index 6a768b80..a90244a5 100644 --- a/lib/message/mu-fields.hh +++ b/lib/message/mu-fields.hh @@ -39,39 +39,28 @@ struct Field { * */ enum struct Id { - /* - * first all the string-based ones - */ - Bcc = 0, /**< Blind Carbon-Copy */ - BodyHtml, /**< HTML Body */ - BodyText, /**< Text body */ - Cc, /**< Carbon-Copy */ - EmbeddedText, /**< Embedded text in message */ - File, /**< Filename */ - From, /**< Message sender */ - Maildir, /**< Maildir path */ - Mime, /**< MIME-Type */ - MessageId, /**< Message Id */ - Path, /**< File-system Path */ - Subject, /**< Message subject */ - To, /**< To: recipient */ - /* - * string list items... - */ - References, /**< All references (incl. Reply-To:) */ - Tags, /**< Message Tags */ - /* - * then the numerical ones - */ - Date, /**< Message date */ - Flags, /**< Message flags */ - Priority, /**< Message priority */ - Size, /**< Message size (in bytes) */ - - /* add new ones here... */ - MailingList, /**< Mailing list */ - ThreadId, /**< Thread Id */ - + Bcc = 0, /**< Blind Carbon-Copy */ + BodyHtml, /**< HTML Body */ + BodyText, /**< Text body */ + Cc, /**< Carbon-Copy */ + Date, /**< Message date */ + EmbeddedText, /**< Embedded text in message */ + File, /**< Filename */ + Flags, /**< Message flags */ + From, /**< Message sender */ + Maildir, /**< Maildir path */ + MailingList, /**< Mailing list */ + MessageId, /**< Message Id */ + Mime, /**< MIME-Type */ + Modified, /**< Last modification time */ + Path, /**< File-system Path */ + Priority, /**< Message priority */ + References, /**< All references (incl. Reply-To:) */ + Size, /**< Message size (in bytes) */ + Subject, /**< Message subject */ + Tags, /**< Message Tags */ + ThreadId, /**< Thread Id */ + To, /**< To: recipient */ /* * */ @@ -146,10 +135,9 @@ struct Field { Value = 1 << 11, /**< Field value is stored (so the literal value can be retrieved) */ - DoNotCache = 1 << 20, - /**< don't cache this field in * the MuMsg cache */ - Range = 1 << 21 + Range = 1 << 21, /**< whether this is a range field (e.g., date, size)*/ + Internal = 1 << 26 }; constexpr bool any_of(Flag some_flag) const{ @@ -164,6 +152,7 @@ struct Field { is_normal_term(); } constexpr bool is_value() const { return any_of(Flag::Value); } + constexpr bool is_internal() const { return any_of(Flag::Internal); } constexpr bool is_contact() const { return any_of(Flag::Contact); } constexpr bool is_range() const { return any_of(Flag::Range); } @@ -215,7 +204,6 @@ MU_ENABLE_BITOPS(Field::Flag); static constexpr std::array Fields = { { - // Bcc { Field::Id::Bcc, Field::Type::ContactList, @@ -226,7 +214,6 @@ static constexpr std::array Field::Flag::Contact | Field::Flag::Value }, - // HTML Body { Field::Id::BodyHtml, Field::Type::String, @@ -234,19 +221,17 @@ static constexpr std::array "Message html body", {}, {}, - {} + Field::Flag::Internal }, - // Body { Field::Id::BodyText, Field::Type::String, "body", "Message plain-text body", - "body:capybara", // example + "body:capybara", 'b', Field::Flag::IndexableTerm, }, - // Cc { Field::Id::Cc, Field::Type::ContactList, @@ -257,124 +242,6 @@ static constexpr std::array Field::Flag::Contact | Field::Flag::Value }, - // Embed - { - Field::Id::EmbeddedText, - Field::Type::String, - "embed", - "Embedded text", - "embed:war OR embed:peace", - 'e', - Field::Flag::IndexableTerm - }, - // File - { - Field::Id::File, - Field::Type::String, - "file", - "Attachment file name", - "file:/image\\.*.jpg/", - 'j', - Field::Flag::NormalTerm - }, - // From - { - Field::Id::From, - Field::Type::ContactList, - "from", - "Message sender", - "from:jimbo", - 'f', - Field::Flag::Contact | - Field::Flag::Value - }, - // Maildir - { - Field::Id::Maildir, - Field::Type::String, - "maildir", - "Maildir path for message", - "maildir:/private/archive", - 'm', - Field::Flag::BooleanTerm | - Field::Flag::Value - }, - // MIME - { - Field::Id::Mime, - Field::Type::String, - "mime", - "Attachment MIME-type", - "mime:image/jpeg", - 'y', - Field::Flag::NormalTerm - }, - // Message-ID - { - Field::Id::MessageId, - Field::Type::String, - "msgid", - "Attachment MIME-type", - "msgid:abc@123", - 'i', - Field::Flag::BooleanTerm | - Field::Flag::Value - }, - // Path - { - Field::Id::Path, - Field::Type::String, - "path", - "File system path to message", - {}, - 'l', - Field::Flag::BooleanTerm | - Field::Flag::Value - }, - // Subject - { - Field::Id::Subject, - Field::Type::String, - "subject", - "Message subject", - "subject:wombat", - 's', - Field::Flag::Value | - Field::Flag::IndexableTerm - }, - // To - { - Field::Id::To, - Field::Type::ContactList, - "to", - "Message recipient", - "to:flimflam@example.com", - 't', - Field::Flag::Contact | - Field::Flag::Value - }, - // References - { - Field::Id::References, - Field::Type::StringList, - "refs", - "Message references to other messages", - {}, - 'r', - Field::Flag::Value - }, - // Tags - { - Field::Id::Tags, - Field::Type::StringList, - "tag", - "Message tags", - "tag:projectx", - 'x', - Field::Flag::NormalTerm | - Field::Flag::Value - }, - // Date { Field::Id::Date, Field::Type::TimeT, @@ -385,7 +252,24 @@ static constexpr std::array Field::Flag::Value | Field::Flag::Range }, - // Flags + { + Field::Id::EmbeddedText, + Field::Type::String, + "embed", + "Embedded text", + "embed:war OR embed:peace", + 'e', + Field::Flag::IndexableTerm + }, + { + Field::Id::File, + Field::Type::String, + "file", + "Attachment file name", + "file:/image\\.*.jpg/", + 'j', + Field::Flag::NormalTerm + }, { Field::Id::Flags, Field::Type::Integer, @@ -396,7 +280,75 @@ static constexpr std::array Field::Flag::NormalTerm | Field::Flag::Value }, - // Priority + { + Field::Id::From, + Field::Type::ContactList, + "from", + "Message sender", + "from:jimbo", + 'f', + Field::Flag::Contact | + Field::Flag::Value + }, + { + Field::Id::Maildir, + Field::Type::String, + "maildir", + "Maildir path for message", + "maildir:/private/archive", + 'm', + Field::Flag::BooleanTerm | + Field::Flag::Value + }, + { + Field::Id::MailingList, + Field::Type::String, + "list", + "Mailing list (List-Id:)", + "list:mu-discuss.example.com", + 'v', + Field::Flag::BooleanTerm | + Field::Flag::Value + }, + { + Field::Id::MessageId, + Field::Type::String, + "msgid", + "Attachment MIME-type", + "msgid:abc@123", + 'i', + Field::Flag::BooleanTerm | + Field::Flag::Value + }, + { + Field::Id::Mime, + Field::Type::String, + "mime", + "Attachment MIME-type", + "mime:image/jpeg", + 'y', + Field::Flag::NormalTerm + }, + { + Field::Id::Modified, + Field::Type::TimeT, + "modified", + "Last modification time", + "modified:30m..now", + 'k', + Field::Flag::Value | + Field::Flag::Range + }, + { + Field::Id::Path, + Field::Type::String, + "path", + "File system path to message", + "path:/a/b/Maildir/cur/msg:2,S", + 'l', + Field::Flag::BooleanTerm | + Field::Flag::Value + }, { Field::Id::Priority, Field::Type::Integer, @@ -407,7 +359,15 @@ static constexpr std::array Field::Flag::BooleanTerm | Field::Flag::Value }, - // Size + { + Field::Id::References, + Field::Type::StringList, + "refs", + "References to related messages", + {}, + 'r', + Field::Flag::Value + }, { Field::Id::Size, Field::Type::ByteSize, @@ -418,18 +378,26 @@ static constexpr std::array Field::Flag::Value | Field::Flag::Range }, - // Mailing List { - Field::Id::MailingList, + Field::Id::Subject, Field::Type::String, - "list", - "Mailing list (List-Id:)", - "list:mu-discuss.googlegroups.com", - 'v', - Field::Flag::BooleanTerm | + "subject", + "Message subject", + "subject:wombat", + 's', + Field::Flag::Value | + Field::Flag::IndexableTerm + }, + { + Field::Id::Tags, + Field::Type::StringList, + "tag", + "Message tags", + "tag:projectx", + 'x', + Field::Flag::NormalTerm | Field::Flag::Value }, - // ThreadId { Field::Id::ThreadId, Field::Type::String, @@ -440,6 +408,16 @@ static constexpr std::array Field::Flag::BooleanTerm | Field::Flag::Value }, + { + Field::Id::To, + Field::Type::ContactList, + "to", + "Message recipient", + "to:flimflam@example.com", + 't', + Field::Flag::Contact | + Field::Flag::Value + }, }}; /* diff --git a/lib/message/mu-message.cc b/lib/message/mu-message.cc index fe20284f..bc2a0853 100644 --- a/lib/message/mu-message.cc +++ b/lib/message/mu-message.cc @@ -596,6 +596,9 @@ fill_document(Message::Private& priv) for (auto&& part: priv.parts) doc.add(field.id, part.mime_type()); break; + case Field::Id::Modified: + doc.add(field.id, priv.mtime); + break; case Field::Id::Path: /* already */ break; case Field::Id::Priority: @@ -708,8 +711,10 @@ Message::update_after_move(const std::string& new_path, return Err(statbuf.error()); priv_->doc.add(Field::Id::Path, new_path); + priv_->doc.add(Field::Id::Modified, statbuf->st_mtime); priv_->doc.add(new_flags); + if (const auto res = set_maildir(new_maildir); !res) return res; diff --git a/lib/message/mu-message.hh b/lib/message/mu-message.hh index 6ab255e0..ac0b8190 100644 --- a/lib/message/mu-message.hh +++ b/lib/message/mu-message.hh @@ -84,18 +84,29 @@ public: /** * Construct a message based on a Message::Document * - * @param doc + * @param doc a Mu Document * * @return a message or an error */ - static Result make_from_document(Xapian::Document&& doc) try { - return Ok(Message{Mu::Document{std::move(doc)}}); + static Result make_from_document(Mu::Document&& doc) try { + return Ok(Message{std::move(doc)}); } catch (Error& err) { return Err(err); } catch (...) { return Err(Mu::Error(Error::Code::Message, "failed to create message")); } + /** + * Construct a message based on a Xapian::Document + * + * @param doc a xapian document + * + * @return a message or an error + */ + static Result make_from_document(Xapian::Document&& doc) noexcept { + return make_from_document(Mu::Document{std::move(doc)}); + } + /** * Construct a message from a string. This is mostly useful for testing. @@ -216,12 +227,24 @@ public: std::string mailing_list() const { return document().string_value(Field::Id::MailingList);} /** - * get the message date/time (the Date: field) as time_t, using UTC + * get the message date/time (the Date: field) as time_t * * @return message date/time or 0 in case of error or if there * is no such header. */ - ::time_t date() const { return static_cast(document().integer_value(Field::Id::Date)); } + ::time_t date() const { + return static_cast<::time_t>(document().integer_value(Field::Id::Date)); + } + + /** + * get the last modification or creation time for this message + * + * @return message date/time or 0 if unknown + */ + ::time_t modified() const { + return static_cast<::time_t>(document().integer_value(Field::Id::Modified)); + } + /** * get the flags for this message. diff --git a/lib/mu-parser.cc b/lib/mu-parser.cc index a04fe004..d95bcdbe 100644 --- a/lib/mu-parser.cc +++ b/lib/mu-parser.cc @@ -177,7 +177,7 @@ process_range(const std::string& field_str, std::string u2 = upper; constexpr auto upper_limit = std::numeric_limits::max(); - if (field_opt->id == Field::Id::Date) { + if (field_opt->id == Field::Id::Date || field_opt->id == Field::Id::Modified) { l2 = to_lexnum(parse_date_time(lower, true).value_or(0)); u2 = to_lexnum(parse_date_time(upper, false).value_or(upper_limit)); } else if (field_opt->id == Field::Id::Size) { @@ -420,7 +420,8 @@ Parser::Private::factor_2(Mu::Tokens& tokens, Node::Type& op, WarningVec& warnin op = Node::Type::OpAnd; // implicit AND break; - default: return empty(); + default: + return empty(); } #pragma GCC diagnostic pop