use std::path::Path;
use clap::ArgMatches;
use crate::config::{get_profile};
use crate::winrm::WinRM;
use crate::winrm::elevated;
type BoxedError = Box<dyn std::error::Error + Send + Sync>;
pub fn cmd_winrm_test(args: &ArgMatches) -> std::result::Result<(), BoxedError> {
let name = args.get_one::<String>("profile").unwrap();
let timeout: u64 = args.get_one::<String>("timeout").unwrap().parse()?;
let profile = get_profile(name).map_err(|e| Box::new(e) as BoxedError)?;
let port = profile.winrm_port.ok_or_else(|| {
format!("No WinRM port configured for '{}'", profile.name)
})?;
let winrm = WinRM::new("127.0.0.1", port, profile.user, profile.pass);
println!("Connecting to WinRM on port {port}...");
let result = winrm.run_ps("$env:COMPUTERNAME");
match result {
Ok(r) => println!("Connected! Host: {} (exit {})", r.stdout.trim(), r.exit_code),
Err(e) => return Err(format!("WinRM not reachable: {e}").into()),
}
println!("\n=== Diagnostic: chunk write mechanism ===");
let test_content = b"Write-Output 'chunk test passed'";
let test_b64 = base64::Engine::encode(
&base64::engine::general_purpose::STANDARD,
test_content,
);
let test_cmd = format!(
"[System.IO.File]::WriteAllBytes('C:\\winrm-test-chunk.bin', [System.Convert]::FromBase64String('{test_b64}')); if (Test-Path 'C:\\winrm-test-chunk.bin') {{ 'CHUNK_OK' }} else {{ 'CHUNK_FAIL' }}"
);
let test_result = winrm.run_ps(&test_cmd)?;
println!("Chunk write result: stdout='{}' stderr='{}' exit={}", test_result.stdout.trim(), test_result.stderr.trim(), test_result.exit_code);
let readback = winrm.run_ps("if (Test-Path 'C:\\winrm-test-chunk.bin') { [System.Convert]::ToBase64String([System.IO.File]::ReadAllBytes('C:\\winrm-test-chunk.bin')) } else { 'MISSING' }")?;
if readback.stdout.trim() != "MISSING" && readback.exit_code == 0 {
let decoded = base64::Engine::decode(
&base64::engine::general_purpose::STANDARD,
readback.stdout.trim(),
);
if let Ok(bytes) = decoded {
println!("Readback content: {}", String::from_utf8_lossy(&bytes));
}
}
winrm.run_ps("Remove-Item 'C:\\winrm-test-chunk.bin' -Force -ErrorAction SilentlyContinue").ok();
let test_elevated_script = r#"
Write-Output "Testing elevated script write"
Set-Content -Path 'C:\winrm-test-elevated.txt' -Value 'elevated worked' -Encoding UTF8
Write-Output "File created"
"#;
println!("\n=== Diagnostic: elevated script write ({} bytes) ===", test_elevated_script.len());
let script_b64 = base64::Engine::encode(
&base64::engine::general_purpose::STANDARD,
test_elevated_script.as_bytes(),
);
println!("Base64 length: {} bytes", script_b64.len());
let write_cmd = format!(
"[System.IO.File]::WriteAllBytes('C:\\winrm-test-script.bin', [System.Convert]::FromBase64String('{script_b64}')); if (Test-Path 'C:\\winrm-test-script.bin') {{ 'WRITE_OK' }} else {{ 'WRITE_FAIL' }}"
);
println!("Write command length: {} bytes", write_cmd.len());
let write_result = winrm.run_ps(&write_cmd)?;
println!("Write result: stdout='{}' stderr='{}' exit={}", write_result.stdout.trim(), write_result.stderr.trim(), write_result.exit_code);
if write_result.stdout.contains("WRITE_OK") {
let file_check = winrm.run_ps("$f = Get-Item 'C:\\winrm-test-script.bin'; '{0} bytes' -f $f.Length")?;
println!("File size on VM: {}", file_check.stdout.trim());
let readback = winrm.run_ps("[System.Convert]::ToBase64String([System.IO.File]::ReadAllBytes('C:\\winrm-test-script.bin'))")?;
if readback.exit_code == 0 {
let rb_b64 = readback.stdout.trim();
if rb_b64 == script_b64 {
println!("Round-trip verification: MATCH");
} else {
println!("Round-trip MISMATCH! sent {} b64, got {} b64", script_b64.len(), rb_b64.len());
if rb_b64.len() < 200 {
println!("Readback b64: {rb_b64}");
}
}
}
}
winrm.run_ps("Remove-Item 'C:\\winrm-test-script.bin','C:\\winrm-test-elevated.txt' -Force -ErrorAction SilentlyContinue").ok();
println!("\n=== Testing actual run_elevated with tiny script ===");
let tiny_script = "Write-Output 'tiny test'";
println!("Script: '{tiny_script}' ({} bytes)", tiny_script.len());
use std::time::{SystemTime, UNIX_EPOCH};
let sentinel_id = SystemTime::now().duration_since(UNIX_EPOCH).unwrap_or_default().as_millis().to_string();
let sentinel_path = format!("C:\\winrm-test-sentinel-{sentinel_id}.done");
let script_path = "C:\\winrm-test-elevated.ps1";
let raw_b64 = base64::Engine::encode(&base64::engine::general_purpose::STANDARD, tiny_script.as_bytes());
let write_cmd = format!(
"[System.IO.File]::WriteAllBytes('{script_path}', [System.Convert]::FromBase64String('{raw_b64}'))"
);
println!("Write command: {} bytes", write_cmd.len());
let wr = winrm.run_ps(&write_cmd)?;
println!("Write result: exit={} stdout='{}'", wr.exit_code, wr.stdout.lines().last().unwrap_or("").trim());
let vr = winrm.run_ps(&format!("if (Test-Path '{script_path}') {{ 'OK' }} else {{ 'MISSING' }}"))?;
println!("Verify: {}", vr.stdout.lines().find(|l| l.contains("OK") || l.contains("MISSING")).unwrap_or("unknown").trim());
if vr.stdout.contains("OK") {
let run_cmd = format!("powershell -NoProfile -ExecutionPolicy Bypass -File {script_path}");
println!("Running via: {run_cmd}");
let rr = winrm.run_ps(&run_cmd)?;
println!("Run result: exit={} stdout='{}' stderr='{}'", rr.exit_code, rr.stdout.trim(), rr.stderr.trim());
}
winrm.run_ps(&format!("Remove-Item '{script_path}','{sentinel_path}' -Force -ErrorAction SilentlyContinue")).ok();
println!("\n=== Diagnostic complete ===\n");
if let Some(script_path) = args.get_one::<String>("script") {
let ps_code = std::fs::read_to_string(script_path)
.map_err(|e| format!("Reading {script_path}: {e}"))?;
println!("\nExecuting {} via elevated run_elevated (timeout={timeout}s)...", Path::new(script_path).display());
println!("Script size: {} bytes\n", ps_code.len());
let progress = |msg: &str| println!(" [progress] {msg}");
elevated::run_elevated(&winrm, &ps_code, timeout, Some(&progress))?;
println!("\nScript completed successfully.");
} else {
let test_script = r#"
Write-Output "Hello from elevated WinRM!"
Write-Output "Testing file write..."
Set-Content -Path 'C:\winrm-test-output.txt' -Value 'test successful' -Encoding UTF8
Write-Output "File written to C:\winrm-test-output.txt"
if (Test-Path 'C:\winrm-test-output.txt') {
Write-Output "File verified: $(Get-Content C:\winrm-test-output.txt)"
} else {
throw "File was not created!"
}
Remove-Item 'C:\winrm-test-output.txt' -Force
Write-Output "Cleanup done. All good."
"#;
println!("\nRunning built-in test script via elevated run_elevated...");
let progress = |msg: &str| println!(" [progress] {msg}");
elevated::run_elevated(&winrm, test_script, timeout, Some(&progress))?;
println!("\nTest script completed successfully.");
}
Ok(())
}