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, DC_GCL_VERIFIED_ONLY};
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    /// - if the flag DC_GCL_VERIFIED_ONLY is set, only verified contacts are returned.
1045    ///   if DC_GCL_VERIFIED_ONLY is not set, verified and unverified contacts are returned.
1046    ///   `query` is a string to filter the list.
1047    pub async fn get_all(
1048        context: &Context,
1049        listflags: u32,
1050        query: Option<&str>,
1051    ) -> Result<Vec<ContactId>> {
1052        let self_addrs = context
1053            .get_all_self_addrs()
1054            .await?
1055            .into_iter()
1056            .collect::<HashSet<_>>();
1057        let mut add_self = false;
1058        let mut ret = Vec::new();
1059        let flag_verified_only = (listflags & DC_GCL_VERIFIED_ONLY) != 0;
1060        let flag_add_self = (listflags & DC_GCL_ADD_SELF) != 0;
1061        let minimal_origin = if context.get_config_bool(Config::Bot).await? {
1062            Origin::Unknown
1063        } else {
1064            Origin::IncomingReplyTo
1065        };
1066        if flag_verified_only || query.is_some() {
1067            let s3str_like_cmd = format!("%{}%", query.unwrap_or(""));
1068            context
1069                .sql
1070                .query_map(
1071                    "SELECT c.id, c.addr FROM contacts c
1072                 LEFT JOIN acpeerstates ps ON c.addr=ps.addr  \
1073                 WHERE c.id>?
1074                 AND c.origin>=? \
1075                 AND c.blocked=0 \
1076                 AND (iif(c.name='',c.authname,c.name) LIKE ? OR c.addr LIKE ?) \
1077                 AND (1=? OR LENGTH(ps.verified_key_fingerprint)!=0)  \
1078                 ORDER BY c.last_seen DESC, c.id DESC;",
1079                    (
1080                        ContactId::LAST_SPECIAL,
1081                        minimal_origin,
1082                        &s3str_like_cmd,
1083                        &s3str_like_cmd,
1084                        if flag_verified_only { 0i32 } else { 1i32 },
1085                    ),
1086                    |row| {
1087                        let id: ContactId = row.get(0)?;
1088                        let addr: String = row.get(1)?;
1089                        Ok((id, addr))
1090                    },
1091                    |rows| {
1092                        for row in rows {
1093                            let (id, addr) = row?;
1094                            if !self_addrs.contains(&addr) {
1095                                ret.push(id);
1096                            }
1097                        }
1098                        Ok(())
1099                    },
1100                )
1101                .await?;
1102
1103            if let Some(query) = query {
1104                let self_addr = context
1105                    .get_config(Config::ConfiguredAddr)
1106                    .await?
1107                    .unwrap_or_default();
1108                let self_name = context
1109                    .get_config(Config::Displayname)
1110                    .await?
1111                    .unwrap_or_default();
1112                let self_name2 = stock_str::self_msg(context);
1113
1114                if self_addr.contains(query)
1115                    || self_name.contains(query)
1116                    || self_name2.await.contains(query)
1117                {
1118                    add_self = true;
1119                }
1120            } else {
1121                add_self = true;
1122            }
1123        } else {
1124            add_self = true;
1125
1126            context
1127                .sql
1128                .query_map(
1129                    "SELECT id, addr FROM contacts
1130                 WHERE id>?
1131                 AND origin>=?
1132                 AND blocked=0
1133                 ORDER BY last_seen DESC, id DESC;",
1134                    (ContactId::LAST_SPECIAL, minimal_origin),
1135                    |row| {
1136                        let id: ContactId = row.get(0)?;
1137                        let addr: String = row.get(1)?;
1138                        Ok((id, addr))
1139                    },
1140                    |rows| {
1141                        for row in rows {
1142                            let (id, addr) = row?;
1143                            if !self_addrs.contains(&addr) {
1144                                ret.push(id);
1145                            }
1146                        }
1147                        Ok(())
1148                    },
1149                )
1150                .await?;
1151        }
1152
1153        if flag_add_self && add_self {
1154            ret.push(ContactId::SELF);
1155        }
1156
1157        Ok(ret)
1158    }
1159
1160    /// Adds blocked mailinglists as contacts
1161    /// to allow unblocking them as if they are contacts
1162    /// (this way, only one unblock-ffi is needed and only one set of ui-functions,
1163    /// from the users perspective,
1164    /// there is not much difference in an email- and a mailinglist-address)
1165    async fn update_blocked_mailinglist_contacts(context: &Context) -> Result<()> {
1166        context
1167            .sql
1168            .transaction(move |transaction| {
1169                let mut stmt = transaction
1170                    .prepare("SELECT name, grpid FROM chats WHERE type=? AND blocked=?")?;
1171                let rows = stmt.query_map((Chattype::Mailinglist, Blocked::Yes), |row| {
1172                    let name: String = row.get(0)?;
1173                    let grpid: String = row.get(1)?;
1174                    Ok((name, grpid))
1175                })?;
1176                let blocked_mailinglists = rows.collect::<std::result::Result<Vec<_>, _>>()?;
1177                for (name, grpid) in blocked_mailinglists {
1178                    let count = transaction.query_row(
1179                        "SELECT COUNT(id) FROM contacts WHERE addr=?",
1180                        [&grpid],
1181                        |row| {
1182                            let count: isize = row.get(0)?;
1183                            Ok(count)
1184                        },
1185                    )?;
1186                    if count == 0 {
1187                        transaction.execute("INSERT INTO contacts (addr) VALUES (?)", [&grpid])?;
1188                    }
1189
1190                    // Always do an update in case the blocking is reset or name is changed.
1191                    transaction.execute(
1192                        "UPDATE contacts SET name=?, origin=?, blocked=1 WHERE addr=?",
1193                        (&name, Origin::MailinglistAddress, &grpid),
1194                    )?;
1195                }
1196                Ok(())
1197            })
1198            .await?;
1199        Ok(())
1200    }
1201
1202    /// Returns number of blocked contacts.
1203    pub async fn get_blocked_cnt(context: &Context) -> Result<usize> {
1204        let count = context
1205            .sql
1206            .count(
1207                "SELECT COUNT(*) FROM contacts WHERE id>? AND blocked!=0",
1208                (ContactId::LAST_SPECIAL,),
1209            )
1210            .await?;
1211        Ok(count)
1212    }
1213
1214    /// Get blocked contacts.
1215    pub async fn get_all_blocked(context: &Context) -> Result<Vec<ContactId>> {
1216        Contact::update_blocked_mailinglist_contacts(context)
1217            .await
1218            .context("cannot update blocked mailinglist contacts")?;
1219
1220        let list = context
1221            .sql
1222            .query_map(
1223                "SELECT id FROM contacts WHERE id>? AND blocked!=0 ORDER BY last_seen DESC, id DESC;",
1224                (ContactId::LAST_SPECIAL,),
1225                |row| row.get::<_, ContactId>(0),
1226                |ids| {
1227                    ids.collect::<std::result::Result<Vec<_>, _>>()
1228                        .map_err(Into::into)
1229                },
1230            )
1231            .await?;
1232        Ok(list)
1233    }
1234
1235    /// Returns a textual summary of the encryption state for the contact.
1236    ///
1237    /// This function returns a string explaining the encryption state
1238    /// of the contact and if the connection is encrypted the
1239    /// fingerprints of the keys involved.
1240    pub async fn get_encrinfo(context: &Context, contact_id: ContactId) -> Result<String> {
1241        ensure!(
1242            !contact_id.is_special(),
1243            "Can not provide encryption info for special contact"
1244        );
1245
1246        let contact = Contact::get_by_id(context, contact_id).await?;
1247        let addr = context
1248            .get_config(Config::ConfiguredAddr)
1249            .await?
1250            .unwrap_or_default();
1251        let peerstate = Peerstate::from_addr(context, &contact.addr).await?;
1252
1253        let Some(peerstate) = peerstate.filter(|peerstate| peerstate.peek_key(false).is_some())
1254        else {
1255            return Ok(stock_str::encr_none(context).await);
1256        };
1257
1258        let stock_message = match peerstate.prefer_encrypt {
1259            EncryptPreference::Mutual => stock_str::e2e_preferred(context).await,
1260            EncryptPreference::NoPreference => stock_str::e2e_available(context).await,
1261            EncryptPreference::Reset => stock_str::encr_none(context).await,
1262        };
1263
1264        let finger_prints = stock_str::finger_prints(context).await;
1265        let mut ret = format!("{stock_message}.\n{finger_prints}:");
1266
1267        let fingerprint_self = load_self_public_key(context)
1268            .await?
1269            .dc_fingerprint()
1270            .to_string();
1271        let fingerprint_other_verified = peerstate
1272            .peek_key(true)
1273            .map(|k| k.dc_fingerprint().to_string())
1274            .unwrap_or_default();
1275        let fingerprint_other_unverified = peerstate
1276            .peek_key(false)
1277            .map(|k| k.dc_fingerprint().to_string())
1278            .unwrap_or_default();
1279        if addr < peerstate.addr {
1280            cat_fingerprint(
1281                &mut ret,
1282                &stock_str::self_msg(context).await,
1283                &addr,
1284                &fingerprint_self,
1285                "",
1286            );
1287            cat_fingerprint(
1288                &mut ret,
1289                contact.get_display_name(),
1290                &peerstate.addr,
1291                &fingerprint_other_verified,
1292                &fingerprint_other_unverified,
1293            );
1294        } else {
1295            cat_fingerprint(
1296                &mut ret,
1297                contact.get_display_name(),
1298                &peerstate.addr,
1299                &fingerprint_other_verified,
1300                &fingerprint_other_unverified,
1301            );
1302            cat_fingerprint(
1303                &mut ret,
1304                &stock_str::self_msg(context).await,
1305                &addr,
1306                &fingerprint_self,
1307                "",
1308            );
1309        }
1310
1311        Ok(ret)
1312    }
1313
1314    /// Delete a contact so that it disappears from the corresponding lists.
1315    /// Depending on whether there are ongoing chats, deletion is done by physical deletion or hiding.
1316    /// The contact is deleted from the local device.
1317    ///
1318    /// May result in a `#DC_EVENT_CONTACTS_CHANGED` event.
1319    pub async fn delete(context: &Context, contact_id: ContactId) -> Result<()> {
1320        ensure!(!contact_id.is_special(), "Can not delete special contact");
1321
1322        context
1323            .sql
1324            .transaction(move |transaction| {
1325                // make sure, the transaction starts with a write command and becomes EXCLUSIVE by that -
1326                // upgrading later may be impossible by races.
1327                let deleted_contacts = transaction.execute(
1328                    "DELETE FROM contacts WHERE id=?
1329                     AND (SELECT COUNT(*) FROM chats_contacts WHERE contact_id=?)=0;",
1330                    (contact_id, contact_id),
1331                )?;
1332                if deleted_contacts == 0 {
1333                    transaction.execute(
1334                        "UPDATE contacts SET origin=? WHERE id=?;",
1335                        (Origin::Hidden, contact_id),
1336                    )?;
1337                }
1338                Ok(())
1339            })
1340            .await?;
1341
1342        context.emit_event(EventType::ContactsChanged(None));
1343        Ok(())
1344    }
1345
1346    /// Updates `param` column in the database.
1347    pub async fn update_param(&self, context: &Context) -> Result<()> {
1348        context
1349            .sql
1350            .execute(
1351                "UPDATE contacts SET param=? WHERE id=?",
1352                (self.param.to_string(), self.id),
1353            )
1354            .await?;
1355        Ok(())
1356    }
1357
1358    /// Updates `status` column in the database.
1359    pub async fn update_status(&self, context: &Context) -> Result<()> {
1360        context
1361            .sql
1362            .execute(
1363                "UPDATE contacts SET status=? WHERE id=?",
1364                (&self.status, self.id),
1365            )
1366            .await?;
1367        Ok(())
1368    }
1369
1370    /// Get the ID of the contact.
1371    pub fn get_id(&self) -> ContactId {
1372        self.id
1373    }
1374
1375    /// Get email address. The email address is always set for a contact.
1376    pub fn get_addr(&self) -> &str {
1377        &self.addr
1378    }
1379
1380    /// Get name authorized by the contact.
1381    pub fn get_authname(&self) -> &str {
1382        &self.authname
1383    }
1384
1385    /// Get the contact name. This is the name as modified by the local user.
1386    /// May be an empty string.
1387    ///
1388    /// This name is typically used in a form where the user can edit the name of a contact.
1389    /// To get a fine name to display in lists etc., use `Contact::get_display_name` or `Contact::get_name_n_addr`.
1390    pub fn get_name(&self) -> &str {
1391        &self.name
1392    }
1393
1394    /// Get display name. This is the name as defined by the contact himself,
1395    /// modified by the user or, if both are unset, the email address.
1396    ///
1397    /// This name is typically used in lists.
1398    /// To get the name editable in a formular, use `Contact::get_name`.
1399    pub fn get_display_name(&self) -> &str {
1400        if !self.name.is_empty() {
1401            return &self.name;
1402        }
1403        if !self.authname.is_empty() {
1404            return &self.authname;
1405        }
1406        &self.addr
1407    }
1408
1409    /// Get authorized name or address.
1410    ///
1411    /// This string is suitable for sending over email
1412    /// as it does not leak the locally set name.
1413    pub(crate) fn get_authname_or_addr(&self) -> String {
1414        if !self.authname.is_empty() {
1415            (&self.authname).into()
1416        } else {
1417            (&self.addr).into()
1418        }
1419    }
1420
1421    /// Get a summary of name and address.
1422    ///
1423    /// The returned string is either "Name (email@domain.com)" or just
1424    /// "email@domain.com" if the name is unset.
1425    ///
1426    /// The result should only be used locally and never sent over the network
1427    /// as it leaks the local contact name.
1428    ///
1429    /// The summary is typically used when asking the user something about the contact.
1430    /// The attached email address makes the question unique, eg. "Chat with Alan Miller (am@uniquedomain.com)?"
1431    pub fn get_name_n_addr(&self) -> String {
1432        if !self.name.is_empty() {
1433            format!("{} ({})", self.name, self.addr)
1434        } else if !self.authname.is_empty() {
1435            format!("{} ({})", self.authname, self.addr)
1436        } else {
1437            (&self.addr).into()
1438        }
1439    }
1440
1441    /// Get the contact's profile image.
1442    /// This is the image set by each remote user on their own
1443    /// using set_config(context, "selfavatar", image).
1444    pub async fn get_profile_image(&self, context: &Context) -> Result<Option<PathBuf>> {
1445        if self.id == ContactId::SELF {
1446            if let Some(p) = context.get_config(Config::Selfavatar).await? {
1447                return Ok(Some(PathBuf::from(p))); // get_config() calls get_abs_path() internally already
1448            }
1449        } else if let Some(image_rel) = self.param.get(Param::ProfileImage) {
1450            if !image_rel.is_empty() {
1451                return Ok(Some(get_abs_path(context, Path::new(image_rel))));
1452            }
1453        }
1454        Ok(None)
1455    }
1456
1457    /// Get a color for the contact.
1458    /// The color is calculated from the contact's email address
1459    /// and can be used for an fallback avatar with white initials
1460    /// as well as for headlines in bubbles of group chats.
1461    pub fn get_color(&self) -> u32 {
1462        str_to_color(&self.addr.to_lowercase())
1463    }
1464
1465    /// Gets the contact's status.
1466    ///
1467    /// Status is the last signature received in a message from this contact.
1468    pub fn get_status(&self) -> &str {
1469        self.status.as_str()
1470    }
1471
1472    /// Returns whether end-to-end encryption to the contact is available.
1473    pub async fn e2ee_avail(&self, context: &Context) -> Result<bool> {
1474        if self.id == ContactId::SELF {
1475            return Ok(true);
1476        }
1477        let Some(peerstate) = Peerstate::from_addr(context, &self.addr).await? else {
1478            return Ok(false);
1479        };
1480        Ok(peerstate.peek_key(false).is_some())
1481    }
1482
1483    /// Returns true if the contact
1484    /// can be added to verified chats,
1485    /// i.e. has a verified key
1486    /// and Autocrypt key matches the verified key.
1487    ///
1488    /// If contact is verified
1489    /// UI should display green checkmark after the contact name
1490    /// in contact list items and
1491    /// in chat member list items.
1492    ///
1493    /// In contact profile view, us this function only if there is no chat with the contact,
1494    /// otherwise use is_chat_protected().
1495    /// Use [Self::get_verifier_id] to display the verifier contact
1496    /// in the info section of the contact profile.
1497    pub async fn is_verified(&self, context: &Context) -> Result<bool> {
1498        // We're always sort of secured-verified as we could verify the key on this device any time with the key
1499        // on this device
1500        if self.id == ContactId::SELF {
1501            return Ok(true);
1502        }
1503
1504        let Some(peerstate) = Peerstate::from_addr(context, &self.addr).await? else {
1505            return Ok(false);
1506        };
1507
1508        let forward_verified = peerstate.is_using_verified_key();
1509        let backward_verified = peerstate.is_backward_verified(context).await?;
1510        Ok(forward_verified && backward_verified)
1511    }
1512
1513    /// Returns true if we have a verified key for the contact
1514    /// and it is the same as Autocrypt key.
1515    /// This is enough to send messages to the contact in verified chat
1516    /// and verify received messages, but not enough to display green checkmark
1517    /// or add the contact to verified groups.
1518    pub async fn is_forward_verified(&self, context: &Context) -> Result<bool> {
1519        if self.id == ContactId::SELF {
1520            return Ok(true);
1521        }
1522
1523        let Some(peerstate) = Peerstate::from_addr(context, &self.addr).await? else {
1524            return Ok(false);
1525        };
1526
1527        Ok(peerstate.is_using_verified_key())
1528    }
1529
1530    /// Returns the `ContactId` that verified the contact.
1531    ///
1532    /// If the function returns non-zero result,
1533    /// display green checkmark in the profile and "Introduced by ..." line
1534    /// with the name and address of the contact
1535    /// formatted by [Self::get_name_n_addr].
1536    ///
1537    /// If this function returns a verifier,
1538    /// this does not necessarily mean
1539    /// you can add the contact to verified chats.
1540    /// Use [Self::is_verified] to check
1541    /// if a contact can be added to a verified chat instead.
1542    pub async fn get_verifier_id(&self, context: &Context) -> Result<Option<ContactId>> {
1543        let Some(verifier_addr) = Peerstate::from_addr(context, self.get_addr())
1544            .await?
1545            .and_then(|peerstate| peerstate.get_verifier().map(|addr| addr.to_owned()))
1546        else {
1547            return Ok(None);
1548        };
1549
1550        if addr_cmp(&verifier_addr, &self.addr) {
1551            // Contact is directly verified via QR code.
1552            return Ok(Some(ContactId::SELF));
1553        }
1554
1555        match Contact::lookup_id_by_addr(context, &verifier_addr, Origin::Unknown).await? {
1556            Some(contact_id) => Ok(Some(contact_id)),
1557            None => {
1558                let addr = &self.addr;
1559                warn!(context, "Could not lookup contact with address {verifier_addr} which introduced {addr}.");
1560                Ok(None)
1561            }
1562        }
1563    }
1564
1565    /// Returns if the contact profile title should display a green checkmark.
1566    ///
1567    /// This generally should be consistent with the 1:1 chat with the contact
1568    /// so 1:1 chat with the contact and the contact profile
1569    /// either both display the green checkmark or both don't display a green checkmark.
1570    ///
1571    /// UI often knows beforehand if a chat exists and can also call
1572    /// `chat.is_protected()` (if there is a chat)
1573    /// or `contact.is_verified()` (if there is no chat) directly.
1574    /// This is often easier and also skips some database calls.
1575    pub async fn is_profile_verified(&self, context: &Context) -> Result<bool> {
1576        let contact_id = self.id;
1577
1578        if let Some(ChatIdBlocked { id: chat_id, .. }) =
1579            ChatIdBlocked::lookup_by_contact(context, contact_id).await?
1580        {
1581            Ok(chat_id.is_protected(context).await? == ProtectionStatus::Protected)
1582        } else {
1583            // 1:1 chat does not exist.
1584            Ok(self.is_verified(context).await?)
1585        }
1586    }
1587
1588    /// Returns the number of real (i.e. non-special) contacts in the database.
1589    pub async fn get_real_cnt(context: &Context) -> Result<usize> {
1590        if !context.sql.is_open().await {
1591            return Ok(0);
1592        }
1593
1594        let count = context
1595            .sql
1596            .count(
1597                "SELECT COUNT(*) FROM contacts WHERE id>?;",
1598                (ContactId::LAST_SPECIAL,),
1599            )
1600            .await?;
1601        Ok(count)
1602    }
1603
1604    /// Returns true if a contact with this ID exists.
1605    pub async fn real_exists_by_id(context: &Context, contact_id: ContactId) -> Result<bool> {
1606        if contact_id.is_special() {
1607            return Ok(false);
1608        }
1609
1610        let exists = context
1611            .sql
1612            .exists("SELECT COUNT(*) FROM contacts WHERE id=?;", (contact_id,))
1613            .await?;
1614        Ok(exists)
1615    }
1616}
1617
1618// Updates the names of the chats which use the contact name.
1619//
1620// This is one of the few duplicated data, however, getting the chat list is easier this way.
1621fn update_chat_names(
1622    context: &Context,
1623    transaction: &rusqlite::Connection,
1624    contact_id: ContactId,
1625) -> Result<()> {
1626    let chat_id: Option<ChatId> = transaction.query_row(
1627            "SELECT id FROM chats WHERE type=? AND id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?)",
1628            (Chattype::Single, contact_id),
1629            |row| {
1630                let chat_id: ChatId = row.get(0)?;
1631                Ok(chat_id)
1632            }
1633        ).optional()?;
1634
1635    if let Some(chat_id) = chat_id {
1636        let (addr, name, authname) = transaction.query_row(
1637            "SELECT addr, name, authname
1638                     FROM contacts
1639                     WHERE id=?",
1640            (contact_id,),
1641            |row| {
1642                let addr: String = row.get(0)?;
1643                let name: String = row.get(1)?;
1644                let authname: String = row.get(2)?;
1645                Ok((addr, name, authname))
1646            },
1647        )?;
1648
1649        let chat_name = if !name.is_empty() {
1650            name
1651        } else if !authname.is_empty() {
1652            authname
1653        } else {
1654            addr
1655        };
1656
1657        let count = transaction.execute(
1658            "UPDATE chats SET name=?1 WHERE id=?2 AND name!=?1",
1659            (chat_name, chat_id),
1660        )?;
1661
1662        if count > 0 {
1663            // Chat name updated
1664            context.emit_event(EventType::ChatModified(chat_id));
1665            chatlist_events::emit_chatlist_items_changed_for_contact(context, contact_id);
1666        }
1667    }
1668
1669    Ok(())
1670}
1671
1672pub(crate) async fn set_blocked(
1673    context: &Context,
1674    sync: sync::Sync,
1675    contact_id: ContactId,
1676    new_blocking: bool,
1677) -> Result<()> {
1678    ensure!(
1679        !contact_id.is_special(),
1680        "Can't block special contact {}",
1681        contact_id
1682    );
1683    let contact = Contact::get_by_id(context, contact_id).await?;
1684
1685    if contact.blocked != new_blocking {
1686        context
1687            .sql
1688            .execute(
1689                "UPDATE contacts SET blocked=? WHERE id=?;",
1690                (i32::from(new_blocking), contact_id),
1691            )
1692            .await?;
1693
1694        // also (un)block all chats with _only_ this contact - we do not delete them to allow a
1695        // non-destructive blocking->unblocking.
1696        // (Maybe, beside normal chats (type=100) we should also block group chats with only this user.
1697        // However, I'm not sure about this point; it may be confusing if the user wants to add other people;
1698        // this would result in recreating the same group...)
1699        if context
1700            .sql
1701            .execute(
1702                r#"
1703UPDATE chats
1704SET blocked=?
1705WHERE type=? AND id IN (
1706  SELECT chat_id FROM chats_contacts WHERE contact_id=?
1707);
1708"#,
1709                (new_blocking, Chattype::Single, contact_id),
1710            )
1711            .await
1712            .is_ok()
1713        {
1714            Contact::mark_noticed(context, contact_id).await?;
1715            context.emit_event(EventType::ContactsChanged(Some(contact_id)));
1716        }
1717
1718        // also unblock mailinglist
1719        // if the contact is a mailinglist address explicitly created to allow unblocking
1720        if !new_blocking && contact.origin == Origin::MailinglistAddress {
1721            if let Some((chat_id, _, _)) =
1722                chat::get_chat_id_by_grpid(context, &contact.addr).await?
1723            {
1724                chat_id.unblock_ex(context, Nosync).await?;
1725            }
1726        }
1727
1728        if sync.into() {
1729            let action = match new_blocking {
1730                true => chat::SyncAction::Block,
1731                false => chat::SyncAction::Unblock,
1732            };
1733            chat::sync(
1734                context,
1735                chat::SyncId::ContactAddr(contact.addr.clone()),
1736                action,
1737            )
1738            .await
1739            .log_err(context)
1740            .ok();
1741        }
1742    }
1743
1744    chatlist_events::emit_chatlist_changed(context);
1745    Ok(())
1746}
1747
1748/// Set profile image for a contact.
1749///
1750/// The given profile image is expected to be already in the blob directory
1751/// as profile images can be set only by receiving messages, this should be always the case, however.
1752///
1753/// For contact SELF, the image is not saved in the contact-database but as Config::Selfavatar;
1754/// this typically happens if we see message with our own profile image.
1755pub(crate) async fn set_profile_image(
1756    context: &Context,
1757    contact_id: ContactId,
1758    profile_image: &AvatarAction,
1759    was_encrypted: bool,
1760) -> Result<()> {
1761    let mut contact = Contact::get_by_id(context, contact_id).await?;
1762    let changed = match profile_image {
1763        AvatarAction::Change(profile_image) => {
1764            if contact_id == ContactId::SELF {
1765                if was_encrypted {
1766                    context
1767                        .set_config_ex(Nosync, Config::Selfavatar, Some(profile_image))
1768                        .await?;
1769                } else {
1770                    info!(context, "Do not use unencrypted selfavatar.");
1771                }
1772            } else {
1773                contact.param.set(Param::ProfileImage, profile_image);
1774            }
1775            true
1776        }
1777        AvatarAction::Delete => {
1778            if contact_id == ContactId::SELF {
1779                if was_encrypted {
1780                    context
1781                        .set_config_ex(Nosync, Config::Selfavatar, None)
1782                        .await?;
1783                } else {
1784                    info!(context, "Do not use unencrypted selfavatar deletion.");
1785                }
1786            } else {
1787                contact.param.remove(Param::ProfileImage);
1788            }
1789            true
1790        }
1791    };
1792    if changed {
1793        contact.update_param(context).await?;
1794        context.emit_event(EventType::ContactsChanged(Some(contact_id)));
1795        chatlist_events::emit_chatlist_item_changed_for_contact_chat(context, contact_id).await;
1796    }
1797    Ok(())
1798}
1799
1800/// Sets contact status.
1801///
1802/// For contact SELF, the status is not saved in the contact table, but as Config::Selfstatus.  This
1803/// is only done if message is sent from Delta Chat and it is encrypted, to synchronize signature
1804/// between Delta Chat devices.
1805pub(crate) async fn set_status(
1806    context: &Context,
1807    contact_id: ContactId,
1808    status: String,
1809    encrypted: bool,
1810    has_chat_version: bool,
1811) -> Result<()> {
1812    if contact_id == ContactId::SELF {
1813        if encrypted && has_chat_version {
1814            context
1815                .set_config_ex(Nosync, Config::Selfstatus, Some(&status))
1816                .await?;
1817        }
1818    } else {
1819        let mut contact = Contact::get_by_id(context, contact_id).await?;
1820
1821        if contact.status != status {
1822            contact.status = status;
1823            contact.update_status(context).await?;
1824            context.emit_event(EventType::ContactsChanged(Some(contact_id)));
1825        }
1826    }
1827    Ok(())
1828}
1829
1830/// Updates last seen timestamp of the contact if it is earlier than the given `timestamp`.
1831pub(crate) async fn update_last_seen(
1832    context: &Context,
1833    contact_id: ContactId,
1834    timestamp: i64,
1835) -> Result<()> {
1836    ensure!(
1837        !contact_id.is_special(),
1838        "Can not update special contact last seen timestamp"
1839    );
1840
1841    if context
1842        .sql
1843        .execute(
1844            "UPDATE contacts SET last_seen = ?1 WHERE last_seen < ?1 AND id = ?2",
1845            (timestamp, contact_id),
1846        )
1847        .await?
1848        > 0
1849        && timestamp > time() - SEEN_RECENTLY_SECONDS
1850    {
1851        context.emit_event(EventType::ContactsChanged(Some(contact_id)));
1852        context
1853            .scheduler
1854            .interrupt_recently_seen(contact_id, timestamp)
1855            .await;
1856    }
1857    Ok(())
1858}
1859
1860fn cat_fingerprint(
1861    ret: &mut String,
1862    name: &str,
1863    addr: &str,
1864    fingerprint_verified: &str,
1865    fingerprint_unverified: &str,
1866) {
1867    *ret += &format!(
1868        "\n\n{} ({}):\n{}",
1869        name,
1870        addr,
1871        if !fingerprint_verified.is_empty() {
1872            fingerprint_verified
1873        } else {
1874            fingerprint_unverified
1875        },
1876    );
1877    if !fingerprint_verified.is_empty()
1878        && !fingerprint_unverified.is_empty()
1879        && fingerprint_verified != fingerprint_unverified
1880    {
1881        *ret += &format!("\n\n{name} (alternative):\n{fingerprint_unverified}");
1882    }
1883}
1884
1885fn split_address_book(book: &str) -> Vec<(&str, &str)> {
1886    book.lines()
1887        .collect::<Vec<&str>>()
1888        .chunks(2)
1889        .filter_map(|chunk| {
1890            let name = chunk.first()?;
1891            let addr = chunk.get(1)?;
1892            Some((*name, *addr))
1893        })
1894        .collect()
1895}
1896
1897#[derive(Debug)]
1898pub(crate) struct RecentlySeenInterrupt {
1899    contact_id: ContactId,
1900    timestamp: i64,
1901}
1902
1903#[derive(Debug)]
1904pub(crate) struct RecentlySeenLoop {
1905    /// Task running "recently seen" loop.
1906    handle: task::JoinHandle<()>,
1907
1908    interrupt_send: Sender<RecentlySeenInterrupt>,
1909}
1910
1911impl RecentlySeenLoop {
1912    pub(crate) fn new(context: Context) -> Self {
1913        let (interrupt_send, interrupt_recv) = channel::bounded(1);
1914
1915        let handle = task::spawn(Self::run(context, interrupt_recv));
1916        Self {
1917            handle,
1918            interrupt_send,
1919        }
1920    }
1921
1922    async fn run(context: Context, interrupt: Receiver<RecentlySeenInterrupt>) {
1923        type MyHeapElem = (Reverse<i64>, ContactId);
1924
1925        let now = SystemTime::now();
1926        let now_ts = now
1927            .duration_since(SystemTime::UNIX_EPOCH)
1928            .unwrap_or_default()
1929            .as_secs() as i64;
1930
1931        // Priority contains all recently seen sorted by the timestamp
1932        // when they become not recently seen.
1933        //
1934        // Initialize with contacts which are currently seen, but will
1935        // become unseen in the future.
1936        let mut unseen_queue: BinaryHeap<MyHeapElem> = context
1937            .sql
1938            .query_map(
1939                "SELECT id, last_seen FROM contacts
1940                 WHERE last_seen > ?",
1941                (now_ts - SEEN_RECENTLY_SECONDS,),
1942                |row| {
1943                    let contact_id: ContactId = row.get("id")?;
1944                    let last_seen: i64 = row.get("last_seen")?;
1945                    Ok((Reverse(last_seen + SEEN_RECENTLY_SECONDS), contact_id))
1946                },
1947                |rows| {
1948                    rows.collect::<std::result::Result<BinaryHeap<MyHeapElem>, _>>()
1949                        .map_err(Into::into)
1950                },
1951            )
1952            .await
1953            .unwrap_or_default();
1954
1955        loop {
1956            let now = SystemTime::now();
1957            let (until, contact_id) =
1958                if let Some((Reverse(timestamp), contact_id)) = unseen_queue.peek() {
1959                    (
1960                        UNIX_EPOCH
1961                            + Duration::from_secs((*timestamp).try_into().unwrap_or(u64::MAX))
1962                            + Duration::from_secs(1),
1963                        Some(contact_id),
1964                    )
1965                } else {
1966                    // Sleep for 24 hours.
1967                    (now + Duration::from_secs(86400), None)
1968                };
1969
1970            if let Ok(duration) = until.duration_since(now) {
1971                info!(
1972                    context,
1973                    "Recently seen loop waiting for {} or interrupt",
1974                    duration_to_str(duration)
1975                );
1976
1977                match timeout(duration, interrupt.recv()).await {
1978                    Err(_) => {
1979                        // Timeout, notify about contact.
1980                        if let Some(contact_id) = contact_id {
1981                            context.emit_event(EventType::ContactsChanged(Some(*contact_id)));
1982                            chatlist_events::emit_chatlist_item_changed_for_contact_chat(
1983                                &context,
1984                                *contact_id,
1985                            )
1986                            .await;
1987                            unseen_queue.pop();
1988                        }
1989                    }
1990                    Ok(Err(err)) => {
1991                        warn!(
1992                            context,
1993                            "Error receiving an interruption in recently seen loop: {}", err
1994                        );
1995                        // Maybe the sender side is closed.
1996                        // Terminate the loop to avoid looping indefinitely.
1997                        return;
1998                    }
1999                    Ok(Ok(RecentlySeenInterrupt {
2000                        contact_id,
2001                        timestamp,
2002                    })) => {
2003                        // Received an interrupt.
2004                        if contact_id != ContactId::UNDEFINED {
2005                            unseen_queue
2006                                .push((Reverse(timestamp + SEEN_RECENTLY_SECONDS), contact_id));
2007                        }
2008                    }
2009                }
2010            } else {
2011                info!(
2012                    context,
2013                    "Recently seen loop is not waiting, event is already due."
2014                );
2015
2016                // Event is already in the past.
2017                if let Some(contact_id) = contact_id {
2018                    context.emit_event(EventType::ContactsChanged(Some(*contact_id)));
2019                    chatlist_events::emit_chatlist_item_changed_for_contact_chat(
2020                        &context,
2021                        *contact_id,
2022                    )
2023                    .await;
2024                }
2025                unseen_queue.pop();
2026            }
2027        }
2028    }
2029
2030    pub(crate) fn try_interrupt(&self, contact_id: ContactId, timestamp: i64) {
2031        self.interrupt_send
2032            .try_send(RecentlySeenInterrupt {
2033                contact_id,
2034                timestamp,
2035            })
2036            .ok();
2037    }
2038
2039    #[cfg(test)]
2040    pub(crate) async fn interrupt(&self, contact_id: ContactId, timestamp: i64) {
2041        self.interrupt_send
2042            .send(RecentlySeenInterrupt {
2043                contact_id,
2044                timestamp,
2045            })
2046            .await
2047            .unwrap();
2048    }
2049
2050    pub(crate) async fn abort(self) {
2051        self.handle.abort();
2052
2053        // Await aborted task to ensure the `Future` is dropped
2054        // with all resources moved inside such as the `Context`
2055        // reference to `InnerContext`.
2056        self.handle.await.ok();
2057    }
2058}
2059
2060#[cfg(test)]
2061mod contact_tests;