17 changed files with 1572 additions and 1629 deletions
@ -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) |
||||||
|
} |
||||||
|
} |
||||||
@ -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))) |
||||||
|
} |
||||||
|
} |
||||||
@ -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] = ¤t; |
||||||
|
|
||||||
|
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"))?, |
||||||
|
)?)?) |
||||||
|
}) |
||||||
|
} |
||||||
|
} |
||||||
@ -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))) |
||||||
|
} |
||||||
|
} |
||||||
@ -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(()) |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,36 @@ |
|||||||
|
use thiserror::Error; |
||||||
|
|
||||||
|
pub type Result<T> = std::result::Result<T, Error>; |
||||||
|
|
||||||
|
#[derive(Error, Debug)] |
||||||
|
pub enum Error { |
||||||
|
#[error("problem with the database")] |
||||||
|
SledError { |
||||||
|
#[from] |
||||||
|
source: sled::Error, |
||||||
|
}, |
||||||
|
#[error("tried to parse invalid string")] |
||||||
|
StringFromBytesError { |
||||||
|
#[from] |
||||||
|
source: std::string::FromUtf8Error, |
||||||
|
}, |
||||||
|
#[error("tried to parse invalid identifier")] |
||||||
|
SerdeJsonError { |
||||||
|
#[from] |
||||||
|
source: serde_json::Error, |
||||||
|
}, |
||||||
|
#[error("tried to parse invalid identifier")] |
||||||
|
RumaIdentifierError { |
||||||
|
#[from] |
||||||
|
source: ruma_identifiers::Error, |
||||||
|
}, |
||||||
|
#[error("tried to parse invalid event")] |
||||||
|
RumaEventError { |
||||||
|
#[from] |
||||||
|
source: ruma_events::InvalidEvent, |
||||||
|
}, |
||||||
|
#[error("bad request")] |
||||||
|
BadRequest(&'static str), |
||||||
|
#[error("problem in that database")] |
||||||
|
BadDatabase(&'static str), |
||||||
|
} |
||||||
Loading…
Reference in new issue