refactor: split database into multiple files, more error handling, cleaner code

This commit is contained in:
timokoesters 2020-05-03 17:25:31 +02:00
parent 4b191a9311
commit 8f67c01efd
No known key found for this signature in database
GPG key ID: 24DA7517711A2BA4
17 changed files with 1573 additions and 1630 deletions

View file

@ -0,0 +1,120 @@
use crate::Result;
use ruma_events::{collections::only::Event as EduEvent, EventJson};
use ruma_identifiers::{RoomId, UserId};
use std::collections::HashMap;
pub struct AccountData {
pub(super) roomuserdataid_accountdata: sled::Tree, // RoomUserDataId = Room + User + Count + Type
}
impl AccountData {
/// Places one event in the account data of the user and removes the previous entry.
pub fn update(
&self,
room_id: Option<&RoomId>,
user_id: &UserId,
event: EduEvent,
globals: &super::globals::Globals,
) -> Result<()> {
let mut prefix = room_id
.map(|r| r.to_string())
.unwrap_or_default()
.as_bytes()
.to_vec();
prefix.push(0xff);
prefix.extend_from_slice(&user_id.to_string().as_bytes());
prefix.push(0xff);
// Remove old entry
if let Some(old) = self
.roomuserdataid_accountdata
.scan_prefix(&prefix)
.keys()
.rev()
.filter_map(|r| r.ok())
.take_while(|key| key.starts_with(&prefix))
.filter(|key| {
key.split(|&b| b == 0xff)
.nth(1)
.filter(|&user| user == user_id.to_string().as_bytes())
.is_some()
})
.next()
{
// This is the old room_latest
self.roomuserdataid_accountdata.remove(old)?;
println!("removed old account data");
}
let mut key = prefix;
key.extend_from_slice(&globals.next_count()?.to_be_bytes());
key.push(0xff);
let json = serde_json::to_value(&event)?;
key.extend_from_slice(json["type"].as_str().unwrap().as_bytes());
self.roomuserdataid_accountdata
.insert(key, &*json.to_string())
.unwrap();
Ok(())
}
// TODO: Optimize
/// Searches the account data for a specific kind.
pub fn get(
&self,
room_id: Option<&RoomId>,
user_id: &UserId,
kind: &str,
) -> Result<Option<EventJson<EduEvent>>> {
Ok(self.all(room_id, user_id)?.remove(kind))
}
/// Returns all changes to the account data that happened after `since`.
pub fn changes_since(
&self,
room_id: Option<&RoomId>,
user_id: &UserId,
since: u64,
) -> Result<HashMap<String, EventJson<EduEvent>>> {
let mut userdata = HashMap::new();
let mut prefix = room_id
.map(|r| r.to_string())
.unwrap_or_default()
.as_bytes()
.to_vec();
prefix.push(0xff);
prefix.extend_from_slice(&user_id.to_string().as_bytes());
prefix.push(0xff);
// Skip the data that's exactly at since, because we sent that last time
let mut first_possible = prefix.clone();
first_possible.extend_from_slice(&(since + 1).to_be_bytes());
for json in self
.roomuserdataid_accountdata
.range(&*first_possible..)
.filter_map(|r| r.ok())
.take_while(move |(k, _)| k.starts_with(&prefix))
.map(|(_, v)| serde_json::from_slice::<serde_json::Value>(&v).unwrap())
{
userdata.insert(
json["type"].as_str().unwrap().to_owned(),
serde_json::from_value::<EventJson<EduEvent>>(json)
.expect("userdata in db is valid"),
);
}
Ok(userdata)
}
/// Returns all account data.
pub fn all(
&self,
room_id: Option<&RoomId>,
user_id: &UserId,
) -> Result<HashMap<String, EventJson<EduEvent>>> {
self.changes_since(room_id, user_id, 0)
}
}

61
src/database/globals.rs Normal file
View file

