change rustfmt configuration

This change is fully automated, except the `rustfmt.toml` changes and
a few clippy directives to allow specific functions with too many lines
because they are longer now.
This commit is contained in:
Charles Hall 2024-05-16 01:19:04 -07:00
parent 40d6ce230d
commit 0afc1d2f50
No known key found for this signature in database
GPG key ID: 7B8E0645816E07CF
123 changed files with 7881 additions and 4687 deletions

View file

@ -1,6 +1,7 @@
use crate::Result;
use ruma::{OwnedRoomAliasId, OwnedRoomId, RoomAliasId, RoomId};
use crate::Result;
pub(crate) trait Data: Send + Sync {
/// Creates or updates the alias to the given room id.
fn set_alias(&self, alias: &RoomAliasId, room_id: &RoomId) -> Result<()>;
@ -9,7 +10,10 @@ pub(crate) trait Data: Send + Sync {
fn remove_alias(&self, alias: &RoomAliasId) -> Result<()>;
/// Looks up the roomid for the given alias.
fn resolve_local_alias(&self, alias: &RoomAliasId) -> Result<Option<OwnedRoomId>>;
fn resolve_local_alias(
&self,
alias: &RoomAliasId,
) -> Result<Option<OwnedRoomId>>;
/// Returns all local aliases that point to the given room
fn local_aliases_for_room<'a>(

View file

@ -43,7 +43,8 @@ impl Service {
let mut i = 0;
for id in starting_events {
let short = services().rooms.short.get_or_create_shorteventid(&id)?;
let short =
services().rooms.short.get_or_create_shorteventid(&id)?;
// I'm afraid to change this in case there is accidental reliance on
// the truncation
#[allow(clippy::as_conversions, clippy::cast_possible_truncation)]
@ -64,7 +65,8 @@ impl Service {
continue;
}
let chunk_key: Vec<u64> = chunk.iter().map(|(short, _)| short).copied().collect();
let chunk_key: Vec<u64> =
chunk.iter().map(|(short, _)| short).copied().collect();
if let Some(cached) = services()
.rooms
.auth_chain
@ -90,11 +92,13 @@ impl Service {
chunk_cache.extend(cached.iter().copied());
} else {
misses2 += 1;
let auth_chain = Arc::new(self.get_auth_chain_inner(room_id, &event_id)?);
services()
.rooms
.auth_chain
.cache_auth_chain(vec![sevent_id], Arc::clone(&auth_chain))?;
let auth_chain = Arc::new(
self.get_auth_chain_inner(room_id, &event_id)?,
);
services().rooms.auth_chain.cache_auth_chain(
vec![sevent_id],
Arc::clone(&auth_chain),
)?;
debug!(
event_id = ?event_id,
chain_length = ?auth_chain.len(),
@ -129,13 +133,17 @@ impl Service {
"Auth chain stats",
);
Ok(full_auth_chain
.into_iter()
.filter_map(move |sid| services().rooms.short.get_eventid_from_short(sid).ok()))
Ok(full_auth_chain.into_iter().filter_map(move |sid| {
services().rooms.short.get_eventid_from_short(sid).ok()
}))
}
#[tracing::instrument(skip(self, event_id))]
fn get_auth_chain_inner(&self, room_id: &RoomId, event_id: &EventId) -> Result<HashSet<u64>> {
fn get_auth_chain_inner(
&self,
room_id: &RoomId,
event_id: &EventId,
) -> Result<HashSet<u64>> {
let mut todo = vec![Arc::from(event_id)];
let mut found = HashSet::new();
@ -143,7 +151,10 @@ impl Service {
match services().rooms.timeline.get_pdu(&event_id) {
Ok(Some(pdu)) => {
if pdu.room_id != room_id {
return Err(Error::BadRequest(ErrorKind::Forbidden, "Evil event in db"));
return Err(Error::BadRequest(
ErrorKind::Forbidden,
"Evil event in db",
));
}
for auth_event in &pdu.auth_events {
let sauthevent = services()
@ -158,10 +169,17 @@ impl Service {
}
}
Ok(None) => {
warn!(?event_id, "Could not find pdu mentioned in auth events");
warn!(
?event_id,
"Could not find pdu mentioned in auth events"
);
}
Err(error) => {
error!(?event_id, ?error, "Could not load event in auth chain");
error!(
?event_id,
?error,
"Could not load event in auth chain"
);
}
}
}

View file

@ -1,11 +1,15 @@
use crate::Result;
use std::{collections::HashSet, sync::Arc};
use crate::Result;
pub(crate) trait Data: Send + Sync {
fn get_cached_eventid_authchain(
&self,
shorteventid: &[u64],
) -> Result<Option<Arc<HashSet<u64>>>>;
fn cache_auth_chain(&self, shorteventid: Vec<u64>, auth_chain: Arc<HashSet<u64>>)
-> Result<()>;
fn cache_auth_chain(
&self,
shorteventid: Vec<u64>,
auth_chain: Arc<HashSet<u64>>,
) -> Result<()>;
}

View file

@ -1,6 +1,7 @@
use crate::Result;
use ruma::{OwnedRoomId, RoomId};
use crate::Result;
pub(crate) trait Data: Send + Sync {
/// Adds the room to the public room directory
fn set_public(&self, room_id: &RoomId) -> Result<()>;
@ -12,5 +13,7 @@ pub(crate) trait Data: Send + Sync {
fn is_public_room(&self, room_id: &RoomId) -> Result<bool>;
/// Returns the unsorted public room directory
fn public_rooms<'a>(&'a self) -> Box<dyn Iterator<Item = Result<OwnedRoomId>> + 'a>;
fn public_rooms<'a>(
&'a self,
) -> Box<dyn Iterator<Item = Result<OwnedRoomId>> + 'a>;
}

View file

@ -1,5 +1,8 @@
use ruma::{
events::receipt::ReceiptEvent, serde::Raw, OwnedUserId, RoomId, UserId,
};
use crate::Result;
use ruma::{events::receipt::ReceiptEvent, serde::Raw, OwnedUserId, RoomId, UserId};
pub(crate) trait Data: Send + Sync {
/// Replaces the previous read receipt.
@ -10,7 +13,8 @@ pub(crate) trait Data: Send + Sync {
event: ReceiptEvent,
) -> Result<()>;
/// Returns an iterator over the most recent read receipts in a room that happened after the event with id `since`.
/// Returns an iterator over the most recent read receipts in a room that
/// happened after the event with id `since`.
#[allow(clippy::type_complexity)]
fn readreceipts_since<'a>(
&'a self,
@ -27,11 +31,24 @@ pub(crate) trait Data: Send + Sync {
>;
/// Sets a private read marker at `count`.
fn private_read_set(&self, room_id: &RoomId, user_id: &UserId, count: u64) -> Result<()>;
fn private_read_set(
&self,
room_id: &RoomId,
user_id: &UserId,
count: u64,
) -> Result<()>;
/// Returns the private read marker.
fn private_read_get(&self, room_id: &RoomId, user_id: &UserId) -> Result<Option<u64>>;
fn private_read_get(
&self,
room_id: &RoomId,
user_id: &UserId,
) -> Result<Option<u64>>;
/// Returns the count of the last typing update in this room.
fn last_privateread_update(&self, user_id: &UserId, room_id: &RoomId) -> Result<u64>;
fn last_privateread_update(
&self,
user_id: &UserId,
room_id: &RoomId,
) -> Result<u64>;
}

View file

@ -1,8 +1,9 @@
use std::collections::BTreeMap;
use ruma::{
events::{typing::TypingEventContent, SyncEphemeralRoomEvent},
OwnedRoomId, OwnedUserId, RoomId, UserId,
};
use std::collections::BTreeMap;
use tokio::sync::{broadcast, RwLock};
use tracing::trace;
@ -10,15 +11,16 @@ use crate::{services, utils, Result};
pub(crate) struct Service {
// u64 is unix timestamp of timeout
pub(crate) typing: RwLock<BTreeMap<OwnedRoomId, BTreeMap<OwnedUserId, u64>>>,
pub(crate) typing:
RwLock<BTreeMap<OwnedRoomId, BTreeMap<OwnedUserId, u64>>>,
// timestamp of the last change to typing users
pub(crate) last_typing_update: RwLock<BTreeMap<OwnedRoomId, u64>>,
pub(crate) typing_update_sender: broadcast::Sender<OwnedRoomId>,
}
impl Service {
/// Sets a user as typing until the timeout timestamp is reached or `roomtyping_remove` is
/// called.
/// Sets a user as typing until the timeout timestamp is reached or
/// `roomtyping_remove` is called.
pub(crate) async fn typing_add(
&self,
user_id: &UserId,
@ -36,13 +38,20 @@ impl Service {
.await
.insert(room_id.to_owned(), services().globals.next_count()?);
if self.typing_update_sender.send(room_id.to_owned()).is_err() {
trace!("receiver found what it was looking for and is no longer interested");
trace!(
"receiver found what it was looking for and is no longer \
interested"
);
}
Ok(())
}
/// Removes a user from typing before the timeout is reached.
pub(crate) async fn typing_remove(&self, user_id: &UserId, room_id: &RoomId) -> Result<()> {
pub(crate) async fn typing_remove(
&self,
user_id: &UserId,
room_id: &RoomId,
) -> Result<()> {
self.typing
.write()
.await
@ -54,7 +63,10 @@ impl Service {
.await
.insert(room_id.to_owned(), services().globals.next_count()?);
if self.typing_update_sender.send(room_id.to_owned()).is_err() {
trace!("receiver found what it was looking for and is no longer interested");
trace!(
"receiver found what it was looking for and is no longer \
interested"
);
}
Ok(())
}
@ -97,14 +109,20 @@ impl Service {
.await
.insert(room_id.to_owned(), services().globals.next_count()?);
if self.typing_update_sender.send(room_id.to_owned()).is_err() {
trace!("receiver found what it was looking for and is no longer interested");
trace!(
"receiver found what it was looking for and is no longer \
interested"
);
}
}
Ok(())
}
/// Returns the count of the last typing update in this room.
pub(crate) async fn last_typing_update(&self, room_id: &RoomId) -> Result<u64> {
pub(crate) async fn last_typing_update(
&self,
room_id: &RoomId,
) -> Result<u64> {
self.typings_maintain(room_id).await?;
Ok(self
.last_typing_update

File diff suppressed because it is too large Load diff

View file

@ -5,16 +5,19 @@ pub(crate) use data::Data;
use ruma::{DeviceId, OwnedDeviceId, OwnedRoomId, OwnedUserId, RoomId, UserId};
use tokio::sync::Mutex;
use crate::Result;
use super::timeline::PduCount;
use crate::Result;
pub(crate) struct Service {
pub(crate) db: &'static dyn Data,
#[allow(clippy::type_complexity)]
pub(crate) lazy_load_waiting:
Mutex<HashMap<(OwnedUserId, OwnedDeviceId, OwnedRoomId, PduCount), HashSet<OwnedUserId>>>,
pub(crate) lazy_load_waiting: Mutex<
HashMap<
(OwnedUserId, OwnedDeviceId, OwnedRoomId, PduCount),
HashSet<OwnedUserId>,
>,
>,
}
impl Service {
@ -26,8 +29,7 @@ impl Service {
room_id: &RoomId,
ll_user: &UserId,
) -> Result<bool> {
self.db
.lazy_load_was_sent_before(user_id, device_id, room_id, ll_user)
self.db.lazy_load_was_sent_before(user_id, device_id, room_id, ll_user)
}
#[tracing::instrument(skip(self))]

View file

@ -1,6 +1,7 @@
use crate::Result;
use ruma::{DeviceId, RoomId, UserId};
use crate::Result;
pub(crate) trait Data: Send + Sync {
fn lazy_load_was_sent_before(
&self,

View file

@ -1,10 +1,13 @@
use crate::Result;
use ruma::{OwnedRoomId, RoomId};
use crate::Result;
pub(crate) trait Data: Send + Sync {
/// Checks if a room exists.
fn exists(&self, room_id: &RoomId) -> Result<bool>;
fn iter_ids<'a>(&'a self) -> Box<dyn Iterator<Item = Result<OwnedRoomId>> + 'a>;
fn iter_ids<'a>(
&'a self,
) -> Box<dyn Iterator<Item = Result<OwnedRoomId>> + 'a>;
fn is_disabled(&self, room_id: &RoomId) -> Result<bool>;
fn disable_room(&self, room_id: &RoomId, disabled: bool) -> Result<()>;
}

View file

@ -4,8 +4,15 @@ use crate::{PduEvent, Result};
pub(crate) trait Data: Send + Sync {
/// Returns the pdu from the outlier tree.
fn get_outlier_pdu_json(&self, event_id: &EventId) -> Result<Option<CanonicalJsonObject>>;
fn get_outlier_pdu_json(
&self,
event_id: &EventId,
) -> Result<Option<CanonicalJsonObject>>;
fn get_outlier_pdu(&self, event_id: &EventId) -> Result<Option<PduEvent>>;
/// Append the PDU as an outlier.
fn add_pdu_outlier(&self, event_id: &EventId, pdu: &CanonicalJsonObject) -> Result<()>;
fn add_pdu_outlier(
&self,
event_id: &EventId,
pdu: &CanonicalJsonObject,
) -> Result<()>;
}

View file

@ -9,9 +9,8 @@ use ruma::{
};
use serde::Deserialize;
use crate::{services, PduEvent, Result};
use super::timeline::PduCount;
use crate::{services, PduEvent, Result};
pub(crate) struct Service {
pub(crate) db: &'static dyn Data,
@ -29,9 +28,15 @@ struct ExtractRelatesToEventId {
impl Service {
#[tracing::instrument(skip(self, from, to))]
pub(crate) fn add_relation(&self, from: PduCount, to: PduCount) -> Result<()> {
pub(crate) fn add_relation(
&self,
from: PduCount,
to: PduCount,
) -> Result<()> {
match (from, to) {
(PduCount::Normal(f), PduCount::Normal(t)) => self.db.add_relation(f, t),
(PduCount::Normal(f), PduCount::Normal(t)) => {
self.db.add_relation(f, t)
}
_ => {
// TODO: Relations with backfilled pdus
@ -42,6 +47,7 @@ impl Service {
#[allow(
clippy::too_many_arguments,
clippy::too_many_lines,
// Allowed because this function uses `services()`
clippy::unused_self,
)]
@ -68,15 +74,17 @@ impl Service {
.relations_until(sender_user, room_id, target, from)?
.filter(|r| {
r.as_ref().map_or(true, |(_, pdu)| {
filter_event_type.as_ref().map_or(true, |t| &&pdu.kind == t)
&& if let Ok(content) =
serde_json::from_str::<ExtractRelatesToEventId>(
pdu.content.get(),
)
{
filter_rel_type
.as_ref()
.map_or(true, |r| &&content.relates_to.rel_type == r)
filter_event_type
.as_ref()
.map_or(true, |t| &&pdu.kind == t)
&& if let Ok(content) = serde_json::from_str::<
ExtractRelatesToEventId,
>(
pdu.content.get()
) {
filter_rel_type.as_ref().map_or(true, |r| {
&&content.relates_to.rel_type == r
})
} else {
false
}
@ -88,13 +96,18 @@ impl Service {
services()
.rooms
.state_accessor
.user_can_see_event(sender_user, room_id, &pdu.event_id)
.user_can_see_event(
sender_user,
room_id,
&pdu.event_id,
)
.unwrap_or(false)
})
.take_while(|&(k, _)| Some(k) != to)
.collect();
next_token = events_after.last().map(|(count, _)| count).copied();
next_token =
events_after.last().map(|(count, _)| count).copied();
// Reversed because relations are always most recent first
let events_after: Vec<_> = events_after
@ -116,15 +129,17 @@ impl Service {
.relations_until(sender_user, room_id, target, from)?
.filter(|r| {
r.as_ref().map_or(true, |(_, pdu)| {
filter_event_type.as_ref().map_or(true, |t| &&pdu.kind == t)
&& if let Ok(content) =
serde_json::from_str::<ExtractRelatesToEventId>(
pdu.content.get(),
)
{
filter_rel_type
.as_ref()
.map_or(true, |r| &&content.relates_to.rel_type == r)
filter_event_type
.as_ref()
.map_or(true, |t| &&pdu.kind == t)
&& if let Ok(content) = serde_json::from_str::<
ExtractRelatesToEventId,
>(
pdu.content.get()
) {
filter_rel_type.as_ref().map_or(true, |r| {
&&content.relates_to.rel_type == r
})
} else {
false
}
@ -136,13 +151,18 @@ impl Service {
services()
.rooms
.state_accessor
.user_can_see_event(sender_user, room_id, &pdu.event_id)
.user_can_see_event(
sender_user,
room_id,
&pdu.event_id,
)
.unwrap_or(false)
})
.take_while(|&(k, _)| Some(k) != to)
.collect();
next_token = events_before.last().map(|(count, _)| count).copied();
next_token =
events_before.last().map(|(count, _)| count).copied();
let events_before: Vec<_> = events_before
.into_iter()
@ -165,7 +185,8 @@ impl Service {
target: &'a EventId,
until: PduCount,
) -> Result<impl Iterator<Item = Result<(PduCount, PduEvent)>> + 'a> {
let room_id = services().rooms.short.get_or_create_shortroomid(room_id)?;
let room_id =
services().rooms.short.get_or_create_shortroomid(room_id)?;
let target = match services().rooms.timeline.get_pdu_count(target)? {
Some(PduCount::Normal(c)) => c,
// TODO: Support backfilled relations
@ -185,17 +206,27 @@ impl Service {
}
#[tracing::instrument(skip(self))]
pub(crate) fn is_event_referenced(&self, room_id: &RoomId, event_id: &EventId) -> Result<bool> {
pub(crate) fn is_event_referenced(
&self,
room_id: &RoomId,
event_id: &EventId,
) -> Result<bool> {
self.db.is_event_referenced(room_id, event_id)
}
#[tracing::instrument(skip(self))]
pub(crate) fn mark_event_soft_failed(&self, event_id: &EventId) -> Result<()> {
pub(crate) fn mark_event_soft_failed(
&self,
event_id: &EventId,
) -> Result<()> {
self.db.mark_event_soft_failed(event_id)
}
#[tracing::instrument(skip(self))]
pub(crate) fn is_event_soft_failed(&self, event_id: &EventId) -> Result<bool> {
pub(crate) fn is_event_soft_failed(
&self,
event_id: &EventId,
) -> Result<bool> {
self.db.is_event_soft_failed(event_id)
}
}

View file

@ -1,8 +1,9 @@
use std::sync::Arc;
use crate::{service::rooms::timeline::PduCount, PduEvent, Result};
use ruma::{EventId, RoomId, UserId};
use crate::{service::rooms::timeline::PduCount, PduEvent, Result};
pub(crate) trait Data: Send + Sync {
fn add_relation(&self, from: u64, to: u64) -> Result<()>;
#[allow(clippy::type_complexity)]
@ -13,8 +14,16 @@ pub(crate) trait Data: Send + Sync {
target: u64,
until: PduCount,
) -> Result<Box<dyn Iterator<Item = Result<(PduCount, PduEvent)>> + 'a>>;
fn mark_as_referenced(&self, room_id: &RoomId, event_ids: &[Arc<EventId>]) -> Result<()>;
fn is_event_referenced(&self, room_id: &RoomId, event_id: &EventId) -> Result<bool>;
fn mark_as_referenced(
&self,
room_id: &RoomId,
event_ids: &[Arc<EventId>],
) -> Result<()>;
fn is_event_referenced(
&self,
room_id: &RoomId,
event_id: &EventId,
) -> Result<bool>;
fn mark_event_soft_failed(&self, event_id: &EventId) -> Result<()>;
fn is_event_soft_failed(&self, event_id: &EventId) -> Result<bool>;
}

View file

@ -1,8 +1,14 @@
use crate::Result;
use ruma::RoomId;
use crate::Result;
pub(crate) trait Data: Send + Sync {
fn index_pdu(&self, shortroomid: u64, pdu_id: &[u8], message_body: &str) -> Result<()>;
fn index_pdu(
&self,
shortroomid: u64,
pdu_id: &[u8],
message_body: &str,
) -> Result<()>;
#[allow(clippy::type_complexity)]
fn search_pdus<'a>(

View file

@ -1,8 +1,9 @@
use std::sync::Arc;
use crate::Result;
use ruma::{events::StateEventType, EventId, RoomId};
use crate::Result;
pub(crate) trait Data: Send + Sync {
fn get_or_create_shorteventid(&self, event_id: &EventId) -> Result<u64>;
@ -18,12 +19,19 @@ pub(crate) trait Data: Send + Sync {
state_key: &str,
) -> Result<u64>;
fn get_eventid_from_short(&self, shorteventid: u64) -> Result<Arc<EventId>>;
fn get_eventid_from_short(&self, shorteventid: u64)
-> Result<Arc<EventId>>;
fn get_statekey_from_short(&self, shortstatekey: u64) -> Result<(StateEventType, String)>;
fn get_statekey_from_short(
&self,
shortstatekey: u64,
) -> Result<(StateEventType, String)>;
/// Returns `(shortstatehash, already_existed)`
fn get_or_create_shortstatehash(&self, state_hash: &[u8]) -> Result<(u64, bool)>;
fn get_or_create_shortstatehash(
&self,
state_hash: &[u8],
) -> Result<(u64, bool)>;
fn get_shortroomid(&self, room_id: &RoomId) -> Result<Option<u64>>;

View file

@ -15,8 +15,12 @@ use ruma::{
canonical_alias::RoomCanonicalAliasEventContent,
create::RoomCreateEventContent,
guest_access::{GuestAccess, RoomGuestAccessEventContent},
history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent},
join_rules::{self, AllowRule, JoinRule, RoomJoinRulesEventContent},
history_visibility::{
HistoryVisibility, RoomHistoryVisibilityEventContent,
},
join_rules::{
self, AllowRule, JoinRule, RoomJoinRulesEventContent,
},
topic::RoomTopicEventContent,
},
space::child::SpaceChildEventContent,
@ -26,7 +30,6 @@ use ruma::{
OwnedRoomId, RoomId, UserId,
};
use tokio::sync::Mutex;
use tracing::{debug, error, warn};
use crate::{services, Error, PduEvent, Result};
@ -42,7 +45,8 @@ pub(crate) struct CachedSpaceChunk {
}
pub(crate) struct Service {
pub(crate) roomid_spacechunk_cache: Mutex<LruCache<OwnedRoomId, Option<CachedSpaceChunk>>>,
pub(crate) roomid_spacechunk_cache:
Mutex<LruCache<OwnedRoomId, Option<CachedSpaceChunk>>>,
}
impl Service {
@ -86,9 +90,11 @@ impl Service {
{
if let Some(cached) = cached {
let allowed = match &cached.join_rule {
CachedJoinRule::Full(f) => {
self.handle_join_rule(f, sender_user, &current_room)?
}
CachedJoinRule::Full(f) => self.handle_join_rule(
f,
sender_user,
&current_room,
)?,
};
if allowed {
if left_to_skip > 0 {
@ -104,10 +110,8 @@ impl Service {
continue;
}
if let Some(current_shortstatehash) = services()
.rooms
.state
.get_room_shortstatehash(&current_room)?
if let Some(current_shortstatehash) =
services().rooms.state.get_room_shortstatehash(&current_room)?
{
let state = services()
.rooms
@ -124,16 +128,21 @@ impl Service {
continue;
}
let pdu = services()
.rooms
.timeline
.get_pdu(&id)?
.ok_or_else(|| Error::bad_database("Event in space state not found"))?;
let pdu =
services().rooms.timeline.get_pdu(&id)?.ok_or_else(
|| {
Error::bad_database(
"Event in space state not found",
)
},
)?;
if serde_json::from_str::<SpaceChildEventContent>(pdu.content.get())
.ok()
.map(|c| c.via)
.map_or(true, |v| v.is_empty())
if serde_json::from_str::<SpaceChildEventContent>(
pdu.content.get(),
)
.ok()
.map(|c| c.via)
.map_or(true, |v| v.is_empty())
{
continue;
}
@ -147,7 +156,11 @@ impl Service {
// TODO: Sort children
children_ids.reverse();
let chunk = self.get_room_chunk(sender_user, &current_room, children_pdus);
let chunk = self.get_room_chunk(
sender_user,
&current_room,
children_pdus,
);
if let Ok(chunk) = chunk {
if left_to_skip > 0 {
left_to_skip -= 1;
@ -157,13 +170,24 @@ impl Service {
let join_rule = services()
.rooms
.state_accessor
.room_state_get(&current_room, &StateEventType::RoomJoinRules, "")?
.room_state_get(
&current_room,
&StateEventType::RoomJoinRules,
"",
)?
.map(|s| {
serde_json::from_str(s.content.get())
.map(|c: RoomJoinRulesEventContent| c.join_rule)
.map_err(|e| {
error!("Invalid room join rule event in database: {}", e);
Error::BadDatabase("Invalid room join rule event in database.")
error!(
"Invalid room join rule event in \
database: {}",
e
);
Error::BadDatabase(
"Invalid room join rule event in \
database.",
)
})
})
.transpose()?
@ -205,7 +229,10 @@ impl Service {
)
.await
{
warn!("Got response from {server} for /hierarchy\n{response:?}");
warn!(
"Got response from {server} for \
/hierarchy\n{response:?}"
);
let chunk = SpaceHierarchyRoomsChunk {
canonical_alias: response.room.canonical_alias,
name: response.room.name,
@ -250,9 +277,17 @@ impl Service {
})
}
SpaceRoomJoinRule::Public => JoinRule::Public,
_ => return Err(Error::BadServerResponse("Unknown join rule")),
_ => {
return Err(Error::BadServerResponse(
"Unknown join rule",
))
}
};
if self.handle_join_rule(&join_rule, sender_user, &current_room)? {
if self.handle_join_rule(
&join_rule,
sender_user,
&current_room,
)? {
if left_to_skip > 0 {
left_to_skip -= 1;
} else {
@ -301,12 +336,18 @@ impl Service {
canonical_alias: services()
.rooms
.state_accessor
.room_state_get(room_id, &StateEventType::RoomCanonicalAlias, "")?
.room_state_get(
room_id,
&StateEventType::RoomCanonicalAlias,
"",
)?
.map_or(Ok(None), |s| {
serde_json::from_str(s.content.get())
.map(|c: RoomCanonicalAliasEventContent| c.alias)
.map_err(|_| {
Error::bad_database("Invalid canonical alias event in database.")
Error::bad_database(
"Invalid canonical alias event in database.",
)
})
})?,
name: services().rooms.state_accessor.get_name(room_id)?,
@ -329,22 +370,34 @@ impl Service {
serde_json::from_str(s.content.get())
.map(|c: RoomTopicEventContent| Some(c.topic))
.map_err(|_| {
error!("Invalid room topic event in database for room {}", room_id);
Error::bad_database("Invalid room topic event in database.")
error!(
"Invalid room topic event in database for \
room {}",
room_id
);
Error::bad_database(
"Invalid room topic event in database.",
)
})
})?,
world_readable: services()
.rooms
.state_accessor
.room_state_get(room_id, &StateEventType::RoomHistoryVisibility, "")?
.room_state_get(
room_id,
&StateEventType::RoomHistoryVisibility,
"",
)?
.map_or(Ok(false), |s| {
serde_json::from_str(s.content.get())
.map(|c: RoomHistoryVisibilityEventContent| {
c.history_visibility == HistoryVisibility::WorldReadable
c.history_visibility
== HistoryVisibility::WorldReadable
})
.map_err(|_| {
Error::bad_database(
"Invalid room history visibility event in database.",
"Invalid room history visibility event in \
database.",
)
})
})?,
@ -358,7 +411,9 @@ impl Service {
c.guest_access == GuestAccess::CanJoin
})
.map_err(|_| {
Error::bad_database("Invalid room guest access event in database.")
Error::bad_database(
"Invalid room guest access event in database.",
)
})
})?,
avatar_url: services()
@ -368,7 +423,11 @@ impl Service {
.map(|s| {
serde_json::from_str(s.content.get())
.map(|c: RoomAvatarEventContent| c.url)
.map_err(|_| Error::bad_database("Invalid room avatar event in database."))
.map_err(|_| {
Error::bad_database(
"Invalid room avatar event in database.",
)
})
})
.transpose()?
.flatten(),
@ -376,13 +435,23 @@ impl Service {
let join_rule = services()
.rooms
.state_accessor
.room_state_get(room_id, &StateEventType::RoomJoinRules, "")?
.room_state_get(
room_id,
&StateEventType::RoomJoinRules,
"",
)?
.map(|s| {
serde_json::from_str(s.content.get())
.map(|c: RoomJoinRulesEventContent| c.join_rule)
.map_err(|e| {
error!("Invalid room join rule event in database: {}", e);
Error::BadDatabase("Invalid room join rule event in database.")
error!(
"Invalid room join rule event in \
database: {}",
e
);
Error::BadDatabase(
"Invalid room join rule event in database.",
)
})
})
.transpose()?
@ -404,9 +473,14 @@ impl Service {
.state_accessor
.room_state_get(room_id, &StateEventType::RoomCreate, "")?
.map(|s| {
serde_json::from_str::<RoomCreateEventContent>(s.content.get()).map_err(|e| {
serde_json::from_str::<RoomCreateEventContent>(
s.content.get(),
)
.map_err(|e| {
error!("Invalid room create event in database: {}", e);
Error::BadDatabase("Invalid room create event in database.")
Error::BadDatabase(
"Invalid room create event in database.",
)
})
})
.transpose()?
@ -424,7 +498,9 @@ impl Service {
JoinRule::Knock => Ok(SpaceRoomJoinRule::Knock),
JoinRule::Private => Ok(SpaceRoomJoinRule::Private),
JoinRule::Restricted(_) => Ok(SpaceRoomJoinRule::Restricted),
JoinRule::KnockRestricted(_) => Ok(SpaceRoomJoinRule::KnockRestricted),
JoinRule::KnockRestricted(_) => {
Ok(SpaceRoomJoinRule::KnockRestricted)
}
JoinRule::Public => Ok(SpaceRoomJoinRule::Public),
_ => Err(Error::BadServerResponse("Unknown join rule")),
}
@ -440,10 +516,9 @@ impl Service {
) -> Result<bool> {
let allowed = match join_rule {
SpaceRoomJoinRule::Knock | SpaceRoomJoinRule::Public => true,
SpaceRoomJoinRule::Invite => services()
.rooms
.state_cache
.is_joined(sender_user, room_id)?,
SpaceRoomJoinRule::Invite => {
services().rooms.state_cache.is_joined(sender_user, room_id)?
}
_ => false,
};

View file

@ -19,9 +19,8 @@ use serde::Deserialize;
use tokio::sync::MutexGuard;
use tracing::warn;
use crate::{services, utils::calculate_hash, Error, PduEvent, Result};
use super::state_compressor::CompressedStateEvent;
use crate::{services, utils::calculate_hash, Error, PduEvent, Result};
pub(crate) struct Service {
pub(crate) db: &'static dyn Data,
@ -46,12 +45,15 @@ impl Service {
.ok()
.map(|(_, id)| id)
}) {
let Some(pdu) = services().rooms.timeline.get_pdu_json(&event_id)? else {
let Some(pdu) =
services().rooms.timeline.get_pdu_json(&event_id)?
else {
continue;
};
let pdu: PduEvent = match serde_json::from_str(
&serde_json::to_string(&pdu).expect("CanonicalJsonObj can be serialized to JSON"),
&serde_json::to_string(&pdu)
.expect("CanonicalJsonObj can be serialized to JSON"),
) {
Ok(pdu) => pdu,
Err(_) => continue,
@ -65,7 +67,9 @@ impl Service {
}
let membership =
match serde_json::from_str::<ExtractMembership>(pdu.content.get()) {
match serde_json::from_str::<ExtractMembership>(
pdu.content.get(),
) {
Ok(e) => e.membership,
Err(_) => continue,
};
@ -102,8 +106,7 @@ impl Service {
services().rooms.state_cache.update_joined_count(room_id)?;
self.db
.set_room_state(room_id, shortstatehash, state_lock)?;
self.db.set_room_state(room_id, shortstatehash, state_lock)?;
Ok(())
}
@ -119,24 +122,18 @@ impl Service {
room_id: &RoomId,
state_ids_compressed: Arc<HashSet<CompressedStateEvent>>,
) -> Result<u64> {
let shorteventid = services()
.rooms
.short
.get_or_create_shorteventid(event_id)?;
let shorteventid =
services().rooms.short.get_or_create_shorteventid(event_id)?;
let previous_shortstatehash = self.db.get_room_shortstatehash(room_id)?;
let previous_shortstatehash =
self.db.get_room_shortstatehash(room_id)?;
let state_hash = calculate_hash(
&state_ids_compressed
.iter()
.map(|s| &s[..])
.collect::<Vec<_>>(),
&state_ids_compressed.iter().map(|s| &s[..]).collect::<Vec<_>>(),
);
let (shortstatehash, already_existed) = services()
.rooms
.short
.get_or_create_shortstatehash(&state_hash)?;
let (shortstatehash, already_existed) =
services().rooms.short.get_or_create_shortstatehash(&state_hash)?;
if !already_existed {
let states_parents = previous_shortstatehash.map_or_else(
@ -192,7 +189,8 @@ impl Service {
.short
.get_or_create_shorteventid(&new_pdu.event_id)?;
let previous_shortstatehash = self.get_room_shortstatehash(&new_pdu.room_id)?;
let previous_shortstatehash =
self.get_room_shortstatehash(&new_pdu.room_id)?;
if let Some(p) = previous_shortstatehash {
self.db.set_event_state(shorteventid, p)?;
@ -209,10 +207,11 @@ impl Service {
},
)?;
let shortstatekey = services()
.rooms
.short
.get_or_create_shortstatekey(&new_pdu.kind.to_string().into(), state_key)?;
let shortstatekey =
services().rooms.short.get_or_create_shortstatekey(
&new_pdu.kind.to_string().into(),
state_key,
)?;
let new = services()
.rooms
@ -222,9 +221,9 @@ impl Service {
let replaces = states_parents
.last()
.map(|info| {
info.1
.iter()
.find(|bytes| bytes.starts_with(&shortstatekey.to_be_bytes()))
info.1.iter().find(|bytes| {
bytes.starts_with(&shortstatekey.to_be_bytes())
})
})
.unwrap_or_default();
@ -253,7 +252,8 @@ impl Service {
Ok(shortstatehash)
} else {
Ok(previous_shortstatehash.expect("first event in room must be a state event"))
Ok(previous_shortstatehash
.expect("first event in room must be a state event"))
}
}
@ -325,7 +325,10 @@ impl Service {
/// Returns the room's version.
#[tracing::instrument(skip(self))]
pub(crate) fn get_room_version(&self, room_id: &RoomId) -> Result<RoomVersionId> {
pub(crate) fn get_room_version(
&self,
room_id: &RoomId,
) -> Result<RoomVersionId> {
let create_event = services().rooms.state_accessor.room_state_get(
room_id,
&StateEventType::RoomCreate,
@ -341,12 +344,20 @@ impl Service {
})
})
.transpose()?
.ok_or_else(|| Error::BadRequest(ErrorKind::InvalidParam, "No create event found"))?;
.ok_or_else(|| {
Error::BadRequest(
ErrorKind::InvalidParam,
"No create event found",
)
})?;
Ok(create_event_content.room_version)
}
pub(crate) fn get_room_shortstatehash(&self, room_id: &RoomId) -> Result<Option<u64>> {
pub(crate) fn get_room_shortstatehash(
&self,
room_id: &RoomId,
) -> Result<Option<u64>> {
self.db.get_room_shortstatehash(room_id)
}
@ -364,8 +375,7 @@ impl Service {
// Take mutex guard to make sure users get the room state mutex
state_lock: &MutexGuard<'_, ()>,
) -> Result<()> {
self.db
.set_forward_extremities(room_id, event_ids, state_lock)
self.db.set_forward_extremities(room_id, event_ids, state_lock)
}
/// This fetches auth events from the current state.
@ -378,12 +388,15 @@ impl Service {
state_key: Option<&str>,
content: &serde_json::value::RawValue,
) -> Result<StateMap<Arc<PduEvent>>> {
let Some(shortstatehash) = services().rooms.state.get_room_shortstatehash(room_id)? else {
let Some(shortstatehash) =
services().rooms.state.get_room_shortstatehash(room_id)?
else {
return Ok(HashMap::new());
};
let auth_events = state_res::auth_types_for_event(kind, sender, state_key, content)
.expect("content is a valid JSON object");
let auth_events =
state_res::auth_types_for_event(kind, sender, state_key, content)
.expect("content is a valid JSON object");
let mut sauthevents = auth_events
.into_iter()
@ -391,7 +404,10 @@ impl Service {
services()
.rooms
.short
.get_shortstatekey(&event_type.to_string().into(), &state_key)
.get_shortstatekey(
&event_type.to_string().into(),
&state_key,
)
.ok()
.flatten()
.map(|s| (s, (event_type, state_key)))

View file

@ -1,8 +1,10 @@
use crate::Result;
use ruma::{EventId, OwnedEventId, RoomId};
use std::{collections::HashSet, sync::Arc};
use ruma::{EventId, OwnedEventId, RoomId};
use tokio::sync::MutexGuard;
use crate::Result;
pub(crate) trait Data: Send + Sync {
/// Returns the last state hash key added to the db for the given room.
fn get_room_shortstatehash(&self, room_id: &RoomId) -> Result<Option<u64>>;
@ -17,10 +19,17 @@ pub(crate) trait Data: Send + Sync {
) -> Result<()>;
/// Associates a state with an event.
fn set_event_state(&self, shorteventid: u64, shortstatehash: u64) -> Result<()>;
fn set_event_state(
&self,
shorteventid: u64,
shortstatehash: u64,
) -> Result<()>;
/// Returns all events we would send as the `prev_events` of the next event.
fn get_forward_extremities(&self, room_id: &RoomId) -> Result<HashSet<Arc<EventId>>>;
fn get_forward_extremities(
&self,
room_id: &RoomId,
) -> Result<HashSet<Arc<EventId>>>;
/// Replace the forward extremities of the room.
fn set_forward_extremities(

View file

@ -10,7 +10,9 @@ use ruma::{
events::{
room::{
avatar::RoomAvatarEventContent,
history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent},
history_visibility::{
HistoryVisibility, RoomHistoryVisibilityEventContent,
},
member::{MembershipState, RoomMemberEventContent},
name::RoomNameEventContent,
power_levels::{RoomPowerLevels, RoomPowerLevelsEventContent},
@ -18,7 +20,8 @@ use ruma::{
StateEventType,
},
state_res::Event,
EventId, JsOption, OwnedServerName, OwnedUserId, RoomId, ServerName, UserId,
EventId, JsOption, OwnedServerName, OwnedUserId, RoomId, ServerName,
UserId,
};
use serde_json::value::to_raw_value;
use tokio::sync::MutexGuard;
@ -28,7 +31,8 @@ use crate::{service::pdu::PduBuilder, services, Error, PduEvent, Result};
pub(crate) struct Service {
pub(crate) db: &'static dyn Data,
pub(crate) server_visibility_cache: Mutex<LruCache<(OwnedServerName, u64), bool>>,
pub(crate) server_visibility_cache:
Mutex<LruCache<(OwnedServerName, u64), bool>>,
pub(crate) user_visibility_cache: Mutex<LruCache<(OwnedUserId, u64), bool>>,
}
@ -50,7 +54,8 @@ impl Service {
self.db.state_full(shortstatehash).await
}
/// Returns a single PDU from `room_id` with key (`event_type`, `state_key`).
/// Returns a single PDU from `room_id` with key (`event_type`,
/// `state_key`).
#[tracing::instrument(skip(self))]
pub(crate) fn state_get_id(
&self,
@ -61,7 +66,8 @@ impl Service {
self.db.state_get_id(shortstatehash, event_type, state_key)
}
/// Returns a single PDU from `room_id` with key (`event_type`, `state_key`).
/// Returns a single PDU from `room_id` with key (`event_type`,
/// `state_key`).
pub(crate) fn state_get(
&self,
shortstatehash: u64,
@ -72,7 +78,11 @@ impl Service {
}
/// Get membership for given user in state
fn user_membership(&self, shortstatehash: u64, user_id: &UserId) -> Result<MembershipState> {
fn user_membership(
&self,
shortstatehash: u64,
user_id: &UserId,
) -> Result<MembershipState> {
self.state_get(
shortstatehash,
&StateEventType::RoomMember,
@ -81,7 +91,11 @@ impl Service {
.map_or(Ok(MembershipState::Leave), |s| {
serde_json::from_str(s.content.get())
.map(|c: RoomMemberEventContent| c.membership)
.map_err(|_| Error::bad_database("Invalid room membership event in database."))
.map_err(|_| {
Error::bad_database(
"Invalid room membership event in database.",
)
})
})
}
@ -123,12 +137,20 @@ impl Service {
}
let history_visibility = self
.state_get(shortstatehash, &StateEventType::RoomHistoryVisibility, "")?
.state_get(
shortstatehash,
&StateEventType::RoomHistoryVisibility,
"",
)?
.map_or(Ok(HistoryVisibility::Shared), |s| {
serde_json::from_str(s.content.get())
.map(|c: RoomHistoryVisibilityEventContent| c.history_visibility)
.map(|c: RoomHistoryVisibilityEventContent| {
c.history_visibility
})
.map_err(|_| {
Error::bad_database("Invalid history visibility event in database.")
Error::bad_database(
"Invalid history visibility event in database.",
)
})
})?;
@ -140,14 +162,20 @@ impl Service {
.filter(|member| member.server_name() == origin);
let visibility = match history_visibility {
HistoryVisibility::WorldReadable | HistoryVisibility::Shared => true,
HistoryVisibility::WorldReadable | HistoryVisibility::Shared => {
true
}
HistoryVisibility::Invited => {
// Allow if any member on requesting server was AT LEAST invited, else deny
current_server_members.any(|member| self.user_was_invited(shortstatehash, &member))
// Allow if any member on requesting server was AT LEAST
// invited, else deny
current_server_members.any(|member| {
self.user_was_invited(shortstatehash, &member)
})
}
HistoryVisibility::Joined => {
// Allow if any member on requested server was joined, else deny
current_server_members.any(|member| self.user_was_joined(shortstatehash, &member))
current_server_members
.any(|member| self.user_was_joined(shortstatehash, &member))
}
_ => {
error!("Unknown history visibility {history_visibility}");
@ -185,15 +213,24 @@ impl Service {
return Ok(*visibility);
}
let currently_member = services().rooms.state_cache.is_joined(user_id, room_id)?;
let currently_member =
services().rooms.state_cache.is_joined(user_id, room_id)?;
let history_visibility = self
.state_get(shortstatehash, &StateEventType::RoomHistoryVisibility, "")?
.state_get(
shortstatehash,
&StateEventType::RoomHistoryVisibility,
"",
)?
.map_or(Ok(HistoryVisibility::Shared), |s| {
serde_json::from_str(s.content.get())
.map(|c: RoomHistoryVisibilityEventContent| c.history_visibility)
.map(|c: RoomHistoryVisibilityEventContent| {
c.history_visibility
})
.map_err(|_| {
Error::bad_database("Invalid history visibility event in database.")
Error::bad_database(
"Invalid history visibility event in database.",
)
})
})?;
@ -201,7 +238,8 @@ impl Service {
HistoryVisibility::WorldReadable => true,
HistoryVisibility::Shared => currently_member,
HistoryVisibility::Invited => {
// Allow if any member on requesting server was AT LEAST invited, else deny
// Allow if any member on requesting server was AT LEAST
// invited, else deny
self.user_was_invited(shortstatehash, user_id)
}
HistoryVisibility::Joined => {
@ -230,23 +268,36 @@ impl Service {
user_id: &UserId,
room_id: &RoomId,
) -> Result<bool> {
let currently_member = services().rooms.state_cache.is_joined(user_id, room_id)?;
let currently_member =
services().rooms.state_cache.is_joined(user_id, room_id)?;
let history_visibility = self
.room_state_get(room_id, &StateEventType::RoomHistoryVisibility, "")?
.room_state_get(
room_id,
&StateEventType::RoomHistoryVisibility,
"",
)?
.map_or(Ok(HistoryVisibility::Shared), |s| {
serde_json::from_str(s.content.get())
.map(|c: RoomHistoryVisibilityEventContent| c.history_visibility)
.map(|c: RoomHistoryVisibilityEventContent| {
c.history_visibility
})
.map_err(|_| {
Error::bad_database("Invalid history visibility event in database.")
Error::bad_database(
"Invalid history visibility event in database.",
)
})
})?;
Ok(currently_member || history_visibility == HistoryVisibility::WorldReadable)
Ok(currently_member
|| history_visibility == HistoryVisibility::WorldReadable)
}
/// Returns the state hash for this pdu.
pub(crate) fn pdu_shortstatehash(&self, event_id: &EventId) -> Result<Option<u64>> {
pub(crate) fn pdu_shortstatehash(
&self,
event_id: &EventId,
) -> Result<Option<u64>> {
self.db.pdu_shortstatehash(event_id)
}
@ -259,7 +310,8 @@ impl Service {
self.db.room_state_full(room_id).await
}
/// Returns a single PDU from `room_id` with key (`event_type`, `state_key`).
/// Returns a single PDU from `room_id` with key (`event_type`,
/// `state_key`).
#[tracing::instrument(skip(self))]
pub(crate) fn room_state_get_id(
&self,
@ -270,7 +322,8 @@ impl Service {
self.db.room_state_get_id(room_id, event_type, state_key)
}
/// Returns a single PDU from `room_id` with key (`event_type`, `state_key`).
/// Returns a single PDU from `room_id` with key (`event_type`,
/// `state_key`).
#[tracing::instrument(skip(self))]
pub(crate) fn room_state_get(
&self,
@ -282,26 +335,39 @@ impl Service {
}
pub(crate) fn get_name(&self, room_id: &RoomId) -> Result<Option<String>> {
self.room_state_get(room_id, &StateEventType::RoomName, "")?
.map_or(Ok(None), |s| {
self.room_state_get(room_id, &StateEventType::RoomName, "")?.map_or(
Ok(None),
|s| {
serde_json::from_str(s.content.get())
.map(|c: RoomNameEventContent| Some(c.name))
.map_err(|e| {
error!(
"Invalid room name event in database for room {}. {}",
"Invalid room name event in database for room {}. \
{}",
room_id, e
);
Error::bad_database("Invalid room name event in database.")
Error::bad_database(
"Invalid room name event in database.",
)
})
})
},
)
}
pub(crate) fn get_avatar(&self, room_id: &RoomId) -> Result<JsOption<RoomAvatarEventContent>> {
self.room_state_get(room_id, &StateEventType::RoomAvatar, "")?
.map_or(Ok(JsOption::Undefined), |s| {
serde_json::from_str(s.content.get())
.map_err(|_| Error::bad_database("Invalid room avatar event in database."))
})
pub(crate) fn get_avatar(
&self,
room_id: &RoomId,
) -> Result<JsOption<RoomAvatarEventContent>> {
self.room_state_get(room_id, &StateEventType::RoomAvatar, "")?.map_or(
Ok(JsOption::Undefined),
|s| {
serde_json::from_str(s.content.get()).map_err(|_| {
Error::bad_database(
"Invalid room avatar event in database.",
)
})
},
)
}
// Allowed because this function uses `services()`
@ -313,8 +379,9 @@ impl Service {
target_user: &UserId,
state_lock: &MutexGuard<'_, ()>,
) -> bool {
let content = to_raw_value(&RoomMemberEventContent::new(MembershipState::Invite))
.expect("Event content always serializes");
let content =
to_raw_value(&RoomMemberEventContent::new(MembershipState::Invite))
.expect("Event content always serializes");
let new_event = PduBuilder {
event_type: ruma::events::TimelineEventType::RoomMember,
@ -336,18 +403,23 @@ impl Service {
room_id: &RoomId,
user_id: &UserId,
) -> Result<Option<RoomMemberEventContent>> {
self.room_state_get(room_id, &StateEventType::RoomMember, user_id.as_str())?
.map_or(Ok(None), |s| {
serde_json::from_str(s.content.get())
.map_err(|_| Error::bad_database("Invalid room member event in database."))
self.room_state_get(
room_id,
&StateEventType::RoomMember,
user_id.as_str(),
)?
.map_or(Ok(None), |s| {
serde_json::from_str(s.content.get()).map_err(|_| {
Error::bad_database("Invalid room member event in database.")
})
})
}
/// Checks if a given user can redact a given event
///
/// If `federation` is `true`, it allows redaction events from any user of the same server
/// as the original event sender, [as required by room versions >=
/// v3](https://spec.matrix.org/v1.10/rooms/v11/#handling-redactions)
/// If `federation` is `true`, it allows redaction events from any user of
/// the same server as the original event sender, [as required by room
/// versions >= v3](https://spec.matrix.org/v1.10/rooms/v11/#handling-redactions)
pub(crate) fn user_can_redact(
&self,
redacts: &EventId,
@ -359,18 +431,23 @@ impl Service {
.map_or_else(
// Falling back on m.room.create to judge power levels
|| {
if let Some(pdu) =
self.room_state_get(room_id, &StateEventType::RoomCreate, "")?
{
if let Some(pdu) = self.room_state_get(
room_id,
&StateEventType::RoomCreate,
"",
)? {
Ok(pdu.sender == sender
|| if let Ok(Some(pdu)) = services().rooms.timeline.get_pdu(redacts) {
|| if let Ok(Some(pdu)) =
services().rooms.timeline.get_pdu(redacts)
{
pdu.sender == sender
} else {
false
})
} else {
Err(Error::bad_database(
"No m.room.power_levels or m.room.create events in database for room",
"No m.room.power_levels or m.room.create events \
in database for room",
))
}
},
@ -380,11 +457,14 @@ impl Service {
.map(|e: RoomPowerLevels| {
e.user_can_redact_event_of_other(sender)
|| e.user_can_redact_own_event(sender)
&& if let Ok(Some(pdu)) =
services().rooms.timeline.get_pdu(redacts)
&& if let Ok(Some(pdu)) = services()
.rooms
.timeline
.get_pdu(redacts)
{
if federation {
pdu.sender().server_name() == sender.server_name()
pdu.sender().server_name()
== sender.server_name()
} else {
pdu.sender == sender
}
@ -393,7 +473,9 @@ impl Service {
}
})
.map_err(|_| {
Error::bad_database("Invalid m.room.power_levels event in database")
Error::bad_database(
"Invalid m.room.power_levels event in database",
)
})
},
)

View file

@ -9,14 +9,18 @@ use crate::{PduEvent, Result};
pub(crate) trait Data: Send + Sync {
/// Builds a StateMap by iterating over all keys that start
/// with state_hash, this gives the full state for the given state_hash.
async fn state_full_ids(&self, shortstatehash: u64) -> Result<HashMap<u64, Arc<EventId>>>;
async fn state_full_ids(
&self,
shortstatehash: u64,
) -> Result<HashMap<u64, Arc<EventId>>>;
async fn state_full(
&self,
shortstatehash: u64,
) -> Result<HashMap<(StateEventType, String), Arc<PduEvent>>>;
/// Returns a single PDU from `room_id` with key (`event_type`, `state_key`).
/// Returns a single PDU from `room_id` with key (`event_type`,
/// `state_key`).
fn state_get_id(
&self,
shortstatehash: u64,
@ -24,7 +28,8 @@ pub(crate) trait Data: Send + Sync {
state_key: &str,
) -> Result<Option<Arc<EventId>>>;
/// Returns a single PDU from `room_id` with key (`event_type`, `state_key`).
/// Returns a single PDU from `room_id` with key (`event_type`,
/// `state_key`).
fn state_get(
&self,
shortstatehash: u64,
@ -41,7 +46,8 @@ pub(crate) trait Data: Send + Sync {
room_id: &RoomId,
) -> Result<HashMap<(StateEventType, String), Arc<PduEvent>>>;
/// Returns a single PDU from `room_id` with key (`event_type`, `state_key`).
/// Returns a single PDU from `room_id` with key (`event_type`,
/// `state_key`).
fn room_state_get_id(
&self,
room_id: &RoomId,
@ -49,7 +55,8 @@ pub(crate) trait Data: Send + Sync {
state_key: &str,
) -> Result<Option<Arc<EventId>>>;
/// Returns a single PDU from `room_id` with key (`event_type`, `state_key`).
/// Returns a single PDU from `room_id` with key (`event_type`,
/// `state_key`).
fn room_state_get(
&self,
room_id: &RoomId,

View file

@ -2,7 +2,6 @@ mod data;
use std::{collections::HashSet, sync::Arc};
pub(crate) use data::Data;
use ruma::{
events::{
direct::DirectEvent,
@ -34,7 +33,8 @@ impl Service {
last_state: Option<Vec<Raw<AnyStrippedStateEvent>>>,
update_joined_count: bool,
) -> Result<()> {
// Keep track what remote users exist by adding them as "deactivated" users
// Keep track what remote users exist by adding them as "deactivated"
// users
if user_id.server_name() != services().globals.server_name() {
services().users.create(user_id, None)?;
// TODO: displayname, avatar url
@ -51,17 +51,26 @@ impl Service {
if let Some(predecessor) = services()
.rooms
.state_accessor
.room_state_get(room_id, &StateEventType::RoomCreate, "")?
.and_then(|create| serde_json::from_str(create.content.get()).ok())
.and_then(|content: RoomCreateEventContent| content.predecessor)
.room_state_get(
room_id,
&StateEventType::RoomCreate,
"",
)?
.and_then(|create| {
serde_json::from_str(create.content.get()).ok()
})
.and_then(|content: RoomCreateEventContent| {
content.predecessor
})
{
// Copy user settings from predecessor to the current room:
// Copy user settings from predecessor to the current
// room:
// - Push rules
//
// TODO: finish this once push rules are implemented.
//
// let mut push_rules_event_content: PushRulesEvent = account_data
// .get(
// let mut push_rules_event_content: PushRulesEvent =
// account_data .get(
// None,
// user_id,
// EventType::PushRules,
@ -90,8 +99,13 @@ impl Service {
)?
.map(|event| {
serde_json::from_str(event.get()).map_err(|e| {
warn!("Invalid account data event in db: {e:?}");
Error::BadDatabase("Invalid account data event in db.")
warn!(
"Invalid account data event in db: \
{e:?}"
);
Error::BadDatabase(
"Invalid account data event in db.",
)
})
})
{
@ -112,20 +126,32 @@ impl Service {
.get(
None,
user_id,
GlobalAccountDataEventType::Direct.to_string().into(),
GlobalAccountDataEventType::Direct
.to_string()
.into(),
)?
.map(|event| {
serde_json::from_str::<DirectEvent>(event.get()).map_err(|e| {
warn!("Invalid account data event in db: {e:?}");
Error::BadDatabase("Invalid account data event in db.")
})
serde_json::from_str::<DirectEvent>(event.get())
.map_err(|e| {
warn!(
"Invalid account data event in \
db: {e:?}"
);
Error::BadDatabase(
"Invalid account data event in db.",
)
})
})
{
let mut direct_event = direct_event?;
let mut room_ids_updated = false;
for room_ids in direct_event.content.0.values_mut() {
if room_ids.iter().any(|r| r == &predecessor.room_id) {
for room_ids in direct_event.content.0.values_mut()
{
if room_ids
.iter()
.any(|r| r == &predecessor.room_id)
{
room_ids.push(room_id.to_owned());
room_ids_updated = true;
}
@ -135,7 +161,9 @@ impl Service {
services().account_data.update(
None,
user_id,
GlobalAccountDataEventType::Direct.to_string().into(),
GlobalAccountDataEventType::Direct
.to_string()
.into(),
&serde_json::to_value(&direct_event)
.expect("to json always works"),
)?;
@ -160,9 +188,14 @@ impl Service {
.into(),
)?
.map(|event| {
serde_json::from_str::<IgnoredUserListEvent>(event.get()).map_err(|e| {
serde_json::from_str::<IgnoredUserListEvent>(
event.get(),
)
.map_err(|e| {
warn!("Invalid account data event in db: {e:?}");
Error::BadDatabase("Invalid account data event in db.")
Error::BadDatabase(
"Invalid account data event in db.",
)
})
})
.transpose()?
@ -199,7 +232,10 @@ impl Service {
}
#[tracing::instrument(skip(self, room_id))]
pub(crate) fn get_our_real_users(&self, room_id: &RoomId) -> Result<Arc<HashSet<OwnedUserId>>> {
pub(crate) fn get_our_real_users(
&self,
room_id: &RoomId,
) -> Result<Arc<HashSet<OwnedUserId>>> {
self.db.get_our_real_users(room_id)
}
@ -214,7 +250,11 @@ impl Service {
/// Makes a user forget a room.
#[tracing::instrument(skip(self))]
pub(crate) fn forget(&self, room_id: &RoomId, user_id: &UserId) -> Result<()> {
pub(crate) fn forget(
&self,
room_id: &RoomId,
user_id: &UserId,
) -> Result<()> {
self.db.forget(room_id, user_id)
}
@ -228,11 +268,16 @@ impl Service {
}
#[tracing::instrument(skip(self))]
pub(crate) fn server_in_room(&self, server: &ServerName, room_id: &RoomId) -> Result<bool> {
pub(crate) fn server_in_room(
&self,
server: &ServerName,
room_id: &RoomId,
) -> Result<bool> {
self.db.server_in_room(server, room_id)
}
/// Returns an iterator of all rooms a server participates in (as far as we know).
/// Returns an iterator of all rooms a server participates in (as far as we
/// know).
#[tracing::instrument(skip(self))]
pub(crate) fn server_rooms<'a>(
&'a self,
@ -251,12 +296,18 @@ impl Service {
}
#[tracing::instrument(skip(self))]
pub(crate) fn room_joined_count(&self, room_id: &RoomId) -> Result<Option<u64>> {
pub(crate) fn room_joined_count(
&self,
room_id: &RoomId,
) -> Result<Option<u64>> {
self.db.room_joined_count(room_id)
}
#[tracing::instrument(skip(self))]
pub(crate) fn room_invited_count(&self, room_id: &RoomId) -> Result<Option<u64>> {
pub(crate) fn room_invited_count(
&self,
room_id: &RoomId,
) -> Result<Option<u64>> {
self.db.room_invited_count(room_id)
}
@ -288,7 +339,11 @@ impl Service {
}
#[tracing::instrument(skip(self))]
pub(crate) fn get_left_count(&self, room_id: &RoomId, user_id: &UserId) -> Result<Option<u64>> {
pub(crate) fn get_left_count(
&self,
room_id: &RoomId,
user_id: &UserId,
) -> Result<Option<u64>> {
self.db.get_left_count(room_id, user_id)
}
@ -306,7 +361,9 @@ impl Service {
pub(crate) fn rooms_invited<'a>(
&'a self,
user_id: &UserId,
) -> impl Iterator<Item = Result<(OwnedRoomId, Vec<Raw<AnyStrippedStateEvent>>)>> + 'a {
) -> impl Iterator<
Item = Result<(OwnedRoomId, Vec<Raw<AnyStrippedStateEvent>>)>,
> + 'a {
self.db.rooms_invited(user_id)
}
@ -333,27 +390,44 @@ impl Service {
pub(crate) fn rooms_left<'a>(
&'a self,
user_id: &UserId,
) -> impl Iterator<Item = Result<(OwnedRoomId, Vec<Raw<AnySyncStateEvent>>)>> + 'a {
) -> impl Iterator<Item = Result<(OwnedRoomId, Vec<Raw<AnySyncStateEvent>>)>> + 'a
{
self.db.rooms_left(user_id)
}
#[tracing::instrument(skip(self))]
pub(crate) fn once_joined(&self, user_id: &UserId, room_id: &RoomId) -> Result<bool> {
pub(crate) fn once_joined(
&self,
user_id: &UserId,
room_id: &RoomId,
) -> Result<bool> {
self.db.once_joined(user_id, room_id)
}
#[tracing::instrument(skip(self))]
pub(crate) fn is_joined(&self, user_id: &UserId, room_id: &RoomId) -> Result<bool> {
pub(crate) fn is_joined(
&self,
user_id: &UserId,
room_id: &RoomId,
) -> Result<bool> {
self.db.is_joined(user_id, room_id)
}
#[tracing::instrument(skip(self))]
pub(crate) fn is_invited(&self, user_id: &UserId, room_id: &RoomId) -> Result<bool> {
pub(crate) fn is_invited(
&self,
user_id: &UserId,
room_id: &RoomId,
) -> Result<bool> {
self.db.is_invited(user_id, room_id)
}
#[tracing::instrument(skip(self))]
pub(crate) fn is_left(&self, user_id: &UserId, room_id: &RoomId) -> Result<bool> {
pub(crate) fn is_left(
&self,
user_id: &UserId,
room_id: &RoomId,
) -> Result<bool> {
self.db.is_left(user_id, room_id)
}
}

View file

@ -1,14 +1,19 @@
use std::{collections::HashSet, sync::Arc};
use crate::{service::appservice::RegistrationInfo, Result};
use ruma::{
events::{AnyStrippedStateEvent, AnySyncStateEvent},
serde::Raw,
OwnedRoomId, OwnedServerName, OwnedUserId, RoomId, ServerName, UserId,
};
use crate::{service::appservice::RegistrationInfo, Result};
pub(crate) trait Data: Send + Sync {
fn mark_as_once_joined(&self, user_id: &UserId, room_id: &RoomId) -> Result<()>;
fn mark_as_once_joined(
&self,
user_id: &UserId,
room_id: &RoomId,
) -> Result<()>;
fn mark_as_joined(&self, user_id: &UserId, room_id: &RoomId) -> Result<()>;
fn mark_as_invited(
&self,
@ -20,9 +25,16 @@ pub(crate) trait Data: Send + Sync {
fn update_joined_count(&self, room_id: &RoomId) -> Result<()>;
fn get_our_real_users(&self, room_id: &RoomId) -> Result<Arc<HashSet<OwnedUserId>>>;
fn get_our_real_users(
&self,
room_id: &RoomId,
) -> Result<Arc<HashSet<OwnedUserId>>>;
fn appservice_in_room(&self, room_id: &RoomId, appservice: &RegistrationInfo) -> Result<bool>;
fn appservice_in_room(
&self,
room_id: &RoomId,
appservice: &RegistrationInfo,
) -> Result<bool>;
/// Makes a user forget a room.
fn forget(&self, room_id: &RoomId, user_id: &UserId) -> Result<()>;
@ -33,9 +45,14 @@ pub(crate) trait Data: Send + Sync {
room_id: &RoomId,
) -> Box<dyn Iterator<Item = Result<OwnedServerName>> + 'a>;
fn server_in_room(&self, server: &ServerName, room_id: &RoomId) -> Result<bool>;
fn server_in_room(
&self,
server: &ServerName,
room_id: &RoomId,
) -> Result<bool>;
/// Returns an iterator of all rooms a server participates in (as far as we know).
/// Returns an iterator of all rooms a server participates in (as far as we
/// know).
fn server_rooms<'a>(
&'a self,
server: &ServerName,
@ -63,9 +80,17 @@ pub(crate) trait Data: Send + Sync {
room_id: &RoomId,
) -> Box<dyn Iterator<Item = Result<OwnedUserId>> + 'a>;
fn get_invite_count(&self, room_id: &RoomId, user_id: &UserId) -> Result<Option<u64>>;
fn get_invite_count(
&self,
room_id: &RoomId,
user_id: &UserId,
) -> Result<Option<u64>>;
fn get_left_count(&self, room_id: &RoomId, user_id: &UserId) -> Result<Option<u64>>;
fn get_left_count(
&self,
room_id: &RoomId,
user_id: &UserId,
) -> Result<Option<u64>>;
/// Returns an iterator over all rooms this user joined.
fn rooms_joined<'a>(
@ -78,7 +103,11 @@ pub(crate) trait Data: Send + Sync {
fn rooms_invited<'a>(
&'a self,
user_id: &UserId,
) -> Box<dyn Iterator<Item = Result<(OwnedRoomId, Vec<Raw<AnyStrippedStateEvent>>)>> + 'a>;
) -> Box<
dyn Iterator<
Item = Result<(OwnedRoomId, Vec<Raw<AnyStrippedStateEvent>>)>,
> + 'a,
>;
fn invite_state(
&self,
@ -97,7 +126,10 @@ pub(crate) trait Data: Send + Sync {
fn rooms_left<'a>(
&'a self,
user_id: &UserId,
) -> Box<dyn Iterator<Item = Result<(OwnedRoomId, Vec<Raw<AnySyncStateEvent>>)>> + 'a>;
) -> Box<
dyn Iterator<Item = Result<(OwnedRoomId, Vec<Raw<AnySyncStateEvent>>)>>
+ 'a,
>;
fn once_joined(&self, user_id: &UserId, room_id: &RoomId) -> Result<bool>;

View file

@ -9,9 +9,8 @@ pub(crate) use data::Data;
use lru_cache::LruCache;
use ruma::{EventId, RoomId};
use crate::{services, utils, Result};
use self::data::StateDiff;
use crate::{services, utils, Result};
pub(crate) struct Service {
pub(crate) db: &'static dyn Data,
@ -37,7 +36,8 @@ pub(crate) struct Service {
pub(crate) type CompressedStateEvent = [u8; 2 * size_of::<u64>()];
impl Service {
/// Returns a stack with info on shortstatehash, full state, added diff and removed diff for the selected shortstatehash and each parent layer.
/// Returns a stack with info on shortstatehash, full state, added diff and
/// removed diff for the selected shortstatehash and each parent layer.
#[allow(clippy::type_complexity)]
#[tracing::instrument(skip(self))]
pub(crate) fn load_shortstatehash_info(
@ -55,11 +55,8 @@ impl Service {
Arc<HashSet<CompressedStateEvent>>,
)>,
> {
if let Some(r) = self
.stateinfo_cache
.lock()
.unwrap()
.get_mut(&shortstatehash)
if let Some(r) =
self.stateinfo_cache.lock().unwrap().get_mut(&shortstatehash)
{
return Ok(r.clone());
}
@ -79,7 +76,12 @@ impl Service {
state.remove(r);
}
response.push((shortstatehash, Arc::new(state), added, Arc::new(removed)));
response.push((
shortstatehash,
Arc::new(state),
added,
Arc::new(removed),
));
self.stateinfo_cache
.lock()
@ -88,7 +90,8 @@ impl Service {
Ok(response)
} else {
let response = vec![(shortstatehash, added.clone(), added, removed)];
let response =
vec![(shortstatehash, added.clone(), added, removed)];
self.stateinfo_cache
.lock()
.unwrap()
@ -132,19 +135,24 @@ impl Service {
))
}
/// Creates a new shortstatehash that often is just a diff to an already existing
/// shortstatehash and therefore very efficient.
/// Creates a new shortstatehash that often is just a diff to an already
/// existing shortstatehash and therefore very efficient.
///
/// There are multiple layers of diffs. The bottom layer 0 always contains the full state. Layer
/// 1 contains diffs to states of layer 0, layer 2 diffs to layer 1 and so on. If layer n > 0
/// grows too big, it will be combined with layer n-1 to create a new diff on layer n-1 that's
/// based on layer n-2. If that layer is also too big, it will recursively fix above layers too.
/// There are multiple layers of diffs. The bottom layer 0 always contains
/// the full state. Layer 1 contains diffs to states of layer 0, layer 2
/// diffs to layer 1 and so on. If layer n > 0 grows too big, it will be
/// combined with layer n-1 to create a new diff on layer n-1 that's
/// based on layer n-2. If that layer is also too big, it will recursively
/// fix above layers too.
///
/// * `shortstatehash` - Shortstatehash of this state
/// * `statediffnew` - Added to base. Each vec is shortstatekey+shorteventid
/// * `statediffremoved` - Removed from base. Each vec is shortstatekey+shorteventid
/// * `diff_to_sibling` - Approximately how much the diff grows each time for this layer
/// * `parent_states` - A stack with info on shortstatehash, full state, added diff and removed diff for each parent layer
/// * `statediffremoved` - Removed from base. Each vec is
/// shortstatekey+shorteventid
/// * `diff_to_sibling` - Approximately how much the diff grows each time
/// for this layer
/// * `parent_states` - A stack with info on shortstatehash, full state,
/// added diff and removed diff for each parent layer
#[allow(clippy::type_complexity)]
#[tracing::instrument(skip(
self,
@ -185,7 +193,8 @@ impl Service {
// It was not added in the parent and we removed it
parent_removed.insert(*removed);
}
// Else it was added in the parent and we removed it again. We can forget this change
// Else it was added in the parent and we removed it again. We
// can forget this change
}
for new in statediffnew.iter() {
@ -193,7 +202,8 @@ impl Service {
// It was not touched in the parent and we added it
parent_new.insert(*new);
}
// Else it was removed in the parent and we added it again. We can forget this change
// Else it was removed in the parent and we added it again. We
// can forget this change
}
self.save_state_from_diff(
@ -238,7 +248,8 @@ impl Service {
// It was not added in the parent and we removed it
parent_removed.insert(*removed);
}
// Else it was added in the parent and we removed it again. We can forget this change
// Else it was added in the parent and we removed it again. We
// can forget this change
}
for new in statediffnew.iter() {
@ -246,7 +257,8 @@ impl Service {
// It was not touched in the parent and we added it
parent_new.insert(*new);
}
// Else it was removed in the parent and we added it again. We can forget this change
// Else it was removed in the parent and we added it again. We
// can forget this change
}
self.save_state_from_diff(
@ -271,7 +283,8 @@ impl Service {
Ok(())
}
/// Returns the new shortstatehash, and the state diff from the previous room state
/// Returns the new shortstatehash, and the state diff from the previous
/// room state
#[allow(clippy::type_complexity)]
pub(crate) fn save_state(
&self,
@ -282,7 +295,8 @@ impl Service {
Arc<HashSet<CompressedStateEvent>>,
Arc<HashSet<CompressedStateEvent>>,
)> {
let previous_shortstatehash = services().rooms.state.get_room_shortstatehash(room_id)?;
let previous_shortstatehash =
services().rooms.state.get_room_shortstatehash(room_id)?;
let state_hash = utils::calculate_hash(
&new_state_ids_compressed
@ -291,10 +305,8 @@ impl Service {
.collect::<Vec<_>>(),
);
let (new_shortstatehash, already_existed) = services()
.rooms
.short
.get_or_create_shortstatehash(&state_hash)?;
let (new_shortstatehash, already_existed) =
services().rooms.short.get_or_create_shortstatehash(&state_hash)?;
if Some(new_shortstatehash) == previous_shortstatehash {
return Ok((
@ -304,26 +316,28 @@ impl Service {
));
}
let states_parents = previous_shortstatehash
.map_or_else(|| Ok(Vec::new()), |p| self.load_shortstatehash_info(p))?;
let states_parents = previous_shortstatehash.map_or_else(
|| Ok(Vec::new()),
|p| self.load_shortstatehash_info(p),
)?;
let (statediffnew, statediffremoved) = if let Some(parent_stateinfo) = states_parents.last()
{
let statediffnew: HashSet<_> = new_state_ids_compressed
.difference(&parent_stateinfo.1)
.copied()
.collect();
let (statediffnew, statediffremoved) =
if let Some(parent_stateinfo) = states_parents.last() {
let statediffnew: HashSet<_> = new_state_ids_compressed
.difference(&parent_stateinfo.1)
.copied()
.collect();
let statediffremoved: HashSet<_> = parent_stateinfo
.1
.difference(&new_state_ids_compressed)
.copied()
.collect();
let statediffremoved: HashSet<_> = parent_stateinfo
.1
.difference(&new_state_ids_compressed)
.copied()
.collect();
(Arc::new(statediffnew), Arc::new(statediffremoved))
} else {
(new_state_ids_compressed, Arc::new(HashSet::new()))
};
(Arc::new(statediffnew), Arc::new(statediffremoved))
} else {
(new_state_ids_compressed, Arc::new(HashSet::new()))
};
if !already_existed {
self.save_state_from_diff(

View file

@ -11,5 +11,9 @@ pub(crate) struct StateDiff {
pub(crate) trait Data: Send + Sync {
fn get_statediff(&self, shortstatehash: u64) -> Result<StateDiff>;
fn save_statediff(&self, shortstatehash: u64, diff: StateDiff) -> Result<()>;
fn save_statediff(
&self,
shortstatehash: u64,
diff: StateDiff,
) -> Result<()>;
}

View file

@ -6,7 +6,6 @@ use ruma::{
events::relation::BundledThread,
uint, CanonicalJsonObject, CanonicalJsonValue, EventId, RoomId, UserId,
};
use serde_json::json;
use crate::{services, Error, PduEvent, Result};
@ -26,51 +25,64 @@ impl Service {
self.db.threads_until(user_id, room_id, until, include)
}
pub(crate) fn add_to_thread(&self, root_event_id: &EventId, pdu: &PduEvent) -> Result<()> {
let root_id = &services()
.rooms
.timeline
.get_pdu_id(root_event_id)?
.ok_or_else(|| {
Error::BadRequest(
ErrorKind::InvalidParam,
"Invalid event id in thread message",
)
})?;
pub(crate) fn add_to_thread(
&self,
root_event_id: &EventId,
pdu: &PduEvent,
) -> Result<()> {
let root_id =
&services().rooms.timeline.get_pdu_id(root_event_id)?.ok_or_else(
|| {
Error::BadRequest(
ErrorKind::InvalidParam,
"Invalid event id in thread message",
)
},
)?;
let root_pdu = services()
.rooms
.timeline
.get_pdu_from_id(root_id)?
.ok_or_else(|| {
Error::BadRequest(ErrorKind::InvalidParam, "Thread root pdu not found")
})?;
let root_pdu =
services().rooms.timeline.get_pdu_from_id(root_id)?.ok_or_else(
|| {
Error::BadRequest(
ErrorKind::InvalidParam,
"Thread root pdu not found",
)
},
)?;
let mut root_pdu_json = services()
.rooms
.timeline
.get_pdu_json_from_id(root_id)?
.ok_or_else(|| {
Error::BadRequest(ErrorKind::InvalidParam, "Thread root pdu not found")
Error::BadRequest(
ErrorKind::InvalidParam,
"Thread root pdu not found",
)
})?;
if let CanonicalJsonValue::Object(unsigned) = root_pdu_json
.entry("unsigned".to_owned())
.or_insert_with(|| CanonicalJsonValue::Object(CanonicalJsonObject::default()))
if let CanonicalJsonValue::Object(unsigned) =
root_pdu_json.entry("unsigned".to_owned()).or_insert_with(|| {
CanonicalJsonValue::Object(CanonicalJsonObject::default())
})
{
if let Some(mut relations) = unsigned
.get("m.relations")
.and_then(|r| r.as_object())
.and_then(|r| r.get("m.thread"))
.and_then(|relations| {
serde_json::from_value::<BundledThread>(relations.clone().into()).ok()
serde_json::from_value::<BundledThread>(
relations.clone().into(),
)
.ok()
})
{
// Thread already existed
relations.count += uint!(1);
relations.latest_event = pdu.to_message_like_event();
let content = serde_json::to_value(relations).expect("to_value always works");
let content = serde_json::to_value(relations)
.expect("to_value always works");
unsigned.insert(
"m.relations".to_owned(),
@ -86,7 +98,8 @@ impl Service {
current_user_participated: true,
};
let content = serde_json::to_value(relations).expect("to_value always works");
let content = serde_json::to_value(relations)
.expect("to_value always works");
unsigned.insert(
"m.relations".to_owned(),
@ -96,10 +109,11 @@ impl Service {
);
}
services()
.rooms
.timeline
.replace_pdu(root_id, &root_pdu_json, &root_pdu)?;
services().rooms.timeline.replace_pdu(
root_id,
&root_pdu_json,
&root_pdu,
)?;
}
let mut users = Vec::new();

View file

@ -1,5 +1,9 @@
use ruma::{
api::client::threads::get_threads::v1::IncludeThreads, OwnedUserId, RoomId,
UserId,
};
use crate::{PduEvent, Result};
use ruma::{api::client::threads::get_threads::v1::IncludeThreads, OwnedUserId, RoomId, UserId};
pub(crate) trait Data: Send + Sync {
#[allow(clippy::type_complexity)]
@ -11,6 +15,13 @@ pub(crate) trait Data: Send + Sync {
include: &'a IncludeThreads,
) -> Result<Box<dyn Iterator<Item = Result<(u64, PduEvent)>> + 'a>>;
fn update_participants(&self, root_id: &[u8], participants: &[OwnedUserId]) -> Result<()>;
fn get_participants(&self, root_id: &[u8]) -> Result<Option<Vec<OwnedUserId>>>;
fn update_participants(
&self,
root_id: &[u8],
participants: &[OwnedUserId],
) -> Result<()>;
fn get_participants(
&self,
root_id: &[u8],
) -> Result<Option<Vec<OwnedUserId>>>;
}

View file

@ -7,29 +7,31 @@ use std::{
};
pub(crate) use data::Data;
use ruma::{
api::{client::error::ErrorKind, federation},
canonical_json::to_canonical_value,
events::{
push_rules::PushRulesEvent,
room::{
create::RoomCreateEventContent, encrypted::Relation, member::MembershipState,
power_levels::RoomPowerLevelsEventContent, redaction::RoomRedactionEventContent,
create::RoomCreateEventContent, encrypted::Relation,
member::MembershipState, power_levels::RoomPowerLevelsEventContent,
redaction::RoomRedactionEventContent,
},
GlobalAccountDataEventType, StateEventType, TimelineEventType,
},
push::{Action, Ruleset, Tweak},
serde::Base64,
state_res::{self, Event, RoomVersion},
uint, user_id, CanonicalJsonObject, CanonicalJsonValue, EventId, OwnedEventId, OwnedRoomId,
OwnedServerName, RoomId, RoomVersionId, ServerName, UserId,
uint, user_id, CanonicalJsonObject, CanonicalJsonValue, EventId,
OwnedEventId, OwnedRoomId, OwnedServerName, RoomId, RoomVersionId,
ServerName, UserId,
};
use serde::Deserialize;
use serde_json::value::{to_raw_value, RawValue as RawJsonValue};
use tokio::sync::{Mutex, MutexGuard, RwLock};
use tracing::{error, info, warn};
use super::state_compressor::CompressedStateEvent;
use crate::{
api::server_server,
service::{
@ -39,8 +41,6 @@ use crate::{
services, utils, Error, PduEvent, Result,
};
use super::state_compressor::CompressedStateEvent;
#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)]
pub(crate) enum PduCount {
Backfilled(u64),
@ -48,8 +48,8 @@ pub(crate) enum PduCount {
}
impl PduCount {
pub(crate) const MIN: Self = Self::Backfilled(u64::MAX);
pub(crate) const MAX: Self = Self::Normal(u64::MAX);
pub(crate) const MIN: Self = Self::Backfilled(u64::MAX);
pub(crate) fn try_from_string(token: &str) -> Result<Self> {
if let Some(stripped) = token.strip_prefix('-') {
@ -57,7 +57,12 @@ impl PduCount {
} else {
token.parse().map(PduCount::Normal)
}
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid pagination token."))
.map_err(|_| {
Error::BadRequest(
ErrorKind::InvalidParam,
"Invalid pagination token.",
)
})
}
pub(crate) fn stringify(&self) -> String {
@ -93,7 +98,10 @@ pub(crate) struct Service {
impl Service {
#[tracing::instrument(skip(self))]
pub(crate) fn first_pdu_in_room(&self, room_id: &RoomId) -> Result<Option<Arc<PduEvent>>> {
pub(crate) fn first_pdu_in_room(
&self,
room_id: &RoomId,
) -> Result<Option<Arc<PduEvent>>> {
self.all_pdus(user_id!("@doesntmatter:grapevine"), room_id)?
.next()
.map(|o| o.map(|(_, p)| Arc::new(p)))
@ -110,12 +118,18 @@ impl Service {
}
/// Returns the `count` of this pdu's id.
pub(crate) fn get_pdu_count(&self, event_id: &EventId) -> Result<Option<PduCount>> {
pub(crate) fn get_pdu_count(
&self,
event_id: &EventId,
) -> Result<Option<PduCount>> {
self.db.get_pdu_count(event_id)
}
/// Returns the json of a pdu.
pub(crate) fn get_pdu_json(&self, event_id: &EventId) -> Result<Option<CanonicalJsonObject>> {
pub(crate) fn get_pdu_json(
&self,
event_id: &EventId,
) -> Result<Option<CanonicalJsonObject>> {
self.db.get_pdu_json(event_id)
}
@ -128,21 +142,30 @@ impl Service {
}
/// Returns the pdu's id.
pub(crate) fn get_pdu_id(&self, event_id: &EventId) -> Result<Option<Vec<u8>>> {
pub(crate) fn get_pdu_id(
&self,
event_id: &EventId,
) -> Result<Option<Vec<u8>>> {
self.db.get_pdu_id(event_id)
}
/// Returns the pdu.
///
/// Checks the `eventid_outlierpdu` Tree if not found in the timeline.
pub(crate) fn get_pdu(&self, event_id: &EventId) -> Result<Option<Arc<PduEvent>>> {
pub(crate) fn get_pdu(
&self,
event_id: &EventId,
) -> Result<Option<Arc<PduEvent>>> {
self.db.get_pdu(event_id)
}
/// Returns the pdu.
///
/// This does __NOT__ check the outliers `Tree`.
pub(crate) fn get_pdu_from_id(&self, pdu_id: &[u8]) -> Result<Option<PduEvent>> {
pub(crate) fn get_pdu_from_id(
&self,
pdu_id: &[u8],
) -> Result<Option<PduEvent>> {
self.db.get_pdu_from_id(pdu_id)
}
@ -167,8 +190,8 @@ impl Service {
/// Creates a new persisted data unit and adds it to a room.
///
/// By this point the incoming event should be fully authenticated, no auth happens
/// in `append_pdu`.
/// By this point the incoming event should be fully authenticated, no auth
/// happens in `append_pdu`.
///
/// Returns pdu id
#[tracing::instrument(skip(self, pdu, pdu_json, leaves))]
@ -186,13 +209,15 @@ impl Service {
.get_shortroomid(&pdu.room_id)?
.expect("room exists");
// Make unsigned fields correct. This is not properly documented in the spec, but state
// events need to have previous content in the unsigned field, so clients can easily
// interpret things like membership changes
// Make unsigned fields correct. This is not properly documented in the
// spec, but state events need to have previous content in the
// unsigned field, so clients can easily interpret things like
// membership changes
if let Some(state_key) = &pdu.state_key {
if let CanonicalJsonValue::Object(unsigned) = pdu_json
.entry("unsigned".to_owned())
.or_insert_with(|| CanonicalJsonValue::Object(CanonicalJsonObject::default()))
if let CanonicalJsonValue::Object(unsigned) =
pdu_json.entry("unsigned".to_owned()).or_insert_with(|| {
CanonicalJsonValue::Object(CanonicalJsonObject::default())
})
{
if let Some(shortstatehash) = services()
.rooms
@ -203,14 +228,20 @@ impl Service {
if let Some(prev_state) = services()
.rooms
.state_accessor
.state_get(shortstatehash, &pdu.kind.to_string().into(), state_key)
.state_get(
shortstatehash,
&pdu.kind.to_string().into(),
state_key,
)
.unwrap()
{
unsigned.insert(
"prev_content".to_owned(),
CanonicalJsonValue::Object(
utils::to_canonical_object(prev_state.content.clone())
.expect("event is valid, we just created it"),
utils::to_canonical_object(
prev_state.content.clone(),
)
.expect("event is valid, we just created it"),
),
);
}
@ -225,10 +256,11 @@ impl Service {
.rooms
.pdu_metadata
.mark_as_referenced(&pdu.room_id, &pdu.prev_events)?;
services()
.rooms
.state
.set_forward_extremities(&pdu.room_id, leaves, state_lock)?;
services().rooms.state.set_forward_extremities(
&pdu.room_id,
leaves,
state_lock,
)?;
let mutex_insert = Arc::clone(
services()
@ -242,13 +274,13 @@ impl Service {
let insert_lock = mutex_insert.lock().await;
let count1 = services().globals.next_count()?;
// Mark as read first so the sending client doesn't get a notification even if appending
// fails
services()
.rooms
.edus
.read_receipt
.private_read_set(&pdu.room_id, &pdu.sender, count1)?;
// Mark as read first so the sending client doesn't get a notification
// even if appending fails
services().rooms.edus.read_receipt.private_read_set(
&pdu.room_id,
&pdu.sender,
count1,
)?;
services()
.rooms
.user
@ -269,8 +301,9 @@ impl Service {
.state_accessor
.room_state_get(&pdu.room_id, &StateEventType::RoomPowerLevels, "")?
.map(|ev| {
serde_json::from_str(ev.content.get())
.map_err(|_| Error::bad_database("invalid m.room.power_levels event"))
serde_json::from_str(ev.content.get()).map_err(|_| {
Error::bad_database("invalid m.room.power_levels event")
})
})
.transpose()?
.unwrap_or_default();
@ -280,10 +313,8 @@ impl Service {
let mut notifies = Vec::new();
let mut highlights = Vec::new();
let mut push_target = services()
.rooms
.state_cache
.get_our_real_users(&pdu.room_id)?;
let mut push_target =
services().rooms.state_cache.get_our_real_users(&pdu.room_id)?;
if pdu.kind == TimelineEventType::RoomMember {
if let Some(state_key) = &pdu.state_key {
@ -312,8 +343,13 @@ impl Service {
GlobalAccountDataEventType::PushRules.to_string().into(),
)?
.map(|event| {
serde_json::from_str::<PushRulesEvent>(event.get())
.map_err(|_| Error::bad_database("Invalid push rules event in db."))
serde_json::from_str::<PushRulesEvent>(event.get()).map_err(
|_| {
Error::bad_database(
"Invalid push rules event in db.",
)
},
)
})
.transpose()?
.map_or_else(
@ -353,12 +389,16 @@ impl Service {
}
}
self.db
.increment_notification_counts(&pdu.room_id, notifies, highlights)?;
self.db.increment_notification_counts(
&pdu.room_id,
notifies,
highlights,
)?;
match pdu.kind {
TimelineEventType::RoomRedaction => {
let room_version_id = services().rooms.state.get_room_version(&pdu.room_id)?;
let room_version_id =
services().rooms.state.get_room_version(&pdu.room_id)?;
match room_version_id {
RoomVersionId::V1
| RoomVersionId::V2
@ -383,10 +423,14 @@ impl Service {
}
RoomVersionId::V11 => {
let content =
serde_json::from_str::<RoomRedactionEventContent>(pdu.content.get())
.map_err(|_| {
Error::bad_database("Invalid content in redaction pdu.")
})?;
serde_json::from_str::<RoomRedactionEventContent>(
pdu.content.get(),
)
.map_err(|_| {
Error::bad_database(
"Invalid content in redaction pdu.",
)
})?;
if let Some(redact_id) = &content.redacts {
if services().rooms.state_accessor.user_can_redact(
redact_id,
@ -398,7 +442,9 @@ impl Service {
}
}
}
_ => unreachable!("Validity of room version already checked"),
_ => {
unreachable!("Validity of room version already checked")
}
};
}
TimelineEventType::SpaceChild => {
@ -423,19 +469,27 @@ impl Service {
let target_user_id = UserId::parse(state_key.clone())
.expect("This state_key was previously validated");
let content = serde_json::from_str::<ExtractMembership>(pdu.content.get())
.map_err(|_| Error::bad_database("Invalid content in pdu."))?;
let content = serde_json::from_str::<ExtractMembership>(
pdu.content.get(),
)
.map_err(|_| {
Error::bad_database("Invalid content in pdu.")
})?;
let invite_state = match content.membership {
MembershipState::Invite => {
let state = services().rooms.state.calculate_invite_state(pdu)?;
let state = services()
.rooms
.state
.calculate_invite_state(pdu)?;
Some(state)
}
_ => None,
};
// Update our membership info, we do this here incase a user is invited
// and immediately leaves we need the DB to record the invite event for auth
// Update our membership info, we do this here incase a user
// is invited and immediately leaves we
// need the DB to record the invite event for auth
services().rooms.state_cache.update_membership(
&pdu.room_id,
&target_user_id,
@ -452,14 +506,18 @@ impl Service {
body: Option<String>,
}
let content = serde_json::from_str::<ExtractBody>(pdu.content.get())
.map_err(|_| Error::bad_database("Invalid content in pdu."))?;
let content =
serde_json::from_str::<ExtractBody>(pdu.content.get())
.map_err(|_| {
Error::bad_database("Invalid content in pdu.")
})?;
if let Some(body) = content.body {
services()
.rooms
.search
.index_pdu(shortroomid, &pdu_id, &body)?;
services().rooms.search.index_pdu(
shortroomid,
&pdu_id,
&body,
)?;
let server_user = format!(
"@{}:{}",
@ -471,18 +529,25 @@ impl Service {
services().globals.server_name()
);
let to_grapevine = body.starts_with(&format!("{server_user}: "))
let to_grapevine = body
.starts_with(&format!("{server_user}: "))
|| body.starts_with(&format!("{server_user} "))
|| body == format!("{server_user}:")
|| body == server_user;
// This will evaluate to false if the emergency password is set up so that
// the administrator can execute commands as grapevine
// This will evaluate to false if the emergency password is
// set up so that the administrator can
// execute commands as grapevine
let from_grapevine = pdu.sender == server_user
&& services().globals.emergency_password().is_none();
if let Some(admin_room) = services().admin.get_admin_room()? {
if to_grapevine && !from_grapevine && admin_room == pdu.room_id {
if let Some(admin_room) =
services().admin.get_admin_room()?
{
if to_grapevine
&& !from_grapevine
&& admin_room == pdu.room_id
{
services().admin.process_message(body);
}
}
@ -493,7 +558,9 @@ impl Service {
// Update Relationships
if let Ok(content) = serde_json::from_str::<ExtractRelatesToEventId>(pdu.content.get()) {
if let Ok(content) =
serde_json::from_str::<ExtractRelatesToEventId>(pdu.content.get())
{
if let Some(related_pducount) = services()
.rooms
.timeline
@ -506,9 +573,13 @@ impl Service {
}
}
if let Ok(content) = serde_json::from_str::<ExtractRelatesTo>(pdu.content.get()) {
if let Ok(content) =
serde_json::from_str::<ExtractRelatesTo>(pdu.content.get())
{
match content.relates_to {
Relation::Reply { in_reply_to } => {
Relation::Reply {
in_reply_to,
} => {
// We need to do it again here, because replies don't have
// event_id as a top level field
if let Some(related_pducount) = services()
@ -516,10 +587,10 @@ impl Service {
.timeline
.get_pdu_count(&in_reply_to.event_id)?
{
services()
.rooms
.pdu_metadata
.add_relation(PduCount::Normal(count2), related_pducount)?;
services().rooms.pdu_metadata.add_relation(
PduCount::Normal(count2),
related_pducount,
)?;
}
}
Relation::Thread(thread) => {
@ -539,21 +610,24 @@ impl Service {
.state_cache
.appservice_in_room(&pdu.room_id, appservice)?
{
services()
.sending
.send_pdu_appservice(appservice.registration.id.clone(), pdu_id.clone())?;
services().sending.send_pdu_appservice(
appservice.registration.id.clone(),
pdu_id.clone(),
)?;
continue;
}
// If the RoomMember event has a non-empty state_key, it is targeted at someone.
// If it is our appservice user, we send this PDU to it.
// If the RoomMember event has a non-empty state_key, it is targeted
// at someone. If it is our appservice user, we send
// this PDU to it.
if pdu.kind == TimelineEventType::RoomMember {
if let Some(state_key_uid) = &pdu
.state_key
.as_ref()
.and_then(|state_key| UserId::parse(state_key.as_str()).ok())
if let Some(state_key_uid) =
&pdu.state_key.as_ref().and_then(|state_key| {
UserId::parse(state_key.as_str()).ok()
})
{
let appservice_uid = appservice.registration.sender_localpart.as_str();
let appservice_uid =
appservice.registration.sender_localpart.as_str();
if state_key_uid == appservice_uid {
services().sending.send_pdu_appservice(
appservice.registration.id.clone(),
@ -567,10 +641,9 @@ impl Service {
let matching_users = |users: &NamespaceRegex| {
appservice.users.is_match(pdu.sender.as_str())
|| pdu.kind == TimelineEventType::RoomMember
&& pdu
.state_key
.as_ref()
.map_or(false, |state_key| users.is_match(state_key))
&& pdu.state_key.as_ref().map_or(false, |state_key| {
users.is_match(state_key)
})
};
let matching_aliases = |aliases: &NamespaceRegex| {
services()
@ -585,9 +658,10 @@ impl Service {
|| appservice.rooms.is_match(pdu.room_id.as_str())
|| matching_users(&appservice.users)
{
services()
.sending
.send_pdu_appservice(appservice.registration.id.clone(), pdu_id.clone())?;
services().sending.send_pdu_appservice(
appservice.registration.id.clone(),
pdu_id.clone(),
)?;
}
}
@ -620,13 +694,13 @@ impl Service {
.collect();
// If there was no create event yet, assume we are creating a room
let room_version_id = services()
.rooms
.state
.get_room_version(room_id)
.or_else(|_| {
let room_version_id =
services().rooms.state.get_room_version(room_id).or_else(|_| {
if event_type == TimelineEventType::RoomCreate {
let content = serde_json::from_str::<RoomCreateEventContent>(content.get())
let content =
serde_json::from_str::<RoomCreateEventContent>(
content.get(),
)
.expect("Invalid content in RoomCreate pdu.");
Ok(content.room_version)
} else {
@ -637,7 +711,8 @@ impl Service {
}
})?;
let room_version = RoomVersion::new(&room_version_id).expect("room version is supported");
let room_version = RoomVersion::new(&room_version_id)
.expect("room version is supported");
let auth_events = services().rooms.state.get_auth_events(
room_id,
@ -658,18 +733,22 @@ impl Service {
let mut unsigned = unsigned.unwrap_or_default();
if let Some(state_key) = &state_key {
if let Some(prev_pdu) = services().rooms.state_accessor.room_state_get(
room_id,
&event_type.to_string().into(),
state_key,
)? {
if let Some(prev_pdu) =
services().rooms.state_accessor.room_state_get(
room_id,
&event_type.to_string().into(),
state_key,
)?
{
unsigned.insert(
"prev_content".to_owned(),
serde_json::from_str(prev_pdu.content.get()).expect("string is valid json"),
serde_json::from_str(prev_pdu.content.get())
.expect("string is valid json"),
);
unsigned.insert(
"prev_sender".to_owned(),
serde_json::to_value(&prev_pdu.sender).expect("UserId::to_value always works"),
serde_json::to_value(&prev_pdu.sender)
.expect("UserId::to_value always works"),
);
}
}
@ -694,7 +773,9 @@ impl Service {
unsigned: if unsigned.is_empty() {
None
} else {
Some(to_raw_value(&unsigned).expect("to_raw_value always works"))
Some(
to_raw_value(&unsigned).expect("to_raw_value always works"),
)
},
hashes: EventHash {
sha256: "aaa".to_owned(),
@ -722,8 +803,8 @@ impl Service {
}
// Hash and sign
let mut pdu_json =
utils::to_canonical_object(&pdu).expect("event is valid, we just created it");
let mut pdu_json = utils::to_canonical_object(&pdu)
.expect("event is valid, we just created it");
pdu_json.remove("event_id");
@ -769,16 +850,15 @@ impl Service {
);
// Generate short event id
let _shorteventid = services()
.rooms
.short
.get_or_create_shorteventid(&pdu.event_id)?;
let _shorteventid =
services().rooms.short.get_or_create_shorteventid(&pdu.event_id)?;
Ok((pdu, pdu_json))
}
/// Creates a new persisted data unit and adds it to a room. This function takes a
/// roomid_mutex_state, meaning that only this function is able to mutate the room state.
/// Creates a new persisted data unit and adds it to a room. This function
/// takes a roomid_mutex_state, meaning that only this function is able
/// to mutate the room state.
#[tracing::instrument(skip(self, state_lock))]
pub(crate) async fn build_and_append_pdu(
&self,
@ -788,8 +868,12 @@ impl Service {
// Take mutex guard to make sure users get the room state mutex
state_lock: &MutexGuard<'_, ()>,
) -> Result<Arc<EventId>> {
let (pdu, pdu_json) =
self.create_hash_and_sign_event(pdu_builder, sender, room_id, state_lock)?;
let (pdu, pdu_json) = self.create_hash_and_sign_event(
pdu_builder,
sender,
room_id,
state_lock,
)?;
if let Some(admin_room) = services().admin.get_admin_room()? {
if admin_room == room_id {
@ -820,15 +904,24 @@ impl Service {
"grapevine"
},
);
let content = serde_json::from_str::<ExtractMembership>(pdu.content.get())
.map_err(|_| Error::bad_database("Invalid content in pdu."))?;
let content =
serde_json::from_str::<ExtractMembership>(
pdu.content.get(),
)
.map_err(|_| {
Error::bad_database("Invalid content in pdu.")
})?;
if content.membership == MembershipState::Leave {
if target == server_user {
warn!("Grapevine user cannot leave from admins room");
warn!(
"Grapevine user cannot leave from admins \
room"
);
return Err(Error::BadRequest(
ErrorKind::Forbidden,
"Grapevine user cannot leave from admins room.",
"Grapevine user cannot leave from admins \
room.",
));
}
@ -841,7 +934,9 @@ impl Service {
.filter(|m| m != target)
.count();
if count < 2 {
warn!("Last admin cannot leave from admins room");
warn!(
"Last admin cannot leave from admins room"
);
return Err(Error::BadRequest(
ErrorKind::Forbidden,
"Last admin cannot leave from admins room.",
@ -849,12 +944,18 @@ impl Service {
}
}
if content.membership == MembershipState::Ban && pdu.state_key().is_some() {
if content.membership == MembershipState::Ban
&& pdu.state_key().is_some()
{
if target == server_user {
warn!("Grapevine user cannot be banned in admins room");
warn!(
"Grapevine user cannot be banned in \
admins room"
);
return Err(Error::BadRequest(
ErrorKind::Forbidden,
"Grapevine user cannot be banned in admins room.",
"Grapevine user cannot be banned in \
admins room.",
));
}
@ -867,10 +968,14 @@ impl Service {
.filter(|m| m != target)
.count();
if count < 2 {
warn!("Last admin cannot be banned in admins room");
warn!(
"Last admin cannot be banned in admins \
room"
);
return Err(Error::BadRequest(
ErrorKind::Forbidden,
"Last admin cannot be banned in admins room.",
"Last admin cannot be banned in admins \
room.",
));
}
}
@ -880,7 +985,8 @@ impl Service {
}
}
// If redaction event is not authorized, do not append it to the timeline
// If redaction event is not authorized, do not append it to the
// timeline
if pdu.kind == TimelineEventType::RoomRedaction {
match services().rooms.state.get_room_version(&pdu.room_id)? {
RoomVersionId::V1
@ -908,11 +1014,12 @@ impl Service {
};
}
RoomVersionId::V11 => {
let content =
serde_json::from_str::<RoomRedactionEventContent>(pdu.content.get())
.map_err(|_| {
Error::bad_database("Invalid content in redaction pdu.")
})?;
let content = serde_json::from_str::<
RoomRedactionEventContent,
>(pdu.content.get())
.map_err(|_| {
Error::bad_database("Invalid content in redaction pdu.")
})?;
if let Some(redact_id) = &content.redacts {
if !services().rooms.state_accessor.user_can_redact(
@ -937,27 +1044,30 @@ impl Service {
}
}
// We append to state before appending the pdu, so we don't have a moment in time with the
// pdu without it's state. This is okay because append_pdu can't fail.
// We append to state before appending the pdu, so we don't have a
// moment in time with the pdu without it's state. This is okay
// because append_pdu can't fail.
let statehashid = services().rooms.state.append_to_state(&pdu)?;
let pdu_id = self
.append_pdu(
&pdu,
pdu_json,
// Since this PDU references all pdu_leaves we can update the leaves
// of the room
// Since this PDU references all pdu_leaves we can update the
// leaves of the room
vec![(*pdu.event_id).to_owned()],
state_lock,
)
.await?;
// We set the room state after inserting the pdu, so that we never have a moment in time
// where events in the current room state do not exist
services()
.rooms
.state
.set_room_state(room_id, statehashid, state_lock)?;
// We set the room state after inserting the pdu, so that we never have
// a moment in time where events in the current room state do
// not exist
services().rooms.state.set_room_state(
room_id,
statehashid,
state_lock,
)?;
let mut servers: HashSet<OwnedServerName> = services()
.rooms
@ -966,7 +1076,8 @@ impl Service {
.filter_map(Result::ok)
.collect();
// In case we are kicking or banning a user, we need to inform their server of the change
// In case we are kicking or banning a user, we need to inform their
// server of the change
if pdu.kind == TimelineEventType::RoomMember {
if let Some(state_key_uid) = &pdu
.state_key
@ -977,7 +1088,8 @@ impl Service {
}
}
// Remove our server from the server list since it will be added to it by room_servers() and/or the if statement above
// Remove our server from the server list since it will be added to it
// by room_servers() and/or the if statement above
servers.remove(services().globals.server_name());
services().sending.send_pdu(servers.into_iter(), &pdu_id)?;
@ -985,8 +1097,8 @@ impl Service {
Ok(pdu.event_id)
}
/// Append the incoming event setting the state snapshot to the state from the
/// server that sent the event.
/// Append the incoming event setting the state snapshot to the state from
/// the server that sent the event.
#[tracing::instrument(skip_all)]
pub(crate) async fn append_incoming_pdu(
&self,
@ -998,8 +1110,9 @@ impl Service {
// Take mutex guard to make sure users get the room state mutex
state_lock: &MutexGuard<'_, ()>,
) -> Result<Option<Vec<u8>>> {
// We append to state before appending the pdu, so we don't have a moment in time with the
// pdu without it's state. This is okay because append_pdu can't fail.
// We append to state before appending the pdu, so we don't have a
// moment in time with the pdu without it's state. This is okay
// because append_pdu can't fail.
services().rooms.state.set_event_state(
&pdu.event_id,
&pdu.room_id,
@ -1037,8 +1150,9 @@ impl Service {
self.pdus_after(user_id, room_id, PduCount::MIN)
}
/// Returns an iterator over all events and their tokens in a room that happened before the
/// event with id `until` in reverse-chronological order.
/// Returns an iterator over all events and their tokens in a room that
/// happened before the event with id `until` in reverse-chronological
/// order.
#[tracing::instrument(skip(self))]
pub(crate) fn pdus_until<'a>(
&'a self,
@ -1049,8 +1163,8 @@ impl Service {
self.db.pdus_until(user_id, room_id, until)
}
/// Returns an iterator over all events and their token in a room that happened after the event
/// with id `from` in chronological order.
/// Returns an iterator over all events and their token in a room that
/// happened after the event with id `from` in chronological order.
#[tracing::instrument(skip(self))]
pub(crate) fn pdus_after<'a>(
&'a self,
@ -1063,13 +1177,18 @@ impl Service {
/// Replace a PDU with the redacted form.
#[tracing::instrument(skip(self, reason))]
pub(crate) fn redact_pdu(&self, event_id: &EventId, reason: &PduEvent) -> Result<()> {
pub(crate) fn redact_pdu(
&self,
event_id: &EventId,
reason: &PduEvent,
) -> Result<()> {
// TODO: Don't reserialize, keep original json
if let Some(pdu_id) = self.get_pdu_id(event_id)? {
let mut pdu = self
.get_pdu_from_id(&pdu_id)?
.ok_or_else(|| Error::bad_database("PDU ID points to invalid PDU."))?;
let room_version_id = services().rooms.state.get_room_version(&pdu.room_id)?;
let mut pdu = self.get_pdu_from_id(&pdu_id)?.ok_or_else(|| {
Error::bad_database("PDU ID points to invalid PDU.")
})?;
let room_version_id =
services().rooms.state.get_room_version(&pdu.room_id)?;
pdu.redact(room_version_id, reason)?;
self.replace_pdu(
&pdu_id,
@ -1102,8 +1221,9 @@ impl Service {
.state_accessor
.room_state_get(room_id, &StateEventType::RoomPowerLevels, "")?
.map(|ev| {
serde_json::from_str(ev.content.get())
.map_err(|_| Error::bad_database("invalid m.room.power_levels event"))
serde_json::from_str(ev.content.get()).map_err(|_| {
Error::bad_database("invalid m.room.power_levels event")
})
})
.transpose()?
.unwrap_or_default();
@ -1133,7 +1253,9 @@ impl Service {
Ok(response) => {
let pub_key_map = RwLock::new(BTreeMap::new());
for pdu in response.pdus {
if let Err(e) = self.backfill_pdu(backfill_server, pdu, &pub_key_map).await
if let Err(e) = self
.backfill_pdu(backfill_server, pdu, &pub_key_map)
.await
{
warn!("Failed to add backfilled pdu: {e}");
}
@ -1157,7 +1279,8 @@ impl Service {
pdu: Box<RawJsonValue>,
pub_key_map: &RwLock<BTreeMap<String, BTreeMap<String, Base64>>>,
) -> Result<()> {
let (event_id, value, room_id) = server_server::parse_incoming_pdu(&pdu)?;
let (event_id, value, room_id) =
server_server::parse_incoming_pdu(&pdu)?;
// Lock so we cannot backfill the same pdu twice at the same time
let mutex = Arc::clone(
@ -1180,7 +1303,14 @@ impl Service {
services()
.rooms
.event_handler
.handle_incoming_pdu(origin, &event_id, &room_id, value, false, pub_key_map)
.handle_incoming_pdu(
origin,
&event_id,
&room_id,
value,
false,
pub_key_map,
)
.await?;
let value = self.get_pdu_json(&event_id)?.expect("We just created it");
@ -1219,14 +1349,18 @@ impl Service {
body: Option<String>,
}
let content = serde_json::from_str::<ExtractBody>(pdu.content.get())
.map_err(|_| Error::bad_database("Invalid content in pdu."))?;
let content =
serde_json::from_str::<ExtractBody>(pdu.content.get())
.map_err(|_| {
Error::bad_database("Invalid content in pdu.")
})?;
if let Some(body) = content.body {
services()
.rooms
.search
.index_pdu(shortroomid, &pdu_id, &body)?;
services().rooms.search.index_pdu(
shortroomid,
&pdu_id,
&body,
)?;
}
}
drop(mutex_lock);

View file

@ -2,21 +2,30 @@ use std::sync::Arc;
use ruma::{CanonicalJsonObject, EventId, OwnedUserId, RoomId, UserId};
use super::PduCount;
use crate::{PduEvent, Result};
use super::PduCount;
pub(crate) trait Data: Send + Sync {
fn last_timeline_count(&self, sender_user: &UserId, room_id: &RoomId) -> Result<PduCount>;
fn last_timeline_count(
&self,
sender_user: &UserId,
room_id: &RoomId,
) -> Result<PduCount>;
/// Returns the `count` of this pdu's id.
fn get_pdu_count(&self, event_id: &EventId) -> Result<Option<PduCount>>;
/// Returns the json of a pdu.
fn get_pdu_json(&self, event_id: &EventId) -> Result<Option<CanonicalJsonObject>>;
fn get_pdu_json(
&self,
event_id: &EventId,
) -> Result<Option<CanonicalJsonObject>>;
/// Returns the json of a pdu.
fn get_non_outlier_pdu_json(&self, event_id: &EventId) -> Result<Option<CanonicalJsonObject>>;
fn get_non_outlier_pdu_json(
&self,
event_id: &EventId,
) -> Result<Option<CanonicalJsonObject>>;
/// Returns the pdu's id.
fn get_pdu_id(&self, event_id: &EventId) -> Result<Option<Vec<u8>>>;
@ -24,7 +33,10 @@ pub(crate) trait Data: Send + Sync {
/// Returns the pdu.
///
/// Checks the `eventid_outlierpdu` Tree if not found in the timeline.
fn get_non_outlier_pdu(&self, event_id: &EventId) -> Result<Option<PduEvent>>;
fn get_non_outlier_pdu(
&self,
event_id: &EventId,
) -> Result<Option<PduEvent>>;
/// Returns the pdu.
///
@ -37,7 +49,10 @@ pub(crate) trait Data: Send + Sync {
fn get_pdu_from_id(&self, pdu_id: &[u8]) -> Result<Option<PduEvent>>;
/// Returns the pdu as a `BTreeMap<String, CanonicalJsonValue>`.
fn get_pdu_json_from_id(&self, pdu_id: &[u8]) -> Result<Option<CanonicalJsonObject>>;
fn get_pdu_json_from_id(
&self,
pdu_id: &[u8],
) -> Result<Option<CanonicalJsonObject>>;
/// Adds a new pdu to the timeline
fn append_pdu(
@ -64,8 +79,9 @@ pub(crate) trait Data: Send + Sync {
pdu: &PduEvent,
) -> Result<()>;
/// Returns an iterator over all events and their tokens in a room that happened before the
/// event with id `until` in reverse-chronological order.
/// Returns an iterator over all events and their tokens in a room that
/// happened before the event with id `until` in reverse-chronological
/// order.
#[allow(clippy::type_complexity)]
fn pdus_until<'a>(
&'a self,
@ -74,8 +90,8 @@ pub(crate) trait Data: Send + Sync {
until: PduCount,
) -> Result<Box<dyn Iterator<Item = Result<(PduCount, PduEvent)>> + 'a>>;
/// Returns an iterator over all events in a room that happened after the event with id `from`
/// in chronological order.
/// Returns an iterator over all events in a room that happened after the
/// event with id `from` in chronological order.
#[allow(clippy::type_complexity)]
fn pdus_after<'a>(
&'a self,

View file

@ -1,15 +1,32 @@
use crate::Result;
use ruma::{OwnedRoomId, OwnedUserId, RoomId, UserId};
use crate::Result;
pub(crate) trait Data: Send + Sync {
fn reset_notification_counts(&self, user_id: &UserId, room_id: &RoomId) -> Result<()>;
fn reset_notification_counts(
&self,
user_id: &UserId,
room_id: &RoomId,
) -> Result<()>;
fn notification_count(&self, user_id: &UserId, room_id: &RoomId) -> Result<u64>;
fn notification_count(
&self,
user_id: &UserId,
room_id: &RoomId,
) -> Result<u64>;
fn highlight_count(&self, user_id: &UserId, room_id: &RoomId) -> Result<u64>;
fn highlight_count(
&self,
user_id: &UserId,
room_id: &RoomId,
) -> Result<u64>;
// Returns the count at which the last reset_notification_counts was called
fn last_notification_read(&self, user_id: &UserId, room_id: &RoomId) -> Result<u64>;
fn last_notification_read(
&self,
user_id: &UserId,
room_id: &RoomId,
) -> Result<u64>;
fn associate_token_shortstatehash(
&self,
@ -18,7 +35,11 @@ pub(crate) trait Data: Send + Sync {
shortstatehash: u64,
) -> Result<()>;
fn get_token_shortstatehash(&self, room_id: &RoomId, token: u64) -> Result<Option<u64>>;
fn get_token_shortstatehash(
&self,
room_id: &RoomId,
token: u64,
) -> Result<Option<u64>>;
fn get_shared_rooms<'a>(
&'a self,