Skip to main content

tuwunel_service/rooms/timeline/
redact.rs

1use ruma::{
2	EventId, RoomId,
3	canonical_json::{RedactedBecause, redact_in_place},
4};
5use tuwunel_core::{Result, err, implement, matrix::event::Event};
6
7use crate::rooms::{short::ShortRoomId, timeline::RoomMutexGuard};
8
9/// Replace a PDU with the redacted form.
10#[implement(super::Service)]
11#[tracing::instrument(name = "redact", level = "debug", skip(self))]
12pub async fn redact_pdu<Pdu: Event + Send + Sync>(
13	&self,
14	event_id: &EventId,
15	reason: &Pdu,
16	shortroomid: ShortRoomId,
17	state_lock: &RoomMutexGuard,
18) -> Result {
19	let Ok(pdu_id) = self.get_pdu_id(event_id).await else {
20		// If event does not exist, just noop
21		// TODO this is actually wrong!
22		return Ok(());
23	};
24
25	let mut pdu = self
26		.get_pdu_json_from_id(&pdu_id)
27		.await
28		.map_err(|e| {
29			err!(Database(error!(?pdu_id, ?event_id, ?e, "PDU ID points to invalid PDU.")))
30		})?;
31
32	self.services
33		.retention
34		.save_original_pdu(event_id, &pdu, state_lock)
35		.await;
36
37	let body = pdu["content"]
38		.as_object()
39		.and_then(|obj| obj.get("body"))
40		.and_then(|body| body.as_str());
41
42	if let Some(body) = body {
43		self.services
44			.search
45			.deindex_pdu(shortroomid, &pdu_id, body);
46	}
47
48	let room_id: &RoomId = pdu.get("room_id").try_into()?;
49
50	let room_version_id = self
51		.services
52		.state
53		.get_room_version(room_id)
54		.await?;
55
56	let room_version_rules = room_version_id.rules().ok_or_else(|| {
57		err!(Request(UnsupportedRoomVersion(
58			"Cannot redact event for unknown room version {room_version_id:?}."
59		)))
60	})?;
61
62	redact_in_place(
63		&mut pdu,
64		&room_version_rules.redaction,
65		Some(RedactedBecause::from_json(reason.to_canonical_object())),
66	)
67	.map_err(|err| err!("invalid event: {err}"))?;
68
69	self.replace_pdu(&pdu_id, &pdu).await
70}