@ -0,0 +1,61 @@
use crate::{utils, Result};
pub const COUNTER: &str = "c";
pub struct Globals {
pub(super) globals: sled::Tree,
hostname: String,
keypair: ruma_signatures::Ed25519KeyPair,
reqwest_client: reqwest::Client,
}
impl Globals {
pub fn load(globals: sled::Tree, hostname: String) -> Self {
let keypair = ruma_signatures::Ed25519KeyPair::new(
&*globals
.update_and_fetch("keypair", utils::generate_keypair)
.unwrap()
.unwrap(),
"key1".to_owned(),
)
.unwrap();
Self {
globals,
hostname,
keypair,
reqwest_client: reqwest::Client::new(),
}
}
/// Returns the hostname of the server.
pub fn hostname(&self) -> &str {
&self.hostname
}
/// Returns this server's keypair.
pub fn keypair(&self) -> &ruma_signatures::Ed25519KeyPair {
&self.keypair
}
/// Returns a reqwest client which can be used to send requests.
pub fn reqwest_client(&self) -> &reqwest::Client {
&self.reqwest_client
}
pub fn next_count(&self) -> Result<u64> {
Ok(utils::u64_from_bytes(
&self
.globals
.update_and_fetch(COUNTER, utils::increment)?
.expect("utils::increment will always put in a value"),
))
}
pub fn current_count(&self) -> Result<u64> {
Ok(self
.globals
.get(COUNTER)?
.map_or(0_u64, |bytes| utils::u64_from_bytes(&bytes)))
}
}

547
src/database/rooms.rs Normal file
View file

