10#include <giomm/appinfo.h>
11#include <giomm/desktopappinfo.h>
12#include <giomm/themedicon.h>
19#include <unordered_map>
20#include <unordered_set>
23#define _(text) gettext(text)
28namespace fs = std::filesystem;
33N_(
"(processo sconosciuto)");
36N_(
"Processo locale sicuro");
40 std::unordered_map<std::string, AppMetadata>
by_key;
47std::string
lower(std::string s) {
48 std::transform(s.begin(), s.end(), s.begin(), [](
unsigned char c) {
49 return static_cast<char>(std::tolower(c));
55 auto is_ws = [](
unsigned char c) {
return std::isspace(c) != 0; };
56 while (!s.empty() && is_ws(
static_cast<unsigned char>(s.front()))) s.erase(s.begin());
57 while (!s.empty() && is_ws(
static_cast<unsigned char>(s.back()))) s.pop_back();
65 if (s.size() >= 2 && s.front() ==
'[' && s.back() ==
']') {
66 s = s.substr(1, s.size() - 2);
70 if (
const auto percent = s.find(
'%'); percent != std::string::npos) {
71 s = s.substr(0, percent);
76 const auto colon = s.rfind(
':');
77 if (colon != std::string::npos && s.find(
':') == colon) {
78 const auto after = s.substr(colon + 1);
79 if (!after.empty() && std::all_of(after.begin(), after.end(), [](
unsigned char c) {
80 return std::isdigit(c) != 0;
82 s = s.substr(0, colon);
93 || d ==
"processo sconosciuto"
95 || d ==
"unknown process";
99 const std::string& app_path,
100 const std::vector<std::string>& app_args) {
112 if (s.empty())
return {};
114 return fs::path(s).filename().string();
116 const auto pos = s.find_last_of(
'/');
117 return pos == std::string::npos ? s : s.substr(pos + 1);
122 if (s.empty())
return s;
123 if ((s.front() ==
'"' && s.back() ==
'"') ||
124 (s.front() ==
'\'' && s.back() ==
'\'')) {
125 s = s.substr(1, s.size() - 2);
127 if (s.rfind(
"--", 0) == 0)
return {};
128 if (!s.empty() && s[0] ==
'%')
return {};
133 std::vector<std::string> out;
137 for (
char ch : exec) {
138 if (esc) { cur.push_back(ch); esc =
false;
continue; }
139 if (ch ==
'\\') { esc =
true;
continue; }
141 if (ch == quote) quote = 0;
142 else cur.push_back(ch);
145 if (ch ==
'\'' || ch ==
'"') { quote = ch;
continue; }
146 if (std::isspace(
static_cast<unsigned char>(ch))) {
147 if (!cur.empty()) { out.push_back(cur); cur.clear(); }
152 if (!cur.empty()) out.push_back(cur);
158 if (s.empty())
return {};
159 return lower(std::move(s));
164void add_key(std::unordered_map<std::string, AppMetadata>& map,
165 const std::string& raw,
168 if (k.empty())
return;
169 map.emplace(k, meta);
171 if (!base.empty()) map.emplace(base, meta);
175 const auto eq = t.find(
'=');
176 return eq != std::string::npos && eq > 0 && t.find(
'/') == std::string::npos;
180 const std::string& commandline,
183 if (tokens.empty())
return;
185 for (std::size_t i = 0; i < tokens.size(); ++i) {
187 if (t.empty())
continue;
188 if (t ==
"env")
continue;
190 if (t ==
"sh" || t ==
"/bin/sh" || t ==
"bash" || t ==
"/bin/bash")
continue;
191 if (t ==
"-c")
continue;
192 if (t ==
"flatpak") {
193 for (std::size_t j = i + 1; j < tokens.size(); ++j) {
194 if (tokens[j] ==
"run" || tokens[j].rfind(
"--", 0) == 0)
continue;
196 const auto dot = tokens[j].find_last_of(
'.');
197 if (dot != std::string::npos)
add_key(map, tokens[j].substr(dot + 1), meta);
207 const auto s =
lower(comm);
208 return s.find(
"thread") != std::string::npos ||
209 s.find(
"web content") != std::string::npos ||
210 s.find(
"socket") != std::string::npos ||
211 s.find(
"renderer") != std::string::npos ||
212 s.find(
"utility") != std::string::npos ||
213 s.find(
"chrome_") != std::string::npos ||
214 s.find(
"isolated web") != std::string::npos ||
215 s ==
"gpu process" ||
216 s ==
"rdd process" ||
217 s ==
"privileged cont";
222 auto icon = ai->get_icon();
223 if (!icon)
return {};
224 auto themed = std::dynamic_pointer_cast<Gio::ThemedIcon>(icon);
225 if (!themed)
return {};
226 const auto names = themed->get_names();
227 for (
const auto& n : names) {
228 if (!n.empty())
return n.raw();
237 auto apps = Gio::AppInfo::get_all();
238 for (
const auto& ai : apps) {
248 auto dai = std::dynamic_pointer_cast<Gio::DesktopAppInfo>(ai);
251 const std::string cmd = dai->get_commandline();
254 const std::string wm = dai->get_startup_wm_class();
261 if (cleaned.empty())
return;
262 out.push_back(cleaned);
264 if (!base.empty() && base != cleaned) out.push_back(base);
268 const auto s =
lower(raw);
269 if (s.find(
"firefox") != std::string::npos || s.find(
"mozilla") != std::string::npos) {
270 return {
"firefox",
"firefox-esr",
"org.mozilla.firefox"};
272 if (s.find(
"chromium") != std::string::npos) {
273 return {
"chromium",
"chromium-browser",
"org.chromium.chromium"};
275 if (s.find(
"google-chrome") != std::string::npos || s ==
"chrome" || s.find(
"chrome_") != std::string::npos) {
276 return {
"google-chrome",
"google-chrome-stable",
"chrome",
"chromium",
"chromium-browser"};
278 if (s.find(
"brave") != std::string::npos) {
279 return {
"brave-browser",
"brave",
"com.brave.browser"};
281 if (s.find(
"vivaldi") != std::string::npos) {
282 return {
"vivaldi",
"vivaldi-stable",
"com.vivaldi.vivaldi"};
284 if (s.find(
"librewolf") != std::string::npos) {
285 return {
"librewolf",
"io.gitlab.librewolf-community"};
287 if (s.find(
"waterfox") != std::string::npos) {
290 if (s.find(
"opera") != std::string::npos) {
291 return {
"opera",
"opera-browser",
"com.opera.Opera"};
293 if (s.find(
"msedge") != std::string::npos || s.find(
"edge") != std::string::npos) {
294 return {
"microsoft-edge",
"microsoft-edge-stable",
"msedge"};
300 for (
const auto& c : candidates) {
302 if (k.empty())
continue;
311std::optional<AppMetadata>
lookup_prefix(
const std::vector<std::string>& candidates) {
312 for (
const auto& c : candidates) {
313 const auto needle =
key_for(c);
314 if (needle.size() < 4)
continue;
316 if (key.size() < 4)
continue;
317 if ((key.size() > needle.size() && key.compare(0, needle.size(), needle) == 0) ||
318 (needle.size() > key.size() && needle.compare(0, key.size(), key) == 0)) {
327 const std::string& app_path,
328 const std::vector<std::string>& app_args) {
332 }
else if (!app_path.empty()) {
334 }
else if (!app_args.empty() && !app_args.front().empty()) {
336 }
else if (!app_comm.empty()) {
342 std::string icon_guess =
basename_of(app_path.empty() ? app_comm : app_path);
343 icon_guess =
key_for(icon_guess);
352 const std::string& app_path,
353 const std::vector<std::string>& app_args) {
354 std::lock_guard lock(g_index_mutex);
355 build_index_locked();
357 std::vector<std::string> candidates;
358 add_candidate(candidates, app_path);
359 if (!app_args.empty()) add_candidate(candidates, app_args.front());
360 add_candidate(candidates, app_comm);
362 if (
auto found = lookup_exact_or_alias(candidates))
return *found;
363 if (
auto found = lookup_prefix(candidates))
return *found;
364 return fallback_metadata(app_comm, app_path, app_args);
368 const std::string& app_path,
369 const std::vector<std::string>& app_args) {
374 const std::string& app_path,
375 const std::vector<std::string>& app_args) {
377 return icon.empty() ? kGenericIcon : icon;
381 const auto s = normalized_address(address);
382 if (s.empty())
return false;
384 if (s ==
"localhost" || s ==
"localhost.localdomain" || s ==
"ip6-localhost")
return true;
385 if (s ==
"::1" || s ==
"0:0:0:0:0:0:0:1")
return true;
388 if (s ==
"0.0.0.0" || s ==
"::" || s ==
"0:0:0:0:0:0:0:0")
return true;
391 if (s.rfind(
"127.", 0) == 0)
return true;
392 if (s.rfind(
"::ffff:127.", 0) == 0)
return true;
393 if (s.rfind(
"0:0:0:0:0:ffff:127.", 0) == 0)
return true;
403 return kSafeLocalProcessLabel;
407 return kSafeLocalProcessIcon;
411 const std::string& app_path,
412 const std::vector<std::string>& app_args,
413 const std::string& dst_ip,
414 const std::string& src_ip) {
416 if (is_unknown_display_name(display)
417 && !has_reliable_process_identity(app_comm, app_path, app_args)
419 return kSafeLocalProcessLabel;
425 const std::string& app_path,
426 const std::vector<std::string>& app_args,
427 const std::string& dst_ip,
428 const std::string& src_ip) {
429 if (!has_reliable_process_identity(app_comm, app_path, app_args)
431 return kSafeLocalProcessIcon;
void add_exec_aliases(std::unordered_map< std::string, AppMetadata > &map, const std::string &commandline, const AppMetadata &meta)
std::string lower(std::string s)
std::string trim_copy(std::string s)
void add_candidate(std::vector< std::string > &out, const std::string &s)
std::string normalized_address(std::string s)
bool token_is_env_assignment(const std::string &t)
bool is_unknown_identity_value(const std::string &value)
constexpr const char * kUnknownProcessLabel
std::optional< AppMetadata > lookup_prefix(const std::vector< std::string > &candidates)
bool looks_like_thread_name(const std::string &comm)
constexpr const char * kSafeLocalProcessIcon
bool has_reliable_process_identity(const std::string &app_comm, const std::string &app_path, const std::vector< std::string > &app_args)
std::vector< std::string > browser_aliases_for(const std::string &raw)
bool is_unknown_display_name(const std::string &display_name)
std::string icon_name_from_appinfo(const Glib::RefPtr< Gio::AppInfo > &ai)
constexpr const char * kSafeLocalProcessLabel
void add_key(std::unordered_map< std::string, AppMetadata > &map, const std::string &raw, const AppMetadata &meta)
std::string key_for(std::string s)
std::optional< AppMetadata > lookup_exact_or_alias(const std::vector< std::string > &candidates)
std::string basename_of(std::string s)
AppMetadata fallback_metadata(const std::string &app_comm, const std::string &app_path, const std::vector< std::string > &app_args)
void build_index_locked()
constexpr const char * kGenericIcon
std::string strip_wrappers(std::string s)
std::vector< std::string > split_exec_tokens(const std::string &exec)
std::string safe_local_process_icon_name()
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 safe_local_process_label()
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)
std::string best_app_display_name(const std::string &app_comm, const std::string &app_path, const std::vector< std::string > &app_args)
bool is_safe_local_connection(const std::string &dst_ip, const std::string &src_ip)
AppMetadata resolve_app_metadata(const std::string &app_comm, const std::string &app_path, const std::vector< std::string > &app_args)
std::string best_app_icon_name(const std::string &app_comm, const std::string &app_path, const std::vector< std::string > &app_args)
bool is_safe_local_address(const std::string &address)
std::unordered_map< std::string, AppMetadata > by_key