mirror of
https://github.com/HamletDuFromage/aio-switch-updater.git
synced 2024-12-28 02:16:03 +00:00
Download firmwares from Mega (#187)
* Add libmegadl * Allow downloads from Mega * Keep compat with older app versions * Add fallback for archive.org link format * Revert "Add libmegadl" This reverts commitddee32ba66
. * Revert "Keep compat with older app versions" This reverts commit11fe274dc6
. * Revert "Add fallback for archive.org link format" This reverts commit74dfaeab42
.
This commit is contained in:
parent
378ad7a401
commit
0b905a1d81
1 changed files with 176 additions and 5 deletions
|
@ -4,6 +4,7 @@
|
|||
#include <math.h>
|
||||
#include <switch.h>
|
||||
#include <time.h>
|
||||
#include <mbedtls/base64.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
|
@ -41,6 +42,7 @@ namespace download {
|
|||
size_t data_size;
|
||||
u_int64_t offset;
|
||||
FILE* out;
|
||||
Aes128CtrContext* aes;
|
||||
} ntwrk_struct_t;
|
||||
|
||||
static size_t WriteMemoryCallback(void* contents, size_t size, size_t num_files, void* userp)
|
||||
|
@ -56,7 +58,11 @@ namespace download {
|
|||
data_struct->offset = 0;
|
||||
}
|
||||
|
||||
memcpy(&data_struct->data[data_struct->offset], contents, realsize);
|
||||
if(data_struct->aes)
|
||||
aes128CtrCrypt(data_struct->aes, &data_struct->data[data_struct->offset], contents, realsize);
|
||||
else
|
||||
memcpy(&data_struct->data[data_struct->offset], contents, realsize);
|
||||
|
||||
data_struct->offset += realsize;
|
||||
data_struct->data[data_struct->offset] = 0;
|
||||
return realsize;
|
||||
|
@ -107,7 +113,7 @@ namespace download {
|
|||
}
|
||||
|
||||
bool checkSize(CURL* curl, const std::string& url)
|
||||
{
|
||||
{
|
||||
curl_off_t dl;
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, API_AGENT);
|
||||
|
@ -125,6 +131,159 @@ namespace download {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string mega_id(std::string url)
|
||||
{
|
||||
auto len = url.length();
|
||||
|
||||
if(len < 52)
|
||||
throw std::invalid_argument("Invalid URL.");
|
||||
|
||||
bool old_link = url.find("#!") < len;
|
||||
|
||||
/* Start from the last '/' and add 2 characters if old link format starting with '#!' */
|
||||
auto init_pos = url.find_last_of('/') + (old_link ? 2 : 0) + 1;
|
||||
|
||||
/* End with the last '#' or "!" if its the old link format */
|
||||
auto end_pos = url.find_last_of(old_link ? '!' : '#');
|
||||
|
||||
/* Finally crop the url */
|
||||
std::string id = url.substr(init_pos, end_pos - init_pos);
|
||||
|
||||
if(id.length() != 8)
|
||||
throw std::invalid_argument("Invalid URL ID.");
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
std::string mega_url(std::string url)
|
||||
{
|
||||
std::string id = mega_id(url);
|
||||
|
||||
json request = json::array({
|
||||
{
|
||||
{"a", "g"},
|
||||
{"g", 1},
|
||||
{"p", id},
|
||||
}
|
||||
});
|
||||
|
||||
std::string body = request.dump();
|
||||
std::string output;
|
||||
|
||||
auto curl = curl_easy_init();
|
||||
curl_easy_setopt(curl, CURLOPT_URL, "https://g.api.mega.co.nz/cs");
|
||||
curl_easy_setopt(curl, CURLOPT_POST, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &output);
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, API_AGENT);
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
|
||||
curl_easy_setopt(
|
||||
curl,
|
||||
CURLOPT_WRITEFUNCTION,
|
||||
+[](void *buffer, size_t size, size_t nmemb, void *userp) -> size_t
|
||||
{
|
||||
std::string* output = reinterpret_cast<std::string*>(userp);
|
||||
size_t actual_size = size * nmemb;
|
||||
output->append(reinterpret_cast<char*>(buffer), actual_size);
|
||||
return actual_size;
|
||||
}
|
||||
);
|
||||
|
||||
curl_easy_perform(curl);
|
||||
|
||||
json response = json::parse(output);
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
|
||||
s64 freeStorage;
|
||||
s64 fileSize = response[0]["s"];
|
||||
if(R_SUCCEEDED(fs::getFreeStorageSD(freeStorage)) && fileSize * 1.1 > freeStorage)
|
||||
return "";
|
||||
|
||||
return response[0]["g"];
|
||||
}
|
||||
|
||||
std::string mega_node_key(std::string url)
|
||||
{
|
||||
/* Check if old link format */
|
||||
auto len = url.length();
|
||||
|
||||
if(len < 52)
|
||||
throw std::invalid_argument("Invalid URL.");
|
||||
|
||||
bool old_link = url.find("#!") < len;
|
||||
|
||||
/* End with the last '#' or "!" if its the old link format */
|
||||
auto end_pos = url.find_last_of(old_link ? '!' : '#') + 1;
|
||||
|
||||
/* Crop the URL to get the file key */
|
||||
std::string key = url.substr(end_pos, len - end_pos);
|
||||
|
||||
/* Replace URL characters with B64 characters */
|
||||
std::replace(key.begin(), key.end(), '_', '/');
|
||||
std::replace(key.begin(), key.end(), '-', '+');
|
||||
|
||||
/* Add padding */
|
||||
auto key_len = key.length();
|
||||
unsigned pad = 4 - key_len%4;
|
||||
key.append(pad, '=');
|
||||
|
||||
/* The encoded key should have 44 characters to produce a 32 byte node key */
|
||||
if(key.length() != 44)
|
||||
throw std::invalid_argument("Invalid URL key.");
|
||||
|
||||
std::string decoded(key.size()*3/4, 0);
|
||||
size_t olen = 0;
|
||||
|
||||
mbedtls_base64_decode(
|
||||
reinterpret_cast<unsigned char*>(decoded.data()),
|
||||
decoded.size(),
|
||||
&olen,
|
||||
reinterpret_cast<const unsigned char*>(key.c_str()),
|
||||
key.size()
|
||||
);
|
||||
|
||||
/**
|
||||
* The encoded base64 is (usually?) 43 characters long. With padding it goes
|
||||
* to 44. When we calculate the decoded size, we need to allocate 33 bytes.
|
||||
* But the last encoded character is padding, and combined with the last
|
||||
* valid character, it should produce a 32 byte node key.
|
||||
*/
|
||||
decoded.resize(olen);
|
||||
if(decoded.size() != 32)
|
||||
throw std::invalid_argument("Invalid node key.");
|
||||
|
||||
return decoded;
|
||||
}
|
||||
|
||||
std::string mega_key(const std::string &node_key)
|
||||
{
|
||||
std::string key(16, 0);
|
||||
|
||||
reinterpret_cast<uint64_t*>(key.data())[0] =
|
||||
reinterpret_cast<const uint64_t*>(node_key.data())[0] ^
|
||||
reinterpret_cast<const uint64_t*>(node_key.data())[2]
|
||||
;
|
||||
reinterpret_cast<uint64_t*>(key.data())[1] =
|
||||
reinterpret_cast<const uint64_t*>(node_key.data())[1] ^
|
||||
reinterpret_cast<const uint64_t*>(node_key.data())[3]
|
||||
;
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
std::string mega_iv(const std::string &node_key)
|
||||
{
|
||||
std::string iv(16, 0);
|
||||
reinterpret_cast<uint64_t*>(iv.data())[0] =
|
||||
reinterpret_cast<const uint64_t*>(node_key.data())[2]
|
||||
;
|
||||
|
||||
return iv;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
long downloadFile(const std::string& url, const std::string& output, int api)
|
||||
|
@ -142,6 +301,17 @@ namespace download {
|
|||
time_old = std::chrono::steady_clock::now();
|
||||
dlold = 0.0f;
|
||||
bool can_download = true;
|
||||
bool is_mega = (url.find("mega.nz") != std::string::npos);
|
||||
std::string real_url = is_mega ? mega_url(url) : url;
|
||||
|
||||
if (is_mega) {
|
||||
std::string node_key = mega_node_key(url);
|
||||
std::string key = mega_key(node_key);
|
||||
std::string iv = mega_iv(node_key);
|
||||
|
||||
chunk.aes = static_cast<Aes128CtrContext*>(malloc(sizeof(Aes128CtrContext)));
|
||||
aes128CtrContextCreate(chunk.aes, key.c_str(), iv.c_str());
|
||||
}
|
||||
|
||||
if (curl) {
|
||||
FILE* fp = fopen(out, "wb");
|
||||
|
@ -151,11 +321,11 @@ namespace download {
|
|||
chunk.out = fp;
|
||||
|
||||
if (*out != 0) {
|
||||
can_download = checkSize(curl, url);
|
||||
can_download = is_mega ? !real_url.empty() : checkSize(curl, url);
|
||||
}
|
||||
|
||||
if (can_download) {
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_URL, real_url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, API_AGENT);
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||
|
@ -171,7 +341,7 @@ namespace download {
|
|||
curl_easy_perform(curl);
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status_code);
|
||||
|
||||
if (fp && chunk.offset && can_download)
|
||||
if (fp && chunk.offset && can_download)
|
||||
fwrite(chunk.data, 1, chunk.offset, fp);
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
|
@ -193,6 +363,7 @@ namespace download {
|
|||
}
|
||||
|
||||
free(chunk.data);
|
||||
free(chunk.aes);
|
||||
|
||||
return status_code;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue