From 9cfe1863917300f7ff3805ef07b7e02eb9a6d73e Mon Sep 17 00:00:00 2001 From: Conrad Kleinespel Date: Mon, 24 Jul 2017 20:48:36 +0000 Subject: [PATCH] bugfix(unix): piped input did not read correctly --- Cargo.toml | 2 +- examples/example.rs | 8 ++-- src/lib.rs | 91 +++++++++++++++++++++++++-------------------- 3 files changed, 55 insertions(+), 46 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 360834c..535e1bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rpassword" -version = "0.4.1" +version = "0.4.2" authors = ["Conrad Kleinespel "] description = "Read passwords in console applications." license = "Apache-2.0" diff --git a/examples/example.rs b/examples/example.rs index af3b6b6..f327ac0 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -1,17 +1,15 @@ 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); @@ -19,9 +17,11 @@ fn main() { 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); } diff --git a/src/lib.rs b/src/lib.rs index 265b39d..926ea9b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; @@ -80,45 +81,53 @@ mod unix { } fn read_input(hide: bool) -> IoResult { - // 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(_) => {}, @@ -223,8 +232,8 @@ pub use windows::read_password; pub fn prompt_response_stdout(prompt: &str) -> std::io::Result { let mut stdout = std::io::stdout(); - try!(write!(stdout, "{}", prompt)); - try!(stdout.flush()); + write!(stdout, "{}", prompt)?; + stdout.flush()?; read_response() } @@ -232,8 +241,8 @@ pub fn prompt_response_stdout(prompt: &str) -> std::io::Result { pub fn prompt_response_stderr(prompt: &str) -> std::io::Result { let mut stderr = std::io::stderr(); - try!(write!(stderr, "{}", prompt)); - try!(stderr.flush()); + write!(stderr, "{}", prompt)?; + stderr.flush()?; read_response() } @@ -241,8 +250,8 @@ pub fn prompt_response_stderr(prompt: &str) -> std::io::Result { pub fn prompt_password_stdout(prompt: &str) -> std::io::Result { let mut stdout = std::io::stdout(); - try!(write!(stdout, "{}", prompt)); - try!(stdout.flush()); + write!(stdout, "{}", prompt)?; + stdout.flush()?; read_password() } @@ -250,7 +259,7 @@ pub fn prompt_password_stdout(prompt: &str) -> std::io::Result { pub fn prompt_password_stderr(prompt: &str) -> std::io::Result { let mut stderr = std::io::stderr(); - try!(write!(stderr, "{}", prompt)); - try!(stderr.flush()); + write!(stderr, "{}", prompt)?; + stderr.flush()?; read_password() }