Skip to content
API Documentation
BastionGuard™
PrivacyPage Class Reference

#include <PrivacyPage.hpp>

Inheritance diagram for PrivacyPage:

Public Member Functions

 PrivacyPage ()
 ~PrivacyPage ()

Private Member Functions

void run_privacy_script_async (bool unblock)
bool is_daemon_active ()
void start_privacy_daemon ()
void unblock_devices_local ()
void save_privacy_state (bool enabled)
bool load_privacy_state ()
std::vector< std::string > detect_active_devices ()
void start_monitor_thread ()
void monitor_loop ()
void show_permission_popup (const std::string &process_name)
void show_next_alert ()
void on_toggle_privacy ()
void on_refresh_clicked ()
void setup_auto_refresh_toggle ()
void update_process_list ()
void manual_unlock_devices ()
void write_log (const std::string &msg)

Private Attributes

Gtk::Label lbl_title_
Gtk::Switch switch_webcam_
Gtk::Button btn_refresh_
Gtk::CheckButton chk_auto_refresh_
Gtk::ScrolledWindow scroller_
Gtk::TextView txt_processes_
Gtk::Label lbl_status_
std::thread monitor_thread_
std::atomic< bool > monitor_running_ {false}
std::set< std::string > known_processes_
sigc::connection auto_refresh_conn_
std::atomic< bool > toggle_busy_ {false}
Gtk::Button btn_unlock_
std::atomic< bool > devices_blocked_ {false}
BastionGuard::AliveFlag alive_ = BastionGuard::make_alive_flag()

Detailed Description

Definition at line 34 of file PrivacyPage.hpp.

Constructor & Destructor Documentation

◆ PrivacyPage()

PrivacyPage::PrivacyPage ( )

Definition at line 180 of file PrivacyPage.cpp.

