1mod profiles;
2#[cfg(test)]
3mod tests;
4mod v3;
5mod v5;
6
7use futures::{StreamExt, pin_mut};
8use ruma::{
9 EventId, OwnedUserId, RoomId, UserId,
10 events::{
11 AnyStrippedStateEvent, AnySyncStateEvent, StateEventType,
12 TimelineEventType::{RoomCreate, RoomMember},
13 room::member::MembershipState,
14 },
15 serde::Raw,
16 uint,
17};
18use tuwunel_core::{
19 Error, Result, debug_warn, is_equal_to,
20 utils::{
21 result::{LogErr, NotFound},
22 time::now_millis,
23 },
24 warn,
25};
26use tuwunel_matrix::{
27 Event, PduCount,
28 pdu::{EventHash, PduEvent},
29};
30use tuwunel_service::{Services, users::InviteFilter};
31
32pub use self::v3::calculate_heroes;
33pub(crate) use self::{v3::sync_events_route, v5::sync_events_v5_route};
34
35#[derive(Clone, Copy)]
36enum TimelineErrors {
37 Ignore,
38 Propagate,
39}
40
41async fn load_timeline(
42 services: &Services,
43 sender_user: &UserId,
44 room_id: &RoomId,
45 roomsincecount: PduCount,
46 next_batch: Option<PduCount>,
47 limit: usize,
48) -> Result<(Vec<(PduCount, PduEvent)>, bool, PduCount), Error> {
49 load_timeline_with_errors(
50 services,
51 sender_user,
52 room_id,
53 roomsincecount,
54 next_batch,
55 limit,
56 TimelineErrors::Ignore,
57 )
58 .await
59}
60
61async fn load_timeline_fallible(
62 services: &Services,
63 sender_user: &UserId,
64 room_id: &RoomId,
65 roomsincecount: PduCount,
66 next_batch: Option<PduCount>,
67 limit: usize,
68) -> Result<(Vec<(PduCount, PduEvent)>, bool, PduCount), Error> {
69 load_timeline_with_errors(
70 services,
71 sender_user,
72 room_id,
73 roomsincecount,
74 next_batch,
75 limit,
76 TimelineErrors::Propagate,
77 )
78 .await
79}
80
81async fn load_timeline_with_errors(
82 services: &Services,
83 sender_user: &UserId,
84 room_id: &RoomId,
85 roomsincecount: PduCount,
86 next_batch: Option<PduCount>,
87 limit: usize,
88 errors: TimelineErrors,
89) -> Result<(Vec<(PduCount, PduEvent)>, bool, PduCount), Error> {
90 let until = next_batch.map(|count| count.saturating_add(1));
91 let pdus = services
92 .timeline
93 .pdus_rev(Some(sender_user), room_id, until);
94
95 pin_mut!(pdus);
97 let mut timeline_pdus = Vec::new();
98 let mut last_timeline_count = PduCount::max();
99 let mut first = true;
100 let mut limited = false;
101
102 while let Some(pdu) = pdus.next().await {
103 let (pducount, pdu) = match pdu {
104 | Ok(pdu) => pdu,
105 | Err(error) if first || matches!(errors, TimelineErrors::Propagate) => {
106 return Err(error);
107 },
108 | Err(_) => continue,
109 };
110
111 if first {
112 first = false;
113 last_timeline_count = matches!(pducount, PduCount::Normal(_))
114 .then_some(pducount)
115 .unwrap_or_else(PduCount::max);
116 }
117
118 if pducount <= roomsincecount {
119 break;
120 }
121
122 if timeline_pdus.len() == limit {
123 limited = true;
124 break;
125 }
126
127 timeline_pdus.push((pducount, pdu));
128 }
129
130 timeline_pdus.reverse();
131
132 Ok((timeline_pdus, limited, last_timeline_count))
133}
134
135fn timeline_prev_batch(
141 timeline_pdus: &[(PduCount, PduEvent)],
142 window_end: PduCount,
143) -> Option<PduCount> {
144 timeline_pdus
145 .first()
146 .map(|(count, pdu)| match pdu.kind() {
147 | RoomCreate => window_end,
148 | _ => *count,
149 })
150}
151
152async fn share_encrypted_room(
153 services: &Services,
154 sender_user: &UserId,
155 user_id: &UserId,
156) -> bool {
157 services
158 .state_cache
159 .get_shared_rooms(sender_user, user_id)
160 .map(ToOwned::to_owned)
161 .any(async |room_id| {
162 services
163 .state_accessor
164 .is_encrypted_room(&room_id)
165 .await
166 })
167 .await
168}
169
170#[tracing::instrument(level = "trace", skip(services))]
176async fn joined_since(
177 services: &Services,
178 room_id: &RoomId,
179 user_id: &UserId,
180 since: u64,
181) -> bool {
182 services
183 .state_cache
184 .get_joined_count(room_id, user_id)
185 .await
186 .inspect_err(|error| debug_warn!(%room_id, %user_id, %error, "joined count unreadable"))
187 .is_ok_and(|recorded| recorded > since)
188}
189
190async fn invite_permitted_room(
196 services: &Services,
197 user_id: &UserId,
198 room_id: &RoomId,
199 filter: &InviteFilter,
200) -> bool {
201 filter.is_permissive()
202 || services
203 .state_cache
204 .invite_state(user_id, room_id)
205 .await
206 .map_or_else(
207 |error| {
208 debug_warn!(%user_id, %room_id, ?error, "invite state is unreadable; skipping the sender rules");
209 filter.permits(None)
210 },
211 |invite_state| invite_permitted(user_id, room_id, filter, &invite_state),
212 )
213}
214
215fn invite_permitted(
223 user_id: &UserId,
224 room_id: &RoomId,
225 filter: &InviteFilter,
226 invite_state: &[Raw<AnyStrippedStateEvent>],
227) -> bool {
228 if filter.is_permissive() {
230 return true;
231 }
232
233 let sender = invite_sender(user_id, invite_state);
234
235 if sender.is_none() {
236 debug_warn!(%user_id, %room_id, "invite state names no sender; skipping the sender rules");
237 }
238
239 filter.permits(sender.as_deref())
240}
241
242fn invite_sender(
252 user_id: &UserId,
253 invite_state: &[Raw<AnyStrippedStateEvent>],
254) -> Option<OwnedUserId> {
255 invite_state
256 .iter()
257 .rev()
258 .filter(|event| {
259 event
260 .get_field::<&str>("state_key")
261 .is_ok_and(|state_key| state_key.is_some_and(is_equal_to!(user_id.as_str())))
262 })
263 .filter_map(|event| event.deserialize().ok())
264 .find_map(|event| match event {
265 | AnyStrippedStateEvent::RoomMember(member) if member.state_key == user_id =>
266 Some(member.sender),
267 | _ => None,
268 })
269}
270
271fn strip_state_unsigned(
279 mut pdu: PduEvent,
280 sender_user: &UserId,
281 in_timeline: impl Fn(&PduEvent) -> bool,
282) -> PduEvent {
283 let own_membership =
284 *pdu.kind() == RoomMember && pdu.state_key() == Some(sender_user.as_str());
285
286 pdu.remove_private_unless_sender(Some(sender_user));
287
288 if !own_membership && !in_timeline(&pdu) {
289 pdu.remove_prev_state().log_err().ok();
290 }
291
292 pdu
293}
294
295async fn own_departure(
304 services: &Services,
305 room_id: &RoomId,
306 user_id: &UserId,
307) -> Option<PduEvent> {
308 services
309 .state_accessor
310 .room_state_get(room_id, &StateEventType::RoomMember, user_id.as_str())
311 .await
312 .optional()
313 .inspect_err(|error| warn!(%room_id, %user_id, %error, "Unreadable own departure event"))
314 .ok()
315 .flatten()
316 .filter(|event| {
317 matches!(
318 event.membership_for(user_id),
319 Some(MembershipState::Leave | MembershipState::Ban)
320 )
321 })
322 .map(|event| event.without_private_unless_sender(Some(user_id)))
323}
324
325fn stand_in_leave(
332 services: &Services,
333 room_id: &RoomId,
334 sender_user: &UserId,
335) -> Result<Raw<AnySyncStateEvent>> {
336 let event = PduEvent {
337 event_id: EventId::new_v1(services.globals.server_name()),
338 origin_server_ts: now_millis().try_into()?,
339 kind: RoomMember,
340 state_key: Some(sender_user.as_str().into()),
341 sender: sender_user.to_owned(),
342 content: serde_json::from_str(r#"{"membership":"leave"}"#)?,
343 room_id: room_id.to_owned(),
345 depth: uint!(1),
346 origin: None,
347 unsigned: None,
348 redacts: None,
349 hashes: EventHash::default(),
350 auth_events: Default::default(),
351 prev_events: Default::default(),
352 };
353
354 Ok(event.into_format())
355}