//! A headless prompt handler. //! //! This prompt handler uses the program's standard input and output to read inputs. It is not //! directly intended to be machine-readable, but can be used for scriptable automation in a //! fashion similar to a terminal handler. use std::io::{IsTerminal, Write}; use crate::{BoxResult, Error, Message, PromptHandler, Result}; /// A headless prompt handler, usable in situations when a terminal might not be available, or for /// scripting purposes where manual input from a terminal is not desirable. pub struct Headless { stdin: std::io::Stdin, stderr: std::io::Stderr, } impl Headless { /// Create a new [`Headless`] prompt handler. #[allow(clippy::missing_errors_doc, clippy::new_without_default)] pub fn new() -> Self { Self { stdin: std::io::stdin(), stderr: std::io::stderr(), } } } impl PromptHandler for Headless { fn prompt_input(&mut self, prompt: &str) -> Result { self.stderr.write_all(prompt.as_bytes())?; self.stderr.flush()?; let mut line = String::new(); self.stdin.read_line(&mut line)?; Ok(line) } fn prompt_wordlist(&mut self, prompt: &str, _wordlist: &[&str]) -> Result { self.stderr.write_all(prompt.as_bytes())?; self.stderr.flush()?; let mut line = String::new(); self.stdin.read_line(&mut line)?; Ok(line) } fn prompt_passphrase(&mut self, prompt: &str) -> Result { // Temporarily perform an IOCTL to disable printed output. if self.stdin.is_terminal() { eprintln!("WARNING: Headless terminal mode may leak passwords!"); } self.stderr.write_all(prompt.as_bytes())?; self.stderr.flush()?; let mut line = String::new(); self.stdin.read_line(&mut line)?; Ok(line) } fn prompt_message(&mut self, prompt: Message) -> Result<()> { match prompt { Message::Text(s) => { self.stderr.write_all(s.as_bytes())?; self.stderr.flush()?; } Message::Data(s) => { self.stderr.write_all(s.as_bytes())?; self.stderr.flush()?; } } Ok(()) } fn prompt_validated_wordlist( &mut self, prompt: &str, retries: u8, _wordlist: &[&str], validator_fn: &mut dyn FnMut(String) -> BoxResult, ) -> Result<()> { let mut line = String::new(); let mut last_error = String::new(); for _ in 0..retries { self.stderr.write_all(prompt.as_bytes())?; self.stderr.flush()?; self.stderr.flush()?; self.stdin.read_line(&mut line)?; if let Err(e) = validator_fn(std::mem::take(&mut line)) { last_error = e.to_string(); self.stderr.write_all(e.to_string().as_bytes())?; self.stderr.flush()?; } else { return Ok(()); } } Err(Error::Validation(retries, last_error)) } fn prompt_validated_passphrase( &mut self, prompt: &str, retries: u8, validator_fn: &mut dyn FnMut(String) -> BoxResult, ) -> Result<()> { let mut line = String::new(); let mut last_error = String::new(); for _ in 0..retries { self.stderr.write_all(prompt.as_bytes())?; self.stderr.flush()?; self.stdin.read_line(&mut line)?; if let Err(e) = validator_fn(std::mem::take(&mut line)) { last_error = e.to_string(); self.stderr.write_all(e.to_string().as_bytes())?; self.stderr.write_all(b"\n")?; self.stderr.flush()?; } else { return Ok(()); } } Err(Error::Validation(retries, last_error)) } }