Ver código fonte

Automatically derive owner tokens from history

timvisee 7 anos atrás
pai
commit
77fb9c436d

+ 7 - 2
ROADMAP.md

@@ -1,11 +1,11 @@
 # Release 0.1
+- Sort history entries
+- Panic when secret is missing from URL with info action
 - History compiler flag
 - Lowercase error messages
-- Automatically get owner token, from file history when setting password
 - Switch to `directories` instead of `app_dirs2`?
 - Allow file/directory archiving on upload
 - Allow unarchiving on download 
-- Show a simplified command list when calling `ffsend` without arguments
 - Use clipboard through `xclip` on Linux if available for persistence
 - Allow environment variable settings using `Arg.env(NAME)`
 - Automated releases through CI
@@ -13,7 +13,12 @@
 - Ubuntu PPA package
 - Implement error handling everywhere properly
 - Embed request errors
+- Extract utility module
+- Think of a new description:
+    Securely and easily share files from the command line;
+    a fully featured Firefox Send client. 
 - Check all TODOs, solve them when possible
+- Windows, macOS and Redox support
 
 # Future releases
 - Color usage flag

+ 10 - 1
api/src/file/remote_file.rs

@@ -252,11 +252,20 @@ impl RemoteFile {
         &mut self.owner_token
     }
 
-    /// Set the owner token.
+    /// Set the owner token, wrapped in an option.
+    /// If `None` is given, the owner token will be unset.
     pub fn set_owner_token(&mut self, token: Option<String>) {
         self.owner_token = token;
     }
 
+    /// Check whether an owner token is set in this remote file.
+    pub fn has_owner_token(&self) -> bool {
+        self.owner_token
+            .clone()
+            .map(|t| !t.is_empty())
+            .unwrap_or(false)
+    }
+
     /// Get the host URL for this remote file.
     pub fn host(&self) -> Url {
         self.host.clone()

+ 2 - 1
cli/src/action/delete.rs

@@ -44,8 +44,9 @@ impl<'a> Delete<'a> {
         // Create a reqwest client
         let client = Client::new();
 
-        // Parse the remote file based on the share URL, get the owner token
+        // Parse the remote file based on the share URL, derive the owner token from history
         let mut file = RemoteFile::parse_url(url, matcher_delete.owner())?;
+        history_tool::derive_owner_token(&matcher_main, &mut file);
 
         // Ensure the owner token is set
         ensure_owner_token(file.owner_token_mut(), &matcher_main);

+ 5 - 2
cli/src/action/info.rs

@@ -50,9 +50,9 @@ impl<'a> Info<'a> {
         // Create a reqwest client
         let client = Client::new();
 
-        // Parse the remote file based on the share URL, get the password
+        // Parse the remote file based on the share URL, derive the owner token from history
         let mut file = RemoteFile::parse_url(url, matcher_info.owner())?;
-        let mut password = matcher_info.password();
+        history_tool::derive_owner_token(&matcher_main, &mut file);
 
         // Ensure the owner token is set
         ensure_owner_token(file.owner_token_mut(), &matcher_main);
@@ -66,6 +66,9 @@ impl<'a> Info<'a> {
             return Err(Error::Expired);
         }
 
+        // Get the password
+        let mut password = matcher_info.password();
+
         // Ensure a password is set when required
         ensure_password(&mut password, exists.has_password(), &matcher_main);
 

+ 2 - 1
cli/src/action/params.rs

@@ -42,8 +42,9 @@ impl<'a> Params<'a> {
         // Create a reqwest client
         let client = Client::new();
 
-        // Parse the remote file based on the share URL
+        // Parse the remote file based on the share URL, derive the owner token from history
         let mut file = RemoteFile::parse_url(url, matcher_params.owner())?;
+        history_tool::derive_owner_token(&matcher_main, &mut file);
 
         // Ensure the owner token is set
         ensure_owner_token(file.owner_token_mut(), &matcher_main);

+ 2 - 1
cli/src/action/password.rs

@@ -41,8 +41,9 @@ impl<'a> Password<'a> {
         // Create a reqwest client
         let client = Client::new();
 
-        // Parse the remote file based on the share URL
+        // Parse the remote file based on the share URL, derive the owner token from history
         let mut file = RemoteFile::parse_url(url, matcher_password.owner())?;
+        history_tool::derive_owner_token(&matcher_main, &mut file);
 
         // Ensure the owner token is set
         ensure_owner_token(file.owner_token_mut(), &matcher_main);

+ 10 - 0
cli/src/history.rs

@@ -198,6 +198,16 @@ impl History {
         &self.files
     }
 
+    /// Get a file from the history, based on the given remote file.
+    /// The file ID and host will be compared against all files in this history.
+    /// If multiple files exist within the history that are equal, only one is returned.
+    /// If no matching file was found, `None` is returned.
+    pub fn get_file(&self, file: &RemoteFile) -> Option<&RemoteFile> {
+        self.files.iter()
+            .filter(|f| f.id() == file.id() && f.host() == file.host())
+            .next()
+    }
+
     /// Garbage collect (remove) all files that have been expired,
     /// as defined by their `expire_at` property.
     ///

+ 46 - 0
cli/src/history_tool.rs

@@ -78,3 +78,49 @@ pub fn remove(matcher_main: &MainMatcher, file: &RemoteFile) -> bool {
     }
     ok
 }
+
+/// Derive an owner token of a remote file from the current history.
+/// The newly derived owner token will be set into the given borrowed remote file.
+/// This method may be used to automatically derive the owner token for some file actions
+/// requiring this token, to prevent the user from having to enter it manually.
+///
+/// If the file already has an owner token set,
+/// nothing will be derived and `false` is returned.
+/// If an error occurred while deriving,
+/// the error is printed and `false` is returned.
+/// If there was no matching file in the history,
+/// and no owner token could be derived, `false` is returned.
+/// If in incognito mode, nothing is derived and `false` is returned.
+///
+/// If an owner token was successfully derived from the history,
+/// `true` is returned.
+///
+/// Note, to check if an owner token is set in the remote file,
+/// use the `file.has_owner_token()` method instead of the returned boolean from this method.
+pub fn derive_owner_token(matcher_main: &MainMatcher, file: &mut RemoteFile) -> bool {
+    // Return if the remote file already has an owner token set, or if we're incognito
+    if file.has_owner_token() || matcher_main.incognito() {
+        return false;
+    }
+
+    // Load the history
+    let history = match History::load_or_new(matcher_main.history()) {
+        Ok(history) => history,
+        Err(err) => {
+            print_error(err.context(
+                "Failed to derive file owner token from history, ignoring",
+            ));
+            return false;
+        }
+    };
+
+    // Find a matching file, grab and set the owner token if available
+    match history.get_file(file) {
+        Some(f) if f.has_owner_token() => {
+            file.set_owner_token(f.owner_token().cloned());
+            println!("DEBUG: Owner token set from history");
+            return true;
+        },
+        _ => return false,
+    }
+}