/**
* Copyright (c) Members of the EGEE Collaboration. 2006-2009.
* See http://www.eu-egee.org/partners/ for details on the copyright holders.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.glite.authz.pap.ui.cli;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.util.Arrays;
/**
* PasswordReader reads a password and masks the typed inputs.
*
* See article: Password Masking in the Java Programming Language
* http://java.sun.com/developer/technicalArticles/Security/pwordmask/
*
* Usage:
*
* <pre>
* char[] password = PasswordReader.getPassword(System.in, "Password: ");
* </pre>
*
* @author Valery Tschopp <tschopp@switch.ch>
*
*/
public class PasswordReader implements Runnable {
/**
*
* @param in
* @param prompt
* @return
* @throws IOException
*/
public static final char[] getPassword(InputStream in, String prompt) throws IOException {
PasswordReader maskingThread = new PasswordReader(prompt);
Thread thread = new Thread(maskingThread);
thread.start();
char[] lineBuffer;
char[] buf;
buf = lineBuffer = new char[128];
int room = buf.length;
int offset = 0;
int c;
loop: while (true) {
switch (c = in.read()) {
case -1:
case '\n':
break loop;
case '\r':
int c2 = in.read();
if ((c2 != '\n') && (c2 != -1)) {
if (!(in instanceof PushbackInputStream)) {
in = new PushbackInputStream(in);
}
((PushbackInputStream) in).unread(c2);
} else {
break loop;
}
default:
if (--room < 0) {
buf = new char[offset + 128];
room = buf.length - offset - 1;
System.arraycopy(lineBuffer, 0, buf, 0, offset);
Arrays.fill(lineBuffer, ' ');
lineBuffer = buf;
}
buf[offset++] = (char) c;
break;
}
}
maskingThread.stopMasking();
if (offset == 0) {
return null;
}
char[] ret = new char[offset];
System.arraycopy(buf, 0, ret, 0, offset);
Arrays.fill(buf, ' ');
return ret;
}
private volatile boolean masking;
private String prompt;
/**
* @param prompt The prompt displayed to the user
*/
private PasswordReader(String prompt) {
this.prompt = prompt;
}
/**
* Begin masking until asked to stop.
*/
public void run() {
int priority = Thread.currentThread().getPriority();
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
try {
masking = true;
while (masking) {
System.out.print("\010 \015" + prompt);
try {
// attempt masking at this rate
Thread.sleep(1);
} catch (InterruptedException iex) {
Thread.currentThread().interrupt();
return;
}
}
} finally { // restore the original priority
Thread.currentThread().setPriority(priority);
}
}
/**
* Instruct the thread to stop masking.
*/
private void stopMasking() {
this.masking = false;
}
/**
* Test drive
*
* @param args
*/
public static void main(String[] args) {
try {
char[] password = PasswordReader.getPassword(System.in, "Password: ");
if (password != null) {
System.out.println("Password entered: " + String.valueOf(password));
} else {
System.out.println("Empty password entered.");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}