Add password support to download action
This commit is contained in:
parent
1c17ef0fcd
commit
376743915d
8 changed files with 51 additions and 7 deletions
|
@ -33,13 +33,17 @@ const FILE_EXPIRED_STATUS: StatusCode = StatusCode::NotFound;
|
|||
pub struct Download<'a> {
|
||||
/// The Send file to download.
|
||||
file: &'a DownloadFile,
|
||||
|
||||
/// An optional password to decrypt a protected file.
|
||||
password: Option<String>,
|
||||
}
|
||||
|
||||
impl<'a> Download<'a> {
|
||||
/// Construct a new download action for the given file.
|
||||
pub fn new(file: &'a DownloadFile) -> Self {
|
||||
pub fn new(file: &'a DownloadFile, password: Option<String>) -> Self {
|
||||
Self {
|
||||
file,
|
||||
password,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,7 +54,7 @@ impl<'a> Download<'a> {
|
|||
reporter: Arc<Mutex<ProgressReporter>>,
|
||||
) -> Result<(), Error> {
|
||||
// Create a key set for the file
|
||||
let mut key = KeySet::from(self.file);
|
||||
let mut key = KeySet::from(self.file, self.password.as_ref());
|
||||
|
||||
// Fetch the authentication nonce
|
||||
let auth_nonce = self.fetch_auth_nonce(client)?;
|
||||
|
|
|
@ -42,7 +42,7 @@ impl<'a> Password<'a> {
|
|||
/// Invoke the password action.
|
||||
pub fn invoke(mut self, client: &Client) -> Result<(), Error> {
|
||||
// Create a key set for the file
|
||||
let mut key = KeySet::from(self.file);
|
||||
let mut key = KeySet::from(self.file, None);
|
||||
|
||||
// Fetch the authentication nonce if not set yet
|
||||
if self.nonce.is_empty() {
|
||||
|
|
|
@ -43,7 +43,7 @@ impl KeySet {
|
|||
// TODO: add a parameter for the password and URL
|
||||
// TODO: return a result?
|
||||
// TODO: supply a client instance as parameter
|
||||
pub fn from(file: &DownloadFile) -> Self {
|
||||
pub fn from(file: &DownloadFile, password: Option<&String>) -> Self {
|
||||
// Create a new key set instance
|
||||
let mut set = Self::new(
|
||||
file.secret_raw().clone(),
|
||||
|
@ -53,6 +53,11 @@ impl KeySet {
|
|||
// Derive all keys
|
||||
set.derive();
|
||||
|
||||
// Derive a pasworded key
|
||||
if let Some(password) = password {
|
||||
set.derive_auth_password(password, &file.download_url(true));
|
||||
}
|
||||
|
||||
set
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ pub trait StatusCodeExt {
|
|||
impl StatusCodeExt for StatusCode {
|
||||
fn err_text(&self) -> String {
|
||||
self.canonical_reason()
|
||||
.map(|text| text.to_owned())
|
||||
.map(|text| format!("{} {}", self.as_u16(), text))
|
||||
.unwrap_or(format!("{}", self.as_u16()))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,8 @@ impl<'a> Download<'a> {
|
|||
let bar = Arc::new(Mutex::new(ProgressBar::new_download()));
|
||||
|
||||
// Execute an download action
|
||||
ApiDownload::new(&file).invoke(&client, bar)?;
|
||||
ApiDownload::new(&file, self.cmd.password())
|
||||
.invoke(&client, bar)?;
|
||||
|
||||
// TODO: open the file, or it's location
|
||||
// TODO: copy the file location
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use ffsend_api::url::{ParseError, Url};
|
||||
|
||||
use super::clap::{App, Arg, ArgMatches, SubCommand};
|
||||
use rpassword::prompt_password_stderr;
|
||||
|
||||
use util::quit_error_msg;
|
||||
|
||||
|
@ -21,7 +22,15 @@ impl<'a: 'b, 'b> CmdDownload<'a> {
|
|||
.arg(Arg::with_name("URL")
|
||||
.help("The share URL")
|
||||
.required(true)
|
||||
.multiple(false));
|
||||
.multiple(false))
|
||||
.arg(Arg::with_name("password")
|
||||
.long("password")
|
||||
.short("p")
|
||||
.alias("pass")
|
||||
.value_name("PASSWORD")
|
||||
.min_values(0)
|
||||
.max_values(1)
|
||||
.help("Unlock a password protected file"));
|
||||
|
||||
cmd
|
||||
}
|
||||
|
@ -61,4 +70,26 @@ impl<'a: 'b, 'b> CmdDownload<'a> {
|
|||
_ => quit_error_msg("The given host is invalid"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the password.
|
||||
/// `None` is returned if no password was specified.
|
||||
pub fn password(&'a self) -> Option<String> {
|
||||
// Return none if the property was not set
|
||||
if !self.matches.is_present("password") {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Get the password from the arguments
|
||||
if let Some(password) = self.matches.value_of("password") {
|
||||
return Some(password.into());
|
||||
}
|
||||
|
||||
// Prompt for the password
|
||||
// TODO: don't unwrap/expect
|
||||
// TODO: create utility function for this
|
||||
Some(
|
||||
prompt_password_stderr("Password: ")
|
||||
.expect("failed to read password from stdin")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,6 +93,7 @@ impl<'a: 'b, 'b> CmdPassword<'a> {
|
|||
|
||||
// Prompt for the password
|
||||
// TODO: don't unwrap/expect
|
||||
// TODO: create utility function for this
|
||||
prompt_password_stderr("New password: ")
|
||||
.expect("failed to read password from stdin")
|
||||
}
|
||||
|
|
|
@ -108,6 +108,7 @@ impl<'a: 'b, 'b> CmdUpload<'a> {
|
|||
}
|
||||
|
||||
/// Get the password.
|
||||
/// `None` is returned if no password was specified.
|
||||
pub fn password(&'a self) -> Option<String> {
|
||||
// Return none if the property was not set
|
||||
if !self.matches.is_present("password") {
|
||||
|
@ -121,6 +122,7 @@ impl<'a: 'b, 'b> CmdUpload<'a> {
|
|||
|
||||
// Prompt for the password
|
||||
// TODO: don't unwrap/expect
|
||||
// TODO: create utility function for this
|
||||
Some(
|
||||
prompt_password_stderr("Password: ")
|
||||
.expect("failed to read password from stdin")
|
||||
|
|
Loading…
Reference in a new issue