Explorar o código

Add exists API action to check file ability and protection

timvisee %!s(int64=7) %!d(string=hai) anos
pai
achega
349e62ed1c
Modificáronse 3 ficheiros con 130 adicións e 0 borrados
  1. 119 0
      api/src/action/exists.rs
  2. 1 0
      api/src/action/mod.rs
  3. 10 0
      api/src/file/remote_file.rs

+ 119 - 0
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<ExistsResponse, Error> {
+        self.check_exists(&client)
+    }
+
+    /// Send a request to check whether the file exists
+    fn check_exists(&self, client: &Client) -> Result<ExistsResponse, Error> {
+        // 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::<ExistsResponse>()
+            .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,
+}

+ 1 - 0
api/src/action/mod.rs

@@ -1,4 +1,5 @@
 pub mod download;
+pub mod exists;
 pub mod info;
 pub mod metadata;
 pub mod params;

+ 10 - 0
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)]