/*******************************************************************************

 * $Novell: ModifyPassword.java,v 1.20 2003/08/21 11:36:58 $

 * Copyright (C) 1999, 2000, 2001 Novell, Inc. All Rights Reserved.

 *

 * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND

 * TREATIES. USE AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO THE LICENSE

 * AGREEMENT ACCOMPANYING THE SOFTWARE DEVELOPMENT KIT (SDK) THAT CONTAINS

 * THIS WORK. PURSUANT TO THE SDK LICENSE AGREEMENT, NOVELL HEREBY GRANTS TO

 * DEVELOPER A ROYALTY-FREE, NON-EXCLUSIVE LICENSE TO INCLUDE NOVELL'S SAMPLE

 * CODE IN ITS PRODUCT. NOVELL GRANTS DEVELOPER WORLDWIDE DISTRIBUTION RIGHTS

 * TO MARKET, DISTRIBUTE, OR SELL NOVELL'S SAMPLE CODE AS A COMPONENT OF

 * DEVELOPER'S PRODUCTS. NOVELL SHALL HAVE NO OBLIGATIONS TO DEVELOPER OR

 * DEVELOPER'S CUSTOMERS WITH RESPECT TO THIS CODE.

 *

 * $name:           ModifyPassword.java

 * $description:    The ModifyPassword.java sample shows how to modify your own

 *                  password, giving both the old and new password.

 *

 *                  Unless the caller has admin privileges, Novell eDirectory

 *                  requires both the old and new passwords in order to change

 *                  a password.

 ******************************************************************************/

import com.novell.ldap.*;

import com.novell.ldap.client.Debug;



import arguments.*;



import java.security.Security;

import java.text.ParseException;

import java.io.UnsupportedEncodingException;



public class ModifyPassword 

{

    public final int BIND_TIMEOUT = 20000; // Timeout value for bind - 20 secs

    

    public static void main( String[] args ) 

    {        

        System.exit(new ModifyPassword().runPassword(args));

    }



    private int runPassword(String[] args)

    {

        LDAPConnection lc;

        // Process command line arguments

        Options options = null;

        try {

            options = new Options();

            options.parse(args);

        } catch( ParseException e) {

            // Print addional info from the exception

            String errorMessage = e.getMessage();

            if( errorMessage == null) {

                errorMessage = e.toString();

            }

            // get the usage message

            errorMessage = options.usage(errorMessage);



            // Display the command line arguments

            displayArguments( options, args);

            // Display the Usage mesage

            System.err.println( errorMessage);

            return 1;

        } catch( Exception e) {

            // Error building options, we just have the exception

            System.err.println("Exception: " + e.toString());

            e.printStackTrace();

            return 1;

        }



        // Debug option, if set turn on trace

        if( options.debug()) {

            // Set Trace PrintStream

            Debug.setTraceStream( System.err);

            // Trace everything

            Debug.setTrace( "TraceAll", true);

        }



        String keyPath = null;

        String type = options.getConnectionType();

        if( (keyPath = options.getKeystore()) != null) {

            LDAPSocketFactory ssf;

            // Dynamically set JSSE as a security provider

            Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());



            // Dynamically set the property that JSSE uses to identify

            // the keystore that holds trusted root certificates

            if( options.getPrintVerbose()) {

                System.err.println("Java key store is \"" + keyPath + "\"");

            }

            System.setProperty("javax.net.ssl.trustStore", keyPath);



            // Initialize the socket factory

            if( type.equalsIgnoreCase("TLS")) {

                if( options.getPrintVerbose()) {

                    System.err.println("Setting factory for a TLS connection");

                }

                ssf = new LDAPJSSEStartTLSFactory(); // tls

            } else {

                if( options.getPrintVerbose()) {

                    System.err.println("Setting factory for a SSL connection");

                }

                ssf = new LDAPJSSESecureSocketFactory();

            }



            // Set the socket factory as the default for all future connections

            LDAPConnection.setSocketFactory(ssf);

        }

        lc = new LDAPConnection();



