1use futures::{FutureExt, TryFutureExt, TryStreamExt, future::try_join5};
2use ruma::{
3 CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, RoomId, ServerName, UserId,
4 events::{
5 AnyStrippedStateEvent, StateEventType,
6 room::member::{MembershipState, RoomMemberEventContent},
7 },
8};
9use tuwunel_core::{
10 Err, Result, async_noinline, debug,
11 debug::INFO_SPAN_LEVEL,
12 debug_warn, err, implement,
13 matrix::{Event, PduCount, PduEvent, pdu::MAX_PREV_EVENTS, room_version::from_create_event},
14 smallvec::SmallVec,
15 trace,
16 utils::{
17 BoolExt,
18 future::ReadyEqExt,
19 stream::{IterStream, TryBroadbandExt, TryReadyExt},
20 },
21 warn,
22};
23
24use super::{
25 backoff::{Context, Disposition, Suppression, UPGRADE_RETRY},
26 fetch_prev::{Pdus, PrevFetch},
27 handle_prev_pdu::PrevUpgrade,
28 prev_walk::PrevWalk,
29 room_version_of,
30};
31use crate::rooms::{state_cache::MembershipUpdate, timeline::RawPduId};
32
33type PrevSplit = SmallVec<[OwnedEventId; MAX_PREV_EVENTS]>;
34
35type Handled = Option<(RawPduId, bool)>;
36
37#[implement(super::Service)]
65#[async_noinline]
67#[tracing::instrument(
68 name = "pdu",
69 level = INFO_SPAN_LEVEL,
70 skip_all,
71 fields(%room_id, %event_id),
72 ret(level = "debug"),
73)]
74pub async fn handle_incoming_pdu<'a>(
75 &'a self,
76 origin: &'a ServerName,
77 room_id: &'a RoomId,
78 event_id: &'a EventId,
79 pdu: CanonicalJsonObject,
80 is_timeline_event: bool,
81) -> Result<Handled> {
82 if let Ok(pdu_id) = self.services.timeline.get_pdu_id(event_id).await {
84 debug!(?pdu_id, "Exists.");
85 return Ok(Some((pdu_id, false)));
86 }
87
88 let meta_exists = self.services.metadata.exists(room_id).map(Ok);
90
91 let is_disabled = self
93 .services
94 .metadata
95 .is_disabled(room_id)
96 .map(Ok);
97
98 let origin_acl_check = self.acl_check(origin, room_id);
100
101 let sender: &UserId = pdu
103 .get("sender")
104 .try_into()
105 .map_err(|e| err!(Request(InvalidParam("PDU does not have a valid sender key: {e}"))))?;
106
107 let sender_acl_check = sender
108 .server_name()
109 .ne(origin)
110 .then_async(|| self.acl_check(sender.server_name(), room_id));
111
112 let create_event = self
114 .services
115 .state_accessor
116 .room_state_get(room_id, &StateEventType::RoomCreate, "")
117 .map(|result| Ok(result.ok()));
118
119 let (meta_exists, is_disabled, (), (), create_event) = try_join5(
120 meta_exists,
121 is_disabled,
122 origin_acl_check,
123 sender_acl_check.map(|o| o.unwrap_or(Ok(()))),
124 create_event,
125 )
126 .await?;
127
128 if !meta_exists {
131 return if self
132 .handle_rescinded_invite(room_id, &pdu)
133 .await?
134 {
135 Ok(None)
136 } else {
137 Err!(Request(NotFound("Room is unknown to this server")))
138 };
139 }
140
141 if is_disabled {
142 return Err!(Request(Forbidden("Federation of this room is disabled by this server.")));
143 }
144
145 let create_event =
146 create_event.ok_or_else(|| err!(Request(NotFound("Room is unknown to this server"))))?;
147
148 let room_version = from_create_event(&create_event)?;
149 let recursion_level = 0;
150
151 let (incoming_pdu, pdu) = self
152 .handle_outlier_pdu(origin, room_id, event_id, pdu, &room_version, recursion_level, false)
153 .await?;
154
155 if !is_timeline_event {
157 debug!(
158 kind = ?incoming_pdu.event_type(),
159 "Not a timeline event.",
160 );
161
162 return Ok(None);
163 }
164
165 let first_ts_in_room = self
167 .services
168 .timeline
169 .first_pdu_in_room(room_id)
170 .await?
171 .origin_server_ts();
172
173 if incoming_pdu.origin_server_ts() < first_ts_in_room {
174 debug!(
175 origin_server_ts = ?incoming_pdu.origin_server_ts(),
176 ?first_ts_in_room,
177 "Skipping old event."
178 );
179 return Ok(None);
180 }
181
182 let gapped = self
183 .services
184 .timeline
185 .non_outlier_pdus_exist(incoming_pdu.prev_events())
186 .await
187 .is_false();
188
189 self.prev_walk.enter(gapped);
190
191 let create_event_id = create_event.event_id();
192 let upgrade = PrevUpgrade {
193 origin,
194 room_id,
195 event_id,
196 room_version: &room_version,
197 recursion_level,
198 first_ts_in_room,
199 create_event_id,
200 };
201
202 let pass = gapped.then(|| PrevWalk::start(self, &upgrade));
204
205 let standing = gapped
206 .then_async(|| self.is_suppressed(Context::Incoming, event_id, UPGRADE_RETRY))
207 .await
208 .unwrap_or(Suppression::Absent);
209
210 if standing.is_deny() {
211 if let Some(pass) = pass {
212 pass.hold();
213 }
214
215 debug_warn!(%origin, %room_id, %event_id, "Backing off from a gapped incoming event.");
216 return Ok(None);
217 }
218
219 let fetch = self
222 .fetch_prev(upgrade, incoming_pdu.prev_events())
223 .await;
224
225 let stopping = self.services.server.is_stopping();
226 let walk = pass.and_then(|pass| pass.fetched(fetch.as_ref(), stopping));
227 let fetch = fetch?;
228
229 let walking = fetch.sorted.is_empty().is_false();
230 let attempt = walking.then(|| self.record_attempt(Context::Incoming, event_id));
231 let PrevFetch { sorted, pdus, .. } = fetch;
232
233 let (handled, upgraded) = self
234 .handle_prev_events(upgrade, sorted, pdus)
235 .boxed() .and_then(|upgraded| {
237 self.upgrade_outlier_to_timeline_pdu(upgrade, incoming_pdu, pdu)
238 .boxed() .map(move |handled| Ok((handled, upgraded)))
240 })
241 .unwrap_or_else(|error| (Err(error), 0))
242 .await;
243
244 let appended = handled.as_ref().map(Option::is_some);
245
246 if let Some(walk) = walk {
247 walk.settle(appended, upgraded, self.services.server.is_stopping());
248 }
249
250 self.record_completion(Context::Incoming, event_id, standing, attempt, appended)
251 .await;
252
253 handled
254}
255
256#[implement(super::Service)]
266#[tracing::instrument(skip_all, level = "debug", fields(%room_id))]
267async fn handle_rescinded_invite(
268 &self,
269 room_id: &RoomId,
270 pdu: &CanonicalJsonObject,
271) -> Result<bool> {
272 if pdu
273 .get("type")
274 .and_then(CanonicalJsonValue::as_str)
275 != Some("m.room.member")
276 {
277 return Ok(false);
278 }
279
280 let Some(target) = pdu
281 .get("state_key")
282 .and_then(CanonicalJsonValue::as_str)
283 .and_then(|state_key| UserId::parse(state_key).ok())
284 else {
285 return Ok(false);
286 };
287
288 let Some(sender) = pdu
289 .get("sender")
290 .and_then(CanonicalJsonValue::as_str)
291 .and_then(|sender| UserId::parse(sender).ok())
292 else {
293 return Ok(false);
294 };
295
296 if sender == target || !self.services.globals.user_is_local(&target) {
297 return Ok(false);
298 }
299
300 let Some(content) = pdu
301 .get("content")
302 .cloned()
303 .map(Into::into)
304 .and_then(|content| serde_json::from_value::<RoomMemberEventContent>(content).ok())
305 else {
306 return Ok(false);
307 };
308
309 if content.membership != MembershipState::Leave {
310 return Ok(false);
311 }
312
313 if self
314 .services
315 .state_cache
316 .user_membership(&target, room_id)
317 .ne(&Some(MembershipState::Invite))
318 .await
319 {
320 return Ok(false);
321 }
322
323 let invite_state = self
325 .services
326 .state_cache
327 .invite_state(&target, room_id)
328 .await?;
329
330 let inviter = invite_state
331 .iter()
332 .find_map(|event| match event.deserialize() {
333 | Ok(AnyStrippedStateEvent::RoomMember(member)) if member.state_key == target =>
334 Some(member.sender),
335 | _ => None,
336 });
337
338 if inviter.as_ref() != Some(&sender) {
340 return Ok(false);
341 }
342
343 let Some(room_version_id) = room_version_of(&invite_state) else {
344 return Ok(false);
345 };
346
347 self.services
349 .server_keys
350 .verify_event(pdu, Some(&room_version_id))
351 .await
352 .map_err(|e| {
353 err!(Request(InvalidParam("Invite rescission signature is invalid: {e}")))
354 })?;
355
356 let count = self.services.globals.next_count();
357 self.services
358 .state_cache
359 .update_membership(MembershipUpdate {
360 room_id,
361 user_id: &target,
362 membership_event: RoomMemberEventContent::new(MembershipState::Leave),
363 sender: &sender,
364 last_state: None,
365 invite_via: None,
366 update_joined_count: false,
367 count: PduCount::Normal(*count),
368 })
369 .await?;
370
371 debug!(%room_id, %target, %sender, "Applied a federated invite rescission.");
372
373 Ok(true)
374}
375
376#[implement(super::Service)]
382async fn handle_prev_events(
383 &self,
384 upgrade: PrevUpgrade<'_>,
385 sorted_prev_events: Vec<OwnedEventId>,
386 mut pdus: Pdus, ) -> Result<usize> {
388 trace!(
389 events = sorted_prev_events.len(),
390 event_ids = ?sorted_prev_events,
391 "Handling previous events"
392 );
393
394 let (interior, extremities): (PrevSplit, PrevSplit) = sorted_prev_events
395 .into_iter()
396 .partition(|prev_id| {
397 pdus.get(prev_id).is_some_and(|(pdu, _)| {
398 pdu.prev_events()
399 .any(|prev| pdus.contains_key(prev))
400 })
401 });
402
403 let concurrency = usize::from(
404 self.services
405 .server
406 .config
407 .prev_events_concurrency,
408 );
409
410 let upgraded = extremities
411 .into_iter()
412 .try_stream()
413 .map_ok(|prev_id| (pdus.remove(&prev_id), prev_id))
414 .broadn_and_then(concurrency, async |(info, prev_id)| {
415 self.upgrade_prev_event(upgrade, info, &prev_id)
416 .await
417 })
418 .ready_try_fold(0, tally_upgraded)
419 .boxed() .await?;
421
422 interior
424 .into_iter()
425 .try_stream()
426 .map_ok(|prev_id| (pdus.remove(&prev_id), prev_id))
427 .and_then(async |(info, prev_id)| {
428 self.upgrade_prev_event(upgrade, info, &prev_id)
429 .await
430 })
431 .ready_try_fold(upgraded, tally_upgraded)
432 .boxed() .await
434}
435
436#[implement(super::Service)]
443async fn upgrade_prev_event(
444 &self,
445 upgrade: PrevUpgrade<'_>,
446 info: Option<(PduEvent, CanonicalJsonObject)>,
447 prev_id: &EventId,
448) -> Result<Handled> {
449 self.services.server.check_running()?;
450
451 let PrevUpgrade { room_id, event_id, .. } = upgrade;
452
453 match self.handle_prev_pdu(upgrade, info, prev_id).await {
454 | Err(error) if error.is_interrupted() || self.services.server.is_stopping() => {
455 debug!(?prev_id, ?event_id, ?room_id, %error, "Prev event processing interrupted.");
456
457 Ok(None)
458 },
459 | Err(error) => {
460 self.record_outcome(Context::Upgrade, prev_id, Disposition::Transient);
461 warn!(?prev_id, ?event_id, ?room_id, %error, "Prev event processing failed.");
462
463 Ok(None)
464 },
465 | Ok(None) => {
466 debug_warn!(?prev_id, "Prev event not processed.");
467
468 Ok(None)
469 },
470 | Ok(handled) => {
471 self.record_success(Context::Upgrade, prev_id)
472 .await;
473
474 debug!(?prev_id, ?handled, "Prev event processed.");
475
476 Ok(handled)
477 },
478 }
479}
480
481fn tally_upgraded(upgraded: usize, handled: Handled) -> Result<usize> {
482 Ok(upgraded.saturating_add(usize::from(handled.is_some())))
483}