SqPad/src/main.rs
Justine Pelletreau b2bff238b2 V1
2023-07-03 16:06:26 +02:00

151 lines
4.9 KiB
Rust

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<String, String> {
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<EncryptedPost>, String) {
let posts = fs::read_to_string("./Files/posts.json").unwrap();
let mut res = Vec::new();
let mut display = String::from("\n<table title=\"Latest Posts\">\n<caption>Latest Posts</caption>\n");
for line in posts.lines() {
let post: EncryptedPost = serde_json::from_str(&line).unwrap();
res.push(post.clone());
display.push_str(&format!("<tr>\n<td>{}</td>\n<td>{}</td>\n</tr>\n<br>\n", post.date, post.id));
}
display.push_str("</table>");
return (res, display);
}
#[post("/")]
async fn post_index(web::Form(form): web::Form<Post>, 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::<u64>();
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 <a href=\"http://{}/decrypt/{}/{}\">here</a>", 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 <b>This is the link to your post. Don't lose it !</b><br>\n><i>{} @{}</i><br><br>\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("</body>\n</html>");
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</body>\n</html>");
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
}