53: Gtk::Box(Gtk::Orientation::VERTICAL, 12),
60 buffer = Gtk::TextBuffer::create();
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);
74 auto saneSwitchLabel = Gtk::make_managed<Gtk::Label>(
75 _(
"Aggiorna automaticamente le firme SaneSecurity (ogni 2 ore):")
82 auto saneBox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 8);
83 saneBox->append(*saneSwitchLabel);
89 sourcesBox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 4);
92 auto sourcesFrame = Gtk::make_managed<Gtk::Frame>();
93 sourcesFrame->set_label(
_(
"Sorgenti di aggiornamento aggiuntive"));
96 auto controlBox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 8);
99 auto scrolled = Gtk::make_managed<Gtk::ScrolledWindow>();
101 scrolled->set_expand(
true);
106 append(*sourcesFrame);
203 buffer->insert(
buffer->end(),
_(
"\n🔽 Scarico firme aggiuntive dalle sorgenti configurate...\n"));
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;
229 source.
name = name->get_text();
230 source.
url = url->get_text();
232 source.
username = username->get_text();
233 if (!source.
url.empty()) sources.push_back(std::move(source));
236 if (sources.empty()) {
237 buffer->insert(
buffer->end(),
_(
"Nessuna sorgente aggiuntiva configurata.\n"));
241 std::thread([
this, alive, sources = std::move(sources)]() {
243 for (
const auto& src : sources) {
246 if (src.url.rfind(
"https://", 0) != 0) {
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");
257 if (src.requires_auth)
260 std::vector<std::string> argv = {
261 "curl",
"-f",
"--proto",
"=https",
"--proto-redir",
"=https"
263 if (src.requires_auth) {
264 argv.push_back(
"-u");
265 argv.push_back(src.username +
":" + pw);
267 argv.insert(argv.end(), {
268 "-O",
"--output-dir",
"/var/lib/clamav",
272 std::string out, err;
275 Glib::spawn_sync(
"", argv, Glib::SpawnFlags::SEARCH_PATH,
276 {}, &out, &err, &status);
277 }
catch (
const Glib::Error& ex) {
280 const bool ok = status == 0;
281 if (!ok) all_ok =
false;
282 if (!alive->load())
return;
284 Glib::signal_idle().connect_once([
this, alive, src, out, err, ok]() {
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");
294 Glib::signal_idle().connect_once([
this, alive, 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"));
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);
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");
321 auto expandIcon = Gtk::make_managed<Gtk::Label>(
"▼");
322 expandIcon->set_margin_start(6);
323 expandIcon->set_css_classes({
"expand-icon" });
325 headerBox->append(*titleLabel);
326 headerBox->append(*expandIcon);
327 headerBox->set_margin(6);
328 headerBox->set_css_classes({
"source-header" });
330 auto innerBox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 6);
331 innerBox->set_css_classes({
"revealer-content" });
334 auto nameEntry = Gtk::make_managed<Gtk::Entry>();
335 nameEntry->set_placeholder_text(
_(
"Nome sorgente (es. Sanesecurity)"));
336 nameEntry->set_text(src.
name);
338 auto urlEntry = Gtk::make_managed<Gtk::Entry>();
339 urlEntry->set_placeholder_text(
_(
"https://example.com/firme.cvd"));
340 urlEntry->set_text(src.
url);
342 auto authSwitch = Gtk::make_managed<Gtk::CheckButton>(
_(
"_Richiede autenticazione"),
true);
345 auto usernameEntry = Gtk::make_managed<Gtk::Entry>();
346 usernameEntry->set_placeholder_text(
_(
"Username"));
347 usernameEntry->set_text(src.
username);
349 auto passwordEntry = Gtk::make_managed<Gtk::PasswordEntry>();
350 passwordEntry->property_placeholder_text() =
_(
"Password");
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");
359 buttonBox->append(*testButton);
360 buttonBox->append(*saveButton);
361 buttonBox->append(*deleteButton);
363 innerBox->append(*nameEntry);
364 innerBox->append(*urlEntry);
365 innerBox->append(*authSwitch);
366 innerBox->append(*usernameEntry);
367 innerBox->append(*passwordEntry);
368 innerBox->append(*buttonBox);
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);
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);
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;
386 bool currentlyOpen = revealer->get_reveal_child();
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() ==
"▼")
409 revealer->set_reveal_child(!currentlyOpen);
410 expandIcon->set_text(!currentlyOpen ?
"▲" :
"▼");
411 if (
auto header =
dynamic_cast<Gtk::Widget*
>(expandIcon->get_parent())) {
413 header->add_css_class(
"open");
415 header->remove_css_class(
"open");
419 headerBox->add_controller(click);
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");
429 titleLabel->set_text(
"📦 " + newName);
430 titleLabel->remove_css_class(
"suggested-action");
431 titleLabel->add_css_class(
"highlight");
435 testButton->signal_clicked().connect([
this, nameEntry, urlEntry, authSwitch, usernameEntry, passwordEntry]() {
436 std::string pw = passwordEntry->get_text();
440 authSwitch->get_active(), usernameEntry->get_text());
443 saveButton->signal_clicked().connect([
this, nameEntry, passwordEntry]() {
444 std::string key =
"BastionGuard:" + nameEntry->get_text();
449 deleteButton->signal_clicked().connect([
this, frame]() {
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);
466 revealer->set_reveal_child(
true);
467 expandIcon->set_text(
"▲");
613 auto push = [
this, alive =
alive_](
const std::string& msg) {
614 Glib::signal_idle().connect_once([
this, alive, msg]() {
621 push(
_(
"🌐 Aggiornamento database SaneSecurity (mirror sicuro)...\n"));
623 namespace fs = std::filesystem;
625 char tmpl[] =
"/tmp/bastionguard-sanesecurity-XXXXXX";
626 char* created = ::mkdtemp(tmpl);
628 push(
_(
"❌ Impossibile creare la directory temporanea di staging.\n"));
631 const fs::path staging(created);
632 struct StagingGuard {
634 ~StagingGuard() { std::error_code ec; fs::remove_all(path, ec); }
638 "rsync://rsync.sanesecurity.net/sanesecurity/", staging.string() +
"/"},
639 true, 4U * 1024U * 1024U);
641 push(
_(
"❌ Download SaneSecurity fallito. Nessun database modificato.\n"));
645 const std::vector<std::string> extensions = {
646 ".ndb",
".hdb",
".ldb",
".yar",
".yara",
".ign2",
".txt"
648 std::size_t installed = 0;
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();
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())
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");
663 if (installed == 0) push(
_(
"⚠ Nessuna firma SaneSecurity installata.\n"));
664 else push(
_(
"📦 Firme SaneSecurity installate: ") + std::to_string(installed) +
"\n");
static const std::vector< std::string > sane_files
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)