message: fall-back to received for dateless
For the (rare) messages without a Date: header, attempt to guess a date from the top (most-recent) Received: header. Not perfect, of course, but better than nothing. Requires re-indexing to apply to already indexed messages. Should help for #1083.
This commit is contained in:
@ -741,7 +741,10 @@ fill_document(Message::Private& priv)
|
||||
doc.add(field.id, priv.ctime);
|
||||
break;
|
||||
case Field::Id::Date:
|
||||
if (const auto& date{mime_msg.date()}; date) {
|
||||
/* for the rare message without a Date: header, fall back
|
||||
* to the most recent Received: header */
|
||||
if (const auto date{mime_msg.date().or_else([&]{
|
||||
return mime_msg.received(); })}; date) {
|
||||
doc.add(field.id, date->first);
|
||||
doc.add(Field::Id::UtcOffset, date->second);
|
||||
}
|
||||
|
||||
@ -22,7 +22,9 @@
|
||||
#include "gmime/gmime-message.h"
|
||||
#include "utils/mu-utils.hh"
|
||||
#include "utils/mu-utils-file.hh"
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <ranges>
|
||||
#include <regex>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
@ -334,19 +336,47 @@ MimeMessage::make_from_text(const std::string& text)
|
||||
return make_from_stream(std::move(stream));
|
||||
}
|
||||
|
||||
static MimeMessage::Date date_from_date_time(GDateTime* dt)
|
||||
{
|
||||
constexpr auto usecs_per_sec = 1'000'000;
|
||||
return MimeMessage::Date {g_date_time_to_unix(dt),
|
||||
g_date_time_get_utc_offset(dt) / usecs_per_sec };
|
||||
}
|
||||
|
||||
|
||||
Option<MimeMessage::Date>
|
||||
MimeMessage::date() const noexcept
|
||||
{
|
||||
if (/*const*/GDateTime *dt{g_mime_message_get_date(self())}; !dt)
|
||||
return Nothing;
|
||||
else {
|
||||
constexpr auto usecs_per_sec = 1'000'000;
|
||||
return Date{ g_date_time_to_unix(dt),
|
||||
g_date_time_get_utc_offset(dt) / usecs_per_sec };
|
||||
|
||||
return date_from_date_time(dt);
|
||||
}
|
||||
}
|
||||
|
||||
Option<MimeMessage::Date>
|
||||
MimeMessage::received() const noexcept
|
||||
{
|
||||
const auto recv{header("Received")};
|
||||
if (!recv)
|
||||
return Nothing;
|
||||
|
||||
/* per RFC 5322, "Received:" is *received-token ";" date-time, so the
|
||||
* date-time follows the _last_ semicolon. */
|
||||
const auto pos{recv->rfind(';')};
|
||||
if (pos == std::string::npos)
|
||||
return Nothing;
|
||||
|
||||
GDateTime *dt{g_mime_utils_header_decode_date(recv->c_str() + pos + 1)};
|
||||
if (!dt)
|
||||
return Nothing;
|
||||
|
||||
const auto date{date_from_date_time(dt)};
|
||||
g_date_time_unref(dt);
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
constexpr Option<GMimeAddressType>
|
||||
address_type(Contact::Type ctype)
|
||||
{
|
||||
@ -372,20 +402,20 @@ address_type(Contact::Type ctype)
|
||||
static Mu::Contacts
|
||||
all_contacts(const MimeMessage& msg)
|
||||
{
|
||||
Contacts contacts;
|
||||
|
||||
for (auto&& cctype: {
|
||||
constexpr auto ctypes = std::to_array({
|
||||
Contact::Type::Sender,
|
||||
Contact::Type::From,
|
||||
Contact::Type::ReplyTo,
|
||||
Contact::Type::To,
|
||||
Contact::Type::Cc,
|
||||
Contact::Type::Bcc
|
||||
}) {
|
||||
auto addrs{msg.contacts(cctype)};
|
||||
std::move(addrs.begin(), addrs.end(),
|
||||
});
|
||||
|
||||
Contacts contacts;
|
||||
std::ranges::move(ctypes | std::views::transform([&](auto ctype) {
|
||||
return msg.contacts(ctype); })
|
||||
| std::views::join,
|
||||
std::back_inserter(contacts));
|
||||
}
|
||||
|
||||
return contacts;
|
||||
}
|
||||
|
||||
@ -1051,16 +1051,23 @@ public:
|
||||
/**< Date type */
|
||||
|
||||
/**
|
||||
* Gets a date, utf-offset pair if it exists, or nullopt otherwise.
|
||||
* Gets a { date, utf-offset } pair if it exists, or nullopt otherwise.
|
||||
*
|
||||
* The date is the number of seconds since epoch (i.e., unix time),
|
||||
* while utf-offset is the number of seconds offset from UTC
|
||||
* (i.e., negative numbers west of GMT, positive numbers for east)
|
||||
*
|
||||
* @return a time_t value (expressed as a 64-bit number) or nullopt
|
||||
* @return a Date pair or nullopt
|
||||
*/
|
||||
Option<Date> date() const noexcept;
|
||||
|
||||
/**
|
||||
* Gets a { date, utc-offset } pair from the first (more recent)
|
||||
* Received: header, or nullopt if there is none.
|
||||
*
|
||||
* @return a Date pair or nullopt
|
||||
*/
|
||||
Option<Date> received() const noexcept;
|
||||
|
||||
/**
|
||||
* Get the references for this message (including in-reply-to), in the
|
||||
|
||||
Reference in New Issue
Block a user