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