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, normalize_text, 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 authname;
119 let name_or_authname = if !name.is_empty() {
120 name
121 } else {
122 authname = transaction.query_row(
123 "SELECT authname FROM contacts WHERE id=?",
124 (self,),
125 |row| {
126 let authname: String = row.get(0)?;
127 Ok(authname)
128 },
129 )?;
130 &authname
131 };
132 let is_changed = transaction.execute(
133 "UPDATE contacts SET name=?1, name_normalized=?2 WHERE id=?3 AND name!=?1",
134 (name, normalize_text(name_or_authname), self),
135 )? > 0;
136 if is_changed {
137 update_chat_names(context, transaction, self)?;
138 let (addr, fingerprint) = transaction.query_row(
139 "SELECT addr, fingerprint FROM contacts WHERE id=?",
140 (self,),
141 |row| {
142 let addr: String = row.get(0)?;
143 let fingerprint: String = row.get(1)?;
144 Ok((addr, fingerprint))
145 },
146 )?;
147 Ok(Some((addr, fingerprint)))
148 } else {
149 Ok(None)
150 }
151 })
152 .await?;
153 if row.is_some() {
154 context.emit_event(EventType::ContactsChanged(Some(self)));
155 }
156
157 if sync.into()
158 && let Some((addr, fingerprint)) = row
159 {
160 if fingerprint.is_empty() {
161 chat::sync(
162 context,
163 chat::SyncId::ContactAddr(addr),
164 chat::SyncAction::Rename(name.to_string()),
165 )
166 .await
167 .log_err(context)
168 .ok();
169 } else {
170 chat::sync(
171 context,
172 chat::SyncId::ContactFingerprint(fingerprint),
173 chat::SyncAction::Rename(name.to_string()),
174 )
175 .await
176 .log_err(context)
177 .ok();
178 }
179 }
180 Ok(())
181 }
182
183 pub(crate) async fn mark_bot(&self, context: &Context, is_bot: bool) -> Result<()> {
185 context
186 .sql
187 .execute("UPDATE contacts SET is_bot=? WHERE id=?;", (is_bot, self.0))
188 .await?;
189 Ok(())
190 }
191
192 pub(crate) async fn regossip_keys(&self, context: &Context) -> Result<()> {
194 context
195 .sql
196 .execute(
197 "UPDATE chats
198 SET gossiped_timestamp=0
199 WHERE EXISTS (SELECT 1 FROM chats_contacts
200 WHERE chats_contacts.chat_id=chats.id
201 AND chats_contacts.contact_id=?
202 AND chats_contacts.add_timestamp >= chats_contacts.remove_timestamp)",
203 (self,),
204 )
205 .await?;
206 Ok(())
207 }
208
209 pub(crate) async fn scaleup_origin(
211 context: &Context,
212 ids: &[Self],
213 origin: Origin,
214 ) -> Result<()> {
215 context
216 .sql
217 .transaction(|transaction| {
218 let mut stmt = transaction
219 .prepare("UPDATE contacts SET origin=?1 WHERE id = ?2 AND origin < ?1")?;
220 for id in ids {
221 stmt.execute((origin, id))?;
222 }
223 Ok(())
224 })
225 .await?;
226 Ok(())
227 }
228
229 pub async fn addr(&self, context: &Context) -> Result<String> {
231 let addr = context
232 .sql
233 .query_row("SELECT addr FROM contacts WHERE id=?", (self,), |row| {
234 let addr: String = row.get(0)?;
235 Ok(addr)
236 })
237 .await?;
238 Ok(addr)
239 }
240}
241
242impl fmt::Display for ContactId {
243 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
244 if *self == ContactId::UNDEFINED {
245 write!(f, "Contact#Undefined")
246 } else if *self == ContactId::SELF {
247 write!(f, "Contact#Self")
248 } else if *self == ContactId::INFO {
249 write!(f, "Contact#Info")
250 } else if *self == ContactId::DEVICE {
251 write!(f, "Contact#Device")
252 } else if self.is_special() {
253 write!(f, "Contact#Special{}", self.0)
254 } else {
255 write!(f, "Contact#{}", self.0)
256 }
257 }
258}
259
260impl rusqlite::types::ToSql for ContactId {
262 fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
263 let val = rusqlite::types::Value::Integer(i64::from(self.0));
264 let out = rusqlite::types::ToSqlOutput::Owned(val);
265 Ok(out)
266 }
267}
268
269impl rusqlite::types::FromSql for ContactId {
271 fn column_result(value: rusqlite::types::ValueRef) -> rusqlite::types::FromSqlResult<Self> {
272 i64::column_result(value).and_then(|val| {
273 val.try_into()
274 .map(ContactId::new)
275 .map_err(|_| rusqlite::types::FromSqlError::OutOfRange(val))
276 })
277 }
278}
279
280pub async fn make_vcard(context: &Context, contacts: &[ContactId]) -> Result<String> {
282 let now = time();
283 let mut vcard_contacts = Vec::with_capacity(contacts.len());
284 for id in contacts {
285 let c = Contact::get_by_id(context, *id).await?;
286 let key = c.public_key(context).await?.map(|k| k.to_base64());
287 let profile_image = match c.get_profile_image_ex(context, false).await? {
288 None => None,
289 Some(path) => tokio::fs::read(path)
290 .await
291 .log_err(context)
292 .ok()
293 .map(|data| base64::engine::general_purpose::STANDARD.encode(data)),
294 };
295 vcard_contacts.push(VcardContact {
296 addr: c.addr,
297 authname: c.authname,
298 key,
299 profile_image,
300 biography: Some(c.status).filter(|s| !s.is_empty()),
301 timestamp: Ok(now),
303 });
304 }
305
306 Ok(contact_tools::make_vcard(&vcard_contacts)
313 .trim_end()
314 .to_string())
315}
316
317pub async fn import_vcard(context: &Context, vcard: &str) -> Result<Vec<ContactId>> {
322 let contacts = contact_tools::parse_vcard(vcard);
323 let mut contact_ids = Vec::with_capacity(contacts.len());
324 for c in &contacts {
325 let Ok(id) = import_vcard_contact(context, c)
326 .await
327 .with_context(|| format!("import_vcard_contact() failed for {}", c.addr))
328 .log_err(context)
329 else {
330 continue;
331 };
332 contact_ids.push(id);
333 }
334 Ok(contact_ids)
335}
336
337async fn import_vcard_contact(context: &Context, contact: &VcardContact) -> Result<ContactId> {
338 let addr = ContactAddress::new(&contact.addr).context("Invalid address")?;
339 let origin = Origin::CreateChat;
343 let key = contact.key.as_ref().and_then(|k| {
344 SignedPublicKey::from_base64(k)
345 .with_context(|| {
346 format!(
347 "import_vcard_contact: Cannot decode key for {}",
348 contact.addr
349 )
350 })
351 .log_err(context)
352 .ok()
353 });
354
355 let fingerprint;
356 if let Some(public_key) = key {
357 fingerprint = public_key.dc_fingerprint().hex();
358
359 context
360 .sql
361 .execute(
362 "INSERT INTO public_keys (fingerprint, public_key)
363 VALUES (?, ?)
364 ON CONFLICT (fingerprint)
365 DO NOTHING",
366 (&fingerprint, public_key.to_bytes()),
367 )
368 .await?;
369 } else {
370 fingerprint = String::new();
371 }
372
373 let (id, modified) =
374 match Contact::add_or_lookup_ex(context, &contact.authname, &addr, &fingerprint, origin)
375 .await
376 {
377 Err(e) => return Err(e).context("Contact::add_or_lookup() failed"),
378 Ok((ContactId::SELF, _)) => return Ok(ContactId::SELF),
379 Ok(val) => val,
380 };
381 if modified != Modifier::None {
382 context.emit_event(EventType::ContactsChanged(Some(id)));
383 }
384 if modified != Modifier::Created {
385 return Ok(id);
386 }
387 let path = match &contact.profile_image {
388 Some(image) => match BlobObject::store_from_base64(context, image)? {
389 None => {
390 warn!(
391 context,
392 "import_vcard_contact: Could not decode avatar for {}.", contact.addr
393 );
394 None
395 }
396 Some(path) => Some(path),
397 },
398 None => None,
399 };
400 if let Some(path) = path
401 && let Err(e) = set_profile_image(context, id, &AvatarAction::Change(path)).await
402 {
403 warn!(
404 context,
405 "import_vcard_contact: Could not set avatar for {}: {e:#}.", contact.addr
406 );
407 }
408 if let Some(biography) = &contact.biography
409 && let Err(e) = set_status(context, id, biography.to_owned()).await
410 {
411 warn!(
412 context,
413 "import_vcard_contact: Could not set biography for {}: {e:#}.", contact.addr
414 );
415 }
416 Ok(id)
417}
418
419#[derive(Debug)]
432pub struct Contact {
433 pub id: ContactId,
435
436 name: String,
440
441 authname: String,
445
446 addr: String,
448
449 fingerprint: Option<String>,
453
454 pub blocked: bool,
456
457 last_seen: i64,
459
460 pub origin: Origin,
462
463 pub param: Params,
465
466 status: String,
468
469 is_bot: bool,
471}
472
473#[derive(
475 Debug,
476 Default,
477 Clone,
478 Copy,
479 PartialEq,
480 Eq,
481 PartialOrd,
482 Ord,
483 FromPrimitive,
484 ToPrimitive,
485 FromSql,
486 ToSql,
487)]
488#[repr(u32)]
489pub enum Origin {
490 #[default]
493 Unknown = 0,
494
495 MailinglistAddress = 0x2,
497
498 Hidden = 0x8,
500
501 IncomingUnknownFrom = 0x10,
503
504 IncomingUnknownCc = 0x20,
506
507 IncomingUnknownTo = 0x40,
509
510 UnhandledQrScan = 0x80,
512
513 UnhandledSecurejoinQrScan = 0x81,
515
516 IncomingReplyTo = 0x100,
519
520 IncomingCc = 0x200,
522
523 IncomingTo = 0x400,
525
526 CreateChat = 0x800,
528
529 OutgoingBcc = 0x1000,
531
532 OutgoingCc = 0x2000,
534
535 OutgoingTo = 0x4000,
537
538 Internal = 0x40000,
540
541 AddressBook = 0x80000,
543
544 SecurejoinInvited = 0x0100_0000,
546
547 SecurejoinJoined = 0x0200_0000,
553
554 ManuallyCreated = 0x0400_0000,
556}
557
558impl Origin {
559 pub fn is_known(self) -> bool {
563 self >= Origin::IncomingReplyTo
564 }
565}
566
567#[derive(Debug, PartialEq, Eq, Clone, Copy)]
568pub(crate) enum Modifier {
569 None,
570 Modified,
571 Created,
572}
573
574impl Contact {
575 pub async fn get_by_id(context: &Context, contact_id: ContactId) -> Result<Self> {
586 let contact = Self::get_by_id_optional(context, contact_id)
587 .await?
588 .with_context(|| format!("contact {contact_id} not found"))?;
589 Ok(contact)
590 }
591
592 pub async fn get_by_id_optional(
596 context: &Context,
597 contact_id: ContactId,
598 ) -> Result<Option<Self>> {
599 if let Some(mut contact) = context
600 .sql
601 .query_row_optional(
602 "SELECT c.name, c.addr, c.origin, c.blocked, c.last_seen,
603 c.authname, c.param, c.status, c.is_bot, c.fingerprint
604 FROM contacts c
605 WHERE c.id=?;",
606 (contact_id,),
607 |row| {
608 let name: String = row.get(0)?;
609 let addr: String = row.get(1)?;
610 let origin: Origin = row.get(2)?;
611 let blocked: Option<bool> = row.get(3)?;
612 let last_seen: i64 = row.get(4)?;
613 let authname: String = row.get(5)?;
614 let param: String = row.get(6)?;
615 let status: Option<String> = row.get(7)?;
616 let is_bot: bool = row.get(8)?;
617 let fingerprint: Option<String> =
618 Some(row.get(9)?).filter(|s: &String| !s.is_empty());
619 let contact = Self {
620 id: contact_id,
621 name,
622 authname,
623 addr,
624 fingerprint,
625 blocked: blocked.unwrap_or_default(),
626 last_seen,
627 origin,
628 param: param.parse().unwrap_or_default(),
629 status: status.unwrap_or_default(),
630 is_bot,
631 };
632 Ok(contact)
633 },
634 )
635 .await?
636 {
637 if contact_id == ContactId::SELF {
638 contact.name = stock_str::self_msg(context).await;
639 contact.authname = context
640 .get_config(Config::Displayname)
641 .await?
642 .unwrap_or_default();
643 contact.addr = context
644 .get_config(Config::ConfiguredAddr)
645 .await?
646 .unwrap_or_default();
647 if let Some(self_fp) = self_fingerprint_opt(context).await? {
648 contact.fingerprint = Some(self_fp.to_string());
649 }
650 contact.status = context
651 .get_config(Config::Selfstatus)
652 .await?
653 .unwrap_or_default();
654 } else if contact_id == ContactId::DEVICE {
655 contact.name = stock_str::device_messages(context).await;
656 contact.addr = ContactId::DEVICE_ADDR.to_string();
657 contact.status = stock_str::device_messages_hint(context).await;
658 }
659 Ok(Some(contact))
660 } else {
661 Ok(None)
662 }
663 }
664
665 pub fn is_blocked(&self) -> bool {
667 self.blocked
668 }
669
670 pub fn last_seen(&self) -> i64 {
672 self.last_seen
673 }
674
675 pub fn was_seen_recently(&self) -> bool {
677 time() - self.last_seen <= SEEN_RECENTLY_SECONDS
678 }
679
680 pub async fn is_blocked_load(context: &Context, id: ContactId) -> Result<bool> {
682 let blocked = context
683 .sql
684 .query_row("SELECT blocked FROM contacts WHERE id=?", (id,), |row| {
685 let blocked: bool = row.get(0)?;
686 Ok(blocked)
687 })
688 .await?;
689 Ok(blocked)
690 }
691
692 pub async fn block(context: &Context, id: ContactId) -> Result<()> {
694 set_blocked(context, Sync, id, true).await
695 }
696
697 pub async fn unblock(context: &Context, id: ContactId) -> Result<()> {
699 set_blocked(context, Sync, id, false).await
700 }
701
702 pub async fn create(context: &Context, name: &str, addr: &str) -> Result<ContactId> {
712 Self::create_ex(context, Sync, name, addr).await
713 }
714
715 pub(crate) async fn create_ex(
716 context: &Context,
717 sync: sync::Sync,
718 name: &str,
719 addr: &str,
720 ) -> Result<ContactId> {
721 let (name, addr) = sanitize_name_and_addr(name, addr);
722 let addr = ContactAddress::new(&addr)?;
723
724 let (contact_id, sth_modified) =
725 Contact::add_or_lookup(context, &name, &addr, Origin::ManuallyCreated)
726 .await
727 .context("add_or_lookup")?;
728 let blocked = Contact::is_blocked_load(context, contact_id).await?;
729 match sth_modified {
730 Modifier::None => {}
731 Modifier::Modified | Modifier::Created => {
732 context.emit_event(EventType::ContactsChanged(Some(contact_id)))
733 }
734 }
735 if blocked {
736 set_blocked(context, Nosync, contact_id, false).await?;
737 }
738
739 if sync.into() && sth_modified != Modifier::None {
740 chat::sync(
741 context,
742 chat::SyncId::ContactAddr(addr.to_string()),
743 chat::SyncAction::Rename(name.to_string()),
744 )
745 .await
746 .log_err(context)
747 .ok();
748 }
749 Ok(contact_id)
750 }
751
752 pub async fn mark_noticed(context: &Context, id: ContactId) -> Result<()> {
754 context
755 .sql
756 .execute(
757 "UPDATE msgs SET state=? WHERE from_id=? AND state=?;",
758 (MessageState::InNoticed, id, MessageState::InFresh),
759 )
760 .await?;
761 Ok(())
762 }
763
764 pub fn is_bot(&self) -> bool {
766 self.is_bot
767 }
768
769 pub async fn lookup_id_by_addr(
791 context: &Context,
792 addr: &str,
793 min_origin: Origin,
794 ) -> Result<Option<ContactId>> {
795 Self::lookup_id_by_addr_ex(context, addr, min_origin, Some(Blocked::Not)).await
796 }
797
798 pub(crate) async fn lookup_id_by_addr_ex(
801 context: &Context,
802 addr: &str,
803 min_origin: Origin,
804 blocked: Option<Blocked>,
805 ) -> Result<Option<ContactId>> {
806 if addr.is_empty() {
807 bail!("lookup_id_by_addr: empty address");
808 }
809
810 let addr_normalized = addr_normalize(addr);
811
812 if context.is_configured().await? && context.is_self_addr(addr).await? {
813 return Ok(Some(ContactId::SELF));
814 }
815
816 let id = context
817 .sql
818 .query_get_value(
819 "SELECT id FROM contacts
820 WHERE addr=?1 COLLATE NOCASE
821 AND id>?2 AND origin>=?3 AND (? OR blocked=?)
822 ORDER BY
823 (
824 SELECT COUNT(*) FROM chats c
825 INNER JOIN chats_contacts cc
826 ON c.id=cc.chat_id
827 WHERE c.type=?
828 AND c.id>?
829 AND c.blocked=?
830 AND cc.contact_id=contacts.id
831 ) DESC,
832 last_seen DESC, fingerprint DESC
833 LIMIT 1",
834 (
835 &addr_normalized,
836 ContactId::LAST_SPECIAL,
837 min_origin as u32,
838 blocked.is_none(),
839 blocked.unwrap_or(Blocked::Not),
840 Chattype::Single,
841 constants::DC_CHAT_ID_LAST_SPECIAL,
842 blocked.unwrap_or(Blocked::Not),
843 ),
844 )
845 .await?;
846 Ok(id)
847 }
848
849 pub(crate) async fn add_or_lookup(
850 context: &Context,
851 name: &str,
852 addr: &ContactAddress,
853 origin: Origin,
854 ) -> Result<(ContactId, Modifier)> {
855 Self::add_or_lookup_ex(context, name, addr, "", origin).await
856 }
857
858 pub(crate) async fn add_or_lookup_ex(
886 context: &Context,
887 name: &str,
888 addr: &str,
889 fingerprint: &str,
890 mut origin: Origin,
891 ) -> Result<(ContactId, Modifier)> {
892 let mut sth_modified = Modifier::None;
893
894 ensure!(
895 !addr.is_empty() || !fingerprint.is_empty(),
896 "Can not add_or_lookup empty address"
897 );
898 ensure!(origin != Origin::Unknown, "Missing valid origin");
899
900 if context.is_configured().await? && context.is_self_addr(addr).await? {
901 return Ok((ContactId::SELF, sth_modified));
902 }
903
904 if !fingerprint.is_empty() && context.is_configured().await? {
905 let fingerprint_self = self_fingerprint(context)
906 .await
907 .context("self_fingerprint")?;
908 if fingerprint == fingerprint_self {
909 return Ok((ContactId::SELF, sth_modified));
910 }
911 }
912
913 let mut name = sanitize_name(name);
914 if origin <= Origin::OutgoingTo {
915 if addr.contains("noreply")
917 || addr.contains("no-reply")
918 || addr.starts_with("notifications@")
919 || (addr.len() > 50 && addr.contains('+'))
921 {
922 info!(context, "hiding contact {}", addr);
923 origin = Origin::Hidden;
924 name = "".to_string();
928 }
929 }
930
931 let manual = matches!(
936 origin,
937 Origin::ManuallyCreated | Origin::AddressBook | Origin::UnhandledQrScan
938 );
939
940 let mut update_addr = false;
941
942 let row_id = context
943 .sql
944 .transaction(|transaction| {
945 let row = transaction
946 .query_row(
947 "SELECT id, name, addr, origin, authname
948 FROM contacts
949 WHERE fingerprint=?1 AND
950 (?1<>'' OR addr=?2 COLLATE NOCASE)",
951 (fingerprint, addr),
952 |row| {
953 let row_id: u32 = row.get(0)?;
954 let row_name: String = row.get(1)?;
955 let row_addr: String = row.get(2)?;
956 let row_origin: Origin = row.get(3)?;
957 let row_authname: String = row.get(4)?;
958
959 Ok((row_id, row_name, row_addr, row_origin, row_authname))
960 },
961 )
962 .optional()?;
963
964 let row_id;
965 if let Some((id, row_name, row_addr, row_origin, row_authname)) = row {
966 let update_name = manual && name != row_name;
967 let update_authname = !manual
968 && name != row_authname
969 && !name.is_empty()
970 && (origin >= row_origin
971 || origin == Origin::IncomingUnknownFrom
972 || row_authname.is_empty());
973
974 row_id = id;
975 if origin >= row_origin && addr != row_addr {
976 update_addr = true;
977 }
978 if update_name || update_authname || update_addr || origin > row_origin {
979 let new_name = if update_name {
980 name.to_string()
981 } else {
982 row_name
983 };
984 let new_authname = if update_authname {
985 name.to_string()
986 } else {
987 row_authname
988 };
989
990 transaction.execute(
991 "UPDATE contacts SET name=?, name_normalized=?, addr=?, origin=?, authname=? WHERE id=?",
992 (
993 &new_name,
994 normalize_text(
995 if !new_name.is_empty() {
996 &new_name
997 } else {
998 &new_authname
999 }),
1000 if update_addr {
1001 addr.to_string()
1002 } else {
1003 row_addr
1004 },
1005 if origin > row_origin {
1006 origin
1007 } else {
1008 row_origin
1009 },
1010 &new_authname,
1011 row_id,
1012 ),
1013 )?;
1014
1015 if update_name || update_authname {
1016 let contact_id = ContactId::new(row_id);
1017 update_chat_names(context, transaction, contact_id)?;
1018 }
1019 sth_modified = Modifier::Modified;
1020 }
1021 } else {
1022 transaction.execute(
1023 "
1024INSERT INTO contacts (name, name_normalized, addr, fingerprint, origin, authname)
1025VALUES (?, ?, ?, ?, ?, ?)
1026 ",
1027 (
1028 if manual { &name } else { "" },
1029 normalize_text(&name),
1030 &addr,
1031 fingerprint,
1032 origin,
1033 if manual { "" } else { &name },
1034 ),
1035 )?;
1036
1037 sth_modified = Modifier::Created;
1038 row_id = u32::try_from(transaction.last_insert_rowid())?;
1039 if fingerprint.is_empty() {
1040 info!(context, "Added contact id={row_id} addr={addr}.");
1041 } else {
1042 info!(
1043 context,
1044 "Added contact id={row_id} fpr={fingerprint} addr={addr}."
1045 );
1046 }
1047 }
1048 Ok(row_id)
1049 })
1050 .await?;
1051
1052 let contact_id = ContactId::new(row_id);
1053
1054 Ok((contact_id, sth_modified))
1055 }
1056
1057 pub async fn add_address_book(context: &Context, addr_book: &str) -> Result<usize> {
1075 let mut modify_cnt = 0;
1076
1077 for (name, addr) in split_address_book(addr_book) {
1078 let (name, addr) = sanitize_name_and_addr(name, addr);
1079 match ContactAddress::new(&addr) {
1080 Ok(addr) => {
1081 match Contact::add_or_lookup(context, &name, &addr, Origin::AddressBook).await {
1082 Ok((_, modified)) => {
1083 if modified != Modifier::None {
1084 modify_cnt += 1
1085 }
1086 }
1087 Err(err) => {
1088 warn!(
1089 context,
1090 "Failed to add address {} from address book: {}", addr, err
1091 );
1092 }
1093 }
1094 }
1095 Err(err) => {
1096 warn!(context, "{:#}.", err);
1097 }
1098 }
1099 }
1100 if modify_cnt > 0 {
1101 context.emit_event(EventType::ContactsChanged(None));
1102 }
1103
1104 Ok(modify_cnt)
1105 }
1106
1107 pub async fn get_all(
1117 context: &Context,
1118 listflags: u32,
1119 query: Option<&str>,
1120 ) -> Result<Vec<ContactId>> {
1121 let self_addrs = context
1122 .get_all_self_addrs()
1123 .await?
1124 .into_iter()
1125 .collect::<HashSet<_>>();
1126 let mut add_self = false;
1127 let mut ret = Vec::new();
1128 let flag_add_self = (listflags & constants::DC_GCL_ADD_SELF) != 0;
1129 let flag_address = (listflags & constants::DC_GCL_ADDRESS) != 0;
1130 let minimal_origin = if context.get_config_bool(Config::Bot).await? {
1131 Origin::Unknown
1132 } else {
1133 Origin::IncomingReplyTo
1134 };
1135 if query.is_some() {
1136 let s3str_like_cmd = format!("%{}%", query.unwrap_or("").to_lowercase());
1137 context
1138 .sql
1139 .query_map(
1140 "
1141SELECT c.id, c.addr FROM contacts c
1142WHERE c.id>?
1143 AND (c.fingerprint='')=?
1144 AND c.origin>=?
1145 AND c.blocked=0
1146 AND (IFNULL(c.name_normalized,IIF(c.name='',c.authname,c.name)) LIKE ? OR c.addr LIKE ?)
1147ORDER BY c.origin>=? DESC, c.last_seen DESC, c.id DESC
1148 ",
1149 (
1150 ContactId::LAST_SPECIAL,
1151 flag_address,
1152 minimal_origin,
1153 &s3str_like_cmd,
1154 &s3str_like_cmd,
1155 Origin::CreateChat,
1156 ),
1157 |row| {
1158 let id: ContactId = row.get(0)?;
1159 let addr: String = row.get(1)?;
1160 Ok((id, addr))
1161 },
1162 |rows| {
1163 for row in rows {
1164 let (id, addr) = row?;
1165 if !self_addrs.contains(&addr) {
1166 ret.push(id);
1167 }
1168 }
1169 Ok(())
1170 },
1171 )
1172 .await?;
1173
1174 if let Some(query) = query {
1175 let self_addr = context
1176 .get_config(Config::ConfiguredAddr)
1177 .await?
1178 .unwrap_or_default();
1179 let self_name = context
1180 .get_config(Config::Displayname)
1181 .await?
1182 .unwrap_or_default();
1183 let self_name2 = stock_str::self_msg(context);
1184
1185 if self_addr.contains(query)
1186 || self_name.contains(query)
1187 || self_name2.await.contains(query)
1188 {
1189 add_self = true;
1190 }
1191 } else {
1192 add_self = true;
1193 }
1194 } else {
1195 add_self = true;
1196
1197 context
1198 .sql
1199 .query_map(
1200 "SELECT id, addr FROM contacts
1201 WHERE id>?
1202 AND (fingerprint='')=?
1203 AND origin>=?
1204 AND blocked=0
1205 ORDER BY origin>=? DESC, last_seen DESC, id DESC",
1206 (
1207 ContactId::LAST_SPECIAL,
1208 flag_address,
1209 minimal_origin,
1210 Origin::CreateChat,
1211 ),
1212 |row| {
1213 let id: ContactId = row.get(0)?;
1214 let addr: String = row.get(1)?;
1215 Ok((id, addr))
1216 },
1217 |rows| {
1218 for row in rows {
1219 let (id, addr) = row?;
1220 if !self_addrs.contains(&addr) {
1221 ret.push(id);
1222 }
1223 }
1224 Ok(())
1225 },
1226 )
1227 .await?;
1228 }
1229
1230 if flag_add_self && add_self {
1231 ret.push(ContactId::SELF);
1232 }
1233
1234 Ok(ret)
1235 }
1236
1237 async fn update_blocked_mailinglist_contacts(context: &Context) -> Result<()> {
1243 context
1244 .sql
1245 .transaction(move |transaction| {
1246 let mut stmt = transaction.prepare(
1247 "SELECT name, grpid, type FROM chats WHERE (type=? OR type=?) AND blocked=?",
1248 )?;
1249 let rows = stmt.query_map(
1250 (Chattype::Mailinglist, Chattype::InBroadcast, Blocked::Yes),
1251 |row| {
1252 let name: String = row.get(0)?;
1253 let grpid: String = row.get(1)?;
1254 let typ: Chattype = row.get(2)?;
1255 Ok((name, grpid, typ))
1256 },
1257 )?;
1258 let blocked_mailinglists = rows.collect::<std::result::Result<Vec<_>, _>>()?;
1259 for (name, grpid, typ) in blocked_mailinglists {
1260 let count = transaction.query_row(
1261 "SELECT COUNT(id) FROM contacts WHERE addr=?",
1262 [&grpid],
1263 |row| {
1264 let count: isize = row.get(0)?;
1265 Ok(count)
1266 },
1267 )?;
1268 if count == 0 {
1269 transaction.execute("INSERT INTO contacts (addr) VALUES (?)", [&grpid])?;
1270 }
1271
1272 let fingerprint = if typ == Chattype::InBroadcast {
1273 "Blocked_broadcast"
1276 } else {
1277 ""
1278 };
1279 transaction.execute(
1281 "
1282UPDATE contacts
1283SET name=?, name_normalized=IIF(?1='',name_normalized,?), origin=?, blocked=1, fingerprint=?
1284WHERE addr=?
1285 ",
1286 (
1287 &name,
1288 normalize_text(&name),
1289 Origin::MailinglistAddress,
1290 fingerprint,
1291 &grpid,
1292 ),
1293 )?;
1294 }
1295 Ok(())
1296 })
1297 .await?;
1298 Ok(())
1299 }
1300
1301 pub async fn get_blocked_cnt(context: &Context) -> Result<usize> {
1303 let count = context
1304 .sql
1305 .count(
1306 "SELECT COUNT(*) FROM contacts WHERE id>? AND blocked!=0",
1307 (ContactId::LAST_SPECIAL,),
1308 )
1309 .await?;
1310 Ok(count)
1311 }
1312
1313 pub async fn get_all_blocked(context: &Context) -> Result<Vec<ContactId>> {
1315 Contact::update_blocked_mailinglist_contacts(context)
1316 .await
1317 .context("cannot update blocked mailinglist contacts")?;
1318
1319 let list = context
1320 .sql
1321 .query_map_vec(
1322 "SELECT id FROM contacts WHERE id>? AND blocked!=0 ORDER BY last_seen DESC, id DESC;",
1323 (ContactId::LAST_SPECIAL,),
1324 |row| {
1325 let contact_id: ContactId = row.get(0)?;
1326 Ok(contact_id)
1327 }
1328 )
1329 .await?;
1330 Ok(list)
1331 }
1332
1333 pub async fn get_encrinfo(context: &Context, contact_id: ContactId) -> Result<String> {
1339 ensure!(
1340 !contact_id.is_special(),
1341 "Can not provide encryption info for special contact"
1342 );
1343
1344 let contact = Contact::get_by_id(context, contact_id).await?;
1345 let addr = context
1346 .get_config(Config::ConfiguredAddr)
1347 .await?
1348 .unwrap_or_default();
1349
1350 let Some(fingerprint_other) = contact.fingerprint() else {
1351 return Ok(stock_str::encr_none(context).await);
1352 };
1353 let fingerprint_other = fingerprint_other.to_string();
1354
1355 let stock_message = if contact.public_key(context).await?.is_some() {
1356 stock_str::e2e_available(context).await
1357 } else {
1358 stock_str::encr_none(context).await
1359 };
1360
1361 let finger_prints = stock_str::finger_prints(context).await;
1362 let mut ret = format!("{stock_message}.\n{finger_prints}:");
1363
1364 let fingerprint_self = load_self_public_key(context)
1365 .await?
1366 .dc_fingerprint()
1367 .to_string();
1368 if addr < contact.addr {
1369 cat_fingerprint(
1370 &mut ret,
1371 &stock_str::self_msg(context).await,
1372 &addr,
1373 &fingerprint_self,
1374 );
1375 cat_fingerprint(
1376 &mut ret,
1377 contact.get_display_name(),
1378 &contact.addr,
1379 &fingerprint_other,
1380 );
1381 } else {
1382 cat_fingerprint(
1383 &mut ret,
1384 contact.get_display_name(),
1385 &contact.addr,
1386 &fingerprint_other,
1387 );
1388 cat_fingerprint(
1389 &mut ret,
1390 &stock_str::self_msg(context).await,
1391 &addr,
1392 &fingerprint_self,
1393 );
1394 }
1395
1396 Ok(ret)
1397 }
1398
1399 pub async fn delete(context: &Context, contact_id: ContactId) -> Result<()> {
1405 ensure!(!contact_id.is_special(), "Can not delete special contact");
1406
1407 context
1408 .sql
1409 .transaction(move |transaction| {
1410 let deleted_contacts = transaction.execute(
1413 "DELETE FROM contacts WHERE id=?
1414 AND (SELECT COUNT(*) FROM chats_contacts WHERE contact_id=?)=0;",
1415 (contact_id, contact_id),
1416 )?;
1417 if deleted_contacts == 0 {
1418 transaction.execute(
1419 "UPDATE contacts SET origin=? WHERE id=?;",
1420 (Origin::Hidden, contact_id),
1421 )?;
1422 }
1423 Ok(())
1424 })
1425 .await?;
1426
1427 context.emit_event(EventType::ContactsChanged(None));
1428 Ok(())
1429 }
1430
1431 pub async fn update_param(&self, context: &Context) -> Result<()> {
1433 context
1434 .sql
1435 .execute(
1436 "UPDATE contacts SET param=? WHERE id=?",
1437 (self.param.to_string(), self.id),
1438 )
1439 .await?;
1440 Ok(())
1441 }
1442
1443 pub async fn update_status(&self, context: &Context) -> Result<()> {
1445 context
1446 .sql
1447 .execute(
1448 "UPDATE contacts SET status=? WHERE id=?",
1449 (&self.status, self.id),
1450 )
1451 .await?;
1452 Ok(())
1453 }
1454
1455 pub fn get_id(&self) -> ContactId {
1457 self.id
1458 }
1459
1460 pub fn get_addr(&self) -> &str {
1462 &self.addr
1463 }
1464
1465 pub fn is_key_contact(&self) -> bool {
1468 self.fingerprint.is_some()
1469 }
1470
1471 pub fn fingerprint(&self) -> Option<Fingerprint> {
1475 if let Some(fingerprint) = &self.fingerprint {
1476 fingerprint.parse().ok()
1477 } else {
1478 None
1479 }
1480 }
1481
1482 pub async fn public_key(&self, context: &Context) -> Result<Option<SignedPublicKey>> {
1489 if self.id == ContactId::SELF {
1490 return Ok(Some(load_self_public_key(context).await?));
1491 }
1492
1493 if let Some(fingerprint) = &self.fingerprint {
1494 if let Some(public_key_bytes) = context
1495 .sql
1496 .query_row_optional(
1497 "SELECT public_key
1498 FROM public_keys
1499 WHERE fingerprint=?",
1500 (fingerprint,),
1501 |row| {
1502 let bytes: Vec<u8> = row.get(0)?;
1503 Ok(bytes)
1504 },
1505 )
1506 .await?
1507 {
1508 let public_key = SignedPublicKey::from_slice(&public_key_bytes)?;
1509 Ok(Some(public_key))
1510 } else {
1511 Ok(None)
1512 }
1513 } else {
1514 Ok(None)
1515 }
1516 }
1517
1518 pub fn get_authname(&self) -> &str {
1520 &self.authname
1521 }
1522
1523 pub fn get_name(&self) -> &str {
1529 &self.name
1530 }
1531
1532 pub fn get_display_name(&self) -> &str {
1538 if !self.name.is_empty() {
1539 return &self.name;
1540 }
1541 if !self.authname.is_empty() {
1542 return &self.authname;
1543 }
1544 &self.addr
1545 }
1546
1547 pub fn get_name_n_addr(&self) -> String {
1558 if !self.name.is_empty() {
1559 format!("{} ({})", self.name, self.addr)
1560 } else if !self.authname.is_empty() {
1561 format!("{} ({})", self.authname, self.addr)
1562 } else {
1563 (&self.addr).into()
1564 }
1565 }
1566
1567 pub async fn get_profile_image(&self, context: &Context) -> Result<Option<PathBuf>> {
1571 self.get_profile_image_ex(context, true).await
1572 }
1573
1574 async fn get_profile_image_ex(
1578 &self,
1579 context: &Context,
1580 show_fallback_icon: bool,
1581 ) -> Result<Option<PathBuf>> {
1582 if self.id == ContactId::SELF {
1583 if let Some(p) = context.get_config(Config::Selfavatar).await? {
1584 return Ok(Some(PathBuf::from(p))); }
1586 } else if self.id == ContactId::DEVICE {
1587 return Ok(Some(chat::get_device_icon(context).await?));
1588 }
1589 if show_fallback_icon && !self.id.is_special() && !self.is_key_contact() {
1590 return Ok(Some(chat::get_unencrypted_icon(context).await?));
1591 }
1592 if let Some(image_rel) = self.param.get(Param::ProfileImage)
1593 && !image_rel.is_empty()
1594 {
1595 return Ok(Some(get_abs_path(context, Path::new(image_rel))));
1596 }
1597 Ok(None)
1598 }
1599
1600 pub fn get_color(&self) -> u32 {
1604 get_color(self.id == ContactId::SELF, &self.addr, &self.fingerprint())
1605 }
1606
1607 pub async fn get_or_gen_color(&self, context: &Context) -> Result<u32> {
1612 let mut fpr = self.fingerprint();
1613 if fpr.is_none() && self.id == ContactId::SELF {
1614 fpr = Some(load_self_public_key(context).await?.dc_fingerprint());
1615 }
1616 Ok(get_color(self.id == ContactId::SELF, &self.addr, &fpr))
1617 }
1618
1619 pub fn get_status(&self) -> &str {
1623 self.status.as_str()
1624 }
1625
1626 pub async fn e2ee_avail(&self, context: &Context) -> Result<bool> {
1628 if self.id == ContactId::SELF {
1629 return Ok(true);
1631 }
1632 Ok(self.public_key(context).await?.is_some())
1633 }
1634
1635 pub async fn is_verified(&self, context: &Context) -> Result<bool> {
1648 if self.id == ContactId::SELF {
1651 return Ok(true);
1652 }
1653
1654 Ok(self.get_verifier_id(context).await?.is_some())
1655 }
1656
1657 pub async fn get_verifier_id(&self, context: &Context) -> Result<Option<Option<ContactId>>> {
1667 let verifier_id: u32 = context
1668 .sql
1669 .query_get_value("SELECT verifier FROM contacts WHERE id=?", (self.id,))
1670 .await?
1671 .with_context(|| format!("Contact {} does not exist", self.id))?;
1672
1673 if verifier_id == 0 {
1674 Ok(None)
1675 } else if verifier_id == self.id.to_u32() {
1676 Ok(Some(None))
1677 } else {
1678 Ok(Some(Some(ContactId::new(verifier_id))))
1679 }
1680 }
1681
1682 pub async fn get_real_cnt(context: &Context) -> Result<usize> {
1684 if !context.sql.is_open().await {
1685 return Ok(0);
1686 }
1687
1688 let count = context
1689 .sql
1690 .count(
1691 "SELECT COUNT(*) FROM contacts WHERE id>?;",
1692 (ContactId::LAST_SPECIAL,),
1693 )
1694 .await?;
1695 Ok(count)
1696 }
1697
1698 pub async fn real_exists_by_id(context: &Context, contact_id: ContactId) -> Result<bool> {
1700 if contact_id.is_special() {
1701 return Ok(false);
1702 }
1703
1704 let exists = context
1705 .sql
1706 .exists("SELECT COUNT(*) FROM contacts WHERE id=?;", (contact_id,))
1707 .await?;
1708 Ok(exists)
1709 }
1710}
1711
1712pub fn get_color(is_self: bool, addr: &str, fingerprint: &Option<Fingerprint>) -> u32 {
1718 if let Some(fingerprint) = fingerprint {
1719 str_to_color(&fingerprint.hex())
1720 } else if is_self {
1721 0x808080
1722 } else {
1723 str_to_color(&to_lowercase(addr))
1724 }
1725}
1726
1727fn update_chat_names(
1731 context: &Context,
1732 transaction: &rusqlite::Connection,
1733 contact_id: ContactId,
1734) -> Result<()> {
1735 let chat_id: Option<ChatId> = transaction.query_row(
1736 "SELECT id FROM chats WHERE type=? AND id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?)",
1737 (Chattype::Single, contact_id),
1738 |row| {
1739 let chat_id: ChatId = row.get(0)?;
1740 Ok(chat_id)
1741 }
1742 ).optional()?;
1743
1744 if let Some(chat_id) = chat_id {
1745 let (addr, name, authname) = transaction.query_row(
1746 "SELECT addr, name, authname
1747 FROM contacts
1748 WHERE id=?",
1749 (contact_id,),
1750 |row| {
1751 let addr: String = row.get(0)?;
1752 let name: String = row.get(1)?;
1753 let authname: String = row.get(2)?;
1754 Ok((addr, name, authname))
1755 },
1756 )?;
1757
1758 let chat_name = if !name.is_empty() {
1759 name
1760 } else if !authname.is_empty() {
1761 authname
1762 } else {
1763 addr
1764 };
1765
1766 let count = transaction.execute(
1767 "UPDATE chats SET name=?1, name_normalized=?2 WHERE id=?3 AND name!=?1",
1768 (&chat_name, normalize_text(&chat_name), chat_id),
1769 )?;
1770
1771 if count > 0 {
1772 context.emit_event(EventType::ChatModified(chat_id));
1774 chatlist_events::emit_chatlist_items_changed_for_contact(context, contact_id);
1775 }
1776 }
1777
1778 Ok(())
1779}
1780
1781pub(crate) async fn set_blocked(
1782 context: &Context,
1783 sync: sync::Sync,
1784 contact_id: ContactId,
1785 new_blocking: bool,
1786) -> Result<()> {
1787 ensure!(
1788 !contact_id.is_special(),
1789 "Can't block special contact {contact_id}"
1790 );
1791 let contact = Contact::get_by_id(context, contact_id).await?;
1792
1793 if contact.blocked != new_blocking {
1794 context
1795 .sql
1796 .execute(
1797 "UPDATE contacts SET blocked=? WHERE id=?;",
1798 (i32::from(new_blocking), contact_id),
1799 )
1800 .await?;
1801
1802 if context
1808 .sql
1809 .execute(
1810 r#"
1811UPDATE chats
1812SET blocked=?
1813WHERE type=? AND id IN (
1814 SELECT chat_id FROM chats_contacts WHERE contact_id=?
1815);
1816"#,
1817 (new_blocking, Chattype::Single, contact_id),
1818 )
1819 .await
1820 .is_ok()
1821 {
1822 Contact::mark_noticed(context, contact_id).await?;
1823 context.emit_event(EventType::ContactsChanged(Some(contact_id)));
1824 }
1825
1826 if !new_blocking
1829 && contact.origin == Origin::MailinglistAddress
1830 && let Some((chat_id, ..)) = chat::get_chat_id_by_grpid(context, &contact.addr).await?
1831 {
1832 chat_id.unblock_ex(context, Nosync).await?;
1833 }
1834
1835 if sync.into() {
1836 let action = match new_blocking {
1837 true => chat::SyncAction::Block,
1838 false => chat::SyncAction::Unblock,
1839 };
1840 let sync_id = if let Some(fingerprint) = contact.fingerprint() {
1841 chat::SyncId::ContactFingerprint(fingerprint.hex())
1842 } else {
1843 chat::SyncId::ContactAddr(contact.addr.clone())
1844 };
1845
1846 chat::sync(context, sync_id, action)
1847 .await
1848 .log_err(context)
1849 .ok();
1850 }
1851 }
1852
1853 chatlist_events::emit_chatlist_changed(context);
1854 Ok(())
1855}
1856
1857pub(crate) async fn set_profile_image(
1864 context: &Context,
1865 contact_id: ContactId,
1866 profile_image: &AvatarAction,
1867) -> Result<()> {
1868 let mut contact = Contact::get_by_id(context, contact_id).await?;
1869 let changed = match profile_image {
1870 AvatarAction::Change(profile_image) => {
1871 if contact_id == ContactId::SELF {
1872 context
1873 .set_config_ex(Nosync, Config::Selfavatar, Some(profile_image))
1874 .await?;
1875 } else {
1876 contact.param.set(Param::ProfileImage, profile_image);
1877 }
1878 true
1879 }
1880 AvatarAction::Delete => {
1881 if contact_id == ContactId::SELF {
1882 context
1883 .set_config_ex(Nosync, Config::Selfavatar, None)
1884 .await?;
1885 } else {
1886 contact.param.remove(Param::ProfileImage);
1887 }
1888 true
1889 }
1890 };
1891 if changed {
1892 contact.update_param(context).await?;
1893 context.emit_event(EventType::ContactsChanged(Some(contact_id)));
1894 chatlist_events::emit_chatlist_item_changed_for_contact_chat(context, contact_id).await;
1895 }
1896 Ok(())
1897}
1898
1899pub(crate) async fn set_status(
1903 context: &Context,
1904 contact_id: ContactId,
1905 status: String,
1906) -> Result<()> {
1907 if contact_id == ContactId::SELF {
1908 context
1909 .set_config_ex(Nosync, Config::Selfstatus, Some(&status))
1910 .await?;
1911 } else {
1912 let mut contact = Contact::get_by_id(context, contact_id).await?;
1913
1914 if contact.status != status {
1915 contact.status = status;
1916 contact.update_status(context).await?;
1917 context.emit_event(EventType::ContactsChanged(Some(contact_id)));
1918 }
1919 }
1920 Ok(())
1921}
1922
1923pub(crate) async fn update_last_seen(
1925 context: &Context,
1926 contact_id: ContactId,
1927 timestamp: i64,
1928) -> Result<()> {
1929 ensure!(
1930 !contact_id.is_special(),
1931 "Can not update special contact last seen timestamp"
1932 );
1933
1934 if context
1935 .sql
1936 .execute(
1937 "UPDATE contacts SET last_seen = ?1 WHERE last_seen < ?1 AND id = ?2",
1938 (timestamp, contact_id),
1939 )
1940 .await?
1941 > 0
1942 && timestamp > time() - SEEN_RECENTLY_SECONDS
1943 {
1944 context.emit_event(EventType::ContactsChanged(Some(contact_id)));
1945 context
1946 .scheduler
1947 .interrupt_recently_seen(contact_id, timestamp)
1948 .await;
1949 }
1950 Ok(())
1951}
1952
1953pub(crate) async fn mark_contact_id_as_verified(
1957 context: &Context,
1958 contact_id: ContactId,
1959 verifier_id: Option<ContactId>,
1960) -> Result<()> {
1961 ensure_and_debug_assert_ne!(contact_id, ContactId::SELF,);
1962 ensure_and_debug_assert_ne!(
1963 Some(contact_id),
1964 verifier_id,
1965 "Contact cannot be verified by self",
1966 );
1967 let by_self = verifier_id == Some(ContactId::SELF);
1968 let mut verifier_id = verifier_id.unwrap_or(contact_id);
1969 context
1970 .sql
1971 .transaction(|transaction| {
1972 let contact_fingerprint: String = transaction.query_row(
1973 "SELECT fingerprint FROM contacts WHERE id=?",
1974 (contact_id,),
1975 |row| row.get(0),
1976 )?;
1977 if contact_fingerprint.is_empty() {
1978 bail!("Non-key-contact {contact_id} cannot be verified");
1979 }
1980 if verifier_id != ContactId::SELF {
1981 let (verifier_fingerprint, verifier_verifier_id): (String, ContactId) = transaction
1982 .query_row(
1983 "SELECT fingerprint, verifier FROM contacts WHERE id=?",
1984 (verifier_id,),
1985 |row| Ok((row.get(0)?, row.get(1)?)),
1986 )?;
1987 if verifier_fingerprint.is_empty() {
1988 bail!(
1989 "Contact {contact_id} cannot be verified by non-key-contact {verifier_id}"
1990 );
1991 }
1992 ensure!(
1993 verifier_id == contact_id || verifier_verifier_id != ContactId::UNDEFINED,
1994 "Contact {contact_id} cannot be verified by unverified contact {verifier_id}",
1995 );
1996 if verifier_verifier_id == verifier_id {
1997 verifier_id = contact_id;
2002 }
2003 }
2004 transaction.execute(
2005 "UPDATE contacts SET verifier=?1
2006 WHERE id=?2 AND (verifier=0 OR verifier=id OR ?3)",
2007 (verifier_id, contact_id, by_self),
2008 )?;
2009 Ok(())
2010 })
2011 .await?;
2012 Ok(())
2013}
2014
2015fn cat_fingerprint(ret: &mut String, name: &str, addr: &str, fingerprint: &str) {
2016 *ret += &format!("\n\n{name} ({addr}):\n{fingerprint}");
2017}
2018
2019fn split_address_book(book: &str) -> Vec<(&str, &str)> {
2020 book.lines()
2021 .collect::<Vec<&str>>()
2022 .chunks(2)
2023 .filter_map(|chunk| {
2024 let name = chunk.first()?;
2025 let addr = chunk.get(1)?;
2026 Some((*name, *addr))
2027 })
2028 .collect()
2029}
2030
2031#[derive(Debug)]
2032pub(crate) struct RecentlySeenInterrupt {
2033 contact_id: ContactId,
2034 timestamp: i64,
2035}
2036
2037#[derive(Debug)]
2038pub(crate) struct RecentlySeenLoop {
2039 handle: task::JoinHandle<()>,
2041
2042 interrupt_send: Sender<RecentlySeenInterrupt>,
2043}
2044
2045impl RecentlySeenLoop {
2046 pub(crate) fn new(context: Context) -> Self {
2047 let (interrupt_send, interrupt_recv) = channel::bounded(1);
2048
2049 let handle = task::spawn(Self::run(context, interrupt_recv));
2050 Self {
2051 handle,
2052 interrupt_send,
2053 }
2054 }
2055
2056 async fn run(context: Context, interrupt: Receiver<RecentlySeenInterrupt>) {
2057 type MyHeapElem = (Reverse<i64>, ContactId);
2058
2059 let now = SystemTime::now();
2060 let now_ts = now
2061 .duration_since(SystemTime::UNIX_EPOCH)
2062 .unwrap_or_default()
2063 .as_secs() as i64;
2064
2065 let mut unseen_queue: BinaryHeap<MyHeapElem> = context
2071 .sql
2072 .query_map_collect(
2073 "SELECT id, last_seen FROM contacts
2074 WHERE last_seen > ?",
2075 (now_ts - SEEN_RECENTLY_SECONDS,),
2076 |row| {
2077 let contact_id: ContactId = row.get("id")?;
2078 let last_seen: i64 = row.get("last_seen")?;
2079 Ok((Reverse(last_seen + SEEN_RECENTLY_SECONDS), contact_id))
2080 },
2081 )
2082 .await
2083 .unwrap_or_default();
2084
2085 loop {
2086 let now = SystemTime::now();
2087 let (until, contact_id) =
2088 if let Some((Reverse(timestamp), contact_id)) = unseen_queue.peek() {
2089 (
2090 UNIX_EPOCH
2091 + Duration::from_secs((*timestamp).try_into().unwrap_or(u64::MAX))
2092 + Duration::from_secs(1),
2093 Some(contact_id),
2094 )
2095 } else {
2096 (now + Duration::from_secs(86400), None)
2098 };
2099
2100 if let Ok(duration) = until.duration_since(now) {
2101 info!(
2102 context,
2103 "Recently seen loop waiting for {} or interrupt",
2104 duration_to_str(duration)
2105 );
2106
2107 match timeout(duration, interrupt.recv()).await {
2108 Err(_) => {
2109 if let Some(contact_id) = contact_id {
2111 context.emit_event(EventType::ContactsChanged(Some(*contact_id)));
2112 chatlist_events::emit_chatlist_item_changed_for_contact_chat(
2113 &context,
2114 *contact_id,
2115 )
2116 .await;
2117 unseen_queue.pop();
2118 }
2119 }
2120 Ok(Err(err)) => {
2121 warn!(
2122 context,
2123 "Error receiving an interruption in recently seen loop: {}", err
2124 );
2125 return;
2128 }
2129 Ok(Ok(RecentlySeenInterrupt {
2130 contact_id,
2131 timestamp,
2132 })) => {
2133 if contact_id != ContactId::UNDEFINED {
2135 unseen_queue
2136 .push((Reverse(timestamp + SEEN_RECENTLY_SECONDS), contact_id));
2137 }
2138 }
2139 }
2140 } else {
2141 info!(
2142 context,
2143 "Recently seen loop is not waiting, event is already due."
2144 );
2145
2146 if let Some(contact_id) = contact_id {
2148 context.emit_event(EventType::ContactsChanged(Some(*contact_id)));
2149 chatlist_events::emit_chatlist_item_changed_for_contact_chat(
2150 &context,
2151 *contact_id,
2152 )
2153 .await;
2154 }
2155 unseen_queue.pop();
2156 }
2157 }
2158 }
2159
2160 pub(crate) fn try_interrupt(&self, contact_id: ContactId, timestamp: i64) {
2161 self.interrupt_send
2162 .try_send(RecentlySeenInterrupt {
2163 contact_id,
2164 timestamp,
2165 })
2166 .ok();
2167 }
2168
2169 #[cfg(test)]
2170 pub(crate) async fn interrupt(&self, contact_id: ContactId, timestamp: i64) {
2171 self.interrupt_send
2172 .send(RecentlySeenInterrupt {
2173 contact_id,
2174 timestamp,
2175 })
2176 .await
2177 .unwrap();
2178 }
2179
2180 pub(crate) async fn abort(self) {
2181 self.handle.abort();
2182
2183 self.handle.await.ok();
2187 }
2188}
2189
2190#[cfg(test)]
2191mod contact_tests;