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#[derive(Debug, serde::Deserialize, serde::Serialize)]
19#[serde(untagged)]
20pub enum Response<T> {
21 Error(ErrorCode),
25
26 Ok(T),
30}
31
32impl<T> Response<T> {
33 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#[derive(Debug, serde::Deserialize, serde::Serialize)]
44#[serde(untagged)]
45pub enum ResponseData {
46 GetAttributes(GetAttributes),
48
49 FetchNodes(FetchNodes),
51}
52
53#[derive(Debug, thiserror::Error)]
55pub enum DecodeAttributesError {
56 #[error("Failed to decode base64")]
58 Base64Decode(#[from] base64::DecodeError),
59
60 #[error("Failed to decrypt")]
62 Decrypt(block_padding::Error),
63
64 #[error("Invalid Utf8")]
66 InvalidUtf8(#[from] std::str::Utf8Error),
67
68 #[error("Missing MEGA prefix")]
70 MissingMegaPrefix,
71
72 #[error("Failed to parse json")]
74 Json(#[from] serde_json::Error),
75
76 #[error("Failed to parse folder key")]
78 ParseFolderKey(#[from] FolderKeyParseError),
79
80 #[error("Key missing header")]
82 KeyMissingHeader,
83
84 #[error("Invalid key length {length}")]
86 InvalidKeyLength { length: usize },
87
88 #[error("Missing key")]
90 MissingKey,
91}
92
93#[derive(Debug, serde::Deserialize, serde::Serialize)]
95pub struct FileAttributes {
96 #[serde(rename = "n")]
98 pub name: String,
99
100 pub c: Option<String>,
102
103 #[serde(flatten)]
105 pub unknown: HashMap<String, serde_json::Value>,
106}
107
108#[derive(Debug, serde::Serialize, serde:: Deserialize)]
110pub struct GetAttributes {
111 #[serde(rename = "s")]
113 pub size: u64,
114
115 #[serde(rename = "at")]
117 pub encoded_attributes: String,
118
119 pub msd: u8,
120
121 #[serde(rename = "g")]
123 pub download_url: Option<Url>,
124
125 #[serde(flatten)]
127 pub unknown: HashMap<String, serde_json::Value>,
128}
129
130impl GetAttributes {
131 pub fn decode_attributes(&self, key: u128) -> Result<FileAttributes, DecodeAttributesError> {
133 decode_attributes(&self.encoded_attributes, key)
134 }
135}
136
137#[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 #[serde(flatten)]
150 pub unknown: HashMap<String, serde_json::Value>,
151}
152
153#[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 File = 0,
168
169 Directory = 1,
171
172 Root = 2,
174
175 Inbox = 3,
177
178 TrashBin = 4,
180}
181
182impl FetchNodesNodeKind {
183 pub fn is_file(self) -> bool {
185 matches!(self, Self::File)
186 }
187
188 pub fn is_dir(self) -> bool {
190 matches!(self, Self::Directory)
191 }
192
193 pub fn is_root(self) -> bool {
195 matches!(self, Self::Root)
196 }
197
198 pub fn is_inbox(self) -> bool {
200 matches!(self, Self::Inbox)
201 }
202
203 pub fn is_trash_bin(self) -> bool {
205 matches!(self, Self::TrashBin)
206 }
207}
208
209#[derive(Debug, serde::Serialize, serde:: Deserialize)]
211pub struct FetchNodesNode {
212 #[serde(rename = "a")]
214 pub encoded_attributes: String,
215
216 #[serde(rename = "h")]
218 pub id: String,
219
220 #[serde(rename = "k")]
224 pub key: String,
225
226 #[serde(rename = "p")]
228 pub parent_id: String,
229
230 #[serde(rename = "t")]
232 pub kind: FetchNodesNodeKind,
233
234 #[serde(rename = "ts")]
236 pub timestamp: u64,
237
238 #[serde(rename = "u")]
240 pub user: String,
241
242 pub fa: Option<String>,
243
244 #[serde(rename = "s")]
246 pub size: Option<u64>,
247
248 #[serde(rename = "sk")]
250 pub sk: Option<u64>,
251
252 #[serde(flatten)]
254 pub unknown: HashMap<String, serde_json::Value>,
255}
256
257impl FetchNodesNode {
258 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 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 let file_key = FileKey::from_encoded_bytes(key.try_into().unwrap());
298 FileOrFolderKey::File(file_key)
299 };
300
301 Ok(key)
302 }
303
304 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 pub fn is_file(&self) -> bool {
315 self.kind.is_file()
316 }
317
318 pub fn is_dir(&self) -> bool {
320 self.kind.is_dir()
321 }
322}
323
324fn 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}