e-app 0.2.8

MII - Machine Internal Inspection
Documentation
use e_utils::system::cmd;
use std::ptr;
use std::ptr::null_mut;
use winapi::shared::minwindef::{DWORD, HKEY};
use winapi::shared::winerror::{ERROR_NO_MORE_ITEMS, ERROR_SUCCESS, S_OK};
use winapi::um::shellapi::{
  SHEmptyRecycleBinW, SHERB_NOCONFIRMATION, SHERB_NOPROGRESSUI, SHERB_NOSOUND,
};
use winapi::um::winnt::{KEY_ENUMERATE_SUB_KEYS, KEY_SET_VALUE};
use winapi::um::winreg::{
  RegCloseKey, RegDeleteKeyW, RegDeleteValueW, RegEnumValueW, RegOpenKeyExW, HKEY_CURRENT_USER,
  LSTATUS,
};

use crate::share::{default_cmd_res, get_powershell_path, CmdRes, SYSTEM_WIN32};
use std::env;
use std::ffi::OsString;
use std::fs;
use std::os::windows::ffi::OsStrExt;
use std::path::PathBuf;

fn add_log(message: &str, level: &str) {
  println!("[{}] {}", level, message);
}

/// 清理回收站
pub fn empty_recycle_bin() -> CmdRes {
  let mut outres = default_cmd_res();
  // 调用SHEmptyRecycleBinW函数来清理回收站
  let result = unsafe {
    SHEmptyRecycleBinW(
      ptr::null_mut(),
      ptr::null(),
      SHERB_NOCONFIRMATION | SHERB_NOPROGRESSUI | SHERB_NOSOUND,
    )
  };
  if result == S_OK || result == 0x8000ffffu32 as i32 {
    outres.status = true;
    outres.content = "Recycle Bin has been successfully cleared!".to_string();
  } else {
    outres.status = false;
    outres.content = format!("Error while clearing the Recycle Bin:{:x}", result);
  }
  outres
}

/// 清理最近访问
pub fn empty_access_log() -> CmdRes {
  let mut outres = default_cmd_res();
  match env::var("APPDATA") {
    Ok(appdata) => {
      let recent_folder = PathBuf::from(&appdata).join("Microsoft\\Windows\\Recent");
      let automatic_destinations = recent_folder.join("AutomaticDestinations");
      let custom_destinations = recent_folder.join("CustomDestinations");

      // Remove files in the Recent folder
      if let Ok(entries) = fs::read_dir(&recent_folder) {
        for entry in entries {
          if let Ok(entry) = entry {
            if let Ok(metadata) = entry.metadata() {
              if metadata.is_file() {
                let _ = fs::remove_file(entry.path());
              }
            }
          }
        }
      }

      // Remove the AutomaticDestinations folder
      if automatic_destinations.exists() {
        let _ = fs::remove_dir_all(&automatic_destinations);
      }

      // Remove the CustomDestinations folder
      if custom_destinations.exists() {
        let _ = fs::remove_dir_all(&custom_destinations);
      }

      add_log("Quick Access history cleared successfully.", "info");

      let keys = [
        "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced\\Start_TrackProgs",
        "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced\\Start_TrackDocs",
      ];

      for key_path in &keys {
        let key_path: Vec<u16> = key_path.encode_utf16().chain(Some(0)).collect();
        let mut key: HKEY = null_mut();

        let result: LSTATUS = unsafe {
          RegOpenKeyExW(
            winapi::um::winreg::HKEY_CURRENT_USER,
            key_path.as_ptr(),
            0,
            KEY_SET_VALUE | KEY_ENUMERATE_SUB_KEYS,
            &mut key,
          )
        };

        if result == ERROR_SUCCESS as LSTATUS {
          let delete_result: LSTATUS = unsafe {
            RegDeleteKeyW(
              key,
              "Recent\0".encode_utf16().collect::<Vec<u16>>().as_ptr(),
            )
          };

          if delete_result == ERROR_SUCCESS as LSTATUS {
            add_log(
              &format!(
                "Quick Access history in {} cleared successfully.",
                String::from_utf16_lossy(&key_path)
              ),
              "info",
            );
          } else {
            add_log(
              &format!(
                "Error clearing Quick Access history in {}: {}",
                String::from_utf16_lossy(&key_path),
                delete_result
              ),
              "info",
            );
          }

          unsafe {
            RegCloseKey(key);
          }
        } else {
          add_log(
            &format!(
              "Error opening registry key {}: {}",
              String::from_utf16_lossy(&key_path),
              result
            ),
            "info",
          );
        }
      }
      outres.content = "PASS".to_string();
      outres.status = true
    }
    Err(e) => outres.content = format!("Error getting APPDATA environment variable: {}", e),
  }
  outres
}

