deltachat/
contact.rs

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