33#include <unordered_set>
34#include <openssl/sha.h>
35#include <nlohmann/json.hpp>
36#include <sys/inotify.h>
39namespace fs = std::filesystem;
40using json = nlohmann::json;
67 return std::chrono::duration_cast<std::chrono::milliseconds>(
68 std::chrono::steady_clock::now().time_since_epoch()).count();
71static bool ends_with(
const std::string& s,
const std::string& suf) {
72 return s.size() >= suf.size() &&
73 s.compare(s.size() - suf.size(), suf.size(), suf) == 0;
80 static const char* exts[] = {
81 ".o",
".obj",
".a",
".lo",
".la",
".pyc",
".pyo",
82 ".class",
".d",
".gch",
".pch",
".mod",
".swp",
".swo",
83 ".rlib",
".rmeta",
".tmp",
".bc",
".ll"
85 for (
auto* e : exts) {
94 static const char* needles[] = {
95 "/build/",
"/cmake-build-",
"/CMakeFiles/",
"/.ninja_",
98 "/dist/",
"/out/",
"/bin/",
"/obj/",
99 "/.gradle/",
"/.m2/",
"/.cargo/",
100 "/__pycache__/",
"/.pytest_cache/",
101 "/.next/",
"/.nuxt/",
"/.svelte-kit/",
102 "/.tox/",
"/.venv/",
"/venv/",
103 "/Debug/",
"/Release/"
105 for (
auto* n : needles) {
106 if (path.find(n) != std::string::npos)
return true;
112 while (!p.empty() && p.back() ==
'/')
117static std::string
trim(
const std::string& s) {
118 size_t start = s.find_first_not_of(
" \n\r\t");
119 if (start == std::string::npos)
return "";
120 size_t end = s.find_last_not_of(
" \n\r\t");
121 return s.substr(start, end - start + 1);
127 if (
alive_)
alive_->store(
false, std::memory_order_release);
132: Gtk::Box(Gtk::Orientation::VERTICAL, 5),
159 autoScanSwitch.property_active().signal_changed().connect([
this]() {
162 :
_(
"⛔ Scansione automatica disattivata"));
175 textView.set_wrap_mode(Gtk::WrapMode::WORD_CHAR);
177 auto scrollLog = Gtk::make_managed<Gtk::ScrolledWindow>();
179 scrollLog->set_min_content_height(180);
180 scrollLog->set_expand(
true);
184 auto scrollFiles = Gtk::make_managed<Gtk::ScrolledWindow>();
186 scrollFiles->set_min_content_height(150);
187 scrollFiles->set_expand(
true);
188 append(*scrollFiles);
192 auto lbl = Gtk::make_managed<Gtk::Label>(
_(
"API Key VirusTotal:"));
193 lbl->set_valign(Gtk::Align::CENTER);
202 lbl->set_valign(Gtk::Align::CENTER);
218 btnPrivacy.set_icon_name(
"dialog-information-symbolic");
219 btnPrivacy.set_tooltip_text(
_(
"Privacy e dati cloud"));
234 apiKeySizeGroup = Gtk::SizeGroup::create(Gtk::SizeGroup::Mode::VERTICAL);
250 btnPrivacy.set_icon_name(
"dialog-information");
267 inotifyFd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
269 enqueueLog(
_(
"❌ Errore: inotify non inizializzato"));
287 std::lock_guard<std::mutex> lock(
logMutex);
291 Glib::signal_timeout().connect_once(
298 std::vector<std::string> batch;
300 std::lock_guard<std::mutex> lock(
logMutex);
305 for (
auto& m : batch) s += m +
"\n";
311 return Glib::get_home_dir() +
"/.local/share/BastionGuard/config.json";
316 fs::create_directories(Glib::get_home_dir() +
"/.local/share/BastionGuard");
337 namespace fs = std::filesystem;
339 if (!fs::exists(root))
return;
358 int wd = inotify_add_watch(
361 IN_CREATE | IN_MOVED_TO | IN_CLOSE_WRITE | IN_ATTRIB
365 enqueueLog(
_(
"⚠️ Impossibile monitorare: ") + norm);
374 for (
auto& entry : fs::directory_iterator(norm)) {
375 if (entry.is_directory()) {
376 std::string sub = entry.path().string();
378 std::string name = entry.path().filename().string();
379 if (!name.empty() && name[0] ==
'.')
401 if (!
static_cast<bool>(cond & Glib::IOCondition::IO_IN))
405 __attribute__((aligned(__alignof__(
struct inotify_event))));
407 ssize_t len = read(
inotifyFd, buffer,
sizeof(buffer));
411 const char* ptr = buffer;
413 while (ptr < buffer + len) {
414 const inotify_event* ev =
415 reinterpret_cast<const inotify_event*
>(ptr);
417 std::string parentDir =
420 std::string name = ev->len ? std::string(ev->name) :
"";
421 std::string fullPath = parentDir +
"/" + name;
425 ptr +=
sizeof(
struct inotify_event) + ev->len;
433 auto dialog = Gtk::FileDialog::create();
434 dialog->set_title(
_(
"Seleziona cartella"));
435 dialog->set_modal(
true);
437 dialog->select_folder(
447 Glib::RefPtr<Gtk::FileDialog> dialog)
450 auto file = dialog->select_folder_finish(result);
457 catch (
const Glib::Error&) {
465 auto dialog = Gtk::FileDialog::create();
466 dialog->set_title(
_(
"Seleziona file"));
467 dialog->set_modal(
true);
479 Glib::RefPtr<Gtk::FileDialog> dialog)
482 auto file = dialog->open_finish(result);
489 catch (
const Glib::Error&) {
496 std::string path = Glib::get_home_dir() +
"/.config/BastionGuard/cloud.conf";
499 if (!fs::exists(path)) {
504 std::ifstream f(path);
507 while (std::getline(f, line)) {
509 if (line.empty() || line[0] ==
'#')
continue;
511 size_t eq = line.find(
'=');
512 if (eq == std::string::npos)
continue;
514 std::string key =
trim(line.substr(0, eq));
515 std::string value =
trim(line.substr(eq + 1));
517 if (key ==
"malware_bazaar_api_key") {
523 enqueueLog(
_(
"🔑 API Key VirusTotal caricata da cloud.conf"));
534 std::string dir = Glib::get_home_dir() +
"/.config/BastionGuard";
535 std::string path = dir +
"/cloud.conf";
538 fs::create_directories(dir);
540 std::ofstream f(path);
561 if (path.empty())
return;
567 if (path.find(
"/.cache/") != std::string::npos ||
568 path.find(
"/.local/") != std::string::npos ||
569 path.find(
"/.git/") != std::string::npos)
573 if (!(mask & (IN_CLOSE_WRITE | IN_MOVED_TO)))
594 const int64_t now =
nowMs();
611 std::memory_order_relaxed);
614 enqueueLog(
_(
"⏸️ Compilazione/burst rilevato: scansione automatica sospesa temporaneamente"));
622 Glib::signal_timeout().connect_once([path](){
627 Glib::signal_timeout().connect_once(
629 if (!fs::exists(path))
return;
648 enqueueLog(
_(
"🛑 Tutti i monitor inotify sono stati disattivati"));
654 enqueueLog(
_(
"⏳ È già in corso un'altra scansione..."));
659 textBuffer->set_text(
_(
"⚠️ Nessun file o cartella selezionato.\n"));
666 textBuffer->set_text(
_(
"🔍 Avvio scansione con clamdscan...\n\n"));
672 std::vector<std::string> argv = {
682 Gio::Subprocess::Flags::STDOUT_PIPE |
683 Gio::Subprocess::Flags::STDERR_PIPE
691 Glib::RefPtr<Gio::Cancellable>(),
692 Glib::PRIORITY_DEFAULT
695 catch (
const Glib::Error& ex) {
697 _(
"❌ Errore avvio clamdscan: ") + Glib::ustring(ex.what()) +
"\n"
708 bool ok =
dataStream->read_line_finish(result, line);
717 Glib::signal_idle().connect_once([
this, alive, line]() {
722 if (line.rfind(
"Scanning ", 0) == 0) {
723 std::string filepath = line.substr(9);
728 if (line.find(
"FOUND") != std::string::npos) {
730 size_t pos = line.find(
": ");
731 size_t end = line.find(
" FOUND");
733 if (pos != std::string::npos && end != std::string::npos) {
734 std::string infected = line.substr(0, pos);
735 std::string virus = line.substr(pos + 2, end - (pos + 2));
747 Glib::RefPtr<Gio::Cancellable>(),
748 Glib::PRIORITY_DEFAULT
751 catch (
const Glib::Error& ex) {
753 _(
"❌ Errore lettura output: ") + Glib::ustring(ex.what()) +
"\n"
763 if (!fs::exists(filepath))
return;
765 if (!fs::is_regular_file(filepath)) {
766 enqueueLog(
_(
"📁 Ignorato (non è un file regolare): ") + filepath);
771 std::uintmax_t size = fs::file_size(filepath);
773 enqueueLog(
_(
"⏳ File troppo piccolo, potrebbe essere ancora in scrittura: ") + filepath);
778 enqueueLog(
_(
"❌ Impossibile determinare dimensione file: ") + filepath);
783 auto size = fs::file_size(filepath);
784 auto timep = fs::last_write_time(filepath).time_since_epoch().count();
786 std::string key = filepath +
"_" + std::to_string(size) +
"_" + std::to_string(timep);
791 Glib::signal_timeout().connect_once([key]() {
795 enqueueLog(
_(
"🔍 Scansione automatica per: ") + filepath);
797 std::vector<std::string> argv = {
805 auto proc = Gio::Subprocess::create(
807 Gio::Subprocess::Flags::STDOUT_PIPE
810 auto out = proc->get_stdout_pipe();
811 auto ds = Gio::DataInputStream::create(out);
813 auto reader = std::make_shared<std::function<void()>>();
814 *reader = [
this, ds, filepath, reader]() {
816 [
this, ds, filepath, reader](
const Glib::RefPtr<Gio::AsyncResult>& res) {
819 bool ok = ds->read_line_finish(res, line);
824 Glib::signal_idle().connect_once([
this, alive, filepath]() {
826 enqueueLog(
_(
"⬜ ClamAV pulito, avvio controllo cloud..."));
834 if (line.find(
"FOUND") != std::string::npos) {
835 size_t pos = line.find(
": ");
836 size_t end = line.find(
" FOUND");
838 std::string virus =
"Sconosciuto";
839 if (pos != std::string::npos && end != std::string::npos)
840 virus = line.substr(pos + 2, end - (pos + 2));
843 Glib::signal_idle().connect_once([
this, alive, filepath, virus]() {
854 catch (
const Glib::Error& ex) {
855 enqueueLog(std::string(
_(
"❌ Errore scansione automatica: ")) + ex.what());
857 catch (
const std::exception& ex) {
858 enqueueLog(std::string(
_(
"❌ Eccezione scansione automatica: ")) + ex.what());
864 Glib::RefPtr<Gio::Cancellable>(),
865 Glib::PRIORITY_DEFAULT
871 catch (
const std::exception& ex) {
872 enqueueLog(std::string(
_(
"❌ Errore accesso file: ")) + ex.what());
880 auto row = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 6);
882 auto icon = Gtk::make_managed<Gtk::Image>();
883 icon->set_from_icon_name(
"text-x-generic");
884 icon->set_pixel_size(20);
887 auto lbl = Gtk::make_managed<Gtk::Label>(filepath);
888 lbl->set_hexpand(
true);
889 lbl->set_ellipsize(Pango::EllipsizeMode::END);
897 std::vector<std::string> dirs;
898 std::string home = Glib::get_home_dir();
901 std::string def = home +
"/Downloads";
902 if (fs::exists(def)) dirs.push_back(def);
906 std::string base = home +
"/.mozilla/firefox";
907 if (fs::exists(base)) {
908 for (
auto& entry : fs::directory_iterator(base)) {
909 if (!entry.is_directory())
continue;
911 fs::path prefs = entry.path() /
"prefs.js";
912 if (!fs::exists(prefs))
continue;
914 std::ifstream f(prefs);
917 while (std::getline(f, line)) {
918 if (line.find(
"browser.download.dir") != std::string::npos) {
919 size_t s = line.find(
'"', line.find(
',') + 1);
920 size_t e = line.find(
'"', s + 1);
921 if (s != std::string::npos && e != std::string::npos) {
922 std::string d = line.substr(s + 1, e - s - 1);
923 if (fs::exists(d)) dirs.push_back(d);
932 std::vector<std::string> cfgs = {
933 home +
"/.config/google-chrome/Default/Preferences",
934 home +
"/.config/chromium/Default/Preferences",
935 home +
"/.config/microsoft-edge/Default/Preferences"
938 for (
auto& pref : cfgs) {
940 if (!fs::exists(pref))
continue;
942 std::ifstream f(pref);
944 (std::istreambuf_iterator<char>(f)),
945 std::istreambuf_iterator<char>()
948 std::string key =
"\"default_directory\"";
949 size_t pos = content.find(key);
950 if (pos == std::string::npos)
continue;
952 size_t s = content.find(
'"', pos + key.size());
953 size_t e = content.find(
'"', s + 1);
955 if (s != std::string::npos && e != std::string::npos) {
956 std::string d = content.substr(s + 1, e - s - 1);
957 if (fs::exists(d)) dirs.push_back(d);
963 std::sort(dirs.begin(), dirs.end());
964 dirs.erase(std::unique(dirs.begin(), dirs.end()), dirs.end());
973 if (path.rfind(d, 0) == 0)
return false;
984 if (!fs::exists(d))
continue;
988 enqueueLog(
_(
"📡 Monitoraggio Download attivo (inotify)"));
994 std::vector<std::string> out;
996 std::string p = Glib::get_home_dir() +
"/.config/user-dirs.dirs";
997 if (!fs::exists(p))
return out;
1002 while (std::getline(f, line)) {
1003 if (line.find(
"XDG_") != 0 || line.find(
"_DIR=") == std::string::npos)
1006 size_t q1 = line.find(
'"');
1007 size_t q2 = line.find(
'"', q1 + 1);
1009 if (q1 == std::string::npos || q2 == std::string::npos)
1012 std::string value = line.substr(q1 + 1, q2 - q1 - 1);
1013 if (value.find(
"$HOME") == 0)
1014 value.replace(0, 5, Glib::get_home_dir());
1016 if (fs::exists(value))
1017 out.push_back(value);
1035 std::vector<std::string> dirs;
1038 dirs.insert(dirs.end(), xdg.begin(), xdg.end());
1040 std::string varApp = Glib::get_home_dir() +
"/.var/app";
1041 if (fs::exists(varApp)) dirs.push_back(varApp);
1043 std::sort(dirs.begin(), dirs.end());
1044 dirs.erase(std::unique(dirs.begin(), dirs.end()), dirs.end());
1046 for (
auto& d : dirs) {
1047 if (!fs::exists(d))
continue;
1053 enqueueLog(
_(
"🌍 Monitor globale inotify attivo (XDG dirs)"));
1066 if (path.rfind(
"INFECTED:", 0) == 0) {
1067 std::string real = path.substr(11);
1084 if (!fs::exists(path)) {
1091 std::vector<std::string> argv = {
1100 auto proc = Gio::Subprocess::create(
1102 Gio::Subprocess::Flags::STDOUT_PIPE
1105 auto out = proc->get_stdout_pipe();
1106 auto ds = Gio::DataInputStream::create(out);
1108 ds->read_line_async(
1109 [
this, ds, path](
const Glib::RefPtr<Gio::AsyncResult>& res) {
1112 bool ok = ds->read_line_finish(res, line);
1114 if (ok && line.find(
"FOUND") != std::string::npos) {
1116 size_t pos = line.find(
": ");
1117 size_t end = line.find(
" FOUND");
1119 std::string virus =
"Sconosciuto";
1120 if (pos != std::string::npos && end != std::string::npos)
1121 virus = line.substr(pos + 2, end - (pos + 2));
1124 Glib::signal_idle().connect_once([
this, alive, path, virus]() {
1134 Glib::signal_idle().connect_once([
this, alive, path]() {
1144 Glib::RefPtr<Gio::Cancellable>(),
1145 Glib::PRIORITY_DEFAULT
1149 enqueueLog(
_(
"❌ Errore avvio clamdscan per PKGBUILD"));
1156 if (!fs::exists(filepath))
1165 if (!
alive_->load())
return;
1169 Glib::signal_idle().connect_once(
1170 [
this, alive, filepath, r]() {
1173 enqueueLog(
_(
"🔏 Nessun match cloud → File sicuro."));
1183 auto conn = Gio::DBus::Connection::get_sync(
1184 Gio::DBus::BusType::SESSION
1186 auto proxy = Gio::DBus::Proxy::create_sync(
1188 "org.BastionGuard.Ransomware.Alert",
1189 "/org/BastionGuard/ransomware/alert",
1190 "org.BastionGuard.Ransomware.Alert"
1193 auto params = Glib::VariantContainerBase::create_tuple({
1194 Glib::Variant<Glib::ustring>::create(filepath),
1195 Glib::Variant<Glib::ustring>::create(
1196 "VirusTotal: " + r.malwareName)
1199 proxy->call_sync(
"ShowAlert", params);
1204 (Glib::ustring(
"Malware cloud:\n") + filepath +
1205 "\nNome: " + r.malwareName).raw(),
1206 Gtk::MessageType::WARNING
1219 enqueueLog(
_(
"⏳ Troppi controlli cloud in corso, salto temporaneamente."));
1227 std::thread([
this, alive, filepath]() {
1231 catch (
const std::exception& ex) {
1233 Glib::signal_idle().connect_once([
this, alive, msg = std::string(ex.what())]() {
1234 BastionGuard::if_alive(alive, [this, &msg]() {
1235 enqueueLog(std::string(_(
"❌ Errore controllo cloud: ")) + msg);
1241 Glib::signal_idle().connect_once([
this, alive]() {
1243 enqueueLog(
_(
"❌ Errore controllo cloud sconosciuto."));
1261 if (files.empty())
return;
1264 auto conn = Gio::DBus::Connection::get_sync(Gio::DBus::BusType::SESSION);
1265 auto proxy = Gio::DBus::Proxy::create_sync(
1267 "org.BastionGuard.Ransomware.Alert",
1268 "/org/BastionGuard/ransomware/alert",
1269 "org.BastionGuard.Ransomware.Alert"
1272 for (
const auto& f : files) {
1273 auto params = Glib::VariantContainerBase::create_tuple({
1274 Glib::Variant<Glib::ustring>::create(f),
1275 Glib::Variant<Glib::ustring>::create(
"ClamAV.Detected")
1277 proxy->call_sync(
"ShowAlert", params);
1279 enqueueLog(
_(
"⚠️ Alert inviato per file infetto: ") + f);
1285 _(
"File infetto rilevato:\n") + files.front(),
1286 Gtk::MessageType::WARNING
1293 const std::string& filepath,
1294 const std::string& virusName)
1297 auto conn = Gio::DBus::Connection::get_sync(Gio::DBus::BusType::SESSION);
1298 auto proxy = Gio::DBus::Proxy::create_sync(
1300 "org.BastionGuard.Ransomware.Alert",
1301 "/org/BastionGuard/ransomware/alert",
1302 "org.BastionGuard.Ransomware.Alert"
1305 auto params = Glib::VariantContainerBase::create_tuple({
1306 Glib::Variant<Glib::ustring>::create(filepath),
1307 Glib::Variant<Glib::ustring>::create(virusName)
1310 proxy->call_sync(
"ShowAlert", params);
1312 enqueueLog(
_(
"⚠️ File infetto: ") + filepath +
" (" + virusName +
")");
1317 _(
"File infetto:\n") + filepath +
"\nVirus: " + virusName,
1318 Gtk::MessageType::WARNING
1328 _(
"Minaccia rilevata:\n") + file +
_(
"\nTipo: ") + family,
1329 Gtk::MessageType::WARNING
1336 try {
return fs::exists(p); }
1337 catch (...) {
return false; }
1342 try {
return fs::file_size(p); }
1343 catch (...) {
return 0; }
1348 auto *dialog =
new Gtk::Window();
1349 dialog->set_title(
"");
1350 dialog->set_default_size(560, 300);
1351 dialog->set_resizable(
false);
1352 dialog->set_modal(
false);
1353 dialog->add_css_class(
"app-dialog");
1354 dialog->add_css_class(
"main-window");
1355 dialog->add_css_class(
"app-window");
1359 auto css = Gtk::CssProvider::create();
1360 css->load_from_path(
resource(
"BastionGuard.css"));
1361 auto display = Gdk::Display::get_default();
1362 Gtk::StyleContext::add_provider_for_display(
1364 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
1368 auto *header = Gtk::make_managed<Gtk::HeaderBar>();
1369 header->set_show_title_buttons(
false);
1370 header->set_decoration_layout(
"");
1371 header->add_css_class(
"custom-headerbar");
1373 auto *close_box = Gtk::make_managed<Gtk::Box>(
1374 Gtk::Orientation::HORIZONTAL);
1375 close_box->set_margin_end(6);
1378 auto icon = Gdk::Pixbuf::create_from_file(
1379 resource(
"icon-close.png"), 28, 28,
true);
1380 auto *btn = Gtk::make_managed<Gtk::Button>();
1382 *Gtk::make_managed<Gtk::Image>(icon));
1383 btn->add_css_class(
"header-button");
1384 btn->signal_clicked().connect(
1385 [dialog]() { dialog->close();
delete dialog; });
1386 close_box->append(*btn);
1389 header->pack_end(*close_box);
1392 Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL);
1393 wrap->append(*header);
1394 dialog->set_titlebar(*wrap);
1397 auto *vbox = Gtk::make_managed<Gtk::Box>(
1398 Gtk::Orientation::VERTICAL, 14);
1399 vbox->set_margin(24);
1401 auto title = Gtk::make_managed<Gtk::Label>(
1402 _(
"🔐 Privacy – Controllo Cloud"));
1403 title->add_css_class(
"title");
1404 title->set_halign(Gtk::Align::CENTER);
1406 auto msg = Gtk::make_managed<Gtk::Label>(
_(
1407 "BastionGuard NON carica alcun file online.\n\n"
1408 "• Nessun contenuto locale viene trasmesso\n"
1409 "• Viene calcolato solo l’hash SHA256 del file\n"
1410 "• L’hash è confrontato con database pubblici\n"
1411 "• Nessun percorso o dato personale viene inviato\n"
1412 "• La API Key resta salvata esclusivamente in locale\n\n"
1413 "Il controllo cloud è una verifica di reputazione, "
1414 "non un upload del file.")
1416 msg->set_wrap(
true);
1417 msg->set_justify(Gtk::Justification::CENTER);
1418 msg->set_halign(Gtk::Align::CENTER);
1421 Gtk::make_managed<Gtk::Button>(
"Ho capito");
1422 closeBtn->add_css_class(
"btn-success");
1423 closeBtn->set_halign(Gtk::Align::CENTER);
1424 closeBtn->signal_clicked().connect(
1425 [dialog]() { dialog->close();
delete dialog; });
1427 vbox->append(*title);
1429 vbox->append(*closeBtn);
1431 dialog->set_child(*vbox);
1443 ?
"view-conceal-symbolic"
1444 :
"view-reveal-symbolic"
static std::string trim(const std::string &s)
std::string globalMalwareBazaarApiKey
static std::atomic< int64_t > g_burstWindowStartMs
static constexpr int64_t kThrottleHoldMs
std::uintmax_t safeSize(const std::string &p) noexcept
static bool isBuildArtifactExt(const std::string &path)
static constexpr int kBurstThreshold
std::string getConfigPath()
static std::atomic< int > activeCloudChecks
static std::string trim(const std::string &s)
static bool ends_with(const std::string &s, const std::string &suf)
static std::atomic< int > g_recentEventCount
static std::string normalizePath(std::string p)
static std::atomic< int64_t > g_throttleUntilMs
static std::unordered_set< std::string > recentlyScanned
static constexpr int kBurstWindowMs
static bool isInBuildDir(const std::string &path)
static MBResult lookupHash(const std::string &sha256)
static std::string sha256_file(const std::string &filepath)
Glib::RefPtr< Gio::InputStream > stdoutStream
BastionGuard::AliveFlag alive_
bool onInotifyEvent(Glib::IOCondition cond)
Glib::RefPtr< Glib::IOSource > inotifySource
void scanAurAuto(const std::string &path)
void enableAutoScanDownloads()
void logMessage(const std::string &msg)
std::vector< std::string > infectedFilesBuffer
std::vector< std::string > detectBrowserDownloadDirs()
void showAlertWindow(const std::string &file, const std::string &family)
std::vector< std::string > pendingLog
void handleInfectedFile(const std::string &filepath)
Glib::RefPtr< Gtk::SizeGroup > apiKeySizeGroup
bool safeExists(const std::string &p) noexcept
void showCloudPrivacyDisclaimer()
Gtk::Switch autoScanSwitch
void onChooseFolderFinish(const Glib::RefPtr< Gio::AsyncResult > &result, Glib::RefPtr< Gtk::FileDialog > dialog)
void cloudCheckMalwareBazaarWorker(const std::string &filepath)
void onToggleApiKeyVisibility()
void addInotifyRecursive(const std::string &path)
std::unordered_map< int, std::string > watchMap
std::vector< std::string > getXdgUserDirs()
void handleInfectedFileWithName(const std::string &filepath, const std::string &virusName)
void readOutput(const Glib::RefPtr< Gio::AsyncResult > &result)
Glib::RefPtr< Gtk::TextBuffer > textBuffer
void handleInfectedFiles(const std::vector< std::string > &files)
Gtk::Button btnChooseFolder
void enqueueLog(const std::string &msg)
Gtk::Button btnShowApiKey
void enableGlobalDownloadMonitor()
std::unordered_set< std::string > alreadyWatched
std::string malwareBazaarApiKey
void onAurPkgbuildDetected(const std::string &path)
bool isOutsideKnownDownloadDirs(const std::string &path)
void onApiKeySaveClicked()
void cloudCheckMalwareBazaar(const std::string &filepath)
void scanFileAutomatically(const std::string &filepath)
void addFileToList(const std::string &filepath)
Gtk::Button btnApiKeySave
Glib::RefPtr< Gio::Subprocess > subprocess
Gtk::Window & parentWindow
Gtk::Button btnChooseFile
void disableAllMonitors()
std::vector< std::string > knownDownloadDirs
Gtk::Box apiKeyActionsBox
void handleInotifyFileEvent(const std::string &path, uint32_t mask)
void onChooseFileFinish(const Glib::RefPtr< Gio::AsyncResult > &result, Glib::RefPtr< Gtk::FileDialog > dialog)
Glib::RefPtr< Gio::DataInputStream > dataStream
ScanPage(Gtk::Window &parent)
static void show_message(Gtk::Window *parent, const std::string &message, Gtk::MessageType type=Gtk::MessageType::INFO, const std::string &secondary=std::string())
void scanPKGBUILD(const std::string &path)
void if_alive(const AliveFlag &alive, Fn &&fn)
std::string resource(const std::string &rel)