Skip to content

Commit

Permalink
Skip calling Windows console api with piped stdin (conradkleinespel#51)
Browse files Browse the repository at this point in the history
* skip calling windows console api with piped stdin

* test with piped stdin

* upgrade appveyor's rustc to 1.44.0
  • Loading branch information
Heliozoa committed Jun 18, 2020
1 parent 8f6b475 commit ed90b6c
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 15 deletions.
4 changes: 2 additions & 2 deletions appveyor.yml
Expand Up @@ -3,8 +3,8 @@ environment:
- TARGET: x86_64-pc-windows-gnu
- TARGET: i686-pc-windows-gnu
install:
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-1.33.0-${env:TARGET}.exe"
- rust-1.33.0-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-1.44.0-${env:TARGET}.exe"
- rust-1.44.0-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
- SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin
- SET PATH=%PATH%;C:\MinGW\bin
- rustc -V
Expand Down
33 changes: 20 additions & 13 deletions src/lib.rs
Expand Up @@ -151,9 +151,9 @@ mod windows {
use winapi::um::winnt::{
GENERIC_READ, GENERIC_WRITE, FILE_SHARE_READ, FILE_SHARE_WRITE,
};
use winapi::um::fileapi::{CreateFileA, OPEN_EXISTING};
use winapi::um::fileapi::{CreateFileA, GetFileType, OPEN_EXISTING};
use winapi::um::processenv::GetStdHandle;
use winapi::um::winbase::STD_INPUT_HANDLE;
use winapi::um::winbase::{FILE_TYPE_PIPE, STD_INPUT_HANDLE};
use winapi::um::handleapi::INVALID_HANDLE_VALUE;
use winapi::um::consoleapi::{GetConsoleMode, SetConsoleMode};
use winapi::shared::minwindef::LPDWORD;
Expand Down Expand Up @@ -181,16 +181,21 @@ mod windows {
return Err(::std::io::Error::last_os_error());
}

// Get the old mode so we can reset back to it when we are done
let mut mode = 0;
if unsafe { GetConsoleMode(handle, &mut mode as LPDWORD) } == 0 {
return Err(::std::io::Error::last_os_error());
}

// We want to be able to read line by line, and we still want backspace to work
let new_mode_flags = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
if unsafe { SetConsoleMode(handle, new_mode_flags) } == 0 {
return Err(::std::io::Error::last_os_error());
// Console mode does not apply when stdin is piped
let handle_type = unsafe { GetFileType(handle) };
if handle_type != FILE_TYPE_PIPE {
// Get the old mode so we can reset back to it when we are done
if unsafe { GetConsoleMode(handle, &mut mode as LPDWORD) } == 0 {
return Err(::std::io::Error::last_os_error());
}

// We want to be able to read line by line, and we still want backspace to work
let new_mode_flags = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
if unsafe { SetConsoleMode(handle, new_mode_flags) } == 0 {
return Err(::std::io::Error::last_os_error());
}
}

// Read the password.
Expand All @@ -201,9 +206,11 @@ mod windows {
// Check the response.
let _ = input?;

// Set the the mode back to normal
if unsafe { SetConsoleMode(handle, mode) } == 0 {
return Err(::std::io::Error::last_os_error());
if handle_type != FILE_TYPE_PIPE {
// Set the the mode back to normal
if unsafe { SetConsoleMode(handle, mode) } == 0 {
return Err(::std::io::Error::last_os_error());
}
}

super::fixes_newline(&mut password);
Expand Down
35 changes: 35 additions & 0 deletions tests/piped.rs
@@ -0,0 +1,35 @@
//! This test checks that piped input is handled correctly.

use std::env;
use std::io::Write;
use std::process::{Command, Stdio};

#[test]
fn piped_password() {
// Find target directory
let target_dir = env::current_exe()
.unwrap()
.parent()
.unwrap()
.parent()
.unwrap()
.to_path_buf();

// Run an example that reads a password and prints it
let mut out = Command::new(target_dir.join("examples/read-password"))
.stdout(Stdio::piped())
.stdin(Stdio::piped())
.spawn()
.unwrap();

// Write "secret" as the password into stdin
let stdin = out.stdin.as_mut().unwrap();
stdin.write_all("secret".as_bytes()).unwrap();

let out = out.wait_with_output().unwrap();
assert!(out.status.success());

let stdout = String::from_utf8(out.stdout).unwrap();
println!("stdout: {}", stdout);
assert!(stdout.ends_with("Password: secret\n"));
}

0 comments on commit ed90b6c

Please sign in to comment.