diff --git a/include/Common.h b/include/Common.h index 74ec111..cc5fb94 100644 --- a/include/Common.h +++ b/include/Common.h @@ -21,6 +21,7 @@ struct Version { uint8_t minor; uint8_t patch; Version(uint8_t major, uint8_t minor, uint8_t patch); + Version(const std::array& v); std::string AsString(); }; @@ -81,8 +82,8 @@ public: static std::string GetBackup2Hostname() { return "backup2.beammp.com"; } static std::string GetBackendUrlForSocketIO() { return "https://backend.beammp.com"; } static void CheckForUpdates(); - static std::array VersionStrToInts(const std::string& str); - static bool IsOutdated(const std::array& Current, const std::array& Newest); + static std::array VersionStrToInts(const std::string& str); + static bool IsOutdated(const Version& Current, const Version& Newest); private: static inline std::string mPPS; @@ -163,4 +164,4 @@ void LogChatMessage(const std::string& name, int id, const std::string& msg); std::string Comp(std::string Data); std::string DeComp(std::string Compressed); -std::string GetPlatformAgnosticErrorString(); \ No newline at end of file +std::string GetPlatformAgnosticErrorString(); diff --git a/include/Http.h b/include/Http.h index 48bae69..daf38da 100644 --- a/include/Http.h +++ b/include/Http.h @@ -5,8 +5,9 @@ namespace Http { std::string GET(const std::string& host, int port, const std::string& target, unsigned int* status = nullptr); -std::string POST(const std::string& host, const std::string& target, const std::unordered_map& fields, const std::string& body, bool json, int* status = nullptr); +std::string POST(const std::string& host, int port, const std::string& target, const std::unordered_map& fields, const std::string& body, const std::string& ContentType, unsigned int* status = nullptr); namespace Status { std::string ToString(int code); } -} \ No newline at end of file +const std::string ErrorString = "-1"; +} diff --git a/src/Common.cpp b/src/Common.cpp index 915c216..26e5f6b 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -30,10 +30,14 @@ void Application::GracefullyShutdown() { } } -std::array Application::VersionStrToInts(const std::string& str) { - std::array Version; +std::string Application::ServerVersionString() { + return mVersion.AsString(); +} + +std::array Application::VersionStrToInts(const std::string& str) { + std::array Version; std::stringstream ss(str); - for (int& i : Version) { + for (uint8_t& i : Version) { std::string Part; std::getline(ss, Part, '.'); std::from_chars(&*Part.begin(), &*Part.begin() + Part.size(), i); @@ -41,12 +45,13 @@ std::array Application::VersionStrToInts(const std::string& str) { return Version; } -bool Application::IsOutdated(const std::array& Current, const std::array& Newest) { - if (Newest[0] > Current[0]) { +// FIXME: This should be used by operator< on Version +bool Application::IsOutdated(const Version& Current, const Version& Newest) { + if (Newest.major > Current.major) { return true; - } else if (Newest[0] == Current[0] && Newest[1] > Current[1]) { + } else if (Newest.major == Current.major && Newest.minor > Current.minor) { return true; - } else if (Newest[0] == Current[0] && Newest[1] == Current[1] && Newest[2] > Current[2]) { + } else if (Newest.major == Current.major && Newest.minor == Current.minor && Newest.patch > Current.patch) { return true; } else { return false; @@ -59,12 +64,10 @@ void Application::CheckForUpdates() { auto Response = Http::GET(GetBackendHostname(), 443, "/v/s"); bool Matches = std::regex_match(Response, VersionRegex); if (Matches) { - auto MyVersion = VersionStrToInts(ServerVersion()); - auto RemoteVersion = VersionStrToInts(Response); + auto MyVersion = ServerVersion(); + auto RemoteVersion = Version(VersionStrToInts(Response)); if (IsOutdated(MyVersion, RemoteVersion)) { - std::string RealVersionString = std::to_string(RemoteVersion[0]) + "."; - RealVersionString += std::to_string(RemoteVersion[1]) + "."; - RealVersionString += std::to_string(RemoteVersion[2]); + std::string RealVersionString = RemoteVersion.AsString(); warn(std::string(ANSI_YELLOW_BOLD) + "NEW VERSION OUT! There's a new version (v" + RealVersionString + ") of the BeamMP-Server available! For info on how to update your server, visit https://wiki.beammp.com/en/home/server-maintenance#updating-the-server." + std::string(ANSI_RESET)); } else { info("Server up-to-date!"); @@ -149,10 +152,16 @@ Version::Version(uint8_t major, uint8_t minor, uint8_t patch) , minor(minor) , patch(patch) { } +Version::Version(const std::array& v) + : Version(v[0], v[1], v[2]) { +} + std::string Version::AsString() { std::stringstream ss {}; ss << int(major) << "." << int(minor) << "." << int(patch); return ss.str(); +} + void LogChatMessage(const std::string& name, int id, const std::string& msg) { std::stringstream ss; ss << "[CHAT] "; @@ -162,7 +171,6 @@ void LogChatMessage(const std::string& name, int id, const std::string& msg) { ss << name << ""; } ss << msg; - } std::string GetPlatformAgnosticErrorString() { @@ -190,4 +198,4 @@ std::string GetPlatformAgnosticErrorString() { #else // posix return std::strerror(errno); #endif -} \ No newline at end of file +} diff --git a/src/Http.cpp b/src/Http.cpp index f69ff5a..d67f18e 100644 --- a/src/Http.cpp +++ b/src/Http.cpp @@ -15,94 +15,7 @@ namespace net = boost::asio; // from namespace ssl = net::ssl; // from using tcp = net::ip::tcp; // from -std::string Http::GET(const std::string& host, int port, const std::string& target, unsigned int* status) { - - - try { - // Check command line arguments. - int version = 11; - - - - - // The io_context is required for all I/O - net::io_context ioc; - - // The SSL context is required, and holds certificates - ssl::context ctx(ssl::context::tlsv12_client); - - // This holds the root certificate used for verification - // we don't do / have this - // load_root_certificates(ctx); - - // Verify the remote server's certificate - ctx.set_verify_mode(ssl::verify_none); - - // These objects perform our I/O - tcp::resolver resolver(ioc); - beast::ssl_stream stream(ioc, ctx); - - // Set SNI Hostname (many hosts need this to handshake successfully) - if (!SSL_set_tlsext_host_name(stream.native_handle(), host.c_str())) { - beast::error_code ec { static_cast(::ERR_get_error()), net::error::get_ssl_category() }; - throw beast::system_error { ec }; - } - - // Look up the domain name - auto const results = resolver.resolve(host.c_str(), std::to_string(port)); - - // Make the connection on the IP address we get from a lookup - beast::get_lowest_layer(stream).connect(results); - - // Perform the SSL handshake - stream.handshake(ssl::stream_base::client); - - // Set up an HTTP GET request message - http::request req { http::verb::get, target, version }; - req.set(http::field::host, host); - - req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING); - - // Send the HTTP request to the remote host - http::write(stream, req); - - // This buffer is used for reading and must be persisted - beast::flat_buffer buffer; - - - - - // Declare a container to hold the response - http::response res; - - // Receive the HTTP response - http::read(stream, buffer, res); - - // Gracefully close the stream - beast::error_code ec; - stream.shutdown(ec); - if (ec == net::error::eof) { - // Rationale: - // http://stackoverflow.com/questions/25587403/boost-asio-ssl-async-shutdown-always-finishes-with-an-error - ec = {}; - } - - if (status) { - *status = res.base().result_int(); - } - - if (ec) - throw beast::system_error { ec }; - - // If we get here then the connection is closed gracefully - return std::string(res.body()); - } catch (std::exception const& e) { - Application::Console().Write(__func__ + std::string(": ") + e.what()); - return ErrorString; - } -} - -std::string Http::POST(const std::string& host, int port, const std::string& target, const std::unordered_map& fields, const std::string& body, const std::string& ContentType, unsigned int* status) { +std::string GenericRequest(http::verb verb, const std::string& host, int port, const std::string& target, const std::unordered_map& fields, const std::string& body, const std::string& ContentType, unsigned int* status) { try { net::io_context io; @@ -140,14 +53,13 @@ std::string Http::POST(const std::string& host, int port, const std::string& tar } //} stream.handshake(ssl::stream_base::client); - http::request req { http::verb::post, target, 11 /* http 1.1 */ }; + http::request req { verb, target, 11 /* http 1.1 */ }; req.set(http::field::host, host); if (!body.empty()) { req.set(http::field::content_type, ContentType); // "application/json" // "application/x-www-form-urlencoded" - req.set(http::field::content_length, std::to_string(body.size())); req.body() = body; // info("body is " + body + " (" + req.body() + ")"); @@ -215,10 +127,18 @@ std::string Http::POST(const std::string& host, int port, const std::string& tar } catch (const std::exception& e) { Application::Console().Write(__func__ + std::string(": ") + e.what()); - return ErrorString; + return Http::ErrorString; } } +std::string Http::GET(const std::string& host, int port, const std::string& target, unsigned int* status) { + return GenericRequest(http::verb::get, host, port, target, {}, {}, {}, status); +} + +std::string Http::POST(const std::string& host, int port, const std::string& target, const std::unordered_map& fields, const std::string& body, const std::string& ContentType, unsigned int* status) { + return GenericRequest(http::verb::post, host, port, target, fields, body, ContentType, status); +} + // RFC 2616, RFC 7231 static std::map Map = { { 100, "Continue" }, @@ -291,4 +211,4 @@ std::string Http::Status::ToString(int code) { } else { return "Unassigned"; } -} \ No newline at end of file +} diff --git a/src/THeartbeatThread.cpp b/src/THeartbeatThread.cpp index 826e0b1..e5d2855 100644 --- a/src/THeartbeatThread.cpp +++ b/src/THeartbeatThread.cpp @@ -49,20 +49,19 @@ void THeartbeatThread::operator()() { Sentry.Log(SentryLevel::Error, "default", Http::Status::ToString(status) + " (" + std::to_string(status) + ")"); }; - auto Target = "/heartbeat"; - int ResponseCode = -1; - T = Http::POST(Application::GetBackendHostname(), Target, {}, Body, false, &ResponseCode); + unsigned int ResponseCode = 0; + T = Http::POST(Application::GetBackendHostname(), 443, Target, {}, Body, "application/x-www-form-urlencoded", &ResponseCode); if (T.substr(0, 2) != "20" || ResponseCode != 200) { trace("got " + T + " from backend"); SentryReportError(Application::GetBackendHostname() + Target, ResponseCode); std::this_thread::sleep_for(std::chrono::milliseconds(500)); - T = Http::POST(Application::GetBackup1Hostname(), Target, {}, Body, false, &ResponseCode); + T = Http::POST(Application::GetBackup1Hostname(), 443, Target, {}, Body, "application/x-www-form-urlencoded", &ResponseCode); if (T.substr(0, 2) != "20" || ResponseCode != 200) { SentryReportError(Application::GetBackup1Hostname() + Target, ResponseCode); std::this_thread::sleep_for(std::chrono::milliseconds(500)); - T = Http::POST(Application::GetBackup2Hostname(), Target, {}, Body, false, &ResponseCode); + T = Http::POST(Application::GetBackup2Hostname(), 443, Target, {}, Body, "application/x-www-form-urlencoded", &ResponseCode); if (T.substr(0, 2) != "20" || ResponseCode != 200) { warn("Backend system refused server! Server will not show in the public server list."); @@ -130,4 +129,4 @@ std::string THeartbeatThread::GetPlayers() { return Return; } /*THeartbeatThread::~THeartbeatThread() { -}*/ \ No newline at end of file +}*/ diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 7bbd874..dca0de8 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -72,17 +72,12 @@ void TLuaEngine::FolderList(const std::string& Path, bool HotSwap) { for (const auto& entry : fs::directory_iterator(Path)) { if (fs::is_directory(entry)) { RegisterFiles(entry.path(), HotSwap); - } } } void TLuaEngine::RegisterFiles(const fs::path& Path, bool HotSwap) { std::string Name = Path.filename().string(); - std::string Name = Path.substr(Path.find_last_of('/') + 1); -#else - -#endif if (!HotSwap) info(("Loading plugin : ") + Name); std::vector Entries; @@ -111,11 +106,10 @@ void TLuaEngine::RegisterFiles(const fs::path& Path, bool HotSwap) { } } - bool TLuaEngine::IsNewFile(const std::string& Path) { for (auto& Script : mLuaFiles) { if (fs::absolute(Path) == fs::absolute(Script->GetFileName())) return false; } return true; -} \ No newline at end of file +} diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index 05a226f..cf0625b 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -241,9 +241,9 @@ void TNetwork::Authentication(const TConnection& ClientConnection) { auto RequestString = R"({"key":")" + Rc + "\"}"; auto Target = "/pkToUser"; - int ResponseCode = -1; + unsigned int ResponseCode = 0; if (!Rc.empty()) { - Rc = Http::POST(Application::GetBackendUrlForAuth(), Target, {}, RequestString, true, &ResponseCode); + Rc = Http::POST(Application::GetBackendUrlForAuth(), 443, Target, {}, RequestString, "application/json", &ResponseCode); } json::Document AuthResponse; @@ -272,7 +272,6 @@ void TNetwork::Authentication(const TConnection& ClientConnection) { Sentry.Log(SentryLevel::Error, "default", "unexpected backend response (" + std::to_string(ResponseCode) + ")"); } return; - } if (AuthResponse["username"].IsString() && AuthResponse["roles"].IsString() @@ -931,4 +930,4 @@ std::string TNetwork::UDPRcvFromClient(sockaddr_in& client) const { return ""; } return std::string(Ret.begin(), Ret.begin() + Rcv); -} \ No newline at end of file +}