mega/types/
response.rs

1use crate::ErrorCode;
2use crate::FileKey;
3use crate::FileOrFolderKey;
4use crate::FolderKey;
5use crate::FolderKeyParseError;
6use base64::Engine;
7use base64::engine::general_purpose::URL_SAFE_NO_PAD;
8use cbc::cipher::BlockDecryptMut;
9use cbc::cipher::KeyInit;
10use cbc::cipher::KeyIvInit;
11use std::collections::HashMap;
12use url::Url;
13
14type Aes128CbcDec = cbc::Decryptor<aes::Aes128>;
15type Aes128EcbDec = ecb::Decryptor<aes::Aes128>;
16
17/// An api response
18#[derive(Debug, serde::Deserialize, serde::Serialize)]
19#[serde(untagged)]
20pub enum Response<T> {
21    /// Error
22    ///
23    /// There was en error with the specified code.
24    Error(ErrorCode),
25
26    /// Success
27    ///
28    /// There is valid data
29    Ok(T),
30}
31
32impl<T> Response<T> {
33    /// Convert this into a Result.
34    pub fn into_result(self) -> Result<T, ErrorCode> {
35        match self {
36            Self::Ok(t) => Ok(t),
37            Self::Error(error) => Err(error),
38        }
39    }
40}
41
42/// API Response data
43#[derive(Debug, serde::Deserialize, serde::Serialize)]
44#[serde(untagged)]
45pub enum ResponseData {
46    /// Response for a GetAttributes command
47    GetAttributes(GetAttributes),
48
49    /// Response for FetchNodes command
50    FetchNodes(FetchNodes),
51}
52
53/// An error that may occur while decoding attributes
54#[derive(Debug, thiserror::Error)]
55pub enum DecodeAttributesError {
56    /// Failed to decode base64
57    #[error(transparent)]
58    Base64Decode(#[from] base64::DecodeError),
59
60    /// Decryption failed
61    #[error("failed to decrypt")]
62    Decrypt(block_padding::UnpadError),
63
64    /// Invalid utf8
65    #[error(transparent)]
66    InvalidUtf8(#[from] std::str::Utf8Error),
67
68    /// Missing the MEGA prefix
69    #[error("missing MEGA prefix")]
70    MissingMegaPrefix,
71
72    /// Json parse error
73    #[error(transparent)]
74    Json(#[from] serde_json::Error),
75
76    /// Failed to parse a folder key
77    #[error("failed to parse folder key")]
78    ParseFolderKey(#[from] FolderKeyParseError),
79
80    /// A key was missing a header
81    #[error("key missing header")]
82    KeyMissingHeader,
83
84    /// The key was the wrong size
85    #[error("invalid key length '{length}'")]
86    InvalidKeyLength { length: usize },
87}
88
89/// File attributes
90#[derive(Debug, serde::Deserialize, serde::Serialize)]
91pub struct FileAttributes {
92    /// The name of the file
93    #[serde(rename = "n")]
94    pub name: String,
95
96    /// ?
97    pub c: Option<String>,
98
99    /// Unknown attributes
100    #[serde(flatten)]
101    pub unknown: HashMap<String, serde_json::Value>,
102}
103
104/// GetAttributes command response
105#[derive(Debug, serde::Serialize, serde:: Deserialize)]
106pub struct GetAttributes {
107    /// The file size
108    #[serde(rename = "s")]
109    pub size: u64,
110
111    /// Encoded attributes
112    #[serde(rename = "at")]
113    pub encoded_attributes: String,
114
115    pub msd: u8,
116
117    /// The download url
118    #[serde(rename = "g")]
119    pub download_url: Option<Url>,
120
121    /// Unknown attributes
122    #[serde(flatten)]
123    pub unknown: HashMap<String, serde_json::Value>,
124}
125
126impl GetAttributes {
127    /// Decode the encoded attributes
128    pub fn decode_attributes(&self, key: u128) -> Result<FileAttributes, DecodeAttributesError> {
129        decode_attributes(&self.encoded_attributes, key)
130    }
131}
132
133/// FetchNodes command response
134#[derive(Debug, serde::Serialize, serde:: Deserialize)]
135pub struct FetchNodes {
136    #[serde(rename = "f")]
137    pub nodes: Vec<FetchNodesNode>,
138
139    pub noc: u8,
140
141    pub sn: String,
142    pub st: String,
143
144    /// Unknown attributes
145    #[serde(flatten)]
146    pub unknown: HashMap<String, serde_json::Value>,
147}
148
149/// The kind of node
150#[derive(
151    Debug,
152    Eq,
153    PartialEq,
154    Hash,
155    Copy,
156    Clone,
157    serde_repr::Deserialize_repr,
158    serde_repr::Serialize_repr,
159)]
160#[repr(u8)]
161pub enum FetchNodesNodeKind {
162    /// A file
163    File = 0,
164
165    /// A directory
166    Directory = 1,
167
168    /// The special root directory
169    Root = 2,
170
171    /// The special inbox directory
172    Inbox = 3,
173
174    /// The special trash bin directory
175    TrashBin = 4,
176}
177
178impl FetchNodesNodeKind {
179    /// Returns true if this is a file.
180    pub fn is_file(self) -> bool {
181        matches!(self, Self::File)
182    }
183
184    /// Returns true if this is a dir.
185    pub fn is_dir(self) -> bool {
186        matches!(self, Self::Directory)
187    }
188
189    /// Returns true if this is a root.
190    pub fn is_root(self) -> bool {
191        matches!(self, Self::Root)
192    }
193
194    /// Returns true if this is an inbox.
195    pub fn is_inbox(self) -> bool {
196        matches!(self, Self::Inbox)
197    }
198
199    /// Returns true if this is a trash bin.
200    pub fn is_trash_bin(self) -> bool {
201        matches!(self, Self::TrashBin)
202    }
203}
204
205/// A FetchNodes Node
206#[derive(Debug, serde::Serialize, serde:: Deserialize)]
207pub struct FetchNodesNode {
208    /// The attributes of the node
209    #[serde(rename = "a")]
210    pub encoded_attributes: String,
211
212    /// The id of the node
213    #[serde(rename = "h")]
214    pub id: String,
215
216    /// The key of the node.
217    ///
218    /// This is encrypted.
219    #[serde(rename = "k")]
220    pub key: String,
221
222    /// The id of the parent node
223    #[serde(rename = "p")]
224    pub parent_id: String,
225
226    /// The kind of the node
227    #[serde(rename = "t")]
228    pub kind: FetchNodesNodeKind,
229
230    /// The time of last modification
231    #[serde(rename = "ts")]
232    pub timestamp: u64,
233
234    /// The owner of the node
235    #[serde(rename = "u")]
236    pub user: String,
237
238    pub fa: Option<String>,
239
240    /// The size of the node
241    #[serde(rename = "s")]
242    pub size: Option<u64>,
243
244    /// Unknown attributes
245    #[serde(flatten)]
246    pub unknown: HashMap<String, serde_json::Value>,
247}
248
249impl FetchNodesNode {
250    /// Decrypt and get the key.
251    pub fn decrypt_key(
252        &self,
253        folder_key: &FolderKey,
254    ) -> Result<FileOrFolderKey, DecodeAttributesError> {
255        let (_, key) = self
256            .key
257            .split_once(':')
258            .ok_or(DecodeAttributesError::KeyMissingHeader)?;
259
260        let mut key = URL_SAFE_NO_PAD.decode(key)?;
261        let cipher = Aes128EcbDec::new(&folder_key.0.to_be_bytes().into());
262        let key = cipher
263            .decrypt_padded_mut::<block_padding::NoPadding>(&mut key)
264            .map_err(DecodeAttributesError::Decrypt)?;
265        let key_len = key.len();
266        let key = if self.kind == FetchNodesNodeKind::Directory {
267            if key_len != 16 {
268                return Err(DecodeAttributesError::InvalidKeyLength { length: key_len });
269            }
270
271            // Length check is done above
272            let folder_key = FolderKey(u128::from_be_bytes(key.try_into().unwrap()));
273            FileOrFolderKey::Folder(folder_key)
274        } else {
275            if key_len != 32 {
276                return Err(DecodeAttributesError::InvalidKeyLength { length: key_len });
277            }
278
279            // Length check is done above
280            let file_key = FileKey::from_encoded_bytes(key.try_into().unwrap());
281            FileOrFolderKey::File(file_key)
282        };
283
284        Ok(key)
285    }
286
287    /// Decode the encoded attributes.
288    pub fn decode_attributes(
289        &self,
290        folder_key: &FolderKey,
291    ) -> Result<FileAttributes, DecodeAttributesError> {
292        let key = self.decrypt_key(folder_key)?;
293        decode_attributes(&self.encoded_attributes, key.key())
294    }
295
296    /// Check if this is a file.
297    pub fn is_file(&self) -> bool {
298        self.kind.is_file()
299    }
300
301    /// Check if this is a dir.
302    pub fn is_dir(&self) -> bool {
303        self.kind.is_dir()
304    }
305}
306
307/// Decode the encoded attributes
308fn decode_attributes(
309    encoded_attributes: &str,
310    key: u128,
311) -> Result<FileAttributes, DecodeAttributesError> {
312    let mut encoded_attributes = URL_SAFE_NO_PAD.decode(encoded_attributes)?;
313
314    let cipher = Aes128CbcDec::new(&key.to_be_bytes().into(), &[0; 16].into());
315    let decrypted = cipher
316        .decrypt_padded_mut::<block_padding::ZeroPadding>(&mut encoded_attributes)
317        .map_err(DecodeAttributesError::Decrypt)?;
318
319    let decrypted = std::str::from_utf8(decrypted)?;
320    let decrypted = decrypted
321        .strip_prefix("MEGA")
322        .ok_or(DecodeAttributesError::MissingMegaPrefix)?;
323
324    Ok(serde_json::from_str(decrypted)?)
325}