events: Add support for encrypted stickers

This commit is contained in:
Marco Antonio Alvarez 2024-05-23 15:49:15 +02:00 committed by GitHub
parent ee5e6b8c9e
commit 6cd3af9b86
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 136 additions and 8 deletions

View File

@ -1,5 +1,13 @@
# [unreleased]
Improvements:
- Add support for encrypted stickers as sent by several bridges under the flag `compat-encrypted-stickers`
Breaking changes:
- `StickerEventContent::url` was replaced by `StickerEventContent::source` which is a `StickerMediaSource`
# 0.28.1
Improvements:

View File

@ -56,6 +56,10 @@ compat-optional = []
# Allow TagInfo to contain a stringified floating-point value for the `order` field.
compat-tag-info = []
# Support encrypted stickers, as sent by several bridges.
# https://github.com/matrix-org/matrix-spec/issues/1667
compat-encrypted-stickers = []
[dependencies]
as_variant = { workspace = true }
indexmap = { version = "2.0.0", features = ["serde"] }

View File

@ -4,9 +4,77 @@
use ruma_common::OwnedMxcUri;
use ruma_macros::EventContent;
use serde::{Deserialize, Serialize};
use serde::{de, Deserialize, Serialize};
use crate::room::ImageInfo;
#[cfg(feature = "compat-encrypted-stickers")]
use crate::room::EncryptedFile;
use crate::room::{ImageInfo, MediaSource};
/// The source of a sticker media file.
#[derive(Clone, Debug, Serialize)]
#[non_exhaustive]
pub enum StickerMediaSource {
/// The MXC URI to the unencrypted media file.
#[serde(rename = "url")]
Plain(OwnedMxcUri),
/// The encryption info of the encrypted media file.
#[cfg(feature = "compat-encrypted-stickers")]
#[serde(rename = "file")]
Encrypted(Box<EncryptedFile>),
}
// Custom implementation of `Deserialize`, because serde doesn't guarantee what variant will be
// deserialized for "externally tagged"¹ enums where multiple "tag" fields exist.
//
// ¹ https://serde.rs/enum-representations.html
impl<'de> Deserialize<'de> for StickerMediaSource {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Deserialize)]
struct StickerMediaSourceJsonRepr {
url: Option<OwnedMxcUri>,
#[cfg(feature = "compat-encrypted-stickers")]
file: Option<Box<EncryptedFile>>,
}
match StickerMediaSourceJsonRepr::deserialize(deserializer)? {
StickerMediaSourceJsonRepr {
url: None,
#[cfg(feature = "compat-encrypted-stickers")]
file: None,
} => Err(de::Error::missing_field("url")),
// Prefer file if it is set
#[cfg(feature = "compat-encrypted-stickers")]
StickerMediaSourceJsonRepr { file: Some(file), .. } => {
Ok(StickerMediaSource::Encrypted(file))
}
StickerMediaSourceJsonRepr { url: Some(url), .. } => Ok(StickerMediaSource::Plain(url)),
}
}
}
impl From<StickerMediaSource> for MediaSource {
fn from(value: StickerMediaSource) -> Self {
match value {
StickerMediaSource::Plain(url) => MediaSource::Plain(url),
#[cfg(feature = "compat-encrypted-stickers")]
StickerMediaSource::Encrypted(file) => MediaSource::Encrypted(file),
}
}
}
#[cfg(feature = "compat-encrypted-stickers")]
impl From<MediaSource> for StickerMediaSource {
fn from(value: MediaSource) -> Self {
match value {
MediaSource::Plain(url) => StickerMediaSource::Plain(url),
MediaSource::Encrypted(file) => StickerMediaSource::Encrypted(file),
}
}
}
/// The content of an `m.sticker` event.
///
@ -24,13 +92,19 @@ pub struct StickerEventContent {
/// Metadata about the image referred to in `url` including a thumbnail representation.
pub info: ImageInfo,
/// The URL to the sticker image.
pub url: OwnedMxcUri,
/// The media source of the sticker image.
#[serde(flatten)]
pub source: StickerMediaSource,
}
impl StickerEventContent {
/// Creates a new `StickerEventContent` with the given body, image info and URL.
pub fn new(body: String, info: ImageInfo, url: OwnedMxcUri) -> Self {
Self { body, info, url }
Self { body, info, source: StickerMediaSource::Plain(url.clone()) }
}
/// Creates a new `StickerEventContent` with the given body, image info, URL, and media source.
pub fn with_source(body: String, info: ImageInfo, source: StickerMediaSource) -> Self {
Self { body, info, source }
}
}

