From e3315c9372447f4e68295eea1ae292f049b0a456 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Wed, 27 Nov 2024 20:43:53 +0100 Subject: [PATCH] Add Linux parsers --- .../peerinfo/application_info/linux.rs | 188 +++++++++++++----- 1 file changed, 137 insertions(+), 51 deletions(-) diff --git a/apps/desktop/desktop_native/core/src/ssh_agent/peerinfo/application_info/linux.rs b/apps/desktop/desktop_native/core/src/ssh_agent/peerinfo/application_info/linux.rs index 771f39fdf1..074fac9651 100644 --- a/apps/desktop/desktop_native/core/src/ssh_agent/peerinfo/application_info/linux.rs +++ b/apps/desktop/desktop_native/core/src/ssh_agent/peerinfo/application_info/linux.rs @@ -3,22 +3,60 @@ use base64::{prelude::BASE64_STANDARD, Engine}; use sysinfo::{Pid, System}; pub fn get_info(pid: usize) -> Result { + println!("[PeerInfo] Getting info for pid: {}", pid); let s = System::new_all(); - let file = std::fs::read_to_string(format!("/proc/{}/cgroup", pid))?; + let scope = std::fs::read_to_string(format!("/proc/{}/cgroup", pid))?; let process_name = s .process(Pid::from(pid)) .ok_or(anyhow::anyhow!("Pid not found"))? .name(); - println!("process name {:?}", process_name); - let scope = file - .split('/') - .last() - .ok_or(anyhow::anyhow!("No scope found"))? - .replace(".scope", ""); - println!("scope {:?}", scope); + println!("[PeerInfo] Checking if is flatpak"); + if let Ok(appinfo) = flatpak_parser(scope.clone()) { + println!("[PeerInfo] Is flatpak"); + return Ok(appinfo) + } - if scope.starts_with("app-flatpak") { + println!("[PeerInfo] Checking if is snap"); + if let Ok(appinfo) = snap_parser(scope.clone()) { + println!("[PeerInfo] Is snap"); + return Ok(appinfo) + } + + println!("[PeerInfo] Checking if is gnome app"); + if let Ok(appinfo) = gnome_app_parser(scope) { + println!("[PeerInfo] Is gnome app"); + return Ok(appinfo) + } + + println!("[PeerInfo] Is not a recognized app"); + return Ok(ApplicationInfo { + name: process_name + .to_str() + .ok_or(anyhow::anyhow!("Could not make path into string"))? + .to_string(), + path: None, + icon: None, + is_installed_app: false, + }); +} + +fn gnome_app_parser(scope: String) -> Result { + let part = scope.split("/").find(|s| s.starts_with("app-gnome")).ok_or(anyhow::anyhow!("No gnome app found"))?; + let app_id = part.split('-').nth(2).ok_or(anyhow::anyhow!("No app id found"))?; + let desktop_file = parse_desktop_file(format!("/usr/share/applications/{}.desktop", app_id).as_str())?; + let icon = find_unsandboxed_icon(&desktop_file.icon_name); + return Ok(ApplicationInfo { + name: desktop_file.name, + path: None, + icon, + is_installed_app: true, + }); +} + +fn flatpak_parser(scope: String) -> Result { + let part = scope.split("/").find(|s| s.starts_with("app-flatpak")); + if let Some(scope) = part { let app_id = scope .split('-') .nth(2) @@ -38,28 +76,22 @@ pub fn get_info(pid: usize) -> Result { "/var/lib/flatpak/app/{}/current/active/export/share/icons/hicolor/", app_id ); - let scalable = try_read_icon(&format!("{}/scalable/apps/{}.svg", path, app_id)); - if scalable.is_some() { - let base64 = BASE64_STANDARD.encode(scalable.unwrap()); - + let scalable = read_icon(&format!("{}/scalable/apps/{}.svg", path, app_id)); + if let Some(icon) = scalable { return Ok(ApplicationInfo { name: name.to_string(), path: Some(path), - icon: Some(format!("data:image/svg+xml;base64,{}", base64)), + icon: Some(icon), is_installed_app: true, }); } else { for size in vec![512, 256, 128, 64] { - let icon = - try_read_icon(&format!("{}/{}x{}/apps/{}.png", path, size, size, app_id)); - if icon.is_some() { + let icon = read_icon(&format!("{}/{}x{}/apps/{}.png", path, size, size, app_id)); + if let Some(icon) = icon { return Ok(ApplicationInfo { name: name.to_string(), path: Some(path), - icon: Some(format!( - "data:image/png;base64,{}", - BASE64_STANDARD.encode(icon.unwrap()) - )), + icon: Some(icon), is_installed_app: true, }); } @@ -67,49 +99,103 @@ pub fn get_info(pid: usize) -> Result { } } - if scope.starts_with("snap.") { + return Err(anyhow::anyhow!("Not a flatpak app")); +} + +fn snap_parser(scope: String) -> Result { + let part = scope.split("/").find(|s| s.starts_with("snap.")); + if let Some(scope) = part { let app_id = scope .split('.') .nth(1) .ok_or(anyhow::anyhow!("Failed to parse snap appid"))?; - let desktop_file = std::fs::read_to_string(format!( - "/var/lib/snapd/desktop/applications/{}_{}.desktop", - app_id, app_id - ))?; - let name = desktop_file - .lines() - .find(|line| line.starts_with("Name=")) - .ok_or(anyhow::anyhow!( - "Name of application in desktop file not found" - ))? - .split('=') - .last() - .ok_or(anyhow::anyhow!( - "Failed to parse desktop file application name" - ))?; + + let desktop_file = parse_desktop_file(format!("/var/lib/snapd/desktop/applications/{}_{}.desktop", app_id, app_id).as_str())?; + let name = desktop_file.name; + let icon = read_icon(&desktop_file.icon_name); + let icon = if let Some(icon) = icon { + Some(icon) + } else { + None + }; return Ok(ApplicationInfo { name: name.to_string(), path: None, - icon: None, + icon, is_installed_app: true, }); } - return Ok(ApplicationInfo { - name: process_name - .to_str() - .ok_or(anyhow::anyhow!("Could not make path into string"))? - .to_string(), - path: None, - icon: None, - is_installed_app: false, - }); + return Err(anyhow::anyhow!("Not a snap app")); } -fn try_read_icon(path: &str) -> Option> { - let icon = std::fs::read(path); - if icon.is_ok() { - return Some(icon.unwrap()); + +fn find_unsandboxed_icon(app_name: &str) -> Option { + let scalable = read_icon(&format!("/usr/share/icons/hicolor/scalable/apps/{}.svg", app_name)); + if let Some(scalable) = scalable { + println!("[PeerInfo] Found scalable icon: {}", scalable); + return Some(scalable); + } + + let paths = vec![ + "/usr/share/icons/hicolor/512x512/apps", + "/usr/share/icons/hicolor/256x256/apps", + "/usr/share/icons/hicolor/128x128/apps", + "/usr/share/icons/hicolor/64x64/apps", + "/usr/share/pixmaps", + ]; + for path in paths { + println!("[PeerInfo] Chceking path for icon: {}", path); + if let Some(icon) = read_icon(&format!("{}/{}.png", path, app_name)) { + println!("[PeerInfo] Found icon: {}", icon); + return Some(icon); + } + } + println!("[PeerInfo] Icon not found"); + None +} + +fn read_icon(path: &str) -> Option { + // ends with .svg + if path.ends_with(".svg") { + let icon = std::fs::read(path); + if icon.is_ok() { + return Some("data:image/svg+xml;base64,".to_string() + &BASE64_STANDARD.encode(icon.unwrap())); + } + } else if path.ends_with(".png") { + let icon = std::fs::read(path); + if icon.is_ok() { + return Some("data:image/png;base64,".to_string() + &BASE64_STANDARD.encode(icon.unwrap())); + } } None } + +#[derive(Debug)] +struct DesktopFile { + name: String, + icon_name: String, +} + +fn parse_desktop_file(path: &str) -> Result { + let desktop_file = std::fs::read_to_string(path).unwrap(); + let name = desktop_file + .lines() + .find(|line| line.starts_with("Name=")) + .ok_or(anyhow::anyhow!("Name not found"))? + .split('=') + .nth(1) + .ok_or(anyhow::anyhow!("Name not found"))?; + let icon = desktop_file + .lines() + .find(|line| line.starts_with("Icon=")) + .ok_or(anyhow::anyhow!("Icon not found"))? + .split('=') + .nth(1) + .ok_or(anyhow::anyhow!("Icon not found"))?; + + return Ok(DesktopFile { + name: name.to_string(), + icon_name: icon.to_string(), + }); +}