31#include <unordered_set>
32#include <system_error>
33#include <nlohmann/json.hpp>
34#include <glibmm/i18n.h>
35#include <glibmm/miscutils.h>
36#include <glibmm/datetime.h>
44using json = nlohmann::json;
45namespace fs = std::filesystem;
50 auto l = s.find_first_not_of(
" \t\r\n");
51 auto r = s.find_last_not_of(
" \t\r\n");
52 if (l == std::string::npos)
return {};
53 return s.substr(l, r - l + 1);
58 if (domain.empty())
return {};
60 auto pos = domain.find(
"://");
61 if (pos != std::string::npos)
62 domain.erase(0, pos + 3);
64 pos = domain.find(
'/');
65 if (pos != std::string::npos)
68 pos = domain.find(
'@');
69 if (pos != std::string::npos)
70 domain = domain.substr(pos + 1);
72 pos = domain.find(
':');
73 if (pos != std::string::npos)
76 if (domain.rfind(
"www.", 0) == 0)
79 std::transform(domain.begin(), domain.end(), domain.begin(),
80 [](
unsigned char c) { return static_cast<char>(std::tolower(c)); });
82 if (!domain.empty() && domain.front() ==
'.')
83 domain.erase(domain.begin());
85 while (!domain.empty() && domain.back() ==
'.')
90 if (domain.empty())
return {};
91 if (domain.find(
'.') == std::string::npos)
return {};
97 auto now = Glib::DateTime::create_now_local();
98 return now.format(
"%Y-%m-%d");
102 int rc = (
bg_process::run({
"/usr/libexec/bastionguard/bastionguard-service",
"--user",
"restart",
"BastionGuard-cef.service"}).exit_code);
104 std::cout <<
_(
"[BankPage] ✔ BastionGuard-cef.service riavviato\n");
106 std::cerr <<
_(
"[BankPage] ⚠ Impossibile riavviare BastionGuard-cef.service\n");
113 if (
alive_)
alive_->store(
false, std::memory_order_release);
119 : Gtk::Box(Gtk::Orientation::VERTICAL, 12)
124 auto title_box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
125 title_box->add_css_class(
"secure-headerbar");
126 title_box->set_margin_bottom(8);
128 auto spacer_left = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
129 spacer_left->set_hexpand(
true);
131 auto title_label = Gtk::make_managed<Gtk::Label>(
_(
"🏦 Zona Sicura – Banche"));
132 title_label->set_margin(8);
133 title_label->set_halign(Gtk::Align::CENTER);
134 title_label->add_css_class(
"secure-header-title");
136 auto spacer_right = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
137 spacer_right->set_hexpand(
true);
139 title_box->append(*spacer_left);
140 title_box->append(*title_label);
141 title_box->append(*spacer_right);
145 entry_url_.set_placeholder_text(
_(
"URL sorgente (es. MISP bank-website/list.json)"));
146 entry_url_.set_text(
"https://raw.githubusercontent.com/MISP/misp-warninglists/main/lists/bank-website/list.json");
154 auto toggle_box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 6);
159 toggle_box->set_margin_bottom(12);
162 auto whitelist_box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 6);
168 append(*whitelist_box);
175 auto open_bank_box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 6);
176 open_bank_entry_.set_placeholder_text(
_(
"Apri URL banca (es. https://www.intesasanpaolo.com)..."));
181 open_bank_box->set_margin_top(4);
182 open_bank_box->set_margin_bottom(8);
183 append(*open_bank_box);
188 txt_banks_.set_wrap_mode(Gtk::WrapMode::WORD_CHAR);
218 lbl_status_.set_text(
_(
"🔁 Aggiornamento automatico abilitato."));
221 lbl_status_.set_text(
_(
"⏸️ Aggiornamento automatico disattivato."));
228 lbl_status_.set_text(
_(
"🔁 Aggiornamento automatico attivo."));
238 std::thread([
this, alive =
alive_, url]() {
240 if (!alive->load())
return;
242 Glib::signal_idle().connect_once([
this, alive, ok, url]() {
250 lbl_status_.set_text(
_(
"✅ Lista aggiornata da MISP, whitelist integrata e servizio CEF riavviato."));
252 lbl_status_.set_text(
_(
"⚠ Lista aggiornata da MISP, ma merge whitelist fallito."));
255 lbl_status_.set_text(
_(
"❌ Errore nel download della lista."));
264 const std::string helper =
"/usr/bin/bank-helper";
265 const std::string userPath = Glib::get_home_dir() +
"/.local/share/BastionGuard/banks.json";
271 const pid_t pid = ::fork();
273 std::cerr <<
_(
"[BankPage] ⚠️ fork fallita per bank-helper") << std::endl;
279 int devnull = ::open(
"/dev/null", O_WRONLY);
281 ::dup2(devnull, STDOUT_FILENO);
282 ::dup2(devnull, STDERR_FILENO);
289 const char* argv[] = {
296 ::execvp(
"sudo",
const_cast<char* const*
>(argv));
302 if (::waitpid(pid, &status, 0) < 0) {
303 std::cerr <<
_(
"[BankPage] ⚠️ waitpid fallita per bank-helper") << std::endl;
307 const int rc = WIFEXITED(status) ? WEXITSTATUS(status)
308 : (WIFSIGNALED(status) ? 128 + WTERMSIG(status) : -1);
310 std::cout <<
_(
"[BankPage] ✅ Lista copiata in /usr/share/BastionGuard/data/bank/banks.json\n");
312 std::cerr <<
_(
"[BankPage] ⚠️ bank-helper fallito (rc=") << rc
313 <<
_(
"). Verificare /etc/sudoers.d/bastionguard-bank e i permessi di bank-helper.\n");
318 std::string path = Glib::get_home_dir() +
"/.local/share/BastionGuard/banks.json";
320 if (!fs::exists(path)) {
321 txt_banks_.get_buffer()->set_text(
_(
"⚠ Nessuna lista trovata.\n"));
325 std::ifstream f(path);
330 txt_banks_.get_buffer()->set_text(
_(
"❌ Errore parsing JSON.\n"));
334 std::ostringstream oss_banks;
335 oss_banks <<
_(
"🌍 Elenco banche conosciute:\n\n");
337 std::vector<std::string> domains;
338 if (j.contains(
"list") && j[
"list"].is_array()) {
339 for (
auto& item : j[
"list"]) {
340 if (item.is_string()) {
341 domains.push_back(item.get<std::string>());
342 }
else if (item.is_object() && item.contains(
"value") && item[
"value"].is_string()) {
343 domains.push_back(item[
"value"].get<std::string>());
348 std::sort(domains.begin(), domains.end());
349 domains.erase(std::unique(domains.begin(), domains.end()), domains.end());
352 for (
auto& domain : domains) {
353 std::string clean = normalize_domain(domain);
354 if (clean.empty())
continue;
357 oss_banks <<
" - " << clean <<
"\n";
362 oss_banks <<
_(
"\nTotale banche: ") << count <<
"\n";
363 txt_banks_.get_buffer()->set_text(oss_banks.str());
365 std::ostringstream oss_white;
366 oss_white <<
_(
"🛡️ Nuovi domini bancari:\n\n");
368 oss_white <<
" + " << w <<
"\n";
371 oss_white <<
_(
"(Nessun dominio whitelistato)\n");
379 if (domain.empty()) {
380 lbl_status_.set_text(
_(
"⚠ Inserisci un dominio valido."));
392 lbl_status_.set_text(
_(
"✅ Dominio aggiunto alla whitelist, integrato in banks.json e servizio CEF riavviato."));
394 lbl_status_.set_text(
_(
"⚠ Dominio salvato in whitelist, ma merge su banks.json fallito."));
398 lbl_status_.set_text(
_(
"ℹ️ Dominio già presente nella whitelist."));
407 std::string path = Glib::get_home_dir() +
"/.config/BastionGuard/whitelist.json";
408 if (!fs::exists(path))
411 std::ifstream f(path);
422 if (j.contains(
"list") && j[
"list"].is_array()) {
423 for (
const auto& w : j[
"list"]) {
424 if (!w.is_string())
continue;
426 std::string d = normalize_domain(w.get<std::string>());
437 std::string dir = Glib::get_home_dir() +
"/.config/BastionGuard";
438 fs::create_directories(dir);
441 for (
auto& d : clean)
442 d = normalize_domain(d);
444 clean.erase(std::remove_if(clean.begin(), clean.end(),
445 [](
const std::string& s) { return s.empty(); }),
448 std::sort(clean.begin(), clean.end());
449 clean.erase(std::unique(clean.begin(), clean.end()), clean.end());
453 j[
"updated_at"] = iso_today_yyyy_mm_dd();
455 j[
"list"] = json::array();
457 for (
const auto& d : clean)
458 j[
"list"].push_back(d);
460 std::ofstream f(dir +
"/whitelist.json", std::ios::trunc);
468 const std::string path = Glib::get_home_dir() +
"/.local/share/BastionGuard/banks.json";
472 if (fs::exists(path)) {
474 std::ifstream in(path);
485 if (!j.contains(
"list") || !j[
"list"].is_array())
486 j[
"list"] = json::array();
488 std::unordered_set<std::string> existing;
489 bool changed =
false;
491 for (
const auto& item : j[
"list"]) {
492 if (item.is_string()) {
493 std::string d = normalize_domain(item.get<std::string>());
496 }
else if (item.is_object() && item.contains(
"value") && item[
"value"].is_string()) {
497 std::string d = normalize_domain(item[
"value"].get<std::string>());
504 std::string d = normalize_domain(w);
505 if (d.empty())
continue;
507 if (!existing.count(d)) {
508 j[
"list"].push_back(
"." + d);
515 std::cout <<
_(
"[BankPage] ℹ Nessun merge necessario in banks.json\n");
519 const std::string tmp = path +
".tmp";
521 std::ofstream out(tmp, std::ios::trunc);
530 fs::rename(tmp, path, ec);
532 fs::remove(path, ec);
534 fs::rename(tmp, path, ec);
541 std::cout <<
_(
"[BankPage] ✔ whitelist integrata in banks.json\n");
542 restart_cef_service_user();
546 try { fs::remove(tmp); }
catch (...) {}
560 std::cout <<
_(
"[BankPage] ⏱️ Auto-update attivo ogni 6 ore.") << std::endl;
567 std::cout <<
_(
"[BankPage] ⏹️ Auto-update disattivato.") << std::endl;
573 std::thread([
this, alive =
alive_, url]() {
574 std::cout <<
_(
"[BankPage] ⏰ Aggiornamento automatico lista banche...") << std::endl;
576 if (!alive->load())
return;
578 Glib::signal_idle().connect_once([
this, alive, ok, url]() {
587 auto now = Glib::DateTime::create_now_local();
588 lbl_status_.set_text(
_(
"✅ Lista aggiornata automaticamente il: ") + now.format(
"%Y-%m-%d %H:%M:%S"));
590 lbl_status_.set_text(
_(
"⚠️ Lista aggiornata, ma merge whitelist fallito."));
593 lbl_status_.set_text(
_(
"⚠️ Errore nell'aggiornamento automatico."));
603 std::string path = Glib::get_home_dir() +
"/.config/BastionGuard/settings.json";
604 if (!fs::exists(path))
607 std::ifstream f(path);
618 if (j.contains(
"auto_update"))
625 std::string dir = Glib::get_home_dir() +
"/.config/BastionGuard";
626 fs::create_directories(dir);
628 std::ofstream f(dir +
"/settings.json", std::ios::trunc);
650 lbl_status_.set_text(
_(
"⚠ Inserisci un URL o un dominio bancario."));
655 if (raw.find(
"://") == std::string::npos)
656 raw =
"https://" + raw;
659 lbl_status_.set_text(
_(
"🔐 Apertura sandbox sicura in corso: ") + raw);
661 lbl_status_.set_text(
_(
"❌ Apertura sandbox fallita. Vedi log."));
666 if (url.empty())
return false;
672 if (c ==
'\n' || c ==
'\r' || c ==
'\0') {
673 std::cerr <<
_(
"[BankPage] URL contiene caratteri non validi.") << std::endl;
681 static const char* candidates[] = {
682 "/usr/bin/BastionGuard-secure-cli",
683 "/usr/local/bin/BastionGuard-secure-cli",
684 "/usr/bin/BastionGuard-secure",
685 "/usr/local/bin/BastionGuard-secure",
689 const char* bin =
nullptr;
690 for (
int i = 0; candidates[i]; ++i) {
691 if (::access(candidates[i], X_OK) == 0) {
697 std::cerr <<
_(
"[BankPage] BastionGuard-secure-cli non trovato; "
698 "installa il pacchetto BastionGuard completo.") << std::endl;
702 std::cout <<
_(
"[BankPage] 🔐 Avvio sandbox sicura via ") << bin
703 <<
_(
" per URL: ") << url << std::endl;
705 const pid_t pid = ::fork();
707 std::cerr <<
_(
"[BankPage] fork() fallita per BastionGuard-secure-cli")
719 int devnull = ::open(
"/dev/null", O_WRONLY);
721 ::dup2(devnull, STDOUT_FILENO);
722 ::dup2(devnull, STDERR_FILENO);
726 const char* argv[] = { bin, url.c_str(),
nullptr };
727 ::execvp(bin,
const_cast<char* const*
>(argv));
static Backend & instance()
bool updateBankListFromURL(const std::string &source_url)
void on_add_whitelist_clicked()
void copy_banklist_to_system(const std::string &source_url)
Gtk::Entry whitelist_entry_
sigc::connection auto_update_conn_
Gtk::Entry open_bank_entry_
Gtk::ScrolledWindow scroller_white_
BastionGuard::AliveFlag alive_
void on_open_bank_clicked()
bool auto_update_bank_list()
Gtk::Label auto_update_label_
void stop_auto_update_timer()
std::vector< std::string > whitelist_
void start_auto_update_timer()
Gtk::Switch auto_update_switch_
void on_refresh_clicked()
void populate_bank_list()
Gtk::Button btn_add_whitelist_
bool auto_update_enabled_
Gtk::Button btn_open_bank_
bool merge_whitelist_into_banks_json()
Gtk::ScrolledWindow scroller_banks_
Gtk::TextView txt_whitelist_
bool open_bank_in_sandbox(const std::string &url)
void if_alive(const AliveFlag &alive, Fn &&fn)
static void restart_cef_service_user()
static std::string iso_today_yyyy_mm_dd()
static std::string normalize_domain(std::string domain)
static std::string trim_copy(std::string s)
Result run(const std::vector< std::string > &args, bool merge_stderr=false, std::size_t limit=8U *1024U *1024U, const std::map< std::string, std::string > &environment={}, const std::function< bool(const std::string &)> &on_line={})