Merge branch 'master' of http://github.com/mono/mono
[mono.git] / mcs / class / System.DirectoryServices / System.DirectoryServices / DirectoryEntry.cs
index 4c0edd9e77f5c182c77e21873a48610dd676b991..a1dffa258ff47f44fed1a2a93712ef84a6c9ee61 100644 (file)
@@ -27,6 +27,7 @@
 // Authors:
 //   Sunil Kumar (sunilk@novell.com)
 //   Andreas Nahr (ClassDevelopment@A-SoftTech.com)
+//      Boris Kirzner (borisk@mainsoft.com)
 //
 // (C)  Novell Inc.
 //
@@ -36,6 +37,9 @@ using Novell.Directory.Ldap;
 using Novell.Directory.Ldap.Utilclass;
 using System.Globalization;
 using System.DirectoryServices.Design;
+using System.Collections.Specialized;
+using System.Configuration;
+using System.Runtime.InteropServices;
 
 namespace System.DirectoryServices
 {
@@ -46,7 +50,9 @@ namespace System.DirectoryServices
        [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;
@@ -60,21 +66,22 @@ namespace System.DirectoryServices
                private PropertyCollection _Properties = null;
                private string _SchemaClassName=null;
                private bool _Nflag = false;
-               private bool _disposed;
+               private bool _usePropertyCache=true;
+               private bool _inPropertiesLoading;
 
                /// <summary>
                /// Returns entry's Fully distinguished name.
                /// </summary>
                internal string Fdn
                {
-                       get                     {
-                               if (_Fdn == null)                               {
-                                       LdapUrl lUrl = new LdapUrl(Path);
+                       get     {
+                               if (_Fdn == null) {
+                                       LdapUrl lUrl = new LdapUrl (ADsPath);
                                        string fDn=lUrl.getDN();
                                        if(fDn != null)
                                                _Fdn = fDn;
                                        else
-                                               _Fdn="";
+                                               _Fdn=String.Empty;
                                }
                                return _Fdn;
                        }
@@ -118,16 +125,14 @@ namespace System.DirectoryServices
                {
                        try                     {
                                _conn= new LdapConnection ();
-                               LdapUrl lUrl=new LdapUrl (Path);
+                               LdapUrl lUrl = new LdapUrl (ADsPath);
                                _conn.Connect(lUrl.Host,lUrl.Port);
-                               _conn.Bind(Username,Password);
+                               _conn.Bind(Username,Password, (Novell.Directory.Ldap.AuthenticationTypes)AuthenticationType);
                        }
                        catch(LdapException ex)                 {
-                               Console.WriteLine("Error:" + ex.LdapErrorMessage);
                                throw ex;
                        }
                        catch(Exception e)                      {
-                               Console.WriteLine("Error:" +  e.Message);
                                throw e;
                        }
                }
@@ -136,15 +141,19 @@ namespace System.DirectoryServices
                /// Initializes the Entry specific properties e.g entry DN etc.
                /// </summary>
                void InitEntry()
-               {
-                       LdapUrl lUrl=new LdapUrl (Path);
-                       if(lUrl.getDN()!=null)                  {
-                               DN userDn = new DN(lUrl.getDN());
+               {                       
+                       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);
-                               LdapUrl cUrl=new LdapUrl(lUrl.Host,lUrl.Port,userDn.Parent.ToString());
-                               _Parent.Path=cUrl.ToString();
+                               _Parent.Path = GetLdapUrlString (lUrl.Host,lUrl.Port,userDn.Parent.ToString ());
+                               }
                        }
                        else                    {
                                _Name=lUrl.Host+":"+lUrl.Port;
@@ -274,7 +283,7 @@ namespace System.DirectoryServices
                {
                        get 
                        {
-                               _Children = new DirectoryEntries(Path, conn);
+                               _Children = new DirectoryEntries(ADsPath, conn);
                                return _Children;
                        }
                }
@@ -315,7 +324,7 @@ namespace System.DirectoryServices
                {
                        get                                                             {
                                if(_Name==null)                         {
-                                       if(CheckEntry(conn,Path))
+                                       if(CheckEntry(conn,ADsPath))
                                                InitEntry();
                                        else
                                                throw new SystemException("There is no such object on the server");
@@ -335,7 +344,7 @@ namespace System.DirectoryServices
                {
                        get                     {
                                if(_Parent==null)                               {
-                                       if(CheckEntry(conn,Path))
+                                       if(CheckEntry(conn,ADsPath))
                                                InitEntry();
                                        else
                                                throw new SystemException("There is no such object on the server");
@@ -389,15 +398,13 @@ namespace System.DirectoryServices
                [DefaultValue (true)]
                public bool UsePropertyCache
                {
-                       [MonoTODO]
                        get 
                        {
-                               throw new NotImplementedException();
+                               return _usePropertyCache;
                        }
-                       [MonoTODO]
                        set 
                        {
-                               throw new NotImplementedException();
+                               _usePropertyCache = value;
                        }
                }
 
@@ -487,9 +494,28 @@ namespace System.DirectoryServices
                                return _Path;
                        }
                        set                     {
-                               _Path = value;
+                               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;
+                       }
                }
 
                /// <summary>
@@ -504,49 +530,7 @@ namespace System.DirectoryServices
                public PropertyCollection Properties
                {
                        get                     {
-                               if ( _Properties == null )                              {
-
-                                       _Properties =  new PropertyCollection();
-
-                                       try                                     {
-                                               LdapSearchResults lsc=conn.Search(      Fdn,
-                                                                                                                       LdapConnection.SCOPE_BASE,
-                                                                                                                       "objectClass=*",
-                                                                                                                       null,
-                                                                                                                       false);
-                                               while(lsc.hasMore())                                            {
-
-                                                       LdapEntry nextEntry = null;
-                                                       try                                                     {
-                                                               nextEntry = lsc.next();
-                                                       }
-                                                       catch(LdapException e)                                                  {
-                                                               Console.WriteLine("Error: " + e.LdapErrorMessage);
-                                                               // Exception is thrown, go for next entry
-                                                               throw e;
-                                                       }
-                                                       LdapAttributeSet attributeSet = nextEntry.getAttributeSet();
-                                                       System.Collections.IEnumerator ienum=attributeSet.GetEnumerator();
-                                                       if(ienum!=null)                                                 {
-                                                               while(ienum.MoveNext())                         {
-                                                                       LdapAttribute attribute=(LdapAttribute)ienum.Current;
-                                                                       string attributeName = attribute.Name;
-                                                                       _Properties[attributeName].AddRange(attribute.StringValueArray);
-                                                                       _Properties[attributeName].Mbit=false;
-                                                                       //                                                      string attributeVal = attribute.StringValue;
-                                                                       //                                                      _Properties[attributeName].Add(attributeVal);
-                                                               }
-                                                       }
-                                                       break;
-                                               }
-                                       }
-                                       catch( LdapException le)                                        {
-                                               if(le.ResultCode == LdapException.NO_SUCH_OBJECT)
-                                               {       }
-                                       }
-
-                               }
-                               return _Properties;
+                               return GetProperties (true);
                        }
                }
 
@@ -585,6 +569,128 @@ namespace System.DirectoryServices
                        }
                }
 
+               private string DefaultHost
+               {
+                       get {
+                               string defaultHost = (string) AppDomain.CurrentDomain.GetData (DEFAULT_LDAP_HOST);
+
+                               if (defaultHost == null) {
+                                       NameValueCollection config = (NameValueCollection) ConfigurationSettings.GetConfig ("mainsoft.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 ("mainsoft.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;
+               }
+
+               /// <summary>
+               /// Returns entry properties.
+               /// </summary>
+               /// <param name="forceLoad">Specifies whenever to force the properties load from the server if local property cache is empty.</param>
+               /// <returns></returns>
+               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;
+               }
+
+               /// <summary>
+               /// Loads the values of the specified properties into the property cache.
+               /// </summary>
+               /// <param name="propertyNames">An array of the specified properties.</param>
+               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;
+                       }
+               }
+
                /// <summary>
                /// Searches an entry in the Ldap directory and returns the attribute value
                /// </summary>
@@ -606,7 +712,6 @@ namespace System.DirectoryServices
                                        nextEntry = lsc.next();
                                }
                                catch(LdapException e)          {
-                                       Console.WriteLine("Error: " + e.LdapErrorMessage);
                                        // Exception is thrown, go for next entry
                                        throw e;
                                }
@@ -653,8 +758,12 @@ namespace System.DirectoryServices
                        string eDn=lUrl.getDN();
                        if(eDn==null)
                        {
-                               eDn="";
+                               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
                        {
@@ -672,7 +781,6 @@ namespace System.DirectoryServices
                                        }
                                        catch(LdapException e) 
                                        {
-                                               Console.WriteLine("Error: " + e.LdapErrorMessage);
                                                // Exception is thrown, go for next entry
                                                throw e;
                                        }
@@ -708,7 +816,9 @@ namespace System.DirectoryServices
                /// </remarks>
                public void Close()
                {
-                       conn.Disconnect();
+                       if (_conn != null && _conn.Connected) {
+                               _conn.Disconnect();
+                       }
                }
 
                /// <summary>
@@ -772,7 +882,11 @@ namespace System.DirectoryServices
                /// </param>
                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();                 
                }
 
                /// <summary>
@@ -788,7 +902,11 @@ namespace System.DirectoryServices
                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(); 
                }
 
                /// <summary>
@@ -802,7 +920,11 @@ namespace System.DirectoryServices
                /// </remarks>
                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(); 
                }
 
                /// <summary>
@@ -824,6 +946,41 @@ namespace System.DirectoryServices
                        throw new NotImplementedException();
                }
 
+#if NET_2_0
+               /// <summary>
+               /// Gets a property value from the native Active Directory Entry.
+               /// </summary>
+               /// <param name="propertyName">The name of the property to get. 
+               /// </param>
+               /// <returns>The value of the property</returns>
+               /// <remarks>
+               /// Not implemented yet.
+               [ComVisibleAttribute (false)]
+               [MonoNotSupported ("")]
+               public object InvokeGet (string propertyName)
+               {
+                       throw new NotImplementedException ();
+               }
+
+               /// <summary>
+               /// Sets a property value on the native Active Directory Entry.
+               /// </summary>
+               /// <param name="propertyName">The name of the property to get. 
+               /// </param>
+               /// <param name="args">
+               /// An array of type Object that contains the arguments of the property 
+               /// beeing set. 
+               /// </param>
+               /// <remarks>
+               /// Not implemented yet.
+               [ComVisibleAttribute (false)]
+               [MonoNotSupported ("")]
+               public void InvokeSet (string propertyName, params object [] args)
+               {
+                       throw new NotImplementedException ();
+               }
+#endif
+
                /// <summary>
                /// Creates a copy of this entry, as a child of the specified parent, with 
                /// the specified new name.
@@ -851,83 +1008,130 @@ namespace System.DirectoryServices
                /// </remarks>
                public void CommitChanges()
                {
+                       if(UsePropertyCache) 
+                       {
+                               CommitEntry();
+                       }
+               }
+
+               private void CommitEntry()
+               {
+                       PropertyCollection properties = GetProperties(false);
                        if(!Nflag)
                        {
                                System.Collections.ArrayList modList = new System.Collections.ArrayList();
-                               System.Collections.IDictionaryEnumerator id = Properties.GetEnumerator();
-                               while(id.MoveNext())
+                               foreach (string attribute in properties.PropertyNames)
                                {
-                                       string attribute=(string)id.Key;
                                        LdapAttribute attr=null;
-                                       if(Properties[attribute].Mbit)
+                                       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);
+                                               switch (properties [attribute].Count) {
+                                                       case 0:
+                                                               attr = new LdapAttribute (attribute, new string [0]);
+                                                               modList.Add (new LdapModification (LdapModification.DELETE, attr));
+                                                               break;
+                                                       case 1:
+                                                               string val = (string) properties [attribute].Value;
+                                                               attr = new LdapAttribute (attribute, val);
+                                                               modList.Add (new LdapModification (LdapModification.REPLACE, attr));
+                                                               break;
+                                                       default:
+                                                               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));
+                                                               break;
                                                }
-                                               modList.Add( new LdapModification(LdapModification.REPLACE, attr));
-                                               Properties[attribute].Mbit=false;
+                                               properties [attribute].Mbit=false;
                                        }
-//                                     Console.WriteLine(attribute + "Total no of attr value" + Properties[attribute].Count);
                                }
-                               LdapModification[] mods = new LdapModification[modList.Count];  
-                               Type mtype=Type.GetType("System.DirectoryServices.LdapModification");
-                               mods = (LdapModification[])modList.ToArray(typeof(LdapModification));
-                               ModEntry(mods);
+                               if (modList.Count > 0) {
+                                       LdapModification[] mods = new LdapModification[modList.Count];  
+                                       Type mtype = typeof (LdapModification);
+                                       mods = (LdapModification[])modList.ToArray(mtype);
+                                       ModEntry(mods);
+                               }
                        }
                        else
                        {
                                LdapAttributeSet attributeSet = new LdapAttributeSet();
-                               System.Collections.IDictionaryEnumerator id = Properties.GetEnumerator();
-                               while(id.MoveNext())
+                               foreach (string attribute in properties.PropertyNames)
                                {
-                                       string attribute=(string)id.Key;
-//                                     Console.WriteLine("attribute:"  + attribute + "Vals:" + Properties[attribute][0]);
-                                       if(Properties[attribute].Count==1)
+                                       if (properties [attribute].Count == 1)
                                        {
-                                               String val = (String)Properties[attribute].Value;
+                                               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);
+                                               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 );                   
+                               conn.Add( newEntry );
+                               Nflag = false;
                        }
                }
 
