diff --git a/README.md b/README.md index 9dcd4be..5b74c60 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ openssl pkcs12 -export -out server.pfx -inkey server.pem -in server.crt * `--mime`, `-m` optional, uploads MIME type whitelist (comma separated, all by default) * `--directory`, `-d` optional, uploads target directory (`public` by default) * `--redirect`, `-r` optional, redirection URL on request handle complete (e.g. `gemini://localhost`) +* `--welcome`, `-w` optional, filepath to welcome page template (in gemtext format) ### Start diff --git a/src/argument.rs b/src/argument.rs index 1f6fbe4..db93f70 100644 --- a/src/argument.rs +++ b/src/argument.rs @@ -35,4 +35,8 @@ pub struct Argument { /// Redirection URL on request handle complete (e.g. `gemini://localhost`) #[arg(short, long)] pub redirect: Option, + + /// Welcome page filename (in gemtext format) + #[arg(short, long)] + pub welcome: Option, } diff --git a/src/main.rs b/src/main.rs index 9999ccd..cf672b0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -139,16 +139,44 @@ fn handle( } fn gemini( - gemini: titanite::request::Gemini, + request: titanite::request::Gemini, argument: &Argument, peer: &SocketAddr, stream: &mut TlsStream, ) { use titanite::*; - println!("[{}] [info] [{peer}] Request: {}", now(), gemini.url); - // file could be large, - // to not overflow the memory pool, build the response using chunks - match storage::Item::from_url(gemini.url.as_str(), &argument.directory) { + + println!("[{}] [info] [{peer}] Request: {}", now(), request.url); + + // try welcome page + if request.url.path().trim_end_matches("/").is_empty() { + return send( + &match welcome(argument, request.url.host_str(), request.url.port()) { + Ok(welcome) => response::success::Default { + data: welcome.as_bytes(), + meta: response::success::default::Meta { + mime: "text/gemini".to_string(), + }, + } + .into_bytes(), + Err(e) => { + println!("[{}] [error] [{peer}] {e}", now()); + response::failure::temporary::General { + message: Some("Internal server error".to_string()), + } + .into_bytes() + } + }, + stream, + |result| match result { + Ok(()) => println!("[{}] [info] [{peer}] /", now()), + Err(e) => println!("[{}] [error] [{peer}] {e}", now()), + }, + ); + } + // try file resource + // * it could be large, to not overflow the memory pool, use chunked read + match storage::Item::from_url(request.url.as_str(), &argument.directory) { Ok(item) => { let mut read: usize = 0; // create header packet @@ -185,7 +213,7 @@ fn gemini( } Err(e) => send( &response::failure::permanent::NotFound { - message: Some("Not found".to_string()), + message: Some("Resource not found".to_string()), } .into_bytes(), stream, @@ -431,6 +459,24 @@ fn send(data: &[u8], stream: &mut TlsStream, callback: impl FnOnce(Re })()); } +fn welcome(argument: &Argument, host: Option<&str>, port: Option) -> Result { + let mut file = File::open(argument.welcome.as_deref().unwrap_or("welcome.gmi"))?; + let mut data = String::new(); + file.read_to_string(&mut data)?; + Ok(data.replace( + "{UPLOAD_URL}", + &if let Some(host) = host { + let mut url = format!("titan://{host}"); + if let Some(port) = port { + url = format!("{url}:{port}") + } + url + } else { + argument.bind.to_string() + }, + )) +} + fn now() -> u128 { SystemTime::now() .duration_since(UNIX_EPOCH) diff --git a/welcome.gmi b/welcome.gmi new file mode 100644 index 0000000..cdca73f --- /dev/null +++ b/welcome.gmi @@ -0,0 +1,9 @@ +# Titan it! + +File Share Server for Gemini & Titan protocols + +=> {UPLOAD_URL} Upload.. + +## About + +=> https://github.com/YGGverse/titanit GitHub \ No newline at end of file