feat: key-contacts

This change introduces a new type of contacts
identified by their public key fingerprint
rather than an e-mail address.

Encrypted chats now stay encrypted
and unencrypted chats stay unencrypted.
For example, 1:1 chats with key-contacts
are encrypted and 1:1 chats with address-contacts
are unencrypted.
Groups that have a group ID are encrypted
and can only contain key-contacts
while groups that don't have a group ID ("adhoc groups")
are unencrypted and can only contain address-contacts.

JSON-RPC API `reset_contact_encryption` is removed.
Python API `Contact.reset_encryption` is removed.
"Group tracking plugin" in legacy Python API was removed because it
relied on parsing email addresses from system messages with regexps.

Co-authored-by: Hocuri <hocuri@gmx.de>
Co-authored-by: iequidoo <dgreshilov@gmail.com>
Co-authored-by: B. Petersen <r10s@b44t.com>
This commit is contained in:
link2xt
2025-06-26 14:07:39 +00:00
parent 7ac04d0204
commit 416131b4a2
84 changed files with 4735 additions and 6338 deletions

View File

@@ -1,4 +1,4 @@
use deltachat_contact_tools::may_be_valid_addr;
use deltachat_contact_tools::{addr_cmp, may_be_valid_addr};
use super::*;
use crate::chat::{get_chat_contacts, send_text_msg, Chat};
@@ -56,58 +56,49 @@ fn test_split_address_book() {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_get_contacts() -> Result<()> {
let context = TestContext::new().await;
let mut tcm = TestContextManager::new();
let context = tcm.bob().await;
let alice = tcm.alice().await;
alice
.set_config(Config::Displayname, Some("MyName"))
.await?;
assert!(context.get_all_self_addrs().await?.is_empty());
// Bob is not in the contacts yet.
let contacts = Contact::get_all(&context.ctx, 0, Some("bob")).await?;
// Alice is not in the contacts yet.
let contacts = Contact::get_all(&context.ctx, 0, Some("Alice")).await?;
assert_eq!(contacts.len(), 0);
let contacts = Contact::get_all(&context.ctx, 0, Some("MyName")).await?;
assert_eq!(contacts.len(), 0);
let (id, _modified) = Contact::add_or_lookup(
&context.ctx,
"bob",
&ContactAddress::new("user@example.org")?,
Origin::IncomingReplyTo,
)
.await?;
let id = context.add_or_lookup_contact_id(&alice).await;
assert_ne!(id, ContactId::UNDEFINED);
let contact = Contact::get_by_id(&context.ctx, id).await.unwrap();
let contact = Contact::get_by_id(&context, id).await.unwrap();
assert_eq!(contact.get_name(), "");
assert_eq!(contact.get_authname(), "bob");
assert_eq!(contact.get_display_name(), "bob");
assert_eq!(contact.get_authname(), "MyName");
assert_eq!(contact.get_display_name(), "MyName");
// Search by name.
let contacts = Contact::get_all(&context.ctx, 0, Some("bob")).await?;
let contacts = Contact::get_all(&context, 0, Some("myname")).await?;
assert_eq!(contacts.len(), 1);
assert_eq!(contacts.first(), Some(&id));
// Search by address.
let contacts = Contact::get_all(&context.ctx, 0, Some("user")).await?;
let contacts = Contact::get_all(&context, 0, Some("alice@example.org")).await?;
assert_eq!(contacts.len(), 1);
assert_eq!(contacts.first(), Some(&id));
let contacts = Contact::get_all(&context.ctx, 0, Some("alice")).await?;
let contacts = Contact::get_all(&context, 0, Some("Foobar")).await?;
assert_eq!(contacts.len(), 0);
// Set Bob name to "someone" manually.
let (contact_bob_id, modified) = Contact::add_or_lookup(
&context.ctx,
"someone",
&ContactAddress::new("user@example.org")?,
Origin::ManuallyCreated,
)
.await?;
assert_eq!(contact_bob_id, id);
assert_eq!(modified, Modifier::Modified);
// Set Alice name to "someone" manually.
id.set_name(&context, "someone").await?;
let contact = Contact::get_by_id(&context.ctx, id).await.unwrap();
assert_eq!(contact.get_name(), "someone");
assert_eq!(contact.get_authname(), "bob");
assert_eq!(contact.get_authname(), "MyName");
assert_eq!(contact.get_display_name(), "someone");
// Not searchable by authname, because it is not displayed.
let contacts = Contact::get_all(&context.ctx, 0, Some("bob")).await?;
let contacts = Contact::get_all(&context, 0, Some("MyName")).await?;
assert_eq!(contacts.len(), 0);
// Search by display name (same as manually set name).
@@ -133,7 +124,7 @@ async fn test_is_self_addr() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_add_or_lookup() {
// add some contacts, this also tests add_address_book()
let t = TestContext::new().await;
let t = TestContext::new_alice().await;
let book = concat!(
" Name one \n one@eins.org \n",
"Name two\ntwo@deux.net\n",
@@ -247,7 +238,7 @@ async fn test_add_or_lookup() {
// check SELF
let contact = Contact::get_by_id(&t, ContactId::SELF).await.unwrap();
assert_eq!(contact.get_name(), stock_str::self_msg(&t).await);
assert_eq!(contact.get_addr(), ""); // we're not configured
assert_eq!(contact.get_addr(), "alice@example.org");
assert!(!contact.is_blocked());
}
@@ -282,7 +273,7 @@ async fn test_contact_name_changes() -> Result<()> {
assert_eq!(contact.get_display_name(), "f@example.org");
assert_eq!(contact.get_name_n_addr(), "f@example.org");
let contacts = Contact::get_all(&t, 0, Some("f@example.org")).await?;
assert_eq!(contacts.len(), 1);
assert_eq!(contacts.len(), 0);
// second message inits the name
receive_imf(
@@ -308,9 +299,9 @@ async fn test_contact_name_changes() -> Result<()> {
assert_eq!(contact.get_display_name(), "Flobbyfoo");
assert_eq!(contact.get_name_n_addr(), "Flobbyfoo (f@example.org)");
let contacts = Contact::get_all(&t, 0, Some("f@example.org")).await?;
assert_eq!(contacts.len(), 1);
assert_eq!(contacts.len(), 0);
let contacts = Contact::get_all(&t, 0, Some("flobbyfoo")).await?;
assert_eq!(contacts.len(), 1);
assert_eq!(contacts.len(), 0);
// third message changes the name
receive_imf(
@@ -338,11 +329,11 @@ async fn test_contact_name_changes() -> Result<()> {
assert_eq!(contact.get_display_name(), "Foo Flobby");
assert_eq!(contact.get_name_n_addr(), "Foo Flobby (f@example.org)");
let contacts = Contact::get_all(&t, 0, Some("f@example.org")).await?;
assert_eq!(contacts.len(), 1);
assert_eq!(contacts.len(), 0);
let contacts = Contact::get_all(&t, 0, Some("flobbyfoo")).await?;
assert_eq!(contacts.len(), 0);
let contacts = Contact::get_all(&t, 0, Some("Foo Flobby")).await?;
assert_eq!(contacts.len(), 1);
assert_eq!(contacts.len(), 0);
// change name manually
let test_id = Contact::create(&t, "Falk", "f@example.org").await?;
@@ -356,9 +347,9 @@ async fn test_contact_name_changes() -> Result<()> {
assert_eq!(contact.get_display_name(), "Falk");
assert_eq!(contact.get_name_n_addr(), "Falk (f@example.org)");
let contacts = Contact::get_all(&t, 0, Some("f@example.org")).await?;
assert_eq!(contacts.len(), 1);
assert_eq!(contacts.len(), 0);
let contacts = Contact::get_all(&t, 0, Some("falk")).await?;
assert_eq!(contacts.len(), 1);
assert_eq!(contacts.len(), 0);
Ok(())
}
@@ -366,20 +357,13 @@ async fn test_contact_name_changes() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_delete() -> Result<()> {
let alice = TestContext::new_alice().await;
let bob = TestContext::new_bob().await;
assert!(Contact::delete(&alice, ContactId::SELF).await.is_err());
// Create Bob contact
let (contact_id, _) = Contact::add_or_lookup(
&alice,
"Bob",
&ContactAddress::new("bob@example.net")?,
Origin::ManuallyCreated,
)
.await?;
let chat = alice
.create_chat_with_contact("Bob", "bob@example.net")
.await;
let contact_id = alice.add_or_lookup_contact_id(&bob).await;
let chat = alice.create_chat(&bob).await;
assert_eq!(
Contact::get_all(&alice, 0, Some("bob@example.net"))
.await?
@@ -416,30 +400,57 @@ async fn test_delete() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_delete_and_recreate_contact() -> Result<()> {
let t = TestContext::new_alice().await;
let mut tcm = TestContextManager::new();
let t = tcm.alice().await;
let bob = tcm.bob().await;
// test recreation after physical deletion
let contact_id1 = Contact::create(&t, "Foo", "foo@bar.de").await?;
assert_eq!(Contact::get_all(&t, 0, Some("foo@bar.de")).await?.len(), 1);
let contact_id1 = t.add_or_lookup_contact_id(&bob).await;
assert_eq!(
Contact::get_all(&t, 0, Some("bob@example.net"))
.await?
.len(),
1
);
Contact::delete(&t, contact_id1).await?;
assert!(Contact::get_by_id(&t, contact_id1).await.is_err());
assert_eq!(Contact::get_all(&t, 0, Some("foo@bar.de")).await?.len(), 0);
let contact_id2 = Contact::create(&t, "Foo", "foo@bar.de").await?;
assert_eq!(
Contact::get_all(&t, 0, Some("bob@example.net"))
.await?
.len(),
0
);
let contact_id2 = t.add_or_lookup_contact_id(&bob).await;
assert_ne!(contact_id2, contact_id1);
assert_eq!(Contact::get_all(&t, 0, Some("foo@bar.de")).await?.len(), 1);
assert_eq!(
Contact::get_all(&t, 0, Some("bob@example.net"))
.await?
.len(),
1
);
// test recreation after hiding
t.create_chat_with_contact("Foo", "foo@bar.de").await;
t.create_chat(&bob).await;
Contact::delete(&t, contact_id2).await?;
let contact = Contact::get_by_id(&t, contact_id2).await?;
assert_eq!(contact.origin, Origin::Hidden);
assert_eq!(Contact::get_all(&t, 0, Some("foo@bar.de")).await?.len(), 0);
assert_eq!(
Contact::get_all(&t, 0, Some("bob@example.net"))
.await?
.len(),
0
);
let contact_id3 = Contact::create(&t, "Foo", "foo@bar.de").await?;
let contact_id3 = t.add_or_lookup_contact_id(&bob).await;
let contact = Contact::get_by_id(&t, contact_id3).await?;
assert_eq!(contact.origin, Origin::ManuallyCreated);
assert_eq!(contact.origin, Origin::CreateChat);
assert_eq!(contact_id3, contact_id2);
assert_eq!(Contact::get_all(&t, 0, Some("foo@bar.de")).await?.len(), 1);
assert_eq!(
Contact::get_all(&t, 0, Some("bob@example.net"))
.await?
.len(),
1
);
Ok(())
}
@@ -728,51 +739,59 @@ async fn test_contact_get_color() -> Result<()> {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_contact_get_encrinfo() -> Result<()> {
let alice = TestContext::new_alice().await;
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
let bob = &tcm.bob().await;
// Return error for special IDs
let encrinfo = Contact::get_encrinfo(&alice, ContactId::SELF).await;
let encrinfo = Contact::get_encrinfo(alice, ContactId::SELF).await;
assert!(encrinfo.is_err());
let encrinfo = Contact::get_encrinfo(&alice, ContactId::DEVICE).await;
let encrinfo = Contact::get_encrinfo(alice, ContactId::DEVICE).await;
assert!(encrinfo.is_err());
let (contact_bob_id, _modified) = Contact::add_or_lookup(
&alice,
"Bob",
&ContactAddress::new("bob@example.net")?,
Origin::ManuallyCreated,
)
.await?;
let encrinfo = Contact::get_encrinfo(&alice, contact_bob_id).await?;
let address_contact_bob_id = alice.add_or_lookup_address_contact_id(bob).await;
let encrinfo = Contact::get_encrinfo(alice, address_contact_bob_id).await?;
assert_eq!(encrinfo, "No encryption");
let contact = Contact::get_by_id(&alice, contact_bob_id).await?;
assert!(!contact.e2ee_avail(&alice).await?);
let bob = TestContext::new_bob().await;
let chat_alice = bob
.create_chat_with_contact("Alice", "alice@example.org")
.await;
send_text_msg(&bob, chat_alice.id, "Hello".to_string()).await?;
let msg = bob.pop_sent_msg().await;
alice.recv_msg(&msg).await;
let contact = Contact::get_by_id(alice, address_contact_bob_id).await?;
assert!(!contact.e2ee_avail(alice).await?);
let encrinfo = Contact::get_encrinfo(&alice, contact_bob_id).await?;
let contact_bob_id = alice.add_or_lookup_contact_id(bob).await;
let encrinfo = Contact::get_encrinfo(alice, contact_bob_id).await?;
assert_eq!(
encrinfo,
"End-to-end encryption preferred.
"End-to-end encryption available.
Fingerprints:
Me (alice@example.org):
2E6F A2CB 23B5 32D7 2863
4B58 64B0 8F61 A9ED 9443
Bob (bob@example.net):
bob@example.net (bob@example.net):
CCCB 5AA9 F6E1 141C 9431
65F1 DB18 B18C BCF7 0487"
);
let contact = Contact::get_by_id(&alice, contact_bob_id).await?;
assert!(contact.e2ee_avail(&alice).await?);
let contact = Contact::get_by_id(alice, contact_bob_id).await?;
assert!(contact.e2ee_avail(alice).await?);
alice.sql.execute("DELETE FROM public_keys", ()).await?;
let encrinfo = Contact::get_encrinfo(alice, contact_bob_id).await?;
assert_eq!(
encrinfo,
"No encryption.
Fingerprints:
Me (alice@example.org):
2E6F A2CB 23B5 32D7 2863
4B58 64B0 8F61 A9ED 9443
bob@example.net (bob@example.net):
CCCB 5AA9 F6E1 141C 9431
65F1 DB18 B18C BCF7 0487"
);
let contact = Contact::get_by_id(alice, contact_bob_id).await?;
assert!(!contact.e2ee_avail(alice).await?);
Ok(())
}
@@ -780,24 +799,24 @@ CCCB 5AA9 F6E1 141C 9431
/// synchronized when the message is not encrypted.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_synchronize_status() -> Result<()> {
let mut tcm = TestContextManager::new();
// Alice has two devices.
let alice1 = TestContext::new_alice().await;
let alice2 = TestContext::new_alice().await;
let alice1 = &tcm.alice().await;
let alice2 = &tcm.alice().await;
// Bob has one device.
let bob = TestContext::new_bob().await;
let bob = &tcm.bob().await;
let default_status = alice1.get_config(Config::Selfstatus).await?;
alice1
.set_config(Config::Selfstatus, Some("New status"))
.await?;
let chat = alice1
.create_chat_with_contact("Bob", "bob@example.net")
.await;
let chat = alice1.create_email_chat(bob).await;
// Alice sends a message to Bob from the first device.
send_text_msg(&alice1, chat.id, "Hello".to_string()).await?;
send_text_msg(alice1, chat.id, "Hello".to_string()).await?;
let sent_msg = alice1.pop_sent_msg().await;
// Message is not encrypted.
@@ -813,18 +832,9 @@ async fn test_synchronize_status() -> Result<()> {
// Message was not encrypted, so status is not copied.
assert_eq!(alice2.get_config(Config::Selfstatus).await?, default_status);
// Bob replies.
let chat = bob
.create_chat_with_contact("Alice", "alice@example.org")
.await;
send_text_msg(&bob, chat.id, "Reply".to_string()).await?;
let sent_msg = bob.pop_sent_msg().await;
alice1.recv_msg(&sent_msg).await;
alice2.recv_msg(&sent_msg).await;
// Alice sends second message.
send_text_msg(&alice1, chat.id, "Hello".to_string()).await?;
// Alice sends encrypted message.
let chat = alice1.create_chat(bob).await;
send_text_msg(alice1, chat.id, "Hello".to_string()).await?;
let sent_msg = alice1.pop_sent_msg().await;
// Second message is encrypted.
@@ -845,12 +855,14 @@ async fn test_synchronize_status() -> Result<()> {
/// Tests that DC_EVENT_SELFAVATAR_CHANGED is emitted on avatar changes.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_selfavatar_changed_event() -> Result<()> {
let mut tcm = TestContextManager::new();
// Alice has two devices.
let alice1 = TestContext::new_alice().await;
let alice2 = TestContext::new_alice().await;
let alice1 = &tcm.alice().await;
let alice2 = &tcm.alice().await;
// Bob has one device.
let bob = TestContext::new_bob().await;
let bob = &tcm.bob().await;
assert_eq!(alice1.get_config(Config::Selfavatar).await?, None);
@@ -866,20 +878,9 @@ async fn test_selfavatar_changed_event() -> Result<()> {
.get_matching(|e| matches!(e, EventType::SelfavatarChanged))
.await;
// Bob sends a message so that Alice can encrypt to him.
let chat = bob
.create_chat_with_contact("Alice", "alice@example.org")
.await;
send_text_msg(&bob, chat.id, "Reply".to_string()).await?;
let sent_msg = bob.pop_sent_msg().await;
alice1.recv_msg(&sent_msg).await;
alice2.recv_msg(&sent_msg).await;
// Alice sends a message.
let alice1_chat_id = alice1.get_last_msg().await.chat_id;
alice1_chat_id.accept(&alice1).await?;
send_text_msg(&alice1, alice1_chat_id, "Hello".to_string()).await?;
let alice1_chat_id = alice1.create_chat(bob).await.id;
send_text_msg(alice1, alice1_chat_id, "Hello".to_string()).await?;
let sent_msg = alice1.pop_sent_msg().await;
// The message is encrypted.
@@ -1008,7 +1009,7 @@ async fn test_verified_by_none() -> Result<()> {
let contact = Contact::get_by_id(&alice, contact_id).await?;
assert!(contact.get_verifier_id(&alice).await?.is_none());
// Receive a message from Bob to create a peerstate.
// Receive a message from Bob to save the public key.
let chat = bob.create_chat(&alice).await;
let sent_msg = bob.send_text(chat.id, "moin").await;
alice.recv_msg(&sent_msg).await;
@@ -1066,14 +1067,9 @@ async fn test_make_n_import_vcard() -> Result<()> {
let bob_biography = bob.get_config(Config::Selfstatus).await?.unwrap();
let chat = bob.create_chat(alice).await;
let sent_msg = bob.send_text(chat.id, "moin").await;
alice.recv_msg(&sent_msg).await;
let bob_id = Contact::create(alice, "Some Bob", &bob_addr).await?;
let key_base64 = Peerstate::from_addr(alice, &bob_addr)
.await?
.unwrap()
.peek_key(false)
.unwrap()
.to_base64();
let bob_id = alice.recv_msg(&sent_msg).await.from_id;
let bob_contact = Contact::get_by_id(alice, bob_id).await?;
let key_base64 = bob_contact.public_key(alice).await?.unwrap().to_base64();
let fiona_id = Contact::create(alice, "Fiona", "fiona@example.net").await?;
assert_eq!(make_vcard(alice, &[]).await?, "".to_string());
@@ -1157,8 +1153,10 @@ async fn test_make_n_import_vcard() -> Result<()> {
Ok(())
}
/// Tests importing a vCard with the same email address,
/// but a new key.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_import_vcard_updates_only_key() -> Result<()> {
async fn test_import_vcard_key_change() -> Result<()> {
let alice = &TestContext::new_alice().await;
let bob = &TestContext::new_bob().await;
let bob_addr = &bob.get_config(Config::Addr).await?.unwrap();
@@ -1176,28 +1174,34 @@ async fn test_import_vcard_updates_only_key() -> Result<()> {
let msg = bob.recv_msg(&sent_msg).await;
assert!(msg.get_showpadlock());
let bob = &TestContext::new().await;
bob.configure_addr(bob_addr).await;
bob.set_config(Config::Displayname, Some("Not Bob")).await?;
let avatar_path = bob.dir.path().join("avatar.png");
let bob1 = &TestContext::new().await;
bob1.configure_addr(bob_addr).await;
bob1.set_config(Config::Displayname, Some("New Bob"))
.await?;
let avatar_path = bob1.dir.path().join("avatar.png");
let avatar_bytes = include_bytes!("../../test-data/image/avatar64x64.png");
tokio::fs::write(&avatar_path, avatar_bytes).await?;
bob.set_config(Config::Selfavatar, Some(avatar_path.to_str().unwrap()))
bob1.set_config(Config::Selfavatar, Some(avatar_path.to_str().unwrap()))
.await?;
SystemTime::shift(Duration::from_secs(1));
let vcard1 = make_vcard(bob, &[ContactId::SELF]).await?;
assert_eq!(import_vcard(alice, &vcard1).await?, vec![alice_bob_id]);
let vcard1 = make_vcard(bob1, &[ContactId::SELF]).await?;
let alice_bob_id1 = import_vcard(alice, &vcard1).await?[0];
assert_ne!(alice_bob_id1, alice_bob_id);
let alice_bob_contact = Contact::get_by_id(alice, alice_bob_id).await?;
assert_eq!(alice_bob_contact.get_authname(), "Bob");
assert_eq!(alice_bob_contact.get_profile_image(alice).await?, None);
let alice_bob_contact1 = Contact::get_by_id(alice, alice_bob_id1).await?;
assert_eq!(alice_bob_contact1.get_authname(), "New Bob");
assert!(alice_bob_contact1.get_profile_image(alice).await?.is_some());
// Last message is still the same,
// no new messages are added.
let msg = alice.get_last_msg_in(chat_id).await;
assert!(msg.is_info());
assert_eq!(
msg.get_text(),
stock_str::contact_setup_changed(alice, bob_addr).await
);
let sent_msg = alice.send_text(chat_id, "moin").await;
let msg = bob.recv_msg(&sent_msg).await;
assert_eq!(msg.get_text(), "moin");
let chat_id1 = ChatId::create_for_contact(alice, alice_bob_id1).await?;
let sent_msg = alice.send_text(chat_id1, "moin").await;
let msg = bob1.recv_msg(&sent_msg).await;
assert!(msg.get_showpadlock());
// The old vCard is imported, but doesn't change Bob's key for Alice.
@@ -1209,63 +1213,6 @@ async fn test_import_vcard_updates_only_key() -> Result<()> {
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_reset_encryption() -> Result<()> {
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
let bob = &tcm.bob().await;
let msg = tcm.send_recv_accept(bob, alice, "Hi!").await;
assert_eq!(msg.get_showpadlock(), true);
let alice_bob_chat_id = msg.chat_id;
let alice_bob_contact_id = msg.from_id;
alice_bob_contact_id.reset_encryption(alice).await?;
let sent = alice.send_text(alice_bob_chat_id, "Unencrypted").await;
let msg = bob.recv_msg(&sent).await;
assert_eq!(msg.get_showpadlock(), false);
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_reset_verified_encryption() -> Result<()> {
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
let bob = &tcm.bob().await;
tcm.execute_securejoin(bob, alice).await;
let msg = tcm.send_recv(bob, alice, "Encrypted").await;
assert_eq!(msg.get_showpadlock(), true);
let alice_bob_chat_id = msg.chat_id;
let alice_bob_contact_id = msg.from_id;
alice_bob_contact_id.reset_encryption(alice).await?;
// Check that the contact is still verified after resetting encryption.
let alice_bob_contact = Contact::get_by_id(alice, alice_bob_contact_id).await?;
assert_eq!(alice_bob_contact.is_verified(alice).await?, true);
// 1:1 chat and profile is no longer verified.
assert_eq!(alice_bob_contact.is_profile_verified(alice).await?, false);
let info_msg = alice.get_last_msg_in(alice_bob_chat_id).await;
assert_eq!(
info_msg.text,
"bob@example.net sent a message from another device."
);
let sent = alice.send_text(alice_bob_chat_id, "Unencrypted").await;
let msg = bob.recv_msg(&sent).await;
assert_eq!(msg.get_showpadlock(), false);
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_self_is_verified() -> Result<()> {
let mut tcm = TestContextManager::new();
@@ -1275,9 +1222,87 @@ async fn test_self_is_verified() -> Result<()> {
assert_eq!(contact.is_verified(&alice).await?, true);
assert!(contact.is_profile_verified(&alice).await?);
assert!(contact.get_verifier_id(&alice).await?.is_none());
assert!(contact.is_key_contact());
let chat_id = ChatId::get_for_contact(&alice, ContactId::SELF).await?;
assert!(chat_id.is_protected(&alice).await.unwrap() == ProtectionStatus::Protected);
Ok(())
}
/// Tests that importing a vCard with a key creates a key-contact.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_vcard_creates_key_contact() -> Result<()> {
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
let bob = &tcm.bob().await;
let vcard = make_vcard(bob, &[ContactId::SELF]).await?;
let contact_ids = import_vcard(alice, &vcard).await?;
assert_eq!(contact_ids.len(), 1);
let contact_id = contact_ids.first().unwrap();
let contact = Contact::get_by_id(alice, *contact_id).await?;
assert!(contact.is_key_contact());
Ok(())
}
/// Tests changing display name by sending a message.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_name_changes() -> Result<()> {
let mut tcm = TestContextManager::new();
let alice = &tcm.alice().await;
let bob = &tcm.bob().await;
alice
.set_config(Config::Displayname, Some("Alice Revision 1"))
.await?;
let alice_bob_chat = alice.create_chat(bob).await;
let sent_msg = alice.send_text(alice_bob_chat.id, "Hello").await;
let bob_alice_id = bob.recv_msg(&sent_msg).await.from_id;
let bob_alice_contact = Contact::get_by_id(bob, bob_alice_id).await?;
assert_eq!(bob_alice_contact.get_display_name(), "Alice Revision 1");
alice
.set_config(Config::Displayname, Some("Alice Revision 2"))
.await?;
let sent_msg = alice.send_text(alice_bob_chat.id, "Hello").await;
bob.recv_msg(&sent_msg).await;
let bob_alice_contact = Contact::get_by_id(bob, bob_alice_id).await?;
assert_eq!(bob_alice_contact.get_display_name(), "Alice Revision 2");
// Explicitly rename contact to "Renamed".
bob.evtracker.clear_events();
bob_alice_contact.id.set_name(bob, "Renamed").await?;
let event = bob
.evtracker
.get_matching(|e| matches!(e, EventType::ContactsChanged { .. }))
.await;
assert_eq!(
event,
EventType::ContactsChanged(Some(bob_alice_contact.id))
);
let bob_alice_contact = Contact::get_by_id(bob, bob_alice_id).await?;
assert_eq!(bob_alice_contact.get_display_name(), "Renamed");
// Alice also renames self into "Renamed".
alice
.set_config(Config::Displayname, Some("Renamed"))
.await?;
let sent_msg = alice.send_text(alice_bob_chat.id, "Hello").await;
bob.recv_msg(&sent_msg).await;
let bob_alice_contact = Contact::get_by_id(bob, bob_alice_id).await?;
assert_eq!(bob_alice_contact.get_display_name(), "Renamed");
// Contact name was set to "Renamed" explicitly before,
// so it should not be changed.
alice
.set_config(Config::Displayname, Some("Renamed again"))
.await?;
let sent_msg = alice.send_text(alice_bob_chat.id, "Hello").await;
bob.recv_msg(&sent_msg).await;
let bob_alice_contact = Contact::get_by_id(bob, bob_alice_id).await?;
assert_eq!(bob_alice_contact.get_display_name(), "Renamed");
Ok(())
}