diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..a3ce056 --- /dev/null +++ b/.clang-format @@ -0,0 +1,5 @@ +--- +BasedOnStyle: WebKit +BreakBeforeBraces: Attach +SpaceAfterTemplateKeyword: false +... diff --git a/.github/workflows/cmake-linux.yml b/.github/workflows/cmake-linux.yml new file mode 100644 index 0000000..8535e30 --- /dev/null +++ b/.github/workflows/cmake-linux.yml @@ -0,0 +1,42 @@ +name: CMake Linux Build + +on: [push, pull_request, workflow_dispatch] + +env: + BUILD_TYPE: Release + +jobs: + linux-build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + submodules: 'true' + + - name: Restore artifacts, or run vcpkg, build and cache artifacts + uses: lukka/run-vcpkg@v7 + id: runvcpkg + with: + vcpkgArguments: 'zlib nlohmann-json openssl cpp-httplib[openssl]' + vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg' + vcpkgGitCommitId: '40616a5e954f7be1077ef37db3fbddbd5dcd1ca6' + + - name: Create Build Environment + run: cmake -E make_directory ${{github.workspace}}/build-linux + + - name: Configure CMake + shell: bash + working-directory: ${{github.workspace}}/build-linux + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE='${{ runner.workspace }}/b/vcpkg/scripts/buildsystems/vcpkg.cmake' + + - name: Build + working-directory: ${{github.workspace}}/build-linux + shell: bash + run: cmake --build . --config $BUILD_TYPE + + - name: Archive artifacts + uses: actions/upload-artifact@v2 + with: + name: BeamMP-Launcher + path: ${{github.workspace}}/build-linux/BeamMP-Launcher diff --git a/.github/workflows/cmake-windows.yml b/.github/workflows/cmake-windows.yml index 5a20f84..63153d8 100644 --- a/.github/workflows/cmake-windows.yml +++ b/.github/workflows/cmake-windows.yml @@ -1,6 +1,6 @@ name: CMake Windows Build -on: [push, pull_request] +on: [push, pull_request, workflow_dispatch] env: BUILD_TYPE: Release @@ -20,7 +20,7 @@ jobs: with: vcpkgArguments: 'discord-rpc zlib nlohmann-json openssl cpp-httplib[openssl]' vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg' - vcpkgGitCommitId: '16ee2ecb31788c336ace8bb14c21801efb6836e4' + vcpkgGitCommitId: '40616a5e954f7be1077ef37db3fbddbd5dcd1ca6' vcpkgTriplet: 'x64-windows-static' - name: Create Build Environment diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index 88e2913..234263e 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -28,6 +28,7 @@ jobs: body: | Files included in this release: - `BeamMP-Launcher.exe` windows build + - `BeamMP-Launcher` linux build upload-release-files-windows: name: Upload Windows Release Files @@ -70,3 +71,34 @@ jobs: asset_path: ${{github.workspace}}/build-windows/Release/BeamMP-Launcher.exe asset_name: BeamMP-Launcher.exe asset_content_type: application/vnd.microsoft.portable-executable + + upload-release-files-linux: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + submodules: 'true' + + - name: Create Build Environment + run: cmake -E make_directory ${{github.workspace}}/build-linux + + - name: Configure CMake + shell: bash + working-directory: ${{github.workspace}}/build-linux + run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE + + - name: Build + working-directory: ${{github.workspace}}/build-linux + shell: bash + run: cmake --build . --config $BUILD_TYPE + + - name: Upload Release Asset + id: upload-release-asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ needs.create-release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps + asset_path: ${{github.workspace}}/build-linux/BeamMP-Launcher + asset_name: BeamMP-Launcher + asset_content_type: application/octet-stream diff --git a/.gitignore b/.gitignore index c58e6c0..01db102 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,11 @@ cmake-build-release *.log /*.sh /*.obj -/*.exe \ No newline at end of file +/*.exe +.cache/ +.https_debug/ +Launcher.cfg +Resources/ +bin/ +compile_commands.json +key diff --git a/CMakeLists.txt b/CMakeLists.txt index 3edcc75..bffd629 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") -file(GLOB source_files "src/*.cpp" "src/*/*.cpp" "src/*/*.hpp" "include/*.h" "include/*/*.h" "include/*/*/*.h") +file(GLOB source_files "src/*.cpp" "src/*/*.cpp" "src/*/*.hpp" "include/*.h" "include/*/*.h" "include/*/*/*.h" "include/*.hpp" "include/*/*.hpp" "include/*/*/*.hpp") find_package(httplib CONFIG REQUIRED) find_package(nlohmann_json CONFIG REQUIRED) @@ -24,6 +24,11 @@ if (WIN32) find_package(OpenSSL REQUIRED) target_link_libraries(${PROJECT_NAME} PRIVATE ZLIB::ZLIB OpenSSL::SSL OpenSSL::Crypto ws2_32 httplib::httplib nlohmann_json::nlohmann_json) +elseif (LINUX) + find_package(ZLIB REQUIRED) + find_package(OpenSSL REQUIRED) + target_link_libraries(${PROJECT_NAME} PRIVATE + ZLIB::ZLIB OpenSSL::SSL OpenSSL::Crypto) else(WIN32) #MINGW add_definitions("-D_WIN32_WINNT=0x0600") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Os -s --static") diff --git a/include/Http.h b/include/Http.h index 69ab403..55a32a8 100644 --- a/include/Http.h +++ b/include/Http.h @@ -6,14 +6,15 @@ /// Created by Anonymous275 on 7/18/2020 /// #pragma once -#include #include "Logger.h" +#include class HTTP { public: - static bool Download(const std::string &IP, const std::string &Path); + static bool Download(const std::string& IP, const std::string& Path); static std::string Post(const std::string& IP, const std::string& Fields); - static std::string Get(const std::string &IP); + static std::string Get(const std::string& IP); static bool ProgressBar(size_t c, size_t t); + public: static bool isDownload; }; \ No newline at end of file diff --git a/include/Logger.h b/include/Logger.h index aa948e3..c588789 100644 --- a/include/Logger.h +++ b/include/Logger.h @@ -6,8 +6,8 @@ /// Created by Anonymous275 on 4/2/2020. /// #pragma once -#include #include +#include void InitLog(); void except(const std::string& toPrint); void fatal(const std::string& toPrint); diff --git a/include/Network/network.h b/include/Network/network.hpp similarity index 80% rename from include/Network/network.h rename to include/Network/network.hpp index 4c3bdf6..6743966 100644 --- a/include/Network/network.h +++ b/include/Network/network.hpp @@ -6,9 +6,16 @@ /// Created by Anonymous275 on 7/18/2020 /// - #pragma once #include + +#ifdef __linux__ +#include "linuxfixes.h" +#include +#include +#include +#endif + void NetReset(); extern bool Dev; extern int ping; @@ -38,11 +45,10 @@ void GameSend(std::string Data); void SendLarge(std::string Data); std::string TCPRcv(uint64_t Sock); void SyncResources(uint64_t TCPSock); -std::string GetAddr(const std::string&IP); +std::string GetAddr(const std::string& IP); void ServerParser(const std::string& Data); std::string Login(const std::string& fields); -void TCPSend(const std::string&Data,uint64_t Sock); -void TCPClientMain(const std::string& IP,int Port); -void UDPClientMain(const std::string& IP,int Port); +void TCPSend(const std::string& Data, uint64_t Sock); +void TCPClientMain(const std::string& IP, int Port); +void UDPClientMain(const std::string& IP, int Port); void TCPGameServer(const std::string& IP, int Port); - diff --git a/include/Security/Init.h b/include/Security/Init.h index c6f3db7..06dbf36 100644 --- a/include/Security/Init.h +++ b/include/Security/Init.h @@ -8,7 +8,7 @@ #pragma once #include void PreGame(const std::string& GamePath); -std::string CheckVer(const std::string &path); +std::string CheckVer(const std::string& path); void InitGame(const std::string& Dir); std::string GetGameDir(); void LegitimacyCheck(); diff --git a/include/Startup.h b/include/Startup.h index 7487146..6f91963 100644 --- a/include/Startup.h +++ b/include/Startup.h @@ -6,12 +6,12 @@ /// Created by Anonymous275 on 7/18/2020 /// #pragma once -#include #include +#include #include void InitLauncher(int argc, char* argv[]); -std::string GetEP(char*P = nullptr); +std::string GetEP(char* P = nullptr); std::string GetGamePath(); std::string GetVer(); std::string GetEN(); diff --git a/include/linuxfixes.h b/include/linuxfixes.h new file mode 100644 index 0000000..730e880 --- /dev/null +++ b/include/linuxfixes.h @@ -0,0 +1,19 @@ +#ifndef _LINUXFIXES_H +#define _LINUXFIXES_H + +#include + +// Translate windows sockets stuff to linux sockets +#define SOCKET uint64_t +#define SOCKADDR sockaddr +#define SOCKADDR_IN sockaddr_in +#define WSAGetLastError() errno +#define closesocket close +#define SD_BOTH SHUT_RDWR +// We dont need wsacleanup +#define WSACleanup() +#define SOCKET_ERROR -1 + +#define ZeroMemory(mem, len) memset(mem, 0, len) + +#endif \ No newline at end of file diff --git a/include/vdf_parser.hpp b/include/vdf_parser.hpp new file mode 100644 index 0000000..23af579 --- /dev/null +++ b/include/vdf_parser.hpp @@ -0,0 +1,730 @@ +//MIT License +// +//Copyright(c) 2016 Matthias Moeller +// +//Permission is hereby granted, free of charge, to any person obtaining a copy +//of this software and associated documentation files(the "Software"), to deal +//in the Software without restriction, including without limitation the rights +//to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +//copies of the Software, and to permit persons to whom the Software is +//furnished to do so, subject to the following conditions : +// +//The above copyright notice and this permission notice shall be included in all +//copies or substantial portions of the Software. +// +//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +//SOFTWARE. + +#ifndef __TYTI_STEAM_VDF_PARSER_H__ +#define __TYTI_STEAM_VDF_PARSER_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +//for wstring support +#include +#include + +// internal +#include + +//VS < 2015 has only partial C++11 support +#if defined(_MSC_VER) && _MSC_VER < 1900 +#ifndef CONSTEXPR +#define CONSTEXPR +#endif + +#ifndef NOEXCEPT +#define NOEXCEPT +#endif +#else +#ifndef CONSTEXPR +#define CONSTEXPR constexpr +#define TYTI_UNDEF_CONSTEXPR +#endif + +#ifndef NOEXCEPT +#define NOEXCEPT noexcept +#define TYTI_UNDEF_NOEXCEPT +#endif + +#endif + +namespace tyti +{ + namespace vdf + { + namespace detail + { + /////////////////////////////////////////////////////////////////////////// + // Helper functions selecting the right encoding (char/wchar_T) + /////////////////////////////////////////////////////////////////////////// + + template + struct literal_macro_help + { + static CONSTEXPR const char* result(const char* c, const wchar_t*) NOEXCEPT + { + return c; + } + static CONSTEXPR const char result(const char c, const wchar_t) NOEXCEPT + { + return c; + } + }; + + template <> + struct literal_macro_help + { + static CONSTEXPR const wchar_t* result(const char*, const wchar_t* wc) NOEXCEPT + { + return wc; + } + static CONSTEXPR const wchar_t result(const char, const wchar_t wc) NOEXCEPT + { + return wc; + } + }; +#define TYTI_L(type, text) vdf::detail::literal_macro_help::result(text, L##text) + + inline std::string string_converter(const std::string& w) NOEXCEPT + { + return w; + } + + // utility wrapper to adapt locale-bound facets for wstring/wbuffer convert + // from cppreference + template + struct deletable_facet : Facet + { + template + deletable_facet(Args &&... args) : Facet(std::forward(args)...) {} + ~deletable_facet() {} + }; + + inline std::string string_converter(const std::wstring& w) //todo: use us-locale + { + std::wstring_convert>> conv1; + return conv1.to_bytes(w); + } + + /////////////////////////////////////////////////////////////////////////// + // Writer helper functions + /////////////////////////////////////////////////////////////////////////// + + template + class tabs + { + const size_t t; + + public: + explicit CONSTEXPR tabs(size_t i) NOEXCEPT : t(i) {} + std::basic_string print() const { return std::basic_string(t, TYTI_L(charT, '\t')); } + inline CONSTEXPR tabs operator+(size_t i) const NOEXCEPT + { + return tabs(t + i); + } + }; + + template + oStreamT& operator<<(oStreamT& s, const tabs t) + { + s << t.print(); + return s; + } + } // end namespace detail + + /////////////////////////////////////////////////////////////////////////// + // Interface + /////////////////////////////////////////////////////////////////////////// + + /// custom objects and their corresponding write functions + + /// basic object node. Every object has a name and can contains attributes saved as key_value pairs or childrens + template + struct basic_object + { + typedef CharT char_type; + std::basic_string name; + std::unordered_map, std::basic_string> attribs; + std::unordered_map, std::shared_ptr>> childs; + + void add_attribute(std::basic_string key, std::basic_string value) + { + attribs.emplace(std::move(key), std::move(value)); + } + void add_child(std::unique_ptr> child) + { + std::shared_ptr> obj{ child.release() }; + childs.emplace(obj->name, obj); + } + void set_name(std::basic_string n) + { + name = std::move(n); + } + }; + + template + struct basic_multikey_object + { + typedef CharT char_type; + std::basic_string name; + std::unordered_multimap, std::basic_string> attribs; + std::unordered_multimap, std::shared_ptr>> childs; + + void add_attribute(std::basic_string key, std::basic_string value) + { + attribs.emplace(std::move(key), std::move(value)); + } + void add_child(std::unique_ptr> child) + { + std::shared_ptr> obj{ child.release() }; + childs.emplace(obj->name, obj); + } + void set_name(std::basic_string n) + { + name = std::move(n); + } + }; + + typedef basic_object object; + typedef basic_object wobject; + typedef basic_multikey_object multikey_object; + typedef basic_multikey_object wmultikey_object; + + struct Options + { + bool strip_escape_symbols; + bool ignore_all_platform_conditionals; + bool ignore_includes; + + Options() : strip_escape_symbols(true), ignore_all_platform_conditionals(false), ignore_includes(false) {} + }; + + //forward decls + //forward decl + template + OutputT read(iStreamT& inStream, const Options& opt = Options{}); + + /** \brief writes given object tree in vdf format to given stream. + Output is prettyfied, using tabs + */ + template + void write(oStreamT& s, const T& r, + const detail::tabs tab = detail::tabs(0)) + { + typedef typename oStreamT::char_type charT; + using namespace detail; + s << tab << TYTI_L(charT, '"') << r.name << TYTI_L(charT, "\"\n") << tab << TYTI_L(charT, "{\n"); + for (const auto& i : r.attribs) + s << tab + 1 << TYTI_L(charT, '"') << i.first << TYTI_L(charT, "\"\t\t\"") << i.second << TYTI_L(charT, "\"\n"); + for (const auto& i : r.childs) + if (i.second) + write(s, *i.second, tab + 1); + s << tab << TYTI_L(charT, "}\n"); + } + + namespace detail + { + template + std::basic_string read_file(iStreamT& inStream) + { + // cache the file + typedef typename iStreamT::char_type charT; + std::basic_string str; + inStream.seekg(0, std::ios::end); + str.resize(static_cast(inStream.tellg())); + if (str.empty()) + return str; + + inStream.seekg(0, std::ios::beg); + inStream.read(&str[0], str.size()); + return str; + } + + /** \brief Read VDF formatted sequences defined by the range [first, last). + If the file is mailformatted, parser will try to read it until it can. + @param first begin iterator + @param end end iterator + @param exclude_files list of files which cant be included anymore. + prevents circular includes + + can thow: + - "std::runtime_error" if a parsing error occured + - "std::bad_alloc" if not enough memory coup be allocated + */ + template + std::vector> read_internal(IterT first, const IterT last, + std::unordered_set::value_type>>& exclude_files, + const Options& opt) + { + static_assert(std::is_default_constructible::value, + "Output Type must be default constructible (provide constructor without arguments)"); + static_assert(std::is_move_constructible::value, + "Output Type must be move constructible"); + + typedef typename std::iterator_traits::value_type charT; + + const std::basic_string comment_end_str = TYTI_L(charT, "*/"); + const std::basic_string whitespaces = TYTI_L(charT, " \n\v\f\r\t"); + +#ifdef WIN32 + std::function&)> is_platform_str = [](const std::basic_string& in) { + return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$WINDOWS"); + }; +#elif __APPLE__ + // WIN32 stands for pc in general + std::function&)> is_platform_str = [](const std::basic_string& in) { + return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$POSIX") || in == TYTI_L(charT, "$OSX"); + }; + +#elif __linux__ + // WIN32 stands for pc in general + std::function&)> is_platform_str = [](const std::basic_string& in) { + return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$POSIX") || in == TYTI_L(charT, "$LINUX"); + }; +#else + std::function&)> is_platform_str = [](const std::basic_string& in) { + return false; + }; +#endif + + if (opt.ignore_all_platform_conditionals) + is_platform_str = [](const std::basic_string&) { + return false; + }; + + // function for skipping a comment block + // iter: iterator poition to the position after a '/' + auto skip_comments = [&comment_end_str](IterT iter, const IterT& last) -> IterT { + ++iter; + if (iter != last) + { + if (*iter == TYTI_L(charT, '/')) + { + // line comment, skip whole line + iter = std::find(iter + 1, last, TYTI_L(charT, '\n')); + } + + if (*iter == '*') + { + // block comment, skip until next occurance of "*\" + iter = std::search(iter + 1, last, std::begin(comment_end_str), std::end(comment_end_str)); + iter += 2; + } + } + return iter; + }; + + auto end_quote = [](IterT iter, const IterT& last) -> IterT { + const auto begin = iter; + auto last_esc = iter; + do + { + ++iter; + iter = std::find(iter, last, TYTI_L(charT, '\"')); + if (iter == last) + break; + + last_esc = std::prev(iter); + while (last_esc != begin && *last_esc == '\\') + --last_esc; + } while (!(std::distance(last_esc, iter) % 2)); + if (iter == last) + throw std::runtime_error{ "quote was opened but not closed." }; + return iter; + }; + + auto end_word = [&whitespaces](IterT iter, const IterT& last) -> IterT { + const auto begin = iter; + auto last_esc = iter; + do + { + ++iter; + iter = std::find_first_of(iter, last, std::begin(whitespaces), std::end(whitespaces)); + if (iter == last) + break; + + last_esc = std::prev(iter); + while (last_esc != begin && *last_esc == '\\') + --last_esc; + } while (!(std::distance(last_esc, iter) % 2)); + //if (iter == last) + // throw std::runtime_error{ "word wasnt properly ended" }; + return iter; + }; + + auto skip_whitespaces = [&whitespaces](IterT iter, const IterT& last) -> IterT { + iter = std::find_if_not(iter, last, [&whitespaces](charT c) { + // return true if whitespace + return std::any_of(std::begin(whitespaces), std::end(whitespaces), [c](charT pc) { return pc == c; }); + }); + return iter; + }; + + std::function&)> strip_escape_symbols = [](std::basic_string& s) { + auto quote_searcher = [&s](size_t pos) { return s.find(TYTI_L(charT, "\\\""), pos); }; + auto p = quote_searcher(0); + while (p != s.npos) + { + s.replace(p, 2, TYTI_L(charT, "\"")); + p = quote_searcher(p); + } + auto searcher = [&s](size_t pos) { return s.find(TYTI_L(charT, "\\\\"), pos); }; + p = searcher(0); + while (p != s.npos) + { + s.replace(p, 2, TYTI_L(charT, "\\")); + p = searcher(p); + } + }; + + if (!opt.strip_escape_symbols) + strip_escape_symbols = [](std::basic_string&) {}; + + auto conditional_fullfilled = [&skip_whitespaces, &is_platform_str](IterT& iter, const IterT& last) { + iter = skip_whitespaces(iter, last); + if (*iter == '[') + { + ++iter; + const auto end = std::find(iter, last, ']'); + const bool negate = *iter == '!'; + if (negate) + ++iter; + auto conditional = std::basic_string(iter, end); + + const bool is_platform = is_platform_str(conditional); + iter = end + 1; + + return static_cast(is_platform ^ negate); + } + return true; + }; + + //read header + // first, quoted name + std::unique_ptr curObj = nullptr; + std::vector> roots; + std::stack> lvls; + auto curIter = first; + + while (curIter != last && *curIter != '\0') + { + //find first starting attrib/child, or ending + curIter = skip_whitespaces(curIter, last); + if (curIter == last || *curIter == '\0') + break; + if (*curIter == TYTI_L(charT, '/')) + { + curIter = skip_comments(curIter, last); + } + else if (*curIter != TYTI_L(charT, '}')) + { + + // get key + const auto keyEnd = (*curIter == TYTI_L(charT, '\"')) ? end_quote(curIter, last) : end_word(curIter, last); + if (*curIter == TYTI_L(charT, '\"')) + ++curIter; + std::basic_string key(curIter, keyEnd); + strip_escape_symbols(key); + curIter = keyEnd + ((*keyEnd == TYTI_L(charT, '\"')) ? 1 : 0); + + curIter = skip_whitespaces(curIter, last); + + auto conditional = conditional_fullfilled(curIter, last); + if (!conditional) + continue; + + while (*curIter == TYTI_L(charT, '/')) + { + + curIter = skip_comments(curIter, last); + if (curIter == last || *curIter == '}') + throw std::runtime_error{ "key declared, but no value" }; + curIter = skip_whitespaces(curIter, last); + if (curIter == last || *curIter == '}') + throw std::runtime_error{ "key declared, but no value" }; + } + // get value + if (*curIter != '{') + { + const auto valueEnd = (*curIter == TYTI_L(charT, '\"')) ? end_quote(curIter, last) : end_word(curIter, last); + if (*curIter == TYTI_L(charT, '\"')) + ++curIter; + + auto value = std::basic_string(curIter, valueEnd); + strip_escape_symbols(value); + curIter = valueEnd + ((*valueEnd == TYTI_L(charT, '\"')) ? 1 : 0); + + auto conditional = conditional_fullfilled(curIter, last); + if (!conditional) + continue; + + // process value + if (key != TYTI_L(charT, "#include") && key != TYTI_L(charT, "#base")) + { + if (curObj) + { + curObj->add_attribute(std::move(key), std::move(value)); + } + else + { + throw std::runtime_error{ "unexpected key without object" }; + } + } + else + { + if (!opt.ignore_includes && exclude_files.find(value) == exclude_files.end()) + { + exclude_files.insert(value); + std::basic_ifstream i(detail::string_converter(value)); + auto str = read_file(i); + auto file_objs = read_internal(str.begin(), str.end(), exclude_files, opt); + for (auto& n : file_objs) + { + if (curObj) + curObj->add_child(std::move(n)); + else + roots.push_back(std::move(n)); + } + exclude_files.erase(value); + } + } + } + else if (*curIter == '{') + { + if (curObj) + lvls.push(std::move(curObj)); + curObj = std::make_unique(); + curObj->set_name(std::move(key)); + ++curIter; + } + } + //end of new object + else if (curObj && *curIter == TYTI_L(charT, '}')) + { + if (!lvls.empty()) + { + //get object before + std::unique_ptr prev{ std::move(lvls.top()) }; + lvls.pop(); + + // add finished obj to obj before and release it from processing + prev->add_child(std::move(curObj)); + curObj = std::move(prev); + } + else + { + roots.push_back(std::move(curObj)); + curObj.reset(); + } + ++curIter; + } + else + { + throw std::runtime_error{ "unexpected '}'" }; + } + } + if (curObj != nullptr || !lvls.empty()) + { + throw std::runtime_error{ "object is not closed with '}'" }; + } + + return roots; + } + + } // namespace detail + + /** \brief Read VDF formatted sequences defined by the range [first, last). + If the file is mailformatted, parser will try to read it until it can. + @param first begin iterator + @param end end iterator + + can thow: + - "std::runtime_error" if a parsing error occured + - "std::bad_alloc" if not enough memory coup be allocated + */ + template + OutputT read(IterT first, const IterT last, const Options& opt = Options{}) + { + auto exclude_files = std::unordered_set::value_type>>{}; + auto roots = detail::read_internal(first, last, exclude_files, opt); + + OutputT result; + if (roots.size() > 1) + { + for (auto& i : roots) + result.add_child(std::move(i)); + } + else if (roots.size() == 1) + result = std::move(*roots[0]); + + return result; + } + + /** \brief Read VDF formatted sequences defined by the range [first, last). + If the file is mailformatted, parser will try to read it until it can. + @param first begin iterator + @param end end iterator + @param ec output bool. 0 if ok, otherwise, holds an system error code + + Possible error codes: + std::errc::protocol_error: file is mailformatted + std::errc::not_enough_memory: not enough space + std::errc::invalid_argument: iterators throws e.g. out of range + */ + template + OutputT read(IterT first, IterT last, std::error_code& ec, const Options& opt = Options{}) NOEXCEPT + + { + ec.clear(); + OutputT r{}; + try + { + r = read(first, last, opt); + } + catch (std::runtime_error&) + { + ec = std::make_error_code(std::errc::protocol_error); + } + catch (std::bad_alloc&) + { + ec = std::make_error_code(std::errc::not_enough_memory); + } + catch (...) + { + ec = std::make_error_code(std::errc::invalid_argument); + } + return r; + } + + /** \brief Read VDF formatted sequences defined by the range [first, last). + If the file is mailformatted, parser will try to read it until it can. + @param first begin iterator + @param end end iterator + @param ok output bool. true, if parser successed, false, if parser failed + */ + template + OutputT read(IterT first, const IterT last, bool* ok, const Options& opt = Options{}) NOEXCEPT + { + std::error_code ec; + auto r = read(first, last, ec, opt); + if (ok) + *ok = !ec; + return r; + } + + template + inline auto read(IterT first, const IterT last, bool* ok, const Options& opt = Options{}) NOEXCEPT -> basic_object::value_type> + { + return read::value_type>>(first, last, ok, opt); + } + + template + inline auto read(IterT first, IterT last, std::error_code& ec, const Options& opt = Options{}) NOEXCEPT + -> basic_object::value_type> + { + return read::value_type>>(first, last, ec, opt); + } + + template + inline auto read(IterT first, const IterT last, const Options& opt = Options{}) + -> basic_object::value_type> + { + return read::value_type>>(first, last, opt); + } + + /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data. + throws "std::bad_alloc" if file buffer could not be allocated + */ + template + OutputT read(iStreamT& inStream, std::error_code& ec, const Options& opt = Options{}) + { + // cache the file + typedef typename iStreamT::char_type charT; + std::basic_string str = detail::read_file(inStream); + + // parse it + return read(str.begin(), str.end(), ec, opt); + } + + template + inline basic_object read(iStreamT& inStream, std::error_code& ec, const Options& opt = Options{}) + { + return read>(inStream, ec, opt); + } + + /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data. + throws "std::bad_alloc" if file buffer could not be allocated + ok == false, if a parsing error occured + */ + template + OutputT read(iStreamT& inStream, bool* ok, const Options& opt = Options{}) + { + std::error_code ec; + const auto r = read(inStream, ec, opt); + if (ok) + *ok = !ec; + return r; + } + + template + inline basic_object read(iStreamT& inStream, bool* ok, const Options& opt = Options{}) + { + return read>(inStream, ok, opt); + } + + /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data. + throws "std::bad_alloc" if file buffer could not be allocated + throws "std::runtime_error" if a parsing error occured + */ + template + OutputT read(iStreamT& inStream, const Options& opt) + { + + // cache the file + typedef typename iStreamT::char_type charT; + std::basic_string str = detail::read_file(inStream); + // parse it + return read(str.begin(), str.end(), opt); + } + + template + inline basic_object read(iStreamT& inStream, const Options& opt = Options{}) + { + return read>(inStream, opt); + } + + } // namespace vdf +} // namespace tyti +#ifndef TYTI_NO_L_UNDEF +#undef TYTI_L +#endif + +#ifdef TYTI_UNDEF_CONSTEXPR +#undef CONSTEXPR +#undef TYTI_NO_L_UNDEF +#endif + +#ifdef TYTI_UNDEF_NOTHROW +#undef NOTHROW +#undef TYTI_UNDEF_NOTHROW +#endif + +#endif //__TYTI_STEAM_VDF_PARSER_H__ diff --git a/src/Compressor.cpp b/src/Compressor.cpp index 6ee00ef..ea3320f 100644 --- a/src/Compressor.cpp +++ b/src/Compressor.cpp @@ -8,47 +8,50 @@ #include #include +#ifdef __linux__ +#include +#endif #define Biggest 30000 -std::string Comp(std::string Data){ - char*C = new char[Biggest]; +std::string Comp(std::string Data) { + char* C = new char[Biggest]; memset(C, 0, Biggest); z_stream defstream; defstream.zalloc = Z_NULL; defstream.zfree = Z_NULL; defstream.opaque = Z_NULL; defstream.avail_in = (uInt)Data.length(); - defstream.next_in = (Bytef *)&Data[0]; + defstream.next_in = (Bytef*)&Data[0]; defstream.avail_out = Biggest; - defstream.next_out = reinterpret_cast(C); + defstream.next_out = reinterpret_cast(C); deflateInit(&defstream, Z_BEST_COMPRESSION); deflate(&defstream, Z_SYNC_FLUSH); deflate(&defstream, Z_FINISH); deflateEnd(&defstream); int TO = defstream.total_out; - std::string Ret(TO,0); - memcpy_s(&Ret[0],TO,C,TO); - delete [] C; + std::string Ret(TO, 0); + memcpy(&Ret[0], C, TO); + delete[] C; return Ret; } -std::string DeComp(std::string Compressed){ - char*C = new char[Biggest]; +std::string DeComp(std::string Compressed) { + char* C = new char[Biggest]; memset(C, 0, Biggest); z_stream infstream; infstream.zalloc = Z_NULL; infstream.zfree = Z_NULL; infstream.opaque = Z_NULL; infstream.avail_in = Biggest; - infstream.next_in = (Bytef *)(&Compressed[0]); + infstream.next_in = (Bytef*)(&Compressed[0]); infstream.avail_out = Biggest; - infstream.next_out = (Bytef *)(C); + infstream.next_out = (Bytef*)(C); inflateInit(&infstream); inflate(&infstream, Z_SYNC_FLUSH); inflate(&infstream, Z_FINISH); inflateEnd(&infstream); int TO = infstream.total_out; - std::string Ret(TO,0); - memcpy_s(&Ret[0],TO,C,TO); - delete [] C; + std::string Ret(TO, 0); + memcpy(&Ret[0], C, TO); + delete[] C; return Ret; } \ No newline at end of file diff --git a/src/Config.cpp b/src/Config.cpp index 0b673eb..b2125cf 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -1,58 +1,59 @@ -/// -/// Created by Anonymous275 on 2/23/2021 -/// - -#include -#include "Network/network.h" -#include -#include "Logger.h" -#include -#include -namespace fs = std::filesystem; - -std::string Branch; -void ParseConfig(const nlohmann::json& d){ - if(d["Port"].is_number()){ - DEFAULT_PORT = d["Port"].get(); - } - //Default -1 - //Release 1 - //EA 2 - //Dev 3 - //Custom 3 - - if(d["Build"].is_string()){ - Branch = d["Build"].get(); - for(char& c : Branch)c = char(tolower(c)); - } -} - -void ConfigInit(){ - if(fs::exists("Launcher.cfg")){ - std::ifstream cfg("Launcher.cfg"); - if(cfg.is_open()){ - auto Size = fs::file_size("Launcher.cfg"); - std::string Buffer(Size, 0); - cfg.read(&Buffer[0], Size); - cfg.close(); - nlohmann::json d = nlohmann::json::parse(Buffer, nullptr, false); - if(d.is_discarded()){ - fatal("Config failed to parse make sure it's valid JSON!"); - } - ParseConfig(d); - }else fatal("Failed to open Launcher.cfg!"); - }else{ - std::ofstream cfg("Launcher.cfg"); - if(cfg.is_open()){ - cfg << - R"({ -"Port": 4444, -"Build": "Default" -})"; - cfg.close(); - }else{ - fatal("Failed to write config on disk!"); - } - } -} - +/// +/// Created by Anonymous275 on 2/23/2021 +/// + +#include "Logger.h" +#include "Network/network.hpp" +#include +#include +#include +#include +namespace fs = std::filesystem; + +std::string Branch; +void ParseConfig(const nlohmann::json& d) { + if (d["Port"].is_number()) { + DEFAULT_PORT = d["Port"].get(); + } + // Default -1 + // Release 1 + // EA 2 + // Dev 3 + // Custom 3 + + if (d["Build"].is_string()) { + Branch = d["Build"].get(); + for (char& c : Branch) + c = char(tolower(c)); + } +} + +void ConfigInit() { + if (fs::exists("Launcher.cfg")) { + std::ifstream cfg("Launcher.cfg"); + if (cfg.is_open()) { + auto Size = fs::file_size("Launcher.cfg"); + std::string Buffer(Size, 0); + cfg.read(&Buffer[0], Size); + cfg.close(); + nlohmann::json d = nlohmann::json::parse(Buffer, nullptr, false); + if (d.is_discarded()) { + fatal("Config failed to parse make sure it's valid JSON!"); + } + ParseConfig(d); + } else + fatal("Failed to open Launcher.cfg!"); + } else { + std::ofstream cfg("Launcher.cfg"); + if (cfg.is_open()) { + cfg << + R"({ +"Port": 4444, +"Build": "Default" +})"; + cfg.close(); + } else { + fatal("Failed to write config on disk!"); + } + } +} diff --git a/src/GameStart.cpp b/src/GameStart.cpp index 3a85eda..ac99d63 100644 --- a/src/GameStart.cpp +++ b/src/GameStart.cpp @@ -6,64 +6,111 @@ /// Created by Anonymous275 on 7/19/2020 /// -#include +#if defined(_WIN32) #include -#include "Startup.h" +#elif defined(__linux__) +#include "vdf_parser.hpp" +#include +#include +#include +#include +#include +#endif + #include "Logger.h" +#include "Startup.h" +#include +#include #include unsigned long GamePID = 0; -std::string QueryKey(HKEY hKey,int ID); -std::string GetGamePath(){ +#if defined(_WIN32) +std::string QueryKey(HKEY hKey, int ID); +std::string GetGamePath() { static std::string Path; - if(!Path.empty())return Path; + if (!Path.empty()) + return Path; HKEY hKey; LPCTSTR sk = "Software\\BeamNG\\BeamNG.drive"; LONG openRes = RegOpenKeyEx(HKEY_CURRENT_USER, sk, 0, KEY_ALL_ACCESS, &hKey); - if (openRes != ERROR_SUCCESS){ + if (openRes != ERROR_SUCCESS) { fatal("Please launch the game at least once!"); } - Path = QueryKey(hKey,4); + Path = QueryKey(hKey, 4); - if(Path.empty()){ + if (Path.empty()) { sk = R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders)"; openRes = RegOpenKeyEx(HKEY_CURRENT_USER, sk, 0, KEY_ALL_ACCESS, &hKey); - if (openRes != ERROR_SUCCESS){ - fatal("Cannot get Local Appdata directory!"); + if (openRes != ERROR_SUCCESS) { + fatal("Cannot get Local Appdata directory!"); } - Path = QueryKey(hKey,5); + Path = QueryKey(hKey, 5); Path += "\\BeamNG.drive\\"; } std::string Ver = CheckVer(GetGameDir()); - Ver = Ver.substr(0,Ver.find('.',Ver.find('.')+1)); + Ver = Ver.substr(0, Ver.find('.', Ver.find('.') + 1)); Path += Ver + "\\"; return Path; } +#elif defined(__linux__) +std::string GetGamePath() { + // Right now only steam is supported + struct passwd* pw = getpwuid(getuid()); + std::string homeDir = pw->pw_dir; -void StartGame(std::string Dir){ + std::string Path = homeDir + "/.local/share/BeamNG.drive/"; + std::string Ver = CheckVer(GetGameDir()); + Ver = Ver.substr(0, Ver.find('.', Ver.find('.') + 1)); + Path += Ver + "/"; + return Path; +} +#endif + +#if defined(_WIN32) +void StartGame(std::string Dir) { BOOL bSuccess = FALSE; PROCESS_INFORMATION pi; - STARTUPINFO si = {0}; + STARTUPINFO si = { 0 }; si.cb = sizeof(si); std::string BaseDir = Dir; //+"\\Bin64"; - //Dir += R"(\Bin64\BeamNG.drive.x64.exe)"; + // Dir += R"(\Bin64\BeamNG.drive.x64.exe)"; Dir += "\\BeamNG.drive.exe"; bSuccess = CreateProcessA(Dir.c_str(), nullptr, nullptr, nullptr, TRUE, 0, nullptr, BaseDir.c_str(), &si, &pi); - if (bSuccess){ + if (bSuccess) { info("Game Launched!"); GamePID = pi.dwProcessId; WaitForSingleObject(pi.hProcess, INFINITE); error("Game Closed! launcher closing soon"); - }else{ + } else { error("Failed to Launch the game! launcher closing soon"); } std::this_thread::sleep_for(std::chrono::seconds(5)); exit(2); } +#elif defined(__linux__) +void StartGame(std::string Dir) { + int status; + std::string filename = (Dir + "/BinLinux/BeamNG.drive.x64"); + char* argv[] = { filename.data(), NULL }; + pid_t pid; + int result = posix_spawn(&pid, filename.c_str(), NULL, NULL, argv, environ); -void InitGame(const std::string& Dir){ - if(!Dev){ + if (result != 0) { + error("Failed to Launch the game! launcher closing soon"); + return; + } else { + waitpid(pid, &status, 0); + error("Game Closed! launcher closing soon"); + } + + std::this_thread::sleep_for(std::chrono::seconds(5)); + exit(2); +} +#endif + +void InitGame(const std::string& Dir) { + if (!Dev) { std::thread Game(StartGame, Dir); Game.detach(); } diff --git a/src/Logger.cpp b/src/Logger.cpp index e23bf9a..557cec0 100644 --- a/src/Logger.cpp +++ b/src/Logger.cpp @@ -6,11 +6,11 @@ /// Created by Anonymous275 on 7/17/2020 /// -#include "Startup.h" #include "Logger.h" +#include "Startup.h" +#include #include #include -#include #include std::string getDate() { @@ -24,24 +24,25 @@ std::string getDate() { std::string Min = (M > 9 ? std::to_string(M) : "0" + std::to_string(M)); std::string Hour = (H > 9 ? std::to_string(H) : "0" + std::to_string(H)); date - << "[" - << local_tm.tm_mday << "/" - << local_tm.tm_mon + 1 << "/" - << local_tm.tm_year + 1900 << " " - << Hour << ":" - << Min << ":" - << Secs - << "] "; + << "[" + << local_tm.tm_mday << "/" + << local_tm.tm_mon + 1 << "/" + << local_tm.tm_year + 1900 << " " + << Hour << ":" + << Min << ":" + << Secs + << "] "; return date.str(); } -void InitLog(){ +void InitLog() { std::ofstream LFS; LFS.open(GetEP() + "Launcher.log"); - if(!LFS.is_open()){ + if (!LFS.is_open()) { error("logger file init failed!"); - }else LFS.close(); + } else + LFS.close(); } -void addToLog(const std::string& Line){ +void addToLog(const std::string& Line) { std::ofstream LFS; LFS.open(GetEP() + "Launcher.log", std::ios_base::app); LFS << Line.c_str(); @@ -53,12 +54,13 @@ void info(const std::string& toPrint) { addToLog(Print); } void debug(const std::string& toPrint) { - if(!Dev)return; + if (!Dev) + return; std::string Print = getDate() + "[DEBUG] " + toPrint + "\n"; std::cout << Print; addToLog(Print); } -void warn(const std::string& toPrint){ +void warn(const std::string& toPrint) { std::string Print = getDate() + "[WARN] " + toPrint + "\n"; std::cout << Print; addToLog(Print); diff --git a/src/Network/Core.cpp b/src/Network/Core.cpp index 0f1139e..c65f257 100644 --- a/src/Network/Core.cpp +++ b/src/Network/Core.cpp @@ -5,19 +5,31 @@ /// /// Created by Anonymous275 on 7/20/2020 /// - -#include "Network/network.h" -#include "Security/Init.h" -#include #include "Http.h" +#include "Network/network.hpp" +#include "Security/Init.h" +#include +#include +#if defined(_WIN32) #include #include -#include "Startup.h" +#elif defined(__linux__) +#include +#include +#include +#include +#include +#include +#include +#include +#endif + #include "Logger.h" -#include +#include "Startup.h" #include -#include +#include #include +#include extern int TraceBack; std::set* ConfList = nullptr; @@ -32,11 +44,13 @@ std::string MStatus; bool ModLoaded; int ping = -1; -void StartSync(const std::string &Data){ - std::string IP = GetAddr(Data.substr(1,Data.find(':')-1)); - if(IP.find('.') == -1){ - if(IP == "DNS")UlStatus ="UlConnection Failed! (DNS Lookup Failed)"; - else UlStatus = "UlConnection Failed! (WSA failed to start)"; +void StartSync(const std::string& Data) { + std::string IP = GetAddr(Data.substr(1, Data.find(':') - 1)); + if (IP.find('.') == -1) { + if (IP == "DNS") + UlStatus = "UlConnection Failed! (DNS Lookup Failed)"; + else + UlStatus = "UlConnection Failed! (WSA failed to start)"; ListOfMods = "-"; Terminate = true; return; @@ -47,7 +61,7 @@ void StartSync(const std::string &Data){ Terminate = false; ConfList->clear(); ping = -1; - std::thread GS(TCPGameServer,IP,std::stoi(Data.substr(Data.find(':')+1))); + std::thread GS(TCPGameServer, IP, std::stoi(Data.substr(Data.find(':') + 1))); GS.detach(); info("Connecting to server"); } @@ -55,138 +69,170 @@ void StartSync(const std::string &Data){ bool IsAllowedLink(const std::string& Link) { std::regex link_pattern(R"(https:\/\/(?:\w+)?(?:\.)?(?:beammp\.com|discord\.gg))"); std::smatch link_match; - return std::regex_search(Link,link_match, link_pattern) && link_match.position() == 0; + return std::regex_search(Link, link_match, link_pattern) && link_match.position() == 0; } -void Parse(std::string Data,SOCKET CSocket){ +void Parse(std::string Data, SOCKET CSocket) { char Code = Data.at(0), SubCode = 0; - if(Data.length() > 1)SubCode = Data.at(1); - switch (Code){ - case 'A': - Data = Data.substr(0,1); - break; - case 'B': + if (Data.length() > 1) + SubCode = Data.at(1); + switch (Code) { + case 'A': + Data = Data.substr(0, 1); + break; + case 'B': + NetReset(); + Terminate = true; + TCPTerminate = true; + Data = Code + HTTP::Get("https://backend.beammp.com/servers-info"); + break; + case 'C': + ListOfMods.clear(); + StartSync(Data); + while (ListOfMods.empty() && !Terminate) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + if (ListOfMods == "-") + Data = "L"; + else + Data = "L" + ListOfMods; + break; + case 'O': // open default browser with URL + if (IsAllowedLink(Data.substr(1))) { +#if defined(__linux) + if (char* browser = getenv("BROWSER"); browser != nullptr && !std::string_view(browser).empty()) { + pid_t pid; + auto arg = Data.substr(1); + char* argv[] = { browser, arg.data() }; + auto status = posix_spawn(&pid, browser, nullptr, nullptr, argv, environ); + if (status == 0) { + debug("Browser PID: " + std::to_string(pid)); + // we don't wait for it to exit, because we just don't care. + // typically, you'd waitpid() here. + } else { + error("Failed to open the following link in the browser (error follows below): " + arg); + error(std::string("posix_spawn: ") + strerror(status)); + } + } else { + error("Failed to open the following link in the browser because the $BROWSER environment variable is not set: " + Data.substr(1)); + } +#elif defined(WIN32) + ShellExecuteA(nullptr, "open", Data.substr(1).c_str(), nullptr, nullptr, SW_SHOW); /// TODO: Look at when working on linux port +#endif + + info("Opening Link \"" + Data.substr(1) + "\""); + } + Data.clear(); + break; + case 'P': + Data = Code + std::to_string(ProxyPort); + break; + case 'U': + if (SubCode == 'l') + Data = UlStatus; + if (SubCode == 'p') { + if (ping > 800) { + Data = "Up-2"; + } else + Data = "Up" + std::to_string(ping); + } + if (!SubCode) { + std::string Ping; + if (ping > 800) + Ping = "-2"; + else + Ping = std::to_string(ping); + Data = std::string(UlStatus) + "\n" + "Up" + Ping; + } + break; + case 'M': + Data = MStatus; + break; + case 'Q': + if (SubCode == 'S') { NetReset(); Terminate = true; TCPTerminate = true; - Data = Code + HTTP::Get("https://backend.beammp.com/servers-info"); - break; - case 'C': - ListOfMods.clear(); - StartSync(Data); - while(ListOfMods.empty() && !Terminate){ - std::this_thread::sleep_for(std::chrono::seconds(1)); + ping = -1; + } + if (SubCode == 'G') + exit(2); + Data.clear(); + break; + case 'R': // will send mod name + if (ConfList->find(Data) == ConfList->end()) { + ConfList->insert(Data); + ModLoaded = true; + } + Data.clear(); + break; + case 'Z': + Data = "Z" + GetVer(); + break; + case 'N': + if (SubCode == 'c') { + nlohmann::json Auth = { + { "Auth", LoginAuth ? 1 : 0 }, + }; + if (!Username.empty()) { + Auth["username"] = Username; } - if(ListOfMods == "-")Data = "L"; - else Data = "L"+ListOfMods; - break; - case 'O': //open default browser with URL - if(IsAllowedLink(Data.substr(1))) { - ShellExecuteA(nullptr, "open", Data.substr(1).c_str(), nullptr, nullptr,SW_SHOW); ///TODO: Look at when working on linux port - info("Opening Link \"" + Data.substr(1) + "\""); + if (!UserRole.empty()) { + Auth["role"] = UserRole; } - Data.clear(); - break; - case 'P': - Data = Code + std::to_string(ProxyPort); - break; - case 'U': - if(SubCode == 'l')Data = UlStatus; - if(SubCode == 'p'){ - if(ping > 800){ - Data = "Up-2"; - }else Data = "Up" + std::to_string(ping); - } - if(!SubCode){ - std::string Ping; - if(ping > 800)Ping = "-2"; - else Ping = std::to_string(ping); - Data = std::string(UlStatus) + "\n" + "Up" + Ping; - } - break; - case 'M': - Data = MStatus; - break; - case 'Q': - if(SubCode == 'S'){ - NetReset(); - Terminate = true; - TCPTerminate = true; - ping = -1; - } - if(SubCode == 'G')exit(2); - Data.clear(); - break; - case 'R': //will send mod name - if(ConfList->find(Data) == ConfList->end()){ - ConfList->insert(Data); - ModLoaded = true; - } - Data.clear(); - break; - case 'Z': - Data = "Z" + GetVer(); - break; - case 'N': - if (SubCode == 'c'){ - nlohmann::json Auth = { - {"Auth", LoginAuth ? 1 : 0 }, - }; - if (!Username.empty()) { - Auth["username"] = Username; - } - if (!UserRole.empty()) { - Auth["role"] = UserRole; - } - Data = Auth.dump(); - }else{ - Data = "N" + Login(Data.substr(Data.find(':') + 1)); - } - break; - default: - Data.clear(); - break; + Data = "N" + Auth.dump(); + } else { + Data = "N" + Login(Data.substr(Data.find(':') + 1)); + } + break; + default: + Data.clear(); + break; } - if(!Data.empty() && CSocket != -1){ - int res = send(CSocket, (Data+"\n").c_str(), int(Data.size())+1, 0); - if(res < 0){ + if (!Data.empty() && CSocket != -1) { + int res = send(CSocket, (Data + "\n").c_str(), int(Data.size()) + 1, 0); + if (res < 0) { debug("(Core) send failed with error: " + std::to_string(WSAGetLastError())); } } } -void GameHandler(SOCKET Client){ +void GameHandler(SOCKET Client) { - int32_t Size,Temp,Rcv; - char Header[10] = {0}; - do{ + int32_t Size, Temp, Rcv; + char Header[10] = { 0 }; + do { Rcv = 0; - do{ - Temp = recv(Client,&Header[Rcv],1,0); - if(Temp < 1)break; - if(!isdigit(Header[Rcv]) && Header[Rcv] != '>') { + do { + Temp = recv(Client, &Header[Rcv], 1, 0); + if (Temp < 1) + break; + if (!isdigit(Header[Rcv]) && Header[Rcv] != '>') { error("(Core) Invalid lua communication"); KillSocket(Client); return; } - }while(Header[Rcv++] != '>'); - if(Temp < 1)break; - if(std::from_chars(Header,&Header[Rcv],Size).ptr[0] != '>'){ - debug("(Core) Invalid lua Header -> " + std::string(Header,Rcv)); + } while (Header[Rcv++] != '>'); + if (Temp < 1) + break; + if (std::from_chars(Header, &Header[Rcv], Size).ptr[0] != '>') { + debug("(Core) Invalid lua Header -> " + std::string(Header, Rcv)); break; } - std::string Ret(Size,0); + std::string Ret(Size, 0); Rcv = 0; - do{ - Temp = recv(Client,&Ret[Rcv],Size-Rcv,0); - if(Temp < 1)break; + do { + Temp = recv(Client, &Ret[Rcv], Size - Rcv, 0); + if (Temp < 1) + break; Rcv += Temp; - }while(Rcv < Size); - if(Temp < 1)break; + } while (Rcv < Size); + if (Temp < 1) + break; std::thread Respond(Parse, Ret, Client); Respond.detach(); - }while(Temp > 0); + } while (Temp > 0); if (Temp == 0) { debug("(Core) Connection closing"); } else { @@ -195,10 +241,10 @@ void GameHandler(SOCKET Client){ NetReset(); KillSocket(Client); } -void localRes(){ +void localRes() { MStatus = " "; UlStatus = "Ulstart"; - if(ConfList != nullptr){ + if (ConfList != nullptr) { ConfList->clear(); delete ConfList; ConfList = nullptr; @@ -207,25 +253,31 @@ void localRes(){ } void CoreMain() { debug("Core Network on start!"); + SOCKET LSocket, CSocket; + struct addrinfo* res = nullptr; + struct addrinfo hints { }; + int iRes; +#ifdef _WIN32 WSADATA wsaData; - SOCKET LSocket,CSocket; - struct addrinfo *res = nullptr; - struct addrinfo hints{}; - int iRes = WSAStartup(514, &wsaData); //2.2 - if (iRes)debug("WSAStartup failed with error: " + std::to_string(iRes)); + iRes = WSAStartup(514, &wsaData); // 2.2 + if (iRes) + debug("WSAStartup failed with error: " + std::to_string(iRes)); +#endif + ZeroMemory(&hints, sizeof(hints)); + hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = AI_PASSIVE; iRes = getaddrinfo(nullptr, std::to_string(DEFAULT_PORT).c_str(), &hints, &res); - if (iRes){ + if (iRes) { debug("(Core) addr info failed with error: " + std::to_string(iRes)); WSACleanup(); return; } LSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (LSocket == -1){ + if (LSocket == -1) { debug("(Core) socket failed with error: " + std::to_string(WSAGetLastError())); freeaddrinfo(res); WSACleanup(); @@ -247,7 +299,7 @@ void CoreMain() { WSACleanup(); return; } - do{ + do { CSocket = accept(LSocket, nullptr, nullptr); if (CSocket == -1) { error("(Core) accept failed with error: " + std::to_string(WSAGetLastError())); @@ -257,28 +309,38 @@ void CoreMain() { info("Game Connected!"); GameHandler(CSocket); warn("Game Reconnecting..."); - }while(CSocket); + } while (CSocket); KillSocket(LSocket); WSACleanup(); } -int Handle(EXCEPTION_POINTERS *ep){ + +#if defined(_WIN32) +int Handle(EXCEPTION_POINTERS* ep) { char* hex = new char[100]; - sprintf_s(hex,100, "%lX", ep->ExceptionRecord->ExceptionCode); + sprintf_s(hex, 100, "%lX", ep->ExceptionRecord->ExceptionCode); except("(Core) Code : " + std::string(hex)); - delete [] hex; + delete[] hex; return 1; } - - -[[noreturn]] void CoreNetwork(){ - while(true) { -#ifndef __MINGW32__ - __try{ #endif - CoreMain(); -#ifndef __MINGW32__ - }__except(Handle(GetExceptionInformation())){} + +[[noreturn]] void CoreNetwork() { + while (true) { +#if not defined(__MINGW32__) + __try { #endif + + CoreMain(); + +#if not defined(__MINGW32__) and not defined(__linux__) + } __except (Handle(GetExceptionInformation())) { } +#elif not defined(__MINGW32__) and defined(__linux__) + } + catch (...) { + except("(Core) Code : " + std::string(strerror(errno))); + } +#endif + std::this_thread::sleep_for(std::chrono::seconds(1)); } } diff --git a/src/Network/DNS.cpp b/src/Network/DNS.cpp index eff5dcf..d28b829 100644 --- a/src/Network/DNS.cpp +++ b/src/Network/DNS.cpp @@ -7,25 +7,37 @@ /// #include + +#if defined(_WIN32) #include +#elif defined(__linux__) +#include "linuxfixes.h" +#include +#include +#endif + #include "Logger.h" -std::string GetAddr(const std::string&IP){ - if(IP.find_first_not_of("0123456789.") == -1)return IP; +std::string GetAddr(const std::string& IP) { + if (IP.find_first_not_of("0123456789.") == -1) + return IP; + hostent* host; +#ifdef _WIN32 WSADATA wsaData; - hostent *host; - if(WSAStartup(514, &wsaData) != 0){ + if (WSAStartup(514, &wsaData) != 0) { error("WSA Startup Failed!"); WSACleanup(); return ""; } +#endif + host = gethostbyname(IP.c_str()); - if(!host){ + if (!host) { error("DNS lookup failed! on " + IP); WSACleanup(); return "DNS"; } - std::string Ret = inet_ntoa(*((struct in_addr *)host->h_addr)); + std::string Ret = inet_ntoa(*((struct in_addr*)host->h_addr)); WSACleanup(); return Ret; } \ No newline at end of file diff --git a/src/Network/GlobalHandler.cpp b/src/Network/GlobalHandler.cpp index d6cb777..2b53584 100644 --- a/src/Network/GlobalHandler.cpp +++ b/src/Network/GlobalHandler.cpp @@ -5,130 +5,155 @@ /// /// Created by Anonymous275 on 7/25/2020 /// -#include "Network/network.h" +#include "Network/network.hpp" +#if defined(_WIN32) #include #include +#elif defined(__linux__) +#include "linuxfixes.h" +#include +#include +#include +#include +#include +#include +#endif + #include "Logger.h" #include +#include #include #include -#include -std::chrono::time_point PingStart,PingEnd; +std::chrono::time_point PingStart, PingEnd; bool GConnected = false; bool CServer = true; SOCKET CSocket = -1; SOCKET GSocket = -1; -int KillSocket(uint64_t Dead){ - if(Dead == (SOCKET)-1){ +int KillSocket(uint64_t Dead) { + if (Dead == (SOCKET)-1) { debug("Kill socket got -1 returning..."); return 0; } - shutdown(Dead,SD_BOTH); + shutdown(Dead, SD_BOTH); int a = closesocket(Dead); - if(a != 0){ + if (a != 0) { warn("Failed to close socket!"); } return a; } -bool CheckBytes(uint32_t Bytes){ - if(Bytes == 0){ +bool CheckBytes(uint32_t Bytes) { + if (Bytes == 0) { debug("(Proxy) Connection closing"); return false; - }else if(Bytes < 0){ + } else if (Bytes < 0) { debug("(Proxy) send failed with error: " + std::to_string(WSAGetLastError())); return false; } return true; } -void GameSend(std::string Data){ +void GameSend(std::string Data) { static std::mutex Lock; std::scoped_lock Guard(Lock); - if(TCPTerminate || !GConnected || CSocket == -1)return; - int32_t Size,Temp,Sent; + if (TCPTerminate || !GConnected || CSocket == -1) + return; + int32_t Size, Temp, Sent; Data += '\n'; Size = int32_t(Data.size()); Sent = 0; - #ifdef DEBUG - if(Size > 1000){ - debug("Launcher -> game (" +std::to_string(Size)+")"); - } - #endif - do{ - if(Sent > -1){ +#ifdef DEBUG + if (Size > 1000) { + debug("Launcher -> game (" + std::to_string(Size) + ")"); + } +#endif + do { + if (Sent > -1) { Temp = send(CSocket, &Data[Sent], Size - Sent, 0); - } - if(!CheckBytes(Temp))return; + } + if (!CheckBytes(Temp)) + return; Sent += Temp; - }while(Sent < Size); + } while (Sent < Size); } -void ServerSend(std::string Data, bool Rel){ - if(Terminate || Data.empty())return; - if(Data.find("Zp") != std::string::npos && Data.size() > 500){ +void ServerSend(std::string Data, bool Rel) { + if (Terminate || Data.empty()) + return; + if (Data.find("Zp") != std::string::npos && Data.size() > 500) { abort(); } char C = 0; bool Ack = false; int DLen = int(Data.length()); - if(DLen > 3)C = Data.at(0); - if (C == 'O' || C == 'T')Ack = true; - if(C == 'N' || C == 'W' || C == 'Y' || C == 'V' || C == 'E' || C == 'C')Rel = true; - if(Ack || Rel){ - if(Ack || DLen > 1000)SendLarge(Data); - else TCPSend(Data,TCPSock); - }else UDPSend(Data); + if (DLen > 3) + C = Data.at(0); + if (C == 'O' || C == 'T') + Ack = true; + if (C == 'N' || C == 'W' || C == 'Y' || C == 'V' || C == 'E' || C == 'C') + Rel = true; + if (Ack || Rel) { + if (Ack || DLen > 1000) + SendLarge(Data); + else + TCPSend(Data, TCPSock); + } else + UDPSend(Data); if (DLen > 1000) { debug("(Launcher->Server) Bytes sent: " + std::to_string(Data.length()) + " : " - + Data.substr(0, 10) - + Data.substr(Data.length() - 10)); - }else if(C == 'Z'){ - //debug("(Game->Launcher) : " + Data); + + Data.substr(0, 10) + + Data.substr(Data.length() - 10)); + } else if (C == 'Z') { + // debug("(Game->Launcher) : " + Data); } } -void NetReset(){ +void NetReset() { TCPTerminate = false; GConnected = false; Terminate = false; UlStatus = "Ulstart"; MStatus = " "; - if(UDPSock != (SOCKET)(-1)){ + if (UDPSock != (SOCKET)(-1)) { debug("Terminating UDP Socket : " + std::to_string(TCPSock)); KillSocket(UDPSock); } UDPSock = -1; - if(TCPSock != (SOCKET)(-1)){ + if (TCPSock != (SOCKET)(-1)) { debug("Terminating TCP Socket : " + std::to_string(TCPSock)); KillSocket(TCPSock); } TCPSock = -1; - if(GSocket != (SOCKET)(-1)){ + if (GSocket != (SOCKET)(-1)) { debug("Terminating GTCP Socket : " + std::to_string(GSocket)); KillSocket(GSocket); } GSocket = -1; } -SOCKET SetupListener(){ - if(GSocket != -1)return GSocket; - struct addrinfo *result = nullptr; - struct addrinfo hints{}; +SOCKET SetupListener() { + if (GSocket != -1) + return GSocket; + struct addrinfo* result = nullptr; + struct addrinfo hints { }; + int iRes; +#ifdef _WIN32 WSADATA wsaData; - int iRes = WSAStartup(514, &wsaData); //2.2 + iRes = WSAStartup(514, &wsaData); // 2.2 if (iRes != 0) { error("(Proxy) WSAStartup failed with error: " + std::to_string(iRes)); return -1; } +#endif + ZeroMemory(&hints, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = AI_PASSIVE; - iRes = getaddrinfo(nullptr, std::to_string(DEFAULT_PORT+1).c_str(), &hints, &result); + iRes = getaddrinfo(nullptr, std::to_string(DEFAULT_PORT + 1).c_str(), &hints, &result); if (iRes != 0) { error("(Proxy) info failed with error: " + std::to_string(iRes)); WSACleanup(); @@ -140,7 +165,7 @@ SOCKET SetupListener(){ WSACleanup(); return -1; } - iRes = bind(GSocket, result->ai_addr, (int) result->ai_addrlen); + iRes = bind(GSocket, result->ai_addr, (int)result->ai_addrlen); if (iRes == SOCKET_ERROR) { error("(Proxy) bind failed with error: " + std::to_string(WSAGetLastError())); freeaddrinfo(result); @@ -158,57 +183,61 @@ SOCKET SetupListener(){ } return GSocket; } -void AutoPing(){ - while(!Terminate){ - ServerSend("p",false); +void AutoPing() { + while (!Terminate) { + ServerSend("p", false); PingStart = std::chrono::high_resolution_clock::now(); - std::this_thread::sleep_for(std::chrono::seconds (1)); + std::this_thread::sleep_for(std::chrono::seconds(1)); } } int ClientID = -1; -void ParserAsync(const std::string& Data){ - if(Data.empty())return; - char Code = Data.at(0),SubCode = 0; - if(Data.length() > 1)SubCode = Data.at(1); +void ParserAsync(const std::string& Data) { + if (Data.empty()) + return; + char Code = Data.at(0), SubCode = 0; + if (Data.length() > 1) + SubCode = Data.at(1); switch (Code) { - case 'p': - PingEnd = std::chrono::high_resolution_clock::now(); - if(PingStart > PingEnd)ping = 0; - else ping = int(std::chrono::duration_cast(PingEnd-PingStart).count()); - return; - case 'M': - MStatus = Data; - UlStatus = "Uldone"; - return; - default: - break; + case 'p': + PingEnd = std::chrono::high_resolution_clock::now(); + if (PingStart > PingEnd) + ping = 0; + else + ping = int(std::chrono::duration_cast(PingEnd - PingStart).count()); + return; + case 'M': + MStatus = Data; + UlStatus = "Uldone"; + return; + default: + break; } GameSend(Data); } -void ServerParser(const std::string& Data){ +void ServerParser(const std::string& Data) { ParserAsync(Data); } -void NetMain(const std::string& IP, int Port){ +void NetMain(const std::string& IP, int Port) { std::thread Ping(AutoPing); Ping.detach(); - UDPClientMain(IP,Port); + UDPClientMain(IP, Port); CServer = true; Terminate = true; info("Connection Terminated!"); } -void TCPGameServer(const std::string& IP, int Port){ +void TCPGameServer(const std::string& IP, int Port) { GSocket = SetupListener(); - while (!TCPTerminate && GSocket != -1){ + while (!TCPTerminate && GSocket != -1) { debug("MAIN LOOP OF GAME SERVER"); GConnected = false; - if(!CServer){ + if (!CServer) { warn("Connection still alive terminating"); NetReset(); TCPTerminate = true; Terminate = true; break; } - if(CServer) { + if (CServer) { std::thread Client(TCPClientMain, IP, Port); Client.detach(); } @@ -219,45 +248,52 @@ void TCPGameServer(const std::string& IP, int Port){ } debug("(Proxy) Game Connected!"); GConnected = true; - if(CServer){ + if (CServer) { std::thread t1(NetMain, IP, Port); t1.detach(); CServer = false; } - int32_t Size,Temp,Rcv; - char Header[10] = {0}; + int32_t Size, Temp, Rcv; + char Header[10] = { 0 }; - //Read byte by byte until '>' is rcved then get the size and read based on it - do{ + // Read byte by byte until '>' is rcved then get the size and read based on it + do { Rcv = 0; - do{ - Temp = recv(CSocket,&Header[Rcv],1,0); - if(Temp < 1 || TCPTerminate)break; - }while(Header[Rcv++] != '>'); - if(Temp < 1 || TCPTerminate)break; - if(std::from_chars(Header,&Header[Rcv],Size).ptr[0] != '>'){ - debug("(Game) Invalid lua Header -> " + std::string(Header,Rcv)); + do { + Temp = recv(CSocket, &Header[Rcv], 1, 0); + if (Temp < 1 || TCPTerminate) + break; + } while (Header[Rcv++] != '>'); + if (Temp < 1 || TCPTerminate) + break; + if (std::from_chars(Header, &Header[Rcv], Size).ptr[0] != '>') { + debug("(Game) Invalid lua Header -> " + std::string(Header, Rcv)); break; } - std::string Ret(Size,0); + std::string Ret(Size, 0); Rcv = 0; - do{ - Temp = recv(CSocket,&Ret[Rcv],Size-Rcv,0); - if(Temp < 1)break; + do { + Temp = recv(CSocket, &Ret[Rcv], Size - Rcv, 0); + if (Temp < 1) + break; Rcv += Temp; - }while(Rcv < Size && !TCPTerminate); - if(Temp < 1 || TCPTerminate)break; + } while (Rcv < Size && !TCPTerminate); + if (Temp < 1 || TCPTerminate) + break; - ServerSend(Ret,false); + ServerSend(Ret, false); - }while(Temp > 0 && !TCPTerminate); - if(Temp == 0)debug("(Proxy) Connection closing"); - else debug("(Proxy) recv failed error : " + std::to_string(WSAGetLastError())); + } while (Temp > 0 && !TCPTerminate); + if (Temp == 0) + debug("(Proxy) Connection closing"); + else + debug("(Proxy) recv failed error : " + std::to_string(WSAGetLastError())); } TCPTerminate = true; GConnected = false; Terminate = true; - if(CSocket != SOCKET_ERROR)KillSocket(CSocket); + if (CSocket != SOCKET_ERROR) + KillSocket(CSocket); debug("END OF GAME SERVER"); } \ No newline at end of file diff --git a/src/Network/Http.cpp b/src/Network/Http.cpp index 503eb80..64a7382 100644 --- a/src/Network/Http.cpp +++ b/src/Network/Http.cpp @@ -7,175 +7,168 @@ /// #define CPPHTTPLIB_OPENSSL_SUPPORT -#include -#include -#include #include "Http.h" -#include +#include #include #include +#include #include +#include +#include #include void WriteHttpDebug(const httplib::Client& client, const std::string& method, const std::string& target, const httplib::Result& result) try { - const std::filesystem::path folder = ".https_debug"; - std::filesystem::create_directories(folder); - if (!std::filesystem::exists(folder / "WHAT IS THIS FOLDER.txt")) { - std::ofstream ignore { folder / "WHAT IS THIS FOLDER.txt" }; - ignore << "This folder exists to help debug current issues with the backend. Do not share this folder with anyone but BeamMP staff. It contains detailed logs of any failed http requests." << std::endl; - } - const auto file = folder / (method + ".json"); - // 1 MB limit - if (std::filesystem::exists(file) && std::filesystem::file_size(file) > 1'000'000) { - std::filesystem::rename(file, file.generic_string() + ".bak"); - } + const std::filesystem::path folder = ".https_debug"; + std::filesystem::create_directories(folder); + if (!std::filesystem::exists(folder / "WHAT IS THIS FOLDER.txt")) { + std::ofstream ignore { folder / "WHAT IS THIS FOLDER.txt" }; + ignore << "This folder exists to help debug current issues with the backend. Do not share this folder with anyone but BeamMP staff. It contains detailed logs of any failed http requests." << std::endl; + } + const auto file = folder / (method + ".json"); + // 1 MB limit + if (std::filesystem::exists(file) && std::filesystem::file_size(file) > 1'000'000) { + std::filesystem::rename(file, file.generic_string() + ".bak"); + } - std::ofstream of { file, std::ios::app }; - nlohmann::json js { - { "utc", std::chrono::system_clock::now().time_since_epoch().count() }, - { "target", target }, - { "client_info", { - { "openssl_verify_result", client.get_openssl_verify_result() }, - { "host", client.host()}, - { "port", client.port()}, - { "socket_open", client.is_socket_open()}, - {"valid", client.is_valid()}, - } - }, - }; - if (result) { - auto value = result.value(); - js["result"] = {}; - js["result"]["body"] = value.body; - js["result"]["status"] = value.status; - js["result"]["headers"] = value.headers; - js["result"]["version"] = value.version; - js["result"]["location"] = value.location; - js["result"]["reason"] = value.reason; - } - of << js.dump(); -} -catch (const std::exception& e) { - error(e.what()); + std::ofstream of { file, std::ios::app }; + nlohmann::json js { + { "utc", std::chrono::system_clock::now().time_since_epoch().count() }, + { "target", target }, + { "client_info", { + { "openssl_verify_result", client.get_openssl_verify_result() }, + { "host", client.host() }, + { "port", client.port() }, + { "socket_open", client.is_socket_open() }, + { "valid", client.is_valid() }, + } }, + }; + if (result) { + auto value = result.value(); + js["result"] = {}; + js["result"]["body"] = value.body; + js["result"]["status"] = value.status; + js["result"]["headers"] = value.headers; + js["result"]["version"] = value.version; + js["result"]["location"] = value.location; + js["result"]["reason"] = value.reason; + } + of << js.dump(); +} catch (const std::exception& e) { + error(e.what()); } bool HTTP::isDownload = false; std::string HTTP::Get(const std::string& IP) { - static std::mutex Lock; - std::scoped_lock Guard(Lock); + static std::mutex Lock; + std::scoped_lock Guard(Lock); - auto pos = IP.find('/', 10); + auto pos = IP.find('/', 10); - httplib::Client cli(IP.substr(0, pos).c_str()); - cli.set_connection_timeout(std::chrono::seconds(10)); - cli.set_follow_location(true); - auto res = cli.Get(IP.substr(pos).c_str(), ProgressBar); - std::string Ret; + httplib::Client cli(IP.substr(0, pos).c_str()); + cli.set_connection_timeout(std::chrono::seconds(10)); + cli.set_follow_location(true); + auto res = cli.Get(IP.substr(pos).c_str(), ProgressBar); + std::string Ret; - if (res) { - if (res->status == 200) { - Ret = res->body; - } - else { - WriteHttpDebug(cli, "GET", IP, res); - error("Failed to GET '" + IP + "': " + res->reason + ", ssl verify = " + std::to_string(cli.get_openssl_verify_result())); - - } - } - else { - if (isDownload) { - std::cout << "\n"; - } - WriteHttpDebug(cli, "GET", IP, res); - error("HTTP Get failed on " + to_string(res.error()) + ", ssl verify = " + std::to_string(cli.get_openssl_verify_result())); - - } + if (res) { + if (res->status == 200) { + Ret = res->body; + } else { + WriteHttpDebug(cli, "GET", IP, res); + error("Failed to GET '" + IP + "': " + res->reason + ", ssl verify = " + std::to_string(cli.get_openssl_verify_result())); + } + } else { + if (isDownload) { + std::cout << "\n"; + } + WriteHttpDebug(cli, "GET", IP, res); + error("HTTP Get failed on " + to_string(res.error()) + ", ssl verify = " + std::to_string(cli.get_openssl_verify_result())); + } - return Ret; + return Ret; } std::string HTTP::Post(const std::string& IP, const std::string& Fields) { - static std::mutex Lock; - std::scoped_lock Guard(Lock); + static std::mutex Lock; + std::scoped_lock Guard(Lock); - auto pos = IP.find('/', 10); + auto pos = IP.find('/', 10); - httplib::Client cli(IP.substr(0, pos).c_str()); - cli.set_connection_timeout(std::chrono::seconds(10)); - std::string Ret; + httplib::Client cli(IP.substr(0, pos).c_str()); + cli.set_connection_timeout(std::chrono::seconds(10)); + std::string Ret; - if (!Fields.empty()) { - httplib::Result res = cli.Post(IP.substr(pos).c_str(), Fields, "application/json"); + if (!Fields.empty()) { + httplib::Result res = cli.Post(IP.substr(pos).c_str(), Fields, "application/json"); - if (res) { - if (res->status != 200) { - error(res->reason); - } - Ret = res->body; - } - else { - WriteHttpDebug(cli, "POST", IP, res); - error("HTTP Post failed on " + to_string(res.error()) + ", ssl verify = " + std::to_string(cli.get_openssl_verify_result())); - - } - } - else { - httplib::Result res = cli.Post(IP.substr(pos).c_str()); - if (res) { - if (res->status != 200) { - error(res->reason); - } - Ret = res->body; - } - else { - WriteHttpDebug(cli, "POST", IP, res); - error("HTTP Post failed on " + to_string(res.error()) + ", ssl verify = " + std::to_string(cli.get_openssl_verify_result())); - - } - } + if (res) { + if (res->status != 200) { + error(res->reason); + } + Ret = res->body; + } else { + WriteHttpDebug(cli, "POST", IP, res); + error("HTTP Post failed on " + to_string(res.error()) + ", ssl verify = " + std::to_string(cli.get_openssl_verify_result())); + } + } else { + httplib::Result res = cli.Post(IP.substr(pos).c_str()); + if (res) { + if (res->status != 200) { + error(res->reason); + } + Ret = res->body; + } else { + WriteHttpDebug(cli, "POST", IP, res); + error("HTTP Post failed on " + to_string(res.error()) + ", ssl verify = " + std::to_string(cli.get_openssl_verify_result())); + } + } - if (Ret.empty())return "-1"; - else return Ret; + if (Ret.empty()) + return "-1"; + else + return Ret; } bool HTTP::ProgressBar(size_t c, size_t t) { - if (isDownload) { - static double last_progress, progress_bar_adv; - progress_bar_adv = round(c / double(t) * 25); - std::cout << "\r"; - std::cout << "Progress : [ "; - std::cout << round(c / double(t) * 100); - std::cout << "% ] ["; - int i; - for (i = 0; i <= progress_bar_adv; i++)std::cout << "#"; - for (i = 0; i < 25 - progress_bar_adv; i++)std::cout << "."; - std::cout << "]"; - last_progress = round(c / double(t) * 100); - } - return true; + if (isDownload) { + static double last_progress, progress_bar_adv; + progress_bar_adv = round(c / double(t) * 25); + std::cout << "\r"; + std::cout << "Progress : [ "; + std::cout << round(c / double(t) * 100); + std::cout << "% ] ["; + int i; + for (i = 0; i <= progress_bar_adv; i++) + std::cout << "#"; + for (i = 0; i < 25 - progress_bar_adv; i++) + std::cout << "."; + std::cout << "]"; + last_progress = round(c / double(t) * 100); + } + return true; } bool HTTP::Download(const std::string& IP, const std::string& Path) { - static std::mutex Lock; - std::scoped_lock Guard(Lock); + static std::mutex Lock; + std::scoped_lock Guard(Lock); - isDownload = true; - std::string Ret = Get(IP); - isDownload = false; + isDownload = true; + std::string Ret = Get(IP); + isDownload = false; - if (Ret.empty())return false; + if (Ret.empty()) + return false; - std::ofstream File(Path, std::ios::binary); - if (File.is_open()) { - File << Ret; - File.close(); - std::cout << "\n"; - info("Download Complete!"); - } - else { - error("Failed to open file directory: " + Path); - return false; - } + std::ofstream File(Path, std::ios::binary); + if (File.is_open()) { + File << Ret; + File.close(); + std::cout << "\n"; + info("Download Complete!"); + } else { + error("Failed to open file directory: " + Path); + return false; + } - return true; + return true; } diff --git a/src/Network/Resources.cpp b/src/Network/Resources.cpp index 29de1cb..e3d8049 100644 --- a/src/Network/Resources.cpp +++ b/src/Network/Resources.cpp @@ -6,129 +6,151 @@ /// Created by Anonymous275 on 4/11/2020 /// -#include "Network/network.h" +#include "Network/network.hpp" + +#if defined(_WIN32) #include -#include -#include "Startup.h" -#include "Logger.h" -#include +#elif defined(__linux__) +#include #include +#include +#include +#include +#include +#endif + +#include "Logger.h" +#include "Startup.h" +#include +#include +#include +#include #include +#include +#include #include #include -#include #include -#include -#include namespace fs = std::filesystem; std::string ListOfMods; -std::vector Split(const std::string& String,const std::string& delimiter){ +std::vector Split(const std::string& String, const std::string& delimiter) { std::vector Val; size_t pos; - std::string token,s = String; + std::string token, s = String; while ((pos = s.find(delimiter)) != std::string::npos) { token = s.substr(0, pos); - if(!token.empty())Val.push_back(token); + if (!token.empty()) + Val.push_back(token); s.erase(0, pos + delimiter.length()); } - if(!s.empty())Val.push_back(s); + if (!s.empty()) + Val.push_back(s); return Val; } -void CheckForDir(){ - if(!fs::exists("Resources")){ +void CheckForDir() { + if (!fs::exists("Resources")) { +// Could we just use fs::create_directory instead? +#if defined(_WIN32) _wmkdir(L"Resources"); +#elif defined(__linux__) + fs::create_directory(L"Resources"); +#endif } } -void WaitForConfirm(){ - while(!Terminate && !ModLoaded){ +void WaitForConfirm() { + while (!Terminate && !ModLoaded) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); } ModLoaded = false; } - -void Abord(){ +void Abord() { Terminate = true; TCPTerminate = true; info("Terminated!"); } -std::string Auth(SOCKET Sock){ - TCPSend("VC" + GetVer(),Sock); +std::string Auth(SOCKET Sock) { + TCPSend("VC" + GetVer(), Sock); auto Res = TCPRcv(Sock); - if(Res.empty() || Res[0] == 'E' || Res[0] == 'K'){ + if (Res.empty() || Res[0] == 'E' || Res[0] == 'K') { Abord(); return ""; } - TCPSend(PublicKey,Sock); - if(Terminate)return ""; + TCPSend(PublicKey, Sock); + if (Terminate) + return ""; Res = TCPRcv(Sock); - if(Res.empty() || Res[0] != 'P'){ + if (Res.empty() || Res[0] != 'P') { Abord(); return ""; } Res = Res.substr(1); - if(Res.find_first_not_of("0123456789") == std::string::npos){ + if (Res.find_first_not_of("0123456789") == std::string::npos) { ClientID = std::stoi(Res); - }else{ + } else { Abord(); UUl("Authentication failed!"); return ""; } - TCPSend("SR",Sock); - if(Terminate)return ""; + TCPSend("SR", Sock); + if (Terminate) + return ""; Res = TCPRcv(Sock); - if(Res[0] == 'E' || Res[0] == 'K'){ + if (Res[0] == 'E' || Res[0] == 'K') { Abord(); return ""; } - if(Res.empty() || Res == "-"){ + if (Res.empty() || Res == "-") { info("Didn't Receive any mods..."); ListOfMods = "-"; - TCPSend("Done",Sock); + TCPSend("Done", Sock); info("Done!"); return ""; } return Res; } -void UpdateUl(bool D,const std::string& msg){ - if(D)UlStatus = "UlDownloading Resource " + msg; - else UlStatus = "UlLoading Resource " + msg; +void UpdateUl(bool D, const std::string& msg) { + if (D) + UlStatus = "UlDownloading Resource " + msg; + else + UlStatus = "UlLoading Resource " + msg; } -void AsyncUpdate(uint64_t& Rcv,uint64_t Size,const std::string& Name){ +void AsyncUpdate(uint64_t& Rcv, uint64_t Size, const std::string& Name) { do { double pr = double(Rcv) / double(Size) * 100; std::string Per = std::to_string(trunc(pr * 10) / 10); UpdateUl(true, Name + " (" + Per.substr(0, Per.find('.') + 2) + "%)"); std::this_thread::sleep_for(std::chrono::milliseconds(100)); - }while(!Terminate && Rcv < Size); + } while (!Terminate && Rcv < Size); } -char* TCPRcvRaw(SOCKET Sock,uint64_t& GRcv, uint64_t Size){ - if(Sock == -1){ +char* TCPRcvRaw(SOCKET Sock, uint64_t& GRcv, uint64_t Size) { + if (Sock == -1) { Terminate = true; UUl("Invalid Socket"); return nullptr; } char* File = new char[Size]; uint64_t Rcv = 0; - do{ - int Len = int(Size-Rcv); - if(Len > 1000000)Len = 1000000; + do { + int Len = int(Size - Rcv); + if (Len > 1000000) + Len = 1000000; int32_t Temp = recv(Sock, &File[Rcv], Len, MSG_WAITALL); - if(Temp < 1){ + if (Temp < 1) { info(std::to_string(Temp)); UUl("Socket Closed Code 1"); KillSocket(Sock); @@ -138,18 +160,18 @@ char* TCPRcvRaw(SOCKET Sock,uint64_t& GRcv, uint64_t Size){ } Rcv += Temp; GRcv += Temp; - }while(Rcv < Size && !Terminate); + } while (Rcv < Size && !Terminate); return File; } -void MultiKill(SOCKET Sock,SOCKET Sock1){ +void MultiKill(SOCKET Sock, SOCKET Sock1) { KillSocket(Sock1); KillSocket(Sock); Terminate = true; } -SOCKET InitDSock(){ +SOCKET InitDSock() { SOCKET DSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); SOCKADDR_IN ServerAddr; - if(DSock < 1){ + if (DSock < 1) { KillSocket(DSock); Terminate = true; return 0; @@ -157,13 +179,13 @@ SOCKET InitDSock(){ ServerAddr.sin_family = AF_INET; ServerAddr.sin_port = htons(LastPort); inet_pton(AF_INET, LastIP.c_str(), &ServerAddr.sin_addr); - if(connect(DSock, (SOCKADDR *) &ServerAddr, sizeof(ServerAddr)) != 0){ + if (connect(DSock, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr)) != 0) { KillSocket(DSock); Terminate = true; return 0; } - char Code[2] = {'D',char(ClientID)}; - if(send(DSock,Code,2,0) != 2){ + char Code[2] = { 'D', char(ClientID) }; + if (send(DSock, Code, 2, 0) != 2) { KillSocket(DSock); Terminate = true; return 0; @@ -171,55 +193,56 @@ SOCKET InitDSock(){ return DSock; } -std::string MultiDownload(SOCKET MSock,SOCKET DSock, uint64_t Size, const std::string& Name){ +std::string MultiDownload(SOCKET MSock, SOCKET DSock, uint64_t Size, const std::string& Name) { - uint64_t GRcv = 0, MSize = Size/2, DSize = Size - MSize; + uint64_t GRcv = 0, MSize = Size / 2, DSize = Size - MSize; - std::thread Au(AsyncUpdate,std::ref(GRcv), Size, Name); + std::thread Au(AsyncUpdate, std::ref(GRcv), Size, Name); - std::packaged_task task([&] { return TCPRcvRaw(MSock,GRcv,MSize); }); + std::packaged_task task([&] { return TCPRcvRaw(MSock, GRcv, MSize); }); std::future f1 = task.get_future(); std::thread Dt(std::move(task)); Dt.detach(); - char* DData = TCPRcvRaw(DSock,GRcv,DSize); + char* DData = TCPRcvRaw(DSock, GRcv, DSize); - if(!DData){ - MultiKill(MSock,DSock); + if (!DData) { + MultiKill(MSock, DSock); return ""; } f1.wait(); char* MData = f1.get(); - if(!MData){ - MultiKill(MSock,DSock); + if (!MData) { + MultiKill(MSock, DSock); return ""; } - if(Au.joinable())Au.join(); + if (Au.joinable()) + Au.join(); + /// omg yes very ugly my god but i was in a rush will revisit + std::string Ret(Size, 0); + memcpy(&Ret[0], MData, MSize); + delete[] MData; - ///omg yes very ugly my god but i was in a rush will revisit - std::string Ret(Size,0); - memcpy_s(&Ret[0],MSize,MData,MSize); - delete[]MData; - - memcpy_s(&Ret[MSize],DSize,DData,DSize); - delete[]DData; + memcpy(&Ret[MSize], DData, DSize); + delete[] DData; return Ret; } -void InvalidResource(const std::string& File){ +void InvalidResource(const std::string& File) { UUl("Invalid mod \"" + File + "\""); warn("The server tried to sync \"" + File + "\" that is not a .zip file!"); Terminate = true; } -void SyncResources(SOCKET Sock){ +void SyncResources(SOCKET Sock) { std::string Ret = Auth(Sock); - if(Ret.empty())return; + if (Ret.empty()) + return; info("Checking Resources..."); CheckForDir(); @@ -230,46 +253,58 @@ void SyncResources(SOCKET Sock){ list.clear(); Ret.clear(); - int Amount = 0,Pos = 0; - std::string a,t; - for(const std::string&name : FNames){ - if(!name.empty()){ + int Amount = 0, Pos = 0; + std::string a, t; + for (const std::string& name : FNames) { + if (!name.empty()) { t += name.substr(name.find_last_of('/') + 1) + ";"; } } - if(t.empty())ListOfMods = "-"; - else ListOfMods = t; + if (t.empty()) + ListOfMods = "-"; + else + ListOfMods = t; t.clear(); - for(auto FN = FNames.begin(),FS = FSizes.begin(); FN != FNames.end() && !Terminate; ++FN,++FS) { + for (auto FN = FNames.begin(), FS = FSizes.begin(); FN != FNames.end() && !Terminate; ++FN, ++FS) { auto pos = FN->find_last_of('/'); auto ZIP = FN->find(".zip"); if (ZIP == std::string::npos || FN->length() - ZIP != 4) { InvalidResource(*FN); return; } - if (pos == std::string::npos)continue; + if (pos == std::string::npos) + continue; Amount++; } - if(!FNames.empty())info("Syncing..."); + if (!FNames.empty()) + info("Syncing..."); SOCKET DSock = InitDSock(); - for(auto FN = FNames.begin(),FS = FSizes.begin(); FN != FNames.end() && !Terminate; ++FN,++FS) { + for (auto FN = FNames.begin(), FS = FSizes.begin(); FN != FNames.end() && !Terminate; ++FN, ++FS) { auto pos = FN->find_last_of('/'); if (pos != std::string::npos) { a = "Resources" + FN->substr(pos); - } else continue; + } else + continue; Pos++; if (fs::exists(a)) { - if (FS->find_first_not_of("0123456789") != std::string::npos)continue; - if (fs::file_size(a) == std::stoull(*FS)){ - UpdateUl(false,std::to_string(Pos) + "/" + std::to_string(Amount) + ": " + a.substr(a.find_last_of('/'))); + if (FS->find_first_not_of("0123456789") != std::string::npos) + continue; + if (fs::file_size(a) == std::stoull(*FS)) { + UpdateUl(false, std::to_string(Pos) + "/" + std::to_string(Amount) + ": " + a.substr(a.find_last_of('/'))); std::this_thread::sleep_for(std::chrono::milliseconds(50)); try { - if(!fs::exists(GetGamePath() + "mods/multiplayer")){ + if (!fs::exists(GetGamePath() + "mods/multiplayer")) { fs::create_directories(GetGamePath() + "mods/multiplayer"); } auto name = GetGamePath() + "mods/multiplayer" + a.substr(a.find_last_of('/')); +#if defined(__linux__) + // Linux version of the game doesnt support uppercase letters in mod names + for (char& c : name) { + c = ::tolower(c); + } +#endif auto tmp_name = name + ".tmp"; - fs::copy_file(a,tmp_name,fs::copy_options::overwrite_existing); + fs::copy_file(a, tmp_name, fs::copy_options::overwrite_existing); fs::rename(tmp_name, name); } catch (std::exception& e) { error("Failed copy to the mods folder! " + std::string(e.what())); @@ -278,15 +313,16 @@ void SyncResources(SOCKET Sock){ } WaitForConfirm(); continue; - }else remove(a.c_str()); + } else + remove(a.c_str()); } CheckForDir(); std::string FName = a.substr(a.find_last_of('/')); do { - TCPSend("f" + *FN,Sock); + TCPSend("f" + *FN, Sock); std::string Data = TCPRcv(Sock); - if (Data == "CO" || Terminate){ + if (Data == "CO" || Terminate) { Terminate = true; UUl("Server cannot find " + FName); break; @@ -294,10 +330,11 @@ void SyncResources(SOCKET Sock){ std::string Name = std::to_string(Pos) + "/" + std::to_string(Amount) + ": " + FName; - Data = MultiDownload(Sock,DSock,std::stoull(*FS), Name); + Data = MultiDownload(Sock, DSock, std::stoull(*FS), Name); - if(Terminate)break; - UpdateUl(false,std::to_string(Pos)+"/"+std::to_string(Amount)+": "+FName); + if (Terminate) + break; + UpdateUl(false, std::to_string(Pos) + "/" + std::to_string(Amount) + ": " + FName); std::ofstream LFS; LFS.open(a.c_str(), std::ios_base::app | std::ios::binary); if (LFS.is_open()) { @@ -305,21 +342,29 @@ void SyncResources(SOCKET Sock){ LFS.close(); } - }while(fs::file_size(a) != std::stoull(*FS) && !Terminate); - if(!Terminate){ - if(!fs::exists(GetGamePath() + "mods/multiplayer")){ + } while (fs::file_size(a) != std::stoull(*FS) && !Terminate); + if (!Terminate) { + if (!fs::exists(GetGamePath() + "mods/multiplayer")) { fs::create_directories(GetGamePath() + "mods/multiplayer"); } - fs::copy_file(a,GetGamePath() + "mods/multiplayer" + FName, fs::copy_options::overwrite_existing); + +// Linux version of the game doesnt support uppercase letters in mod names +#if defined(__linux__) + for (char& c : FName) { + c = ::tolower(c); + } +#endif + + fs::copy_file(a, GetGamePath() + "mods/multiplayer" + FName, fs::copy_options::overwrite_existing); } WaitForConfirm(); } KillSocket(DSock); - if(!Terminate){ - TCPSend("Done",Sock); + if (!Terminate) { + TCPSend("Done", Sock); info("Done!"); - }else{ + } else { UlStatus = "Ulstart"; info("Connection Terminated!"); } -} \ No newline at end of file +} diff --git a/src/Network/VehicleData.cpp b/src/Network/VehicleData.cpp index d1c399d..4f1269f 100644 --- a/src/Network/VehicleData.cpp +++ b/src/Network/VehicleData.cpp @@ -5,69 +5,91 @@ /// /// Created by Anonymous275 on 5/8/2020 /// +#include "Network/network.hpp" #include "Zlib/Compressor.h" -#include "Network/network.h" +#if defined(_WIN32) #include +#elif defined(__linux__) +#include "linuxfixes.h" +#include +#include +#include +#include +#include +#include +#endif + #include "Logger.h" -#include #include +#include SOCKET UDPSock = -1; sockaddr_in* ToServer = nullptr; -void UDPSend(std::string Data){ - if(ClientID == -1 || UDPSock == -1)return; - if(Data.length() > 400){ +void UDPSend(std::string Data) { + if (ClientID == -1 || UDPSock == -1) + return; + if (Data.length() > 400) { std::string CMP(Comp(Data)); Data = "ABG:" + CMP; } - std::string Packet = char(ClientID+1) + std::string(":") + Data; + std::string Packet = char(ClientID + 1) + std::string(":") + Data; int sendOk = sendto(UDPSock, Packet.c_str(), int(Packet.size()), 0, (sockaddr*)ToServer, sizeof(*ToServer)); - if (sendOk == SOCKET_ERROR)error("Error Code : " + std::to_string(WSAGetLastError())); + if (sendOk == SOCKET_ERROR) + error("Error Code : " + std::to_string(WSAGetLastError())); } - -void SendLarge(std::string Data){ - if(Data.length() > 400){ +void SendLarge(std::string Data) { + if (Data.length() > 400) { std::string CMP(Comp(Data)); Data = "ABG:" + CMP; } - TCPSend(Data,TCPSock); + TCPSend(Data, TCPSock); } -void UDPParser(std::string Packet){ - if(Packet.substr(0,4) == "ABG:"){ +void UDPParser(std::string Packet) { + if (Packet.substr(0, 4) == "ABG:") { Packet = DeComp(Packet.substr(4)); } ServerParser(Packet); } -void UDPRcv(){ - sockaddr_in FromServer{}; +void UDPRcv() { + sockaddr_in FromServer {}; +#if defined(_WIN32) int clientLength = sizeof(FromServer); +#elif defined(__linux__) + socklen_t clientLength = sizeof(FromServer); +#endif ZeroMemory(&FromServer, clientLength); - std::string Ret(10240,0); - if(UDPSock == -1)return; + std::string Ret(10240, 0); + if (UDPSock == -1) + return; int32_t Rcv = recvfrom(UDPSock, &Ret[0], 10240, 0, (sockaddr*)&FromServer, &clientLength); - if (Rcv == SOCKET_ERROR)return; - UDPParser(Ret.substr(0,Rcv)); + if (Rcv == SOCKET_ERROR) + return; + UDPParser(Ret.substr(0, Rcv)); } -void UDPClientMain(const std::string& IP,int Port){ +void UDPClientMain(const std::string& IP, int Port) { +#ifdef _WIN32 WSADATA data; - if (WSAStartup(514, &data)){ + if (WSAStartup(514, &data)) { error("Can't start Winsock!"); return; } +#endif + delete ToServer; ToServer = new sockaddr_in; ToServer->sin_family = AF_INET; ToServer->sin_port = htons(Port); inet_pton(AF_INET, IP.c_str(), &ToServer->sin_addr); UDPSock = socket(AF_INET, SOCK_DGRAM, 0); - GameSend("P"+std::to_string(ClientID)); - TCPSend("H",TCPSock); + GameSend("P" + std::to_string(ClientID)); + TCPSend("H", TCPSock); UDPSend("p"); - while(!Terminate)UDPRcv(); + while (!Terminate) + UDPRcv(); KillSocket(UDPSock); WSACleanup(); } \ No newline at end of file diff --git a/src/Network/VehicleEvent.cpp b/src/Network/VehicleEvent.cpp index 989e923..d60f669 100644 --- a/src/Network/VehicleEvent.cpp +++ b/src/Network/VehicleEvent.cpp @@ -6,26 +6,35 @@ /// Created by Anonymous275 on 5/8/2020 /// -#include -#include #include "Logger.h" -#include -#include #include +#include +#include +#include -#include "Network/network.h" +#if defined(_WIN32) +#include +#elif defined(__linux__) +#include +#include +#include +#include +#include +#include +#endif +#include "Network/network.hpp" int LastPort; std::string LastIP; SOCKET TCPSock = -1; -bool CheckBytes(int32_t Bytes){ - if (Bytes == 0){ +bool CheckBytes(int32_t Bytes) { + if (Bytes == 0) { debug("(TCP) Connection closing... CheckBytes(16)"); Terminate = true; return false; - }else if (Bytes < 0) { + } else if (Bytes < 0) { debug("(TCP CB) recv failed with error: " + std::to_string(WSAGetLastError())); KillSocket(TCPSock); Terminate = true; @@ -33,105 +42,109 @@ bool CheckBytes(int32_t Bytes){ } return true; } -void UUl(const std::string& R){ +void UUl(const std::string& R) { UlStatus = "UlDisconnected: " + R; } -void TCPSend(const std::string&Data,uint64_t Sock){ - if(Sock == -1){ - Terminate = true; - UUl("Invalid Socket"); - return; - } +void TCPSend(const std::string& Data, uint64_t Sock) { + if (Sock == -1) { + Terminate = true; + UUl("Invalid Socket"); + return; + } - int32_t Size,Sent,Temp; - std::string Send(4,0); - Size = int32_t(Data.size()); - memcpy(&Send[0],&Size,sizeof(Size)); - Send += Data; - // Do not use Size before this point for anything but the header - Sent = 0; - Size += 4; - do{ - if (size_t(Sent) >= Send.size()) { - error("string OOB in " + std::string(__func__)); - UUl("TCP Send OOB"); - return; - } - Temp = send(Sock, &Send[Sent], Size - Sent, 0); - if(!CheckBytes(Temp)){ - UUl("Socket Closed Code 2"); - return; - } - Sent += Temp; - }while(Sent < Size); + int32_t Size, Sent, Temp; + std::string Send(4, 0); + Size = int32_t(Data.size()); + memcpy(&Send[0], &Size, sizeof(Size)); + Send += Data; + // Do not use Size before this point for anything but the header + Sent = 0; + Size += 4; + do { + if (size_t(Sent) >= Send.size()) { + error("string OOB in " + std::string(__func__)); + UUl("TCP Send OOB"); + return; + } + Temp = send(Sock, &Send[Sent], Size - Sent, 0); + if (!CheckBytes(Temp)) { + UUl("Socket Closed Code 2"); + return; + } + Sent += Temp; + } while (Sent < Size); } -std::string TCPRcv(SOCKET Sock){ - if(Sock == -1){ +std::string TCPRcv(SOCKET Sock) { + if (Sock == -1) { Terminate = true; UUl("Invalid Socket"); return ""; } - int32_t Header,BytesRcv = 0,Temp; + int32_t Header, BytesRcv = 0, Temp; std::vector Data(sizeof(Header)); - do{ - Temp = recv(Sock,&Data[BytesRcv],4-BytesRcv,0); - if(!CheckBytes(Temp)){ + do { + Temp = recv(Sock, &Data[BytesRcv], 4 - BytesRcv, 0); + if (!CheckBytes(Temp)) { UUl("Socket Closed Code 3"); return ""; } BytesRcv += Temp; - }while(BytesRcv < 4); - memcpy(&Header,&Data[0],sizeof(Header)); + } while (BytesRcv < 4); + memcpy(&Header, &Data[0], sizeof(Header)); - if(!CheckBytes(BytesRcv)){ + if (!CheckBytes(BytesRcv)) { UUl("Socket Closed Code 4"); return ""; } Data.resize(Header); BytesRcv = 0; - do{ - Temp = recv(Sock,&Data[BytesRcv],Header-BytesRcv,0); - if(!CheckBytes(Temp)){ + do { + Temp = recv(Sock, &Data[BytesRcv], Header - BytesRcv, 0); + if (!CheckBytes(Temp)) { UUl("Socket Closed Code 5"); return ""; } BytesRcv += Temp; - }while(BytesRcv < Header); + } while (BytesRcv < Header); - std::string Ret(Data.data(),Header); + std::string Ret(Data.data(), Header); if (Ret.substr(0, 4) == "ABG:") { Ret = DeComp(Ret.substr(4)); } #ifdef DEBUG - //debug("Parsing from server -> " + std::to_string(Ret.size())); + // debug("Parsing from server -> " + std::to_string(Ret.size())); #endif - if(Ret[0] == 'E' || Ret[0] == 'K')UUl(Ret.substr(1)); + if (Ret[0] == 'E' || Ret[0] == 'K') + UUl(Ret.substr(1)); return Ret; } -void TCPClientMain(const std::string& IP,int Port){ +void TCPClientMain(const std::string& IP, int Port) { LastIP = IP; LastPort = Port; - WSADATA wsaData; SOCKADDR_IN ServerAddr; int RetCode; - WSAStartup(514, &wsaData); //2.2 +#ifdef _WIN32 + WSADATA wsaData; + WSAStartup(514, &wsaData); // 2.2 +#endif TCPSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if(TCPSock == -1){ + if (TCPSock == -1) { printf("Client: socket failed! Error code: %d\n", WSAGetLastError()); WSACleanup(); return; } + ServerAddr.sin_family = AF_INET; ServerAddr.sin_port = htons(Port); inet_pton(AF_INET, IP.c_str(), &ServerAddr.sin_addr); - RetCode = connect(TCPSock, (SOCKADDR *) &ServerAddr, sizeof(ServerAddr)); - if(RetCode != 0){ + RetCode = connect(TCPSock, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr)); + if (RetCode != 0) { UlStatus = "UlConnection Failed!"; error("Client: connect failed! Error code: " + std::to_string(WSAGetLastError())); KillSocket(TCPSock); @@ -144,14 +157,16 @@ void TCPClientMain(const std::string& IP,int Port){ char Code = 'C'; send(TCPSock, &Code, 1, 0); SyncResources(TCPSock); - while(!Terminate){ + while (!Terminate) { ServerParser(TCPRcv(TCPSock)); } GameSend("T"); ////Game Send Terminate - if(KillSocket(TCPSock) != 0) + if (KillSocket(TCPSock) != 0) debug("(TCP) Cannot close socket. Error code: " + std::to_string(WSAGetLastError())); - if(WSACleanup() != 0) +#ifdef _WIN32 + if (WSACleanup() != 0) debug("(TCP) Client: WSACleanup() failed!..."); +#endif } diff --git a/src/Security/BeamNG.cpp b/src/Security/BeamNG.cpp index 2595934..0ec69d9 100644 --- a/src/Security/BeamNG.cpp +++ b/src/Security/BeamNG.cpp @@ -7,7 +7,14 @@ /// #include +#if defined(_WIN32) #include +#elif defined(__linux__) +#include "vdf_parser.hpp" +#include +#include +#include +#endif #include "Logger.h" #include #include @@ -20,11 +27,10 @@ int TraceBack = 0; std::string GameDir; -void lowExit(int code){ +void lowExit(int code) { TraceBack = 0; - std::string msg = - "Failed to find the game please launch it. Report this if the issue persists code "; - error(msg+std::to_string(code)); + std::string msg = "Failed to find the game please launch it. Report this if the issue persists code "; + error(msg + std::to_string(code)); std::this_thread::sleep_for(std::chrono::seconds(10)); exit(2); } @@ -44,117 +50,136 @@ void SteamExit(int code){ std::this_thread::sleep_for(std::chrono::seconds(10)); exit(4); }*/ -std::string GetGameDir(){ - //if(TraceBack != 4)Exit(0); - return GameDir.substr(0,GameDir.find_last_of('\\')); +std::string GetGameDir() { +// if(TraceBack != 4)Exit(0); +#if defined(_WIN32) + return GameDir.substr(0, GameDir.find_last_of('\\')); +#elif defined(__linux__) + return GameDir.substr(0, GameDir.find_last_of('/')); +#endif } -LONG OpenKey(HKEY root,const char* path,PHKEY hKey){ +#ifdef _WIN32 +LONG OpenKey(HKEY root, const char* path, PHKEY hKey) { return RegOpenKeyEx(root, reinterpret_cast(path), 0, KEY_READ, hKey); } -std::string QueryKey(HKEY hKey,int ID){ - TCHAR achKey[MAX_KEY_LENGTH]; // buffer for subkey name - DWORD cbName; // size of name string - TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name - DWORD cchClassName = MAX_PATH; // size of class string - DWORD cSubKeys=0; // number of subkeys - DWORD cbMaxSubKey; // longest subkey size - DWORD cchMaxClass; // longest class string - DWORD cValues; // number of values for key - DWORD cchMaxValue; // longest value name - DWORD cbMaxValueData; // longest value data - DWORD cbSecurityDescriptor; // size of security descriptor - FILETIME ftLastWriteTime; // last write time +std::string QueryKey(HKEY hKey, int ID) { + TCHAR achKey[MAX_KEY_LENGTH]; // buffer for subkey name + DWORD cbName; // size of name string + TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name + DWORD cchClassName = MAX_PATH; // size of class string + DWORD cSubKeys = 0; // number of subkeys + DWORD cbMaxSubKey; // longest subkey size + DWORD cchMaxClass; // longest class string + DWORD cValues; // number of values for key + DWORD cchMaxValue; // longest value name + DWORD cbMaxValueData; // longest value data + DWORD cbSecurityDescriptor; // size of security descriptor + FILETIME ftLastWriteTime; // last write time DWORD i, retCode; - TCHAR achValue[MAX_VALUE_NAME]; + TCHAR achValue[MAX_VALUE_NAME]; DWORD cchValue = MAX_VALUE_NAME; retCode = RegQueryInfoKey( - hKey, // key handle - achClass, // buffer for class name - &cchClassName, // size of class string - nullptr, // reserved - &cSubKeys, // number of subkeys - &cbMaxSubKey, // longest subkey size - &cchMaxClass, // longest class string - &cValues, // number of values for this key - &cchMaxValue, // longest value name - &cbMaxValueData, // longest value data - &cbSecurityDescriptor, // security descriptor - &ftLastWriteTime); // last write time + hKey, // key handle + achClass, // buffer for class name + &cchClassName, // size of class string + nullptr, // reserved + &cSubKeys, // number of subkeys + &cbMaxSubKey, // longest subkey size + &cchMaxClass, // longest class string + &cValues, // number of values for this key + &cchMaxValue, // longest value name + &cbMaxValueData, // longest value data + &cbSecurityDescriptor, // security descriptor + &ftLastWriteTime); // last write time BYTE* buffer = new BYTE[cbMaxValueData]; ZeroMemory(buffer, cbMaxValueData); - if (cSubKeys){ - for (i=0; i&a,const std::string& Path){ - for (const auto &entry : fs::directory_iterator(Path)) { +void FileList(std::vector& a, const std::string& Path) { + for (const auto& entry : fs::directory_iterator(Path)) { const auto& DPath = entry.path(); if (!entry.is_directory()) { a.emplace_back(DPath.string()); - }else if(NameValid(DPath.filename().string())){ + } else if (NameValid(DPath.filename().string())) { FileList(a, DPath.string()); } } } -bool Find(const std::string& FName,const std::string& Path){ +bool Find(const std::string& FName, const std::string& Path) { std::vector FS; - FileList(FS,Path+"\\userdata"); - for(std::string&a : FS){ - if(a.find(FName) != std::string::npos){ + FileList(FS, Path + "\\userdata"); + for (std::string& a : FS) { + if (a.find(FName) != std::string::npos) { FS.clear(); return true; } @@ -162,22 +187,24 @@ bool Find(const std::string& FName,const std::string& Path){ FS.clear(); return false; } -bool FindHack(const std::string& Path){ +bool FindHack(const std::string& Path) { bool s = true; - for (const auto &entry : fs::directory_iterator(Path)) { + for (const auto& entry : fs::directory_iterator(Path)) { std::string Name = entry.path().filename().string(); - for(char&c : Name)c = char(tolower(c)); - if(Name == "steam.exe")s = false; - if(Name.find("greenluma") != -1){ - error("Found malicious file/folder \"" + Name+"\""); + for (char& c : Name) + c = char(tolower(c)); + if (Name == "steam.exe") + s = false; + if (Name.find("greenluma") != -1) { + error("Found malicious file/folder \"" + Name + "\""); return true; } Name.clear(); } return s; } -std::vector GetID(const std::string& log){ - std::string vec,t,r; +std::vector GetID(const std::string& log) { + std::string vec, t, r; std::vector Ret; std::ifstream f(log.c_str(), std::ios::binary); f.seekg(0, std::ios_base::end); @@ -189,10 +216,12 @@ std::vector GetID(const std::string& log){ std::stringstream ss(vec); bool S = false; while (std::getline(ss, t, '{')) { - if(!S)S = true; - else{ - for(char& c : t){ - if(isdigit(c))r += c; + if (!S) + S = true; + else { + for (char& c : t) { + if (isdigit(c)) + r += c; } break; } @@ -202,15 +231,16 @@ std::vector GetID(const std::string& log){ S = false; bool L = true; while (std::getline(ss, t, '}')) { - if(L){ + if (L) { L = false; continue; } - for(char& c : t){ - if(c == '"'){ - if(!S)S = true; - else{ - if(r.length() > 10) { + for (char& c : t) { + if (c == '"') { + if (!S) + S = true; + else { + if (r.length() > 10) { Ret.emplace_back(r); } r.clear(); @@ -218,13 +248,14 @@ std::vector GetID(const std::string& log){ continue; } } - if(isdigit(c))r += c; + if (isdigit(c)) + r += c; } } vec.clear(); return Ret; } -std::string GetManifest(const std::string& Man){ +std::string GetManifest(const std::string& Man) { std::string vec; std::ifstream f(Man.c_str(), std::ios::binary); f.seekg(0, std::ios_base::end); @@ -235,84 +266,109 @@ std::string GetManifest(const std::string& Man){ f.close(); std::string ToFind = "\"LastOwner\"\t\t\""; int pos = int(vec.find(ToFind)); - if(pos != -1){ + if (pos != -1) { pos += int(ToFind.length()); vec = vec.substr(pos); - return vec.substr(0,vec.find('\"')); - }else return ""; + return vec.substr(0, vec.find('\"')); + } else + return ""; } -bool IDCheck(std::string Man, std::string steam){ - bool a = false,b = true; +bool IDCheck(std::string Man, std::string steam) { + bool a = false, b = true; int pos = int(Man.rfind("steamapps")); - // if(pos == -1)Exit(5); - Man = Man.substr(0,pos+9) + "\\appmanifest_284160.acf"; + // if(pos == -1)Exit(5); + Man = Man.substr(0, pos + 9) + "\\appmanifest_284160.acf"; steam += "\\config\\loginusers.vdf"; - if(fs::exists(Man) && fs::exists(steam)){ - for(const std::string&ID : GetID(steam)){ - if(ID == GetManifest(Man))b = false; + if (fs::exists(Man) && fs::exists(steam)) { + for (const std::string& ID : GetID(steam)) { + if (ID == GetManifest(Man)) + b = false; } - //if(b)Exit(6); - }else a = true; + // if(b)Exit(6); + } else + a = true; return a; } -void LegitimacyCheck(){ +void LegitimacyCheck() { - //std::string K1 = R"(Software\Valve\Steam)"; - //std::string K2 = R"(Software\Valve\Steam\Apps\284160)"; +// std::string K1 = R"(Software\Valve\Steam)"; +// std::string K2 = R"(Software\Valve\Steam\Apps\284160)"; - /*LONG dwRegOPenKey = OpenKey(HKEY_CURRENT_USER, K1.c_str(), &hKey); +/*LONG dwRegOPenKey = OpenKey(HKEY_CURRENT_USER, K1.c_str(), &hKey); - if(dwRegOPenKey == ERROR_SUCCESS) { - Result = QueryKey(hKey, 1); - if(Result.empty())Exit(1); +if(dwRegOPenKey == ERROR_SUCCESS) { + Result = QueryKey(hKey, 1); + if(Result.empty())Exit(1); - if(fs::exists(Result)){ - if(!Find("284160.json",Result))Exit(2); - if(FindHack(Result))SteamExit(1); - }else Exit(3); + if(fs::exists(Result)){ + if(!Find("284160.json",Result))Exit(2); + if(FindHack(Result))SteamExit(1); + }else Exit(3); - T = Result; - Result.clear(); - TraceBack++; - }else Exit(4); + T = Result; + Result.clear(); + TraceBack++; +}else Exit(4); - K1.clear(); - RegCloseKey(hKey); - dwRegOPenKey = OpenKey(HKEY_CURRENT_USER, K2.c_str(), &hKey); - if(dwRegOPenKey == ERROR_SUCCESS) { - Result = QueryKey(hKey, 2); - if(Result.empty())lowExit(1); - TraceBack++; - }else lowExit(2); - K2.clear(); - RegCloseKey(hKey);*/ +K1.clear(); +RegCloseKey(hKey); +dwRegOPenKey = OpenKey(HKEY_CURRENT_USER, K2.c_str(), &hKey); +if(dwRegOPenKey == ERROR_SUCCESS) { + Result = QueryKey(hKey, 2); + if(Result.empty())lowExit(1); + TraceBack++; +}else lowExit(2); +K2.clear(); +RegCloseKey(hKey);*/ +#if defined(_WIN32) std::string Result; std::string K3 = R"(Software\BeamNG\BeamNG.drive)"; HKEY hKey; LONG dwRegOPenKey = OpenKey(HKEY_CURRENT_USER, K3.c_str(), &hKey); - if(dwRegOPenKey == ERROR_SUCCESS) { + if (dwRegOPenKey == ERROR_SUCCESS) { Result = QueryKey(hKey, 3); - if(Result.empty())lowExit(3); - //if(IDCheck(Result,T))lowExit(5); + if (Result.empty()) + lowExit(3); + // if(IDCheck(Result,T))lowExit(5); GameDir = Result; - //TraceBack++; - }else lowExit(4); + // TraceBack++; + } else + lowExit(4); K3.clear(); Result.clear(); RegCloseKey(hKey); - //if(TraceBack < 3)exit(-1); +// if(TraceBack < 3)exit(-1); +#elif defined(__linux__) + struct passwd* pw = getpwuid(getuid()); + std::string homeDir = pw->pw_dir; + // Right now only steam is supported + std::ifstream libraryFolders(homeDir + "/.steam/root/steamapps/libraryfolders.vdf"); + auto root = tyti::vdf::read(libraryFolders); + + for (auto folderInfo : root.childs) { + if (std::filesystem::exists(folderInfo.second->attribs["path"] + "/steamapps/common/BeamNG.drive/")) { + GameDir = folderInfo.second->attribs["path"] + "/steamapps/common/BeamNG.drive/"; + break; + } + } +#endif } -std::string CheckVer(const std::string &dir){ - std::string temp,Path = dir + "\\integrity.json"; +std::string CheckVer(const std::string& dir) { +#if defined(_WIN32) + std::string temp, Path = dir + "\\integrity.json"; +#elif defined(__linux__) + std::string temp, Path = dir + "/integrity.json"; +#endif std::ifstream f(Path.c_str(), std::ios::binary); int Size = int(std::filesystem::file_size(Path)); - std::string vec(Size,0); + std::string vec(Size, 0); f.read(&vec[0], Size); f.close(); - vec = vec.substr(vec.find_last_of("version"),vec.find_last_of('"')); - for(const char &a : vec){ - if(isdigit(a) || a == '.')temp+=a; + vec = vec.substr(vec.find_last_of("version"), vec.find_last_of('"')); + for (const char& a : vec) { + if (isdigit(a) || a == '.') + temp += a; } return temp; } diff --git a/src/Security/Login.cpp b/src/Security/Login.cpp index 1fec7f1..8fbf15a 100644 --- a/src/Security/Login.cpp +++ b/src/Security/Login.cpp @@ -1,141 +1,143 @@ -// Copyright (c) 2019-present Anonymous275. -// BeamMP Launcher code is not in the public domain and is not free software. -// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. -// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. -/// -/// Created by Anonymous275 on 11/26/2020 -/// - -#include -#include "Http.h" -#include -#include "Logger.h" -#include - - -namespace fs = std::filesystem; -std::string PublicKey; -std::string PrivateKey; -extern bool LoginAuth; -extern std::string Username; -extern std::string UserRole; - -void UpdateKey(const char* newKey){ - if(newKey && std::isalnum(newKey[0])){ - PrivateKey = newKey; - std::ofstream Key("key"); - if(Key.is_open()){ - Key << newKey; - Key.close(); - }else fatal("Cannot write to disk!"); - }else if(fs::exists("key")){ - remove("key"); - } -} - -/// "username":"value","password":"value" -/// "Guest":"Name" -/// "pk":"private_key" - -std::string GetFail(const std::string& R){ - std::string DRet = R"({"success":false,"message":)"; - DRet += "\""+R+"\"}"; - error(R); - return DRet; -} - -std::string Login(const std::string& fields){ - if(fields == "LO"){ - Username = ""; - UserRole = ""; - LoginAuth = false; - UpdateKey(nullptr); - return ""; - } - info("Attempting to authenticate..."); - try { - std::string Buffer = HTTP::Post("https://auth.beammp.com/userlogin", fields); - - if(Buffer == "-1"){ - return GetFail("Failed to communicate with the auth system!"); - } - - nlohmann::json d = nlohmann::json::parse(Buffer, nullptr, false); - - if (Buffer.at(0) != '{' || d.is_discarded()) { - error(Buffer); - return GetFail("Invalid answer from authentication servers, please try again later!"); - } - if(d.contains("success") && d["success"].get()){ - LoginAuth = true; - if (d.contains("username")) { - Username = d["username"].get(); - } - if (d.contains("role")) { - UserRole = d["role"].get(); - } - if(d.contains("private_key")) { - UpdateKey(d["private_key"].get().c_str()); - } - if(d.contains("public_key")){ - PublicKey = d["public_key"].get(); - } - info("Authentication successful!"); - }else info("Authentication failed!"); - if(d.contains("message")){ - d.erase("private_key"); - d.erase("public_key"); - return d.dump(); - } - return GetFail("Invalid message parsing!"); - } catch (const std::exception& e) { - return GetFail(e.what()); - } -} - -void CheckLocalKey(){ - if(fs::exists("key") && fs::file_size("key") < 100){ - std::ifstream Key("key"); - if(Key.is_open()) { - auto Size = fs::file_size("key"); - std::string Buffer(Size, 0); - Key.read(&Buffer[0], Size); - Key.close(); - - for (char& c : Buffer) { - if (!std::isalnum(c) && c != '-') { - UpdateKey(nullptr); - return; - } - } - - Buffer = HTTP::Post("https://auth.beammp.com/userlogin", R"({"pk":")" + Buffer + "\"}"); - - nlohmann::json d = nlohmann::json::parse(Buffer, nullptr, false); - - if (Buffer == "-1" || Buffer.at(0) != '{' || d.is_discarded()) { - error(Buffer); - info("Invalid answer from authentication servers."); - UpdateKey(nullptr); - } - if(d["success"].get()){ - LoginAuth = true; - UpdateKey(d["private_key"].get().c_str()); - PublicKey = d["public_key"].get(); - if (d.contains("username")) { - Username = d["username"].get(); - } - if (d.contains("role")) { - UserRole = d["role"].get(); - } - //info(Role); - }else{ - info("Auto-Authentication unsuccessful please re-login!"); - UpdateKey(nullptr); - } - }else{ - warn("Could not open saved key!"); - UpdateKey(nullptr); - } - }else UpdateKey(nullptr); -} +// Copyright (c) 2019-present Anonymous275. +// BeamMP Launcher code is not in the public domain and is not free software. +// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. +// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. +/// +/// Created by Anonymous275 on 11/26/2020 +/// + +#include "Http.h" +#include "Logger.h" +#include +#include +#include + +namespace fs = std::filesystem; +std::string PublicKey; +std::string PrivateKey; +extern bool LoginAuth; +extern std::string Username; +extern std::string UserRole; + +void UpdateKey(const char* newKey) { + if (newKey && std::isalnum(newKey[0])) { + PrivateKey = newKey; + std::ofstream Key("key"); + if (Key.is_open()) { + Key << newKey; + Key.close(); + } else + fatal("Cannot write to disk!"); + } else if (fs::exists("key")) { + remove("key"); + } +} + +/// "username":"value","password":"value" +/// "Guest":"Name" +/// "pk":"private_key" + +std::string GetFail(const std::string& R) { + std::string DRet = R"({"success":false,"message":)"; + DRet += "\"" + R + "\"}"; + error(R); + return DRet; +} + +std::string Login(const std::string& fields) { + if (fields == "LO") { + Username = ""; + UserRole = ""; + LoginAuth = false; + UpdateKey(nullptr); + return ""; + } + info("Attempting to authenticate..."); + try { + std::string Buffer = HTTP::Post("https://auth.beammp.com/userlogin", fields); + + if (Buffer == "-1") { + return GetFail("Failed to communicate with the auth system!"); + } + + nlohmann::json d = nlohmann::json::parse(Buffer, nullptr, false); + + if (Buffer.at(0) != '{' || d.is_discarded()) { + error(Buffer); + return GetFail("Invalid answer from authentication servers, please try again later!"); + } + if (d.contains("success") && d["success"].get()) { + LoginAuth = true; + if (d.contains("username")) { + Username = d["username"].get(); + } + if (d.contains("role")) { + UserRole = d["role"].get(); + } + if (d.contains("private_key")) { + UpdateKey(d["private_key"].get().c_str()); + } + if (d.contains("public_key")) { + PublicKey = d["public_key"].get(); + } + info("Authentication successful!"); + } else + info("Authentication failed!"); + if (d.contains("message")) { + d.erase("private_key"); + d.erase("public_key"); + return d.dump(); + } + return GetFail("Invalid message parsing!"); + } catch (const std::exception& e) { + return GetFail(e.what()); + } +} + +void CheckLocalKey() { + if (fs::exists("key") && fs::file_size("key") < 100) { + std::ifstream Key("key"); + if (Key.is_open()) { + auto Size = fs::file_size("key"); + std::string Buffer(Size, 0); + Key.read(&Buffer[0], Size); + Key.close(); + + for (char& c : Buffer) { + if (!std::isalnum(c) && c != '-') { + UpdateKey(nullptr); + return; + } + } + + Buffer = HTTP::Post("https://auth.beammp.com/userlogin", R"({"pk":")" + Buffer + "\"}"); + + nlohmann::json d = nlohmann::json::parse(Buffer, nullptr, false); + + if (Buffer == "-1" || Buffer.at(0) != '{' || d.is_discarded()) { + error(Buffer); + info("Invalid answer from authentication servers."); + UpdateKey(nullptr); + } + if (d["success"].get()) { + LoginAuth = true; + UpdateKey(d["private_key"].get().c_str()); + PublicKey = d["public_key"].get(); + if (d.contains("username")) { + Username = d["username"].get(); + } + if (d.contains("role")) { + UserRole = d["role"].get(); + } + // info(Role); + } else { + info("Auto-Authentication unsuccessful please re-login!"); + UpdateKey(nullptr); + } + } else { + warn("Could not open saved key!"); + UpdateKey(nullptr); + } + } else + UpdateKey(nullptr); +} diff --git a/src/Startup.cpp b/src/Startup.cpp index 1db2af3..3191c83 100644 --- a/src/Startup.cpp +++ b/src/Startup.cpp @@ -1,324 +1,406 @@ -// Copyright (c) 2019-present Anonymous275. -// BeamMP Launcher code is not in the public domain and is not free software. -// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. -// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. -/// -/// Created by Anonymous275 on 7/16/2020 -/// - -#include -#include -#include "zip_file.h" -#include -#include "Network/network.h" -#include "Security/Init.h" -#include -#include "Startup.h" -#include "hashpp.h" -#include "Logger.h" -#include -#include -#include "Http.h" - - -extern int TraceBack; -bool Dev = false; -int ProxyPort = 0; - -namespace fs = std::filesystem; - -VersionParser::VersionParser(const std::string& from_string) { - std::string token; - std::istringstream tokenStream(from_string); - while (std::getline(tokenStream, token, '.')) { - data.emplace_back(std::stol(token)); - split.emplace_back(token); - } -} - -std::strong_ordering VersionParser::operator<=>( - const VersionParser& rhs) const noexcept { - size_t const fields = std::min(data.size(), rhs.data.size()); - for (size_t i = 0; i != fields; ++i) { - if (data[i] == rhs.data[i]) continue; - else if (data[i] < rhs.data[i]) return std::strong_ordering::less; - else return std::strong_ordering::greater; - } - if (data.size() == rhs.data.size()) return std::strong_ordering::equal; - else if (data.size() > rhs.data.size()) return std::strong_ordering::greater; - else return std::strong_ordering::less; -} - -bool VersionParser::operator==(const VersionParser& rhs) const noexcept { - return std::is_eq(*this <=> rhs); -} - - -std::string GetEN(){ - return "BeamMP-Launcher.exe"; -} -std::string GetVer(){ - return "2.0"; -} -std::string GetPatch(){ - return ".85"; -} - -std::string GetEP(char*P){ - static std::string Ret = [&](){ - std::string path(P); - return path.substr(0, path.find_last_of("\\/") + 1); - } (); - return Ret; -} -void ReLaunch(int argc,char*args[]){ - std::string Arg; - for(int c = 2; c <= argc; c++){ - Arg += " "; - Arg += args[c-1]; - } - system("cls"); - ShellExecute(nullptr,"runas",(GetEP() + GetEN()).c_str(),Arg.c_str(),nullptr,SW_SHOWNORMAL); - ShowWindow(GetConsoleWindow(),0); - std::this_thread::sleep_for(std::chrono::seconds(1)); - exit(1); -} -void URelaunch(int argc,char* args[]){ - std::string Arg; - for(int c = 2; c <= argc; c++){ - Arg += " "; - Arg += args[c-1]; - } - ShellExecute(nullptr,"open",(GetEP() + GetEN()).c_str(),Arg.c_str(),nullptr,SW_SHOWNORMAL); - ShowWindow(GetConsoleWindow(),0); - std::this_thread::sleep_for(std::chrono::seconds(1)); - exit(1); -} -void CheckName(int argc,char* args[]){ - std::string DN = GetEN(),CDir = args[0],FN = CDir.substr(CDir.find_last_of('\\')+1); - if(FN != DN){ - if(fs::exists(DN))remove(DN.c_str()); - if(fs::exists(DN))ReLaunch(argc,args); - std::rename(FN.c_str(), DN.c_str()); - URelaunch(argc,args); - } -} - -void CheckForUpdates(int argc, char* args[], const std::string& CV) { - std::string LatestHash = HTTP::Get("https://backend.beammp.com/sha/launcher?branch=" + Branch + "&pk=" + PublicKey); - std::string LatestVersion = HTTP::Get( - "https://backend.beammp.com/version/launcher?branch=" + Branch + "&pk=" + PublicKey); - - transform(LatestHash.begin(), LatestHash.end(), LatestHash.begin(), ::tolower); - std::string EP(GetEP() + GetEN()), Back(GetEP() + "BeamMP-Launcher.back"); - - std::string FileHash = hashpp::get::getFileHash(hashpp::ALGORITHMS::SHA2_256, EP); - - if (FileHash != LatestHash && VersionParser(LatestVersion) > VersionParser(GetVer()+GetPatch())) { - info("Launcher update found!"); - fs::remove(Back); - fs::rename(EP, Back); - info("Downloading Launcher update " + LatestHash); - HTTP::Download( - "https://backend.beammp.com/builds/launcher?download=true" - "&pk=" + - PublicKey + "&branch=" + Branch, - EP); - URelaunch(argc, args); - } else info("Launcher version is up to date"); - TraceBack++; -} - -void CustomPort(int argc, char* argv[]){ - if(argc > 1){ - std::string Port = argv[1]; - if(Port.find_first_not_of("0123456789") == std::string::npos){ - if(std::stoi(Port) > 1000){ - DEFAULT_PORT = std::stoi(Port); - warn("Running on custom port : " + std::to_string(DEFAULT_PORT)); - } - } - if(argc > 2)Dev = true; - } -} - -void LinuxPatch(){ - HKEY hKey = nullptr; - LONG result = RegOpenKeyEx(HKEY_CURRENT_USER, R"(Software\Wine)", 0, KEY_READ, &hKey); - if (result != ERROR_SUCCESS || getenv("USER") == nullptr)return; - RegCloseKey(hKey); - info("Wine/Proton Detected! If you are on windows delete HKEY_CURRENT_USER\\Software\\Wine in regedit"); - info("Applying patches..."); - - result = RegCreateKey(HKEY_CURRENT_USER, R"(Software\Valve\Steam\Apps\284160)", &hKey); - - if (result != ERROR_SUCCESS){ - fatal(R"(failed to create HKEY_CURRENT_USER\Software\Valve\Steam\Apps\284160)"); - return; - } - - result = RegSetValueEx(hKey, "Name", 0, REG_SZ, (BYTE*)"BeamNG.drive", 12); - - if (result != ERROR_SUCCESS){ - fatal(R"(failed to create the value "Name" under HKEY_CURRENT_USER\Software\Valve\Steam\Apps\284160)"); - return; - } - RegCloseKey(hKey); - - info("Patched!"); -} - -void InitLauncher(int argc, char* argv[]) { - system("cls"); - SetConsoleTitleA(("BeamMP Launcher v" + std::string(GetVer()) + GetPatch()).c_str()); - InitLog(); - CheckName(argc, argv); - LinuxPatch(); - CheckLocalKey(); - ConfigInit(); - CustomPort(argc, argv); - CheckForUpdates(argc, argv, std::string(GetVer()) + GetPatch()); -} - -size_t DirCount(const std::filesystem::path& path){ - return (size_t)std::distance(std::filesystem::directory_iterator{path}, std::filesystem::directory_iterator{}); -} - -void CheckMP(const std::string& Path) { - if (!fs::exists(Path))return; - size_t c = DirCount(fs::path(Path)); - try { - for (auto& p : fs::directory_iterator(Path)){ - if(p.exists() && !p.is_directory()){ - std::string Name = p.path().filename().string(); - for(char&Ch : Name)Ch = char(tolower(Ch)); - if(Name != "beammp.zip")fs::remove(p.path()); - } - } - } catch (...) { - fatal("We were unable to clean the multiplayer mods folder! Is the game still running or do you have something open in that folder?"); - } - -} - -void EnableMP(){ - std::string File(GetGamePath() + "mods/db.json"); - if(!fs::exists(File))return; - auto Size = fs::file_size(File); - if(Size < 2)return; - std::ifstream db(File); - if(db.is_open()) { - std::string Data(Size, 0); - db.read(&Data[0], Size); - db.close(); - nlohmann::json d = nlohmann::json::parse(Data, nullptr, false); - if(Data.at(0) != '{' || d.is_discarded()) { - //error("Failed to parse " + File); //TODO illegal formatting - return; - } - if(d.contains("mods") && d["mods"].contains("multiplayerbeammp")){ - d["mods"]["multiplayerbeammp"]["active"] = true; - std::ofstream ofs(File); - if(ofs.is_open()){ - ofs << d.dump(); - ofs.close(); - }else{ - error("Failed to write " + File); - } - } - } -} - -void PreGame(const std::string& GamePath){ - std::string GameVer = CheckVer(GamePath); - info("Game Version : " + GameVer); - - CheckMP(GetGamePath() + "mods/multiplayer"); - - if(!Dev) { - std::string LatestHash = HTTP::Get("https://backend.beammp.com/sha/mod?branch=" + Branch + "&pk=" + PublicKey); - transform(LatestHash.begin(), LatestHash.end(), LatestHash.begin(), ::tolower); - LatestHash.erase(std::remove_if(LatestHash.begin(), LatestHash.end(), - [](auto const& c ) -> bool { return !std::isalnum(c); } ), LatestHash.end()); - - try { - if (!fs::exists(GetGamePath() + "mods/multiplayer")) { - fs::create_directories(GetGamePath() + "mods/multiplayer"); - } - EnableMP(); - }catch(std::exception&e){ - fatal(e.what()); - } - - std::string ZipPath(GetGamePath() + R"(mods\multiplayer\BeamMP.zip)"); - - std::string FileHash = hashpp::get::getFileHash(hashpp::ALGORITHMS::SHA2_256, ZipPath); - - if (FileHash != LatestHash) { - info("Downloading BeamMP Update " + LatestHash); - HTTP::Download("https://backend.beammp.com/builds/client?download=true" - "&pk=" + PublicKey + "&branch=" + Branch, ZipPath); - } - - std::string Target(GetGamePath() + "mods/unpacked/beammp"); - - if(fs::is_directory(Target)) { - fs::remove_all(Target); - } - } -} - -void set_headers(httplib::Response& res) { - res.set_header("Access-Control-Allow-Origin", "*"); - res.set_header("Access-Control-Request-Method", "POST, OPTIONS, GET"); - res.set_header("Access-Control-Request-Headers", "X-API-Version"); -} - -void StartProxy() { - std::thread proxy([&](){ - httplib::Server HTTPProxy; - httplib::Headers headers = { - {"User-Agent", "BeamMP-Launcher/" + GetVer() + GetPatch()}, - {"Accept", "*/*"} - }; - std::string pattern = "/:any1"; - for (int i = 2; i <= 4; i++) { - HTTPProxy.Get(pattern, [&](const httplib::Request &req, httplib::Response &res) { - httplib::Client cli("https://backend.beammp.com"); - set_headers(res); - if (req.has_header("X-BMP-Authentication")) { - headers.emplace("X-BMP-Authentication", PrivateKey); - } - if (req.has_header("X-API-Version")) { - headers.emplace("X-API-Version", req.get_header_value("X-API-Version")); - } - if (auto cli_res = cli.Get(req.path, headers); cli_res) { - res.set_content(cli_res->body, cli_res->get_header_value("Content-Type")); - } else { - res.set_content(to_string(cli_res.error()), "text/plain"); - } - }); - - HTTPProxy.Post(pattern, [&](const httplib::Request &req, httplib::Response &res) { - httplib::Client cli("https://backend.beammp.com"); - set_headers(res); - if (req.has_header("X-BMP-Authentication")) { - headers.emplace("X-BMP-Authentication", PrivateKey); - } - if (req.has_header("X-API-Version")) { - headers.emplace("X-API-Version", req.get_header_value("X-API-Version")); - } - if (auto cli_res = cli.Post(req.path, headers, req.body, - req.get_header_value("Content-Type")); cli_res) { - res.set_content(cli_res->body, cli_res->get_header_value("Content-Type")); - } else { - res.set_content(to_string(cli_res.error()), "text/plain"); - } - }); - pattern += "/:any" + std::to_string(i); - } - ProxyPort = HTTPProxy.bind_to_any_port("0.0.0.0"); - HTTPProxy.listen_after_bind(); - }); - proxy.detach(); -} +// Copyright (c) 2019-present Anonymous275. +// BeamMP Launcher code is not in the public domain and is not free software. +// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries. +// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository. +/// +/// Created by Anonymous275 on 7/16/2020 +/// + +#include "zip_file.h" +#include +#include +#include +#if defined(_WIN32) +#include +#elif defined(__linux__) +#include +#endif +#include "Http.h" +#include "Logger.h" +#include "Network/network.hpp" +#include "Security/Init.h" +#include "Startup.h" +#include "hashpp.h" +#include +#include +#include + +extern int TraceBack; +bool Dev = false; +int ProxyPort = 0; + +namespace fs = std::filesystem; + +VersionParser::VersionParser(const std::string& from_string) { + std::string token; + std::istringstream tokenStream(from_string); + while (std::getline(tokenStream, token, '.')) { + data.emplace_back(std::stol(token)); + split.emplace_back(token); + } +} + +std::strong_ordering VersionParser::operator<=>( + const VersionParser& rhs) const noexcept { + size_t const fields = std::min(data.size(), rhs.data.size()); + for (size_t i = 0; i != fields; ++i) { + if (data[i] == rhs.data[i]) + continue; + else if (data[i] < rhs.data[i]) + return std::strong_ordering::less; + else + return std::strong_ordering::greater; + } + if (data.size() == rhs.data.size()) + return std::strong_ordering::equal; + else if (data.size() > rhs.data.size()) + return std::strong_ordering::greater; + else + return std::strong_ordering::less; +} + +bool VersionParser::operator==(const VersionParser& rhs) const noexcept { + return std::is_eq(*this <=> rhs); +} + +std::string GetEN() { +#if defined(_WIN32) + return "BeamMP-Launcher.exe"; +#elif defined(__linux__) + return "BeamMP-Launcher"; +#endif +} + +std::string GetVer() { + return "2.0"; +} +std::string GetPatch() { + return ".85"; +} + +std::string GetEP(char* P) { + static std::string Ret = [&]() { + std::string path(P); + return path.substr(0, path.find_last_of("\\/") + 1); + }(); + return Ret; +} +#if defined(_WIN32) +void ReLaunch(int argc, char* args[]) { + std::string Arg; + for (int c = 2; c <= argc; c++) { + Arg += " "; + Arg += args[c - 1]; + } + system("cls"); + ShellExecute(nullptr, "runas", (GetEP() + GetEN()).c_str(), Arg.c_str(), nullptr, SW_SHOWNORMAL); + ShowWindow(GetConsoleWindow(), 0); + std::this_thread::sleep_for(std::chrono::seconds(1)); + exit(1); +} +void URelaunch(int argc, char* args[]) { + std::string Arg; + for (int c = 2; c <= argc; c++) { + Arg += " "; + Arg += args[c - 1]; + } + ShellExecute(nullptr, "open", (GetEP() + GetEN()).c_str(), Arg.c_str(), nullptr, SW_SHOWNORMAL); + ShowWindow(GetConsoleWindow(), 0); + std::this_thread::sleep_for(std::chrono::seconds(1)); + exit(1); +} +#elif defined(__linux__) +void ReLaunch(int argc, char* args[]) { + std::string Arg; + for (int c = 2; c <= argc; c++) { + Arg += " "; + Arg += args[c - 1]; + } + system("clear"); + execl((GetEP() + GetEN()).c_str(), Arg.c_str(), NULL); + std::this_thread::sleep_for(std::chrono::seconds(1)); + exit(1); +} +void URelaunch(int argc, char* args[]) { + std::string Arg; + for (int c = 2; c <= argc; c++) { + Arg += " "; + Arg += args[c - 1]; + } + execl((GetEP() + GetEN()).c_str(), Arg.c_str(), NULL); + std::this_thread::sleep_for(std::chrono::seconds(1)); + exit(1); +} +#endif + +void CheckName(int argc, char* args[]) { +#if defined(_WIN32) + std::string DN = GetEN(), CDir = args[0], FN = CDir.substr(CDir.find_last_of('\\') + 1); +#elif defined(__linux__) + std::string DN = GetEN(), CDir = args[0], FN = CDir.substr(CDir.find_last_of('/') + 1); +#endif + if (FN != DN) { + if (fs::exists(DN)) + remove(DN.c_str()); + if (fs::exists(DN)) + ReLaunch(argc, args); + std::rename(FN.c_str(), DN.c_str()); + URelaunch(argc, args); + } +} + +void CheckForUpdates(int argc, char* args[], const std::string& CV) { + std::string LatestHash = HTTP::Get("https://backend.beammp.com/sha/launcher?branch=" + Branch + "&pk=" + PublicKey); + std::string LatestVersion = HTTP::Get( + "https://backend.beammp.com/version/launcher?branch=" + Branch + "&pk=" + PublicKey); + + transform(LatestHash.begin(), LatestHash.end(), LatestHash.begin(), ::tolower); + std::string EP(GetEP() + GetEN()), Back(GetEP() + "BeamMP-Launcher.back"); + + std::string FileHash = hashpp::get::getFileHash(hashpp::ALGORITHMS::SHA2_256, EP); +#if defined(_WIN32) +#elif defined(__linux__) + system("clear"); +#endif + + if (FileHash != LatestHash && VersionParser(LatestVersion) > VersionParser(GetVer() + GetPatch())) { + info("Launcher update found!"); +#if defined(__linux__) + error("Auto update is NOT implemented for the Linux version. Please update manually ASAP as updates contain security patches."); +#else + fs::remove(Back); + fs::rename(EP, Back); + info("Downloading Launcher update " + LatestHash); + HTTP::Download( + "https://backend.beammp.com/builds/launcher?download=true" + "&pk=" + + PublicKey + "&branch=" + Branch, + EP); + URelaunch(argc, args); + #endif + } else + info("Launcher version is up to date"); + TraceBack++; +} + +void CustomPort(int argc, char* argv[]) { + if (argc > 1) { + std::string Port = argv[1]; + if (Port.find_first_not_of("0123456789") == std::string::npos) { + if (std::stoi(Port) > 1000) { + DEFAULT_PORT = std::stoi(Port); + warn("Running on custom port : " + std::to_string(DEFAULT_PORT)); + } + } + if (argc > 2) + Dev = true; + } +} + +#ifdef _WIN32 +void LinuxPatch() { + HKEY hKey = nullptr; + LONG result = RegOpenKeyEx(HKEY_CURRENT_USER, R"(Software\Wine)", 0, KEY_READ, &hKey); + if (result != ERROR_SUCCESS || getenv("USER") == nullptr) + return; + RegCloseKey(hKey); + info("Wine/Proton Detected! If you are on windows delete HKEY_CURRENT_USER\\Software\\Wine in regedit"); + info("Applying patches..."); + + result = RegCreateKey(HKEY_CURRENT_USER, R"(Software\Valve\Steam\Apps\284160)", &hKey); + + if (result != ERROR_SUCCESS) { + fatal(R"(failed to create HKEY_CURRENT_USER\Software\Valve\Steam\Apps\284160)"); + return; + } + + result = RegSetValueEx(hKey, "Name", 0, REG_SZ, (BYTE*)"BeamNG.drive", 12); + + if (result != ERROR_SUCCESS) { + fatal(R"(failed to create the value "Name" under HKEY_CURRENT_USER\Software\Valve\Steam\Apps\284160)"); + return; + } + RegCloseKey(hKey); + + info("Patched!"); +} +#endif + +#if defined(_WIN32) +void InitLauncher(int argc, char* argv[]) { + system("cls"); + SetConsoleTitleA(("BeamMP Launcher v" + std::string(GetVer()) + GetPatch()).c_str()); + InitLog(); + CheckName(argc, argv); + LinuxPatch(); + CheckLocalKey(); + ConfigInit(); + CustomPort(argc, argv); + CheckForUpdates(argc, argv, std::string(GetVer()) + GetPatch()); +} +#elif defined(__linux__) +void InitLauncher(int argc, char* argv[]) { + system("clear"); + InitLog(); + CheckName(argc, argv); + CheckLocalKey(); + ConfigInit(); + CustomPort(argc, argv); + CheckForUpdates(argc, argv, std::string(GetVer()) + GetPatch()); +} +#endif + +size_t DirCount(const std::filesystem::path& path) { + return (size_t)std::distance(std::filesystem::directory_iterator { path }, std::filesystem::directory_iterator {}); +} + +void CheckMP(const std::string& Path) { + if (!fs::exists(Path)) + return; + size_t c = DirCount(fs::path(Path)); + try { + for (auto& p : fs::directory_iterator(Path)) { + if (p.exists() && !p.is_directory()) { + std::string Name = p.path().filename().string(); + for (char& Ch : Name) + Ch = char(tolower(Ch)); + if (Name != "beammp.zip") + fs::remove(p.path()); + } + } + } catch (...) { + fatal("We were unable to clean the multiplayer mods folder! Is the game still running or do you have something open in that folder?"); + } +} + +void EnableMP() { + std::string File(GetGamePath() + "mods/db.json"); + if (!fs::exists(File)) + return; + auto Size = fs::file_size(File); + if (Size < 2) + return; + std::ifstream db(File); + if (db.is_open()) { + std::string Data(Size, 0); + db.read(&Data[0], Size); + db.close(); + nlohmann::json d = nlohmann::json::parse(Data, nullptr, false); + if (Data.at(0) != '{' || d.is_discarded()) { + // error("Failed to parse " + File); //TODO illegal formatting + return; + } + if (d.contains("mods") && d["mods"].contains("multiplayerbeammp")) { + d["mods"]["multiplayerbeammp"]["active"] = true; + std::ofstream ofs(File); + if (ofs.is_open()) { + ofs << d.dump(); + ofs.close(); + } else { + error("Failed to write " + File); + } + } + } +} + +void PreGame(const std::string& GamePath) { + std::string GameVer = CheckVer(GamePath); + info("Game Version : " + GameVer); + + CheckMP(GetGamePath() + "mods/multiplayer"); + + if (!Dev) { + std::string LatestHash = HTTP::Get("https://backend.beammp.com/sha/mod?branch=" + Branch + "&pk=" + PublicKey); + transform(LatestHash.begin(), LatestHash.end(), LatestHash.begin(), ::tolower); + LatestHash.erase(std::remove_if(LatestHash.begin(), LatestHash.end(), + [](auto const& c) -> bool { return !std::isalnum(c); }), + LatestHash.end()); + + try { + if (!fs::exists(GetGamePath() + "mods/multiplayer")) { + fs::create_directories(GetGamePath() + "mods/multiplayer"); + } + EnableMP(); + } catch (std::exception& e) { + fatal(e.what()); + } +#if defined(_WIN32) + std::string ZipPath(GetGamePath() + R"(mods\multiplayer\BeamMP.zip)"); +#elif defined(__linux__) + // Linux version of the game cant handle mods with uppercase names + std::string ZipPath(GetGamePath() + R"(mods/multiplayer/beammp.zip)"); +#endif + + std::string FileHash = hashpp::get::getFileHash(hashpp::ALGORITHMS::SHA2_256, ZipPath); + + if (FileHash != LatestHash) { + info("Downloading BeamMP Update " + LatestHash); + HTTP::Download("https://backend.beammp.com/builds/client?download=true" + "&pk=" + + PublicKey + "&branch=" + Branch, + ZipPath); + } + + std::string Target(GetGamePath() + "mods/unpacked/beammp"); + + if (fs::is_directory(Target)) { + fs::remove_all(Target); + } + } +} + +void set_headers(httplib::Response& res) { + res.set_header("Access-Control-Allow-Origin", "*"); + res.set_header("Access-Control-Request-Method", "POST, OPTIONS, GET"); + res.set_header("Access-Control-Request-Headers", "X-API-Version"); +} + +void StartProxy() { + std::thread proxy([&]() { + httplib::Server HTTPProxy; + httplib::Headers headers = { + { "User-Agent", "BeamMP-Launcher/" + GetVer() + GetPatch() }, + { "Accept", "*/*" } + }; + std::string pattern = "/:any1"; + for (int i = 2; i <= 4; i++) { + HTTPProxy.Get(pattern, [&](const httplib::Request& req, httplib::Response& res) { + httplib::Client cli("https://backend.beammp.com"); + set_headers(res); + if (req.has_header("X-BMP-Authentication")) { + headers.emplace("X-BMP-Authentication", PrivateKey); + } + if (req.has_header("X-API-Version")) { + headers.emplace("X-API-Version", req.get_header_value("X-API-Version")); + } + if (auto cli_res = cli.Get(req.path, headers); cli_res) { + res.set_content(cli_res->body, cli_res->get_header_value("Content-Type")); + } else { + res.set_content(to_string(cli_res.error()), "text/plain"); + } + }); + + HTTPProxy.Post(pattern, [&](const httplib::Request& req, httplib::Response& res) { + httplib::Client cli("https://backend.beammp.com"); + set_headers(res); + if (req.has_header("X-BMP-Authentication")) { + headers.emplace("X-BMP-Authentication", PrivateKey); + } + if (req.has_header("X-API-Version")) { + headers.emplace("X-API-Version", req.get_header_value("X-API-Version")); + } + if (auto cli_res = cli.Post(req.path, headers, req.body, + req.get_header_value("Content-Type")); + cli_res) { + res.set_content(cli_res->body, cli_res->get_header_value("Content-Type")); + } else { + res.set_content(to_string(cli_res.error()), "text/plain"); + } + }); + pattern += "/:any" + std::to_string(i); + } + ProxyPort = HTTPProxy.bind_to_any_port("0.0.0.0"); + HTTPProxy.listen_after_bind(); + }); + proxy.detach(); +} diff --git a/src/main.cpp b/src/main.cpp index e710327..0859282 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,16 +5,16 @@ /// /// Created by Anonymous275 on 7/16/2020 /// -#include "Network/network.h" +#include "Http.h" +#include "Logger.h" +#include "Network/network.hpp" #include "Security/Init.h" #include "Startup.h" #include -#include "Logger.h" #include -#include "Http.h" -[[noreturn]] void flush(){ - while(true){ +[[noreturn]] void flush() { + while (true) { std::cout.flush(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); } @@ -32,7 +32,7 @@ int main(int argc, char* argv[]) { try { LegitimacyCheck(); - } catch (std::exception &e) { + } catch (std::exception& e) { fatal("Main 1 : " + std::string(e.what())); } @@ -41,5 +41,5 @@ int main(int argc, char* argv[]) { InitGame(GetGameDir()); CoreNetwork(); - ///TODO: make sure to use argv[0] for everything that should be in the same dir (mod down ect...) + /// TODO: make sure to use argv[0] for everything that should be in the same dir (mod down ect...) }