1use std::{collections::BTreeMap, fmt, ops::Not, sync::Arc};
16
17use ruma::{
18 DeviceKeyAlgorithm, MilliSecondsSinceUnixEpoch, OwnedDeviceId, OwnedEventId, OwnedUserId,
19 events::{
20 AnySyncMessageLikeEvent, AnySyncTimelineEvent, AnyTimelineEvent, AnyToDeviceEvent,
21 MessageLikeEventType, room::encrypted::EncryptedEventScheme,
22 },
23 push::Action,
24 serde::{
25 AsRefStr, AsStrAsRefStr, DebugAsRefStr, DeserializeFromCowStr, FromString, JsonObject, Raw,
26 SerializeAsRefStr,
27 },
28};
29use serde::{Deserialize, Serialize};
30use tracing::warn;
31#[cfg(target_family = "wasm")]
32use wasm_bindgen::prelude::*;
33
34use crate::{
35 debug::{DebugRawEvent, DebugStructExt},
36 serde_helpers::{extract_bundled_thread_summary, extract_timestamp},
37};
38
39const AUTHENTICITY_NOT_GUARANTEED: &str =
40 "The authenticity of this encrypted message can't be guaranteed on this device.";
41const UNVERIFIED_IDENTITY: &str = "Encrypted by an unverified user.";
42const VERIFICATION_VIOLATION: &str =
43 "Encrypted by a previously-verified user who is no longer verified.";
44const UNSIGNED_DEVICE: &str = "Encrypted by a device not verified by its owner.";
45const UNKNOWN_DEVICE: &str = "Encrypted by an unknown or deleted device.";
46const MISMATCHED_SENDER: &str = "\
47 The sender of the event does not match the owner of the device \
48 that created the Megolm session.";
49
50#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
53#[serde(from = "OldVerificationStateHelper")]
54pub enum VerificationState {
55 Verified,
60
61 Unverified(VerificationLevel),
66}
67
68#[derive(Clone, Debug, Deserialize)]
71enum OldVerificationStateHelper {
72 Untrusted,
73 UnknownDevice,
74 #[serde(alias = "Trusted")]
75 Verified,
76 Unverified(VerificationLevel),
77}
78
79impl From<OldVerificationStateHelper> for VerificationState {
80 fn from(value: OldVerificationStateHelper) -> Self {
81 match value {
82 OldVerificationStateHelper::Untrusted => {
85 VerificationState::Unverified(VerificationLevel::UnsignedDevice)
86 }
87 OldVerificationStateHelper::UnknownDevice => {
88 Self::Unverified(VerificationLevel::None(DeviceLinkProblem::MissingDevice))
89 }
90 OldVerificationStateHelper::Verified => Self::Verified,
91 OldVerificationStateHelper::Unverified(l) => Self::Unverified(l),
92 }
93 }
94}
95
96impl VerificationState {
97 pub fn to_shield_state_strict(&self) -> ShieldState {
104 match self {
105 VerificationState::Verified => ShieldState::None,
106 VerificationState::Unverified(level) => match level {
107 VerificationLevel::UnverifiedIdentity
108 | VerificationLevel::VerificationViolation
109 | VerificationLevel::UnsignedDevice => ShieldState::Red {
110 code: ShieldStateCode::UnverifiedIdentity,
111 message: UNVERIFIED_IDENTITY,
112 },
113 VerificationLevel::None(link) => match link {
114 DeviceLinkProblem::MissingDevice => ShieldState::Red {
115 code: ShieldStateCode::UnknownDevice,
116 message: UNKNOWN_DEVICE,
117 },
118 DeviceLinkProblem::InsecureSource => ShieldState::Red {
119 code: ShieldStateCode::AuthenticityNotGuaranteed,
120 message: AUTHENTICITY_NOT_GUARANTEED,
121 },
122 },
123 VerificationLevel::MismatchedSender => ShieldState::Red {
124 code: ShieldStateCode::MismatchedSender,
125 message: MISMATCHED_SENDER,
126 },
127 },
128 }
129 }
130
131 pub fn to_shield_state_lax(&self) -> ShieldState {
139 match self {
140 VerificationState::Verified => ShieldState::None,
141 VerificationState::Unverified(level) => match level {
142 VerificationLevel::UnverifiedIdentity => {
143 ShieldState::None
146 }
147 VerificationLevel::VerificationViolation => {
148 ShieldState::Red {
151 code: ShieldStateCode::VerificationViolation,
152 message: VERIFICATION_VIOLATION,
153 }
154 }
155 VerificationLevel::UnsignedDevice => {
156 ShieldState::Red {
158 code: ShieldStateCode::UnsignedDevice,
159 message: UNSIGNED_DEVICE,
160 }
161 }
162 VerificationLevel::None(link) => match link {
163 DeviceLinkProblem::MissingDevice => {
164 ShieldState::Red {
168 code: ShieldStateCode::UnknownDevice,
169 message: UNKNOWN_DEVICE,
170 }
171 }
172 DeviceLinkProblem::InsecureSource => {
173 ShieldState::Grey {
176 code: ShieldStateCode::AuthenticityNotGuaranteed,
177 message: AUTHENTICITY_NOT_GUARANTEED,
178 }
179 }
180 },
181 VerificationLevel::MismatchedSender => ShieldState::Red {
182 code: ShieldStateCode::MismatchedSender,
183 message: MISMATCHED_SENDER,
184 },
185 },
186 }
187 }
188}
189
190#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
193pub enum VerificationLevel {
194 UnverifiedIdentity,
196
197 #[serde(alias = "PreviouslyVerified")]
200 VerificationViolation,
201
202 UnsignedDevice,
205
206 None(DeviceLinkProblem),
212
213 MismatchedSender,
216}
217
218impl fmt::Display for VerificationLevel {
219 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
220 let display = match self {
221 VerificationLevel::UnverifiedIdentity => "The sender's identity was not verified",
222 VerificationLevel::VerificationViolation => {
223 "The sender's identity was previously verified but has changed"
224 }
225 VerificationLevel::UnsignedDevice => {
226 "The sending device was not signed by the user's identity"
227 }
228 VerificationLevel::None(..) => "The sending device is not known",
229 VerificationLevel::MismatchedSender => MISMATCHED_SENDER,
230 };
231 write!(f, "{display}")
232 }
233}
234
235#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
238pub enum DeviceLinkProblem {
239 MissingDevice,
243 InsecureSource,
246}
247
248#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
251pub enum ShieldState {
252 Red {
255 code: ShieldStateCode,
257 message: &'static str,
259 },
260 Grey {
263 code: ShieldStateCode,
265 message: &'static str,
267 },
268 None,
270}
271
272#[derive(Clone, Copy, Debug, Deserialize, Serialize, Eq, PartialEq)]
274#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
275#[cfg_attr(target_family = "wasm", wasm_bindgen)]
276pub enum ShieldStateCode {
277 AuthenticityNotGuaranteed,
279 UnknownDevice,
281 UnsignedDevice,
283 UnverifiedIdentity,
285 #[serde(alias = "PreviouslyVerified")]
287 VerificationViolation,
288 MismatchedSender,
291}
292
293#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
295pub enum AlgorithmInfo {
296 MegolmV1AesSha2 {
298 curve25519_key: String,
301 sender_claimed_keys: BTreeMap<DeviceKeyAlgorithm, String>,
305
306 #[serde(default, skip_serializing_if = "Option::is_none")]
309 session_id: Option<String>,
310 },
311
312 OlmV1Curve25519AesSha2 {
314 curve25519_public_key_base64: String,
316 },
317}
318
319#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
322pub struct ForwarderInfo {
323 pub user_id: OwnedUserId,
325 pub device_id: OwnedDeviceId,
327}
328
329#[derive(Clone, Debug, PartialEq, Serialize)]
331pub struct EncryptionInfo {
332 pub sender: OwnedUserId,
335 pub sender_device: Option<OwnedDeviceId>,
338 pub forwarder: Option<ForwarderInfo>,
343 pub algorithm_info: AlgorithmInfo,
345 pub verification_state: VerificationState,
352}
353
354impl EncryptionInfo {
355 pub fn session_id(&self) -> Option<&str> {
357 if let AlgorithmInfo::MegolmV1AesSha2 { session_id, .. } = &self.algorithm_info {
358 session_id.as_deref()
359 } else {
360 None
361 }
362 }
363}
364
365impl<'de> Deserialize<'de> for EncryptionInfo {
366 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
367 where
368 D: serde::Deserializer<'de>,
369 {
370 #[derive(Deserialize)]
373 struct Helper {
374 pub sender: OwnedUserId,
375 pub sender_device: Option<OwnedDeviceId>,
376 pub forwarder: Option<ForwarderInfo>,
377 pub algorithm_info: AlgorithmInfo,
378 pub verification_state: VerificationState,
379 #[serde(rename = "session_id")]
380 pub old_session_id: Option<String>,
381 }
382
383 let Helper {
384 sender,
385 sender_device,
386 forwarder,
387 algorithm_info,
388 verification_state,
389 old_session_id,
390 } = Helper::deserialize(deserializer)?;
391
392 let algorithm_info = match algorithm_info {
393 AlgorithmInfo::MegolmV1AesSha2 { curve25519_key, sender_claimed_keys, session_id } => {
394 AlgorithmInfo::MegolmV1AesSha2 {
395 session_id: session_id.or(old_session_id),
397 curve25519_key,
398 sender_claimed_keys,
399 }
400 }
401 other => other,
402 };
403
404 Ok(EncryptionInfo { sender, sender_device, forwarder, algorithm_info, verification_state })
405 }
406}
407
408#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
419pub struct ThreadSummary {
420 #[serde(skip_serializing_if = "Option::is_none")]
422 pub latest_reply: Option<OwnedEventId>,
423
424 pub num_replies: u32,
430}
431
432#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
434pub enum ThreadSummaryStatus {
435 #[default]
437 Unknown,
438 None,
440 Some(ThreadSummary),
442}
443
444impl ThreadSummaryStatus {
445 pub fn from_opt(summary: Option<ThreadSummary>) -> Self {
447 match summary {
448 None => ThreadSummaryStatus::None,
449 Some(summary) => ThreadSummaryStatus::Some(summary),
450 }
451 }
452
453 fn is_unknown(&self) -> bool {
455 matches!(self, ThreadSummaryStatus::Unknown)
456 }
457
458 pub fn summary(&self) -> Option<&ThreadSummary> {
461 match self {
462 ThreadSummaryStatus::Unknown | ThreadSummaryStatus::None => None,
463 ThreadSummaryStatus::Some(thread_summary) => Some(thread_summary),
464 }
465 }
466}
467
468#[derive(Clone, Debug, Serialize)]
492pub struct TimelineEvent {
493 pub kind: TimelineEventKind,
495
496 pub timestamp: Option<MilliSecondsSinceUnixEpoch>,
502
503 #[serde(skip_serializing_if = "skip_serialize_push_actions")]
508 push_actions: Option<Vec<Action>>,
509
510 #[serde(default, skip_serializing_if = "ThreadSummaryStatus::is_unknown")]
512 pub thread_summary: ThreadSummaryStatus,
513
514 #[serde(skip)]
519 pub bundled_latest_thread_event: Option<Box<TimelineEvent>>,
520}
521
522fn skip_serialize_push_actions(push_actions: &Option<Vec<Action>>) -> bool {
524 push_actions.as_ref().is_none_or(|v| v.is_empty())
525}
526
527#[cfg(not(feature = "test-send-sync"))]
529unsafe impl Send for TimelineEvent {}
530
531#[cfg(not(feature = "test-send-sync"))]
533unsafe impl Sync for TimelineEvent {}
534
535#[cfg(feature = "test-send-sync")]
536#[test]
537fn test_send_sync_for_sync_timeline_event() {
539 fn assert_send_sync<T: crate::SendOutsideWasm + crate::SyncOutsideWasm>() {}
540
541 assert_send_sync::<TimelineEvent>();
542}
543
544impl TimelineEvent {
545 pub fn from_plaintext(event: Raw<AnySyncTimelineEvent>) -> Self {
550 Self::from_plaintext_with_max_timestamp(event, MilliSecondsSinceUnixEpoch::now())
551 }
552
553 pub fn from_plaintext_with_max_timestamp(
555 event: Raw<AnySyncTimelineEvent>,
556 max_timestamp: MilliSecondsSinceUnixEpoch,
557 ) -> Self {
558 Self::new(TimelineEventKind::PlainText { event }, None, max_timestamp)
559 }
560
561 pub fn from_decrypted(
563 decrypted: DecryptedRoomEvent,
564 push_actions: Option<Vec<Action>>,
565 ) -> Self {
566 Self::from_decrypted_with_max_timestamp(
567 decrypted,
568 push_actions,
569 MilliSecondsSinceUnixEpoch::now(),
570 )
571 }
572
573 pub fn from_decrypted_with_max_timestamp(
575 decrypted: DecryptedRoomEvent,
576 push_actions: Option<Vec<Action>>,
577 max_timestamp: MilliSecondsSinceUnixEpoch,
578 ) -> Self {
579 Self::new(TimelineEventKind::Decrypted(decrypted), push_actions, max_timestamp)
580 }
581
582 pub fn from_utd(event: Raw<AnySyncTimelineEvent>, utd_info: UnableToDecryptInfo) -> Self {
585 Self::from_utd_with_max_timestamp(event, utd_info, MilliSecondsSinceUnixEpoch::now())
586 }
587
588 pub fn from_utd_with_max_timestamp(
590 event: Raw<AnySyncTimelineEvent>,
591 utd_info: UnableToDecryptInfo,
592 max_timestamp: MilliSecondsSinceUnixEpoch,
593 ) -> Self {
594 Self::new(TimelineEventKind::UnableToDecrypt { event, utd_info }, None, max_timestamp)
595 }
596
597 fn new(
602 kind: TimelineEventKind,
603 push_actions: Option<Vec<Action>>,
604 max_timestamp: MilliSecondsSinceUnixEpoch,
605 ) -> Self {
606 let raw = kind.raw();
607
608 let (thread_summary, latest_thread_event) = extract_bundled_thread_summary(raw);
609
610 let bundled_latest_thread_event =
611 Self::from_bundled_latest_event(&kind, latest_thread_event, max_timestamp);
612
613 let timestamp = extract_timestamp(raw, max_timestamp);
614
615 Self { kind, push_actions, timestamp, thread_summary, bundled_latest_thread_event }
616 }
617
618 pub fn to_decrypted(
626 &self,
627 decrypted: DecryptedRoomEvent,
628 push_actions: Option<Vec<Action>>,
629 ) -> Self {
630 debug_assert!(
631 matches!(self.kind, TimelineEventKind::Decrypted(_)).not(),
632 "`TimelineEvent::to_decrypted` has been called on an already decrypted `TimelineEvent`."
633 );
634
635 Self {
636 kind: TimelineEventKind::Decrypted(decrypted),
637 timestamp: self.timestamp,
638 push_actions,
639 thread_summary: self.thread_summary.clone(),
640 bundled_latest_thread_event: self.bundled_latest_thread_event.clone(),
641 }
642 }
643
644 pub fn to_utd(&self, utd_info: UnableToDecryptInfo) -> Self {
652 debug_assert!(
653 matches!(self.kind, TimelineEventKind::UnableToDecrypt { .. }).not(),
654 "`TimelineEvent::to_utd` has been called on an already UTD `TimelineEvent`."
655 );
656
657 Self {
658 kind: TimelineEventKind::UnableToDecrypt { event: self.raw().clone(), utd_info },
659 timestamp: self.timestamp,
660 push_actions: None,
661 thread_summary: self.thread_summary.clone(),
662 bundled_latest_thread_event: self.bundled_latest_thread_event.clone(),
663 }
664 }
665
666 fn from_bundled_latest_event(
670 kind: &TimelineEventKind,
671 latest_event: Option<Raw<AnySyncMessageLikeEvent>>,
672 max_timestamp: MilliSecondsSinceUnixEpoch,
673 ) -> Option<Box<Self>> {
674 let latest_event = latest_event?;
675
676 match kind {
677 TimelineEventKind::Decrypted(decrypted) => {
678 if let Some(unsigned_decryption_result) =
679 decrypted.unsigned_encryption_info.as_ref().and_then(|unsigned_map| {
680 unsigned_map.get(&UnsignedEventLocation::RelationsThreadLatestEvent)
681 })
682 {
683 match unsigned_decryption_result {
684 UnsignedDecryptionResult::Decrypted(encryption_info) => {
685 return Some(Box::new(
688 TimelineEvent::from_decrypted_with_max_timestamp(
689 DecryptedRoomEvent {
690 event: latest_event.cast_unchecked(),
693 encryption_info: encryption_info.clone(),
694 unsigned_encryption_info: None,
699 },
700 None,
701 max_timestamp,
702 ),
703 ));
704 }
705
706 UnsignedDecryptionResult::UnableToDecrypt(utd_info) => {
707 return Some(Box::new(TimelineEvent::from_utd_with_max_timestamp(
709 latest_event.cast(),
710 utd_info.clone(),
711 max_timestamp,
712 )));
713 }
714 }
715 }
716 }
717
718 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => {
719 }
721 }
722
723 match latest_event.get_field::<MessageLikeEventType>("type") {
724 Ok(None) => {
725 let event_id = latest_event.get_field::<OwnedEventId>("event_id").ok().flatten();
726 warn!(
727 ?event_id,
728 "couldn't deserialize bundled latest thread event: missing `type` field \
729 in bundled latest thread event"
730 );
731 None
732 }
733
734 Ok(Some(MessageLikeEventType::RoomEncrypted)) => {
735 let session_id = if let Some(content) =
739 latest_event.get_field::<EncryptedEventScheme>("content").ok().flatten()
740 {
741 match content {
742 EncryptedEventScheme::MegolmV1AesSha2(content) => Some(content.session_id),
743 _ => None,
744 }
745 } else {
746 None
747 };
748 Some(Box::new(TimelineEvent::from_utd_with_max_timestamp(
749 latest_event.cast(),
750 UnableToDecryptInfo { session_id, reason: UnableToDecryptReason::Unknown },
751 max_timestamp,
752 )))
753 }
754
755 Ok(_) => Some(Box::new(TimelineEvent::from_plaintext_with_max_timestamp(
756 latest_event.cast(),
757 max_timestamp,
758 ))),
759
760 Err(err) => {
761 let event_id = latest_event.get_field::<OwnedEventId>("event_id").ok().flatten();
762 warn!(?event_id, "couldn't deserialize bundled latest thread event's type: {err}");
763 None
764 }
765 }
766 }
767
768 pub fn push_actions(&self) -> Option<&[Action]> {
773 self.push_actions.as_deref()
774 }
775
776 pub fn set_push_actions(&mut self, push_actions: Vec<Action>) {
778 self.push_actions = Some(push_actions);
779 }
780
781 pub fn event_id(&self) -> Option<OwnedEventId> {
784 self.kind.event_id()
785 }
786
787 pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
790 self.kind.raw()
791 }
792
793 pub fn replace_raw(&mut self, replacement: Raw<AnyTimelineEvent>) {
795 match &mut self.kind {
796 TimelineEventKind::Decrypted(decrypted) => decrypted.event = replacement,
797 TimelineEventKind::UnableToDecrypt { event, .. }
798 | TimelineEventKind::PlainText { event } => {
799 *event = replacement.cast();
802 }
803 }
804 }
805
806 pub fn timestamp(&self) -> Option<MilliSecondsSinceUnixEpoch> {
815 self.timestamp.or_else(|| {
816 warn!("`TimelineEvent::timestamp` is parsing the raw event to extract the `timestamp`");
817
818 extract_timestamp(self.raw(), MilliSecondsSinceUnixEpoch::now())
819 })
820 }
821
822 pub fn timestamp_raw(&self) -> Option<MilliSecondsSinceUnixEpoch> {
824 self.timestamp
825 }
826
827 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
830 self.kind.encryption_info()
831 }
832
833 pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
836 self.kind.into_raw()
837 }
838}
839
840impl<'de> Deserialize<'de> for TimelineEvent {
841 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
849 where
850 D: serde::Deserializer<'de>,
851 {
852 use serde_json::{Map, Value};
853
854 let value = Map::<String, Value>::deserialize(deserializer)?;
856
857 if value.contains_key("event") {
859 let v0: SyncTimelineEventDeserializationHelperV0 =
860 serde_json::from_value(Value::Object(value)).map_err(|e| {
861 serde::de::Error::custom(format!(
862 "Unable to deserialize V0-format TimelineEvent: {e}",
863 ))
864 })?;
865 Ok(v0.into())
866 }
867 else {
869 let v1: SyncTimelineEventDeserializationHelperV1 =
870 serde_json::from_value(Value::Object(value)).map_err(|e| {
871 serde::de::Error::custom(format!(
872 "Unable to deserialize V1-format TimelineEvent: {e}",
873 ))
874 })?;
875 Ok(v1.into())
876 }
877 }
878}
879
880#[derive(Clone, Serialize, Deserialize)]
882pub enum TimelineEventKind {
883 Decrypted(DecryptedRoomEvent),
885
886 UnableToDecrypt {
888 event: Raw<AnySyncTimelineEvent>,
892
893 utd_info: UnableToDecryptInfo,
895 },
896
897 PlainText {
899 event: Raw<AnySyncTimelineEvent>,
903 },
904}
905
906impl TimelineEventKind {
907 pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
910 match self {
911 TimelineEventKind::Decrypted(d) => d.event.cast_ref(),
917 TimelineEventKind::UnableToDecrypt { event, .. } => event,
918 TimelineEventKind::PlainText { event } => event,
919 }
920 }
921
922 pub fn event_id(&self) -> Option<OwnedEventId> {
925 self.raw().get_field::<OwnedEventId>("event_id").ok().flatten()
926 }
927
928 pub fn is_utd(&self) -> bool {
930 matches!(self, TimelineEventKind::UnableToDecrypt { .. })
931 }
932
933 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
936 match self {
937 TimelineEventKind::Decrypted(d) => Some(&d.encryption_info),
938 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => None,
939 }
940 }
941
942 pub fn unsigned_encryption_map(
945 &self,
946 ) -> Option<&BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>> {
947 match self {
948 TimelineEventKind::Decrypted(d) => d.unsigned_encryption_info.as_ref(),
949 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => None,
950 }
951 }
952
953 pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
956 match self {
957 TimelineEventKind::Decrypted(d) => d.event.cast(),
963 TimelineEventKind::UnableToDecrypt { event, .. } => event,
964 TimelineEventKind::PlainText { event } => event,
965 }
966 }
967
968 pub fn session_id(&self) -> Option<&str> {
971 match self {
972 TimelineEventKind::Decrypted(decrypted_room_event) => {
973 decrypted_room_event.encryption_info.session_id()
974 }
975 TimelineEventKind::UnableToDecrypt { utd_info, .. } => utd_info.session_id.as_deref(),
976 TimelineEventKind::PlainText { .. } => None,
977 }
978 }
979
980 pub fn event_type(&self) -> Option<String> {
985 self.raw().get_field("type").ok().flatten()
986 }
987}
988
989#[cfg(not(tarpaulin_include))]
990impl fmt::Debug for TimelineEventKind {
991 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
992 match &self {
993 Self::PlainText { event } => f
994 .debug_struct("TimelineEventKind::PlainText")
995 .field("event", &DebugRawEvent(event))
996 .finish(),
997
998 Self::UnableToDecrypt { event, utd_info } => f
999 .debug_struct("TimelineEventKind::UnableToDecrypt")
1000 .field("event", &DebugRawEvent(event))
1001 .field("utd_info", &utd_info)
1002 .finish(),
1003
1004 Self::Decrypted(decrypted) => {
1005 f.debug_tuple("TimelineEventKind::Decrypted").field(decrypted).finish()
1006 }
1007 }
1008 }
1009}
1010
1011#[derive(Clone, Serialize, Deserialize)]
1012pub struct DecryptedRoomEvent {
1014 pub event: Raw<AnyTimelineEvent>,
1022
1023 pub encryption_info: Arc<EncryptionInfo>,
1025
1026 #[serde(skip_serializing_if = "Option::is_none")]
1031 pub unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
1032}
1033
1034#[cfg(not(tarpaulin_include))]
1035impl fmt::Debug for DecryptedRoomEvent {
1036 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1037 let DecryptedRoomEvent { event, encryption_info, unsigned_encryption_info } = self;
1038
1039 f.debug_struct("DecryptedRoomEvent")
1040 .field("event", &DebugRawEvent(event))
1041 .field("encryption_info", encryption_info)
1042 .maybe_field("unsigned_encryption_info", unsigned_encryption_info)
1043 .finish()
1044 }
1045}
1046
1047#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
1049pub enum UnsignedEventLocation {
1050 RelationsReplace,
1053 RelationsThreadLatestEvent,
1056}
1057
1058impl UnsignedEventLocation {
1059 pub fn find_mut<'a>(&self, unsigned: &'a mut JsonObject) -> Option<&'a mut serde_json::Value> {
1066 let relations = unsigned.get_mut("m.relations")?.as_object_mut()?;
1067
1068 match self {
1069 Self::RelationsReplace => relations.get_mut("m.replace"),
1070 Self::RelationsThreadLatestEvent => {
1071 relations.get_mut("m.thread")?.as_object_mut()?.get_mut("latest_event")
1072 }
1073 }
1074 }
1075}
1076
1077#[derive(Debug, Clone, Serialize, Deserialize)]
1079pub enum UnsignedDecryptionResult {
1080 Decrypted(Arc<EncryptionInfo>),
1082 UnableToDecrypt(UnableToDecryptInfo),
1084}
1085
1086impl UnsignedDecryptionResult {
1087 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
1090 match self {
1091 Self::Decrypted(info) => Some(info),
1092 Self::UnableToDecrypt(_) => None,
1093 }
1094 }
1095}
1096
1097#[derive(Debug, Clone, Serialize, Deserialize)]
1099pub struct UnableToDecryptInfo {
1100 #[serde(skip_serializing_if = "Option::is_none")]
1103 pub session_id: Option<String>,
1104
1105 #[serde(default = "unknown_utd_reason", deserialize_with = "deserialize_utd_reason")]
1107 pub reason: UnableToDecryptReason,
1108}
1109
1110fn unknown_utd_reason() -> UnableToDecryptReason {
1111 UnableToDecryptReason::Unknown
1112}
1113
1114pub fn deserialize_utd_reason<'de, D>(d: D) -> Result<UnableToDecryptReason, D::Error>
1117where
1118 D: serde::Deserializer<'de>,
1119{
1120 let v: serde_json::Value = Deserialize::deserialize(d)?;
1122 if v.as_str().is_some_and(|s| s == "MissingMegolmSession") {
1125 return Ok(UnableToDecryptReason::MissingMegolmSession { withheld_code: None });
1126 }
1127 serde_json::from_value::<UnableToDecryptReason>(v).map_err(serde::de::Error::custom)
1130}
1131
1132#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1134pub enum UnableToDecryptReason {
1135 #[doc(hidden)]
1138 Unknown,
1139
1140 MalformedEncryptedEvent,
1144
1145 MissingMegolmSession {
1148 withheld_code: Option<WithheldCode>,
1151 },
1152
1153 UnknownMegolmMessageIndex,
1156
1157 MegolmDecryptionFailure,
1164
1165 PayloadDeserializationFailure,
1167
1168 MismatchedIdentityKeys,
1172
1173 SenderIdentityNotTrusted(VerificationLevel),
1177
1178 #[cfg(feature = "experimental-encrypted-state-events")]
1181 StateKeyVerificationFailed,
1182}
1183
1184impl UnableToDecryptReason {
1185 pub fn is_missing_room_key(&self) -> bool {
1188 matches!(
1191 self,
1192 Self::MissingMegolmSession { withheld_code: None } | Self::UnknownMegolmMessageIndex
1193 )
1194 }
1195}
1196
1197#[derive(
1201 Clone,
1202 PartialEq,
1203 Eq,
1204 Hash,
1205 AsStrAsRefStr,
1206 AsRefStr,
1207 FromString,
1208 DebugAsRefStr,
1209 SerializeAsRefStr,
1210 DeserializeFromCowStr,
1211)]
1212pub enum WithheldCode {
1213 #[ruma_enum(rename = "m.blacklisted")]
1215 Blacklisted,
1216
1217 #[ruma_enum(rename = "m.unverified")]
1219 Unverified,
1220
1221 #[ruma_enum(rename = "m.unauthorised")]
1225 Unauthorised,
1226
1227 #[ruma_enum(rename = "m.unavailable")]
1230 Unavailable,
1231
1232 #[ruma_enum(rename = "m.no_olm")]
1236 NoOlm,
1237
1238 #[ruma_enum(rename = "io.element.msc4268.history_not_shared", alias = "m.history_not_shared")]
1243 HistoryNotShared,
1244
1245 #[doc(hidden)]
1246 _Custom(PrivOwnedStr),
1247}
1248
1249impl fmt::Display for WithheldCode {
1250 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1251 let string = match self {
1252 WithheldCode::Blacklisted => "The sender has blocked you.",
1253 WithheldCode::Unverified => "The sender has disabled encrypting to unverified devices.",
1254 WithheldCode::Unauthorised => "You are not authorised to read the message.",
1255 WithheldCode::Unavailable => "The requested key was not found.",
1256 WithheldCode::NoOlm => "Unable to establish a secure channel.",
1257 WithheldCode::HistoryNotShared => "The sender disabled sharing encrypted history.",
1258 _ => self.as_str(),
1259 };
1260
1261 f.write_str(string)
1262 }
1263}
1264
1265#[doc(hidden)]
1269#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1270pub struct PrivOwnedStr(pub Box<str>);
1271
1272#[cfg(not(tarpaulin_include))]
1273impl fmt::Debug for PrivOwnedStr {
1274 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1275 self.0.fmt(f)
1276 }
1277}
1278
1279#[derive(Debug, Deserialize)]
1284struct SyncTimelineEventDeserializationHelperV1 {
1285 kind: TimelineEventKind,
1287
1288 #[serde(default)]
1291 timestamp: Option<MilliSecondsSinceUnixEpoch>,
1292
1293 #[serde(default)]
1295 push_actions: Vec<Action>,
1296
1297 #[serde(default)]
1299 thread_summary: ThreadSummaryStatus,
1300}
1301
1302impl From<SyncTimelineEventDeserializationHelperV1> for TimelineEvent {
1303 fn from(value: SyncTimelineEventDeserializationHelperV1) -> Self {
1304 let SyncTimelineEventDeserializationHelperV1 {
1305 kind,
1306 timestamp,
1307 push_actions,
1308 thread_summary,
1309 } = value;
1310
1311 TimelineEvent {
1320 kind,
1321 timestamp,
1322 push_actions: Some(push_actions),
1323 thread_summary,
1324 bundled_latest_thread_event: None,
1326 }
1327 }
1328}
1329
1330#[derive(Deserialize)]
1332struct SyncTimelineEventDeserializationHelperV0 {
1333 event: Raw<AnySyncTimelineEvent>,
1335
1336 encryption_info: Option<Arc<EncryptionInfo>>,
1340
1341 #[serde(default)]
1343 push_actions: Vec<Action>,
1344
1345 unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
1350}
1351
1352impl From<SyncTimelineEventDeserializationHelperV0> for TimelineEvent {
1353 fn from(value: SyncTimelineEventDeserializationHelperV0) -> Self {
1354 let SyncTimelineEventDeserializationHelperV0 {
1355 event,
1356 encryption_info,
1357 push_actions,
1358 unsigned_encryption_info,
1359 } = value;
1360
1361 let timestamp = None;
1368
1369 let kind = match encryption_info {
1370 Some(encryption_info) => {
1371 TimelineEventKind::Decrypted(DecryptedRoomEvent {
1372 event: event.cast_unchecked(),
1379 encryption_info,
1380 unsigned_encryption_info,
1381 })
1382 }
1383
1384 None => TimelineEventKind::PlainText { event },
1385 };
1386
1387 TimelineEvent {
1388 kind,
1389 timestamp,
1390 push_actions: Some(push_actions),
1391 thread_summary: ThreadSummaryStatus::Unknown,
1393 bundled_latest_thread_event: None,
1395 }
1396 }
1397}
1398
1399#[derive(Debug, Clone, PartialEq)]
1401pub enum ToDeviceUnableToDecryptReason {
1402 DecryptionFailure,
1405
1406 UnverifiedSenderDevice,
1410
1411 NoOlmMachine,
1414
1415 EncryptionIsDisabled,
1417}
1418
1419#[derive(Clone, Debug)]
1421pub struct ToDeviceUnableToDecryptInfo {
1422 pub reason: ToDeviceUnableToDecryptReason,
1424}
1425
1426#[derive(Clone, Debug)]
1428pub enum ProcessedToDeviceEvent {
1429 Decrypted {
1432 raw: Raw<AnyToDeviceEvent>,
1434 encryption_info: EncryptionInfo,
1436 },
1437
1438 UnableToDecrypt {
1440 encrypted_event: Raw<AnyToDeviceEvent>,
1441 utd_info: ToDeviceUnableToDecryptInfo,
1442 },
1443
1444 PlainText(Raw<AnyToDeviceEvent>),
1446
1447 Invalid(Raw<AnyToDeviceEvent>),
1451}
1452
1453impl ProcessedToDeviceEvent {
1454 pub fn to_raw(&self) -> Raw<AnyToDeviceEvent> {
1457 match self {
1458 ProcessedToDeviceEvent::Decrypted { raw, .. } => raw.clone(),
1459 ProcessedToDeviceEvent::UnableToDecrypt { encrypted_event, .. } => {
1460 encrypted_event.clone()
1461 }
1462 ProcessedToDeviceEvent::PlainText(event) => event.clone(),
1463 ProcessedToDeviceEvent::Invalid(event) => event.clone(),
1464 }
1465 }
1466
1467 pub fn as_raw(&self) -> &Raw<AnyToDeviceEvent> {
1469 match self {
1470 ProcessedToDeviceEvent::Decrypted { raw, .. } => raw,
1471 ProcessedToDeviceEvent::UnableToDecrypt { encrypted_event, .. } => encrypted_event,
1472 ProcessedToDeviceEvent::PlainText(event) => event,
1473 ProcessedToDeviceEvent::Invalid(event) => event,
1474 }
1475 }
1476}
1477
1478#[cfg(test)]
1479mod tests {
1480 use std::{collections::BTreeMap, sync::Arc};
1481
1482 use assert_matches::assert_matches;
1483 use assert_matches2::assert_let;
1484 use insta::{assert_json_snapshot, with_settings};
1485 use ruma::{
1486 DeviceKeyAlgorithm, MilliSecondsSinceUnixEpoch, UInt, device_id, event_id,
1487 events::{AnySyncTimelineEvent, room::message::RoomMessageEventContent},
1488 serde::Raw,
1489 user_id,
1490 };
1491 use serde::Deserialize;
1492 use serde_json::json;
1493
1494 use super::{
1495 AlgorithmInfo, DecryptedRoomEvent, DeviceLinkProblem, EncryptionInfo, ShieldState,
1496 ShieldStateCode, TimelineEvent, TimelineEventKind, UnableToDecryptInfo,
1497 UnableToDecryptReason, UnsignedDecryptionResult, UnsignedEventLocation, VerificationLevel,
1498 VerificationState, WithheldCode,
1499 };
1500 use crate::deserialized_responses::{ThreadSummary, ThreadSummaryStatus};
1501
1502 fn example_event() -> serde_json::Value {
1503 json!({
1504 "content": RoomMessageEventContent::text_plain("secret"),
1505 "type": "m.room.message",
1506 "event_id": "$xxxxx:example.org",
1507 "room_id": "!someroom:example.com",
1508 "origin_server_ts": 2189,
1509 "sender": "@carl:example.com",
1510 })
1511 }
1512
1513 #[test]
1514 fn sync_timeline_debug_content() {
1515 let room_event =
1516 TimelineEvent::from_plaintext(Raw::new(&example_event()).unwrap().cast_unchecked());
1517 let debug_s = format!("{room_event:?}");
1518 assert!(
1519 !debug_s.contains("secret"),
1520 "Debug representation contains event content!\n{debug_s}"
1521 );
1522 }
1523
1524 #[test]
1525 fn old_verification_state_to_new_migration() {
1526 #[derive(Deserialize)]
1527 struct State {
1528 state: VerificationState,
1529 }
1530
1531 let state = json!({
1532 "state": "Trusted",
1533 });
1534 let deserialized: State =
1535 serde_json::from_value(state).expect("We can deserialize the old trusted value");
1536 assert_eq!(deserialized.state, VerificationState::Verified);
1537
1538 let state = json!({
1539 "state": "UnknownDevice",
1540 });
1541
1542 let deserialized: State =
1543 serde_json::from_value(state).expect("We can deserialize the old unknown device value");
1544
1545 assert_eq!(
1546 deserialized.state,
1547 VerificationState::Unverified(VerificationLevel::None(
1548 DeviceLinkProblem::MissingDevice
1549 ))
1550 );
1551
1552 let state = json!({
1553 "state": "Untrusted",
1554 });
1555 let deserialized: State =
1556 serde_json::from_value(state).expect("We can deserialize the old trusted value");
1557
1558 assert_eq!(
1559 deserialized.state,
1560 VerificationState::Unverified(VerificationLevel::UnsignedDevice)
1561 );
1562 }
1563
1564 #[test]
1565 fn test_verification_level_deserializes() {
1566 #[derive(Deserialize)]
1568 struct Container {
1569 verification_level: VerificationLevel,
1570 }
1571 let container = json!({ "verification_level": "VerificationViolation" });
1572
1573 let deserialized: Container = serde_json::from_value(container)
1575 .expect("We can deserialize the old PreviouslyVerified value");
1576
1577 assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1579 }
1580
1581 #[test]
1582 fn test_verification_level_deserializes_from_old_previously_verified_value() {
1583 #[derive(Deserialize)]
1585 struct Container {
1586 verification_level: VerificationLevel,
1587 }
1588 let container = json!({ "verification_level": "PreviouslyVerified" });
1589
1590 let deserialized: Container = serde_json::from_value(container)
1592 .expect("We can deserialize the old PreviouslyVerified value");
1593
1594 assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1596 }
1597
1598 #[test]
1599 fn test_shield_state_code_deserializes() {
1600 #[derive(Deserialize)]
1602 struct Container {
1603 shield_state_code: ShieldStateCode,
1604 }
1605 let container = json!({ "shield_state_code": "VerificationViolation" });
1606
1607 let deserialized: Container = serde_json::from_value(container)
1609 .expect("We can deserialize the old PreviouslyVerified value");
1610
1611 assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1613 }
1614
1615 #[test]
1616 fn test_shield_state_code_deserializes_from_old_previously_verified_value() {
1617 #[derive(Deserialize)]
1619 struct Container {
1620 shield_state_code: ShieldStateCode,
1621 }
1622 let container = json!({ "shield_state_code": "PreviouslyVerified" });
1623
1624 let deserialized: Container = serde_json::from_value(container)
1626 .expect("We can deserialize the old PreviouslyVerified value");
1627
1628 assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1630 }
1631
1632 #[test]
1633 fn sync_timeline_event_serialisation() {
1634 let room_event = TimelineEvent {
1635 kind: TimelineEventKind::Decrypted(DecryptedRoomEvent {
1636 event: Raw::new(&example_event()).unwrap().cast_unchecked(),
1637 encryption_info: Arc::new(EncryptionInfo {
1638 sender: user_id!("@sender:example.com").to_owned(),
1639 sender_device: None,
1640 forwarder: None,
1641 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
1642 curve25519_key: "xxx".to_owned(),
1643 sender_claimed_keys: Default::default(),
1644 session_id: Some("xyz".to_owned()),
1645 },
1646 verification_state: VerificationState::Verified,
1647 }),
1648 unsigned_encryption_info: Some(BTreeMap::from([(
1649 UnsignedEventLocation::RelationsReplace,
1650 UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
1651 session_id: Some("xyz".to_owned()),
1652 reason: UnableToDecryptReason::MalformedEncryptedEvent,
1653 }),
1654 )])),
1655 }),
1656 timestamp: Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))),
1657 push_actions: Default::default(),
1658 thread_summary: ThreadSummaryStatus::Unknown,
1659 bundled_latest_thread_event: None,
1660 };
1661
1662 let serialized = serde_json::to_value(&room_event).unwrap();
1663
1664 assert_eq!(
1666 serialized,
1667 json!({
1668 "kind": {
1669 "Decrypted": {
1670 "event": {
1671 "content": {"body": "secret", "msgtype": "m.text"},
1672 "event_id": "$xxxxx:example.org",
1673 "origin_server_ts": 2189,
1674 "room_id": "!someroom:example.com",
1675 "sender": "@carl:example.com",
1676 "type": "m.room.message",
1677 },
1678 "encryption_info": {
1679 "sender": "@sender:example.com",
1680 "sender_device": null,
1681 "forwarder": null,
1682 "algorithm_info": {
1683 "MegolmV1AesSha2": {
1684 "curve25519_key": "xxx",
1685 "sender_claimed_keys": {},
1686 "session_id": "xyz",
1687 }
1688 },
1689 "verification_state": "Verified",
1690 },
1691 "unsigned_encryption_info": {
1692 "RelationsReplace": {"UnableToDecrypt": {
1693 "session_id": "xyz",
1694 "reason": "MalformedEncryptedEvent",
1695 }}
1696 }
1697 }
1698 },
1699 "timestamp": 2189,
1700 })
1701 );
1702
1703 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1705 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1706 assert_matches!(
1707 event.encryption_info().unwrap().algorithm_info,
1708 AlgorithmInfo::MegolmV1AesSha2 { .. }
1709 );
1710 assert_eq!(event.timestamp(), Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))));
1711 assert_eq!(event.timestamp(), event.timestamp_raw());
1712
1713 let serialized = json!({
1715 "event": {
1716 "content": {"body": "secret", "msgtype": "m.text"},
1717 "event_id": "$xxxxx:example.org",
1718 "origin_server_ts": 2189,
1719 "room_id": "!someroom:example.com",
1720 "sender": "@carl:example.com",
1721 "type": "m.room.message",
1722 },
1723 "encryption_info": {
1724 "sender": "@sender:example.com",
1725 "sender_device": null,
1726 "algorithm_info": {
1727 "MegolmV1AesSha2": {
1728 "curve25519_key": "xxx",
1729 "sender_claimed_keys": {}
1730 }
1731 },
1732 "verification_state": "Verified",
1733 },
1734 });
1735 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1736 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1737 assert_matches!(
1738 event.encryption_info().unwrap().algorithm_info,
1739 AlgorithmInfo::MegolmV1AesSha2 { session_id: None, .. }
1740 );
1741 assert_eq!(event.timestamp(), Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))));
1742 assert!(event.timestamp_raw().is_none());
1743
1744 let serialized = json!({
1747 "event": {
1748 "content": {"body": "secret", "msgtype": "m.text"},
1749 "event_id": "$xxxxx:example.org",
1750 "origin_server_ts": 2189,
1751 "room_id": "!someroom:example.com",
1752 "sender": "@carl:example.com",
1753 "type": "m.room.message",
1754 },
1755 "encryption_info": {
1756 "sender": "@sender:example.com",
1757 "sender_device": null,
1758 "algorithm_info": {
1759 "MegolmV1AesSha2": {
1760 "curve25519_key": "xxx",
1761 "sender_claimed_keys": {}
1762 }
1763 },
1764 "verification_state": "Verified",
1765 },
1766 "unsigned_encryption_info": {
1767 "RelationsReplace": {"UnableToDecrypt": {"session_id": "xyz"}}
1768 }
1769 });
1770 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1771 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1772 assert_matches!(
1773 event.encryption_info().unwrap().algorithm_info,
1774 AlgorithmInfo::MegolmV1AesSha2 { .. }
1775 );
1776 assert_eq!(event.timestamp(), Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))));
1777 assert!(event.timestamp_raw().is_none());
1778 assert_matches!(event.kind, TimelineEventKind::Decrypted(decrypted) => {
1779 assert_matches!(decrypted.unsigned_encryption_info, Some(map) => {
1780 assert_eq!(map.len(), 1);
1781 let (location, result) = map.into_iter().next().unwrap();
1782 assert_eq!(location, UnsignedEventLocation::RelationsReplace);
1783 assert_matches!(result, UnsignedDecryptionResult::UnableToDecrypt(utd_info) => {
1784 assert_eq!(utd_info.session_id, Some("xyz".to_owned()));
1785 assert_eq!(utd_info.reason, UnableToDecryptReason::Unknown);
1786 })
1787 });
1788 });
1789 }
1790
1791 #[test]
1792 fn test_creating_or_deserializing_an_event_extracts_summary() {
1793 let event = json!({
1794 "event_id": "$eid:example.com",
1795 "type": "m.room.message",
1796 "sender": "@alice:example.com",
1797 "origin_server_ts": 42,
1798 "content": {
1799 "body": "Hello, world!",
1800 },
1801 "unsigned": {
1802 "m.relations": {
1803 "m.thread": {
1804 "latest_event": {
1805 "event_id": "$latest_event:example.com",
1806 "type": "m.room.message",
1807 "sender": "@bob:example.com",
1808 "origin_server_ts": 42,
1809 "content": {
1810 "body": "Hello to you too!",
1811 "msgtype": "m.text",
1812 }
1813 },
1814 "count": 2,
1815 "current_user_participated": true,
1816 }
1817 }
1818 }
1819 });
1820
1821 let raw = Raw::new(&event).unwrap().cast_unchecked();
1822
1823 let timeline_event = TimelineEvent::from_plaintext(raw);
1826 assert_matches!(timeline_event.thread_summary, ThreadSummaryStatus::Some(ThreadSummary { num_replies, latest_reply }) => {
1827 assert_eq!(num_replies, 2);
1828 assert_eq!(latest_reply.as_deref(), Some(event_id!("$latest_event:example.com")));
1829 });
1830
1831 assert!(timeline_event.bundled_latest_thread_event.is_some());
1832
1833 let serialized_timeline_item = json!({
1836 "kind": {
1837 "PlainText": {
1838 "event": event
1839 }
1840 }
1841 });
1842
1843 let timeline_event: TimelineEvent =
1844 serde_json::from_value(serialized_timeline_item).unwrap();
1845 assert_matches!(timeline_event.thread_summary, ThreadSummaryStatus::Unknown);
1846
1847 assert!(timeline_event.bundled_latest_thread_event.is_none());
1850 }
1851
1852 #[test]
1853 fn sync_timeline_event_deserialisation_migration_for_withheld() {
1854 let serialized = json!({
1871 "kind": {
1872 "UnableToDecrypt": {
1873 "event": {
1874 "content": {
1875 "algorithm": "m.megolm.v1.aes-sha2",
1876 "ciphertext": "AwgAEoABzL1JYhqhjW9jXrlT3M6H8mJ4qffYtOQOnPuAPNxsuG20oiD/Fnpv6jnQGhU6YbV9pNM+1mRnTvxW3CbWOPjLKqCWTJTc7Q0vDEVtYePg38ncXNcwMmfhgnNAoW9S7vNs8C003x3yUl6NeZ8bH+ci870BZL+kWM/lMl10tn6U7snNmSjnE3ckvRdO+11/R4//5VzFQpZdf4j036lNSls/WIiI67Fk9iFpinz9xdRVWJFVdrAiPFwb8L5xRZ8aX+e2JDMlc1eW8gk",
1877 "device_id": "SKCGPNUWAU",
1878 "sender_key": "Gim/c7uQdSXyrrUbmUOrBT6sMC0gO7QSLmOK6B7NOm0",
1879 "session_id": "hgLyeSqXfb8vc5AjQLsg6TSHVu0HJ7HZ4B6jgMvxkrs"
1880 },
1881 "event_id": "$xxxxx:example.org",
1882 "origin_server_ts": 2189,
1883 "room_id": "!someroom:example.com",
1884 "sender": "@carl:example.com",
1885 "type": "m.room.message"
1886 },
1887 "utd_info": {
1888 "reason": "MissingMegolmSession",
1889 "session_id": "session000"
1890 }
1891 }
1892 }
1893 });
1894
1895 let result = serde_json::from_value(serialized);
1896 assert!(result.is_ok());
1897
1898 let event: TimelineEvent = result.unwrap();
1900 assert_matches!(
1901 event.kind,
1902 TimelineEventKind::UnableToDecrypt { utd_info, .. }=> {
1903 assert_matches!(
1904 utd_info.reason,
1905 UnableToDecryptReason::MissingMegolmSession { withheld_code: None }
1906 );
1907 }
1908 )
1909 }
1910
1911 #[test]
1912 fn unable_to_decrypt_info_migration_for_withheld() {
1913 let old_format = json!({
1914 "reason": "MissingMegolmSession",
1915 "session_id": "session000"
1916 });
1917
1918 let deserialized = serde_json::from_value::<UnableToDecryptInfo>(old_format).unwrap();
1919 let session_id = Some("session000".to_owned());
1920
1921 assert_eq!(deserialized.session_id, session_id);
1922 assert_eq!(
1923 deserialized.reason,
1924 UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
1925 );
1926
1927 let new_format = json!({
1928 "session_id": "session000",
1929 "reason": {
1930 "MissingMegolmSession": {
1931 "withheld_code": null
1932 }
1933 }
1934 });
1935
1936 let deserialized = serde_json::from_value::<UnableToDecryptInfo>(new_format).unwrap();
1937
1938 assert_eq!(
1939 deserialized.reason,
1940 UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
1941 );
1942 assert_eq!(deserialized.session_id, session_id);
1943 }
1944
1945 #[test]
1946 fn unable_to_decrypt_reason_is_missing_room_key() {
1947 let reason = UnableToDecryptReason::MissingMegolmSession { withheld_code: None };
1948 assert!(reason.is_missing_room_key());
1949
1950 let reason = UnableToDecryptReason::MissingMegolmSession {
1951 withheld_code: Some(WithheldCode::Blacklisted),
1952 };
1953 assert!(!reason.is_missing_room_key());
1954
1955 let reason = UnableToDecryptReason::UnknownMegolmMessageIndex;
1956 assert!(reason.is_missing_room_key());
1957 }
1958
1959 #[test]
1960 fn snapshot_test_verification_level() {
1961 with_settings!({ prepend_module_to_snapshot => false }, {
1962 assert_json_snapshot!(VerificationLevel::VerificationViolation);
1963 assert_json_snapshot!(VerificationLevel::UnsignedDevice);
1964 assert_json_snapshot!(VerificationLevel::None(DeviceLinkProblem::InsecureSource));
1965 assert_json_snapshot!(VerificationLevel::None(DeviceLinkProblem::MissingDevice));
1966 assert_json_snapshot!(VerificationLevel::UnverifiedIdentity);
1967 });
1968 }
1969
1970 #[test]
1971 fn snapshot_test_verification_states() {
1972 with_settings!({ prepend_module_to_snapshot => false }, {
1973 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::UnsignedDevice));
1974 assert_json_snapshot!(VerificationState::Unverified(
1975 VerificationLevel::VerificationViolation
1976 ));
1977 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::None(
1978 DeviceLinkProblem::InsecureSource,
1979 )));
1980 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::None(
1981 DeviceLinkProblem::MissingDevice,
1982 )));
1983 assert_json_snapshot!(VerificationState::Verified);
1984 });
1985 }
1986
1987 #[test]
1988 fn snapshot_test_shield_states() {
1989 with_settings!({ prepend_module_to_snapshot => false }, {
1990 assert_json_snapshot!(ShieldState::None);
1991 assert_json_snapshot!(ShieldState::Red {
1992 code: ShieldStateCode::UnverifiedIdentity,
1993 message: "a message"
1994 });
1995 assert_json_snapshot!(ShieldState::Grey {
1996 code: ShieldStateCode::AuthenticityNotGuaranteed,
1997 message: "authenticity of this message cannot be guaranteed",
1998 });
1999 });
2000 }
2001
2002 #[test]
2003 fn snapshot_test_shield_codes() {
2004 with_settings!({ prepend_module_to_snapshot => false }, {
2005 assert_json_snapshot!(ShieldStateCode::AuthenticityNotGuaranteed);
2006 assert_json_snapshot!(ShieldStateCode::UnknownDevice);
2007 assert_json_snapshot!(ShieldStateCode::UnsignedDevice);
2008 assert_json_snapshot!(ShieldStateCode::UnverifiedIdentity);
2009 assert_json_snapshot!(ShieldStateCode::VerificationViolation);
2010 });
2011 }
2012
2013 #[test]
2014 fn snapshot_test_algorithm_info() {
2015 let mut map = BTreeMap::new();
2016 map.insert(DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned());
2017 map.insert(DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned());
2018 let info = AlgorithmInfo::MegolmV1AesSha2 {
2019 curve25519_key: "curvecurvecurve".into(),
2020 sender_claimed_keys: BTreeMap::from([
2021 (DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned()),
2022 (DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned()),
2023 ]),
2024 session_id: None,
2025 };
2026
2027 with_settings!({ prepend_module_to_snapshot => false }, {
2028 assert_json_snapshot!(info)
2029 });
2030 }
2031
2032 #[test]
2033 fn test_encryption_info_migration() {
2034 let old_format = json!({
2037 "sender": "@alice:localhost",
2038 "sender_device": "ABCDEFGH",
2039 "algorithm_info": {
2040 "MegolmV1AesSha2": {
2041 "curve25519_key": "curvecurvecurve",
2042 "sender_claimed_keys": {}
2043 }
2044 },
2045 "verification_state": "Verified",
2046 "session_id": "mysessionid76"
2047 });
2048
2049 let deserialized = serde_json::from_value::<EncryptionInfo>(old_format).unwrap();
2050 let expected_session_id = Some("mysessionid76".to_owned());
2051
2052 assert_let!(
2053 AlgorithmInfo::MegolmV1AesSha2 { session_id, .. } = deserialized.algorithm_info.clone()
2054 );
2055 assert_eq!(session_id, expected_session_id);
2056
2057 assert_json_snapshot!(deserialized);
2058 }
2059
2060 #[test]
2061 fn snapshot_test_encryption_info() {
2062 let info = EncryptionInfo {
2063 sender: user_id!("@alice:localhost").to_owned(),
2064 sender_device: Some(device_id!("ABCDEFGH").to_owned()),
2065 forwarder: None,
2066 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
2067 curve25519_key: "curvecurvecurve".into(),
2068 sender_claimed_keys: Default::default(),
2069 session_id: Some("mysessionid76".to_owned()),
2070 },
2071 verification_state: VerificationState::Verified,
2072 };
2073
2074 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
2075 assert_json_snapshot!(info)
2076 })
2077 }
2078
2079 #[test]
2080 fn snapshot_test_sync_timeline_event() {
2081 let room_event = TimelineEvent {
2082 kind: TimelineEventKind::Decrypted(DecryptedRoomEvent {
2083 event: Raw::new(&example_event()).unwrap().cast_unchecked(),
2084 encryption_info: Arc::new(EncryptionInfo {
2085 sender: user_id!("@sender:example.com").to_owned(),
2086 sender_device: Some(device_id!("ABCDEFGHIJ").to_owned()),
2087 forwarder: None,
2088 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
2089 curve25519_key: "xxx".to_owned(),
2090 sender_claimed_keys: BTreeMap::from([
2091 (
2092 DeviceKeyAlgorithm::Ed25519,
2093 "I3YsPwqMZQXHkSQbjFNEs7b529uac2xBpI83eN3LUXo".to_owned(),
2094 ),
2095 (
2096 DeviceKeyAlgorithm::Curve25519,
2097 "qzdW3F5IMPFl0HQgz5w/L5Oi/npKUFn8Um84acIHfPY".to_owned(),
2098 ),
2099 ]),
2100 session_id: Some("mysessionid112".to_owned()),
2101 },
2102 verification_state: VerificationState::Verified,
2103 }),
2104 unsigned_encryption_info: Some(BTreeMap::from([(
2105 UnsignedEventLocation::RelationsThreadLatestEvent,
2106 UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
2107 session_id: Some("xyz".to_owned()),
2108 reason: UnableToDecryptReason::MissingMegolmSession {
2109 withheld_code: Some(WithheldCode::Unverified),
2110 },
2111 }),
2112 )])),
2113 }),
2114 timestamp: Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))),
2115 push_actions: Default::default(),
2116 thread_summary: ThreadSummaryStatus::Some(ThreadSummary {
2117 num_replies: 2,
2118 latest_reply: None,
2119 }),
2120 bundled_latest_thread_event: None,
2121 };
2122
2123 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
2124 assert_json_snapshot! {
2127 serde_json::to_value(&room_event).unwrap(),
2128 }
2129 });
2130 }
2131
2132 #[test]
2133 fn test_from_bundled_latest_event_keeps_session_id() {
2134 let session_id = "hgLyeSqXfb8vc5AjQLsg6TSHVu0HJ7HZ4B6jgMvxkrs";
2135 let serialized = json!({
2136 "content": {
2137 "algorithm": "m.megolm.v1.aes-sha2",
2138 "ciphertext": "AwgAEoABzL1JYhqhjW9jXrlT3M6H8mJ4qffYtOQOnPuAPNxsuG20oiD/Fnpv6jnQGhU6YbV9pNM+1mRnTvxW3CbWOPjLKqCWTJTc7Q0vDEVtYePg38ncXNcwMmfhgnNAoW9S7vNs8C003x3yUl6NeZ8bH+ci870BZL+kWM/lMl10tn6U7snNmSjnE3ckvRdO+11/R4//5VzFQpZdf4j036lNSls/WIiI67Fk9iFpinz9xdRVWJFVdrAiPFwb8L5xRZ8aX+e2JDMlc1eW8gk",
2139 "device_id": "SKCGPNUWAU",
2140 "sender_key": "Gim/c7uQdSXyrrUbmUOrBT6sMC0gO7QSLmOK6B7NOm0",
2141 "session_id": session_id,
2142 },
2143 "event_id": "$xxxxx:example.org",
2144 "origin_server_ts": 2189,
2145 "room_id": "!someroom:example.com",
2146 "sender": "@carl:example.com",
2147 "type": "m.room.encrypted"
2148 });
2149 let json = serialized.to_string();
2150 let value = Raw::<AnySyncTimelineEvent>::from_json_string(json).unwrap();
2151
2152 let kind = TimelineEventKind::UnableToDecrypt {
2153 event: value.clone(),
2154 utd_info: UnableToDecryptInfo {
2155 session_id: None,
2156 reason: UnableToDecryptReason::Unknown,
2157 },
2158 };
2159 let result = TimelineEvent::from_bundled_latest_event(
2160 &kind,
2161 Some(value.cast_unchecked()),
2162 MilliSecondsSinceUnixEpoch::now(),
2163 )
2164 .expect("Could not get bundled latest event");
2165
2166 assert_let!(TimelineEventKind::UnableToDecrypt { utd_info, .. } = result.kind);
2167 assert!(utd_info.session_id.is_some());
2168 assert_eq!(utd_info.session_id.unwrap(), session_id);
2169 }
2170}