Compare commits

...

14 Commits

Author SHA1 Message Date
Jade Ellis 7a8c409ff9 refactor: Use snafu
This should gradtly improve the debugging experience by allowing
tracking backtraces to get more context from the error.
A lot of the touced files here are just cleaning up the old way of
creating errors.
2026-02-22 14:06:15 +00:00
Renovate Bot 688ef727e5 chore(deps): update rust crate nix to 0.31.0 2026-02-21 16:33:05 +00:00
Shannon Sterz 3de026160e docs: express forbidden_remote_server_names as valid regex
this field expects a regex not a glob, so the correct value should be
".*" if one wants to block all remote server names. otherwise, setting
"*" as documented results in an error on start because the configuration
could not be properly parsed.
2026-02-21 16:27:59 +00:00
Ginger 9fe761513d chore: Clippy & prek fixes 2026-02-21 11:27:39 -05:00
Renovate Bot abf1e1195a chore(deps): update rust crate libloading to 0.9.0 2026-02-21 01:55:48 +00:00
Ginger d9537e9b55 fix: Forbid registering users with a non-local localpart 2026-02-20 20:54:19 -05:00
Jade Ellis 0d1de70d8f fix(deps): Update lockfile 2026-02-21 00:22:42 +00:00
Ben Botwin 4aa03a71eb fix(nix): Added unstable flag to buildDeps 2026-02-21 00:15:53 +00:00
aviac f847918575 fix(nix): Fix all-features build
The build was broken since we started using an unstable reqwest version
which requires setting an extra feature flag
2026-02-21 00:15:53 +00:00
Renovate Bot 7569a0545b chore(deps): update dependency lddtree to 0.5.0 2026-02-20 22:59:34 +00:00
Jade Ellis b6c5991e1f chore(deps): Update rand
A couple indirect deps are still on rand_core 0.6 but we can deal
2026-02-20 22:57:45 +00:00
Katie Kloss efd879fcd8 docs: Add news fragment 2026-02-20 10:13:54 +00:00
Katie Kloss 92a848f74d fix: Crash before starting on OpenBSD
core_affinity doesn't return any cores on OpenBSD, so we try to
clamp(1, 0). This is Less Good than fixing that crate, but at
least allows the server to start up.
2026-02-20 10:13:54 +00:00
Renovate Bot 776b5865ba chore(deps): update sentry-rust monorepo to 0.46.0 2026-02-19 14:56:25 +00:00
62 changed files with 1424 additions and 833 deletions
Generated
+482 -407
View File
File diff suppressed because it is too large Load Diff
+15 -10
View File
@@ -68,7 +68,7 @@ default-features = false
version = "0.1.3"
[workspace.dependencies.rand]
version = "0.8.5"
version = "0.10.0"
# Used for the http request / response body type for Ruma endpoints used with reqwest
[workspace.dependencies.bytes]
@@ -253,7 +253,7 @@ features = [
version = "0.4.0"
[workspace.dependencies.libloading]
version = "0.8.6"
version = "0.9.0"
# Validating urls in config, was already a transitive dependency
[workspace.dependencies.url]
@@ -298,7 +298,7 @@ default-features = false
features = ["env", "toml"]
[workspace.dependencies.hickory-resolver]
version = "0.25.1"
version = "0.25.2"
default-features = false
features = [
"serde",
@@ -307,9 +307,14 @@ features = [
]
# Used for conduwuit::Error type
[workspace.dependencies.thiserror]
version = "2.0.12"
[workspace.dependencies.snafu]
version = "0.8"
default-features = false
features = ["std", "rust_1_81"]
# Used for macro name generation
[workspace.dependencies.paste]
version = "1.0"
# Used when hashing the state
[workspace.dependencies.ring]
@@ -343,7 +348,7 @@ version = "0.1.2"
[workspace.dependencies.ruma]
git = "https://forgejo.ellis.link/continuwuation/ruwuma"
#branch = "conduwuit-changes"
rev = "3126cb5eea991ec40590e54d8c9d75637650641a"
rev = "e087ff15888156942ca2ffe6097d1b4c3fd27628"
features = [
"compat",
"rand",
@@ -425,7 +430,7 @@ features = ["http", "grpc-tonic", "trace", "logs", "metrics"]
# optional sentry metrics for crash/panic reporting
[workspace.dependencies.sentry]
version = "0.45.0"
version = "0.46.0"
default-features = false
features = [
"backtrace",
@@ -441,9 +446,9 @@ features = [
]
[workspace.dependencies.sentry-tracing]
version = "0.45.0"
version = "0.46.0"
[workspace.dependencies.sentry-tower]
version = "0.45.0"
version = "0.46.0"
# jemalloc usage
[workspace.dependencies.tikv-jemalloc-sys]
@@ -472,7 +477,7 @@ features = ["use_std"]
version = "0.5"
[workspace.dependencies.nix]
version = "0.30.1"
version = "0.31.0"
default-features = false
features = ["resource"]
+1
View File
@@ -0,0 +1 @@
Fixed a startup crash in the sender service if we can't detect the number of CPU cores, even if the `sender_workers' config option is set correctly. Contributed by @katie.
+1 -1
View File
@@ -1325,7 +1325,7 @@
# sender user's server name, inbound federation X-Matrix origin, and
# outbound federation handler.
#
# You can set this to ["*"] to block all servers by default, and then
# You can set this to [".*"] to block all servers by default, and then
# use `allowed_remote_server_names` to allow only specific servers.
#
# example: ["badserver\\.tld$", "badphrase", "19dollarfortnitecards"]
+1 -1
View File
@@ -52,7 +52,7 @@ ENV BINSTALL_VERSION=1.17.5
# renovate: datasource=github-releases depName=psastras/sbom-rs
ENV CARGO_SBOM_VERSION=0.9.1
# renovate: datasource=crate depName=lddtree
ENV LDDTREE_VERSION=0.4.0
ENV LDDTREE_VERSION=0.5.0
# renovate: datasource=crate depName=timelord-cli
ENV TIMELORD_VERSION=3.0.1
+1 -1
View File
@@ -22,7 +22,7 @@ ENV BINSTALL_VERSION=1.17.5
# renovate: datasource=github-releases depName=psastras/sbom-rs
ENV CARGO_SBOM_VERSION=0.9.1
# renovate: datasource=crate depName=lddtree
ENV LDDTREE_VERSION=0.4.0
ENV LDDTREE_VERSION=0.5.0
# Install unpackaged tools
RUN <<EOF
+13 -2
View File
@@ -77,7 +77,12 @@ rec {
craneLib.buildDepsOnly (
(commonAttrs commonAttrsArgs)
// {
env = uwuenv.buildDepsOnlyEnv // (makeRocksDBEnv { inherit rocksdb; });
env = uwuenv.buildDepsOnlyEnv
// (makeRocksDBEnv { inherit rocksdb; })
// {
# required since we started using unstable reqwest apparently ... otherwise the all-features build will fail
RUSTFLAGS = "--cfg reqwest_unstable";
};
inherit (features) cargoExtraArgs;
}
@@ -102,7 +107,13 @@ rec {
'';
cargoArtifacts = deps;
doCheck = true;
env = uwuenv.buildPackageEnv // rocksdbEnv;
env =
uwuenv.buildPackageEnv
// rocksdbEnv
// {
# required since we started using unstable reqwest apparently ... otherwise the all-features build will fail
RUSTFLAGS = "--cfg reqwest_unstable";
};
passthru.env = uwuenv.buildPackageEnv // rocksdbEnv;
meta.mainProgram = crateInfo.pname;
inherit (features) cargoExtraArgs;
+14 -7
View File
@@ -3,7 +3,7 @@ use std::fmt::Write;
use axum::extract::State;
use axum_client_ip::InsecureClientIp;
use conduwuit::{
Err, Error, Event, Result, debug_info, err, error, info,
Err, Event, Result, debug_info, err, error, info,
matrix::pdu::PduBuilder,
utils::{self, ReadyExt, stream::BroadbandExt},
warn,
@@ -252,6 +252,13 @@ pub(crate) async fn register_route(
}
}
// Don't allow registration with user IDs that aren't local
if !services.globals.user_is_local(&user_id) {
return Err!(Request(InvalidUsername(
"Username {body_username} is not local to this server"
)));
}
user_id
},
| Err(e) => {
@@ -380,7 +387,7 @@ pub(crate) async fn register_route(
)
.await?;
if !worked {
return Err(Error::Uiaa(uiaainfo));
return Err!(Uiaa(uiaainfo));
}
// Success!
},
@@ -394,7 +401,7 @@ pub(crate) async fn register_route(
&uiaainfo,
json,
);
return Err(Error::Uiaa(uiaainfo));
return Err!(Uiaa(uiaainfo));
},
| _ => {
return Err!(Request(NotJson("JSON body is not valid")));
@@ -654,7 +661,7 @@ pub(crate) async fn change_password_route(
.await?;
if !worked {
return Err(Error::Uiaa(uiaainfo));
return Err!(Uiaa(uiaainfo));
}
// Success!
@@ -666,7 +673,7 @@ pub(crate) async fn change_password_route(
.uiaa
.create(sender_user, body.sender_device(), &uiaainfo, json);
return Err(Error::Uiaa(uiaainfo));
return Err!(Uiaa(uiaainfo));
},
| _ => {
return Err!(Request(NotJson("JSON body is not valid")));
@@ -784,7 +791,7 @@ pub(crate) async fn deactivate_route(
.await?;
if !worked {
return Err(Error::Uiaa(uiaainfo));
return Err!(Uiaa(uiaainfo));
}
// Success!
},
@@ -795,7 +802,7 @@ pub(crate) async fn deactivate_route(
.uiaa
.create(sender_user, body.sender_device(), &uiaainfo, json);
return Err(Error::Uiaa(uiaainfo));
return Err!(Uiaa(uiaainfo));
},
| _ => {
return Err!(Request(NotJson("JSON body is not valid")));
+4 -4
View File
@@ -1,6 +1,6 @@
use axum::extract::State;
use axum_client_ip::InsecureClientIp;
use conduwuit::{Err, Error, Result, debug, err, utils};
use conduwuit::{Err, Result, debug, err, utils};
use futures::StreamExt;
use ruma::{
MilliSecondsSinceUnixEpoch, OwnedDeviceId,
@@ -232,7 +232,7 @@ pub(crate) async fn delete_devices_route(
.await?;
if !worked {
return Err(Error::Uiaa(uiaainfo));
return Err!(Uiaa(uiaainfo));
}
// Success!
},
@@ -243,10 +243,10 @@ pub(crate) async fn delete_devices_route(
.uiaa
.create(sender_user, sender_device, &uiaainfo, json);
return Err(Error::Uiaa(uiaainfo));
return Err!(Uiaa(uiaainfo));
},
| _ => {
return Err(Error::BadRequest(ErrorKind::NotJson, "Not json."));
return Err!(BadRequest(ErrorKind::NotJson, "Not json."));
},
},
}
+6 -6
View File
@@ -5,7 +5,7 @@ use std::{
use axum::extract::State;
use conduwuit::{
Err, Error, Result, debug, debug_warn, err,
Err, Result, debug, debug_warn, err,
result::NotFound,
utils,
utils::{IterStream, stream::WidebandExt},
@@ -215,7 +215,7 @@ pub(crate) async fn upload_signing_keys_route(
.await?;
if !worked {
return Err(Error::Uiaa(uiaainfo));
return Err!(Uiaa(uiaainfo));
}
// Success!
},
@@ -226,10 +226,10 @@ pub(crate) async fn upload_signing_keys_route(
.uiaa
.create(sender_user, sender_device, &uiaainfo, json);
return Err(Error::Uiaa(uiaainfo));
return Err!(Uiaa(uiaainfo));
},
| _ => {
return Err(Error::BadRequest(ErrorKind::NotJson, "Not json."));
return Err!(BadRequest(ErrorKind::NotJson, "Not json."));
},
},
}
@@ -396,12 +396,12 @@ pub(crate) async fn get_key_changes_route(
let from = body
.from
.parse()
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid `from`."))?;
.map_err(|_| err!(BadRequest(ErrorKind::InvalidParam, "Invalid `from`.")))?;
let to = body
.to
.parse()
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid `to`."))?;
.map_err(|_| err!(BadRequest(ErrorKind::InvalidParam, "Invalid `to`.")))?;
device_list_updates.extend(
services
+2 -2
View File
@@ -3,7 +3,7 @@ use std::time::Duration;
use axum::extract::State;
use axum_client_ip::InsecureClientIp;
use conduwuit::{
Err, Result, err,
Err, Result, err, error,
utils::{self, content_disposition::make_content_disposition, math::ruma_from_usize},
};
use conduwuit_service::{
@@ -69,7 +69,7 @@ pub(crate) async fn create_content_route(
.create(mxc, Some(user), Some(&content_disposition), content_type, &body.file)
.await
{
err!("Failed to save uploaded media: {e}");
error!("Failed to save uploaded media: {e}");
return Err!(Request(Unknown("Failed to save uploaded media")));
}
+3 -3
View File
@@ -1,7 +1,7 @@
use axum::extract::State;
use axum_client_ip::InsecureClientIp;
use conduwuit::{
Err, Error, Result, at, debug_warn,
Err, Result, at, debug_warn,
matrix::{
event::{Event, Matches},
pdu::PduCount,
@@ -322,7 +322,7 @@ where
if server_ignored {
// the sender's server is ignored, so ignore this event
return Err(Error::BadRequest(
return Err!(BadRequest(
ErrorKind::SenderIgnored { sender: None },
"The sender's server is ignored by this server.",
));
@@ -331,7 +331,7 @@ where
if user_ignored && !services.config.send_messages_from_ignored_users_to_client {
// the recipient of this PDU has the sender ignored, and we're not
// configured to send ignored messages to clients
return Err(Error::BadRequest(
return Err!(BadRequest(
ErrorKind::SenderIgnored { sender: Some(event.sender().to_owned()) },
"You have ignored this sender.",
));
+16 -16
View File
@@ -1,5 +1,5 @@
use axum::extract::State;
use conduwuit::{Err, Error, Result, err};
use conduwuit::{Err, Result, err};
use conduwuit_service::Services;
use ruma::{
CanonicalJsonObject, CanonicalJsonValue,
@@ -243,27 +243,27 @@ pub(crate) async fn set_pushrule_route(
body.before.as_deref(),
) {
let err = match error {
| InsertPushRuleError::ServerDefaultRuleId => Error::BadRequest(
| InsertPushRuleError::ServerDefaultRuleId => err!(BadRequest(
ErrorKind::InvalidParam,
"Rule IDs starting with a dot are reserved for server-default rules.",
),
| InsertPushRuleError::InvalidRuleId => Error::BadRequest(
)),
| InsertPushRuleError::InvalidRuleId => err!(BadRequest(
ErrorKind::InvalidParam,
"Rule ID containing invalid characters.",
),
| InsertPushRuleError::RelativeToServerDefaultRule => Error::BadRequest(
)),
| InsertPushRuleError::RelativeToServerDefaultRule => err!(BadRequest(
ErrorKind::InvalidParam,
"Can't place a push rule relatively to a server-default rule.",
),
| InsertPushRuleError::UnknownRuleId => Error::BadRequest(
)),
| InsertPushRuleError::UnknownRuleId => err!(BadRequest(
ErrorKind::NotFound,
"The before or after rule could not be found.",
),
| InsertPushRuleError::BeforeHigherThanAfter => Error::BadRequest(
)),
| InsertPushRuleError::BeforeHigherThanAfter => err!(BadRequest(
ErrorKind::InvalidParam,
"The before rule has a higher priority than the after rule.",
),
| _ => Error::BadRequest(ErrorKind::InvalidParam, "Invalid data."),
)),
| _ => err!(BadRequest(ErrorKind::InvalidParam, "Invalid data.")),
};
return Err(err);
@@ -433,13 +433,13 @@ pub(crate) async fn delete_pushrule_route(
.remove(body.kind.clone(), &body.rule_id)
{
let err = match error {
| RemovePushRuleError::ServerDefault => Error::BadRequest(
| RemovePushRuleError::ServerDefault => err!(BadRequest(
ErrorKind::InvalidParam,
"Cannot delete a server-default pushrule.",
),
)),
| RemovePushRuleError::NotFound =>
Error::BadRequest(ErrorKind::NotFound, "Push rule not found."),
| _ => Error::BadRequest(ErrorKind::InvalidParam, "Invalid data."),
err!(BadRequest(ErrorKind::NotFound, "Push rule not found.")),
| _ => err!(BadRequest(ErrorKind::InvalidParam, "Invalid data.")),
};
return Err(err);
+1 -2
View File
@@ -4,7 +4,6 @@ use axum::extract::State;
use axum_client_ip::InsecureClientIp;
use conduwuit::{Err, Event, Result, debug_info, info, matrix::pdu::PduEvent, utils::ReadyExt};
use conduwuit_service::Services;
use rand::Rng;
use ruma::{
EventId, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, UserId,
api::client::{
@@ -244,7 +243,7 @@ fn build_report(report: Report) -> RoomMessageEventContent {
/// random delay sending a response per spec suggestion regarding
/// enumerating for potential events existing in our server.
async fn delay_response() {
let time_to_wait = rand::thread_rng().gen_range(2..5);
let time_to_wait = rand::random_range(2..5);
debug_info!(
"Got successful /report request, waiting {time_to_wait} seconds before sending \
successful response."
+6 -6
View File
@@ -2,7 +2,7 @@ use std::cmp::max;
use axum::extract::State;
use conduwuit::{
Err, Error, Event, Result, RoomVersion, debug, err, info,
Err, Event, Result, RoomVersion, debug, err, info,
matrix::{StateKey, pdu::PduBuilder},
};
use futures::{FutureExt, StreamExt};
@@ -58,7 +58,7 @@ pub(crate) async fn upgrade_room_route(
let sender_user = body.sender_user.as_ref().expect("user is authenticated");
if !services.server.supported_room_version(&body.new_version) {
return Err(Error::BadRequest(
return Err!(BadRequest(
ErrorKind::UnsupportedRoomVersion,
"This server does not support that room version.",
));
@@ -170,7 +170,7 @@ pub(crate) async fn upgrade_room_route(
"creator".into(),
json!(&sender_user).try_into().map_err(|e| {
info!("Error forming creation event: {e}");
Error::BadRequest(ErrorKind::BadJson, "Error forming creation event")
err!(BadRequest(ErrorKind::BadJson, "Error forming creation event"))
})?,
);
},
@@ -186,13 +186,13 @@ pub(crate) async fn upgrade_room_route(
"room_version".into(),
json!(&body.new_version)
.try_into()
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Error forming creation event"))?,
.map_err(|_| err!(BadRequest(ErrorKind::BadJson, "Error forming creation event")))?,
);
create_event_content.insert(
"predecessor".into(),
json!(predecessor)
.try_into()
.map_err(|_| Error::BadRequest(ErrorKind::BadJson, "Error forming creation event"))?,
.map_err(|_| err!(BadRequest(ErrorKind::BadJson, "Error forming creation event")))?,
);
// Validate creation event content
@@ -203,7 +203,7 @@ pub(crate) async fn upgrade_room_route(
)
.is_err()
{
return Err(Error::BadRequest(ErrorKind::BadJson, "Error forming creation event"));
return Err!(BadRequest(ErrorKind::BadJson, "Error forming creation event"));
}
let create_event_id = services
+4 -4
View File
@@ -3,7 +3,7 @@ use std::time::Duration;
use axum::extract::State;
use axum_client_ip::InsecureClientIp;
use conduwuit::{
Err, Error, Result, debug, err, info,
Err, Result, debug, err, info,
utils::{self, ReadyExt, hash},
warn,
};
@@ -191,7 +191,7 @@ pub(crate) async fn handle_login(
}
if services.users.is_locked(&user_id).await? {
return Err(Error::BadRequest(ErrorKind::UserLocked, "This account has been locked."));
return Err!(BadRequest(ErrorKind::UserLocked, "This account has been locked."));
}
if services.users.is_login_disabled(&user_id).await {
@@ -390,7 +390,7 @@ pub(crate) async fn login_token_route(
.await?;
if !worked {
return Err(Error::Uiaa(uiaainfo));
return Err!(Uiaa(uiaainfo));
}
// Success!
@@ -402,7 +402,7 @@ pub(crate) async fn login_token_route(
.uiaa
.create(sender_user, sender_device, &uiaainfo, json);
return Err(Error::Uiaa(uiaainfo));
return Err!(Uiaa(uiaainfo));
},
| _ => {
return Err!(Request(NotJson("No JSON body was sent when required.")));
+2 -2
View File
@@ -1,7 +1,7 @@
use std::collections::BTreeMap;
use axum::extract::State;
use conduwuit::{Error, Result};
use conduwuit::{Result, err};
use conduwuit_service::sending::EduBuf;
use futures::StreamExt;
use ruma::{
@@ -66,7 +66,7 @@ pub(crate) async fn send_event_to_device_route(
let event = event
.deserialize_as()
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Event is invalid"))?;
.map_err(|_| err!(BadRequest(ErrorKind::InvalidParam, "Event is invalid")))?;
match target_device_id_maybe {
| DeviceIdOrAllDevices::DeviceId(target_device_id) => {
+7 -10
View File
@@ -1,11 +1,8 @@
use axum::{Json, extract::State, response::IntoResponse};
use conduwuit::{Error, Result};
use ruma::api::client::{
discovery::{
discover_homeserver::{self, HomeserverInfo, SlidingSyncProxyInfo},
discover_support::{self, Contact},
},
error::ErrorKind,
use conduwuit::{Err, Result};
use ruma::api::client::discovery::{
discover_homeserver::{self, HomeserverInfo, SlidingSyncProxyInfo},
discover_support::{self, Contact},
};
use crate::Ruma;
@@ -19,7 +16,7 @@ pub(crate) async fn well_known_client(
) -> Result<discover_homeserver::Response> {
let client_url = match services.config.well_known.client.as_ref() {
| Some(url) => url.to_string(),
| None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")),
| None => return Err!(BadRequest(ErrorKind::NotFound, "Not found.")),
};
Ok(discover_homeserver::Response {
@@ -88,7 +85,7 @@ pub(crate) async fn well_known_support(
if contacts.is_empty() && support_page.is_none() {
// No admin room, no configured contacts, and no support page
return Err(Error::BadRequest(ErrorKind::NotFound, "Not found."));
return Err!(BadRequest(ErrorKind::NotFound, "Not found."));
}
Ok(discover_support::Response { contacts, support_page })
@@ -105,7 +102,7 @@ pub(crate) async fn syncv3_client_server_json(
| Some(url) => url.to_string(),
| None => match services.config.well_known.server.as_ref() {
| Some(url) => url.to_string(),
| None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")),
| None => return Err!(BadRequest(ErrorKind::NotFound, "Not found.")),
},
};
+10 -10
View File
@@ -4,7 +4,7 @@ use axum_extra::{
headers::{Authorization, authorization::Bearer},
typed_header::TypedHeaderRejectionReason,
};
use conduwuit::{Err, Error, Result, debug_error, err, warn};
use conduwuit::{Err, Result, debug_error, err, warn};
use futures::{
TryFutureExt,
future::{
@@ -77,7 +77,7 @@ pub(super) async fn auth(
// already
},
| Token::None | Token::Invalid => {
return Err(Error::BadRequest(
return Err!(BadRequest(
ErrorKind::MissingToken,
"Missing or invalid access token.",
));
@@ -96,7 +96,7 @@ pub(super) async fn auth(
// already
},
| Token::None | Token::Invalid => {
return Err(Error::BadRequest(
return Err!(BadRequest(
ErrorKind::MissingToken,
"Missing or invalid access token.",
));
@@ -130,10 +130,10 @@ pub(super) async fn auth(
appservice_info: None,
})
} else {
Err(Error::BadRequest(ErrorKind::MissingToken, "Missing access token."))
Err!(BadRequest(ErrorKind::MissingToken, "Missing access token."))
}
},
| _ => Err(Error::BadRequest(ErrorKind::MissingToken, "Missing access token.")),
| _ => Err!(BadRequest(ErrorKind::MissingToken, "Missing access token.")),
},
| (
AuthScheme::AccessToken | AuthScheme::AccessTokenOptional | AuthScheme::None,
@@ -149,7 +149,7 @@ pub(super) async fn auth(
&ruma::api::client::session::logout::v3::Request::METADATA
| &ruma::api::client::session::logout_all::v3::Request::METADATA
) {
return Err(Error::BadRequest(
return Err!(BadRequest(
ErrorKind::UserLocked,
"This account has been locked.",
));
@@ -174,11 +174,11 @@ pub(super) async fn auth(
appservice_info: None,
}),
| (AuthScheme::ServerSignatures, Token::Appservice(_) | Token::User(_)) =>
Err(Error::BadRequest(
Err!(BadRequest(
ErrorKind::Unauthorized,
"Only server signatures should be used on this endpoint.",
)),
| (AuthScheme::AppserviceToken, Token::User(_)) => Err(Error::BadRequest(
| (AuthScheme::AppserviceToken, Token::User(_)) => Err!(BadRequest(
ErrorKind::Unauthorized,
"Only appservice access tokens should be used on this endpoint.",
)),
@@ -196,13 +196,13 @@ pub(super) async fn auth(
appservice_info: None,
})
} else {
Err(Error::BadRequest(
Err!(BadRequest(
ErrorKind::UnknownToken { soft_logout: false },
"Unknown access token.",
))
}
},
| (_, Token::Invalid) => Err(Error::BadRequest(
| (_, Token::Invalid) => Err!(BadRequest(
ErrorKind::UnknownToken { soft_logout: false },
"Unknown access token.",
)),
+3 -6
View File
@@ -1,12 +1,9 @@
use std::{borrow::Borrow, iter::once};
use axum::extract::State;
use conduwuit::{Err, Error, Result, info, utils::stream::ReadyExt};
use conduwuit::{Err, Error, Result, err, info, utils::stream::ReadyExt};
use futures::StreamExt;
use ruma::{
RoomId,
api::{client::error::ErrorKind, federation::authorization::get_event_authorization},
};
use ruma::{RoomId, api::federation::authorization::get_event_authorization};
use super::AccessCheck;
use crate::Ruma;
@@ -47,7 +44,7 @@ pub(crate) async fn get_event_authorization_route(
.timeline
.get_pdu_json(&body.event_id)
.await
.map_err(|_| Error::BadRequest(ErrorKind::NotFound, "Event not found."))?;
.map_err(|_| err!(BadRequest(ErrorKind::NotFound, "Event not found.")))?;
let room_id_str = event
.get("room_id")
+2 -2
View File
@@ -2,7 +2,7 @@ use axum::extract::State;
use axum_client_ip::InsecureClientIp;
use base64::{Engine as _, engine::general_purpose};
use conduwuit::{
Err, Error, PduEvent, Result, err, error,
Err, PduEvent, Result, err, error,
matrix::{Event, event::gen_event_id},
utils::{self, hash::sha256},
warn,
@@ -33,7 +33,7 @@ pub(crate) async fn create_invite_route(
.await?;
if !services.server.supported_room_version(&body.room_version) {
return Err(Error::BadRequest(
return Err!(BadRequest(
ErrorKind::IncompatibleRoomVersion { room_version: body.room_version.clone() },
"Server does not support this room version.",
));
+2 -2
View File
@@ -1,7 +1,7 @@
use std::borrow::ToOwned;
use axum::extract::State;
use conduwuit::{Err, Error, Result, debug, debug_info, info, matrix::pdu::PduBuilder, warn};
use conduwuit::{Err, Result, debug, debug_info, info, matrix::pdu::PduBuilder, warn};
use conduwuit_service::Services;
use futures::StreamExt;
use ruma::{
@@ -80,7 +80,7 @@ pub(crate) async fn create_join_event_template_route(
let room_version_id = services.rooms.state.get_room_version(&body.room_id).await?;
if !body.ver.contains(&room_version_id) {
return Err(Error::BadRequest(
return Err!(BadRequest(
ErrorKind::IncompatibleRoomVersion { room_version: room_version_id },
"Room version not supported.",
));
+3 -3
View File
@@ -1,6 +1,6 @@
use RoomVersionId::*;
use axum::extract::State;
use conduwuit::{Err, Error, Result, debug_warn, info, matrix::pdu::PduBuilder, warn};
use conduwuit::{Err, Result, debug_warn, info, matrix::pdu::PduBuilder, warn};
use ruma::{
RoomVersionId,
api::{client::error::ErrorKind, federation::knock::create_knock_event_template},
@@ -67,14 +67,14 @@ pub(crate) async fn create_knock_event_template_route(
let room_version_id = services.rooms.state.get_room_version(&body.room_id).await?;
if matches!(room_version_id, V1 | V2 | V3 | V4 | V5 | V6) {
return Err(Error::BadRequest(
return Err!(BadRequest(
ErrorKind::IncompatibleRoomVersion { room_version: room_version_id },
"Room version does not support knocking.",
));
}
if !body.ver.contains(&room_version_id) {
return Err(Error::BadRequest(
return Err!(BadRequest(
ErrorKind::IncompatibleRoomVersion { room_version: room_version_id },
"Your homeserver does not support the features required to knock on this room.",
));
+11 -5
View File
@@ -1,6 +1,6 @@
use axum::extract::State;
use axum_client_ip::InsecureClientIp;
use conduwuit::{Error, Result};
use conduwuit::{Err, Result, err};
use ruma::{
api::{
client::error::ErrorKind,
@@ -25,7 +25,7 @@ pub(crate) async fn get_public_rooms_filtered_route(
.config
.allow_public_room_directory_over_federation
{
return Err(Error::BadRequest(ErrorKind::forbidden(), "Room directory is not public"));
return Err!(BadRequest(ErrorKind::forbidden(), "Room directory is not public"));
}
let response = crate::client::get_public_rooms_filtered_helper(
@@ -38,7 +38,10 @@ pub(crate) async fn get_public_rooms_filtered_route(
)
.await
.map_err(|_| {
Error::BadRequest(ErrorKind::Unknown, "Failed to return this server's public room list.")
err!(BadRequest(
ErrorKind::Unknown,
"Failed to return this server's public room list."
))
})?;
Ok(get_public_rooms_filtered::v1::Response {
@@ -62,7 +65,7 @@ pub(crate) async fn get_public_rooms_route(
.globals
.allow_public_room_directory_over_federation()
{
return Err(Error::BadRequest(ErrorKind::forbidden(), "Room directory is not public"));
return Err!(BadRequest(ErrorKind::forbidden(), "Room directory is not public"));
}
let response = crate::client::get_public_rooms_filtered_helper(
@@ -75,7 +78,10 @@ pub(crate) async fn get_public_rooms_route(
)
.await
.map_err(|_| {
Error::BadRequest(ErrorKind::Unknown, "Failed to return this server's public room list.")
err!(BadRequest(
ErrorKind::Unknown,
"Failed to return this server's public room list."
))
})?;
Ok(get_public_rooms::v1::Response {
+6 -7
View File
@@ -1,7 +1,7 @@
use std::collections::BTreeMap;
use axum::extract::State;
use conduwuit::{Error, Result, err};
use conduwuit::{Err, Result, err};
use futures::StreamExt;
use get_profile_information::v1::ProfileField;
use rand::seq::SliceRandom;
@@ -40,7 +40,7 @@ pub(crate) async fn get_room_information_route(
servers.sort_unstable();
servers.dedup();
servers.shuffle(&mut rand::thread_rng());
servers.shuffle(&mut rand::rng());
// insert our server as the very first choice if in list
if let Some(server_index) = servers
@@ -67,17 +67,16 @@ pub(crate) async fn get_profile_information_route(
.config
.allow_inbound_profile_lookup_federation_requests
{
return Err(Error::BadRequest(
return Err!(BadRequest(
ErrorKind::forbidden(),
"Profile lookup over federation is not allowed on this homeserver.",
));
}
if !services.globals.server_is_ours(body.user_id.server_name()) {
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"User does not belong to this server.",
));
return Err!(
BadRequest(ErrorKind::InvalidParam, "User does not belong to this server.",)
);
}
let mut displayname = None;
+1 -1
View File
@@ -114,7 +114,7 @@ pub(crate) async fn send_transaction_message_route(
);
for (id, result) in &results {
if let Err(e) = result {
if matches!(e, Error::BadRequest(ErrorKind::NotFound, _)) {
if matches!(e, Error::BadRequest { kind: ErrorKind::NotFound, .. }) {
warn!("Incoming PDU failed {id}: {e:?}");
}
}
+6 -7
View File
@@ -1,7 +1,7 @@
use std::time::Duration;
use axum::extract::State;
use conduwuit::{Error, Result};
use conduwuit::{Err, Result};
use futures::{FutureExt, StreamExt, TryFutureExt};
use ruma::api::{
client::error::ErrorKind,
@@ -24,7 +24,7 @@ pub(crate) async fn get_devices_route(
body: Ruma<get_devices::v1::Request>,
) -> Result<get_devices::v1::Response> {
if !services.globals.user_is_local(&body.user_id) {
return Err(Error::BadRequest(
return Err!(BadRequest(
ErrorKind::InvalidParam,
"Tried to access user from other server.",
));
@@ -86,10 +86,9 @@ pub(crate) async fn get_keys_route(
.iter()
.any(|(u, _)| !services.globals.user_is_local(u))
{
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"User does not belong to this server.",
));
return Err!(
BadRequest(ErrorKind::InvalidParam, "User does not belong to this server.",)
);
}
let result = get_keys_helper(
@@ -121,7 +120,7 @@ pub(crate) async fn claim_keys_route(
.iter()
.any(|(u, _)| !services.globals.user_is_local(u))
{
return Err(Error::BadRequest(
return Err!(BadRequest(
ErrorKind::InvalidParam,
"Tried to access user from other server.",
));
+3 -3
View File
@@ -1,6 +1,6 @@
use axum::extract::State;
use conduwuit::{Error, Result};
use ruma::api::{client::error::ErrorKind, federation::discovery::discover_homeserver};
use conduwuit::{Err, Result};
use ruma::api::federation::discovery::discover_homeserver;
use crate::Ruma;
@@ -14,7 +14,7 @@ pub(crate) async fn well_known_server(
Ok(discover_homeserver::Response {
server: match services.server.config.well_known.server.as_ref() {
| Some(server_name) => server_name.to_owned(),
| None => return Err(Error::BadRequest(ErrorKind::NotFound, "Not found.")),
| None => return Err!(BadRequest(ErrorKind::NotFound, "Not found.")),
},
})
}
+3 -1
View File
@@ -86,6 +86,7 @@ libloading.optional = true
log.workspace = true
num-traits.workspace = true
rand.workspace = true
rand_core = { version = "0.6.4", features = ["getrandom"] }
regex.workspace = true
reqwest.workspace = true
ring.workspace = true
@@ -97,7 +98,8 @@ serde-saphyr.workspace = true
serde.workspace = true
smallvec.workspace = true
smallstr.workspace = true
thiserror.workspace = true
snafu.workspace = true
paste.workspace = true
tikv-jemallocator.optional = true
tikv-jemallocator.workspace = true
tikv-jemalloc-ctl.optional = true
+1 -1
View File
@@ -1525,7 +1525,7 @@ pub struct Config {
/// sender user's server name, inbound federation X-Matrix origin, and
/// outbound federation handler.
///
/// You can set this to ["*"] to block all servers by default, and then
/// You can set this to [".*"] to block all servers by default, and then
/// use `allowed_remote_server_names` to allow only specific servers.
///
/// example: ["badserver\\.tld$", "badphrase", "19dollarfortnitecards"]
+129 -30
View File
@@ -45,63 +45,162 @@ macro_rules! Err {
macro_rules! err {
(Request(Forbidden($level:ident!($($args:tt)+)))) => {{
let mut buf = String::new();
$crate::error::Error::Request(
$crate::ruma::api::client::error::ErrorKind::forbidden(),
$crate::err_log!(buf, $level, $($args)+),
$crate::http::StatusCode::BAD_REQUEST
)
$crate::error::Error::Request {
kind: $crate::ruma::api::client::error::ErrorKind::forbidden(),
message: $crate::err_log!(buf, $level, $($args)+),
code: $crate::http::StatusCode::BAD_REQUEST,
backtrace: Some($crate::snafu::Backtrace::capture()),
}
}};
(Request(Forbidden($($args:tt)+))) => {
$crate::error::Error::Request(
$crate::ruma::api::client::error::ErrorKind::forbidden(),
$crate::format_maybe!($($args)+),
$crate::http::StatusCode::BAD_REQUEST
)
{
let message: std::borrow::Cow<'static, str> = $crate::format_maybe!($($args)+);
$crate::error::Error::Request {
kind: $crate::ruma::api::client::error::ErrorKind::forbidden(),
message,
code: $crate::http::StatusCode::BAD_REQUEST,
backtrace: Some($crate::snafu::Backtrace::capture()),
}
}
};
(Request(NotFound($level:ident!($($args:tt)+)))) => {{
let mut buf = String::new();
$crate::error::Error::Request {
kind: $crate::ruma::api::client::error::ErrorKind::NotFound,
message: $crate::err_log!(buf, $level, $($args)+),
code: $crate::http::StatusCode::BAD_REQUEST,
backtrace: None,
}
}};
(Request(NotFound($($args:tt)+))) => {
{
let message: std::borrow::Cow<'static, str> = $crate::format_maybe!($($args)+);
$crate::error::Error::Request {
kind: $crate::ruma::api::client::error::ErrorKind::NotFound,
message,
code: $crate::http::StatusCode::BAD_REQUEST,
backtrace: None,
}
}
};
(Request($variant:ident($level:ident!($($args:tt)+)))) => {{
let mut buf = String::new();
$crate::error::Error::Request(
$crate::ruma::api::client::error::ErrorKind::$variant,
$crate::err_log!(buf, $level, $($args)+),
$crate::http::StatusCode::BAD_REQUEST
)
$crate::error::Error::Request {
kind: $crate::ruma::api::client::error::ErrorKind::$variant,
message: $crate::err_log!(buf, $level, $($args)+),
code: $crate::http::StatusCode::BAD_REQUEST,
backtrace: Some($crate::snafu::Backtrace::capture()),
}
}};
(Request($variant:ident($($args:tt)+))) => {
$crate::error::Error::Request(
$crate::ruma::api::client::error::ErrorKind::$variant,
$crate::format_maybe!($($args)+),
$crate::http::StatusCode::BAD_REQUEST
)
{
let message: std::borrow::Cow<'static, str> = $crate::format_maybe!($($args)+);
$crate::error::Error::Request {
kind: $crate::ruma::api::client::error::ErrorKind::$variant,
message,
code: $crate::http::StatusCode::BAD_REQUEST,
backtrace: Some($crate::snafu::Backtrace::capture()),
}
}
};
(Config($item:literal, $($args:tt)+)) => {{
let mut buf = String::new();
$crate::error::Error::Config($item, $crate::err_log!(buf, error, config = %$item, $($args)+))
$crate::error::ConfigSnafu {
directive: $item,
message: $crate::err_log!(buf, error, config = %$item, $($args)+),
}.build()
}};
(BadRequest(ErrorKind::NotFound, $($args:tt)+)) => {
{
let message: std::borrow::Cow<'static, str> = $crate::format_maybe!($($args)+);
$crate::error::Error::Request {
kind: $crate::ruma::api::client::error::ErrorKind::NotFound,
message,
code: $crate::http::StatusCode::BAD_REQUEST,
backtrace: None,
}
}
};
(BadRequest($kind:expr, $($args:tt)+)) => {
{
let message: std::borrow::Cow<'static, str> = $crate::format_maybe!($($args)+);
$crate::error::BadRequestSnafu {
kind: $kind,
message,
}.build()
}
};
(FeatureDisabled($($args:tt)+)) => {
{
let feature: std::borrow::Cow<'static, str> = $crate::format_maybe!($($args)+);
$crate::error::FeatureDisabledSnafu { feature }.build()
}
};
(Federation($server:expr, $error:expr $(,)?)) => {
{
$crate::error::FederationSnafu {
server: $server,
error: $error,
}.build()
}
};
(InconsistentRoomState($message:expr, $room_id:expr $(,)?)) => {
{
$crate::error::InconsistentRoomStateSnafu {
message: $message,
room_id: $room_id,
}.build()
}
};
(Uiaa($info:expr $(,)?)) => {
{
$crate::error::UiaaSnafu {
info: $info,
}.build()
}
};
($variant:ident($level:ident!($($args:tt)+))) => {{
let mut buf = String::new();
$crate::error::Error::$variant($crate::err_log!(buf, $level, $($args)+))
$crate::paste::paste! {
$crate::error::[<$variant Snafu>] {
message: $crate::err_log!(buf, $level, $($args)+),
}.build()
}
}};
($variant:ident($($args:ident),+)) => {
$crate::error::Error::$variant($($args),+)
};
($variant:ident($($args:tt)+)) => {
$crate::error::Error::$variant($crate::format_maybe!($($args)+))
$crate::paste::paste! {
{
let message: std::borrow::Cow<'static, str> = $crate::format_maybe!($($args)+);
$crate::error::[<$variant Snafu>] { message }.build()
}
}
};
($level:ident!($($args:tt)+)) => {{
let mut buf = String::new();
$crate::error::Error::Err($crate::err_log!(buf, $level, $($args)+))
let message: std::borrow::Cow<'static, str> = $crate::err_log!(buf, $level, $($args)+);
$crate::error::ErrSnafu { message }.build()
}};
($($args:tt)+) => {
$crate::error::Error::Err($crate::format_maybe!($($args)+))
{
let message: std::borrow::Cow<'static, str> = $crate::format_maybe!($($args)+);
$crate::error::ErrSnafu { message }.build()
}
};
}
@@ -134,7 +233,7 @@ macro_rules! err_log {
};
($crate::error::visit)(&mut $out, LEVEL, &__CALLSITE, &mut valueset_all!(__CALLSITE.metadata().fields(), $($fields)+));
($out).into()
std::borrow::Cow::<'static, str>::from($out)
}}
}
+448 -139
View File
@@ -6,151 +6,391 @@ mod serde;
use std::{any::Any, borrow::Cow, convert::Infallible, sync::PoisonError};
use snafu::{IntoError, prelude::*};
pub use self::{err::visit, log::*};
#[derive(thiserror::Error)]
#[derive(Debug, Snafu)]
#[snafu(visibility(pub))]
pub enum Error {
#[error("PANIC!")]
PanicAny(Box<dyn Any + Send>),
#[error("PANIC! {0}")]
Panic(&'static str, Box<dyn Any + Send + 'static>),
#[snafu(display("PANIC!"))]
PanicAny {
panic: Box<dyn Any + Send>,
backtrace: snafu::Backtrace,
},
#[snafu(display("PANIC! {message}"))]
Panic {
message: &'static str,
panic: Box<dyn Any + Send + 'static>,
backtrace: snafu::Backtrace,
},
// std
#[error(transparent)]
Fmt(#[from] std::fmt::Error),
#[error(transparent)]
FromUtf8(#[from] std::string::FromUtf8Error),
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error(transparent)]
ParseFloat(#[from] std::num::ParseFloatError),
#[error(transparent)]
ParseInt(#[from] std::num::ParseIntError),
#[error(transparent)]
Std(#[from] Box<dyn std::error::Error + Send>),
#[error(transparent)]
ThreadAccessError(#[from] std::thread::AccessError),
#[error(transparent)]
TryFromInt(#[from] std::num::TryFromIntError),
#[error(transparent)]
TryFromSlice(#[from] std::array::TryFromSliceError),
#[error(transparent)]
Utf8(#[from] std::str::Utf8Error),
#[snafu(display("Format error: {source}"))]
Fmt {
source: std::fmt::Error,
backtrace: snafu::Backtrace,
},
#[snafu(display("UTF-8 conversion error: {source}"))]
FromUtf8 {
source: std::string::FromUtf8Error,
backtrace: snafu::Backtrace,
},
#[snafu(display("I/O error: {source}"))]
Io {
source: std::io::Error,
backtrace: snafu::Backtrace,
},
#[snafu(display("Parse float error: {source}"))]
ParseFloat {
source: std::num::ParseFloatError,
backtrace: snafu::Backtrace,
},
#[snafu(display("Parse int error: {source}"))]
ParseInt {
source: std::num::ParseIntError,
backtrace: snafu::Backtrace,
},
#[snafu(display("Error: {source}"))]
Std {
source: Box<dyn std::error::Error + Send>,
backtrace: snafu::Backtrace,
},
#[snafu(display("Thread access error: {source}"))]
ThreadAccessError {
source: std::thread::AccessError,
backtrace: snafu::Backtrace,
},
#[snafu(display("Integer conversion error: {source}"))]
TryFromInt {
source: std::num::TryFromIntError,
backtrace: snafu::Backtrace,
},
#[snafu(display("Slice conversion error: {source}"))]
TryFromSlice {
source: std::array::TryFromSliceError,
backtrace: snafu::Backtrace,
},
#[snafu(display("UTF-8 error: {source}"))]
Utf8 {
source: std::str::Utf8Error,
backtrace: snafu::Backtrace,
},
// third-party
#[error(transparent)]
CapacityError(#[from] arrayvec::CapacityError),
#[error(transparent)]
CargoToml(#[from] cargo_toml::Error),
#[error(transparent)]
Clap(#[from] clap::error::Error),
#[error(transparent)]
Extension(#[from] axum::extract::rejection::ExtensionRejection),
#[error(transparent)]
Figment(#[from] figment::error::Error),
#[error(transparent)]
Http(#[from] http::Error),
#[error(transparent)]
HttpHeader(#[from] http::header::InvalidHeaderValue),
#[error("Join error: {0}")]
JoinError(#[from] tokio::task::JoinError),
#[error(transparent)]
Json(#[from] serde_json::Error),
#[error(transparent)]
JsParseInt(#[from] ruma::JsParseIntError), // js_int re-export
#[error(transparent)]
JsTryFromInt(#[from] ruma::JsTryFromIntError), // js_int re-export
#[error(transparent)]
Path(#[from] axum::extract::rejection::PathRejection),
#[error("Mutex poisoned: {0}")]
Poison(Cow<'static, str>),
#[error("Regex error: {0}")]
Regex(#[from] regex::Error),
#[error("Request error: {0}")]
Reqwest(#[from] reqwest::Error),
#[error("{0}")]
SerdeDe(Cow<'static, str>),
#[error("{0}")]
SerdeSer(Cow<'static, str>),
#[error(transparent)]
TomlDe(#[from] toml::de::Error),
#[error(transparent)]
TomlSer(#[from] toml::ser::Error),
#[error("Tracing filter error: {0}")]
TracingFilter(#[from] tracing_subscriber::filter::ParseError),
#[error("Tracing reload error: {0}")]
TracingReload(#[from] tracing_subscriber::reload::Error),
#[error(transparent)]
TypedHeader(#[from] axum_extra::typed_header::TypedHeaderRejection),
#[error(transparent)]
YamlDe(#[from] serde_saphyr::Error),
#[error(transparent)]
YamlSer(#[from] serde_saphyr::ser_error::Error),
#[snafu(display("Capacity error: {source}"))]
CapacityError {
source: arrayvec::CapacityError,
backtrace: snafu::Backtrace,
},
#[snafu(display("Cargo.toml error: {source}"))]
CargoToml {
source: cargo_toml::Error,
backtrace: snafu::Backtrace,
},
#[snafu(display("Clap error: {source}"))]
Clap {
source: clap::error::Error,
backtrace: snafu::Backtrace,
},
#[snafu(display("Extension rejection: {source}"))]
Extension {
source: axum::extract::rejection::ExtensionRejection,
backtrace: snafu::Backtrace,
},
#[snafu(display("Figment error: {source}"))]
Figment {
source: figment::error::Error,
backtrace: snafu::Backtrace,
},
#[snafu(display("HTTP error: {source}"))]
Http {
source: http::Error,
backtrace: snafu::Backtrace,
},
#[snafu(display("Invalid HTTP header value: {source}"))]
HttpHeader {
source: http::header::InvalidHeaderValue,
backtrace: snafu::Backtrace,
},
#[snafu(display("Join error: {source}"))]
JoinError {
source: tokio::task::JoinError,
backtrace: snafu::Backtrace,
},
#[snafu(display("JSON error: {source}"))]
Json {
source: serde_json::Error,
backtrace: snafu::Backtrace,
},
#[snafu(display("JS parse int error: {source}"))]
JsParseInt {
source: ruma::JsParseIntError,
backtrace: snafu::Backtrace,
},
#[snafu(display("JS try from int error: {source}"))]
JsTryFromInt {
source: ruma::JsTryFromIntError,
backtrace: snafu::Backtrace,
},
#[snafu(display("Path rejection: {source}"))]
Path {
source: axum::extract::rejection::PathRejection,
backtrace: snafu::Backtrace,
},
#[snafu(display("Mutex poisoned: {message}"))]
Poison {
message: Cow<'static, str>,
backtrace: snafu::Backtrace,
},
#[snafu(display("Regex error: {source}"))]
Regex {
source: regex::Error,
backtrace: snafu::Backtrace,
},
#[snafu(display("Request error: {source}"))]
Reqwest {
source: reqwest::Error,
backtrace: snafu::Backtrace,
},
#[snafu(display("{message}"))]
SerdeDe {
message: Cow<'static, str>,
backtrace: snafu::Backtrace,
},
#[snafu(display("{message}"))]
SerdeSer {
message: Cow<'static, str>,
backtrace: snafu::Backtrace,
},
#[snafu(display("TOML deserialization error: {source}"))]
TomlDe {
source: toml::de::Error,
backtrace: snafu::Backtrace,
},
#[snafu(display("TOML serialization error: {source}"))]
TomlSer {
source: toml::ser::Error,
backtrace: snafu::Backtrace,
},
#[snafu(display("Tracing filter error: {source}"))]
TracingFilter {
source: tracing_subscriber::filter::ParseError,
backtrace: snafu::Backtrace,
},
#[snafu(display("Tracing reload error: {source}"))]
TracingReload {
source: tracing_subscriber::reload::Error,
backtrace: snafu::Backtrace,
},
#[snafu(display("Typed header rejection: {source}"))]
TypedHeader {
source: axum_extra::typed_header::TypedHeaderRejection,
backtrace: snafu::Backtrace,
},
#[snafu(display("YAML deserialization error: {source}"))]
YamlDe {
source: serde_saphyr::Error,
backtrace: snafu::Backtrace,
},
#[snafu(display("YAML serialization error: {source}"))]
YamlSer {
source: serde_saphyr::ser_error::Error,
backtrace: snafu::Backtrace,
},
// ruma/conduwuit
#[error("Arithmetic operation failed: {0}")]
Arithmetic(Cow<'static, str>),
#[error("{0}: {1}")]
BadRequest(ruma::api::client::error::ErrorKind, &'static str), //TODO: remove
#[error("{0}")]
BadServerResponse(Cow<'static, str>),
#[error(transparent)]
CanonicalJson(#[from] ruma::CanonicalJsonError),
#[error("There was a problem with the '{0}' directive in your configuration: {1}")]
Config(&'static str, Cow<'static, str>),
#[error("{0}")]
Conflict(Cow<'static, str>), // This is only needed for when a room alias already exists
#[error(transparent)]
ContentDisposition(#[from] ruma::http_headers::ContentDispositionParseError),
#[error("{0}")]
Database(Cow<'static, str>),
#[error("Feature '{0}' is not available on this server.")]
FeatureDisabled(Cow<'static, str>),
#[error("Remote server {0} responded with: {1}")]
Federation(ruma::OwnedServerName, ruma::api::client::error::Error),
#[error("{0} in {1}")]
InconsistentRoomState(&'static str, ruma::OwnedRoomId),
#[error(transparent)]
IntoHttp(#[from] ruma::api::error::IntoHttpError),
#[error("{0}")]
Ldap(Cow<'static, str>),
#[error(transparent)]
Mxc(#[from] ruma::MxcUriError),
#[error(transparent)]
Mxid(#[from] ruma::IdParseError),
#[error("from {0}: {1}")]
Redaction(ruma::OwnedServerName, ruma::canonical_json::RedactionError),
#[error("{0}: {1}")]
Request(ruma::api::client::error::ErrorKind, Cow<'static, str>, http::StatusCode),
#[error(transparent)]
Ruma(#[from] ruma::api::client::error::Error),
#[error(transparent)]
Signatures(#[from] ruma::signatures::Error),
#[error(transparent)]
StateRes(#[from] crate::state_res::Error),
#[error("uiaa")]
Uiaa(ruma::api::client::uiaa::UiaaInfo),
#[snafu(display("Arithmetic operation failed: {message}"))]
Arithmetic {
message: Cow<'static, str>,
backtrace: snafu::Backtrace,
},
#[snafu(display("{kind}: {message}"))]
BadRequest {
kind: ruma::api::client::error::ErrorKind,
message: Cow<'static, str>,
backtrace: snafu::Backtrace,
},
#[snafu(display("{message}"))]
BadServerResponse {
message: Cow<'static, str>,
backtrace: snafu::Backtrace,
},
#[snafu(display("Canonical JSON error: {source}"))]
CanonicalJson {
source: ruma::CanonicalJsonError,
backtrace: snafu::Backtrace,
},
#[snafu(display(
"There was a problem with the '{directive}' directive in your configuration: {message}"
))]
Config {
directive: &'static str,
message: Cow<'static, str>,
backtrace: snafu::Backtrace,
},
#[snafu(display("{message}"))]
Conflict {
message: Cow<'static, str>,
backtrace: snafu::Backtrace,
},
#[snafu(display("Content disposition error: {source}"))]
ContentDisposition {
source: ruma::http_headers::ContentDispositionParseError,
backtrace: snafu::Backtrace,
},
#[snafu(display("{message}"))]
Database {
message: Cow<'static, str>,
backtrace: snafu::Backtrace,
},
#[snafu(display("Feature '{feature}' is not available on this server."))]
FeatureDisabled {
feature: Cow<'static, str>,
},
#[snafu(display("Remote server {server} responded with: {error}"))]
Federation {
server: ruma::OwnedServerName,
error: ruma::api::client::error::Error,
backtrace: snafu::Backtrace,
},
#[snafu(display("{message} in {room_id}"))]
InconsistentRoomState {
message: &'static str,
room_id: ruma::OwnedRoomId,
backtrace: snafu::Backtrace,
},
#[snafu(display("HTTP conversion error: {source}"))]
IntoHttp {
source: ruma::api::error::IntoHttpError,
backtrace: snafu::Backtrace,
},
#[snafu(display("{message}"))]
Ldap {
message: Cow<'static, str>,
backtrace: snafu::Backtrace,
},
#[snafu(display("MXC URI error: {source}"))]
Mxc {
source: ruma::MxcUriError,
backtrace: snafu::Backtrace,
},
#[snafu(display("Matrix ID parse error: {source}"))]
Mxid {
source: ruma::IdParseError,
backtrace: snafu::Backtrace,
},
#[snafu(display("from {server}: {error}"))]
Redaction {
server: ruma::OwnedServerName,
error: ruma::canonical_json::RedactionError,
backtrace: snafu::Backtrace,
},
#[snafu(display("{kind}: {message}"))]
Request {
kind: ruma::api::client::error::ErrorKind,
message: Cow<'static, str>,
code: http::StatusCode,
backtrace: Option<snafu::Backtrace>,
},
#[snafu(display("Ruma error: {source}"))]
Ruma {
source: ruma::api::client::error::Error,
backtrace: snafu::Backtrace,
},
#[snafu(display("Signature error: {source}"))]
Signatures {
source: ruma::signatures::Error,
backtrace: snafu::Backtrace,
},
#[snafu(display("State resolution error: {source}"))]
#[snafu(context(false))]
StateRes {
source: crate::state_res::Error,
},
#[snafu(display("uiaa"))]
Uiaa {
info: ruma::api::client::uiaa::UiaaInfo,
},
// unique / untyped
#[error("{0}")]
Err(Cow<'static, str>),
#[snafu(display("{message}"))]
Err {
message: Cow<'static, str>,
backtrace: snafu::Backtrace,
},
}
impl Error {
#[inline]
#[must_use]
pub fn from_errno() -> Self { Self::Io(std::io::Error::last_os_error()) }
pub fn from_errno() -> Self { IoSnafu {}.into_error(std::io::Error::last_os_error()) }
//#[deprecated]
#[must_use]
pub fn bad_database(message: &'static str) -> Self {
crate::err!(Database(error!("{message}")))
let message: Cow<'static, str> = message.into();
DatabaseSnafu { message }.build()
}
/// Sanitizes public-facing errors that can leak sensitive information.
pub fn sanitized_message(&self) -> String {
match self {
| Self::Database(..) => String::from("Database error occurred."),
| Self::Io(..) => String::from("I/O error occurred."),
| Self::Database { .. } => String::from("Database error occurred."),
| Self::Io { .. } => String::from("I/O error occurred."),
| _ => self.message(),
}
}
@@ -158,8 +398,8 @@ impl Error {
/// Generate the error message string.
pub fn message(&self) -> String {
match self {
| Self::Federation(origin, error) => format!("Answer from {origin}: {error}"),
| Self::Ruma(error) => response::ruma_error_message(error),
| Self::Federation { server, error, .. } => format!("Answer from {server}: {error}"),
| Self::Ruma { source, .. } => response::ruma_error_message(source),
| _ => format!("{self}"),
}
}
@@ -170,10 +410,10 @@ impl Error {
use ruma::api::client::error::ErrorKind::{FeatureDisabled, Unknown};
match self {
| Self::Federation(_, error) | Self::Ruma(error) =>
response::ruma_error_kind(error).clone(),
| Self::BadRequest(kind, ..) | Self::Request(kind, ..) => kind.clone(),
| Self::FeatureDisabled(..) => FeatureDisabled,
| Self::Federation { error, .. } => response::ruma_error_kind(error).clone(),
| Self::Ruma { source, .. } => response::ruma_error_kind(source).clone(),
| Self::BadRequest { kind, .. } | Self::Request { kind, .. } => kind.clone(),
| Self::FeatureDisabled { .. } => FeatureDisabled,
| _ => Unknown,
}
}
@@ -184,13 +424,15 @@ impl Error {
use http::StatusCode;
match self {
| Self::Federation(_, error) | Self::Ruma(error) => error.status_code,
| Self::Request(kind, _, code) => response::status_code(kind, *code),
| Self::BadRequest(kind, ..) => response::bad_request_code(kind),
| Self::FeatureDisabled(..) => response::bad_request_code(&self.kind()),
| Self::Reqwest(error) => error.status().unwrap_or(StatusCode::INTERNAL_SERVER_ERROR),
| Self::Conflict(_) => StatusCode::CONFLICT,
| Self::Io(error) => response::io_error_code(error.kind()),
| Self::Federation { error, .. } => error.status_code,
| Self::Ruma { source, .. } => source.status_code,
| Self::Request { kind, code, .. } => response::status_code(kind, *code),
| Self::BadRequest { kind, .. } => response::bad_request_code(kind),
| Self::FeatureDisabled { .. } => response::bad_request_code(&self.kind()),
| Self::Reqwest { source, .. } =>
source.status().unwrap_or(StatusCode::INTERNAL_SERVER_ERROR),
| Self::Conflict { .. } => StatusCode::CONFLICT,
| Self::Io { source, .. } => response::io_error_code(source.kind()),
| _ => StatusCode::INTERNAL_SERVER_ERROR,
}
}
@@ -203,16 +445,46 @@ impl Error {
pub fn is_not_found(&self) -> bool { self.status_code() == http::StatusCode::NOT_FOUND }
}
impl std::fmt::Debug for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.message())
}
// Debug is already derived by Snafu
/// Macro to reduce boilerplate for From implementations using Snafu context
macro_rules! impl_from_snafu {
($source_ty:ty => $context:ident) => {
impl From<$source_ty> for Error {
fn from(source: $source_ty) -> Self { $context.into_error(source) }
}
};
}
/// Macro for From impls that format messages into ErrSnafu or other
/// message-based contexts
macro_rules! impl_from_message {
($source_ty:ty => $context:ident, $msg:expr) => {
impl From<$source_ty> for Error {
fn from(source: $source_ty) -> Self {
let message: Cow<'static, str> = format!($msg, source).into();
$context { message }.build()
}
}
};
}
/// Macro for From impls with constant messages (no formatting)
macro_rules! impl_from_const_message {
($source_ty:ty => $context:ident, $msg:expr) => {
impl From<$source_ty> for Error {
fn from(_source: $source_ty) -> Self {
let message: Cow<'static, str> = $msg.into();
$context { message }.build()
}
}
};
}
impl<T> From<PoisonError<T>> for Error {
#[cold]
#[inline(never)]
fn from(e: PoisonError<T>) -> Self { Self::Poison(e.to_string().into()) }
fn from(e: PoisonError<T>) -> Self { PoisonSnafu { message: e.to_string() }.build() }
}
#[allow(clippy::fallible_impl_from)]
@@ -224,6 +496,43 @@ impl From<Infallible> for Error {
}
}
// Implementations using the macro
impl_from_snafu!(std::io::Error => IoSnafu);
impl_from_snafu!(std::string::FromUtf8Error => FromUtf8Snafu);
impl_from_snafu!(regex::Error => RegexSnafu);
impl_from_snafu!(ruma::http_headers::ContentDispositionParseError => ContentDispositionSnafu);
impl_from_snafu!(ruma::api::error::IntoHttpError => IntoHttpSnafu);
impl_from_snafu!(ruma::JsTryFromIntError => JsTryFromIntSnafu);
impl_from_snafu!(ruma::CanonicalJsonError => CanonicalJsonSnafu);
impl_from_snafu!(axum::extract::rejection::PathRejection => PathSnafu);
impl_from_snafu!(clap::error::Error => ClapSnafu);
impl_from_snafu!(ruma::MxcUriError => MxcSnafu);
impl_from_snafu!(serde_saphyr::ser_error::Error => YamlSerSnafu);
impl_from_snafu!(toml::de::Error => TomlDeSnafu);
impl_from_snafu!(http::header::InvalidHeaderValue => HttpHeaderSnafu);
impl_from_snafu!(serde_json::Error => JsonSnafu);
// Custom implementations using message formatting
impl_from_const_message!(std::fmt::Error => ErrSnafu, "formatting error");
impl_from_message!(std::str::Utf8Error => ErrSnafu, "UTF-8 error: {}");
impl_from_message!(std::num::TryFromIntError => ArithmeticSnafu, "integer conversion error: {}");
impl_from_message!(tracing_subscriber::reload::Error => ErrSnafu, "tracing reload error: {}");
impl_from_message!(reqwest::Error => ErrSnafu, "HTTP client error: {}");
impl_from_message!(ruma::signatures::Error => ErrSnafu, "Signature error: {}");
impl_from_message!(ruma::IdParseError => ErrSnafu, "ID parse error: {}");
impl_from_message!(std::num::ParseIntError => ErrSnafu, "Integer parse error: {}");
impl_from_message!(std::array::TryFromSliceError => ErrSnafu, "Slice conversion error: {}");
impl_from_message!(tokio::task::JoinError => ErrSnafu, "Task join error: {}");
impl_from_message!(serde_saphyr::Error => ErrSnafu, "YAML error: {}");
// Generic implementation for CapacityError
impl<T> From<arrayvec::CapacityError<T>> for Error {
fn from(_source: arrayvec::CapacityError<T>) -> Self {
let message: Cow<'static, str> = "capacity error: buffer is full".into();
ErrSnafu { message }.build()
}
}
#[cold]
#[inline(never)]
pub fn infallible(_e: &Infallible) {
+8 -5
View File
@@ -15,13 +15,16 @@ impl Error {
#[must_use]
#[inline]
pub fn from_panic(e: Box<dyn Any + Send>) -> Self { Self::Panic(debug::panic_str(&e), e) }
pub fn from_panic(e: Box<dyn Any + Send>) -> Self {
use super::PanicSnafu;
PanicSnafu { message: debug::panic_str(&e), panic: e }.build()
}
#[inline]
pub fn into_panic(self) -> Box<dyn Any + Send + 'static> {
match self {
| Self::Panic(_, e) | Self::PanicAny(e) => e,
| Self::JoinError(e) => e.into_panic(),
| Self::Panic { panic, .. } | Self::PanicAny { panic, .. } => panic,
| Self::JoinError { source, .. } => source.into_panic(),
| _ => Box::new(self),
}
}
@@ -37,8 +40,8 @@ impl Error {
#[inline]
pub fn is_panic(&self) -> bool {
match &self {
| Self::Panic(..) | Self::PanicAny(..) => true,
| Self::JoinError(e) => e.is_panic(),
| Self::Panic { .. } | Self::PanicAny { .. } => true,
| Self::JoinError { source, .. } => source.is_panic(),
| _ => false,
}
}
+2 -2
View File
@@ -47,8 +47,8 @@ impl axum::response::IntoResponse for Error {
impl From<Error> for UiaaResponse {
#[inline]
fn from(error: Error) -> Self {
if let Error::Uiaa(uiaainfo) = error {
return Self::AuthResponse(uiaainfo);
if let Error::Uiaa { info, .. } = error {
return Self::AuthResponse(info);
}
let body = ErrorBody::Standard {
+8 -2
View File
@@ -5,9 +5,15 @@ use serde::{de, ser};
use crate::Error;
impl de::Error for Error {
fn custom<T: Display + ToString>(msg: T) -> Self { Self::SerdeDe(msg.to_string().into()) }
fn custom<T: Display + ToString>(msg: T) -> Self {
let message: std::borrow::Cow<'static, str> = msg.to_string().into();
super::SerdeDeSnafu { message }.build()
}
}
impl ser::Error for Error {
fn custom<T: Display + ToString>(msg: T) -> Self { Self::SerdeSer(msg.to_string().into()) }
fn custom<T: Display + ToString>(msg: T) -> Self {
let message: std::borrow::Cow<'static, str> = msg.to_string().into();
super::SerdeSerSnafu { message }.build()
}
}
+10 -3
View File
@@ -1,7 +1,7 @@
use ruma::{RoomVersionId, canonical_json::redact_content_in_place};
use serde_json::{Value as JsonValue, json, value::to_raw_value};
use crate::{Error, Result, err, implement};
use crate::{Result, err, implement};
#[implement(super::Pdu)]
pub fn redact(&mut self, room_version_id: &RoomVersionId, reason: JsonValue) -> Result {
@@ -10,8 +10,15 @@ pub fn redact(&mut self, room_version_id: &RoomVersionId, reason: JsonValue) ->
let mut content = serde_json::from_str(self.content.get())
.map_err(|e| err!(Request(BadJson("Failed to deserialize content into type: {e}"))))?;
redact_content_in_place(&mut content, room_version_id, self.kind.to_string())
.map_err(|e| Error::Redaction(self.sender.server_name().to_owned(), e))?;
redact_content_in_place(&mut content, room_version_id, self.kind.to_string()).map_err(
|error| {
crate::error::RedactionSnafu {
server: self.sender.server_name().to_owned(),
error,
}
.build()
},
)?;
let reason = serde_json::to_value(reason).expect("Failed to preserialize reason");
+7 -5
View File
@@ -27,7 +27,7 @@ use serde_json::{
use crate::{
matrix::{Event, Pdu, pdu::EventHash},
state_res::{self as state_res, Error, Result, StateMap},
state_res::{self as state_res, Error, Result, StateMap, error::NotFoundSnafu},
};
static SERVER_TIMESTAMP: AtomicU64 = AtomicU64::new(0);
@@ -170,10 +170,12 @@ struct TestStore<E: Event>(HashMap<OwnedEventId, E>);
#[allow(unused)]
impl<E: Event + Clone> TestStore<E> {
fn get_event(&self, room_id: &RoomId, event_id: &EventId) -> Result<E> {
self.0
.get(event_id)
.cloned()
.ok_or_else(|| Error::NotFound(format!("{} not found", event_id)))
self.0.get(event_id).cloned().ok_or_else(|| {
NotFoundSnafu {
message: format!("{} not found", event_id),
}
.build()
})
}
/// Returns the events that correspond to the `event_ids` sorted in the same
+27 -10
View File
@@ -1,23 +1,40 @@
use serde_json::Error as JsonError;
use thiserror::Error;
use snafu::{IntoError, prelude::*};
/// Represents the various errors that arise when resolving state.
#[derive(Error, Debug)]
#[derive(Debug, Snafu)]
#[snafu(visibility(pub))]
#[non_exhaustive]
pub enum Error {
/// A deserialization error.
#[error(transparent)]
SerdeJson(#[from] JsonError),
#[snafu(display("JSON error: {source}"))]
SerdeJson {
source: JsonError,
backtrace: snafu::Backtrace,
},
/// The given option or version is unsupported.
#[error("Unsupported room version: {0}")]
Unsupported(String),
#[snafu(display("Unsupported room version: {version}"))]
Unsupported {
version: String,
backtrace: snafu::Backtrace,
},
/// The given event was not found.
#[error("Not found error: {0}")]
NotFound(String),
#[snafu(display("Not found error: {message}"))]
NotFound {
message: String,
backtrace: snafu::Backtrace,
},
/// Invalid fields in the given PDU.
#[error("Invalid PDU: {0}")]
InvalidPdu(String),
#[snafu(display("Invalid PDU: {message}"))]
InvalidPdu {
message: String,
backtrace: snafu::Backtrace,
},
}
impl From<serde_json::Error> for Error {
fn from(source: serde_json::Error) -> Self { SerdeJsonSnafu.into_error(source) }
}
+4 -3
View File
@@ -24,6 +24,7 @@ use serde_json::{from_str as from_json_str, value::RawValue as RawJsonValue};
use super::{
Error, Event, Result, StateEventType, StateKey, TimelineEventType,
error::InvalidPduSnafu,
power_levels::{
deserialize_power_levels, deserialize_power_levels_content_fields,
deserialize_power_levels_content_invite, deserialize_power_levels_content_redact,
@@ -383,8 +384,8 @@ where
return Ok(false);
}
let target_user =
<&UserId>::try_from(state_key).map_err(|e| Error::InvalidPdu(format!("{e}")))?;
let target_user = <&UserId>::try_from(state_key)
.map_err(|e| InvalidPduSnafu { message: format!("{e}") }.build())?;
let user_for_join_auth = content
.join_authorised_via_users_server
@@ -461,7 +462,7 @@ where
?sender_membership_event_content,
"Sender membership event content missing membership field"
);
return Err(Error::InvalidPdu("Missing membership field".to_owned()));
return Err(InvalidPduSnafu { message: "Missing membership field" }.build());
};
let membership_state = membership_state.deserialize()?;
+42 -30
View File
@@ -29,18 +29,18 @@ use ruma::{
};
use serde_json::from_str as from_json_str;
pub(crate) use self::error::Error;
pub(crate) use self::error::{Error, InvalidPduSnafu, NotFoundSnafu};
use self::power_levels::PowerLevelsContentFields;
pub use self::{
event_auth::{auth_check, auth_types_for_event},
room_version::RoomVersion,
};
use super::{Event, StateKey};
use crate::{
debug, debug_error, err,
matrix::{Event, StateKey},
debug, debug_error,
state_res::room_version::StateResolutionVersion,
trace,
utils::stream::{BroadbandExt, IterStream, ReadyExt, TryBroadbandExt, WidebandExt},
utils::stream::{BroadbandExt, IterStream, ReadyExt, TryBroadbandExt},
warn,
};
@@ -118,7 +118,10 @@ where
let csg = calculate_conflicted_subgraph(&conflicting, event_fetch)
.await
.ok_or_else(|| {
Error::InvalidPdu("Failed to calculate conflicted subgraph".to_owned())
InvalidPduSnafu {
message: "Failed to calculate conflicted subgraph",
}
.build()
})?;
debug!(count = csg.len(), "conflicted subgraph");
trace!(set = ?csg, "conflicted subgraph");
@@ -149,10 +152,11 @@ where
let control_events: Vec<_> = all_conflicted
.iter()
.stream()
.wide_filter_map(async |id| {
is_power_event_id(id, &event_fetch)
.broad_filter_map(async |id| {
event_fetch(id.clone())
.await
.then_some(id.clone())
.filter(|event| is_power_event(&event))
.map(|_| id.clone())
})
.collect()
.await;
@@ -314,7 +318,10 @@ where
trace!(event_id = event_id.as_str(), "fetching event for its auth events");
let evt = fetch_event(event_id.clone()).await;
if evt.is_none() {
err!("could not fetch event {} to calculate conflicted subgraph", event_id);
tracing::error!(
"could not fetch event {} to calculate conflicted subgraph",
event_id
);
path.pop();
continue;
}
@@ -402,11 +409,11 @@ where
let fetcher = async |event_id: OwnedEventId| {
let pl = *event_to_pl
.get(&event_id)
.ok_or_else(|| Error::NotFound(String::new()))?;
.ok_or_else(|| NotFoundSnafu { message: "" }.build())?;
let ev = fetch_event(event_id)
.await
.ok_or_else(|| Error::NotFound(String::new()))?;
.ok_or_else(|| NotFoundSnafu { message: "" }.build())?;
Ok((pl, ev.origin_server_ts()))
};
@@ -612,9 +619,12 @@ where
let events_to_check: Vec<_> = events_to_check
.map(Result::Ok)
.broad_and_then(async |event_id| {
fetch_event(event_id.to_owned())
.await
.ok_or_else(|| Error::NotFound(format!("Failed to find {event_id}")))
fetch_event(event_id.to_owned()).await.ok_or_else(|| {
NotFoundSnafu {
message: format!("Failed to find {event_id}"),
}
.build()
})
})
.try_collect()
.boxed()
@@ -653,7 +663,7 @@ where
trace!(event_id = event.event_id().as_str(), "checking event");
let state_key = event
.state_key()
.ok_or_else(|| Error::InvalidPdu("State event had no state key".to_owned()))?;
.ok_or_else(|| InvalidPduSnafu { message: "State event had no state key" }.build())?;
let auth_types = auth_types_for_event(
event.event_type(),
@@ -669,13 +679,14 @@ where
trace!("room version uses hashed IDs, manually fetching create event");
let create_event_id_raw = event.room_id_or_hash().as_str().replace('!', "$");
let create_event_id = EventId::parse(&create_event_id_raw).map_err(|e| {
Error::InvalidPdu(format!(
"Failed to parse create event ID from room ID/hash: {e}"
))
InvalidPduSnafu {
message: format!("Failed to parse create event ID from room ID/hash: {e}"),
}
.build()
})?;
let create_event = fetch_event(create_event_id.into()).await.ok_or_else(|| {
NotFoundSnafu { message: "Failed to find create event" }.build()
})?;
let create_event = fetch_event(create_event_id.into())
.await
.ok_or_else(|| Error::NotFound("Failed to find create event".into()))?;
auth_state.insert(create_event.event_type().with_state_key(""), create_event);
}
for aid in event.auth_events() {
@@ -686,7 +697,7 @@ where
auth_state.insert(
ev.event_type()
.with_state_key(ev.state_key().ok_or_else(|| {
Error::InvalidPdu("State event had no state key".to_owned())
InvalidPduSnafu { message: "State event had no state key" }.build()
})?),
ev.clone(),
);
@@ -801,13 +812,13 @@ where
let event = fetch_event(p.clone())
.await
.ok_or_else(|| Error::NotFound(format!("Failed to find {p}")))?;
.ok_or_else(|| NotFoundSnafu { message: format!("Failed to find {p}") }.build())?;
pl = None;
for aid in event.auth_events() {
let ev = fetch_event(aid.to_owned())
.await
.ok_or_else(|| Error::NotFound(format!("Failed to find {aid}")))?;
let ev = fetch_event(aid.to_owned()).await.ok_or_else(|| {
NotFoundSnafu { message: format!("Failed to find {aid}") }.build()
})?;
if is_type_and_key(&ev, &TimelineEventType::RoomPowerLevels, "") {
pl = Some(aid.to_owned());
@@ -869,9 +880,9 @@ where
event = None;
for aid in sort_ev.auth_events() {
let aev = fetch_event(aid.to_owned())
.await
.ok_or_else(|| Error::NotFound(format!("Failed to find {aid}")))?;
let aev = fetch_event(aid.to_owned()).await.ok_or_else(|| {
NotFoundSnafu { message: format!("Failed to find {aid}") }.build()
})?;
if is_type_and_key(&aev, &TimelineEventType::RoomPowerLevels, "") {
event = Some(aev);
@@ -915,6 +926,7 @@ async fn add_event_and_auth_chain_to_graph<E, F, Fut>(
}
}
#[allow(dead_code)]
async fn is_power_event_id<E, F, Fut>(event_id: &EventId, fetch: &F) -> bool
where
F: Fn(OwnedEventId) -> Fut + Sync,
@@ -1046,7 +1058,7 @@ mod tests {
// don't remove any events so we know it sorts them all correctly
let mut events_to_sort = events.keys().cloned().collect::<Vec<_>>();
events_to_sort.shuffle(&mut rand::thread_rng());
events_to_sort.shuffle(&mut rand::rng());
let power_level = resolved_power
.get(&(StateEventType::RoomPowerLevels, "".into()))
+6 -2
View File
@@ -1,6 +1,6 @@
use ruma::RoomVersionId;
use super::{Error, Result};
use super::{Result, error::UnsupportedSnafu};
#[derive(Debug)]
#[allow(clippy::exhaustive_enums)]
@@ -163,7 +163,11 @@ impl RoomVersion {
| RoomVersionId::V10 => Self::V10,
| RoomVersionId::V11 => Self::V11,
| RoomVersionId::V12 => Self::V12,
| ver => return Err(Error::Unsupported(format!("found version `{ver}`"))),
| ver =>
return Err(UnsupportedSnafu {
version: format!("found version `{ver}`"),
}
.build()),
})
}
}
+2 -2
View File
@@ -22,7 +22,7 @@ use serde_json::{
value::{RawValue as RawJsonValue, to_raw_value as to_raw_json_value},
};
use super::auth_types_for_event;
use super::{auth_types_for_event, error::NotFoundSnafu};
use crate::{
Result, RoomVersion, info,
matrix::{Event, EventTypeExt, Pdu, StateMap, pdu::EventHash},
@@ -232,7 +232,7 @@ impl<E: Event + Clone> TestStore<E> {
self.0
.get(event_id)
.cloned()
.ok_or_else(|| super::Error::NotFound(format!("{event_id} not found")))
.ok_or_else(|| NotFoundSnafu { message: format!("{event_id} not found") }.build())
.map_err(Into::into)
}
+2
View File
@@ -14,9 +14,11 @@ pub mod utils;
pub use ::arrayvec;
pub use ::http;
pub use ::paste;
pub use ::ruma;
pub use ::smallstr;
pub use ::smallvec;
pub use ::snafu;
pub use ::toml;
pub use ::tracing;
pub use config::Config;
+1 -1
View File
@@ -28,7 +28,7 @@ fn init_argon() -> Argon2<'static> {
}
pub(super) fn password(password: &str) -> Result<String> {
let salt = SaltString::generate(rand::thread_rng());
let salt = SaltString::generate(rand_core::OsRng);
ARGON
.get_or_init(init_argon)
.hash_password(password.as_bytes(), &salt)
+7 -10
View File
@@ -4,16 +4,16 @@ use std::{
};
use arrayvec::ArrayString;
use rand::{Rng, seq::SliceRandom, thread_rng};
use rand::{RngExt, seq::SliceRandom};
pub fn shuffle<T>(vec: &mut [T]) {
let mut rng = thread_rng();
let mut rng = rand::rng();
vec.shuffle(&mut rng);
}
pub fn string(length: usize) -> String {
thread_rng()
.sample_iter(&rand::distributions::Alphanumeric)
rand::rng()
.sample_iter(&rand::distr::Alphanumeric)
.take(length)
.map(char::from)
.collect()
@@ -22,8 +22,8 @@ pub fn string(length: usize) -> String {
#[inline]
pub fn string_array<const LENGTH: usize>() -> ArrayString<LENGTH> {
let mut ret = ArrayString::<LENGTH>::new();
thread_rng()
.sample_iter(&rand::distributions::Alphanumeric)
rand::rng()
.sample_iter(&rand::distr::Alphanumeric)
.take(LENGTH)
.map(char::from)
.for_each(|c| ret.push(c));
@@ -40,7 +40,4 @@ pub fn time_from_now_secs(range: Range<u64>) -> SystemTime {
}
#[must_use]
pub fn secs(range: Range<u64>) -> Duration {
let mut rng = thread_rng();
Duration::from_secs(rng.gen_range(range))
}
pub fn secs(range: Range<u64>) -> Duration { Duration::from_secs(rand::random_range(range)) }
+5 -1
View File
@@ -2,6 +2,8 @@
use std::{cell::Cell, fmt::Debug, path::PathBuf, sync::LazyLock};
use snafu::IntoError;
use crate::{Result, is_equal_to};
type Id = usize;
@@ -142,7 +144,9 @@ pub fn getcpu() -> Result<usize> {
#[cfg(not(target_os = "linux"))]
#[inline]
pub fn getcpu() -> Result<usize> { Err(crate::Error::Io(std::io::ErrorKind::Unsupported.into())) }
pub fn getcpu() -> Result<usize> {
Err(crate::error::IoSnafu.into_error(std::io::ErrorKind::Unsupported.into()))
}
fn query_cores_available() -> impl Iterator<Item = Id> {
core_affinity::get_core_ids()
+12 -7
View File
@@ -255,7 +255,10 @@ impl<'a, 'de: 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
| "$serde_json::private::RawValue" => visitor.visit_map(self),
| "Cbor" => visitor
.visit_newtype_struct(&mut minicbor_serde::Deserializer::new(self.record_trail()))
.map_err(|e| Self::Error::SerdeDe(e.to_string().into())),
.map_err(|e| {
let message: std::borrow::Cow<'static, str> = e.to_string().into();
conduwuit_core::error::SerdeDeSnafu { message }.build()
}),
| _ => visitor.visit_newtype_struct(self),
}
@@ -313,9 +316,10 @@ impl<'a, 'de: 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
let end = self.pos.saturating_add(BYTES).min(self.buf.len());
let bytes: ArrayVec<u8, BYTES> = self.buf[self.pos..end].try_into()?;
let bytes = bytes
.into_inner()
.map_err(|_| Self::Error::SerdeDe("i64 buffer underflow".into()))?;
let bytes = bytes.into_inner().map_err(|_| {
let message: std::borrow::Cow<'static, str> = "i64 buffer underflow".into();
conduwuit_core::error::SerdeDeSnafu { message }.build()
})?;
self.inc_pos(BYTES);
visitor.visit_i64(i64::from_be_bytes(bytes))
@@ -345,9 +349,10 @@ impl<'a, 'de: 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
let end = self.pos.saturating_add(BYTES).min(self.buf.len());
let bytes: ArrayVec<u8, BYTES> = self.buf[self.pos..end].try_into()?;
let bytes = bytes
.into_inner()
.map_err(|_| Self::Error::SerdeDe("u64 buffer underflow".into()))?;
let bytes = bytes.into_inner().map_err(|_| {
let message: std::borrow::Cow<'static, str> = "u64 buffer underflow".into();
conduwuit_core::error::SerdeDeSnafu { message }.build()
})?;
self.inc_pos(BYTES);
visitor.visit_u64(u64::from_be_bytes(bytes))
+4 -1
View File
@@ -199,7 +199,10 @@ impl<W: Write> ser::Serializer for &mut Serializer<'_, W> {
value
.serialize(&mut Serializer::new(&mut Writer::new(&mut self.out)))
.map_err(|e| Self::Error::SerdeSer(e.to_string().into()))
.map_err(|e| {
let message: std::borrow::Cow<'static, str> = e.to_string().into();
conduwuit_core::error::SerdeSerSnafu { message }.build()
})
},
| _ => unhandled!("Unrecognized serialization Newtype {name:?}"),
}
+7 -2
View File
@@ -1,4 +1,4 @@
use std::sync::Arc;
use std::{borrow::Cow, sync::Arc};
use axum::{Router, response::IntoResponse};
use conduwuit::Error;
@@ -18,5 +18,10 @@ pub(crate) fn build(services: &Arc<Services>) -> (Router, Guard) {
}
async fn not_found(_uri: Uri) -> impl IntoResponse {
Error::Request(ErrorKind::Unrecognized, "Not Found".into(), StatusCode::NOT_FOUND)
Error::Request {
kind: ErrorKind::Unrecognized,
message: Cow::Borrowed("Not Found"),
code: StatusCode::NOT_FOUND,
backtrace: None,
}
}
+1 -3
View File
@@ -20,7 +20,6 @@ use std::{sync::Arc, time::Duration};
use async_trait::async_trait;
use conduwuit::{Result, Server, debug, error, warn};
use database::{Deserialized, Map};
use rand::Rng;
use ruma::events::{Mentions, room::message::RoomMessageEventContent};
use serde::Deserialize;
use tokio::{
@@ -100,8 +99,7 @@ impl crate::Service for Service {
}
let first_check_jitter = {
let mut rng = rand::thread_rng();
let jitter_percent = rng.gen_range(-50.0..=10.0);
let jitter_percent = rand::random_range(-50.0..=10.0);
self.interval.mul_f64(1.0 + jitter_percent / 100.0)
};
+4 -4
View File
@@ -147,11 +147,11 @@ impl Service {
// same appservice)
if let Ok(existing) = self.find_from_token(&registration.as_token).await {
if existing.registration.id != registration.id {
return Err(err!(Request(InvalidParam(
return Err!(Request(InvalidParam(
"Cannot register appservice: Token is already used by appservice '{}'. \
Please generate a different token.",
existing.registration.id
))));
)));
}
}
@@ -163,10 +163,10 @@ impl Service {
.await
.is_ok()
{
return Err(err!(Request(InvalidParam(
return Err!(Request(InvalidParam(
"Cannot register appservice: The provided token is already in use by a user \
device. Please generate a different token for the appservice."
))));
)));
}
self.db
+2 -5
View File
@@ -2,7 +2,7 @@ use std::{fmt::Debug, mem};
use bytes::Bytes;
use conduwuit::{
Err, Error, Result, debug, debug::INFO_SPAN_LEVEL, debug_error, debug_warn, err,
Err, Result, debug, debug::INFO_SPAN_LEVEL, debug_error, debug_warn, err,
error::inspect_debug_log, implement, trace,
};
use http::{HeaderValue, header::AUTHORIZATION};
@@ -179,10 +179,7 @@ async fn into_http_response(
debug!("Got {status:?} for {method} {url}");
if !status.is_success() {
return Err(Error::Federation(
dest.to_owned(),
RumaError::from_http_response(http_response),
));
return Err!(Federation(dest.to_owned(), RumaError::from_http_response(http_response),));
}
Ok(http_response)
+2 -2
View File
@@ -35,7 +35,7 @@ pub async fn fetch_remote_thumbnail(
.fetch_thumbnail_authenticated(mxc, user, server, timeout_ms, dim)
.await;
if let Err(Error::Request(NotFound, ..)) = &result {
if let Err(Error::Request { kind: NotFound, .. }) = &result {
return self
.fetch_thumbnail_unauthenticated(mxc, user, server, timeout_ms, dim)
.await;
@@ -67,7 +67,7 @@ pub async fn fetch_remote_content(
);
});
if let Err(Error::Request(Unrecognized, ..)) = &result {
if let Err(Error::Request { kind: Unrecognized, .. }) = &result {
return self
.fetch_content_unauthenticated(mxc, user, server, timeout_ms)
.await;
@@ -112,7 +112,14 @@ where
{
let event_fetch = |event_id| self.event_fetch(event_id);
let event_exists = |event_id| self.event_exists(event_id);
state_res::resolve(room_version, state_sets, auth_chain_sets, &event_fetch, &event_exists)
.map_err(|e| err!(error!("State resolution failed: {e:?}")))
.await
Ok(
state_res::resolve(
room_version,
state_sets,
auth_chain_sets,
&event_fetch,
&event_exists,
)
.await?,
)
}
+2 -2
View File
@@ -3,7 +3,7 @@ use std::{
str::FromStr,
};
use conduwuit::{Error, Result};
use conduwuit::{Err, Error, Result};
use ruma::{UInt, api::client::error::ErrorKind};
use crate::rooms::short::ShortRoomId;
@@ -57,7 +57,7 @@ impl FromStr for PaginationToken {
if let Some(token) = pag_tok() {
Ok(token)
} else {
Err(Error::BadRequest(ErrorKind::InvalidParam, "invalid token"))
Err!(BadRequest(ErrorKind::InvalidParam, "invalid token"))
}
}
}
+4 -5
View File
@@ -75,10 +75,7 @@ pub async fn create_hash_and_sign_event(
let content: RoomCreateEventContent = serde_json::from_str(content.get())?;
Ok(content.room_version)
} else {
Err(Error::InconsistentRoomState(
"non-create event for room of unknown version",
room_id,
))
Err!(InconsistentRoomState("non-create event for room of unknown version", room_id))
}
}
let PduBuilder {
@@ -275,7 +272,9 @@ pub async fn create_hash_and_sign_event(
.hash_and_sign_event(&mut pdu_json, &room_version_id)
{
return match e {
| Error::Signatures(ruma::signatures::Error::PduSize) => {
| Error::Signatures { source, .. }
if matches!(source, ruma::signatures::Error::PduSize) =>
{
Err!(Request(TooLarge("Message/PDU is too long (exceeds 65535 bytes)")))
},
| _ => Err!(Request(Unknown(warn!("Signing event failed: {e}")))),
+7 -5
View File
@@ -385,11 +385,13 @@ fn num_senders(args: &crate::Args<'_>) -> usize {
const MIN_SENDERS: usize = 1;
// Limit the number of senders to the number of workers threads or number of
// cores, conservatively.
let max_senders = args
.server
.metrics
.num_workers()
.min(available_parallelism());
let mut max_senders = args.server.metrics.num_workers();
// Work around some platforms not returning the number of cores.
let num_cores = available_parallelism();
if num_cores > 0 {
max_senders = max_senders.min(num_cores);
}
// If the user doesn't override the default 0, this is intended to then default
// to 1 for now as multiple senders is experimental.
+4 -4
View File
@@ -1,7 +1,7 @@
use std::{collections::BTreeMap, sync::Arc};
use conduwuit::{
Err, Error, Result, SyncRwLock, err, error, implement, utils,
Err, Result, SyncRwLock, err, error, implement, utils,
utils::{hash, string::EMPTY},
};
use database::{Deserialized, Json, Map};
@@ -117,7 +117,7 @@ pub async fn try_auth(
} else if let Some(username) = user {
username
} else {
return Err(Error::BadRequest(
return Err!(BadRequest(
ErrorKind::Unrecognized,
"Identifier type not recognized.",
));
@@ -125,7 +125,7 @@ pub async fn try_auth(
#[cfg(not(feature = "element_hacks"))]
let Some(UserIdentifier::UserIdOrLocalpart(username)) = identifier else {
return Err(Error::BadRequest(
return Err!(BadRequest(
ErrorKind::Unrecognized,
"Identifier type not recognized.",
));
@@ -135,7 +135,7 @@ pub async fn try_auth(
username.clone(),
self.services.globals.server_name(),
)
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "User ID is invalid."))?;
.map_err(|_| err!(BadRequest(ErrorKind::InvalidParam, "User ID is invalid.")))?;
// Check if the access token being used matches the credentials used for UIAA
if user_id.localpart() != user_id_from_username.localpart() {
+12 -6
View File
@@ -184,6 +184,12 @@ impl Service {
password: Option<&str>,
origin: Option<&str>,
) -> Result<()> {
if !self.services.globals.user_is_local(user_id)
&& (password.is_some() || origin.is_some())
{
return Err!("Cannot create a nonlocal user with a set password or origin");
}
self.db
.userid_origin
.insert(user_id, origin.unwrap_or("password"));
@@ -755,13 +761,13 @@ impl Service {
.keys
.into_values();
let self_signing_key_id = self_signing_key_ids.next().ok_or(Error::BadRequest(
let self_signing_key_id = self_signing_key_ids.next().ok_or(err!(BadRequest(
ErrorKind::InvalidParam,
"Self signing key contained no key.",
))?;
)))?;
if self_signing_key_ids.next().is_some() {
return Err(Error::BadRequest(
return Err!(BadRequest(
ErrorKind::InvalidParam,
"Self signing key contained more than one key.",
));
@@ -1433,13 +1439,13 @@ pub fn parse_master_key(
let master_key = master_key
.deserialize()
.map_err(|_| Error::BadRequest(ErrorKind::InvalidParam, "Invalid master key"))?;
.map_err(|_| err!(BadRequest(ErrorKind::InvalidParam, "Invalid master key")))?;
let mut master_key_ids = master_key.keys.values();
let master_key_id = master_key_ids
.next()
.ok_or(Error::BadRequest(ErrorKind::InvalidParam, "Master key contained no key."))?;
.ok_or(err!(BadRequest(ErrorKind::InvalidParam, "Master key contained no key.")))?;
if master_key_ids.next().is_some() {
return Err(Error::BadRequest(
return Err!(BadRequest(
ErrorKind::InvalidParam,
"Master key contained more than one key.",
));
+1 -1
View File
@@ -25,7 +25,7 @@ axum.workspace = true
futures.workspace = true
tracing.workspace = true
rand.workspace = true
thiserror.workspace = true
snafu.workspace = true
[lints]
workspace = true
+12 -4
View File
@@ -8,6 +8,7 @@ use axum::{
};
use conduwuit_build_metadata::{GIT_REMOTE_COMMIT_URL, GIT_REMOTE_WEB_URL, version_tag};
use conduwuit_service::state;
use snafu::{IntoError, prelude::*};
pub fn build() -> Router<state::State> {
Router::<state::State>::new()
@@ -48,10 +49,17 @@ async fn logo_handler() -> impl IntoResponse {
)
}
#[derive(Debug, thiserror::Error)]
#[derive(Debug, Snafu)]
enum WebError {
#[error("Failed to render template: {0}")]
Render(#[from] askama::Error),
#[snafu(display("Failed to render template: {source}"))]
Render {
source: askama::Error,
backtrace: snafu::Backtrace,
},
}
impl From<askama::Error> for WebError {
fn from(source: askama::Error) -> Self { RenderSnafu.into_error(source) }
}
impl IntoResponse for WebError {
@@ -66,7 +74,7 @@ impl IntoResponse for WebError {
let nonce = rand::random::<u64>().to_string();
let status = match &self {
| Self::Render(_) => StatusCode::INTERNAL_SERVER_ERROR,
| Self::Render { .. } => StatusCode::INTERNAL_SERVER_ERROR,
};
let tmpl = Error { nonce: &nonce, err: self };
if let Ok(body) = tmpl.render() {
+2 -2
View File
@@ -12,8 +12,8 @@ Server Error
</h1>
{%- match err -%}
{% when WebError::Render(err) -%}
<pre>{{ err }}</pre>
{% when WebError::Render { source, .. } -%}
<pre>{{ source }}</pre>
{% else -%} <p>An error occurred</p>
{%- endmatch -%}