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

 * $Novell: SearchUtil.java,v 1.26 2005/02/17 13:23:16 $

 * Copyright (c) 2001 - 2002 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:         SearchUtil.java

 *

 * $description:  SearchUtil is a generalized search utility.  It allows

 *                specification from the command line of any or all of the

 *                parameters associated with Constraints, SearchConstraints,

 *                and the search method of LDAPConnection.

 *

 *                It has a multitude of options to specify these parameters.

 *                To simplify the parsing of the options, it uses a

 *                generalized Java command line parsing class

 *                "ApplicationArguments" which contains the parsed command

 *                line arguments as "Argument" classes.

 *

 *                The option handling specific to this utility is encapsulated

 *                in an "Options" class with get methods for each option.

 *

 *                This utility demonstrates the four mechanisms for handling

 *                referrals & search references in a java application.

 *                It uses search to demonstrate the effects of handling the

 *                referral in various ways.

 *

 *                The four ways to handling referrals are:

 *                OFF    - Set Automatic Referral following to off.  The API

 *                         returns the referral to the application.  It is up

 *                         to the application to take some action.  This

 *                         utility prints the referral information.

 *                NO     - Set Automatic Referral following to on, but register

 *                         NO referral handler.  The API creates a new

 *                         connection and uses it to follow the referral

 *                         using anonymous credentials (does not bind to

 *                         to the connection).

 *                AUTH   - Set Automatic Referral following to on, and register

 *                         an LDAPAuthHandler ReferralHandler class.  When a

 *                         referral is received, the API creates a

 *                         new connection, performs a simple bind using

 *                         credentials supplied by the LDAPAuthProvider class,

 *                         and uses the new connection to follow the referral.

 *                BIND   - Set Automatic Referral following to on, and register

 *                         an LDAPBindHandler ReferalHandler class. When a

 *                         referral is received, the LDAPBindHandler class

 *                         creates a new connection, and binds using mechanism

 *                         and credentials of its choosing (in this case, a

 *                         simple bind and the orignal login password). The

 *                         LDAPBindHandler manages its own connections and is

 *                         responsible for connection reuse and release.  The

 *                         API uses the new connection to follow the referral.

 *

 * Note: all search results are returned to System.out.  All other information,

 * including verbose display goes to System.err.

 *

 * The following is a typical usage statement for this utility as generated

 * automatically by the ApplicationArguments class.  Note: By default,

 * only the count of objects returned from the search is displayed.

 *

 *------------------------------------------------------------------------------

 *

 * Usage: jldapsearch [ -Adv ] [ -a <alias> ] [ -b <baseDN> ]

 *      [ -C <referralHandling> ] [ -D <bind DN> ] [ -e <keystore> ]

 *      [ -h <host> ] [ -l <server timeout> ] [ -L <export format> ]

 *      [ -m <max results> ] [ -M <control> ] [ -p <port> ] [ -q <queue size> ]

 *      [ -r <referral hop limit> ] [ -s <scope> ] [ -t <client timeout> ]

 *      [ -w <password> ] [ -Z <encrypted connection> ]

 *      [  <filter> [  <attributes> ] ]

 * 

 *     -a    Alias dereferencing

 *             NEVER     - do not dereference aliases in 

 *                         searching or in locating the base

 *                         object of the search

 *             SEARCHING - dereference aliases in subordinates

 *                         of the base object in searching, 

 *                         but not in locating the base

 *                         object of the search

 *             FINDING   - dereference aliases in locating the

 *                         base object of the search, but not

 *                         when searching subordinates of the

 *                         base object

 *             ALWAYS    - dereference aliases both in

 *                         searching and in locating the base

 *                         object of the search. - Default="NEVER"

 *     -A    retrieve attribute names only (no values) - Default=false

 *     -b    the base DN for the search - Default=""

 *     -C    Follow refeerals, OFF | ON | AUTH | BIND

 *             OFF    - Referral following turned off

 *             NO     - Follow with NO referral handler (anonymous)

 *             AUTH   - Follow with AUTH referral handler

 *             BIND   - Follow with BIND referral handler - Default="OFF"

 *     -d    enable API debug output - Default=false

 *     -D    the DN of the object used for authentication - Default=""

 *     -e    Path to a Java Keystore.  A valid certificate in the keystore enables

 *           an encrypted TLS connection.  See also the -Z option. - Default=""

 *     -h    host name or IP address.  A port can  be specified with the

 *           host name as hostname:port, i.e. myhost:389.  See also 

 *           the -p option - Default="localhost"

 *     -l    server time limit in seconds to complete the search

 *             0 = no limit - Default=0

 *     -L    Export format - LDIF|DSML

 *             DSML - output in DSML V2.0 format

 *             LDIF - output in LDIF V1.0 format

 *             TEXT - output in descriptive text formt

 *             Note: selecting DSML or LDIF forces the

 *                   -a and -c options to be set - Default="TEXT"

 *     -m    maximum number of entries for server to return - Default=1000

 *     -M    a control to include with the search.  Any control specified

 *           will be sent as critical, i.e., the operation will fail

 *           if the server is unwilling to perform the control with the

 *           search operation.

 *             ManageDsaIT - Causes Directory-specific entries,

 *                           regardless of type, to be treated as

 *                           normal entries

 *             PSearch     - A simple change notification mechanism.

 *                           When set, changed entries are returned

 *                           until the application is interrupted,

 *                           or STOP is entered from the standard input

 *                           stream

 *           You specify multiple controls by preceding each control with

 *           the -M option.  A "+" sign on the front of the control name

 *           designates it as critical, i.e. "-M +Sort".  Psearch is

 *           is always considered critical. - Default=""

 *     -p    host IP port number.  See also the -h option - Default=389

 *     -q    number of entries to queue before the search

 *            results are displayed - Default=1

 *     -r    the maximum number of referral hops allowed before

 *             the operation fails - Default=10

 *     -s    Search Scope - BASE|ONE|SUB

 *             BASE   - Base level search

 *             ONE    - One level search

 *             SUB    - Subtree search - Default="ONE"

 *     -t    client message timeout in milliseconds

 *              0 = no timeout - Default=0

 *     -v    enable verbose output - Default=false

 *     -w    the password value used for authentication - Default=""

 *     -Z    sets the type of encrypted connection.  A Keystore must be specified

 *             with the -e option to enable an encrypted connection.

 *             SSL   - Establishes an encrypted connection using 

 *                     SSL.  The default port is 636

 *             TLS   - Establishes an encrypted connection using 

 *                     TLS.  The default port is 389 - Default="TLS"

 * <filter>  an LDAP search filter - Default="(objectclass=*)"

 * <attributes> only the the named attributes will be displayed in the results.

 *           You specify multiple attributes by separating each attribute

 *           description with a space. An attribute description may include:

 *             1.1 - no attributes

 *             *   - all user attributes

 *             +   - all informational attribute

 *           Note: You must specify a filter if you specify any attribute

 *                 descriptions - Default="*"

 *

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



