mirror of https://github.com/djcb/mu.git
lib: support ignoring addresses for contacts-cache
Skip annoying 'noreply' & friends.
This commit is contained in:
parent
960a436e77
commit
5dc41ed811
|
@ -43,15 +43,16 @@ struct Property {
|
|||
enum struct Id {
|
||||
BatchSize, /**< Xapian batch-size */
|
||||
Contacts, /**< Cache of contact information */
|
||||
Created, /** Time of creation */
|
||||
Created, /**< Time of creation */
|
||||
IgnoredAddresses,/**< Email addresses ignored for the contacts-cache */
|
||||
LastChange, /**< Time of last change */
|
||||
LastIndex, /**< Time of last index */
|
||||
MaxMessageSize, /**< Maximum message size (in bytes) */
|
||||
PersonalAddresses, /**< List of personal e-mail addresses */
|
||||
PersonalAddresses, /**< List of personal e-mail addresses */
|
||||
RootMaildir, /**< Root maildir path */
|
||||
SchemaVersion, /**< Xapian DB schema version */
|
||||
/* <private> */
|
||||
_count_ /* Number of Ids */
|
||||
_count_ /* Number of Ids */
|
||||
};
|
||||
|
||||
static constexpr size_t id_size = static_cast<size_t>(Id::_count_);
|
||||
|
@ -118,6 +119,15 @@ public:
|
|||
{},
|
||||
"Database creation time"
|
||||
},
|
||||
{
|
||||
Id::IgnoredAddresses,
|
||||
Type::StringList,
|
||||
Flags::Configurable,
|
||||
"ignored-addresses",
|
||||
{},
|
||||
"E-mail addresses ignored for the contacts-cache, "
|
||||
"literal or /regexp/"
|
||||
},
|
||||
{
|
||||
Id::LastChange,
|
||||
Type::Timestamp,
|
||||
|
@ -147,8 +157,8 @@ public:
|
|||
Type::StringList,
|
||||
Flags::Configurable,
|
||||
"personal-addresses",
|
||||
"",
|
||||
"List of personal e-mail addresses, literal or /regexp/"
|
||||
{},
|
||||
"Personal e-mail addresses, literal or /regexp/"
|
||||
},
|
||||
{
|
||||
Id::RootMaildir,
|
||||
|
|
|
@ -49,8 +49,10 @@ struct ContactsCache::Private {
|
|||
Private(Config& config_db)
|
||||
:config_db_{config_db},
|
||||
contacts_{deserialize(config_db_.get<Config::Id::Contacts>())},
|
||||
personal_plain_{make_personal_plain(config_db_.get<Config::Id::PersonalAddresses>())},
|
||||
personal_rx_{make_personal_rx(config_db_.get<Config::Id::PersonalAddresses>())},
|
||||
personal_plain_{make_plain(config_db_.get<Config::Id::PersonalAddresses>())},
|
||||
personal_rx_{make_rx(config_db_.get<Config::Id::PersonalAddresses>())},
|
||||
ignored_plain_{make_plain(config_db_.get<Config::Id::IgnoredAddresses>())},
|
||||
ignored_rx_{make_rx(config_db_.get<Config::Id::IgnoredAddresses>())},
|
||||
dirty_{0}
|
||||
{}
|
||||
|
||||
|
@ -61,12 +63,16 @@ struct ContactsCache::Private {
|
|||
ContactUMap deserialize(const std::string&) const;
|
||||
void serialize() const;
|
||||
|
||||
Config& config_db_;
|
||||
Config& config_db_;
|
||||
ContactUMap contacts_;
|
||||
mutable std::mutex mtx_;
|
||||
|
||||
const StringVec personal_plain_;
|
||||
const std::vector<Regex> personal_rx_;
|
||||
|
||||
const StringVec ignored_plain_;
|
||||
const std::vector<Regex> ignored_rx_;
|
||||
|
||||
mutable size_t dirty_;
|
||||
|
||||
private:
|
||||
|
@ -77,7 +83,7 @@ private:
|
|||
*
|
||||
* @return
|
||||
*/
|
||||
StringVec make_personal_plain(const StringVec& personal) const {
|
||||
StringVec make_plain(const StringVec& personal) const {
|
||||
StringVec svec;
|
||||
std::copy_if(personal.begin(), personal.end(),
|
||||
std::back_inserter(svec), [&](auto&& p) {
|
||||
|
@ -94,7 +100,7 @@ private:
|
|||
*
|
||||
* @return
|
||||
*/
|
||||
std::vector<Regex> make_personal_rx(const StringVec& personal) const {
|
||||
std::vector<Regex> make_rx(const StringVec& personal) const {
|
||||
std::vector<Regex> rxvec;
|
||||
for(auto&& p: personal) {
|
||||
if (p.size() < 2 || p[0] != '/' || p[p.length()- 1] != '/')
|
||||
|
@ -209,6 +215,11 @@ ContactsCache::add(Contact&& contact)
|
|||
return;
|
||||
}
|
||||
|
||||
if (is_ignored(contact.email)) {
|
||||
/* ignored this address, e.g. 'noreply@example.com */
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> l_{priv_->mtx_};
|
||||
|
||||
++priv_->dirty_;
|
||||
|
@ -257,7 +268,6 @@ ContactsCache::add(Contacts&& contacts, bool& personal)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
const Contact*
|
||||
ContactsCache::_find(const std::string& email) const
|
||||
{
|
||||
|
@ -343,14 +353,14 @@ ContactsCache::for_each(const EachContactFunc& each_contact) const
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ContactsCache::is_personal(const std::string& addr) const
|
||||
static bool
|
||||
address_matches(const std::string& addr, const StringVec& plain, const std::vector<Regex>& regexes)
|
||||
{
|
||||
for (auto&& p : priv_->personal_plain_)
|
||||
for (auto&& p : plain)
|
||||
if (g_ascii_strcasecmp(addr.c_str(), p.c_str()) == 0)
|
||||
return true;
|
||||
|
||||
for (auto&& rx : priv_->personal_rx_) {
|
||||
for (auto&& rx : regexes) {
|
||||
if (rx.matches(addr))
|
||||
return true;
|
||||
}
|
||||
|
@ -358,6 +368,22 @@ ContactsCache::is_personal(const std::string& addr) const
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
ContactsCache::is_personal(const std::string& addr) const
|
||||
{
|
||||
return address_matches(addr, priv_->personal_plain_, priv_->personal_rx_);
|
||||
}
|
||||
|
||||
bool
|
||||
ContactsCache::is_ignored(const std::string& addr) const
|
||||
{
|
||||
return address_matches(addr, priv_->ignored_plain_, priv_->ignored_rx_);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef BUILD_TESTS
|
||||
/*
|
||||
* Tests.
|
||||
|
@ -432,6 +458,36 @@ test_mu_contacts_cache_personal()
|
|||
g_assert_false(contacts.is_personal("bar-zzz@fnorb.xr"));
|
||||
}
|
||||
|
||||
static void
|
||||
test_mu_contacts_cache_ignored()
|
||||
{
|
||||
MemDb xdb{};
|
||||
Config cdb{xdb};
|
||||
cdb.set<Config::Id::IgnoredAddresses>
|
||||
(StringVec{{"foo@example.com", "bar@cuux.org", "/bar-.*@fnorb.f./"}});
|
||||
ContactsCache contacts{cdb};
|
||||
|
||||
g_assert_true(contacts.is_ignored("foo@example.com"));
|
||||
g_assert_true(contacts.is_ignored("Bar@CuuX.orG"));
|
||||
g_assert_true(contacts.is_ignored("bar-123abc@fnorb.fi"));
|
||||
g_assert_true(contacts.is_ignored("bar-zzz@fnorb.fr"));
|
||||
|
||||
g_assert_false(contacts.is_ignored("foo@bar.com"));
|
||||
g_assert_false(contacts.is_ignored("BÂr@CuuX.orG"));
|
||||
g_assert_false(contacts.is_ignored("bar@fnorb.fi"));
|
||||
g_assert_false(contacts.is_ignored("bar-zzz@fnorb.xr"));
|
||||
|
||||
g_assert_cmpuint(contacts.size(),==,0);
|
||||
contacts.add(Mu::Contact{"a@example.com", "a", 123, true, 1000, 0});
|
||||
g_assert_cmpuint(contacts.size(),==,1);
|
||||
contacts.add(Mu::Contact{"foo@example.com", "b", 123, true, 1000, 0}); // ignored
|
||||
contacts.add(Mu::Contact{"bar-123abc@fnorb.fi", "c", 123, true, 1000, 0}); // ignored
|
||||
g_assert_cmpuint(contacts.size(),==,1);
|
||||
contacts.add(Mu::Contact{"b@example.com", "d", 123, true, 1000, 0});
|
||||
g_assert_cmpuint(contacts.size(),==,2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
test_mu_contacts_cache_foreach()
|
||||
|
@ -544,6 +600,7 @@ main(int argc, char* argv[])
|
|||
|
||||
g_test_add_func("/lib/contacts-cache/base", test_mu_contacts_cache_base);
|
||||
g_test_add_func("/lib/contacts-cache/personal", test_mu_contacts_cache_personal);
|
||||
g_test_add_func("/lib/contacts-cache/ignored", test_mu_contacts_cache_ignored);
|
||||
g_test_add_func("/lib/contacts-cache/for-each", test_mu_contacts_cache_foreach);
|
||||
g_test_add_func("/lib/contacts-cache/sort", test_mu_contacts_cache_sort);
|
||||
|
||||
|
|
|
@ -53,10 +53,10 @@ public:
|
|||
/**
|
||||
* Add a contact
|
||||
*
|
||||
* Invalid email address are not cached (but we log a warning)
|
||||
* Invalid email address are not cached (but we log a warning); neither
|
||||
* are "ignored" addresses (see --ignored-address in mu-init(1))
|
||||
*
|
||||
* @param contact a Contact object
|
||||
*
|
||||
*/
|
||||
void add(Contact&& contact);
|
||||
|
||||
|
@ -67,7 +67,8 @@ public:
|
|||
* if any of the contacts matches one of the personal addresses,
|
||||
* any of the senders/recipients are considered "personal"
|
||||
*
|
||||
* Invalid email address are not cached (but we log a warning)
|
||||
* Invalid email address are not cached (but we log a warning); neither
|
||||
* are "ignored" addresses (see --ignored-address in mu-init(1))
|
||||
*
|
||||
* @param contacts a Contact object sequence
|
||||
* @param is_personal receives true if any of the contacts was personal;
|
||||
|
@ -115,6 +116,16 @@ public:
|
|||
*/
|
||||
bool is_personal(const std::string& addr) const;
|
||||
|
||||
|
||||
/**
|
||||
* Does this look like an email-address that should be ignored?
|
||||
*
|
||||
* @param addr some e-mail address
|
||||
*
|
||||
* @return true or false
|
||||
*/
|
||||
bool is_ignored(const std::string& addr) const;
|
||||
|
||||
/**
|
||||
* Find a contact based on the email address. This is not safe, since
|
||||
* the returned ptr can be invalidated at any time; only for unit-tests.
|
||||
|
|
Loading…
Reference in New Issue