events: Add support for transitional extensible file messages
According to MSC3551
This commit is contained in:
parent
0e11996545
commit
8673d0d3f6
@ -10,11 +10,26 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
message::MessageContent,
|
message::MessageContent,
|
||||||
room::{message::Relation, JsonWebKey},
|
room::{
|
||||||
|
message::{FileInfo, FileMessageEventContent, Relation},
|
||||||
|
EncryptedFile, JsonWebKey, MediaSource,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use crate::{serde::Base64, MxcUri};
|
use crate::{serde::Base64, MxcUri};
|
||||||
|
|
||||||
/// The payload for an extensible text message.
|
/// The payload for an extensible file message.
|
||||||
|
///
|
||||||
|
/// This is the new primary type introduced in [MSC3551] and should not be sent before the end of
|
||||||
|
/// the transition period. See the documentation of the [`message`] module for more information.
|
||||||
|
///
|
||||||
|
/// `FileEventContent` can be converted to a [`RoomMessageEventContent`] with a
|
||||||
|
/// [`MessageType::File`]. You can convert it back with
|
||||||
|
/// [`FileEventContent::from_file_room_message()`].
|
||||||
|
///
|
||||||
|
/// [MSC3551]: https://github.com/matrix-org/matrix-spec-proposals/pull/3551
|
||||||
|
/// [`message`]: super::message
|
||||||
|
/// [`RoomMessageEventContent`]: super::room::message::RoomMessageEventContent
|
||||||
|
/// [`MessageType::File`]: super::room::message::MessageType::File
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, EventContent)]
|
#[derive(Clone, Debug, Serialize, Deserialize, EventContent)]
|
||||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
#[ruma_event(type = "m.file", kind = MessageLike)]
|
#[ruma_event(type = "m.file", kind = MessageLike)]
|
||||||
@ -84,6 +99,22 @@ impl FileEventContent {
|
|||||||
) -> Self {
|
) -> Self {
|
||||||
Self { message, file: FileContent::encrypted(url, encryption_info, info), relates_to: None }
|
Self { message, file: FileContent::encrypted(url, encryption_info, info), relates_to: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new `FileEventContent` from the given `FileMessageEventContent` and optional
|
||||||
|
/// relation.
|
||||||
|
pub fn from_file_room_message(
|
||||||
|
content: FileMessageEventContent,
|
||||||
|
relates_to: Option<Relation>,
|
||||||
|
) -> Self {
|
||||||
|
let FileMessageEventContent { body, filename, source, info, message, file } = content;
|
||||||
|
|
||||||
|
let message = message.unwrap_or_else(|| MessageContent::plain(body));
|
||||||
|
let file = file.unwrap_or_else(|| {
|
||||||
|
FileContent::from_room_message_content(source, info.as_deref(), filename)
|
||||||
|
});
|
||||||
|
|
||||||
|
Self { message, file, relates_to }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// File content.
|
/// File content.
|
||||||
@ -119,6 +150,21 @@ impl FileContent {
|
|||||||
Self { url, info, encryption_info: Some(Box::new(encryption_info)) }
|
Self { url, info, encryption_info: Some(Box::new(encryption_info)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new `FileContent` with the given media source, file info and filename.
|
||||||
|
pub fn from_room_message_content(
|
||||||
|
source: MediaSource,
|
||||||
|
info: Option<impl Into<FileContentInfo>>,
|
||||||
|
filename: Option<String>,
|
||||||
|
) -> Self {
|
||||||
|
let (url, encryption_info) = match source {
|
||||||
|
MediaSource::Plain(url) => (url, None),
|
||||||
|
MediaSource::Encrypted(file) => (file.url.to_owned(), Some(Box::new((&*file).into()))),
|
||||||
|
};
|
||||||
|
let info = FileContentInfo::from_room_message_content(info, filename).map(Box::new);
|
||||||
|
|
||||||
|
Self { url, encryption_info, info }
|
||||||
|
}
|
||||||
|
|
||||||
/// Whether the file is encrypted.
|
/// Whether the file is encrypted.
|
||||||
pub fn is_encrypted(&self) -> bool {
|
pub fn is_encrypted(&self) -> bool {
|
||||||
self.encryption_info.is_some()
|
self.encryption_info.is_some()
|
||||||
@ -147,6 +193,29 @@ impl FileContentInfo {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new `FileContentInfo` with the given file info and filename.
|
||||||
|
///
|
||||||
|
/// Returns `None` if both parameters are `None`
|
||||||
|
pub fn from_room_message_content(
|
||||||
|
info: Option<impl Into<FileContentInfo>>,
|
||||||
|
filename: Option<String>,
|
||||||
|
) -> Option<Self> {
|
||||||
|
if filename.is_none() && info.is_none() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let mut info: Self = info.map(Into::into).unwrap_or_default();
|
||||||
|
info.name = filename;
|
||||||
|
Some(info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&FileInfo> for FileContentInfo {
|
||||||
|
fn from(info: &FileInfo) -> Self {
|
||||||
|
let FileInfo { mimetype, size, .. } = info;
|
||||||
|
Self { mimetype: mimetype.to_owned(), size: size.to_owned(), ..Default::default() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The encryption info of a file sent to a room with end-to-end encryption enabled.
|
/// The encryption info of a file sent to a room with end-to-end encryption enabled.
|
||||||
@ -203,3 +272,10 @@ impl From<EncryptedContentInit> for EncryptedContent {
|
|||||||
Self { key, iv, hashes, v }
|
Self { key, iv, hashes, v }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&EncryptedFile> for EncryptedContent {
|
||||||
|
fn from(encrypted: &EncryptedFile) -> Self {
|
||||||
|
let EncryptedFile { key, iv, hashes, v, .. } = encrypted.to_owned();
|
||||||
|
Self { key, iv, hashes, v }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -7,6 +7,8 @@ use std::collections::BTreeMap;
|
|||||||
use js_int::UInt;
|
use js_int::UInt;
|
||||||
use serde::{de, Deserialize, Serialize};
|
use serde::{de, Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc3551")]
|
||||||
|
use super::file::{EncryptedContent, FileContent};
|
||||||
use crate::{
|
use crate::{
|
||||||
serde::{base64::UrlSafe, Base64},
|
serde::{base64::UrlSafe, Base64},
|
||||||
MxcUri,
|
MxcUri,
|
||||||
@ -70,6 +72,18 @@ impl<'de> Deserialize<'de> for MediaSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc3551")]
|
||||||
|
impl From<&FileContent> for MediaSource {
|
||||||
|
fn from(content: &FileContent) -> Self {
|
||||||
|
let FileContent { url, encryption_info, .. } = content;
|
||||||
|
if let Some(encryption_info) = encryption_info.as_deref() {
|
||||||
|
Self::Encrypted(Box::new(EncryptedFile::from_extensible_content(url, encryption_info)))
|
||||||
|
} else {
|
||||||
|
Self::Plain(url.to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Metadata about an image.
|
/// Metadata about an image.
|
||||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
@ -173,6 +187,15 @@ pub struct EncryptedFile {
|
|||||||
pub v: String,
|
pub v: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc3551")]
|
||||||
|
impl EncryptedFile {
|
||||||
|
/// Create an `EncryptedFile` from the given url and encryption info.
|
||||||
|
pub fn from_extensible_content(url: &MxcUri, encryption_info: &EncryptedContent) -> Self {
|
||||||
|
let EncryptedContent { key, iv, hashes, v } = encryption_info.to_owned();
|
||||||
|
Self { url: url.to_owned(), key, iv, hashes, v }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Initial set of fields of `EncryptedFile`.
|
/// Initial set of fields of `EncryptedFile`.
|
||||||
///
|
///
|
||||||
/// This struct will not be updated even if additional fields are added to `EncryptedFile` in a new
|
/// This struct will not be updated even if additional fields are added to `EncryptedFile` in a new
|
||||||
|
@ -10,6 +10,8 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
|||||||
use serde_json::Value as JsonValue;
|
use serde_json::Value as JsonValue;
|
||||||
|
|
||||||
use super::{EncryptedFile, ImageInfo, MediaSource, ThumbnailInfo};
|
use super::{EncryptedFile, ImageInfo, MediaSource, ThumbnailInfo};
|
||||||
|
#[cfg(feature = "unstable-msc3551")]
|
||||||
|
use crate::events::file::{FileContent, FileContentInfo, FileEventContent};
|
||||||
#[cfg(feature = "unstable-msc1767")]
|
#[cfg(feature = "unstable-msc1767")]
|
||||||
use crate::events::{
|
use crate::events::{
|
||||||
emote::EmoteEventContent,
|
emote::EmoteEventContent,
|
||||||
@ -197,6 +199,20 @@ impl From<EmoteEventContent> for RoomMessageEventContent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc3551")]
|
||||||
|
impl From<FileEventContent> for RoomMessageEventContent {
|
||||||
|
fn from(content: FileEventContent) -> Self {
|
||||||
|
let FileEventContent { message, file, relates_to } = content;
|
||||||
|
|
||||||
|
Self {
|
||||||
|
msgtype: MessageType::File(FileMessageEventContent::from_extensible_content(
|
||||||
|
message, file,
|
||||||
|
)),
|
||||||
|
relates_to,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "unstable-msc1767")]
|
#[cfg(feature = "unstable-msc1767")]
|
||||||
impl From<MessageEventContent> for RoomMessageEventContent {
|
impl From<MessageEventContent> for RoomMessageEventContent {
|
||||||
fn from(content: MessageEventContent) -> Self {
|
fn from(content: MessageEventContent) -> Self {
|
||||||
@ -597,6 +613,10 @@ impl From<MessageContent> for EmoteMessageEventContent {
|
|||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
#[serde(tag = "msgtype", rename = "m.file")]
|
#[serde(tag = "msgtype", rename = "m.file")]
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "unstable-msc3551",
|
||||||
|
serde(from = "content_serde::FileMessageEventContentDeHelper")
|
||||||
|
)]
|
||||||
pub struct FileMessageEventContent {
|
pub struct FileMessageEventContent {
|
||||||
/// A human-readable description of the file.
|
/// A human-readable description of the file.
|
||||||
///
|
///
|
||||||
@ -614,19 +634,69 @@ pub struct FileMessageEventContent {
|
|||||||
/// Metadata about the file referred to in `url`.
|
/// Metadata about the file referred to in `url`.
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub info: Option<Box<FileInfo>>,
|
pub info: Option<Box<FileInfo>>,
|
||||||
|
|
||||||
|
/// Extensible-event text representation of the message.
|
||||||
|
///
|
||||||
|
/// If present, this should be preferred over the `body` field.
|
||||||
|
#[cfg(feature = "unstable-msc3551")]
|
||||||
|
#[serde(flatten, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub message: Option<MessageContent>,
|
||||||
|
|
||||||
|
/// Extensible-event file content of the message.
|
||||||
|
///
|
||||||
|
/// If present, this should be preferred over the `source` and `info` fields.
|
||||||
|
#[cfg(feature = "unstable-msc3551")]
|
||||||
|
#[serde(rename = "org.matrix.msc1767.file", skip_serializing_if = "Option::is_none")]
|
||||||
|
pub file: Option<FileContent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileMessageEventContent {
|
impl FileMessageEventContent {
|
||||||
/// Creates a new non-encrypted `RoomFileMessageEventContent` with the given body, url and
|
/// Creates a new non-encrypted `RoomFileMessageEventContent` with the given body, url and
|
||||||
/// optional extra info.
|
/// optional extra info.
|
||||||
pub fn plain(body: String, url: Box<MxcUri>, info: Option<Box<FileInfo>>) -> Self {
|
pub fn plain(body: String, url: Box<MxcUri>, info: Option<Box<FileInfo>>) -> Self {
|
||||||
Self { body, filename: None, source: MediaSource::Plain(url), info }
|
Self {
|
||||||
|
#[cfg(feature = "unstable-msc3551")]
|
||||||
|
message: Some(MessageContent::plain(body.clone())),
|
||||||
|
#[cfg(feature = "unstable-msc3551")]
|
||||||
|
file: Some(FileContent::plain(
|
||||||
|
url.clone(),
|
||||||
|
info.as_deref().map(|info| Box::new(info.into())),
|
||||||
|
)),
|
||||||
|
body,
|
||||||
|
filename: None,
|
||||||
|
source: MediaSource::Plain(url),
|
||||||
|
info,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new encrypted `RoomFileMessageEventContent` with the given body and encrypted
|
/// Creates a new encrypted `RoomFileMessageEventContent` with the given body and encrypted
|
||||||
/// file.
|
/// file.
|
||||||
pub fn encrypted(body: String, file: EncryptedFile) -> Self {
|
pub fn encrypted(body: String, file: EncryptedFile) -> Self {
|
||||||
Self { body, filename: None, source: MediaSource::Encrypted(Box::new(file)), info: None }
|
Self {
|
||||||
|
#[cfg(feature = "unstable-msc3551")]
|
||||||
|
message: Some(MessageContent::plain(body.clone())),
|
||||||
|
#[cfg(feature = "unstable-msc3551")]
|
||||||
|
file: Some(FileContent::encrypted(file.url.clone(), (&file).into(), None)),
|
||||||
|
body,
|
||||||
|
filename: None,
|
||||||
|
source: MediaSource::Encrypted(Box::new(file)),
|
||||||
|
info: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new `RoomFileMessageEventContent` with the given message and file info.
|
||||||
|
#[cfg(feature = "unstable-msc3551")]
|
||||||
|
pub fn from_extensible_content(message: MessageContent, file: FileContent) -> Self {
|
||||||
|
let body = if let Some(body) = message.find_plain() {
|
||||||
|
body.to_owned()
|
||||||
|
} else {
|
||||||
|
message[0].body.clone()
|
||||||
|
};
|
||||||
|
let filename = file.info.as_deref().and_then(|info| info.name.clone());
|
||||||
|
let info = file.info.as_deref().map(|info| Box::new(info.into()));
|
||||||
|
let source = (&file).into();
|
||||||
|
|
||||||
|
Self { message: Some(message), file: Some(file), body, filename, source, info }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -662,6 +732,14 @@ impl FileInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc3551")]
|
||||||
|
impl From<&FileContentInfo> for FileInfo {
|
||||||
|
fn from(info: &FileContentInfo) -> Self {
|
||||||
|
let FileContentInfo { mimetype, size, .. } = info;
|
||||||
|
Self { mimetype: mimetype.to_owned(), size: size.to_owned(), ..Default::default() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The payload for an image message.
|
/// The payload for an image message.
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
use serde::{de, Deserialize};
|
use serde::{de, Deserialize};
|
||||||
use serde_json::value::RawValue as RawJsonValue;
|
use serde_json::value::RawValue as RawJsonValue;
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc3551")]
|
||||||
|
use super::{FileContent, FileInfo, FileMessageEventContent, MediaSource, MessageContent};
|
||||||
use super::{MessageType, Relation, RoomMessageEventContent};
|
use super::{MessageType, Relation, RoomMessageEventContent};
|
||||||
use crate::serde::from_raw_json_value;
|
use crate::serde::from_raw_json_value;
|
||||||
|
|
||||||
@ -50,3 +52,55 @@ impl<'de> Deserialize<'de> for MessageType {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper struct for deserializing `FileMessageEventContent` with stable and unstable field names.
|
||||||
|
///
|
||||||
|
/// It's not possible to use the `alias` attribute of serde because of
|
||||||
|
/// https://github.com/serde-rs/serde/issues/1504.
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
#[cfg(feature = "unstable-msc3551")]
|
||||||
|
pub struct FileMessageEventContentDeHelper {
|
||||||
|
/// A human-readable description of the file.
|
||||||
|
pub body: String,
|
||||||
|
|
||||||
|
/// The original filename of the uploaded file.
|
||||||
|
pub filename: Option<String>,
|
||||||
|
|
||||||
|
/// The source of the file.
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub source: MediaSource,
|
||||||
|
|
||||||
|
/// Metadata about the file referred to in `url`.
|
||||||
|
pub info: Option<Box<FileInfo>>,
|
||||||
|
|
||||||
|
/// Extensible-event text representation of the message.
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub message: Option<MessageContent>,
|
||||||
|
|
||||||
|
/// Extensible-event file content of the message, with stable name.
|
||||||
|
#[serde(rename = "m.file")]
|
||||||
|
pub file_stable: Option<FileContent>,
|
||||||
|
|
||||||
|
/// Extensible-event file content of the message, with unstable name.
|
||||||
|
#[serde(rename = "org.matrix.msc1767.file")]
|
||||||
|
pub file_unstable: Option<FileContent>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "unstable-msc3551")]
|
||||||
|
impl From<FileMessageEventContentDeHelper> for FileMessageEventContent {
|
||||||
|
fn from(helper: FileMessageEventContentDeHelper) -> Self {
|
||||||
|
let FileMessageEventContentDeHelper {
|
||||||
|
body,
|
||||||
|
filename,
|
||||||
|
source,
|
||||||
|
info,
|
||||||
|
message,
|
||||||
|
file_stable,
|
||||||
|
file_unstable,
|
||||||
|
} = helper;
|
||||||
|
|
||||||
|
let file = file_stable.or(file_unstable);
|
||||||
|
|
||||||
|
Self { body, filename, source, info, message, file }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,8 +9,10 @@ use ruma_common::{
|
|||||||
file::{EncryptedContentInit, FileContent, FileContentInfo, FileEventContent},
|
file::{EncryptedContentInit, FileContent, FileContentInfo, FileEventContent},
|
||||||
message::MessageContent,
|
message::MessageContent,
|
||||||
room::{
|
room::{
|
||||||
message::{InReplyTo, Relation},
|
message::{
|
||||||
JsonWebKeyInit,
|
FileMessageEventContent, InReplyTo, MessageType, Relation, RoomMessageEventContent,
|
||||||
|
},
|
||||||
|
EncryptedFileInit, JsonWebKeyInit, MediaSource,
|
||||||
},
|
},
|
||||||
AnyMessageLikeEvent, MessageLikeEvent, MessageLikeUnsigned,
|
AnyMessageLikeEvent, MessageLikeEvent, MessageLikeUnsigned,
|
||||||
},
|
},
|
||||||
@ -152,7 +154,7 @@ fn file_event_serialization() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn plain_content_deserialization() {
|
fn plain_content_deserialization() {
|
||||||
let json_data = json!({
|
let json_data = json!({
|
||||||
"org.matrix.msc1767.text": "Upload: my_file.txt",
|
"m.text": "Upload: my_file.txt",
|
||||||
"m.file": {
|
"m.file": {
|
||||||
"url": "mxc://notareal.hs/abcdef",
|
"url": "mxc://notareal.hs/abcdef",
|
||||||
}
|
}
|
||||||
@ -171,7 +173,7 @@ fn plain_content_deserialization() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn encrypted_content_deserialization() {
|
fn encrypted_content_deserialization() {
|
||||||
let json_data = json!({
|
let json_data = json!({
|
||||||
"org.matrix.msc1767.text": "Upload: my_file.txt",
|
"m.text": "Upload: my_file.txt",
|
||||||
"m.file": {
|
"m.file": {
|
||||||
"url": "mxc://notareal.hs/abcdef",
|
"url": "mxc://notareal.hs/abcdef",
|
||||||
"key": {
|
"key": {
|
||||||
@ -204,7 +206,7 @@ fn encrypted_content_deserialization() {
|
|||||||
fn message_event_deserialization() {
|
fn message_event_deserialization() {
|
||||||
let json_data = json!({
|
let json_data = json!({
|
||||||
"content": {
|
"content": {
|
||||||
"org.matrix.msc1767.message": [
|
"m.message": [
|
||||||
{ "body": "Upload: <strong>my_file.txt</strong>", "mimetype": "text/html"},
|
{ "body": "Upload: <strong>my_file.txt</strong>", "mimetype": "text/html"},
|
||||||
{ "body": "Upload: my_file.txt", "mimetype": "text/plain"},
|
{ "body": "Upload: my_file.txt", "mimetype": "text/plain"},
|
||||||
],
|
],
|
||||||
@ -252,3 +254,260 @@ fn message_event_deserialization() {
|
|||||||
&& unsigned.is_empty()
|
&& unsigned.is_empty()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn room_message_plain_content_serialization() {
|
||||||
|
let message_event_content =
|
||||||
|
RoomMessageEventContent::new(MessageType::File(FileMessageEventContent::plain(
|
||||||
|
"Upload: my_file.txt".to_owned(),
|
||||||
|
mxc_uri!("mxc://notareal.hs/file").to_owned(),
|
||||||
|
None,
|
||||||
|
)));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
to_json_value(&message_event_content).unwrap(),
|
||||||
|
json!({
|
||||||
|
"body": "Upload: my_file.txt",
|
||||||
|
"url": "mxc://notareal.hs/file",
|
||||||
|
"msgtype": "m.file",
|
||||||
|
"org.matrix.msc1767.text": "Upload: my_file.txt",
|
||||||
|
"org.matrix.msc1767.file": {
|
||||||
|
"url": "mxc://notareal.hs/file",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn room_message_encrypted_content_serialization() {
|
||||||
|
let message_event_content =
|
||||||
|
RoomMessageEventContent::new(MessageType::File(FileMessageEventContent::encrypted(
|
||||||
|
"Upload: my_file.txt".to_owned(),
|
||||||
|
EncryptedFileInit {
|
||||||
|
url: mxc_uri!("mxc://notareal.hs/file").to_owned(),
|
||||||
|
key: JsonWebKeyInit {
|
||||||
|
kty: "oct".to_owned(),
|
||||||
|
key_ops: vec!["encrypt".to_owned(), "decrypt".to_owned()],
|
||||||
|
alg: "A256CTR".to_owned(),
|
||||||
|
k: Base64::parse("TLlG_OpX807zzQuuwv4QZGJ21_u7weemFGYJFszMn9A").unwrap(),
|
||||||
|
ext: true,
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
iv: Base64::parse("S22dq3NAX8wAAAAAAAAAAA").unwrap(),
|
||||||
|
hashes: [(
|
||||||
|
"sha256".to_owned(),
|
||||||
|
Base64::parse("aWOHudBnDkJ9IwaR1Nd8XKoI7DOrqDTwt6xDPfVGN6Q").unwrap(),
|
||||||
|
)]
|
||||||
|
.into(),
|
||||||
|
v: "v2".to_owned(),
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
)));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
to_json_value(&message_event_content).unwrap(),
|
||||||
|
json!({
|
||||||
|
"body": "Upload: my_file.txt",
|
||||||
|
"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",
|
||||||
|
},
|
||||||
|
"msgtype": "m.file",
|
||||||
|
"org.matrix.msc1767.text": "Upload: my_file.txt",
|
||||||
|
"org.matrix.msc1767.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",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn room_message_plain_content_stable_deserialization() {
|
||||||
|
let json_data = json!({
|
||||||
|
"body": "Upload: my_file.txt",
|
||||||
|
"url": "mxc://notareal.hs/file",
|
||||||
|
"msgtype": "m.file",
|
||||||
|
"m.text": "Upload: my_file.txt",
|
||||||
|
"m.file": {
|
||||||
|
"url": "mxc://notareal.hs/file",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let event_content = from_json_value::<RoomMessageEventContent>(json_data).unwrap();
|
||||||
|
assert_matches!(event_content.msgtype, MessageType::File(_));
|
||||||
|
if let MessageType::File(content) = event_content.msgtype {
|
||||||
|
assert_eq!(content.body, "Upload: my_file.txt");
|
||||||
|
assert_matches!(content.source, MediaSource::Plain(_));
|
||||||
|
if let MediaSource::Plain(url) = content.source {
|
||||||
|
assert_eq!(url, "mxc://notareal.hs/file");
|
||||||
|
}
|
||||||
|
let message = content.message.unwrap();
|
||||||
|
assert_eq!(message.len(), 1);
|
||||||
|
assert_eq!(message[0].body, "Upload: my_file.txt");
|
||||||
|
let file = content.file.unwrap();
|
||||||
|
assert_eq!(file.url, "mxc://notareal.hs/file");
|
||||||
|
assert!(!file.is_encrypted());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn room_message_plain_content_unstable_deserialization() {
|
||||||
|
let json_data = json!({
|
||||||
|
"body": "Upload: my_file.txt",
|
||||||
|
"url": "mxc://notareal.hs/file",
|
||||||
|
"msgtype": "m.file",
|
||||||
|
"org.matrix.msc1767.text": "Upload: my_file.txt",
|
||||||
|
"org.matrix.msc1767.file": {
|
||||||
|
"url": "mxc://notareal.hs/file",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let event_content = from_json_value::<RoomMessageEventContent>(json_data).unwrap();
|
||||||
|
assert_matches!(event_content.msgtype, MessageType::File(_));
|
||||||
|
if let MessageType::File(content) = event_content.msgtype {
|
||||||
|
assert_eq!(content.body, "Upload: my_file.txt");
|
||||||
|
assert_matches!(content.source, MediaSource::Plain(_));
|
||||||
|
if let MediaSource::Plain(url) = content.source {
|
||||||
|
assert_eq!(url, "mxc://notareal.hs/file");
|
||||||
|
}
|
||||||
|
let message = content.message.unwrap();
|
||||||
|
assert_eq!(message.len(), 1);
|
||||||
|
assert_eq!(message[0].body, "Upload: my_file.txt");
|
||||||
|
let file = content.file.unwrap();
|
||||||
|
assert_eq!(file.url, "mxc://notareal.hs/file");
|
||||||
|
assert!(!file.is_encrypted());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn room_message_encrypted_content_stable_deserialization() {
|
||||||
|
let json_data = json!({
|
||||||
|
"body": "Upload: my_file.txt",
|
||||||
|
"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",
|
||||||
|
},
|
||||||
|
"msgtype": "m.file",
|
||||||
|
"m.text": "Upload: my_file.txt",
|
||||||
|
"m.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",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let event_content = from_json_value::<RoomMessageEventContent>(json_data).unwrap();
|
||||||
|
assert_matches!(event_content.msgtype, MessageType::File(_));
|
||||||
|
if let MessageType::File(content) = event_content.msgtype {
|
||||||
|
assert_eq!(content.body, "Upload: my_file.txt");
|
||||||
|
assert_matches!(content.source, MediaSource::Encrypted(_));
|
||||||
|
if let MediaSource::Encrypted(encrypted_file) = content.source {
|
||||||
|
assert_eq!(encrypted_file.url, "mxc://notareal.hs/file");
|
||||||
|
}
|
||||||
|
let message = content.message.unwrap();
|
||||||
|
assert_eq!(message.len(), 1);
|
||||||
|
assert_eq!(message[0].body, "Upload: my_file.txt");
|
||||||
|
let file = content.file.unwrap();
|
||||||
|
assert_eq!(file.url, "mxc://notareal.hs/file");
|
||||||
|
assert!(file.is_encrypted());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn room_message_encrypted_content_unstable_deserialization() {
|
||||||
|
let json_data = json!({
|
||||||
|
"body": "Upload: my_file.txt",
|
||||||
|
"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",
|
||||||
|
},
|
||||||
|
"msgtype": "m.file",
|
||||||
|
"org.matrix.msc1767.text": "Upload: my_file.txt",
|
||||||
|
"org.matrix.msc1767.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",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let event_content = from_json_value::<RoomMessageEventContent>(json_data).unwrap();
|
||||||
|
assert_matches!(event_content.msgtype, MessageType::File(_));
|
||||||
|
if let MessageType::File(content) = event_content.msgtype {
|
||||||
|
assert_eq!(content.body, "Upload: my_file.txt");
|
||||||
|
assert_matches!(content.source, MediaSource::Encrypted(_));
|
||||||
|
if let MediaSource::Encrypted(encrypted_file) = content.source {
|
||||||
|
assert_eq!(encrypted_file.url, "mxc://notareal.hs/file");
|
||||||
|
}
|
||||||
|
let message = content.message.unwrap();
|
||||||
|
assert_eq!(message.len(), 1);
|
||||||
|
assert_eq!(message[0].body, "Upload: my_file.txt");
|
||||||
|
let file = content.file.unwrap();
|
||||||
|
assert_eq!(file.url, "mxc://notareal.hs/file");
|
||||||
|
assert!(file.is_encrypted());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user