[Download] Add ssl support

This commit is contained in:
momo5502 2017-05-15 21:57:45 +02:00
parent 3b12f3d30e
commit f6894b46c5
9 changed files with 197 additions and 140 deletions

View File

@ -68,6 +68,7 @@ namespace Components
#include "Modules/Monitor.hpp" #include "Modules/Monitor.hpp"
#include "Modules/Network.hpp" #include "Modules/Network.hpp"
#include "Modules/Theatre.hpp" #include "Modules/Theatre.hpp"
#include "Modules/QuickPatch.hpp"
#include "Modules/Node.hpp" #include "Modules/Node.hpp"
#include "Modules/RCon.hpp" #include "Modules/RCon.hpp"
#include "Modules/Party.hpp" // Destroys the order, but requires network classes :D #include "Modules/Party.hpp" // Destroys the order, but requires network classes :D
@ -93,7 +94,6 @@ namespace Components
#include "Modules/FileSystem.hpp" #include "Modules/FileSystem.hpp"
#include "Modules/ModelSurfs.hpp" #include "Modules/ModelSurfs.hpp"
#include "Modules/PlayerName.hpp" #include "Modules/PlayerName.hpp"
#include "Modules/QuickPatch.hpp"
#include "Modules/ServerInfo.hpp" #include "Modules/ServerInfo.hpp"
#include "Modules/ServerList.hpp" #include "Modules/ServerList.hpp"
#include "Modules/SlowMotion.hpp" #include "Modules/SlowMotion.hpp"

View File