181 : Gtk::Box(Gtk::Orientation::VERTICAL, 12)
182{
183 set_margin(16);
184 set_vexpand(true);
185
186
187 auto title_box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
188 title_box->add_css_class("secure-headerbar");
189 title_box->set_margin_bottom(8);
190
191 lbl_title_.set_text(_("🔒 Privacy Webcam / Microfono"));
192 lbl_title_.set_margin(8);
193 lbl_title_.set_halign(Gtk::Align::CENTER);
194 lbl_title_.set_hexpand(true);
195 lbl_title_.add_css_class("secure-header-title");
196 title_box->append(lbl_title_);
197 append(*title_box);
198
199
200 bool last_state = load_privacy_state();
201 switch_webcam_.set_active(last_state);
202
203 try {
204 std::string config_path = Glib::get_home_dir() + "/.config/BastionGuard/privacy.conf";
205 if (Glib::file_test(config_path, Glib::FileTest::EXISTS)) {
206 auto keyfile = Glib::KeyFile::create();
207 keyfile->load_from_file(config_path);
208 bool auto_refresh_enabled = false;
209 if (keyfile->has_key("privacy", "auto_refresh"))
210 auto_refresh_enabled = keyfile->get_boolean("privacy", "auto_refresh");
211 chk_auto_refresh_.set_active(auto_refresh_enabled);
212 }
213 } catch (...) {
214 std::cerr << _("[Privacy] Errore lettura auto_refresh") << std::endl;
215 }
216
217 switch_webcam_.set_halign(Gtk::Align::CENTER);
218 switch_webcam_.set_valign(Gtk::Align::CENTER);
219 switch_webcam_.set_margin_bottom(6);
220 append(switch_webcam_);
221
222 auto lbl_switch = Gtk::make_managed<Gtk::Label>(_("Abilita/Disabilita Webcam e Microfono"));
223 lbl_switch->set_halign(Gtk::Align::CENTER);
224 append(*lbl_switch);
225
226
227 auto control_box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 8);
228 control_box->set_halign(Gtk::Align::CENTER);
229
230 btn_refresh_.set_label(_("🔄 Aggiorna"));
231 btn_refresh_.signal_clicked().connect(sigc::mem_fun(*this, &PrivacyPage::on_refresh_clicked));
232 control_box->append(btn_refresh_);
233
234 chk_auto_refresh_.set_label(_("Aggiornamento automatico ogni 5 minuti"));
235 chk_auto_refresh_.signal_toggled().connect(sigc::mem_fun(*this, &PrivacyPage::setup_auto_refresh_toggle));
236 control_box->append(chk_auto_refresh_);
237 append(*control_box);
238
239 btn_unlock_.set_label(_("🔓 Sblocca dispositivi"));
240 btn_unlock_.add_css_class("btn-warning");
241 btn_unlock_.set_halign(Gtk::Align::END);
242 btn_unlock_.set_margin_top(10);
243 btn_unlock_.set_visible(false);
244
245 btn_unlock_.signal_clicked().connect([this]() {
246 write_log(_("Richiesto sblocco manuale dei dispositivi"));
248 });
249
250
251 append(btn_unlock_);
252
253
254 scroller_.set_vexpand(true);
255 scroller_.set_hexpand(true);
256 txt_processes_.set_editable(false);
257 txt_processes_.set_wrap_mode(Gtk::WrapMode::WORD_CHAR);
258 scroller_.set_child(txt_processes_);
259 append(scroller_);
260
261
262 lbl_status_.set_margin_top(8);
263 lbl_status_.set_halign(Gtk::Align::CENTER);
264 append(lbl_status_);
265
266 {
267 auto alive = alive_;
268 Glib::signal_idle().connect_once([this, alive]() {
269 BastionGuard::if_alive(alive, [this]() {
270 try {
271 std::string base_dir = Glib::get_home_dir() + "/.local/share/BastionGuard/logs";
272 auto t = std::time(nullptr);
273 std::tm tm{};
274 localtime_r(&t, &tm); // thread-safe rispetto a std::localtime
275 char datebuf[16];
276 std::strftime(datebuf, sizeof(datebuf), "%Y-%m-%d", &tm);
277 std::string log_path = base_dir + "/privacyd_" + std::string(datebuf) + ".log";
278
279 if (fs::exists(log_path)) {
280 std::ifstream log_file(log_path);
281 std::stringstream buffer;
282 buffer << log_file.rdbuf();
283 txt_processes_.get_buffer()->set_text(buffer.str());
284 lbl_status_.set_text(_("📜 Log giornaliero caricato."));
285 } else {
286 lbl_status_.set_text(_("📝 Nessun log disponibile per oggi."));
287 }
288 } catch (...) {
289 lbl_status_.set_text(_("⚠️ Errore durante il caricamento del log."));
290 }
291 });
292 });
293 }
294
295
296 switch_webcam_.property_active().signal_changed().connect(
297 sigc::mem_fun(*this, &PrivacyPage::on_toggle_privacy));
298
299
300 {
301 auto alive_ctor = alive_;
302 Glib::signal_idle().connect_once([this, alive_ctor]() {
303 if (!alive_ctor || !alive_ctor->load(std::memory_order_acquire)) return;
304 lbl_status_.set_text(_("⏳ Inizializzazione del servizio privacy..."));
305 write_log(_("=== Avvio BastionGuard PrivacyPage ==="));
306 auto alive = alive_;
307 std::thread([this, alive]() {
308 if (!alive || !alive->load(std::memory_order_acquire)) return;
309 if (!is_daemon_active())
312 Glib::signal_idle().connect_once([this, alive]() {
313 BastionGuard::if_alive(alive, [this]() {
315 lbl_status_.set_text(_("🕵️ Monitoraggio privacy attivo"));
316 monitor_running_ = true;
318 });
319 });
320 }).detach();
321 });
322 }
323}
#define _(s)
BastionGuard::AliveFlag alive_
bool load_privacy_state()
void start_monitor_thread()
Gtk::TextView txt_processes_
void on_toggle_privacy()
Gtk::Switch switch_webcam_
Gtk::Button btn_unlock_
Gtk::Button btn_refresh_
Gtk::Label lbl_title_
Gtk::ScrolledWindow scroller_
void unblock_devices_local()
void update_process_list()
std::atomic< bool > monitor_running_
void write_log(const std::string &msg)
void on_refresh_clicked()
Gtk::Label lbl_status_
void start_privacy_daemon()
Gtk::CheckButton chk_auto_refresh_
void setup_auto_refresh_toggle()
void manual_unlock_devices()
bool is_daemon_active()
void if_alive(const AliveFlag &alive, Fn &&fn)

References _, alive_, btn_refresh_, btn_unlock_, chk_auto_refresh_, BastionGuard::if_alive(), is_daemon_active(), lbl_status_, lbl_title_, load_privacy_state(), manual_unlock_devices(), monitor_running_, on_refresh_clicked(), on_toggle_privacy(), scroller_, setup_auto_refresh_toggle(), start_monitor_thread(), start_privacy_daemon(), switch_webcam_, txt_processes_, unblock_devices_local(), update_process_list(), and write_log().

◆ ~PrivacyPage()

PrivacyPage::~PrivacyPage ( )

Definition at line 325 of file PrivacyPage.cpp.

325 {
326 // Segnala ai lambda in volo (signal_idle + thread detached) che `this`
327 // sta per sparire: faranno early-return invece di fare use-after-free.
328 if (alive_) alive_->store(false, std::memory_order_release);
329
330 monitor_running_ = false;
331 if (monitor_thread_.joinable())
332 monitor_thread_.join();
333 write_log(_("=== Arresto monitoraggio privacy ==="));
334}
std::thread monitor_thread_

References _, alive_, monitor_running_, monitor_thread_, and write_log().

Member Function Documentation

◆ detect_active_devices()

std::vector< std::string > PrivacyPage::detect_active_devices ( )
private

Definition at line 356 of file PrivacyPage.cpp.

