Skip to content

Commit

Permalink
bugfix(unix): piped input did not read correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
conradkleinespel committed Jul 24, 2017
1 parent 320cdd8 commit 9cfe186
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 46 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]

name = "rpassword"
version = "0.4.1"
version = "0.4.2"
authors = ["Conrad Kleinespel <conradk@conradk.com>"]
description = "Read passwords in console applications."
license = "Apache-2.0"
Expand Down
8 changes: 4 additions & 4 deletions examples/example.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
extern crate rpassword;

use std::io::{stdout, Write};

fn main() {
let mut stdout = stdout();

// Password without prompt
let pass = rpassword::read_password().unwrap();
println!("Your password is {}", pass);

// Prompt for password on stdout
let pass = rpassword::prompt_password_stdout("Password with prompt on stdout: ").unwrap();
println!("Your password is {}", pass);

// Prompt for password on stderr
let pass = rpassword::prompt_password_stderr("Password with prompt on stderr: ").unwrap();
println!("Your password is {}", pass);

// Password (displayed, not hidden) without prompt
let pass = rpassword::read_response().unwrap();
println!("Your password is {}", pass);

// Prompt for password (displayed, not hidden)
let pass = rpassword::prompt_response_stdout("Password (displayed, not hidden) with prompt on stdout: ").unwrap();
println!("Your password is {}", pass);

// Prompt for password (displayed, not hidden)
let pass = rpassword::prompt_response_stderr("Password (displayed, not hidden) with prompt on stderr: ").unwrap();
println!("Your password is {}", pass);
}
91 changes: 50 additions & 41 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ mod unix {
extern crate libc;

use self::libc::STDIN_FILENO;
use self::libc::isatty;
use std::io::{ Error, ErrorKind };
use std::io::Result as IoResult;
use std::ptr;
Expand Down Expand Up @@ -80,45 +81,53 @@ mod unix {
}

fn read_input(hide: bool) -> IoResult<String> {
// Make two copies of the terminal settings. The first one will be modified
// and the second one will act as a backup for when we want to set the
// terminal back to its original state.
let mut term = try!(termios::Termios::from_fd(STDIN_FILENO));
let term_orig = term;

if hide {
// Hide the password. This is what makes this function useful.
term.c_lflag &= !termios::ECHO;
}

// But don't hide the NL character when the user hits ENTER.
term.c_lflag |= termios::ECHONL;

// Save the settings for now.
try!(termios::tcsetattr(STDIN_FILENO, termios::TCSANOW, &term));

// Read the password.
let mut password = String::new();
match get_reader().read_line(&mut password) {
Ok(_) => { },
Err(err) => {
// Reset the terminal and quit.
try!(termios::tcsetattr(STDIN_FILENO, termios::TCSANOW, &term_orig));

// Return the original IoError.
return Err(err);

let input_is_piped = unsafe { isatty(0) } == 0;
// When output is piped, the termios functions don't work
if input_is_piped {
get_reader().read_line(&mut password)?;
} else {
// Make two copies of the terminal settings. The first one will be modified
// and the second one will act as a backup for when we want to set the
// terminal back to its original state.
let mut term = termios::Termios::from_fd(STDIN_FILENO)?;
let term_orig = term;

if hide {
// Hide the password. This is what makes this function useful.
term.c_lflag &= !termios::ECHO;
}
};

// Reset the terminal and quit.
match termios::tcsetattr(STDIN_FILENO, termios::TCSANOW, &term_orig) {
Ok(_) => {},
Err(err) => {
unsafe { password.as_mut_vec() }.set_memory(0);
return Err(err);
// But don't hide the NL character when the user hits ENTER.
term.c_lflag |= termios::ECHONL;

// Save the settings for now.
termios::tcsetattr(STDIN_FILENO, termios::TCSANOW, &term)?;

// Read the password.
match get_reader().read_line(&mut password) {
Ok(_) => { },
Err(err) => {
// Reset the terminal and quit.
termios::tcsetattr(STDIN_FILENO, termios::TCSANOW, &term_orig)?;

// Return the original IoError.
return Err(err);
}
};

// Reset the terminal and quit.
match termios::tcsetattr(STDIN_FILENO, termios::TCSANOW, &term_orig) {
Ok(_) => {},
Err(err) => {
unsafe { password.as_mut_vec() }.set_memory(0);
return Err(err);
}
}
}


// Remove the \n from the line.
match password.pop() {
Some(_) => {},
Expand Down Expand Up @@ -223,34 +232,34 @@ pub use windows::read_password;
pub fn prompt_response_stdout(prompt: &str) -> std::io::Result<String> {
let mut stdout = std::io::stdout();

try!(write!(stdout, "{}", prompt));
try!(stdout.flush());
write!(stdout, "{}", prompt)?;
stdout.flush()?;
read_response()
}

/// Prompts for a password on STDERR and reads it from STDIN.
pub fn prompt_response_stderr(prompt: &str) -> std::io::Result<String> {
let mut stderr = std::io::stderr();

try!(write!(stderr, "{}", prompt));
try!(stderr.flush());
write!(stderr, "{}", prompt)?;
stderr.flush()?;
read_response()
}

/// Prompts for a password on STDOUT and reads it from STDIN.
pub fn prompt_password_stdout(prompt: &str) -> std::io::Result<String> {
let mut stdout = std::io::stdout();

try!(write!(stdout, "{}", prompt));
try!(stdout.flush());
write!(stdout, "{}", prompt)?;
stdout.flush()?;
read_password()
}

/// Prompts for a password on STDERR and reads it from STDIN.
pub fn prompt_password_stderr(prompt: &str) -> std::io::Result<String> {
let mut stderr = std::io::stderr();

try!(write!(stderr, "{}", prompt));
try!(stderr.flush());
write!(stderr, "{}", prompt)?;
stderr.flush()?;
read_password()
}

0 comments on commit 9cfe186

Please sign in to comment.