@ -4,7 +4,7 @@ namespace Components
{ {
mg_mgr Download::Mgr; mg_mgr Download::Mgr;
Download::ClientDownload Download::CLDownload; Download::ClientDownload Download::CLDownload;
std::vector<Download::ScriptDownload> Download::ScriptDownloads; std::vector<std::shared_ptr<Download::ScriptDownload>> Download::ScriptDownloads;
#pragma region Client #pragma region Client
@ -748,15 +748,36 @@ namespace Components
QuickPatch::OnFrame([]() QuickPatch::OnFrame([]()
{ {
int workingCount = 0;
for(auto i = Download::ScriptDownloads.begin(); i != Download::ScriptDownloads.end();) for(auto i = Download::ScriptDownloads.begin(); i != Download::ScriptDownloads.end();)
{ {
i->work(); auto download = *i;
if(i->isDone()) if(download->isDone())
{ {
download->notifyDone();
i = Download::ScriptDownloads.erase(i); i = Download::ScriptDownloads.erase(i);
continue;
}
if (download->isWorking())
{
download->notifyProgress();
++workingCount;
}
++i;
}
for(auto& download : Download::ScriptDownloads)
{
if (workingCount > 5) break;
if(!download->isWorking())
{
download->startWorking();
++workingCount;
} }
else ++i;
} }
}); });
@ -765,6 +786,8 @@ namespace Components
Download::ScriptDownloads.clear(); Download::ScriptDownloads.clear();
}); });
if (Dedicated::IsEnabled() || Flags::HasFlag("scriptablehttp"))
{
Script::AddFunction("httpGet", [](Game::scr_entref_t) Script::AddFunction("httpGet", [](Game::scr_entref_t)
{ {
if (Game::Scr_GetNumParam() < 1) return; if (Game::Scr_GetNumParam() < 1) return;
@ -774,7 +797,7 @@ namespace Components
Game::Scr_AddObject(object); Game::Scr_AddObject(object);
Download::ScriptDownloads.push_back({ url, object }); Download::ScriptDownloads.push_back(std::make_shared<ScriptDownload>(url, object));
Game::RemoveRefToObject(object); Game::RemoveRefToObject(object);
}); });
@ -783,16 +806,17 @@ namespace Components
if (Game::Scr_GetNumParam() < 1) return; if (Game::Scr_GetNumParam() < 1) return;
unsigned int object = Game::Scr_GetObject(0); unsigned int object = Game::Scr_GetObject(0);
for(auto& download : Download::ScriptDownloads) for (auto& download : Download::ScriptDownloads)
{ {
if(object == download.getObject()) if (object == download->getObject())
{ {
download.notifyDone(false, std::string()); download->cancel();
break; break;
} }
} }
}); });
} }
}
Download::~Download() Download::~Download()
{ {

View File

@ -1,4 +1,5 @@
#pragma once #pragma once
#include "Game/Functions.hpp"
namespace Components namespace Components
{ {
@ -86,35 +87,13 @@ namespace Components
class ScriptDownload class ScriptDownload
{ {
public: public:
ScriptDownload(std::string _url, unsigned int _object) : url(_url), object(_object), mgr(new mg_mgr), totalSize(0), receivedSize(0), done(false) ScriptDownload(std::string _url, unsigned int _object) : url(_url), object(_object), webIO(nullptr), done(false), notifyRequired(false), totalSize(0), currentSize(0)
{ {
Game::AddRefToObject(this->getObject()); Game::AddRefToObject(this->getObject());
ZeroMemory(this->getMgr(), sizeof(*this->getMgr()));
mg_mgr_init(this->getMgr(), this);
mg_connect_http(this->getMgr(), ScriptDownload::Handler, this->getUrl().data(), nullptr, nullptr);
} }
ScriptDownload(ScriptDownload&& other) noexcept ScriptDownload(ScriptDownload&& other) noexcept = delete;
{ ScriptDownload& operator=(ScriptDownload&& other) noexcept = delete;
this->operator=(std::move(other));
}
ScriptDownload& operator=(ScriptDownload&& other) noexcept
{
this->object = other.object;
this->done = other.done;
this->totalSize = other.totalSize;
this->receivedSize = other.receivedSize;
this->url = std::move(other.url);
this->mgr = std::move(other.mgr);
this->getMgr()->user_data = this;
other.object = 0;
return *this;
}
~ScriptDownload() ~ScriptDownload()
{ {
@ -122,105 +101,115 @@ namespace Components
{ {
Game::RemoveRefToObject(this->getObject()); Game::RemoveRefToObject(this->getObject());
this->object = 0; this->object = 0;
mg_mgr_free(this->getMgr());
}
} }
void work() if(this->workerThread.joinable())
{ {
mg_mgr_poll(this->getMgr(), 0); this->workerThread.join();
} }
void notifyProgress(size_t bytes) this->destroyWebIO();
{ }
this->receivedSize += bytes;
void startWorking()
{
if(!this->isWorking())
{
this->workerThread = std::thread(std::bind(&ScriptDownload::handler, this));
}
}
bool isWorking()
{
return this->workerThread.joinable();
}
void notifyProgress()
{
if (this->notifyRequired)
{
this->notifyRequired = false;
if (Game::Scr_IsSystemActive())
{
Game::Scr_AddInt(static_cast<int>(this->totalSize)); Game::Scr_AddInt(static_cast<int>(this->totalSize));
Game::Scr_AddInt(static_cast<int>(this->receivedSize)); Game::Scr_AddInt(static_cast<int>(this->currentSize));
Game::Scr_NotifyId(this->getObject(), Game::SL_GetString("progress", 0), 2); Game::Scr_NotifyId(this->getObject(), Game::SL_GetString("progress", 0), 2);
} }
void setSize(char* buf, size_t len)
{
if(buf && !this->totalSize)
{
std::string data(buf, len);
auto pos = data.find("Content-Length: ");
if(pos != std::string::npos)
{
data = data.substr(pos + 16);
pos = data.find("\r\n");
if (pos != std::string::npos)
{
data = data.substr(0, pos);
this->totalSize = size_t(atoll(data.data()));
return;
} }
} }
this->totalSize = ~0ul; void updateProgress(size_t _currentSize, size_t _toalSize)
}
}
void notifyDone(bool success, std::string result)
{ {
if (this->isDone()) return; this->currentSize = _currentSize;
this->totalSize = _toalSize;
this->notifyRequired = true;
}
Game::Scr_AddString(result.data()); // No binary data supported yet void notifyDone()
Game::Scr_AddInt(success); {
if (!this->isDone()) return;
if (Game::Scr_IsSystemActive())
{
Game::Scr_AddString(this->result.data()); // No binary data supported yet
Game::Scr_AddInt(this->success);
Game::Scr_NotifyId(this->getObject(), Game::SL_GetString("done", 0), 2); Game::Scr_NotifyId(this->getObject(), Game::SL_GetString("done", 0), 2);
}
this->done = true;
} }
bool isDone() { return this->done; }; bool isDone() { return this->done; };
std::string getUrl() { return this->url; } std::string getUrl() { return this->url; }
mg_mgr* getMgr() { return this->mgr.get(); }
unsigned int getObject() { return this->object; } unsigned int getObject() { return this->object; }
void cancel()
{
if(this->webIO)
{
this->webIO->cancelDownload();
}
}
private: private:
std::string url; std::string url;
std::string result;
unsigned int object; unsigned int object;
std::shared_ptr<mg_mgr> mgr; std::thread workerThread;
Utils::WebIO* webIO;
size_t totalSize;
size_t receivedSize;
bool done; bool done;
bool success;
bool notifyRequired;
size_t totalSize;
size_t currentSize;
static void Handler(mg_connection *nc, int ev, void* ev_data) void handler()
{ {
http_message* hm = reinterpret_cast<http_message*>(ev_data); this->destroyWebIO();
ScriptDownload* object = reinterpret_cast<ScriptDownload*>(nc->mgr->user_data);
if (ev == MG_EV_RECV) this->webIO = new Utils::WebIO("IW4x");
{ this->webIO->setProgressCallback(std::bind(&ScriptDownload::updateProgress, this, std::placeholders::_1, std::placeholders::_2));
object->setSize(nc->recv_mbuf.buf, nc->recv_mbuf.len);
size_t bytes = static_cast<size_t>(*reinterpret_cast<int*>(ev_data)); this->result = this->webIO->get(this->url, &this->success);
object->notifyProgress(bytes);
this->destroyWebIO();
this->done = true;
} }
else if (ev == MG_EV_HTTP_REPLY)
void destroyWebIO()
{ {
object->notifyDone(true, std::string(hm->body.p, hm->body.len)); if (this->webIO)
}
else if(ev == MG_EV_CONNECT)
{ {
if (*static_cast<int*>(ev_data)) delete this->webIO;
{ this->webIO = nullptr;
object->notifyDone(false, std::string());
}
} }
} }
}; };
static mg_mgr Mgr; static mg_mgr Mgr;
static ClientDownload CLDownload; static ClientDownload CLDownload;
static std::vector<ScriptDownload> ScriptDownloads; static std::vector<std::shared_ptr<ScriptDownload>> ScriptDownloads;
static void EventHandler(mg_connection *nc, int ev, void *ev_data); static void EventHandler(mg_connection *nc, int ev, void *ev_data);
static void ListHandler(mg_connection *nc, int ev, void *ev_data); static void ListHandler(mg_connection *nc, int ev, void *ev_data);

View File

@ -2,6 +2,7 @@
namespace Components namespace Components
{ {
int QuickPatch::FrameTime = 0;
bool QuickPatch::ReadyPassed = false; bool QuickPatch::ReadyPassed = false;
Utils::Signal<QuickPatch::Callback> QuickPatch::ReadySignal; Utils::Signal<QuickPatch::Callback> QuickPatch::ReadySignal;
Utils::Signal<QuickPatch::Callback> QuickPatch::ShutdownSignal; Utils::Signal<QuickPatch::Callback> QuickPatch::ShutdownSignal;
@ -198,6 +199,12 @@ namespace Components
QuickPatch::ReadyPassed = false; QuickPatch::ReadyPassed = false;
QuickPatch::Once(QuickPatch::ReadyHandler); QuickPatch::Once(QuickPatch::ReadyHandler);
QuickPatch::FrameTime = 0;
QuickPatch::OnFrame([]()
{
QuickPatch::FrameTime = Game::Sys_Milliseconds();
});
// Make sure preDestroy is called when the game shuts down // Make sure preDestroy is called when the game shuts down
QuickPatch::OnShutdown(Loader::PreDestroy); QuickPatch::OnShutdown(Loader::PreDestroy);

View File

@ -21,8 +21,10 @@ namespace Components
static void OnFrame(Utils::Slot<Callback> callback); static void OnFrame(Utils::Slot<Callback> callback);
static void OnReady(Utils::Slot<Callback> callback); static void OnReady(Utils::Slot<Callback> callback);
static void Once(Utils::Slot<Callback> callback); static void Once(Utils::Slot<Callback> callback);
static int GetFrameTime() { return FrameTime; }
private: private:
static int FrameTime;
static bool ReadyPassed; static bool ReadyPassed;
static Utils::Signal<Callback> ReadySignal; static Utils::Signal<Callback> ReadySignal;
static Utils::Signal<Callback> ShutdownSignal; static Utils::Signal<Callback> ShutdownSignal;

View File

@ -230,6 +230,7 @@ namespace Game
Scr_RegisterFunction_t Scr_RegisterFunction = Scr_RegisterFunction_t(0x492D50); Scr_RegisterFunction_t Scr_RegisterFunction = Scr_RegisterFunction_t(0x492D50);
Scr_ShutdownAllocNode_t Scr_ShutdownAllocNode = Scr_ShutdownAllocNode_t(0x441650); Scr_ShutdownAllocNode_t Scr_ShutdownAllocNode = Scr_ShutdownAllocNode_t(0x441650);
Scr_IsSystemActive_t Scr_IsSystemActive = Scr_IsSystemActive_t(0x4B24E0);
Script_Alloc_t Script_Alloc = Script_Alloc_t(0x422E70); Script_Alloc_t Script_Alloc = Script_Alloc_t(0x422E70);
Script_SetupTokens_t Script_SetupTokens = Script_SetupTokens_t(0x4E6950); Script_SetupTokens_t Script_SetupTokens = Script_SetupTokens_t(0x4E6950);

View File

@ -571,6 +571,9 @@ namespace Game
typedef void(__cdecl * Scr_RegisterFunction_t)(scr_function_t function); typedef void(__cdecl * Scr_RegisterFunction_t)(scr_function_t function);
extern Scr_RegisterFunction_t Scr_RegisterFunction; extern Scr_RegisterFunction_t Scr_RegisterFunction;
typedef bool(__cdecl * Scr_IsSystemActive_t)();
extern Scr_IsSystemActive_t Scr_IsSystemActive;
typedef script_t* (__cdecl * Script_Alloc_t)(int length); typedef script_t* (__cdecl * Script_Alloc_t)(int length);
extern Script_Alloc_t Script_Alloc; extern Script_Alloc_t Script_Alloc;

View File

@ -10,7 +10,7 @@ namespace Utils
this->setURL(url); this->setURL(url);
} }
WebIO::WebIO(std::string useragent) : timeout(5000) // 5 seconds timeout by default WebIO::WebIO(std::string useragent) : cancel(false), timeout(5000), hSession(nullptr) // 5 seconds timeout by default
{ {
this->openSession(useragent); this->openSession(useragent);
} }
@ -26,12 +26,13 @@ namespace Utils
void WebIO::openSession(std::string useragent) void WebIO::openSession(std::string useragent)
{ {
this->closeSession();
this->hSession = InternetOpenA(useragent.data(), INTERNET_OPEN_TYPE_DIRECT, nullptr, nullptr, 0); this->hSession = InternetOpenA(useragent.data(), INTERNET_OPEN_TYPE_DIRECT, nullptr, nullptr, 0);
} }
void WebIO::closeSession() void WebIO::closeSession()
{ {
InternetCloseHandle(this->hSession); if(this->hSession) InternetCloseHandle(this->hSession);
} }
void WebIO::SetCredentials(std::string _username, std::string _password) void WebIO::SetCredentials(std::string _username, std::string _password)
@ -183,37 +184,37 @@ namespace Utils
return this->execute("POST", body, headers); return this->execute("POST", body, headers);
} }
std::string WebIO::post(std::string _url, std::string body) std::string WebIO::post(std::string _url, std::string body, bool* success)
{ {
this->setURL(_url); this->setURL(_url);
return this->post(body); return this->post(body, success);
} }
std::string WebIO::post(std::string _url, WebIO::Params params) std::string WebIO::post(std::string _url, WebIO::Params params, bool* success)
{ {
this->setURL(_url); this->setURL(_url);
return this->post(params); return this->post(params, success);
} }
std::string WebIO::post(WebIO::Params params) std::string WebIO::post(WebIO::Params params, bool* success)
{ {
return this->post(this->buildPostBody(params)); return this->post(this->buildPostBody(params), success);
} }
std::string WebIO::post(std::string body) std::string WebIO::post(std::string body, bool* success)
{ {
return this->execute("POST", body); return this->execute("POST", body, WebIO::Params(), success);
} }
std::string WebIO::get(std::string _url) std::string WebIO::get(std::string _url, bool* success)
{ {
this->setURL(_url); this->setURL(_url);
return this->get(); return this->get(success);
} }
std::string WebIO::get() std::string WebIO::get(bool* success)
{ {
return this->execute("GET", ""); return this->execute("GET", "", WebIO::Params(), success);
} }
bool WebIO::openConnection() bool WebIO::openConnection()
@ -257,8 +258,9 @@ namespace Utils
return this; return this;
} }
std::string WebIO::execute(const char* command, std::string body, WebIO::Params headers) std::string WebIO::execute(const char* command, std::string body, WebIO::Params headers, bool* success)
{ {
if (success) *success = false;
if (!this->openConnection()) return ""; if (!this->openConnection()) return "";
const char *acceptTypes[] = { "application/x-www-form-urlencoded", nullptr }; const char *acceptTypes[] = { "application/x-www-form-urlencoded", nullptr };
@ -307,19 +309,36 @@ namespace Utils
return ""; return "";
} }
DWORD contentLength = 0;
length = sizeof(statusCode);
if (HttpQueryInfo(this->hFile, HTTP_QUERY_FLAG_NUMBER | HTTP_QUERY_CONTENT_LENGTH, &contentLength, &length, nullptr) == FALSE)
{
this->closeConnection();
return "";
}
std::string returnBuffer; std::string returnBuffer;
returnBuffer.reserve(contentLength);
DWORD size = 0; DWORD size = 0;
char buffer[0x2001] = { 0 }; char buffer[0x2001] = { 0 };
while (InternetReadFile(this->hFile, buffer, 0x2000, &size)) while (InternetReadFile(this->hFile, buffer, 0x2000, &size))
{ {
if(this->cancel)
{
this->closeConnection();
return "";
}
returnBuffer.append(buffer, size); returnBuffer.append(buffer, size);
if (this->progressCallback) this->progressCallback(returnBuffer.size(), contentLength);
if (!size) break; if (!size) break;
} }
this->closeConnection(); this->closeConnection();
if (success) *success = true;
return returnBuffer; return returnBuffer;
} }
@ -539,4 +558,9 @@ namespace Utils
return false; return false;
} }
void WebIO::setProgressCallback(std::function<void(size_t, size_t)> callback)
{
this->progressCallback = callback;
}
} }

