feat: Also lookup key contacts in lookup_id_by_addr() (#7073)

If there is both a key and an address contact, return the most recently
seen one.
This commit is contained in:
Hocuri
2025-08-04 21:32:09 +02:00
committed by GitHub
parent 8fe3ce5cab
commit 93241a4beb
10 changed files with 102 additions and 13 deletions

View File

@@ -755,7 +755,19 @@ impl Contact {
self.is_bot
}
/// Check if an e-mail address belongs to a known and unblocked contact.
/// Looks up a known and unblocked contact with a given e-mail address.
/// To get a list of all known and unblocked contacts, use contacts_get_contacts().
///
///
/// **POTENTIAL SECURITY ISSUE**: If there are multiple contacts with this address
/// (e.g. an address-contact and a key-contact),
/// this looks up the most recently seen contact,
/// i.e. which contact is returned depends on which contact last sent a message.
/// If the user just clicked on a mailto: link, then this is the best thing you can do.
/// But **DO NOT** internally represent contacts by their email address
/// and do not use this function to look them up;
/// otherwise this function will sometimes look up the wrong contact.
/// Instead, you should internally represent contacts by their ids.
///
/// Known and unblocked contacts will be returned by `get_contacts()`.
///
@@ -795,8 +807,8 @@ impl Contact {
.query_get_value(
"SELECT id FROM contacts
WHERE addr=?1 COLLATE NOCASE
AND fingerprint='' -- Do not lookup key-contacts
AND id>?2 AND origin>=?3 AND (? OR blocked=?)",
AND id>?2 AND origin>=?3 AND (? OR blocked=?)
ORDER BY last_seen DESC LIMIT 1",
(
&addr_normalized,
ContactId::LAST_SPECIAL,

View File

@@ -1072,6 +1072,7 @@ async fn test_sync_create() -> Result<()> {
.unwrap();
let a1b_contact = Contact::get_by_id(alice1, a1b_contact_id).await?;
assert_eq!(a1b_contact.name, "Bob");
assert_eq!(a1b_contact.is_key_contact(), false);
Contact::create(alice0, "Bob Renamed", "bob@example.net").await?;
test_utils::sync(alice0, alice1).await;
@@ -1081,6 +1082,7 @@ async fn test_sync_create() -> Result<()> {
assert_eq!(id, a1b_contact_id);
let a1b_contact = Contact::get_by_id(alice1, a1b_contact_id).await?;
assert_eq!(a1b_contact.name, "Bob Renamed");
assert_eq!(a1b_contact.is_key_contact(), false);
Ok(())
}

View File

@@ -683,6 +683,7 @@ async fn test_selfavatar_unencrypted_signed() {
.unwrap()
.unwrap();
let alice_contact = Contact::get_by_id(&bob.ctx, alice_id).await.unwrap();
assert_eq!(alice_contact.is_key_contact(), false);
assert!(
alice_contact
.get_profile_image(&bob.ctx)

View File

@@ -991,6 +991,7 @@ async fn test_other_device_writes_to_mailinglist() -> Result<()> {
.await?
.unwrap();
let list_post_contact = Contact::get_by_id(&t, list_post_contact_id).await?;
assert_eq!(list_post_contact.is_key_contact(), false);
assert_eq!(
list_post_contact.param.get(Param::ListId).unwrap(),
"delta.codespeak.net"
@@ -1453,6 +1454,7 @@ async fn test_apply_mailinglist_changes_assigned_by_reply() {
.unwrap()
.unwrap();
let contact = Contact::get_by_id(&t, contact_id).await.unwrap();
assert_eq!(contact.is_key_contact(), false);
assert_eq!(
contact.param.get(Param::ListId).unwrap(),
"deltachat-core-rust.deltachat.github.com"
@@ -3682,10 +3684,13 @@ async fn test_mua_user_adds_recipient_to_single_chat() -> Result<()> {
chat::get_chat_contacts(&alice, group_chat.id).await?.len(),
4
);
let fiona = Contact::lookup_id_by_addr(&alice, "fiona@example.net", Origin::IncomingTo)
.await?
.unwrap();
assert!(chat::is_contact_in_chat(&alice, group_chat.id, fiona).await?);
let fiona_contact_id =
Contact::lookup_id_by_addr(&alice, "fiona@example.net", Origin::IncomingTo)
.await?
.unwrap();
assert!(chat::is_contact_in_chat(&alice, group_chat.id, fiona_contact_id).await?);
let fiona_contact = Contact::get_by_id(&alice, fiona_contact_id).await?;
assert_eq!(fiona_contact.is_key_contact(), false);
Ok(())
}

View File

@@ -47,6 +47,7 @@ async fn test_key_contacts_migration_autocrypt() -> Result<()> {
.await?
.unwrap();
let email_bob = Contact::get_by_id(&t, email_bob_id).await?;
assert_eq!(email_bob.is_key_contact(), false);
assert_eq!(email_bob.origin, Origin::Hidden); // Email bob is in no chats, so, contact is hidden
assert_eq!(email_bob.e2ee_avail(&t).await?, false);
assert_eq!(email_bob.fingerprint(), None);
@@ -85,6 +86,7 @@ async fn test_key_contacts_migration_email1() -> Result<()> {
.await?
.unwrap();
let email_bob = Contact::get_by_id(&t, email_bob_id).await?;
assert_eq!(email_bob.is_key_contact(), false);
assert_eq!(email_bob.origin, Origin::OutgoingTo);
assert_eq!(email_bob.e2ee_avail(&t).await?, false);
assert_eq!(email_bob.fingerprint(), None);
@@ -114,6 +116,7 @@ async fn test_key_contacts_migration_email2() -> Result<()> {
.await?
.unwrap();
let email_bob = Contact::get_by_id(&t, email_bob_id).await?;
assert_eq!(email_bob.is_key_contact(), false);
assert_eq!(email_bob.origin, Origin::OutgoingTo); // Email bob is in no chats, so, contact is hidden
assert_eq!(email_bob.e2ee_avail(&t).await?, false);
assert_eq!(email_bob.fingerprint(), None);
@@ -148,6 +151,7 @@ async fn test_key_contacts_migration_verified() -> Result<()> {
.unwrap();
let email_bob = Contact::get_by_id(&t, email_bob_id).await?;
dbg!(&email_bob);
assert_eq!(email_bob.is_key_contact(), false);
assert_eq!(email_bob.origin, Origin::Hidden); // Email bob is in no chats, so, contact is hidden
assert_eq!(email_bob.e2ee_avail(&t).await?, false);
assert_eq!(email_bob.fingerprint(), None);