From 3b206e11216b383fae0e860e0092f71301fd4425 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Fri, 30 Jun 2023 20:43:08 -0400 Subject: [PATCH 01/13] remote auth impl. up to QR code display --- .gitmodules | 3 + CMakeLists.txt | 18 +- src/abaddon.cpp | 13 ++ src/abaddon.hpp | 1 + src/discord/websocket.cpp | 5 +- src/remoteauth/remoteauthclient.cpp | 321 ++++++++++++++++++++++++++++ src/remoteauth/remoteauthclient.hpp | 64 ++++++ src/remoteauth/remoteauthdialog.cpp | 79 +++++++ src/remoteauth/remoteauthdialog.hpp | 23 ++ src/remoteauth/ssl.hpp | 31 +++ src/windows/mainwindow.cpp | 11 + src/windows/mainwindow.hpp | 4 + subprojects/qrcodegen | 1 + 13 files changed, 567 insertions(+), 7 deletions(-) create mode 100644 src/remoteauth/remoteauthclient.cpp create mode 100644 src/remoteauth/remoteauthclient.hpp create mode 100644 src/remoteauth/remoteauthdialog.cpp create mode 100644 src/remoteauth/remoteauthdialog.hpp create mode 100644 src/remoteauth/ssl.hpp create mode 160000 subprojects/qrcodegen diff --git a/.gitmodules b/.gitmodules index 27efb8e..aae3862 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "subprojects/miniaudio"] path = subprojects/miniaudio url = https://github.com/mackron/miniaudio +[submodule "subprojects/qrcodegen"] + path = subprojects/qrcodegen + url = https://github.com/nayuki/QR-Code-generator diff --git a/CMakeLists.txt b/CMakeLists.txt index de97c32..b85e6c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,6 +61,12 @@ target_include_directories(abaddon PUBLIC ${ZLIB_INCLUDE_DIRS}) target_include_directories(abaddon PUBLIC ${SQLite3_INCLUDE_DIRS}) target_include_directories(abaddon PUBLIC ${NLOHMANN_JSON_INCLUDE_DIRS}) +add_library(qrcodegen subprojects/qrcodegen/cpp/qrcodegen.hpp subprojects/qrcodegen/cpp/qrcodegen.cpp) +target_include_directories(qrcodegen PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/subprojects/qrcodegen/cpp") +target_link_libraries(abaddon qrcodegen) + +target_include_directories(abaddon PUBLIC "subprojects/qrcodegen/cpp") + target_precompile_headers(abaddon PRIVATE src/abaddon.hpp src/util.hpp) if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR @@ -151,12 +157,12 @@ if (ENABLE_VOICE) target_link_libraries(abaddon ${CMAKE_DL_LIBS}) - if(APPLE) - target_link_libraries(abaddon "-framework CoreFoundation") - target_link_libraries(abaddon "-framework CoreAudio") - target_link_libraries(abaddon "-framework AudioToolbox") - endif() - + if (APPLE) + target_link_libraries(abaddon "-framework CoreFoundation") + target_link_libraries(abaddon "-framework CoreAudio") + target_link_libraries(abaddon "-framework AudioToolbox") + endif () + endif () if (${ENABLE_NOTIFICATION_SOUNDS}) diff --git a/src/abaddon.cpp b/src/abaddon.cpp index 92cc494..545a3c2 100644 --- a/src/abaddon.cpp +++ b/src/abaddon.cpp @@ -21,6 +21,7 @@ #include "windows/voicewindow.hpp" #include "startup.hpp" #include "notifications/notifications.hpp" +#include "remoteauth/remoteauthdialog.hpp" #ifdef WITH_LIBHANDY #include @@ -267,6 +268,7 @@ int Abaddon::StartGTK() { m_main_window->signal_action_connect().connect(sigc::mem_fun(*this, &Abaddon::ActionConnect)); m_main_window->signal_action_disconnect().connect(sigc::mem_fun(*this, &Abaddon::ActionDisconnect)); m_main_window->signal_action_set_token().connect(sigc::mem_fun(*this, &Abaddon::ActionSetToken)); + m_main_window->signal_action_login_qr().connect(sigc::mem_fun(*this, &Abaddon::ActionLoginQR)); m_main_window->signal_action_reload_css().connect(sigc::mem_fun(*this, &Abaddon::ActionReloadCSS)); m_main_window->signal_action_set_status().connect(sigc::mem_fun(*this, &Abaddon::ActionSetStatus)); m_main_window->signal_action_add_recipient().connect(sigc::mem_fun(*this, &Abaddon::ActionAddRecipient)); @@ -834,6 +836,16 @@ void Abaddon::ActionSetToken() { m_main_window->UpdateMenus(); } +void Abaddon::ActionLoginQR() { + RemoteAuthDialog dlg(*m_main_window); + auto response = dlg.run(); + if (response == Gtk::RESPONSE_OK) { + m_discord_token = dlg.GetToken(); + m_main_window->UpdateComponents(); + } + m_main_window->UpdateMenus(); +} + void Abaddon::ActionChannelOpened(Snowflake id, bool expand_to) { if (!id.IsValid()) { m_discord.SetReferringChannel(Snowflake::Invalid); @@ -1142,6 +1154,7 @@ int main(int argc, char **argv) { auto log_audio = spdlog::stdout_color_mt("audio"); auto log_voice = spdlog::stdout_color_mt("voice"); auto log_discord = spdlog::stdout_color_mt("discord"); + auto log_ra = spdlog::stdout_color_mt("remote-auth"); Gtk::Main::init_gtkmm_internals(); // why??? return Abaddon::Get().StartGTK(); diff --git a/src/abaddon.hpp b/src/abaddon.hpp index 08070ae..85b2fa0 100644 --- a/src/abaddon.hpp +++ b/src/abaddon.hpp @@ -41,6 +41,7 @@ public: void ActionConnect(); void ActionDisconnect(); void ActionSetToken(); + void ActionLoginQR(); void ActionJoinGuildDialog(); void ActionChannelOpened(Snowflake id, bool expand_to = true); void ActionChatInputSubmit(ChatSubmitParams data); diff --git a/src/discord/websocket.cpp b/src/discord/websocket.cpp index f886e69..cdc4db1 100644 --- a/src/discord/websocket.cpp +++ b/src/discord/websocket.cpp @@ -26,7 +26,7 @@ void Websocket::StartConnection(const std::string &url) { m_websocket->disableAutomaticReconnection(); m_websocket->setUrl(url); m_websocket->setOnMessageCallback([this](auto &&msg) { OnMessage(std::forward(msg)); }); - m_websocket->setExtraHeaders(ix::WebSocketHttpHeaders { { "User-Agent", m_agent } }); // idk if this actually works + m_websocket->setExtraHeaders(ix::WebSocketHttpHeaders { { "User-Agent", m_agent }, { "Origin", "https://discord.com" } }); // idk if this actually works m_websocket->start(); } @@ -81,6 +81,9 @@ void Websocket::OnMessage(const ix::WebSocketMessagePtr &msg) { case ix::WebSocketMessageType::Message: { m_signal_message.emit(msg->str); } break; + case ix::WebSocketMessageType::Error: { + m_log->error("Websocket error: Status: {} Reason: {}", msg->errorInfo.http_status, msg->errorInfo.reason); + } break; default: break; } diff --git a/src/remoteauth/remoteauthclient.cpp b/src/remoteauth/remoteauthclient.cpp new file mode 100644 index 0000000..1ebb7cb --- /dev/null +++ b/src/remoteauth/remoteauthclient.cpp @@ -0,0 +1,321 @@ +#include "remoteauthclient.hpp" +#include +#include + +struct RemoteAuthGatewayMessage { + std::string Opcode; + + friend void from_json(const nlohmann::json &j, RemoteAuthGatewayMessage &m) { + j.at("op").get_to(m.Opcode); + } +}; + +struct RemoteAuthHelloMessage { + int TimeoutMS; + int HeartbeatInterval; + + friend void from_json(const nlohmann::json &j, RemoteAuthHelloMessage &m) { + j.at("timeout_ms").get_to(m.TimeoutMS); + j.at("heartbeat_interval").get_to(m.HeartbeatInterval); + } +}; + +struct RemoteAuthHeartbeatMessage { + friend void to_json(nlohmann::json &j, const RemoteAuthHeartbeatMessage &m) { + j["op"] = "heartbeat"; + } +}; + +struct RemoteAuthInitMessage { + std::string EncodedPublicKey; + + friend void to_json(nlohmann::json &j, const RemoteAuthInitMessage &m) { + j["op"] = "init"; + j["encoded_public_key"] = m.EncodedPublicKey; + } +}; + +struct RemoteAuthNonceProofMessage { + std::string EncryptedNonce; + std::string Nonce; + + friend void from_json(const nlohmann::json &j, RemoteAuthNonceProofMessage &m) { + j.at("encrypted_nonce").get_to(m.EncryptedNonce); + } + + friend void to_json(nlohmann::json &j, const RemoteAuthNonceProofMessage &m) { + j["op"] = "nonce_proof"; + j["nonce"] = m.Nonce; + } +}; + +struct RemoteAuthFingerprintMessage { + std::string Fingerprint; + + friend void from_json(const nlohmann::json &j, RemoteAuthFingerprintMessage &m) { + j.at("fingerprint").get_to(m.Fingerprint); + } +}; + +struct RemoteAuthPendingTicketMessage { + std::string EncryptedUserPayload; + + friend void from_json(const nlohmann::json &j, RemoteAuthPendingTicketMessage &m) { + j.at("encrypted_user_payload").get_to(m.EncryptedUserPayload); + } +}; + +RemoteAuthClient::RemoteAuthClient() + : m_ws("remote-auth-ws") + , m_log(spdlog::get("remote-auth")) { + m_ws.signal_open().connect(sigc::mem_fun(*this, &RemoteAuthClient::OnWebsocketOpen)); + m_ws.signal_close().connect(sigc::mem_fun(*this, &RemoteAuthClient::OnWebsocketClose)); + m_ws.signal_message().connect(sigc::mem_fun(*this, &RemoteAuthClient::OnWebsocketMessage)); + + m_dispatcher.connect(sigc::mem_fun(*this, &RemoteAuthClient::OnDispatch)); +} + +RemoteAuthClient::~RemoteAuthClient() { + Stop(); +} + +void RemoteAuthClient::Start() { + if (IsConnected()) { + Stop(); + } + + m_connected = true; + m_heartbeat_waiter.revive(); + m_ws.StartConnection("wss://remote-auth-gateway.discord.gg/?v=2"); +} + +void RemoteAuthClient::Stop() { + if (!IsConnected()) { + m_log->warn("Requested stop while not connected"); + return; + } + + m_connected = false; + m_ws.Stop(1000); + m_heartbeat_waiter.kill(); + if (m_heartbeat_thread.joinable()) m_heartbeat_thread.join(); +} + +bool RemoteAuthClient::IsConnected() const noexcept { + return m_connected; +} + +void RemoteAuthClient::OnGatewayMessage(const std::string &str) { + auto j = nlohmann::json::parse(str); + RemoteAuthGatewayMessage msg = j; + if (msg.Opcode == "hello") { + HandleGatewayHello(j); + } else if (msg.Opcode == "nonce_proof") { + HandleGatewayNonceProof(j); + } else if (msg.Opcode == "pending_remote_init") { + HandleGatewayPendingRemoteInit(j); + } else if (msg.Opcode == "pending_ticket") { + HandleGatewayPendingTicket(j); + } +} + +void RemoteAuthClient::HandleGatewayHello(const nlohmann::json &j) { + RemoteAuthHelloMessage msg = j; + m_log->debug("Timeout: {}, Heartbeat: {}", msg.TimeoutMS, msg.HeartbeatInterval); + + m_heartbeat_msec = msg.HeartbeatInterval; + m_heartbeat_thread = std::thread(&RemoteAuthClient::HeartbeatThread, this); + + Init(); +} + +void RemoteAuthClient::HandleGatewayNonceProof(const nlohmann::json &j) { + RemoteAuthNonceProofMessage msg = j; + m_log->debug("Received encrypted nonce"); + + const auto encrypted_nonce = Glib::Base64::decode(msg.EncryptedNonce); + const auto proof = Decrypt(reinterpret_cast(encrypted_nonce.data()), encrypted_nonce.size()); + auto proof_encoded = Glib::Base64::encode(std::string(proof.begin(), proof.end())); + + std::replace(proof_encoded.begin(), proof_encoded.end(), '/', '_'); + std::replace(proof_encoded.begin(), proof_encoded.end(), '+', '-'); + proof_encoded.erase(std::remove(proof_encoded.begin(), proof_encoded.end(), '='), proof_encoded.end()); + + RemoteAuthNonceProofMessage reply; + reply.Nonce = proof_encoded; + m_ws.Send(reply); +} + +void RemoteAuthClient::HandleGatewayPendingRemoteInit(const nlohmann::json &j) { + RemoteAuthFingerprintMessage msg = j; + m_log->debug("Received fingerprint"); + + m_signal_fingerprint.emit(msg.Fingerprint); +} + +void RemoteAuthClient::HandleGatewayPendingTicket(const nlohmann::json &j) { + RemoteAuthPendingTicketMessage msg = j; + + const auto encrypted_payload = Glib::Base64::decode(msg.EncryptedUserPayload); + const auto payload = Decrypt(reinterpret_cast(encrypted_payload.data()), encrypted_payload.size()); + + m_log->trace("User payload: {}", std::string(payload.begin(), payload.end())); +} + +void RemoteAuthClient::Init() { + RemoteAuthInitMessage msg; + + GenerateKey(); + msg.EncodedPublicKey = GetEncodedPublicKey(); + if (msg.EncodedPublicKey.empty()) { + m_log->error("Something went wrong"); + // todo disconnect + return; + } + + m_ws.Send(msg); +} + +void RemoteAuthClient::GenerateKey() { + // you javascript people have it so easy + // check out this documentation https://www.openssl.org/docs/man1.1.1/man3/PEM_write_bio_PUBKEY.html + + m_pkey_ctx.reset(EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr)); + if (!m_pkey_ctx) { + m_log->error("Failed to create RSA context"); + return; + } + + if (EVP_PKEY_keygen_init(m_pkey_ctx.get()) <= 0) { + m_log->error("Failed to initialize RSA context"); + return; + } + + if (EVP_PKEY_CTX_set_rsa_keygen_bits(m_pkey_ctx.get(), 2048) <= 0) { + m_log->error("Failed to set keygen bits"); + return; + } + + EVP_PKEY *pkey_tmp = nullptr; + if (EVP_PKEY_keygen(m_pkey_ctx.get(), &pkey_tmp) <= 0) { + m_log->error("Failed to generate keypair"); + return; + } + m_pkey.reset(pkey_tmp); + + m_dec_ctx.reset(EVP_PKEY_CTX_new(m_pkey.get(), nullptr)); + if (EVP_PKEY_decrypt_init(m_dec_ctx.get()) <= 0) { + m_log->error("Failed to initialize RSA decrypt context"); + return; + } + + if (EVP_PKEY_CTX_set_rsa_padding(m_dec_ctx.get(), RSA_PKCS1_OAEP_PADDING) <= 0) { + m_log->error("EVP_PKEY_CTX_set_rsa_padding failed"); + return; + } + + if (EVP_PKEY_CTX_set_rsa_oaep_md(m_dec_ctx.get(), EVP_sha256()) <= 0) { + m_log->error("EVP_PKEY_CTX_set_rsa_oaep_md failed"); + return; + } + + if (EVP_PKEY_CTX_set_rsa_mgf1_md(m_dec_ctx.get(), EVP_sha256()) <= 0) { + m_log->error("EVP_PKEY_CTX_set_rsa_mgf1_md"); + return; + } +} + +std::string RemoteAuthClient::GetEncodedPublicKey() const { + auto bio = BIO_ptr(BIO_new(BIO_s_mem()), BIO_free); + if (!bio) { + m_log->error("Failed to create BIO"); + return {}; + } + + if (PEM_write_bio_PUBKEY(bio.get(), m_pkey.get()) <= 0) { + m_log->error("Failed to write public key to BIO"); + return {}; + } + + // i think this is freed when the bio is too + BUF_MEM *mem = nullptr; + if (BIO_get_mem_ptr(bio.get(), &mem) <= 0) { + m_log->error("Failed to get BIO mem buf"); + return {}; + } + + if (mem->data == nullptr || mem->length == 0) { + m_log->error("BIO mem buf is null or of zero length"); + return {}; + } + + std::string pem_pubkey(mem->data, mem->length); + // isolate key + pem_pubkey.erase(0, pem_pubkey.find("\n") + 1); + pem_pubkey.erase(pem_pubkey.rfind("\n-")); + size_t pos; + while ((pos = pem_pubkey.find("\n")) != std::string::npos) { + pem_pubkey.erase(pos, 1); + } + return pem_pubkey; +} + +std::vector RemoteAuthClient::Decrypt(const unsigned char *in, size_t inlen) const { + // get length + size_t outlen; + if (EVP_PKEY_decrypt(m_dec_ctx.get(), nullptr, &outlen, in, inlen) <= 0) { + m_log->error("Failed to get length when decrypting"); + return {}; + } + + std::vector ret(outlen); + if (EVP_PKEY_decrypt(m_dec_ctx.get(), ret.data(), &outlen, in, inlen) <= 0) { + m_log->error("Failed to decrypt"); + return {}; + } + ret.resize(outlen); + return ret; +} + +void RemoteAuthClient::OnWebsocketOpen() { + m_log->info("Websocket opened"); +} + +void RemoteAuthClient::OnWebsocketClose(const ix::WebSocketCloseInfo &info) { + if (info.remote) { + m_log->debug("Websocket closed (remote): {} ({})", info.code, info.reason); + } else { + m_log->debug("Websocket closed (local): {} ({})", info.code, info.reason); + } +} + +void RemoteAuthClient::OnWebsocketMessage(const std::string &data) { + m_dispatch_mutex.lock(); + m_dispatch_queue.push(data); + m_dispatcher.emit(); + m_dispatch_mutex.unlock(); +} + +void RemoteAuthClient::HeartbeatThread() { + while (true) { + if (!m_heartbeat_waiter.wait_for(std::chrono::milliseconds(m_heartbeat_msec))) break; + + m_ws.Send(RemoteAuthHeartbeatMessage()); + } +} + +void RemoteAuthClient::OnDispatch() { + m_dispatch_mutex.lock(); + if (m_dispatch_queue.empty()) { + m_dispatch_mutex.unlock(); + return; + } + auto msg = std::move(m_dispatch_queue.front()); + m_dispatch_queue.pop(); + m_dispatch_mutex.unlock(); + OnGatewayMessage(msg); +} + +RemoteAuthClient::type_signal_fingerprint RemoteAuthClient::signal_fingerprint() { + return m_signal_fingerprint; +} diff --git a/src/remoteauth/remoteauthclient.hpp b/src/remoteauth/remoteauthclient.hpp new file mode 100644 index 0000000..178e10a --- /dev/null +++ b/src/remoteauth/remoteauthclient.hpp @@ -0,0 +1,64 @@ +#pragma once +#include +#include +#include +#include "ssl.hpp" +#include "discord/waiter.hpp" +#include "discord/websocket.hpp" + +class RemoteAuthClient { +public: + RemoteAuthClient(); + ~RemoteAuthClient(); + + void Start(); + void Stop(); + + [[nodiscard]] bool IsConnected() const noexcept; + +private: + void OnGatewayMessage(const std::string &str); + void HandleGatewayHello(const nlohmann::json &j); + void HandleGatewayNonceProof(const nlohmann::json &j); + void HandleGatewayPendingRemoteInit(const nlohmann::json &j); + void HandleGatewayPendingTicket(const nlohmann::json &j); + + void Init(); + + void GenerateKey(); + std::string GetEncodedPublicKey() const; + + std::vector Decrypt(const unsigned char *in, size_t inlen) const; + + void OnWebsocketOpen(); + void OnWebsocketClose(const ix::WebSocketCloseInfo &info); + void OnWebsocketMessage(const std::string &str); + + void HeartbeatThread(); + + int m_heartbeat_msec; + Waiter m_heartbeat_waiter; + std::thread m_heartbeat_thread; + + Glib::Dispatcher m_dispatcher; + std::queue m_dispatch_queue; + std::mutex m_dispatch_mutex; + + void OnDispatch(); + + Websocket m_ws; + bool m_connected = false; + + std::shared_ptr m_log; + + EVP_PKEY_CTX_ptr m_pkey_ctx; + EVP_PKEY_CTX_ptr m_dec_ctx; + EVP_PKEY_ptr m_pkey; + +public: + using type_signal_fingerprint = sigc::signal; + type_signal_fingerprint signal_fingerprint(); + +private: + type_signal_fingerprint m_signal_fingerprint; +}; diff --git a/src/remoteauth/remoteauthdialog.cpp b/src/remoteauth/remoteauthdialog.cpp new file mode 100644 index 0000000..db154c4 --- /dev/null +++ b/src/remoteauth/remoteauthdialog.cpp @@ -0,0 +1,79 @@ +#include "remoteauthdialog.hpp" +#include + +RemoteAuthDialog::RemoteAuthDialog(Gtk::Window &parent) + : Gtk::Dialog("Login with QR Code", parent, true) + , m_layout(Gtk::ORIENTATION_VERTICAL) + , m_ok("OK") + , m_cancel("Cancel") + , m_bbox(Gtk::ORIENTATION_HORIZONTAL) { + set_default_size(300, 50); + get_style_context()->add_class("app-window"); + get_style_context()->add_class("app-popup"); + + m_ok.signal_clicked().connect([&]() { + response(Gtk::RESPONSE_OK); + }); + + m_cancel.signal_clicked().connect([&]() { + response(Gtk::RESPONSE_CANCEL); + }); + + m_bbox.pack_start(m_ok, Gtk::PACK_SHRINK); + m_bbox.pack_start(m_cancel, Gtk::PACK_SHRINK); + m_bbox.set_layout(Gtk::BUTTONBOX_END); + + m_ra.signal_fingerprint().connect(sigc::mem_fun(*this, &RemoteAuthDialog::OnFingerprint)); + + m_ra.Start(); + + m_image.set_size_request(256, 256); + + m_layout.add(m_image); + m_layout.add(m_bbox); + get_content_area()->add(m_layout); + + show_all_children(); +} + +void RemoteAuthDialog::OnFingerprint(const std::string &fingerprint) { + const auto url = "https://discord.com/ra/" + fingerprint; + + const auto level = qrcodegen::QrCode::Ecc::QUARTILE; + const auto qr = qrcodegen::QrCode::encodeText(url.c_str(), level); + + int size = qr.getSize(); + const int border = 4; + + std::ostringstream sb; + sb << "\n"; + sb << "\n"; + sb << "\n"; + sb << "\t\n"; + sb << "\t\n"; + sb << "\n"; + + const auto svg = sb.str(); + + auto loader = Gdk::PixbufLoader::create(); + loader->write(reinterpret_cast(svg.data()), svg.size()); + loader->close(); + const auto pb = loader->get_pixbuf()->scale_simple(256, 256, Gdk::INTERP_NEAREST); + + m_image.property_pixbuf() = pb; +} + +std::string RemoteAuthDialog::GetToken() { + return m_token; +} diff --git a/src/remoteauth/remoteauthdialog.hpp b/src/remoteauth/remoteauthdialog.hpp new file mode 100644 index 0000000..24fad56 --- /dev/null +++ b/src/remoteauth/remoteauthdialog.hpp @@ -0,0 +1,23 @@ +#pragma once +#include +#include "remoteauthclient.hpp" + +class RemoteAuthDialog : public Gtk::Dialog { +public: + RemoteAuthDialog(Gtk::Window &parent); + std::string GetToken(); + +protected: + Gtk::Image m_image; + Gtk::Box m_layout; + Gtk::Button m_ok; + Gtk::Button m_cancel; + Gtk::ButtonBox m_bbox; + +private: + RemoteAuthClient m_ra; + + void OnFingerprint(const std::string &fingerprint); + + std::string m_token; +}; diff --git a/src/remoteauth/ssl.hpp b/src/remoteauth/ssl.hpp new file mode 100644 index 0000000..1753bd3 --- /dev/null +++ b/src/remoteauth/ssl.hpp @@ -0,0 +1,31 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +struct EVP_PKEY_CTX_deleter { + void operator()(EVP_PKEY_CTX *ptr) const { + EVP_PKEY_CTX_free(ptr); + } +}; + +struct EVP_PKEY_deleter { + void operator()(EVP_PKEY *ptr) const { + EVP_PKEY_free(ptr); + } +}; + +struct EVP_MD_CTX_deleter { + void operator()(EVP_MD_CTX *ptr) const { + EVP_MD_CTX_free(ptr); + } +}; + +using EVP_PKEY_CTX_ptr = std::unique_ptr; +using EVP_PKEY_ptr = std::unique_ptr; +using EVP_MD_CTX_ptr = std::unique_ptr; +using BIO_ptr = std::unique_ptr; +using BUF_MEM_ptr = std::unique_ptr; diff --git a/src/windows/mainwindow.cpp b/src/windows/mainwindow.cpp index bb09735..56d735e 100644 --- a/src/windows/mainwindow.cpp +++ b/src/windows/mainwindow.cpp @@ -206,6 +206,7 @@ void MainWindow::OnDiscordSubmenuPopup() { m_menu_discord_connect.set_sensitive(!token.empty() && !discord_active); m_menu_discord_disconnect.set_sensitive(discord_active); m_menu_discord_set_token.set_sensitive(!discord_active); + m_menu_discord_login_qr.set_sensitive(!discord_active); m_menu_discord_set_status.set_sensitive(discord_active); } @@ -247,12 +248,14 @@ void MainWindow::SetupMenu() { m_menu_discord_disconnect.set_label("Disconnect"); m_menu_discord_disconnect.set_sensitive(false); m_menu_discord_set_token.set_label("Set Token"); + m_menu_discord_login_qr.set_label("Login with QR Code"); m_menu_discord_set_status.set_label("Set Status"); m_menu_discord_set_status.set_sensitive(false); m_menu_discord_add_recipient.set_label("Add user to DM"); m_menu_discord_sub.append(m_menu_discord_connect); m_menu_discord_sub.append(m_menu_discord_disconnect); m_menu_discord_sub.append(m_menu_discord_set_token); + m_menu_discord_sub.append(m_menu_discord_login_qr); m_menu_discord_sub.append(m_menu_discord_set_status); m_menu_discord_sub.append(m_menu_discord_add_recipient); m_menu_discord.set_submenu(m_menu_discord_sub); @@ -331,6 +334,10 @@ void MainWindow::SetupMenu() { m_signal_action_set_token.emit(); }); + m_menu_discord_login_qr.signal_activate().connect([this] { + m_signal_action_login_qr.emit(); + }); + m_menu_file_reload_css.signal_activate().connect([this] { m_signal_action_reload_css.emit(); }); @@ -421,6 +428,10 @@ MainWindow::type_signal_action_set_token MainWindow::signal_action_set_token() { return m_signal_action_set_token; } +MainWindow::type_signal_action_login_qr MainWindow::signal_action_login_qr() { + return m_signal_action_login_qr; +} + MainWindow::type_signal_action_reload_css MainWindow::signal_action_reload_css() { return m_signal_action_reload_css; } diff --git a/src/windows/mainwindow.hpp b/src/windows/mainwindow.hpp index e075c10..604043c 100644 --- a/src/windows/mainwindow.hpp +++ b/src/windows/mainwindow.hpp @@ -74,6 +74,7 @@ private: Gtk::MenuItem m_menu_discord_connect; Gtk::MenuItem m_menu_discord_disconnect; Gtk::MenuItem m_menu_discord_set_token; + Gtk::MenuItem m_menu_discord_login_qr; Gtk::MenuItem m_menu_discord_set_status; Gtk::MenuItem m_menu_discord_add_recipient; // move me somewhere else some day void OnDiscordSubmenuPopup(); @@ -103,6 +104,7 @@ public: typedef sigc::signal type_signal_action_connect; typedef sigc::signal type_signal_action_disconnect; typedef sigc::signal type_signal_action_set_token; + typedef sigc::signal type_signal_action_login_qr; typedef sigc::signal type_signal_action_reload_css; typedef sigc::signal type_signal_action_set_status; // this should probably be removed @@ -113,6 +115,7 @@ public: type_signal_action_connect signal_action_connect(); type_signal_action_disconnect signal_action_disconnect(); type_signal_action_set_token signal_action_set_token(); + type_signal_action_login_qr signal_action_login_qr(); type_signal_action_reload_css signal_action_reload_css(); type_signal_action_set_status signal_action_set_status(); type_signal_action_add_recipient signal_action_add_recipient(); @@ -123,6 +126,7 @@ private: type_signal_action_connect m_signal_action_connect; type_signal_action_disconnect m_signal_action_disconnect; type_signal_action_set_token m_signal_action_set_token; + type_signal_action_login_qr m_signal_action_login_qr; type_signal_action_reload_css m_signal_action_reload_css; type_signal_action_set_status m_signal_action_set_status; type_signal_action_add_recipient m_signal_action_add_recipient; diff --git a/subprojects/qrcodegen b/subprojects/qrcodegen new file mode 160000 index 0000000..22fac31 --- /dev/null +++ b/subprojects/qrcodegen @@ -0,0 +1 @@ +Subproject commit 22fac31bdf81da68730c177c0e931c93234d2a30 From 4ac7ca9245016616bbc77acf8e989e41729e1afd Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Sat, 1 Jul 2023 00:56:09 -0400 Subject: [PATCH 02/13] finalize login --- src/abaddon.cpp | 3 +++ src/discord/discord.cpp | 14 +++++++++++++ src/discord/discord.hpp | 2 ++ src/remoteauth/remoteauthclient.cpp | 32 +++++++++++++++++++++++++++++ src/remoteauth/remoteauthclient.hpp | 6 ++++++ src/remoteauth/remoteauthdialog.cpp | 7 +++++++ src/remoteauth/remoteauthdialog.hpp | 1 + 7 files changed, 65 insertions(+) diff --git a/src/abaddon.cpp b/src/abaddon.cpp index 545a3c2..e0d39b5 100644 --- a/src/abaddon.cpp +++ b/src/abaddon.cpp @@ -841,9 +841,12 @@ void Abaddon::ActionLoginQR() { auto response = dlg.run(); if (response == Gtk::RESPONSE_OK) { m_discord_token = dlg.GetToken(); + m_discord.UpdateToken(m_discord_token); m_main_window->UpdateComponents(); + GetSettings().DiscordToken = m_discord_token; } m_main_window->UpdateMenus(); + ActionConnect(); } void Abaddon::ActionChannelOpened(Snowflake id, bool expand_to) { diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp index 817aca8..5dc0464 100644 --- a/src/discord/discord.cpp +++ b/src/discord/discord.cpp @@ -1197,6 +1197,20 @@ void DiscordClient::AcceptVerificationGate(Snowflake guild_id, VerificationGateI }); } +void DiscordClient::RemoteAuthLogin(const std::string &ticket, const sigc::slot, DiscordError code)> &callback) { + http::request req(http::REQUEST_POST, "https://discord.com/api/v9/users/@me/remote-auth/login"); + req.set_header("Content-Type", "application/json"); + req.set_user_agent(Abaddon::Get().GetSettings().UserAgent); + req.set_body("{\"ticket\":\"" + ticket + "\"}"); + m_http.Execute(std::move(req), [this, callback](const http::response_type &r) { + if (CheckCode(r)) { + callback(nlohmann::json::parse(r.text).at("encrypted_token").get(), DiscordError::NONE); + } else { + callback(std::nullopt, GetCodeFromResponse(r)); + } + }); +} + #ifdef WITH_VOICE void DiscordClient::ConnectToVoice(Snowflake channel_id) { auto channel = GetChannel(channel_id); diff --git a/src/discord/discord.hpp b/src/discord/discord.hpp index d2435dd..438e4e6 100644 --- a/src/discord/discord.hpp +++ b/src/discord/discord.hpp @@ -184,6 +184,8 @@ public: void GetVerificationGateInfo(Snowflake guild_id, const sigc::slot)> &callback); void AcceptVerificationGate(Snowflake guild_id, VerificationGateInfoObject info, const sigc::slot &callback); + void RemoteAuthLogin(const std::string &ticket, const sigc::slot, DiscordError code)> &callback); + #ifdef WITH_VOICE void ConnectToVoice(Snowflake channel_id); void DisconnectFromVoice(); diff --git a/src/remoteauth/remoteauthclient.cpp b/src/remoteauth/remoteauthclient.cpp index 1ebb7cb..390f42d 100644 --- a/src/remoteauth/remoteauthclient.cpp +++ b/src/remoteauth/remoteauthclient.cpp @@ -1,4 +1,5 @@ #include "remoteauthclient.hpp" +#include "http.hpp" #include #include @@ -65,6 +66,14 @@ struct RemoteAuthPendingTicketMessage { } }; +struct RemoteAuthPendingLoginMessage { + std::string Ticket; + + friend void from_json(const nlohmann::json &j, RemoteAuthPendingLoginMessage &m) { + j.at("ticket").get_to(m.Ticket); + } +}; + RemoteAuthClient::RemoteAuthClient() : m_ws("remote-auth-ws") , m_log(spdlog::get("remote-auth")) { @@ -116,6 +125,8 @@ void RemoteAuthClient::OnGatewayMessage(const std::string &str) { HandleGatewayPendingRemoteInit(j); } else if (msg.Opcode == "pending_ticket") { HandleGatewayPendingTicket(j); + } else if (msg.Opcode == "pending_login") { + HandleGatewayPendingLogin(j); } } @@ -162,6 +173,23 @@ void RemoteAuthClient::HandleGatewayPendingTicket(const nlohmann::json &j) { m_log->trace("User payload: {}", std::string(payload.begin(), payload.end())); } +void RemoteAuthClient::HandleGatewayPendingLogin(const nlohmann::json &j) { + RemoteAuthPendingLoginMessage msg = j; + + Abaddon::Get().GetDiscordClient().RemoteAuthLogin(msg.Ticket, sigc::mem_fun(*this, &RemoteAuthClient::OnRemoteAuthLoginResponse)); +} + +void RemoteAuthClient::OnRemoteAuthLoginResponse(const std::optional &encrypted_token, DiscordError err) { + if (!encrypted_token.has_value()) { + m_log->error("Remote auth login failed: {}", static_cast(err)); + return; + } + + const auto encrypted = Glib::Base64::decode(*encrypted_token); + const auto token = Decrypt(reinterpret_cast(encrypted.data()), encrypted.size()); + m_signal_token.emit(std::string(token.begin(), token.end())); +} + void RemoteAuthClient::Init() { RemoteAuthInitMessage msg; @@ -319,3 +347,7 @@ void RemoteAuthClient::OnDispatch() { RemoteAuthClient::type_signal_fingerprint RemoteAuthClient::signal_fingerprint() { return m_signal_fingerprint; } + +RemoteAuthClient::type_signal_token RemoteAuthClient::signal_token() { + return m_signal_token; +} diff --git a/src/remoteauth/remoteauthclient.hpp b/src/remoteauth/remoteauthclient.hpp index 178e10a..8fb8bc3 100644 --- a/src/remoteauth/remoteauthclient.hpp +++ b/src/remoteauth/remoteauthclient.hpp @@ -22,6 +22,9 @@ private: void HandleGatewayNonceProof(const nlohmann::json &j); void HandleGatewayPendingRemoteInit(const nlohmann::json &j); void HandleGatewayPendingTicket(const nlohmann::json &j); + void HandleGatewayPendingLogin(const nlohmann::json &j); + + void OnRemoteAuthLoginResponse(const std::optional &encrypted_token, DiscordError err); void Init(); @@ -57,8 +60,11 @@ private: public: using type_signal_fingerprint = sigc::signal; + using type_signal_token = sigc::signal; type_signal_fingerprint signal_fingerprint(); + type_signal_token signal_token(); private: type_signal_fingerprint m_signal_fingerprint; + type_signal_token m_signal_token; }; diff --git a/src/remoteauth/remoteauthdialog.cpp b/src/remoteauth/remoteauthdialog.cpp index db154c4..ea250d6 100644 --- a/src/remoteauth/remoteauthdialog.cpp +++ b/src/remoteauth/remoteauthdialog.cpp @@ -24,6 +24,7 @@ RemoteAuthDialog::RemoteAuthDialog(Gtk::Window &parent) m_bbox.set_layout(Gtk::BUTTONBOX_END); m_ra.signal_fingerprint().connect(sigc::mem_fun(*this, &RemoteAuthDialog::OnFingerprint)); + m_ra.signal_token().connect(sigc::mem_fun(*this, &RemoteAuthDialog::OnToken)); m_ra.Start(); @@ -77,3 +78,9 @@ void RemoteAuthDialog::OnFingerprint(const std::string &fingerprint) { std::string RemoteAuthDialog::GetToken() { return m_token; } + +void RemoteAuthDialog::OnToken(const std::string &token) { + m_token = token; + m_ra.Stop(); + response(Gtk::RESPONSE_OK); +} diff --git a/src/remoteauth/remoteauthdialog.hpp b/src/remoteauth/remoteauthdialog.hpp index 24fad56..44af821 100644 --- a/src/remoteauth/remoteauthdialog.hpp +++ b/src/remoteauth/remoteauthdialog.hpp @@ -18,6 +18,7 @@ private: RemoteAuthClient m_ra; void OnFingerprint(const std::string &fingerprint); + void OnToken(const std::string &token); std::string m_token; }; From 656bea8e55c13f1d29fb84040b5007aef3eef0c8 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Sat, 1 Jul 2023 01:02:53 -0400 Subject: [PATCH 03/13] get rid of the message structs because theyre unnecessary --- src/remoteauth/remoteauthclient.cpp | 122 ++++++---------------------- 1 file changed, 25 insertions(+), 97 deletions(-) diff --git a/src/remoteauth/remoteauthclient.cpp b/src/remoteauth/remoteauthclient.cpp index 390f42d..5749f10 100644 --- a/src/remoteauth/remoteauthclient.cpp +++ b/src/remoteauth/remoteauthclient.cpp @@ -3,77 +3,6 @@ #include #include -struct RemoteAuthGatewayMessage { - std::string Opcode; - - friend void from_json(const nlohmann::json &j, RemoteAuthGatewayMessage &m) { - j.at("op").get_to(m.Opcode); - } -}; - -struct RemoteAuthHelloMessage { - int TimeoutMS; - int HeartbeatInterval; - - friend void from_json(const nlohmann::json &j, RemoteAuthHelloMessage &m) { - j.at("timeout_ms").get_to(m.TimeoutMS); - j.at("heartbeat_interval").get_to(m.HeartbeatInterval); - } -}; - -struct RemoteAuthHeartbeatMessage { - friend void to_json(nlohmann::json &j, const RemoteAuthHeartbeatMessage &m) { - j["op"] = "heartbeat"; - } -}; - -struct RemoteAuthInitMessage { - std::string EncodedPublicKey; - - friend void to_json(nlohmann::json &j, const RemoteAuthInitMessage &m) { - j["op"] = "init"; - j["encoded_public_key"] = m.EncodedPublicKey; - } -}; - -struct RemoteAuthNonceProofMessage { - std::string EncryptedNonce; - std::string Nonce; - - friend void from_json(const nlohmann::json &j, RemoteAuthNonceProofMessage &m) { - j.at("encrypted_nonce").get_to(m.EncryptedNonce); - } - - friend void to_json(nlohmann::json &j, const RemoteAuthNonceProofMessage &m) { - j["op"] = "nonce_proof"; - j["nonce"] = m.Nonce; - } -}; - -struct RemoteAuthFingerprintMessage { - std::string Fingerprint; - - friend void from_json(const nlohmann::json &j, RemoteAuthFingerprintMessage &m) { - j.at("fingerprint").get_to(m.Fingerprint); - } -}; - -struct RemoteAuthPendingTicketMessage { - std::string EncryptedUserPayload; - - friend void from_json(const nlohmann::json &j, RemoteAuthPendingTicketMessage &m) { - j.at("encrypted_user_payload").get_to(m.EncryptedUserPayload); - } -}; - -struct RemoteAuthPendingLoginMessage { - std::string Ticket; - - friend void from_json(const nlohmann::json &j, RemoteAuthPendingLoginMessage &m) { - j.at("ticket").get_to(m.Ticket); - } -}; - RemoteAuthClient::RemoteAuthClient() : m_ws("remote-auth-ws") , m_log(spdlog::get("remote-auth")) { @@ -116,35 +45,35 @@ bool RemoteAuthClient::IsConnected() const noexcept { void RemoteAuthClient::OnGatewayMessage(const std::string &str) { auto j = nlohmann::json::parse(str); - RemoteAuthGatewayMessage msg = j; - if (msg.Opcode == "hello") { + const auto opcode = j.at("op").get(); + if (opcode == "hello") { HandleGatewayHello(j); - } else if (msg.Opcode == "nonce_proof") { + } else if (opcode == "nonce_proof") { HandleGatewayNonceProof(j); - } else if (msg.Opcode == "pending_remote_init") { + } else if (opcode == "pending_remote_init") { HandleGatewayPendingRemoteInit(j); - } else if (msg.Opcode == "pending_ticket") { + } else if (opcode == "pending_ticket") { HandleGatewayPendingTicket(j); - } else if (msg.Opcode == "pending_login") { + } else if (opcode == "pending_login") { HandleGatewayPendingLogin(j); } } void RemoteAuthClient::HandleGatewayHello(const nlohmann::json &j) { - RemoteAuthHelloMessage msg = j; - m_log->debug("Timeout: {}, Heartbeat: {}", msg.TimeoutMS, msg.HeartbeatInterval); + const auto timeout_ms = j.at("timeout_ms").get(); + const auto heartbeat_interval = j.at("heartbeat_interval").get(); + m_log->debug("Timeout: {}, Heartbeat: {}", timeout_ms, heartbeat_interval); - m_heartbeat_msec = msg.HeartbeatInterval; + m_heartbeat_msec = heartbeat_interval; m_heartbeat_thread = std::thread(&RemoteAuthClient::HeartbeatThread, this); Init(); } void RemoteAuthClient::HandleGatewayNonceProof(const nlohmann::json &j) { - RemoteAuthNonceProofMessage msg = j; m_log->debug("Received encrypted nonce"); - const auto encrypted_nonce = Glib::Base64::decode(msg.EncryptedNonce); + const auto encrypted_nonce = Glib::Base64::decode(j.at("encrypted_nonce").get()); const auto proof = Decrypt(reinterpret_cast(encrypted_nonce.data()), encrypted_nonce.size()); auto proof_encoded = Glib::Base64::encode(std::string(proof.begin(), proof.end())); @@ -152,31 +81,27 @@ void RemoteAuthClient::HandleGatewayNonceProof(const nlohmann::json &j) { std::replace(proof_encoded.begin(), proof_encoded.end(), '+', '-'); proof_encoded.erase(std::remove(proof_encoded.begin(), proof_encoded.end(), '='), proof_encoded.end()); - RemoteAuthNonceProofMessage reply; - reply.Nonce = proof_encoded; + nlohmann::json reply; + reply["op"] = "nonce_proof"; + reply["nonce"] = proof_encoded; m_ws.Send(reply); } void RemoteAuthClient::HandleGatewayPendingRemoteInit(const nlohmann::json &j) { - RemoteAuthFingerprintMessage msg = j; m_log->debug("Received fingerprint"); - m_signal_fingerprint.emit(msg.Fingerprint); + m_signal_fingerprint.emit(j.at("fingerprint").get()); } void RemoteAuthClient::HandleGatewayPendingTicket(const nlohmann::json &j) { - RemoteAuthPendingTicketMessage msg = j; - - const auto encrypted_payload = Glib::Base64::decode(msg.EncryptedUserPayload); + const auto encrypted_payload = Glib::Base64::decode(j.at("encrypted_user_payload").get()); const auto payload = Decrypt(reinterpret_cast(encrypted_payload.data()), encrypted_payload.size()); m_log->trace("User payload: {}", std::string(payload.begin(), payload.end())); } void RemoteAuthClient::HandleGatewayPendingLogin(const nlohmann::json &j) { - RemoteAuthPendingLoginMessage msg = j; - - Abaddon::Get().GetDiscordClient().RemoteAuthLogin(msg.Ticket, sigc::mem_fun(*this, &RemoteAuthClient::OnRemoteAuthLoginResponse)); + Abaddon::Get().GetDiscordClient().RemoteAuthLogin(j.at("ticket").get(), sigc::mem_fun(*this, &RemoteAuthClient::OnRemoteAuthLoginResponse)); } void RemoteAuthClient::OnRemoteAuthLoginResponse(const std::optional &encrypted_token, DiscordError err) { @@ -191,16 +116,17 @@ void RemoteAuthClient::OnRemoteAuthLoginResponse(const std::optionalerror("Something went wrong"); // todo disconnect return; } + nlohmann::json msg; + msg["op"] = "init"; + msg["encoded_public_key"] = key; m_ws.Send(msg); } @@ -328,7 +254,9 @@ void RemoteAuthClient::HeartbeatThread() { while (true) { if (!m_heartbeat_waiter.wait_for(std::chrono::milliseconds(m_heartbeat_msec))) break; - m_ws.Send(RemoteAuthHeartbeatMessage()); + nlohmann::json hb; + hb["op"] = "hearbeat"; + m_ws.Send(hb); } } From 044e508df7868856648e14ad7c957be8b9bb46b5 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Sat, 1 Jul 2023 01:47:45 -0400 Subject: [PATCH 04/13] only connect on RESPONSE_OK --- src/abaddon.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/abaddon.cpp b/src/abaddon.cpp index e0d39b5..c161595 100644 --- a/src/abaddon.cpp +++ b/src/abaddon.cpp @@ -844,9 +844,9 @@ void Abaddon::ActionLoginQR() { m_discord.UpdateToken(m_discord_token); m_main_window->UpdateComponents(); GetSettings().DiscordToken = m_discord_token; + ActionConnect(); } m_main_window->UpdateMenus(); - ActionConnect(); } void Abaddon::ActionChannelOpened(Snowflake id, bool expand_to) { From ab448a3a9820663a882bde242525546cc4273516 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Sat, 1 Jul 2023 02:10:42 -0400 Subject: [PATCH 05/13] show status, error when captcha required --- src/discord/discord.cpp | 7 +++++ src/discord/errors.hpp | 1 + src/remoteauth/remoteauthclient.cpp | 38 ++++++++++++++++++++++++ src/remoteauth/remoteauthclient.hpp | 12 ++++++++ src/remoteauth/remoteauthdialog.cpp | 45 +++++++++++++++++++++++++++-- src/remoteauth/remoteauthdialog.hpp | 5 ++++ 6 files changed, 106 insertions(+), 2 deletions(-) diff --git a/src/discord/discord.cpp b/src/discord/discord.cpp index 5dc0464..75d066b 100644 --- a/src/discord/discord.cpp +++ b/src/discord/discord.cpp @@ -1206,6 +1206,13 @@ void DiscordClient::RemoteAuthLogin(const std::string &ticket, const sigc::slot< if (CheckCode(r)) { callback(nlohmann::json::parse(r.text).at("encrypted_token").get(), DiscordError::NONE); } else { + try { + const auto j = nlohmann::json::parse(r.text); + if (j.contains("captcha_service")) { + callback(std::nullopt, DiscordError::CAPTCHA_REQUIRED); + return; + } + } catch (...) {} callback(std::nullopt, GetCodeFromResponse(r)); } }); diff --git a/src/discord/errors.hpp b/src/discord/errors.hpp index 4579563..8ead4f2 100644 --- a/src/discord/errors.hpp +++ b/src/discord/errors.hpp @@ -11,6 +11,7 @@ enum class DiscordError { RELATIONSHIP_ALREADY_FRIENDS = 80007, NONE = -1, + CAPTCHA_REQUIRED = -2, }; constexpr const char *GetDiscordErrorDisplayString(DiscordError error) { diff --git a/src/remoteauth/remoteauthclient.cpp b/src/remoteauth/remoteauthclient.cpp index 5749f10..e2d3cc6 100644 --- a/src/remoteauth/remoteauthclient.cpp +++ b/src/remoteauth/remoteauthclient.cpp @@ -68,6 +68,8 @@ void RemoteAuthClient::HandleGatewayHello(const nlohmann::json &j) { m_heartbeat_thread = std::thread(&RemoteAuthClient::HeartbeatThread, this); Init(); + + m_signal_hello.emit(); } void RemoteAuthClient::HandleGatewayNonceProof(const nlohmann::json &j) { @@ -98,15 +100,35 @@ void RemoteAuthClient::HandleGatewayPendingTicket(const nlohmann::json &j) { const auto payload = Decrypt(reinterpret_cast(encrypted_payload.data()), encrypted_payload.size()); m_log->trace("User payload: {}", std::string(payload.begin(), payload.end())); + + const std::vector user_info = Glib::Regex::split_simple(":", std::string(payload.begin(), payload.end())); + Snowflake user_id; + std::string discriminator; + std::string avatar_hash; + std::string username; + if (user_info.size() >= 4) { + user_id = Snowflake(user_info[0]); + discriminator = user_info[1]; + avatar_hash = user_info[2]; + username = user_info[3]; + } + + m_signal_pending_ticket.emit(user_id, discriminator, avatar_hash, username); } void RemoteAuthClient::HandleGatewayPendingLogin(const nlohmann::json &j) { Abaddon::Get().GetDiscordClient().RemoteAuthLogin(j.at("ticket").get(), sigc::mem_fun(*this, &RemoteAuthClient::OnRemoteAuthLoginResponse)); + m_signal_pending_login.emit(); } void RemoteAuthClient::OnRemoteAuthLoginResponse(const std::optional &encrypted_token, DiscordError err) { if (!encrypted_token.has_value()) { m_log->error("Remote auth login failed: {}", static_cast(err)); + if (err == DiscordError::CAPTCHA_REQUIRED) { + m_signal_error.emit("Discord is requiring a captcha. You must use a web browser to log in."); + } else { + m_signal_error.emit("An error occurred. Try again."); + } return; } @@ -272,10 +294,26 @@ void RemoteAuthClient::OnDispatch() { OnGatewayMessage(msg); } +RemoteAuthClient::type_signal_hello RemoteAuthClient::signal_hello() { + return m_signal_hello; +} + RemoteAuthClient::type_signal_fingerprint RemoteAuthClient::signal_fingerprint() { return m_signal_fingerprint; } +RemoteAuthClient::type_signal_pending_ticket RemoteAuthClient::signal_pending_ticket() { + return m_signal_pending_ticket; +} + +RemoteAuthClient::type_signal_pending_login RemoteAuthClient::signal_pending_login() { + return m_signal_pending_login; +} + RemoteAuthClient::type_signal_token RemoteAuthClient::signal_token() { return m_signal_token; } + +RemoteAuthClient::type_signal_error RemoteAuthClient::signal_error() { + return m_signal_error; +} diff --git a/src/remoteauth/remoteauthclient.hpp b/src/remoteauth/remoteauthclient.hpp index 8fb8bc3..7d7dee9 100644 --- a/src/remoteauth/remoteauthclient.hpp +++ b/src/remoteauth/remoteauthclient.hpp @@ -59,12 +59,24 @@ private: EVP_PKEY_ptr m_pkey; public: + using type_signal_hello = sigc::signal; using type_signal_fingerprint = sigc::signal; + using type_signal_pending_ticket = sigc::signal; + using type_signal_pending_login = sigc::signal; using type_signal_token = sigc::signal; + using type_signal_error = sigc::signal; + type_signal_hello signal_hello(); type_signal_fingerprint signal_fingerprint(); + type_signal_pending_ticket signal_pending_ticket(); + type_signal_pending_login signal_pending_login(); type_signal_token signal_token(); + type_signal_error signal_error(); private: + type_signal_hello m_signal_hello; type_signal_fingerprint m_signal_fingerprint; + type_signal_pending_ticket m_signal_pending_ticket; + type_signal_pending_login m_signal_pending_login; type_signal_token m_signal_token; + type_signal_error m_signal_error; }; diff --git a/src/remoteauth/remoteauthdialog.cpp b/src/remoteauth/remoteauthdialog.cpp index ea250d6..c59f0ed 100644 --- a/src/remoteauth/remoteauthdialog.cpp +++ b/src/remoteauth/remoteauthdialog.cpp @@ -23,21 +23,40 @@ RemoteAuthDialog::RemoteAuthDialog(Gtk::Window &parent) m_bbox.pack_start(m_cancel, Gtk::PACK_SHRINK); m_bbox.set_layout(Gtk::BUTTONBOX_END); + m_ra.signal_hello().connect(sigc::mem_fun(*this, &RemoteAuthDialog::OnHello)); m_ra.signal_fingerprint().connect(sigc::mem_fun(*this, &RemoteAuthDialog::OnFingerprint)); + m_ra.signal_pending_ticket().connect(sigc::mem_fun(*this, &RemoteAuthDialog::OnPendingTicket)); + m_ra.signal_pending_login().connect(sigc::mem_fun(*this, &RemoteAuthDialog::OnPendingLogin)); m_ra.signal_token().connect(sigc::mem_fun(*this, &RemoteAuthDialog::OnToken)); + m_ra.signal_error().connect(sigc::mem_fun(*this, &RemoteAuthDialog::OnError)); m_ra.Start(); m_image.set_size_request(256, 256); + m_status.set_text("Connecting..."); + m_status.set_hexpand(true); + m_status.set_halign(Gtk::ALIGN_CENTER); + m_layout.add(m_image); + m_layout.add(m_status); m_layout.add(m_bbox); get_content_area()->add(m_layout); show_all_children(); } +std::string RemoteAuthDialog::GetToken() { + return m_token; +} + +void RemoteAuthDialog::OnHello() { + m_status.set_text("Handshaking..."); +} + void RemoteAuthDialog::OnFingerprint(const std::string &fingerprint) { + m_status.set_text("Waiting for mobile device..."); + const auto url = "https://discord.com/ra/" + fingerprint; const auto level = qrcodegen::QrCode::Ecc::QUARTILE; @@ -75,8 +94,24 @@ void RemoteAuthDialog::OnFingerprint(const std::string &fingerprint) { m_image.property_pixbuf() = pb; } -std::string RemoteAuthDialog::GetToken() { - return m_token; +void RemoteAuthDialog::OnPendingTicket(Snowflake user_id, std::string discriminator, std::string avatar_hash, std::string username) { + Glib::ustring name = username; + if (discriminator != "0") { + name += "#" + discriminator; + } + m_status.set_text("Waiting for confirmation... (" + name + ")"); + + if (!avatar_hash.empty()) { + const auto url = "https://cdn.discordapp.com/avatars/" + std::to_string(user_id) + "/" + avatar_hash + ".png?size=256"; + const auto cb = [this](const Glib::RefPtr &pb) { + m_image.property_pixbuf() = pb->scale_simple(256, 256, Gdk::INTERP_BILINEAR); + }; + Abaddon::Get().GetImageManager().LoadFromURL(url, sigc::track_obj(cb, *this)); + } +} + +void RemoteAuthDialog::OnPendingLogin() { + m_status.set_text("Logging in!"); } void RemoteAuthDialog::OnToken(const std::string &token) { @@ -84,3 +119,9 @@ void RemoteAuthDialog::OnToken(const std::string &token) { m_ra.Stop(); response(Gtk::RESPONSE_OK); } + +void RemoteAuthDialog::OnError(const std::string &error) { + m_ra.Stop(); + Abaddon::Get().ShowConfirm(error, dynamic_cast(get_toplevel())); + response(Gtk::RESPONSE_CANCEL); +} diff --git a/src/remoteauth/remoteauthdialog.hpp b/src/remoteauth/remoteauthdialog.hpp index 44af821..be559df 100644 --- a/src/remoteauth/remoteauthdialog.hpp +++ b/src/remoteauth/remoteauthdialog.hpp @@ -9,6 +9,7 @@ public: protected: Gtk::Image m_image; + Gtk::Label m_status; Gtk::Box m_layout; Gtk::Button m_ok; Gtk::Button m_cancel; @@ -17,8 +18,12 @@ protected: private: RemoteAuthClient m_ra; + void OnHello(); void OnFingerprint(const std::string &fingerprint); + void OnPendingTicket(Snowflake user_id, std::string discriminator, std::string avatar_hash, std::string username); + void OnPendingLogin(); void OnToken(const std::string &token); + void OnError(const std::string &error); std::string m_token; }; From cf02c1395271fd6c127fa2ed6557b3aeb279e5fe Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Mon, 3 Jul 2023 20:01:53 -0400 Subject: [PATCH 06/13] use ppm instead of svg for generating qr pixbuf --- src/remoteauth/remoteauthdialog.cpp | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/remoteauth/remoteauthdialog.cpp b/src/remoteauth/remoteauthdialog.cpp index c59f0ed..5ba91ae 100644 --- a/src/remoteauth/remoteauthdialog.cpp +++ b/src/remoteauth/remoteauthdialog.cpp @@ -65,29 +65,26 @@ void RemoteAuthDialog::OnFingerprint(const std::string &fingerprint) { int size = qr.getSize(); const int border = 4; + const auto module_set = "0 0 0"; + const auto module_clr = "255 255 255"; + std::ostringstream sb; - sb << "\n"; - sb << "\n"; - sb << "\n"; - sb << "\t\n"; - sb << "\t\n"; - sb << "\n"; - const auto svg = sb.str(); + const auto img = sb.str(); auto loader = Gdk::PixbufLoader::create(); - loader->write(reinterpret_cast(svg.data()), svg.size()); + loader->write(reinterpret_cast(img.data()), img.size()); loader->close(); const auto pb = loader->get_pixbuf()->scale_simple(256, 256, Gdk::INTERP_NEAREST); From e0623281eb7fcdc1d04c44b4cd3a30dbffc97134 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Sun, 9 Jul 2023 21:20:36 -0400 Subject: [PATCH 07/13] fix typo in heartbeat --- src/remoteauth/remoteauthclient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remoteauth/remoteauthclient.cpp b/src/remoteauth/remoteauthclient.cpp index e2d3cc6..c0a06d9 100644 --- a/src/remoteauth/remoteauthclient.cpp +++ b/src/remoteauth/remoteauthclient.cpp @@ -277,7 +277,7 @@ void RemoteAuthClient::HeartbeatThread() { if (!m_heartbeat_waiter.wait_for(std::chrono::milliseconds(m_heartbeat_msec))) break; nlohmann::json hb; - hb["op"] = "hearbeat"; + hb["op"] = "heartbeat"; m_ws.Send(hb); } } From aaa219ce27561bfe060bee5a4eaba0326bc50412 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Sun, 9 Jul 2023 21:21:29 -0400 Subject: [PATCH 08/13] restart ra ws on timeout --- src/remoteauth/remoteauthclient.cpp | 11 +++++++++++ src/remoteauth/remoteauthclient.hpp | 3 +++ src/remoteauth/remoteauthdialog.cpp | 4 ++-- src/remoteauth/remoteauthdialog.hpp | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/remoteauth/remoteauthclient.cpp b/src/remoteauth/remoteauthclient.cpp index c0a06d9..083bdaa 100644 --- a/src/remoteauth/remoteauthclient.cpp +++ b/src/remoteauth/remoteauthclient.cpp @@ -34,6 +34,7 @@ void RemoteAuthClient::Stop() { } m_connected = false; + if (m_timeout_conn) m_timeout_conn.disconnect(); m_ws.Stop(1000); m_heartbeat_waiter.kill(); if (m_heartbeat_thread.joinable()) m_heartbeat_thread.join(); @@ -44,6 +45,7 @@ bool RemoteAuthClient::IsConnected() const noexcept { } void RemoteAuthClient::OnGatewayMessage(const std::string &str) { + m_log->trace(str); auto j = nlohmann::json::parse(str); const auto opcode = j.at("op").get(); if (opcode == "hello") { @@ -67,6 +69,8 @@ void RemoteAuthClient::HandleGatewayHello(const nlohmann::json &j) { m_heartbeat_msec = heartbeat_interval; m_heartbeat_thread = std::thread(&RemoteAuthClient::HeartbeatThread, this); + m_timeout_conn = Glib::signal_timeout().connect(sigc::mem_fun(*this, &RemoteAuthClient::OnTimeout), timeout_ms); + Init(); m_signal_hello.emit(); @@ -294,6 +298,13 @@ void RemoteAuthClient::OnDispatch() { OnGatewayMessage(msg); } +bool RemoteAuthClient::OnTimeout() { + m_log->trace("Socket timeout"); + Stop(); + Start(); + return false; // disconnect +} + RemoteAuthClient::type_signal_hello RemoteAuthClient::signal_hello() { return m_signal_hello; } diff --git a/src/remoteauth/remoteauthclient.hpp b/src/remoteauth/remoteauthclient.hpp index 7d7dee9..36f224a 100644 --- a/src/remoteauth/remoteauthclient.hpp +++ b/src/remoteauth/remoteauthclient.hpp @@ -49,6 +49,9 @@ private: void OnDispatch(); + bool OnTimeout(); + sigc::connection m_timeout_conn; + Websocket m_ws; bool m_connected = false; diff --git a/src/remoteauth/remoteauthdialog.cpp b/src/remoteauth/remoteauthdialog.cpp index 5ba91ae..547eb39 100644 --- a/src/remoteauth/remoteauthdialog.cpp +++ b/src/remoteauth/remoteauthdialog.cpp @@ -91,7 +91,7 @@ void RemoteAuthDialog::OnFingerprint(const std::string &fingerprint) { m_image.property_pixbuf() = pb; } -void RemoteAuthDialog::OnPendingTicket(Snowflake user_id, std::string discriminator, std::string avatar_hash, std::string username) { +void RemoteAuthDialog::OnPendingTicket(Snowflake user_id, const std::string &discriminator, const std::string &avatar_hash, const std::string &username) { Glib::ustring name = username; if (discriminator != "0") { name += "#" + discriminator; @@ -119,6 +119,6 @@ void RemoteAuthDialog::OnToken(const std::string &token) { void RemoteAuthDialog::OnError(const std::string &error) { m_ra.Stop(); - Abaddon::Get().ShowConfirm(error, dynamic_cast(get_toplevel())); + Abaddon::Get().ShowConfirm(error, dynamic_cast(get_toplevel())); response(Gtk::RESPONSE_CANCEL); } diff --git a/src/remoteauth/remoteauthdialog.hpp b/src/remoteauth/remoteauthdialog.hpp index be559df..9a6ca29 100644 --- a/src/remoteauth/remoteauthdialog.hpp +++ b/src/remoteauth/remoteauthdialog.hpp @@ -20,7 +20,7 @@ private: void OnHello(); void OnFingerprint(const std::string &fingerprint); - void OnPendingTicket(Snowflake user_id, std::string discriminator, std::string avatar_hash, std::string username); + void OnPendingTicket(Snowflake user_id, const std::string &discriminator, const std::string &avatar_hash, const std::string &username); void OnPendingLogin(); void OnToken(const std::string &token); void OnError(const std::string &error); From 3c2fde661e0d8240fa03290c28e1475330ca15b2 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Sun, 9 Jul 2023 21:27:08 -0400 Subject: [PATCH 09/13] emit remoteauth error on unexpected close --- src/remoteauth/remoteauthclient.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/remoteauth/remoteauthclient.cpp b/src/remoteauth/remoteauthclient.cpp index 083bdaa..7f41d71 100644 --- a/src/remoteauth/remoteauthclient.cpp +++ b/src/remoteauth/remoteauthclient.cpp @@ -264,8 +264,14 @@ void RemoteAuthClient::OnWebsocketOpen() { void RemoteAuthClient::OnWebsocketClose(const ix::WebSocketCloseInfo &info) { if (info.remote) { m_log->debug("Websocket closed (remote): {} ({})", info.code, info.reason); + if (m_connected) { + m_signal_error.emit("Error. Websocket closed (remote): " + std::to_string(info.code) + " (" + info.reason + ")"); + } } else { m_log->debug("Websocket closed (local): {} ({})", info.code, info.reason); + if (m_connected) { + m_signal_error.emit("Error. Websocket closed (local): " + std::to_string(info.code) + " (" + info.reason + ")"); + } } } From 2ca5a21ab4a5c03ea8c8a584ca832d01d8661691 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Sun, 9 Jul 2023 21:49:15 -0400 Subject: [PATCH 10/13] restart remote auth on cancel opcode --- src/remoteauth/remoteauthclient.cpp | 7 +++++++ src/remoteauth/remoteauthclient.hpp | 1 + 2 files changed, 8 insertions(+) diff --git a/src/remoteauth/remoteauthclient.cpp b/src/remoteauth/remoteauthclient.cpp index 7f41d71..467ea8b 100644 --- a/src/remoteauth/remoteauthclient.cpp +++ b/src/remoteauth/remoteauthclient.cpp @@ -58,6 +58,8 @@ void RemoteAuthClient::OnGatewayMessage(const std::string &str) { HandleGatewayPendingTicket(j); } else if (opcode == "pending_login") { HandleGatewayPendingLogin(j); + } else if (opcode == "cancel") { + HandleGatewayCancel(j); } } @@ -125,6 +127,11 @@ void RemoteAuthClient::HandleGatewayPendingLogin(const nlohmann::json &j) { m_signal_pending_login.emit(); } +void RemoteAuthClient::HandleGatewayCancel(const nlohmann::json &j) { + Stop(); + Start(); +} + void RemoteAuthClient::OnRemoteAuthLoginResponse(const std::optional &encrypted_token, DiscordError err) { if (!encrypted_token.has_value()) { m_log->error("Remote auth login failed: {}", static_cast(err)); diff --git a/src/remoteauth/remoteauthclient.hpp b/src/remoteauth/remoteauthclient.hpp index 36f224a..c2e00f3 100644 --- a/src/remoteauth/remoteauthclient.hpp +++ b/src/remoteauth/remoteauthclient.hpp @@ -23,6 +23,7 @@ private: void HandleGatewayPendingRemoteInit(const nlohmann::json &j); void HandleGatewayPendingTicket(const nlohmann::json &j); void HandleGatewayPendingLogin(const nlohmann::json &j); + void HandleGatewayCancel(const nlohmann::json &j); void OnRemoteAuthLoginResponse(const std::optional &encrypted_token, DiscordError err); From b5d85f4958601ef512dfc9fe302b4b9bfcda25f1 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Sun, 9 Jul 2023 21:58:05 -0400 Subject: [PATCH 11/13] use another color for qr code cuz im special --- src/remoteauth/remoteauthdialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/remoteauth/remoteauthdialog.cpp b/src/remoteauth/remoteauthdialog.cpp index 547eb39..7ee045f 100644 --- a/src/remoteauth/remoteauthdialog.cpp +++ b/src/remoteauth/remoteauthdialog.cpp @@ -65,7 +65,7 @@ void RemoteAuthDialog::OnFingerprint(const std::string &fingerprint) { int size = qr.getSize(); const int border = 4; - const auto module_set = "0 0 0"; + const auto module_set = "192 0 255"; const auto module_clr = "255 255 255"; std::ostringstream sb; From 229555939d697700e988f09f04871bde55adc5a7 Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Sun, 9 Jul 2023 22:31:21 -0400 Subject: [PATCH 12/13] make qrcodegen optional wow!!! --- CMakeLists.txt | 12 ++++++++---- src/abaddon.cpp | 2 ++ src/remoteauth/remoteauthclient.cpp | 8 ++++++++ src/remoteauth/remoteauthclient.hpp | 9 +++++++++ src/remoteauth/remoteauthdialog.cpp | 8 ++++++++ src/remoteauth/remoteauthdialog.hpp | 9 +++++++++ src/windows/mainwindow.cpp | 6 ++++++ 7 files changed, 50 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b85e6c1..7a782ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ option(USE_LIBHANDY "Enable features that require libhandy (default)" ON) option(ENABLE_VOICE "Enable voice suppport" ON) option(USE_KEYCHAIN "Store the token in the keychain (default)" ON) option(ENABLE_NOTIFICATION_SOUNDS "Enable notification sounds (default)" ON) +option(ENABLE_QRCODE_LOGIN "Enable QR code login (default)" ON) find_package(nlohmann_json REQUIRED) find_package(CURL) @@ -61,11 +62,14 @@ target_include_directories(abaddon PUBLIC ${ZLIB_INCLUDE_DIRS}) target_include_directories(abaddon PUBLIC ${SQLite3_INCLUDE_DIRS}) target_include_directories(abaddon PUBLIC ${NLOHMANN_JSON_INCLUDE_DIRS}) -add_library(qrcodegen subprojects/qrcodegen/cpp/qrcodegen.hpp subprojects/qrcodegen/cpp/qrcodegen.cpp) -target_include_directories(qrcodegen PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/subprojects/qrcodegen/cpp") -target_link_libraries(abaddon qrcodegen) +if (ENABLE_QRCODE_LOGIN) + add_library(qrcodegen subprojects/qrcodegen/cpp/qrcodegen.hpp subprojects/qrcodegen/cpp/qrcodegen.cpp) + target_include_directories(qrcodegen PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/subprojects/qrcodegen/cpp") + target_link_libraries(abaddon qrcodegen) -target_include_directories(abaddon PUBLIC "subprojects/qrcodegen/cpp") + target_include_directories(abaddon PUBLIC "subprojects/qrcodegen/cpp") + target_compile_definitions(abaddon PRIVATE WITH_QRLOGIN) +endif () target_precompile_headers(abaddon PRIVATE src/abaddon.hpp src/util.hpp) diff --git a/src/abaddon.cpp b/src/abaddon.cpp index c161595..921549b 100644 --- a/src/abaddon.cpp +++ b/src/abaddon.cpp @@ -837,6 +837,7 @@ void Abaddon::ActionSetToken() { } void Abaddon::ActionLoginQR() { +#ifdef WITH_QRLOGIN RemoteAuthDialog dlg(*m_main_window); auto response = dlg.run(); if (response == Gtk::RESPONSE_OK) { @@ -847,6 +848,7 @@ void Abaddon::ActionLoginQR() { ActionConnect(); } m_main_window->UpdateMenus(); +#endif } void Abaddon::ActionChannelOpened(Snowflake id, bool expand_to) { diff --git a/src/remoteauth/remoteauthclient.cpp b/src/remoteauth/remoteauthclient.cpp index 467ea8b..7653b78 100644 --- a/src/remoteauth/remoteauthclient.cpp +++ b/src/remoteauth/remoteauthclient.cpp @@ -1,8 +1,14 @@ +#ifdef WITH_QRLOGIN + +// clang-format off + #include "remoteauthclient.hpp" #include "http.hpp" #include #include +// clang-format on + RemoteAuthClient::RemoteAuthClient() : m_ws("remote-auth-ws") , m_log(spdlog::get("remote-auth")) { @@ -341,3 +347,5 @@ RemoteAuthClient::type_signal_token RemoteAuthClient::signal_token() { RemoteAuthClient::type_signal_error RemoteAuthClient::signal_error() { return m_signal_error; } + +#endif diff --git a/src/remoteauth/remoteauthclient.hpp b/src/remoteauth/remoteauthclient.hpp index c2e00f3..6ab6dbb 100644 --- a/src/remoteauth/remoteauthclient.hpp +++ b/src/remoteauth/remoteauthclient.hpp @@ -1,4 +1,9 @@ #pragma once + +#ifdef WITH_QRLOGIN + +// clang-format off + #include #include #include @@ -6,6 +11,8 @@ #include "discord/waiter.hpp" #include "discord/websocket.hpp" +// clang-format on + class RemoteAuthClient { public: RemoteAuthClient(); @@ -84,3 +91,5 @@ private: type_signal_token m_signal_token; type_signal_error m_signal_error; }; + +#endif diff --git a/src/remoteauth/remoteauthdialog.cpp b/src/remoteauth/remoteauthdialog.cpp index 7ee045f..7975b4e 100644 --- a/src/remoteauth/remoteauthdialog.cpp +++ b/src/remoteauth/remoteauthdialog.cpp @@ -1,6 +1,12 @@ +#ifdef WITH_QRLOGIN + +// clang-format off + #include "remoteauthdialog.hpp" #include +// clang-format on + RemoteAuthDialog::RemoteAuthDialog(Gtk::Window &parent) : Gtk::Dialog("Login with QR Code", parent, true) , m_layout(Gtk::ORIENTATION_VERTICAL) @@ -122,3 +128,5 @@ void RemoteAuthDialog::OnError(const std::string &error) { Abaddon::Get().ShowConfirm(error, dynamic_cast(get_toplevel())); response(Gtk::RESPONSE_CANCEL); } + +#endif diff --git a/src/remoteauth/remoteauthdialog.hpp b/src/remoteauth/remoteauthdialog.hpp index 9a6ca29..465a188 100644 --- a/src/remoteauth/remoteauthdialog.hpp +++ b/src/remoteauth/remoteauthdialog.hpp @@ -1,7 +1,14 @@ #pragma once + +#ifdef WITH_QRLOGIN + +// clang-format off + #include #include "remoteauthclient.hpp" +// clang-format on + class RemoteAuthDialog : public Gtk::Dialog { public: RemoteAuthDialog(Gtk::Window &parent); @@ -27,3 +34,5 @@ private: std::string m_token; }; + +#endif diff --git a/src/windows/mainwindow.cpp b/src/windows/mainwindow.cpp index 56d735e..0b21567 100644 --- a/src/windows/mainwindow.cpp +++ b/src/windows/mainwindow.cpp @@ -206,7 +206,9 @@ void MainWindow::OnDiscordSubmenuPopup() { m_menu_discord_connect.set_sensitive(!token.empty() && !discord_active); m_menu_discord_disconnect.set_sensitive(discord_active); m_menu_discord_set_token.set_sensitive(!discord_active); +#ifdef WITH_QRLOGIN m_menu_discord_login_qr.set_sensitive(!discord_active); +#endif m_menu_discord_set_status.set_sensitive(discord_active); } @@ -249,6 +251,10 @@ void MainWindow::SetupMenu() { m_menu_discord_disconnect.set_sensitive(false); m_menu_discord_set_token.set_label("Set Token"); m_menu_discord_login_qr.set_label("Login with QR Code"); +#ifndef WITH_QRLOGIN + m_menu_discord_login_qr.set_sensitive(false); + m_menu_discord_login_qr.set_tooltip_text("Not compiled with support"); +#endif m_menu_discord_set_status.set_label("Set Status"); m_menu_discord_set_status.set_sensitive(false); m_menu_discord_add_recipient.set_label("Add user to DM"); From 5bf5cc75eb8cffb3aed44a6743f1bb87a7b2b00d Mon Sep 17 00:00:00 2001 From: ouwou <26526779+ouwou@users.noreply.github.com> Date: Tue, 11 Jul 2023 20:57:09 -0400 Subject: [PATCH 13/13] add qrcode to mindeps ci run --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 116a800..6cfb8b7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,7 +67,7 @@ jobs: with: cond: ${{ matrix.mindeps == true }} if_true: | - cmake -GNinja -Bbuild -DCMAKE_BUILD_TYPE=${{ matrix.buildtype }} -DUSE_LIBHANDY=OFF -DENABLE_VOICE=OFF -DENABLE_NOTIFICATION_SOUNDS=OFF + cmake -GNinja -Bbuild -DCMAKE_BUILD_TYPE=${{ matrix.buildtype }} -DUSE_LIBHANDY=OFF -DENABLE_VOICE=OFF -DENABLE_NOTIFICATION_SOUNDS=OFF -DENABLE_QRCODE_LOGIN=OFF cmake --build build if_false: | cmake -GNinja -Bbuild -DCMAKE_BUILD_TYPE=${{ matrix.buildtype }} -DCMAKE_CXX_FLAGS="-Wl,--default-image-base-low"