356 {
357 std::vector<std::string> result;
358 const std::vector<std::string> prefixes = {"/dev/video", "/dev/snd/pcm"};
359
360 // Usiamo l'overload con std::error_code per NON lanciare eccezioni:
361 // /proc è intrinsecamente race-y (i PID possono sparire in qualsiasi momento)
362 // e una filesystem_error non gestita in questo thread causerebbe std::terminate().
363 std::error_code ec;
364 fs::directory_iterator proc_it("/proc", ec);
365 if (ec) {
366 // Impossibile aprire /proc: restituiamo una lista vuota anziché crashare.
367 return result;
368 }
369
370 for (; proc_it != fs::directory_iterator(); proc_it.increment(ec)) {
371 if (ec) {
372 // Errore avanzando l'iteratore (es. PID scomparso): fermiamo il ciclo
373 // in modo pulito, i device attivi verranno rilevati al prossimo giro.
374 break;
375 }
376
377 const auto& pid_entry = *proc_it;
378
379 std::error_code ec_dir;
380 if (!pid_entry.is_directory(ec_dir) || ec_dir) continue;
381
382 const std::string pid_str = pid_entry.path().filename().string();
383 if (!std::all_of(pid_str.begin(), pid_str.end(), ::isdigit)) continue;
384
385 const std::string fd_path = "/proc/" + pid_str + "/fd";
386 std::error_code ec_exists;
387 if (!fs::exists(fd_path, ec_exists) || ec_exists) continue;
388
389 // Lazy: carichiamo info processo solo se troviamo un fd interessante
390 std::string comm, exe, cmdline;
391 bool proc_info_loaded = false;
392
393 // Anche qui: overload con error_code. Se il processo muore tra exists()
394 // e la creazione dell'iteratore, non vogliamo un'eccezione non gestita.
395 std::error_code ec_fd;
396 fs::directory_iterator fd_it(
397 fd_path,
398 fs::directory_options::skip_permission_denied,
399 ec_fd);
400 if (ec_fd) continue;
401
402 for (; fd_it != fs::directory_iterator(); fd_it.increment(ec_fd)) {
403 if (ec_fd) break; // pid scomparso mentre iteravamo: skippa questo PID
404
405 const auto& fd_entry = *fd_it;
406
407 std::error_code ec_link;
408 auto link_target = fs::read_symlink(fd_entry.path(), ec_link);
409 if (ec_link) continue;
410 std::string target = link_target.string();
411
412 bool matches = false;
413 for (const auto& prefix : prefixes) {
414 if (target.rfind(prefix, 0) == 0) { // starts_with(prefix)
415 matches = true;
416 break;
417 }
418 }
419 if (!matches) continue;
420
421 if (!proc_info_loaded) {
422 comm = read_text_file("/proc/" + pid_str + "/comm");
423 if (comm.empty()) comm = "(sconosciuto)";
424 exe = read_exe_path(pid_str);
425 cmdline = read_cmdline("/proc/" + pid_str + "/cmdline");
426 proc_info_loaded = true;
427 }
428
429 // Soppressione popup per terminali/polkit/portal/pipewire ecc.
430 if (is_noise_process(comm, exe, cmdline)) break;
431
432 // Soppressione anche se un parent (fino a 3 livelli) è un processo "noise"
433 if (parent_is_noise(pid_str)) break;
434
435 result.push_back(comm + " (PID=" + pid_str + ")");
436 break; // trovato un accesso rilevante per questo PID, passa al prossimo PID
437 }
438 }
439
440 std::sort(result.begin(), result.end());
441 result.erase(std::unique(result.begin(), result.end()), result.end());
442 return result;
443}
static std::string read_exe_path(const std::string &pid_str)
static std::string read_cmdline(const std::string &path)
static bool parent_is_noise(const std::string &pid_str)
static bool is_noise_process(const std::string &comm, const std::string &exe, const std::string &cmdline)
static std::string read_text_file(const std::string &path)

References is_noise_process(), parent_is_noise(), read_cmdline(), read_exe_path(), and read_text_file().

Referenced by monitor_loop(), and update_process_list().

◆ is_daemon_active()

bool PrivacyPage::is_daemon_active ( )
private

Definition at line 337 of file PrivacyPage.cpp.

337 {
338 int rc = (bg_process::run({"/usr/libexec/bastionguard/bastionguard-service", "--user", "is-active", "BastionGuard-privacyd.service"}).exit_code);
339 return (WIFEXITED(rc) && WEXITSTATUS(rc) == 0);
340}
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 PrivacyPage().

◆ load_privacy_state()

bool PrivacyPage::load_privacy_state ( )
private

Definition at line 851 of file PrivacyPage.cpp.

851 {
852 try {
853 auto keyfile = Glib::KeyFile::create();
854 std::string config_path = Glib::get_home_dir() + "/.config/BastionGuard/privacy.conf";
855 if (Glib::file_test(config_path, Glib::FileTest::EXISTS)) {
856 keyfile->load_from_file(config_path);
857 return keyfile->get_boolean("privacy", "enabled");
858 }
859 } catch (...) {
860 std::cerr << _("[Privacy] Errore caricamento config") << std::endl;
861 }
862 return true;
863}

References _.

Referenced by PrivacyPage().

◆ manual_unlock_devices()

void PrivacyPage::manual_unlock_devices ( )
private

