1 /* 2 * Copyright (c) 2000, 2009, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.security.auth.callback; 27 28 /* JAAS imports */ 29 import javax.security.auth.callback.Callback; 30 import javax.security.auth.callback.CallbackHandler; 31 import javax.security.auth.callback.ConfirmationCallback; 32 import javax.security.auth.callback.NameCallback; 33 import javax.security.auth.callback.PasswordCallback; 34 import javax.security.auth.callback.TextOutputCallback; 35 import javax.security.auth.callback.UnsupportedCallbackException; 36 37 /* Java imports */ 38 import java.io.BufferedReader; 39 import java.io.IOException; 40 import java.io.InputStream; 41 import java.io.InputStreamReader; 42 import java.io.PushbackInputStream; 43 import java.util.Arrays; 44 45 import sun.security.util.Password; 46 47 /** 48 * <p> 49 * Prompts and reads from the command line for answers to authentication 50 * questions. 51 * This can be used by a JAAS application to instantiate a 52 * CallbackHandler 53 * @see javax.security.auth.callback 54 */ 55 56 @jdk.Supported 57 public class TextCallbackHandler implements CallbackHandler { 58 59 /** 60 * <p>Creates a callback handler that prompts and reads from the 61 * command line for answers to authentication questions. 62 * This can be used by JAAS applications to instantiate a 63 * CallbackHandler. 64 65 */ 66 public TextCallbackHandler() { } 67 68 /** 69 * Handles the specified set of callbacks. 70 * 71 * @param callbacks the callbacks to handle 72 * @throws IOException if an input or output error occurs. 73 * @throws UnsupportedCallbackException if the callback is not an 74 * instance of NameCallback or PasswordCallback 75 */ 76 public void handle(Callback[] callbacks) 77 throws IOException, UnsupportedCallbackException 78 { 79 ConfirmationCallback confirmation = null; 80 81 for (int i = 0; i < callbacks.length; i++) { 82 if (callbacks[i] instanceof TextOutputCallback) { 83 TextOutputCallback tc = (TextOutputCallback) callbacks[i]; 84 85 String text; 86 switch (tc.getMessageType()) { 87 case TextOutputCallback.INFORMATION: 88 text = ""; 89 break; 90 case TextOutputCallback.WARNING: 91 text = "Warning: "; 92 break; 93 case TextOutputCallback.ERROR: 94 text = "Error: "; 95 break; 96 default: 97 throw new UnsupportedCallbackException( 98 callbacks[i], "Unrecognized message type"); 99 } 100 101 String message = tc.getMessage(); 102 if (message != null) { 103 text += message; 104 } 105 if (text != null) { 106 System.err.println(text); 107 } 108 109 } else if (callbacks[i] instanceof NameCallback) { 110 NameCallback nc = (NameCallback) callbacks[i]; 111 112 if (nc.getDefaultName() == null) { 113 System.err.print(nc.getPrompt()); 114 } else { 115 System.err.print(nc.getPrompt() + 116 " [" + nc.getDefaultName() + "] "); 117 } 118 System.err.flush(); 119 120 String result = readLine(); 121 if (result.equals("")) { 122 result = nc.getDefaultName(); 123 } 124 125 nc.setName(result); 126 127 } else if (callbacks[i] instanceof PasswordCallback) { 128 PasswordCallback pc = (PasswordCallback) callbacks[i]; 129 130 System.err.print(pc.getPrompt()); 131 System.err.flush(); 132 133 pc.setPassword(Password.readPassword(System.in, pc.isEchoOn())); 134 135 } else if (callbacks[i] instanceof ConfirmationCallback) { 136 confirmation = (ConfirmationCallback) callbacks[i]; 137 138 } else { 139 throw new UnsupportedCallbackException( 140 callbacks[i], "Unrecognized Callback"); 141 } 142 } 143 144 /* Do the confirmation callback last. */ 145 if (confirmation != null) { 146 doConfirmation(confirmation); 147 } 148 } 149 150 /* Reads a line of input */ 151 private String readLine() throws IOException { 152 String result = new BufferedReader 153 (new InputStreamReader(System.in)).readLine(); 154 if (result == null) { 155 throw new IOException("Cannot read from System.in"); 156 } 157 return result; 158 } 159 160 private void doConfirmation(ConfirmationCallback confirmation) 161 throws IOException, UnsupportedCallbackException 162 { 163 String prefix; 164 int messageType = confirmation.getMessageType(); 165 switch (messageType) { 166 case ConfirmationCallback.WARNING: 167 prefix = "Warning: "; 168 break; 169 case ConfirmationCallback.ERROR: 170 prefix = "Error: "; 171 break; 172 case ConfirmationCallback.INFORMATION: 173 prefix = ""; 174 break; 175 default: 176 throw new UnsupportedCallbackException( 177 confirmation, "Unrecognized message type: " + messageType); 178 } 179 180 class OptionInfo { 181 String name; 182 int value; 183 OptionInfo(String name, int value) { 184 this.name = name; 185 this.value = value; 186 } 187 } 188 189 OptionInfo[] options; 190 int optionType = confirmation.getOptionType(); 191 switch (optionType) { 192 case ConfirmationCallback.YES_NO_OPTION: 193 options = new OptionInfo[] { 194 new OptionInfo("Yes", ConfirmationCallback.YES), 195 new OptionInfo("No", ConfirmationCallback.NO) 196 }; 197 break; 198 case ConfirmationCallback.YES_NO_CANCEL_OPTION: 199 options = new OptionInfo[] { 200 new OptionInfo("Yes", ConfirmationCallback.YES), 201 new OptionInfo("No", ConfirmationCallback.NO), 202 new OptionInfo("Cancel", ConfirmationCallback.CANCEL) 203 }; 204 break; 205 case ConfirmationCallback.OK_CANCEL_OPTION: 206 options = new OptionInfo[] { 207 new OptionInfo("OK", ConfirmationCallback.OK), 208 new OptionInfo("Cancel", ConfirmationCallback.CANCEL) 209 }; 210 break; 211 case ConfirmationCallback.UNSPECIFIED_OPTION: 212 String[] optionStrings = confirmation.getOptions(); 213 options = new OptionInfo[optionStrings.length]; 214 for (int i = 0; i < options.length; i++) { 215 options[i] = new OptionInfo(optionStrings[i], i); 216 } 217 break; 218 default: 219 throw new UnsupportedCallbackException( 220 confirmation, "Unrecognized option type: " + optionType); 221 } 222 223 int defaultOption = confirmation.getDefaultOption(); 224 225 String prompt = confirmation.getPrompt(); 226 if (prompt == null) { 227 prompt = ""; 228 } 229 prompt = prefix + prompt; 230 if (!prompt.equals("")) { 231 System.err.println(prompt); 232 } 233 234 for (int i = 0; i < options.length; i++) { 235 if (optionType == ConfirmationCallback.UNSPECIFIED_OPTION) { 236 // defaultOption is an index into the options array 237 System.err.println( 238 i + ". " + options[i].name + 239 (i == defaultOption ? " [default]" : "")); 240 } else { 241 // defaultOption is an option value 242 System.err.println( 243 i + ". " + options[i].name + 244 (options[i].value == defaultOption ? " [default]" : "")); 245 } 246 } 247 System.err.print("Enter a number: "); 248 System.err.flush(); 249 int result; 250 try { 251 result = Integer.parseInt(readLine()); 252 if (result < 0 || result > (options.length - 1)) { 253 result = defaultOption; 254 } 255 result = options[result].value; 256 } catch (NumberFormatException e) { 257 result = defaultOption; 258 } 259 260 confirmation.setSelectedIndex(result); 261 } 262 }