Skip to main content

tuwunel_service/rooms/state_res/
events.rs

1//! Helper traits and types to work with events (aka PDUs).
2
3pub mod create;
4pub mod join_rules;
5pub mod member;
6pub mod power_levels;
7pub mod third_party_invite;
8
9use std::borrow::Cow;
10
11use ruma::events::room::member::MembershipState;
12use serde::{Deserialize, Deserializer};
13use serde_json::value::RawValue as RawJsonValue;
14use tuwunel_core::matrix::StateKey;
15
16pub use self::{
17	create::RoomCreateEvent,
18	join_rules::{JoinRule, RoomJoinRulesEvent},
19	member::{RoomMemberEvent, RoomMemberEventContent},
20	power_levels::{RoomPowerLevelsEvent, RoomPowerLevelsIntField},
21	third_party_invite::RoomThirdPartyInviteEvent,
22};
23
24/// Whether a stored event is a power event, decoded from the fields that decide
25/// it.
26///
27/// Definition in the spec:
28///
29/// > A power event is a state event with type `m.room.power_levels` or
30/// > `m.room.join_rules`, or a
31/// > state event with type `m.room.member` where the `membership` is `leave` or
32/// > `ban` and the
33/// > `sender` does not match the `state_key`. The idea behind this is that
34/// > power events are events
35/// > that might remove someone’s ability to do something in the room.
36///
37/// `m.room.create` is included, extending the spec definition to match Synapse
38/// and Ruma.
39pub(super) struct PowerEvent(pub(super) bool);
40
41#[derive(Deserialize)]
42struct PowerEventFields<'a> {
43	#[serde(rename = "type")]
44	kind: PowerEventType,
45	#[serde(borrow)]
46	sender: Cow<'a, str>,
47	state_key: Option<StateKey>,
48	#[serde(borrow)]
49	content: &'a RawJsonValue,
50}
51
52#[derive(Deserialize)]
53enum PowerEventType {
54	#[serde(rename = "m.room.create")]
55	Create,
56	#[serde(rename = "m.room.power_levels")]
57	PowerLevels,
58	#[serde(rename = "m.room.join_rules")]
59	JoinRules,
60	#[serde(rename = "m.room.member")]
61	Member,
62	#[serde(other)]
63	Other,
64}
65
66impl<'de> Deserialize<'de> for PowerEvent {
67	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
68	where
69		D: Deserializer<'de>,
70	{
71		let event = PowerEventFields::deserialize(deserializer)?;
72		let state_key = event.state_key.as_deref();
73		let is_power = match event.kind {
74			| PowerEventType::Other => false,
75			| PowerEventType::Create
76			| PowerEventType::PowerLevels
77			| PowerEventType::JoinRules => state_key == Some(""),
78			| PowerEventType::Member =>
79				is_power_membership(event.content) && Some(&*event.sender) != state_key,
80		};
81
82		Ok(Self(is_power))
83	}
84}
85
86fn is_power_membership(content: &RawJsonValue) -> bool {
87	RoomMemberEventContent::new(content)
88		.membership()
89		.is_ok_and(|membership| {
90			matches!(membership, MembershipState::Leave | MembershipState::Ban)
91		})
92}
93
94/// Whether the given event is a power event.
95///
96/// Operates on a fully decoded event; `PowerEvent` must classify identically.
97#[cfg(test)]
98pub(super) fn is_power_event<Pdu>(event: &Pdu) -> bool
99where
100	Pdu: tuwunel_core::matrix::Event,
101{
102	use ruma::events::TimelineEventType;
103
104	match event.event_type() {
105		| TimelineEventType::RoomPowerLevels
106		| TimelineEventType::RoomJoinRules
107		| TimelineEventType::RoomCreate => event.state_key() == Some(""),
108		| TimelineEventType::RoomMember => {
109			let content = RoomMemberEventContent::new(event.content());
110			if content.membership().is_ok_and(|membership| {
111				matches!(membership, MembershipState::Leave | MembershipState::Ban)
112			}) {
113				return Some(event.sender().as_str()) != event.state_key();
114			}
115
116			false
117		},
118		| _ => false,
119	}
120}