/// 清空活动访问历史
pub fn empty_activity_history() -> CmdRes {
  let mut outres = default_cmd_res();
  match cmd(
    "powershell.exe",
    [
      "-Command",
      "Get-History | ForEach-Object { Remove-History $_.Id }",
    ],
    Some(std::path::Path::new(&get_powershell_path()).to_path_buf()),
    false,
    false,
  ) {
    Ok(_x) => {
      outres.content = format!("Windows search history cleared successfully");
      outres.status = true
    }
    Err(e) => {
      outres.content = format!("Error clearing Windows search history: {}", e);
      outres.status = false
    }
  }
  outres
}

/// Convert a Rust string to a null-terminated UTF-16 encoded string suitable for use with WinAPI.
fn to_utf16_null_terminated(s: &str) -> Vec<u16> {
  OsString::from(s).encode_wide().chain(Some(0)).collect()
}

/// 清空运行记录
pub fn empty_run_history() -> CmdRes {
  let mut outres = default_cmd_res();
  let path = to_utf16_null_terminated(r"Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU");

  // Open the registry key
  let mut hkey: winapi::shared::minwindef::HKEY = null_mut();
  let result = unsafe {
    RegOpenKeyExW(
      HKEY_CURRENT_USER,
      path.as_ptr(),
      0,
      KEY_SET_VALUE | KEY_ENUMERATE_SUB_KEYS,
      &mut hkey,
    )
  };

  if result != ERROR_SUCCESS as i32 {
    outres.content = format!("Failed to open registry key: {}", result);
    return outres;
  }

  // Enumerate and delete each value in the subkey
  let mut index: DWORD = 0;
  let mut value_name: [u16; 16383] = [0; 16383]; // Buffer size for value name
  loop {
    let mut value_name_size: DWORD = value_name.len() as DWORD;
    let mut value_type: DWORD = 0;
    let mut value_data: [u8; 1024] = [0; 1024]; // Buffer size for value data
    let mut value_data_size: DWORD = value_data.len() as DWORD;

    let enum_result = unsafe {
      RegEnumValueW(
        hkey,
        index,
        value_name.as_mut_ptr(),
        &mut value_name_size,
        null_mut(),
        &mut value_type,
        value_data.as_mut_ptr(),
        &mut value_data_size,
      )
    };

    if enum_result == ERROR_NO_MORE_ITEMS as i32 {
      break; // No more items to enumerate
    } else if enum_result != ERROR_SUCCESS as i32 {
      outres.content = format!("Failed to enumerate registry values: {}", enum_result);
      unsafe { RegCloseKey(hkey) };
      return outres;
    }

    // Delete the value
    let delete_result = unsafe { RegDeleteValueW(hkey, value_name.as_ptr()) };

    if delete_result != ERROR_SUCCESS as i32 {
      outres.content = format!("Failed to delete registry value: {}", delete_result);
      unsafe { RegCloseKey(hkey) };
      return outres;
    }

    index += 1;
  }

  unsafe { RegCloseKey(hkey) };

  outres.content = "运行历史记录已清理成功!".to_string();
  outres.status = true;
  outres
}

/// 清空网络共享
pub fn empty_netshare(name: &str) -> CmdRes {
  let mut outres = default_cmd_res();
  let mut name = name;
  if name == "" {
    name = "*";
  }
  match cmd(
    "net",
    ["use", name, "/DELETE", "/YES"],
    Some(std::path::Path::new(SYSTEM_WIN32).to_path_buf()),
    false,
    true,
  ) {
    Ok(x) => {
      outres.status = !x.stdout.contains("NET HELPMSG");
      outres.content = x.stdout;
    }
    Err(e) => {
      outres.content = format!("清空网络共享失败: {e}");
    }
  }
  outres.content = "清空网络共享成功!".to_string();
  outres.status = true;
  outres
}