@ -0,0 +1,547 @@
mod edus;
pub use edus::RoomEdus;
use crate::{utils, Error, PduEvent, Result};
use ruma_events::{room::power_levels::PowerLevelsEventContent, EventJson, EventType};
use ruma_identifiers::{EventId, RoomId, UserId};
use serde_json::json;
use std::{
collections::HashMap,
convert::{TryFrom, TryInto},
mem,
};
pub struct Rooms {
pub edus: edus::RoomEdus,
pub(super) pduid_pdu: sled::Tree, // PduId = RoomId + Count
pub(super) eventid_pduid: sled::Tree,
pub(super) roomid_pduleaves: sled::Tree,
pub(super) roomstateid_pdu: sled::Tree, // Room + StateType + StateKey
pub(super) userroomid_joined: sled::Tree,
pub(super) roomuserid_joined: sled::Tree,
pub(super) userroomid_invited: sled::Tree,
pub(super) roomuserid_invited: sled::Tree,
pub(super) userroomid_left: sled::Tree,
}
impl Rooms {
/// Checks if a room exists.
pub fn exists(&self, room_id: &RoomId) -> Result<bool> {
// Look for PDUs in that room.
let mut prefix = room_id.to_string().as_bytes().to_vec();
prefix.push(0xff);
Ok(self
.pduid_pdu
.get_gt(&prefix)?
.filter(|(k, _)| k.starts_with(&prefix))
.is_some())
}
// TODO: Remove and replace with public room dir
/// Returns a vector over all rooms.
pub fn all_rooms(&self) -> Vec<RoomId> {
let mut room_ids = self
.roomid_pduleaves
.iter()
.keys()
.map(|key| {
RoomId::try_from(
&*utils::string_from_bytes(
&key.unwrap()
.iter()
.copied()
.take_while(|&x| x != 0xff) // until delimiter
.collect::<Vec<_>>(),
)
.unwrap(),
)
.unwrap()
})
.collect::<Vec<_>>();
room_ids.dedup();
room_ids
}
/// Returns the full room state.
pub fn room_state(&self, room_id: &RoomId) -> Result<HashMap<(EventType, String), PduEvent>> {
let mut hashmap = HashMap::new();
for pdu in self
.roomstateid_pdu
.scan_prefix(&room_id.to_string().as_bytes())
.values()
.map(|value| Ok::<_, Error>(serde_json::from_slice::<PduEvent>(&value?)?))
{
let pdu = pdu?;
hashmap.insert(
(
pdu.kind.clone(),
pdu.state_key
.clone()
.expect("state events have a state key"),
),
pdu,
);
}
Ok(hashmap)
}
/// Returns the `count` of this pdu's id.
pub fn get_pdu_count(&self, event_id: &EventId) -> Result<Option<u64>> {
Ok(self
.eventid_pduid
.get(event_id.to_string().as_bytes())?
.map(|pdu_id| {
utils::u64_from_bytes(&pdu_id[pdu_id.len() - mem::size_of::<u64>()..pdu_id.len()])
}))
}
/// Returns the json of a pdu.
pub fn get_pdu_json(&self, event_id: &EventId) -> Result<Option<serde_json::Value>> {
self.eventid_pduid
.get(event_id.to_string().as_bytes())?
.map_or(Ok(None), |pdu_id| {
Ok(serde_json::from_slice(
&self.pduid_pdu.get(pdu_id)?.ok_or(Error::BadDatabase(
"eventid_pduid points to nonexistent pdu",
))?,
)?)
.map(Some)
})
}
/// Returns the leaf pdus of a room.
pub fn get_pdu_leaves(&self, room_id: &RoomId) -> Result<Vec<EventId>> {
let mut prefix = room_id.to_string().as_bytes().to_vec();
prefix.push(0xff);
let mut events = Vec::new();
for event in self
.roomid_pduleaves
.scan_prefix(prefix)
.values()
.map(|bytes| Ok::<_, Error>(EventId::try_from(&*utils::string_from_bytes(&bytes?)?)?))
{
events.push(event?);
}
Ok(events)
}
/// Replace the leaves of a room with a new event.
pub fn replace_pdu_leaves(&self, room_id: &RoomId, event_id: &EventId) -> Result<()> {
let mut prefix = room_id.to_string().as_bytes().to_vec();
prefix.push(0xff);
for key in self.roomid_pduleaves.scan_prefix(&prefix).keys() {
self.roomid_pduleaves.remove(key?)?;
}
prefix.extend_from_slice(event_id.to_string().as_bytes());
self.roomid_pduleaves
.insert(&prefix, &*event_id.to_string())?;
Ok(())
}
/// Creates a new persisted data unit and adds it to a room.
pub fn append_pdu(
&self,
room_id: RoomId,
sender: UserId,
event_type: EventType,
content: serde_json::Value,
unsigned: Option<serde_json::Map<String, serde_json::Value>>,
state_key: Option<String>,
globals: &super::globals::Globals,
) -> Result<EventId> {
// Is the event authorized?
if state_key.is_some() {
if let Some(pdu) = self
.room_state(&room_id)?
.get(&(EventType::RoomPowerLevels, "".to_owned()))
{
let power_levels = serde_json::from_value::<EventJson<PowerLevelsEventContent>>(
pdu.content.clone(),
)?
.deserialize()?;
match event_type {
EventType::RoomMember => {
// Member events are okay for now (TODO)
}
_ if power_levels
.users
.get(&sender)
.unwrap_or(&power_levels.users_default)
<= &0.into() =>
{
// Not authorized
return Err(Error::BadRequest("event not authorized"));
}
// User has sufficient power
_ => {}
}
}
}
// prev_events are the leaves of the current graph. This method removes all leaves from the
// room and replaces them with our event
// TODO: Make sure this isn't called twice in parallel
let prev_events = self.get_pdu_leaves(&room_id)?;
// Our depth is the maximum depth of prev_events + 1
let depth = prev_events
.iter()
.filter_map(|event_id| Some(self.get_pdu_json(event_id).ok()??.get("depth")?.as_u64()?))
.max()
.unwrap_or(0_u64)
+ 1;
let mut unsigned = unsigned.unwrap_or_default();
// TODO: Optimize this to not load the whole room state?
if let Some(state_key) = &state_key {
if let Some(prev_pdu) = self
.room_state(&room_id)?
.get(&(event_type.clone(), state_key.clone()))
{
unsigned.insert("prev_content".to_owned(), prev_pdu.content.clone());
}
}
let mut pdu = PduEvent {
event_id: EventId::try_from("$thiswillbefilledinlater").expect("we know this is valid"),
room_id: room_id.clone(),
sender: sender.clone(),
origin: globals.hostname().to_owned(),
origin_server_ts: utils::millis_since_unix_epoch()
.try_into()
.expect("this only fails many years in the future"),
kind: event_type,
content,
state_key,
prev_events,
depth: depth
.try_into()
.expect("depth can overflow and should be deprecated..."),
auth_events: Vec::new(),
redacts: None,
unsigned,
hashes: ruma_federation_api::EventHash {
sha256: "aaa".to_owned(),
},
signatures: HashMap::new(),
};
// Generate event id
pdu.event_id = EventId::try_from(&*format!(
"${}",
ruma_signatures::reference_hash(&serde_json::to_value(&pdu)?)
.expect("ruma can calculate reference hashes")
))
.expect("ruma's reference hashes are correct");
let mut pdu_json = serde_json::to_value(&pdu)?;
ruma_signatures::hash_and_sign_event(globals.hostname(), globals.keypair(), &mut pdu_json)
.expect("our new event can be hashed and signed");
self.replace_pdu_leaves(&room_id, &pdu.event_id)?;
// Increment the last index and use that
// This is also the next_batch/since value
let index = globals.next_count()?;
let mut pdu_id = room_id.to_string().as_bytes().to_vec();
pdu_id.push(0xff);
pdu_id.extend_from_slice(&index.to_be_bytes());
self.pduid_pdu.insert(&pdu_id, &*pdu_json.to_string())?;
self.eventid_pduid
.insert(pdu.event_id.to_string(), pdu_id.clone())?;
if let Some(state_key) = pdu.state_key {
let mut key = room_id.to_string().as_bytes().to_vec();
key.push(0xff);
key.extend_from_slice(pdu.kind.to_string().as_bytes());
key.push(0xff);
key.extend_from_slice(state_key.to_string().as_bytes());
self.roomstateid_pdu.insert(key, &*pdu_json.to_string())?;
}
self.edus.room_read_set(&room_id, &sender, index)?;
Ok(pdu.event_id)
}
/// Returns an iterator over all PDUs in a room.
pub fn all_pdus(&self, room_id: &RoomId) -> Result<impl Iterator<Item = Result<PduEvent>>> {
self.pdus_since(room_id, 0)
}
/// Returns an iterator over all events in a room that happened after the event with id `since`.
pub fn pdus_since(
&self,
room_id: &RoomId,
since: u64,
) -> Result<impl Iterator<Item = Result<PduEvent>>> {
// Create the first part of the full pdu id
let mut pdu_id = room_id.to_string().as_bytes().to_vec();
pdu_id.push(0xff);
pdu_id.extend_from_slice(&(since).to_be_bytes());
self.pdus_since_pduid(room_id, &pdu_id)
}
/// Returns an iterator over all events in a room that happened after the event with id `since`.
pub fn pdus_since_pduid(
&self,
room_id: &RoomId,
pdu_id: &[u8],
) -> Result<impl Iterator<Item = Result<PduEvent>>> {
// Create the first part of the full pdu id
let mut prefix = room_id.to_string().as_bytes().to_vec();
prefix.push(0xff);
Ok(self
.pduid_pdu
.range(pdu_id..)
// Skip the first pdu if it's exactly at since, because we sent that last time
.skip(if self.pduid_pdu.get(pdu_id)?.is_some() {
1
} else {
0
})
.filter_map(|r| r.ok())
.take_while(move |(k, _)| k.starts_with(&prefix))
.map(|(_, v)| Ok(serde_json::from_slice(&v)?)))
}
/// Returns an iterator over all events in a room that happened before the event with id
/// `until` in reverse-chronological order.
pub fn pdus_until(
&self,
room_id: &RoomId,
until: u64,
) -> impl Iterator<Item = Result<PduEvent>> {
// Create the first part of the full pdu id
let mut prefix = room_id.to_string().as_bytes().to_vec();
prefix.push(0xff);
let mut current = prefix.clone();
current.extend_from_slice(&until.to_be_bytes());
let current: &[u8] = &current;
self.pduid_pdu
.range(..current)
.rev()
.filter_map(|r| r.ok())
.take_while(move |(k, _)| k.starts_with(&prefix))
.map(|(_, v)| Ok(serde_json::from_slice(&v)?))
}
/// Makes a user join a room.
pub fn join(
&self,
room_id: &RoomId,
user_id: &UserId,
displayname: Option<String>,
globals: &super::globals::Globals,
) -> Result<()> {
if !self.exists(room_id)? {
return Err(Error::BadRequest("room does not exist"));
}
let mut userroom_id = user_id.to_string().as_bytes().to_vec();
userroom_id.push(0xff);
userroom_id.extend_from_slice(room_id.to_string().as_bytes());
let mut roomuser_id = room_id.to_string().as_bytes().to_vec();
roomuser_id.push(0xff);
roomuser_id.extend_from_slice(user_id.to_string().as_bytes());
self.userroomid_joined.insert(&userroom_id, &[])?;
self.roomuserid_joined.insert(&roomuser_id, &[])?;
self.userroomid_invited.remove(&userroom_id)?;
self.roomuserid_invited.remove(&roomuser_id)?;
self.userroomid_left.remove(&userroom_id)?;
let mut content = json!({"membership": "join"});
if let Some(displayname) = displayname {
content
.as_object_mut()
.unwrap()
.insert("displayname".to_owned(), displayname.into());
}
self.append_pdu(
room_id.clone(),
user_id.clone(),
EventType::RoomMember,
content,
None,
Some(user_id.to_string()),
globals,
)?;
Ok(())
}
/// Makes a user leave a room.
pub fn leave(
&self,
sender: &UserId,
room_id: &RoomId,
user_id: &UserId,
globals: &super::globals::Globals,
) -> Result<()> {
let mut userroom_id = user_id.to_string().as_bytes().to_vec();
userroom_id.push(0xff);
userroom_id.extend_from_slice(room_id.to_string().as_bytes());
let mut roomuser_id = room_id.to_string().as_bytes().to_vec();
roomuser_id.push(0xff);
roomuser_id.extend_from_slice(user_id.to_string().as_bytes());
self.userroomid_joined.remove(&userroom_id)?;
self.roomuserid_joined.remove(&roomuser_id)?;
self.userroomid_invited.remove(&userroom_id)?;
self.roomuserid_invited.remove(&userroom_id)?;
self.userroomid_left.insert(&userroom_id, &[])?;
self.append_pdu(
room_id.clone(),
sender.clone(),
EventType::RoomMember,
json!({"membership": "leave"}),
None,
Some(user_id.to_string()),
globals,
)?;
Ok(())
}
/// Makes a user forget a room.
pub fn forget(&self, room_id: &RoomId, user_id: &UserId) -> Result<()> {
let mut userroom_id = user_id.to_string().as_bytes().to_vec();
userroom_id.push(0xff);
userroom_id.extend_from_slice(room_id.to_string().as_bytes());
self.userroomid_left.remove(userroom_id)?;
Ok(())
}
/// Makes a user invite another user into room.
pub fn invite(
&self,
sender: &UserId,
room_id: &RoomId,
user_id: &UserId,
globals: &super::globals::Globals,
) -> Result<()> {
let mut userroom_id = user_id.to_string().as_bytes().to_vec();
userroom_id.push(0xff);
userroom_id.extend_from_slice(room_id.to_string().as_bytes());
let mut roomuser_id = room_id.to_string().as_bytes().to_vec();
roomuser_id.push(0xff);
roomuser_id.extend_from_slice(user_id.to_string().as_bytes());
self.userroomid_invited.insert(userroom_id, &[])?;
self.roomuserid_invited.insert(roomuser_id, &[])?;
self.append_pdu(
room_id.clone(),
sender.clone(),
EventType::RoomMember,
json!({"membership": "invite"}),
None,
Some(user_id.to_string()),
globals,
)?;
Ok(())
}
/// Returns an iterator over all rooms a user joined.
pub fn room_members(&self, room_id: &RoomId) -> impl Iterator<Item = Result<UserId>> {
self.roomuserid_joined
.scan_prefix(room_id.to_string())
.values()
.map(|key| {
Ok(UserId::try_from(&*utils::string_from_bytes(
&key?
.rsplit(|&b| b == 0xff)
.next()
.ok_or(Error::BadDatabase("userroomid is invalid"))?,
)?)?)
})
}
/// Returns an iterator over all rooms a user joined.
pub fn room_members_invited(&self, room_id: &RoomId) -> impl Iterator<Item = Result<UserId>> {
self.roomuserid_invited
.scan_prefix(room_id.to_string())
.keys()
.map(|key| {
Ok(UserId::try_from(&*utils::string_from_bytes(
&key?
.rsplit(|&b| b == 0xff)
.next()
.ok_or(Error::BadDatabase("userroomid is invalid"))?,
)?)?)
})
}
/// Returns an iterator over all rooms a user joined.
pub fn rooms_joined(&self, user_id: &UserId) -> impl Iterator<Item = Result<RoomId>> {
self.userroomid_joined
.scan_prefix(user_id.to_string())
.keys()
.map(|key| {
Ok(RoomId::try_from(&*utils::string_from_bytes(
&key?
.rsplit(|&b| b == 0xff)
.next()
.ok_or(Error::BadDatabase("userroomid is invalid"))?,
)?)?)
})
}
/// Returns an iterator over all rooms a user was invited to.
pub fn rooms_invited(&self, user_id: &UserId) -> impl Iterator<Item = Result<RoomId>> {
self.userroomid_invited
.scan_prefix(&user_id.to_string())
.keys()
.map(|key| {
Ok(RoomId::try_from(&*utils::string_from_bytes(
&key?
.rsplit(|&b| b == 0xff)
.next()
.ok_or(Error::BadDatabase("userroomid is invalid"))?,
)?)?)
})
}
/// Returns an iterator over all rooms a user left.
pub fn rooms_left(&self, user_id: &UserId) -> impl Iterator<Item = Result<RoomId>> {
self.userroomid_left
.scan_prefix(&user_id.to_string())
.keys()
.map(|key| {
Ok(RoomId::try_from(&*utils::string_from_bytes(
&key?
.rsplit(|&b| b == 0xff)
.next()
.ok_or(Error::BadDatabase("userroomid is invalid"))?,
)?)?)
})
}
}

