libspmg 0.2.1

Secure password manager library
Documentation
use std::process::{ Command, Output, Stdio };
use std::io::{self, Write, BufRead, BufReader};
use std::io::ErrorKind;

use std::fs;

pub struct PinEntry;

static prompt_commands: &str = r#"SETDESC {}
GETPIN"#;


impl PinEntry {
    pub fn pinentry_prompt(
        tty_path: &str,
        commands: Vec<(&str, Vec<&str>)>
    ) -> io::Result<String> {
        let mut child = Command::new("pinentry")
            .arg("--ttyname")
            .arg(tty_path)
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .spawn()?;

        {
            let mut stdin = child.stdin.as_mut().unwrap();
            for (template, values) in commands {
                let line = match values.len() {
                    0 => template.to_string(),
                    1 => template.replace("{}", values[0]),
                    2 => template.replace("{}", values[0]).replacen("{}", values[1], 1),
                    3 => template.replace("{}", values[0])
                                 .replacen("{}", values[1], 1)
                                 .replacen("{}", values[2], 1),
                    _ => return Err(io::Error::new(
                        io::ErrorKind::InvalidInput,
                        "Too many format arguments (max 3 supported)",
                    )),
                };
                writeln!(stdin, "{}", line)?;
            }
            writeln!(stdin, "BYE")?;
        }

        let stdout = child.stdout.take().unwrap();
        let reader = BufReader::new(stdout);
        for line in reader.lines() {
            let line = line?;
            if line.starts_with("D ") {
                return Ok(line[2..].to_string());
            } else if line.starts_with("ERR") {
                return Err(io::Error::new(io::ErrorKind::Other, line));
            }
        }

        Err(io::Error::new(io::ErrorKind::Other, "No data received from pinentry"))
    }

    pub fn prompt(desc: &str) -> io::Result<String> {
        let commands = vec![
            ("SETDESC {}", vec![desc]),
            ("SETPROMPT Password:", vec![]),
            ("GETPIN", vec![]),
        ];

        PinEntry::pinentry_prompt("/dev/tty", commands)
    }

    pub fn prompt_repeat(desc: &str) -> io::Result<String> {
        let commands = vec![
            ("SETDESC {}", vec![desc]),
            ("SETPROMPT New password:", vec![]),
            ("SETREPEATERROR does not match - try again", vec![]),
            ("SETREPEATOK Passwords match.", vec![]),
            ("SETREPEAT Repeat: ", vec![]),
            ("GETPIN", vec![]),
        ];

        PinEntry::pinentry_prompt("/dev/tty", commands)
    }
    
}