/****************************************************************************** * 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. *******************************************************************************/ // // System.DirectoryServices.DirectoryEntry.cs // // Authors: // Sunil Kumar (sunilk@novell.com) // Andreas Nahr (ClassDevelopment@A-SoftTech.com) // Boris Kirzner (borisk@mainsoft.com) // // (C) Novell Inc. // using System.ComponentModel; using Novell.Directory.Ldap; using Novell.Directory.Ldap.Utilclass; using System.Globalization; using System.DirectoryServices.Design; using System.Collections.Specialized; using System.Configuration; namespace System.DirectoryServices { /// ///Encapsulates a node or object in the Ldap Directory hierarchy. /// [TypeConverter (typeof (DirectoryEntryConverter))] public class DirectoryEntry : Component { private static readonly string DEFAULT_LDAP_HOST = "System.DirectoryServices.DefaultLdapHost"; private static readonly string DEFAULT_LDAP_PORT = "System.DirectoryServices.DefaultLdapPort"; private LdapConnection _conn = null; private AuthenticationTypes _AuthenticationType=AuthenticationTypes.None; private DirectoryEntries _Children; private string _Fdn = null; private string _Path=""; private string _Name=null; private DirectoryEntry _Parent=null; private string _Username; private string _Password; //private string _Nativeguid; private PropertyCollection _Properties = null; private string _SchemaClassName=null; private bool _Nflag = false; private bool _usePropertyCache=true; private bool _inPropertiesLoading; /// /// Returns entry's Fully distinguished name. /// internal string Fdn { get { if (_Fdn == null) { LdapUrl lUrl = new LdapUrl (ADsPath); string fDn=lUrl.getDN(); if(fDn != null) _Fdn = fDn; else _Fdn=String.Empty; } return _Fdn; } } /// /// Returns the connection object used to communicate with /// Ldap server /// internal LdapConnection conn { get { if( _conn == null) InitBlock(); return _conn; } set { _conn=value; } } /// /// Flag to check whether the entry is to be cerated or it already /// exists. /// internal bool Nflag { get { return _Nflag; } set { _Nflag = value; } } /// Initializes the Connection and other properties. /// /// private void InitBlock() { try { _conn= new LdapConnection (); LdapUrl lUrl = new LdapUrl (ADsPath); _conn.Connect(lUrl.Host,lUrl.Port); _conn.Bind(Username,Password, (Novell.Directory.Ldap.AuthenticationTypes)AuthenticationType); } catch(LdapException ex) { throw ex; } catch(Exception e) { throw e; } } /// /// Initializes the Entry specific properties e.g entry DN etc. /// void InitEntry() { LdapUrl lUrl = new LdapUrl (ADsPath); string dn = lUrl.getDN(); if (dn != null ) { if (String.Compare (dn,"rootDSE",true) == 0) InitToRootDse (lUrl.Host,lUrl.Port); else { DN userDn = new DN (dn); String[] lRdn = userDn.explodeDN(false); _Name = (string)lRdn[0]; _Parent = new DirectoryEntry(conn); _Parent.Path = GetLdapUrlString (lUrl.Host,lUrl.Port,userDn.Parent.ToString ()); } } else { _Name=lUrl.Host+":"+lUrl.Port; _Parent = new DirectoryEntry(conn); _Parent.Path = "Ldap:"; } } /// /// Initializes a new instance of the DirectoryEntry class /// public DirectoryEntry() { } /// /// Initializes a new instance of the DirectoryEntry class that binds /// to the specified native Active Directory object. /// /// native active directory object public DirectoryEntry(object adsObject) { throw new NotImplementedException(); } /// /// Initializes a new instance of the DirectoryEntry class that binds /// this instance to the node in Ldap Directory located at the /// specified path. /// /// Path of the entry i.e Ldap URL specifying /// entry path public DirectoryEntry(string path) { _Path=path; } /// /// Initializes a new instance of the DirectoryEntry class. The Path, /// Username, and Password properties are set to the specified values. /// /// Path of the entry i.e Ldap URL specifying /// entry path /// user name to use when authenticating the client /// /// password to use when authenticating the client /// public DirectoryEntry(string path,string username,string password) { _Path=path; _Username=username; _Password=password; } /// /// Initializes a new instance of the DirectoryEntry class. The Path, /// Username, and Password properties are set to the specified values. /// /// Path of the entry i.e Ldap URL specifying /// entry path /// user name to use when authenticating the client /// /// password to use when authenticating the client /// /// type of authentication to use public DirectoryEntry( string path, string username, string password, AuthenticationTypes authenticationType) { _Path=path; _Username=username; _Password=password; _AuthenticationType=authenticationType; } /// /// Creates the entry object /// /// Connection object used to communicate with /// Ldap server internal DirectoryEntry(LdapConnection lconn) { conn = lconn; } /// /// Returns Type of authentication to use while Binding to Ldap server /// [DSDescription ("Type of authentication to use while Binding to Ldap server")] [DefaultValue (AuthenticationTypes.None)] public AuthenticationTypes AuthenticationType { get { return _AuthenticationType; } set { _AuthenticationType = value; } } /// /// Gets a DirectoryEntries containing the child entries of this node /// in the Ldap Directory hierarchy. /// /// A DirectoryEntries containing the child entries of this node /// in the Ldap Directory hierarchy. /// /// The child entries are only the immediate children of this node. /// Use this property to find, retrieve, or create a directory entry /// in the hierarchy. This property is a collection that, along with /// usual iteration capabilities, provides an Add method through which /// you add a node to the collection directly below the parent node /// that you are currently bound to. When adding a node to the /// collection, you must specify a name for the new node and the name of /// a schema template that you want to associate with the node. For /// example, you might want to use a schema titled "Computer" to add /// new computers to the hierarchy. /// [DSDescription ("Child entries of this node")] [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] [Browsable (false)] public DirectoryEntries Children { get { _Children = new DirectoryEntries(ADsPath, conn); return _Children; } } /// /// Gets the globally unique identifier (GUID) of the DirectoryEntry /// /// The globally unique identifier of the DirectoryEntry. /// /// Not implemented yet. /// [DSDescription ("A globally unique identifier for this DirectoryEntry")] [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] [Browsable (false)] [MonoTODO] public Guid Guid { get { throw new NotImplementedException(); } } /// /// Gets the name of the object as named with the underlying directory /// service /// /// The name of the object as named with the underlying directory /// service /// This name, along with SchemaClassName, distinguishes this /// entry from its siblings and must be unique amongst its siblings /// in each instance of DirectoryEntry. [DSDescription ("The name of the object as named with the underlying directory")] [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] [Browsable (false)] public string Name { get { if(_Name==null) { if(CheckEntry(conn,ADsPath)) InitEntry(); else throw new SystemException("There is no such object on the server"); } return _Name; } } /// /// Gets this entry's parent in the Ldap Directory hierarchy. /// /// This entry's parent in the Active Directory hierarc [DSDescription ("This entry's parent in the Ldap Directory hierarchy.")] [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] [Browsable (false)] public DirectoryEntry Parent { get { if(_Parent==null) { if(CheckEntry(conn,ADsPath)) InitEntry(); else throw new SystemException("There is no such object on the server"); } return _Parent; } } /// /// Gets the globally unique identifier of the DirectoryEntry, as /// returned from the provider /// /// /// The globally unique identifier of the DirectoryEntry, as returned /// from the provider. /// /// /// Not implemented yet. /// [DSDescription ("The globally unique identifier of the DirectoryEntry, as returned from the provider")] [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] [Browsable (false)] [MonoTODO] public string NativeGuid { get { throw new NotImplementedException(); } } /// /// Gets the native Active Directory Service Interfaces (ADSI) object. /// /// /// Not implemented yet [DSDescription ("The native Active Directory Service Interfaces (ADSI) object.")] [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] [Browsable (false)] public object NativeObject { [MonoTODO] get { throw new NotImplementedException(); } } /// /// Determines if a cache should be used. /// [DSDescription ("Determines if a cache should be used.")] [DefaultValue (true)] public bool UsePropertyCache { get { return _usePropertyCache; } set { _usePropertyCache = value; } } /// /// Gets or sets the password to use when authenticating the client. /// /// /// The password to use when authenticating the client. /// /// /// You can set the Username and password in order to specify alternate /// credentials with which to access the information in Ldap Directory. /// Any other DirectoryEntry objects retrieved from this instance (for /// example, through Children) are automatically created with the same /// alternate credentials. /// [DSDescription ("The password to use when authenticating the client.")] [DefaultValue (null)] [Browsable (false)] public string Password { get { return _Password; } set { _Password = value; } } /// /// Gets or sets the user name to use when authenticating the client. /// /// /// The user name to use when authenticating the client. /// /// /// You can set the user name and Password in order to specify alternate /// credentials with which to access the information in Ldap Directory. /// Any other DirectoryEntry objects retrieved from this instance (for /// example, through Children) are automatically created with the same /// alternate /// [DSDescription ("The user name to use when authenticating the client.")] [DefaultValue (null)] [Browsable (false)] [TypeConverter ("System.Diagnostics.Design.StringValueConverter, " + Consts.AssemblySystem_Design)] public string Username { get { return _Username ; } set { _Username = value; } } /// /// Gets or sets the path for this DirectoryEntry. /// /// /// The path of this DirectoryEntry. The default is an empty string (""). /// /// /// The Path property uniquely identifies this entry in a networked /// environment. This entry can always be retrieved using this Path. /// /// Setting the Path retrieves a new entry from the directory store; it /// does not change the path of the currently bound entry. /// /// The classes associated with the DirectoryEntry component can be used /// with any of the Directory service providers. Some of the current /// providers are Internet Information Services (IIS), Lightweight Directory /// Access Protocol (Ldap), Novell NetWare Directory Service (NDS), and WinNT. /// /// Currently we Support only Ldap provider. /// e.g Ldap://[hostname]:[port number]/[ObjectFDN] /// [DSDescription ("The path for this DirectoryEntry.")] [DefaultValue ("")] [RecommendedAsConfigurable (true)] [TypeConverter ("System.Diagnostics.Design.StringValueConverter, " + Consts.AssemblySystem_Design)] public string Path { get { return _Path; } set { if (value == null) _Path = String.Empty; else _Path = value; } } internal string ADsPath { get { if (Path == null || Path == String.Empty) { DirectoryEntry rootDse = new DirectoryEntry (); rootDse.InitToRootDse (null,-1); string namingContext = (string) rootDse.Properties ["defaultNamingContext"].Value; if ( namingContext == null ) namingContext = (string) rootDse.Properties ["namingContexts"].Value; LdapUrl actualUrl= new LdapUrl (DefaultHost,DefaultPort,namingContext); return actualUrl.ToString (); } return Path; } } /// /// Gets a PropertyCollection of properties set on this object. /// /// /// A PropertyCollection of properties set on this object. /// [DSDescription ("Properties set on this object.")] [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] [Browsable (false)] public PropertyCollection Properties { get { return GetProperties (true); } } /// /// Gets the name of the schema used for this DirectoryEntry /// /// /// The name of the schema used for this DirectoryEntry. /// [DSDescription ("The name of the schema used for this DirectoryEntry.")] [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] [Browsable (false)] public string SchemaClassName { get { if(_SchemaClassName==null) { _SchemaClassName = FindAttrValue("structuralObjectClass"); } return _SchemaClassName; } } /// /// Gets the current schema directory entry. /// /// /// Not implemented yet [DSDescription ("The current schema directory entry.")] [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] [Browsable (false)] public DirectoryEntry SchemaEntry { [MonoTODO] get { throw new NotImplementedException(); } } private string DefaultHost { get { string defaultHost = (string) AppDomain.CurrentDomain.GetData (DEFAULT_LDAP_HOST); if (defaultHost == null) { NameValueCollection config = (NameValueCollection) ConfigurationSettings.GetConfig ("System.DirectoryServices/Settings"); if (config != null) defaultHost = config ["servername"]; if (defaultHost == null) defaultHost = "localhost"; AppDomain.CurrentDomain.SetData (DEFAULT_LDAP_HOST,defaultHost); } return defaultHost; } } private int DefaultPort { get { string defaultPortStr = (string) AppDomain.CurrentDomain.GetData (DEFAULT_LDAP_PORT); if (defaultPortStr == null) { NameValueCollection config = (NameValueCollection) ConfigurationSettings.GetConfig ("System.DirectoryServices/Settings"); if (config != null) defaultPortStr = config ["port"]; if (defaultPortStr == null) defaultPortStr = "389"; AppDomain.CurrentDomain.SetData (DEFAULT_LDAP_PORT,defaultPortStr); } return Int32.Parse (defaultPortStr); } } private void InitToRootDse(string host,int port) { if ( host == null ) host = DefaultHost; if ( port < 0 ) port = DefaultPort; LdapUrl rootPath = new LdapUrl (host,port,String.Empty); string [] attrs = new string [] {"+","*"}; DirectoryEntry rootEntry = new DirectoryEntry (rootPath.ToString (),this.Username,this.Password,this.AuthenticationType); DirectorySearcher searcher = new DirectorySearcher (rootEntry,null,attrs,SearchScope.Base); SearchResult result = searcher.FindOne (); // copy properties from search result PropertyCollection pcoll = new PropertyCollection (); foreach (string propertyName in result.Properties.PropertyNames) { System.Collections.IEnumerator enumerator = result.Properties [propertyName].GetEnumerator (); if (enumerator != null) while (enumerator.MoveNext ()) if (String.Compare (propertyName,"ADsPath",true) != 0) pcoll [propertyName].Add (enumerator.Current); } this.SetProperties (pcoll); this._Name = "rootDSE"; } private void SetProperties(PropertyCollection pcoll) { _Properties = pcoll; } /// /// Returns entry properties. /// /// Specifies whenever to force the properties load from the server if local property cache is empty. /// private PropertyCollection GetProperties(bool forceLoad) { if (_Properties == null) { // load properties into a different collection // to preserve original collection state if exception occurs PropertyCollection properties = new PropertyCollection (this); if (forceLoad && !Nflag) LoadProperties (properties,null); _Properties = properties ; } return _Properties; } /// /// Loads the values of the specified properties into the property cache. /// /// An array of the specified properties. private void LoadProperties(PropertyCollection properties,string[] propertyNames) { _inPropertiesLoading = true; try { LdapSearchResults lsc=conn.Search (Fdn,LdapConnection.SCOPE_BASE,"objectClass=*",propertyNames,false); if (lsc.hasMore ()) { LdapEntry nextEntry = lsc.next (); string [] lowcasePropertyNames = null; int length = 0; if (propertyNames != null) { length = propertyNames.Length; lowcasePropertyNames = new string [length]; for(int i=0; i < length; i++) lowcasePropertyNames [i] = propertyNames [i].ToLower (); } foreach (LdapAttribute attribute in nextEntry.getAttributeSet ()) { string attributeName = attribute.Name; if ((propertyNames == null) || (Array.IndexOf (lowcasePropertyNames,attributeName.ToLower ()) != -1)) { properties [attributeName].Value = null; properties [attributeName].AddRange (attribute.StringValueArray); properties [attributeName].Mbit=false; } } } } finally { _inPropertiesLoading = false; } } /// /// Searches an entry in the Ldap directory and returns the attribute value /// /// attribute whose value is required /// value of the attribute stored in Ldap directory private string FindAttrValue(string attrName) { string aValue=null; string[] attrs={attrName}; LdapSearchResults lsc=conn.Search( Fdn, LdapConnection.SCOPE_BASE, "objectClass=*", attrs, false); while(lsc.hasMore()) { LdapEntry nextEntry = null; try { nextEntry = lsc.next(); } catch(LdapException e) { // Exception is thrown, go for next entry throw e; } LdapAttribute attribute = nextEntry.getAttribute(attrName); aValue = attribute.StringValue; break; } return aValue; } /// /// Modifies an entry in the Ldap directory with the input LdapModification /// values. /// /// Array consisting of the entry attribute name and the /// attribute values to be modified. private void ModEntry(LdapModification[] mods) { try { conn.Modify(Fdn,mods); } catch(LdapException le) { throw le; } } /// /// Checks whether the entry exists in the Ldap directory or not /// /// /// Connection used to communicate with directory /// /// /// path of the entry /// /// /// true of the entry exists in the Ldap directory /// false if entry doesn't exists /// private static bool CheckEntry(LdapConnection lconn, string epath) { LdapUrl lUrl=new LdapUrl(epath); string eDn=lUrl.getDN(); if(eDn==null) { eDn = String.Empty; } // rootDSE is a "virtual" entry that always exists else if (String.Compare (eDn,"rootDSE",true) == 0) return true; string[] attrs={"objectClass"}; try { LdapSearchResults lsc=lconn.Search( eDn, LdapConnection.SCOPE_BASE, "objectClass=*", attrs, false); while(lsc.hasMore()) { LdapEntry nextEntry = null; try { nextEntry = lsc.next(); } catch(LdapException e) { // Exception is thrown, go for next entry throw e; } break; } } catch(LdapException le) { if(le.ResultCode == LdapException.NO_SUCH_OBJECT) { return false; } else { throw le; } } catch(Exception e) { throw e; } return true; } /// /// Closes the DirectoryEntry and releases any system resources associated /// with this component. /// /// /// Following a call to Close, any operations on the DirectoryEntry might /// raise exceptions. /// public void Close() { if (_conn != null && _conn.Connected) { _conn.Disconnect(); } } /// /// Creates a copy of this entry as a child of the specified parent. /// /// The parent DirectoryEntry. /// A copy of this entry as a child of the specified parent. [MonoTODO] public DirectoryEntry CopyTo(DirectoryEntry newParent) { throw new NotImplementedException(); } /// /// Deletes this entry and its entire subtree from the Active Directory /// hierarchy. /// /// /// CAUTION The entry and its entire subtree are deleted from the /// Ldap Directory hierarchy. /// public void DeleteTree() { System.Collections.IEnumerator ienum = Children.GetEnumerator(); while(ienum.MoveNext()) { DirectoryEntry de=(DirectoryEntry)ienum.Current; conn.Delete(de.Fdn); } conn.Delete(Fdn); } /// /// Searches the directory store at the specified path to see whether /// an entry exists /// /// /// The path at which to search the directory store. /// /// /// true if an entry exists in the directory store at the specified /// path; otherwise, false. /// public static bool Exists(string path) { LdapConnection aconn=new LdapConnection(); LdapUrl lurl=new LdapUrl(path); aconn.Connect(lurl.Host,lurl.Port); aconn.Bind("",""); if(CheckEntry(aconn,path)) return true; else return false; } /// /// Moves this entry to the specified parent. /// /// /// The parent to which you want to move this entry /// public void MoveTo(DirectoryEntry newParent) { string oldParentFdn = Parent.Fdn; conn.Rename(Fdn, Name, newParent.Fdn, true); // TBD : threat multiple name instance in path Path = Path.Replace(oldParentFdn,newParent.Fdn); RefreshEntry(); } /// /// Moves this entry to the specified parent and changes its name to /// the value of the newName parameter. /// /// The parent to which you want to move /// this entry /// /// /// The new name of this entry. /// public void MoveTo( DirectoryEntry newParent, string newName ) { string oldParentFdn = Parent.Fdn; conn.Rename(Fdn, newName, newParent.Fdn, true); // TBD : threat multiple name instance in path Path = Path.Replace(oldParentFdn,newParent.Fdn).Replace(Name,newName); RefreshEntry(); } /// /// Changes the name of this entry. /// /// /// The new name of the entry. /// /// /// Note This will also affect the path used to refer to this entry. /// public void Rename( string newName ) { string oldName = Name; conn.Rename( Fdn, newName, true); // TBD : threat multiple name instance in path Path = Path.Replace(oldName,newName); RefreshEntry(); } /// /// Calls a method on the native Active Directory. /// /// The name of the method to invoke. /// /// /// An array of type Object that contains the arguments of the method /// to invoke. /// /// The return value of the invoked method /// /// Not implemented. [MonoTODO] public object Invoke(string methodName, params object[] args) { throw new NotImplementedException(); } /// /// Creates a copy of this entry, as a child of the specified parent, with /// the specified new name. /// /// The parent DirectoryEntry. /// The name of the copy of this entry. /// /// A renamed copy of this entry as a child of the specified parent. [MonoTODO] public DirectoryEntry CopyTo( DirectoryEntry newParent, string newName ) { throw new NotImplementedException(); } /// /// Saves any changes to the entry in the Ldap Directory store. /// /// /// By default, changes to properties are done locally to a cache, and /// property values to be read are cached after the first read. For more /// information, see UsePropertyCache. /// Changes made to the cache include changes to the properties as well as /// calls to Add (if this is the newly created entry). /// public void CommitChanges() { if(UsePropertyCache) { CommitEntry(); } } private void CommitEntry() { PropertyCollection properties = GetProperties(false); if(!Nflag) { System.Collections.ArrayList modList = new System.Collections.ArrayList(); foreach (string attribute in properties.PropertyNames) { LdapAttribute attr=null; if (properties [attribute].Mbit) { if (properties [attribute].Count == 1) { string val = (string) properties [attribute].Value; attr = new LdapAttribute( attribute , val); } else { object[] vals =(object []) properties [attribute].Value; string[] aStrVals = new string [properties [attribute].Count]; Array.Copy (vals,0,aStrVals,0,properties [attribute].Count); attr = new LdapAttribute( attribute , aStrVals); } modList.Add( new LdapModification(LdapModification.REPLACE, attr)); properties [attribute].Mbit=false; } } if (modList.Count > 0) { LdapModification[] mods = new LdapModification[modList.Count]; Type mtype=Type.GetType("System.DirectoryServices.LdapModification"); mods = (LdapModification[])modList.ToArray(typeof(LdapModification)); ModEntry(mods); } } else { LdapAttributeSet attributeSet = new LdapAttributeSet(); foreach (string attribute in properties.PropertyNames) { if (properties [attribute].Count == 1) { string val = (string) properties [attribute].Value; attributeSet.Add(new LdapAttribute(attribute, val)); } else { object[] vals = (object []) properties [attribute].Value; string[] aStrVals = new string [properties [attribute].Count]; Array.Copy (vals,0,aStrVals,0,properties [attribute].Count); attributeSet.Add( new LdapAttribute( attribute , aStrVals)); } } LdapEntry newEntry = new LdapEntry( Fdn, attributeSet ); conn.Add( newEntry ); Nflag = false; } } internal void CommitDeferred() { if (!_inPropertiesLoading && !UsePropertyCache && !Nflag) { CommitEntry(); } } void RefreshEntry() { _Properties = null; _Fdn = null; _Name = null; _Parent = null; _SchemaClassName = null; InitEntry(); } /// /// Loads the values of the specified properties into the property cache. /// public void RefreshCache () { // note that GetProperties must be called with false, elswere infinite loop will be caused PropertyCollection properties = new PropertyCollection (); LoadProperties(properties, null); SetProperties (properties); } /// /// Loads the values of the specified properties into the property cache. /// /// An array of the specified properties. public void RefreshCache (string[] propertyNames) { // note that GetProperties must be called with false, elswere infinite loop will be caused LoadProperties(GetProperties(false),propertyNames); } protected override void Dispose (bool disposing) { if (disposing) { Close (); } base.Dispose (disposing); } internal static string GetLdapUrlString(string host, int port, string dn) { LdapUrl lUrl; if (port == LdapConnection.DEFAULT_PORT) lUrl = new LdapUrl (host,0,dn); else lUrl = new LdapUrl (host,port,dn); return lUrl.ToString(); } } }