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