Skip to content
API Documentation
BastionGuard™
UpdatePage Class Reference

#include <UpdatePage.hpp>

Inheritance diagram for UpdatePage:

Public Member Functions

 UpdatePage ()
 ~UpdatePage ()
void updateSaneSecurity ()

Private Member Functions

bool checkSaneTimer ()
void onSaneAutoToggled ()
void onUpdateClicked ()
void onServiceToggled ()
void onAddSourceClicked ()
void onSaveSourcesClicked ()
void updateFromExtraSources ()
bool checkServiceStatus ()
void loadSources ()
void saveSources ()
void createSourceRow (const SourceItem &src={})
void testConnection (const std::string &name, const std::string &url, bool auth, const std::string &user)
void logSafe (const std::string &msg)
std::string generateSecretKey ()
std::string lookupPassword (const std::string &key)
bool storePassword (const std::string &key, const std::string &password)

Private Attributes

Gtk::Button updateButton
Gtk::Switch serviceSwitch
Gtk::Label sourcesLabel
Gtk::Button addSourceButton
Gtk::Button saveSourcesButton
Gtk::TextView outputView
Glib::RefPtr< Gtk::TextBuffer > buffer
Gtk::Notebook * sourcesNotebook = nullptr
Gtk::Box * sourcesBox = nullptr
Gtk::Switch saneAutoSwitch
BastionGuard::AliveFlag alive_ = BastionGuard::make_alive_flag()

Detailed Description

Definition at line 36 of file UpdatePage.hpp.

Constructor & Destructor Documentation

◆ UpdatePage()

UpdatePage::UpdatePage ( )

Definition at line 52 of file UpdatePage.cpp.

53: Gtk::Box(Gtk::Orientation::VERTICAL, 12),
54 updateButton(_("Aggiorna firme (freshclam)")),
56 addSourceButton(_("Aggiungi sorgente")),
57 saveSourcesButton(_("Salva sorgenti")),
58 sourcesLabel(_("Sorgenti aggiuntive"))
59{
60 buffer = Gtk::TextBuffer::create();
61 outputView.set_buffer(buffer);
62 outputView.set_editable(false);
63 outputView.set_monospace(true);
64
65 updateButton.signal_clicked().connect(sigc::mem_fun(*this, &UpdatePage::onUpdateClicked));
66
68 serviceSwitch.property_active().signal_changed().connect(sigc::mem_fun(*this, &UpdatePage::onServiceToggled));
69
70 auto serviceBox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 8);
71 auto serviceLabel = Gtk::make_managed<Gtk::Label>(_("Servizio automatico Freshclam:"));
72 serviceBox->append(*serviceLabel);
73 serviceBox->append(serviceSwitch);
74 auto saneSwitchLabel = Gtk::make_managed<Gtk::Label>(
75 _("Aggiorna automaticamente le firme SaneSecurity (ogni 2 ore):")
76 );
77 saneAutoSwitch.set_active(checkSaneTimer());
78 saneAutoSwitch.property_active().signal_changed().connect(
79 sigc::mem_fun(*this, &UpdatePage::onSaneAutoToggled)
80 );
81
82 auto saneBox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 8);
83 saneBox->append(*saneSwitchLabel);
84 saneBox->append(saneAutoSwitch);
85 append(*saneBox);
86 addSourceButton.signal_clicked().connect(sigc::mem_fun(*this, &UpdatePage::onAddSourceClicked));
87 saveSourcesButton.signal_clicked().connect(sigc::mem_fun(*this, &UpdatePage::onSaveSourcesClicked));
88
89 sourcesBox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 4);
91
92 auto sourcesFrame = Gtk::make_managed<Gtk::Frame>();
93 sourcesFrame->set_label(_("Sorgenti di aggiornamento aggiuntive"));
94 sourcesFrame->set_child(*sourcesBox);
95
96 auto controlBox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 8);
97 controlBox->append(addSourceButton);
98 controlBox->append(saveSourcesButton);
99 auto scrolled = Gtk::make_managed<Gtk::ScrolledWindow>();
100 scrolled->set_child(outputView);
101 scrolled->set_expand(true);
102
103 append(updateButton);
104 append(*serviceBox);
105 append(sourcesLabel);
106 append(*sourcesFrame);
107 append(*controlBox);
108 append(*scrolled);
109}
#define _(s)
Gtk::Button addSourceButton
bool checkServiceStatus()
void onSaneAutoToggled()
void onSaveSourcesClicked()
Gtk::Box * sourcesBox
Gtk::Button saveSourcesButton
Glib::RefPtr< Gtk::TextBuffer > buffer
void onAddSourceClicked()
bool checkSaneTimer()
Gtk::Label sourcesLabel
void onServiceToggled()
Gtk::TextView outputView
Gtk::Switch serviceSwitch
Gtk::Switch saneAutoSwitch
void loadSources()
void onUpdateClicked()
Gtk::Button updateButton