Definition at line 929 of file PrivacyPage.cpp.

929 {
930 auto alive = alive_;
931 std::thread([this, alive]() {
932 const bool ok = run_privhelper_pkexec("unblock");
933
934 Glib::signal_idle().connect_once([this, alive, ok]() {
935 BastionGuard::if_alive(alive, [this, ok]() {
936 if (ok) {
937 write_log(_("🔓 Dispositivi sbloccati manualmente"));
938 devices_blocked_ = false;
939 btn_unlock_.set_visible(false);
940 lbl_status_.set_text(_("Webcam e microfono sbloccati"));
941 } else {
942 write_log(_("❌ Errore durante sblocco manuale"));
943 lbl_status_.set_text(_("❌ Impossibile sbloccare i dispositivi."));
944 }
945
947 });
948 });
949 }).detach();
950}
static bool run_privhelper_pkexec(const std::string &action)
std::atomic< bool > devices_blocked_

References _, alive_, btn_unlock_, devices_blocked_, BastionGuard::if_alive(), lbl_status_, run_privhelper_pkexec(), update_process_list(), and write_log().

Referenced by PrivacyPage().

◆ monitor_loop()

void PrivacyPage::monitor_loop ( )
private

Definition at line 453 of file PrivacyPage.cpp.

453 {
454 while (monitor_running_) {
455 try {
456 auto processes = detect_active_devices();
457 for (const auto &proc : processes) {
458 if (!known_processes_.count(proc)) {
459 known_processes_.insert(proc);
460 write_log(Glib::ustring::compose(_("Nuovo accesso rilevato: %1"), proc));
461 auto alive = alive_;
462 Glib::signal_idle().connect_once([this, alive, proc]() {
463 BastionGuard::if_alive(alive, [this, &proc]() {
465 });
466 });
467 }
468 }
469 for (auto it = known_processes_.begin(); it != known_processes_.end();) {
470 if (std::find(processes.begin(), processes.end(), *it) == processes.end())
471 it = known_processes_.erase(it);
472 else ++it;
473 }
474 } catch (const std::exception& e) {
475 // Difesa in profondità: un'eccezione non catturata qui ucciderebbe il thread
476 // (std::terminate -> SIGABRT). La logghiamo e proseguiamo al prossimo giro.
477 try {
478 write_log(std::string(_("⚠️ Errore in monitor_loop: ")) + e.what());
479 } catch (...) { /* nulla di più da fare */ }
480 } catch (...) {
481 try {
482 write_log(_("⚠️ Errore sconosciuto in monitor_loop"));
483 } catch (...) { /* nulla di più da fare */ }
484 }
485 std::this_thread::sleep_for(std::chrono::seconds(3));
486 }
487}
void show_permission_popup(const std::string &process_name)
std::vector< std::string > detect_active_devices()
std::set< std::string > known_processes_

References _, alive_, detect_active_devices(), BastionGuard::if_alive(), known_processes_, monitor_running_, show_permission_popup(), and write_log().

Referenced by start_monitor_thread().

◆ on_refresh_clicked()

void PrivacyPage::on_refresh_clicked ( )
private

Definition at line 673 of file PrivacyPage.cpp.

673 {
674 lbl_status_.set_text(_("🔄 Aggiornamento elenco processi e log..."));
675 write_log(_("Aggiornamento manuale avviato"));
677}

References _, lbl_status_, update_process_list(), and write_log().

Referenced by PrivacyPage().

◆ on_toggle_privacy()

void PrivacyPage::on_toggle_privacy ( )
private

Definition at line 865 of file PrivacyPage.cpp.

865 {
866 if (toggle_busy_.load())
867 return;
868
869 const bool active = switch_webcam_.get_active();
870 save_privacy_state(active);
871
872 switch_webcam_.set_sensitive(false);
873 lbl_status_.set_text(active
874 ? _("📷 Riattivazione webcam/microfono...")
875 : _("🔒 Disattivazione webcam/microfono in corso..."));
876
877 write_log(std::string(_("Toggle privacy: ")) + (active ? "ON (unblock)" : "OFF (block)"));
878
879 toggle_busy_ = true;
881}
std::atomic< bool > toggle_busy_
void save_privacy_state(bool enabled)
void run_privacy_script_async(bool unblock)

References _, lbl_status_, run_privacy_script_async(), save_privacy_state(), switch_webcam_, toggle_busy_, and write_log().

Referenced by PrivacyPage().

◆ run_privacy_script_async()

void PrivacyPage::run_privacy_script_async ( bool unblock)
private

Definition at line 885 of file PrivacyPage.cpp.

