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
69impl Capabilities {
70 pub(super) fn allow_reading<'a>(
76 &self,
77 event_filter_input: impl TryInto<FilterInput<'a>>,
78 ) -> bool {
79 match &event_filter_input.try_into() {
80 Err(_) => {
81 warn!("Failed to convert event into filter input for `allow_reading`.");
82 false
83 }
84 Ok(filter_input) => self.read.iter().any(|f| f.matches(filter_input)),
85 }
86 }
87
88 pub(super) fn allow_sending<'a>(
94 &self,
95 event_filter_input: impl TryInto<FilterInput<'a>>,
96 ) -> bool {
97 match &event_filter_input.try_into() {
98 Err(_) => {
99 warn!("Failed to convert event into filter input for `allow_sending`.");
100 false
101 }
102 Ok(filter_input) => self.send.iter().any(|f| f.matches(filter_input)),
103 }
104 }
105
106 pub(super) fn has_read_filter_for_type(&self, event_type: &str) -> bool {
110 self.read.iter().any(|f| f.filter_event_type() == event_type)
111 }
112}
113
114pub(super) const SEND_EVENT: &str = "org.matrix.msc2762.send.event";
115pub(super) const READ_EVENT: &str = "org.matrix.msc2762.receive.event";
116pub(super) const SEND_STATE: &str = "org.matrix.msc2762.send.state_event";
117pub(super) const READ_STATE: &str = "org.matrix.msc2762.receive.state_event";
118pub(super) const SEND_TODEVICE: &str = "org.matrix.msc3819.send.to_device";
119pub(super) const READ_TODEVICE: &str = "org.matrix.msc3819.receive.to_device";
120pub(super) const REQUIRES_CLIENT: &str = "io.element.requires_client";
121pub(super) const SEND_DELAYED_EVENT: &str = "org.matrix.msc4157.send.delayed_event";
122pub(super) const UPDATE_DELAYED_EVENT: &str = "org.matrix.msc4157.update_delayed_event";
123
124pub(super) const DOWNLOAD_FILE: &str = "org.matrix.msc4039.download_file";
125
126pub(super) const RTC_TRANSPORTS: &str = "org.matrix.msc4515.rtc_transports";
127
128impl Serialize for Capabilities {
129 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
130 where
131 S: Serializer,
132 {
133 struct PrintEventFilter<'a>(&'a Filter);
134 impl fmt::Display for PrintEventFilter<'_> {
135 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136 match self.0 {
137 Filter::MessageLike(filter) => PrintMessageLikeEventFilter(filter).fmt(f),
138 Filter::State(filter) => PrintStateEventFilter(filter).fmt(f),
139 Filter::ToDevice(filter) => {
140 write!(f, "{}", filter.event_type)
144 }
145 }
146 }
147 }
148
149 struct PrintMessageLikeEventFilter<'a>(&'a MessageLikeEventFilter);
150 impl fmt::Display for PrintMessageLikeEventFilter<'_> {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 match self.0 {
153 MessageLikeEventFilter::WithType(event_type) => {
154 write!(f, "{event_type}")
156 }
157 MessageLikeEventFilter::RoomMessageWithMsgtype(msgtype) => {
158 write!(f, "m.room.message#{msgtype}")
159 }
160 }
161 }
162 }
163
164 struct PrintStateEventFilter<'a>(&'a StateEventFilter);
165 impl fmt::Display for PrintStateEventFilter<'_> {
166 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167 match self.0 {
169 StateEventFilter::WithType(event_type) => write!(f, "{event_type}"),
170 StateEventFilter::WithTypeAndStateKey(event_type, state_key) => {
171 write!(f, "{event_type}#{state_key}")
172 }
173 }
174 }
175 }
176
177 let mut seq = serializer.serialize_seq(None)?;
178
179 if self.requires_client {
180 seq.serialize_element(REQUIRES_CLIENT)?;
181 }
182 if self.update_delayed_event {
183 seq.serialize_element(UPDATE_DELAYED_EVENT)?;
184 }
185 if self.send_delayed_event {
186 seq.serialize_element(SEND_DELAYED_EVENT)?;
187 }
188 if self.download_file {
189 seq.serialize_element(DOWNLOAD_FILE)?;
190 }
191 if self.rtc_transports {
192 seq.serialize_element(RTC_TRANSPORTS)?;
193 }
194 for filter in &self.read {
195 let name = match filter {
196 Filter::MessageLike(_) => READ_EVENT,
197 Filter::State(_) => READ_STATE,
198 Filter::ToDevice(_) => READ_TODEVICE,
199 };
200 seq.serialize_element(&format!("{name}:{}", PrintEventFilter(filter)))?;
201 }
202 for filter in &self.send {
203 let name = match filter {
204 Filter::MessageLike(_) => SEND_EVENT,
205 Filter::State(_) => SEND_STATE,
206 Filter::ToDevice(_) => SEND_TODEVICE,
207 };
208 seq.serialize_element(&format!("{name}:{}", PrintEventFilter(filter)))?;
209 }
210
211 seq.end()
212 }
213}
214
215impl<'de> Deserialize<'de> for Capabilities {
216 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
217 where
218 D: Deserializer<'de>,
219 {
220 enum Permission {
221 RequiresClient,
222 UpdateDelayedEvent,
223 SendDelayedEvent,
224 DownloadFile,
225 RtcTransports,
226 Read(Filter),
227 Send(Filter),
228 Unknown,
229 }
230
231 impl<'de> Deserialize<'de> for Permission {
232 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
233 where
234 D: Deserializer<'de>,
235 {
236 let s = ruma::serde::deserialize_cow_str(deserializer)?;
237 if s == REQUIRES_CLIENT {
238 return Ok(Self::RequiresClient);
239 }
240 if s == UPDATE_DELAYED_EVENT {
241 return Ok(Self::UpdateDelayedEvent);
242 }
243 if s == SEND_DELAYED_EVENT {
244 return Ok(Self::SendDelayedEvent);
245 }
246 if s == DOWNLOAD_FILE {
247 return Ok(Self::DownloadFile);
248 }
249 if s == RTC_TRANSPORTS {
250 return Ok(Self::RtcTransports);
251 }
252
253 match s.split_once(':') {
254 Some((READ_EVENT, filter_s)) => Ok(Permission::Read(Filter::MessageLike(
255 parse_message_event_filter(filter_s),
256 ))),
257 Some((SEND_EVENT, filter_s)) => Ok(Permission::Send(Filter::MessageLike(
258 parse_message_event_filter(filter_s),
259 ))),
260 Some((READ_STATE, filter_s)) => {
261 Ok(Permission::Read(Filter::State(parse_state_event_filter(filter_s))))
262 }
263 Some((SEND_STATE, filter_s)) => {
264 Ok(Permission::Send(Filter::State(parse_state_event_filter(filter_s))))
265 }
266 Some((READ_TODEVICE, filter_s)) => Ok(Permission::Read(Filter::ToDevice(
267 parse_to_device_event_filter(filter_s),
268 ))),
269 Some((SEND_TODEVICE, filter_s)) => Ok(Permission::Send(Filter::ToDevice(
270 parse_to_device_event_filter(filter_s),
271 ))),
272 _ => {
273 debug!("Unknown capability `{s}`");
274 Ok(Self::Unknown)
275 }
276 }
277 }
278 }
279
280 fn parse_message_event_filter(s: &str) -> MessageLikeEventFilter {
281 match s.strip_prefix("m.room.message#") {
282 Some(msgtype) => MessageLikeEventFilter::RoomMessageWithMsgtype(msgtype.to_owned()),
283 None => MessageLikeEventFilter::WithType(s.into()),
285 }
286 }
287
288 fn parse_state_event_filter(s: &str) -> StateEventFilter {
289 match s.split_once('#') {
291 Some((event_type, state_key)) => {
292 StateEventFilter::WithTypeAndStateKey(event_type.into(), state_key.to_owned())
293 }
294 None => StateEventFilter::WithType(s.into()),
295 }
296 }
297
298 fn parse_to_device_event_filter(s: &str) -> ToDeviceEventFilter {
299 ToDeviceEventFilter::new(s.into())
300 }
301
302 let mut capabilities = Capabilities::default();
303 for capability in Vec::<Permission>::deserialize(deserializer)? {
304 match capability {
305 Permission::RequiresClient => capabilities.requires_client = true,
306 Permission::Read(filter) => capabilities.read.push(filter),
307 Permission::Send(filter) => capabilities.send.push(filter),
308 Permission::Unknown => {}
310 Permission::UpdateDelayedEvent => capabilities.update_delayed_event = true,
311 Permission::SendDelayedEvent => capabilities.send_delayed_event = true,
312 Permission::DownloadFile => capabilities.download_file = true,
313 Permission::RtcTransports => capabilities.rtc_transports = true,
314 }
315 }
316
317 Ok(capabilities)
318 }
319}
320
321#[cfg(test)]
322mod tests {
323 use ruma::events::StateEventType;
324
325 use super::*;
326 use crate::widget::filter::ToDeviceEventFilter;
327
328 #[test]
329 fn deserialization_of_no_capabilities() {
330 let capabilities_str = r#"[]"#;
331
332 let parsed = serde_json::from_str::<Capabilities>(capabilities_str).unwrap();
333 let expected = Capabilities::default();
334
335 assert_eq!(parsed, expected);
336 }
337
338 #[test]
339 fn deserialization_of_capabilities() {
340 let capabilities_str = r#"[
341 "m.always_on_screen",
342 "io.element.requires_client",
343 "org.matrix.msc2762.receive.event:org.matrix.rageshake_request",
344 "org.matrix.msc2762.receive.state_event:m.room.member",
345 "org.matrix.msc2762.receive.state_event:org.matrix.msc3401.call.member",
346 "org.matrix.msc3819.receive.to_device:io.element.call.encryption_keys",
347 "org.matrix.msc2762.send.event:org.matrix.rageshake_request",
348 "org.matrix.msc2762.send.state_event:org.matrix.msc3401.call.member#@user:matrix.server",
349 "org.matrix.msc3819.send.to_device:io.element.call.encryption_keys",
350 "org.matrix.msc4157.send.delayed_event",
351 "org.matrix.msc4157.update_delayed_event",
352 "org.matrix.msc4039.download_file",
353 "org.matrix.msc4515.rtc_transports"
354 ]"#;
355
356 let parsed = serde_json::from_str::<Capabilities>(capabilities_str).unwrap();
357 let expected = Capabilities {
358 read: vec![
359 Filter::MessageLike(MessageLikeEventFilter::WithType(
360 "org.matrix.rageshake_request".into(),
361 )),
362 Filter::State(StateEventFilter::WithType(StateEventType::RoomMember)),
363 Filter::State(StateEventFilter::WithType("org.matrix.msc3401.call.member".into())),
364 Filter::ToDevice(ToDeviceEventFilter::new(
365 "io.element.call.encryption_keys".into(),
366 )),
367 ],
368 send: vec![
369 Filter::MessageLike(MessageLikeEventFilter::WithType(
370 "org.matrix.rageshake_request".into(),
371 )),
372 Filter::State(StateEventFilter::WithTypeAndStateKey(
373 "org.matrix.msc3401.call.member".into(),
374 "@user:matrix.server".into(),
375 )),
376 Filter::ToDevice(ToDeviceEventFilter::new(
377 "io.element.call.encryption_keys".into(),
378 )),
379 ],
380 requires_client: true,
381 update_delayed_event: true,
382 send_delayed_event: true,
383 download_file: true,
384 rtc_transports: true,
385 };
386
387 assert_eq!(parsed, expected);
388 }
389
390 #[test]
391 fn serialization_and_deserialization_are_symmetrical() {
392 let capabilities = Capabilities {
393 read: vec![
394 Filter::MessageLike(MessageLikeEventFilter::WithType("io.element.custom".into())),
395 Filter::State(StateEventFilter::WithType(StateEventType::RoomMember)),
396 Filter::State(StateEventFilter::WithTypeAndStateKey(
397 "org.matrix.msc3401.call.member".into(),
398 "@user:matrix.server".into(),
399 )),
400 Filter::ToDevice(ToDeviceEventFilter::new(
401 "io.element.call.encryption_keys".into(),
402 )),
403 ],
404 send: vec![
405 Filter::MessageLike(MessageLikeEventFilter::WithType("io.element.custom".into())),
406 Filter::State(StateEventFilter::WithTypeAndStateKey(
407 "org.matrix.msc3401.call.member".into(),
408 "@user:matrix.server".into(),
409 )),
410 Filter::ToDevice(ToDeviceEventFilter::new("my.org.other.to_device_event".into())),
411 ],
412 requires_client: true,
413 update_delayed_event: false,
414 send_delayed_event: false,
415 download_file: false,
416 rtc_transports: true,
417 };
418
419 let capabilities_str = serde_json::to_string(&capabilities).unwrap();
420 let parsed = serde_json::from_str::<Capabilities>(&capabilities_str).unwrap();
421 assert_eq!(parsed, capabilities);
422 }
423}