0.8.0: add user configuration.

This commit is contained in:
abbie 2024-01-20 18:33:40 +00:00
parent 53b47ad522
commit c2e35fcf5c
Signed by: threeoh6000
GPG key ID: 801FE4AD456E922C
4 changed files with 112 additions and 21 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
/target /target
/src/content /src/content
index.html index.html
*settings.toml

9
Cargo.lock generated
View file

@ -2,10 +2,17 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "frostwalker"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17cbf58e19f2bda088d8c4c95a46b41895375e15a2d63dd686a4273f8c43d46b"
[[package]] [[package]]
name = "herb" name = "herb"
version = "0.7.3" version = "0.8.0"
dependencies = [ dependencies = [
"frostwalker",
"packeteer", "packeteer",
] ]

View file

@ -1,7 +1,8 @@
[package] [package]
name = "herb" name = "herb"
version = "0.7.3" version = "0.8.0"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
frostwalker = "0.1.1"
packeteer = { version = "0.5.2", features = ["http1"] } packeteer = { version = "0.5.2", features = ["http1"] }

View file

@ -6,6 +6,7 @@ use std::fs::File;
use std::string::{String}; use std::string::{String};
use std::process::Command; use std::process::Command;
use packeteer::http1::*; use packeteer::http1::*;
use frostwalker;
use packeteer::{generate_kvheader, unwrap_url_into_segments}; use packeteer::{generate_kvheader, unwrap_url_into_segments};
struct Resource { struct Resource {
@ -18,10 +19,24 @@ struct Resource {
struct GetPageResult { struct GetPageResult {
is500: bool, is500: bool,
is502: bool, is502: bool,
is403: bool,
contents: Vec<u8>, contents: Vec<u8>,
iscgi: bool, 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<Vec<u8>> { fn process_cgi(filename: String) -> Option<Vec<u8>> {
// This is gonna be the boggiest implementation of CGI that anyone // This is gonna be the boggiest implementation of CGI that anyone
// has ever seen in the history of the fucking world // 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(), "txt" => "text/plain".to_string(),
"html" => "text/html".to_string(), "html" => "text/html".to_string(),
"htm" => "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(), _ => "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 // The loaded page should be left immutable as it does
// not need to be modified by the server. // 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 checks = check_if_path_exists(path.clone());
let index = check_if_dir(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); println!("{} {} {}", path, checks, index);
if checks == true && index == false { if checks == true && index == false {
if path.contains(".") != true && path.contains(".cgi?") != true { if path.contains(".") != true && path.contains(".cgi?") != true {
let result = generate_index(filename); let result;
if settings.index_generation == false {
result = "<!DOCTYPE HTML><html><body><h1>403 Forbidden</h1><p>You do not have permission to access this resource</p></body></html>".to_string();
resultstruct.is403 = true;
} else {
result = generate_index(filename);
}
resultstruct.contents = result.to_string().into_bytes(); resultstruct.contents = result.to_string().into_bytes();
return resultstruct; return resultstruct;
} else if path.contains(".cgi?") { } else if path.contains(".cgi?") {
if settings.cgi == false {
let result = "<!DOCTYPE HTML><html><body><h1>403 Forbidden</h1><p>You do not have permission to access this resource</p></body></html>".to_string();
resultstruct.contents = result.into_bytes();
resultstruct.is403 = true;
return resultstruct;
}
let result = process_cgi(filename); let result = process_cgi(filename);
if result.is_some() { if result.is_some() {
resultstruct.contents = result.unwrap(); resultstruct.contents = result.unwrap();
@ -228,6 +254,13 @@ fn get_page(filename: String) -> GetPageResult {
} }
if filename.contains(".cgi") { if filename.contains(".cgi") {
if settings.cgi == false {
let result = "<!DOCTYPE HTML><html><body><h1>403 Forbidden</h1><p>You do not have permission to access this resource</p></body></html>".to_string();
resultstruct.contents = result.into_bytes();
resultstruct.is403 = true;
return resultstruct;
}
let result = process_cgi(filename); let result = process_cgi(filename);
if result.is_some() { if result.is_some() {
resultstruct.contents = result.unwrap(); resultstruct.contents = result.unwrap();
@ -252,14 +285,20 @@ fn get_page(filename: String) -> GetPageResult {
return resultstruct; return resultstruct;
} }
} else { } else {
if filename == "index.html" { if filename == "index.html" {
let newresult = generate_index(".".to_string()); let newresult;
resultstruct.contents = newresult.to_string().into_bytes(); if settings.index_generation == false {
return resultstruct; newresult = "<!DOCTYPE HTML><html><body><h1>403 Forbidden</h1><p>You do not have permission to access this resource</p></body></html>".to_string();
resultstruct.is403 = true;
} else {
newresult = generate_index(".".to_string());
} }
resultstruct.contents = "<!DOCTYPE HTML><html><body><h1>500 Internal Server Error</h1><p>The resource you are trying to access cannot be read by the server.</p></body></html>".to_string().into_bytes(); resultstruct.contents = newresult.to_string().into_bytes();
resultstruct.is500 = true;
return resultstruct; return resultstruct;
}
resultstruct.contents = "<!DOCTYPE HTML><html><body><h1>500 Internal Server Error</h1><p>The resource you are trying to access cannot be read by the server.</p></body></html>".to_string().into_bytes();
resultstruct.is500 = true;
return resultstruct;
} }
} }
} }
@ -274,7 +313,7 @@ fn grab_time() -> String{
} }
fn process_request(request: Vec<u8>) -> Resource { fn process_request(request: Vec<u8>, settings: Settings) -> Resource {
let input = String::from_utf8_lossy(&request).to_string(); let input = String::from_utf8_lossy(&request).to_string();
let prerequest = construct_request(&input); let prerequest = construct_request(&input);
let request; let request;
@ -317,6 +356,12 @@ fn process_request(request: Vec<u8>) -> Resource {
cgipathraw = format!("{}/{}", cgipathraw, seg); cgipathraw = format!("{}/{}", cgipathraw, seg);
} }
} }
if settings.cgi == false {
let newcontents = "<!DOCTYPE HTML><html><body><h1>403 Forbidden</h1><p>You do not have permission to access this resource</p></body></html>".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); let contents = process_cgi_with_path(cgipathraw, segclone, cgiscript);
if contents.is_some() { if contents.is_some() {
let resource = Resource { contents: contents.unwrap(), status_code: 200, mime: "text/html".to_string(), iscgi: true }; 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<u8>) -> Resource {
} }
// Did you want to see chars.as_str().to_string()? // 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::<Vec<&str>>()[0].to_string()), iscgi: false }; let mut resource = Resource { contents: rescontents.contents, status_code: 200, mime: detect_media_type(output.split("?").collect::<Vec<&str>>()[0].to_string()), iscgi: false };
if rescontents.is500 { if rescontents.is500 {
resource.status_code = 500; resource.status_code = 500;
@ -365,6 +410,11 @@ fn process_request(request: Vec<u8>) -> Resource {
resource.mime = "text/html".to_string(); resource.mime = "text/html".to_string();
} }
if rescontents.is403 {
resource.status_code = 403;
resource.mime = "text/html".to_string();
}
if rescontents.iscgi { if rescontents.iscgi {
resource.iscgi = true; resource.iscgi = true;
} }
@ -373,7 +423,7 @@ fn process_request(request: Vec<u8>) -> Resource {
} }
fn serve(mut stream: TcpStream) { fn serve(mut stream: TcpStream, settings: Settings) {
thread::spawn(move || { thread::spawn(move || {
println!("Stream thread created."); println!("Stream thread created.");
let mut request = Vec::new(); let mut request = Vec::new();
@ -382,7 +432,7 @@ fn serve(mut stream: TcpStream) {
.read_until(b'\n', &mut request) .read_until(b'\n', &mut request)
.expect("Failed to read from stream!"); .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, ""); let mut response_constructed = generate_response(resource.status_code, "");
response_constructed.headers.push(generate_kvheader("Content-Type", &resource.mime)); 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::<bool>().is_ok() {
settings.cgi = hashmap.get("cgi").unwrap_or(&"true".to_string()).parse::<bool>().unwrap_or(true);
}
}
if hashmap.get("index_generation").is_some() {
println!("a");
if hashmap.get("index_generation").unwrap_or(&"".to_string()).parse::<bool>().is_ok() {
settings.index_generation = hashmap.get("index_generation").unwrap_or(&"true".to_string()).parse::<bool>().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<()> { 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() { for stream in listen.incoming() {
println!("Serving incoming stream."); println!("Serving incoming stream.");
serve(stream?); serve(stream?, settings.clone());
} }
Ok(()) Ok(())
} }