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::{Context as _, Result, bail, ensure};
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, ContactAddress, VcardContact, addr_normalize, sanitize_name,
15    sanitize_name_and_addr,
16};
17use deltachat_derive::{FromSql, ToSql};
18use rusqlite::OptionalExtension;
19use serde::{Deserialize, Serialize};
20use tokio::task;
21use tokio::time::{Duration, timeout};
22
23use crate::blob::BlobObject;
24use crate::chat::{ChatId, ChatIdBlocked, ProtectionStatus};
25use crate::color::str_to_color;
26use crate::config::Config;
27use crate::constants::{self, Blocked, Chattype};
28use crate::context::Context;
29use crate::events::EventType;
30use crate::key::{
31    DcKey, Fingerprint, SignedPublicKey, load_self_public_key, self_fingerprint,
32    self_fingerprint_opt,
33};
34use crate::log::{LogExt, info, warn};
35use crate::message::MessageState;
36use crate::mimeparser::AvatarAction;
37use crate::param::{Param, Params};
38use crate::sync::{self, Sync::*};
39use crate::tools::{SystemTime, duration_to_str, get_abs_path, time};
40use crate::{chat, chatlist_events, ensure_and_debug_assert_ne, 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_configured().await? && context.is_self_addr(addr).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_configured().await? && context.is_self_addr(addr).await? {
864            return Ok((ContactId::SELF, sth_modified));
865        }
866
867        if !fingerprint.is_empty() && context.is_configured().await? {
868            let fingerprint_self = self_fingerprint(context)
869                .await
870                .context("self_fingerprint")?;
871            if fingerprint == fingerprint_self {
872                return Ok((ContactId::SELF, sth_modified));
873            }
874        }
875
876        let mut name = sanitize_name(name);
877        if origin <= Origin::OutgoingTo {
878            // The user may accidentally have written to a "noreply" address with another MUA:
879            if addr.contains("noreply")
880                || addr.contains("no-reply")
881                || addr.starts_with("notifications@")
882                // Filter out use-once addresses (like reply+AEJDGPOECLAP...@reply.github.com):
883                || (addr.len() > 50 && addr.contains('+'))
884            {
885                info!(context, "hiding contact {}", addr);
886                origin = Origin::Hidden;
887                // For these kind of email addresses, sender and address often don't belong together
888                // (like hocuri <notifications@github.com>). In this example, hocuri shouldn't
889                // be saved as the displayname for notifications@github.com.
890                name = "".to_string();
891            }
892        }
893
894        // If the origin indicates that user entered the contact manually, from the address book or
895        // from the QR-code scan (potentially from the address book of their other phone), then name
896        // should go into the "name" column and never into "authname" column, to avoid leaking it
897        // into the network.
898        let manual = matches!(
899            origin,
900            Origin::ManuallyCreated | Origin::AddressBook | Origin::UnhandledQrScan
901        );
902
903        let mut update_addr = false;
904
905        let row_id = context
906            .sql
907            .transaction(|transaction| {
908                let row = transaction
909                    .query_row(
910                        "SELECT id, name, addr, origin, authname
911                         FROM contacts
912                         WHERE fingerprint=?1 AND
913                         (?1<>'' OR addr=?2 COLLATE NOCASE)",
914                        (fingerprint, addr),
915                        |row| {
916                            let row_id: u32 = row.get(0)?;
917                            let row_name: String = row.get(1)?;
918                            let row_addr: String = row.get(2)?;
919                            let row_origin: Origin = row.get(3)?;
920                            let row_authname: String = row.get(4)?;
921
922                            Ok((row_id, row_name, row_addr, row_origin, row_authname))
923                        },
924                    )
925                    .optional()?;
926
927                let row_id;
928                if let Some((id, row_name, row_addr, row_origin, row_authname)) = row {
929                    let update_name = manual && name != row_name;
930                    let update_authname = !manual
931                        && name != row_authname
932                        && !name.is_empty()
933                        && (origin >= row_origin
934                            || origin == Origin::IncomingUnknownFrom
935                            || row_authname.is_empty());
936
937                    row_id = id;
938                    if origin >= row_origin && addr != row_addr {
939                        update_addr = true;
940                    }
941                    if update_name || update_authname || update_addr || origin > row_origin {
942                        let new_name = if update_name {
943                            name.to_string()
944                        } else {
945                            row_name
946                        };
947
948                        transaction.execute(
949                            "UPDATE contacts SET name=?, addr=?, origin=?, authname=? WHERE id=?;",
950                            (
951                                new_name,
952                                if update_addr {
953                                    addr.to_string()
954                                } else {
955                                    row_addr
956                                },
957                                if origin > row_origin {
958                                    origin
959                                } else {
960                                    row_origin
961                                },
962                                if update_authname {
963                                    name.to_string()
964                                } else {
965                                    row_authname
966                                },
967                                row_id,
968                            ),
969                        )?;
970
971                        if update_name || update_authname {
972                            let contact_id = ContactId::new(row_id);
973                            update_chat_names(context, transaction, contact_id)?;
974                        }
975                        sth_modified = Modifier::Modified;
976                    }
977                } else {
978                    let update_name = manual;
979                    let update_authname = !manual;
980
981                    transaction.execute(
982                        "INSERT INTO contacts (name, addr, fingerprint, origin, authname)
983                         VALUES (?, ?, ?, ?, ?);",
984                        (
985                            if update_name { &name } else { "" },
986                            &addr,
987                            fingerprint,
988                            origin,
989                            if update_authname { &name } else { "" },
990                        ),
991                    )?;
992
993                    sth_modified = Modifier::Created;
994                    row_id = u32::try_from(transaction.last_insert_rowid())?;
995                    if fingerprint.is_empty() {
996                        info!(context, "Added contact id={row_id} addr={addr}.");
997                    } else {
998                        info!(
999                            context,
1000                            "Added contact id={row_id} fpr={fingerprint} addr={addr}."
1001                        );
1002                    }
1003                }
1004                Ok(row_id)
1005            })
1006            .await?;
1007
1008        let contact_id = ContactId::new(row_id);
1009
1010        Ok((contact_id, sth_modified))
1011    }
1012
1013    /// Add a number of contacts.
1014    ///
1015    /// Typically used to add the whole address book from the OS. As names here are typically not
1016    /// well formatted, we call `normalize()` for each name given.
1017    ///
1018    /// No email-address is added twice.
1019    /// Trying to add email-addresses that are already in the contact list,
1020    /// results in updating the name unless the name was changed manually by the user.
1021    /// If any email-address or any name is really updated,
1022    /// the event `DC_EVENT_CONTACTS_CHANGED` is sent.
1023    ///
1024    /// To add a single contact entered by the user, you should prefer `Contact::create`,
1025    /// however, for adding a bunch of addresses, this function is much faster.
1026    ///
1027    /// The `addr_book` is a multiline string in the format `Name one\nAddress one\nName two\nAddress two`.
1028    ///
1029    /// Returns the number of modified contacts.
1030    pub async fn add_address_book(context: &Context, addr_book: &str) -> Result<usize> {
1031        let mut modify_cnt = 0;
1032
1033        for (name, addr) in split_address_book(addr_book) {
1034            let (name, addr) = sanitize_name_and_addr(name, addr);
1035            match ContactAddress::new(&addr) {
1036                Ok(addr) => {
1037                    match Contact::add_or_lookup(context, &name, &addr, Origin::AddressBook).await {
1038                        Ok((_, modified)) => {
1039                            if modified != Modifier::None {
1040                                modify_cnt += 1
1041                            }
1042                        }
1043                        Err(err) => {
1044                            warn!(
1045                                context,
1046                                "Failed to add address {} from address book: {}", addr, err
1047                            );
1048                        }
1049                    }
1050                }
1051                Err(err) => {
1052                    warn!(context, "{:#}.", err);
1053                }
1054            }
1055        }
1056        if modify_cnt > 0 {
1057            context.emit_event(EventType::ContactsChanged(None));
1058        }
1059
1060        Ok(modify_cnt)
1061    }
1062
1063    /// Returns known and unblocked contacts.
1064    ///
1065    /// To get information about a single contact, see get_contact().
1066    /// By default, key-contacts are listed.
1067    ///
1068    /// * `listflags` - A combination of flags:
1069    ///   - `DC_GCL_ADD_SELF` - Add SELF unless filtered by other parameters.
1070    ///   - `DC_GCL_ADDRESS` - List address-contacts instead of key-contacts.
1071    /// * `query` - A string to filter the list.
1072    pub async fn get_all(
1073        context: &Context,
1074        listflags: u32,
1075        query: Option<&str>,
1076    ) -> Result<Vec<ContactId>> {
1077        let self_addrs = context
1078            .get_all_self_addrs()
1079            .await?
1080            .into_iter()
1081            .collect::<HashSet<_>>();
1082        let mut add_self = false;
1083        let mut ret = Vec::new();
1084        let flag_add_self = (listflags & constants::DC_GCL_ADD_SELF) != 0;
1085        let flag_address = (listflags & constants::DC_GCL_ADDRESS) != 0;
1086        let minimal_origin = if context.get_config_bool(Config::Bot).await? {
1087            Origin::Unknown
1088        } else {
1089            Origin::IncomingReplyTo
1090        };
1091        if query.is_some() {
1092            let s3str_like_cmd = format!("%{}%", query.unwrap_or(""));
1093            context
1094                .sql
1095                .query_map(
1096                    "SELECT c.id, c.addr FROM contacts c
1097                 WHERE c.id>?
1098                 AND (c.fingerprint='')=?
1099                 AND c.origin>=? \
1100                 AND c.blocked=0 \
1101                 AND (iif(c.name='',c.authname,c.name) LIKE ? OR c.addr LIKE ?) \
1102                 ORDER BY c.last_seen DESC, c.id DESC;",
1103                    (
1104                        ContactId::LAST_SPECIAL,
1105                        flag_address,
1106                        minimal_origin,
1107                        &s3str_like_cmd,
1108                        &s3str_like_cmd,
1109                    ),
1110                    |row| {
1111                        let id: ContactId = row.get(0)?;
1112                        let addr: String = row.get(1)?;
1113                        Ok((id, addr))
1114                    },
1115                    |rows| {
1116                        for row in rows {
1117                            let (id, addr) = row?;
1118                            if !self_addrs.contains(&addr) {
1119                                ret.push(id);
1120                            }
1121                        }
1122                        Ok(())
1123                    },
1124                )
1125                .await?;
1126
1127            if let Some(query) = query {
1128                let self_addr = context
1129                    .get_config(Config::ConfiguredAddr)
1130                    .await?
1131                    .unwrap_or_default();
1132                let self_name = context
1133                    .get_config(Config::Displayname)
1134                    .await?
1135                    .unwrap_or_default();
1136                let self_name2 = stock_str::self_msg(context);
1137
1138                if self_addr.contains(query)
1139                    || self_name.contains(query)
1140                    || self_name2.await.contains(query)
1141                {
1142                    add_self = true;
1143                }
1144            } else {
1145                add_self = true;
1146            }
1147        } else {
1148            add_self = true;
1149
1150            context
1151                .sql
1152                .query_map(
1153                    "SELECT id, addr FROM contacts
1154                 WHERE id>?
1155                 AND (fingerprint='')=?
1156                 AND origin>=?
1157                 AND blocked=0
1158                 ORDER BY last_seen DESC, id DESC;",
1159                    (ContactId::LAST_SPECIAL, flag_address, minimal_origin),
1160                    |row| {
1161                        let id: ContactId = row.get(0)?;
1162                        let addr: String = row.get(1)?;
1163                        Ok((id, addr))
1164                    },
1165                    |rows| {
1166                        for row in rows {
1167                            let (id, addr) = row?;
1168                            if !self_addrs.contains(&addr) {
1169                                ret.push(id);
1170                            }
1171                        }
1172                        Ok(())
1173                    },
1174                )
1175                .await?;
1176        }
1177
1178        if flag_add_self && add_self {
1179            ret.push(ContactId::SELF);
1180        }
1181
1182        Ok(ret)
1183    }
1184
1185    /// Adds blocked mailinglists and broadcast channels as pseudo-contacts
1186    /// to allow unblocking them as if they are contacts
1187    /// (this way, only one unblock-ffi is needed and only one set of ui-functions,
1188    /// from the users perspective,
1189    /// there is not much difference in an email- and a mailinglist-address)
1190    async fn update_blocked_mailinglist_contacts(context: &Context) -> Result<()> {
1191        context
1192            .sql
1193            .transaction(move |transaction| {
1194                let mut stmt = transaction.prepare(
1195                    "SELECT name, grpid, type FROM chats WHERE (type=? OR type=?) AND blocked=?",
1196                )?;
1197                let rows = stmt.query_map(
1198                    (Chattype::Mailinglist, Chattype::InBroadcast, Blocked::Yes),
1199                    |row| {
1200                        let name: String = row.get(0)?;
1201                        let grpid: String = row.get(1)?;
1202                        let typ: Chattype = row.get(2)?;
1203                        Ok((name, grpid, typ))
1204                    },
1205                )?;
1206                let blocked_mailinglists = rows.collect::<std::result::Result<Vec<_>, _>>()?;
1207                for (name, grpid, typ) in blocked_mailinglists {
1208                    let count = transaction.query_row(
1209                        "SELECT COUNT(id) FROM contacts WHERE addr=?",
1210                        [&grpid],
1211                        |row| {
1212                            let count: isize = row.get(0)?;
1213                            Ok(count)
1214                        },
1215                    )?;
1216                    if count == 0 {
1217                        transaction.execute("INSERT INTO contacts (addr) VALUES (?)", [&grpid])?;
1218                    }
1219
1220                    let fingerprint = if typ == Chattype::InBroadcast {
1221                        // Set some fingerprint so that is_pgp_contact() returns true,
1222                        // and the contact isn't marked with a letter icon.
1223                        "Blocked_broadcast"
1224                    } else {
1225                        ""
1226                    };
1227                    // Always do an update in case the blocking is reset or name is changed.
1228                    transaction.execute(
1229                        "UPDATE contacts SET name=?, origin=?, blocked=1, fingerprint=? WHERE addr=?",
1230                        (&name, Origin::MailinglistAddress, fingerprint, &grpid),
1231                    )?;
1232                }
1233                Ok(())
1234            })
1235            .await?;
1236        Ok(())
1237    }
1238
1239    /// Returns number of blocked contacts.
1240    pub async fn get_blocked_cnt(context: &Context) -> Result<usize> {
1241        let count = context
1242            .sql
1243            .count(
1244                "SELECT COUNT(*) FROM contacts WHERE id>? AND blocked!=0",
1245                (ContactId::LAST_SPECIAL,),
1246            )
1247            .await?;
1248        Ok(count)
1249    }
1250
1251    /// Get blocked contacts.
1252    pub async fn get_all_blocked(context: &Context) -> Result<Vec<ContactId>> {
1253        Contact::update_blocked_mailinglist_contacts(context)
1254            .await
1255            .context("cannot update blocked mailinglist contacts")?;
1256
1257        let list = context
1258            .sql
1259            .query_map(
1260                "SELECT id FROM contacts WHERE id>? AND blocked!=0 ORDER BY last_seen DESC, id DESC;",
1261                (ContactId::LAST_SPECIAL,),
1262                |row| row.get::<_, ContactId>(0),
1263                |ids| {
1264                    ids.collect::<std::result::Result<Vec<_>, _>>()
1265                        .map_err(Into::into)
1266                },
1267            )
1268            .await?;
1269        Ok(list)
1270    }
1271
1272    /// Returns a textual summary of the encryption state for the contact.
1273    ///
1274    /// This function returns a string explaining the encryption state
1275    /// of the contact and if the connection is encrypted the
1276    /// fingerprints of the keys involved.
1277    pub async fn get_encrinfo(context: &Context, contact_id: ContactId) -> Result<String> {
1278        ensure!(
1279            !contact_id.is_special(),
1280            "Can not provide encryption info for special contact"
1281        );
1282
1283        let contact = Contact::get_by_id(context, contact_id).await?;
1284        let addr = context
1285            .get_config(Config::ConfiguredAddr)
1286            .await?
1287            .unwrap_or_default();
1288
1289        let Some(fingerprint_other) = contact.fingerprint() else {
1290            return Ok(stock_str::encr_none(context).await);
1291        };
1292        let fingerprint_other = fingerprint_other.to_string();
1293
1294        let stock_message = if contact.public_key(context).await?.is_some() {
1295            stock_str::e2e_available(context).await
1296        } else {
1297            stock_str::encr_none(context).await
1298        };
1299
1300        let finger_prints = stock_str::finger_prints(context).await;
1301        let mut ret = format!("{stock_message}.\n{finger_prints}:");
1302
1303        let fingerprint_self = load_self_public_key(context)
1304            .await?
1305            .dc_fingerprint()
1306            .to_string();
1307        if addr < contact.addr {
1308            cat_fingerprint(
1309                &mut ret,
1310                &stock_str::self_msg(context).await,
1311                &addr,
1312                &fingerprint_self,
1313            );
1314            cat_fingerprint(
1315                &mut ret,
1316                contact.get_display_name(),
1317                &contact.addr,
1318                &fingerprint_other,
1319            );
1320        } else {
1321            cat_fingerprint(
1322                &mut ret,
1323                contact.get_display_name(),
1324                &contact.addr,
1325                &fingerprint_other,
1326            );
1327            cat_fingerprint(
1328                &mut ret,
1329                &stock_str::self_msg(context).await,
1330                &addr,
1331                &fingerprint_self,
1332            );
1333        }
1334
1335        Ok(ret)
1336    }
1337
1338    /// Delete a contact so that it disappears from the corresponding lists.
1339    /// Depending on whether there are ongoing chats, deletion is done by physical deletion or hiding.
1340    /// The contact is deleted from the local device.
1341    ///
1342    /// May result in a `#DC_EVENT_CONTACTS_CHANGED` event.
1343    pub async fn delete(context: &Context, contact_id: ContactId) -> Result<()> {
1344        ensure!(!contact_id.is_special(), "Can not delete special contact");
1345
1346        context
1347            .sql
1348            .transaction(move |transaction| {
1349                // make sure, the transaction starts with a write command and becomes EXCLUSIVE by that -
1350                // upgrading later may be impossible by races.
1351                let deleted_contacts = transaction.execute(
1352                    "DELETE FROM contacts WHERE id=?
1353                     AND (SELECT COUNT(*) FROM chats_contacts WHERE contact_id=?)=0;",
1354                    (contact_id, contact_id),
1355                )?;
1356                if deleted_contacts == 0 {
1357                    transaction.execute(
1358                        "UPDATE contacts SET origin=? WHERE id=?;",
1359                        (Origin::Hidden, contact_id),
1360                    )?;
1361                }
1362                Ok(())
1363            })
1364            .await?;
1365
1366        context.emit_event(EventType::ContactsChanged(None));
1367        Ok(())
1368    }
1369
1370    /// Updates `param` column in the database.
1371    pub async fn update_param(&self, context: &Context) -> Result<()> {
1372        context
1373            .sql
1374            .execute(
1375                "UPDATE contacts SET param=? WHERE id=?",
1376                (self.param.to_string(), self.id),
1377            )
1378            .await?;
1379        Ok(())
1380    }
1381
1382    /// Updates `status` column in the database.
1383    pub async fn update_status(&self, context: &Context) -> Result<()> {
1384        context
1385            .sql
1386            .execute(
1387                "UPDATE contacts SET status=? WHERE id=?",
1388                (&self.status, self.id),
1389            )
1390            .await?;
1391        Ok(())
1392    }
1393
1394    /// Get the ID of the contact.
1395    pub fn get_id(&self) -> ContactId {
1396        self.id
1397    }
1398
1399    /// Get email address. The email address is always set for a contact.
1400    pub fn get_addr(&self) -> &str {
1401        &self.addr
1402    }
1403
1404    /// Returns true if the contact is a key-contact.
1405    /// Otherwise it is an addresss-contact.
1406    pub fn is_key_contact(&self) -> bool {
1407        self.fingerprint.is_some()
1408    }
1409
1410    /// Returns OpenPGP fingerprint of a contact.
1411    ///
1412    /// `None` for address-contacts.
1413    pub fn fingerprint(&self) -> Option<Fingerprint> {
1414        if let Some(fingerprint) = &self.fingerprint {
1415            fingerprint.parse().ok()
1416        } else {
1417            None
1418        }
1419    }
1420
1421    /// Returns OpenPGP public key of a contact.
1422    ///
1423    /// Returns `None` if the contact is not a key-contact
1424    /// or if the key is not available.
1425    /// It is possible for a key-contact to not have a key,
1426    /// e.g. if only the fingerprint is known from a QR-code.
1427    pub async fn public_key(&self, context: &Context) -> Result<Option<SignedPublicKey>> {
1428        if self.id == ContactId::SELF {
1429            return Ok(Some(load_self_public_key(context).await?));
1430        }
1431
1432        if let Some(fingerprint) = &self.fingerprint {
1433            if let Some(public_key_bytes) = context
1434                .sql
1435                .query_row_optional(
1436                    "SELECT public_key
1437                     FROM public_keys
1438                     WHERE fingerprint=?",
1439                    (fingerprint,),
1440                    |row| {
1441                        let bytes: Vec<u8> = row.get(0)?;
1442                        Ok(bytes)
1443                    },
1444                )
1445                .await?
1446            {
1447                let public_key = SignedPublicKey::from_slice(&public_key_bytes)?;
1448                Ok(Some(public_key))
1449            } else {
1450                Ok(None)
1451            }
1452        } else {
1453            Ok(None)
1454        }
1455    }
1456
1457    /// Get name authorized by the contact.
1458    pub fn get_authname(&self) -> &str {
1459        &self.authname
1460    }
1461
1462    /// Get the contact name. This is the name as modified by the local user.
1463    /// May be an empty string.
1464    ///
1465    /// This name is typically used in a form where the user can edit the name of a contact.
1466    /// To get a fine name to display in lists etc., use `Contact::get_display_name` or `Contact::get_name_n_addr`.
1467    pub fn get_name(&self) -> &str {
1468        &self.name
1469    }
1470
1471    /// Get display name. This is the name as defined by the contact himself,
1472    /// modified by the user or, if both are unset, the email address.
1473    ///
1474    /// This name is typically used in lists.
1475    /// To get the name editable in a formular, use `Contact::get_name`.
1476    pub fn get_display_name(&self) -> &str {
1477        if !self.name.is_empty() {
1478            return &self.name;
1479        }
1480        if !self.authname.is_empty() {
1481            return &self.authname;
1482        }
1483        &self.addr
1484    }
1485
1486    /// Get authorized name or address.
1487    ///
1488    /// This string is suitable for sending over email
1489    /// as it does not leak the locally set name.
1490    pub(crate) fn get_authname_or_addr(&self) -> String {
1491        if !self.authname.is_empty() {
1492            (&self.authname).into()
1493        } else {
1494            (&self.addr).into()
1495        }
1496    }
1497
1498    /// Get a summary of name and address.
1499    ///
1500    /// The returned string is either "Name (email@domain.com)" or just
1501    /// "email@domain.com" if the name is unset.
1502    ///
1503    /// The result should only be used locally and never sent over the network
1504    /// as it leaks the local contact name.
1505    ///
1506    /// The summary is typically used when asking the user something about the contact.
1507    /// The attached email address makes the question unique, eg. "Chat with Alan Miller (am@uniquedomain.com)?"
1508    pub fn get_name_n_addr(&self) -> String {
1509        if !self.name.is_empty() {
1510            format!("{} ({})", self.name, self.addr)
1511        } else if !self.authname.is_empty() {
1512            format!("{} ({})", self.authname, self.addr)
1513        } else {
1514            (&self.addr).into()
1515        }
1516    }
1517
1518    /// Get the contact's profile image.
1519    /// This is the image set by each remote user on their own
1520    /// using set_config(context, "selfavatar", image).
1521    pub async fn get_profile_image(&self, context: &Context) -> Result<Option<PathBuf>> {
1522        self.get_profile_image_ex(context, true).await
1523    }
1524
1525    /// Get the contact's profile image.
1526    /// This is the image set by each remote user on their own
1527    /// using set_config(context, "selfavatar", image).
1528    async fn get_profile_image_ex(
1529        &self,
1530        context: &Context,
1531        show_fallback_icon: bool,
1532    ) -> Result<Option<PathBuf>> {
1533        if self.id == ContactId::SELF {
1534            if let Some(p) = context.get_config(Config::Selfavatar).await? {
1535                return Ok(Some(PathBuf::from(p))); // get_config() calls get_abs_path() internally already
1536            }
1537        } else if self.id == ContactId::DEVICE {
1538            return Ok(Some(chat::get_device_icon(context).await?));
1539        }
1540        if show_fallback_icon && !self.id.is_special() && !self.is_key_contact() {
1541            return Ok(Some(chat::get_address_contact_icon(context).await?));
1542        }
1543        if let Some(image_rel) = self.param.get(Param::ProfileImage) {
1544            if !image_rel.is_empty() {
1545                return Ok(Some(get_abs_path(context, Path::new(image_rel))));
1546            }
1547        }
1548        Ok(None)
1549    }
1550
1551    /// Get a color for the contact.
1552    /// The color is calculated from the contact's email address
1553    /// and can be used for an fallback avatar with white initials
1554    /// as well as for headlines in bubbles of group chats.
1555    pub fn get_color(&self) -> u32 {
1556        str_to_color(&self.addr.to_lowercase())
1557    }
1558
1559    /// Gets the contact's status.
1560    ///
1561    /// Status is the last signature received in a message from this contact.
1562    pub fn get_status(&self) -> &str {
1563        self.status.as_str()
1564    }
1565
1566    /// Returns whether end-to-end encryption to the contact is available.
1567    pub async fn e2ee_avail(&self, context: &Context) -> Result<bool> {
1568        if self.id == ContactId::SELF {
1569            // We don't need to check if we have our own key.
1570            return Ok(true);
1571        }
1572        Ok(self.public_key(context).await?.is_some())
1573    }
1574
1575    /// Returns true if the contact
1576    /// can be added to verified chats.
1577    ///
1578    /// If contact is verified
1579    /// UI should display green checkmark after the contact name
1580    /// in contact list items and
1581    /// in chat member list items.
1582    ///
1583    /// In contact profile view, use this function only if there is no chat with the contact,
1584    /// otherwise use is_chat_protected().
1585    /// Use [Self::get_verifier_id] to display the verifier contact
1586    /// in the info section of the contact profile.
1587    pub async fn is_verified(&self, context: &Context) -> Result<bool> {
1588        // We're always sort of secured-verified as we could verify the key on this device any time with the key
1589        // on this device
1590        if self.id == ContactId::SELF {
1591            return Ok(true);
1592        }
1593
1594        Ok(self.get_verifier_id(context).await?.is_some())
1595    }
1596
1597    /// Returns the `ContactId` that verified the contact.
1598    ///
1599    /// If this returns Some(_),
1600    /// display green checkmark in the profile and "Introduced by ..." line
1601    /// with the name and address of the contact
1602    /// formatted by [Self::get_name_n_addr].
1603    ///
1604    /// If this returns `Some(None)`, then the contact is verified,
1605    /// but it's unclear by whom.
1606    pub async fn get_verifier_id(&self, context: &Context) -> Result<Option<Option<ContactId>>> {
1607        let verifier_id: u32 = context
1608            .sql
1609            .query_get_value("SELECT verifier FROM contacts WHERE id=?", (self.id,))
1610            .await?
1611            .with_context(|| format!("Contact {} does not exist", self.id))?;
1612
1613        if verifier_id == 0 {
1614            Ok(None)
1615        } else if verifier_id == self.id.to_u32() {
1616            Ok(Some(None))
1617        } else {
1618            Ok(Some(Some(ContactId::new(verifier_id))))
1619        }
1620    }
1621
1622    /// Returns if the contact profile title should display a green checkmark.
1623    ///
1624    /// This generally should be consistent with the 1:1 chat with the contact
1625    /// so 1:1 chat with the contact and the contact profile
1626    /// either both display the green checkmark or both don't display a green checkmark.
1627    ///
1628    /// UI often knows beforehand if a chat exists and can also call
1629    /// `chat.is_protected()` (if there is a chat)
1630    /// or `contact.is_verified()` (if there is no chat) directly.
1631    /// This is often easier and also skips some database calls.
1632    pub async fn is_profile_verified(&self, context: &Context) -> Result<bool> {
1633        let contact_id = self.id;
1634
1635        if let Some(ChatIdBlocked { id: chat_id, .. }) =
1636            ChatIdBlocked::lookup_by_contact(context, contact_id).await?
1637        {
1638            Ok(chat_id.is_protected(context).await? == ProtectionStatus::Protected)
1639        } else {
1640            // 1:1 chat does not exist.
1641            Ok(self.is_verified(context).await?)
1642        }
1643    }
1644
1645    /// Returns the number of real (i.e. non-special) contacts in the database.
1646    pub async fn get_real_cnt(context: &Context) -> Result<usize> {
1647        if !context.sql.is_open().await {
1648            return Ok(0);
1649        }
1650
1651        let count = context
1652            .sql
1653            .count(
1654                "SELECT COUNT(*) FROM contacts WHERE id>?;",
1655                (ContactId::LAST_SPECIAL,),
1656            )
1657            .await?;
1658        Ok(count)
1659    }
1660
1661    /// Returns true if a contact with this ID exists.
1662    pub async fn real_exists_by_id(context: &Context, contact_id: ContactId) -> Result<bool> {
1663        if contact_id.is_special() {
1664            return Ok(false);
1665        }
1666
1667        let exists = context
1668            .sql
1669            .exists("SELECT COUNT(*) FROM contacts WHERE id=?;", (contact_id,))
1670            .await?;
1671        Ok(exists)
1672    }
1673}
1674
1675// Updates the names of the chats which use the contact name.
1676//
1677// This is one of the few duplicated data, however, getting the chat list is easier this way.
1678fn update_chat_names(
1679    context: &Context,
1680    transaction: &rusqlite::Connection,
1681    contact_id: ContactId,
1682) -> Result<()> {
1683    let chat_id: Option<ChatId> = transaction.query_row(
1684            "SELECT id FROM chats WHERE type=? AND id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?)",
1685            (Chattype::Single, contact_id),
1686            |row| {
1687                let chat_id: ChatId = row.get(0)?;
1688                Ok(chat_id)
1689            }
1690        ).optional()?;
1691
1692    if let Some(chat_id) = chat_id {
1693        let (addr, name, authname) = transaction.query_row(
1694            "SELECT addr, name, authname
1695                     FROM contacts
1696                     WHERE id=?",
1697            (contact_id,),
1698            |row| {
1699                let addr: String = row.get(0)?;
1700                let name: String = row.get(1)?;
1701                let authname: String = row.get(2)?;
1702                Ok((addr, name, authname))
1703            },
1704        )?;
1705
1706        let chat_name = if !name.is_empty() {
1707            name
1708        } else if !authname.is_empty() {
1709            authname
1710        } else {
1711            addr
1712        };
1713
1714        let count = transaction.execute(
1715            "UPDATE chats SET name=?1 WHERE id=?2 AND name!=?1",
1716            (chat_name, chat_id),
1717        )?;
1718
1719        if count > 0 {
1720            // Chat name updated
1721            context.emit_event(EventType::ChatModified(chat_id));
1722            chatlist_events::emit_chatlist_items_changed_for_contact(context, contact_id);
1723        }
1724    }
1725
1726    Ok(())
1727}
1728
1729pub(crate) async fn set_blocked(
1730    context: &Context,
1731    sync: sync::Sync,
1732    contact_id: ContactId,
1733    new_blocking: bool,
1734) -> Result<()> {
1735    ensure!(
1736        !contact_id.is_special(),
1737        "Can't block special contact {}",
1738        contact_id
1739    );
1740    let contact = Contact::get_by_id(context, contact_id).await?;
1741
1742    if contact.blocked != new_blocking {
1743        context
1744            .sql
1745            .execute(
1746                "UPDATE contacts SET blocked=? WHERE id=?;",
1747                (i32::from(new_blocking), contact_id),
1748            )
1749            .await?;
1750
1751        // also (un)block all chats with _only_ this contact - we do not delete them to allow a
1752        // non-destructive blocking->unblocking.
1753        // (Maybe, beside normal chats (type=100) we should also block group chats with only this user.
1754        // However, I'm not sure about this point; it may be confusing if the user wants to add other people;
1755        // this would result in recreating the same group...)
1756        if context
1757            .sql
1758            .execute(
1759                r#"
1760UPDATE chats
1761SET blocked=?
1762WHERE type=? AND id IN (
1763  SELECT chat_id FROM chats_contacts WHERE contact_id=?
1764);
1765"#,
1766                (new_blocking, Chattype::Single, contact_id),
1767            )
1768            .await
1769            .is_ok()
1770        {
1771            Contact::mark_noticed(context, contact_id).await?;
1772            context.emit_event(EventType::ContactsChanged(Some(contact_id)));
1773        }
1774
1775        // also unblock mailinglist
1776        // if the contact is a mailinglist address explicitly created to allow unblocking
1777        if !new_blocking && contact.origin == Origin::MailinglistAddress {
1778            if let Some((chat_id, _, _)) =
1779                chat::get_chat_id_by_grpid(context, &contact.addr).await?
1780            {
1781                chat_id.unblock_ex(context, Nosync).await?;
1782            }
1783        }
1784
1785        if sync.into() {
1786            let action = match new_blocking {
1787                true => chat::SyncAction::Block,
1788                false => chat::SyncAction::Unblock,
1789            };
1790            let sync_id = if let Some(fingerprint) = contact.fingerprint() {
1791                chat::SyncId::ContactFingerprint(fingerprint.hex())
1792            } else {
1793                chat::SyncId::ContactAddr(contact.addr.clone())
1794            };
1795
1796            chat::sync(context, sync_id, action)
1797                .await
1798                .log_err(context)
1799                .ok();
1800        }
1801    }
1802
1803    chatlist_events::emit_chatlist_changed(context);
1804    Ok(())
1805}
1806
1807/// Set profile image for a contact.
1808///
1809/// The given profile image is expected to be already in the blob directory
1810/// as profile images can be set only by receiving messages, this should be always the case, however.
1811///
1812/// For contact SELF, the image is not saved in the contact-database but as Config::Selfavatar;
1813/// this typically happens if we see message with our own profile image.
1814pub(crate) async fn set_profile_image(
1815    context: &Context,
1816    contact_id: ContactId,
1817    profile_image: &AvatarAction,
1818    was_encrypted: bool,
1819) -> Result<()> {
1820    let mut contact = Contact::get_by_id(context, contact_id).await?;
1821    let changed = match profile_image {
1822        AvatarAction::Change(profile_image) => {
1823            if contact_id == ContactId::SELF {
1824                if was_encrypted {
1825                    context
1826                        .set_config_ex(Nosync, Config::Selfavatar, Some(profile_image))
1827                        .await?;
1828                } else {
1829                    info!(context, "Do not use unencrypted selfavatar.");
1830                }
1831            } else {
1832                contact.param.set(Param::ProfileImage, profile_image);
1833            }
1834            true
1835        }
1836        AvatarAction::Delete => {
1837            if contact_id == ContactId::SELF {
1838                if was_encrypted {
1839                    context
1840                        .set_config_ex(Nosync, Config::Selfavatar, None)
1841                        .await?;
1842                } else {
1843                    info!(context, "Do not use unencrypted selfavatar deletion.");
1844                }
1845            } else {
1846                contact.param.remove(Param::ProfileImage);
1847            }
1848            true
1849        }
1850    };
1851    if changed {
1852        contact.update_param(context).await?;
1853        context.emit_event(EventType::ContactsChanged(Some(contact_id)));
1854        chatlist_events::emit_chatlist_item_changed_for_contact_chat(context, contact_id).await;
1855    }
1856    Ok(())
1857}
1858
1859/// Sets contact status.
1860///
1861/// For contact SELF, the status is not saved in the contact table, but as Config::Selfstatus.  This
1862/// is only done if message is sent from Delta Chat and it is encrypted, to synchronize signature
1863/// between Delta Chat devices.
1864pub(crate) async fn set_status(
1865    context: &Context,
1866    contact_id: ContactId,
1867    status: String,
1868    encrypted: bool,
1869    has_chat_version: bool,
1870) -> Result<()> {
1871    if contact_id == ContactId::SELF {
1872        if encrypted && has_chat_version {
1873            context
1874                .set_config_ex(Nosync, Config::Selfstatus, Some(&status))
1875                .await?;
1876        }
1877    } else {
1878        let mut contact = Contact::get_by_id(context, contact_id).await?;
1879
1880        if contact.status != status {
1881            contact.status = status;
1882            contact.update_status(context).await?;
1883            context.emit_event(EventType::ContactsChanged(Some(contact_id)));
1884        }
1885    }
1886    Ok(())
1887}
1888
1889/// Updates last seen timestamp of the contact if it is earlier than the given `timestamp`.
1890pub(crate) async fn update_last_seen(
1891    context: &Context,
1892    contact_id: ContactId,
1893    timestamp: i64,
1894) -> Result<()> {
1895    ensure!(
1896        !contact_id.is_special(),
1897        "Can not update special contact last seen timestamp"
1898    );
1899
1900    if context
1901        .sql
1902        .execute(
1903            "UPDATE contacts SET last_seen = ?1 WHERE last_seen < ?1 AND id = ?2",
1904            (timestamp, contact_id),
1905        )
1906        .await?
1907        > 0
1908        && timestamp > time() - SEEN_RECENTLY_SECONDS
1909    {
1910        context.emit_event(EventType::ContactsChanged(Some(contact_id)));
1911        context
1912            .scheduler
1913            .interrupt_recently_seen(contact_id, timestamp)
1914            .await;
1915    }
1916    Ok(())
1917}
1918
1919/// Marks contact `contact_id` as verified by `verifier_id`.
1920pub(crate) async fn mark_contact_id_as_verified(
1921    context: &Context,
1922    contact_id: ContactId,
1923    verifier_id: ContactId,
1924) -> Result<()> {
1925    ensure_and_debug_assert_ne!(
1926        contact_id,
1927        verifier_id,
1928        "Contact cannot be verified by self",
1929    );
1930    context
1931        .sql
1932        .transaction(|transaction| {
1933            let contact_fingerprint: String = transaction.query_row(
1934                "SELECT fingerprint FROM contacts WHERE id=?",
1935                (contact_id,),
1936                |row| row.get(0),
1937            )?;
1938            if contact_fingerprint.is_empty() {
1939                bail!("Non-key-contact {contact_id} cannot be verified");
1940            }
1941            if verifier_id != ContactId::SELF {
1942                let verifier_fingerprint: String = transaction.query_row(
1943                    "SELECT fingerprint FROM contacts WHERE id=?",
1944                    (verifier_id,),
1945                    |row| row.get(0),
1946                )?;
1947                if verifier_fingerprint.is_empty() {
1948                    bail!(
1949                        "Contact {contact_id} cannot be verified by non-key-contact {verifier_id}"
1950                    );
1951                }
1952            }
1953            transaction.execute(
1954                "UPDATE contacts SET verifier=? WHERE id=?",
1955                (verifier_id, contact_id),
1956            )?;
1957            Ok(())
1958        })
1959        .await?;
1960    Ok(())
1961}
1962
1963fn cat_fingerprint(ret: &mut String, name: &str, addr: &str, fingerprint: &str) {
1964    *ret += &format!("\n\n{name} ({addr}):\n{fingerprint}");
1965}
1966
1967fn split_address_book(book: &str) -> Vec<(&str, &str)> {
1968    book.lines()
1969        .collect::<Vec<&str>>()
1970        .chunks(2)
1971        .filter_map(|chunk| {
1972            let name = chunk.first()?;
1973            let addr = chunk.get(1)?;
1974            Some((*name, *addr))
1975        })
1976        .collect()
1977}
1978
1979#[derive(Debug)]
1980pub(crate) struct RecentlySeenInterrupt {
1981    contact_id: ContactId,
1982    timestamp: i64,
1983}
1984
1985#[derive(Debug)]
1986pub(crate) struct RecentlySeenLoop {
1987    /// Task running "recently seen" loop.
1988    handle: task::JoinHandle<()>,
1989
1990    interrupt_send: Sender<RecentlySeenInterrupt>,
1991}
1992
1993impl RecentlySeenLoop {
1994    pub(crate) fn new(context: Context) -> Self {
1995        let (interrupt_send, interrupt_recv) = channel::bounded(1);
1996
1997        let handle = task::spawn(Self::run(context, interrupt_recv));
1998        Self {
1999            handle,
2000            interrupt_send,
2001        }
2002    }
2003
2004    async fn run(context: Context, interrupt: Receiver<RecentlySeenInterrupt>) {
2005        type MyHeapElem = (Reverse<i64>, ContactId);
2006
2007        let now = SystemTime::now();
2008        let now_ts = now
2009            .duration_since(SystemTime::UNIX_EPOCH)
2010            .unwrap_or_default()
2011            .as_secs() as i64;
2012
2013        // Priority contains all recently seen sorted by the timestamp
2014        // when they become not recently seen.
2015        //
2016        // Initialize with contacts which are currently seen, but will
2017        // become unseen in the future.
2018        let mut unseen_queue: BinaryHeap<MyHeapElem> = context
2019            .sql
2020            .query_map(
2021                "SELECT id, last_seen FROM contacts
2022                 WHERE last_seen > ?",
2023                (now_ts - SEEN_RECENTLY_SECONDS,),
2024                |row| {
2025                    let contact_id: ContactId = row.get("id")?;
2026                    let last_seen: i64 = row.get("last_seen")?;
2027                    Ok((Reverse(last_seen + SEEN_RECENTLY_SECONDS), contact_id))
2028                },
2029                |rows| {
2030                    rows.collect::<std::result::Result<BinaryHeap<MyHeapElem>, _>>()
2031                        .map_err(Into::into)
2032                },
2033            )
2034            .await
2035            .unwrap_or_default();
2036
2037        loop {
2038            let now = SystemTime::now();
2039            let (until, contact_id) =
2040                if let Some((Reverse(timestamp), contact_id)) = unseen_queue.peek() {
2041                    (
2042                        UNIX_EPOCH
2043                            + Duration::from_secs((*timestamp).try_into().unwrap_or(u64::MAX))
2044                            + Duration::from_secs(1),
2045                        Some(contact_id),
2046                    )
2047                } else {
2048                    // Sleep for 24 hours.
2049                    (now + Duration::from_secs(86400), None)
2050                };
2051
2052            if let Ok(duration) = until.duration_since(now) {
2053                info!(
2054                    context,
2055                    "Recently seen loop waiting for {} or interrupt",
2056                    duration_to_str(duration)
2057                );
2058
2059                match timeout(duration, interrupt.recv()).await {
2060                    Err(_) => {
2061                        // Timeout, notify about contact.
2062                        if let Some(contact_id) = contact_id {
2063                            context.emit_event(EventType::ContactsChanged(Some(*contact_id)));
2064                            chatlist_events::emit_chatlist_item_changed_for_contact_chat(
2065                                &context,
2066                                *contact_id,
2067                            )
2068                            .await;
2069                            unseen_queue.pop();
2070                        }
2071                    }
2072                    Ok(Err(err)) => {
2073                        warn!(
2074                            context,
2075                            "Error receiving an interruption in recently seen loop: {}", err
2076                        );
2077                        // Maybe the sender side is closed.
2078                        // Terminate the loop to avoid looping indefinitely.
2079                        return;
2080                    }
2081                    Ok(Ok(RecentlySeenInterrupt {
2082                        contact_id,
2083                        timestamp,
2084                    })) => {
2085                        // Received an interrupt.
2086                        if contact_id != ContactId::UNDEFINED {
2087                            unseen_queue
2088                                .push((Reverse(timestamp + SEEN_RECENTLY_SECONDS), contact_id));
2089                        }
2090                    }
2091                }
2092            } else {
2093                info!(
2094                    context,
2095                    "Recently seen loop is not waiting, event is already due."
2096                );
2097
2098                // Event is already in the past.
2099                if let Some(contact_id) = contact_id {
2100                    context.emit_event(EventType::ContactsChanged(Some(*contact_id)));
2101                    chatlist_events::emit_chatlist_item_changed_for_contact_chat(
2102                        &context,
2103                        *contact_id,
2104                    )
2105                    .await;
2106                }
2107                unseen_queue.pop();
2108            }
2109        }
2110    }
2111
2112    pub(crate) fn try_interrupt(&self, contact_id: ContactId, timestamp: i64) {
2113        self.interrupt_send
2114            .try_send(RecentlySeenInterrupt {
2115                contact_id,
2116                timestamp,
2117            })
2118            .ok();
2119    }
2120
2121    #[cfg(test)]
2122    pub(crate) async fn interrupt(&self, contact_id: ContactId, timestamp: i64) {
2123        self.interrupt_send
2124            .send(RecentlySeenInterrupt {
2125                contact_id,
2126                timestamp,
2127            })
2128            .await
2129            .unwrap();
2130    }
2131
2132    pub(crate) async fn abort(self) {
2133        self.handle.abort();
2134
2135        // Await aborted task to ensure the `Future` is dropped
2136        // with all resources moved inside such as the `Context`
2137        // reference to `InnerContext`.
2138        self.handle.await.ok();
2139    }
2140}
2141
2142#[cfg(test)]
2143mod contact_tests;