use log::info;
use std::error::Error;
use std::path::{Path, PathBuf};
use std::process::Command;
use which::which;
pub fn locate_openconnect(user_path: &str) -> Option<PathBuf> {
let candidate = Path::new(user_path);
if candidate.exists() && candidate.is_file() {
return Some(candidate.to_path_buf());
}
if let Ok(found) = which(user_path) {
return Some(found);
}
let fallback_dirs = ["/sbin", "/usr/sbin", "/usr/local/sbin"];
for dir in &fallback_dirs {
let path_in_dir = Path::new(dir).join("openconnect");
if path_in_dir.exists() && path_in_dir.is_file() {
return Some(path_in_dir);
}
}
None
}
pub fn execute_openconnect(
cookie_value: String,
url: String,
run_command: &Option<String>,
openconnect_path: &Path,
) -> Result<(), Box<dyn Error>> {
let mut default_tools = vec!["doas", "sudo", "pkexec"];
if let Some(custom_command) = run_command {
info!("Custom run command provided: {}", custom_command);
if which(custom_command).is_ok() {
info!("Custom command found: {}", custom_command);
default_tools.insert(0, custom_command.as_str());
} else {
println!(
"Custom command '{}' not found, falling back to default tools.",
custom_command
);
info!(
"Custom command '{}' not found, using default tools.",
custom_command
);
}
} else {
info!("No custom run command provided, defaulting to built-in tools.");
}
info!("Checking for available tools/commands: {:?}", default_tools);
let command_to_run = default_tools
.iter()
.find_map(|&tool| which(tool).ok().map(|_| tool))
.ok_or("No available tool for running openconnect (sudo/doas/pkexec not found)")?;
println!(
"Running openconnect using {} for elevated privileges or execution",
command_to_run
);
info!(
"Command to run: {} {:?} --protocol nc -C DSID={} {}",
command_to_run,
openconnect_path.display(),
cookie_value,
url
);
Command::new(command_to_run)
.arg(openconnect_path)
.arg("--protocol")
.arg("nc")
.arg("-C")
.arg(format!("DSID={}", cookie_value))
.arg(url)
.status()?;
Ok(())
}