92: Gtk::Box(Gtk::Orientation::VERTICAL, 16),
93 lbl_title_(
"<b>Dashboard BastionGuard Endpoint</b>", Gtk::Align::START),
103 set_halign(Gtk::Align::FILL);
108 lbl_title_.get_style_context()->add_class(
"title");
119 auto vbox_status = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 6);
145 auto spacer = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL);
146 spacer->set_vexpand(
false);
149 footer_box_ = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
156 auto applyTheme = [
this](
const std::string& themeId) {
158 if (themeId ==
"dark")
159 cssFile = Glib::build_filename(
DATA_DIR,
"BastionGuard-dark.css");
161 cssFile = Glib::build_filename(
DATA_DIR,
"BastionGuard.css");
162 if (!std::filesystem::exists(cssFile)) {
163 char self_path[1024] = {};
164 ssize_t n = readlink(
"/proc/self/exe", self_path,
sizeof(self_path) - 1);
167 std::filesystem::path(self_path).parent_path().string();
168 std::string candidate = exeDir +
"/" +
169 (themeId ==
"dark" ?
"BastionGuard-dark.css" :
"BastionGuard.css");
170 if (std::filesystem::exists(candidate))
175 if (!std::filesystem::exists(cssFile)) {
176 std::cerr <<
"[BastionGuard] CSS non trovato: " << cssFile <<
"\n";
181 auto provider = Gtk::CssProvider::create();
182 provider->load_from_path(cssFile);
183 Gtk::StyleContext::add_provider_for_display(
184 Gdk::Display::get_default(),
186 GTK_STYLE_PROVIDER_PRIORITY_USER);
187 }
catch (
const Glib::Error& e) {
188 std::cerr <<
"[BastionGuard] Errore CSS: " << e.what() <<
"\n";
192 std::string cfgDir = Glib::get_home_dir() +
"/.config/BastionGuard";
193 std::filesystem::create_directories(cfgDir);
194 std::ofstream tf(cfgDir +
"/theme.conf", std::ios::trunc);
198 btn_theme_dark_->get_style_context()->remove_class(
"theme-btn-active");
199 if (themeId ==
"dark")
209 btn_theme_light_->signal_clicked().connect([applyTheme]() { applyTheme(
"light"); });
215 btn_theme_dark_->signal_clicked().connect([applyTheme]() { applyTheme(
"dark"); });
219 std::ifstream tf(Glib::get_home_dir() +
"/.config/BastionGuard/theme.conf");
237 ctx->add_class(
"update-badge");
238 ctx->add_class(
"update-checking");
268 const char* url =
"https://bastionguard.eu/version.php";
270 SoupSession* session = soup_session_new();
271 g_object_set(session,
"timeout", 10, NULL);
273 SoupMessage* msg = soup_message_new(
"GET", url);
278 struct CheckUpdateCtx {
282 auto* ctx =
new CheckUpdateCtx{
this,
alive_};
284 soup_session_send_and_read_async(
289 +[](GObject* source, GAsyncResult* res, gpointer user_data) {
291 std::unique_ptr<CheckUpdateCtx> ctx(
292 static_cast<CheckUpdateCtx*
>(user_data));
294 auto alive = ctx->alive;
296 SoupSession* session = SOUP_SESSION(source);
298 GError* error =
nullptr;
299 GBytes* bytes = soup_session_send_and_read_finish(session, res, &error);
301 if (!alive->load()) {
302 if (bytes) g_bytes_unref(bytes);
303 if (error) g_error_free(error);
304 g_object_unref(session);
308 if (error || !bytes) {
309 Glib::signal_idle().connect_once([self, alive]{
316 if (error) g_error_free(error);
317 g_object_unref(session);
322 const char* data =
static_cast<const char*
>(g_bytes_get_data(bytes, &size));
323 std::string body(data, size);
324 g_bytes_unref(bytes);
326 std::istringstream iss(body);
328 std::string remote_version =
"0.0";
329 int remote_build = 0;
332 while (std::getline(iss, line)) {
333 if (!line.empty() && line.back() ==
'\r')
336 if (line.rfind(
"version=", 0) == 0) {
337 remote_version = line.substr(8);
338 }
else if (line.rfind(
"build=", 0) == 0) {
339 remote_build = std::stoi(line.substr(6));
343 Glib::signal_idle().connect_once([self, alive]{
350 g_object_unref(session);
357 bool update_available =
false;
360 if (vcmp < 0) update_available =
true;
361 else if (vcmp == 0 && remote_build > local_build) update_available =
true;
363 Glib::signal_idle().connect_once([self, alive, update_available, remote_version, remote_build]{
369 Glib::ustring::compose(
370 _(
"Remoto: %1 (%2)"),
372 std::to_string(remote_build)
378 g_object_unref(session);
420 auto pic = Gtk::make_managed<Gtk::Picture>();
422 auto pb = Gdk::Pixbuf::create_from_file(path);
423 auto scaled = pb->scale_simple(size, size, Gdk::InterpType::BILINEAR);
424 auto tex = Gdk::Texture::create_for_pixbuf(scaled);
427 pic->set_paintable(tex);
428 pic->set_content_fit(Gtk::ContentFit::CONTAIN);
429 pic->set_keep_aspect_ratio(
true);
431 std::cerr <<
_(
"⚠ Impossibile caricare immagine: ") << path <<
"\n";
435 pic->set_hexpand(
false);
436 pic->set_vexpand(
false);
437 pic->set_halign(Gtk::Align::START);
438 pic->set_valign(Gtk::Align::START);
453 Gtk::Widget& content,
454 const Glib::ustring& title)
456 auto frame = Gtk::make_managed<Gtk::Frame>();
457 frame->get_style_context()->add_class(
"card");
458 frame->set_margin_bottom(10);
459 frame->set_hexpand(
true);
460 frame->set_halign(Gtk::Align::FILL);
462 auto hbox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 12);
463 hbox->set_margin(12);
464 hbox->set_hexpand(
true);
465 hbox->set_halign(Gtk::Align::FILL);
468 auto icon_box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL);
469 icon_box->set_size_request(64, 64);
470 icon_box->set_halign(Gtk::Align::START);
471 icon_box->set_valign(Gtk::Align::START);
473 auto pic = Gtk::make_managed<Gtk::Picture>();
475 auto pb = Gdk::Pixbuf::create_from_file(icon_path);
476 auto scaled = pb->scale_simple(64, 64, Gdk::InterpType::BILINEAR);
477 auto tex = Gdk::Texture::create_for_pixbuf(scaled);
478 pic->set_paintable(tex);
479 pic->set_content_fit(Gtk::ContentFit::FILL);
480 pic->set_keep_aspect_ratio(
true);
482 std::cerr <<
_(
"⚠ Impossibile caricare icona: ") << icon_path <<
"\n";
485 icon_box->append(*pic);
486 hbox->append(*icon_box);
489 auto vbox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 6);
490 vbox->set_hexpand(
true);
491 vbox->set_halign(Gtk::Align::FILL);
493 auto lbl_title = Gtk::make_managed<Gtk::Label>();
494 lbl_title->set_markup(
"<b>" + title +
"</b>");
495 lbl_title->set_halign(Gtk::Align::START);
497 if (
auto lbl =
dynamic_cast<Gtk::Label*
>(&content)) {
499 lbl->set_wrap_mode(Pango::WrapMode::WORD_CHAR);
500 lbl->set_ellipsize(Pango::EllipsizeMode::NONE);
501 lbl->set_xalign(0.0);
502 lbl->set_hexpand(
true);
503 lbl->set_halign(Gtk::Align::FILL);
506 vbox->append(*lbl_title);
507 vbox->append(content);
510 frame->set_child(*hbox);
static Gtk::Picture * load_picture_fixed(const std::string &path, int size)
static bool check_service_active(const std::string &service_name)
static int compare_semver(const std::string &a, const std::string &b)
Gtk::Picture * make_large_icon(const std::string &path)
Gtk::Picture * make_small_icon(const std::string &path)
Gtk::Frame * make_card(const std::string &icon_path, Gtk::Widget &content, const Glib::ustring &title)
Gtk::Frame * card_db_date_
Gtk::Label lbl_update_status_
Gtk::Frame * card_status_
Gtk::Frame * card_db_version_
sigc::connection update_check_timer_
void set_update_status(const std::string &state)
~DashboardPage() override
Gtk::Label lbl_ransomware_status_
Gtk::Button * btn_theme_light_
void check_updates_async()
Gtk::Label lbl_db_version_
Gtk::Label lbl_phishing_status_
Gtk::Button * btn_theme_dark_
bool update_check_in_progress_
Gtk::TextView txt_onaccess_report_
bool on_update_check_timer()
sigc::connection refresh_timer_
void update_from_logs(const std::string &log_path, const Glib::ustring &db_version, bool on_access_enabled)
BastionGuard::AliveFlag alive_
Glib::RefPtr< Gtk::TextBuffer > buf_onaccess_report_
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={})