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)]
491pub struct TimelineEvent {
492 pub kind: TimelineEventKind,
494
495 pub timestamp: Option<MilliSecondsSinceUnixEpoch>,
501
502 #[serde(skip_serializing_if = "skip_serialize_push_actions")]
507 push_actions: Option<Vec<Action>>,
508
509 #[serde(default, skip_serializing_if = "ThreadSummaryStatus::is_unknown")]
511 pub thread_summary: ThreadSummaryStatus,
512
513 #[serde(skip)]
518 pub bundled_latest_thread_event: Option<Box<TimelineEvent>>,
519}
520
521fn skip_serialize_push_actions(push_actions: &Option<Vec<Action>>) -> bool {
523 push_actions.as_ref().is_none_or(|v| v.is_empty())
524}
525
526#[cfg(not(feature = "test-send-sync"))]
528unsafe impl Send for TimelineEvent {}
529
530#[cfg(not(feature = "test-send-sync"))]
532unsafe impl Sync for TimelineEvent {}
533
534#[cfg(feature = "test-send-sync")]
535#[test]
536fn test_send_sync_for_sync_timeline_event() {
538 fn assert_send_sync<T: crate::SendOutsideWasm + crate::SyncOutsideWasm>() {}
539
540 assert_send_sync::<TimelineEvent>();
541}
542
543impl TimelineEvent {
544 pub fn from_plaintext(event: Raw<AnySyncTimelineEvent>) -> Self {
549 Self::from_plaintext_with_max_timestamp(event, MilliSecondsSinceUnixEpoch::now())
550 }
551
552 pub fn from_plaintext_with_max_timestamp(
554 event: Raw<AnySyncTimelineEvent>,
555 max_timestamp: MilliSecondsSinceUnixEpoch,
556 ) -> Self {
557 Self::new(TimelineEventKind::PlainText { event }, None, max_timestamp)
558 }
559
560 pub fn from_decrypted(
562 decrypted: DecryptedRoomEvent,
563 push_actions: Option<Vec<Action>>,
564 ) -> Self {
565 Self::from_decrypted_with_max_timestamp(
566 decrypted,
567 push_actions,
568 MilliSecondsSinceUnixEpoch::now(),
569 )
570 }
571
572 pub fn from_decrypted_with_max_timestamp(
574 decrypted: DecryptedRoomEvent,
575 push_actions: Option<Vec<Action>>,
576 max_timestamp: MilliSecondsSinceUnixEpoch,
577 ) -> Self {
578 Self::new(TimelineEventKind::Decrypted(decrypted), push_actions, max_timestamp)
579 }
580
581 pub fn from_utd(event: Raw<AnySyncTimelineEvent>, utd_info: UnableToDecryptInfo) -> Self {
584 Self::from_utd_with_max_timestamp(event, utd_info, MilliSecondsSinceUnixEpoch::now())
585 }
586
587 pub fn from_utd_with_max_timestamp(
589 event: Raw<AnySyncTimelineEvent>,
590 utd_info: UnableToDecryptInfo,
591 max_timestamp: MilliSecondsSinceUnixEpoch,
592 ) -> Self {
593 Self::new(TimelineEventKind::UnableToDecrypt { event, utd_info }, None, max_timestamp)
594 }
595
596 fn new(
601 kind: TimelineEventKind,
602 push_actions: Option<Vec<Action>>,
603 max_timestamp: MilliSecondsSinceUnixEpoch,
604 ) -> Self {
605 let raw = kind.raw();
606
607 let (thread_summary, latest_thread_event) = extract_bundled_thread_summary(raw);
608
609 let bundled_latest_thread_event =
610 Self::from_bundled_latest_event(&kind, latest_thread_event, max_timestamp);
611
612 let timestamp = extract_timestamp(raw, max_timestamp);
613
614 Self { kind, push_actions, timestamp, thread_summary, bundled_latest_thread_event }
615 }
616
617 pub fn to_decrypted(
625 &self,
626 decrypted: DecryptedRoomEvent,
627 push_actions: Option<Vec<Action>>,
628 ) -> Self {
629 debug_assert!(
630 matches!(self.kind, TimelineEventKind::Decrypted(_)).not(),
631 "`TimelineEvent::to_decrypted` has been called on an already decrypted `TimelineEvent`."
632 );
633
634 Self {
635 kind: TimelineEventKind::Decrypted(decrypted),
636 timestamp: self.timestamp,
637 push_actions,
638 thread_summary: self.thread_summary.clone(),
639 bundled_latest_thread_event: self.bundled_latest_thread_event.clone(),
640 }
641 }
642
643 pub fn to_utd(&self, utd_info: UnableToDecryptInfo) -> Self {
651 debug_assert!(
652 matches!(self.kind, TimelineEventKind::UnableToDecrypt { .. }).not(),
653 "`TimelineEvent::to_utd` has been called on an already UTD `TimelineEvent`."
654 );
655
656 Self {
657 kind: TimelineEventKind::UnableToDecrypt { event: self.raw().clone(), utd_info },
658 timestamp: self.timestamp,
659 push_actions: None,
660 thread_summary: self.thread_summary.clone(),
661 bundled_latest_thread_event: self.bundled_latest_thread_event.clone(),
662 }
663 }
664
665 fn from_bundled_latest_event(
669 kind: &TimelineEventKind,
670 latest_event: Option<Raw<AnySyncMessageLikeEvent>>,
671 max_timestamp: MilliSecondsSinceUnixEpoch,
672 ) -> Option<Box<Self>> {
673 let latest_event = latest_event?;
674
675 match kind {
676 TimelineEventKind::Decrypted(decrypted) => {
677 if let Some(unsigned_decryption_result) =
678 decrypted.unsigned_encryption_info.as_ref().and_then(|unsigned_map| {
679 unsigned_map.get(&UnsignedEventLocation::RelationsThreadLatestEvent)
680 })
681 {
682 match unsigned_decryption_result {
683 UnsignedDecryptionResult::Decrypted(encryption_info) => {
684 return Some(Box::new(
687 TimelineEvent::from_decrypted_with_max_timestamp(
688 DecryptedRoomEvent {
689 event: latest_event.cast_unchecked(),
692 encryption_info: encryption_info.clone(),
693 unsigned_encryption_info: None,
698 },
699 None,
700 max_timestamp,
701 ),
702 ));
703 }
704
705 UnsignedDecryptionResult::UnableToDecrypt(utd_info) => {
706 return Some(Box::new(TimelineEvent::from_utd_with_max_timestamp(
708 latest_event.cast(),
709 utd_info.clone(),
710 max_timestamp,
711 )));
712 }
713 }
714 }
715 }
716
717 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => {
718 }
720 }
721
722 match latest_event.get_field::<MessageLikeEventType>("type") {
723 Ok(None) => {
724 let event_id = latest_event.get_field::<OwnedEventId>("event_id").ok().flatten();
725 warn!(
726 ?event_id,
727 "couldn't deserialize bundled latest thread event: missing `type` field \
728 in bundled latest thread event"
729 );
730 None
731 }
732
733 Ok(Some(MessageLikeEventType::RoomEncrypted)) => {
734 let session_id = if let Some(content) =
738 latest_event.get_field::<EncryptedEventScheme>("content").ok().flatten()
739 {
740 match content {
741 EncryptedEventScheme::MegolmV1AesSha2(content) => Some(content.session_id),
742 _ => None,
743 }
744 } else {
745 None
746 };
747 Some(Box::new(TimelineEvent::from_utd_with_max_timestamp(
748 latest_event.cast(),
749 UnableToDecryptInfo { session_id, reason: UnableToDecryptReason::Unknown },
750 max_timestamp,
751 )))
752 }
753
754 Ok(_) => Some(Box::new(TimelineEvent::from_plaintext_with_max_timestamp(
755 latest_event.cast(),
756 max_timestamp,
757 ))),
758
759 Err(err) => {
760 let event_id = latest_event.get_field::<OwnedEventId>("event_id").ok().flatten();
761 warn!(?event_id, "couldn't deserialize bundled latest thread event's type: {err}");
762 None
763 }
764 }
765 }
766
767 pub fn push_actions(&self) -> Option<&[Action]> {
772 self.push_actions.as_deref()
773 }
774
775 pub fn set_push_actions(&mut self, push_actions: Vec<Action>) {
777 self.push_actions = Some(push_actions);
778 }
779
780 pub fn event_id(&self) -> Option<OwnedEventId> {
783 self.kind.event_id()
784 }
785
786 pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
789 self.kind.raw()
790 }
791
792 pub fn replace_raw(&mut self, replacement: Raw<AnyTimelineEvent>) {
794 match &mut self.kind {
795 TimelineEventKind::Decrypted(decrypted) => decrypted.event = replacement,
796 TimelineEventKind::UnableToDecrypt { event, .. }
797 | TimelineEventKind::PlainText { event } => {
798 *event = replacement.cast();
801 }
802 }
803 }
804
805 pub fn timestamp(&self) -> Option<MilliSecondsSinceUnixEpoch> {
814 self.timestamp.or_else(|| {
815 warn!("`TimelineEvent::timestamp` is parsing the raw event to extract the `timestamp`");
816
817 extract_timestamp(self.raw(), MilliSecondsSinceUnixEpoch::now())
818 })
819 }
820
821 pub fn timestamp_raw(&self) -> Option<MilliSecondsSinceUnixEpoch> {
823 self.timestamp
824 }
825
826 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
829 self.kind.encryption_info()
830 }
831
832 pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
835 self.kind.into_raw()
836 }
837}
838
839impl<'de> Deserialize<'de> for TimelineEvent {
840 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
848 where
849 D: serde::Deserializer<'de>,
850 {
851 use serde_json::{Map, Value};
852
853 let value = Map::<String, Value>::deserialize(deserializer)?;
855
856 if value.contains_key("event") {
858 let v0: SyncTimelineEventDeserializationHelperV0 =
859 serde_json::from_value(Value::Object(value)).map_err(|e| {
860 serde::de::Error::custom(format!(
861 "Unable to deserialize V0-format TimelineEvent: {e}",
862 ))
863 })?;
864 Ok(v0.into())
865 }
866 else {
868 let v1: SyncTimelineEventDeserializationHelperV1 =
869 serde_json::from_value(Value::Object(value)).map_err(|e| {
870 serde::de::Error::custom(format!(
871 "Unable to deserialize V1-format TimelineEvent: {e}",
872 ))
873 })?;
874 Ok(v1.into())
875 }
876 }
877}
878
879#[derive(Clone, Serialize, Deserialize)]
881pub enum TimelineEventKind {
882 Decrypted(DecryptedRoomEvent),
884
885 UnableToDecrypt {
887 event: Raw<AnySyncTimelineEvent>,
891
892 utd_info: UnableToDecryptInfo,
894 },
895
896 PlainText {
898 event: Raw<AnySyncTimelineEvent>,
902 },
903}
904
905impl TimelineEventKind {
906 pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
909 match self {
910 TimelineEventKind::Decrypted(d) => d.event.cast_ref(),
916 TimelineEventKind::UnableToDecrypt { event, .. } => event,
917 TimelineEventKind::PlainText { event } => event,
918 }
919 }
920
921 pub fn event_id(&self) -> Option<OwnedEventId> {
924 self.raw().get_field::<OwnedEventId>("event_id").ok().flatten()
925 }
926
927 pub fn is_utd(&self) -> bool {
929 matches!(self, TimelineEventKind::UnableToDecrypt { .. })
930 }
931
932 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
935 match self {
936 TimelineEventKind::Decrypted(d) => Some(&d.encryption_info),
937 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => None,
938 }
939 }
940
941 pub fn unsigned_encryption_map(
944 &self,
945 ) -> Option<&BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>> {
946 match self {
947 TimelineEventKind::Decrypted(d) => d.unsigned_encryption_info.as_ref(),
948 TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => None,
949 }
950 }
951
952 pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
955 match self {
956 TimelineEventKind::Decrypted(d) => d.event.cast(),
962 TimelineEventKind::UnableToDecrypt { event, .. } => event,
963 TimelineEventKind::PlainText { event } => event,
964 }
965 }
966
967 pub fn session_id(&self) -> Option<&str> {
970 match self {
971 TimelineEventKind::Decrypted(decrypted_room_event) => {
972 decrypted_room_event.encryption_info.session_id()
973 }
974 TimelineEventKind::UnableToDecrypt { utd_info, .. } => utd_info.session_id.as_deref(),
975 TimelineEventKind::PlainText { .. } => None,
976 }
977 }
978
979 pub fn event_type(&self) -> Option<String> {
984 self.raw().get_field("type").ok().flatten()
985 }
986}
987
988#[cfg(not(tarpaulin_include))]
989impl fmt::Debug for TimelineEventKind {
990 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
991 match &self {
992 Self::PlainText { event } => f
993 .debug_struct("TimelineEventKind::PlainText")
994 .field("event", &DebugRawEvent(event))
995 .finish(),
996
997 Self::UnableToDecrypt { event, utd_info } => f
998 .debug_struct("TimelineEventKind::UnableToDecrypt")
999 .field("event", &DebugRawEvent(event))
1000 .field("utd_info", &utd_info)
1001 .finish(),
1002
1003 Self::Decrypted(decrypted) => {
1004 f.debug_tuple("TimelineEventKind::Decrypted").field(decrypted).finish()
1005 }
1006 }
1007 }
1008}
1009
1010#[derive(Clone, Serialize, Deserialize)]
1011pub struct DecryptedRoomEvent {
1013 pub event: Raw<AnyTimelineEvent>,
1021
1022 pub encryption_info: Arc<EncryptionInfo>,
1024
1025 #[serde(skip_serializing_if = "Option::is_none")]
1030 pub unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
1031}
1032
1033#[cfg(not(tarpaulin_include))]
1034impl fmt::Debug for DecryptedRoomEvent {
1035 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1036 let DecryptedRoomEvent { event, encryption_info, unsigned_encryption_info } = self;
1037
1038 f.debug_struct("DecryptedRoomEvent")
1039 .field("event", &DebugRawEvent(event))
1040 .field("encryption_info", encryption_info)
1041 .maybe_field("unsigned_encryption_info", unsigned_encryption_info)
1042 .finish()
1043 }
1044}
1045
1046#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
1048pub enum UnsignedEventLocation {
1049 RelationsReplace,
1052 RelationsThreadLatestEvent,
1055}
1056
1057impl UnsignedEventLocation {
1058 pub fn find_mut<'a>(&self, unsigned: &'a mut JsonObject) -> Option<&'a mut serde_json::Value> {
1065 let relations = unsigned.get_mut("m.relations")?.as_object_mut()?;
1066
1067 match self {
1068 Self::RelationsReplace => relations.get_mut("m.replace"),
1069 Self::RelationsThreadLatestEvent => {
1070 relations.get_mut("m.thread")?.as_object_mut()?.get_mut("latest_event")
1071 }
1072 }
1073 }
1074}
1075
1076#[derive(Debug, Clone, Serialize, Deserialize)]
1078pub enum UnsignedDecryptionResult {
1079 Decrypted(Arc<EncryptionInfo>),
1081 UnableToDecrypt(UnableToDecryptInfo),
1083}
1084
1085impl UnsignedDecryptionResult {
1086 pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
1089 match self {
1090 Self::Decrypted(info) => Some(info),
1091 Self::UnableToDecrypt(_) => None,
1092 }
1093 }
1094}
1095
1096#[derive(Debug, Clone, Serialize, Deserialize)]
1098pub struct UnableToDecryptInfo {
1099 #[serde(skip_serializing_if = "Option::is_none")]
1102 pub session_id: Option<String>,
1103
1104 #[serde(default = "unknown_utd_reason", deserialize_with = "deserialize_utd_reason")]
1106 pub reason: UnableToDecryptReason,
1107}
1108
1109fn unknown_utd_reason() -> UnableToDecryptReason {
1110 UnableToDecryptReason::Unknown
1111}
1112
1113pub fn deserialize_utd_reason<'de, D>(d: D) -> Result<UnableToDecryptReason, D::Error>
1116where
1117 D: serde::Deserializer<'de>,
1118{
1119 let v: serde_json::Value = Deserialize::deserialize(d)?;
1121 if v.as_str().is_some_and(|s| s == "MissingMegolmSession") {
1124 return Ok(UnableToDecryptReason::MissingMegolmSession { withheld_code: None });
1125 }
1126 serde_json::from_value::<UnableToDecryptReason>(v).map_err(serde::de::Error::custom)
1129}
1130
1131#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1133pub enum UnableToDecryptReason {
1134 #[doc(hidden)]
1137 Unknown,
1138
1139 MalformedEncryptedEvent,
1143
1144 MissingMegolmSession {
1147 withheld_code: Option<WithheldCode>,
1150 },
1151
1152 UnknownMegolmMessageIndex,
1155
1156 MegolmDecryptionFailure,
1163
1164 PayloadDeserializationFailure,
1166
1167 MismatchedIdentityKeys,
1171
1172 SenderIdentityNotTrusted(VerificationLevel),
1176
1177 #[cfg(feature = "experimental-encrypted-state-events")]
1180 StateKeyVerificationFailed,
1181}
1182
1183impl UnableToDecryptReason {
1184 pub fn is_missing_room_key(&self) -> bool {
1187 matches!(
1190 self,
1191 Self::MissingMegolmSession { withheld_code: None } | Self::UnknownMegolmMessageIndex
1192 )
1193 }
1194}
1195
1196#[derive(
1200 Clone,
1201 PartialEq,
1202 Eq,
1203 Hash,
1204 AsStrAsRefStr,
1205 AsRefStr,
1206 FromString,
1207 DebugAsRefStr,
1208 SerializeAsRefStr,
1209 DeserializeFromCowStr,
1210)]
1211pub enum WithheldCode {
1212 #[ruma_enum(rename = "m.blacklisted")]
1214 Blacklisted,
1215
1216 #[ruma_enum(rename = "m.unverified")]
1218 Unverified,
1219
1220 #[ruma_enum(rename = "m.unauthorised")]
1224 Unauthorised,
1225
1226 #[ruma_enum(rename = "m.unavailable")]
1229 Unavailable,
1230
1231 #[ruma_enum(rename = "m.no_olm")]
1235 NoOlm,
1236
1237 #[ruma_enum(rename = "io.element.msc4268.history_not_shared", alias = "m.history_not_shared")]
1242 HistoryNotShared,
1243
1244 #[doc(hidden)]
1245 _Custom(PrivOwnedStr),
1246}
1247
1248impl fmt::Display for WithheldCode {
1249 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1250 let string = match self {
1251 WithheldCode::Blacklisted => "The sender has blocked you.",
1252 WithheldCode::Unverified => "The sender has disabled encrypting to unverified devices.",
1253 WithheldCode::Unauthorised => "You are not authorised to read the message.",
1254 WithheldCode::Unavailable => "The requested key was not found.",
1255 WithheldCode::NoOlm => "Unable to establish a secure channel.",
1256 WithheldCode::HistoryNotShared => "The sender disabled sharing encrypted history.",
1257 _ => self.as_str(),
1258 };
1259
1260 f.write_str(string)
1261 }
1262}
1263
1264#[doc(hidden)]
1268#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1269pub struct PrivOwnedStr(pub Box<str>);
1270
1271#[cfg(not(tarpaulin_include))]
1272impl fmt::Debug for PrivOwnedStr {
1273 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1274 self.0.fmt(f)
1275 }
1276}
1277
1278#[derive(Debug, Deserialize)]
1283struct SyncTimelineEventDeserializationHelperV1 {
1284 kind: TimelineEventKind,
1286
1287 #[serde(default)]
1290 timestamp: Option<MilliSecondsSinceUnixEpoch>,
1291
1292 #[serde(default)]
1294 push_actions: Vec<Action>,
1295
1296 #[serde(default)]
1298 thread_summary: ThreadSummaryStatus,
1299}
1300
1301impl From<SyncTimelineEventDeserializationHelperV1> for TimelineEvent {
1302 fn from(value: SyncTimelineEventDeserializationHelperV1) -> Self {
1303 let SyncTimelineEventDeserializationHelperV1 {
1304 kind,
1305 timestamp,
1306 push_actions,
1307 thread_summary,
1308 } = value;
1309
1310 TimelineEvent {
1319 kind,
1320 timestamp,
1321 push_actions: Some(push_actions),
1322 thread_summary,
1323 bundled_latest_thread_event: None,
1325 }
1326 }
1327}
1328
1329#[derive(Deserialize)]
1331struct SyncTimelineEventDeserializationHelperV0 {
1332 event: Raw<AnySyncTimelineEvent>,
1334
1335 encryption_info: Option<Arc<EncryptionInfo>>,
1339
1340 #[serde(default)]
1342 push_actions: Vec<Action>,
1343
1344 unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
1349}
1350
1351impl From<SyncTimelineEventDeserializationHelperV0> for TimelineEvent {
1352 fn from(value: SyncTimelineEventDeserializationHelperV0) -> Self {
1353 let SyncTimelineEventDeserializationHelperV0 {
1354 event,
1355 encryption_info,
1356 push_actions,
1357 unsigned_encryption_info,
1358 } = value;
1359
1360 let timestamp = None;
1367
1368 let kind = match encryption_info {
1369 Some(encryption_info) => {
1370 TimelineEventKind::Decrypted(DecryptedRoomEvent {
1371 event: event.cast_unchecked(),
1378 encryption_info,
1379 unsigned_encryption_info,
1380 })
1381 }
1382
1383 None => TimelineEventKind::PlainText { event },
1384 };
1385
1386 TimelineEvent {
1387 kind,
1388 timestamp,
1389 push_actions: Some(push_actions),
1390 thread_summary: ThreadSummaryStatus::Unknown,
1392 bundled_latest_thread_event: None,
1394 }
1395 }
1396}
1397
1398#[derive(Debug, Clone, PartialEq)]
1400pub enum ToDeviceUnableToDecryptReason {
1401 DecryptionFailure,
1404
1405 UnverifiedSenderDevice,
1409
1410 NoOlmMachine,
1413
1414 EncryptionIsDisabled,
1416}
1417
1418#[derive(Clone, Debug)]
1420pub struct ToDeviceUnableToDecryptInfo {
1421 pub reason: ToDeviceUnableToDecryptReason,
1423}
1424
1425#[derive(Clone, Debug)]
1427pub enum ProcessedToDeviceEvent {
1428 Decrypted {
1431 raw: Raw<AnyToDeviceEvent>,
1433 encryption_info: EncryptionInfo,
1435 },
1436
1437 UnableToDecrypt {
1439 encrypted_event: Raw<AnyToDeviceEvent>,
1440 utd_info: ToDeviceUnableToDecryptInfo,
1441 },
1442
1443 PlainText(Raw<AnyToDeviceEvent>),
1445
1446 Invalid(Raw<AnyToDeviceEvent>),
1450}
1451
1452impl ProcessedToDeviceEvent {
1453 pub fn to_raw(&self) -> Raw<AnyToDeviceEvent> {
1456 match self {
1457 ProcessedToDeviceEvent::Decrypted { raw, .. } => raw.clone(),
1458 ProcessedToDeviceEvent::UnableToDecrypt { encrypted_event, .. } => {
1459 encrypted_event.clone()
1460 }
1461 ProcessedToDeviceEvent::PlainText(event) => event.clone(),
1462 ProcessedToDeviceEvent::Invalid(event) => event.clone(),
1463 }
1464 }
1465
1466 pub fn as_raw(&self) -> &Raw<AnyToDeviceEvent> {
1468 match self {
1469 ProcessedToDeviceEvent::Decrypted { raw, .. } => raw,
1470 ProcessedToDeviceEvent::UnableToDecrypt { encrypted_event, .. } => encrypted_event,
1471 ProcessedToDeviceEvent::PlainText(event) => event,
1472 ProcessedToDeviceEvent::Invalid(event) => event,
1473 }
1474 }
1475}
1476
1477#[cfg(test)]
1478mod tests {
1479 use std::{collections::BTreeMap, sync::Arc};
1480
1481 use assert_matches::assert_matches;
1482 use assert_matches2::assert_let;
1483 use insta::{assert_json_snapshot, with_settings};
1484 use ruma::{
1485 DeviceKeyAlgorithm, MilliSecondsSinceUnixEpoch, UInt, device_id, event_id,
1486 events::{AnySyncTimelineEvent, room::message::RoomMessageEventContent},
1487 serde::Raw,
1488 user_id,
1489 };
1490 use serde::Deserialize;
1491 use serde_json::json;
1492
1493 use super::{
1494 AlgorithmInfo, DecryptedRoomEvent, DeviceLinkProblem, EncryptionInfo, ShieldState,
1495 ShieldStateCode, TimelineEvent, TimelineEventKind, UnableToDecryptInfo,
1496 UnableToDecryptReason, UnsignedDecryptionResult, UnsignedEventLocation, VerificationLevel,
1497 VerificationState, WithheldCode,
1498 };
1499 use crate::deserialized_responses::{ThreadSummary, ThreadSummaryStatus};
1500
1501 fn example_event() -> serde_json::Value {
1502 json!({
1503 "content": RoomMessageEventContent::text_plain("secret"),
1504 "type": "m.room.message",
1505 "event_id": "$xxxxx:example.org",
1506 "room_id": "!someroom:example.com",
1507 "origin_server_ts": 2189,
1508 "sender": "@carl:example.com",
1509 })
1510 }
1511
1512 #[test]
1513 fn sync_timeline_debug_content() {
1514 let room_event =
1515 TimelineEvent::from_plaintext(Raw::new(&example_event()).unwrap().cast_unchecked());
1516 let debug_s = format!("{room_event:?}");
1517 assert!(
1518 !debug_s.contains("secret"),
1519 "Debug representation contains event content!\n{debug_s}"
1520 );
1521 }
1522
1523 #[test]
1524 fn old_verification_state_to_new_migration() {
1525 #[derive(Deserialize)]
1526 struct State {
1527 state: VerificationState,
1528 }
1529
1530 let state = json!({
1531 "state": "Trusted",
1532 });
1533 let deserialized: State =
1534 serde_json::from_value(state).expect("We can deserialize the old trusted value");
1535 assert_eq!(deserialized.state, VerificationState::Verified);
1536
1537 let state = json!({
1538 "state": "UnknownDevice",
1539 });
1540
1541 let deserialized: State =
1542 serde_json::from_value(state).expect("We can deserialize the old unknown device value");
1543
1544 assert_eq!(
1545 deserialized.state,
1546 VerificationState::Unverified(VerificationLevel::None(
1547 DeviceLinkProblem::MissingDevice
1548 ))
1549 );
1550
1551 let state = json!({
1552 "state": "Untrusted",
1553 });
1554 let deserialized: State =
1555 serde_json::from_value(state).expect("We can deserialize the old trusted value");
1556
1557 assert_eq!(
1558 deserialized.state,
1559 VerificationState::Unverified(VerificationLevel::UnsignedDevice)
1560 );
1561 }
1562
1563 #[test]
1564 fn test_verification_level_deserializes() {
1565 #[derive(Deserialize)]
1567 struct Container {
1568 verification_level: VerificationLevel,
1569 }
1570 let container = json!({ "verification_level": "VerificationViolation" });
1571
1572 let deserialized: Container = serde_json::from_value(container)
1574 .expect("We can deserialize the old PreviouslyVerified value");
1575
1576 assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1578 }
1579
1580 #[test]
1581 fn test_verification_level_deserializes_from_old_previously_verified_value() {
1582 #[derive(Deserialize)]
1584 struct Container {
1585 verification_level: VerificationLevel,
1586 }
1587 let container = json!({ "verification_level": "PreviouslyVerified" });
1588
1589 let deserialized: Container = serde_json::from_value(container)
1591 .expect("We can deserialize the old PreviouslyVerified value");
1592
1593 assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1595 }
1596
1597 #[test]
1598 fn test_shield_state_code_deserializes() {
1599 #[derive(Deserialize)]
1601 struct Container {
1602 shield_state_code: ShieldStateCode,
1603 }
1604 let container = json!({ "shield_state_code": "VerificationViolation" });
1605
1606 let deserialized: Container = serde_json::from_value(container)
1608 .expect("We can deserialize the old PreviouslyVerified value");
1609
1610 assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1612 }
1613
1614 #[test]
1615 fn test_shield_state_code_deserializes_from_old_previously_verified_value() {
1616 #[derive(Deserialize)]
1618 struct Container {
1619 shield_state_code: ShieldStateCode,
1620 }
1621 let container = json!({ "shield_state_code": "PreviouslyVerified" });
1622
1623 let deserialized: Container = serde_json::from_value(container)
1625 .expect("We can deserialize the old PreviouslyVerified value");
1626
1627 assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1629 }
1630
1631 #[test]
1632 fn sync_timeline_event_serialisation() {
1633 let room_event = TimelineEvent {
1634 kind: TimelineEventKind::Decrypted(DecryptedRoomEvent {
1635 event: Raw::new(&example_event()).unwrap().cast_unchecked(),
1636 encryption_info: Arc::new(EncryptionInfo {
1637 sender: user_id!("@sender:example.com").to_owned(),
1638 sender_device: None,
1639 forwarder: None,
1640 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
1641 curve25519_key: "xxx".to_owned(),
1642 sender_claimed_keys: Default::default(),
1643 session_id: Some("xyz".to_owned()),
1644 },
1645 verification_state: VerificationState::Verified,
1646 }),
1647 unsigned_encryption_info: Some(BTreeMap::from([(
1648 UnsignedEventLocation::RelationsReplace,
1649 UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
1650 session_id: Some("xyz".to_owned()),
1651 reason: UnableToDecryptReason::MalformedEncryptedEvent,
1652 }),
1653 )])),
1654 }),
1655 timestamp: Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))),
1656 push_actions: Default::default(),
1657 thread_summary: ThreadSummaryStatus::Unknown,
1658 bundled_latest_thread_event: None,
1659 };
1660
1661 let serialized = serde_json::to_value(&room_event).unwrap();
1662
1663 assert_eq!(
1665 serialized,
1666 json!({
1667 "kind": {
1668 "Decrypted": {
1669 "event": {
1670 "content": {"body": "secret", "msgtype": "m.text"},
1671 "event_id": "$xxxxx:example.org",
1672 "origin_server_ts": 2189,
1673 "room_id": "!someroom:example.com",
1674 "sender": "@carl:example.com",
1675 "type": "m.room.message",
1676 },
1677 "encryption_info": {
1678 "sender": "@sender:example.com",
1679 "sender_device": null,
1680 "forwarder": null,
1681 "algorithm_info": {
1682 "MegolmV1AesSha2": {
1683 "curve25519_key": "xxx",
1684 "sender_claimed_keys": {},
1685 "session_id": "xyz",
1686 }
1687 },
1688 "verification_state": "Verified",
1689 },
1690 "unsigned_encryption_info": {
1691 "RelationsReplace": {"UnableToDecrypt": {
1692 "session_id": "xyz",
1693 "reason": "MalformedEncryptedEvent",
1694 }}
1695 }
1696 }
1697 },
1698 "timestamp": 2189,
1699 })
1700 );
1701
1702 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1704 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1705 assert_matches!(
1706 event.encryption_info().unwrap().algorithm_info,
1707 AlgorithmInfo::MegolmV1AesSha2 { .. }
1708 );
1709 assert_eq!(event.timestamp(), Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))));
1710 assert_eq!(event.timestamp(), event.timestamp_raw());
1711
1712 let serialized = json!({
1714 "event": {
1715 "content": {"body": "secret", "msgtype": "m.text"},
1716 "event_id": "$xxxxx:example.org",
1717 "origin_server_ts": 2189,
1718 "room_id": "!someroom:example.com",
1719 "sender": "@carl:example.com",
1720 "type": "m.room.message",
1721 },
1722 "encryption_info": {
1723 "sender": "@sender:example.com",
1724 "sender_device": null,
1725 "algorithm_info": {
1726 "MegolmV1AesSha2": {
1727 "curve25519_key": "xxx",
1728 "sender_claimed_keys": {}
1729 }
1730 },
1731 "verification_state": "Verified",
1732 },
1733 });
1734 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1735 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1736 assert_matches!(
1737 event.encryption_info().unwrap().algorithm_info,
1738 AlgorithmInfo::MegolmV1AesSha2 { session_id: None, .. }
1739 );
1740 assert_eq!(event.timestamp(), Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))));
1741 assert!(event.timestamp_raw().is_none());
1742
1743 let serialized = json!({
1746 "event": {
1747 "content": {"body": "secret", "msgtype": "m.text"},
1748 "event_id": "$xxxxx:example.org",
1749 "origin_server_ts": 2189,
1750 "room_id": "!someroom:example.com",
1751 "sender": "@carl:example.com",
1752 "type": "m.room.message",
1753 },
1754 "encryption_info": {
1755 "sender": "@sender:example.com",
1756 "sender_device": null,
1757 "algorithm_info": {
1758 "MegolmV1AesSha2": {
1759 "curve25519_key": "xxx",
1760 "sender_claimed_keys": {}
1761 }
1762 },
1763 "verification_state": "Verified",
1764 },
1765 "unsigned_encryption_info": {
1766 "RelationsReplace": {"UnableToDecrypt": {"session_id": "xyz"}}
1767 }
1768 });
1769 let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1770 assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1771 assert_matches!(
1772 event.encryption_info().unwrap().algorithm_info,
1773 AlgorithmInfo::MegolmV1AesSha2 { .. }
1774 );
1775 assert_eq!(event.timestamp(), Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))));
1776 assert!(event.timestamp_raw().is_none());
1777 assert_matches!(event.kind, TimelineEventKind::Decrypted(decrypted) => {
1778 assert_matches!(decrypted.unsigned_encryption_info, Some(map) => {
1779 assert_eq!(map.len(), 1);
1780 let (location, result) = map.into_iter().next().unwrap();
1781 assert_eq!(location, UnsignedEventLocation::RelationsReplace);
1782 assert_matches!(result, UnsignedDecryptionResult::UnableToDecrypt(utd_info) => {
1783 assert_eq!(utd_info.session_id, Some("xyz".to_owned()));
1784 assert_eq!(utd_info.reason, UnableToDecryptReason::Unknown);
1785 })
1786 });
1787 });
1788 }
1789
1790 #[test]
1791 fn test_creating_or_deserializing_an_event_extracts_summary() {
1792 let event = json!({
1793 "event_id": "$eid:example.com",
1794 "type": "m.room.message",
1795 "sender": "@alice:example.com",
1796 "origin_server_ts": 42,
1797 "content": {
1798 "body": "Hello, world!",
1799 },
1800 "unsigned": {
1801 "m.relations": {
1802 "m.thread": {
1803 "latest_event": {
1804 "event_id": "$latest_event:example.com",
1805 "type": "m.room.message",
1806 "sender": "@bob:example.com",
1807 "origin_server_ts": 42,
1808 "content": {
1809 "body": "Hello to you too!",
1810 "msgtype": "m.text",
1811 }
1812 },
1813 "count": 2,
1814 "current_user_participated": true,
1815 }
1816 }
1817 }
1818 });
1819
1820 let raw = Raw::new(&event).unwrap().cast_unchecked();
1821
1822 let timeline_event = TimelineEvent::from_plaintext(raw);
1825 assert_matches!(timeline_event.thread_summary, ThreadSummaryStatus::Some(ThreadSummary { num_replies, latest_reply }) => {
1826 assert_eq!(num_replies, 2);
1827 assert_eq!(latest_reply.as_deref(), Some(event_id!("$latest_event:example.com")));
1828 });
1829
1830 assert!(timeline_event.bundled_latest_thread_event.is_some());
1831
1832 let serialized_timeline_item = json!({
1835 "kind": {
1836 "PlainText": {
1837 "event": event
1838 }
1839 }
1840 });
1841
1842 let timeline_event: TimelineEvent =
1843 serde_json::from_value(serialized_timeline_item).unwrap();
1844 assert_matches!(timeline_event.thread_summary, ThreadSummaryStatus::Unknown);
1845
1846 assert!(timeline_event.bundled_latest_thread_event.is_none());
1849 }
1850
1851 #[test]
1852 fn sync_timeline_event_deserialisation_migration_for_withheld() {
1853 let serialized = json!({
1870 "kind": {
1871 "UnableToDecrypt": {
1872 "event": {
1873 "content": {
1874 "algorithm": "m.megolm.v1.aes-sha2",
1875 "ciphertext": "AwgAEoABzL1JYhqhjW9jXrlT3M6H8mJ4qffYtOQOnPuAPNxsuG20oiD/Fnpv6jnQGhU6YbV9pNM+1mRnTvxW3CbWOPjLKqCWTJTc7Q0vDEVtYePg38ncXNcwMmfhgnNAoW9S7vNs8C003x3yUl6NeZ8bH+ci870BZL+kWM/lMl10tn6U7snNmSjnE3ckvRdO+11/R4//5VzFQpZdf4j036lNSls/WIiI67Fk9iFpinz9xdRVWJFVdrAiPFwb8L5xRZ8aX+e2JDMlc1eW8gk",
1876 "device_id": "SKCGPNUWAU",
1877 "sender_key": "Gim/c7uQdSXyrrUbmUOrBT6sMC0gO7QSLmOK6B7NOm0",
1878 "session_id": "hgLyeSqXfb8vc5AjQLsg6TSHVu0HJ7HZ4B6jgMvxkrs"
1879 },
1880 "event_id": "$xxxxx:example.org",
1881 "origin_server_ts": 2189,
1882 "room_id": "!someroom:example.com",
1883 "sender": "@carl:example.com",
1884 "type": "m.room.message"
1885 },
1886 "utd_info": {
1887 "reason": "MissingMegolmSession",
1888 "session_id": "session000"
1889 }
1890 }
1891 }
1892 });
1893
1894 let result = serde_json::from_value(serialized);
1895 assert!(result.is_ok());
1896
1897 let event: TimelineEvent = result.unwrap();
1899 assert_matches!(
1900 event.kind,
1901 TimelineEventKind::UnableToDecrypt { utd_info, .. }=> {
1902 assert_matches!(
1903 utd_info.reason,
1904 UnableToDecryptReason::MissingMegolmSession { withheld_code: None }
1905 );
1906 }
1907 )
1908 }
1909
1910 #[test]
1911 fn unable_to_decrypt_info_migration_for_withheld() {
1912 let old_format = json!({
1913 "reason": "MissingMegolmSession",
1914 "session_id": "session000"
1915 });
1916
1917 let deserialized = serde_json::from_value::<UnableToDecryptInfo>(old_format).unwrap();
1918 let session_id = Some("session000".to_owned());
1919
1920 assert_eq!(deserialized.session_id, session_id);
1921 assert_eq!(
1922 deserialized.reason,
1923 UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
1924 );
1925
1926 let new_format = json!({
1927 "session_id": "session000",
1928 "reason": {
1929 "MissingMegolmSession": {
1930 "withheld_code": null
1931 }
1932 }
1933 });
1934
1935 let deserialized = serde_json::from_value::<UnableToDecryptInfo>(new_format).unwrap();
1936
1937 assert_eq!(
1938 deserialized.reason,
1939 UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
1940 );
1941 assert_eq!(deserialized.session_id, session_id);
1942 }
1943
1944 #[test]
1945 fn unable_to_decrypt_reason_is_missing_room_key() {
1946 let reason = UnableToDecryptReason::MissingMegolmSession { withheld_code: None };
1947 assert!(reason.is_missing_room_key());
1948
1949 let reason = UnableToDecryptReason::MissingMegolmSession {
1950 withheld_code: Some(WithheldCode::Blacklisted),
1951 };
1952 assert!(!reason.is_missing_room_key());
1953
1954 let reason = UnableToDecryptReason::UnknownMegolmMessageIndex;
1955 assert!(reason.is_missing_room_key());
1956 }
1957
1958 #[test]
1959 fn snapshot_test_verification_level() {
1960 with_settings!({ prepend_module_to_snapshot => false }, {
1961 assert_json_snapshot!(VerificationLevel::VerificationViolation);
1962 assert_json_snapshot!(VerificationLevel::UnsignedDevice);
1963 assert_json_snapshot!(VerificationLevel::None(DeviceLinkProblem::InsecureSource));
1964 assert_json_snapshot!(VerificationLevel::None(DeviceLinkProblem::MissingDevice));
1965 assert_json_snapshot!(VerificationLevel::UnverifiedIdentity);
1966 });
1967 }
1968
1969 #[test]
1970 fn snapshot_test_verification_states() {
1971 with_settings!({ prepend_module_to_snapshot => false }, {
1972 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::UnsignedDevice));
1973 assert_json_snapshot!(VerificationState::Unverified(
1974 VerificationLevel::VerificationViolation
1975 ));
1976 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::None(
1977 DeviceLinkProblem::InsecureSource,
1978 )));
1979 assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::None(
1980 DeviceLinkProblem::MissingDevice,
1981 )));
1982 assert_json_snapshot!(VerificationState::Verified);
1983 });
1984 }
1985
1986 #[test]
1987 fn snapshot_test_shield_states() {
1988 with_settings!({ prepend_module_to_snapshot => false }, {
1989 assert_json_snapshot!(ShieldState::None);
1990 assert_json_snapshot!(ShieldState::Red {
1991 code: ShieldStateCode::UnverifiedIdentity,
1992 message: "a message"
1993 });
1994 assert_json_snapshot!(ShieldState::Grey {
1995 code: ShieldStateCode::AuthenticityNotGuaranteed,
1996 message: "authenticity of this message cannot be guaranteed",
1997 });
1998 });
1999 }
2000
2001 #[test]
2002 fn snapshot_test_shield_codes() {
2003 with_settings!({ prepend_module_to_snapshot => false }, {
2004 assert_json_snapshot!(ShieldStateCode::AuthenticityNotGuaranteed);
2005 assert_json_snapshot!(ShieldStateCode::UnknownDevice);
2006 assert_json_snapshot!(ShieldStateCode::UnsignedDevice);
2007 assert_json_snapshot!(ShieldStateCode::UnverifiedIdentity);
2008 assert_json_snapshot!(ShieldStateCode::VerificationViolation);
2009 });
2010 }
2011
2012 #[test]
2013 fn snapshot_test_algorithm_info() {
2014 let mut map = BTreeMap::new();
2015 map.insert(DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned());
2016 map.insert(DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned());
2017 let info = AlgorithmInfo::MegolmV1AesSha2 {
2018 curve25519_key: "curvecurvecurve".into(),
2019 sender_claimed_keys: BTreeMap::from([
2020 (DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned()),
2021 (DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned()),
2022 ]),
2023 session_id: None,
2024 };
2025
2026 with_settings!({ prepend_module_to_snapshot => false }, {
2027 assert_json_snapshot!(info)
2028 });
2029 }
2030
2031 #[test]
2032 fn test_encryption_info_migration() {
2033 let old_format = json!({
2036 "sender": "@alice:localhost",
2037 "sender_device": "ABCDEFGH",
2038 "algorithm_info": {
2039 "MegolmV1AesSha2": {
2040 "curve25519_key": "curvecurvecurve",
2041 "sender_claimed_keys": {}
2042 }
2043 },
2044 "verification_state": "Verified",
2045 "session_id": "mysessionid76"
2046 });
2047
2048 let deserialized = serde_json::from_value::<EncryptionInfo>(old_format).unwrap();
2049 let expected_session_id = Some("mysessionid76".to_owned());
2050
2051 assert_let!(
2052 AlgorithmInfo::MegolmV1AesSha2 { session_id, .. } = deserialized.algorithm_info.clone()
2053 );
2054 assert_eq!(session_id, expected_session_id);
2055
2056 assert_json_snapshot!(deserialized);
2057 }
2058
2059 #[test]
2060 fn snapshot_test_encryption_info() {
2061 let info = EncryptionInfo {
2062 sender: user_id!("@alice:localhost").to_owned(),
2063 sender_device: Some(device_id!("ABCDEFGH").to_owned()),
2064 forwarder: None,
2065 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
2066 curve25519_key: "curvecurvecurve".into(),
2067 sender_claimed_keys: Default::default(),
2068 session_id: Some("mysessionid76".to_owned()),
2069 },
2070 verification_state: VerificationState::Verified,
2071 };
2072
2073 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
2074 assert_json_snapshot!(info)
2075 })
2076 }
2077
2078 #[test]
2079 fn snapshot_test_sync_timeline_event() {
2080 let room_event = TimelineEvent {
2081 kind: TimelineEventKind::Decrypted(DecryptedRoomEvent {
2082 event: Raw::new(&example_event()).unwrap().cast_unchecked(),
2083 encryption_info: Arc::new(EncryptionInfo {
2084 sender: user_id!("@sender:example.com").to_owned(),
2085 sender_device: Some(device_id!("ABCDEFGHIJ").to_owned()),
2086 forwarder: None,
2087 algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
2088 curve25519_key: "xxx".to_owned(),
2089 sender_claimed_keys: BTreeMap::from([
2090 (
2091 DeviceKeyAlgorithm::Ed25519,
2092 "I3YsPwqMZQXHkSQbjFNEs7b529uac2xBpI83eN3LUXo".to_owned(),
2093 ),
2094 (
2095 DeviceKeyAlgorithm::Curve25519,
2096 "qzdW3F5IMPFl0HQgz5w/L5Oi/npKUFn8Um84acIHfPY".to_owned(),
2097 ),
2098 ]),
2099 session_id: Some("mysessionid112".to_owned()),
2100 },
2101 verification_state: VerificationState::Verified,
2102 }),
2103 unsigned_encryption_info: Some(BTreeMap::from([(
2104 UnsignedEventLocation::RelationsThreadLatestEvent,
2105 UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
2106 session_id: Some("xyz".to_owned()),
2107 reason: UnableToDecryptReason::MissingMegolmSession {
2108 withheld_code: Some(WithheldCode::Unverified),
2109 },
2110 }),
2111 )])),
2112 }),
2113 timestamp: Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))),
2114 push_actions: Default::default(),
2115 thread_summary: ThreadSummaryStatus::Some(ThreadSummary {
2116 num_replies: 2,
2117 latest_reply: None,
2118 }),
2119 bundled_latest_thread_event: None,
2120 };
2121
2122 with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
2123 assert_json_snapshot! {
2126 serde_json::to_value(&room_event).unwrap(),
2127 }
2128 });
2129 }
2130
2131 #[test]
2132 fn test_from_bundled_latest_event_keeps_session_id() {
2133 let session_id = "hgLyeSqXfb8vc5AjQLsg6TSHVu0HJ7HZ4B6jgMvxkrs";
2134 let serialized = json!({
2135 "content": {
2136 "algorithm": "m.megolm.v1.aes-sha2",
2137 "ciphertext": "AwgAEoABzL1JYhqhjW9jXrlT3M6H8mJ4qffYtOQOnPuAPNxsuG20oiD/Fnpv6jnQGhU6YbV9pNM+1mRnTvxW3CbWOPjLKqCWTJTc7Q0vDEVtYePg38ncXNcwMmfhgnNAoW9S7vNs8C003x3yUl6NeZ8bH+ci870BZL+kWM/lMl10tn6U7snNmSjnE3ckvRdO+11/R4//5VzFQpZdf4j036lNSls/WIiI67Fk9iFpinz9xdRVWJFVdrAiPFwb8L5xRZ8aX+e2JDMlc1eW8gk",
2138 "device_id": "SKCGPNUWAU",
2139 "sender_key": "Gim/c7uQdSXyrrUbmUOrBT6sMC0gO7QSLmOK6B7NOm0",
2140 "session_id": session_id,
2141 },
2142 "event_id": "$xxxxx:example.org",
2143 "origin_server_ts": 2189,
2144 "room_id": "!someroom:example.com",
2145 "sender": "@carl:example.com",
2146 "type": "m.room.encrypted"
2147 });
2148 let json = serialized.to_string();
2149 let value = Raw::<AnySyncTimelineEvent>::from_json_string(json).unwrap();
2150
2151 let kind = TimelineEventKind::UnableToDecrypt {
2152 event: value.clone(),
2153 utd_info: UnableToDecryptInfo {
2154 session_id: None,
2155 reason: UnableToDecryptReason::Unknown,
2156 },
2157 };
2158 let result = TimelineEvent::from_bundled_latest_event(
2159 &kind,
2160 Some(value.cast_unchecked()),
2161 MilliSecondsSinceUnixEpoch::now(),
2162 )
2163 .expect("Could not get bundled latest event");
2164
2165 assert_let!(TimelineEventKind::UnableToDecrypt { utd_info, .. } = result.kind);
2166 assert!(utd_info.session_id.is_some());
2167 assert_eq!(utd_info.session_id.unwrap(), session_id);
2168 }
2169}