885 {
886 auto alive = alive_;
887 std::thread([this, alive, unblock]() {
888
889 const bool ok = run_privhelper_pkexec(unblock ? "unblock" : "block");
890
891
892 Glib::signal_idle().connect_once([this, alive, ok, unblock]() {
893 BastionGuard::if_alive(alive, [this, ok, unblock]() {
894 if (ok) {
895 if (unblock) {
896
897 lbl_status_.set_text(_("🕵️ Monitoraggio privacy attivo"));
898 write_log(_("Script OK: UNBLOCK eseguito"));
899 monitor_running_ = true;
900 if (!monitor_thread_.joinable()) {
902 }
903 } else {
904
905 lbl_status_.set_text(_("🔕 Privacy monitor disattivato"));
906 write_log(_("Script OK: BLOCK eseguito"));
907 monitor_running_ = false;
908 if (monitor_thread_.joinable()) {
909 monitor_thread_.join();
910 }
911 }
912 } else {
913
914 write_log(_("ERRORE: esecuzione script privacy fallita"));
915 lbl_status_.set_text(_("❌ Errore nell'esecuzione dello script."));
916
917 switch_webcam_.set_active(!unblock);
918 }
919
920
922 switch_webcam_.set_sensitive(true);
923 toggle_busy_ = false;
924 });
925 });
926 }).detach();
927}

References _, alive_, BastionGuard::if_alive(), lbl_status_, monitor_running_, monitor_thread_, run_privhelper_pkexec(), start_monitor_thread(), switch_webcam_, toggle_busy_, update_process_list(), and write_log().

Referenced by on_toggle_privacy().

◆ save_privacy_state()

void PrivacyPage::save_privacy_state ( bool enabled)
private

Definition at line 835 of file PrivacyPage.cpp.

835 {
836 try {
837 auto keyfile = Glib::KeyFile::create();
838 auto config_dir = Glib::get_home_dir() + "/.config/BastionGuard";
839 if (!std::filesystem::exists(config_dir))
840 std::filesystem::create_directories(config_dir);
841
842 std::string config_path = config_dir + "/privacy.conf";
843 keyfile->set_boolean("privacy", "enabled", enabled);
844 keyfile->save_to_file(config_path);
845 write_log(enabled ? _("Privacy abilitata") : _("Privacy disabilitata"));
846 } catch (const Glib::Error &e) {
847 std::cerr << _("[Privacy] Errore salvataggio config: ") << e.what() << std::endl;
848 }
849}

References _, and write_log().

Referenced by on_toggle_privacy().

◆ setup_auto_refresh_toggle()

void PrivacyPage::setup_auto_refresh_toggle ( )
private

Definition at line 681 of file PrivacyPage.cpp.

681 {
682 bool enabled = chk_auto_refresh_.get_active();
683
684
685 try {
686 auto keyfile = Glib::KeyFile::create();
687 std::string config_dir = Glib::get_home_dir() + "/.config/BastionGuard";
688 std::string config_path = config_dir + "/privacy.conf";
689
690 if (!fs::exists(config_dir))
691 fs::create_directories(config_dir);
692
693 if (Glib::file_test(config_path, Glib::FileTest::EXISTS))
694 keyfile->load_from_file(config_path);
695
696 keyfile->set_boolean("privacy", "auto_refresh", enabled);
697 keyfile->save_to_file(config_path);
698 write_log(enabled ? _("Auto aggiornamento attivato (salvato in config)")
699 : _("Auto aggiornamento disattivato (salvato in config)"));
700 } catch (const Glib::Error& e) {
701 std::cerr << _("[Privacy] Errore salvataggio auto_refresh: ") << e.what() << std::endl;
702 }
703
704
705 if (enabled) {
706 lbl_status_.set_text(_("⏳ Auto aggiornamento attivo ogni 5 minuti."));
707 auto_refresh_conn_ = Glib::signal_timeout().connect_seconds(
708 [this]() {
709 // update_process_list() lancia internamente un thread protetto
710 // dall'alive flag, quindi è sicuro anche se la pagina muore.
712 return chk_auto_refresh_.get_active();
713 },
714 300);
715 } else {
716 lbl_status_.set_text(_("🛑 Auto aggiornamento disattivato."));
717 auto_refresh_conn_.disconnect();
718 }
719}
sigc::connection auto_refresh_conn_

References _, auto_refresh_conn_, chk_auto_refresh_, lbl_status_, update_process_list(), and write_log().

Referenced by PrivacyPage().

◆ show_next_alert()

void PrivacyPage::show_next_alert ( )
private

Definition at line 500 of file PrivacyPage.cpp.

