Merge pull request #12 from lionkor/overhaul-resources-folder-code

Overhaul resources folder code
This commit is contained in:
Luuk van Oijen 2023-11-24 09:44:11 +01:00 committed by GitHub
commit 145b74aefc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 66 additions and 21 deletions

View File

@ -1,5 +1,7 @@
use std::path::{Path, PathBuf};
use serde::Deserialize; use serde::Deserialize;
use uuid::Uuid; use uuid::Uuid;
use crate::fs_util;
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct Config { pub struct Config {
@ -47,10 +49,26 @@ pub struct GeneralSettings {
impl GeneralSettings { impl GeneralSettings {
pub fn is_auth_key_valid(&self) -> bool { pub fn is_auth_key_valid(&self) -> bool {
return if let Some(auth_key) = &self.auth_key { if let Some(auth_key) = &self.auth_key {
Uuid::parse_str(auth_key.as_str()).is_ok() Uuid::parse_str(auth_key.as_str()).is_ok()
} else { } else {
false false
} }
} }
/// Returns the client resource path, and ensures it exists.
/// Default is Resources/Client.
pub fn get_client_resource_folder(&self) -> anyhow::Result<String> {
let res_client_path = Path::new(self.resource_folder.as_str()).join("Client");
fs_util::ensure_path_exists(&res_client_path)?;
Ok(fs_util::path_to_string(res_client_path))
}
/// Returns the server resource path, and ensures it exists.
/// Default is Resources/Server.
pub fn get_server_resource_folder(&self) -> anyhow::Result<String> {
let res_server_path = Path::new(self.resource_folder.as_str()).join("Server");
fs_util::ensure_path_exists(&res_server_path)?;
Ok(fs_util::path_to_string(res_server_path))
}
} }

22
src/fs_util.rs Normal file
View File

@ -0,0 +1,22 @@
use std::path::{Path, PathBuf};
/// Ensures the given path exists by creating it if it doesn't.
pub fn ensure_path_exists(path: &PathBuf) -> anyhow::Result<()> {
if !path.exists() {
debug!("Path {:?} doesn't exist, creating it", path);
std::fs::create_dir_all(path)?;
}
Ok(())
}
/// Joins a parent folder and a sub-path, resolving the subpath beforehand to ensure that
/// the resulting path is still within the parent folder, regardless of ".." in the sub-path.
pub fn join_path_secure(parent: &Path, sub: &Path) -> anyhow::Result<PathBuf> {
Ok(parent.join(sub.canonicalize()?.as_path()))
}
/// Converts a PathBuf into a String in a lossy way. This is generally the way we want to do it
/// in the server.
pub fn path_to_string(path: PathBuf) -> String {
path.into_os_string().to_string_lossy().to_string()
}

View File

@ -2,6 +2,7 @@
#[macro_use] extern crate async_trait; #[macro_use] extern crate async_trait;
#[macro_use] extern crate lazy_static; #[macro_use] extern crate lazy_static;
use std::path::Path;
use argh::FromArgs; use argh::FromArgs;
use std::sync::Arc; use std::sync::Arc;
@ -12,6 +13,7 @@ mod tui;
mod server; mod server;
mod config; mod config;
mod heartbeat; mod heartbeat;
mod fs_util;
#[derive(FromArgs)] #[derive(FromArgs)]
/// BeamMP Server v3.3.0 /// BeamMP Server v3.3.0
@ -40,8 +42,12 @@ async fn main() {
.map_err(|_| error!("Failed to parse config file!")) .map_err(|_| error!("Failed to parse config file!"))
.expect("Failed to parse config file!"); .expect("Failed to parse config file!");
// TODO: This should not error lol
for entry in std::fs::read_dir("Resources/Client").expect("Failed to read Resources/Client!") { let client_resources = user_config.general
.get_client_resource_folder()
.expect("Failed to create the client resource folder");
for entry in std::fs::read_dir(client_resources).expect("Failed to read client resource folder!") {
if let Ok(entry) = entry { if let Ok(entry) = entry {
if entry.path().is_file() { if entry.path().is_file() {
if let Ok(metadata) = entry.metadata() { if let Ok(metadata) = entry.metadata() {

View File

@ -1,6 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::ops::DerefMut; use std::ops::DerefMut;
use std::path::Path;
use std::sync::atomic::{AtomicU8, Ordering}; use std::sync::atomic::{AtomicU8, Ordering};
use std::sync::Arc; use std::sync::Arc;
use std::time::Instant; use std::time::Instant;
@ -18,6 +19,7 @@ use nalgebra::*;
use serde::Deserialize; use serde::Deserialize;
use serde_aux::prelude::*; use serde_aux::prelude::*;
use crate::fs_util;
use super::backend::*; use super::backend::*;
use super::car::*; use super::car::*;
@ -225,21 +227,13 @@ impl Client {
// Handle file download // Handle file download
let mut mod_name = packet.data_as_string().clone(); let mut mod_name = packet.data_as_string().clone();
mod_name.remove(0); // Remove f mod_name.remove(0); // Remove f
debug!("Client requested file {}", mod_name); debug!("Client is requesting file {}", mod_name);
// making sure that the requested file cant point to files outside of Resources/Client/* let client_resources = config.general.get_server_resource_folder()?;
let path = std::path::Path::new(&mod_name); let mod_path = fs_util::join_path_secure(Path::new(&client_resources), Path::new(&mod_name))?;
let mod_name = match path.file_name() {
Some(v) => "/".to_string() + v.to_str().unwrap(), if !mod_path.exists() || !mod_path.is_file() {
None => { error!("Client requested mod which doesn't exist: {:?}. Disconnecting", mod_path);
error!("Client requests invalid mod. Disconnecting");
self.kick("Invalid mod request").await;
return Ok(());
}, // client requested path (fResources/Client/) or nothing at all (f) - invalid
};
let mod_path = format!("Resources/Client{mod_name}");
if !std::path::Path::new(&mod_path).exists() {
error!("Client requests inexistent mod. Disconnecting");
self.kick("Invalid mod request").await; self.kick("Invalid mod request").await;
return Ok(()) // client requested mod that doesnt exists within "Resources/Client/*" return Ok(()) // client requested mod that doesnt exists within "Resources/Client/*"
} }

View File

@ -27,11 +27,12 @@ pub use plugins::*;
pub use http::*; pub use http::*;
pub use crate::config::Config; pub use crate::config::Config;
use crate::config::GeneralSettings;
fn load_plugins() -> Vec<Plugin> { fn load_plugins(server_resource_folder: String) -> Vec<Plugin> {
let mut plugins = Vec::new(); let mut plugins = Vec::new();
for res_entry in std::fs::read_dir("Resources/Server").expect("Failed to read Resources/Server!") { for res_entry in std::fs::read_dir(server_resource_folder).expect("Failed to read server resource folder!") {
if let Ok(res_entry) = res_entry { if let Ok(res_entry) = res_entry {
let res_path = res_entry.path(); let res_path = res_entry.path();
if res_path.is_dir() { if res_path.is_dir() {
@ -158,8 +159,12 @@ impl Server {
Arc::new(UdpSocket::bind(bind_addr).await?) Arc::new(UdpSocket::bind(bind_addr).await?)
}; };
let server_resource_folder = config.general
.get_server_resource_folder()
.expect("Failed to create the server resource folder");
// Load existing plugins // Load existing plugins
let plugins = load_plugins(); let plugins = load_plugins(server_resource_folder);
// Start client runtime // Start client runtime
let (clients_incoming_tx, clients_incoming_rx) = mpsc::channel(100); let (clients_incoming_tx, clients_incoming_rx) = mpsc::channel(100);