Check and limit file upload sizes

This commit is contained in:
timvisee 2018-04-12 23:32:35 +02:00
parent 1099a80c79
commit 5dbafa6dbd
No known key found for this signature in database
GPG key ID: 109CBA0BF74036C2
5 changed files with 84 additions and 9 deletions

View file

@ -1,7 +1,6 @@
# First release
- Only allow file extension renaming on upload with `-f` flag
- 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
- Incognito mode, to not remember files `--incognito`
- Automatically get owner token, from file history when setting password
@ -12,7 +11,7 @@
# Other ideas
- 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
- Quick upload/download without `upload` or `download` subcommands?
- Flag to explicitly delete file after download

5
api/src/config.rs Normal file
View 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;

View file

@ -16,6 +16,7 @@ pub extern crate url;
pub mod action;
mod api;
pub mod config;
pub mod crypto;
mod ext;
pub mod file;

View file

@ -1,3 +1,4 @@
use std::fs::File;
use std::path::Path;
use std::sync::{Arc, Mutex};
@ -5,17 +6,24 @@ use clap::ArgMatches;
use failure::{err_msg, Fail};
use ffsend_api::action::params::ParamsDataBuilder;
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 cmd::matcher::{
Matcher,
upload::UploadMatcher,
};
use cmd::matcher::{Matcher, MainMatcher, UploadMatcher};
use error::ActionError;
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")]
use util::{print_error, set_clipboard};
use util::set_clipboard;
/// A file upload action.
pub struct Upload<'a> {
@ -34,12 +42,52 @@ impl<'a> Upload<'a> {
// TODO: create a trait for this method
pub fn invoke(&self) -> Result<(), ActionError> {
// Create the command matchers
let matcher_main = MainMatcher::with(self.cmd_matches).unwrap();
let matcher_upload = UploadMatcher::with(self.cmd_matches).unwrap();
// Get API parameters
let path = Path::new(matcher_upload.file()).to_path_buf();
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
let client = Client::new();

View file

@ -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.
pub fn quit() -> ! {
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),
}
}