2023-06-26 08:33:31 +02:00
|
|
|
#![allow(deprecated)]
|
|
|
|
|
2021-05-20 23:46:52 +02:00
|
|
|
use crate::{
|
2022-09-06 23:15:09 +02:00
|
|
|
api::client_server::{self, claim_keys_helper, get_keys_helper},
|
2024-07-06 22:56:08 +01:00
|
|
|
service::{
|
|
|
|
globals::SigningKeys,
|
|
|
|
pdu::{gen_event_id_canonical_json, PduBuilder},
|
|
|
|
},
|
2022-10-05 20:34:31 +02:00
|
|
|
services, utils, Error, PduEvent, Result, Ruma,
|
2021-05-20 23:46:52 +02:00
|
|
|
};
|
2022-01-20 11:51:31 +01:00
|
|
|
use axum::{response::IntoResponse, Json};
|
2024-06-24 22:12:54 +01:00
|
|
|
use axum_extra::headers::{authorization::Credentials, CacheControl, Header};
|
2020-10-05 22:19:22 +02:00
|
|
|
use get_profile_information::v1::ProfileField;
|
2024-06-25 09:39:06 +01:00
|
|
|
use http::header::AUTHORIZATION;
|
2022-10-05 20:41:05 +02:00
|
|
|
|
2020-09-12 22:41:33 +02:00
|
|
|
use ruma::{
|
|
|
|
api::{
|
2021-06-17 20:12:36 +02:00
|
|
|
client::error::{Error as RumaError, ErrorKind},
|
2020-09-12 22:41:33 +02:00
|
|
|
federation::{
|
2021-06-14 11:36:18 +02:00
|
|
|
authorization::get_event_authorization,
|
2023-02-18 13:20:20 +01:00
|
|
|
backfill::get_backfill,
|
2021-04-21 10:51:34 +02:00
|
|
|
device::get_devices::{self, v1::UserDevice},
|
2020-09-25 12:26:29 +02:00
|
|
|
directory::{get_public_rooms, get_public_rooms_filtered},
|
2024-05-02 09:26:43 +01:00
|
|
|
discovery::{
|
|
|
|
discover_homeserver, get_server_keys, get_server_version, ServerSigningKeys,
|
|
|
|
VerifyKey,
|
|
|
|
},
|
2021-06-14 10:52:27 +02:00
|
|
|
event::{get_event, get_missing_events, get_room_state, get_room_state_ids},
|
2021-05-28 13:44:40 +02:00
|
|
|
keys::{claim_keys, get_keys},
|
2023-02-26 16:29:06 +01:00
|
|
|
membership::{create_invite, create_join_event, prepare_join_event},
|
2024-05-28 00:22:11 +02:00
|
|
|
openid::get_openid_userinfo,
|
2021-04-16 18:18:29 +02:00
|
|
|
query::{get_profile_information, get_room_information},
|
2021-05-28 13:44:40 +02:00
|
|
|
transactions::{
|
2022-03-05 10:16:21 +08:00
|
|
|
edu::{DeviceListUpdateContent, DirectDeviceContent, Edu, SigningKeyUpdateContent},
|
2021-05-28 13:44:40 +02:00
|
|
|
send_transaction_message,
|
|
|
|
},
|
2020-08-06 08:29:59 -04:00
|
|
|
},
|
2022-02-12 21:04:38 +01:00
|
|
|
EndpointError, IncomingResponse, MatrixVersion, OutgoingRequest, OutgoingResponse,
|
|
|
|
SendAccessToken,
|
2020-08-14 11:29:32 +02:00
|
|
|
},
|
2022-12-14 13:09:10 +01:00
|
|
|
directory::{Filter, RoomNetwork},
|
2021-04-11 21:01:27 +02:00
|
|
|
events::{
|
2022-10-09 17:25:06 +02:00
|
|
|
receipt::{ReceiptEvent, ReceiptEventContent, ReceiptType},
|
2021-04-16 18:18:29 +02:00
|
|
|
room::{
|
2022-01-18 16:53:25 +01:00
|
|
|
join_rules::{JoinRule, RoomJoinRulesEventContent},
|
2021-10-13 10:16:45 +02:00
|
|
|
member::{MembershipState, RoomMemberEventContent},
|
2021-04-16 18:18:29 +02:00
|
|
|
},
|
2023-02-26 16:29:06 +01:00
|
|
|
StateEventType, TimelineEventType,
|
2021-04-11 21:01:27 +02:00
|
|
|
},
|
2022-02-18 15:33:14 +01:00
|
|
|
serde::{Base64, JsonObject, Raw},
|
2024-06-25 09:39:06 +01:00
|
|
|
server_util::authorization::XMatrix,
|
2022-10-08 13:03:07 +02:00
|
|
|
to_device::DeviceIdOrAllDevices,
|
2023-02-18 13:20:20 +01:00
|
|
|
uint, user_id, CanonicalJsonObject, CanonicalJsonValue, EventId, MilliSecondsSinceUnixEpoch,
|
|
|
|
OwnedEventId, OwnedRoomId, OwnedServerName, OwnedServerSigningKeyId, OwnedUserId, RoomId,
|
|
|
|
ServerName,
|
2020-05-26 10:27:51 +02:00
|
|
|
};
|
2021-10-13 10:16:45 +02:00
|
|
|
use serde_json::value::{to_raw_value, RawValue as RawJsonValue};
|
2020-04-22 20:55:11 +02:00
|
|
|
use std::{
|
2022-10-08 13:03:07 +02:00
|
|
|
collections::BTreeMap,
|
2020-08-14 11:31:31 +02:00
|
|
|
fmt::Debug,
|
2021-04-29 20:58:05 +02:00
|
|
|
mem,
|
2020-12-08 12:34:46 +01:00
|
|
|
net::{IpAddr, SocketAddr},
|
2024-03-05 14:22:54 +00:00
|
|
|
sync::Arc,
|
2021-05-20 23:46:52 +02:00
|
|
|
time::{Duration, Instant, SystemTime},
|
2020-04-22 20:55:11 +02:00
|
|
|
};
|
2024-03-05 14:22:54 +00:00
|
|
|
use tokio::sync::RwLock;
|
2022-10-05 20:41:05 +02:00
|
|
|
|
2023-12-23 19:34:27 -08:00
|
|
|
use tracing::{debug, error, warn};
|
2020-04-19 14:14:47 +02:00
|
|
|
|
2021-04-21 00:35:44 -03:00
|
|
|
/// Wraps either an literal IP address plus port, or a hostname plus complement
|
|
|
|
/// (colon-plus-port if it was specified).
|
|
|
|
///
|
|
|
|
/// Note: A `FedDest::Named` might contain an IP address in string form if there
|
|
|
|
/// was no port specified to construct a SocketAddr with.
|
|
|
|
///
|
|
|
|
/// # Examples:
|
2022-04-14 16:42:08 +02:00
|
|
|
/// ```rust
|
2022-10-12 14:39:58 -07:00
|
|
|
/// # use conduit::api::server_server::FedDest;
|
2022-04-14 16:42:08 +02:00
|
|
|
/// # fn main() -> Result<(), std::net::AddrParseError> {
|
2021-04-21 00:35:44 -03:00
|
|
|
/// FedDest::Literal("198.51.100.3:8448".parse()?);
|
|
|
|
/// FedDest::Literal("[2001:db8::4:5]:443".parse()?);
|
|
|
|
/// FedDest::Named("matrix.example.org".to_owned(), "".to_owned());
|
|
|
|
/// FedDest::Named("matrix.example.org".to_owned(), ":8448".to_owned());
|
|
|
|
/// FedDest::Named("198.51.100.5".to_owned(), "".to_owned());
|
2022-04-14 16:42:08 +02:00
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
2021-04-21 00:35:44 -03:00
|
|
|
/// ```
|
2022-10-10 14:09:11 +02:00
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
2021-08-26 23:11:13 +02:00
|
|
|
pub enum FedDest {
|
2021-04-16 00:27:26 -03:00
|
|
|
Literal(SocketAddr),
|
|
|
|
Named(String, String),
|
|
|
|
}
|
|
|
|
|
2021-04-16 12:18:22 -03:00
|
|
|
impl FedDest {
|
2021-04-21 00:35:44 -03:00
|
|
|
fn into_https_string(self) -> String {
|
2021-04-16 00:27:26 -03:00
|
|
|
match self {
|
2022-12-21 10:42:12 +01:00
|
|
|
Self::Literal(addr) => format!("https://{addr}"),
|
|
|
|
Self::Named(host, port) => format!("https://{host}{port}"),
|
2021-04-16 00:27:26 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-21 00:35:44 -03:00
|
|
|
fn hostname(&self) -> String {
|
2021-04-16 00:27:26 -03:00
|
|
|
match &self {
|
|
|
|
Self::Literal(addr) => addr.ip().to_string(),
|
2021-04-16 12:18:22 -03:00
|
|
|
Self::Named(host, _) => host.clone(),
|
2021-04-16 00:27:26 -03:00
|
|
|
}
|
|
|
|
}
|
2021-08-26 23:11:13 +02:00
|
|
|
|
|
|
|
fn port(&self) -> Option<u16> {
|
|
|
|
match &self {
|
|
|
|
Self::Literal(addr) => Some(addr.port()),
|
|
|
|
Self::Named(_, port) => port[1..].parse().ok(),
|
|
|
|
}
|
|
|
|
}
|
2021-04-16 00:27:26 -03:00
|
|
|
}
|
|
|
|
|
2022-09-06 23:15:09 +02:00
|
|
|
#[tracing::instrument(skip(request))]
|
2024-05-05 13:11:44 +01:00
|
|
|
pub(crate) async fn send_request<T>(
|
2021-01-14 14:39:56 -05:00
|
|
|
destination: &ServerName,
|
2020-04-19 14:14:47 +02:00
|
|
|
request: T,
|
2020-08-14 11:31:31 +02:00
|
|
|
) -> Result<T::IncomingResponse>
|
|
|
|
where
|
2024-05-05 13:11:44 +01:00
|
|
|
T: OutgoingRequest + Debug,
|
2020-08-14 11:31:31 +02:00
|
|
|
{
|
2022-09-06 23:15:09 +02:00
|
|
|
if !services().globals.allow_federation() {
|
2020-11-14 23:13:06 +01:00
|
|
|
return Err(Error::bad_config("Federation is disabled."));
|
2020-10-06 21:04:51 +02:00
|
|
|
}
|
|
|
|
|
2023-07-10 16:26:36 +02:00
|
|
|
if destination == services().globals.server_name() {
|
|
|
|
return Err(Error::bad_config(
|
|
|
|
"Won't send federation request to ourselves",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2023-03-13 08:27:59 +01:00
|
|
|
debug!("Preparing to send request to {destination}");
|
2023-02-23 11:20:40 +01:00
|
|
|
|
2022-10-05 20:34:31 +02:00
|
|
|
let cached_result = services()
|
|
|
|
.globals
|
2020-12-06 11:05:51 +01:00
|
|
|
.actual_destination_cache
|
|
|
|
.read()
|
2024-03-05 14:22:54 +00:00
|
|
|
.await
|
2021-01-14 14:39:56 -05:00
|
|
|
.get(destination)
|
2020-12-06 11:05:51 +01:00
|
|
|
.cloned();
|
2020-09-23 12:03:08 +02:00
|
|
|
|
2024-06-24 22:12:54 +01:00
|
|
|
let actual_destination = if let Some(DestinationResponse {
|
|
|
|
actual_destination,
|
|
|
|
dest_type,
|
|
|
|
}) = cached_result
|
|
|
|
{
|
|
|
|
match dest_type {
|
|
|
|
DestType::IsIpOrHasPort => actual_destination,
|
|
|
|
DestType::LookupFailed {
|
|
|
|
well_known_retry,
|
|
|
|
well_known_backoff_mins,
|
|
|
|
} => {
|
|
|
|
if well_known_retry < Instant::now() {
|
|
|
|
find_actual_destination(destination, None, false, Some(well_known_backoff_mins))
|
|
|
|
.await
|
|
|
|
} else {
|
|
|
|
actual_destination
|
|
|
|
}
|
|
|
|
}
|
2021-08-26 23:11:13 +02:00
|
|
|
|
2024-06-24 22:12:54 +01:00
|
|
|
DestType::WellKnown { expires } => {
|
|
|
|
if expires < Instant::now() {
|
|
|
|
find_actual_destination(destination, None, false, None).await
|
|
|
|
} else {
|
|
|
|
actual_destination
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DestType::WellKnownSrv {
|
|
|
|
srv_expires,
|
|
|
|
well_known_expires,
|
|
|
|
well_known_host,
|
|
|
|
} => {
|
|
|
|
if well_known_expires < Instant::now() {
|
|
|
|
find_actual_destination(destination, None, false, None).await
|
|
|
|
} else if srv_expires < Instant::now() {
|
|
|
|
find_actual_destination(destination, Some(well_known_host), true, None).await
|
|
|
|
} else {
|
|
|
|
actual_destination
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DestType::Srv {
|
|
|
|
well_known_retry,
|
|
|
|
well_known_backoff_mins,
|
|
|
|
srv_expires,
|
|
|
|
} => {
|
|
|
|
if well_known_retry < Instant::now() {
|
|
|
|
find_actual_destination(destination, None, false, Some(well_known_backoff_mins))
|
|
|
|
.await
|
|
|
|
} else if srv_expires < Instant::now() {
|
|
|
|
find_actual_destination(destination, None, true, Some(well_known_backoff_mins))
|
|
|
|
.await
|
|
|
|
} else {
|
|
|
|
actual_destination
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
find_actual_destination(destination, None, false, None).await
|
2020-12-06 11:05:51 +01:00
|
|
|
};
|
2020-08-14 11:31:31 +02:00
|
|
|
|
2021-08-26 23:11:13 +02:00
|
|
|
let actual_destination_str = actual_destination.clone().into_https_string();
|
|
|
|
|
2020-08-14 11:31:31 +02:00
|
|
|
let mut http_request = request
|
2022-02-12 21:04:38 +01:00
|
|
|
.try_into_http_request::<Vec<u8>>(
|
|
|
|
&actual_destination_str,
|
|
|
|
SendAccessToken::IfRequired(""),
|
2024-08-28 11:24:51 +01:00
|
|
|
&[MatrixVersion::V1_11],
|
2022-02-12 21:04:38 +01:00
|
|
|
)
|
2020-09-15 08:55:02 +02:00
|
|
|
.map_err(|e| {
|
2021-08-26 23:11:13 +02:00
|
|
|
warn!(
|
|
|
|
"Failed to find destination {}: {}",
|
|
|
|
actual_destination_str, e
|
|
|
|
);
|
2020-09-15 08:55:02 +02:00
|
|
|
Error::BadServerResponse("Invalid destination")
|
|
|
|
})?;
|
2020-04-22 11:53:06 +02:00
|
|
|
|
2020-04-22 21:14:40 +02:00
|
|
|
let mut request_map = serde_json::Map::new();
|
2020-04-19 14:14:47 +02:00
|
|
|
|
2020-04-22 21:14:40 +02:00
|
|
|
if !http_request.body().is_empty() {
|
2020-04-25 11:47:32 +02:00
|
|
|
request_map.insert(
|
|
|
|
"content".to_owned(),
|
2020-09-15 08:55:02 +02:00
|
|
|
serde_json::from_slice(http_request.body())
|
|
|
|
.expect("body is valid json, we just created it"),
|
2020-04-25 11:47:32 +02:00
|
|
|
);
|
2020-04-22 21:14:40 +02:00
|
|
|
};
|
2020-04-19 14:14:47 +02:00
|
|
|
|
2020-04-22 11:53:06 +02:00
|
|
|
request_map.insert("method".to_owned(), T::METADATA.method.to_string().into());
|
2020-08-14 11:31:31 +02:00
|
|
|
request_map.insert(
|
|
|
|
"uri".to_owned(),
|
|
|
|
http_request
|
|
|
|
.uri()
|
|
|
|
.path_and_query()
|
|
|
|
.expect("all requests have a path")
|
|
|
|
.to_string()
|
|
|
|
.into(),
|
|
|
|
);
|
2022-10-05 20:34:31 +02:00
|
|
|
request_map.insert(
|
|
|
|
"origin".to_owned(),
|
|
|
|
services().globals.server_name().as_str().into(),
|
|
|
|
);
|
2020-09-14 11:00:31 +02:00
|
|
|
request_map.insert("destination".to_owned(), destination.as_str().into());
|
2020-04-22 21:14:40 +02:00
|
|
|
|
2020-10-27 19:10:09 -04:00
|
|
|
let mut request_json =
|
|
|
|
serde_json::from_value(request_map.into()).expect("valid JSON is valid BTreeMap");
|
|
|
|
|
2020-06-05 18:19:26 +02:00
|
|
|
ruma::signatures::sign_json(
|
2022-09-06 23:15:09 +02:00
|
|
|
services().globals.server_name().as_str(),
|
|
|
|
services().globals.keypair(),
|
2020-12-31 21:07:05 +01:00
|
|
|
&mut request_json,
|
2020-05-09 21:47:09 +02:00
|
|
|
)
|
2020-09-15 08:55:02 +02:00
|
|
|
.expect("our request json is what ruma expects");
|
2020-04-19 14:14:47 +02:00
|
|
|
|
2020-10-27 19:10:09 -04:00
|
|
|
let request_json: serde_json::Map<String, serde_json::Value> =
|
|
|
|
serde_json::from_slice(&serde_json::to_vec(&request_json).unwrap()).unwrap();
|
|
|
|
|
2020-04-22 11:53:06 +02:00
|
|
|
let signatures = request_json["signatures"]
|
|
|
|
.as_object()
|
|
|
|
.unwrap()
|
|
|
|
.values()
|
2020-08-14 11:31:31 +02:00
|
|
|
.map(|v| {
|
|
|
|
v.as_object()
|
|
|
|
.unwrap()
|
|
|
|
.iter()
|
|
|
|
.map(|(k, v)| (k, v.as_str().unwrap()))
|
|
|
|
});
|
|
|
|
|
|
|
|
for signature_server in signatures {
|
|
|
|
for s in signature_server {
|
|
|
|
http_request.headers_mut().insert(
|
|
|
|
AUTHORIZATION,
|
2024-06-25 09:39:06 +01:00
|
|
|
XMatrix::parse(&format!(
|
2024-04-30 09:31:44 +01:00
|
|
|
"X-Matrix origin=\"{}\",destination=\"{}\",key=\"{}\",sig=\"{}\"",
|
2022-09-06 23:15:09 +02:00
|
|
|
services().globals.server_name(),
|
2024-04-30 09:31:44 +01:00
|
|
|
destination,
|
2020-08-14 11:31:31 +02:00
|
|
|
s.0,
|
|
|
|
s.1
|
|
|
|
))
|
2024-06-25 09:39:06 +01:00
|
|
|
.expect("When Ruma signs JSON, it produces a valid base64 signature. All other types are valid ServerNames or OwnedKeyId")
|
|
|
|
.encode(),
|
2020-08-14 11:31:31 +02:00
|
|
|
);
|
|
|
|
}
|
2020-04-22 11:53:06 +02:00
|
|
|
}
|
|
|
|
|
2024-03-31 21:30:26 +01:00
|
|
|
let reqwest_request = reqwest::Request::try_from(http_request)?;
|
2020-08-14 11:31:31 +02:00
|
|
|
|
2020-09-23 15:23:29 +02:00
|
|
|
let url = reqwest_request.url().clone();
|
2021-08-26 23:11:13 +02:00
|
|
|
|
2023-03-13 08:27:59 +01:00
|
|
|
debug!("Sending request to {destination} at {url}");
|
2022-10-05 20:34:31 +02:00
|
|
|
let response = services()
|
|
|
|
.globals
|
|
|
|
.federation_client()
|
|
|
|
.execute(reqwest_request)
|
|
|
|
.await;
|
2023-03-13 08:27:59 +01:00
|
|
|
debug!("Received response from {destination} at {url}");
|
2020-04-22 11:53:06 +02:00
|
|
|
|
2021-04-29 20:58:05 +02:00
|
|
|
match response {
|
|
|
|
Ok(mut response) => {
|
|
|
|
// reqwest::Response -> http::Response conversion
|
|
|
|
let status = response.status();
|
|
|
|
let mut http_response_builder = http::Response::builder()
|
|
|
|
.status(status)
|
|
|
|
.version(response.version());
|
|
|
|
mem::swap(
|
|
|
|
response.headers_mut(),
|
|
|
|
http_response_builder
|
|
|
|
.headers_mut()
|
|
|
|
.expect("http::response::Builder is usable"),
|
|
|
|
);
|
2020-12-05 21:03:43 +01:00
|
|
|
|
2023-03-13 08:27:59 +01:00
|
|
|
debug!("Getting response bytes from {destination}");
|
2021-04-29 20:58:05 +02:00
|
|
|
let body = response.bytes().await.unwrap_or_else(|e| {
|
2021-04-13 15:00:45 +02:00
|
|
|
warn!("server error {}", e);
|
|
|
|
Vec::new().into()
|
|
|
|
}); // TODO: handle timeout
|
2023-03-13 08:27:59 +01:00
|
|
|
debug!("Got response bytes from {destination}");
|
2020-12-05 21:03:43 +01:00
|
|
|
|
|
|
|
if status != 200 {
|
2021-08-21 14:22:21 +02:00
|
|
|
warn!(
|
2021-03-26 11:10:45 +01:00
|
|
|
"{} {}: {}",
|
|
|
|
url,
|
|
|
|
status,
|
2021-03-26 13:41:05 +01:00
|
|
|
String::from_utf8_lossy(&body)
|
|
|
|
.lines()
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join(" ")
|
2021-03-26 11:10:45 +01:00
|
|
|
);
|
2020-12-05 21:03:43 +01:00
|
|
|
}
|
2020-09-12 21:30:07 +02:00
|
|
|
|
2021-05-23 16:45:32 +02:00
|
|
|
let http_response = http_response_builder
|
|
|
|
.body(body)
|
|
|
|
.expect("reqwest body is valid http body");
|
|
|
|
|
|
|
|
if status == 200 {
|
2023-03-13 08:27:59 +01:00
|
|
|
debug!("Parsing response bytes from {destination}");
|
2021-05-23 16:45:32 +02:00
|
|
|
let response = T::IncomingResponse::try_from_http_response(http_response);
|
2021-08-26 23:11:13 +02:00
|
|
|
|
2021-08-04 22:55:03 +02:00
|
|
|
response.map_err(|e| {
|
2021-08-17 16:06:09 +02:00
|
|
|
warn!(
|
|
|
|
"Invalid 200 response from {} on: {} {}",
|
|
|
|
&destination, url, e
|
|
|
|
);
|
2021-08-04 22:55:03 +02:00
|
|
|
Error::BadServerResponse("Server returned bad 200 response.")
|
|
|
|
})
|
2021-05-23 16:45:32 +02:00
|
|
|
} else {
|
2023-03-13 08:27:59 +01:00
|
|
|
debug!("Returning error from {destination}");
|
2021-05-23 16:45:32 +02:00
|
|
|
Err(Error::FederationError(
|
|
|
|
destination.to_owned(),
|
2022-12-14 13:09:10 +01:00
|
|
|
RumaError::from_http_response(http_response),
|
2021-05-23 16:45:32 +02:00
|
|
|
))
|
|
|
|
}
|
2020-04-22 11:53:06 +02:00
|
|
|
}
|
2022-10-13 10:14:52 +02:00
|
|
|
Err(e) => {
|
2022-10-15 00:28:43 +02:00
|
|
|
warn!(
|
|
|
|
"Could not send request to {} at {}: {}",
|
|
|
|
destination, actual_destination_str, e
|
|
|
|
);
|
2022-10-13 10:14:52 +02:00
|
|
|
Err(e.into())
|
2022-10-15 00:28:43 +02:00
|
|
|
}
|
2020-04-22 11:53:06 +02:00
|
|
|
}
|
2020-04-19 14:14:47 +02:00
|
|
|
}
|
2020-04-22 20:55:11 +02:00
|
|
|
|
2021-04-16 12:18:22 -03:00
|
|
|
fn get_ip_with_port(destination_str: &str) -> Option<FedDest> {
|
2021-04-16 00:27:26 -03:00
|
|
|
if let Ok(destination) = destination_str.parse::<SocketAddr>() {
|
2021-04-16 12:18:22 -03:00
|
|
|
Some(FedDest::Literal(destination))
|
2020-12-08 12:34:46 +01:00
|
|
|
} else if let Ok(ip_addr) = destination_str.parse::<IpAddr>() {
|
2021-04-16 12:18:22 -03:00
|
|
|
Some(FedDest::Literal(SocketAddr::new(ip_addr, 8448)))
|
2020-12-08 12:34:46 +01:00
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-16 12:18:22 -03:00
|
|
|
fn add_port_to_hostname(destination_str: &str) -> FedDest {
|
2021-04-16 00:27:26 -03:00
|
|
|
let (host, port) = match destination_str.find(':') {
|
|
|
|
None => (destination_str, ":8448"),
|
|
|
|
Some(pos) => destination_str.split_at(pos),
|
|
|
|
};
|
2021-10-13 10:24:39 +02:00
|
|
|
FedDest::Named(host.to_owned(), port.to_owned())
|
2020-12-08 12:34:46 +01:00
|
|
|
}
|
|
|
|
|
2024-06-24 22:12:54 +01:00
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct DestinationResponse {
|
|
|
|
pub actual_destination: FedDest,
|
|
|
|
pub dest_type: DestType,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub enum DestType {
|
|
|
|
WellKnownSrv {
|
|
|
|
srv_expires: Instant,
|
|
|
|
well_known_expires: Instant,
|
|
|
|
well_known_host: String,
|
|
|
|
},
|
|
|
|
WellKnown {
|
|
|
|
expires: Instant,
|
|
|
|
},
|
|
|
|
Srv {
|
|
|
|
srv_expires: Instant,
|
|
|
|
well_known_retry: Instant,
|
|
|
|
well_known_backoff_mins: u16,
|
|
|
|
},
|
|
|
|
IsIpOrHasPort,
|
|
|
|
LookupFailed {
|
|
|
|
well_known_retry: Instant,
|
|
|
|
well_known_backoff_mins: u16,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Implemented according to the specification at <https://spec.matrix.org/v1.11/server-server-api/#resolving-server-names>
|
2020-12-08 12:34:46 +01:00
|
|
|
/// Numbers in comments below refer to bullet points in linked section of specification
|
2024-06-24 22:12:54 +01:00
|
|
|
async fn find_actual_destination(
|
|
|
|
destination: &'_ ServerName,
|
|
|
|
// The host used to potentially lookup SRV records against, only used when only_request_srv is true
|
|
|
|
well_known_dest: Option<String>,
|
|
|
|
// Should be used when only the SRV lookup has expired
|
|
|
|
only_request_srv: bool,
|
|
|
|
// The backoff time for the last well known failure, if any
|
|
|
|
well_known_backoff_mins: Option<u16>,
|
|
|
|
) -> FedDest {
|
2023-03-13 08:27:59 +01:00
|
|
|
debug!("Finding actual destination for {destination}");
|
2024-06-24 22:12:54 +01:00
|
|
|
let destination_str = destination.to_string();
|
|
|
|
let next_backoff_mins = well_known_backoff_mins
|
|
|
|
// Errors are recommended to be cached for up to an hour
|
|
|
|
.map(|mins| (mins * 2).min(60))
|
|
|
|
.unwrap_or(1);
|
|
|
|
|
|
|
|
let (actual_destination, dest_type) = if only_request_srv {
|
|
|
|
let destination_str = well_known_dest.unwrap_or(destination_str);
|
|
|
|
let (dest, expires) = get_srv_destination(destination_str).await;
|
|
|
|
let well_known_retry =
|
|
|
|
Instant::now() + Duration::from_secs((60 * next_backoff_mins).into());
|
|
|
|
(
|
|
|
|
dest,
|
|
|
|
if let Some(expires) = expires {
|
|
|
|
DestType::Srv {
|
|
|
|
well_known_backoff_mins: next_backoff_mins,
|
|
|
|
srv_expires: expires,
|
|
|
|
|
|
|
|
well_known_retry,
|
|
|
|
}
|
2021-04-16 12:18:22 -03:00
|
|
|
} else {
|
2024-06-24 22:12:54 +01:00
|
|
|
DestType::LookupFailed {
|
|
|
|
well_known_retry,
|
|
|
|
well_known_backoff_mins: next_backoff_mins,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
match get_ip_with_port(&destination_str) {
|
|
|
|
Some(host_port) => {
|
|
|
|
debug!("1: IP literal with provided or default port");
|
|
|
|
(host_port, DestType::IsIpOrHasPort)
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
if let Some(pos) = destination_str.find(':') {
|
|
|
|
debug!("2: Hostname with included port");
|
|
|
|
let (host, port) = destination_str.split_at(pos);
|
|
|
|
(
|
|
|
|
FedDest::Named(host.to_owned(), port.to_owned()),
|
|
|
|
DestType::IsIpOrHasPort,
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
debug!("Requesting well known for {destination_str}");
|
|
|
|
match request_well_known(destination_str.as_str()).await {
|
|
|
|
Some((delegated_hostname, timestamp)) => {
|
|
|
|
debug!("3: A .well-known file is available");
|
|
|
|
match get_ip_with_port(&delegated_hostname) {
|
|
|
|
// 3.1: IP literal in .well-known file
|
|
|
|
Some(host_and_port) => {
|
|
|
|
(host_and_port, DestType::WellKnown { expires: timestamp })
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
if let Some(pos) = delegated_hostname.find(':') {
|
|
|
|
debug!("3.2: Hostname with port in .well-known file");
|
|
|
|
let (host, port) = delegated_hostname.split_at(pos);
|
|
|
|
(
|
|
|
|
FedDest::Named(host.to_owned(), port.to_owned()),
|
|
|
|
DestType::WellKnown { expires: timestamp },
|
|
|
|
)
|
2021-08-26 23:11:13 +02:00
|
|
|
} else {
|
2024-06-24 22:12:54 +01:00
|
|
|
debug!("Delegated hostname has no port in this branch");
|
|
|
|
let (dest, srv_expires) =
|
|
|
|
get_srv_destination(delegated_hostname.clone()).await;
|
|
|
|
(
|
|
|
|
dest,
|
|
|
|
if let Some(srv_expires) = srv_expires {
|
|
|
|
DestType::WellKnownSrv {
|
|
|
|
srv_expires,
|
|
|
|
well_known_expires: timestamp,
|
|
|
|
well_known_host: delegated_hostname,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
DestType::WellKnown { expires: timestamp }
|
|
|
|
},
|
|
|
|
)
|
2020-12-08 12:34:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-06-24 22:12:54 +01:00
|
|
|
None => {
|
|
|
|
debug!("4: No .well-known or an error occured");
|
|
|
|
let (dest, expires) = get_srv_destination(destination_str).await;
|
|
|
|
let well_known_retry = Instant::now()
|
|
|
|
+ Duration::from_secs((60 * next_backoff_mins).into());
|
|
|
|
(
|
|
|
|
dest,
|
|
|
|
if let Some(expires) = expires {
|
|
|
|
DestType::Srv {
|
|
|
|
srv_expires: expires,
|
|
|
|
well_known_retry,
|
|
|
|
well_known_backoff_mins: next_backoff_mins,
|
|
|
|
}
|
2021-08-26 23:11:13 +02:00
|
|
|
} else {
|
2024-06-24 22:12:54 +01:00
|
|
|
DestType::LookupFailed {
|
|
|
|
well_known_retry,
|
|
|
|
well_known_backoff_mins: next_backoff_mins,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
)
|
2020-12-08 12:34:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-12-06 11:05:51 +01:00
|
|
|
}
|
2021-04-16 00:27:26 -03:00
|
|
|
}
|
2021-04-16 12:18:22 -03:00
|
|
|
};
|
2024-06-24 22:12:54 +01:00
|
|
|
|
2023-03-13 08:27:59 +01:00
|
|
|
debug!("Actual destination: {actual_destination:?}");
|
2020-12-06 11:05:51 +01:00
|
|
|
|
2024-06-24 22:12:54 +01:00
|
|
|
let response = DestinationResponse {
|
|
|
|
actual_destination,
|
|
|
|
dest_type,
|
2021-04-16 12:18:22 -03:00
|
|
|
};
|
2024-06-24 22:12:54 +01:00
|
|
|
|
|
|
|
services()
|
|
|
|
.globals
|
|
|
|
.actual_destination_cache
|
|
|
|
.write()
|
|
|
|
.await
|
|
|
|
.insert(destination.to_owned(), response.clone());
|
|
|
|
|
|
|
|
response.actual_destination
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Looks up the SRV records for federation usage
|
|
|
|
///
|
|
|
|
/// If no timestamp is returned, that means no SRV record was found
|
|
|
|
async fn get_srv_destination(delegated_hostname: String) -> (FedDest, Option<Instant>) {
|
|
|
|
if let Some((hostname_override, timestamp)) = query_srv_record(&delegated_hostname).await {
|
|
|
|
debug!("SRV lookup successful");
|
|
|
|
let force_port = hostname_override.port();
|
|
|
|
|
|
|
|
if let Ok(override_ip) = services()
|
|
|
|
.globals
|
|
|
|
.dns_resolver()
|
|
|
|
.lookup_ip(hostname_override.hostname())
|
|
|
|
.await
|
|
|
|
{
|
|
|
|
services()
|
|
|
|
.globals
|
|
|
|
.tls_name_override
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.insert(
|
|
|
|
delegated_hostname.clone(),
|
|
|
|
(override_ip.iter().collect(), force_port.unwrap_or(8448)),
|
|
|
|
);
|
|
|
|
} else {
|
2024-07-06 17:06:11 +01:00
|
|
|
// Removing in case there was previously a SRV record
|
|
|
|
services()
|
|
|
|
.globals
|
|
|
|
.tls_name_override
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.remove(&delegated_hostname);
|
2024-06-24 22:12:54 +01:00
|
|
|
warn!("Using SRV record, but could not resolve to IP");
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(port) = force_port {
|
|
|
|
(
|
|
|
|
FedDest::Named(delegated_hostname, format!(":{port}")),
|
|
|
|
Some(timestamp),
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
(add_port_to_hostname(&delegated_hostname), Some(timestamp))
|
|
|
|
}
|
|
|
|
} else {
|
2024-07-06 17:31:31 +01:00
|
|
|
// Removing in case there was previously a SRV record
|
|
|
|
services()
|
|
|
|
.globals
|
|
|
|
.tls_name_override
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.remove(&delegated_hostname);
|
2024-06-24 22:12:54 +01:00
|
|
|
debug!("No SRV records found");
|
|
|
|
(add_port_to_hostname(&delegated_hostname), None)
|
|
|
|
}
|
2020-12-06 11:05:51 +01:00
|
|
|
}
|
|
|
|
|
2024-06-24 22:12:54 +01:00
|
|
|
async fn query_given_srv_record(record: &str) -> Option<(FedDest, Instant)> {
|
2024-04-01 20:55:13 +01:00
|
|
|
services()
|
2022-10-05 20:34:31 +02:00
|
|
|
.globals
|
2020-12-08 12:34:46 +01:00
|
|
|
.dns_resolver()
|
2024-04-01 20:55:13 +01:00
|
|
|
.srv_lookup(record)
|
2020-12-08 12:34:46 +01:00
|
|
|
.await
|
|
|
|
.map(|srv| {
|
|
|
|
srv.iter().next().map(|result| {
|
2024-06-24 22:12:54 +01:00
|
|
|
(
|
|
|
|
FedDest::Named(
|
|
|
|
result.target().to_string().trim_end_matches('.').to_owned(),
|
|
|
|
format!(":{}", result.port()),
|
|
|
|
),
|
|
|
|
srv.as_lookup().valid_until(),
|
2020-12-08 12:34:46 +01:00
|
|
|
)
|
|
|
|
})
|
|
|
|
})
|
2024-04-01 20:55:13 +01:00
|
|
|
.unwrap_or(None)
|
|
|
|
}
|
|
|
|
|
2024-06-24 22:12:54 +01:00
|
|
|
async fn query_srv_record(hostname: &'_ str) -> Option<(FedDest, Instant)> {
|
2024-04-01 20:55:13 +01:00
|
|
|
let hostname = hostname.trim_end_matches('.');
|
|
|
|
|
|
|
|
if let Some(host_port) = query_given_srv_record(&format!("_matrix-fed._tcp.{hostname}.")).await
|
2020-12-08 12:34:46 +01:00
|
|
|
{
|
|
|
|
Some(host_port)
|
|
|
|
} else {
|
2024-04-01 20:55:13 +01:00
|
|
|
query_given_srv_record(&format!("_matrix._tcp.{hostname}.")).await
|
2020-12-08 12:34:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-24 22:12:54 +01:00
|
|
|
async fn request_well_known(destination: &str) -> Option<(String, Instant)> {
|
2023-02-23 12:28:15 +01:00
|
|
|
let response = services()
|
|
|
|
.globals
|
|
|
|
.default_client()
|
|
|
|
.get(&format!("https://{destination}/.well-known/matrix/server"))
|
|
|
|
.send()
|
|
|
|
.await;
|
2023-03-13 08:27:59 +01:00
|
|
|
debug!("Got well known response");
|
2024-06-24 22:12:54 +01:00
|
|
|
let response = match response {
|
|
|
|
Err(e) => {
|
|
|
|
debug!("Well known error: {e:?}");
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
Ok(r) => r,
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut headers = response.headers().values();
|
|
|
|
|
|
|
|
let cache_for = CacheControl::decode(&mut headers)
|
|
|
|
.ok()
|
|
|
|
.and_then(|cc| {
|
|
|
|
// Servers should respect the cache control headers present on the response, or use a sensible default when headers are not present.
|
|
|
|
if cc.no_store() || cc.no_cache() {
|
|
|
|
Some(Duration::ZERO)
|
|
|
|
} else {
|
|
|
|
cc.max_age()
|
|
|
|
// Servers should additionally impose a maximum cache time for responses: 48 hours is recommended.
|
|
|
|
.map(|age| age.min(Duration::from_secs(60 * 60 * 48)))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
// The recommended sensible default is 24 hours.
|
|
|
|
.unwrap_or_else(|| Duration::from_secs(60 * 60 * 24));
|
|
|
|
|
|
|
|
let text = response.text().await;
|
2023-03-13 08:27:59 +01:00
|
|
|
debug!("Got well known response text");
|
2024-06-24 22:12:54 +01:00
|
|
|
|
|
|
|
let host = || {
|
|
|
|
let body: serde_json::Value = serde_json::from_str(&text.ok()?).ok()?;
|
|
|
|
body.get("m.server")?.as_str().map(ToOwned::to_owned)
|
|
|
|
};
|
|
|
|
|
|
|
|
host().map(|host| (host, Instant::now() + cache_for))
|
2020-12-08 12:34:46 +01:00
|
|
|
}
|
|
|
|
|
2021-08-31 19:14:37 +02:00
|
|
|
/// # `GET /_matrix/federation/v1/version`
|
|
|
|
///
|
|
|
|
/// Get version information on this server.
|
2022-01-20 11:51:31 +01:00
|
|
|
pub async fn get_server_version_route(
|
|
|
|
_body: Ruma<get_server_version::v1::Request>,
|
2022-01-22 16:58:32 +01:00
|
|
|
) -> Result<get_server_version::v1::Response> {
|
2021-04-11 21:01:27 +02:00
|
|
|
Ok(get_server_version::v1::Response {
|
|
|
|
server: Some(get_server_version::v1::Server {
|
2020-04-22 20:55:11 +02:00
|
|
|
name: Some("Conduit".to_owned()),
|
|
|
|
version: Some(env!("CARGO_PKG_VERSION").to_owned()),
|
2020-04-28 20:03:14 +02:00
|
|
|
}),
|
2022-01-22 16:58:32 +01:00
|
|
|
})
|
2020-04-22 20:55:11 +02:00
|
|
|
}
|
|
|
|
|
2021-08-31 19:14:37 +02:00
|
|
|
/// # `GET /_matrix/key/v2/server`
|
|
|
|
///
|
|
|
|
/// Gets the public signing keys of this server.
|
|
|
|
///
|
|
|
|
/// - Matrix does not support invalidating public keys, so the key returned by this will be valid
|
|
|
|
/// forever.
|
2021-04-13 15:00:45 +02:00
|
|
|
// Response type for this endpoint is Json because we need to calculate a signature for the response
|
2022-09-06 23:15:09 +02:00
|
|
|
pub async fn get_server_keys_route() -> Result<impl IntoResponse> {
|
2022-10-09 17:25:06 +02:00
|
|
|
let mut verify_keys: BTreeMap<OwnedServerSigningKeyId, VerifyKey> = BTreeMap::new();
|
2020-04-22 20:55:11 +02:00
|
|
|
verify_keys.insert(
|
2022-09-06 23:15:09 +02:00
|
|
|
format!("ed25519:{}", services().globals.keypair().version())
|
2021-11-27 00:30:28 +01:00
|
|
|
.try_into()
|
|
|
|
.expect("found invalid server signing keys in DB"),
|
2020-08-14 11:31:31 +02:00
|
|
|
VerifyKey {
|
2022-09-06 23:15:09 +02:00
|
|
|
key: Base64::new(services().globals.keypair().public_key().to_vec()),
|
2020-04-22 20:55:11 +02:00
|
|
|
},
|
|
|
|
);
|
|
|
|
let mut response = serde_json::from_slice(
|
2021-04-13 15:00:45 +02:00
|
|
|
get_server_keys::v2::Response {
|
2022-02-18 15:33:14 +01:00
|
|
|
server_key: Raw::new(&ServerSigningKeys {
|
2022-09-06 23:15:09 +02:00
|
|
|
server_name: services().globals.server_name().to_owned(),
|
2020-08-14 11:31:31 +02:00
|
|
|
verify_keys,
|
|
|
|
old_verify_keys: BTreeMap::new(),
|
|
|
|
signatures: BTreeMap::new(),
|
2021-05-20 23:46:52 +02:00
|
|
|
valid_until_ts: MilliSecondsSinceUnixEpoch::from_system_time(
|
2021-08-25 16:06:35 +02:00
|
|
|
SystemTime::now() + Duration::from_secs(86400 * 7),
|
2021-05-20 23:46:52 +02:00
|
|
|
)
|
|
|
|
.expect("time is valid"),
|
2022-02-18 15:33:14 +01:00
|
|
|
})
|
|
|
|
.expect("static conversion, no errors"),
|
2021-04-13 15:00:45 +02:00
|
|
|
}
|
2021-04-23 18:45:06 +02:00
|
|
|
.try_into_http_response::<Vec<u8>>()
|
2020-04-22 20:55:11 +02:00
|
|
|
.unwrap()
|
|
|
|
.body(),
|
|
|
|
)
|
|
|
|
.unwrap();
|
2020-11-15 16:48:43 -05:00
|
|
|
|
2020-06-05 18:19:26 +02:00
|
|
|
ruma::signatures::sign_json(
|
2022-09-06 23:15:09 +02:00
|
|
|
services().globals.server_name().as_str(),
|
|
|
|
services().globals.keypair(),
|
2020-05-17 19:56:40 +02:00
|
|
|
&mut response,
|
|
|
|
)
|
|
|
|
.unwrap();
|
2020-11-15 16:48:43 -05:00
|
|
|
|
2022-01-22 13:32:21 +01:00
|
|
|
Ok(Json(response))
|
2020-04-22 20:55:11 +02:00
|
|
|
}
|
|
|
|
|
2021-08-31 19:14:37 +02:00
|
|
|
/// # `GET /_matrix/key/v2/server/{keyId}`
|
|
|
|
///
|
|
|
|
/// Gets the public signing keys of this server.
|
|
|
|
///
|
|
|
|
/// - Matrix does not support invalidating public keys, so the key returned by this will be valid
|
|
|
|
/// forever.
|
2022-09-06 23:15:09 +02:00
|
|
|
pub async fn get_server_keys_deprecated_route() -> impl IntoResponse {
|
|
|
|
get_server_keys_route().await
|
2020-04-22 20:55:11 +02:00
|
|
|
}
|
2020-08-14 11:29:32 +02:00
|
|
|
|
2021-08-31 19:14:37 +02:00
|
|
|
/// # `POST /_matrix/federation/v1/publicRooms`
|
|
|
|
///
|
|
|
|
/// Lists the public rooms on this server.
|
2020-09-14 11:42:16 +02:00
|
|
|
pub async fn get_public_rooms_filtered_route(
|
2022-12-14 13:09:10 +01:00
|
|
|
body: Ruma<get_public_rooms_filtered::v1::Request>,
|
2022-01-22 16:58:32 +01:00
|
|
|
) -> Result<get_public_rooms_filtered::v1::Response> {
|
2020-09-14 11:42:16 +02:00
|
|
|
let response = client_server::get_public_rooms_filtered_helper(
|
|
|
|
None,
|
|
|
|
body.limit,
|
|
|
|
body.since.as_deref(),
|
|
|
|
&body.filter,
|
|
|
|
&body.room_network,
|
|
|
|
)
|
2022-01-22 16:58:32 +01:00
|
|
|
.await?;
|
2020-09-14 11:42:16 +02:00
|
|
|
|
|
|
|
Ok(get_public_rooms_filtered::v1::Response {
|
2022-02-18 11:52:00 +01:00
|
|
|
chunk: response.chunk,
|
2020-09-14 11:42:16 +02:00
|
|
|
prev_batch: response.prev_batch,
|
|
|
|
next_batch: response.next_batch,
|
|
|
|
total_room_count_estimate: response.total_room_count_estimate,
|
2022-01-22 16:58:32 +01:00
|
|
|
})
|
2020-09-14 11:42:16 +02:00
|
|
|
}
|
|
|
|
|
2021-08-31 19:14:37 +02:00
|
|
|
/// # `GET /_matrix/federation/v1/publicRooms`
|
|
|
|
///
|
|
|
|
/// Lists the public rooms on this server.
|
2020-08-14 11:29:32 +02:00
|
|
|
pub async fn get_public_rooms_route(
|
2022-12-14 13:09:10 +01:00
|
|
|
body: Ruma<get_public_rooms::v1::Request>,
|
2022-01-22 16:58:32 +01:00
|
|
|
) -> Result<get_public_rooms::v1::Response> {
|
2020-09-14 11:42:16 +02:00
|
|
|
let response = client_server::get_public_rooms_filtered_helper(
|
2020-08-23 08:32:43 -04:00
|
|
|
None,
|
2020-09-14 11:42:16 +02:00
|
|
|
body.limit,
|
|
|
|
body.since.as_deref(),
|
2022-12-14 13:09:10 +01:00
|
|
|
&Filter::default(),
|
|
|
|
&RoomNetwork::Matrix,
|
2020-08-14 11:29:32 +02:00
|
|
|
)
|
2022-01-22 16:58:32 +01:00
|
|
|
.await?;
|
2020-08-14 11:29:32 +02:00
|
|
|
|
|
|
|
Ok(get_public_rooms::v1::Response {
|
2022-02-18 11:52:00 +01:00
|
|
|
chunk: response.chunk,
|
2020-09-14 11:42:16 +02:00
|
|
|
prev_batch: response.prev_batch,
|
|
|
|
next_batch: response.next_batch,
|
|
|
|
total_room_count_estimate: response.total_room_count_estimate,
|
2022-01-22 16:58:32 +01:00
|
|
|
})
|
2020-08-14 11:29:32 +02:00
|
|
|
}
|
|
|
|
|
2023-02-20 22:59:45 +01:00
|
|
|
pub fn parse_incoming_pdu(
|
|
|
|
pdu: &RawJsonValue,
|
|
|
|
) -> Result<(OwnedEventId, CanonicalJsonObject, OwnedRoomId)> {
|
|
|
|
let value: CanonicalJsonObject = serde_json::from_str(pdu.get()).map_err(|e| {
|
|
|
|
warn!("Error parsing incoming event {:?}: {:?}", pdu, e);
|
|
|
|
Error::BadServerResponse("Invalid PDU in server response")
|
|
|
|
})?;
|
|
|
|
|
|
|
|
let room_id: OwnedRoomId = value
|
|
|
|
.get("room_id")
|
|
|
|
.and_then(|id| RoomId::parse(id.as_str()?).ok())
|
|
|
|
.ok_or(Error::BadRequest(
|
|
|
|
ErrorKind::InvalidParam,
|
|
|
|
"Invalid room id in pdu",
|
|
|
|
))?;
|
|
|
|
|
|
|
|
let room_version_id = services().rooms.state.get_room_version(&room_id)?;
|
|
|
|
|
2023-12-23 19:48:14 -08:00
|
|
|
let (event_id, value) = match gen_event_id_canonical_json(pdu, &room_version_id) {
|
2023-02-20 22:59:45 +01:00
|
|
|
Ok(t) => t,
|
2024-07-06 22:56:08 +01:00
|
|
|
Err(e) => {
|
2023-02-20 22:59:45 +01:00
|
|
|
// Event could not be converted to canonical json
|
2024-07-06 22:56:08 +01:00
|
|
|
return Err(e);
|
2023-02-20 22:59:45 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
Ok((event_id, value, room_id))
|
|
|
|
}
|
|
|
|
|
2024-07-06 22:56:08 +01:00
|
|
|
/// Attempts to parse and append PDU to timeline.
|
|
|
|
/// If no event ID is returned, then the PDU was failed to be parsed.
|
|
|
|
/// If the Ok(()) is returned, then the PDU was successfully appended to the timeline.
|
|
|
|
async fn handle_pdu_in_transaction(
|
|
|
|
origin: &ServerName,
|
|
|
|
pub_key_map: &RwLock<BTreeMap<String, SigningKeys>>,
|
|
|
|
pdu: &RawJsonValue,
|
|
|
|
) -> (Option<OwnedEventId>, Result<()>) {
|
|
|
|
let (event_id, value, room_id) = match parse_incoming_pdu(pdu) {
|
|
|
|
Ok(t) => t,
|
|
|
|
Err(e) => {
|
|
|
|
warn!("Could not parse PDU: {e}");
|
|
|
|
warn!("Full PDU: {:?}", &pdu);
|
|
|
|
return (None, Err(Error::BadServerResponse("Could not parse PDU")));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Makes use of the m.room.create event. If we cannot fetch this event,
|
|
|
|
// we must have never been in that room.
|
|
|
|
if services().rooms.state.get_room_version(&room_id).is_err() {
|
|
|
|
debug!("Room {room_id} is not known to this server");
|
|
|
|
return (
|
|
|
|
Some(event_id),
|
|
|
|
Err(Error::BadServerResponse("Room is not known to this server")),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// We do not add the event_id field to the pdu here because of signature and hashes checks
|
|
|
|
|
|
|
|
let mutex = Arc::clone(
|
|
|
|
services()
|
|
|
|
.globals
|
|
|
|
.roomid_mutex_federation
|
|
|
|
.write()
|
|
|
|
.await
|
|
|
|
.entry(room_id.to_owned())
|
|
|
|
.or_default(),
|
|
|
|
);
|
|
|
|
let mutex_lock = mutex.lock().await;
|
|
|
|
let start_time = Instant::now();
|
|
|
|
|
|
|
|
if let Err(e) = services()
|
|
|
|
.rooms
|
|
|
|
.event_handler
|
|
|
|
.handle_incoming_pdu(origin, &event_id, &room_id, value, true, pub_key_map)
|
|
|
|
.await
|
|
|
|
{
|
|
|
|
warn!("Error appending PDU to timeline: {}: {:?}", e, pdu);
|
|
|
|
return (Some(event_id), Err(e));
|
|
|
|
}
|
|
|
|
|
|
|
|
drop(mutex_lock);
|
|
|
|
|
|
|
|
let elapsed = start_time.elapsed();
|
|
|
|
debug!(
|
|
|
|
"Handling transaction of event {} took {}m{}s",
|
|
|
|
event_id,
|
|
|
|
elapsed.as_secs() / 60,
|
|
|
|
elapsed.as_secs() % 60
|
|
|
|
);
|
|
|
|
|
|
|
|
(Some(event_id), Ok(()))
|
|
|
|
}
|
|
|
|
|
2021-08-31 19:14:37 +02:00
|
|
|
/// # `PUT /_matrix/federation/v1/send/{txnId}`
|
|
|
|
///
|
|
|
|
/// Push EDUs and PDUs to this server.
|
2021-06-08 18:10:00 +02:00
|
|
|
pub async fn send_transaction_message_route(
|
2022-12-14 13:09:10 +01:00
|
|
|
body: Ruma<send_transaction_message::v1::Request>,
|
2022-01-22 16:58:32 +01:00
|
|
|
) -> Result<send_transaction_message::v1::Response> {
|
2022-04-06 19:08:23 +02:00
|
|
|
let sender_servername = body
|
|
|
|
.sender_servername
|
|
|
|
.as_ref()
|
|
|
|
.expect("server is authenticated");
|
|
|
|
|
2021-02-01 17:02:56 -05:00
|
|
|
let mut resolved_map = BTreeMap::new();
|
|
|
|
|
2021-04-13 21:34:31 +02:00
|
|
|
let pub_key_map = RwLock::new(BTreeMap::new());
|
2021-03-26 11:10:45 +01:00
|
|
|
|
|
|
|
// This is all the auth_events that have been recursively fetched so they don't have to be
|
|
|
|
// deserialized over and over again.
|
|
|
|
// TODO: make this persist across requests but not in a DB Tree (in globals?)
|
|
|
|
// TODO: This could potentially also be some sort of trie (suffix tree) like structure so
|
|
|
|
// that once an auth event is known it would know (using indexes maybe) all of the auth
|
|
|
|
// events that it references.
|
2021-06-29 20:18:52 -04:00
|
|
|
// let mut auth_cache = EventMap::new();
|
2021-03-26 11:10:45 +01:00
|
|
|
|
2021-04-05 21:46:10 +02:00
|
|
|
for pdu in &body.pdus {
|
2024-07-06 22:56:08 +01:00
|
|
|
let (event_id, result) =
|
|
|
|
handle_pdu_in_transaction(sender_servername, &pub_key_map, pdu).await;
|
2023-08-11 10:48:48 +02:00
|
|
|
|
2024-07-06 22:56:08 +01:00
|
|
|
if let Some(event_id) = event_id {
|
|
|
|
resolved_map.insert(event_id.clone(), result.map_err(|e| e.sanitized_error()));
|
2021-03-26 11:10:45 +01:00
|
|
|
}
|
2021-03-25 23:55:40 +01:00
|
|
|
}
|
|
|
|
|
2021-05-12 20:04:28 +02:00
|
|
|
for edu in body
|
|
|
|
.edus
|
|
|
|
.iter()
|
2021-06-11 15:47:53 -04:00
|
|
|
.filter_map(|edu| serde_json::from_str::<Edu>(edu.json().get()).ok())
|
2021-05-12 20:04:28 +02:00
|
|
|
{
|
|
|
|
match edu {
|
|
|
|
Edu::Presence(_) => {}
|
|
|
|
Edu::Receipt(receipt) => {
|
|
|
|
for (room_id, room_updates) in receipt.receipts {
|
|
|
|
for (user_id, user_updates) in room_updates.read {
|
2024-06-11 16:06:30 +02:00
|
|
|
if user_id.server_name() == sender_servername {
|
|
|
|
if let Some((event_id, _)) = user_updates
|
|
|
|
.event_ids
|
|
|
|
.iter()
|
|
|
|
.filter_map(|id| {
|
|
|
|
services()
|
|
|
|
.rooms
|
|
|
|
.timeline
|
|
|
|
.get_pdu_count(id)
|
|
|
|
.ok()
|
|
|
|
.flatten()
|
|
|
|
.map(|r| (id, r))
|
|
|
|
})
|
|
|
|
.max_by_key(|(_, count)| *count)
|
|
|
|
{
|
|
|
|
let mut user_receipts = BTreeMap::new();
|
|
|
|
user_receipts.insert(user_id.clone(), user_updates.data);
|
|
|
|
|
|
|
|
let mut receipts = BTreeMap::new();
|
|
|
|
receipts.insert(ReceiptType::Read, user_receipts);
|
|
|
|
|
|
|
|
let mut receipt_content = BTreeMap::new();
|
|
|
|
receipt_content.insert(event_id.to_owned(), receipts);
|
|
|
|
|
|
|
|
let event = ReceiptEvent {
|
|
|
|
content: ReceiptEventContent(receipt_content),
|
|
|
|
room_id: room_id.clone(),
|
|
|
|
};
|
2022-10-05 20:34:31 +02:00
|
|
|
services()
|
|
|
|
.rooms
|
2024-06-11 16:06:30 +02:00
|
|
|
.edus
|
|
|
|
.read_receipt
|
|
|
|
.readreceipt_update(&user_id, &room_id, event)?;
|
|
|
|
} else {
|
|
|
|
// TODO fetch missing events
|
|
|
|
debug!("No known event ids in read receipt: {:?}", user_updates);
|
|
|
|
}
|
2021-05-12 20:04:28 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Edu::Typing(typing) => {
|
2024-06-11 16:06:30 +02:00
|
|
|
if typing.user_id.server_name() == sender_servername
|
|
|
|
&& services()
|
|
|
|
.rooms
|
|
|
|
.state_cache
|
|
|
|
.is_joined(&typing.user_id, &typing.room_id)?
|
2022-10-05 20:34:31 +02:00
|
|
|
{
|
2022-03-23 11:05:41 +01:00
|
|
|
if typing.typing {
|
2024-03-22 08:22:15 +01:00
|
|
|
services()
|
|
|
|
.rooms
|
|
|
|
.edus
|
|
|
|
.typing
|
|
|
|
.typing_add(
|
|
|
|
&typing.user_id,
|
|
|
|
&typing.room_id,
|
|
|
|
3000 + utils::millis_since_unix_epoch(),
|
|
|
|
)
|
|
|
|
.await?;
|
2022-03-23 11:05:41 +01:00
|
|
|
} else {
|
2022-10-05 20:34:31 +02:00
|
|
|
services()
|
|
|
|
.rooms
|
|
|
|
.edus
|
|
|
|
.typing
|
2024-03-22 08:22:15 +01:00
|
|
|
.typing_remove(&typing.user_id, &typing.room_id)
|
|
|
|
.await?;
|
2022-03-23 11:05:41 +01:00
|
|
|
}
|
2021-05-12 20:04:28 +02:00
|
|
|
}
|
|
|
|
}
|
2021-08-24 19:10:31 +02:00
|
|
|
Edu::DeviceListUpdate(DeviceListUpdateContent { user_id, .. }) => {
|
2024-06-11 16:06:30 +02:00
|
|
|
if user_id.server_name() == sender_servername {
|
|
|
|
services().users.mark_device_key_update(&user_id)?;
|
|
|
|
}
|
2021-05-28 13:44:40 +02:00
|
|
|
}
|
|
|
|
Edu::DirectToDevice(DirectDeviceContent {
|
|
|
|
sender,
|
|
|
|
ev_type,
|
|
|
|
message_id,
|
|
|
|
messages,
|
|
|
|
}) => {
|
2024-06-11 16:06:30 +02:00
|
|
|
if sender.server_name() == sender_servername
|
|
|
|
// Check if this is a new transaction id
|
|
|
|
&& services()
|
|
|
|
.transaction_ids
|
|
|
|
.existing_txnid(&sender, None, &message_id)?
|
|
|
|
.is_none()
|
2021-05-28 13:44:40 +02:00
|
|
|
{
|
2024-06-11 16:06:30 +02:00
|
|
|
for (target_user_id, map) in &messages {
|
|
|
|
for (target_device_id_maybe, event) in map {
|
|
|
|
match target_device_id_maybe {
|
|
|
|
DeviceIdOrAllDevices::DeviceId(target_device_id) => {
|
2022-09-06 23:15:09 +02:00
|
|
|
services().users.add_to_device_event(
|
2021-05-28 13:44:40 +02:00
|
|
|
&sender,
|
2021-09-13 19:45:56 +02:00
|
|
|
target_user_id,
|
2024-06-11 16:06:30 +02:00
|
|
|
target_device_id,
|
2021-06-17 20:12:36 +02:00
|
|
|
&ev_type.to_string(),
|
2024-06-11 16:06:30 +02:00
|
|
|
event.deserialize_as().map_err(|e| {
|
|
|
|
warn!("To-Device event is invalid: {event:?} {e}");
|
2021-05-28 13:44:40 +02:00
|
|
|
Error::BadRequest(
|
|
|
|
ErrorKind::InvalidParam,
|
|
|
|
"Event is invalid",
|
|
|
|
)
|
|
|
|
})?,
|
2024-06-11 16:06:30 +02:00
|
|
|
)?
|
|
|
|
}
|
|
|
|
|
|
|
|
DeviceIdOrAllDevices::AllDevices => {
|
|
|
|
for target_device_id in
|
|
|
|
services().users.all_device_ids(target_user_id)
|
|
|
|
{
|
|
|
|
services().users.add_to_device_event(
|
|
|
|
&sender,
|
|
|
|
target_user_id,
|
|
|
|
&target_device_id?,
|
|
|
|
&ev_type.to_string(),
|
|
|
|
event.deserialize_as().map_err(|_| {
|
|
|
|
Error::BadRequest(
|
|
|
|
ErrorKind::InvalidParam,
|
|
|
|
"Event is invalid",
|
|
|
|
)
|
|
|
|
})?,
|
|
|
|
)?;
|
|
|
|
}
|
2021-05-28 13:44:40 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-11 16:06:30 +02:00
|
|
|
// Save transaction id with empty data
|
|
|
|
services()
|
|
|
|
.transaction_ids
|
|
|
|
.add_txnid(&sender, None, &message_id, &[])?;
|
|
|
|
}
|
2021-05-28 13:44:40 +02:00
|
|
|
}
|
2022-03-05 10:16:21 +08:00
|
|
|
Edu::SigningKeyUpdate(SigningKeyUpdateContent {
|
|
|
|
user_id,
|
|
|
|
master_key,
|
|
|
|
self_signing_key,
|
|
|
|
}) => {
|
2024-06-11 16:06:30 +02:00
|
|
|
if user_id.server_name() == sender_servername {
|
|
|
|
if let Some(master_key) = master_key {
|
|
|
|
services().users.add_cross_signing_keys(
|
|
|
|
&user_id,
|
|
|
|
&master_key,
|
|
|
|
&self_signing_key,
|
|
|
|
&None,
|
|
|
|
true,
|
|
|
|
)?;
|
|
|
|
}
|
2022-03-05 10:16:21 +08:00
|
|
|
}
|
|
|
|
}
|
2021-05-12 20:04:28 +02:00
|
|
|
Edu::_Custom(_) => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-06 22:56:08 +01:00
|
|
|
Ok(send_transaction_message::v1::Response { pdus: resolved_map })
|
2021-01-03 17:26:17 -05:00
|
|
|
}
|
2021-01-15 15:46:47 -05:00
|
|
|
|
2021-08-31 19:14:37 +02:00
|
|
|
/// # `GET /_matrix/federation/v1/event/{eventId}`
|
|
|
|
///
|
|
|
|
/// Retrieves a single event from the server.
|
|
|
|
///
|
|
|
|
/// - Only works if a user of this server is currently invited or joined the room
|
2022-01-20 11:51:31 +01:00
|
|
|
pub async fn get_event_route(
|
2022-12-14 13:09:10 +01:00
|
|
|
body: Ruma<get_event::v1::Request>,
|
2022-01-22 16:58:32 +01:00
|
|
|
) -> Result<get_event::v1::Response> {
|
2021-08-31 19:14:37 +02:00
|
|
|
let sender_servername = body
|
|
|
|
.sender_servername
|
|
|
|
.as_ref()
|
|
|
|
.expect("server is authenticated");
|
|
|
|
|
2022-09-06 23:15:09 +02:00
|
|
|
let event = services()
|
2022-10-05 20:34:31 +02:00
|
|
|
.rooms
|
|
|
|
.timeline
|
2021-08-31 19:14:37 +02:00
|
|
|
.get_pdu_json(&body.event_id)?
|
2023-07-29 19:17:12 +00:00
|
|
|
.ok_or_else(|| {
|
2023-07-28 23:40:10 +00:00
|
|
|
warn!("Event not found, event ID: {:?}", &body.event_id);
|
2023-07-29 14:30:48 +00:00
|
|
|
Error::BadRequest(ErrorKind::NotFound, "Event not found.")
|
|
|
|
})?;
|
2021-08-31 19:14:37 +02:00
|
|
|
|
|
|
|
let room_id_str = event
|
|
|
|
.get("room_id")
|
|
|
|
.and_then(|val| val.as_str())
|
|
|
|
.ok_or_else(|| Error::bad_database("Invalid event in database"))?;
|
|
|
|
|
2021-11-27 00:30:28 +01:00
|
|
|
let room_id = <&RoomId>::try_from(room_id_str)
|
2021-08-31 19:14:37 +02:00
|
|
|
.map_err(|_| Error::bad_database("Invalid room id field in event in database"))?;
|
|
|
|
|
2022-10-05 20:34:31 +02:00
|
|
|
if !services()
|
|
|
|
.rooms
|
|
|
|
.state_cache
|
|
|
|
.server_in_room(sender_servername, room_id)?
|
|
|
|
{
|
2022-01-17 14:35:38 +01:00
|
|
|
return Err(Error::BadRequest(
|
2024-04-07 20:46:18 +01:00
|
|
|
ErrorKind::forbidden(),
|
2022-01-17 14:35:38 +01:00
|
|
|
"Server is not in room",
|
|
|
|
));
|
2021-08-31 19:14:37 +02:00
|
|
|
}
|
|
|
|
|
2023-02-22 15:49:55 +01:00
|
|
|
if !services().rooms.state_accessor.server_can_see_event(
|
|
|
|
sender_servername,
|
2023-12-23 19:48:14 -08:00
|
|
|
room_id,
|
2023-02-22 15:49:55 +01:00
|
|
|
&body.event_id,
|
|
|
|
)? {
|
|
|
|
return Err(Error::BadRequest(
|
2024-04-07 20:46:18 +01:00
|
|
|
ErrorKind::forbidden(),
|
2023-02-22 15:49:55 +01:00
|
|
|
"Server is not allowed to see event.",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2021-04-07 15:56:57 +02:00
|
|
|
Ok(get_event::v1::Response {
|
2022-09-06 23:15:09 +02:00
|
|
|
origin: services().globals.server_name().to_owned(),
|
2021-05-20 23:46:52 +02:00
|
|
|
origin_server_ts: MilliSecondsSinceUnixEpoch::now(),
|
2021-08-31 19:14:37 +02:00
|
|
|
pdu: PduEvent::convert_to_outgoing_federation_event(event),
|
2022-01-22 16:58:32 +01:00
|
|
|
})
|
2021-04-07 15:56:57 +02:00
|
|
|
}
|
|
|
|
|
2023-02-18 13:20:20 +01:00
|
|
|
/// # `GET /_matrix/federation/v1/backfill/<room_id>`
|
|
|
|
///
|
|
|
|
/// Retrieves events from before the sender joined the room, if the room's
|
|
|
|
/// history visibility allows.
|
|
|
|
pub async fn get_backfill_route(
|
|
|
|
body: Ruma<get_backfill::v1::Request>,
|
|
|
|
) -> Result<get_backfill::v1::Response> {
|
|
|
|
let sender_servername = body
|
|
|
|
.sender_servername
|
|
|
|
.as_ref()
|
|
|
|
.expect("server is authenticated");
|
|
|
|
|
2023-07-31 16:18:23 +02:00
|
|
|
debug!("Got backfill request from: {}", sender_servername);
|
2023-02-18 13:20:20 +01:00
|
|
|
|
|
|
|
if !services()
|
|
|
|
.rooms
|
|
|
|
.state_cache
|
|
|
|
.server_in_room(sender_servername, &body.room_id)?
|
|
|
|
{
|
|
|
|
return Err(Error::BadRequest(
|
2024-04-07 20:46:18 +01:00
|
|
|
ErrorKind::forbidden(),
|
2023-02-18 13:20:20 +01:00
|
|
|
"Server is not in room.",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
services()
|
|
|
|
.rooms
|
|
|
|
.event_handler
|
|
|
|
.acl_check(sender_servername, &body.room_id)?;
|
|
|
|
|
|
|
|
let until = body
|
|
|
|
.v
|
|
|
|
.iter()
|
|
|
|
.map(|eventid| services().rooms.timeline.get_pdu_count(eventid))
|
|
|
|
.filter_map(|r| r.ok().flatten())
|
|
|
|
.max()
|
|
|
|
.ok_or(Error::BadRequest(
|
|
|
|
ErrorKind::InvalidParam,
|
|
|
|
"No known eventid in v",
|
|
|
|
))?;
|
|
|
|
|
|
|
|
let limit = body.limit.min(uint!(100));
|
|
|
|
|
|
|
|
let all_events = services()
|
|
|
|
.rooms
|
|
|
|
.timeline
|
2023-12-23 19:48:14 -08:00
|
|
|
.pdus_until(user_id!("@doesntmatter:conduit.rs"), &body.room_id, until)?
|
2023-02-18 13:20:20 +01:00
|
|
|
.take(limit.try_into().unwrap());
|
|
|
|
|
|
|
|
let events = all_events
|
|
|
|
.filter_map(|r| r.ok())
|
|
|
|
.filter(|(_, e)| {
|
|
|
|
matches!(
|
|
|
|
services().rooms.state_accessor.server_can_see_event(
|
|
|
|
sender_servername,
|
|
|
|
&e.room_id,
|
|
|
|
&e.event_id,
|
|
|
|
),
|
|
|
|
Ok(true),
|
|
|
|
)
|
|
|
|
})
|
2023-02-20 22:59:45 +01:00
|
|
|
.map(|(_, pdu)| services().rooms.timeline.get_pdu_json(&pdu.event_id))
|
2023-02-18 13:20:20 +01:00
|
|
|
.filter_map(|r| r.ok().flatten())
|
2023-12-23 19:48:14 -08:00
|
|
|
.map(PduEvent::convert_to_outgoing_federation_event)
|
2023-02-18 13:20:20 +01:00
|
|
|
.collect();
|
|
|
|
|
|
|
|
Ok(get_backfill::v1::Response {
|
|
|
|
origin: services().globals.server_name().to_owned(),
|
|
|
|
origin_server_ts: MilliSecondsSinceUnixEpoch::now(),
|
|
|
|
pdus: events,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-08-31 19:14:37 +02:00
|
|
|
/// # `POST /_matrix/federation/v1/get_missing_events/{roomId}`
|
|
|
|
///
|
|
|
|
/// Retrieves events that the sender is missing.
|
2022-01-20 11:51:31 +01:00
|
|
|
pub async fn get_missing_events_route(
|
2022-12-14 13:09:10 +01:00
|
|
|
body: Ruma<get_missing_events::v1::Request>,
|
2022-01-22 16:58:32 +01:00
|
|
|
) -> Result<get_missing_events::v1::Response> {
|
2021-08-31 19:14:37 +02:00
|
|
|
let sender_servername = body
|
|
|
|
.sender_servername
|
|
|
|
.as_ref()
|
|
|
|
.expect("server is authenticated");
|
|
|
|
|
2022-10-05 20:34:31 +02:00
|
|
|
if !services()
|
|
|
|
.rooms
|
|
|
|
.state_cache
|
|
|
|
.server_in_room(sender_servername, &body.room_id)?
|
|
|
|
{
|
2021-08-31 19:14:37 +02:00
|
|
|
return Err(Error::BadRequest(
|
2024-04-07 20:46:18 +01:00
|
|
|
ErrorKind::forbidden(),
|
2021-08-31 19:14:37 +02:00
|
|
|
"Server is not in room",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2022-10-05 20:34:31 +02:00
|
|
|
services()
|
|
|
|
.rooms
|
|
|
|
.event_handler
|
2022-10-10 14:09:11 +02:00
|
|
|
.acl_check(sender_servername, &body.room_id)?;
|
2022-01-17 14:35:38 +01:00
|
|
|
|
2020-09-25 12:26:29 +02:00
|
|
|
let mut queued_events = body.latest_events.clone();
|
|
|
|
let mut events = Vec::new();
|
|
|
|
|
|
|
|
let mut i = 0;
|
|
|
|
while i < queued_events.len() && events.len() < u64::from(body.limit) as usize {
|
2022-10-05 09:34:25 +02:00
|
|
|
if let Some(pdu) = services().rooms.timeline.get_pdu_json(&queued_events[i])? {
|
2021-08-31 19:14:37 +02:00
|
|
|
let room_id_str = pdu
|
|
|
|
.get("room_id")
|
|
|
|
.and_then(|val| val.as_str())
|
|
|
|
.ok_or_else(|| Error::bad_database("Invalid event in database"))?;
|
|
|
|
|
2021-11-27 00:30:28 +01:00
|
|
|
let event_room_id = <&RoomId>::try_from(room_id_str)
|
2021-08-31 19:14:37 +02:00
|
|
|
.map_err(|_| Error::bad_database("Invalid room id field in event in database"))?;
|
|
|
|
|
|
|
|
if event_room_id != body.room_id {
|
|
|
|
warn!(
|
|
|
|
"Evil event detected: Event {} found while searching in room {}",
|
|
|
|
queued_events[i], body.room_id
|
|
|
|
);
|
|
|
|
return Err(Error::BadRequest(
|
|
|
|
ErrorKind::InvalidParam,
|
|
|
|
"Evil event detected",
|
|
|
|
));
|
|
|
|
}
|
2021-04-14 10:43:31 +02:00
|
|
|
|
2021-08-31 19:14:37 +02:00
|
|
|
if body.earliest_events.contains(&queued_events[i]) {
|
2020-09-25 12:26:29 +02:00
|
|
|
i += 1;
|
|
|
|
continue;
|
|
|
|
}
|
2023-02-22 15:49:55 +01:00
|
|
|
|
|
|
|
if !services().rooms.state_accessor.server_can_see_event(
|
|
|
|
sender_servername,
|
|
|
|
&body.room_id,
|
|
|
|
&queued_events[i],
|
|
|
|
)? {
|
|
|
|
i += 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-09-25 12:26:29 +02:00
|
|
|
queued_events.extend_from_slice(
|
2022-10-09 17:25:06 +02:00
|
|
|
&serde_json::from_value::<Vec<OwnedEventId>>(
|
2021-04-11 21:01:27 +02:00
|
|
|
serde_json::to_value(pdu.get("prev_events").cloned().ok_or_else(|| {
|
|
|
|
Error::bad_database("Event in db has no prev_events field.")
|
|
|
|
})?)
|
|
|
|
.expect("canonical json is valid json value"),
|
2020-09-25 12:26:29 +02:00
|
|
|
)
|
|
|
|
.map_err(|_| Error::bad_database("Invalid prev_events content in pdu in db."))?,
|
|
|
|
);
|
2021-04-11 21:01:27 +02:00
|
|
|
events.push(PduEvent::convert_to_outgoing_federation_event(pdu));
|
2020-09-25 12:26:29 +02:00
|
|
|
}
|
|
|
|
i += 1;
|
|
|
|
}
|
|
|
|
|
2022-01-22 16:58:32 +01:00
|
|
|
Ok(get_missing_events::v1::Response { events })
|
2020-09-25 12:26:29 +02:00
|
|
|
}
|
2020-10-05 22:19:22 +02:00
|
|
|
|
2021-08-31 19:14:37 +02:00
|
|
|
/// # `GET /_matrix/federation/v1/event_auth/{roomId}/{eventId}`
|
|
|
|
///
|
|
|
|
/// Retrieves the auth chain for a given event.
|
|
|
|
///
|
|
|
|
/// - This does not include the event itself
|
2022-01-20 11:51:31 +01:00
|
|
|
pub async fn get_event_authorization_route(
|
2022-12-14 13:09:10 +01:00
|
|
|
body: Ruma<get_event_authorization::v1::Request>,
|
2022-01-22 16:58:32 +01:00
|
|
|
) -> Result<get_event_authorization::v1::Response> {
|
2021-08-31 19:14:37 +02:00
|
|
|
let sender_servername = body
|
|
|
|
.sender_servername
|
|
|
|
.as_ref()
|
|
|
|
.expect("server is authenticated");
|
|
|
|
|
2022-10-05 20:34:31 +02:00
|
|
|
if !services()
|
|
|
|
.rooms
|
|
|
|
.state_cache
|
|
|
|
.server_in_room(sender_servername, &body.room_id)?
|
|
|
|
{
|
2022-01-17 14:35:38 +01:00
|
|
|
return Err(Error::BadRequest(
|
2024-04-07 20:46:18 +01:00
|
|
|
ErrorKind::forbidden(),
|
2022-01-17 14:35:38 +01:00
|
|
|
"Server is not in room.",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2022-10-05 20:34:31 +02:00
|
|
|
services()
|
|
|
|
.rooms
|
|
|
|
.event_handler
|
2022-10-10 14:09:11 +02:00
|
|
|
.acl_check(sender_servername, &body.room_id)?;
|
2022-01-17 14:35:38 +01:00
|
|
|
|
2022-09-06 23:15:09 +02:00
|
|
|
let event = services()
|
2022-10-05 20:34:31 +02:00
|
|
|
.rooms
|
|
|
|
.timeline
|
2021-08-31 19:14:37 +02:00
|
|
|
.get_pdu_json(&body.event_id)?
|
2023-07-29 19:17:12 +00:00
|
|
|
.ok_or_else(|| {
|
2023-07-28 23:40:10 +00:00
|
|
|
warn!("Event not found, event ID: {:?}", &body.event_id);
|
2023-07-29 14:30:48 +00:00
|
|
|
Error::BadRequest(ErrorKind::NotFound, "Event not found.")
|
|
|
|
})?;
|
2021-08-31 19:14:37 +02:00
|
|
|
|
|
|
|
let room_id_str = event
|
|
|
|
.get("room_id")
|
|
|
|
.and_then(|val| val.as_str())
|
|
|
|
.ok_or_else(|| Error::bad_database("Invalid event in database"))?;
|
|
|
|
|
2021-11-27 00:30:28 +01:00
|
|
|
let room_id = <&RoomId>::try_from(room_id_str)
|
2021-08-31 19:14:37 +02:00
|
|
|
.map_err(|_| Error::bad_database("Invalid room id field in event in database"))?;
|
|
|
|
|
2022-10-05 20:34:31 +02:00
|
|
|
let auth_chain_ids = services()
|
|
|
|
.rooms
|
|
|
|
.auth_chain
|
|
|
|
.get_auth_chain(room_id, vec![Arc::from(&*body.event_id)])
|
|
|
|
.await?;
|
2021-06-14 11:36:18 +02:00
|
|
|
|
2021-07-18 20:43:39 +02:00
|
|
|
Ok(get_event_authorization::v1::Response {
|
|
|
|
auth_chain: auth_chain_ids
|
2022-10-05 09:34:25 +02:00
|
|
|
.filter_map(|id| services().rooms.timeline.get_pdu_json(&id).ok()?)
|
2021-08-26 17:58:32 -04:00
|
|
|
.map(PduEvent::convert_to_outgoing_federation_event)
|
2021-07-18 20:43:39 +02:00
|
|
|
.collect(),
|
2022-01-22 16:58:32 +01:00
|
|
|
})
|
2021-06-14 11:36:18 +02:00
|
|
|
}
|
|
|
|
|
2021-08-31 19:14:37 +02:00
|
|
|
/// # `GET /_matrix/federation/v1/state/{roomId}`
|
|
|
|
///
|
|
|
|
/// Retrieves the current state of the room.
|
2022-01-20 11:51:31 +01:00
|
|
|
pub async fn get_room_state_route(
|
2022-12-14 13:09:10 +01:00
|
|
|
body: Ruma<get_room_state::v1::Request>,
|
2022-01-22 16:58:32 +01:00
|
|
|
) -> Result<get_room_state::v1::Response> {
|
2021-08-31 19:14:37 +02:00
|
|
|
let sender_servername = body
|
|
|
|
.sender_servername
|
|
|
|
.as_ref()
|
|
|
|
.expect("server is authenticated");
|
|
|
|
|
2022-10-05 20:34:31 +02:00
|
|
|
if !services()
|
|
|
|
.rooms
|
|
|
|
.state_cache
|
|
|
|
.server_in_room(sender_servername, &body.room_id)?
|
|
|
|
{
|
2021-08-31 19:14:37 +02:00
|
|
|
return Err(Error::BadRequest(
|
2024-04-07 20:46:18 +01:00
|
|
|
ErrorKind::forbidden(),
|
2021-08-31 19:14:37 +02:00
|
|
|
"Server is not in room.",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2022-10-05 20:34:31 +02:00
|
|
|
services()
|
|
|
|
.rooms
|
|
|
|
.event_handler
|
2022-10-10 14:09:11 +02:00
|
|
|
.acl_check(sender_servername, &body.room_id)?;
|
2022-01-17 14:35:38 +01:00
|
|
|
|
2022-09-06 23:15:09 +02:00
|
|
|
let shortstatehash = services()
|
2022-10-05 20:34:31 +02:00
|
|
|
.rooms
|
|
|
|
.state_accessor
|
2021-06-14 10:52:27 +02:00
|
|
|
.pdu_shortstatehash(&body.event_id)?
|
|
|
|
.ok_or(Error::BadRequest(
|
|
|
|
ErrorKind::NotFound,
|
|
|
|
"Pdu state not found.",
|
|
|
|
))?;
|
|
|
|
|
2022-09-06 23:15:09 +02:00
|
|
|
let pdus = services()
|
2022-10-05 20:34:31 +02:00
|
|
|
.rooms
|
|
|
|
.state_accessor
|
2022-06-18 16:38:41 +02:00
|
|
|
.state_full_ids(shortstatehash)
|
|
|
|
.await?
|
2022-10-10 14:09:11 +02:00
|
|
|
.into_values()
|
|
|
|
.map(|id| {
|
2021-06-14 10:52:27 +02:00
|
|
|
PduEvent::convert_to_outgoing_federation_event(
|
2022-10-05 20:34:31 +02:00
|
|
|
services()
|
|
|
|
.rooms
|
|
|
|
.timeline
|
|
|
|
.get_pdu_json(&id)
|
|
|
|
.unwrap()
|
|
|
|
.unwrap(),
|
2021-06-14 10:52:27 +02:00
|
|
|
)
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
2022-10-05 20:34:31 +02:00
|
|
|
let auth_chain_ids = services()
|
|
|
|
.rooms
|
|
|
|
.auth_chain
|
|
|
|
.get_auth_chain(&body.room_id, vec![Arc::from(&*body.event_id)])
|
|
|
|
.await?;
|
2021-06-14 10:52:27 +02:00
|
|
|
|
2021-07-18 20:43:39 +02:00
|
|
|
Ok(get_room_state::v1::Response {
|
|
|
|
auth_chain: auth_chain_ids
|
2022-10-11 18:10:51 +02:00
|
|
|
.filter_map(
|
|
|
|
|id| match services().rooms.timeline.get_pdu_json(&id).ok()? {
|
|
|
|
Some(json) => Some(PduEvent::convert_to_outgoing_federation_event(json)),
|
|
|
|
None => {
|
|
|
|
error!("Could not find event json for {id} in db.");
|
|
|
|
None
|
2022-10-11 17:59:49 +02:00
|
|
|
}
|
2022-10-11 18:10:51 +02:00
|
|
|
},
|
|
|
|
)
|
2021-07-18 20:43:39 +02:00
|
|
|
.collect(),
|
|
|
|
pdus,
|
2022-01-22 16:58:32 +01:00
|
|
|
})
|
2021-06-14 10:52:27 +02:00
|
|
|
}
|
|
|
|
|
2021-08-31 19:14:37 +02:00
|
|
|
/// # `GET /_matrix/federation/v1/state_ids/{roomId}`
|
|
|
|
///
|
|
|
|
/// Retrieves the current state of the room.
|
2022-01-20 11:51:31 +01:00
|
|
|
pub async fn get_room_state_ids_route(
|
2022-12-14 13:09:10 +01:00
|
|
|
body: Ruma<get_room_state_ids::v1::Request>,
|
2022-01-22 16:58:32 +01:00
|
|
|
) -> Result<get_room_state_ids::v1::Response> {
|
2021-08-31 19:14:37 +02:00
|
|
|
let sender_servername = body
|
|
|
|
.sender_servername
|
|
|
|
.as_ref()
|
|
|
|
.expect("server is authenticated");
|
|
|
|
|
2022-10-05 20:34:31 +02:00
|
|
|
if !services()
|
|
|
|
.rooms
|
|
|
|
.state_cache
|
|
|
|
.server_in_room(sender_servername, &body.room_id)?
|
|
|
|
{
|
2021-08-31 19:14:37 +02:00
|
|
|
return Err(Error::BadRequest(
|
2024-04-07 20:46:18 +01:00
|
|
|
ErrorKind::forbidden(),
|
2021-08-31 19:14:37 +02:00
|
|
|
"Server is not in room.",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2022-10-05 20:34:31 +02:00
|
|
|
services()
|
|
|
|
.rooms
|
|
|
|
.event_handler
|
2022-10-10 14:09:11 +02:00
|
|
|
.acl_check(sender_servername, &body.room_id)?;
|
2022-01-17 14:35:38 +01:00
|
|
|
|
2022-09-06 23:15:09 +02:00
|
|
|
let shortstatehash = services()
|
2022-10-05 20:34:31 +02:00
|
|
|
.rooms
|
|
|
|
.state_accessor
|
2021-03-18 00:09:57 +01:00
|
|
|
.pdu_shortstatehash(&body.event_id)?
|
|
|
|
.ok_or(Error::BadRequest(
|
|
|
|
ErrorKind::NotFound,
|
|
|
|
"Pdu state not found.",
|
|
|
|
))?;
|
|
|
|
|
2022-09-06 23:15:09 +02:00
|
|
|
let pdu_ids = services()
|
2022-10-05 20:34:31 +02:00
|
|
|
.rooms
|
|
|
|
.state_accessor
|
2022-06-18 16:38:41 +02:00
|
|
|
.state_full_ids(shortstatehash)
|
|
|
|
.await?
|
2022-10-10 14:09:11 +02:00
|
|
|
.into_values()
|
|
|
|
.map(|id| (*id).to_owned())
|
2021-07-01 19:55:26 +02:00
|
|
|
.collect();
|
2021-03-18 00:09:57 +01:00
|
|
|
|
2022-10-05 20:34:31 +02:00
|
|
|
let auth_chain_ids = services()
|
|
|
|
.rooms
|
|
|
|
.auth_chain
|
|
|
|
.get_auth_chain(&body.room_id, vec![Arc::from(&*body.event_id)])
|
|
|
|
.await?;
|
2021-03-18 00:09:57 +01:00
|
|
|
|
|
|
|
Ok(get_room_state_ids::v1::Response {
|
2021-11-26 20:36:40 +01:00
|
|
|
auth_chain_ids: auth_chain_ids.map(|id| (*id).to_owned()).collect(),
|
2021-03-18 00:09:57 +01:00
|
|
|
pdu_ids,
|
2022-01-22 16:58:32 +01:00
|
|
|
})
|
2021-03-18 00:09:57 +01:00
|
|
|
}
|
|
|
|
|
2021-08-31 19:14:37 +02:00
|
|
|
/// # `GET /_matrix/federation/v1/make_join/{roomId}/{userId}`
|
|
|
|
///
|
|
|
|
/// Creates a join template.
|
2022-01-20 11:51:31 +01:00
|
|
|
pub async fn create_join_event_template_route(
|
2022-12-14 13:09:10 +01:00
|
|
|
body: Ruma<prepare_join_event::v1::Request>,
|
2022-02-18 15:33:14 +01:00
|
|
|
) -> Result<prepare_join_event::v1::Response> {
|
2022-10-05 09:34:25 +02:00
|
|
|
if !services().rooms.metadata.exists(&body.room_id)? {
|
2021-04-16 18:18:29 +02:00
|
|
|
return Err(Error::BadRequest(
|
|
|
|
ErrorKind::NotFound,
|
2022-01-17 14:35:38 +01:00
|
|
|
"Room is unknown to this server.",
|
2021-04-16 18:18:29 +02:00
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2022-01-17 14:35:38 +01:00
|
|
|
let sender_servername = body
|
|
|
|
.sender_servername
|
|
|
|
.as_ref()
|
|
|
|
.expect("server is authenticated");
|
|
|
|
|
2022-10-05 20:34:31 +02:00
|
|
|
services()
|
|
|
|
.rooms
|
|
|
|
.event_handler
|
2022-10-10 14:09:11 +02:00
|
|
|
.acl_check(sender_servername, &body.room_id)?;
|
2022-09-06 23:15:09 +02:00
|
|
|
|
|
|
|
let mutex_state = Arc::clone(
|
2022-10-05 20:34:31 +02:00
|
|
|
services()
|
|
|
|
.globals
|
2022-09-06 23:15:09 +02:00
|
|
|
.roomid_mutex_state
|
|
|
|
.write()
|
2024-03-05 14:22:54 +00:00
|
|
|
.await
|
2022-09-06 23:15:09 +02:00
|
|
|
.entry(body.room_id.to_owned())
|
|
|
|
.or_default(),
|
|
|
|
);
|
|
|
|
let state_lock = mutex_state.lock().await;
|
2022-01-17 14:35:38 +01:00
|
|
|
|
2022-01-18 16:53:25 +01:00
|
|
|
// TODO: Conduit does not implement restricted join rules yet, we always reject
|
2022-10-05 20:34:31 +02:00
|
|
|
let join_rules_event = services().rooms.state_accessor.room_state_get(
|
|
|
|
&body.room_id,
|
|
|
|
&StateEventType::RoomJoinRules,
|
|
|
|
"",
|
|
|
|
)?;
|
2022-01-18 16:53:25 +01:00
|
|
|
|
|
|
|
let join_rules_event_content: Option<RoomJoinRulesEventContent> = join_rules_event
|
|
|
|
.as_ref()
|
|
|
|
.map(|join_rules_event| {
|
|
|
|
serde_json::from_str(join_rules_event.content.get()).map_err(|e| {
|
|
|
|
warn!("Invalid join rules event: {}", e);
|
|
|
|
Error::bad_database("Invalid join rules event in db.")
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.transpose()?;
|
|
|
|
|
|
|
|
if let Some(join_rules_event_content) = join_rules_event_content {
|
|
|
|
if matches!(
|
|
|
|
join_rules_event_content.join_rule,
|
2022-10-11 11:53:13 +02:00
|
|
|
JoinRule::Restricted { .. } | JoinRule::KnockRestricted { .. }
|
2022-01-18 16:53:25 +01:00
|
|
|
) {
|
|
|
|
return Err(Error::BadRequest(
|
2022-10-11 11:53:13 +02:00
|
|
|
ErrorKind::UnableToAuthorizeJoin,
|
2022-01-18 16:53:25 +01:00
|
|
|
"Conduit does not support restricted rooms yet.",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-05 09:34:25 +02:00
|
|
|
let room_version_id = services().rooms.state.get_room_version(&body.room_id)?;
|
|
|
|
if !body.ver.contains(&room_version_id) {
|
2021-07-21 11:29:13 +02:00
|
|
|
return Err(Error::BadRequest(
|
|
|
|
ErrorKind::IncompatibleRoomVersion {
|
|
|
|
room_version: room_version_id,
|
|
|
|
},
|
|
|
|
"Room version not supported.",
|
|
|
|
));
|
|
|
|
}
|
2021-04-16 18:18:29 +02:00
|
|
|
|
2021-10-13 10:16:45 +02:00
|
|
|
let content = to_raw_value(&RoomMemberEventContent {
|
2021-04-16 18:18:29 +02:00
|
|
|
avatar_url: None,
|
2021-07-15 23:17:58 +02:00
|
|
|
blurhash: None,
|
2021-04-16 18:18:29 +02:00
|
|
|
displayname: None,
|
|
|
|
is_direct: None,
|
|
|
|
membership: MembershipState::Join,
|
|
|
|
third_party_invite: None,
|
2021-08-19 11:01:18 +02:00
|
|
|
reason: None,
|
2021-11-27 17:44:52 +01:00
|
|
|
join_authorized_via_users_server: None,
|
2021-04-16 18:18:29 +02:00
|
|
|
})
|
|
|
|
.expect("member event is valid value");
|
|
|
|
|
2022-10-12 10:57:54 +02:00
|
|
|
let (_pdu, mut pdu_json) = services().rooms.timeline.create_hash_and_sign_event(
|
2022-10-05 20:34:31 +02:00
|
|
|
PduBuilder {
|
2023-02-26 16:29:06 +01:00
|
|
|
event_type: TimelineEventType::RoomMember,
|
2022-10-05 20:34:31 +02:00
|
|
|
content,
|
|
|
|
unsigned: None,
|
|
|
|
state_key: Some(body.user_id.to_string()),
|
|
|
|
redacts: None,
|
2024-06-22 11:50:39 +01:00
|
|
|
timestamp: None,
|
2022-10-05 20:34:31 +02:00
|
|
|
},
|
|
|
|
&body.user_id,
|
|
|
|
&body.room_id,
|
|
|
|
&state_lock,
|
|
|
|
)?;
|
2021-04-16 18:18:29 +02:00
|
|
|
|
2022-09-06 23:15:09 +02:00
|
|
|
drop(state_lock);
|
2021-04-16 18:18:29 +02:00
|
|
|
|
2022-10-12 10:57:54 +02:00
|
|
|
pdu_json.remove("event_id");
|
|
|
|
|
2022-02-18 15:33:14 +01:00
|
|
|
Ok(prepare_join_event::v1::Response {
|
2021-07-21 11:29:13 +02:00
|
|
|
room_version: Some(room_version_id),
|
2021-10-13 10:16:45 +02:00
|
|
|
event: to_raw_value(&pdu_json).expect("CanonicalJson can be serialized to JSON"),
|
2022-01-22 16:58:32 +01:00
|
|
|
})
|
2021-04-16 18:18:29 +02:00
|
|
|
}
|
|
|
|
|
2021-07-25 19:28:54 +02:00
|
|
|
async fn create_join_event(
|
2022-01-17 14:35:38 +01:00
|
|
|
sender_servername: &ServerName,
|
2021-07-25 19:28:54 +02:00
|
|
|
room_id: &RoomId,
|
2021-10-13 10:16:45 +02:00
|
|
|
pdu: &RawJsonValue,
|
2023-02-26 16:29:06 +01:00
|
|
|
) -> Result<create_join_event::v1::RoomState> {
|
2022-10-05 09:34:25 +02:00
|
|
|
if !services().rooms.metadata.exists(room_id)? {
|
2022-01-17 14:35:38 +01:00
|
|
|
return Err(Error::BadRequest(
|
|
|
|
ErrorKind::NotFound,
|
|
|
|
"Room is unknown to this server.",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2022-10-05 20:34:31 +02:00
|
|
|
services()
|
|
|
|
.rooms
|
|
|
|
.event_handler
|
2022-10-10 14:09:11 +02:00
|
|
|
.acl_check(sender_servername, room_id)?;
|
2022-01-17 14:35:38 +01:00
|
|
|
|
2022-01-18 16:53:25 +01:00
|
|
|
// TODO: Conduit does not implement restricted join rules yet, we always reject
|
2022-10-05 20:34:31 +02:00
|
|
|
let join_rules_event = services().rooms.state_accessor.room_state_get(
|
|
|
|
room_id,
|
|
|
|
&StateEventType::RoomJoinRules,
|
|
|
|
"",
|
|
|
|
)?;
|
2022-01-18 16:53:25 +01:00
|
|
|
|
|
|
|
let join_rules_event_content: Option<RoomJoinRulesEventContent> = join_rules_event
|
|
|
|
.as_ref()
|
|
|
|
.map(|join_rules_event| {
|
|
|
|
serde_json::from_str(join_rules_event.content.get()).map_err(|e| {
|
|
|
|
warn!("Invalid join rules event: {}", e);
|
|
|
|
Error::bad_database("Invalid join rules event in db.")
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.transpose()?;
|
|
|
|
|
|
|
|
if let Some(join_rules_event_content) = join_rules_event_content {
|
|
|
|
if matches!(
|
|
|
|
join_rules_event_content.join_rule,
|
2022-10-11 11:53:13 +02:00
|
|
|
JoinRule::Restricted { .. } | JoinRule::KnockRestricted { .. }
|
2022-01-18 16:53:25 +01:00
|
|
|
) {
|
|
|
|
return Err(Error::BadRequest(
|
2022-10-11 11:53:13 +02:00
|
|
|
ErrorKind::UnableToAuthorizeJoin,
|
2022-01-18 16:53:25 +01:00
|
|
|
"Conduit does not support restricted rooms yet.",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-16 18:18:29 +02:00
|
|
|
// We need to return the state prior to joining, let's keep a reference to that here
|
2022-09-06 23:15:09 +02:00
|
|
|
let shortstatehash = services()
|
2022-10-05 20:34:31 +02:00
|
|
|
.rooms
|
|
|
|
.state
|
2022-10-05 09:34:25 +02:00
|
|
|
.get_room_shortstatehash(room_id)?
|
2021-07-25 19:28:54 +02:00
|
|
|
.ok_or(Error::BadRequest(
|
|
|
|
ErrorKind::NotFound,
|
|
|
|
"Pdu state not found.",
|
|
|
|
))?;
|
2021-04-16 18:18:29 +02:00
|
|
|
|
|
|
|
let pub_key_map = RwLock::new(BTreeMap::new());
|
2021-06-29 20:18:52 -04:00
|
|
|
// let mut auth_cache = EventMap::new();
|
2021-04-16 18:18:29 +02:00
|
|
|
|
|
|
|
// We do not add the event_id field to the pdu here because of signature and hashes checks
|
2022-11-27 23:25:42 +01:00
|
|
|
let room_version_id = services().rooms.state.get_room_version(room_id)?;
|
|
|
|
let (event_id, value) = match gen_event_id_canonical_json(pdu, &room_version_id) {
|
2021-04-16 18:18:29 +02:00
|
|
|
Ok(t) => t,
|
|
|
|
Err(_) => {
|
|
|
|
// Event could not be converted to canonical json
|
|
|
|
return Err(Error::BadRequest(
|
|
|
|
ErrorKind::InvalidParam,
|
|
|
|
"Could not convert event to canonical json.",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-10-09 17:25:06 +02:00
|
|
|
let origin: OwnedServerName = serde_json::from_value(
|
2021-04-16 18:18:29 +02:00
|
|
|
serde_json::to_value(value.get("origin").ok_or(Error::BadRequest(
|
|
|
|
ErrorKind::InvalidParam,
|
|
|
|
"Event needs an origin field.",
|
|
|
|
))?)
|
|
|
|
.expect("CanonicalJson is valid json value"),
|
|
|
|
)
|
|
|
|
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Origin field is invalid."))?;
|
|
|
|
|
2021-07-01 19:55:26 +02:00
|
|
|
let mutex = Arc::clone(
|
2022-10-05 20:34:31 +02:00
|
|
|
services()
|
|
|
|
.globals
|
2021-07-13 15:44:25 +02:00
|
|
|
.roomid_mutex_federation
|
2021-07-01 19:55:26 +02:00
|
|
|
.write()
|
2024-03-05 14:22:54 +00:00
|
|
|
.await
|
2021-11-26 20:36:40 +01:00
|
|
|
.entry(room_id.to_owned())
|
2021-07-01 19:55:26 +02:00
|
|
|
.or_default(),
|
|
|
|
);
|
|
|
|
let mutex_lock = mutex.lock().await;
|
2022-10-05 20:34:31 +02:00
|
|
|
let pdu_id: Vec<u8> = services()
|
|
|
|
.rooms
|
|
|
|
.event_handler
|
|
|
|
.handle_incoming_pdu(&origin, &event_id, room_id, value, true, &pub_key_map)
|
2022-10-05 09:34:25 +02:00
|
|
|
.await?
|
2021-07-25 19:28:54 +02:00
|
|
|
.ok_or(Error::BadRequest(
|
2021-04-16 18:18:29 +02:00
|
|
|
ErrorKind::InvalidParam,
|
2021-07-25 19:28:54 +02:00
|
|
|
"Could not accept incoming PDU as timeline event.",
|
|
|
|
))?;
|
2021-07-01 19:55:26 +02:00
|
|
|
drop(mutex_lock);
|
2021-04-16 18:18:29 +02:00
|
|
|
|
2022-10-05 20:34:31 +02:00
|
|
|
let state_ids = services()
|
|
|
|
.rooms
|
|
|
|
.state_accessor
|
|
|
|
.state_full_ids(shortstatehash)
|
|
|
|
.await?;
|
|
|
|
let auth_chain_ids = services()
|
|
|
|
.rooms
|
|
|
|
.auth_chain
|
2022-10-10 14:09:11 +02:00
|
|
|
.get_auth_chain(room_id, state_ids.values().cloned().collect())
|
2022-10-05 20:34:31 +02:00
|
|
|
.await?;
|
2021-04-16 18:18:29 +02:00
|
|
|
|
2022-09-06 23:15:09 +02:00
|
|
|
let servers = services()
|
2021-04-16 18:18:29 +02:00
|
|
|
.rooms
|
2022-10-05 09:34:25 +02:00
|
|
|
.state_cache
|
2021-09-13 19:45:56 +02:00
|
|
|
.room_servers(room_id)
|
2021-04-16 18:18:29 +02:00
|
|
|
.filter_map(|r| r.ok())
|
2022-09-06 23:15:09 +02:00
|
|
|
.filter(|server| &**server != services().globals.server_name());
|
2021-04-16 18:18:29 +02:00
|
|
|
|
2022-09-06 23:15:09 +02:00
|
|
|
services().sending.send_pdu(servers, &pdu_id)?;
|
2021-07-14 07:07:08 +00:00
|
|
|
|
2023-02-26 16:29:06 +01:00
|
|
|
Ok(create_join_event::v1::RoomState {
|
2021-07-25 19:28:54 +02:00
|
|
|
auth_chain: auth_chain_ids
|
2022-10-05 09:34:25 +02:00
|
|
|
.filter_map(|id| services().rooms.timeline.get_pdu_json(&id).ok().flatten())
|
2021-07-25 19:28:54 +02:00
|
|
|
.map(PduEvent::convert_to_outgoing_federation_event)
|
|
|
|
.collect(),
|
|
|
|
state: state_ids
|
|
|
|
.iter()
|
2022-10-05 09:34:25 +02:00
|
|
|
.filter_map(|(_, id)| services().rooms.timeline.get_pdu_json(id).ok().flatten())
|
2021-07-25 19:28:54 +02:00
|
|
|
.map(PduEvent::convert_to_outgoing_federation_event)
|
|
|
|
.collect(),
|
2022-11-09 18:46:10 +01:00
|
|
|
event: None, // TODO: handle restricted joins
|
2021-07-25 19:28:54 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-08-31 19:14:37 +02:00
|
|
|
/// # `PUT /_matrix/federation/v1/send_join/{roomId}/{eventId}`
|
|
|
|
///
|
|
|
|
/// Submits a signed join event.
|
2021-07-25 19:28:54 +02:00
|
|
|
pub async fn create_join_event_v1_route(
|
2022-12-14 13:09:10 +01:00
|
|
|
body: Ruma<create_join_event::v1::Request>,
|
2022-01-22 16:58:32 +01:00
|
|
|
) -> Result<create_join_event::v1::Response> {
|
2022-01-17 14:35:38 +01:00
|
|
|
let sender_servername = body
|
|
|
|
.sender_servername
|
|
|
|
.as_ref()
|
|
|
|
.expect("server is authenticated");
|
|
|
|
|
2022-09-06 23:15:09 +02:00
|
|
|
let room_state = create_join_event(sender_servername, &body.room_id, &body.pdu).await?;
|
2021-07-25 19:28:54 +02:00
|
|
|
|
2022-01-22 16:58:32 +01:00
|
|
|
Ok(create_join_event::v1::Response { room_state })
|
2021-07-25 19:28:54 +02:00
|
|
|
}
|
|
|
|
|
2021-08-31 19:14:37 +02:00
|
|
|
/// # `PUT /_matrix/federation/v2/send_join/{roomId}/{eventId}`
|
|
|
|
///
|
|
|
|
/// Submits a signed join event.
|
2021-07-25 19:28:54 +02:00
|
|
|
pub async fn create_join_event_v2_route(
|
2022-12-14 13:09:10 +01:00
|
|
|
body: Ruma<create_join_event::v2::Request>,
|
2022-01-22 16:58:32 +01:00
|
|
|
) -> Result<create_join_event::v2::Response> {
|
2022-01-17 14:35:38 +01:00
|
|
|
let sender_servername = body
|
|
|
|
.sender_servername
|
|
|
|
.as_ref()
|
|
|
|
.expect("server is authenticated");
|
|
|
|
|
2023-02-26 16:29:06 +01:00
|
|
|
let create_join_event::v1::RoomState {
|
|
|
|
auth_chain,
|
|
|
|
state,
|
|
|
|
event,
|
|
|
|
} = create_join_event(sender_servername, &body.room_id, &body.pdu).await?;
|
|
|
|
let room_state = create_join_event::v2::RoomState {
|
|
|
|
members_omitted: false,
|
|
|
|
auth_chain,
|
|
|
|
state,
|
|
|
|
event,
|
|
|
|
servers_in_room: None,
|
|
|
|
};
|
2021-07-25 19:28:54 +02:00
|
|
|
|
2022-01-22 16:58:32 +01:00
|
|
|
Ok(create_join_event::v2::Response { room_state })
|
2021-04-16 18:18:29 +02:00
|
|
|
}
|
|
|
|
|
2021-08-31 19:14:37 +02:00
|
|
|
/// # `PUT /_matrix/federation/v2/invite/{roomId}/{eventId}`
|
|
|
|
///
|
|
|
|
/// Invites a remote user to a room.
|
2021-06-08 18:10:00 +02:00
|
|
|
pub async fn create_invite_route(
|
2022-12-14 13:09:10 +01:00
|
|
|
body: Ruma<create_invite::v2::Request>,
|
2022-01-22 16:58:32 +01:00
|
|
|
) -> Result<create_invite::v2::Response> {
|
2022-01-17 14:35:38 +01:00
|
|
|
let sender_servername = body
|
|
|
|
.sender_servername
|
|
|
|
.as_ref()
|
|
|
|
.expect("server is authenticated");
|
|
|
|
|
2022-10-05 20:34:31 +02:00
|
|
|
services()
|
|
|
|
.rooms
|
|
|
|
.event_handler
|
2022-10-10 14:09:11 +02:00
|
|
|
.acl_check(sender_servername, &body.room_id)?;
|
2022-01-17 14:35:38 +01:00
|
|
|
|
2022-10-05 20:34:31 +02:00
|
|
|
if !services()
|
|
|
|
.globals
|
|
|
|
.supported_room_versions()
|
|
|
|
.contains(&body.room_version)
|
|
|
|
{
|
2021-04-11 21:01:27 +02:00
|
|
|
return Err(Error::BadRequest(
|
|
|
|
ErrorKind::IncompatibleRoomVersion {
|
|
|
|
room_version: body.room_version.clone(),
|
|
|
|
},
|
|
|
|
"Server does not support this room version.",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut signed_event = utils::to_canonical_object(&body.event)
|
|
|
|
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invite event is invalid."))?;
|
|
|
|
|
|
|
|
ruma::signatures::hash_and_sign_event(
|
2022-09-06 23:15:09 +02:00
|
|
|
services().globals.server_name().as_str(),
|
|
|
|
services().globals.keypair(),
|
2021-04-11 21:01:27 +02:00
|
|
|
&mut signed_event,
|
|
|
|
&body.room_version,
|
|
|
|
)
|
|
|
|
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Failed to sign event."))?;
|
|
|
|
|
2021-04-13 21:34:31 +02:00
|
|
|
// Generate event id
|
2021-11-27 00:30:28 +01:00
|
|
|
let event_id = EventId::parse(format!(
|
2021-04-13 21:34:31 +02:00
|
|
|
"${}",
|
|
|
|
ruma::signatures::reference_hash(&signed_event, &body.room_version)
|
2024-07-07 13:30:53 +01:00
|
|
|
.expect("Event format validated when event was hashed")
|
2021-04-13 21:34:31 +02:00
|
|
|
))
|
|
|
|
.expect("ruma's reference hashes are valid event ids");
|
|
|
|
|
|
|
|
// Add event_id back
|
|
|
|
signed_event.insert(
|
|
|
|
"event_id".to_owned(),
|
2022-10-09 17:25:06 +02:00
|
|
|
CanonicalJsonValue::String(event_id.to_string()),
|
2021-04-13 21:34:31 +02:00
|
|
|
);
|
|
|
|
|
2022-10-09 17:25:06 +02:00
|
|
|
let sender: OwnedUserId = serde_json::from_value(
|
2021-04-26 18:20:20 +02:00
|
|
|
signed_event
|
|
|
|
.get("sender")
|
|
|
|
.ok_or(Error::BadRequest(
|
|
|
|
ErrorKind::InvalidParam,
|
|
|
|
"Event had no sender field.",
|
|
|
|
))?
|
|
|
|
.clone()
|
|
|
|
.into(),
|
2021-04-11 21:01:27 +02:00
|
|
|
)
|
|
|
|
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "sender is not a user id."))?;
|
2021-04-26 18:20:20 +02:00
|
|
|
|
2021-11-26 20:36:40 +01:00
|
|
|
let invited_user: Box<_> = serde_json::from_value(
|
2021-04-26 18:20:20 +02:00
|
|
|
signed_event
|
|
|
|
.get("state_key")
|
|
|
|
.ok_or(Error::BadRequest(
|
|
|
|
ErrorKind::InvalidParam,
|
|
|
|
"Event had no state_key field.",
|
|
|
|
))?
|
|
|
|
.clone()
|
|
|
|
.into(),
|
2021-04-11 21:01:27 +02:00
|
|
|
)
|
|
|
|
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "state_key is not a user id."))?;
|
|
|
|
|
|
|
|
let mut invite_state = body.invite_room_state.clone();
|
|
|
|
|
2021-10-13 11:51:30 +02:00
|
|
|
let mut event: JsonObject = serde_json::from_str(body.event.get())
|
|
|
|
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid invite event bytes."))?;
|
2021-04-11 21:01:27 +02:00
|
|
|
|
|
|
|
event.insert("event_id".to_owned(), "$dummy".into());
|
|
|
|
|
2021-10-13 11:51:30 +02:00
|
|
|
let pdu: PduEvent = serde_json::from_value(event.into()).map_err(|e| {
|
2021-04-13 21:34:31 +02:00
|
|
|
warn!("Invalid invite event: {}", e);
|
|
|
|
Error::BadRequest(ErrorKind::InvalidParam, "Invalid invite event.")
|
|
|
|
})?;
|
|
|
|
|
|
|
|
invite_state.push(pdu.to_stripped_state_event());
|
|
|
|
|
2022-11-27 23:25:42 +01:00
|
|
|
// If we are active in the room, the remote server will notify us about the join via /send
|
|
|
|
if !services()
|
|
|
|
.rooms
|
|
|
|
.state_cache
|
|
|
|
.server_in_room(services().globals.server_name(), &body.room_id)?
|
|
|
|
{
|
2022-10-05 09:34:25 +02:00
|
|
|
services().rooms.state_cache.update_membership(
|
2021-04-13 21:34:31 +02:00
|
|
|
&body.room_id,
|
|
|
|
&invited_user,
|
|
|
|
MembershipState::Invite,
|
|
|
|
&sender,
|
|
|
|
Some(invite_state),
|
2021-08-17 00:22:52 +02:00
|
|
|
true,
|
2021-04-13 21:34:31 +02:00
|
|
|
)?;
|
|
|
|
}
|
2021-04-11 21:01:27 +02:00
|
|
|
|
|
|
|
Ok(create_invite::v2::Response {
|
|
|
|
event: PduEvent::convert_to_outgoing_federation_event(signed_event),
|
2022-01-22 16:58:32 +01:00
|
|
|
})
|
2021-04-11 21:01:27 +02:00
|
|
|
}
|
|
|
|
|
2021-08-31 19:14:37 +02:00
|
|
|
/// # `GET /_matrix/federation/v1/user/devices/{userId}`
|
|
|
|
///
|
|
|
|
/// Gets information on all devices of the user.
|
2022-01-20 11:51:31 +01:00
|
|
|
pub async fn get_devices_route(
|
2022-12-14 13:09:10 +01:00
|
|
|
body: Ruma<get_devices::v1::Request>,
|
2022-01-22 16:58:32 +01:00
|
|
|
) -> Result<get_devices::v1::Response> {
|
2024-02-01 12:05:59 +01:00
|
|
|
if body.user_id.server_name() != services().globals.server_name() {
|
|
|
|
return Err(Error::BadRequest(
|
|
|
|
ErrorKind::InvalidParam,
|
|
|
|
"Tried to access user from other server.",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2022-04-06 18:49:46 +02:00
|
|
|
let sender_servername = body
|
|
|
|
.sender_servername
|
|
|
|
.as_ref()
|
|
|
|
.expect("server is authenticated");
|
|
|
|
|
2021-04-21 10:51:34 +02:00
|
|
|
Ok(get_devices::v1::Response {
|
|
|
|
user_id: body.user_id.clone(),
|
2022-09-06 23:15:09 +02:00
|
|
|
stream_id: services()
|
2021-04-21 10:51:34 +02:00
|
|
|
.users
|
|
|
|
.get_devicelist_version(&body.user_id)?
|
|
|
|
.unwrap_or(0)
|
|
|
|
.try_into()
|
|
|
|
.expect("version will not grow that large"),
|
2022-09-06 23:15:09 +02:00
|
|
|
devices: services()
|
2021-04-21 10:51:34 +02:00
|
|
|
.users
|
|
|
|
.all_devices_metadata(&body.user_id)
|
|
|
|
.filter_map(|r| r.ok())
|
|
|
|
.filter_map(|metadata| {
|
|
|
|
Some(UserDevice {
|
2022-09-06 23:15:09 +02:00
|
|
|
keys: services()
|
2021-04-21 10:51:34 +02:00
|
|
|
.users
|
|
|
|
.get_device_keys(&body.user_id, &metadata.device_id)
|
|
|
|
.ok()??,
|
|
|
|
device_id: metadata.device_id,
|
|
|
|
device_display_name: metadata.display_name,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
.collect(),
|
2023-07-16 16:50:03 +02:00
|
|
|
master_key: services().users.get_master_key(None, &body.user_id, &|u| {
|
|
|
|
u.server_name() == sender_servername
|
|
|
|
})?,
|
2022-09-06 23:15:09 +02:00
|
|
|
self_signing_key: services()
|
2022-03-05 10:16:21 +08:00
|
|
|
.users
|
2023-07-16 16:50:03 +02:00
|
|
|
.get_self_signing_key(None, &body.user_id, &|u| {
|
|
|
|
u.server_name() == sender_servername
|
|
|
|
})?,
|
2022-01-22 16:58:32 +01:00
|
|
|
})
|
2021-04-21 10:51:34 +02:00
|
|
|
}
|
|
|
|
|
2021-08-31 19:14:37 +02:00
|
|
|
/// # `GET /_matrix/federation/v1/query/directory`
|
|
|
|
///
|
|
|
|
/// Resolve a room alias to a room id.
|
2022-01-20 11:51:31 +01:00
|
|
|
pub async fn get_room_information_route(
|
2022-12-14 13:09:10 +01:00
|
|
|
body: Ruma<get_room_information::v1::Request>,
|
2022-01-22 16:58:32 +01:00
|
|
|
) -> Result<get_room_information::v1::Response> {
|
2022-09-06 23:15:09 +02:00
|
|
|
let room_id = services()
|
2021-04-16 18:18:29 +02:00
|
|
|
.rooms
|
2022-10-05 20:34:31 +02:00
|
|
|
.alias
|
|
|
|
.resolve_local_alias(&body.room_alias)?
|
2021-04-21 14:06:39 +02:00
|
|
|
.ok_or(Error::BadRequest(
|
|
|
|
ErrorKind::NotFound,
|
|
|
|
"Room alias not found.",
|
|
|
|
))?;
|
2021-04-16 18:18:29 +02:00
|
|
|
|
|
|
|
Ok(get_room_information::v1::Response {
|
|
|
|
room_id,
|
2022-09-06 23:15:09 +02:00
|
|
|
servers: vec![services().globals.server_name().to_owned()],
|
2022-01-22 16:58:32 +01:00
|
|
|
})
|
2021-04-16 18:18:29 +02:00
|
|
|
}
|
|
|
|
|
2021-08-31 19:14:37 +02:00
|
|
|
/// # `GET /_matrix/federation/v1/query/profile`
|
|
|
|
///
|
|
|
|
/// Gets information on a profile.
|
2022-01-20 11:51:31 +01:00
|
|
|
pub async fn get_profile_information_route(
|
2022-12-14 13:09:10 +01:00
|
|
|
body: Ruma<get_profile_information::v1::Request>,
|
2022-01-22 16:58:32 +01:00
|
|
|
) -> Result<get_profile_information::v1::Response> {
|
2024-02-01 12:05:59 +01:00
|
|
|
if body.user_id.server_name() != services().globals.server_name() {
|
|
|
|
return Err(Error::BadRequest(
|
|
|
|
ErrorKind::InvalidParam,
|
|
|
|
"Tried to access user from other server.",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2020-10-05 22:19:22 +02:00
|
|
|
let mut displayname = None;
|
|
|
|
let mut avatar_url = None;
|
2021-07-15 23:17:58 +02:00
|
|
|
let mut blurhash = None;
|
2020-10-05 22:19:22 +02:00
|
|
|
|
2020-12-04 18:16:17 -05:00
|
|
|
match &body.field {
|
2022-10-05 20:34:31 +02:00
|
|
|
Some(ProfileField::DisplayName) => {
|
|
|
|
displayname = services().users.displayname(&body.user_id)?
|
|
|
|
}
|
2021-07-15 23:17:58 +02:00
|
|
|
Some(ProfileField::AvatarUrl) => {
|
2022-09-06 23:15:09 +02:00
|
|
|
avatar_url = services().users.avatar_url(&body.user_id)?;
|
|
|
|
blurhash = services().users.blurhash(&body.user_id)?
|
2021-07-15 23:17:58 +02:00
|
|
|
}
|
2021-07-15 19:54:04 +02:00
|
|
|
// TODO: what to do with custom
|
|
|
|
Some(_) => {}
|
2020-10-05 22:19:22 +02:00
|
|
|
None => {
|
2022-09-06 23:15:09 +02:00
|
|
|
displayname = services().users.displayname(&body.user_id)?;
|
|
|
|
avatar_url = services().users.avatar_url(&body.user_id)?;
|
|
|
|
blurhash = services().users.blurhash(&body.user_id)?;
|
2020-10-05 22:19:22 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(get_profile_information::v1::Response {
|
2021-07-15 23:17:58 +02:00
|
|
|
blurhash,
|
2020-10-05 22:19:22 +02:00
|
|
|
displayname,
|
|
|
|
avatar_url,
|
2022-01-22 16:58:32 +01:00
|
|
|
})
|
2020-10-05 22:19:22 +02:00
|
|
|
}
|
|
|
|
|
2021-08-31 19:14:37 +02:00
|
|
|
/// # `POST /_matrix/federation/v1/user/keys/query`
|
|
|
|
///
|
|
|
|
/// Gets devices and identity keys for the given users.
|
2022-10-05 20:34:31 +02:00
|
|
|
pub async fn get_keys_route(body: Ruma<get_keys::v1::Request>) -> Result<get_keys::v1::Response> {
|
2024-02-01 12:05:59 +01:00
|
|
|
if body
|
|
|
|
.device_keys
|
|
|
|
.iter()
|
|
|
|
.any(|(u, _)| u.server_name() != services().globals.server_name())
|
|
|
|
{
|
|
|
|
return Err(Error::BadRequest(
|
|
|
|
ErrorKind::InvalidParam,
|
|
|
|
"Tried to access user from other server.",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2022-10-05 20:34:31 +02:00
|
|
|
let result = get_keys_helper(None, &body.device_keys, |u| {
|
|
|
|
Some(u.server_name()) == body.sender_servername.as_deref()
|
|
|
|
})
|
2021-07-20 19:40:25 +02:00
|
|
|
.await?;
|
2021-05-20 23:46:52 +02:00
|
|
|
|
|
|
|
Ok(get_keys::v1::Response {
|
|
|
|
device_keys: result.device_keys,
|
|
|
|
master_keys: result.master_keys,
|
|
|
|
self_signing_keys: result.self_signing_keys,
|
2022-01-22 16:58:32 +01:00
|
|
|
})
|
2021-05-20 23:46:52 +02:00
|
|
|
}
|
|
|
|
|
2021-08-31 19:14:37 +02:00
|
|
|
/// # `POST /_matrix/federation/v1/user/keys/claim`
|
|
|
|
///
|
|
|
|
/// Claims one-time keys.
|
2021-06-08 18:10:00 +02:00
|
|
|
pub async fn claim_keys_route(
|
2021-05-28 13:44:40 +02:00
|
|
|
body: Ruma<claim_keys::v1::Request>,
|
2022-01-22 16:58:32 +01:00
|
|
|
) -> Result<claim_keys::v1::Response> {
|
2024-02-01 12:05:59 +01:00
|
|
|
if body
|
|
|
|
.one_time_keys
|
|
|
|
.iter()
|
|
|
|
.any(|(u, _)| u.server_name() != services().globals.server_name())
|
|
|
|
{
|
|
|
|
return Err(Error::BadRequest(
|
|
|
|
ErrorKind::InvalidParam,
|
|
|
|
"Tried to access user from other server.",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2022-09-06 23:15:09 +02:00
|
|
|
let result = claim_keys_helper(&body.one_time_keys).await?;
|
2021-05-28 13:44:40 +02:00
|
|
|
|
|
|
|
Ok(claim_keys::v1::Response {
|
|
|
|
one_time_keys: result.one_time_keys,
|
2022-01-22 16:58:32 +01:00
|
|
|
})
|
2021-05-28 13:44:40 +02:00
|
|
|
}
|
|
|
|
|
2024-05-28 00:22:11 +02:00
|
|
|
/// # `GET /_matrix/federation/v1/openid/userinfo`
|
|
|
|
///
|
|
|
|
/// Get information about the user that generated the OpenID token.
|
|
|
|
pub async fn get_openid_userinfo_route(
|
|
|
|
body: Ruma<get_openid_userinfo::v1::Request>,
|
|
|
|
) -> Result<get_openid_userinfo::v1::Response> {
|
|
|
|
Ok(get_openid_userinfo::v1::Response::new(
|
|
|
|
services()
|
|
|
|
.users
|
|
|
|
.find_from_openid_token(&body.access_token)?
|
|
|
|
.ok_or_else(|| {
|
|
|
|
Error::BadRequest(
|
|
|
|
ErrorKind::Unauthorized,
|
|
|
|
"OpenID token has expired or does not exist.",
|
|
|
|
)
|
|
|
|
})?,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
|
2024-05-02 09:26:43 +01:00
|
|
|
/// # `GET /.well-known/matrix/server`
|
|
|
|
///
|
|
|
|
/// Returns the federation server discovery information.
|
|
|
|
pub async fn well_known_server(
|
|
|
|
_body: Ruma<discover_homeserver::Request>,
|
|
|
|
) -> Result<discover_homeserver::Response> {
|
|
|
|
Ok(discover_homeserver::Response {
|
|
|
|
server: services().globals.well_known_server(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-12-08 12:34:46 +01:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2021-04-24 12:27:46 +02:00
|
|
|
use super::{add_port_to_hostname, get_ip_with_port, FedDest};
|
2020-12-08 12:34:46 +01:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn ips_get_default_ports() {
|
|
|
|
assert_eq!(
|
2021-04-21 00:35:44 -03:00
|
|
|
get_ip_with_port("1.1.1.1"),
|
|
|
|
Some(FedDest::Literal("1.1.1.1:8448".parse().unwrap()))
|
2020-12-08 12:34:46 +01:00
|
|
|
);
|
|
|
|
assert_eq!(
|
2021-04-21 00:35:44 -03:00
|
|
|
get_ip_with_port("dead:beef::"),
|
|
|
|
Some(FedDest::Literal("[dead:beef::]:8448".parse().unwrap()))
|
2020-12-08 12:34:46 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn ips_keep_custom_ports() {
|
|
|
|
assert_eq!(
|
2021-04-21 00:35:44 -03:00
|
|
|
get_ip_with_port("1.1.1.1:1234"),
|
|
|
|
Some(FedDest::Literal("1.1.1.1:1234".parse().unwrap()))
|
2020-12-08 12:34:46 +01:00
|
|
|
);
|
|
|
|
assert_eq!(
|
2021-04-21 00:35:44 -03:00
|
|
|
get_ip_with_port("[dead::beef]:8933"),
|
|
|
|
Some(FedDest::Literal("[dead::beef]:8933".parse().unwrap()))
|
2020-12-08 12:34:46 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn hostnames_get_default_ports() {
|
|
|
|
assert_eq!(
|
2021-04-21 00:35:44 -03:00
|
|
|
add_port_to_hostname("example.com"),
|
|
|
|
FedDest::Named(String::from("example.com"), String::from(":8448"))
|
2020-12-08 12:34:46 +01:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn hostnames_keep_custom_ports() {
|
|
|
|
assert_eq!(
|
2021-04-21 00:35:44 -03:00
|
|
|
add_port_to_hostname("example.com:1337"),
|
|
|
|
FedDest::Named(String::from("example.com"), String::from(":1337"))
|
2020-12-08 12:34:46 +01:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|