Show metadata properties with info CLI command, check if file exists

This commit is contained in:
timvisee 2018-04-02 21:52:17 +02:00
parent 349e62ed1c
commit a7827197a7
No known key found for this signature in database
GPG key ID: 109CBA0BF74036C2
7 changed files with 142 additions and 10 deletions

View file

@ -1,7 +1,5 @@
# Ideas # Ideas
- Endpoints: - Endpoints:
- exists
- metadata
- delete - delete
- allow creating non existent directories with the `-f` flag - allow creating non existent directories with the `-f` flag
- only allow file extension renaming on upload with `-f` flag - only allow file extension renaming on upload with `-f` flag

View file

@ -18,6 +18,10 @@ use crypto::sig::signature_encoded;
use ext::status_code::StatusCodeExt; use ext::status_code::StatusCodeExt;
use file::remote_file::RemoteFile; use file::remote_file::RemoteFile;
use reader::{EncryptedFileWriter, ProgressReporter, ProgressWriter}; use reader::{EncryptedFileWriter, ProgressReporter, ProgressWriter};
use super::exists::{
Error as ExistsError,
Exists as ExistsAction,
};
use super::metadata::{ use super::metadata::{
Error as MetadataError, Error as MetadataError,
Metadata as MetadataAction, Metadata as MetadataAction,
@ -55,6 +59,27 @@ impl<'a> Download<'a> {
client: &Client, client: &Client,
reporter: Arc<Mutex<ProgressReporter>>, reporter: Arc<Mutex<ProgressReporter>>,
) -> Result<(), Error> { ) -> Result<(), Error> {
// Make sure the given file exists
let exist_response = ExistsAction::new(&self.file)
.invoke(&client)?;
// Return an error if the file does not exist
if !exist_response.exists() {
return Err(Error::Expired);
}
// Make sure a password is given when it is required
let has_password = self.password.is_some();
if has_password != exist_response.has_password() {
if has_password {
// TODO: show a proper message here
println!("file not password protected, ignoring password");
} else {
// TODO: show a propper error here, or prompt for the password
panic!("password required");
}
}
// Create a key set for the file // Create a key set for the file
let mut key = KeySet::from(self.file, self.password.as_ref()); let mut key = KeySet::from(self.file, self.password.as_ref());
@ -232,6 +257,11 @@ impl<'a> Download<'a> {
#[derive(Fail, Debug)] #[derive(Fail, Debug)]
pub enum Error { pub enum Error {
/// An error occurred while checking whether the file exists on the
/// server.
#[fail(display = "Failed to check whether the file exists")]
Exists(#[cause] ExistsError),
/// An error occurred while fetching the metadata of the file. /// An error occurred while fetching the metadata of the file.
/// This step is required in order to succsessfully decrypt the /// This step is required in order to succsessfully decrypt the
/// file that will be downloaded. /// file that will be downloaded.
@ -257,6 +287,12 @@ pub enum Error {
File(String, #[cause] FileError), File(String, #[cause] FileError),
} }
impl From<ExistsError> for Error {
fn from(err: ExistsError) -> Error {
Error::Exists(err)
}
}
impl From<MetadataError> for Error { impl From<MetadataError> for Error {
fn from(err: MetadataError) -> Error { fn from(err: MetadataError) -> Error {
Error::Meta(err) Error::Meta(err)

View file

@ -49,8 +49,9 @@ impl<'a> Exists<'a> {
} }
// Parse the response // Parse the response
let response = response.json::<ExistsResponse>() let mut response = response.json::<ExistsResponse>()
.map_err(|_| Error::Malformed)?; .map_err(|_| Error::Malformed)?;
response.set_exists(true);
// TODO: fetch the metadata nonce from the response headers // TODO: fetch the metadata nonce from the response headers
@ -84,6 +85,11 @@ impl ExistsResponse {
self.exists self.exists
} }
/// Set whether the remote file exists.
pub fn set_exists(&mut self, exists: bool) {
self.exists = exists;
}
/// Whether the remote file is protected by a password. /// Whether the remote file is protected by a password.
pub fn has_password(&self) -> bool { pub fn has_password(&self) -> bool {
self.has_password self.has_password

View file

@ -52,6 +52,11 @@ impl Metadata {
&self.name &self.name
} }
/// Get the file MIME type.
pub fn mime(&self) -> &str {
&self.mime
}
/// Get the input vector /// Get the input vector
// TODO: use an input vector length from a constant // TODO: use an input vector length from a constant
pub fn iv(&self) -> [u8; 12] { pub fn iv(&self) -> [u8; 12] {

View file

@ -133,6 +133,11 @@ impl RemoteFile {
)) ))
} }
/// Get the file ID.
pub fn id(&self) -> &str {
&self.id
}
/// Get the raw secret. /// Get the raw secret.
pub fn secret_raw(&self) -> &Vec<u8> { pub fn secret_raw(&self) -> &Vec<u8> {
// A secret must have been set // A secret must have been set

View file

@ -1,9 +1,21 @@
use ffsend_api::action::info::Info as ApiInfo; use failure::Fail;
use ffsend_api::file::remote_file::RemoteFile; use ffsend_api::action::exists::{
Error as ExistsError,
Exists as ApiExists,
};
use ffsend_api::action::info::{
Error as InfoError,
Info as ApiInfo,
};
use ffsend_api::action::metadata::Metadata as ApiMetadata;
use ffsend_api::file::remote_file::{
FileParseError,
RemoteFile,
};
use ffsend_api::reqwest::Client; use ffsend_api::reqwest::Client;
use cmd::cmd_info::CmdInfo; use cmd::cmd_info::CmdInfo;
use error::ActionError; use util::print_error;
/// A file info action. /// A file info action.
pub struct Info<'a> { pub struct Info<'a> {
@ -20,7 +32,7 @@ impl<'a> Info<'a> {
/// Invoke the info action. /// Invoke the info action.
// 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<(), Error> {
// Get the share URL // Get the share URL
let url = self.cmd.url(); let url = self.cmd.url();
@ -33,13 +45,76 @@ impl<'a> Info<'a> {
// TODO: show an informative error if the owner token isn't set // TODO: show an informative error if the owner token isn't set
// Execute the info fetch action // Make sure the file exists
let exists_response = ApiExists::new(&file)
.invoke(&client)?;
// Make sure the file exists
if !exists_response.exists() {
return Err(Error::Expired);
}
// TODO: make sure a password is set if required
// Fetch both file info and metadata
let info = ApiInfo::new(&file, None).invoke(&client)?; let info = ApiInfo::new(&file, None).invoke(&client)?;
// TODO: supply a password here
let metadata = ApiMetadata::new(&file, None).invoke(&client)
.map_err(|err| print_error(err.context(
"Failed to fetch file metadata, showing limited info",
)))
.ok();
// Print the result // Print the result
println!("ID: {}", file.id());
if let Some(metadata) = metadata {
println!("File name: {}", metadata.metadata().name());
println!("MIME type: {}", metadata.metadata().mime());
}
println!("Downloads: {} of {}", info.download_count(), info.download_limit()); println!("Downloads: {} of {}", info.download_count(), info.download_limit());
println!("TTL: {}", info.ttl_millis()); println!("TTL: {} ms", info.ttl_millis());
// TODO: show the file size, fetch TTL from metadata?
Ok(()) Ok(())
} }
} }
#[derive(Debug, Fail)]
pub enum Error {
/// Failed to parse a share URL, it was invalid.
/// This error is not related to a specific action.
#[fail(display = "Invalid share URL")]
InvalidUrl(#[cause] FileParseError),
/// An error occurred while checking if the file exists.
#[fail(display = "Failed to check whether the file exists")]
Exists(#[cause] ExistsError),
/// An error occurred while fetching the file information.
#[fail(display = "Failed to fetch file info")]
Info(#[cause] InfoError),
/// The given Send file has expired, or did never exist in the first place.
// TODO: do not return an error, but write to stdout that the file does not exist
#[fail(display = "The file has expired or did never exist")]
Expired,
}
impl From<FileParseError> for Error {
fn from(err: FileParseError) -> Error {
Error::InvalidUrl(err)
}
}
impl From<ExistsError> for Error {
fn from(err: ExistsError) -> Error {
Error::Exists(err)
}
}
impl From<InfoError> for Error {
fn from(err: InfoError) -> Error {
Error::Info(err)
}
}

View file

@ -1,10 +1,11 @@
use ffsend_api::action::download::Error as DownloadError; use ffsend_api::action::download::Error as DownloadError;
use ffsend_api::action::info::Error as InfoError;
use ffsend_api::action::params::Error as ParamsError; use ffsend_api::action::params::Error as ParamsError;
use ffsend_api::action::password::Error as PasswordError; use ffsend_api::action::password::Error as PasswordError;
use ffsend_api::action::upload::Error as UploadError; use ffsend_api::action::upload::Error as UploadError;
use ffsend_api::file::remote_file::FileParseError; use ffsend_api::file::remote_file::FileParseError;
use action::info::Error as InfoError;
#[derive(Fail, Debug)] #[derive(Fail, Debug)]
pub enum Error { pub enum Error {
/// An error occurred while invoking an action. /// An error occurred while invoking an action.
@ -12,6 +13,12 @@ pub enum Error {
Action(#[cause] ActionError), Action(#[cause] ActionError),
} }
impl From<InfoError> for Error {
fn from(err: InfoError) -> Error {
Error::Action(ActionError::Info(err))
}
}
impl From<ActionError> for Error { impl From<ActionError> for Error {
fn from(err: ActionError) -> Error { fn from(err: ActionError) -> Error {
Error::Action(err) Error::Action(err)