use magic_crypt::{new_magic_crypt, MagicCryptTrait}; use std::fs; use serde::{Serialize, Deserialize}; use serde_derive::{Serialize, Deserialize}; use actix_web::{get, post, web, App, HttpResponse, HttpServer, HttpRequest, Responder}; use std::fs::OpenOptions; use std::io::prelude::*; use chrono::Local; use rand::Rng; #[derive(Deserialize)] struct Post { content: String, name: String, } #[derive(Debug, Serialize, Deserialize, Clone)] struct EncryptedPost { name: String, crypt: String, date: String, id: u64, } fn decrypt(encrypted: String, pass: &str) -> Result { let mc = new_magic_crypt!(pass, 256); println!("Trying with {} and pass {}", encrypted, pass); let result = mc.decrypt_base64_to_string(&encrypted); match result { Ok(v) => { return Ok(v); }, Err(_) => { return Err("Could not decrypt".to_string()); }, } } fn encrypt(content: &String, password: &String) -> String { let mc = new_magic_crypt!(password, 256); let encrypted = mc.encrypt_str_to_base64(&content); return String::from(encrypted); } fn save_post(text: &str) { let mut file = OpenOptions::new() .write(true) .append(true) .open("./Files/posts.json") .unwrap(); if let Err(e) = writeln!(file, "{}", text) { eprintln!("Could not save the post, got {}", e); }; } fn get_all_posts() -> (Vec, String) { let posts = fs::read_to_string("./Files/posts.json").unwrap(); let mut res = Vec::new(); let mut display = String::from("\n\n\n"); for line in posts.lines() { let post: EncryptedPost = serde_json::from_str(&line).unwrap(); res.push(post.clone()); display.push_str(&format!("\n\n\n\n
\n", post.date, post.id)); } display.push_str("
Latest Posts
{}{}
"); return (res, display); } #[post("/")] async fn post_index(web::Form(form): web::Form, req: HttpRequest) -> impl Responder { println!("{:?}", req); let hostname = req.headers().get("Host").unwrap().to_str().unwrap(); let pass = random_password(); let encrypted = encrypt(&form.content, &pass); let now = Local::now(); let now = now.format("%d/%m/%Y@%H:%M:%S"); let mut rng = rand::thread_rng(); let id: u64 = rng.gen::(); let my_encrypted = EncryptedPost { name: form.name.clone(), crypt: encrypted.clone(), date: format!("{}", now), id: id }; let json = serde_json::to_string(&my_encrypted).unwrap(); save_post(&json); let header = fs::read_to_string("./Files/header.html").unwrap_or("".to_string()); let ret = format!("{}\nPost saved here", header, hostname, id, pass); return HttpResponse::Ok().body(ret); } fn random_password() -> String { const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\ abcdefghijklmnopqrstuvwxyz\ 0123456789)(*&^%$#@!~"; let mut rng = rand::thread_rng(); let password: String = (0..64) .map(|_| { let idx = rng.gen_range(0..CHARSET.len()); CHARSET[idx] as char }) .collect(); return password; } #[get("/decrypt/{givenid}/{password}")] // <- define path parameters async fn show_post(path: web::Path<(u64, String)>) -> impl Responder { let (givenid, password) = path.into_inner(); let (posts, _) = get_all_posts(); let mut ret_text = fs::read_to_string("./Files/header.html").unwrap(); for post in posts { if givenid == post.id { let _ = &mut ret_text.push_str(&format!("\n This is the link to your post. Don't lose it !
\n>{} @{}

\n", post.name, post.date)); let _ = match decrypt(post.crypt, &password) { Ok(v) => { let _ = &mut ret_text.push_str(&v); let _ = &mut ret_text.push_str("\n"); ret_text.push_str("\n"); break; }, Err(_) => { *&mut ret_text = String::from("Could not decrypt your post."); break; }, }; } } if ret_text.len() == 0 { *&mut ret_text.push_str("Something went wrong. Does the post exist ?"); } return HttpResponse::Ok().body(ret_text); } #[get("/")] async fn get_index() -> impl Responder { let mut page = fs::read_to_string("./Files/index.html").unwrap(); let (_, mut posts) = get_all_posts(); posts.push_str("\n\n"); page.push_str(&posts); return HttpResponse::Ok().body(page); } #[actix_web::main] async fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new() .service(get_index) .service(post_index) .service(show_post) }) .bind(("0.0.0.0", 8080))? .run() .await }