001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2006-2009 Sun Microsystems, Inc.
015 * Portions Copyright 2011-2016 ForgeRock AS.
016 */
017package org.opends.server.tools;
018
019import static com.forgerock.opendj.cli.ArgumentConstants.*;
020import static com.forgerock.opendj.cli.CliMessages.INFO_BINDPWD_FILE_PLACEHOLDER;
021import static com.forgerock.opendj.cli.CliMessages.INFO_KEYSTORE_PWD_FILE_PLACEHOLDER;
022import static com.forgerock.opendj.cli.CliMessages.INFO_PORT_PLACEHOLDER;
023import static com.forgerock.opendj.cli.CliMessages.INFO_TRUSTSTORE_PWD_FILE_PLACEHOLDER;
024import static com.forgerock.opendj.cli.Utils.*;
025import static com.forgerock.opendj.cli.CommonArguments.*;
026
027import static org.opends.messages.ToolMessages.*;
028import static org.opends.server.extensions.PasswordPolicyStateExtendedOperation.*;
029import static org.opends.server.protocols.ldap.LDAPResultCode.*;
030import static org.opends.server.util.ServerConstants.*;
031import static org.opends.server.util.StaticUtils.*;
032
033import java.io.IOException;
034import java.io.OutputStream;
035import java.io.PrintStream;
036import java.util.ArrayList;
037import java.util.HashSet;
038import java.util.LinkedList;
039import java.util.concurrent.atomic.AtomicInteger;
040
041import javax.net.ssl.SSLException;
042
043import org.forgerock.i18n.LocalizableMessage;
044import org.forgerock.opendj.io.ASN1;
045import org.forgerock.opendj.io.ASN1Reader;
046import org.forgerock.opendj.io.ASN1Writer;
047import org.forgerock.opendj.ldap.ByteStringBuilder;
048import org.opends.server.config.AdministrationConnector;
049import org.opends.server.core.DirectoryServer.DirectoryServerVersionHandler;
050import org.opends.server.loggers.JDKLogging;
051import org.opends.server.protocols.ldap.ExtendedRequestProtocolOp;
052import org.opends.server.protocols.ldap.ExtendedResponseProtocolOp;
053import org.opends.server.protocols.ldap.LDAPMessage;
054import org.opends.server.protocols.ldap.LDAPResultCode;
055import org.opends.server.types.NullOutputStream;
056import org.opends.server.util.EmbeddedUtils;
057import org.opends.server.util.cli.LDAPConnectionArgumentParser;
058
059import com.forgerock.opendj.cli.Argument;
060import com.forgerock.opendj.cli.ArgumentException;
061import com.forgerock.opendj.cli.BooleanArgument;
062import com.forgerock.opendj.cli.FileBasedArgument;
063import com.forgerock.opendj.cli.IntegerArgument;
064import com.forgerock.opendj.cli.MultiChoiceArgument;
065import com.forgerock.opendj.cli.StringArgument;
066import com.forgerock.opendj.cli.SubCommand;
067import com.forgerock.opendj.cli.SubCommandArgumentParser;
068
069/**
070 * This class provides a tool that can be used to perform various kinds of
071 * account management using the password policy state extended operation.
072 */
073public class ManageAccount
074{
075  /** The fully-qualified name of this class. */
076  private static final String CLASS_NAME =
077       "org.opends.server.tools.ManageAccount";
078
079
080
081  /**
082   * The name of the subcommand that will be used to get all password policy
083   * state information for the user.
084   */
085  private static final String SC_GET_ALL = "get-all";
086
087
088
089  /**
090   * The name of the subcommand that will be used to get the DN of the password
091   * policy for a given user.
092   */
093  private static final String SC_GET_PASSWORD_POLICY_DN =
094       "get-password-policy-dn";
095
096
097
098  /**
099   * The name of the subcommand that will be used to get the disabled state for
100   * a user.
101   */
102  private static final String SC_GET_ACCOUNT_DISABLED_STATE =
103       "get-account-is-disabled";
104
105
106
107  /**
108   * The name of the subcommand that will be used to set the disabled state for
109   * a user.
110   */
111  private static final String SC_SET_ACCOUNT_DISABLED_STATE =
112       "set-account-is-disabled";
113
114
115
116  /**
117   * The name of the subcommand that will be used to clear the disabled state
118   * for a user.
119   */
120  private static final String SC_CLEAR_ACCOUNT_DISABLED_STATE =
121       "clear-account-is-disabled";
122
123
124
125  /**
126   * The name of the subcommand that will be used to get the account expiration
127   * time.
128   */
129  private static final String SC_GET_ACCOUNT_EXPIRATION_TIME =
130       "get-account-expiration-time";
131
132
133
134  /**
135   * The name of the subcommand that will be used to set the account expiration
136   * time.
137   */
138  private static final String SC_SET_ACCOUNT_EXPIRATION_TIME =
139       "set-account-expiration-time";
140
141
142
143  /**
144   * The name of the subcommand that will be used to clear the account
145   * expiration time.
146   */
147  private static final String SC_CLEAR_ACCOUNT_EXPIRATION_TIME =
148       "clear-account-expiration-time";
149
150
151
152  /**
153   * The name of the subcommand that will be used to get the length of time
154   * before the account expires.
155   */
156  private static final String SC_GET_SECONDS_UNTIL_ACCOUNT_EXPIRATION =
157       "get-seconds-until-account-expiration";
158
159
160
161  /**
162   * The name of the subcommand that will be used to get the time the password
163   * was last changed.
164   */
165  private static final String SC_GET_PASSWORD_CHANGED_TIME =
166       "get-password-changed-time";
167
168
169
170  /**
171   * The name of the subcommand that will be used to set the time the password
172   * was last changed.
173   */
174  private static final String SC_SET_PASSWORD_CHANGED_TIME =
175       "set-password-changed-time";
176
177
178
179  /**
180   * The name of the subcommand that will be used to clear the time the password
181   * was last changed.
182   */
183  private static final String SC_CLEAR_PASSWORD_CHANGED_TIME =
184       "clear-password-changed-time";
185
186
187
188  /**
189   * The name of the subcommand that will be used to get the time the user was
190   * first warned about an upcoming password expiration.
191   */
192  private static final String SC_GET_PASSWORD_EXP_WARNED_TIME =
193       "get-password-expiration-warned-time";
194
195
196
197  /**
198   * The name of the subcommand that will be used to set the time the user was
199   * first warned about an upcoming password expiration.
200   */
201  private static final String SC_SET_PASSWORD_EXP_WARNED_TIME =
202       "set-password-expiration-warned-time";
203
204
205
206  /**
207   * The name of the subcommand that will be used to clear the time the user was
208   * first warned about an upcoming password expiration.
209   */
210  private static final String SC_CLEAR_PASSWORD_EXP_WARNED_TIME =
211       "clear-password-expiration-warned-time";
212
213
214
215  /**
216   * The name of the subcommand that will be used to get the length of time
217   * before the password expires.
218   */
219  private static final String SC_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION =
220       "get-seconds-until-password-expiration";
221
222
223
224  /**
225   * The name of the subcommand that will be used to get the length of time
226   * before the user is first warned about an upcoming password expiration.
227   */
228  private static final String SC_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION_WARNING =
229       "get-seconds-until-password-expiration-warning";
230
231
232
233  /**
234   * The name of the subcommand that will be used to get the authentication
235   * failure times for the user.
236   */
237  private static final String SC_GET_AUTHENTICATION_FAILURE_TIMES =
238       "get-authentication-failure-times";
239
240
241
242  /**
243   * The name of the subcommand that will be used to add an authentication
244   * failure time for the user.
245   */
246  private static final String SC_ADD_AUTHENTICATION_FAILURE_TIME =
247       "add-authentication-failure-time";
248
249
250
251  /**
252   * The name of the subcommand that will be used to set the authentication
253   * failure times for the user.
254   */
255  private static final String SC_SET_AUTHENTICATION_FAILURE_TIMES =
256       "set-authentication-failure-times";
257
258
259
260  /**
261   * The name of the subcommand that will be used to clear the authentication
262   * failure times for the user.
263   */
264  private static final String SC_CLEAR_AUTHENTICATION_FAILURE_TIMES =
265       "clear-authentication-failure-times";
266
267
268
269  /**
270   * The name of the subcommand that will be used to get the length of time
271   * before the user's account is unlocked.
272   */
273  private static final String
274       SC_GET_SECONDS_UNTIL_AUTHENTICATION_FAILURE_UNLOCK =
275            "get-seconds-until-authentication-failure-unlock";
276
277
278
279  /**
280   * The name of the subcommand that will be used to get the number of remaining
281   * authentication failures for the user.
282   */
283  private static final String SC_GET_REMAINING_AUTHENTICATION_FAILURE_COUNT =
284       "get-remaining-authentication-failure-count";
285
286
287
288  /**
289   * The name of the subcommand that will be used to get the last login time for
290   * the user.
291   */
292  private static final String SC_GET_LAST_LOGIN_TIME =
293       "get-last-login-time";
294
295
296
297  /**
298   * The name of the subcommand that will be used to set the last login time for
299   * the user.
300   */
301  private static final String SC_SET_LAST_LOGIN_TIME =
302       "set-last-login-time";
303
304
305
306  /**
307   * The name of the subcommand that will be used to clear the last login time
308   * for the user.
309   */
310  private static final String SC_CLEAR_LAST_LOGIN_TIME =
311       "clear-last-login-time";
312
313
314
315  /**
316   * The name of the subcommand that will be used to get the length of time
317   * before the account is idle locked.
318   */
319  private static final String SC_GET_SECONDS_UNTIL_IDLE_LOCKOUT =
320       "get-seconds-until-idle-lockout";
321
322
323
324  /**
325   * The name of the subcommand that will be used to get the password reset
326   * state for a user.
327   */
328  private static final String SC_GET_PASSWORD_RESET_STATE =
329       "get-password-is-reset";
330
331
332
333  /**
334   * The name of the subcommand that will be used to set the password reset
335   * state for a user.
336   */
337  private static final String SC_SET_PASSWORD_RESET_STATE =
338       "set-password-is-reset";
339
340
341
342  /**
343   * The name of the subcommand that will be used to clear the password reset
344   * state for a user.
345   */
346  private static final String SC_CLEAR_PASSWORD_RESET_STATE =
347       "clear-password-is-reset";
348
349
350
351  /**
352   * The name of the subcommand that will be used to get the length of time
353   * before the password reset lockout occurs.
354   */
355  private static final String SC_GET_SECONDS_UNTIL_PASSWORD_RESET_LOCKOUT =
356       "get-seconds-until-password-reset-lockout";
357
358
359
360  /**
361   * The name of the subcommand that will be used to get the grace login use
362   * times for the user.
363   */
364  private static final String SC_GET_GRACE_LOGIN_USE_TIMES =
365       "get-grace-login-use-times";
366
367
368
369  /**
370   * The name of the subcommand that will be used to add a grace login use time
371   * for the user.
372   */
373  private static final String SC_ADD_GRACE_LOGIN_USE_TIME =
374       "add-grace-login-use-time";
375
376
377
378  /**
379   * The name of the subcommand that will be used to set the grace login use
380   * times for the user.
381   */
382  private static final String SC_SET_GRACE_LOGIN_USE_TIMES =
383       "set-grace-login-use-times";
384
385
386
387  /**
388   * The name of the subcommand that will be used to clear the grace login use
389   * times for the user.
390   */
391  private static final String SC_CLEAR_GRACE_LOGIN_USE_TIMES =
392       "clear-grace-login-use-times";
393
394
395
396  /**
397   * The name of the subcommand that will be used to get number of remaining
398   * grace logins for the user.
399   */
400  private static final String SC_GET_REMAINING_GRACE_LOGIN_COUNT =
401       "get-remaining-grace-login-count";
402
403
404
405  /**
406   * The name of the subcommand that will be used to get the password changed by
407   * required time for the user.
408   */
409  private static final String SC_GET_PASSWORD_CHANGED_BY_REQUIRED_TIME =
410       "get-password-changed-by-required-time";
411
412
413
414  /**
415   * The name of the subcommand that will be used to set the password changed by
416   * required time for the user.
417   */
418  private static final String SC_SET_PASSWORD_CHANGED_BY_REQUIRED_TIME =
419       "set-password-changed-by-required-time";
420
421
422
423  /**
424   * The name of the subcommand that will be used to clear the password changed
425   * by required time for the user.
426   */
427  private static final String SC_CLEAR_PASSWORD_CHANGED_BY_REQUIRED_TIME =
428       "clear-password-changed-by-required-time";
429
430
431
432  /**
433   * The name of the subcommand that will be used to get the length of time
434   * before the user is required to change his/her password due to the required
435   * change time.
436   */
437  private static final String SC_GET_SECONDS_UNTIL_REQUIRED_CHANGE_TIME =
438       "get-seconds-until-required-change-time";
439
440
441
442  /**
443   * The name of the subcommand that will be used to get the password history
444   * state values.
445   */
446  private static final String SC_GET_PASSWORD_HISTORY = "get-password-history";
447
448
449
450  /**
451   * The name of the subcommand that will be used to clear the password history
452   * state values.
453   */
454  private static final String SC_CLEAR_PASSWORD_HISTORY =
455       "clear-password-history";
456
457
458
459  /**
460   * The name of the argument that will be used for holding the value(s) to use
461   * for the target operation.
462   */
463  private static final String ARG_OP_VALUE = "operationValue";
464
465
466
467  /**
468   * The value that will be used when encoding a password policy state operation
469   * that should not have any values.
470   */
471  private static final String NO_VALUE = null;
472
473
474
475  /** The LDAP reader used to read responses from the server. */
476  private static LDAPReader ldapReader;
477
478  /** The LDAP writer used to send requests to the server. */
479  private static LDAPWriter ldapWriter;
480
481  /** The counter that will be used for LDAP message IDs. */
482  private static AtomicInteger nextMessageID;
483
484  /** The connection to the server. */
485  private static LDAPConnection connection;
486
487  /** The print stream to use when writing messages to standard error. */
488  private static PrintStream err;
489
490  /** The print stream to use when writing messages to standard output. */
491  private static PrintStream out;
492
493  /** The DN of the user to target with the operation. */
494  private static String targetDNString;
495
496  /** The argument parser for this tool. */
497  private static SubCommandArgumentParser argParser;
498
499
500
501  /**
502   * Parses the command-line arguments, connects to the server, and performs the
503   * appropriate processing.
504   *
505   * @param  args  The command-line arguments provided to this program.
506   */
507  public static void main(String[] args)
508  {
509    int returnCode = main(args, true, System.out, System.err);
510    if (returnCode != 0)
511    {
512      System.exit(filterExitCode(returnCode));
513    }
514  }
515
516
517
518  /**
519   * Parses the command-line arguments, connects to the server, and performs the
520   * appropriate processing.
521   *
522   * @param  args       The command-line arguments provided to this program.
523   * @param  initServer Indicates whether to initialize the server.
524   * @param  outStream  The output stream to use for standard output, or
525   *                    {@code null} if standard output is not needed.
526   * @param  errStream  The output stream to use for standard error, or
527   *                    {@code null} if standard error is not needed.
528   *
529   * @return  A result code indicating whether the processing was successful.
530   */
531  public static int main(String[] args, Boolean initServer,
532                         OutputStream outStream, OutputStream errStream)
533  {
534    out = NullOutputStream.wrapOrNullStream(outStream);
535    err = NullOutputStream.wrapOrNullStream(errStream);
536    JDKLogging.disableLogging();
537
538    // Parse the command-line arguments provided to the program.
539    int result = parseArgsAndConnect(args, initServer);
540    if (result < 0)
541    {
542      // This should only happen if we're only displaying usage information or
543      // doing something else other than actually running the tool.
544      return LDAPResultCode.SUCCESS;
545    }
546    else if (result != LDAPResultCode.SUCCESS)
547    {
548      return result;
549    }
550
551
552    try
553    {
554      ByteStringBuilder builder = new ByteStringBuilder();
555      ASN1Writer writer = ASN1.getWriter(builder);
556
557      try
558      {
559        writer.writeStartSequence();
560        writer.writeOctetString(targetDNString);
561
562        // Use the subcommand provided to figure out how to encode the request.
563        writer.writeStartSequence();
564        result = processSubcommand(writer);
565        if (result != LDAPResultCode.SUCCESS)
566        {
567          return result;
568        }
569        writer.writeEndSequence();
570
571        writer.writeEndSequence();
572      }
573      catch(Exception e)
574      {
575        // TODO: Better message
576        err.println(e);
577      }
578
579
580      ExtendedRequestProtocolOp extendedRequest =
581           new ExtendedRequestProtocolOp(OID_PASSWORD_POLICY_STATE_EXTOP,
582                                         builder.toByteString());
583
584      LDAPMessage requestMessage =
585           new LDAPMessage(nextMessageID.getAndIncrement(), extendedRequest);
586
587      try
588      {
589        ldapWriter.writeMessage(requestMessage);
590      }
591      catch (Exception e)
592      {
593        printWrappedText(err, ERR_PWPSTATE_CANNOT_SEND_REQUEST_EXTOP.get(getExceptionMessage(e)));
594        return CLIENT_SIDE_SERVER_DOWN;
595      }
596
597
598      // Read the response from the server.
599      try
600      {
601        LDAPMessage responseMessage = ldapReader.readMessage();
602        if (responseMessage == null)
603        {
604          printWrappedText(err, ERR_PWPSTATE_CONNECTION_CLOSED_READING_RESPONSE.get());
605          return CLIENT_SIDE_SERVER_DOWN;
606        }
607
608        ExtendedResponseProtocolOp extendedResponse =
609             responseMessage.getExtendedResponseProtocolOp();
610
611        int resultCode = extendedResponse.getResultCode();
612        if (resultCode != LDAPResultCode.SUCCESS)
613        {
614          printWrappedText(err, ERR_PWPSTATE_REQUEST_FAILED.get(
615              resultCode, LDAPResultCode.toString(resultCode), extendedResponse.getErrorMessage()));
616          return resultCode;
617        }
618
619        ASN1Reader reader = ASN1.getReader(extendedResponse.getValue());
620        reader.readStartSequence();
621
622        // Skip the target user DN element
623        reader.skipElement();
624        reader.readStartSequence();
625
626        while(reader.hasNextElement())
627        {
628          // Get the response value and parse its individual elements.
629          int opType;
630          ArrayList<String> opValues;
631
632          try
633          {
634            reader.readStartSequence();
635            opType = (int)reader.readInteger();
636            opValues = new ArrayList<>();
637            if (reader.hasNextElement())
638            {
639              reader.readStartSequence();
640              while(reader.hasNextElement())
641              {
642                opValues.add(reader.readOctetStringAsString());
643              }
644              reader.readEndSequence();
645            }
646            reader.readEndSequence();
647          }
648          catch (Exception e)
649          {
650            printWrappedText(err, ERR_PWPSTATE_CANNOT_DECODE_RESPONSE_OP.get(getExceptionMessage(e)));
651            continue;
652          }
653
654          switch (opType)
655          {
656            case OP_GET_PASSWORD_POLICY_DN:
657              LocalizableMessage message = INFO_PWPSTATE_LABEL_PASSWORD_POLICY_DN.get();
658              printLabelAndValues(message, opValues);
659              break;
660
661            case OP_GET_ACCOUNT_DISABLED_STATE:
662              message = INFO_PWPSTATE_LABEL_ACCOUNT_DISABLED_STATE.get();
663              printLabelAndValues(message, opValues);
664              break;
665
666            case OP_GET_ACCOUNT_EXPIRATION_TIME:
667              message = INFO_PWPSTATE_LABEL_ACCOUNT_EXPIRATION_TIME.get();
668              printLabelAndValues(message, opValues);
669              break;
670
671            case OP_GET_SECONDS_UNTIL_ACCOUNT_EXPIRATION:
672              message =
673                  INFO_PWPSTATE_LABEL_SECONDS_UNTIL_ACCOUNT_EXPIRATION.get();
674              printLabelAndValues(message, opValues);
675              break;
676
677            case OP_GET_PASSWORD_CHANGED_TIME:
678              message = INFO_PWPSTATE_LABEL_PASSWORD_CHANGED_TIME.get();
679              printLabelAndValues(message, opValues);
680              break;
681
682            case OP_GET_PASSWORD_EXPIRATION_WARNED_TIME:
683              message =
684                  INFO_PWPSTATE_LABEL_PASSWORD_EXPIRATION_WARNED_TIME.get();
685              printLabelAndValues(message, opValues);
686              break;
687
688            case OP_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION:
689              message =
690                  INFO_PWPSTATE_LABEL_SECONDS_UNTIL_PASSWORD_EXPIRATION.get();
691              printLabelAndValues(message, opValues);
692              break;
693
694            case OP_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION_WARNING:
695              message =
696                  INFO_PWPSTATE_LABEL_SECONDS_UNTIL_PASSWORD_EXPIRATION_WARNING
697                      .get();
698              printLabelAndValues(message, opValues);
699              break;
700
701            case OP_GET_AUTHENTICATION_FAILURE_TIMES:
702              message = INFO_PWPSTATE_LABEL_AUTH_FAILURE_TIMES.get();
703              printLabelAndValues(message, opValues);
704              break;
705
706            case OP_GET_SECONDS_UNTIL_AUTHENTICATION_FAILURE_UNLOCK:
707              message =
708                  INFO_PWPSTATE_LABEL_SECONDS_UNTIL_AUTH_FAILURE_UNLOCK.get();
709              printLabelAndValues(message, opValues);
710              break;
711
712            case OP_GET_REMAINING_AUTHENTICATION_FAILURE_COUNT:
713              message = INFO_PWPSTATE_LABEL_REMAINING_AUTH_FAILURE_COUNT.get();
714              printLabelAndValues(message, opValues);
715              break;
716
717            case OP_GET_LAST_LOGIN_TIME:
718              message = INFO_PWPSTATE_LABEL_LAST_LOGIN_TIME.get();
719              printLabelAndValues(message, opValues);
720              break;
721
722            case OP_GET_SECONDS_UNTIL_IDLE_LOCKOUT:
723              message = INFO_PWPSTATE_LABEL_SECONDS_UNTIL_IDLE_LOCKOUT.get();
724              printLabelAndValues(message, opValues);
725              break;
726
727            case OP_GET_PASSWORD_RESET_STATE:
728              message = INFO_PWPSTATE_LABEL_PASSWORD_RESET_STATE.get();
729              printLabelAndValues(message, opValues);
730              break;
731
732            case OP_GET_SECONDS_UNTIL_PASSWORD_RESET_LOCKOUT:
733              message =
734                  INFO_PWPSTATE_LABEL_SECONDS_UNTIL_PASSWORD_RESET_LOCKOUT
735                      .get();
736              printLabelAndValues(message, opValues);
737              break;
738
739            case OP_GET_GRACE_LOGIN_USE_TIMES:
740              message = INFO_PWPSTATE_LABEL_GRACE_LOGIN_USE_TIMES.get();
741              printLabelAndValues(message, opValues);
742              break;
743
744            case OP_GET_REMAINING_GRACE_LOGIN_COUNT:
745              message = INFO_PWPSTATE_LABEL_REMAINING_GRACE_LOGIN_COUNT.get();
746              printLabelAndValues(message, opValues);
747              break;
748
749            case OP_GET_PASSWORD_CHANGED_BY_REQUIRED_TIME:
750              message =
751                  INFO_PWPSTATE_LABEL_PASSWORD_CHANGED_BY_REQUIRED_TIME.get();
752              printLabelAndValues(message, opValues);
753              break;
754
755            case OP_GET_SECONDS_UNTIL_REQUIRED_CHANGE_TIME:
756              message =
757                  INFO_PWPSTATE_LABEL_SECONDS_UNTIL_REQUIRED_CHANGE_TIME
758                      .get();
759              printLabelAndValues(message, opValues);
760              break;
761
762            case OP_GET_PASSWORD_HISTORY:
763              message = INFO_PWPSTATE_LABEL_PASSWORD_HISTORY.get();
764              printLabelAndValues(message, opValues);
765              break;
766
767            default:
768              message = ERR_PWPSTATE_INVALID_RESPONSE_OP_TYPE.get(opType);
769              printWrappedText(err, message);
770              break;
771          }
772        }
773        reader.readEndSequence();
774        reader.readEndSequence();
775      }
776      catch (Exception e)
777      {
778        printWrappedText(err, ERR_PWPSTATE_CANNOT_DECODE_RESPONSE_MESSAGE.get(getExceptionMessage(e)));
779        return CLIENT_SIDE_SERVER_DOWN;
780      }
781
782      // If we've gotten here, then everything completed successfully.
783      return 0;
784    }
785    finally
786    {
787      // Close the connection to the server if it's active.
788      if (connection != null)
789      {
790        connection.close(nextMessageID);
791      }
792    }
793  }
794
795
796
797  /**
798   * Initializes the argument parser for this tool, parses the provided
799   * arguments, and establishes a connection to the server.
800   *
801   * @param args       Command arguments to parse.
802   * @param initServer Indicates whether to initialize the server.
803   * @return  A result code that indicates the result of the processing.  A
804   *          value of zero indicates that all processing completed
805   *          successfully.  A value of -1 indicates that only the usage
806   *          information was displayed and no further action is required.
807   */
808  private static int parseArgsAndConnect(String[] args, Boolean initServer)
809  {
810    argParser = new SubCommandArgumentParser(
811            CLASS_NAME, INFO_PWPSTATE_TOOL_DESCRIPTION.get(),
812            false);
813    argParser.setShortToolDescription(REF_SHORT_DESC_MANAGE_ACCOUNT.get());
814    argParser.setVersionHandler(new DirectoryServerVersionHandler());
815
816    BooleanArgument   showUsage;
817    BooleanArgument   trustAll;
818    FileBasedArgument bindPWFile;
819    FileBasedArgument keyStorePWFile;
820    FileBasedArgument trustStorePWFile;
821    IntegerArgument   port;
822    StringArgument    bindDN;
823    StringArgument    bindPW;
824    StringArgument    certNickname;
825    StringArgument    host;
826    StringArgument    keyStoreFile;
827    StringArgument    keyStorePW;
828    StringArgument    saslOption;
829    StringArgument    targetDN;
830    StringArgument    trustStoreFile;
831    StringArgument    trustStorePW;
832    BooleanArgument   verbose;
833
834    try
835    {
836      host =
837              StringArgument.builder(OPTION_LONG_HOST)
838                      .shortIdentifier(OPTION_SHORT_HOST)
839                      .description(INFO_PWPSTATE_DESCRIPTION_HOST.get())
840                      .defaultValue("127.0.0.1")
841                      .valuePlaceholder(INFO_HOST_PLACEHOLDER.get())
842                      .buildArgument();
843      argParser.addGlobalArgument(host);
844
845      port =
846              IntegerArgument.builder(OPTION_LONG_PORT)
847                      .shortIdentifier(OPTION_SHORT_PORT)
848                      .description(INFO_PWPSTATE_DESCRIPTION_PORT.get())
849                      .range(1, 65535)
850                      .defaultValue(AdministrationConnector.DEFAULT_ADMINISTRATION_CONNECTOR_PORT)
851                      .valuePlaceholder(INFO_PORT_PLACEHOLDER.get())
852                      .buildArgument();
853      argParser.addGlobalArgument(port);
854
855      bindDN =
856              StringArgument.builder(OPTION_LONG_BINDDN)
857                      .shortIdentifier(OPTION_SHORT_BINDDN)
858                      .description(INFO_PWPSTATE_DESCRIPTION_BINDDN.get())
859                      .valuePlaceholder(INFO_BINDDN_PLACEHOLDER.get())
860                      .buildArgument();
861      argParser.addGlobalArgument(bindDN);
862
863      bindPW =
864              StringArgument.builder(OPTION_LONG_BINDPWD)
865                      .shortIdentifier(OPTION_SHORT_BINDPWD)
866                      .description(INFO_PWPSTATE_DESCRIPTION_BINDPW.get())
867                      .valuePlaceholder(INFO_BINDPWD_PLACEHOLDER.get())
868                      .buildArgument();
869      argParser.addGlobalArgument(bindPW);
870
871      bindPWFile =
872              FileBasedArgument.builder(OPTION_LONG_BINDPWD_FILE)
873                      .shortIdentifier(OPTION_SHORT_BINDPWD_FILE)
874                      .description(INFO_PWPSTATE_DESCRIPTION_BINDPWFILE.get())
875                      .valuePlaceholder(INFO_BINDPWD_FILE_PLACEHOLDER.get())
876                      .buildArgument();
877      argParser.addGlobalArgument(bindPWFile);
878
879      targetDN =
880              StringArgument.builder("targetDN")
881                      .shortIdentifier('b')
882                      .description(INFO_PWPSTATE_DESCRIPTION_TARGETDN.get())
883                      .required()
884                      .valuePlaceholder(INFO_TARGETDN_PLACEHOLDER.get())
885                      .buildArgument();
886      argParser.addGlobalArgument(targetDN);
887
888      saslOption =
889              StringArgument.builder(OPTION_LONG_SASLOPTION)
890                      .shortIdentifier(OPTION_SHORT_SASLOPTION)
891                      .description(INFO_PWPSTATE_DESCRIPTION_SASLOPTIONS.get())
892                      .multiValued()
893                      .valuePlaceholder(INFO_SASL_OPTION_PLACEHOLDER.get())
894                      .buildArgument();
895      argParser.addGlobalArgument(saslOption);
896
897      trustAll = trustAllArgument();
898      argParser.addGlobalArgument(trustAll);
899
900      keyStoreFile =
901              StringArgument.builder(OPTION_LONG_KEYSTOREPATH)
902                      .shortIdentifier(OPTION_SHORT_KEYSTOREPATH)
903                      .description(INFO_PWPSTATE_DESCRIPTION_KSFILE.get())
904                      .valuePlaceholder(INFO_KEYSTOREPATH_PLACEHOLDER.get())
905                      .buildArgument();
906      argParser.addGlobalArgument(keyStoreFile);
907
908      keyStorePW =
909              StringArgument.builder(OPTION_LONG_KEYSTORE_PWD)
910                      .shortIdentifier(OPTION_SHORT_KEYSTORE_PWD)
911                      .description(INFO_PWPSTATE_DESCRIPTION_KSPW.get())
912                      .valuePlaceholder(INFO_KEYSTORE_PWD_PLACEHOLDER.get())
913                      .buildArgument();
914      argParser.addGlobalArgument(keyStorePW);
915
916      keyStorePWFile =
917              FileBasedArgument.builder(OPTION_LONG_KEYSTORE_PWD_FILE)
918                      .shortIdentifier(OPTION_SHORT_KEYSTORE_PWD_FILE)
919                      .description(INFO_PWPSTATE_DESCRIPTION_KSPWFILE.get())
920                      .valuePlaceholder(INFO_KEYSTORE_PWD_FILE_PLACEHOLDER.get())
921                      .buildArgument();
922      argParser.addGlobalArgument(keyStorePWFile);
923
924      certNickname =
925              StringArgument.builder("certNickname")
926                      .shortIdentifier('N')
927                      .description(INFO_DESCRIPTION_CERT_NICKNAME.get())
928                      .valuePlaceholder(INFO_NICKNAME_PLACEHOLDER.get())
929                      .buildArgument();
930      argParser.addGlobalArgument(certNickname);
931
932      trustStoreFile =
933              StringArgument.builder(OPTION_LONG_TRUSTSTOREPATH)
934                      .shortIdentifier(OPTION_SHORT_TRUSTSTOREPATH)
935                      .description(INFO_PWPSTATE_DESCRIPTION_TSFILE.get())
936                      .valuePlaceholder(INFO_TRUSTSTOREPATH_PLACEHOLDER.get())
937                      .buildArgument();
938      argParser.addGlobalArgument(trustStoreFile);
939
940      trustStorePW =
941              StringArgument.builder(OPTION_LONG_TRUSTSTORE_PWD)
942                      .shortIdentifier('T')
943                      .description(INFO_PWPSTATE_DESCRIPTION_TSPW.get())
944                      .valuePlaceholder(INFO_TRUSTSTORE_PWD_PLACEHOLDER.get())
945                      .buildArgument();
946      argParser.addGlobalArgument(trustStorePW);
947
948      trustStorePWFile =
949              FileBasedArgument.builder(OPTION_LONG_TRUSTSTORE_PWD_FILE)
950                      .shortIdentifier(OPTION_SHORT_TRUSTSTORE_PWD_FILE)
951                      .description(INFO_PWPSTATE_DESCRIPTION_TSPWFILE.get())
952                      .valuePlaceholder(INFO_TRUSTSTORE_PWD_FILE_PLACEHOLDER.get())
953                      .buildArgument();
954      argParser.addGlobalArgument(trustStorePWFile);
955
956      verbose = verboseArgument();
957      argParser.addGlobalArgument(verbose);
958
959      showUsage = showUsageArgument();
960      argParser.addGlobalArgument(showUsage);
961      argParser.setUsageArgument(showUsage, out);
962
963
964      HashSet<String> booleanValues = new HashSet<>(2);
965      booleanValues.add(INFO_MULTICHOICE_TRUE_VALUE.get().toString());
966      booleanValues.add(INFO_MULTICHOICE_FALSE_VALUE.get().toString());
967
968
969      LocalizableMessage msg = INFO_DESCRIPTION_PWPSTATE_GET_ALL.get();
970      new SubCommand(argParser, SC_GET_ALL, msg);
971
972      msg = INFO_DESCRIPTION_PWPSTATE_GET_PASSWORD_POLICY_DN.get();
973      new SubCommand(argParser, SC_GET_PASSWORD_POLICY_DN, msg);
974
975      msg = INFO_DESCRIPTION_PWPSTATE_GET_ACCOUNT_DISABLED_STATE.get();
976      new SubCommand(argParser, SC_GET_ACCOUNT_DISABLED_STATE, msg);
977
978      msg = INFO_DESCRIPTION_PWPSTATE_SET_ACCOUNT_DISABLED_STATE.get();
979      SubCommand sc = new SubCommand(argParser, SC_SET_ACCOUNT_DISABLED_STATE,
980                                     msg);
981      sc.addArgument(MultiChoiceArgument.<String>builder(ARG_OP_VALUE)
982              .shortIdentifier('O')
983              .description(INFO_DESCRIPTION_OPERATION_BOOLEAN_VALUE.get())
984              .required()
985              .allowedValues(booleanValues)
986              .valuePlaceholder(INFO_TRUE_FALSE_PLACEHOLDER.get())
987              .buildArgument());
988
989      msg = INFO_DESCRIPTION_PWPSTATE_CLEAR_ACCOUNT_DISABLED_STATE.get();
990      new SubCommand(argParser, SC_CLEAR_ACCOUNT_DISABLED_STATE, msg);
991
992      msg = INFO_DESCRIPTION_PWPSTATE_GET_ACCOUNT_EXPIRATION_TIME.get();
993      new SubCommand(argParser, SC_GET_ACCOUNT_EXPIRATION_TIME, msg);
994
995      msg = INFO_DESCRIPTION_PWPSTATE_SET_ACCOUNT_EXPIRATION_TIME.get();
996      sc = new SubCommand(argParser, SC_SET_ACCOUNT_EXPIRATION_TIME, msg);
997      sc.addArgument(StringArgument.builder(ARG_OP_VALUE)
998              .shortIdentifier('O')
999              .description(INFO_DESCRIPTION_OPERATION_TIME_VALUE.get())
1000              .valuePlaceholder(INFO_TIME_PLACEHOLDER.get())
1001              .buildArgument());
1002      sc.setHidden(true);
1003
1004      msg = INFO_DESCRIPTION_PWPSTATE_CLEAR_ACCOUNT_EXPIRATION_TIME.get();
1005      sc = new SubCommand(argParser, SC_CLEAR_ACCOUNT_EXPIRATION_TIME, msg);
1006      sc.setHidden(true);
1007
1008      msg =
1009              INFO_DESCRIPTION_PWPSTATE_GET_SECONDS_UNTIL_ACCOUNT_EXPIRATION
1010                      .get();
1011      new SubCommand(argParser,
1012              SC_GET_SECONDS_UNTIL_ACCOUNT_EXPIRATION,
1013              msg);
1014
1015      msg = INFO_DESCRIPTION_PWPSTATE_GET_PASSWORD_CHANGED_TIME.get();
1016      new SubCommand(argParser, SC_GET_PASSWORD_CHANGED_TIME, msg);
1017
1018      msg = INFO_DESCRIPTION_PWPSTATE_SET_PASSWORD_CHANGED_TIME.get();
1019      sc = new SubCommand(argParser, SC_SET_PASSWORD_CHANGED_TIME, msg);
1020      sc.addArgument(StringArgument.builder(ARG_OP_VALUE)
1021              .shortIdentifier('O')
1022              .description(INFO_DESCRIPTION_OPERATION_TIME_VALUE.get())
1023              .valuePlaceholder(INFO_TIME_PLACEHOLDER.get())
1024              .buildArgument());
1025      sc.setHidden(true);
1026
1027      msg = INFO_DESCRIPTION_PWPSTATE_CLEAR_PASSWORD_CHANGED_TIME.get();
1028      sc = new SubCommand(argParser, SC_CLEAR_PASSWORD_CHANGED_TIME, msg);
1029      sc.setHidden(true);
1030
1031      msg = INFO_DESCRIPTION_PWPSTATE_GET_PASSWORD_EXPIRATION_WARNED_TIME
1032              .get();
1033      new SubCommand(argParser, SC_GET_PASSWORD_EXP_WARNED_TIME, msg);
1034
1035      msg = INFO_DESCRIPTION_PWPSTATE_SET_PASSWORD_EXPIRATION_WARNED_TIME
1036              .get();
1037      sc = new SubCommand(argParser, SC_SET_PASSWORD_EXP_WARNED_TIME, msg);
1038      sc.addArgument(StringArgument.builder(ARG_OP_VALUE)
1039              .shortIdentifier('O')
1040              .description(INFO_DESCRIPTION_OPERATION_TIME_VALUE.get())
1041              .valuePlaceholder(INFO_TIME_PLACEHOLDER.get())
1042              .buildArgument());
1043      sc.setHidden(true);
1044
1045      msg = INFO_DESCRIPTION_PWPSTATE_CLEAR_PASSWORD_EXPIRATION_WARNED_TIME
1046              .get();
1047      sc = new SubCommand(argParser, SC_CLEAR_PASSWORD_EXP_WARNED_TIME, msg);
1048      sc.setHidden(true);
1049
1050      msg = INFO_DESCRIPTION_PWPSTATE_GET_SECONDS_UNTIL_PASSWORD_EXP.get();
1051      new SubCommand(argParser, SC_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION,
1052                     msg);
1053
1054      msg = INFO_DESCRIPTION_PWPSTATE_GET_SECONDS_UNTIL_PASSWORD_EXP_WARNING
1055              .get();
1056      new SubCommand(argParser,
1057                     SC_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION_WARNING, msg);
1058
1059      msg = INFO_DESCRIPTION_PWPSTATE_GET_AUTH_FAILURE_TIMES.get();
1060      new SubCommand(argParser, SC_GET_AUTHENTICATION_FAILURE_TIMES, msg);
1061
1062      msg = INFO_DESCRIPTION_PWPSTATE_ADD_AUTH_FAILURE_TIME.get();
1063      sc = new SubCommand(argParser, SC_ADD_AUTHENTICATION_FAILURE_TIME,
1064              msg);
1065      sc.addArgument(StringArgument.builder(ARG_OP_VALUE)
1066              .shortIdentifier('O')
1067              .description(INFO_DESCRIPTION_OPERATION_TIME_VALUE.get())
1068              .multiValued()
1069              .valuePlaceholder(INFO_TIME_PLACEHOLDER.get())
1070              .buildArgument());
1071      sc.setHidden(true);
1072
1073      msg = INFO_DESCRIPTION_PWPSTATE_SET_AUTH_FAILURE_TIMES.get();
1074      sc = new SubCommand(argParser, SC_SET_AUTHENTICATION_FAILURE_TIMES,
1075                          msg);
1076      sc.addArgument(StringArgument.builder(ARG_OP_VALUE)
1077              .shortIdentifier('O')
1078              .description(INFO_DESCRIPTION_OPERATION_TIME_VALUES.get())
1079              .multiValued()
1080              .valuePlaceholder(INFO_TIME_PLACEHOLDER.get())
1081              .buildArgument());
1082      sc.setHidden(true);
1083
1084      msg = INFO_DESCRIPTION_PWPSTATE_CLEAR_AUTH_FAILURE_TIMES.get();
1085      sc = new SubCommand(argParser, SC_CLEAR_AUTHENTICATION_FAILURE_TIMES,
1086                          msg);
1087      sc.setHidden(true);
1088
1089      msg = INFO_DESCRIPTION_PWPSTATE_GET_SECONDS_UNTIL_AUTH_FAILURE_UNLOCK
1090              .get();
1091      new SubCommand(argParser,
1092                     SC_GET_SECONDS_UNTIL_AUTHENTICATION_FAILURE_UNLOCK,
1093                     msg);
1094
1095      msg =
1096              INFO_DESCRIPTION_PWPSTATE_GET_REMAINING_AUTH_FAILURE_COUNT.get();
1097      new SubCommand(argParser, SC_GET_REMAINING_AUTHENTICATION_FAILURE_COUNT,
1098                     msg);
1099
1100      msg = INFO_DESCRIPTION_PWPSTATE_GET_LAST_LOGIN_TIME.get();
1101      new SubCommand(argParser, SC_GET_LAST_LOGIN_TIME, msg);
1102
1103      msg = INFO_DESCRIPTION_PWPSTATE_SET_LAST_LOGIN_TIME.get();
1104      sc = new SubCommand(argParser, SC_SET_LAST_LOGIN_TIME, msg);
1105      sc.addArgument(StringArgument.builder(ARG_OP_VALUE)
1106              .shortIdentifier('O')
1107              .description(INFO_DESCRIPTION_OPERATION_TIME_VALUE.get())
1108              .valuePlaceholder(INFO_TIME_PLACEHOLDER.get())
1109              .buildArgument());
1110      sc.setHidden(true);
1111
1112      msg = INFO_DESCRIPTION_PWPSTATE_CLEAR_LAST_LOGIN_TIME.get();
1113      sc = new SubCommand(argParser, SC_CLEAR_LAST_LOGIN_TIME, msg);
1114      sc.setHidden(true);
1115
1116      msg = INFO_DESCRIPTION_PWPSTATE_GET_SECONDS_UNTIL_IDLE_LOCKOUT.get();
1117      new SubCommand(argParser, SC_GET_SECONDS_UNTIL_IDLE_LOCKOUT, msg);
1118
1119      msg = INFO_DESCRIPTION_PWPSTATE_GET_PASSWORD_RESET_STATE.get();
1120      new SubCommand(argParser, SC_GET_PASSWORD_RESET_STATE, msg);
1121
1122      msg = INFO_DESCRIPTION_PWPSTATE_SET_PASSWORD_RESET_STATE.get();
1123      sc = new SubCommand(argParser, SC_SET_PASSWORD_RESET_STATE, msg);
1124      sc.addArgument(MultiChoiceArgument.<String>builder(ARG_OP_VALUE)
1125              .shortIdentifier('O')
1126              .description(INFO_DESCRIPTION_OPERATION_BOOLEAN_VALUE.get())
1127              .required()
1128              .allowedValues(booleanValues)
1129              .valuePlaceholder(INFO_TRUE_FALSE_PLACEHOLDER.get())
1130              .buildArgument());
1131      sc.setHidden(true);
1132
1133      msg = INFO_DESCRIPTION_PWPSTATE_CLEAR_PASSWORD_RESET_STATE.get();
1134      sc = new SubCommand(argParser, SC_CLEAR_PASSWORD_RESET_STATE, msg);
1135      sc.setHidden(true);
1136
1137      msg = INFO_DESCRIPTION_PWPSTATE_GET_SECONDS_UNTIL_RESET_LOCKOUT.get();
1138      new SubCommand(argParser, SC_GET_SECONDS_UNTIL_PASSWORD_RESET_LOCKOUT,
1139                     msg);
1140
1141      msg = INFO_DESCRIPTION_PWPSTATE_GET_GRACE_LOGIN_USE_TIMES.get();
1142      new SubCommand(argParser, SC_GET_GRACE_LOGIN_USE_TIMES, msg);
1143
1144      msg = INFO_DESCRIPTION_PWPSTATE_ADD_GRACE_LOGIN_USE_TIME.get();
1145      sc = new SubCommand(argParser, SC_ADD_GRACE_LOGIN_USE_TIME, msg);
1146      sc.addArgument(StringArgument.builder(ARG_OP_VALUE)
1147              .shortIdentifier('O')
1148              .description(INFO_DESCRIPTION_OPERATION_TIME_VALUE.get())
1149              .multiValued()
1150              .valuePlaceholder(INFO_TIME_PLACEHOLDER.get())
1151              .buildArgument());
1152      sc.setHidden(true);
1153
1154      msg = INFO_DESCRIPTION_PWPSTATE_SET_GRACE_LOGIN_USE_TIMES.get();
1155      sc = new SubCommand(argParser, SC_SET_GRACE_LOGIN_USE_TIMES, msg);
1156      sc.addArgument(StringArgument.builder(ARG_OP_VALUE)
1157              .shortIdentifier('O')
1158              .description(INFO_DESCRIPTION_OPERATION_TIME_VALUES.get())
1159              .multiValued()
1160              .valuePlaceholder(INFO_TIME_PLACEHOLDER.get())
1161              .buildArgument());
1162      sc.setHidden(true);
1163
1164      msg = INFO_DESCRIPTION_PWPSTATE_CLEAR_GRACE_LOGIN_USE_TIMES.get();
1165      sc = new SubCommand(argParser, SC_CLEAR_GRACE_LOGIN_USE_TIMES, msg);
1166      sc.setHidden(true);
1167
1168      msg = INFO_DESCRIPTION_PWPSTATE_GET_REMAINING_GRACE_LOGIN_COUNT.get();
1169      new SubCommand(argParser, SC_GET_REMAINING_GRACE_LOGIN_COUNT,
1170                     msg);
1171
1172      msg = INFO_DESCRIPTION_PWPSTATE_GET_PW_CHANGED_BY_REQUIRED_TIME.get();
1173      new SubCommand(argParser, SC_GET_PASSWORD_CHANGED_BY_REQUIRED_TIME,
1174                     msg);
1175
1176      msg = INFO_DESCRIPTION_PWPSTATE_SET_PW_CHANGED_BY_REQUIRED_TIME.get();
1177      sc = new SubCommand(argParser, SC_SET_PASSWORD_CHANGED_BY_REQUIRED_TIME,
1178                          msg);
1179      sc.addArgument(StringArgument.builder(ARG_OP_VALUE)
1180              .shortIdentifier('O')
1181              .description(INFO_DESCRIPTION_OPERATION_TIME_VALUE.get())
1182              .valuePlaceholder(INFO_TIME_PLACEHOLDER.get())
1183              .buildArgument());
1184      sc.setHidden(true);
1185
1186      msg =
1187              INFO_DESCRIPTION_PWPSTATE_CLEAR_PW_CHANGED_BY_REQUIRED_TIME.get();
1188      sc = new SubCommand(argParser, SC_CLEAR_PASSWORD_CHANGED_BY_REQUIRED_TIME,
1189                          msg);
1190      sc.setHidden(true);
1191
1192      msg =
1193              INFO_DESCRIPTION_PWPSTATE_GET_SECONDS_UNTIL_REQUIRED_CHANGE_TIME
1194                      .get();
1195      new SubCommand(argParser, SC_GET_SECONDS_UNTIL_REQUIRED_CHANGE_TIME,
1196                     msg);
1197
1198      msg = INFO_DESCRIPTION_PWPSTATE_GET_PASSWORD_HISTORY.get();
1199      new SubCommand(argParser, SC_GET_PASSWORD_HISTORY, msg);
1200
1201      msg = INFO_DESCRIPTION_PWPSTATE_CLEAR_PASSWORD_HISTORY.get();
1202      sc = new SubCommand(argParser, SC_CLEAR_PASSWORD_HISTORY, msg);
1203      sc.setHidden(true);
1204    }
1205    catch (ArgumentException ae)
1206    {
1207      printWrappedText(err, ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage()));
1208      return CLIENT_SIDE_LOCAL_ERROR;
1209    }
1210
1211    try
1212    {
1213      argParser.parseArguments(args);
1214    }
1215    catch (ArgumentException ae)
1216    {
1217      argParser.displayMessageAndUsageReference(err, ERR_ERROR_PARSING_ARGS.get(ae.getMessage()));
1218      return CLIENT_SIDE_PARAM_ERROR;
1219    }
1220
1221
1222    // If we should just display usage or version information,
1223    // then exit because it will have already been done.
1224    if (argParser.usageOrVersionDisplayed())
1225    {
1226      return -1;
1227    }
1228
1229
1230    // Get the target DN as a string for later use.
1231    targetDNString = targetDN.getValue();
1232
1233    // Bootstrap and initialize directory data structures.
1234    if (initServer)
1235    {
1236      EmbeddedUtils.initializeForClientUse();
1237    }
1238    // Create the LDAP connection options object, which will be used to
1239    // customize the way that we connect to the server and specify a set of
1240    // basic defaults.
1241    LDAPConnectionOptions connectionOptions = new LDAPConnectionOptions();
1242    connectionOptions.setVersionNumber(3);
1243    connectionOptions.setVerbose(verbose.isPresent());
1244
1245    //  If both a bind password and bind password file were provided, then
1246    // return an error.
1247    if (bindPW.isPresent() && bindPWFile.isPresent())
1248    {
1249      printWrappedText(err,
1250          ERR_PWPSTATE_MUTUALLY_EXCLUSIVE_ARGUMENTS.get(bindPW.getLongIdentifier(), bindPWFile.getLongIdentifier()));
1251      return CLIENT_SIDE_PARAM_ERROR;
1252    }
1253
1254    // If both a key store password and key store password file were provided,
1255    // then return an error.
1256    if (keyStorePW.isPresent() && keyStorePWFile.isPresent())
1257    {
1258      printWrappedText(err, ERR_PWPSTATE_MUTUALLY_EXCLUSIVE_ARGUMENTS.get(
1259          keyStorePW.getLongIdentifier(), keyStorePWFile.getLongIdentifier()));
1260      return CLIENT_SIDE_PARAM_ERROR;
1261    }
1262
1263
1264    // If both a trust store password and trust store password file were
1265    // provided, then return an error.
1266    if (trustStorePW.isPresent() && trustStorePWFile.isPresent())
1267    {
1268      printWrappedText(err, ERR_PWPSTATE_MUTUALLY_EXCLUSIVE_ARGUMENTS.get(
1269          trustStorePW.getLongIdentifier(), trustStorePWFile.getLongIdentifier()));
1270      return CLIENT_SIDE_PARAM_ERROR;
1271    }
1272
1273
1274    // If we should blindly trust any certificate, then install the appropriate
1275    // SSL connection factory.
1276    try {
1277      String clientAlias;
1278      if (certNickname.isPresent()) {
1279        clientAlias = certNickname.getValue();
1280      } else {
1281        clientAlias = null;
1282      }
1283
1284      SSLConnectionFactory sslConnectionFactory = new SSLConnectionFactory();
1285      sslConnectionFactory.init(trustAll.isPresent(), keyStoreFile.getValue(),
1286        keyStorePW.getValue(), clientAlias,
1287        trustStoreFile.getValue(),
1288        trustStorePW.getValue());
1289
1290      connectionOptions.setSSLConnectionFactory(sslConnectionFactory);
1291    } catch (SSLConnectionException sce) {
1292      printWrappedText(err, ERR_PWPSTATE_CANNOT_INITIALIZE_SSL.get(sce.getMessage()));
1293      return CLIENT_SIDE_LOCAL_ERROR;
1294    }
1295
1296
1297    // If one or more SASL options were provided, then make sure that one of
1298    // them was "mech" and specified a valid SASL mechanism.
1299    if (saslOption.isPresent())
1300    {
1301      String             mechanism = null;
1302      LinkedList<String> options   = new LinkedList<>();
1303
1304      for (String s : saslOption.getValues())
1305      {
1306        int equalPos = s.indexOf('=');
1307        if (equalPos <= 0)
1308        {
1309          printWrappedText(err, ERR_PWPSTATE_CANNOT_PARSE_SASL_OPTION.get(s));
1310          return CLIENT_SIDE_PARAM_ERROR;
1311        }
1312        else
1313        {
1314          String name  = s.substring(0, equalPos);
1315
1316          if (name.equalsIgnoreCase("mech"))
1317          {
1318            mechanism = s;
1319          }
1320          else
1321          {
1322            options.add(s);
1323          }
1324        }
1325      }
1326
1327      if (mechanism == null)
1328      {
1329        printWrappedText(err, ERR_PWPSTATE_NO_SASL_MECHANISM.get());
1330        return CLIENT_SIDE_PARAM_ERROR;
1331      }
1332
1333      connectionOptions.setSASLMechanism(mechanism);
1334
1335      for (String option : options)
1336      {
1337        connectionOptions.addSASLProperty(option);
1338      }
1339    }
1340
1341
1342    // Attempt to connect and authenticate to the Directory Server.
1343    nextMessageID = new AtomicInteger(1);
1344    try
1345    {
1346      connection = new LDAPConnection(host.getValue(), port.getIntValue(),
1347                                      connectionOptions, out, err);
1348      connection.connectToHost(bindDN.getValue(),
1349          LDAPConnectionArgumentParser.getPasswordValue(bindPW, bindPWFile,
1350                                                        bindDN, out, err),
1351                               nextMessageID);
1352    }
1353    catch (ArgumentException ae)
1354    {
1355      argParser.displayMessageAndUsageReference(
1356          err, ERR_PWPSTATE_CANNOT_DETERMINE_PORT.get(port.getLongIdentifier(), ae.getMessage()));
1357      return CLIENT_SIDE_PARAM_ERROR;
1358    }
1359    catch (LDAPConnectionException lce)
1360    {
1361      LocalizableMessage message;
1362      if (lce.getCause() != null && lce.getCause().getCause() != null &&
1363        lce.getCause().getCause() instanceof SSLException) {
1364        message = ERR_PWPSTATE_CANNOT_CONNECT_SSL.get(host.getValue(),
1365          port.getValue());
1366      } else {
1367        String hostPort = host.getValue() + ":" + port.getValue();
1368        message = ERR_PWPSTATE_CANNOT_CONNECT.get(hostPort,
1369          lce.getMessage());
1370      }
1371      printWrappedText(err, message);
1372      return CLIENT_SIDE_CONNECT_ERROR;
1373    }
1374
1375    ldapReader = connection.getLDAPReader();
1376    ldapWriter = connection.getLDAPWriter();
1377
1378    return SUCCESS;
1379  }
1380
1381
1382
1383  /**
1384   * Processes the subcommand from the provided argument parser and writes the
1385   * appropriate operation elements to the given writer.
1386   *
1387   * @param  writer The ASN.1 writer used to write the operation elements.
1388   *
1389   * @return  A result code indicating the results of the processing.
1390   */
1391  private static int processSubcommand(ASN1Writer writer) throws IOException
1392  {
1393    SubCommand subCommand = argParser.getSubCommand();
1394    if (subCommand == null)
1395    {
1396      printWrappedText(err, ERR_PWPSTATE_NO_SUBCOMMAND.get());
1397      err.println(argParser.getUsage());
1398      return CLIENT_SIDE_PARAM_ERROR;
1399    }
1400
1401    String subCommandName = subCommand.getName();
1402    if (subCommandName.equals(SC_GET_ALL))
1403    {
1404      // The list should stay empty for this one.
1405    }
1406    else if (subCommandName.equals(SC_GET_PASSWORD_POLICY_DN))
1407    {
1408      encode(writer, OP_GET_PASSWORD_POLICY_DN, NO_VALUE);
1409    }
1410    else if (subCommandName.equals(SC_GET_ACCOUNT_DISABLED_STATE))
1411    {
1412      encode(writer, OP_GET_ACCOUNT_DISABLED_STATE, NO_VALUE);
1413    }
1414    else if (subCommandName.equals(SC_SET_ACCOUNT_DISABLED_STATE))
1415    {
1416      Argument a = subCommand.getArgumentForLongIdentifier(ARG_OP_VALUE);
1417      if (a != null && a.isPresent())
1418      {
1419        String valueStr = a.getValue();
1420        if (isTrueValue(valueStr))
1421        {
1422          encode(writer, OP_SET_ACCOUNT_DISABLED_STATE, "true");
1423        }
1424        else if (isFalseValue(valueStr))
1425        {
1426          encode(writer, OP_SET_ACCOUNT_DISABLED_STATE, "false");
1427        }
1428        else
1429        {
1430          printWrappedText(err, ERR_PWPSTATE_INVALID_BOOLEAN_VALUE.get(valueStr));
1431          return CLIENT_SIDE_PARAM_ERROR;
1432        }
1433      }
1434      else
1435      {
1436        printWrappedText(err, ERR_PWPSTATE_NO_BOOLEAN_VALUE.get());
1437        return CLIENT_SIDE_PARAM_ERROR;
1438      }
1439    }
1440    else if (subCommandName.equals(SC_CLEAR_ACCOUNT_DISABLED_STATE))
1441    {
1442      encode(writer, OP_CLEAR_ACCOUNT_DISABLED_STATE, NO_VALUE);
1443    }
1444    else if (subCommandName.equals(SC_GET_ACCOUNT_EXPIRATION_TIME))
1445    {
1446      encode(writer, OP_GET_ACCOUNT_EXPIRATION_TIME, NO_VALUE);
1447    }
1448    else if (subCommandName.equals(SC_SET_ACCOUNT_EXPIRATION_TIME))
1449    {
1450      Argument a = subCommand.getArgumentForLongIdentifier(ARG_OP_VALUE);
1451      if (a != null && a.isPresent())
1452      {
1453        encode(writer, OP_SET_ACCOUNT_EXPIRATION_TIME, a.getValue());
1454      }
1455      else
1456      {
1457        encode(writer, OP_SET_ACCOUNT_EXPIRATION_TIME, NO_VALUE);
1458      }
1459    }
1460    else if (subCommandName.equals(SC_CLEAR_ACCOUNT_EXPIRATION_TIME))
1461    {
1462      encode(writer, OP_CLEAR_ACCOUNT_EXPIRATION_TIME, NO_VALUE);
1463    }
1464    else if (subCommandName.equals(SC_GET_SECONDS_UNTIL_ACCOUNT_EXPIRATION))
1465    {
1466      encode(writer, OP_GET_SECONDS_UNTIL_ACCOUNT_EXPIRATION, NO_VALUE);
1467    }
1468    else if (subCommandName.equals(SC_GET_PASSWORD_CHANGED_TIME))
1469    {
1470      encode(writer, OP_GET_PASSWORD_CHANGED_TIME, NO_VALUE);
1471    }
1472    else if (subCommandName.equals(SC_SET_PASSWORD_CHANGED_TIME))
1473    {
1474      Argument a = subCommand.getArgumentForLongIdentifier(ARG_OP_VALUE);
1475      if (a != null && a.isPresent())
1476      {
1477        encode(writer, OP_SET_PASSWORD_CHANGED_TIME, a.getValue());
1478      }
1479      else
1480      {
1481        encode(writer, OP_SET_PASSWORD_CHANGED_TIME, NO_VALUE);
1482      }
1483    }
1484    else if (subCommandName.equals(SC_CLEAR_PASSWORD_CHANGED_TIME))
1485    {
1486      encode(writer, OP_CLEAR_PASSWORD_CHANGED_TIME, NO_VALUE);
1487    }
1488    else if(subCommandName.equals(SC_GET_PASSWORD_EXP_WARNED_TIME))
1489    {
1490      encode(writer, OP_GET_PASSWORD_EXPIRATION_WARNED_TIME, NO_VALUE);
1491    }
1492    else if(subCommandName.equals(SC_SET_PASSWORD_EXP_WARNED_TIME))
1493    {
1494      Argument a = subCommand.getArgumentForLongIdentifier(ARG_OP_VALUE);
1495      if (a != null && a.isPresent())
1496      {
1497        encode(writer, OP_SET_PASSWORD_EXPIRATION_WARNED_TIME,
1498                              a.getValue());
1499      }
1500      else
1501      {
1502        encode(writer, OP_SET_PASSWORD_EXPIRATION_WARNED_TIME,
1503                              NO_VALUE);
1504      }
1505    }
1506    else if(subCommandName.equals(SC_CLEAR_PASSWORD_EXP_WARNED_TIME))
1507    {
1508      encode(writer, OP_CLEAR_PASSWORD_EXPIRATION_WARNED_TIME,
1509                            NO_VALUE);
1510    }
1511    else if(subCommandName.equals(SC_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION))
1512    {
1513      encode(writer, OP_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION,
1514                            NO_VALUE);
1515    }
1516    else if(subCommandName.equals(
1517                 SC_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION_WARNING))
1518    {
1519      encode(writer, OP_GET_SECONDS_UNTIL_PASSWORD_EXPIRATION_WARNING,
1520                            NO_VALUE);
1521    }
1522    else if(subCommandName.equals(SC_GET_AUTHENTICATION_FAILURE_TIMES))
1523    {
1524      encode(writer, OP_GET_AUTHENTICATION_FAILURE_TIMES, NO_VALUE);
1525    }
1526    else if(subCommandName.equals(SC_ADD_AUTHENTICATION_FAILURE_TIME))
1527    {
1528      Argument a = subCommand.getArgumentForLongIdentifier(ARG_OP_VALUE);
1529      if (a != null && a.isPresent())
1530      {
1531        encode(writer, OP_ADD_AUTHENTICATION_FAILURE_TIME,
1532                              a.getValue());
1533      }
1534      else
1535      {
1536        encode(writer, OP_ADD_AUTHENTICATION_FAILURE_TIME, NO_VALUE);
1537      }
1538    }
1539    else if(subCommandName.equals(SC_SET_AUTHENTICATION_FAILURE_TIMES))
1540    {
1541      Argument a = subCommand.getArgumentForLongIdentifier(ARG_OP_VALUE);
1542      if (a != null && a.isPresent())
1543      {
1544        ArrayList<String> valueList = new ArrayList<>(a.getValues());
1545        String[] values = new String[valueList.size()];
1546        valueList.toArray(values);
1547
1548        encode(writer, OP_SET_AUTHENTICATION_FAILURE_TIMES, values);
1549      }
1550      else
1551      {
1552        encode(writer, OP_SET_AUTHENTICATION_FAILURE_TIMES, NO_VALUE);
1553      }
1554    }
1555    else if(subCommandName.equals(SC_CLEAR_AUTHENTICATION_FAILURE_TIMES))
1556    {
1557      encode(writer, OP_CLEAR_AUTHENTICATION_FAILURE_TIMES, NO_VALUE);
1558    }
1559    else if(subCommandName.equals(
1560                 SC_GET_SECONDS_UNTIL_AUTHENTICATION_FAILURE_UNLOCK))
1561    {
1562      encode(writer, OP_GET_SECONDS_UNTIL_AUTHENTICATION_FAILURE_UNLOCK,
1563                            NO_VALUE);
1564    }
1565    else if(subCommandName.equals(
1566                 SC_GET_REMAINING_AUTHENTICATION_FAILURE_COUNT))
1567    {
1568      encode(writer, OP_GET_REMAINING_AUTHENTICATION_FAILURE_COUNT,
1569                            NO_VALUE);
1570    }
1571    else if(subCommandName.equals(SC_GET_LAST_LOGIN_TIME))
1572    {
1573      encode(writer, OP_GET_LAST_LOGIN_TIME, NO_VALUE);
1574    }
1575    else if(subCommandName.equals(SC_SET_LAST_LOGIN_TIME))
1576    {
1577      Argument a = subCommand.getArgumentForLongIdentifier(ARG_OP_VALUE);
1578      if (a != null && a.isPresent())
1579      {
1580        encode(writer, OP_SET_LAST_LOGIN_TIME, a.getValue());
1581      }
1582      else
1583      {
1584        encode(writer, OP_SET_LAST_LOGIN_TIME, NO_VALUE);
1585      }
1586    }
1587    else if(subCommandName.equals(SC_CLEAR_LAST_LOGIN_TIME))
1588    {
1589      encode(writer, OP_CLEAR_LAST_LOGIN_TIME, NO_VALUE);
1590    }
1591    else if(subCommandName.equals(SC_GET_SECONDS_UNTIL_IDLE_LOCKOUT))
1592    {
1593      encode(writer, OP_GET_SECONDS_UNTIL_IDLE_LOCKOUT, NO_VALUE);
1594    }
1595    else if(subCommandName.equals(SC_GET_PASSWORD_RESET_STATE))
1596    {
1597      encode(writer, OP_GET_PASSWORD_RESET_STATE, NO_VALUE);
1598    }
1599    else if(subCommandName.equals(SC_SET_PASSWORD_RESET_STATE))
1600    {
1601      Argument a = subCommand.getArgumentForLongIdentifier(ARG_OP_VALUE);
1602      if (a != null && a.isPresent())
1603      {
1604        String valueStr = a.getValue();
1605        if (isTrueValue(valueStr))
1606        {
1607          encode(writer, OP_SET_PASSWORD_RESET_STATE, "true");
1608        }
1609        else if (isFalseValue(valueStr))
1610        {
1611          encode(writer, OP_SET_PASSWORD_RESET_STATE, "false");
1612        }
1613        else
1614        {
1615          printWrappedText(err, ERR_PWPSTATE_INVALID_BOOLEAN_VALUE.get(valueStr));
1616          return CLIENT_SIDE_PARAM_ERROR;
1617        }
1618      }
1619      else
1620      {
1621        printWrappedText(err, ERR_PWPSTATE_NO_BOOLEAN_VALUE.get());
1622        return CLIENT_SIDE_PARAM_ERROR;
1623      }
1624    }
1625    else if(subCommandName.equals(SC_CLEAR_PASSWORD_RESET_STATE))
1626    {
1627      encode(writer, OP_GET_PASSWORD_RESET_STATE, NO_VALUE);
1628    }
1629    else if(subCommandName.equals(SC_GET_SECONDS_UNTIL_PASSWORD_RESET_LOCKOUT))
1630    {
1631      encode(writer, OP_GET_SECONDS_UNTIL_PASSWORD_RESET_LOCKOUT,
1632                            NO_VALUE);
1633    }
1634    else if(subCommandName.equals(SC_GET_GRACE_LOGIN_USE_TIMES))
1635    {
1636      encode(writer, OP_GET_GRACE_LOGIN_USE_TIMES, NO_VALUE);
1637    }
1638    else if(subCommandName.equals(SC_ADD_GRACE_LOGIN_USE_TIME))
1639    {
1640      Argument a = subCommand.getArgumentForLongIdentifier(ARG_OP_VALUE);
1641      if (a != null && a.isPresent())
1642      {
1643        encode(writer, OP_ADD_GRACE_LOGIN_USE_TIME, a.getValue());
1644      }
1645      else
1646      {
1647        encode(writer, OP_ADD_GRACE_LOGIN_USE_TIME, NO_VALUE);
1648      }
1649    }
1650    else if(subCommandName.equals(SC_SET_GRACE_LOGIN_USE_TIMES))
1651    {
1652      Argument a = subCommand.getArgumentForLongIdentifier(ARG_OP_VALUE);
1653      if (a != null && a.isPresent())
1654      {
1655        ArrayList<String> valueList = new ArrayList<>(a.getValues());
1656        String[] values = new String[valueList.size()];
1657        valueList.toArray(values);
1658
1659        encode(writer, OP_SET_GRACE_LOGIN_USE_TIMES, values);
1660      }
1661      else
1662      {
1663        encode(writer, OP_SET_GRACE_LOGIN_USE_TIMES, NO_VALUE);
1664      }
1665    }
1666    else if(subCommandName.equals(SC_CLEAR_GRACE_LOGIN_USE_TIMES))
1667    {
1668      encode(writer, OP_CLEAR_GRACE_LOGIN_USE_TIMES, NO_VALUE);
1669    }
1670    else if(subCommandName.equals(SC_GET_REMAINING_GRACE_LOGIN_COUNT))
1671    {
1672      encode(writer, OP_GET_REMAINING_GRACE_LOGIN_COUNT, NO_VALUE);
1673    }
1674    else if(subCommandName.equals(SC_GET_PASSWORD_CHANGED_BY_REQUIRED_TIME))
1675    {
1676      encode(writer, OP_GET_PASSWORD_CHANGED_BY_REQUIRED_TIME,
1677                            NO_VALUE);
1678    }
1679    else if(subCommandName.equals(SC_SET_PASSWORD_CHANGED_BY_REQUIRED_TIME))
1680    {
1681      Argument a = subCommand.getArgumentForLongIdentifier(ARG_OP_VALUE);
1682      if (a != null && a.isPresent())
1683      {
1684        encode(writer, OP_SET_PASSWORD_CHANGED_BY_REQUIRED_TIME,
1685                              a.getValue());
1686      }
1687      else
1688      {
1689        encode(writer, OP_SET_PASSWORD_CHANGED_BY_REQUIRED_TIME,
1690                              NO_VALUE);
1691      }
1692    }
1693    else if(subCommandName.equals(SC_CLEAR_PASSWORD_CHANGED_BY_REQUIRED_TIME))
1694    {
1695      encode(writer, OP_CLEAR_PASSWORD_CHANGED_BY_REQUIRED_TIME,
1696                            NO_VALUE);
1697    }
1698    else if(subCommandName.equals(SC_GET_SECONDS_UNTIL_REQUIRED_CHANGE_TIME))
1699    {
1700      encode(writer, OP_GET_SECONDS_UNTIL_REQUIRED_CHANGE_TIME,
1701                            NO_VALUE);
1702    }
1703    else if (subCommandName.equals(SC_GET_PASSWORD_HISTORY))
1704    {
1705      encode(writer, OP_GET_PASSWORD_HISTORY, NO_VALUE);
1706    }
1707    else if (subCommandName.equals(SC_CLEAR_PASSWORD_HISTORY))
1708    {
1709      encode(writer, OP_CLEAR_PASSWORD_HISTORY, NO_VALUE);
1710    }
1711    else
1712    {
1713      printWrappedText(err, ERR_PWPSTATE_INVALID_SUBCOMMAND.get(subCommandName));
1714      err.println(argParser.getUsage());
1715      return CLIENT_SIDE_PARAM_ERROR;
1716    }
1717
1718    return SUCCESS;
1719  }
1720
1721
1722
1723  /**
1724   * Prints information about a password policy state variable to standard
1725   * output.
1726   *
1727   * @param  msg     The message ID for the message to use as the label.
1728   * @param  values  The set of values for the associated state variable.
1729   */
1730  private static void printLabelAndValues(LocalizableMessage msg, ArrayList<String> values)
1731  {
1732    String label = String.valueOf(msg);
1733    if (values == null || values.isEmpty())
1734    {
1735      out.print(label);
1736      out.println(":");
1737    }
1738    else
1739    {
1740      for (String value : values)
1741      {
1742        out.print(label);
1743        out.print(":  ");
1744        out.println(value);
1745      }
1746    }
1747  }
1748
1749  private static boolean isTrueValue(String value)
1750  {
1751    return INFO_MULTICHOICE_TRUE_VALUE.get().toString().equalsIgnoreCase(value);
1752  }
1753
1754  private static boolean isFalseValue(String value)
1755  {
1756    return INFO_MULTICHOICE_FALSE_VALUE.get().toString().equalsIgnoreCase(
1757        value);
1758  }
1759}
1760