events: Add support for encrypted stickers
This commit is contained in:
parent
ee5e6b8c9e
commit
6cd3af9b86
@ -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:
|
||||
|
@ -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"] }
|
||||
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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"]
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user