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