浏览代码

Create file/API URL builder

timvisee 7 年之前
父节点
当前提交
6e913428c2

+ 1 - 0
IDEAS.md

@@ -39,3 +39,4 @@
 - Ask to add MIME extension to downloaded files without one on Windows
 - Fetch max file size from `server/jsconfig.js`
 - Define a redirect policy (allow setting max redirects)
+- Support servers that are hosted on a sub path (URL builder resets path)

+ 3 - 2
api/src/action/delete.rs

@@ -5,6 +5,7 @@ use api::data::{
     OwnedData,
 };
 use api::nonce::{NonceError, request_nonce};
+use api::url::UrlBuilder;
 use ext::status_code::StatusCodeExt;
 use file::remote_file::RemoteFile;
 
@@ -48,7 +49,7 @@ impl<'a> Delete<'a> {
     {
         request_nonce(
             client,
-            self.file.download_url(false),
+            UrlBuilder::download(self.file, false),
         ).map_err(|err| PrepareError::Auth(err))
     }
 
@@ -59,7 +60,7 @@ impl<'a> Delete<'a> {
         data: OwnedData<DeleteData>,
     ) -> Result<(), DeleteError> {
         // Get the delete URL, and send the request
-        let url = self.file.api_delete_url();
+        let url = UrlBuilder::api_delete(self.file);
         let response = client.post(url)
             .json(&data)
             .send()

+ 2 - 1
api/src/action/download.rs

@@ -11,6 +11,7 @@ use reqwest::{Client, Response, StatusCode};
 use reqwest::header::Authorization;
 use reqwest::header::ContentLength;
 
+use api::url::UrlBuilder;
 use crypto::key_set::KeySet;
 use crypto::sig::signature_encoded;
 use ext::status_code::StatusCodeExt;
@@ -172,7 +173,7 @@ impl<'a> Download<'a> {
             .map_err(|_| DownloadError::ComputeSignature)?;
 
         // Build and send the download request
-        let response = client.get(self.file.api_download_url())
+        let response = client.get(UrlBuilder::api_download(self.file))
             .header(Authorization(
                 format!("send-v1 {}", sig)
             ))

+ 2 - 1
api/src/action/exists.rs

@@ -1,5 +1,6 @@
 use reqwest::{Client, StatusCode};
 
+use api::url::UrlBuilder;
 use ext::status_code::StatusCodeExt;
 use file::remote_file::RemoteFile;
 
@@ -30,7 +31,7 @@ impl<'a> Exists<'a> {
     /// 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 exists_url = UrlBuilder::api_exists(self.file);
         let mut response = client.get(exists_url)
             .send()
             .map_err(|_| Error::Request)?;

+ 3 - 2
api/src/action/info.rs

@@ -11,6 +11,7 @@ use api::data::{
     OwnedData,
 };
 use api::nonce::{NonceError, request_nonce};
+use api::url::UrlBuilder;
 use ext::status_code::StatusCodeExt;
 use file::remote_file::RemoteFile;
 
@@ -54,7 +55,7 @@ impl<'a> Info<'a> {
     {
         request_nonce(
             client,
-            self.file.download_url(false),
+            UrlBuilder::download(self.file, false),
         ).map_err(|err| PrepareError::Auth(err))
     }
 
@@ -65,7 +66,7 @@ impl<'a> Info<'a> {
         data: OwnedData<InfoData>,
     ) -> Result<InfoResponse, InfoError> {
         // Get the info URL, and send the request
-        let url = self.file.api_info_url();
+        let url = UrlBuilder::api_info(self.file);
         let mut response = client.post(url)
             .json(&data)
             .send()

+ 3 - 2
api/src/action/metadata.rs

@@ -5,6 +5,7 @@ use reqwest::header::Authorization;
 use serde_json;
 
 use api::nonce::{header_nonce, NonceError, request_nonce};
+use api::url::UrlBuilder;
 use crypto::b64;
 use crypto::key_set::KeySet;
 use crypto::sig::signature_encoded;
@@ -52,7 +53,7 @@ impl<'a> Metadata<'a> {
     {
         request_nonce(
             client,
-            self.file.download_url(false),
+            UrlBuilder::download(self.file, false),
         ).map_err(|err| RequestError::Auth(err))
     }
 
@@ -73,7 +74,7 @@ impl<'a> Metadata<'a> {
             .map_err(|_| MetaError::ComputeSignature)?;
 
         // Build the request, fetch the encrypted metadata
-        let mut response = client.get(self.file.api_meta_url())
+        let mut response = client.get(UrlBuilder::api_metadata(self.file))
             .header(Authorization(
                 format!("send-v1 {}", sig)
             ))

+ 3 - 2
api/src/action/params.rs

@@ -5,6 +5,7 @@ use api::data::{
     OwnedData,
 };
 use api::nonce::{NonceError, request_nonce};
+use api::url::UrlBuilder;
 use ext::status_code::StatusCodeExt;
 use file::remote_file::RemoteFile;
 
@@ -70,7 +71,7 @@ impl<'a> Params<'a> {
     {
         request_nonce(
             client,
-            self.file.download_url(false),
+            UrlBuilder::download(self.file, false),
         ).map_err(|err| PrepareError::Auth(err))
     }
 
@@ -81,7 +82,7 @@ impl<'a> Params<'a> {
         data: OwnedData<ParamsData>,
     ) -> Result<(), ChangeError> {
         // Get the params URL, and send the change
-        let url = self.file.api_params_url();
+        let url = UrlBuilder::api_params(self.file);
         let response = client.post(url)
             .json(&data)
             .send()

+ 4 - 3
api/src/action/password.rs

@@ -5,6 +5,7 @@ use api::data::{
     OwnedData,
 };
 use api::nonce::{NonceError, request_nonce};
+use api::url::UrlBuilder;
 use crypto::key_set::KeySet;
 use ext::status_code::StatusCodeExt;
 use file::remote_file::RemoteFile;
@@ -47,7 +48,7 @@ impl<'a> Password<'a> {
         }
 
         // Derive a new authentication key
-        key.derive_auth_password(self.password, &self.file.download_url(true));
+        key.derive_auth_password(self.password, &UrlBuilder::download(self.file, true));
 
         // Build the password data, wrap it as owned
         let data = OwnedData::from(PasswordData::from(&key), &self.file)
@@ -64,7 +65,7 @@ impl<'a> Password<'a> {
     {
         request_nonce(
             client,
-            self.file.download_url(false),
+            UrlBuilder::download(self.file, false),
         ).map_err(|err| PrepareError::Auth(err))
     }
 
@@ -75,7 +76,7 @@ impl<'a> Password<'a> {
         data: OwnedData<PasswordData>,
     ) -> Result<(), ChangeError> {
         // Get the password URL, and send the change
-        let url = self.file.api_password_url();
+        let url = UrlBuilder::api_password(self.file);
         let response = client.post(url)
             .json(&data)
             .send()

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

@@ -1,2 +1,3 @@
 pub mod data;
 pub mod nonce;
+pub mod url;

+ 72 - 0
api/src/api/url.rs

@@ -0,0 +1,72 @@
+use url::Url;
+
+use file::remote_file::RemoteFile;
+
+/// A struct, that helps building URLs for communicating with a remote host.
+pub struct UrlBuilder;
+
+impl UrlBuilder {
+    /// Get the download URL of the given file.
+    /// This URL is identical to the share URL, a term used in this API.
+    /// Set `secret` to `true`, to include it in the URL if known.
+    pub fn download(file: &RemoteFile, secret: bool) -> Url {
+        // Get the share URL, and update the secret fragment
+        let mut url = file.url().clone();
+        if secret && file.has_secret() {
+            url.set_fragment(Some(&file.secret()));
+        } else {
+            url.set_fragment(None);
+        }
+
+        url
+    }
+
+    /// Generate an API file URL, with the given endpoint.
+    /// The endpoint should not contain any slashes.
+    ///
+    /// Valid endpoints may be 'metadata', 'download' or for example
+    /// 'password'.
+    fn api(endpoint: &str, file: &RemoteFile) -> Url {
+        // Get the share URL, and add the secret fragment
+        let mut url = file.url().clone();
+        url.set_path(format!("/api/{}/{}", endpoint, file.id()).as_str());
+        url.set_fragment(None);
+
+        url
+    }
+
+    /// Get the API metadata URL for the given file.
+    pub fn api_metadata(file: &RemoteFile) -> Url {
+        Self::api("metadata", file)
+    }
+
+    /// Get the API download URL for the given file.
+    pub fn api_download(file: &RemoteFile) -> Url {
+        Self::api("download", file)
+    }
+
+    /// Get the API password URL for the given file.
+    pub fn api_password(file: &RemoteFile) -> Url {
+        Self::api("password", file)
+    }
+
+    /// Get the API params URL for the given file.
+    pub fn api_params(file: &RemoteFile) -> Url {
+        Self::api("params", file)
+    }
+
+    /// Get the API info URL for the given file.
+    pub fn api_info(file: &RemoteFile) -> Url {
+        Self::api("info", file)
+    }
+
+    /// Get the API exists URL for the given file.
+    pub fn api_exists(file: &RemoteFile) -> Url {
+        Self::api("exists", file)
+    }
+
+    /// Get the API delete URL for the given file.
+    pub fn api_delete(file: &RemoteFile) -> Url {
+        Self::api("delete", file)
+    }
+}

+ 2 - 1
api/src/crypto/key_set.rs

@@ -1,6 +1,7 @@
 use openssl::symm::Cipher;
 use url::Url;
 
+use api::url::UrlBuilder;
 use file::remote_file::RemoteFile;
 use super::{b64, rand_bytes};
 use super::hdkf::{derive_auth_key, derive_file_key, derive_meta_key};
@@ -54,7 +55,7 @@ impl KeySet {
 
         // Derive a pasworded key
         if let Some(password) = password {
-            set.derive_auth_password(password, &file.download_url(true));
+            set.derive_auth_password(password, &UrlBuilder::download(&file, true));
         }
 
         set

+ 5 - 85
api/src/file/remote_file.rs

@@ -138,6 +138,11 @@ impl RemoteFile {
         &self.id
     }
 
+    /// Get the file URL, provided by the server.
+    pub fn url(&self) -> &Url {
+        &self.url
+    }
+
     /// Get the raw secret.
     pub fn secret_raw(&self) -> &Vec<u8> {
         // A secret must have been set
@@ -169,91 +174,6 @@ impl RemoteFile {
     pub fn set_owner_token(&mut self, token: Option<String>) {
         self.owner_token = token;
     }
-
-    /// Get the download URL of the file
-    /// This URL is identical to the share URL, a term used in this API.
-    /// Set `secret` to `true`, to include it in the URL if known.
-    pub fn download_url(&self, secret: bool) -> Url {
-        // Get the share URL, and add the secret fragment
-        let mut url = self.url.clone();
-        if secret && self.has_secret() {
-            url.set_fragment(Some(&self.secret()));
-        } else {
-            url.set_fragment(None);
-        }
-
-        url
-    }
-
-    /// Get the API metadata URL of the file.
-    pub fn api_meta_url(&self) -> Url {
-        // Get the share URL, and add the secret fragment
-        let mut url = self.url.clone();
-        url.set_path(format!("/api/metadata/{}", self.id).as_str());
-        url.set_fragment(None);
-
-        url
-    }
-
-    /// Get the API download URL of the file.
-    pub fn api_download_url(&self) -> Url {
-        // Get the share URL, and add the secret fragment
-        let mut url = self.url.clone();
-        url.set_path(format!("/api/download/{}", self.id).as_str());
-        url.set_fragment(None);
-
-        url
-    }
-
-    /// Get the API password URL of the file.
-    pub fn api_password_url(&self) -> Url {
-        // Get the share URL, and add the secret fragment
-        let mut url = self.url.clone();
-        url.set_path(format!("/api/password/{}", self.id).as_str());
-        url.set_fragment(None);
-
-        url
-    }
-
-    /// Get the API params URL of the file.
-    pub fn api_params_url(&self) -> Url {
-        // Get the share URL, and add the secret fragment
-        let mut url = self.url.clone();
-        url.set_path(format!("/api/params/{}", self.id).as_str());
-        url.set_fragment(None);
-
-        url
-    }
-
-    /// Get the API info URL of the file.
-    pub fn api_info_url(&self) -> Url {
-        // Get the share URL, and add the secret fragment
-        let mut url = self.url.clone();
-        url.set_path(format!("/api/info/{}", self.id).as_str());
-        url.set_fragment(None);
-
-        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
-    }
-
-    /// Get the API delete URL of the file.
-    pub fn api_delete_url(&self) -> Url {
-        // Get the share URL, and add the secret fragment
-        let mut url = self.url.clone();
-        url.set_path(format!("/api/delete/{}", self.id).as_str());
-        url.set_fragment(None);
-
-        url
-    }
 }
 
 #[derive(Debug, Fail)]