summaryrefslogtreecommitdiff
path: root/src/net
diff options
context:
space:
mode:
Diffstat (limited to 'src/net')
-rw-r--r--src/net/download.cpp252
-rw-r--r--src/net/download.h31
2 files changed, 124 insertions, 159 deletions
diff --git a/src/net/download.cpp b/src/net/download.cpp
index 8a41ebfa..2cfdc3a1 100644
--- a/src/net/download.cpp
+++ b/src/net/download.cpp
@@ -31,7 +31,7 @@
#include <zlib.h>
-const char *DOWNLOAD_ERROR_MESSAGE_THREAD = "Could not create download thread!";
+constexpr char DOWNLOAD_ERROR_MESSAGE_THREAD[] = "Could not create download thread!";
namespace Net {
@@ -59,12 +59,12 @@ unsigned long Download::fadler32(FILE *file)
return adler;
}
-
-Download::Download(void *ptr, const std::string &url,
- DownloadUpdate updateFunction):
- mPtr(ptr),
- mUrl(url),
- mUpdateFunction(updateFunction)
+Download::Download(void *ptr,
+ const std::string &url,
+ DownloadUpdate updateFunction)
+ : mPtr(ptr)
+ , mUrl(url)
+ , mUpdateFunction(updateFunction)
{
mError = (char*) malloc(CURL_ERROR_SIZE);
mError[0] = 0;
@@ -74,17 +74,15 @@ Download::Download(void *ptr, const std::string &url,
Download::~Download()
{
- if (mHeaders)
- curl_slist_free_all(mHeaders);
+ SDL_WaitThread(mThread, nullptr);
- int status;
- SDL_WaitThread(mThread, &status);
+ curl_slist_free_all(mHeaders);
free(mError);
}
-void Download::addHeader(const std::string &header)
+void Download::addHeader(const char *header)
{
- mHeaders = curl_slist_append(mHeaders, header.c_str());
+ mHeaders = curl_slist_append(mHeaders, header);
}
void Download::noCache()
@@ -101,7 +99,7 @@ void Download::setFile(const std::string &filename,
mAdler = adler32;
}
-void Download::setWriteFunction(WriteFunction write)
+void Download::setWriteFunction(curl_write_callback write)
{
mOptions.memoryWrite = true;
mWriteFunction = write;
@@ -116,7 +114,7 @@ bool Download::start()
if (!mThread)
{
logger->log("%s", DOWNLOAD_ERROR_MESSAGE_THREAD);
- strcpy(mError, DOWNLOAD_ERROR_MESSAGE_THREAD);
+ strncpy(mError, DOWNLOAD_ERROR_MESSAGE_THREAD, CURL_ERROR_SIZE - 1);
mUpdateFunction(mPtr, DOWNLOAD_STATUS_THREAD_ERROR, 0, 0);
return false;
@@ -128,13 +126,7 @@ bool Download::start()
void Download::cancel()
{
logger->log("Canceling download: %s", mUrl.c_str());
-
mOptions.cancel = true;
- if (mThread && SDL_GetThreadID(mThread) != 0)
- {
- SDL_WaitThread(mThread, nullptr);
- mThread = nullptr;
- }
}
const char *Download::getError() const
@@ -147,177 +139,149 @@ int Download::downloadProgress(void *clientp,
curl_off_t ultotal, curl_off_t ulnow)
{
auto *d = reinterpret_cast<Download*>(clientp);
+ DownloadStatus status = d->mOptions.cancel ? DOWNLOAD_STATUS_CANCELLED
+ : DOWNLOAD_STATUS_IN_PROGRESS;
- if (d->mOptions.cancel)
- {
- return d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_CANCELLED,
- (size_t) dltotal, (size_t) dlnow);
- return -5;
- }
-
- return d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_IDLE,
- (size_t) dltotal, (size_t) dlnow);
+ return d->mUpdateFunction(d->mPtr, status, (size_t) dltotal, (size_t) dlnow);
}
int Download::downloadThread(void *ptr)
{
- int attempts = 0;
- bool complete = false;
auto *d = reinterpret_cast<Download*>(ptr);
- CURLcode res;
+ bool complete = false;
std::string outFilename;
- if (!d)
- {
- return 0;
- }
if (!d->mOptions.memoryWrite)
- {
outFilename = d->mFileName + ".part";
- }
- while (attempts < 3 && !complete && !d->mOptions.cancel)
+ for (int attempts = 0; attempts < 3 && !complete && !d->mOptions.cancel; ++attempts)
{
- FILE *file = nullptr;
-
d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_STARTING, 0, 0);
- if (d->mOptions.cancel)
- {
- d->mThread = nullptr;
- return 0;
- }
+ CURL *curl = curl_easy_init();
+ if (!curl)
+ break;
+
+ logger->log("Downloading: %s", d->mUrl.c_str());
- d->mCurl = curl_easy_init();
+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, d->mHeaders);
- if (d->mCurl && !d->mOptions.cancel)
+ FILE *file = nullptr;
+
+ if (d->mOptions.memoryWrite)
+ {
+ curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, d->mWriteFunction);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, d->mPtr);
+ }
+ else
{
- logger->log("Downloading: %s", d->mUrl.c_str());
+ file = fopen(outFilename.c_str(), "w+b");
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);
+ }
- curl_easy_setopt(d->mCurl, CURLOPT_FOLLOWLOCATION, 1);
- curl_easy_setopt(d->mCurl, CURLOPT_HTTPHEADER, d->mHeaders);
+ const std::string appShort = branding.getStringValue("appShort");
+ const std::string userAgent =
+ strprintf(PACKAGE_EXTENDED_VERSION, appShort.c_str());
- if (d->mOptions.memoryWrite)
- {
- curl_easy_setopt(d->mCurl, CURLOPT_FAILONERROR, 1);
- curl_easy_setopt(d->mCurl, CURLOPT_WRITEFUNCTION, d->mWriteFunction);
- curl_easy_setopt(d->mCurl, CURLOPT_WRITEDATA, d->mPtr);
- }
- else
- {
- file = fopen(outFilename.c_str(), "w+b");
- curl_easy_setopt(d->mCurl, CURLOPT_WRITEDATA, file);
- }
+ curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent.c_str());
+ curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, d->mError);
+ curl_easy_setopt(curl, CURLOPT_URL, d->mUrl.c_str());
+ curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
+ curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, downloadProgress);
+ curl_easy_setopt(curl, CURLOPT_XFERINFODATA, ptr);
+ curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
+ curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15);
- const std::string appShort = branding.getStringValue("appShort");
- const std::string userAgent =
- strprintf(PACKAGE_EXTENDED_VERSION, appShort.c_str());
+ const CURLcode res = curl_easy_perform(curl);
+ curl_easy_cleanup(curl);
- curl_easy_setopt(d->mCurl, CURLOPT_USERAGENT, userAgent.c_str());
- curl_easy_setopt(d->mCurl, CURLOPT_ERRORBUFFER, d->mError);
- curl_easy_setopt(d->mCurl, CURLOPT_URL, d->mUrl.c_str());
- curl_easy_setopt(d->mCurl, CURLOPT_NOPROGRESS, 0);
- curl_easy_setopt(d->mCurl, CURLOPT_XFERINFOFUNCTION, downloadProgress);
- curl_easy_setopt(d->mCurl, CURLOPT_XFERINFODATA, ptr);
- curl_easy_setopt(d->mCurl, CURLOPT_NOSIGNAL, 1);
- curl_easy_setopt(d->mCurl, CURLOPT_CONNECTTIMEOUT, 15);
+ if (res == CURLE_ABORTED_BY_CALLBACK)
+ {
+ d->mOptions.cancel = true;
- if ((res = curl_easy_perform(d->mCurl)) != 0 && !d->mOptions.cancel)
+ if (file)
{
- switch (res)
- {
- case CURLE_ABORTED_BY_CALLBACK:
- d->mOptions.cancel = true;
- break;
- case CURLE_COULDNT_CONNECT:
- default:
- logger->log("curl error %d: %s host: %s",
- res, d->mError, d->mUrl.c_str());
- break;
- }
+ fclose(file);
+ ::remove(outFilename.c_str());
+ }
- if (d->mOptions.cancel)
- {
- break;
- }
+ break;
+ }
- d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_ERROR, 0, 0);
+ if (res != CURLE_OK)
+ {
+ logger->log("curl error %d: %s host: %s",
+ res, d->mError, d->mUrl.c_str());
- if (!d->mOptions.memoryWrite)
- {
- fclose(file);
- ::remove(outFilename.c_str());
- }
- attempts++;
- continue;
+ if (file)
+ {
+ fclose(file);
+ ::remove(outFilename.c_str());
}
- curl_easy_cleanup(d->mCurl);
+ break;
+ }
- if (!d->mOptions.memoryWrite)
+ if (!d->mOptions.memoryWrite)
+ {
+ // Don't check resources.xml checksum
+ if (d->mAdler)
{
- // Don't check resources.xml checksum
- if (d->mAdler)
- {
- unsigned long adler = fadler32(file);
+ unsigned long adler = fadler32(file);
- if (d->mAdler != adler)
- {
+ if (d->mAdler != adler)
+ {
+ if (file)
fclose(file);
- // Remove the corrupted file
- ::remove(d->mFileName.c_str());
- logger->log("Checksum for file %s failed: (%lx/%lx)",
- d->mFileName.c_str(),
- adler, *d->mAdler);
- attempts++;
- continue; // Bail out here to avoid the renaming
- }
+ // Remove the corrupted file
+ ::remove(outFilename.c_str());
+ logger->log("Checksum for file %s failed: (%lx/%lx)",
+ d->mFileName.c_str(),
+ adler, *d->mAdler);
+
+ continue; // Bail out here to avoid the renaming
}
+ }
+
+ if (file)
fclose(file);
- // Any existing file with this name is deleted first, otherwise
- // the rename will fail on Windows.
- ::remove(d->mFileName.c_str());
- ::rename(outFilename.c_str(), d->mFileName.c_str());
+ // Any existing file with this name is deleted first, otherwise
+ // the rename will fail on Windows.
+ ::remove(d->mFileName.c_str());
+ ::rename(outFilename.c_str(), d->mFileName.c_str());
- // Check if we can open it and no errors were encountered
- // during renaming
- file = fopen(d->mFileName.c_str(), "rb");
- if (file)
- {
- fclose(file);
- complete = true;
- }
- }
- else
+ // Check if we can open it and no errors were encountered
+ // during renaming
+ file = fopen(d->mFileName.c_str(), "rb");
+ if (file)
{
- // It's stored in memory, we're done
+ fclose(file);
+ file = nullptr;
complete = true;
}
}
- if (d->mOptions.cancel)
+ else
{
- d->mThread = nullptr;
- return 0;
+ // It's stored in memory, we're done
+ complete = true;
}
- attempts++;
- }
- if (d->mOptions.cancel)
- {
- // Nothing to do...
+ if (file)
+ fclose(file);
}
- else if (!complete || attempts >= 3)
- {
- d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_ERROR, 0, 0);
- }
- else
+
+ if (!d->mOptions.cancel)
{
- d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_COMPLETE, 0, 0);
+ if (complete)
+ d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_COMPLETE, 0, 0);
+ else
+ d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_ERROR, 0, 0);
}
- d->mThread = nullptr;
return 0;
}
diff --git a/src/net/download.h b/src/net/download.h
index b0f79e79..be2e374f 100644
--- a/src/net/download.h
+++ b/src/net/download.h
@@ -18,7 +18,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <cstdlib> // pulls in int64_t
#include <cstdio>
#include <string>
#include <optional>
@@ -33,28 +32,31 @@ enum DownloadStatus
DOWNLOAD_STATUS_THREAD_ERROR = -2,
DOWNLOAD_STATUS_ERROR = -1,
DOWNLOAD_STATUS_STARTING = 0,
- DOWNLOAD_STATUS_IDLE,
+ DOWNLOAD_STATUS_IN_PROGRESS,
DOWNLOAD_STATUS_COMPLETE
};
-using DownloadUpdate = int (*)(void *, DownloadStatus, size_t, size_t);
-
-// Matches what CURL expects
-using WriteFunction = size_t (*)(void *, size_t, size_t, void *);
-
struct SDL_Thread;
-using CURL = void;
-struct curl_slist;
namespace Net {
+
class Download
{
public:
- Download(void *ptr, const std::string &url, DownloadUpdate updateFunction);
+ /**
+ * Callback function for download updates.
+ *
+ * @param ptr Pointer passed to Download constructor
+ * @param status Current download status
+ * @param dltotal Total number of bytes to download
+ * @param dlnow Number of bytes downloaded so far
+ */
+ using DownloadUpdate = int (*)(void *, DownloadStatus, size_t, size_t);
+ Download(void *ptr, const std::string &url, DownloadUpdate updateFunction);
~Download();
- void addHeader(const std::string &header);
+ void addHeader(const char *header);
/**
* Convience method for adding no-cache headers.
@@ -64,7 +66,7 @@ class Download
void setFile(const std::string &filename,
std::optional<unsigned long> adler32 = {});
- void setWriteFunction(WriteFunction write);
+ void setWriteFunction(curl_write_callback write);
/**
* Starts the download thread.
@@ -76,7 +78,7 @@ class Download
/**
* Cancels the download. Returns immediately, the cancelled status will
- * be noted in the next avialable update call.
+ * be noted in the next available update call.
*/
void cancel();
@@ -96,11 +98,10 @@ class Download
unsigned memoryWrite: 1;
} mOptions;
std::string mFileName;
- WriteFunction mWriteFunction = nullptr;
+ curl_write_callback mWriteFunction = nullptr;
std::optional<unsigned long> mAdler;
DownloadUpdate mUpdateFunction;
SDL_Thread *mThread = nullptr;
- CURL *mCurl = nullptr;
curl_slist *mHeaders = nullptr;
char *mError;
};