2016-07-11 13:09:54 -07:00

348 lines
14 KiB
C

#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <3ds.h>
#include "section.h"
#include "task/task.h"
#include "../error.h"
#include "../info.h"
#include "../prompt.h"
#include "../ui.h"
#include "../../core/screen.h"
#include "../../core/util.h"
#include "../../json/json.h"
#define URL_MAX 1024
typedef struct {
char url[URL_MAX];
u32 responseCode;
data_op_data installInfo;
} update_data;
static Result update_is_src_directory(void* data, u32 index, bool* isDirectory) {
*isDirectory = false;
return 0;
}
static Result update_make_dst_directory(void* data, u32 index) {
return 0;
}
static Result update_open_src(void* data, u32 index, u32* handle) {
update_data* updateData = (update_data*) data;
Result res = 0;
httpcContext* context = (httpcContext*) calloc(1, sizeof(httpcContext));
if(context != NULL) {
if(R_SUCCEEDED(res = httpcOpenContext(context, HTTPC_METHOD_GET, updateData->url, 1))) {
if(R_SUCCEEDED(res = httpcSetSSLOpt(context, SSLCOPT_DisableVerify)) && R_SUCCEEDED(res = httpcBeginRequest(context)) && R_SUCCEEDED(res = httpcGetResponseStatusCode(context, &updateData->responseCode, 0))) {
if(updateData->responseCode == 200) {
*handle = (u32) context;
} else if(updateData->responseCode == 301 || updateData->responseCode == 302 || updateData->responseCode == 303) {
if(R_SUCCEEDED(res = httpcGetResponseHeader(context, "Location", updateData->url, URL_MAX))) {
httpcCloseContext(context);
free(context);
return update_open_src(data, index, handle);
}
} else {
res = R_FBI_HTTP_RESPONSE_CODE;
}
}
if(R_FAILED(res)) {
httpcCloseContext(context);
}
}
if(R_FAILED(res)) {
free(context);
}
} else {
res = R_FBI_OUT_OF_MEMORY;
}
return res;
}
static Result update_close_src(void* data, u32 index, bool succeeded, u32 handle) {
return httpcCloseContext((httpcContext*) handle);
}
static Result update_get_src_size(void* data, u32 handle, u64* size) {
u32 downloadSize = 0;
Result res = httpcGetDownloadSizeState((httpcContext*) handle, NULL, &downloadSize);
*size = downloadSize;
return res;
}
static Result update_read_src(void* data, u32 handle, u32* bytesRead, void* buffer, u64 offset, u32 size) {
Result res = httpcDownloadData((httpcContext*) handle, buffer, size, bytesRead);
return res != HTTPC_RESULTCODE_DOWNLOADPENDING ? res : 0;
}
static Result update_open_dst(void* data, u32 index, void* initialReadBlock, u64 size, u32* handle) {
if(util_get_3dsx_path() != NULL) {
FS_Path* path = util_make_path_utf8(util_get_3dsx_path());
if(path != NULL) {
Result res = FSUSER_OpenFileDirectly(handle, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), *path, FS_OPEN_WRITE | FS_OPEN_CREATE, 0);
util_free_path_utf8(path);
return res;
} else {
return R_FBI_OUT_OF_MEMORY;
}
} else {
return AM_StartCiaInstall(MEDIATYPE_SD, handle);
}
}
static Result update_close_dst(void* data, u32 index, bool succeeded, u32 handle) {
if(util_get_3dsx_path() != NULL) {
return FSFILE_Close(handle);
} else {
if(succeeded) {
return AM_FinishCiaInstall(handle);
} else {
return AM_CancelCIAInstall(handle);
}
}
}
static Result update_write_dst(void* data, u32 handle, u32* bytesWritten, void* buffer, u64 offset, u32 size) {
return FSFILE_Write(handle, bytesWritten, offset, buffer, size, 0);
}
static Result update_suspend_copy(void* data, u32 index, u32* srcHandle, u32* dstHandle) {
return 0;
}
static Result update_restore_copy(void* data, u32 index, u32* srcHandle, u32* dstHandle) {
return 0;
}
static Result update_suspend(void* data, u32 index) {
return 0;
}
static Result update_restore(void* data, u32 index) {
return 0;
}
static bool update_error(void* data, u32 index, Result res) {
update_data* updateData = (update_data*) data;
if(res == R_FBI_CANCELLED) {
prompt_display("Failure", "Install cancelled.", COLOR_TEXT, false, NULL, NULL, NULL);
} else if(res == R_FBI_HTTP_RESPONSE_CODE) {
error_display(NULL, NULL, "Failed to update FBI.\nHTTP server returned response code %d", updateData->responseCode);
} else {
error_display_res(NULL, NULL, res, "Failed to update FBI.");
}
return false;
}
static void update_install_update(ui_view* view, void* data, float* progress, char* text) {
update_data* updateData = (update_data*) data;
if(updateData->installInfo.finished) {
ui_pop();
info_destroy(view);
if(R_SUCCEEDED(updateData->installInfo.result)) {
prompt_display("Success", "Update complete.", COLOR_TEXT, false, NULL, NULL, NULL);
}
free(updateData);
return;
}
if(hidKeysDown() & KEY_B) {
svcSignalEvent(updateData->installInfo.cancelEvent);
}
*progress = updateData->installInfo.currTotal != 0 ? (float) ((double) updateData->installInfo.currProcessed / (double) updateData->installInfo.currTotal) : 0;
snprintf(text, PROGRESS_TEXT_MAX, "%.2f %s / %.2f %s", util_get_display_size(updateData->installInfo.currProcessed), util_get_display_size_units(updateData->installInfo.currProcessed), util_get_display_size(updateData->installInfo.currTotal), util_get_display_size_units(updateData->installInfo.currTotal));
}
static void update_check_update(ui_view* view, void* data, float* progress, char* text) {
update_data* updateData = (update_data*) data;
bool hasUpdate = false;
Result res = 0;
u32 responseCode = 0;
httpcContext context;
if(R_SUCCEEDED(res = httpcOpenContext(&context, HTTPC_METHOD_GET, "https://api.github.com/repos/Steveice10/FBI/releases/latest", 1))) {
if(R_SUCCEEDED(res = httpcSetSSLOpt(&context, SSLCOPT_DisableVerify))
&& R_SUCCEEDED(res = httpcAddRequestHeaderField(&context, "User-Agent", "FBI"))
&& R_SUCCEEDED(res = httpcBeginRequest(&context))
&& R_SUCCEEDED(res = httpcGetResponseStatusCode(&context, &responseCode, 0))) {
if(responseCode == 200) {
u32 size = 0;
if(R_SUCCEEDED(res = httpcGetDownloadSizeState(&context, NULL, &size))) {
char* text = (char*) calloc(sizeof(char), size);
if(text != NULL) {
u32 bytesRead = 0;
if(R_SUCCEEDED(res = httpcDownloadData(&context, (u8*) text, size, &bytesRead))) {
json_value* json = json_parse(text, size);
if(json != NULL) {
if(json->type == json_object) {
json_value* name = NULL;
json_value* assets = NULL;
for(u32 i = 0; i < json->u.object.length; i++) {
json_value* val = json->u.object.values[i].value;
if(strncmp(json->u.object.values[i].name, "name", json->u.object.values[i].name_length) == 0 && val->type == json_string) {
name = val;
} else if(strncmp(json->u.object.values[i].name, "assets", json->u.object.values[i].name_length) == 0 && val->type == json_array) {
assets = val;
}
}
if(name != NULL && assets != NULL) {
char versionString[16];
snprintf(versionString, sizeof(versionString), "%d.%d.%d", VERSION_MAJOR, VERSION_MINOR, VERSION_MICRO);
if(strncmp(name->u.string.ptr, versionString, name->u.string.length) != 0) {
char* url = NULL;
for(u32 i = 0; i < assets->u.array.length; i++) {
json_value* val = assets->u.array.values[i];
if(val->type == json_object) {
json_value* assetName = NULL;
json_value* assetUrl = NULL;
for(u32 j = 0; j < val->u.object.length; j++) {
json_value* subVal = val->u.object.values[j].value;
if(strncmp(val->u.object.values[j].name, "name", val->u.object.values[j].name_length) == 0 && subVal->type == json_string) {
assetName = subVal;
} else if(strncmp(val->u.object.values[j].name, "browser_download_url", val->u.object.values[j].name_length) == 0 && subVal->type == json_string) {
assetUrl = subVal;
}
}
if(assetName != NULL && assetUrl != NULL) {
if(strncmp(assetName->u.string.ptr, util_get_3dsx_path() != NULL ? "FBI.3dsx" : "FBI.cia", assetName->u.string.length) == 0) {
url = assetUrl->u.string.ptr;
break;
}
}
}
}
if(url != NULL) {
strncpy(updateData->url, url, URL_MAX);
hasUpdate = true;
} else {
res = R_FBI_BAD_DATA;
}
}
} else {
res = R_FBI_BAD_DATA;
}
} else {
res = R_FBI_BAD_DATA;
}
} else {
res = R_FBI_PARSE_FAILED;
}
}
} else {
res = R_FBI_OUT_OF_MEMORY;
}
}
} else {
res = R_FBI_HTTP_RESPONSE_CODE;
}
}
httpcCloseContext(&context);
}
ui_pop();
info_destroy(view);
if(hasUpdate) {
if(R_SUCCEEDED(res = task_data_op(&updateData->installInfo))) {
info_display("Updating FBI", "Press B to cancel.", true, data, update_install_update, NULL);
} else {
error_display_res(NULL, NULL, res, "Failed to begin update.");
}
} else {
if(R_FAILED(res)) {
if(res == R_FBI_HTTP_RESPONSE_CODE) {
error_display(NULL, NULL, "Failed to check for update.\nHTTP server returned response code %d", responseCode);
} else {
error_display_res(NULL, NULL, res, "Failed to check for update.");
}
} else {
prompt_display("Success", "No updates available.", COLOR_TEXT, false, NULL, NULL, NULL);
}
free(data);
}
}
static void update_onresponse(ui_view* view, void* data, bool response) {
if(response) {
info_display("Checking For Updates", "", false, data, update_check_update, NULL);
} else {
free(data);
}
}
void update_open() {
update_data* data = (update_data*) calloc(1, sizeof(update_data));
if(data == NULL) {
error_display(NULL, NULL, "Failed to allocate update check data.");
return;
}
data->responseCode = 0;
data->installInfo.data = data;
data->installInfo.op = DATAOP_COPY;
data->installInfo.copyBufferSize = 256 * 1024;
data->installInfo.copyEmpty = false;
data->installInfo.total = 1;
data->installInfo.isSrcDirectory = update_is_src_directory;
data->installInfo.makeDstDirectory = update_make_dst_directory;
data->installInfo.openSrc = update_open_src;
data->installInfo.closeSrc = update_close_src;
data->installInfo.getSrcSize = update_get_src_size;
data->installInfo.readSrc = update_read_src;
data->installInfo.openDst = update_open_dst;
data->installInfo.closeDst = update_close_dst;
data->installInfo.writeDst = update_write_dst;
data->installInfo.suspendCopy = update_suspend_copy;
data->installInfo.restoreCopy = update_restore_copy;
data->installInfo.suspend = update_suspend;
data->installInfo.restore = update_restore;
data->installInfo.error = update_error;
data->installInfo.finished = true;
prompt_display("Confirmation", "Check for FBI updates?", COLOR_TEXT, true, data, NULL, update_onresponse);
}