feat: replace config with figment and a bunch more
I also replaced magic-tree-mini with the full magic lib, as it's better. I also updated all the crates. oh yeah I added a robots.txt I kinda started working on something else, then did another thing half way through then I did another thing. audron is a champion, a true hero.main
parent
567dcc3c03
commit
1ccc82cac0
File diff suppressed because it is too large
Load Diff
|
@ -20,7 +20,9 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
|||
nanoid = "0.4.0"
|
||||
chrono = "0.4.23"
|
||||
rand = "0.8.5"
|
||||
config = "0.13.3"
|
||||
figment = { version = "0.10.10", features = ["toml"] }
|
||||
lazy_static = "1.4.0"
|
||||
tree_magic_mini = "3.0.3"
|
||||
chrono-humanize = "0.2.2"
|
||||
magic = "0.13.0"
|
||||
serde_derive = "1.0.164"
|
||||
serde = "1.0.164"
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
use serde_derive::Deserialize;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct ServerConfig {
|
||||
pub host: String,
|
||||
pub port: u16,
|
||||
pub nginx_sendfile: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct DatabaseConfig {
|
||||
pub sql_backend: String,
|
||||
pub url: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct LoggingConfig {
|
||||
pub level: String,
|
||||
pub metrics: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct OperationsConfig {
|
||||
pub filename_size: u8,
|
||||
pub adminkey_size: u8,
|
||||
pub apikey_size: u8,
|
||||
pub engine_mode: i64,
|
||||
pub cleaner_interval: i32,
|
||||
pub file_expiry_min: u32,
|
||||
pub file_expiry_max: u32,
|
||||
pub max_filesize: u64,
|
||||
pub banned_mimetype: Vec<String>,
|
||||
pub unsafe_mimetype: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct AppConfig {
|
||||
pub server: ServerConfig,
|
||||
pub database: DatabaseConfig,
|
||||
pub logging: LoggingConfig,
|
||||
pub operations: OperationsConfig,
|
||||
}
|
11
src/db.rs
11
src/db.rs
|
@ -277,12 +277,15 @@ fn update_expiry_override() {
|
|||
|
||||
/// This function gets a array of all the files uploaded by a specific API Key.
|
||||
/// This allows for the select few who hold an API key to view the information about the files they upload.
|
||||
pub async fn get_my_files(sqlconn: &Pool<Sqlite>, ip: &str) -> Result<Vec<FileMetric>, sqlx::Error> {
|
||||
pub async fn get_my_files(
|
||||
sqlconn: &Pool<Sqlite>,
|
||||
ip: &str,
|
||||
) -> Result<Vec<FileMetric>, sqlx::Error> {
|
||||
let result = sqlx::query!(
|
||||
"SELECT file, filesize, mimetype, views, expiry, expiry_override, ip, isDeleted
|
||||
FROM files
|
||||
WHERE ip = ?",
|
||||
ip
|
||||
ip
|
||||
)
|
||||
.fetch_all(sqlconn)
|
||||
.await?;
|
||||
|
@ -300,7 +303,7 @@ pub async fn get_my_files(sqlconn: &Pool<Sqlite>, ip: &str) -> Result<Vec<FileMe
|
|||
filesize: row.filesize,
|
||||
views: row.views.unwrap(),
|
||||
expiry: row.expiry,
|
||||
is_deleted
|
||||
is_deleted,
|
||||
};
|
||||
files.push(file);
|
||||
}
|
||||
|
@ -421,7 +424,7 @@ pub async fn get_file_metrics(sqlconn: &Pool<Sqlite>) -> Option<Vec<FileMetric>>
|
|||
filesize: row.filesize,
|
||||
views: row.views.unwrap(),
|
||||
expiry: row.expiry,
|
||||
is_deleted: false
|
||||
is_deleted: false,
|
||||
};
|
||||
// Then add the struct to the Vec
|
||||
filevec.push(file);
|
||||
|
|
|
@ -37,8 +37,8 @@ where
|
|||
// This should check if the adminkey exists for another file, and regenerate if it does.
|
||||
pub async fn generate_adminkey(sqlconn: &Pool<Sqlite>) -> String {
|
||||
let adminkey_size: usize = CONFIG
|
||||
.get_int("operations.adminkey_size")
|
||||
.expect("Couldn't find 'adminkey_size' in config. :(")
|
||||
.operations
|
||||
.adminkey_size
|
||||
.try_into()
|
||||
.expect("Problem with converting adminkey_size to usize");
|
||||
let adminkey = nanoid!(adminkey_size);
|
||||
|
@ -54,9 +54,7 @@ pub async fn calculate_expiry<S>(sqlconn: &Pool<Sqlite>, filename: S, filesize:
|
|||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
let engine_mode: i64 = CONFIG
|
||||
.get_int("operations.engine_mode")
|
||||
.expect("Couldn't find 'engine_mode' in config. :(");
|
||||
let engine_mode: i64 = CONFIG.operations.engine_mode;
|
||||
|
||||
let expiry = match engine_mode {
|
||||
1 => engine_1(sqlconn, filename.as_ref()).await,
|
||||
|
@ -87,8 +85,8 @@ where
|
|||
{
|
||||
// Read the file expiry settings from the config
|
||||
let file_expiry_min: i32 = CONFIG
|
||||
.get_int("operations.file_expiry_min")
|
||||
.expect("Couldn't find 'file_expiry_min' in config. :(")
|
||||
.operations
|
||||
.file_expiry_min
|
||||
.try_into()
|
||||
.expect("The file_expiry_min is wayyyy to big.");
|
||||
|
||||
|
@ -115,15 +113,9 @@ async fn engine_2(filesize: i32) -> i32 {
|
|||
// audron REVIEW: also, probably make that engine match use enums and give them descriptive names
|
||||
|
||||
// Load settings from config
|
||||
let file_expiry_min: f64 = CONFIG
|
||||
.get_float("operations.file_expiry_min")
|
||||
.expect("Couldn't find 'file_expiry_min' in config. :(");
|
||||
let file_expiry_max: f64 = CONFIG
|
||||
.get_float("operations.file_expiry_max")
|
||||
.expect("Couldn't find 'file_expiry_min' in config. :(");
|
||||
let max_filesize: f64 = CONFIG
|
||||
.get_float("operations.max_filesize")
|
||||
.expect("Couldn't find 'max_filesize' in config. :(");
|
||||
let file_expiry_min: f64 = CONFIG.operations.file_expiry_min.into();
|
||||
let file_expiry_max: f64 = CONFIG.operations.file_expiry_max.into();
|
||||
let max_filesize: f64 = CONFIG.operations.max_filesize as f64;
|
||||
|
||||
let time = SystemTime::now()
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
|
|
|
@ -68,7 +68,7 @@ pub async fn delete_file(req: &mut Request, res: &mut Response) {
|
|||
};
|
||||
render_template(res, &headers, "error.html", template, StatusCode::OK).await
|
||||
} else {
|
||||
res.set_status_code(StatusCode::OK);
|
||||
res.status_code(StatusCode::OK);
|
||||
res.render(Text::Json(r#"{"deletion": "success"}"#));
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ pub async fn delete_file(req: &mut Request, res: &mut Response) {
|
|||
)
|
||||
.await
|
||||
} else {
|
||||
res.set_status_code(StatusCode::UNAUTHORIZED);
|
||||
res.status_code(StatusCode::UNAUTHORIZED);
|
||||
res.render(Text::Json(r#"{"error": "InvalidAdminKey"}"#));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,12 +23,5 @@ pub async fn index(req: &mut Request, res: &mut Response) {
|
|||
),
|
||||
..Default::default()
|
||||
};
|
||||
render_template(
|
||||
res,
|
||||
headers,
|
||||
"upload.html",
|
||||
template,
|
||||
StatusCode::OK,
|
||||
)
|
||||
.await
|
||||
render_template(res, headers, "upload.html", template, StatusCode::OK).await
|
||||
}
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
use salvo::{handler, Request, Response, hyper::header::HOST, prelude::StatusCode};
|
||||
use salvo::{handler, hyper::header::HOST, prelude::StatusCode, Request, Response};
|
||||
|
||||
use crate::{db, SQLITE, handlers::{TemplateStruct, render_template, convert_file_size, convert_unix_timestamp, count_file_metrics}};
|
||||
use crate::{
|
||||
db,
|
||||
handlers::{
|
||||
convert_file_size, convert_unix_timestamp, count_file_metrics, render_template,
|
||||
TemplateStruct,
|
||||
},
|
||||
SQLITE,
|
||||
};
|
||||
|
||||
use super::guess_ip;
|
||||
|
||||
|
@ -14,7 +21,7 @@ pub async fn list_files(req: &mut Request, res: &mut Response) {
|
|||
|
||||
// Get the IP of the person viewing, so we can show their files
|
||||
let headers = req.headers();
|
||||
let remote_addr = &req.remote_addr().unwrap().clone();
|
||||
let remote_addr = &req.remote_addr().clone();
|
||||
let ip = guess_ip(headers, remote_addr);
|
||||
|
||||
tracing::debug!("list_files(remote_addr, ip): {:?}, {:?}", remote_addr, ip);
|
||||
|
@ -77,12 +84,15 @@ pub async fn list_files(req: &mut Request, res: &mut Response) {
|
|||
render_template(res, headers, template_filename, template, status_code).await;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Close the table when all files are 'rendered'
|
||||
html.push_str("</table>");
|
||||
|
||||
// Log this to the server console
|
||||
tracing::info!("New Request: /my_files ({} Files)", count_file_metrics(&files) );
|
||||
// Log this to the server console
|
||||
tracing::info!(
|
||||
"New Request: /my_files ({} Files)",
|
||||
count_file_metrics(&files)
|
||||
);
|
||||
|
||||
let template_filename = "my_files.html";
|
||||
let template = TemplateStruct {
|
||||
|
|
|
@ -1,18 +1,22 @@
|
|||
use std::path::PathBuf;
|
||||
use std::{path::PathBuf, time::Duration};
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use chrono_humanize::Humanize;
|
||||
use ramhorns::{Content, Ramhorns};
|
||||
use salvo::{
|
||||
addr::SocketAddr,
|
||||
conn::SocketAddr,
|
||||
hyper::{header::HOST, HeaderMap},
|
||||
prelude::StatusCode,
|
||||
writer::Text,
|
||||
Response,
|
||||
};
|
||||
use chrono::{DateTime, Utc};
|
||||
use chrono_humanize::Humanize;
|
||||
use sqlx::Error;
|
||||
use tokio::{task, time};
|
||||
|
||||
use crate::{CONFIG, db::FileMetric};
|
||||
use crate::{
|
||||
db::{self, FileMetric},
|
||||
engine, CONFIG, COOKIE, SQLITE,
|
||||
};
|
||||
|
||||
// Submodules for each request handler
|
||||
pub mod delete_file;
|
||||
|
@ -132,10 +136,13 @@ pub async fn render_template(
|
|||
};
|
||||
|
||||
// Tell the engine to render the template assigned above
|
||||
let rendered = tpls.get(template_filename).expect("Couldn't find the template filename").render(&template);
|
||||
let rendered = tpls
|
||||
.get(template_filename)
|
||||
.expect("Couldn't find the template filename")
|
||||
.render(&template);
|
||||
|
||||
// Set the status code and render the completed page
|
||||
res.set_status_code(status_code);
|
||||
res.status_code(status_code);
|
||||
res.render(Text::Html(rendered));
|
||||
}
|
||||
|
||||
|
@ -146,9 +153,7 @@ where
|
|||
S: AsRef<str>,
|
||||
{
|
||||
// Load the banned files from the config
|
||||
let banned = CONFIG
|
||||
.get_array("operations.banned_mimetype")
|
||||
.expect("Couldn't find 'banned_mimetype' in config. :(");
|
||||
let banned = &CONFIG.operations.banned_mimetype;
|
||||
|
||||
// Convert the provided mimetype to lowercase for case-insensitive comparison
|
||||
let lowercase_mimetype = mimetype.as_ref().to_lowercase();
|
||||
|
@ -157,11 +162,7 @@ where
|
|||
let lowercase_banned: Vec<String> = banned
|
||||
.iter()
|
||||
.map(|mime| {
|
||||
let mime_str = mime
|
||||
.clone()
|
||||
.into_string()
|
||||
.expect("Invalid banned mimetype in config")
|
||||
.to_lowercase();
|
||||
let mime_str = mime.clone().to_lowercase();
|
||||
tracing::debug!("Banned mimetype: {}", mime_str);
|
||||
mime_str
|
||||
})
|
||||
|
@ -172,7 +173,6 @@ where
|
|||
lowercase_banned.contains(&lowercase_mimetype)
|
||||
}
|
||||
|
||||
|
||||
/// Convert the filesize to a human readable number
|
||||
fn convert_file_size(file_size: i64) -> String {
|
||||
let units = ["B", "KB", "MB", "GB", "TB"];
|
||||
|
@ -207,4 +207,58 @@ fn count_file_metrics(file_metrics: &Result<Vec<FileMetric>, Error>) -> usize {
|
|||
Ok(metrics) => metrics.len(),
|
||||
Err(_) => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Guess the mimetype
|
||||
/// This uses the magic library, which seems to be the most accurate out of the ones I've seen.
|
||||
/// As the name suggests, this is magic. I have no idea HOW it works, but it should.
|
||||
/// This doesn't need the full file loaded into memory, so it loads 2048 bytes.
|
||||
fn detect_mime_type(file_data: &[u8]) -> Result<String, magic::MagicError> {
|
||||
COOKIE.with(|cookie| {
|
||||
let result = cookie.buffer(file_data)?;
|
||||
Ok(result)
|
||||
})
|
||||
}
|
||||
|
||||
// This spawns a tokio task to run a interval timer forever.
|
||||
// the interval timer runs every 'period' seconds.
|
||||
pub fn cleaner_thread(period: i32) {
|
||||
let _forever = task::spawn(async move {
|
||||
let mut interval = time::interval(Duration::from_secs(period.try_into().unwrap()));
|
||||
let sqlconn = SQLITE.get().unwrap();
|
||||
loop {
|
||||
// Wait for the next interval
|
||||
interval.tick().await;
|
||||
// Get a vec of files that will expire this loop.
|
||||
// Also I'm sorta just realising how annoying it is to pass sqlconn through like 4 functions deep just to do something.
|
||||
let old_files_result = db::get_old_files(sqlconn).await;
|
||||
|
||||
match old_files_result {
|
||||
Ok(file_list) => {
|
||||
tracing::info!("Running Cleaner");
|
||||
for file in file_list {
|
||||
// Delete the file from the database
|
||||
db::delete_file(sqlconn, &file).await.unwrap_or_else(|err| {
|
||||
tracing::error!(
|
||||
"Failed to delete file from database: {}, error: {:?}",
|
||||
&file,
|
||||
err
|
||||
);
|
||||
0
|
||||
});
|
||||
// Delete the file from the filesystem
|
||||
engine::delete_file(&file).await.unwrap_or_else(|err| {
|
||||
tracing::error!("Failed to delete file from database: {:?}", err);
|
||||
});
|
||||
}
|
||||
tracing::info!("Cleaner finished");
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!("Error getting files to expire: {}", err);
|
||||
// Return a empty Vec so it doesn't delete anything
|
||||
return Vec::<String>::new();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::handlers::{guess_ip, is_mimetype_banned};
|
||||
use config::Config;
|
||||
use salvo::addr::SocketAddr as SalvoSocketAddr;
|
||||
use crate::handlers::{detect_mime_type, guess_ip, is_mimetype_banned};
|
||||
use salvo::conn::SocketAddr as SalvoSocketAddr;
|
||||
use salvo::hyper::HeaderMap;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||
|
||||
|
@ -102,4 +101,181 @@ mod tests {
|
|||
let result = is_mimetype_banned("aUdIo/MP4").await;
|
||||
assert!(!result);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_detect_mime_type() {
|
||||
// Test case 1: Valid file data
|
||||
let file_data = b"This is a test file";
|
||||
let result = detect_mime_type(file_data);
|
||||
println!("Test 1: Valid file data: {:?}", result);
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(result.unwrap(), "text/plain");
|
||||
|
||||
// Test case 2: Empty file data
|
||||
let file_data = b"";
|
||||
let result = detect_mime_type(file_data);
|
||||
println!("Test 2: Empty file data Result: {:?}", result);
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(result.unwrap(), "application/x-empty");
|
||||
|
||||
// Test case 4: putty.exe
|
||||
let file_data: &[u8] = &[
|
||||
// Putty.exe trimmed at 2048
|
||||
// This is stupid, and probably not needed for a test lol.
|
||||
0x4D, 0x5A, 0x78, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x0E, 0x1F, 0xBA, 0x0E, 0x00, 0xB4,
|
||||
0x09, 0xCD, 0x21, 0xB8, 0x01, 0x4C, 0xCD, 0x21, 0x54, 0x68, 0x69, 0x73, 0x20, 0x70,
|
||||
0x72, 0x6F, 0x67, 0x72, 0x61, 0x6D, 0x20, 0x63, 0x61, 0x6E, 0x6E, 0x6F, 0x74, 0x20,
|
||||
0x62, 0x65, 0x20, 0x72, 0x75, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x44, 0x4F, 0x53, 0x20,
|
||||
0x6D, 0x6F, 0x64, 0x65, 0x2E, 0x24, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x64, 0x86,
|
||||
0x0A, 0x00, 0x91, 0x10, 0x5C, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xF0, 0x00, 0x22, 0x00, 0x0B, 0x02, 0x0E, 0x00, 0x00, 0x5A, 0x0E, 0x00, 0x00, 0x70,
|
||||
0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x88, 0x0B, 0x00, 0x00, 0x10, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02,
|
||||
0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x19, 0x00, 0x00, 0x04, 0x00, 0x00, 0x44, 0xA8,
|
||||
0x19, 0x00, 0x02, 0x00, 0x60, 0x81, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x11,
|
||||
0x12, 0x00, 0xB4, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x13, 0x00, 0x58, 0x9F, 0x05, 0x00,
|
||||
0x00, 0xD0, 0x12, 0x00, 0x78, 0x6C, 0x00, 0x00, 0x00, 0xCE, 0x18, 0x00, 0x28, 0x57,
|
||||
0x00, 0x00, 0x00, 0x40, 0x19, 0x00, 0xA8, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x0E, 0x10, 0x00, 0x28, 0x00, 0x00, 0x00,
|
||||
0xE0, 0xA6, 0x0F, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x28, 0x1D, 0x12, 0x00, 0xE0, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00,
|
||||
0x66, 0x59, 0x0E, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x5A, 0x0E, 0x00, 0x00, 0x04,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x20, 0x00, 0x00, 0x60, 0x2E, 0x72, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0xBC, 0xFF,
|
||||
0x03, 0x00, 0x00, 0x70, 0x0E, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x5E, 0x0E, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
|
||||
0x00, 0x40, 0x2E, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x9C, 0x55, 0x00, 0x00,
|
||||
0x00, 0x70, 0x12, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x5E, 0x12, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0xC0,
|
||||
0x2E, 0x70, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x78, 0x6C, 0x00, 0x00, 0x00, 0xD0,
|
||||
0x12, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x6E, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x2E, 0x30,
|
||||
0x30, 0x63, 0x66, 0x67, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x40, 0x13, 0x00,
|
||||
0x00, 0x02, 0x00, 0x00, 0x00, 0xDC, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x2E, 0x67, 0x78, 0x66,
|
||||
0x67, 0x00, 0x00, 0x00, 0x60, 0x2A, 0x00, 0x00, 0x00, 0x50, 0x13, 0x00, 0x00, 0x2C,
|
||||
0x00, 0x00, 0x00, 0xDE, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x2E, 0x74, 0x6C, 0x73, 0x00, 0x00,
|
||||
0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x80, 0x13, 0x00, 0x00, 0x02, 0x00, 0x00,
|
||||
0x00, 0x0A, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x40, 0x00, 0x00, 0xC0, 0x5F, 0x52, 0x44, 0x41, 0x54, 0x41, 0x00, 0x00,
|
||||
0x5C, 0x01, 0x00, 0x00, 0x00, 0x90, 0x13, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0C,
|
||||
0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x40, 0x00, 0x00, 0x40, 0x2E, 0x72, 0x73, 0x72, 0x63, 0x00, 0x00, 0x00, 0x58, 0x9F,
|
||||
0x05, 0x00, 0x00, 0xA0, 0x13, 0x00, 0x00, 0xA0, 0x05, 0x00, 0x00, 0x0E, 0x13, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
|
||||
0x00, 0x40, 0x2E, 0x72, 0x65, 0x6C, 0x6F, 0x63, 0x00, 0x00, 0xA8, 0x1E, 0x00, 0x00,
|
||||
0x00, 0x40, 0x19, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xAE, 0x18, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x42,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x56, 0x57, 0x53, 0x48, 0x83, 0xEC, 0x40, 0x48, 0x89, 0xD6, 0x48, 0x89,
|
||||
0xCF, 0x4C, 0x89, 0x44, 0x24, 0x70, 0x4C, 0x89, 0x4C, 0x24, 0x78, 0x48, 0x8B, 0x05,
|
||||
0x32, 0x60, 0x12, 0x00, 0x48, 0x31, 0xE0, 0x48, 0x89, 0x44, 0x24, 0x38, 0x48, 0x8D,
|
||||
0x5C, 0x24, 0x70, 0x48, 0x89, 0x5C, 0x24, 0x30, 0xE8, 0xBB, 0x61, 0x00, 0x00, 0x48,
|
||||
0x8B, 0x08, 0x48, 0x83, 0xC9, 0x01, 0x48, 0x89, 0x5C, 0x24, 0x28, 0x48, 0xC7, 0x44,
|
||||
0x24, 0x20, 0x00, 0x00, 0x00, 0x00, 0x48, 0x89, 0xFA, 0x49, 0xC7, 0xC0, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0x49, 0x89, 0xF1, 0xE8, 0x9C, 0x98, 0x0B, 0x00, 0x89, 0xC6, 0x85, 0xC0,
|
||||
0xB8, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x48, 0xF0, 0x48, 0x8B, 0x4C, 0x24, 0x38, 0x48,
|
||||
0x31, 0xE1, 0xE8, 0x8B, 0x72, 0x0B, 0x00, 0x89, 0xF0, 0x48, 0x83, 0xC4, 0x40, 0x5B,
|
||||
0x5F, 0x5E, 0xC3, 0xCC, 0x56, 0x48, 0x83, 0xEC, 0x70, 0x48, 0x8B, 0x05, 0xC4, 0x5F,
|
||||
0x12, 0x00, 0x48, 0x31, 0xE0, 0x48, 0x89, 0x44, 0x24, 0x68, 0x48, 0x8B, 0x35, 0x6D,
|
||||
0x6E, 0x12, 0x00, 0x48, 0x85, 0xF6, 0x74, 0x20, 0x48, 0x83, 0x3D, 0x68, 0x6E, 0x12,
|
||||
0x00, 0x00, 0x74, 0x3A, 0x48, 0x8B, 0x4C, 0x24, 0x68, 0x48, 0x31, 0xE1, 0xE8, 0x49,
|
||||
0x72, 0x0B, 0x00, 0x48, 0x89, 0xF0, 0x48, 0x83, 0xC4, 0x70, 0x5E, 0xC3, 0x4C, 0x8B,
|
||||
0x05, 0x49, 0x61, 0x0E, 0x00, 0x31, 0xC9, 0x31, 0xD2, 0xE8, 0x60, 0x0F, 0x04, 0x00,
|
||||
0x48, 0x89, 0xC6, 0x48, 0x89, 0x05, 0x2E, 0x6E, 0x12, 0x00, 0x48, 0x83, 0x3D, 0x2E,
|
||||
0x6E, 0x12, 0x00, 0x00, 0x75, 0xC6, 0xC7, 0x44, 0x24, 0x20, 0x00, 0x00, 0x00, 0x00,
|
||||
0x48, 0x8D, 0x05, 0x6D, 0x00, 0x00, 0x00, 0x48, 0x89, 0x44, 0x24, 0x28, 0x48, 0xC7,
|
||||
0x44, 0x24, 0x30, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8B, 0x0D, 0x10, 0x6E, 0x12, 0x00,
|
||||
0x48, 0x89, 0x4C, 0x24, 0x38, 0xBA, 0xC8, 0x00, 0x00, 0x00, 0xFF, 0x15, 0x20, 0x10,
|
||||
0x12, 0x00, 0x48, 0x89, 0x44, 0x24, 0x40, 0xBA, 0x01, 0x7F, 0x00, 0x00, 0x31, 0xC9,
|
||||
0xFF, 0x15, 0x06, 0x10, 0x12, 0x00, 0x48, 0x89, 0x44, 0x24, 0x48, 0x0F, 0x57, 0xC0,
|
||||
0x0F, 0x11, 0x44, 0x24, 0x50, 0x48, 0x8B, 0x05, 0xCA, 0x6D, 0x12, 0x00, 0x48, 0x89,
|
||||
0x44, 0x24, 0x60, 0x48, 0x8D, 0x4C, 0x24, 0x20, 0xFF, 0x15, 0x62, 0x10, 0x12, 0x00,
|
||||
0x48, 0x8B, 0x35, 0xB3, 0x6D, 0x12, 0x00, 0xE9, 0x50, 0xFF, 0xFF, 0xFF, 0xCC, 0xCC,
|
||||
0xCC, 0xCC, 0xCC, 0xCC, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41, 0x54, 0x56, 0x57,
|
||||
0x55, 0x53, 0x48, 0x81, 0xEC, 0x18, 0x09, 0x00, 0x00, 0x4D, 0x89, 0xCD, 0x4D, 0x89,
|
||||
0xC7, 0x89, 0xD6, 0x49, 0x89, 0xCE, 0x48, 0x8B, 0x05, 0xCB, 0x5E, 0x12, 0x00, 0x48,
|
||||
0x31, 0xE0, 0x48, 0x89, 0x84, 0x24, 0x10, 0x09, 0x00, 0x00, 0x81, 0xFA, 0xFF, 0x01,
|
||||
0x00, 0x00, 0x0F, 0x8F, 0x8A, 0x00, 0x00, 0x00, 0x8D, 0x86, 0x60, 0xFF, 0xFF, 0xFF,
|
||||
0x83, 0xF8, 0x77, 0x0F, 0x87, 0xC1, 0x01, 0x00, 0x00, 0x48, 0x8D, 0x0D, 0xC2, 0x39,
|
||||
0x00, 0x00, 0x48, 0x63, 0x04, 0x81, 0x48, 0x01, 0xC8, 0xFF, 0xE0, 0xB9, 0x03, 0x00,
|
||||
0x00, 0x00, 0x44, 0x89, 0xEA, 0xE8, 0x08, 0xEE, 0x02, 0x00, 0x4C, 0x89, 0xF8, 0x48,
|
||||
0x83, 0xE0, 0xFD, 0x48, 0x3D, 0xE5, 0x00, 0x00, 0x00, 0x0F, 0x85, 0xDF, 0x02, 0x00,
|
||||
0x00, 0x81, 0xFE, 0x00, 0x01, 0x00, 0x00, 0x0F, 0x85, 0x8C, 0x24, 0x00, 0x00, 0x4C,
|
||||
0x89, 0xB4, 0x24, 0x10, 0x01, 0x00, 0x00, 0xC7, 0x84, 0x24, 0x18, 0x01, 0x00, 0x00,
|
||||
0x00, 0x01, 0x00, 0x00, 0x4C, 0x89, 0xBC, 0x24, 0x20, 0x01, 0x00, 0x00, 0x41, 0x81,
|
||||
0xE5, 0xFF, 0xDF, 0x00, 0x00, 0x4C, 0x89, 0xAC, 0x24, 0x28, 0x01, 0x00, 0x00, 0x48,
|
||||
0x8D, 0x8C, 0x24, 0x10, 0x01, 0x00, 0x00, 0xFF, 0x15, 0x89, 0x10, 0x12, 0x00, 0x31,
|
||||
0xF6, 0xE9, 0x61, 0x24, 0x00, 0x00, 0x81, 0xFE, 0xDF, 0x02, 0x00, 0x00, 0x0F, 0x8F,
|
||||
0x91, 0x01, 0x00, 0x00, 0x8D, 0x86, 0x00, 0xFE, 0xFF, 0xFF, 0x83, 0xF8, 0x32, 0x0F,
|
||||
0x87, 0xBF, 0x03, 0x00, 0x00, 0x48, 0x8D, 0x0D, 0x0C, 0x3B, 0x00, 0x00, 0x48, 0x63,
|
||||
0x04, 0x81, 0x48, 0x01, 0xC8, 0xFF, 0xE0, 0x4C, 0x39, 0x3D, 0x88, 0x80, 0x12, 0x00,
|
||||
0x75, 0x15, 0x4C, 0x39, 0x2D, 0x87, 0x80, 0x12, 0x00, 0x75, 0x0C, 0x81, 0x3D, 0x57,
|
||||
0x80, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, 0x74, 0x1F, 0xB1, 0x01, 0xE8, 0x5A, 0x5E,
|
||||
0x00, 0x00, 0x4C, 0x89, 0x3D, 0x63, 0x80, 0x12, 0x00, 0x4C, 0x89, 0x2D, 0x64, 0x80,
|
||||
0x12, 0x00, 0xC7, 0x05, 0x36, 0x80, 0x12, 0x00, 0x00, 0x02, 0x00, 0x00, 0xB9, 0x05,
|
||||
0x00, 0x00, 0x00, 0x44, 0x89, 0xEA, 0xE8, 0x35, 0xED, 0x02, 0x00, 0x41, 0xF6, 0xC7,
|
||||
0x13, 0x0F, 0x84, 0x1F, 0x1E, 0x00, 0x00, 0xFF, 0x15, 0x5D, 0x0D, 0x12, 0x00, 0x4C,
|
||||
0x39, 0xF0, 0x0F, 0x85, 0x10, 0x1E, 0x00, 0x00, 0x31, 0xC0, 0x41, 0xF6, 0xC7, 0x10,
|
||||
0x0F, 0x94, 0xC0, 0x83, 0xC8, 0x02, 0x41, 0xF6, 0xC7, 0x01, 0xBE, 0x01, 0x00, 0x00,
|
||||
0x00, 0x0F, 0x44, 0xF0, 0xE8, 0x6F, 0xB3, 0x00, 0x00, 0x41, 0x89, 0xC6, 0x44, 0x89,
|
||||
0xFF, 0x83, 0xE7, 0x08, 0xC1, 0xEF, 0x03, 0x41, 0x83, 0xE7, 0x04, 0x41, 0xC1, 0xEF,
|
||||
0x02, 0x44, 0x89, 0xE9, 0xC1, 0xF9, 0x10, 0x8B, 0x2D, 0x4F, 0x77, 0x12, 0x00, 0x89,
|
||||
0xC8, 0x29, 0xE8, 0x83, 0xC0, 0x01, 0x45, 0x85, 0xED, 0x0F, 0x49, 0xC1, 0x2B, 0x05,
|
||||
0x94, 0x77, 0x12, 0x00, 0x99, 0xF7, 0xFD, 0x89, 0xC5, 0x44, 0x89, 0xE9, 0xC1, 0xE1,
|
||||
0x10, 0x41, 0x0F, 0xBF, 0xD5, 0x8B, 0x1D, 0x23, 0x77, 0x12, 0x00, 0x89, 0xD0, 0x29,
|
||||
0xD8, 0x83, 0xC0, 0x01, 0x85, 0xC9, 0x0F, 0x49, 0xC2, 0x2B, 0x05, 0x71, 0x77, 0x12,
|
||||
0x00, 0x99, 0xF7, 0xFB, 0x89, 0xC3, 0x89, 0xF1, 0xE8, 0x69, 0xB3, 0x00, 0x00, 0x48,
|
||||
0x8B, 0x0D, 0x4A, 0x77, 0x12, 0x00, 0x44, 0x88, 0x74, 0x24, 0x40, 0x40, 0x88, 0x7C,
|
||||
0x24, 0x38, 0x44, 0x88, 0x7C, 0x24, 0x30, 0x89, 0x6C, 0x24, 0x28, 0x89, 0x5C, 0x24,
|
||||
0x20, 0x89, 0xF2, 0x41, 0x89, 0xC0, 0x41, 0xB9, 0x04, 0x00, 0x00, 0x00, 0xE8, 0x7B,
|
||||
0x25, 0x01, 0x00, 0x31, 0xF6, 0xE9, 0x1B, 0x23, 0x00, 0x00, 0x8D, 0x46, 0xFF, 0x83,
|
||||
0xF8, 0x50, 0x0F, 0x87, 0x42, 0x0B, 0x00, 0x00, 0x48, 0x8D, 0x0D, 0xB1, 0x36, 0x00,
|
||||
0x00, 0x48, 0x63, 0x04, 0x81, 0x48, 0x01, 0xC8, 0xFF, 0xE0, 0x48, 0x8B, 0x0D, 0x99,
|
||||
0x6B, 0x12, 0x00, 0xBA, 0x89, 0x00, 0x00, 0x00, 0xE8, 0x37, 0xF6, 0x03, 0x00, 0x80,
|
||||
0x3D, 0x90, 0x5C, 0x12, 0x00, 0x00, 0x75, 0x0B, 0xB9, 0x01, 0x00, 0x00, 0x00, 0xFF,
|
||||
0x15, 0xD3, 0x0E, 0x12, 0x00, 0xC6, 0x05, 0x7C, 0x5C, 0x12, 0x00, 0x01, 0x31, 0xF6,
|
||||
0x31, 0xC9, 0xFF, 0x15, 0xE2, 0x0D, 0x12, 0x00, 0xE9, 0xC4, 0x22, 0x00, 0x00, 0x81,
|
||||
0xFE, 0x10, 0x03, 0x00, 0x00, 0x7F, 0x6B, 0x81, 0xFE, 0xE0, 0x02, 0x00, 0x00, 0x0F,
|
||||
0x84, 0xF5, 0x01, 0x00, 0x00, 0x81, 0xFE, 0x07, 0x03, 0x00, 0x00, 0x0F, 0x84, 0xC1,
|
||||
0x01, 0x00, 0x00, 0x81, 0xFE, 0x0F, 0x03, 0x00, 0x00, 0x0F, 0x85, 0xCB, 0x0A, 0x00,
|
||||
0x00, 0x48, 0x83, 0x3D, 0x09, 0x77, 0x12, 0x00, 0x00, 0x0F, 0x84, 0xC7, 0x1C, 0x00,
|
||||
0x00, 0xE8, 0x3E, 0x89,
|
||||
];
|
||||
let result = detect_mime_type(file_data);
|
||||
println!("Test case 3: putty.exe: {:?}", result);
|
||||
assert!(result.is_ok());
|
||||
assert_eq!(
|
||||
result.unwrap(),
|
||||
"application/vnd.microsoft.portable-executable"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ pub async fn serve_file(req: &mut Request, res: &mut Response) {
|
|||
let status_code = StatusCode::NOT_FOUND;
|
||||
render_template(res, &headers, template_filename, template, status_code).await;
|
||||
} else {
|
||||
res.set_status_code(StatusCode::NOT_FOUND);
|
||||
res.status_code(StatusCode::NOT_FOUND);
|
||||
res.render(Text::Json(r#"{"error": "FileNotFound"}"#));
|
||||
}
|
||||
|
||||
|
@ -81,7 +81,7 @@ pub async fn serve_file(req: &mut Request, res: &mut Response) {
|
|||
let status_code = StatusCode::INTERNAL_SERVER_ERROR;
|
||||
render_template(res, &headers, template_filename, template, status_code).await;
|
||||
} else {
|
||||
res.set_status_code(StatusCode::UNAUTHORIZED);
|
||||
res.status_code(StatusCode::UNAUTHORIZED);
|
||||
res.render(Text::Json(r#"{"error": "InternalServerError"}"#));
|
||||
}
|
||||
}
|
||||
|
@ -91,9 +91,7 @@ pub async fn serve_file(req: &mut Request, res: &mut Response) {
|
|||
tracing::info!("New File View: {:?}", &filename.to_string());
|
||||
|
||||
// Read the list of unsafe mimetypes from the config
|
||||
let mime_unsafe = CONFIG
|
||||
.get_array("operations.unsafe_mimetype")
|
||||
.expect("Couldn't find 'unsafe_mimetype' in config. :(");
|
||||
let mime_unsafe = &CONFIG.operations.unsafe_mimetype;
|
||||
|
||||
// Get the mimetype of the file.
|
||||
let mut mimetype: String = db::get_mimetype(sqlconn, &filename).await.unwrap();
|
||||
|
@ -103,7 +101,7 @@ pub async fn serve_file(req: &mut Request, res: &mut Response) {
|
|||
// The predicate in this case is a closure that checks if the current mime type matches the unsafe mime type.
|
||||
if mime_unsafe
|
||||
.iter() // Iterate over the collection
|
||||
.any(|mime| mime.clone().into_string().unwrap() == mimetype.clone())
|
||||
.any(|mime| mime.clone() == mimetype.clone())
|
||||
{
|
||||
// Check if any of the unsafe mime types match the given mimetype
|
||||
tracing::info!("Unsafe Extension Filtered: {:?}", mimetype);
|
||||
|
@ -137,9 +135,7 @@ pub async fn serve_file(req: &mut Request, res: &mut Response) {
|
|||
engine::calculate_expiry(sqlconn, filename.clone(), filesize).await;
|
||||
|
||||
// Check if nginx sendfile is enabled, because we can skip this if it is.
|
||||
let nginxsendfile = CONFIG
|
||||
.get_bool("server.nginx_sendfile")
|
||||
.expect("Couldn't find 'nginx_sendfile' in config. :(");
|
||||
let nginxsendfile = CONFIG.server.nginx_sendfile;
|
||||
|
||||
if nginxsendfile {
|
||||
// Add the X-Accel-Redirect header, allowing for faster file serving.
|
||||
|
@ -154,7 +150,7 @@ pub async fn serve_file(req: &mut Request, res: &mut Response) {
|
|||
tracing::debug!("response headers: {:?}", res.headers());
|
||||
|
||||
// https://github.com/salvo-rs/salvo/issues/233
|
||||
res.set_status_code(StatusCode::OK);
|
||||
res.status_code(StatusCode::OK);
|
||||
} else {
|
||||
// If nginx sendfile is disabled, we need to render the file directly
|
||||
|
||||
|
@ -170,7 +166,7 @@ pub async fn serve_file(req: &mut Request, res: &mut Response) {
|
|||
// Go through all the headers and print them out, just to check for now!
|
||||
tracing::debug!("response headers: {:?}", res.headers());
|
||||
|
||||
res.set_status_code(StatusCode::OK);
|
||||
res.status_code(StatusCode::OK);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -16,7 +16,7 @@ use crate::{db, handlers::TemplateStruct, SQLITE};
|
|||
#[handler]
|
||||
pub async fn serve_static(req: &mut Request, res: &mut Response) {
|
||||
let headers = &req.headers().clone();
|
||||
let remote_addr = &req.remote_addr().unwrap().clone();
|
||||
let remote_addr = &req.remote_addr().clone();
|
||||
let host = headers[HOST].to_str().unwrap_or("localhost:8282");
|
||||
let template_host_path = PathBuf::from("./templates/").join(host);
|
||||
let template_host_path_default = PathBuf::from("./templates/localhost:8282");
|
||||
|
@ -38,7 +38,7 @@ pub async fn serve_static(req: &mut Request, res: &mut Response) {
|
|||
..Default::default()
|
||||
};
|
||||
let rendered = tpls.get("services.html").unwrap().render(&template);
|
||||
res.set_status_code(StatusCode::OK);
|
||||
res.status_code(StatusCode::OK);
|
||||
res.render(Text::Html(rendered));
|
||||
}
|
||||
"/about" => {
|
||||
|
@ -52,7 +52,7 @@ pub async fn serve_static(req: &mut Request, res: &mut Response) {
|
|||
..Default::default()
|
||||
};
|
||||
let rendered = tpls.get("about.html").unwrap().render(&template);
|
||||
res.set_status_code(StatusCode::OK);
|
||||
res.status_code(StatusCode::OK);
|
||||
res.render(Text::Html(rendered));
|
||||
}
|
||||
"/faq" => {
|
||||
|
@ -66,7 +66,7 @@ pub async fn serve_static(req: &mut Request, res: &mut Response) {
|
|||
..Default::default()
|
||||
};
|
||||
let rendered = tpls.get("faq.html").unwrap().render(&template);
|
||||
res.set_status_code(StatusCode::OK);
|
||||
res.status_code(StatusCode::OK);
|
||||
res.render(Text::Html(rendered));
|
||||
}
|
||||
"/dmca" => {
|
||||
|
@ -81,7 +81,7 @@ pub async fn serve_static(req: &mut Request, res: &mut Response) {
|
|||
..Default::default()
|
||||
};
|
||||
let rendered = tpls.get("dmca.html").unwrap().render(&template);
|
||||
res.set_status_code(StatusCode::OK);
|
||||
res.status_code(StatusCode::OK);
|
||||
res.render(Text::Html(rendered));
|
||||
}
|
||||
"/welcome" => {
|
||||
|
@ -96,7 +96,7 @@ pub async fn serve_static(req: &mut Request, res: &mut Response) {
|
|||
..Default::default()
|
||||
};
|
||||
let rendered = tpls.get("welcome.html").unwrap().render(&template);
|
||||
res.set_status_code(StatusCode::OK);
|
||||
res.status_code(StatusCode::OK);
|
||||
res.render(Text::Html(rendered));
|
||||
}
|
||||
"/czb" => {
|
||||
|
@ -111,7 +111,7 @@ pub async fn serve_static(req: &mut Request, res: &mut Response) {
|
|||
..Default::default()
|
||||
};
|
||||
let rendered = tpls.get("czb.html").unwrap().render(&template);
|
||||
res.set_status_code(StatusCode::OK);
|
||||
res.status_code(StatusCode::OK);
|
||||
res.render(Text::Html(rendered));
|
||||
}
|
||||
"/qr" => {
|
||||
|
@ -138,7 +138,7 @@ pub async fn serve_static(req: &mut Request, res: &mut Response) {
|
|||
..Default::default()
|
||||
};
|
||||
let rendered = tpls.get("qr.html").unwrap().render(&template);
|
||||
res.set_status_code(StatusCode::OK);
|
||||
res.status_code(StatusCode::OK);
|
||||
res.render(Text::Html(rendered));
|
||||
}
|
||||
_ => {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use chrono::{TimeZone, Utc};
|
||||
use salvo::{handler, hyper::header::HOST, prelude::StatusCode, writer::Text, Request, Response};
|
||||
use std::{
|
||||
fs,
|
||||
fs::{self, File},
|
||||
io::Read,
|
||||
path::{Path, PathBuf},
|
||||
time::SystemTime,
|
||||
};
|
||||
|
@ -9,8 +10,8 @@ use std::{
|
|||
use super::guess_ip;
|
||||
use crate::{
|
||||
db, engine,
|
||||
handlers::{is_mimetype_banned, render_template, TemplateStruct},
|
||||
CONFIG, SQLITE, MAGIC,
|
||||
handlers::{detect_mime_type, is_mimetype_banned, render_template, TemplateStruct},
|
||||
CONFIG, SQLITE,
|
||||
};
|
||||
|
||||
/// This file handles (heh) uploading files to the server.
|
||||
|
@ -20,7 +21,7 @@ pub async fn upload(req: &mut Request, res: &mut Response) {
|
|||
let sqlconn = SQLITE.get().unwrap();
|
||||
// Clone out the header and remote_addr so we can guess the ip later
|
||||
let headers = &req.headers().clone();
|
||||
let remote_addr = &req.remote_addr().unwrap().clone();
|
||||
let remote_addr = &req.remote_addr().clone();
|
||||
// Get the host header for nicely setting up the response.
|
||||
let host = headers[HOST].to_str().unwrap_or("localhost:8282");
|
||||
tracing::debug!("upload(req): {:?}", req);
|
||||
|
@ -45,16 +46,21 @@ pub async fn upload(req: &mut Request, res: &mut Response) {
|
|||
if let Some(file) = req.file("file").await {
|
||||
// Generate new filename.
|
||||
// Set up the filename length from the config
|
||||
let length = CONFIG
|
||||
.get_int("operations.filename_size")
|
||||
.expect("Couldn't find 'filename_size' in config. :(") as usize;
|
||||
let length = CONFIG.operations.filename_size as usize;
|
||||
let filename =
|
||||
engine::generate_filename(length, file.name().unwrap_or("file").to_string()).await;
|
||||
|
||||
// Guess the mimetype from the file.
|
||||
let file_path = file.path();
|
||||
let mimetype = tree_magic_mini::from_filepath(&file_path)
|
||||
.unwrap_or("text/plain");
|
||||
// Get the temporary file
|
||||
let mut mime_file = File::open(file.path()).expect("Error opening file");
|
||||
let mut buffer = vec![0; 2048];
|
||||
|
||||
// Load up 2048 bytes (There wasn't a decent answer on how much or little I should read)
|
||||
mime_file
|
||||
.read_exact(&mut buffer)
|
||||
.expect("Error reading file");
|
||||
|
||||
// Guess the mimetype
|
||||
let mimetype: &str = &detect_mime_type(&buffer).unwrap_or("text/plain".to_string());
|
||||
|
||||
tracing::debug!("upload(mimetype): {:?}", mimetype);
|
||||
|
||||
|
@ -75,7 +81,7 @@ pub async fn upload(req: &mut Request, res: &mut Response) {
|
|||
render_template(res, headers, template_filename, template, status_code).await;
|
||||
} else {
|
||||
// Otherwise, render the error in json for the API.
|
||||
res.set_status_code(StatusCode::FORBIDDEN);
|
||||
res.status_code(StatusCode::FORBIDDEN);
|
||||
res.render(Text::Json(r#"{"error": "BlockedFiletype"}"#));
|
||||
}
|
||||
} else {
|
||||
|
@ -111,7 +117,7 @@ pub async fn upload(req: &mut Request, res: &mut Response) {
|
|||
let status_code = StatusCode::INTERNAL_SERVER_ERROR;
|
||||
render_template(res, headers, template_filename, template, status_code).await;
|
||||
} else {
|
||||
res.set_status_code(StatusCode::INTERNAL_SERVER_ERROR);
|
||||
res.status_code(StatusCode::INTERNAL_SERVER_ERROR);
|
||||
res.render(Text::Json(r#"{"error": "InternalServerError"}"#));
|
||||
}
|
||||
} else {
|
||||
|
@ -189,7 +195,7 @@ pub async fn upload(req: &mut Request, res: &mut Response) {
|
|||
let status_code = StatusCode::OK;
|
||||
render_template(res, headers, template_filename, template, status_code).await;
|
||||
} else {
|
||||
res.set_status_code(StatusCode::OK);
|
||||
res.status_code(StatusCode::OK);
|
||||
|
||||
res.render(Text::Json(format!(
|
||||
r#"{{"file": "{}", "url": "{}", "adminurl": "{}", "expiry": {}}}"#,
|
||||
|
@ -214,7 +220,7 @@ pub async fn upload(req: &mut Request, res: &mut Response) {
|
|||
let status_code = StatusCode::BAD_REQUEST;
|
||||
render_template(res, headers, template_filename, template, status_code).await;
|
||||
} else {
|
||||
res.set_status_code(StatusCode::BAD_REQUEST);
|
||||
res.status_code(StatusCode::BAD_REQUEST);
|
||||
res.render(Text::Json(r#"{"error": "BadRequest"}"#));
|
||||
};
|
||||
}
|
||||
|
|
133
src/main.rs
133
src/main.rs
|
@ -1,20 +1,23 @@
|
|||
use figment::providers::{Format, Toml};
|
||||
use figment::Figment;
|
||||
use magic::Cookie;
|
||||
use once_cell::sync::OnceCell;
|
||||
use salvo::prelude::*;
|
||||
use salvo::serve_static::{StaticDir, StaticFile};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
use config::Config;
|
||||
use lazy_static::lazy_static;
|
||||
use std::fs::create_dir_all;
|
||||
use std::time::Duration;
|
||||
use tokio::{task, time};
|
||||
use tracing_subscriber::filter::EnvFilter;
|
||||
use tracing_subscriber::fmt;
|
||||
use tracing_subscriber::prelude::*;
|
||||
|
||||
use crate::config::AppConfig;
|
||||
use crate::db::DatabaseType;
|
||||
use crate::handlers::cleaner_thread;
|
||||
|
||||
// Import sub-modules.
|
||||
mod config;
|
||||
mod db;
|
||||
mod engine;
|
||||
|
||||
|
@ -30,21 +33,40 @@ mod handlers;
|
|||
// Setup the global sqlite db
|
||||
static SQLITE: OnceCell<SqlitePool> = OnceCell::new();
|
||||
|
||||
// Initialise the magic db
|
||||
// This is thread-local, so each thread will have it's own database loaded.
|
||||
// It's probably not the best way of doing it
|
||||
// and we could probably have a dedicated thread for mimetypes but idk i'm not that smart
|
||||
thread_local! {
|
||||
static COOKIE: Cookie = {
|
||||
let cookie = Cookie::open(magic::CookieFlags::MIME_TYPE).unwrap();
|
||||
// Load the default database
|
||||
cookie.load::<&str>(&[]).unwrap();
|
||||
cookie
|
||||
};
|
||||
}
|
||||
|
||||
// Setup the config globally because I can't figure out how to pass it to functions I don't call directly.
|
||||
// This is evaluated at runtime, and not compilation. \o/
|
||||
lazy_static! {
|
||||
pub static ref CONFIG: Config = Config::builder()
|
||||
.add_source(config::File::with_name("config.toml"))
|
||||
.build()
|
||||
.unwrap();
|
||||
pub static ref CONFIG: AppConfig = {
|
||||
let config = Figment::new()
|
||||
.merge(Toml::file("config.toml"))
|
||||
.extract()
|
||||
.unwrap_or_else(|error| {
|
||||
eprintln!("Error loading configuration: {}", error);
|
||||
std::process::exit(1);
|
||||
});
|
||||
|
||||
tracing::debug!("full config: {:?}", config);
|
||||
config
|
||||
};
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
// Read log level from config
|
||||
let log_config = CONFIG
|
||||
.get_string("logging.level")
|
||||
.expect("Couldn't find 'sql_backend' in config. :(");
|
||||
let log_config = &CONFIG.logging.level;
|
||||
|
||||
// Construct tracing_subscriber with the filter from config.
|
||||
tracing_subscriber::registry()
|
||||
|
@ -53,13 +75,9 @@ async fn main() {
|
|||
.init();
|
||||
|
||||
// Determine what SQL engine we're running.
|
||||
let sql_backend = CONFIG
|
||||
.get_string("database.sql_backend")
|
||||
.expect("Couldn't find 'sql_backend' in config. :(");
|
||||
let sql_backend = &CONFIG.database.sql_backend;
|
||||
|
||||
let db_url = CONFIG
|
||||
.get_string("database.url")
|
||||
.expect("Couldn't find 'url' in config. :(");
|
||||
let db_url = &CONFIG.database.url;
|
||||
|
||||
// Match on the database type
|
||||
match DatabaseType::from_str(&sql_backend) {
|
||||
|
@ -94,15 +112,11 @@ async fn main() {
|
|||
};
|
||||
|
||||
// Initialise the cleaner task
|
||||
let interval = CONFIG
|
||||
.get_int("operations.cleaner_interval")
|
||||
.expect("Couldn't find 'cleaner_interval' in config. :(");
|
||||
tracing::debug!("cleaner_interval: {}", interval);
|
||||
cleaner_thread(
|
||||
interval
|
||||
.try_into()
|
||||
.expect("Cleaner interval was too long to fit in a i32.... wow"),
|
||||
tracing::info!(
|
||||
"Starting cleaner thread every {} seconds",
|
||||
CONFIG.operations.cleaner_interval
|
||||
);
|
||||
cleaner_thread(CONFIG.operations.cleaner_interval);
|
||||
|
||||
// Attempt to create the files directory
|
||||
create_dir_all("files").unwrap();
|
||||
|
@ -110,7 +124,6 @@ async fn main() {
|
|||
// Internal routing
|
||||
let router = Router::new()
|
||||
// Main / functions
|
||||
.hoop(Logger)
|
||||
.get(handlers::index::index)
|
||||
.post(handlers::upload::upload)
|
||||
// Static Pages
|
||||
|
@ -123,10 +136,9 @@ async fn main() {
|
|||
.push(Router::with_path("/welcome").get(handlers::serve_static::serve_static))
|
||||
.push(Router::with_path("/metrics").get(handlers::serve_metrics::serve_metrics))
|
||||
.push(Router::with_path("/favicon.ico").get(StaticFile::new("static/favicon32.webp")))
|
||||
.push(Router::with_path("/robots.txt").get(StaticFile::new("static/robots.txt")))
|
||||
// Static File Serving
|
||||
.push(
|
||||
Router::with_path("static/<**path>").get(StaticDir::new("static/").with_listing(true)),
|
||||
)
|
||||
.push(Router::with_path("static/<**path>").get(StaticDir::new("static/").listing(true)))
|
||||
// Deletion API
|
||||
.push(
|
||||
Router::with_path("/delete/<adminkey>")
|
||||
|
@ -144,62 +156,21 @@ async fn main() {
|
|||
.push(Router::with_path("<file>").get(handlers::serve_file::serve_file));
|
||||
|
||||
// Read environment variables for host and port
|
||||
let host = CONFIG
|
||||
.get_string("server.host")
|
||||
.expect("Couldn't find 'host' in config. :(");
|
||||
let port = CONFIG
|
||||
.get_int("server.port")
|
||||
.expect("Couldn't find 'port' in config. :(");
|
||||
let host = &CONFIG.server.host;
|
||||
let port = CONFIG.server.port;
|
||||
let server_url = format!("{}:{}", host, port);
|
||||
tracing::info!("Listening on http://{}", server_url);
|
||||
Server::new(TcpListener::bind(&server_url))
|
||||
.serve(router)
|
||||
.await;
|
||||
// TODO: Allow us to listen on a socket too?
|
||||
// let acceptor = UnixListener::new("/tmp/salvo.sock").bind().await;
|
||||
let acceptor = TcpListener::new(&server_url).bind().await;
|
||||
|
||||
Server::new(acceptor).serve(router).await;
|
||||
|
||||
// Close SQLite before closing
|
||||
tracing::info!("Attempting to safely shut down Ephemeral.");
|
||||
SQLITE.get().expect("Problem while safely closing the database :(").close().await;
|
||||
}
|
||||
|
||||
// This spawns a tokio task to run a interval timer forever.
|
||||
// the interval timer runs every 'period' seconds.
|
||||
pub fn cleaner_thread(period: i32) {
|
||||
let _forever = task::spawn(async move {
|
||||
let mut interval = time::interval(Duration::from_secs(period.try_into().unwrap()));
|
||||
let sqlconn = SQLITE.get().unwrap();
|
||||
loop {
|
||||
// Wait for the next interval
|
||||
interval.tick().await;
|
||||
// Get a vec of files that will expire this loop.
|
||||
// Also I'm sorta just realising how annoying it is to pass sqlconn through like 4 functions deep just to do something.
|
||||
let old_files_result = db::get_old_files(sqlconn).await;
|
||||
|
||||
match old_files_result {
|
||||
Ok(file_list) => {
|
||||
tracing::info!("Running Cleaner");
|
||||
for file in file_list {
|
||||
// Delete the file from the database
|
||||
db::delete_file(sqlconn, &file).await.unwrap_or_else(|err| {
|
||||
tracing::error!(
|
||||
"Failed to delete file from database: {}, error: {:?}",
|
||||
&file,
|
||||
err
|
||||
);
|
||||
0
|
||||
});
|
||||
// Delete the file from the filesystem
|
||||
engine::delete_file(&file).await.unwrap_or_else(|err| {
|
||||
tracing::error!("Failed to delete file from database: {:?}", err);
|
||||
});
|
||||
}
|
||||
tracing::info!("Cleaner finished");
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!("Error getting files to expire: {}", err);
|
||||
// Return a empty Vec so it doesn't delete anything
|
||||
return Vec::<String>::new();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
SQLITE
|
||||
.get()
|
||||
.expect("Problem while safely closing the database :(")
|
||||
.close()
|
||||
.await;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue