ffsend_api/action/
exists.rs

1use thiserror::Error;
2
3use crate::api::request::{ensure_success, ResponseError};
4use crate::api::url::UrlBuilder;
5use crate::client::Client;
6use crate::file::remote_file::RemoteFile;
7
8/// An action to check whether a remote file exists.
9/// This aciton returns an `ExistsResponse`, that defines whether the file
10/// exists, and whether it is protected by a password.
11///
12/// This API specification for this action is compatible with both Firefox Send v2 and v3.
13pub struct Exists<'a> {
14    /// The remote file to check.
15    file: &'a RemoteFile,
16}
17
18impl<'a> Exists<'a> {
19    /// Construct a new exists action.
20    pub fn new(file: &'a RemoteFile) -> Self {
21        Self { file }
22    }
23
24    /// Invoke the exists action.
25    pub fn invoke(self, client: &Client) -> Result<ExistsResponse, Error> {
26        self.check_exists(&client)
27    }
28
29    /// Send a request to check whether the file exists
30    fn check_exists(&self, client: &Client) -> Result<ExistsResponse, Error> {
31        // Get the download url, and parse the nonce
32        let exists_url = UrlBuilder::api_exists(self.file);
33        let response = client.get(exists_url).send().map_err(|_| Error::Request)?;
34
35        // Ensure the status code is successful, check the expiry state
36        match ensure_success(&response) {
37            Ok(_) => {}
38            Err(ResponseError::Expired) => return Ok(ExistsResponse::new(false, false)),
39            Err(err) => return Err(Error::Response(err)),
40        }
41
42        // Parse the response
43        let mut response = response
44            .json::<ExistsResponse>()
45            .map_err(|_| Error::Malformed)?;
46        response.set_exists(true);
47
48        // TODO: fetch the metadata nonce from the response headers
49
50        Ok(response)
51    }
52}
53
54/// The exists response.
55#[derive(Debug, Deserialize)]
56pub struct ExistsResponse {
57    /// Whether the file exists.
58    #[serde(skip)]
59    exists: bool,
60
61    /// Whether this file requires a password.
62    // This field is named `password` in Send v2
63    #[serde(rename = "requiresPassword", alias = "password")]
64    requires_password: bool,
65}
66
67impl ExistsResponse {
68    /// Construct a new response.
69    pub fn new(exists: bool, requires_password: bool) -> Self {
70        ExistsResponse {
71            exists,
72            requires_password,
73        }
74    }
75
76    /// Whether the remote file exists on the server.
77    pub fn exists(&self) -> bool {
78        self.exists
79    }
80
81    /// Set whether the remote file exists.
82    pub fn set_exists(&mut self, exists: bool) {
83        self.exists = exists;
84    }
85
86    /// Whether the remote file is protected by a password.
87    #[deprecated(since = "0.2", note = "please use `requires_password` instead")]
88    pub fn has_password(&self) -> bool {
89        self.requires_password()
90    }
91
92    /// Whether the remote file is protected by a password.
93    pub fn requires_password(&self) -> bool {
94        self.requires_password
95    }
96}
97
98impl Default for ExistsResponse {
99    fn default() -> Self {
100        ExistsResponse {
101            exists: false,
102            requires_password: false,
103        }
104    }
105}
106
107#[derive(Error, Debug)]
108pub enum Error {
109    /// Sending the request to check whether the file exists failed.
110    #[error("failed to send request whether the file exists")]
111    Request,
112
113    /// The server responded with an error while checking whether the file
114    /// exists.
115    #[error("bad response from server while checking file existence")]
116    Response(#[from] ResponseError),
117
118    /// The response from the server when checking if the file exists was
119    /// malformed.
120    /// Maybe the server responded with a new format that isn't supported yet
121    /// by this client.
122    #[error("received malformed authentication nonce")]
123    Malformed,
124}