From a8305b55b43a14c1951f349c967db94283b3cffd Mon Sep 17 00:00:00 2001 From: Maschell Date: Sun, 11 Mar 2018 20:49:40 +0100 Subject: [PATCH] Added a FileDownloader, bases on the one from loadiine --- Makefile | 3 + source/network/FileDownloader.cpp | 185 ++++++++++++++++++++++++++++++ source/network/FileDownloader.h | 46 ++++++++ 3 files changed, 234 insertions(+) create mode 100644 source/network/FileDownloader.cpp create mode 100644 source/network/FileDownloader.h diff --git a/Makefile b/Makefile index 963ebee..e60d301 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,7 @@ SOURCES := source \ source/fs \ source/utils \ source/language \ + source/network \ source/system \ source/kernel INCLUDES := include \ @@ -106,12 +107,14 @@ install: @mkdir -p $(INCLUDEDIR)/utils/ @mkdir -p $(INCLUDEDIR)/kernel/ @mkdir -p $(INCLUDEDIR)/system/ + @mkdir -p $(INCLUDEDIR)/network/ @mkdir -p $(INCLUDEDIR)/language/ @mkdir -p $(INCLUDEDIR)/fs/ @cp source/utils/*.h $(INCLUDEDIR)/utils/ @cp source/utils/*.hpp $(INCLUDEDIR)/utils/ @cp source/system/*.h $(INCLUDEDIR)/system/ @cp source/language/*.h $(INCLUDEDIR)/language/ + @cp source/network/*.h $(INCLUDEDIR)/network/ @cp source/fs/*.h $(INCLUDEDIR)/fs/ @cp source/fs/*.hpp $(INCLUDEDIR)/fs/ @cp source/kernel/*.h $(INCLUDEDIR)/kernel/ diff --git a/source/network/FileDownloader.cpp b/source/network/FileDownloader.cpp new file mode 100644 index 0000000..c179628 --- /dev/null +++ b/source/network/FileDownloader.cpp @@ -0,0 +1,185 @@ +/**************************************************************************** + * Copyright (C) 2016 Dimok + * Modified by Maschell, 2018 + * + * 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 . + ****************************************************************************/ +#include +#include +#include "FileDownloader.h" +#include +#include "utils/logger.h" +#include "utils/utils.h" + + +bool FileDownloader::getFile(const std::string & downloadUrl, std::string & fileBuffer, ProgressCallback callback, void *arg) { + curl_private_data_t private_data; + private_data.progressCallback = callback; + private_data.progressArg = arg; + private_data.buffer = 0; + private_data.filesize = 0; + private_data.file = 0; + + bool result = internalGetFile(downloadUrl, &private_data); + + if(private_data.filesize > 0 && private_data.buffer) { + fileBuffer.resize(private_data.filesize); + memcpy(&fileBuffer[0], private_data.buffer, private_data.filesize); + } + + if(private_data.buffer) { + free(private_data.buffer); + } + + return result; +} + +bool FileDownloader::getFile(const std::string & downloadUrl, const std::string & outputPath, ProgressCallback callback, void *arg) { + curl_private_data_t private_data; + private_data.progressCallback = callback; + private_data.progressArg = arg; + private_data.buffer = 0; + private_data.filesize = 0; + + s32 res = open(outputPath.c_str(), O_CREAT | O_TRUNC | O_WRONLY); + close(res); + + private_data.file = new CFile(outputPath.c_str(), CFile::WriteOnly); + + if(!private_data.file->isOpen()) { + delete private_data.file; + DEBUG_FUNCTION_LINE("Can not write to file %s\n", outputPath.c_str()); + return false; + } + + bool result = internalGetFile(downloadUrl, &private_data); + + private_data.file->close(); + delete private_data.file; + return result; +} + +bool FileDownloader::internalGetFile(const std::string & downloadUrl, curl_private_data_t * private_data) { + bool result = false; + int resp = 404; + int ret = -1; + CURL * curl = n_curl_easy_init(); + if(!curl) { + return false; + } + + int ssl_context = -1; + + std::string prefix = "https"; + if(strncmp(downloadUrl.c_str(), prefix.c_str(), prefix.size()) == 0) { + DEBUG_FUNCTION_LINE("Needs a SSL context\n"); + ssl_context = NSSLCreateContext(0); + if(ssl_context < 0) { + DEBUG_FUNCTION_LINE("Failed to create SSL Context\n"); + goto exit_error; + } + + // Add all existing certs + for(int i = 100; i<106; i++) { + NSSLAddServerPKI(ssl_context,i); + } + + for(int i = 1001; i<1034; i++) { + NSSLAddServerPKI(ssl_context,i); + } + n_curl_easy_setopt(curl, CURLOPT_GSSAPI_DELEGATION, ssl_context); // Is CURLOPT_NSSL_CONTEXT on the Wii U + } + + n_curl_easy_setopt(curl, CURLOPT_URL, downloadUrl.c_str()); + n_curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, FileDownloader::curlCallback); + n_curl_easy_setopt(curl, CURLOPT_WRITEDATA, private_data); + n_curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + + //n_curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf); + + if(private_data->progressCallback) { + n_curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, FileDownloader::curlProgressCallback); + n_curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, private_data); + n_curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); + } + + ret = n_curl_easy_perform(curl); + if(ret) { + DEBUG_FUNCTION_LINE("n_curl_easy_perform ret %i\n", ret); + goto exit_error; + } + + if(!private_data->filesize) { + DEBUG_FUNCTION_LINE("file length is 0\n"); + goto exit_error; + } + + n_curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &resp); + + if(resp != 200) { + DEBUG_FUNCTION_LINE("response != 200\n"); + goto exit_error; + } + + result = true; + +exit_error: + if(ssl_context >= 0) { + NSSLDestroyContext(ssl_context); + } + + n_curl_easy_cleanup(curl); + return result; +} + +int FileDownloader::curlCallback(void *buffer, int size, int nmemb, void *userp) { + curl_private_data_t *private_data = (curl_private_data_t *)userp; + int read_len = size*nmemb; + + if(private_data->file) { + int res = private_data->file->write((u8*)buffer, read_len); + private_data->filesize += res; + return res; + } else { + if(!private_data->buffer) { + private_data->buffer = (u8*) malloc(read_len); + } else { + u8 *tmp = (u8*) realloc(private_data->buffer, private_data->filesize + read_len); + if(!tmp) { + free(private_data->buffer); + private_data->buffer = NULL; + } else { + private_data->buffer = tmp; + } + } + + if(!private_data->buffer) { + private_data->filesize = 0; + return -1; + } + + memcpy(private_data->buffer + private_data->filesize, buffer, read_len); + private_data->filesize += read_len; + return read_len; + } +} + +int FileDownloader::curlProgressCallback(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow) { + curl_private_data_t *private_data = (curl_private_data_t *)clientp; + if(private_data->progressCallback) { + private_data->progressCallback(private_data->progressArg, (u32)dlnow, (u32)dltotal); + } + return 0; +} + diff --git a/source/network/FileDownloader.h b/source/network/FileDownloader.h new file mode 100644 index 0000000..ab897f9 --- /dev/null +++ b/source/network/FileDownloader.h @@ -0,0 +1,46 @@ +/**************************************************************************** + * Copyright (C) 2016 Dimok + * Modified by Maschell, 2018 + * + * 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 . + ****************************************************************************/ +#ifndef _FILE_DOWNLOADER_H +#define _FILE_DOWNLOADER_H + +#include +#include "fs/CFile.hpp" + +class FileDownloader +{ +public: + typedef void (*ProgressCallback)(void *arg, u32 done, u32 total); + + static bool getFile(const std::string & downloadUrl, std::string & fileBuffer, ProgressCallback callback = 0, void *arg = 0); + static bool getFile(const std::string & downloadUrl, const std::string & outputPath, ProgressCallback callback = 0, void *arg = 0); +private: + typedef struct + { + ProgressCallback progressCallback; + void *progressArg; + CFile * file; + u8 *buffer; + u32 filesize; + } curl_private_data_t; + + static bool internalGetFile(const std::string & downloadUrl, curl_private_data_t * private_data); + static int curlCallback(void *buffer, int size, int nmemb, void *userp); + static int curlProgressCallback(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow); +}; + +#endif // _FILE_DOWNLOADER_H