500 {
501 if (alert_queue_.empty() || current_alert_)
502 return;
503
504 std::string proc = alert_queue_.front();
505 alert_queue_.pop();
506 size_t pending = alert_queue_.size();
507
508 current_alert_ = Gtk::make_managed<Gtk::Window>();
509 auto *win = current_alert_;
510
511 win->set_title("");
512 win->set_default_size(480, 230);
513 win->set_resizable(false);
514 win->set_modal(false);
515 win->add_css_class("BastionGuard-popup");
516 win->add_css_class("BastionGuard-dialog");
517
518
519 if (auto* root = dynamic_cast<Gtk::Window*>(get_root()))
520 win->set_transient_for(*root);
521
522
523 auto *header = Gtk::make_managed<Gtk::HeaderBar>();
524 header->set_show_title_buttons(false);
525 header->set_decoration_layout("");
526 header->add_css_class("custom-headerbar");
527
528
529 auto *left = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
530 left->set_size_request(240, -1);
531
532 auto *title_lbl = Gtk::make_managed<Gtk::Label>(_("⚠️ Accesso dispositivo rilevato"));
533 title_lbl->add_css_class("BastionGuard-title");
534 title_lbl->set_halign(Gtk::Align::START);
535 left->append(*title_lbl);
536
537 header->pack_start(*left);
538
539
540 auto *center = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
541 center->set_hexpand(true);
542 header->pack_start(*center);
543
544
545 auto *right = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL);
546
547 try {
548 auto pmin = Gdk::Pixbuf::create_from_file(resource("icon-minimize.png"), 28, 28, true);
549 auto *btn_min = Gtk::make_managed<Gtk::Button>();
550 btn_min->set_child(*Gtk::make_managed<Gtk::Image>(pmin));
551 btn_min->add_css_class("header-button");
552 btn_min->signal_clicked().connect([win]() { win->minimize(); });
553 right->append(*btn_min);
554 } catch (...) {}
555
556 try {
557 auto pclose = Gdk::Pixbuf::create_from_file(resource("icon-close.png"), 28, 28, true);
558 auto *btn_close = Gtk::make_managed<Gtk::Button>();
559 btn_close->set_child(*Gtk::make_managed<Gtk::Image>(pclose));
560 btn_close->add_css_class("header-button");
561 btn_close->signal_clicked().connect([this, win]() {
562 win->close();
563 current_alert_ = nullptr;
564 auto alive = alive_;
565 Glib::signal_idle().connect_once([this, alive]() {
566 BastionGuard::if_alive(alive, [this]() { show_next_alert(); });
567 });
568 });
569 right->append(*btn_close);
570 } catch (...) {}
571
572 header->pack_end(*right);
573
574 win->set_titlebar(*header);
575
576
577 auto *vbox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 12);
578 vbox->set_margin(20);
579 win->set_child(*vbox);
580
581 auto *msg = Gtk::make_managed<Gtk::Label>(
582 Glib::ustring::compose(
583 _("<b>%1</b> sta tentando di accedere a webcam o microfono."),
584 proc
585 )
586 );
587 msg->set_use_markup(true);
588 msg->set_wrap(true);
589 msg->set_halign(Gtk::Align::CENTER);
590 vbox->append(*msg);
591
592 if (pending > 0) {
593 auto *lbl_pending = Gtk::make_managed<Gtk::Label>(
594 Glib::ustring::compose(_("🕒 %1 altri processi in attesa..."), pending));
595 lbl_pending->set_halign(Gtk::Align::CENTER);
596 vbox->append(*lbl_pending);
597 }
598
599
600 auto *btn_box = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 12);
601 btn_box->set_halign(Gtk::Align::CENTER);
602
603 auto *btn_allow = Gtk::make_managed<Gtk::Button>(_("✔ Consenti"));
604 auto *btn_deny = Gtk::make_managed<Gtk::Button>(_("✖ Nega"));
605
606 btn_allow->add_css_class("btn-primary");
607 btn_deny->add_css_class("btn-danger");
608
609 btn_box->append(*btn_allow);
610 btn_box->append(*btn_deny);
611 vbox->append(*btn_box);
612
613 btn_allow->signal_clicked().connect([this, proc, win]() {
614 write_log(Glib::ustring::compose(_("Accesso CONSENTITO a %1"), proc));
615
616 win->close();
617 current_alert_ = nullptr;
618
619 auto alive = alive_;
620 Glib::signal_idle().connect_once([this, alive]() {
621 BastionGuard::if_alive(alive, [this]() { show_next_alert(); });
622 });
623 });
624
625
626 btn_deny->signal_clicked().connect([this, proc, win]() {
627
628 write_log(Glib::ustring::compose(
629 _("Accesso NEGATO a %1 — blocco webcam/microfono"), proc));
630
631
632 devices_blocked_ = true;
633
634
635 btn_unlock_.set_visible(true);
636
637
638 auto alive = alive_;
639 std::thread([this, alive]() {
640 const bool ok = run_privhelper_pkexec("block");
641
642 Glib::signal_idle().connect_once([this, alive, ok]() {
643 BastionGuard::if_alive(alive, [this, ok]() {
644 if (!ok) {
645 write_log(_("❌ ERRORE: impossibile applicare il blocco tramite helper!"));
646 lbl_status_.set_text(_("❌ Errore: impossibile bloccare webcam/microfono"));
647 } else {
648 write_log(_("🔒 Webcam/microfono BLOCCATI tramite helper (popup)"));
649 lbl_status_.set_text(_("🔒 Webcam e microfono bloccati"));
650 }
651
653 });
654 });
655 }).detach();
656
657
658 win->close();
659 current_alert_ = nullptr;
660 {
661 auto alive2 = alive_;
662 Glib::signal_idle().connect_once([this, alive2]() {
663 BastionGuard::if_alive(alive2, [this]() { show_next_alert(); });
664 });
665 }
666 });
667
668
669 win->present();
670}
std::queue< std::string > alert_queue_
Gtk::Window * current_alert_
void show_next_alert()
std::string resource(const std::string &rel)
Definition Resource.hpp:31

