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