aboutsummaryrefslogtreecommitdiffstats
path: root/src/pinentry.rs
blob: ff778e7a5eb6337a41fa72c93fcdd8f4bc4fe120 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
use crate::prelude::*;

use tokio::io::AsyncWriteExt as _;

pub async fn getpin(
    prompt: &str,
    desc: &str,
    tty: Option<&str>,
) -> Result<String> {
    let mut opts = tokio::process::Command::new("pinentry");
    let opts = opts
        .stdin(std::process::Stdio::piped())
        .stdout(std::process::Stdio::piped());
    let opts = if let Some(tty) = tty {
        opts.args(&["-T", tty])
    } else {
        opts
    };
    let mut child = opts.spawn().context(crate::error::Spawn)?;
    {
        let stdin = child.stdin.as_mut().unwrap();

        stdin
            .write_all(b"SETTITLE rbw\n")
            .await
            .context(crate::error::WriteStdin)?;
        stdin
            .write_all(format!("SETPROMPT {}\n", prompt).as_bytes())
            .await
            .context(crate::error::WriteStdin)?;
        stdin
            .write_all(format!("SETDESC {}\n", desc).as_bytes())
            .await
            .context(crate::error::WriteStdin)?;
        stdin
            .write_all(b"GETPIN\n")
            .await
            .context(crate::error::WriteStdin)?;
    }

    let out = child
        .wait_with_output()
        .await
        .context(crate::error::ProcessWaitOutput)?
        .stdout;
    let out_str = String::from_utf8(out.clone()).context(
        crate::error::FailedToParsePinentryUtf8 { out: out.clone() },
    )?;
    for line in out_str.lines() {
        if line.starts_with("D ") {
            return Ok(line[2..line.len()].to_string());
        } else if !line.starts_with("OK") {
            break;
        }
    }

    Err(Error::FailedToParsePinentry { out })
}