1use reqwest::header::AUTHORIZATION;
2use serde::{
3 de::{Error as SerdeError, Unexpected},
4 Deserialize, Deserializer,
5};
6use serde_json::{self, Value as JsonValue};
7use thiserror::Error;
8
9use super::exists::{Error as ExistsError, Exists as ExistsAction};
10use crate::api::nonce::{header_nonce, request_nonce, NonceError};
11use crate::api::request::{ensure_success, ResponseError};
12use crate::api::url::UrlBuilder;
13use crate::client::Client;
14use crate::crypto::key_set::KeySet;
15use crate::crypto::sig::signature_encoded;
16use crate::crypto::{self, api::MetadataError};
17use crate::file::metadata::Metadata as MetadataData;
18use crate::file::remote_file::RemoteFile;
19
20pub struct Metadata<'a> {
25 file: &'a RemoteFile,
27
28 password: Option<String>,
30
31 check_exists: bool,
33}
34
35impl<'a> Metadata<'a> {
36 pub fn new(file: &'a RemoteFile, password: Option<String>, check_exists: bool) -> Self {
38 Self {
39 file,
40 password,
41 check_exists,
42 }
43 }
44
45 pub fn invoke(self, client: &Client) -> Result<MetadataResponse, Error> {
47 if self.check_exists {
49 let exist_response = ExistsAction::new(&self.file).invoke(&client)?;
50
51 if !exist_response.exists() {
53 return Err(Error::Expired);
54 }
55
56 if self.password.is_none() && exist_response.requires_password() {
58 return Err(Error::PasswordRequired);
59 }
60 }
61
62 let key = KeySet::from(self.file, self.password.as_ref());
64
65 let auth_nonce = self.fetch_auth_nonce(client)?;
67
68 self.fetch_metadata(&client, &key, &auth_nonce)
70 .map_err(|err| err.into())
71 }
72
73 fn fetch_auth_nonce(&self, client: &Client) -> Result<Vec<u8>, Error> {
75 request_nonce(client, UrlBuilder::download(self.file, false)).map_err(|err| err.into())
76 }
77
78 fn fetch_metadata(
85 &self,
86 client: &Client,
87 key: &KeySet,
88 auth_nonce: &[u8],
89 ) -> Result<MetadataResponse, MetaError> {
90 let sig = signature_encoded(key.auth_key().unwrap(), &auth_nonce)
92 .map_err(|_| MetaError::ComputeSignature)?;
93
94 let response = client
96 .get(UrlBuilder::api_metadata(self.file))
97 .header(AUTHORIZATION.as_str(), format!("send-v1 {}", sig))
98 .send()
99 .map_err(|_| MetaError::NonceRequest)?;
100
101 ensure_success(&response).map_err(MetaError::NonceResponse)?;
103
104 let nonce = header_nonce(&response).map_err(MetaError::Nonce)?;
106
107 MetadataResponse::from(
109 &response
110 .json::<RawMetadataResponse>()
111 .map_err(|_| MetaError::Malformed)?,
112 &key,
113 nonce,
114 )
115 .map_err(|_| MetaError::Decrypt)
116 }
117}
118
119#[derive(Debug, Deserialize)]
125#[serde(untagged)]
126pub enum RawMetadataResponse {
127 V2 {
129 #[serde(rename = "metadata")]
131 meta: String,
132
133 #[serde(deserialize_with = "deserialize_u64")]
135 size: u64,
136 },
137
138 V3 {
140 #[serde(rename = "metadata")]
142 meta: String,
143 },
144 }
147
148impl RawMetadataResponse {
149 pub fn decrypt_metadata(&self, key_set: &KeySet) -> Result<MetadataData, MetadataError> {
154 crypto::api::decrypt_metadata(self.meta(), key_set)
155 }
156
157 fn meta(&self) -> &str {
159 match self {
160 RawMetadataResponse::V2 { meta, .. } => &meta,
161 RawMetadataResponse::V3 { meta } => &meta,
162 }
163 }
164
165 pub fn size(&self) -> Option<u64> {
171 match self {
172 RawMetadataResponse::V2 { size, .. } => Some(*size),
173 RawMetadataResponse::V3 { .. } => None,
174 }
175 }
176}
177
178fn deserialize_u64<'d, D>(d: D) -> Result<u64, D::Error>
182where
183 D: Deserializer<'d>,
184{
185 Deserialize::deserialize(d).and_then(|value: JsonValue| match value {
186 JsonValue::Number(n) => n.as_u64().ok_or_else(|| {
187 if let Some(n) = n.as_i64() {
188 SerdeError::invalid_type(Unexpected::Signed(n), &"a positive integer")
189 } else if let Some(n) = n.as_f64() {
190 SerdeError::invalid_type(Unexpected::Float(n), &"a positive integer")
191 } else {
192 SerdeError::invalid_type(Unexpected::Str(&n.to_string()), &"a positive integer")
193 }
194 }),
195 JsonValue::String(s) => s
196 .parse()
197 .map_err(|_| SerdeError::invalid_type(Unexpected::Str(&s), &"a positive integer")),
198 o => Err(SerdeError::invalid_type(
199 Unexpected::Other(&o.to_string()),
200 &"a positive integer",
201 )),
202 })
203}
204
205pub struct MetadataResponse {
208 metadata: MetadataData,
210
211 size: Option<u64>,
213
214 nonce: Vec<u8>,
216}
217
218impl<'a> MetadataResponse {
219 pub fn new(metadata: MetadataData, size: Option<u64>, nonce: Vec<u8>) -> Self {
221 MetadataResponse {
222 metadata,
223 size,
224 nonce,
225 }
226 }
227
228 pub fn from(
234 raw: &RawMetadataResponse,
235 key_set: &KeySet,
236 nonce: Vec<u8>,
237 ) -> Result<Self, MetadataError> {
238 Ok(Self::new(raw.decrypt_metadata(key_set)?, raw.size(), nonce))
239 }
240
241 pub fn metadata(&self) -> &MetadataData {
243 &self.metadata
244 }
245
246 pub fn size(&self) -> u64 {
248 self.size
250 .unwrap_or_else(|| self.metadata.size().expect("file size unknown, newer API?"))
251 }
252
253 pub fn nonce(&self) -> &Vec<u8> {
255 &self.nonce
256 }
257}
258
259#[derive(Error, Debug)]
260pub enum Error {
261 #[error("failed to check whether the file exists")]
264 Exists(#[from] ExistsError),
265
266 #[error("failed to request file data")]
270 Request(#[from] RequestError),
271
272 #[error("the file has expired or did never exist")]
275 Expired,
276
277 #[error("missing password, password required")]
279 PasswordRequired,
280}
281
282impl From<MetaError> for Error {
283 fn from(err: MetaError) -> Error {
284 Error::Request(RequestError::Meta(err))
285 }
286}
287
288impl From<NonceError> for Error {
289 fn from(err: NonceError) -> Error {
290 match err {
291 NonceError::Expired => Error::Expired,
292 err => Error::Request(RequestError::Auth(err)),
293 }
294 }
295}
296
297#[derive(Error, Debug)]
298pub enum RequestError {
299 #[error("failed to authenticate")]
301 Auth(#[from] NonceError),
302
303 #[error("failed to retrieve file metadata")]
305 Meta(#[from] MetaError),
306}
307
308#[derive(Error, Debug)]
309pub enum MetaError {
310 #[error("failed to compute cryptographic signature")]
313 ComputeSignature,
314
315 #[error("failed to request metadata nonce")]
317 NonceRequest,
318
319 #[error("bad response from server while fetching metadata nonce")]
322 NonceResponse(#[from] ResponseError),
323
324 #[error("failed to parse the metadata encryption nonce")]
326 Nonce(#[from] NonceError),
327
328 #[error("received malformed metadata")]
331 Malformed,
332
333 #[error("failed to decrypt received metadata")]
335 Decrypt,
336}