1use std::{fmt, future::Future};
19
20use matrix_sdk_common::{SendOutsideWasm, SyncOutsideWasm};
21use serde::{Deserialize, Deserializer, Serialize, Serializer, ser::SerializeSeq};
22use tracing::{debug, warn};
23
24use super::{
25 MessageLikeEventFilter, StateEventFilter,
26 filter::{Filter, FilterInput, ToDeviceEventFilter},
27};
28
29pub trait CapabilitiesProvider: SendOutsideWasm + SyncOutsideWasm + 'static {
33 fn acquire_capabilities(
37 &self,
38 capabilities: Capabilities,
39 ) -> impl Future<Output = Capabilities> + SendOutsideWasm;
40}
41
42#[derive(Clone, Debug, Default)]
44#[cfg_attr(test, derive(PartialEq))]
45pub struct Capabilities {
46 pub read: Vec<Filter>,
48 pub send: Vec<Filter>,
50 pub requires_client: bool,
56 pub update_delayed_event: bool,
58 pub send_delayed_event: bool,
60
61 pub download_file: bool,
63
64 pub rtc_transports: bool,
67
68 pub rtc_livekit_get_token: bool,
71
72 pub rtc_livekit_delegate_delayed_leave: bool,
75}
76
77impl Capabilities {
78 pub(super) fn allow_reading<'a>(
84 &self,
85 event_filter_input: impl TryInto<FilterInput<'a>>,
86 ) -> bool {
87 match &event_filter_input.try_into() {
88 Err(_) => {
89 warn!("Failed to convert event into filter input for `allow_reading`.");
90 false
91 }
92 Ok(filter_input) => self.read.iter().any(|f| f.matches(filter_input)),
93 }
94 }
95
96 pub(super) fn allow_sending<'a>(
102 &self,
103 event_filter_input: impl TryInto<FilterInput<'a>>,
104 ) -> bool {
105 match &event_filter_input.try_into() {
106 Err(_) => {
107 warn!("Failed to convert event into filter input for `allow_sending`.");
108 false
109 }
110 Ok(filter_input) => self.send.iter().any(|f| f.matches(filter_input)),
111 }
112 }
113
114 pub(super) fn has_read_filter_for_type(&self, event_type: &str) -> bool {
118 self.read.iter().any(|f| f.filter_event_type() == event_type)
119 }
120}
121
122pub(super) const SEND_EVENT: &str = "org.matrix.msc2762.send.event";
123pub(super) const READ_EVENT: &str = "org.matrix.msc2762.receive.event";
124pub(super) const SEND_STATE: &str = "org.matrix.msc2762.send.state_event";
125pub(super) const READ_STATE: &str = "org.matrix.msc2762.receive.state_event";
126pub(super) const SEND_TODEVICE: &str = "org.matrix.msc3819.send.to_device";
127pub(super) const READ_TODEVICE: &str = "org.matrix.msc3819.receive.to_device";
128pub(super) const REQUIRES_CLIENT: &str = "io.element.requires_client";
129pub(super) const SEND_DELAYED_EVENT: &str = "org.matrix.msc4157.send.delayed_event";
130pub(super) const UPDATE_DELAYED_EVENT: &str = "org.matrix.msc4157.update_delayed_event";
131
132pub(super) const DOWNLOAD_FILE: &str = "org.matrix.msc4039.download_file";
133
134pub(super) const RTC_TRANSPORTS: &str = "org.matrix.msc4515.rtc_transports";
135
136pub(super) const RTC_LIVEKIT_GET_TOKEN: &str = "org.matrix.msc4533.rtc_livekit_get_token";
137pub(super) const RTC_LIVEKIT_DELEGATE_DELAYED_LEAVE: &str =
138 "org.matrix.msc4533.rtc_livekit_delegate_delayed_leave";
139
140impl Serialize for Capabilities {
141 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
142 where
143 S: Serializer,
144 {
145 struct PrintEventFilter<'a>(&'a Filter);
146 impl fmt::Display for PrintEventFilter<'_> {
147 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148 match self.0 {
149 Filter::MessageLike(filter) => PrintMessageLikeEventFilter(filter).fmt(f),
150 Filter::State(filter) => PrintStateEventFilter(filter).fmt(f),
151 Filter::ToDevice(filter) => {
152 write!(f, "{}", filter.event_type)
158 }
159 }
160 }
161 }
162
163 struct PrintMessageLikeEventFilter<'a>(&'a MessageLikeEventFilter);
164 impl fmt::Display for PrintMessageLikeEventFilter<'_> {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 match self.0 {
167 MessageLikeEventFilter::WithType(event_type) => {
168 write!(f, "{event_type}")
171 }
172 MessageLikeEventFilter::RoomMessageWithMsgtype(msgtype) => {
173 write!(f, "m.room.message#{msgtype}")
174 }
175 }
176 }
177 }
178
179 struct PrintStateEventFilter<'a>(&'a StateEventFilter);
180 impl fmt::Display for PrintStateEventFilter<'_> {
181 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182 match self.0 {
184 StateEventFilter::WithType(event_type) => write!(f, "{event_type}"),
185 StateEventFilter::WithTypeAndStateKey(event_type, state_key) => {
186 write!(f, "{event_type}#{state_key}")
187 }
188 }
189 }
190 }
191
192 let mut seq = serializer.serialize_seq(None)?;
193
194 if self.requires_client {
195 seq.serialize_element(REQUIRES_CLIENT)?;
196 }
197 if self.update_delayed_event {
198 seq.serialize_element(UPDATE_DELAYED_EVENT)?;
199 }
200 if self.send_delayed_event {
201 seq.serialize_element(SEND_DELAYED_EVENT)?;
202 }
203 if self.download_file {
204 seq.serialize_element(DOWNLOAD_FILE)?;
205 }
206 if self.rtc_transports {
207 seq.serialize_element(RTC_TRANSPORTS)?;
208 }
209 if self.rtc_livekit_get_token {
210 seq.serialize_element(RTC_LIVEKIT_GET_TOKEN)?;
211 }
212 if self.rtc_livekit_delegate_delayed_leave {
213 seq.serialize_element(RTC_LIVEKIT_DELEGATE_DELAYED_LEAVE)?;
214 }
215 for filter in &self.read {
216 let name = match filter {
217 Filter::MessageLike(_) => READ_EVENT,
218 Filter::State(_) => READ_STATE,
219 Filter::ToDevice(_) => READ_TODEVICE,
220 };
221 seq.serialize_element(&format!("{name}:{}", PrintEventFilter(filter)))?;
222 }
223 for filter in &self.send {
224 let name = match filter {
225 Filter::MessageLike(_) => SEND_EVENT,
226 Filter::State(_) => SEND_STATE,
227 Filter::ToDevice(_) => SEND_TODEVICE,
228 };
229 seq.serialize_element(&format!("{name}:{}", PrintEventFilter(filter)))?;
230 }
231
232 seq.end()
233 }
234}
235
236impl<'de> Deserialize<'de> for Capabilities {
237 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
238 where
239 D: Deserializer<'de>,
240 {
241 enum Permission {
242 RequiresClient,
243 UpdateDelayedEvent,
244 SendDelayedEvent,
245 DownloadFile,
246 RtcTransports,
247 RtcLivekitGetToken,
248 RtcLivekitDelegateDelayedLeave,
249 Read(Filter),
250 Send(Filter),
251 Unknown,
252 }
253
254 impl<'de> Deserialize<'de> for Permission {
255 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
256 where
257 D: Deserializer<'de>,
258 {
259 let s = ruma::serde::deserialize_cow_str(deserializer)?;
260 if s == REQUIRES_CLIENT {
261 return Ok(Self::RequiresClient);
262 }
263 if s == UPDATE_DELAYED_EVENT {
264 return Ok(Self::UpdateDelayedEvent);
265 }
266 if s == SEND_DELAYED_EVENT {
267 return Ok(Self::SendDelayedEvent);
268 }
269 if s == DOWNLOAD_FILE {
270 return Ok(Self::DownloadFile);
271 }
272 if s == RTC_TRANSPORTS {
273 return Ok(Self::RtcTransports);
274 }
275 if s == RTC_LIVEKIT_GET_TOKEN {
276 return Ok(Self::RtcLivekitGetToken);
277 }
278 if s == RTC_LIVEKIT_DELEGATE_DELAYED_LEAVE {
279 return Ok(Self::RtcLivekitDelegateDelayedLeave);
280 }
281
282 match s.split_once(':') {
283 Some((READ_EVENT, filter_s)) => Ok(Permission::Read(Filter::MessageLike(
284 parse_message_event_filter(filter_s),
285 ))),
286 Some((SEND_EVENT, filter_s)) => Ok(Permission::Send(Filter::MessageLike(
287 parse_message_event_filter(filter_s),
288 ))),
289 Some((READ_STATE, filter_s)) => {
290 Ok(Permission::Read(Filter::State(parse_state_event_filter(filter_s))))
291 }
292 Some((SEND_STATE, filter_s)) => {
293 Ok(Permission::Send(Filter::State(parse_state_event_filter(filter_s))))
294 }
295 Some((READ_TODEVICE, filter_s)) => Ok(Permission::Read(Filter::ToDevice(
296 parse_to_device_event_filter(filter_s),
297 ))),
298 Some((SEND_TODEVICE, filter_s)) => Ok(Permission::Send(Filter::ToDevice(
299 parse_to_device_event_filter(filter_s),
300 ))),
301 _ => {
302 debug!("Unknown capability `{s}`");
303 Ok(Self::Unknown)
304 }
305 }
306 }
307 }
308
309 fn parse_message_event_filter(s: &str) -> MessageLikeEventFilter {
310 match s.strip_prefix("m.room.message#") {
311 Some(msgtype) => MessageLikeEventFilter::RoomMessageWithMsgtype(msgtype.to_owned()),
312 None => MessageLikeEventFilter::WithType(s.into()),
314 }
315 }
316
317 fn parse_state_event_filter(s: &str) -> StateEventFilter {
318 match s.split_once('#') {
321 Some((event_type, state_key)) => {
322 StateEventFilter::WithTypeAndStateKey(event_type.into(), state_key.to_owned())
323 }
324 None => StateEventFilter::WithType(s.into()),
325 }
326 }
327
328 fn parse_to_device_event_filter(s: &str) -> ToDeviceEventFilter {
329 ToDeviceEventFilter::new(s.into())
330 }
331
332 let mut capabilities = Capabilities::default();
333 for capability in Vec::<Permission>::deserialize(deserializer)? {
334 match capability {
335 Permission::RequiresClient => capabilities.requires_client = true,
336 Permission::Read(filter) => capabilities.read.push(filter),
337 Permission::Send(filter) => capabilities.send.push(filter),
338 Permission::Unknown => {}
340 Permission::UpdateDelayedEvent => capabilities.update_delayed_event = true,
341 Permission::SendDelayedEvent => capabilities.send_delayed_event = true,
342 Permission::DownloadFile => capabilities.download_file = true,
343 Permission::RtcTransports => capabilities.rtc_transports = true,
344 Permission::RtcLivekitGetToken => capabilities.rtc_livekit_get_token = true,
345 Permission::RtcLivekitDelegateDelayedLeave => {
346 capabilities.rtc_livekit_delegate_delayed_leave = true
347 }
348 }
349 }
350
351 Ok(capabilities)
352 }
353}
354
355#[cfg(test)]
356mod tests {
357 use ruma::events::StateEventType;
358
359 use super::*;
360 use crate::widget::filter::ToDeviceEventFilter;
361
362 #[test]
363 fn deserialization_of_no_capabilities() {
364 let capabilities_str = r#"[]"#;
365
366 let parsed = serde_json::from_str::<Capabilities>(capabilities_str).unwrap();
367 let expected = Capabilities::default();
368
369 assert_eq!(parsed, expected);
370 }
371
372 #[test]
373 fn deserialization_of_capabilities() {
374 let capabilities_str = r#"[
375 "m.always_on_screen",
376 "io.element.requires_client",
377 "org.matrix.msc2762.receive.event:org.matrix.rageshake_request",
378 "org.matrix.msc2762.receive.state_event:m.room.member",
379 "org.matrix.msc2762.receive.state_event:org.matrix.msc3401.call.member",
380 "org.matrix.msc3819.receive.to_device:io.element.call.encryption_keys",
381 "org.matrix.msc2762.send.event:org.matrix.rageshake_request",
382 "org.matrix.msc2762.send.state_event:org.matrix.msc3401.call.member#@user:matrix.server",
383 "org.matrix.msc3819.send.to_device:io.element.call.encryption_keys",
384 "org.matrix.msc4157.send.delayed_event",
385 "org.matrix.msc4157.update_delayed_event",
386 "org.matrix.msc4039.download_file",
387 "org.matrix.msc4515.rtc_transports",
388 "org.matrix.msc4533.rtc_livekit_get_token",
389 "org.matrix.msc4533.rtc_livekit_delegate_delayed_leave"
390 ]"#;
391
392 let parsed = serde_json::from_str::<Capabilities>(capabilities_str).unwrap();
393 let expected = Capabilities {
394 read: vec![
395 Filter::MessageLike(MessageLikeEventFilter::WithType(
396 "org.matrix.rageshake_request".into(),
397 )),
398 Filter::State(StateEventFilter::WithType(StateEventType::RoomMember)),
399 Filter::State(StateEventFilter::WithType("org.matrix.msc3401.call.member".into())),
400 Filter::ToDevice(ToDeviceEventFilter::new(
401 "io.element.call.encryption_keys".into(),
402 )),
403 ],
404 send: vec![
405 Filter::MessageLike(MessageLikeEventFilter::WithType(
406 "org.matrix.rageshake_request".into(),
407 )),
408 Filter::State(StateEventFilter::WithTypeAndStateKey(
409 "org.matrix.msc3401.call.member".into(),
410 "@user:matrix.server".into(),
411 )),
412 Filter::ToDevice(ToDeviceEventFilter::new(
413 "io.element.call.encryption_keys".into(),
414 )),
415 ],
416 requires_client: true,
417 update_delayed_event: true,
418 send_delayed_event: true,
419 download_file: true,
420 rtc_transports: true,
421 rtc_livekit_get_token: true,
422 rtc_livekit_delegate_delayed_leave: true,
423 };
424
425 assert_eq!(parsed, expected);
426 }
427
428 #[test]
429 fn serialization_and_deserialization_are_symmetrical() {
430 let capabilities = Capabilities {
431 read: vec![
432 Filter::MessageLike(MessageLikeEventFilter::WithType("io.element.custom".into())),
433 Filter::State(StateEventFilter::WithType(StateEventType::RoomMember)),
434 Filter::State(StateEventFilter::WithTypeAndStateKey(
435 "org.matrix.msc3401.call.member".into(),
436 "@user:matrix.server".into(),
437 )),
438 Filter::ToDevice(ToDeviceEventFilter::new(
439 "io.element.call.encryption_keys".into(),
440 )),
441 ],
442 send: vec![
443 Filter::MessageLike(MessageLikeEventFilter::WithType("io.element.custom".into())),
444 Filter::State(StateEventFilter::WithTypeAndStateKey(
445 "org.matrix.msc3401.call.member".into(),
446 "@user:matrix.server".into(),
447 )),
448 Filter::ToDevice(ToDeviceEventFilter::new("my.org.other.to_device_event".into())),
449 ],
450 requires_client: true,
451 update_delayed_event: false,
452 send_delayed_event: false,
453 download_file: false,
454 rtc_transports: true,
455 rtc_livekit_get_token: true,
456 rtc_livekit_delegate_delayed_leave: false,
457 };
458
459 let capabilities_str = serde_json::to_string(&capabilities).unwrap();
460 let parsed = serde_json::from_str::<Capabilities>(&capabilities_str).unwrap();
461 assert_eq!(parsed, capabilities);
462 }
463}