View File

@ -4,7 +4,7 @@ use js_int::{uint, UInt};
use ruma_common::{mxc_uri, serde::CanBeEmpty, MilliSecondsSinceUnixEpoch};
use ruma_events::{
room::{ImageInfo, MediaSource, ThumbnailInfo},
sticker::StickerEventContent,
sticker::{StickerEventContent, StickerMediaSource},
AnyMessageLikeEvent, MessageLikeEvent,
};
use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
@ -79,7 +79,44 @@ fn content_deserialization() {
let content = from_json_value::<StickerEventContent>(json_data).unwrap();
assert_eq!(content.body, "Upload: my_image.jpg");
assert_eq!(content.url, "mxc://notareal.hs/file");
assert_matches!(content.source, StickerMediaSource::Plain(sticker_url));
assert_eq!(sticker_url, "mxc://notareal.hs/file");
let encrypted_json_data = json!({
"body": "Upload: my_image.jpg",
"file": {
"url": "mxc://notareal.hs/file",
"key": {
"kty": "oct",
"key_ops": ["encrypt", "decrypt"],
"alg": "A256CTR",
"k": "TLlG_OpX807zzQuuwv4QZGJ21_u7weemFGYJFszMn9A",
"ext": true
},
"iv": "S22dq3NAX8wAAAAAAAAAAA",
"hashes": {
"sha256": "aWOHudBnDkJ9IwaR1Nd8XKoI7DOrqDTwt6xDPfVGN6Q"
},
"v": "v2",
},
"info": {},
});
#[cfg(not(feature = "compat-encrypted-stickers"))]
{
from_json_value::<StickerEventContent>(encrypted_json_data).unwrap_err();
}
#[cfg(feature = "compat-encrypted-stickers")]
{
let encrypted_content =
from_json_value::<StickerEventContent>(encrypted_json_data).unwrap();
assert_eq!(encrypted_content.body, "Upload: my_image.jpg");
assert_matches!(
encrypted_content.source,
StickerMediaSource::Encrypted(encrypted_sticker_url)
);
assert_eq!(encrypted_sticker_url.url, "mxc://notareal.hs/file");
}
}
#[test]
@ -126,7 +163,8 @@ fn event_deserialization() {
assert_eq!(content.info.width, Some(uint!(1011)));
assert_eq!(content.info.mimetype.as_deref(), Some("image/png"));
assert_eq!(content.info.size, Some(uint!(84242)));
assert_eq!(content.url, "mxc://matrix.org/jxPXTKpyydzdHJkdFNZjTZrD");
assert_matches!(content.source, StickerMediaSource::Plain(sticker_url));
assert_eq!(sticker_url, "mxc://matrix.org/jxPXTKpyydzdHJkdFNZjTZrD");
assert_matches!(content.info.thumbnail_source, Some(MediaSource::Plain(thumbnail_url)));
assert_eq!(thumbnail_url, "mxc://matrix.org/irnsNRS2879");

View File

@ -153,6 +153,10 @@ compat-signature-id = ["ruma-signatures?/compat-signature-id"]
# Allow TagInfo to contain a stringified floating-point value for the `order` field.
compat-tag-info = ["ruma-events?/compat-tag-info"]
# Support encrypted stickers, as sent by several bridges.
# https://github.com/matrix-org/matrix-spec/issues/1667
compat-encrypted-stickers = ["ruma-events?/compat-encrypted-stickers"]
# Specific compatibility for past ring public/private key documents.
ring-compat = ["dep:ruma-signatures", "ruma-signatures?/ring-compat"]