2022-06-03 16:24:34 +00:00
|
|
|
use crate::dbio::save_to_file;
|
2022-07-13 21:50:10 +00:00
|
|
|
use crate::pasta::PastaFile;
|
2022-06-03 16:24:34 +00:00
|
|
|
use crate::util::animalnumbers::to_animal_names;
|
2022-11-01 13:15:13 +00:00
|
|
|
use crate::util::hashids::to_hashids;
|
2022-06-03 16:24:34 +00:00
|
|
|
use crate::util::misc::is_valid_url;
|
|
|
|
use crate::{AppState, Pasta, ARGS};
|
|
|
|
use actix_multipart::Multipart;
|
|
|
|
use actix_web::{get, web, Error, HttpResponse, Responder};
|
|
|
|
use askama::Template;
|
2022-07-13 21:50:10 +00:00
|
|
|
use bytesize::ByteSize;
|
2022-06-03 16:24:34 +00:00
|
|
|
use futures::TryStreamExt;
|
2022-07-31 20:31:35 +00:00
|
|
|
use log::warn;
|
2022-06-03 16:24:34 +00:00
|
|
|
use rand::Rng;
|
|
|
|
use std::io::Write;
|
|
|
|
use std::time::{SystemTime, UNIX_EPOCH};
|
|
|
|
|
|
|
|
#[derive(Template)]
|
|
|
|
#[template(path = "index.html")]
|
|
|
|
struct IndexTemplate<'a> {
|
|
|
|
args: &'a ARGS,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[get("/")]
|
|
|
|
pub async fn index() -> impl Responder {
|
2022-06-04 20:50:34 +00:00
|
|
|
HttpResponse::Ok()
|
2022-06-03 16:24:34 +00:00
|
|
|
.content_type("text/html")
|
|
|
|
.body(IndexTemplate { args: &ARGS }.render().unwrap())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn create(
|
|
|
|
data: web::Data<AppState>,
|
|
|
|
mut payload: Multipart,
|
|
|
|
) -> Result<HttpResponse, Error> {
|
|
|
|
if ARGS.readonly {
|
|
|
|
return Ok(HttpResponse::Found()
|
2022-10-12 15:13:21 +00:00
|
|
|
.append_header(("Location", format!("{}/", ARGS.public_path)))
|
2022-06-03 16:24:34 +00:00
|
|
|
.finish());
|
|
|
|
}
|
|
|
|
|
2023-02-17 21:14:43 +00:00
|
|
|
let mut pastas = data.pastas.lock().await;
|
2022-06-03 16:24:34 +00:00
|
|
|
|
|
|
|
let timenow: i64 = match SystemTime::now().duration_since(UNIX_EPOCH) {
|
|
|
|
Ok(n) => n.as_secs(),
|
|
|
|
Err(_) => {
|
|
|
|
log::error!("SystemTime before UNIX EPOCH!");
|
|
|
|
0
|
|
|
|
}
|
|
|
|
} as i64;
|
|
|
|
|
|
|
|
let mut new_pasta = Pasta {
|
|
|
|
id: rand::thread_rng().gen::<u16>() as u64,
|
|
|
|
content: String::from("No Text Content"),
|
2022-07-13 21:50:10 +00:00
|
|
|
file: None,
|
2022-06-03 16:24:34 +00:00
|
|
|
extension: String::from(""),
|
|
|
|
private: false,
|
|
|
|
editable: false,
|
|
|
|
created: timenow,
|
2022-10-29 11:11:55 +00:00
|
|
|
read_count: 0,
|
|
|
|
burn_after_reads: 0,
|
|
|
|
last_read: timenow,
|
2022-06-03 16:24:34 +00:00
|
|
|
pasta_type: String::from(""),
|
|
|
|
expiration: 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
while let Some(mut field) = payload.try_next().await? {
|
|
|
|
match field.name() {
|
|
|
|
"editable" => {
|
|
|
|
new_pasta.editable = true;
|
|
|
|
}
|
|
|
|
"private" => {
|
|
|
|
new_pasta.private = true;
|
|
|
|
}
|
|
|
|
"expiration" => {
|
|
|
|
while let Some(chunk) = field.try_next().await? {
|
|
|
|
new_pasta.expiration = match std::str::from_utf8(&chunk).unwrap() {
|
2023-02-17 21:14:43 +00:00
|
|
|
// TODO: customizable times
|
2022-06-03 16:24:34 +00:00
|
|
|
"1min" => timenow + 60,
|
|
|
|
"10min" => timenow + 60 * 10,
|
|
|
|
"1hour" => timenow + 60 * 60,
|
|
|
|
"24hour" => timenow + 60 * 60 * 24,
|
2022-11-09 10:57:49 +00:00
|
|
|
"3days" => timenow + 60 * 60 * 24 * 3,
|
2022-06-03 16:24:34 +00:00
|
|
|
"1week" => timenow + 60 * 60 * 24 * 7,
|
2022-10-29 11:11:55 +00:00
|
|
|
"never" => {
|
|
|
|
if ARGS.no_eternal_pasta {
|
|
|
|
timenow + 60 * 60 * 24 * 7
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
}
|
|
|
|
}
|
2022-06-03 16:24:34 +00:00
|
|
|
_ => {
|
|
|
|
log::error!("{}", "Unexpected expiration time!");
|
2022-10-29 11:11:55 +00:00
|
|
|
timenow + 60 * 60 * 24 * 7
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"burn_after" => {
|
|
|
|
while let Some(chunk) = field.try_next().await? {
|
|
|
|
new_pasta.burn_after_reads = match std::str::from_utf8(&chunk).unwrap() {
|
2023-02-17 21:14:43 +00:00
|
|
|
// TODO: also make customizable
|
|
|
|
// maybe options in config files, with defaults
|
2022-10-29 11:11:55 +00:00
|
|
|
// give an extra read because the user will be redirected to the pasta page automatically
|
|
|
|
"1" => 2,
|
|
|
|
"10" => 10,
|
|
|
|
"100" => 100,
|
|
|
|
"1000" => 1000,
|
|
|
|
"10000" => 10000,
|
|
|
|
"0" => 0,
|
|
|
|
_ => {
|
|
|
|
log::error!("{}", "Unexpected burn after value!");
|
2022-06-03 16:24:34 +00:00
|
|
|
0
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"content" => {
|
2022-11-06 21:47:56 +00:00
|
|
|
let mut content = String::from("");
|
2022-06-03 16:24:34 +00:00
|
|
|
while let Some(chunk) = field.try_next().await? {
|
2022-11-06 21:47:56 +00:00
|
|
|
content.push_str(std::str::from_utf8(&chunk).unwrap().to_string().as_str());
|
2022-11-07 18:25:42 +00:00
|
|
|
}
|
2022-11-08 21:30:16 +00:00
|
|
|
if !content.is_empty() {
|
2022-11-07 18:25:42 +00:00
|
|
|
new_pasta.content = content;
|
|
|
|
|
2022-06-03 16:24:34 +00:00
|
|
|
new_pasta.pasta_type = if is_valid_url(new_pasta.content.as_str()) {
|
|
|
|
String::from("url")
|
|
|
|
} else {
|
|
|
|
String::from("text")
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"syntax-highlight" => {
|
|
|
|
while let Some(chunk) = field.try_next().await? {
|
|
|
|
new_pasta.extension = std::str::from_utf8(&chunk).unwrap().to_string();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
"file" => {
|
2022-10-01 06:11:03 +00:00
|
|
|
if ARGS.no_file_upload {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-07-31 20:31:35 +00:00
|
|
|
let path = field.content_disposition().get_filename();
|
2022-06-03 16:24:34 +00:00
|
|
|
|
2022-07-31 20:31:35 +00:00
|
|
|
let path = match path {
|
2022-06-03 16:24:34 +00:00
|
|
|
Some("") => continue,
|
2022-07-31 20:31:35 +00:00
|
|
|
Some(p) => p,
|
2022-06-03 16:24:34 +00:00
|
|
|
None => continue,
|
|
|
|
};
|
|
|
|
|
2022-11-08 21:30:16 +00:00
|
|
|
let mut file = match PastaFile::from_unsanitized(path) {
|
2022-07-31 20:31:35 +00:00
|
|
|
Ok(f) => f,
|
|
|
|
Err(e) => {
|
|
|
|
warn!("Unsafe file name: {e:?}");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-10-29 11:11:55 +00:00
|
|
|
std::fs::create_dir_all(format!(
|
|
|
|
"./pasta_data/public/{}",
|
|
|
|
&new_pasta.id_as_animals()
|
|
|
|
))
|
|
|
|
.unwrap();
|
2022-06-03 16:24:34 +00:00
|
|
|
|
2022-07-31 20:31:35 +00:00
|
|
|
let filepath = format!(
|
2022-07-31 20:49:36 +00:00
|
|
|
"./pasta_data/public/{}/{}",
|
2022-07-31 20:31:35 +00:00
|
|
|
&new_pasta.id_as_animals(),
|
|
|
|
&file.name()
|
|
|
|
);
|
2022-06-03 16:24:34 +00:00
|
|
|
|
2022-07-31 20:31:35 +00:00
|
|
|
let mut f = web::block(|| std::fs::File::create(filepath)).await??;
|
2022-07-13 21:50:10 +00:00
|
|
|
let mut size = 0;
|
2022-06-03 16:24:34 +00:00
|
|
|
while let Some(chunk) = field.try_next().await? {
|
2022-07-13 21:50:10 +00:00
|
|
|
size += chunk.len();
|
2022-06-03 16:24:34 +00:00
|
|
|
f = web::block(move || f.write_all(&chunk).map(|_| f)).await??;
|
|
|
|
}
|
|
|
|
|
2022-07-31 20:31:35 +00:00
|
|
|
file.size = ByteSize::b(size as u64);
|
|
|
|
|
|
|
|
new_pasta.file = Some(file);
|
2022-06-03 16:24:34 +00:00
|
|
|
new_pasta.pasta_type = String::from("text");
|
|
|
|
}
|
2022-11-06 21:47:56 +00:00
|
|
|
field => {
|
|
|
|
log::error!("Unexpected multipart field: {}", field);
|
|
|
|
}
|
2022-06-03 16:24:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let id = new_pasta.id;
|
|
|
|
|
|
|
|
pastas.push(new_pasta);
|
|
|
|
|
|
|
|
save_to_file(&pastas);
|
|
|
|
|
2022-11-01 13:15:13 +00:00
|
|
|
let slug = if ARGS.hash_ids {
|
|
|
|
to_hashids(id)
|
|
|
|
} else {
|
|
|
|
to_animal_names(id)
|
|
|
|
};
|
2022-06-03 16:24:34 +00:00
|
|
|
Ok(HttpResponse::Found()
|
2022-11-01 13:15:13 +00:00
|
|
|
.append_header(("Location", format!("{}/pasta/{}", ARGS.public_path, slug)))
|
2022-06-03 16:24:34 +00:00
|
|
|
.finish())
|
|
|
|
}
|