From d0c767c23c1dff11400388c5a8dd9e43f68705f1 Mon Sep 17 00:00:00 2001
From: June Clementine Strawberry <june@3.dog>
Date: Sun, 9 Mar 2025 01:43:49 -0500
Subject: [PATCH] fix a few things to make some complement tests pass

Signed-off-by: June Clementine Strawberry <june@3.dog>
---
 src/api/client/membership.rs  |  4 +-
 src/api/client/room/create.rs |  8 +---
 src/api/client/session.rs     | 79 +++++++++++++++++++----------------
 src/service/media/preview.rs  | 23 ++++++----
 src/service/users/mod.rs      |  4 +-
 5 files changed, 60 insertions(+), 58 deletions(-)

diff --git a/src/api/client/membership.rs b/src/api/client/membership.rs
index 940c8639..3f77e69e 100644
--- a/src/api/client/membership.rs
+++ b/src/api/client/membership.rs
@@ -517,9 +517,7 @@ pub(crate) async fn invite_user_route(
 				join!(sender_ignored_recipient, recipient_ignored_by_sender);
 
 			if sender_ignored_recipient {
-				return Err!(Request(Forbidden(
-					"You cannot invite users you have ignored to rooms."
-				)));
+				return Ok(invite_user::v3::Response {});
 			}
 
 			if let Ok(target_user_membership) = services
diff --git a/src/api/client/room/create.rs b/src/api/client/room/create.rs
index 1b8294a5..bb06e966 100644
--- a/src/api/client/room/create.rs
+++ b/src/api/client/room/create.rs
@@ -239,9 +239,7 @@ pub(crate) async fn create_room_route(
 	if preset == RoomPreset::TrustedPrivateChat {
 		for invite in &body.invite {
 			if services.users.user_is_ignored(sender_user, invite).await {
-				return Err!(Request(Forbidden(
-					"You cannot invite users you have ignored to rooms."
-				)));
+				continue;
 			} else if services.users.user_is_ignored(invite, sender_user).await {
 				// silently drop the invite to the recipient if they've been ignored by the
 				// sender, pretend it worked
@@ -420,9 +418,7 @@ pub(crate) async fn create_room_route(
 	drop(state_lock);
 	for user_id in &body.invite {
 		if services.users.user_is_ignored(sender_user, user_id).await {
-			return Err!(Request(Forbidden(
-				"You cannot invite users you have ignored to rooms."
-			)));
+			continue;
 		} else if services.users.user_is_ignored(user_id, sender_user).await {
 			// silently drop the invite to the recipient if they've been ignored by the
 			// sender, pretend it worked
diff --git a/src/api/client/session.rs b/src/api/client/session.rs
index ab67ee18..3de625e4 100644
--- a/src/api/client/session.rs
+++ b/src/api/client/session.rs
@@ -3,7 +3,7 @@ use std::time::Duration;
 use axum::extract::State;
 use axum_client_ip::InsecureClientIp;
 use conduwuit::{Err, debug, err, info, utils::ReadyExt};
-use futures::{StreamExt, TryFutureExt};
+use futures::StreamExt;
 use ruma::{
 	UserId,
 	api::client::{
@@ -96,32 +96,50 @@ pub(crate) async fn login_route(
 				&services.config.server_name,
 			)?;
 
-			assert!(
-				services.globals.user_is_local(&user_id),
-				"User ID does not belong to this homeserver"
-			);
-			assert!(
-				services.globals.user_is_local(&lowercased_user_id),
-				"User ID does not belong to this homeserver"
-			);
+			if !services.globals.user_is_local(&user_id)
+				|| !services.globals.user_is_local(&lowercased_user_id)
+			{
+				return Err!(Request(Unknown("User ID does not belong to this homeserver")));
+			}
 
+			// first try the username as-is
 			let hash = services
 				.users
 				.password_hash(&user_id)
-				.or_else(|_| services.users.password_hash(&lowercased_user_id))
 				.await
-				.inspect_err(|e| debug!("{e}"))
-				.map_err(|_| err!(Request(Forbidden("Wrong username or password."))))?;
+				.inspect_err(|e| debug!("{e}"));
 
-			if hash.is_empty() {
-				return Err!(Request(UserDeactivated("The user has been deactivated")));
+			match hash {
+				| Ok(hash) => {
+					if hash.is_empty() {
+						return Err!(Request(UserDeactivated("The user has been deactivated")));
+					}
+
+					hash::verify_password(password, &hash)
+						.inspect_err(|e| debug!("{e}"))
+						.map_err(|_| err!(Request(Forbidden("Wrong username or password."))))?;
+
+					user_id
+				},
+				| Err(_e) => {
+					let hash_lowercased_user_id = services
+						.users
+						.password_hash(&lowercased_user_id)
+						.await
+						.inspect_err(|e| debug!("{e}"))
+						.map_err(|_| err!(Request(Forbidden("Wrong username or password."))))?;
+
+					if hash_lowercased_user_id.is_empty() {
+						return Err!(Request(UserDeactivated("The user has been deactivated")));
+					}
+
+					hash::verify_password(password, &hash_lowercased_user_id)
+						.inspect_err(|e| debug!("{e}"))
+						.map_err(|_| err!(Request(Forbidden("Wrong username or password."))))?;
+
+					lowercased_user_id
+				},
 			}
-
-			hash::verify_password(password, &hash)
-				.inspect_err(|e| debug!("{e}"))
-				.map_err(|_| err!(Request(Forbidden("Wrong username or password."))))?;
-
-			user_id
 		},
 		| login::v3::LoginInfo::Token(login::v3::Token { token }) => {
 			debug!("Got token login type");
@@ -153,24 +171,11 @@ pub(crate) async fn login_route(
 				}
 				.map_err(|e| err!(Request(InvalidUsername(warn!("Username is invalid: {e}")))))?;
 
-			let lowercased_user_id = UserId::parse_with_server_name(
-				user_id.localpart().to_lowercase(),
-				&services.config.server_name,
-			)?;
+			if !services.globals.user_is_local(&user_id) {
+				return Err!(Request(Unknown("User ID does not belong to this homeserver")));
+			}
 
-			assert!(
-				services.globals.user_is_local(&user_id),
-				"User ID does not belong to this homeserver"
-			);
-			assert!(
-				services.globals.user_is_local(&lowercased_user_id),
-				"User ID does not belong to this homeserver"
-			);
-
-			if !info.is_user_match(&user_id)
-				&& !info.is_user_match(&lowercased_user_id)
-				&& !emergency_mode_enabled
-			{
+			if !info.is_user_match(&user_id) && !emergency_mode_enabled {
 				return Err!(Request(Exclusive("Username is not in an appservice namespace.")));
 			}
 
diff --git a/src/service/media/preview.rs b/src/service/media/preview.rs
index 17216869..ba5be7d4 100644
--- a/src/service/media/preview.rs
+++ b/src/service/media/preview.rs
@@ -7,7 +7,7 @@
 
 use std::time::SystemTime;
 
-use conduwuit::{Err, Result, debug};
+use conduwuit::{Err, Result, debug, err};
 use conduwuit_core::implement;
 use ipaddress::IPAddress;
 use serde::Serialize;
@@ -64,28 +64,33 @@ pub async fn get_url_preview(&self, url: &Url) -> Result<UrlPreviewData> {
 async fn request_url_preview(&self, url: &Url) -> Result<UrlPreviewData> {
 	if let Ok(ip) = IPAddress::parse(url.host_str().expect("URL previously validated")) {
 		if !self.services.client.valid_cidr_range(&ip) {
-			return Err!(BadServerResponse("Requesting from this address is forbidden"));
+			return Err!(Request(Forbidden("Requesting from this address is forbidden")));
 		}
 	}
 
 	let client = &self.services.client.url_preview;
 	let response = client.head(url.as_str()).send().await?;
 
+	debug!(?url, "URL preview response headers: {:?}", response.headers());
+
 	if let Some(remote_addr) = response.remote_addr() {
+		debug!(?url, "URL preview response remote address: {:?}", remote_addr);
+
 		if let Ok(ip) = IPAddress::parse(remote_addr.ip().to_string()) {
 			if !self.services.client.valid_cidr_range(&ip) {
-				return Err!(BadServerResponse("Requesting from this address is forbidden"));
+				return Err!(Request(Forbidden("Requesting from this address is forbidden")));
 			}
 		}
 	}
 
-	let Some(content_type) = response
-		.headers()
-		.get(reqwest::header::CONTENT_TYPE)
-		.and_then(|x| x.to_str().ok())
-	else {
-		return Err!(Request(Unknown("Unknown Content-Type")));
+	let Some(content_type) = response.headers().get(reqwest::header::CONTENT_TYPE) else {
+		return Err!(Request(Unknown("Unknown or invalid Content-Type header")));
 	};
+
+	let content_type = content_type
+		.to_str()
+		.map_err(|e| err!(Request(Unknown("Unknown or invalid Content-Type header: {e}"))))?;
+
 	let data = match content_type {
 		| html if html.starts_with("text/html") => self.download_html(url.as_str()).await?,
 		| img if img.starts_with("image/") => self.download_image(url.as_str()).await?,
diff --git a/src/service/users/mod.rs b/src/service/users/mod.rs
index b3f5db88..5265e64b 100644
--- a/src/service/users/mod.rs
+++ b/src/service/users/mod.rs
@@ -278,11 +278,9 @@ impl Service {
 		initial_device_display_name: Option<String>,
 		client_ip: Option<String>,
 	) -> Result<()> {
-		// This method should never be called for nonexistent users. We shouldn't assert
-		// though...
 		if !self.exists(user_id).await {
 			return Err!(Request(InvalidParam(error!(
-				"Called create_device for non-existent {user_id}"
+				"Called create_device for non-existent user {user_id}"
 			))));
 		}