import com.novell.ldap.LDAPAttribute;

import com.novell.ldap.LDAPAttributeSet;

import com.novell.ldap.LDAPAuthHandler;

import com.novell.ldap.LDAPAuthProvider;

import com.novell.ldap.LDAPBindHandler;

import com.novell.ldap.LDAPConnection;

import com.novell.ldap.LDAPControl;

import com.novell.ldap.LDAPEntry;

import com.novell.ldap.LDAPException;

import com.novell.ldap.LDAPJSSEStartTLSFactory;

import com.novell.ldap.LDAPJSSESecureSocketFactory;

import com.novell.ldap.LDAPReferralException;

import com.novell.ldap.LDAPReferralHandler;

import com.novell.ldap.LDAPSearchConstraints;

import com.novell.ldap.LDAPSearchResults;

import com.novell.ldap.LDAPSocketFactory;

import com.novell.ldap.LDAPUrl;

import com.novell.ldap.controls.LDAPPersistSearchControl;

import com.novell.ldap.controls.LDAPSortKey;

import com.novell.ldap.client.Debug;

import com.novell.ldap.util.LDAPWriter;

import com.novell.ldap.util.LDIFWriter;

import com.novell.ldap.util.DSMLWriter;



import arguments.*;



import java.io.BufferedReader;

import java.io.FileDescriptor;

import java.io.FileOutputStream;

import java.io.InputStreamReader;

import java.io.UnsupportedEncodingException;

import java.net.MalformedURLException;

import java.security.Security;

import java.util.Hashtable;

import java.util.Enumeration;

import java.text.ParseException;



public class SearchUtil extends Thread

{

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

    public final int FORMAT_LDIF =  9; // Export in LDIF Format

    public final int FORMAT_DSML = -9; // Export in DSML Format

    public final int FORMAT_TEXT =  0; // No Export Format, use descriptive text

    public final String DSAIT_OID = "2.16.840.1.113730.3.4.2";

    public final String PSEARCH_OID = "2.16.840.1.113730.3.4.3";

    public final String SORT_OID = "1.2.840.113556.1.4.473";

    private LDAPSearchResults searchResults;

    private LDAPConnection conn;



    /**

     * Disallow external construction of a Search object

     */

    private SearchUtil()

    {

        return;

    }



    /**

     * Main - create a Search object, issue the search, and exit when it returns

     */

    public

    static void main( String[] args )

    {

        System.exit(new SearchUtil().runSearch( args));

    }



    /**

     * The main workhorse for the Search application.  It interprets the

     * options, sets up the constraints including referral handling,

     * issues the search, and displays the results.

     *

     * @param args the command line arguments

     *

     * @return 0 for success, 1 for failure

     */

    private

    int runSearch( String[] args)

    {



        int exfmt = FORMAT_TEXT;

        LDAPWriter writer = null;

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

        }



        // Set up the constraints for this connection

        // Note: getReferralHandler creates an appropriate ReferralHandler

        // class (may be null).  If created, this class will be the

        // only holder of authentication data.



