diff --git a/.gitignore b/.gitignore index 6fb90cc..e3662cc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target /src/content index.html +*settings.toml diff --git a/Cargo.lock b/Cargo.lock index 388f9fe..e195bb8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,10 +2,17 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "frostwalker" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17cbf58e19f2bda088d8c4c95a46b41895375e15a2d63dd686a4273f8c43d46b" + [[package]] name = "herb" -version = "0.7.3" +version = "0.8.0" dependencies = [ + "frostwalker", "packeteer", ] diff --git a/Cargo.toml b/Cargo.toml index 79b017f..9a92898 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "herb" -version = "0.7.3" +version = "0.8.0" edition = "2018" [dependencies] +frostwalker = "0.1.1" packeteer = { version = "0.5.2", features = ["http1"] } diff --git a/src/main.rs b/src/main.rs index 4ae05ca..c9b92a5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ use std::fs::File; use std::string::{String}; use std::process::Command; use packeteer::http1::*; +use frostwalker; use packeteer::{generate_kvheader, unwrap_url_into_segments}; struct Resource { @@ -18,10 +19,24 @@ struct Resource { struct GetPageResult { is500: bool, is502: bool, + is403: bool, contents: Vec, iscgi: bool, } +#[derive(Clone)] +struct Settings { + cgi: bool, + index_generation: bool, + address: String, +} + +impl Settings { + fn new() -> Settings { + return Settings { cgi: true, index_generation: true, address: "0.0.0.0:8080".to_string()}; + } +} + fn process_cgi(filename: String) -> Option> { // This is gonna be the boggiest implementation of CGI that anyone // has ever seen in the history of the fucking world @@ -154,9 +169,6 @@ fn detect_media_type(filename: String) -> String { "txt" => "text/plain".to_string(), "html" => "text/html".to_string(), "htm" => "text/html".to_string(), - "cgi" => "text/html".to_string(), - ".error_server_404" => "text/html".to_string(), - ".error_server_501" => "text/html".to_string(), _ => "application/octet-stream".to_string(), } } @@ -195,7 +207,7 @@ fn generate_index(directory: String) -> String { } -fn get_page(filename: String) -> GetPageResult { +fn get_page(filename: String, settings: Settings) -> GetPageResult { // The loaded page should be left immutable as it does // not need to be modified by the server. @@ -204,16 +216,30 @@ fn get_page(filename: String) -> GetPageResult { let checks = check_if_path_exists(path.clone()); let index = check_if_dir(path.clone()); - let mut resultstruct = GetPageResult { is500: false, is502: false, contents: vec![], iscgi: false }; + let mut resultstruct = GetPageResult { is500: false, is502: false, is403: false, contents: vec![], iscgi: false }; println!("{} {} {}", path, checks, index); if checks == true && index == false { if path.contains(".") != true && path.contains(".cgi?") != true { - let result = generate_index(filename); + let result; + if settings.index_generation == false { + result = "

403 Forbidden

You do not have permission to access this resource

".to_string(); + resultstruct.is403 = true; + } else { + result = generate_index(filename); + } + resultstruct.contents = result.to_string().into_bytes(); return resultstruct; - } else if path.contains(".cgi?") { + } else if path.contains(".cgi?") { + if settings.cgi == false { + let result = "

403 Forbidden

You do not have permission to access this resource

".to_string(); + resultstruct.contents = result.into_bytes(); + resultstruct.is403 = true; + return resultstruct; + } + let result = process_cgi(filename); if result.is_some() { resultstruct.contents = result.unwrap(); @@ -228,6 +254,13 @@ fn get_page(filename: String) -> GetPageResult { } if filename.contains(".cgi") { + if settings.cgi == false { + let result = "

403 Forbidden

You do not have permission to access this resource

".to_string(); + resultstruct.contents = result.into_bytes(); + resultstruct.is403 = true; + return resultstruct; + } + let result = process_cgi(filename); if result.is_some() { resultstruct.contents = result.unwrap(); @@ -252,14 +285,20 @@ fn get_page(filename: String) -> GetPageResult { return resultstruct; } } else { - if filename == "index.html" { - let newresult = generate_index(".".to_string()); - resultstruct.contents = newresult.to_string().into_bytes(); - return resultstruct; + if filename == "index.html" { + let newresult; + if settings.index_generation == false { + newresult = "

403 Forbidden

You do not have permission to access this resource

