| | |
| | | |
| | | |
| | | import static com.sun.opends.sdk.messages.Messages.*; |
| | | import static com.sun.opends.sdk.util.StaticUtils.*; |
| | | import static com.sun.opends.sdk.util.StaticUtils.EOL; |
| | | |
| | | import java.io.File; |
| | | import java.io.FileInputStream; |
| | |
| | | import org.opends.sdk.controls.*; |
| | | import org.opends.sdk.responses.BindResult; |
| | | |
| | | import com.sun.opends.sdk.controls.AccountUsabilityRequestControl; |
| | | import com.sun.opends.sdk.tools.AuthenticatedConnectionFactory.AuthenticatedConnection; |
| | | import com.sun.opends.sdk.util.StaticUtils; |
| | | |
| | |
| | | */ |
| | | final class Utils |
| | | { |
| | | // Prevent instantiation. |
| | | private Utils() |
| | | { |
| | | // Do nothing. |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * The name of a command-line script used to launch a tool. |
| | | */ |
| | | static final String PROPERTY_SCRIPT_NAME = "com.sun.opends.sdk.tools.scriptName"; |
| | | |
| | | /** |
| | | * The column at which to wrap long lines of output in the |
| | | * command-line tools. |
| | | * The column at which to wrap long lines of output in the command-line tools. |
| | | */ |
| | | static final int MAX_LINE_WIDTH; |
| | | |
| | | static |
| | | { |
| | | int columns = 80; |
| | | try |
| | | { |
| | | String s = System.getenv("COLUMNS"); |
| | | final String s = System.getenv("COLUMNS"); |
| | | if (s != null) |
| | | { |
| | | columns = Integer.parseInt(s); |
| | | } |
| | | } |
| | | catch (Exception e) |
| | | catch (final Exception e) |
| | | { |
| | | // Do nothing. |
| | | } |
| | |
| | | |
| | | |
| | | /** |
| | | * Inserts line breaks into the provided buffer to wrap text at no |
| | | * more than the specified column width. Wrapping will only be done at |
| | | * space boundaries and if there are no spaces within the specified |
| | | * width, then wrapping will be performed at the first space after the |
| | | * specified column. |
| | | * Filters the provided value to ensure that it is appropriate for use as an |
| | | * exit code. Exit code values are generally only allowed to be between 0 and |
| | | * 255, so any value outside of this range will be converted to 255, which is |
| | | * the typical exit code used to indicate an overflow value. |
| | | * |
| | | * @param exitCode |
| | | * The exit code value to be processed. |
| | | * @return An integer value between 0 and 255, inclusive. If the provided exit |
| | | * code was already between 0 and 255, then the original value will be |
| | | * returned. If the provided value was out of this range, then 255 |
| | | * will be returned. |
| | | */ |
| | | static int filterExitCode(final int exitCode) |
| | | { |
| | | if (exitCode < 0) |
| | | { |
| | | return 255; |
| | | } |
| | | else if (exitCode > 255) |
| | | { |
| | | return 255; |
| | | } |
| | | else |
| | | { |
| | | return exitCode; |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Parse the specified command line argument to create the appropriate |
| | | * LDAPControl. The argument string should be in the format |
| | | * controloid[:criticality[:value|::b64value|:<fileurl]] |
| | | * |
| | | * @param argString |
| | | * The argument string containing the encoded control information. |
| | | * @return The control decoded from the provided string, or <CODE>null</CODE> |
| | | * if an error occurs while parsing the argument value. |
| | | * @throws org.opends.sdk.DecodeException |
| | | * If an error occurs. |
| | | */ |
| | | static GenericControl getControl(final String argString) |
| | | throws DecodeException |
| | | { |
| | | String controlOID = null; |
| | | boolean controlCriticality = false; |
| | | ByteString controlValue = null; |
| | | |
| | | int idx = argString.indexOf(":"); |
| | | |
| | | if (idx < 0) |
| | | { |
| | | controlOID = argString; |
| | | } |
| | | else |
| | | { |
| | | controlOID = argString.substring(0, idx); |
| | | } |
| | | |
| | | final String lowerOID = StaticUtils.toLowerCase(controlOID); |
| | | if (lowerOID.equals("accountusable") || lowerOID.equals("accountusability")) |
| | | { |
| | | controlOID = AccountUsabilityRequestControl.OID; |
| | | } |
| | | else if (lowerOID.equals("authzid") |
| | | || lowerOID.equals("authorizationidentity")) |
| | | { |
| | | controlOID = AuthorizationIdentityRequestControl.OID; |
| | | } |
| | | else if (lowerOID.equals("noop") || lowerOID.equals("no-op")) |
| | | { |
| | | // controlOID = OID_LDAP_NOOP_OPENLDAP_ASSIGNED; |
| | | } |
| | | else if (lowerOID.equals("subentries")) |
| | | { |
| | | // controlOID = OID_LDAP_SUBENTRIES; |
| | | } |
| | | else if (lowerOID.equals("managedsait")) |
| | | { |
| | | // controlOID = OID_MANAGE_DSAIT_CONTROL; |
| | | } |
| | | else if (lowerOID.equals("pwpolicy") || lowerOID.equals("passwordpolicy")) |
| | | { |
| | | controlOID = PasswordPolicyRequestControl.OID; |
| | | } |
| | | else if (lowerOID.equals("subtreedelete") || lowerOID.equals("treedelete")) |
| | | { |
| | | controlOID = SubtreeDeleteRequestControl.OID; |
| | | } |
| | | else if (lowerOID.equals("realattrsonly") |
| | | || lowerOID.equals("realattributesonly")) |
| | | { |
| | | // controlOID = OID_REAL_ATTRS_ONLY; |
| | | } |
| | | else if (lowerOID.equals("virtualattrsonly") |
| | | || lowerOID.equals("virtualattributesonly")) |
| | | { |
| | | // controlOID = OID_VIRTUAL_ATTRS_ONLY; |
| | | } |
| | | else if (lowerOID.equals("effectiverights") |
| | | || lowerOID.equals("geteffectiverights")) |
| | | { |
| | | controlOID = GetEffectiveRightsRequestControl.OID; |
| | | } |
| | | |
| | | if (idx < 0) |
| | | { |
| | | return GenericControl.newControl(controlOID); |
| | | } |
| | | |
| | | final String remainder = argString.substring(idx + 1, argString.length()); |
| | | |
| | | idx = remainder.indexOf(":"); |
| | | if (idx == -1) |
| | | { |
| | | if (remainder.equalsIgnoreCase("true")) |
| | | { |
| | | controlCriticality = true; |
| | | } |
| | | else if (remainder.equalsIgnoreCase("false")) |
| | | { |
| | | controlCriticality = false; |
| | | } |
| | | else |
| | | { |
| | | // TODO: I18N |
| | | throw DecodeException.error(LocalizableMessage |
| | | .raw("Invalid format for criticality value:" + remainder)); |
| | | } |
| | | return GenericControl.newControl(controlOID, controlCriticality); |
| | | |
| | | } |
| | | |
| | | final String critical = remainder.substring(0, idx); |
| | | if (critical.equalsIgnoreCase("true")) |
| | | { |
| | | controlCriticality = true; |
| | | } |
| | | else if (critical.equalsIgnoreCase("false")) |
| | | { |
| | | controlCriticality = false; |
| | | } |
| | | else |
| | | { |
| | | // TODO: I18N |
| | | throw DecodeException.error(LocalizableMessage |
| | | .raw("Invalid format for criticality value:" + critical)); |
| | | } |
| | | |
| | | final String valString = remainder.substring(idx + 1, remainder.length()); |
| | | if (valString.charAt(0) == ':') |
| | | { |
| | | controlValue = ByteString.valueOf(valString.substring(1, valString |
| | | .length())); |
| | | } |
| | | else if (valString.charAt(0) == '<') |
| | | { |
| | | // Read data from the file. |
| | | final String filePath = valString.substring(1, valString.length()); |
| | | try |
| | | { |
| | | final byte[] val = readBytesFromFile(filePath); |
| | | controlValue = ByteString.wrap(val); |
| | | } |
| | | catch (final Exception e) |
| | | { |
| | | return null; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | controlValue = ByteString.valueOf(valString); |
| | | } |
| | | |
| | | return GenericControl.newControl(controlOID, controlCriticality, |
| | | controlValue); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Prints a multi-line error message with the provided information to the |
| | | * given print stream. |
| | | * |
| | | * @param app |
| | | * The console app to use to write the error message. |
| | | * @param ere |
| | | * The error result. |
| | | * @return The error code. |
| | | */ |
| | | static int printErrorMessage(final ConsoleApplication app, |
| | | final ErrorResultException ere) |
| | | { |
| | | // if ((ere.getMessage() != null) && (ere.getMessage().length() > |
| | | // 0)) |
| | | // { |
| | | // app.println(LocalizableMessage.raw(ere.getMessage())); |
| | | // } |
| | | |
| | | if (ere.getResult().getResultCode().intValue() >= 0) |
| | | { |
| | | app.println(ERR_TOOL_RESULT_CODE.get(ere.getResult().getResultCode() |
| | | .intValue(), ere.getResult().getResultCode().toString())); |
| | | } |
| | | |
| | | if ((ere.getResult().getDiagnosticMessage() != null) |
| | | && (ere.getResult().getDiagnosticMessage().length() > 0)) |
| | | { |
| | | app.println(ERR_TOOL_ERROR_MESSAGE.get(ere.getResult() |
| | | .getDiagnosticMessage())); |
| | | } |
| | | |
| | | if (ere.getResult().getMatchedDN() != null |
| | | && ere.getResult().getMatchedDN().length() > 0) |
| | | { |
| | | app.println(ERR_TOOL_MATCHED_DN.get(ere.getResult().getMatchedDN())); |
| | | } |
| | | |
| | | if (app.isVerbose() && ere.getResult().getCause() != null) |
| | | { |
| | | ere.getResult().getCause().printStackTrace(app.getErrorStream()); |
| | | } |
| | | |
| | | return ere.getResult().getResultCode().intValue(); |
| | | } |
| | | |
| | | |
| | | |
| | | static void printPasswordPolicyResults(final ConsoleApplication app, |
| | | final Connection connection) |
| | | { |
| | | if (connection instanceof AuthenticatedConnection) |
| | | { |
| | | final AuthenticatedConnection conn = (AuthenticatedConnection) connection; |
| | | final BindResult result = conn.getAuthenticatedBindResult(); |
| | | |
| | | try |
| | | { |
| | | final AuthorizationIdentityResponseControl control = result.getControl( |
| | | AuthorizationIdentityResponseControl.DECODER, new DecodeOptions()); |
| | | if (control != null) |
| | | { |
| | | final LocalizableMessage message = INFO_BIND_AUTHZID_RETURNED |
| | | .get(control.getAuthorizationID()); |
| | | app.println(message); |
| | | } |
| | | } |
| | | catch (final DecodeException e) |
| | | { |
| | | app.println(ERR_DECODE_CONTROL_FAILURE.get(e.getLocalizedMessage())); |
| | | } |
| | | |
| | | try |
| | | { |
| | | final PasswordExpiredResponseControl control = result.getControl( |
| | | PasswordExpiredResponseControl.DECODER, new DecodeOptions()); |
| | | if (control != null) |
| | | { |
| | | final LocalizableMessage message = INFO_BIND_PASSWORD_EXPIRED.get(); |
| | | app.println(message); |
| | | } |
| | | } |
| | | catch (final DecodeException e) |
| | | { |
| | | app.println(ERR_DECODE_CONTROL_FAILURE.get(e.getLocalizedMessage())); |
| | | } |
| | | |
| | | try |
| | | { |
| | | final PasswordExpiringResponseControl control = result.getControl( |
| | | PasswordExpiringResponseControl.DECODER, new DecodeOptions()); |
| | | if (control != null) |
| | | { |
| | | final LocalizableMessage timeString = Utils |
| | | .secondsToTimeString(control.getSecondsUntilExpiration()); |
| | | final LocalizableMessage message = INFO_BIND_PASSWORD_EXPIRING |
| | | .get(timeString); |
| | | app.println(message); |
| | | } |
| | | } |
| | | catch (final DecodeException e) |
| | | { |
| | | app.println(ERR_DECODE_CONTROL_FAILURE.get(e.getLocalizedMessage())); |
| | | } |
| | | |
| | | try |
| | | { |
| | | final PasswordPolicyResponseControl control = result.getControl( |
| | | PasswordPolicyResponseControl.DECODER, new DecodeOptions()); |
| | | if (control != null) |
| | | { |
| | | final PasswordPolicyErrorType errorType = control.getErrorType(); |
| | | if (errorType == PasswordPolicyErrorType.PASSWORD_EXPIRED) |
| | | { |
| | | final LocalizableMessage message = INFO_BIND_PASSWORD_EXPIRED.get(); |
| | | app.println(message); |
| | | } |
| | | else if (errorType == PasswordPolicyErrorType.ACCOUNT_LOCKED) |
| | | { |
| | | final LocalizableMessage message = INFO_BIND_ACCOUNT_LOCKED.get(); |
| | | app.println(message); |
| | | } |
| | | else if (errorType == PasswordPolicyErrorType.CHANGE_AFTER_RESET) |
| | | { |
| | | |
| | | final LocalizableMessage message = INFO_BIND_MUST_CHANGE_PASSWORD |
| | | .get(); |
| | | app.println(message); |
| | | } |
| | | |
| | | final PasswordPolicyWarningType warningType = control |
| | | .getWarningType(); |
| | | if (warningType == PasswordPolicyWarningType.TIME_BEFORE_EXPIRATION) |
| | | { |
| | | final LocalizableMessage timeString = Utils |
| | | .secondsToTimeString(control.getWarningValue()); |
| | | final LocalizableMessage message = INFO_BIND_PASSWORD_EXPIRING |
| | | .get(timeString); |
| | | app.println(message); |
| | | } |
| | | else if (warningType == PasswordPolicyWarningType.GRACE_LOGINS_REMAINING) |
| | | { |
| | | final LocalizableMessage message = INFO_BIND_GRACE_LOGINS_REMAINING |
| | | .get(control.getWarningValue()); |
| | | app.println(message); |
| | | } |
| | | } |
| | | } |
| | | catch (final DecodeException e) |
| | | { |
| | | app.println(ERR_DECODE_CONTROL_FAILURE.get(e.getLocalizedMessage())); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Read the data from the specified file and return it in a byte array. |
| | | * |
| | | * @param filePath |
| | | * The path to the file that should be read. |
| | | * @return A byte array containing the contents of the requested file. |
| | | * @throws IOException |
| | | * If a problem occurs while trying to read the specified file. |
| | | */ |
| | | static byte[] readBytesFromFile(final String filePath) throws IOException |
| | | { |
| | | byte[] val = null; |
| | | FileInputStream fis = null; |
| | | try |
| | | { |
| | | final File file = new File(filePath); |
| | | fis = new FileInputStream(file); |
| | | final long length = file.length(); |
| | | val = new byte[(int) length]; |
| | | // Read in the bytes |
| | | int offset = 0; |
| | | int numRead = 0; |
| | | while (offset < val.length |
| | | && (numRead = fis.read(val, offset, val.length - offset)) >= 0) |
| | | { |
| | | offset += numRead; |
| | | } |
| | | |
| | | // Ensure all the bytes have been read in |
| | | if (offset < val.length) |
| | | { |
| | | throw new IOException("Could not completely read file " + filePath); |
| | | } |
| | | |
| | | return val; |
| | | } |
| | | finally |
| | | { |
| | | if (fis != null) |
| | | { |
| | | fis.close(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves a user-friendly string that indicates the length of time (in |
| | | * days, hours, minutes, and seconds) in the specified number of seconds. |
| | | * |
| | | * @param numSeconds |
| | | * The number of seconds to be converted to a more user-friendly |
| | | * value. |
| | | * @return The user-friendly representation of the specified number of |
| | | * seconds. |
| | | */ |
| | | static LocalizableMessage secondsToTimeString(final int numSeconds) |
| | | { |
| | | if (numSeconds < 60) |
| | | { |
| | | // We can express it in seconds. |
| | | return INFO_TIME_IN_SECONDS.get(numSeconds); |
| | | } |
| | | else if (numSeconds < 3600) |
| | | { |
| | | // We can express it in minutes and seconds. |
| | | final int m = numSeconds / 60; |
| | | final int s = numSeconds % 60; |
| | | return INFO_TIME_IN_MINUTES_SECONDS.get(m, s); |
| | | } |
| | | else if (numSeconds < 86400) |
| | | { |
| | | // We can express it in hours, minutes, and seconds. |
| | | final int h = numSeconds / 3600; |
| | | final int m = (numSeconds % 3600) / 60; |
| | | final int s = numSeconds % 3600 % 60; |
| | | return INFO_TIME_IN_HOURS_MINUTES_SECONDS.get(h, m, s); |
| | | } |
| | | else |
| | | { |
| | | // We can express it in days, hours, minutes, and seconds. |
| | | final int d = numSeconds / 86400; |
| | | final int h = (numSeconds % 86400) / 3600; |
| | | final int m = (numSeconds % 86400 % 3600) / 60; |
| | | final int s = numSeconds % 86400 % 3600 % 60; |
| | | return INFO_TIME_IN_DAYS_HOURS_MINUTES_SECONDS.get(d, h, m, s); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Inserts line breaks into the provided buffer to wrap text at no more than |
| | | * the specified column width. Wrapping will only be done at space boundaries |
| | | * and if there are no spaces within the specified width, then wrapping will |
| | | * be performed at the first space after the specified column. |
| | | * |
| | | * @param message |
| | | * The message to be wrapped. |
| | | * @param width |
| | | * The maximum number of characters to allow on a line if |
| | | * there is a suitable breaking point. |
| | | * The maximum number of characters to allow on a line if there is a |
| | | * suitable breaking point. |
| | | * @return The wrapped text. |
| | | */ |
| | | static String wrapText(LocalizableMessage message, int width) |
| | | static String wrapText(final LocalizableMessage message, final int width) |
| | | { |
| | | return wrapText(message.toString(), width, 0); |
| | | } |
| | |
| | | |
| | | |
| | | /** |
| | | * Inserts line breaks into the provided buffer to wrap text at no |
| | | * more than the specified column width. Wrapping will only be done at |
| | | * space boundaries and if there are no spaces within the specified |
| | | * width, then wrapping will be performed at the first space after the |
| | | * specified column. |
| | | * |
| | | * @param text |
| | | * The text to be wrapped. |
| | | * @param width |
| | | * The maximum number of characters to allow on a line if |
| | | * there is a suitable breaking point. |
| | | * @return The wrapped text. |
| | | */ |
| | | static String wrapText(String text, int width) |
| | | { |
| | | return wrapText(text, width, 0); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Inserts line breaks into the provided buffer to wrap text at no |
| | | * more than the specified column width. Wrapping will only be done at |
| | | * space boundaries and if there are no spaces within the specified |
| | | * width, then wrapping will be performed at the first space after the |
| | | * specified column. In addition each line will be indented by the |
| | | * specified amount. |
| | | * Inserts line breaks into the provided buffer to wrap text at no more than |
| | | * the specified column width. Wrapping will only be done at space boundaries |
| | | * and if there are no spaces within the specified width, then wrapping will |
| | | * be performed at the first space after the specified column. In addition |
| | | * each line will be indented by the specified amount. |
| | | * |
| | | * @param message |
| | | * The message to be wrapped. |
| | | * @param width |
| | | * The maximum number of characters to allow on a line if |
| | | * there is a suitable breaking point (including any |
| | | * indentation). |
| | | * The maximum number of characters to allow on a line if there is a |
| | | * suitable breaking point (including any indentation). |
| | | * @param indent |
| | | * The number of columns to indent each line. |
| | | * @return The wrapped text. |
| | | */ |
| | | static String wrapText(LocalizableMessage message, int width, int indent) |
| | | static String wrapText(final LocalizableMessage message, final int width, |
| | | final int indent) |
| | | { |
| | | return wrapText(message.toString(), width, indent); |
| | | } |
| | |
| | | |
| | | |
| | | /** |
| | | * Inserts line breaks into the provided buffer to wrap text at no |
| | | * more than the specified column width. Wrapping will only be done at |
| | | * space boundaries and if there are no spaces within the specified |
| | | * width, then wrapping will be performed at the first space after the |
| | | * specified column. In addition each line will be indented by the |
| | | * specified amount. |
| | | * Inserts line breaks into the provided buffer to wrap text at no more than |
| | | * the specified column width. Wrapping will only be done at space boundaries |
| | | * and if there are no spaces within the specified width, then wrapping will |
| | | * be performed at the first space after the specified column. |
| | | * |
| | | * @param text |
| | | * The text to be wrapped. |
| | | * @param width |
| | | * The maximum number of characters to allow on a line if |
| | | * there is a suitable breaking point (including any |
| | | * indentation). |
| | | * The maximum number of characters to allow on a line if there is a |
| | | * suitable breaking point. |
| | | * @return The wrapped text. |
| | | */ |
| | | static String wrapText(final String text, final int width) |
| | | { |
| | | return wrapText(text, width, 0); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Inserts line breaks into the provided buffer to wrap text at no more than |
| | | * the specified column width. Wrapping will only be done at space boundaries |
| | | * and if there are no spaces within the specified width, then wrapping will |
| | | * be performed at the first space after the specified column. In addition |
| | | * each line will be indented by the specified amount. |
| | | * |
| | | * @param text |
| | | * The text to be wrapped. |
| | | * @param width |
| | | * The maximum number of characters to allow on a line if there is a |
| | | * suitable breaking point (including any indentation). |
| | | * @param indent |
| | | * The number of columns to indent each line. |
| | | * @return The wrapped text. |
| | | */ |
| | | static String wrapText(String text, int width, int indent) |
| | | static String wrapText(final String text, int width, final int indent) |
| | | { |
| | | // Calculate the real width and indentation padding. |
| | | width -= indent; |
| | | StringBuilder pb = new StringBuilder(); |
| | | final StringBuilder pb = new StringBuilder(); |
| | | for (int i = 0; i < indent; i++) |
| | | { |
| | | pb.append(' '); |
| | | } |
| | | String padding = pb.toString(); |
| | | final String padding = pb.toString(); |
| | | |
| | | StringBuilder buffer = new StringBuilder(); |
| | | final StringBuilder buffer = new StringBuilder(); |
| | | if (text != null) |
| | | { |
| | | StringTokenizer lineTokenizer = new StringTokenizer(text, "\r\n", |
| | | final StringTokenizer lineTokenizer = new StringTokenizer(text, "\r\n", |
| | | true); |
| | | while (lineTokenizer.hasMoreTokens()) |
| | | { |
| | | String line = lineTokenizer.nextToken(); |
| | | final String line = lineTokenizer.nextToken(); |
| | | if (line.equals("\r") || line.equals("\n")) |
| | | { |
| | | // It's an end-of-line character, so append it as-is. |
| | |
| | | // wrapped. Do so at space boundaries. |
| | | StringBuilder lineBuffer = new StringBuilder(); |
| | | StringBuilder delimBuffer = new StringBuilder(); |
| | | StringTokenizer wordTokenizer = new StringTokenizer(line, |
| | | " ", true); |
| | | final StringTokenizer wordTokenizer = new StringTokenizer(line, " ", |
| | | true); |
| | | while (wordTokenizer.hasMoreTokens()) |
| | | { |
| | | String word = wordTokenizer.nextToken(); |
| | | final String word = wordTokenizer.nextToken(); |
| | | if (word.equals(" ")) |
| | | { |
| | | // It's a space, so add it to the delim buffer only if the |
| | |
| | | { |
| | | // It's not a space, so see if we can fit it on the curent |
| | | // line. |
| | | int newLineLength = lineBuffer.length() |
| | | final int newLineLength = lineBuffer.length() |
| | | + delimBuffer.length() + word.length(); |
| | | if (newLineLength < width) |
| | | { |
| | |
| | | |
| | | |
| | | |
| | | /** |
| | | * Parse the specified command line argument to create the appropriate |
| | | * LDAPControl. The argument string should be in the format |
| | | * controloid[:criticality[:value|::b64value|:<fileurl]] |
| | | * |
| | | * @param argString |
| | | * The argument string containing the encoded control |
| | | * information. |
| | | * @return The control decoded from the provided string, or |
| | | * <CODE>null</CODE> if an error occurs while parsing the |
| | | * argument value. |
| | | * @throws org.opends.sdk.DecodeException |
| | | * If an error occurs. |
| | | */ |
| | | static GenericControl getControl(String argString) |
| | | throws DecodeException |
| | | // Prevent instantiation. |
| | | private Utils() |
| | | { |
| | | String controlOID = null; |
| | | boolean controlCriticality = false; |
| | | ByteString controlValue = null; |
| | | |
| | | int idx = argString.indexOf(":"); |
| | | |
| | | if (idx < 0) |
| | | { |
| | | controlOID = argString; |
| | | } |
| | | else |
| | | { |
| | | controlOID = argString.substring(0, idx); |
| | | } |
| | | |
| | | String lowerOID = StaticUtils.toLowerCase(controlOID); |
| | | if (lowerOID.equals("accountusable") |
| | | || lowerOID.equals("accountusability")) |
| | | { |
| | | controlOID = AccountUsabilityControl.OID_ACCOUNT_USABLE_CONTROL; |
| | | } |
| | | else if (lowerOID.equals("authzid") |
| | | || lowerOID.equals("authorizationidentity")) |
| | | { |
| | | controlOID = AuthorizationIdentityControl.OID_AUTHZID_REQUEST; |
| | | } |
| | | else if (lowerOID.equals("noop") || lowerOID.equals("no-op")) |
| | | { |
| | | // controlOID = OID_LDAP_NOOP_OPENLDAP_ASSIGNED; |
| | | } |
| | | else if (lowerOID.equals("subentries")) |
| | | { |
| | | // controlOID = OID_LDAP_SUBENTRIES; |
| | | } |
| | | else if (lowerOID.equals("managedsait")) |
| | | { |
| | | // controlOID = OID_MANAGE_DSAIT_CONTROL; |
| | | } |
| | | else if (lowerOID.equals("pwpolicy") |
| | | || lowerOID.equals("passwordpolicy")) |
| | | { |
| | | controlOID = PasswordPolicyControl.OID_PASSWORD_POLICY_CONTROL; |
| | | } |
| | | else if (lowerOID.equals("subtreedelete") |
| | | || lowerOID.equals("treedelete")) |
| | | { |
| | | controlOID = SubtreeDeleteControl.OID_SUBTREE_DELETE_CONTROL; |
| | | } |
| | | else if (lowerOID.equals("realattrsonly") |
| | | || lowerOID.equals("realattributesonly")) |
| | | { |
| | | // controlOID = OID_REAL_ATTRS_ONLY; |
| | | } |
| | | else if (lowerOID.equals("virtualattrsonly") |
| | | || lowerOID.equals("virtualattributesonly")) |
| | | { |
| | | // controlOID = OID_VIRTUAL_ATTRS_ONLY; |
| | | } |
| | | else if (lowerOID.equals("effectiverights") |
| | | || lowerOID.equals("geteffectiverights")) |
| | | { |
| | | controlOID = GetEffectiveRightsRequestControl.OID_GET_EFFECTIVE_RIGHTS; |
| | | } |
| | | |
| | | if (idx < 0) |
| | | { |
| | | return new GenericControl(controlOID); |
| | | } |
| | | |
| | | String remainder = argString.substring(idx + 1, argString.length()); |
| | | |
| | | idx = remainder.indexOf(":"); |
| | | if (idx == -1) |
| | | { |
| | | if (remainder.equalsIgnoreCase("true")) |
| | | { |
| | | controlCriticality = true; |
| | | } |
| | | else if (remainder.equalsIgnoreCase("false")) |
| | | { |
| | | controlCriticality = false; |
| | | } |
| | | else |
| | | { |
| | | // TODO: I18N |
| | | throw DecodeException.error(LocalizableMessage |
| | | .raw("Invalid format for criticality value:" + remainder)); |
| | | } |
| | | return new GenericControl(controlOID, controlCriticality); |
| | | |
| | | } |
| | | |
| | | String critical = remainder.substring(0, idx); |
| | | if (critical.equalsIgnoreCase("true")) |
| | | { |
| | | controlCriticality = true; |
| | | } |
| | | else if (critical.equalsIgnoreCase("false")) |
| | | { |
| | | controlCriticality = false; |
| | | } |
| | | else |
| | | { |
| | | // TODO: I18N |
| | | throw DecodeException.error(LocalizableMessage |
| | | .raw("Invalid format for criticality value:" + critical)); |
| | | } |
| | | |
| | | String valString = remainder.substring(idx + 1, remainder.length()); |
| | | if (valString.charAt(0) == ':') |
| | | { |
| | | controlValue = ByteString.valueOf(valString.substring(1, |
| | | valString.length())); |
| | | } |
| | | else if (valString.charAt(0) == '<') |
| | | { |
| | | // Read data from the file. |
| | | String filePath = valString.substring(1, valString.length()); |
| | | try |
| | | { |
| | | byte[] val = readBytesFromFile(filePath); |
| | | controlValue = ByteString.wrap(val); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | return null; |
| | | } |
| | | } |
| | | else |
| | | { |
| | | controlValue = ByteString.valueOf(valString); |
| | | } |
| | | |
| | | return new GenericControl(controlOID, controlCriticality, |
| | | controlValue); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Read the data from the specified file and return it in a byte |
| | | * array. |
| | | * |
| | | * @param filePath |
| | | * The path to the file that should be read. |
| | | * @return A byte array containing the contents of the requested file. |
| | | * @throws IOException |
| | | * If a problem occurs while trying to read the specified |
| | | * file. |
| | | */ |
| | | static byte[] readBytesFromFile(String filePath) throws IOException |
| | | { |
| | | byte[] val = null; |
| | | FileInputStream fis = null; |
| | | try |
| | | { |
| | | File file = new File(filePath); |
| | | fis = new FileInputStream(file); |
| | | long length = file.length(); |
| | | val = new byte[(int) length]; |
| | | // Read in the bytes |
| | | int offset = 0; |
| | | int numRead = 0; |
| | | while (offset < val.length |
| | | && (numRead = fis.read(val, offset, val.length - offset)) >= 0) |
| | | { |
| | | offset += numRead; |
| | | } |
| | | |
| | | // Ensure all the bytes have been read in |
| | | if (offset < val.length) |
| | | { |
| | | throw new IOException("Could not completely read file " |
| | | + filePath); |
| | | } |
| | | |
| | | return val; |
| | | } |
| | | finally |
| | | { |
| | | if (fis != null) |
| | | { |
| | | fis.close(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Prints a multi-line error message with the provided information to |
| | | * the given print stream. |
| | | * |
| | | * @param app |
| | | * The console app to use to write the error message. |
| | | * @param ere |
| | | * The error result. |
| | | * @return The error code. |
| | | */ |
| | | static int printErrorMessage(ConsoleApplication app, |
| | | ErrorResultException ere) |
| | | { |
| | | // if ((ere.getMessage() != null) && (ere.getMessage().length() > |
| | | // 0)) |
| | | // { |
| | | // app.println(LocalizableMessage.raw(ere.getMessage())); |
| | | // } |
| | | |
| | | if (ere.getResult().getResultCode().intValue() >= 0) |
| | | { |
| | | app.println(ERR_TOOL_RESULT_CODE.get(ere.getResult() |
| | | .getResultCode().intValue(), ere.getResult().getResultCode() |
| | | .toString())); |
| | | } |
| | | |
| | | if ((ere.getResult().getDiagnosticMessage() != null) |
| | | && (ere.getResult().getDiagnosticMessage().length() > 0)) |
| | | { |
| | | app.println(ERR_TOOL_ERROR_MESSAGE.get(ere.getResult() |
| | | .getDiagnosticMessage())); |
| | | } |
| | | |
| | | if (ere.getResult().getMatchedDN() != null |
| | | && ere.getResult().getMatchedDN().length() > 0) |
| | | { |
| | | app.println(ERR_TOOL_MATCHED_DN.get(ere.getResult() |
| | | .getMatchedDN())); |
| | | } |
| | | |
| | | if (app.isVerbose() && ere.getResult().getCause() != null) |
| | | { |
| | | ere.getResult().getCause().printStackTrace(app.getErrorStream()); |
| | | } |
| | | |
| | | return ere.getResult().getResultCode().intValue(); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves a user-friendly string that indicates the length of time |
| | | * (in days, hours, minutes, and seconds) in the specified number of |
| | | * seconds. |
| | | * |
| | | * @param numSeconds |
| | | * The number of seconds to be converted to a more |
| | | * user-friendly value. |
| | | * @return The user-friendly representation of the specified number of |
| | | * seconds. |
| | | */ |
| | | static LocalizableMessage secondsToTimeString(int numSeconds) |
| | | { |
| | | if (numSeconds < 60) |
| | | { |
| | | // We can express it in seconds. |
| | | return INFO_TIME_IN_SECONDS.get(numSeconds); |
| | | } |
| | | else if (numSeconds < 3600) |
| | | { |
| | | // We can express it in minutes and seconds. |
| | | int m = numSeconds / 60; |
| | | int s = numSeconds % 60; |
| | | return INFO_TIME_IN_MINUTES_SECONDS.get(m, s); |
| | | } |
| | | else if (numSeconds < 86400) |
| | | { |
| | | // We can express it in hours, minutes, and seconds. |
| | | int h = numSeconds / 3600; |
| | | int m = (numSeconds % 3600) / 60; |
| | | int s = numSeconds % 3600 % 60; |
| | | return INFO_TIME_IN_HOURS_MINUTES_SECONDS.get(h, m, s); |
| | | } |
| | | else |
| | | { |
| | | // We can express it in days, hours, minutes, and seconds. |
| | | int d = numSeconds / 86400; |
| | | int h = (numSeconds % 86400) / 3600; |
| | | int m = (numSeconds % 86400 % 3600) / 60; |
| | | int s = numSeconds % 86400 % 3600 % 60; |
| | | return INFO_TIME_IN_DAYS_HOURS_MINUTES_SECONDS.get(d, h, m, s); |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | static void printPasswordPolicyResults(ConsoleApplication app, |
| | | Connection connection) |
| | | { |
| | | if (connection instanceof AuthenticatedConnection) |
| | | { |
| | | AuthenticatedConnection conn = (AuthenticatedConnection) connection; |
| | | BindResult result = conn.getAuthenticatedBindResult(); |
| | | |
| | | Control control = result |
| | | .getControl(AuthorizationIdentityControl.OID_AUTHZID_RESPONSE); |
| | | if (control != null) |
| | | { |
| | | AuthorizationIdentityControl.Response dc = (AuthorizationIdentityControl.Response) control; |
| | | LocalizableMessage message = INFO_BIND_AUTHZID_RETURNED.get(dc |
| | | .getAuthorizationID()); |
| | | app.println(message); |
| | | } |
| | | control = result |
| | | .getControl(PasswordExpiredControl.OID_NS_PASSWORD_EXPIRED); |
| | | if (control != null) |
| | | { |
| | | LocalizableMessage message = INFO_BIND_PASSWORD_EXPIRED.get(); |
| | | app.println(message); |
| | | } |
| | | control = result |
| | | .getControl(PasswordExpiringControl.OID_NS_PASSWORD_EXPIRING); |
| | | if (control != null) |
| | | { |
| | | PasswordExpiringControl dc = (PasswordExpiringControl) control; |
| | | LocalizableMessage timeString = Utils.secondsToTimeString(dc |
| | | .getSecondsUntilExpiration()); |
| | | LocalizableMessage message = INFO_BIND_PASSWORD_EXPIRING.get(timeString); |
| | | app.println(message); |
| | | } |
| | | control = result |
| | | .getControl(PasswordPolicyControl.OID_PASSWORD_POLICY_CONTROL); |
| | | if (control != null) |
| | | { |
| | | PasswordPolicyControl.Response dc = (PasswordPolicyControl.Response) control; |
| | | PasswordPolicyErrorType errorType = dc.getErrorType(); |
| | | if (errorType == PasswordPolicyErrorType.PASSWORD_EXPIRED) |
| | | { |
| | | LocalizableMessage message = INFO_BIND_PASSWORD_EXPIRED.get(); |
| | | app.println(message); |
| | | } |
| | | else if (errorType == PasswordPolicyErrorType.ACCOUNT_LOCKED) |
| | | { |
| | | LocalizableMessage message = INFO_BIND_ACCOUNT_LOCKED.get(); |
| | | app.println(message); |
| | | } |
| | | else if (errorType == PasswordPolicyErrorType.CHANGE_AFTER_RESET) |
| | | { |
| | | |
| | | LocalizableMessage message = INFO_BIND_MUST_CHANGE_PASSWORD.get(); |
| | | app.println(message); |
| | | } |
| | | |
| | | PasswordPolicyWarningType warningType = dc.getWarningType(); |
| | | if (warningType == PasswordPolicyWarningType.TIME_BEFORE_EXPIRATION) |
| | | { |
| | | LocalizableMessage timeString = Utils.secondsToTimeString(dc |
| | | .getWarningValue()); |
| | | LocalizableMessage message = INFO_BIND_PASSWORD_EXPIRING.get(timeString); |
| | | app.println(message); |
| | | } |
| | | else if (warningType == PasswordPolicyWarningType.GRACE_LOGINS_REMAINING) |
| | | { |
| | | LocalizableMessage message = INFO_BIND_GRACE_LOGINS_REMAINING.get(dc |
| | | .getWarningValue()); |
| | | app.println(message); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Filters the provided value to ensure that it is appropriate for use |
| | | * as an exit code. Exit code values are generally only allowed to be |
| | | * between 0 and 255, so any value outside of this range will be |
| | | * converted to 255, which is the typical exit code used to indicate |
| | | * an overflow value. |
| | | * |
| | | * @param exitCode |
| | | * The exit code value to be processed. |
| | | * @return An integer value between 0 and 255, inclusive. If the |
| | | * provided exit code was already between 0 and 255, then the |
| | | * original value will be returned. If the provided value was |
| | | * out of this range, then 255 will be returned. |
| | | */ |
| | | static int filterExitCode(int exitCode) |
| | | { |
| | | if (exitCode < 0) |
| | | { |
| | | return 255; |
| | | } |
| | | else if (exitCode > 255) |
| | | { |
| | | return 255; |
| | | } |
| | | else |
| | | { |
| | | return exitCode; |
| | | } |
| | | // Do nothing. |
| | | } |
| | | } |