diff --git a/src/argument.rs b/src/config.rs similarity index 78% rename from src/argument.rs rename to src/config.rs index e03a5e5..0b15f55 100644 --- a/src/argument.rs +++ b/src/config.rs @@ -6,7 +6,15 @@ use std::{ #[derive(Parser, Debug)] #[command(version, about, long_about = None)] -pub struct Argument { +pub struct Config { + /// Server name + #[arg(short, long, default_value_t = String::from("βtracker"))] + pub name: String, + + /// Tracker(s) to join / scrape requests + #[arg(short, long)] + pub tracker: Option>, + /// Bind server `host:port` to listen incoming connections on it #[arg(short, long, default_value_t = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 1965)))] pub bind: SocketAddr, diff --git a/src/main.rs b/src/main.rs index 2103bcd..73545eb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,11 @@ -mod argument; +mod config; mod format; use anyhow::Result; -use argument::Argument; use btracker_fs::public::{Order, Public, Sort}; use chrono::Local; use clap::Parser; +use config::Config; use librqbit_core::torrent_metainfo::{TorrentMetaV1Owned, torrent_from_bytes}; use log::*; use native_tls::{HandshakeError, Identity, TlsAcceptor, TlsStream}; @@ -34,33 +34,33 @@ fn main() -> Result<()> { .init() } - let argument = Arc::new(Argument::parse()); + let config = Arc::new(Config::parse()); - let public = - Arc::new(Public::init(&argument.storage, argument.limit, argument.capacity).unwrap()); + let public = Arc::new(Public::init(&config.storage, config.limit, config.capacity).unwrap()); // https://geminiprotocol.net/docs/protocol-specification.gmi#the-use-of-tls let acceptor = TlsAcceptor::new(Identity::from_pkcs12( &{ let mut buffer = vec![]; - File::open(&argument.identity)?.read_to_end(&mut buffer)?; + File::open(&config.identity)?.read_to_end(&mut buffer)?; buffer }, - &argument.password, + &config.password, )?)?; - let listener = TcpListener::bind(&argument.bind)?; + let listener = TcpListener::bind(&config.bind)?; - info!("Server started on `{}`", argument.bind); + info!("Server started on `{}`", config.bind); for stream in listener.incoming() { match stream { Ok(stream) => { thread::spawn({ + let config = config.clone(); let public = public.clone(); let peer = stream.peer_addr()?; let connection = acceptor.accept(stream); - move || handle(public, peer, connection) + move || handle(config, public, peer, connection) }); } Err(e) => error!("{e}"), @@ -70,6 +70,7 @@ fn main() -> Result<()> { } fn handle( + config: Arc, public: Arc, peer: SocketAddr, connection: Result, HandshakeError>, @@ -108,7 +109,7 @@ fn handle( if header_buffer.last().is_some_and(|&b| b == b'\n') { // header bytes contain valid Gemini **request** if let Ok(request) = request::Gemini::from_bytes(&header_buffer) { - return response(request, &public, &peer, &mut stream); + return response(request, &config, &public, &peer, &mut stream); } // header bytes received but yet could not be parsed, @@ -146,6 +147,7 @@ fn handle( fn response( request: titanite::request::Gemini, + config: &Config, public: &Public, peer: &SocketAddr, stream: &mut TlsStream, @@ -156,6 +158,7 @@ fn response( if request.url.path().trim_end_matches("/").is_empty() { return send( &match index( + config, public, request.url.query_pairs().find_map(|a| { if a.0 == "page" { @@ -207,7 +210,7 @@ fn send(data: &[u8], stream: &mut TlsStream, callback: impl FnOnce(Re })()); } -fn index(public: &Public, page: Option) -> Result { +fn index(config: &Config, public: &Public, page: Option) -> Result { let (total, torrents) = public.torrents( None, // @TODO Some((Sort::Modified, Order::Desc)), @@ -215,7 +218,19 @@ fn index(public: &Public, page: Option) -> Result { Some(public.default_limit), )?; - let mut b = Vec::with_capacity(torrents.len()); + let mut b = Vec::new(); + + b.push(format!("# {}\n", config.name)); + + if let Some(ref trackers) = config.tracker { + b.push("```".into()); + for tracker in trackers { + b.push(tracker.clone()); + } + b.push("```\n".into()); + } + + b.push("## Recent\n".into()); for torrent in torrents { let i: TorrentMetaV1Owned = torrent_from_bytes(&torrent.bytes)?; @@ -236,6 +251,8 @@ fn index(public: &Public, page: Option) -> Result { )); } + b.push("## Navigation\n".into()); + b.push(format!( "Page {} / {} ({total} {} total)", page.unwrap_or(1),