2014-10-29 12:37:38 +00:00
|
|
|
#include <base/system.h>
|
2017-09-23 21:11:12 +00:00
|
|
|
#include <engine/engine.h>
|
2014-10-29 12:37:38 +00:00
|
|
|
#include <engine/storage.h>
|
2015-05-09 23:36:29 +00:00
|
|
|
#include <engine/shared/config.h>
|
2017-09-03 07:18:00 +00:00
|
|
|
#include <game/version.h>
|
2014-10-29 12:37:38 +00:00
|
|
|
|
2017-09-23 21:11:12 +00:00
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
|
|
#include "curl/curl.h"
|
|
|
|
#include "curl/easy.h"
|
2014-10-29 12:37:38 +00:00
|
|
|
|
2017-09-23 21:11:12 +00:00
|
|
|
#include "fetcher.h"
|
2014-10-29 12:37:38 +00:00
|
|
|
|
2017-10-09 17:22:44 +00:00
|
|
|
class CFetchTask : public IFetchTask
|
2017-10-09 08:41:41 +00:00
|
|
|
{
|
|
|
|
friend class CFetcher;
|
|
|
|
class CFetcher *m_pFetcher;
|
|
|
|
|
|
|
|
CJob m_Job;
|
|
|
|
CURL *m_pHandle;
|
|
|
|
void *m_pUser;
|
|
|
|
|
|
|
|
char m_aUrl[256];
|
|
|
|
char m_aDest[128];
|
|
|
|
int m_StorageType;
|
|
|
|
bool m_UseDDNetCA;
|
|
|
|
bool m_CanTimeout;
|
|
|
|
|
|
|
|
PROGFUNC m_pfnProgressCallback;
|
|
|
|
COMPFUNC m_pfnCompCallback;
|
|
|
|
|
|
|
|
double m_Size;
|
|
|
|
double m_Current;
|
|
|
|
int m_Progress;
|
|
|
|
int m_State;
|
|
|
|
|
|
|
|
bool m_Abort;
|
2017-10-09 08:48:40 +00:00
|
|
|
bool m_Destroy;
|
2017-10-09 08:41:41 +00:00
|
|
|
|
2017-10-09 17:22:44 +00:00
|
|
|
public:
|
2017-10-09 18:04:23 +00:00
|
|
|
virtual double Current() { return m_Current; };
|
|
|
|
virtual double Size() { return m_Size; };
|
|
|
|
virtual int Progress() { return m_Progress; };
|
|
|
|
virtual int State() { return m_State; };
|
|
|
|
virtual const char *Dest() { return m_aDest; };
|
2017-10-09 08:41:41 +00:00
|
|
|
|
2017-10-09 17:22:44 +00:00
|
|
|
virtual void Abort() { m_Abort = true; };
|
|
|
|
virtual void Destroy();
|
2017-10-09 08:41:41 +00:00
|
|
|
};
|
|
|
|
|
2017-10-09 08:48:40 +00:00
|
|
|
void CFetchTask::Destroy()
|
|
|
|
{
|
|
|
|
if(m_State >= IFetchTask::STATE_DONE || m_State == IFetchTask::STATE_ERROR)
|
2017-10-09 18:04:23 +00:00
|
|
|
delete this;
|
2017-10-09 08:48:40 +00:00
|
|
|
else {
|
|
|
|
m_Abort = true;
|
|
|
|
m_Destroy = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-29 12:37:38 +00:00
|
|
|
bool CFetcher::Init()
|
|
|
|
{
|
|
|
|
m_pStorage = Kernel()->RequestInterface<IStorage>();
|
2017-09-23 21:11:12 +00:00
|
|
|
m_pEngine = Kernel()->RequestInterface<IEngine>();
|
|
|
|
if(curl_global_init(CURL_GLOBAL_DEFAULT))
|
|
|
|
return false;
|
|
|
|
return true;
|
2014-10-29 12:37:38 +00:00
|
|
|
}
|
|
|
|
|
2017-09-23 21:11:12 +00:00
|
|
|
void CFetcher::Escape(char *pBuf, size_t size, const char *pStr)
|
2014-10-29 12:37:38 +00:00
|
|
|
{
|
2017-09-23 21:11:12 +00:00
|
|
|
char *pEsc = curl_easy_escape(0, pStr, 0);
|
|
|
|
str_copy(pBuf, pEsc, size);
|
|
|
|
curl_free(pEsc);
|
2014-10-29 12:37:38 +00:00
|
|
|
}
|
|
|
|
|
2017-10-09 08:41:41 +00:00
|
|
|
IFetchTask *CFetcher::FetchFile(const char *pUrl, const char *pDest, int StorageType, bool UseDDNetCA, bool CanTimeout, void *pUser, COMPFUNC pfnCompCb, PROGFUNC pfnProgCb)
|
2014-10-29 12:37:38 +00:00
|
|
|
{
|
2017-10-09 18:04:23 +00:00
|
|
|
CFetchTask *pTask = new CFetchTask;
|
2017-09-23 21:11:12 +00:00
|
|
|
pTask->m_pFetcher = this;
|
|
|
|
|
2015-08-29 20:39:44 +00:00
|
|
|
str_copy(pTask->m_aUrl, pUrl, sizeof(pTask->m_aUrl));
|
|
|
|
str_copy(pTask->m_aDest, pDest, sizeof(pTask->m_aDest));
|
2014-10-29 12:37:38 +00:00
|
|
|
pTask->m_StorageType = StorageType;
|
|
|
|
pTask->m_pUser = pUser;
|
2017-09-23 21:11:12 +00:00
|
|
|
pTask->m_pfnCompCallback = pfnCompCb;
|
|
|
|
pTask->m_pfnProgressCallback = pfnProgCb;
|
2014-10-29 12:37:38 +00:00
|
|
|
|
2017-09-23 21:11:12 +00:00
|
|
|
pTask->m_Abort = false;
|
2017-10-09 08:48:40 +00:00
|
|
|
pTask->m_Destroy = false;
|
2017-09-23 21:11:12 +00:00
|
|
|
pTask->m_Size = pTask->m_Progress = 0;
|
2015-02-27 21:06:19 +00:00
|
|
|
|
2017-10-09 08:41:41 +00:00
|
|
|
pTask->m_State = IFetchTask::STATE_QUEUED;
|
2017-09-23 21:11:12 +00:00
|
|
|
m_pEngine->AddJob(&pTask->m_Job, FetcherThread, pTask);
|
2017-10-09 08:41:41 +00:00
|
|
|
|
|
|
|
return pTask;
|
2014-10-29 12:37:38 +00:00
|
|
|
}
|
|
|
|
|
2017-09-23 21:11:12 +00:00
|
|
|
int CFetcher::FetcherThread(void *pUser)
|
2015-02-12 17:58:54 +00:00
|
|
|
{
|
2017-09-23 21:11:12 +00:00
|
|
|
CFetchTask *pTask = (CFetchTask *)pUser;
|
2015-02-12 17:58:54 +00:00
|
|
|
|
2017-09-23 21:11:12 +00:00
|
|
|
pTask->m_pHandle = curl_easy_init();
|
|
|
|
if(!pTask->m_pHandle)
|
|
|
|
return 1;
|
2014-10-29 12:37:38 +00:00
|
|
|
|
2015-03-14 19:01:18 +00:00
|
|
|
char aPath[512];
|
|
|
|
if(pTask->m_StorageType == -2)
|
2017-09-23 21:11:12 +00:00
|
|
|
pTask->m_pFetcher->m_pStorage->GetBinaryPath(pTask->m_aDest, aPath, sizeof(aPath));
|
2015-03-14 19:01:18 +00:00
|
|
|
else
|
2017-09-23 21:11:12 +00:00
|
|
|
pTask->m_pFetcher->m_pStorage->GetCompletePath(pTask->m_StorageType, pTask->m_aDest, aPath, sizeof(aPath));
|
2015-08-29 20:40:10 +00:00
|
|
|
|
2016-05-01 12:20:55 +00:00
|
|
|
if(fs_makedir_rec_for(aPath) < 0)
|
2016-05-02 19:35:32 +00:00
|
|
|
dbg_msg("fetcher", "i/o error, cannot create folder for: %s", aPath);
|
2015-08-29 20:40:10 +00:00
|
|
|
|
2015-02-27 21:06:19 +00:00
|
|
|
IOHANDLE File = io_open(aPath, IOFLAG_WRITE);
|
|
|
|
|
2017-03-04 14:43:49 +00:00
|
|
|
if(!File)
|
|
|
|
{
|
2016-05-02 19:35:32 +00:00
|
|
|
dbg_msg("fetcher", "i/o error, cannot open file: %s", pTask->m_aDest);
|
2017-10-09 08:41:41 +00:00
|
|
|
pTask->m_State = IFetchTask::STATE_ERROR;
|
2017-09-23 21:11:12 +00:00
|
|
|
return 1;
|
2015-03-28 22:41:58 +00:00
|
|
|
}
|
|
|
|
|
2014-10-29 12:37:38 +00:00
|
|
|
char aErr[CURL_ERROR_SIZE];
|
2017-09-23 21:11:12 +00:00
|
|
|
curl_easy_setopt(pTask->m_pHandle, CURLOPT_ERRORBUFFER, aErr);
|
2014-10-29 12:37:38 +00:00
|
|
|
|
2017-09-23 21:11:12 +00:00
|
|
|
//curl_easy_setopt(pTask->m_pHandle, CURLOPT_VERBOSE, 1L);
|
2015-05-09 23:53:26 +00:00
|
|
|
if(pTask->m_CanTimeout)
|
|
|
|
{
|
2017-09-23 21:11:12 +00:00
|
|
|
curl_easy_setopt(pTask->m_pHandle, CURLOPT_CONNECTTIMEOUT_MS, (long)g_Config.m_ClHTTPConnectTimeoutMs);
|
|
|
|
curl_easy_setopt(pTask->m_pHandle, CURLOPT_LOW_SPEED_LIMIT, (long)g_Config.m_ClHTTPLowSpeedLimit);
|
|
|
|
curl_easy_setopt(pTask->m_pHandle, CURLOPT_LOW_SPEED_TIME, (long)g_Config.m_ClHTTPLowSpeedTime);
|
2015-05-09 23:53:26 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-09-23 21:11:12 +00:00
|
|
|
curl_easy_setopt(pTask->m_pHandle, CURLOPT_CONNECTTIMEOUT_MS, 0);
|
|
|
|
curl_easy_setopt(pTask->m_pHandle, CURLOPT_LOW_SPEED_LIMIT, 0);
|
|
|
|
curl_easy_setopt(pTask->m_pHandle, CURLOPT_LOW_SPEED_TIME, 0);
|
2015-05-09 23:53:26 +00:00
|
|
|
}
|
2017-09-23 21:11:12 +00:00
|
|
|
curl_easy_setopt(pTask->m_pHandle, CURLOPT_FOLLOWLOCATION, 1L);
|
|
|
|
curl_easy_setopt(pTask->m_pHandle, CURLOPT_MAXREDIRS, 4L);
|
|
|
|
curl_easy_setopt(pTask->m_pHandle, CURLOPT_FAILONERROR, 1L);
|
2017-07-16 08:45:41 +00:00
|
|
|
if(pTask->m_UseDDNetCA)
|
|
|
|
{
|
|
|
|
char aCAFile[512];
|
2017-09-23 21:11:12 +00:00
|
|
|
pTask->m_pFetcher->m_pStorage->GetBinaryPath("data/ca-ddnet.pem", aCAFile, sizeof aCAFile);
|
|
|
|
curl_easy_setopt(pTask->m_pHandle, CURLOPT_CAINFO, aCAFile);
|
2017-07-16 08:45:41 +00:00
|
|
|
}
|
2017-09-23 21:11:12 +00:00
|
|
|
curl_easy_setopt(pTask->m_pHandle, CURLOPT_URL, pTask->m_aUrl);
|
|
|
|
curl_easy_setopt(pTask->m_pHandle, CURLOPT_WRITEDATA, File);
|
|
|
|
curl_easy_setopt(pTask->m_pHandle, CURLOPT_WRITEFUNCTION, &CFetcher::WriteToFile);
|
|
|
|
curl_easy_setopt(pTask->m_pHandle, CURLOPT_NOPROGRESS, 0);
|
|
|
|
curl_easy_setopt(pTask->m_pHandle, CURLOPT_PROGRESSDATA, pTask);
|
|
|
|
curl_easy_setopt(pTask->m_pHandle, CURLOPT_PROGRESSFUNCTION, &CFetcher::ProgressCallback);
|
|
|
|
curl_easy_setopt(pTask->m_pHandle, CURLOPT_NOSIGNAL, 1L);
|
|
|
|
curl_easy_setopt(pTask->m_pHandle, CURLOPT_USERAGENT, "DDNet " GAME_RELEASE_VERSION " (" CONF_PLATFORM_STRING "; " CONF_ARCH_STRING ")");
|
2017-02-21 16:10:08 +00:00
|
|
|
|
2016-05-02 19:35:32 +00:00
|
|
|
dbg_msg("fetcher", "downloading %s", pTask->m_aDest);
|
2017-10-09 08:41:41 +00:00
|
|
|
pTask->m_State = IFetchTask::STATE_RUNNING;
|
2017-09-23 21:11:12 +00:00
|
|
|
int ret = curl_easy_perform(pTask->m_pHandle);
|
2015-07-09 00:08:14 +00:00
|
|
|
io_close(File);
|
2015-01-28 12:13:56 +00:00
|
|
|
if(ret != CURLE_OK)
|
2015-01-19 22:00:08 +00:00
|
|
|
{
|
2016-05-02 19:35:32 +00:00
|
|
|
dbg_msg("fetcher", "task failed. libcurl error: %s", aErr);
|
2017-10-09 08:41:41 +00:00
|
|
|
pTask->m_State = (ret == CURLE_ABORTED_BY_CALLBACK) ? IFetchTask::STATE_ABORTED : IFetchTask::STATE_ERROR;
|
2014-10-29 12:37:38 +00:00
|
|
|
}
|
2015-03-13 19:17:23 +00:00
|
|
|
else
|
|
|
|
{
|
2016-05-02 19:35:32 +00:00
|
|
|
dbg_msg("fetcher", "task done %s", pTask->m_aDest);
|
2017-10-09 08:41:41 +00:00
|
|
|
pTask->m_State = IFetchTask::STATE_DONE;
|
2015-03-13 19:17:23 +00:00
|
|
|
}
|
2017-09-23 21:11:12 +00:00
|
|
|
|
|
|
|
curl_easy_cleanup(pTask->m_pHandle);
|
|
|
|
|
|
|
|
if(pTask->m_pfnCompCallback)
|
|
|
|
pTask->m_pfnCompCallback(pTask, pTask->m_pUser);
|
|
|
|
|
2017-10-09 08:48:40 +00:00
|
|
|
if(pTask->m_Destroy)
|
2017-10-09 18:04:23 +00:00
|
|
|
delete pTask;
|
2017-10-09 08:48:40 +00:00
|
|
|
|
2017-09-23 21:11:12 +00:00
|
|
|
return(ret != CURLE_OK) ? 1 : 0;
|
2014-10-29 12:37:38 +00:00
|
|
|
}
|
|
|
|
|
2017-08-30 19:01:19 +00:00
|
|
|
size_t CFetcher::WriteToFile(char *pData, size_t size, size_t nmemb, void *pFile)
|
2014-10-29 12:37:38 +00:00
|
|
|
{
|
2017-08-30 19:01:19 +00:00
|
|
|
return io_write((IOHANDLE)pFile, pData, size*nmemb);
|
2014-10-29 12:37:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int CFetcher::ProgressCallback(void *pUser, double DlTotal, double DlCurr, double UlTotal, double UlCurr)
|
|
|
|
{
|
|
|
|
CFetchTask *pTask = (CFetchTask *)pUser;
|
|
|
|
pTask->m_Current = DlCurr;
|
2015-01-28 11:57:11 +00:00
|
|
|
pTask->m_Size = DlTotal;
|
2015-01-19 22:00:08 +00:00
|
|
|
pTask->m_Progress = (100 * DlCurr) / (DlTotal ? DlTotal : 1);
|
2014-10-29 12:37:38 +00:00
|
|
|
if(pTask->m_pfnProgressCallback)
|
|
|
|
pTask->m_pfnProgressCallback(pTask, pTask->m_pUser);
|
2015-01-28 12:13:56 +00:00
|
|
|
return pTask->m_Abort ? -1 : 0;
|
2015-01-19 21:19:27 +00:00
|
|
|
}
|