mirror of
https://github.com/timvisee/ffsend.git
synced 2025-10-03 17:49:15 +02:00
Check and limit file upload sizes
This commit is contained in:
parent
1099a80c79
commit
5dbafa6dbd
5 changed files with 84 additions and 9 deletions
5
IDEAS.md
5
IDEAS.md
|
@ -1,7 +1,6 @@
|
||||||
# First release
|
# First release
|
||||||
- Only allow file extension renaming on upload with `-f` flag
|
|
||||||
- Check for file expiry everywhere
|
- Check for file expiry everywhere
|
||||||
- Soft limit uploads to 1GB and 2GB
|
- On download, mention a wrong or missing password with a HTTP 401 response
|
||||||
- Remember all uploaded files, make files listable
|
- Remember all uploaded files, make files listable
|
||||||
- Incognito mode, to not remember files `--incognito`
|
- Incognito mode, to not remember files `--incognito`
|
||||||
- Automatically get owner token, from file history when setting password
|
- Automatically get owner token, from file history when setting password
|
||||||
|
@ -12,7 +11,7 @@
|
||||||
|
|
||||||
# Other ideas
|
# Other ideas
|
||||||
- Box errors
|
- Box errors
|
||||||
- On download, mention a wrong or missing password with a HTTP 401 response
|
- Only allow file extension renaming on upload with `-f` flag
|
||||||
- Implement error handling everywhere properly
|
- Implement error handling everywhere properly
|
||||||
- Quick upload/download without `upload` or `download` subcommands?
|
- Quick upload/download without `upload` or `download` subcommands?
|
||||||
- Flag to explicitly delete file after download
|
- Flag to explicitly delete file after download
|
||||||
|
|
5
api/src/config.rs
Normal file
5
api/src/config.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
/// The recommended maximum upload size in bytes.
|
||||||
|
pub const UPLOAD_SIZE_MAX_RECOMMENDED: u64 = 1_073_741_824;
|
||||||
|
|
||||||
|
/// The maximum upload size in bytes.
|
||||||
|
pub const UPLOAD_SIZE_MAX: u64 = 2_147_483_648;
|
|
@ -16,6 +16,7 @@ pub extern crate url;
|
||||||
|
|
||||||
pub mod action;
|
pub mod action;
|
||||||
mod api;
|
mod api;
|
||||||
|
pub mod config;
|
||||||
pub mod crypto;
|
pub mod crypto;
|
||||||
mod ext;
|
mod ext;
|
||||||
pub mod file;
|
pub mod file;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::fs::File;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
@ -5,17 +6,24 @@ use clap::ArgMatches;
|
||||||
use failure::{err_msg, Fail};
|
use failure::{err_msg, Fail};
|
||||||
use ffsend_api::action::params::ParamsDataBuilder;
|
use ffsend_api::action::params::ParamsDataBuilder;
|
||||||
use ffsend_api::action::upload::Upload as ApiUpload;
|
use ffsend_api::action::upload::Upload as ApiUpload;
|
||||||
|
use ffsend_api::config::{UPLOAD_SIZE_MAX, UPLOAD_SIZE_MAX_RECOMMENDED};
|
||||||
use ffsend_api::reqwest::Client;
|
use ffsend_api::reqwest::Client;
|
||||||
|
|
||||||
use cmd::matcher::{
|
use cmd::matcher::{Matcher, MainMatcher, UploadMatcher};
|
||||||
Matcher,
|
|
||||||
upload::UploadMatcher,
|
|
||||||
};
|
|
||||||
use error::ActionError;
|
use error::ActionError;
|
||||||
use progress::ProgressBar;
|
use progress::ProgressBar;
|
||||||
use util::open_url;
|
use util::{
|
||||||
|
ErrorHintsBuilder,
|
||||||
|
format_bytes,
|
||||||
|
open_url,
|
||||||
|
print_error,
|
||||||
|
print_error_msg,
|
||||||
|
prompt_yes,
|
||||||
|
quit,
|
||||||
|
quit_error_msg,
|
||||||
|
};
|
||||||
#[cfg(feature = "clipboard")]
|
#[cfg(feature = "clipboard")]
|
||||||
use util::{print_error, set_clipboard};
|
use util::set_clipboard;
|
||||||
|
|
||||||
/// A file upload action.
|
/// A file upload action.
|
||||||
pub struct Upload<'a> {
|
pub struct Upload<'a> {
|
||||||
|
@ -34,12 +42,52 @@ impl<'a> Upload<'a> {
|
||||||
// TODO: create a trait for this method
|
// TODO: create a trait for this method
|
||||||
pub fn invoke(&self) -> Result<(), ActionError> {
|
pub fn invoke(&self) -> Result<(), ActionError> {
|
||||||
// Create the command matchers
|
// Create the command matchers
|
||||||
|
let matcher_main = MainMatcher::with(self.cmd_matches).unwrap();
|
||||||
let matcher_upload = UploadMatcher::with(self.cmd_matches).unwrap();
|
let matcher_upload = UploadMatcher::with(self.cmd_matches).unwrap();
|
||||||
|
|
||||||
// Get API parameters
|
// Get API parameters
|
||||||
let path = Path::new(matcher_upload.file()).to_path_buf();
|
let path = Path::new(matcher_upload.file()).to_path_buf();
|
||||||
let host = matcher_upload.host();
|
let host = matcher_upload.host();
|
||||||
|
|
||||||
|
// TODO: ensure the file exists and is accessible
|
||||||
|
|
||||||
|
// Get the file size to warn about large files
|
||||||
|
if let Ok(size) = File::open(&path)
|
||||||
|
.and_then(|f| f.metadata())
|
||||||
|
.map(|m| m.len())
|
||||||
|
{
|
||||||
|
if size > UPLOAD_SIZE_MAX && !matcher_main.force() {
|
||||||
|
// The file is too large, show an error and quit
|
||||||
|
quit_error_msg(
|
||||||
|
format!(
|
||||||
|
"The file size is {}, bigger than the maximum allowed of {}",
|
||||||
|
format_bytes(size),
|
||||||
|
format_bytes(UPLOAD_SIZE_MAX),
|
||||||
|
),
|
||||||
|
ErrorHintsBuilder::default()
|
||||||
|
.force(true)
|
||||||
|
.verbose(false)
|
||||||
|
.build()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
} else if size > UPLOAD_SIZE_MAX_RECOMMENDED && !matcher_main.force() {
|
||||||
|
// The file is larger than the recommended maximum, warn
|
||||||
|
eprintln!(
|
||||||
|
"The file size is {}, bigger than the recommended maximum of {}",
|
||||||
|
format_bytes(size),
|
||||||
|
format_bytes(UPLOAD_SIZE_MAX_RECOMMENDED),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Prompt the user to continue, quit if the user answered no
|
||||||
|
if !prompt_yes("Continue uploading?", Some(true), &matcher_main) {
|
||||||
|
println!("Upload cancelled");
|
||||||
|
quit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print_error_msg("Failed to check the file size, ignoring");
|
||||||
|
}
|
||||||
|
|
||||||
// Create a reqwest client
|
// Create a reqwest client
|
||||||
let client = Client::new();
|
let client = Client::new();
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,15 @@ pub fn print_error<E: Fail>(err: E) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Print the given error message in a proper format for the user,
|
||||||
|
/// with it's causes.
|
||||||
|
pub fn print_error_msg<S>(err: S)
|
||||||
|
where
|
||||||
|
S: AsRef<str> + Display + Debug + Sync + Send + 'static
|
||||||
|
{
|
||||||
|
print_error(err_msg(err).compat());
|
||||||
|
}
|
||||||
|
|
||||||
/// Quit the application regularly.
|
/// Quit the application regularly.
|
||||||
pub fn quit() -> ! {
|
pub fn quit() -> ! {
|
||||||
exit(0);
|
exit(0);
|
||||||
|
@ -393,3 +402,16 @@ pub fn ensure_owner_token(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Format the given number of bytes readable for humans.
|
||||||
|
pub fn format_bytes(bytes: u64) -> String {
|
||||||
|
let bytes = bytes as f64;
|
||||||
|
let kb = 1024f64;
|
||||||
|
match bytes {
|
||||||
|
bytes if bytes >= kb.powf(4_f64) => format!("{:.*} TiB", 2, bytes / kb.powf(4_f64)),
|
||||||
|
bytes if bytes >= kb.powf(3_f64) => format!("{:.*} GiB", 2, bytes / kb.powf(3_f64)),
|
||||||
|
bytes if bytes >= kb.powf(2_f64) => format!("{:.*} MiB", 2, bytes / kb.powf(2_f64)),
|
||||||
|
bytes if bytes >= kb => format!("{:.*} KiB", 2, bytes / kb),
|
||||||
|
_ => format!("{:.*} B", 0, bytes),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue