From 32fc7def7c5936e9a4c8c4d9b36b9a7570ca8664 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Wed, 17 Aug 2022 01:42:30 -0400 Subject: [PATCH] fetch cookies and build number on startup --- src/abaddon.cpp | 41 ++++++++++++ src/abaddon.hpp | 2 + src/dialogs/confirm.cpp | 4 ++ src/dialogs/confirm.hpp | 1 + src/discord/discord.cpp | 10 ++- src/discord/discord.hpp | 5 ++ src/discord/httpclient.cpp | 30 ++------- src/discord/httpclient.hpp | 2 + src/http.cpp | 6 ++ src/startup.cpp | 126 +++++++++++++++++++++++++++++++++++++ src/startup.hpp | 25 ++++++++ 11 files changed, 226 insertions(+), 26 deletions(-) create mode 100644 src/startup.cpp create mode 100644 src/startup.hpp diff --git a/src/abaddon.cpp b/src/abaddon.cpp index e296aa4..343dff7 100644 --- a/src/abaddon.cpp +++ b/src/abaddon.cpp @@ -17,6 +17,7 @@ #include "windows/profilewindow.hpp" #include "windows/pinnedwindow.hpp" #include "windows/threadswindow.hpp" +#include "startup.hpp" #ifdef WITH_LIBHANDY #include @@ -253,6 +254,9 @@ int Abaddon::StartGTK() { m_main_window->UpdateMenus(); m_main_window->show(); + + RunFirstTimeDiscordStartup(); + return m_gtk_app->run(*m_main_window); } @@ -435,6 +439,43 @@ void Abaddon::ShowUserMenu(const GdkEvent *event, Snowflake id, Snowflake guild_ m_user_menu->popup_at_pointer(event); } +void Abaddon::RunFirstTimeDiscordStartup() { + DiscordStartupDialog dlg(*m_main_window); + dlg.set_position(Gtk::WIN_POS_CENTER); + + std::optional cookie; + std::optional build_number; + + dlg.signal_response().connect([&](int response) { + if (response == Gtk::RESPONSE_OK) { + cookie = dlg.GetCookie(); + build_number = dlg.GetBuildNumber(); + } + }); + + dlg.run(); + + Glib::signal_idle().connect_once([this, cookie, build_number]() { + if (cookie.has_value()) { + m_discord.SetCookie(*cookie); + } else { + ConfirmDialog confirm(*m_main_window); + confirm.SetConfirmText("Cookies could not be fetched. This may increase your chances of being flagged by Discord's anti-spam"); + confirm.SetAcceptOnly(true); + confirm.run(); + } + + if (build_number.has_value()) { + m_discord.SetBuildNumber(*build_number); + } else { + ConfirmDialog confirm(*m_main_window); + confirm.SetConfirmText("Build number could not be fetched. This may increase your chances of being flagged by Discord's anti-spam"); + confirm.SetAcceptOnly(true); + confirm.run(); + } + }); +} + void Abaddon::ShowGuildVerificationGateDialog(Snowflake guild_id) { VerificationGateDialog dlg(*m_main_window, guild_id); if (dlg.run() == Gtk::RESPONSE_OK) { diff --git a/src/abaddon.hpp b/src/abaddon.hpp index 3cd04e9..ab80c46 100644 --- a/src/abaddon.hpp +++ b/src/abaddon.hpp @@ -94,6 +94,8 @@ public: static std::string GetStateCachePath(const std::string &path); protected: + void RunFirstTimeDiscordStartup(); + void ShowGuildVerificationGateDialog(Snowflake guild_id); void CheckMessagesForMembers(const ChannelData &chan, const std::vector &msgs); diff --git a/src/dialogs/confirm.cpp b/src/dialogs/confirm.cpp index 39d8971..8e606df 100644 --- a/src/dialogs/confirm.cpp +++ b/src/dialogs/confirm.cpp @@ -34,3 +34,7 @@ ConfirmDialog::ConfirmDialog(Gtk::Window &parent) void ConfirmDialog::SetConfirmText(const Glib::ustring &text) { m_label.set_text(text); } + +void ConfirmDialog::SetAcceptOnly(bool accept_only) { + m_cancel.set_visible(!accept_only); +} diff --git a/src/dialogs/confirm.hpp b/src/dialogs/confirm.hpp index df1e185..84a089c 100644 --- a/src/dialogs/confirm.hpp +++ b/src/dialogs/confirm.hpp @@ -5,6 +5,7 @@ class ConfirmDialog : public Gtk::Dialog { public: ConfirmDialog(Gtk::Window &parent); void SetConfirmText(const Glib::ustring &text); + void SetAcceptOnly(bool accept_only); protected: Gtk::Label m_label; diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp index 48f08d6..561b25b 100644 --- a/src/discord/discord.cpp +++ b/src/discord/discord.cpp @@ -1188,6 +1188,14 @@ void DiscordClient::SetReferringChannel(Snowflake id) { } } +void DiscordClient::SetBuildNumber(uint32_t build_number) { + m_build_number = build_number; +} + +void DiscordClient::SetCookie(std::string_view cookie) { + m_http.SetCookie(cookie); +} + void DiscordClient::UpdateToken(const std::string &token) { if (!IsStarted()) { m_token = token; @@ -2314,7 +2322,7 @@ void DiscordClient::SendIdentify() { msg.Properties.ReferrerCurrent = ""; msg.Properties.ReferringDomainCurrent = ""; msg.Properties.ReleaseChannel = "stable"; - msg.Properties.ClientBuildNumber = 141021; + msg.Properties.ClientBuildNumber = m_build_number; msg.Properties.ClientEventSource = ""; msg.Presence.Status = "online"; msg.Presence.Since = 0; diff --git a/src/discord/discord.hpp b/src/discord/discord.hpp index b1b623b..70c2d82 100644 --- a/src/discord/discord.hpp +++ b/src/discord/discord.hpp @@ -206,6 +206,9 @@ public: void SetReferringChannel(Snowflake id); + void SetBuildNumber(uint32_t build_number); + void SetCookie(std::string_view cookie); + void UpdateToken(const std::string &token); void SetUserAgent(const std::string &agent); @@ -303,6 +306,8 @@ private: std::string m_token; + uint32_t m_build_number = 142000; + void AddUserToGuild(Snowflake user_id, Snowflake guild_id); std::map> m_guild_to_users; std::map> m_guild_to_channels; diff --git a/src/discord/httpclient.cpp b/src/discord/httpclient.cpp index d13246d..cc0bb93 100644 --- a/src/discord/httpclient.cpp +++ b/src/discord/httpclient.cpp @@ -2,7 +2,6 @@ #include -//#define USE_LOCAL_PROXY HTTPClient::HTTPClient() { m_dispatcher.connect(sigc::mem_fun(*this, &HTTPClient::RunCallbacks)); } @@ -23,6 +22,10 @@ void HTTPClient::SetPersistentHeader(std::string name, std::string value) { m_headers.insert_or_assign(std::move(name), std::move(value)); } +void HTTPClient::SetCookie(std::string_view cookie) { + m_cookie = cookie; +} + void HTTPClient::MakeDELETE(const std::string &path, const std::function &cb) { printf("DELETE %s\n", path.c_str()); m_futures.push_back(std::async(std::launch::async, [this, path, cb] { @@ -31,10 +34,6 @@ void HTTPClient::MakeDELETE(const std::string &path, const std::function &cb); void MakeGET(const std::string &path, const std::function &cb); @@ -44,4 +45,5 @@ private: std::string m_authorization; std::string m_agent; std::unordered_map m_headers; + std::string m_cookie; }; diff --git a/src/http.cpp b/src/http.cpp index 0fae39f..f0471a1 100644 --- a/src/http.cpp +++ b/src/http.cpp @@ -2,6 +2,8 @@ #include +// #define USE_LOCAL_PROXY + namespace http { request::request(EMethod method, std::string url) : m_url(std::move(url)) { @@ -147,6 +149,10 @@ response request::execute() { detail::check_init(); std::string str; +#ifdef USE_LOCAL_PROXY + set_proxy("http://127.0.0.1:8888"); + set_verify_ssl(false); +#endif curl_easy_setopt(m_curl, CURLOPT_NOSIGNAL, 1L); curl_easy_setopt(m_curl, CURLOPT_CUSTOMREQUEST, m_method); curl_easy_setopt(m_curl, CURLOPT_URL, m_url.c_str()); diff --git a/src/startup.cpp b/src/startup.cpp new file mode 100644 index 0000000..baedf76 --- /dev/null +++ b/src/startup.cpp @@ -0,0 +1,126 @@ +#include "startup.hpp" +#include "abaddon.hpp" +#include +#include + +DiscordStartupDialog::DiscordStartupDialog(Gtk::Window &window) + : Gtk::MessageDialog(window, "", false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_NONE, true) { + m_dispatcher.connect(sigc::mem_fun(*this, &DiscordStartupDialog::DispatchCallback)); + + property_text() = "Getting connection info..."; + + RunAsync(); +} + +std::optional DiscordStartupDialog::GetCookie() const { + return m_cookie; +} + +std::optional DiscordStartupDialog::GetBuildNumber() const { + return m_build_number; +} + +// good enough +std::optional> ParseCookie(const Glib::ustring &str) { + auto regex = Glib::Regex::create("\\t"); + const std::vector split = regex->split(str); + if (split.size() < 7) return {}; + + return { { split[5], split[6] } }; +} + +std::optional GetJavascriptFileFromAppPage(const Glib::ustring &contents) { + auto regex = Glib::Regex::create(R"(app-mount.*(/assets/[\w\d]*.js).*/assets/[\w\d]*.js)"); + Glib::MatchInfo match; + if (regex->match(contents, match)) { + return match.fetch(1); + } + + return {}; +} + +std::optional GetBuildNumberFromJSURL(const Glib::ustring &url, const std::string &cookie) { + http::request req(http::REQUEST_GET, "https://discord.com" + url); + req.set_header("Accept-Language", "en-US,en;q=0.9"); + req.set_header("Sec-Fetch-Dest", "document"); + req.set_header("Sec-Fetch-Mode", "navigate"); + req.set_header("Sec-Fetch-Site", "none"); + req.set_header("Sec-Fetch-User", "?1"); + req.set_user_agent(Abaddon::Get().GetSettings().UserAgent); + + curl_easy_setopt(req.get_curl(), CURLOPT_COOKIE, cookie.c_str()); + + auto res = req.execute(); + if (res.error) return {}; + + auto regex = Glib::Regex::create(R"("buildNumber",null!==\(t="(\d+)\"\))"); + Glib::MatchInfo match; + if (regex->match(res.text, match)) { + const auto str_value = match.fetch(1); + try { + return std::stoul(str_value); + } catch (...) { return {}; } + } + + return {}; +} + +std::pair, std::string> GetCookieTask() { + http::request req(http::REQUEST_GET, "https://discord.com/app"); + req.set_header("Accept-Language", "en-US,en;q=0.9"); + req.set_header("Sec-Fetch-Dest", "document"); + req.set_header("Sec-Fetch-Mode", "navigate"); + req.set_header("Sec-Fetch-Site", "none"); + req.set_header("Sec-Fetch-User", "?1"); + req.set_user_agent(Abaddon::Get().GetSettings().UserAgent); + + curl_easy_setopt(req.get_curl(), CURLOPT_COOKIEFILE, ""); + + auto res = req.execute(); + if (res.error) return {}; + + curl_slist *slist; + if (curl_easy_getinfo(req.get_curl(), CURLINFO_COOKIELIST, &slist) != CURLE_OK) { + return {}; + } + + std::string dcfduid; + std::string sdcfduid; + + for (auto *cur = slist; cur != nullptr; cur = cur->next) { + const auto cookie = ParseCookie(cur->data); + if (cookie.has_value()) { + if (cookie->first == "__dcfduid") { + dcfduid = cookie->second; + } else if (cookie->first == "__sdcfduid") { + sdcfduid = cookie->second; + } + } + } + curl_slist_free_all(slist); + + if (!dcfduid.empty() && !sdcfduid.empty()) { + return { "__dcfduid=" + dcfduid + "; __sdcfduid=" + sdcfduid, res.text }; + } + + return {}; +} + +void DiscordStartupDialog::RunAsync() { + auto futptr = std::make_shared>(); + *futptr = std::async(std::launch::async, [this, futptr] { + auto [opt_cookie, app_page] = GetCookieTask(); + m_cookie = opt_cookie; + if (opt_cookie.has_value()) { + auto js_url = GetJavascriptFileFromAppPage(app_page); + if (js_url.has_value()) { + m_build_number = GetBuildNumberFromJSURL(*js_url, *opt_cookie); + } + } + m_dispatcher.emit(); + }); +} + +void DiscordStartupDialog::DispatchCallback() { + response(Gtk::RESPONSE_OK); +} diff --git a/src/startup.hpp b/src/startup.hpp new file mode 100644 index 0000000..ba1459d --- /dev/null +++ b/src/startup.hpp @@ -0,0 +1,25 @@ +#pragma once +#include +#include +#include +#include + +// fetch cookies, build number async + +class DiscordStartupDialog : public Gtk::MessageDialog { +public: + DiscordStartupDialog(Gtk::Window &window); + + [[nodiscard]] std::optional GetCookie() const; + [[nodiscard]] std::optional GetBuildNumber() const; + +private: + void RunAsync(); + + void DispatchCallback(); + + Glib::Dispatcher m_dispatcher; + + std::optional m_cookie; + std::optional m_build_number; +};