/*
* This file is part of Universal-Updater
* Copyright (C) 2019 VoltZ, Epicpkmn11, Flame, RocketRobz, TotallyNotGuy
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it.
* * Prohibiting misrepresentation of the origin of that material,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version.
*/
#include "gui.hpp"
#include "download/download.hpp"
#include "lang/lang.hpp"
#include "screens/screenCommon.hpp"
#include "utils/extract.hpp"
#include "utils/fileBrowse.h"
#include "utils/inifile.h"
#include "utils/thread.hpp"
#include
#include
#include
extern "C" {
#include "utils/cia.h"
}
#define USER_AGENT APP_TITLE "-" V_STRING
static char* result_buf = NULL;
static size_t result_sz = 0;
static size_t result_written = 0;
std::vector _topText;
std::string jsonName;
extern bool downloadNightlies;
extern int filesExtracted;
extern std::string extractingFile;
char progressBarMsg[128] = "";
bool showProgressBar = false;
bool progressBarType = 0; // 0 = Download | 1 = Extract
// following function is from
// https://github.com/angelsl/libctrfgh/blob/master/curl_test/src/main.c
static size_t handle_data(char* ptr, size_t size, size_t nmemb, void* userdata)
{
(void) userdata;
const size_t bsz = size*nmemb;
if (result_sz == 0 || !result_buf)
{
result_sz = 0x1000;
result_buf = (char*)malloc(result_sz);
}
bool need_realloc = false;
while (result_written + bsz > result_sz)
{
result_sz <<= 1;
need_realloc = true;
}
if (need_realloc)
{
char *new_buf = (char*)realloc(result_buf, result_sz);
if (!new_buf)
{
return 0;
}
result_buf = new_buf;
}
if (!result_buf)
{
return 0;
}
memcpy(result_buf + result_written, ptr, bsz);
result_written += bsz;
return bsz;
}
static Result setupContext(CURL *hnd, const char * url)
{
curl_easy_setopt(hnd, CURLOPT_BUFFERSIZE, 102400L);
curl_easy_setopt(hnd, CURLOPT_URL, url);
curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 1L);
curl_easy_setopt(hnd, CURLOPT_USERAGENT, USER_AGENT);
curl_easy_setopt(hnd, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 50L);
curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, (long)CURL_HTTP_VERSION_2TLS);
curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, handle_data);
curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(hnd, CURLOPT_STDERR, stdout);
return 0;
}
Result downloadToFile(std::string url, std::string path)
{
Result ret = 0;
printf("Downloading from:\n%s\nto:\n%s\n", url.c_str(), path.c_str());
void *socubuf = memalign(0x1000, 0x100000);
if (!socubuf)
{
return -1;
}
ret = socInit((u32*)socubuf, 0x100000);
if (R_FAILED(ret))
{
free(socubuf);
return ret;
}
CURL *hnd = curl_easy_init();
ret = setupContext(hnd, url.c_str());
if (ret != 0) {
socExit();
free(result_buf);
free(socubuf);
result_buf = NULL;
result_sz = 0;
result_written = 0;
return ret;
}
Handle fileHandle;
u64 offset = 0;
u32 bytesWritten = 0;
ret = openFile(&fileHandle, path.c_str(), true);
if (R_FAILED(ret)) {
printf("Error: couldn't open file to write.\n");
socExit();
free(result_buf);
free(socubuf);
result_buf = NULL;
result_sz = 0;
result_written = 0;
return DL_ERROR_WRITEFILE;
}
u64 startTime = osGetTime();
CURLcode cres = curl_easy_perform(hnd);
curl_easy_cleanup(hnd);
if (cres != CURLE_OK) {
printf("Error in:\ncurl\n");
socExit();
free(result_buf);
free(socubuf);
result_buf = NULL;
result_sz = 0;
result_written = 0;
return -1;
}
FSFILE_Write(fileHandle, &bytesWritten, offset, result_buf, result_written, 0);
u64 endTime = osGetTime();
u64 totalTime = endTime - startTime;
printf("Download took %llu milliseconds.\n", totalTime);
socExit();
free(result_buf);
free(socubuf);
result_buf = NULL;
result_sz = 0;
result_written = 0;
FSFILE_Close(fileHandle);
return 0;
}
Result downloadFromRelease(std::string url, std::string asset, std::string path)
{
Result ret = 0;
void *socubuf = memalign(0x1000, 0x100000);
if (!socubuf)
{
return -1;
}
ret = socInit((u32*)socubuf, 0x100000);
if (R_FAILED(ret))
{
free(socubuf);
return ret;
}
std::regex parseUrl("github\\.com\\/(.+)\\/(.+)");
std::smatch result;
regex_search(url, result, parseUrl);
std::string repoOwner = result[1].str(), repoName = result[2].str();
std::stringstream apiurlStream;
apiurlStream << "https://api.github.com/repos/" << repoOwner << "/" << repoName << "/releases/latest";
std::string apiurl = apiurlStream.str();
printf("Downloading latest release from repo:\n%s\nby:\n%s\n", repoName.c_str(), repoOwner.c_str());
printf("Crafted API url:\n%s\n", apiurl.c_str());
CURL *hnd = curl_easy_init();
ret = setupContext(hnd, apiurl.c_str());
if (ret != 0) {
socExit();
free(result_buf);
free(socubuf);
result_buf = NULL;
result_sz = 0;
result_written = 0;
return ret;
}
CURLcode cres = curl_easy_perform(hnd);
curl_easy_cleanup(hnd);
char* newbuf = (char*)realloc(result_buf, result_written + 1);
result_buf = newbuf;
result_buf[result_written] = 0; //nullbyte to end it as a proper C style string
if (cres != CURLE_OK) {
printf("Error in:\ncurl\n");
socExit();
free(result_buf);
free(socubuf);
result_buf = NULL;
result_sz = 0;
result_written = 0;
return -1;
}
printf("Looking for asset with matching name:\n%s\n", asset.c_str());
std::string assetUrl;
json parsedAPI = json::parse(result_buf);
if (parsedAPI["assets"].is_array()) {
for (auto jsonAsset : parsedAPI["assets"]) {
if (jsonAsset.is_object() && jsonAsset["name"].is_string() && jsonAsset["browser_download_url"].is_string()) {
std::string assetName = jsonAsset["name"];
if (matchPattern(asset, assetName)) {
assetUrl = jsonAsset["browser_download_url"];
break;
}
}
}
}
socExit();
free(result_buf);
free(socubuf);
result_buf = NULL;
result_sz = 0;
result_written = 0;
if (assetUrl.empty())
ret = DL_ERROR_GIT;
else
ret = downloadToFile(assetUrl, path);
return ret;
}
/**
* Check Wi-Fi status.
* @return True if Wi-Fi is connected; false if not.
*/
bool checkWifiStatus(void) {
u32 wifiStatus;
bool res = false;
if (R_SUCCEEDED(ACU_GetWifiStatus(&wifiStatus)) && wifiStatus) {
res = true;
}
return res;
}
void downloadFailed(void) {
DisplayMsg(Lang::get("DOWNLOAD_FAILED"));
for (int i = 0; i < 60*2; i++) {
gspWaitForVBlank();
}
}
void notImplemented(void) {
DisplayMsg(Lang::get("NOT_IMPLEMENTED"));
for (int i = 0; i < 60*2; i++) {
gspWaitForVBlank();
}
}
void doneMsg(void) {
DisplayMsg(Lang::get("DONE"));
for (int i = 0; i < 60*2; i++) {
gspWaitForVBlank();
}
}
void notConnectedMsg(void) {
DisplayMsg(Lang::get("CONNECT_WIFI"));
for (int i = 0; i < 60*2; i++) {
gspWaitForVBlank();
}
}
std::string getLatestRelease(std::string repo, std::string item)
{
Result ret = 0;
void *socubuf = memalign(0x1000, 0x100000);
if (!socubuf)
{
return "";
}
ret = socInit((u32*)socubuf, 0x100000);
if (R_FAILED(ret))
{
free(socubuf);
return "";
}
std::stringstream apiurlStream;
apiurlStream << "https://api.github.com/repos/" << repo << "/releases/latest";
std::string apiurl = apiurlStream.str();
CURL *hnd = curl_easy_init();
ret = setupContext(hnd, apiurl.c_str());
if (ret != 0) {
socExit();
free(result_buf);
free(socubuf);
result_buf = NULL;
result_sz = 0;
result_written = 0;
return "";
}
CURLcode cres = curl_easy_perform(hnd);
curl_easy_cleanup(hnd);
char* newbuf = (char*)realloc(result_buf, result_written + 1);
result_buf = newbuf;
result_buf[result_written] = 0; //nullbyte to end it as a proper C style string
if (cres != CURLE_OK) {
printf("Error in:\ncurl\n");
socExit();
free(result_buf);
free(socubuf);
result_buf = NULL;
result_sz = 0;
result_written = 0;
return "";
}
std::string jsonItem;
json parsedAPI = json::parse(result_buf);
if (parsedAPI[item].is_string()) {
jsonItem = parsedAPI[item];
}
socExit();
free(result_buf);
free(socubuf);
result_buf = NULL;
result_sz = 0;
result_written = 0;
return jsonItem;
}
std::string getLatestCommit(std::string repo, std::string item)
{
Result ret = 0;
void *socubuf = memalign(0x1000, 0x100000);
if (!socubuf)
{
return "";
}
ret = socInit((u32*)socubuf, 0x100000);
if (R_FAILED(ret))
{
free(socubuf);
return "";
}
std::stringstream apiurlStream;
apiurlStream << "https://api.github.com/repos/" << repo << "/commits/master";
std::string apiurl = apiurlStream.str();
CURL *hnd = curl_easy_init();
ret = setupContext(hnd, apiurl.c_str());
if (ret != 0) {
socExit();
free(result_buf);
free(socubuf);
result_buf = NULL;
result_sz = 0;
result_written = 0;
return "";
}
CURLcode cres = curl_easy_perform(hnd);
curl_easy_cleanup(hnd);
char* newbuf = (char*)realloc(result_buf, result_written + 1);
result_buf = newbuf;
result_buf[result_written] = 0; //nullbyte to end it as a proper C style string
if (cres != CURLE_OK) {
printf("Error in:\ncurl\n");
socExit();
free(result_buf);
free(socubuf);
result_buf = NULL;
result_sz = 0;
result_written = 0;
return "";
}
std::string jsonItem;
json parsedAPI = json::parse(result_buf);
if (parsedAPI[item].is_string()) {
jsonItem = parsedAPI[item];
}
socExit();
free(result_buf);
free(socubuf);
result_buf = NULL;
result_sz = 0;
result_written = 0;
return jsonItem;
}
std::string getLatestCommit(std::string repo, std::string array, std::string item)
{
Result ret = 0;
void *socubuf = memalign(0x1000, 0x100000);
if (!socubuf)
{
return "";
}
ret = socInit((u32*)socubuf, 0x100000);
if (R_FAILED(ret))
{
free(socubuf);
return "";
}
std::stringstream apiurlStream;
apiurlStream << "https://api.github.com/repos/" << repo << "/commits/master";
std::string apiurl = apiurlStream.str();
CURL *hnd = curl_easy_init();
ret = setupContext(hnd, apiurl.c_str());
if (ret != 0) {
socExit();
free(result_buf);
free(socubuf);
result_buf = NULL;
result_sz = 0;
result_written = 0;
return "";
}
CURLcode cres = curl_easy_perform(hnd);
curl_easy_cleanup(hnd);
char* newbuf = (char*)realloc(result_buf, result_written + 1);
result_buf = newbuf;
result_buf[result_written] = 0; //nullbyte to end it as a proper C style string
if (cres != CURLE_OK) {
printf("Error in:\ncurl\n");
socExit();
free(result_buf);
free(socubuf);
result_buf = NULL;
result_sz = 0;
result_written = 0;
return "";
}
std::string jsonItem;
json parsedAPI = json::parse(result_buf);
if (parsedAPI[array][item].is_string()) {
jsonItem = parsedAPI[array][item];
}
socExit();
free(result_buf);
free(socubuf);
result_buf = NULL;
result_sz = 0;
result_written = 0;
return jsonItem;
}
std::vector getThemeList(std::string repo, std::string path)
{
Result ret = 0;
void *socubuf = memalign(0x1000, 0x100000);
std::vector emptyVector;
if (!socubuf)
{
return emptyVector;
}
ret = socInit((u32*)socubuf, 0x100000);
if (R_FAILED(ret))
{
free(socubuf);
return emptyVector;
}
std::stringstream apiurlStream;
apiurlStream << "https://api.github.com/repos/" << repo << "/contents/" << path;
std::string apiurl = apiurlStream.str();
CURL *hnd = curl_easy_init();
ret = setupContext(hnd, apiurl.c_str());
if (ret != 0) {
socExit();
free(result_buf);
free(socubuf);
result_buf = NULL;
result_sz = 0;
result_written = 0;
return emptyVector;
}
CURLcode cres = curl_easy_perform(hnd);
curl_easy_cleanup(hnd);
char* newbuf = (char*)realloc(result_buf, result_written + 1);
result_buf = newbuf;
result_buf[result_written] = 0; //nullbyte to end it as a proper C style string
if (cres != CURLE_OK) {
printf("Error in:\ncurl\n");
socExit();
free(result_buf);
free(socubuf);
result_buf = NULL;
result_sz = 0;
result_written = 0;
return emptyVector;
}
std::vector jsonItems;
json parsedAPI = json::parse(result_buf);
for(uint i=0;i themeContents = getThemeList("Universal-Team/extras", path);
for(uint i=0;i