Skip to content
API Documentation
BastionGuard™
BankPage.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 "SafeProcess.hpp"
22#include "BankPage.hpp"
23#include "Backend.hpp"
24#include <thread>
25#include <fstream>
26#include <filesystem>
27#include <sstream>
28#include <iostream>
29#include <algorithm>
30#include <cctype>
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>
37#include <cstdlib>
38
39#include <sys/types.h>
40#include <sys/wait.h>
41#include <unistd.h>
42#include <fcntl.h>
43
44using json = nlohmann::json;
45namespace fs = std::filesystem;
46
47namespace {
48
49static std::string trim_copy(std::string s) {
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);
54}
55
56static std::string normalize_domain(std::string domain) {
57 domain = trim_copy(domain);
58 if (domain.empty()) return {};
59
60 auto pos = domain.find("://");
61 if (pos != std::string::npos)
62 domain.erase(0, pos + 3);
63
64 pos = domain.find('/');
65 if (pos != std::string::npos)
66 domain.erase(pos);
67
68 pos = domain.find('@');
69 if (pos != std::string::npos)
70 domain = domain.substr(pos + 1);
71
72 pos = domain.find(':');
73 if (pos != std::string::npos)
74 domain.erase(pos);
75
76 if (domain.rfind("www.", 0) == 0)
77 domain.erase(0, 4);
78
79 std::transform(domain.begin(), domain.end(), domain.begin(),
80 [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
81
82 if (!domain.empty() && domain.front() == '.')
83 domain.erase(domain.begin());
84
85 while (!domain.empty() && domain.back() == '.')
86 domain.pop_back();
87
88 domain = trim_copy(domain);
89
90 if (domain.empty()) return {};
91 if (domain.find('.') == std::string::npos) return {};
92
93 return domain;
94}
95
96static std::string iso_today_yyyy_mm_dd() {
97 auto now = Glib::DateTime::create_now_local();
98 return now.format("%Y-%m-%d");
99}
100
102 int rc = (bg_process::run({"/usr/libexec/bastionguard/bastionguard-service", "--user", "restart", "BastionGuard-cef.service"}).exit_code);
103 if (rc == 0)
104 std::cout << _("[BankPage] ✔ BastionGuard-cef.service riavviato\n");
105 else
106 std::cerr << _("[BankPage] ⚠ Impossibile riavviare BastionGuard-cef.service\n");
107}
108
109} // namespace
110
112{
113 if (alive_) alive_->store(false, std::memory_order_release);
114 if (auto_update_conn_.connected()) auto_update_conn_.disconnect();
115}
116
117
119 : Gtk::Box(Gtk::Orientation::VERTICAL, 12)
120{
121 set_margin(16);
122 set_vexpand(true);
123
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);
127
128 auto spacer_left = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
129 spacer_left->set_hexpand(true);
130
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");
135
136 auto spacer_right = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
137 spacer_right->set_hexpand(true);
138
139 title_box->append(*spacer_left);
140 title_box->append(*title_label);
141 title_box->append(*spacer_right);
142
143 append(*title_box);
144
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");
147 entry_url_.set_margin_bottom(6);
148 append(entry_url_);
149
150 btn_refresh_.set_label(_("🔄 Aggiorna lista"));
151 btn_refresh_.set_margin_bottom(12);
152 append(btn_refresh_);
153
154 auto toggle_box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 6);
155 auto_update_switch_.set_active(false);
156 auto_update_label_.set_text(_("Aggiornamento automatico ogni 6 ore"));
157 toggle_box->append(auto_update_switch_);
158 toggle_box->append(auto_update_label_);
159 toggle_box->set_margin_bottom(12);
160 append(*toggle_box);
161
162 auto whitelist_box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 6);
163 whitelist_entry_.set_placeholder_text(_("Aggiungi dominio alla whitelist..."));
164 whitelist_entry_.set_hexpand(true);
165 btn_add_whitelist_.set_label(_("➕ Aggiungi"));
166 whitelist_box->append(whitelist_entry_);
167 whitelist_box->append(btn_add_whitelist_);
168 append(*whitelist_box);
169
170 // -------------------------------------------------------------------------
171 // Apertura banca dentro sandbox sicura (BastionGuard-secure + bubblewrap).
172 // L'URL viene passato a /usr/bin/BastionGuard-secure-cli, che a sua volta
173 // si re-exec dentro bwrap (Wayland o X11) prima di aprire CEF.
174 // -------------------------------------------------------------------------
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)..."));
177 open_bank_entry_.set_hexpand(true);
178 btn_open_bank_.set_label(_("🔐 Apri in sandbox sicura"));
179 open_bank_box->append(open_bank_entry_);
180 open_bank_box->append(btn_open_bank_);
181 open_bank_box->set_margin_top(4);
182 open_bank_box->set_margin_bottom(8);
183 append(*open_bank_box);
184
185 scroller_banks_.set_vexpand(true);
186 scroller_banks_.set_hexpand(true);
187 txt_banks_.set_editable(false);
188 txt_banks_.set_wrap_mode(Gtk::WrapMode::WORD_CHAR);
189 scroller_banks_.set_child(txt_banks_);
190 append(scroller_banks_);
191
192 scroller_white_.set_vexpand(true);
193 scroller_white_.set_hexpand(true);
194 txt_whitelist_.set_editable(false);
195 txt_whitelist_.set_wrap_mode(Gtk::WrapMode::WORD_CHAR);
197 append(scroller_white_);
198
199 lbl_status_.set_margin_top(8);
200 append(lbl_status_);
201
206
207 btn_refresh_.signal_clicked().connect(sigc::mem_fun(*this, &BankPage::on_refresh_clicked));
208 btn_add_whitelist_.signal_clicked().connect(sigc::mem_fun(*this, &BankPage::on_add_whitelist_clicked));
209 btn_open_bank_.signal_clicked().connect(sigc::mem_fun(*this, &BankPage::on_open_bank_clicked));
210 open_bank_entry_.signal_activate().connect(sigc::mem_fun(*this, &BankPage::on_open_bank_clicked));
211
212 auto_update_switch_.property_active().signal_changed().connect([this]() {
215
218 lbl_status_.set_text(_("🔁 Aggiornamento automatico abilitato."));
219 } else {
221 lbl_status_.set_text(_("⏸️ Aggiornamento automatico disattivato."));
222 }
223 });
224
226 auto_update_switch_.set_active(true);
228 lbl_status_.set_text(_("🔁 Aggiornamento automatico attivo."));
229 }
230}
231
233 lbl_status_.set_text(_("⬇️ Download in corso..."));
234 btn_refresh_.set_sensitive(false);
235
236 std::string url = entry_url_.get_text();
237
238 std::thread([this, alive = alive_, url]() {
240 if (!alive->load()) return;
241
242 Glib::signal_idle().connect_once([this, alive, ok, url]() {
243 BastionGuard::if_alive(alive, [this, ok, &url]() {
244 if (ok) {
246
250 lbl_status_.set_text(_("✅ Lista aggiornata da MISP, whitelist integrata e servizio CEF riavviato."));
251 } else {
252 lbl_status_.set_text(_("⚠ Lista aggiornata da MISP, ma merge whitelist fallito."));
253 }
254 } else {
255 lbl_status_.set_text(_("❌ Errore nel download della lista."));
256 }
257 btn_refresh_.set_sensitive(true);
258 });
259 });
260 }).detach();
261}
262
263void BankPage::copy_banklist_to_system(const std::string& source_url) {
264 const std::string helper = "/usr/bin/bank-helper";
265 const std::string userPath = Glib::get_home_dir() + "/.local/share/BastionGuard/banks.json";
266
267 // NON usiamo std::system() con il URL interpolato: sarebbe shell injection
268 // (caratteri come ; | $() ` newline scappano dal quoting).
269 // Invochiamo sudo + helper direttamente con execvp: ogni argomento è
270 // un elemento separato di argv, non c'è shell che lo interpreta.
271 const pid_t pid = ::fork();
272 if (pid < 0) {
273 std::cerr << _("[BankPage] ⚠️ fork fallita per bank-helper") << std::endl;
274 return;
275 }
276
277 if (pid == 0) {
278 // child: redirige stdout/stderr su /dev/null (equivalente a >/dev/null 2>&1)
279 int devnull = ::open("/dev/null", O_WRONLY);
280 if (devnull >= 0) {
281 ::dup2(devnull, STDOUT_FILENO);
282 ::dup2(devnull, STDERR_FILENO);
283 ::close(devnull);
284 }
285
286 // argv: sudo, helper, userPath, source_url
287 // Nota: source_url viene passato come singolo argomento di argv,
288 // quindi contenuti come "; rm -rf /" restano una stringa letterale.
289 const char* argv[] = {
290 "sudo",
291 helper.c_str(),
292 userPath.c_str(),
293 source_url.c_str(),
294 nullptr
295 };
296 ::execvp("sudo", const_cast<char* const*>(argv));
297 _exit(127); // execvp fallito
298 }
299
300 // parent: aspetta il child
301 int status = 0;
302 if (::waitpid(pid, &status, 0) < 0) {
303 std::cerr << _("[BankPage] ⚠️ waitpid fallita per bank-helper") << std::endl;
304 return;
305 }
306
307 const int rc = WIFEXITED(status) ? WEXITSTATUS(status)
308 : (WIFSIGNALED(status) ? 128 + WTERMSIG(status) : -1);
309 if (rc == 0) {
310 std::cout << _("[BankPage] ✅ Lista copiata in /usr/share/BastionGuard/data/bank/banks.json\n");
311 } else {
312 std::cerr << _("[BankPage] ⚠️ bank-helper fallito (rc=") << rc
313 << _("). Verificare /etc/sudoers.d/bastionguard-bank e i permessi di bank-helper.\n");
314 }
315}
316
318 std::string path = Glib::get_home_dir() + "/.local/share/BastionGuard/banks.json";
319
320 if (!fs::exists(path)) {
321 txt_banks_.get_buffer()->set_text(_("⚠ Nessuna lista trovata.\n"));
322 return;
323 }
324
325 std::ifstream f(path);
326 json j;
327 try {
328 f >> j;
329 } catch (...) {
330 txt_banks_.get_buffer()->set_text(_("❌ Errore parsing JSON.\n"));
331 return;
332 }
333
334 std::ostringstream oss_banks;
335 oss_banks << _("🌍 Elenco banche conosciute:\n\n");
336
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>());
344 }
345 }
346 }
347
348 std::sort(domains.begin(), domains.end());
349 domains.erase(std::unique(domains.begin(), domains.end()), domains.end());
350
351 int count = 0;
352 for (auto& domain : domains) {
353 std::string clean = normalize_domain(domain);
354 if (clean.empty()) continue;
355
356 if (std::find(whitelist_.begin(), whitelist_.end(), clean) == whitelist_.end()) {
357 oss_banks << " - " << clean << "\n";
358 count++;
359 }
360 }
361
362 oss_banks << _("\nTotale banche: ") << count << "\n";
363 txt_banks_.get_buffer()->set_text(oss_banks.str());
364
365 std::ostringstream oss_white;
366 oss_white << _("🛡️ Nuovi domini bancari:\n\n");
367 for (auto& w : whitelist_)
368 oss_white << " + " << w << "\n";
369
370 if (whitelist_.empty())
371 oss_white << _("(Nessun dominio whitelistato)\n");
372
373 txt_whitelist_.get_buffer()->set_text(oss_white.str());
374}
375
377 std::string domain = normalize_domain(whitelist_entry_.get_text());
378
379 if (domain.empty()) {
380 lbl_status_.set_text(_("⚠ Inserisci un dominio valido."));
381 return;
382 }
383
384 if (std::find(whitelist_.begin(), whitelist_.end(), domain) == whitelist_.end()) {
385 whitelist_.push_back(domain);
386 std::sort(whitelist_.begin(), whitelist_.end());
387 whitelist_.erase(std::unique(whitelist_.begin(), whitelist_.end()), whitelist_.end());
388
390
392 lbl_status_.set_text(_("✅ Dominio aggiunto alla whitelist, integrato in banks.json e servizio CEF riavviato."));
393 else
394 lbl_status_.set_text(_("⚠ Dominio salvato in whitelist, ma merge su banks.json fallito."));
395
397 } else {
398 lbl_status_.set_text(_("ℹ️ Dominio già presente nella whitelist."));
399 }
400
401 whitelist_entry_.set_text("");
402}
403
405 whitelist_.clear();
406
407 std::string path = Glib::get_home_dir() + "/.config/BastionGuard/whitelist.json";
408 if (!fs::exists(path))
409 return;
410
411 std::ifstream f(path);
412 if (!f.is_open())
413 return;
414
415 json j;
416 try {
417 f >> j;
418 } catch (...) {
419 return;
420 }
421
422 if (j.contains("list") && j["list"].is_array()) {
423 for (const auto& w : j["list"]) {
424 if (!w.is_string()) continue;
425
426 std::string d = normalize_domain(w.get<std::string>());
427 if (!d.empty())
428 whitelist_.push_back(d);
429 }
430 }
431
432 std::sort(whitelist_.begin(), whitelist_.end());
433 whitelist_.erase(std::unique(whitelist_.begin(), whitelist_.end()), whitelist_.end());
434}
435
437 std::string dir = Glib::get_home_dir() + "/.config/BastionGuard";
438 fs::create_directories(dir);
439
440 std::vector<std::string> clean = whitelist_;
441 for (auto& d : clean)
442 d = normalize_domain(d);
443
444 clean.erase(std::remove_if(clean.begin(), clean.end(),
445 [](const std::string& s) { return s.empty(); }),
446 clean.end());
447
448 std::sort(clean.begin(), clean.end());
449 clean.erase(std::unique(clean.begin(), clean.end()), clean.end());
450
451 json j;
452 j["version"] = 1;
453 j["updated_at"] = iso_today_yyyy_mm_dd();
454 j["enabled"] = true;
455 j["list"] = json::array();
456
457 for (const auto& d : clean)
458 j["list"].push_back(d);
459
460 std::ofstream f(dir + "/whitelist.json", std::ios::trunc);
461 if (!f.is_open())
462 return;
463
464 f << j.dump(2);
465}
466
468 const std::string path = Glib::get_home_dir() + "/.local/share/BastionGuard/banks.json";
469
470 json j;
471
472 if (fs::exists(path)) {
473 try {
474 std::ifstream in(path);
475 if (in.is_open())
476 in >> j;
477 } catch (...) {
478 j = json::object();
479 }
480 }
481
482 if (!j.is_object())
483 j = json::object();
484
485 if (!j.contains("list") || !j["list"].is_array())
486 j["list"] = json::array();
487
488 std::unordered_set<std::string> existing;
489 bool changed = false;
490
491 for (const auto& item : j["list"]) {
492 if (item.is_string()) {
493 std::string d = normalize_domain(item.get<std::string>());
494 if (!d.empty())
495 existing.insert(d);
496 } else if (item.is_object() && item.contains("value") && item["value"].is_string()) {
497 std::string d = normalize_domain(item["value"].get<std::string>());
498 if (!d.empty())
499 existing.insert(d);
500 }
501 }
502
503 for (const auto& w : whitelist_) {
504 std::string d = normalize_domain(w);
505 if (d.empty()) continue;
506
507 if (!existing.count(d)) {
508 j["list"].push_back("." + d);
509 existing.insert(d);
510 changed = true;
511 }
512 }
513
514 if (!changed) {
515 std::cout << _("[BankPage] ℹ Nessun merge necessario in banks.json\n");
516 return true;
517 }
518
519 const std::string tmp = path + ".tmp";
520 try {
521 std::ofstream out(tmp, std::ios::trunc);
522 if (!out.is_open())
523 return false;
524
525 out << j.dump(2);
526 out.flush();
527 out.close();
528
529 std::error_code ec;
530 fs::rename(tmp, path, ec);
531 if (ec) {
532 fs::remove(path, ec);
533 ec.clear();
534 fs::rename(tmp, path, ec);
535 if (ec) {
536 fs::remove(tmp, ec);
537 return false;
538 }
539 }
540
541 std::cout << _("[BankPage] ✔ whitelist integrata in banks.json\n");
542 restart_cef_service_user();
543 return true;
544
545 } catch (...) {
546 try { fs::remove(tmp); } catch (...) {}
547 return false;
548 }
549}
550
553 auto_update_conn_.disconnect();
554
555 auto_update_conn_ = Glib::signal_timeout().connect_seconds(
556 sigc::mem_fun(*this, &BankPage::auto_update_bank_list),
557 6 * 60 * 60
558 );
559
560 std::cout << _("[BankPage] ⏱️ Auto-update attivo ogni 6 ore.") << std::endl;
561}
562
565 auto_update_conn_.disconnect();
566
567 std::cout << _("[BankPage] ⏹️ Auto-update disattivato.") << std::endl;
568}
569
571 std::string url = entry_url_.get_text();
572
573 std::thread([this, alive = alive_, url]() {
574 std::cout << _("[BankPage] ⏰ Aggiornamento automatico lista banche...") << std::endl;
576 if (!alive->load()) return;
577
578 Glib::signal_idle().connect_once([this, alive, ok, url]() {
579 BastionGuard::if_alive(alive, [this, ok, &url]() {
580 if (ok) {
582
586
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"));
589 } else {
590 lbl_status_.set_text(_("⚠️ Lista aggiornata, ma merge whitelist fallito."));
591 }
592 } else {
593 lbl_status_.set_text(_("⚠️ Errore nell'aggiornamento automatico."));
594 }
595 });
596 });
597 }).detach();
598
599 return true;
600}
601
603 std::string path = Glib::get_home_dir() + "/.config/BastionGuard/settings.json";
604 if (!fs::exists(path))
605 return;
606
607 std::ifstream f(path);
608 if (!f.is_open())
609 return;
610
611 json j;
612 try {
613 f >> j;
614 } catch (...) {
615 return;
616 }
617
618 if (j.contains("auto_update"))
619 auto_update_enabled_ = j["auto_update"].get<bool>();
620
622}
623
625 std::string dir = Glib::get_home_dir() + "/.config/BastionGuard";
626 fs::create_directories(dir);
627
628 std::ofstream f(dir + "/settings.json", std::ios::trunc);
629 json j;
630 j["auto_update"] = auto_update_enabled_;
631 f << j.dump(2);
632}
633
634// =============================================================================
635// Apertura banca in sandbox sicura.
636//
637// Il bottone "🔐 Apri in sandbox sicura" passa l'URL al binario
638// BastionGuard-secure-cli, che:
639// 1) si re-exec dentro bubblewrap (con DNS custom + bind X11/Wayland);
640// 2) avvia CEF con OZONE_PLATFORM corretto (Wayland o X11);
641// 3) fa il match contro payments.json/banks.json e apre la pagina.
642//
643// Vantaggio: anche le banche nuove (whitelist utente) vengono aperte in
644// un processo isolato dal browser di sistema, senza dover passare dal
645// proxy CEF (BastionGuard-cef.service).
646// =============================================================================
648 std::string raw = trim_copy(open_bank_entry_.get_text());
649 if (raw.empty()) {
650 lbl_status_.set_text(_("⚠ Inserisci un URL o un dominio bancario."));
651 return;
652 }
653
654 // Se l'utente ha messo solo il dominio, completiamo con https://
655 if (raw.find("://") == std::string::npos)
656 raw = "https://" + raw;
657
658 if (open_bank_in_sandbox(raw)) {
659 lbl_status_.set_text(_("🔐 Apertura sandbox sicura in corso: ") + raw);
660 } else {
661 lbl_status_.set_text(_("❌ Apertura sandbox fallita. Vedi log."));
662 }
663}
664
665bool BankPage::open_bank_in_sandbox(const std::string& url) {
666 if (url.empty()) return false;
667
668 // Validazione minima: blocca caratteri pericolosi anche se passiamo
669 // l'URL come argv separato (non c'è shell-injection, ma evitiamo
670 // comunque newline e null bytes che confonderebbero il helper).
671 for (char c : url) {
672 if (c == '\n' || c == '\r' || c == '\0') {
673 std::cerr << _("[BankPage] URL contiene caratteri non validi.") << std::endl;
674 return false;
675 }
676 }
677
678 // Cerchiamo il binario in path standard. Lo script di sandbox è
679 // BastionGuard-secure-cli; se non c'è, ripieghiamo su BastionGuard-secure
680 // (la versione GUI), che fa anch'essa re-exec dentro bwrap.
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",
686 nullptr
687 };
688
689 const char* bin = nullptr;
690 for (int i = 0; candidates[i]; ++i) {
691 if (::access(candidates[i], X_OK) == 0) {
692 bin = candidates[i];
693 break;
694 }
695 }
696 if (!bin) {
697 std::cerr << _("[BankPage] BastionGuard-secure-cli non trovato; "
698 "installa il pacchetto BastionGuard completo.") << std::endl;
699 return false;
700 }
701
702 std::cout << _("[BankPage] 🔐 Avvio sandbox sicura via ") << bin
703 << _(" per URL: ") << url << std::endl;
704
705 const pid_t pid = ::fork();
706 if (pid < 0) {
707 std::cerr << _("[BankPage] fork() fallita per BastionGuard-secure-cli")
708 << std::endl;
709 return false;
710 }
711
712 if (pid == 0) {
713 // Child: detach completo dalla GUI principale.
714 // setsid() così la chiusura della GUI di BastionGuard non killa la sandbox.
715 ::setsid();
716
717 // Redirige stdout/stderr per non sporcare la console di BastionGuard.
718 // (I log finiscono comunque nei journald del servizio se è registrato.)
719 int devnull = ::open("/dev/null", O_WRONLY);
720 if (devnull >= 0) {
721 ::dup2(devnull, STDOUT_FILENO);
722 ::dup2(devnull, STDERR_FILENO);
723 ::close(devnull);
724 }
725
726 const char* argv[] = { bin, url.c_str(), nullptr };
727 ::execvp(bin, const_cast<char* const*>(argv));
728 _exit(127);
729 }
730
731 // Parent: non aspettiamo (è un processo a vita lunga, il browser).
732 // setsid + non waitpid = detached: corretto per "spawn and forget".
733 return true;
734}
nlohmann::json json
Definition Backend.cpp:2908
#define _(s)
static Backend & instance()
Definition Backend.cpp:830
bool updateBankListFromURL(const std::string &source_url)
Definition Backend.cpp:2805
void on_add_whitelist_clicked()
Definition BankPage.cpp:376
void copy_banklist_to_system(const std::string &source_url)
Definition BankPage.cpp:263
Gtk::Entry whitelist_entry_
Definition BankPage.hpp:41
sigc::connection auto_update_conn_
Definition BankPage.hpp:57
Gtk::Label lbl_status_
Definition BankPage.hpp:47
Gtk::Entry open_bank_entry_
Definition BankPage.hpp:50
Gtk::ScrolledWindow scroller_white_
Definition BankPage.hpp:44
BastionGuard::AliveFlag alive_
Definition BankPage.hpp:92
void on_open_bank_clicked()
Definition BankPage.cpp:647
Gtk::Entry entry_url_
Definition BankPage.hpp:37
bool auto_update_bank_list()
Definition BankPage.cpp:570
Gtk::Label auto_update_label_
Definition BankPage.hpp:40
void stop_auto_update_timer()
Definition BankPage.cpp:563
std::vector< std::string > whitelist_
Definition BankPage.hpp:54
~BankPage() override
Definition BankPage.cpp:111
void start_auto_update_timer()
Definition BankPage.cpp:551
Gtk::Switch auto_update_switch_
Definition BankPage.hpp:39
void load_whitelist()
Definition BankPage.cpp:404
void save_settings()
Definition BankPage.cpp:624
void on_refresh_clicked()
Definition BankPage.cpp:232
void populate_bank_list()
Definition BankPage.cpp:317
Gtk::TextView txt_banks_
Definition BankPage.hpp:45
Gtk::Button btn_add_whitelist_
Definition BankPage.hpp:42
bool auto_update_enabled_
Definition BankPage.hpp:56
Gtk::Button btn_open_bank_
Definition BankPage.hpp:51
bool merge_whitelist_into_banks_json()
Definition BankPage.cpp:467
Gtk::ScrolledWindow scroller_banks_
Definition BankPage.hpp:43
void load_settings()
Definition BankPage.cpp:602
void save_whitelist()
Definition BankPage.cpp:436
Gtk::TextView txt_whitelist_
Definition BankPage.hpp:46
Gtk::Button btn_refresh_
Definition BankPage.hpp:38
bool open_bank_in_sandbox(const std::string &url)
Definition BankPage.cpp:665
void if_alive(const AliveFlag &alive, Fn &&fn)
static std::string iso_today_yyyy_mm_dd()
Definition BankPage.cpp:96
static std::string normalize_domain(std::string domain)
Definition BankPage.cpp:56
static std::string trim_copy(std::string s)
Definition BankPage.cpp:49
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={})