1. /*
  2. * @(#)TextCallbackHandler.java 1.6 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.security.auth.callback;
  8. /* JAAS imports */
  9. import javax.security.auth.callback.Callback;
  10. import javax.security.auth.callback.CallbackHandler;
  11. import javax.security.auth.callback.ConfirmationCallback;
  12. import javax.security.auth.callback.NameCallback;
  13. import javax.security.auth.callback.PasswordCallback;
  14. import javax.security.auth.callback.TextOutputCallback;
  15. import javax.security.auth.callback.UnsupportedCallbackException;
  16. /* Java imports */
  17. import java.io.BufferedReader;
  18. import java.io.IOException;
  19. import java.io.InputStream;
  20. import java.io.InputStreamReader;
  21. import java.io.PushbackInputStream;
  22. import java.util.Arrays;
  23. /**
  24. * <p>
  25. * Prompts and reads from the command line for answers to authentication
  26. * questions.
  27. * This can be used by a JAAS application to instantiate a
  28. * CallbackHandler
  29. * @see javax.security.auth.callback
  30. */
  31. public class TextCallbackHandler implements CallbackHandler {
  32. /**
  33. * <p>Creates a callback handler that prompts and reads from the
  34. * command line for answers to authentication questions.
  35. * This can be used by JAAS applications to instantiate a
  36. * CallbackHandler.
  37. */
  38. public TextCallbackHandler() { }
  39. /**
  40. * Handles the specified set of callbacks.
  41. *
  42. * @param callbacks the callbacks to handle
  43. * @throws IOException if an input or output error occurs.
  44. * @throws UnsupportedCallbackException if the callback is not an
  45. * instance of NameCallback or PasswordCallback
  46. */
  47. public void handle(Callback[] callbacks)
  48. throws IOException, UnsupportedCallbackException
  49. {
  50. ConfirmationCallback confirmation = null;
  51. for (int i = 0; i < callbacks.length; i++) {
  52. if (callbacks[i] instanceof TextOutputCallback) {
  53. TextOutputCallback tc = (TextOutputCallback) callbacks[i];
  54. String text;
  55. switch (tc.getMessageType()) {
  56. case TextOutputCallback.INFORMATION:
  57. text = "";
  58. break;
  59. case TextOutputCallback.WARNING:
  60. text = "Warning: ";
  61. break;
  62. case TextOutputCallback.ERROR:
  63. text = "Error: ";
  64. break;
  65. default:
  66. throw new UnsupportedCallbackException(
  67. callbacks[i], "Unrecognized message type");
  68. }
  69. String message = tc.getMessage();
  70. if (message != null) {
  71. text += message;
  72. }
  73. if (text != null) {
  74. System.err.println(text);
  75. }
  76. } else if (callbacks[i] instanceof NameCallback) {
  77. NameCallback nc = (NameCallback) callbacks[i];
  78. if (nc.getDefaultName() == null) {
  79. System.err.print(nc.getPrompt());
  80. } else {
  81. System.err.print(nc.getPrompt() +
  82. " [" + nc.getDefaultName() + "] ");
  83. }
  84. System.err.flush();
  85. String result = readLine();
  86. if (result.equals("")) {
  87. result = nc.getDefaultName();
  88. }
  89. nc.setName(result);
  90. } else if (callbacks[i] instanceof PasswordCallback) {
  91. PasswordCallback pc = (PasswordCallback) callbacks[i];
  92. System.err.print(pc.getPrompt());
  93. System.err.flush();
  94. pc.setPassword(readPassword(System.in));
  95. } else if (callbacks[i] instanceof ConfirmationCallback) {
  96. confirmation = (ConfirmationCallback) callbacks[i];
  97. } else {
  98. throw new UnsupportedCallbackException(
  99. callbacks[i], "Unrecognized Callback");
  100. }
  101. }
  102. /* Do the confirmation callback last. */
  103. if (confirmation != null) {
  104. doConfirmation(confirmation);
  105. }
  106. }
  107. /* Reads a line of input */
  108. private String readLine() throws IOException {
  109. return new BufferedReader
  110. (new InputStreamReader(System.in)).readLine();
  111. }
  112. /* Reads a user password from an input stream */
  113. private char[] readPassword(InputStream in) throws IOException {
  114. char[] lineBuffer;
  115. char[] buf;
  116. int i;
  117. buf = lineBuffer = new char[128];
  118. int room = buf.length;
  119. int offset = 0;
  120. int c;
  121. boolean done = false;
  122. while (!done) {
  123. switch (c = in.read()) {
  124. case -1:
  125. case '\n':
  126. done = true;
  127. break;
  128. case '\r':
  129. int c2 = in.read();
  130. if ((c2 != '\n') && (c2 != -1)) {
  131. if (!(in instanceof PushbackInputStream)) {
  132. in = new PushbackInputStream(in);
  133. }
  134. ((PushbackInputStream)in).unread(c2);
  135. } else {
  136. done = true;
  137. break;
  138. }
  139. default:
  140. if (--room < 0) {
  141. buf = new char[offset + 128];
  142. room = buf.length - offset - 1;
  143. System.arraycopy(lineBuffer, 0, buf, 0, offset);
  144. Arrays.fill(lineBuffer, ' ');
  145. lineBuffer = buf;
  146. }
  147. buf[offset++] = (char) c;
  148. break;
  149. }
  150. }
  151. if (offset == 0) {
  152. return null;
  153. }
  154. char[] ret = new char[offset];
  155. System.arraycopy(buf, 0, ret, 0, offset);
  156. Arrays.fill(buf, ' ');
  157. return ret;
  158. }
  159. private void doConfirmation(ConfirmationCallback confirmation)
  160. throws IOException, UnsupportedCallbackException
  161. {
  162. String prefix;
  163. int messageType = confirmation.getMessageType();
  164. switch (messageType) {
  165. case ConfirmationCallback.WARNING:
  166. prefix = "Warning: ";
  167. break;
  168. case ConfirmationCallback.ERROR:
  169. prefix = "Error: ";
  170. break;
  171. case ConfirmationCallback.INFORMATION:
  172. prefix = "";
  173. break;
  174. default:
  175. throw new UnsupportedCallbackException(
  176. confirmation, "Unrecognized message type: " + messageType);
  177. }
  178. class OptionInfo {
  179. String name;
  180. int value;
  181. OptionInfo(String name, int value) {
  182. this.name = name;
  183. this.value = value;
  184. }
  185. }
  186. OptionInfo[] options;
  187. int optionType = confirmation.getOptionType();
  188. switch (optionType) {
  189. case ConfirmationCallback.YES_NO_OPTION:
  190. options = new OptionInfo[] {
  191. new OptionInfo("Yes", ConfirmationCallback.YES),
  192. new OptionInfo("No", ConfirmationCallback.NO)
  193. };
  194. break;
  195. case ConfirmationCallback.YES_NO_CANCEL_OPTION:
  196. options = new OptionInfo[] {
  197. new OptionInfo("Yes", ConfirmationCallback.YES),
  198. new OptionInfo("No", ConfirmationCallback.NO),
  199. new OptionInfo("Cancel", ConfirmationCallback.CANCEL)
  200. };
  201. break;
  202. case ConfirmationCallback.OK_CANCEL_OPTION:
  203. options = new OptionInfo[] {
  204. new OptionInfo("OK", ConfirmationCallback.OK),
  205. new OptionInfo("Cancel", ConfirmationCallback.CANCEL)
  206. };
  207. break;
  208. case ConfirmationCallback.UNSPECIFIED_OPTION:
  209. String[] optionStrings = confirmation.getOptions();
  210. options = new OptionInfo[optionStrings.length];
  211. for (int i = 0; i < options.length; i++) {
  212. options[i].value = i;
  213. }
  214. break;
  215. default:
  216. throw new UnsupportedCallbackException(
  217. confirmation, "Unrecognized option type: " + optionType);
  218. }
  219. int defaultOption = confirmation.getDefaultOption();
  220. String prompt = confirmation.getPrompt();
  221. if (prompt == null) {
  222. prompt = "";
  223. }
  224. prompt = prefix + prompt;
  225. if (!prompt.equals("")) {
  226. System.err.println(prompt);
  227. }
  228. for (int i = 0; i < options.length; i++) {
  229. if (optionType == ConfirmationCallback.UNSPECIFIED_OPTION) {
  230. // defaultOption is an index into the options array
  231. System.err.println(
  232. i + ". " + options[i].name +
  233. (i == defaultOption ? " [default]" : ""));
  234. } else {
  235. // defaultOption is an option value
  236. System.err.println(
  237. i + ". " + options[i].name +
  238. (options[i].value == defaultOption ? " [default]" : ""));
  239. }
  240. }
  241. System.err.print("Enter a number: ");
  242. System.err.flush();
  243. int result;
  244. try {
  245. result = Integer.parseInt(readLine());
  246. if (result < 0 || result > (options.length - 1)) {
  247. result = defaultOption;
  248. }
  249. result = options[result].value;
  250. } catch (NumberFormatException e) {
  251. result = defaultOption;
  252. }
  253. confirmation.setSelectedIndex(result);
  254. }
  255. }