Merge branch 'master' of state-store
commit
183fffe85e
|
@ -27,10 +27,10 @@ serde_json = "1.0.51"
|
||||||
|
|
||||||
# Ruma dependencies
|
# Ruma dependencies
|
||||||
js_int = "0.1.4"
|
js_int = "0.1.4"
|
||||||
ruma-api = "0.15.1"
|
ruma-api = "0.16.0-rc.2"
|
||||||
ruma-client-api = { git = "https://github.com/matrix-org/ruma-client-api/", version = "0.7.0" }
|
ruma-client-api = { version = "0.8.0-rc.5" }
|
||||||
ruma-events = { git = "https://github.com/matrix-org/ruma-events", version = "0.18.0" }
|
ruma-events = { version = "0.21.0-beta.1" }
|
||||||
ruma-identifiers = "0.14.1"
|
ruma-identifiers = "0.16.0"
|
||||||
uuid = { version = "0.8.1", features = ["v4"] }
|
uuid = { version = "0.8.1", features = ["v4"] }
|
||||||
|
|
||||||
# Dependencies for the encryption support
|
# Dependencies for the encryption support
|
||||||
|
|
|
@ -101,6 +101,8 @@ async fn login_and_sync(
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), matrix_sdk::Error> {
|
async fn main() -> Result<(), matrix_sdk::Error> {
|
||||||
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
let (homeserver_url, username, password) =
|
let (homeserver_url, username, password) =
|
||||||
match (env::args().nth(1), env::args().nth(2), env::args().nth(3)) {
|
match (env::args().nth(1), env::args().nth(2), env::args().nth(3)) {
|
||||||
(Some(a), Some(b), Some(c)) => (a, b, c),
|
(Some(a), Some(b), Some(c)) => (a, b, c),
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
#[cfg(feature = "encryption")]
|
||||||
|
use std::collections::BTreeMap;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::{TryFrom, TryInto};
|
use std::convert::{TryFrom, TryInto};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
@ -35,9 +37,9 @@ use http::Response as HttpResponse;
|
||||||
use reqwest::header::{HeaderValue, InvalidHeaderValue};
|
use reqwest::header::{HeaderValue, InvalidHeaderValue};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use ruma_api::{Endpoint, Outgoing};
|
use ruma_api::Endpoint;
|
||||||
use ruma_events::room::message::MessageEventContent;
|
use ruma_events::room::message::MessageEventContent;
|
||||||
use ruma_events::EventResult;
|
use ruma_events::EventJson;
|
||||||
pub use ruma_events::EventType;
|
pub use ruma_events::EventType;
|
||||||
use ruma_identifiers::{RoomId, RoomIdOrAliasId, UserId};
|
use ruma_identifiers::{RoomId, RoomIdOrAliasId, UserId};
|
||||||
|
|
||||||
|
@ -422,9 +424,11 @@ impl AsyncClient {
|
||||||
pub async fn join_room_by_id_or_alias(
|
pub async fn join_room_by_id_or_alias(
|
||||||
&self,
|
&self,
|
||||||
alias: &RoomIdOrAliasId,
|
alias: &RoomIdOrAliasId,
|
||||||
|
server_name: &str,
|
||||||
) -> Result<join_room_by_id_or_alias::Response> {
|
) -> Result<join_room_by_id_or_alias::Response> {
|
||||||
let request = join_room_by_id_or_alias::Request {
|
let request = join_room_by_id_or_alias::Request {
|
||||||
room_id_or_alias: alias.clone(),
|
room_id_or_alias: alias.clone(),
|
||||||
|
server_name: server_name.to_owned(),
|
||||||
third_party_signed: None,
|
third_party_signed: None,
|
||||||
};
|
};
|
||||||
self.send(request).await
|
self.send(request).await
|
||||||
|
@ -590,7 +594,7 @@ impl AsyncClient {
|
||||||
pub async fn room_messages<R: Into<get_message_events::Request>>(
|
pub async fn room_messages<R: Into<get_message_events::Request>>(
|
||||||
&self,
|
&self,
|
||||||
request: R,
|
request: R,
|
||||||
) -> Result<get_message_events::IncomingResponse> {
|
) -> Result<get_message_events::Response> {
|
||||||
let req = request.into();
|
let req = request.into();
|
||||||
self.send(req).await
|
self.send(req).await
|
||||||
}
|
}
|
||||||
|
@ -604,10 +608,7 @@ impl AsyncClient {
|
||||||
///
|
///
|
||||||
/// * `sync_settings` - Settings for the sync call.
|
/// * `sync_settings` - Settings for the sync call.
|
||||||
#[instrument]
|
#[instrument]
|
||||||
pub async fn sync(
|
pub async fn sync(&self, mut sync_settings: SyncSettings) -> Result<sync_events::Response> {
|
||||||
&self,
|
|
||||||
mut sync_settings: SyncSettings,
|
|
||||||
) -> Result<sync_events::IncomingResponse> {
|
|
||||||
{
|
{
|
||||||
if self.base_client.read().await.is_state_store_synced() {
|
if self.base_client.read().await.is_state_store_synced() {
|
||||||
if let Ok(synced) = self.sync_with_state_store().await {
|
if let Ok(synced) = self.sync_with_state_store().await {
|
||||||
|
@ -634,7 +635,7 @@ impl AsyncClient {
|
||||||
let matrix_room = {
|
let matrix_room = {
|
||||||
let mut client = self.base_client.write().await;
|
let mut client = self.base_client.write().await;
|
||||||
for event in &room.state.events {
|
for event in &room.state.events {
|
||||||
if let EventResult::Ok(e) = event {
|
if let Ok(e) = event.deserialize() {
|
||||||
if client.receive_joined_state_event(&room_id, &e).await {
|
if client.receive_joined_state_event(&room_id, &e).await {
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
|
@ -649,9 +650,9 @@ impl AsyncClient {
|
||||||
|
|
||||||
// re looping is not ideal here
|
// re looping is not ideal here
|
||||||
for event in &mut room.state.events {
|
for event in &mut room.state.events {
|
||||||
if let EventResult::Ok(e) = event {
|
if let Ok(e) = event.deserialize() {
|
||||||
let client = self.base_client.read().await;
|
let client = self.base_client.read().await;
|
||||||
client.emit_state_event(room_id, e).await;
|
client.emit_state_event(&room_id, &e).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -672,21 +673,21 @@ impl AsyncClient {
|
||||||
*event = e;
|
*event = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let EventResult::Ok(e) = event {
|
if let Ok(e) = event.deserialize() {
|
||||||
let client = self.base_client.read().await;
|
let client = self.base_client.read().await;
|
||||||
client.emit_timeline_event(room_id, e).await;
|
client.emit_timeline_event(&room_id, &e).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// look at AccountData to further cut down users by collecting ignored users
|
// look at AccountData to further cut down users by collecting ignored users
|
||||||
for account_data in &mut room.account_data.events {
|
for account_data in &mut room.account_data.events {
|
||||||
{
|
{
|
||||||
if let EventResult::Ok(e) = account_data {
|
if let Ok(e) = account_data.deserialize() {
|
||||||
let mut client = self.base_client.write().await;
|
let mut client = self.base_client.write().await;
|
||||||
if client.receive_account_data_event(&room_id, e).await {
|
if client.receive_account_data_event(&room_id, &e).await {
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
client.emit_account_data_event(room_id, e).await;
|
client.emit_account_data_event(room_id, &e).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -696,26 +697,26 @@ impl AsyncClient {
|
||||||
// efficient but we need a room_id so we would loop through now or later.
|
// efficient but we need a room_id so we would loop through now or later.
|
||||||
for presence in &mut response.presence.events {
|
for presence in &mut response.presence.events {
|
||||||
{
|
{
|
||||||
if let EventResult::Ok(e) = presence {
|
if let Ok(e) = presence.deserialize() {
|
||||||
let mut client = self.base_client.write().await;
|
let mut client = self.base_client.write().await;
|
||||||
if client.receive_presence_event(&room_id, e).await {
|
if client.receive_presence_event(&room_id, &e).await {
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
client.emit_presence_event(room_id, e).await;
|
client.emit_presence_event(&room_id, &e).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for ephemeral in &mut room.ephemeral.events {
|
for ephemeral in &mut room.ephemeral.events {
|
||||||
{
|
{
|
||||||
if let EventResult::Ok(e) = ephemeral {
|
if let Ok(e) = ephemeral.deserialize() {
|
||||||
let mut client = self.base_client.write().await;
|
let mut client = self.base_client.write().await;
|
||||||
if client.receive_ephemeral_event(&room_id, e).await {
|
if client.receive_ephemeral_event(&room_id, &e).await {
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
client.emit_ephemeral_event(room_id, e).await;
|
client.emit_ephemeral_event(&room_id, &e).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -800,7 +801,7 @@ impl AsyncClient {
|
||||||
pub async fn sync_forever<C>(
|
pub async fn sync_forever<C>(
|
||||||
&self,
|
&self,
|
||||||
sync_settings: SyncSettings,
|
sync_settings: SyncSettings,
|
||||||
callback: impl Fn(sync_events::IncomingResponse) -> C + Send,
|
callback: impl Fn(sync_events::Response) -> C + Send,
|
||||||
) where
|
) where
|
||||||
C: Future<Output = ()>,
|
C: Future<Output = ()>,
|
||||||
{
|
{
|
||||||
|
@ -856,18 +857,7 @@ impl AsyncClient {
|
||||||
async fn send<Request: Endpoint<ResponseError = ruma_client_api::Error> + std::fmt::Debug>(
|
async fn send<Request: Endpoint<ResponseError = ruma_client_api::Error> + std::fmt::Debug>(
|
||||||
&self,
|
&self,
|
||||||
request: Request,
|
request: Request,
|
||||||
) -> Result<<Request::Response as Outgoing>::Incoming>
|
) -> Result<Request::Response> {
|
||||||
where
|
|
||||||
Request::Incoming:
|
|
||||||
TryFrom<http::Request<Vec<u8>>, Error = ruma_api::error::FromHttpRequestError>,
|
|
||||||
<Request::Response as Outgoing>::Incoming: TryFrom<
|
|
||||||
http::Response<Vec<u8>>,
|
|
||||||
Error = ruma_api::error::FromHttpResponseError<
|
|
||||||
<Request as ruma_api::Endpoint>::ResponseError,
|
|
||||||
>,
|
|
||||||
>,
|
|
||||||
<Request as ruma_api::Endpoint>::ResponseError: std::fmt::Debug,
|
|
||||||
{
|
|
||||||
let request: http::Request<Vec<u8>> = request.try_into()?;
|
let request: http::Request<Vec<u8>> = request.try_into()?;
|
||||||
let url = request.uri();
|
let url = request.uri();
|
||||||
let path_and_query = url.path_and_query().unwrap();
|
let path_and_query = url.path_and_query().unwrap();
|
||||||
|
@ -925,9 +915,7 @@ impl AsyncClient {
|
||||||
let body = response.bytes().await?.as_ref().to_owned();
|
let body = response.bytes().await?.as_ref().to_owned();
|
||||||
let http_response = http_builder.body(body).unwrap();
|
let http_response = http_builder.body(body).unwrap();
|
||||||
|
|
||||||
Ok(<Request::Response as Outgoing>::Incoming::try_from(
|
Ok(<Request::Response>::try_from(http_response)?)
|
||||||
http_response,
|
|
||||||
)?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Send a room message to the homeserver.
|
/// Send a room message to the homeserver.
|
||||||
|
@ -1037,7 +1025,7 @@ impl AsyncClient {
|
||||||
room_id: room_id.clone(),
|
room_id: room_id.clone(),
|
||||||
event_type,
|
event_type,
|
||||||
txn_id: txn_id.unwrap_or_else(Uuid::new_v4).to_string(),
|
txn_id: txn_id.unwrap_or_else(Uuid::new_v4).to_string(),
|
||||||
data: content,
|
data: EventJson::from(content),
|
||||||
};
|
};
|
||||||
|
|
||||||
let response = self.send(request).await?;
|
let response = self.send(request).await?;
|
||||||
|
@ -1059,7 +1047,7 @@ impl AsyncClient {
|
||||||
#[instrument]
|
#[instrument]
|
||||||
async fn claim_one_time_keys(
|
async fn claim_one_time_keys(
|
||||||
&self,
|
&self,
|
||||||
one_time_keys: HashMap<UserId, HashMap<DeviceId, KeyAlgorithm>>,
|
one_time_keys: BTreeMap<UserId, BTreeMap<DeviceId, KeyAlgorithm>>,
|
||||||
) -> Result<claim_keys::Response> {
|
) -> Result<claim_keys::Response> {
|
||||||
let request = claim_keys::Request {
|
let request = claim_keys::Request {
|
||||||
timeout: None,
|
timeout: None,
|
||||||
|
@ -1068,7 +1056,7 @@ impl AsyncClient {
|
||||||
|
|
||||||
let response = self.send(request).await?;
|
let response = self.send(request).await?;
|
||||||
self.base_client
|
self.base_client
|
||||||
.write()
|
.read()
|
||||||
.await
|
.await
|
||||||
.receive_keys_claim_response(&response)
|
.receive_keys_claim_response(&response)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -1138,7 +1126,7 @@ impl AsyncClient {
|
||||||
|
|
||||||
let response = self.send(request).await?;
|
let response = self.send(request).await?;
|
||||||
self.base_client
|
self.base_client
|
||||||
.write()
|
.read()
|
||||||
.await
|
.await
|
||||||
.receive_keys_upload_response(&response)
|
.receive_keys_upload_response(&response)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -1173,7 +1161,7 @@ impl AsyncClient {
|
||||||
users_for_query
|
users_for_query
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut device_keys: HashMap<UserId, Vec<DeviceId>> = HashMap::new();
|
let mut device_keys: BTreeMap<UserId, Vec<DeviceId>> = BTreeMap::new();
|
||||||
|
|
||||||
for user in users_for_query.drain() {
|
for user in users_for_query.drain() {
|
||||||
device_keys.insert(user, Vec::new());
|
device_keys.insert(user, Vec::new());
|
||||||
|
@ -1187,7 +1175,7 @@ impl AsyncClient {
|
||||||
|
|
||||||
let response = self.send(request).await?;
|
let response = self.send(request).await?;
|
||||||
self.base_client
|
self.base_client
|
||||||
.write()
|
.read()
|
||||||
.await
|
.await
|
||||||
.receive_keys_query_response(&response)
|
.receive_keys_query_response(&response)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
#[cfg(feature = "encryption")]
|
#[cfg(feature = "encryption")]
|
||||||
use std::collections::HashSet;
|
use std::collections::{BTreeMap, HashSet};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ use crate::events::presence::PresenceEvent;
|
||||||
use crate::events::collections::only::Event as NonRoomEvent;
|
use crate::events::collections::only::Event as NonRoomEvent;
|
||||||
use crate::events::ignored_user_list::IgnoredUserListEvent;
|
use crate::events::ignored_user_list::IgnoredUserListEvent;
|
||||||
use crate::events::push_rules::{PushRulesEvent, Ruleset};
|
use crate::events::push_rules::{PushRulesEvent, Ruleset};
|
||||||
use crate::events::EventResult;
|
use crate::events::EventJson;
|
||||||
use crate::identifiers::{RoomId, UserId};
|
use crate::identifiers::{RoomId, UserId};
|
||||||
use crate::models::Room;
|
use crate::models::Room;
|
||||||
use crate::session::Session;
|
use crate::session::Session;
|
||||||
|
@ -277,11 +277,12 @@ impl Client {
|
||||||
pub async fn receive_joined_timeline_event(
|
pub async fn receive_joined_timeline_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
event: &mut EventResult<RoomEvent>,
|
event: &mut EventJson<RoomEvent>,
|
||||||
did_update: &mut bool,
|
did_update: &mut bool,
|
||||||
) -> Option<EventResult<RoomEvent>> {
|
) -> Option<EventJson<RoomEvent>> {
|
||||||
match event {
|
match event.deserialize() {
|
||||||
EventResult::Ok(e) => {
|
#[allow(unused_mut)]
|
||||||
|
Ok(mut e) => {
|
||||||
#[cfg(feature = "encryption")]
|
#[cfg(feature = "encryption")]
|
||||||
let mut decrypted_event = None;
|
let mut decrypted_event = None;
|
||||||
#[cfg(not(feature = "encryption"))]
|
#[cfg(not(feature = "encryption"))]
|
||||||
|
@ -290,12 +291,12 @@ impl Client {
|
||||||
#[cfg(feature = "encryption")]
|
#[cfg(feature = "encryption")]
|
||||||
{
|
{
|
||||||
match e {
|
match e {
|
||||||
RoomEvent::RoomEncrypted(e) => {
|
RoomEvent::RoomEncrypted(ref mut e) => {
|
||||||
e.room_id = Some(room_id.to_owned());
|
e.room_id = Some(room_id.to_owned());
|
||||||
let mut olm = self.olm.lock().await;
|
let mut olm = self.olm.lock().await;
|
||||||
|
|
||||||
if let Some(o) = &mut *olm {
|
if let Some(o) = &mut *olm {
|
||||||
decrypted_event = o.decrypt_room_event(e).await.ok();
|
decrypted_event = o.decrypt_room_event(&e).await.ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
|
@ -303,8 +304,8 @@ impl Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut room = self.get_or_create_room(&room_id).write().await;
|
let mut room = self.get_or_create_room(&room_id).write().await;
|
||||||
// Not sure what the best way to do this is ??
|
// TODO is passing in the bool to use in `AsyncClient::sync` ok here
|
||||||
*did_update = room.receive_timeline_event(e);
|
*did_update = room.receive_timeline_event(&e);
|
||||||
decrypted_event
|
decrypted_event
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -403,10 +404,7 @@ impl Client {
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `response` - The response that we received after a successful sync.
|
/// * `response` - The response that we received after a successful sync.
|
||||||
pub async fn receive_sync_response(
|
pub async fn receive_sync_response(&mut self, response: &mut api::sync::sync_events::Response) {
|
||||||
&mut self,
|
|
||||||
response: &mut api::sync::sync_events::IncomingResponse,
|
|
||||||
) {
|
|
||||||
self.sync_token = Some(response.next_batch.clone());
|
self.sync_token = Some(response.next_batch.clone());
|
||||||
|
|
||||||
#[cfg(feature = "encryption")]
|
#[cfg(feature = "encryption")]
|
||||||
|
@ -482,12 +480,12 @@ impl Client {
|
||||||
pub async fn get_missing_sessions(
|
pub async fn get_missing_sessions(
|
||||||
&self,
|
&self,
|
||||||
users: impl Iterator<Item = &UserId>,
|
users: impl Iterator<Item = &UserId>,
|
||||||
) -> HashMap<UserId, HashMap<DeviceId, KeyAlgorithm>> {
|
) -> BTreeMap<UserId, BTreeMap<DeviceId, KeyAlgorithm>> {
|
||||||
let mut olm = self.olm.lock().await;
|
let mut olm = self.olm.lock().await;
|
||||||
|
|
||||||
match &mut *olm {
|
match &mut *olm {
|
||||||
Some(o) => o.get_missing_sessions(users).await,
|
Some(o) => o.get_missing_sessions(users).await,
|
||||||
None => HashMap::new(),
|
None => BTreeMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::BTreeMap;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -23,12 +23,13 @@ use crate::api::r0::keys::{DeviceKeys, KeyAlgorithm};
|
||||||
use crate::events::Algorithm;
|
use crate::events::Algorithm;
|
||||||
use crate::identifiers::{DeviceId, UserId};
|
use crate::identifiers::{DeviceId, UserId};
|
||||||
|
|
||||||
|
/// A device represents a E2EE capable client of an user.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Device {
|
pub struct Device {
|
||||||
user_id: Arc<UserId>,
|
user_id: Arc<UserId>,
|
||||||
device_id: Arc<DeviceId>,
|
device_id: Arc<DeviceId>,
|
||||||
algorithms: Arc<Vec<Algorithm>>,
|
algorithms: Arc<Vec<Algorithm>>,
|
||||||
keys: Arc<HashMap<KeyAlgorithm, String>>,
|
keys: Arc<BTreeMap<KeyAlgorithm, String>>,
|
||||||
display_name: Arc<Option<String>>,
|
display_name: Arc<Option<String>>,
|
||||||
deleted: Arc<AtomicBool>,
|
deleted: Arc<AtomicBool>,
|
||||||
trust_state: Arc<Atomic<TrustState>>,
|
trust_state: Arc<Atomic<TrustState>>,
|
||||||
|
@ -67,7 +68,7 @@ impl Device {
|
||||||
display_name: Option<String>,
|
display_name: Option<String>,
|
||||||
trust_state: TrustState,
|
trust_state: TrustState,
|
||||||
algorithms: Vec<Algorithm>,
|
algorithms: Vec<Algorithm>,
|
||||||
keys: HashMap<KeyAlgorithm, String>,
|
keys: BTreeMap<KeyAlgorithm, String>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Device {
|
Device {
|
||||||
user_id: Arc::new(user_id),
|
user_id: Arc::new(user_id),
|
||||||
|
@ -101,7 +102,7 @@ impl Device {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a map containing all the device keys.
|
/// Get a map containing all the device keys.
|
||||||
pub fn keys(&self) -> &HashMap<KeyAlgorithm, String> {
|
pub fn keys(&self) -> &BTreeMap<KeyAlgorithm, String> {
|
||||||
&self.keys
|
&self.keys
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,7 +123,7 @@ impl Device {
|
||||||
|
|
||||||
/// Update a device with a new device keys struct.
|
/// Update a device with a new device keys struct.
|
||||||
pub(crate) fn update_device(&mut self, device_keys: &DeviceKeys) {
|
pub(crate) fn update_device(&mut self, device_keys: &DeviceKeys) {
|
||||||
let mut keys = HashMap::new();
|
let mut keys = BTreeMap::new();
|
||||||
|
|
||||||
for (key_id, key) in device_keys.keys.iter() {
|
for (key_id, key) in device_keys.keys.iter() {
|
||||||
let key_id = key_id.0;
|
let key_id = key_id.0;
|
||||||
|
@ -152,7 +153,7 @@ impl Device {
|
||||||
|
|
||||||
impl From<&DeviceKeys> for Device {
|
impl From<&DeviceKeys> for Device {
|
||||||
fn from(device_keys: &DeviceKeys) -> Self {
|
fn from(device_keys: &DeviceKeys) -> Self {
|
||||||
let mut keys = HashMap::new();
|
let mut keys = BTreeMap::new();
|
||||||
|
|
||||||
for (key_id, key) in device_keys.keys.iter() {
|
for (key_id, key) in device_keys.keys.iter() {
|
||||||
let key_id = key_id.0;
|
let key_id = key_id.0;
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
#[cfg(feature = "sqlite-cryptostore")]
|
#[cfg(feature = "sqlite-cryptostore")]
|
||||||
|
@ -21,28 +21,17 @@ use std::result::Result as StdResult;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use super::error::{OlmError, Result, SignatureError, VerificationResult};
|
use super::error::{OlmError, Result, SignatureError, VerificationResult};
|
||||||
use super::olm::{Account, GroupSessionKey, InboundGroupSession, OutboundGroupSession, Session};
|
use super::olm::{
|
||||||
|
Account, GroupSessionKey, InboundGroupSession, OlmMessage, OlmUtility, OutboundGroupSession,
|
||||||
|
Session,
|
||||||
|
};
|
||||||
use super::store::memorystore::MemoryStore;
|
use super::store::memorystore::MemoryStore;
|
||||||
#[cfg(feature = "sqlite-cryptostore")]
|
#[cfg(feature = "sqlite-cryptostore")]
|
||||||
use super::store::sqlite::SqliteStore;
|
use super::store::sqlite::SqliteStore;
|
||||||
use super::{device::Device, CryptoStore};
|
use super::{device::Device, CryptoStore};
|
||||||
|
|
||||||
use crate::api;
|
use crate::api;
|
||||||
|
use crate::events::{
|
||||||
use api::r0::keys;
|
|
||||||
|
|
||||||
use cjson;
|
|
||||||
use olm_rs::{session::OlmMessage, utility::OlmUtility};
|
|
||||||
use serde_json::{json, Value};
|
|
||||||
use tracing::{debug, error, info, instrument, trace, warn};
|
|
||||||
|
|
||||||
use ruma_client_api::r0::client_exchange::{
|
|
||||||
send_event_to_device::Request as ToDeviceRequest, DeviceIdOrAllDevices,
|
|
||||||
};
|
|
||||||
use ruma_client_api::r0::keys::{
|
|
||||||
AlgorithmAndDeviceId, DeviceKeys, KeyAlgorithm, OneTimeKey, SignedKey,
|
|
||||||
};
|
|
||||||
use ruma_client_api::r0::sync::sync_events::IncomingResponse as SyncResponse;
|
|
||||||
use ruma_events::{
|
|
||||||
collections::all::RoomEvent,
|
collections::all::RoomEvent,
|
||||||
room::encrypted::{
|
room::encrypted::{
|
||||||
CiphertextInfo, EncryptedEvent, EncryptedEventContent, MegolmV1AesSha2Content,
|
CiphertextInfo, EncryptedEvent, EncryptedEventContent, MegolmV1AesSha2Content,
|
||||||
|
@ -53,14 +42,23 @@ use ruma_events::{
|
||||||
AnyToDeviceEvent as ToDeviceEvent, ToDeviceEncrypted, ToDeviceForwardedRoomKey,
|
AnyToDeviceEvent as ToDeviceEvent, ToDeviceEncrypted, ToDeviceForwardedRoomKey,
|
||||||
ToDeviceRoomKey, ToDeviceRoomKeyRequest,
|
ToDeviceRoomKey, ToDeviceRoomKeyRequest,
|
||||||
},
|
},
|
||||||
Algorithm, EventResult, EventType,
|
Algorithm, EventJson, EventType,
|
||||||
};
|
};
|
||||||
use ruma_identifiers::RoomId;
|
use crate::identifiers::{DeviceId, RoomId, UserId};
|
||||||
use ruma_identifiers::{DeviceId, UserId};
|
|
||||||
|
|
||||||
pub type OneTimeKeys = HashMap<AlgorithmAndDeviceId, OneTimeKey>;
|
use api::r0::keys;
|
||||||
|
use api::r0::{
|
||||||
|
client_exchange::{send_event_to_device::Request as ToDeviceRequest, DeviceIdOrAllDevices},
|
||||||
|
keys::{AlgorithmAndDeviceId, DeviceKeys, KeyAlgorithm, OneTimeKey, SignedKey},
|
||||||
|
sync::sync_events::Response as SyncResponse,
|
||||||
|
};
|
||||||
|
|
||||||
|
use cjson;
|
||||||
|
use serde_json::{json, Value};
|
||||||
|
use tracing::{debug, error, info, instrument, trace, warn};
|
||||||
|
|
||||||
|
pub type OneTimeKeys = BTreeMap<AlgorithmAndDeviceId, OneTimeKey>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct OlmMachine {
|
pub struct OlmMachine {
|
||||||
/// The unique user id that owns this account.
|
/// The unique user id that owns this account.
|
||||||
user_id: UserId,
|
user_id: UserId,
|
||||||
|
@ -84,6 +82,16 @@ pub struct OlmMachine {
|
||||||
outbound_group_session: HashMap<RoomId, OutboundGroupSession>,
|
outbound_group_session: HashMap<RoomId, OutboundGroupSession>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(tarpaulin, skip)]
|
||||||
|
impl std::fmt::Debug for OlmMachine {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("OlmMachine")
|
||||||
|
.field("user_id", &self.user_id)
|
||||||
|
.field("device_id", &self.device_id)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl OlmMachine {
|
impl OlmMachine {
|
||||||
const ALGORITHMS: &'static [&'static ruma_events::Algorithm] = &[
|
const ALGORITHMS: &'static [&'static ruma_events::Algorithm] = &[
|
||||||
&Algorithm::OlmV1Curve25519AesSha2,
|
&Algorithm::OlmV1Curve25519AesSha2,
|
||||||
|
@ -194,8 +202,8 @@ impl OlmMachine {
|
||||||
pub async fn get_missing_sessions(
|
pub async fn get_missing_sessions(
|
||||||
&mut self,
|
&mut self,
|
||||||
users: impl Iterator<Item = &UserId>,
|
users: impl Iterator<Item = &UserId>,
|
||||||
) -> HashMap<UserId, HashMap<DeviceId, KeyAlgorithm>> {
|
) -> BTreeMap<UserId, BTreeMap<DeviceId, KeyAlgorithm>> {
|
||||||
let mut missing = HashMap::new();
|
let mut missing = BTreeMap::new();
|
||||||
|
|
||||||
for user_id in users {
|
for user_id in users {
|
||||||
let user_devices = self.store.get_user_devices(user_id).await.unwrap();
|
let user_devices = self.store.get_user_devices(user_id).await.unwrap();
|
||||||
|
@ -217,7 +225,7 @@ impl OlmMachine {
|
||||||
|
|
||||||
if is_missing {
|
if is_missing {
|
||||||
if !missing.contains_key(user_id) {
|
if !missing.contains_key(user_id) {
|
||||||
missing.insert(user_id.clone(), HashMap::new());
|
missing.insert(user_id.clone(), BTreeMap::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
let user_map = missing.get_mut(user_id).unwrap();
|
let user_map = missing.get_mut(user_id).unwrap();
|
||||||
|
@ -428,12 +436,13 @@ impl OlmMachine {
|
||||||
for device_id in deleted_devices {
|
for device_id in deleted_devices {
|
||||||
if let Some(device) = stored_devices.get(device_id) {
|
if let Some(device) = stored_devices.get(device_id) {
|
||||||
device.mark_as_deleted();
|
device.mark_as_deleted();
|
||||||
// TODO change this to a delete device.
|
// TODO change this to a bulk deletion.
|
||||||
self.store.save_device(device).await?;
|
self.store.delete_device(device).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO change this to a bulk operation.
|
||||||
for device in &changed_devices {
|
for device in &changed_devices {
|
||||||
self.store.save_device(device.clone()).await?;
|
self.store.save_device(device.clone()).await?;
|
||||||
}
|
}
|
||||||
|
@ -472,7 +481,7 @@ impl OlmMachine {
|
||||||
async fn device_keys(&self) -> DeviceKeys {
|
async fn device_keys(&self) -> DeviceKeys {
|
||||||
let identity_keys = self.account.identity_keys();
|
let identity_keys = self.account.identity_keys();
|
||||||
|
|
||||||
let mut keys = HashMap::new();
|
let mut keys = BTreeMap::new();
|
||||||
|
|
||||||
keys.insert(
|
keys.insert(
|
||||||
AlgorithmAndDeviceId(KeyAlgorithm::Curve25519, self.device_id.clone()),
|
AlgorithmAndDeviceId(KeyAlgorithm::Curve25519, self.device_id.clone()),
|
||||||
|
@ -490,9 +499,9 @@ impl OlmMachine {
|
||||||
"keys": keys,
|
"keys": keys,
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut signatures = HashMap::new();
|
let mut signatures = BTreeMap::new();
|
||||||
|
|
||||||
let mut signature = HashMap::new();
|
let mut signature = BTreeMap::new();
|
||||||
signature.insert(
|
signature.insert(
|
||||||
AlgorithmAndDeviceId(KeyAlgorithm::Ed25519, self.device_id.clone()),
|
AlgorithmAndDeviceId(KeyAlgorithm::Ed25519, self.device_id.clone()),
|
||||||
self.sign_json(&device_keys).await,
|
self.sign_json(&device_keys).await,
|
||||||
|
@ -518,7 +527,7 @@ impl OlmMachine {
|
||||||
async fn signed_one_time_keys(&self) -> StdResult<OneTimeKeys, ()> {
|
async fn signed_one_time_keys(&self) -> StdResult<OneTimeKeys, ()> {
|
||||||
let _ = self.generate_one_time_keys().await?;
|
let _ = self.generate_one_time_keys().await?;
|
||||||
let one_time_keys = self.account.one_time_keys().await;
|
let one_time_keys = self.account.one_time_keys().await;
|
||||||
let mut one_time_key_map = HashMap::new();
|
let mut one_time_key_map = BTreeMap::new();
|
||||||
|
|
||||||
for (key_id, key) in one_time_keys.curve25519().iter() {
|
for (key_id, key) in one_time_keys.curve25519().iter() {
|
||||||
let key_json = json!({
|
let key_json = json!({
|
||||||
|
@ -527,14 +536,14 @@ impl OlmMachine {
|
||||||
|
|
||||||
let signature = self.sign_json(&key_json).await;
|
let signature = self.sign_json(&key_json).await;
|
||||||
|
|
||||||
let mut signature_map = HashMap::new();
|
let mut signature_map = BTreeMap::new();
|
||||||
|
|
||||||
signature_map.insert(
|
signature_map.insert(
|
||||||
AlgorithmAndDeviceId(KeyAlgorithm::Ed25519, self.device_id.clone()),
|
AlgorithmAndDeviceId(KeyAlgorithm::Ed25519, self.device_id.clone()),
|
||||||
signature,
|
signature,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut signatures = HashMap::new();
|
let mut signatures = BTreeMap::new();
|
||||||
signatures.insert(self.user_id.clone(), signature_map);
|
signatures.insert(self.user_id.clone(), signature_map);
|
||||||
|
|
||||||
let signed_key = SignedKey {
|
let signed_key = SignedKey {
|
||||||
|
@ -655,22 +664,30 @@ impl OlmMachine {
|
||||||
|
|
||||||
async fn try_decrypt_olm_event(
|
async fn try_decrypt_olm_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
sender: &UserId,
|
||||||
sender_key: &str,
|
sender_key: &str,
|
||||||
message: &OlmMessage,
|
message: &OlmMessage,
|
||||||
) -> Result<Option<String>> {
|
) -> Result<Option<String>> {
|
||||||
let s = self.store.get_sessions(sender_key).await?;
|
let s = self.store.get_sessions(sender_key).await?;
|
||||||
|
|
||||||
|
// We don't have any existing sessions, return early.
|
||||||
let sessions = if let Some(s) = s {
|
let sessions = if let Some(s) = s {
|
||||||
s
|
s
|
||||||
} else {
|
} else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut session_to_save = None;
|
||||||
|
let mut plaintext = None;
|
||||||
|
|
||||||
for session in &mut *sessions.lock().await {
|
for session in &mut *sessions.lock().await {
|
||||||
let mut matches = false;
|
let mut matches = false;
|
||||||
|
|
||||||
|
// If this is a pre-key message check if it was encrypted for our
|
||||||
|
// session, if it wasn't decryption will fail so no need to try.
|
||||||
if let OlmMessage::PreKey(m) = &message {
|
if let OlmMessage::PreKey(m) = &message {
|
||||||
matches = session.matches(sender_key, m.clone()).await?;
|
matches = session.matches(sender_key, m.clone()).await?;
|
||||||
|
|
||||||
if !matches {
|
if !matches {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -679,48 +696,154 @@ impl OlmMachine {
|
||||||
let ret = session.decrypt(message.clone()).await;
|
let ret = session.decrypt(message.clone()).await;
|
||||||
|
|
||||||
if let Ok(p) = ret {
|
if let Ok(p) = ret {
|
||||||
self.store.save_session(session.clone()).await?;
|
plaintext = Some(p);
|
||||||
return Ok(Some(p));
|
session_to_save = Some(session.clone());
|
||||||
|
|
||||||
|
break;
|
||||||
} else {
|
} else {
|
||||||
|
// Decryption failed with a matching session, the session is
|
||||||
|
// likely wedged and needs to be rotated.
|
||||||
if matches {
|
if matches {
|
||||||
|
warn!(
|
||||||
|
"Found a matching Olm session yet decryption failed
|
||||||
|
for sender {} and sender_key {}",
|
||||||
|
sender, sender_key
|
||||||
|
);
|
||||||
return Err(OlmError::SessionWedged);
|
return Err(OlmError::SessionWedged);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(None)
|
if let Some(session) = session_to_save {
|
||||||
|
// Decryption was successful, save the new ratchet state of the
|
||||||
|
// session that was used to decrypt the message.
|
||||||
|
trace!("Saved the new session state for {}", sender);
|
||||||
|
self.store.save_session(session).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(plaintext)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn decrypt_olm_message(
|
async fn decrypt_olm_message(
|
||||||
&mut self,
|
&mut self,
|
||||||
_sender: &str,
|
sender: &UserId,
|
||||||
sender_key: &str,
|
sender_key: &str,
|
||||||
message: OlmMessage,
|
message: OlmMessage,
|
||||||
) -> Result<EventResult<ToDeviceEvent>> {
|
) -> Result<(EventJson<ToDeviceEvent>, String)> {
|
||||||
let plaintext = if let Some(p) = self.try_decrypt_olm_event(sender_key, &message).await? {
|
// First try to decrypt using an existing session.
|
||||||
|
let plaintext = if let Some(p) = self
|
||||||
|
.try_decrypt_olm_event(sender, sender_key, &message)
|
||||||
|
.await?
|
||||||
|
{
|
||||||
|
// Decryption succeeded, destructure the plaintext out of the
|
||||||
|
// Option.
|
||||||
p
|
p
|
||||||
} else {
|
} else {
|
||||||
|
// Decryption failed with every known session, let's try to create a
|
||||||
|
// new session.
|
||||||
let mut session = match &message {
|
let mut session = match &message {
|
||||||
OlmMessage::Message(_) => return Err(OlmError::SessionWedged),
|
// A new session can only be created using a pre-key message,
|
||||||
|
// return with an error if it isn't one.
|
||||||
|
OlmMessage::Message(_) => {
|
||||||
|
warn!(
|
||||||
|
"Failed to decrypt a non-pre-key message with all
|
||||||
|
available sessions {} {}",
|
||||||
|
sender, sender_key
|
||||||
|
);
|
||||||
|
return Err(OlmError::SessionWedged);
|
||||||
|
}
|
||||||
|
|
||||||
OlmMessage::PreKey(m) => {
|
OlmMessage::PreKey(m) => {
|
||||||
let session = self
|
// Create the new session.
|
||||||
|
let session = match self
|
||||||
.account
|
.account
|
||||||
.create_inbound_session(sender_key, m.clone())
|
.create_inbound_session(sender_key, m.clone())
|
||||||
.await?;
|
.await
|
||||||
|
{
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => {
|
||||||
|
warn!(
|
||||||
|
"Failed to create a new Olm session for {} {}
|
||||||
|
from a prekey message: {}",
|
||||||
|
sender, sender_key, e
|
||||||
|
);
|
||||||
|
return Err(OlmError::SessionWedged);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Save the account since we remove the one-time key that
|
||||||
|
// was used to create this session.
|
||||||
self.store.save_account(self.account.clone()).await?;
|
self.store.save_account(self.account.clone()).await?;
|
||||||
session
|
session
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Decrypt our message, this shouldn't fail since we're using a
|
||||||
|
// newly created Session.
|
||||||
let plaintext = session.decrypt(message).await?;
|
let plaintext = session.decrypt(message).await?;
|
||||||
|
|
||||||
|
// Save the new ratcheted state of the session.
|
||||||
self.store.save_session(session).await?;
|
self.store.save_session(session).await?;
|
||||||
plaintext
|
plaintext
|
||||||
};
|
};
|
||||||
|
|
||||||
trace!("Successfully decrypted a Olm message: {}", plaintext);
|
trace!("Successfully decrypted a Olm message: {}", plaintext);
|
||||||
Ok(serde_json::from_str::<EventResult<ToDeviceEvent>>(
|
|
||||||
&plaintext,
|
Ok(self.parse_decrypted_to_device_event(sender, &plaintext)?)
|
||||||
)?)
|
}
|
||||||
|
|
||||||
|
fn parse_decrypted_to_device_event(
|
||||||
|
&self,
|
||||||
|
sender: &UserId,
|
||||||
|
plaintext: &str,
|
||||||
|
) -> Result<(EventJson<ToDeviceEvent>, String)> {
|
||||||
|
// TODO make the errors a bit more specific.
|
||||||
|
let decrypted_json: Value = serde_json::from_str(&plaintext)?;
|
||||||
|
|
||||||
|
let encrytped_sender = decrypted_json
|
||||||
|
.get("sender")
|
||||||
|
.cloned()
|
||||||
|
.ok_or(OlmError::MissingCiphertext)?;
|
||||||
|
let encrytped_sender: UserId = serde_json::from_value(encrytped_sender)?;
|
||||||
|
let recipient = decrypted_json
|
||||||
|
.get("recipient")
|
||||||
|
.cloned()
|
||||||
|
.ok_or(OlmError::MissingCiphertext)?;
|
||||||
|
let recipient: UserId = serde_json::from_value(recipient)?;
|
||||||
|
|
||||||
|
let recipient_keys: BTreeMap<KeyAlgorithm, String> = serde_json::from_value(
|
||||||
|
decrypted_json
|
||||||
|
.get("recipient_keys")
|
||||||
|
.cloned()
|
||||||
|
.ok_or(OlmError::MissingCiphertext)?,
|
||||||
|
)?;
|
||||||
|
let keys: BTreeMap<KeyAlgorithm, String> = serde_json::from_value(
|
||||||
|
decrypted_json
|
||||||
|
.get("keys")
|
||||||
|
.cloned()
|
||||||
|
.ok_or(OlmError::MissingCiphertext)?,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
if recipient != self.user_id || sender != &encrytped_sender {
|
||||||
|
return Err(OlmError::MissingCiphertext);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.account.identity_keys().ed25519()
|
||||||
|
!= recipient_keys
|
||||||
|
.get(&KeyAlgorithm::Ed25519)
|
||||||
|
.ok_or(OlmError::MissingCiphertext)?
|
||||||
|
{
|
||||||
|
return Err(OlmError::MissingCiphertext);
|
||||||
|
}
|
||||||
|
|
||||||
|
let signing_key = keys
|
||||||
|
.get(&KeyAlgorithm::Ed25519)
|
||||||
|
.ok_or(OlmError::MissingSigningKey)?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
serde_json::from_value::<EventJson<ToDeviceEvent>>(decrypted_json)?,
|
||||||
|
signing_key.to_owned(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decrypt a to-device event.
|
/// Decrypt a to-device event.
|
||||||
|
@ -731,11 +854,10 @@ impl OlmMachine {
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `event` - The to-device event that should be decrypted.
|
/// * `event` - The to-device event that should be decrypted.
|
||||||
#[instrument]
|
|
||||||
async fn decrypt_to_device_event(
|
async fn decrypt_to_device_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
event: &ToDeviceEncrypted,
|
event: &ToDeviceEncrypted,
|
||||||
) -> Result<EventResult<ToDeviceEvent>> {
|
) -> Result<EventJson<ToDeviceEvent>> {
|
||||||
info!("Decrypting to-device event");
|
info!("Decrypting to-device event");
|
||||||
|
|
||||||
let content = if let EncryptedEventContent::OlmV1Curve25519AesSha2(c) = &event.content {
|
let content = if let EncryptedEventContent::OlmV1Curve25519AesSha2(c) = &event.content {
|
||||||
|
@ -749,21 +871,33 @@ impl OlmMachine {
|
||||||
let own_key = identity_keys.curve25519();
|
let own_key = identity_keys.curve25519();
|
||||||
let own_ciphertext = content.ciphertext.get(own_key);
|
let own_ciphertext = content.ciphertext.get(own_key);
|
||||||
|
|
||||||
|
// Try to find a ciphertext that was meant for our device.
|
||||||
if let Some(ciphertext) = own_ciphertext {
|
if let Some(ciphertext) = own_ciphertext {
|
||||||
let message_type: u8 = ciphertext
|
let message_type: u8 = ciphertext
|
||||||
.message_type
|
.message_type
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(|_| OlmError::UnsupportedOlmType)?;
|
.map_err(|_| OlmError::UnsupportedOlmType)?;
|
||||||
|
|
||||||
|
// Create a OlmMessage from the ciphertext and the type.
|
||||||
let message =
|
let message =
|
||||||
OlmMessage::from_type_and_ciphertext(message_type.into(), ciphertext.body.clone())
|
OlmMessage::from_type_and_ciphertext(message_type.into(), ciphertext.body.clone())
|
||||||
.map_err(|_| OlmError::UnsupportedOlmType)?;
|
.map_err(|_| OlmError::UnsupportedOlmType)?;
|
||||||
|
|
||||||
let mut decrypted_event = self
|
// Decrypt the OlmMessage and get a Ruma event out of it.
|
||||||
.decrypt_olm_message(&event.sender.to_string(), &content.sender_key, message)
|
let (mut decrypted_event, signing_key) = self
|
||||||
|
.decrypt_olm_message(&event.sender, &content.sender_key, message)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
debug!("Decrypted a to-device event {:?}", decrypted_event);
|
debug!("Decrypted a to-device event {:?}", decrypted_event);
|
||||||
self.handle_decrypted_to_device_event(&content.sender_key, &mut decrypted_event)
|
|
||||||
.await?;
|
// Handle the decrypted event, e.g. fetch out megolm sessions out of
|
||||||
|
// the event.
|
||||||
|
self.handle_decrypted_to_device_event(
|
||||||
|
&content.sender_key,
|
||||||
|
&signing_key,
|
||||||
|
&mut decrypted_event,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(decrypted_event)
|
Ok(decrypted_event)
|
||||||
} else {
|
} else {
|
||||||
|
@ -772,15 +906,14 @@ impl OlmMachine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn add_room_key(&mut self, sender_key: &str, event: &mut ToDeviceRoomKey) -> Result<()> {
|
async fn add_room_key(
|
||||||
|
&mut self,
|
||||||
|
sender_key: &str,
|
||||||
|
signing_key: &str,
|
||||||
|
event: &mut ToDeviceRoomKey,
|
||||||
|
) -> Result<()> {
|
||||||
match event.content.algorithm {
|
match event.content.algorithm {
|
||||||
Algorithm::MegolmV1AesSha2 => {
|
Algorithm::MegolmV1AesSha2 => {
|
||||||
// TODO check for all the valid fields.
|
|
||||||
let signing_key = event
|
|
||||||
.keys
|
|
||||||
.get("ed25519")
|
|
||||||
.ok_or(OlmError::MissingSigningKey)?;
|
|
||||||
|
|
||||||
let session_key = GroupSessionKey(mem::take(&mut event.content.session_key));
|
let session_key = GroupSessionKey(mem::take(&mut event.content.session_key));
|
||||||
|
|
||||||
let session = InboundGroupSession::new(
|
let session = InboundGroupSession::new(
|
||||||
|
@ -828,7 +961,7 @@ impl OlmMachine {
|
||||||
&self,
|
&self,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
content: MessageEventContent,
|
content: MessageEventContent,
|
||||||
) -> Result<MegolmV1AesSha2Content> {
|
) -> Result<EncryptedEventContent> {
|
||||||
let session = self.outbound_group_session.get(room_id);
|
let session = self.outbound_group_session.get(room_id);
|
||||||
|
|
||||||
let session = if let Some(s) = session {
|
let session = if let Some(s) = session {
|
||||||
|
@ -856,13 +989,15 @@ impl OlmMachine {
|
||||||
|
|
||||||
let ciphertext = session.encrypt(plaintext).await;
|
let ciphertext = session.encrypt(plaintext).await;
|
||||||
|
|
||||||
Ok(MegolmV1AesSha2Content {
|
Ok(EncryptedEventContent::MegolmV1AesSha2(
|
||||||
algorithm: Algorithm::MegolmV1AesSha2,
|
MegolmV1AesSha2Content {
|
||||||
ciphertext,
|
algorithm: Algorithm::MegolmV1AesSha2,
|
||||||
sender_key: self.account.identity_keys().curve25519().to_owned(),
|
ciphertext,
|
||||||
session_id: session.session_id().to_owned(),
|
sender_key: self.account.identity_keys().curve25519().to_owned(),
|
||||||
device_id: self.device_id.to_owned(),
|
session_id: session.session_id().to_owned(),
|
||||||
})
|
device_id: self.device_id.to_owned(),
|
||||||
|
},
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn olm_encrypt(
|
async fn olm_encrypt(
|
||||||
|
@ -871,7 +1006,7 @@ impl OlmMachine {
|
||||||
recipient_device: &Device,
|
recipient_device: &Device,
|
||||||
event_type: EventType,
|
event_type: EventType,
|
||||||
content: Value,
|
content: Value,
|
||||||
) -> Result<OlmV1Curve25519AesSha2Content> {
|
) -> Result<EncryptedEventContent> {
|
||||||
let identity_keys = self.account.identity_keys();
|
let identity_keys = self.account.identity_keys();
|
||||||
|
|
||||||
let recipient_signing_key = recipient_device
|
let recipient_signing_key = recipient_device
|
||||||
|
@ -908,15 +1043,17 @@ impl OlmMachine {
|
||||||
message_type: (message_type as u32).into(),
|
message_type: (message_type as u32).into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut content = HashMap::new();
|
let mut content = BTreeMap::new();
|
||||||
|
|
||||||
content.insert(recipient_sender_key.to_owned(), ciphertext);
|
content.insert(recipient_sender_key.to_owned(), ciphertext);
|
||||||
|
|
||||||
Ok(OlmV1Curve25519AesSha2Content {
|
Ok(EncryptedEventContent::OlmV1Curve25519AesSha2(
|
||||||
algorithm: Algorithm::OlmV1Curve25519AesSha2,
|
OlmV1Curve25519AesSha2Content {
|
||||||
sender_key: identity_keys.curve25519().to_owned(),
|
algorithm: Algorithm::OlmV1Curve25519AesSha2,
|
||||||
ciphertext: content,
|
sender_key: identity_keys.curve25519().to_owned(),
|
||||||
})
|
ciphertext: content,
|
||||||
|
},
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Should the client share a group session for the given room.
|
/// Should the client share a group session for the given room.
|
||||||
|
@ -999,11 +1136,11 @@ impl OlmMachine {
|
||||||
let mut message_vec = Vec::new();
|
let mut message_vec = Vec::new();
|
||||||
|
|
||||||
for user_map_chunk in user_map.chunks(OlmMachine::MAX_TO_DEVICE_MESSAGES) {
|
for user_map_chunk in user_map.chunks(OlmMachine::MAX_TO_DEVICE_MESSAGES) {
|
||||||
let mut messages = HashMap::new();
|
let mut messages = BTreeMap::new();
|
||||||
|
|
||||||
for (session, device) in user_map_chunk {
|
for (session, device) in user_map_chunk {
|
||||||
if !messages.contains_key(device.user_id()) {
|
if !messages.contains_key(device.user_id()) {
|
||||||
messages.insert(device.user_id().clone(), HashMap::new());
|
messages.insert(device.user_id().clone(), BTreeMap::new());
|
||||||
};
|
};
|
||||||
|
|
||||||
let user_messages = messages.get_mut(device.user_id()).unwrap();
|
let user_messages = messages.get_mut(device.user_id()).unwrap();
|
||||||
|
@ -1019,12 +1156,12 @@ impl OlmMachine {
|
||||||
|
|
||||||
user_messages.insert(
|
user_messages.insert(
|
||||||
DeviceIdOrAllDevices::DeviceId(device.device_id().clone()),
|
DeviceIdOrAllDevices::DeviceId(device.device_id().clone()),
|
||||||
encrypted_content,
|
EventJson::from(MessageEventContent::Encrypted(encrypted_content)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
message_vec.push(ToDeviceRequest {
|
message_vec.push(ToDeviceRequest {
|
||||||
event_type: "m.room.encrypted".to_owned(),
|
event_type: EventType::RoomEncrypted,
|
||||||
txn_id: Uuid::new_v4().to_string(),
|
txn_id: Uuid::new_v4().to_string(),
|
||||||
messages,
|
messages,
|
||||||
});
|
});
|
||||||
|
@ -1036,6 +1173,7 @@ impl OlmMachine {
|
||||||
fn add_forwarded_room_key(
|
fn add_forwarded_room_key(
|
||||||
&self,
|
&self,
|
||||||
_sender_key: &str,
|
_sender_key: &str,
|
||||||
|
_signing_key: &str,
|
||||||
_event: &ToDeviceForwardedRoomKey,
|
_event: &ToDeviceForwardedRoomKey,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1045,9 +1183,10 @@ impl OlmMachine {
|
||||||
async fn handle_decrypted_to_device_event(
|
async fn handle_decrypted_to_device_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
sender_key: &str,
|
sender_key: &str,
|
||||||
event: &mut EventResult<ToDeviceEvent>,
|
signing_key: &str,
|
||||||
|
event: &mut EventJson<ToDeviceEvent>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let event = if let EventResult::Ok(e) = event {
|
let event = if let Ok(e) = event.deserialize() {
|
||||||
e
|
e
|
||||||
} else {
|
} else {
|
||||||
warn!("Decrypted to-device event failed to be parsed correctly");
|
warn!("Decrypted to-device event failed to be parsed correctly");
|
||||||
|
@ -1055,8 +1194,12 @@ impl OlmMachine {
|
||||||
};
|
};
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
ToDeviceEvent::RoomKey(e) => self.add_room_key(sender_key, e).await,
|
ToDeviceEvent::RoomKey(mut e) => {
|
||||||
ToDeviceEvent::ForwardedRoomKey(e) => self.add_forwarded_room_key(sender_key, e),
|
self.add_room_key(sender_key, signing_key, &mut e).await
|
||||||
|
}
|
||||||
|
ToDeviceEvent::ForwardedRoomKey(mut e) => {
|
||||||
|
self.add_forwarded_room_key(sender_key, signing_key, &mut e)
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
warn!("Received a unexpected encrypted to-device event");
|
warn!("Received a unexpected encrypted to-device event");
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -1089,7 +1232,7 @@ impl OlmMachine {
|
||||||
self.uploaded_signed_key_count = Some(count);
|
self.uploaded_signed_key_count = Some(count);
|
||||||
|
|
||||||
for event_result in &mut response.to_device.events {
|
for event_result in &mut response.to_device.events {
|
||||||
let event = if let EventResult::Ok(e) = &event_result {
|
let event = if let Ok(e) = event_result.deserialize() {
|
||||||
e
|
e
|
||||||
} else {
|
} else {
|
||||||
// Skip invalid events.
|
// Skip invalid events.
|
||||||
|
@ -1099,7 +1242,7 @@ impl OlmMachine {
|
||||||
|
|
||||||
info!("Received a to-device event {:?}", event);
|
info!("Received a to-device event {:?}", event);
|
||||||
|
|
||||||
match event {
|
match &event {
|
||||||
ToDeviceEvent::RoomEncrypted(e) => {
|
ToDeviceEvent::RoomEncrypted(e) => {
|
||||||
let decrypted_event = match self.decrypt_to_device_event(e).await {
|
let decrypted_event = match self.decrypt_to_device_event(e).await {
|
||||||
Ok(e) => e,
|
Ok(e) => e,
|
||||||
|
@ -1124,7 +1267,7 @@ impl OlmMachine {
|
||||||
| ToDeviceEvent::KeyVerificationKey(..)
|
| ToDeviceEvent::KeyVerificationKey(..)
|
||||||
| ToDeviceEvent::KeyVerificationMac(..)
|
| ToDeviceEvent::KeyVerificationMac(..)
|
||||||
| ToDeviceEvent::KeyVerificationRequest(..)
|
| ToDeviceEvent::KeyVerificationRequest(..)
|
||||||
| ToDeviceEvent::KeyVerificationStart(..) => self.handle_verification_event(event),
|
| ToDeviceEvent::KeyVerificationStart(..) => self.handle_verification_event(&event),
|
||||||
_ => continue,
|
_ => continue,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1133,7 +1276,7 @@ impl OlmMachine {
|
||||||
pub async fn decrypt_room_event(
|
pub async fn decrypt_room_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
event: &EncryptedEvent,
|
event: &EncryptedEvent,
|
||||||
) -> Result<EventResult<RoomEvent>> {
|
) -> Result<EventJson<RoomEvent>> {
|
||||||
let content = match &event.content {
|
let content = match &event.content {
|
||||||
EncryptedEventContent::MegolmV1AesSha2(c) => c,
|
EncryptedEventContent::MegolmV1AesSha2(c) => c,
|
||||||
_ => return Err(OlmError::UnsupportedAlgorithm),
|
_ => return Err(OlmError::UnsupportedAlgorithm),
|
||||||
|
@ -1157,15 +1300,24 @@ impl OlmMachine {
|
||||||
.as_object_mut()
|
.as_object_mut()
|
||||||
.ok_or(OlmError::NotAnObject)?;
|
.ok_or(OlmError::NotAnObject)?;
|
||||||
|
|
||||||
let server_ts: u64 = event.origin_server_ts.into();
|
// TODO better number conversion here.
|
||||||
|
let server_ts = event
|
||||||
|
.origin_server_ts
|
||||||
|
.duration_since(std::time::SystemTime::UNIX_EPOCH)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.as_millis();
|
||||||
|
let server_ts: i64 = server_ts.try_into().unwrap_or_default();
|
||||||
|
|
||||||
decrypted_object.insert("sender".to_owned(), event.sender.to_string().into());
|
decrypted_object.insert("sender".to_owned(), event.sender.to_string().into());
|
||||||
decrypted_object.insert("event_id".to_owned(), event.event_id.to_string().into());
|
decrypted_object.insert("event_id".to_owned(), event.event_id.to_string().into());
|
||||||
decrypted_object.insert("origin_server_ts".to_owned(), server_ts.into());
|
decrypted_object.insert("origin_server_ts".to_owned(), server_ts.into());
|
||||||
|
|
||||||
decrypted_object.insert("unsigned".to_owned(), event.unsigned.clone().into());
|
decrypted_object.insert(
|
||||||
|
"unsigned".to_owned(),
|
||||||
|
serde_json::to_value(&event.unsigned).unwrap_or_default(),
|
||||||
|
);
|
||||||
|
|
||||||
let decrypted_event = serde_json::from_value::<EventResult<RoomEvent>>(decrypted_value)?;
|
let decrypted_event = serde_json::from_value::<EventJson<RoomEvent>>(decrypted_value)?;
|
||||||
trace!("Successfully decrypted megolm event {:?}", decrypted_event);
|
trace!("Successfully decrypted megolm event {:?}", decrypted_event);
|
||||||
// TODO set the encryption info on the event (is it verified, was it
|
// TODO set the encryption info on the event (is it verified, was it
|
||||||
// decrypted, sender key...)
|
// decrypted, sender key...)
|
||||||
|
|
|
@ -12,15 +12,14 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
mod error;
|
|
||||||
// TODO remove this.
|
|
||||||
mod device;
|
mod device;
|
||||||
|
mod error;
|
||||||
mod machine;
|
mod machine;
|
||||||
mod memory_stores;
|
mod memory_stores;
|
||||||
#[allow(dead_code)]
|
|
||||||
mod olm;
|
mod olm;
|
||||||
mod store;
|
mod store;
|
||||||
|
|
||||||
|
pub use device::{Device, TrustState};
|
||||||
pub use error::OlmError;
|
pub use error::OlmError;
|
||||||
pub use machine::{OlmMachine, OneTimeKeys};
|
pub use machine::{OlmMachine, OneTimeKeys};
|
||||||
pub use store::{CryptoStore, CryptoStoreError};
|
pub use store::{CryptoStore, CryptoStoreError};
|
||||||
|
|
|
@ -26,9 +26,14 @@ use olm_rs::account::{IdentityKeys, OlmAccount, OneTimeKeys};
|
||||||
use olm_rs::errors::{OlmAccountError, OlmGroupSessionError, OlmSessionError};
|
use olm_rs::errors::{OlmAccountError, OlmGroupSessionError, OlmSessionError};
|
||||||
use olm_rs::inbound_group_session::OlmInboundGroupSession;
|
use olm_rs::inbound_group_session::OlmInboundGroupSession;
|
||||||
use olm_rs::outbound_group_session::OlmOutboundGroupSession;
|
use olm_rs::outbound_group_session::OlmOutboundGroupSession;
|
||||||
use olm_rs::session::{OlmMessage, OlmSession, PreKeyMessage};
|
use olm_rs::session::OlmSession;
|
||||||
use olm_rs::PicklingMode;
|
use olm_rs::PicklingMode;
|
||||||
|
|
||||||
|
pub use olm_rs::{
|
||||||
|
session::{OlmMessage, PreKeyMessage},
|
||||||
|
utility::OlmUtility,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::api::r0::keys::SignedKey;
|
use crate::api::r0::keys::SignedKey;
|
||||||
use crate::identifiers::RoomId;
|
use crate::identifiers::RoomId;
|
||||||
|
|
||||||
|
@ -618,7 +623,7 @@ pub(crate) mod test {
|
||||||
use crate::identifiers::RoomId;
|
use crate::identifiers::RoomId;
|
||||||
use olm_rs::session::OlmMessage;
|
use olm_rs::session::OlmMessage;
|
||||||
use ruma_client_api::r0::keys::SignedKey;
|
use ruma_client_api::r0::keys::SignedKey;
|
||||||
use std::collections::HashMap;
|
use std::collections::BTreeMap;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
pub(crate) async fn get_account_and_session() -> (Account, Session) {
|
pub(crate) async fn get_account_and_session() -> (Account, Session) {
|
||||||
|
@ -638,7 +643,7 @@ pub(crate) mod test {
|
||||||
.to_owned();
|
.to_owned();
|
||||||
let one_time_key = SignedKey {
|
let one_time_key = SignedKey {
|
||||||
key: one_time_key,
|
key: one_time_key,
|
||||||
signatures: HashMap::new(),
|
signatures: BTreeMap::new(),
|
||||||
};
|
};
|
||||||
let sender_key = bob.identity_keys().curve25519().to_owned();
|
let sender_key = bob.identity_keys().curve25519().to_owned();
|
||||||
let session = alice
|
let session = alice
|
||||||
|
@ -716,7 +721,7 @@ pub(crate) mod test {
|
||||||
|
|
||||||
let one_time_key = SignedKey {
|
let one_time_key = SignedKey {
|
||||||
key: one_time_key,
|
key: one_time_key,
|
||||||
signatures: HashMap::new(),
|
signatures: BTreeMap::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut bob_session = bob
|
let mut bob_session = bob
|
||||||
|
|
|
@ -88,6 +88,11 @@ impl CryptoStore for MemoryStore {
|
||||||
Ok(self.devices.get(user_id, device_id))
|
Ok(self.devices.get(user_id, device_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn delete_device(&self, device: Device) -> Result<()> {
|
||||||
|
self.devices.remove(device.user_id(), device.device_id());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_user_devices(&self, user_id: &UserId) -> Result<UserDevices> {
|
async fn get_user_devices(&self, user_id: &UserId) -> Result<UserDevices> {
|
||||||
Ok(self.devices.user_devices(user_id))
|
Ok(self.devices.user_devices(user_id))
|
||||||
}
|
}
|
||||||
|
@ -181,6 +186,13 @@ mod test {
|
||||||
let loaded_device = user_devices.get(device.device_id()).unwrap();
|
let loaded_device = user_devices.get(device.device_id()).unwrap();
|
||||||
|
|
||||||
assert_eq!(device, loaded_device);
|
assert_eq!(device, loaded_device);
|
||||||
|
|
||||||
|
store.delete_device(device.clone()).await.unwrap();
|
||||||
|
assert!(store
|
||||||
|
.get_device(device.user_id(), device.device_id())
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|
|
@ -133,6 +133,13 @@ pub trait CryptoStore: Debug + Send + Sync {
|
||||||
/// * `device` - The device that should be stored.
|
/// * `device` - The device that should be stored.
|
||||||
async fn save_device(&self, device: Device) -> Result<()>;
|
async fn save_device(&self, device: Device) -> Result<()>;
|
||||||
|
|
||||||
|
/// Delete the given device from the store.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `device` - The device that should be stored.
|
||||||
|
async fn delete_device(&self, device: Device) -> Result<()>;
|
||||||
|
|
||||||
/// Get the device for the given user with the given device id.
|
/// Get the device for the given user with the given device id.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{BTreeMap, HashSet};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
@ -348,7 +348,7 @@ impl SqliteStore {
|
||||||
.fetch_all(&mut *connection)
|
.fetch_all(&mut *connection)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let mut keys = HashMap::new();
|
let mut keys = BTreeMap::new();
|
||||||
|
|
||||||
for row in key_rows {
|
for row in key_rows {
|
||||||
let algorithm = if let Ok(a) = KeyAlgorithm::try_from(&row.0 as &str) {
|
let algorithm = if let Ok(a) = KeyAlgorithm::try_from(&row.0 as &str) {
|
||||||
|
@ -613,6 +613,10 @@ impl CryptoStore for SqliteStore {
|
||||||
self.save_device_helper(device).await
|
self.save_device_helper(device).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn delete_device(&self, device: Device) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_device(&self, user_id: &UserId, device_id: &DeviceId) -> Result<Option<Device>> {
|
async fn get_device(&self, user_id: &UserId, device_id: &DeviceId) -> Result<Option<Device>> {
|
||||||
Ok(self.devices.get(user_id, device_id))
|
Ok(self.devices.get(user_id, device_id))
|
||||||
}
|
}
|
||||||
|
@ -639,7 +643,7 @@ mod test {
|
||||||
use crate::crypto::device::test::get_device;
|
use crate::crypto::device::test::get_device;
|
||||||
use crate::crypto::olm::GroupSessionKey;
|
use crate::crypto::olm::GroupSessionKey;
|
||||||
use olm_rs::outbound_group_session::OlmOutboundGroupSession;
|
use olm_rs::outbound_group_session::OlmOutboundGroupSession;
|
||||||
use std::collections::HashMap;
|
use std::collections::BTreeMap;
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
@ -705,7 +709,7 @@ mod test {
|
||||||
.to_owned();
|
.to_owned();
|
||||||
let one_time_key = SignedKey {
|
let one_time_key = SignedKey {
|
||||||
key: one_time_key,
|
key: one_time_key,
|
||||||
signatures: HashMap::new(),
|
signatures: BTreeMap::new(),
|
||||||
};
|
};
|
||||||
let sender_key = bob.identity_keys().curve25519().to_owned();
|
let sender_key = bob.identity_keys().curve25519().to_owned();
|
||||||
let session = alice
|
let session = alice
|
||||||
|
|
|
@ -50,6 +50,8 @@ mod crypto;
|
||||||
|
|
||||||
pub use async_client::{AsyncClient, AsyncClientConfig, SyncSettings};
|
pub use async_client::{AsyncClient, AsyncClientConfig, SyncSettings};
|
||||||
pub use base_client::Client;
|
pub use base_client::Client;
|
||||||
|
#[cfg(feature = "encryption")]
|
||||||
|
pub use crypto::{Device, TrustState};
|
||||||
pub use event_emitter::EventEmitter;
|
pub use event_emitter::EventEmitter;
|
||||||
pub use models::Room;
|
pub use models::Room;
|
||||||
pub use request_builder::{MessagesRequestBuilder, RoomBuilder};
|
pub use request_builder::{MessagesRequestBuilder, RoomBuilder};
|
||||||
|
|
|
@ -4,16 +4,16 @@ use serde::de::{Deserialize, Deserializer, Error as _};
|
||||||
|
|
||||||
use crate::events::collections::all::Event;
|
use crate::events::collections::all::Event;
|
||||||
use crate::events::presence::PresenceEvent;
|
use crate::events::presence::PresenceEvent;
|
||||||
use crate::events::EventResult;
|
use crate::events::EventJson;
|
||||||
|
|
||||||
pub fn deserialize_events<'de, D>(deserializer: D) -> Result<Vec<Event>, D::Error>
|
pub fn deserialize_events<'de, D>(deserializer: D) -> Result<Vec<Event>, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
let mut events = vec![];
|
let mut events = vec![];
|
||||||
let ev = Vec::<EventResult<Event>>::deserialize(deserializer)?;
|
let ev = Vec::<EventJson<Event>>::deserialize(deserializer)?;
|
||||||
for event in ev {
|
for event in ev {
|
||||||
events.push(event.into_result().map_err(D::Error::custom)?);
|
events.push(event.deserialize().map_err(D::Error::custom)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(events)
|
Ok(events)
|
||||||
|
@ -24,9 +24,9 @@ where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
let mut events = vec![];
|
let mut events = vec![];
|
||||||
let ev = Vec::<EventResult<PresenceEvent>>::deserialize(deserializer)?;
|
let ev = Vec::<EventJson<PresenceEvent>>::deserialize(deserializer)?;
|
||||||
for event in ev {
|
for event in ev {
|
||||||
events.push(event.into_result().map_err(D::Error::custom)?);
|
events.push(event.deserialize().map_err(D::Error::custom)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(events)
|
Ok(events)
|
||||||
|
@ -37,15 +37,15 @@ mod test {
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
use crate::events::room::member::MemberEvent;
|
use crate::events::room::member::MemberEvent;
|
||||||
use crate::events::EventResult;
|
use crate::events::EventJson;
|
||||||
use crate::models::RoomMember;
|
use crate::models::RoomMember;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn events_and_presence_deserialization() {
|
fn events_and_presence_deserialization() {
|
||||||
let ev_json = fs::read_to_string("./tests/data/events/member.json").unwrap();
|
let ev_json = fs::read_to_string("./tests/data/events/member.json").unwrap();
|
||||||
let ev = serde_json::from_str::<EventResult<MemberEvent>>(&ev_json)
|
let ev = serde_json::from_str::<EventJson<MemberEvent>>(&ev_json)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.into_result()
|
.deserialize()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let member = RoomMember::new(&ev);
|
let member = RoomMember::new(&ev);
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::{BTreeMap, HashMap};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use super::RoomMember;
|
use super::RoomMember;
|
||||||
|
@ -63,7 +63,7 @@ pub struct PowerLevels {
|
||||||
/// The level required to send specific event types.
|
/// The level required to send specific event types.
|
||||||
///
|
///
|
||||||
/// This is a mapping from event type to power level required.
|
/// This is a mapping from event type to power level required.
|
||||||
pub events: HashMap<EventType, Int>,
|
pub events: BTreeMap<EventType, Int>,
|
||||||
/// The default level required to send message events.
|
/// The default level required to send message events.
|
||||||
pub events_default: Int,
|
pub events_default: Int,
|
||||||
/// The level required to invite a user.
|
/// The level required to invite a user.
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use crate::api;
|
use crate::api;
|
||||||
use crate::events::room::power_levels::PowerLevelsEventContent;
|
use crate::events::room::power_levels::PowerLevelsEventContent;
|
||||||
|
use crate::events::EventJson;
|
||||||
use crate::identifiers::{RoomId, UserId};
|
use crate::identifiers::{RoomId, UserId};
|
||||||
use api::r0::filter::RoomEventFilter;
|
use api::r0::filter::RoomEventFilter;
|
||||||
use api::r0::membership::Invite3pid;
|
use api::r0::membership::Invite3pid;
|
||||||
|
@ -163,7 +164,7 @@ impl Into<create_room::Request> for RoomBuilder {
|
||||||
invite_3pid: self.invite_3pid,
|
invite_3pid: self.invite_3pid,
|
||||||
is_direct: self.is_direct,
|
is_direct: self.is_direct,
|
||||||
name: self.name,
|
name: self.name,
|
||||||
power_level_content_override: self.power_level_content_override,
|
power_level_content_override: self.power_level_content_override.map(EventJson::from),
|
||||||
preset: self.preset,
|
preset: self.preset,
|
||||||
room_alias_name: self.room_alias_name,
|
room_alias_name: self.room_alias_name,
|
||||||
room_version: self.room_version,
|
room_version: self.room_version,
|
||||||
|
@ -177,6 +178,7 @@ impl Into<create_room::Request> for RoomBuilder {
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
|
/// # use std::convert::TryFrom;
|
||||||
/// # use matrix_sdk::{AsyncClient, MessagesRequestBuilder};
|
/// # use matrix_sdk::{AsyncClient, MessagesRequestBuilder};
|
||||||
/// # use matrix_sdk::api::r0::message::get_message_events::{self, Direction};
|
/// # use matrix_sdk::api::r0::message::get_message_events::{self, Direction};
|
||||||
/// # use matrix_sdk::identifiers::RoomId;
|
/// # use matrix_sdk::identifiers::RoomId;
|
||||||
|
@ -184,8 +186,8 @@ impl Into<create_room::Request> for RoomBuilder {
|
||||||
/// # let homeserver = Url::parse("http://example.com").unwrap();
|
/// # let homeserver = Url::parse("http://example.com").unwrap();
|
||||||
/// # let mut rt = tokio::runtime::Runtime::new().unwrap();
|
/// # let mut rt = tokio::runtime::Runtime::new().unwrap();
|
||||||
/// # rt.block_on(async {
|
/// # rt.block_on(async {
|
||||||
/// # let room_id = RoomId::new(homeserver.as_str()).unwrap();
|
/// # let room_id = RoomId::try_from("!test:localhost").unwrap();
|
||||||
/// # let last_sync_token = "".to_string();;
|
/// # let last_sync_token = "".to_string();
|
||||||
/// let mut cli = AsyncClient::new(homeserver, None).unwrap();
|
/// let mut cli = AsyncClient::new(homeserver, None).unwrap();
|
||||||
///
|
///
|
||||||
/// let mut builder = MessagesRequestBuilder::new();
|
/// let mut builder = MessagesRequestBuilder::new();
|
||||||
|
@ -288,7 +290,7 @@ impl Into<get_message_events::Request> for MessagesRequestBuilder {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use std::collections::HashMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::events::room::power_levels::NotificationPowerLevels;
|
use crate::events::room::power_levels::NotificationPowerLevels;
|
||||||
|
@ -325,7 +327,7 @@ mod test {
|
||||||
.is_direct(true)
|
.is_direct(true)
|
||||||
.power_level_override(PowerLevelsEventContent {
|
.power_level_override(PowerLevelsEventContent {
|
||||||
ban: Int::max_value(),
|
ban: Int::max_value(),
|
||||||
events: HashMap::default(),
|
events: BTreeMap::default(),
|
||||||
events_default: Int::min_value(),
|
events_default: Int::min_value(),
|
||||||
invite: Int::min_value(),
|
invite: Int::min_value(),
|
||||||
kick: Int::min_value(),
|
kick: Int::min_value(),
|
||||||
|
@ -335,7 +337,7 @@ mod test {
|
||||||
notifications: NotificationPowerLevels {
|
notifications: NotificationPowerLevels {
|
||||||
room: Int::min_value(),
|
room: Int::min_value(),
|
||||||
},
|
},
|
||||||
users: HashMap::default(),
|
users: BTreeMap::default(),
|
||||||
})
|
})
|
||||||
.preset(RoomPreset::PrivateChat)
|
.preset(RoomPreset::PrivateChat)
|
||||||
.room_alias_name("room_alias")
|
.room_alias_name("room_alias")
|
||||||
|
|
|
@ -10,7 +10,7 @@ use crate::events::{
|
||||||
only::Event,
|
only::Event,
|
||||||
},
|
},
|
||||||
presence::PresenceEvent,
|
presence::PresenceEvent,
|
||||||
EventResult, TryFromRaw,
|
EventJson, TryFromRaw,
|
||||||
};
|
};
|
||||||
use crate::identifiers::{RoomId, UserId};
|
use crate::identifiers::{RoomId, UserId};
|
||||||
use crate::AsyncClient;
|
use crate::AsyncClient;
|
||||||
|
@ -97,9 +97,9 @@ impl EventBuilder {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let val = fs::read_to_string(path.as_ref())
|
let val = fs::read_to_string(path.as_ref())
|
||||||
.expect(&format!("file not found {:?}", path.as_ref()));
|
.expect(&format!("file not found {:?}", path.as_ref()));
|
||||||
let event = serde_json::from_str::<EventResult<Ev>>(&val)
|
let event = serde_json::from_str::<EventJson<Ev>>(&val)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.into_result()
|
.deserialize()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self.ephemeral.push(variant(event));
|
self.ephemeral.push(variant(event));
|
||||||
self
|
self
|
||||||
|
@ -113,9 +113,9 @@ impl EventBuilder {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let val = fs::read_to_string(path.as_ref())
|
let val = fs::read_to_string(path.as_ref())
|
||||||
.expect(&format!("file not found {:?}", path.as_ref()));
|
.expect(&format!("file not found {:?}", path.as_ref()));
|
||||||
let event = serde_json::from_str::<EventResult<Ev>>(&val)
|
let event = serde_json::from_str::<EventJson<Ev>>(&val)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.into_result()
|
.deserialize()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self.account_data.push(variant(event));
|
self.account_data.push(variant(event));
|
||||||
self
|
self
|
||||||
|
@ -129,9 +129,9 @@ impl EventBuilder {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let val = fs::read_to_string(path.as_ref())
|
let val = fs::read_to_string(path.as_ref())
|
||||||
.expect(&format!("file not found {:?}", path.as_ref()));
|
.expect(&format!("file not found {:?}", path.as_ref()));
|
||||||
let event = serde_json::from_str::<EventResult<Ev>>(&val)
|
let event = serde_json::from_str::<EventJson<Ev>>(&val)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.into_result()
|
.deserialize()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self.room_events.push(variant(event));
|
self.room_events.push(variant(event));
|
||||||
self
|
self
|
||||||
|
@ -145,9 +145,9 @@ impl EventBuilder {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let val = fs::read_to_string(path.as_ref())
|
let val = fs::read_to_string(path.as_ref())
|
||||||
.expect(&format!("file not found {:?}", path.as_ref()));
|
.expect(&format!("file not found {:?}", path.as_ref()));
|
||||||
let event = serde_json::from_str::<EventResult<Ev>>(&val)
|
let event = serde_json::from_str::<EventJson<Ev>>(&val)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.into_result()
|
.deserialize()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self.state_events.push(variant(event));
|
self.state_events.push(variant(event));
|
||||||
self
|
self
|
||||||
|
@ -157,9 +157,9 @@ impl EventBuilder {
|
||||||
pub fn add_presence_event_from_file<P: AsRef<Path>>(mut self, path: P) -> Self {
|
pub fn add_presence_event_from_file<P: AsRef<Path>>(mut self, path: P) -> Self {
|
||||||
let val = fs::read_to_string(path.as_ref())
|
let val = fs::read_to_string(path.as_ref())
|
||||||
.expect(&format!("file not found {:?}", path.as_ref()));
|
.expect(&format!("file not found {:?}", path.as_ref()));
|
||||||
let event = serde_json::from_str::<EventResult<PresenceEvent>>(&val)
|
let event = serde_json::from_str::<EventJson<PresenceEvent>>(&val)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.into_result()
|
.deserialize()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self.presence_events.push(event);
|
self.presence_events.push(event);
|
||||||
self
|
self
|
||||||
|
@ -342,7 +342,7 @@ impl ClientTestRunner {
|
||||||
for event in &self.room_events {
|
for event in &self.room_events {
|
||||||
cli.receive_joined_timeline_event(
|
cli.receive_joined_timeline_event(
|
||||||
room_id,
|
room_id,
|
||||||
&mut EventResult::Ok(event.clone()),
|
&mut EventJson::from(event.clone()),
|
||||||
&mut false,
|
&mut false,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
Loading…
Reference in New Issue