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

 * $Novell: GetDSE.java,v 1.25 2003/08/21 11:33:21 $

 * 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:         GetDSE.java

 * $description:  GetDSE.java demonstrates how to get root DSE. For known

 *                extensions and controls, a lookup table is used to print

 *                a descriptive name.

 * $sample run:

 *                Attributes:

 *                    supportedSASLMechanisms

 *                       EXTERNAL

 *                    supportedLDAPVersion

 *                       2

 *                       3

 *                    supportedExtension

 *                        2.16.840.1.113719.1.27.100.55

 *                            (Trigger Partition Purge Request)

 *                        2.16.840.1.113719.1.27.100.56

 *                            (Trigger Partition Purge Response)

 *                        2.16.840.1.113719.1.27.100.53

 *                            (Trigger Schema Sync Request)

 *                       ....

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

import java.security.Security;

import java.text.ParseException;

import java.util.Enumeration;

import java.util.Iterator;

import java.io.UnsupportedEncodingException;



import com.novell.ldap.*;

import com.novell.ldap.client.Debug;



import arguments.*;



public class GetDSE implements LDAPOIDs{



    private int len = NamesAndOIDs.length;

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



    public static void main( String[] args )

    {

        System.exit( new GetDSE().runDSE(args));

    }



    private int runDSE( String[] args)

    {

        String         returnedAttributes[] = {"*", "+"};

        String         oid, value;

        boolean        attributeOnly = false;

        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);

            }



           /* To search for root DSE,

            *    1. Set LDAP version to LDAP_V3 before binding

            *    2. Set the search base to an empty string

            *    3. Set the search filter to (objectclass=*)

            *    4. Set the search scope to LDAP_SCOPE_BASE

            */





            LDAPSearchResults searchResults = lc.search(

                                        "",

                                        LDAPConnection.SCOPE_BASE,

                                        "(objectclass=*)",

                                        returnedAttributes,

                                        attributeOnly     );



            /*  The search returns one entry in the search results, and

             *  it is the root DSE.

             */



            LDAPEntry entry = null;



            try {

                entry = searchResults.next();

            }

            catch(LDAPException e) {

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

            }



            System.out.println("\n" + entry.getDN());

            System.out.println("    Attributes: ");



            LDAPAttributeSet attributeSet = entry.getAttributeSet();

            Iterator allAttributes = attributeSet.iterator();



            while(allAttributes.hasNext()) {

                LDAPAttribute attribute = (LDAPAttribute)allAttributes.next();

                String attrName = attribute.getName();



                System.out.println("        " + attrName);

                Enumeration allValues = attribute.getStringValues();



                if( allValues != null) {

                    while(allValues.hasMoreElements()) {

                        if ( (attrName.equalsIgnoreCase("supportedExtension"))

                           ||(attrName.equalsIgnoreCase("supportedControl"))) {

                           oid = (String) allValues.nextElement();

                           System.out.print("          " + oid);

                           getOidInfo(oid);

                        }

                        else {

                            value = (String) allValues.nextElement();

                            System.out.println("            " + value);

                        }

                    }

                }

            }

        }

        catch( LDAPException e ) {

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

        }

        catch( UnsupportedEncodingException e ) {

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

        }

        try {

                lc.disconnect();

        }

        catch( Exception e ) {

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

            System.exit(1);

        }

        return 0;

    }



    public void getOidInfo( String value ) {



        String descriptiveName="";



        for ( int i = 0; i < len; i++ ) {

            if ( value.equalsIgnoreCase(NamesAndOIDs[i][OIDValueIndex]) ) {

                descriptiveName = NamesAndOIDs[i][OIDDescrIndex];

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

                    System.out.print( "  (" + descriptiveName + ")\n" );

                    break;

                }

                else

                    System.out.println("");

            }

        }

        if (descriptiveName.length() == 0) {

            System.out.print("\n");

        }

    }



    /**

     * 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 GetDSE", 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(                  //

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

                        "bind DN",                      // name

                        "the DN of the object used to bind",// description string

                        "",                             // default

                        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(                  //

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

                        "password",                     // name

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

                        "",                             // default

                        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

            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('D').getValue());

            } catch( NoSuchFieldException e) {

                throw new RuntimeException(e.toString());

            }

        }



        /**

         * clears Login DN value

         */

        /* protected */

        void clearLoginDN()

        {

            try {

                options.getArgument('D').clearValues();

                return;

            } catch( NoSuchFieldException e) {

                throw new RuntimeException(e.toString());

            }

        }



        /**

         * gets Login password.

         *

         * @return the password.  Default "".

         */

        /* protected */

        String getLoginPasswd()

        {

            try {

                return (String)(options.getArgument('w').getValue());

            } catch( NoSuchFieldException e) {

                throw new RuntimeException(e.toString());

            }

        }



        /**

         * clears Login password value

         */

        /* protected */

        void clearLoginPassword()

        {

            try {

                options.getArgument('w').clearValues();

                return;

            } 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 {

                // We don't use debugging level for now

                return( options.hasArgument('d'));

            } 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);

        }

    }

}