References _, addSourceButton, buffer, checkSaneTimer(), checkServiceStatus(), loadSources(), onAddSourceClicked(), onSaneAutoToggled(), onSaveSourcesClicked(), onServiceToggled(), onUpdateClicked(), outputView, saneAutoSwitch, saveSourcesButton, serviceSwitch, sourcesBox, sourcesLabel, and updateButton.

◆ ~UpdatePage()

UpdatePage::~UpdatePage ( )

Definition at line 602 of file UpdatePage.cpp.

602 {
603 // Invalida i lambda async in volo.
604 if (alive_) alive_->store(false, std::memory_order_release);
605
606 if (sourcesNotebook) {
607 sourcesNotebook->set_visible(false);
608 sourcesNotebook = nullptr;
609 }
610}
BastionGuard::AliveFlag alive_
Gtk::Notebook * sourcesNotebook

References alive_, and sourcesNotebook.

Member Function Documentation

◆ checkSaneTimer()

bool UpdatePage::checkSaneTimer ( )
private

Definition at line 679 of file UpdatePage.cpp.

679 {
680 std::string out;
681 int status = -1;
682
683 try {
684 Glib::spawn_command_line_sync(
685 "/usr/libexec/bastionguard/bastionguard-service --system is-enabled bastionguard-sanesecurity.timer",
686 &out, nullptr, &status
687 );
688 return out.find("enabled") != std::string::npos;
689 }
690 catch (...) {
691 return false;
692 }
693}

Referenced by UpdatePage().

◆ checkServiceStatus()

bool UpdatePage::checkServiceStatus ( )
private

Definition at line 111 of file UpdatePage.cpp.

111 {
112 return bg_process::run({"/usr/libexec/bastionguard/bastionguard-service",
113 "--system", "is-active", "clamav-freshclam.service"}).ok();
114}
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={})

References bg_process::run().

Referenced by UpdatePage().

◆ createSourceRow()

void UpdatePage::createSourceRow ( const SourceItem & src = {})
private

Definition at line 309 of file UpdatePage.cpp.