        try {

            if( options.getPrintVerbose()) {

                System.err.println("Connecting to host \"" + options.getHostPort() + "\"");

            }

            // Connect to server.  Note if host is format host:port, then

            // the port parameter is ignored.

            lc.connect(options.getHostPort(), 0);



            if( (keyPath != null) && (type.equalsIgnoreCase("TLS")) ) {

                lc.startTLS();

            }



            // Get loginDN

            String loginDN = options.getLoginDN();



            // Avoid bind overhead if anonymous bind

            if( loginDN.length() != 0) {

                LDAPConstraints cons = new LDAPConstraints();

                // Set timeout secs on bind so don't hang on ssl socket

                cons.setTimeLimit( BIND_TIMEOUT);

                lc.bind(3, loginDN,

                        options.getLoginPasswd().getBytes("UTF8"), cons);

            }



            LDAPModification[] modifications = new LDAPModification[2];



            /* To modify your password:

             *   -- Specify the password attribute to modify

             *   -- Specify the DELETE option to delete the old password

             *   -- Specify the ADD option to add the new password

             *   -- Add these to the array of modifications

             *   -- Call LDAPConnection modify() method to delete your old

             *         password and add your new password

             */

            LDAPAttribute deletePassword = new LDAPAttribute( "userPassword",

                                                options.getLoginPasswd());

            modifications[0] =

                new LDAPModification( LDAPModification.DELETE, deletePassword);



            LDAPAttribute addPassword = new LDAPAttribute( "userPassword",

                                                options.getNewPasswd());

            modifications[1] =

                new LDAPModification( LDAPModification.ADD, addPassword);



            lc.modify( loginDN, modifications);



            System.out.println("Your password has been modified.");



            // disconnect with the server

            lc.disconnect();

        }

        catch( LDAPException e ) {

            if ( e.getResultCode() == LDAPException.NO_SUCH_OBJECT ) {

                System.err.println( "Error: No such entry" );

            } else {

                System.err.println( "Error: " + e.toString() );

            }

            System.err.println("Your password has not been modified.");

        }

        catch( UnsupportedEncodingException e ) {

            System.out.println( "Error: " + e.toString() );

        }        

