diff --git a/rust/Cargo.toml b/rust/Cargo.toml index f57e7d4..97a7387 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -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", diff --git a/rust/crates/clean_flash_common/src/lib.rs b/rust/crates/clean_flash_common/src/lib.rs index 6c71650..63358a7 100644 --- a/rust/crates/clean_flash_common/src/lib.rs +++ b/rust/crates/clean_flash_common/src/lib.rs @@ -1,4 +1,5 @@ pub mod file_util; +pub mod native_host; pub mod process_utils; pub mod redirection; pub mod registry; diff --git a/rust/crates/clean_flash_common/src/native_host.rs b/rust/crates/clean_flash_common/src/native_host.rs new file mode 100644 index 0000000..9acc2e0 --- /dev/null +++ b/rust/crates/clean_flash_common/src/native_host.rs @@ -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 { + 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 = reg_key.encode_utf16().chain(std::iter::once(0)).collect(); + let val_wide: Vec = 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 = nmh_base_key + .encode_utf16() + .chain(std::iter::once(0)) + .collect(); + let name_wide: Vec = 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 = 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 +} diff --git a/rust/crates/clean_flash_common/src/uninstaller.rs b/rust/crates/clean_flash_common/src/uninstaller.rs index ee97a46..df5a43d 100644 --- a/rust/crates/clean_flash_common/src/uninstaller.rs +++ b/rust/crates/clean_flash_common/src/uninstaller.rs @@ -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(()) } diff --git a/rust/crates/clean_flash_installer/src/install_flags.rs b/rust/crates/clean_flash_installer/src/install_flags.rs index 49a51e8..172d974 100644 --- a/rust/crates/clean_flash_installer/src/install_flags.rs +++ b/rust/crates/clean_flash_installer/src/install_flags.rs @@ -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 diff --git a/rust/crates/clean_flash_installer/src/install_form.rs b/rust/crates/clean_flash_installer/src/install_form.rs index 62aff7b..d257031 100644 --- a/rust/crates/clean_flash_installer/src/install_form.rs +++ b/rust/crates/clean_flash_installer/src/install_form.rs @@ -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); diff --git a/rust/crates/clean_flash_installer/src/installer.rs b/rust/crates/clean_flash_installer/src/installer.rs index 4e22b18..16bec64 100644 --- a/rust/crates/clean_flash_installer/src/installer.rs +++ b/rust/crates/clean_flash_installer/src/installer.rs @@ -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); - - // Find the matching entry. - let Some((_key, install_entry)) = entries.iter().find(|(k, _)| *k == install_key) + 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: 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(()) } diff --git a/rust/crates/clean_flash_installer/src/main.rs b/rust/crates/clean_flash_installer/src/main.rs index a8beb5e..405a95b 100644 --- a/rust/crates/clean_flash_installer/src/main.rs +++ b/rust/crates/clean_flash_installer/src/main.rs @@ -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); diff --git a/rust/crates/clean_flash_uninstaller/src/uninstall_form.rs b/rust/crates/clean_flash_uninstaller/src/uninstall_form.rs index d90feef..8466d79 100644 --- a/rust/crates/clean_flash_uninstaller/src/uninstall_form.rs +++ b/rust/crates/clean_flash_uninstaller/src/uninstall_form.rs @@ -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 {