Skip to main content

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::BlockModeDecrypt;
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("Failed to decode base64")]
58    Base64Decode(#[from] base64::DecodeError),
59
60    /// Decryption failed
61    #[error("Failed to decrypt")]
62    Decrypt(block_padding::Error),
63
64    /// Invalid utf8
65    #[error("Invalid Utf8")]
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("Failed to parse json")]
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    /// Failed to select a key.
89    #[error("Missing key")]
90    MissingKey,
91}
92
93/// File attributes
94#[derive(Debug, serde::Deserialize, serde::Serialize)]
95pub struct FileAttributes {
96    /// The name of the file
97    #[serde(rename = "n")]
98    pub name: String,
99
100    /// ?
101    pub c: Option<String>,
102
103    /// Unknown attributes
104    #[serde(flatten)]
105    pub unknown: HashMap<String, serde_json::Value>,
106}
107
108/// GetAttributes command response
109#[derive(Debug, serde::Serialize, serde:: Deserialize)]
110pub struct GetAttributes {
111    /// The file size
112    #[serde(rename = "s")]
113    pub size: u64,
114
115    /// Encoded attributes
116    #[serde(rename = "at")]
117    pub encoded_attributes: String,
118
119    pub msd: u8,
120
121    /// The download url
122    #[serde(rename = "g")]
123    pub download_url: Option<Url>,
124
125    /// Unknown attributes
126    #[serde(flatten)]
127    pub unknown: HashMap<String, serde_json::Value>,
128}
129
130impl GetAttributes {
131    /// Decode the encoded attributes
132    pub fn decode_attributes(&self, key: u128) -> Result<FileAttributes, DecodeAttributesError> {
133        decode_attributes(&self.encoded_attributes, key)
134    }
135}
136
137/// FetchNodes command response
138#[derive(Debug, serde::Serialize, serde:: Deserialize)]
139pub struct FetchNodes {
140    #[serde(rename = "f")]
141    pub nodes: Vec<FetchNodesNode>,
142
143    pub noc: u8,
144
145    pub sn: String,
146    pub st: String,
147
148    /// Unknown attributes
149    #[serde(flatten)]
150    pub unknown: HashMap<String, serde_json::Value>,
151}
152
153/// The kind of node
154#[derive(
155    Debug,
156    Eq,
157    PartialEq,
158    Hash,
159    Copy,
160    Clone,
161    serde_repr::Deserialize_repr,
162    serde_repr::Serialize_repr,
163)]
164#[repr(u8)]
165pub enum FetchNodesNodeKind {
166    /// A file
167    File = 0,
168
169    /// A directory
170    Directory = 1,
171
172    /// The special root directory
173    Root = 2,
174
175    /// The special inbox directory
176    Inbox = 3,
177
178    /// The special trash bin directory
179    TrashBin = 4,
180}
181
182impl FetchNodesNodeKind {
183    /// Returns true if this is a file.
184    pub fn is_file(self) -> bool {
185        matches!(self, Self::File)
186    }
187
188    /// Returns true if this is a dir.
189    pub fn is_dir(self) -> bool {
190        matches!(self, Self::Directory)
191    }
192
193    /// Returns true if this is a root.
194    pub fn is_root(self) -> bool {
195        matches!(self, Self::Root)
196    }
197
198    /// Returns true if this is an inbox.
199    pub fn is_inbox(self) -> bool {
200        matches!(self, Self::Inbox)
201    }
202
203    /// Returns true if this is a trash bin.
204    pub fn is_trash_bin(self) -> bool {
205        matches!(self, Self::TrashBin)
206    }
207}
208
209/// A FetchNodes Node
210#[derive(Debug, serde::Serialize, serde:: Deserialize)]
211pub struct FetchNodesNode {
212    /// The attributes of the node
213    #[serde(rename = "a")]
214    pub encoded_attributes: String,
215
216    /// The id of the node
217    #[serde(rename = "h")]
218    pub id: String,
219
220    /// The key of the node.
221    ///
222    /// This is encrypted.
223    #[serde(rename = "k")]
224    pub key: String,
225
226    /// The id of the parent node
227    #[serde(rename = "p")]
228    pub parent_id: String,
229
230    /// The kind of the node
231    #[serde(rename = "t")]
232    pub kind: FetchNodesNodeKind,
233
234    /// The time of last modification
235    #[serde(rename = "ts")]
236    pub timestamp: u64,
237
238    /// The owner of the node
239    #[serde(rename = "u")]
240    pub user: String,
241
242    pub fa: Option<String>,
243
244    /// The size of the node
245    #[serde(rename = "s")]
246    pub size: Option<u64>,
247
248    /// The shared key
249    #[serde(rename = "sk")]
250    pub sk: Option<u64>,
251
252    /// Unknown attributes
253    #[serde(flatten)]
254    pub unknown: HashMap<String, serde_json::Value>,
255}
256
257impl FetchNodesNode {
258    /// Decrypt and get the key.
259    pub fn decrypt_key(
260        &self,
261        key_map: &HashMap<String, FolderKey>,
262    ) -> Result<FileOrFolderKey, DecodeAttributesError> {
263        let (key, folder_key) = self
264            .key
265            .split('/')
266            .find_map(|key| {
267                let parsed = key.split_once(':');
268                let (id, key) = match parsed {
269                    Some(parsed) => parsed,
270                    None => return Some(Err(DecodeAttributesError::KeyMissingHeader)),
271                };
272                let folder_key = key_map.get(id)?;
273
274                Some(Ok((key, folder_key)))
275            })
276            .ok_or(DecodeAttributesError::MissingKey)??;
277        let mut key = URL_SAFE_NO_PAD.decode(key)?;
278        let cipher = Aes128EcbDec::new(&folder_key.0.to_be_bytes().into());
279        let key = cipher
280            .decrypt_padded::<block_padding::NoPadding>(&mut key)
281            .map_err(DecodeAttributesError::Decrypt)?;
282        let key_len = key.len();
283        let key = if self.kind == FetchNodesNodeKind::Directory {
284            if key_len != 16 {
285                return Err(DecodeAttributesError::InvalidKeyLength { length: key_len });
286            }
287
288            // Length check is done above
289            let folder_key = FolderKey(u128::from_be_bytes(key.try_into().unwrap()));
290            FileOrFolderKey::Folder(folder_key)
291        } else {
292            if key_len != 32 {
293                return Err(DecodeAttributesError::InvalidKeyLength { length: key_len });
294            }
295
296            // Length check is done above
297            let file_key = FileKey::from_encoded_bytes(key.try_into().unwrap());
298            FileOrFolderKey::File(file_key)
299        };
300
301        Ok(key)
302    }
303
304    /// Decode the encoded attributes.
305    pub fn decode_attributes(
306        &self,
307        key_map: &HashMap<String, FolderKey>,
308    ) -> Result<FileAttributes, DecodeAttributesError> {
309        let key = self.decrypt_key(key_map)?;
310        decode_attributes(&self.encoded_attributes, key.key())
311    }
312
313    /// Check if this is a file.
314    pub fn is_file(&self) -> bool {
315        self.kind.is_file()
316    }
317
318    /// Check if this is a dir.
319    pub fn is_dir(&self) -> bool {
320        self.kind.is_dir()
321    }
322}
323
324/// Decode the encoded attributes
325fn decode_attributes(
326    encoded_attributes: &str,
327    key: u128,
328) -> Result<FileAttributes, DecodeAttributesError> {
329    let mut encoded_attributes = URL_SAFE_NO_PAD.decode(encoded_attributes)?;
330
331    let cipher = Aes128CbcDec::new(&key.to_be_bytes().into(), &[0; 16].into());
332    let decrypted = cipher
333        .decrypt_padded::<block_padding::ZeroPadding>(&mut encoded_attributes)
334        .map_err(DecodeAttributesError::Decrypt)?;
335
336    let decrypted = std::str::from_utf8(decrypted)?;
337    let decrypted = decrypted
338        .strip_prefix("MEGA")
339        .ok_or(DecodeAttributesError::MissingMegaPrefix)?;
340
341    Ok(serde_json::from_str(decrypted)?)
342}