kftray-helper 0.27.24

Privileged helper binary for KFTray
Documentation
use std::path::Path;
use std::process::Command;
use std::time::Duration;

use crate::error::HelperError;

pub fn install_helper(helper_path: &Path) -> Result<(), HelperError> {
    #[cfg(target_os = "macos")]
    {
        let output = Command::new(helper_path).args(["install"]).output();

        if let Err(_e) = output {
            let escaped_path = helper_path.to_string_lossy().replace("\"", "\\\"");
            let script = format!(
                r#"do shell script "{escaped_path} install" with administrator privileges"#
            );

            let output = Command::new("osascript")
                .args(["-e", &script])
                .output()
                .map_err(|e| {
                    HelperError::PlatformService(format!(
                        "Failed to install helper with admin privileges: {e}"
                    ))
                })?;

            if !output.status.success() {
                let error = String::from_utf8_lossy(&output.stderr);
                return Err(HelperError::PlatformService(format!(
                    "Failed to install helper with admin privileges: {error}"
                )));
            }
        } else if let Ok(output) = output
            && !output.status.success()
        {
            let error = String::from_utf8_lossy(&output.stderr);
            return Err(HelperError::PlatformService(format!(
                "Failed to install helper: {error}"
            )));
        }
    }

    #[cfg(target_os = "linux")]
    {
        let helper_path_str = helper_path.to_string_lossy();
        let final_helper_path = if helper_path_str.contains("/tmp/.mount_") {
            let temp_dir = std::env::temp_dir();
            let extracted_helper = temp_dir.join("kftray-helper-install");

            std::fs::copy(helper_path, &extracted_helper).map_err(|e| {
                HelperError::PlatformService(format!("Failed to copy helper from AppImage: {}", e))
            })?;

            #[cfg(unix)]
            {
                use std::os::unix::fs::PermissionsExt;
                let mut perms = std::fs::metadata(&extracted_helper)
                    .map_err(|e| {
                        HelperError::PlatformService(format!(
                            "Failed to get helper permissions: {}",
                            e
                        ))
                    })?
                    .permissions();
                perms.set_mode(0o755);
                std::fs::set_permissions(&extracted_helper, perms).map_err(|e| {
                    HelperError::PlatformService(format!("Failed to set helper permissions: {}", e))
                })?;
            }

            extracted_helper
        } else {
            helper_path.to_path_buf()
        };

        if std::env::var("RUST_LOG").is_ok() {
            println!("Installing helper via pkexec...");
        }
        let output = Command::new("/usr/bin/pkexec")
            .args([final_helper_path.to_string_lossy().as_ref(), "install"])
            .output();

        if output.is_err() {
            if std::env::var("RUST_LOG").is_ok() {
                println!("pkexec failed, trying sudo...");
            }
            let output = Command::new("sudo")
                .args([final_helper_path.to_string_lossy().as_ref(), "install"])
                .output()
                .map_err(|e| {
                    HelperError::PlatformService(format!(
                        "Failed to install helper with sudo: {}",
                        e
                    ))
                })?;

            if !output.status.success() {
                let error = String::from_utf8_lossy(&output.stderr);
                return Err(HelperError::PlatformService(format!(
                    "Failed to install helper with sudo: {}",
                    error
                )));
            }
        } else if let Ok(output) = output {
            let stdout = String::from_utf8_lossy(&output.stdout);
            let stderr = String::from_utf8_lossy(&output.stderr);

            if std::env::var("RUST_LOG").is_ok() && !stdout.is_empty() {
                println!("Helper output:\n{}", stdout);
            }

            if !output.status.success() {
                if std::env::var("RUST_LOG").is_ok() && !stderr.is_empty() {
                    println!("Helper error:\n{}", stderr);
                }
                return Err(HelperError::PlatformService(format!(
                    "Failed to install helper with pkexec: {}",
                    stderr
                )));
            }
        }

        if helper_path_str.contains("/tmp/.mount_") {
            let _ = std::fs::remove_file(std::env::temp_dir().join("kftray-helper-install"));
        }
    }

    #[cfg(target_os = "windows")]
    {
        let _args = format!("\"{}\" install", helper_path.to_string_lossy());

        let output = Command::new("powershell")
            .args([
                "-Command",
                &format!(
                    "Start-Process -FilePath \"{}\" -ArgumentList \"install\" -Verb RunAs -Wait",
                    helper_path.to_string_lossy()
                ),
            ])
            .output()
            .map_err(|e| {
                HelperError::PlatformService(format!(
                    "Failed to install helper with elevation: {}",
                    e
                ))
            })?;

        if !output.status.success() {
            let error = String::from_utf8_lossy(&output.stderr);
            return Err(HelperError::PlatformService(format!(
                "Failed to install helper with elevation: {}",
                error
            )));
        }
    }

    std::thread::sleep(Duration::from_secs(2));
    Ok(())
}