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