11#include <glibmm/i18n.h>
12#include <glibmm/miscutils.h>
13#include <gdkmm/pixbuf.h>
27#define _(text) gettext(text)
29#ifndef BSC_SERVICECTL_PATH
30#define BSC_SERVICECTL_PATH "/usr/libexec/bastionguard/bastionguard-service"
38 const char* object_path,
39 const char* interface_name)
41 GError* error =
nullptr;
42 GDBusConnection* conn = g_bus_get_sync(G_BUS_TYPE_SESSION,
nullptr, &error);
44 if (error) g_error_free(error);
48 GVariant* reply = g_dbus_connection_call_sync(
55 G_VARIANT_TYPE(
"(b)"),
56 G_DBUS_CALL_FLAGS_NONE,
64 if (error) g_error_free(error);
68 gboolean active = FALSE;
69 g_variant_get(reply,
"(b)", &active);
70 g_variant_unref(reply);
71 return active == TRUE;
82 static constexpr Candidate candidates[] = {
83 {
"org.freedesktop.ScreenSaver",
"/org/freedesktop/ScreenSaver",
"org.freedesktop.ScreenSaver"},
84 {
"org.gnome.ScreenSaver",
"/org/gnome/ScreenSaver",
"org.gnome.ScreenSaver"},
85 {
"org.cinnamon.ScreenSaver",
"/org/cinnamon/ScreenSaver",
"org.cinnamon.ScreenSaver"},
86 {
"org.mate.ScreenSaver",
"/org/mate/ScreenSaver",
"org.mate.ScreenSaver"},
89 for (
const auto& c : candidates) {
108 "Name=BastionGuard Secure Connection\n"
109 "Name[it]=BastionGuard Connessione Sicura\n"
110 "Comment=Interactive application firewall\n"
111 "Comment[it]=Firewall applicativo interattivo\n"
112 "Exec=bastionguard-sc-ui --start-minimized\n"
113 "Icon=/usr/share/bastionguard-sc/data/logo.svg\n"
116 "Categories=Network;Security;System;\n"
117 "Keywords=firewall;network;security;connection;\n"
118 "StartupNotify=true\n");
124 const std::filesystem::path cfgDir =
125 std::filesystem::path(Glib::get_user_config_dir()) /
"bastionguard-sc";
126 std::filesystem::create_directories(cfgDir);
128 std::ofstream out(cfgDir /
"service-control.ini", std::ios::trunc);
129 out <<
"[ServiceControl]\n";
130 out <<
"enabled=" << (enabled ?
"true" :
"false") <<
"\n";
131 out <<
"autostart_enabled=" << (enabled ?
"true" :
"false") <<
"\n";
132 out <<
"daemon_enabled=" << (enabled ?
"true" :
"false") <<
"\n";
136 out <<
"generated_autostart_file=" << (cfgDir /
"bastionguard-sc-autostart.desktop").
string() <<
"\n";
137 out <<
"backup_path=" << backupFile <<
"\n";
138 return static_cast<bool>(out);
147 std::filesystem::create_directories(path.parent_path());
148 std::ofstream out(path, std::ios::trunc);
150 return static_cast<bool>(out);
158 std::string out =
"'";
160 if (c ==
'\'') out +=
"'\\''";
169 const int rc = std::system(cmd.c_str());
175 for (
const auto& p : candidates) {
176 if (std::filesystem::exists(p))
return p;
193 set_default_size(1158, 803);
194 set_size_request(1158, 803);
196 m_configFile = Glib::get_user_config_dir() +
"/bastionguard-sc/settings.ini";
216 m_stack.set_transition_type(Gtk::StackTransitionType::SLIDE_LEFT_RIGHT);
217 m_stack.set_transition_duration(220);
233 auto outerBox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 0);
236 set_child(*outerBox);
255 if (is_visible() && is_active()) {
259 m_stack.set_transition_type(Gtk::StackTransitionType::NONE);
260 m_stack.set_visible_child(
"connections");
261 m_stack.set_transition_type(Gtk::StackTransitionType::SLIDE_LEFT_RIGHT);
281 if (
auto app = get_application()) {
315 std::filesystem::create_directories(
319 f <<
"dark_mode=" << (
m_darkMode ?
"true" :
"false") <<
"\n";
325 }
catch (
const std::exception& e) {
326 std::cerr <<
"⚠ Impossibile salvare config: " << e.what() <<
"\n";
340 while (std::getline(f, line)) {
341 if (line.find(
"dark_mode=true") != std::string::npos)
345 const std::string langKey =
"language=";
346 auto pos = line.find(langKey);
347 if (pos != std::string::npos)
348 m_language = line.substr(pos + langKey.size());
350 if (line.find(
"protection_enabled=false") != std::string::npos)
353 const std::string backupKey =
"autostart_backup=";
354 pos = line.find(backupKey);
355 if (pos != std::string::npos)
362 const std::filesystem::path serviceControlFile =
363 std::filesystem::path(
m_configFile).parent_path() /
"service-control.ini";
364 std::ifstream sc(serviceControlFile);
365 bool sawServiceControlState =
false;
366 bool serviceControlEnabled =
true;
368 while (std::getline(sc, line)) {
369 if (line ==
"enabled=true" || line ==
"autostart_enabled=true" || line ==
"daemon_enabled=true") {
370 sawServiceControlState =
true;
372 if (line ==
"enabled=false" || line ==
"autostart_enabled=false" || line ==
"daemon_enabled=false") {
373 sawServiceControlState =
true;
374 serviceControlEnabled =
false;
377 const std::string backupPathKey =
"backup_path=";
378 auto backupPathPos = line.find(backupPathKey);
379 if (backupPathPos == 0) {
383 if (sawServiceControlState) {
402 ::setenv(
"LANGUAGE", lang.c_str(), 1);
403 ::setenv(
"LANG", (lang +
".UTF-8").c_str(), 1);
404 ::setlocale(LC_ALL, (lang +
".UTF-8").c_str());
406 ::unsetenv(
"LANGUAGE");
407 ::setlocale(LC_ALL,
"");
411 ::textdomain(
"bastionguard-sc");
421 const std::string chosen =
m_langDialog->selectedLanguage();
425 setStatus(
_(
"Lingua aggiornata — riavvia per applicare la modifica."));
437 const std::string cssFile = dark ?
resource(
"BSC-dark.css") :
resource(
"BSC.css");
440 Gtk::StyleContext::add_provider_for_display(
443 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
444 }
catch (
const Glib::Error& e) {
445 std::cerr <<
"⚠ CSS: " << e.what() <<
"\n";
457 m_btnLight->get_style_context()->remove_class(
"theme-btn-active");
458 m_btnDark->get_style_context()->add_class(
"theme-btn-active");
460 m_btnDark->get_style_context()->remove_class(
"theme-btn-active");
461 m_btnLight->get_style_context()->add_class(
"theme-btn-active");
472 auto header = Gtk::make_managed<Gtk::HeaderBar>();
473 header->set_show_title_buttons(
false);
474 header->set_decoration_layout(
"");
475 header->get_style_context()->add_class(
"custom-headerbar");
477 auto headerLeft = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
478 headerLeft->set_size_request(240, -1);
479 headerLeft->set_hexpand(
false);
480 headerLeft->get_style_context()->add_class(
"header-left");
481 header->pack_start(*headerLeft);
483 auto spacer = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
484 spacer->set_hexpand(
true);
485 header->pack_start(*spacer);
487 auto headerRight = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 4);
488 headerRight->set_hexpand(
false);
489 headerRight->get_style_context()->add_class(
"header-right");
492 auto btnLang = Gtk::make_managed<Gtk::Button>(
"🌐");
493 btnLang->get_style_context()->add_class(
"theme-btn");
494 btnLang->set_tooltip_text(
_(
"Cambia lingua"));
495 btnLang->signal_clicked().connect(
497 headerRight->append(*btnLang);
500 m_btnLight = Gtk::make_managed<Gtk::Button>(
"☀");
501 m_btnDark = Gtk::make_managed<Gtk::Button>(
"🌙");
503 m_btnLight->get_style_context()->add_class(
"theme-btn");
504 m_btnDark->get_style_context()->add_class(
"theme-btn");
510 m_btnDark->get_style_context()->add_class(
"theme-btn-active");
512 m_btnLight->get_style_context()->add_class(
"theme-btn-active");
520 auto addWinBtn = [&](
const std::string& iconRes,
521 const std::string& fallback,
524 auto btn = Gtk::make_managed<Gtk::Button>();
525 btn->get_style_context()->add_class(
"header-button");
528 auto pix = Gdk::Pixbuf::create_from_file(
resource(iconRes), 32, 32,
true);
529 btn->set_child(*Gtk::make_managed<Gtk::Image>(pix));
531 btn->set_label(fallback);
534 btn->signal_clicked().connect(cb);
535 headerRight->append(*btn);
538 addWinBtn(
"icon-minimize.png",
"—", [
this]() {
minimize(); });
539 addWinBtn(
"icon-close.png",
"✕", [
this]() { hide(); });
541 header->pack_end(*headerRight);
542 set_titlebar(*header);
551 auto sidebar = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 10);
552 sidebar->set_size_request(240, -1);
553 sidebar->set_hexpand(
false);
554 sidebar->set_vexpand(
true);
555 sidebar->get_style_context()->add_class(
"sidebar");
559 auto hdr = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 8);
560 hdr->set_hexpand(
false);
561 hdr->get_style_context()->add_class(
"sidebar-header");
563 auto ico = Gtk::make_managed<Gtk::Image>();
565 auto pix = Gdk::Pixbuf::create_from_file(
566 resource(
"logo-sidebar.png"), 32, 32,
true);
569 ico->set_from_icon_name(
"security-high-symbolic");
570 ico->set_pixel_size(28);
573 ico->set_hexpand(
false);
576 auto lbl = Gtk::make_managed<Gtk::Label>(
577 "BastionGuard™\nSecure Connection");
578 lbl->set_justify(Gtk::Justification::LEFT);
580 lbl->set_max_width_chars(16);
581 lbl->set_width_chars(16);
582 lbl->get_style_context()->add_class(
"sidebar-title");
583 lbl->set_halign(Gtk::Align::START);
584 lbl->set_hexpand(
false);
587 sidebar->append(*hdr);
591 resource(
"icons/connections.svg"),
_(
"Connessioni"),
"connections"));
593 resource(
"icons/apps.svg"),
_(
"Applicazioni"),
"apps"));
595 resource(
"icons/network.svg"),
_(
"Monitor di rete"),
"network"));
597 resource(
"icons/rules.svg"),
_(
"Regole"),
"rules"));
599 resource(
"icons/stats.svg"),
_(
"Statistiche"),
"stats"));
601 resource(
"icons/about.svg"),
_(
"Impostazioni"),
"settings"));
604 resource(
"icons/about.svg"),
_(
"Informazioni"),
"about"));
606 auto spacer = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL);
607 spacer->set_vexpand(
true);
608 sidebar->append(*spacer);
610 auto sep = Gtk::make_managed<Gtk::Separator>(Gtk::Orientation::HORIZONTAL);
611 sep->get_style_context()->add_class(
"sidebar-separator");
612 sidebar->append(*sep);
614 m_daemonLabel = Gtk::make_managed<Gtk::Label>(
_(
"⚙ Daemon: in attesa..."));
615 m_daemonLabel->get_style_context()->add_class(
"daemon-status");
626 const std::string& label,
627 const std::string& pageName)
629 auto box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 6);
631 auto pic = Gtk::make_managed<Gtk::Picture>();
633 auto pb = Gdk::Pixbuf::create_from_file(iconPath);
634 auto scaled = pb->scale_simple(16, 16, Gdk::InterpType::BILINEAR);
635 auto tex = Gdk::Texture::create_for_pixbuf(scaled);
636 pic->set_paintable(tex);
637 pic->set_content_fit(Gtk::ContentFit::FILL);
640 auto iconBox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL);
641 iconBox->set_size_request(16, 16);
642 iconBox->set_hexpand(
false);
643 iconBox->set_valign(Gtk::Align::CENTER);
644 iconBox->append(*pic);
645 box->append(*iconBox);
647 auto lbl = Gtk::make_managed<Gtk::Label>(label);
648 lbl->set_halign(Gtk::Align::START);
649 lbl->set_hexpand(
false);
652 auto btn = Gtk::make_managed<Gtk::Button>();
653 btn->set_child(*box);
654 btn->set_hexpand(
true);
655 btn->get_style_context()->add_class(
"sidebar-button");
658 btn->signal_clicked().connect([
this, btn, pageName]() {
660 static const std::vector<std::string> pageOrder = {
661 "connections",
"apps",
"network",
"rules",
"stats",
"settings",
"about"
663 const std::string currentPage =
m_stack.get_visible_child_name();
664 auto itCur = std::find(pageOrder.begin(), pageOrder.end(), currentPage);
665 auto itDst = std::find(pageOrder.begin(), pageOrder.end(), pageName);
666 if (itCur != pageOrder.end() && itDst != pageOrder.end()) {
668 m_stack.set_transition_type(Gtk::StackTransitionType::SLIDE_LEFT);
669 else if (itDst < itCur)
670 m_stack.set_transition_type(Gtk::StackTransitionType::SLIDE_RIGHT);
674 m_stack.set_visible_child(pageName);
683 b->get_style_context()->remove_class(
"active-sidebar-button");
686 btn->get_style_context()->add_class(
"active-sidebar-button");
700 auto install_activity_controller = [
this](GtkEventController* controller) {
701 gtk_event_controller_set_propagation_phase(controller, GTK_PHASE_CAPTURE);
702 gtk_widget_add_controller(GTK_WIDGET(gobj()), controller);
705 auto* motion = gtk_event_controller_motion_new();
709 G_CALLBACK(+[](GtkEventControllerMotion*,
double,
double, gpointer data) {
713 install_activity_controller(GTK_EVENT_CONTROLLER(motion));
715 auto* click = gtk_gesture_click_new();
719 G_CALLBACK(+[](GtkGestureClick*,
int,
double,
double, gpointer data) {
723 install_activity_controller(GTK_EVENT_CONTROLLER(click));
725 auto* key = gtk_event_controller_key_new();
729 G_CALLBACK(+[](GtkEventControllerKey*, guint, guint, GdkModifierType, gpointer data) -> gboolean {
731 return GDK_EVENT_PROPAGATE;
734 install_activity_controller(GTK_EVENT_CONTROLLER(key));
741 +[](gpointer data) -> gboolean {
753 const auto screensaver_active = queryScreensaverActive();
757 const bool desktop_active = screensaver_active ? !*screensaver_active :
true;
775 setStatus(
_(
"⏸ Desktop inattivo: monitoraggio connessioni sospeso"));
794 setStatus(
_(
"⚠ Daemon disconnesso — in attesa..."));
809 const std::string& host,
810 const std::string& ip)
812 return app +
"|" + host +
"|" + ip;
824 const std::string esito = allowed
825 ? std::string{
"✅ "} +
_(
"Consentito (sempre)")
826 : std::string{
"❌ "} +
_(
"Negato (sempre)");
841 setStatus(esito +
" — " + displayNameForStatus(info));
877 std::string text = alert.
text.empty() ?
_(
"Alert dal daemon") : alert.
text;
882 constexpr int kWhatConnection = 3;
883 constexpr int kTypeWarning = 1;
885 const bool is_connection_alert = alert.
what == kWhatConnection && alert.
has_connection;
887 if (is_connection_alert) {
891 if (alert.
type == kTypeWarning) {
893 esito =
_(
"❌ Negato");
897 esito =
_(
"✅ Consentito");
924 esito = std::string{
"✅ "} +
_(
"Consentito (una volta)");
930 esito = std::string{
"✅ "} +
_(
"Consentito (sempre)");
936 esito = std::string{
"❌ "} +
_(
"Negato (una volta)");
942 esito = std::string{
"❌ "} +
_(
"Negato (sempre)");
968 setStatus(esito +
" — " + displayNameForStatus(info));
984 const std::filesystem::path cfgDir = std::filesystem::path(
m_configFile).parent_path();
985 const std::filesystem::path generatedAutostart = cfgDir /
"bastionguard-sc-autostart.desktop";
986 const std::string backupFile = kSystemAutostartBackupDesktop;
990 bool ok = !enabled || writeConfigAutostartDesktop(generatedAutostart);
994 std::ostringstream script;
995 script <<
"set -eu; ";
1000 script <<
"if [ -f " << shellQuote(kSystemAutostartDesktop) <<
" ]; then "
1001 <<
"install -m 0644 " << shellQuote(kSystemAutostartDesktop) <<
" " << shellQuote(backupFile) <<
"; fi; ";
1002 script <<
"if [ ! -f " << shellQuote(backupFile) <<
" ] && [ -f " << shellQuote(kFallbackSystemAutostartDesktop) <<
" ]; then "
1003 <<
"install -m 0644 " << shellQuote(kFallbackSystemAutostartDesktop) <<
" " << shellQuote(backupFile) <<
"; fi; ";
1004 script <<
"rm -f " << shellQuote(kSystemAutostartDesktop) <<
" " << shellQuote(kFallbackSystemAutostartDesktop) <<
"; ";
1005 script <<
"test -x " << shellQuote(kServiceCtlPath) <<
"; ";
1006 script << shellQuote(kServiceCtlPath) <<
" --system disable --now "
1007 << shellQuote(kServiceName) <<
"; ";
1009 script <<
"mkdir -p " << shellQuote(std::filesystem::path(kSystemAutostartDesktop).parent_path().
string()) <<
"; ";
1010 script <<
"if [ -f " << shellQuote(backupFile) <<
" ]; then "
1011 <<
"install -D -m 0644 " << shellQuote(backupFile) <<
" " << shellQuote(kSystemAutostartDesktop) <<
"; "
1012 <<
"else install -D -m 0644 " << shellQuote(generatedAutostart.string()) <<
" " << shellQuote(kSystemAutostartDesktop) <<
"; fi; ";
1013 script <<
"rm -f " << shellQuote(kFallbackSystemAutostartDesktop) <<
"; ";
1014 script <<
"test -x " << shellQuote(kServiceCtlPath) <<
"; ";
1015 script << shellQuote(kServiceCtlPath) <<
" --system enable --now "
1016 << shellQuote(kServiceName) <<
"; ";
1019 ok = runCommand(
"pkexec /bin/sh -c " + shellQuote(script.str()));
1023 ok = writeServiceControlPreference(enabled, backupFile);
1025 status =
_(
"Operazione di sistema completata, ma salvataggio della preferenza in ~/.config non riuscito.");
1034 ?
_(
"Avvio persistente attivato: autostart_enabled=true e daemon_enabled=true salvati, autostart installato e bsc-daemon abilitato.")
1035 :
_(
"Protezione disattivata: enabled=false, autostart_enabled=false e daemon_enabled=false salvati, bsc-daemon fermato/disabilitato e autostart rimosso. Backup creato in /etc/xdg/autostart.");
1036 }
else if (status.empty()) {
1038 ?
_(
"Riattivazione non completata. Controlla PolicyKit, il gestore servizi, il backup in /etc/xdg/autostart e il salvataggio config.")
1039 :
_(
"Disattivazione non completata. Controlla PolicyKit, il gestore servizi, il backup in /etc/xdg/autostart, bsc-daemon e il salvataggio config.");
1050 const bool serviceEnabled = runCommand(
1051 shellQuote(kServiceCtlPath) +
" --system is-enabled " +
1052 shellQuote(kServiceName) +
" >/dev/null 2>&1");
1053 const bool autostartPresent = std::filesystem::exists(kSystemAutostartDesktop) ||
1054 std::filesystem::exists(kFallbackSystemAutostartDesktop);
1057 text =
_(
"Protezione attiva nelle impostazioni utente.");
1059 text =
_(
"Protezione disattivata nelle impostazioni utente.");
1061 text += std::string(
" ") + (serviceEnabled ?
_(
"Servizio di sistema abilitato.") :
_(
"Servizio di sistema disabilitato."));
1062 text += std::string(
" ") + (autostartPresent ?
_(
"Avvio automatico presente.") :
_(
"Avvio automatico assente."));
1064 text += std::string(
" ") +
_(
"Backup autostart disponibile.");
void noteConnection(const ConnectionInfo &info, const std::string &verdict)
void setLastVerdict(const std::string &verdict)
void onNewConnection(const ConnectionInfo &info, bool pending=true)
ConnectionsPage m_connectionsPage
void applyLanguage(const std::string &lang)
static std::string cacheKey(const std::string &app, const std::string &host, const std::string &ip)
Gtk::Button * makeSidebarButton(const std::string &iconPath, const std::string &label, const std::string &pageName)
void onVerdict(Verdict v, const ConnectionInfo &info)
bool on_close_request() override
~MainWindow() override=default
void refreshSystemIntegrationStatus()
std::map< std::string, ReplyAction > m_verdictCache
void onDaemonDisconnected()
void applyProtectionEnabled(bool enabled)
void applyTheme(bool dark)
void activateSidebarButton(Gtk::Button *btn)
void showLanguageDialog()
std::unique_ptr< LanguageDialog > m_langDialog
SettingsPage m_settingsPage
std::unique_ptr< AlertDialog > m_alertDialog
Gtk::Label * m_daemonLabel
void setStatus(const std::string &msg)
void noteDesktopActivity()
void onDaemonAlert(const AlertInfo &alert)
Gtk::Statusbar m_statusbar
std::chrono::steady_clock::time_point m_lastUiActivity
bool pollDesktopActivity()
guint m_desktopActivityTimer
Gtk::Button * m_activeButton
void loadTheme(bool dark)
std::string m_autostartBackupFile
void sendDesktopActivityState(bool active, bool force=false)
NetworkPage m_networkPage
void startDesktopActivityMonitor()
Glib::RefPtr< Gtk::CssProvider > m_cssProvider
void onNewConnection(const ConnectionInfo &info)
std::vector< Gtk::Button * > m_sidebarButtons
void noteConnection(const ConnectionInfo &info)
void setProtectionEnabled(bool enabled)
sigc::signal< void(bool)> & signal_toggle_protection_requested()
void setSystemIntegrationStatus(const std::string &text)
void updateStats(uint64_t allowed, uint64_t denied, uint64_t total)
void set_on_quit(std::function< void()> cb)
void set_on_make_startup_persistent(std::function< void()> cb)
void set_tooltip(const std::string &text)
void set_on_activate(std::function< void()> cb)
sigc::signal< void(const ConnectionInfo &)> signal_new_connection
sigc::signal< void()> signal_daemon_disconnected
void sendReply(ReplyAction action)
void queueNotification(int type, const std::string &data={})
sigc::signal< void(const AlertInfo &)> signal_alert
sigc::signal< void()> signal_daemon_connected
void start(const std::string &socketPath="unix:///tmp/bsd-daemon.sock")
constexpr const char * kSystemAutostartDesktop
bool runCommand(const std::string &cmd)
std::string autostartDesktopContent()
std::string shellQuote(const std::string &s)
std::optional< bool > queryScreensaverActive()
constexpr int kNotifDesktopIdle
constexpr const char * kUISocketPath
constexpr const char * kServiceCtlPath
std::optional< bool > queryScreensaverActiveOnce(const char *bus_name, const char *object_path, const char *interface_name)
constexpr const char * kFallbackSystemAutostartDesktop
constexpr int kNotifDesktopActive
constexpr const char * kServiceName
std::string firstExistingPath(const std::initializer_list< std::string > &candidates)
constexpr const char * kSystemAutostartBackupDesktop
bool writeServiceControlPreference(bool enabled, const std::string &backupFile)
std::string displayNameForStatus(const ConnectionInfo &info)
bool writeConfigAutostartDesktop(const std::filesystem::path &path)
std::string best_connection_display_name(const std::string &app_comm, const std::string &app_path, const std::vector< std::string > &app_args, const std::string &dst_ip, const std::string &src_ip)
std::string resource(const std::string &rel)
ConnectionInfo connection
std::vector< std::string > app_args
#define BSC_SERVICECTL_PATH