309 {
310 auto frame = Gtk::make_managed<Gtk::Frame>();
311 frame->set_hexpand(true);
312 frame->set_margin(4);
313 frame->set_valign(Gtk::Align::START);
314
315 auto headerBox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 8);
316 auto titleLabel = Gtk::make_managed<Gtk::Label>(
317 src.name.empty() ? _("➕ Nuova sorgente") : ("📦 " + src.name));
318 titleLabel->set_xalign(0);
319 titleLabel->add_css_class(src.name.empty() ? "suggested-action" : "highlight");
320
321 auto expandIcon = Gtk::make_managed<Gtk::Label>("▼");
322 expandIcon->set_margin_start(6);
323 expandIcon->set_css_classes({ "expand-icon" });
324
325 headerBox->append(*titleLabel);
326 headerBox->append(*expandIcon);
327 headerBox->set_margin(6);
328 headerBox->set_css_classes({ "source-header" });
329
330 auto innerBox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 6);
331 innerBox->set_css_classes({ "revealer-content" });
332
333
334 auto nameEntry = Gtk::make_managed<Gtk::Entry>();
335 nameEntry->set_placeholder_text(_("Nome sorgente (es. Sanesecurity)"));
336 nameEntry->set_text(src.name);
337
338 auto urlEntry = Gtk::make_managed<Gtk::Entry>();
339 urlEntry->set_placeholder_text(_("https://example.com/firme.cvd"));
340 urlEntry->set_text(src.url);
341
342 auto authSwitch = Gtk::make_managed<Gtk::CheckButton>(_("_Richiede autenticazione"), true);
343 authSwitch->set_active(src.requires_auth);
344
345 auto usernameEntry = Gtk::make_managed<Gtk::Entry>();
346 usernameEntry->set_placeholder_text(_("Username"));
347 usernameEntry->set_text(src.username);
348
349 auto passwordEntry = Gtk::make_managed<Gtk::PasswordEntry>();
350 passwordEntry->property_placeholder_text() = _("Password");
351 passwordEntry->set_text(lookupPassword("BastionGuard:" + src.name));
352
353 auto buttonBox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 6);
354 auto testButton = Gtk::make_managed<Gtk::Button>(_("_Test connessione"), true);
355 auto saveButton = Gtk::make_managed<Gtk::Button>(_("_Salva sorgente"), true);
356 auto deleteButton = Gtk::make_managed<Gtk::Button>(_("_Elimina sorgente"), true);
357 deleteButton->add_css_class("btn-danger");
358
359 buttonBox->append(*testButton);
360 buttonBox->append(*saveButton);
361 buttonBox->append(*deleteButton);
362
363 innerBox->append(*nameEntry);
364 innerBox->append(*urlEntry);
365 innerBox->append(*authSwitch);
366 innerBox->append(*usernameEntry);
367 innerBox->append(*passwordEntry);
368 innerBox->append(*buttonBox);
369
370 auto revealer = Gtk::make_managed<Gtk::Revealer>();
371 revealer->set_transition_type(Gtk::RevealerTransitionType::SLIDE_DOWN);
372 revealer->set_transition_duration(200);
373 revealer->set_reveal_child(false);
374 revealer->set_child(*innerBox);
375
376 auto sectionBox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 0);
377 sectionBox->append(*headerBox);
378 sectionBox->append(*revealer);
379 frame->set_child(*sectionBox);
380 sourcesBox->append(*frame);
381
382 auto click = Gtk::GestureClick::create();
383 click->signal_pressed().connect([this, revealer, expandIcon](int n_press, double, double) {
384 if (n_press != 1) return;
385
386 bool currentlyOpen = revealer->get_reveal_child();
387
388 for (auto child : sourcesBox->get_children()) {
389 if (auto f = dynamic_cast<Gtk::Frame*>(child)) {
390 if (auto box = dynamic_cast<Gtk::Box*>(f->get_child())) {
391 auto children = box->get_children();
392 if (children.size() >= 2) {
393 if (auto r = dynamic_cast<Gtk::Revealer*>(children[1])) {
394 r->set_reveal_child(false);
395 if (auto hBox = dynamic_cast<Gtk::Box*>(children[0])) {
396 for (auto& c : hBox->get_children()) {
397 if (auto lbl = dynamic_cast<Gtk::Label*>(c)) {
398 if (lbl->get_text() == "▲" || lbl->get_text() == "▼")
399 lbl->set_text("▼");
400 }
401 }
402 }
403 }
404 }
405 }
406 }
407 }
408
409 revealer->set_reveal_child(!currentlyOpen);
410 expandIcon->set_text(!currentlyOpen ? "▲" : "▼");
411 if (auto header = dynamic_cast<Gtk::Widget*>(expandIcon->get_parent())) {
412 if (!currentlyOpen)
413 header->add_css_class("open");
414 else
415 header->remove_css_class("open");
416 }
417
418 });
419 headerBox->add_controller(click);
420
421 // --- Aggiorna titolo dinamico ---
422 nameEntry->signal_changed().connect([titleLabel, nameEntry]() {
423 std::string newName = nameEntry->get_text();
424 if (newName.empty()) {
425 titleLabel->set_text(_("➕ Nuova sorgente"));
426 titleLabel->remove_css_class("highlight");
427 titleLabel->add_css_class("suggested-action");
428 } else {
429 titleLabel->set_text("📦 " + newName);
430 titleLabel->remove_css_class("suggested-action");
431 titleLabel->add_css_class("highlight");
432 }
433 });
434
435 testButton->signal_clicked().connect([this, nameEntry, urlEntry, authSwitch, usernameEntry, passwordEntry]() {
436 std::string pw = passwordEntry->get_text();
437 if (!pw.empty())
438 storePassword("BastionGuard:" + nameEntry->get_text(), pw);
439 testConnection(nameEntry->get_text(), urlEntry->get_text(),
440 authSwitch->get_active(), usernameEntry->get_text());
441 });
442
443 saveButton->signal_clicked().connect([this, nameEntry, passwordEntry]() {
444 std::string key = "BastionGuard:" + nameEntry->get_text();
445 storePassword(key, passwordEntry->get_text());
446 buffer->insert(buffer->end(), _("💾 Sorgente salvata.\n"));
447 });
448
449 deleteButton->signal_clicked().connect([this, frame]() {
450 sourcesBox->remove(*frame);
451 buffer->insert(buffer->end(), _("❌ Sorgente rimossa.\n"));
452 });
453 for (auto child : sourcesBox->get_children()) {
454 if (auto f = dynamic_cast<Gtk::Frame*>(child)) {
455 if (auto box = dynamic_cast<Gtk::Box*>(f->get_child())) {
456 auto children = box->get_children();
457 if (children.size() >= 2) {
458 if (auto r = dynamic_cast<Gtk::Revealer*>(children[1])) {
459 r->set_reveal_child(false);
460 }
461 }
462 }
463 }
464 }
465
466 revealer->set_reveal_child(true);
467 expandIcon->set_text("▲");
468}
std::string lookupPassword(const std::string &key)
bool storePassword(const std::string &key, const std::string &password)
void testConnection(const std::string &name, const std::string &url, bool auth, const std::string &user)
std::string url
std::string username
bool requires_auth
std::string name

