From a1ccc27cb896ef38861bbbd9aa5607ae4169be3f Mon Sep 17 00:00:00 2001 From: yggverse Date: Sat, 22 Feb 2025 09:13:00 +0200 Subject: [PATCH] dump meta on file commit --- src/main.rs | 34 +++++++++++++++++++++++++++++----- src/storage.rs | 29 +++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 7 deletions(-) diff --git a/src/main.rs b/src/main.rs index 5ac2040..636abe6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -97,7 +97,7 @@ fn gemini( match storage::Item::from_url(gemini.url.as_str(), &argument.directory) { Ok(item) => send( &response::success::Default { - mime: "text/gemini".to_string(), + mime: item.mime, //data: item.file.read(vec![1000]), } .into_bytes(), @@ -128,8 +128,29 @@ fn titan( stream: &mut TlsStream, ) { use titanite::*; + // require content type for application, + // even MIME value is optional by Titan specification + let mime = match titan.mime { + Some(mime) => mime, + None => { + const MESSAGE: &str = "Content type is required"; + return send( + &response::failure::permanent::BadRequest { + message: Some(MESSAGE.to_string()), + } + .into_bytes(), + stream, + |result| match result { + Ok(()) => println!("[{}] [warning] [{peer}] {MESSAGE}", now()), + Err(e) => println!("[{}] [error] [{peer}] {e}", now()), + }, + ); + } + }; + // validate total bytes let mut total = 0; - match storage::Item::create(&argument.directory) { + // create new destination file + match storage::Item::create(&argument.directory, mime) { Ok(mut tmp) => loop { let mut input = vec![0; argument.chunk]; match stream.read(&mut input) { @@ -205,10 +226,13 @@ fn titan( ); } + // just to make sure + if titan.size > total { + panic!() + } + // all data received - if titan.size >= total { - // @TODO detect/validate/cache mime based on data received - // success + if titan.size == total { return match tmp.commit() { Ok(pmt) => send( &response::redirect::Permanent { diff --git a/src/storage.rs b/src/storage.rs index bd0b4f5..bcbeac8 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -1,6 +1,7 @@ use anyhow::{anyhow, bail, Error, Result}; use std::{ fs::{create_dir_all, remove_file, rename, File}, + io::{Read, Write}, path::{PathBuf, MAIN_SEPARATOR}, str::FromStr, thread, @@ -8,10 +9,12 @@ use std::{ }; const TMP_SUFFIX: &str = ".tmp"; +const META_SUFFIX: &str = ".meta"; pub struct Item { pub file: File, pub path: PathBuf, + pub mime: String, // pub id: u64, } @@ -42,12 +45,13 @@ impl Item { ))?; Ok(Self { file: File::open(&path)?, + mime: read_meta(path.to_str().unwrap())?, path, }) } /// Create new `Self` with unique pathname in the root `directory` - pub fn create(directory: &str) -> Result { + pub fn create(directory: &str, mime: String) -> Result { loop { // generate file id from current unix time let id = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs(); @@ -77,6 +81,7 @@ impl Item { return Ok(Self { file, path, + mime, // id }); } @@ -102,7 +107,10 @@ impl Item { Ok(path) => path, Err(e) => return Err((self, anyhow!(e))), }; - Ok(self) + match self.write_meta() { + Ok(()) => Ok(self), + Err(e) => Err((self, anyhow!(e))), + } } Err(e) => Err((self, anyhow!(e))), }, @@ -127,4 +135,21 @@ impl Item { .replace(directory, "") .replace("/", "") } + + // System + + fn write_meta(&self) -> Result<()> { + Ok(File::create_new(meta_path(self.path.to_str().unwrap()))? + .write_all(self.mime.as_bytes())?) + } +} + +fn meta_path(path: &str) -> String { + format!("{}{META_SUFFIX}", path) +} + +fn read_meta(path: &str) -> Result { + let mut buffer = String::new(); + File::open(meta_path(path))?.read_to_string(&mut buffer)?; + Ok(buffer) }