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 `/sync`,
469/// after initial processing.
470///
471/// Previously, this differed from [`TimelineEvent`] by wrapping an
472/// [`AnySyncTimelineEvent`] instead of an [`AnyTimelineEvent`], but nowadays
473/// they are essentially identical, and one of them should probably be removed.
474//
475// 🚨 Note about this type, please read! 🚨
476//
477// `TimelineEvent` is heavily used across the SDK crates. In some cases, we
478// are reaching a [`recursion_limit`] when the compiler is trying to figure out
479// if `TimelineEvent` implements `Sync` when it's embedded in other types.
480//
481// We want to help the compiler so that one doesn't need to increase the
482// `recursion_limit`. We stop the recursive check by (un)safely implement `Sync`
483// and `Send` on `TimelineEvent` directly.
484//
485// See
486// https://github.com/matrix-org/matrix-rust-sdk/pull/3749#issuecomment-2312939823
487// which has addressed this issue first
488//
489// [`recursion_limit`]: https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute
490#[derive(Clone, Debug, Serialize)]
491pub struct TimelineEvent {
492    /// The event itself, together with any information on decryption.
493    pub kind: TimelineEventKind,
494
495    /// The timestamp of the event. It's the `origin_server_ts` value (if any),
496    /// corrected if detected as malicious.
497    ///
498    /// It can be `None` if the event has been serialised before the addition of
499    /// this field, or if parsing the `origin_server_ts` value failed.
500    pub timestamp: Option<MilliSecondsSinceUnixEpoch>,
501
502    /// The push actions associated with this event.
503    ///
504    /// If it's set to `None`, then it means we couldn't compute those actions,
505    /// or that they could be computed but there were none.
506    #[serde(skip_serializing_if = "skip_serialize_push_actions")]
507    push_actions: Option<Vec<Action>>,
508
509    /// If the event is part of a thread, a thread summary.
510    #[serde(default, skip_serializing_if = "ThreadSummaryStatus::is_unknown")]
511    pub thread_summary: ThreadSummaryStatus,
512
513    /// The bundled latest thread event, if it was provided in the unsigned
514    /// relations of this event.
515    ///
516    /// Not serialized.
517    #[serde(skip)]
518    pub bundled_latest_thread_event: Option<Box<TimelineEvent>>,
519}
520
521// Don't serialize push actions if they're `None` or an empty vec.
522fn skip_serialize_push_actions(push_actions: &Option<Vec<Action>>) -> bool {
523    push_actions.as_ref().is_none_or(|v| v.is_empty())
524}
525
526// See https://github.com/matrix-org/matrix-rust-sdk/pull/3749#issuecomment-2312939823.
527#[cfg(not(feature = "test-send-sync"))]
528unsafe impl Send for TimelineEvent {}
529
530// See https://github.com/matrix-org/matrix-rust-sdk/pull/3749#issuecomment-2312939823.
531#[cfg(not(feature = "test-send-sync"))]
532unsafe impl Sync for TimelineEvent {}
533
534#[cfg(feature = "test-send-sync")]
535#[test]
536// See https://github.com/matrix-org/matrix-rust-sdk/pull/3749#issuecomment-2312939823.
537fn test_send_sync_for_sync_timeline_event() {
538    fn assert_send_sync<T: crate::SendOutsideWasm + crate::SyncOutsideWasm>() {}
539
540    assert_send_sync::<TimelineEvent>();
541}
542
543impl TimelineEvent {
544    /// Create a new [`TimelineEvent`] from the given raw event.
545    ///
546    /// This is a convenience constructor for a plaintext event when you don't
547    /// need to set `push_action`, for example inside a test.
548    pub fn from_plaintext(event: Raw<AnySyncTimelineEvent>) -> Self {
549        Self::from_plaintext_with_max_timestamp(event, MilliSecondsSinceUnixEpoch::now())
550    }
551
552    /// Like [`TimelineEvent::from_plaintext`] but with a given `max_timestamp`.
553    pub fn from_plaintext_with_max_timestamp(
554        event: Raw<AnySyncTimelineEvent>,
555        max_timestamp: MilliSecondsSinceUnixEpoch,
556    ) -> Self {
557        Self::new(TimelineEventKind::PlainText { event }, None, max_timestamp)
558    }
559
560    /// Create a new [`TimelineEvent`] from a decrypted event.
561    pub fn from_decrypted(
562        decrypted: DecryptedRoomEvent,
563        push_actions: Option<Vec<Action>>,
564    ) -> Self {
565        Self::from_decrypted_with_max_timestamp(
566            decrypted,
567            push_actions,
568            MilliSecondsSinceUnixEpoch::now(),
569        )
570    }
571
572    /// Like [`TimelineEvent::from_decrypted`] but with a given `max_timestamp`.
573    pub fn from_decrypted_with_max_timestamp(
574        decrypted: DecryptedRoomEvent,
575        push_actions: Option<Vec<Action>>,
576        max_timestamp: MilliSecondsSinceUnixEpoch,
577    ) -> Self {
578        Self::new(TimelineEventKind::Decrypted(decrypted), push_actions, max_timestamp)
579    }
580
581    /// Create a new [`TimelineEvent`] to represent the given decryption
582    /// failure.
583    pub fn from_utd(event: Raw<AnySyncTimelineEvent>, utd_info: UnableToDecryptInfo) -> Self {
584        Self::from_utd_with_max_timestamp(event, utd_info, MilliSecondsSinceUnixEpoch::now())
585    }
586
587    /// Like [`TimelineEvent::from_utd`] but with a given `max_timestamp`.
588    pub fn from_utd_with_max_timestamp(
589        event: Raw<AnySyncTimelineEvent>,
590        utd_info: UnableToDecryptInfo,
591        max_timestamp: MilliSecondsSinceUnixEpoch,
592    ) -> Self {
593        Self::new(TimelineEventKind::UnableToDecrypt { event, utd_info }, None, max_timestamp)
594    }
595
596    /// Internal only: helps extracting a thread summary and latest thread event
597    /// when creating a new [`TimelineEvent`].
598    ///
599    /// Build the `timestamp` value by using `now()` as the max value.
600    fn new(
601        kind: TimelineEventKind,
602        push_actions: Option<Vec<Action>>,
603        max_timestamp: MilliSecondsSinceUnixEpoch,
604    ) -> Self {
605        let raw = kind.raw();
606
607        let (thread_summary, latest_thread_event) = extract_bundled_thread_summary(raw);
608
609        let bundled_latest_thread_event =
610            Self::from_bundled_latest_event(&kind, latest_thread_event, max_timestamp);
611
612        let timestamp = extract_timestamp(raw, max_timestamp);
613
614        Self { kind, push_actions, timestamp, thread_summary, bundled_latest_thread_event }
615    }
616
617    /// Transform this [`TimelineEvent`] into another [`TimelineEvent`] with the
618    /// [`TimelineEventKind::Decrypted`] kind.
619    ///
620    /// ## Panics
621    ///
622    /// It panics (on debug builds only) if the kind already is
623    /// [`TimelineEventKind::Decrypted`].
624    pub fn to_decrypted(
625        &self,
626        decrypted: DecryptedRoomEvent,
627        push_actions: Option<Vec<Action>>,
628    ) -> Self {
629        debug_assert!(
630            matches!(self.kind, TimelineEventKind::Decrypted(_)).not(),
631            "`TimelineEvent::to_decrypted` has been called on an already decrypted `TimelineEvent`."
632        );
633
634        Self {
635            kind: TimelineEventKind::Decrypted(decrypted),
636            timestamp: self.timestamp,
637            push_actions,
638            thread_summary: self.thread_summary.clone(),
639            bundled_latest_thread_event: self.bundled_latest_thread_event.clone(),
640        }
641    }
642
643    /// Transform this [`TimelineEvent`] into another [`TimelineEvent`] with the
644    /// [`TimelineEventKind::Decrypted`] kind.
645    ///
646    /// ## Panics
647    ///
648    /// It panics (on debug builds only) if the kind already is
649    /// [`TimelineEventKind::Decrypted`].
650    pub fn to_utd(&self, utd_info: UnableToDecryptInfo) -> Self {
651        debug_assert!(
652            matches!(self.kind, TimelineEventKind::UnableToDecrypt { .. }).not(),
653            "`TimelineEvent::to_utd` has been called on an already UTD `TimelineEvent`."
654        );
655
656        Self {
657            kind: TimelineEventKind::UnableToDecrypt { event: self.raw().clone(), utd_info },
658            timestamp: self.timestamp,
659            push_actions: None,
660            thread_summary: self.thread_summary.clone(),
661            bundled_latest_thread_event: self.bundled_latest_thread_event.clone(),
662        }
663    }
664
665    /// Try to create a new [`TimelineEvent`] for the bundled latest thread
666    /// event, if available, and if we have enough information about the
667    /// encryption status for it.
668    fn from_bundled_latest_event(
669        kind: &TimelineEventKind,
670        latest_event: Option<Raw<AnySyncMessageLikeEvent>>,
671        max_timestamp: MilliSecondsSinceUnixEpoch,
672    ) -> Option<Box<Self>> {
673        let latest_event = latest_event?;
674
675        match kind {
676            TimelineEventKind::Decrypted(decrypted) => {
677                if let Some(unsigned_decryption_result) =
678                    decrypted.unsigned_encryption_info.as_ref().and_then(|unsigned_map| {
679                        unsigned_map.get(&UnsignedEventLocation::RelationsThreadLatestEvent)
680                    })
681                {
682                    match unsigned_decryption_result {
683                        UnsignedDecryptionResult::Decrypted(encryption_info) => {
684                            // The bundled event was encrypted, and we could decrypt it: pass that
685                            // information around.
686                            return Some(Box::new(
687                                TimelineEvent::from_decrypted_with_max_timestamp(
688                                    DecryptedRoomEvent {
689                                        // Safety: A decrypted event always includes a room_id in
690                                        // its payload.
691                                        event: latest_event.cast_unchecked(),
692                                        encryption_info: encryption_info.clone(),
693                                        // A bundled latest event is never a thread root. It could
694                                        // have
695                                        // a replacement event, but we don't carry this information
696                                        // around.
697                                        unsigned_encryption_info: None,
698                                    },
699                                    None,
700                                    max_timestamp,
701                                ),
702                            ));
703                        }
704
705                        UnsignedDecryptionResult::UnableToDecrypt(utd_info) => {
706                            // The bundled event was a UTD; store that information.
707                            return Some(Box::new(TimelineEvent::from_utd_with_max_timestamp(
708                                latest_event.cast(),
709                                utd_info.clone(),
710                                max_timestamp,
711                            )));
712                        }
713                    }
714                }
715            }
716
717            TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => {
718                // Figure based on the event type below.
719            }
720        }
721
722        match latest_event.get_field::<MessageLikeEventType>("type") {
723            Ok(None) => {
724                let event_id = latest_event.get_field::<OwnedEventId>("event_id").ok().flatten();
725                warn!(
726                    ?event_id,
727                    "couldn't deserialize bundled latest thread event: missing `type` field \
728                     in bundled latest thread event"
729                );
730                None
731            }
732
733            Ok(Some(MessageLikeEventType::RoomEncrypted)) => {
734                // The bundled latest thread event is encrypted, but we didn't have any
735                // information about it in the unsigned map. Try to fetch the information from
736                // the content instead.
737                let session_id = if let Some(content) =
738                    latest_event.get_field::<EncryptedEventScheme>("content").ok().flatten()
739                {
740                    match content {
741                        EncryptedEventScheme::MegolmV1AesSha2(content) => Some(content.session_id),
742                        _ => None,
743                    }
744                } else {
745                    None
746                };
747                Some(Box::new(TimelineEvent::from_utd_with_max_timestamp(
748                    latest_event.cast(),
749                    UnableToDecryptInfo { session_id, reason: UnableToDecryptReason::Unknown },
750                    max_timestamp,
751                )))
752            }
753
754            Ok(_) => Some(Box::new(TimelineEvent::from_plaintext_with_max_timestamp(
755                latest_event.cast(),
756                max_timestamp,
757            ))),
758
759            Err(err) => {
760                let event_id = latest_event.get_field::<OwnedEventId>("event_id").ok().flatten();
761                warn!(?event_id, "couldn't deserialize bundled latest thread event's type: {err}");
762                None
763            }
764        }
765    }
766
767    /// Read the current push actions.
768    ///
769    /// Returns `None` if they were never computed, or if they could not be
770    /// computed.
771    pub fn push_actions(&self) -> Option<&[Action]> {
772        self.push_actions.as_deref()
773    }
774
775    /// Set the push actions for this event.
776    pub fn set_push_actions(&mut self, push_actions: Vec<Action>) {
777        self.push_actions = Some(push_actions);
778    }
779
780    /// Get the event id of this [`TimelineEvent`] if the event has any valid
781    /// id.
782    pub fn event_id(&self) -> Option<OwnedEventId> {
783        self.kind.event_id()
784    }
785
786    /// Returns a reference to the (potentially decrypted) Matrix event inside
787    /// this [`TimelineEvent`].
788    pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
789        self.kind.raw()
790    }
791
792    /// Replace the raw event included in this item by another one.
793    pub fn replace_raw(&mut self, replacement: Raw<AnyTimelineEvent>) {
794        match &mut self.kind {
795            TimelineEventKind::Decrypted(decrypted) => decrypted.event = replacement,
796            TimelineEventKind::UnableToDecrypt { event, .. }
797            | TimelineEventKind::PlainText { event } => {
798                // It's safe to cast `AnyMessageLikeEvent` into `AnySyncMessageLikeEvent`,
799                // because the former contains a superset of the fields included in the latter.
800                *event = replacement.cast();
801            }
802        }
803    }
804
805    /// Get the timestamp.
806    ///
807    /// If the timestamp is missing (most likely because the event has been
808    /// created before the addition of the [`TimelineEvent::timestamp`] field),
809    /// this method will try to extract it from the `origin_server_ts` value. If
810    /// the `origin_server_ts` value is malicious, it will be capped to
811    /// [`MilliSecondsSinceUnixEpoch::now`]. It means that the returned value
812    /// might not be constant.
813    pub fn timestamp(&self) -> Option<MilliSecondsSinceUnixEpoch> {
814        self.timestamp.or_else(|| {
815            warn!("`TimelineEvent::timestamp` is parsing the raw event to extract the `timestamp`");
816
817            extract_timestamp(self.raw(), MilliSecondsSinceUnixEpoch::now())
818        })
819    }
820
821    /// Get the timestamp value, without trying to backfill it if `None`.
822    pub fn timestamp_raw(&self) -> Option<MilliSecondsSinceUnixEpoch> {
823        self.timestamp
824    }
825
826    /// If the event was a decrypted event that was successfully decrypted, get
827    /// its encryption info. Otherwise, `None`.
828    pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
829        self.kind.encryption_info()
830    }
831
832    /// Takes ownership of this [`TimelineEvent`], returning the (potentially
833    /// decrypted) Matrix event within.
834    pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
835        self.kind.into_raw()
836    }
837}
838
839impl<'de> Deserialize<'de> for TimelineEvent {
840    /// Custom deserializer for [`TimelineEvent`], to support older formats.
841    ///
842    /// Ideally we might use an untagged enum and then convert from that;
843    /// however, that doesn't work due to a [serde bug](https://github.com/serde-rs/json/issues/497).
844    ///
845    /// Instead, we first deserialize into an unstructured JSON map, and then
846    /// inspect the json to figure out which format we have.
847    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
848    where
849        D: serde::Deserializer<'de>,
850    {
851        use serde_json::{Map, Value};
852
853        // First, deserialize to an unstructured JSON map
854        let value = Map::<String, Value>::deserialize(deserializer)?;
855
856        // If we have a top-level `event`, it's V0
857        if value.contains_key("event") {
858            let v0: SyncTimelineEventDeserializationHelperV0 =
859                serde_json::from_value(Value::Object(value)).map_err(|e| {
860                    serde::de::Error::custom(format!(
861                        "Unable to deserialize V0-format TimelineEvent: {e}",
862                    ))
863                })?;
864            Ok(v0.into())
865        }
866        // Otherwise, it's V1
867        else {
868            let v1: SyncTimelineEventDeserializationHelperV1 =
869                serde_json::from_value(Value::Object(value)).map_err(|e| {
870                    serde::de::Error::custom(format!(
871                        "Unable to deserialize V1-format TimelineEvent: {e}",
872                    ))
873                })?;
874            Ok(v1.into())
875        }
876    }
877}
878
879/// The event within a [`TimelineEvent`], together with encryption data.
880#[derive(Clone, Serialize, Deserialize)]
881pub enum TimelineEventKind {
882    /// A successfully-decrypted encrypted event.
883    Decrypted(DecryptedRoomEvent),
884
885    /// An encrypted event which could not be decrypted.
886    UnableToDecrypt {
887        /// The `m.room.encrypted` event. Depending on the source of the event,
888        /// it could actually be an [`AnyTimelineEvent`] (i.e., it may
889        /// have a `room_id` property).
890        event: Raw<AnySyncTimelineEvent>,
891
892        /// Information on the reason we failed to decrypt
893        utd_info: UnableToDecryptInfo,
894    },
895
896    /// An unencrypted event.
897    PlainText {
898        /// The actual event. Depending on the source of the event, it could
899        /// actually be a [`AnyTimelineEvent`] (which differs from
900        /// [`AnySyncTimelineEvent`] by the addition of a `room_id` property).
901        event: Raw<AnySyncTimelineEvent>,
902    },
903}
904
905impl TimelineEventKind {
906    /// Returns a reference to the (potentially decrypted) Matrix event inside
907    /// this `TimelineEvent`.
908    pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
909        match self {
910            // It is safe to cast from an `AnyMessageLikeEvent` (i.e. JSON which does
911            // *not* contain a `state_key` and *does* contain a `room_id`) into an
912            // `AnySyncTimelineEvent` (i.e. JSON which *may* contain a `state_key` and is *not*
913            // expected to contain a `room_id`). It just means that the `room_id` will be ignored
914            // in a future deserialization.
915            TimelineEventKind::Decrypted(d) => d.event.cast_ref(),
916            TimelineEventKind::UnableToDecrypt { event, .. } => event,
917            TimelineEventKind::PlainText { event } => event,
918        }
919    }
920
921    /// Get the event id of this `TimelineEventKind` if the event has any valid
922    /// id.
923    pub fn event_id(&self) -> Option<OwnedEventId> {
924        self.raw().get_field::<OwnedEventId>("event_id").ok().flatten()
925    }
926
927    /// Whether we could not decrypt the event (i.e. it is a UTD).
928    pub fn is_utd(&self) -> bool {
929        matches!(self, TimelineEventKind::UnableToDecrypt { .. })
930    }
931
932    /// If the event was a decrypted event that was successfully decrypted, get
933    /// its encryption info. Otherwise, `None`.
934    pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
935        match self {
936            TimelineEventKind::Decrypted(d) => Some(&d.encryption_info),
937            TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => None,
938        }
939    }
940
941    /// If the event was a decrypted event that was successfully decrypted, get
942    /// the map of decryption metadata related to the bundled events.
943    pub fn unsigned_encryption_map(
944        &self,
945    ) -> Option<&BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>> {
946        match self {
947            TimelineEventKind::Decrypted(d) => d.unsigned_encryption_info.as_ref(),
948            TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => None,
949        }
950    }
951
952    /// Takes ownership of this `TimelineEvent`, returning the (potentially
953    /// decrypted) Matrix event within.
954    pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
955        match self {
956            // It is safe to cast from an `AnyMessageLikeEvent` (i.e. JSON which does
957            // *not* contain a `state_key` and *does* contain a `room_id`) into an
958            // `AnySyncTimelineEvent` (i.e. JSON which *may* contain a `state_key` and is *not*
959            // expected to contain a `room_id`). It just means that the `room_id` will be ignored
960            // in a future deserialization.
961            TimelineEventKind::Decrypted(d) => d.event.cast(),
962            TimelineEventKind::UnableToDecrypt { event, .. } => event,
963            TimelineEventKind::PlainText { event } => event,
964        }
965    }
966
967    /// The Megolm session ID that was used to send this event, if it was
968    /// encrypted.
969    pub fn session_id(&self) -> Option<&str> {
970        match self {
971            TimelineEventKind::Decrypted(decrypted_room_event) => {
972                decrypted_room_event.encryption_info.session_id()
973            }
974            TimelineEventKind::UnableToDecrypt { utd_info, .. } => utd_info.session_id.as_deref(),
975            TimelineEventKind::PlainText { .. } => None,
976        }
977    }
978
979    /// Get the event type of this event.
980    ///
981    /// Returns `None` if there isn't an event type or if the event failed to be
982    /// deserialized.
983    pub fn event_type(&self) -> Option<String> {
984        self.raw().get_field("type").ok().flatten()
985    }
986}
987
988#[cfg(not(tarpaulin_include))]
989impl fmt::Debug for TimelineEventKind {
990    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
991        match &self {
992            Self::PlainText { event } => f
993                .debug_struct("TimelineEventKind::PlainText")
994                .field("event", &DebugRawEvent(event))
995                .finish(),
996
997            Self::UnableToDecrypt { event, utd_info } => f
998                .debug_struct("TimelineEventKind::UnableToDecrypt")
999                .field("event", &DebugRawEvent(event))
1000                .field("utd_info", &utd_info)
1001                .finish(),
1002
1003            Self::Decrypted(decrypted) => {
1004                f.debug_tuple("TimelineEventKind::Decrypted").field(decrypted).finish()
1005            }
1006        }
1007    }
1008}
1009
1010#[derive(Clone, Serialize, Deserialize)]
1011/// A successfully-decrypted encrypted event.
1012pub struct DecryptedRoomEvent {
1013    /// The decrypted event.
1014    ///
1015    /// Note: it's not an error that this contains an [`AnyTimelineEvent`]
1016    /// (as opposed to an [`AnySyncTimelineEvent`]): an
1017    /// encrypted payload *always contains* a room id, by the [spec].
1018    ///
1019    /// [spec]: https://spec.matrix.org/v1.12/client-server-api/#mmegolmv1aes-sha2
1020    pub event: Raw<AnyTimelineEvent>,
1021
1022    /// The encryption info about the event.
1023    pub encryption_info: Arc<EncryptionInfo>,
1024
1025    /// The encryption info about the events bundled in the `unsigned`
1026    /// object.
1027    ///
1028    /// Will be `None` if no bundled event was encrypted.
1029    #[serde(skip_serializing_if = "Option::is_none")]
1030    pub unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
1031}
1032
1033#[cfg(not(tarpaulin_include))]
1034impl fmt::Debug for DecryptedRoomEvent {
1035    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1036        let DecryptedRoomEvent { event, encryption_info, unsigned_encryption_info } = self;
1037
1038        f.debug_struct("DecryptedRoomEvent")
1039            .field("event", &DebugRawEvent(event))
1040            .field("encryption_info", encryption_info)
1041            .maybe_field("unsigned_encryption_info", unsigned_encryption_info)
1042            .finish()
1043    }
1044}
1045
1046/// The location of an event bundled in an `unsigned` object.
1047#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
1048pub enum UnsignedEventLocation {
1049    /// An event at the `m.replace` key of the `m.relations` object, that is a
1050    /// bundled replacement.
1051    RelationsReplace,
1052    /// An event at the `latest_event` key of the `m.thread` object of the
1053    /// `m.relations` object, that is the latest event of a thread.
1054    RelationsThreadLatestEvent,
1055}
1056
1057impl UnsignedEventLocation {
1058    /// Find the mutable JSON value at this location in the given unsigned
1059    /// object.
1060    ///
1061    /// # Arguments
1062    ///
1063    /// * `unsigned` - The `unsigned` property of an event as a JSON object.
1064    pub fn find_mut<'a>(&self, unsigned: &'a mut JsonObject) -> Option<&'a mut serde_json::Value> {
1065        let relations = unsigned.get_mut("m.relations")?.as_object_mut()?;
1066
1067        match self {
1068            Self::RelationsReplace => relations.get_mut("m.replace"),
1069            Self::RelationsThreadLatestEvent => {
1070                relations.get_mut("m.thread")?.as_object_mut()?.get_mut("latest_event")
1071            }
1072        }
1073    }
1074}
1075
1076/// The result of the decryption of an event bundled in an `unsigned` object.
1077#[derive(Debug, Clone, Serialize, Deserialize)]
1078pub enum UnsignedDecryptionResult {
1079    /// The event was successfully decrypted.
1080    Decrypted(Arc<EncryptionInfo>),
1081    /// The event failed to be decrypted.
1082    UnableToDecrypt(UnableToDecryptInfo),
1083}
1084
1085impl UnsignedDecryptionResult {
1086    /// Returns the encryption info for this bundled event if it was
1087    /// successfully decrypted.
1088    pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
1089        match self {
1090            Self::Decrypted(info) => Some(info),
1091            Self::UnableToDecrypt(_) => None,
1092        }
1093    }
1094}
1095
1096/// Metadata about an event that could not be decrypted.
1097#[derive(Debug, Clone, Serialize, Deserialize)]
1098pub struct UnableToDecryptInfo {
1099    /// The ID of the session used to encrypt the message, if it used the
1100    /// `m.megolm.v1.aes-sha2` algorithm.
1101    #[serde(skip_serializing_if = "Option::is_none")]
1102    pub session_id: Option<String>,
1103
1104    /// Reason code for the decryption failure
1105    #[serde(default = "unknown_utd_reason", deserialize_with = "deserialize_utd_reason")]
1106    pub reason: UnableToDecryptReason,
1107}
1108
1109fn unknown_utd_reason() -> UnableToDecryptReason {
1110    UnableToDecryptReason::Unknown
1111}
1112
1113/// Provides basic backward compatibility for deserializing older serialized
1114/// `UnableToDecryptReason` values.
1115pub fn deserialize_utd_reason<'de, D>(d: D) -> Result<UnableToDecryptReason, D::Error>
1116where
1117    D: serde::Deserializer<'de>,
1118{
1119    // Start by deserializing as to an untyped JSON value.
1120    let v: serde_json::Value = Deserialize::deserialize(d)?;
1121    // Backwards compatibility: `MissingMegolmSession` used to be stored without the
1122    // withheld code.
1123    if v.as_str().is_some_and(|s| s == "MissingMegolmSession") {
1124        return Ok(UnableToDecryptReason::MissingMegolmSession { withheld_code: None });
1125    }
1126    // Otherwise, use the derived deserialize impl to turn the JSON into a
1127    // UnableToDecryptReason
1128    serde_json::from_value::<UnableToDecryptReason>(v).map_err(serde::de::Error::custom)
1129}
1130
1131/// Reason code for a decryption failure
1132#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1133pub enum UnableToDecryptReason {
1134    /// The reason for the decryption failure is unknown. This is only intended
1135    /// for use when deserializing old UnableToDecryptInfo instances.
1136    #[doc(hidden)]
1137    Unknown,
1138
1139    /// The `m.room.encrypted` event that should have been decrypted is
1140    /// malformed in some way (e.g. unsupported algorithm, missing fields,
1141    /// unknown megolm message type).
1142    MalformedEncryptedEvent,
1143
1144    /// Decryption failed because we're missing the megolm session that was used
1145    /// to encrypt the event.
1146    MissingMegolmSession {
1147        /// If the key was withheld on purpose, the associated code. `None`
1148        /// means no withheld code was received.
1149        withheld_code: Option<WithheldCode>,
1150    },
1151
1152    /// Decryption failed because, while we have the megolm session that was
1153    /// used to encrypt the message, it is ratcheted too far forward.
1154    UnknownMegolmMessageIndex,
1155
1156    /// We found the Megolm session, but were unable to decrypt the event using
1157    /// that session for some reason (e.g. incorrect MAC).
1158    ///
1159    /// This represents all `vodozemac::megolm::DecryptionError`s, except
1160    /// `UnknownMessageIndex`, which is represented as
1161    /// `UnknownMegolmMessageIndex`.
1162    MegolmDecryptionFailure,
1163
1164    /// The event could not be deserialized after decryption.
1165    PayloadDeserializationFailure,
1166
1167    /// Decryption failed because of a mismatch between the identity keys of the
1168    /// device we received the room key from and the identity keys recorded in
1169    /// the plaintext of the room key to-device message.
1170    MismatchedIdentityKeys,
1171
1172    /// An encrypted message wasn't decrypted, because the sender's
1173    /// cross-signing identity did not satisfy the requested
1174    /// `TrustRequirement`.
1175    SenderIdentityNotTrusted(VerificationLevel),
1176
1177    /// The outer state key could not be verified against the inner encrypted
1178    /// state key and type.
1179    #[cfg(feature = "experimental-encrypted-state-events")]
1180    StateKeyVerificationFailed,
1181}
1182
1183impl UnableToDecryptReason {
1184    /// Returns true if this UTD is due to a missing room key (and hence might
1185    /// resolve itself if we wait a bit.)
1186    pub fn is_missing_room_key(&self) -> bool {
1187        // In case of MissingMegolmSession with a withheld code we return false here
1188        // given that this API is used to decide if waiting a bit will help.
1189        matches!(
1190            self,
1191            Self::MissingMegolmSession { withheld_code: None } | Self::UnknownMegolmMessageIndex
1192        )
1193    }
1194}
1195
1196/// A machine-readable code for why a Megolm key was not sent.
1197///
1198/// 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.
1199#[derive(
1200    Clone,
1201    PartialEq,
1202    Eq,
1203    Hash,
1204    AsStrAsRefStr,
1205    AsRefStr,
1206    FromString,
1207    DebugAsRefStr,
1208    SerializeAsRefStr,
1209    DeserializeFromCowStr,
1210)]
1211pub enum WithheldCode {
1212    /// the user/device was blacklisted.
1213    #[ruma_enum(rename = "m.blacklisted")]
1214    Blacklisted,
1215
1216    /// the user/devices is unverified.
1217    #[ruma_enum(rename = "m.unverified")]
1218    Unverified,
1219
1220    /// The user/device is not allowed have the key. For example, this would
1221    /// usually be sent in response to a key request if the user was not in
1222    /// the room when the message was sent.
1223    #[ruma_enum(rename = "m.unauthorised")]
1224    Unauthorised,
1225
1226    /// Sent in reply to a key request if the device that the key is requested
1227    /// from does not have the requested key.
1228    #[ruma_enum(rename = "m.unavailable")]
1229    Unavailable,
1230
1231    /// An olm session could not be established.
1232    /// This may happen, for example, if the sender was unable to obtain a
1233    /// one-time key from the recipient.
1234    #[ruma_enum(rename = "m.no_olm")]
1235    NoOlm,
1236
1237    /// Normally used when sharing history, per [MSC4268]: indicates
1238    /// that the session was not marked as "shared_history".
1239    ///
1240    /// [MSC4268]: https://github.com/matrix-org/matrix-spec-proposals/pull/4268
1241    #[ruma_enum(rename = "io.element.msc4268.history_not_shared", alias = "m.history_not_shared")]
1242    HistoryNotShared,
1243
1244    #[doc(hidden)]
1245    _Custom(PrivOwnedStr),
1246}
1247
1248impl fmt::Display for WithheldCode {
1249    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1250        let string = match self {
1251            WithheldCode::Blacklisted => "The sender has blocked you.",
1252            WithheldCode::Unverified => "The sender has disabled encrypting to unverified devices.",
1253            WithheldCode::Unauthorised => "You are not authorised to read the message.",
1254            WithheldCode::Unavailable => "The requested key was not found.",
1255            WithheldCode::NoOlm => "Unable to establish a secure channel.",
1256            WithheldCode::HistoryNotShared => "The sender disabled sharing encrypted history.",
1257            _ => self.as_str(),
1258        };
1259
1260        f.write_str(string)
1261    }
1262}
1263
1264// The Ruma macro expects the type to have this name.
1265// The payload is counter intuitively made public in order to avoid having
1266// multiple copies of this struct.
1267#[doc(hidden)]
1268#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1269pub struct PrivOwnedStr(pub Box<str>);
1270
1271#[cfg(not(tarpaulin_include))]
1272impl fmt::Debug for PrivOwnedStr {
1273    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1274        self.0.fmt(f)
1275    }
1276}
1277
1278/// Deserialization helper for [`TimelineEvent`], for the modern format.
1279///
1280/// This has the exact same fields as [`TimelineEvent`] itself, but has a
1281/// regular `Deserialize` implementation.
1282#[derive(Debug, Deserialize)]
1283struct SyncTimelineEventDeserializationHelperV1 {
1284    /// The event itself, together with any information on decryption.
1285    kind: TimelineEventKind,
1286
1287    /// The timestamp of the event. It's the `origin_server_ts` value (if any),
1288    /// corrected if detected as malicious.
1289    #[serde(default)]
1290    timestamp: Option<MilliSecondsSinceUnixEpoch>,
1291
1292    /// The push actions associated with this event.
1293    #[serde(default)]
1294    push_actions: Vec<Action>,
1295
1296    /// If the event is part of a thread, a thread summary.
1297    #[serde(default)]
1298    thread_summary: ThreadSummaryStatus,
1299}
1300
1301impl From<SyncTimelineEventDeserializationHelperV1> for TimelineEvent {
1302    fn from(value: SyncTimelineEventDeserializationHelperV1) -> Self {
1303        let SyncTimelineEventDeserializationHelperV1 {
1304            kind,
1305            timestamp,
1306            push_actions,
1307            thread_summary,
1308        } = value;
1309
1310        // If `timestamp` is `None`, it is very likely that the event was serialised
1311        // before the addition of the `timestamp` field. We _could_ compute it here, but
1312        // if the `timestamp` was malicious, it means we are going to _cap_ the
1313        // `timestamp` to `now()` for every deserialisation. It is annoying because it
1314        // means the event is no longer deterministic, it's not constant.
1315        // We don't want that. Consequently, we keep `None` here, and we let
1316        // [`TimelineEvent::timestamp`] to handle that case for us.
1317
1318        TimelineEvent {
1319            kind,
1320            timestamp,
1321            push_actions: Some(push_actions),
1322            thread_summary,
1323            // Bundled latest thread event is not persisted.
1324            bundled_latest_thread_event: None,
1325        }
1326    }
1327}
1328
1329/// Deserialization helper for [`TimelineEvent`], for an older format.
1330#[derive(Deserialize)]
1331struct SyncTimelineEventDeserializationHelperV0 {
1332    /// The actual event.
1333    event: Raw<AnySyncTimelineEvent>,
1334
1335    /// The encryption info about the event.
1336    ///
1337    /// Will be `None` if the event was not encrypted.
1338    encryption_info: Option<Arc<EncryptionInfo>>,
1339
1340    /// The push actions associated with this event.
1341    #[serde(default)]
1342    push_actions: Vec<Action>,
1343
1344    /// The encryption info about the events bundled in the `unsigned`
1345    /// object.
1346    ///
1347    /// Will be `None` if no bundled event was encrypted.
1348    unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
1349}
1350
1351impl From<SyncTimelineEventDeserializationHelperV0> for TimelineEvent {
1352    fn from(value: SyncTimelineEventDeserializationHelperV0) -> Self {
1353        let SyncTimelineEventDeserializationHelperV0 {
1354            event,
1355            encryption_info,
1356            push_actions,
1357            unsigned_encryption_info,
1358        } = value;
1359
1360        // We do not compute the `timestamp` value here because if the `timestamp` is
1361        // malicious, it means we are going to _cap_ the `timestamp` to `now()` for
1362        // every deserialisation. It is annoying because it means the event is no longer
1363        // deterministic, it's not constant. We don't want that. Consequently, we keep
1364        // `None` here, and we let [`TimelineEvent::timestamp`] to handle that case for
1365        // us.
1366        let timestamp = None;
1367
1368        let kind = match encryption_info {
1369            Some(encryption_info) => {
1370                TimelineEventKind::Decrypted(DecryptedRoomEvent {
1371                    // We cast from `Raw<AnySyncTimelineEvent>` to
1372                    // `Raw<AnyMessageLikeEvent>`, which means
1373                    // we are asserting that it contains a room_id.
1374                    // That *should* be ok, because if this is genuinely a decrypted
1375                    // room event (as the encryption_info indicates), then it will have
1376                    // a room_id.
1377                    event: event.cast_unchecked(),
1378                    encryption_info,
1379                    unsigned_encryption_info,
1380                })
1381            }
1382
1383            None => TimelineEventKind::PlainText { event },
1384        };
1385
1386        TimelineEvent {
1387            kind,
1388            timestamp,
1389            push_actions: Some(push_actions),
1390            // No serialized events had a thread summary at this version of the struct.
1391            thread_summary: ThreadSummaryStatus::Unknown,
1392            // Bundled latest thread event is not persisted.
1393            bundled_latest_thread_event: None,
1394        }
1395    }
1396}
1397
1398/// Reason code for a to-device decryption failure
1399#[derive(Debug, Clone, PartialEq)]
1400pub enum ToDeviceUnableToDecryptReason {
1401    /// An error occurred while encrypting the event. This covers all
1402    /// `OlmError` types.
1403    DecryptionFailure,
1404
1405    /// We refused to decrypt the message because the sender's device is not
1406    /// verified, or more generally, the sender's identity did not match the
1407    /// trust requirement we were asked to provide.
1408    UnverifiedSenderDevice,
1409
1410    /// We have no `OlmMachine`. This should not happen unless we forget to set
1411    /// things up by calling `OlmMachine::activate()`.
1412    NoOlmMachine,
1413
1414    /// The Matrix SDK was compiled without encryption support.
1415    EncryptionIsDisabled,
1416}
1417
1418/// Metadata about a to-device event that could not be decrypted.
1419#[derive(Clone, Debug)]
1420pub struct ToDeviceUnableToDecryptInfo {
1421    /// Reason code for the decryption failure
1422    pub reason: ToDeviceUnableToDecryptReason,
1423}
1424
1425/// Represents a to-device event after it has been processed by the Olm machine.
1426#[derive(Clone, Debug)]
1427pub enum ProcessedToDeviceEvent {
1428    /// A successfully-decrypted encrypted event.
1429    /// Contains the raw decrypted event and encryption info
1430    Decrypted {
1431        /// The raw decrypted event
1432        raw: Raw<AnyToDeviceEvent>,
1433        /// The Olm encryption info
1434        encryption_info: EncryptionInfo,
1435    },
1436
1437    /// An encrypted event which could not be decrypted.
1438    UnableToDecrypt {
1439        encrypted_event: Raw<AnyToDeviceEvent>,
1440        utd_info: ToDeviceUnableToDecryptInfo,
1441    },
1442
1443    /// An unencrypted event.
1444    PlainText(Raw<AnyToDeviceEvent>),
1445
1446    /// An invalid to device event that was ignored because it is missing some
1447    /// required information to be processed (like no event `type` for
1448    /// example)
1449    Invalid(Raw<AnyToDeviceEvent>),
1450}
1451
1452impl ProcessedToDeviceEvent {
1453    /// Converts a ProcessedToDeviceEvent to the `Raw<AnyToDeviceEvent>` it
1454    /// encapsulates
1455    pub fn to_raw(&self) -> Raw<AnyToDeviceEvent> {
1456        match self {
1457            ProcessedToDeviceEvent::Decrypted { raw, .. } => raw.clone(),
1458            ProcessedToDeviceEvent::UnableToDecrypt { encrypted_event, .. } => {
1459                encrypted_event.clone()
1460            }
1461            ProcessedToDeviceEvent::PlainText(event) => event.clone(),
1462            ProcessedToDeviceEvent::Invalid(event) => event.clone(),
1463        }
1464    }
1465
1466    /// Gets the raw to-device event.
1467    pub fn as_raw(&self) -> &Raw<AnyToDeviceEvent> {
1468        match self {
1469            ProcessedToDeviceEvent::Decrypted { raw, .. } => raw,
1470            ProcessedToDeviceEvent::UnableToDecrypt { encrypted_event, .. } => encrypted_event,
1471            ProcessedToDeviceEvent::PlainText(event) => event,
1472            ProcessedToDeviceEvent::Invalid(event) => event,
1473        }
1474    }
1475}
1476
1477#[cfg(test)]
1478mod tests {
1479    use std::{collections::BTreeMap, sync::Arc};
1480
1481    use assert_matches::assert_matches;
1482    use assert_matches2::assert_let;
1483    use insta::{assert_json_snapshot, with_settings};
1484    use ruma::{
1485        DeviceKeyAlgorithm, MilliSecondsSinceUnixEpoch, UInt, device_id, event_id,
1486        events::{AnySyncTimelineEvent, room::message::RoomMessageEventContent},
1487        serde::Raw,
1488        user_id,
1489    };
1490    use serde::Deserialize;
1491    use serde_json::json;
1492
1493    use super::{
1494        AlgorithmInfo, DecryptedRoomEvent, DeviceLinkProblem, EncryptionInfo, ShieldState,
1495        ShieldStateCode, TimelineEvent, TimelineEventKind, UnableToDecryptInfo,
1496        UnableToDecryptReason, UnsignedDecryptionResult, UnsignedEventLocation, VerificationLevel,
1497        VerificationState, WithheldCode,
1498    };
1499    use crate::deserialized_responses::{ThreadSummary, ThreadSummaryStatus};
1500
1501    fn example_event() -> serde_json::Value {
1502        json!({
1503            "content": RoomMessageEventContent::text_plain("secret"),
1504            "type": "m.room.message",
1505            "event_id": "$xxxxx:example.org",
1506            "room_id": "!someroom:example.com",
1507            "origin_server_ts": 2189,
1508            "sender": "@carl:example.com",
1509        })
1510    }
1511
1512    #[test]
1513    fn sync_timeline_debug_content() {
1514        let room_event =
1515            TimelineEvent::from_plaintext(Raw::new(&example_event()).unwrap().cast_unchecked());
1516        let debug_s = format!("{room_event:?}");
1517        assert!(
1518            !debug_s.contains("secret"),
1519            "Debug representation contains event content!\n{debug_s}"
1520        );
1521    }
1522
1523    #[test]
1524    fn old_verification_state_to_new_migration() {
1525        #[derive(Deserialize)]
1526        struct State {
1527            state: VerificationState,
1528        }
1529
1530        let state = json!({
1531            "state": "Trusted",
1532        });
1533        let deserialized: State =
1534            serde_json::from_value(state).expect("We can deserialize the old trusted value");
1535        assert_eq!(deserialized.state, VerificationState::Verified);
1536
1537        let state = json!({
1538            "state": "UnknownDevice",
1539        });
1540
1541        let deserialized: State =
1542            serde_json::from_value(state).expect("We can deserialize the old unknown device value");
1543
1544        assert_eq!(
1545            deserialized.state,
1546            VerificationState::Unverified(VerificationLevel::None(
1547                DeviceLinkProblem::MissingDevice
1548            ))
1549        );
1550
1551        let state = json!({
1552            "state": "Untrusted",
1553        });
1554        let deserialized: State =
1555            serde_json::from_value(state).expect("We can deserialize the old trusted value");
1556
1557        assert_eq!(
1558            deserialized.state,
1559            VerificationState::Unverified(VerificationLevel::UnsignedDevice)
1560        );
1561    }
1562
1563    #[test]
1564    fn test_verification_level_deserializes() {
1565        // Given a JSON VerificationLevel
1566        #[derive(Deserialize)]
1567        struct Container {
1568            verification_level: VerificationLevel,
1569        }
1570        let container = json!({ "verification_level": "VerificationViolation" });
1571
1572        // When we deserialize it
1573        let deserialized: Container = serde_json::from_value(container)
1574            .expect("We can deserialize the old PreviouslyVerified value");
1575
1576        // Then it is populated correctly
1577        assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1578    }
1579
1580    #[test]
1581    fn test_verification_level_deserializes_from_old_previously_verified_value() {
1582        // Given a JSON VerificationLevel with the old value PreviouslyVerified
1583        #[derive(Deserialize)]
1584        struct Container {
1585            verification_level: VerificationLevel,
1586        }
1587        let container = json!({ "verification_level": "PreviouslyVerified" });
1588
1589        // When we deserialize it
1590        let deserialized: Container = serde_json::from_value(container)
1591            .expect("We can deserialize the old PreviouslyVerified value");
1592
1593        // Then it is migrated to the new value
1594        assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1595    }
1596
1597    #[test]
1598    fn test_shield_state_code_deserializes() {
1599        // Given a JSON ShieldStateCode with value VerificationViolation
1600        #[derive(Deserialize)]
1601        struct Container {
1602            shield_state_code: ShieldStateCode,
1603        }
1604        let container = json!({ "shield_state_code": "VerificationViolation" });
1605
1606        // When we deserialize it
1607        let deserialized: Container = serde_json::from_value(container)
1608            .expect("We can deserialize the old PreviouslyVerified value");
1609
1610        // Then it is populated correctly
1611        assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1612    }
1613
1614    #[test]
1615    fn test_shield_state_code_deserializes_from_old_previously_verified_value() {
1616        // Given a JSON ShieldStateCode with the old value PreviouslyVerified
1617        #[derive(Deserialize)]
1618        struct Container {
1619            shield_state_code: ShieldStateCode,
1620        }
1621        let container = json!({ "shield_state_code": "PreviouslyVerified" });
1622
1623        // When we deserialize it
1624        let deserialized: Container = serde_json::from_value(container)
1625            .expect("We can deserialize the old PreviouslyVerified value");
1626
1627        // Then it is migrated to the new value
1628        assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1629    }
1630
1631    #[test]
1632    fn sync_timeline_event_serialisation() {
1633        let room_event = TimelineEvent {
1634            kind: TimelineEventKind::Decrypted(DecryptedRoomEvent {
1635                event: Raw::new(&example_event()).unwrap().cast_unchecked(),
1636                encryption_info: Arc::new(EncryptionInfo {
1637                    sender: user_id!("@sender:example.com").to_owned(),
1638                    sender_device: None,
1639                    forwarder: None,
1640                    algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
1641                        curve25519_key: "xxx".to_owned(),
1642                        sender_claimed_keys: Default::default(),
1643                        session_id: Some("xyz".to_owned()),
1644                    },
1645                    verification_state: VerificationState::Verified,
1646                }),
1647                unsigned_encryption_info: Some(BTreeMap::from([(
1648                    UnsignedEventLocation::RelationsReplace,
1649                    UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
1650                        session_id: Some("xyz".to_owned()),
1651                        reason: UnableToDecryptReason::MalformedEncryptedEvent,
1652                    }),
1653                )])),
1654            }),
1655            timestamp: Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))),
1656            push_actions: Default::default(),
1657            thread_summary: ThreadSummaryStatus::Unknown,
1658            bundled_latest_thread_event: None,
1659        };
1660
1661        let serialized = serde_json::to_value(&room_event).unwrap();
1662
1663        // Test that the serialization is as expected
1664        assert_eq!(
1665            serialized,
1666            json!({
1667                "kind": {
1668                    "Decrypted": {
1669                        "event": {
1670                            "content": {"body": "secret", "msgtype": "m.text"},
1671                            "event_id": "$xxxxx:example.org",
1672                            "origin_server_ts": 2189,
1673                            "room_id": "!someroom:example.com",
1674                            "sender": "@carl:example.com",
1675                            "type": "m.room.message",
1676                        },
1677                        "encryption_info": {
1678                            "sender": "@sender:example.com",
1679                            "sender_device": null,
1680                            "forwarder": null,
1681                            "algorithm_info": {
1682                                "MegolmV1AesSha2": {
1683                                    "curve25519_key": "xxx",
1684                                    "sender_claimed_keys": {},
1685                                    "session_id": "xyz",
1686                                }
1687                            },
1688                            "verification_state": "Verified",
1689                        },
1690                        "unsigned_encryption_info": {
1691                            "RelationsReplace": {"UnableToDecrypt": {
1692                                "session_id": "xyz",
1693                                "reason": "MalformedEncryptedEvent",
1694                            }}
1695                        }
1696                    }
1697                },
1698                "timestamp": 2189,
1699            })
1700        );
1701
1702        // And it can be properly deserialized from the new format.
1703        let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1704        assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1705        assert_matches!(
1706            event.encryption_info().unwrap().algorithm_info,
1707            AlgorithmInfo::MegolmV1AesSha2 { .. }
1708        );
1709        assert_eq!(event.timestamp(), Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))));
1710        assert_eq!(event.timestamp(), event.timestamp_raw());
1711
1712        // Test that the previous format can also be deserialized.
1713        let serialized = json!({
1714            "event": {
1715                "content": {"body": "secret", "msgtype": "m.text"},
1716                "event_id": "$xxxxx:example.org",
1717                "origin_server_ts": 2189,
1718                "room_id": "!someroom:example.com",
1719                "sender": "@carl:example.com",
1720                "type": "m.room.message",
1721            },
1722            "encryption_info": {
1723                "sender": "@sender:example.com",
1724                "sender_device": null,
1725                "algorithm_info": {
1726                    "MegolmV1AesSha2": {
1727                        "curve25519_key": "xxx",
1728                        "sender_claimed_keys": {}
1729                    }
1730                },
1731                "verification_state": "Verified",
1732            },
1733        });
1734        let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1735        assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1736        assert_matches!(
1737            event.encryption_info().unwrap().algorithm_info,
1738            AlgorithmInfo::MegolmV1AesSha2 { session_id: None, .. }
1739        );
1740        assert_eq!(event.timestamp(), Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))));
1741        assert!(event.timestamp_raw().is_none());
1742
1743        // Test that the previous format, with an undecryptable unsigned event, can also
1744        // be deserialized.
1745        let serialized = json!({
1746            "event": {
1747                "content": {"body": "secret", "msgtype": "m.text"},
1748                "event_id": "$xxxxx:example.org",
1749                "origin_server_ts": 2189,
1750                "room_id": "!someroom:example.com",
1751                "sender": "@carl:example.com",
1752                "type": "m.room.message",
1753            },
1754            "encryption_info": {
1755                "sender": "@sender:example.com",
1756                "sender_device": null,
1757                "algorithm_info": {
1758                    "MegolmV1AesSha2": {
1759                        "curve25519_key": "xxx",
1760                        "sender_claimed_keys": {}
1761                    }
1762                },
1763                "verification_state": "Verified",
1764            },
1765            "unsigned_encryption_info": {
1766                "RelationsReplace": {"UnableToDecrypt": {"session_id": "xyz"}}
1767            }
1768        });
1769        let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1770        assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org").to_owned()));
1771        assert_matches!(
1772            event.encryption_info().unwrap().algorithm_info,
1773            AlgorithmInfo::MegolmV1AesSha2 { .. }
1774        );
1775        assert_eq!(event.timestamp(), Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))));
1776        assert!(event.timestamp_raw().is_none());
1777        assert_matches!(event.kind, TimelineEventKind::Decrypted(decrypted) => {
1778            assert_matches!(decrypted.unsigned_encryption_info, Some(map) => {
1779                assert_eq!(map.len(), 1);
1780                let (location, result) = map.into_iter().next().unwrap();
1781                assert_eq!(location, UnsignedEventLocation::RelationsReplace);
1782                assert_matches!(result, UnsignedDecryptionResult::UnableToDecrypt(utd_info) => {
1783                    assert_eq!(utd_info.session_id, Some("xyz".to_owned()));
1784                    assert_eq!(utd_info.reason, UnableToDecryptReason::Unknown);
1785                })
1786            });
1787        });
1788    }
1789
1790    #[test]
1791    fn test_creating_or_deserializing_an_event_extracts_summary() {
1792        let event = json!({
1793            "event_id": "$eid:example.com",
1794            "type": "m.room.message",
1795            "sender": "@alice:example.com",
1796            "origin_server_ts": 42,
1797            "content": {
1798                "body": "Hello, world!",
1799            },
1800            "unsigned": {
1801                "m.relations": {
1802                    "m.thread": {
1803                        "latest_event": {
1804                            "event_id": "$latest_event:example.com",
1805                            "type": "m.room.message",
1806                            "sender": "@bob:example.com",
1807                            "origin_server_ts": 42,
1808                            "content": {
1809                                "body": "Hello to you too!",
1810                                "msgtype": "m.text",
1811                            }
1812                        },
1813                        "count": 2,
1814                        "current_user_participated": true,
1815                    }
1816                }
1817            }
1818        });
1819
1820        let raw = Raw::new(&event).unwrap().cast_unchecked();
1821
1822        // When creating a timeline event from a raw event, the thread summary is always
1823        // extracted, if available.
1824        let timeline_event = TimelineEvent::from_plaintext(raw);
1825        assert_matches!(timeline_event.thread_summary, ThreadSummaryStatus::Some(ThreadSummary { num_replies, latest_reply }) => {
1826            assert_eq!(num_replies, 2);
1827            assert_eq!(latest_reply.as_deref(), Some(event_id!("$latest_event:example.com")));
1828        });
1829
1830        assert!(timeline_event.bundled_latest_thread_event.is_some());
1831
1832        // When deserializing an old serialized timeline event, the thread summary is
1833        // also extracted, if it wasn't serialized.
1834        let serialized_timeline_item = json!({
1835            "kind": {
1836                "PlainText": {
1837                    "event": event
1838                }
1839            }
1840        });
1841
1842        let timeline_event: TimelineEvent =
1843            serde_json::from_value(serialized_timeline_item).unwrap();
1844        assert_matches!(timeline_event.thread_summary, ThreadSummaryStatus::Unknown);
1845
1846        // The bundled latest thread event is not persisted, so it should be `None` when
1847        // deserialized from a previously serialized `TimelineEvent`.
1848        assert!(timeline_event.bundled_latest_thread_event.is_none());
1849    }
1850
1851    #[test]
1852    fn sync_timeline_event_deserialisation_migration_for_withheld() {
1853        // Old serialized version was
1854        //    "utd_info": {
1855        //         "reason": "MissingMegolmSession",
1856        //         "session_id": "session000"
1857        //       }
1858
1859        // The new version would be
1860        //      "utd_info": {
1861        //         "reason": {
1862        //           "MissingMegolmSession": {
1863        //              "withheld_code": null
1864        //           }
1865        //         },
1866        //         "session_id": "session000"
1867        //       }
1868
1869        let serialized = json!({
1870             "kind": {
1871                "UnableToDecrypt": {
1872                  "event": {
1873                    "content": {
1874                      "algorithm": "m.megolm.v1.aes-sha2",
1875                      "ciphertext": "AwgAEoABzL1JYhqhjW9jXrlT3M6H8mJ4qffYtOQOnPuAPNxsuG20oiD/Fnpv6jnQGhU6YbV9pNM+1mRnTvxW3CbWOPjLKqCWTJTc7Q0vDEVtYePg38ncXNcwMmfhgnNAoW9S7vNs8C003x3yUl6NeZ8bH+ci870BZL+kWM/lMl10tn6U7snNmSjnE3ckvRdO+11/R4//5VzFQpZdf4j036lNSls/WIiI67Fk9iFpinz9xdRVWJFVdrAiPFwb8L5xRZ8aX+e2JDMlc1eW8gk",
1876                      "device_id": "SKCGPNUWAU",
1877                      "sender_key": "Gim/c7uQdSXyrrUbmUOrBT6sMC0gO7QSLmOK6B7NOm0",
1878                      "session_id": "hgLyeSqXfb8vc5AjQLsg6TSHVu0HJ7HZ4B6jgMvxkrs"
1879                    },
1880                    "event_id": "$xxxxx:example.org",
1881                    "origin_server_ts": 2189,
1882                    "room_id": "!someroom:example.com",
1883                    "sender": "@carl:example.com",
1884                    "type": "m.room.message"
1885                  },
1886                  "utd_info": {
1887                    "reason": "MissingMegolmSession",
1888                    "session_id": "session000"
1889                  }
1890                }
1891              }
1892        });
1893
1894        let result = serde_json::from_value(serialized);
1895        assert!(result.is_ok());
1896
1897        // should have migrated to the new format
1898        let event: TimelineEvent = result.unwrap();
1899        assert_matches!(
1900            event.kind,
1901            TimelineEventKind::UnableToDecrypt { utd_info, .. }=> {
1902                assert_matches!(
1903                    utd_info.reason,
1904                    UnableToDecryptReason::MissingMegolmSession { withheld_code: None }
1905                );
1906            }
1907        )
1908    }
1909
1910    #[test]
1911    fn unable_to_decrypt_info_migration_for_withheld() {
1912        let old_format = json!({
1913            "reason": "MissingMegolmSession",
1914            "session_id": "session000"
1915        });
1916
1917        let deserialized = serde_json::from_value::<UnableToDecryptInfo>(old_format).unwrap();
1918        let session_id = Some("session000".to_owned());
1919
1920        assert_eq!(deserialized.session_id, session_id);
1921        assert_eq!(
1922            deserialized.reason,
1923            UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
1924        );
1925
1926        let new_format = json!({
1927             "session_id": "session000",
1928              "reason": {
1929                "MissingMegolmSession": {
1930                  "withheld_code": null
1931                }
1932              }
1933        });
1934
1935        let deserialized = serde_json::from_value::<UnableToDecryptInfo>(new_format).unwrap();
1936
1937        assert_eq!(
1938            deserialized.reason,
1939            UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
1940        );
1941        assert_eq!(deserialized.session_id, session_id);
1942    }
1943
1944    #[test]
1945    fn unable_to_decrypt_reason_is_missing_room_key() {
1946        let reason = UnableToDecryptReason::MissingMegolmSession { withheld_code: None };
1947        assert!(reason.is_missing_room_key());
1948
1949        let reason = UnableToDecryptReason::MissingMegolmSession {
1950            withheld_code: Some(WithheldCode::Blacklisted),
1951        };
1952        assert!(!reason.is_missing_room_key());
1953
1954        let reason = UnableToDecryptReason::UnknownMegolmMessageIndex;
1955        assert!(reason.is_missing_room_key());
1956    }
1957
1958    #[test]
1959    fn snapshot_test_verification_level() {
1960        with_settings!({ prepend_module_to_snapshot => false }, {
1961            assert_json_snapshot!(VerificationLevel::VerificationViolation);
1962            assert_json_snapshot!(VerificationLevel::UnsignedDevice);
1963            assert_json_snapshot!(VerificationLevel::None(DeviceLinkProblem::InsecureSource));
1964            assert_json_snapshot!(VerificationLevel::None(DeviceLinkProblem::MissingDevice));
1965            assert_json_snapshot!(VerificationLevel::UnverifiedIdentity);
1966        });
1967    }
1968
1969    #[test]
1970    fn snapshot_test_verification_states() {
1971        with_settings!({ prepend_module_to_snapshot => false }, {
1972            assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::UnsignedDevice));
1973            assert_json_snapshot!(VerificationState::Unverified(
1974                VerificationLevel::VerificationViolation
1975            ));
1976            assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::None(
1977                DeviceLinkProblem::InsecureSource,
1978            )));
1979            assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::None(
1980                DeviceLinkProblem::MissingDevice,
1981            )));
1982            assert_json_snapshot!(VerificationState::Verified);
1983        });
1984    }
1985
1986    #[test]
1987    fn snapshot_test_shield_states() {
1988        with_settings!({ prepend_module_to_snapshot => false }, {
1989            assert_json_snapshot!(ShieldState::None);
1990            assert_json_snapshot!(ShieldState::Red {
1991                code: ShieldStateCode::UnverifiedIdentity,
1992                message: "a message"
1993            });
1994            assert_json_snapshot!(ShieldState::Grey {
1995                code: ShieldStateCode::AuthenticityNotGuaranteed,
1996                message: "authenticity of this message cannot be guaranteed",
1997            });
1998        });
1999    }
2000
2001    #[test]
2002    fn snapshot_test_shield_codes() {
2003        with_settings!({ prepend_module_to_snapshot => false }, {
2004            assert_json_snapshot!(ShieldStateCode::AuthenticityNotGuaranteed);
2005            assert_json_snapshot!(ShieldStateCode::UnknownDevice);
2006            assert_json_snapshot!(ShieldStateCode::UnsignedDevice);
2007            assert_json_snapshot!(ShieldStateCode::UnverifiedIdentity);
2008            assert_json_snapshot!(ShieldStateCode::VerificationViolation);
2009        });
2010    }
2011
2012    #[test]
2013    fn snapshot_test_algorithm_info() {
2014        let mut map = BTreeMap::new();
2015        map.insert(DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned());
2016        map.insert(DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned());
2017        let info = AlgorithmInfo::MegolmV1AesSha2 {
2018            curve25519_key: "curvecurvecurve".into(),
2019            sender_claimed_keys: BTreeMap::from([
2020                (DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned()),
2021                (DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned()),
2022            ]),
2023            session_id: None,
2024        };
2025
2026        with_settings!({ prepend_module_to_snapshot => false }, {
2027            assert_json_snapshot!(info)
2028        });
2029    }
2030
2031    #[test]
2032    fn test_encryption_info_migration() {
2033        // In the old format the session_id was in the EncryptionInfo, now
2034        // it is moved to the `algorithm_info` struct.
2035        let old_format = json!({
2036          "sender": "@alice:localhost",
2037          "sender_device": "ABCDEFGH",
2038          "algorithm_info": {
2039            "MegolmV1AesSha2": {
2040              "curve25519_key": "curvecurvecurve",
2041              "sender_claimed_keys": {}
2042            }
2043          },
2044          "verification_state": "Verified",
2045          "session_id": "mysessionid76"
2046        });
2047
2048        let deserialized = serde_json::from_value::<EncryptionInfo>(old_format).unwrap();
2049        let expected_session_id = Some("mysessionid76".to_owned());
2050
2051        assert_let!(
2052            AlgorithmInfo::MegolmV1AesSha2 { session_id, .. } = deserialized.algorithm_info.clone()
2053        );
2054        assert_eq!(session_id, expected_session_id);
2055
2056        assert_json_snapshot!(deserialized);
2057    }
2058
2059    #[test]
2060    fn snapshot_test_encryption_info() {
2061        let info = EncryptionInfo {
2062            sender: user_id!("@alice:localhost").to_owned(),
2063            sender_device: Some(device_id!("ABCDEFGH").to_owned()),
2064            forwarder: None,
2065            algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
2066                curve25519_key: "curvecurvecurve".into(),
2067                sender_claimed_keys: Default::default(),
2068                session_id: Some("mysessionid76".to_owned()),
2069            },
2070            verification_state: VerificationState::Verified,
2071        };
2072
2073        with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
2074            assert_json_snapshot!(info)
2075        })
2076    }
2077
2078    #[test]
2079    fn snapshot_test_sync_timeline_event() {
2080        let room_event = TimelineEvent {
2081            kind: TimelineEventKind::Decrypted(DecryptedRoomEvent {
2082                event: Raw::new(&example_event()).unwrap().cast_unchecked(),
2083                encryption_info: Arc::new(EncryptionInfo {
2084                    sender: user_id!("@sender:example.com").to_owned(),
2085                    sender_device: Some(device_id!("ABCDEFGHIJ").to_owned()),
2086                    forwarder: None,
2087                    algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
2088                        curve25519_key: "xxx".to_owned(),
2089                        sender_claimed_keys: BTreeMap::from([
2090                            (
2091                                DeviceKeyAlgorithm::Ed25519,
2092                                "I3YsPwqMZQXHkSQbjFNEs7b529uac2xBpI83eN3LUXo".to_owned(),
2093                            ),
2094                            (
2095                                DeviceKeyAlgorithm::Curve25519,
2096                                "qzdW3F5IMPFl0HQgz5w/L5Oi/npKUFn8Um84acIHfPY".to_owned(),
2097                            ),
2098                        ]),
2099                        session_id: Some("mysessionid112".to_owned()),
2100                    },
2101                    verification_state: VerificationState::Verified,
2102                }),
2103                unsigned_encryption_info: Some(BTreeMap::from([(
2104                    UnsignedEventLocation::RelationsThreadLatestEvent,
2105                    UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
2106                        session_id: Some("xyz".to_owned()),
2107                        reason: UnableToDecryptReason::MissingMegolmSession {
2108                            withheld_code: Some(WithheldCode::Unverified),
2109                        },
2110                    }),
2111                )])),
2112            }),
2113            timestamp: Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))),
2114            push_actions: Default::default(),
2115            thread_summary: ThreadSummaryStatus::Some(ThreadSummary {
2116                num_replies: 2,
2117                latest_reply: None,
2118            }),
2119            bundled_latest_thread_event: None,
2120        };
2121
2122        with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
2123            // We use directly the serde_json formatter here, because of a bug in insta
2124            // not serializing custom BTreeMap key enum https://github.com/mitsuhiko/insta/issues/689
2125            assert_json_snapshot! {
2126                serde_json::to_value(&room_event).unwrap(),
2127            }
2128        });
2129    }
2130
2131    #[test]
2132    fn test_from_bundled_latest_event_keeps_session_id() {
2133        let session_id = "hgLyeSqXfb8vc5AjQLsg6TSHVu0HJ7HZ4B6jgMvxkrs";
2134        let serialized = json!({
2135            "content": {
2136              "algorithm": "m.megolm.v1.aes-sha2",
2137              "ciphertext": "AwgAEoABzL1JYhqhjW9jXrlT3M6H8mJ4qffYtOQOnPuAPNxsuG20oiD/Fnpv6jnQGhU6YbV9pNM+1mRnTvxW3CbWOPjLKqCWTJTc7Q0vDEVtYePg38ncXNcwMmfhgnNAoW9S7vNs8C003x3yUl6NeZ8bH+ci870BZL+kWM/lMl10tn6U7snNmSjnE3ckvRdO+11/R4//5VzFQpZdf4j036lNSls/WIiI67Fk9iFpinz9xdRVWJFVdrAiPFwb8L5xRZ8aX+e2JDMlc1eW8gk",
2138              "device_id": "SKCGPNUWAU",
2139              "sender_key": "Gim/c7uQdSXyrrUbmUOrBT6sMC0gO7QSLmOK6B7NOm0",
2140              "session_id": session_id,
2141            },
2142            "event_id": "$xxxxx:example.org",
2143            "origin_server_ts": 2189,
2144            "room_id": "!someroom:example.com",
2145            "sender": "@carl:example.com",
2146            "type": "m.room.encrypted"
2147        });
2148        let json = serialized.to_string();
2149        let value = Raw::<AnySyncTimelineEvent>::from_json_string(json).unwrap();
2150
2151        let kind = TimelineEventKind::UnableToDecrypt {
2152            event: value.clone(),
2153            utd_info: UnableToDecryptInfo {
2154                session_id: None,
2155                reason: UnableToDecryptReason::Unknown,
2156            },
2157        };
2158        let result = TimelineEvent::from_bundled_latest_event(
2159            &kind,
2160            Some(value.cast_unchecked()),
2161            MilliSecondsSinceUnixEpoch::now(),
2162        )
2163        .expect("Could not get bundled latest event");
2164
2165        assert_let!(TimelineEventKind::UnableToDecrypt { utd_info, .. } = result.kind);
2166        assert!(utd_info.session_id.is_some());
2167        assert_eq!(utd_info.session_id.unwrap(), session_id);
2168    }
2169}