diff --git a/src/database.rs b/src/database.rs index 84f13dd..cda2927 100644 --- a/src/database.rs +++ b/src/database.rs @@ -1,5 +1,5 @@ use std::{ - collections::{hash_map::Values, HashMap, HashSet}, fs::{self, File}, path::{Path, PathBuf}, sync::{Arc, RwLock} + collections::{hash_map::Values, HashMap, HashSet}, ffi::OsStr, fs::{self, File}, path::{Path, PathBuf}, sync::{Arc, RwLock} }; use bincode::{config::Configuration, decode_from_std_read, encode_into_std_write, Decode, Encode}; @@ -308,3 +308,38 @@ impl TryFrom<&str> for Mmid { Ok(Self(value.to_owned())) } } + +impl TryFrom<&Path> for Mmid { + type Error = (); + + fn try_from(value: &Path) -> Result { + value.as_os_str().try_into() + } +} + +impl TryFrom<&OsStr> for Mmid { + type Error = (); + + fn try_from(value: &OsStr) -> Result { + let string = match value.to_str() { + Some(p) => p, + None => return Err(()), + }; + + if string.len() != 8 { + return Err(()) + } + + if string.chars().any(|c| !c.is_ascii_alphanumeric()) { + return Err(()) + } + + Ok(Self(string.to_owned())) + } +} + +impl std::fmt::Display for Mmid { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} diff --git a/src/endpoints.rs b/src/endpoints.rs index df08227..9641128 100644 --- a/src/endpoints.rs +++ b/src/endpoints.rs @@ -1,7 +1,7 @@ -use std::sync::{Arc, RwLock}; +use std::{path::PathBuf, sync::{Arc, RwLock}}; use rocket::{ - fs::NamedFile, get, http::ContentType, serde::{self, json::Json}, tokio::fs::File, State + get, http::ContentType, response::Redirect, serde::{self, json::Json}, tokio::fs::File, uri, State }; use serde::Serialize; @@ -36,56 +36,36 @@ pub struct ServerInfo { /// Look up the [`Mmid`] of a file to find it. #[get("/f/")] -pub async fn lookup( +pub async fn lookup_mmid( db: &State>>, - settings: &State, - mmid: &str -) -> Option<(ContentType, NamedFile)> { - let mmid: Mmid = match mmid.try_into() { - Ok(v) => v, - Err(_) => return None, - }; + mmid: &str, +) -> Option { + let mmid: Mmid = mmid.try_into().ok()?; + let entry = db.read().unwrap().get(&mmid).cloned()?; - let entry = if let Some(e) = db.read().unwrap().get(&mmid).cloned() { - e - } else { - return None - }; - - let file = NamedFile::open(settings.file_dir.join(entry.hash().to_string())).await.ok()?; - - Some(( - ContentType::from_extension(entry.extension()).unwrap_or(ContentType::Binary), - file - )) + Some( + Redirect::to(uri!(lookup_mmid_name(mmid.to_string(), entry.name()))) + ) } - -#[get("/f//")] -pub async fn lookup_filename( +/// Look up the [`Mmid`] of a file to find it. +#[get("/f//")] +pub async fn lookup_mmid_name( db: &State>>, settings: &State, mmid: &str, - filename: &str, -) -> Option<(ContentType, NamedFile)> { - let mmid: Mmid = match mmid.try_into() { - Ok(v) => v, - Err(_) => return None, - }; + name: &str, +) -> Option<(ContentType, File)> { + let mmid: Mmid = mmid.try_into().ok()?; - let entry = if let Some(e) = db.read().unwrap().get(&mmid).cloned() { - e - } else { - return None - }; + let entry = db.read().unwrap().get(&mmid).cloned()?; - dbg!(entry.name()); - dbg!(filename); - if entry.name() != filename { + // If the name does not match, then this is invalid + if name != entry.name() { return None } - let file = NamedFile::open(settings.file_dir.join(entry.hash().to_string())).await.ok()?; + let file = File::open(settings.file_dir.join(entry.hash().to_string())).await.ok()?; Some(( ContentType::from_extension(entry.extension()).unwrap_or(ContentType::Binary), diff --git a/src/main.rs b/src/main.rs index be756d0..f4f7538 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,7 @@ use std::{ use chrono::{DateTime, TimeDelta, Utc}; use database::{clean_loop, Database, Mmid, MochiFile}; -use endpoints::{lookup, lookup_filename, server_info}; +use endpoints::{lookup_mmid, lookup_mmid_name, server_info}; use log::info; use maud::{html, Markup, PreEscaped, DOCTYPE}; use rocket::{ @@ -102,7 +102,7 @@ fn home(settings: &State) -> Markup { footer { p {a href="https://github.com/G2-Games/confetti-box" {"Source"}} p {a href="https://g2games.dev/" {"My Website"}} - p {a href="#" {"Links"}} + p {a href="api" {"API Info"}} p {a href="#" {"Go"}} p {a href="#" {"Here"}} } @@ -264,8 +264,8 @@ async fn main() { stylesheet, server_info, favicon, - lookup, - lookup_filename, + lookup_mmid, + lookup_mmid_name, ], ) .mount( @@ -284,12 +284,14 @@ async fn main() { // Ensure the server gracefully shuts down rocket.expect("Server failed to shutdown gracefully"); - info!("Stopping database cleaning thread"); + info!("Stopping database cleaning thread..."); shutdown .send(()) .await - .expect("Failed to stop cleaner thread"); + .expect("Failed to stop cleaner thread."); + info!("Stopping database cleaning thread completed successfully."); info!("Saving database on shutdown..."); local_db.write().unwrap().save(); + info!("Saving database completed successfully."); }