f385a0e861167119beb484366219341b2c2a9240
[rup.git] / src / main.rs
1 extern crate actix_web;
2 extern crate listenfd;
3 extern crate askama;
4 extern crate percent_encoding;
5
6 use listenfd::ListenFd;
7 use actix_files as fs;
8 use actix_web::{web, middleware, App, HttpServer, HttpResponse, web::Query};
9 use askama::Template;
10
11 use std::{fs::File, path::Path, env::args, io::prelude::*};
12 use ron::de::from_reader;
13 use serde::Deserialize;
14
15 use itertools::Itertools;
16
17 mod consts;
18 mod crypto;
19
20 #[derive(Template)]
21 #[template(path = "main.html")]
22 struct MainTemplate<'a> {
23 sentence: &'a str,
24 }
25
26 #[derive(Deserialize)]
27 pub struct Request {
28 m: Option<String>
29 }
30
31 fn main_page(query: Query<Request>, key: &str) -> HttpResponse {
32 let m =
33 match &query.m {
34 Some(b) =>
35 match crypto::decrypt(key, b) {
36 Ok(m) => m,
37 Err(_e) => String::from(consts::DEFAULT_MESSAGE) // TODO: log error.
38 },
39 None => String::from(consts::DEFAULT_MESSAGE)
40 };
41
42 let hello = MainTemplate { sentence: &m };
43
44 let s = hello.render().unwrap();
45 HttpResponse::Ok().content_type("text/html").body(s)
46 }
47
48 #[derive(Debug, Deserialize)]
49 struct Config {
50 port: u16
51 }
52
53 fn get_exe_name() -> String {
54 let first_arg = std::env::args().next().unwrap();
55 let sep: &[_] = &['\\', '/'];
56 first_arg[first_arg.rfind(sep).unwrap()+1..].to_string()
57 }
58
59 fn read_key() -> String {
60 let mut key = String::new();
61 File::open(consts::FILE_KEY)
62 .unwrap_or_else(|_| panic!("Failed to open key file: {}", consts::FILE_KEY))
63 .read_to_string(&mut key)
64 .unwrap_or_else(|_| panic!("Failed to read key file: {}", consts::FILE_KEY));
65
66 String::from(
67 percent_encoding::percent_decode(key.replace('\n', "").as_bytes())
68 .decode_utf8()
69 .unwrap_or_else(|_| panic!("Failed to decode key file: {}", consts::FILE_KEY))
70 )
71 }
72
73 fn write_key(key : &str) {
74 let percent_encoded = percent_encoding::utf8_percent_encode(key, percent_encoding::NON_ALPHANUMERIC).to_string();
75 let mut file = File::create(consts::FILE_KEY).unwrap();
76 file.write_all(percent_encoded.as_bytes()).unwrap();
77 }
78
79 #[actix_rt::main]
80 async fn main() -> std::io::Result<()> {
81 let key = {
82 // If the key file doesn't exist then create a new one with a random key in it.
83 if !Path::new(consts::FILE_KEY).exists() {
84 let new_key = crypto::generate_key();
85 write_key(&new_key);
86 println!("A key has been generated here: {}", consts::FILE_KEY);
87 new_key
88 } else {
89 read_key()
90 }
91 };
92
93 if process_args(&key) { return Ok(()) }
94
95 println!("Starting RUP as web server...");
96
97 let config: Config = {
98 let f = File::open(consts::FILE_CONF).unwrap_or_else(|_| panic!("Failed to open configuration file {}", consts::FILE_CONF));
99 match from_reader(f) {
100 Ok(c) => c,
101 Err(e) => panic!("Failed to load config: {}", e)
102 }
103 };
104
105 println!("Configuration: {:?}", config);
106
107 let mut listenfd = ListenFd::from_env();
108 let mut server =
109 HttpServer::new(
110 move || {
111 let key = key.clone(); // Is this neccessary??
112
113 App::new()
114 .wrap(middleware::Compress::default())
115 .wrap(middleware::Logger::default())
116 .service(web::resource("/").to(move |query| main_page(query, &key)))
117 .service(fs::Files::new("/static", "static").show_files_listing())
118 }
119 );
120
121 server =
122 if let Some(l) = listenfd.take_tcp_listener(0).unwrap() {
123 server.listen(l).unwrap()
124 } else {
125 server.bind(&format!("0.0.0.0:{}", config.port)).unwrap()
126 };
127
128 server.run().await
129 }
130
131 fn process_args(key: &str) -> bool {
132 fn print_usage() {
133 println!("Usage:");
134 println!(" {} [--help] [--encrypt <plain-text> | --decrypt <cipher-text>]", get_exe_name());
135 }
136
137 let args: Vec<String> = args().collect();
138
139 if args.iter().any(|arg| arg == "--help") {
140 print_usage();
141 return true
142 } else if let Some((position_arg_encrypt, _)) = args.iter().find_position(|arg| arg == &"--encrypt") {
143 match args.get(position_arg_encrypt + 1) {
144 Some(mess_to_encrypt) => {
145 // Encrypt to version 2 (version 1 is obsolete).
146 match crypto::encrypt(&key, mess_to_encrypt, 2) {
147 Ok(encrypted_mess) => {
148 let encrypted_mess_encoded = percent_encoding::utf8_percent_encode(&encrypted_mess, percent_encoding::NON_ALPHANUMERIC).to_string();
149 println!("Encrypted message percent-encoded: {}", encrypted_mess_encoded); },
150 Err(error) =>
151 println!("Unable to encrypt: {:?}", error)
152 }
153 }
154 None => print_usage()
155 }
156
157 return true
158 } else if let Some((position_arg_decrypt, _)) = args.iter().find_position(|arg| arg == &"--decrypt") {
159 match args.get(position_arg_decrypt + 1) {
160 Some(cipher_text) => {
161 let cipher_text_decoded = percent_encoding::percent_decode(cipher_text.as_bytes()).decode_utf8().expect("Unable to decode encoded cipher text");
162 match crypto::decrypt(&key, &cipher_text_decoded) {
163 Ok(plain_text) =>
164 println!("Decrypted message: {}", plain_text),
165 Err(error) =>
166 println!("Unable to decrypt: {:?}", error)
167 }
168 }
169 None => print_usage()
170 }
171
172 return true
173 }
174
175 false
176 }