Skip to main content

tuwunel_api_client/
redact.rs

1use axum::extract::State;
2use ruma::{
3	api::client::redact::redact_event::v3::{Request, Response},
4	events::room::redaction::RoomRedactionEventContent,
5};
6use tuwunel_core::{Err, Result, result::NotFound, warn};
7use tuwunel_matrix::pdu::PduBuilder;
8use tuwunel_service::transaction_ids::{RoomScope, RoomTxn};
9
10use crate::{
11	Ruma,
12	utils::{is_self_redaction, txnid_event_id, txnid_unsigned},
13};
14
15/// # `PUT /_matrix/client/v3/rooms/{roomId}/redact/{eventId}/{txnId}`
16///
17/// Tries to send a redaction event into the room.
18///
19/// A retried transaction (the same sender and device, room, target and
20/// transaction ID) returns the earlier redaction's event ID and sends nothing
21/// new.
22pub(crate) async fn redact_event_route(
23	State(services): State<crate::State>,
24	body: Ruma<Request>,
25) -> Result<Response> {
26	let sender_user = body.sender_user();
27	let sender_device = body.sender_device.as_deref();
28
29	if services.config.rooms.redaction.disable_local
30		&& !services.admin.user_is_admin(sender_user).await
31	{
32		warn!(
33			%sender_user,
34			event_id = %body.event_id,
35			"Local redactions are disabled, non-admin user attempted to redact an event"
36		);
37		return Err!(Request(Forbidden("Redactions are disabled on this server.")));
38	}
39
40	if services.users.is_suspended(sender_user).await
41		&& !is_self_redaction(&services, sender_user, &body.event_id).await
42	{
43		return Err!(Request(UserSuspended("Account is suspended.")));
44	}
45
46	let state_lock = services.state.mutex.lock(&body.room_id).await;
47	let scope = RoomScope::Redact(&body.event_id);
48	let txn = RoomTxn {
49		user_id: sender_user,
50		device_id: sender_device,
51		txn_id: &body.txn_id,
52		room_id: &body.room_id,
53	};
54
55	let existing = services
56		.transaction_ids
57		.existing_room_txnid(txn, scope)
58		.await
59		.optional()?;
60
61	if let Some(response) = existing {
62		return txnid_event_id(&response).map(Response::new);
63	}
64
65	let redacts = || Some(body.event_id.clone());
66	let pdu = PduBuilder {
67		redacts: redacts(),
68		unsigned: txnid_unsigned(&body.txn_id).into(),
69		..PduBuilder::timeline(&RoomRedactionEventContent {
70			redacts: redacts(),
71			reason: body.reason.clone(),
72		})
73	};
74
75	let event_id = services
76		.timeline
77		.build_and_append_pdu(pdu, sender_user, &body.room_id, &state_lock)
78		.await?;
79
80	services
81		.transaction_ids
82		.add_room_txnid(txn, scope, event_id.as_bytes());
83
84	drop(state_lock);
85
86	Ok(Response { event_id })
87}