megabasterd/src/main/java/com/tonikelope/megabasterd/MegaCrypterAPI.java

330 lines
9.7 KiB
Java

package com.tonikelope.megabasterd;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import static com.tonikelope.megabasterd.CryptTools.*;
import static com.tonikelope.megabasterd.MiscTools.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.Cipher;
import javax.swing.JOptionPane;
/**
*
* @author tonikelope
*/
public class MegaCrypterAPI {
public static final Set<String> PASS_CACHE = new HashSet<>();
public static final Object PASS_LOCK = new Object();
private static final Logger LOG = Logger.getLogger(MegaCrypterAPI.class.getName());
private static String _rawRequest(String request, URL url_api) throws MegaCrypterAPIException {
String response = null;
HttpURLConnection con = null;
try {
if (MainPanel.isUse_proxy()) {
con = (HttpURLConnection) url_api.openConnection(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(MainPanel.getProxy_host(), MainPanel.getProxy_port())));
if (MainPanel.getProxy_user() != null && !"".equals(MainPanel.getProxy_user())) {
con.setRequestProperty("Proxy-Authorization", "Basic " + MiscTools.Bin2BASE64((MainPanel.getProxy_user() + ":" + MainPanel.getProxy_pass()).getBytes("UTF-8")));
}
} else {
con = (HttpURLConnection) url_api.openConnection();
}
con.setRequestProperty("Content-type", "application/json");
con.setUseCaches(false);
con.setRequestProperty("User-Agent", MainPanel.DEFAULT_USER_AGENT);
con.setRequestMethod("POST");
con.setDoOutput(true);
con.getOutputStream().write(request.getBytes("UTF-8"));
con.getOutputStream().close();
if (con.getResponseCode() != 200) {
Logger.getLogger(MegaCrypterAPI.class.getName()).log(Level.INFO, "{0} Failed : HTTP error code : {1}", new Object[]{Thread.currentThread().getName(), con.getResponseCode()});
} else {
try ( InputStream is = con.getInputStream(); ByteArrayOutputStream byte_res = new ByteArrayOutputStream()) {
byte[] buffer = new byte[MainPanel.DEFAULT_BYTE_BUFFER_SIZE];
int reads;
while ((reads = is.read(buffer)) != -1) {
byte_res.write(buffer, 0, reads);
}
response = new String(byte_res.toByteArray(), "UTF-8");
if (response.length() > 0) {
int mc_error;
if ((mc_error = MegaCrypterAPI.checkMCError(response)) != 0) {
throw new MegaCrypterAPIException(mc_error);
}
}
}
}
} catch (IOException ex) {
Logger.getLogger(MegaCrypterAPI.class.getName()).log(Level.SEVERE, ex.getMessage());
} finally {
if (con != null) {
con.disconnect();
}
}
return response;
}
public static String getMegaFileDownloadUrl(String link, String pass_hash, String noexpire_token, String sid, String reverse) throws IOException, MegaCrypterAPIException {
String request = "{\"m\":\"dl\", \"link\": \"" + link + "\"" + (noexpire_token != null ? ", \"noexpire\": \"" + noexpire_token + "\"" : "") + (sid != null ? ", \"sid\": \"" + sid + "\"" : "") + (reverse != null ? ", \"reverse\": \"" + reverse + "\"" : "") + "}";
URL url_api = new URL(findFirstRegex("https?://[^/]+", link, 0) + "/api");
String res = MegaCrypterAPI._rawRequest(request, url_api);
ObjectMapper objectMapper = new ObjectMapper();
HashMap res_map = objectMapper.readValue(res, HashMap.class);
String dl_url = (String) res_map.get("url");
if (pass_hash != null) {
try {
String pass = (String) res_map.get("pass");
byte[] decrypted_url = aes_cbc_decrypt_pkcs7(BASE642Bin(dl_url), BASE642Bin(pass_hash), BASE642Bin(pass));
dl_url = new String(decrypted_url, "UTF-8");
} catch (Exception ex) {
throw new MegaCrypterAPIException(25);
}
}
if (dl_url == null || "".equals(dl_url)) {
throw new MegaCrypterAPIException(-101);
}
return dl_url;
}
public static String[] getMegaFileMetadata(String link, MainPanelView panel, String reverse) throws MegaCrypterAPIException, MalformedURLException, IOException {
String request = "{\"m\":\"info\", \"link\": \"" + link + "\"" + (reverse != null ? ", \"reverse\": \"" + reverse + "\"" : "") + "}";
URL url_api = new URL(findFirstRegex("https?://[^/]+", link, 0) + "/api");
String res = MegaCrypterAPI._rawRequest(request, url_api);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
objectMapper.configure(JsonParser.Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true);
HashMap res_map = objectMapper.readValue(res, HashMap.class);
String fname = cleanFilename((String) res_map.get("name"));
String fpath = null;
Object fpath_val = res_map.get("path");
if (fpath_val instanceof Boolean) {
fpath = null;
} else if (fpath_val instanceof String) {
fpath = cleanFilePath((String) fpath_val);
}
String file_size;
try {
file_size = String.valueOf(res_map.get("size"));
} catch (java.lang.ClassCastException ex) {
file_size = String.valueOf(res_map.get("size"));
}
String fkey = (String) res_map.get("key");
String noexpire_token = null;
Object expire_val = res_map.get("expire");
if (expire_val instanceof Boolean) {
noexpire_token = null;
} else if (expire_val instanceof String) {
String aux[] = ((String) expire_val).split("#");
noexpire_token = aux[1];
}
Object pass_val = res_map.get("pass");
String pass = null;
if (pass_val instanceof Boolean) {
pass = null;
} else if (pass_val instanceof String) {
pass = (String) pass_val;
}
if (pass != null) {
String[] pass_items = pass.split("#");
if (pass_items.length != 4) {
throw new MegaCrypterAPIException(25);
}
int iterations = Integer.parseInt(pass_items[0]);
byte[] key_check = BASE642Bin(pass_items[1]);
byte[] salt = BASE642Bin(pass_items[2]);
byte[] iv = BASE642Bin(pass_items[3]);
String password;
byte[] info_key = null;
boolean bad_pass;
Cipher decrypter;
synchronized (PASS_LOCK) {
LinkedList<String> pass_list = new LinkedList(PASS_CACHE);
do {
bad_pass = true;
if ((password = pass_list.poll()) == null) {
password = JOptionPane.showInputDialog(panel, "Enter password for MegaCrypter link:");
}
if (password != null) {
try {
info_key = PBKDF2HMACSHA256(password, salt, (int) Math.pow(2, iterations), 256);
decrypter = genDecrypter("AES", "AES/CBC/PKCS5Padding", info_key, iv);
bad_pass = !Arrays.equals(info_key, decrypter.doFinal(key_check));
if (!bad_pass) {
PASS_CACHE.add(password);
}
} catch (Exception ex) {
bad_pass = true;
}
}
} while (password != null && bad_pass);
}
if (bad_pass) {
throw new MegaCrypterAPIException(25);
} else {
try {
decrypter = genDecrypter("AES", "AES/CBC/PKCS5Padding", info_key, iv);
byte[] decrypted_key = decrypter.doFinal(BASE642Bin(fkey));
fkey = Bin2UrlBASE64(decrypted_key);
decrypter = genDecrypter("AES", "AES/CBC/PKCS5Padding", info_key, iv);
byte[] decrypted_name = decrypter.doFinal(BASE642Bin(fname));
fname = new String(decrypted_name, "UTF-8");
if (fpath != null) {
byte[] decrypted_fpath = decrypter.doFinal(BASE642Bin(fpath));
fpath = new String(decrypted_fpath, "UTF-8");
}
pass = Bin2BASE64(info_key);
} catch (Exception ex) {
throw new MegaCrypterAPIException(25);
}
}
}
if (fpath != null) {
fname = fpath + fname;
}
String file_data[] = {fname, file_size, fkey, pass, noexpire_token};
return file_data;
}
private static int checkMCError(String data) {
String error = findFirstRegex("\"error\" *: *([0-9-]+)", data, 1);
return error != null ? Integer.parseInt(error) : 0;
}
private MegaCrypterAPI() {
}
}