* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / class / System.DirectoryServices / System.DirectoryServices / DirectoryEntry.cs
1 /******************************************************************************
2 * The MIT License
3 * Copyright (c) 2003 Novell Inc.,  www.novell.com
4
5 * Permission is hereby granted, free of charge, to any person obtaining  a copy
6 * of this software and associated documentation files (the Software), to deal
7 * in the Software without restriction, including  without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
9 * copies of the Software, and to  permit persons to whom the Software is 
10 * furnished to do so, subject to the following conditions:
11
12 * The above copyright notice and this permission notice shall be included in 
13 * all copies or substantial portions of the Software.
14
15 * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 *******************************************************************************/
23
24 //
25 // System.DirectoryServices.DirectoryEntry.cs
26 //
27 // Authors:
28 //   Sunil Kumar (sunilk@novell.com)
29 //   Andreas Nahr (ClassDevelopment@A-SoftTech.com)
30 //       Boris Kirzner (borisk@mainsoft.com)
31 //
32 // (C)  Novell Inc.
33 //
34
35 using System.ComponentModel;
36 using Novell.Directory.Ldap;
37 using Novell.Directory.Ldap.Utilclass;
38 using System.Globalization;
39 using System.DirectoryServices.Design;
40 using System.Collections.Specialized;
41 using System.Configuration;
42
43 namespace System.DirectoryServices
44 {
45         
46         /// <summary>
47         ///Encapsulates a node or object in the Ldap Directory hierarchy.
48         /// </summary>
49         [TypeConverter (typeof (DirectoryEntryConverter))]
50         public class DirectoryEntry : Component 
51         {
52                 private static readonly string DEFAULT_LDAP_HOST = "System.DirectoryServices.DefaultLdapHost";
53                 private static readonly string DEFAULT_LDAP_PORT = "System.DirectoryServices.DefaultLdapPort";
54
55                 private LdapConnection _conn = null;
56                 private AuthenticationTypes _AuthenticationType=AuthenticationTypes.None;
57                 private DirectoryEntries _Children;
58                 private string _Fdn = null;
59                 private string _Path="";
60                 private string _Name=null;
61                 private DirectoryEntry _Parent=null;
62                 private string _Username;
63                 private string _Password;
64                 //private string _Nativeguid;
65                 private PropertyCollection _Properties = null;
66                 private string _SchemaClassName=null;
67                 private bool _Nflag = false;
68                 private bool _usePropertyCache=true;
69                 private bool _inPropertiesLoading;
70
71                 /// <summary>
72                 /// Returns entry's Fully distinguished name.
73                 /// </summary>
74                 internal string Fdn
75                 {
76                         get     {
77                                 if (_Fdn == null) {
78                                         LdapUrl lUrl = new LdapUrl (ADsPath);
79                                         string fDn=lUrl.getDN();
80                                         if(fDn != null)
81                                                 _Fdn = fDn;
82                                         else
83                                                 _Fdn=String.Empty;
84                                 }
85                                 return _Fdn;
86                         }
87                 }
88
89                 /// <summary>
90                 ///  Returns the connection object used to communicate with
91                 /// Ldap server
92                 /// </summary>
93                 internal LdapConnection conn
94                 {
95                         get                     {
96                                 if( _conn == null)
97                                         InitBlock();
98
99                                 return _conn;
100                         }
101                         set                     {
102                                 _conn=value;
103                         }
104                 }
105
106                 /// <summary>
107                 /// Flag to check whether the entry is to be cerated or it already
108                 /// exists.
109                 /// </summary>
110                 internal bool Nflag
111                 {
112                         get                     {
113                                 return _Nflag;
114                         }
115                         set                     {
116                                 _Nflag = value;
117                         }
118                 }
119
120                 /// <summary> Initializes the Connection and other properties.
121                 /// 
122                 /// </summary>
123                 private void InitBlock()
124                 {
125                         try                     {
126                                 _conn= new LdapConnection ();
127                                 LdapUrl lUrl = new LdapUrl (ADsPath);
128                                 _conn.Connect(lUrl.Host,lUrl.Port);
129                                 _conn.Bind(Username,Password, (Novell.Directory.Ldap.AuthenticationTypes)AuthenticationType);
130                         }
131                         catch(LdapException ex)                 {
132                                 throw ex;
133                         }
134                         catch(Exception e)                      {
135                                 throw e;
136                         }
137                 }
138
139                 /// <summary>
140                 /// Initializes the Entry specific properties e.g entry DN etc.
141                 /// </summary>
142                 void InitEntry()
143                 {                       
144                         LdapUrl lUrl = new LdapUrl (ADsPath);
145                         string dn = lUrl.getDN();
146                         if (dn != null ) {
147                                 if (String.Compare (dn,"rootDSE",true) == 0)
148                                         InitToRootDse (lUrl.Host,lUrl.Port);
149                                 else {
150                                 DN userDn = new DN (dn);
151                                 String[] lRdn = userDn.explodeDN(false);
152                                 _Name = (string)lRdn[0];
153                                 _Parent = new DirectoryEntry(conn);
154                                 _Parent.Path = GetLdapUrlString (lUrl.Host,lUrl.Port,userDn.Parent.ToString ());
155                                 }
156                         }
157                         else                    {
158                                 _Name=lUrl.Host+":"+lUrl.Port;
159                                 _Parent = new DirectoryEntry(conn);
160                                 _Parent.Path = "Ldap:";
161                         }
162                 }
163
164                 /// <summary>
165                 /// Initializes a new instance of the DirectoryEntry class
166                 /// </summary>
167                 public DirectoryEntry()
168                 {
169                 }
170
171                 /// <summary>
172                 /// Initializes a new instance of the DirectoryEntry class that binds
173                 ///  to the specified native Active Directory object.
174                 /// </summary>
175                 /// <param name="adsObject"> native active directory object</param>
176                 public DirectoryEntry(object adsObject)
177                 {
178                          throw new NotImplementedException();
179                 }
180
181                 /// <summary>
182                 /// Initializes a new instance of the DirectoryEntry class that binds
183                 ///  this instance to the node in Ldap Directory located at the
184                 ///  specified path.
185                 /// </summary>
186                 /// <param name="path"> Path of the entry i.e Ldap URL specifying 
187                 /// entry path</param>
188                 public DirectoryEntry(string path)
189                 {
190                         _Path=path;
191                 }
192
193                 /// <summary>
194                 /// Initializes a new instance of the DirectoryEntry class. The Path,
195                 ///  Username, and Password properties are set to the specified values.
196                 /// </summary>
197                 /// <param name="path">Path of the entry i.e Ldap URL specifying 
198                 /// entry path</param>
199                 /// <param name="username">user name to use when authenticating the client
200                 /// </param>
201                 /// <param name="password">password to use when authenticating the client
202                 /// </param>
203                 public DirectoryEntry(string path,string username,string password)
204                 {
205                         _Path=path;
206                         _Username=username;
207                         _Password=password;
208                 }
209
210                 /// <summary>
211                 /// Initializes a new instance of the DirectoryEntry class. The Path,
212                 ///  Username, and Password properties are set to the specified values.
213                 /// </summary>
214                 /// <param name="path">Path of the entry i.e Ldap URL specifying 
215                 /// entry path</param>
216                 /// <param name="username">user name to use when authenticating the client
217                 /// </param>
218                 /// <param name="password">password to use when authenticating the client
219                 /// </param>
220                 /// <param name="authenticationType"> type of authentication to use</param>
221                 public DirectoryEntry(
222                                 string path,
223                                 string username,
224                                 string password,
225                                 AuthenticationTypes authenticationType)
226                 {
227                         _Path=path;
228                         _Username=username;
229                         _Password=password;
230                         _AuthenticationType=authenticationType;
231                 }
232
233                 /// <summary>
234                 /// Creates the entry object
235                 /// </summary>
236                 /// <param name="lconn">Connection object used to communicate with
237                 /// Ldap server</param>
238                 internal DirectoryEntry(LdapConnection lconn)
239                 {
240                         conn = lconn;
241                 }
242
243                 /// <summary>
244                 /// Returns Type of authentication to use while Binding to Ldap server
245                 /// </summary>
246                 [DSDescription ("Type of authentication to use while Binding to Ldap server")]
247                 [DefaultValue (AuthenticationTypes.None)]
248                 public AuthenticationTypes AuthenticationType 
249                 {
250                         get 
251                         {
252                                 return _AuthenticationType;
253                         }
254                         set 
255                         {
256                                 _AuthenticationType = value;
257                         }
258                 }
259
260                 /// <summary>
261                 /// Gets a DirectoryEntries containing the child entries of this node
262                 ///  in the Ldap Directory hierarchy.
263                 /// </summary>
264                 /// <value>A DirectoryEntries containing the child entries of this node
265                 ///  in the Ldap Directory hierarchy.</value>
266                 ///  <remarks>
267                 ///  The child entries are only the immediate children of this node.
268                 ///  Use this property to find, retrieve, or create a directory entry
269                 ///  in the hierarchy. This property is a collection that, along with 
270                 ///  usual iteration capabilities, provides an Add method through which
271                 ///  you add a node to the collection directly below the parent node
272                 ///  that you are currently bound to. When adding a node to the 
273                 ///  collection, you must specify a name for the new node and the name of 
274                 ///  a schema template that you want to associate with the node. For 
275                 ///  example, you might want to use a schema titled "Computer" to add 
276                 ///  new computers to the hierarchy.
277                 ///  </remarks>
278                 [DSDescription ("Child entries of this node")]
279                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
280                 [Browsable (false)]
281                 public DirectoryEntries Children 
282                 {
283                         get 
284                         {
285                                 _Children = new DirectoryEntries(ADsPath, conn);
286                                 return _Children;
287                         }
288                 }
289
290                 /// <summary>
291                 /// Gets the globally unique identifier (GUID) of the DirectoryEntry
292                 /// </summary>
293                 /// <value>The globally unique identifier of the DirectoryEntry.</value>
294                 /// <remarks>
295                 /// Not implemented yet.                
296                 /// </remarks>
297                 [DSDescription ("A globally unique identifier for this DirectoryEntry")]
298                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
299                 [Browsable (false)]
300                 [MonoTODO]
301                 public Guid Guid 
302                 {
303                         get 
304                         {
305                                 throw new NotImplementedException();
306                         }
307
308                 }
309
310                 /// <summary>
311                 /// Gets the name of the object as named with the underlying directory
312                 ///  service
313                 /// </summary>
314                 /// <value>The name of the object as named with the underlying directory
315                 ///  service</value>
316                 /// <remarks>This name, along with SchemaClassName, distinguishes this
317                 ///  entry from its siblings and must be unique amongst its siblings 
318                 ///  in each instance of DirectoryEntry.</remarks>
319                 [DSDescription ("The name of the object as named with the underlying directory")]
320                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
321                 [Browsable (false)]
322                 public string Name 
323                 {
324                         get                                                             {
325                                 if(_Name==null)                         {
326                                         if(CheckEntry(conn,ADsPath))
327                                                 InitEntry();
328                                         else
329                                                 throw new SystemException("There is no such object on the server");
330                                 }
331                                 return _Name;
332                         }
333                 }
334
335                 /// <summary>
336                 /// Gets this entry's parent in the Ldap Directory hierarchy.
337                 /// </summary>
338                 /// <value>This entry's parent in the Active Directory hierarc</value>
339                 [DSDescription ("This entry's parent in the Ldap Directory hierarchy.")]
340                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
341                 [Browsable (false)]
342                 public DirectoryEntry Parent 
343                 {
344                         get                     {
345                                 if(_Parent==null)                               {
346                                         if(CheckEntry(conn,ADsPath))
347                                                 InitEntry();
348                                         else
349                                                 throw new SystemException("There is no such object on the server");
350                                 }
351                                 return _Parent;
352                         }
353                 }
354
355                 /// <summary>
356                 /// Gets the globally unique identifier of the DirectoryEntry, as 
357                 /// returned from the provider
358                 /// </summary>
359                 /// <value>
360                 /// The globally unique identifier of the DirectoryEntry, as returned 
361                 /// from the provider.
362                 /// </value>
363                 /// <remarks>
364                 /// Not implemented yet.
365                 /// </remarks>
366                 [DSDescription ("The globally unique identifier of the DirectoryEntry, as returned from the provider")]
367                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
368                 [Browsable (false)]
369                 [MonoTODO]
370                 public string NativeGuid 
371                 {
372                         get                     {
373                                 throw new NotImplementedException();
374                         }
375                 }
376
377                 /// <summary>
378                 /// Gets the native Active Directory Service Interfaces (ADSI) object.
379                 /// </summary>
380                 /// <remarks>
381                 /// Not implemented yet
382                 [DSDescription ("The native Active Directory Service Interfaces (ADSI) object.")]
383                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
384                 [Browsable (false)]
385                 public object NativeObject 
386                 {
387                         [MonoTODO]
388                         get                     {
389                                 throw new NotImplementedException();
390                         }
391                 }
392
393                 /// <summary>
394                 /// Determines if a cache should be used.
395                 /// </summary>
396                 [DSDescription ("Determines if a cache should be used.")]
397                 [DefaultValue (true)]
398                 public bool UsePropertyCache
399                 {
400                         get 
401                         {
402                                 return _usePropertyCache;
403                         }
404                         set 
405                         {
406                                 _usePropertyCache = value;
407                         }
408                 }
409
410                 /// <summary>
411                 /// Gets or sets the password to use when authenticating the client.
412                 /// </summary>
413                 /// <value>
414                 /// The password to use when authenticating the client.
415                 /// </value>
416                 /// <remarks>
417                 /// You can set the Username and password in order to specify alternate 
418                 /// credentials with which to access the information in Ldap Directory. 
419                 /// Any other DirectoryEntry objects retrieved from this instance (for 
420                 /// example, through Children) are automatically created with the same 
421                 /// alternate credentials.
422                 /// </remarks>
423                 [DSDescription ("The password to use when authenticating the client.")]
424                 [DefaultValue (null)]
425                 [Browsable (false)]
426                 public string Password 
427                 {
428                         get             {
429                                 return _Password;
430                         }
431                         set                     {
432                                 _Password = value;
433                         }
434
435                 }
436
437                 /// <summary>
438                 /// Gets or sets the user name to use when authenticating the client.
439                 /// </summary>
440                 /// <value>
441                 /// The user name to use when authenticating the client.
442                 /// </value>
443                 /// <remarks>
444                 /// You can set the user name and Password in order to specify alternate 
445                 /// credentials with which to access the information in Ldap Directory. 
446                 /// Any other DirectoryEntry objects retrieved from this instance (for 
447                 /// example, through Children) are automatically created with the same 
448                 /// alternate 
449                 /// </remarks>
450                 [DSDescription ("The user name to use when authenticating the client.")]
451                 [DefaultValue (null)]
452                 [Browsable (false)]
453                 [TypeConverter ("System.Diagnostics.Design.StringValueConverter, " + Consts.AssemblySystem_Design)]
454                 public string Username 
455                 {
456                         get                     {
457                                 return _Username ;
458                         }
459                         set                     {
460                                 _Username = value;
461                         }
462
463                 }
464
465                 /// <summary>
466                 /// Gets or sets the path for this DirectoryEntry.
467                 /// </summary>
468                 /// <value>
469                 /// The path of this DirectoryEntry. The default is an empty string ("").
470                 /// </value>
471                 /// <remarks>
472                 /// The Path property uniquely identifies this entry in a networked 
473                 /// environment. This entry can always be retrieved using this Path.
474                 /// 
475                 /// Setting the Path retrieves a new entry from the directory store; it 
476                 /// does not change the path of the currently bound entry.
477                 /// 
478                 /// The classes associated with the DirectoryEntry component can be used 
479                 /// with any of the  Directory service providers. Some of the current 
480                 /// providers are Internet Information Services (IIS), Lightweight Directory 
481                 /// Access Protocol (Ldap), Novell NetWare Directory Service (NDS), and WinNT.
482                 /// 
483                 /// Currently we Support only Ldap provider.
484                 /// e.g Ldap://[hostname]:[port number]/[ObjectFDN]
485                 /// </remarks>
486                 [DSDescription ("The path for this DirectoryEntry.")]
487                 [DefaultValue ("")]
488                 [RecommendedAsConfigurable (true)]
489                 [TypeConverter ("System.Diagnostics.Design.StringValueConverter, " + Consts.AssemblySystem_Design)]
490                 public string Path 
491                 {
492                         get                     {
493                                 return _Path;
494                         }
495                         set                     {
496                                 if (value == null)
497                                         _Path = String.Empty;
498                                 else
499                                         _Path = value;
500                         }
501                 }
502
503                 internal string ADsPath
504                 {
505                         get     {
506                                 if (Path == null || Path == String.Empty) {
507                                         DirectoryEntry rootDse = new DirectoryEntry ();
508                                         rootDse.InitToRootDse (null,-1);
509                                         string namingContext = (string) rootDse.Properties ["defaultNamingContext"].Value;
510                                         if ( namingContext == null )
511                                                 namingContext = (string) rootDse.Properties ["namingContexts"].Value;
512
513                                         LdapUrl actualUrl= new LdapUrl (DefaultHost,DefaultPort,namingContext);
514                                         return actualUrl.ToString ();
515                                 }
516                                 return Path;
517                         }
518                 }
519
520                 /// <summary>
521                 /// Gets a PropertyCollection of properties set on this object.
522                 /// </summary>
523                 /// <value>
524                 /// A PropertyCollection of properties set on this object.
525                 /// </value>
526                 [DSDescription ("Properties set on this object.")]
527                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
528                 [Browsable (false)]
529                 public PropertyCollection Properties
530                 {
531                         get                     {
532                                 return GetProperties (true);
533                         }
534                 }
535
536                 /// <summary>
537                 /// Gets the name of the schema used for this DirectoryEntry
538                 /// </summary>
539                 /// <value>
540                 /// The name of the schema used for this DirectoryEntry.
541                 /// </value>
542                 [DSDescription ("The name of the schema used for this DirectoryEntry.")]
543                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
544                 [Browsable (false)]
545                 public string SchemaClassName 
546                 {
547                         get                     {
548                                 if(_SchemaClassName==null)                              {
549                                                 _SchemaClassName = FindAttrValue("structuralObjectClass");
550                                 }
551                                 return _SchemaClassName;
552                         }
553                 }
554
555                 /// <summary>
556                 /// Gets the current schema directory entry.
557                 /// </summary>
558                 /// <remarks>
559                 /// Not implemented yet
560                 [DSDescription ("The current schema directory entry.")]
561                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
562                 [Browsable (false)]
563                 public DirectoryEntry SchemaEntry 
564                 {
565                         [MonoTODO]
566                         get                     {
567                                 throw new NotImplementedException();
568                         }
569                 }
570
571                 private string DefaultHost
572                 {
573                         get {
574                                 string defaultHost = (string) AppDomain.CurrentDomain.GetData (DEFAULT_LDAP_HOST);
575
576                                 if (defaultHost == null) {
577                                         NameValueCollection config = (NameValueCollection) ConfigurationSettings.GetConfig ("System.DirectoryServices/Settings");
578                                         if (config != null) 
579                                                 defaultHost = config ["servername"];
580
581                                         if (defaultHost == null) 
582                                                 defaultHost = "localhost";
583
584                                         AppDomain.CurrentDomain.SetData (DEFAULT_LDAP_HOST,defaultHost);
585                                 }
586                                 return defaultHost;
587                         }
588                 }
589
590                 private int DefaultPort
591                 {
592                         get {
593                                 string defaultPortStr = (string) AppDomain.CurrentDomain.GetData (DEFAULT_LDAP_PORT);
594
595                                 if (defaultPortStr == null) {
596                                         NameValueCollection config = (NameValueCollection) ConfigurationSettings.GetConfig ("System.DirectoryServices/Settings");
597                                         if (config != null)
598                                                 defaultPortStr = config ["port"];
599
600                                         if (defaultPortStr == null) 
601                                                 defaultPortStr = "389";
602
603                                         AppDomain.CurrentDomain.SetData (DEFAULT_LDAP_PORT,defaultPortStr);
604                                 }
605                                 return Int32.Parse (defaultPortStr);
606                         }
607                 }
608
609                 private void InitToRootDse(string host,int port)
610                 {
611                         if ( host == null )
612                                 host = DefaultHost;
613                         if ( port < 0 )
614                                 port = DefaultPort;     
615                 
616                         LdapUrl rootPath = new LdapUrl (host,port,String.Empty);
617                         string [] attrs = new string [] {"+","*"};
618                         DirectoryEntry rootEntry = new DirectoryEntry (rootPath.ToString (),this.Username,this.Password,this.AuthenticationType);
619                         DirectorySearcher searcher = new DirectorySearcher (rootEntry,null,attrs,SearchScope.Base);
620
621                         SearchResult result = searcher.FindOne ();                      
622                         // copy properties from search result
623                         PropertyCollection pcoll = new PropertyCollection ();
624                         foreach (string propertyName in result.Properties.PropertyNames) {
625                                 System.Collections.IEnumerator enumerator = result.Properties [propertyName].GetEnumerator ();
626                                 if (enumerator != null)
627                                         while (enumerator.MoveNext ())
628                                                 if (String.Compare (propertyName,"ADsPath",true) != 0)
629                                                         pcoll [propertyName].Add (enumerator.Current);
630                         }                       
631                         this.SetProperties (pcoll);
632                         this._Name = "rootDSE";
633                 }
634
635                 private void SetProperties(PropertyCollection pcoll)
636                 {
637                         _Properties = pcoll;
638                 }
639
640                 /// <summary>
641                 /// Returns entry properties.
642                 /// </summary>
643                 /// <param name="forceLoad">Specifies whenever to force the properties load from the server if local property cache is empty.</param>
644                 /// <returns></returns>
645                 private PropertyCollection GetProperties(bool forceLoad)
646                 {
647                         if (_Properties == null) {
648                                 // load properties into a different collection 
649                                 // to preserve original collection state if exception occurs
650                                 PropertyCollection properties = new PropertyCollection (this);
651                                 if (forceLoad && !Nflag)                                
652                                         LoadProperties (properties,null);
653
654                                 _Properties = properties ;
655                         }                       
656                         return _Properties;
657                 }
658
659                 /// <summary>
660                 /// Loads the values of the specified properties into the property cache.
661                 /// </summary>
662                 /// <param name="propertyNames">An array of the specified properties.</param>
663                 private void LoadProperties(PropertyCollection properties,string[] propertyNames)
664                 {
665                         _inPropertiesLoading = true;
666                         try     {
667                                 LdapSearchResults lsc=conn.Search (Fdn,LdapConnection.SCOPE_BASE,"objectClass=*",propertyNames,false);
668                                 if (lsc.hasMore ()) {
669                                         LdapEntry nextEntry = lsc.next ();
670                                         string [] lowcasePropertyNames = null;
671                                         int length = 0;
672                                         if (propertyNames != null) {
673                                                 length = propertyNames.Length;
674                                                 lowcasePropertyNames = new string [length];
675                                                 for(int i=0; i < length; i++)
676                                                         lowcasePropertyNames [i] = propertyNames [i].ToLower ();
677                                         }
678                                         foreach (LdapAttribute attribute in nextEntry.getAttributeSet ())       {
679                                                 string attributeName = attribute.Name;
680                                                 if ((propertyNames == null) || (Array.IndexOf (lowcasePropertyNames,attributeName.ToLower ()) != -1)) {
681                                                         properties [attributeName].Value = null;
682                                                         properties [attributeName].AddRange (attribute.StringValueArray);
683                                                         properties [attributeName].Mbit=false;
684                                                 }
685                                         }
686                                 }
687                         }
688                         finally {
689                                 _inPropertiesLoading = false;
690                         }
691                 }
692
693                 /// <summary>
694                 /// Searches an entry in the Ldap directory and returns the attribute value
695                 /// </summary>
696                 /// <param name="attrName">attribute whose value is required</param>
697                 /// <returns> value of the attribute stored in Ldap directory</returns>
698                 private string FindAttrValue(string attrName)
699                 {
700                         string aValue=null;
701                         string[] attrs={attrName};
702
703                         LdapSearchResults lsc=conn.Search(      Fdn,
704                                                                                                 LdapConnection.SCOPE_BASE,
705                                                                                                 "objectClass=*",
706                                                                                                 attrs,
707                                                                                                 false);
708                         while(lsc.hasMore())                    {
709                                 LdapEntry nextEntry = null;
710                                 try                                             {
711                                         nextEntry = lsc.next();
712                                 }
713                                 catch(LdapException e)          {
714                                         // Exception is thrown, go for next entry
715                                         throw e;
716                                 }
717                                 LdapAttribute attribute = nextEntry.getAttribute(attrName);
718                                 aValue = attribute.StringValue;
719                                 break;
720                         }
721                         return aValue;
722                 }
723
724                 /// <summary>
725                 /// Modifies an entry in the Ldap directory with the input LdapModification
726                 /// values.
727                 /// </summary>
728                 /// <param name="mods">Array consisting of the entry attribute name and the
729                 /// attribute  values to be modified.</param>
730                 private void ModEntry(LdapModification[] mods)
731                 {
732
733                         try                                             {
734                                 conn.Modify(Fdn,mods);
735                         }
736                         catch(LdapException le) {
737                                 throw le;
738                         }
739                 }
740
741                 /// <summary>
742                 /// Checks whether the entry exists in the Ldap directory or not
743                 /// </summary>
744                 /// <param name="lconn">
745                 /// Connection used to communicate with directory
746                 /// </param>
747                 /// <param name="epath">
748                 /// path of the entry
749                 /// </param>
750                 /// <returns>
751                 ///             true of the entry exists in the Ldap directory
752                 ///             false if entry doesn't exists
753                 /// </returns>
754                 private static bool CheckEntry(LdapConnection lconn, string epath)
755                 {
756                         LdapUrl lUrl=new LdapUrl(epath);
757                         string eDn=lUrl.getDN();
758                         if(eDn==null)
759                         {
760                                 eDn = String.Empty;
761                         }
762                         // rootDSE is a "virtual" entry that always exists
763                         else if (String.Compare (eDn,"rootDSE",true) == 0)
764                                 return true;
765
766                         string[] attrs={"objectClass"};
767                         try
768                         {
769                                 LdapSearchResults lsc=lconn.Search(     eDn,
770                                         LdapConnection.SCOPE_BASE,
771                                         "objectClass=*",
772                                         attrs,
773                                         false);
774                                 while(lsc.hasMore())
775                                 {
776                                         LdapEntry nextEntry = null;
777                                         try 
778                                         {
779                                                 nextEntry = lsc.next();
780                                         }
781                                         catch(LdapException e) 
782                                         {
783                                                 // Exception is thrown, go for next entry
784                                                 throw e;
785                                         }
786                                         break;
787                                 }
788
789                         }
790                         catch(LdapException le)
791                         {
792                                 if(le.ResultCode == LdapException.NO_SUCH_OBJECT)
793                                 {
794                                         return false;
795                                 }
796                                 else
797                                 {
798                                         throw le;
799                                 }
800                         }
801                         catch(Exception e)
802                         {
803                                 throw e;
804                         }
805                         return true;
806                 }
807
808                 /// <summary>
809                 /// Closes the DirectoryEntry and releases any system resources associated 
810                 /// with this component.
811                 /// </summary>
812                 /// <remarks>
813                 /// Following a call to Close, any operations on the DirectoryEntry might 
814                 /// raise exceptions.
815                 /// </remarks>
816                 public void Close()
817                 {
818                         if (_conn != null && _conn.Connected) {
819                                 _conn.Disconnect();
820                         }
821                 }
822
823                 /// <summary>
824                 /// Creates a copy of this entry as a child of the specified parent.
825                 /// </summary>
826                 /// <param name="newParent">The parent DirectoryEntry.  </param>
827                 /// <returns>A copy of this entry as a child of the specified parent.
828                 [MonoTODO]
829                 public DirectoryEntry CopyTo(DirectoryEntry newParent)
830                 {
831                         throw new NotImplementedException();
832                 }
833
834                 /// <summary>
835                 /// Deletes this entry and its entire subtree from the Active Directory 
836                 /// hierarchy.
837                 /// </summary>
838                 /// <remarks>
839                 /// CAUTION   The entry and its entire subtree are deleted from the 
840                 /// Ldap Directory hierarchy.
841                 /// </remarks>
842                 public void DeleteTree()
843                 {
844                         System.Collections.IEnumerator ienum = Children.GetEnumerator();
845                         while(ienum.MoveNext())
846                         {
847                                 DirectoryEntry de=(DirectoryEntry)ienum.Current;
848                                 conn.Delete(de.Fdn);
849                         }
850                         conn.Delete(Fdn);
851                 }
852
853                 /// <summary>
854                 /// Searches the directory store at the specified path to see whether 
855                 /// an entry exists
856                 /// </summary>
857                 /// <param name="path">
858                 /// The path at which to search the directory store. 
859                 /// </param>
860                 /// <returns>
861                 /// true if an entry exists in the directory store at the specified 
862                 /// path; otherwise, false.
863                 /// </returns>
864                 public static bool Exists(string path)
865                 {
866                         LdapConnection aconn=new LdapConnection();
867                         LdapUrl lurl=new LdapUrl(path);
868                         aconn.Connect(lurl.Host,lurl.Port);
869                         aconn.Bind("","");
870                         if(CheckEntry(aconn,path))
871                                 return true;
872                         else
873                                 return false;
874                 }
875
876                 /// <summary>
877                 /// Moves this entry to the specified parent.
878                 /// </summary>
879                 /// <param name="pentry">
880                 /// The parent to which you want to move this entry
881                 /// </param>
882                 public void MoveTo(DirectoryEntry newParent)
883                 {
884                         string oldParentFdn = Parent.Fdn;
885                         conn.Rename(Fdn, Name, newParent.Fdn, true);
886                         // TBD : threat multiple name instance in path
887                         Path = Path.Replace(oldParentFdn,newParent.Fdn);
888                         RefreshEntry();                 
889                 }
890
891                 /// <summary>
892                 /// Moves this entry to the specified parent and changes its name to 
893                 /// the value of the newName parameter.
894                 /// </summary>
895                 /// <param name="newParent"> The parent to which you want to move 
896                 /// this entry
897                 /// </param>
898                 /// <param name="newName">
899                 /// The new name of this entry. 
900                 /// </param>
901                 public void MoveTo(     DirectoryEntry newParent,
902                                                         string newName  )
903                 {
904                         string oldParentFdn = Parent.Fdn;
905                         conn.Rename(Fdn, newName, newParent.Fdn, true);
906                         // TBD : threat multiple name instance in path
907                         Path = Path.Replace(oldParentFdn,newParent.Fdn).Replace(Name,newName);
908                         RefreshEntry(); 
909                 }
910
911                 /// <summary>
912                 /// Changes the name of this entry.
913                 /// </summary>
914                 /// <param name="newName">
915                 /// The new name of the entry. 
916                 /// </param>
917                 /// <remarks>
918                 /// Note   This will also affect the path used to refer to this entry.
919                 /// </remarks>
920                 public void Rename(     string newName  )
921                 {
922                         string oldName = Name;
923                         conn.Rename( Fdn, newName, true);
924                         // TBD : threat multiple name instance in path
925                         Path = Path.Replace(oldName,newName);
926                         RefreshEntry(); 
927                 }
928
929                 /// <summary>
930                 /// Calls a method on the native Active Directory.
931                 /// </summary>
932                 /// <param name="methodName">The name of the method to invoke. 
933                 /// </param>
934                 /// <param name="args">
935                 /// An array of type Object that contains the arguments of the method 
936                 /// to invoke. 
937                 /// </param>
938                 /// <returns>The return value of the invoked method</returns>
939                 /// <remarks>
940                 /// Not implemented.
941                 [MonoTODO]
942                 public object Invoke(string methodName,
943                         params object[] args)
944                 {
945                         throw new NotImplementedException();
946                 }
947
948                 /// <summary>
949                 /// Creates a copy of this entry, as a child of the specified parent, with 
950                 /// the specified new name.
951                 /// </summary>
952                 /// <param name="newParent">The parent DirectoryEntry.  </param>
953                 /// <param name="newName"> The name of the copy of this entry. 
954                 /// </param>
955                 /// <returns>A renamed copy of this entry as a child of the specified parent.
956                 [MonoTODO]
957                 public DirectoryEntry CopyTo( DirectoryEntry newParent,
958                         string newName  )
959                 {
960                         throw new NotImplementedException();
961                 }
962
963                 /// <summary>
964                 /// Saves any changes to the entry in the Ldap Directory store.
965                 /// </summary>
966                 /// <remarks>
967                 /// By default, changes to properties are done locally to a cache, and 
968                 /// property values to be read are cached after the first read. For more 
969                 /// information, see UsePropertyCache.
970                 /// Changes made to the cache include changes to the properties as well as 
971                 /// calls to Add (if this is the newly created entry).
972                 /// </remarks>
973                 public void CommitChanges()
974                 {
975                         if(UsePropertyCache) 
976                         {
977                                 CommitEntry();
978                         }
979                 }
980
981                 private void CommitEntry()
982                 {
983                         PropertyCollection properties = GetProperties(false);
984                         if(!Nflag)
985                         {
986                                 System.Collections.ArrayList modList = new System.Collections.ArrayList();
987                                 foreach (string attribute in properties.PropertyNames)
988                                 {
989                                         LdapAttribute attr=null;
990                                         if (properties [attribute].Mbit)
991                                         {
992                                                 if (properties [attribute].Count == 1)
993                                                 {
994                                                         string val = (string) properties [attribute].Value;
995                                                         attr = new LdapAttribute( attribute , val);
996                                                 }
997                                                 else
998                                                 {
999                                                         object[] vals =(object []) properties [attribute].Value;
1000                                                         string[] aStrVals = new string [properties [attribute].Count];
1001                                                         Array.Copy (vals,0,aStrVals,0,properties [attribute].Count);
1002                                                         attr = new LdapAttribute( attribute , aStrVals);
1003                                                 }
1004                                                 modList.Add( new LdapModification(LdapModification.REPLACE, attr));
1005                                                 properties [attribute].Mbit=false;
1006                                         }
1007                                 }
1008                                 if (modList.Count > 0) {
1009                                         LdapModification[] mods = new LdapModification[modList.Count];  
1010                                         Type mtype=Type.GetType("System.DirectoryServices.LdapModification");
1011                                         mods = (LdapModification[])modList.ToArray(typeof(LdapModification));
1012                                         ModEntry(mods);
1013                                 }
1014                         }
1015                         else
1016                         {
1017                                 LdapAttributeSet attributeSet = new LdapAttributeSet();
1018                                 foreach (string attribute in properties.PropertyNames)
1019                                 {
1020                                         if (properties [attribute].Count == 1)
1021                                         {
1022                                                 string val = (string) properties [attribute].Value;
1023                                                 attributeSet.Add(new LdapAttribute(attribute, val));                
1024                                         }
1025                                         else
1026                                         {
1027                                                 object[] vals = (object []) properties [attribute].Value;
1028                                                 string[] aStrVals = new string [properties [attribute].Count];
1029                                                 Array.Copy (vals,0,aStrVals,0,properties [attribute].Count);
1030                                                 attributeSet.Add( new LdapAttribute( attribute , aStrVals));
1031                                         }
1032                                 }
1033                                 LdapEntry newEntry = new LdapEntry( Fdn, attributeSet );
1034                                 conn.Add( newEntry );
1035                                 Nflag = false;
1036                         }
1037                 }
1038
1039                 internal void CommitDeferred()
1040                 {
1041                         if (!_inPropertiesLoading && !UsePropertyCache && !Nflag) 
1042                         {
1043                                 CommitEntry();
1044                         }
1045                 }
1046
1047                 void RefreshEntry()
1048                 {
1049                         _Properties = null;
1050                         _Fdn = null;
1051                         _Name = null;
1052                         _Parent = null;
1053                         _SchemaClassName = null;
1054                         InitEntry();
1055                 }
1056
1057                 /// <summary>
1058                 /// Loads the values of the specified properties into the property cache.
1059                 /// </summary>
1060                 public void RefreshCache ()
1061                 {
1062                         // note that GetProperties must be called with false, elswere infinite loop will be caused
1063                         PropertyCollection properties = new PropertyCollection ();
1064                         LoadProperties(properties, null);
1065                         SetProperties (properties);
1066                 }
1067
1068                 /// <summary>
1069                 /// Loads the values of the specified properties into the property cache.
1070                 /// </summary>
1071                 /// <param name="propertyNames">An array of the specified properties. </param>
1072                 public void RefreshCache (string[] propertyNames)
1073                 {
1074                         // note that GetProperties must be called with false, elswere infinite loop will be caused
1075                         LoadProperties(GetProperties(false),propertyNames);
1076                 }
1077
1078                 protected override void Dispose (bool disposing)
1079                 {
1080                         if (disposing) {
1081                                 Close ();
1082                         }
1083                         base.Dispose (disposing);
1084                 }
1085
1086                 internal static string GetLdapUrlString(string host, int port, string dn)
1087                 {
1088                         LdapUrl lUrl;
1089                         if (port == LdapConnection.DEFAULT_PORT)
1090                                 lUrl = new LdapUrl (host,0,dn);
1091                         else
1092                                 lUrl = new LdapUrl (host,port,dn);
1093                         return lUrl.ToString();
1094                 }
1095         }
1096 }