/******************************************************************************
* The MIT License
* Copyright (c) 2003 Novell Inc. www.novell.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the Software), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*******************************************************************************/
//
// Novell.Directory.Ldap.LdapConnection.cs
//
// Author:
// Sunil Kumar (Sunilk@novell.com)
//
// (C) 2003 Novell, Inc (http://www.novell.com)
//
using System;
using Novell.Directory.Ldap;
using Novell.Directory.Ldap.Asn1;
using Novell.Directory.Ldap.Rfc2251;
using Novell.Directory.Ldap.Utilclass;
#if !TARGET_JVM
using Mono.Security.Protocol.Tls;
#endif
using System.Security.Cryptography.X509Certificates;
namespace Novell.Directory.Ldap
{
/// The central class that encapsulates the connection
/// to a directory server through the Ldap protocol.
/// LdapConnection objects are used to perform common Ldap
/// operations such as search, modify and add.
///
/// In addition, LdapConnection objects allow you to bind to an
/// Ldap server, set connection and search constraints, and perform
/// several other tasks.
///
/// An LdapConnection object is not connected on
/// construction and can only be connected to one server at one
/// port. Multiple threads may share this single connection, typically
/// by cloning the connection object, one for each thread. An
/// application may have more than one LdapConnection object, connected
/// to the same or different directory servers.
///
///
///
public class LdapConnection : System.ICloneable
{
private void InitBlock()
{
defSearchCons = new LdapSearchConstraints();
responseCtlSemaphore = new System.Object();
}
/// Returns the protocol version uses to authenticate.
///
/// 0 is returned if no authentication has been performed.
///
///
/// The protol version used for authentication or 0
/// not authenticated.
///
///
virtual public int ProtocolVersion
{
get
{
BindProperties prop = conn.BindProperties;
if (prop == null)
{
return Ldap_V3;
}
return prop.ProtocolVersion;
}
}
/// Returns the distinguished name (DN) used for as the bind name during
/// the last successful bind operation. null
is returned
/// if no authentication has been performed or if the bind resulted in
/// an aonymous connection.
///
///
/// The distinguished name if authenticated; otherwise, null.
///
///
virtual public System.String AuthenticationDN
{
get
{
BindProperties prop = conn.BindProperties;
if (prop == null)
{
return null;
}
if (prop.Anonymous)
{
return null;
}
return prop.AuthenticationDN;
}
}
/// Returns the method used to authenticate the connection. The return
/// value is one of the following:
///
///
/// - "none" indicates the connection is not authenticated.
///
///
/// - "simple" indicates simple authentication was used or that a null
/// or empty authentication DN was specified.
///
/// - "sasl" indicates that a SASL mechanism was used to authenticate
///
///
///
/// The method used to authenticate the connection.
///
virtual public System.String AuthenticationMethod
{
get
{
BindProperties prop = conn.BindProperties;
if (prop == null)
{
return "simple";
}
return conn.BindProperties.AuthenticationMethod;
}
}
/// Returns the properties if any specified on binding with a
/// SASL mechanism.
///
/// Null is returned if no authentication has been performed
/// or no authentication Map is present.
///
///
/// The bind properties Map Object used for SASL bind or null if
/// the connection is not present or not authenticated.
///
///
virtual public System.Collections.IDictionary SaslBindProperties
{
get
{
BindProperties prop = conn.BindProperties;
if (prop == null)
{
return null;
}
return conn.BindProperties.SaslBindProperties;
}
}
/// Returns the call back handler if any specified on binding with a
/// SASL mechanism.
///
/// Null is returned if no authentication has been performed
/// or no authentication call back handler is present.
///
///
/// The call back handler used for SASL bind or null if the
/// object is not present or not authenticated.
///
///
virtual public System.Object SaslBindCallbackHandler
{
get
{
BindProperties prop = conn.BindProperties;
if (prop == null)
{
return null;
}
return conn.BindProperties.SaslCallbackHandler;
}
}
/// Returns a copy of the set of constraints associated with this
/// connection. These constraints apply to all operations performed
/// through this connection (unless a different set of constraints is
/// specified when calling an operation method).
///
///
/// The set of default contraints that apply to this connection.
///
///
/// Sets the constraints that apply to all operations performed through
/// this connection (unless a different set of constraints is specified
/// when calling an operation method). An LdapSearchConstraints object
/// which is passed to this method sets all constraints, while an
/// LdapConstraints object passed to this method sets only base constraints.
///
///
/// An LdapConstraints or LdapSearchConstraints Object
/// containing the contstraint values to set.
///
///
///
///
///
///
virtual public LdapConstraints Constraints
{
get
{
return (LdapConstraints) (this.defSearchCons).Clone();
}
set
{
// Set all constraints, replace the object with a new one
if (value is LdapSearchConstraints)
{
defSearchCons = (LdapSearchConstraints) value.Clone();
return ;
}
// We set the constraints this way, so a thread doesn't get an
// conconsistant view of the referrals.
LdapSearchConstraints newCons = (LdapSearchConstraints) defSearchCons.Clone();
newCons.HopLimit = value.HopLimit;
newCons.TimeLimit = value.TimeLimit;
newCons.setReferralHandler(value.getReferralHandler());
newCons.ReferralFollowing = value.ReferralFollowing;
LdapControl[] lsc = value.getControls();
if (lsc != null)
{
newCons.setControls(lsc);
}
System.Collections.Hashtable lp = newCons.Properties;
if (lp != null)
{
newCons.Properties = lp;
}
defSearchCons = newCons;
return ;
}
}
/// Returns the host name of the Ldap server to which the object is or
/// was last connected, in the format originally specified.
///
///
/// The host name of the Ldap server to which the object last
/// connected or null if the object has never connected.
///
///
virtual public System.String Host
{
get
{
return conn.Host;
}
}
/// Returns the port number of the Ldap server to which the object is or
/// was last connected.
///
///
/// The port number of the Ldap server to which the object last
/// connected or -1 if the object has never connected.
///
///
virtual public int Port
{
get
{
return conn.Port;
}
}
/// Returns a copy of the set of search constraints associated with this
/// connection. These constraints apply to search operations performed
/// through this connection (unless a different set of
/// constraints is specified when calling the search operation method).
///
///
/// The set of default search contraints that apply to
/// this connection.
///
///
///
///
///
///
virtual public LdapSearchConstraints SearchConstraints
{
get
{
return (LdapSearchConstraints) this.defSearchCons.Clone();
}
}
/// Indicates whther the perform Secure Operation or not
///
///
///
/// True if SSL is on
/// False if its not on
///
public bool SecureSocketLayer
{
get
{
return conn.Ssl;
}
set
{
conn.Ssl=value;
}
}
/// Indicates whether the object has authenticated to the connected Ldap
/// server.
///
///
/// True if the object has authenticated; false if it has not
/// authenticated.
///
///
virtual public bool Bound
{
get
{
return conn.Bound;
}
}
/// Indicates whether the connection represented by this object is open
/// at this time.
///
///
/// True if connection is open; false if the connection is closed.
///
virtual public bool Connected
{
get
{
return conn.Connected;
}
}
/// Indicatates if the connection is protected by TLS.
///
///
/// If startTLS has completed this method returns true.
/// If stopTLS has completed or start tls failed, this method returns false.
///
/// True if the connection is protected by TLS.
///
///
virtual public bool TLS
{
get
{
return conn.TLS;
}
}
/// Returns the Server Controls associated with the most recent response
/// to a synchronous request on this connection object, or null
/// if the latest response contained no Server Controls. The method
/// always returns null for asynchronous requests. For asynchronous
/// requests, the response controls are available in LdapMessage.
///
///
/// The server controls associated with the most recent response
/// to a synchronous request or null if the response contains no server
/// controls.
///
///
///
///
virtual public LdapControl[] ResponseControls
{
get
{
if (responseCtls == null)
{
return null;
}
// We have to clone the control just in case
// we have two client threads that end up retreiving the
// same control.
LdapControl[] clonedControl = new LdapControl[responseCtls.Length];
// Also note we synchronize access to the local response
// control object just in case another message containing controls
// comes in from the server while we are busy duplicating
// this one.
lock (responseCtlSemaphore)
{
for (int i = 0; i < responseCtls.Length; i++)
{
clonedControl[i] = (LdapControl) (responseCtls[i]).Clone();
}
}
// Return the cloned copy. Note we have still left the
// control in the local responseCtls variable just in case
// somebody requests it again.
return clonedControl;
}
}
/// Return the Connection object associated with this LdapConnection
///
///
/// the Connection object
///
virtual internal Connection Connection
{
/* package */
get
{
return conn;
}
}
/// Return the Connection object name associated with this LdapConnection
///
///
/// the Connection object name
///
virtual internal System.String ConnectionName
{
/* package */
get
{
return name;
}
}
private LdapSearchConstraints defSearchCons;
private LdapControl[] responseCtls = null;
// Synchronization Object used to synchronize access to responseCtls
private System.Object responseCtlSemaphore;
private Connection conn = null;
private static System.Object nameLock; // protect agentNum
private static int lConnNum = 0; // Debug, LdapConnection number
private System.String name; // String name for debug
/// Used with search to specify that the scope of entrys to search is to
/// search only the base obect.
///
/// SCOPE_BASE = 0
///
public const int SCOPE_BASE = 0;
/// Used with search to specify that the scope of entrys to search is to
/// search only the immediate subordinates of the base obect.
///
/// SCOPE_ONE = 1
///
public const int SCOPE_ONE = 1;
/// Used with search to specify that the scope of entrys to search is to
/// search the base object and all entries within its subtree.
///
/// SCOPE_ONE = 2
///
public const int SCOPE_SUB = 2;
/// Used with search instead of an attribute list to indicate that no
/// attributes are to be returned.
///
/// NO_ATTRS = "1.1"
///
public const System.String NO_ATTRS = "1.1";
/// Used with search instead of an attribute list to indicate that all
/// attributes are to be returned.
///
/// ALL_USER_ATTRS = "*"
///
public const System.String ALL_USER_ATTRS = "*";
/// Specifies the Ldapv3 protocol version when performing a bind operation.
///
/// Specifies Ldap version V3 of the protocol, and is specified
/// when performing bind operations.
/// You can use this identifier in the version parameter
/// of the bind method to specify an Ldapv3 bind.
/// Ldap_V3 is the default protocol version
///
/// Ldap_V3 = 3
///
///
public const int Ldap_V3 = 3;
/// The default port number for Ldap servers.
///
/// You can use this identifier to specify the port when establishing
/// a clear text connection to a server. This the default port.
///
/// DEFAULT_PORT = 389
///
///
public const int DEFAULT_PORT = 389;
/// The default SSL port number for Ldap servers.
///
/// DEFAULT_SSL_PORT = 636
///
/// You can use this identifier to specify the port when establishing
/// a an SSL connection to a server..
///
public const int DEFAULT_SSL_PORT = 636;
/// A string that can be passed in to the getProperty method.
///
/// Ldap_PROPERTY_SDK = "version.sdk"
///
/// You can use this string to request the version of the SDK.
///
public const System.String Ldap_PROPERTY_SDK = "version.sdk";
/// A string that can be passed in to the getProperty method.
///
/// Ldap_PROPERTY_PROTOCOL = "version.protocol"
///
/// You can use this string to request the version of the
/// Ldap protocol.
///
public const System.String Ldap_PROPERTY_PROTOCOL = "version.protocol";
/// A string that can be passed in to the getProperty method.
///
/// Ldap_PROPERTY_SECURITY = "version.security"
///
/// You can use this string to request the type of security
/// being used.
///
public const System.String Ldap_PROPERTY_SECURITY = "version.security";
/// A string that corresponds to the server shutdown notification OID.
/// This notification may be used by the server to advise the client that
/// the server is about to close the connection due to an error
/// condition.
///
/// SERVER_SHUTDOWN_OID = "1.3.6.1.4.1.1466.20036"
///
public const System.String SERVER_SHUTDOWN_OID = "1.3.6.1.4.1.1466.20036";
/// The OID string that identifies a StartTLS request and response.
private const System.String START_TLS_OID = "1.3.6.1.4.1.1466.20037";
/*
* Constructors
*/
/// Constructs a new LdapConnection object, which will use the supplied
/// class factory to construct a socket connection during
/// LdapConnection.connect method.
///
///
/// An object capable of producing a Socket.
///
///
public LdapConnection()
{
InitBlock();
// Get a unique connection name for debug
conn = new Connection();
return ;
}
/* public LdapConnection(X509Certificate cert)
{
InitBlock();
// Get a unique connection name for debug
conn = new Connection();
conn.Cert = cert;
return ;
}
*/
/*
* The following are methods that affect the operation of
* LdapConnection, but are not Ldap requests.
*/
/// Returns a copy of the object with a private context, but sharing the
/// network connection if there is one.
///
/// The network connection remains open until all clones have
/// disconnected or gone out of scope. Any connection opened after
/// cloning is private to the object making the connection.
///
/// The clone can issue requests and freely modify options and search
/// constraints, and , without affecting the source object or other clones.
/// If the clone disconnects or reconnects, it is completely dissociated
/// from the source object and other clones. Reauthenticating in a clone,
/// however, is a global operation which will affect the source object
/// and all associated clones, because it applies to the single shared
/// physical connection. Any request by an associated object after one
/// has reauthenticated will carry the new identity.
///
///
/// A of the object.
///
public System.Object Clone()
{
LdapConnection newClone;
System.Object newObj;
try
{
newObj = base.MemberwiseClone();
newClone = (LdapConnection) newObj;
}
catch (System.Exception ce)
{
throw new System.SystemException("Internal error, cannot create clone");
}
newClone.conn = conn; // same underlying connection
//now just duplicate the defSearchCons and responseCtls
if (defSearchCons != null)
{
newClone.defSearchCons = (LdapSearchConstraints) defSearchCons.Clone();
}
else
{
newClone.defSearchCons = null;
}
if (responseCtls != null)
{
newClone.responseCtls = new LdapControl[responseCtls.Length];
for (int i = 0; i < responseCtls.Length; i++)
{
newClone.responseCtls[i] = (LdapControl) responseCtls[i].Clone();
}
}
else
{
newClone.responseCtls = null;
}
conn.incrCloneCount(); // Increment the count of clones
return newObj;
}
/// Closes the connection, if open, and releases any other resources held
/// by the object.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
///
///
///
~LdapConnection()
{
// Disconnect did not come from user API call
Disconnect(defSearchCons, false);
return ;
}
/// Returns a property of a connection object.
///
///
/// Name of the property to be returned.
///
/// The following read-only properties are available
/// for any given connection:
///
/// - Ldap_PROPERTY_SDK returns the version of this SDK,
/// as a Float data type.
///
/// - Ldap_PROPERTY_PROTOCOL returns the highest supported version of
/// the Ldap protocol, as a Float data type.
///
/// - Ldap_PROPERTY_SECURITY returns a comma-separated list of the
/// types of authentication supported, as a
/// string.
///
///
/// A deep copy of the property is provided where applicable; a
/// client does not need to clone the object received.
///
///
/// The object associated with the requested property,
/// or null if the property is not defined.
///
///
///
///
///
///
public virtual System.Object getProperty(System.String name)
{
if (name.ToUpper().Equals(Ldap_PROPERTY_SDK.ToUpper()))
return Connection.sdk;
else if (name.ToUpper().Equals(Ldap_PROPERTY_PROTOCOL.ToUpper()))
return Connection.protocol;
else if (name.ToUpper().Equals(Ldap_PROPERTY_SECURITY.ToUpper()))
return Connection.security;
else
{
return null;
}
}
/// Registers an object to be notified on arrival of an unsolicited
/// message from a server.
///
/// An unsolicited message has the ID 0. A new thread is created and
/// the method "messageReceived" in each registered object is called in
/// turn.
///
///
/// An object to be notified on arrival of an
/// unsolicited message from a server. This object must
/// implement the LdapUnsolicitedNotificationListener interface.
///
///
public virtual void AddUnsolicitedNotificationListener(LdapUnsolicitedNotificationListener listener)
{
if (listener != null)
conn.AddUnsolicitedNotificationListener(listener);
}
/// Deregisters an object so that it will no longer be notified on
/// arrival of an unsolicited message from a server. If the object is
/// null or was not previously registered for unsolicited notifications,
/// the method does nothing.
///
///
///
/// An object to no longer be notified on arrival of
/// an unsolicited message from a server.
///
///
public virtual void RemoveUnsolicitedNotificationListener(LdapUnsolicitedNotificationListener listener)
{
if (listener != null)
conn.RemoveUnsolicitedNotificationListener(listener);
}
/// Starts Transport Layer Security (TLS) protocol on this connection
/// to enable session privacy.
///
/// This affects the LdapConnection object and all cloned objects. A
/// socket factory that implements LdapTLSSocketFactory must be set on the
/// connection.
///
///
/// LdapException Thrown if TLS cannot be started. If a
/// SocketFactory has been specified that does not implement
/// LdapTLSSocketFactory an LdapException is thrown.
///
///
public virtual void startTLS()
{
LdapMessage startTLS = MakeExtendedOperation(new LdapExtendedOperation(LdapConnection.START_TLS_OID, null), null);
int tlsID = startTLS.MessageID;
conn.acquireWriteSemaphore(tlsID);
try
{
if (!conn.areMessagesComplete())
{
throw new LdapLocalException(ExceptionMessages.OUTSTANDING_OPERATIONS, LdapException.OPERATIONS_ERROR);
}
// Stop reader when response to startTLS request received
conn.stopReaderOnReply(tlsID);
// send tls message
LdapResponseQueue queue = SendRequestToServer(startTLS, defSearchCons.TimeLimit, null, null);
LdapExtendedResponse response = (LdapExtendedResponse) queue.getResponse();
response.chkResultCode();
conn.startTLS();
}
finally
{
//Free this semaphore no matter what exceptions get thrown
conn.startReader();
conn.freeWriteSemaphore(tlsID);
}
return ;
}
/// Stops Transport Layer Security(TLS) on the LDAPConnection and reverts
/// back to an anonymous state.
///
/// @throws LDAPException This can occur for the following reasons:
///
/// - StartTLS has not been called before stopTLS
/// - There exists outstanding messages that have not received all
/// responses
/// - The sever was not able to support the operation
///
/// Note: The Sun and IBM implementions of JSSE do not currently allow
/// stopping TLS on an open Socket. In order to produce the same results
/// this method currently disconnects the socket and reconnects, giving
/// the application an anonymous connection to the server, as required
/// by StopTLS
///
public virtual void stopTLS()
{
if (!TLS)
{
throw new LdapLocalException(ExceptionMessages.NO_STARTTLS, LdapException.OPERATIONS_ERROR);
}
int semaphoreID = conn.acquireWriteSemaphore();
try
{
if (!conn.areMessagesComplete())
{
throw new LdapLocalException(ExceptionMessages.OUTSTANDING_OPERATIONS, LdapException.OPERATIONS_ERROR);
}
//stopTLS stops and starts the reader thread for us.
conn.stopTLS();
}
finally
{
conn.freeWriteSemaphore(semaphoreID);
/* Now that the TLS socket is closed, reset everything. This next
line is temporary until JSSE is fixed to properly handle TLS stop */
this.Connect(this.Host, this.Port);
}
return ;
}
//*************************************************************************
// Below are all of the Ldap protocol operation methods
//*************************************************************************
//*************************************************************************
// abandon methods
//*************************************************************************
///
///
/// Notifies the server not to send additional results associated with
/// this LdapSearchResults object, and discards any results already
/// received.
///
///
/// An object returned from a search.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual void Abandon(LdapSearchResults results)
{
Abandon(results, defSearchCons);
return ;
}
///
///
/// Notifies the server not to send additional results associated with
/// this LdapSearchResults object, and discards any results already
/// received.
///
///
/// An object returned from a search.
///
///
/// The contraints specific to the operation.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual void Abandon(LdapSearchResults results, LdapConstraints cons)
{
results.Abandon();
return ;
}
///
/// Abandons an asynchronous operation.
///
///
/// The ID of the asynchronous operation to abandon. The ID
/// can be obtained from the response queue for the
/// operation.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual void Abandon(int id)
{
Abandon(id, defSearchCons);
return ;
}
/// Abandons an asynchronous operation, using the specified
/// constraints.
///
///
/// The ID of the asynchronous operation to abandon.
/// The ID can be obtained from the search
/// queue for the operation.
///
///
/// The contraints specific to the operation.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual void Abandon(int id, LdapConstraints cons)
{
// We need to inform the Message Agent which owns this messageID to
// remove it from the queue.
try
{
MessageAgent agent = conn.getMessageAgent(id);
agent.Abandon(id, cons);
return ;
}
catch (System.FieldAccessException ex)
{
return ; // Ignore error
}
}
/// Abandons all outstanding operations managed by the queue.
///
/// All operations in progress, which are managed by the specified queue,
/// are abandoned.
///
///
/// The queue returned from an asynchronous request.
/// All outstanding operations managed by the queue
/// are abandoned, and the queue is emptied.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual void Abandon(LdapMessageQueue queue)
{
Abandon(queue, defSearchCons);
return ;
}
/// Abandons all outstanding operations managed by the queue.
///
/// All operations in progress, which are managed by the specified
/// queue, are abandoned.
///
///
/// The queue returned from an asynchronous request.
/// All outstanding operations managed by the queue
/// are abandoned, and the queue is emptied.
///
///
/// The contraints specific to the operation.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual void Abandon(LdapMessageQueue queue, LdapConstraints cons)
{
if (queue != null)
{
MessageAgent agent;
if (queue is LdapSearchQueue)
{
agent = queue.MessageAgent;
}
else
{
agent = queue.MessageAgent;
}
int[] msgIds = agent.MessageIDs;
for (int i = 0; i < msgIds.Length; i++)
{
agent.Abandon(msgIds[i], cons);
}
}
return ;
}
//*************************************************************************
// add methods
//*************************************************************************
/// Synchronously adds an entry to the directory.
///
///
/// LdapEntry object specifying the distinguished
/// name and attributes of the new entry.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual void Add(LdapEntry entry)
{
Add(entry, defSearchCons);
return ;
}
///
/// Synchronously adds an entry to the directory, using the specified
/// constraints.
///
///
/// LdapEntry object specifying the distinguished
/// name and attributes of the new entry.
///
///
/// Constraints specific to the operation.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual void Add(LdapEntry entry, LdapConstraints cons)
{
LdapResponseQueue queue = Add(entry, null, cons);
// Get a handle to the add response
LdapResponse addResponse = (LdapResponse) (queue.getResponse());
// Set local copy of responseControls synchronously if there were any
lock (responseCtlSemaphore)
{
responseCtls = addResponse.Controls;
}
chkResultCode(queue, cons, addResponse);
return ;
}
/// Asynchronously adds an entry to the directory.
///
///
/// LdapEntry object specifying the distinguished
/// name and attributes of the new entry.
///
///
/// Handler for messages returned from a server in
/// response to this request. If it is null, a
/// queue object is created internally.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual LdapResponseQueue Add(LdapEntry entry, LdapResponseQueue queue)
{
return Add(entry, queue, defSearchCons);
}
/// Asynchronously adds an entry to the directory, using the specified
/// constraints.
///
///
/// LdapEntry object specifying the distinguished
/// name and attributes of the new entry.
///
///
/// Handler for messages returned from a server in
/// response to this request. If it is null, a
/// queue object is created internally.
///
///
/// Constraints specific to the operation.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual LdapResponseQueue Add(LdapEntry entry, LdapResponseQueue queue, LdapConstraints cons)
{
if (cons == null)
cons = defSearchCons;
// error check the parameters
if (entry == null)
{
throw new System.ArgumentException("The LdapEntry parameter" + " cannot be null");
}
if ((System.Object) entry.DN == null)
{
throw new System.ArgumentException("The DN value must be present" + " in the LdapEntry object");
}
LdapMessage msg = new LdapAddRequest(entry, cons.getControls());
return SendRequestToServer(msg, cons.TimeLimit, queue, null);
}
//*************************************************************************
// bind methods
//*************************************************************************
/// Synchronously authenticates to the Ldap server (that the object is
/// currently connected to) as an Ldapv3 bind, using the specified name and
/// password.
///
/// If the object has been disconnected from an Ldap server,
/// this method attempts to reconnect to the server. If the object
/// has already authenticated, the old authentication is discarded.
///
///
/// If non-null and non-empty, specifies that the
/// connection and all operations through it should
/// be authenticated with dn as the distinguished
/// name.
///
///
/// 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.
///
/// Note: the application should use care in the use
/// of String password objects. These are long lived
/// objects, and may expose a security risk, especially
/// in objects that are serialized. The LdapConnection
/// keeps no long lived instances of these objects.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
///
public virtual void Bind(System.String dn, System.String passwd)
{
Bind(Ldap_V3, dn, passwd, defSearchCons);
return ;
}
/// Synchronously authenticates to the Ldap server (that the object is
/// currently connected to) using the specified name, password,
/// and Ldap version.
///
/// If the object has been disconnected from an Ldap server,
/// this method attempts to reconnect to the server. If the object
/// has already authenticated, the old authentication is discarded.
///
///
/// The Ldap protocol version, use Ldap_V3.
/// Ldap_V2 is not supported.
///
///
/// If non-null and non-empty, specifies that the
/// connection and all operations through it should
/// be authenticated with dn as the distinguished
/// name.
///
///
/// 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.
///
/// Note: the application should use care in the use
/// of String password objects. These are long lived
/// objects, and may expose a security risk, especially
/// in objects that are serialized. The LdapConnection
/// keeps no long lived instances of these objects.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
///
public virtual void Bind(int version, System.String dn, System.String passwd)
{
Bind(version, dn, passwd, defSearchCons);
return ;
}
/// Synchronously authenticates to the Ldap server (that the object is
/// currently connected to) as an Ldapv3 bind, using the specified name,
/// password, and constraints.
///
/// If the object has been disconnected from an Ldap server,
/// this method attempts to reconnect to the server. If the object
/// has already authenticated, the old authentication is discarded.
///
///
/// If non-null and non-empty, specifies that the
/// connection and all operations through it should
/// be authenticated with dn as the distinguished
/// name.
///
///
/// 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.
/// Note: the application should use care in the use
/// of String password objects. These are long lived
/// objects, and may expose a security risk, especially
/// in objects that are serialized. The LdapConnection
/// keeps no long lived instances of these objects.
///
///
/// Constraints specific to the operation.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
///
public virtual void Bind(System.String dn, System.String passwd, LdapConstraints cons)
{
Bind(Ldap_V3, dn, passwd, cons);
return ;
}
/// Synchronously authenticates to the Ldap server (that the object is
/// currently connected to) using the specified name, password, Ldap version,
/// and constraints.
///
/// If the object has been disconnected from an Ldap server,
/// this method attempts to reconnect to the server. If the object
/// has already authenticated, the old authentication is discarded.
///
///
/// The Ldap protocol version, use Ldap_V3.
/// Ldap_V2 is not supported.
///
///
/// If non-null and non-empty, specifies that the
/// connection and all operations through it should
/// be authenticated with dn as the distinguished
/// name.
///
///
/// 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.
///
/// Note: the application should use care in the use
/// of String password objects. These are long lived
/// objects, and may expose a security risk, especially
/// in objects that are serialized. The LdapConnection
/// keeps no long lived instances of these objects.
///
///
/// The constraints specific to the operation.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
///
public virtual void Bind(int version, System.String dn, System.String passwd, LdapConstraints cons)
{
sbyte[] pw = null;
if ((System.Object) passwd != null)
{
try
{
System.Text.Encoding encoder = System.Text.Encoding.GetEncoding("utf-8");
byte[] ibytes = encoder.GetBytes(passwd);
pw=SupportClass.ToSByteArray(ibytes);
// pw = passwd.getBytes("UTF8");
passwd = null; // Keep no reference to String object
}
catch (System.IO.IOException ex)
{
passwd = null; // Keep no reference to String object
throw new System.SystemException(ex.ToString());
}
}
Bind(version, dn, pw, cons);
return ;
}
/// Synchronously authenticates to the Ldap server (that the object is
/// currently connected to) using the specified name, password,
/// and Ldap version.
///
/// If the object has been disconnected from an Ldap server,
/// this method attempts to reconnect to the server. If the object
/// has already authenticated, the old authentication is discarded.
///
///
/// The version of the Ldap protocol to use
/// in the bind, use Ldap_V3. Ldap_V2 is not supported.
///
///
/// If non-null and non-empty, specifies that the
/// connection and all operations through it should
/// be authenticated with dn as the distinguished
/// name.
///
///
/// 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.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
[CLSCompliantAttribute(false)]
public virtual void Bind(int version, System.String dn, sbyte[] passwd)
{
Bind(version, dn, passwd, defSearchCons);
return ;
}
///
/// Synchronously authenticates to the Ldap server (that the object is
/// currently connected to) using the specified name, password, Ldap version,
/// and constraints.
///
/// If the object has been disconnected from an Ldap server,
/// this method attempts to reconnect to the server. If the object
/// has already authenticated, the old authentication is discarded.
///
///
/// The Ldap protocol version, use Ldap_V3.
/// Ldap_V2 is not supported.
///
///
/// If non-null and non-empty, specifies that the
/// connection and all operations through it should
/// be authenticated with dn as the distinguished
/// name.
///
///
/// 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.
///
///
/// The constraints specific to the operation.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
[CLSCompliantAttribute(false)]
public virtual void Bind(int version, System.String dn, sbyte[] passwd, LdapConstraints cons)
{
LdapResponseQueue queue = Bind(version, dn, passwd, null, cons);
LdapResponse res = (LdapResponse) queue.getResponse();
if (res != null)
{
// Set local copy of responseControls synchronously if any
lock (responseCtlSemaphore)
{
responseCtls = res.Controls;
}
chkResultCode(queue, cons, res);
}
return ;
}
/// Asynchronously authenticates to the Ldap server (that the object is
/// currently connected to) using the specified name, password, Ldap
/// version, and queue.
///
/// If the object has been disconnected from an Ldap server,
/// this method attempts to reconnect to the server. If the object
/// has already authenticated, the old authentication is discarded.
///
///
///
/// The Ldap protocol version, use Ldap_V3.
/// Ldap_V2 is not supported.
///
///
/// If non-null and non-empty, specifies that the
/// connection and all operations through it should
/// be authenticated with dn as the distinguished
/// name.
///
///
/// 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.
///
///
/// Handler for messages returned from a server in
/// response to this request. If it is null, a
/// queue object is created internally.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
[CLSCompliantAttribute(false)]
public virtual LdapResponseQueue Bind(int version, System.String dn, sbyte[] passwd, LdapResponseQueue queue)
{
return Bind(version, dn, passwd, queue, defSearchCons);
}
/// Asynchronously authenticates to the Ldap server (that the object is
/// currently connected to) using the specified name, password, Ldap
/// version, queue, and constraints.
///
/// 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.
///
///
/// The Ldap protocol version, use Ldap_V3.
/// Ldap_V2 is not supported.
///
///
/// If non-null and non-empty, specifies that the
/// connection and all operations through it should
/// be authenticated with dn as the distinguished
/// name.
///
///
/// 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.
///
///
/// Handler for messages returned from a server in
/// response to this request. If it is null, a
/// queue object is created internally.
///
///
/// Constraints specific to the operation.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
[CLSCompliantAttribute(false)]
public virtual LdapResponseQueue Bind(int version, System.String dn, sbyte[] passwd, LdapResponseQueue queue, LdapConstraints cons)
{
int msgId;
BindProperties bindProps;
if (cons == null)
cons = defSearchCons;
if ((System.Object) dn == null)
{
dn = "";
}
else
{
dn = dn.Trim();
}
if (passwd == null)
passwd = new sbyte[]{};
bool anonymous = false;
if (passwd.Length == 0)
{
anonymous = true; // anonymous, passwd length zero with simple bind
dn = ""; // set to null if anonymous
}
LdapMessage msg = new LdapBindRequest(version, dn, passwd, cons.getControls());
msgId = msg.MessageID;
bindProps = new BindProperties(version, dn, "simple", anonymous, null, null);
// For bind requests, if not connected, attempt to reconnect
if (!conn.Connected)
{
if ((System.Object) conn.Host != null)
{
conn.connect(conn.Host, conn.Port);
}
else
{
throw new LdapException(ExceptionMessages.CONNECTION_IMPOSSIBLE, LdapException.CONNECT_ERROR, null);
}
}
// The semaphore is released when the bind response is queued.
conn.acquireWriteSemaphore(msgId);
return SendRequestToServer(msg, cons.TimeLimit, queue, bindProps);
}
//*************************************************************************
// compare methods
//*************************************************************************
///
/// Synchronously checks to see if an entry contains an attribute
/// with a specified value.
///
///
/// The distinguished name of the entry to use in the
/// comparison.
///
///
/// The attribute 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.
///
///
/// True if the entry has the value,
/// and false if the entry does not
/// have the value or the attribute.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual bool Compare(System.String dn, LdapAttribute attr)
{
return Compare(dn, attr, defSearchCons);
}
///
/// Synchronously checks to see if an entry contains an attribute with a
/// specified value, using the specified constraints.
///
///
/// The distinguished name of the entry to use in the
/// comparison.
///
///
/// The attribute 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.
///
///
/// Constraints specific to the operation.
///
///
/// True if the entry has the value,
/// and false if the entry does not
/// have the value or the attribute.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual bool Compare(System.String dn, LdapAttribute attr, LdapConstraints cons)
{
bool ret = false;
LdapResponseQueue queue = Compare(dn, attr, null, cons);
LdapResponse res = (LdapResponse) queue.getResponse();
// Set local copy of responseControls synchronously - if there were any
lock (responseCtlSemaphore)
{
responseCtls = res.Controls;
}
if (res.ResultCode == LdapException.COMPARE_TRUE)
{
ret = true;
}
else if (res.ResultCode == LdapException.COMPARE_FALSE)
{
ret = false;
}
else
{
chkResultCode(queue, cons, res);
}
return ret;
}
/// Asynchronously compares an attribute value with one in the directory,
/// using the specified queue.
///
/// Please note that a successful completion of this command results in
/// one of two status codes: LdapException.COMPARE_TRUE if the entry
/// has the value, and LdapException.COMPARE_FALSE if the entry
/// does not have the value or the attribute.
///
///
/// The distinguished name of the entry containing an
/// attribute to compare.
///
///
/// An attribute to compare.
///
///
/// The queue for messages returned from a server in
/// response to this request. If it is null, a
/// queue object is created internally.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
///
///
///
///
///
public virtual LdapResponseQueue Compare(System.String dn, LdapAttribute attr, LdapResponseQueue queue)
{
return Compare(dn, attr, queue, defSearchCons);
}
/// Asynchronously compares an attribute value with one in the directory,
/// using the specified queue and contraints.
///
/// Please note that a successful completion of this command results in
/// one of two status codes: LdapException.COMPARE_TRUE if the entry
/// has the value, and LdapException.COMPARE_FALSE if the entry
/// does not have the value or the attribute.
///
///
/// The distinguished name of the entry containing an
/// attribute to compare.
///
///
/// An attribute to compare.
///
///
/// Handler for messages returned from a server in
/// response to this request. If it is null, a
/// queue object is created internally.
///
///
/// Constraints specific to the operation.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
///
///
///
///
///
public virtual LdapResponseQueue Compare(System.String dn, LdapAttribute attr, LdapResponseQueue queue, LdapConstraints cons)
{
if (attr.size() != 1)
{
throw new System.ArgumentException("compare: Exactly one value " + "must be present in the LdapAttribute");
}
if ((System.Object) dn == null)
{
// Invalid parameter
throw new System.ArgumentException("compare: DN cannot be null");
}
if (cons == null)
cons = defSearchCons;
LdapMessage msg = new LdapCompareRequest(dn, attr.Name, attr.ByteValue, cons.getControls());
return SendRequestToServer(msg, cons.TimeLimit, queue, null);
}
//*************************************************************************
// connect methods
//*************************************************************************
///
/// 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.
/// At this point, there is no authentication, and any operations are
/// conducted as an anonymous client.
///
/// When more than one host name is specified, each host is contacted
/// in turn until a connection can be established.
///
///
/// A host name or a dotted string representing the IP address
/// of a host running an Ldap server. It may also
/// contain a list of host names, space-delimited. Each host
/// name can include a trailing colon and port number.
///
///
/// The TCP or UDP port number to connect to or contact.
/// The default Ldap port is 389. The port parameter is
/// ignored for any host hame which includes a colon and
/// port number.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
///
public virtual void Connect(System.String host, int port)
{
// connect doesn't affect other clones
// If not a clone, destroys old connection.
// Step through the space-delimited list
SupportClass.Tokenizer hostList = new SupportClass.Tokenizer(host, " ");
System.String address = null;
int specifiedPort;
int colonIndex; //after the colon is the port
while (hostList.HasMoreTokens())
{
try
{
specifiedPort = port;
address = hostList.NextToken();
colonIndex = address.IndexOf((System.Char) ':');
if (colonIndex != - 1 && colonIndex + 1 != address.Length)
{
//parse Port out of address
try
{
specifiedPort = System.Int32.Parse(address.Substring(colonIndex + 1));
address = address.Substring(0, (colonIndex) - (0));
}
catch (System.Exception e)
{
throw new System.ArgumentException(ExceptionMessages.INVALID_ADDRESS);
}
}
// This may return a different conn object
// Disassociate this clone with the underlying connection.
conn = conn.destroyClone(true);
conn.connect(address, specifiedPort);
break;
}
catch (LdapException LE)
{
if (!hostList.HasMoreTokens())
throw LE;
}
}
return ;
}
//*************************************************************************
// delete methods
//*************************************************************************
///
/// Synchronously deletes the entry with the specified distinguished name
/// from the directory.
///
/// Note: A Delete operation will not remove an entry that contains
/// subordinate entries, nor will it dereference alias entries.
///
///
/// The distinguished name of the entry to delete.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual void Delete(System.String dn)
{
Delete(dn, defSearchCons);
return ;
}
/// Synchronously deletes the entry with the specified distinguished name
/// from the directory, using the specified constraints.
///
/// Note: A Delete operation will not remove an entry that contains
/// subordinate entries, nor will it dereference alias entries.
///
///
/// The distinguished name of the entry to delete.
///
///
/// Constraints specific to the operation.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual void Delete(System.String dn, LdapConstraints cons)
{
LdapResponseQueue queue = Delete(dn, null, cons);
// Get a handle to the delete response
LdapResponse deleteResponse = (LdapResponse) (queue.getResponse());
// Set local copy of responseControls synchronously - if there were any
lock (responseCtlSemaphore)
{
responseCtls = deleteResponse.Controls;
}
chkResultCode(queue, cons, deleteResponse);
return ;
}
/// Asynchronously deletes the entry with the specified distinguished name
/// from the directory and returns the results to the specified queue.
///
/// Note: A Delete operation will not remove an entry that contains
/// subordinate entries, nor will it dereference alias entries.
///
///
/// The distinguished name of the entry to modify.
///
///
/// The queue for messages returned from a server in
/// response to this request. If it is null, a
/// queue object is created internally.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
///
public virtual LdapResponseQueue Delete(System.String dn, LdapResponseQueue queue)
{
return Delete(dn, queue, defSearchCons);
}
/// Asynchronously deletes the entry with the specified distinguished name
/// from the directory, using the specified contraints and queue.
///
/// Note: A Delete operation will not remove an entry that contains
/// subordinate entries, nor will it dereference alias entries.
///
///
/// The distinguished name of the entry to delete.
///
///
/// The queue for messages returned from a server in
/// response to this request. If it is null, a
/// queue object is created internally.
///
///
/// The constraints specific to the operation.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
///
public virtual LdapResponseQueue Delete(System.String dn, LdapResponseQueue queue, LdapConstraints cons)
{
if ((System.Object) dn == null)
{
// Invalid DN parameter
throw new System.ArgumentException(ExceptionMessages.DN_PARAM_ERROR);
}
if (cons == null)
cons = defSearchCons;
LdapMessage msg = new LdapDeleteRequest(dn, cons.getControls());
return SendRequestToServer(msg, cons.TimeLimit, queue, null);
}
//*************************************************************************
// disconnect method
//*************************************************************************
///
/// Synchronously disconnects from the Ldap server.
///
/// Before the object can perform Ldap operations again, it must
/// reconnect to the server by calling connect.
///
/// The disconnect method abandons any outstanding requests, issues an
/// unbind request to the server, and then closes the socket.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
///
public virtual void Disconnect()
{
// disconnect from API call
Disconnect(defSearchCons, true);
return ;
}
/// Synchronously disconnects from the Ldap server.
///
/// Before the object can perform Ldap operations again, it must
/// reconnect to the server by calling connect.
///
/// The disconnect method abandons any outstanding requests, issues an
/// unbind request to the server, and then closes the socket.
///
///
/// LDPConstraints to be set with the unbind request
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual void Disconnect(LdapConstraints cons)
{
// disconnect from API call
Disconnect(cons, true);
return ;
}
/// Synchronously disconnect from the server
///
///
/// true if application call disconnect API, false if finalize.
///
private void Disconnect(LdapConstraints cons, bool how)
{
// disconnect doesn't affect other clones
// If not a clone, distroys connection
conn = conn.destroyClone(how);
return ;
}
//*************************************************************************
// extendedOperation methods
//*************************************************************************
/// Provides a synchronous means to access extended, non-mandatory
/// operations offered by a particular Ldapv3 compliant server.
///
///
/// The object which contains (1) an identifier of an extended
/// operation which should be recognized by the particular Ldap
/// server this client is connected to and (2)
/// an operation-specific sequence of octet strings
/// or BER-encoded values.
///
///
/// An operation-specific object, containing an ID and either an octet
/// string or BER-encoded values.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual LdapExtendedResponse ExtendedOperation(LdapExtendedOperation op)
{
return ExtendedOperation(op, defSearchCons);
}
/*
* Synchronous Ldap extended request with SearchConstraints
*/
///
/// Provides a synchronous means to access extended, non-mandatory
/// operations offered by a particular Ldapv3 compliant server.
///
///
/// The object which contains (1) an identifier of an extended
/// operation which should be recognized by the particular Ldap
/// server this client is connected to and (2) an
/// operation-specific sequence of octet strings
/// or BER-encoded values.
///
///
/// The constraints specific to the operation.
///
///
/// An operation-specific object, containing an ID and either an
/// octet string or BER-encoded values.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual LdapExtendedResponse ExtendedOperation(LdapExtendedOperation op, LdapConstraints cons)
{
// Call asynchronous API and get back handler to reponse queue
LdapResponseQueue queue = ExtendedOperation(op, cons, null);
LdapExtendedResponse response = (LdapExtendedResponse) queue.getResponse();
// Set local copy of responseControls synchronously - if there were any
lock (responseCtlSemaphore)
{
responseCtls = response.Controls;
}
chkResultCode(queue, cons, response);
return response;
}
/*
* Asynchronous Ldap extended request
*/
/// Provides an asynchronous means to access extended, non-mandatory
/// operations offered by a particular Ldapv3 compliant server.
///
///
/// The object which contains (1) an identifier of an extended
/// operation which should be recognized by the particular Ldap
/// server this client is connected to and (2) an
/// operation-specific sequence of octet strings
/// or BER-encoded values.
///
///
/// The queue for messages returned from a server in
/// response to this request. If it is null, a queue
/// object is created internally.
///
///
/// An operation-specific object, containing an ID and either an octet
/// string or BER-encoded values.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual LdapResponseQueue ExtendedOperation(LdapExtendedOperation op, LdapResponseQueue queue)
{
return ExtendedOperation(op, defSearchCons, queue);
}
/*
* Asynchronous Ldap extended request with SearchConstraints
*/
/// Provides an asynchronous means to access extended, non-mandatory
/// operations offered by a particular Ldapv3 compliant server.
///
///
/// The object which contains (1) an identifier of an extended
/// operation which should be recognized by the particular Ldap
/// server this client is connected to and (2) an operation-
/// specific sequence of octet strings or BER-encoded values.
///
///
/// The queue for messages returned from a server in
/// response to this request. If it is null, a queue
/// object is created internally.
///
///
/// The constraints specific to this operation.
///
///
/// An operation-specific object, containing an ID and either an
/// octet string or BER-encoded values.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual LdapResponseQueue ExtendedOperation(LdapExtendedOperation op, LdapConstraints cons, LdapResponseQueue queue)
{
// Use default constraints if none-specified
if (cons == null)
cons = defSearchCons;
LdapMessage msg = MakeExtendedOperation(op, cons);
return SendRequestToServer(msg, cons.TimeLimit, queue, null);
}
/// Formulates the extended operation, constraints into an
/// LdapMessage and returns the LdapMessage. This is used by
/// extendedOperation and startTLS which needs the LdapMessage to
/// get the MessageID.
///
protected internal virtual LdapMessage MakeExtendedOperation(LdapExtendedOperation op, LdapConstraints cons)
{
// Use default constraints if none-specified
if (cons == null)
cons = defSearchCons;
// error check the parameters
if ((System.Object) op.getID() == null)
{
// Invalid extended operation parameter, no OID specified
throw new System.ArgumentException(ExceptionMessages.OP_PARAM_ERROR);
}
return new LdapExtendedRequest(op, cons.getControls());
}
//*************************************************************************
// getResponseControls method
//*************************************************************************
//*************************************************************************
// modify methods
//*************************************************************************
/// Synchronously makes a single change to an existing entry in the
/// directory.
///
/// For example, this modify method changes the value of an attribute,
/// adds a new attribute value, or removes an existing attribute value.
///
/// The LdapModification object specifies both the change to be made and
/// the LdapAttribute value to be changed.
///
/// If the request fails with {@link LdapException.CONNECT_ERROR},
/// it is indeterminate whether or not the server made the modification.
///
///
/// The distinguished name of the entry to modify.
///
///
/// A single change to be made to the entry.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual void Modify(System.String dn, LdapModification mod)
{
Modify(dn, mod, defSearchCons);
return ;
}
///
/// Synchronously makes a single change to an existing entry in the
/// directory, using the specified constraints.
///
/// For example, this modify method changes the value of an attribute,
/// adds a new attribute value, or removes an existing attribute value.
///
/// The LdapModification object specifies both the change to be
/// made and the LdapAttribute value to be changed.
///
/// If the request fails with {@link LdapException.CONNECT_ERROR},
/// it is indeterminate whether or not the server made the modification.
///
///
/// The distinguished name of the entry to modify.
///
///
/// A single change to be made to the entry.
///
///
/// The constraints specific to the operation.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual void Modify(System.String dn, LdapModification mod, LdapConstraints cons)
{
LdapModification[] mods = new LdapModification[1];
mods[0] = mod;
Modify(dn, mods, cons);
return ;
}
///
/// Synchronously makes a set of changes to an existing entry in the
/// directory.
///
/// For example, this modify method changes attribute values, adds
/// new attribute values, or removes existing attribute values.
///
/// Because the server applies all changes in an LdapModification array
/// atomically, the application can expect that no changes
/// have been performed if an error is returned.
/// If the request fails with {@link LdapException.CONNECT_ERROR},
/// it is indeterminate whether or not the server made the modifications.
///
///
/// Distinguished name of the entry to modify.
///
///
/// The changes to be made to the entry.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual void Modify(System.String dn, LdapModification[] mods)
{
Modify(dn, mods, defSearchCons);
return ;
}
/// Synchronously makes a set of changes to an existing entry in the
/// directory, using the specified constraints.
///
/// For example, this modify method changes attribute values, adds new
/// attribute values, or removes existing attribute values.
///
/// Because the server applies all changes in an LdapModification array
/// atomically, the application can expect that no changes
/// have been performed if an error is returned.
/// If the request fails with {@link LdapException.CONNECT_ERROR},
/// it is indeterminate whether or not the server made the modifications.
///
///
/// The distinguished name of the entry to modify.
///
///
/// The changes to be made to the entry.
///
///
/// The constraints specific to the operation.
///
///
/// LdapException A general exception which includes an
/// error message and an Ldap error code.
///
public virtual void Modify(System.String dn, LdapModification[] mods, LdapConstraints cons)
{
LdapResponseQueue queue = Modify(dn, mods, null, cons);
// Get a handle to the modify response
LdapResponse modifyResponse = (LdapResponse) (queue.getResponse());
// Set local copy of responseControls synchronously - if there were any
lock (responseCtlSemaphore)
{
responseCtls = modifyResponse.Controls;
}
chkResultCode(queue, cons, modifyResponse);
return ;
}
/// Asynchronously makes a single change to an existing entry in the
/// directory.
///
/// For example, this modify method can change the value of an attribute,
/// add a new attribute value, or remove an existing attribute value.
///
/// The LdapModification object specifies both the change to be made and
/// the LdapAttribute value to be changed.
///
/// If the request fails with {@link LdapException.CONNECT_ERROR},
/// it is indeterminate whether or not the server made the modification.
///
///
/// Distinguished name of the entry to modify.
///
///
/// A single change to be made to the entry.
///
///
/// Handler for messages returned from a server in
/// response to this request. If it is null, a
/// queue object is created internally.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual LdapResponseQueue Modify(System.String dn, LdapModification mod, LdapResponseQueue queue)
{
return Modify(dn, mod, queue, defSearchCons);
}
/// Asynchronously makes a single change to an existing entry in the
/// directory, using the specified constraints and queue.
///
/// For example, this modify method can change the value of an attribute,
/// add a new attribute value, or remove an existing attribute value.
///
/// The LdapModification object specifies both the change to be made
/// and the LdapAttribute value to be changed.
///
/// If the request fails with {@link LdapException.CONNECT_ERROR},
/// it is indeterminate whether or not the server made the modification.
///
///
/// Distinguished name of the entry to modify.
///
///
/// A single change to be made to the entry.
///
///
/// Handler for messages returned from a server in
/// response to this request. If it is null, a
/// queue object is created internally.
///
///
/// Constraints specific to the operation.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual LdapResponseQueue Modify(System.String dn, LdapModification mod, LdapResponseQueue queue, LdapConstraints cons)
{
LdapModification[] mods = new LdapModification[1];
mods[0] = mod;
return Modify(dn, mods, queue, cons);
}
/// Asynchronously makes a set of changes to an existing entry in the
/// directory.
///
/// For example, this modify method can change attribute values, add new
/// attribute values, or remove existing attribute values.
///
/// Because the server applies all changes in an LdapModification array
/// atomically, the application can expect that no changes
/// have been performed if an error is returned.
/// If the request fails with {@link LdapException.CONNECT_ERROR},
/// it is indeterminate whether or not the server made the modifications.
///
///
/// The distinguished name of the entry to modify.
///
///
/// The changes to be made to the entry.
///
///
/// The queue for messages returned from a server in
/// response to this request. If it is null, a
/// queue object is created internally.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual LdapResponseQueue Modify(System.String dn, LdapModification[] mods, LdapResponseQueue queue)
{
return Modify(dn, mods, queue, defSearchCons);
}
/// Asynchronously makes a set of changes to an existing entry in the
/// directory, using the specified constraints and queue.
///
/// For example, this modify method can change attribute values, add new
/// attribute values, or remove existing attribute values.
///
/// Because the server applies all changes in an LdapModification array
/// atomically, the application can expect that no changes
/// have been performed if an error is returned.
/// If the request fails with {@link LdapException.CONNECT_ERROR},
/// it is indeterminate whether or not the server made the modifications.
///
///
/// The distinguished name of the entry to modify.
///
///
/// The changes to be made to the entry.
///
///
/// The queue for messages returned from a server in
/// response to this request. If it is null, a
/// queue object is created internally.
///
///
/// Constraints specific to the operation.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual LdapResponseQueue Modify(System.String dn, LdapModification[] mods, LdapResponseQueue queue, LdapConstraints cons)
{
if ((System.Object) dn == null)
{
// Invalid DN parameter
throw new System.ArgumentException(ExceptionMessages.DN_PARAM_ERROR);
}
if (cons == null)
cons = defSearchCons;
LdapMessage msg = new LdapModifyRequest(dn, mods, cons.getControls());
return SendRequestToServer(msg, cons.TimeLimit, queue, null);
}
//*************************************************************************
// read methods
//*************************************************************************
/// Synchronously reads the entry for the specified distiguished name (DN)
/// and retrieves all attributes for the entry.
///
///
/// The distinguished name of the entry to retrieve.
///
///
/// the LdapEntry read from the server.
///
///
/// LdapException if the object was not found
///
public virtual LdapEntry Read(System.String dn)
{
return Read(dn, defSearchCons);
}
///
/// Synchronously reads the entry for the specified distiguished name (DN),
/// using the specified constraints, and retrieves all attributes for the
/// entry.
///
///
/// The distinguished name of the entry to retrieve.
///
///
/// The constraints specific to the operation.
///
///
/// the LdapEntry read from the server
///
///
/// LdapException if the object was not found
///
public virtual LdapEntry Read(System.String dn, LdapSearchConstraints cons)
{
return Read(dn, null, cons);
}
///
/// Synchronously reads the entry for the specified distinguished name (DN)
/// and retrieves only the specified attributes from the entry.
///
///
/// The distinguished name of the entry to retrieve.
///
///
/// The names of the attributes to retrieve.
///
///
/// the LdapEntry read from the server
///
///
/// LdapException if the object was not found
///
public virtual LdapEntry Read(System.String dn, System.String[] attrs)
{
return Read(dn, attrs, defSearchCons);
}
/// Synchronously reads the entry for the specified distinguished name (DN),
/// using the specified constraints, and retrieves only the specified
/// attributes from the entry.
///
///
/// The distinguished name of the entry to retrieve.
///
///
/// The names of the attributes to retrieve.
///
///
/// The constraints specific to the operation.
///
///
/// the LdapEntry read from the server
///
///
/// LdapException if the object was not found
///
public virtual LdapEntry Read(System.String dn, System.String[] attrs, LdapSearchConstraints cons)
{
LdapSearchResults sr = Search(dn, SCOPE_BASE, null, attrs, false, cons);
LdapEntry ret = null;
if (sr.hasMore())
{
ret = sr.next();
if (sr.hasMore())
{
// "Read response is ambiguous, multiple entries returned"
throw new LdapLocalException(ExceptionMessages.READ_MULTIPLE, LdapException.AMBIGUOUS_RESPONSE);
}
}
return ret;
}
/// Synchronously reads the entry specified by the Ldap URL.
///
/// When this read method is called, a new connection is created
/// automatically, using the host and port specified in the URL. After
/// finding the entry, the method closes the connection (in other words,
/// it disconnects from the Ldap server).
///
/// If the URL specifies a filter and scope, they 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.
///
///
/// Ldap URL specifying the entry to read.
///
///
/// The entry specified by the base DN.
///
///
/// LdapException if the object was not found
///
public static LdapEntry Read(LdapUrl toGet)
{
LdapConnection lconn = new LdapConnection();
lconn.Connect(toGet.Host, toGet.Port);
LdapEntry toReturn = lconn.Read(toGet.getDN(), toGet.AttributeArray);
lconn.Disconnect();
return toReturn;
}
/// Synchronously reads the entry specified by the Ldap URL, using the
/// specified constraints.
///
/// When this method is called, a new connection is created
/// automatically, using the host and port specified in the URL. After
/// finding the entry, the method closes the connection (in other words,
/// it disconnects from the Ldap server).
///
/// If the URL specifies a filter and scope, they 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 entry specified by the base DN.
///
///
/// Ldap URL specifying the entry to read.
///
///
/// Constraints specific to the operation.
///
///
/// LdapException if the object was not found
///
public static LdapEntry Read(LdapUrl toGet, LdapSearchConstraints cons)
{
LdapConnection lconn = new LdapConnection();
lconn.Connect(toGet.Host, toGet.Port);
LdapEntry toReturn = lconn.Read(toGet.getDN(), toGet.AttributeArray, cons);
lconn.Disconnect();
return toReturn;
}
//*************************************************************************
// rename methods
//*************************************************************************
///
/// Synchronously renames an existing entry in the directory.
///
///
/// The current distinguished name of the entry.
///
///
/// The new relative distinguished name for the entry.
///
///
/// If true, the old name is not retained as an
/// attribute value. If false, the old name is
/// retained as an attribute value.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual void Rename(System.String dn, System.String newRdn, bool deleteOldRdn)
{
Rename(dn, newRdn, deleteOldRdn, defSearchCons);
return ;
}
///
/// Synchronously renames an existing entry in the directory, using the
/// specified constraints.
///
///
/// The current distinguished name of the entry.
///
///
/// The new relative distinguished name for the entry.
///
///
/// If true, the old name is not retained as an
/// attribute value. If false, the old name is
/// retained as an attribute value.
///
///
/// The constraints specific to the operation.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual void Rename(System.String dn, System.String newRdn, bool deleteOldRdn, LdapConstraints cons)
{
// null for newParentdn means that this is originating as an Ldapv2 call
Rename(dn, newRdn, null, deleteOldRdn, cons);
return ;
}
/// Synchronously renames an existing entry in the directory, possibly
/// repositioning the entry in the directory tree.
///
///
/// The current distinguished name of the entry.
///
///
/// The new relative distinguished name for the entry.
///
///
/// The distinguished name of an existing entry which
/// is to be the new parent of the entry.
///
///
/// If true, the old name is not retained as an
/// attribute value. If false, the old name is
/// retained as an attribute value.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual void Rename(System.String dn, System.String newRdn, System.String newParentdn, bool deleteOldRdn)
{
Rename(dn, newRdn, newParentdn, deleteOldRdn, defSearchCons);
return ;
}
///
/// Synchronously renames an existing entry in the directory, using the
/// specified constraints and possibly repositioning the entry in the
/// directory tree.
///
///
/// The current distinguished name of the entry.
///
///
/// The new relative distinguished name for the entry.
///
///
/// The distinguished name of an existing entry which
/// is to be the new parent of the entry.
///
///
/// If true, the old name is not retained as an
/// attribute value. If false, the old name is
/// retained as an attribute value.
///
///
/// The constraints specific to the operation.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual void Rename(System.String dn, System.String newRdn, System.String newParentdn, bool deleteOldRdn, LdapConstraints cons)
{
LdapResponseQueue queue = Rename(dn, newRdn, newParentdn, deleteOldRdn, null, cons);
// Get a handle to the rename response
LdapResponse renameResponse = (LdapResponse) (queue.getResponse());
// Set local copy of responseControls synchronously - if there were any
lock (responseCtlSemaphore)
{
responseCtls = renameResponse.Controls;
}
chkResultCode(queue, cons, renameResponse);
return ;
}
/*
* rename
*/
/// Asynchronously renames an existing entry in the directory.
///
///
/// The current distinguished name of the entry.
///
///
/// The new relative distinguished name for the entry.
///
///
/// If true, the old name is not retained as an
/// attribute value. If false, the old name is
/// retained as an attribute value.
///
///
/// The queue for messages returned from a server in
/// response to this request. If it is null, a
/// queue object is created internally.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual LdapResponseQueue Rename(System.String dn, System.String newRdn, bool deleteOldRdn, LdapResponseQueue queue)
{
return Rename(dn, newRdn, deleteOldRdn, queue, defSearchCons);
}
/// Asynchronously renames an existing entry in the directory, using the
/// specified constraints.
///
///
/// The current distinguished name of the entry.
///
///
/// The new relative distinguished name for the entry.
///
///
/// If true, the old name is not retained as an
/// attribute value. If false, the old name is
/// retained as an attribute value.
///
///
/// The queue for messages returned from a server in
/// response to this request. If it is null, a
/// queue object is created internally.
///
///
/// The constraints specific to the operation.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual LdapResponseQueue Rename(System.String dn, System.String newRdn, bool deleteOldRdn, LdapResponseQueue queue, LdapConstraints cons)
{
return Rename(dn, newRdn, null, deleteOldRdn, queue, cons);
}
/// Asynchronously renames an existing entry in the directory, possibly
/// repositioning the entry in the directory.
///
///
/// The current distinguished name of the entry.
///
///
/// The new relative distinguished name for the entry.
///
///
/// The distinguished name of an existing entry which
/// is to be the new parent of the entry.
///
///
/// If true, the old name is not retained as an
/// attribute value. If false, the old name is
/// retained as an attribute value.
///
///
/// The queue for messages returned from a server in
/// response to this request. If it is null, a
/// queue object is created internally.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual LdapResponseQueue Rename(System.String dn, System.String newRdn, System.String newParentdn, bool deleteOldRdn, LdapResponseQueue queue)
{
return Rename(dn, newRdn, newParentdn, deleteOldRdn, queue, defSearchCons);
}
/// Asynchronously renames an existing entry in the directory, using the
/// specified constraints and possibily repositioning the entry in the
/// directory.
///
///
/// The current distinguished name of the entry.
///
///
/// The new relative distinguished name for the entry.
///
///
/// The distinguished name of an existing entry which
/// is to be the new parent of the entry.
///
///
/// If true, the old name is not retained as an
/// attribute value. If false, the old name is
/// retained as an attribute value.
///
///
/// The queue for messages returned from a server in
/// response to this request. If it is null, a
/// queue object is created internally.
///
///
/// The constraints specific to the operation.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual LdapResponseQueue Rename(System.String dn, System.String newRdn, System.String newParentdn, bool deleteOldRdn, LdapResponseQueue queue, LdapConstraints cons)
{
if ((System.Object) dn == null || (System.Object) newRdn == null)
{
// Invalid DN or RDN parameter
throw new System.ArgumentException(ExceptionMessages.RDN_PARAM_ERROR);
}
if (cons == null)
cons = defSearchCons;
LdapMessage msg = new LdapModifyDNRequest(dn, newRdn, newParentdn, deleteOldRdn, cons.getControls());
return SendRequestToServer(msg, cons.TimeLimit, queue, null);
}
//*************************************************************************
// search methods
//*************************************************************************
///
/// Synchronously performs the search specified by the parameters.
///
///
/// The base distinguished name to search from.
///
///
/// The scope of the entries to search. The following
/// are the valid options:
///
/// - SCOPE_BASE - searches only the base DN
///
/// - SCOPE_ONE - searches only entries under the base DN
///
/// - SCOPE_SUB - searches the base DN and all entries
/// within its subtree
///
///
/// Search filter specifying the search criteria.
///
///
/// Names of attributes to retrieve.
///
///
/// If true, returns the names but not the values of
/// the attributes found. If false, returns the
/// names and values for attributes found.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual LdapSearchResults Search(System.String base_Renamed, int scope, System.String filter, System.String[] attrs, bool typesOnly)
{
return Search(base_Renamed, scope, filter, attrs, typesOnly, defSearchCons);
}
///
/// Synchronously performs the search specified by the parameters,
/// using the specified search constraints (such as the
/// maximum number of entries to find or the maximum time to wait for
/// search results).
///
/// As part of the search constraints, the method allows specifying
/// whether or not the results are to be delivered all at once or in
/// smaller batches. If specified that the results are to be delivered in
/// smaller batches, each iteration blocks only until the next batch of
/// results is returned.
///
///
/// The base distinguished name to search from.
///
///
/// The scope of the entries to search. The following
/// are the valid options:
///
/// - SCOPE_BASE - searches only the base DN
///
/// - SCOPE_ONE - searches only entries under the base DN
///
/// - SCOPE_SUB - searches the base DN and all entries
/// within its subtree
///
///
/// The search filter specifying the search criteria.
///
///
/// The names of attributes to retrieve.
///
///
/// If true, returns the names but not the values of
/// the attributes found. If false, returns the
/// names and values for attributes found.
///
///
/// The constraints specific to the search.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual LdapSearchResults Search(System.String base_Renamed, int scope, System.String filter, System.String[] attrs, bool typesOnly, LdapSearchConstraints cons)
{
LdapSearchQueue queue = Search(base_Renamed, scope, filter, attrs, typesOnly, null, cons);
if (cons == null)
cons = defSearchCons;
return new LdapSearchResults(this, queue, cons);
}
/// Asynchronously performs the search specified by the parameters.
///
///
/// The base distinguished name to search from.
///
///
/// The scope of the entries to search. The following
/// are the valid options:
///
/// - SCOPE_BASE - searches only the base DN
///
/// - SCOPE_ONE - searches only entries under the base DN
///
/// - SCOPE_SUB - searches the base DN and all entries
/// within its subtree
///
///
/// Search filter specifying the search criteria.
///
///
/// Names of attributes to retrieve.
///
///
/// If true, returns the names but not the values of
/// the attributes found. If false, returns the
/// names and values for attributes found.
///
///
/// Handler for messages returned from a server in
/// response to this request. If it is null, a
/// queue object is created internally.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual LdapSearchQueue Search(System.String base_Renamed, int scope, System.String filter, System.String[] attrs, bool typesOnly, LdapSearchQueue queue)
{
return Search(base_Renamed, scope, filter, attrs, typesOnly, queue, defSearchCons);
}
/// Asynchronously performs the search specified by the parameters,
/// also allowing specification of constraints for the search (such
/// as the maximum number of entries to find or the maximum time to
/// wait for search results).
///
///
/// The base distinguished name to search from.
///
///
/// The scope of the entries to search. The following
/// are the valid options:
///
/// - SCOPE_BASE - searches only the base DN
///
/// - SCOPE_ONE - searches only entries under the base DN
///
/// - SCOPE_SUB - searches the base DN and all entries
/// within its subtree
///
///
/// The search filter specifying the search criteria.
///
///
/// The names of attributes to retrieve.
///
///
/// If true, returns the names but not the values of
/// the attributes found. If false, returns the
/// names and values for attributes found.
///
///
/// The queue for messages returned from a server in
/// response to this request. If it is null, a
/// queue object is created internally.
///
///
/// The constraints specific to the search.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public virtual LdapSearchQueue Search(System.String base_Renamed, int scope, System.String filter, System.String[] attrs, bool typesOnly, LdapSearchQueue queue, LdapSearchConstraints cons)
{
if ((System.Object) filter == null)
{
filter = "objectclass=*";
}
if (cons == null)
cons = defSearchCons;
LdapMessage msg = new LdapSearchRequest(base_Renamed, scope, filter, attrs, cons.Dereference, cons.MaxResults, cons.ServerTimeLimit, typesOnly, cons.getControls());
MessageAgent agent;
LdapSearchQueue myqueue = queue;
if (myqueue == null)
{
agent = new MessageAgent();
myqueue = new LdapSearchQueue(agent);
}
else
{
agent = queue.MessageAgent;
}
try
{
agent.sendMessage(conn, msg, cons.TimeLimit, myqueue, null);
}
catch (LdapException lex)
{
throw lex;
}
return myqueue;
}
/*
* Ldap URL search
*/
/// Synchronously performs the search specified by the Ldap URL, returning
/// an enumerable LdapSearchResults object.
///
///
/// The Ldap URL specifying the entry to read.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public static LdapSearchResults Search(LdapUrl toGet)
{
// Get a clone of default search constraints, method alters batchSize
return Search(toGet, null);
}
/*
* Ldap URL search
*/
/// Synchronously perfoms the search specified by the Ldap URL, using
/// the specified search constraints (such as the maximum number of
/// entries to find or the maximum time to wait for search results).
///
/// When this method is called, a new connection is created
/// automatically, using the host and port specified in the URL. After
/// all search results have been received from the server, the method
/// closes the connection (in other words, it disconnects from the Ldap
/// server).
///
/// As part of the search constraints, a choice can be made as to whether
/// to have the results delivered all at once or in smaller batches. If
/// the results are to be delivered in smaller batches, each iteration
/// blocks only until the next batch of results is returned.
///
///
///
/// Ldap URL specifying the entry to read.
///
///
/// The constraints specific to the search.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
public static LdapSearchResults Search(LdapUrl toGet, LdapSearchConstraints cons)
{
LdapConnection lconn = new LdapConnection();
lconn.Connect(toGet.Host, toGet.Port);
if (cons == null)
{
// This is a clone, so we already have our own copy
cons = lconn.SearchConstraints;
}
else
{
// get our own copy of user's constraints because we modify it
cons = (LdapSearchConstraints) cons.Clone();
}
cons.BatchSize = 0; // Must wait until all results arrive
LdapSearchResults toReturn = lconn.Search(toGet.getDN(), toGet.Scope, toGet.Filter, toGet.AttributeArray, false, cons);
lconn.Disconnect();
return toReturn;
}
/// Sends an Ldap request to a directory server.
///
/// The specified the Ldap request is sent to the directory server
/// associated with this connection using default constraints. An Ldap
/// request object is a subclass {@link LdapMessage} with the operation
/// type set to one of the request types. You can build a request by using
/// the request classes found in this package
///
/// You should note that, since Ldap requests sent to the server
/// using sendRequest are asynchronous, automatic referral following
/// does not apply to these requests.
///
///
/// The Ldap request to send to the directory server.
///
/// The queue for messages returned from a server in
/// response to this request. If it is null, a
/// queue object is created internally.
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
///
///
///
///
///
public virtual LdapMessageQueue SendRequest(LdapMessage request, LdapMessageQueue queue)
{
return SendRequest(request, queue, null);
}
/// Sends an Ldap request to a directory server.
///
/// The specified the Ldap request is sent to the directory server
/// associated with this connection. An Ldap request object is an
/// {@link LdapMessage} with the operation type set to one of the request
/// types. You can build a request by using the request classes found in this
/// package
///
/// You should note that, since Ldap requests sent to the server
/// using sendRequest are asynchronous, automatic referral following
/// does not apply to these requests.
///
///
/// The Ldap request to send to the directory server.
///
/// The queue for messages returned from a server in
/// response to this request. If it is null, a
/// queue object is created internally.
///
/// The constraints that apply to this request
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
///
///
///
///
///
public virtual LdapMessageQueue SendRequest(LdapMessage request, LdapMessageQueue queue, LdapConstraints cons)
{
if (!request.Request)
{
throw new System.SystemException("Object is not a request message");
}
if (cons == null)
{
cons = defSearchCons;
}
// Get the correct queue for a search request
MessageAgent agent;
LdapMessageQueue myqueue = queue;
if (myqueue == null)
{
agent = new MessageAgent();
if (request.Type == LdapMessage.SEARCH_REQUEST)
{
myqueue = new LdapSearchQueue(agent);
}
else
{
myqueue = new LdapResponseQueue(agent);
}
}
else
{
if (request.Type == LdapMessage.SEARCH_REQUEST)
{
agent = queue.MessageAgent;
}
else
{
agent = queue.MessageAgent;
}
}
try
{
agent.sendMessage(conn, request, cons.TimeLimit, myqueue, null);
}
catch (LdapException lex)
{
throw lex;
}
return myqueue;
}
//*************************************************************************
// helper methods
//*************************************************************************
/// Locates the appropriate message agent and sends
/// the Ldap request to a directory server.
///
///
/// the message to send
///
///
/// the timeout value
///
///
/// the response queue or null
///
///
/// the LdapResponseQueue for this request
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
private LdapResponseQueue SendRequestToServer(LdapMessage msg, int timeout, LdapResponseQueue queue, BindProperties bindProps)
{
MessageAgent agent;
if (queue == null)
{
agent = new MessageAgent();
queue = new LdapResponseQueue(agent);
}
else
{
agent = queue.MessageAgent;
}
agent.sendMessage(conn, msg, timeout, queue, bindProps);
return queue;
}
/// get an LdapConnection object so that we can follow a referral.
/// This function is never called if cons.getReferralFollowing() returns
/// false.
///
///
/// the array of referral strings
///
///
///
/// The referralInfo object
///
///
/// LdapReferralException A general exception which includes
/// an error message and an Ldap error code.
///
private ReferralInfo getReferralConnection(System.String[] referrals)
{
ReferralInfo refInfo = null;
System.Exception ex = null;
LdapConnection rconn = null;
LdapReferralHandler rh = defSearchCons.getReferralHandler();
int i = 0;
// Check if we use LdapRebind to get authentication credentials
if ((rh == null) || (rh is LdapAuthHandler))
{
for (i = 0; i < referrals.Length; i++)
{
// dn, pw are null in the default case (anonymous bind)
System.String dn = null;
sbyte[] pw = null;
try
{
rconn = new LdapConnection();
rconn.Constraints = defSearchCons;
LdapUrl url = new LdapUrl(referrals[i]);
rconn.Connect(url.Host, url.Port);
if (rh != null)
{
if (rh is LdapAuthHandler)
{
// Get application supplied dn and pw
LdapAuthProvider ap = ((LdapAuthHandler) rh).getAuthProvider(url.Host, url.Port);
dn = ap.DN;
pw = ap.Password;
}
}
rconn.Bind(Ldap_V3, dn, pw);
ex = null;
refInfo = new ReferralInfo(rconn, referrals, url);
// Indicate this connection created to follow referral
rconn.Connection.ActiveReferral = refInfo;
break;
}
catch (System.Exception lex)
{
if (rconn != null)
{
try
{
rconn.Disconnect();
rconn = null;
ex = lex;
}
catch (LdapException e)
{
; // ignore
}
}
}
}
}
// Check if application gets connection and does bind
else
{
// rh instanceof LdapBind
try
{
rconn = ((LdapBindHandler) rh).Bind(referrals, this);
if (rconn == null)
{
LdapReferralException rex = new LdapReferralException(ExceptionMessages.REFERRAL_ERROR);
rex.setReferrals(referrals);
throw rex;
}
// Figure out which Url belongs to the connection
for (int idx = 0; idx < referrals.Length; idx++)
{
try
{
LdapUrl url = new LdapUrl(referrals[idx]);
if (url.Host.ToUpper().Equals(rconn.Host.ToUpper()) && (url.Port == rconn.Port))
{
refInfo = new ReferralInfo(rconn, referrals, url);
break;
}
}
catch (System.Exception e)
{
; // ignore
}
}
if (refInfo == null)
{
// Could not match LdapBind.bind() connecction with URL list
ex = new LdapLocalException(ExceptionMessages.REFERRAL_BIND_MATCH, LdapException.CONNECT_ERROR);
}
}
catch (System.Exception lex)
{
rconn = null;
ex = lex;
}
}
if (ex != null)
{
// Could not connect to any server, throw an exception
LdapException ldapex;
if (ex is LdapReferralException)
{
throw (LdapReferralException) ex;
}
else if (ex is LdapException)
{
ldapex = (LdapException) ex;
}
else
{
ldapex = new LdapLocalException(ExceptionMessages.SERVER_CONNECT_ERROR, new System.Object[]{conn.Host}, LdapException.CONNECT_ERROR, ex);
}
// Error attempting to follow a referral
LdapReferralException rex = new LdapReferralException(ExceptionMessages.REFERRAL_ERROR, ldapex);
rex.setReferrals(referrals);
// Use last URL string for the failed referral
rex.FailedReferral = referrals[referrals.Length - 1];
throw rex;
}
// We now have an authenticated connection
// to be used to follow the referral.
return refInfo;
}
/// Check the result code and throw an exception if needed.
///
/// If referral following is enabled, checks if we need to
/// follow a referral
///
///
/// - the message queue of the current response
///
///
/// - the constraints that apply to the request
///
///
/// - the LdapResponse to check
///
private void chkResultCode(LdapMessageQueue queue, LdapConstraints cons, LdapResponse response)
{
if ((response.ResultCode == LdapException.REFERRAL) && cons.ReferralFollowing)
{
// Perform referral following and return
System.Collections.ArrayList refConn = null;
try
{
chaseReferral(queue, cons, response, response.Referrals, 0, false, null);
}
finally
{
releaseReferralConnections(refConn);
}
}
else
{
// Throws exception for non success result
response.chkResultCode();
}
return ;
}
/// Follow referrals if necessary referral following enabled.
/// This function is called only by synchronous requests.
/// Search responses come here only if referral following is
/// enabled and if we are processing a SearchResultReference
/// or a Response with a status of REFERRAL, i.e. we are
/// going to follow a referral.
///
/// This functions recursively follows a referral until a result
/// is returned or until the hop limit is reached.
///
///
/// The LdapResponseQueue for this request
///
///
/// The constraints that apply to the request
///
///
/// The referral or search reference response message
///
///
/// The referral array returned from the
/// initial request.
///
///
/// the number of hops already used while
/// following this referral
///
///
/// true if the message is a search reference
///
///
/// An optional array list used to store
/// the LdapConnection objects used in following the referral.
///
///
/// The array list used to store the all LdapConnection objects
/// used in following the referral. The list will be empty
/// if there were none.
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
/* package */
internal virtual System.Collections.ArrayList chaseReferral(LdapMessageQueue queue, LdapConstraints cons, LdapMessage msg, System.String[] initialReferrals, int hopCount, bool searchReference, System.Collections.ArrayList connectionList)
{
System.Collections.ArrayList connList = connectionList;
LdapConnection rconn = null; // new conn for following referral
ReferralInfo rinfo = null; // referral info
LdapMessage origMsg;
// Get a place to store new connections
if (connList == null)
{
connList = new System.Collections.ArrayList(cons.HopLimit);
}
// Following referrals or search reference
System.String[] refs; // referral list
if (initialReferrals != null)
{
// Search continuation reference from a search request
refs = initialReferrals;
origMsg = msg.RequestingMessage;
}
else
{
// Not a search request
LdapResponse resp = (LdapResponse) queue.getResponse();
if (resp.ResultCode != LdapException.REFERRAL)
{
// Not referral result,throw Exception if nonzero result
resp.chkResultCode();
return connList;
}
// We have a referral response
refs = resp.Referrals;
origMsg = resp.RequestingMessage;
}
LdapUrl refUrl; // referral represented as URL
try
{
// increment hop count, check max hops
if (hopCount++ > cons.HopLimit)
{
throw new LdapLocalException("Max hops exceeded", LdapException.REFERRAL_LIMIT_EXCEEDED);
}
// Get a connection to follow the referral
rinfo = getReferralConnection(refs);
rconn = rinfo.ReferralConnection;
refUrl = rinfo.ReferralUrl;
connList.Add(rconn);
// rebuild msg into new msg changing msgID,dn,scope,filter
LdapMessage newMsg = rebuildRequest(origMsg, refUrl, searchReference);
// Send new message on new connection
try
{
MessageAgent agent;
if (queue is LdapResponseQueue)
{
agent = queue.MessageAgent;
}
else
{
agent = queue.MessageAgent;
}
agent.sendMessage(rconn.Connection, newMsg, defSearchCons.TimeLimit, queue, null);
}
catch (InterThreadException ex)
{
// Error ending request to referred server
LdapReferralException rex = new LdapReferralException(ExceptionMessages.REFERRAL_SEND, LdapException.CONNECT_ERROR, null, ex);
rex.setReferrals(initialReferrals);
ReferralInfo ref_Renamed = rconn.Connection.ActiveReferral;
rex.FailedReferral = ref_Renamed.ReferralUrl.ToString();
throw rex;
}
if (initialReferrals == null)
{
// For operation results, when all responses are complete,
// the stack unwinds back to the original and returns
// to the application.
// An exception is thrown for an error
connList = chaseReferral(queue, cons, null, null, hopCount, false, connList);
}
else
{
// For search, just return to LdapSearchResults object
return connList;
}
}
catch (System.Exception ex)
{
if (ex is LdapReferralException)
{
throw (LdapReferralException) ex;
}
else
{
// Set referral list and failed referral
LdapReferralException rex = new LdapReferralException(ExceptionMessages.REFERRAL_ERROR, ex);
rex.setReferrals(refs);
if (rinfo != null)
{
rex.FailedReferral = rinfo.ReferralUrl.ToString();
}
else
{
rex.FailedReferral = refs[refs.Length - 1];
}
throw rex;
}
}
return connList;
}
/// Builds a new request replacing dn, scope, and filter where approprate
///
///
/// the original LdapMessage to build the new request from
///
///
/// the referral url
///
///
/// a new LdapMessage with appropriate information replaced
///
///
/// LdapException A general exception which includes an error
/// message and an Ldap error code.
///
private LdapMessage rebuildRequest(LdapMessage msg, LdapUrl url, bool reference)
{
System.String dn = url.getDN(); // new base
System.String filter = null;
switch (msg.Type)
{
case LdapMessage.SEARCH_REQUEST:
if (reference)
{
filter = url.Filter;
}
break;
// We are allowed to get a referral for the following
case LdapMessage.ADD_REQUEST:
case LdapMessage.BIND_REQUEST:
case LdapMessage.COMPARE_REQUEST:
case LdapMessage.DEL_REQUEST:
case LdapMessage.EXTENDED_REQUEST:
case LdapMessage.MODIFY_RDN_REQUEST:
case LdapMessage.MODIFY_REQUEST:
break;
// The following return no response
case LdapMessage.ABANDON_REQUEST:
case LdapMessage.UNBIND_REQUEST:
default:
throw new LdapLocalException(ExceptionMessages.IMPROPER_REFERRAL, new System.Object[]{msg.Type}, LdapException.LOCAL_ERROR);
}
return msg.Clone(dn, filter, reference);
}
/*
* Release connections acquired by following referrals
*
* @param list the list of the connections
*/
/* package */
internal virtual void releaseReferralConnections(System.Collections.ArrayList list)
{
if (list == null)
{
return ;
}
// Release referral connections
for (int i = list.Count - 1; i >= 0; i--)
{
LdapConnection rconn = null;
try
{
rconn=(LdapConnection)list[i];
list.RemoveAt(i);
// rconn = (LdapConnection) list.RemoveAt(i);
rconn.Disconnect();
}
catch (System.IndexOutOfRangeException ex)
{
continue;
}
catch (LdapException lex)
{
continue;
}
}
return ;
}
//*************************************************************************
// Schema Related methods
//*************************************************************************
/// Retrieves the schema associated with a particular schema DN in the
/// directory server.
/// The schema DN for a particular entry is obtained by calling the
/// getSchemaDN method of LDAPConnection
///
///
/// The schema DN used to fetch the schema.
///
///
/// An LDAPSchema entry containing schema attributes. If the
/// entry contains no schema attributes then the returned LDAPSchema object
/// will be empty.
///
///
/// LDAPException This exception occurs if the schema entry
/// cannot be retrieved with this connection.
///
///
///
///
///
public virtual LdapSchema FetchSchema(System.String schemaDN)
{
LdapEntry ent = Read(schemaDN, LdapSchema.schemaTypeNames);
return new LdapSchema(ent);
}
/// Retrieves the Distiguished Name (DN) for the schema advertised in the
/// root DSE of the Directory Server.
///
/// The DN can be used with the methods fetchSchema and modify to retreive
/// and extend schema definitions. The schema entry is located by reading
/// subschemaSubentry attribute of the root DSE. This is equivalent to
/// calling {@link #getSchemaDN(String) } with the DN parameter as an empty
/// string: getSchemaDN("")
.
///
///
///
/// Distinguished Name of a schema entry in effect for the
/// Directory.
///
/// LDAPException This exception occurs if the schema DN
/// cannot be retrieved, or if the subschemaSubentry attribute associated
/// with the root DSE contains multiple values.
///
///
///
///
///
///
public virtual System.String GetSchemaDN()
{
return GetSchemaDN("");
}
/// Retrieves the Distiguished Name (DN) of the schema associated with a
/// entry in the Directory.
///
/// The DN can be used with the methods fetchSchema and modify to retreive
/// and extend schema definitions. Reads the subschemaSubentry of the entry
/// specified.
///
///
/// Distinguished Name of any entry. The subschemaSubentry
/// attribute is queried from this entry.
///
///
/// Distinguished Name of a schema entry in effect for the entry
/// identified by dn
.
///
///
/// LDAPException This exception occurs if a null or empty
/// value is passed as dn, if the subschemasubentry attribute cannot
/// be retrieved, or the subschemasubentry contains multiple values.
///
///
///
///
///
///
public virtual System.String GetSchemaDN(System.String dn)
{
System.String[] attrSubSchema = new System.String[]{"subschemaSubentry"};
/* Read the entries subschemaSubentry attribute. Throws an exception if
* no entries are returned. */
LdapEntry ent = this.Read(dn, attrSubSchema);
LdapAttribute attr = ent.getAttribute(attrSubSchema[0]);
System.String[] values = attr.StringValueArray;
if (values == null || values.Length < 1)
{
throw new LdapLocalException(ExceptionMessages.NO_SCHEMA, new System.Object[]{dn}, LdapException.NO_RESULTS_RETURNED);
}
else if (values.Length > 1)
{
throw new LdapLocalException(ExceptionMessages.MULTIPLE_SCHEMA, new System.Object[]{dn}, LdapException.CONSTRAINT_VIOLATION);
}
return values[0];
}
}
}