Skip to main content

matrix_sdk_common/
deserialized_responses.rs

1// Copyright 2023 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::{collections::BTreeMap, fmt, ops::Not, sync::Arc};
16
17use ruma::{
18    DeviceKeyAlgorithm, EventId, MilliSecondsSinceUnixEpoch, OwnedDeviceId, OwnedEventId,
19    OwnedUserId,
20    events::{
21        AnySyncMessageLikeEvent, AnySyncTimelineEvent, AnyTimelineEvent, AnyToDeviceEvent,
22        MessageLikeEventType, room::encrypted::EncryptedEventScheme,
23    },
24    push::Action,
25    serde::{
26        AsRefStr, AsStrAsRefStr, DebugAsRefStr, DeserializeFromCowStr, FromString, JsonObject, Raw,
27        SerializeAsRefStr,
28    },
29};
30use serde::{Deserialize, Serialize};
31use tracing::warn;
32#[cfg(target_family = "wasm")]
33use wasm_bindgen::prelude::*;
34
35use crate::{
36    debug::{DebugRawEvent, DebugStructExt},
37    serde_helpers::{extract_bundled_thread, extract_is_thread_root, extract_timestamp},
38};
39
40const AUTHENTICITY_NOT_GUARANTEED: &str =
41    "The authenticity of this encrypted message can't be guaranteed on this device.";
42const UNVERIFIED_IDENTITY: &str = "Encrypted by an unverified user.";
43const VERIFICATION_VIOLATION: &str =
44    "Encrypted by a previously-verified user who is no longer verified.";
45const UNSIGNED_DEVICE: &str = "Encrypted by a device not verified by its owner.";
46const UNKNOWN_DEVICE: &str = "Encrypted by an unknown or deleted device.";
47const MISMATCHED_SENDER: &str = "\
48    The sender of the event does not match the owner of the device \
49    that created the Megolm session.";
50
51/// Represents the state of verification for a decrypted message sent by a
52/// device.
53#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
54#[serde(from = "OldVerificationStateHelper")]
55pub enum VerificationState {
56    /// This message is guaranteed to be authentic as it is coming from a device
57    /// belonging to a user that we have verified.
58    ///
59    /// This is the only state where authenticity can be guaranteed.
60    Verified,
61
62    /// The message could not be linked to a verified device.
63    ///
64    /// For more detailed information on why the message is considered
65    /// unverified, refer to the VerificationLevel sub-enum.
66    Unverified(VerificationLevel),
67}
68
69// TODO: Remove this once we're confident that everybody that serialized these
70// states uses the new enum.
71#[derive(Clone, Debug, Deserialize)]
72enum OldVerificationStateHelper {
73    Untrusted,
74    UnknownDevice,
75    #[serde(alias = "Trusted")]
76    Verified,
77    Unverified(VerificationLevel),
78}
79
80impl From<OldVerificationStateHelper> for VerificationState {
81    fn from(value: OldVerificationStateHelper) -> Self {
82        match value {
83            // This mapping isn't strictly correct but we don't know which part
84            // in the old `VerificationState` enum was unverified.
85            OldVerificationStateHelper::Untrusted => {
86                VerificationState::Unverified(VerificationLevel::UnsignedDevice)
87            }
88            OldVerificationStateHelper::UnknownDevice => {
89                Self::Unverified(VerificationLevel::None(DeviceLinkProblem::MissingDevice))
90            }
91            OldVerificationStateHelper::Verified => Self::Verified,
92            OldVerificationStateHelper::Unverified(l) => Self::Unverified(l),
93        }
94    }
95}
96
97impl VerificationState {
98    /// Convert the `VerificationState` into a `ShieldState` which can be
99    /// directly used to decorate messages in the recommended way.
100    ///
101    /// This method decorates messages using a strict ruleset, for a more lax
102    /// variant of this method take a look at
103    /// [`VerificationState::to_shield_state_lax()`].
104    pub fn to_shield_state_strict(&self) -> ShieldState {
105        match self {
106            VerificationState::Verified => ShieldState::None,
107            VerificationState::Unverified(level) => match level {
108                VerificationLevel::UnverifiedIdentity
109                | VerificationLevel::VerificationViolation
110                | VerificationLevel::UnsignedDevice => ShieldState::Red {
111                    code: ShieldStateCode::UnverifiedIdentity,
112                    message: UNVERIFIED_IDENTITY,
113                },
114                VerificationLevel::None(link) => match link {
115                    DeviceLinkProblem::MissingDevice => ShieldState::Red {
116                        code: ShieldStateCode::UnknownDevice,
117                        message: UNKNOWN_DEVICE,
118                    },
119                    DeviceLinkProblem::InsecureSource => ShieldState::Red {
120                        code: ShieldStateCode::AuthenticityNotGuaranteed,
121                        message: AUTHENTICITY_NOT_GUARANTEED,
122                    },
123                },
124                VerificationLevel::MismatchedSender => ShieldState::Red {
125                    code: ShieldStateCode::MismatchedSender,
126                    message: MISMATCHED_SENDER,
127                },
128            },
129        }
130    }
131
132    /// Convert the `VerificationState` into a `ShieldState` which can be used
133    /// to decorate messages in the recommended way.
134    ///
135    /// This implements a legacy, lax decoration mode.
136    ///
137    /// For a more strict variant of this method take a look at
138    /// [`VerificationState::to_shield_state_strict()`].
139    pub fn to_shield_state_lax(&self) -> ShieldState {
140        match self {
141            VerificationState::Verified => ShieldState::None,
142            VerificationState::Unverified(level) => match level {
143                VerificationLevel::UnverifiedIdentity => {
144                    // If you didn't show interest in verifying that user we
145                    // don't nag you with an error message.
146                    ShieldState::None
147                }
148                VerificationLevel::VerificationViolation => {
149                    // This is a high warning. The sender was previously
150                    // verified, but changed their identity.
151                    ShieldState::Red {
152                        code: ShieldStateCode::VerificationViolation,
153                        message: VERIFICATION_VIOLATION,
154                    }
155                }
156                VerificationLevel::UnsignedDevice => {
157                    // This is a high warning. The sender hasn't verified his
158                    // own device.
159                    ShieldState::Red {
160                        code: ShieldStateCode::UnsignedDevice,
161                        message: UNSIGNED_DEVICE,
162                    }
163                }
164                VerificationLevel::None(link) => match link {
165                    DeviceLinkProblem::MissingDevice => {
166                        // Have to warn as it could have been a temporary
167                        // injected device. Notice that the device might just
168                        // not be known at this time, so callers should retry
169                        // when there is a device change for that user.
170                        ShieldState::Red {
171                            code: ShieldStateCode::UnknownDevice,
172                            message: UNKNOWN_DEVICE,
173                        }
174                    }
175                    DeviceLinkProblem::InsecureSource => {
176                        // In legacy mode, we tone down this warning as it is
177                        // quite common and mostly noise (due to legacy backup
178                        // and lack of trusted forwards).
179                        ShieldState::Grey {
180                            code: ShieldStateCode::AuthenticityNotGuaranteed,
181                            message: AUTHENTICITY_NOT_GUARANTEED,
182                        }
183                    }
184                },
185                VerificationLevel::MismatchedSender => ShieldState::Red {
186                    code: ShieldStateCode::MismatchedSender,
187                    message: MISMATCHED_SENDER,
188                },
189            },
190        }
191    }
192}
193
194/// The sub-enum containing detailed information on why a message is considered
195/// to be unverified.
196#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
197pub enum VerificationLevel {
198    /// The message was sent by a user identity we have not verified.
199    UnverifiedIdentity,
200
201    /// The message was sent by a user identity we have not verified, but the
202    /// user was previously verified.
203    #[serde(alias = "PreviouslyVerified")]
204    VerificationViolation,
205
206    /// The message was sent by a device not linked to (signed by) any user
207    /// identity.
208    UnsignedDevice,
209
210    /// We weren't able to link the message back to any device. This might be
211    /// because the message claims to have been sent by a device which we have
212    /// not been able to obtain (for example, because the device was since
213    /// deleted) or because the key to decrypt the message was obtained from an
214    /// insecure source.
215    None(DeviceLinkProblem),
216
217    /// The `sender` field on the event does not match the owner of the device
218    /// that established the Megolm session.
219    MismatchedSender,
220}
221
222impl fmt::Display for VerificationLevel {
223    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
224        let display = match self {
225            VerificationLevel::UnverifiedIdentity => "The sender's identity was not verified",
226            VerificationLevel::VerificationViolation => {
227                "The sender's identity was previously verified but has changed"
228            }
229            VerificationLevel::UnsignedDevice => {
230                "The sending device was not signed by the user's identity"
231            }
232            VerificationLevel::None(..) => "The sending device is not known",
233            VerificationLevel::MismatchedSender => MISMATCHED_SENDER,
234        };
235        write!(f, "{display}")
236    }
237}
238
239/// The sub-enum containing detailed information on why we were not able to link
240/// a message back to a device.
241#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
242pub enum DeviceLinkProblem {
243    /// The device is missing, either because it was deleted, or you haven't yet
244    /// downoaled it or the server is erroneously omitting it (federation lag).
245    MissingDevice,
246    /// The key was obtained from an insecure source: imported from a file,
247    /// obtained from a legacy (asymmetric) backup, unsafe key forward, etc.
248    InsecureSource,
249}
250
251/// Recommended decorations for decrypted messages, representing the message's
252/// authenticity properties.
253#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
254pub enum ShieldState {
255    /// A red shield with a tooltip containing the associated message should be
256    /// presented.
257    Red {
258        /// A machine-readable representation.
259        code: ShieldStateCode,
260        /// A human readable description.
261        message: &'static str,
262    },
263    /// A grey shield with a tooltip containing the associated message should be
264    /// presented.
265    Grey {
266        /// A machine-readable representation.
267        code: ShieldStateCode,
268        /// A human readable description.
269        message: &'static str,
270    },
271    /// No shield should be presented.
272    None,
273}
274
275/// A machine-readable representation of the authenticity for a `ShieldState`.
276#[derive(Clone, Copy, Debug, Deserialize, Serialize, Eq, PartialEq)]
277#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
278#[cfg_attr(target_family = "wasm", wasm_bindgen)]
279pub enum ShieldStateCode {
280    /// Not enough information available to check the authenticity.
281    AuthenticityNotGuaranteed,
282    /// The sending device isn't yet known by the Client.
283    UnknownDevice,
284    /// The sending device hasn't been verified by the sender.
285    UnsignedDevice,
286    /// The sender hasn't been verified by the Client's user.
287    UnverifiedIdentity,
288    /// The sender was previously verified but changed their identity.
289    #[serde(alias = "PreviouslyVerified")]
290    VerificationViolation,
291    /// The `sender` field on the event does not match the owner of the device
292    /// that established the Megolm session.
293    MismatchedSender,
294}
295
296/// The algorithm specific information of a decrypted event.
297#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
298pub enum AlgorithmInfo {
299    /// The info if the event was encrypted using m.megolm.v1.aes-sha2
300    MegolmV1AesSha2 {
301        /// The curve25519 key of the device that created the megolm decryption
302        /// key originally.
303        curve25519_key: String,
304        /// The signing keys that have created the megolm key that was used to
305        /// decrypt this session. This map will usually contain a single ed25519
306        /// key.
307        sender_claimed_keys: BTreeMap<DeviceKeyAlgorithm, String>,
308
309        /// The Megolm session ID that was used to encrypt this event, or None
310        /// if this info was stored before we collected this data.
311        #[serde(default, skip_serializing_if = "Option::is_none")]
312        session_id: Option<String>,
313    },
314
315    /// The info if the event was encrypted using m.olm.v1.curve25519-aes-sha2
316    OlmV1Curve25519AesSha2 {
317        // The sender device key, base64 encoded
318        curve25519_public_key_base64: String,
319    },
320}
321
322/// Struct containing information on the forwarder of the keys used to decrypt
323/// an event.
324#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
325pub struct ForwarderInfo {
326    /// The user ID of the forwarder.
327    pub user_id: OwnedUserId,
328    /// The device ID of the forwarder.
329    pub device_id: OwnedDeviceId,
330}
331
332/// Struct containing information on how an event was decrypted.
333#[derive(Clone, Debug, PartialEq, Serialize)]
334pub struct EncryptionInfo {
335    /// The user ID of the event sender, note this is untrusted data unless the
336    /// `verification_state` is `Verified` as well.
337    pub sender: OwnedUserId,
338    /// The device ID of the device that sent us the event, note this is
339    /// untrusted data unless `verification_state` is `Verified` as well.
340    pub sender_device: Option<OwnedDeviceId>,
341    /// If the keys for this message were shared-on-invite as part of an
342    /// [MSC4268] key bundle, information about the forwarder.
343    ///
344    /// [MSC4268]: https://github.com/matrix-org/matrix-spec-proposals/pull/4268
345    pub forwarder: Option<ForwarderInfo>,
346    /// Information about the algorithm that was used to encrypt the event.
347    pub algorithm_info: AlgorithmInfo,
348    /// The verification state of the device that sent us the event, note this
349    /// is the state of the device at the time of decryption. It may change in
350    /// the future if a device gets verified or deleted.
351    ///
352    /// Callers that persist this should mark the state as dirty when a device
353    /// change is received down the sync.
354    pub verification_state: VerificationState,
355}
356
357impl EncryptionInfo {
358    /// Helper to get the megolm session id used to encrypt.
359    pub fn session_id(&self) -> Option<&str> {
360        if let AlgorithmInfo::MegolmV1AesSha2 { session_id, .. } = &self.algorithm_info {
361            session_id.as_deref()
362        } else {
363            None
364        }
365    }
366}
367
368impl<'de> Deserialize<'de> for EncryptionInfo {
369    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
370    where
371        D: serde::Deserializer<'de>,
372    {
373        // Backwards compatibility: Capture session_id at root if exists. In
374        // legacy EncryptionInfo the session_id was not in AlgorithmInfo
375        #[derive(Deserialize)]
376        struct Helper {
377            pub sender: OwnedUserId,
378            pub sender_device: Option<OwnedDeviceId>,
379            pub forwarder: Option<ForwarderInfo>,
380            pub algorithm_info: AlgorithmInfo,
381            pub verification_state: VerificationState,
382            #[serde(rename = "session_id")]
383            pub old_session_id: Option<String>,
384        }
385
386        let Helper {
387            sender,
388            sender_device,
389            forwarder,
390            algorithm_info,
391            verification_state,
392            old_session_id,
393        } = Helper::deserialize(deserializer)?;
394
395        let algorithm_info = match algorithm_info {
396            AlgorithmInfo::MegolmV1AesSha2 { curve25519_key, sender_claimed_keys, session_id } => {
397                AlgorithmInfo::MegolmV1AesSha2 {
398                    // Migration, merge the old_session_id in algorithm_info
399                    session_id: session_id.or(old_session_id),
400                    curve25519_key,
401                    sender_claimed_keys,
402                }
403            }
404            other => other,
405        };
406
407        Ok(EncryptionInfo { sender, sender_device, forwarder, algorithm_info, verification_state })
408    }
409}
410
411/// A simplified thread summary.
412///
413/// A thread summary contains useful information pertaining to a thread, and
414/// that would be usually attached in clients to a thread root event (i.e. the
415/// first event from which the thread originated), along with links into the
416/// thread's view. This summary may include, for instance:
417///
418/// - the number of replies to the thread,
419/// - the full event of the latest reply to the thread,
420/// - whether the user participated or not to this thread.
421#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
422pub struct ThreadSummary {
423    /// The event id for the latest reply to the thread.
424    #[serde(skip_serializing_if = "Option::is_none")]
425    pub latest_reply: Option<OwnedEventId>,
426
427    /// The number of replies to the thread.
428    ///
429    /// This doesn't include the thread root event itself. It can be zero if no
430    /// events in the thread are considered to be meaningful (or they've all
431    /// been redacted).
432    pub num_replies: u32,
433}
434
435impl ThreadSummary {
436    /// Create a new [`ThreadSummary`].
437    ///
438    /// `num_replies` is set to [`u32::MAX`] if it fails to convert.
439    pub fn new<N>(latest_reply: Option<OwnedEventId>, num_replies: N) -> Self
440    where
441        N: TryInto<u32>,
442    {
443        Self { latest_reply, num_replies: num_replies.try_into().unwrap_or(u32::MAX) }
444    }
445}
446
447/// Represents a matrix room event that has been returned from a Matrix
448/// client-server API endpoint such as `/sync` or `/messages`, after initial
449/// processing.
450///
451/// The "initial processing" includes an attempt to decrypt encrypted events, so
452/// the main thing this adds over [`AnyTimelineEvent`] is information on
453/// encryption.
454// 🚨 Note about this type, please read! 🚨
455//
456// `TimelineEvent` is heavily used across the SDK crates. In some cases, we are
457// reaching a [`recursion_limit`] when the compiler is trying to figure out if
458// `TimelineEvent` implements `Sync` when it's embedded in other types.
459//
460// We want to help the compiler so that one doesn't need to increase the
461// `recursion_limit`. We stop the recursive check by (un)safely implement `Sync`
462// and `Send` on `TimelineEvent` directly.
463//
464// See
465// https://github.com/matrix-org/matrix-rust-sdk/pull/3749#issuecomment-2312939823
466// which has addressed this issue first
467//
468// [`recursion_limit`]: https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute
469#[derive(Clone, Debug, Serialize)]
470pub struct TimelineEvent {
471    /// The event ID (cached from `Self::kind`).
472    ///
473    /// This field contains a copy of `TimelineEventKind::parse_event_id`. Why?
474    /// Because reading the event ID is done **a lot** in the SDK.
475    /// `TimelineEventKind::parse_event_id` implies parsing/deserializing the
476    /// JSON payload looking for the event ID. It has a non-negligible cost.
477    /// Hence this cache.
478    #[serde(skip)]
479    event_id: Option<OwnedEventId>,
480
481    /// The event itself, together with any information on decryption.
482    pub kind: TimelineEventKind,
483
484    /// The timestamp of the event. It's the `origin_server_ts` value (if any),
485    /// corrected if detected as malicious.
486    ///
487    /// It can be `None` if the event has been serialised before the addition of
488    /// this field, or if parsing the `origin_server_ts` value failed.
489    pub timestamp: Option<MilliSecondsSinceUnixEpoch>,
490
491    /// The push actions associated with this event.
492    ///
493    /// If it's set to `None`, then it means we couldn't compute those actions,
494    /// or that they could be computed but there were none.
495    #[serde(skip_serializing_if = "skip_serialize_push_actions")]
496    push_actions: Option<Vec<Action>>,
497}
498
499// Don't serialize push actions if they're `None` or an empty vec.
500fn skip_serialize_push_actions(push_actions: &Option<Vec<Action>>) -> bool {
501    push_actions.as_ref().is_none_or(|v| v.is_empty())
502}
503
504// See https://github.com/matrix-org/matrix-rust-sdk/pull/3749#issuecomment-2312939823.
505#[cfg(not(feature = "test-send-sync"))]
506unsafe impl Send for TimelineEvent {}
507
508// See https://github.com/matrix-org/matrix-rust-sdk/pull/3749#issuecomment-2312939823.
509#[cfg(not(feature = "test-send-sync"))]
510unsafe impl Sync for TimelineEvent {}
511
512#[cfg(feature = "test-send-sync")]
513#[test]
514// See https://github.com/matrix-org/matrix-rust-sdk/pull/3749#issuecomment-2312939823.
515fn test_send_sync_for_sync_timeline_event() {
516    fn assert_send_sync<T: crate::SendOutsideWasm + crate::SyncOutsideWasm>() {}
517
518    assert_send_sync::<TimelineEvent>();
519}
520
521impl TimelineEvent {
522    /// Create a new [`TimelineEvent`] from the given raw event.
523    ///
524    /// This is a convenience constructor for a plaintext event when you don't
525    /// need to set `push_action`, for example inside a test.
526    pub fn from_plaintext(event: Raw<AnySyncTimelineEvent>) -> Self {
527        Self::from_plaintext_with_max_timestamp(event, MilliSecondsSinceUnixEpoch::now())
528    }
529
530    /// Like [`TimelineEvent::from_plaintext`] but with a given `max_timestamp`.
531    pub fn from_plaintext_with_max_timestamp(
532        event: Raw<AnySyncTimelineEvent>,
533        max_timestamp: MilliSecondsSinceUnixEpoch,
534    ) -> Self {
535        Self::new(TimelineEventKind::PlainText { event }, None, max_timestamp)
536    }
537
538    /// Create a new [`TimelineEvent`] from a decrypted event.
539    pub fn from_decrypted(
540        decrypted: DecryptedRoomEvent,
541        push_actions: Option<Vec<Action>>,
542    ) -> Self {
543        Self::from_decrypted_with_max_timestamp(
544            decrypted,
545            push_actions,
546            MilliSecondsSinceUnixEpoch::now(),
547        )
548    }
549
550    /// Like [`TimelineEvent::from_decrypted`] but with a given `max_timestamp`.
551    pub fn from_decrypted_with_max_timestamp(
552        decrypted: DecryptedRoomEvent,
553        push_actions: Option<Vec<Action>>,
554        max_timestamp: MilliSecondsSinceUnixEpoch,
555    ) -> Self {
556        Self::new(TimelineEventKind::Decrypted(decrypted), push_actions, max_timestamp)
557    }
558
559    /// Create a new [`TimelineEvent`] to represent the given decryption
560    /// failure.
561    pub fn from_utd(event: Raw<AnySyncTimelineEvent>, utd_info: UnableToDecryptInfo) -> Self {
562        Self::from_utd_with_max_timestamp(event, utd_info, MilliSecondsSinceUnixEpoch::now())
563    }
564
565    /// Like [`TimelineEvent::from_utd`] but with a given `max_timestamp`.
566    pub fn from_utd_with_max_timestamp(
567        event: Raw<AnySyncTimelineEvent>,
568        utd_info: UnableToDecryptInfo,
569        max_timestamp: MilliSecondsSinceUnixEpoch,
570    ) -> Self {
571        Self::new(TimelineEventKind::UnableToDecrypt { event, utd_info }, None, max_timestamp)
572    }
573
574    /// Internal only: helps extracting a thread summary and latest thread event
575    /// when creating a new [`TimelineEvent`].
576    ///
577    /// Build the `timestamp` value by using `now()` as the max value.
578    fn new(
579        kind: TimelineEventKind,
580        push_actions: Option<Vec<Action>>,
581        max_timestamp: MilliSecondsSinceUnixEpoch,
582    ) -> Self {
583        let raw = kind.raw();
584
585        let timestamp = extract_timestamp(raw, max_timestamp);
586
587        Self { event_id: kind.parse_event_id(), kind, push_actions, timestamp }
588    }
589
590    /// Transform this [`TimelineEvent`] into another [`TimelineEvent`] with the
591    /// [`TimelineEventKind::Decrypted`] kind.
592    ///
593    /// ## Panics
594    ///
595    /// It panics (on debug builds only) if the kind already is
596    /// [`TimelineEventKind::Decrypted`].
597    pub fn to_decrypted(
598        &self,
599        decrypted: DecryptedRoomEvent,
600        push_actions: Option<Vec<Action>>,
601    ) -> Self {
602        debug_assert!(
603            matches!(self.kind, TimelineEventKind::Decrypted(_)).not(),
604            "`TimelineEvent::to_decrypted` has been called on an already decrypted `TimelineEvent`."
605        );
606
607        let kind = TimelineEventKind::Decrypted(decrypted);
608
609        Self {
610            // We could clone `self.event_id`, but we prefer to re-parse the
611            // event ID from `decrypted` in case it has changed (it MUST NOT
612            // happen, but we never know).
613            event_id: kind.parse_event_id(),
614            kind,
615            timestamp: self.timestamp,
616            push_actions,
617        }
618    }
619
620    /// Transform this [`TimelineEvent`] into another [`TimelineEvent`] with the
621    /// [`TimelineEventKind::Decrypted`] kind.
622    ///
623    /// ## Panics
624    ///
625    /// It panics (on debug builds only) if the kind already is
626    /// [`TimelineEventKind::Decrypted`].
627    pub fn to_utd(&self, utd_info: UnableToDecryptInfo) -> Self {
628        debug_assert!(
629            matches!(self.kind, TimelineEventKind::UnableToDecrypt { .. }).not(),
630            "`TimelineEvent::to_utd` has been called on an already UTD `TimelineEvent`."
631        );
632
633        Self {
634            event_id: self.event_id.clone(),
635            kind: TimelineEventKind::UnableToDecrypt { event: self.raw().clone(), utd_info },
636            timestamp: self.timestamp,
637            push_actions: None,
638        }
639    }
640
641    /// Try to create a new [`TimelineEvent`] for the bundled latest thread
642    /// event, if we have enough information about the encryption status for it.
643    fn from_bundled_latest_event(
644        kind: &TimelineEventKind,
645        latest_event: Raw<AnySyncMessageLikeEvent>,
646        max_timestamp: MilliSecondsSinceUnixEpoch,
647    ) -> Option<Self> {
648        match kind {
649            TimelineEventKind::Decrypted(decrypted) => {
650                if let Some(unsigned_decryption_result) =
651                    decrypted.unsigned_encryption_info.as_ref().and_then(|unsigned_map| {
652                        unsigned_map.get(&UnsignedEventLocation::RelationsThreadLatestEvent)
653                    })
654                {
655                    match unsigned_decryption_result {
656                        UnsignedDecryptionResult::Decrypted(encryption_info) => {
657                            // The bundled event was encrypted, and we could
658                            // decrypt it: pass that information around.
659                            return Some(TimelineEvent::from_decrypted_with_max_timestamp(
660                                DecryptedRoomEvent {
661                                    // Safety: A decrypted event always includes
662                                    // a room_id in its payload.
663                                    event: latest_event.cast_unchecked(),
664                                    encryption_info: encryption_info.clone(),
665                                    // A bundled latest event is never a thread
666                                    // root. It could have a replacement event,
667                                    // but we don't carry this information
668                                    // around.
669                                    unsigned_encryption_info: None,
670                                },
671                                None,
672                                max_timestamp,
673                            ));
674                        }
675
676                        UnsignedDecryptionResult::UnableToDecrypt(utd_info) => {
677                            // The bundled event was a UTD; store that
678                            // information.
679                            return Some(TimelineEvent::from_utd_with_max_timestamp(
680                                latest_event.cast(),
681                                utd_info.clone(),
682                                max_timestamp,
683                            ));
684                        }
685                    }
686                }
687            }
688
689            TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => {
690                // Figure based on the event type below.
691            }
692        }
693
694        match latest_event.get_field::<MessageLikeEventType>("type") {
695            Ok(None) => {
696                let event_id = latest_event.get_field::<OwnedEventId>("event_id").ok().flatten();
697                warn!(
698                    ?event_id,
699                    "couldn't deserialize bundled latest thread event: missing `type` field \
700                     in bundled latest thread event"
701                );
702                None
703            }
704
705            Ok(Some(MessageLikeEventType::RoomEncrypted)) => {
706                // The bundled latest thread event is encrypted, but we didn't
707                // have any information about it in the unsigned map. Try to
708                // fetch the information from the content instead.
709                let session_id = if let Some(content) =
710                    latest_event.get_field::<EncryptedEventScheme>("content").ok().flatten()
711                {
712                    match content {
713                        EncryptedEventScheme::MegolmV1AesSha2(content) => Some(content.session_id),
714                        _ => None,
715                    }
716                } else {
717                    None
718                };
719
720                Some(TimelineEvent::from_utd_with_max_timestamp(
721                    latest_event.cast(),
722                    UnableToDecryptInfo { session_id, reason: UnableToDecryptReason::Unknown },
723                    max_timestamp,
724                ))
725            }
726
727            Ok(_) => Some(TimelineEvent::from_plaintext_with_max_timestamp(
728                latest_event.cast(),
729                max_timestamp,
730            )),
731
732            Err(err) => {
733                let event_id = latest_event.get_field::<OwnedEventId>("event_id").ok().flatten();
734                warn!(?event_id, "couldn't deserialize bundled latest thread event's type: {err}");
735                None
736            }
737        }
738    }
739
740    /// Read the current push actions.
741    ///
742    /// Returns `None` if they were never computed, or if they could not be
743    /// computed.
744    pub fn push_actions(&self) -> Option<&[Action]> {
745        self.push_actions.as_deref()
746    }
747
748    /// Set the push actions for this event.
749    pub fn set_push_actions(&mut self, push_actions: Vec<Action>) {
750        self.push_actions = Some(push_actions);
751    }
752
753    /// Get the (cached) event ID of this [`TimelineEvent`] if the event has any
754    /// valid ID.
755    pub fn event_id(&self) -> Option<&EventId> {
756        self.event_id.as_deref()
757    }
758
759    /// Get the sender of this [`TimelineEvent`] if the event has one.
760    pub fn sender(&self) -> Option<OwnedUserId> {
761        self.kind.parse_sender()
762    }
763
764    /// Returns a reference to the (potentially decrypted) Matrix event inside
765    /// this [`TimelineEvent`].
766    pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
767        self.kind.raw()
768    }
769
770    /// Replace the raw event included in this item by another one.
771    pub fn replace_raw(&mut self, replacement: Raw<AnyTimelineEvent>) {
772        match &mut self.kind {
773            TimelineEventKind::Decrypted(decrypted) => decrypted.event = replacement,
774            TimelineEventKind::UnableToDecrypt { event, .. }
775            | TimelineEventKind::PlainText { event } => {
776                // It's safe to cast `AnyMessageLikeEvent` into
777                // `AnySyncMessageLikeEvent`, because the former contains a
778                // superset of the fields included in the latter.
779                *event = replacement.cast();
780            }
781        }
782
783        self.event_id = self.kind.parse_event_id();
784    }
785
786    /// Get the timestamp.
787    ///
788    /// If the timestamp is missing (most likely because the event has been
789    /// created before the addition of the [`TimelineEvent::timestamp`] field),
790    /// this method will try to extract it from the `origin_server_ts` value. If
791    /// the `origin_server_ts` value is malicious, it will be capped to
792    /// [`MilliSecondsSinceUnixEpoch::now`]. It means that the returned value
793    /// might not be constant.
794    pub fn timestamp(&self) -> Option<MilliSecondsSinceUnixEpoch> {
795        self.timestamp.or_else(|| {
796            warn!("`TimelineEvent::timestamp` is parsing the raw event to extract the `timestamp`");
797
798            extract_timestamp(self.raw(), MilliSecondsSinceUnixEpoch::now())
799        })
800    }
801
802    /// Get the timestamp value, without trying to backfill it if `None`.
803    pub fn timestamp_raw(&self) -> Option<MilliSecondsSinceUnixEpoch> {
804        self.timestamp
805    }
806
807    /// If the event was a decrypted event that was successfully decrypted, get
808    /// its encryption info. Otherwise, `None`.
809    pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
810        self.kind.encryption_info()
811    }
812
813    /// Takes ownership of this [`TimelineEvent`], returning the (potentially
814    /// decrypted) Matrix event within.
815    pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
816        self.kind.into_raw()
817    }
818
819    /// Checks whether this event is a thread root, i.e. in-thread events are
820    /// attached to it.
821    pub fn is_thread_root(&self) -> bool {
822        extract_is_thread_root(self.raw())
823    }
824
825    /// If this event is a thread root, find, parse, and create the
826    /// [`ThreadSummary`] of the thread.
827    ///
828    /// Note that this value is **NEVER** updated. An event is immutable in
829    /// Matrix. However, the thread summary is updated dynamically in the SDK,
830    /// see `matrix_sdk_base::event_cache::thread::ThreadInfo`.
831    pub fn thread_summary(&self) -> Option<ThreadSummary> {
832        extract_bundled_thread(self.raw()).map(|bundled_thread| {
833            ThreadSummary::new(
834                bundled_thread.latest_event.get_field::<OwnedEventId>("event_id").ok().flatten(),
835                bundled_thread.count,
836            )
837        })
838    }
839
840    /// If this event is a thread root, find, parse, and create the latest event
841    /// of the thread.
842    ///
843    /// The latest event comes bundled with this event, if it was provided in
844    /// the unsigned relations of this event.
845    ///
846    /// Note that this value is **NEVER** updated. An event is immutable in
847    /// Matrix. However, the thread summary is updated dynamically in the SDK,
848    /// see `matrix_sdk_base::event_cache::thread::ThreadInfo`.
849    pub fn bundled_latest_thread_event(&self) -> Option<Self> {
850        let bundled_thread = extract_bundled_thread(self.raw())?;
851
852        Self::from_bundled_latest_event(
853            &self.kind,
854            bundled_thread.latest_event,
855            self.timestamp_raw().unwrap_or_else(MilliSecondsSinceUnixEpoch::now),
856        )
857    }
858
859    /// Combo of [`Self::thread_summary`] and
860    /// [`Self::bundled_latest_thread_event`]: if this event is a thread root, find,
861    /// parse **once**, and create the [`ThreadSummary`] and the
862    /// [`TimelineEvent`] representing the latest event of the thread.
863    ///
864    /// Note that this value is **NEVER** updated. An event is immutable in
865    /// Matrix. However, the thread summary is updated dynamically in the SDK,
866    /// see `matrix_sdk_base::event_cache::thread::ThreadInfo`.
867    pub fn thread_summary_with_latest_event(&self) -> Option<(ThreadSummary, Self)> {
868        extract_bundled_thread(self.raw()).and_then(|bundled_thread| {
869            Some((
870                ThreadSummary::new(
871                    bundled_thread
872                        .latest_event
873                        .get_field::<OwnedEventId>("event_id")
874                        .ok()
875                        .flatten(),
876                    bundled_thread.count,
877                ),
878                Self::from_bundled_latest_event(
879                    &self.kind,
880                    bundled_thread.latest_event,
881                    self.timestamp_raw().unwrap_or_else(MilliSecondsSinceUnixEpoch::now),
882                )?,
883            ))
884        })
885    }
886}
887
888impl<'de> Deserialize<'de> for TimelineEvent {
889    /// Custom deserializer for [`TimelineEvent`], to support older formats.
890    ///
891    /// Ideally we might use an untagged enum and then convert from that;
892    /// however, that doesn't work due to a
893    /// [serde bug](https://github.com/serde-rs/json/issues/497).
894    ///
895    /// Instead, we first deserialize into an unstructured JSON map, and then
896    /// inspect the json to figure out which format we have.
897    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
898    where
899        D: serde::Deserializer<'de>,
900    {
901        use serde_json::{Map, Value};
902
903        // First, deserialize to an unstructured JSON map
904        let value = Map::<String, Value>::deserialize(deserializer)?;
905
906        // If we have a top-level `event`, it's V0
907        if value.contains_key("event") {
908            let v0: SyncTimelineEventDeserializationHelperV0 =
909                serde_json::from_value(Value::Object(value)).map_err(|e| {
910                    serde::de::Error::custom(format!(
911                        "Unable to deserialize V0-format TimelineEvent: {e}",
912                    ))
913                })?;
914            Ok(v0.into())
915        }
916        // Otherwise, it's V1
917        else {
918            let v1: SyncTimelineEventDeserializationHelperV1 =
919                serde_json::from_value(Value::Object(value)).map_err(|e| {
920                    serde::de::Error::custom(format!(
921                        "Unable to deserialize V1-format TimelineEvent: {e}",
922                    ))
923                })?;
924            Ok(v1.into())
925        }
926    }
927}
928
929/// The event within a [`TimelineEvent`], together with encryption data.
930#[derive(Clone, Serialize, Deserialize)]
931pub enum TimelineEventKind {
932    /// A successfully-decrypted encrypted event.
933    Decrypted(DecryptedRoomEvent),
934
935    /// An encrypted event which could not be decrypted.
936    UnableToDecrypt {
937        /// The `m.room.encrypted` event. Depending on the source of the event,
938        /// it could actually be an [`AnyTimelineEvent`] (i.e., it may have a
939        /// `room_id` property).
940        event: Raw<AnySyncTimelineEvent>,
941
942        /// Information on the reason we failed to decrypt
943        utd_info: UnableToDecryptInfo,
944    },
945
946    /// An unencrypted event.
947    PlainText {
948        /// The actual event. Depending on the source of the event, it could
949        /// actually be a [`AnyTimelineEvent`] (which differs from
950        /// [`AnySyncTimelineEvent`] by the addition of a `room_id` property).
951        event: Raw<AnySyncTimelineEvent>,
952    },
953}
954
955impl TimelineEventKind {
956    /// Returns a reference to the (potentially decrypted) Matrix event inside
957    /// this `TimelineEvent`.
958    pub fn raw(&self) -> &Raw<AnySyncTimelineEvent> {
959        match self {
960            // It is safe to cast from an `AnyMessageLikeEvent` (i.e. JSON which
961            // does _not_ contain a `state_key` and _does_ contain a `room_id`)
962            // into an `AnySyncTimelineEvent` (i.e. JSON which _may_ contain a
963            // `state_key` and is _not_ expected to contain a `room_id`). It
964            // just means that the `room_id` will be ignored in a future
965            // deserialization.
966            TimelineEventKind::Decrypted(d) => d.event.cast_ref(),
967            TimelineEventKind::UnableToDecrypt { event, .. } => event,
968            TimelineEventKind::PlainText { event } => event,
969        }
970    }
971
972    /// Parse the event ID of this `TimelineEventKind` if the event has any
973    /// valid id.
974    pub fn parse_event_id(&self) -> Option<OwnedEventId> {
975        self.raw().get_field::<OwnedEventId>("event_id").ok().flatten()
976    }
977
978    /// Parse the sender of this [`TimelineEventKind`] if the event has one.
979    pub fn parse_sender(&self) -> Option<OwnedUserId> {
980        self.raw().get_field::<OwnedUserId>("sender").ok().flatten()
981    }
982
983    /// Whether we could not decrypt the event (i.e. it is a UTD).
984    pub fn is_utd(&self) -> bool {
985        matches!(self, TimelineEventKind::UnableToDecrypt { .. })
986    }
987
988    /// If the event was a decrypted event that was successfully decrypted, get
989    /// its encryption info. Otherwise, `None`.
990    pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
991        match self {
992            TimelineEventKind::Decrypted(d) => Some(&d.encryption_info),
993            TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => None,
994        }
995    }
996
997    /// If the event was a decrypted event that was successfully decrypted, get
998    /// the map of decryption metadata related to the bundled events.
999    pub fn unsigned_encryption_map(
1000        &self,
1001    ) -> Option<&BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>> {
1002        match self {
1003            TimelineEventKind::Decrypted(d) => d.unsigned_encryption_info.as_ref(),
1004            TimelineEventKind::UnableToDecrypt { .. } | TimelineEventKind::PlainText { .. } => None,
1005        }
1006    }
1007
1008    /// Takes ownership of this `TimelineEvent`, returning the (potentially
1009    /// decrypted) Matrix event within.
1010    pub fn into_raw(self) -> Raw<AnySyncTimelineEvent> {
1011        match self {
1012            // It is safe to cast from an `AnyMessageLikeEvent` (i.e. JSON which
1013            // does _not_ contain a `state_key` and _does_ contain a `room_id`)
1014            // into an `AnySyncTimelineEvent` (i.e. JSON which _may_ contain a
1015            // `state_key` and is _not_ expected to contain a `room_id`). It
1016            // just means that the `room_id` will be ignored in a future
1017            // deserialization.
1018            TimelineEventKind::Decrypted(d) => d.event.cast(),
1019            TimelineEventKind::UnableToDecrypt { event, .. } => event,
1020            TimelineEventKind::PlainText { event } => event,
1021        }
1022    }
1023
1024    /// The Megolm session ID that was used to send this event, if it was
1025    /// encrypted.
1026    pub fn session_id(&self) -> Option<&str> {
1027        match self {
1028            TimelineEventKind::Decrypted(decrypted_room_event) => {
1029                decrypted_room_event.encryption_info.session_id()
1030            }
1031            TimelineEventKind::UnableToDecrypt { utd_info, .. } => utd_info.session_id.as_deref(),
1032            TimelineEventKind::PlainText { .. } => None,
1033        }
1034    }
1035
1036    /// Parse the event type of this event.
1037    ///
1038    /// Returns `None` if there isn't an event type or if the event failed to be
1039    /// deserialized.
1040    pub fn event_type(&self) -> Option<String> {
1041        self.raw().get_field("type").ok().flatten()
1042    }
1043}
1044
1045#[cfg(not(tarpaulin_include))]
1046impl fmt::Debug for TimelineEventKind {
1047    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1048        match &self {
1049            Self::PlainText { event } => f
1050                .debug_struct("TimelineEventKind::PlainText")
1051                .field("event", &DebugRawEvent(event))
1052                .finish(),
1053
1054            Self::UnableToDecrypt { event, utd_info } => f
1055                .debug_struct("TimelineEventKind::UnableToDecrypt")
1056                .field("event", &DebugRawEvent(event))
1057                .field("utd_info", &utd_info)
1058                .finish(),
1059
1060            Self::Decrypted(decrypted) => {
1061                f.debug_tuple("TimelineEventKind::Decrypted").field(decrypted).finish()
1062            }
1063        }
1064    }
1065}
1066
1067/// A successfully-decrypted encrypted event.
1068#[derive(Clone, Serialize, Deserialize)]
1069pub struct DecryptedRoomEvent {
1070    /// The decrypted event.
1071    ///
1072    /// Note: it's not an error that this contains an [`AnyTimelineEvent`] (as
1073    /// opposed to an [`AnySyncTimelineEvent`]): an encrypted payload
1074    /// _always contains_ a room id, by the [spec].
1075    ///
1076    /// [spec]: https://spec.matrix.org/v1.12/client-server-api/#mmegolmv1aes-sha2
1077    pub event: Raw<AnyTimelineEvent>,
1078
1079    /// The encryption info about the event.
1080    pub encryption_info: Arc<EncryptionInfo>,
1081
1082    /// The encryption info about the events bundled in the `unsigned` object.
1083    ///
1084    /// Will be `None` if no bundled event was encrypted.
1085    #[serde(skip_serializing_if = "Option::is_none")]
1086    pub unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
1087}
1088
1089#[cfg(not(tarpaulin_include))]
1090impl fmt::Debug for DecryptedRoomEvent {
1091    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1092        let DecryptedRoomEvent { event, encryption_info, unsigned_encryption_info } = self;
1093
1094        f.debug_struct("DecryptedRoomEvent")
1095            .field("event", &DebugRawEvent(event))
1096            .field("encryption_info", encryption_info)
1097            .maybe_field("unsigned_encryption_info", unsigned_encryption_info)
1098            .finish()
1099    }
1100}
1101
1102/// The location of an event bundled in an `unsigned` object.
1103#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
1104pub enum UnsignedEventLocation {
1105    /// An event at the `m.replace` key of the `m.relations` object, that is a
1106    /// bundled replacement.
1107    RelationsReplace,
1108    /// An event at the `latest_event` key of the `m.thread` object of the
1109    /// `m.relations` object, that is the latest event of a thread.
1110    RelationsThreadLatestEvent,
1111}
1112
1113impl UnsignedEventLocation {
1114    /// Find the mutable JSON value at this location in the given unsigned
1115    /// object.
1116    ///
1117    /// # Arguments
1118    ///
1119    /// - `unsigned` - The `unsigned` property of an event as a JSON object.
1120    pub fn find_mut<'a>(&self, unsigned: &'a mut JsonObject) -> Option<&'a mut serde_json::Value> {
1121        let relations = unsigned.get_mut("m.relations")?.as_object_mut()?;
1122
1123        match self {
1124            Self::RelationsReplace => relations.get_mut("m.replace"),
1125            Self::RelationsThreadLatestEvent => {
1126                relations.get_mut("m.thread")?.as_object_mut()?.get_mut("latest_event")
1127            }
1128        }
1129    }
1130}
1131
1132/// The result of the decryption of an event bundled in an `unsigned` object.
1133#[derive(Debug, Clone, Serialize, Deserialize)]
1134pub enum UnsignedDecryptionResult {
1135    /// The event was successfully decrypted.
1136    Decrypted(Arc<EncryptionInfo>),
1137    /// The event failed to be decrypted.
1138    UnableToDecrypt(UnableToDecryptInfo),
1139}
1140
1141impl UnsignedDecryptionResult {
1142    /// Returns the encryption info for this bundled event if it was
1143    /// successfully decrypted.
1144    pub fn encryption_info(&self) -> Option<&Arc<EncryptionInfo>> {
1145        match self {
1146            Self::Decrypted(info) => Some(info),
1147            Self::UnableToDecrypt(_) => None,
1148        }
1149    }
1150}
1151
1152/// Metadata about an event that could not be decrypted.
1153#[derive(Debug, Clone, Serialize, Deserialize)]
1154pub struct UnableToDecryptInfo {
1155    /// The ID of the session used to encrypt the message, if it used the
1156    /// `m.megolm.v1.aes-sha2` algorithm.
1157    #[serde(skip_serializing_if = "Option::is_none")]
1158    pub session_id: Option<String>,
1159
1160    /// Reason code for the decryption failure
1161    #[serde(default = "unknown_utd_reason", deserialize_with = "deserialize_utd_reason")]
1162    pub reason: UnableToDecryptReason,
1163}
1164
1165fn unknown_utd_reason() -> UnableToDecryptReason {
1166    UnableToDecryptReason::Unknown
1167}
1168
1169/// Provides basic backward compatibility for deserializing older serialized
1170/// `UnableToDecryptReason` values.
1171pub fn deserialize_utd_reason<'de, D>(d: D) -> Result<UnableToDecryptReason, D::Error>
1172where
1173    D: serde::Deserializer<'de>,
1174{
1175    // Start by deserializing as to an untyped JSON value.
1176    let v: serde_json::Value = Deserialize::deserialize(d)?;
1177    // Backwards compatibility: `MissingMegolmSession` used to be stored without
1178    // the withheld code.
1179    if v.as_str().is_some_and(|s| s == "MissingMegolmSession") {
1180        return Ok(UnableToDecryptReason::MissingMegolmSession { withheld_code: None });
1181    }
1182    // Otherwise, use the derived deserialize impl to turn the JSON into a
1183    // UnableToDecryptReason
1184    serde_json::from_value::<UnableToDecryptReason>(v).map_err(serde::de::Error::custom)
1185}
1186
1187/// Reason code for a decryption failure
1188#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1189pub enum UnableToDecryptReason {
1190    /// The reason for the decryption failure is unknown. This is only intended
1191    /// for use when deserializing old UnableToDecryptInfo instances.
1192    #[doc(hidden)]
1193    Unknown,
1194
1195    /// The `m.room.encrypted` event that should have been decrypted is
1196    /// malformed in some way (e.g. unsupported algorithm, missing fields,
1197    /// unknown megolm message type).
1198    MalformedEncryptedEvent,
1199
1200    /// Decryption failed because we're missing the megolm session that was used
1201    /// to encrypt the event.
1202    MissingMegolmSession {
1203        /// If the key was withheld on purpose, the associated code. `None`
1204        /// means no withheld code was received.
1205        withheld_code: Option<WithheldCode>,
1206    },
1207
1208    /// Decryption failed because, while we have the megolm session that was
1209    /// used to encrypt the message, it is ratcheted too far forward.
1210    UnknownMegolmMessageIndex,
1211
1212    /// We found the Megolm session, but were unable to decrypt the event using
1213    /// that session for some reason (e.g. incorrect MAC).
1214    ///
1215    /// This represents all `vodozemac::megolm::DecryptionError`s, except
1216    /// `UnknownMessageIndex`, which is represented as
1217    /// `UnknownMegolmMessageIndex`.
1218    MegolmDecryptionFailure,
1219
1220    /// The event could not be deserialized after decryption.
1221    PayloadDeserializationFailure,
1222
1223    /// Decryption failed because of a mismatch between the identity keys of the
1224    /// device we received the room key from and the identity keys recorded in
1225    /// the plaintext of the room key to-device message.
1226    MismatchedIdentityKeys,
1227
1228    /// An encrypted message wasn't decrypted, because the sender's
1229    /// cross-signing identity did not satisfy the requested `TrustRequirement`.
1230    SenderIdentityNotTrusted(VerificationLevel),
1231
1232    /// The outer state key could not be verified against the inner encrypted
1233    /// state key and type.
1234    #[cfg(feature = "experimental-encrypted-state-events")]
1235    StateKeyVerificationFailed,
1236}
1237
1238impl UnableToDecryptReason {
1239    /// Returns true if this UTD is due to a missing room key (and hence might
1240    /// resolve itself if we wait a bit.)
1241    pub fn is_missing_room_key(&self) -> bool {
1242        // In case of MissingMegolmSession with a withheld code we return false
1243        // here given that this API is used to decide if waiting a bit will
1244        // help.
1245        matches!(
1246            self,
1247            Self::MissingMegolmSession { withheld_code: None } | Self::UnknownMegolmMessageIndex
1248        )
1249    }
1250}
1251
1252/// A machine-readable code for why a Megolm key was not sent.
1253///
1254/// 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.
1255#[derive(
1256    Clone,
1257    PartialEq,
1258    Eq,
1259    Hash,
1260    AsStrAsRefStr,
1261    AsRefStr,
1262    FromString,
1263    DebugAsRefStr,
1264    SerializeAsRefStr,
1265    DeserializeFromCowStr,
1266)]
1267pub enum WithheldCode {
1268    /// the user/device was blacklisted.
1269    #[ruma_enum(rename = "m.blacklisted")]
1270    Blacklisted,
1271
1272    /// the user/devices is unverified.
1273    #[ruma_enum(rename = "m.unverified")]
1274    Unverified,
1275
1276    /// The user/device is not allowed have the key. For example, this would
1277    /// usually be sent in response to a key request if the user was not in the
1278    /// room when the message was sent.
1279    #[ruma_enum(rename = "m.unauthorised")]
1280    Unauthorised,
1281
1282    /// Sent in reply to a key request if the device that the key is requested
1283    /// from does not have the requested key.
1284    #[ruma_enum(rename = "m.unavailable")]
1285    Unavailable,
1286
1287    /// An olm session could not be established. This may happen, for example,
1288    /// if the sender was unable to obtain a one-time key from the recipient.
1289    #[ruma_enum(rename = "m.no_olm")]
1290    NoOlm,
1291
1292    /// Normally used when sharing history, per [MSC4268]: indicates that the
1293    /// session was not marked as "shared_history".
1294    ///
1295    /// [MSC4268]: https://github.com/matrix-org/matrix-spec-proposals/pull/4268
1296    #[ruma_enum(rename = "m.history_not_shared", alias = "io.element.msc4268.history_not_shared")]
1297    HistoryNotShared,
1298
1299    #[doc(hidden)]
1300    _Custom(PrivOwnedStr),
1301}
1302
1303impl fmt::Display for WithheldCode {
1304    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
1305        let string = match self {
1306            WithheldCode::Blacklisted => "The sender has blocked you.",
1307            WithheldCode::Unverified => "The sender has disabled encrypting to unverified devices.",
1308            WithheldCode::Unauthorised => "You are not authorised to read the message.",
1309            WithheldCode::Unavailable => "The requested key was not found.",
1310            WithheldCode::NoOlm => "Unable to establish a secure channel.",
1311            WithheldCode::HistoryNotShared => "The sender disabled sharing encrypted history.",
1312            _ => self.as_str(),
1313        };
1314
1315        f.write_str(string)
1316    }
1317}
1318
1319// The Ruma macro expects the type to have this name. The payload is counter
1320// intuitively made public in order to avoid having multiple copies of this
1321// struct.
1322#[doc(hidden)]
1323#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
1324pub struct PrivOwnedStr(pub Box<str>);
1325
1326#[cfg(not(tarpaulin_include))]
1327impl fmt::Debug for PrivOwnedStr {
1328    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1329        self.0.fmt(f)
1330    }
1331}
1332
1333/// Deserialization helper for [`TimelineEvent`], for the modern format.
1334///
1335/// This has the exact same fields as [`TimelineEvent`] itself, but has a
1336/// regular `Deserialize` implementation.
1337#[derive(Debug, Deserialize)]
1338struct SyncTimelineEventDeserializationHelperV1 {
1339    /// The event itself, together with any information on decryption.
1340    kind: TimelineEventKind,
1341
1342    /// The timestamp of the event. It's the `origin_server_ts` value (if any),
1343    /// corrected if detected as malicious.
1344    #[serde(default)]
1345    timestamp: Option<MilliSecondsSinceUnixEpoch>,
1346
1347    /// The push actions associated with this event.
1348    #[serde(default)]
1349    push_actions: Vec<Action>,
1350}
1351
1352impl From<SyncTimelineEventDeserializationHelperV1> for TimelineEvent {
1353    fn from(value: SyncTimelineEventDeserializationHelperV1) -> Self {
1354        let SyncTimelineEventDeserializationHelperV1 { kind, timestamp, push_actions } = value;
1355
1356        // If `timestamp` is `None`, it is very likely that the event was
1357        // serialised before the addition of the `timestamp` field. We _could_
1358        // compute it here, but if the `timestamp` was malicious, it means we
1359        // are going to _cap_ the `timestamp` to `now()` for every
1360        // deserialisation. It is annoying because it means the event is no
1361        // longer deterministic, it's not constant. We don't want that.
1362        // Consequently, we keep `None` here, and we let
1363        // [`TimelineEvent::timestamp`] to handle that case for us.
1364
1365        TimelineEvent {
1366            event_id: kind.parse_event_id(),
1367            kind,
1368            timestamp,
1369            push_actions: Some(push_actions),
1370        }
1371    }
1372}
1373
1374/// Deserialization helper for [`TimelineEvent`], for an older format.
1375#[derive(Deserialize)]
1376struct SyncTimelineEventDeserializationHelperV0 {
1377    /// The actual event.
1378    event: Raw<AnySyncTimelineEvent>,
1379
1380    /// The encryption info about the event.
1381    ///
1382    /// Will be `None` if the event was not encrypted.
1383    encryption_info: Option<Arc<EncryptionInfo>>,
1384
1385    /// The push actions associated with this event.
1386    #[serde(default)]
1387    push_actions: Vec<Action>,
1388
1389    /// The encryption info about the events bundled in the `unsigned` object.
1390    ///
1391    /// Will be `None` if no bundled event was encrypted.
1392    unsigned_encryption_info: Option<BTreeMap<UnsignedEventLocation, UnsignedDecryptionResult>>,
1393}
1394
1395impl From<SyncTimelineEventDeserializationHelperV0> for TimelineEvent {
1396    fn from(value: SyncTimelineEventDeserializationHelperV0) -> Self {
1397        let SyncTimelineEventDeserializationHelperV0 {
1398            event,
1399            encryption_info,
1400            push_actions,
1401            unsigned_encryption_info,
1402        } = value;
1403
1404        // We do not compute the `timestamp` value here because if the
1405        // `timestamp` is malicious, it means we are going to _cap_ the
1406        // `timestamp` to `now()` for every deserialisation. It is annoying
1407        // because it means the event is no longer deterministic, it's not
1408        // constant. We don't want that. Consequently, we keep `None` here, and
1409        // we let [`TimelineEvent::timestamp`] to handle that case for us.
1410        let timestamp = None;
1411
1412        let kind = match encryption_info {
1413            Some(encryption_info) => {
1414                TimelineEventKind::Decrypted(DecryptedRoomEvent {
1415                    // We cast from `Raw<AnySyncTimelineEvent>` to
1416                    // `Raw<AnyMessageLikeEvent>`, which means we are asserting
1417                    // that it contains a room_id. That _should_ be ok, because
1418                    // if this is genuinely a decrypted room event (as the
1419                    // encryption_info indicates), then it will have a room_id.
1420                    event: event.cast_unchecked(),
1421                    encryption_info,
1422                    unsigned_encryption_info,
1423                })
1424            }
1425
1426            None => TimelineEventKind::PlainText { event },
1427        };
1428
1429        TimelineEvent {
1430            event_id: kind.parse_event_id(),
1431            kind,
1432            timestamp,
1433            push_actions: Some(push_actions),
1434        }
1435    }
1436}
1437
1438/// Reason code for a to-device decryption failure
1439#[derive(Debug, Clone, PartialEq)]
1440pub enum ToDeviceUnableToDecryptReason {
1441    /// An error occurred while encrypting the event. This covers all `OlmError`
1442    /// types.
1443    DecryptionFailure,
1444
1445    /// We refused to decrypt the message because the sender's device is not
1446    /// verified, or more generally, the sender's identity did not match the
1447    /// trust requirement we were asked to provide.
1448    UnverifiedSenderDevice,
1449
1450    /// We have no `OlmMachine`. This should not happen unless we forget to set
1451    /// things up by calling `OlmMachine::activate()`.
1452    NoOlmMachine,
1453
1454    /// The Matrix SDK was compiled without encryption support.
1455    EncryptionIsDisabled,
1456}
1457
1458/// Metadata about a to-device event that could not be decrypted.
1459#[derive(Clone, Debug)]
1460pub struct ToDeviceUnableToDecryptInfo {
1461    /// Reason code for the decryption failure
1462    pub reason: ToDeviceUnableToDecryptReason,
1463}
1464
1465/// Represents a to-device event after it has been processed by the Olm machine.
1466#[derive(Clone, Debug)]
1467pub enum ProcessedToDeviceEvent {
1468    /// A successfully-decrypted encrypted event. Contains the raw decrypted
1469    /// event and encryption info
1470    Decrypted {
1471        /// The raw decrypted event
1472        raw: Raw<AnyToDeviceEvent>,
1473        /// The Olm encryption info
1474        encryption_info: EncryptionInfo,
1475    },
1476
1477    /// An encrypted event which could not be decrypted.
1478    UnableToDecrypt {
1479        encrypted_event: Raw<AnyToDeviceEvent>,
1480        utd_info: ToDeviceUnableToDecryptInfo,
1481    },
1482
1483    /// An unencrypted event.
1484    PlainText(Raw<AnyToDeviceEvent>),
1485
1486    /// An invalid to device event that was ignored because it is missing some
1487    /// required information to be processed (like no event `type` for example)
1488    Invalid(Raw<AnyToDeviceEvent>),
1489}
1490
1491impl ProcessedToDeviceEvent {
1492    /// Converts a ProcessedToDeviceEvent to the `Raw<AnyToDeviceEvent>` it
1493    /// encapsulates
1494    pub fn to_raw(&self) -> Raw<AnyToDeviceEvent> {
1495        match self {
1496            ProcessedToDeviceEvent::Decrypted { raw, .. } => raw.clone(),
1497            ProcessedToDeviceEvent::UnableToDecrypt { encrypted_event, .. } => {
1498                encrypted_event.clone()
1499            }
1500            ProcessedToDeviceEvent::PlainText(event) => event.clone(),
1501            ProcessedToDeviceEvent::Invalid(event) => event.clone(),
1502        }
1503    }
1504
1505    /// Gets the raw to-device event.
1506    pub fn as_raw(&self) -> &Raw<AnyToDeviceEvent> {
1507        match self {
1508            ProcessedToDeviceEvent::Decrypted { raw, .. } => raw,
1509            ProcessedToDeviceEvent::UnableToDecrypt { encrypted_event, .. } => encrypted_event,
1510            ProcessedToDeviceEvent::PlainText(event) => event,
1511            ProcessedToDeviceEvent::Invalid(event) => event,
1512        }
1513    }
1514}
1515
1516#[cfg(test)]
1517mod tests {
1518    use std::{collections::BTreeMap, sync::Arc};
1519
1520    use assert_matches::assert_matches;
1521    use insta::{assert_json_snapshot, with_settings};
1522    use ruma::{
1523        DeviceKeyAlgorithm, MilliSecondsSinceUnixEpoch, UInt, event_id,
1524        events::{AnySyncTimelineEvent, room::message::RoomMessageEventContent},
1525        owned_device_id, owned_user_id,
1526        serde::Raw,
1527    };
1528    use serde::Deserialize;
1529    use serde_json::json;
1530    use strass::assert_let;
1531
1532    use super::{
1533        AlgorithmInfo, DecryptedRoomEvent, DeviceLinkProblem, EncryptionInfo, ShieldState,
1534        ShieldStateCode, TimelineEvent, TimelineEventKind, UnableToDecryptInfo,
1535        UnableToDecryptReason, UnsignedDecryptionResult, UnsignedEventLocation, VerificationLevel,
1536        VerificationState, WithheldCode,
1537    };
1538
1539    fn example_event() -> serde_json::Value {
1540        json!({
1541            "content": RoomMessageEventContent::text_plain("secret"),
1542            "type": "m.room.message",
1543            "event_id": "$xxxxx:example.org",
1544            "room_id": "!someroom:example.com",
1545            "origin_server_ts": 2189,
1546            "sender": "@carl:example.com",
1547        })
1548    }
1549
1550    #[test]
1551    fn sync_timeline_debug_content() {
1552        let room_event =
1553            TimelineEvent::from_plaintext(Raw::new(&example_event()).unwrap().cast_unchecked());
1554        let debug_s = format!("{room_event:?}");
1555        assert!(
1556            !debug_s.contains("secret"),
1557            "Debug representation contains event content!\n{debug_s}"
1558        );
1559    }
1560
1561    #[test]
1562    fn old_verification_state_to_new_migration() {
1563        #[derive(Deserialize)]
1564        struct State {
1565            state: VerificationState,
1566        }
1567
1568        let state = json!({
1569            "state": "Trusted",
1570        });
1571        let deserialized: State =
1572            serde_json::from_value(state).expect("We can deserialize the old trusted value");
1573        assert_eq!(deserialized.state, VerificationState::Verified);
1574
1575        let state = json!({
1576            "state": "UnknownDevice",
1577        });
1578
1579        let deserialized: State =
1580            serde_json::from_value(state).expect("We can deserialize the old unknown device value");
1581
1582        assert_eq!(
1583            deserialized.state,
1584            VerificationState::Unverified(VerificationLevel::None(
1585                DeviceLinkProblem::MissingDevice
1586            ))
1587        );
1588
1589        let state = json!({
1590            "state": "Untrusted",
1591        });
1592        let deserialized: State =
1593            serde_json::from_value(state).expect("We can deserialize the old trusted value");
1594
1595        assert_eq!(
1596            deserialized.state,
1597            VerificationState::Unverified(VerificationLevel::UnsignedDevice)
1598        );
1599    }
1600
1601    #[test]
1602    fn test_verification_level_deserializes() {
1603        // Given a JSON VerificationLevel
1604        #[derive(Deserialize)]
1605        struct Container {
1606            verification_level: VerificationLevel,
1607        }
1608        let container = json!({ "verification_level": "VerificationViolation" });
1609
1610        // When we deserialize it
1611        let deserialized: Container = serde_json::from_value(container)
1612            .expect("We can deserialize the old PreviouslyVerified value");
1613
1614        // Then it is populated correctly
1615        assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1616    }
1617
1618    #[test]
1619    fn test_verification_level_deserializes_from_old_previously_verified_value() {
1620        // Given a JSON VerificationLevel with the old value PreviouslyVerified
1621        #[derive(Deserialize)]
1622        struct Container {
1623            verification_level: VerificationLevel,
1624        }
1625        let container = json!({ "verification_level": "PreviouslyVerified" });
1626
1627        // When we deserialize it
1628        let deserialized: Container = serde_json::from_value(container)
1629            .expect("We can deserialize the old PreviouslyVerified value");
1630
1631        // Then it is migrated to the new value
1632        assert_eq!(deserialized.verification_level, VerificationLevel::VerificationViolation);
1633    }
1634
1635    #[test]
1636    fn test_shield_state_code_deserializes() {
1637        // Given a JSON ShieldStateCode with value VerificationViolation
1638        #[derive(Deserialize)]
1639        struct Container {
1640            shield_state_code: ShieldStateCode,
1641        }
1642        let container = json!({ "shield_state_code": "VerificationViolation" });
1643
1644        // When we deserialize it
1645        let deserialized: Container = serde_json::from_value(container)
1646            .expect("We can deserialize the old PreviouslyVerified value");
1647
1648        // Then it is populated correctly
1649        assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1650    }
1651
1652    #[test]
1653    fn test_shield_state_code_deserializes_from_old_previously_verified_value() {
1654        // Given a JSON ShieldStateCode with the old value PreviouslyVerified
1655        #[derive(Deserialize)]
1656        struct Container {
1657            shield_state_code: ShieldStateCode,
1658        }
1659        let container = json!({ "shield_state_code": "PreviouslyVerified" });
1660
1661        // When we deserialize it
1662        let deserialized: Container = serde_json::from_value(container)
1663            .expect("We can deserialize the old PreviouslyVerified value");
1664
1665        // Then it is migrated to the new value
1666        assert_eq!(deserialized.shield_state_code, ShieldStateCode::VerificationViolation);
1667    }
1668
1669    #[test]
1670    fn sync_timeline_event_serialisation() {
1671        let kind = TimelineEventKind::Decrypted(DecryptedRoomEvent {
1672            event: Raw::new(&example_event()).unwrap().cast_unchecked(),
1673            encryption_info: Arc::new(EncryptionInfo {
1674                sender: owned_user_id!("@sender:example.com"),
1675                sender_device: None,
1676                forwarder: None,
1677                algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
1678                    curve25519_key: "xxx".to_owned(),
1679                    sender_claimed_keys: Default::default(),
1680                    session_id: Some("xyz".to_owned()),
1681                },
1682                verification_state: VerificationState::Verified,
1683            }),
1684            unsigned_encryption_info: Some(BTreeMap::from([(
1685                UnsignedEventLocation::RelationsReplace,
1686                UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
1687                    session_id: Some("xyz".to_owned()),
1688                    reason: UnableToDecryptReason::MalformedEncryptedEvent,
1689                }),
1690            )])),
1691        });
1692        let room_event = TimelineEvent {
1693            event_id: kind.parse_event_id(),
1694            kind,
1695            timestamp: Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))),
1696            push_actions: Default::default(),
1697        };
1698
1699        let serialized = serde_json::to_value(&room_event).unwrap();
1700
1701        // Test that the serialization is as expected
1702        assert_eq!(
1703            serialized,
1704            json!({
1705                "kind": {
1706                    "Decrypted": {
1707                        "event": {
1708                            "content": {"body": "secret", "msgtype": "m.text"},
1709                            "event_id": "$xxxxx:example.org",
1710                            "origin_server_ts": 2189,
1711                            "room_id": "!someroom:example.com",
1712                            "sender": "@carl:example.com",
1713                            "type": "m.room.message",
1714                        },
1715                        "encryption_info": {
1716                            "sender": "@sender:example.com",
1717                            "sender_device": null,
1718                            "forwarder": null,
1719                            "algorithm_info": {
1720                                "MegolmV1AesSha2": {
1721                                    "curve25519_key": "xxx",
1722                                    "sender_claimed_keys": {},
1723                                    "session_id": "xyz",
1724                                }
1725                            },
1726                            "verification_state": "Verified",
1727                        },
1728                        "unsigned_encryption_info": {
1729                            "RelationsReplace": {"UnableToDecrypt": {
1730                                "session_id": "xyz",
1731                                "reason": "MalformedEncryptedEvent",
1732                            }}
1733                        }
1734                    }
1735                },
1736                "timestamp": 2189,
1737            })
1738        );
1739
1740        // And it can be properly deserialized from the new format.
1741        let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1742        assert_eq!(event.event_id.as_deref(), Some(event_id!("$xxxxx:example.org")));
1743        assert_eq!(event.event_id.as_deref(), event.event_id());
1744        assert_matches!(
1745            event.encryption_info().unwrap().algorithm_info,
1746            AlgorithmInfo::MegolmV1AesSha2 { .. }
1747        );
1748        assert_eq!(event.timestamp(), Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))));
1749        assert_eq!(event.timestamp(), event.timestamp_raw());
1750
1751        // Test that the previous format can also be deserialized.
1752        let serialized = json!({
1753            "event": {
1754                "content": {"body": "secret", "msgtype": "m.text"},
1755                "event_id": "$xxxxx:example.org",
1756                "origin_server_ts": 2189,
1757                "room_id": "!someroom:example.com",
1758                "sender": "@carl:example.com",
1759                "type": "m.room.message",
1760            },
1761            "encryption_info": {
1762                "sender": "@sender:example.com",
1763                "sender_device": null,
1764                "algorithm_info": {
1765                    "MegolmV1AesSha2": {
1766                        "curve25519_key": "xxx",
1767                        "sender_claimed_keys": {}
1768                    }
1769                },
1770                "verification_state": "Verified",
1771            },
1772        });
1773        let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1774        assert_eq!(event.event_id(), Some(event_id!("$xxxxx:example.org")));
1775        assert_matches!(
1776            event.encryption_info().unwrap().algorithm_info,
1777            AlgorithmInfo::MegolmV1AesSha2 { session_id: None, .. }
1778        );
1779        assert_eq!(event.timestamp(), Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))));
1780        assert!(event.timestamp_raw().is_none());
1781
1782        // Test that the previous format, with an undecryptable unsigned event,
1783        // can also be deserialized.
1784        let serialized = json!({
1785            "event": {
1786                "content": {"body": "secret", "msgtype": "m.text"},
1787                "event_id": "$xxxxx:example.org",
1788                "origin_server_ts": 2189,
1789                "room_id": "!someroom:example.com",
1790                "sender": "@carl:example.com",
1791                "type": "m.room.message",
1792            },
1793            "encryption_info": {
1794                "sender": "@sender:example.com",
1795                "sender_device": null,
1796                "algorithm_info": {
1797                    "MegolmV1AesSha2": {
1798                        "curve25519_key": "xxx",
1799                        "sender_claimed_keys": {}
1800                    }
1801                },
1802                "verification_state": "Verified",
1803            },
1804            "unsigned_encryption_info": {
1805                "RelationsReplace": {"UnableToDecrypt": {"session_id": "xyz"}}
1806            }
1807        });
1808        let event: TimelineEvent = serde_json::from_value(serialized).unwrap();
1809        assert_eq!(event.event_id.as_deref(), event.event_id());
1810        assert_eq!(event.event_id.as_deref(), Some(event_id!("$xxxxx:example.org")));
1811        assert_matches!(
1812            event.encryption_info().unwrap().algorithm_info,
1813            AlgorithmInfo::MegolmV1AesSha2 { .. }
1814        );
1815        assert_eq!(event.timestamp(), Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))));
1816        assert!(event.timestamp_raw().is_none());
1817        assert_matches!(event.kind, TimelineEventKind::Decrypted(decrypted) => {
1818            assert_matches!(decrypted.unsigned_encryption_info, Some(map) => {
1819                assert_eq!(map.len(), 1);
1820                let (location, result) = map.into_iter().next().unwrap();
1821                assert_eq!(location, UnsignedEventLocation::RelationsReplace);
1822                assert_matches!(result, UnsignedDecryptionResult::UnableToDecrypt(utd_info) => {
1823                    assert_eq!(utd_info.session_id, Some("xyz".to_owned()));
1824                    assert_eq!(utd_info.reason, UnableToDecryptReason::Unknown);
1825                })
1826            });
1827        });
1828    }
1829
1830    #[test]
1831    fn sync_timeline_event_deserialisation_migration_for_withheld() {
1832        // Old serialized version was "utd_info": { "reason":
1833        // "MissingMegolmSession", "session_id": "session000" }
1834
1835        // The new version would be "utd_info": { "reason": {
1836        // "MissingMegolmSession": { "withheld_code": null } }, "session_id":
1837        // "session000" }
1838
1839        let serialized = json!({
1840             "kind": {
1841                "UnableToDecrypt": {
1842                  "event": {
1843                    "content": {
1844                      "algorithm": "m.megolm.v1.aes-sha2",
1845                      "ciphertext": "AwgAEoABzL1JYhqhjW9jXrlT3M6H8mJ4qffYtOQOnPuAPNxsuG20oiD/Fnpv6jnQGhU6YbV9pNM+1mRnTvxW3CbWOPjLKqCWTJTc7Q0vDEVtYePg38ncXNcwMmfhgnNAoW9S7vNs8C003x3yUl6NeZ8bH+ci870BZL+kWM/lMl10tn6U7snNmSjnE3ckvRdO+11/R4//5VzFQpZdf4j036lNSls/WIiI67Fk9iFpinz9xdRVWJFVdrAiPFwb8L5xRZ8aX+e2JDMlc1eW8gk",
1846                      "device_id": "SKCGPNUWAU",
1847                      "sender_key": "Gim/c7uQdSXyrrUbmUOrBT6sMC0gO7QSLmOK6B7NOm0",
1848                      "session_id": "hgLyeSqXfb8vc5AjQLsg6TSHVu0HJ7HZ4B6jgMvxkrs"
1849                    },
1850                    "event_id": "$xxxxx:example.org",
1851                    "origin_server_ts": 2189,
1852                    "room_id": "!someroom:example.com",
1853                    "sender": "@carl:example.com",
1854                    "type": "m.room.message"
1855                  },
1856                  "utd_info": {
1857                    "reason": "MissingMegolmSession",
1858                    "session_id": "session000"
1859                  }
1860                }
1861              }
1862        });
1863
1864        let result = serde_json::from_value(serialized);
1865        assert!(result.is_ok());
1866
1867        // should have migrated to the new format
1868        let event: TimelineEvent = result.unwrap();
1869        assert_matches!(
1870            event.kind,
1871            TimelineEventKind::UnableToDecrypt { utd_info, .. }=> {
1872                assert_matches!(
1873                    utd_info.reason,
1874                    UnableToDecryptReason::MissingMegolmSession { withheld_code: None }
1875                );
1876            }
1877        )
1878    }
1879
1880    #[test]
1881    fn unable_to_decrypt_info_migration_for_withheld() {
1882        let old_format = json!({
1883            "reason": "MissingMegolmSession",
1884            "session_id": "session000"
1885        });
1886
1887        let deserialized = serde_json::from_value::<UnableToDecryptInfo>(old_format).unwrap();
1888        let session_id = Some("session000".to_owned());
1889
1890        assert_eq!(deserialized.session_id, session_id);
1891        assert_eq!(
1892            deserialized.reason,
1893            UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
1894        );
1895
1896        let new_format = json!({
1897             "session_id": "session000",
1898              "reason": {
1899                "MissingMegolmSession": {
1900                  "withheld_code": null
1901                }
1902              }
1903        });
1904
1905        let deserialized = serde_json::from_value::<UnableToDecryptInfo>(new_format).unwrap();
1906
1907        assert_eq!(
1908            deserialized.reason,
1909            UnableToDecryptReason::MissingMegolmSession { withheld_code: None },
1910        );
1911        assert_eq!(deserialized.session_id, session_id);
1912    }
1913
1914    #[test]
1915    fn unable_to_decrypt_reason_is_missing_room_key() {
1916        let reason = UnableToDecryptReason::MissingMegolmSession { withheld_code: None };
1917        assert!(reason.is_missing_room_key());
1918
1919        let reason = UnableToDecryptReason::MissingMegolmSession {
1920            withheld_code: Some(WithheldCode::Blacklisted),
1921        };
1922        assert!(!reason.is_missing_room_key());
1923
1924        let reason = UnableToDecryptReason::UnknownMegolmMessageIndex;
1925        assert!(reason.is_missing_room_key());
1926    }
1927
1928    #[test]
1929    fn snapshot_test_verification_level() {
1930        with_settings!({ prepend_module_to_snapshot => false }, {
1931            assert_json_snapshot!(VerificationLevel::VerificationViolation);
1932            assert_json_snapshot!(VerificationLevel::UnsignedDevice);
1933            assert_json_snapshot!(VerificationLevel::None(DeviceLinkProblem::InsecureSource));
1934            assert_json_snapshot!(VerificationLevel::None(DeviceLinkProblem::MissingDevice));
1935            assert_json_snapshot!(VerificationLevel::UnverifiedIdentity);
1936        });
1937    }
1938
1939    #[test]
1940    fn snapshot_test_verification_states() {
1941        with_settings!({ prepend_module_to_snapshot => false }, {
1942            assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::UnsignedDevice));
1943            assert_json_snapshot!(VerificationState::Unverified(
1944                VerificationLevel::VerificationViolation
1945            ));
1946            assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::None(
1947                DeviceLinkProblem::InsecureSource,
1948            )));
1949            assert_json_snapshot!(VerificationState::Unverified(VerificationLevel::None(
1950                DeviceLinkProblem::MissingDevice,
1951            )));
1952            assert_json_snapshot!(VerificationState::Verified);
1953        });
1954    }
1955
1956    #[test]
1957    fn snapshot_test_shield_states() {
1958        with_settings!({ prepend_module_to_snapshot => false }, {
1959            assert_json_snapshot!(ShieldState::None);
1960            assert_json_snapshot!(ShieldState::Red {
1961                code: ShieldStateCode::UnverifiedIdentity,
1962                message: "a message"
1963            });
1964            assert_json_snapshot!(ShieldState::Grey {
1965                code: ShieldStateCode::AuthenticityNotGuaranteed,
1966                message: "authenticity of this message cannot be guaranteed",
1967            });
1968        });
1969    }
1970
1971    #[test]
1972    fn snapshot_test_shield_codes() {
1973        with_settings!({ prepend_module_to_snapshot => false }, {
1974            assert_json_snapshot!(ShieldStateCode::AuthenticityNotGuaranteed);
1975            assert_json_snapshot!(ShieldStateCode::UnknownDevice);
1976            assert_json_snapshot!(ShieldStateCode::UnsignedDevice);
1977            assert_json_snapshot!(ShieldStateCode::UnverifiedIdentity);
1978            assert_json_snapshot!(ShieldStateCode::VerificationViolation);
1979        });
1980    }
1981
1982    #[test]
1983    fn snapshot_test_algorithm_info() {
1984        let mut map = BTreeMap::new();
1985        map.insert(DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned());
1986        map.insert(DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned());
1987        let info = AlgorithmInfo::MegolmV1AesSha2 {
1988            curve25519_key: "curvecurvecurve".into(),
1989            sender_claimed_keys: BTreeMap::from([
1990                (DeviceKeyAlgorithm::Curve25519, "claimedclaimedcurve25519".to_owned()),
1991                (DeviceKeyAlgorithm::Ed25519, "claimedclaimeded25519".to_owned()),
1992            ]),
1993            session_id: None,
1994        };
1995
1996        with_settings!({ prepend_module_to_snapshot => false }, {
1997            assert_json_snapshot!(info);
1998        });
1999    }
2000
2001    #[test]
2002    fn test_encryption_info_migration() {
2003        // In the old format the session_id was in the EncryptionInfo, now it is
2004        // moved to the `algorithm_info` struct.
2005        let old_format = json!({
2006          "sender": "@alice:localhost",
2007          "sender_device": "ABCDEFGH",
2008          "algorithm_info": {
2009            "MegolmV1AesSha2": {
2010              "curve25519_key": "curvecurvecurve",
2011              "sender_claimed_keys": {}
2012            }
2013          },
2014          "verification_state": "Verified",
2015          "session_id": "mysessionid76"
2016        });
2017
2018        let deserialized = serde_json::from_value::<EncryptionInfo>(old_format).unwrap();
2019        let expected_session_id = Some("mysessionid76".to_owned());
2020
2021        assert_let!(
2022            AlgorithmInfo::MegolmV1AesSha2 { session_id, .. } = deserialized.algorithm_info.clone()
2023        );
2024        assert_eq!(session_id, expected_session_id);
2025
2026        assert_json_snapshot!(deserialized);
2027    }
2028
2029    #[test]
2030    fn snapshot_test_encryption_info() {
2031        let info = EncryptionInfo {
2032            sender: owned_user_id!("@alice:localhost"),
2033            sender_device: Some(owned_device_id!("ABCDEFGH")),
2034            forwarder: None,
2035            algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
2036                curve25519_key: "curvecurvecurve".into(),
2037                sender_claimed_keys: Default::default(),
2038                session_id: Some("mysessionid76".to_owned()),
2039            },
2040            verification_state: VerificationState::Verified,
2041        };
2042
2043        with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
2044            assert_json_snapshot!(info);
2045        })
2046    }
2047
2048    #[test]
2049    fn snapshot_test_sync_timeline_event() {
2050        let kind = TimelineEventKind::Decrypted(DecryptedRoomEvent {
2051            event: Raw::new(&example_event()).unwrap().cast_unchecked(),
2052            encryption_info: Arc::new(EncryptionInfo {
2053                sender: owned_user_id!("@sender:example.com"),
2054                sender_device: Some(owned_device_id!("ABCDEFGHIJ")),
2055                forwarder: None,
2056                algorithm_info: AlgorithmInfo::MegolmV1AesSha2 {
2057                    curve25519_key: "xxx".to_owned(),
2058                    sender_claimed_keys: BTreeMap::from([
2059                        (
2060                            DeviceKeyAlgorithm::Ed25519,
2061                            "I3YsPwqMZQXHkSQbjFNEs7b529uac2xBpI83eN3LUXo".to_owned(),
2062                        ),
2063                        (
2064                            DeviceKeyAlgorithm::Curve25519,
2065                            "qzdW3F5IMPFl0HQgz5w/L5Oi/npKUFn8Um84acIHfPY".to_owned(),
2066                        ),
2067                    ]),
2068                    session_id: Some("mysessionid112".to_owned()),
2069                },
2070                verification_state: VerificationState::Verified,
2071            }),
2072            unsigned_encryption_info: Some(BTreeMap::from([(
2073                UnsignedEventLocation::RelationsThreadLatestEvent,
2074                UnsignedDecryptionResult::UnableToDecrypt(UnableToDecryptInfo {
2075                    session_id: Some("xyz".to_owned()),
2076                    reason: UnableToDecryptReason::MissingMegolmSession {
2077                        withheld_code: Some(WithheldCode::Unverified),
2078                    },
2079                }),
2080            )])),
2081        });
2082        let room_event = TimelineEvent {
2083            event_id: kind.parse_event_id(),
2084            kind,
2085            timestamp: Some(MilliSecondsSinceUnixEpoch(UInt::new_saturating(2189))),
2086            push_actions: Default::default(),
2087        };
2088
2089        with_settings!({ sort_maps => true, prepend_module_to_snapshot => false }, {
2090            // We use directly the serde_json formatter here, because of a bug
2091            // in insta not serializing custom BTreeMap key enum
2092            // https://github.com/mitsuhiko/insta/issues/689
2093            assert_json_snapshot! {
2094                serde_json::to_value(&room_event).unwrap(),
2095            }
2096        });
2097    }
2098
2099    #[test]
2100    fn test_from_bundled_latest_event_keeps_session_id() {
2101        let session_id = "hgLyeSqXfb8vc5AjQLsg6TSHVu0HJ7HZ4B6jgMvxkrs";
2102        let serialized = json!({
2103            "content": {
2104              "algorithm": "m.megolm.v1.aes-sha2",
2105              "ciphertext": "AwgAEoABzL1JYhqhjW9jXrlT3M6H8mJ4qffYtOQOnPuAPNxsuG20oiD/Fnpv6jnQGhU6YbV9pNM+1mRnTvxW3CbWOPjLKqCWTJTc7Q0vDEVtYePg38ncXNcwMmfhgnNAoW9S7vNs8C003x3yUl6NeZ8bH+ci870BZL+kWM/lMl10tn6U7snNmSjnE3ckvRdO+11/R4//5VzFQpZdf4j036lNSls/WIiI67Fk9iFpinz9xdRVWJFVdrAiPFwb8L5xRZ8aX+e2JDMlc1eW8gk",
2106              "device_id": "SKCGPNUWAU",
2107              "sender_key": "Gim/c7uQdSXyrrUbmUOrBT6sMC0gO7QSLmOK6B7NOm0",
2108              "session_id": session_id,
2109            },
2110            "event_id": "$xxxxx:example.org",
2111            "origin_server_ts": 2189,
2112            "room_id": "!someroom:example.com",
2113            "sender": "@carl:example.com",
2114            "type": "m.room.encrypted"
2115        });
2116        let json = serialized.to_string();
2117        let value = Raw::<AnySyncTimelineEvent>::from_json_string(json).unwrap();
2118
2119        let kind = TimelineEventKind::UnableToDecrypt {
2120            event: value.clone(),
2121            utd_info: UnableToDecryptInfo {
2122                session_id: None,
2123                reason: UnableToDecryptReason::Unknown,
2124            },
2125        };
2126        let result = TimelineEvent::from_bundled_latest_event(
2127            &kind,
2128            value.cast_unchecked(),
2129            MilliSecondsSinceUnixEpoch::now(),
2130        )
2131        .expect("Could not get bundled latest event");
2132
2133        assert_let!(TimelineEventKind::UnableToDecrypt { utd_info, .. } = result.kind);
2134        assert!(utd_info.session_id.is_some());
2135        assert_eq!(utd_info.session_id.unwrap(), session_id);
2136    }
2137
2138    #[test]
2139    fn test_timeline_event_replace_raw_update_the_event_id() {
2140        let mut timeline_event = TimelineEvent::from_plaintext(
2141            Raw::new(&json!({
2142                "event_id": "$ev0",
2143                "type": "m.room.message",
2144                "sender": "@alice",
2145                "origin_server_ts": 42,
2146                "content": {
2147                    "body": "Hello, World!",
2148                },
2149                "unsigned": {},
2150            }))
2151            .unwrap()
2152            .cast_unchecked(),
2153        );
2154
2155        assert_eq!(timeline_event.event_id(), Some(event_id!("$ev0")));
2156
2157        timeline_event.replace_raw(
2158            Raw::new(&json!({
2159                "event_id": "$ev1",
2160                "type": "m.room.message",
2161                "sender": "@bob",
2162                "origin_server_ts": 153,
2163                "content": {
2164                    "body": "Bonjour !",
2165                },
2166                "unsigned": {},
2167            }))
2168            .unwrap()
2169            .cast_unchecked(),
2170        );
2171
2172        assert_eq!(timeline_event.event_id(), Some(event_id!("$ev1")));
2173    }
2174}