References _, buffer, lookupPassword(), SourceItem::name, SourceItem::requires_auth, sourcesBox, storePassword(), testConnection(), SourceItem::url, and SourceItem::username.

Referenced by loadSources(), and onAddSourceClicked().

◆ generateSecretKey()

std::string UpdatePage::generateSecretKey ( )
private

Definition at line 593 of file UpdatePage.cpp.

593 {
594 std::random_device rd;
595 std::mt19937 gen(rd());
596 std::uniform_int_distribution<> dis(0, 15);
597 std::ostringstream oss;
598 for (int i = 0; i < 12; ++i) oss << std::hex << dis(gen);
599 return "BastionGuard:" + oss.str();
600}

◆ loadSources()

void UpdatePage::loadSources ( )
private

Definition at line 508 of file UpdatePage.cpp.

508 {
509 std::ifstream file(Glib::get_home_dir() + "/.config/BastionGuard/sources.conf");
510 std::string line;
511 while (std::getline(file, line)) {
512 SourceItem src;
513 std::stringstream ss(line);
514 std::getline(ss, src.name, '|');
515 std::getline(ss, src.url, '|');
516 std::string auth;
517 std::getline(ss, auth, '|');
518 src.requires_auth = (auth == "1");
519 std::getline(ss, src.username, '|');
520 createSourceRow(src);
521 }
522}
void createSourceRow(const SourceItem &src={})

References createSourceRow(), SourceItem::name, SourceItem::requires_auth, SourceItem::url, and SourceItem::username.

Referenced by UpdatePage().

◆ logSafe()

void UpdatePage::logSafe ( const std::string & msg)
private

Definition at line 670 of file UpdatePage.cpp.

670 {
671 auto alive = alive_;
672 Glib::signal_idle().connect_once([this, alive, msg]() {
673 BastionGuard::if_alive(alive, [this, &msg]() {
674 buffer->insert(buffer->end(), msg);
675 });
676 });
677}
void if_alive(const AliveFlag &alive, Fn &&fn)

References alive_, buffer, and BastionGuard::if_alive().

Referenced by onUpdateClicked().

◆ lookupPassword()

std::string UpdatePage::lookupPassword ( const std::string & key)
private

Definition at line 577 of file UpdatePage.cpp.

577 {
578 GError *error = nullptr;
579 gchar *pw = secret_password_lookup_sync(
580 &BastionGuard_SCHEMA, nullptr, &error,
581 "secret_key", key.c_str(),
582 nullptr
583 );
584 std::string result;
585 if (pw) {
586 result = pw;
587 secret_password_free(pw);
588 }
589 if (error) g_error_free(error);
590 return result;
591}
static const SecretSchema BastionGuard_SCHEMA

References BastionGuard_SCHEMA.

Referenced by createSourceRow(), testConnection(), and updateFromExtraSources().

◆ onAddSourceClicked()

void UpdatePage::onAddSourceClicked ( )
private

Definition at line 305 of file UpdatePage.cpp.

305 {
307}

References createSourceRow().

Referenced by UpdatePage().

◆ onSaneAutoToggled()

void UpdatePage::onSaneAutoToggled ( )
private

Definition at line 695 of file UpdatePage.cpp.

695 {
696 bool enable = saneAutoSwitch.get_active();
697
698 buffer->insert(buffer->end(),
699 enable ?
700 _("⏱️ Attivo aggiornamento automatico SaneSecurity ogni 2 ore...\n" ):
701 _("⏸️ Disattivo aggiornamento automatico SaneSecurity...\n")
702 );
703
704 const auto result = bg_process::run({"pkexec", "/usr/libexec/bastionguard/bastionguard-service",
705 "--system", enable ? "enable" : "disable", "--now", "bastionguard-sanesecurity.timer"}, true);
706 buffer->insert(buffer->end(), result.output + (result.ok() ? "\n" : _("Errore modifica timer.\n")));
707}

