mirror of
https://gitlab.com/cleanflash/installer.git
synced 2026-08-12 18:31:37 +08:00
Install native host
This commit is contained in:
@@ -21,6 +21,7 @@ windows-sys = { version = "0.61", features = [
|
||||
"Win32_System_Threading",
|
||||
"Win32_System_ProcessStatus",
|
||||
"Win32_Storage_FileSystem",
|
||||
"Win32_System_Registry",
|
||||
"Win32_System_SystemInformation",
|
||||
"Win32_UI_HiDpi",
|
||||
"Win32_UI_Shell",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod file_util;
|
||||
pub mod native_host;
|
||||
pub mod process_utils;
|
||||
pub mod redirection;
|
||||
pub mod registry;
|
||||
|
||||
@@ -0,0 +1,444 @@
|
||||
//! Install and uninstall the native messaging host for modern browsers.
|
||||
//!
|
||||
//! The native messaging host allows the Clean Flash Player browser extension
|
||||
//! to communicate with the local flash-player-host binary via stdio.
|
||||
|
||||
use crate::{InstallError, ProgressCallback};
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
const MANIFEST_NAME: &str = "org.cleanflash.flash_player";
|
||||
const FIREFOX_MANIFEST_FILENAME: &str = "org.cleanflash.flash_player.firefox.json";
|
||||
const CHROME_MANIFEST_FILENAME: &str = "org.cleanflash.flash_player.chrome.json";
|
||||
const FIREFOX_ALLOWED_EXTENSION: &str = "flash-player@cleanflash.org";
|
||||
const ALLOWED_ORIGIN: &str = "chrome-extension://dcikaadaeajidejkoekdflmfdgeoldcb/";
|
||||
|
||||
#[cfg(windows)]
|
||||
const HOST_BINARY_NAME: &str = "flash-player-host.exe";
|
||||
|
||||
#[cfg(not(windows))]
|
||||
const HOST_BINARY_NAME: &str = "flash-player-host";
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum BrowserKind {
|
||||
Firefox,
|
||||
ChromeLike,
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
struct WindowsBrowser {
|
||||
detect_keys: &'static [&'static str],
|
||||
native_messaging_reg_path: &'static str,
|
||||
kind: BrowserKind,
|
||||
}
|
||||
|
||||
/// Browser registry key paths on Windows (HKCU/HKLM) for native messaging hosts.
|
||||
#[cfg(windows)]
|
||||
const WINDOWS_BROWSERS: &[WindowsBrowser] = &[
|
||||
WindowsBrowser {
|
||||
detect_keys: &[r"SOFTWARE\Google\Chrome"],
|
||||
native_messaging_reg_path: r"SOFTWARE\Google\Chrome\NativeMessagingHosts",
|
||||
kind: BrowserKind::ChromeLike,
|
||||
},
|
||||
WindowsBrowser {
|
||||
detect_keys: &[r"SOFTWARE\Microsoft\Edge"],
|
||||
native_messaging_reg_path: r"SOFTWARE\Microsoft\Edge\NativeMessagingHosts",
|
||||
kind: BrowserKind::ChromeLike,
|
||||
},
|
||||
WindowsBrowser {
|
||||
detect_keys: &[r"SOFTWARE\BraveSoftware\Brave-Browser"],
|
||||
native_messaging_reg_path: r"SOFTWARE\BraveSoftware\Brave-Browser\NativeMessagingHosts",
|
||||
kind: BrowserKind::ChromeLike,
|
||||
},
|
||||
WindowsBrowser {
|
||||
detect_keys: &[r"SOFTWARE\Vivaldi"],
|
||||
native_messaging_reg_path: r"SOFTWARE\Vivaldi\NativeMessagingHosts",
|
||||
kind: BrowserKind::ChromeLike,
|
||||
},
|
||||
WindowsBrowser {
|
||||
detect_keys: &[r"SOFTWARE\Opera Software"],
|
||||
native_messaging_reg_path: r"SOFTWARE\Opera Software\NativeMessagingHosts",
|
||||
kind: BrowserKind::ChromeLike,
|
||||
},
|
||||
WindowsBrowser {
|
||||
detect_keys: &[r"SOFTWARE\Chromium"],
|
||||
native_messaging_reg_path: r"SOFTWARE\Chromium\NativeMessagingHosts",
|
||||
kind: BrowserKind::ChromeLike,
|
||||
},
|
||||
WindowsBrowser {
|
||||
detect_keys: &[r"SOFTWARE\The Browser Company\Arc"],
|
||||
native_messaging_reg_path: r"SOFTWARE\The Browser Company\Arc\NativeMessagingHosts",
|
||||
kind: BrowserKind::ChromeLike,
|
||||
},
|
||||
WindowsBrowser {
|
||||
detect_keys: &[r"SOFTWARE\Mozilla\Mozilla Firefox", r"SOFTWARE\Mozilla"],
|
||||
native_messaging_reg_path: r"SOFTWARE\Mozilla\NativeMessagingHosts",
|
||||
kind: BrowserKind::Firefox,
|
||||
},
|
||||
];
|
||||
|
||||
struct BrowserManifestTarget {
|
||||
detect_path: PathBuf,
|
||||
manifest_dir: PathBuf,
|
||||
kind: BrowserKind,
|
||||
}
|
||||
|
||||
/// Build the JSON manifest content for the native messaging host.
|
||||
fn build_manifest_json(host_path: &Path, kind: BrowserKind) -> String {
|
||||
// Escape backslashes in the path for JSON.
|
||||
let path_str = host_path.to_string_lossy().replace('\\', "\\\\");
|
||||
match kind {
|
||||
BrowserKind::Firefox => format!(
|
||||
"{{\n \"name\": \"{}\",\n \"description\": \"Flash Player Native Messaging Host\",\n \"path\": \"{}\",\n \"type\": \"stdio\",\n \"allowed_extensions\": [\"{}\"]\n}}\n",
|
||||
MANIFEST_NAME, path_str, FIREFOX_ALLOWED_EXTENSION,
|
||||
),
|
||||
BrowserKind::ChromeLike => format!(
|
||||
"{{\n \"name\": \"{}\",\n \"description\": \"Flash Player Native Messaging Host\",\n \"path\": \"{}\",\n \"type\": \"stdio\",\n \"allowed_origins\": [\"{}\"]\n}}\n",
|
||||
MANIFEST_NAME, path_str, ALLOWED_ORIGIN,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the path where the native host exe should be installed on Windows.
|
||||
/// On a 64-bit system it goes into the System32\Macromed\Flash folder (the 64-bit one).
|
||||
/// On a 32-bit system it goes into the SysWOW64 (or System32) Macromed\Flash folder.
|
||||
#[cfg(windows)]
|
||||
pub fn get_native_host_install_dir() -> PathBuf {
|
||||
crate::system_info::with_system_info(|si| {
|
||||
if si.is_64bit {
|
||||
si.flash64_path.clone()
|
||||
} else {
|
||||
si.flash32_path.clone()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
pub fn get_native_host_install_dir() -> PathBuf {
|
||||
if cfg!(target_os = "macos") {
|
||||
PathBuf::from("/Library/Application Support/Clean Flash")
|
||||
} else {
|
||||
// Linux
|
||||
if let Ok(home) = std::env::var("HOME") {
|
||||
PathBuf::from(home).join(".cleanflash")
|
||||
} else {
|
||||
PathBuf::from("/tmp/.cleanflash")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the full path to the installed native host executable.
|
||||
pub fn get_native_host_exe_path() -> PathBuf {
|
||||
get_native_host_install_dir().join(HOST_BINARY_NAME)
|
||||
}
|
||||
|
||||
fn manifest_filename_for_kind(kind: BrowserKind) -> &'static str {
|
||||
match kind {
|
||||
BrowserKind::Firefox => FIREFOX_MANIFEST_FILENAME,
|
||||
BrowserKind::ChromeLike => CHROME_MANIFEST_FILENAME,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn get_non_windows_browser_targets(home: &Path) -> Vec<BrowserManifestTarget> {
|
||||
if cfg!(target_os = "macos") {
|
||||
vec![
|
||||
BrowserManifestTarget {
|
||||
detect_path: home.join("Library/Application Support/Google/Chrome"),
|
||||
manifest_dir: home.join("Library/Application Support/Google/Chrome/NativeMessagingHosts"),
|
||||
kind: BrowserKind::ChromeLike,
|
||||
},
|
||||
BrowserManifestTarget {
|
||||
detect_path: home.join("Library/Application Support/Microsoft Edge"),
|
||||
manifest_dir: home.join("Library/Application Support/Microsoft Edge/NativeMessagingHosts"),
|
||||
kind: BrowserKind::ChromeLike,
|
||||
},
|
||||
BrowserManifestTarget {
|
||||
detect_path: home.join("Library/Application Support/BraveSoftware/Brave-Browser"),
|
||||
manifest_dir: home.join("Library/Application Support/BraveSoftware/Brave-Browser/NativeMessagingHosts"),
|
||||
kind: BrowserKind::ChromeLike,
|
||||
},
|
||||
BrowserManifestTarget {
|
||||
detect_path: home.join("Library/Application Support/Vivaldi"),
|
||||
manifest_dir: home.join("Library/Application Support/Vivaldi/NativeMessagingHosts"),
|
||||
kind: BrowserKind::ChromeLike,
|
||||
},
|
||||
BrowserManifestTarget {
|
||||
detect_path: home.join("Library/Application Support/Chromium"),
|
||||
manifest_dir: home.join("Library/Application Support/Chromium/NativeMessagingHosts"),
|
||||
kind: BrowserKind::ChromeLike,
|
||||
},
|
||||
BrowserManifestTarget {
|
||||
detect_path: home.join("Library/Application Support/Arc"),
|
||||
manifest_dir: home.join("Library/Application Support/Arc/NativeMessagingHosts"),
|
||||
kind: BrowserKind::ChromeLike,
|
||||
},
|
||||
BrowserManifestTarget {
|
||||
detect_path: home.join("Library/Application Support/Mozilla"),
|
||||
manifest_dir: home.join("Library/Application Support/Mozilla/NativeMessagingHosts"),
|
||||
kind: BrowserKind::Firefox,
|
||||
},
|
||||
]
|
||||
} else {
|
||||
vec![
|
||||
BrowserManifestTarget {
|
||||
detect_path: home.join(".config/google-chrome"),
|
||||
manifest_dir: home.join(".config/google-chrome/NativeMessagingHosts"),
|
||||
kind: BrowserKind::ChromeLike,
|
||||
},
|
||||
BrowserManifestTarget {
|
||||
detect_path: home.join(".config/microsoft-edge"),
|
||||
manifest_dir: home.join(".config/microsoft-edge/NativeMessagingHosts"),
|
||||
kind: BrowserKind::ChromeLike,
|
||||
},
|
||||
BrowserManifestTarget {
|
||||
detect_path: home.join(".config/BraveSoftware/Brave-Browser"),
|
||||
manifest_dir: home.join(".config/BraveSoftware/Brave-Browser/NativeMessagingHosts"),
|
||||
kind: BrowserKind::ChromeLike,
|
||||
},
|
||||
BrowserManifestTarget {
|
||||
detect_path: home.join(".config/vivaldi"),
|
||||
manifest_dir: home.join(".config/vivaldi/NativeMessagingHosts"),
|
||||
kind: BrowserKind::ChromeLike,
|
||||
},
|
||||
BrowserManifestTarget {
|
||||
detect_path: home.join(".config/chromium"),
|
||||
manifest_dir: home.join(".config/chromium/NativeMessagingHosts"),
|
||||
kind: BrowserKind::ChromeLike,
|
||||
},
|
||||
BrowserManifestTarget {
|
||||
detect_path: home.join(".config/arc"),
|
||||
manifest_dir: home.join(".config/arc/NativeMessagingHosts"),
|
||||
kind: BrowserKind::ChromeLike,
|
||||
},
|
||||
BrowserManifestTarget {
|
||||
detect_path: home.join(".mozilla"),
|
||||
manifest_dir: home.join(".mozilla/native-messaging-hosts"),
|
||||
kind: BrowserKind::Firefox,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn set_windows_manifest_registry_value(nmh_base_key: &str, manifest_path: &str) {
|
||||
use windows_sys::Win32::System::Registry::{
|
||||
RegCloseKey, RegCreateKeyExW, RegSetValueExW, HKEY_CURRENT_USER,
|
||||
KEY_WOW64_64KEY, KEY_WRITE, REG_OPTION_NON_VOLATILE, REG_SZ,
|
||||
};
|
||||
|
||||
let reg_key = format!("{}\\{}", nmh_base_key, MANIFEST_NAME);
|
||||
let key_wide: Vec<u16> = reg_key.encode_utf16().chain(std::iter::once(0)).collect();
|
||||
let val_wide: Vec<u16> = manifest_path
|
||||
.encode_utf16()
|
||||
.chain(std::iter::once(0))
|
||||
.collect();
|
||||
|
||||
unsafe {
|
||||
let mut hkey = std::ptr::null_mut();
|
||||
let mut disposition: u32 = 0;
|
||||
let result = RegCreateKeyExW(
|
||||
HKEY_CURRENT_USER,
|
||||
key_wide.as_ptr(),
|
||||
0,
|
||||
std::ptr::null(),
|
||||
REG_OPTION_NON_VOLATILE,
|
||||
KEY_WRITE | KEY_WOW64_64KEY,
|
||||
std::ptr::null(),
|
||||
&mut hkey,
|
||||
&mut disposition,
|
||||
);
|
||||
if result == 0 {
|
||||
RegSetValueExW(
|
||||
hkey,
|
||||
std::ptr::null(),
|
||||
0,
|
||||
REG_SZ as u32,
|
||||
val_wide.as_ptr() as *const u8,
|
||||
(val_wide.len() * 2) as u32,
|
||||
);
|
||||
RegCloseKey(hkey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn delete_windows_manifest_registry_value(nmh_base_key: &str) {
|
||||
use windows_sys::Win32::System::Registry::{
|
||||
RegCloseKey, RegDeleteTreeW, RegOpenKeyExW, HKEY_CURRENT_USER,
|
||||
KEY_WOW64_64KEY, KEY_WRITE,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let parent_wide: Vec<u16> = nmh_base_key
|
||||
.encode_utf16()
|
||||
.chain(std::iter::once(0))
|
||||
.collect();
|
||||
let name_wide: Vec<u16> = MANIFEST_NAME
|
||||
.encode_utf16()
|
||||
.chain(std::iter::once(0))
|
||||
.collect();
|
||||
|
||||
let mut parent_hkey = std::ptr::null_mut();
|
||||
let result = RegOpenKeyExW(
|
||||
HKEY_CURRENT_USER,
|
||||
parent_wide.as_ptr(),
|
||||
0,
|
||||
KEY_WRITE | KEY_WOW64_64KEY,
|
||||
&mut parent_hkey,
|
||||
);
|
||||
if result == 0 {
|
||||
RegDeleteTreeW(parent_hkey, name_wide.as_ptr());
|
||||
RegCloseKey(parent_hkey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Install the native messaging host manifests for all detected browsers.
|
||||
#[cfg(windows)]
|
||||
pub fn install_native_host(form: &dyn ProgressCallback) -> Result<(), InstallError> {
|
||||
form.update_progress_label("Installing native messaging host for modern browsers...", true);
|
||||
|
||||
let host_exe = get_native_host_exe_path();
|
||||
let chrome_manifest_json = build_manifest_json(&host_exe, BrowserKind::ChromeLike);
|
||||
let firefox_manifest_json = build_manifest_json(&host_exe, BrowserKind::Firefox);
|
||||
|
||||
// Create manifest directory inside the install dir.
|
||||
let manifest_dir = get_native_host_install_dir().join("manifests");
|
||||
let _ = fs::create_dir_all(&manifest_dir);
|
||||
let chrome_manifest_path = manifest_dir.join(CHROME_MANIFEST_FILENAME);
|
||||
fs::write(&chrome_manifest_path, chrome_manifest_json.as_bytes()).map_err(|e| {
|
||||
InstallError::new(format!(
|
||||
"Failed to write Chrome native messaging manifest: {}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
let firefox_manifest_path = manifest_dir.join(FIREFOX_MANIFEST_FILENAME);
|
||||
fs::write(&firefox_manifest_path, firefox_manifest_json.as_bytes()).map_err(|e| {
|
||||
InstallError::new(format!(
|
||||
"Failed to write Firefox native messaging manifest: {}",
|
||||
e
|
||||
))
|
||||
})?;
|
||||
|
||||
let chrome_manifest_path_str = chrome_manifest_path.to_string_lossy().to_string();
|
||||
let firefox_manifest_path_str = firefox_manifest_path.to_string_lossy().to_string();
|
||||
|
||||
// For each browser that is detected via registry, register the manifest.
|
||||
for browser in WINDOWS_BROWSERS {
|
||||
if !browser
|
||||
.detect_keys
|
||||
.iter()
|
||||
.any(|detect_key| windows_registry_key_exists(detect_key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let manifest_path = match browser.kind {
|
||||
BrowserKind::Firefox => &firefox_manifest_path_str,
|
||||
BrowserKind::ChromeLike => &chrome_manifest_path_str,
|
||||
};
|
||||
set_windows_manifest_registry_value(browser.native_messaging_reg_path, manifest_path);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
pub fn install_native_host(form: &dyn ProgressCallback) -> Result<(), InstallError> {
|
||||
form.update_progress_label("Installing native messaging host for modern browsers...", true);
|
||||
|
||||
let host_exe = get_native_host_exe_path();
|
||||
let chrome_manifest_json = build_manifest_json(&host_exe, BrowserKind::ChromeLike);
|
||||
let firefox_manifest_json = build_manifest_json(&host_exe, BrowserKind::Firefox);
|
||||
|
||||
// Determine browser config directories that exist.
|
||||
let home = std::env::var("HOME")
|
||||
.map_err(|_| InstallError::new("HOME environment variable not set"))?;
|
||||
let home = PathBuf::from(home);
|
||||
|
||||
// Check known browser paths and install manifests only for those that exist.
|
||||
for target in get_non_windows_browser_targets(&home) {
|
||||
if !target.detect_path.exists() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let _ = fs::create_dir_all(&target.manifest_dir);
|
||||
let manifest_filename = manifest_filename_for_kind(target.kind);
|
||||
let manifest_json = match target.kind {
|
||||
BrowserKind::Firefox => &firefox_manifest_json,
|
||||
BrowserKind::ChromeLike => &chrome_manifest_json,
|
||||
};
|
||||
let manifest_path = target.manifest_dir.join(manifest_filename);
|
||||
let _ = fs::write(&manifest_path, manifest_json.as_bytes());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Uninstall the native messaging host: remove manifests and registry entries.
|
||||
#[cfg(windows)]
|
||||
pub fn uninstall_native_host(form: &dyn ProgressCallback) {
|
||||
form.update_progress_label("Removing native messaging host...", true);
|
||||
|
||||
// Remove manifest files.
|
||||
let install_dir = get_native_host_install_dir();
|
||||
let manifest_dir = install_dir.join("manifests");
|
||||
let _ = fs::remove_file(manifest_dir.join(CHROME_MANIFEST_FILENAME));
|
||||
let _ = fs::remove_file(manifest_dir.join(FIREFOX_MANIFEST_FILENAME));
|
||||
let _ = fs::remove_dir(&manifest_dir);
|
||||
|
||||
// Remove the host exe.
|
||||
let host_exe = install_dir.join(HOST_BINARY_NAME);
|
||||
crate::file_util::delete_file(&host_exe);
|
||||
|
||||
// Remove registry entries for all browsers.
|
||||
for browser in WINDOWS_BROWSERS {
|
||||
delete_windows_manifest_registry_value(browser.native_messaging_reg_path);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
pub fn uninstall_native_host(form: &dyn ProgressCallback) {
|
||||
form.update_progress_label("Removing native messaging host...", true);
|
||||
|
||||
// Remove the host folder and everything inside it.
|
||||
let install_dir = get_native_host_install_dir();
|
||||
let host_exe = install_dir.join(HOST_BINARY_NAME);
|
||||
let _ = fs::remove_file(&host_exe);
|
||||
let _ = fs::remove_dir_all(&install_dir);
|
||||
|
||||
let home = match std::env::var("HOME") {
|
||||
Ok(h) => PathBuf::from(h),
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
for target in get_non_windows_browser_targets(&home) {
|
||||
let _ = fs::remove_file(target.manifest_dir.join(CHROME_MANIFEST_FILENAME));
|
||||
let _ = fs::remove_file(target.manifest_dir.join(FIREFOX_MANIFEST_FILENAME));
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if a registry key exists under HKCU.
|
||||
#[cfg(windows)]
|
||||
fn windows_registry_key_exists(subkey: &str) -> bool {
|
||||
use windows_sys::Win32::System::Registry::{
|
||||
RegCloseKey, RegOpenKeyExW, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE,
|
||||
KEY_READ, KEY_WOW64_64KEY,
|
||||
};
|
||||
|
||||
let key_wide: Vec<u16> = subkey.encode_utf16().chain(std::iter::once(0)).collect();
|
||||
|
||||
// Check HKCU first, then HKLM.
|
||||
for &root in &[HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE] {
|
||||
unsafe {
|
||||
let mut hkey = std::ptr::null_mut();
|
||||
let result = RegOpenKeyExW(root, key_wide.as_ptr(), 0, KEY_READ | KEY_WOW64_64KEY, &mut hkey);
|
||||
if result == 0 {
|
||||
RegCloseKey(hkey);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
file_util, process_utils, registry, resources, system_info, winapi_helpers, InstallError,
|
||||
file_util, native_host, process_utils, registry, resources, system_info, winapi_helpers, InstallError,
|
||||
ProgressCallback,
|
||||
};
|
||||
use std::env;
|
||||
@@ -21,6 +21,7 @@ const PROCESSES_TO_KILL: &[&str] = &[
|
||||
"flashplayerapp",
|
||||
"flashplayer_sa",
|
||||
"flashplayer_sa_debug",
|
||||
"flash-player-host",
|
||||
];
|
||||
|
||||
const CONDITIONAL_PROCESSES: &[&str] = &[
|
||||
@@ -329,6 +330,8 @@ pub fn uninstall(form: &dyn ProgressCallback) -> Result<(), InstallError> {
|
||||
form.update_progress_label("Removing Flash Player...", true);
|
||||
delete_flash_player();
|
||||
|
||||
native_host::uninstall_native_host(form);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,9 @@ pub const PLAYER_START_MENU: u32 = 1 << 4;
|
||||
pub const PLAYER_DESKTOP: u32 = 1 << 5;
|
||||
pub const X64: u32 = 1 << 6;
|
||||
pub const DEBUG: u32 = 1 << 7;
|
||||
pub const NATIVE_HOST: u32 = 1 << 8;
|
||||
|
||||
const UNINSTALL_TICKS: u32 = 9;
|
||||
const UNINSTALL_TICKS: u32 = 10;
|
||||
const INSTALL_GENERAL_TICKS: u32 = 5;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
@@ -65,6 +66,10 @@ impl InstallFlags {
|
||||
ticks += 1;
|
||||
}
|
||||
|
||||
if self.is_set(NATIVE_HOST) {
|
||||
ticks += 1;
|
||||
}
|
||||
|
||||
ticks += UNINSTALL_TICKS;
|
||||
ticks += INSTALL_GENERAL_TICKS;
|
||||
ticks
|
||||
|
||||
@@ -26,6 +26,10 @@ const COMPLETE_INSTALL_TEXT: &str = "Clean Flash Player has been successfully in
|
||||
Don't forget, Flash Player is no longer compatible with new browsers.\n\n\
|
||||
For browser recommendations and Flash Player updates, check out Clean Flash Player's website!";
|
||||
|
||||
const COMPLETE_INSTALL_WITH_EXTENSION_TEXT: &str = "Clean Flash Player has been successfully installed!\n\n\
|
||||
To use Flash in modern browsers, install the Clean Flash Player extension into your browser.\n\n\
|
||||
For Flash Player updates, check out Clean Flash Player's website!";
|
||||
|
||||
const COMPLETE_UNINSTALL_TEXT: &str = "\nAll versions of Flash Player have been successfully uninstalled.\n\n\
|
||||
If you ever change your mind, check out Clean Flash Player's website!";
|
||||
|
||||
@@ -66,6 +70,8 @@ pub struct InstallForm {
|
||||
pub disclaimer_box: ImageCheckBox,
|
||||
// Choice panel (browser plugins)
|
||||
pub browser_ask_label: Label,
|
||||
pub native_host_box: ImageCheckBox,
|
||||
pub native_host_label: Label,
|
||||
pub pepper_box: ImageCheckBox,
|
||||
pub pepper_label: Label,
|
||||
pub netscape_box: ImageCheckBox,
|
||||
@@ -130,7 +136,7 @@ impl InstallForm {
|
||||
|
||||
let fonts = FontManager::new();
|
||||
|
||||
Self {
|
||||
let mut form = Self {
|
||||
scale,
|
||||
panel: Panel::Disclaimer,
|
||||
title_text,
|
||||
@@ -143,12 +149,14 @@ impl InstallForm {
|
||||
disclaimer_box: chk(PANEL_X, PANEL_Y),
|
||||
// Choice panel
|
||||
browser_ask_label: lbl(PANEL_X - 2, PANEL_Y + 2, "Which browser plugins would you like to install?", 15.0),
|
||||
pepper_box: chk(PANEL_X, PANEL_Y + 47),
|
||||
pepper_label: lbl(PANEL_X + 24, PANEL_Y + 47, "Pepper API (PPAPI)\n(Chrome/Opera/Brave)", 15.0),
|
||||
netscape_box: chk(PANEL_X + 186, PANEL_Y + 47),
|
||||
netscape_label: lbl(PANEL_X + 210, PANEL_Y + 47, "Netscape API (NPAPI)\n(Firefox/ESR/Waterfox)", 15.0),
|
||||
activex_box: chk(PANEL_X + 365, PANEL_Y + 47),
|
||||
activex_label: lbl(PANEL_X + 389, PANEL_Y + 47, "ActiveX (OCX)\n(IE/Embedded/Desktop)", 15.0),
|
||||
native_host_box: chk(PANEL_X, PANEL_Y + 27),
|
||||
native_host_label: lbl(PANEL_X + 24, PANEL_Y + 27, "Modern Browsers (MV3)\n(Chrome/Firefox/Edge)", 15.0),
|
||||
pepper_box: chk(PANEL_X, PANEL_Y + 73),
|
||||
pepper_label: lbl(PANEL_X + 24, PANEL_Y + 73, "Pepper API (PPAPI)\n(Chrome/Opera/Brave)", 15.0),
|
||||
netscape_box: chk(PANEL_X + 186, PANEL_Y + 73),
|
||||
netscape_label: lbl(PANEL_X + 210, PANEL_Y + 73, "Netscape API (NPAPI)\n(Firefox/ESR/Waterfox)", 15.0),
|
||||
activex_box: chk(PANEL_X + 365, PANEL_Y + 73),
|
||||
activex_label: lbl(PANEL_X + 389, PANEL_Y + 73, "ActiveX (OCX)\n(IE/Embedded/Desktop)", 15.0),
|
||||
// Player choice panel
|
||||
player_ask_label: lbl(PANEL_X - 2, PANEL_Y + 2, "Would you like to install the standalone Flash Player?", 15.0),
|
||||
player_box: chk(PANEL_X, PANEL_Y + 47),
|
||||
@@ -194,7 +202,29 @@ The following details could be useful. Press the Retry button to try again.",
|
||||
})),
|
||||
fonts,
|
||||
prev_mouse_down: false,
|
||||
};
|
||||
|
||||
// Modern browser support should be the default choice.
|
||||
form.native_host_box.checked = true;
|
||||
|
||||
// On non-Windows platforms, disable legacy browser plugins and player options.
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
{
|
||||
form.pepper_box.checked = false;
|
||||
form.pepper_box.enabled = false;
|
||||
form.netscape_box.checked = false;
|
||||
form.netscape_box.enabled = false;
|
||||
form.activex_box.checked = false;
|
||||
form.activex_box.enabled = false;
|
||||
form.player_box.checked = false;
|
||||
form.player_box.enabled = false;
|
||||
form.player_desktop_box.checked = false;
|
||||
form.player_desktop_box.enabled = false;
|
||||
form.player_start_menu_box.checked = false;
|
||||
form.player_start_menu_box.enabled = false;
|
||||
}
|
||||
|
||||
form
|
||||
}
|
||||
|
||||
/// Scale a logical integer coordinate to physical pixels.
|
||||
@@ -296,16 +326,30 @@ The following details could be useful. Press the Retry button to try again.",
|
||||
}
|
||||
}
|
||||
Panel::Choice => {
|
||||
self.native_host_box.toggle_if_clicked(mx, my, mouse_released);
|
||||
self.pepper_box.toggle_if_clicked(mx, my, mouse_released);
|
||||
self.netscape_box.toggle_if_clicked(mx, my, mouse_released);
|
||||
self.activex_box.toggle_if_clicked(mx, my, mouse_released);
|
||||
if self.pepper_label.clicked(mx, my, mouse_released, &self.fonts) {
|
||||
if self.native_host_box.enabled
|
||||
&& self
|
||||
.native_host_label
|
||||
.clicked(mx, my, mouse_released, &self.fonts)
|
||||
{
|
||||
self.native_host_box.checked = !self.native_host_box.checked;
|
||||
}
|
||||
if self.pepper_box.enabled
|
||||
&& self.pepper_label.clicked(mx, my, mouse_released, &self.fonts)
|
||||
{
|
||||
self.pepper_box.checked = !self.pepper_box.checked;
|
||||
}
|
||||
if self.netscape_label.clicked(mx, my, mouse_released, &self.fonts) {
|
||||
if self.netscape_box.enabled
|
||||
&& self.netscape_label.clicked(mx, my, mouse_released, &self.fonts)
|
||||
{
|
||||
self.netscape_box.checked = !self.netscape_box.checked;
|
||||
}
|
||||
if self.activex_label.clicked(mx, my, mouse_released, &self.fonts) {
|
||||
if self.activex_box.enabled
|
||||
&& self.activex_label.clicked(mx, my, mouse_released, &self.fonts)
|
||||
{
|
||||
self.activex_box.checked = !self.activex_box.checked;
|
||||
}
|
||||
}
|
||||
@@ -313,13 +357,25 @@ The following details could be useful. Press the Retry button to try again.",
|
||||
self.player_box.toggle_if_clicked(mx, my, mouse_released);
|
||||
self.player_desktop_box.toggle_if_clicked(mx, my, mouse_released);
|
||||
self.player_start_menu_box.toggle_if_clicked(mx, my, mouse_released);
|
||||
if self.player_label.clicked(mx, my, mouse_released, &self.fonts) {
|
||||
if self.player_box.enabled
|
||||
&& self.player_label.clicked(mx, my, mouse_released, &self.fonts)
|
||||
{
|
||||
self.player_box.checked = !self.player_box.checked;
|
||||
}
|
||||
if self.player_desktop_label.clicked(mx, my, mouse_released, &self.fonts) && self.player_box.checked {
|
||||
if self.player_desktop_box.enabled
|
||||
&& self
|
||||
.player_desktop_label
|
||||
.clicked(mx, my, mouse_released, &self.fonts)
|
||||
&& self.player_box.checked
|
||||
{
|
||||
self.player_desktop_box.checked = !self.player_desktop_box.checked;
|
||||
}
|
||||
if self.player_start_menu_label.clicked(mx, my, mouse_released, &self.fonts) && self.player_box.checked {
|
||||
if self.player_start_menu_box.enabled
|
||||
&& self
|
||||
.player_start_menu_label
|
||||
.clicked(mx, my, mouse_released, &self.fonts)
|
||||
&& self.player_box.checked
|
||||
{
|
||||
self.player_start_menu_box.checked = !self.player_start_menu_box.checked;
|
||||
}
|
||||
// Disable sub-options when player unchecked.
|
||||
@@ -359,7 +415,13 @@ The following details could be useful. Press the Retry button to try again.",
|
||||
Panel::Choice => self.open_disclaimer(),
|
||||
Panel::PlayerChoice => self.open_choice(),
|
||||
Panel::DebugChoice => self.open_player_choice(),
|
||||
Panel::BeforeInstall => self.open_debug_choice(),
|
||||
Panel::BeforeInstall => {
|
||||
if cfg!(target_os = "windows") {
|
||||
self.open_debug_choice();
|
||||
} else {
|
||||
self.open_choice();
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -367,7 +429,13 @@ The following details could be useful. Press the Retry button to try again.",
|
||||
fn on_next_clicked(&mut self) {
|
||||
match self.panel {
|
||||
Panel::Disclaimer => self.open_choice(),
|
||||
Panel::Choice => self.open_player_choice(),
|
||||
Panel::Choice => {
|
||||
if cfg!(target_os = "windows") {
|
||||
self.open_player_choice();
|
||||
} else {
|
||||
self.open_before_install();
|
||||
}
|
||||
}
|
||||
Panel::PlayerChoice => self.open_debug_choice(),
|
||||
Panel::DebugChoice => self.open_before_install(),
|
||||
Panel::BeforeInstall | Panel::Failure => self.open_install(),
|
||||
@@ -418,10 +486,13 @@ The following details could be useful. Press the Retry button to try again.",
|
||||
self.prev_button.enabled = true;
|
||||
|
||||
let has_plugins =
|
||||
self.pepper_box.checked || self.netscape_box.checked || self.activex_box.checked || self.player_box.checked;
|
||||
self.native_host_box.checked || self.pepper_box.checked || self.netscape_box.checked || self.activex_box.checked || self.player_box.checked;
|
||||
|
||||
if has_plugins {
|
||||
let mut browsers = Vec::new();
|
||||
if self.native_host_box.checked {
|
||||
browsers.push("Modern Browsers (via extension)");
|
||||
}
|
||||
if self.pepper_box.checked {
|
||||
browsers.push("Google Chrome");
|
||||
}
|
||||
@@ -458,6 +529,7 @@ The installer will completely remove all versions of Flash Player from this comp
|
||||
self.next_button.visible = false;
|
||||
|
||||
let mut flags = InstallFlags::new();
|
||||
flags.set_conditionally(self.native_host_box.checked, install_flags::NATIVE_HOST);
|
||||
flags.set_conditionally(self.pepper_box.checked, install_flags::PEPPER);
|
||||
flags.set_conditionally(self.netscape_box.checked, install_flags::NETSCAPE);
|
||||
flags.set_conditionally(self.activex_box.checked, install_flags::ACTIVEX);
|
||||
@@ -529,7 +601,9 @@ The installer will completely remove all versions of Flash Player from this comp
|
||||
self.prev_button.enabled = true;
|
||||
self.next_button.visible = false;
|
||||
|
||||
if self.pepper_box.checked || self.netscape_box.checked || self.activex_box.checked {
|
||||
if self.native_host_box.checked {
|
||||
self.complete_label.text = COMPLETE_INSTALL_WITH_EXTENSION_TEXT.to_string();
|
||||
} else if self.pepper_box.checked || self.netscape_box.checked || self.activex_box.checked {
|
||||
self.complete_label.text = COMPLETE_INSTALL_TEXT.to_string();
|
||||
} else {
|
||||
self.complete_label.text = COMPLETE_UNINSTALL_TEXT.to_string();
|
||||
@@ -553,6 +627,8 @@ The installer will completely remove all versions of Flash Player from this comp
|
||||
|
||||
fn draw_choice(&self, r: &mut Renderer) {
|
||||
self.browser_ask_label.draw(r, &self.fonts);
|
||||
self.native_host_box.draw(r);
|
||||
self.native_host_label.draw(r, &self.fonts);
|
||||
self.pepper_box.draw(r);
|
||||
self.pepper_label.draw(r, &self.fonts);
|
||||
self.netscape_box.draw(r);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::install_flags::{self, InstallFlags};
|
||||
use clean_flash_common::{
|
||||
process_utils, registry, resources, system_info, InstallError, ProgressCallback,
|
||||
native_host, process_utils, registry, resources, system_info, InstallError, ProgressCallback,
|
||||
};
|
||||
use std::env;
|
||||
use std::fs;
|
||||
@@ -89,6 +89,7 @@ fn install_from_archive(
|
||||
let flash64_path = si.flash64_path.clone();
|
||||
let system32_path = si.system32_path.clone();
|
||||
let flash_program32_path = si.flash_program32_path.clone();
|
||||
let native_host_dir = native_host::get_native_host_install_dir();
|
||||
|
||||
let mut registry_to_apply: Vec<&str> = vec![resources::INSTALL_GENERAL];
|
||||
|
||||
@@ -179,6 +180,24 @@ fn install_from_archive(
|
||||
registry_instructions: Some(resources::INSTALL_PP_64),
|
||||
},
|
||||
),
|
||||
(
|
||||
"native-host-64",
|
||||
InstallEntry {
|
||||
install_text: "Installing native messaging host (64-bit)...",
|
||||
required_flags: install_flags::NATIVE_HOST | install_flags::X64,
|
||||
target_directory: native_host_dir.clone(),
|
||||
registry_instructions: None,
|
||||
},
|
||||
),
|
||||
(
|
||||
"native-host-32",
|
||||
InstallEntry {
|
||||
install_text: "Installing native messaging host (32-bit)...",
|
||||
required_flags: install_flags::NATIVE_HOST,
|
||||
target_directory: native_host_dir.clone(),
|
||||
registry_instructions: None,
|
||||
},
|
||||
),
|
||||
];
|
||||
|
||||
let legacy = si.is_legacy_windows();
|
||||
@@ -202,24 +221,48 @@ fn install_from_archive(
|
||||
|
||||
let filename = parts[0];
|
||||
let install_key = filename.split('-').next().unwrap_or(filename);
|
||||
let is_pp32 = install_key == "pp32";
|
||||
let is_pp64 = install_key == "pp64";
|
||||
let should_extract_pepper_for_native_host =
|
||||
flags.is_set(install_flags::NATIVE_HOST)
|
||||
&& ((si.is_64bit && is_pp64) || (!si.is_64bit && is_pp32));
|
||||
|
||||
// Find the matching entry.
|
||||
let Some((_key, install_entry)) = entries.iter().find(|(k, _)| *k == install_key)
|
||||
// Find the matching entry: try exact match on full dirname first,
|
||||
// then fall back to first segment before '-'.
|
||||
let Some((_key, install_entry)) = entries
|
||||
.iter()
|
||||
.find(|(k, _)| *k == filename)
|
||||
.or_else(|| entries.iter().find(|(k, _)| *k == install_key))
|
||||
else {
|
||||
io::copy(reader, &mut io::sink()).map_err(sevenz_rust2::Error::from)?;
|
||||
return Ok(true);
|
||||
};
|
||||
|
||||
// Check required flags.
|
||||
let should_install_by_flags = install_entry.required_flags == install_flags::NONE
|
||||
|| flags.is_set(install_entry.required_flags);
|
||||
if install_entry.required_flags != install_flags::NONE
|
||||
&& !flags.is_set(install_entry.required_flags)
|
||||
&& !(should_extract_pepper_for_native_host && (is_pp32 || is_pp64))
|
||||
{
|
||||
io::copy(reader, &mut io::sink()).map_err(sevenz_rust2::Error::from)?;
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
// Check debug flag match.
|
||||
if install_entry.required_flags != install_flags::NONE {
|
||||
// For native-host: on 64-bit use only native-host-64, on 32-bit use only native-host-32.
|
||||
if filename == "native-host-32" && si.is_64bit {
|
||||
io::copy(reader, &mut io::sink()).map_err(sevenz_rust2::Error::from)?;
|
||||
return Ok(true);
|
||||
}
|
||||
if filename == "native-host-64" && !si.is_64bit {
|
||||
io::copy(reader, &mut io::sink()).map_err(sevenz_rust2::Error::from)?;
|
||||
return Ok(true);
|
||||
}
|
||||
|
||||
// Check debug flag match (skip native-host entries from this check).
|
||||
if install_entry.required_flags != install_flags::NONE
|
||||
&& (install_entry.required_flags & install_flags::NATIVE_HOST) == 0
|
||||
{
|
||||
let is_debug_file = filename.contains("-debug");
|
||||
if flags.is_set(install_flags::DEBUG) != is_debug_file {
|
||||
io::copy(reader, &mut io::sink()).map_err(sevenz_rust2::Error::from)?;
|
||||
@@ -236,14 +279,31 @@ fn install_from_archive(
|
||||
}
|
||||
}
|
||||
|
||||
form.update_progress_label(install_entry.install_text, true);
|
||||
let extract_for_native_host_only = !should_install_by_flags
|
||||
&& should_extract_pepper_for_native_host
|
||||
&& (is_pp32 || is_pp64);
|
||||
|
||||
if extract_for_native_host_only {
|
||||
form.update_progress_label(
|
||||
"Extracting Pepper files required by modern browser support...",
|
||||
true,
|
||||
);
|
||||
} else {
|
||||
form.update_progress_label(install_entry.install_text, true);
|
||||
}
|
||||
|
||||
let target_directory = if extract_for_native_host_only {
|
||||
native_host_dir.clone()
|
||||
} else {
|
||||
install_entry.target_directory.clone()
|
||||
};
|
||||
|
||||
// Ensure target directory exists.
|
||||
let _ = fs::create_dir_all(&install_entry.target_directory);
|
||||
let _ = fs::create_dir_all(&target_directory);
|
||||
|
||||
// Extract file: use just the file name (strip any path prefix).
|
||||
let out_name = parts.last().unwrap_or(&filename);
|
||||
let out_path = install_entry.target_directory.join(out_name);
|
||||
let out_path = target_directory.join(out_name);
|
||||
|
||||
let mut buf = Vec::new();
|
||||
reader.read_to_end(&mut buf).map_err(sevenz_rust2::Error::from)?;
|
||||
@@ -319,6 +379,11 @@ fn install_from_archive(
|
||||
}
|
||||
}
|
||||
|
||||
// Install native messaging host manifests for detected browsers.
|
||||
if flags.is_set(install_flags::NATIVE_HOST) {
|
||||
native_host::install_native_host(form)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//#![windows_subsystem = "windows"]
|
||||
#![windows_subsystem = "windows"]
|
||||
|
||||
mod install_flags;
|
||||
mod install_form;
|
||||
@@ -34,8 +34,8 @@ fn main() {
|
||||
// Set window icon from the resource embedded by build.rs.
|
||||
clean_flash_ui::set_window_icon(&window);
|
||||
|
||||
// Cap at ~60 fps.
|
||||
window.set_target_fps(60);
|
||||
// Cap at ~24 fps.
|
||||
window.set_target_fps(24);
|
||||
|
||||
// Renderer operates at physical resolution; the form layout is scaled accordingly.
|
||||
let mut renderer = Renderer::new(phys_w, phys_h);
|
||||
|
||||
@@ -21,7 +21,7 @@ including Clean Flash Player and older versions of Adobe Flash Player.";
|
||||
const COMPLETE_TEXT: &str = "\nAll versions of Flash Player have been successfully uninstalled.\n\n\
|
||||
If you ever change your mind, check out Clean Flash Player's website!";
|
||||
|
||||
const UNINSTALL_TICKS: i32 = 9;
|
||||
const UNINSTALL_TICKS: i32 = 10;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
enum Panel {
|
||||
|
||||
Reference in New Issue
Block a user