190
src/database/rooms/edus.rs Normal file
View file

@ -0,0 +1,190 @@
use crate::{utils, Result};
use ruma_events::{collections::only::Event as EduEvent, EventJson};
use ruma_identifiers::{RoomId, UserId};
pub struct RoomEdus {
pub(in super::super) roomuserid_lastread: sled::Tree, // RoomUserId = Room + User
pub(in super::super) roomlatestid_roomlatest: sled::Tree, // Read Receipts, RoomLatestId = RoomId + Count + UserId
pub(in super::super) roomactiveid_roomactive: sled::Tree, // Typing, RoomActiveId = RoomId + TimeoutTime + Count
}
impl RoomEdus {
/// Adds an event which will be saved until a new event replaces it (e.g. read receipt).
pub fn roomlatest_update(
&self,
user_id: &UserId,
room_id: &RoomId,
event: EduEvent,
globals: &super::super::globals::Globals,
) -> Result<()> {
let mut prefix = room_id.to_string().as_bytes().to_vec();
prefix.push(0xff);
// Remove old entry
if let Some(old) = self
.roomlatestid_roomlatest
.scan_prefix(&prefix)
.keys()
.rev()
.filter_map(|r| r.ok())
.take_while(|key| key.starts_with(&prefix))
.find(|key| {
key.rsplit(|&b| b == 0xff).next().unwrap() == user_id.to_string().as_bytes()
})
{
// This is the old room_latest
self.roomlatestid_roomlatest.remove(old)?;
}
let mut room_latest_id = prefix;
room_latest_id.extend_from_slice(&globals.next_count()?.to_be_bytes());
room_latest_id.push(0xff);
room_latest_id.extend_from_slice(&user_id.to_string().as_bytes());
self.roomlatestid_roomlatest
.insert(room_latest_id, &*serde_json::to_string(&event)?)?;
Ok(())
}
/// Returns an iterator over the most recent read_receipts in a room that happened after the event with id `since`.
pub fn roomlatests_since(
&self,
room_id: &RoomId,
since: u64,
) -> Result<impl Iterator<Item = Result<EventJson<EduEvent>>>> {
let mut prefix = room_id.to_string().as_bytes().to_vec();
prefix.push(0xff);
let mut first_possible_edu = prefix.clone();
first_possible_edu.extend_from_slice(&since.to_be_bytes());
Ok(self
.roomlatestid_roomlatest
.range(&*first_possible_edu..)
// Skip the first pdu if it's exactly at since, because we sent that last time
.skip(
if self
.roomlatestid_roomlatest
.get(first_possible_edu)?
.is_some()
{
1
} else {
0
},
)
.filter_map(|r| r.ok())
.take_while(move |(k, _)| k.starts_with(&prefix))
.map(|(_, v)| Ok(serde_json::from_slice(&v)?)))
}
/// Returns a vector of the most recent read_receipts in a room that happened after the event with id `since`.
pub fn roomlatests_all(
&self,
room_id: &RoomId,
) -> Result<impl Iterator<Item = Result<EventJson<EduEvent>>>> {
self.roomlatests_since(room_id, 0)
}
/// Adds an event that will be saved until the `timeout` timestamp (e.g. typing notifications).
pub fn roomactive_add(
&self,
event: EduEvent,
room_id: &RoomId,
timeout: u64,
globals: &super::super::globals::Globals,
) -> Result<()> {
let mut prefix = room_id.to_string().as_bytes().to_vec();
prefix.push(0xff);
// Cleanup all outdated edus before inserting a new one
for outdated_edu in self
.roomactiveid_roomactive
.scan_prefix(&prefix)
.keys()
.filter_map(|r| r.ok())
.take_while(|k| {
utils::u64_from_bytes(
k.split(|&c| c == 0xff)
.nth(1)
.expect("roomactive has valid timestamp and delimiters"),
) < utils::millis_since_unix_epoch()
})
{
// This is an outdated edu (time > timestamp)
self.roomlatestid_roomlatest.remove(outdated_edu)?;
}
let mut room_active_id = prefix;
room_active_id.extend_from_slice(&timeout.to_be_bytes());
room_active_id.push(0xff);
room_active_id.extend_from_slice(&globals.next_count()?.to_be_bytes());
self.roomactiveid_roomactive
.insert(room_active_id, &*serde_json::to_string(&event)?)?;
Ok(())
}
/// Removes an active event manually (before the timeout is reached).
pub fn roomactive_remove(&self, event: EduEvent, room_id: &RoomId) -> Result<()> {
let mut prefix = room_id.to_string().as_bytes().to_vec();
prefix.push(0xff);
let json = serde_json::to_string(&event)?;
// Remove outdated entries
for outdated_edu in self
.roomactiveid_roomactive
.scan_prefix(&prefix)
.filter_map(|r| r.ok())
.filter(|(_, v)| v == json.as_bytes())
{
self.roomactiveid_roomactive.remove(outdated_edu.0)?;
}
Ok(())
}
/// Returns an iterator over all active events (e.g. typing notifications).
pub fn roomactives_all(
&self,
room_id: &RoomId,
) -> impl Iterator<Item = Result<EventJson<EduEvent>>> {
let mut prefix = room_id.to_string().as_bytes().to_vec();
prefix.push(0xff);
let mut first_active_edu = prefix.clone();
first_active_edu.extend_from_slice(&utils::millis_since_unix_epoch().to_be_bytes());
self.roomactiveid_roomactive
.range(first_active_edu..)
.filter_map(|r| r.ok())
.take_while(move |(k, _)| k.starts_with(&prefix))
.map(|(_, v)| Ok(serde_json::from_slice(&v)?))
}
/// Sets a private read marker at `count`.
pub fn room_read_set(&self, room_id: &RoomId, user_id: &UserId, count: u64) -> Result<()> {
let mut key = room_id.to_string().as_bytes().to_vec();
key.push(0xff);
key.extend_from_slice(&user_id.to_string().as_bytes());
self.roomuserid_lastread.insert(key, &count.to_be_bytes())?;
Ok(())
}
/// Returns the private read marker.
pub fn room_read_get(&self, room_id: &RoomId, user_id: &UserId) -> Result<Option<u64>> {
let mut key = room_id.to_string().as_bytes().to_vec();
key.push(0xff);
key.extend_from_slice(&user_id.to_string().as_bytes());
Ok(self
.roomuserid_lastread
.get(key)?
.map(|v| utils::u64_from_bytes(&v)))
}
}