References _, alert_queue_, alive_, btn_unlock_, current_alert_, devices_blocked_, BastionGuard::if_alive(), lbl_status_, resource(), run_privhelper_pkexec(), show_next_alert(), update_process_list(), and write_log().

Referenced by show_next_alert(), and show_permission_popup().

◆ show_permission_popup()

void PrivacyPage::show_permission_popup ( const std::string & process_name)
private

Definition at line 490 of file PrivacyPage.cpp.

490 {
491 alert_queue_.push(process_name);
492 if (!current_alert_) {
493 auto alive = alive_;
494 Glib::signal_idle().connect_once([this, alive]() {
495 BastionGuard::if_alive(alive, [this]() { show_next_alert(); });
496 });
497 }
498}

References alert_queue_, alive_, current_alert_, BastionGuard::if_alive(), and show_next_alert().

Referenced by monitor_loop().

◆ start_monitor_thread()

void PrivacyPage::start_monitor_thread ( )
private

Definition at line 448 of file PrivacyPage.cpp.

448 {
449 monitor_thread_ = std::thread(&PrivacyPage::monitor_loop, this);
450 write_log(_("🕵️ Monitoraggio in tempo reale avviato"));
451}
void monitor_loop()

References _, monitor_loop(), monitor_thread_, and write_log().

Referenced by PrivacyPage(), and run_privacy_script_async().

◆ start_privacy_daemon()

void PrivacyPage::start_privacy_daemon ( )
private

Definition at line 342 of file PrivacyPage.cpp.

342 {
343 write_log(_("Avvio automatico del servizio BastionGuard-privacyd..."));
344 (bg_process::run({"/usr/libexec/bastionguard/bastionguard-service", "--user", "start", "BastionGuard-privacyd.service"}).exit_code);
345}

References _, bg_process::run(), and write_log().

Referenced by PrivacyPage().

◆ unblock_devices_local()

void PrivacyPage::unblock_devices_local ( )
private

Definition at line 348 of file PrivacyPage.cpp.

348 {
349 if (run_privhelper_pkexec("unblock"))
350 write_log(_("📷 Webcam/Microfono sbloccati automaticamente"));
351 else
352 write_log(_("❌ Errore nello sblocco automatico"));
353}

References _, run_privhelper_pkexec(), and write_log().

Referenced by PrivacyPage().

◆ update_process_list()

void PrivacyPage::update_process_list ( )
private

Definition at line 723 of file PrivacyPage.cpp.

723 {
724 auto alive = alive_;
725 std::thread([this, alive]() {
726 if (!alive || !alive->load(std::memory_order_acquire)) return;
727 try {
728
729 auto processes = detect_active_devices();
730 std::ostringstream oss;
731 if (processes.empty()) {
732 oss << _("Nessun processo sta utilizzando webcam o microfono.") << "\n\n";
733 } else {
734 oss << _("📋 Processi attivi:\n\n");
735 for (const auto &p : processes)
736 oss << " • " << p << "\n";
737 oss << "\n";
738 }
739
740
741 oss << "────────────────────────────────────────────\n";
742 oss << _("📜 Log giornaliero:\n\n");
743
744
745 std::string base_dir = Glib::get_home_dir() + "/.local/share/BastionGuard/logs";
746 auto t = std::time(nullptr);
747 std::tm tm{};
748 localtime_r(&t, &tm); // thread-safe rispetto a std::localtime
749 char datebuf[16];
750 std::strftime(datebuf, sizeof(datebuf), "%Y-%m-%d", &tm);
751 std::string log_path = base_dir + "/privacyd_" + std::string(datebuf) + ".log";
752
753 if (fs::exists(log_path)) {
754 std::ifstream log_file(log_path);
755 if (log_file.is_open()) {
756 std::stringstream buffer;
757 buffer << log_file.rdbuf();
758 oss << buffer.str();
759 } else {
760 oss << _("⚠️ Impossibile leggere il log giornaliero.") << "\n";
761 }
762 } else {
763 oss << _("📝 Nessun log disponibile per oggi.") << "\n";
764 }
765
766
767 Glib::signal_idle().connect_once([this, alive, txt = oss.str()]() {
768 BastionGuard::if_alive(alive, [this, &txt]() {
769 txt_processes_.get_buffer()->set_text(txt);
770 lbl_status_.set_text(_("✅ Aggiornamento completato."));
771 });
772 });
773
774 } catch (const std::exception &e) {
775 std::string err = std::string(_("Errore update_process_list: ")) + e.what();
776 write_log(err);
777 }
778 }).detach();
779}

References _, alive_, detect_active_devices(), and write_log().

Referenced by manual_unlock_devices(), on_refresh_clicked(), PrivacyPage(), run_privacy_script_async(), setup_auto_refresh_toggle(), and show_next_alert().

◆ write_log()

void PrivacyPage::write_log ( const std::string & msg)
private

