1use 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;
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, 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, to_lowercase};
40use crate::{chat, chatlist_events, ensure_and_debug_assert_ne, stock_str};
41
42const SEEN_RECENTLY_SECONDS: i64 = 600;
44
45#[derive(
50 Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
51)]
52pub struct ContactId(u32);
53
54impl ContactId {
55 pub const UNDEFINED: ContactId = ContactId::new(0);
57
58 pub const SELF: ContactId = ContactId::new(1);
62
63 pub const INFO: ContactId = ContactId::new(2);
65
66 pub const DEVICE: ContactId = ContactId::new(5);
68 pub(crate) const LAST_SPECIAL: ContactId = ContactId::new(9);
69
70 pub const DEVICE_ADDR: &'static str = "device@localhost";
74
75 pub const fn new(id: u32) -> ContactId {
77 ContactId(id)
78 }
79
80 pub fn is_special(&self) -> bool {
87 self.0 <= Self::LAST_SPECIAL.0
88 }
89
90 pub const fn to_u32(&self) -> u32 {
96 self.0
97 }
98
99 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 Ok(Some((addr, fingerprint)))
134 } else {
135 Ok(None)
136 }
137 })
138 .await?;
139 if row.is_some() {
140 context.emit_event(EventType::ContactsChanged(Some(self)));
141 }
142
143 if sync.into()
144 && let Some((addr, fingerprint)) = row
145 {
146 if fingerprint.is_empty() {
147 chat::sync(
148 context,
149 chat::SyncId::ContactAddr(addr),
150 chat::SyncAction::Rename(name.to_string()),
151 )
152 .await
153 .log_err(context)
154 .ok();
155 } else {
156 chat::sync(
157 context,
158 chat::SyncId::ContactFingerprint(fingerprint),
159 chat::SyncAction::Rename(name.to_string()),
160 )
161 .await
162 .log_err(context)
163 .ok();
164 }
165 }
166 Ok(())
167 }
168
169 pub(crate) async fn mark_bot(&self, context: &Context, is_bot: bool) -> Result<()> {
171 context
172 .sql
173 .execute("UPDATE contacts SET is_bot=? WHERE id=?;", (is_bot, self.0))
174 .await?;
175 Ok(())
176 }
177
178 pub(crate) async fn regossip_keys(&self, context: &Context) -> Result<()> {
180 context
181 .sql
182 .execute(
183 "UPDATE chats
184 SET gossiped_timestamp=0
185 WHERE EXISTS (SELECT 1 FROM chats_contacts
186 WHERE chats_contacts.chat_id=chats.id
187 AND chats_contacts.contact_id=?
188 AND chats_contacts.add_timestamp >= chats_contacts.remove_timestamp)",
189 (self,),
190 )
191 .await?;
192 Ok(())
193 }
194
195 pub(crate) async fn scaleup_origin(
197 context: &Context,
198 ids: &[Self],
199 origin: Origin,
200 ) -> Result<()> {
201 context
202 .sql
203 .transaction(|transaction| {
204 let mut stmt = transaction
205 .prepare("UPDATE contacts SET origin=?1 WHERE id = ?2 AND origin < ?1")?;
206 for id in ids {
207 stmt.execute((origin, id))?;
208 }
209 Ok(())
210 })
211 .await?;
212 Ok(())
213 }
214
215 pub async fn addr(&self, context: &Context) -> Result<String> {
217 let addr = context
218 .sql
219 .query_row("SELECT addr FROM contacts WHERE id=?", (self,), |row| {
220 let addr: String = row.get(0)?;
221 Ok(addr)
222 })
223 .await?;
224 Ok(addr)
225 }
226}
227
228impl fmt::Display for ContactId {
229 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
230 if *self == ContactId::UNDEFINED {
231 write!(f, "Contact#Undefined")
232 } else if *self == ContactId::SELF {
233 write!(f, "Contact#Self")
234 } else if *self == ContactId::INFO {
235 write!(f, "Contact#Info")
236 } else if *self == ContactId::DEVICE {
237 write!(f, "Contact#Device")
238 } else if self.is_special() {
239 write!(f, "Contact#Special{}", self.0)
240 } else {
241 write!(f, "Contact#{}", self.0)
242 }
243 }
244}
245
246impl rusqlite::types::ToSql for ContactId {
248 fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
249 let val = rusqlite::types::Value::Integer(i64::from(self.0));
250 let out = rusqlite::types::ToSqlOutput::Owned(val);
251 Ok(out)
252 }
253}
254
255impl rusqlite::types::FromSql for ContactId {
257 fn column_result(value: rusqlite::types::ValueRef) -> rusqlite::types::FromSqlResult<Self> {
258 i64::column_result(value).and_then(|val| {
259 val.try_into()
260 .map(ContactId::new)
261 .map_err(|_| rusqlite::types::FromSqlError::OutOfRange(val))
262 })
263 }
264}
265
266pub async fn make_vcard(context: &Context, contacts: &[ContactId]) -> Result<String> {
268 let now = time();
269 let mut vcard_contacts = Vec::with_capacity(contacts.len());
270 for id in contacts {
271 let c = Contact::get_by_id(context, *id).await?;
272 let key = c.public_key(context).await?.map(|k| k.to_base64());
273 let profile_image = match c.get_profile_image_ex(context, false).await? {
274 None => None,
275 Some(path) => tokio::fs::read(path)
276 .await
277 .log_err(context)
278 .ok()
279 .map(|data| base64::engine::general_purpose::STANDARD.encode(data)),
280 };
281 vcard_contacts.push(VcardContact {
282 addr: c.addr,
283 authname: c.authname,
284 key,
285 profile_image,
286 biography: Some(c.status).filter(|s| !s.is_empty()),
287 timestamp: Ok(now),
289 });
290 }
291
292 Ok(contact_tools::make_vcard(&vcard_contacts)
299 .trim_end()
300 .to_string())
301}
302
303pub async fn import_vcard(context: &Context, vcard: &str) -> Result<Vec<ContactId>> {
308 let contacts = contact_tools::parse_vcard(vcard);
309 let mut contact_ids = Vec::with_capacity(contacts.len());
310 for c in &contacts {
311 let Ok(id) = import_vcard_contact(context, c)
312 .await
313 .with_context(|| format!("import_vcard_contact() failed for {}", c.addr))
314 .log_err(context)
315 else {
316 continue;
317 };
318 contact_ids.push(id);
319 }
320 Ok(contact_ids)
321}
322
323async fn import_vcard_contact(context: &Context, contact: &VcardContact) -> Result<ContactId> {
324 let addr = ContactAddress::new(&contact.addr).context("Invalid address")?;
325 let origin = Origin::CreateChat;
329 let key = contact.key.as_ref().and_then(|k| {
330 SignedPublicKey::from_base64(k)
331 .with_context(|| {
332 format!(
333 "import_vcard_contact: Cannot decode key for {}",
334 contact.addr
335 )
336 })
337 .log_err(context)
338 .ok()
339 });
340
341 let fingerprint;
342 if let Some(public_key) = key {
343 fingerprint = public_key.dc_fingerprint().hex();
344
345 context
346 .sql
347 .execute(
348 "INSERT INTO public_keys (fingerprint, public_key)
349 VALUES (?, ?)
350 ON CONFLICT (fingerprint)
351 DO NOTHING",
352 (&fingerprint, public_key.to_bytes()),
353 )
354 .await?;
355 } else {
356 fingerprint = String::new();
357 }
358
359 let (id, modified) =
360 match Contact::add_or_lookup_ex(context, &contact.authname, &addr, &fingerprint, origin)
361 .await
362 {
363 Err(e) => return Err(e).context("Contact::add_or_lookup() failed"),
364 Ok((ContactId::SELF, _)) => return Ok(ContactId::SELF),
365 Ok(val) => val,
366 };
367 if modified != Modifier::None {
368 context.emit_event(EventType::ContactsChanged(Some(id)));
369 }
370 if modified != Modifier::Created {
371 return Ok(id);
372 }
373 let path = match &contact.profile_image {
374 Some(image) => match BlobObject::store_from_base64(context, image)? {
375 None => {
376 warn!(
377 context,
378 "import_vcard_contact: Could not decode avatar for {}.", contact.addr
379 );
380 None
381 }
382 Some(path) => Some(path),
383 },
384 None => None,
385 };
386 if let Some(path) = path
387 && let Err(e) = set_profile_image(context, id, &AvatarAction::Change(path)).await
388 {
389 warn!(
390 context,
391 "import_vcard_contact: Could not set avatar for {}: {e:#}.", contact.addr
392 );
393 }
394 if let Some(biography) = &contact.biography
395 && let Err(e) = set_status(context, id, biography.to_owned()).await
396 {
397 warn!(
398 context,
399 "import_vcard_contact: Could not set biography for {}: {e:#}.", contact.addr
400 );
401 }
402 Ok(id)
403}
404
405#[derive(Debug)]
418pub struct Contact {
419 pub id: ContactId,
421
422 name: String,
426
427 authname: String,
431
432 addr: String,
434
435 fingerprint: Option<String>,
439
440 pub blocked: bool,
442
443 last_seen: i64,
445
446 pub origin: Origin,
448
449 pub param: Params,
451
452 status: String,
454
455 is_bot: bool,
457}
458
459#[derive(
461 Debug,
462 Default,
463 Clone,
464 Copy,
465 PartialEq,
466 Eq,
467 PartialOrd,
468 Ord,
469 FromPrimitive,
470 ToPrimitive,
471 FromSql,
472 ToSql,
473)]
474#[repr(u32)]
475pub enum Origin {
476 #[default]
479 Unknown = 0,
480
481 MailinglistAddress = 0x2,
483
484 Hidden = 0x8,
486
487 IncomingUnknownFrom = 0x10,
489
490 IncomingUnknownCc = 0x20,
492
493 IncomingUnknownTo = 0x40,
495
496 UnhandledQrScan = 0x80,
498
499 UnhandledSecurejoinQrScan = 0x81,
501
502 IncomingReplyTo = 0x100,
505
506 IncomingCc = 0x200,
508
509 IncomingTo = 0x400,
511
512 CreateChat = 0x800,
514
515 OutgoingBcc = 0x1000,
517
518 OutgoingCc = 0x2000,
520
521 OutgoingTo = 0x4000,
523
524 Internal = 0x40000,
526
527 AddressBook = 0x80000,
529
530 SecurejoinInvited = 0x0100_0000,
532
533 SecurejoinJoined = 0x0200_0000,
539
540 ManuallyCreated = 0x0400_0000,
542}
543
544impl Origin {
545 pub fn is_known(self) -> bool {
549 self >= Origin::IncomingReplyTo
550 }
551}
552
553#[derive(Debug, PartialEq, Eq, Clone, Copy)]
554pub(crate) enum Modifier {
555 None,
556 Modified,
557 Created,
558}
559
560impl Contact {
561 pub async fn get_by_id(context: &Context, contact_id: ContactId) -> Result<Self> {
572 let contact = Self::get_by_id_optional(context, contact_id)
573 .await?
574 .with_context(|| format!("contact {contact_id} not found"))?;
575 Ok(contact)
576 }
577
578 pub async fn get_by_id_optional(
582 context: &Context,
583 contact_id: ContactId,
584 ) -> Result<Option<Self>> {
585 if let Some(mut contact) = context
586 .sql
587 .query_row_optional(
588 "SELECT c.name, c.addr, c.origin, c.blocked, c.last_seen,
589 c.authname, c.param, c.status, c.is_bot, c.fingerprint
590 FROM contacts c
591 WHERE c.id=?;",
592 (contact_id,),
593 |row| {
594 let name: String = row.get(0)?;
595 let addr: String = row.get(1)?;
596 let origin: Origin = row.get(2)?;
597 let blocked: Option<bool> = row.get(3)?;
598 let last_seen: i64 = row.get(4)?;
599 let authname: String = row.get(5)?;
600 let param: String = row.get(6)?;
601 let status: Option<String> = row.get(7)?;
602 let is_bot: bool = row.get(8)?;
603 let fingerprint: Option<String> =
604 Some(row.get(9)?).filter(|s: &String| !s.is_empty());
605 let contact = Self {
606 id: contact_id,
607 name,
608 authname,
609 addr,
610 fingerprint,
611 blocked: blocked.unwrap_or_default(),
612 last_seen,
613 origin,
614 param: param.parse().unwrap_or_default(),
615 status: status.unwrap_or_default(),
616 is_bot,
617 };
618 Ok(contact)
619 },
620 )
621 .await?
622 {
623 if contact_id == ContactId::SELF {
624 contact.name = stock_str::self_msg(context).await;
625 contact.authname = context
626 .get_config(Config::Displayname)
627 .await?
628 .unwrap_or_default();
629 contact.addr = context
630 .get_config(Config::ConfiguredAddr)
631 .await?
632 .unwrap_or_default();
633 if let Some(self_fp) = self_fingerprint_opt(context).await? {
634 contact.fingerprint = Some(self_fp.to_string());
635 }
636 contact.status = context
637 .get_config(Config::Selfstatus)
638 .await?
639 .unwrap_or_default();
640 } else if contact_id == ContactId::DEVICE {
641 contact.name = stock_str::device_messages(context).await;
642 contact.addr = ContactId::DEVICE_ADDR.to_string();
643 contact.status = stock_str::device_messages_hint(context).await;
644 }
645 Ok(Some(contact))
646 } else {
647 Ok(None)
648 }
649 }
650
651 pub fn is_blocked(&self) -> bool {
653 self.blocked
654 }
655
656 pub fn last_seen(&self) -> i64 {
658 self.last_seen
659 }
660
661 pub fn was_seen_recently(&self) -> bool {
663 time() - self.last_seen <= SEEN_RECENTLY_SECONDS
664 }
665
666 pub async fn is_blocked_load(context: &Context, id: ContactId) -> Result<bool> {
668 let blocked = context
669 .sql
670 .query_row("SELECT blocked FROM contacts WHERE id=?", (id,), |row| {
671 let blocked: bool = row.get(0)?;
672 Ok(blocked)
673 })
674 .await?;
675 Ok(blocked)
676 }
677
678 pub async fn block(context: &Context, id: ContactId) -> Result<()> {
680 set_blocked(context, Sync, id, true).await
681 }
682
683 pub async fn unblock(context: &Context, id: ContactId) -> Result<()> {
685 set_blocked(context, Sync, id, false).await
686 }
687
688 pub async fn create(context: &Context, name: &str, addr: &str) -> Result<ContactId> {
698 Self::create_ex(context, Sync, name, addr).await
699 }
700
701 pub(crate) async fn create_ex(
702 context: &Context,
703 sync: sync::Sync,
704 name: &str,
705 addr: &str,
706 ) -> Result<ContactId> {
707 let (name, addr) = sanitize_name_and_addr(name, addr);
708 let addr = ContactAddress::new(&addr)?;
709
710 let (contact_id, sth_modified) =
711 Contact::add_or_lookup(context, &name, &addr, Origin::ManuallyCreated)
712 .await
713 .context("add_or_lookup")?;
714 let blocked = Contact::is_blocked_load(context, contact_id).await?;
715 match sth_modified {
716 Modifier::None => {}
717 Modifier::Modified | Modifier::Created => {
718 context.emit_event(EventType::ContactsChanged(Some(contact_id)))
719 }
720 }
721 if blocked {
722 set_blocked(context, Nosync, contact_id, false).await?;
723 }
724
725 if sync.into() && sth_modified != Modifier::None {
726 chat::sync(
727 context,
728 chat::SyncId::ContactAddr(addr.to_string()),
729 chat::SyncAction::Rename(name.to_string()),
730 )
731 .await
732 .log_err(context)
733 .ok();
734 }
735 Ok(contact_id)
736 }
737
738 pub async fn mark_noticed(context: &Context, id: ContactId) -> Result<()> {
740 context
741 .sql
742 .execute(
743 "UPDATE msgs SET state=? WHERE from_id=? AND state=?;",
744 (MessageState::InNoticed, id, MessageState::InFresh),
745 )
746 .await?;
747 Ok(())
748 }
749
750 pub fn is_bot(&self) -> bool {
752 self.is_bot
753 }
754
755 pub async fn lookup_id_by_addr(
777 context: &Context,
778 addr: &str,
779 min_origin: Origin,
780 ) -> Result<Option<ContactId>> {
781 Self::lookup_id_by_addr_ex(context, addr, min_origin, Some(Blocked::Not)).await
782 }
783
784 pub(crate) async fn lookup_id_by_addr_ex(
787 context: &Context,
788 addr: &str,
789 min_origin: Origin,
790 blocked: Option<Blocked>,
791 ) -> Result<Option<ContactId>> {
792 if addr.is_empty() {
793 bail!("lookup_id_by_addr: empty address");
794 }
795
796 let addr_normalized = addr_normalize(addr);
797
798 if context.is_configured().await? && context.is_self_addr(addr).await? {
799 return Ok(Some(ContactId::SELF));
800 }
801
802 let id = context
803 .sql
804 .query_get_value(
805 "SELECT id FROM contacts
806 WHERE addr=?1 COLLATE NOCASE
807 AND id>?2 AND origin>=?3 AND (? OR blocked=?)
808 ORDER BY
809 (
810 SELECT COUNT(*) FROM chats c
811 INNER JOIN chats_contacts cc
812 ON c.id=cc.chat_id
813 WHERE c.type=?
814 AND c.id>?
815 AND c.blocked=?
816 AND cc.contact_id=contacts.id
817 ) DESC,
818 last_seen DESC, fingerprint DESC
819 LIMIT 1",
820 (
821 &addr_normalized,
822 ContactId::LAST_SPECIAL,
823 min_origin as u32,
824 blocked.is_none(),
825 blocked.unwrap_or(Blocked::Not),
826 Chattype::Single,
827 constants::DC_CHAT_ID_LAST_SPECIAL,
828 blocked.unwrap_or(Blocked::Not),
829 ),
830 )
831 .await?;
832 Ok(id)
833 }
834
835 pub(crate) async fn add_or_lookup(
836 context: &Context,
837 name: &str,
838 addr: &ContactAddress,
839 origin: Origin,
840 ) -> Result<(ContactId, Modifier)> {
841 Self::add_or_lookup_ex(context, name, addr, "", origin).await
842 }
843
844 pub(crate) async fn add_or_lookup_ex(
872 context: &Context,
873 name: &str,
874 addr: &str,
875 fingerprint: &str,
876 mut origin: Origin,
877 ) -> Result<(ContactId, Modifier)> {
878 let mut sth_modified = Modifier::None;
879
880 ensure!(
881 !addr.is_empty() || !fingerprint.is_empty(),
882 "Can not add_or_lookup empty address"
883 );
884 ensure!(origin != Origin::Unknown, "Missing valid origin");
885
886 if context.is_configured().await? && context.is_self_addr(addr).await? {
887 return Ok((ContactId::SELF, sth_modified));
888 }
889
890 if !fingerprint.is_empty() && context.is_configured().await? {
891 let fingerprint_self = self_fingerprint(context)
892 .await
893 .context("self_fingerprint")?;
894 if fingerprint == fingerprint_self {
895 return Ok((ContactId::SELF, sth_modified));
896 }
897 }
898
899 let mut name = sanitize_name(name);
900 if origin <= Origin::OutgoingTo {
901 if addr.contains("noreply")
903 || addr.contains("no-reply")
904 || addr.starts_with("notifications@")
905 || (addr.len() > 50 && addr.contains('+'))
907 {
908 info!(context, "hiding contact {}", addr);
909 origin = Origin::Hidden;
910 name = "".to_string();
914 }
915 }
916
917 let manual = matches!(
922 origin,
923 Origin::ManuallyCreated | Origin::AddressBook | Origin::UnhandledQrScan
924 );
925
926 let mut update_addr = false;
927
928 let row_id = context
929 .sql
930 .transaction(|transaction| {
931 let row = transaction
932 .query_row(
933 "SELECT id, name, addr, origin, authname
934 FROM contacts
935 WHERE fingerprint=?1 AND
936 (?1<>'' OR addr=?2 COLLATE NOCASE)",
937 (fingerprint, addr),
938 |row| {
939 let row_id: u32 = row.get(0)?;
940 let row_name: String = row.get(1)?;
941 let row_addr: String = row.get(2)?;
942 let row_origin: Origin = row.get(3)?;
943 let row_authname: String = row.get(4)?;
944
945 Ok((row_id, row_name, row_addr, row_origin, row_authname))
946 },
947 )
948 .optional()?;
949
950 let row_id;
951 if let Some((id, row_name, row_addr, row_origin, row_authname)) = row {
952 let update_name = manual && name != row_name;
953 let update_authname = !manual
954 && name != row_authname
955 && !name.is_empty()
956 && (origin >= row_origin
957 || origin == Origin::IncomingUnknownFrom
958 || row_authname.is_empty());
959
960 row_id = id;
961 if origin >= row_origin && addr != row_addr {
962 update_addr = true;
963 }
964 if update_name || update_authname || update_addr || origin > row_origin {
965 let new_name = if update_name {
966 name.to_string()
967 } else {
968 row_name
969 };
970
971 transaction.execute(
972 "UPDATE contacts SET name=?, addr=?, origin=?, authname=? WHERE id=?;",
973 (
974 new_name,
975 if update_addr {
976 addr.to_string()
977 } else {
978 row_addr
979 },
980 if origin > row_origin {
981 origin
982 } else {
983 row_origin
984 },
985 if update_authname {
986 name.to_string()
987 } else {
988 row_authname
989 },
990 row_id,
991 ),
992 )?;
993
994 if update_name || update_authname {
995 let contact_id = ContactId::new(row_id);
996 update_chat_names(context, transaction, contact_id)?;
997 }
998 sth_modified = Modifier::Modified;
999 }
1000 } else {
1001 let update_name = manual;
1002 let update_authname = !manual;
1003
1004 transaction.execute(
1005 "INSERT INTO contacts (name, addr, fingerprint, origin, authname)
1006 VALUES (?, ?, ?, ?, ?);",
1007 (
1008 if update_name { &name } else { "" },
1009 &addr,
1010 fingerprint,
1011 origin,
1012 if update_authname { &name } else { "" },
1013 ),
1014 )?;
1015
1016 sth_modified = Modifier::Created;
1017 row_id = u32::try_from(transaction.last_insert_rowid())?;
1018 if fingerprint.is_empty() {
1019 info!(context, "Added contact id={row_id} addr={addr}.");
1020 } else {
1021 info!(
1022 context,
1023 "Added contact id={row_id} fpr={fingerprint} addr={addr}."
1024 );
1025 }
1026 }
1027 Ok(row_id)
1028 })
1029 .await?;
1030
1031 let contact_id = ContactId::new(row_id);
1032
1033 Ok((contact_id, sth_modified))
1034 }
1035
1036 pub async fn add_address_book(context: &Context, addr_book: &str) -> Result<usize> {
1054 let mut modify_cnt = 0;
1055
1056 for (name, addr) in split_address_book(addr_book) {
1057 let (name, addr) = sanitize_name_and_addr(name, addr);
1058 match ContactAddress::new(&addr) {
1059 Ok(addr) => {
1060 match Contact::add_or_lookup(context, &name, &addr, Origin::AddressBook).await {
1061 Ok((_, modified)) => {
1062 if modified != Modifier::None {
1063 modify_cnt += 1
1064 }
1065 }
1066 Err(err) => {
1067 warn!(
1068 context,
1069 "Failed to add address {} from address book: {}", addr, err
1070 );
1071 }
1072 }
1073 }
1074 Err(err) => {
1075 warn!(context, "{:#}.", err);
1076 }
1077 }
1078 }
1079 if modify_cnt > 0 {
1080 context.emit_event(EventType::ContactsChanged(None));
1081 }
1082
1083 Ok(modify_cnt)
1084 }
1085
1086 pub async fn get_all(
1096 context: &Context,
1097 listflags: u32,
1098 query: Option<&str>,
1099 ) -> Result<Vec<ContactId>> {
1100 let self_addrs = context
1101 .get_all_self_addrs()
1102 .await?
1103 .into_iter()
1104 .collect::<HashSet<_>>();
1105 let mut add_self = false;
1106 let mut ret = Vec::new();
1107 let flag_add_self = (listflags & constants::DC_GCL_ADD_SELF) != 0;
1108 let flag_address = (listflags & constants::DC_GCL_ADDRESS) != 0;
1109 let minimal_origin = if context.get_config_bool(Config::Bot).await? {
1110 Origin::Unknown
1111 } else {
1112 Origin::IncomingReplyTo
1113 };
1114 if query.is_some() {
1115 let s3str_like_cmd = format!("%{}%", query.unwrap_or(""));
1116 context
1117 .sql
1118 .query_map(
1119 "SELECT c.id, c.addr FROM contacts c
1120 WHERE c.id>?
1121 AND (c.fingerprint='')=?
1122 AND c.origin>=? \
1123 AND c.blocked=0 \
1124 AND (iif(c.name='',c.authname,c.name) LIKE ? OR c.addr LIKE ?) \
1125 ORDER BY c.last_seen DESC, c.id DESC;",
1126 (
1127 ContactId::LAST_SPECIAL,
1128 flag_address,
1129 minimal_origin,
1130 &s3str_like_cmd,
1131 &s3str_like_cmd,
1132 ),
1133 |row| {
1134 let id: ContactId = row.get(0)?;
1135 let addr: String = row.get(1)?;
1136 Ok((id, addr))
1137 },
1138 |rows| {
1139 for row in rows {
1140 let (id, addr) = row?;
1141 if !self_addrs.contains(&addr) {
1142 ret.push(id);
1143 }
1144 }
1145 Ok(())
1146 },
1147 )
1148 .await?;
1149
1150 if let Some(query) = query {
1151 let self_addr = context
1152 .get_config(Config::ConfiguredAddr)
1153 .await?
1154 .unwrap_or_default();
1155 let self_name = context
1156 .get_config(Config::Displayname)
1157 .await?
1158 .unwrap_or_default();
1159 let self_name2 = stock_str::self_msg(context);
1160
1161 if self_addr.contains(query)
1162 || self_name.contains(query)
1163 || self_name2.await.contains(query)
1164 {
1165 add_self = true;
1166 }
1167 } else {
1168 add_self = true;
1169 }
1170 } else {
1171 add_self = true;
1172
1173 context
1174 .sql
1175 .query_map(
1176 "SELECT id, addr FROM contacts
1177 WHERE id>?
1178 AND (fingerprint='')=?
1179 AND origin>=?
1180 AND blocked=0
1181 ORDER BY last_seen DESC, id DESC;",
1182 (ContactId::LAST_SPECIAL, flag_address, minimal_origin),
1183 |row| {
1184 let id: ContactId = row.get(0)?;
1185 let addr: String = row.get(1)?;
1186 Ok((id, addr))
1187 },
1188 |rows| {
1189 for row in rows {
1190 let (id, addr) = row?;
1191 if !self_addrs.contains(&addr) {
1192 ret.push(id);
1193 }
1194 }
1195 Ok(())
1196 },
1197 )
1198 .await?;
1199 }
1200
1201 if flag_add_self && add_self {
1202 ret.push(ContactId::SELF);
1203 }
1204
1205 Ok(ret)
1206 }
1207
1208 async fn update_blocked_mailinglist_contacts(context: &Context) -> Result<()> {
1214 context
1215 .sql
1216 .transaction(move |transaction| {
1217 let mut stmt = transaction.prepare(
1218 "SELECT name, grpid, type FROM chats WHERE (type=? OR type=?) AND blocked=?",
1219 )?;
1220 let rows = stmt.query_map(
1221 (Chattype::Mailinglist, Chattype::InBroadcast, Blocked::Yes),
1222 |row| {
1223 let name: String = row.get(0)?;
1224 let grpid: String = row.get(1)?;
1225 let typ: Chattype = row.get(2)?;
1226 Ok((name, grpid, typ))
1227 },
1228 )?;
1229 let blocked_mailinglists = rows.collect::<std::result::Result<Vec<_>, _>>()?;
1230 for (name, grpid, typ) in blocked_mailinglists {
1231 let count = transaction.query_row(
1232 "SELECT COUNT(id) FROM contacts WHERE addr=?",
1233 [&grpid],
1234 |row| {
1235 let count: isize = row.get(0)?;
1236 Ok(count)
1237 },
1238 )?;
1239 if count == 0 {
1240 transaction.execute("INSERT INTO contacts (addr) VALUES (?)", [&grpid])?;
1241 }
1242
1243 let fingerprint = if typ == Chattype::InBroadcast {
1244 "Blocked_broadcast"
1247 } else {
1248 ""
1249 };
1250 transaction.execute(
1252 "UPDATE contacts SET name=?, origin=?, blocked=1, fingerprint=? WHERE addr=?",
1253 (&name, Origin::MailinglistAddress, fingerprint, &grpid),
1254 )?;
1255 }
1256 Ok(())
1257 })
1258 .await?;
1259 Ok(())
1260 }
1261
1262 pub async fn get_blocked_cnt(context: &Context) -> Result<usize> {
1264 let count = context
1265 .sql
1266 .count(
1267 "SELECT COUNT(*) FROM contacts WHERE id>? AND blocked!=0",
1268 (ContactId::LAST_SPECIAL,),
1269 )
1270 .await?;
1271 Ok(count)
1272 }
1273
1274 pub async fn get_all_blocked(context: &Context) -> Result<Vec<ContactId>> {
1276 Contact::update_blocked_mailinglist_contacts(context)
1277 .await
1278 .context("cannot update blocked mailinglist contacts")?;
1279
1280 let list = context
1281 .sql
1282 .query_map_vec(
1283 "SELECT id FROM contacts WHERE id>? AND blocked!=0 ORDER BY last_seen DESC, id DESC;",
1284 (ContactId::LAST_SPECIAL,),
1285 |row| {
1286 let contact_id: ContactId = row.get(0)?;
1287 Ok(contact_id)
1288 }
1289 )
1290 .await?;
1291 Ok(list)
1292 }
1293
1294 pub async fn get_encrinfo(context: &Context, contact_id: ContactId) -> Result<String> {
1300 ensure!(
1301 !contact_id.is_special(),
1302 "Can not provide encryption info for special contact"
1303 );
1304
1305 let contact = Contact::get_by_id(context, contact_id).await?;
1306 let addr = context
1307 .get_config(Config::ConfiguredAddr)
1308 .await?
1309 .unwrap_or_default();
1310
1311 let Some(fingerprint_other) = contact.fingerprint() else {
1312 return Ok(stock_str::encr_none(context).await);
1313 };
1314 let fingerprint_other = fingerprint_other.to_string();
1315
1316 let stock_message = if contact.public_key(context).await?.is_some() {
1317 stock_str::e2e_available(context).await
1318 } else {
1319 stock_str::encr_none(context).await
1320 };
1321
1322 let finger_prints = stock_str::finger_prints(context).await;
1323 let mut ret = format!("{stock_message}.\n{finger_prints}:");
1324
1325 let fingerprint_self = load_self_public_key(context)
1326 .await?
1327 .dc_fingerprint()
1328 .to_string();
1329 if addr < contact.addr {
1330 cat_fingerprint(
1331 &mut ret,
1332 &stock_str::self_msg(context).await,
1333 &addr,
1334 &fingerprint_self,
1335 );
1336 cat_fingerprint(
1337 &mut ret,
1338 contact.get_display_name(),
1339 &contact.addr,
1340 &fingerprint_other,
1341 );
1342 } else {
1343 cat_fingerprint(
1344 &mut ret,
1345 contact.get_display_name(),
1346 &contact.addr,
1347 &fingerprint_other,
1348 );
1349 cat_fingerprint(
1350 &mut ret,
1351 &stock_str::self_msg(context).await,
1352 &addr,
1353 &fingerprint_self,
1354 );
1355 }
1356
1357 Ok(ret)
1358 }
1359
1360 pub async fn delete(context: &Context, contact_id: ContactId) -> Result<()> {
1366 ensure!(!contact_id.is_special(), "Can not delete special contact");
1367
1368 context
1369 .sql
1370 .transaction(move |transaction| {
1371 let deleted_contacts = transaction.execute(
1374 "DELETE FROM contacts WHERE id=?
1375 AND (SELECT COUNT(*) FROM chats_contacts WHERE contact_id=?)=0;",
1376 (contact_id, contact_id),
1377 )?;
1378 if deleted_contacts == 0 {
1379 transaction.execute(
1380 "UPDATE contacts SET origin=? WHERE id=?;",
1381 (Origin::Hidden, contact_id),
1382 )?;
1383 }
1384 Ok(())
1385 })
1386 .await?;
1387
1388 context.emit_event(EventType::ContactsChanged(None));
1389 Ok(())
1390 }
1391
1392 pub async fn update_param(&self, context: &Context) -> Result<()> {
1394 context
1395 .sql
1396 .execute(
1397 "UPDATE contacts SET param=? WHERE id=?",
1398 (self.param.to_string(), self.id),
1399 )
1400 .await?;
1401 Ok(())
1402 }
1403
1404 pub async fn update_status(&self, context: &Context) -> Result<()> {
1406 context
1407 .sql
1408 .execute(
1409 "UPDATE contacts SET status=? WHERE id=?",
1410 (&self.status, self.id),
1411 )
1412 .await?;
1413 Ok(())
1414 }
1415
1416 pub fn get_id(&self) -> ContactId {
1418 self.id
1419 }
1420
1421 pub fn get_addr(&self) -> &str {
1423 &self.addr
1424 }
1425
1426 pub fn is_key_contact(&self) -> bool {
1429 self.fingerprint.is_some()
1430 }
1431
1432 pub fn fingerprint(&self) -> Option<Fingerprint> {
1436 if let Some(fingerprint) = &self.fingerprint {
1437 fingerprint.parse().ok()
1438 } else {
1439 None
1440 }
1441 }
1442
1443 pub async fn public_key(&self, context: &Context) -> Result<Option<SignedPublicKey>> {
1450 if self.id == ContactId::SELF {
1451 return Ok(Some(load_self_public_key(context).await?));
1452 }
1453
1454 if let Some(fingerprint) = &self.fingerprint {
1455 if let Some(public_key_bytes) = context
1456 .sql
1457 .query_row_optional(
1458 "SELECT public_key
1459 FROM public_keys
1460 WHERE fingerprint=?",
1461 (fingerprint,),
1462 |row| {
1463 let bytes: Vec<u8> = row.get(0)?;
1464 Ok(bytes)
1465 },
1466 )
1467 .await?
1468 {
1469 let public_key = SignedPublicKey::from_slice(&public_key_bytes)?;
1470 Ok(Some(public_key))
1471 } else {
1472 Ok(None)
1473 }
1474 } else {
1475 Ok(None)
1476 }
1477 }
1478
1479 pub fn get_authname(&self) -> &str {
1481 &self.authname
1482 }
1483
1484 pub fn get_name(&self) -> &str {
1490 &self.name
1491 }
1492
1493 pub fn get_display_name(&self) -> &str {
1499 if !self.name.is_empty() {
1500 return &self.name;
1501 }
1502 if !self.authname.is_empty() {
1503 return &self.authname;
1504 }
1505 &self.addr
1506 }
1507
1508 pub fn get_name_n_addr(&self) -> String {
1519 if !self.name.is_empty() {
1520 format!("{} ({})", self.name, self.addr)
1521 } else if !self.authname.is_empty() {
1522 format!("{} ({})", self.authname, self.addr)
1523 } else {
1524 (&self.addr).into()
1525 }
1526 }
1527
1528 pub async fn get_profile_image(&self, context: &Context) -> Result<Option<PathBuf>> {
1532 self.get_profile_image_ex(context, true).await
1533 }
1534
1535 async fn get_profile_image_ex(
1539 &self,
1540 context: &Context,
1541 show_fallback_icon: bool,
1542 ) -> Result<Option<PathBuf>> {
1543 if self.id == ContactId::SELF {
1544 if let Some(p) = context.get_config(Config::Selfavatar).await? {
1545 return Ok(Some(PathBuf::from(p))); }
1547 } else if self.id == ContactId::DEVICE {
1548 return Ok(Some(chat::get_device_icon(context).await?));
1549 }
1550 if show_fallback_icon && !self.id.is_special() && !self.is_key_contact() {
1551 return Ok(Some(chat::get_unencrypted_icon(context).await?));
1552 }
1553 if let Some(image_rel) = self.param.get(Param::ProfileImage)
1554 && !image_rel.is_empty()
1555 {
1556 return Ok(Some(get_abs_path(context, Path::new(image_rel))));
1557 }
1558 Ok(None)
1559 }
1560
1561 pub fn get_color(&self) -> u32 {
1565 get_color(self.id == ContactId::SELF, &self.addr, &self.fingerprint())
1566 }
1567
1568 pub async fn get_or_gen_color(&self, context: &Context) -> Result<u32> {
1573 let mut fpr = self.fingerprint();
1574 if fpr.is_none() && self.id == ContactId::SELF {
1575 fpr = Some(load_self_public_key(context).await?.dc_fingerprint());
1576 }
1577 Ok(get_color(self.id == ContactId::SELF, &self.addr, &fpr))
1578 }
1579
1580 pub fn get_status(&self) -> &str {
1584 self.status.as_str()
1585 }
1586
1587 pub async fn e2ee_avail(&self, context: &Context) -> Result<bool> {
1589 if self.id == ContactId::SELF {
1590 return Ok(true);
1592 }
1593 Ok(self.public_key(context).await?.is_some())
1594 }
1595
1596 pub async fn is_verified(&self, context: &Context) -> Result<bool> {
1609 if self.id == ContactId::SELF {
1612 return Ok(true);
1613 }
1614
1615 Ok(self.get_verifier_id(context).await?.is_some())
1616 }
1617
1618 pub async fn get_verifier_id(&self, context: &Context) -> Result<Option<Option<ContactId>>> {
1628 let verifier_id: u32 = context
1629 .sql
1630 .query_get_value("SELECT verifier FROM contacts WHERE id=?", (self.id,))
1631 .await?
1632 .with_context(|| format!("Contact {} does not exist", self.id))?;
1633
1634 if verifier_id == 0 {
1635 Ok(None)
1636 } else if verifier_id == self.id.to_u32() {
1637 Ok(Some(None))
1638 } else {
1639 Ok(Some(Some(ContactId::new(verifier_id))))
1640 }
1641 }
1642
1643 pub async fn get_real_cnt(context: &Context) -> Result<usize> {
1645 if !context.sql.is_open().await {
1646 return Ok(0);
1647 }
1648
1649 let count = context
1650 .sql
1651 .count(
1652 "SELECT COUNT(*) FROM contacts WHERE id>?;",
1653 (ContactId::LAST_SPECIAL,),
1654 )
1655 .await?;
1656 Ok(count)
1657 }
1658
1659 pub async fn real_exists_by_id(context: &Context, contact_id: ContactId) -> Result<bool> {
1661 if contact_id.is_special() {
1662 return Ok(false);
1663 }
1664
1665 let exists = context
1666 .sql
1667 .exists("SELECT COUNT(*) FROM contacts WHERE id=?;", (contact_id,))
1668 .await?;
1669 Ok(exists)
1670 }
1671}
1672
1673pub fn get_color(is_self: bool, addr: &str, fingerprint: &Option<Fingerprint>) -> u32 {
1679 if let Some(fingerprint) = fingerprint {
1680 str_to_color(&fingerprint.hex())
1681 } else if is_self {
1682 0x808080
1683 } else {
1684 str_to_color(&to_lowercase(addr))
1685 }
1686}
1687
1688fn update_chat_names(
1692 context: &Context,
1693 transaction: &rusqlite::Connection,
1694 contact_id: ContactId,
1695) -> Result<()> {
1696 let chat_id: Option<ChatId> = transaction.query_row(
1697 "SELECT id FROM chats WHERE type=? AND id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?)",
1698 (Chattype::Single, contact_id),
1699 |row| {
1700 let chat_id: ChatId = row.get(0)?;
1701 Ok(chat_id)
1702 }
1703 ).optional()?;
1704
1705 if let Some(chat_id) = chat_id {
1706 let (addr, name, authname) = transaction.query_row(
1707 "SELECT addr, name, authname
1708 FROM contacts
1709 WHERE id=?",
1710 (contact_id,),
1711 |row| {
1712 let addr: String = row.get(0)?;
1713 let name: String = row.get(1)?;
1714 let authname: String = row.get(2)?;
1715 Ok((addr, name, authname))
1716 },
1717 )?;
1718
1719 let chat_name = if !name.is_empty() {
1720 name
1721 } else if !authname.is_empty() {
1722 authname
1723 } else {
1724 addr
1725 };
1726
1727 let count = transaction.execute(
1728 "UPDATE chats SET name=?1 WHERE id=?2 AND name!=?1",
1729 (chat_name, chat_id),
1730 )?;
1731
1732 if count > 0 {
1733 context.emit_event(EventType::ChatModified(chat_id));
1735 chatlist_events::emit_chatlist_items_changed_for_contact(context, contact_id);
1736 }
1737 }
1738
1739 Ok(())
1740}
1741
1742pub(crate) async fn set_blocked(
1743 context: &Context,
1744 sync: sync::Sync,
1745 contact_id: ContactId,
1746 new_blocking: bool,
1747) -> Result<()> {
1748 ensure!(
1749 !contact_id.is_special(),
1750 "Can't block special contact {contact_id}"
1751 );
1752 let contact = Contact::get_by_id(context, contact_id).await?;
1753
1754 if contact.blocked != new_blocking {
1755 context
1756 .sql
1757 .execute(
1758 "UPDATE contacts SET blocked=? WHERE id=?;",
1759 (i32::from(new_blocking), contact_id),
1760 )
1761 .await?;
1762
1763 if context
1769 .sql
1770 .execute(
1771 r#"
1772UPDATE chats
1773SET blocked=?
1774WHERE type=? AND id IN (
1775 SELECT chat_id FROM chats_contacts WHERE contact_id=?
1776);
1777"#,
1778 (new_blocking, Chattype::Single, contact_id),
1779 )
1780 .await
1781 .is_ok()
1782 {
1783 Contact::mark_noticed(context, contact_id).await?;
1784 context.emit_event(EventType::ContactsChanged(Some(contact_id)));
1785 }
1786
1787 if !new_blocking
1790 && contact.origin == Origin::MailinglistAddress
1791 && let Some((chat_id, ..)) = chat::get_chat_id_by_grpid(context, &contact.addr).await?
1792 {
1793 chat_id.unblock_ex(context, Nosync).await?;
1794 }
1795
1796 if sync.into() {
1797 let action = match new_blocking {
1798 true => chat::SyncAction::Block,
1799 false => chat::SyncAction::Unblock,
1800 };
1801 let sync_id = if let Some(fingerprint) = contact.fingerprint() {
1802 chat::SyncId::ContactFingerprint(fingerprint.hex())
1803 } else {
1804 chat::SyncId::ContactAddr(contact.addr.clone())
1805 };
1806
1807 chat::sync(context, sync_id, action)
1808 .await
1809 .log_err(context)
1810 .ok();
1811 }
1812 }
1813
1814 chatlist_events::emit_chatlist_changed(context);
1815 Ok(())
1816}
1817
1818pub(crate) async fn set_profile_image(
1825 context: &Context,
1826 contact_id: ContactId,
1827 profile_image: &AvatarAction,
1828) -> Result<()> {
1829 let mut contact = Contact::get_by_id(context, contact_id).await?;
1830 let changed = match profile_image {
1831 AvatarAction::Change(profile_image) => {
1832 if contact_id == ContactId::SELF {
1833 context
1834 .set_config_ex(Nosync, Config::Selfavatar, Some(profile_image))
1835 .await?;
1836 } else {
1837 contact.param.set(Param::ProfileImage, profile_image);
1838 }
1839 true
1840 }
1841 AvatarAction::Delete => {
1842 if contact_id == ContactId::SELF {
1843 context
1844 .set_config_ex(Nosync, Config::Selfavatar, None)
1845 .await?;
1846 } else {
1847 contact.param.remove(Param::ProfileImage);
1848 }
1849 true
1850 }
1851 };
1852 if changed {
1853 contact.update_param(context).await?;
1854 context.emit_event(EventType::ContactsChanged(Some(contact_id)));
1855 chatlist_events::emit_chatlist_item_changed_for_contact_chat(context, contact_id).await;
1856 }
1857 Ok(())
1858}
1859
1860pub(crate) async fn set_status(
1864 context: &Context,
1865 contact_id: ContactId,
1866 status: String,
1867) -> Result<()> {
1868 if contact_id == ContactId::SELF {
1869 context
1870 .set_config_ex(Nosync, Config::Selfstatus, Some(&status))
1871 .await?;
1872 } else {
1873 let mut contact = Contact::get_by_id(context, contact_id).await?;
1874
1875 if contact.status != status {
1876 contact.status = status;
1877 contact.update_status(context).await?;
1878 context.emit_event(EventType::ContactsChanged(Some(contact_id)));
1879 }
1880 }
1881 Ok(())
1882}
1883
1884pub(crate) async fn update_last_seen(
1886 context: &Context,
1887 contact_id: ContactId,
1888 timestamp: i64,
1889) -> Result<()> {
1890 ensure!(
1891 !contact_id.is_special(),
1892 "Can not update special contact last seen timestamp"
1893 );
1894
1895 if context
1896 .sql
1897 .execute(
1898 "UPDATE contacts SET last_seen = ?1 WHERE last_seen < ?1 AND id = ?2",
1899 (timestamp, contact_id),
1900 )
1901 .await?
1902 > 0
1903 && timestamp > time() - SEEN_RECENTLY_SECONDS
1904 {
1905 context.emit_event(EventType::ContactsChanged(Some(contact_id)));
1906 context
1907 .scheduler
1908 .interrupt_recently_seen(contact_id, timestamp)
1909 .await;
1910 }
1911 Ok(())
1912}
1913
1914pub(crate) async fn mark_contact_id_as_verified(
1918 context: &Context,
1919 contact_id: ContactId,
1920 verifier_id: Option<ContactId>,
1921) -> Result<()> {
1922 ensure_and_debug_assert_ne!(contact_id, ContactId::SELF,);
1923 ensure_and_debug_assert_ne!(
1924 Some(contact_id),
1925 verifier_id,
1926 "Contact cannot be verified by self",
1927 );
1928 let by_self = verifier_id == Some(ContactId::SELF);
1929 let mut verifier_id = verifier_id.unwrap_or(contact_id);
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, verifier_verifier_id): (String, ContactId) = transaction
1943 .query_row(
1944 "SELECT fingerprint, verifier FROM contacts WHERE id=?",
1945 (verifier_id,),
1946 |row| Ok((row.get(0)?, row.get(1)?)),
1947 )?;
1948 if verifier_fingerprint.is_empty() {
1949 bail!(
1950 "Contact {contact_id} cannot be verified by non-key-contact {verifier_id}"
1951 );
1952 }
1953 ensure!(
1954 verifier_id == contact_id || verifier_verifier_id != ContactId::UNDEFINED,
1955 "Contact {contact_id} cannot be verified by unverified contact {verifier_id}",
1956 );
1957 if verifier_verifier_id == verifier_id {
1958 verifier_id = contact_id;
1963 }
1964 }
1965 transaction.execute(
1966 "UPDATE contacts SET verifier=?1
1967 WHERE id=?2 AND (verifier=0 OR verifier=id OR ?3)",
1968 (verifier_id, contact_id, by_self),
1969 )?;
1970 Ok(())
1971 })
1972 .await?;
1973 Ok(())
1974}
1975
1976fn cat_fingerprint(ret: &mut String, name: &str, addr: &str, fingerprint: &str) {
1977 *ret += &format!("\n\n{name} ({addr}):\n{fingerprint}");
1978}
1979
1980fn split_address_book(book: &str) -> Vec<(&str, &str)> {
1981 book.lines()
1982 .collect::<Vec<&str>>()
1983 .chunks(2)
1984 .filter_map(|chunk| {
1985 let name = chunk.first()?;
1986 let addr = chunk.get(1)?;
1987 Some((*name, *addr))
1988 })
1989 .collect()
1990}
1991
1992#[derive(Debug)]
1993pub(crate) struct RecentlySeenInterrupt {
1994 contact_id: ContactId,
1995 timestamp: i64,
1996}
1997
1998#[derive(Debug)]
1999pub(crate) struct RecentlySeenLoop {
2000 handle: task::JoinHandle<()>,
2002
2003 interrupt_send: Sender<RecentlySeenInterrupt>,
2004}
2005
2006impl RecentlySeenLoop {
2007 pub(crate) fn new(context: Context) -> Self {
2008 let (interrupt_send, interrupt_recv) = channel::bounded(1);
2009
2010 let handle = task::spawn(Self::run(context, interrupt_recv));
2011 Self {
2012 handle,
2013 interrupt_send,
2014 }
2015 }
2016
2017 async fn run(context: Context, interrupt: Receiver<RecentlySeenInterrupt>) {
2018 type MyHeapElem = (Reverse<i64>, ContactId);
2019
2020 let now = SystemTime::now();
2021 let now_ts = now
2022 .duration_since(SystemTime::UNIX_EPOCH)
2023 .unwrap_or_default()
2024 .as_secs() as i64;
2025
2026 let mut unseen_queue: BinaryHeap<MyHeapElem> = context
2032 .sql
2033 .query_map_collect(
2034 "SELECT id, last_seen FROM contacts
2035 WHERE last_seen > ?",
2036 (now_ts - SEEN_RECENTLY_SECONDS,),
2037 |row| {
2038 let contact_id: ContactId = row.get("id")?;
2039 let last_seen: i64 = row.get("last_seen")?;
2040 Ok((Reverse(last_seen + SEEN_RECENTLY_SECONDS), contact_id))
2041 },
2042 )
2043 .await
2044 .unwrap_or_default();
2045
2046 loop {
2047 let now = SystemTime::now();
2048 let (until, contact_id) =
2049 if let Some((Reverse(timestamp), contact_id)) = unseen_queue.peek() {
2050 (
2051 UNIX_EPOCH
2052 + Duration::from_secs((*timestamp).try_into().unwrap_or(u64::MAX))
2053 + Duration::from_secs(1),
2054 Some(contact_id),
2055 )
2056 } else {
2057 (now + Duration::from_secs(86400), None)
2059 };
2060
2061 if let Ok(duration) = until.duration_since(now) {
2062 info!(
2063 context,
2064 "Recently seen loop waiting for {} or interrupt",
2065 duration_to_str(duration)
2066 );
2067
2068 match timeout(duration, interrupt.recv()).await {
2069 Err(_) => {
2070 if let Some(contact_id) = contact_id {
2072 context.emit_event(EventType::ContactsChanged(Some(*contact_id)));
2073 chatlist_events::emit_chatlist_item_changed_for_contact_chat(
2074 &context,
2075 *contact_id,
2076 )
2077 .await;
2078 unseen_queue.pop();
2079 }
2080 }
2081 Ok(Err(err)) => {
2082 warn!(
2083 context,
2084 "Error receiving an interruption in recently seen loop: {}", err
2085 );
2086 return;
2089 }
2090 Ok(Ok(RecentlySeenInterrupt {
2091 contact_id,
2092 timestamp,
2093 })) => {
2094 if contact_id != ContactId::UNDEFINED {
2096 unseen_queue
2097 .push((Reverse(timestamp + SEEN_RECENTLY_SECONDS), contact_id));
2098 }
2099 }
2100 }
2101 } else {
2102 info!(
2103 context,
2104 "Recently seen loop is not waiting, event is already due."
2105 );
2106
2107 if let Some(contact_id) = contact_id {
2109 context.emit_event(EventType::ContactsChanged(Some(*contact_id)));
2110 chatlist_events::emit_chatlist_item_changed_for_contact_chat(
2111 &context,
2112 *contact_id,
2113 )
2114 .await;
2115 }
2116 unseen_queue.pop();
2117 }
2118 }
2119 }
2120
2121 pub(crate) fn try_interrupt(&self, contact_id: ContactId, timestamp: i64) {
2122 self.interrupt_send
2123 .try_send(RecentlySeenInterrupt {
2124 contact_id,
2125 timestamp,
2126 })
2127 .ok();
2128 }
2129
2130 #[cfg(test)]
2131 pub(crate) async fn interrupt(&self, contact_id: ContactId, timestamp: i64) {
2132 self.interrupt_send
2133 .send(RecentlySeenInterrupt {
2134 contact_id,
2135 timestamp,
2136 })
2137 .await
2138 .unwrap();
2139 }
2140
2141 pub(crate) async fn abort(self) {
2142 self.handle.abort();
2143
2144 self.handle.await.ok();
2148 }
2149}
2150
2151#[cfg(test)]
2152mod contact_tests;