diff --git a/Cargo.toml b/Cargo.toml index ef55204..d1b176b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "packeteer" description = "An attempt at a Rust library that can be used to assist in programmatically analysing, serving and handling received protocol packets." -version = "0.1.4" +version = "0.2.0" edition = "2021" authors = ["Celeste "] license = "LGPL-3.0-or-later" @@ -16,5 +16,6 @@ all-features = true [features] http1 = [] +gemini = [] [dependencies] diff --git a/src/gemini.rs b/src/gemini.rs new file mode 100644 index 0000000..e073b7e --- /dev/null +++ b/src/gemini.rs @@ -0,0 +1,85 @@ +//! A module for handling Gemini operations. +//! It contains structures for Gemini requests and responses as well as functions for generating, unpacking and constructing them. +use super::{Url, wrap_url, unwrap_url}; +use std::str::FromStr; + +// A structure for Gemini requests. +// Generated with packeteer::gemini::generate_request +#[derive(Debug)] +pub struct GeminiRequest { + pub url: Url, +} + +// A structure for Gemini responses. +// Generated with packeteer::gemini::generate_response +#[derive(Debug)] +pub struct GeminiResponse { + pub code: i32, + pub meta: String, + pub body: String, +} + +// A function for generating GeminiRequest structures. +pub fn generate_request(url: &str) -> GeminiRequest { + let wurl = wrap_url(url); + let gem = GeminiRequest { url: wurl }; + return gem +} + +// A function for generating GeminiResponse structures. +// Any invalid status code (valid status codes are 69 > x >= 10) will automatically be switched to 40 (Temporary failure). +pub fn generate_response(code: i32, body: &str) -> GeminiResponse { + let mut xcode = code; + if code < 10 { xcode = 40 }; + if code > 69 { xcode = 40 }; + let res = GeminiResponse { code: xcode, meta: "".to_string(), body: body.to_string() }; + return res +} + +// A function for converting GeminiRequest structures into a Gemini request string. +// This is extremely simple since GeminiRequest is basically storing a Url. +// Unpacking functions are simply taking the data stored in the structure and concatenating it into a string so nothing changes apart from the conversion. +pub fn unpack_request(req: GeminiRequest) -> String { + let url = unwrap_url(req.url); + let string = format!("{}\r\n", url); + return string.to_string() +} + +// A function for converting GeminiResponse structures into split header and body strings. +// The header and body are split as the Gemini specification seperates them. +// Unpacking functions are simply taking the data stored in the structure and concatenating it into a string so nothing changes apart from the conversion. +pub fn unpack_response(res: GeminiResponse) -> (String, String) { + let string = format!("{} {}\r\n", res.code, res.meta); + return (string.to_string(), res.body) +} + +// A function for converting Gemini request strings into GeminiRequest. +// This is essentially equivalent to packeteer::gemini::generate_request apart from the fact it automatically removes the character return and line feed after the URL to allow for conversion from raw Gemini requests. +// Construction functions aim to be as transparent as possible so apart from turning it into a structure, the request stays relatively unchanged. +pub fn construct_request(req: &str) -> GeminiRequest { + let url: Vec<&str> = req.split("\r\n").collect(); + let wurl = wrap_url(url[0]); + let gem = GeminiRequest { url: wurl }; + return gem +} + +// A function for converting Gemini header and body strings into GeminiResponse. +// Construction functions aim to be as transparent as possible so apart from turning it into a structure, the response stays relatively unchanged. +pub fn construct_response(res: &str, body: &str) -> GeminiResponse { + let split1: Vec<&str> = res.split("\r\n").collect(); + let split2: Vec<&str> = split1[0].split(" ").collect(); + let mut meta = "".to_string(); + for v in &split2 { + if v != &split2[0] { + if meta == "" { + meta = v.to_string(); + } else { + meta = format!("{} {}", meta, v.to_string()); + } + } + } + + let n: i32 = FromStr::from_str(split2[0]).unwrap(); + let resp = GeminiResponse { code: n, meta: meta.to_string(), body: body.to_string() }; + return resp +} diff --git a/src/lib.rs b/src/lib.rs index c8eab0a..36b225a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,5 +43,42 @@ pub fn generate_kvheader(key: &str, value: &str) -> KVHeader { return result } +/// A global structure for integrating URLs within the structural system of packeteer. +/// Can be automatically generated with the function packeteer::wrap_url. +#[derive(Debug)] +pub struct Url { + pub protocol: String, + pub domain: String, + pub segments: Vec, +} + +/// A public function for wrapping a string reference as a Url structure. +pub fn wrap_url(input: &str) -> Url { + let split1: Vec<&str> = input.split("://").collect(); + let split2: Vec<&str> = split1[1].split("/").collect(); + let protocol = split1[0]; + let domain = split2[0]; + let mut segments: Vec = vec![]; + for v in split2 { + if v != "" && v != domain { + segments.push(v.to_string()); + } + } + let url = Url { protocol: protocol.to_string(), domain: domain.to_string(), segments: segments }; + return url +} + +/// A public function for unwrapping a Url structure into a String. +pub fn unwrap_url(input: Url) -> String { + let mut string = format!("{}://{}", input.protocol, input.domain); + for v in input.segments { + string = format!("{}/{}", string, v); + } + return string.to_string(); +} + #[cfg(feature = "http1")] pub mod http1; + +#[cfg(feature = "gemini")] +pub mod gemini;