From 349e62ed1c436e9d281e4af924e499e63c166f12 Mon Sep 17 00:00:00 2001 From: timvisee Date: Mon, 2 Apr 2018 18:52:08 +0200 Subject: [PATCH] Add exists API action to check file ability and protection --- api/src/action/exists.rs | 119 ++++++++++++++++++++++++++++++++++++ api/src/action/mod.rs | 1 + api/src/file/remote_file.rs | 10 +++ 3 files changed, 130 insertions(+) create mode 100644 api/src/action/exists.rs diff --git a/api/src/action/exists.rs b/api/src/action/exists.rs new file mode 100644 index 0000000..8f24975 --- /dev/null +++ b/api/src/action/exists.rs @@ -0,0 +1,119 @@ +// TODO: define redirect policy + +use reqwest::{Client, StatusCode}; + +use ext::status_code::StatusCodeExt; +use file::remote_file::RemoteFile; + +/// The HTTP status code that is returned for expired files. +const FILE_EXPIRED_STATUS: StatusCode = StatusCode::NotFound; + +/// An action to check whether a remote file exists. +/// This aciton returns an `ExistsResponse`, that defines whether the file +/// exists, and whether it is protected by a password. +pub struct Exists<'a> { + /// The remote file to check. + file: &'a RemoteFile, +} + +impl<'a> Exists<'a> { + /// Construct a new exists action. + pub fn new(file: &'a RemoteFile) -> Self { + Self { + file, + } + } + + /// Invoke the exists action. + pub fn invoke(self, client: &Client) -> Result { + self.check_exists(&client) + } + + /// Send a request to check whether the file exists + fn check_exists(&self, client: &Client) -> Result { + // Get the download url, and parse the nonce + let exists_url = self.file.api_exists_url(); + let mut response = client.get(exists_url) + .send() + .map_err(|_| Error::Request)?; + + // Validate the status code + let status = response.status(); + if !status.is_success() { + // Handle expired files + if status == FILE_EXPIRED_STATUS { + return Ok(ExistsResponse::new(false, false)); + } else { + return Err(Error::RequestStatus(status, status.err_text()).into()); + } + } + + // Parse the response + let response = response.json::() + .map_err(|_| Error::Malformed)?; + + // TODO: fetch the metadata nonce from the response headers + + Ok(response) + } +} + +/// The exists response. +#[derive(Debug, Deserialize)] +pub struct ExistsResponse { + /// Whether the file exists. + #[serde(skip)] + exists: bool, + + /// Whether this file requires a password. + #[serde(rename = "password")] + has_password: bool, +} + +impl ExistsResponse { + /// Construct a new response. + pub fn new(exists: bool, has_password: bool) -> Self { + ExistsResponse { + exists, + has_password, + } + } + + /// Whether the remote file exists on the server. + pub fn exists(&self) -> bool { + self.exists + } + + /// Whether the remote file is protected by a password. + pub fn has_password(&self) -> bool { + self.has_password + } +} + +impl Default for ExistsResponse { + fn default() -> Self { + ExistsResponse { + exists: false, + has_password: false, + } + } +} + +#[derive(Fail, Debug)] +pub enum Error { + /// Sending the request to check whether the file exists failed. + #[fail(display = "Failed to send request whether the file exists")] + Request, + + /// The response for checking whether the file exists indicated an error + /// and wasn't successful. + #[fail(display = "Bad HTTP response '{}' while requesting whether the file exists", _1)] + RequestStatus(StatusCode, String), + + /// The response from the server when checking if the file exists was + /// malformed. + /// Maybe the server responded with a new format that isn't supported yet + /// by this client. + #[fail(display = "Received malformed authentication nonce")] + Malformed, +} diff --git a/api/src/action/mod.rs b/api/src/action/mod.rs index 834e7c6..99bce3f 100644 --- a/api/src/action/mod.rs +++ b/api/src/action/mod.rs @@ -1,4 +1,5 @@ pub mod download; +pub mod exists; pub mod info; pub mod metadata; pub mod params; diff --git a/api/src/file/remote_file.rs b/api/src/file/remote_file.rs index beab89a..f6662c7 100644 --- a/api/src/file/remote_file.rs +++ b/api/src/file/remote_file.rs @@ -229,6 +229,16 @@ impl RemoteFile { url } + + /// Get the API exists URL of the file. + pub fn api_exists_url(&self) -> Url { + // Get the share URL, and add the secret fragment + let mut url = self.url.clone(); + url.set_path(format!("/api/exists/{}", self.id).as_str()); + url.set_fragment(None); + + url + } } #[derive(Debug, Fail)]