144
src/database/users.rs Normal file
View file

@ -0,0 +1,144 @@
use crate::{utils, Error, Result};
use ruma_identifiers::UserId;
use std::convert::TryFrom;
pub struct Users {
pub(super) userid_password: sled::Tree,
pub(super) userid_displayname: sled::Tree,
pub(super) userid_avatarurl: sled::Tree,
pub(super) userdeviceid: sled::Tree,
pub(super) userdeviceid_token: sled::Tree,
pub(super) token_userid: sled::Tree,
}
impl Users {
/// Check if a user has an account on this homeserver.
pub fn exists(&self, user_id: &UserId) -> Result<bool> {
Ok(self.userid_password.contains_key(user_id.to_string())?)
}
/// Create a new user account on this homeserver.
pub fn create(&self, user_id: &UserId, hash: &str) -> Result<()> {
self.userid_password.insert(user_id.to_string(), hash)?;
Ok(())
}
/// Find out which user an access token belongs to.
pub fn find_from_token(&self, token: &str) -> Result<Option<UserId>> {
self.token_userid.get(token)?.map_or(Ok(None), |bytes| {
utils::string_from_bytes(&bytes)
.and_then(|string| Ok(UserId::try_from(string)?))
.map(Some)
})
}
/// Returns an iterator over all users on this homeserver.
pub fn iter(&self) -> impl Iterator<Item = Result<UserId>> {
self.userid_password.iter().keys().map(|r| {
utils::string_from_bytes(&r?).and_then(|string| Ok(UserId::try_from(&*string)?))
})
}
/// Returns the password hash for the given user.
pub fn password_hash(&self, user_id: &UserId) -> Result<Option<String>> {
self.userid_password
.get(user_id.to_string())?
.map_or(Ok(None), |bytes| utils::string_from_bytes(&bytes).map(Some))
}
/// Returns the displayname of a user on this homeserver.
pub fn displayname(&self, user_id: &UserId) -> Result<Option<String>> {
self.userid_displayname
.get(user_id.to_string())?
.map_or(Ok(None), |bytes| utils::string_from_bytes(&bytes).map(Some))
}
/// Sets a new displayname or removes it if displayname is None. You still need to nofify all rooms of this change.
pub fn set_displayname(&self, user_id: &UserId, displayname: Option<String>) -> Result<()> {
if let Some(displayname) = displayname {
self.userid_displayname
.insert(user_id.to_string(), &*displayname)?;
} else {
self.userid_displayname.remove(user_id.to_string())?;
}
Ok(())
/* TODO:
for room_id in self.rooms_joined(user_id) {
self.pdu_append(
room_id.clone(),
user_id.clone(),
EventType::RoomMember,
json!({"membership": "join", "displayname": displayname}),
None,
Some(user_id.to_string()),
);
}
*/
}
/// Get a the avatar_url of a user.
pub fn avatar_url(&self, user_id: &UserId) -> Result<Option<String>> {
self.userid_avatarurl
.get(user_id.to_string())?
.map_or(Ok(None), |bytes| utils::string_from_bytes(&bytes).map(Some))
}
/// Sets a new avatar_url or removes it if avatar_url is None.
pub fn set_avatar_url(&self, user_id: &UserId, avatar_url: Option<String>) -> Result<()> {
if let Some(avatar_url) = avatar_url {
self.userid_avatarurl
.insert(user_id.to_string(), &*avatar_url)?;
} else {
self.userid_avatarurl.remove(user_id.to_string())?;
}
Ok(())
}
/// Adds a new device to a user.
pub fn create_device(&self, user_id: &UserId, device_id: &str, token: &str) -> Result<()> {
if !self.exists(user_id)? {
return Err(Error::BadRequest(
"tried to create device for nonexistent user",
));
}
let mut key = user_id.to_string().as_bytes().to_vec();
key.push(0xff);
key.extend_from_slice(device_id.as_bytes());
self.userdeviceid.insert(key, &[])?;
self.set_token(user_id, device_id, token)?;
Ok(())
}
/// Replaces the access token of one device.
pub fn set_token(&self, user_id: &UserId, device_id: &str, token: &str) -> Result<()> {
let mut key = user_id.to_string().as_bytes().to_vec();
key.push(0xff);
key.extend_from_slice(device_id.as_bytes());
if self.userdeviceid.get(&key)?.is_none() {
return Err(Error::BadRequest(
"Tried to set token for nonexistent device",
));
}
// Remove old token
if let Some(old_token) = self.userdeviceid_token.get(&key)? {
self.token_userid.remove(old_token)?;
// It will be removed from userdeviceid_token by the insert later
}
// Assign token to device_id
self.userdeviceid_token.insert(key, &*token)?;
// Assign token to user
self.token_userid.insert(token, &*user_id.to_string())?;
Ok(())
}
}