Skip to content
API Documentation
BastionGuard™
IdentityLeakPage.cpp
Go to the documentation of this file.
1/*
2 * BastionGuard™
3 * Copyright (C) 2025–2026 Calogero Scarnà
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 *
17 * BastionGuard™ is a trademark of Calogero Scarnà.
18 * The BastionGuard™ name and branding are not licensed under the GPL.
19 */
20
21#include "IdentityLeakPage.hpp"
22#include "../../Resource.hpp"
27#include <sigc++/sigc++.h>
28#include <glib/gi18n.h>
29
30
32: Gtk::Box(Gtk::Orientation::VERTICAL, 12),
33 parentWindow(parent)
34{
35 add_css_class("content");
36
37 auto title_box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
38 title_box->add_css_class("secure-headerbar");
39 title_box->set_margin_bottom(12);
40
41 auto spacer_left = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
42 spacer_left->set_hexpand(true);
43
44 auto title_label = Gtk::make_managed<Gtk::Label>(_("🛡️ Controllo violazioni identità"));
45 title_label->set_margin(8);
46 title_label->set_halign(Gtk::Align::CENTER);
47 title_label->add_css_class("secure-header-title");
48
49 auto spacer_right = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
50 spacer_right->set_hexpand(true);
51
52 title_box->append(*spacer_left);
53 title_box->append(*title_label);
54 title_box->append(*spacer_right);
55
56 append(*title_box);
57
58 auto emailBox = Gtk::make_managed<Gtk::Box>(
59 Gtk::Orientation::HORIZONTAL, 6);
60
61 emailEntry.set_placeholder_text(_("Inserisci email da monitorare"));
62 emailEntry.set_hexpand(true);
63
64 auto privacyBtn = Gtk::make_managed<Gtk::Button>(_("ℹ️ Privacy"));
65 privacyBtn->add_css_class("btn-secondary");
66
67 privacyBtn->signal_clicked().connect(
68 sigc::mem_fun(*this,
70
71 emailBox->append(emailEntry);
72 emailBox->append(*privacyBtn);
73
74 append(*emailBox);
75
76 auto hibpBox = Gtk::make_managed<Gtk::Box>(
77 Gtk::Orientation::HORIZONTAL, 6);
78
79 apiKeyEntry.set_placeholder_text(_("HIBP API Key"));
80 apiKeyEntry.set_visibility(false);
81 apiKeyEntry.set_hexpand(true);
83
84 apiToggleButton.set_label(_("Mostra"));
85
86 saveHibpButton.set_label(_("Salva"));
87
88 apiToggleButton.signal_clicked().connect(
89 sigc::mem_fun(*this,
91
92 saveHibpButton.signal_clicked().connect(
93 sigc::mem_fun(*this,
95
96 hibpBox->append(apiKeyEntry);
97 hibpBox->append(apiToggleButton);
98 hibpBox->append(saveHibpButton);
99 append(*hibpBox);
100
101 auto leakCheckBox = Gtk::make_managed<Gtk::Box>(
102 Gtk::Orientation::HORIZONTAL, 6);
103
104 leakCheckApiEntry.set_placeholder_text(_("LeakCheck API Key"));
105 leakCheckApiEntry.set_visibility(false);
106 leakCheckApiEntry.set_hexpand(true);
107 leakCheckApiEntry.set_text(
109
110 leakCheckToggleButton.set_label(_("Mostra"));
111
112 saveLeakCheckButton.set_label(_("Salva"));
113 auto leakCheckInfoBtn =
114 Gtk::make_managed<Gtk::Button>(_("⚠️ Info"));
115 leakCheckInfoBtn->add_css_class("btn-secondary");
116
117 leakCheckInfoBtn->signal_clicked().connect(
118 sigc::mem_fun(*this,
120
121 leakCheckToggleButton.signal_clicked().connect(
122 sigc::mem_fun(*this,
124
125 saveLeakCheckButton.signal_clicked().connect(
126 sigc::mem_fun(*this,
128
129 leakCheckBox->append(leakCheckApiEntry);
130 leakCheckBox->append(leakCheckToggleButton);
131 leakCheckBox->append(saveLeakCheckButton);
132 leakCheckBox->append(*leakCheckInfoBtn);
133 append(*leakCheckBox);
134
135 auto pwdBox = Gtk::make_managed<Gtk::Box>(
136 Gtk::Orientation::HORIZONTAL, 6);
137
138 passwordEntry.set_placeholder_text(_("Verifica password compromessa"));
139 passwordEntry.set_visibility(false);
140 passwordEntry.set_hexpand(true);
141
142 checkPasswordButton.set_label(_("Verifica password"));
143 checkPasswordButton.add_css_class("btn-primary");
144
145 checkPasswordButton.signal_clicked().connect(
146 sigc::mem_fun(*this,
148
149 pwdBox->append(passwordEntry);
150 pwdBox->append(checkPasswordButton);
151 append(*pwdBox);
152
153 monitorToggle.set_label(_("Monitor automatico (ogni 24h)"));
154 monitorToggle.set_active(
156
157 monitorToggle.signal_toggled().connect(
158 sigc::mem_fun(*this,
160
161 append(monitorToggle);
162
163
164 checkButton.set_label(_("Verifica violazioni"));
165 checkButton.add_css_class("btn-primary");
166 checkButton.signal_clicked()
167 .connect(sigc::mem_fun(*this,
169 append(checkButton);
170
171 logBuffer = Gtk::TextBuffer::create();
172 logView.set_buffer(logBuffer);
173 logView.set_editable(false);
174 logView.add_css_class("log-card");
175
176 logScroller.set_child(logView);
177 logScroller.set_policy(
178 Gtk::PolicyType::AUTOMATIC,
179 Gtk::PolicyType::AUTOMATIC);
180 logScroller.set_hexpand(true);
181 logScroller.set_vexpand(true);
182
183 append(logScroller);
184}
185
187{
188 if (alive_) alive_->store(false, std::memory_order_release);
189
190 stopMonitor();
191
192 running = false;
193 if (workerThread.joinable())
194 workerThread.join();
195}
196
197
198
199void IdentityLeakPage::appendLog(const Glib::ustring& text)
200{
201 auto alive = alive_;
202 Glib::signal_idle().connect_once(
203 [this, alive, text]() {
204 BastionGuard::if_alive(alive, [this, &text]() {
205 logBuffer->insert(
206 logBuffer->end(),
207 text + "\n");
208 });
209 });
210}
211
212
213
215{
216 bool visible = apiKeyEntry.get_visibility();
217 apiKeyEntry.set_visibility(!visible);
218 apiToggleButton.set_label(
219 visible ? _("Mostra") : _("Nascondi"));
220}
221
223{
224 bool visible = leakCheckApiEntry.get_visibility();
225 leakCheckApiEntry.set_visibility(!visible);
226 leakCheckToggleButton.set_label(
227 visible ? _("Mostra") : _("Nascondi"));
228}
229
230
232{
233 const std::string key = apiKeyEntry.get_text();
235
236 appendLog(
237 key.empty()
238 ? _("⚠ HIBP API rimossa")
239 : _("✔ HIBP API salvata"));
240}
241
243{
244 const std::string key = leakCheckApiEntry.get_text();
246
247 appendLog(
248 key.empty()
249 ? _("⚠ LeakCheck API rimossa")
250 : _("✔ LeakCheck API salvata"));
251}
252
253
254void IdentityLeakPage::startMonitor(const std::string& email)
255{
256 if (monitorActive)
257 return;
258
259 appendLog(_("Monitor automatico attivato (24h)"));
260
261 monitor = std::make_unique<LeakMonitor>(
262 email,
263 [this](const std::string& msg) {
264 appendLog("[MONITOR] " + msg);
265 });
266
267 monitor->start();
268 monitorActive = true;
269}
270
272{
273 if (!monitorActive)
274 return;
275
276 monitor->stop();
277 monitor.reset();
278 monitorActive = false;
279}
280
281
282
284{
285 bool enabled = monitorToggle.get_active();
287
288 if (!enabled) {
289 appendLog(_("Monitor automatico disattivato"));
290 stopMonitor();
291 } else {
292 appendLog(_("Monitor automatico abilitato"));
293 }
294}
295
296
297
299{
300 if (running)
301 return;
302
303 const std::string email = emailEntry.get_text();
304 if (email.empty())
305 return;
306
307 // A std::thread remains joinable even after its function has returned.
308 // Reassigning a joinable std::thread calls std::terminate().
309 // running == false means the previous check has finished, so reap it
310 // before creating the next worker.
311 if (workerThread.joinable())
312 workerThread.join();
313
314 stopMonitor();
315
316 running = true;
317 logBuffer->set_text("");
318 appendLog(_("Avvio controllo violazioni…"));
319
320 try {
321 workerThread = std::thread(
322 [this, email]() {
323 try {
324 LeakCheckWorker worker(
325 email,
326 [this](const std::string& msg) {
327 appendLog(msg);
328 });
329
330 worker.run();
331
332 appendLog(_("Controllo completato."));
333
335 startMonitor(email);
336 }
337 catch (const std::exception& ex) {
338 appendLog(
339 std::string(_("❌ Errore controllo violazioni: ")) +
340 ex.what());
341 }
342 catch (...) {
343 appendLog(_("❌ Errore inatteso durante il controllo violazioni"));
344 }
345
346 running = false;
347 });
348 }
349 catch (const std::exception& ex) {
350 running = false;
351 appendLog(
352 std::string(_("❌ Impossibile avviare il controllo: ")) +
353 ex.what());
354 }
355}
356
358{
359 const std::string password =
360 passwordEntry.get_text();
361
362 if (password.empty())
363 return;
364
365 appendLog(_("🔐 Controllo password (HIBP)…"));
366
367 try {
368 auto result =
370
371 if (result.compromised) {
372 appendLog(
373 _("❌ Password compromessa – trovata in ") +
374 std::to_string(result.count) +
375 _(" violazioni"));
376 appendLog(
377 _(" → Cambiala immediatamente"));
378 } else {
379 appendLog(
380 _("✔ Password NON presente in database noti"));
381 }
382 }
383 catch (const std::exception& ex) {
384 appendLog(
385 std::string(_("❌ Errore controllo password: ")) +
386 ex.what());
387 }
388
389 passwordEntry.set_text("");
390}
391
393{
394 auto *dialog = new Gtk::Window();
395 dialog->set_title("");
396 dialog->set_default_size(520, 260);
397 dialog->set_resizable(false);
398 dialog->set_modal(false);
399 dialog->add_css_class("app-dialog");
400 dialog->add_css_class("main-window");
401 dialog->add_css_class("app-window");
402
403 auto *header = Gtk::make_managed<Gtk::HeaderBar>();
404 header->set_show_title_buttons(false);
405 header->set_decoration_layout("");
406 header->add_css_class("custom-headerbar");
407
408 auto *close_box = Gtk::make_managed<Gtk::Box>(
409 Gtk::Orientation::HORIZONTAL);
410 close_box->set_margin_end(6);
411
412 try {
413 auto icon = Gdk::Pixbuf::create_from_file(
414 resource("icon-close.png"), 28, 28, true);
415 auto *btn = Gtk::make_managed<Gtk::Button>();
416 btn->set_child(
417 *Gtk::make_managed<Gtk::Image>(icon));
418 btn->add_css_class("header-button");
419 btn->signal_clicked().connect(
420 [dialog]() { dialog->close(); delete dialog; });
421 close_box->append(*btn);
422 } catch (...) {}
423
424 header->pack_end(*close_box);
425
426 auto *header_wrap =
427 Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL);
428 header_wrap->append(*header);
429 dialog->set_titlebar(*header_wrap);
430
431 auto *vbox = Gtk::make_managed<Gtk::Box>(
432 Gtk::Orientation::VERTICAL, 14);
433 vbox->set_margin(24);
434
435 auto title = Gtk::make_managed<Gtk::Label>(
436 _("🔐 Tutela della privacy"));
437 title->add_css_class("title");
438 title->set_halign(Gtk::Align::CENTER);
439
440 auto msg = Gtk::make_managed<Gtk::Label>(_(
441 "Nessun dato personale viene inviato o memorizzato "
442 "da BastionGuard.\n\n"
443 "• Le email vengono verificate tramite API ufficiali\n"
444 "• Le password NON vengono mai trasmesse\n"
445 "• Il controllo password usa solo hash parziali (HIBP Range)\n"
446 "• Nessuna informazione viene salvata su server esterni\n\n"
447 "Il controllo avviene nel rispetto totale della privacy."));
448 msg->set_wrap(true);
449 msg->set_justify(Gtk::Justification::CENTER);
450 msg->set_halign(Gtk::Align::CENTER);
451
452 auto closeBtn = Gtk::make_managed<Gtk::Button>(_("Ho capito"));
453 closeBtn->add_css_class("btn-success");
454 closeBtn->set_halign(Gtk::Align::CENTER);
455 closeBtn->signal_clicked().connect(
456 [dialog]() { dialog->close(); delete dialog; });
457
458 vbox->append(*title);
459 vbox->append(*msg);
460 vbox->append(*closeBtn);
461
462 dialog->set_child(*vbox);
463 dialog->present();
464}
465
467{
468 auto *dialog = new Gtk::Window();
469 dialog->set_title("");
470 dialog->set_default_size(540, 280);
471 dialog->set_resizable(false);
472 dialog->set_modal(false);
473 dialog->add_css_class("app-dialog");
474 dialog->add_css_class("main-window");
475 dialog->add_css_class("app-window");
476
477 auto *header = Gtk::make_managed<Gtk::HeaderBar>();
478 header->set_show_title_buttons(false);
479 header->set_decoration_layout("");
480 header->add_css_class("custom-headerbar");
481
482 auto *close_box = Gtk::make_managed<Gtk::Box>(
483 Gtk::Orientation::HORIZONTAL);
484 close_box->set_margin_end(6);
485
486 try {
487 auto icon = Gdk::Pixbuf::create_from_file(
488 resource("icon-close.png"), 28, 28, true);
489 auto *btn = Gtk::make_managed<Gtk::Button>();
490 btn->set_child(
491 *Gtk::make_managed<Gtk::Image>(icon));
492 btn->add_css_class("header-button");
493 btn->signal_clicked().connect(
494 [dialog]() { dialog->close(); delete dialog; });
495 close_box->append(*btn);
496 } catch (...) {}
497
498 header->pack_end(*close_box);
499
500 auto *wrap =
501 Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL);
502 wrap->append(*header);
503 dialog->set_titlebar(*wrap);
504
505 auto *vbox = Gtk::make_managed<Gtk::Box>(
506 Gtk::Orientation::VERTICAL, 14);
507 vbox->set_margin(24);
508
509 auto title = Gtk::make_managed<Gtk::Label>(
510 _("⚠️ Avviso su LeakCheck"));
511 title->add_css_class("title");
512 title->set_halign(Gtk::Align::CENTER);
513
514 auto msg = Gtk::make_managed<Gtk::Label>(_(
515 "Le API utilizzate da BastionGuard per LeakCheck "
516 "NON sono le stesse impiegate dal sito web ufficiale.\n\n"
517 "• BastionGuard non è affiliato a LeakCheck\n"
518 "• Non esiste alcuna partnership o approvazione implicita\n"
519 "• I risultati possono differire da quelli mostrati sul sito\n\n"
520 "L’utente è responsabile dell’utilizzo delle API."));
521 msg->set_wrap(true);
522 msg->set_justify(Gtk::Justification::CENTER);
523 msg->set_halign(Gtk::Align::CENTER);
524
525 auto closeBtn =
526 Gtk::make_managed<Gtk::Button>(_("Ho capito"));
527 closeBtn->add_css_class("btn-success");
528 closeBtn->set_halign(Gtk::Align::CENTER);
529 closeBtn->signal_clicked().connect(
530 [dialog]() { dialog->close(); delete dialog; });
531
532 vbox->append(*title);
533 vbox->append(*msg);
534 vbox->append(*closeBtn);
535
536 dialog->set_child(*vbox);
537 dialog->present();
538}
#define _(s)
static PasswordLeakResult checkPassword(const std::string &password)
static std::string hibpApiKey()
static void setLeakCheckApiKey(const std::string &)
static std::string leakCheckApiKey()
static void setHibpApiKey(const std::string &)
static void setMonitorEnabled(bool)
Gtk::CheckButton monitorToggle
IdentityLeakPage(Gtk::Window &parent)
Gtk::Window & parentWindow
Gtk::Button leakCheckToggleButton
Glib::RefPtr< Gtk::TextBuffer > logBuffer
Gtk::Button apiToggleButton
void startMonitor(const std::string &email)
void onToggleLeakCheckApiVisibility()
Gtk::Button checkPasswordButton
Gtk::Entry leakCheckApiEntry
void appendLog(const Glib::ustring &text)
std::atomic< bool > running
Gtk::TextView logView
std::unique_ptr< LeakMonitor > monitor
Gtk::Button saveLeakCheckButton
std::atomic< bool > monitorActive
Gtk::Button saveHibpButton
BastionGuard::AliveFlag alive_
Gtk::ScrolledWindow logScroller
void if_alive(const AliveFlag &alive, Fn &&fn)
std::string resource(const std::string &rel)
Definition Resource.hpp:31