diff --git a/include/Helpers/FileDownloader.cpp b/include/Helpers/FileDownloader.cpp index e4034d9..edf5568 100644 --- a/include/Helpers/FileDownloader.cpp +++ b/include/Helpers/FileDownloader.cpp @@ -1,7 +1,7 @@ -#ifndef __EMSCRIPTEN__ - #include "FileDownloader.hpp" +#ifndef __EMSCRIPTEN__ + #include #include #include @@ -18,18 +18,23 @@ void FileDownloader::init() { shutdownThread = false; } -std::shared_ptr FileDownloader::download_data_from_url(std::string_view url) { +std::shared_ptr FileDownloader::download_data_from_url(const std::string& url) { DownloadHandler h; auto downloadData = std::make_shared(); h.data = downloadData; h.eHandle = curl_easy_init(); + downloadData->fileName = get_potential_filename_from_url(url); + curl_easy_setopt(h.eHandle, CURLOPT_URL, url.data()); curl_easy_setopt(h.eHandle, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(h.eHandle, CURLOPT_FOLLOWLOCATION, CURLFOLLOW_ALL); - curl_easy_setopt(h.eHandle, CURLOPT_USERAGENT, "InfiniPaint/1.0"); + curl_easy_setopt(h.eHandle, CURLOPT_USERAGENT, "InfiniPaint/0.2.0"); curl_easy_setopt(h.eHandle, CURLOPT_NOPROGRESS, 0); curl_easy_setopt(h.eHandle, CURLOPT_XFERINFODATA, h.data.get()); curl_easy_setopt(h.eHandle, CURLOPT_XFERINFOFUNCTION, download_progress_callback); + curl_easy_setopt(h.eHandle, CURLOPT_HEADERFUNCTION, header_callback); + curl_easy_setopt(h.eHandle, CURLOPT_HEADERDATA, h.data.get()); + disable_ssl_verification(h.eHandle); save_to_string(h.eHandle, &h.data->str); @@ -131,6 +136,16 @@ int FileDownloader::download_progress_callback(void *clientp, curl_off_t dltotal return 0; } +size_t FileDownloader::header_callback(char* buffer, size_t size, size_t nitems, void* userData) { + DownloadData* downData = static_cast(userData); + std::string header(buffer, nitems); + std::string newFilename = parse_http_headers_for_filename(header); + if(!newFilename.empty()) + downData->fileName = newFilename; + + return nitems; +} + void FileDownloader::cleanup() { if(downloadThread && downloadThread->joinable()) { shutdownThread = true; @@ -142,3 +157,35 @@ void FileDownloader::cleanup() { } #endif + +std::string FileDownloader::parse_http_headers_for_filename(std::string header) { + std::string headerLower = header; + std::transform(headerLower.begin(), headerLower.end(), headerLower.begin(), ::tolower); + std::string contentDispositionHeader = "content-disposition:"; + size_t contentDispositionLoc = headerLower.find(contentDispositionHeader); + if(contentDispositionLoc < headerLower.size()) { + size_t newlineHeaderLoc = std::min(headerLower.find('\n', contentDispositionLoc), headerLower.size()); + + std::string filenameHeader = "filename="; + size_t filenameLoc = headerLower.find(filenameHeader, contentDispositionLoc); + if(filenameLoc < newlineHeaderLoc) { + header = header.substr(filenameLoc + filenameHeader.size()); + header = header.substr(0, header.find(';')); + if(header.size() >= 2 && header.front() == '"' && header.back() == '"') { + header.erase(0, 1); + header.pop_back(); + } + return header; + } + } + + return ""; +} + +std::string FileDownloader::get_potential_filename_from_url(const std::string& url) { + size_t fileNameStart = url.find_last_of('/'); + if(fileNameStart >= (url.size() - 1)) + return ""; + std::string fileName = url.substr(fileNameStart + 1); + return fileName.substr(0, fileName.find('?')); +} diff --git a/include/Helpers/FileDownloader.hpp b/include/Helpers/FileDownloader.hpp index 393309e..019f302 100644 --- a/include/Helpers/FileDownloader.hpp +++ b/include/Helpers/FileDownloader.hpp @@ -20,12 +20,13 @@ class FileDownloader { FAILURE }; std::string str; + std::string fileName; std::atomic progress = 0; std::atomic status = DownloadData::Status::IN_PROGRESS; }; static void init(); - static std::shared_ptr download_data_from_url(std::string_view url); + static std::shared_ptr download_data_from_url(const std::string& url); static void cleanup(); private: @@ -38,6 +39,9 @@ class FileDownloader { static std::vector currentDownloads; + static std::string get_potential_filename_from_url(const std::string& url); + static std::string parse_http_headers_for_filename(std::string header); + #ifndef __EMSCRIPTEN__ static std::mutex newDownloadsMutex; static std::vector newDownloads; @@ -47,6 +51,7 @@ class FileDownloader { static void save_to_string(CURL* curl, std::string* str); static int download_progress_callback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow); + static size_t header_callback(char* buffer, size_t size, size_t nitems, void* userData); static void disable_ssl_verification(CURL* curl); static size_t write_to_string(void* contents, size_t size, size_t nmemb, void* userp); static void add_new_handlers_to_multi(); diff --git a/include/Helpers/FileDownloaderEmscripten.cpp b/include/Helpers/FileDownloaderEmscripten.cpp index bf8b9ea..dc1e654 100644 --- a/include/Helpers/FileDownloaderEmscripten.cpp +++ b/include/Helpers/FileDownloaderEmscripten.cpp @@ -16,10 +16,24 @@ void FileDownloader::download_progress(emscripten_fetch_t *fetch) { void FileDownloader::download_succeeded(emscripten_fetch_t *fetch) { DownloadData* downData = static_cast(fetch->userData); downData->str = std::string(fetch->data, fetch->numBytes); + + size_t headerLength = emscripten_fetch_get_response_headers_length(fetch); + if(headerLength != 0) { + std::string header(headerLength + 1, ' '); + emscripten_fetch_get_response_headers(fetch, header.data(), header.size() + 1); + if(!header.empty()) + header.pop_back(); // Remove additional null terminator + std::string newFilename = parse_http_headers_for_filename(header); + if(!newFilename.empty()) + downData->fileName = newFilename; + } + downData->status = DownloadData::Status::SUCCESS; + std::erase_if(currentDownloads, [fetch](DownloadHandler& d) { return (DownloadData*)fetch->userData == d.data.get(); }); + emscripten_fetch_close(fetch); } @@ -35,9 +49,10 @@ void FileDownloader::download_failed(emscripten_fetch_t *fetch) { void FileDownloader::init() { } -std::shared_ptr FileDownloader::download_data_from_url(std::string_view url) { +std::shared_ptr FileDownloader::download_data_from_url(const std::string& url) { DownloadHandler& newDownload = currentDownloads.emplace_back(); newDownload.data = std::make_shared(); + newDownload.data->fileName = get_potential_filename_from_url(url); emscripten_fetch_attr_t attr; emscripten_fetch_attr_init(&attr); diff --git a/src/DrawingProgram/DrawingProgram.cpp b/src/DrawingProgram/DrawingProgram.cpp index 861f76b..f54da1e 100644 --- a/src/DrawingProgram/DrawingProgram.cpp +++ b/src/DrawingProgram/DrawingProgram.cpp @@ -462,6 +462,7 @@ void DrawingProgram::update_downloading_dropped_files() { ResourceData newResource; newResource.data = std::make_shared(downFile.downData->str); + newResource.name = downFile.downData->fileName; ServerClientID imageID = world.rMan.add_resource(newResource); ResourceDisplay* display = world.rMan.get_display_data(imageID); if(display->get_type() == ResourceDisplay::Type::FILE) { diff --git a/src/DrawingProgram/ImageEditTool.cpp b/src/DrawingProgram/ImageEditTool.cpp index dbb08e2..779c20b 100644 --- a/src/DrawingProgram/ImageEditTool.cpp +++ b/src/DrawingProgram/ImageEditTool.cpp @@ -7,6 +7,7 @@ #include "../ResourceManager.hpp" #include "DrawingProgramEditToolBase.hpp" #include + #ifdef __EMSCRIPTEN__ #include #endif @@ -31,6 +32,8 @@ bool ImageEditTool::edit_gui(const std::shared_ptr& comp) { t.gui.push_id("edit tool image"); t.gui.text_label_centered("File Properties"); std::optional resourceData = drawP.world.rMan.get_resource(a->d.imageID); + + if(resourceData) { t.gui.text_label("Name: " + resourceData->name); if(t.gui.text_button_wide("file download", "Download file")) {