View File

@ -30,13 +30,13 @@ namespace Utils
std::string postFile(std::string url, std::string data, std::string fieldName, std::string fileName); std::string postFile(std::string url, std::string data, std::string fieldName, std::string fileName);
std::string postFile(std::string data, std::string fieldName, std::string fileName); std::string postFile(std::string data, std::string fieldName, std::string fileName);
std::string post(std::string url, WebIO::Params params); std::string post(std::string url, WebIO::Params params, bool* success= nullptr);
std::string post(std::string url, std::string body); std::string post(std::string url, std::string body, bool* success = nullptr);
std::string post(WebIO::Params params); std::string post(WebIO::Params params, bool* success = nullptr);
std::string post(std::string body); std::string post(std::string body, bool* success = nullptr);
std::string get(std::string url); std::string get(std::string url, bool* success = nullptr);
std::string get(); std::string get(bool* success = nullptr);
WebIO* setTimeout(DWORD mseconds); WebIO* setTimeout(DWORD mseconds);
@ -62,6 +62,9 @@ namespace Utils
bool uploadFileData(std::string file, std::string data); bool uploadFileData(std::string file, std::string data);
bool downloadFileData(std::string file, std::string &data); bool downloadFileData(std::string file, std::string &data);
void setProgressCallback(std::function<void(size_t, size_t)> callback);
void cancelDownload() { this->cancel = true; }
private: private:
enum Command enum Command
@ -79,6 +82,8 @@ namespace Utils
std::string raw; std::string raw;
}; };
bool cancel;
bool isFTP; bool isFTP;
std::string username; std::string username;
std::string password; std::string password;
@ -91,11 +96,13 @@ namespace Utils
DWORD timeout; DWORD timeout;
std::function<void(size_t, size_t)> progressCallback;
std::string buildPostBody(WebIO::Params params); std::string buildPostBody(WebIO::Params params);
bool isSecuredConnection(); bool isSecuredConnection();
std::string execute(const char* command, std::string body, WebIO::Params headers = WebIO::Params()); std::string execute(const char* command, std::string body, WebIO::Params headers = WebIO::Params(), bool* success = nullptr);
bool listElements(std::string directory, std::vector<std::string> &list, bool files); bool listElements(std::string directory, std::vector<std::string> &list, bool files);