diff --git a/Cargo.toml b/Cargo.toml index 5f7a3fde..40a67355 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -258,6 +258,7 @@ features = [ "unstable-msc2870", "unstable-msc3026", "unstable-msc3061", + "unstable-msc4121", "unstable-extensible-events", ] diff --git a/conduwuit-example.toml b/conduwuit-example.toml index cfd0b0e9..045d1c3b 100644 --- a/conduwuit-example.toml +++ b/conduwuit-example.toml @@ -51,7 +51,6 @@ #sentry_traces_sample_rate = 0.15 - ### Database configuration # This is the only directory where conduwuit will save its data, including media @@ -63,7 +62,6 @@ database_path = "/var/lib/matrix-conduit/" database_backend = "rocksdb" - ### Network # The port(s) conduwuit will be running on. You need to set up a reverse proxy such as @@ -75,7 +73,7 @@ port = 6167 # default address (IPv4 or IPv6) conduwuit will listen on. Generally you want this to be # localhost (127.0.0.1 / ::1). If you are using Docker or a container NAT networking setup, you -# likely need this to be 0.0.0.0. +# likely need this to be 0.0.0.0. address = "127.0.0.1" # How many requests conduwuit sends to other servers at the same time concurrently. Default is 500 @@ -153,7 +151,6 @@ ip_range_denylist = [ ] - ### Moderation / Privacy / Security # Set to true to allow user type "guest" registrations. Element attempts to register guest users automatically. @@ -250,6 +247,13 @@ url_preview_max_spider_size = 384_000 # Useful if the domain contains allowlist is still too broad for you but you still want to allow all the subdomains under a root domain. url_preview_check_root_domain = false +# A single contact and/or support page for /.well-known/matrix/support +# All options here are strings. Currently only supports 1 single contact. +# No default. +#well_known_support_page = "" +#well_known_support_role = "" +#well_known_support_email = "" +#well_known_support_mxid = "" ### Misc @@ -344,7 +348,6 @@ url_preview_check_root_domain = false #cleanup_second_interval = 1800 - ### RocksDB options # Set this to true to use RocksDB config options that are tailored to HDDs (slower device storage) @@ -439,7 +442,6 @@ url_preview_check_root_domain = false #rocksdb_recovery_mode = 1 - ### Domain Name Resolution and Caching # Maximum entries stored in DNS memory-cache. The size of an entry may vary so please take care if @@ -470,7 +472,6 @@ url_preview_check_root_domain = false #query_all_nameservers = false - ### Request Timeouts, Connection Timeouts, and Connection Pooling ## Request Timeouts are HTTP response timeouts @@ -548,7 +549,6 @@ url_preview_check_root_domain = false #pusher_idle_timeout = 15 - ### Presence / Typing Indicators / Read Receipts # Config option to control local (your server only) presence updates/requests. Defaults to true. @@ -597,7 +597,6 @@ url_preview_check_root_domain = false #typing_client_timeout_max_s = 45 - # Other options not in [global]: # # diff --git a/src/api/client_server/unversioned.rs b/src/api/client_server/unversioned.rs index 1a530b6e..be2bd75c 100644 --- a/src/api/client_server/unversioned.rs +++ b/src/api/client_server/unversioned.rs @@ -1,7 +1,13 @@ use std::collections::BTreeMap; use axum::{response::IntoResponse, Json}; -use ruma::api::client::{discovery::get_supported_versions, error::ErrorKind}; +use ruma::api::client::{ + discovery::{ + discover_support::{self, Contact}, + get_supported_versions, + }, + error::ErrorKind, +}; use crate::{services, Error, Result, Ruma}; @@ -62,6 +68,51 @@ pub async fn well_known_client_route() -> Result { }))) } +/// # `GET /.well-known/matrix/support` +/// +/// Server support contact and support page of a homeserver's domain. +pub async fn well_known_support(_body: Ruma) -> Result { + let support_page = services().globals.well_known_support_page().clone(); + + let role = services().globals.well_known_support_role().clone(); + + // support page or role must be either defined for this to be valid + if support_page.is_none() && role.is_none() { + return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")); + } + + let email_address = services().globals.well_known_support_email().clone(); + let matrix_id = services().globals.well_known_support_mxid().clone(); + + // if a role is specified, an email address or matrix id is required + if role.is_some() && (email_address.is_none() && matrix_id.is_none()) { + return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")); + } + + // TOOD: support defining multiple contacts in the config + let mut contacts: Vec = vec![]; + + if let Some(role) = role { + let contact = Contact { + role, + email_address, + matrix_id, + }; + + contacts.push(contact); + } + + // support page or role+contacts must be either defined for this to be valid + if contacts.is_empty() && support_page.is_none() { + return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")); + } + + Ok(discover_support::Response { + contacts, + support_page, + }) +} + /// # `GET /client/server.json` /// /// Endpoint provided by sliding sync proxy used by some clients such as Element diff --git a/src/config/mod.rs b/src/config/mod.rs index e0b379df..786168a6 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -15,7 +15,9 @@ use figment::{ }; use itertools::Itertools; use regex::RegexSet; -use ruma::{OwnedRoomId, OwnedServerName, RoomVersionId}; +use ruma::{ + api::client::discovery::discover_support::ContactRole, OwnedRoomId, OwnedServerName, OwnedUserId, RoomVersionId, +}; use serde::{de::IgnoredAny, Deserialize}; use tracing::{debug, error, warn}; @@ -254,6 +256,11 @@ pub struct Config { #[serde(default = "default_ip_range_denylist")] pub ip_range_denylist: Vec, + pub well_known_support_page: Option, + pub well_known_support_role: Option, + pub well_known_support_email: Option, + pub well_known_support_mxid: Option, + #[serde(default = "Vec::new")] pub url_preview_domain_contains_allowlist: Vec, #[serde(default = "Vec::new")] diff --git a/src/routes.rs b/src/routes.rs index cb43322f..2ae1072f 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -206,6 +206,7 @@ pub fn routes() -> Router { .ruma_route(server_server::get_keys_route) .ruma_route(server_server::claim_keys_route) .ruma_route(server_server::get_hierarchy_route) + .ruma_route(client_server::well_known_support) .route("/_conduwuit/server_version", get(client_server::conduwuit_server_version)) .route("/_matrix/client/r0/rooms/:room_id/initialSync", get(initial_sync)) .route("/_matrix/client/v3/rooms/:room_id/initialSync", get(initial_sync)) diff --git a/src/service/globals/mod.rs b/src/service/globals/mod.rs index b9086e34..d78f5a12 100644 --- a/src/service/globals/mod.rs +++ b/src/service/globals/mod.rs @@ -17,7 +17,7 @@ use hickory_resolver::TokioAsyncResolver; use regex::RegexSet; use ruma::{ api::{ - client::sync::sync_events, + client::{discovery::discover_support::ContactRole, sync::sync_events}, federation::discovery::{ServerSigningKeys, VerifyKey}, }, serde::Base64, @@ -307,6 +307,14 @@ impl Service<'_> { pub fn ip_range_denylist(&self) -> &[String] { &self.config.ip_range_denylist } + pub fn well_known_support_page(&self) -> &Option { &self.config.well_known_support_page } + + pub fn well_known_support_role(&self) -> &Option { &self.config.well_known_support_role } + + pub fn well_known_support_email(&self) -> &Option { &self.config.well_known_support_email } + + pub fn well_known_support_mxid(&self) -> &Option { &self.config.well_known_support_mxid } + pub fn block_non_admin_invites(&self) -> bool { self.config.block_non_admin_invites } pub fn supported_room_versions(&self) -> Vec {