use std::fs;
use std::path::{Path, PathBuf};
use lnk::ShellLink;
use serde::{Deserialize, Serialize};
use winreg::enums::*;
use winreg::reg_key::RegKey;
use winreg::HKEY;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct App {
pub name: String,
pub root: String,
pub bin: String,
pub icon: String,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AppList {
pub installed_apps: Vec<App>,
}
impl Default for AppList {
fn default() -> Self {
Self {
installed_apps: Vec::new(),
}
}
}
impl AppList {
pub fn new(hive: HKEY, path: &str) -> AppList {
let hklm = RegKey::predef(hive);
let software_key = hklm.open_subkey_with_flags(path, KEY_READ).unwrap();
let app_paths = AppList::get_apps_path(&hklm, path);
let subkeys: Vec<String> = software_key.enum_keys().map(|x| x.unwrap()).collect();
let mut installed_apps: Vec<App> = Vec::new();
for key in subkeys {
let app_key = software_key.open_subkey(&key).unwrap();
let name: String = app_key
.get_value("DisplayName")
.unwrap_or_else(|_| String::from(""));
let install_location: String = app_key
.get_value("InstallLocation")
.unwrap_or_else(|_| String::from(""));
let icon: String = app_key
.get_value("DisplayIcon")
.unwrap_or_else(|_| String::from(""));
if name.is_empty() || install_location.is_empty() {
continue;
}
let mut bin = String::from("");
for path in &app_paths {
let tmp_path = path.display().to_string();
if tmp_path.contains(&install_location) {
bin = tmp_path;
break;
}
}
installed_apps.push(App {
name,
root: install_location,
bin,
icon: if icon.ends_with(".ico") {
icon
} else {
String::from("")
},
});
}
AppList { installed_apps }
}
fn get_apps_path(hklm: &RegKey, path: &str) -> Vec<PathBuf> {
let mut app_paths: Vec<PathBuf> = Vec::new();
match hklm.open_subkey_with_flags(path.replace("Uninstall", "App Paths"), KEY_READ) {
Ok(app_paths_key) => {
let subkeys: Vec<String> = app_paths_key.enum_keys().map(|x| x.unwrap()).collect();
for key in subkeys {
if let Ok(app_key) = app_paths_key.open_subkey(&key) {
if let Ok(path) = app_key.get_raw_value("") {
app_paths.push(PathBuf::from(path.to_string()));
}
}
}
}
Err(_) => {
}
}
let startmenu_app_bins = AppList::get_apps_path_by_startmenu();
return app_paths.into_iter().chain(startmenu_app_bins).collect();
}
fn resolve_lnk_target(lnk_path: &Path) -> Option<PathBuf> {
if lnk_path
.display()
.to_string()
.contains("Windows PowerShell")
{
return None;
}
match ShellLink::open(lnk_path) {
Ok(shell_link) => match shell_link.link_info() {
Some(link_info) => {
return link_info.local_base_path().clone().map(PathBuf::from);
}
_ => {
return None;
}
},
Err(_) => return None,
};
}
fn traverse_dir(dir_path: &Path, app_paths: &mut Vec<PathBuf>) {
if let Ok(entries) = fs::read_dir(dir_path) {
for entry in entries {
if let Ok(entry) = entry {
let file_path = entry.path();
if file_path.is_dir() {
AppList::traverse_dir(&file_path, app_paths);
} else {
if let Some(extension) = file_path.extension() {
if extension == "lnk" {
match AppList::resolve_lnk_target(&file_path) {
Some(tmp) => {
app_paths.push(tmp);
}
None => {}
}
}
}
}
}
}
} else {
let entry = fs::read_link(dir_path).expect("Failed to read link");
let file_path = entry.as_path();
if let Some(extension) = file_path.extension() {
if extension == "lnk" {
match AppList::resolve_lnk_target(&file_path) {
Some(tmp) => {
app_paths.push(tmp);
}
None => {}
}
}
}
}
}
fn get_apps_path_by_startmenu() -> Vec<PathBuf> {
let appdata = std::env::var("APPDATA").expect("Failed to get APPDATA environment variable");
let start_menu_path = Path::new(&appdata).join("Microsoft\\Windows\\Start Menu\\Programs");
let mut app_paths: Vec<PathBuf> = Vec::new();
AppList::traverse_dir(&start_menu_path, &mut app_paths);
return app_paths;
}
}
impl App {
pub fn name(&self) -> &str {
&self.name
}
pub fn root(&self) -> &str {
&self.root
}
pub fn bin(&self) -> &str {
&self.bin
}
pub fn icon(&self) -> &str {
&self.icon
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Installed {
pub apps: Vec<App>,
pub apps_sys: Vec<App>,
pub apps_user: Vec<App>,
}
impl Default for Installed {
fn default() -> Self {
Self {
apps: Vec::new(),
apps_sys: Vec::new(),
apps_user: Vec::new(),
}
}
}
impl Installed {
pub fn new() -> Self {
let system_apps = AppList::new(
HKEY_LOCAL_MACHINE,
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall",
);
let system_apps_32 = AppList::new(
HKEY_LOCAL_MACHINE,
"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall",
);
let user_apps = AppList::new(
HKEY_CURRENT_USER,
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall",
);
let user_apps_32 = AppList::new(
HKEY_CURRENT_USER,
"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall",
);
let apps_sys: Vec<App> = Vec::new()
.into_iter()
.chain(system_apps.installed_apps)
.chain(system_apps_32.installed_apps)
.collect();
let apps_user: Vec<App> = Vec::new()
.into_iter()
.chain(user_apps.installed_apps)
.chain(user_apps_32.installed_apps)
.collect();
let apps: Vec<App> = Vec::new()
.into_iter()
.chain(apps_sys.clone())
.chain(apps_user.clone())
.collect();
Installed {
apps,
apps_sys,
apps_user,
}
}
}