References _, buffer, bg_process::run(), and saneAutoSwitch.

Referenced by UpdatePage().

◆ onSaveSourcesClicked()

void UpdatePage::onSaveSourcesClicked ( )
private

Definition at line 472 of file UpdatePage.cpp.

472 {
473 saveSources();
474 buffer->insert(buffer->end(), _("Sorgenti salvate in ~/.config/BastionGuard/sources.conf\n"));
475}
void saveSources()

References _, buffer, and saveSources().

Referenced by UpdatePage().

◆ onServiceToggled()

void UpdatePage::onServiceToggled ( )
private

Definition at line 116 of file UpdatePage.cpp.

116 {
117 bool enable = serviceSwitch.get_active();
118 buffer->insert(buffer->end(), (enable ? _("Abilito") : _("Disabilito")) + std::string(" il servizio freshclam...\n"));
119 const auto result = bg_process::run({"pkexec", "/usr/libexec/bastionguard/bastionguard-service",
120 "--system", enable ? "enable" : "disable", "--now", "clamav-freshclam.service"}, true);
121 buffer->insert(buffer->end(), result.output + (result.ok() ? "\n" : _("Errore modifica servizio.\n")));
122}

References _, buffer, bg_process::run(), and serviceSwitch.

Referenced by UpdatePage().

◆ onUpdateClicked()

void UpdatePage::onUpdateClicked ( )
private

Definition at line 142 of file UpdatePage.cpp.

142 {
143 buffer->set_text(_("🔄 Avvio aggiornamento delle firme...\n"));
144 updateButton.set_sensitive(false);
145 std::thread([this, alive = alive_]() {
146 std::string out, err;
147 int exit_status = -1;
148
149 try {
150 logSafe(_("⏸️ Arresto servizio clamav-freshclam...\n"));
151 (bg_process::run({"pkexec", "/usr/libexec/bastionguard/bastionguard-service", "--system", "stop", "clamav-freshclam.service"}).exit_code);
152 logSafe(_("🚀 Avvio aggiornamento con pkexec freshclam...\n"));
153 try {
154 Glib::spawn_command_line_sync("pkexec freshclam", &out, &err, &exit_status);
155 } catch (const Glib::Error& ex) {
156 logSafe(std::string(_("❌ Errore freshclam: ")) + ex.what() + "\n");
157 }
158 if (!out.empty())
159 logSafe("\n" + out + "\n");
160 if (!err.empty()) {
161 logSafe("\n--- STDERR ---\n");
162 logSafe(err + "\n");
163 }
164 logSafe(Glib::ustring::compose(_("\n🧾 Codice di uscita: %1\n"), exit_status));
165 logSafe(_("▶️ Riavvio del servizio clamav-freshclam...\n"));
166 (bg_process::run({"pkexec", "/usr/libexec/bastionguard/bastionguard-service", "--system", "start", "clamav-freshclam.service"}).exit_code);
167
168 logSafe(_("\n🔽 Scarico firme aggiuntive dalle sorgenti configurate...\n"));
169 if (!alive->load()) return;
171
172 logSafe(_("🔐 Aggiornamento firme SaneSecurity...\n"));
173 if (!alive->load()) return;
175
176 logSafe(_("🔁 Ricarico clamd (clamav-daemon)...\n"));
177 int reload_status = (bg_process::run({"pkexec", "/usr/libexec/bastionguard/bastionguard-service", "--system", "try-reload-or-restart", "clamav-daemon.service"}).exit_code);
178 if (reload_status != 0) {
179 logSafe(_("⚠ Reload clamav-daemon fallito (codice non zero).\n"));
180 }
181 Glib::signal_idle().connect_once([this, alive]() {
182 BastionGuard::if_alive(alive, [this]() {
183 buffer->insert(buffer->end(), _("✅ Aggiornamento completato con successo.\n"));
184 updateButton.set_sensitive(true);
185 });
186 });
187
188 } catch (const Glib::Error& ex) {
189 Glib::signal_idle().connect_once([this, alive, ex]() {
190 BastionGuard::if_alive(alive, [this, &ex]() {
191 buffer->insert(buffer->end(),
192 Glib::ustring::compose(_("❌ Errore durante l'aggiornamento: %1\n"), ex.what()));
193 updateButton.set_sensitive(true);
194 });
195 });
196 }
197
198 }).detach();
199}
void updateFromExtraSources()
void logSafe(const std::string &msg)
void updateSaneSecurity()

References _, alive_, buffer, BastionGuard::if_alive(), logSafe(), bg_process::run(), updateButton, updateFromExtraSources(), and updateSaneSecurity().

