diff --git a/lib/message/mu-contact.cc b/lib/message/mu-contact.cc index fd0da046..4c164735 100644 --- a/lib/message/mu-contact.cc +++ b/lib/message/mu-contact.cc @@ -35,57 +35,6 @@ Contact::display_name() const return name + " <" + email + '>'; } -Mu::Contacts -Mu::make_contacts(InternetAddressList* addr_lst, - Field::Id field_id, int64_t message_date) -{ - Contacts contacts; - size_t num{}; - - g_return_val_if_fail(addr_lst, contacts); - - auto lst_len{internet_address_list_length(addr_lst)}; - contacts.reserve(lst_len); - for (auto i = 0; i != lst_len; ++i) { - - auto&& addr{internet_address_list_get_address(addr_lst, i)}; - const auto name{internet_address_get_name(addr)}; - - if (G_UNLIKELY(!INTERNET_ADDRESS_IS_MAILBOX(addr))) - continue; - - const auto email{internet_address_mailbox_get_addr ( - INTERNET_ADDRESS_MAILBOX(addr))}; - if (G_UNLIKELY(!email)) - continue; - - contacts.push_back(Contact{email, name ? name : "", - field_id, message_date}); - ++num; - } - - return contacts; -} - - -Mu::Contacts -Mu::make_contacts(const std::string& addrs, - Field::Id field_id, - int64_t message_date) -{ - auto addr_list = internet_address_list_parse(NULL, addrs.c_str()); - if (!addr_list) { - g_warning("no addresses found in '%s'", addrs.c_str()); - return {}; - } - - auto contacts{make_contacts(addr_list, field_id, message_date)}; - g_object_unref(addr_list); - - return contacts; -} - - std::string Mu::to_string(const Mu::Contacts& contacts) { @@ -125,13 +74,13 @@ test_ctor_foo() Contact c{ "foo@example.com", "Foo Bar", - Field::Id::Bcc, + Contact::Type::Bcc, 1645214647 }; assert_equal(c.email, "foo@example.com"); assert_equal(c.name, "Foo Bar"); - g_assert_true(c.field_id == Field::Id::Bcc); + g_assert_true(*c.field_id() == Field::Id::Bcc); g_assert_cmpuint(c.message_date,==,1645214647); assert_equal(c.display_name(), "Foo Bar "); @@ -183,64 +132,6 @@ test_ctor_cleanup() } - -static void -test_make_contacts() -{ - const auto str = "Abc , " - "Def , " - "Ghi "; - InternetAddressList *lst{ - internet_address_list_parse(NULL, str)}; - - g_assert_true(lst); - const auto addrs{make_contacts(lst, Field::Id::Cc, 54321 )}; - g_object_unref(lst); - - g_assert_cmpuint(addrs.size(),==,3); - - const auto addrs2{make_contacts(str, Field::Id::To, 12345 )}; - g_assert_cmpuint(addrs2.size(),==,3); - - assert_equal(addrs2[0].name, "Abc"); - assert_equal(addrs2[0].email, "boo@example.com"); - assert_equal(addrs2[1].name, "Def"); - assert_equal(addrs2[1].email, "baa@example.com"); - assert_equal(addrs2[2].name, "Ghi"); - assert_equal(addrs2[2].email, "zzz@example.com"); -} - -static void -test_make_contacts_2() -{ - const auto str = "Äbc , " - "De\nf , " - "\tGhi "; - - const auto addrs2{make_contacts(str, Field::Id::Bcc, 12345 )}; - g_assert_cmpuint(addrs2.size(),==,3); - - assert_equal(addrs2[0].name, "Äbc"); - assert_equal(addrs2[0].email, "boo@example.com"); - assert_equal(addrs2[1].name, "De f"); - assert_equal(addrs2[1].email, "baa@example.com"); - assert_equal(addrs2[2].name, "Ghi"); - assert_equal(addrs2[2].email, "zzz@example.com"); -} - - -static void -test_make_contacts_err() -{ - allow_warnings(); - - InternetAddressList *lst{ internet_address_list_parse(NULL, "")}; - g_assert_false(lst); - - const auto addrs{make_contacts("", Field::Id::To, 77777)}; - g_assert_true(addrs.empty()); -} - int main(int argc, char* argv[]) { @@ -250,9 +141,6 @@ main(int argc, char* argv[]) g_test_add_func("/message/contact/ctor-foo", test_ctor_foo); g_test_add_func("/message/contact/ctor-blinky", test_ctor_blinky); g_test_add_func("/message/contact/ctor-cleanup", test_ctor_cleanup); - g_test_add_func("/message/contact/make-contacts", test_make_contacts); - g_test_add_func("/message/contact/make-contacts-2", test_make_contacts_2); - g_test_add_func("/message/contact/make-contacts-err", test_make_contacts_err); return g_test_run(); } diff --git a/lib/message/mu-contact.hh b/lib/message/mu-contact.hh index 5f40a8d4..4644f091 100644 --- a/lib/message/mu-contact.hh +++ b/lib/message/mu-contact.hh @@ -46,19 +46,22 @@ namespace Mu { size_t lowercase_hash(const std::string& s); struct Contact { + enum struct Type { + None, Sender, From, ReplyTo, To, Cc, Bcc + }; + /** * Construct a new Contact * * @param email_ email address * @param name_ name or empty - * @param field_id_ contact field id, or {} + * @param type_ contact field type * @param message_date_ data for the message for this contact */ Contact(const std::string& email_, const std::string& name_ = "", - Option field_id_ = {}, - time_t message_date_ = 0) - : email{email_}, name{name_}, field_id{field_id_}, - message_date{message_date_}, personal{}, frequency{1}, tstamp{} + Type type_ = Type::None, ::time_t message_date_ = 0) + : email{email_}, name{name_}, type{type_}, + message_date{message_date_}, personal{}, frequency{1}, tstamp{} { cleanup_name(); } /** @@ -74,7 +77,7 @@ struct Contact { Contact(const std::string& email_, const std::string& name_, time_t message_date_, bool personal_, size_t freq_, int64_t tstamp_) - : email{email_}, name{name_}, field_id{}, + : email{email_}, name{name_}, type{Type::None}, message_date{message_date_}, personal{personal_}, frequency{freq_}, tstamp{tstamp_} { cleanup_name();} @@ -115,18 +118,39 @@ struct Contact { return cached_hash; } + /** + * Get the corresponding Field::Id (if any) + * for this contact. + * + * @return the field-id or Nothing. + */ + constexpr Option field_id() const noexcept { + switch(type) { + case Type::Bcc: + return Field::Id::Bcc; + case Type::Cc: + return Field::Id::Cc; + case Type::From: + return Field::Id::From; + case Type::To: + return Field::Id::To; + default: + return Nothing; + } + } + + /* * data members */ - std::string email; /**< Email address for this contact.Not empty */ - std::string name; /**< Name for this contact; can be empty. */ - Option field_id; /**< Field Id of contact or nullopt */ - int64_t message_date; /**< date of the message from which the - * contact originates (or 0) */ + std::string email; /**< Email address for this contact.Not empty */ + std::string name; /**< Name for this contact; can be empty. */ + Type type; /**< Type of contact */ + int64_t message_date; /**< date of the contact's message */ bool personal; /**< A personal message? */ size_t frequency; /**< Frequency of this contact */ - int64_t tstamp; /**< Timestamp for this contact (internal use) */ + int64_t tstamp; /**< Timestamp for this contact (internal use) */ private: void cleanup_name() { // replace control characters by spaces. @@ -136,35 +160,27 @@ private: } }; + +constexpr Option +contact_type_from_field_id(Field::Id id) noexcept { + + switch(id) { + case Field::Id::Bcc: + return Contact::Type::Bcc; + case Field::Id::Cc: + return Contact::Type::Cc; + case Field::Id::From: + return Contact::Type::From; + case Field::Id::To: + return Contact::Type::To; + default: + return Nothing; + } +} + + using Contacts = std::vector; -/** - * Create a sequence of Contact objects from an InternetAddressList - * - * @param addr_lst an address list - * @param field_id the field_id for message field for these addresses - * @param message_date the date of the message from which the InternetAddressList - * originates. - * - * @return a sequence of Contact objects. - */ -Contacts -make_contacts(/*const*/ struct _InternetAddressList* addr_lst, - Field::Id field_id, int64_t message_date); - -/** - * Create a sequence of Contact objects from an InternetAddressList - * - * @param addrs a string with one more valid addresses (as per internet_address_list_parse()) - * @param field_id the field_id for message field for these addresses - * @param message_date the date of the message from which the addresses originate - * - * @return a sequence of Contact objects. - */ -Contacts -make_contacts(const std::string& addrs, - Field::Id field_id, int64_t message_date); - /** * Get contacts as a comma-separated list. diff --git a/lib/message/mu-document.cc b/lib/message/mu-document.cc index a2832207..4f5c2360 100644 --- a/lib/message/mu-document.cc +++ b/lib/message/mu-document.cc @@ -107,7 +107,8 @@ Document::add(Field::Id id, const Contacts& contacts) for (auto&& contact: contacts) { - if (!contact.field_id || *contact.field_id != id) + const auto cfield_id{contact.field_id()}; + if (!cfield_id || *cfield_id != id) continue; xdoc_.add_term(field.xapian_term(contact.email)); @@ -130,6 +131,13 @@ Document::contacts_value(Field::Id id) const noexcept Contacts contacts; contacts.reserve(vals.size()); + const auto ctype{contact_type_from_field_id(id)}; + if (G_UNLIKELY(!ctype)) { + g_critical("invalid field-id for contact-type: <%zu>", + static_cast(id)); + return {}; + } + for (auto&& s: vals) { const auto pos = s.find(SepaChar2); @@ -138,7 +146,7 @@ Document::contacts_value(Field::Id id) const noexcept break; } - contacts.emplace_back(s.substr(0, pos), s.substr(pos + 1), id); + contacts.emplace_back(s.substr(0, pos), s.substr(pos + 1), *ctype); } return contacts; @@ -223,14 +231,14 @@ Document::flags_value() const noexcept static const Contacts test_contacts = {{ - Contact{"john@example.com", "John", Field::Id::Bcc}, - Contact{"ringo@example.com", "Ringo", Field::Id::Bcc}, - Contact{"paul@example.com", "Paul", Field::Id::Cc}, - Contact{"george@example.com", "George", Field::Id::Cc}, - Contact{"james@example.com", "James", Field::Id::From}, - Contact{"lars@example.com", "Lars", Field::Id::To}, - Contact{"kirk@example.com", "Kirk", Field::Id::To}, - Contact{"jason@example.com", "Jason", Field::Id::To} + Contact{"john@example.com", "John", Contact::Type::Bcc}, + Contact{"ringo@example.com", "Ringo", Contact::Type::Bcc}, + Contact{"paul@example.com", "Paul", Contact::Type::Cc}, + Contact{"george@example.com", "George", Contact::Type::Cc}, + Contact{"james@example.com", "James", Contact::Type::From}, + Contact{"lars@example.com", "Lars", Contact::Type::To}, + Contact{"kirk@example.com", "Kirk", Contact::Type::To}, + Contact{"jason@example.com", "Jason", Contact::Type::To} }}; static void @@ -241,8 +249,8 @@ test_bcc() doc.add(Field::Id::Bcc, test_contacts); Contacts expected_contacts = {{ - Contact{"john@example.com", "John", Field::Id::Bcc}, - Contact{"ringo@example.com", "Ringo", Field::Id::Bcc}, + Contact{"john@example.com", "John", Contact::Type::Bcc}, + Contact{"ringo@example.com", "Ringo", Contact::Type::Bcc}, }}; const auto actual_contacts = doc.contacts_value(Field::Id::Bcc); assert_same_contacts(expected_contacts, actual_contacts); @@ -251,8 +259,8 @@ test_bcc() { Document doc; Contacts contacts = {{ - Contact{"john@example.com", "John Lennon", Field::Id::Bcc}, - Contact{"ringo@example.com", "Ringo", Field::Id::Bcc}, + Contact{"john@example.com", "John Lennon", Contact::Type::Bcc}, + Contact{"ringo@example.com", "Ringo", Contact::Type::Bcc}, }}; doc.add(Field::Id::Bcc, contacts); @@ -273,8 +281,8 @@ test_cc() doc.add(Field::Id::Cc, test_contacts); Contacts expected_contacts = {{ - Contact{"paul@example.com", "Paul", Field::Id::Cc}, - Contact{"george@example.com", "George", Field::Id::Cc} + Contact{"paul@example.com", "Paul", Contact::Type::Cc}, + Contact{"george@example.com", "George", Contact::Type::Cc} }}; const auto actual_contacts = doc.contacts_value(Field::Id::Cc); @@ -289,7 +297,7 @@ test_from() doc.add(Field::Id::From, test_contacts); Contacts expected_contacts = {{ - Contact{"james@example.com", "James", Field::Id::From}, + Contact{"james@example.com", "James", Contact::Type::From}, }}; const auto actual_contacts = doc.contacts_value(Field::Id::From); @@ -303,9 +311,9 @@ test_to() doc.add(Field::Id::To, test_contacts); Contacts expected_contacts = {{ - Contact{"lars@example.com", "Lars", Field::Id::To}, - Contact{"kirk@example.com", "Kirk", Field::Id::To}, - Contact{"jason@example.com", "Jason", Field::Id::To} + Contact{"lars@example.com", "Lars", Contact::Type::To}, + Contact{"kirk@example.com", "Kirk", Contact::Type::To}, + Contact{"jason@example.com", "Jason", Contact::Type::To} }}; const auto actual_contacts = doc.contacts_value(Field::Id::To); diff --git a/lib/message/mu-mime-object.cc b/lib/message/mu-mime-object.cc index 07a4ef3d..07894e4a 100644 --- a/lib/message/mu-mime-object.cc +++ b/lib/message/mu-mime-object.cc @@ -267,31 +267,64 @@ MimeMessage::date() const noexcept return g_date_time_to_unix(dt); } -Mu::Contacts -MimeMessage::addresses(AddressType atype) const noexcept +constexpr Option +address_type(Contact::Type ctype) { - auto addrs{g_mime_message_get_addresses( - self(), static_cast(atype))}; + switch(ctype) { + case Contact::Type::Bcc: + return GMIME_ADDRESS_TYPE_BCC; + case Contact::Type::Cc: + return GMIME_ADDRESS_TYPE_CC; + case Contact::Type::From: + return GMIME_ADDRESS_TYPE_FROM; + case Contact::Type::To: + return GMIME_ADDRESS_TYPE_TO; + case Contact::Type::ReplyTo: + return GMIME_ADDRESS_TYPE_REPLY_TO; + case Contact::Type::Sender: + return GMIME_ADDRESS_TYPE_SENDER; + default: + return Nothing; + } +} + +static Mu::Contacts +all_contacts(const MimeMessage& msg) +{ + Contacts contacts; + + for (auto&& cctype: { + 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(), + std::back_inserter(contacts)); + } + + return contacts; +} + +Mu::Contacts +MimeMessage::contacts(Contact::Type ctype) const noexcept +{ + /* special case: get all */ + if (ctype == Contact::Type::None) + return all_contacts(*this); + + const auto atype{address_type(ctype)}; + if (!atype) + return {}; + + auto addrs{g_mime_message_get_addresses(self(), *atype)}; if (!addrs) return {}; - const auto msgtime{date().value_or(0)}; - const auto opt_field_id = std::invoke( - [&]()->Option{ - switch(atype) { - case AddressType::To: - return Field::Id::To; - case AddressType::From: - return Field::Id::From; - case AddressType::Bcc: - return Field::Id::Bcc; - case AddressType::Cc: - return Field::Id::Cc; - default: - return Nothing; - } - }); Contacts contacts; auto lst_len{internet_address_list_length(addrs)}; @@ -309,8 +342,7 @@ MimeMessage::addresses(AddressType atype) const noexcept if (G_UNLIKELY(!email)) continue; - contacts.push_back(Contact{email, name ? name : "", - opt_field_id, msgtime}); + contacts.emplace_back(email, name ? name : "", ctype, msgtime); } return contacts; @@ -343,12 +375,11 @@ MimeMessage::references() const noexcept for (auto i = 0; i != g_mime_references_length(mime_refs); ++i) { - if (auto&& msgid{g_mime_references_get_message_id(mime_refs, i)}; !msgid) - continue; // invalid - else if (is_dup(refs, msgid)) - continue; // skip dups - else - refs.emplace_back(msgid); + const auto msgid{g_mime_references_get_message_id(mime_refs, i)}; + if (!msgid || is_dup(refs, msgid)) + continue; // invalid or skip dups + + refs.emplace_back(msgid); } g_mime_references_free(mime_refs); } diff --git a/lib/message/mu-mime-object.hh b/lib/message/mu-mime-object.hh index 7e260a10..14475cf9 100644 --- a/lib/message/mu-mime-object.hh +++ b/lib/message/mu-mime-object.hh @@ -914,21 +914,14 @@ public: */ static Result make_from_text (const std::string& text); - /** - * Address types + * Get the contacts of a given type, or None for _all_ * + * @param ctype contact type + * + * @return contacts */ - enum struct AddressType { - Sender = GMIME_ADDRESS_TYPE_SENDER, - From = GMIME_ADDRESS_TYPE_FROM, - ReplyTo = GMIME_ADDRESS_TYPE_REPLY_TO, - To = GMIME_ADDRESS_TYPE_TO, - Cc = GMIME_ADDRESS_TYPE_CC, - Bcc = GMIME_ADDRESS_TYPE_BCC - }; - - Contacts addresses(AddressType atype) const noexcept; + Contacts contacts(Contact::Type ctype) const noexcept; /** * Gets the message-id if it exists, or nullopt otherwise.