1use std::{collections::BTreeMap, fmt, ops::Not, sync::Arc};
16
17use ruma::{
18 DeviceKeyAlgorithm, EventId, MilliSecondsSinceUnixEpoch, OwnedDeviceId, OwnedEventId,
19 OwnedUserId,
20 events::{
21 AnySyncMessageLikeEvent, AnySyncTimelineEvent, AnyTimelineEvent, AnyToDeviceEvent,
22 MessageLikeEventType, room::encrypted::EncryptedEventScheme,
23 },
24 push::Action,
25 serde::{
26 AsRefStr, AsStrAsRefStr, DebugAsRefStr, DeserializeFromCowStr, FromString, JsonObject, Raw,
27 SerializeAsRefStr,
28 },
29};
30use serde::{Deserialize, Serialize};
31use tracing::warn;
32#[cfg(target_family = "wasm")]
33use wasm_bindgen::prelude::*;
34
35use crate::{
36 debug::{DebugRawEvent, DebugStructExt},
37 serde_helpers::{extract_bundled_thread, extract_timestamp},
38};
39
40const AUTHENTICITY_NOT_GUARANTEED: &str =
41 "The authenticity of this encrypted message can't be guaranteed on this device.";
42const UNVERIFIED_IDENTITY: &str = "Encrypted by an unverified user.";
43const VERIFICATION_VIOLATION: &str =
44 "Encrypted by a previously-verified user who is no longer verified.";
45const UNSIGNED_DEVICE: &str = "Encrypted by a device not verified by its owner.";
46const UNKNOWN_DEVICE: &str = "Encrypted by an unknown or deleted device.";
47const MISMATCHED_SENDER: &str = "\
48 The sender of the event does not match the owner of the device \
49 that created the Megolm session.";
50
51#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
54#[serde(from = "OldVerificationStateHelper")]
55pub enum VerificationState {
56 Verified,
61
62 Unverified(VerificationLevel),
67}
68
69#[derive(Clone, Debug, Deserialize)]
72enum OldVerificationStateHelper {
73 Untrusted,
74 UnknownDevice,
75 #[serde(alias = "Trusted")]
76 Verified,
77 Unverified(VerificationLevel),
78}
79
80impl From<OldVerificationStateHelper> for VerificationState {
81 fn from(value: OldVerificationStateHelper) -> Self {
82 match value {
83 OldVerificationStateHelper::Untrusted => {
86 VerificationState::Unverified(VerificationLevel::UnsignedDevice)
87 }
88 OldVerificationStateHelper::UnknownDevice => {
89 Self::Unverified(VerificationLevel::None(DeviceLinkProblem::MissingDevice))
90 }
91 OldVerificationStateHelper::Verified => Self::Verified,
92 OldVerificationStateHelper::Unverified(l) => Self::Unverified(l),
93 }
94 }
95}
96
97impl VerificationState {
98 pub fn to_shield_state_strict(&self) -> ShieldState {
105 match self {
106 VerificationState::Verified => ShieldState::None,
107 VerificationState::Unverified(level) => match level {
108 VerificationLevel::UnverifiedIdentity
109 | VerificationLevel::VerificationViolation
110 | VerificationLevel::UnsignedDevice => ShieldState::Red {
111 code: ShieldStateCode::UnverifiedIdentity,
112 message: UNVERIFIED_IDENTITY,
113 },
114 VerificationLevel::None(link) => match link {
115 DeviceLinkProblem::MissingDevice => ShieldState::Red {
116 code: ShieldStateCode::UnknownDevice,
117 message: UNKNOWN_DEVICE,
118 },
119 DeviceLinkProblem::InsecureSource => ShieldState::Red {
120 code: ShieldStateCode::AuthenticityNotGuaranteed,
121 message: AUTHENTICITY_NOT_GUARANTEED,
122 },
123 },
124 VerificationLevel::MismatchedSender => ShieldState::Red {
125 code: ShieldStateCode::MismatchedSender,
126 message: MISMATCHED_SENDER,
127 },
128 },
129 }
130 }
131
132 pub fn to_shield_state_lax(&self) -> ShieldState {
140 match self {
141 VerificationState::Verified => ShieldState::None,
142 VerificationState::Unverified(level) => match level {
143 VerificationLevel::UnverifiedIdentity => {
144 ShieldState::None
147 }
148 VerificationLevel::VerificationViolation => {
149 ShieldState::Red {
152 code: ShieldStateCode::VerificationViolation,
153 message: VERIFICATION_VIOLATION,
154 }
155 }
156 VerificationLevel::UnsignedDevice => {
157 ShieldState::Red {
159 code: ShieldStateCode::UnsignedDevice,
160 message: UNSIGNED_DEVICE,
161 }
162 }
163 VerificationLevel::None(link) => match link {
164 DeviceLinkProblem::MissingDevice => {
165 ShieldState::Red {
169 code: ShieldStateCode::UnknownDevice,
170 message: UNKNOWN_DEVICE,
171 }
172 }
173 DeviceLinkProblem::InsecureSource => {
174 ShieldState::Grey {
177 code: ShieldStateCode::AuthenticityNotGuaranteed,
178 message: AUTHENTICITY_NOT_GUARANTEED,
179 }
180 }
181 },
182 VerificationLevel::MismatchedSender => ShieldState::Red {
183 code: ShieldStateCode::MismatchedSender,
184 message: MISMATCHED_SENDER,
185 },
186 },
187 }
188 }
189}
190
191#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
194pub enum VerificationLevel {
195 UnverifiedIdentity,
197
198 #[serde(alias = "PreviouslyVerified")]
201 VerificationViolation,
202
203 UnsignedDevice,
206
207 None(DeviceLinkProblem),
213
214 MismatchedSender,
217}
218
219impl fmt::Display for VerificationLevel {
220 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
221 let display = match self {
222 VerificationLevel::UnverifiedIdentity => "The sender's identity was not verified",
223 VerificationLevel::VerificationViolation => {
224 "The sender's identity was previously verified but has changed"
225 }
226 VerificationLevel::UnsignedDevice => {
227 "The sending device was not signed by the user's identity"
228 }
229 VerificationLevel::None(..) => "The sending device is not known",
230 VerificationLevel::MismatchedSender => MISMATCHED_SENDER,
231 };
232 write!(f, "{display}")
233 }
234}
235
236#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
239pub enum DeviceLinkProblem {
240 MissingDevice,
244 InsecureSource,
247}
248
249#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
252pub enum ShieldState {
253 Red {
256 code: ShieldStateCode,
258 message: &'static str,
260 },
261 Grey {
264 code: ShieldStateCode,
266 message: &'static str,
268 },
269 None,
271}
272
273#[derive(Clone, Copy, Debug, Deserialize, Serialize, Eq, PartialEq)]
275#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
276#[cfg_attr(target_family = "wasm", wasm_bindgen)]
277pub enum ShieldStateCode {
278 AuthenticityNotGuaranteed,
280 UnknownDevice,
282 UnsignedDevice,
284 UnverifiedIdentity,
286 #[serde(alias = "PreviouslyVerified")]
288 VerificationViolation,
289 MismatchedSender,
292}
293
294#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
296pub enum AlgorithmInfo {
297 MegolmV1AesSha2 {
299 curve25519_key: String,
302 sender_claimed_keys: BTreeMap<DeviceKeyAlgorithm, String>,
306
307 #[serde(default, skip_serializing_if = "Option::is_none")]
310 session_id: Option<String>,
311 },
312
313 OlmV1Curve25519AesSha2 {
315 curve25519_public_key_base64: String,
317 },
318}
319
320#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
323pub struct ForwarderInfo {
324 pub user_id: OwnedUserId,
326 pub device_id: OwnedDeviceId,
328}
329
330#[derive(Clone, Debug, PartialEq, Serialize)]
332pub struct EncryptionInfo {
333 pub sender: OwnedUserId,
336 pub sender_device: Option<OwnedDeviceId>,
339 pub forwarder: Option<ForwarderInfo>,
344 pub algorithm_info: AlgorithmInfo,
346 pub verification_state: VerificationState,
353}
354
355impl EncryptionInfo {
356 pub fn session_id(&self) -> Option<&str> {
358 if let AlgorithmInfo::MegolmV1AesSha2 { session_id, .. } = &self.algorithm_info {
359 session_id.as_deref()
360 } else {
361 None
362 }
363 }
364}
365
366impl<'de> Deserialize<'de> for EncryptionInfo {
367 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
368 where
369 D: serde::Deserializer<'de>,
370 {
371 #[derive(Deserialize)]
374 struct Helper {
375 pub sender: OwnedUserId,
376 pub sender_device: Option<OwnedDeviceId>,
377 pub forwarder: Option<ForwarderInfo>,
378 pub algorithm_info: AlgorithmInfo,
379 pub verification_state: VerificationState,
380 #[serde(rename = "session_id")]
381 pub old_session_id: Option<String>,
382 }
383
384 let Helper {
385 sender,
386 sender_device,
387 forwarder,
388 algorithm_info,
389 verification_state,
390 old_session_id,
391 } = Helper::deserialize(deserializer)?;
392
393 let algorithm_info = match algorithm_info {
394 AlgorithmInfo::MegolmV1AesSha2 { curve25519_key, sender_claimed_keys, session_id } => {
395 AlgorithmInfo::MegolmV1AesSha2 {
396 session_id: session_id.or(old_session_id),
398 curve25519_key,
399 sender_claimed_keys,
400 }
401 }
402 other => other,
403 };
404
405 Ok(EncryptionInfo { sender, sender_device, forwarder, algorithm_info, verification_state })
406 }
407}
408
409#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
420pub struct ThreadSummary {
421 #[serde(skip_serializing_if = "Option::is_none")]
423 pub latest_reply: Option<OwnedEventId>,
424
425 pub num_replies: u32,
431}
432
433#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
435pub enum ThreadSummaryStatus {
436 #[default]
438 Unknown,
439 None,
441 Some(ThreadSummary),
443}
444
445impl ThreadSummaryStatus {
446 pub fn from_opt(summary: Option<ThreadSummary>) -> Self {
448 match summary {
449 None => ThreadSummaryStatus::None,
450 Some(summary) => ThreadSummaryStatus::Some(summary),
451 }
452 }
453
454 fn is_unknown(&self) -> bool {
456 matches!(self, ThreadSummaryStatus::Unknown)
457 }
458
459 pub fn summary(&self) -> Option<&ThreadSummary> {
462 match self {
463 ThreadSummaryStatus::Unknown | ThreadSummaryStatus::None => None,
464 ThreadSummaryStatus::Some(thread_summary) => Some(thread_summary),
465 }
466 }
467}
468
469#[derive(Clone, Debug, Serialize)]
493pub struct TimelineEvent {
494 #[serde(skip)]
502 event_id: Option<OwnedEventId>,
503
504 pub kind: TimelineEventKind,
506
507 pub timestamp: Option<MilliSecondsSinceUnixEpoch>,
513
514 #[serde(skip_serializing_if = "skip_serialize_push_actions")]
519 push_actions: Option<Vec<Action>>,
520
521 #[serde(default, skip_serializing_if = "ThreadSummaryStatus::is_unknown")]
523 pub thread_summary: ThreadSummaryStatus,
524}
525
526fn skip_serialize_push_actions(push_actions: &Option<Vec<Action>>) -> bool {
528 push_actions.as_ref().is_none_or(|v| v.is_empty())
529}
530
531#[cfg(not(feature = "test-send-sync"))]
533unsafe impl Send for TimelineEvent {}
534
535#[cfg(not(feature = "test-send-sync"))]
537unsafe impl Sync for TimelineEvent {}
538
539#[cfg(feature = "test-send-sync")]
540#[test]
541fn test_send_sync_for_sync_timeline_event() {
543 fn assert_send_sync<T: crate::SendOutsideWasm + crate::SyncOutsideWasm>() {}
544
545 assert_send_sync::<TimelineEvent>();
546}
547
548impl TimelineEvent {
549 pub fn from_plaintext(event: Raw<AnySyncTimelineEvent>) -> Self {
554 Self::from_plaintext_with_max_timestamp(event, MilliSecondsSinceUnixEpoch::now())
555 }
556
557 pub fn from_plaintext_with_max_timestamp(
559 event: Raw<AnySyncTimelineEvent>,
560 max_timestamp: MilliSecondsSinceUnixEpoch,
561 ) -> Self {
562 Self::new(TimelineEventKind::PlainText { event }, None, max_timestamp)
563 }
564
565 pub fn from_decrypted(
567 decrypted: DecryptedRoomEvent,
568 push_actions: Option<Vec<Action>>,
569 ) -> Self {
570 Self::from_decrypted_with_max_timestamp(
571 decrypted,
572 push_actions,
573 MilliSecondsSinceUnixEpoch::now(),
574 )
575 }
576
577 pub fn from_decrypted_with_max_timestamp(
579 decrypted: DecryptedRoomEvent,
580 push_actions: Option<Vec<Action>>,
581 max_timestamp: MilliSecondsSinceUnixEpoch,
582 ) -> Self {
583 Self::new(TimelineEventKind::Decrypted(decrypted), push_actions, max_timestamp)
584 }
585
586 pub fn from_utd(event: Raw<AnySyncTimelineEvent>, utd_info: UnableToDecryptInfo) -> Self {
589 Self::from_utd_with_max_timestamp(event, utd_info, MilliSecondsSinceUnixEpoch::now())
590 }
591
592 pub fn from_utd_with_max_timestamp(
594 event: Raw<AnySyncTimelineEvent>,
595 utd_info: UnableToDecryptInfo,
596 max_timestamp: MilliSecondsSinceUnixEpoch,
597 ) -> Self {
598 Self::new(TimelineEventKind::UnableToDecrypt { event, utd_info }, None, max_timestamp)
599 }
600
601 fn new(
606 kind: TimelineEventKind,
607 push_actions: Option<Vec<Action>>,
608 max_timestamp: MilliSecondsSinceUnixEpoch,
609 ) -> Self {
610 let raw = kind.raw();
611
612 let bundled_thread = extract_bundled_thread(raw);
613 let timestamp = extract_timestamp(raw, max_timestamp);
614
615 Self {
616 event_id: kind.parse_event_id(),
617 kind,
618 push_actions,
619 timestamp,
620 thread_summary: match bundled_thread {
621 Some(bundled_thread) => ThreadSummaryStatus::Some(ThreadSummary {
622 latest_reply: bundled_thread
623 .latest_event
624 .get_field::<OwnedEventId>("event_id")
625 .ok()
626 .flatten(),
627 num_replies: bundled_thread.count.try_into().unwrap_or(u32::MAX),
628 }),
629 None => ThreadSummaryStatus::None,
630 },
631 }
632 }
633
634 pub fn to_decrypted(
642 &self,
643 decrypted: DecryptedRoomEvent,
644 push_actions: Option<Vec<Action>>,
645 ) -> Self {
646 debug_assert!(
647 matches!(self.kind, TimelineEventKind::Decrypted(_)).not(),
648 "`TimelineEvent::to_decrypted` has been called on an already decrypted `TimelineEvent`."
649 );
650
651 let kind = TimelineEventKind::Decrypted(decrypted);
652
653 Self {
654 event_id: kind.parse_event_id(),
657 kind,
658 timestamp: self.timestamp,
659 push_actions,
660 thread_summary: self.thread_summary.clone(),
661 }
662 }
663
664 pub fn to_utd(&self, utd_info: UnableToDecryptInfo) -> Self {
672 debug_assert!(
673 matches!(self.kind, TimelineEventKind::UnableToDecrypt { .. }).not(),
674 "`TimelineEvent::to_utd` has been called on an already UTD `TimelineEvent`."
675 );
676
677 Self {
678 event_id: self.event_id.clone(),
679 kind: TimelineEventKind::UnableToDecrypt { event: self.raw().clone(), utd_info },
680 timestamp: self.timestamp,
681 push_actions: None,
682 thread_summary: self.thread_summary.clone(),
683 }
684 }
685
686 fn from_bundled_latest_event(
689 kind: &TimelineEventKind,
690 latest_event: Raw<AnySyncMessageLikeEvent>,
691 max_timestamp: MilliSecondsSinceUnixEpoch,
692 ) -> Option<Self> {
693 match kind {
694 TimelineEventKind::Decrypted(decrypted) => {
695 if let Some(unsigned_decryption_result) =
696 decrypted.unsigned_encryption_info.as_ref().and_then(|unsigned_map| {
697 unsigned_map.get(&UnsignedEventLocation::RelationsThreadLatestEvent)
698 })
699 {
700 match unsigned_decryption_result {
701 UnsignedDecryptionResult::Decrypted(encryption_info) => {
702 return Some(TimelineEvent::from_decrypted_with_max_timestamp(
705 DecryptedRoomEvent {
706 event: latest_event.cast_unchecked(),
709 encryption_info: encryption_info.clone(),
710 unsigned_encryption_info: None,
715 },
716 None,
717 max_timestamp,
718 ));
719 }
720
721 UnsignedDecryptionResult::UnableToDecrypt(utd_info) => {
722 return Some(TimelineEvent::from_utd_with_max_timestamp(
724 latest_event.cast(),
725 utd_info.clone(),
726 max_timestamp,
727 ));
728 }
729 }
730 }
731 }
732
733 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => {
734 }
736 }
737
738 match latest_event.get_field::<MessageLikeEventType>("type") {
739 Ok(None) => {
740 let event_id = latest_event.get_field::<OwnedEventId>("event_id").ok().flatten();
741 warn!(
742 ?event_id,
743 "couldn't deserialize bundled latest thread event: missing `type` field \
744 in bundled latest thread event"
745 );
746 None
747 }
748
749 Ok(Some(MessageLikeEventType::RoomEncrypted)) => {
750 let session_id = if let Some(content) =
754 latest_event.get_field::<EncryptedEventScheme>("content").ok().flatten()
755 {
756 match content {
757 EncryptedEventScheme::MegolmV1AesSha2(content) => Some(content.session_id),
758 _ => None,
759 }
760 } else {
761 None
762 };
763
764 Some(TimelineEvent::from_utd_with_max_timestamp(
765 latest_event.cast(),
766 UnableToDecryptInfo { session_id, reason: UnableToDecryptReason::Unknown },
767 max_timestamp,
768 ))
769 }
770
771 Ok(_) => Some(TimelineEvent::from_plaintext_with_max_timestamp(
772 latest_event.cast(),
773 max_timestamp,
774 )),
775
776 Err(err) => {
777 let event_id = latest_event.get_field::<OwnedEventId>("event_id").ok().flatten();
778 warn!(?event_id, "couldn't deserialize bundled latest thread event's type: {err}");
779 None
780 }
781 }
782 }
783
784 pub fn push_actions(&self) -> Option<&[Action]> {
789 self.push_actions.as_deref()
790 }
791
792 pub fn set_push_actions(&mut self, push_actions: Vec<Action>) {
794 self.push_actions = Some(push_actions);
795 }
796
797 pub fn event_id(&self) -> Option<&EventId> {
800 self.event_id.as_deref()
801 }
802
803 pub fn sender(&self) -> Option<OwnedUserId> {
805 self.kind.parse_sender()
806 }
807
808 pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
811 self.kind.raw()
812 }
813
814 pub fn replace_raw(&mut self, replacement: Raw<AnyTimelineEvent>) {
816 match &mut self.kind {
817 TimelineEventKind::Decrypted(decrypted) => decrypted.event = replacement,
818 TimelineEventKind::UnableToDecrypt { event, .. }
819 | TimelineEventKind::PlainText { event } => {
820 *event = replacement.cast();
823 }
824 }
825
826 self.event_id = self.kind.parse_event_id();
827 }
828
829 pub fn timestamp(&self) -> Option<MilliSecondsSinceUnixEpoch> {
838 self.timestamp.or_else(|| {
839 warn!("`TimelineEvent::timestamp` is parsing the raw event to extract the `timestamp`");
840
841 extract_timestamp(self.raw(), MilliSecondsSinceUnixEpoch::now())
842 })
843 }
844
845 pub fn timestamp_raw(&self) -> Option<MilliSecondsSinceUnixEpoch> {
847 self.timestamp
848 }
849
850 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
853 self.kind.encryption_info()
854 }
855
856 pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
859 self.kind.into_raw()
860 }
861
862 pub fn bundled_latest_thread_event(&self) -> Option<Self> {
868 let bundled_thread = extract_bundled_thread(self.raw())?;
869
870 Self::from_bundled_latest_event(
871 &self.kind,
872 bundled_thread.latest_event,
873 self.timestamp_raw().unwrap_or_else(MilliSecondsSinceUnixEpoch::now),
874 )
875 }
876}
877
878impl<'de> Deserialize<'de> for TimelineEvent {
879 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
887 where
888 D: serde::Deserializer<'de>,
889 {
890 use serde_json::{Map, Value};
891
892 let value = Map::<String, Value>::deserialize(deserializer)?;
894
895 if value.contains_key("event") {
897 let v0: SyncTimelineEventDeserializationHelperV0 =
898 serde_json::from_value(Value::Object(value)).map_err(|e| {
899 serde::de::Error::custom(format!(
900 "Unable to deserialize V0-format TimelineEvent: {e}",
901 ))
902 })?;
903 Ok(v0.into())
904 }
905 else {
907 let v1: SyncTimelineEventDeserializationHelperV1 =
908 serde_json::from_value(Value::Object(value)).map_err(|e| {
909 serde::de::Error::custom(format!(
910 "Unable to deserialize V1-format TimelineEvent: {e}",
911 ))
912 })?;
913 Ok(v1.into())
914 }
915 }
916}
917
918#[derive(Clone, Serialize, Deserialize)]
920pub enum TimelineEventKind {
921 Decrypted(DecryptedRoomEvent),
923
924 UnableToDecrypt {
926 event: Raw<AnySyncTimelineEvent>,
930
931 utd_info: UnableToDecryptInfo,
933 },
934
935 PlainText {
937 event: Raw<AnySyncTimelineEvent>,
941 },
942}
943
944impl TimelineEventKind {
945 pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
948 match self {
949 TimelineEventKind::Decrypted(d) => d.event.cast_ref(),
955 TimelineEventKind::UnableToDecrypt { event, .. } => event,
956 TimelineEventKind::PlainText { event } => event,
957 }
958 }
959
960 pub fn parse_event_id(&self) -> Option<OwnedEventId> {
963 self.raw().get_field::<OwnedEventId>("event_id").ok().flatten()
964 }
965
966 pub fn parse_sender(&self) -> Option<OwnedUserId> {
968 self.raw().get_field::<OwnedUserId>("sender").ok().flatten()
969 }
970
971 pub fn is_utd(&self) -> bool {
973 matches!(self, TimelineEventKind::UnableToDecrypt { .. })
974 }
975
976 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
979 match self {
980 TimelineEventKind::Decrypted(d) => Some(&d.encryption_info),
981 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => None,
982 }
983 }
984
985 pub fn unsigned_encryption_map(
988 &self,
989 ) -> Option<&BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>> {
990 match self {
991 TimelineEventKind::Decrypted(d) => d.unsigned_encryption_info.as_ref(),
992 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => None,
993 }
994 }
995
996 pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
999 match self {
1000 TimelineEventKind::Decrypted(d) => d.event.cast(),
1006 TimelineEventKind::UnableToDecrypt { event, .. } => event,
1007 TimelineEventKind::PlainText { event } => event,
1008 }
1009 }
1010
1011 pub fn session_id(&self) -> Option<&str> {
1014 match self {
1015 TimelineEventKind::Decrypted(decrypted_room_event) => {
1016 decrypted_room_event.encryption_info.session_id()
1017 }
1018 TimelineEventKind::UnableToDecrypt { utd_info, .. } => utd_info.session_id.as_deref(),
1019 TimelineEventKind::PlainText { .. } => None,
1020 }
1021 }
1022
1023 pub fn event_type(&self) -> Option<String> {
1028 self.raw().get_field("type").ok().flatten()
1029 }
1030}
1031
1032#[cfg(not(tarpaulin_include))]
1033impl fmt::Debug for TimelineEventKind {
1034 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1035 match &self {
1036 Self::PlainText { event } => f
1037 .debug_struct("TimelineEventKind::PlainText")
1038 .field("event", &DebugRawEvent(event))
1039 .finish(),
1040
1041 Self::UnableToDecrypt { event, utd_info } => f
1042 .debug_struct("TimelineEventKind::UnableToDecrypt")
1043 .field("event", &DebugRawEvent(event))
1044 .field("utd_info", &utd_info)
1045 .finish(),
1046
1047 Self::Decrypted(decrypted) => {
1048 f.debug_tuple("TimelineEventKind::Decrypted").field(decrypted).finish()
1049 }
1050 }
1051 }
1052}
1053
1054#[derive(Clone, Serialize, Deserialize)]
1056pub struct DecryptedRoomEvent {
1057 pub event: Raw<AnyTimelineEvent>,
1065
1066 pub encryption_info: Arc<EncryptionInfo>,
1068
1069 #[serde(skip_serializing_if = "Option::is_none")]
1074 pub unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
1075}
1076
1077#[cfg(not(tarpaulin_include))]
1078impl fmt::Debug for DecryptedRoomEvent {
1079 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1080 let DecryptedRoomEvent { event, encryption_info, unsigned_encryption_info } = self;
1081
1082 f.debug_struct("DecryptedRoomEvent")
1083 .field("event", &DebugRawEvent(event))
1084 .field("encryption_info", encryption_info)
1085 .maybe_field("unsigned_encryption_info", unsigned_encryption_info)
1086 .finish()
1087 }
1088}
1089
1090#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
1092pub enum UnsignedEventLocation {
1093 RelationsReplace,
1096 RelationsThreadLatestEvent,
1099}
1100
1101impl UnsignedEventLocation {
1102 pub fn find_mut<'a>(&self, unsigned: &'a mut JsonObject) -> Option<&'a mut serde_json::Value> {
1109 let relations = unsigned.get_mut("m.relations")?.as_object_mut()?;
1110
1111 match self {
1112 Self::RelationsReplace => relations.get_mut("m.replace"),
1113 Self::RelationsThreadLatestEvent => {
1114 relations.get_mut("m.thread")?.as_object_mut()?.get_mut("latest_event")
1115 }
1116 }
1117 }
1118}
1119
1120#[derive(Debug, Clone, Serialize, Deserialize)]
1122pub enum UnsignedDecryptionResult {
1123 Decrypted(Arc<EncryptionInfo>),
1125 UnableToDecrypt(UnableToDecryptInfo),
1127}
1128
1129impl UnsignedDecryptionResult {
1130 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
1133 match self {
1134 Self::Decrypted(info) => Some(info),
1135 Self::UnableToDecrypt(_) => None,
1136 }
1137 }
1138}
1139
1140#[derive(Debug, Clone, Serialize, Deserialize)]
1142pub struct UnableToDecryptInfo {
1143 #[serde(skip_serializing_if = "Option::is_none")]
1146 pub session_id: Option<String>,
1147
1148 #[serde(default = "unknown_utd_reason", deserialize_with = "deserialize_utd_reason")]
1150 pub reason: UnableToDecryptReason,
1151}
1152
1153fn unknown_utd_reason() -> UnableToDecryptReason {
1154 UnableToDecryptReason::Unknown
1155}
1156
1157pub fn deserialize_utd_reason<'de, D>(d: D) -> Result<UnableToDecryptReason, D::Error>
1160where
1161 D: serde::Deserializer<'de>,
1162{
1163 let v: serde_json::Value = Deserialize::deserialize(d)?;
1165 if v.as_str().is_some_and(|s| s == "MissingMegolmSession") {
1168 return Ok(UnableToDecryptReason::MissingMegolmSession { withheld_code: None });
1169 }
1170 serde_json::from_value::<UnableToDecryptReason>(v).map_err(serde::de::Error::custom)
1173}
1174
1175#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1177pub enum UnableToDecryptReason {
1178 #[doc(hidden)]
1181 Unknown,
1182
1183 MalformedEncryptedEvent,
1187
1188 MissingMegolmSession {
1191 withheld_code: Option<WithheldCode>,
1194 },
1195
1196 UnknownMegolmMessageIndex,
1199
1200 MegolmDecryptionFailure,
1207
1208 PayloadDeserializationFailure,
1210
1211 MismatchedIdentityKeys,
1215
1216 SenderIdentityNotTrusted(VerificationLevel),
1220
1221 #[cfg(feature = "experimental-encrypted-state-events")]
1224 StateKeyVerificationFailed,
1225}
1226
1227impl UnableToDecryptReason {
1228 pub fn is_missing_room_key(&self) -> bool {
1231 matches!(
1234 self,
1235 Self::MissingMegolmSession { withheld_code: None } | Self::UnknownMegolmMessageIndex
1236 )
1237 }
1238}
1239
1240#[derive(
1244 Clone,
1245 PartialEq,
1246 Eq,
1247 Hash,
1248 AsStrAsRefStr,
1249 AsRefStr,
1250 FromString,
1251 DebugAsRefStr,
1252 SerializeAsRefStr,
1253 DeserializeFromCowStr,
1254)]
1255pub enum WithheldCode {
1256 #[ruma_enum(rename = "m.blacklisted")]
1258 Blacklisted,
1259
1260 #[ruma_enum(rename = "m.unverified")]
1262 Unverified,
1263
1264 #[ruma_enum(rename = "m.unauthorised")]
1268 Unauthorised,
1269
1270 #[ruma_enum(rename = "m.unavailable")]
1273 Unavailable,
1274
1275 #[ruma_enum(rename = "m.no_olm")]
1279 NoOlm,
1280
1281 #[ruma_enum(rename = "m.history_not_shared", alias = "io.element.msc4268.history_not_shared")]
1286 HistoryNotShared,
1287
1288 #[doc(hidden)]
1289 _Custom(PrivOwnedStr),
1290}
1291
1292impl fmt::Display for WithheldCode {
1293 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1294 let string = match self {
1295 WithheldCode::Blacklisted => "The sender has blocked you.",
1296 WithheldCode::Unverified => "The sender has disabled encrypting to unverified devices.",
1297 WithheldCode::Unauthorised => "You are not authorised to read the message.",
1298 WithheldCode::Unavailable => "The requested key was not found.",
1299 WithheldCode::NoOlm => "Unable to establish a secure channel.",
1300 WithheldCode::HistoryNotShared => "The sender disabled sharing encrypted history.",
1301 _ => self.as_str(),
1302 };
1303
1304 f.write_str(string)
1305 }
1306}
1307
1308#[doc(hidden)]
1312#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1313pub struct PrivOwnedStr(pub Box<str>);
1314
1315#[cfg(not(tarpaulin_include))]
1316impl fmt::Debug for PrivOwnedStr {
1317 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1318 self.0.fmt(f)
1319 }
1320}
1321
1322#[derive(Debug, Deserialize)]
1327struct SyncTimelineEventDeserializationHelperV1 {
1328 kind: TimelineEventKind,
1330
1331 #[serde(default)]
1334 timestamp: Option<MilliSecondsSinceUnixEpoch>,
1335
1336 #[serde(default)]
1338 push_actions: Vec<Action>,
1339
1340 #[serde(default)]
1342 thread_summary: ThreadSummaryStatus,
1343}
1344
1345impl From<SyncTimelineEventDeserializationHelperV1> for TimelineEvent {
1346 fn from(value: SyncTimelineEventDeserializationHelperV1) -> Self {
1347 let SyncTimelineEventDeserializationHelperV1 {
1348 kind,
1349 timestamp,
1350 push_actions,
1351 thread_summary,
1352 } = value;
1353
1354 TimelineEvent {
1363 event_id: kind.parse_event_id(),
1364 kind,
1365 timestamp,
1366 push_actions: Some(push_actions),
1367 thread_summary,
1368 }
1369 }
1370}
1371
1372#[derive(Deserialize)]
1374struct SyncTimelineEventDeserializationHelperV0 {
1375 event: Raw<AnySyncTimelineEvent>,
1377
1378 encryption_info: Option<Arc<EncryptionInfo>>,
1382
1383 #[serde(default)]
1385 push_actions: Vec<Action>,
1386
1387 unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
1392}
1393
1394impl From<SyncTimelineEventDeserializationHelperV0> for TimelineEvent {
1395 fn from(value: SyncTimelineEventDeserializationHelperV0) -> Self {
1396 let SyncTimelineEventDeserializationHelperV0 {
1397 event,
1398 encryption_info,
1399 push_actions,
1400 unsigned_encryption_info,
1401 } = value;
1402
1403 let timestamp = None;
1410
1411 let kind = match encryption_info {
1412 Some(encryption_info) => {
1413 TimelineEventKind::Decrypted(DecryptedRoomEvent {
1414 event: event.cast_unchecked(),
1421 encryption_info,
1422 unsigned_encryption_info,
1423 })
1424 }
1425
1426 None => TimelineEventKind::PlainText { event },
1427 };
1428
1429 TimelineEvent {
1430 event_id: kind.parse_event_id(),
1431 kind,
1432 timestamp,
1433 push_actions: Some(push_actions),
1434 thread_summary: ThreadSummaryStatus::Unknown,
1436 }
1437 }
1438}
1439
1440#[derive(Debug, Clone, PartialEq)]
1442pub enum ToDeviceUnableToDecryptReason {
1443 DecryptionFailure,
1446
1447 UnverifiedSenderDevice,
1451
1452 NoOlmMachine,
1455
1456 EncryptionIsDisabled,
1458}
1459
1460#[derive(Clone, Debug)]
1462pub struct ToDeviceUnableToDecryptInfo {
1463 pub reason: ToDeviceUnableToDecryptReason,
1465}
1466
1467#[derive(Clone, Debug)]
1469pub enum ProcessedToDeviceEvent {
1470 Decrypted {
1473 raw: Raw<AnyToDeviceEvent>,
1475 encryption_info: EncryptionInfo,
1477 },
1478
1479 UnableToDecrypt {
1481 encrypted_event: Raw<AnyToDeviceEvent>,
1482 utd_info: ToDeviceUnableToDecryptInfo,
1483 },
1484
1485 PlainText(Raw<AnyToDeviceEvent>),
1487
1488 Invalid(Raw<AnyToDeviceEvent>),
1492}
1493
1494impl ProcessedToDeviceEvent {
1495 pub fn to_raw(&self) -> Raw<AnyToDeviceEvent> {
1498 match self {
1499 ProcessedToDeviceEvent::Decrypted { raw, .. } => raw.clone(),
1500 ProcessedToDeviceEvent::UnableToDecrypt { encrypted_event, .. } => {
1501 encrypted_event.clone()
1502 }
1503 ProcessedToDeviceEvent::PlainText(event) => event.clone(),
1504 ProcessedToDeviceEvent::Invalid(event) => event.clone(),
1505 }
1506 }
1507
1508 pub fn as_raw(&self) -> &Raw<AnyToDeviceEvent> {
1510 match self {
1511 ProcessedToDeviceEvent::Decrypted { raw, .. } => raw,
1512 ProcessedToDeviceEvent::UnableToDecrypt { encrypted_event, .. } => encrypted_event,
1513 ProcessedToDeviceEvent::PlainText(event) => event,
1514 ProcessedToDeviceEvent::Invalid(event) => event,
1515 }
1516 }
1517}
1518
1519#[cfg(test)]
1520mod tests {
1521 use std::{collections::BTreeMap, sync::Arc};
1522
1523 use assert_matches::assert_matches;
1524 use assert_matches2::assert_let;
1525 use insta::{assert_json_snapshot, with_settings};
1526 use ruma::{
1527 DeviceKeyAlgorithm, MilliSecondsSinceUnixEpoch, UInt, event_id,
1528 events::{AnySyncTimelineEvent, room::message::RoomMessageEventContent},
1529 owned_device_id, owned_user_id,
1530 serde::Raw,
1531 };
1532 use serde::Deserialize;
1533 use serde_json::json;
1534
1535 use super::{
1536 AlgorithmInfo, DecryptedRoomEvent, DeviceLinkProblem, EncryptionInfo, ShieldState,
1537 ShieldStateCode, TimelineEvent, TimelineEventKind, UnableToDecryptInfo,
1538 UnableToDecryptReason, UnsignedDecryptionResult, UnsignedEventLocation, VerificationLevel,
1539 VerificationState, WithheldCode,
1540 };
1541 use crate::deserialized_responses::{ThreadSummary, ThreadSummaryStatus};
1542
1543 fn example_event() -> serde_json::Value {
1544 json!({
1545 "content": RoomMessageEventContent::text_plain("secret"),
1546 "type": "m.room.message",
1547 "event_id": "$xxxxx:example.org",
1548 "room_id": "!someroom:example.com",
1549 "origin_server_ts": 2189,
1550 "sender": "@carl:example.com",
1551 })
1552 }
1553
1554 #[test]
1555 fn sync_timeline_debug_content() {
1556 let room_event =
1557 TimelineEvent::from_plaintext(Raw::new(&example_event()).unwrap().cast_unchecked());
1558 let debug_s = format!("{room_event:?}");
1559 assert!(
1560 !debug_s.contains("secret"),
1561 "Debug representation contains event content!\n{debug_s}"
1562 );
1563 }
1564
1565 #[test]
1566 fn old_verification_state_to_new_migration() {
1567 #[derive(Deserialize)]
1568 struct State {
1569 state: VerificationState,
1570 }
1571
1572 let state = json!({
1573 "state": "Trusted",
1574 });
1575 let deserialized: State =
1576 serde_json::from_value(state).expect("We can deserialize the old trusted value");
1577 assert_eq!(deserialized.state, VerificationState::Verified);
1578
1579 let state = json!({
1580 "state": "UnknownDevice",
1581 });
1582
1583 let deserialized: State =
1584 serde_json::from_value(state).expect("We can deserialize the old unknown device value");
1585
1586 assert_eq!(
1587 deserialized.state,
1588 VerificationState::Unverified(VerificationLevel::None(
1589 DeviceLinkProblem::MissingDevice
1590 ))
1591 );
1592
1593 let state = json!({
1594 "state": "Untrusted",
1595 });
1596 let deserialized: State =
1597 serde_json::from_value(state).expect("We can deserialize the old trusted value");
1598
1599 assert_eq!(
1600 deserialized.state,
1601 VerificationState::Unverified(VerificationLevel::UnsignedDevice)
1602 );
1603 }
1604
1605 #[test]
1606 fn test_verification_level_deserializes() {
1607 #[derive(Deserialize)]
1609 struct Container {
1610 verification_level: VerificationLevel,
1611 }
1612 let container = json!({ "verification_level": "VerificationViolation" });
1613
1614 let deserialized: Container = serde_json::from_value(container)
1616 .expect("We can deserialize the old PreviouslyVerified value");
1617
1618 assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1620 }
1621
1622 #[test]
1623 fn test_verification_level_deserializes_from_old_previously_verified_value() {
1624 #[derive(Deserialize)]
1626 struct Container {
1627 verification_level: VerificationLevel,
1628 }
1629 let container = json!({ "verification_level": "PreviouslyVerified" });
1630
1631 let deserialized: Container = serde_json::from_value(container)
1633 .expect("We can deserialize the old PreviouslyVerified value");
1634
1635 assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1637 }
1638
1639 #[test]
1640 fn test_shield_state_code_deserializes() {
1641 #[derive(Deserialize)]
1643 struct Container {
1644 shield_state_code: ShieldStateCode,
1645 }
1646 let container = json!({ "shield_state_code": "VerificationViolation" });
1647
1648 let deserialized: Container = serde_json::from_value(container)
1650 .expect("We can deserialize the old PreviouslyVerified value");
1651
1652 assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1654 }
1655
1656 #[test]
1657 fn test_shield_state_code_deserializes_from_old_previously_verified_value() {
1658 #[derive(Deserialize)]
1660 struct Container {
1661 shield_state_code: ShieldStateCode,
1662 }
1663 let container = json!({ "shield_state_code": "PreviouslyVerified" });
1664
1665 let deserialized: Container = serde_json::from_value(container)
1667 .expect("We can deserialize the old PreviouslyVerified value");
1668
1669 assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1671 }
1672
1673 #[test]
1674 fn sync_timeline_event_serialisation() {
1675 let kind = TimelineEventKind::Decrypted(DecryptedRoomEvent {
1676 event: Raw::new(&example_event()).unwrap().cast_unchecked(),
1677 encryption_info: Arc::new(EncryptionInfo {
1678 sender: owned_user_id!("@sender:example.com"),
1679 sender_device: None,
1680 forwarder: None,
1681 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
1682 curve25519_key: "xxx".to_owned(),
1683 sender_claimed_keys: Default::default(),
1684 session_id: Some("xyz".to_owned()),
1685 },
1686 verification_state: VerificationState::Verified,
1687 }),
1688 unsigned_encryption_info: Some(BTreeMap::from([(
1689 UnsignedEventLocation::RelationsReplace,
1690 UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
1691 session_id: Some("xyz".to_owned()),
1692 reason: UnableToDecryptReason::MalformedEncryptedEvent,
1693 }),
1694 )])),
1695 });
1696 let room_event = TimelineEvent {
1697 event_id: kind.parse_event_id(),
1698 kind,
1699 timestamp: Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))),
1700 push_actions: Default::default(),
1701 thread_summary: ThreadSummaryStatus::Unknown,
1702 };
1703
1704 let serialized = serde_json::to_value(&room_event).unwrap();
1705
1706 assert_eq!(
1708 serialized,
1709 json!({
1710 "kind": {
1711 "Decrypted": {
1712 "event": {
1713 "content": {"body": "secret", "msgtype": "m.text"},
1714 "event_id": "$xxxxx:example.org",
1715 "origin_server_ts": 2189,
1716 "room_id": "!someroom:example.com",
1717 "sender": "@carl:example.com",
1718 "type": "m.room.message",
1719 },
1720 "encryption_info": {
1721 "sender": "@sender:example.com",
1722 "sender_device": null,
1723 "forwarder": null,
1724 "algorithm_info": {
1725 "MegolmV1AesSha2": {
1726 "curve25519_key": "xxx",
1727 "sender_claimed_keys": {},
1728 "session_id": "xyz",
1729 }
1730 },
1731 "verification_state": "Verified",
1732 },
1733 "unsigned_encryption_info": {
1734 "RelationsReplace": {"UnableToDecrypt": {
1735 "session_id": "xyz",
1736 "reason": "MalformedEncryptedEvent",
1737 }}
1738 }
1739 }
1740 },
1741 "timestamp": 2189,
1742 })
1743 );
1744
1745 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1747 assert_eq!(event.event_id.as_deref(), Some(event_id!("$xxxxx:example.org")));
1748 assert_eq!(event.event_id.as_deref(), event.event_id());
1749 assert_matches!(
1750 event.encryption_info().unwrap().algorithm_info,
1751 AlgorithmInfo::MegolmV1AesSha2 { .. }
1752 );
1753 assert_eq!(event.timestamp(), Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))));
1754 assert_eq!(event.timestamp(), event.timestamp_raw());
1755
1756 let serialized = json!({
1758 "event": {
1759 "content": {"body": "secret", "msgtype": "m.text"},
1760 "event_id": "$xxxxx:example.org",
1761 "origin_server_ts": 2189,
1762 "room_id": "!someroom:example.com",
1763 "sender": "@carl:example.com",
1764 "type": "m.room.message",
1765 },
1766 "encryption_info": {
1767 "sender": "@sender:example.com",
1768 "sender_device": null,
1769 "algorithm_info": {
1770 "MegolmV1AesSha2": {
1771 "curve25519_key": "xxx",
1772 "sender_claimed_keys": {}
1773 }
1774 },
1775 "verification_state": "Verified",
1776 },
1777 });
1778 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1779 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org")));
1780 assert_matches!(
1781 event.encryption_info().unwrap().algorithm_info,
1782 AlgorithmInfo::MegolmV1AesSha2 { session_id: None, .. }
1783 );
1784 assert_eq!(event.timestamp(), Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))));
1785 assert!(event.timestamp_raw().is_none());
1786
1787 let serialized = json!({
1790 "event": {
1791 "content": {"body": "secret", "msgtype": "m.text"},
1792 "event_id": "$xxxxx:example.org",
1793 "origin_server_ts": 2189,
1794 "room_id": "!someroom:example.com",
1795 "sender": "@carl:example.com",
1796 "type": "m.room.message",
1797 },
1798 "encryption_info": {
1799 "sender": "@sender:example.com",
1800 "sender_device": null,
1801 "algorithm_info": {
1802 "MegolmV1AesSha2": {
1803 "curve25519_key": "xxx",
1804 "sender_claimed_keys": {}
1805 }
1806 },
1807 "verification_state": "Verified",
1808 },
1809 "unsigned_encryption_info": {
1810 "RelationsReplace": {"UnableToDecrypt": {"session_id": "xyz"}}
1811 }
1812 });
1813 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1814 assert_eq!(event.event_id.as_deref(), event.event_id());
1815 assert_eq!(event.event_id.as_deref(), Some(event_id!("$xxxxx:example.org")));
1816 assert_matches!(
1817 event.encryption_info().unwrap().algorithm_info,
1818 AlgorithmInfo::MegolmV1AesSha2 { .. }
1819 );
1820 assert_eq!(event.timestamp(), Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))));
1821 assert!(event.timestamp_raw().is_none());
1822 assert_matches!(event.kind, TimelineEventKind::Decrypted(decrypted) => {
1823 assert_matches!(decrypted.unsigned_encryption_info, Some(map) => {
1824 assert_eq!(map.len(), 1);
1825 let (location, result) = map.into_iter().next().unwrap();
1826 assert_eq!(location, UnsignedEventLocation::RelationsReplace);
1827 assert_matches!(result, UnsignedDecryptionResult::UnableToDecrypt(utd_info) => {
1828 assert_eq!(utd_info.session_id, Some("xyz".to_owned()));
1829 assert_eq!(utd_info.reason, UnableToDecryptReason::Unknown);
1830 })
1831 });
1832 });
1833 }
1834
1835 #[test]
1836 fn test_creating_or_deserializing_an_event_extracts_summary() {
1837 let event = json!({
1838 "event_id": "$eid:example.com",
1839 "type": "m.room.message",
1840 "sender": "@alice:example.com",
1841 "origin_server_ts": 42,
1842 "content": {
1843 "body": "Hello, world!",
1844 },
1845 "unsigned": {
1846 "m.relations": {
1847 "m.thread": {
1848 "latest_event": {
1849 "event_id": "$latest_event:example.com",
1850 "type": "m.room.message",
1851 "sender": "@bob:example.com",
1852 "origin_server_ts": 42,
1853 "content": {
1854 "body": "Hello to you too!",
1855 "msgtype": "m.text",
1856 }
1857 },
1858 "count": 2,
1859 "current_user_participated": true,
1860 }
1861 }
1862 }
1863 });
1864
1865 let raw = Raw::new(&event).unwrap().cast_unchecked();
1866
1867 let timeline_event = TimelineEvent::from_plaintext(raw);
1870 assert_matches!(timeline_event.thread_summary, ThreadSummaryStatus::Some(ThreadSummary { num_replies, latest_reply }) => {
1871 assert_eq!(num_replies, 2);
1872 assert_eq!(latest_reply.as_deref(), Some(event_id!("$latest_event:example.com")));
1873 });
1874
1875 let serialized_timeline_item = json!({
1878 "kind": {
1879 "PlainText": {
1880 "event": event
1881 }
1882 }
1883 });
1884
1885 let timeline_event: TimelineEvent =
1886 serde_json::from_value(serialized_timeline_item).unwrap();
1887 assert_matches!(timeline_event.thread_summary, ThreadSummaryStatus::Unknown);
1888 }
1889
1890 #[test]
1891 fn sync_timeline_event_deserialisation_migration_for_withheld() {
1892 let serialized = json!({
1909 "kind": {
1910 "UnableToDecrypt": {
1911 "event": {
1912 "content": {
1913 "algorithm": "m.megolm.v1.aes-sha2",
1914 "ciphertext": "AwgAEoABzL1JYhqhjW9jXrlT3M6H8mJ4qffYtOQOnPuAPNxsuG20oiD/Fnpv6jnQGhU6YbV9pNM+1mRnTvxW3CbWOPjLKqCWTJTc7Q0vDEVtYePg38ncXNcwMmfhgnNAoW9S7vNs8C003x3yUl6NeZ8bH+ci870BZL+kWM/lMl10tn6U7snNmSjnE3ckvRdO+11/R4//5VzFQpZdf4j036lNSls/WIiI67Fk9iFpinz9xdRVWJFVdrAiPFwb8L5xRZ8aX+e2JDMlc1eW8gk",
1915 "device_id": "SKCGPNUWAU",
1916 "sender_key": "Gim/c7uQdSXyrrUbmUOrBT6sMC0gO7QSLmOK6B7NOm0",
1917 "session_id": "hgLyeSqXfb8vc5AjQLsg6TSHVu0HJ7HZ4B6jgMvxkrs"
1918 },
1919 "event_id": "$xxxxx:example.org",
1920 "origin_server_ts": 2189,
1921 "room_id": "!someroom:example.com",
1922 "sender": "@carl:example.com",
1923 "type": "m.room.message"
1924 },
1925 "utd_info": {
1926 "reason": "MissingMegolmSession",
1927 "session_id": "session000"
1928 }
1929 }
1930 }
1931 });
1932
1933 let result = serde_json::from_value(serialized);
1934 assert!(result.is_ok());
1935
1936 let event: TimelineEvent = result.unwrap();
1938 assert_matches!(
1939 event.kind,
1940 TimelineEventKind::UnableToDecrypt { utd_info, .. }=> {
1941 assert_matches!(
1942 utd_info.reason,
1943 UnableToDecryptReason::MissingMegolmSession { withheld_code: None }
1944 );
1945 }
1946 )
1947 }
1948
1949 #[test]
1950 fn unable_to_decrypt_info_migration_for_withheld() {
1951 let old_format = json!({
1952 "reason": "MissingMegolmSession",
1953 "session_id": "session000"
1954 });
1955
1956 let deserialized = serde_json::from_value::<UnableToDecryptInfo>(old_format).unwrap();
1957 let session_id = Some("session000".to_owned());
1958
1959 assert_eq!(deserialized.session_id, session_id);
1960 assert_eq!(
1961 deserialized.reason,
1962 UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
1963 );
1964
1965 let new_format = json!({
1966 "session_id": "session000",
1967 "reason": {
1968 "MissingMegolmSession": {
1969 "withheld_code": null
1970 }
1971 }
1972 });
1973
1974 let deserialized = serde_json::from_value::<UnableToDecryptInfo>(new_format).unwrap();
1975
1976 assert_eq!(
1977 deserialized.reason,
1978 UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
1979 );
1980 assert_eq!(deserialized.session_id, session_id);
1981 }
1982
1983 #[test]
1984 fn unable_to_decrypt_reason_is_missing_room_key() {
1985 let reason = UnableToDecryptReason::MissingMegolmSession { withheld_code: None };
1986 assert!(reason.is_missing_room_key());
1987
1988 let reason = UnableToDecryptReason::MissingMegolmSession {
1989 withheld_code: Some(WithheldCode::Blacklisted),
1990 };
1991 assert!(!reason.is_missing_room_key());
1992
1993 let reason = UnableToDecryptReason::UnknownMegolmMessageIndex;
1994 assert!(reason.is_missing_room_key());
1995 }
1996
1997 #[test]
1998 fn snapshot_test_verification_level() {
1999 with_settings!({ prepend_module_to_snapshot => false }, {
2000 assert_json_snapshot!(VerificationLevel::VerificationViolation);
2001 assert_json_snapshot!(VerificationLevel::UnsignedDevice);
2002 assert_json_snapshot!(VerificationLevel::None(DeviceLinkProblem::InsecureSource));
2003 assert_json_snapshot!(VerificationLevel::None(DeviceLinkProblem::MissingDevice));
2004 assert_json_snapshot!(VerificationLevel::UnverifiedIdentity);
2005 });
2006 }
2007
2008 #[test]
2009 fn snapshot_test_verification_states() {
2010 with_settings!({ prepend_module_to_snapshot => false }, {
2011 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::UnsignedDevice));
2012 assert_json_snapshot!(VerificationState::Unverified(
2013 VerificationLevel::VerificationViolation
2014 ));
2015 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::None(
2016 DeviceLinkProblem::InsecureSource,
2017 )));
2018 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::None(
2019 DeviceLinkProblem::MissingDevice,
2020 )));
2021 assert_json_snapshot!(VerificationState::Verified);
2022 });
2023 }
2024
2025 #[test]
2026 fn snapshot_test_shield_states() {
2027 with_settings!({ prepend_module_to_snapshot => false }, {
2028 assert_json_snapshot!(ShieldState::None);
2029 assert_json_snapshot!(ShieldState::Red {
2030 code: ShieldStateCode::UnverifiedIdentity,
2031 message: "a message"
2032 });
2033 assert_json_snapshot!(ShieldState::Grey {
2034 code: ShieldStateCode::AuthenticityNotGuaranteed,
2035 message: "authenticity of this message cannot be guaranteed",
2036 });
2037 });
2038 }
2039
2040 #[test]
2041 fn snapshot_test_shield_codes() {
2042 with_settings!({ prepend_module_to_snapshot => false }, {
2043 assert_json_snapshot!(ShieldStateCode::AuthenticityNotGuaranteed);
2044 assert_json_snapshot!(ShieldStateCode::UnknownDevice);
2045 assert_json_snapshot!(ShieldStateCode::UnsignedDevice);
2046 assert_json_snapshot!(ShieldStateCode::UnverifiedIdentity);
2047 assert_json_snapshot!(ShieldStateCode::VerificationViolation);
2048 });
2049 }
2050
2051 #[test]
2052 fn snapshot_test_algorithm_info() {
2053 let mut map = BTreeMap::new();
2054 map.insert(DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned());
2055 map.insert(DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned());
2056 let info = AlgorithmInfo::MegolmV1AesSha2 {
2057 curve25519_key: "curvecurvecurve".into(),
2058 sender_claimed_keys: BTreeMap::from([
2059 (DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned()),
2060 (DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned()),
2061 ]),
2062 session_id: None,
2063 };
2064
2065 with_settings!({ prepend_module_to_snapshot => false }, {
2066 assert_json_snapshot!(info);
2067 });
2068 }
2069
2070 #[test]
2071 fn test_encryption_info_migration() {
2072 let old_format = json!({
2075 "sender": "@alice:localhost",
2076 "sender_device": "ABCDEFGH",
2077 "algorithm_info": {
2078 "MegolmV1AesSha2": {
2079 "curve25519_key": "curvecurvecurve",
2080 "sender_claimed_keys": {}
2081 }
2082 },
2083 "verification_state": "Verified",
2084 "session_id": "mysessionid76"
2085 });
2086
2087 let deserialized = serde_json::from_value::<EncryptionInfo>(old_format).unwrap();
2088 let expected_session_id = Some("mysessionid76".to_owned());
2089
2090 assert_let!(
2091 AlgorithmInfo::MegolmV1AesSha2 { session_id, .. } = deserialized.algorithm_info.clone()
2092 );
2093 assert_eq!(session_id, expected_session_id);
2094
2095 assert_json_snapshot!(deserialized);
2096 }
2097
2098 #[test]
2099 fn snapshot_test_encryption_info() {
2100 let info = EncryptionInfo {
2101 sender: owned_user_id!("@alice:localhost"),
2102 sender_device: Some(owned_device_id!("ABCDEFGH")),
2103 forwarder: None,
2104 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
2105 curve25519_key: "curvecurvecurve".into(),
2106 sender_claimed_keys: Default::default(),
2107 session_id: Some("mysessionid76".to_owned()),
2108 },
2109 verification_state: VerificationState::Verified,
2110 };
2111
2112 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
2113 assert_json_snapshot!(info);
2114 })
2115 }
2116
2117 #[test]
2118 fn snapshot_test_sync_timeline_event() {
2119 let kind = TimelineEventKind::Decrypted(DecryptedRoomEvent {
2120 event: Raw::new(&example_event()).unwrap().cast_unchecked(),
2121 encryption_info: Arc::new(EncryptionInfo {
2122 sender: owned_user_id!("@sender:example.com"),
2123 sender_device: Some(owned_device_id!("ABCDEFGHIJ")),
2124 forwarder: None,
2125 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
2126 curve25519_key: "xxx".to_owned(),
2127 sender_claimed_keys: BTreeMap::from([
2128 (
2129 DeviceKeyAlgorithm::Ed25519,
2130 "I3YsPwqMZQXHkSQbjFNEs7b529uac2xBpI83eN3LUXo".to_owned(),
2131 ),
2132 (
2133 DeviceKeyAlgorithm::Curve25519,
2134 "qzdW3F5IMPFl0HQgz5w/L5Oi/npKUFn8Um84acIHfPY".to_owned(),
2135 ),
2136 ]),
2137 session_id: Some("mysessionid112".to_owned()),
2138 },
2139 verification_state: VerificationState::Verified,
2140 }),
2141 unsigned_encryption_info: Some(BTreeMap::from([(
2142 UnsignedEventLocation::RelationsThreadLatestEvent,
2143 UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
2144 session_id: Some("xyz".to_owned()),
2145 reason: UnableToDecryptReason::MissingMegolmSession {
2146 withheld_code: Some(WithheldCode::Unverified),
2147 },
2148 }),
2149 )])),
2150 });
2151 let room_event = TimelineEvent {
2152 event_id: kind.parse_event_id(),
2153 kind,
2154 timestamp: Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))),
2155 push_actions: Default::default(),
2156 thread_summary: ThreadSummaryStatus::Some(ThreadSummary {
2157 num_replies: 2,
2158 latest_reply: None,
2159 }),
2160 };
2161
2162 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
2163 assert_json_snapshot! {
2166 serde_json::to_value(&room_event).unwrap(),
2167 }
2168 });
2169 }
2170
2171 #[test]
2172 fn test_from_bundled_latest_event_keeps_session_id() {
2173 let session_id = "hgLyeSqXfb8vc5AjQLsg6TSHVu0HJ7HZ4B6jgMvxkrs";
2174 let serialized = json!({
2175 "content": {
2176 "algorithm": "m.megolm.v1.aes-sha2",
2177 "ciphertext": "AwgAEoABzL1JYhqhjW9jXrlT3M6H8mJ4qffYtOQOnPuAPNxsuG20oiD/Fnpv6jnQGhU6YbV9pNM+1mRnTvxW3CbWOPjLKqCWTJTc7Q0vDEVtYePg38ncXNcwMmfhgnNAoW9S7vNs8C003x3yUl6NeZ8bH+ci870BZL+kWM/lMl10tn6U7snNmSjnE3ckvRdO+11/R4//5VzFQpZdf4j036lNSls/WIiI67Fk9iFpinz9xdRVWJFVdrAiPFwb8L5xRZ8aX+e2JDMlc1eW8gk",
2178 "device_id": "SKCGPNUWAU",
2179 "sender_key": "Gim/c7uQdSXyrrUbmUOrBT6sMC0gO7QSLmOK6B7NOm0",
2180 "session_id": session_id,
2181 },
2182 "event_id": "$xxxxx:example.org",
2183 "origin_server_ts": 2189,
2184 "room_id": "!someroom:example.com",
2185 "sender": "@carl:example.com",
2186 "type": "m.room.encrypted"
2187 });
2188 let json = serialized.to_string();
2189 let value = Raw::<AnySyncTimelineEvent>::from_json_string(json).unwrap();
2190
2191 let kind = TimelineEventKind::UnableToDecrypt {
2192 event: value.clone(),
2193 utd_info: UnableToDecryptInfo {
2194 session_id: None,
2195 reason: UnableToDecryptReason::Unknown,
2196 },
2197 };
2198 let result = TimelineEvent::from_bundled_latest_event(
2199 &kind,
2200 value.cast_unchecked(),
2201 MilliSecondsSinceUnixEpoch::now(),
2202 )
2203 .expect("Could not get bundled latest event");
2204
2205 assert_let!(TimelineEventKind::UnableToDecrypt { utd_info, .. } = result.kind);
2206 assert!(utd_info.session_id.is_some());
2207 assert_eq!(utd_info.session_id.unwrap(), session_id);
2208 }
2209
2210 #[test]
2211 fn test_timeline_event_replace_raw_update_the_event_id() {
2212 let mut timeline_event = TimelineEvent::from_plaintext(
2213 Raw::new(&json!({
2214 "event_id": "$ev0",
2215 "type": "m.room.message",
2216 "sender": "@alice",
2217 "origin_server_ts": 42,
2218 "content": {
2219 "body": "Hello, World!",
2220 },
2221 "unsigned": {},
2222 }))
2223 .unwrap()
2224 .cast_unchecked(),
2225 );
2226
2227 assert_eq!(timeline_event.event_id(), Some(event_id!("$ev0")));
2228
2229 timeline_event.replace_raw(
2230 Raw::new(&json!({
2231 "event_id": "$ev1",
2232 "type": "m.room.message",
2233 "sender": "@bob",
2234 "origin_server_ts": 153,
2235 "content": {
2236 "body": "Bonjour !",
2237 },
2238 "unsigned": {},
2239 }))
2240 .unwrap()
2241 .cast_unchecked(),
2242 );
2243
2244 assert_eq!(timeline_event.event_id(), Some(event_id!("$ev1")));
2245 }
2246}