You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
CleanFlashInstaller/rust/crates/clean_flash_installer/src/installer_windows.rs

428 lines
15 KiB
Rust

use crate::install_flags::{self, InstallFlags};
use clean_flash_common::{
native_host, process_utils, registry, resources, system_info, InstallError, ProgressCallback,
};
use std::env;
use std::fs;
use std::io::{self, Cursor};
use std::path::{Path, PathBuf};
/// Metadata for a single installable component.
struct InstallEntry {
install_text: &'static str,
required_flags: u32,
target_directory: PathBuf,
registry_instructions: Option<&'static str>,
}
/// Register an ActiveX OCX via regsvr32 (unregister first, then register).
pub fn register_activex(filename: &str) -> Result<(), InstallError> {
let path = Path::new(filename);
let dir = path.parent().unwrap_or(Path::new("."));
let file_name = path
.file_name()
.unwrap_or_default()
.to_string_lossy()
.to_string();
let _ = env::set_current_dir(dir);
let process = process_utils::run_process("regsvr32.exe", &["/s", "/u", &file_name]);
if !process.is_successful() {
return Err(InstallError::new(format!(
"Failed to unregister ActiveX plugin: error code {}\n\n{}",
process.exit_code, process.output
)));
}
let process = process_utils::run_process("regsvr32.exe", &["/s", &file_name]);
if !process.is_successful() {
return Err(InstallError::new(format!(
"Failed to register ActiveX plugin: error code {}\n\n{}",
process.exit_code, process.output
)));
}
Ok(())
}
/// Create a Windows shortcut (.lnk) using a PowerShell one-liner.
fn create_shortcut(
folder: &Path,
executable: &Path,
name: &str,
description: &str,
) -> Result<(), InstallError> {
let lnk_path = folder.join(format!("{}.lnk", name));
let exe_str = executable.to_string_lossy();
let lnk_str = lnk_path.to_string_lossy();
// Use PowerShell to create the shortcut via WScript.Shell COM.
let script = format!(
"$ws = New-Object -ComObject WScript.Shell; \
$s = $ws.CreateShortcut('{}'); \
$s.TargetPath = '{}'; \
$s.Description = '{}'; \
$s.IconLocation = '{}'; \
$s.Save()",
lnk_str, exe_str, description, exe_str
);
let result = process_utils::run_process("powershell.exe", &["-NoProfile", "-Command", &script]);
if !result.is_successful() {
return Err(InstallError::new(format!(
"Failed to create shortcut: {}",
result.output
)));
}
Ok(())
}
/// Extract the embedded 7z archive and install files to the correct locations.
fn install_from_archive(
archive_bytes: &[u8],
form: &dyn ProgressCallback,
flags: &mut InstallFlags,
) -> Result<(), InstallError> {
let si = system_info::SystemInfo::new();
let flash32_path = si.flash32_path.clone();
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];
if si.is_64bit {
flags.set_flag(install_flags::X64);
registry_to_apply.push(resources::INSTALL_GENERAL_64);
}
let entries: Vec<(&str, InstallEntry)> = vec![
(
"controlpanel",
InstallEntry {
install_text: "Installing Flash Player utilities...",
required_flags: install_flags::NONE,
target_directory: system32_path.clone(),
registry_instructions: None,
},
),
(
"uninstaller",
InstallEntry {
install_text: "Extracting uninstaller...",
required_flags: install_flags::NONE,
target_directory: flash_program32_path.clone(),
registry_instructions: None,
},
),
(
"standalone",
InstallEntry {
install_text: "Installing 32-bit Standalone Flash Player...",
required_flags: install_flags::PLAYER,
target_directory: flash_program32_path.clone(),
registry_instructions: None,
},
),
(
"ocx32",
InstallEntry {
install_text: "Installing 32-bit Flash Player for Internet Explorer...",
required_flags: install_flags::ACTIVEX,
target_directory: flash32_path.clone(),
registry_instructions: None,
},
),
(
"np32",
InstallEntry {
install_text: "Installing 32-bit Flash Player for Firefox...",
required_flags: install_flags::NETSCAPE,
target_directory: flash32_path.clone(),
registry_instructions: Some(resources::INSTALL_NP),
},
),
(
"pp32",
InstallEntry {
install_text: "Installing 32-bit Flash Player for Chrome...",
required_flags: install_flags::PEPPER,
target_directory: flash32_path.clone(),
registry_instructions: Some(resources::INSTALL_PP),
},
),
(
"ocx64",
InstallEntry {
install_text: "Installing 64-bit Flash Player for Internet Explorer...",
required_flags: install_flags::ACTIVEX | install_flags::X64,
target_directory: flash64_path.clone(),
registry_instructions: None,
},
),
(
"np64",
InstallEntry {
install_text: "Installing 64-bit Flash Player for Firefox...",
required_flags: install_flags::NETSCAPE | install_flags::X64,
target_directory: flash64_path.clone(),
registry_instructions: Some(resources::INSTALL_NP_64),
},
),
(
"pp64",
InstallEntry {
install_text: "Installing 64-bit Flash Player for Chrome...",
required_flags: install_flags::PEPPER | install_flags::X64,
target_directory: flash64_path.clone(),
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();
// Extract archive using sevenz-rust2.
sevenz_rust2::decompress_with_extract_fn(
Cursor::new(archive_bytes),
".",
|entry, reader, _dest| {
// Skip directory entries — they have no file content.
if entry.is_directory() {
io::copy(reader, &mut io::sink()).map_err(sevenz_rust2::Error::from)?;
return Ok(true);
}
let entry_name = entry.name().to_string();
let parts: Vec<&str> = entry_name.split('/').collect();
if parts.is_empty() {
return Ok(true);
}
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: 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);
}
// 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)?;
return Ok(true);
}
}
// Check legacy flag for ActiveX entries.
if (install_entry.required_flags & install_flags::ACTIVEX) != 0 {
let is_legacy_file = filename.contains("-legacy");
if legacy != is_legacy_file {
io::copy(reader, &mut io::sink()).map_err(sevenz_rust2::Error::from)?;
return Ok(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(&target_directory);
// Extract file: use just the file name (strip any path prefix).
let out_name = parts.last().unwrap_or(&filename);
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)?;
fs::write(&out_path, &buf).map_err(sevenz_rust2::Error::from)?;
Ok(true)
},
)
.map_err(|e| InstallError::new(format!("Failed to extract archive: {}", e)))?;
// Create Player shortcuts.
if flags.is_set(install_flags::PLAYER) {
let is_debug = flags.is_set(install_flags::DEBUG);
let name = if is_debug {
"Flash Player (Debug)"
} else {
"Flash Player"
};
let description = format!(
"Standalone Flash Player {}{}",
clean_flash_common::update_checker::FLASH_VERSION,
if is_debug { " (Debug)" } else { "" }
);
let exe_name = if is_debug {
"flashplayer_sa_debug.exe"
} else {
"flashplayer_sa.exe"
};
let executable = flash_program32_path.join(exe_name);
if flags.is_set(install_flags::PLAYER_START_MENU) {
if let Some(start_menu) = get_start_menu() {
let _ = create_shortcut(&start_menu, &executable, name, &description);
}
}
if flags.is_set(install_flags::PLAYER_DESKTOP) {
if let Some(desktop) = get_desktop() {
let _ = create_shortcut(&desktop, &executable, name, &description);
}
}
}
// Collect registry entries for enabled components.
for (_key, entry) in &entries {
if flags.is_set(entry.required_flags) {
if let Some(reg) = entry.registry_instructions {
registry_to_apply.push(reg);
}
}
}
form.update_progress_label("Applying registry changes...", true);
let refs: Vec<&str> = registry_to_apply.iter().copied().collect();
registry::apply_registry(&refs)?;
// Register ActiveX OCX files.
if flags.is_set(install_flags::ACTIVEX) {
form.update_progress_label(
"Activating 32-bit Flash Player for Internet Explorer...",
true,
);
let ocx32 = flash32_path.join(format!("Flash32_{}.ocx", si.version_path));
register_activex(&ocx32.to_string_lossy())?;
if si.is_64bit {
form.update_progress_label(
"Activating 64-bit Flash Player for Internet Explorer...",
true,
);
let ocx64 = flash64_path.join(format!("Flash64_{}.ocx", si.version_path));
register_activex(&ocx64.to_string_lossy())?;
}
}
// Install native messaging host manifests for detected browsers.
if flags.is_set(install_flags::NATIVE_HOST) {
native_host::install_native_host(form)?;
}
Ok(())
}
/// Main install entry point.
pub fn install(
form: &dyn ProgressCallback,
flags: &mut InstallFlags,
) -> Result<(), InstallError> {
if flags.is_none_set() {
return Ok(());
}
// The 7z archive is embedded in the binary via include_bytes!.
// For the port, we expect it at a known resource path; if not present,
// this is a no-op placeholder.
let archive_bytes: &[u8] = include_bytes!("../cleanflash.7z");
if archive_bytes.is_empty() {
// Nothing to extract; still apply the rest of the steps.
return Ok(());
}
install_from_archive(archive_bytes, form, flags)
}
fn get_start_menu() -> Option<PathBuf> {
env::var("APPDATA")
.ok()
.map(|p| {
PathBuf::from(p)
.join("Microsoft")
.join("Windows")
.join("Start Menu")
})
}
fn get_desktop() -> Option<PathBuf> {
env::var("USERPROFILE")
.ok()
.map(|p| PathBuf::from(p).join("Desktop"))
}