Add ContactAddress type

This commit is contained in:
link2xt
2023-01-11 04:19:28 +00:00
parent 6d9d31cad1
commit 89c8d26968
11 changed files with 373 additions and 254 deletions

View File

@@ -3629,7 +3629,7 @@ mod tests {
use crate::chatlist::{get_archived_cnt, Chatlist}; use crate::chatlist::{get_archived_cnt, Chatlist};
use crate::constants::{DC_GCL_ARCHIVED_ONLY, DC_GCL_NO_SPECIALS}; use crate::constants::{DC_GCL_ARCHIVED_ONLY, DC_GCL_NO_SPECIALS};
use crate::contact::Contact; use crate::contact::{Contact, ContactAddress};
use crate::receive_imf::receive_imf; use crate::receive_imf::receive_imf;
use crate::test_utils::TestContext; use crate::test_utils::TestContext;
@@ -4692,10 +4692,13 @@ mod tests {
let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?; let chat_id = create_group_chat(&t, ProtectionStatus::Unprotected, "foo").await?;
assert!(!shall_attach_selfavatar(&t, chat_id).await?); assert!(!shall_attach_selfavatar(&t, chat_id).await?);
let (contact_id, _) = let (contact_id, _) = Contact::add_or_lookup(
Contact::add_or_lookup(&t, "", "foo@bar.org", Origin::IncomingUnknownTo) &t,
.await? "",
.unwrap(); ContactAddress::new("foo@bar.org")?,
Origin::IncomingUnknownTo,
)
.await?;
add_contact_to_chat(&t, chat_id, contact_id).await?; add_contact_to_chat(&t, chat_id, contact_id).await?;
assert!(!shall_attach_selfavatar(&t, chat_id).await?); assert!(!shall_attach_selfavatar(&t, chat_id).await?);
t.set_config(Config::Selfavatar, None).await?; // setting to None also forces re-sending t.set_config(Config::Selfavatar, None).await?; // setting to None also forces re-sending
@@ -4941,10 +4944,8 @@ mod tests {
alice.set_config(Config::ShowEmails, Some("2")).await?; alice.set_config(Config::ShowEmails, Some("2")).await?;
bob.set_config(Config::ShowEmails, Some("2")).await?; bob.set_config(Config::ShowEmails, Some("2")).await?;
let (contact_id, _) = let alice_bob_contact = alice.add_or_lookup_contact(&bob).await;
Contact::add_or_lookup(&alice, "", "bob@example.net", Origin::ManuallyCreated) let contact_id = alice_bob_contact.id;
.await?
.unwrap();
let alice_chat_id = create_group_chat(&alice, ProtectionStatus::Unprotected, "grp").await?; let alice_chat_id = create_group_chat(&alice, ProtectionStatus::Unprotected, "grp").await?;
let alice_chat = Chat::load_from_db(&alice, alice_chat_id).await?; let alice_chat = Chat::load_from_db(&alice, alice_chat_id).await?;
@@ -5654,10 +5655,13 @@ mod tests {
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_create_for_contact_with_blocked() -> Result<()> { async fn test_create_for_contact_with_blocked() -> Result<()> {
let t = TestContext::new().await; let t = TestContext::new().await;
let (contact_id, _) = let (contact_id, _) = Contact::add_or_lookup(
Contact::add_or_lookup(&t, "", "foo@bar.org", Origin::ManuallyCreated) &t,
.await? "",
.unwrap(); ContactAddress::new("foo@bar.org")?,
Origin::ManuallyCreated,
)
.await?;
// create a blocked chat // create a blocked chat
let chat_id_orig = let chat_id_orig =

View File

@@ -4,6 +4,7 @@ use std::cmp::Reverse;
use std::collections::BinaryHeap; use std::collections::BinaryHeap;
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use std::fmt; use std::fmt;
use std::ops::Deref;
use std::path::PathBuf; use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
@@ -36,6 +37,51 @@ use crate::{chat, stock_str};
/// Time during which a contact is considered as seen recently. /// Time during which a contact is considered as seen recently.
const SEEN_RECENTLY_SECONDS: i64 = 600; const SEEN_RECENTLY_SECONDS: i64 = 600;
/// Valid contact address.
#[derive(Debug, Clone, Copy)]
pub(crate) struct ContactAddress<'a>(&'a str);
impl Deref for ContactAddress<'_> {
type Target = str;
fn deref(&self) -> &Self::Target {
self.0
}
}
impl AsRef<str> for ContactAddress<'_> {
fn as_ref(&self) -> &str {
self.0
}
}
impl fmt::Display for ContactAddress<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl<'a> ContactAddress<'a> {
/// Constructs a new contact address from string,
/// normalizing and validating it.
pub fn new(s: &'a str) -> Result<Self> {
let addr = addr_normalize(s);
if !may_be_valid_addr(addr) {
bail!("invalid address {:?}", s);
}
Ok(Self(addr))
}
}
/// Allow converting [`ContactAddress`] to an SQLite type.
impl rusqlite::types::ToSql for ContactAddress<'_> {
fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput> {
let val = rusqlite::types::Value::Text(self.0.to_string());
let out = rusqlite::types::ToSqlOutput::Owned(val);
Ok(out)
}
}
/// Contact ID, including reserved IDs. /// Contact ID, including reserved IDs.
/// ///
/// Some contact IDs are reserved to identify special contacts. This /// Some contact IDs are reserved to identify special contacts. This
@@ -378,12 +424,12 @@ impl Contact {
let name = improve_single_line_input(name); let name = improve_single_line_input(name);
let (name, addr) = sanitize_name_and_addr(&name, addr); let (name, addr) = sanitize_name_and_addr(&name, addr);
let addr = ContactAddress::new(&addr)?;
let (contact_id, sth_modified) = let (contact_id, sth_modified) =
Contact::add_or_lookup(context, &name, &addr, Origin::ManuallyCreated) Contact::add_or_lookup(context, &name, addr, Origin::ManuallyCreated)
.await .await
.context("add_or_lookup")? .context("add_or_lookup")?;
.with_context(|| format!("can't create a contact with address {:?}", addr))?;
let blocked = Contact::is_blocked_load(context, contact_id).await?; let blocked = Contact::is_blocked_load(context, contact_id).await?;
match sth_modified { match sth_modified {
Modifier::None => {} Modifier::None => {}
@@ -473,28 +519,16 @@ impl Contact {
pub(crate) async fn add_or_lookup( pub(crate) async fn add_or_lookup(
context: &Context, context: &Context,
name: &str, name: &str,
addr: &str, addr: ContactAddress<'_>,
mut origin: Origin, mut origin: Origin,
) -> Result<Option<(ContactId, Modifier)>> { ) -> Result<(ContactId, Modifier)> {
let mut sth_modified = Modifier::None; let mut sth_modified = Modifier::None;
ensure!(!addr.is_empty(), "Can not add_or_lookup empty address"); ensure!(!addr.is_empty(), "Can not add_or_lookup empty address");
ensure!(origin != Origin::Unknown, "Missing valid origin"); ensure!(origin != Origin::Unknown, "Missing valid origin");
let addr = addr_normalize(addr).to_string();
if context.is_self_addr(&addr).await? { if context.is_self_addr(&addr).await? {
return Ok(Some((ContactId::SELF, sth_modified))); return Ok((ContactId::SELF, sth_modified));
}
if !may_be_valid_addr(&addr) {
warn!(
context,
"Bad address \"{}\" for contact \"{}\".",
addr,
if !name.is_empty() { name } else { "<unset>" },
);
return Ok(None);
} }
let mut name = name; let mut name = name;
@@ -555,7 +589,7 @@ impl Contact {
|| row_authname.is_empty()); || row_authname.is_empty());
row_id = u32::try_from(id)?; row_id = u32::try_from(id)?;
if origin as i32 >= row_origin as i32 && addr != row_addr { if origin >= row_origin && addr.as_ref() != row_addr {
update_addr = true; update_addr = true;
} }
if update_name || update_authname || update_addr || origin > row_origin { if update_name || update_authname || update_addr || origin > row_origin {
@@ -657,7 +691,7 @@ impl Contact {
} }
} }
Ok(Some((ContactId::new(row_id), sth_modified))) Ok((ContactId::new(row_id), sth_modified))
} }
/// Add a number of contacts. /// Add a number of contacts.
@@ -683,21 +717,25 @@ impl Contact {
for (name, addr) in split_address_book(addr_book).into_iter() { for (name, addr) in split_address_book(addr_book).into_iter() {
let (name, addr) = sanitize_name_and_addr(name, addr); let (name, addr) = sanitize_name_and_addr(name, addr);
let name = normalize_name(&name); let name = normalize_name(&name);
match Contact::add_or_lookup(context, &name, &addr, Origin::AddressBook).await { match ContactAddress::new(&addr) {
Err(err) => { Ok(addr) => {
warn!( match Contact::add_or_lookup(context, &name, addr, Origin::AddressBook).await {
context, Ok((_, modified)) => {
"Failed to add address {} from address book: {}", addr, err if modified != Modifier::None {
); modify_cnt += 1
} }
Ok(None) => { }
warn!(context, "Cannot create contact with address {}.", addr); Err(err) => {
} warn!(
Ok(Some((_, modified))) => { context,
if modified != Modifier::None { "Failed to add address {} from address book: {}", addr, err
modify_cnt += 1 );
}
} }
} }
Err(err) => {
warn!(context, "{:#}.", err);
}
} }
} }
if modify_cnt > 0 { if modify_cnt > 0 {
@@ -1701,11 +1739,10 @@ mod tests {
let (id, _modified) = Contact::add_or_lookup( let (id, _modified) = Contact::add_or_lookup(
&context.ctx, &context.ctx,
"bob", "bob",
"user@example.org", ContactAddress::new("user@example.org")?,
Origin::IncomingReplyTo, Origin::IncomingReplyTo,
) )
.await? .await?;
.unwrap();
assert_ne!(id, ContactId::UNDEFINED); assert_ne!(id, ContactId::UNDEFINED);
let contact = Contact::load_from_db(&context.ctx, id).await.unwrap(); let contact = Contact::load_from_db(&context.ctx, id).await.unwrap();
@@ -1730,11 +1767,10 @@ mod tests {
let (contact_bob_id, modified) = Contact::add_or_lookup( let (contact_bob_id, modified) = Contact::add_or_lookup(
&context.ctx, &context.ctx,
"someone", "someone",
"user@example.org", ContactAddress::new("user@example.org")?,
Origin::ManuallyCreated, Origin::ManuallyCreated,
) )
.await? .await?;
.unwrap();
assert_eq!(contact_bob_id, id); assert_eq!(contact_bob_id, id);
assert_eq!(modified, Modifier::Modified); assert_eq!(modified, Modifier::Modified);
let contact = Contact::load_from_db(&context.ctx, id).await.unwrap(); let contact = Contact::load_from_db(&context.ctx, id).await.unwrap();
@@ -1766,6 +1802,18 @@ mod tests {
Ok(()) Ok(())
} }
#[test]
fn test_contact_address() -> Result<()> {
let alice_addr = "alice@example.org";
let contact_address = ContactAddress::new(alice_addr)?;
assert_eq!(contact_address.as_ref(), alice_addr);
let invalid_addr = "<> foobar";
assert!(ContactAddress::new(invalid_addr).is_err());
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_add_or_lookup() { async fn test_add_or_lookup() {
// add some contacts, this also tests add_address_book() // add some contacts, this also tests add_address_book()
@@ -1781,11 +1829,14 @@ mod tests {
assert_eq!(Contact::add_address_book(&t, book).await.unwrap(), 4); assert_eq!(Contact::add_address_book(&t, book).await.unwrap(), 4);
// check first added contact, this modifies authname because it is empty // check first added contact, this modifies authname because it is empty
let (contact_id, sth_modified) = let (contact_id, sth_modified) = Contact::add_or_lookup(
Contact::add_or_lookup(&t, "bla foo", "one@eins.org", Origin::IncomingUnknownTo) &t,
.await "bla foo",
.unwrap() ContactAddress::new("one@eins.org").unwrap(),
.unwrap(); Origin::IncomingUnknownTo,
)
.await
.unwrap();
assert!(!contact_id.is_special()); assert!(!contact_id.is_special());
assert_eq!(sth_modified, Modifier::Modified); assert_eq!(sth_modified, Modifier::Modified);
let contact = Contact::load_from_db(&t, contact_id).await.unwrap(); let contact = Contact::load_from_db(&t, contact_id).await.unwrap();
@@ -1797,11 +1848,14 @@ mod tests {
assert_eq!(contact.get_name_n_addr(), "Name one (one@eins.org)"); assert_eq!(contact.get_name_n_addr(), "Name one (one@eins.org)");
// modify first added contact // modify first added contact
let (contact_id_test, sth_modified) = let (contact_id_test, sth_modified) = Contact::add_or_lookup(
Contact::add_or_lookup(&t, "Real one", " one@eins.org ", Origin::ManuallyCreated) &t,
.await "Real one",
.unwrap() ContactAddress::new(" one@eins.org ").unwrap(),
.unwrap(); Origin::ManuallyCreated,
)
.await
.unwrap();
assert_eq!(contact_id, contact_id_test); assert_eq!(contact_id, contact_id_test);
assert_eq!(sth_modified, Modifier::Modified); assert_eq!(sth_modified, Modifier::Modified);
let contact = Contact::load_from_db(&t, contact_id).await.unwrap(); let contact = Contact::load_from_db(&t, contact_id).await.unwrap();
@@ -1810,11 +1864,14 @@ mod tests {
assert!(!contact.is_blocked()); assert!(!contact.is_blocked());
// check third added contact (contact without name) // check third added contact (contact without name)
let (contact_id, sth_modified) = let (contact_id, sth_modified) = Contact::add_or_lookup(
Contact::add_or_lookup(&t, "", "three@drei.sam", Origin::IncomingUnknownTo) &t,
.await "",
.unwrap() ContactAddress::new("three@drei.sam").unwrap(),
.unwrap(); Origin::IncomingUnknownTo,
)
.await
.unwrap();
assert!(!contact_id.is_special()); assert!(!contact_id.is_special());
assert_eq!(sth_modified, Modifier::None); assert_eq!(sth_modified, Modifier::None);
let contact = Contact::load_from_db(&t, contact_id).await.unwrap(); let contact = Contact::load_from_db(&t, contact_id).await.unwrap();
@@ -1827,11 +1884,10 @@ mod tests {
let (contact_id_test, sth_modified) = Contact::add_or_lookup( let (contact_id_test, sth_modified) = Contact::add_or_lookup(
&t, &t,
"m. serious", "m. serious",
"three@drei.sam", ContactAddress::new("three@drei.sam").unwrap(),
Origin::IncomingUnknownFrom, Origin::IncomingUnknownFrom,
) )
.await .await
.unwrap()
.unwrap(); .unwrap();
assert_eq!(contact_id, contact_id_test); assert_eq!(contact_id, contact_id_test);
assert_eq!(sth_modified, Modifier::Modified); assert_eq!(sth_modified, Modifier::Modified);
@@ -1840,11 +1896,14 @@ mod tests {
assert!(!contact.is_blocked()); assert!(!contact.is_blocked());
// manually edit name of third contact (does not changed authorized name) // manually edit name of third contact (does not changed authorized name)
let (contact_id_test, sth_modified) = let (contact_id_test, sth_modified) = Contact::add_or_lookup(
Contact::add_or_lookup(&t, "schnucki", "three@drei.sam", Origin::ManuallyCreated) &t,
.await "schnucki",
.unwrap() ContactAddress::new("three@drei.sam").unwrap(),
.unwrap(); Origin::ManuallyCreated,
)
.await
.unwrap();
assert_eq!(contact_id, contact_id_test); assert_eq!(contact_id, contact_id_test);
assert_eq!(sth_modified, Modifier::Modified); assert_eq!(sth_modified, Modifier::Modified);
let contact = Contact::load_from_db(&t, contact_id).await.unwrap(); let contact = Contact::load_from_db(&t, contact_id).await.unwrap();
@@ -1853,11 +1912,14 @@ mod tests {
assert!(!contact.is_blocked()); assert!(!contact.is_blocked());
// Fourth contact: // Fourth contact:
let (contact_id, sth_modified) = let (contact_id, sth_modified) = Contact::add_or_lookup(
Contact::add_or_lookup(&t, "", "alice@w.de", Origin::IncomingUnknownTo) &t,
.await "",
.unwrap() ContactAddress::new("alice@w.de").unwrap(),
.unwrap(); Origin::IncomingUnknownTo,
)
.await
.unwrap();
assert!(!contact_id.is_special()); assert!(!contact_id.is_special());
assert_eq!(sth_modified, Modifier::None); assert_eq!(sth_modified, Modifier::None);
let contact = Contact::load_from_db(&t, contact_id).await.unwrap(); let contact = Contact::load_from_db(&t, contact_id).await.unwrap();
@@ -1992,10 +2054,13 @@ mod tests {
assert!(Contact::delete(&alice, ContactId::SELF).await.is_err()); assert!(Contact::delete(&alice, ContactId::SELF).await.is_err());
// Create Bob contact // Create Bob contact
let (contact_id, _) = let (contact_id, _) = Contact::add_or_lookup(
Contact::add_or_lookup(&alice, "Bob", "bob@example.net", Origin::ManuallyCreated) &alice,
.await? "Bob",
.unwrap(); ContactAddress::new("bob@example.net")?,
Origin::ManuallyCreated,
)
.await?;
let chat = alice let chat = alice
.create_chat_with_contact("Bob", "bob@example.net") .create_chat_with_contact("Bob", "bob@example.net")
.await; .await;
@@ -2068,11 +2133,14 @@ mod tests {
let t = TestContext::new().await; let t = TestContext::new().await;
// incoming mail `From: bob1 <bob@example.org>` - this should init authname // incoming mail `From: bob1 <bob@example.org>` - this should init authname
let (contact_id, sth_modified) = let (contact_id, sth_modified) = Contact::add_or_lookup(
Contact::add_or_lookup(&t, "bob1", "bob@example.org", Origin::IncomingUnknownFrom) &t,
.await "bob1",
.unwrap() ContactAddress::new("bob@example.org").unwrap(),
.unwrap(); Origin::IncomingUnknownFrom,
)
.await
.unwrap();
assert!(!contact_id.is_special()); assert!(!contact_id.is_special());
assert_eq!(sth_modified, Modifier::Created); assert_eq!(sth_modified, Modifier::Created);
let contact = Contact::load_from_db(&t, contact_id).await.unwrap(); let contact = Contact::load_from_db(&t, contact_id).await.unwrap();
@@ -2081,11 +2149,14 @@ mod tests {
assert_eq!(contact.get_display_name(), "bob1"); assert_eq!(contact.get_display_name(), "bob1");
// incoming mail `From: bob2 <bob@example.org>` - this should update authname // incoming mail `From: bob2 <bob@example.org>` - this should update authname
let (contact_id, sth_modified) = let (contact_id, sth_modified) = Contact::add_or_lookup(
Contact::add_or_lookup(&t, "bob2", "bob@example.org", Origin::IncomingUnknownFrom) &t,
.await "bob2",
.unwrap() ContactAddress::new("bob@example.org").unwrap(),
.unwrap(); Origin::IncomingUnknownFrom,
)
.await
.unwrap();
assert!(!contact_id.is_special()); assert!(!contact_id.is_special());
assert_eq!(sth_modified, Modifier::Modified); assert_eq!(sth_modified, Modifier::Modified);
let contact = Contact::load_from_db(&t, contact_id).await.unwrap(); let contact = Contact::load_from_db(&t, contact_id).await.unwrap();
@@ -2104,11 +2175,14 @@ mod tests {
assert_eq!(contact.get_display_name(), "bob3"); assert_eq!(contact.get_display_name(), "bob3");
// incoming mail `From: bob4 <bob@example.org>` - this should update authname, manually given name is still "bob3" // incoming mail `From: bob4 <bob@example.org>` - this should update authname, manually given name is still "bob3"
let (contact_id, sth_modified) = let (contact_id, sth_modified) = Contact::add_or_lookup(
Contact::add_or_lookup(&t, "bob4", "bob@example.org", Origin::IncomingUnknownFrom) &t,
.await "bob4",
.unwrap() ContactAddress::new("bob@example.org").unwrap(),
.unwrap(); Origin::IncomingUnknownFrom,
)
.await
.unwrap();
assert!(!contact_id.is_special()); assert!(!contact_id.is_special());
assert_eq!(sth_modified, Modifier::Modified); assert_eq!(sth_modified, Modifier::Modified);
let contact = Contact::load_from_db(&t, contact_id).await.unwrap(); let contact = Contact::load_from_db(&t, contact_id).await.unwrap();
@@ -2133,11 +2207,10 @@ mod tests {
let (contact_id_same, sth_modified) = Contact::add_or_lookup( let (contact_id_same, sth_modified) = Contact::add_or_lookup(
&t, &t,
"claire1", "claire1",
"claire@example.org", ContactAddress::new("claire@example.org").unwrap(),
Origin::IncomingUnknownFrom, Origin::IncomingUnknownFrom,
) )
.await .await
.unwrap()
.unwrap(); .unwrap();
assert_eq!(contact_id, contact_id_same); assert_eq!(contact_id, contact_id_same);
assert_eq!(sth_modified, Modifier::Modified); assert_eq!(sth_modified, Modifier::Modified);
@@ -2150,11 +2223,10 @@ mod tests {
let (contact_id_same, sth_modified) = Contact::add_or_lookup( let (contact_id_same, sth_modified) = Contact::add_or_lookup(
&t, &t,
"claire2", "claire2",
"claire@example.org", ContactAddress::new("claire@example.org").unwrap(),
Origin::IncomingUnknownFrom, Origin::IncomingUnknownFrom,
) )
.await .await
.unwrap()
.unwrap(); .unwrap();
assert_eq!(contact_id, contact_id_same); assert_eq!(contact_id, contact_id_same);
assert_eq!(sth_modified, Modifier::Modified); assert_eq!(sth_modified, Modifier::Modified);
@@ -2173,29 +2245,38 @@ mod tests {
let t = TestContext::new().await; let t = TestContext::new().await;
// Incoming message from Bob. // Incoming message from Bob.
let (contact_id, sth_modified) = let (contact_id, sth_modified) = Contact::add_or_lookup(
Contact::add_or_lookup(&t, "Bob", "bob@example.org", Origin::IncomingUnknownFrom) &t,
.await? "Bob",
.unwrap(); ContactAddress::new("bob@example.org")?,
Origin::IncomingUnknownFrom,
)
.await?;
assert_eq!(sth_modified, Modifier::Created); assert_eq!(sth_modified, Modifier::Created);
let contact = Contact::load_from_db(&t, contact_id).await?; let contact = Contact::load_from_db(&t, contact_id).await?;
assert_eq!(contact.get_display_name(), "Bob"); assert_eq!(contact.get_display_name(), "Bob");
// Incoming message from someone else with "Not Bob" <bob@example.org> in the "To:" field. // Incoming message from someone else with "Not Bob" <bob@example.org> in the "To:" field.
let (contact_id_same, sth_modified) = let (contact_id_same, sth_modified) = Contact::add_or_lookup(
Contact::add_or_lookup(&t, "Not Bob", "bob@example.org", Origin::IncomingUnknownTo) &t,
.await? "Not Bob",
.unwrap(); ContactAddress::new("bob@example.org")?,
Origin::IncomingUnknownTo,
)
.await?;
assert_eq!(contact_id, contact_id_same); assert_eq!(contact_id, contact_id_same);
assert_eq!(sth_modified, Modifier::Modified); assert_eq!(sth_modified, Modifier::Modified);
let contact = Contact::load_from_db(&t, contact_id).await?; let contact = Contact::load_from_db(&t, contact_id).await?;
assert_eq!(contact.get_display_name(), "Not Bob"); assert_eq!(contact.get_display_name(), "Not Bob");
// Incoming message from Bob, changing the name back. // Incoming message from Bob, changing the name back.
let (contact_id_same, sth_modified) = let (contact_id_same, sth_modified) = Contact::add_or_lookup(
Contact::add_or_lookup(&t, "Bob", "bob@example.org", Origin::IncomingUnknownFrom) &t,
.await? "Bob",
.unwrap(); ContactAddress::new("bob@example.org")?,
Origin::IncomingUnknownFrom,
)
.await?;
assert_eq!(contact_id, contact_id_same); assert_eq!(contact_id, contact_id_same);
assert_eq!(sth_modified, Modifier::Modified); // This was None until the bugfix assert_eq!(sth_modified, Modifier::Modified); // This was None until the bugfix
let contact = Contact::load_from_db(&t, contact_id).await?; let contact = Contact::load_from_db(&t, contact_id).await?;
@@ -2218,9 +2299,14 @@ mod tests {
assert_eq!(contact.get_display_name(), "dave1"); assert_eq!(contact.get_display_name(), "dave1");
// incoming mail `From: dave2 <dave@example.org>` - this should update authname // incoming mail `From: dave2 <dave@example.org>` - this should update authname
Contact::add_or_lookup(&t, "dave2", "dave@example.org", Origin::IncomingUnknownFrom) Contact::add_or_lookup(
.await &t,
.unwrap(); "dave2",
ContactAddress::new("dave@example.org").unwrap(),
Origin::IncomingUnknownFrom,
)
.await
.unwrap();
let contact = Contact::load_from_db(&t, contact_id).await.unwrap(); let contact = Contact::load_from_db(&t, contact_id).await.unwrap();
assert_eq!(contact.get_authname(), "dave2"); assert_eq!(contact.get_authname(), "dave2");
assert_eq!(contact.get_name(), "dave1"); assert_eq!(contact.get_name(), "dave1");
@@ -2334,10 +2420,13 @@ mod tests {
let encrinfo = Contact::get_encrinfo(&alice, ContactId::DEVICE).await; let encrinfo = Contact::get_encrinfo(&alice, ContactId::DEVICE).await;
assert!(encrinfo.is_err()); assert!(encrinfo.is_err());
let (contact_bob_id, _modified) = let (contact_bob_id, _modified) = Contact::add_or_lookup(
Contact::add_or_lookup(&alice, "Bob", "bob@example.net", Origin::ManuallyCreated) &alice,
.await? "Bob",
.unwrap(); ContactAddress::new("bob@example.net")?,
Origin::ManuallyCreated,
)
.await?;
let encrinfo = Contact::get_encrinfo(&alice, contact_bob_id).await?; let encrinfo = Contact::get_encrinfo(&alice, contact_bob_id).await?;
assert_eq!(encrinfo, "No encryption"); assert_eq!(encrinfo, "No encryption");
@@ -2494,10 +2583,13 @@ CCCB 5AA9 F6E1 141C 9431
async fn test_last_seen() -> Result<()> { async fn test_last_seen() -> Result<()> {
let alice = TestContext::new_alice().await; let alice = TestContext::new_alice().await;
let (contact_id, _) = let (contact_id, _) = Contact::add_or_lookup(
Contact::add_or_lookup(&alice, "Bob", "bob@example.net", Origin::ManuallyCreated) &alice,
.await? "Bob",
.unwrap(); ContactAddress::new("bob@example.net")?,
Origin::ManuallyCreated,
)
.await?;
let contact = Contact::load_from_db(&alice, contact_id).await?; let contact = Contact::load_from_db(&alice, contact_id).await?;
assert_eq!(contact.last_seen(), 0); assert_eq!(contact.last_seen(), 0);

View File

@@ -22,7 +22,7 @@ use crate::config::Config;
use crate::constants::{ use crate::constants::{
Blocked, Chattype, ShowEmails, DC_FETCH_EXISTING_MSGS_COUNT, DC_FOLDERS_CONFIGURED_VERSION, Blocked, Chattype, ShowEmails, DC_FETCH_EXISTING_MSGS_COUNT, DC_FOLDERS_CONFIGURED_VERSION,
}; };
use crate::contact::{normalize_name, Contact, ContactId, Modifier, Origin}; use crate::contact::{normalize_name, Contact, ContactAddress, ContactId, Modifier, Origin};
use crate::context::Context; use crate::context::Context;
use crate::events::EventType; use crate::events::EventType;
use crate::headerdef::{HeaderDef, HeaderDefMap}; use crate::headerdef::{HeaderDef, HeaderDefMap};
@@ -2375,23 +2375,23 @@ async fn add_all_recipients_as_contacts(
.map(|s| normalize_name(s)) .map(|s| normalize_name(s))
.unwrap_or_default(); .unwrap_or_default();
match Contact::add_or_lookup( match ContactAddress::new(&recipient.addr) {
context, Err(err) => warn!(
&display_name_normalized, context,
&recipient.addr, "Could not add contact for recipient with address {:?}: {:#}", recipient.addr, err
Origin::OutgoingTo, ),
) Ok(recipient_addr) => {
.await? let (_, modified) = Contact::add_or_lookup(
{ context,
Some((_, modified)) => { &display_name_normalized,
recipient_addr,
Origin::OutgoingTo,
)
.await?;
if modified != Modifier::None { if modified != Modifier::None {
any_modified = true; any_modified = true;
} }
} }
None => warn!(
context,
"Could not add contact for recipient with address {:?}", recipient.addr
),
} }
} }
if any_modified { if any_modified {

View File

@@ -1490,7 +1490,7 @@ mod tests {
ProtectionStatus, ProtectionStatus,
}; };
use crate::chatlist::Chatlist; use crate::chatlist::Chatlist;
use crate::contact::Origin; use crate::contact::{ContactAddress, Origin};
use crate::mimeparser::MimeMessage; use crate::mimeparser::MimeMessage;
use crate::receive_imf::receive_imf; use crate::receive_imf::receive_imf;
use crate::test_utils::{get_chat_msg, TestContext}; use crate::test_utils::{get_chat_msg, TestContext};
@@ -1817,12 +1817,15 @@ mod tests {
} }
async fn first_subject_str(t: TestContext) -> String { async fn first_subject_str(t: TestContext) -> String {
let contact_id = let contact_id = Contact::add_or_lookup(
Contact::add_or_lookup(&t, "Dave", "dave@example.com", Origin::ManuallyCreated) &t,
.await "Dave",
.unwrap() ContactAddress::new("dave@example.com").unwrap(),
.unwrap() Origin::ManuallyCreated,
.0; )
.await
.unwrap()
.0;
let chat_id = ChatId::create_for_contact(&t, contact_id).await.unwrap(); let chat_id = ChatId::create_for_contact(&t, contact_id).await.unwrap();

View File

@@ -9,7 +9,7 @@ use crate::aheader::{Aheader, EncryptPreference};
use crate::chat::{self, Chat}; use crate::chat::{self, Chat};
use crate::chatlist::Chatlist; use crate::chatlist::Chatlist;
use crate::constants::Chattype; use crate::constants::Chattype;
use crate::contact::{addr_cmp, Contact, Origin}; use crate::contact::{addr_cmp, Contact, ContactAddress, Origin};
use crate::context::Context; use crate::context::Context;
use crate::events::EventType; use crate::events::EventType;
use crate::key::{DcKey, Fingerprint, SignedPublicKey}; use crate::key::{DcKey, Fingerprint, SignedPublicKey};
@@ -542,16 +542,30 @@ impl Peerstate {
if (chat.typ == Chattype::Group && chat.is_protected()) if (chat.typ == Chattype::Group && chat.is_protected())
|| chat.typ == Chattype::Broadcast || chat.typ == Chattype::Broadcast
{ {
if let Some((new_contact_id, _)) = match ContactAddress::new(new_addr) {
Contact::add_or_lookup(context, "", new_addr, Origin::IncomingUnknownFrom) Ok(new_addr) => {
.await? let (new_contact_id, _) = Contact::add_or_lookup(
{ context,
chat::remove_from_chat_contacts_table(context, *chat_id, contact_id) "",
.await?; new_addr,
chat::add_to_chat_contacts_table(context, *chat_id, &[new_contact_id]) Origin::IncomingUnknownFrom,
)
.await?; .await?;
chat::remove_from_chat_contacts_table(context, *chat_id, contact_id)
.await?;
chat::add_to_chat_contacts_table(context, *chat_id, &[new_contact_id])
.await?;
context.emit_event(EventType::ChatModified(*chat_id)); context.emit_event(EventType::ChatModified(*chat_id));
}
Err(err) => {
warn!(
context,
"New address {:?} is not vaild, not doing AEAP: {:#}.",
new_addr,
err
)
}
} }
} }
} }

View File

@@ -14,7 +14,9 @@ use std::collections::BTreeMap;
use crate::chat::{self, get_chat_id_by_grpid, ChatIdBlocked}; use crate::chat::{self, get_chat_id_by_grpid, ChatIdBlocked};
use crate::config::Config; use crate::config::Config;
use crate::constants::Blocked; use crate::constants::Blocked;
use crate::contact::{addr_normalize, may_be_valid_addr, Contact, ContactId, Origin}; use crate::contact::{
addr_normalize, may_be_valid_addr, Contact, ContactAddress, ContactId, Origin,
};
use crate::context::Context; use crate::context::Context;
use crate::key::Fingerprint; use crate::key::Fingerprint;
use crate::message::Message; use crate::message::Message;
@@ -221,19 +223,14 @@ async fn decode_openpgp(context: &Context, qr: &str) -> Result<Qr> {
.context("Can't load peerstate")?; .context("Can't load peerstate")?;
if let (Some(addr), Some(invitenumber), Some(authcode)) = (&addr, invitenumber, authcode) { if let (Some(addr), Some(invitenumber), Some(authcode)) = (&addr, invitenumber, authcode) {
let addr = ContactAddress::new(addr)?;
let (contact_id, _) = Contact::add_or_lookup(context, &name, addr, Origin::UnhandledQrScan) let (contact_id, _) = Contact::add_or_lookup(context, &name, addr, Origin::UnhandledQrScan)
.await .await
.with_context(|| format!("failed to add or lookup contact for address {:?}", addr))? .with_context(|| format!("failed to add or lookup contact for address {:?}", addr))?;
.with_context(|| {
format!(
"do not want to lookup contact for invalid address {:?}",
addr
)
})?;
if let (Some(grpid), Some(grpname)) = (grpid, grpname) { if let (Some(grpid), Some(grpname)) = (grpid, grpname) {
if context if context
.is_self_addr(addr) .is_self_addr(&addr)
.await .await
.with_context(|| format!("can't check if address {:?} is our address", addr))? .with_context(|| format!("can't check if address {:?} is our address", addr))?
{ {
@@ -266,7 +263,7 @@ async fn decode_openpgp(context: &Context, qr: &str) -> Result<Qr> {
authcode, authcode,
}) })
} }
} else if context.is_self_addr(addr).await? { } else if context.is_self_addr(&addr).await? {
if token::exists(context, token::Namespace::InviteNumber, &invitenumber).await { if token::exists(context, token::Namespace::InviteNumber, &invitenumber).await {
Ok(Qr::WithdrawVerifyContact { Ok(Qr::WithdrawVerifyContact {
contact_id, contact_id,
@@ -292,13 +289,11 @@ async fn decode_openpgp(context: &Context, qr: &str) -> Result<Qr> {
} }
} else if let Some(addr) = addr { } else if let Some(addr) = addr {
if let Some(peerstate) = peerstate { if let Some(peerstate) = peerstate {
let peerstate_addr = ContactAddress::new(&peerstate.addr)?;
let (contact_id, _) = let (contact_id, _) =
Contact::add_or_lookup(context, &name, &peerstate.addr, Origin::UnhandledQrScan) Contact::add_or_lookup(context, &name, peerstate_addr, Origin::UnhandledQrScan)
.await .await
.context("add_or_lookup")? .context("add_or_lookup")?;
.with_context(|| {
format!("Not looking up contact for address {:?}", &peerstate.addr)
})?;
let chat = ChatIdBlocked::get_for_contact(context, contact_id, Blocked::Request) let chat = ChatIdBlocked::get_for_contact(context, contact_id, Blocked::Request)
.await .await
.context("Failed to create (new) chat for contact")?; .context("Failed to create (new) chat for contact")?;
@@ -538,11 +533,11 @@ async fn decode_mailto(context: &Context, qr: &str) -> Result<Qr> {
}; };
let addr = normalize_address(addr)?; let addr = normalize_address(addr)?;
let name = "".to_string(); let name = "";
Qr::from_address( Qr::from_address(
context, context,
name, name,
addr, &addr,
if draft.is_empty() { None } else { Some(draft) }, if draft.is_empty() { None } else { Some(draft) },
) )
.await .await
@@ -562,8 +557,8 @@ async fn decode_smtp(context: &Context, qr: &str) -> Result<Qr> {
}; };
let addr = normalize_address(addr)?; let addr = normalize_address(addr)?;
let name = "".to_string(); let name = "";
Qr::from_address(context, name, addr, None).await Qr::from_address(context, name, &addr, None).await
} }
/// Extract address for the matmsg scheme. /// Extract address for the matmsg scheme.
@@ -587,8 +582,8 @@ async fn decode_matmsg(context: &Context, qr: &str) -> Result<Qr> {
}; };
let addr = normalize_address(addr)?; let addr = normalize_address(addr)?;
let name = "".to_string(); let name = "";
Qr::from_address(context, name, addr, None).await Qr::from_address(context, name, &addr, None).await
} }
static VCARD_NAME_RE: Lazy<regex::Regex> = static VCARD_NAME_RE: Lazy<regex::Regex> =
@@ -617,20 +612,19 @@ async fn decode_vcard(context: &Context, qr: &str) -> Result<Qr> {
bail!("Bad e-mail address"); bail!("Bad e-mail address");
}; };
Qr::from_address(context, name, addr, None).await Qr::from_address(context, &name, &addr, None).await
} }
impl Qr { impl Qr {
pub async fn from_address( pub async fn from_address(
context: &Context, context: &Context,
name: String, name: &str,
addr: String, addr: &str,
draft: Option<String>, draft: Option<String>,
) -> Result<Self> { ) -> Result<Self> {
let addr = ContactAddress::new(addr)?;
let (contact_id, _) = let (contact_id, _) =
Contact::add_or_lookup(context, &name, &addr, Origin::UnhandledQrScan) Contact::add_or_lookup(context, name, addr, Origin::UnhandledQrScan).await?;
.await?
.context("QR code does not contain a valid address")?;
Ok(Qr::Addr { contact_id, draft }) Ok(Qr::Addr { contact_id, draft })
} }
} }

View File

@@ -286,11 +286,11 @@ pub async fn get_msg_reactions(context: &Context, msg_id: MsgId) -> Result<React
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::chat::get_chat_msgs;
use crate::chat::get_chat_msgs;
use crate::config::Config; use crate::config::Config;
use crate::constants::DC_CHAT_ID_TRASH; use crate::constants::DC_CHAT_ID_TRASH;
use crate::contact::{Contact, Origin}; use crate::contact::{Contact, ContactAddress, Origin};
use crate::download::DownloadState; use crate::download::DownloadState;
use crate::message::MessageState; use crate::message::MessageState;
use crate::receive_imf::{receive_imf, receive_imf_inner}; use crate::receive_imf::{receive_imf, receive_imf_inner};
@@ -366,10 +366,14 @@ Can we chat at 1pm pacific, today?"
let contacts = reactions.contacts(); let contacts = reactions.contacts();
assert_eq!(contacts.len(), 0); assert_eq!(contacts.len(), 0);
let bob_id = Contact::add_or_lookup(&alice, "", "bob@example.net", Origin::ManuallyCreated) let bob_id = Contact::add_or_lookup(
.await? &alice,
.unwrap() "",
.0; ContactAddress::new("bob@example.net")?,
Origin::ManuallyCreated,
)
.await?
.0;
let bob_reaction = reactions.get(bob_id); let bob_reaction = reactions.get(bob_id);
assert!(bob_reaction.is_empty()); // Bob has not reacted to message yet. assert!(bob_reaction.is_empty()); // Bob has not reacted to message yet.

View File

@@ -16,7 +16,7 @@ use crate::chat::{self, Chat, ChatId, ChatIdBlocked, ProtectionStatus};
use crate::config::Config; use crate::config::Config;
use crate::constants::{Blocked, Chattype, ShowEmails, DC_CHAT_ID_TRASH}; use crate::constants::{Blocked, Chattype, ShowEmails, DC_CHAT_ID_TRASH};
use crate::contact::{ use crate::contact::{
may_be_valid_addr, normalize_name, Contact, ContactId, Origin, VerifiedStatus, may_be_valid_addr, normalize_name, Contact, ContactAddress, ContactId, Origin, VerifiedStatus,
}; };
use crate::context::Context; use crate::context::Context;
use crate::download::DownloadState; use crate::download::DownloadState;
@@ -378,6 +378,8 @@ pub(crate) async fn receive_imf_inner(
/// Also returns whether it is blocked or not and its origin. /// Also returns whether it is blocked or not and its origin.
/// ///
/// * `prevent_rename`: passed through to `add_or_lookup_contacts_by_address_list()` /// * `prevent_rename`: passed through to `add_or_lookup_contacts_by_address_list()`
///
/// Returns `None` if From field does not contain a valid contact address.
pub async fn from_field_to_contact_id( pub async fn from_field_to_contact_id(
context: &Context, context: &Context,
from: &SingleInfo, from: &SingleInfo,
@@ -388,19 +390,24 @@ pub async fn from_field_to_contact_id(
} else { } else {
from.display_name.as_deref() from.display_name.as_deref()
}; };
let from_id = if let Some(from_id) = add_or_lookup_contact_by_addr( let from_addr = match ContactAddress::new(&from.addr) {
Ok(from_addr) => from_addr,
Err(err) => {
warn!(
context,
"Cannot create a contact for the given From field: {:#}.", err
);
return Ok(None);
}
};
let from_id = add_or_lookup_contact_by_addr(
context, context,
display_name, display_name,
&from.addr, from_addr,
Origin::IncomingUnknownFrom, Origin::IncomingUnknownFrom,
) )
.await .await?;
.context("add_or_lookup_contact_by_addr")?
{
from_id
} else {
return Ok(None);
};
if from_id == ContactId::SELF { if from_id == ContactId::SELF {
Ok(Some((ContactId::SELF, false, Origin::OutgoingBcc))) Ok(Some((ContactId::SELF, false, Origin::OutgoingBcc)))
@@ -1943,11 +1950,15 @@ async fn apply_mailinglist_changes(
} }
let listid = &chat.grpid; let listid = &chat.grpid;
let contact_id = let list_post = match ContactAddress::new(list_post) {
match Contact::add_or_lookup(context, "", list_post, Origin::Hidden).await? { Ok(list_post) => list_post,
Some((contact_id, _)) => contact_id, Err(err) => {
None => return Ok(()), warn!(context, "Invalid List-Post: {:#}.", err);
}; return Ok(());
}
};
let (contact_id, _) =
Contact::add_or_lookup(context, "", list_post, Origin::Hidden).await?;
let mut contact = Contact::load_from_db(context, contact_id).await?; let mut contact = Contact::load_from_db(context, contact_id).await?;
if contact.param.get(Param::ListId) != Some(listid) { if contact.param.get(Param::ListId) != Some(listid) {
contact.param.set(Param::ListId, listid); contact.param.set(Param::ListId, listid);
@@ -1955,7 +1966,7 @@ async fn apply_mailinglist_changes(
} }
if let Some(old_list_post) = chat.param.get(Param::ListPost) { if let Some(old_list_post) = chat.param.get(Param::ListPost) {
if list_post != old_list_post { if list_post.as_ref() != old_list_post {
// Apparently the mailing list is using a different List-Post header in each message. // Apparently the mailing list is using a different List-Post header in each message.
// Make the mailing list read-only because we would't know which message the user wants to reply to. // Make the mailing list read-only because we would't know which message the user wants to reply to.
chat.param.remove(Param::ListPost); chat.param.remove(Param::ListPost);
@@ -2286,9 +2297,9 @@ async fn add_or_lookup_contacts_by_address_list(
continue; continue;
} }
let display_name = info.display_name.as_deref(); let display_name = info.display_name.as_deref();
if let Some(contact_id) = if let Ok(addr) = ContactAddress::new(addr) {
add_or_lookup_contact_by_addr(context, display_name, addr, origin).await? let contact_id =
{ add_or_lookup_contact_by_addr(context, display_name, addr, origin).await?;
contact_ids.insert(contact_id); contact_ids.insert(contact_id);
} else { } else {
warn!(context, "Contact with address {:?} cannot exist.", addr); warn!(context, "Contact with address {:?} cannot exist.", addr);
@@ -2299,26 +2310,20 @@ async fn add_or_lookup_contacts_by_address_list(
} }
/// Add contacts to database on receiving messages. /// Add contacts to database on receiving messages.
///
/// Returns `None` if the address can't be a valid email address.
async fn add_or_lookup_contact_by_addr( async fn add_or_lookup_contact_by_addr(
context: &Context, context: &Context,
display_name: Option<&str>, display_name: Option<&str>,
addr: &str, addr: ContactAddress<'_>,
origin: Origin, origin: Origin,
) -> Result<Option<ContactId>> { ) -> Result<ContactId> {
if context.is_self_addr(addr).await? { if context.is_self_addr(&addr).await? {
return Ok(Some(ContactId::SELF)); return Ok(ContactId::SELF);
} }
let display_name_normalized = display_name.map(normalize_name).unwrap_or_default(); let display_name_normalized = display_name.map(normalize_name).unwrap_or_default();
if let Some((contact_id, _modified)) = let (contact_id, _modified) =
Contact::add_or_lookup(context, &display_name_normalized, addr, origin).await? Contact::add_or_lookup(context, &display_name_normalized, addr, origin).await?;
{ Ok(contact_id)
Ok(Some(contact_id))
} else {
Ok(None)
}
} }
#[cfg(test)] #[cfg(test)]

View File

@@ -425,12 +425,15 @@ async fn test_escaped_recipients() {
.await .await
.unwrap(); .unwrap();
let carl_contact_id = let carl_contact_id = Contact::add_or_lookup(
Contact::add_or_lookup(&t, "Carl", "carl@host.tld", Origin::IncomingUnknownFrom) &t,
.await "Carl",
.unwrap() ContactAddress::new("carl@host.tld").unwrap(),
.unwrap() Origin::IncomingUnknownFrom,
.0; )
.await
.unwrap()
.0;
receive_imf( receive_imf(
&t, &t,
@@ -468,12 +471,15 @@ async fn test_cc_to_contact() {
.await .await
.unwrap(); .unwrap();
let carl_contact_id = let carl_contact_id = Contact::add_or_lookup(
Contact::add_or_lookup(&t, "garabage", "carl@host.tld", Origin::IncomingUnknownFrom) &t,
.await "garabage",
.unwrap() ContactAddress::new("carl@host.tld").unwrap(),
.unwrap() Origin::IncomingUnknownFrom,
.0; )
.await
.unwrap()
.0;
receive_imf( receive_imf(
&t, &t,
@@ -2056,11 +2062,10 @@ async fn test_duplicate_message() -> Result<()> {
let bob_contact_id = Contact::add_or_lookup( let bob_contact_id = Contact::add_or_lookup(
&alice, &alice,
"Bob", "Bob",
"bob@example.org", ContactAddress::new("bob@example.org").unwrap(),
Origin::IncomingUnknownFrom, Origin::IncomingUnknownFrom,
) )
.await? .await?
.unwrap()
.0; .0;
let first_message = b"Received: from [127.0.0.1] let first_message = b"Received: from [127.0.0.1]
@@ -2112,10 +2117,14 @@ Second signature";
async fn test_ignore_footer_status_from_mailinglist() -> Result<()> { async fn test_ignore_footer_status_from_mailinglist() -> Result<()> {
let t = TestContext::new_alice().await; let t = TestContext::new_alice().await;
t.set_config(Config::ShowEmails, Some("2")).await?; t.set_config(Config::ShowEmails, Some("2")).await?;
let bob_id = Contact::add_or_lookup(&t, "", "bob@example.net", Origin::IncomingUnknownCc) let bob_id = Contact::add_or_lookup(
.await? &t,
.unwrap() "",
.0; ContactAddress::new("bob@example.net").unwrap(),
Origin::IncomingUnknownCc,
)
.await?
.0;
let bob = Contact::load_from_db(&t, bob_id).await?; let bob = Contact::load_from_db(&t, bob_id).await?;
assert_eq!(bob.get_status(), ""); assert_eq!(bob.get_status(), "");
assert_eq!(Chatlist::try_load(&t, 0, None, None).await?.len(), 0); assert_eq!(Chatlist::try_load(&t, 0, None, None).await?.len(), 0);
@@ -2527,14 +2536,8 @@ Second thread."#;
// Alice adds Fiona to both ad hoc groups. // Alice adds Fiona to both ad hoc groups.
let fiona = TestContext::new_fiona().await; let fiona = TestContext::new_fiona().await;
let (alice_fiona_contact_id, _) = Contact::add_or_lookup( let alice_fiona_contact = alice.add_or_lookup_contact(&fiona).await;
&alice, let alice_fiona_contact_id = alice_fiona_contact.id;
"Fiona",
"fiona@example.net",
Origin::IncomingUnknownTo,
)
.await?
.unwrap();
chat::add_contact_to_chat(&alice, alice_first_msg.chat_id, alice_fiona_contact_id).await?; chat::add_contact_to_chat(&alice, alice_first_msg.chat_id, alice_fiona_contact_id).await?;
let alice_first_invite = alice.pop_sent_msg().await; let alice_first_invite = alice.pop_sent_msg().await;

View File

@@ -710,6 +710,7 @@ mod tests {
use crate::chat::ProtectionStatus; use crate::chat::ProtectionStatus;
use crate::chatlist::Chatlist; use crate::chatlist::Chatlist;
use crate::constants::{Chattype, DC_GCM_ADDDAYMARKER}; use crate::constants::{Chattype, DC_GCM_ADDDAYMARKER};
use crate::contact::ContactAddress;
use crate::peerstate::Peerstate; use crate::peerstate::Peerstate;
use crate::receive_imf::receive_imf; use crate::receive_imf::receive_imf;
use crate::test_utils::{TestContext, TestContextManager}; use crate::test_utils::{TestContext, TestContextManager};
@@ -1003,11 +1004,10 @@ mod tests {
let (contact_bob_id, _modified) = Contact::add_or_lookup( let (contact_bob_id, _modified) = Contact::add_or_lookup(
&alice.ctx, &alice.ctx,
"Bob", "Bob",
"bob@example.net", ContactAddress::new("bob@example.net")?,
Origin::ManuallyCreated, Origin::ManuallyCreated,
) )
.await? .await?;
.unwrap();
let contact_bob = Contact::load_from_db(&alice.ctx, contact_bob_id).await?; let contact_bob = Contact::load_from_db(&alice.ctx, contact_bob_id).await?;
assert_eq!( assert_eq!(
contact_bob.is_verified(&alice.ctx).await?, contact_bob.is_verified(&alice.ctx).await?,

View File

@@ -23,7 +23,7 @@ use crate::chatlist::Chatlist;
use crate::config::Config; use crate::config::Config;
use crate::constants::Chattype; use crate::constants::Chattype;
use crate::constants::{DC_GCL_NO_SPECIALS, DC_GCM_ADDDAYMARKER, DC_MSG_ID_DAYMARKER}; use crate::constants::{DC_GCL_NO_SPECIALS, DC_GCM_ADDDAYMARKER, DC_MSG_ID_DAYMARKER};
use crate::contact::{Contact, ContactId, Modifier, Origin}; use crate::contact::{Contact, ContactAddress, ContactId, Modifier, Origin};
use crate::context::Context; use crate::context::Context;
use crate::events::{Event, EventType, Events}; use crate::events::{Event, EventType, Events};
use crate::key::{self, DcKey, KeyPair, KeyPairUse}; use crate::key::{self, DcKey, KeyPair, KeyPairUse};
@@ -523,14 +523,14 @@ impl TestContext {
.await .await
.unwrap_or_default() .unwrap_or_default()
.unwrap_or_default(); .unwrap_or_default();
let addr = other.ctx.get_primary_self_addr().await.unwrap(); let primary_self_addr = other.ctx.get_primary_self_addr().await.unwrap();
let addr = ContactAddress::new(&primary_self_addr).unwrap();
// MailinglistAddress is the lowest allowed origin, we'd prefer to not modify the // MailinglistAddress is the lowest allowed origin, we'd prefer to not modify the
// origin when creating this contact. // origin when creating this contact.
let (contact_id, modified) = let (contact_id, modified) =
Contact::add_or_lookup(self, &name, &addr, Origin::MailinglistAddress) Contact::add_or_lookup(self, &name, addr, Origin::MailinglistAddress)
.await .await
.expect("add_or_lookup") .expect("add_or_lookup");
.unwrap_or_else(|| panic!("contact with address {:?} cannot be created", &addr));
match modified { match modified {
Modifier::None => (), Modifier::None => (),
Modifier::Modified => warn!(&self.ctx, "Contact {} modified by TestContext", &addr), Modifier::Modified => warn!(&self.ctx, "Contact {} modified by TestContext", &addr),