        LDAPSearchConstraints cons = new LDAPSearchConstraints(

                options.getTimeout(),               // client timeout

                options.getServerTimeLimit(),       // server timeout

                options.getAliasDereference(),      // alias dereference

                options.getMaxSearchResults(),      // max search results

                options.getReferralFollowing(),     // referral following

                options.getBatchSize(),             // results batch size

                options.getReferralHandler(),       // referral handler

                options.getReferralHopLimit());     // referral hop limit



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

        }

        conn = new LDAPConnection();



        // Save both the constraints and the search constraints

        // Sets timeout for bind and connect

        conn.setConstraints( cons );



        int entryCount = 0; // Counter for number of entries read

        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.

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



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

                conn.startTLS();

            }



            // Get loginDN

            String loginDN = options.getLoginDN();



            // Avoid bind overhead if anonymous bind

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

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

                cons.setTimeLimit( BIND_TIMEOUT);

                conn.bind(3, loginDN,

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

            }



            // Set any controls for the search

            cons = conn.getSearchConstraints();

            cons.setControls( options.getControls());

            conn.setConstraints( cons);



            // Report on the values for the constraints

            displaySearchConstraints( options, cons);



            // no longer need our copy of the constraints

            cons = null;



            // Eliminate unnecessary copies of the DN & password

            // Now, only the BindHandler has credentials

            options.clearLoginDN();

            options.clearLoginPassword();



            // Display authentication status

            displayAuthentication( options, conn);



            // Display Search Parameters

            displaySearchParameters( options);



            // Start reader thread if Persistent Search is set

            if( options.getPersistentSearch()) {

                Thread r = new Thread(this);

                r.setDaemon(true); // If this is the last thread running, allow exit.

                r.start();

                System.err.println("\nMonitoring changes. Enter a 'STOP' to quit: ");

            }



            // Search the directory

            searchResults = conn.search(

                        options.getSearchBase(),      // DN where search starts

                        options.getSearchScope(),     // Search scope

                        options.getSearchFilter(),    // Search Filter

                        options.getAttributeNames(),  // Attribute Names

                        options.getTypesOnly());      // Types only



            /**

             * Determine if outputting into LDIF or DSML and setup writer

             */

            // Currently all output goes to standard out.

            FileOutputStream fos = new FileOutputStream(FileDescriptor.out);

            exfmt = options.getExportFormat();

            if( exfmt == FORMAT_LDIF) {

                writer = new LDIFWriter(fos);

            } else

            if( exfmt == FORMAT_DSML) {

                fos = new FileOutputStream(FileDescriptor.out);

                writer = new DSMLWriter(fos);

                ((DSMLWriter)writer).setIndent(3);

                ((DSMLWriter)writer).useIndent(true);

            }





            /**

             * Print out the search results

             *   -- The first while loop goes through all the entries

             *    -- The second while loop goes through all the attributes

             *    -- The third while loop goes through all the attribute values

             */

            LDAPEntry entry;

            while ( searchResults.hasMore()) {

                try {

                    entry = searchResults.next();

                    entryCount++;       // Only count entries we actually get

                }

                catch(LDAPException e) {

                    // Referral handling is off, or could not follow referral

                    displayException( options, e);

                    continue; // Caught exception, go for next entry

               }



               if( exfmt != FORMAT_TEXT) {

                    writer.writeEntry(entry);

                    continue;

               }

               // Display the DN of the entry

               if( options.getPrintDN() || options.getPrintAttrs()) {

                   String dn = entry.getDN();

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

                       dn = "\"\"";

                   }

                   System.out.println("\n" + dn);

               }

               // Display the attribute names and values

               if( options.getPrintAttrs()) {

                   System.out.println("\tAttributes: ");



                   LDAPAttributeSet attributeSet = entry.getAttributeSet();

                   java.util.Iterator allAttributes = attributeSet.iterator();



                   while( allAttributes.hasNext()) {

                      LDAPAttribute attribute =

                                     (LDAPAttribute)allAttributes.next();

                      String attributeName = attribute.getName();



                      System.out.println("\t\t" + attributeName);



                      Enumeration allValues = attribute.getStringValues();



                      // Print attribute values

                      // This assumes String values, but Java will create

                      // something printable for even binary data, though

                      // it may not mean much.

                      if( allValues != null) {

                         while(allValues.hasMoreElements()) {

                            String Value = (String) allValues.nextElement();

                            System.out.println("\t\t\t" + Value);

                         }

                      }

                   }

               }

            }

        } catch( LDAPException e ) {

            // One of the operations failed

            displayException( options, e);

        } catch( Exception ex) {

            // Some other exception

            displayException( options, ex);

        } finally {

            if( exfmt != FORMAT_TEXT) {

                try {

                    writer.finish();

                    // Since output stream is standard out, don't close it.

                } catch( Exception ex) {

                    ; // ignore

                }

            }

        }



        // Display count of entries found

        System.err.println( entryCount + " Entries found");



        // If we are still connected, disconnect

        if ( conn.isConnected() ) {

            try {

               conn.disconnect();

            } catch ( LDAPException e ) {

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

            }

        }

        return 0;

    }



    public void run()

    {

        BufferedReader in

            = new BufferedReader(new InputStreamReader(System.in));



        try {   //loop until the user quits by entering 'STOP'

            String input;

            while (true) {

                input = in.readLine();

                if ( ! input.equalsIgnoreCase("STOP")) {

                        System.err.println(

                        "\nSearchUtil: input ignored during persistent search: "

                                + input);

                        continue;

                }

                // Stopping search

                break;

            }

        }

        catch (Exception e) {

            System.out.println(e.getMessage());

        }

        System.err.println("\nSearchUtil: stopping persistent search");



        // Abandon search, causes read loop to stopo

        try {

            if( searchResults == null) {

                // in case not yet returned from search

                conn.disconnect();

            } else {

                conn.abandon(searchResults); //abandon the search

            }

        }

        catch( LDAPException e ) {

            System.out.println();

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

        }

        // End reader thread

        return;

    }



    /**

     * A simple implementation of the referral handler LDAPAuthHandler

     *<p>

     * This implementation uses the commandline specified login dn & password

     * as authentication credentials.

     */

    private class AuthImpl implements LDAPAuthHandler

    {

        // The dn used to authenticate

        private String dn;

        // The password used to authenticate

        private byte[] passwd;

        // Flag used to enable verbose printing

        private boolean printVerbose;



        /**

         * Construct this instance using data from the command line options

         */

        private AuthImpl( Options options)

        {



            // Create an LDAPAuthProvider class to encapsulate the

            // authentication credentials.

            dn = options.getLoginDN();

            printVerbose = options.getPrintVerbose();

            try {

                passwd = options.getLoginPasswd().getBytes("UTF8");

            } catch ( UnsupportedEncodingException ex) {

                // Should never get this

                System.out.println( "Unable to encode password to UTF-8: " +

                                                              ex.toString());

                System.exit(1);

            }

            return;

        }



        /**

         * This method is allowed to choose the login dn & password based

         * on the host String & port, but this implementation does not do so

         */

        public LDAPAuthProvider getAuthProvider( String host, int port)

        {

            // Return the LDAPAuthProvider Class

            if( printVerbose) {

                System.err.println("\nReBind to host " + host + ":" + port);

            }

            return new LDAPAuthProvider( dn, passwd);

        }

    }



    /**

     * An implementation of the referral handler LDAPReferralHandler.

     *<p>

     * This class returns an authenticated LDAPConnection back to

     * the API.  The API uses that connection to follow a referral.

     *<p>

     * The API attempts to reuse connections.  It scans the referral

     * list for existing connections and will reuse connections

     * if a host:port match is found in the referral list.

     */

    private class BindImpl implements LDAPBindHandler

    {

        // A list of the connections being used for following referrals

        // It is hashed on host:port

        private Hashtable connections;

        // The dn used to authenticate

        private String dn;

        // The password used to authenticate

        private byte[] passwd;

        // Flag used to enable verbose printing

        private boolean printVerbose;



        /**

         * Construct the BindImpl class

         */

        private BindImpl( Options options)

        {

            connections = new Hashtable();

            dn = options.getLoginDN();

            printVerbose = options.getPrintVerbose();

            try {

                passwd = options.getLoginPasswd().getBytes("UTF8");

            } catch ( UnsupportedEncodingException ex) {

                // Should never get this

                System.out.println( "Unable to encode password to UTF-8: " +

                                                              ex.toString());

                System.exit(1);

            }

            return;

        }



        /**

         * the bind method is called by the API.  This method is

         * responsible to create an authenticated connection that

         * the API can use to follow the referral.

         */

        public LDAPConnection bind( String[] urls, LDAPConnection conn)

            throws LDAPReferralException

        {

            LDAPReferralException ex = null;

            LDAPConnection newconn = null;

            LDAPUrl url;

            String host;



            // Check if original connection is in the hash table, if not add it

            host = conn.getHost() + ":" + conn.getPort();

            if( (LDAPConnection)connections.get( host) == null) {

                connections.put( host, conn);

            }



            // Report what URLs were received on referral/reference

            // and what Connection the referrals originated from.

            if( printVerbose) {

                System.err.println("\nBind: Referral contains " + urls.length +

                        " URLs");

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

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

                }

                System.err.println("Bind: Original host is " + host);

            }



            // Check if any URL matches a known authenticated connection

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

                try {

                    url = new LDAPUrl( urls[i]);

                    host = url.getHost() + ":" + conn.getPort();

                    if((newconn=(LDAPConnection)connections.get( host))==null) {

                        continue;

                    } else

                    if( printVerbose) {

                        System.err.println(

                            "Bind: Using existing connection to host " + host);

                    }

                    ex = null;

                    break;

                } catch( Throwable e) {

                    newconn = null; // just try the next one

                }

            }





            if( newconn == null) {

                // We found no existing connections, so make a new one

                // Try to connect/bind to one of the referrals in the list.



                // Use socket factory from original connection

                newconn = new LDAPConnection( conn.getSocketFactory());



                // copy the Constraints from the original connection

                LDAPSearchConstraints cons = conn.getSearchConstraints();



                // Copy original constraints to new Connection object

                newconn.setConstraints( cons);      // Sets Constraints



                // Disallow referral following, and set timelimit on bind

                cons.setReferralFollowing(false);

                cons.setTimeLimit(BIND_TIMEOUT);



                // Loop through the referrals in the list and use

                // the first one that we can connect and bind to.

                //

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



                    // Convert the Referral String to a URL

                    try {

                        url = new LDAPUrl( urls[i]);

                    } catch( MalformedURLException e) {

                        continue;

                    }

                    // We now have the host and port

                    host = url.getHost() + ":" + url.getPort();

                    try {

                        // Connect to server.

                        // Note: if host is format host:port, then the port

                        // parameter is ignored.

                        newconn.connect( host,0);



                        if( conn.isTLS()) {

                            newconn.startTLS();

                        }

                        // Skip bind, if anonymous

                        if( conn.isBound()) {

                            String method = conn.getAuthenticationMethod();

                            // Note: cannot get method "none" as we are bound

                            if( method.equals("simple")) {

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

                                newconn.bind( conn.getProtocolVersion(),

                                      dn, passwd, cons);

                            } else

                            if( method.equals("sasl")) {

                                throw new LDAPException( "Bind: " + method +

                                       " not implemented",

                                       LDAPException.AUTH_UNKNOWN,(String)null);

                            } else {

                                throw new LDAPException( "Bind: " + method +

                                       " unknown",

                                       LDAPException.AUTH_UNKNOWN,(String)null);

                            }

                            if( printVerbose) {

                                System.err.println(

                                  "Bind: Successful " + method +

                                   " Bind to host " + host);

                            }

                        } else {

                            if( printVerbose) {

                                System.err.println(

                                  "Bind: Anonymous connection to host " + host);

                            }

                        }



                        // Add new connecction to hash table

                        connections.put( host, newconn);

                        ex = null;

                        break;

                    } catch( Throwable e) {

                        // Bind operation did not succeed.

                        //Note: connect also disconnects any previous connection

                        try {

                            newconn.disconnect();

                        } catch( LDAPException dex) {

                            ;

                        }

                        System.err.println(

                                    "Bind: Exception on  Bind to host " +

                                    host + " - " + e.toString());



                        if( e instanceof LDAPReferralException) {

                            // Already an LDAPReferralException

                            ex = (LDAPReferralException)e;

                        } else {

                            // Set up an appropriate LDAPReferralException

                            // When thrown to the API, the API sets referral list

                            ex = new LDAPReferralException(

                                    "Bind: Could not follow referrals", e);

                            ex.setFailedReferral(urls[i]);



                            if( printVerbose) {

                                if( ! (e instanceof LDAPException)) {

                                    e.printStackTrace();

                                }

                            }

                        }

                    }

                }

            }



            if( ex != null) {

                // Failed to follow the referral

                newconn = null;  // Let GC clean up connection object

                if( printVerbose) {

                    System.err.println(

                       "Bind: Throwing LDAPReferralException " + ex.toString());

                }

                throw ex;

            }



            return newconn;

        }

    }



    /**

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

    }



    /**

     * Display the values set in the LDAPSearchConstraints object

     * Values are displayed only if verbose printing is enabled

     */

    private

    void displaySearchConstraints( Options options, LDAPSearchConstraints cons)

    {

        if( options.getPrintVerbose()) {

            System.err.println("Constraints Values");

            System.err.print("    Search alias dereferencing is ");

            switch( cons.getDereference())

            {

            case LDAPSearchConstraints.DEREF_NEVER:

                System.err.println("DEREF_NEVER");

                break;

            case LDAPSearchConstraints.DEREF_FINDING:

                System.err.println("DEREF_FINDING");

                break;

            case LDAPSearchConstraints.DEREF_SEARCHING:

                System.err.println("DEREF_SEARCHING");

                break;

            case LDAPSearchConstraints.DEREF_ALWAYS:

                System.err.println("DEREF_ALWAYS");

                break;

            }

            System.err.println("    Search batch size is " +

                    cons.getBatchSize());

            System.err.println("    Search max results is " +

                    cons.getMaxResults());

            System.err.println("    Search server time limit is " +

                    cons.getServerTimeLimit() + " seconds");

            System.err.println("    API request time limit is " +

                    cons.getTimeLimit() + " milliseconds");

            System.err.println("    Referral following is " +

                    cons.getReferralFollowing());

            if( cons.getReferralFollowing()) {

                LDAPReferralHandler rh = options.getReferralHandler();

                if( rh == null) {

                    System.err.println("    Referral handler not set");

                } else {

                    System.err.print("    Referral handler is ");

                    if( rh instanceof LDAPBindHandler) {

                        System.err.println("LDAPBindHandler");

                    } else {

                        System.err.println("LDAPAuthHandler");

                    }

                    System.err.println("    Referral hop limit set to " +

                            cons.getHopLimit());

                }

            }



            if( cons.getControls() != null) {

                LDAPControl[] controls = cons.getControls();

                StringBuffer msg = new StringBuffer("Controls:\n");

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

                    String oid = controls[i].getID();

                    if( oid.equals(DSAIT_OID)) {

                        msg.append( "    ManageDsaIT\n");

                    } else

                    if( oid.equals(PSEARCH_OID)) {

                        msg.append( "    Persistent Search\n");

                    /*

                    } else

                    if( oid.equals(SORT_OID)) {

                        msg.append( "    Server Side Sort\n");

                        LDAPSortKey[] keys = options.getSortKeys();

                        for(int j=0; j < keys.length; j++) {

                            msg.append( "        Key: " + keys[j].getKey() +

                               ", order " +

                               (keys[j].getReverse()?"descending":"ascending"));

                            String rule = keys[j].getMatchRule();

                            if( rule != null) {

                                msg.append( ", matching rule " + rule);

                            }

                            msg.append("\n");

                        }

                    */

                    } else {

                        msg.append( "    " + controls[i].getID() + "\n");

                    }

                }

                System.err.print(msg.toString());

            }

        }

        return;

    }



    /**

     * Display the values set in the LDAPSearchConstraints object

     * Values are displayed only if verbose printing is enabled

     */

    private

    void displayAuthentication( Options options, LDAPConnection conn)

    {

        // Display authentication status

        if( options.getPrintVerbose()) {

            if( conn.isBound()) {

                System.err.println(

                        "Application is authenticated to server as " +

                        conn.getAuthenticationDN());

            } else {

                System.err.println(

                        "Application authentication is anonymous");

            }

        }

        return;

    }





    /**

     * Display the values used for the search operation

     */

    private

    void displaySearchParameters( Options options)

    {

        if( options.getPrintVerbose()) {

            System.err.println("Search Parameters");

            System.err.println("    Search Base: \"" +

                    options.getSearchBase() + "\"");

            System.err.print("    Search Scope: ");

            int scope = options.getSearchScope();

            switch( scope) {

                case LDAPConnection.SCOPE_SUB:

                    System.err.println("SUB_TREE");

                    break;

                case LDAPConnection.SCOPE_BASE:

                    System.err.println("BASE");

                    break;

                case LDAPConnection.SCOPE_ONE:

                    System.err.println("ONE_LEVEL");

                    break;

                case LDAPConnection.SCOPE_SUBORDINATESUBTREE:

                    System.err.println("SUBORDINATE_SUBTREE");

                    break;

            }

            System.err.println("    Search Filter: \"" +

                    options.getSearchFilter() + "\"");

            String[] attrs = options.getAttributeNames();

            if(  attrs.length == 1) {

                System.err.println("    Attribute Name: \"" +

                        attrs[0] + "\"");

            } else {

                System.err.println("    Attribute Names:");

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

                    System.err.println("        \"" + attrs[i] + "\"");

                }

            }

            System.err.println("    Typesonly: " + options.getTypesOnly());

        }

        return;

    }



    /**

     * Display exception information

     */

    private

    void displayException( Options options, Exception ex)

    {

        if( options.debug()) {

            ex.printStackTrace();

        } else

        if( ex instanceof RuntimeException) {

            ex.printStackTrace();

            System.exit(1);

        } else {

            System.err.println("\n" + ex.toString());

        }

        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 SearchUtil", 25);

            // Add all the options to the ApplicationArguments object



            options.add( new Argument(

               'a',                                     // -i option (String)

               "alias",                                 // name

               "Alias dereferencing\n" +                // description string

               "            NEVER     - do not dereference aliases in \n" +

               "                        searching or in locating the base\n" +

               "                        object of the search\n" +

               "            SEARCHING - dereference aliases in subordinates\n" +

               "                        of the base object in searching, \n" +

               "                        but not in locating the base\n" +

               "                        object of the search\n" +

               "            FINDING   - dereference aliases in locating the\n" +

               "                        base object of the search, but not\n" +

               "                        when searching subordinates of the\n" +

               "                        base object\n" +

               "            ALWAYS    - dereference aliases both in\n" +

               "                        searching and in locating the base\n" +

               "                        object of the search.",

               "NEVER",                                 // default value

               Argument.SINGLE_VALUED));                // single valued



            options.add( new Argument(

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

                        "retrieve attribute names only (no values)",  // description string

                        false));                        // default value



            options.add( new Argument(

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

                        "baseDN",                       // name

                        "the base DN for the search",   // description string

                        "",                             // default value (root)

                        Argument.SINGLE_VALUED));       // single valued



            options.add( new Argument(

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

               "referralHandling",                      // name

                                                        // description string

               "Follow refeerals, OFF | ON | AUTH | BIND\n" +

               "            OFF    - Referral following turned off\n" +

               "            NO     - Follow with NO referral handler (anonymous)\n" +

               "            AUTH   - Follow with AUTH referral handler\n" +

               "            BIND   - Follow with BIND referral handler",

               "OFF",                                   // default value

               Argument.SINGLE_VALUED));                // single valued



            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 for authentication",// 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(

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

                        "server timeout",               // name

                                                        // description string

                        "server time limit in seconds to complete the search\n" +

                        "            0 = no limit",

                        0,                              // default value

                        Argument.SINGLE_VALUED));       // single valued



            options.add( new Argument(

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

                        "export format",                // name

                                                        // description string

                        "Export format - LDIF|DSML\n"  +

                        "            DSML - output in DSML V2.0 format\n" +

                        "            LDIF - output in LDIF V1.0 format\n" +

                        "            TEXT - output in descriptive text formt\n" +

                        "            Note: selecting DSML or LDIF forces the\n" +

                        "                  -a and -c options to be set",

                        "TEXT",                         // standard text output

                        Argument.SINGLE_VALUED));       // single valued



            options.add( new Argument(

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

                        "max results",                  // name

                                                        // description string

                        "maximum number of entries for server to return",

                        1000,                           // default value

                        Argument.SINGLE_VALUED));       // single valued



            options.add( new Argument(

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

               "control",                               // name

               "a control to include with the search.  Any control specified\n" +

               "          will be sent as critical, i.e., the operation will fail\n" +

               "          if the server is unwilling to perform the control with the\n" +

               "          search operation.\n" +

               "            ManageDsaIT - Causes Directory-specific entries,\n" +

               "                          regardless of type, to be treated as\n" +

               "                          normal entries\n" +

               "            PSearch     - A simple change notification mechanism.\n" +

               "                          When set, changed entries are returned\n" +

               "                          until the application is interrupted,\n" +

               "                          or STOP is entered from the standard input\n" +

               "                          stream\n" +

               /*

               "            Sort        - A sort of the entries by the server before\n" +

               "                          they are returned to the application.  See \n" +

               "                          the -S option to specify sort keys.\n" +

               */

               "          You specify multiple controls by preceding each control with\n" +

               "          the -M option.  A \"+\" sign on the front of the control name\n" +

               "          designates it as critical, i.e. \"-M +Sort\".  Psearch is\n" +

               "          is always considered critical.",

                        null,                           // default value, none

                        Argument.MULTI_VALUED));        // multi 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(

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

                        "queue size",                    // name

                        "number of entries to queue before the search\n" +

                        "           results are displayed", // description string

                        1,                              // default value

                        Argument.SINGLE_VALUED));       // single valued



            options.add( new Argument(

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

                        "referral hop limit",           // name

                        "the maximum number of referral hops allowed before\n" +

                        "            the operation fails", // description string

                        10,                             // default value

                        Argument.SINGLE_VALUED));       // single valued



            options.add( new Argument(

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

                        "scope",                        // name

                                                        // description string

                        "Search Scope - BASE|ONE|SUB\n" +

                        "            BASE   - Base level search\n" +

                        "            ONE    - One level search\n" +

                        "            SUB    - Subtree search",

                        "ONE",                          // default value

                        Argument.SINGLE_VALUED));       // single valued



            /*

            options.add( new Argument(

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

               "sort key",                              // name

               "specifies a sort key for use with the "-M sort" option. The key\n" +

               "          consists of an attribute name, the value of which determines\n" +

               "          order when sorting. If the key is preceded by a \"-\" (minus sign),\n" +

               "          the sort will be done in reverse (descending) order. An OID\n" +

               "          for a matching rule may be appended following a \":\".\n\n" +

               "          Example:\n" +

               "            \"-S cn\" (sorts in ascending order by the cn attribute)\n" +

               "            \"-S -cn:1.2.3.4.5.6\" (sorts in descending order by\n" +

               "                   the cn attribute using matching rule 1.2.3.4.5.6)\n" +

               "          You specify multiple sort keys by preceding each key with\n" +

               "          the -S option",

                          "cn",                        // default value

               Argument.MULTI_VALUED));                // multi valued

               */



            options.add( new Argument(

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

                        "client timeout",               // name

                                                        // description string

                        "client message timeout in milliseconds\n" +

                        "             0 = no timeout",

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

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

                        "password",                     // name

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

                        "",                             // default

                        Argument.SINGLE_VALUED));       // single valued



            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 Arguments

            options.add( new Argument(                  // Positional Attribute 1

                        "filter",                       // name

                        "an LDAP search filter",        // description string

                        "(objectclass=*)",              // default

                        Argument.SINGLE_VALUED));       // single valued



            options.add( new Argument(

                        "attributes",                   // name

                                                        // description string

             "only the the named attributes will be displayed in the results.\n" +

             "          You specify multiple attributes by separating each attribute\n" +

             "          description with a space. An attribute description may include:\n" +

             "            1.1 - no attributes\n" +

             "            *   - all user attributes\n" +

             "            +   - all informational attribute\n" +

             "          Note: You must specify a filter if you specify any attribute\n" +

             "                descriptions",

             "*",                                       // default

                        Argument.MULTI_VALUED));        // multi 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 the timeout value

         *

         * @return the timeout value in milliseconds.  Default 0.

         */

        /* protected */

        int getTimeout()

        {

            try {

                return ((Integer)

                    (options.getArgument('t').getValue())).intValue();

            } catch( NoSuchFieldException e) {

                throw new RuntimeException(e.toString());

            }

        }



        /**

         * gets search base.

         *

         * @return the DN used to start the search. Default "".

         */

        /* protected */

        String getSearchBase()

        {

            try {

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

            } catch( NoSuchFieldException e) {

                throw new RuntimeException(e.toString());

            }

        }



        /**

         * gets search parameter - typesOnly

         *

         * @return the typesonly value

         */

        /* protected */

        boolean getTypesOnly()

        {

            return (getPrintAttrs() == false) &&

                   (getAttrValues() == false) &&

                   (getExportFormat() == 0);

        }



        /**

         * gets search filter

         *

         * @return the search filter.  Default "(objectclass=*)"

         */

        /* protected */

        String getSearchFilter()

        {

            try {

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

            } catch( NoSuchFieldException e) {

                throw new RuntimeException(e.toString());

            }

        }



        /**

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

        }



        /**

         * Returns true if the persistent search control is set

         *

         * @return true if persistent search is specified, otherwise false

         */

        /* protected */

        boolean getPersistentSearch()

        {

            try {

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

                Enumeration e = arg.getValues();

                while( e.hasMoreElements()) {

                    if( "PSearch".equalsIgnoreCase((String)e.nextElement())) {

                        return true;

                    }

                }

                return false;

            } catch( NoSuchFieldException e) {

                throw new RuntimeException(e.toString());

            }

        }



        /**

         * gets names of controls to set

         *

         * @return the controls, or null if none

         */

        /* protected */

        LDAPControl[] getControls()

        {

            try {

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

                Enumeration e = arg.getValues();

                int size = arg.getValueCount();

                // None specified

                if( size == 0) {

                    return null;

                }

                // If none, size is zero

                LDAPControl[] controls = new LDAPControl[size];

                int i = 0;

                while( e.hasMoreElements()) {

                    String name = (String)e.nextElement();

                    if( name.equalsIgnoreCase("ManageDsaIT") ) {

                        controls[i++] = new LDAPControl( DSAIT_OID, false, null);

                    } else

                    if( name.equalsIgnoreCase("+ManageDsaIT") ) {

                        controls[i++] = new LDAPControl( DSAIT_OID, true, null);

                    } else

                    if( name.equalsIgnoreCase("PSearch") ) {

                        controls[i++] = new LDAPPersistSearchControl();

                    } else

                    if( name.equalsIgnoreCase("+PSearch") ) {

                        controls[i++] = new LDAPPersistSearchControl();

                    /*

                    } else

                    if( name.equalsIgnoreCase("Sort") ) {

                        controls[i++] = new LDAPSortControl(getSortKeys(),false);

                    } else

                    if( name.equalsIgnoreCase("+Sort") ) {

                        controls[i++] = new LDAPSortControl(getSortKeys(),true);

                    */

                    } else {

                        throw new NoSuchFieldException(

                            "Invalid Control specified: " + name);

                    }

                }

                return controls;

            } catch( NoSuchFieldException e) {

                throw new RuntimeException(e.toString());

            }

        }



        /**

         * gets keys for the sort control

         *

         * @return the keys, or null if none

         */

        /* protected */

        /*

        LDAPSortKey[] getSortKeys()

        {

            try {

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

                Enumeration e = arg.getValues();

                int size = arg.getValueCount();

                // None specified

                if( size == 0) {

                    LDAPSortKey[] keys = new LDAPSortKey[1];

                    keys[0] = new LDAPSortKey("cn");

                    return keys;

                }

                // If none, size is zero

                LDAPSortKey[] keys = new LDAPSortKey[size];

                int i = 0;

                while( e.hasMoreElements()) {

                    String name = (String)e.nextElement();

                    keys[i++] = new LDAPSortKey( name);

                }

                return keys;

            } catch( NoSuchFieldException e) {

                throw new RuntimeException(e.toString());

            }

        }

        */



        /**

         * 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 names of attributes to retrieve

         *

         * @return the attribute names

         */

        /* protected */

        String[] getAttributeNames()

        {

            try {

                String[] attrs;

                // get number of positional argument values

                Argument arg;

                arg = options.getArgument(2);

                int cnt = arg.getValueCount();

                if( cnt == 0) {

                    cnt =1; // use default value

                }

                attrs = new String[cnt];

                Enumeration values = arg.getValues();

                int i=0;

                while(values.hasMoreElements()) {

                    attrs[i++] = (String)values.nextElement();

                }

                return attrs;

            } catch( NoSuchFieldException e) {

                throw new RuntimeException(e.toString());

            }

        }



        /**

         * gets search scope

         *

         * @return one of BASE, ONE, or SUB.  Default BASE.

         */

        /* protected */

        int getSearchScope()

        {

            int sscope = 0;

            try {

                String scope = (String)(options.getArgument('s').getValue());

                if( scope.equalsIgnoreCase("SUB") ) {

                    sscope = LDAPConnection.SCOPE_SUB;

                } else

                if( scope.equalsIgnoreCase("BASE") ) {

                    sscope = LDAPConnection.SCOPE_BASE;

                } else

                if( scope.equalsIgnoreCase("ONE") ) {

                    sscope = LDAPConnection.SCOPE_ONE;

                } else {

                    throw new NoSuchFieldException("Invalid Search Scope Argument");

                }

            } catch( NoSuchFieldException e) {

                throw new RuntimeException(e.toString());

            }

            return sscope;

        }



        /**

         * gets export format

         *

         * @return one of FORMAT_LDIF, FORMAT_DSML, FORMAT_TEXT.

         *         Default value FORMAT_NONE

         */

        /* protected */

        int getExportFormat()

        {

            int fmt = FORMAT_TEXT;

            try {

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

                if( arg.getValueCount() == 0) {

                    return fmt;

                }

                String format = (String)(arg.getValue());

                if( format.equalsIgnoreCase("DSML") ) {

                    fmt = FORMAT_DSML;

                } else

                if( format.equalsIgnoreCase("LDIF") ) {

                    fmt = FORMAT_LDIF;

                } else

                if( format.equalsIgnoreCase("TEXT") ) {

                    fmt = FORMAT_TEXT;

                } else {

                    throw new NoSuchFieldException("Invalid Export Format Argument");

                }

            } catch( NoSuchFieldException e) {

                throw new RuntimeException(e.toString());

            }

            return fmt;

        }



        /**

         * gets the method used to follow referrals

         *

         * @return one of OFF, ON, AUTH, or BIND.  Default value OFF

         */

        /* protected */

        LDAPReferralHandler getReferralHandler( )

        {

            LDAPReferralHandler rh = null;

            try {

                // Get referral handling options

                String referString = (String)

                        (options.getArgument('C').getValue());

                if( referString.equalsIgnoreCase("NO") ) {

                    ; // rh already set to null

                } else

                if( referString.equalsIgnoreCase("ON") ) {

                    ; // rh already set to null

                } else

                if( referString.equalsIgnoreCase("BIND") ) {

                    rh = new BindImpl( this);

                } else

                if( referString.equalsIgnoreCase("AUTH") ) {

                    rh = new AuthImpl( this);

                } else

                if( referString.equalsIgnoreCase("REBIND") ) { // deprecated

                    rh = new AuthImpl( this);

                } else

                if( referString.equalsIgnoreCase("OFF") ) {

                    ; // rh already set to null

                } else {

                    throw new NoSuchFieldException(

                            "Invalid Referral Following Argument: \"" +

                            referString + "\"");

                }

            } catch( NoSuchFieldException e) {

                throw new RuntimeException(e.toString());

            }

            return rh;

        }



        /**

         * gets referral following option

         *

         * @return one of OFF, ON, REBIND, or BIND.  Default value OFF

         */

        /* protected */

        boolean getReferralFollowing( )

        {

            boolean        follow = false;

            try {

                // Get referral handling options

                String referString = (String)

                        (options.getArgument('C').getValue());

                if( referString.equalsIgnoreCase("ON") ) {

                    follow = true;

                } else

                if( referString.equalsIgnoreCase("BIND") ) {

                    follow = true;

                } else

                if( referString.equalsIgnoreCase("AUTH") ) {

                    follow = true;

                } else

                if( referString.equalsIgnoreCase("REBIND") ) { // deprecated

                    follow = true;

                } else

                if( referString.equalsIgnoreCase("OFF") ) {

                    ; // follow already false

                } else {

                    throw new NoSuchFieldException(

                            "Invalid Referral Following Argument: \"" +

                            referString + "\"");

                }

            } catch( NoSuchFieldException e) {

                throw new RuntimeException(e.toString());

            }

            return follow;

        }



        /**

         * gets the referral dereferencing value

         *

         * @return the referral dereferencing value. Default value DEREF_NEVER

         */

        /* protected */

        int getAliasDereference( )

        {

            int val = 0;

            try {

                String deref = (String)(options.getArgument('a').getValue());

                if( deref.equalsIgnoreCase("NEVER")) {

                    val = LDAPSearchConstraints.DEREF_NEVER;

                } else

                if( deref.equalsIgnoreCase("FINDING")) {

                    val = LDAPSearchConstraints.DEREF_FINDING;

                } else

                if( deref.equalsIgnoreCase("SEARCHING")) {

                    val = LDAPSearchConstraints.DEREF_SEARCHING;

                } else

                if( deref.equalsIgnoreCase("ALWAYS")) {

                    val = LDAPSearchConstraints.DEREF_ALWAYS;

                } else {

                    throw new NoSuchFieldException(

                            "Invalid Alias Dereference Argument: \"" +

                            deref + "\"");

                }

            } catch( NoSuchFieldException e) {

                throw new RuntimeException(e.toString());

            }

            return val;

        }



        /**

         * gets search batch size

         *

         * @return the batch size. Default value 1

         */

        /* protected */

        int getBatchSize()

        {

            try {

                return ((Integer)

                    (options.getArgument('q').getValue())).intValue();

            } catch( NoSuchFieldException e) {

                throw new RuntimeException(e.toString());

            }

        }



        /**

         * gets referral hop limit

         *

         * @return referral hop limit, default value 10

         */

        /* protected */

        int getReferralHopLimit()

        {

            try {

                return ((Integer)

                    (options.getArgument('r').getValue())).intValue();

            } catch( NoSuchFieldException e) {

                throw new RuntimeException(e.toString());

            }

        }



        /**

         * gets max search results

         *

         * @return the max search results. Default value 1000

         */

        /* protected */

        int getMaxSearchResults()

        {

            try {

                return ((Integer)

                    (options.getArgument('m').getValue())).intValue();

            } catch( NoSuchFieldException e) {

                throw new RuntimeException(e.toString());

            }

        }



        /**

         * gets server time limit for a request

         *

         * @return server time limit, default value 0

         */

        /* protected */

        int getServerTimeLimit()

        {

            try {

                return ((Integer)

                    (options.getArgument('l').getValue())).intValue();

            } 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 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 option that determines if to print attribute values

         *

         * @return the print attributes option.  Default true.

         */

        /* protected */

        boolean getPrintAttrs()

        {

            return getAttrValues();

        }



        /**

         * gets option that determines if to retrieve attribute values

         *

         * @return the get attribute values option.  Default true.

         */

        /* protected */

        boolean getAttrValues()

        {

            try {

                return ! ((Boolean)

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

            } catch( NoSuchFieldException e) {

                throw new RuntimeException(e.toString());

            }

        }



        /**

         * gets the print classes option

         *

         * @return the print classes option.  Default false.

         */

        /* protected */

        boolean getPrintDN()

        {

            return true;

        }



        /**

         * 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 usage String

         *

         * @return the usage String.

         */

        /* protected */

        String usage( String msg)

        {

            return options.usage( msg);

        }

    }

}

