diff --git a/crates/matrix/Cargo.toml b/crates/matrix/Cargo.toml index 68b8b70..8d24aca 100644 --- a/crates/matrix/Cargo.toml +++ b/crates/matrix/Cargo.toml @@ -6,18 +6,22 @@ publish = false [dependencies] ruma-events = { version = "0.27.11", features = ["html", "markdown"] } -ruma-common = { version = "0.12.0", default_features = false, features = ["api", "rand"] } +ruma-common = { version = "0.12.0", default_features = false, features = [ + "api", + "rand", +] } ruma-macros = "0.12.0" # Workspace Dependencies -anyhow = { workspace = true } -chrono = { workspace = true, features = ["serde"] } mime = { workspace = true } reqwest = { workspace = true, features = ["json"] } serde = { workspace = true } tokio = { workspace = true } tracing = { workspace = true } +sha1 = { workspace = true } url = { workspace = true, features = ["serde"] } +hex = { workspace = true } +hmac = { workspace = true } [features] client = [] diff --git a/crates/matrix/src/admin/mod.rs b/crates/matrix/src/admin/mod.rs index 2a77d65..dcb275b 100644 --- a/crates/matrix/src/admin/mod.rs +++ b/crates/matrix/src/admin/mod.rs @@ -1,3 +1,7 @@ -mod user; +//! This module is the root of the admin API. +//! +//! reference: https://matrix-org.github.io/synapse/latest/usage/administration/admin_api/index.html + mod room; -// mod room; +mod session; +mod user; diff --git a/crates/matrix/src/admin/room.rs b/crates/matrix/src/admin/room.rs index 17117b9..39b3a22 100644 --- a/crates/matrix/src/admin/room.rs +++ b/crates/matrix/src/admin/room.rs @@ -1,6 +1,6 @@ -//! reference: https://matrix-org.github.io/synapse/latest/admin_api/rooms.html -//! //! This module contains handlers for managing rooms. +//! +//! reference: https://matrix-org.github.io/synapse/latest/admin_api/rooms.html use ruma_common::{ room::RoomType, EventEncryptionAlgorithm, OwnedMxcUri, OwnedRoomAliasId, OwnedRoomId, diff --git a/crates/matrix/src/admin/session.rs b/crates/matrix/src/admin/session.rs new file mode 100644 index 0000000..88cabf2 --- /dev/null +++ b/crates/matrix/src/admin/session.rs @@ -0,0 +1,45 @@ +//! This module contains handlers for user registration. +//! +//! reference: https://matrix-org.github.io/synapse/latest/admin_api/register_api.html + +use hmac::Mac; + +mod get_nonce; +mod register; + +#[derive(Clone, Debug)] +pub struct Hmac { + inner: Vec, +} + +impl Hmac { + fn new( + shared_secret: &str, + nonce: &str, + username: &str, + password: &str, + admin: bool, + ) -> Result { + let mut mac = hmac::Hmac::::new_from_slice(shared_secret.as_bytes())?; + let admin = match admin { + true => "admin", + false => "notadmin", + }; + + mac.update( + &[nonce, username, password, admin] + .map(str::as_bytes) + .join(&0x00), + ); + + let result = mac.finalize().into_bytes(); + + Ok(Self { + inner: result.to_vec(), + }) + } + + fn get(&self) -> String { + hex::encode(&self.inner) + } +} diff --git a/crates/matrix/src/admin/session/get_nonce.rs b/crates/matrix/src/admin/session/get_nonce.rs new file mode 100644 index 0000000..d5f5c00 --- /dev/null +++ b/crates/matrix/src/admin/session/get_nonce.rs @@ -0,0 +1,22 @@ +use ruma_common::{ + api::{request, response, Metadata}, + metadata, +}; + +#[allow(dead_code)] +const METADATA: Metadata = metadata! { + method: GET, + rate_limited: false, + authentication: AccessToken, + history: { + unstable => "/_synapse/admin/v1/register", + } +}; + +#[request(error = crate::Error)] +pub struct Request {} + +#[response(error = crate::Error)] +pub struct Response { + nonce: String, +} diff --git a/crates/matrix/src/admin/session/register.rs b/crates/matrix/src/admin/session/register.rs new file mode 100644 index 0000000..c71d8f0 --- /dev/null +++ b/crates/matrix/src/admin/session/register.rs @@ -0,0 +1,43 @@ +use ruma_common::{ + api::{request, response, Metadata}, + metadata, OwnedDeviceId, OwnedServerName, OwnedUserId, +}; + +use super::Hmac; + +#[allow(dead_code)] +const METADATA: Metadata = metadata! { + method: GET, + rate_limited: false, + authentication: AccessToken, + history: { + unstable => "/_synapse/admin/v1/register", + } +}; + +#[request(error = crate::Error)] +pub struct Request { + pub nonce: String, + + pub username: String, + + pub password: String, + + #[serde(skip_deserializing_if = "String::is_empty")] + pub displayname: String, + + pub admin: bool, + + pub hmac: Hmac, +} + +#[response(error = crate::Error)] +pub struct Response { + pub access_token: String, + + pub user_id: OwnedUserId, + + pub home_server: OwnedServerName, + + pub device_id: OwnedDeviceId, +} diff --git a/crates/matrix/src/admin/user.rs b/crates/matrix/src/admin/user.rs index 2e39665..3417bb5 100644 --- a/crates/matrix/src/admin/user.rs +++ b/crates/matrix/src/admin/user.rs @@ -1,6 +1,6 @@ -//! reference: https://matrix-org.github.io/synapse/latest/admin_api/user_admin_api.html +//! This module contains handlers for managing users. //! -//! This module contains handlers for managing user accounts. +//! reference: https://matrix-org.github.io/synapse/latest/admin_api/user_admin_api.html use ruma_common::{thirdparty::ThirdPartyIdentifier, OwnedMxcUri, OwnedUserId}; use serde::{Deserialize, Serialize}; diff --git a/crates/matrix/src/client/error.rs b/crates/matrix/src/client/error.rs deleted file mode 100644 index 872754b..0000000 --- a/crates/matrix/src/client/error.rs +++ /dev/null @@ -1,9 +0,0 @@ -use serde::Deserialize; - -#[derive(Clone, Debug, Deserialize)] -pub struct MatrixError { - #[serde(rename = "errcode")] - pub error_code: String, - #[serde(rename = "error")] - pub error: String, -} diff --git a/crates/matrix/src/client/events.rs b/crates/matrix/src/client/events.rs deleted file mode 100644 index 2953b2b..0000000 --- a/crates/matrix/src/client/events.rs +++ /dev/null @@ -1,310 +0,0 @@ -use anyhow::Result; -use ruma_common::{serde::Raw, EventId, OwnedEventId, OwnedTransactionId, RoomId}; - -use ruma_events::{ - relation::RelationType, AnyMessageLikeEvent, AnyStateEvent, AnyStateEventContent, - AnyTimelineEvent, MessageLikeEventContent, MessageLikeEventType, StateEventContent, - StateEventType, -}; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use tracing::instrument; - -use crate::{admin::resources::room::Direction, error::MatrixError, Client}; - -pub struct EventsService; - -#[derive(Debug, Default, Clone, Serialize)] -pub struct GetMessagesQuery { - #[serde(skip_serializing_if = "Option::is_none")] - pub from: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub to: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub limit: Option, - - pub dir: Direction, - - #[serde(skip_serializing_if = "String::is_empty")] - pub filter: String, -} - -#[derive(Debug, Default, Clone, Serialize)] -pub struct GetRelationsQuery { - #[serde(skip_serializing_if = "Option::is_none")] - pub from: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub to: Option, - - #[serde(skip_serializing_if = "Option::is_none")] - pub limit: Option, - - pub dir: Direction, -} - -#[derive(Debug, Deserialize)] -pub struct GetMessagesResponse { - pub chunk: Vec>, - pub start: String, - pub end: String, - pub state: Option>>, -} - -#[derive(Debug, Deserialize)] -#[serde(transparent)] -pub struct GetStateResponse(pub Vec>); - -#[derive(Debug, Deserialize)] -pub struct GetRelationsResponse { - pub chunk: Vec>, - pub prev_batch: Option, - pub next_batch: Option, -} - -#[derive(Debug, Default, Serialize)] -pub struct SendRedactionBody { - #[serde(skip_serializing_if = "String::is_empty")] - pub reason: String, -} - -#[derive(Debug, Deserialize)] -pub struct SendMessageResponse { - pub event_id: OwnedEventId, -} - -#[derive(Debug, Deserialize)] -pub struct SendStateResponse { - pub event_id: OwnedEventId, -} - -#[derive(Debug, Deserialize)] -pub struct SendRedactionResponse { - pub event_id: OwnedEventId, -} - -impl EventsService { - #[instrument(skip(client, access_token))] - pub async fn get_event( - client: &Client, - access_token: impl Into, - room_id: &RoomId, - event_id: &EventId, - ) -> Result> { - let mut tmp = (*client).clone(); - tmp.set_token(access_token)?; - - let resp = tmp - .get(format!( - "/_matrix/client/v3/rooms/{room_id}/event/{event_id}", - room_id = room_id, - event_id = event_id, - )) - .await?; - - Ok(resp.json().await?) - } - - #[instrument(skip(client, access_token))] - pub async fn get_messages( - client: &Client, - access_token: impl Into, - room_id: &RoomId, - query: GetMessagesQuery, - ) -> Result { - let mut tmp = (*client).clone(); - tmp.set_token(access_token)?; - - let resp = tmp - .get_query( - format!( - "/_matrix/client/v3/rooms/{room_id}/messages", - room_id = room_id, - ), - &query, - ) - .await?; - - Ok(resp.json().await?) - } - - #[instrument(skip(client, access_token))] - pub async fn get_state( - client: &Client, - access_token: impl Into, - room_id: &RoomId, - ) -> Result { - let mut tmp = (*client).clone(); - tmp.set_token(access_token)?; - - let resp = tmp - .get(format!( - "/_matrix/client/v3/rooms/{room_id}/state", - room_id = room_id, - )) - .await?; - - Ok(resp.json().await?) - } - - #[instrument(skip(client, access_token))] - pub async fn get_state_content( - client: &Client, - access_token: impl Into, - room_id: &RoomId, - event_type: StateEventType, - state_key: Option, - ) -> Result> { - let mut tmp = (*client).clone(); - tmp.set_token(access_token)?; - - let mut path = format!( - "/_matrix/client/v3/rooms/{room_id}/state/{event_type}", - room_id = room_id, - event_type = event_type, - ); - - if let Some(state_key) = state_key { - path.push_str(&format!("/{state_key}", state_key = state_key)) - } - - let resp = tmp.get(path).await?; - - Ok(resp.json().await?) - } - - #[instrument(skip(client, access_token))] - pub async fn get_relations( - client: &Client, - access_token: impl Into, - room_id: &RoomId, - event_id: &EventId, - rel_type: Option>, - event_type: Option, - query: GetRelationsQuery, - ) -> Result { - let mut tmp = (*client).clone(); - tmp.set_token(access_token)?; - - let mut path = format!( - "/_matrix/client/v3/rooms/{room_id}/relations/{event_id}", - room_id = room_id, - event_id = event_id, - ); - - if let Some(rel_type) = rel_type { - path.push_str(&format!( - "/{rel_type}", - rel_type = rel_type - .as_ref() - .map_or("m.in_reply_to".into(), ToString::to_string) - )); - - if let Some(event_type) = event_type { - path.push_str(&format!("/{event_type}", event_type = event_type)) - } - } - - let resp = tmp.get_query(path, &query).await?; - - Ok(resp.json().await?) - } - - #[instrument(skip(client, access_token, body))] - pub async fn send_message( - client: &Client, - access_token: impl Into, - room_id: &RoomId, - txn_id: OwnedTransactionId, - body: T, - ) -> Result { - let mut tmp = (*client).clone(); - tmp.set_token(access_token)?; - - let resp = tmp - .put_json( - format!( - "/_matrix/client/v3/rooms/{room_id}/send/{event_type}/{txn_id}", - room_id = room_id, - event_type = body.event_type(), - txn_id = txn_id, - ), - &body, - ) - .await?; - - if resp.status().is_success() { - return Ok(resp.json().await?); - } - - let error = resp.json::().await?; - - Err(anyhow::anyhow!(error.error)) - } - - #[instrument(skip(client, access_token, body))] - pub async fn send_state( - client: &Client, - access_token: impl Into, - room_id: &RoomId, - state_key: Option, - body: T, - ) -> Result { - let mut tmp = (*client).clone(); - tmp.set_token(access_token)?; - - let mut path = format!( - "/_matrix/client/v3/rooms/{room_id}/state/{event_type}", - room_id = room_id, - event_type = body.event_type(), - ); - - if let Some(state_key) = state_key { - path.push_str(&format!("/{state_key}", state_key = state_key)) - } - - let resp = tmp.put_json(path, &body).await?; - - if resp.status().is_success() { - return Ok(resp.json().await?); - } - - let error = resp.json::().await?; - - Err(anyhow::anyhow!(error.error)) - } - - #[instrument(skip(client, access_token, body))] - pub async fn send_redaction( - client: &Client, - access_token: impl Into, - room_id: &RoomId, - event_id: &EventId, - txn_id: OwnedTransactionId, - body: SendRedactionBody, - ) -> Result { - let mut tmp = (*client).clone(); - tmp.set_token(access_token)?; - - let resp = tmp - .put_json( - format!( - "/_matrix/client/v3/rooms/{room_id}/redact/{event_id}/{txn_id}", - room_id = room_id, - event_id = event_id, - txn_id = txn_id, - ), - &body, - ) - .await?; - - if resp.status().is_success() { - return Ok(resp.json().await?); - } - - let error = resp.json::().await?; - - Err(anyhow::anyhow!(error.error)) - } -} diff --git a/crates/matrix/src/client/login.rs b/crates/matrix/src/client/login.rs deleted file mode 100644 index 6a65fba..0000000 --- a/crates/matrix/src/client/login.rs +++ /dev/null @@ -1,93 +0,0 @@ -use anyhow::Result; -use serde::{Deserialize, Serialize}; -use tracing::instrument; - -use crate::{error::MatrixError, http::Client}; - -#[derive(Debug, Deserialize)] -pub struct LoginCredentials { - pub access_token: String, -} - -#[derive(Debug, Serialize)] -pub struct LoginCredentialsPayload { - pub r#type: &'static str, - pub user: String, - pub password: String, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct LoginFlow { - pub get_login_token: Option, - #[serde(rename = "type")] - pub kind: String, -} - -#[derive(Debug, Serialize, Deserialize)] -#[serde(transparent)] -pub struct LoginFlows { - pub flows: Vec, -} - -pub struct Login; - -impl Login { - /// Retrieves an access token by logging in with Username and Password - /// - /// This is equivalent to executing: - /// - /// ```ignore - /// curl -sS -d '{"type":"m.login.password", "user":"X", "password":"Y"}' http://server:port/_matrix/client/v3/login - /// ``` - #[instrument(skip(client, username, password))] - pub async fn login_credentials( - client: &Client, - username: impl AsRef, - password: impl AsRef, - ) -> Result { - let resp = client - .post_json( - "/_matrix/client/v3/login", - &LoginCredentialsPayload { - r#type: "m.login.password", - user: username.as_ref().to_string(), - password: password.as_ref().to_string(), - }, - ) - .await?; - - if resp.status().is_success() { - return Ok(resp.json().await?); - } - - let error = resp.json::().await?; - - Err(anyhow::anyhow!(error.error)) - } - - #[instrument(skip(client))] - pub async fn get_login_flows(client: &Client) -> Result { - let resp = client.get("/_matrix/client/v3/login").await?; - - if resp.status().is_success() { - return Ok(resp.json().await?); - } - - let error = resp.json::().await?; - - Err(anyhow::anyhow!(error.error)) - } - - #[instrument(skip(client))] - pub async fn redirect_sso(client: &Client) -> Result { - let resp = client.get("/_matrix/client/v3/login").await?; - - if resp.status().is_success() { - return Ok(resp.json().await?); - } - - let error = resp.json::().await?; - - Err(anyhow::anyhow!(error.error)) - } -} diff --git a/crates/matrix/src/client/mod.rs b/crates/matrix/src/client/mod.rs index 43420e9..196920e 100644 --- a/crates/matrix/src/client/mod.rs +++ b/crates/matrix/src/client/mod.rs @@ -1,6 +1,6 @@ -pub mod error; -pub mod events; -pub mod login; -pub mod mxc; +//! This module is the root of the client-server API. +//! +//! reference: https://spec.matrix.org/unstable/client-server-api + pub mod room; pub mod session; diff --git a/crates/matrix/src/client/mxc.rs b/crates/matrix/src/client/mxc.rs deleted file mode 100644 index 7dc669a..0000000 --- a/crates/matrix/src/client/mxc.rs +++ /dev/null @@ -1,184 +0,0 @@ -use std::str::FromStr; - -use anyhow::Result; -use mime::Mime; -use ruma_common::{MxcUri, OwnedMxcUri}; -use serde::{de, Deserialize, Deserializer, Serialize}; -use tracing::instrument; - -use chrono::{serde::ts_microseconds_option, DateTime, Utc}; - -use crate::error::MatrixError; - -fn parse_mime_opt<'de, D>(d: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - Option::<&str>::deserialize(d)? - .map(::from_str) - .transpose() - .map_err(de::Error::custom) -} - -#[derive(Debug, Serialize)] -pub struct GetPreviewUrlQuery { - pub url: url::Url, - pub ts: DateTime, -} - -#[derive(Debug, Deserialize)] -pub struct CreateMxcUriResponse { - pub content_uri: String, - - #[serde(with = "ts_microseconds_option")] - pub unused_expires_at: Option>, -} - -#[derive(Debug, Deserialize)] -pub struct GetPreviewUrlResponse { - #[serde(rename = "matrix:image_size")] - pub image_size: Option, - - #[serde(rename = "og:description")] - pub description: Option, - - #[serde(rename = "og:image")] - pub image: Option, - - #[serde(rename = "og:image:height")] - pub height: Option, - - #[serde(rename = "og:image:width")] - pub width: Option, - - #[serde(rename = "og:image:type", deserialize_with = "parse_mime_opt")] - pub kind: Option, - - #[serde(rename = "og:title")] - pub title: Option, -} - -#[derive(Debug, Deserialize)] -pub struct GetConfigResponse { - #[serde(rename = "m.upload.size")] - pub upload_size: Option, -} - -#[derive(Debug, Serialize)] -pub enum ResizeMethod { - Crop, - Scale, -} - -pub struct MxcService; - -#[derive(Debug, Deserialize)] -pub struct MxcError { - #[serde(flatten)] - pub inner: MatrixError, - - pub retry_after_ms: u64, -} - -impl MxcService { - /// Creates a new `MxcUri`, independently of the content being uploaded - /// - /// Refer: https://spec.matrix.org/v1.9/client-server-api/#post_matrixmediav1create - #[instrument(skip(client, access_token))] - pub async fn create( - client: &crate::http::Client, - access_token: impl Into, - ) -> Result { - let mut tmp = (*client).clone(); - tmp.set_token(access_token)?; - - let resp = tmp.post("/_matrix/media/v1/create").await?; - - if resp.status().is_success() { - return Ok(resp.json().await?); - } - - let error = resp.json::().await?; - - Err(anyhow::anyhow!(error.inner.error)) - } - - /// Retrieve the configuration of the content repository - /// - /// Refer: https://spec.matrix.org/v1.9/client-server-api/#get_matrixmediav3config - #[instrument(skip(client, access_token))] - pub async fn get_config( - client: &crate::http::Client, - access_token: impl Into, - ) -> Result { - let mut tmp = (*client).clone(); - tmp.set_token(access_token)?; - - let resp = tmp.get("/_matrix/media/v3/config").await?; - - if resp.status().is_success() { - return Ok(resp.json().await?); - } - - let error = resp.json::().await?; - - Err(anyhow::anyhow!(error.inner.error)) - } - - /// Retrieve a URL to download content from the content repository, - /// optionally replacing the name of the file. - /// - /// Refer: https://spec.matrix.org/v1.9/client-server-api/#get_matrixmediav3downloadservernamemediaid - #[instrument(skip(client, access_token))] - pub async fn get_download_url( - client: &crate::http::Client, - access_token: impl Into, - mxc_uri: &MxcUri, - mut base_url: url::Url, - file_name: Option, - ) -> Result { - let mut tmp = (*client).clone(); - tmp.set_token(access_token)?; - - let (server_name, media_id) = mxc_uri.parts().unwrap(); - - let mut path = format!( - "/_matrix/media/v3/download/{server_name}/{media_id}", - server_name = server_name, - media_id = media_id, - ); - - if let Some(file_name) = file_name { - path.push_str(&format!("/{file_name}", file_name = file_name)) - } - - base_url.set_path(&path); - - Ok(base_url) - } - - /// - /// - /// Refer: https://spec.matrix.org/v1.9/client-server-api/#get_matrixmediav3preview_url - #[instrument(skip(client, access_token))] - pub async fn get_preview( - client: &crate::http::Client, - access_token: impl Into, - query: GetPreviewUrlQuery, - ) -> Result { - let mut tmp = (*client).clone(); - tmp.set_token(access_token)?; - - let resp = tmp - .get_query("/_matrix/media/v3/preview_url".to_string(), &query) - .await?; - - if resp.status().is_success() { - return Ok(resp.json().await?); - } - - let error = resp.json::().await?; - - Err(anyhow::anyhow!(error.inner.error)) - } -} diff --git a/crates/matrix/src/client/room.rs b/crates/matrix/src/client/room.rs index ce0d312..27a797f 100644 --- a/crates/matrix/src/client/room.rs +++ b/crates/matrix/src/client/room.rs @@ -1,6 +1,6 @@ -//! reference: https://spec.matrix.org/unstable/client-server-api/#rooms -//! //! This module contains handlers to interact with rooms. +//! +//! reference: https://spec.matrix.org/unstable/client-server-api/#rooms mod ban_from_room; mod create_room; diff --git a/crates/matrix/src/client/session.rs b/crates/matrix/src/client/session.rs index 18b8249..18328e9 100644 --- a/crates/matrix/src/client/session.rs +++ b/crates/matrix/src/client/session.rs @@ -1,3 +1,9 @@ -mod login; +//! This module contains handlers for user sessions. +//! +//! reference: https://spec.matrix.org/unstable/client-server-api/#client-authentication + mod get_flows; +mod login; +mod register; +mod username_available; mod whoami; diff --git a/crates/matrix/src/client/session/get_flows.rs b/crates/matrix/src/client/session/get_flows.rs index d3f6f7a..14e9a29 100644 --- a/crates/matrix/src/client/session/get_flows.rs +++ b/crates/matrix/src/client/session/get_flows.rs @@ -19,7 +19,7 @@ pub struct Request {} #[response(error = crate::Error)] pub struct Response { - body: Vec, + pub body: Vec, } #[derive(Clone, Debug, Deserialize)] diff --git a/crates/matrix/src/client/session/register.rs b/crates/matrix/src/client/session/register.rs index e662413..505e8e9 100644 --- a/crates/matrix/src/client/session/register.rs +++ b/crates/matrix/src/client/session/register.rs @@ -1,8 +1,7 @@ use ruma_common::{ api::{request, response, Metadata}, - metadata, OwnedMxcUri, thirdparty::Medium, OwnedDeviceId, OwnedUserId, + metadata, OwnedDeviceId, OwnedUserId, }; -use serde::{Serialize, Deserialize}; #[allow(dead_code)] const METADATA: Metadata = metadata! { @@ -10,17 +9,15 @@ const METADATA: Metadata = metadata! { rate_limited: true, authentication: None, history: { - unstable => "", + unstable => "/_matrix/client/v3/register", } }; #[request(error = crate::Error)] pub struct Request { - #[serde(flatten, rename = "type")] - pub kind: LoginType, + pub username: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub identifier: Option, + pub password: String, #[serde( rename = "initial_device_display_name", @@ -45,64 +42,4 @@ pub struct Response { pub refresh_token: Option, pub user_id: OwnedUserId, - - #[serde(skip_serializing_if = "Option::is_none")] - pub well_known: Option, -} - -#[derive(Clone, Debug, Serialize)] -pub struct IdentityProvider { - pub id: String, - - #[serde(skip_serializing_if = "String::is_empty")] - pub name: String, - - #[serde(skip_serializing_if = "Option::is_none")] - pub icon: Option, -} - -#[derive(Clone, Debug, Serialize)] -#[serde(tag = "type")] -pub enum LoginType { - #[serde(rename = "m.login.password")] - Password { password: String }, - - #[serde(rename = "m.login.token")] - Token { token: String }, - - #[serde(rename = "m.login.sso")] - Sso { - #[serde(skip_serializing_if = "<[_]>::is_empty")] - identity_providers: Vec, - }, - - #[serde(rename = "m.login.application_service")] - ApplicationService, -} - -#[derive(Clone, Debug, Serialize)] -#[serde(tag = "type")] -pub enum Identifier { - #[serde(rename = "m.id.user")] - User { user: String }, - - #[serde(rename = "m.id.thirdparty")] - ThirdParty { medium: Medium, address: String }, - - #[serde(rename = "m.id.phone")] - Phone { country: String, phone: String }, -} - -#[derive(Clone, Debug, Deserialize)] -pub struct BaseUrl { - pub base_url: url::Url, -} - -#[derive(Clone, Debug, Deserialize)] -pub struct WellKnown { - #[serde(rename = "m.homeserver")] - pub homeserver: BaseUrl, - - #[serde(rename = "m.identity_server")] - pub identity_server: BaseUrl, } diff --git a/crates/matrix/src/client/session/username_available.rs b/crates/matrix/src/client/session/username_available.rs new file mode 100644 index 0000000..e380f6f --- /dev/null +++ b/crates/matrix/src/client/session/username_available.rs @@ -0,0 +1,24 @@ +use ruma_common::{ + api::{request, response, Metadata}, + metadata, +}; + +#[allow(dead_code)] +const METADATA: Metadata = metadata! { + method: POST, + rate_limited: true, + authentication: None, + history: { + unstable => "/_matrix/client/v3/register/available", + } +}; + +#[request(error = crate::Error)] +pub struct Request { + pub username: String, +} + +#[response(error = crate::Error)] +pub struct Response { + pub available: bool, +} diff --git a/crates/matrix/src/lib.rs b/crates/matrix/src/lib.rs index 2b2848e..ace15fb 100644 --- a/crates/matrix/src/lib.rs +++ b/crates/matrix/src/lib.rs @@ -1,3 +1,11 @@ +//! This library deals with forwarding Matrix requests to the server. +//! Comments have been used sparingly as the specification contains all the technical details. + +//! We rely on `ruma` to abstract away the boilerplate introduced by HTTP requests, +//! without sacrificing flexibility by defining our own request and response types. +//! +//! reference: https://docs.ruma.io/ruma_common/api/index.html + pub mod admin; pub mod client;