Referenced by UpdatePage().

◆ saveSources()

void UpdatePage::saveSources ( )
private

Definition at line 477 of file UpdatePage.cpp.

477 {
478 std::string cfgPath = Glib::get_home_dir() + "/.config/BastionGuard";
479 auto dirFile = Gio::File::create_for_path(cfgPath);
480 if (!dirFile->query_exists()) {
481 dirFile->make_directory_with_parents();
482 }
483
484 std::ofstream file(cfgPath + "/sources.conf");
485 if (!file) return;
486
487 for (auto child : sourcesBox->get_children()) {
488 if (auto frame = dynamic_cast<Gtk::Frame*>(child)) {
489 auto inner = dynamic_cast<Gtk::Box*>(frame->get_child());
490 if (!inner) continue;
491
492 auto entries = inner->get_children();
493 if (entries.size() < 5) continue;
494
495 auto nameEntry = dynamic_cast<Gtk::Entry*>(entries[0]);
496 auto urlEntry = dynamic_cast<Gtk::Entry*>(entries[1]);
497 auto authSwitch= dynamic_cast<Gtk::CheckButton*>(entries[2]);
498 auto userEntry = dynamic_cast<Gtk::Entry*>(entries[3]);
499
500 file << nameEntry->get_text() << "|"
501 << urlEntry->get_text() << "|"
502 << (authSwitch->get_active() ? "1" : "0") << "|"
503 << userEntry->get_text() << "\n";
504 }
505 }
506}

References sourcesBox.

Referenced by onSaveSourcesClicked().

◆ storePassword()

bool UpdatePage::storePassword ( const std::string & key,
const std::string & password )
private

Definition at line 563 of file UpdatePage.cpp.

563 {
564 GError *error = nullptr;
565 gboolean res = secret_password_store_sync(
566 &BastionGuard_SCHEMA, SECRET_COLLECTION_DEFAULT,
567 ("BastionGuard source " + key).c_str(),
568 password.c_str(),
569 nullptr, &error,
570 "secret_key", key.c_str(),
571 nullptr
572 );
573 if (error) g_error_free(error);
574 return res == TRUE;
575}

References BastionGuard_SCHEMA.

Referenced by createSourceRow().

◆ testConnection()

void UpdatePage::testConnection ( const std::string & name,
const std::string & url,
bool auth,
const std::string & user )
private

Definition at line 524 of file UpdatePage.cpp.

524 {
525 if (url.empty()) {
526 buffer->insert(buffer->end(), _("⚠ Nessun URL specificato per ") + name + "\n");
527 return;
528 }
529
530 buffer->insert(buffer->end(), _("🔍 Test connessione a ") + url + "...\n");
531 std::string out, err;
532
533 std::vector<std::string> argv = {
534 "curl", "-I", "-s", "-o", "/dev/null", "-w", "%{http_code}"
535 };
536 if (auth) {
537 std::string pw = lookupPassword("BastionGuard:" + name);
538 if (pw.empty()) {
539 buffer->insert(buffer->end(), _("⚠ Nessuna password trovata per ") + name + "\n");
540 return;
541 }
542 argv.push_back("-u");
543 argv.push_back(user + ":" + pw);
544 }
545 argv.push_back("--url");
546 argv.push_back(url);
547
548 int status = -1;
549 Glib::spawn_sync("", argv, Glib::SpawnFlags::SEARCH_PATH,
550 {}, &out, &err, &status);
551 if (!err.empty())
552 buffer->insert(buffer->end(), _("Errore: ") + err + "\n");
553
554 if (!out.empty()) {
555 buffer->insert(buffer->end(), _("Risposta HTTP: ") + out + "\n");
556 if (out.find("200") != std::string::npos)
557 buffer->insert(buffer->end(), _("✅ Connessione riuscita.\n"));
558 else
559 buffer->insert(buffer->end(), _("❌ Connessione fallita o accesso negato.\n"));
560 }
561}

References _, buffer, and lookupPassword().

Referenced by createSourceRow().

◆ updateFromExtraSources()

void UpdatePage::updateFromExtraSources ( )
private

Definition at line 201 of file UpdatePage.cpp.

