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