".to_string(); + resultstruct.is403 = true; + } else { + newresult = generate_index(".".to_string()); } - resultstruct.contents = "

500 Internal Server Error

The resource you are trying to access cannot be read by the server.

".to_string().into_bytes(); - resultstruct.is500 = true; + resultstruct.contents = newresult.to_string().into_bytes(); return resultstruct; + } + resultstruct.contents = "

500 Internal Server Error

The resource you are trying to access cannot be read by the server.

".to_string().into_bytes(); + resultstruct.is500 = true; + return resultstruct; } } } @@ -274,7 +313,7 @@ fn grab_time() -> String{ } -fn process_request(request: Vec) -> Resource { +fn process_request(request: Vec, settings: Settings) -> Resource { let input = String::from_utf8_lossy(&request).to_string(); let prerequest = construct_request(&input); let request; @@ -317,6 +356,12 @@ fn process_request(request: Vec) -> Resource { cgipathraw = format!("{}/{}", cgipathraw, seg); } } + if settings.cgi == false { + let newcontents = "

403 Forbidden

You do not have permission to access this resource

".to_string().into_bytes(); + let resource = Resource { contents: newcontents, status_code: 403, mime: "text/html".to_string(), iscgi: false }; + return resource; + } + let contents = process_cgi_with_path(cgipathraw, segclone, cgiscript); if contents.is_some() { let resource = Resource { contents: contents.unwrap(), status_code: 200, mime: "text/html".to_string(), iscgi: true }; @@ -353,7 +398,7 @@ fn process_request(request: Vec) -> Resource { } // Did you want to see chars.as_str().to_string()? - let rescontents = get_page(output.to_string()); + let rescontents = get_page(output.to_string(), settings.clone()); let mut resource = Resource { contents: rescontents.contents, status_code: 200, mime: detect_media_type(output.split("?").collect::>()[0].to_string()), iscgi: false }; if rescontents.is500 { resource.status_code = 500; @@ -365,6 +410,11 @@ fn process_request(request: Vec) -> Resource { resource.mime = "text/html".to_string(); } + if rescontents.is403 { + resource.status_code = 403; + resource.mime = "text/html".to_string(); + } + if rescontents.iscgi { resource.iscgi = true; } @@ -373,7 +423,7 @@ fn process_request(request: Vec) -> Resource { } -fn serve(mut stream: TcpStream) { +fn serve(mut stream: TcpStream, settings: Settings) { thread::spawn(move || { println!("Stream thread created."); let mut request = Vec::new(); @@ -382,7 +432,7 @@ fn serve(mut stream: TcpStream) { .read_until(b'\n', &mut request) .expect("Failed to read from stream!"); - let resource = process_request(request); + let resource = process_request(request, settings.clone()); let mut response_constructed = generate_response(resource.status_code, ""); response_constructed.headers.push(generate_kvheader("Content-Type", &resource.mime)); @@ -409,13 +459,45 @@ fn serve(mut stream: TcpStream) { }); } +fn process_settings() -> Settings { + let string = fs::read_to_string("../settings.toml"); + if string.is_err() { + return Settings::new(); + } + let parsed = frostwalker::parse(&string.unwrap()); + if parsed.is_err() { + return Settings::new(); + } + let hashmap = parsed.unwrap(); + let mut settings = Settings::new(); + if hashmap.get("cgi").is_some() { + if hashmap.get("cgi").unwrap_or(&"".to_string()).parse::().is_ok() { + settings.cgi = hashmap.get("cgi").unwrap_or(&"true".to_string()).parse::().unwrap_or(true); + } + } + + if hashmap.get("index_generation").is_some() { + println!("a"); + if hashmap.get("index_generation").unwrap_or(&"".to_string()).parse::().is_ok() { + settings.index_generation = hashmap.get("index_generation").unwrap_or(&"true".to_string()).parse::().unwrap_or(true); + println!("{}", settings.index_generation); + } + } + + if hashmap.get("address").is_some() { + settings.address = hashmap.get("address").unwrap_or(&"0.0.0.0:8080".to_string()).to_string(); + } + + return settings; +} fn main() -> std::io::Result<()> { - let listen = TcpListener::bind("0.0.0.0:8080")?; + let settings = process_settings(); + let listen = TcpListener::bind(settings.address.clone())?; for stream in listen.incoming() { println!("Serving incoming stream."); - serve(stream?); + serve(stream?, settings.clone()); } Ok(()) }