1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
#include "remoteauthdialog.hpp"
#include <qrcodegen.hpp>
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_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;
const auto qr = qrcodegen::QrCode::encodeText(url.c_str(), level);
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 << "P3\n";
sb << size + border * 2 << " " << size + border * 2 << " 255\n";
for (int y = -border; y < size + border; y++) {
for (int x = -border; x < size + border; x++) {
if (qr.getModule(x, y)) {
sb << module_set << "\n";
} else {
sb << module_clr << "\n";
}
}
}
const auto img = sb.str();
auto loader = Gdk::PixbufLoader::create();
loader->write(reinterpret_cast<const guint8 *>(img.data()), img.size());
loader->close();
const auto pb = loader->get_pixbuf()->scale_simple(256, 256, Gdk::INTERP_NEAREST);
m_image.property_pixbuf() = pb;
}
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<Gdk::Pixbuf> &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) {
m_token = token;
m_ra.Stop();
response(Gtk::RESPONSE_OK);
}
void RemoteAuthDialog::OnError(const std::string &error) {
m_ra.Stop();
Abaddon::Get().ShowConfirm(error, dynamic_cast<Gtk::Window*>(get_toplevel()));
response(Gtk::RESPONSE_CANCEL);
}
|