-               [MonoTODO]
+               internal void CommitDeferred()
+               {
+                       if (!_inPropertiesLoading && !UsePropertyCache && !Nflag) 
+                       {
+                               CommitEntry();
+                       }
+               }
+
+               void RefreshEntry()
+               {
+                       _Properties = null;
+                       _Fdn = null;
+                       _Name = null;
+                       _Parent = null;
+                       _SchemaClassName = null;
+                       InitEntry();
+               }
+
+               /// <summary>
+               /// Loads the values of the specified properties into the property cache.
+               /// </summary>
                public void RefreshCache ()
                {
-                       throw new NotImplementedException ("System.DirectoryServices.DirectoryEntry.RefreshCache()");
+                       // note that GetProperties must be called with false, elswere infinite loop will be caused
+                       PropertyCollection properties = new PropertyCollection ();
+                       LoadProperties(properties, null);
+                       SetProperties (properties);
                }
 
-               [MonoTODO]
-               public void RefreshCache (string[] args)
+               /// <summary>
+               /// Loads the values of the specified properties into the property cache.
+               /// </summary>
+               /// <param name="propertyNames">An array of the specified properties. </param>
+               public void RefreshCache (string[] propertyNames)
                {
-                       throw new NotImplementedException ("System.DirectoryServices.DirectoryEntry.RefreshCache(System.String[])");
+                       // 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 (!_disposed && disposing) {
+                       if (disposing) {
                                Close ();
-                               _disposed = true;
                        }
                        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();
+               }
        }
 }