X-Git-Url: https://git.octo.it/?a=blobdiff_plain;ds=sidebyside;f=src%2Faddon%2Fdownloader.cpp;h=7c51d84044124aacbc60b6af67ffa5825997c8b1;hb=8f2d3b4b577297359c0cb57acb7e0782a3c35ea5;hp=f40a1415810e18648cb45b8172b2032905084e11;hpb=140e8eab7d0732663de2c1f0a0f2edcc5bc1749f;p=supertux.git diff --git a/src/addon/downloader.cpp b/src/addon/downloader.cpp index f40a14158..7c51d8404 100644 --- a/src/addon/downloader.cpp +++ b/src/addon/downloader.cpp @@ -18,8 +18,11 @@ #include "addon/downloader.hpp" #include +#include +#include #include #include +#include #include #include "util/log.hpp" @@ -46,6 +49,18 @@ size_t my_curl_physfs_write(void* ptr, size_t size, size_t nmemb, void* userdata } // namespace +void +TransferStatus::abort() +{ + m_downloader.abort(id); +} + +void +TransferStatus::update() +{ + m_downloader.update(); +} + class Transfer { private: @@ -57,16 +72,28 @@ private: std::array m_error_buffer; TransferStatusPtr m_status; + std::unique_ptr m_fout; public: - Transfer(Downloader& downloader, TransferId id, const std::string& url) : + Transfer(Downloader& downloader, TransferId id, + const std::string& url, + const std::string& outfile) : m_downloader(downloader), m_id(id), m_url(url), - m_handle(curl_easy_init()), - m_error_buffer(), - m_status(new TransferStatus(id)) + m_handle(), + m_error_buffer({{'\0'}}), + m_status(new TransferStatus(m_downloader, id)), + m_fout(PHYSFS_openWrite(outfile.c_str()), PHYSFS_close) { + if (!m_fout) + { + std::ostringstream out; + out << "PHYSFS_openRead() failed: " << PHYSFS_getLastError(); + throw std::runtime_error(out.str()); + } + + m_handle = curl_easy_init(); if (!m_handle) { throw std::runtime_error("curl_easy_init() failed"); @@ -77,12 +104,7 @@ public: curl_easy_setopt(m_handle, CURLOPT_USERAGENT, "SuperTux/" PACKAGE_VERSION " libcURL"); curl_easy_setopt(m_handle, CURLOPT_WRITEDATA, this); - curl_easy_setopt(m_handle, CURLOPT_WRITEFUNCTION, - [](void* ptr, size_t size, size_t nmemb, void* userdata) -> size_t - { - return static_cast(userdata) - ->on_data(ptr, size, nmemb); - }); + curl_easy_setopt(m_handle, CURLOPT_WRITEFUNCTION, &Transfer::on_data_wrap); curl_easy_setopt(m_handle, CURLOPT_ERRORBUFFER, m_error_buffer.data()); curl_easy_setopt(m_handle, CURLOPT_NOSIGNAL, 1); @@ -90,16 +112,8 @@ public: curl_easy_setopt(m_handle, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(m_handle, CURLOPT_NOPROGRESS, 0); - curl_easy_setopt(m_handle, CURLOPT_XFERINFODATA, this); - curl_easy_setopt(m_handle, CURLOPT_XFERINFOFUNCTION, - [](void* userdata, - curl_off_t dltotal, curl_off_t dlnow, - curl_off_t ultotal, curl_off_t ulnow) - { - return static_cast(userdata) - ->on_progress(dltotal, dlnow, - ultotal, ulnow); - }); + curl_easy_setopt(m_handle, CURLOPT_PROGRESSDATA, this); + curl_easy_setopt(m_handle, CURLOPT_PROGRESSFUNCTION, &Transfer::on_progress_wrap); } } @@ -113,6 +127,11 @@ public: return m_status; } + const char* get_error_buffer() const + { + return m_error_buffer.data(); + } + TransferId get_id() const { return m_id; @@ -130,17 +149,33 @@ public: size_t on_data(void* ptr, size_t size, size_t nmemb) { - return size * nmemb;; + PHYSFS_write(m_fout.get(), ptr, size, nmemb); + return size * nmemb; } - void on_progress(curl_off_t dltotal, curl_off_t dlnow, - curl_off_t ultotal, curl_off_t ulnow) + int on_progress(double dltotal, double dlnow, + double ultotal, double ulnow) { - m_status->dltotal = dltotal; - m_status->dlnow = dlnow; + m_status->dltotal = static_cast(dltotal); + m_status->dlnow = static_cast(dlnow); + + m_status->ultotal = static_cast(ultotal); + m_status->ulnow = static_cast(ulnow); - m_status->ultotal = ultotal; - m_status->ulnow = ulnow; + return 0; + } + +private: + static size_t on_data_wrap(char* ptr, size_t size, size_t nmemb, void* userdata) + { + return static_cast(userdata)->on_data(ptr, size, nmemb); + } + + static int on_progress_wrap(void* userdata, + double dltotal, double dlnow, + double ultotal, double ulnow) + { + return static_cast(userdata)->on_progress(dltotal, dlnow, ultotal, ulnow); } private: @@ -232,8 +267,22 @@ Downloader::abort(TransferId id) } else { + TransferStatusPtr status = (*it)->get_status(); + curl_multi_remove_handle(m_multi_handle, (*it)->get_curl_handle()); m_transfers.erase(it); + + for(auto& callback : status->callbacks) + { + try + { + callback(false); + } + catch(const std::exception& err) + { + log_warning << "Illegal exception in Downloader: " << err.what() << std::endl; + } + } } } @@ -256,8 +305,52 @@ Downloader::update() switch(msg->msg) { case CURLMSG_DONE: - curl_multi_remove_handle(m_multi_handle, msg->easy_handle); - //FIXME: finish_transfer(msg->easy_handle); + { + log_info << "Download completed with " << msg->data.result << std::endl; + curl_multi_remove_handle(m_multi_handle, msg->easy_handle); + + auto it = std::find_if(m_transfers.begin(), m_transfers.end(), + [&msg](const std::unique_ptr& rhs) { + return rhs->get_curl_handle() == msg->easy_handle; + }); + assert(it != m_transfers.end()); + TransferStatusPtr status = (*it)->get_status(); + status->error_msg = (*it)->get_error_buffer(); + m_transfers.erase(it); + + if (msg->data.result == CURLE_OK) + { + bool success = true; + for(auto& callback : status->callbacks) + { + try + { + callback(success); + } + catch(const std::exception& err) + { + success = false; + log_warning << "Exception in Downloader: " << err.what() << std::endl; + status->error_msg = err.what(); + } + } + } + else + { + log_warning << "Error: " << curl_easy_strerror(msg->data.result) << std::endl; + for(auto& callback : status->callbacks) + { + try + { + callback(false); + } + catch(const std::exception& err) + { + log_warning << "Illegal exception in Downloader: " << err.what() << std::endl; + } + } + } + } break; default: @@ -268,9 +361,9 @@ Downloader::update() } TransferStatusPtr -Downloader::request_download(const std::string& url, const std::string& filename) +Downloader::request_download(const std::string& url, const std::string& outfile) { - std::unique_ptr transfer(new Transfer(*this, m_next_transfer_id++, url)); + std::unique_ptr transfer(new Transfer(*this, m_next_transfer_id++, url, outfile)); curl_multi_add_handle(m_multi_handle, transfer->get_curl_handle()); m_transfers.push_back(std::move(transfer)); return m_transfers.back()->get_status();