1. Overview
The ScanPage.hpp header defines the ScanPage class, a GTKmm (GTK4) UI component responsible for BastionGuard’s file and folder scanning workflow. The page provides both manual scanning (user-selected file/folder) and automatic monitoring (downloads and system locations) using an inotify-based engine. It integrates with a ClamAV subprocess for on-device scanning and optionally performs a cloud reputation check via MalwareBazaar, guarded by an API key and a privacy disclaimer.
Functionally, ScanPage provides:
- Folder and file selection via GTK dialogs, with a “Scan” action
- On-device scan execution via a ClamAV subprocess (async output parsing)
- Auto-scan toggle for downloads/system monitoring
- Inotify-based recursive monitoring with duplicate watch avoidance
- Results presentation in a log view and an infected-files list
- Cloud reputation checks (MalwareBazaar) with API key persistence
- Special handling for AUR (Arch Linux) package build detection
- Thread-safe, batched log buffering to keep the UI responsive
2. Dependencies and Includes
#include <gtkmm.h>
#include <giomm.h>
#include <glibmm.h>
#include <glib/gi18n.h>
#include <string>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <filesystem>
#include <mutex>
- gtkmm.h – GTK4 widgets, containers, dialogs, and signals
- giomm.h – GIO async APIs and
Gio::Subprocessintegration - glibmm.h – GLib main-loop helpers, IO sources, and utilities
- glib/gi18n.h – gettext macro
_()for localization-ready UI strings - <filesystem> – path operations and existence checks
- <unordered_map> / <unordered_set> – watch bookkeeping and deduplication
- <mutex> – log batching synchronization
3. Class Declaration and Scope
class ScanPage : public Gtk::Box
The class derives from Gtk::Box, enabling composition as a page in the main application navigation stack. The constructor takes a reference to a parent window to anchor dialogs and modal alerts.
explicit ScanPage(Gtk::Window& parent);
4. Public Interface
4.1 Constructor
explicit ScanPage(Gtk::Window& parent);
Initializes the scan UI, binds signals for user actions (choose folder/file, start scan, toggle auto-scan), prepares buffers and list widgets, loads persisted configuration, and sets up monitoring state as configured.
5. UI Components
ScanPage provides a composite layout made of manual scan controls, an auto-scan toggle, a text-based log output view, and an infected-file list area. It also includes a dedicated section for cloud API key management.
5.1 Manual Scan Controls
Gtk::Box buttonBox{Gtk::Orientation::HORIZONTAL, 5};
Gtk::Button btnChooseFolder{_("Seleziona cartella")};
Gtk::Button btnChooseFile{_("Seleziona file")};
Gtk::Button btnScan{_("Scansiona")};
- btnChooseFolder – opens a folder chooser dialog
- btnChooseFile – opens a file chooser dialog
- btnScan – triggers scanning of the selected path
- buttonBox – horizontal container providing consistent spacing
5.2 Auto-Scan Controls
Gtk::Box autoScanBox{Gtk::Orientation::HORIZONTAL, 6};
Gtk::Label autoScanLabel;
Gtk::Switch autoScanSwitch;
bool autoScanEnabled = true;
Toggle that enables/disables automatic monitoring and scanning of downloads/system paths. The autoScanEnabled flag caches the current state.
5.3 Log View
Gtk::TextView textView;
Glib::RefPtr<Gtk::TextBuffer> textBuffer;
Text-based output panel showing scan progress, warnings, detections, and cloud results. Updates are batched to avoid overloading the GTK main loop.
5.4 Infected Files List
Gtk::ListBox fileListBox;
List widget used to present detected infected files or items requiring attention. Entries are typically added as detections occur.
5.5 Cloud API Key UI
Gtk::Box apiKeyActionsBox{Gtk::Orientation::HORIZONTAL, 6};
Glib::RefPtr<Gtk::SizeGroup> apiKeySizeGroup;
Gtk::Box apiKeyBox{Gtk::Orientation::HORIZONTAL, 6};
Gtk::Entry apiKeyEntry;
Gtk::Button btnShowApiKey;
Gtk::Button btnApiKeySave{"Salva"};
Gtk::Button btnPrivacy;
bool apiKeyVisible = false;
std::string malwareBazaarApiKey;
- apiKeyEntry – API key input field for MalwareBazaar cloud checks
- btnShowApiKey – toggles key visibility
- btnApiKeySave – persists the API key (cloud configuration)
- btnPrivacy – shows a privacy disclaimer related to cloud queries
- apiKeySizeGroup – aligns control sizing for consistent layout
6. Internal State and Data Model
ScanPage maintains state for scan execution, monitoring, watch bookkeeping, and batched logging.
6.1 ClamAV Subprocess State
Glib::RefPtr<Gio::Subprocess> subprocess;
Glib::RefPtr<Gio::InputStream> stdoutStream;
Glib::RefPtr<Gio::DataInputStream> dataStream;
- subprocess – ClamAV scanner process instance
- stdoutStream – raw stream for process output
- dataStream – structured reader for line-based parsing
6.2 Log Batch Buffer
std::mutex logMutex;
std::vector<std::string> pendingLog;
bool logFlushScheduled = false;
- pendingLog – queued messages awaiting insertion into the GTK buffer
- logMutex – protects concurrent access to the queue
- logFlushScheduled – prevents redundant UI flush scheduling
6.3 Manual Scan State
std::string selectedPath;
std::vector<std::string> infectedFilesBuffer;
bool scanInProgress = false;
- selectedPath – current file/folder selected by the user
- infectedFilesBuffer – collection used to accumulate detections
- scanInProgress – guard to prevent overlapping scan runs
6.4 Download Directory Detection
std::vector<std::string> knownDownloadDirs;
Cache of directories considered “known download locations” derived from browser heuristics and XDG user dirs.
6.5 Inotify Monitoring State
int inotifyFd = -1;
Glib::RefPtr<Glib::IOSource> inotifySource;
std::unordered_map<int, std::string> watchMap;
std::unordered_set<std::string> alreadyWatched;
- inotifyFd – inotify file descriptor
- inotifySource – GLib IO source binding inotify events to the main loop
- watchMap – watch descriptor to path mapping
- alreadyWatched – deduplication set to avoid redundant recursive watches
7. User Actions (Callbacks)
void onChooseFolder();
void onChooseFile();
void onStartScan();
void onChooseFolderFinish(const Glib::RefPtr<Gio::AsyncResult>& result,
Glib::RefPtr<Gtk::FileDialog> dialog);
void onChooseFileFinish(const Glib::RefPtr<Gio::AsyncResult>& result,
Glib::RefPtr<Gtk::FileDialog> dialog);
- onChooseFolder() – initiates async folder selection
- onChooseFile() – initiates async file selection
- onStartScan() – starts a scan for the current selection
- onChooseFolderFinish() / onChooseFileFinish() – finalize selection and update internal state
7.1 API Key and Privacy Actions
void onApiKeySaveClicked();
void onToggleApiKeyVisibility();
void showCloudPrivacyDisclaimer();
- onApiKeySaveClicked() – persists the MalwareBazaar API key
- onToggleApiKeyVisibility() – shows/hides key contents in the entry field
- showCloudPrivacyDisclaimer() – displays privacy messaging for cloud checks
8. Internal Logic
8.1 Logging Pipeline
void enqueueLog(const std::string& msg);
void flushLog();
Implements a batched log write pattern to reduce UI churn. Messages are collected into pendingLog under a mutex and flushed to textBuffer on the GTK main loop.
8.2 Configuration Persistence
void saveConfig();
void loadConfig();
void saveCloudConfig();
void loadCloudConfig();
Separates general page configuration from cloud-specific configuration to keep sensitive cloud settings isolated and easier to manage.
8.3 Subprocess Output Handling
void readOutput(const Glib::RefPtr<Gio::AsyncResult>& result);
void addFileToList(const std::string& filepath);
Parses ClamAV subprocess output asynchronously and updates the UI. Detected items are added to the infected-files list as appropriate.
8.4 Download Directory Discovery
std::vector<std::string> detectBrowserDownloadDirs();
std::vector<std::string> getXdgUserDirs();
bool isOutsideKnownDownloadDirs(const std::string& path);
Locates common download directories using browser heuristics and XDG user directory definitions, then uses this knowledge to focus automatic monitoring where it is most valuable.
8.5 Inotify Engine
void addInotifyRecursive(const std::string& path);
bool onInotifyEvent(Glib::IOCondition cond);
void handleInotifyFileEvent(const std::string& path, uint32_t mask);
- addInotifyRecursive() – recursively registers watchers on a directory tree
- onInotifyEvent() – IO callback invoked when inotify events are available
- handleInotifyFileEvent() – routes file events to auto-scan logic
8.6 Automatic Monitoring Controls
void enableAutoScanDownloads();
void enableGlobalDownloadMonitor();
void disableAllMonitors();
Enables or disables automatic file monitoring. These methods coordinate watch registration, cleanup, and state transitions based on the autoScanSwitch setting.
8.7 Automatic Scan Heuristics
void scanFileAutomatically(const std::string& filepath);
bool isInterestingDownload(const std::string& path);
std::string resolveFinalDownloadedFile(const std::string& path);
Determines whether a file should be scanned automatically and attempts to resolve temporary or partial download artifacts to the final file (browser-dependent behavior).
8.8 AUR (Arch Linux) Handling
void onAurPkgbuildDetected(const std::string& path);
void scanAurAuto(const std::string& path);
Implements special-case handling for detected AUR-related files (e.g., PKGBUILD), enabling additional scrutiny in contexts where user-downloaded build scripts may pose risk.
8.9 Cloud Reputation Checks (MalwareBazaar)
void cloudCheckMalwareBazaar(const std::string& filepath);
void cloudCheckMalwareBazaarWorker(const std::string& filepath);
Performs an optional cloud lookup for a scanned file. The worker variant is designed for background execution, while the main entry point coordinates UI updates and result reporting.
8.10 Infected File Handling
void handleInfectedFile(const std::string& filepath);
void handleInfectedFiles(const std::vector<std::string>& files);
void handleInfectedFileWithName(const std::string& filepath, const std::string& virusName);
Centralizes detection handling, including per-file processing, batch handling, and cases where the detection name is explicitly available.
8.11 Alert and Safety Helpers
void showAlertWindow(const std::string& file, const std::string& family);
void logMessage(const std::string& msg);
bool safeExists(const std::string& p) noexcept;
- showAlertWindow() – graphical fallback alert for detections
- logMessage() – unified logging method (typically routes through the batching system)
- safeExists() – existence check hardened for error conditions
9. Auto-Update (Scheduled Refresh)
Automatic behavior is event-driven rather than timer-driven. The page relies on inotify events delivered through a GLib IO source (inotifySource) to respond to filesystem changes in monitored directories.
10. Settings Storage
The page declares separate persistence routines for general configuration and cloud configuration:
void saveConfig();
void loadConfig();
void saveCloudConfig();
void loadCloudConfig();
Storage locations and formats are implementation-defined. Cloud settings should be protected appropriately, and UI should clearly disclose what is stored and where.
11. Helper Functions and Filesystem Layout
The component interacts with user directories and download locations identified via XDG conventions and browser-specific heuristics. Monitoring is performed recursively, and a deduplication strategy prevents re-watching the same directories.
12. Runtime and Security Considerations
- Main-loop safety: UI updates must occur on the GTK main thread. Background work (cloud checks, heavy parsing) should be marshaled back safely.
- Subprocess containment: ClamAV execution should enforce timeouts, handle exit codes, and sanitize paths passed to the scanner to avoid command injection.
- Watch scalability: recursive inotify watches can be expensive; implement limits, avoid duplication (
alreadyWatched), and handle inotify exhaustion gracefully. - Privacy and cloud checks: cloud lookups can leak file fingerprints or metadata; require explicit consent and provide clear disclaimers (
btnPrivacy). - Secrets handling: API keys should be stored securely and never written to logs. Limit visibility with
apiKeyVisibleand protect configuration at rest. - Auto-scan policy: automatic scanning should apply only to “interesting” downloads to reduce noise and prevent scanning sensitive paths unnecessarily.
- Safe file resolution: temporary download files should be resolved to their final paths before scanning (
resolveFinalDownloadedFile()) to avoid partial-file false positives.