201 {
202 auto alive = alive_;
203 buffer->insert(buffer->end(), _("\n🔽 Scarico firme aggiuntive dalle sorgenti configurate...\n"));
204
205 // Snapshot the GTK widget values on the UI thread before starting a worker.
206 // A deferred idle callback here would capture a dead stack reference and
207 // race with the worker's copy of the source list.
208 std::vector<SourceItem> sources;
209 for (auto child : sourcesBox->get_children()) {
210 auto frame = dynamic_cast<Gtk::Frame*>(child);
211 if (!frame) continue;
212 auto section = dynamic_cast<Gtk::Box*>(frame->get_child());
213 if (!section) continue;
214 auto section_children = section->get_children();
215 if (section_children.size() < 2) continue;
216 auto revealer = dynamic_cast<Gtk::Revealer*>(section_children[1]);
217 if (!revealer) continue;
218 auto inner = dynamic_cast<Gtk::Box*>(revealer->get_child());
219 if (!inner) continue;
220 auto entries = inner->get_children();
221 if (entries.size() < 4) continue;
222 auto name = dynamic_cast<Gtk::Entry*>(entries[0]);
223 auto url = dynamic_cast<Gtk::Entry*>(entries[1]);
224 auto auth = dynamic_cast<Gtk::CheckButton*>(entries[2]);
225 auto username = dynamic_cast<Gtk::Entry*>(entries[3]);
226 if (!name || !url || !auth || !username) continue;
227
228 SourceItem source;
229 source.name = name->get_text();
230 source.url = url->get_text();
231 source.requires_auth = auth->get_active();
232 source.username = username->get_text();
233 if (!source.url.empty()) sources.push_back(std::move(source));
234 }
235
236 if (sources.empty()) {
237 buffer->insert(buffer->end(), _("Nessuna sorgente aggiuntiva configurata.\n"));
238 return;
239 }
240
241 std::thread([this, alive, sources = std::move(sources)]() {
242 bool all_ok = true;
243 for (const auto& src : sources) {
244 // Never allow an untrusted URL to select a local-file or other
245 // potentially unsafe curl protocol. No URL reaches a shell.
246 if (src.url.rfind("https://", 0) != 0) {
247 all_ok = false;
248 Glib::signal_idle().connect_once([this, alive, url = src.url]() {
249 BastionGuard::if_alive(alive, [this, &url]() {
250 buffer->insert(buffer->end(), _("⚠ Sorgente non HTTPS ignorata: ") + url + "\n");
251 });
252 });
253 continue;
254 }
255
256 std::string pw;
257 if (src.requires_auth)
258 pw = lookupPassword("BastionGuard:" + src.name);
259
260 std::vector<std::string> argv = {
261 "curl", "-f", "--proto", "=https", "--proto-redir", "=https"
262 };
263 if (src.requires_auth) {
264 argv.push_back("-u");
265 argv.push_back(src.username + ":" + pw);
266 }
267 argv.insert(argv.end(), {
268 "-O", "--output-dir", "/var/lib/clamav",
269 "--url", src.url
270 });
271
272 std::string out, err;
273 int status = -1;
274 try {
275 Glib::spawn_sync("", argv, Glib::SpawnFlags::SEARCH_PATH,
276 {}, &out, &err, &status);
277 } catch (const Glib::Error& ex) {
278 err = ex.what();
279 }
280 const bool ok = status == 0;
281 if (!ok) all_ok = false;
282 if (!alive->load()) return;
283
284 Glib::signal_idle().connect_once([this, alive, src, out, err, ok]() {
285 BastionGuard::if_alive(alive, [this, &src, &out, &err, ok]() {
286 buffer->insert(buffer->end(),
287 (ok ? _("⬇️ Scarico da: ") : _("❌ Download fallito: ")) + src.url + "\n");
288 if (!out.empty()) buffer->insert(buffer->end(), out + "\n");
289 if (!err.empty()) buffer->insert(buffer->end(), err + "\n");
290 });
291 });
292 }
293
294 Glib::signal_idle().connect_once([this, alive, all_ok]() {
295 BastionGuard::if_alive(alive, [this, all_ok]() {
296 buffer->insert(buffer->end(), all_ok
297 ? _("\n✅ Aggiornamento firme extra completato.\n")
298 : _("\n⚠ Aggiornamento firme extra incompleto: controllare gli errori.\n"));
299 });
300 });
301 }).detach();
302}

References _, alive_, buffer, BastionGuard::if_alive(), lookupPassword(), SourceItem::name, SourceItem::requires_auth, sourcesBox, SourceItem::url, and SourceItem::username.

Referenced by onUpdateClicked().

◆ updateSaneSecurity()

void UpdatePage::updateSaneSecurity ( )

Definition at line 612 of file UpdatePage.cpp.

