kftray-helper 0.27.13

Privileged helper binary for KFTray
Documentation
use std::path::PathBuf;
use std::time::Duration;

use log::{
    debug,
    error,
    info,
    warn,
};

use super::binary_finder;
use super::{
    installation,
    socket_comm,
    uninstallation,
};
use crate::communication::get_default_socket_path;
use crate::error::HelperError;
use crate::messages::{
    HelperResponse,
    RequestCommand,
};

pub struct HelperClient {
    app_id: String,
    socket_path: PathBuf,
}

impl HelperClient {
    pub fn new(app_id: String) -> Result<Self, HelperError> {
        let socket_path = get_default_socket_path()?;
        Ok(Self {
            app_id,
            socket_path,
        })
    }

    pub fn is_helper_available(&self) -> bool {
        socket_comm::is_socket_available(&self.socket_path)
    }

    pub fn ensure_helper_installed(&self) -> Result<(), HelperError> {
        if self.is_helper_available() {
            info!("Helper already available at {}", self.socket_path.display());
            return Ok(());
        }

        let helper_path = binary_finder::find_helper_binary()?;
        info!("Found helper binary at {}", helper_path.display());

        installation::install_helper(&helper_path)?;
        info!("Helper installation completed, waiting for socket to become available");

        let mut retry_count = 0;
        let max_retries = 10;
        while retry_count < max_retries {
            if self.is_helper_available() {
                info!("Helper is now available at {}", self.socket_path.display());
                return Ok(());
            }

            let wait_time = 500 * (retry_count + 1);
            debug!(
                "Helper not available yet, waiting {}ms (attempt {}/{})",
                wait_time,
                retry_count + 1,
                max_retries
            );
            std::thread::sleep(Duration::from_millis(wait_time));
            retry_count += 1;
        }

        if !self.is_helper_available() {
            error!(
                "Helper installation failed - socket not available at {}",
                self.socket_path.display()
            );
            return Err(HelperError::PlatformService(format!(
                "Helper was installed but socket is not available at {}",
                self.socket_path.display()
            )));
        }

        Ok(())
    }

    pub fn ensure_helper_uninstalled(&self) -> Result<(), HelperError> {
        if !self.is_helper_available() {
            info!("Helper is not running, nothing to uninstall");
            return Ok(());
        }

        info!("Attempting to stop the helper service...");
        let _ = self.stop_service();

        let helper_path = binary_finder::find_helper_binary()?;
        info!("Found helper binary at {}", helper_path.display());

        info!("Uninstalling helper...");
        uninstallation::uninstall_helper(&helper_path)?;

        info!("Waiting for helper to be fully uninstalled...");
        let mut retry_count = 0;
        let max_retries = 10;

        while retry_count < max_retries {
            if !self.is_helper_available() {
                info!("Confirmed helper is no longer available");
                return Ok(());
            }

            let wait_time = 500 * (retry_count + 1);
            debug!(
                "Helper still available, waiting {}ms (attempt {}/{})",
                wait_time,
                retry_count + 1,
                max_retries
            );
            std::thread::sleep(Duration::from_millis(wait_time));
            retry_count += 1;
        }

        if self.is_helper_available() {
            warn!(
                "Helper service is still responding after uninstall at {}",
                self.socket_path.display()
            );
            return Err(HelperError::PlatformService(format!(
                "Helper was uninstalled but is still available at {}",
                self.socket_path.display()
            )));
        }

        info!("Helper service successfully uninstalled");
        Ok(())
    }

    pub fn send_request(&self, command: RequestCommand) -> Result<HelperResponse, HelperError> {
        self.ensure_helper_installed()?;
        socket_comm::send_request(&self.socket_path, &self.app_id, command)
    }

    pub fn allocate_local_address(&self, service_name: String) -> Result<String, HelperError> {
        let command = RequestCommand::Address(super::super::messages::AddressCommand::Allocate {
            service_name,
        });
        let response = self.send_request(command)?;

        match response.result {
            super::super::messages::RequestResult::StringSuccess(address) => Ok(address),
            super::super::messages::RequestResult::Error(error) => {
                Err(HelperError::Communication(error))
            }
            _ => Err(HelperError::Communication(
                "Unexpected response format".to_string(),
            )),
        }
    }

    pub fn release_local_address(&self, address: String) -> Result<(), HelperError> {
        let command =
            RequestCommand::Address(super::super::messages::AddressCommand::Release { address });
        let response = self.send_request(command)?;

        match response.result {
            super::super::messages::RequestResult::Success => Ok(()),
            super::super::messages::RequestResult::Error(error) => {
                Err(HelperError::Communication(error))
            }
            _ => Err(HelperError::Communication(
                "Unexpected response format".to_string(),
            )),
        }
    }
}