sdk_base: message events in message queue have content redacted

The MessageQueue holds MessageEventStub<AnyMessageEventContent> so when
a redaction event is encountered the redacted event's content can be
replaced. The Unsigned field redacted_because is also populated with the
redaction event itself with the addition of a room_id Stub -> full
event.
master
Devin R 2020-07-03 16:52:35 -04:00
parent dcc3d6e755
commit e7c70854ab
3 changed files with 101 additions and 21 deletions

View File

@ -7,28 +7,42 @@ use std::cmp::Ordering;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::vec::IntoIter; use std::vec::IntoIter;
use crate::events::AnyMessageEventStub; use crate::events::{AnyMessageEventContent, AnyMessageEventStub, MessageEventStub};
use serde::{de, ser, Serialize}; use serde::{de, ser, Serialize};
const MESSAGE_QUEUE_CAP: usize = 35; const MESSAGE_QUEUE_CAP: usize = 35;
pub type SyncMessageEvent = MessageEventStub<AnyMessageEventContent>;
/// A queue that holds the 35 most recent messages received from the server. /// A queue that holds the 35 most recent messages received from the server.
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct MessageQueue { pub struct MessageQueue {
msgs: Vec<MessageWrapper>, pub(crate) msgs: Vec<MessageWrapper>,
} }
/// A wrapper for `ruma_events::AnyMessageEventStub` that allows implementation of /// A wrapper for `ruma_events::SyncMessageEvent` that allows implementation of
/// Eq, Ord and the Partial versions of the traits. /// Eq, Ord and the Partial versions of the traits.
/// ///
/// `MessageWrapper` also implements Deref and DerefMut so accessing the events contents /// `MessageWrapper` also implements Deref and DerefMut so accessing the events contents
/// are simplified. /// are simplified.
#[derive(Clone, Debug, Serialize)] #[derive(Clone, Debug, Serialize)]
pub struct MessageWrapper(pub AnyMessageEventStub); pub struct MessageWrapper(pub SyncMessageEvent);
impl MessageWrapper {
pub fn clone_into_any_content(event: &AnyMessageEventStub) -> SyncMessageEvent {
MessageEventStub {
content: event.content().clone(),
sender: event.sender().clone(),
origin_server_ts: event.origin_server_ts().clone(),
event_id: event.event_id().clone(),
unsigned: event.unsigned().clone(),
}
}
}
impl Deref for MessageWrapper { impl Deref for MessageWrapper {
type Target = AnyMessageEventStub; type Target = SyncMessageEvent;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.0 &self.0
@ -43,7 +57,7 @@ impl DerefMut for MessageWrapper {
impl PartialEq for MessageWrapper { impl PartialEq for MessageWrapper {
fn eq(&self, other: &MessageWrapper) -> bool { fn eq(&self, other: &MessageWrapper) -> bool {
self.0.event_id() == other.0.event_id() self.0.event_id == other.0.event_id
} }
} }
@ -51,7 +65,7 @@ impl Eq for MessageWrapper {}
impl PartialOrd for MessageWrapper { impl PartialOrd for MessageWrapper {
fn partial_cmp(&self, other: &MessageWrapper) -> Option<Ordering> { fn partial_cmp(&self, other: &MessageWrapper) -> Option<Ordering> {
Some(self.0.origin_server_ts().cmp(&other.0.origin_server_ts())) Some(self.0.origin_server_ts.cmp(&other.0.origin_server_ts))
} }
} }
@ -68,7 +82,7 @@ impl PartialEq for MessageQueue {
.msgs .msgs
.iter() .iter()
.zip(other.msgs.iter()) .zip(other.msgs.iter())
.all(|(msg_a, msg_b)| msg_a.event_id() == msg_b.event_id()) .all(|(msg_a, msg_b)| msg_a.event_id == msg_b.event_id)
} }
} }
@ -83,10 +97,10 @@ impl MessageQueue {
/// Inserts a `MessageEvent` into `MessageQueue`, sorted by by `origin_server_ts`. /// Inserts a `MessageEvent` into `MessageQueue`, sorted by by `origin_server_ts`.
/// ///
/// Removes the oldest element in the queue if there are more than 10 elements. /// Removes the oldest element in the queue if there are more than 10 elements.
pub fn push(&mut self, msg: AnyMessageEventStub) -> bool { pub fn push(&mut self, msg: SyncMessageEvent) -> bool {
// only push new messages into the queue // only push new messages into the queue
if let Some(latest) = self.msgs.last() { if let Some(latest) = self.msgs.last() {
if msg.origin_server_ts() < latest.origin_server_ts() && self.msgs.len() >= 10 { if msg.origin_server_ts < latest.origin_server_ts && self.msgs.len() >= 10 {
return false; return false;
} }
} }
@ -131,7 +145,7 @@ pub(crate) mod ser_deser {
where where
D: de::Deserializer<'de>, D: de::Deserializer<'de>,
{ {
let messages: Vec<AnyMessageEventStub> = de::Deserialize::deserialize(deserializer)?; let messages: Vec<SyncMessageEvent> = de::Deserialize::deserialize(deserializer)?;
let mut msgs = vec![]; let mut msgs = vec![];
for msg in messages { for msg in messages {
@ -172,7 +186,7 @@ mod test {
let mut room = Room::new(&id, &user); let mut room = Room::new(&id, &user);
let json: &serde_json::Value = &test_json::MESSAGE_TEXT; let json: &serde_json::Value = &test_json::MESSAGE_TEXT;
let msg = serde_json::from_value::<AnyMessageEventStub>(json.clone()).unwrap(); let msg = serde_json::from_value::<SyncMessageEvent>(json.clone()).unwrap();
let mut msgs = MessageQueue::new(); let mut msgs = MessageQueue::new();
msgs.push(msg.clone()); msgs.push(msg.clone());
@ -218,7 +232,7 @@ mod test {
let mut room = Room::new(&id, &user); let mut room = Room::new(&id, &user);
let json: &serde_json::Value = &test_json::MESSAGE_TEXT; let json: &serde_json::Value = &test_json::MESSAGE_TEXT;
let msg = serde_json::from_value::<AnyMessageEventStub>(json.clone()).unwrap(); let msg = serde_json::from_value::<SyncMessageEvent>(json.clone()).unwrap();
let mut msgs = MessageQueue::new(); let mut msgs = MessageQueue::new();
msgs.push(msg.clone()); msgs.push(msg.clone());

View File

@ -34,18 +34,34 @@ use crate::events::room::{
}; };
use crate::events::{ use crate::events::{
Algorithm, AnyRoomEventStub, AnyStateEventStub, AnyStrippedStateEventStub, EventType, Algorithm, AnyMessageEventContent, AnyRoomEventStub, AnyStateEventStub,
StateEventStub, StrippedStateEventStub, AnyStrippedStateEventStub, EventJson, EventType, StateEventStub, StrippedStateEventStub,
}; };
#[cfg(feature = "messages")] #[cfg(feature = "messages")]
use crate::events::{room::redaction::RedactionEventStub, AnyMessageEventStub}; use crate::events::{
room::redaction::{RedactionEvent, RedactionEventStub},
AnyMessageEventStub,
};
use crate::identifiers::{RoomAliasId, RoomId, UserId}; use crate::identifiers::{RoomAliasId, RoomId, UserId};
use crate::js_int::{Int, UInt}; use crate::js_int::{Int, UInt};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[cfg(feature = "messages")]
fn full_event_from_stub(event: RedactionEventStub, room_id: RoomId) -> RedactionEvent {
RedactionEvent {
content: event.content,
redacts: event.redacts,
event_id: event.event_id,
unsigned: event.unsigned,
sender: event.sender,
origin_server_ts: event.origin_server_ts,
room_id,
}
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
/// `RoomName` allows the calculation of a text room name. /// `RoomName` allows the calculation of a text room name.
pub struct RoomName { pub struct RoomName {
@ -618,7 +634,8 @@ impl Room {
#[cfg(feature = "messages")] #[cfg(feature = "messages")]
#[cfg_attr(docsrs, doc(cfg(feature = "messages")))] #[cfg_attr(docsrs, doc(cfg(feature = "messages")))]
pub fn handle_message(&mut self, event: &AnyMessageEventStub) -> bool { pub fn handle_message(&mut self, event: &AnyMessageEventStub) -> bool {
self.messages.push(event.clone()) let message = MessageWrapper::clone_into_any_content(event);
self.messages.push(message)
} }
/// Handle a room.redaction event and update the `MessageQueue` if necessary. /// Handle a room.redaction event and update the `MessageQueue` if necessary.
@ -630,9 +647,12 @@ impl Room {
if let Some(msg) = self if let Some(msg) = self
.messages .messages
.iter_mut() .iter_mut()
.find(|msg| &event.redacts == msg.event_id()) .find(|msg| event.redacts == msg.event_id)
{ {
*msg = MessageWrapper(AnyMessageEventStub::RoomRedaction(event.clone())); msg.content = AnyMessageEventContent::RoomRedaction(event.content.clone());
let redaction = full_event_from_stub(event.clone(), self.room_id.clone());
msg.unsigned.redacted_because = Some(EventJson::from(redaction));
true true
} else { } else {
false false
@ -1152,6 +1172,51 @@ mod test {
assert_eq!(vec!["example2"], room_names); assert_eq!(vec!["example2"], room_names);
} }
#[cfg(feature = "messages")]
#[async_test]
async fn message_queue_redaction_event() {
let room_id = get_room_id();
let mut response = sync_response(SyncResponseFile::DefaultWithSummary);
let session = Session {
access_token: "1234".to_owned(),
user_id: UserId::try_from("@example:localhost").unwrap(),
device_id: "DEVICEID".to_owned(),
};
let client = BaseClient::new().unwrap();
client.restore_login(session).await.unwrap();
client.receive_sync_response(&mut response).await.unwrap();
let json = serde_json::json!({
"content": {
"reason": "😀"
},
"event_id": "$151957878228ssqrJ:localhost",
"origin_server_ts": 151957878,
"sender": "@example:localhost",
"type": "m.room.redaction",
"redacts": "$152037280074GZeOm:localhost"
});
let mut event: EventJson<AnyRoomEventStub> = serde_json::from_value(json).unwrap();
client
.receive_joined_timeline_event(&room_id, &mut event)
.await
.unwrap();
for room in client.joined_rooms().read().await.values() {
let queue = &room.read().await.messages;
println!("{:?}", queue);
if let crate::events::AnyMessageEventContent::RoomRedaction(content) =
&queue.msgs[0].content
{
assert_eq!(content.reason, Some("😀".to_string()));
} else {
panic!("message event in message queue should be redacted")
}
}
}
#[async_test] #[async_test]
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
async fn encryption_info_test() { async fn encryption_info_test() {

View File

@ -9,7 +9,8 @@ pub mod sync;
pub use events::{ pub use events::{
ALIAS, ALIASES, EVENT_ID, KEYS_QUERY, KEYS_UPLOAD, LOGIN, LOGIN_RESPONSE_ERR, LOGOUT, MEMBER, ALIAS, ALIASES, EVENT_ID, KEYS_QUERY, KEYS_UPLOAD, LOGIN, LOGIN_RESPONSE_ERR, LOGOUT, MEMBER,
MESSAGE_EDIT, MESSAGE_TEXT, NAME, POWER_LEVELS, PRESENCE, PUBLIC_ROOMS, REACTION, MESSAGE_EDIT, MESSAGE_TEXT, NAME, POWER_LEVELS, PRESENCE, PUBLIC_ROOMS, REACTION, REDACTED,
REGISTRATION_RESPONSE_ERR, ROOM_ID, ROOM_MESSAGES, TYPING, REDACTED_INVALID, REDACTED_STATE, REDACTION, REGISTRATION_RESPONSE_ERR, ROOM_ID, ROOM_MESSAGES,
TYPING,
}; };
pub use sync::{DEFAULT_SYNC_SUMMARY, INVITE_SYNC, LEAVE_SYNC, LEAVE_SYNC_EVENT, MORE_SYNC, SYNC}; pub use sync::{DEFAULT_SYNC_SUMMARY, INVITE_SYNC, LEAVE_SYNC, LEAVE_SYNC_EVENT, MORE_SYNC, SYNC};