From f941e853103089897127fb22b28cc2e7eb8e556f Mon Sep 17 00:00:00 2001 From: Morgana Date: Fri, 23 Oct 2026 11:19:55 -0500 Subject: Initial Commit --- src/main.rs | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 src/main.rs (limited to 'src') diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..c2b4509 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,103 @@ +use std::fs::{self, DirEntry}; +use std::io::Write; +use std::net::TcpStream; +use std::path::{Path, PathBuf}; +use std::sync::Arc; +use std::{io::Read, net::TcpListener}; + +use rustls::ServerConnection; +use rustls::{ + ServerConfig, Stream, + pki_types::{CertificateDer, PrivateKeyDer, pem::PemObject}, +}; + +fn main() { + println!("Starting MorGemTS Server..."); + let root: PathBuf = fs::canonicalize("content").unwrap(); + let certs = CertificateDer::pem_file_iter("cert/morgemrs.crt") + .unwrap() + .map(|cert| cert.unwrap()) + .collect(); + let private_key = PrivateKeyDer::from_pem_file("cert/morgemrs.key").unwrap(); + let config = Arc::new( + ServerConfig::builder() + .with_no_client_auth() + .with_single_cert(certs, private_key) + .unwrap(), + ); + let listener = TcpListener::bind("127.0.0.1:1965").unwrap(); + for mut tcp_stream in listener.incoming().flatten() { + let mut conn = ServerConnection::new(config.clone()).unwrap(); + let mut tls_stream = Stream::new(&mut conn, &mut tcp_stream); + let mut buffer = [0; 1027]; // 1024 URI bytes + 2 CRLF bytes + 1 overflow byte + let Ok(n) = tls_stream.read(&mut buffer[..]) else { + let _ = writeln!(tls_stream, "59\r\n"); + continue; + }; + if n == 1027 { + let _ = writeln!(tls_stream, "59\r\n"); + continue; + } + let Ok(raw_uri) = String::from_utf8(buffer.to_vec()) else { + let _ = writeln!(tls_stream, "59\r\n"); + continue; + }; + let Some(mut uri) = raw_uri + .trim_end_matches(|c: char| c == char::from(0) || c.is_whitespace()) + .strip_prefix("gemini://localhost") + else { + let _ = writeln!(tls_stream, "53 Proxy Not Supported\r\n"); + continue; + }; + uri = uri.trim_matches('/'); + let Ok(path) = root.join(uri).canonicalize() else { + let _ = writeln!(tls_stream, "51 Not Found\r\n"); + continue; + }; + if !path.starts_with(&root) { + let _ = writeln!(tls_stream, "51 Not Found\rpath\n"); + continue; + } + if path.is_dir() { + serve_dir(&path, uri, &mut tls_stream); + } else if path.is_file() { + serve_file(&path, &mut tls_stream); + } else { + let _ = writeln!(tls_stream, "51 Not Found\r\n"); + } + } +} + +fn serve_dir(dir: &Path, uri: &str, tls_stream: &mut Stream<'_, ServerConnection, TcpStream>) { + let index_path = dir.join("index.gem"); + if index_path.is_file() { + serve_file(&index_path, tls_stream); + return; + } + let Ok(read_dir) = dir.read_dir() else { + let _ = writeln!(tls_stream, "50 Server Error\r\n"); + return; + }; + let contents: Vec = read_dir.flatten().collect(); + let content_string = contents + .iter() + .flat_map(|f| f.file_name().into_string()) + .map(|s| { + if uri.is_empty() { + format!("=> /{}", s) + } else { + format!("=> /{}/{} ", uri, s) + } + }) + .collect::>() + .join("\r\n"); + let _ = writeln!(tls_stream, "20 text/gemini\r\n{}", content_string); +} + +fn serve_file(file: &Path, tls_stream: &mut Stream<'_, ServerConnection, TcpStream>) { + let Ok(contents) = fs::read_to_string(file) else { + let _ = writeln!(tls_stream, "50 Server Error\r\n"); + return; + }; + let _ = writeln!(tls_stream, "20 text/gemini\r\n{}", contents); +} -- cgit v1.3.1