V1
This commit is contained in:
commit
b2bff238b2
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
1653
Cargo.lock
generated
Normal file
1653
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
19
Cargo.toml
Normal file
19
Cargo.toml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
[package]
|
||||||
|
name = "sqpad"
|
||||||
|
version = "1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
actix-web = "4.3.1"
|
||||||
|
chrono = "0.4.26"
|
||||||
|
#clap = "4.2.4"
|
||||||
|
magic-crypt = "3.1.12"
|
||||||
|
#message-io = "0.15.1"
|
||||||
|
rand = "0.8.5"
|
||||||
|
serde = "1.0.164"
|
||||||
|
serde_derive = "1.0.165"
|
||||||
|
serde_json = "1.0.99"
|
||||||
|
toml = "0.7.3"
|
||||||
|
|
2
Files/footer.html
Normal file
2
Files/footer.html
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
</body>
|
||||||
|
</html>
|
109
Files/header.html
Normal file
109
Files/header.html
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>SqDecrypt</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: black;
|
||||||
|
color: white;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
#logo {
|
||||||
|
max-width: 200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#form-container {
|
||||||
|
max-width: 400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 20px;
|
||||||
|
border: 2px solid orange;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"] {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px;
|
||||||
|
margin-top: 5px;
|
||||||
|
border: 1px solid orange;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: black;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="submit"] {
|
||||||
|
background-color: orange;
|
||||||
|
color: black;
|
||||||
|
padding: 10px 20px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="submit"]:hover {
|
||||||
|
background-color: darkorange;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CSS for the table */
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-top: 20px;
|
||||||
|
border: 2px solid orange;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
padding: 10px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background-color: black;
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:nth-child(even) {
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:nth-child(odd) {
|
||||||
|
background-color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Additional styles for the table */
|
||||||
|
caption {
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>SqPad</h1>
|
||||||
|
|
138
Files/index.html
Normal file
138
Files/index.html
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>SqDecrypt</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background-color: black;
|
||||||
|
color: white;
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
#logo {
|
||||||
|
max-width: 200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#form-container {
|
||||||
|
max-width: 400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 20px;
|
||||||
|
border: 2px solid orange;
|
||||||
|
border-radius: 10px;
|
||||||
|
align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"] {
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid orange;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: black;
|
||||||
|
color: white;
|
||||||
|
margin: auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid orange;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: black;
|
||||||
|
color: white;
|
||||||
|
align: center;
|
||||||
|
margin: auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
input[type="submit"] {
|
||||||
|
background-color: orange;
|
||||||
|
color: black;
|
||||||
|
padding: 10px 20px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="submit"]:hover {
|
||||||
|
background-color: darkorange;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CSS for the table */
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-top: 20px;
|
||||||
|
border: 2px solid orange;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
padding: 10px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background-color: black;
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:nth-child(even) {
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:nth-child(odd) {
|
||||||
|
background-color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Additional styles for the table */
|
||||||
|
caption {
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-container {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>SqPad</h1>
|
||||||
|
|
||||||
|
<div id="form-container">
|
||||||
|
<form action="/" method="post" enctype="application/x-www-form-urlencoded">
|
||||||
|
|
||||||
|
<label for="name">Name:</label>
|
||||||
|
<input type="text" id="name" name="name" required>
|
||||||
|
|
||||||
|
<label for="content">Content:</label>
|
||||||
|
<textarea type="input" id="content" name="content" rows="30" cols="55" required>
|
||||||
|
Your content here.
|
||||||
|
</textarea>
|
||||||
|
|
||||||
|
<input type="submit" value="submit">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
0
Files/posts.json
Normal file
0
Files/posts.json
Normal file
4
README.md
Normal file
4
README.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# SqPad
|
||||||
|
A encrypted pastebin.
|
||||||
|
|
||||||
|
Juste compile and launch it in the same folder as "Files".
|
2
config.toml
Normal file
2
config.toml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
password = "squirrels"
|
||||||
|
port = "3456"
|
150
src/main.rs
Normal file
150
src/main.rs
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
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
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user