#include "DownloadUtils.h" #include "../common.h" #include "FSUtils.h" #include "logger.h" #include #include #define IO_BUFSIZE (128 * 1024) // 128 KB bool DownloadUtils::libInitDone = false; uint8_t *DownloadUtils::cacert_pem = nullptr; uint32_t DownloadUtils::cacert_pem_size = 0; static int initSocket(void *ptr, curl_socket_t socket, curlsocktype type) { int o = 1; // Activate WinScale int r = setsockopt(socket, SOL_SOCKET, SO_WINSCALE, &o, sizeof(o)); if (r != 0) { DEBUG_FUNCTION_LINE_ERR("initSocket: Error setting WinScale: %d", r); return CURL_SOCKOPT_ERROR; } o = IO_BUFSIZE; // Set receive buffersize r = setsockopt(socket, SOL_SOCKET, SO_RCVBUF, &o, sizeof(o)); if (r != 0) { DEBUG_FUNCTION_LINE_ERR("initSocket: Error setting RBS: %d", r); return CURL_SOCKOPT_ERROR; } return CURL_SOCKOPT_OK; } size_t writeCallback(char *buf, size_t size, size_t nmemb, void *up) { auto *data = (std::string *) up; data->append(buf, size * nmemb); return size * nmemb; //tell curl how many bytes we handled } int progress_callback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { if (clientp != nullptr) { auto *progress = (float *) clientp; if (dltotal > 0 && dlnow > 0) { *progress = (float) dlnow / (float) dltotal; } else { *progress = 0.0f; } OSMemoryBarrier(); } return 0; } bool DownloadUtils::Init() { if (libInitDone) { return true; } CURLcode res = curl_global_init(CURL_GLOBAL_DEFAULT); if (res != CURLE_OK) { return false; } if (LoadFileToMem(CERT_FILE_LOCATION, &cacert_pem, &cacert_pem_size) < 0) { DEBUG_FUNCTION_LINE_ERR("Failed to load cert"); cacert_pem = nullptr; cacert_pem_size = 0; } libInitDone = true; return true; } void DownloadUtils::Deinit() { if (!libInitDone) { return; } free(cacert_pem); cacert_pem = nullptr; cacert_pem_size = 0; curl_global_cleanup(); libInitDone = false; } int DownloadUtils::DownloadFileToBuffer(const char *url, std::string &outBuffer, int &responseCodeOut, float *progress) { if (!libInitDone) { return -1; } // Start a curl session CURL *curl = curl_easy_init(); if (!curl) { curl_global_cleanup(); return -1; } if (cacert_pem != nullptr) { struct curl_blob blob {}; blob.data = (void *) cacert_pem; blob.len = cacert_pem_size; blob.flags = CURL_BLOB_COPY; // Use the certificate bundle in the data curl_easy_setopt(curl, CURLOPT_CAINFO_BLOB, &blob); } else { DEBUG_FUNCTION_LINE_ERR("Warning, missing certificate."); return -4; } // Enable optimizations curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, initSocket); // Follow redirects curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // Set the download URL curl_easy_setopt(curl, CURLOPT_URL, url); if (progress != nullptr) { /* pass struct to callback */ curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, progress); curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progress_callback); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); } curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &outBuffer); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &writeCallback); if (curl_easy_perform(curl) != CURLE_OK) { curl_easy_cleanup(curl); curl_global_cleanup(); return -1; } int32_t response_code; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); responseCodeOut = response_code; curl_easy_cleanup(curl); return 0; }