deltachat/
contact.rs

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