612 {
613 auto push = [this, alive = alive_](const std::string& msg) {
614 Glib::signal_idle().connect_once([this, alive, msg]() {
615 BastionGuard::if_alive(alive, [this, &msg]() {
616 buffer->insert(buffer->end(), msg);
617 });
618 });
619 };
620
621 push(_("🌐 Aggiornamento database SaneSecurity (mirror sicuro)...\n"));
622
623 namespace fs = std::filesystem;
624 // Keep the sync unprivileged. mkdtemp ensures an exclusive 0700 directory.
625 char tmpl[] = "/tmp/bastionguard-sanesecurity-XXXXXX";
626 char* created = ::mkdtemp(tmpl);
627 if (!created) {
628 push(_("❌ Impossibile creare la directory temporanea di staging.\n"));
629 return;
630 }
631 const fs::path staging(created);
632 struct StagingGuard {
633 fs::path path;
634 ~StagingGuard() { std::error_code ec; fs::remove_all(path, ec); }
635 } cleanup{staging};
636
637 const auto sync = bg_process::run({"rsync", "-av", "--",
638 "rsync://rsync.sanesecurity.net/sanesecurity/", staging.string() + "/"},
639 true, 4U * 1024U * 1024U);
640 if (!sync.ok()) {
641 push(_("❌ Download SaneSecurity fallito. Nessun database modificato.\n"));
642 return;
643 }
644
645 const std::vector<std::string> extensions = {
646 ".ndb", ".hdb", ".ldb", ".yar", ".yara", ".ign2", ".txt"
647 };
648 std::size_t installed = 0;
649 std::error_code ec;
650 for (fs::directory_iterator it(staging, fs::directory_options::skip_permission_denied, ec),
651 end; !ec && it != end; it.increment(ec)) {
652 const fs::path path = it->path();
653 // Do not follow symlinks, directories, or special devices from a mirror.
654 if (!it->is_regular_file(ec) || it->is_symlink(ec)) { ec.clear(); continue; }
655 const std::string extension = path.extension().string();
656 if (std::find(extensions.begin(), extensions.end(), extension) == extensions.end())
657 continue;
658 const auto install = bg_process::run({"pkexec", "/usr/bin/install", "-m", "0644", "--",
659 path.string(), (fs::path("/var/lib/clamav") / path.filename()).string()}, true);
660 if (install.ok()) ++installed;
661 else push(_("⚠ Installazione firma fallita: ") + path.filename().string() + "\n");
662 }
663 if (installed == 0) push(_("⚠ Nessuna firma SaneSecurity installata.\n"));
664 else push(_("📦 Firme SaneSecurity installate: ") + std::to_string(installed) + "\n");
665}

References _, alive_, buffer, BastionGuard::if_alive(), and bg_process::run().

Referenced by onUpdateClicked().

Member Data Documentation

◆ addSourceButton

Gtk::Button UpdatePage::addSourceButton
private

Definition at line 45 of file UpdatePage.hpp.

Referenced by UpdatePage().

◆ alive_

◆ buffer

◆ outputView

Gtk::TextView UpdatePage::outputView
private

Definition at line 46 of file UpdatePage.hpp.

Referenced by UpdatePage().

◆ saneAutoSwitch

Gtk::Switch UpdatePage::saneAutoSwitch
private

Definition at line 50 of file UpdatePage.hpp.

Referenced by onSaneAutoToggled(), and UpdatePage().

◆ saveSourcesButton

Gtk::Button UpdatePage::saveSourcesButton
private

Definition at line 45 of file UpdatePage.hpp.

Referenced by UpdatePage().

◆ serviceSwitch

Gtk::Switch UpdatePage::serviceSwitch
private

Definition at line 43 of file UpdatePage.hpp.

Referenced by onServiceToggled(), and UpdatePage().

◆ sourcesBox

Gtk::Box* UpdatePage::sourcesBox = nullptr
private

Definition at line 49 of file UpdatePage.hpp.

Referenced by createSourceRow(), saveSources(), updateFromExtraSources(), and UpdatePage().

◆ sourcesLabel

Gtk::Label UpdatePage::sourcesLabel
private

Definition at line 44 of file UpdatePage.hpp.

Referenced by UpdatePage().

◆ sourcesNotebook

Gtk::Notebook* UpdatePage::sourcesNotebook = nullptr
private

Definition at line 48 of file UpdatePage.hpp.

Referenced by ~UpdatePage().

◆ updateButton

Gtk::Button UpdatePage::updateButton
private

Definition at line 42 of file UpdatePage.hpp.

Referenced by onUpdateClicked(), and UpdatePage().


The documentation for this class was generated from the following files: