matrix_sdk_common/
deserialized_responses.rs

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