From 195f6bb2976aabad92773ec685e8f89487eb2d7b Mon Sep 17 00:00:00 2001 From: TheApadayo Date: Mon, 12 Jun 2017 19:22:39 -0400 Subject: [PATCH 1/6] [Download] Added external server download. Still needs error checking to see if the file doesn't exist to revert back to the original host. Completely untested cause i can't get my dedi to start right now. --- src/Components/Modules/Download.cpp | 33 ++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/Components/Modules/Download.cpp b/src/Components/Modules/Download.cpp index 593e5ecf..56fbd15e 100644 --- a/src/Components/Modules/Download.cpp +++ b/src/Components/Modules/Download.cpp @@ -192,7 +192,32 @@ namespace Components } } - std::string url = "http://" + download->target.getString() + "/file/" + (download->isMap ? "map/" : "") + file.name; + std::string host = "http://" + download->target.getString(); + std::string fastHost = "http://" + Dvar::Var("sv_wwwBaseUrl").get(); + + std::string url; + + // file directory for fasthost looks like this + // /-usermaps + // /-mp_test + // -mp_test.ff + // -mp_test.iwd + // /-mp_whatever + // /-mp_whatever.ff + // /-mods + // /-mod1 + // -mod1.iwd + // -mod.ff + // /-mod2 + // ... + if (Dvar::Var("sv_wwwDownload").get()) + { + url = fastHost + path; + } + else + { + url = host + "/file/" + (download->isMap ? "map/" : "") + file.name; + } Download::FileDownload fDownload; fDownload.file = file; @@ -738,6 +763,12 @@ namespace Components mg_mgr_poll(&Download::Mgr, 100); } }); + + Dvar::OnInit([]() + { + Dvar::Register("sv_wwwDownload", false, Game::dvar_flag::DVAR_FLAG_DEDISAVED, "Set to true to enable downloading maps/mods from an external server."); + Dvar::Register("sv_wwwBaseUrl", "", Game::dvar_flag::DVAR_FLAG_DEDISAVED, "Set to the base url for the external map download."); + }); } else { From 579fa0a87c2aa139926fecf96628aedff12cf547 Mon Sep 17 00:00:00 2001 From: TheApadayo Date: Thu, 15 Jun 2017 12:52:29 -0400 Subject: [PATCH 2/6] [Download] send base url as part of inforesponse. also set client dvar correctly for downloader. --- src/Components/Modules/Download.cpp | 2 ++ src/Components/Modules/Party.cpp | 39 ++++++++++++++++++++--------- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/Components/Modules/Download.cpp b/src/Components/Modules/Download.cpp index 56fbd15e..5776e01b 100644 --- a/src/Components/Modules/Download.cpp +++ b/src/Components/Modules/Download.cpp @@ -777,6 +777,8 @@ namespace Components Dvar::Register("ui_dl_timeLeft", "", Game::dvar_flag::DVAR_FLAG_NONE, ""); Dvar::Register("ui_dl_progress", "", Game::dvar_flag::DVAR_FLAG_NONE, ""); Dvar::Register("ui_dl_transRate", "", Game::dvar_flag::DVAR_FLAG_NONE, ""); + Dvar::Register("sv_wwwDownload", false, Game::dvar_flag::DVAR_FLAG_DEDISAVED, "Set to true to enable downloading maps/mods from an external server."); + Dvar::Register("sv_wwwBaseUrl", "", Game::dvar_flag::DVAR_FLAG_DEDISAVED, "Set to the base url for the external map download."); }); UIScript::Add("mod_download_cancel", [] (UIScript::Token) diff --git a/src/Components/Modules/Party.cpp b/src/Components/Modules/Party.cpp index 43614c6c..4abcda84 100644 --- a/src/Components/Modules/Party.cpp +++ b/src/Components/Modules/Party.cpp @@ -47,7 +47,7 @@ namespace Components { return Utils::String::VA("%d", address.getIP().full); } - else if (key =="port") + else if (key == "port") { return Utils::String::VA("%d", address.getPort()); } @@ -154,7 +154,7 @@ namespace Components Utils::Hook::Set(0x5AC2CF, 0xEB); // CL_ParseGamestate Utils::Hook::Set(0x5AC2C3, 0xEB); // CL_ParseGamestate - // AnonymousAddRequest + // AnonymousAddRequest Utils::Hook::Set(0x5B5E18, 0xEB); Utils::Hook::Set(0x5B5E64, 0xEB); Utils::Hook::Nop(0x5B5E5C, 2); @@ -217,7 +217,7 @@ namespace Components Utils::Hook::Set(0x4D6171, 0); Utils::Hook::Nop(0x4077A1, 5); // PartyMigrate_Frame - // Patch playlist stuff for non-party behavior + // Patch playlist stuff for non-party behavior Utils::Hook::Set(0x4A4093, &partyEnable); Utils::Hook::Set(0x4573F1, &partyEnable); Utils::Hook::Set(0x5B1A0C, &partyEnable); @@ -249,14 +249,14 @@ namespace Components // Patch Live_PlayerHasLoopbackAddr //Utils::Hook::Set(0x418F30, 0x90C3C033); - Command::Add("connect", [] (Command::Params* params) + Command::Add("connect", [](Command::Params* params) { if (params->length() < 2) { return; } - if(Game::CL_IsCgameInitialized()) + if (Game::CL_IsCgameInitialized()) { Command::Execute("disconnect", false); Command::Execute(Utils::String::VA("%s", params->join(0).data()), false); @@ -266,12 +266,12 @@ namespace Components Party::Connect(Network::Address(params->get(1))); } }); - Command::Add("reconnect", [] (Command::Params*) + Command::Add("reconnect", [](Command::Params*) { Party::Connect(Party::Container.target); }); - Scheduler::OnFrame([] () + Scheduler::OnFrame([]() { if (Party::Container.valid) { @@ -293,7 +293,7 @@ namespace Components }, true); // Basic info handler - Network::Handle("getInfo", [] (Network::Address address, std::string data) + Network::Handle("getInfo", [](Network::Address address, std::string data) { int botCount = 0; int clientCount = 0; @@ -370,10 +370,13 @@ namespace Components info.set("matchtype", "0"); } + info.set("wwwDownload", (Dvar::Var("sv_wwwDownload").get() ? "1" : "0")); + info.set("wwwUrl", Dvar::Var("sv_wwwBaseUrl").get()); + Network::SendCommand(address, "infoResponse", "\\" + info.build()); }); - Network::Handle("infoResponse", [] (Network::Address address, std::string data) + Network::Handle("infoResponse", [](Network::Address address, std::string data) { Utils::InfoString info(data); @@ -393,6 +396,18 @@ namespace Components std::string mod = Dvar::Var("fs_game").get(); + // set fast server stuff here so its updated when we go to download stuff + if (info.get("wwwDownload") == "1"s) + { + Dvar::Var("sv_wwwDownload").set(true); + Dvar::Var("sv_wwwBaseUrl").set(info.get("wwwUrl")); + } + else + { + Dvar::Var("sv_wwwDownload").set(false); + Dvar::Var("sv_wwwBaseUrl").set(""); + } + if (info.get("challenge") != Party::Container.challenge) { Party::ConnectError("Invalid join response: Challenge mismatch."); @@ -407,7 +422,7 @@ namespace Components { Party::ConnectError("Server is not hosting a match."); } - else if(Party::Container.matchType > 2 || Party::Container.matchType < 0) + else if (Party::Container.matchType > 2 || Party::Container.matchType < 0) { Party::ConnectError("Invalid join response: Unknown matchtype"); } @@ -415,12 +430,12 @@ namespace Components { Party::ConnectError("Invalid map or gametype."); } - else if(isUsermap && usermapHash != Maps::GetUsermapHash(info.get("mapname"))) + else if (isUsermap && usermapHash != Maps::GetUsermapHash(info.get("mapname"))) { Command::Execute("closemenu popup_reconnectingtoparty"); Download::InitiateMapDownload(info.get("mapname")); } - else if(!info.get("fs_game").empty() && Utils::String::ToLower(mod) != Utils::String::ToLower(info.get("fs_game"))) + else if (!info.get("fs_game").empty() && Utils::String::ToLower(mod) != Utils::String::ToLower(info.get("fs_game"))) { Command::Execute("closemenu popup_reconnectingtoparty"); Download::InitiateClientDownload(info.get("fs_game")); From e86fff0b4afecaf45edb6a527b5aa4dc19ae540c Mon Sep 17 00:00:00 2001 From: /dev/full Date: Sat, 17 Jun 2017 00:44:31 +0200 Subject: [PATCH 3/6] [Changelog] Update CHANGELOG.md --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2db21794..c45e3619 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,11 +9,13 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0. ### Added - Show friend avatars when they play IW4x (request) +- Implement cursor with hourglass while loading fastfiles +- Add toast if an update is available when the game starts +- Show bots in parenthesis after the number of players in the serverlist(request) ### Changed - ### Fixed - Fix lags and frame drops caused by server sorting From 7805b012495414b6f662e05e029583404baa4e6c Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 18 Jun 2017 01:27:28 +0200 Subject: [PATCH 4/6] [Changelog] Update changelog --- CHANGELOG.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c45e3619..4746083e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,18 +8,18 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0. ### Added -- Show friend avatars when they play IW4x (request) -- Implement cursor with hourglass while loading fastfiles -- Add toast if an update is available when the game starts -- Show bots in parenthesis after the number of players in the serverlist(request) +- Display a toast when an update is available. +- Use the hourglass cursor while loading assets (with the native cursor feature). +- Show bots in parenthesis after the number of players in the serverlist (request). ### Changed +- Show friend avatars when they play IW4x (request). ### Fixed -- Fix lags and frame drops caused by server sorting -- Fix demos on custom maps +- Fix lags and frame drops caused by server sorting. +- Fix demos on custom maps. ## [0.5.0] - 2017-06-04 From 491cf4caf8c28459fb7a1ab752c6428ce1f9cdd0 Mon Sep 17 00:00:00 2001 From: TheApadayo Date: Mon, 19 Jun 2017 15:39:48 -0400 Subject: [PATCH 5/6] [Download] Don't allow downloading of mod files when the client provides an invalid password -also don't even leave the menu if there is no password set when connecting to a private server --- src/Components/Modules/Download.cpp | 68 +++++++++++++++++++++++++---- src/Components/Modules/Download.hpp | 8 +++- src/Components/Modules/Party.cpp | 8 +++- 3 files changed, 71 insertions(+), 13 deletions(-) diff --git a/src/Components/Modules/Download.cpp b/src/Components/Modules/Download.cpp index 85e6f59a..27d1439a 100644 --- a/src/Components/Modules/Download.cpp +++ b/src/Components/Modules/Download.cpp @@ -11,12 +11,12 @@ namespace Components #pragma region Client - void Download::InitiateMapDownload(std::string map) + void Download::InitiateMapDownload(std::string map, bool needPassword) { - Download::InitiateClientDownload(map, true); + Download::InitiateClientDownload(map, needPassword, true); } - void Download::InitiateClientDownload(std::string mod, bool map) + void Download::InitiateClientDownload(std::string mod, bool needPassword, bool map) { if (Download::CLDownload.running) return; @@ -29,6 +29,20 @@ namespace Components Command::Execute("openmenu mod_download_popmenu", false); + if (needPassword) + { + std::string pass = Dvar::Var("password").get(); + if (!pass.length()) + { + // shouldn't ever happen but this is safe + Party::ConnectError("A password is required to connect to this server!"); + return; + } + + Download::CLDownload.isPrivate = needPassword; + Download::CLDownload.hashedPassword = Utils::Cryptography::SHA256::Compute(pass); + } + Download::CLDownload.running = true; Download::CLDownload.isMap = map; Download::CLDownload.mod = mod; @@ -192,7 +206,8 @@ namespace Components } } - std::string url = "http://" + download->target.getString() + "/file/" + (download->isMap ? "map/" : "") + file.name; + std::string url = "http://" + download->target.getString() + "/file/" + (download->isMap ? "map/" : "") + file.name + + (download->isPrivate ? ("?password=" + download->hashedPassword) : ""); Download::FileDownload fDownload; fDownload.file = file; @@ -233,7 +248,9 @@ namespace Components std::string host = "http://" + download->target.getString(); - std::string list = Utils::WebIO("IW4x", host + (download->isMap ? "/map" : "/list")).setTimeout(5000)->get(); + std::string listUrl = host + (download->isMap ? "/map" : "/list") + (download->isPrivate ? ("?password=" + download->hashedPassword) : ""); + + std::string list = Utils::WebIO("IW4x", listUrl).setTimeout(5000)->get(); if (list.empty()) { if (download->terminateThread) return; @@ -361,6 +378,33 @@ namespace Components return nullptr; } + bool Download::VerifyPassword(mg_connection *nc, http_message* message) + { + std::string g_password = Dvar::Var("g_password").get(); + + if (!g_password.size()) return true; + + Utils::Memory::Allocator* alloc = Utils::Memory::GetAllocator(); + + // sha256 hashes are 64 chars long but we're gonna be safe here + char* buffer = alloc->allocateArray(128); + int passLen = mg_get_http_var(&message->query_string, "password", buffer, 128); + + if (passLen <= 0 || std::string(buffer, passLen) != g_password)//Utils::Cryptography::SHA256::Compute(g_password)) + { + mg_printf(nc, ("HTTP/1.1 403 Forbidden\r\n"s + + "Content-Type: text/html\r\n"s + + "Connection: close\r\n"s + + "\r\n"s + + ((passLen == 0) ? "Password Required"s : "Invalid Password"s)).c_str()); + + nc->flags |= MG_F_SEND_AND_CLOSE; + return false; + } + + return true; + } + void Download::Forbid(mg_connection *nc) { mg_printf(nc, "HTTP/1.1 403 Forbidden\r\n" @@ -372,11 +416,13 @@ namespace Components nc->flags |= MG_F_SEND_AND_CLOSE; } - void Download::MapHandler(mg_connection *nc, int ev, void* /*ev_data*/) + void Download::MapHandler(mg_connection *nc, int ev, void* ev_data) { // Only handle http requests if (ev != MG_EV_HTTP_REQUEST) return; + if (!Download::VerifyPassword(nc, reinterpret_cast(ev_data))) return; + static std::string mapnamePre; static json11::Json jsonList; @@ -423,11 +469,13 @@ namespace Components nc->flags |= MG_F_SEND_AND_CLOSE; } - void Download::ListHandler(mg_connection* nc, int ev, void* /*ev_data*/) + void Download::ListHandler(mg_connection* nc, int ev, void* ev_data) { // Only handle http requests if (ev != MG_EV_HTTP_REQUEST) return; + if (!Download::VerifyPassword(nc, reinterpret_cast(ev_data))) return; + // if (!Download::IsClient(nc)) // { // Download::Forbid(nc); @@ -487,6 +535,8 @@ namespace Components http_message* message = reinterpret_cast(ev_data); + //if (!Download::VerifyPassword(nc, message)) return; + // if (!Download::IsClient(nc)) // { // Download::Forbid(nc); @@ -567,12 +617,12 @@ namespace Components } } - void Download::InfoHandler(mg_connection* nc, int ev, void* /*ev_data*/) + void Download::InfoHandler(mg_connection* nc, int ev, void* ev_data) { // Only handle http requests if (ev != MG_EV_HTTP_REQUEST) return; - //http_message* message = reinterpret_cast(ev_data); + //if (!Download::VerifyPassword(nc, reinterpret_cast(ev_data))) return; Utils::InfoString status = ServerInfo::GetInfo(); diff --git a/src/Components/Modules/Download.hpp b/src/Components/Modules/Download.hpp index 186f237e..911e983e 100644 --- a/src/Components/Modules/Download.hpp +++ b/src/Components/Modules/Download.hpp @@ -11,8 +11,8 @@ namespace Components void preDestroy() override; - static void InitiateClientDownload(std::string mod, bool map = false); - static void InitiateMapDownload(std::string map); + static void InitiateClientDownload(std::string mod, bool needPassword, bool map = false); + static void InitiateMapDownload(std::string map, bool needPassword); private: class ClientDownload @@ -25,8 +25,10 @@ namespace Components bool valid; bool terminateThread; bool isMap; + bool isPrivate; mg_mgr mgr; Network::Address target; + std::string hashedPassword; std::string mod; std::thread thread; @@ -209,6 +211,8 @@ namespace Components static std::thread ServerThread; static bool Terminate; + static bool VerifyPassword(mg_connection *nc, http_message* message); + static void EventHandler(mg_connection *nc, int ev, void *ev_data); static void ListHandler(mg_connection *nc, int ev, void *ev_data); static void MapHandler(mg_connection *nc, int ev, void *ev_data); diff --git a/src/Components/Modules/Party.cpp b/src/Components/Modules/Party.cpp index fe00e498..bea7413a 100644 --- a/src/Components/Modules/Party.cpp +++ b/src/Components/Modules/Party.cpp @@ -415,15 +415,19 @@ namespace Components { Party::ConnectError("Invalid map or gametype."); } + else if (Party::Container.info.get("isPrivate") == "1"s && !Dvar::Var("password").get().length()) + { + Party::ConnectError("A password is required to join this server! Set it at the bottom of the serverlist."); + } else if (isUsermap && usermapHash != Maps::GetUsermapHash(info.get("mapname"))) { Command::Execute("closemenu popup_reconnectingtoparty"); - Download::InitiateMapDownload(info.get("mapname")); + Download::InitiateMapDownload(info.get("mapname"), info.get("isPrivate") == "1"); } else if (!info.get("fs_game").empty() && Utils::String::ToLower(mod) != Utils::String::ToLower(info.get("fs_game"))) { Command::Execute("closemenu popup_reconnectingtoparty"); - Download::InitiateClientDownload(info.get("fs_game")); + Download::InitiateClientDownload(info.get("fs_game"), info.get("isPrivate") == "1"s); } else if (!Dvar::Var("fs_game").get().empty() && info.get("fs_game").empty()) { From 743841731e553a5f5c0ea3504ea4e77398b512f1 Mon Sep 17 00:00:00 2001 From: TheApadayo Date: Mon, 19 Jun 2017 15:44:59 -0400 Subject: [PATCH 6/6] [Download] Always set private state --- src/Components/Modules/Download.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Components/Modules/Download.cpp b/src/Components/Modules/Download.cpp index 27d1439a..207de653 100644 --- a/src/Components/Modules/Download.cpp +++ b/src/Components/Modules/Download.cpp @@ -38,8 +38,6 @@ namespace Components Party::ConnectError("A password is required to connect to this server!"); return; } - - Download::CLDownload.isPrivate = needPassword; Download::CLDownload.hashedPassword = Utils::Cryptography::SHA256::Compute(pass); } @@ -51,6 +49,7 @@ namespace Components Download::CLDownload.lastTimeStamp = 0; Download::CLDownload.downBytes = 0; Download::CLDownload.timeStampBytes = 0; + Download::CLDownload.isPrivate = needPassword; Download::CLDownload.target = Party::Target(); Download::CLDownload.thread = std::thread(Download::ModDownloader, &Download::CLDownload); }