deltachat/
contact.rs

1//! Contacts module
2
3use std::cmp::{min, Reverse};
4use std::collections::{BinaryHeap, HashSet};
5use std::fmt;
6use std::path::{Path, PathBuf};
7use std::time::UNIX_EPOCH;
8
9use anyhow::{bail, ensure, Context as _, Result};
10use async_channel::{self as channel, Receiver, Sender};
11use base64::Engine as _;
12pub use deltachat_contact_tools::may_be_valid_addr;
13use deltachat_contact_tools::{
14    self as contact_tools, addr_cmp, addr_normalize, sanitize_name, sanitize_name_and_addr,
15    ContactAddress, VcardContact,
16};
17use deltachat_derive::{FromSql, ToSql};
18use rusqlite::OptionalExtension;
19use serde::{Deserialize, Serialize};
20use tokio::task;
21use tokio::time::{timeout, Duration};
22
23use crate::aheader::{Aheader, EncryptPreference};
24use crate::blob::BlobObject;
25use crate::chat::{ChatId, ChatIdBlocked, ProtectionStatus};
26use crate::color::str_to_color;
27use crate::config::Config;
28use crate::constants::{Blocked, Chattype, DC_GCL_ADD_SELF};
29use crate::context::Context;
30use crate::events::EventType;
31use crate::key::{load_self_public_key, DcKey, SignedPublicKey};
32use crate::log::LogExt;
33use crate::message::MessageState;
34use crate::mimeparser::AvatarAction;
35use crate::param::{Param, Params};
36use crate::peerstate::Peerstate;
37use crate::sync::{self, Sync::*};
38use crate::tools::{duration_to_str, get_abs_path, smeared_time, time, SystemTime};
39use crate::{chat, chatlist_events, stock_str};
40
41/// Time during which a contact is considered as seen recently.
42const SEEN_RECENTLY_SECONDS: i64 = 600;
43
44/// Contact ID, including reserved IDs.
45///
46/// Some contact IDs are reserved to identify special contacts.  This
47/// type can represent both the special as well as normal contacts.
48#[derive(
49    Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
50)]
51pub struct ContactId(u32);
52
53impl ContactId {
54    /// Undefined contact. Used as a placeholder for trashed messages.
55    pub const UNDEFINED: ContactId = ContactId::new(0);
56
57    /// The owner of the account.
58    ///
59    /// The email-address is set by `set_config` using "addr".
60    pub const SELF: ContactId = ContactId::new(1);
61
62    /// ID of the contact for info messages.
63    pub const INFO: ContactId = ContactId::new(2);
64
65    /// ID of the contact for device messages.
66    pub const DEVICE: ContactId = ContactId::new(5);
67    pub(crate) const LAST_SPECIAL: ContactId = ContactId::new(9);
68
69    /// Address to go with [`ContactId::DEVICE`].
70    ///
71    /// This is used by APIs which need to return an email address for this contact.
72    pub const DEVICE_ADDR: &'static str = "device@localhost";
73
74    /// Creates a new [`ContactId`].
75    pub const fn new(id: u32) -> ContactId {
76        ContactId(id)
77    }
78
79    /// Whether this is a special [`ContactId`].
80    ///
81    /// Some [`ContactId`]s are reserved for special contacts like [`ContactId::SELF`],
82    /// [`ContactId::INFO`] and [`ContactId::DEVICE`].  This function indicates whether this
83    /// [`ContactId`] is any of the reserved special [`ContactId`]s (`true`) or whether it
84    /// is the [`ContactId`] of a real contact (`false`).
85    pub fn is_special(&self) -> bool {
86        self.0 <= Self::LAST_SPECIAL.0
87    }
88
89    /// Numerical representation of the [`ContactId`].
90    ///
91    /// Each contact ID has a unique numerical representation which is used in the database
92    /// (via [`rusqlite::ToSql`]) and also for FFI purposes.  In Rust code you should never
93    /// need to use this directly.
94    pub const fn to_u32(&self) -> u32 {
95        self.0
96    }
97
98    /// Sets display name for existing contact.
99    ///
100    /// Display name may be an empty string,
101    /// in which case the name displayed in the UI
102    /// for this contact will switch to the
103    /// contact's authorized name.
104    pub async fn set_name(self, context: &Context, name: &str) -> Result<()> {
105        let addr = context
106            .sql
107            .transaction(|transaction| {
108                let is_changed = transaction.execute(
109                    "UPDATE contacts SET name=?1 WHERE id=?2 AND name!=?1",
110                    (name, self),
111                )? > 0;
112                if is_changed {
113                    update_chat_names(context, transaction, self)?;
114                    let addr = transaction.query_row(
115                        "SELECT addr FROM contacts WHERE id=?",
116                        (self,),
117                        |row| {
118                            let addr: String = row.get(0)?;
119                            Ok(addr)
120                        },
121                    )?;
122                    Ok(Some(addr))
123                } else {
124                    Ok(None)
125                }
126            })
127            .await?;
128
129        if let Some(addr) = addr {
130            chat::sync(
131                context,
132                chat::SyncId::ContactAddr(addr.to_string()),
133                chat::SyncAction::Rename(name.to_string()),
134            )
135            .await
136            .log_err(context)
137            .ok();
138        }
139        Ok(())
140    }
141
142    /// Mark contact as bot.
143    pub(crate) async fn mark_bot(&self, context: &Context, is_bot: bool) -> Result<()> {
144        context
145            .sql
146            .execute("UPDATE contacts SET is_bot=? WHERE id=?;", (is_bot, self.0))
147            .await?;
148        Ok(())
149    }
150
151    /// Reset gossip timestamp in all chats with this contact.
152    pub(crate) async fn regossip_keys(&self, context: &Context) -> Result<()> {
153        context
154            .sql
155            .execute(
156                "UPDATE chats
157                 SET gossiped_timestamp=0
158                 WHERE EXISTS (SELECT 1 FROM chats_contacts
159                               WHERE chats_contacts.chat_id=chats.id
160                               AND chats_contacts.contact_id=?
161                               AND chats_contacts.add_timestamp >= chats_contacts.remove_timestamp)",
162                (self,),
163            )
164            .await?;
165        Ok(())
166    }
167
168    /// Updates the origin of the contacts, but only if `origin` is higher than the current one.
169    pub(crate) async fn scaleup_origin(
170        context: &Context,
171        ids: &[Self],
172        origin: Origin,
173    ) -> Result<()> {
174        context
175            .sql
176            .transaction(|transaction| {
177                let mut stmt = transaction
178                    .prepare("UPDATE contacts SET origin=?1 WHERE id = ?2 AND origin < ?1")?;
179                for id in ids {
180                    stmt.execute((origin, id))?;
181                }
182                Ok(())
183            })
184            .await?;
185        Ok(())
186    }
187
188    /// Returns contact address.
189    pub async fn addr(&self, context: &Context) -> Result<String> {
190        let addr = context
191            .sql
192            .query_row("SELECT addr FROM contacts WHERE id=?", (self,), |row| {
193                let addr: String = row.get(0)?;
194                Ok(addr)
195            })
196            .await?;
197        Ok(addr)
198    }
199
200    /// Resets encryption with the contact.
201    ///
202    /// Effect is similar to receiving a message without Autocrypt header
203    /// from the contact, but this action is triggered manually by the user.
204    ///
205    /// For example, this will result in sending the next message
206    /// to 1:1 chat unencrypted, but will not remove existing verified keys.
207    pub async fn reset_encryption(self, context: &Context) -> Result<()> {
208        let now = time();
209
210        let addr = self.addr(context).await?;
211        if let Some(mut peerstate) = Peerstate::from_addr(context, &addr).await? {
212            peerstate.degrade_encryption(now);
213            peerstate.save_to_db(&context.sql).await?;
214        }
215
216        // Reset 1:1 chat protection.
217        if let Some(chat_id) = ChatId::lookup_by_contact(context, self).await? {
218            chat_id
219                .set_protection(context, ProtectionStatus::Unprotected, now, Some(self))
220                .await?;
221        }
222        Ok(())
223    }
224}
225
226impl fmt::Display for ContactId {
227    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
228        if *self == ContactId::UNDEFINED {
229            write!(f, "Contact#Undefined")
230        } else if *self == ContactId::SELF {
231            write!(f, "Contact#Self")
232        } else if *self == ContactId::INFO {
233            write!(f, "Contact#Info")
234        } else if *self == ContactId::DEVICE {
235            write!(f, "Contact#Device")
236        } else if self.is_special() {
237            write!(f, "Contact#Special{}", self.0)
238        } else {
239            write!(f, "Contact#{}", self.0)
240        }
241    }
242}
243
244/// Allow converting [`ContactId`] to an SQLite type.
245impl rusqlite::types::ToSql for ContactId {
246    fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput> {
247        let val = rusqlite::types::Value::Integer(i64::from(self.0));
248        let out = rusqlite::types::ToSqlOutput::Owned(val);
249        Ok(out)
250    }
251}
252
253/// Allow converting an SQLite integer directly into [`ContactId`].
254impl rusqlite::types::FromSql for ContactId {
255    fn column_result(value: rusqlite::types::ValueRef) -> rusqlite::types::FromSqlResult<Self> {
256        i64::column_result(value).and_then(|val| {
257            val.try_into()
258                .map(ContactId::new)
259                .map_err(|_| rusqlite::types::FromSqlError::OutOfRange(val))
260        })
261    }
262}
263
264/// Returns a vCard containing contacts with the given ids.
265pub async fn make_vcard(context: &Context, contacts: &[ContactId]) -> Result<String> {
266    let now = time();
267    let mut vcard_contacts = Vec::with_capacity(contacts.len());
268    for id in contacts {
269        let c = Contact::get_by_id(context, *id).await?;
270        let key = match *id {
271            ContactId::SELF => Some(load_self_public_key(context).await?),
272            _ => Peerstate::from_addr(context, &c.addr)
273                .await?
274                .and_then(|peerstate| peerstate.take_key(false)),
275        };
276        let key = key.map(|k| k.to_base64());
277        let profile_image = match c.get_profile_image(context).await? {
278            None => None,
279            Some(path) => tokio::fs::read(path)
280                .await
281                .log_err(context)
282                .ok()
283                .map(|data| base64::engine::general_purpose::STANDARD.encode(data)),
284        };
285        vcard_contacts.push(VcardContact {
286            addr: c.addr,
287            authname: c.authname,
288            key,
289            profile_image,
290            // Use the current time to not reveal our or contact's online time.
291            timestamp: Ok(now),
292        });
293    }
294
295    // XXX: newline at the end of vCard is trimmed
296    // for compatibility with core <=1.155.3
297    // Newer core should be able to deal with
298    // trailing CRLF as the fix
299    // <https://github.com/deltachat/deltachat-core-rust/pull/6522>
300    // is merged.
301    Ok(contact_tools::make_vcard(&vcard_contacts)
302        .trim_end()
303        .to_string())
304}
305
306/// Imports contacts from the given vCard.
307///
308/// Returns the ids of successfully processed contacts in the order they appear in `vcard`,
309/// regardless of whether they are just created, modified or left untouched.
310pub async fn import_vcard(context: &Context, vcard: &str) -> Result<Vec<ContactId>> {
311    let contacts = contact_tools::parse_vcard(vcard);
312    let mut contact_ids = Vec::with_capacity(contacts.len());
313    for c in &contacts {
314        let Ok(id) = import_vcard_contact(context, c)
315            .await
316            .with_context(|| format!("import_vcard_contact() failed for {}", c.addr))
317            .log_err(context)
318        else {
319            continue;
320        };
321        contact_ids.push(id);
322    }
323    Ok(contact_ids)
324}
325
326async fn import_vcard_contact(context: &Context, contact: &VcardContact) -> Result<ContactId> {
327    let addr = ContactAddress::new(&contact.addr).context("Invalid address")?;
328    // Importing a vCard is also an explicit user action like creating a chat with the contact. We
329    // mustn't use `Origin::AddressBook` here because the vCard may be created not by us, also we
330    // want `contact.authname` to be saved as the authname and not a locally given name.
331    let origin = Origin::CreateChat;
332    let (id, modified) =
333        match Contact::add_or_lookup(context, &contact.authname, &addr, origin).await {
334            Err(e) => return Err(e).context("Contact::add_or_lookup() failed"),
335            Ok((ContactId::SELF, _)) => return Ok(ContactId::SELF),
336            Ok(val) => val,
337        };
338    if modified != Modifier::None {
339        context.emit_event(EventType::ContactsChanged(Some(id)));
340    }
341    let key = contact.key.as_ref().and_then(|k| {
342        SignedPublicKey::from_base64(k)
343            .with_context(|| {
344                format!(
345                    "import_vcard_contact: Cannot decode key for {}",
346                    contact.addr
347                )
348            })
349            .log_err(context)
350            .ok()
351    });
352    if let Some(public_key) = key {
353        let timestamp = contact
354            .timestamp
355            .as_ref()
356            .map_or(0, |&t| min(t, smeared_time(context)));
357        let aheader = Aheader {
358            addr: contact.addr.clone(),
359            public_key,
360            prefer_encrypt: EncryptPreference::Mutual,
361        };
362        let peerstate = match Peerstate::from_addr(context, &aheader.addr).await {
363            Err(e) => {
364                warn!(
365                    context,
366                    "import_vcard_contact: Cannot create peerstate from {}: {e:#}.", contact.addr
367                );
368                return Ok(id);
369            }
370            Ok(p) => p,
371        };
372        let peerstate = if let Some(mut p) = peerstate {
373            p.apply_gossip(&aheader, timestamp);
374            p
375        } else {
376            Peerstate::from_gossip(&aheader, timestamp)
377        };
378        if let Err(e) = peerstate.save_to_db(&context.sql).await {
379            warn!(
380                context,
381                "import_vcard_contact: Could not save peerstate for {}: {e:#}.", contact.addr
382            );
383            return Ok(id);
384        }
385        if let Err(e) = peerstate
386            .handle_fingerprint_change(context, timestamp)
387            .await
388        {
389            warn!(
390                context,
391                "import_vcard_contact: handle_fingerprint_change() failed for {}: {e:#}.",
392                contact.addr
393            );
394            return Ok(id);
395        }
396    }
397    if modified != Modifier::Created {
398        return Ok(id);
399    }
400    let path = match &contact.profile_image {
401        Some(image) => match BlobObject::store_from_base64(context, image) {
402            Err(e) => {
403                warn!(
404                    context,
405                    "import_vcard_contact: Could not decode and save avatar for {}: {e:#}.",
406                    contact.addr
407                );
408                None
409            }
410            Ok(path) => Some(path),
411        },
412        None => None,
413    };
414    if let Some(path) = path {
415        // Currently this value doesn't matter as we don't import the contact of self.
416        let was_encrypted = false;
417        if let Err(e) =
418            set_profile_image(context, id, &AvatarAction::Change(path), was_encrypted).await
419        {
420            warn!(
421                context,
422                "import_vcard_contact: Could not set avatar for {}: {e:#}.", contact.addr
423            );
424        }
425    }
426    Ok(id)
427}
428
429/// An object representing a single contact in memory.
430///
431/// The contact object is not updated.
432/// If you want an update, you have to recreate the object.
433///
434/// The library makes sure
435/// only to use names _authorized_ by the contact in `To:` or `Cc:`.
436/// *Given-names* as "Daddy" or "Honey" are not used there.
437/// For this purpose, internally, two names are tracked -
438/// authorized name and given name.
439/// By default, these names are equal, but functions working with contact names
440/// only affect the given name.
441#[derive(Debug)]
442pub struct Contact {
443    /// The contact ID.
444    pub id: ContactId,
445
446    /// Contact name. It is recommended to use `Contact::get_name`,
447    /// `Contact::get_display_name` or `Contact::get_name_n_addr` to access this field.
448    /// May be empty, initially set to `authname`.
449    name: String,
450
451    /// Name authorized by the contact himself. Only this name may be spread to others,
452    /// e.g. in To:-lists. May be empty. It is recommended to use `Contact::get_authname`,
453    /// to access this field.
454    authname: String,
455
456    /// E-Mail-Address of the contact. It is recommended to use `Contact::get_addr` to access this field.
457    addr: String,
458
459    /// Blocked state. Use contact_is_blocked to access this field.
460    pub blocked: bool,
461
462    /// Time when the contact was seen last time, Unix time in seconds.
463    last_seen: i64,
464
465    /// The origin/source of the contact.
466    pub origin: Origin,
467
468    /// Parameters as Param::ProfileImage
469    pub param: Params,
470
471    /// Last seen message signature for this contact, to be displayed in the profile.
472    status: String,
473
474    /// If the contact is a bot.
475    is_bot: bool,
476}
477
478/// Possible origins of a contact.
479#[derive(
480    Debug,
481    Default,
482    Clone,
483    Copy,
484    PartialEq,
485    Eq,
486    PartialOrd,
487    Ord,
488    FromPrimitive,
489    ToPrimitive,
490    FromSql,
491    ToSql,
492)]
493#[repr(u32)]
494pub enum Origin {
495    /// Unknown origin. Can be used as a minimum origin to specify that the caller does not care
496    /// about origin of the contact.
497    #[default]
498    Unknown = 0,
499
500    /// The contact is a mailing list address, needed to unblock mailing lists
501    MailinglistAddress = 0x2,
502
503    /// Hidden on purpose, e.g. addresses with the word "noreply" in it
504    Hidden = 0x8,
505
506    /// From: of incoming messages of unknown sender
507    IncomingUnknownFrom = 0x10,
508
509    /// Cc: of incoming messages of unknown sender
510    IncomingUnknownCc = 0x20,
511
512    /// To: of incoming messages of unknown sender
513    IncomingUnknownTo = 0x40,
514
515    /// Address scanned but not verified.
516    UnhandledQrScan = 0x80,
517
518    /// Address scanned from a SecureJoin QR code, but not verified yet.
519    UnhandledSecurejoinQrScan = 0x81,
520
521    /// Reply-To: of incoming message of known sender
522    /// Contacts with at least this origin value are shown in the contact list.
523    IncomingReplyTo = 0x100,
524
525    /// Cc: of incoming message of known sender
526    IncomingCc = 0x200,
527
528    /// additional To:'s of incoming message of known sender
529    IncomingTo = 0x400,
530
531    /// a chat was manually created for this user, but no message yet sent
532    CreateChat = 0x800,
533
534    /// message sent by us
535    OutgoingBcc = 0x1000,
536
537    /// message sent by us
538    OutgoingCc = 0x2000,
539
540    /// message sent by us
541    OutgoingTo = 0x4000,
542
543    /// internal use
544    Internal = 0x40000,
545
546    /// address is in our address book
547    AddressBook = 0x80000,
548
549    /// set on Alice's side for contacts like Bob that have scanned the QR code offered by her. Only means the contact has once been established using the "securejoin" procedure in the past, getting the current key verification status requires calling contact_is_verified() !
550    SecurejoinInvited = 0x0100_0000,
551
552    /// Set on Bob's side for contacts scanned from a QR code.
553    /// Only means the contact has been scanned from the QR code,
554    /// but does not mean that securejoin succeeded
555    /// or the key has not changed since the last scan.
556    /// Getting the current key verification status requires calling contact_is_verified() !
557    SecurejoinJoined = 0x0200_0000,
558
559    /// contact added manually by create_contact(), this should be the largest origin as otherwise the user cannot modify the names
560    ManuallyCreated = 0x0400_0000,
561}
562
563impl Origin {
564    /// Contacts that are known, i. e. they came in via accepted contacts or
565    /// themselves an accepted contact. Known contacts are shown in the
566    /// contact list when one creates a chat and wants to add members etc.
567    pub fn is_known(self) -> bool {
568        self >= Origin::IncomingReplyTo
569    }
570}
571
572#[derive(Debug, PartialEq, Eq, Clone, Copy)]
573pub(crate) enum Modifier {
574    None,
575    Modified,
576    Created,
577}
578
579impl Contact {
580    /// Loads a single contact object from the database.
581    ///
582    /// Returns an error if the contact does not exist.
583    ///
584    /// For contact ContactId::SELF (1), the function returns sth.
585    /// like "Me" in the selected language and the email address
586    /// defined by set_config().
587    ///
588    /// For contact ContactId::DEVICE, the function overrides
589    /// the contact name and status with localized address.
590    pub async fn get_by_id(context: &Context, contact_id: ContactId) -> Result<Self> {
591        let contact = Self::get_by_id_optional(context, contact_id)
592            .await?
593            .with_context(|| format!("contact {contact_id} not found"))?;
594        Ok(contact)
595    }
596
597    /// Loads a single contact object from the database.
598    ///
599    /// Similar to [`Contact::get_by_id()`] but returns `None` if the contact does not exist.
600    pub async fn get_by_id_optional(
601        context: &Context,
602        contact_id: ContactId,
603    ) -> Result<Option<Self>> {
604        if let Some(mut contact) = context
605            .sql
606            .query_row_optional(
607                "SELECT c.name, c.addr, c.origin, c.blocked, c.last_seen,
608                c.authname, c.param, c.status, c.is_bot
609               FROM contacts c
610              WHERE c.id=?;",
611                (contact_id,),
612                |row| {
613                    let name: String = row.get(0)?;
614                    let addr: String = row.get(1)?;
615                    let origin: Origin = row.get(2)?;
616                    let blocked: Option<bool> = row.get(3)?;
617                    let last_seen: i64 = row.get(4)?;
618                    let authname: String = row.get(5)?;
619                    let param: String = row.get(6)?;
620                    let status: Option<String> = row.get(7)?;
621                    let is_bot: bool = row.get(8)?;
622                    let contact = Self {
623                        id: contact_id,
624                        name,
625                        authname,
626                        addr,
627                        blocked: blocked.unwrap_or_default(),
628                        last_seen,
629                        origin,
630                        param: param.parse().unwrap_or_default(),
631                        status: status.unwrap_or_default(),
632                        is_bot,
633                    };
634                    Ok(contact)
635                },
636            )
637            .await?
638        {
639            if contact_id == ContactId::SELF {
640                contact.name = stock_str::self_msg(context).await;
641                contact.authname = context
642                    .get_config(Config::Displayname)
643                    .await?
644                    .unwrap_or_default();
645                contact.addr = context
646                    .get_config(Config::ConfiguredAddr)
647                    .await?
648                    .unwrap_or_default();
649                contact.status = context
650                    .get_config(Config::Selfstatus)
651                    .await?
652                    .unwrap_or_default();
653            } else if contact_id == ContactId::DEVICE {
654                contact.name = stock_str::device_messages(context).await;
655                contact.addr = ContactId::DEVICE_ADDR.to_string();
656                contact.status = stock_str::device_messages_hint(context).await;
657            }
658            Ok(Some(contact))
659        } else {
660            Ok(None)
661        }
662    }
663
664    /// Returns `true` if this contact is blocked.
665    pub fn is_blocked(&self) -> bool {
666        self.blocked
667    }
668
669    /// Returns last seen timestamp.
670    pub fn last_seen(&self) -> i64 {
671        self.last_seen
672    }
673
674    /// Returns `true` if this contact was seen recently.
675    pub fn was_seen_recently(&self) -> bool {
676        time() - self.last_seen <= SEEN_RECENTLY_SECONDS
677    }
678
679    /// Check if a contact is blocked.
680    pub async fn is_blocked_load(context: &Context, id: ContactId) -> Result<bool> {
681        let blocked = context
682            .sql
683            .query_row("SELECT blocked FROM contacts WHERE id=?", (id,), |row| {
684                let blocked: bool = row.get(0)?;
685                Ok(blocked)
686            })
687            .await?;
688        Ok(blocked)
689    }
690
691    /// Block the given contact.
692    pub async fn block(context: &Context, id: ContactId) -> Result<()> {
693        set_blocked(context, Sync, id, true).await
694    }
695
696    /// Unblock the given contact.
697    pub async fn unblock(context: &Context, id: ContactId) -> Result<()> {
698        set_blocked(context, Sync, id, false).await
699    }
700
701    /// Add a single contact as a result of an _explicit_ user action.
702    ///
703    /// We assume, the contact name, if any, is entered by the user and is used "as is" therefore,
704    /// normalize() is *not* called for the name. If the contact is blocked, it is unblocked.
705    ///
706    /// To add a number of contacts, see `add_address_book()` which is much faster for adding
707    /// a bunch of addresses.
708    ///
709    /// May result in a `#DC_EVENT_CONTACTS_CHANGED` event.
710    pub async fn create(context: &Context, name: &str, addr: &str) -> Result<ContactId> {
711        Self::create_ex(context, Sync, name, addr).await
712    }
713
714    pub(crate) async fn create_ex(
715        context: &Context,
716        sync: sync::Sync,
717        name: &str,
718        addr: &str,
719    ) -> Result<ContactId> {
720        let (name, addr) = sanitize_name_and_addr(name, addr);
721        let addr = ContactAddress::new(&addr)?;
722
723        let (contact_id, sth_modified) =
724            Contact::add_or_lookup(context, &name, &addr, Origin::ManuallyCreated)
725                .await
726                .context("add_or_lookup")?;
727        let blocked = Contact::is_blocked_load(context, contact_id).await?;
728        match sth_modified {
729            Modifier::None => {}
730            Modifier::Modified | Modifier::Created => {
731                context.emit_event(EventType::ContactsChanged(Some(contact_id)))
732            }
733        }
734        if blocked {
735            set_blocked(context, Nosync, contact_id, false).await?;
736        }
737
738        if sync.into() && sth_modified != Modifier::None {
739            chat::sync(
740                context,
741                chat::SyncId::ContactAddr(addr.to_string()),
742                chat::SyncAction::Rename(name.to_string()),
743            )
744            .await
745            .log_err(context)
746            .ok();
747        }
748        Ok(contact_id)
749    }
750
751    /// Mark messages from a contact as noticed.
752    pub async fn mark_noticed(context: &Context, id: ContactId) -> Result<()> {
753        context
754            .sql
755            .execute(
756                "UPDATE msgs SET state=? WHERE from_id=? AND state=?;",
757                (MessageState::InNoticed, id, MessageState::InFresh),
758            )
759            .await?;
760        Ok(())
761    }
762
763    /// Returns whether contact is a bot.
764    pub fn is_bot(&self) -> bool {
765        self.is_bot
766    }
767
768    /// Check if an e-mail address belongs to a known and unblocked contact.
769    ///
770    /// Known and unblocked contacts will be returned by `get_contacts()`.
771    ///
772    /// To validate an e-mail address independently of the contact database
773    /// use `may_be_valid_addr()`.
774    ///
775    /// Returns the contact ID of the contact belonging to the e-mail address or 0 if there is no
776    /// contact that is or was introduced by an accepted contact.
777    pub async fn lookup_id_by_addr(
778        context: &Context,
779        addr: &str,
780        min_origin: Origin,
781    ) -> Result<Option<ContactId>> {
782        Self::lookup_id_by_addr_ex(context, addr, min_origin, Some(Blocked::Not)).await
783    }
784
785    /// The same as `lookup_id_by_addr()`, but internal function. Currently also allows looking up
786    /// not unblocked contacts.
787    pub(crate) async fn lookup_id_by_addr_ex(
788        context: &Context,
789        addr: &str,
790        min_origin: Origin,
791        blocked: Option<Blocked>,
792    ) -> Result<Option<ContactId>> {
793        if addr.is_empty() {
794            bail!("lookup_id_by_addr: empty address");
795        }
796
797        let addr_normalized = addr_normalize(addr);
798
799        if context.is_self_addr(&addr_normalized).await? {
800            return Ok(Some(ContactId::SELF));
801        }
802
803        let id = context
804            .sql
805            .query_get_value(
806                "SELECT id FROM contacts \
807            WHERE addr=?1 COLLATE NOCASE \
808            AND id>?2 AND origin>=?3 AND (? OR blocked=?)",
809                (
810                    &addr_normalized,
811                    ContactId::LAST_SPECIAL,
812                    min_origin as u32,
813                    blocked.is_none(),
814                    blocked.unwrap_or_default(),
815                ),
816            )
817            .await?;
818        Ok(id)
819    }
820
821    /// Lookup a contact and create it if it does not exist yet.
822    /// The contact is identified by the email-address, a name and an "origin" can be given.
823    ///
824    /// The "origin" is where the address comes from -
825    /// from-header, cc-header, addressbook, qr, manual-edit etc.
826    /// In general, "better" origins overwrite the names of "worse" origins -
827    /// Eg. if we got a name in cc-header and later in from-header, the name will change -
828    /// this does not happen the other way round.
829    ///
830    /// The "best" origin are manually created contacts -
831    /// names given manually can only be overwritten by further manual edits
832    /// (until they are set empty again or reset to the name seen in the From-header).
833    ///
834    /// These manually edited names are _never_ used for sending on the wire -
835    /// this should avoid sending sth. as "Mama" or "Daddy" to some 3rd party.
836    /// Instead, for the wire, we use so called "authnames"
837    /// that can only be set and updated by a From-header.
838    ///
839    /// The different names used in the function are:
840    /// - "name": name passed as function argument, belonging to the given origin
841    /// - "row_name": current name used in the database, typically set to "name"
842    /// - "row_authname": name as authorized from a contact, set only through a From-header
843    ///   Depending on the origin, both, "row_name" and "row_authname" are updated from "name".
844    ///
845    /// Returns the contact_id and a `Modifier` value indicating if a modification occurred.
846    pub(crate) async fn add_or_lookup(
847        context: &Context,
848        name: &str,
849        addr: &ContactAddress,
850        mut origin: Origin,
851    ) -> Result<(ContactId, Modifier)> {
852        let mut sth_modified = Modifier::None;
853
854        ensure!(!addr.is_empty(), "Can not add_or_lookup empty address");
855        ensure!(origin != Origin::Unknown, "Missing valid origin");
856
857        if context.is_self_addr(addr).await? {
858            return Ok((ContactId::SELF, sth_modified));
859        }
860
861        let mut name = sanitize_name(name);
862        if origin <= Origin::OutgoingTo {
863            // The user may accidentally have written to a "noreply" address with another MUA:
864            if addr.contains("noreply")
865                || addr.contains("no-reply")
866                || addr.starts_with("notifications@")
867                // Filter out use-once addresses (like reply+AEJDGPOECLAP...@reply.github.com):
868                || (addr.len() > 50 && addr.contains('+'))
869            {
870                info!(context, "hiding contact {}", addr);
871                origin = Origin::Hidden;
872                // For these kind of email addresses, sender and address often don't belong together
873                // (like hocuri <notifications@github.com>). In this example, hocuri shouldn't
874                // be saved as the displayname for notifications@github.com.
875                name = "".to_string();
876            }
877        }
878
879        // If the origin indicates that user entered the contact manually, from the address book or
880        // from the QR-code scan (potentially from the address book of their other phone), then name
881        // should go into the "name" column and never into "authname" column, to avoid leaking it
882        // into the network.
883        let manual = matches!(
884            origin,
885            Origin::ManuallyCreated | Origin::AddressBook | Origin::UnhandledQrScan
886        );
887
888        let mut update_addr = false;
889
890        let row_id = context
891            .sql
892            .transaction(|transaction| {
893                let row = transaction
894                    .query_row(
895                        "SELECT id, name, addr, origin, authname
896                 FROM contacts WHERE addr=? COLLATE NOCASE",
897                        (addr,),
898                        |row| {
899                            let row_id: u32 = row.get(0)?;
900                            let row_name: String = row.get(1)?;
901                            let row_addr: String = row.get(2)?;
902                            let row_origin: Origin = row.get(3)?;
903                            let row_authname: String = row.get(4)?;
904
905                            Ok((row_id, row_name, row_addr, row_origin, row_authname))
906                        },
907                    )
908                    .optional()?;
909
910                let row_id;
911                if let Some((id, row_name, row_addr, row_origin, row_authname)) = row {
912                    let update_name = manual && name != row_name;
913                    let update_authname = !manual
914                        && name != row_authname
915                        && !name.is_empty()
916                        && (origin >= row_origin
917                            || origin == Origin::IncomingUnknownFrom
918                            || row_authname.is_empty());
919
920                    row_id = id;
921                    if origin >= row_origin && addr.as_ref() != row_addr {
922                        update_addr = true;
923                    }
924                    if update_name || update_authname || update_addr || origin > row_origin {
925                        let new_name = if update_name {
926                            name.to_string()
927                        } else {
928                            row_name
929                        };
930
931                        transaction.execute(
932                            "UPDATE contacts SET name=?, addr=?, origin=?, authname=? WHERE id=?;",
933                            (
934                                new_name,
935                                if update_addr {
936                                    addr.to_string()
937                                } else {
938                                    row_addr
939                                },
940                                if origin > row_origin {
941                                    origin
942                                } else {
943                                    row_origin
944                                },
945                                if update_authname {
946                                    name.to_string()
947                                } else {
948                                    row_authname
949                                },
950                                row_id,
951                            ),
952                        )?;
953
954                        if update_name || update_authname {
955                            let contact_id = ContactId::new(row_id);
956                            update_chat_names(context, transaction, contact_id)?;
957                        }
958                        sth_modified = Modifier::Modified;
959                    }
960                } else {
961                    let update_name = manual;
962                    let update_authname = !manual;
963
964                    transaction.execute(
965                        "INSERT INTO contacts (name, addr, origin, authname)
966                         VALUES (?, ?, ?, ?);",
967                        (
968                            if update_name { &name } else { "" },
969                            &addr,
970                            origin,
971                            if update_authname { &name } else { "" },
972                        ),
973                    )?;
974
975                    sth_modified = Modifier::Created;
976                    row_id = u32::try_from(transaction.last_insert_rowid())?;
977                    info!(context, "Added contact id={row_id} addr={addr}.");
978                }
979                Ok(row_id)
980            })
981            .await?;
982
983        let contact_id = ContactId::new(row_id);
984
985        Ok((contact_id, sth_modified))
986    }
987
988    /// Add a number of contacts.
989    ///
990    /// Typically used to add the whole address book from the OS. As names here are typically not
991    /// well formatted, we call `normalize()` for each name given.
992    ///
993    /// No email-address is added twice.
994    /// Trying to add email-addresses that are already in the contact list,
995    /// results in updating the name unless the name was changed manually by the user.
996    /// If any email-address or any name is really updated,
997    /// the event `DC_EVENT_CONTACTS_CHANGED` is sent.
998    ///
999    /// To add a single contact entered by the user, you should prefer `Contact::create`,
1000    /// however, for adding a bunch of addresses, this function is much faster.
1001    ///
1002    /// The `addr_book` is a multiline string in the format `Name one\nAddress one\nName two\nAddress two`.
1003    ///
1004    /// Returns the number of modified contacts.
1005    pub async fn add_address_book(context: &Context, addr_book: &str) -> Result<usize> {
1006        let mut modify_cnt = 0;
1007
1008        for (name, addr) in split_address_book(addr_book) {
1009            let (name, addr) = sanitize_name_and_addr(name, addr);
1010            match ContactAddress::new(&addr) {
1011                Ok(addr) => {
1012                    match Contact::add_or_lookup(context, &name, &addr, Origin::AddressBook).await {
1013                        Ok((_, modified)) => {
1014                            if modified != Modifier::None {
1015                                modify_cnt += 1
1016                            }
1017                        }
1018                        Err(err) => {
1019                            warn!(
1020                                context,
1021                                "Failed to add address {} from address book: {}", addr, err
1022                            );
1023                        }
1024                    }
1025                }
1026                Err(err) => {
1027                    warn!(context, "{:#}.", err);
1028                }
1029            }
1030        }
1031        if modify_cnt > 0 {
1032            context.emit_event(EventType::ContactsChanged(None));
1033        }
1034
1035        Ok(modify_cnt)
1036    }
1037
1038    /// Returns known and unblocked contacts.
1039    ///
1040    /// To get information about a single contact, see get_contact().
1041    ///
1042    /// `listflags` is a combination of flags:
1043    /// - if the flag DC_GCL_ADD_SELF is set, SELF is added to the list unless filtered by other parameters
1044    ///
1045    /// `query` is a string to filter the list.
1046    pub async fn get_all(
1047        context: &Context,
1048        listflags: u32,
1049        query: Option<&str>,
1050    ) -> Result<Vec<ContactId>> {
1051        let self_addrs = context
1052            .get_all_self_addrs()
1053            .await?
1054            .into_iter()
1055            .collect::<HashSet<_>>();
1056        let mut add_self = false;
1057        let mut ret = Vec::new();
1058        let flag_add_self = (listflags & DC_GCL_ADD_SELF) != 0;
1059        let minimal_origin = if context.get_config_bool(Config::Bot).await? {
1060            Origin::Unknown
1061        } else {
1062            Origin::IncomingReplyTo
1063        };
1064        if query.is_some() {
1065            let s3str_like_cmd = format!("%{}%", query.unwrap_or(""));
1066            context
1067                .sql
1068                .query_map(
1069                    "SELECT c.id, c.addr FROM contacts c
1070                 LEFT JOIN acpeerstates ps ON c.addr=ps.addr  \
1071                 WHERE c.id>?
1072                 AND c.origin>=? \
1073                 AND c.blocked=0 \
1074                 AND (iif(c.name='',c.authname,c.name) LIKE ? OR c.addr LIKE ?) \
1075                 ORDER BY c.last_seen DESC, c.id DESC;",
1076                    (
1077                        ContactId::LAST_SPECIAL,
1078                        minimal_origin,
1079                        &s3str_like_cmd,
1080                        &s3str_like_cmd,
1081                    ),
1082                    |row| {
1083                        let id: ContactId = row.get(0)?;
1084                        let addr: String = row.get(1)?;
1085                        Ok((id, addr))
1086                    },
1087                    |rows| {
1088                        for row in rows {
1089                            let (id, addr) = row?;
1090                            if !self_addrs.contains(&addr) {
1091                                ret.push(id);
1092                            }
1093                        }
1094                        Ok(())
1095                    },
1096                )
1097                .await?;
1098
1099            if let Some(query) = query {
1100                let self_addr = context
1101                    .get_config(Config::ConfiguredAddr)
1102                    .await?
1103                    .unwrap_or_default();
1104                let self_name = context
1105                    .get_config(Config::Displayname)
1106                    .await?
1107                    .unwrap_or_default();
1108                let self_name2 = stock_str::self_msg(context);
1109
1110                if self_addr.contains(query)
1111                    || self_name.contains(query)
1112                    || self_name2.await.contains(query)
1113                {
1114                    add_self = true;
1115                }
1116            } else {
1117                add_self = true;
1118            }
1119        } else {
1120            add_self = true;
1121
1122            context
1123                .sql
1124                .query_map(
1125                    "SELECT id, addr FROM contacts
1126                 WHERE id>?
1127                 AND origin>=?
1128                 AND blocked=0
1129                 ORDER BY last_seen DESC, id DESC;",
1130                    (ContactId::LAST_SPECIAL, minimal_origin),
1131                    |row| {
1132                        let id: ContactId = row.get(0)?;
1133                        let addr: String = row.get(1)?;
1134                        Ok((id, addr))
1135                    },
1136                    |rows| {
1137                        for row in rows {
1138                            let (id, addr) = row?;
1139                            if !self_addrs.contains(&addr) {
1140                                ret.push(id);
1141                            }
1142                        }
1143                        Ok(())
1144                    },
1145                )
1146                .await?;
1147        }
1148
1149        if flag_add_self && add_self {
1150            ret.push(ContactId::SELF);
1151        }
1152
1153        Ok(ret)
1154    }
1155
1156    /// Adds blocked mailinglists as contacts
1157    /// to allow unblocking them as if they are contacts
1158    /// (this way, only one unblock-ffi is needed and only one set of ui-functions,
1159    /// from the users perspective,
1160    /// there is not much difference in an email- and a mailinglist-address)
1161    async fn update_blocked_mailinglist_contacts(context: &Context) -> Result<()> {
1162        context
1163            .sql
1164            .transaction(move |transaction| {
1165                let mut stmt = transaction
1166                    .prepare("SELECT name, grpid FROM chats WHERE type=? AND blocked=?")?;
1167                let rows = stmt.query_map((Chattype::Mailinglist, Blocked::Yes), |row| {
1168                    let name: String = row.get(0)?;
1169                    let grpid: String = row.get(1)?;
1170                    Ok((name, grpid))
1171                })?;
1172                let blocked_mailinglists = rows.collect::<std::result::Result<Vec<_>, _>>()?;
1173                for (name, grpid) in blocked_mailinglists {
1174                    let count = transaction.query_row(
1175                        "SELECT COUNT(id) FROM contacts WHERE addr=?",
1176                        [&grpid],
1177                        |row| {
1178                            let count: isize = row.get(0)?;
1179                            Ok(count)
1180                        },
1181                    )?;
1182                    if count == 0 {
1183                        transaction.execute("INSERT INTO contacts (addr) VALUES (?)", [&grpid])?;
1184                    }
1185
1186                    // Always do an update in case the blocking is reset or name is changed.
1187                    transaction.execute(
1188                        "UPDATE contacts SET name=?, origin=?, blocked=1 WHERE addr=?",
1189                        (&name, Origin::MailinglistAddress, &grpid),
1190                    )?;
1191                }
1192                Ok(())
1193            })
1194            .await?;
1195        Ok(())
1196    }
1197
1198    /// Returns number of blocked contacts.
1199    pub async fn get_blocked_cnt(context: &Context) -> Result<usize> {
1200        let count = context
1201            .sql
1202            .count(
1203                "SELECT COUNT(*) FROM contacts WHERE id>? AND blocked!=0",
1204                (ContactId::LAST_SPECIAL,),
1205            )
1206            .await?;
1207        Ok(count)
1208    }
1209
1210    /// Get blocked contacts.
1211    pub async fn get_all_blocked(context: &Context) -> Result<Vec<ContactId>> {
1212        Contact::update_blocked_mailinglist_contacts(context)
1213            .await
1214            .context("cannot update blocked mailinglist contacts")?;
1215
1216        let list = context
1217            .sql
1218            .query_map(
1219                "SELECT id FROM contacts WHERE id>? AND blocked!=0 ORDER BY last_seen DESC, id DESC;",
1220                (ContactId::LAST_SPECIAL,),
1221                |row| row.get::<_, ContactId>(0),
1222                |ids| {
1223                    ids.collect::<std::result::Result<Vec<_>, _>>()
1224                        .map_err(Into::into)
1225                },
1226            )
1227            .await?;
1228        Ok(list)
1229    }
1230
1231    /// Returns a textual summary of the encryption state for the contact.
1232    ///
1233    /// This function returns a string explaining the encryption state
1234    /// of the contact and if the connection is encrypted the
1235    /// fingerprints of the keys involved.
1236    pub async fn get_encrinfo(context: &Context, contact_id: ContactId) -> Result<String> {
1237        ensure!(
1238            !contact_id.is_special(),
1239            "Can not provide encryption info for special contact"
1240        );
1241
1242        let contact = Contact::get_by_id(context, contact_id).await?;
1243        let addr = context
1244            .get_config(Config::ConfiguredAddr)
1245            .await?
1246            .unwrap_or_default();
1247        let peerstate = Peerstate::from_addr(context, &contact.addr).await?;
1248
1249        let Some(peerstate) = peerstate.filter(|peerstate| peerstate.peek_key(false).is_some())
1250        else {
1251            return Ok(stock_str::encr_none(context).await);
1252        };
1253
1254        let stock_message = match peerstate.prefer_encrypt {
1255            EncryptPreference::Mutual => stock_str::e2e_preferred(context).await,
1256            EncryptPreference::NoPreference => stock_str::e2e_available(context).await,
1257            EncryptPreference::Reset => stock_str::encr_none(context).await,
1258        };
1259
1260        let finger_prints = stock_str::finger_prints(context).await;
1261        let mut ret = format!("{stock_message}.\n{finger_prints}:");
1262
1263        let fingerprint_self = load_self_public_key(context)
1264            .await?
1265            .dc_fingerprint()
1266            .to_string();
1267        let fingerprint_other_verified = peerstate
1268            .peek_key(true)
1269            .map(|k| k.dc_fingerprint().to_string())
1270            .unwrap_or_default();
1271        let fingerprint_other_unverified = peerstate
1272            .peek_key(false)
1273            .map(|k| k.dc_fingerprint().to_string())
1274            .unwrap_or_default();
1275        if addr < peerstate.addr {
1276            cat_fingerprint(
1277                &mut ret,
1278                &stock_str::self_msg(context).await,
1279                &addr,
1280                &fingerprint_self,
1281                "",
1282            );
1283            cat_fingerprint(
1284                &mut ret,
1285                contact.get_display_name(),
1286                &peerstate.addr,
1287                &fingerprint_other_verified,
1288                &fingerprint_other_unverified,
1289            );
1290        } else {
1291            cat_fingerprint(
1292                &mut ret,
1293                contact.get_display_name(),
1294                &peerstate.addr,
1295                &fingerprint_other_verified,
1296                &fingerprint_other_unverified,
1297            );
1298            cat_fingerprint(
1299                &mut ret,
1300                &stock_str::self_msg(context).await,
1301                &addr,
1302                &fingerprint_self,
1303                "",
1304            );
1305        }
1306
1307        Ok(ret)
1308    }
1309
1310    /// Delete a contact so that it disappears from the corresponding lists.
1311    /// Depending on whether there are ongoing chats, deletion is done by physical deletion or hiding.
1312    /// The contact is deleted from the local device.
1313    ///
1314    /// May result in a `#DC_EVENT_CONTACTS_CHANGED` event.
1315    pub async fn delete(context: &Context, contact_id: ContactId) -> Result<()> {
1316        ensure!(!contact_id.is_special(), "Can not delete special contact");
1317
1318        context
1319            .sql
1320            .transaction(move |transaction| {
1321                // make sure, the transaction starts with a write command and becomes EXCLUSIVE by that -
1322                // upgrading later may be impossible by races.
1323                let deleted_contacts = transaction.execute(
1324                    "DELETE FROM contacts WHERE id=?
1325                     AND (SELECT COUNT(*) FROM chats_contacts WHERE contact_id=?)=0;",
1326                    (contact_id, contact_id),
1327                )?;
1328                if deleted_contacts == 0 {
1329                    transaction.execute(
1330                        "UPDATE contacts SET origin=? WHERE id=?;",
1331                        (Origin::Hidden, contact_id),
1332                    )?;
1333                }
1334                Ok(())
1335            })
1336            .await?;
1337
1338        context.emit_event(EventType::ContactsChanged(None));
1339        Ok(())
1340    }
1341
1342    /// Updates `param` column in the database.
1343    pub async fn update_param(&self, context: &Context) -> Result<()> {
1344        context
1345            .sql
1346            .execute(
1347                "UPDATE contacts SET param=? WHERE id=?",
1348                (self.param.to_string(), self.id),
1349            )
1350            .await?;
1351        Ok(())
1352    }
1353
1354    /// Updates `status` column in the database.
1355    pub async fn update_status(&self, context: &Context) -> Result<()> {
1356        context
1357            .sql
1358            .execute(
1359                "UPDATE contacts SET status=? WHERE id=?",
1360                (&self.status, self.id),
1361            )
1362            .await?;
1363        Ok(())
1364    }
1365
1366    /// Get the ID of the contact.
1367    pub fn get_id(&self) -> ContactId {
1368        self.id
1369    }
1370
1371    /// Get email address. The email address is always set for a contact.
1372    pub fn get_addr(&self) -> &str {
1373        &self.addr
1374    }
1375
1376    /// Get name authorized by the contact.
1377    pub fn get_authname(&self) -> &str {
1378        &self.authname
1379    }
1380
1381    /// Get the contact name. This is the name as modified by the local user.
1382    /// May be an empty string.
1383    ///
1384    /// This name is typically used in a form where the user can edit the name of a contact.
1385    /// To get a fine name to display in lists etc., use `Contact::get_display_name` or `Contact::get_name_n_addr`.
1386    pub fn get_name(&self) -> &str {
1387        &self.name
1388    }
1389
1390    /// Get display name. This is the name as defined by the contact himself,
1391    /// modified by the user or, if both are unset, the email address.
1392    ///
1393    /// This name is typically used in lists.
1394    /// To get the name editable in a formular, use `Contact::get_name`.
1395    pub fn get_display_name(&self) -> &str {
1396        if !self.name.is_empty() {
1397            return &self.name;
1398        }
1399        if !self.authname.is_empty() {
1400            return &self.authname;
1401        }
1402        &self.addr
1403    }
1404
1405    /// Get authorized name or address.
1406    ///
1407    /// This string is suitable for sending over email
1408    /// as it does not leak the locally set name.
1409    pub(crate) fn get_authname_or_addr(&self) -> String {
1410        if !self.authname.is_empty() {
1411            (&self.authname).into()
1412        } else {
1413            (&self.addr).into()
1414        }
1415    }
1416
1417    /// Get a summary of name and address.
1418    ///
1419    /// The returned string is either "Name (email@domain.com)" or just
1420    /// "email@domain.com" if the name is unset.
1421    ///
1422    /// The result should only be used locally and never sent over the network
1423    /// as it leaks the local contact name.
1424    ///
1425    /// The summary is typically used when asking the user something about the contact.
1426    /// The attached email address makes the question unique, eg. "Chat with Alan Miller (am@uniquedomain.com)?"
1427    pub fn get_name_n_addr(&self) -> String {
1428        if !self.name.is_empty() {
1429            format!("{} ({})", self.name, self.addr)
1430        } else if !self.authname.is_empty() {
1431            format!("{} ({})", self.authname, self.addr)
1432        } else {
1433            (&self.addr).into()
1434        }
1435    }
1436
1437    /// Get the contact's profile image.
1438    /// This is the image set by each remote user on their own
1439    /// using set_config(context, "selfavatar", image).
1440    pub async fn get_profile_image(&self, context: &Context) -> Result<Option<PathBuf>> {
1441        if self.id == ContactId::SELF {
1442            if let Some(p) = context.get_config(Config::Selfavatar).await? {
1443                return Ok(Some(PathBuf::from(p))); // get_config() calls get_abs_path() internally already
1444            }
1445        } else if let Some(image_rel) = self.param.get(Param::ProfileImage) {
1446            if !image_rel.is_empty() {
1447                return Ok(Some(get_abs_path(context, Path::new(image_rel))));
1448            }
1449        }
1450        Ok(None)
1451    }
1452
1453    /// Get a color for the contact.
1454    /// The color is calculated from the contact's email address
1455    /// and can be used for an fallback avatar with white initials
1456    /// as well as for headlines in bubbles of group chats.
1457    pub fn get_color(&self) -> u32 {
1458        str_to_color(&self.addr.to_lowercase())
1459    }
1460
1461    /// Gets the contact's status.
1462    ///
1463    /// Status is the last signature received in a message from this contact.
1464    pub fn get_status(&self) -> &str {
1465        self.status.as_str()
1466    }
1467
1468    /// Returns whether end-to-end encryption to the contact is available.
1469    pub async fn e2ee_avail(&self, context: &Context) -> Result<bool> {
1470        if self.id == ContactId::SELF {
1471            return Ok(true);
1472        }
1473        let Some(peerstate) = Peerstate::from_addr(context, &self.addr).await? else {
1474            return Ok(false);
1475        };
1476        Ok(peerstate.peek_key(false).is_some())
1477    }
1478
1479    /// Returns true if the contact
1480    /// can be added to verified chats,
1481    /// i.e. has a verified key
1482    /// and Autocrypt key matches the verified key.
1483    ///
1484    /// If contact is verified
1485    /// UI should display green checkmark after the contact name
1486    /// in contact list items and
1487    /// in chat member list items.
1488    ///
1489    /// In contact profile view, us this function only if there is no chat with the contact,
1490    /// otherwise use is_chat_protected().
1491    /// Use [Self::get_verifier_id] to display the verifier contact
1492    /// in the info section of the contact profile.
1493    pub async fn is_verified(&self, context: &Context) -> Result<bool> {
1494        // We're always sort of secured-verified as we could verify the key on this device any time with the key
1495        // on this device
1496        if self.id == ContactId::SELF {
1497            return Ok(true);
1498        }
1499
1500        let Some(peerstate) = Peerstate::from_addr(context, &self.addr).await? else {
1501            return Ok(false);
1502        };
1503
1504        let forward_verified = peerstate.is_using_verified_key();
1505        let backward_verified = peerstate.is_backward_verified(context).await?;
1506        Ok(forward_verified && backward_verified)
1507    }
1508
1509    /// Returns true if we have a verified key for the contact
1510    /// and it is the same as Autocrypt key.
1511    /// This is enough to send messages to the contact in verified chat
1512    /// and verify received messages, but not enough to display green checkmark
1513    /// or add the contact to verified groups.
1514    pub async fn is_forward_verified(&self, context: &Context) -> Result<bool> {
1515        if self.id == ContactId::SELF {
1516            return Ok(true);
1517        }
1518
1519        let Some(peerstate) = Peerstate::from_addr(context, &self.addr).await? else {
1520            return Ok(false);
1521        };
1522
1523        Ok(peerstate.is_using_verified_key())
1524    }
1525
1526    /// Returns the `ContactId` that verified the contact.
1527    ///
1528    /// If the function returns non-zero result,
1529    /// display green checkmark in the profile and "Introduced by ..." line
1530    /// with the name and address of the contact
1531    /// formatted by [Self::get_name_n_addr].
1532    ///
1533    /// If this function returns a verifier,
1534    /// this does not necessarily mean
1535    /// you can add the contact to verified chats.
1536    /// Use [Self::is_verified] to check
1537    /// if a contact can be added to a verified chat instead.
1538    pub async fn get_verifier_id(&self, context: &Context) -> Result<Option<ContactId>> {
1539        let Some(verifier_addr) = Peerstate::from_addr(context, self.get_addr())
1540            .await?
1541            .and_then(|peerstate| peerstate.get_verifier().map(|addr| addr.to_owned()))
1542        else {
1543            return Ok(None);
1544        };
1545
1546        if addr_cmp(&verifier_addr, &self.addr) {
1547            // Contact is directly verified via QR code.
1548            return Ok(Some(ContactId::SELF));
1549        }
1550
1551        match Contact::lookup_id_by_addr(context, &verifier_addr, Origin::Unknown).await? {
1552            Some(contact_id) => Ok(Some(contact_id)),
1553            None => {
1554                let addr = &self.addr;
1555                warn!(context, "Could not lookup contact with address {verifier_addr} which introduced {addr}.");
1556                Ok(None)
1557            }
1558        }
1559    }
1560
1561    /// Returns if the contact profile title should display a green checkmark.
1562    ///
1563    /// This generally should be consistent with the 1:1 chat with the contact
1564    /// so 1:1 chat with the contact and the contact profile
1565    /// either both display the green checkmark or both don't display a green checkmark.
1566    ///
1567    /// UI often knows beforehand if a chat exists and can also call
1568    /// `chat.is_protected()` (if there is a chat)
1569    /// or `contact.is_verified()` (if there is no chat) directly.
1570    /// This is often easier and also skips some database calls.
1571    pub async fn is_profile_verified(&self, context: &Context) -> Result<bool> {
1572        let contact_id = self.id;
1573
1574        if let Some(ChatIdBlocked { id: chat_id, .. }) =
1575            ChatIdBlocked::lookup_by_contact(context, contact_id).await?
1576        {
1577            Ok(chat_id.is_protected(context).await? == ProtectionStatus::Protected)
1578        } else {
1579            // 1:1 chat does not exist.
1580            Ok(self.is_verified(context).await?)
1581        }
1582    }
1583
1584    /// Returns the number of real (i.e. non-special) contacts in the database.
1585    pub async fn get_real_cnt(context: &Context) -> Result<usize> {
1586        if !context.sql.is_open().await {
1587            return Ok(0);
1588        }
1589
1590        let count = context
1591            .sql
1592            .count(
1593                "SELECT COUNT(*) FROM contacts WHERE id>?;",
1594                (ContactId::LAST_SPECIAL,),
1595            )
1596            .await?;
1597        Ok(count)
1598    }
1599
1600    /// Returns true if a contact with this ID exists.
1601    pub async fn real_exists_by_id(context: &Context, contact_id: ContactId) -> Result<bool> {
1602        if contact_id.is_special() {
1603            return Ok(false);
1604        }
1605
1606        let exists = context
1607            .sql
1608            .exists("SELECT COUNT(*) FROM contacts WHERE id=?;", (contact_id,))
1609            .await?;
1610        Ok(exists)
1611    }
1612}
1613
1614// Updates the names of the chats which use the contact name.
1615//
1616// This is one of the few duplicated data, however, getting the chat list is easier this way.
1617fn update_chat_names(
1618    context: &Context,
1619    transaction: &rusqlite::Connection,
1620    contact_id: ContactId,
1621) -> Result<()> {
1622    let chat_id: Option<ChatId> = transaction.query_row(
1623            "SELECT id FROM chats WHERE type=? AND id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?)",
1624            (Chattype::Single, contact_id),
1625            |row| {
1626                let chat_id: ChatId = row.get(0)?;
1627                Ok(chat_id)
1628            }
1629        ).optional()?;
1630
1631    if let Some(chat_id) = chat_id {
1632        let (addr, name, authname) = transaction.query_row(
1633            "SELECT addr, name, authname
1634                     FROM contacts
1635                     WHERE id=?",
1636            (contact_id,),
1637            |row| {
1638                let addr: String = row.get(0)?;
1639                let name: String = row.get(1)?;
1640                let authname: String = row.get(2)?;
1641                Ok((addr, name, authname))
1642            },
1643        )?;
1644
1645        let chat_name = if !name.is_empty() {
1646            name
1647        } else if !authname.is_empty() {
1648            authname
1649        } else {
1650            addr
1651        };
1652
1653        let count = transaction.execute(
1654            "UPDATE chats SET name=?1 WHERE id=?2 AND name!=?1",
1655            (chat_name, chat_id),
1656        )?;
1657
1658        if count > 0 {
1659            // Chat name updated
1660            context.emit_event(EventType::ChatModified(chat_id));
1661            chatlist_events::emit_chatlist_items_changed_for_contact(context, contact_id);
1662        }
1663    }
1664
1665    Ok(())
1666}
1667
1668pub(crate) async fn set_blocked(
1669    context: &Context,
1670    sync: sync::Sync,
1671    contact_id: ContactId,
1672    new_blocking: bool,
1673) -> Result<()> {
1674    ensure!(
1675        !contact_id.is_special(),
1676        "Can't block special contact {}",
1677        contact_id
1678    );
1679    let contact = Contact::get_by_id(context, contact_id).await?;
1680
1681    if contact.blocked != new_blocking {
1682        context
1683            .sql
1684            .execute(
1685                "UPDATE contacts SET blocked=? WHERE id=?;",
1686                (i32::from(new_blocking), contact_id),
1687            )
1688            .await?;
1689
1690        // also (un)block all chats with _only_ this contact - we do not delete them to allow a
1691        // non-destructive blocking->unblocking.
1692        // (Maybe, beside normal chats (type=100) we should also block group chats with only this user.
1693        // However, I'm not sure about this point; it may be confusing if the user wants to add other people;
1694        // this would result in recreating the same group...)
1695        if context
1696            .sql
1697            .execute(
1698                r#"
1699UPDATE chats
1700SET blocked=?
1701WHERE type=? AND id IN (
1702  SELECT chat_id FROM chats_contacts WHERE contact_id=?
1703);
1704"#,
1705                (new_blocking, Chattype::Single, contact_id),
1706            )
1707            .await
1708            .is_ok()
1709        {
1710            Contact::mark_noticed(context, contact_id).await?;
1711            context.emit_event(EventType::ContactsChanged(Some(contact_id)));
1712        }
1713
1714        // also unblock mailinglist
1715        // if the contact is a mailinglist address explicitly created to allow unblocking
1716        if !new_blocking && contact.origin == Origin::MailinglistAddress {
1717            if let Some((chat_id, _, _)) =
1718                chat::get_chat_id_by_grpid(context, &contact.addr).await?
1719            {
1720                chat_id.unblock_ex(context, Nosync).await?;
1721            }
1722        }
1723
1724        if sync.into() {
1725            let action = match new_blocking {
1726                true => chat::SyncAction::Block,
1727                false => chat::SyncAction::Unblock,
1728            };
1729            chat::sync(
1730                context,
1731                chat::SyncId::ContactAddr(contact.addr.clone()),
1732                action,
1733            )
1734            .await
1735            .log_err(context)
1736            .ok();
1737        }
1738    }
1739
1740    chatlist_events::emit_chatlist_changed(context);
1741    Ok(())
1742}
1743
1744/// Set profile image for a contact.
1745///
1746/// The given profile image is expected to be already in the blob directory
1747/// as profile images can be set only by receiving messages, this should be always the case, however.
1748///
1749/// For contact SELF, the image is not saved in the contact-database but as Config::Selfavatar;
1750/// this typically happens if we see message with our own profile image.
1751pub(crate) async fn set_profile_image(
1752    context: &Context,
1753    contact_id: ContactId,
1754    profile_image: &AvatarAction,
1755    was_encrypted: bool,
1756) -> Result<()> {
1757    let mut contact = Contact::get_by_id(context, contact_id).await?;
1758    let changed = match profile_image {
1759        AvatarAction::Change(profile_image) => {
1760            if contact_id == ContactId::SELF {
1761                if was_encrypted {
1762                    context
1763                        .set_config_ex(Nosync, Config::Selfavatar, Some(profile_image))
1764                        .await?;
1765                } else {
1766                    info!(context, "Do not use unencrypted selfavatar.");
1767                }
1768            } else {
1769                contact.param.set(Param::ProfileImage, profile_image);
1770            }
1771            true
1772        }
1773        AvatarAction::Delete => {
1774            if contact_id == ContactId::SELF {
1775                if was_encrypted {
1776                    context
1777                        .set_config_ex(Nosync, Config::Selfavatar, None)
1778                        .await?;
1779                } else {
1780                    info!(context, "Do not use unencrypted selfavatar deletion.");
1781                }
1782            } else {
1783                contact.param.remove(Param::ProfileImage);
1784            }
1785            true
1786        }
1787    };
1788    if changed {
1789        contact.update_param(context).await?;
1790        context.emit_event(EventType::ContactsChanged(Some(contact_id)));
1791        chatlist_events::emit_chatlist_item_changed_for_contact_chat(context, contact_id).await;
1792    }
1793    Ok(())
1794}
1795
1796/// Sets contact status.
1797///
1798/// For contact SELF, the status is not saved in the contact table, but as Config::Selfstatus.  This
1799/// is only done if message is sent from Delta Chat and it is encrypted, to synchronize signature
1800/// between Delta Chat devices.
1801pub(crate) async fn set_status(
1802    context: &Context,
1803    contact_id: ContactId,
1804    status: String,
1805    encrypted: bool,
1806    has_chat_version: bool,
1807) -> Result<()> {
1808    if contact_id == ContactId::SELF {
1809        if encrypted && has_chat_version {
1810            context
1811                .set_config_ex(Nosync, Config::Selfstatus, Some(&status))
1812                .await?;
1813        }
1814    } else {
1815        let mut contact = Contact::get_by_id(context, contact_id).await?;
1816
1817        if contact.status != status {
1818            contact.status = status;
1819            contact.update_status(context).await?;
1820            context.emit_event(EventType::ContactsChanged(Some(contact_id)));
1821        }
1822    }
1823    Ok(())
1824}
1825
1826/// Updates last seen timestamp of the contact if it is earlier than the given `timestamp`.
1827pub(crate) async fn update_last_seen(
1828    context: &Context,
1829    contact_id: ContactId,
1830    timestamp: i64,
1831) -> Result<()> {
1832    ensure!(
1833        !contact_id.is_special(),
1834        "Can not update special contact last seen timestamp"
1835    );
1836
1837    if context
1838        .sql
1839        .execute(
1840            "UPDATE contacts SET last_seen = ?1 WHERE last_seen < ?1 AND id = ?2",
1841            (timestamp, contact_id),
1842        )
1843        .await?
1844        > 0
1845        && timestamp > time() - SEEN_RECENTLY_SECONDS
1846    {
1847        context.emit_event(EventType::ContactsChanged(Some(contact_id)));
1848        context
1849            .scheduler
1850            .interrupt_recently_seen(contact_id, timestamp)
1851            .await;
1852    }
1853    Ok(())
1854}
1855
1856fn cat_fingerprint(
1857    ret: &mut String,
1858    name: &str,
1859    addr: &str,
1860    fingerprint_verified: &str,
1861    fingerprint_unverified: &str,
1862) {
1863    *ret += &format!(
1864        "\n\n{} ({}):\n{}",
1865        name,
1866        addr,
1867        if !fingerprint_verified.is_empty() {
1868            fingerprint_verified
1869        } else {
1870            fingerprint_unverified
1871        },
1872    );
1873    if !fingerprint_verified.is_empty()
1874        && !fingerprint_unverified.is_empty()
1875        && fingerprint_verified != fingerprint_unverified
1876    {
1877        *ret += &format!("\n\n{name} (alternative):\n{fingerprint_unverified}");
1878    }
1879}
1880
1881fn split_address_book(book: &str) -> Vec<(&str, &str)> {
1882    book.lines()
1883        .collect::<Vec<&str>>()
1884        .chunks(2)
1885        .filter_map(|chunk| {
1886            let name = chunk.first()?;
1887            let addr = chunk.get(1)?;
1888            Some((*name, *addr))
1889        })
1890        .collect()
1891}
1892
1893#[derive(Debug)]
1894pub(crate) struct RecentlySeenInterrupt {
1895    contact_id: ContactId,
1896    timestamp: i64,
1897}
1898
1899#[derive(Debug)]
1900pub(crate) struct RecentlySeenLoop {
1901    /// Task running "recently seen" loop.
1902    handle: task::JoinHandle<()>,
1903
1904    interrupt_send: Sender<RecentlySeenInterrupt>,
1905}
1906
1907impl RecentlySeenLoop {
1908    pub(crate) fn new(context: Context) -> Self {
1909        let (interrupt_send, interrupt_recv) = channel::bounded(1);
1910
1911        let handle = task::spawn(Self::run(context, interrupt_recv));
1912        Self {
1913            handle,
1914            interrupt_send,
1915        }
1916    }
1917
1918    async fn run(context: Context, interrupt: Receiver<RecentlySeenInterrupt>) {
1919        type MyHeapElem = (Reverse<i64>, ContactId);
1920
1921        let now = SystemTime::now();
1922        let now_ts = now
1923            .duration_since(SystemTime::UNIX_EPOCH)
1924            .unwrap_or_default()
1925            .as_secs() as i64;
1926
1927        // Priority contains all recently seen sorted by the timestamp
1928        // when they become not recently seen.
1929        //
1930        // Initialize with contacts which are currently seen, but will
1931        // become unseen in the future.
1932        let mut unseen_queue: BinaryHeap<MyHeapElem> = context
1933            .sql
1934            .query_map(
1935                "SELECT id, last_seen FROM contacts
1936                 WHERE last_seen > ?",
1937                (now_ts - SEEN_RECENTLY_SECONDS,),
1938                |row| {
1939                    let contact_id: ContactId = row.get("id")?;
1940                    let last_seen: i64 = row.get("last_seen")?;
1941                    Ok((Reverse(last_seen + SEEN_RECENTLY_SECONDS), contact_id))
1942                },
1943                |rows| {
1944                    rows.collect::<std::result::Result<BinaryHeap<MyHeapElem>, _>>()
1945                        .map_err(Into::into)
1946                },
1947            )
1948            .await
1949            .unwrap_or_default();
1950
1951        loop {
1952            let now = SystemTime::now();
1953            let (until, contact_id) =
1954                if let Some((Reverse(timestamp), contact_id)) = unseen_queue.peek() {
1955                    (
1956                        UNIX_EPOCH
1957                            + Duration::from_secs((*timestamp).try_into().unwrap_or(u64::MAX))
1958                            + Duration::from_secs(1),
1959                        Some(contact_id),
1960                    )
1961                } else {
1962                    // Sleep for 24 hours.
1963                    (now + Duration::from_secs(86400), None)
1964                };
1965
1966            if let Ok(duration) = until.duration_since(now) {
1967                info!(
1968                    context,
1969                    "Recently seen loop waiting for {} or interrupt",
1970                    duration_to_str(duration)
1971                );
1972
1973                match timeout(duration, interrupt.recv()).await {
1974                    Err(_) => {
1975                        // Timeout, notify about contact.
1976                        if let Some(contact_id) = contact_id {
1977                            context.emit_event(EventType::ContactsChanged(Some(*contact_id)));
1978                            chatlist_events::emit_chatlist_item_changed_for_contact_chat(
1979                                &context,
1980                                *contact_id,
1981                            )
1982                            .await;
1983                            unseen_queue.pop();
1984                        }
1985                    }
1986                    Ok(Err(err)) => {
1987                        warn!(
1988                            context,
1989                            "Error receiving an interruption in recently seen loop: {}", err
1990                        );
1991                        // Maybe the sender side is closed.
1992                        // Terminate the loop to avoid looping indefinitely.
1993                        return;
1994                    }
1995                    Ok(Ok(RecentlySeenInterrupt {
1996                        contact_id,
1997                        timestamp,
1998                    })) => {
1999                        // Received an interrupt.
2000                        if contact_id != ContactId::UNDEFINED {
2001                            unseen_queue
2002                                .push((Reverse(timestamp + SEEN_RECENTLY_SECONDS), contact_id));
2003                        }
2004                    }
2005                }
2006            } else {
2007                info!(
2008                    context,
2009                    "Recently seen loop is not waiting, event is already due."
2010                );
2011
2012                // Event is already in the past.
2013                if let Some(contact_id) = contact_id {
2014                    context.emit_event(EventType::ContactsChanged(Some(*contact_id)));
2015                    chatlist_events::emit_chatlist_item_changed_for_contact_chat(
2016                        &context,
2017                        *contact_id,
2018                    )
2019                    .await;
2020                }
2021                unseen_queue.pop();
2022            }
2023        }
2024    }
2025
2026    pub(crate) fn try_interrupt(&self, contact_id: ContactId, timestamp: i64) {
2027        self.interrupt_send
2028            .try_send(RecentlySeenInterrupt {
2029                contact_id,
2030                timestamp,
2031            })
2032            .ok();
2033    }
2034
2035    #[cfg(test)]
2036    pub(crate) async fn interrupt(&self, contact_id: ContactId, timestamp: i64) {
2037        self.interrupt_send
2038            .send(RecentlySeenInterrupt {
2039                contact_id,
2040                timestamp,
2041            })
2042            .await
2043            .unwrap();
2044    }
2045
2046    pub(crate) async fn abort(self) {
2047        self.handle.abort();
2048
2049        // Await aborted task to ensure the `Future` is dropped
2050        // with all resources moved inside such as the `Context`
2051        // reference to `InnerContext`.
2052        self.handle.await.ok();
2053    }
2054}
2055
2056#[cfg(test)]
2057mod contact_tests;