Definition at line 783 of file PrivacyPage.cpp.

783 {
784 std::thread([msg]() {
785 try {
786 std::string base_dir = Glib::get_home_dir() + "/.local/share/BastionGuard/logs";
787 if (!fs::exists(base_dir))
788 fs::create_directories(base_dir);
789
790
791 auto t = std::time(nullptr);
792 std::tm tm{};
793 localtime_r(&t, &tm); // thread-safe rispetto a std::localtime
794 char datebuf[16];
795 std::strftime(datebuf, sizeof(datebuf), "%Y-%m-%d", &tm);
796 std::string log_path = base_dir + "/privacyd_" + std::string(datebuf) + ".log";
797
798
799 std::ofstream log(log_path, std::ios::app);
800 if (log.is_open()) {
801 char timebuf[32];
802 std::strftime(timebuf, sizeof(timebuf), "%H:%M:%S", &tm);
803 log << "[" << timebuf << "] " << msg << std::endl;
804 }
805
806
807 const auto now = std::chrono::system_clock::now();
808 const auto seven_days = std::chrono::hours(24 * 7);
809
810 for (const auto& entry : fs::directory_iterator(base_dir)) {
811 if (entry.is_regular_file()) {
812 auto ftime = fs::last_write_time(entry);
813 auto sctp = std::chrono::time_point_cast<std::chrono::system_clock::duration>(
814 ftime - fs::file_time_type::clock::now()
815 + std::chrono::system_clock::now());
816 auto age = now - sctp;
817 if (age > seven_days) {
818 try {
819 fs::remove(entry.path());
820 } catch (...) {
821
822 }
823 }
824 }
825 }
826
827 } catch (...) {
828 std::cerr << _("[Privacy] Errore scrittura log giornaliero.") << std::endl;
829 }
830 }).detach();
831}

References _.

Referenced by manual_unlock_devices(), monitor_loop(), on_refresh_clicked(), on_toggle_privacy(), PrivacyPage(), run_privacy_script_async(), save_privacy_state(), setup_auto_refresh_toggle(), show_next_alert(), start_monitor_thread(), start_privacy_daemon(), unblock_devices_local(), update_process_list(), and ~PrivacyPage().

Member Data Documentation

◆ alive_

◆ auto_refresh_conn_

sigc::connection PrivacyPage::auto_refresh_conn_
private

Definition at line 53 of file PrivacyPage.hpp.

Referenced by setup_auto_refresh_toggle().

◆ btn_refresh_

Gtk::Button PrivacyPage::btn_refresh_
private

Definition at line 43 of file PrivacyPage.hpp.

Referenced by PrivacyPage().

◆ btn_unlock_

Gtk::Button PrivacyPage::btn_unlock_
private

Definition at line 83 of file PrivacyPage.hpp.

Referenced by manual_unlock_devices(), PrivacyPage(), and show_next_alert().

◆ chk_auto_refresh_

Gtk::CheckButton PrivacyPage::chk_auto_refresh_
private

Definition at line 44 of file PrivacyPage.hpp.

Referenced by PrivacyPage(), and setup_auto_refresh_toggle().

◆ devices_blocked_

std::atomic<bool> PrivacyPage::devices_blocked_ {false}
private

Definition at line 84 of file PrivacyPage.hpp.

Referenced by manual_unlock_devices(), and show_next_alert().

◆ known_processes_

std::set<std::string> PrivacyPage::known_processes_
private

Definition at line 52 of file PrivacyPage.hpp.

Referenced by monitor_loop().

◆ lbl_status_

◆ lbl_title_

Gtk::Label PrivacyPage::lbl_title_
private

Definition at line 41 of file PrivacyPage.hpp.

Referenced by PrivacyPage().

◆ monitor_running_

std::atomic<bool> PrivacyPage::monitor_running_ {false}
private

Definition at line 51 of file PrivacyPage.hpp.

Referenced by monitor_loop(), PrivacyPage(), run_privacy_script_async(), and ~PrivacyPage().

◆ monitor_thread_

std::thread PrivacyPage::monitor_thread_
private

Definition at line 50 of file PrivacyPage.hpp.

Referenced by run_privacy_script_async(), start_monitor_thread(), and ~PrivacyPage().

◆ scroller_

Gtk::ScrolledWindow PrivacyPage::scroller_
private

Definition at line 45 of file PrivacyPage.hpp.

Referenced by PrivacyPage().

◆ switch_webcam_

Gtk::Switch PrivacyPage::switch_webcam_
private

Definition at line 42 of file PrivacyPage.hpp.

Referenced by on_toggle_privacy(), PrivacyPage(), and run_privacy_script_async().

◆ toggle_busy_

std::atomic<bool> PrivacyPage::toggle_busy_ {false}
private

Definition at line 58 of file PrivacyPage.hpp.

Referenced by on_toggle_privacy(), and run_privacy_script_async().

◆ txt_processes_

Gtk::TextView PrivacyPage::txt_processes_
private

Definition at line 46 of file PrivacyPage.hpp.

Referenced by PrivacyPage().


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