X-Git-Url: http://git.euphorik.ch/?p=rup.git;a=blobdiff_plain;f=backend%2Fsrc%2Fmain.rs;fp=backend%2Fsrc%2Fmain.rs;h=726fcf8e0707c7dd70f01640540c6b1355d0cb50;hp=0000000000000000000000000000000000000000;hb=1f84b22050cf470b00aef6b3c3ecb7ae70242e1a;hpb=295ca1daf01ff83964812c50fe1374dd679a0559 diff --git a/backend/src/main.rs b/backend/src/main.rs new file mode 100644 index 0000000..726fcf8 --- /dev/null +++ b/backend/src/main.rs @@ -0,0 +1,187 @@ + +extern crate listenfd; +extern crate askama; +extern crate percent_encoding; + +use listenfd::ListenFd; +use actix_files as fs; +use actix_web::{ get, web, Responder, middleware, App, HttpServer, HttpRequest, HttpResponse, web::Query }; +use askama::Template; + +use std::{ sync::Mutex, fs::File, path::Path, env::args, io::prelude::* }; +use ron::{ de::from_reader, ser::{ to_string_pretty, PrettyConfig } }; +use serde::{ Deserialize, Serialize }; + +use itertools::Itertools; + +mod consts; +mod crypto; +mod db; +mod types; + +#[derive(Template)] +#[template(path = "main.html")] +struct MainTemplate { + sentence: String, +} + +#[derive(Deserialize)] +pub struct Request { + m: Option +} + +#[get("/")] +async fn main_page(key_shared: web::Data>, query: Query) -> impl Responder { + let key = key_shared.lock().unwrap(); + + let m = + match &query.m { + Some(b) => + match crypto::decrypt(&*key, b) { + Ok(m) => m, + Err(_e) => String::from(consts::DEFAULT_MESSAGE) // TODO: log error. + }, + None => String::from(consts::DEFAULT_MESSAGE) + }; + + MainTemplate { sentence: m } +} + +#[derive(Debug, Deserialize, Serialize)] +struct Config { + port: u16 +} + +const DEFAULT_CONFIG: Config = Config { port: 8082 }; + +fn get_exe_name() -> String { + let first_arg = std::env::args().next().unwrap(); + let sep: &[_] = &['\\', '/']; + first_arg[first_arg.rfind(sep).unwrap()+1..].to_string() +} + +fn load_config() -> Config { + // unwrap_or_else(|_| panic!("Failed to open configuration file {}", consts::FILE_CONF)); + match File::open(consts::FILE_CONF) { + Ok(file) => from_reader(file).unwrap_or_else(|_| panic!("Failed to open configuration file {}", consts::FILE_CONF)), + Err(_) => { + let mut file = File::create(consts::FILE_CONF) .unwrap(); + file.write_all(to_string_pretty(&DEFAULT_CONFIG, PrettyConfig::new()).unwrap().as_bytes()).unwrap(); // We do not use 'to_writer' because it can't pretty format the output. + DEFAULT_CONFIG + } + } +} + +fn read_key() -> String { + let mut key = String::new(); + File::open(consts::FILE_KEY) + .unwrap_or_else(|_| panic!("Failed to open key file: {}", consts::FILE_KEY)) + .read_to_string(&mut key) + .unwrap_or_else(|_| panic!("Failed to read key file: {}", consts::FILE_KEY)); + + String::from( + percent_encoding::percent_decode(key.replace('\n', "").as_bytes()) + .decode_utf8() + .unwrap_or_else(|_| panic!("Failed to decode key file: {}", consts::FILE_KEY)) + ) +} + +fn write_key(key : &str) { + let percent_encoded = percent_encoding::utf8_percent_encode(key, percent_encoding::NON_ALPHANUMERIC).to_string(); + let mut file = File::create(consts::FILE_KEY).unwrap(); + file.write_all(percent_encoded.as_bytes()).unwrap(); +} + +#[actix_rt::main] +async fn main() -> std::io::Result<()> { + let key = { + // If the key file doesn't exist then create a new one with a random key in it. + if !Path::new(consts::FILE_KEY).exists() { + let new_key = crypto::generate_key(); + write_key(&new_key); + println!("A key has been generated here: {}", consts::FILE_KEY); + new_key + } else { + read_key() + } + }; + + if process_args(&key) { return Ok(()) } + + println!("Starting RUP as web server..."); + + let config = load_config(); + + println!("Configuration: {:?}", config); + + let key_shared = web::Data::new(Mutex::new(key)); + + let mut listenfd = ListenFd::from_env(); + let mut server = + HttpServer::new( + move || { + App::new() + .app_data(key_shared.clone()) + .wrap(middleware::Compress::default()) + .wrap(middleware::Logger::default()) + .service(main_page) + .service(fs::Files::new("/static", "static").show_files_listing()) + } + ); + + server = + if let Some(l) = listenfd.take_tcp_listener(0).unwrap() { + server.listen(l).unwrap() + } else { + server.bind(&format!("0.0.0.0:{}", config.port)).unwrap() + }; + + server.run().await +} + +fn process_args(key: &str) -> bool { + fn print_usage() { + println!("Usage:"); + println!(" {} [--help] [--encrypt | --decrypt ]", get_exe_name()); + } + + let args: Vec = args().collect(); + + if args.iter().any(|arg| arg == "--help") { + print_usage(); + return true + } else if let Some((position_arg_encrypt, _)) = args.iter().find_position(|arg| arg == &"--encrypt") { + match args.get(position_arg_encrypt + 1) { + Some(mess_to_encrypt) => { + // Encrypt to version 2 (version 1 is obsolete). + match crypto::encrypt(&key, mess_to_encrypt, 2) { + Ok(encrypted_mess) => { + let encrypted_mess_encoded = percent_encoding::utf8_percent_encode(&encrypted_mess, percent_encoding::NON_ALPHANUMERIC).to_string(); + println!("Encrypted message percent-encoded: {}", encrypted_mess_encoded); }, + Err(error) => + println!("Unable to encrypt: {:?}", error) + } + } + None => print_usage() + } + + return true + } else if let Some((position_arg_decrypt, _)) = args.iter().find_position(|arg| arg == &"--decrypt") { + match args.get(position_arg_decrypt + 1) { + Some(cipher_text) => { + let cipher_text_decoded = percent_encoding::percent_decode(cipher_text.as_bytes()).decode_utf8().expect("Unable to decode encoded cipher text"); + match crypto::decrypt(&key, &cipher_text_decoded) { + Ok(plain_text) => + println!("Decrypted message: {}", plain_text), + Err(error) => + println!("Unable to decrypt: {:?}", error) + } + } + None => print_usage() + } + + return true + } + + false +}