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