        return 0;

    }

    

    /**

     * Display the command line arguments

     */

    private

    void displayArguments( Options options, String[] args)

    {

        if( options.getPrintVerbose()) {

            System.err.println("\nCommand line arguments");

            for( int i=0; i < args.length; i++) {

                System.err.println("   " + i + ": " + args[i]);

            }

        }

        return;

    }



    /**

     * This class defines the command line options and arguments

     * and provides methods to get their values.

     *

     * The argument parsing is handled by the ApplicationArguments

     * and Argument classes.

     */

    private

    class Options

    {

        private ApplicationArguments options;

        private Options()

            throws Exception

        {

            // Read and verify the options

            super();



            options = new ApplicationArguments("java ModifyPassword", 25);

            // Add all the options to the ApplicationArguments object



            options.add( new Argument(

                        'd',                            // -d option (Boolean)

                        "enable API debug output",      // description string

                        Argument.SINGLE_VALUED));       // single valued



            options.add( new Argument(

                        'e',                            // -e option (String)

                        "keystore",                     // name

                                                        // description string

               "Path to a Java Keystore.  A valid certificate in the keystore enables\n" +

               "          an encrypted TLS connection.  See also the -Z option.",

                        "",                             // no default value

                        Argument.SINGLE_VALUED));       // single valued



            options.add( new Argument(                  //

                        'h',                            // -h option

                        "host",                         // name

                                                        // description string

                        "host name or IP address.  A port can  be specified with the\n" +

                        "          host name as hostname:port, i.e. myhost:389.  See also \n" +

                        "          the -p option",

                        "localhost",                    // default value

                        Argument.SINGLE_VALUED));       // single valued



            options.add( new Argument(

                        'p',                            // -p option (Integer)

                        "port",                         // name

                        "host IP port number.  See also the -h option",          // description string

                        LDAPConnection.DEFAULT_PORT,    // default value

                        Argument.SINGLE_VALUED));       // single valued



            options.add( new Argument(

                        'v',                            // -v option (Boolean)

                        "enable verbose output",        // description string

                        false));                        // default value



            options.add( new Argument(

                        'Z',                            // -e option (String)

                        "encrypted connection",         // name

               "sets the type of encrypted connection.  A Keystore must be specified\n" +

               "            with the -e option to enable an encrypted connection.\n" +

               "            SSL   - Establishes an encrypted connection using \n" +

               "                    SSL.  The default port is 636\n" +

               "            TLS   - Establishes an encrypted connection using \n" +

               "                    TLS.  The default port is 389",

                        "TLS",                          // default value

                        Argument.SINGLE_VALUED));       // single valued

                        

            // Positional Parameter            

            options.add( new Argument(

                        Argument.REQUIRED_STRING,

                        "bind DN",                      // name

                        "the DN of the object used for authentication",// description string

                        Argument.SINGLE_VALUED));       // single valued

                        

            options.add( new Argument(

                        Argument.REQUIRED_STRING,

                        "current password",             // name

                        "the current password value used for authentication",   // description string

                        Argument.SINGLE_VALUED));       // single valued

                        

            options.add( new Argument(

                        Argument.REQUIRED_STRING,

                        "new password",                 // name

                        "the new password value",       // description string

                        Argument.SINGLE_VALUED));       // single valued

                        

            return;

        }



        /**

         * Parses the array of arguments passed in to the program

         *

         * @param args the argument string passed to main

         */

        /* protected */

        void parse( String[] args)

                throws Exception

        {

            options.parse(args);

            return;

        }



        // Get the option values

        /**

         * gets host_name:port_value

         *

         * @return the host:port value.  The Default port is 389

         */

        /* protected */

        String getHostPort()

        {

            String host;

            int port;

            try {

                host=(String)(options.getArgument('h').getValue());

                Argument arg = options.getArgument('p');

                int cnt = arg.getValueCount();

                if( cnt == 0) {

                    // Default port, varies whether SSL, TLS, or clear

                    port=((Integer)arg.getValue()).intValue();

                    String type = getConnectionType();

                    if( type.equalsIgnoreCase("SSL")) {

                        port = LDAPConnection.DEFAULT_SSL_PORT;

                    }

                } else {

                    // App specified port, use it

                    port=((Integer)arg.getValue()).intValue();

                }

            } catch( NoSuchFieldException e) {

                throw new RuntimeException(e.toString());

            }

            // If user specified port, use it, otherwise use port value

            if( host.indexOf(":") == -1) {

                host = host + ":" + port;

            }

            return host;

        }



        /**

         * gets encrypted connection type

         *

         * @return the encrypted connection type - TLS or SSL

         */

        /* protected */

        String getConnectionType()

        {

            String type = null;

            try {

                type = (String)(options.getArgument('Z').getValue());

                if( type.equalsIgnoreCase("SSL") ) {

                    ;

                } else

                if( type.equalsIgnoreCase("TLS") ) {

                    ;

                } else {

                        throw new NoSuchFieldException(

                            "Invalid connection type specified: " + type);

                }

            } catch( NoSuchFieldException e) {

                throw new RuntimeException(e.toString());

            }

            return type;

        }



        /**

         * Get the path to the Java Keystore

         *

         * @return the path to the keystore, or null if none

         */

        /* protected */

        String getKeystore()

        {

            try {

                Argument arg = options.getArgument('e');

                int size = arg.getValueCount();

                if( size == 0)

                    return null;

                return (String)arg.getValue();

            } catch( NoSuchFieldException e) {

                throw new RuntimeException(e.toString());

            }

        }



        /**

         * gets Login DN.

         *

         * @return the loginDN.  Default "".

         */

        /* protected */

        String getLoginDN()

        {

            try {

                return (String)(options.getArgument(1).getValue());

            } catch( NoSuchFieldException e) {

                throw new RuntimeException(e.toString());

            }

        }



        /**

         * gets Login password.

         *

         * @return the password.  Default "".

         */

        /* protected */

        String getLoginPasswd()

        {

            try {

                return (String)(options.getArgument(2).getValue());

            } catch( NoSuchFieldException e) {

                throw new RuntimeException(e.toString());

            }

        }



        /**

         * gets new password.

         *

         * @return the password.  Default "".

         */

        /* protected */

        String getNewPasswd()

        {

            try {

                return (String)(options.getArgument(3).getValue());

            } catch( NoSuchFieldException e) {

                throw new RuntimeException(e.toString());

            }

        }



        /**

         * gets the print verbose option

         *

         * @return the print verbose option.  Default false.

         */

        /* protected */

        boolean getPrintVerbose()

        {

            try {

                return ((Boolean)

                    (options.getArgument('v').getValue())).booleanValue();

            } catch( NoSuchFieldException e) {

                throw new RuntimeException(e.toString());

            }

        }



        /**

         * gets the setting for the debug option

         *

         * @return the debug option.  Default false.

         */

        /* protected */

        boolean debug()

        {

            try {

                return ((Boolean)

                    (options.getArgument('d').getValue())).booleanValue();

            } catch( NoSuchFieldException e) {

                throw new RuntimeException(e.toString());

            }

        }



        /**

         * gets the usage String

         *

         * @return the usage String.

         */

        /* protected */

        String usage( String msg)

        {

            return options.usage( msg);

        }

    }

}

