Skip to content
API Documentation
BastionGuard™
AlertDialog.cpp
Go to the documentation of this file.
1/*
2 * BastionGuard™ Secure Connection
3 * Copyright (C) 2025–2026 Calogero Scarnà
4 * GPL-3.0 — BastionGuard™ is a trademark of Calogero Scarnà.
5 */
6
7#include "AlertDialog.hpp"
10#include <glibmm/i18n.h>
11#include <glibmm/main.h>
12#include <gdkmm/pixbuf.h>
13#include <vector>
14#include "../Resource.hpp"
15#include <libintl.h>
16
17#define _(text) gettext(text)
18#define N_(text) text
19
20AlertDialog::AlertDialog(const std::string& appComm,
21 const std::string& appPath,
22 const std::vector<std::string>& appArgs,
23 const std::string& destHost,
24 const std::string& destIp,
25 uint32_t destPort,
26 int timeoutSecs)
27: Gtk::Window()
28, m_timeoutSecs(timeoutSecs)
29, m_remaining(timeoutSecs)
30{
31 set_title("BastionGuard — Connessione rilevata");
32 set_resizable(false);
33 set_modal(false);
34 set_deletable(false);
35 set_default_size(520, -1);
36
37 // Aggiungi una classe CSS dedicata cosi' lo stylesheet del tema (BSC.css /
38 // BSC-dark.css) puo' personalizzare il dialog in modo coerente con il
39 // resto della UI.
40 get_style_context()->add_class("bsc-alert-dialog");
41
43 buildUI(appComm, appPath, appArgs, destHost, destIp, destPort);
45}
46
48{
49 m_timerConn.disconnect();
50}
51
52
53// ---------------------------------------------------------------------------
54// HeaderBar identica a MainWindow
55// ---------------------------------------------------------------------------
56
58{
59 auto header = Gtk::make_managed<Gtk::HeaderBar>();
60 header->set_show_title_buttons(false);
61 header->set_decoration_layout("");
62 header->get_style_context()->add_class("custom-headerbar");
63
64 // Titolo centrato
65 auto titleLabel = Gtk::make_managed<Gtk::Label>();
66 titleLabel->set_markup("<b>" + Glib::ustring(_("Connessione rilevata")) + "</b>");
67 titleLabel->get_style_context()->add_class("headerbar-title");
68 header->set_title_widget(*titleLabel);
69
70 // Pulsanti destra: solo chiudi (non minimize — è un dialogo modale)
71 auto headerRight = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 4);
72 headerRight->get_style_context()->add_class("header-right");
73
74 auto closeBtn = Gtk::make_managed<Gtk::Button>();
75 closeBtn->get_style_context()->add_class("header-button");
76 try {
77 auto pix = Gdk::Pixbuf::create_from_file(resource("icon-close.png"), 32, 32, true);
78 closeBtn->set_child(*Gtk::make_managed<Gtk::Image>(pix));
79 } catch (...) {
80 closeBtn->set_label("✕");
81 }
82 // Chiudi = nega una volta (comportamento sicuro di default)
83 closeBtn->signal_clicked().connect([this]() { emitVerdict(Verdict::DENY_ONCE); });
84 headerRight->append(*closeBtn);
85
86 header->pack_end(*headerRight);
87 set_titlebar(*header);
88}
89
90// ---------------------------------------------------------------------------
91// UI
92// ---------------------------------------------------------------------------
93
94void AlertDialog::buildUI(const std::string& appComm,
95 const std::string& appPath,
96 const std::vector<std::string>& appArgs,
97 const std::string& destHost,
98 const std::string& destIp,
99 uint32_t destPort)
100{
101 m_vbox.set_margin_top(20);
102 m_vbox.set_margin_bottom(20);
103 m_vbox.set_margin_start(20);
104 m_vbox.set_margin_end(20);
105
106 // ── Icona e nome applicazione ─────────────────────────────────────
107 auto topBox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 12);
108
109 const bool safe_local_connection = bsc::ui_util::is_safe_local_connection(destIp);
110 const std::string display_name = bsc::ui_util::best_connection_display_name(
111 appComm, appPath, appArgs, destIp);
112 const std::string icon_name = bsc::ui_util::best_connection_icon_name(
113 appComm, appPath, appArgs, destIp);
114
115 m_appIcon.set_from_icon_name(icon_name.empty()
116 ? "application-x-executable"
117 : icon_name);
118 m_appIcon.set_pixel_size(48);
119 m_appIcon.set_valign(Gtk::Align::START);
120 topBox->append(m_appIcon);
121
122 auto nameBox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 4);
123
124 m_appName.set_markup("<b><big>" + Glib::Markup::escape_text(display_name) + "</big></b>");
125 m_appName.set_halign(Gtk::Align::START);
126 m_appName.set_wrap(true);
127 m_appName.get_style_context()->add_class("bsc-alert-app-name");
128 nameBox->append(m_appName);
129
130 // Path completo del binario - dato secondario per l'utente esperto
131 if (!appPath.empty()) {
132 m_appPath.set_markup("<small><span foreground='gray'>" +
133 Glib::Markup::escape_text(appPath) + "</span></small>");
134 m_appPath.set_halign(Gtk::Align::START);
135 m_appPath.set_wrap(true);
136 m_appPath.set_ellipsize(Pango::EllipsizeMode::MIDDLE);
137 nameBox->append(m_appPath);
138 }
139
140 topBox->append(*nameBox);
141 m_vbox.append(*topBox);
142
143 // ── Card info connessione: tre righe separate ──────────────────────
144 m_infoBox.set_margin_top(4);
145 m_infoBox.get_style_context()->add_class("card");
146 m_infoBox.get_style_context()->add_class("bsc-alert-info");
147
148 // 🌐 Sito - hostname risolto (vuoto se reverse DNS non l'ha trovato)
149 const std::string host_text = destHost.empty()
150 ? (safe_local_connection
151 ? std::string{_("Connessione locale sicura")}
152 : std::string{_("(non risolto)")})
153 : destHost;
154 m_hostLabel.set_markup(
155 Glib::ustring(_("🌐 <b>Sito:</b> ")) + Glib::Markup::escape_text(host_text));
156 m_hostLabel.set_halign(Gtk::Align::START);
157 m_hostLabel.set_selectable(true);
158 m_infoBox.append(m_hostLabel);
159
160 // 📍 IP - sempre presente (e' quello dei pacchetti)
161 m_ipLabel.set_markup(
162 Glib::ustring(_("📍 <b>IP:</b> ")) + Glib::Markup::escape_text(destIp));
163 m_ipLabel.set_halign(Gtk::Align::START);
164 m_ipLabel.set_selectable(true);
165 m_infoBox.append(m_ipLabel);
166
167 // 🔌 Porta
168 m_portLabel.set_markup(
169 Glib::ustring(_("🔌 <b>Porta:</b> ")) + Glib::Markup::escape_text(std::to_string(destPort)));
170 m_portLabel.set_halign(Gtk::Align::START);
171 m_infoBox.append(m_portLabel);
172
173 // ── Hint proxy ──────────────────────────────────────────────────────
174 if (bsc::ui_util::matches_configured_proxy(destHost.empty() ? destIp : destHost,
175 static_cast<std::uint16_t>(destPort))) {
176 auto proxyHint = Gtk::make_managed<Gtk::Label>();
177 proxyHint->set_markup(
178 "<span foreground='#2e7d32'>" +
179 Glib::ustring(_("🛡 Connessione verso il tuo proxy configurato")) +
180 "</span>");
181 proxyHint->set_halign(Gtk::Align::START);
182 proxyHint->set_wrap(true);
183 m_infoBox.append(*proxyHint);
184 }
185
186 if (safe_local_connection) {
187 auto localHint = Gtk::make_managed<Gtk::Label>();
188 localHint->set_markup(
189 "<span foreground='#2e7d32'>" +
190 Glib::ustring(_("✅ Traffico locale/loopback del sistema: non è una connessione Internet esterna")) +
191 "</span>");
192 localHint->set_halign(Gtk::Align::START);
193 localHint->set_wrap(true);
194 m_infoBox.append(*localHint);
195 }
196
197 m_vbox.append(m_infoBox);
198
199 // ── Countdown ────────────────────────────────────────────────────────
200 m_countdown.set_show_text(true);
201 m_vbox.append(m_countdown);
202
203 // ── Pulsanti ─────────────────────────────────────────────────────────
204 // Label dei pulsanti tradotte tramite gettext
205 m_allowOnce.set_label(_("✅ Consenti una volta"));
206 m_allowAlways.set_label(_("🔓 Consenti sempre"));
207 m_denyOnce.set_label(_("❌ Nega una volta"));
208 m_denyAlways.set_label(_("🔒 Nega sempre"));
209
210 m_allowOnce.get_style_context()->add_class("suggested-action");
211 m_denyAlways.get_style_context()->add_class("destructive-action");
212
213 m_allowOnce.signal_clicked().connect(
214 [this]() { emitVerdict(Verdict::ALLOW_ONCE); });
215 m_allowAlways.signal_clicked().connect(
216 [this]() { emitVerdict(Verdict::ALLOW_ALWAYS); });
217 m_denyOnce.signal_clicked().connect(
218 [this]() { emitVerdict(Verdict::DENY_ONCE); });
219 m_denyAlways.signal_clicked().connect(
220 [this]() { emitVerdict(Verdict::DENY_ALWAYS); });
221
222 m_btnBox.set_homogeneous(true);
223 m_btnBox.append(m_allowOnce);
224 m_btnBox.append(m_allowAlways);
225 m_btnBox.append(m_denyOnce);
226 m_btnBox.append(m_denyAlways);
227 m_vbox.append(m_btnBox);
228
229 set_child(m_vbox);
230}
231
232// ---------------------------------------------------------------------------
233// Countdown
234// ---------------------------------------------------------------------------
235
237{
238 onTick(); // aggiorna subito senza aspettare 1s
239 m_timerConn = Glib::signal_timeout().connect(
240 sigc::mem_fun(*this, &AlertDialog::onTick), 1000);
241}
242
244{
245 double fraction = 1.0 - (double)m_remaining / m_timeoutSecs;
246 m_countdown.set_fraction(fraction);
247 m_countdown.set_text(
248 std::string(_("Nega automaticamente in ")) +
249 std::to_string(m_remaining) + "s");
250
251 if (m_remaining <= 0) {
253 return false;
254 }
255 --m_remaining;
256 return true;
257}
258
259// ---------------------------------------------------------------------------
260// Emetti verdetto
261// ---------------------------------------------------------------------------
262
264{
265 m_timerConn.disconnect();
266 signal_verdict.emit(v);
267 hide();
268}
Verdict
#define _(s)
Gtk::Label m_appName
Gtk::Button m_allowAlways
Gtk::Label m_portLabel
Gtk::Button m_denyOnce
AlertDialog(const std::string &appComm, const std::string &appPath, const std::vector< std::string > &appArgs, const std::string &destHost, const std::string &destIp, uint32_t destPort, int timeoutSecs=15)
sigc::connection m_timerConn
void buildHeaderBar()
void emitVerdict(Verdict v)
~AlertDialog() override
Gtk::Image m_appIcon
Gtk::Button m_denyAlways
Gtk::Label m_ipLabel
Gtk::ProgressBar m_countdown
void buildUI(const std::string &appComm, const std::string &appPath, const std::vector< std::string > &appArgs, const std::string &destHost, const std::string &destIp, uint32_t destPort)
Gtk::Box m_btnBox
void startCountdown()
Gtk::Label m_hostLabel
Gtk::Box m_vbox
Gtk::Button m_allowOnce
sigc::signal< void(Verdict)> signal_verdict
Gtk::Label m_appPath
Gtk::Box m_infoBox
std::string best_connection_icon_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 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)
bool is_safe_local_connection(const std::string &dst_ip, const std::string &src_ip)
bool matches_configured_proxy(const std::string &host, std::uint16_t port)
std::string resource(const std::string &rel)
Definition Resource.hpp:31