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

View File

@ -4,7 +4,7 @@ namespace Components
{
mg_mgr Download::Mgr;
Download::ClientDownload Download::CLDownload;
std::vector<Download::ScriptDownload> Download::ScriptDownloads;
std::vector<std::shared_ptr<Download::ScriptDownload>> Download::ScriptDownloads;
#pragma region Client
@ -748,15 +748,36 @@ namespace Components
QuickPatch::OnFrame([]()
{
int workingCount = 0;
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);
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,33 +786,36 @@ namespace Components
Download::ScriptDownloads.clear();
});
Script::AddFunction("httpGet", [](Game::scr_entref_t)
if (Dedicated::IsEnabled() || Flags::HasFlag("scriptablehttp"))
{
if (Game::Scr_GetNumParam() < 1) return;
std::string url = Game::Scr_GetString(0);
unsigned int object = Game::AllocObject();
Game::Scr_AddObject(object);
Download::ScriptDownloads.push_back({ url, object });
Game::RemoveRefToObject(object);
});
Script::AddFunction("httpCancel", [](Game::scr_entref_t)
{
if (Game::Scr_GetNumParam() < 1) return;
unsigned int object = Game::Scr_GetObject(0);
for(auto& download : Download::ScriptDownloads)
Script::AddFunction("httpGet", [](Game::scr_entref_t)
{
if(object == download.getObject())
if (Game::Scr_GetNumParam() < 1) return;
std::string url = Game::Scr_GetString(0);
unsigned int object = Game::AllocObject();
Game::Scr_AddObject(object);
Download::ScriptDownloads.push_back(std::make_shared<ScriptDownload>(url, object));
Game::RemoveRefToObject(object);
});
Script::AddFunction("httpCancel", [](Game::scr_entref_t)
{
if (Game::Scr_GetNumParam() < 1) return;
unsigned int object = Game::Scr_GetObject(0);
for (auto& download : Download::ScriptDownloads)
{
download.notifyDone(false, std::string());
break;
if (object == download->getObject())
{
download->cancel();
break;
}
}
}
});
});
}
}
Download::~Download()

View File

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

View File

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

View File

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

View File

@ -230,6 +230,7 @@ namespace Game
Scr_RegisterFunction_t Scr_RegisterFunction = Scr_RegisterFunction_t(0x492D50);
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_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);
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);
extern Script_Alloc_t Script_Alloc;

View File

@ -10,7 +10,7 @@ namespace Utils
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);
}
@ -26,12 +26,13 @@ namespace Utils
void WebIO::openSession(std::string useragent)
{
this->closeSession();
this->hSession = InternetOpenA(useragent.data(), INTERNET_OPEN_TYPE_DIRECT, nullptr, nullptr, 0);
}
void WebIO::closeSession()
{
InternetCloseHandle(this->hSession);
if(this->hSession) InternetCloseHandle(this->hSession);
}
void WebIO::SetCredentials(std::string _username, std::string _password)
@ -183,37 +184,37 @@ namespace Utils
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);
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);
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);
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()
@ -257,8 +258,9 @@ namespace Utils
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 "";
const char *acceptTypes[] = { "application/x-www-form-urlencoded", nullptr };
@ -307,19 +309,36 @@ namespace Utils
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;
returnBuffer.reserve(contentLength);
DWORD size = 0;
char buffer[0x2001] = { 0 };
while (InternetReadFile(this->hFile, buffer, 0x2000, &size))
{
if(this->cancel)
{
this->closeConnection();
return "";
}
returnBuffer.append(buffer, size);
if (this->progressCallback) this->progressCallback(returnBuffer.size(), contentLength);
if (!size) break;
}
this->closeConnection();
if (success) *success = true;
return returnBuffer;
}
@ -539,4 +558,9 @@ namespace Utils
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 data, std::string fieldName, std::string fileName);
std::string post(std::string url, WebIO::Params params);
std::string post(std::string url, std::string body);
std::string post(WebIO::Params params);
std::string post(std::string body);
std::string post(std::string url, WebIO::Params params, bool* success= nullptr);
std::string post(std::string url, std::string body, bool* success = nullptr);
std::string post(WebIO::Params params, bool* success = nullptr);
std::string post(std::string body, bool* success = nullptr);
std::string get(std::string url);
std::string get();
std::string get(std::string url, bool* success = nullptr);
std::string get(bool* success = nullptr);
WebIO* setTimeout(DWORD mseconds);
@ -62,6 +62,9 @@ namespace Utils
bool uploadFileData(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:
enum Command
@ -79,6 +82,8 @@ namespace Utils
std::string raw;
};
bool cancel;
bool isFTP;
std::string username;
std::string password;
@ -91,11 +96,13 @@ namespace Utils
DWORD timeout;
std::function<void(size_t, size_t)> progressCallback;
std::string buildPostBody(WebIO::Params params);
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);