From 67b4f19c60b00f6637b0eed218d0fd298dd3f0d3 Mon Sep 17 00:00:00 2001
From: strawberry <strawberry@puppygock.gay>
Date: Sun, 21 Apr 2024 13:02:56 -0400
Subject: [PATCH] simplify room v11 top level redacts key

Signed-off-by: strawberry <strawberry@puppygock.gay>
---
 src/service/pdu.rs                | 63 +++++++++++++++++++++++++------
 src/service/rooms/timeline/mod.rs | 15 --------
 2 files changed, 52 insertions(+), 26 deletions(-)

diff --git a/src/service/pdu.rs b/src/service/pdu.rs
index 6dc965ff8..d8f020e6c 100644
--- a/src/service/pdu.rs
+++ b/src/service/pdu.rs
@@ -3,9 +3,10 @@
 use ruma::{
 	canonical_json::redact_content_in_place,
 	events::{
-		room::member::RoomMemberEventContent, space::child::HierarchySpaceChildEvent, AnyEphemeralRoomEvent,
-		AnyMessageLikeEvent, AnyStateEvent, AnyStrippedStateEvent, AnySyncStateEvent, AnySyncTimelineEvent,
-		AnyTimelineEvent, StateEvent, TimelineEventType,
+		room::{member::RoomMemberEventContent, redaction::RoomRedactionEventContent},
+		space::child::HierarchySpaceChildEvent,
+		AnyEphemeralRoomEvent, AnyMessageLikeEvent, AnyStateEvent, AnyStrippedStateEvent, AnySyncStateEvent,
+		AnySyncTimelineEvent, AnyTimelineEvent, StateEvent, TimelineEventType,
 	},
 	serde::Raw,
 	state_res, CanonicalJsonObject, CanonicalJsonValue, EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId,
@@ -98,10 +99,47 @@ pub fn add_age(&mut self) -> crate::Result<()> {
 		Ok(())
 	}
 
+	/// Copies the `redacts` property of the event to the `content` dict and
+	/// vice-versa.
+	///
+	/// This follows the specification's
+	/// [recommendation](https://spec.matrix.org/v1.10/rooms/v11/#moving-the-redacts-property-of-mroomredaction-events-to-a-content-property):
+	///
+	/// > For backwards-compatibility with older clients, servers should add a
+	/// > redacts
+	/// > property to the top level of m.room.redaction events in when serving
+	/// > such events
+	/// > over the Client-Server API.
+	///
+	/// > For improved compatibility with newer clients, servers should add a
+	/// > redacts property
+	/// > to the content of m.room.redaction events in older room versions when
+	/// > serving
+	/// > such events over the Client-Server API.
+	#[must_use]
+	pub fn copy_redacts(&self) -> (Option<Arc<EventId>>, Box<RawJsonValue>) {
+		if self.kind == TimelineEventType::RoomRedaction {
+			if let Ok(mut content) = serde_json::from_str::<RoomRedactionEventContent>(self.content.get()) {
+				if let Some(redacts) = content.redacts {
+					return (Some(redacts.into()), self.content.clone());
+				} else if let Some(redacts) = self.redacts.clone() {
+					content.redacts = Some(redacts.into());
+					return (
+						self.redacts.clone(),
+						to_raw_value(&content).expect("Must be valid, we only added redacts field"),
+					);
+				}
+			}
+		}
+
+		(self.redacts.clone(), self.content.clone())
+	}
+
 	#[tracing::instrument(skip(self))]
 	pub fn to_sync_room_event(&self) -> Raw<AnySyncTimelineEvent> {
+		let (redacts, content) = self.copy_redacts();
 		let mut json = json!({
-			"content": self.content,
+			"content": content,
 			"type": self.kind,
 			"event_id": self.event_id,
 			"sender": self.sender,
@@ -114,7 +152,7 @@ pub fn to_sync_room_event(&self) -> Raw<AnySyncTimelineEvent> {
 		if let Some(state_key) = &self.state_key {
 			json["state_key"] = json!(state_key);
 		}
-		if let Some(redacts) = &self.redacts {
+		if let Some(redacts) = &redacts {
 			json["redacts"] = json!(redacts);
 		}
 
@@ -124,8 +162,9 @@ pub fn to_sync_room_event(&self) -> Raw<AnySyncTimelineEvent> {
 	/// This only works for events that are also AnyRoomEvents.
 	#[tracing::instrument(skip(self))]
 	pub fn to_any_event(&self) -> Raw<AnyEphemeralRoomEvent> {
+		let (redacts, content) = self.copy_redacts();
 		let mut json = json!({
-			"content": self.content,
+			"content": content,
 			"type": self.kind,
 			"event_id": self.event_id,
 			"sender": self.sender,
@@ -139,7 +178,7 @@ pub fn to_any_event(&self) -> Raw<AnyEphemeralRoomEvent> {
 		if let Some(state_key) = &self.state_key {
 			json["state_key"] = json!(state_key);
 		}
-		if let Some(redacts) = &self.redacts {
+		if let Some(redacts) = &redacts {
 			json["redacts"] = json!(redacts);
 		}
 
@@ -148,8 +187,9 @@ pub fn to_any_event(&self) -> Raw<AnyEphemeralRoomEvent> {
 
 	#[tracing::instrument(skip(self))]
 	pub fn to_room_event(&self) -> Raw<AnyTimelineEvent> {
+		let (redacts, content) = self.copy_redacts();
 		let mut json = json!({
-			"content": self.content,
+			"content": content,
 			"type": self.kind,
 			"event_id": self.event_id,
 			"sender": self.sender,
@@ -163,7 +203,7 @@ pub fn to_room_event(&self) -> Raw<AnyTimelineEvent> {
 		if let Some(state_key) = &self.state_key {
 			json["state_key"] = json!(state_key);
 		}
-		if let Some(redacts) = &self.redacts {
+		if let Some(redacts) = &redacts {
 			json["redacts"] = json!(redacts);
 		}
 
@@ -172,8 +212,9 @@ pub fn to_room_event(&self) -> Raw<AnyTimelineEvent> {
 
 	#[tracing::instrument(skip(self))]
 	pub fn to_message_like_event(&self) -> Raw<AnyMessageLikeEvent> {
+		let (redacts, content) = self.copy_redacts();
 		let mut json = json!({
-			"content": self.content,
+			"content": content,
 			"type": self.kind,
 			"event_id": self.event_id,
 			"sender": self.sender,
@@ -187,7 +228,7 @@ pub fn to_message_like_event(&self) -> Raw<AnyMessageLikeEvent> {
 		if let Some(state_key) = &self.state_key {
 			json["state_key"] = json!(state_key);
 		}
-		if let Some(redacts) = &self.redacts {
+		if let Some(redacts) = &redacts {
 			json["redacts"] = json!(redacts);
 		}
 
diff --git a/src/service/rooms/timeline/mod.rs b/src/service/rooms/timeline/mod.rs
index 3639c56b1..94b993f8d 100644
--- a/src/service/rooms/timeline/mod.rs
+++ b/src/service/rooms/timeline/mod.rs
@@ -307,21 +307,6 @@ pub async fn append_pdu(
 		let mut pdu_id = shortroomid.to_be_bytes().to_vec();
 		pdu_id.extend_from_slice(&count2.to_be_bytes());
 
-		// https://spec.matrix.org/v1.9/rooms/v11/#moving-the-redacts-property-of-mroomredaction-events-to-a-content-property
-		// For backwards-compatibility with older clients,
-		// servers should add a redacts property to the top level of m.room.redaction
-		// events in when serving such events over the Client-Server API.
-		if pdu.kind == TimelineEventType::RoomRedaction
-			&& services().rooms.state.get_room_version(&pdu.room_id)? == RoomVersionId::V11
-		{
-			let content = serde_json::from_str::<RoomRedactionEventContent>(pdu.content.get())
-				.map_err(|_| Error::bad_database("Invalid content in redaction pdu."))?;
-
-			if let Some(redact_id) = &content.redacts {
-				pdu_json.insert("redacts".to_owned(), CanonicalJsonValue::String(redact_id.to_string()));
-			}
-		}
-
 		// Insert pdu
 		self.db.append_pdu(&pdu_id, pdu, &pdu_json, count2)?;
 
-- 
GitLab