├── README.md └── src ├── MegaCrypt.java ├── MegaFile.java └── MegaHandler.java /README.md: -------------------------------------------------------------------------------- 1 | # With the [official mega sdk](https://github.com/meganz/sdk) this project is DEPRECATED. 2 | 3 | # MegaJava 4 | 5 | Java library for the [mega.co.nz](https://mega.co.nz) API, currently supporting: 6 | - login 7 | - downloading 8 | - list directory & files (also if shared from others) 9 | - get download link 10 | - add contacts 11 | - get space left 12 | 13 | This work is based on the source code released by [@NT2005](https://github.com/NT2005). 14 | 15 | ## Requirements 16 | [Java-json](http://json.org/java/) 17 | 18 | ## How to use 19 | Import MegaJava and json library on your project 20 | 21 | ### Login (you need this step before do anything) 22 | ```java 23 | MegaHandler mh = new MegaHandler("user@mail.com", "password"); 24 | mh.login(); 25 | ``` 26 | ### Get user details 27 | ```java 28 | mh.get_user() 29 | ``` 30 | ### Get and print user files 31 | ```java 32 | ArrayList mf = mh.get_files(); 33 | for(int i = 0;i 1 && IA[str.charAt(--i)] <= 0;) 279 | if (str.charAt(i) == '=') 280 | pad++; 281 | 282 | int len = ((sLen - sepCnt) * 6 >> 3) - pad; 283 | 284 | byte[] dArr = new byte[len]; // Preallocate byte[] of exact length 285 | 286 | for (int s = 0, d = 0; d < len;) { 287 | // Assemble three bytes into an int from four "valid" characters. 288 | int i = 0; 289 | for (int j = 0; j < 4; j++) { // j only increased if a valid char was found. 290 | int c = IA[str.charAt(s++)]; 291 | if (c >= 0) 292 | i |= c << (18 - j * 6); 293 | else 294 | j--; 295 | } 296 | // Add the bytes 297 | dArr[d++] = (byte) (i >> 16); 298 | if (d < len) { 299 | dArr[d++]= (byte) (i >> 8); 300 | if (d < len) 301 | dArr[d++] = (byte) i; 302 | } 303 | } 304 | return dArr; 305 | } 306 | 307 | public final static byte[] base64_url_encode_byte(byte[] sArr, boolean lineSep) 308 | { 309 | // Check special case 310 | int sLen = sArr != null ? sArr.length : 0; 311 | if (sLen == 0) 312 | return new byte[0]; 313 | 314 | int eLen = (sLen / 3) * 3; // Length of even 24-bits. 315 | int cCnt = ((sLen - 1) / 3 + 1) << 2; // Returned character count 316 | int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array 317 | byte[] dArr = new byte[dLen]; 318 | 319 | // Encode even 24-bits 320 | for (int s = 0, d = 0, cc = 0; s < eLen;) { 321 | // Copy next three bytes into lower 24 bits of int, paying attension to sign. 322 | int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff); 323 | 324 | // Encode the int into four chars 325 | dArr[d++] = (byte) CA[(i >>> 18) & 0x3f]; 326 | dArr[d++] = (byte) CA[(i >>> 12) & 0x3f]; 327 | dArr[d++] = (byte) CA[(i >>> 6) & 0x3f]; 328 | dArr[d++] = (byte) CA[i & 0x3f]; 329 | 330 | // Add optional line separator 331 | if (lineSep && ++cc == 19 && d < dLen - 2) { 332 | dArr[d++] = '\r'; 333 | dArr[d++] = '\n'; 334 | cc = 0; 335 | } 336 | } 337 | 338 | // Pad and encode last bits if source isn't an even 24 bits. 339 | int left = sLen - eLen; // 0 - 2. 340 | if (left > 0) { 341 | // Prepare the int 342 | int i = ((sArr[eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0); 343 | 344 | // Set last four chars 345 | dArr[dLen - 4] = (byte) CA[i >> 12]; 346 | dArr[dLen - 3] = (byte) CA[(i >>> 6) & 0x3f]; 347 | dArr[dLen - 2] = left == 2 ? (byte) CA[i & 0x3f] : (byte) '='; 348 | dArr[dLen - 1] = '='; 349 | } 350 | return dArr; 351 | } 352 | 353 | public static String encodeHexString(String s) throws IOException { 354 | return DatatypeConverter.printHexBinary(s.getBytes("ISO-8859-1")); 355 | } 356 | 357 | public static byte[] decodeHexString(String s) { 358 | return DatatypeConverter.parseHexBinary(s); 359 | } 360 | 361 | public static void print(Object o) { 362 | System.out.println(o); 363 | } 364 | } 365 | 366 | 367 | -------------------------------------------------------------------------------- /src/MegaFile.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2013 Ale46. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the GNU Public License v3.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.gnu.org/licenses/gpl.html 7 | * 8 | * Contributors: 9 | * @NT2005 - initial API and implementation 10 | ******************************************************************************/ 11 | public class MegaFile { 12 | 13 | private String uid, name, h; 14 | private long[] key; 15 | private boolean isDir = false; 16 | 17 | public String getName() { 18 | if (name == null) return "NO NAME"; else return name; 19 | //return name; 20 | } 21 | 22 | public void setKey(long[] k){ 23 | key = k; 24 | } 25 | 26 | public long[] getKey(){ 27 | return key; 28 | } 29 | 30 | public void setHandle(String h){ 31 | this.h = h; 32 | } 33 | 34 | public String getHandle(){ 35 | return h; 36 | } 37 | 38 | public void setName(String name) { 39 | 40 | this.name = name; 41 | } 42 | 43 | public String getUID() { 44 | return uid; 45 | } 46 | 47 | public void setUID(String uid) { 48 | this.uid = uid; 49 | } 50 | 51 | public void setAttributes(String attributes) { 52 | 53 | if (attributes.contains("MEGA")){ 54 | 55 | this.name = attributes.substring(10,attributes.lastIndexOf("\"")); 56 | 57 | }else 58 | 59 | this.name = attributes; 60 | } 61 | 62 | public void setDirectory(boolean d){ 63 | isDir = d; 64 | } 65 | 66 | public boolean isDirectory(){ 67 | return isDir; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/MegaHandler.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2013 Ale46. 3 | * All rights reserved. This program and the accompanying materials 4 | * are made available under the terms of the GNU Public License v3.0 5 | * which accompanies this distribution, and is available at 6 | * http://www.gnu.org/licenses/gpl.html 7 | * 8 | * Contributors: 9 | * @NT2005 - initial API and implementation 10 | ******************************************************************************/ 11 | import org.json.JSONArray; 12 | import org.json.JSONException; 13 | import org.json.JSONObject; 14 | 15 | import javax.crypto.BadPaddingException; 16 | import javax.crypto.Cipher; 17 | import javax.crypto.CipherOutputStream; 18 | import javax.crypto.IllegalBlockSizeException; 19 | import javax.crypto.NoSuchPaddingException; 20 | import javax.crypto.spec.IvParameterSpec; 21 | import javax.crypto.spec.SecretKeySpec; 22 | 23 | import java.io.*; 24 | 25 | 26 | import java.math.BigInteger; 27 | import java.net.HttpURLConnection; 28 | import java.net.URL; 29 | import java.net.URLConnection; 30 | import java.security.InvalidAlgorithmParameterException; 31 | import java.security.InvalidKeyException; 32 | import java.security.KeyFactory; 33 | import java.security.NoSuchAlgorithmException; 34 | import java.security.PrivateKey; 35 | import java.security.spec.RSAPrivateKeySpec; 36 | import java.util.ArrayList; 37 | import java.util.HashMap; 38 | import java.util.Random; 39 | 40 | 41 | 42 | 43 | public class MegaHandler { 44 | 45 | private String email, password, sid; 46 | private int sequence_number; 47 | private long[] master_key; 48 | private BigInteger[] rsa_private_key; 49 | private long[] password_aes; 50 | HashMap user_keys = new HashMap(); 51 | 52 | public MegaHandler(String email, String password) { 53 | this.email = email; 54 | this.password = password; 55 | Random rg = new Random(); 56 | sequence_number = rg.nextInt(Integer.MAX_VALUE); 57 | } 58 | 59 | public int login() throws IOException { 60 | 61 | password_aes = MegaCrypt.prepare_key_pw(password); 62 | String uh = MegaCrypt.stringhash(email, password_aes); 63 | 64 | JSONObject json = new JSONObject(); 65 | try { 66 | json.put("a", "us"); 67 | json.put("user", email); 68 | json.put("uh", uh); 69 | } catch (JSONException e) { 70 | e.printStackTrace(); 71 | } 72 | 73 | while (true) { 74 | String response = api_request(json.toString()); 75 | 76 | if (isInteger(response)) 77 | return Integer.parseInt(response); 78 | 79 | try { 80 | if (login_process(new JSONObject(response), password_aes) != -2) { 81 | break; 82 | } 83 | } catch (JSONException e) { 84 | e.printStackTrace(); 85 | } 86 | } 87 | 88 | return 0; 89 | } 90 | 91 | private int login_process(JSONObject json, long[] password_aes) throws IOException { 92 | 93 | String master_key_b64 = null; 94 | try { 95 | master_key_b64 = json.getString("k"); 96 | } catch (JSONException e) { 97 | e.printStackTrace(); 98 | } 99 | if (master_key_b64 == null || master_key_b64.isEmpty()) 100 | return -1; 101 | 102 | long[] encrypted_master_key = MegaCrypt.base64_to_a32(master_key_b64); 103 | master_key = MegaCrypt.decrypt_key(encrypted_master_key, password_aes); 104 | 105 | if (json.has("csid")) { 106 | String encrypted_rsa_private_key_b64 = null; 107 | try { 108 | encrypted_rsa_private_key_b64 = json.getString("privk"); 109 | } catch (JSONException e) { 110 | e.printStackTrace(); 111 | } 112 | 113 | long[] encrypted_rsa_private_key = MegaCrypt.base64_to_a32(encrypted_rsa_private_key_b64); 114 | long[] rsa_private_key = MegaCrypt.decrypt_key(encrypted_rsa_private_key, master_key); 115 | String private_key = MegaCrypt.a32_to_str(rsa_private_key); 116 | 117 | this.rsa_private_key = new BigInteger[4]; 118 | for (int i = 0; i < 4; i++) { 119 | int l = ((((int) private_key.charAt(0)) * 256 + ((int) private_key.charAt(1)) + 7) / 8) + 2; 120 | this.rsa_private_key[i] = MegaCrypt.mpi_to_int(private_key.substring(0, l)); 121 | private_key = private_key.substring(l); 122 | } 123 | 124 | BigInteger encrypted_sid = null; 125 | try { 126 | encrypted_sid = MegaCrypt.mpi_to_int(MegaCrypt.base64_url_decode(json.getString("csid"))); 127 | } catch (JSONException e) { 128 | e.printStackTrace(); 129 | } 130 | 131 | BigInteger modulus = this.rsa_private_key[0].multiply(this.rsa_private_key[1]); 132 | BigInteger privateExponent = this.rsa_private_key[2]; 133 | 134 | BigInteger sid = null; 135 | try { 136 | PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(new RSAPrivateKeySpec(modulus, privateExponent)); 137 | Cipher cipher = Cipher.getInstance("RSA/ECB/NoPadding"); 138 | cipher.init(Cipher.DECRYPT_MODE, privateKey); 139 | // PyCrypt can handle >256 bit length... what the fuck... sometimes i get 257 140 | if (encrypted_sid.toByteArray().length > 256) { 141 | Random rg = new Random(); 142 | sequence_number = rg.nextInt(Integer.MAX_VALUE); 143 | return -2; // lets get a new seession 144 | } 145 | sid = new BigInteger(cipher.doFinal(encrypted_sid.toByteArray())); 146 | } catch (Exception e) { 147 | e.printStackTrace(); 148 | return -1; 149 | } 150 | 151 | String sidS = sid.toString(16); 152 | if (sidS.length() % 2 != 0) 153 | sidS = "0" + sidS; 154 | try { 155 | byte[] sidsnohex = MegaCrypt.decodeHexString(sidS); 156 | this.sid = MegaCrypt.base64_url_encode(new String(sidsnohex, "ISO-8859-1").substring(0, 43)); 157 | } catch (Exception e) { 158 | e.printStackTrace(); 159 | return -1; 160 | } 161 | } 162 | return 0; 163 | } 164 | 165 | public String add_user(String email) { 166 | JSONObject json = new JSONObject(); 167 | try { 168 | json.put("a", "ur"); 169 | json.put("u", email); 170 | json.put("l", 1); 171 | } catch (JSONException e) { 172 | e.printStackTrace(); 173 | } 174 | return api_request(json.toString()); 175 | } 176 | 177 | public long get_quota() throws JSONException { 178 | JSONObject json = new JSONObject(); 179 | try { 180 | json.put("a", "uq"); 181 | json.put("xfer", 1); 182 | 183 | } catch (JSONException e) { 184 | e.printStackTrace(); 185 | } 186 | 187 | return new JSONObject(api_request(json.toString())).getLong("mstrg"); 188 | } 189 | 190 | public String get_user() { 191 | JSONObject json = new JSONObject(); 192 | try { 193 | json.put("a", "ug"); 194 | } catch (JSONException e) { 195 | e.printStackTrace(); 196 | } 197 | return api_request(json.toString()); 198 | } 199 | 200 | 201 | public ArrayList get_files() throws UnsupportedEncodingException { 202 | JSONObject json = new JSONObject(); 203 | try { 204 | json.put("a", "f"); 205 | json.put("c", "1"); 206 | 207 | } catch (JSONException e) { 208 | e.printStackTrace(); 209 | } 210 | 211 | String files = api_request(json.toString()); 212 | // TODO check for negativ error 213 | //print(json.toString()); 214 | ArrayList megaFiles = new ArrayList(); 215 | 216 | JSONArray array = null; 217 | try { 218 | json = new JSONObject(files); 219 | array = json.getJSONArray("f"); 220 | for (int i = 0; i < array.length(); i++) { 221 | //print(array.get(i).toString()); 222 | megaFiles.add(process_file(new JSONObject(array.get(i).toString()))); 223 | 224 | } 225 | } catch (JSONException e) { 226 | e.printStackTrace(); 227 | return null; 228 | } 229 | return megaFiles; 230 | } 231 | 232 | 233 | private MegaFile process_file(JSONObject jsonFile) throws UnsupportedEncodingException { 234 | 235 | MegaFile file = new MegaFile(); 236 | try { 237 | 238 | if (jsonFile.getInt("t") < 2) { 239 | 240 | String key = ""; 241 | String uid = jsonFile.getString("u"); 242 | String h =(jsonFile.getString("h")); 243 | file.setUID(uid); 244 | file.setHandle(h); 245 | //print (h); 246 | if (jsonFile.getString("k").contains("/")){ 247 | String[] keys = jsonFile.getString("k").split("/"); 248 | int start = keys[0].indexOf(":")+1; 249 | key = keys[0].substring(start); 250 | 251 | } 252 | 253 | String attributes = MegaCrypt.base64_url_decode(jsonFile.getString("a")); 254 | 255 | long[] k = new long[4]; 256 | if (!key.isEmpty()){ 257 | long[] keys_a32 = MegaCrypt.decrypt_key(MegaCrypt.base64_to_a32(key), master_key); 258 | if (jsonFile.getInt("t") == 0) { 259 | 260 | k[0] = keys_a32[0] ^ keys_a32[4]; 261 | k[1] = keys_a32[1] ^ keys_a32[5]; 262 | k[2] = keys_a32[2] ^ keys_a32[6]; 263 | k[3] = keys_a32[3] ^ keys_a32[7]; 264 | 265 | 266 | } else { 267 | k[0] = keys_a32[0]; 268 | k[1] = keys_a32[1]; 269 | k[2] = keys_a32[2]; 270 | k[3] = keys_a32[3]; 271 | file.setDirectory(true); 272 | 273 | } 274 | 275 | file.setKey(k); 276 | file.setAttributes(MegaCrypt.decrypt_attr(attributes, k)); 277 | }else if(!jsonFile.isNull("su") && !jsonFile.isNull("sk") && jsonFile.getString("k").contains(":")){ 278 | long[] keyS; 279 | 280 | user_keys.put(jsonFile.getString("u"), MegaCrypt.decrypt_key(MegaCrypt.base64_to_a32(jsonFile.getString("sk")), master_key)); 281 | //print("ShareKey->"+jsonFile.getString("sk")); 282 | int dd1 = jsonFile.getString("k").indexOf(':'); 283 | String sk = jsonFile.getString("k").substring(dd1 + 1); 284 | 285 | keyS = MegaCrypt.decrypt_key(MegaCrypt.base64_to_a32(sk) ,user_keys.get(jsonFile.getString("u"))); 286 | if (jsonFile.getInt("t") == 0){ 287 | long[] keys_a32S = keyS; 288 | k[0] = keys_a32S[0] ^ keys_a32S[4]; 289 | k[1] = keys_a32S[1] ^ keys_a32S[5]; 290 | k[2] = keys_a32S[2] ^ keys_a32S[6]; 291 | k[3] = keys_a32S[3] ^ keys_a32S[7]; 292 | }else{ 293 | k = keyS; 294 | file.setDirectory(true); 295 | } 296 | 297 | file.setKey(k); 298 | file.setAttributes(MegaCrypt.decrypt_attr(attributes, k)); 299 | 300 | }else if (!jsonFile.isNull("u") && jsonFile.getString("k").contains(":") && user_keys.containsKey(jsonFile.getString("u"))) { 301 | 302 | int dd1 = jsonFile.getString("k").indexOf(':'); 303 | String sk = jsonFile.getString("k").substring(dd1 + 1); 304 | //print(user_keys.get(jsonFile.getString("u"))); 305 | long[] keyS = MegaCrypt.decrypt_key(MegaCrypt.base64_to_a32(sk) ,user_keys.get(jsonFile.getString("u"))); 306 | if (jsonFile.getInt("t") == 0){ 307 | long[] keys_a32S = keyS; 308 | k[0] = keys_a32S[0] ^ keys_a32S[4]; 309 | k[1] = keys_a32S[1] ^ keys_a32S[5]; 310 | k[2] = keys_a32S[2] ^ keys_a32S[6]; 311 | k[3] = keys_a32S[3] ^ keys_a32S[7]; 312 | }else{ 313 | k = keyS; 314 | file.setDirectory(true); 315 | } 316 | 317 | file.setKey(k); 318 | file.setAttributes(MegaCrypt.decrypt_attr(attributes, k)); 319 | 320 | }else if (!jsonFile.isNull("k")){ 321 | int dd1 = jsonFile.getString("k").indexOf(':'); 322 | key = jsonFile.getString("k").substring(dd1 + 1); 323 | long[] keys_a32S = MegaCrypt.decrypt_key(MegaCrypt.base64_to_a32(key), master_key); 324 | if (jsonFile.getInt("t") == 0){ 325 | 326 | k[0] = keys_a32S[0] ^ keys_a32S[4]; 327 | k[1] = keys_a32S[1] ^ keys_a32S[5]; 328 | k[2] = keys_a32S[2] ^ keys_a32S[6]; 329 | k[3] = keys_a32S[3] ^ keys_a32S[7]; 330 | file.setDirectory(true); 331 | 332 | 333 | }/*else{ 334 | k = keys_a32S; 335 | 336 | file.setDirectory(true); 337 | 338 | }*/ 339 | file.setKey(k); 340 | 341 | file.setAttributes(MegaCrypt.decrypt_attr(attributes, k)); 342 | }else{ 343 | file.setAttributes(jsonFile.toString()); 344 | } 345 | 346 | } else if (jsonFile.getInt("t") == 2) { 347 | file.setName("Cloud Drive"); 348 | } else if (jsonFile.getInt("t") == 3) { 349 | file.setName("Cloud Inbox"); 350 | } else if (jsonFile.getInt("t") == 4) { 351 | file.setName("Rubbish Bin"); 352 | } else { 353 | file.setName(jsonFile.toString()); 354 | } 355 | return file; 356 | } catch (JSONException e) { 357 | e.printStackTrace(); 358 | } 359 | 360 | //file.setAttributes(jsonFile.toString()); 361 | return file; 362 | } 363 | 364 | public String get_url(MegaFile f){ 365 | 366 | if ( f.getHandle() == null || f.getKey() == null) 367 | return "Error"; 368 | JSONObject json = new JSONObject(); 369 | try { 370 | json.put("a", "l"); 371 | json.put("n", f.getHandle()); 372 | 373 | } catch (JSONException e) { 374 | e.printStackTrace(); 375 | } 376 | 377 | String public_handle = api_request(json.toString()); 378 | if (public_handle.equals("-11")) 379 | return "Shared file, no public url"; 380 | return "https://mega.co.nz/#!"+public_handle.substring(1, public_handle.length()-1)+"!"+MegaCrypt.a32_to_base64(f.getKey()); 381 | 382 | } 383 | 384 | private String api_request(String data) { 385 | HttpURLConnection connection = null; 386 | try { 387 | String urlString = "https://g.api.mega.co.nz/cs?id=" + sequence_number; 388 | if (sid != null) 389 | urlString += "&sid=" + sid; 390 | 391 | URL url = new URL(urlString); 392 | connection = (HttpURLConnection) url.openConnection(); 393 | connection.setRequestMethod("POST"); //use post method 394 | connection.setDoOutput(true); //we will send stuff 395 | connection.setDoInput(true); //we want feedback 396 | connection.setUseCaches(false); //no caches 397 | connection.setAllowUserInteraction(false); 398 | connection.setRequestProperty("Content-Type", "text/xml"); 399 | 400 | OutputStream out = connection.getOutputStream(); 401 | try { 402 | OutputStreamWriter wr = new OutputStreamWriter(out); 403 | wr.write("[" + data + "]"); //data is JSON object containing the api commands 404 | wr.flush(); 405 | wr.close(); 406 | } catch (IOException e) { 407 | e.printStackTrace(); 408 | } finally { //in this case, we are ensured to close the output stream 409 | if (out != null) 410 | out.close(); 411 | } 412 | 413 | InputStream in = connection.getInputStream(); 414 | StringBuffer response = new StringBuffer(); 415 | try { 416 | BufferedReader rd = new BufferedReader(new InputStreamReader(in)); 417 | String line = ""; 418 | while ((line = rd.readLine()) != null) { 419 | response.append(line); 420 | } 421 | rd.close(); //close the reader 422 | } catch (IOException e) { 423 | e.printStackTrace(); 424 | } finally { //in this case, we are ensured to close the input stream 425 | if (in != null) 426 | in.close(); 427 | } 428 | 429 | return response.toString().substring(1, response.toString().length() - 1); 430 | 431 | 432 | } catch (IOException e) { 433 | e.printStackTrace(); 434 | } 435 | 436 | return ""; 437 | } 438 | 439 | public static boolean isInteger(String string) { 440 | if (string == null || string.isEmpty()) { 441 | return false; 442 | } 443 | int length = string.length(); 444 | int i = 0; 445 | if (string.charAt(i) == '[') { 446 | if (length == 1) 447 | return false; 448 | i++; 449 | } 450 | if (string.charAt(i) == '-') { 451 | if (length == 1 + i) 452 | return false; 453 | i++; 454 | } 455 | for (; i < length; i++) { 456 | char c = string.charAt(i); 457 | if (c <= '/' || c >= ':') { 458 | return false; 459 | } 460 | } 461 | return true; 462 | } 463 | 464 | 465 | 466 | public void download(String url) throws IOException, NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IllegalBlockSizeException, JSONException, BadPaddingException, InvalidKeyException { 467 | download(url, new File(".").getCanonicalPath(), false); 468 | } 469 | 470 | public void download(String url, String path) throws NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException, JSONException, BadPaddingException, IllegalBlockSizeException, InvalidKeyException { 471 | download(url, path, false); 472 | } 473 | 474 | public void download_verbose(String url, String path) throws NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException, JSONException, BadPaddingException, IllegalBlockSizeException, InvalidKeyException { 475 | download(url, path, true); 476 | } 477 | 478 | public void download_verbose(String url) throws NoSuchPaddingException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException, JSONException, BadPaddingException, IllegalBlockSizeException, InvalidKeyException { 479 | download(url, new File(".").getCanonicalPath(), true); 480 | } 481 | 482 | 483 | private void download(String url, String path, boolean verbose) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IOException, IllegalBlockSizeException, BadPaddingException, JSONException { 484 | //TODO DOWNLOAD mismatch? 485 | print("Download started"); 486 | String[] s = url.split("!"); 487 | String file_id = s[1]; 488 | byte[] file_key = MegaCrypt.base64_url_decode_byte(s[2]); 489 | 490 | int[] intKey = MegaCrypt.aByte_to_aInt(file_key); 491 | JSONObject json = new JSONObject(); 492 | try { 493 | json.put("a", "g"); 494 | json.put("g", "1"); 495 | json.put("p", file_id); 496 | } catch (JSONException e) { 497 | e.printStackTrace(); 498 | } 499 | 500 | JSONObject file_data = new JSONObject(api_request(json.toString())); 501 | //print(file_data); 502 | int[] keyNOnce = new int[] { intKey[0] ^ intKey[4], intKey[1] ^ intKey[5], intKey[2] ^ intKey[6], intKey[3] ^ intKey[7], intKey[4], intKey[5] }; 503 | byte[] key = MegaCrypt.aInt_to_aByte(keyNOnce[0], keyNOnce[1], keyNOnce[2], keyNOnce[3]); 504 | 505 | int[] iiv = new int[] { keyNOnce[4], keyNOnce[5], 0, 0 }; 506 | byte[] iv = MegaCrypt.aInt_to_aByte(iiv); 507 | 508 | @SuppressWarnings("unused") 509 | int file_size = file_data.getInt("s"); 510 | String attribs = (file_data.getString("at")); 511 | 512 | attribs = new String(MegaCrypt.aes_cbc_decrypt(MegaCrypt.base64_url_decode_byte(attribs), key)); 513 | //print(attribs.substring(4, attribs.length())); 514 | 515 | String file_name = new JSONObject(attribs.substring(4, attribs.length())).getString("n"); 516 | //print("Filename->>" +file_name); 517 | final IvParameterSpec ivSpec = new IvParameterSpec(iv); 518 | final SecretKeySpec skeySpec = new SecretKeySpec(key, "AES"); 519 | Cipher cipher = Cipher.getInstance("AES/CTR/nopadding"); 520 | cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivSpec); 521 | InputStream is = null; 522 | String file_url = null; 523 | try { 524 | file_url = file_data.getString("g"); 525 | } catch (JSONException e) { 526 | e.printStackTrace(); 527 | } 528 | 529 | FileOutputStream fos = new FileOutputStream(path + File.separator + file_name); 530 | final OutputStream cos = new CipherOutputStream(fos, cipher); 531 | final Cipher decipher = Cipher.getInstance("AES/CTR/NoPadding"); 532 | decipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivSpec); 533 | int read = 0; 534 | final byte[] buffer = new byte[32767]; 535 | try { 536 | 537 | URLConnection urlConn = new URL(file_url).openConnection(); 538 | 539 | ProgressBar bar = new ProgressBar(); 540 | //print(file_url); 541 | if(verbose) bar.update(0, file_size, ""); 542 | //print("FILESIZE:" +file_size); 543 | is = urlConn.getInputStream(); 544 | long mDownloaded = 0; 545 | double current_speed; 546 | long startTime = System.nanoTime(); 547 | final double NANOS_PER_SECOND = 1000000000.0; 548 | final double BYTES_PER_MIB = 1024 * 1024; 549 | while ((read = is.read(buffer,0, 1024)) > 0) { 550 | cos.write(buffer, 0, read); 551 | mDownloaded += read; 552 | //print(mDownloaded); 553 | long timeInSecs = (System.nanoTime() - startTime + 1); 554 | //print("Debug:" + mDownloaded + "/" + timeInSecs); 555 | current_speed = NANOS_PER_SECOND / BYTES_PER_MIB * mDownloaded / (timeInSecs); 556 | //print("Speed: "+ (current_speed) + " Mbps"); 557 | if(verbose) bar.update(mDownloaded, file_size, String.format("%.2f", current_speed) + " Mbps"); 558 | } 559 | } finally { 560 | try { 561 | cos.close(); 562 | if (is != null) { 563 | is.close(); 564 | } 565 | } finally { 566 | if (fos != null) { 567 | fos.close(); 568 | } 569 | } 570 | } 571 | print("Download finished"); 572 | } 573 | 574 | 575 | 576 | public static void print(Object o) { 577 | System.out.println(o); 578 | } 579 | class ProgressBar { 580 | private StringBuilder progress; 581 | 582 | /** 583 | * initialize progress bar properties. 584 | */ 585 | public ProgressBar() { 586 | init(); 587 | } 588 | 589 | /** 590 | * called whenever the progress bar needs to be updated. 591 | * that is whenever progress was made. 592 | * 593 | * @param done an int representing the work done so far 594 | * @param total an int representing the total work 595 | */ 596 | public void update(double done, double total, String append) { 597 | char[] workchars = {'|', '/', '-', '\\'}; 598 | String format = "\r%3d%% %s %c %s"; 599 | 600 | int percent = (int)((++done * 100) / total); 601 | int extrachars = (percent / 2) - this.progress.length(); 602 | 603 | while (extrachars-- > 0) { 604 | progress.append("#"); 605 | } 606 | 607 | System.out.printf(format, percent, progress, workchars[(int)done % workchars.length], append); 608 | 609 | if (done >= total) { 610 | System.out.flush(); 611 | System.out.println(); 612 | init(); 613 | } 614 | } 615 | 616 | private void init() { 617 | this.progress = new StringBuilder(60); 618 | } 619 | } 620 | } 621 | --------------------------------------------------------------------------------