mirror of
https://github.com/timvisee/ffsend.git
synced 2025-10-03 17:49:15 +02:00
Show metadata properties with info CLI command, check if file exists
This commit is contained in:
parent
349e62ed1c
commit
a7827197a7
7 changed files with 142 additions and 10 deletions
2
IDEAS.md
2
IDEAS.md
|
@ -1,7 +1,5 @@
|
|||
# Ideas
|
||||
- Endpoints:
|
||||
- exists
|
||||
- metadata
|
||||
- delete
|
||||
- allow creating non existent directories with the `-f` flag
|
||||
- only allow file extension renaming on upload with `-f` flag
|
||||
|
|
|
@ -18,6 +18,10 @@ use crypto::sig::signature_encoded;
|
|||
use ext::status_code::StatusCodeExt;
|
||||
use file::remote_file::RemoteFile;
|
||||
use reader::{EncryptedFileWriter, ProgressReporter, ProgressWriter};
|
||||
use super::exists::{
|
||||
Error as ExistsError,
|
||||
Exists as ExistsAction,
|
||||
};
|
||||
use super::metadata::{
|
||||
Error as MetadataError,
|
||||
Metadata as MetadataAction,
|
||||
|
@ -55,6 +59,27 @@ impl<'a> Download<'a> {
|
|||
client: &Client,
|
||||
reporter: Arc<Mutex<ProgressReporter>>,
|
||||
) -> 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
|
||||
let mut key = KeySet::from(self.file, self.password.as_ref());
|
||||
|
||||
|
@ -232,6 +257,11 @@ impl<'a> Download<'a> {
|
|||
|
||||
#[derive(Fail, Debug)]
|
||||
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.
|
||||
/// This step is required in order to succsessfully decrypt the
|
||||
/// file that will be downloaded.
|
||||
|
@ -257,6 +287,12 @@ pub enum Error {
|
|||
File(String, #[cause] FileError),
|
||||
}
|
||||
|
||||
impl From<ExistsError> for Error {
|
||||
fn from(err: ExistsError) -> Error {
|
||||
Error::Exists(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MetadataError> for Error {
|
||||
fn from(err: MetadataError) -> Error {
|
||||
Error::Meta(err)
|
||||
|
|
|
@ -49,8 +49,9 @@ impl<'a> Exists<'a> {
|
|||
}
|
||||
|
||||
// Parse the response
|
||||
let response = response.json::<ExistsResponse>()
|
||||
let mut response = response.json::<ExistsResponse>()
|
||||
.map_err(|_| Error::Malformed)?;
|
||||
response.set_exists(true);
|
||||
|
||||
// TODO: fetch the metadata nonce from the response headers
|
||||
|
||||
|
@ -84,6 +85,11 @@ impl ExistsResponse {
|
|||
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.
|
||||
pub fn has_password(&self) -> bool {
|
||||
self.has_password
|
||||
|
|
|
@ -52,6 +52,11 @@ impl Metadata {
|
|||
&self.name
|
||||
}
|
||||
|
||||
/// Get the file MIME type.
|
||||
pub fn mime(&self) -> &str {
|
||||
&self.mime
|
||||
}
|
||||
|
||||
/// Get the input vector
|
||||
// TODO: use an input vector length from a constant
|
||||
pub fn iv(&self) -> [u8; 12] {
|
||||
|
|
|
@ -133,6 +133,11 @@ impl RemoteFile {
|
|||
))
|
||||
}
|
||||
|
||||
/// Get the file ID.
|
||||
pub fn id(&self) -> &str {
|
||||
&self.id
|
||||
}
|
||||
|
||||
/// Get the raw secret.
|
||||
pub fn secret_raw(&self) -> &Vec<u8> {
|
||||
// A secret must have been set
|
||||
|
|
|
@ -1,9 +1,21 @@
|
|||
use ffsend_api::action::info::Info as ApiInfo;
|
||||
use ffsend_api::file::remote_file::RemoteFile;
|
||||
use failure::Fail;
|
||||
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 cmd::cmd_info::CmdInfo;
|
||||
use error::ActionError;
|
||||
use util::print_error;
|
||||
|
||||
/// A file info action.
|
||||
pub struct Info<'a> {
|
||||
|
@ -20,7 +32,7 @@ impl<'a> Info<'a> {
|
|||
|
||||
/// Invoke the info action.
|
||||
// TODO: create a trait for this method
|
||||
pub fn invoke(&self) -> Result<(), ActionError> {
|
||||
pub fn invoke(&self) -> Result<(), Error> {
|
||||
// Get the share 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
|
||||
|
||||
// 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)?;
|
||||
// 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
|
||||
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!("TTL: {}", info.ttl_millis());
|
||||
println!("TTL: {} ms", info.ttl_millis());
|
||||
|
||||
// TODO: show the file size, fetch TTL from metadata?
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
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::password::Error as PasswordError;
|
||||
use ffsend_api::action::upload::Error as UploadError;
|
||||
use ffsend_api::file::remote_file::FileParseError;
|
||||
|
||||
use action::info::Error as InfoError;
|
||||
|
||||
#[derive(Fail, Debug)]
|
||||
pub enum Error {
|
||||
/// An error occurred while invoking an action.
|
||||
|
@ -12,6 +13,12 @@ pub enum Error {
|
|||
Action(#[cause] ActionError),
|
||||
}
|
||||
|
||||
impl From<InfoError> for Error {
|
||||
fn from(err: InfoError) -> Error {
|
||||
Error::Action(ActionError::Info(err))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ActionError> for Error {
|
||||
fn from(err: ActionError) -> Error {
|
||||
Error::Action(err)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue