From 375daf481084d517c19a0b8a1de358842de577f7 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Tue, 13 Sep 2022 13:53:03 -0600 Subject: [PATCH] libpbd: new threader Inflater and Downlaoder classes --- libs/pbd/downloader.cc | 185 ++++++++++++++++++++++++++++++++++++++ libs/pbd/inflater.cc | 77 ++++++++++++++++ libs/pbd/pbd/downloader.h | 68 ++++++++++++++ libs/pbd/pbd/inflater.h | 54 +++++++++++ libs/pbd/wscript | 2 + 5 files changed, 386 insertions(+) create mode 100644 libs/pbd/downloader.cc create mode 100644 libs/pbd/inflater.cc create mode 100644 libs/pbd/pbd/downloader.h create mode 100644 libs/pbd/pbd/inflater.h diff --git a/libs/pbd/downloader.cc b/libs/pbd/downloader.cc new file mode 100644 index 0000000000..4ed2bf0a6e --- /dev/null +++ b/libs/pbd/downloader.cc @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2022 Paul Davis + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include + +#include +#include + +#include "pbd/downloader.h" +#include "pbd/error.h" +#include "pbd/i18n.h" +#include "pbd/pthread_utils.h" + +using namespace PBD; +using std::string; + +static size_t +CurlWrite_CallbackFunc_Downloader(void *contents, size_t size, size_t nmemb, Downloader* dl) +{ + return dl->write (contents, size, nmemb); +} + +size_t +Downloader::write (void *ptr, size_t size, size_t nmemb) +{ + if (_cancel) { + fclose (file); + file = 0; + ::g_unlink (file_path.c_str()); + + _downloaded = 0; + _download_size = 0; + + return 0; + } + + size_t nwritten = fwrite (ptr, size, nmemb, file); + + _downloaded += nwritten; + + return nwritten; +} + +Downloader::Downloader (string const & u, string const & dir) + : url (u) + , destdir (dir) + , file (0) + , _cancel (false) + , _download_size (0) + , _downloaded (0) +{ +} + +Downloader::~Downloader () +{ + cleanup(); +} + +int +Downloader::start () +{ + file_path = Glib::build_filename (destdir, Glib::path_get_basename (url)); + + if (!(file = fopen (file_path.c_str(), "w"))) { + return -1; + } + + _cancel = false; + _status = 0; /* unknown at this point */ + thr = std::thread (&Downloader::download, this); + return 0; +} + +void +Downloader::cleanup () +{ + thr.join (); +} + +void +Downloader::cancel () +{ + _cancel = true; +} + +double +Downloader::progress () const +{ + if (_download_size == 0) { + return 0.; + } + + return (double) _downloaded / _download_size; +} + +void +Downloader::download () +{ + char curl_error[CURL_ERROR_SIZE]; + + { + /* First curl fetch to get the data size so that we can offer a + * progress meter + */ + + curl = curl_easy_init (); + if (!curl) { + _status = -1; + return; + } + + /* get size */ + + curl_easy_setopt (curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); + curl_easy_setopt(curl, CURLOPT_HEADER, 0L); + curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, curl_error); + + CURLcode res = curl_easy_perform (curl); + + if (res == CURLE_OK) { + double dsize; + curl_easy_getinfo (curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &dsize); + _download_size = dsize; + } + + curl_easy_cleanup (curl); + + if (res != CURLE_OK ) { + error << string_compose (_("Download failed, error code %1 (%2)"), curl_easy_strerror (res), curl_error) << endmsg; + _status = -2; + return; + } + } + + curl = curl_easy_init (); + if (!curl) { + _status = -1; + return; + } + + curl_easy_setopt (curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt (curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWrite_CallbackFunc_Downloader); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, this); + CURLcode res = curl_easy_perform (curl); + curl_easy_cleanup (curl); + + if (res == CURLE_OK) { + _status = 1; + } else { + _status = -1; + } + + if (file) { + fclose (file); + file = 0; + } +} + +std::string +Downloader::download_path() const +{ + /* Can only return the download path if we completed, and completed successfully */ + if (_status > 0) { + return file_path; + } + return std::string(); +} diff --git a/libs/pbd/inflater.cc b/libs/pbd/inflater.cc new file mode 100644 index 0000000000..c856d3a879 --- /dev/null +++ b/libs/pbd/inflater.cc @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2022 Paul Davis + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include + +#include +#include + +#include "pbd/inflater.h" +#include "pbd/pthread_utils.h" + +using namespace PBD; + +Inflater::Inflater (std::string const & ap, std::string const & dd) + : FileArchive (ap) + , thread (0) + , _status (-1) /* means "unknown" */ + , archive_path (ap) + , destdir (dd) +{ +} + +Inflater::~Inflater () +{ + if (thread) { + thread->join (); + } +} + +int +Inflater::start () +{ + return 0 != (thread = PBD::Thread::create (boost::bind (&Inflater::threaded_inflate, this))); +} + +void +Inflater::threaded_inflate () +{ + require_progress (); + + try { + std::string pwd (Glib::get_current_dir ()); + + if (inflate (destdir)) { + /* cleanup ? */ + _status = 1; /* failure */ + } + + _status = 0; /* success */ + + } catch (...) { + _status = 1; /* failure */ + } + + + /* final progress signal, values do not matter much because status is + * set to be >= 0 + */ + + progress (1, 1); +} + diff --git a/libs/pbd/pbd/downloader.h b/libs/pbd/pbd/downloader.h new file mode 100644 index 0000000000..19f8f46042 --- /dev/null +++ b/libs/pbd/pbd/downloader.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2022 Paul Davis + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __libpbd_downloader_h__ +#define __libpbd_downloader_h__ + +#include +#include +#include + +#include + +#include "pbd/libpbd_visibility.h" + +namespace PBD { + +class LIBPBD_API Downloader { + public: + Downloader (std::string const & url, std::string const & destdir); + ~Downloader (); + + int start (); + void cleanup (); + void cancel (); + double progress() const; + + uint64_t download_size() const { return _download_size; } + uint64_t downloaded () const { return _downloaded; } + + /* public so it can be called from a static C function */ + size_t write (void *contents, size_t size, size_t nmemb); + + int status() const { return _status; } + std::string download_path() const; + + private: + std::string url; + std::string destdir; + std::string file_path; + FILE* file; + CURL* curl; + bool _cancel; + std::atomic _download_size; /* read-only from requestor thread */ + std::atomic _downloaded; /* read-only from requestor thread */ + std::atomic _status; + std::thread thr; + + void download (); +}; + +} /* namespace */ + +#endif diff --git a/libs/pbd/pbd/inflater.h b/libs/pbd/pbd/inflater.h new file mode 100644 index 0000000000..ace55746a7 --- /dev/null +++ b/libs/pbd/pbd/inflater.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2022 Paul Davis + * + * 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 2 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __libpbd_inflater_h__ +#define __libpbd_inflater_h__ + +#include + +#include "pbd/file_archive.h" +#include "pbd/libpbd_visibility.h" + +namespace PBD { + class Thread; +} + +namespace PBD { + +class LIBPBD_API Inflater : public PBD::FileArchive +{ + public: + Inflater (std::string const & archive_path, std::string const & destdir); + ~Inflater (); + + int start (); + bool running() const { return thread != 0; } + int status() const { return _status; } + + private: + PBD::Thread* thread; + int _status; + std::string archive_path; + std::string destdir; + + void threaded_inflate (); +}; + +} /* namespace */ + +#endif diff --git a/libs/pbd/wscript b/libs/pbd/wscript index 82960539a2..bad6925f92 100644 --- a/libs/pbd/wscript +++ b/libs/pbd/wscript @@ -42,6 +42,7 @@ libpbd_sources = [ 'cpus.cc', 'debug.cc', 'demangle.cc', + 'downloader.cc', 'enumwriter.cc', 'event_loop.cc', 'enums.cc', @@ -53,6 +54,7 @@ libpbd_sources = [ 'fpu.cc', 'glib_event_source.cc', 'id.cc', + 'inflater.cc', 'locale_guard.cc', 'localtime_r.cc', 'malign.cc',