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#[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(transparent)]
58 Base64Decode(#[from] base64::DecodeError),
59
60 #[error("failed to decrypt")]
62 Decrypt(block_padding::UnpadError),
63
64 #[error(transparent)]
66 InvalidUtf8(#[from] std::str::Utf8Error),
67
68 #[error("missing MEGA prefix")]
70 MissingMegaPrefix,
71
72 #[error(transparent)]
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
89#[derive(Debug, serde::Deserialize, serde::Serialize)]
91pub struct FileAttributes {
92 #[serde(rename = "n")]
94 pub name: String,
95
96 pub c: Option<String>,
98
99 #[serde(flatten)]
101 pub unknown: HashMap<String, serde_json::Value>,
102}
103
104#[derive(Debug, serde::Serialize, serde:: Deserialize)]
106pub struct GetAttributes {
107 #[serde(rename = "s")]
109 pub size: u64,
110
111 #[serde(rename = "at")]
113 pub encoded_attributes: String,
114
115 pub msd: u8,
116
117 #[serde(rename = "g")]
119 pub download_url: Option<Url>,
120
121 #[serde(flatten)]
123 pub unknown: HashMap<String, serde_json::Value>,
124}
125
126impl GetAttributes {
127 pub fn decode_attributes(&self, key: u128) -> Result<FileAttributes, DecodeAttributesError> {
129 decode_attributes(&self.encoded_attributes, key)
130 }
131}
132
133#[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 #[serde(flatten)]
146 pub unknown: HashMap<String, serde_json::Value>,
147}
148
149#[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 File = 0,
164
165 Directory = 1,
167
168 Root = 2,
170
171 Inbox = 3,
173
174 TrashBin = 4,
176}
177
178impl FetchNodesNodeKind {
179 pub fn is_file(self) -> bool {
181 matches!(self, Self::File)
182 }
183
184 pub fn is_dir(self) -> bool {
186 matches!(self, Self::Directory)
187 }
188
189 pub fn is_root(self) -> bool {
191 matches!(self, Self::Root)
192 }
193
194 pub fn is_inbox(self) -> bool {
196 matches!(self, Self::Inbox)
197 }
198
199 pub fn is_trash_bin(self) -> bool {
201 matches!(self, Self::TrashBin)
202 }
203}
204
205#[derive(Debug, serde::Serialize, serde:: Deserialize)]
207pub struct FetchNodesNode {
208 #[serde(rename = "a")]
210 pub encoded_attributes: String,
211
212 #[serde(rename = "h")]
214 pub id: String,
215
216 #[serde(rename = "k")]
220 pub key: String,
221
222 #[serde(rename = "p")]
224 pub parent_id: String,
225
226 #[serde(rename = "t")]
228 pub kind: FetchNodesNodeKind,
229
230 #[serde(rename = "ts")]
232 pub timestamp: u64,
233
234 #[serde(rename = "u")]
236 pub user: String,
237
238 pub fa: Option<String>,
239
240 #[serde(rename = "s")]
242 pub size: Option<u64>,
243
244 #[serde(flatten)]
246 pub unknown: HashMap<String, serde_json::Value>,
247}
248
249impl FetchNodesNode {
250 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 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 let file_key = FileKey::from_encoded_bytes(key.try_into().unwrap());
281 FileOrFolderKey::File(file_key)
282 };
283
284 Ok(key)
285 }
286
287 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 pub fn is_file(&self) -> bool {
298 self.kind.is_file()
299 }
300
301 pub fn is_dir(&self) -> bool {
303 self.kind.is_dir()
304 }
305}
306
307fn 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}