Skip to main content

matrix_sdk_common/
deserialized_responses.rs

1// Copyright 2023 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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/// Represents the state of verification for a decrypted message sent by a
51/// device.
52#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
53#[serde(from = "OldVerificationStateHelper")]
54pub enum VerificationState {
55    /// This message is guaranteed to be authentic as it is coming from a device
56    /// belonging to a user that we have verified.
57    ///
58    /// This is the only state where authenticity can be guaranteed.
59    Verified,
60
61    /// The message could not be linked to a verified device.
62    ///
63    /// For more detailed information on why the message is considered
64    /// unverified, refer to the VerificationLevel sub-enum.
65    Unverified(VerificationLevel),
66}
67
68// TODO: Remove this once we're confident that everybody that serialized these
69// states uses the new enum.
70#[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            // This mapping isn't strictly correct but we don't know which part in the old
83            // `VerificationState` enum was unverified.
84            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    /// Convert the `VerificationState` into a `ShieldState` which can be
98    /// directly used to decorate messages in the recommended way.
99    ///
100    /// This method decorates messages using a strict ruleset, for a more lax
101    /// variant of this method take a look at
102    /// [`VerificationState::to_shield_state_lax()`].
103    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    /// Convert the `VerificationState` into a `ShieldState` which can be used
132    /// to decorate messages in the recommended way.
133    ///
134    /// This implements a legacy, lax decoration mode.
135    ///
136    /// For a more strict variant of this method take a look at
137    /// [`VerificationState::to_shield_state_strict()`].
138    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                    // If you didn't show interest in verifying that user we don't
144                    // nag you with an error message.
145                    ShieldState::None
146                }
147                VerificationLevel::VerificationViolation => {
148                    // This is a high warning. The sender was previously
149                    // verified, but changed their identity.
150                    ShieldState::Red {
151                        code: ShieldStateCode::VerificationViolation,
152                        message: VERIFICATION_VIOLATION,
153                    }
154                }
155                VerificationLevel::UnsignedDevice => {
156                    // This is a high warning. The sender hasn't verified his own device.
157                    ShieldState::Red {
158                        code: ShieldStateCode::UnsignedDevice,
159                        message: UNSIGNED_DEVICE,
160                    }
161                }
162                VerificationLevel::None(link) => match link {
163                    DeviceLinkProblem::MissingDevice => {
164                        // Have to warn as it could have been a temporary injected device.
165                        // Notice that the device might just not be known at this time, so callers
166                        // should retry when there is a device change for that user.
167                        ShieldState::Red {
168                            code: ShieldStateCode::UnknownDevice,
169                            message: UNKNOWN_DEVICE,
170                        }
171                    }
172                    DeviceLinkProblem::InsecureSource => {
173                        // In legacy mode, we tone down this warning as it is quite common and
174                        // mostly noise (due to legacy backup and lack of trusted forwards).
175                        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/// The sub-enum containing detailed information on why a message is considered
191/// to be unverified.
192#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
193pub enum VerificationLevel {
194    /// The message was sent by a user identity we have not verified.
195    UnverifiedIdentity,
196
197    /// The message was sent by a user identity we have not verified, but the
198    /// user was previously verified.
199    #[serde(alias = "PreviouslyVerified")]
200    VerificationViolation,
201
202    /// The message was sent by a device not linked to (signed by) any user
203    /// identity.
204    UnsignedDevice,
205
206    /// We weren't able to link the message back to any device. This might be
207    /// because the message claims to have been sent by a device which we have
208    /// not been able to obtain (for example, because the device was since
209    /// deleted) or because the key to decrypt the message was obtained from
210    /// an insecure source.
211    None(DeviceLinkProblem),
212
213    /// The `sender` field on the event does not match the owner of the device
214    /// that established the Megolm session.
215    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/// The sub-enum containing detailed information on why we were not able to link
236/// a message back to a device.
237#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
238pub enum DeviceLinkProblem {
239    /// The device is missing, either because it was deleted, or you haven't
240    /// yet downoaled it or the server is erroneously omitting it (federation
241    /// lag).
242    MissingDevice,
243    /// The key was obtained from an insecure source: imported from a file,
244    /// obtained from a legacy (asymmetric) backup, unsafe key forward, etc.
245    InsecureSource,
246}
247
248/// Recommended decorations for decrypted messages, representing the message's
249/// authenticity properties.
250#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
251pub enum ShieldState {
252    /// A red shield with a tooltip containing the associated message should be
253    /// presented.
254    Red {
255        /// A machine-readable representation.
256        code: ShieldStateCode,
257        /// A human readable description.
258        message: &'static str,
259    },
260    /// A grey shield with a tooltip containing the associated message should be
261    /// presented.
262    Grey {
263        /// A machine-readable representation.
264        code: ShieldStateCode,
265        /// A human readable description.
266        message: &'static str,
267    },
268    /// No shield should be presented.
269    None,
270}
271
272/// A machine-readable representation of the authenticity for a `ShieldState`.
273#[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    /// Not enough information available to check the authenticity.
278    AuthenticityNotGuaranteed,
279    /// The sending device isn't yet known by the Client.
280    UnknownDevice,
281    /// The sending device hasn't been verified by the sender.
282    UnsignedDevice,
283    /// The sender hasn't been verified by the Client's user.
284    UnverifiedIdentity,
285    /// The sender was previously verified but changed their identity.
286    #[serde(alias = "PreviouslyVerified")]
287    VerificationViolation,
288    /// The `sender` field on the event does not match the owner of the device
289    /// that established the Megolm session.
290    MismatchedSender,
291}
292
293/// The algorithm specific information of a decrypted event.
294#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
295pub enum AlgorithmInfo {
296    /// The info if the event was encrypted using m.megolm.v1.aes-sha2
297    MegolmV1AesSha2 {
298        /// The curve25519 key of the device that created the megolm decryption
299        /// key originally.
300        curve25519_key: String,
301        /// The signing keys that have created the megolm key that was used to
302        /// decrypt this session. This map will usually contain a single ed25519
303        /// key.
304        sender_claimed_keys: BTreeMap<DeviceKeyAlgorithm, String>,
305
306        /// The Megolm session ID that was used to encrypt this event, or None
307        /// if this info was stored before we collected this data.
308        #[serde(default, skip_serializing_if = "Option::is_none")]
309        session_id: Option<String>,
310    },
311
312    /// The info if the event was encrypted using m.olm.v1.curve25519-aes-sha2
313    OlmV1Curve25519AesSha2 {
314        // The sender device key, base64 encoded
315        curve25519_public_key_base64: String,
316    },
317}
318
319/// Struct containing information on the forwarder of the keys used to decrypt
320/// an event.
321#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
322pub struct ForwarderInfo {
323    /// The user ID of the forwarder.
324    pub user_id: OwnedUserId,
325    /// The device ID of the forwarder.
326    pub device_id: OwnedDeviceId,
327}
328
329/// Struct containing information on how an event was decrypted.
330#[derive(Clone, Debug, PartialEq, Serialize)]
331pub struct EncryptionInfo {
332    /// The user ID of the event sender, note this is untrusted data unless the
333    /// `verification_state` is `Verified` as well.
334    pub sender: OwnedUserId,
335    /// The device ID of the device that sent us the event, note this is
336    /// untrusted data unless `verification_state` is `Verified` as well.
337    pub sender_device: Option<OwnedDeviceId>,
338    /// If the keys for this message were shared-on-invite as part of an
339    /// [MSC4268] key bundle, information about the forwarder.
340    ///
341    /// [MSC4268]: https://github.com/matrix-org/matrix-spec-proposals/pull/4268
342    pub forwarder: Option<ForwarderInfo>,
343    /// Information about the algorithm that was used to encrypt the event.
344    pub algorithm_info: AlgorithmInfo,
345    /// The verification state of the device that sent us the event, note this
346    /// is the state of the device at the time of decryption. It may change in
347    /// the future if a device gets verified or deleted.
348    ///
349    /// Callers that persist this should mark the state as dirty when a device
350    /// change is received down the sync.
351    pub verification_state: VerificationState,
352}
353
354impl EncryptionInfo {
355    /// Helper to get the megolm session id used to encrypt.
356    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        // Backwards compatibility: Capture session_id at root if exists. In legacy
371        // EncryptionInfo the session_id was not in AlgorithmInfo
372        #[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                    // Migration, merge the old_session_id in algorithm_info
396                    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/// A simplified thread summary.
409///
410/// A thread summary contains useful information pertaining to a thread, and
411/// that would be usually attached in clients to a thread root event (i.e. the
412/// first event from which the thread originated), along with links into the
413/// thread's view. This summary may include, for instance:
414///
415/// - the number of replies to the thread,
416/// - the full event of the latest reply to the thread,
417/// - whether the user participated or not to this thread.
418#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
419pub struct ThreadSummary {
420    /// The event id for the latest reply to the thread.
421    #[serde(skip_serializing_if = "Option::is_none")]
422    pub latest_reply: Option<OwnedEventId>,
423
424    /// The number of replies to the thread.
425    ///
426    /// This doesn't include the thread root event itself. It can be zero if no
427    /// events in the thread are considered to be meaningful (or they've all
428    /// been redacted).
429    pub num_replies: u32,
430}
431
432/// The status of a thread summary.
433#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
434pub enum ThreadSummaryStatus {
435    /// We don't know if the event has a thread summary.
436    #[default]
437    Unknown,
438    /// The event has no thread summary.
439    None,
440    /// The event has a thread summary, which is bundled in the event itself.
441    Some(ThreadSummary),
442}
443
444impl ThreadSummaryStatus {
445    /// Create a [`ThreadSummaryStatus`] from an optional thread summary.
446    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    /// Is the thread status of this event unknown?
454    fn is_unknown(&self) -> bool {
455        matches!(self, ThreadSummaryStatus::Unknown)
456    }
457
458    /// Transforms the [`ThreadSummaryStatus`] into an optional thread summary,
459    /// for cases where we don't care about distinguishing unknown and none.
460    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/// Represents a matrix room event that has been returned from a Matrix
469/// client-server API endpoint such as `/sync` or `/messages`, after initial
470/// processing.
471///
472/// The "initial processing" includes an attempt to decrypt encrypted events, so
473/// the main thing this adds over [`AnyTimelineEvent`] is information on
474/// encryption.
475//
476// 🚨 Note about this type, please read! 🚨
477//
478// `TimelineEvent` is heavily used across the SDK crates. In some cases, we
479// are reaching a [`recursion_limit`] when the compiler is trying to figure out
480// if `TimelineEvent` implements `Sync` when it's embedded in other types.
481//
482// We want to help the compiler so that one doesn't need to increase the
483// `recursion_limit`. We stop the recursive check by (un)safely implement `Sync`
484// and `Send` on `TimelineEvent` directly.
485//
486// See
487// https://github.com/matrix-org/matrix-rust-sdk/pull/3749#issuecomment-2312939823
488// which has addressed this issue first
489//
490// [`recursion_limit`]: https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute
491#[derive(Clone, Debug, Serialize)]
492pub struct TimelineEvent {
493    /// The event itself, together with any information on decryption.
494    pub kind: TimelineEventKind,
495
496    /// The timestamp of the event. It's the `origin_server_ts` value (if any),
497    /// corrected if detected as malicious.
498    ///
499    /// It can be `None` if the event has been serialised before the addition of
500    /// this field, or if parsing the `origin_server_ts` value failed.
501    pub timestamp: Option<MilliSecondsSinceUnixEpoch>,
502
503    /// The push actions associated with this event.
504    ///
505    /// If it's set to `None`, then it means we couldn't compute those actions,
506    /// or that they could be computed but there were none.
507    #[serde(skip_serializing_if = "skip_serialize_push_actions")]
508    push_actions: Option<Vec<Action>>,
509
510    /// If the event is part of a thread, a thread summary.
511    #[serde(default, skip_serializing_if = "ThreadSummaryStatus::is_unknown")]
512    pub thread_summary: ThreadSummaryStatus,
513
514    /// The bundled latest thread event, if it was provided in the unsigned
515    /// relations of this event.
516    ///
517    /// Not serialized.
518    #[serde(skip)]
519    pub bundled_latest_thread_event: Option<Box<TimelineEvent>>,
520}
521
522// Don't serialize push actions if they're `None` or an empty vec.
523fn 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// See https://github.com/matrix-org/matrix-rust-sdk/pull/3749#issuecomment-2312939823.
528#[cfg(not(feature = "test-send-sync"))]
529unsafe impl Send for TimelineEvent {}
530
531// See https://github.com/matrix-org/matrix-rust-sdk/pull/3749#issuecomment-2312939823.
532#[cfg(not(feature = "test-send-sync"))]
533unsafe impl Sync for TimelineEvent {}
534
535#[cfg(feature = "test-send-sync")]
536#[test]
537// See https://github.com/matrix-org/matrix-rust-sdk/pull/3749#issuecomment-2312939823.
538fn 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    /// Create a new [`TimelineEvent`] from the given raw event.
546    ///
547    /// This is a convenience constructor for a plaintext event when you don't
548    /// need to set `push_action`, for example inside a test.
549    pub fn from_plaintext(event: Raw<AnySyncTimelineEvent>) -> Self {
550        Self::from_plaintext_with_max_timestamp(event, MilliSecondsSinceUnixEpoch::now())
551    }
552
553    /// Like [`TimelineEvent::from_plaintext`] but with a given `max_timestamp`.
554    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    /// Create a new [`TimelineEvent`] from a decrypted event.
562    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    /// Like [`TimelineEvent::from_decrypted`] but with a given `max_timestamp`.
574    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    /// Create a new [`TimelineEvent`] to represent the given decryption
583    /// failure.
584    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    /// Like [`TimelineEvent::from_utd`] but with a given `max_timestamp`.
589    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    /// Internal only: helps extracting a thread summary and latest thread event
598    /// when creating a new [`TimelineEvent`].
599    ///
600    /// Build the `timestamp` value by using `now()` as the max value.
601    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    /// Transform this [`TimelineEvent`] into another [`TimelineEvent`] with the
619    /// [`TimelineEventKind::Decrypted`] kind.
620    ///
621    /// ## Panics
622    ///
623    /// It panics (on debug builds only) if the kind already is
624    /// [`TimelineEventKind::Decrypted`].
625    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    /// Transform this [`TimelineEvent`] into another [`TimelineEvent`] with the
645    /// [`TimelineEventKind::Decrypted`] kind.
646    ///
647    /// ## Panics
648    ///
649    /// It panics (on debug builds only) if the kind already is
650    /// [`TimelineEventKind::Decrypted`].
651    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    /// Try to create a new [`TimelineEvent`] for the bundled latest thread
667    /// event, if available, and if we have enough information about the
668    /// encryption status for it.
669    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                            // The bundled event was encrypted, and we could decrypt it: pass that
686                            // information around.
687                            return Some(Box::new(
688                                TimelineEvent::from_decrypted_with_max_timestamp(
689                                    DecryptedRoomEvent {
690                                        // Safety: A decrypted event always includes a room_id in
691                                        // its payload.
692                                        event: latest_event.cast_unchecked(),
693                                        encryption_info: encryption_info.clone(),
694                                        // A bundled latest event is never a thread root. It could
695                                        // have
696                                        // a replacement event, but we don't carry this information
697                                        // around.
698                                        unsigned_encryption_info: None,
699                                    },
700                                    None,
701                                    max_timestamp,
702                                ),
703                            ));
704                        }
705
706                        UnsignedDecryptionResult::UnableToDecrypt(utd_info) => {
707                            // The bundled event was a UTD; store that information.
708                            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                // Figure based on the event type below.
720            }
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                // The bundled latest thread event is encrypted, but we didn't have any
736                // information about it in the unsigned map. Try to fetch the information from
737                // the content instead.
738                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    /// Read the current push actions.
769    ///
770    /// Returns `None` if they were never computed, or if they could not be
771    /// computed.
772    pub fn push_actions(&self) -> Option<&[Action]> {
773        self.push_actions.as_deref()
774    }
775
776    /// Set the push actions for this event.
777    pub fn set_push_actions(&mut self, push_actions: Vec<Action>) {
778        self.push_actions = Some(push_actions);
779    }
780
781    /// Get the event id of this [`TimelineEvent`] if the event has any valid
782    /// id.
783    pub fn event_id(&self) -> Option<OwnedEventId> {
784        self.kind.event_id()
785    }
786
787    /// Get the sender of this [`TimelineEvent`] if the event has one.
788    pub fn sender(&self) -> Option<OwnedUserId> {
789        self.kind.sender()
790    }
791
792    /// Returns a reference to the (potentially decrypted) Matrix event inside
793    /// this [`TimelineEvent`].
794    pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
795        self.kind.raw()
796    }
797
798    /// Replace the raw event included in this item by another one.
799    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                // It's safe to cast `AnyMessageLikeEvent` into `AnySyncMessageLikeEvent`,
805                // because the former contains a superset of the fields included in the latter.
806                *event = replacement.cast();
807            }
808        }
809    }
810
811    /// Get the timestamp.
812    ///
813    /// If the timestamp is missing (most likely because the event has been
814    /// created before the addition of the [`TimelineEvent::timestamp`] field),
815    /// this method will try to extract it from the `origin_server_ts` value. If
816    /// the `origin_server_ts` value is malicious, it will be capped to
817    /// [`MilliSecondsSinceUnixEpoch::now`]. It means that the returned value
818    /// might not be constant.
819    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    /// Get the timestamp value, without trying to backfill it if `None`.
828    pub fn timestamp_raw(&self) -> Option<MilliSecondsSinceUnixEpoch> {
829        self.timestamp
830    }
831
832    /// If the event was a decrypted event that was successfully decrypted, get
833    /// its encryption info. Otherwise, `None`.
834    pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
835        self.kind.encryption_info()
836    }
837
838    /// Takes ownership of this [`TimelineEvent`], returning the (potentially
839    /// decrypted) Matrix event within.
840    pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
841        self.kind.into_raw()
842    }
843}
844
845impl<'de> Deserialize<'de> for TimelineEvent {
846    /// Custom deserializer for [`TimelineEvent`], to support older formats.
847    ///
848    /// Ideally we might use an untagged enum and then convert from that;
849    /// however, that doesn't work due to a [serde bug](https://github.com/serde-rs/json/issues/497).
850    ///
851    /// Instead, we first deserialize into an unstructured JSON map, and then
852    /// inspect the json to figure out which format we have.
853    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        // First, deserialize to an unstructured JSON map
860        let value = Map::<String, Value>::deserialize(deserializer)?;
861
862        // If we have a top-level `event`, it's V0
863        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        // Otherwise, it's V1
873        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/// The event within a [`TimelineEvent`], together with encryption data.
886#[derive(Clone, Serialize, Deserialize)]
887pub enum TimelineEventKind {
888    /// A successfully-decrypted encrypted event.
889    Decrypted(DecryptedRoomEvent),
890
891    /// An encrypted event which could not be decrypted.
892    UnableToDecrypt {
893        /// The `m.room.encrypted` event. Depending on the source of the event,
894        /// it could actually be an [`AnyTimelineEvent`] (i.e., it may
895        /// have a `room_id` property).
896        event: Raw<AnySyncTimelineEvent>,
897
898        /// Information on the reason we failed to decrypt
899        utd_info: UnableToDecryptInfo,
900    },
901
902    /// An unencrypted event.
903    PlainText {
904        /// The actual event. Depending on the source of the event, it could
905        /// actually be a [`AnyTimelineEvent`] (which differs from
906        /// [`AnySyncTimelineEvent`] by the addition of a `room_id` property).
907        event: Raw<AnySyncTimelineEvent>,
908    },
909}
910
911impl TimelineEventKind {
912    /// Returns a reference to the (potentially decrypted) Matrix event inside
913    /// this `TimelineEvent`.
914    pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
915        match self {
916            // It is safe to cast from an `AnyMessageLikeEvent` (i.e. JSON which does
917            // *not* contain a `state_key` and *does* contain a `room_id`) into an
918            // `AnySyncTimelineEvent` (i.e. JSON which *may* contain a `state_key` and is *not*
919            // expected to contain a `room_id`). It just means that the `room_id` will be ignored
920            // in a future deserialization.
921            TimelineEventKind::Decrypted(d) => d.event.cast_ref(),
922            TimelineEventKind::UnableToDecrypt { event, .. } => event,
923            TimelineEventKind::PlainText { event } => event,
924        }
925    }
926
927    /// Get the event id of this `TimelineEventKind` if the event has any valid
928    /// id.
929    pub fn event_id(&self) -> Option<OwnedEventId> {
930        self.raw().get_field::<OwnedEventId>("event_id").ok().flatten()
931    }
932
933    /// Get the sender of this [`TimelineEventKind`] if the event has one.
934    pub fn sender(&self) -> Option<OwnedUserId> {
935        self.raw().get_field::<OwnedUserId>("sender").ok().flatten()
936    }
937
938    /// Whether we could not decrypt the event (i.e. it is a UTD).
939    pub fn is_utd(&self) -> bool {
940        matches!(self, TimelineEventKind::UnableToDecrypt { .. })
941    }
942
943    /// If the event was a decrypted event that was successfully decrypted, get
944    /// its encryption info. Otherwise, `None`.
945    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    /// If the event was a decrypted event that was successfully decrypted, get
953    /// the map of decryption metadata related to the bundled events.
954    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    /// Takes ownership of this `TimelineEvent`, returning the (potentially
964    /// decrypted) Matrix event within.
965    pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
966        match self {
967            // It is safe to cast from an `AnyMessageLikeEvent` (i.e. JSON which does
968            // *not* contain a `state_key` and *does* contain a `room_id`) into an
969            // `AnySyncTimelineEvent` (i.e. JSON which *may* contain a `state_key` and is *not*
970            // expected to contain a `room_id`). It just means that the `room_id` will be ignored
971            // in a future deserialization.
972            TimelineEventKind::Decrypted(d) => d.event.cast(),
973            TimelineEventKind::UnableToDecrypt { event, .. } => event,
974            TimelineEventKind::PlainText { event } => event,
975        }
976    }
977
978    /// The Megolm session ID that was used to send this event, if it was
979    /// encrypted.
980    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    /// Get the event type of this event.
991    ///
992    /// Returns `None` if there isn't an event type or if the event failed to be
993    /// deserialized.
994    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)]
1022/// A successfully-decrypted encrypted event.
1023pub struct DecryptedRoomEvent {
1024    /// The decrypted event.
1025    ///
1026    /// Note: it's not an error that this contains an [`AnyTimelineEvent`]
1027    /// (as opposed to an [`AnySyncTimelineEvent`]): an
1028    /// encrypted payload *always contains* a room id, by the [spec].
1029    ///
1030    /// [spec]: https://spec.matrix.org/v1.12/client-server-api/#mmegolmv1aes-sha2
1031    pub event: Raw<AnyTimelineEvent>,
1032
1033    /// The encryption info about the event.
1034    pub encryption_info: Arc<EncryptionInfo>,
1035
1036    /// The encryption info about the events bundled in the `unsigned`
1037    /// object.
1038    ///
1039    /// Will be `None` if no bundled event was encrypted.
1040    #[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/// The location of an event bundled in an `unsigned` object.
1058#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
1059pub enum UnsignedEventLocation {
1060    /// An event at the `m.replace` key of the `m.relations` object, that is a
1061    /// bundled replacement.
1062    RelationsReplace,
1063    /// An event at the `latest_event` key of the `m.thread` object of the
1064    /// `m.relations` object, that is the latest event of a thread.
1065    RelationsThreadLatestEvent,
1066}
1067
1068impl UnsignedEventLocation {
1069    /// Find the mutable JSON value at this location in the given unsigned
1070    /// object.
1071    ///
1072    /// # Arguments
1073    ///
1074    /// * `unsigned` - The `unsigned` property of an event as a JSON object.
1075    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/// The result of the decryption of an event bundled in an `unsigned` object.
1088#[derive(Debug, Clone, Serialize, Deserialize)]
1089pub enum UnsignedDecryptionResult {
1090    /// The event was successfully decrypted.
1091    Decrypted(Arc<EncryptionInfo>),
1092    /// The event failed to be decrypted.
1093    UnableToDecrypt(UnableToDecryptInfo),
1094}
1095
1096impl UnsignedDecryptionResult {
1097    /// Returns the encryption info for this bundled event if it was
1098    /// successfully decrypted.
1099    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/// Metadata about an event that could not be decrypted.
1108#[derive(Debug, Clone, Serialize, Deserialize)]
1109pub struct UnableToDecryptInfo {
1110    /// The ID of the session used to encrypt the message, if it used the
1111    /// `m.megolm.v1.aes-sha2` algorithm.
1112    #[serde(skip_serializing_if = "Option::is_none")]
1113    pub session_id: Option<String>,
1114
1115    /// Reason code for the decryption failure
1116    #[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
1124/// Provides basic backward compatibility for deserializing older serialized
1125/// `UnableToDecryptReason` values.
1126pub fn deserialize_utd_reason<'de, D>(d: D) -> Result<UnableToDecryptReason, D::Error>
1127where
1128    D: serde::Deserializer<'de>,
1129{
1130    // Start by deserializing as to an untyped JSON value.
1131    let v: serde_json::Value = Deserialize::deserialize(d)?;
1132    // Backwards compatibility: `MissingMegolmSession` used to be stored without the
1133    // withheld code.
1134    if v.as_str().is_some_and(|s| s == "MissingMegolmSession") {
1135        return Ok(UnableToDecryptReason::MissingMegolmSession { withheld_code: None });
1136    }
1137    // Otherwise, use the derived deserialize impl to turn the JSON into a
1138    // UnableToDecryptReason
1139    serde_json::from_value::<UnableToDecryptReason>(v).map_err(serde::de::Error::custom)
1140}
1141
1142/// Reason code for a decryption failure
1143#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1144pub enum UnableToDecryptReason {
1145    /// The reason for the decryption failure is unknown. This is only intended
1146    /// for use when deserializing old UnableToDecryptInfo instances.
1147    #[doc(hidden)]
1148    Unknown,
1149
1150    /// The `m.room.encrypted` event that should have been decrypted is
1151    /// malformed in some way (e.g. unsupported algorithm, missing fields,
1152    /// unknown megolm message type).
1153    MalformedEncryptedEvent,
1154
1155    /// Decryption failed because we're missing the megolm session that was used
1156    /// to encrypt the event.
1157    MissingMegolmSession {
1158        /// If the key was withheld on purpose, the associated code. `None`
1159        /// means no withheld code was received.
1160        withheld_code: Option<WithheldCode>,
1161    },
1162
1163    /// Decryption failed because, while we have the megolm session that was
1164    /// used to encrypt the message, it is ratcheted too far forward.
1165    UnknownMegolmMessageIndex,
1166
1167    /// We found the Megolm session, but were unable to decrypt the event using
1168    /// that session for some reason (e.g. incorrect MAC).
1169    ///
1170    /// This represents all `vodozemac::megolm::DecryptionError`s, except
1171    /// `UnknownMessageIndex`, which is represented as
1172    /// `UnknownMegolmMessageIndex`.
1173    MegolmDecryptionFailure,
1174
1175    /// The event could not be deserialized after decryption.
1176    PayloadDeserializationFailure,
1177
1178    /// Decryption failed because of a mismatch between the identity keys of the
1179    /// device we received the room key from and the identity keys recorded in
1180    /// the plaintext of the room key to-device message.
1181    MismatchedIdentityKeys,
1182
1183    /// An encrypted message wasn't decrypted, because the sender's
1184    /// cross-signing identity did not satisfy the requested
1185    /// `TrustRequirement`.
1186    SenderIdentityNotTrusted(VerificationLevel),
1187
1188    /// The outer state key could not be verified against the inner encrypted
1189    /// state key and type.
1190    #[cfg(feature = "experimental-encrypted-state-events")]
1191    StateKeyVerificationFailed,
1192}
1193
1194impl UnableToDecryptReason {
1195    /// Returns true if this UTD is due to a missing room key (and hence might
1196    /// resolve itself if we wait a bit.)
1197    pub fn is_missing_room_key(&self) -> bool {
1198        // In case of MissingMegolmSession with a withheld code we return false here
1199        // given that this API is used to decide if waiting a bit will help.
1200        matches!(
1201            self,
1202            Self::MissingMegolmSession { withheld_code: None } | Self::UnknownMegolmMessageIndex
1203        )
1204    }
1205}
1206
1207/// A machine-readable code for why a Megolm key was not sent.
1208///
1209/// Normally sent as the payload of an [`m.room_key.withheld`](https://spec.matrix.org/v1.12/client-server-api/#mroom_keywithheld) to-device message.
1210#[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    /// the user/device was blacklisted.
1224    #[ruma_enum(rename = "m.blacklisted")]
1225    Blacklisted,
1226
1227    /// the user/devices is unverified.
1228    #[ruma_enum(rename = "m.unverified")]
1229    Unverified,
1230
1231    /// The user/device is not allowed have the key. For example, this would
1232    /// usually be sent in response to a key request if the user was not in
1233    /// the room when the message was sent.
1234    #[ruma_enum(rename = "m.unauthorised")]
1235    Unauthorised,
1236
1237    /// Sent in reply to a key request if the device that the key is requested
1238    /// from does not have the requested key.
1239    #[ruma_enum(rename = "m.unavailable")]
1240    Unavailable,
1241
1242    /// An olm session could not be established.
1243    /// This may happen, for example, if the sender was unable to obtain a
1244    /// one-time key from the recipient.
1245    #[ruma_enum(rename = "m.no_olm")]
1246    NoOlm,
1247
1248    /// Normally used when sharing history, per [MSC4268]: indicates
1249    /// that the session was not marked as "shared_history".
1250    ///
1251    /// [MSC4268]: https://github.com/matrix-org/matrix-spec-proposals/pull/4268
1252    #[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// The Ruma macro expects the type to have this name.
1276// The payload is counter intuitively made public in order to avoid having
1277// multiple copies of this struct.
1278#[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/// Deserialization helper for [`TimelineEvent`], for the modern format.
1290///
1291/// This has the exact same fields as [`TimelineEvent`] itself, but has a
1292/// regular `Deserialize` implementation.
1293#[derive(Debug, Deserialize)]
1294struct SyncTimelineEventDeserializationHelperV1 {
1295    /// The event itself, together with any information on decryption.
1296    kind: TimelineEventKind,
1297
1298    /// The timestamp of the event. It's the `origin_server_ts` value (if any),
1299    /// corrected if detected as malicious.
1300    #[serde(default)]
1301    timestamp: Option<MilliSecondsSinceUnixEpoch>,
1302
1303    /// The push actions associated with this event.
1304    #[serde(default)]
1305    push_actions: Vec<Action>,
1306
1307    /// If the event is part of a thread, a thread summary.
1308    #[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        // If `timestamp` is `None`, it is very likely that the event was serialised
1322        // before the addition of the `timestamp` field. We _could_ compute it here, but
1323        // if the `timestamp` was malicious, it means we are going to _cap_ the
1324        // `timestamp` to `now()` for every deserialisation. It is annoying because it
1325        // means the event is no longer deterministic, it's not constant.
1326        // We don't want that. Consequently, we keep `None` here, and we let
1327        // [`TimelineEvent::timestamp`] to handle that case for us.
1328
1329        TimelineEvent {
1330            kind,
1331            timestamp,
1332            push_actions: Some(push_actions),
1333            thread_summary,
1334            // Bundled latest thread event is not persisted.
1335            bundled_latest_thread_event: None,
1336        }
1337    }
1338}
1339
1340/// Deserialization helper for [`TimelineEvent`], for an older format.
1341#[derive(Deserialize)]
1342struct SyncTimelineEventDeserializationHelperV0 {
1343    /// The actual event.
1344    event: Raw<AnySyncTimelineEvent>,
1345
1346    /// The encryption info about the event.
1347    ///
1348    /// Will be `None` if the event was not encrypted.
1349    encryption_info: Option<Arc<EncryptionInfo>>,
1350
1351    /// The push actions associated with this event.
1352    #[serde(default)]
1353    push_actions: Vec<Action>,
1354
1355    /// The encryption info about the events bundled in the `unsigned`
1356    /// object.
1357    ///
1358    /// Will be `None` if no bundled event was encrypted.
1359    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        // We do not compute the `timestamp` value here because if the `timestamp` is
1372        // malicious, it means we are going to _cap_ the `timestamp` to `now()` for
1373        // every deserialisation. It is annoying because it means the event is no longer
1374        // deterministic, it's not constant. We don't want that. Consequently, we keep
1375        // `None` here, and we let [`TimelineEvent::timestamp`] to handle that case for
1376        // us.
1377        let timestamp = None;
1378
1379        let kind = match encryption_info {
1380            Some(encryption_info) => {
1381                TimelineEventKind::Decrypted(DecryptedRoomEvent {
1382                    // We cast from `Raw<AnySyncTimelineEvent>` to
1383                    // `Raw<AnyMessageLikeEvent>`, which means
1384                    // we are asserting that it contains a room_id.
1385                    // That *should* be ok, because if this is genuinely a decrypted
1386                    // room event (as the encryption_info indicates), then it will have
1387                    // a room_id.
1388                    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            // No serialized events had a thread summary at this version of the struct.
1402            thread_summary: ThreadSummaryStatus::Unknown,
1403            // Bundled latest thread event is not persisted.
1404            bundled_latest_thread_event: None,
1405        }
1406    }
1407}
1408
1409/// Reason code for a to-device decryption failure
1410#[derive(Debug, Clone, PartialEq)]
1411pub enum ToDeviceUnableToDecryptReason {
1412    /// An error occurred while encrypting the event. This covers all
1413    /// `OlmError` types.
1414    DecryptionFailure,
1415
1416    /// We refused to decrypt the message because the sender's device is not
1417    /// verified, or more generally, the sender's identity did not match the
1418    /// trust requirement we were asked to provide.
1419    UnverifiedSenderDevice,
1420
1421    /// We have no `OlmMachine`. This should not happen unless we forget to set
1422    /// things up by calling `OlmMachine::activate()`.
1423    NoOlmMachine,
1424
1425    /// The Matrix SDK was compiled without encryption support.
1426    EncryptionIsDisabled,
1427}
1428
1429/// Metadata about a to-device event that could not be decrypted.
1430#[derive(Clone, Debug)]
1431pub struct ToDeviceUnableToDecryptInfo {
1432    /// Reason code for the decryption failure
1433    pub reason: ToDeviceUnableToDecryptReason,
1434}
1435
1436/// Represents a to-device event after it has been processed by the Olm machine.
1437#[derive(Clone, Debug)]
1438pub enum ProcessedToDeviceEvent {
1439    /// A successfully-decrypted encrypted event.
1440    /// Contains the raw decrypted event and encryption info
1441    Decrypted {
1442        /// The raw decrypted event
1443        raw: Raw<AnyToDeviceEvent>,
1444        /// The Olm encryption info
1445        encryption_info: EncryptionInfo,
1446    },
1447
1448    /// An encrypted event which could not be decrypted.
1449    UnableToDecrypt {
1450        encrypted_event: Raw<AnyToDeviceEvent>,
1451        utd_info: ToDeviceUnableToDecryptInfo,
1452    },
1453
1454    /// An unencrypted event.
1455    PlainText(Raw<AnyToDeviceEvent>),
1456
1457    /// An invalid to device event that was ignored because it is missing some
1458    /// required information to be processed (like no event `type` for
1459    /// example)
1460    Invalid(Raw<AnyToDeviceEvent>),
1461}
1462
1463impl ProcessedToDeviceEvent {
1464    /// Converts a ProcessedToDeviceEvent to the `Raw<AnyToDeviceEvent>` it
1465    /// encapsulates
1466    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    /// Gets the raw to-device event.
1478    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        // Given a JSON VerificationLevel
1577        #[derive(Deserialize)]
1578        struct Container {
1579            verification_level: VerificationLevel,
1580        }
1581        let container = json!({ "verification_level": "VerificationViolation" });
1582
1583        // When we deserialize it
1584        let deserialized: Container = serde_json::from_value(container)
1585            .expect("We can deserialize the old PreviouslyVerified value");
1586
1587        // Then it is populated correctly
1588        assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1589    }
1590
1591    #[test]
1592    fn test_verification_level_deserializes_from_old_previously_verified_value() {
1593        // Given a JSON VerificationLevel with the old value PreviouslyVerified
1594        #[derive(Deserialize)]
1595        struct Container {
1596            verification_level: VerificationLevel,
1597        }
1598        let container = json!({ "verification_level": "PreviouslyVerified" });
1599
1600        // When we deserialize it
1601        let deserialized: Container = serde_json::from_value(container)
1602            .expect("We can deserialize the old PreviouslyVerified value");
1603
1604        // Then it is migrated to the new value
1605        assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1606    }
1607
1608    #[test]
1609    fn test_shield_state_code_deserializes() {
1610        // Given a JSON ShieldStateCode with value VerificationViolation
1611        #[derive(Deserialize)]
1612        struct Container {
1613            shield_state_code: ShieldStateCode,
1614        }
1615        let container = json!({ "shield_state_code": "VerificationViolation" });
1616
1617        // When we deserialize it
1618        let deserialized: Container = serde_json::from_value(container)
1619            .expect("We can deserialize the old PreviouslyVerified value");
1620
1621        // Then it is populated correctly
1622        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        // Given a JSON ShieldStateCode with the old value PreviouslyVerified
1628        #[derive(Deserialize)]
1629        struct Container {
1630            shield_state_code: ShieldStateCode,
1631        }
1632        let container = json!({ "shield_state_code": "PreviouslyVerified" });
1633
1634        // When we deserialize it
1635        let deserialized: Container = serde_json::from_value(container)
1636            .expect("We can deserialize the old PreviouslyVerified value");
1637
1638        // Then it is migrated to the new value
1639        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        // Test that the serialization is as expected
1675        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        // And it can be properly deserialized from the new format.
1714        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        // Test that the previous format can also be deserialized.
1724        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        // Test that the previous format, with an undecryptable unsigned event, can also
1755        // be deserialized.
1756        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        // When creating a timeline event from a raw event, the thread summary is always
1834        // extracted, if available.
1835        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        // When deserializing an old serialized timeline event, the thread summary is
1844        // also extracted, if it wasn't serialized.
1845        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        // The bundled latest thread event is not persisted, so it should be `None` when
1858        // deserialized from a previously serialized `TimelineEvent`.
1859        assert!(timeline_event.bundled_latest_thread_event.is_none());
1860    }
1861
1862    #[test]
1863    fn sync_timeline_event_deserialisation_migration_for_withheld() {
1864        // Old serialized version was
1865        //    "utd_info": {
1866        //         "reason": "MissingMegolmSession",
1867        //         "session_id": "session000"
1868        //       }
1869
1870        // The new version would be
1871        //      "utd_info": {
1872        //         "reason": {
1873        //           "MissingMegolmSession": {
1874        //              "withheld_code": null
1875        //           }
1876        //         },
1877        //         "session_id": "session000"
1878        //       }
1879
1880        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        // should have migrated to the new format
1909        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        // In the old format the session_id was in the EncryptionInfo, now
2045        // it is moved to the `algorithm_info` struct.
2046        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            // We use directly the serde_json formatter here, because of a bug in insta
2135            // not serializing custom BTreeMap key enum https://github.com/mitsuhiko/insta/issues/689
2136            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}