use std::io::{self, Read};
use std::process::Command;
fn main() {
let args: Vec<String> = std::env::args().collect();
if args.len() < 2 {
eprintln!("entrouter-universal CLI");
eprintln!();
eprintln!("Usage:");
eprintln!(" entrouter encode Read stdin, print base64 + fingerprint as JSON");
eprintln!(" entrouter decode Read JSON from stdin, print original data");
eprintln!(" entrouter verify Read JSON from stdin, verify integrity");
eprintln!(" entrouter raw-encode Read stdin, print just the base64 (no JSON)");
eprintln!(" entrouter raw-decode Read base64 from stdin, print original data");
eprintln!(" entrouter ssh <host> Read a command from stdin, execute it on remote host");
eprintln!();
eprintln!("Pipe-friendly: echo 'hello' | entrouter encode | entrouter verify");
eprintln!("SSH example: echo 'curl ...' | entrouter ssh root@your-vps");
std::process::exit(1);
}
match args[1].as_str() {
"ssh" => {
if args.len() < 3 {
eprintln!("Usage: entrouter ssh <user@host>");
eprintln!(" Reads the command to run from stdin.");
eprintln!(" Example: echo 'curl -s http://localhost:3000/health' | entrouter ssh root@your-vps");
std::process::exit(1);
}
let host = &args[2];
let input = read_stdin();
cmd_ssh(host, &input);
}
cmd => {
let input = read_stdin();
match cmd {
"encode" => cmd_encode(&input),
"decode" => cmd_decode(&input),
"verify" => cmd_verify(&input),
"raw-encode" => cmd_raw_encode(&input),
"raw-decode" => cmd_raw_decode(&input),
other => {
eprintln!("Unknown command: {other}");
eprintln!("Try: encode, decode, verify, raw-encode, raw-decode, ssh");
std::process::exit(1);
}
}
}
}
}
fn read_stdin() -> String {
let mut buf = String::new();
io::stdin().read_to_string(&mut buf).unwrap_or_else(|e| {
eprintln!("Failed to read stdin: {e}");
std::process::exit(1);
});
if buf.ends_with('\n') {
buf.pop();
if buf.ends_with('\r') {
buf.pop();
}
}
buf
}
fn cmd_encode(input: &str) {
let encoded = entrouter_universal::encode_str(input);
let fp = entrouter_universal::fingerprint_str(input);
println!("{{\"encoded\":\"{encoded}\",\"fingerprint\":\"{fp}\"}}");
}
fn cmd_decode(input: &str) {
let v: serde_json::Value = serde_json::from_str(input).unwrap_or_else(|e| {
eprintln!("Invalid JSON: {e}");
std::process::exit(1);
});
let encoded = v["encoded"].as_str().unwrap_or_else(|| {
eprintln!("Missing \"encoded\" field in JSON");
std::process::exit(1);
});
match entrouter_universal::decode(encoded) {
Ok(bytes) => {
let text = String::from_utf8_lossy(&bytes);
print!("{text}");
}
Err(e) => {
eprintln!("Decode failed: {e}");
std::process::exit(1);
}
}
}
fn cmd_verify(input: &str) {
let v: serde_json::Value = serde_json::from_str(input).unwrap_or_else(|e| {
eprintln!("Invalid JSON: {e}");
std::process::exit(1);
});
let encoded = v["encoded"].as_str().unwrap_or_else(|| {
eprintln!("Missing \"encoded\" field");
std::process::exit(1);
});
let fp = v["fingerprint"].as_str().unwrap_or_else(|| {
eprintln!("Missing \"fingerprint\" field");
std::process::exit(1);
});
match entrouter_universal::verify(encoded, fp) {
Ok(result) => {
if result.intact {
println!("INTACT");
println!("Decoded: {}", String::from_utf8_lossy(&result.decoded));
} else {
eprintln!("TAMPERED");
eprintln!("Expected fingerprint: {fp}");
eprintln!("Actual fingerprint: {}", result.fingerprint);
std::process::exit(1);
}
}
Err(e) => {
eprintln!("Verify failed: {e}");
std::process::exit(1);
}
}
}
fn cmd_raw_encode(input: &str) {
print!("{}", entrouter_universal::encode_str(input));
}
fn cmd_raw_decode(input: &str) {
match entrouter_universal::decode(input) {
Ok(bytes) => {
let text = String::from_utf8_lossy(&bytes);
print!("{text}");
}
Err(e) => {
eprintln!("Decode failed: {e}");
std::process::exit(1);
}
}
}
fn cmd_ssh(host: &str, command: &str) {
let encoded = entrouter_universal::encode_str(command);
let remote_cmd = format!(
"echo '{}' | entrouter raw-decode | sh",
encoded
);
let status = Command::new("ssh")
.arg(host)
.arg(&remote_cmd)
.stdin(std::process::Stdio::inherit())
.stdout(std::process::Stdio::inherit())
.stderr(std::process::Stdio::inherit())
.status()
.unwrap_or_else(|e| {
eprintln!("Failed to run ssh: {e}");
std::process::exit(1);
});
if !status.success() {
std::process::exit(status.code().unwrap_or(1));
}
}