Merge pull request #1542 from ninjarobot/UriTemplateMatchException
[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                 [DSDescription ("The provider-specific options for this entry.")]
412                 [Browsable (false)]
413                 [MonoTODO]
414                 public DirectoryEntryConfiguration Options
415                 { 
416                         get {
417                                 throw new NotImplementedException ();
418                         }
419                 }
420
421                 /// <summary>
422                 /// Gets or sets the password to use when authenticating the client.
423                 /// </summary>
424                 /// <value>
425                 /// The password to use when authenticating the client.
426                 /// </value>
427                 /// <remarks>
428                 /// You can set the Username and password in order to specify alternate 
429                 /// credentials with which to access the information in Ldap Directory. 
430                 /// Any other DirectoryEntry objects retrieved from this instance (for 
431                 /// example, through Children) are automatically created with the same 
432                 /// alternate credentials.
433                 /// </remarks>
434                 [DSDescription ("The password to use when authenticating the client.")]
435                 [DefaultValue (null)]
436                 [Browsable (false)]
437                 public string Password 
438                 {
439                         get             {
440                                 return _Password;
441                         }
442                         set                     {
443                                 _Password = value;
444                         }
445
446                 }
447
448                 /// <summary>
449                 /// Gets or sets the user name to use when authenticating the client.
450                 /// </summary>
451                 /// <value>
452                 /// The user name to use when authenticating the client.
453                 /// </value>
454                 /// <remarks>
455                 /// You can set the user name and Password in order to specify alternate 
456                 /// credentials with which to access the information in Ldap Directory. 
457                 /// Any other DirectoryEntry objects retrieved from this instance (for 
458                 /// example, through Children) are automatically created with the same 
459                 /// alternate 
460                 /// </remarks>
461                 [DSDescription ("The user name to use when authenticating the client.")]
462                 [DefaultValue (null)]
463                 [Browsable (false)]
464                 [TypeConverter ("System.Diagnostics.Design.StringValueConverter, " + Consts.AssemblySystem_Design)]
465                 public string Username 
466                 {
467                         get                     {
468                                 return _Username ;
469                         }
470                         set                     {
471                                 _Username = value;
472                         }
473
474                 }
475
476                 /// <summary>
477                 /// Gets or sets the path for this DirectoryEntry.
478                 /// </summary>
479                 /// <value>
480                 /// The path of this DirectoryEntry. The default is an empty string ("").
481                 /// </value>
482                 /// <remarks>
483                 /// The Path property uniquely identifies this entry in a networked 
484                 /// environment. This entry can always be retrieved using this Path.
485                 /// 
486                 /// Setting the Path retrieves a new entry from the directory store; it 
487                 /// does not change the path of the currently bound entry.
488                 /// 
489                 /// The classes associated with the DirectoryEntry component can be used 
490                 /// with any of the  Directory service providers. Some of the current 
491                 /// providers are Internet Information Services (IIS), Lightweight Directory 
492                 /// Access Protocol (Ldap), Novell NetWare Directory Service (NDS), and WinNT.
493                 /// 
494                 /// Currently we Support only Ldap provider.
495                 /// e.g Ldap://[hostname]:[port number]/[ObjectFDN]
496                 /// </remarks>
497                 [DSDescription ("The path for this DirectoryEntry.")]
498                 [DefaultValue ("")]
499                 [RecommendedAsConfigurable (true)]
500                 [TypeConverter ("System.Diagnostics.Design.StringValueConverter, " + Consts.AssemblySystem_Design)]
501                 public string Path 
502                 {
503                         get                     {
504                                 return _Path;
505                         }
506                         set                     {
507                                 if (value == null)
508                                         _Path = String.Empty;
509                                 else
510                                         _Path = value;
511                         }
512                 }
513
514                 internal string ADsPath
515                 {
516                         get     {
517                                 if (Path == null || Path == String.Empty) {
518                                         DirectoryEntry rootDse = new DirectoryEntry ();
519                                         rootDse.InitToRootDse (null,-1);
520                                         string namingContext = (string) rootDse.Properties ["defaultNamingContext"].Value;
521                                         if ( namingContext == null )
522                                                 namingContext = (string) rootDse.Properties ["namingContexts"].Value;
523
524                                         LdapUrl actualUrl= new LdapUrl (DefaultHost,DefaultPort,namingContext);
525                                         return actualUrl.ToString ();
526                                 }
527                                 return Path;
528                         }
529                 }
530
531                 /// <summary>
532                 /// Gets a PropertyCollection of properties set on this object.
533                 /// </summary>
534                 /// <value>
535                 /// A PropertyCollection of properties set on this object.
536                 /// </value>
537                 [DSDescription ("Properties set on this object.")]
538                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
539                 [Browsable (false)]
540                 public PropertyCollection Properties
541                 {
542                         get                     {
543                                 return GetProperties (true);
544                         }
545                 }
546
547                 /// <summary>
548                 /// Gets the name of the schema used for this DirectoryEntry
549                 /// </summary>
550                 /// <value>
551                 /// The name of the schema used for this DirectoryEntry.
552                 /// </value>
553                 [DSDescription ("The name of the schema used for this DirectoryEntry.")]
554                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
555                 [Browsable (false)]
556                 public string SchemaClassName 
557                 {
558                         get                     {
559                                 if(_SchemaClassName==null)                              {
560                                                 _SchemaClassName = FindAttrValue("structuralObjectClass");
561                                 }
562                                 return _SchemaClassName;
563                         }
564                 }
565
566                 /// <summary>
567                 /// Gets the current schema directory entry.
568                 /// </summary>
569                 /// <remarks>
570                 /// Not implemented yet
571                 [DSDescription ("The current schema directory entry.")]
572                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
573                 [Browsable (false)]
574                 public DirectoryEntry SchemaEntry 
575                 {
576                         [MonoTODO]
577                         get                     {
578                                 throw new NotImplementedException();
579                         }
580                 }
581
582                 private string DefaultHost
583                 {
584                         get {
585                                 string defaultHost = (string) AppDomain.CurrentDomain.GetData (DEFAULT_LDAP_HOST);
586
587                                 if (defaultHost == null) {
588                                         NameValueCollection config = (NameValueCollection) ConfigurationSettings.GetConfig ("mainsoft.directoryservices/settings");
589                                         if (config != null) 
590                                                 defaultHost = config ["servername"];
591
592                                         if (defaultHost == null) 
593                                                 defaultHost = "localhost";
594
595                                         AppDomain.CurrentDomain.SetData (DEFAULT_LDAP_HOST,defaultHost);
596                                 }
597                                 return defaultHost;
598                         }
599                 }
600
601                 private int DefaultPort
602                 {
603                         get {
604                                 string defaultPortStr = (string) AppDomain.CurrentDomain.GetData (DEFAULT_LDAP_PORT);
605
606                                 if (defaultPortStr == null) {
607                                         NameValueCollection config = (NameValueCollection) ConfigurationSettings.GetConfig ("mainsoft.directoryservices/settings");
608                                         if (config != null)
609                                                 defaultPortStr = config ["port"];
610
611                                         if (defaultPortStr == null) 
612                                                 defaultPortStr = "389";
613
614                                         AppDomain.CurrentDomain.SetData (DEFAULT_LDAP_PORT,defaultPortStr);
615                                 }
616                                 return Int32.Parse (defaultPortStr);
617                         }
618                 }
619
620                 private void InitToRootDse(string host,int port)
621                 {
622                         if ( host == null )
623                                 host = DefaultHost;
624                         if ( port < 0 )
625                                 port = DefaultPort;     
626                 
627                         LdapUrl rootPath = new LdapUrl (host,port,String.Empty);
628                         string [] attrs = new string [] {"+","*"};
629                         DirectoryEntry rootEntry = new DirectoryEntry (rootPath.ToString (),this.Username,this.Password,this.AuthenticationType);
630                         DirectorySearcher searcher = new DirectorySearcher (rootEntry,null,attrs,SearchScope.Base);
631
632                         SearchResult result = searcher.FindOne ();                      
633                         // copy properties from search result
634                         PropertyCollection pcoll = new PropertyCollection ();
635                         foreach (string propertyName in result.Properties.PropertyNames) {
636                                 System.Collections.IEnumerator enumerator = result.Properties [propertyName].GetEnumerator ();
637                                 if (enumerator != null)
638                                         while (enumerator.MoveNext ())
639                                                 if (String.Compare (propertyName,"ADsPath",true) != 0)
640                                                         pcoll [propertyName].Add (enumerator.Current);
641                         }                       
642                         this.SetProperties (pcoll);
643                         this._Name = "rootDSE";
644                 }
645
646                 private void SetProperties(PropertyCollection pcoll)
647                 {
648                         _Properties = pcoll;
649                 }
650
651                 /// <summary>
652                 /// Returns entry properties.
653                 /// </summary>
654                 /// <param name="forceLoad">Specifies whenever to force the properties load from the server if local property cache is empty.</param>
655                 /// <returns></returns>
656                 private PropertyCollection GetProperties(bool forceLoad)
657                 {
658                         if (_Properties == null) {
659                                 // load properties into a different collection 
660                                 // to preserve original collection state if exception occurs
661                                 PropertyCollection properties = new PropertyCollection (this);
662                                 if (forceLoad && !Nflag)                                
663                                         LoadProperties (properties,null);
664
665                                 _Properties = properties ;
666                         }                       
667                         return _Properties;
668                 }
669
670                 /// <summary>
671                 /// Loads the values of the specified properties into the property cache.
672                 /// </summary>
673                 /// <param name="propertyNames">An array of the specified properties.</param>
674                 private void LoadProperties(PropertyCollection properties,string[] propertyNames)
675                 {
676                         _inPropertiesLoading = true;
677                         try     {
678                                 LdapSearchResults lsc=conn.Search (Fdn,LdapConnection.SCOPE_BASE,"objectClass=*",propertyNames,false);
679                                 if (lsc.hasMore ()) {
680                                         LdapEntry nextEntry = lsc.next ();
681                                         string [] lowcasePropertyNames = null;
682                                         int length = 0;
683                                         if (propertyNames != null) {
684                                                 length = propertyNames.Length;
685                                                 lowcasePropertyNames = new string [length];
686                                                 for(int i=0; i < length; i++)
687                                                         lowcasePropertyNames [i] = propertyNames [i].ToLower ();
688                                         }
689                                         foreach (LdapAttribute attribute in nextEntry.getAttributeSet ())       {
690                                                 string attributeName = attribute.Name;
691                                                 if ((propertyNames == null) || (Array.IndexOf (lowcasePropertyNames,attributeName.ToLower ()) != -1)) {
692                                                         properties [attributeName].Value = null;
693                                                         properties [attributeName].AddRange (attribute.StringValueArray);
694                                                         properties [attributeName].Mbit=false;
695                                                 }
696                                         }
697                                 }
698                         }
699                         finally {
700                                 _inPropertiesLoading = false;
701                         }
702                 }
703
704                 /// <summary>
705                 /// Searches an entry in the Ldap directory and returns the attribute value
706                 /// </summary>
707                 /// <param name="attrName">attribute whose value is required</param>
708                 /// <returns> value of the attribute stored in Ldap directory</returns>
709                 private string FindAttrValue(string attrName)
710                 {
711                         string aValue=null;
712                         string[] attrs={attrName};
713
714                         LdapSearchResults lsc=conn.Search(      Fdn,
715                                                                                                 LdapConnection.SCOPE_BASE,
716                                                                                                 "objectClass=*",
717                                                                                                 attrs,
718                                                                                                 false);
719                         while(lsc.hasMore())                    {
720                                 LdapEntry nextEntry = null;
721                                 try                                             {
722                                         nextEntry = lsc.next();
723                                 }
724                                 catch(LdapException e)          {
725                                         // Exception is thrown, go for next entry
726                                         throw e;
727                                 }
728                                 LdapAttribute attribute = nextEntry.getAttribute(attrName);
729                                 aValue = attribute.StringValue;
730                                 break;
731                         }
732                         return aValue;
733                 }
734
735                 /// <summary>
736                 /// Modifies an entry in the Ldap directory with the input LdapModification
737                 /// values.
738                 /// </summary>
739                 /// <param name="mods">Array consisting of the entry attribute name and the
740                 /// attribute  values to be modified.</param>
741                 private void ModEntry(LdapModification[] mods)
742                 {
743
744                         try                                             {
745                                 conn.Modify(Fdn,mods);
746                         }
747                         catch(LdapException le) {
748                                 throw le;
749                         }
750                 }
751
752                 /// <summary>
753                 /// Checks whether the entry exists in the Ldap directory or not
754                 /// </summary>
755                 /// <param name="lconn">
756                 /// Connection used to communicate with directory
757                 /// </param>
758                 /// <param name="epath">
759                 /// path of the entry
760                 /// </param>
761                 /// <returns>
762                 ///             true of the entry exists in the Ldap directory
763                 ///             false if entry doesn't exists
764                 /// </returns>
765                 private static bool CheckEntry(LdapConnection lconn, string epath)
766                 {
767                         LdapUrl lUrl=new LdapUrl(epath);
768                         string eDn=lUrl.getDN();
769                         if(eDn==null)
770                         {
771                                 eDn = String.Empty;
772                         }
773                         // rootDSE is a "virtual" entry that always exists
774                         else if (String.Compare (eDn,"rootDSE",true) == 0)
775                                 return true;
776
777                         string[] attrs={"objectClass"};
778                         try
779                         {
780                                 LdapSearchResults lsc=lconn.Search(     eDn,
781                                         LdapConnection.SCOPE_BASE,
782                                         "objectClass=*",
783                                         attrs,
784                                         false);
785                                 while(lsc.hasMore())
786                                 {
787                                         LdapEntry nextEntry = null;
788                                         try 
789                                         {
790                                                 nextEntry = lsc.next();
791                                         }
792                                         catch(LdapException e) 
793                                         {
794                                                 // Exception is thrown, go for next entry
795                                                 throw e;
796                                         }
797                                         break;
798                                 }
799
800                         }
801                         catch(LdapException le)
802                         {
803                                 if(le.ResultCode == LdapException.NO_SUCH_OBJECT)
804                                 {
805                                         return false;
806                                 }
807                                 else
808                                 {
809                                         throw le;
810                                 }
811                         }
812                         catch(Exception e)
813                         {
814                                 throw e;
815                         }
816                         return true;
817                 }
818
819                 /// <summary>
820                 /// Closes the DirectoryEntry and releases any system resources associated 
821                 /// with this component.
822                 /// </summary>
823                 /// <remarks>
824                 /// Following a call to Close, any operations on the DirectoryEntry might 
825                 /// raise exceptions.
826                 /// </remarks>
827                 public void Close()
828                 {
829                         if (_conn != null && _conn.Connected) {
830                                 _conn.Disconnect();
831                         }
832                 }
833
834                 /// <summary>
835                 /// Creates a copy of this entry as a child of the specified parent.
836                 /// </summary>
837                 /// <param name="newParent">The parent DirectoryEntry.  </param>
838                 /// <returns>A copy of this entry as a child of the specified parent.
839                 [MonoTODO]
840                 public DirectoryEntry CopyTo(DirectoryEntry newParent)
841                 {
842                         throw new NotImplementedException();
843                 }
844
845                 /// <summary>
846                 /// Deletes this entry and its entire subtree from the Active Directory 
847                 /// hierarchy.
848                 /// </summary>
849                 /// <remarks>
850                 /// CAUTION   The entry and its entire subtree are deleted from the 
851                 /// Ldap Directory hierarchy.
852                 /// </remarks>
853                 public void DeleteTree()
854                 {
855                         System.Collections.IEnumerator ienum = Children.GetEnumerator();
856                         while(ienum.MoveNext())
857                         {
858                                 DirectoryEntry de=(DirectoryEntry)ienum.Current;
859                                 conn.Delete(de.Fdn);
860                         }
861                         conn.Delete(Fdn);
862                 }
863
864                 /// <summary>
865                 /// Searches the directory store at the specified path to see whether 
866                 /// an entry exists
867                 /// </summary>
868                 /// <param name="path">
869                 /// The path at which to search the directory store. 
870                 /// </param>
871                 /// <returns>
872                 /// true if an entry exists in the directory store at the specified 
873                 /// path; otherwise, false.
874                 /// </returns>
875                 public static bool Exists(string path)
876                 {
877                         LdapConnection aconn=new LdapConnection();
878                         LdapUrl lurl=new LdapUrl(path);
879                         aconn.Connect(lurl.Host,lurl.Port);
880                         aconn.Bind("","");
881                         if(CheckEntry(aconn,path))
882                                 return true;
883                         else
884                                 return false;
885                 }
886
887                 /// <summary>
888                 /// Moves this entry to the specified parent.
889                 /// </summary>
890                 /// <param name="pentry">
891                 /// The parent to which you want to move this entry
892                 /// </param>
893                 public void MoveTo(DirectoryEntry newParent)
894                 {
895                         string oldParentFdn = Parent.Fdn;
896                         conn.Rename(Fdn, Name, newParent.Fdn, true);
897                         // TBD : threat multiple name instance in path
898                         Path = Path.Replace(oldParentFdn,newParent.Fdn);
899                         RefreshEntry();                 
900                 }
901
902                 /// <summary>
903                 /// Moves this entry to the specified parent and changes its name to 
904                 /// the value of the newName parameter.
905                 /// </summary>
906                 /// <param name="newParent"> The parent to which you want to move 
907                 /// this entry
908                 /// </param>
909                 /// <param name="newName">
910                 /// The new name of this entry. 
911                 /// </param>
912                 public void MoveTo(     DirectoryEntry newParent,
913                                                         string newName  )
914                 {
915                         string oldParentFdn = Parent.Fdn;
916                         conn.Rename(Fdn, newName, newParent.Fdn, true);
917                         // TBD : threat multiple name instance in path
918                         Path = Path.Replace(oldParentFdn,newParent.Fdn).Replace(Name,newName);
919                         RefreshEntry(); 
920                 }
921
922                 /// <summary>
923                 /// Changes the name of this entry.
924                 /// </summary>
925                 /// <param name="newName">
926                 /// The new name of the entry. 
927                 /// </param>
928                 /// <remarks>
929                 /// Note   This will also affect the path used to refer to this entry.
930                 /// </remarks>
931                 public void Rename(     string newName  )
932                 {
933                         string oldName = Name;
934                         conn.Rename( Fdn, newName, true);
935                         // TBD : threat multiple name instance in path
936                         Path = Path.Replace(oldName,newName);
937                         RefreshEntry(); 
938                 }
939
940                 /// <summary>
941                 /// Calls a method on the native Active Directory.
942                 /// </summary>
943                 /// <param name="methodName">The name of the method to invoke. 
944                 /// </param>
945                 /// <param name="args">
946                 /// An array of type Object that contains the arguments of the method 
947                 /// to invoke. 
948                 /// </param>
949                 /// <returns>The return value of the invoked method</returns>
950                 /// <remarks>
951                 /// Not implemented.
952                 [MonoTODO]
953                 public object Invoke(string methodName,
954                         params object[] args)
955                 {
956                         throw new NotImplementedException();
957                 }
958
959                 /// <summary>
960                 /// Gets a property value from the native Active Directory Entry.
961                 /// </summary>
962                 /// <param name="propertyName">The name of the property to get. 
963                 /// </param>
964                 /// <returns>The value of the property</returns>
965                 /// <remarks>
966                 /// Not implemented yet.
967                 [ComVisibleAttribute (false)]
968                 [MonoNotSupported ("")]
969                 public object InvokeGet (string propertyName)
970                 {
971                         throw new NotImplementedException ();
972                 }
973
974                 /// <summary>
975                 /// Sets a property value on the native Active Directory Entry.
976                 /// </summary>
977                 /// <param name="propertyName">The name of the property to get. 
978                 /// </param>
979                 /// <param name="args">
980                 /// An array of type Object that contains the arguments of the property 
981                 /// beeing set. 
982                 /// </param>
983                 /// <remarks>
984                 /// Not implemented yet.
985                 [ComVisibleAttribute (false)]
986                 [MonoNotSupported ("")]
987                 public void InvokeSet (string propertyName, params object [] args)
988                 {
989                         throw new NotImplementedException ();
990                 }
991
992                 /// <summary>
993                 /// Creates a copy of this entry, as a child of the specified parent, with 
994                 /// the specified new name.
995                 /// </summary>
996                 /// <param name="newParent">The parent DirectoryEntry.  </param>
997                 /// <param name="newName"> The name of the copy of this entry. 
998                 /// </param>
999                 /// <returns>A renamed copy of this entry as a child of the specified parent.
1000                 [MonoTODO]
1001                 public DirectoryEntry CopyTo( DirectoryEntry newParent,
1002                         string newName  )
1003                 {
1004                         throw new NotImplementedException();
1005                 }
1006
1007                 /// <summary>
1008                 /// Saves any changes to the entry in the Ldap Directory store.
1009                 /// </summary>
1010                 /// <remarks>
1011                 /// By default, changes to properties are done locally to a cache, and 
1012                 /// property values to be read are cached after the first read. For more 
1013                 /// information, see UsePropertyCache.
1014                 /// Changes made to the cache include changes to the properties as well as 
1015                 /// calls to Add (if this is the newly created entry).
1016                 /// </remarks>
1017                 public void CommitChanges()
1018                 {
1019                         if(UsePropertyCache) 
1020                         {
1021                                 CommitEntry();
1022                         }
1023                 }
1024
1025                 private void CommitEntry()
1026                 {
1027                         PropertyCollection properties = GetProperties(false);
1028                         if(!Nflag)
1029                         {
1030                                 System.Collections.ArrayList modList = new System.Collections.ArrayList();
1031                                 foreach (string attribute in properties.PropertyNames)
1032                                 {
1033                                         LdapAttribute attr=null;
1034                                         if (properties [attribute].Mbit)
1035                                         {
1036                                                 switch (properties [attribute].Count) {
1037                                                         case 0:
1038                                                                 attr = new LdapAttribute (attribute, new string [0]);
1039                                                                 modList.Add (new LdapModification (LdapModification.DELETE, attr));
1040                                                                 break;
1041                                                         case 1:
1042                                                                 string val = (string) properties [attribute].Value;
1043                                                                 attr = new LdapAttribute (attribute, val);
1044                                                                 modList.Add (new LdapModification (LdapModification.REPLACE, attr));
1045                                                                 break;
1046                                                         default:
1047                                                                 object [] vals = (object [])properties [attribute].Value;
1048                                                                 string [] aStrVals = new string [properties [attribute].Count];
1049                                                                 Array.Copy (vals, 0, aStrVals, 0, properties [attribute].Count);
1050                                                                 attr = new LdapAttribute (attribute, aStrVals);
1051                                                                 modList.Add (new LdapModification (LdapModification.REPLACE, attr));
1052                                                                 break;
1053                                                 }
1054                                                 properties [attribute].Mbit=false;
1055                                         }
1056                                 }
1057                                 if (modList.Count > 0) {
1058                                         LdapModification[] mods = new LdapModification[modList.Count];  
1059                                         Type mtype = typeof (LdapModification);
1060                                         mods = (LdapModification[])modList.ToArray(mtype);
1061                                         ModEntry(mods);
1062                                 }
1063                         }
1064                         else
1065                         {
1066                                 LdapAttributeSet attributeSet = new LdapAttributeSet();
1067                                 foreach (string attribute in properties.PropertyNames)
1068                                 {
1069                                         if (properties [attribute].Count == 1)
1070                                         {
1071                                                 string val = (string) properties [attribute].Value;
1072                                                 attributeSet.Add(new LdapAttribute(attribute, val));                
1073                                         }
1074                                         else
1075                                         {
1076                                                 object[] vals = (object []) properties [attribute].Value;
1077                                                 string[] aStrVals = new string [properties [attribute].Count];
1078                                                 Array.Copy (vals,0,aStrVals,0,properties [attribute].Count);
1079                                                 attributeSet.Add( new LdapAttribute( attribute , aStrVals));
1080                                         }
1081                                 }
1082                                 LdapEntry newEntry = new LdapEntry( Fdn, attributeSet );
1083                                 conn.Add( newEntry );
1084                                 Nflag = false;
1085                         }
1086                 }
1087
1088                 internal void CommitDeferred()
1089                 {
1090                         if (!_inPropertiesLoading && !UsePropertyCache && !Nflag) 
1091                         {
1092                                 CommitEntry();
1093                         }
1094                 }
1095
1096                 void RefreshEntry()
1097                 {
1098                         _Properties = null;
1099                         _Fdn = null;
1100                         _Name = null;
1101                         _Parent = null;
1102                         _SchemaClassName = null;
1103                         InitEntry();
1104                 }
1105
1106                 /// <summary>
1107                 /// Loads the values of the specified properties into the property cache.
1108                 /// </summary>
1109                 public void RefreshCache ()
1110                 {
1111                         // note that GetProperties must be called with false, elswere infinite loop will be caused
1112                         PropertyCollection properties = new PropertyCollection ();
1113                         LoadProperties(properties, null);
1114                         SetProperties (properties);
1115                 }
1116
1117                 /// <summary>
1118                 /// Loads the values of the specified properties into the property cache.
1119                 /// </summary>
1120                 /// <param name="propertyNames">An array of the specified properties. </param>
1121                 public void RefreshCache (string[] propertyNames)
1122                 {
1123                         // note that GetProperties must be called with false, elswere infinite loop will be caused
1124                         LoadProperties(GetProperties(false),propertyNames);
1125                 }
1126
1127                 protected override void Dispose (bool disposing)
1128                 {
1129                         if (disposing) {
1130                                 Close ();
1131                         }
1132                         base.Dispose (disposing);
1133                 }
1134
1135                 internal static string GetLdapUrlString(string host, int port, string dn)
1136                 {
1137                         LdapUrl lUrl;
1138                         if (port == LdapConnection.DEFAULT_PORT)
1139                                 lUrl = new LdapUrl (host,0,dn);
1140                         else
1141                                 lUrl = new LdapUrl (host,port,dn);
1142                         return lUrl.ToString();
1143                 }
1144         }
1145 }