|
|
|
|
@ -653,213 +653,133 @@ async fn load_joined_room(
|
|
|
|
|
.user |
|
|
|
|
.get_token_shortstatehash(&room_id, since)?; |
|
|
|
|
|
|
|
|
|
// Calculates joined_member_count, invited_member_count and heroes
|
|
|
|
|
let calculate_counts = || { |
|
|
|
|
let joined_member_count = services() |
|
|
|
|
.rooms |
|
|
|
|
.state_cache |
|
|
|
|
.room_joined_count(&room_id)? |
|
|
|
|
.unwrap_or(0); |
|
|
|
|
let invited_member_count = services() |
|
|
|
|
.rooms |
|
|
|
|
.state_cache |
|
|
|
|
.room_invited_count(&room_id)? |
|
|
|
|
.unwrap_or(0); |
|
|
|
|
|
|
|
|
|
// Recalculate heroes (first 5 members)
|
|
|
|
|
let mut heroes = Vec::new(); |
|
|
|
|
|
|
|
|
|
if joined_member_count + invited_member_count <= 5 { |
|
|
|
|
// Go through all PDUs and for each member event, check if the user is still joined or
|
|
|
|
|
// invited until we have 5 or we reach the end
|
|
|
|
|
|
|
|
|
|
for hero in services() |
|
|
|
|
.rooms |
|
|
|
|
.timeline |
|
|
|
|
.all_pdus(&sender_user, &room_id)? |
|
|
|
|
.filter_map(|pdu| pdu.ok()) // Ignore all broken pdus
|
|
|
|
|
.filter(|(_, pdu)| pdu.kind == TimelineEventType::RoomMember) |
|
|
|
|
.map(|(_, pdu)| { |
|
|
|
|
let content: RoomMemberEventContent = serde_json::from_str(pdu.content.get()) |
|
|
|
|
.map_err(|_| { |
|
|
|
|
Error::bad_database("Invalid member event in database.") |
|
|
|
|
})?; |
|
|
|
|
|
|
|
|
|
if let Some(state_key) = &pdu.state_key { |
|
|
|
|
let user_id = UserId::parse(state_key.clone()) |
|
|
|
|
.map_err(|_| Error::bad_database("Invalid UserId in member PDU."))?; |
|
|
|
|
|
|
|
|
|
// The membership was and still is invite or join
|
|
|
|
|
if matches!( |
|
|
|
|
content.membership, |
|
|
|
|
MembershipState::Join | MembershipState::Invite |
|
|
|
|
) && (services().rooms.state_cache.is_joined(&user_id, &room_id)? |
|
|
|
|
|| services() |
|
|
|
|
.rooms |
|
|
|
|
.state_cache |
|
|
|
|
.is_invited(&user_id, &room_id)?) |
|
|
|
|
{ |
|
|
|
|
Ok::<_, Error>(Some(state_key.clone())) |
|
|
|
|
} else { |
|
|
|
|
Ok(None) |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
Ok(None) |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
// Filter out buggy users
|
|
|
|
|
.filter_map(|u| u.ok()) |
|
|
|
|
// Filter for possible heroes
|
|
|
|
|
.flatten() |
|
|
|
|
{ |
|
|
|
|
if heroes.contains(&hero) || hero == sender_user.as_str() { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
heroes.push(hero); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Ok::<_, Error>(( |
|
|
|
|
Some(joined_member_count), |
|
|
|
|
Some(invited_member_count), |
|
|
|
|
heroes, |
|
|
|
|
)) |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let since_sender_member: Option<RoomMemberEventContent> = since_shortstatehash |
|
|
|
|
.and_then(|shortstatehash| { |
|
|
|
|
services() |
|
|
|
|
.rooms |
|
|
|
|
.state_accessor |
|
|
|
|
.state_get( |
|
|
|
|
shortstatehash, |
|
|
|
|
&StateEventType::RoomMember, |
|
|
|
|
sender_user.as_str(), |
|
|
|
|
) |
|
|
|
|
.transpose() |
|
|
|
|
}) |
|
|
|
|
.transpose()? |
|
|
|
|
.and_then(|pdu| { |
|
|
|
|
serde_json::from_str(pdu.content.get()) |
|
|
|
|
.map_err(|_| Error::bad_database("Invalid PDU in database.")) |
|
|
|
|
.ok() |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
let joined_since_last_sync = |
|
|
|
|
since_sender_member.map_or(true, |member| member.membership != MembershipState::Join); |
|
|
|
|
|
|
|
|
|
let (heroes, joined_member_count, invited_member_count, joined_since_last_sync, state_events) = |
|
|
|
|
if since_shortstatehash.is_none() || joined_since_last_sync { |
|
|
|
|
// Probably since = 0, we will do an initial sync
|
|
|
|
|
|
|
|
|
|
let (joined_member_count, invited_member_count, heroes) = calculate_counts()?; |
|
|
|
|
|
|
|
|
|
let current_state_ids = services() |
|
|
|
|
.rooms |
|
|
|
|
.state_accessor |
|
|
|
|
.state_full_ids(current_shortstatehash) |
|
|
|
|
.await?; |
|
|
|
|
if timeline_pdus.is_empty() && since_shortstatehash == Some(current_shortstatehash) { |
|
|
|
|
// No state changes
|
|
|
|
|
(Vec::new(), None, None, false, Vec::new()) |
|
|
|
|
} else { |
|
|
|
|
// Calculates joined_member_count, invited_member_count and heroes
|
|
|
|
|
let calculate_counts = || { |
|
|
|
|
let joined_member_count = services() |
|
|
|
|
.rooms |
|
|
|
|
.state_cache |
|
|
|
|
.room_joined_count(&room_id)? |
|
|
|
|
.unwrap_or(0); |
|
|
|
|
let invited_member_count = services() |
|
|
|
|
.rooms |
|
|
|
|
.state_cache |
|
|
|
|
.room_invited_count(&room_id)? |
|
|
|
|
.unwrap_or(0); |
|
|
|
|
|
|
|
|
|
let mut state_events = Vec::new(); |
|
|
|
|
let mut lazy_loaded = HashSet::new(); |
|
|
|
|
// Recalculate heroes (first 5 members)
|
|
|
|
|
let mut heroes = Vec::new(); |
|
|
|
|
|
|
|
|
|
let mut i = 0; |
|
|
|
|
for (shortstatekey, id) in current_state_ids { |
|
|
|
|
let (event_type, state_key) = services() |
|
|
|
|
.rooms |
|
|
|
|
.short |
|
|
|
|
.get_statekey_from_short(shortstatekey)?; |
|
|
|
|
if joined_member_count + invited_member_count <= 5 { |
|
|
|
|
// Go through all PDUs and for each member event, check if the user is still joined or
|
|
|
|
|
// invited until we have 5 or we reach the end
|
|
|
|
|
|
|
|
|
|
if event_type != StateEventType::RoomMember { |
|
|
|
|
let pdu = match services().rooms.timeline.get_pdu(&id)? { |
|
|
|
|
Some(pdu) => pdu, |
|
|
|
|
None => { |
|
|
|
|
error!("Pdu in state not found: {}", id); |
|
|
|
|
for hero in services() |
|
|
|
|
.rooms |
|
|
|
|
.timeline |
|
|
|
|
.all_pdus(&sender_user, &room_id)? |
|
|
|
|
.filter_map(|pdu| pdu.ok()) // Ignore all broken pdus
|
|
|
|
|
.filter(|(_, pdu)| pdu.kind == TimelineEventType::RoomMember) |
|
|
|
|
.map(|(_, pdu)| { |
|
|
|
|
let content: RoomMemberEventContent = |
|
|
|
|
serde_json::from_str(pdu.content.get()).map_err(|_| { |
|
|
|
|
Error::bad_database("Invalid member event in database.") |
|
|
|
|
})?; |
|
|
|
|
|
|
|
|
|
if let Some(state_key) = &pdu.state_key { |
|
|
|
|
let user_id = UserId::parse(state_key.clone()).map_err(|_| { |
|
|
|
|
Error::bad_database("Invalid UserId in member PDU.") |
|
|
|
|
})?; |
|
|
|
|
|
|
|
|
|
// The membership was and still is invite or join
|
|
|
|
|
if matches!( |
|
|
|
|
content.membership, |
|
|
|
|
MembershipState::Join | MembershipState::Invite |
|
|
|
|
) && (services() |
|
|
|
|
.rooms |
|
|
|
|
.state_cache |
|
|
|
|
.is_joined(&user_id, &room_id)? |
|
|
|
|
|| services() |
|
|
|
|
.rooms |
|
|
|
|
.state_cache |
|
|
|
|
.is_invited(&user_id, &room_id)?) |
|
|
|
|
{ |
|
|
|
|
Ok::<_, Error>(Some(state_key.clone())) |
|
|
|
|
} else { |
|
|
|
|
Ok(None) |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
Ok(None) |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
// Filter out buggy users
|
|
|
|
|
.filter_map(|u| u.ok()) |
|
|
|
|
// Filter for possible heroes
|
|
|
|
|
.flatten() |
|
|
|
|
{ |
|
|
|
|
if heroes.contains(&hero) || hero == sender_user.as_str() { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
state_events.push(pdu); |
|
|
|
|
|
|
|
|
|
i += 1; |
|
|
|
|
if i % 100 == 0 { |
|
|
|
|
tokio::task::yield_now().await; |
|
|
|
|
heroes.push(hero); |
|
|
|
|
} |
|
|
|
|
} else if !lazy_load_enabled |
|
|
|
|
|| full_state |
|
|
|
|
|| timeline_users.contains(&state_key) |
|
|
|
|
// TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565
|
|
|
|
|
|| *sender_user == state_key |
|
|
|
|
{ |
|
|
|
|
let pdu = match services().rooms.timeline.get_pdu(&id)? { |
|
|
|
|
Some(pdu) => pdu, |
|
|
|
|
None => { |
|
|
|
|
error!("Pdu in state not found: {}", id); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// This check is in case a bad user ID made it into the database
|
|
|
|
|
if let Ok(uid) = UserId::parse(&state_key) { |
|
|
|
|
lazy_loaded.insert(uid); |
|
|
|
|
} |
|
|
|
|
state_events.push(pdu); |
|
|
|
|
Ok::<_, Error>(( |
|
|
|
|
Some(joined_member_count), |
|
|
|
|
Some(invited_member_count), |
|
|
|
|
heroes, |
|
|
|
|
)) |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
i += 1; |
|
|
|
|
if i % 100 == 0 { |
|
|
|
|
tokio::task::yield_now().await; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
let since_sender_member: Option<RoomMemberEventContent> = since_shortstatehash |
|
|
|
|
.and_then(|shortstatehash| { |
|
|
|
|
services() |
|
|
|
|
.rooms |
|
|
|
|
.state_accessor |
|
|
|
|
.state_get( |
|
|
|
|
shortstatehash, |
|
|
|
|
&StateEventType::RoomMember, |
|
|
|
|
sender_user.as_str(), |
|
|
|
|
) |
|
|
|
|
.transpose() |
|
|
|
|
}) |
|
|
|
|
.transpose()? |
|
|
|
|
.and_then(|pdu| { |
|
|
|
|
serde_json::from_str(pdu.content.get()) |
|
|
|
|
.map_err(|_| Error::bad_database("Invalid PDU in database.")) |
|
|
|
|
.ok() |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
// Reset lazy loading because this is an initial sync
|
|
|
|
|
services().rooms.lazy_loading.lazy_load_reset( |
|
|
|
|
&sender_user, |
|
|
|
|
&sender_device, |
|
|
|
|
&room_id, |
|
|
|
|
)?; |
|
|
|
|
|
|
|
|
|
// The state_events above should contain all timeline_users, let's mark them as lazy
|
|
|
|
|
// loaded.
|
|
|
|
|
services().rooms.lazy_loading.lazy_load_mark_sent( |
|
|
|
|
&sender_user, |
|
|
|
|
&sender_device, |
|
|
|
|
&room_id, |
|
|
|
|
lazy_loaded, |
|
|
|
|
next_batchcount, |
|
|
|
|
); |
|
|
|
|
let joined_since_last_sync = since_sender_member |
|
|
|
|
.map_or(true, |member| member.membership != MembershipState::Join); |
|
|
|
|
|
|
|
|
|
( |
|
|
|
|
heroes, |
|
|
|
|
joined_member_count, |
|
|
|
|
invited_member_count, |
|
|
|
|
true, |
|
|
|
|
state_events, |
|
|
|
|
) |
|
|
|
|
} else if timeline_pdus.is_empty() && since_shortstatehash == Some(current_shortstatehash) { |
|
|
|
|
// No state changes
|
|
|
|
|
(Vec::new(), None, None, false, Vec::new()) |
|
|
|
|
} else { |
|
|
|
|
// Incremental /sync
|
|
|
|
|
let since_shortstatehash = since_shortstatehash.unwrap(); |
|
|
|
|
if since_shortstatehash.is_none() || joined_since_last_sync { |
|
|
|
|
// Probably since = 0, we will do an initial sync
|
|
|
|
|
|
|
|
|
|
let mut state_events = Vec::new(); |
|
|
|
|
let mut lazy_loaded = HashSet::new(); |
|
|
|
|
let (joined_member_count, invited_member_count, heroes) = calculate_counts()?; |
|
|
|
|
|
|
|
|
|
if since_shortstatehash != current_shortstatehash { |
|
|
|
|
let current_state_ids = services() |
|
|
|
|
.rooms |
|
|
|
|
.state_accessor |
|
|
|
|
.state_full_ids(current_shortstatehash) |
|
|
|
|
.await?; |
|
|
|
|
let since_state_ids = services() |
|
|
|
|
.rooms |
|
|
|
|
.state_accessor |
|
|
|
|
.state_full_ids(since_shortstatehash) |
|
|
|
|
.await?; |
|
|
|
|
|
|
|
|
|
for (key, id) in current_state_ids { |
|
|
|
|
if full_state || since_state_ids.get(&key) != Some(&id) { |
|
|
|
|
let mut state_events = Vec::new(); |
|
|
|
|
let mut lazy_loaded = HashSet::new(); |
|
|
|
|
|
|
|
|
|
let mut i = 0; |
|
|
|
|
for (shortstatekey, id) in current_state_ids { |
|
|
|
|
let (event_type, state_key) = services() |
|
|
|
|
.rooms |
|
|
|
|
.short |
|
|
|
|
.get_statekey_from_short(shortstatekey)?; |
|
|
|
|
|
|
|
|
|
if event_type != StateEventType::RoomMember { |
|
|
|
|
let pdu = match services().rooms.timeline.get_pdu(&id)? { |
|
|
|
|
Some(pdu) => pdu, |
|
|
|
|
None => { |
|
|
|
|
@ -867,146 +787,234 @@ async fn load_joined_room(
|
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
state_events.push(pdu); |
|
|
|
|
|
|
|
|
|
if pdu.kind == TimelineEventType::RoomMember { |
|
|
|
|
match UserId::parse( |
|
|
|
|
pdu.state_key |
|
|
|
|
.as_ref() |
|
|
|
|
.expect("State event has state key") |
|
|
|
|
.clone(), |
|
|
|
|
) { |
|
|
|
|
Ok(state_key_userid) => { |
|
|
|
|
lazy_loaded.insert(state_key_userid); |
|
|
|
|
} |
|
|
|
|
Err(e) => error!("Invalid state key for member event: {}", e), |
|
|
|
|
} |
|
|
|
|
i += 1; |
|
|
|
|
if i % 100 == 0 { |
|
|
|
|
tokio::task::yield_now().await; |
|
|
|
|
} |
|
|
|
|
} else if !lazy_load_enabled |
|
|
|
|
|| full_state |
|
|
|
|
|| timeline_users.contains(&state_key) |
|
|
|
|
// TODO: Delete the following line when this is resolved: https://github.com/vector-im/element-web/issues/22565
|
|
|
|
|
|| *sender_user == state_key |
|
|
|
|
{ |
|
|
|
|
let pdu = match services().rooms.timeline.get_pdu(&id)? { |
|
|
|
|
Some(pdu) => pdu, |
|
|
|
|
None => { |
|
|
|
|
error!("Pdu in state not found: {}", id); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// This check is in case a bad user ID made it into the database
|
|
|
|
|
if let Ok(uid) = UserId::parse(&state_key) { |
|
|
|
|
lazy_loaded.insert(uid); |
|
|
|
|
} |
|
|
|
|
state_events.push(pdu); |
|
|
|
|
tokio::task::yield_now().await; |
|
|
|
|
|
|
|
|
|
i += 1; |
|
|
|
|
if i % 100 == 0 { |
|
|
|
|
tokio::task::yield_now().await; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (_, event) in &timeline_pdus { |
|
|
|
|
if lazy_loaded.contains(&event.sender) { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
// Reset lazy loading because this is an initial sync
|
|
|
|
|
services().rooms.lazy_loading.lazy_load_reset( |
|
|
|
|
&sender_user, |
|
|
|
|
&sender_device, |
|
|
|
|
&room_id, |
|
|
|
|
)?; |
|
|
|
|
|
|
|
|
|
if !services().rooms.lazy_loading.lazy_load_was_sent_before( |
|
|
|
|
// The state_events above should contain all timeline_users, let's mark them as lazy
|
|
|
|
|
// loaded.
|
|
|
|
|
services().rooms.lazy_loading.lazy_load_mark_sent( |
|
|
|
|
&sender_user, |
|
|
|
|
&sender_device, |
|
|
|
|
&room_id, |
|
|
|
|
&event.sender, |
|
|
|
|
)? || lazy_load_send_redundant |
|
|
|
|
{ |
|
|
|
|
if let Some(member_event) = services().rooms.state_accessor.room_state_get( |
|
|
|
|
&room_id, |
|
|
|
|
&StateEventType::RoomMember, |
|
|
|
|
event.sender.as_str(), |
|
|
|
|
)? { |
|
|
|
|
lazy_loaded.insert(event.sender.clone()); |
|
|
|
|
state_events.push(member_event); |
|
|
|
|
lazy_loaded, |
|
|
|
|
next_batchcount, |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
( |
|
|
|
|
heroes, |
|
|
|
|
joined_member_count, |
|
|
|
|
invited_member_count, |
|
|
|
|
true, |
|
|
|
|
state_events, |
|
|
|
|
) |
|
|
|
|
} else { |
|
|
|
|
// Incremental /sync
|
|
|
|
|
let since_shortstatehash = since_shortstatehash.unwrap(); |
|
|
|
|
|
|
|
|
|
let mut state_events = Vec::new(); |
|
|
|
|
let mut lazy_loaded = HashSet::new(); |
|
|
|
|
|
|
|
|
|
if since_shortstatehash != current_shortstatehash { |
|
|
|
|
let current_state_ids = services() |
|
|
|
|
.rooms |
|
|
|
|
.state_accessor |
|
|
|
|
.state_full_ids(current_shortstatehash) |
|
|
|
|
.await?; |
|
|
|
|
let since_state_ids = services() |
|
|
|
|
.rooms |
|
|
|
|
.state_accessor |
|
|
|
|
.state_full_ids(since_shortstatehash) |
|
|
|
|
.await?; |
|
|
|
|
|
|
|
|
|
for (key, id) in current_state_ids { |
|
|
|
|
if full_state || since_state_ids.get(&key) != Some(&id) { |
|
|
|
|
let pdu = match services().rooms.timeline.get_pdu(&id)? { |
|
|
|
|
Some(pdu) => pdu, |
|
|
|
|
None => { |
|
|
|
|
error!("Pdu in state not found: {}", id); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
if pdu.kind == TimelineEventType::RoomMember { |
|
|
|
|
match UserId::parse( |
|
|
|
|
pdu.state_key |
|
|
|
|
.as_ref() |
|
|
|
|
.expect("State event has state key") |
|
|
|
|
.clone(), |
|
|
|
|
) { |
|
|
|
|
Ok(state_key_userid) => { |
|
|
|
|
lazy_loaded.insert(state_key_userid); |
|
|
|
|
} |
|
|
|
|
Err(e) => error!("Invalid state key for member event: {}", e), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
state_events.push(pdu); |
|
|
|
|
tokio::task::yield_now().await; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
services().rooms.lazy_loading.lazy_load_mark_sent( |
|
|
|
|
&sender_user, |
|
|
|
|
&sender_device, |
|
|
|
|
&room_id, |
|
|
|
|
lazy_loaded, |
|
|
|
|
next_batchcount, |
|
|
|
|
); |
|
|
|
|
for (_, event) in &timeline_pdus { |
|
|
|
|
if lazy_loaded.contains(&event.sender) { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let encrypted_room = services() |
|
|
|
|
.rooms |
|
|
|
|
.state_accessor |
|
|
|
|
.state_get(current_shortstatehash, &StateEventType::RoomEncryption, "")? |
|
|
|
|
.is_some(); |
|
|
|
|
if !services().rooms.lazy_loading.lazy_load_was_sent_before( |
|
|
|
|
&sender_user, |
|
|
|
|
&sender_device, |
|
|
|
|
&room_id, |
|
|
|
|
&event.sender, |
|
|
|
|
)? || lazy_load_send_redundant |
|
|
|
|
{ |
|
|
|
|
if let Some(member_event) = services().rooms.state_accessor.room_state_get( |
|
|
|
|
&room_id, |
|
|
|
|
&StateEventType::RoomMember, |
|
|
|
|
event.sender.as_str(), |
|
|
|
|
)? { |
|
|
|
|
lazy_loaded.insert(event.sender.clone()); |
|
|
|
|
state_events.push(member_event); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let since_encryption = services().rooms.state_accessor.state_get( |
|
|
|
|
since_shortstatehash, |
|
|
|
|
&StateEventType::RoomEncryption, |
|
|
|
|
"", |
|
|
|
|
)?; |
|
|
|
|
services().rooms.lazy_loading.lazy_load_mark_sent( |
|
|
|
|
&sender_user, |
|
|
|
|
&sender_device, |
|
|
|
|
&room_id, |
|
|
|
|
lazy_loaded, |
|
|
|
|
next_batchcount, |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
// Calculations:
|
|
|
|
|
let new_encrypted_room = encrypted_room && since_encryption.is_none(); |
|
|
|
|
let encrypted_room = services() |
|
|
|
|
.rooms |
|
|
|
|
.state_accessor |
|
|
|
|
.state_get(current_shortstatehash, &StateEventType::RoomEncryption, "")? |
|
|
|
|
.is_some(); |
|
|
|
|
|
|
|
|
|
let send_member_count = state_events |
|
|
|
|
.iter() |
|
|
|
|
.any(|event| event.kind == TimelineEventType::RoomMember); |
|
|
|
|
let since_encryption = services().rooms.state_accessor.state_get( |
|
|
|
|
since_shortstatehash, |
|
|
|
|
&StateEventType::RoomEncryption, |
|
|
|
|
"", |
|
|
|
|
)?; |
|
|
|
|
|
|
|
|
|
if encrypted_room { |
|
|
|
|
for state_event in &state_events { |
|
|
|
|
if state_event.kind != TimelineEventType::RoomMember { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
// Calculations:
|
|
|
|
|
let new_encrypted_room = encrypted_room && since_encryption.is_none(); |
|
|
|
|
|
|
|
|
|
if let Some(state_key) = &state_event.state_key { |
|
|
|
|
let user_id = UserId::parse(state_key.clone()) |
|
|
|
|
.map_err(|_| Error::bad_database("Invalid UserId in member PDU."))?; |
|
|
|
|
let send_member_count = state_events |
|
|
|
|
.iter() |
|
|
|
|
.any(|event| event.kind == TimelineEventType::RoomMember); |
|
|
|
|
|
|
|
|
|
if user_id == sender_user { |
|
|
|
|
if encrypted_room { |
|
|
|
|
for state_event in &state_events { |
|
|
|
|
if state_event.kind != TimelineEventType::RoomMember { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let new_membership = serde_json::from_str::<RoomMemberEventContent>( |
|
|
|
|
state_event.content.get(), |
|
|
|
|
) |
|
|
|
|
.map_err(|_| Error::bad_database("Invalid PDU in database."))? |
|
|
|
|
.membership; |
|
|
|
|
|
|
|
|
|
match new_membership { |
|
|
|
|
MembershipState::Join => { |
|
|
|
|
// A new user joined an encrypted room
|
|
|
|
|
if !share_encrypted_room(&sender_user, &user_id, &room_id)? { |
|
|
|
|
device_list_updates.insert(user_id); |
|
|
|
|
} |
|
|
|
|
if let Some(state_key) = &state_event.state_key { |
|
|
|
|
let user_id = UserId::parse(state_key.clone()).map_err(|_| { |
|
|
|
|
Error::bad_database("Invalid UserId in member PDU.") |
|
|
|
|
})?; |
|
|
|
|
|
|
|
|
|
if user_id == sender_user { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
MembershipState::Leave => { |
|
|
|
|
// Write down users that have left encrypted rooms we are in
|
|
|
|
|
left_encrypted_users.insert(user_id); |
|
|
|
|
|
|
|
|
|
let new_membership = serde_json::from_str::<RoomMemberEventContent>( |
|
|
|
|
state_event.content.get(), |
|
|
|
|
) |
|
|
|
|
.map_err(|_| Error::bad_database("Invalid PDU in database."))? |
|
|
|
|
.membership; |
|
|
|
|
|
|
|
|
|
match new_membership { |
|
|
|
|
MembershipState::Join => { |
|
|
|
|
// A new user joined an encrypted room
|
|
|
|
|
if !share_encrypted_room(&sender_user, &user_id, &room_id)? { |
|
|
|
|
device_list_updates.insert(user_id); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
MembershipState::Leave => { |
|
|
|
|
// Write down users that have left encrypted rooms we are in
|
|
|
|
|
left_encrypted_users.insert(user_id); |
|
|
|
|
} |
|
|
|
|
_ => {} |
|
|
|
|
} |
|
|
|
|
_ => {} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if joined_since_last_sync && encrypted_room || new_encrypted_room { |
|
|
|
|
// If the user is in a new encrypted room, give them all joined users
|
|
|
|
|
device_list_updates.extend( |
|
|
|
|
services() |
|
|
|
|
.rooms |
|
|
|
|
.state_cache |
|
|
|
|
.room_members(&room_id) |
|
|
|
|
.flatten() |
|
|
|
|
.filter(|user_id| { |
|
|
|
|
// Don't send key updates from the sender to the sender
|
|
|
|
|
&sender_user != user_id |
|
|
|
|
}) |
|
|
|
|
.filter(|user_id| { |
|
|
|
|
// Only send keys if the sender doesn't share an encrypted room with the target already
|
|
|
|
|
!share_encrypted_room(&sender_user, user_id, &room_id).unwrap_or(false) |
|
|
|
|
}), |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let (joined_member_count, invited_member_count, heroes) = if send_member_count { |
|
|
|
|
calculate_counts()? |
|
|
|
|
} else { |
|
|
|
|
(None, None, Vec::new()) |
|
|
|
|
}; |
|
|
|
|
if joined_since_last_sync && encrypted_room || new_encrypted_room { |
|
|
|
|
// If the user is in a new encrypted room, give them all joined users
|
|
|
|
|
device_list_updates.extend( |
|
|
|
|
services() |
|
|
|
|
.rooms |
|
|
|
|
.state_cache |
|
|
|
|
.room_members(&room_id) |
|
|
|
|
.flatten() |
|
|
|
|
.filter(|user_id| { |
|
|
|
|
// Don't send key updates from the sender to the sender
|
|
|
|
|
&sender_user != user_id |
|
|
|
|
}) |
|
|
|
|
.filter(|user_id| { |
|
|
|
|
// Only send keys if the sender doesn't share an encrypted room with the target already
|
|
|
|
|
!share_encrypted_room(&sender_user, user_id, &room_id) |
|
|
|
|
.unwrap_or(false) |
|
|
|
|
}), |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
( |
|
|
|
|
heroes, |
|
|
|
|
joined_member_count, |
|
|
|
|
invited_member_count, |
|
|
|
|
joined_since_last_sync, |
|
|
|
|
state_events, |
|
|
|
|
) |
|
|
|
|
let (joined_member_count, invited_member_count, heroes) = if send_member_count { |
|
|
|
|
calculate_counts()? |
|
|
|
|
} else { |
|
|
|
|
(None, None, Vec::new()) |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
( |
|
|
|
|
heroes, |
|
|
|
|
joined_member_count, |
|
|
|
|
invited_member_count, |
|
|
|
|
joined_since_last_sync, |
|
|
|
|
state_events, |
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// Look for device list updates in this room
|
|
|
|
|
|