/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ package netscape.ldap; import java.util.*; import netscape.ldap.client.*; import netscape.ldap.client.opers.*; import netscape.ldap.ber.stream.*; import netscape.ldap.util.*; import java.io.*; import java.net.*; /** * Represents a connection to an LDAP server.

* * Use objects of this class to perform LDAP operations (such as * search, modify, and add) on an LDAP server.

* * To perform an LDAP operation on a server, you need to follow * these steps:

* *

    *
  1. Create a new LDAPConnection object. *
  2. Use the connect method to connect to the * LDAP server. *
  3. Use the authenticate method to authenticate * to server. *
  4. Perform the LDAP operation. *
  5. Use the disconnect method to disconnect from * the server when done. *
*

* * All operations block until completion (with the exception of * the search method when the results are not all returned at * the same time). *

* * This class also specifies a default set of search constraints * (such as the maximum number of results returned in a search) * which apply to all operations. To get and set these constraints, * use the getOption and setOption methods. * To override these constraints for an individual search operation, * define a new set of constraints by creating a LDAPSearchConstraints * option and pass the object to the search method. *

* * If you set up your client to follow referrals automatically, * an operation that results in a referral will create a new connection * to the LDAP server identified in the referral. In order to have * your client authenticate to that LDAP server automatically, you need * to define a class that implements the LDAPRebind interface. * In your definition of the class, you need to define a * getRebindAuthentication method that creates an LDAPRebindAuth * object containing the distinguished name and password to use for reauthentication. *

* * Most errors that occur raise the same exception (LDAPException). * In order to determine the exact problem that occurred, you can retrieve the * result code from this exception and compare its value against a set of defined * result codes. *

* * @version 1.0 * @see netscape.ldap.LDAPSearchConstraints * @see netscape.ldap.LDAPRebind * @see netscape.ldap.LDAPRebindAuth * @see netscape.ldap.LDAPException */ public class LDAPConnection implements LDAPv3, Cloneable { /** * Version of the LDAP protocol used by default. * LDAP_VERSION is 2, so your client will * attempt to authenticate to LDAP servers as an LDAP v2 client. * The following is an example of some code that prints the * value of this variable: *

* *

     * LDAPConnection ld = new LDAPConnection();
     * System.out.println( "The default LDAP protocol version used is "
     *                      ld.LDAP_VERSION );
     * 
* * If you want to authenticate as an LDAP v3 client, * use the authenticate(int version, String dn, String passwd) method. * For example: *

* *

     * ld.authenticate( 3, myDN, myPW );
     * 
* * @see netscape.ldap.LDAPConnection#authenticate(int, java.lang.String, java.lang.String) */ public final static int LDAP_VERSION = 2; /** * Name of the property specifying the version of the SDK.

* * To get the version number, pass this name to the * getProperty method. The SDK version number * is a Float type. For example:

*

     *      ...
     *      Float sdkVersion = ( Float )myConn.getProperty( myConn.LDAP_PROPERTY_SDK );
     *      System.out.println( "SDK version: " + sdkVersion );
     *      ... 
* @see netscape.ldap.LDAPConnection#getProperty(java.lang.String) */ public final static String LDAP_PROPERTY_SDK = "version.sdk"; /** * Name of the property specifying the highest supported version of * the LDAP protocol.

* * To get the version number, pass this name to the * getProperty method. The LDAP protocol version number * is a Float type. For example:

*

     *      ...
     *      Float LDAPVersion = ( Float )myConn.getProperty( myConn.LDAP_PROPERTY_PROTOCOL );
     *      System.out.println( "Highest supported LDAP protocol version: " + LDAPVersion );
     *      ... 
* @see netscape.ldap.LDAPConnection#getProperty(java.lang.String) */ public final static String LDAP_PROPERTY_PROTOCOL = "version.protocol"; /** * Name of the property specifying the types of authentication allowed by this * API (for example, anonymous authentication and simple authentication).

* * To get the supported types, pass this name to the * getProperty method. The value of this property is * a String type. For example:

*

     *      ...
     *      String authTypes = ( String )myConn.getProperty( myConn.LDAP_PROPERTY_SECURITY );
     *      System.out.println( "Supported authentication types: " + authTypes );
     *      ... 
* @see netscape.ldap.LDAPConnection#getProperty(java.lang.String) */ public final static String LDAP_PROPERTY_SECURITY = "version.security"; /** * Constants */ private final static String defaultFilter = "(objectClass=*)"; private final static LDAPSearchConstraints readConstraints = new LDAPSearchConstraints(); /** * Internal variables */ transient private LDAPSearchConstraints defaultConstraints = new LDAPSearchConstraints (); transient private Vector responseListeners; transient private Vector searchListeners; transient private boolean bound; transient private String host; transient private String[] m_hostList; transient private int port; transient private int[] m_portList; transient private int m_defaultPort; transient private String prevBoundDN; transient private String prevBoundPasswd; transient private String boundDN; transient private String boundPasswd; transient private int protocolVersion = LDAP_VERSION; transient private LDAPSocketFactory m_factory; /* th does all socket i/o for the object and any clones */ transient private LDAPConnThread th = null; /* To manage received server controls on a per-thread basis, we keep a table of active threads and a table of controls, indexed by thread */ transient private Vector m_attachedList = new Vector(); transient private Hashtable m_responseControlTable = new Hashtable(); transient private LDAPCache m_cache = null; static Hashtable m_threadConnTable = new Hashtable(); // this handles the case when the client lost the connection with the // server. After the client reconnects with the server, the bound resets // to false. If the client used to have anonymous bind, then this boolean // will take care of the case whether the client should send anonymous bind // request to the server. private boolean m_anonymousBound = false; private Object m_security = null; private boolean saslBind = false; private Object m_mechanismDriver; private Properties m_securityProperties; private Object m_clientCB; private Hashtable m_methodLookup = new Hashtable(); private LDAPConnection m_referralConnection; /** * Properties */ private final static Float SdkVersion = new Float(3.2f); private final static Float ProtocolVersion = new Float(3.0f); private final static String SecurityVersion = new String("none,simple,sasl"); private final static Float MajorVersion = new Float(3.0f); private final static Float MinorVersion = new Float(0.2f); private final static String DELIM = "#"; private final static String PersistSearchPackageName = "netscape.ldap.controls.LDAPPersistSearchControl"; private final static String EXTERNAL_MECHANISM = "SASLExternalMechanism"; private final static String EXTERNAL_MECHANISM_PACKAGE = "com.netscape.sasl.mechanisms"; /** * Constructs a new LDAPConnection object, * which represents a connection to an LDAP server.

* * Calling the constructor does not actually establish * the connection. To connect to the LDAP server, use the * connect method. * * @see netscape.ldap.LDAPConnection#connect(java.lang.String, int) * @see netscape.ldap.LDAPConnection#authenticate(java.lang.String, java.lang.String) */ public LDAPConnection () { super(); port = -1; m_factory = null; } /** * Constructs a new LDAPConnection object that * will use the specified socket factory class to create * socket connections. The socket factory class must implement * the LDAPSocketFactory interface.
* (For example, the LDAPSSLSocketFactory * class implements this interface.) *

* * Note that calling the LDAPConnection constructor * does not actually establish a connection to an LDAP server. * To connect to an LDAP server, use the * connect method. The socket connection will be * constructed when this method is called. *

* * @see netscape.ldap.LDAPSocketFactory * @see netscape.ldap.LDAPSSLSocketFactory * @see netscape.ldap.LDAPConnection#connect(java.lang.String, int) * @see netscape.ldap.LDAPConnection#authenticate(java.lang.String, java.lang.String) * @see netscape.ldap.LDAPConnection#getSocketFactory * @see netscape.ldap.LDAPConnection#setSocketFactory(netscape.ldap.LDAPSocketFactory) */ public LDAPConnection ( LDAPSocketFactory factory ) { super(); port = -1; m_factory = factory; } /** * Finalize method, which disconnects from the LDAP server. * @exception LDAPException Thrown when the connection cannot be disconnected. */ public void finalize() throws LDAPException { if (th != null) disconnect(); } /** * Sets the specified LDAPCache object as the * cache for the LDAPConnection object. *

* * @param cache The LDAPCache object representing * the cache you want used by the current connection. * @see netscape.ldap.LDAPCache * @see netscape.ldap.LDAPConnection#getCache */ public void setCache(LDAPCache cache) { m_cache = cache; } /** * Gets the LDAPCache object associated with * the current LDAPConnection object. *

* * @return The LDAPCache object representing * the cache used by the current connection. * @see netscape.ldap.LDAPCache * @see netscape.ldap.LDAPConnection#setCache(netscape.ldap.LDAPCache) */ public LDAPCache getCache() { return m_cache; } /** * Gets a property of a connection.

* * You can get the following properties for a given connection:

*

*

* * For example, the following section of code gets the version of * the SDK.

* *

     *       ...
     *       Float sdkVersion = ( Float )myConn.getProperty( myConn.LDAP_PROPERTY_SDK );
     *       System.out.println( "SDK version: " + sdkVersion );
     *       ... 
* * @param name Name of the property (for example, * LDAP_PROPERTY_SDK).

* * @return The value of the property.

* * Since the return value is an object, you * should recast it as the appropriate type. * (For example, when getting the LDAP_PROPERTY_SDK property, * recast the return value as a Float.)

* * If you pass this method an unknown property name, the method * returns null.

* * @exception LDAPException Unable to get the value of the * specified property.

* * @see netscape.ldap.LDAPConnection#LDAP_PROPERTY_SDK * @see netscape.ldap.LDAPConnection#LDAP_PROPERTY_PROTOCOL * @see netscape.ldap.LDAPConnection#LDAP_PROPERTY_SECURITY */ public Object getProperty(String name) throws LDAPException { if (name.equals(LDAP_PROPERTY_SDK)) return SdkVersion; else if (name.equals(LDAP_PROPERTY_PROTOCOL)) return ProtocolVersion; else if (name.equals(LDAP_PROPERTY_SECURITY)) return SecurityVersion; else if (name.equals("version.major")) return MajorVersion; else if (name.equals("version.minor")) return MinorVersion; else return null; } /** * This method is reserved for future use and does not currently * allow you to set any properties. *

* * @param name Name of the property that you want to set. * @param val Value that you want to set. * @exception LDAPException Unable to set the value of the specified * property. */ public void setProperty(String name, Object val) throws LDAPException { throw new LDAPException("No property has been set"); } /** * Sets the LDAP protocol version that your client prefers to use when * connecting to the LDAP server. *

* * @param version The LDAP protocol version that your client uses. */ private void setProtocolVersion(int version) { protocolVersion = version; } /** * Returns the host name of the LDAP server to which you are connected. * @return Host name of the LDAP server. */ public String getHost () { return host; } /** * Returns the port number of the LDAP server to which you are connected. * @return Port number of the LDAP server. */ public int getPort () { return port; } /** * Returns the distinguished name (DN) used for authentication over * this connection. * @return Distinguished name used for authentication over this connection. */ public String getAuthenticationDN () { return boundDN; } /** * Returns the password used for authentication over this connection. * @return Password used for authentication over this connection. */ public String getAuthenticationPassword () { return boundPasswd; } /** * Gets the object representing the socket factory used to establish * a connection to the LDAP server. *

* * @return The object representing the socket factory used to * establish a connection to a server. * @see netscape.ldap.LDAPSocketFactory * @see netscape.ldap.LDAPSSLSocketFactory * @see netscape.ldap.LDAPConnection#setSocketFactory(netscape.ldap.LDAPSocketFactory) */ public LDAPSocketFactory getSocketFactory () { return m_factory; } /** * Specifies the object representing the socket factory that you * want to use to establish a connection to a server. *

* * @param factory The object representing the socket factory that * you want to use to establish a connection to a server. * @see netscape.ldap.LDAPSocketFactory * @see netscape.ldap.LDAPSSLSocketFactory * @see netscape.ldap.LDAPConnection#getSocketFactory */ public void setSocketFactory (LDAPSocketFactory factory) { m_factory = factory; } /** * Indicates whether the connection represented by this object * is open at this time. * @return If connected to an LDAP server over this connection, * returns true. If not connected to an LDAP server, * returns false. */ public boolean isConnected() { // This is the hack: If the user program calls isConnected() when // the thread is about to shut down, the isConnected might get called // before the deregisterConnection(). We add the yield() so that // the deregisterConnection() will get called first. // This problem only exists on Solaris. Thread.yield(); return (th != null); } /** * Indicates whether this client has authenticated to the LDAP server * @return If authenticated, returns true. If not * authenticated, or if authenticated as an anonymous user (with * either a blank name or password), returns false. */ public boolean isAuthenticated () { if (bound) { if ((boundDN == null) || boundDN.equals("") || (boundPasswd == null) || boundPasswd.equals("")) return false; } return bound; } /** * Connects to the specified host and port. If this LDAPConnection object * represents an open connection, the connection is closed first * before the new connection is opened. *

* * For example, the following section of code establishes a connection with * the LDAP server running on the host ldap.netscape.com and the port 389. *

* *

     * String ldapHost = "ldap.netscape.com";
     * int ldapPort = 389;
     * LDAPConnection myConn = new LDAPConnection();
     * try {
     *     myConn.connect( ldapHost, ldapPort );
     * } catch ( LDAPException e ) {
     *     System.out.println( "Unable to connect to " + ldapHost +
     *                         " at port " + ldapPort );
     *     return;
     * }
     * System.out.println( "Connected to " + ldapHost + " at port " + ldapPort )
     * 
* * @param host Host name of the LDAP server that you want to connect to. * This value can also be a space-delimited list of hostnames or * hostnames and port numbers (using the syntax * hostname:portnumber). Your client application or applet * will attempt to contact each host in the order you specify until a * connection is established. For example, you can specify the following * values for the host argument:
*
     *   myhost
     *   myhost hishost:389 herhost:5000 whathost
     *   myhost:686 myhost:389 hishost:5000 whathost:1024
     *
* @param port Port number of the LDAP server that you want to connect to. * This parameter is ignored for any host in the host * parameter which includes a colon and port number. * @exception LDAPException The connection failed. */ public void connect(String host, int port) throws LDAPException { connect( host, port, null, null, defaultConstraints, false ); } /** * Connects to the specified host and port and uses the specified DN and * password to authenticate to the server. If this LDAPConnection object * represents an open connection, the connection is closed first * before the new connection is opened. *

* * For example, the following section of code establishes a connection * with the LDAP server running on ldap.netscape.com at port 389. The * example also attempts to authenticate the client as Barbara Jensen. *

* *

     * String ldapHost = "ldap.netscape.com";
     * int ldapPort = 389;
     * String myDN = "cn=Barbara Jensen,ou=Product Development,o=Ace Industry,c=US";
     * String myPW = "hifalutin";
     * LDAPConnection myConn = new LDAPConnection();
     * try {
     *     myConn.connect( ldapHost, ldapPort, myDN, myPW );
     * } catch ( LDAPException e ) {
     *     switch( e.getLDAPResultCode() ) {
     *         case e.NO_SUCH_OBJECT:
     *             System.out.println( "The specified user does not exist." );
     *             break;
     *         case e.INVALID_CREDENTIALS:
     *             System.out.println( "Invalid password." );
     *             break;
     *         default:
     *             System.out.println( "Error number: " + e.getLDAPResultCode() );
     *             System.out.println( "Failed to connect to " + ldapHost + " at port " + ldapPort );
     *             break;
     *     }
     *     return;
     * }
     * System.out.println( "Connected to " + ldapHost + " at port " + ldapPort );
     * 
* * @param host Host name of the LDAP server that you want to connect to. * This value can also be a space-delimited list of hostnames or * hostnames and port numbers (using the syntax * hostname:portnumber). Your client application or applet * will attempt to contact each host in the order you specify until a * connection is established. For example, you can specify the following * values for the host argument:
*
     *   myhost
     *   myhost hishost:389 herhost:5000 whathost
     *   myhost:686 myhost:389 hishost:5000 whathost:1024
     *
* @param port Port number of the LDAP server that you want to connect to. * This parameter is ignored for any host in the host * parameter which includes a colon and port number. * @param dn Distinguished name used for authentication * @param passwd Password used for authentication * @exception LDAPException The connection or authentication failed. */ public void connect(String host, int port, String dn, String passwd) throws LDAPException { connect(host, port, dn, passwd, defaultConstraints, true); } /** * Connects to the specified host and port and uses the specified DN and * password to authenticate to the server. If this LDAPConnection object * represents an open connection, the connection is closed first * before the new connection is opened. This method allows the user to * specify the preferences for the bind operation. * * @param host Host name of the LDAP server that you want to connect to. * This value can also be a space-delimited list of hostnames or * hostnames and port numbers (using the syntax * hostname:portnumber). Your client application or applet * will attempt to contact each host in the order you specify until a * connection is established. For example, you can specify the following * values for the host argument:
*
     *   myhost
     *   myhost hishost:389 herhost:5000 whathost
     *   myhost:686 myhost:389 hishost:5000 whathost:1024
     *
* @param port Port number of the LDAP server that you want to connect to. * This parameter is ignored for any host in the host * parameter which includes a colon and port number. * @param dn Distinguished name used for authentication * @param passwd Password used for authentication * @param cons Preferences for the bind operation. * @exception LDAPException The connection or authentication failed. */ public void connect(String host, int port, String dn, String passwd, LDAPSearchConstraints cons) throws LDAPException { connect(host, port, dn, passwd, cons, true); } private void connect(String host, int port, String dn, String passwd, LDAPSearchConstraints cons, boolean doAuthenticate) throws LDAPException { if (th != null) disconnect (); if ((host == null) || (host.equals(""))) throw new LDAPException ( "no host for connection", LDAPException.PARAM_ERROR ); /* Parse the list of hosts */ m_defaultPort = port; StringTokenizer st = new StringTokenizer( host ); m_hostList = new String[st.countTokens()]; m_portList = new int[st.countTokens()]; int i = 0; while( st.hasMoreTokens() ) { String s = st.nextToken(); int colon = s.indexOf( ':' ); if ( colon > 0 ) { m_hostList[i] = s.substring( 0, colon ); m_portList[i] = Integer.parseInt( s.substring( colon+1 ) ); } else { m_hostList[i] = s; m_portList[i] = m_defaultPort; } i++; } /* Try each possible host until a connection attempt doesn't cause an exception */ LDAPException conex = null; for( i = 0; i < m_hostList.length; i++ ) { try { this.host = m_hostList[i]; this.port = m_portList[i]; connect (); conex = null; break; } catch ( LDAPException e ) { conex = e; } } if ( conex != null ) { /* All connection attempts failed */ this.host = m_hostList[0]; this.port = m_defaultPort; throw conex; } if (doAuthenticate) authenticate(dn, passwd, cons); } /** * Connects to the specified host and port and uses the specified DN and * password to authenticate to the server, with the specified LDAP * protocol version. If the server does not support the requested * protocol version, an exception is thrown. If this LDAPConnection * object represents an open connection, the connection is closed first * before the new connection is opened. This is equivalent to * connect(host, port) followed by authenticate(version, dn, passwd).

* * @param version LDAP protocol version requested: currently 2 or 3 * @param host Contains a hostname or dotted string representing * the IP address of a host running an LDAP server to connect to. * Alternatively, it may contain a list of host names, space-delimited. * Each host name may include a trailing colon and port number. In the * case where more than one host name is specified, each host name in * turn will be contacted until a connection can be established.

* *

     *   Examples:
     *      "directory.knowledge.com"
     *      "199.254.1.2"
     *      "directory.knowledge.com:1050 people.catalog.com 199.254.1.2"
     * 
*

* @param port Contains the TCP or UDP port number to connect to or contact. * The default LDAP port is 389. "port" is ignored for any host name which * includes a colon and port number. * @param dn If non-null and non-empty, specifies that the connection and * all operations through it should be authenticated with dn as the * distinguished name. * @param passwd If non-null and non-empty, specifies that the connection and * all operations through it should be authenticated with dn as the * distinguished name and passwd as password. * @exception LDAPException The connection or authentication failed. */ public void connect(int version, String host, int port, String dn, String passwd) throws LDAPException { connect(version, host, port, dn, passwd, defaultConstraints); } /** * Connects to the specified host and port and uses the specified DN and * password to authenticate to the server, with the specified LDAP * protocol version. If the server does not support the requested * protocol version, an exception is thrown. This method allows the user * to specify preferences for the bind operation. If this LDAPConnection * object represents an open connection, the connection is closed first * before the new connection is opened. This is equivalent to * connect(host, port) followed by authenticate(version, dn, passwd).

* * @param version LDAP protocol version requested: currently 2 or 3 * @param host Contains a hostname or dotted string representing * the IP address of a host running an LDAP server to connect to. * Alternatively, it may contain a list of host names, space-delimited. * Each host name may include a trailing colon and port number. In the * case where more than one host name is specified, each host name in * turn will be contacted until a connection can be established.

* *

     *   Examples:
     *      "directory.knowledge.com"
     *      "199.254.1.2"
     *      "directory.knowledge.com:1050 people.catalog.com 199.254.1.2"
     * 
*

* @param port Contains the TCP or UDP port number to connect to or contact. * The default LDAP port is 389. "port" is ignored for any host name which * includes a colon and port number. * @param dn If non-null and non-empty, specifies that the connection and * all operations through it should be authenticated with dn as the * distinguished name. * @param passwd If non-null and non-empty, specifies that the connection and * all operations through it should be authenticated with dn as the * distinguished name and passwd as password. * @param cons Preferences for the bind operation. * @exception LDAPException The connection or authentication failed. */ public void connect(int version, String host, int port, String dn, String passwd, LDAPSearchConstraints cons) throws LDAPException { setProtocolVersion(version); connect(host, port, dn, passwd, cons); } /** * Internal routine to connect with internal params * @exception LDAPException failed to connect */ private synchronized void connect () throws LDAPException { if (th != null) return; if (host == null || port < 0) throw new LDAPException ( "no connection parameters", LDAPException.PARAM_ERROR ); th = getNewThread(host, port, m_factory, m_cache); } private synchronized LDAPConnThread getNewThread(String host, int port, LDAPSocketFactory factory, LDAPCache cache) throws LDAPException { LDAPConnThread newThread = null; Vector v = null; synchronized(m_threadConnTable) { Enumeration keys = m_threadConnTable.keys(); boolean connExist = false; // transverse each thread while (keys.hasMoreElements()) { LDAPConnThread connThread = (LDAPConnThread)keys.nextElement(); Vector connVector = (Vector)m_threadConnTable.get(connThread); Enumeration enumv = connVector.elements(); // transverse through each LDAPConnection under the same thread while (enumv.hasMoreElements()) { LDAPConnection conn = (LDAPConnection)enumv.nextElement(); // this is not the brand new connection if (conn.equals(this)) { connExist = true; if (!connThread.isAlive()) { // need to move all the LDAPConnections under the dead thread // to the new thread try { newThread = new LDAPConnThread(host, port, factory, cache); newThread.setMaxBacklog( getSearchConstraints().getMaxBacklog() ); v = (Vector)m_threadConnTable.remove(connThread); break; } catch (Exception e) { throw new LDAPException ("unable to establish connection", LDAPException.UNAVAILABLE); } } break; } } if (connExist) break; } // if this connection is new or the corresponding thread for the current // connection is dead if (!connExist) { try { newThread = new LDAPConnThread(host, port, factory, cache); newThread.setMaxBacklog( getSearchConstraints().getMaxBacklog() ); v = new Vector(); v.addElement(this); } catch (Exception e) { throw new LDAPException ("unable to establish connection", LDAPException.UNAVAILABLE); } } // add new thread to the table if (newThread != null) { m_threadConnTable.put(newThread, v); for (int i=0, n=v.size(); i * * For example, the following section of code authenticates the * client as Barbara Jensen. The code assumes that the client * has already established a connection with an LDAP server. *

* *

     * String myDN = "cn=Barbara Jensen,ou=Product Development,o=Ace Industry,c=US";
     * String myPW = "hifalutin";
     * try {
     *     myConn.authenticate( myDN, myPW );
     * } catch ( LDAPException e ) {
     *     switch( e.getLDAPResultCode() ) {
     *         case e.NO_SUCH_OBJECT:
     *             System.out.println( "The specified user does not exist." );
     *             break;
     *         case e.INVALID_CREDENTIALS:
     *             System.out.println( "Invalid password." );
     *             break;
     *         default:
     *             System.out.println( "Error number: " + e.getLDAPResultCode() );
     *             System.out.println( "Failed to authentice as " + myDN );
     *             break;
     *     }
     *     return;
     * }
     * System.out.println( "Authenticated as " + myDN );
     * 
* * @param dn Distinguished name used for authentication. * @param passwd Password used for authentication. * @exception LDAPException Failed to authenticate to the LDAP server. */ public void authenticate(String dn, String passwd) throws LDAPException { authenticate(protocolVersion, dn, passwd, defaultConstraints); } /** * Authenticates to the LDAP server (that you are currently * connected to) using the specified name and password. The * default protocol version (version 2) is used. If the server * doesn't support the default version, an LDAPException is thrown * with the error code PROTOCOL_ERROR. This method allows the * user to specify the preferences for the bind operation. * * @param dn Distinguished name used for authentication. * @param passwd Password used for authentication. * @param cons Preferences for the bind operation. * @exception LDAPException Failed to authenticate to the LDAP server. */ public void authenticate(String dn, String passwd, LDAPSearchConstraints cons) throws LDAPException { authenticate(protocolVersion, dn, passwd, cons); } /** * Authenticates to the LDAP server (that you are currently * connected to) using the specified name and password, and * requesting that the server use at least the specified * protocol version. If the server doesn't support that * level, an LDAPException is thrown with the error code * PROTOCOL_ERROR. * * @param version Required LDAP protocol version. * @param dn Distinguished name used for authentication. * @param passwd Password used for authentication. * @exception LDAPException Failed to authenticate to the LDAP server. */ public void authenticate(int version, String dn, String passwd) throws LDAPException { authenticate(version, dn, passwd, defaultConstraints); } /** * Authenticates to the LDAP server (that you are currently * connected to) using the specified name and password, and * requesting that the server use at least the specified * protocol version. If the server doesn't support that * level, an LDAPException is thrown with the error code * PROTOCOL_ERROR. This method allows the user to specify the * preferences for the bind operation. * * @param version Required LDAP protocol version. * @param dn Distinguished name used for authentication. * @param passwd Password used for authentication. * @param cons Preferences for the bind operation. * @exception LDAPException Failed to authenticate to the LDAP server. */ public void authenticate(int version, String dn, String passwd, LDAPSearchConstraints cons) throws LDAPException { prevBoundDN = boundDN; prevBoundPasswd = boundPasswd; boundDN = dn; boundPasswd = passwd; if ((prevBoundDN == null) || (prevBoundPasswd == null)) m_anonymousBound = true; else m_anonymousBound = false; bind (version, true, cons); } /** * Authenticates to the LDAP server (that the object is currently * connected to) using the specified name and a specified SASL mechanism * or set of mechanisms. If the requested SASL mechanism is not * available, an exception is thrown. If the object has been * disconnected from an LDAP server, this method attempts to reconnect * to the server. If the object had already authenticated, the old * authentication is discarded. * * @param dn If non-null and non-empty, specifies that the connection and * all operations through it should be authenticated with dn as the * distinguished name. * @param mechanism A single mechanism name, e.g. "GSSAPI". * @param packageName A package from which to instantiate the Mechanism * Driver, e.g. "myclasses.SASL.mechanisms". If null, a system default * is used. * @param getter A class which may be called by the Mechanism Driver to * obtain additional information required. * @exception LDAPException Failed to authenticate to the LDAP server. */ public void authenticate(String dn, String mechanism, String packageName, Properties props, Object getter) throws LDAPException { try { Object[] args = new Object[2]; args[0] = mechanism; args[1] = packageName; String[] argNames = new String[2]; argNames[0] = "java.lang.String"; argNames[1] = "java.lang.String"; // Get a mechanism driver m_mechanismDriver = invokeMethod(null, "com.netscape.sasl.SASLMechanismFactory", "getMechanismDriver", args, argNames); } catch (Exception e) { throw new LDAPException(e.toString(), LDAPException.OTHER); } m_securityProperties = props; m_clientCB = getter; boundDN = dn; saslBind(true); } /** * Authenticates to the LDAP server (that the object is currently * connected to) using the specified name and a specified SASL mechanism * or set of mechanisms. If the requested SASL mechanism is not * available, an exception is thrown. If the object has been * disconnected from an LDAP server, this method attempts to reconnect * to the server. If the object had already authenticated, the old * authentication is discarded. * * @param dn If non-null and non-empty, specifies that the connection and * all operations through it should be authenticated with dn as the * distinguished name. * @param mechanisms A list of acceptable mechanisms. The first one * for which a Mechanism Driver can be instantiated is returned. * @param packageName A package from which to instantiate the Mechanism * Driver, e.g. "myclasses.SASL.mechanisms". If null, a system default * is used. * @param getter A class which may be called by the Mechanism Driver to * obtain additional information required. * @exception LDAPException Failed to authenticate to the LDAP server. */ public void authenticate(String dn, String[] mechanisms, String packageName, Properties props, Object getter) throws LDAPException { for (int i=0; i 1) { disconnect(); connect(); } } if ((bound && rebind) || (!bound)) { try { // Get the initial request to start authentication Object[] arg1 = new Object[5]; arg1[0] = boundDN; arg1[1] = "LDAP"; arg1[2] = null; arg1[3] = m_securityProperties; arg1[4] = m_clientCB; String[] argNames = new String[5]; argNames[0] = "java.lang.String"; argNames[1] = "java.lang.String"; argNames[2] = "java.lang.String"; argNames[3] = "java.util.Properties"; argNames[4] = "com.netscape.sasl.SASLClientCB"; String mechanismName = m_mechanismDriver.getClass().getName(); byte[] outVals = (byte[])invokeMethod(m_mechanismDriver, mechanismName, "startAuthentication", arg1, argNames); boolean isExternal = isExternalMechanism(mechanismName); int resultCode = LDAPException.SASL_BIND_IN_PROGRESS; JDAPBindResponse response = null; while (!checkForSASLBindCompletion(resultCode)) { response = saslBind(outVals); resultCode = response.getResultCode(); if (isExternal) continue; String challenge = response.getCredentials(); byte[] b = challenge.getBytes(); Object[] arg2 = new Object[1]; arg2[0] = b; String[] arg2Names = new String[1]; arg2Names[0] = "[B"; //class name for byte array outVals = (byte[])invokeMethod(m_mechanismDriver, mechanismName, "evaluateResponse", arg2, arg2Names); } // Make sure authentication REALLY is complete Boolean bool = (Boolean)invokeMethod(m_mechanismDriver, mechanismName, "isComplete", null, null); if (!bool.booleanValue()) { // Authentication session hijacked! throw new LDAPException("The server indicates that " + "authentication is successful, but the SASL driver " + "indicates that authentication is not yet done.", LDAPException.OTHER); } m_security = invokeMethod(m_mechanismDriver, mechanismName, "getSecurityLayer", null, null); th.setSecurityLayer(m_security); updateThreadConnTable(); } catch (LDAPException e) { throw e; } catch (Exception e) { throw new LDAPException(e.toString(), LDAPException.OTHER); } } } private boolean isExternalMechanism(String name) { String s = name; int dot = name.lastIndexOf( '.' ); if ( (dot >= 0) && (dot < (name.length()-1)) ) { s = name.substring( dot + 1 ); } return s.equals( EXTERNAL_MECHANISM ); } private Object invokeMethod(Object obj, String packageName, String methodName, Object[] args, String[] argNames) throws LDAPException { try { java.lang.reflect.Method m = getMethod(packageName, methodName, argNames); if (m != null) { return (m.invoke(obj, args)); } } catch (Exception e) { throw new LDAPException("Invoking "+methodName+": "+ e.toString(), LDAPException.PARAM_ERROR); } return null; } private java.lang.reflect.Method getMethod(String packageName, String methodName, String[] args) throws LDAPException { try { java.lang.reflect.Method method = null; String suffix = ""; if (args != null) for (int i=0; i 1) { disconnect(); connect(); } // if the connection is still intact and no rebind request if (bound && !rebind) return; // if the connection was lost and did not have any kind of bind // operation and the current one does not request any bind operation (ie, // no authenticate has been called) if (!m_anonymousBound && ((boundDN == null) || (boundPasswd == null)) && !rebind) return; if (bound && rebind) { if (protocolVersion == version) { if (m_anonymousBound && ((boundDN == null) || (boundPasswd == null))) return; if (!m_anonymousBound && (boundDN != null) && (boundPasswd != null) && boundDN.equals(prevBoundDN) && boundPasswd.equals(prevBoundPasswd)) return; } // if get to here, means that we need to do rebind since previous and // current credentials are not the same. // if the current connection is the only connection for the th thread, // then reuse this current connection. otherwise, disconnect the current // one (ie, detach from the current th) and reconnect again (ie, have // a new th). if (th.getClientCount() > 1) { disconnect(); connect(); } } protocolVersion = version; LDAPResponseListener myListener = getResponseListener (); JDAPMessage response; try { if (m_referralConnection != null) { m_referralConnection.disconnect(); m_referralConnection = null; } sendRequest(new JDAPBindRequest(protocolVersion, boundDN, boundPasswd), myListener, cons); response = myListener.getResponse(); checkMsg(response); } catch (LDAPReferralException e) { m_referralConnection = createReferralConnection(e, cons); } finally { releaseResponseListener(myListener); } updateThreadConnTable(); } private void updateThreadConnTable() { synchronized(m_threadConnTable) { if (m_threadConnTable.containsKey(th)) { Vector v = (Vector)m_threadConnTable.get(th); for (int i=0, n=v.size(); iconnect. * @exception LDAPException Failed to disconnect from the LDAP server. * @see netscape.ldap.LDAPConnection#connect(java.lang.String, int) * @see netscape.ldap.LDAPConnection#connect(java.lang.String, int, java.lang.String, java.lang.String) */ public synchronized void disconnect() throws LDAPException { if (m_referralConnection != null) { m_referralConnection.disconnect(); m_referralConnection = null; } if (th == null) throw new LDAPException ( "unable to disconnect() without connecting", LDAPException.OTHER ); if (m_cache != null) { m_cache.cleanup(); m_cache = null; } deleteThreadConnEntry(); deregisterConnection(); } private void deleteThreadConnEntry() { synchronized (m_threadConnTable) { Enumeration keys = m_threadConnTable.keys(); while (keys.hasMoreElements()) { LDAPConnThread connThread = (LDAPConnThread)keys.nextElement(); Vector connVector = (Vector)m_threadConnTable.get(connThread); Enumeration enumv = connVector.elements(); while (enumv.hasMoreElements()) { LDAPConnection c = (LDAPConnection)enumv.nextElement(); if (c.equals(this)) { connVector.removeElement(c); if (connVector.size() == 0) m_threadConnTable.remove(connThread); return; } } } } } /** * Decrement the reference count for the connection */ synchronized void deregisterConnection() { th.deregister(this); th = null; bound = false; } /** * Reads the entry for the specified distiguished name (DN) and retrieves all * attributes for the entry. *

* * For example, the following section of code reads the entry for * Barbara Jensen and retrieves all attributes for that entry. *

* *

     * String findDN = "cn=Barbara Jensen,ou=Product Development,o=Ace Industry,c=US";
     * LDAPEntry foundEntry = null;
     * try {
     *     foundEntry = myConn.read( findDN );
     * } catch ( LDAPException e ) {
     *     switch( e.getLDAPResultCode() ) {
     *         case e.NO_SUCH_OBJECT:
     *             System.out.println( "The specified entry does not exist." );
     *             break;
     *         case e.LDAP_PARTIAL_RESULTS:
     *             System.out.println( "Entry served by a different LDAP server." );
     *             break;
     *         case e.INSUFFICIENT_ACCESS_RIGHTS:
     *             System.out.println( "You do not have the access rights to perform this operation." );
     *             break;
     *         default:
     *             System.out.println( "Error number: " + e.getLDAPResultCode() );
     *             System.out.println( "Could not read the specified entry." );
     *             break;
     *     }
     *     return;
     * }
     * System.out.println( "Found the specified entry." );
     * 
* * @param DN Distinguished name of the entry that you want to retrieve. * @exception LDAPException Failed to find or read the specified entry * from the directory. * @return LDAPEntry Returns the specified entry or raises an exception * if the entry is not found. */ public LDAPEntry read (String DN) throws LDAPException { return read (DN, null, defaultConstraints); } /** * Reads the entry for the specified distiguished name (DN) and retrieves all * attributes for the entry. This method allows the user to specify the * preferences for the read operation. *

* * For example, the following section of code reads the entry for * Barbara Jensen and retrieves all attributes for that entry. *

* *

     * String findDN = "cn=Barbara Jensen,ou=Product Development,o=Ace Industry,c=US";
     * LDAPEntry foundEntry = null;
     * try {
     *     foundEntry = myConn.read( findDN );
     * } catch ( LDAPException e ) {
     *     switch( e.getLDAPResultCode() ) {
     *         case e.NO_SUCH_OBJECT:
     *             System.out.println( "The specified entry does not exist." );
     *             break;
     *         case e.LDAP_PARTIAL_RESULTS:
     *             System.out.println( "Entry served by a different LDAP server." );
     *             break;
     *         case e.INSUFFICIENT_ACCESS_RIGHTS:
     *             System.out.println( "You do not have the access rights to perform this operation." );
     *             break;
     *         default:
     *             System.out.println( "Error number: " + e.getLDAPResultCode() );
     *             System.out.println( "Could not read the specified entry." );
     *             break;
     *     }
     *     return;
     * }
     * System.out.println( "Found the specified entry." );
     * 
* * @param DN Distinguished name of the entry that you want to retrieve. * @param cons Preferences for the read operation. * @exception LDAPException Failed to find or read the specified entry * from the directory. * @return LDAPEntry Returns the specified entry or raises an exception * if the entry is not found. */ public LDAPEntry read (String DN, LDAPSearchConstraints cons) throws LDAPException { return read (DN, null, cons); } /** * Reads the entry for the specified distinguished name (DN) and * retrieves only the specified attributes from the entry. *

* * For example, the following section of code reads the entry for * Barbara Jensen and retrieves only the cn and * sn attributes. * The example prints out all attributes that have been retrieved * (the two specified attributes). *

* *

     * String findDN = "cn=Barbara Jensen,ou=Product Development,o=Ace Industry,c=US";
     * LDAPEntry foundEntry = null;
     * String getAttrs[] = { "cn", "sn" };
     * try {
     *      foundEntry = myConn.read( findDN, getAttrs );
     * } catch ( LDAPException e ) {
     *      switch( e.getLDAPResultCode() ) {
     *           case e.NO_SUCH_OBJECT:
     *               System.out.println( "The specified entry does not exist." );
     *               break;
     *           case e.LDAP_PARTIAL_RESULTS:
     *               System.out.println( "Entry served by a different LDAP server." );
     *               break;
     *           case e.INSUFFICIENT_ACCESS_RIGHTS:
     *               System.out.println( "You do not have the access " +
     *                                   "rights to perform this operation." );
     *               break;
     *           default:
     *               System.out.println( "Error number: " + e.getLDAPResultCode() );
     *               System.out.println( "Could not read the specified entry." );
     *               break;
     *      }
     *      return;
     * }
     *
     * LDAPAttributeSet foundAttrs = foundEntry.getAttributeSet();
     * int size = foundAttrs.size();
     * Enumeration enumAttrs = foundAttrs.getAttributes();
     * System.out.println( "Attributes: " );
     *
     * while ( enumAttrs.hasMoreElements() ) {
     *      LDAPAttribute anAttr = ( LDAPAttribute )enumAttrs.nextElement();
     *      String attrName = anAttr.getName();
     *      System.out.println( "\t" + attrName );
     *      Enumeration enumVals = anAttr.getStringValues();
     *      while ( enumVals.hasMoreElements() ) {
     *           String aVal = ( String )enumVals.nextElement();
     *           System.out.println( "\t\t" + aVal );
     *      }
     * }
     * 
* * @param DN Distinguished name of the entry that you want to retrieve. * @param attrs Names of attributes that you want to retrieve. * @return LDAPEntry Returns the specified entry (or raises an * exception if the entry is not found). * @exception LDAPException Failed to read the specified entry from * the directory. */ public LDAPEntry read (String DN, String attrs[]) throws LDAPException { return read(DN, attrs, defaultConstraints); } public LDAPEntry read (String DN, String attrs[], LDAPSearchConstraints cons) throws LDAPException { LDAPSearchResults results = search (DN, LDAPv2.SCOPE_BASE, defaultFilter, attrs, false, cons); if (results == null) return null; return results.next (); } /** * Reads the entry specified by the LDAP URL.

* * When you call this method, a new connection is created automatically, * using the host and port specified in the URL. After finding the entry, * the method closes this connection (in other words, it disconnects from * the LDAP server).

* * If the URL specifies a filter and scope, these are not used. * Of the information specified in the URL, this method only uses * the LDAP host name and port number, the base distinguished name (DN), * and the list of attributes to return.

* * The method returns the entry specified by the base DN.

* * (Note: If you want to search for more than one entry, use the * search( LDAPUrl ) method instead.)

* * For example, the following section of code reads the entry specified * by the LDAP URL. *

* *

     * String flatURL = "ldap://alway.mcom.com:3890/cn=Barbara Jenson,ou=Product Development,o=Ace Industry,c=US?cn,sn,mail";
     * LDAPUrl myURL;
     * try {
     *    myURL = new LDAPUrl( flatURL );
     * } catch ( java.net.MalformedURLException e ) {
     *    System.out.println( "BAD URL!!!  BAD, BAD, BAD URL!!!" );
     *    return;
     * }
     * LDAPEntry myEntry = null;
     * try {
     *    myEntry = myConn.read( myURL );
     * } catch ( LDAPException e ) {
     *    int errCode = e.getLDAPResultCode();
     *    switch( errCode ) {
     *        case ( e.NO_SUCH_OBJECT ):
     *            System.out.println( "The specified entry " + myDN +
     *                                " does not exist in the directory." );
     *            return;
     *        default:
     *            System.out.println( "An internal error occurred." );
     *            return;
     *    }
     * }
     * 
* * @param toGet LDAP URL specifying the entry that you want to read. * @return LDAPEntry Returns the entry specified by the URL (or raises * an exception if the entry is not found). * @exception LDAPException Failed to read the specified entry from * the directory. * @see netscape.ldap.LDAPUrl * @see netscape.ldap.LDAPConnection#search(netscape.ldap.LDAPUrl) */ public static LDAPEntry read (LDAPUrl toGet) throws LDAPException { String host = toGet.getHost (); int port = toGet.getPort(); if (host == null) throw new LDAPException ( "no host for connection", LDAPException.PARAM_ERROR ); String[] attributes = toGet.getAttributeArray (); String DN = toGet.getDN(); LDAPEntry returnValue; LDAPConnection connection = new LDAPConnection (); connection.connect (host, port); returnValue = connection.read (DN, attributes); connection.disconnect (); return returnValue; } /** * Performs the search specified by the LDAP URL.

* * For example, the following section of code searches for all entries under * the ou=Product Development,o=Ace Industry,c=US subtree of a * directory. The example gets and prints the mail attribute for each entry * found.

* *

     * String flatURL = "ldap://alway.mcom.com:3890/ou=Product Development,o=Ace Industry,c=US?mail?sub?objectclass=*";
     * LDAPUrl myURL;
     * try {
     *    myURL = new LDAPUrl( flatURL );
     * } catch ( java.net.MalformedURLException e ) {
     *    System.out.println( "Incorrect URL syntax." );
     *    return;
     * }
     *
     * LDAPSearchResults myResults = null;
     * try {
     *    myResults = myConn.search( myURL );
     * } catch ( LDAPException e ) {
     *    int errCode = e.getLDAPResultCode();
     *    System.out.println( "LDAPException: return code:" + errCode );
     *    return;
     * }
     *
     * while ( myResults.hasMoreElements() ) {
     *    LDAPEntry myEntry = myResults.next();
     *    String nextDN = myEntry.getDN();
     *    System.out.println( nextDN );
     *    LDAPAttributeSet entryAttrs = myEntry.getAttributeSet();
     *    Enumeration attrsInSet = entryAttrs.getAttributes();
     *    while ( attrsInSet.hasMoreElements() ) {
     *        LDAPAttribute nextAttr = (LDAPAttribute)attrsInSet.nextElement();
     *        String attrName = nextAttr.getName();
     *        System.out.print( "\t" + attrName + ": " );
     *        Enumeration valsInAttr = nextAttr.getStringValues();
     *        while ( valsInAttr.hasMoreElements() ) {
     *            String nextValue = (String)valsInAttr.nextElement();
     *            System.out.println( nextValue );
     *        }
     *    }
     * }
     * 
*

* * To abandon the search, use the abandon method. * * @param toGet LDAP URL representing the search that you want to perform. * @return LDAPSearchResults The results of the search as an enumeration. * @exception LDAPException Failed to complete the search specified by * the LDAP URL. * @see netscape.ldap.LDAPUrl * @see netscape.ldap.LDAPSearchResults * @see netscape.ldap.LDAPConnection#abandon(netscape.ldap.LDAPSearchResults) */ public static LDAPSearchResults search (LDAPUrl toGet) throws LDAPException { return search (toGet, null); } /** * Performs the search specified by the LDAP URL. This method also * allows you to specify constraints for the search (such as the * maximum number of entries to find or the * maximum time to wait for search results).

* * As part of the search constraints, you can specify whether or not you * want the results delivered all at once or in smaller batches. * If you specify that you want the results delivered in smaller * batches, each iteration blocks until the next batch of results is * returned.

* * For example, the following section of code retrieves the first 5 * matching entries for the search specified by the LDAP URL. The * example accomplishes this by creating a new set of search * constraints where the maximum number of search results is 5.

* *

     * LDAPSearchConstraints mySearchConstraints = myConn.getSearchConstraints();
     * mySearchConstraints.setMaxResults( 5 );
     * String flatURL = "ldap://alway.mcom.com:3890/ou=Product Development,o=Ace Industry,c=US?mail?sub?objectclass=*";
     * LDAPUrl myURL;
     * try {
     *    myURL = new LDAPUrl( flatURL );
     * } catch ( java.net.MalformedURLException e ) {
     *    System.out.println( "Incorrect URL syntax." );
     *    return;
     * }
     * LDAPSearchResults myResults = null;
     * try {
     *    myResults = myConn.search( myURL, mySearchConstraints );
     * } catch ( LDAPException e ) {
     *    int errCode = e.getLDAPResultCode();
     *    System.out.println( "LDAPException: return code:" + errCode );
     *    return;
     * }
     * 
*

* * To abandon the search, use the abandon method. * * @param toGet LDAP URL representing the search that you want to run. * @param cons Constraints specific to the search. * @return LDAPSearchResults The results of the search as an enumeration. * @exception LDAPException Failed to complete the search specified * by the LDAP URL. * @see netscape.ldap.LDAPUrl * @see netscape.ldap.LDAPSearchResults * @see netscape.ldap.LDAPConnection#abandon(netscape.ldap.LDAPSearchResults) */ public static LDAPSearchResults search (LDAPUrl toGet, LDAPSearchConstraints cons) throws LDAPException { String host = toGet.getHost (); int port = toGet.getPort(); if (host == null) throw new LDAPException ( "no host for connection", LDAPException.PARAM_ERROR ); String[] attributes = toGet.getAttributeArray (); String DN = toGet.getDN(); String filter = toGet.getFilter(); if (filter == null) { filter = "(objectClass=*)"; } int scope = toGet.getScope (); LDAPConnection connection = new LDAPConnection (); connection.connect (host, port); LDAPSearchResults results; if (cons != null) results = connection.search (DN, scope, filter, attributes, false, cons); else results = connection.search (DN, scope, filter, attributes, false); results.closeOnCompletion(connection); return results; } /** * Performs the search specified by the criteria that you enter.

* * For example, the following section of code searches for all entries under * the ou=Product Development,o=Ace Industry,c=US subtree of a * directory. The example gets and prints the mail attribute for each entry * found.

* *

     * String myBaseDN = "ou=Product Development,o=Ace Industry,c=US";
     * String myFilter="(objectclass=*)";
     * String[] myAttrs = { "mail" };
     *
     * LDAPSearchResults myResults = null;
     * try {
     *    myResults = myConn.search( myBaseDN, LDAPv2.SCOPE_SUB, myFilter, myAttrs, false );
     * } catch ( LDAPException e ) {
     *    int errCode = e.getLDAPResultCode();
     *    System.out.println( "LDAPException: return code:" + errCode );
     *    return;
     * }
     *
     * while ( myResults.hasMoreElements() ) {
     *    LDAPEntry myEntry = myResults.next();
     *    String nextDN = myEntry.getDN();
     *    System.out.println( nextDN );
     *    LDAPAttributeSet entryAttrs = myEntry.getAttributeSet();
     *    Enumeration attrsInSet = entryAttrs.getAttributes();
     *    while ( attrsInSet.hasMoreElements() ) {
     *        LDAPAttribute nextAttr = (LDAPAttribute)attrsInSet.nextElement();
     *        String attrName = nextAttr.getName();
     *        System.out.println( "\t" + attrName + ":" );
     *        Enumeration valsInAttr = nextAttr.getStringValues();
     *        while ( valsInAttr.hasMoreElements() ) {
     *            String nextValue = (String)valsInAttr.nextElement();
     *            System.out.println( "\t\t" + nextValue );
     *        }
     *    }
     * }
     * 
*

* * To abandon the search, use the abandon method. * * @param base The base distinguished name to search from * @param scope The scope of the entries to search. You can specify one * of the following:

*

    *
  • LDAPv2.SCOPE_BASE (search only the base DN)

    *

  • LDAPv2.SCOPE_ONE * (search only entries under the base DN)

    *

  • LDAPv2.SCOPE_SUB * (search the base DN and all entries within its subtree)

    *

*

* @param filter Search filter specifying the search criteria. * @param attrs List of attributes that you want returned in the * search results. * @param attrsOnly If true, returns the names but not the values of the * attributes found. If false, returns the names and values for * attributes found * @return LDAPSearchResults The results of the search as an enumeration. * @exception LDAPException Failed to complete the specified search. * @see netscape.ldap.LDAPConnection#abandon(netscape.ldap.LDAPSearchResults) */ public LDAPSearchResults search( String base, int scope, String filter, String[] attrs, boolean attrsOnly ) throws LDAPException { return search( base, scope, filter, attrs, attrsOnly, defaultConstraints); } /** * Performs the search specified by the criteria that you enter. * This method also allows you to specify constraints for the search * (such as the maximum number of entries to find or the * maximum time to wait for search results).

* * As part of the search constraints, you can specify whether or not * you want the * results delivered all at once or in smaller batches. If you * specify that you want the results delivered in smaller batches, * each iteration blocks until the * next batch of results is returned.

* * For example, the following section of code retrieves the first 5 entries * matching the specified search criteria. The example accomplishes * this by creating a new set of search constraints where the maximum * number of search results is 5.

* *

     * String myBaseDN = "ou=Product Development,o=Ace Industry,c=US";
     * String myFilter="(objectclass=*)";
     * String[] myAttrs = { "mail" };
     * LDAPSearchConstraints mySearchConstraints = myConn.getSearchConstraints();
     * mySearchConstraints.setMaxResults( 5 );
     *
     * LDAPSearchResults myResults = null;
     * try {
     *    myResults = myConn.search( myBaseDN, LDAPv2.SCOPE_SUB, myFilter, myAttrs, false, mySearchConstraints );
     * } catch ( LDAPException e ) {
     *    int errCode = e.getLDAPResultCode();
     *    System.out.println( "LDAPException: return code:" + errCode );
     *    return;
     * }
     * 
*

* * To abandon the search, use the abandon method. * * @param base The base distinguished name to search from * @param scope The scope of the entries to search. You can specify one * of the following:

*

    *
  • LDAPv2.SCOPE_BASE (search only the base DN)

    *

  • LDAPv2.SCOPE_ONE * (search only entries under the base DN)

    *

  • LDAPv2.SCOPE_SUB * (search the base DN and all entries within its subtree)

    *

*

* @param filter Search filter specifying the search criteria. * @param attrs List of attributes that you want returned in the search * results. * @param cons Constraints specific to this search (for example, the * maximum number * of entries to return). * @param attrsOnly If true, returns the names but not the values of the * attributes found. If false, returns the names and values for * attributes found * @return LDAPSearchResults The results of the search as an enumeration. * @exception LDAPException Failed to complete the specified search. * @see netscape.ldap.LDAPConnection#abandon(netscape.ldap.LDAPSearchResults) */ public LDAPSearchResults search( String base, int scope, String filter, String[] attrs, boolean attrsOnly, LDAPSearchConstraints cons ) throws LDAPException { if (cons == null) cons = defaultConstraints; LDAPSearchResults returnValue = new LDAPSearchResults(this, cons, base, scope, filter, attrs, attrsOnly); Vector cacheValue = null; Long key = null; boolean isKeyValid = true; try { // get entry from cache which is a vector of JDAPMessages if (m_cache != null) { // create key for cache entry using search arguments key = m_cache.createKey(host, port, base, filter, scope, attrs, boundDN, cons); cacheValue = (Vector)m_cache.getEntry(key); if (cacheValue != null) return (new LDAPSearchResults(cacheValue, this, cons, base, scope, filter, attrs, attrsOnly)); } } catch (LDAPException e) { isKeyValid = false; printDebug("Exception: "+e); } bind(cons); LDAPSearchListener myListener = getSearchListener ( cons ); int deref = cons.getDereference(); JDAPSearchRequest request = new JDAPSearchRequest (base, scope, deref, cons.getMaxResults(), cons.getServerTimeLimit(), attrsOnly, filter, attrs); synchronized(myListener) { boolean success = false; try { sendRequest (request, myListener, cons); success = true; } finally { if (!success) releaseSearchListener (myListener); } // if using cache, then need to add the key to the search listener. // The search listener retrieves the key and then add the key and // a vector of results to the hashtable. if ((m_cache != null) && (isKeyValid)) myListener.setKey(key); } /* Synchronous search if all requested at once */ if ( cons.getBatchSize() == 0 ) { try { /* Block until all results are in */ JDAPMessage response = myListener.getResponse (); Enumeration results = myListener.getSearchResults (); try { checkSearchMsg(returnValue, response, cons, base, scope, filter, attrs, attrsOnly); } catch ( LDAPException ex ) { /* Was the exception caused by a bad referral? */ JDAPProtocolOp op = response.getProtocolOp(); if ( (op instanceof JDAPSearchResultReference) || (op instanceof JDAPSearchResult) ){ System.err.println( "LDAPConnection.checkSearchMsg: " + "ignoring bad referral" ); } else { throw ex; } } while (results.hasMoreElements ()) { JDAPMessage msg = (JDAPMessage)results.nextElement(); checkSearchMsg(returnValue, msg, cons, base, scope, filter, attrs, attrsOnly); } } catch (LDAPException ee) { throw ee; } finally { releaseSearchListener (myListener); } } else { /* * Asynchronous to retrieve one at a time, check to make sure * the search didn't fail */ JDAPMessage firstResult = myListener.nextResult (); if (firstResult == null) { firstResult = myListener.getResponse (); try { checkSearchMsg(returnValue, firstResult, cons, base, scope, filter, attrs, attrsOnly); } finally { releaseSearchListener (myListener); } } else { /* First result could be a bad referral, ultimately causing a NO_SUCH_OBJECT exception. Don't want to miss all following results, so skip it. */ try { checkSearchMsg(returnValue, firstResult, cons, base, scope, filter, attrs, attrsOnly); } catch ( LDAPException ex ) { /* Was the exception caused by a bad referral? */ if (firstResult.getProtocolOp() instanceof JDAPSearchResultReference) { System.err.println( "LDAPConnection.checkSearchMsg: " + "ignoring bad referral" ); } else { throw ex; } } LDAPControl[] controls = (LDAPControl[])getOption(LDAPv3.SERVERCONTROLS, cons); for (int i=0; (controls != null) && (itrue if the entry has the value. Returns * false if the entry does not have the value or the * attribute. To represent the value that you want compared, you need * to create an LDAPAttribute object.

* * Note that only string values can be compared.

* * For example, the following section of code checks to see if the entry * "cn=Barbara Jensen,ou=Product Development,o=Ace Industry,c=US" contains * the attribute "mail" with the value "bjensen@aceindustry.com". * *

     * ...
     * LDAPConnection myConn = new LDAPConnection();
     * ...
     * String myDN = "cn=Barbara Jensen,ou=Product Development,o=Ace Industry,c=US";
     * String nameOfAttr = "mail";
     * String valOfAttr = "bjensen@aceindustry.com";
     * LDAPAttribute cmpThisAttr = new LDAPAttribute( nameOfAttr, valOfAttr );
     * boolean hasValue = myConn.compare( myDN, cmpThisAttr );
     * if ( hasValue ) {
     *     System.out.println( "Attribute and value found in entry." );
     * } else {
     *     System.out.println( "Attribute and value not found in entry." );
     * }
     * ...
* * @param DN The distinguished name of the entry that you want to use in * the comparison. * @param attr The attribute that you want to compare against the entry. * (The method checks to see if the entry has an attribute with the same name * and value as this attribute.) * @return true if the entry contains the specified attribute and value. * @exception LDAPException Failed to perform the comparison. * @see netscape.ldap.LDAPAttribute */ public boolean compare( String DN, LDAPAttribute attr ) throws LDAPException { return compare(DN, attr, defaultConstraints); } public boolean compare( String DN, LDAPAttribute attr, LDAPSearchConstraints cons) throws LDAPException { bind(cons); LDAPResponseListener myListener = getResponseListener (); Enumeration en = attr.getByteValues(); byte val[] = (byte[])en.nextElement(); String debug = ""; try { debug = new String(val, "UTF8"); } catch(Throwable x) {} JDAPAVA ass = new JDAPAVA(attr.getName(), debug); //JDAPAVA ass = new JDAPAVA(attr.getName(), new String(val,0)); JDAPMessage response; try { sendRequest (new JDAPCompareRequest (DN, ass), myListener, cons); response = myListener.getResponse (); int resultCode = ((JDAPResult)response.getProtocolOp()).getResultCode(); if (resultCode == JDAPResult.COMPARE_FALSE) return false; if (resultCode == JDAPResult.COMPARE_TRUE) return true; checkMsg (response); } catch (LDAPReferralException e) { Vector res = new Vector(); performReferrals(e, cons, JDAPProtocolOp.COMPARE_REQUEST, DN, 0, null, null, false, null, null, attr, res); boolean bool = false; if (res.size() > 0) bool = ((Boolean)res.elementAt(0)).booleanValue(); res = null; return bool; } finally { releaseResponseListener (myListener); } return false; /* this should never be executed */ } /** * Adds an entry to the directory.

* * Before using this method, you need to create an * LDAPEntry object and use it to specify the * distinguished name and attributes of the new entry. Make sure * to specify values for all required attributes in the * entry. If all required attributes are not specified and the LDAP server * checks the entry against the schema, an LDAPException * may be thrown (where the LDAP result code is * OBJECT_CLASS_VIOLATION).

* * For example, the following section of code creates an * LDAPEntry object for a new entry and uses the object * to add the new entry to the directory. Because the definition of * the LDAP inetOrgPerson class specifies that the * cn, sn, and objectclass * attributes are required, these attributes are specified as part * of the new entry. (mail is not required but is shown * here as an example of specifying additional attributes.) *

* *

     * ...
     * String myDN = "cn=Barbara Jensen,ou=Product Development,o=Ace Industry,c=US";
     *
     * LDAPAttribute attr1 = new LDAPAttribute( "cn", "Barbara Jensen" );
     * LDAPAttribute attr2 = new LDAPAttribute( "sn", "Jensen" );
     * LDAPAttribute attr3 = new LDAPAttribute( "objectclass", "top" );
     * LDAPAttribute attr4 = new LDAPAttribute( "objectclass", "person" );
     * LDAPAttribute attr5 = new LDAPAttribute( "objectclass", "organizationalPerson" );
     * LDAPAttribute attr6 = new LDAPAttribute( "objectclass", "inetOrgPerson" );
     * LDAPAttribute attr7 = new LDAPAttribute( "mail", "bjensen@aceindustry.com" );
     *
     * LDAPAttributeSet myAttrs = new LDAPAttributeSet();
     * myAttrs.add( attr1 );
     * myAttrs.add( attr2 );
     * myAttrs.add( attr3 );
     * myAttrs.add( attr4 );
     * myAttrs.add( attr5 );
     * myAttrs.add( attr6 );
     * myAttrs.add( attr7 );
     *
     * LDAPEntry myEntry = new LDAPEntry( myDN, myAttrs );
     *
     * myConn.add( myEntry );
     * ... 
* * @param entry LDAPEntry object specifying the distinguished name and * attributes of the new entry. * @exception LDAPException Failed to add the specified entry to the * directory. * @see netscape.ldap.LDAPEntry */ public void add( LDAPEntry entry ) throws LDAPException { add(entry, defaultConstraints); } /** * Adds an entry to the directory and allows you to specify preferences * for this LDAP add operation by using an * LDAPSearchConstraints object. For * example, you can specify whether or not to follow referrals. * You can also apply LDAP v3 controls to the operation. *

* * @param entry LDAPEntry object specifying the distinguished name and * attributes of the new entry. * @param cons The set of preferences that you want applied to this operation. * @exception LDAPException Failed to add the specified entry to the * directory. * @see netscape.ldap.LDAPEntry * @see netscape.ldap.LDAPSearchConstraints */ public void add( LDAPEntry entry, LDAPSearchConstraints cons ) throws LDAPException { bind (cons); LDAPResponseListener myListener = getResponseListener (); LDAPAttributeSet attrs = entry.getAttributeSet (); LDAPAttribute[] attrList = new LDAPAttribute[attrs.size()]; for( int i = 0; i < attrs.size(); i++ ) attrList[i] = (LDAPAttribute)attrs.elementAt( i ); int attrPosition = 0; JDAPMessage response; try { sendRequest (new JDAPAddRequest (entry.getDN(), attrList), myListener, cons); response = myListener.getResponse(); checkMsg (response); } catch (LDAPReferralException e) { performReferrals(e, cons, JDAPProtocolOp.ADD_REQUEST, null, 0, null, null, false, null, entry, null, null); } finally { releaseResponseListener (myListener); } } /** * Performs an extended operation on the directory. Extended operations * are part of version 3 of the LDAP protocol.

* * Note that in order for the extended operation to work, the server * that you are connecting to must support LDAP v3 and must be configured * to process the specified extended operation. * * @param op LDAPExtendedOperation object specifying the OID of the * extended operation and the data to be used in the operation. * @exception LDAPException Failed to execute the operation * @return LDAPExtendedOperation object representing the extended response * returned by the server. * @see netscape.ldap.LDAPExtendedOperation */ public LDAPExtendedOperation extendedOperation( LDAPExtendedOperation op ) throws LDAPException { return extendedOperation(op, defaultConstraints); } /** * Performs an extended operation on the directory. Extended operations * are part of version 3 of the LDAP protocol. This method allows the * user to set the preferences for the operation.

* * Note that in order for the extended operation to work, the server * that you are connecting to must support LDAP v3 and must be configured * to process the specified extended operation. * * @param op LDAPExtendedOperation object specifying the OID of the * extended operation and the data to be used in the operation. * @param cons Preferences for the extended operation. * @exception LDAPException Failed to execute the operation * @return LDAPExtendedOperation object representing the extended response * returned by the server. * @see netscape.ldap.LDAPExtendedOperation */ public LDAPExtendedOperation extendedOperation( LDAPExtendedOperation op, LDAPSearchConstraints cons) throws LDAPException { bind (cons); LDAPResponseListener myListener = getResponseListener (); JDAPMessage response = null; byte[] results = null; String resultID; try { sendRequest ( new JDAPExtendedRequest( op.getID(), op.getValue() ), myListener, cons ); response = myListener.getResponse(); checkMsg (response); JDAPExtendedResponse res = (JDAPExtendedResponse)response.getProtocolOp(); results = res.getValue(); resultID = res.getID(); } catch (LDAPReferralException e) { return performExtendedReferrals( e, cons, op ); } finally { releaseResponseListener (myListener); } return new LDAPExtendedOperation( resultID, results ); } /** * Makes a single change to an existing entry in the directory * (for example, changes the value of an attribute, adds a new * attribute value, or removes an existing attribute value).

* * Use the LDAPModification object to specify the change * that needs to be made and the LDAPAttribute object * to specify the attribute value that needs to be changed. The * LDAPModification object allows you add an attribute * value, change an attibute value, or remove an attribute * value.

* * For example, the following section of code changes Barbara Jensen's email * address in the directory to babs@aceindustry.com.

* *

     * ...
     * String myEntryDN = "cn=Barbara Jensen,ou=Product Development,o=Ace Industry,c=US";
     *
     * LDAPAttribute attrEmail = new LDAPAttribute( "mail", "babs@aceindustry.com" );
     * LDAPModification singleChange = new LDAPModification( LDAPModification.REPLACE, attrEmail );
     *
     * myConn.modify( myEntryDN, singleChange );
     * ... 
* * @param DN The distinguished name of the entry that you want to modify * @param mod A single change to be made to the entry * @exception LDAPException Failed to make the specified change to the * directory entry. * @see netscape.ldap.LDAPModification */ public void modify( String DN, LDAPModification mod ) throws LDAPException { modify(DN, mod, defaultConstraints); } /** * Makes a single change to an existing entry in the directory and * allows you to specify preferences for this LDAP modify operation * by using an LDAPSearchConstraints object. For * example, you can specify whether or not to follow referrals. * You can also apply LDAP v3 controls to the operation. *

* * @param DN The distinguished name of the entry that you want to modify * @param mod A single change to be made to the entry * @param cons The set of preferences that you want applied to this operation. * @exception LDAPException Failed to make the specified change to the * directory entry. * @see netscape.ldap.LDAPModification * @see netscape.ldap.LDAPSearchConstraints */ public void modify( String DN, LDAPModification mod, LDAPSearchConstraints cons ) throws LDAPException { LDAPModification[] mods = new LDAPModification [1]; mods[0] = mod; modify (DN, mods, cons); } /** * Makes a set of changes to an existing entry in the directory * (for example, changes attribute values, adds new attribute values, * or removes existing attribute values).

* * Use the LDAPModificationSet object to specify the set * of changes that needs to be made. Changes are specified in terms * of attribute values. Each attribute value to be modified, added, * or removed must be specified by an LDAPAttribute object. *

* * For example, the following section of code changes Barbara Jensen's * title, adds a telephone number to the entry, and removes the room * number from the entry.

* *

     * ...
     * String myEntryDN = "cn=Barbara Jensen,ou=Product Development,o=Ace Industry,c=US";
     *
     * LDAPModificationSet manyChanges = new LDAPModificationSet();
     * LDAPAttribute attrTelephoneNumber = new LDAPAttribute( "telephoneNumber",
     *                                                        "555-1212" );
     * manyChanges.add( LDAPModification.ADD, attrTelephoneNumber );
     * LDAPAttribute attrRoomNumber = new LDAPAttribute( "roomnumber", "222" );
     * manyChanges.add( LDAPModification.DELETE, attrRoomNumber );
     * LDAPAttribute attrTitle = new LDAPAttribute( "title",
     *                                       "Manager of Product Development" );
     * manyChanges.add( LDAPModification.REPLACE, attrTitle );
     *
     * myConn.modify( myEntryDN, manyChanges );
     * ... 
* * @param DN The distinguished name of the entry that you want to modify * @param mods A set of changes to be made to the entry * @exception LDAPException Failed to make the specified changes to the * directory entry. * @see netscape.ldap.LDAPModificationSet */ public void modify (String DN, LDAPModificationSet mods) throws LDAPException { modify(DN, mods, defaultConstraints); } /** * Makes a set of changes to an existing entry in the directory and * allows you to specify preferences for this LDAP modify operation * by using an LDAPSearchConstraints object. For * example, you can specify whether or not to follow referrals. * You can also apply LDAP v3 controls to the operation. *

* * @param DN The distinguished name of the entry that you want to modify * @param mods A set of changes to be made to the entry * @param cons The set of preferences that you want applied to this operation. * @exception LDAPException Failed to make the specified changes to the * directory entry. * @see netscape.ldap.LDAPModificationSet * @see netscape.ldap.LDAPSearchConstraints */ public void modify (String DN, LDAPModificationSet mods, LDAPSearchConstraints cons) throws LDAPException { LDAPModification[] modList = new LDAPModification[mods.size()]; for( int i = 0; i < mods.size(); i++ ) modList[i] = mods.elementAt( i ); modify (DN, modList, cons); } /** * Makes a set of changes to an existing entry in the directory * (for example, changes attribute values, adds new attribute values, * or removes existing attribute values).

* * Use an array of LDAPModification objects to specify the * changes that need to be made. Each change must be specified by * an LDAPModification object, and each attribute value * to be modified, added, or removed must be specified by an * LDAPAttribute object.

* * @param DN The distinguished name of the entry that you want to modify * @param mods An array of objects representing the changes to be made * to the entry * @exception LDAPException Failed to make the specified changes to the * directory entry. * @see netscape.ldap.LDAPModification */ public void modify (String DN, LDAPModification[] mods) throws LDAPException { modify(DN, mods, defaultConstraints); } /** * Makes a set of changes to an existing entry in the directory and * allows you to specify preferences for this LDAP modify operation * by using an LDAPSearchConstraints object. For * example, you can specify whether or not to follow referrals. * You can also apply LDAP v3 controls to the operation. *

* * @param DN The distinguished name of the entry that you want to modify * @param mods An array of objects representing the changes to be made * to the entry * @param cons The set of preferences that you want applied to this operation. * @exception LDAPException Failed to make the specified changes to the * directory entry. * @see netscape.ldap.LDAPModification * @see netscape.ldap.LDAPSearchConstraints */ public void modify (String DN, LDAPModification[] mods, LDAPSearchConstraints cons) throws LDAPException { bind (cons); LDAPResponseListener myListener = getResponseListener (); JDAPMessage response = null; try { sendRequest (new JDAPModifyRequest (DN, mods), myListener, cons); response = myListener.getResponse(); checkMsg (response); } catch (LDAPReferralException e) { performReferrals(e, cons, JDAPProtocolOp.MODIFY_REQUEST, DN, 0, null, null, false, mods, null, null, null); } finally { releaseResponseListener (myListener); } } /** * Deletes the entry for the specified DN from the directory.

* * For example, the following section of code deletes the entry for * Barbara Jensen from the directory.

* *

     * ...
     * String myEntryDN = "cn=Barbara Jensen,ou=Product Development,o=Ace Industry,c=US";
     * myConn.delete( myEntryDN );
     * ... 
* * @param DN Distinguished name identifying the entry that you want * to remove from the directory. * @exception LDAPException Failed to delete the specified entry from * the directory. */ public void delete( String DN ) throws LDAPException { delete(DN, defaultConstraints); } /** * Deletes the entry for the specified DN from the directory and * allows you to specify preferences for this LDAP delete operation * by using an LDAPSearchConstraints object. For * example, you can specify whether or not to follow referrals. * You can also apply LDAP v3 controls to the operation. *

* * @param DN Distinguished name identifying the entry that you want * to remove from the directory. * @param cons The set of preferences that you want applied to this operation. * @exception LDAPException Failed to delete the specified entry from * the directory. * @see netscape.ldap.LDAPSearchConstraints */ public void delete( String DN, LDAPSearchConstraints cons ) throws LDAPException { bind (cons); LDAPResponseListener myListener = getResponseListener (); JDAPMessage response; try { sendRequest (new JDAPDeleteRequest (DN), myListener, cons); response = myListener.getResponse(); checkMsg (response); } catch (LDAPReferralException e) { performReferrals(e, cons, JDAPProtocolOp.DEL_REQUEST, DN, 0, null, null, false, null, null, null, null); } finally { releaseResponseListener (myListener); } } /** * Renames an existing entry in the directory.

* * You can specify whether or not the original name of the entry is * retained as a value in the entry. For example, suppose you rename * the entry "cn=Barbara" to "cn=Babs". You can keep "cn=Barbara" * as a value in the entry so that the cn attribute has two values:

* *

     *       cn=Barbara
     *       cn=Babs
     * 
* The following example renames an entry. The old name of the entry * is kept as a value in the entry.

* *

     * ...
     * String myEntryDN = "cn=Barbara Jensen,ou=Product Development,o=Ace Industry,c=US";
     * String newRDN = "cn=Babs Jensen";
     * myConn.rename( myEntryDN, newRDN, false );
     * ... 
* * @param DN Current distinguished name of the entry. * @param newRDN New relative distinguished name for the entry (for example, * "cn=newName"). * @param deleteOldRDN If true, the old name is not retained * as an attribute value (for example, the attribute value "cn=oldName" is * removed). If false, the old name is retained * as an attribute value (for example, the entry might now have two values * for the cn attribute: "cn=oldName" and "cn=newName"). * @exception LDAPException Failed to rename the specified entry. */ public void rename (String DN, String newRDN, boolean deleteOldRDN ) throws LDAPException { rename(DN, newRDN, null, deleteOldRDN); } /** * Renames an existing entry in the directory.

* * You can specify whether or not the original name of the entry is * retained as a value in the entry. For example, suppose you rename * the entry "cn=Barbara" to "cn=Babs". You can keep "cn=Barbara" * as a value in the entry so that the cn attribute has two values:

* *

     *       cn=Barbara
     *       cn=Babs
     * 
* The following example renames an entry. The old name of the entry * is kept as a value in the entry.

* *

     * ...
     * String myEntryDN = "cn=Barbara Jensen,ou=Product Development,o=Ace Industry,c=US";
     * String newRDN = "cn=Babs Jensen";
     * myConn.rename( myEntryDN, newRDN, false );
     * ... 
* * @param DN Current distinguished name of the entry. * @param newRDN New relative distinguished name for the entry (for example, * "cn=newName"). * @param deleteOldRDN If true, the old name is not retained * as an attribute value (for example, the attribute value "cn=oldName" is * removed). If false, the old name is retained * as an attribute value (for example, the entry might now have two values * for the cn attribute: "cn=oldName" and "cn=newName"). * @param cons The set of preferences that you want applied to this operation. * @exception LDAPException Failed to rename the specified entry. */ public void rename (String DN, String newRDN, boolean deleteOldRDN, LDAPSearchConstraints cons ) throws LDAPException { rename(DN, newRDN, null, deleteOldRDN, cons); } /** * Renames an existing entry in the directory and (optionally) * changes the location of the entry in the directory tree.

* * NOTE: Netscape Directory Server 3.0 does not support the * capability to move an entry to a different location in the * directory tree. If you specify a value for the newParentDN * argument, an LDAPException will be thrown. *

* * @param DN Current distinguished name of the entry. * @param newRDN New relative distinguished name for the entry (for example, * "cn=newName"). * @param newParentDN If not null, the distinguished name for the * entry under which the entry should be moved (for example, to move * an entry under the Accounting subtree, specify this argument as * "ou=Accounting, o=Ace Industry, c=US"). * @param deleteOldRDN If true, the old name is not retained * as an attribute value (for example, the attribute value "cn=oldName" is * removed). If false, the old name is retained * as an attribute value (for example, the entry might now have two values * for the cn attribute: "cn=oldName" and "cn=newName"). * @exception LDAPException Failed to rename the specified entry. */ public void rename(String dn, String newRDN, String newParentDN, boolean deleteOldRDN) throws LDAPException { rename(dn, newRDN, newParentDN, deleteOldRDN, defaultConstraints); } /** * Renames an existing entry in the directory and (optionally) * changes the location of the entry in the directory tree. Also * allows you to specify preferences for this LDAP modify DN operation * by using an LDAPSearchConstraints object. For * example, you can specify whether or not to follow referrals. * You can also apply LDAP v3 controls to the operation. *

* * NOTE: Netscape Directory Server 3.0 does not support the * capability to move an entry to a different location in the * directory tree. If you specify a value for the newParentDN * argument, an LDAPException will be thrown. *

* * @param DN Current distinguished name of the entry. * @param newRDN New relative distinguished name for the entry (for example, * "cn=newName"). * @param newParentDN If not null, the distinguished name for the * entry under which the entry should be moved (for example, to move * an entry under the Accounting subtree, specify this argument as * "ou=Accounting, o=Ace Industry, c=US"). * @param deleteOldRDN If true, the old name is not retained * as an attribute value (for example, the attribute value "cn=oldName" is * removed). If false, the old name is retained * as an attribute value (for example, the entry might now have two values * for the cn attribute: "cn=oldName" and "cn=newName"). * @param cons The set of preferences that you want applied to this operation. * @exception LDAPException Failed to rename the specified entry. * @see netscape.ldap.LDAPSearchConstraints */ public void rename (String DN, String newRDN, String newParentDN, boolean deleteOldRDN, LDAPSearchConstraints cons) throws LDAPException { bind (cons); LDAPResponseListener myListener = getResponseListener (); JDAPMessage response; try { JDAPModifyRDNRequest request = null; if ( newParentDN != null ) request = new JDAPModifyRDNRequest (DN, newRDN, deleteOldRDN, newParentDN); else request = new JDAPModifyRDNRequest (DN, newRDN, deleteOldRDN); sendRequest (request, myListener, cons); response = myListener.getResponse(); checkMsg (response); } catch (LDAPReferralException e) { performReferrals(e, cons, JDAPProtocolOp.MODIFY_RDN_REQUEST, DN, 0, newRDN, null, deleteOldRDN, null, null, null, null); } finally { releaseResponseListener (myListener); } } /** * Returns the value of the specified option for this * LDAPConnection object.

* * These options represent the search constraints for the current connection. * To get all search constraints for the current connection, call the * getSearchConstraints method. *

* * By default, the search constraints apply to all searches performed * through the current connection. You can change these search constraints: *

* *

    *
  • If you want to override these constraints only for a particular * search, create an LDAPSearchConstraints object with * your new constraints and pass it to the * LDAPConnection.search method. *

    * *

  • If you want to override these constraints for all searches * performed under the current connection, call the * setOption method to change the search constraint. *

    * *

*

* * For example, the following section of code gets and prints the * maximum number of search results that are returned for searches * performed through this connection. (This applies to all searches * unless a different set of search constraints is specified in an * LDAPSearchConstraints object.) *

* *

     * LDAPConnection ld = new LDAPConnection();
     * int sizeLimit = ( (Integer)ld.getOption( LDAPv2.SIZELIMIT ) ).intValue();
     * System.out.println( "Maximum number of results: " + sizeLimit );
     * 
* * @param option You can specify one of the following options: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
OptionData TypeDescription
* LDAPv2.PROTOCOL_VERSIONIntegerSpecifies the version of the LDAP protocol used by the * client. *

By default, the value of this option is 2.

* LDAPv2.DEREFIntegerSpecifies when your client dereferences aliases. *
     * Legal values for this option are:
     *
     * DEREF_NEVER       Aliases are never dereferenced.
     *
     * DEREF_FINDING     Aliases are dereferenced when find-
     *                   ing the starting point for the
     *                   search (but not when searching
     *                   under that starting entry).
     *
     * DEREF_SEARCHING   Aliases are dereferenced when
     *                   searching the entries beneath the
     *                   starting point of the search (but
     *                   not when finding the starting
     *                   entry).
     *
     * DEREF_ALWAYS      Aliases are always dereferenced.
     *
*

By default, the value of this option is * DEREF_NEVER.

* LDAPv2.SIZELIMITIntegerSpecifies the maximum number of search results to return. * If this option is set to 0, there is no maximum limit. *

By default, the value of this option is 1000.

* LDAPv2.TIMELIMITIntegerSpecifies the maximum number of milliseconds to wait for results * before timing out. If this option is set to 0, there is no maximum * time limit. *

By default, the value of this option is 0.

* LDAPv2.REFERRALSBooleanSpecifies whether or not your client follows referrals automatically. * If true, your client follows referrals automatically. * If false, an LDAPReferralException is raised * when referral is detected. *

By default, the value of this option is false.

* LDAPv2.REFERRALS_REBIND_PROCLDAPRebindSpecifies an object with a class that implements the * LDAPRebind interface. You must define this class and * the getRebindAuthentication method that will be used to * get the distinguished name and password to use for authentication. *

By default, the value of this option is null.

* LDAPv2.REFERRALS_HOP_LIMITIntegerSpecifies the maximum number of referrals in a sequence that * your client will follow. (For example, if REFERRALS_HOP_LIMIT is 5, * your client will follow no more than 5 referrals in a row when resolving * a single LDAP request.) *

By default, the value of this option is 10.

* LDAPv2.BATCHSIZEIntegerSpecifies the number of search results to return at a time. * (For example, if BATCHSIZE is 1, results are returned one at a time.) *

By default, the value of this option is 1.

* LDAPv3.CLIENTCONTROLSLDAPControl[]Specifies the client controls that may affect the handling of LDAP * operations in the LDAP classes. These controls are used by the client * and are not passed to the LDAP server. At this time, no client controls * are defined for clients built with the Netscape LDAP classes.
* LDAPv3.SERVERCONTROLSLDAPControl[]Specifies the server controls that are passed to the LDAP * server on each LDAP operation. Not all servers support server * controls; a particular server may or may not support a given * server control.
* MAXBACKLOGIntegerSpecifies the maximum number of search results to accumulate in an * LDAPSearchResults before suspending the reading of input from the server. *

By default, the value of this option is 100.

* @return The value for the option wrapped in an object. (You * need to cast the returned value as its appropriate type. For * example, when getting the SIZELIMIT option, cast the returned * value as an Integer.) * @exception LDAPException Failed to get the specified option. * @see netscape.ldap.LDAPRebind * @see netscape.ldap.LDAPSearchConstraints * @see netscape.ldap.LDAPReferralException * @see netscape.ldap.LDAPControl * @see netscape.ldap.LDAPConnection#getSearchConstraints * @see netscape.ldap.LDAPConnection#search(java.lang.String, int, java.lang.String, java.lang.String[], boolean, netscape.ldap.LDAPSearchConstraints) */ public Object getOption( int option ) throws LDAPException { if (option == LDAPv2.PROTOCOL_VERSION) { return new Integer(protocolVersion); } return getOption(option, defaultConstraints); } private static Object getOption( int option, LDAPSearchConstraints cons ) throws LDAPException { switch (option) { case LDAPv2.DEREF: return new Integer (cons.getDereference()); case LDAPv2.SIZELIMIT: return new Integer (cons.getMaxResults()); case LDAPv2.TIMELIMIT: return new Integer (cons.getServerTimeLimit()); case LDAPv2.REFERRALS: return new Boolean (cons.getReferrals()); case LDAPv2.REFERRALS_REBIND_PROC: return cons.getRebindProc(); case LDAPv2.REFERRALS_HOP_LIMIT: return new Integer (cons.getHopLimit()); case LDAPv2.BATCHSIZE: return new Integer (cons.getBatchSize()); case LDAPv3.CLIENTCONTROLS: return cons.getClientControls(); case LDAPv3.SERVERCONTROLS: return cons.getServerControls(); case MAXBACKLOG: return new Integer (cons.getMaxBacklog()); default: throw new LDAPException ( "invalid option", LDAPException.PARAM_ERROR ); } } /** * Sets the value of the specified option for this * LDAPConnection object.

* * These options represent the search constraints for the current * connection. * To get all search constraints for the current connection, call the * getSearchConstraints method. *

* * By default, the option that you set applies to all subsequent * searches performed through the current connection. If you want to * set a constraint only for a particular search, create an * LDAPSearchConstraints object with your new constraints * and pass it to the LDAPConnection.search method. *

* * For example, the following section of code changes the constraint for * the maximum number of search results that are returned for searches * performed through this connection. (This applies to all searches * unless a different set of search constraints is specified in an * LDAPSearchConstraints object.) *

* *

     * LDAPConnection ld = new LDAPConnection();
     * Integer newLimit = new Integer( 20 );
     * ld.setOption( LDAPv2.SIZELIMIT, newLimit );
     * System.out.println( "Changed the maximum number of results to " + newLimit.intValue() );
     * 
* * @param option You can specify one of the following options: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
OptionData TypeDescription
* LDAPv2.PROTOCOL_VERSIONIntegerSpecifies the version of the LDAP protocol used by the * client. *

By default, the value of this option is 2. If you want * to use LDAP v3 features (such as extended operations or * controls), you need to set this value to 3.

* LDAPv2.DEREFIntegerSpecifies when your client dereferences aliases. *
     * Legal values for this option are:
     *
     * DEREF_NEVER       Aliases are never dereferenced.
     *
     * DEREF_FINDING     Aliases are dereferenced when find-
     *                   ing the starting point for the
     *                   search (but not when searching
     *                   under that starting entry).
     *
     * DEREF_SEARCHING   Aliases are dereferenced when
     *                   searching the entries beneath the
     *                   starting point of the search (but
     *                   not when finding the starting
     *                   entry).
     *
     * DEREF_ALWAYS      Aliases are always dereferenced.
     *
*

By default, the value of this option is * DEREF_NEVER.

* LDAPv2.SIZELIMITIntegerSpecifies the maximum number of search results to return. * If this option is set to 0, there is no maximum limit. *

By default, the value of this option is 1000.

* LDAPv2.TIMELIMITIntegerSpecifies the maximum number of milliseconds to wait for results * before timing out. If this option is set to 0, there is no maximum * time limit. *

By default, the value of this option is 0.

* LDAPv2.REFERRALSBooleanSpecifies whether or not your client follows referrals automatically. * If true, your client follows referrals automatically. * If false, an LDAPReferralException is * raised when a referral is detected. *

By default, the value of this option is false.

* LDAPv2.REFERRALS_REBIND_PROCLDAPRebindSpecifies an object with a class that implements the * LDAPRebind * interface. You must define this class and the * getRebindAuthentication method that will be used to get * the distinguished name and password to use for authentication. *

By default, the value of this option is null.

* LDAPv2.REFERRALS_HOP_LIMITIntegerSpecifies the maximum number of referrals in a sequence that * your client will follow. (For example, if REFERRALS_HOP_LIMIT is 5, * your client will follow no more than 5 referrals in a row when resolving * a single LDAP request.) *

By default, the value of this option is 10.

* LDAPv2.BATCHSIZEIntegerSpecifies the number of search results to return at a time. * (For example, if BATCHSIZE is 1, results are returned one at a time.) *

By default, the value of this option is 1.

* LDAPv3.CLIENTCONTROLSLDAPControl[]Specifies the client controls that may affect handling of LDAP * operations in the LDAP classes. These controls are used by the client * and are not passed to the server. At this time, no client controls * are defined for clients built with the Netscape LDAP classes.
* LDAPv3.SERVERCONTROLSLDAPControl[]Specifies the server controls that are passed to the LDAP * server on each LDAP operation. Not all servers support server * controls; a particular server may or may not support a particular * control.
* MAXBACKLOGIntegerSpecifies the maximum number of search results to accumulate in an * LDAPSearchResults before suspending the reading of input from the server. *

By default, the value of this option is 100.

* @param value The value to assign to the option. The value must be * the java.lang object wrapper for the appropriate parameter * (e.g. boolean->Boolean, * integer->Integer) * @exception LDAPException Failed to set the specified option. * @see netscape.ldap.LDAPRebind * @see netscape.ldap.LDAPSearchConstraints * @see netscape.ldap.LDAPReferralException * @see netscape.ldap.LDAPControl * @see netscape.ldap.LDAPConnection#getSearchConstraints * @see netscape.ldap.LDAPConnection#search(java.lang.String, int, java.lang.String, java.lang.String[], boolean, netscape.ldap.LDAPSearchConstraints) */ public void setOption( int option, Object value ) throws LDAPException { if (option == LDAPv2.PROTOCOL_VERSION) { setProtocolVersion(((Integer)value).intValue()); return; } setOption(option, value, defaultConstraints); if ( (option == MAXBACKLOG) && (th != null) ) { int val = ((Integer)value).intValue(); if ( val >= 1 ) { th.setMaxBacklog( val ); } } } private static void setOption( int option, Object value, LDAPSearchConstraints cons ) throws LDAPException { try { switch (option) { case LDAPv2.DEREF: cons.setDereference(((Integer)value).intValue()); return; case LDAPv2.SIZELIMIT: cons.setMaxResults(((Integer)value).intValue()); return; case LDAPv2.TIMELIMIT: cons.setServerTimeLimit(((Integer)value).intValue()); return; case LDAPv2.REFERRALS: cons.setReferrals(((Boolean)value).booleanValue()); return; case LDAPv2.REFERRALS_REBIND_PROC: cons.setRebindProc((LDAPRebind)value); return; case LDAPv2.REFERRALS_HOP_LIMIT: cons.setHopLimit(((Integer)value).intValue()); return; case LDAPv2.BATCHSIZE: cons.setBatchSize(((Integer)value).intValue()); return; case LDAPv3.CLIENTCONTROLS: if ( value == null ) cons.setClientControls( (LDAPControl[]) null ); else if ( value instanceof LDAPControl ) cons.setClientControls( (LDAPControl) value ); else if ( value instanceof LDAPControl[] ) cons.setClientControls( (LDAPControl[])value ); else throw new LDAPException ( "invalid LDAPControl", LDAPException.PARAM_ERROR ); return; case LDAPv3.SERVERCONTROLS: if ( value == null ) cons.setServerControls( (LDAPControl[]) null ); else if ( value instanceof LDAPControl ) cons.setServerControls( (LDAPControl) value ); else if ( value instanceof LDAPControl[] ) cons.setServerControls( (LDAPControl[])value ); else throw new LDAPException ( "invalid LDAPControl", LDAPException.PARAM_ERROR ); return; case MAXBACKLOG: int val = ((Integer)value).intValue(); if ( val < 1 ) { throw new LDAPException ( "MAXBACKLOG must be at least 1", LDAPException.PARAM_ERROR ); } else { cons.setMaxBacklog(((Integer)value).intValue()); } return; default: throw new LDAPException ("invalid option", LDAPException.PARAM_ERROR ); } } catch (ClassCastException cc) { throw new LDAPException ("invalid option value", LDAPException.PARAM_ERROR ); } } /** * Returns an array of the latest controls (if any) from server. * @return An array of the controls returned by an operation, or * null if none. * @see netscape.ldap.LDAPControl */ public LDAPControl[] getResponseControls() { LDAPControl[] controls = null; /* Get the latest controls returned for our thread */ synchronized(m_responseControlTable) { Vector responses = (Vector)m_responseControlTable.get(th); if (responses != null) { // iterate through each response control for (int i=0,size=responses.size(); isearch * method).

* * Note that if you want to get individual constraints (rather than * getting the * entire set of constraints), call the getOption method. *

* * Typically, you might call the getSearchConstraints method * if you want to create a slightly different set of search constraints * that you want to apply to a particular search. *

* * For example, the following section of code changes the maximum number * of results to 10 for a specific search. Rather than construct a new * set of search constraints from scratch, the example gets the current * settings for the connections and just changes the setting for the * maximum results. *

* * Note that this change only applies to the searches performed with this * custom set of constraints. All other searches performed through this * connection use the original set of search constraints. *

* *

     * ...
     * LDAPSearchConstraints myOptions = ld.getSearchConstraints();
     * myOptions.setMaxResults( 10 );
     * String[] myAttrs = { "objectclass" };
     * LDAPSearchResults myResults = ld.search( "o=Ace Industry,c=US",
     *                                          LDAPv2.SCOPE_SUB,
     *                                          "(objectclass=*)",
     *                                          myAttrs,
     *                                          false,
     *                                          myOptions );
     * ...
     * 
* * @return The LDAPSearchConstraints object representing the * set of search constraints that apply (by default) to all searches * performed through this connection. * @see netscape.ldap.LDAPSearchConstraints * @see netscape.ldap.LDAPConnection#getOption * @see netscape.ldap.LDAPConnection#search(java.lang.String, int, java.lang.String, java.lang.String[], boolean, netscape.ldap.LDAPSearchConstraints) */ public LDAPSearchConstraints getSearchConstraints () { return defaultConstraints; } /** * Get a new listening agent from the internal buffer of available agents. * These objects are used to make the asynchronous LDAP operations * synchronous. * @return response listener object */ private synchronized LDAPResponseListener getResponseListener () { if (responseListeners == null) responseListeners = new Vector (5); LDAPResponseListener l; if ( responseListeners.size() < 1 ) { l = new LDAPResponseListener ( this ); } else { l = (LDAPResponseListener)responseListeners.elementAt (0); responseListeners.removeElementAt (0); } return l; } /** * Get a new search listening agent from the internal buffer of available * agents. These objects are used to make the asynchronous LDAP operations * synchronous. * @return A search response listener object */ private synchronized LDAPSearchListener getSearchListener ( LDAPSearchConstraints cons ) { if (searchListeners == null) { searchListeners = new Vector (5); } LDAPSearchListener l; if ( searchListeners.size() < 1 ) { l = new LDAPSearchListener ( this, cons ); } else { l = (LDAPSearchListener)searchListeners.elementAt (0); searchListeners.removeElementAt (0); } return l; } /** * Put a listening agent into the internal buffer of available agents. * These objects are used to make the asynchronous LDAP operations * synchronous. * @param l Listener to buffer */ private synchronized void releaseResponseListener (LDAPResponseListener l) { if (responseListeners == null) responseListeners = new Vector (5); l.reset (); responseListeners.addElement (l); } /** * Put a search listening agent into the internal buffer of available * agents. These objects are used to make the asynchronous LDAP * operations synchronous. * @param Listener to buffer */ synchronized void releaseSearchListener (LDAPSearchListener l) { if (searchListeners == null) searchListeners = new Vector (5); l.reset (); searchListeners.addElement (l); } /** * Checks the message (assumed to be a return value). If the resultCode * is anything other than SUCCESS, it throws an LDAPException describing * the server's (error) response. * @param m Server response to validate * @exception LDAPException failed to check message */ void checkMsg (JDAPMessage m) throws LDAPException { if (m.getProtocolOp() instanceof JDAPResult) { JDAPResult response = (JDAPResult)(m.getProtocolOp()); int resultCode = response.getResultCode (); if (resultCode == JDAPResult.SUCCESS) return; if (resultCode == JDAPResult.REFERRAL) throw new LDAPReferralException ("referral", resultCode, response.getReferrals()); if (resultCode == JDAPResult.LDAP_PARTIAL_RESULTS) throw new LDAPReferralException ("referral", resultCode, response.getErrorMessage()); else throw new LDAPException ("error result", resultCode, response.getErrorMessage(), response.getMatchedDN()); } else if (m.getProtocolOp() instanceof JDAPSearchResultReference) { String[] referrals = ((JDAPSearchResultReference)m.getProtocolOp()).getUrls(); throw new LDAPReferralException ("referral", JDAPResult.SUCCESS, referrals); } else return; } /** * Set response controls for the current connection for a particular * thread. Get the oldest returned controls and remove them from the * queue. If the connection is executing a persistent search, there may * be more than one set of controls in the queue. For any other * operation, there will only ever be at most one set of controls * (controls from any earlier operation are replaced by controls * received on the latest operation on this connection by this thread). * @param current The target thread. * @param con The server response controls. */ void setResponseControls( LDAPConnThread current, ResponseControl con ) { synchronized(m_responseControlTable) { Vector v = (Vector)m_responseControlTable.get(current); // if the current thread already contains response controls from // a previous operation if ((v != null) && (v.size() > 0)) { // look at each response control for (int i=v.size()-1; i>=0; i--) { ResponseControl response = (ResponseControl)v.elementAt(i); // if this response control belongs to this connection if (response.getConnection().equals(this)) { // if the given control is null or // the given control is not null and the current // control does not correspond to the new JDAPMessage if ((con == null) || (con.getMsgID() != response.getMsgID())) v.removeElement(response); // For the same connection, if the message id from the // given control is the same as the one in the queue, // those controls in the queue will not get removed // since they come from the persistent search control // which allows more than one control response for // one persistent search operation. } } } else { if (con != null) v = new Vector(); } if (con != null) { v.addElement(con); m_responseControlTable.put(current, v); } /* Do some garbage collection: check if any attached threads have exited */ /* Now check all threads in the list */ Enumeration e = m_attachedList.elements(); while( e.hasMoreElements() ) { LDAPConnThread aThread = (LDAPConnThread)e.nextElement(); if ( !aThread.isAlive() ) { m_responseControlTable.remove( aThread ); m_attachedList.removeElement( aThread ); } } } /* Make sure we're registered */ if ( m_attachedList.indexOf( current ) < 0 ) m_attachedList.addElement( current ); } /** * Set up connection for referral. * @param u referral URL * @param cons search constraints * @return new LDAPConnection, already connected and authenticated */ private LDAPConnection prepareReferral( LDAPUrl u, LDAPSearchConstraints cons ) throws LDAPException { LDAPConnection connection = new LDAPConnection (this.getSocketFactory()); connection.setOption(REFERRALS, new Boolean(true)); connection.setOption(REFERRALS_REBIND_PROC, cons.getRebindProc()); // need to set the protocol version which gets passed to connection connection.setOption(PROTOCOL_VERSION, new Integer(protocolVersion)); connection.setOption(REFERRALS_HOP_LIMIT, new Integer(cons.getHopLimit()-1)); connection.connect (u.getHost(), u.getPort()); return connection; } /** * Establish the LDAPConnection to the referred server. This one is used * for bind operation only since we need to keep this new connection for * the subsequent operations. This new connection will be destroyed in * two circumstances: disconnect gets called or the client binds as * someone else. * @return The new LDAPConnection to the referred server */ LDAPConnection createReferralConnection(LDAPReferralException e, LDAPSearchConstraints cons) throws LDAPException { if (cons.getHopLimit() <= 0) throw new LDAPException("exceed hop limit", e.getLDAPResultCode(), e.getLDAPErrorMessage()); if (!cons.getReferrals()) { throw e; } LDAPUrl u[] = e.getURLs(); // If there are no referrals (because the server isn't set up for // them), give up here if ((u == null) || (u.length <= 0) || (u[0].equals(""))) throw new LDAPException("No target URL in referral", LDAPException.NO_RESULTS_RETURNED); LDAPConnection connection = null; connection = prepareReferral(u[0], cons); String DN = u[0].getDN(); if ((DN == null) || (DN.equals(""))) DN = boundDN; connection.authenticate(protocolVersion, DN, boundPasswd); return connection; } /** * Performs the referral. * @param e referral exception * @param cons search constraints */ void performReferrals(LDAPReferralException e, LDAPSearchConstraints cons, int ops, /* unions of different operation parameters */ String dn, int scope, String filter, String types[], boolean attrsOnly, LDAPModification mods[], LDAPEntry entry, LDAPAttribute attr, /* result */ Vector results ) throws LDAPException { if (cons.getHopLimit() <= 0) throw new LDAPException("exceed hop limit", e.getLDAPResultCode(), e.getLDAPErrorMessage()); if (!cons.getReferrals()) { if (ops == JDAPProtocolOp.SEARCH_REQUEST) { LDAPSearchResults res = new LDAPSearchResults(); res.add(e); results.addElement(res); return; } else throw e; } LDAPUrl u[] = e.getURLs(); // If there are no referrals (because the server isn't set up for // them), give up here if ( u == null ) return; for (int i = 0; i < u.length; i++) { String newDN = u[i].getDN(); String DN = null; if ((newDN == null) || (newDN.equals(""))) DN = dn; else DN = newDN; // If this was a one-level search, and a direct subordinate // has a referral, there will be a "?base" in the URL, and // we have to rewrite the scope from one to base if ( u[i].getUrl().indexOf("?base") > -1 ) { scope = SCOPE_BASE; } LDAPSearchResults res = null; LDAPSearchConstraints newcons = (LDAPSearchConstraints)cons.clone(); newcons.setHopLimit( cons.getHopLimit()-1 ); try { if ((m_referralConnection != null) && (u[i].getHost().equals(m_referralConnection.host)) && (u[i].getPort() == m_referralConnection.port)) { performReferrals(m_referralConnection, newcons, ops, DN, scope, filter, types, attrsOnly, mods, entry, attr, results); continue; } } catch (LDAPException le) { if (le.getLDAPResultCode() != LDAPException.INSUFFICIENT_ACCESS_RIGHTS) { throw le; } } LDAPConnection connection = null; connection = prepareReferral( u[i], cons ); if (cons.getRebindProc() == null) { connection.authenticate (null, null); } else { LDAPRebindAuth auth = cons.getRebindProc().getRebindAuthentication(u[i].getHost(), u[i].getPort()); connection.authenticate(auth.getDN(), auth.getPassword()); } performReferrals(connection, newcons, ops, DN, scope, filter, types, attrsOnly, mods, entry, attr, results); } } void performReferrals(LDAPConnection connection, LDAPSearchConstraints cons, int ops, String dn, int scope, String filter, String types[], boolean attrsOnly, LDAPModification mods[], LDAPEntry entry, LDAPAttribute attr, Vector results) throws LDAPException { LDAPSearchResults res = null; try { switch (ops) { case JDAPProtocolOp.SEARCH_REQUEST: res = connection.search(dn, scope, filter, types, attrsOnly, cons); if (res != null) { res.closeOnCompletion(connection); results.addElement(res); } else { if ((m_referralConnection == null) || (!connection.equals(m_referralConnection))) connection.disconnect(); } break; case JDAPProtocolOp.MODIFY_REQUEST: connection.modify(dn, mods, cons); break; case JDAPProtocolOp.ADD_REQUEST: if ((dn != null) && (!dn.equals(""))) entry.setDN(dn); connection.add(entry, cons); break; case JDAPProtocolOp.DEL_REQUEST: connection.delete(dn, cons); break; case JDAPProtocolOp.MODIFY_RDN_REQUEST: connection.rename(dn, filter /* newRDN */, attrsOnly /* deleteOld */, cons); break; case JDAPProtocolOp.COMPARE_REQUEST: boolean bool = connection.compare(dn, attr, cons); results.addElement(new Boolean(bool)); break; default: /* impossible */ break; } } catch (LDAPException ee) { throw ee; } finally { if ((connection != null) && ((ops != JDAPProtocolOp.SEARCH_REQUEST) || (res == null)) && ((m_referralConnection == null) || (!connection.equals(m_referralConnection)))) connection.disconnect(); } } /** * Performs the referral. * @param e referral exception * @param cons search constraints */ private LDAPExtendedOperation performExtendedReferrals( LDAPReferralException e, LDAPSearchConstraints cons, LDAPExtendedOperation op ) throws LDAPException { if (cons.getHopLimit() <= 0) throw new LDAPException("exceed hop limit", e.getLDAPResultCode(), e.getLDAPErrorMessage()); if (!cons.getReferrals()) { throw e; } LDAPUrl u[] = e.getURLs(); // If there are no referrals (because the server isn't set up for // them), give up here if ( u == null ) return null; for (int i = 0; i < u.length; i++) { try { LDAPConnection connection = prepareReferral( u[i], cons ); LDAPExtendedOperation results = connection.extendedOperation( op ); connection.disconnect(); return results; /* return right away if operation is successful */ } catch (LDAPException ee) { continue; } } return null; } /** * Creates and returns an new LDAPConnection object that * contains the same information as the current connection, including: *
    *
  • the default search constraints *
  • host name and port number of the LAAP server *
  • the DN and password used to authenticate to the LDAP server *
*

* @return The LDAPconnection object representing the * new object. */ public synchronized Object clone() { try { LDAPConnection c = (LDAPConnection)super.clone(); if (this.th == null) this.bind(defaultConstraints); c.defaultConstraints = (LDAPSearchConstraints)defaultConstraints.clone(); c.responseListeners = null; c.searchListeners = null; c.bound = this.bound; c.host = this.host; c.port = this.port; c.boundDN = this.boundDN; c.boundPasswd = this.boundPasswd; c.prevBoundDN = this.prevBoundDN; c.prevBoundPasswd = this.prevBoundPasswd; c.m_anonymousBound = this.m_anonymousBound; c.m_factory = this.m_factory; c.th = this.th; /* share current connection thread */ synchronized (m_threadConnTable) { Vector v = (Vector)m_threadConnTable.get(this.th); if (v != null) { v.addElement(c); } else { printDebug("Failed to clone"); return null; } } c.th.register(c); return c; } catch (Exception e) { } return null; } /** * This is called when a search result has been retrieved from the incoming * queue. We use the notification to unblock the listener thread, if it * is waiting for the backlog to lighten. */ void resultRetrieved() { if ( th != null ) { th.resultRetrieved(); } } /** * Sets up basic connection privileges for Communicator. * @return true if in Communicator and connections okay. */ private static boolean checkCommunicator() { try { java.lang.reflect.Method m = LDAPCheckComm.getMethod( "netscape.security.PrivilegeManager", "enablePrivilege"); if (m == null) { printDebug("Method is null"); return false; } Object[] args = new Object[1]; args[0] = new String("UniversalConnect"); m.invoke( null, args); printDebug( "UniversalConnect enabled" ); return true; } catch (LDAPException e) { printDebug("Exception: "+e.toString()); } catch (Exception ie) { printDebug("Exception on invoking " + "enablePrivilege: "+ie.toString()); } return false; } /** * Reports if the class is running in a Netscape browser. * @return true if the class is running in a Netscape browser. */ public static boolean isNetscape() { return isCommunicator; } static void printDebug( String msg ) { if ( debug ) { System.out.println( msg ); } } /** * Prints out the LDAP Java SDK version and the highest LDAP * protocol version supported by the SDK. To view this * information, open a terminal window, and enter: *

java netscape.ldap.LDAPConnection
     * 
* @param args Not currently used. */ public static void main(String[] args) { System.out.println("LDAP SDK Version is "+SdkVersion); System.out.println("LDAP Protocol Version is "+ProtocolVersion); } /** * Option specifying the maximum number of unread entries to be cached in any * LDAPSearchResults without suspending reading from the server. * @see netscape.ldap.LDAPConnection#getOption * @see netscape.ldap.LDAPConnection#setOption */ public static final int MAXBACKLOG = 30; private static boolean isCommunicator = checkCommunicator(); private static final boolean debug = false; } /* * This object represents the value of the m_responseControlTable hashtable. * It stores the response controls and its corresponding LDAPConnection and * the message ID for its corresponding JDAPMessage. */ class ResponseControl { private LDAPConnection m_connection; private int m_messageID; private LDAPControl[] m_controls; public ResponseControl(LDAPConnection conn, int msgID, LDAPControl[] controls) { m_connection = conn; m_messageID = msgID; m_controls = new LDAPControl[controls.length]; for (int i=0; i 0) ) { JDAPControl[] jc = new JDAPControl[lc.length]; for( int i = 0; i < lc.length; i++ ) jc[i] = new JDAPControl( lc[i].getID(), lc[i].isCritical(), lc[i].getValue() ); msg = new JDAPMessage(allocateId(), request, jc); } else msg = new JDAPMessage(allocateId(), request); if ( toNotify != null ) { if (!(request instanceof JDAPAbandonRequest || request instanceof JDAPUnbindRequest)) { /* Only worry about toNotify if we expect a response... */ this.m_requests.put (new Integer (msg.getId()), toNotify); /* Notify the backlog checker that there may be another outstanding request */ resultRetrieved(); } toNotify.setID( msg.getId() ); } try { msg.write (m_serverOutput); m_serverOutput.flush (); } catch (IOException e) { networkError(e); } } /** * Register with this connection thread. * @param conn LDAP connection */ public synchronized void register(LDAPConnection conn) { if (!m_registered.contains(conn)) m_registered.addElement(conn); } void setSecurityLayer(Object layer) { m_securityLayer = layer; } synchronized int getClientCount() { return m_registered.size(); } /** * De-Register with this connection thread. If all the connection * is deregistered. Then, this thread should be killed. * @param conn LDAP connection */ public synchronized void deregister(LDAPConnection conn) { m_registered.removeElement(conn); if (m_registered.size() == 0) { try { LDAPSearchConstraints cons = conn.getSearchConstraints(); sendRequest (new JDAPUnbindRequest (), null, cons); cleanUp(); this.stop(); this.sleep(100); /* give enough time for threads to shutdown */ } catch (Exception e) { LDAPConnection.printDebug(e.toString()); } } } /** * Clean ups before shutdown the thread. */ private void cleanUp() { if (!m_disconnected) { try { m_serverOutput.close (); } catch (Exception e) { } finally { m_serverOutput = null; } try { m_serverInput.close (); } catch (Exception e) { } finally { m_serverInput = null; } try { m_socket.close (); } catch (Exception e) { } finally { m_socket = null; } m_disconnected = true; /** * Notify all the registered about this bad moment. * IMPORTANT: This needs to be done at last. Otherwise, the socket * input stream and output stream might never get closed and the whole * task will get stuck in the stop method when we tried to stop the * LDAPConnThread. */ if (m_registered != null) { Vector registerCopy = (Vector)m_registered.clone(); Enumeration cancelled = registerCopy.elements(); while (cancelled.hasMoreElements ()) { LDAPConnection c = (LDAPConnection)cancelled.nextElement(); c.deregisterConnection(); } } m_registered = null; m_messages = null; m_requests.clear(); } } /** * Set and get the maximum number of unread entries any search listener can * have before we stop reading from the server. */ void setMaxBacklog( int backlog ) { m_maxBacklog = backlog; } int getMaxBacklog() { return m_maxBacklog; } /** * Sleep if there is a backlog of search results */ private void checkBacklog() { boolean doWait; do { doWait = false; Enumeration listeners = m_requests.elements(); while( listeners.hasMoreElements() ) { LDAPResponseListener l = (LDAPResponseListener)listeners.nextElement(); // If there are any threads waiting for a regular response // message, we have to go read the next incoming message if ( !(l instanceof LDAPSearchListener) ) { doWait = false; break; } // If the backlog of any search thread is too great, // wait for it to diminish, but if this is a synchronous // search, then just keep reading LDAPSearchListener sl = (LDAPSearchListener)l; if ( (sl.getConstraints().getBatchSize() != 0) && (sl.getCount() >= m_maxBacklog) ) { doWait = true; } } if ( doWait ) { synchronized(this) { try { wait(); } catch (InterruptedException e ) { } } } } while ( doWait ); } /** * This is called when a search result has been retrieved from the incoming * queue. We use the notification to unblock the listener thread, if it * is waiting for the backlog to lighten. */ synchronized void resultRetrieved() { notifyAll(); } /** * Reads from the LDAP server input stream for incoming LDAP messages. */ public void run() { // if there is a problem of establishing connection to the server, // stop the thread right away... if (m_failed) this.stop(); JDAPMessage msg = null; JDAPBERTagDecoder decoder = new JDAPBERTagDecoder(); while (true) { yield(); int[] nread = new int[1]; nread[0] = 0; // Avoid too great a backlog of results checkBacklog(); try { BERElement element = BERElement.getElement(decoder, m_serverInput, nread); msg = new JDAPMessage(element); // passed in the ber element size to approximate the size of the cache // entry, thereby avoiding serialization of the entry stored in the // cache processResponse (msg, nread[0]); } catch (Exception e) { networkError(e); } if (m_disconnected) break; } } /** * Allocates a new LDAP message ID. These are arbitrary numbers used to * correlate client requests with server responses. * @return new unique msgId */ private synchronized int allocateId () { m_highMsgId = (m_highMsgId + 1) % MAXMSGID; return m_highMsgId; } /** * When a response arrives from the LDAP server, it is processed by * this routine. It will pass the message on to the listening object * associated with the LDAP msgId. * @param incoming New message from LDAP server */ private synchronized void processResponse (JDAPMessage incoming, int size) { Integer messageID = new Integer (incoming.getId()); LDAPResponseListener l = (LDAPResponseListener)m_requests.get (messageID); if (l == null) return; /* nobody is waiting for this response (!) */ /* Were there any controls for this client? */ LDAPControl[] con = checkControls( incoming ); ResponseControl responseControl = null; if (con != null) responseControl = new ResponseControl(l.getConnection(), incoming.getId(), con); l.getConnection().setResponseControls( this, responseControl ); JDAPProtocolOp op = incoming.getProtocolOp (); Vector v = null; if ((op instanceof JDAPSearchResponse) || (op instanceof JDAPSearchResultReference)) { ((LDAPSearchListener)l).addSearchResult (incoming); Long key = ((LDAPSearchListener)l).getKey(); if ((m_cache != null) && (key != null)) { // get the vector containing the JDAPmessages for the specified messageID v = (Vector)m_messages.get(messageID); if (v == null) { v = new Vector(); // keeps track of the total size of all JDAPMessages belonging to the // same messageID, now the size is 0 v.addElement(new Long(0)); } // add the size of the current JDAPmessage to the lump sum // assume the size of JDAPMessage is more or less the same as the size // of LDAPEntry. Eventually LDAPEntry object gets stored in the cache // instead of JDAPMessage object. long entrySize = ((Long)v.firstElement()).longValue() + size; // update the lump sum located in the first element of the vector v.setElementAt(new Long(entrySize), 0); // convert JDAPMessage object into LDAPEntry which is stored to the // end of the Vector v.addElement(constructLDAPEntry(incoming)); // replace the entry m_messages.put(messageID, v); } } else { l.setResponse (incoming); if (l instanceof LDAPSearchListener) { Long key = ((LDAPSearchListener)l).getKey(); if (key != null) { boolean fail = false; JDAPProtocolOp protocolOp = incoming.getProtocolOp(); if (protocolOp instanceof JDAPSearchResult) { JDAPResult res = (JDAPResult)protocolOp; if (res.getResultCode() > 0) fail = true; } if ((!fail) && (m_cache != null)) { // that is. Collects all the JDAPMessages for the specified messageID // no need to keep track of this entry. Remove it. v = (Vector)m_messages.remove(messageID); // If v is null, meaning there are no search results from the // server if (v == null) { v = new Vector(); // set the entry size to be 0 v.addElement(new Long(0)); } try { // add the new entry with key and value (a vector of // LDAPEntry objects) m_cache.addEntry(key, v); } catch (LDAPException e) { System.out.println("Exception: "+e.toString()); } } } } m_requests.remove (messageID); } } /** * Construct the LDAPEntry from the JDAPMessage object. * @param msg The JDAPMessage object * @return The LDAPEntry object required for the cache entry. */ private LDAPEntry constructLDAPEntry(JDAPMessage msg) { JDAPProtocolOp op = msg.getProtocolOp(); JDAPSearchResponse sr = (JDAPSearchResponse)op; LDAPAttribute[] lattrs = sr.getAttributes(); LDAPAttributeSet attrs; if ( lattrs != null ) attrs = new LDAPAttributeSet( lattrs ); else attrs = new LDAPAttributeSet(); String dn = sr.getObjectName(); return(new LDAPEntry( dn, attrs )); } /** * Extract controls from message and stash them away for retrieval * by client. * @param response The results of an operation, possibly containing * Controls. */ private LDAPControl[] checkControls( JDAPMessage response ) { LDAPControl[] con = null; if ( response != null ) { JDAPControl[] jc = response.getControls(); if ( (jc != null) && (jc.length > 0) ) { con = new LDAPControl[jc.length]; for( int i = 0; i < jc.length; i++ ) con[i] = new LDAPControl( jc[i].getID(), jc[i].isCritical(), jc[i].getValue() ); } } return con; } /** * Stop dispatching responses for a particular message ID. * @param id Message ID for which to discard responses. */ void abandon (int id ) { m_requests.remove( new Integer(id) ); } /** * Handles network errors. Basically shuts down the whole connection. * @param e The exception which was caught while trying to read from * input stream. */ private synchronized void networkError (Exception e) { try { // notify each listener that the server is down. Enumeration requests = m_requests.elements(); while (requests.hasMoreElements()) { LDAPResponseListener listener = (LDAPResponseListener)requests.nextElement(); listener.setException(new LDAPException("Server down", LDAPException.OTHER)); } cleanUp(); } catch (NullPointerException ee) { System.err.println("Exception: "+ee.toString()); } /** * Notify all the registered about this bad moment. * IMPORTANT: This needs to be done at last. Otherwise, the socket * input stream and output stream might never get closed and the whole * task will get stuck in the stop method when we tried to stop the * LDAPConnThread. */ if (m_registered != null) { Vector registerCopy = (Vector)m_registered.clone(); Enumeration cancelled = registerCopy.elements(); while (cancelled.hasMoreElements ()) { LDAPConnection c = (LDAPConnection)cancelled.nextElement(); c.deregisterConnection(); } } } }