* roottypes.cs: Rename from tree.cs.
[mono.git] / mcs / class / Novell.Directory.Ldap / Novell.Directory.Ldap / LdapConnection.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 // Novell.Directory.Ldap.LdapConnection.cs
25 //
26 // Author:
27 //   Sunil Kumar (Sunilk@novell.com)
28 //
29 // (C) 2003 Novell, Inc (http://www.novell.com)
30 //
31
32 using System;
33 using Novell.Directory.Ldap;
34 using Novell.Directory.Ldap.Asn1;
35 using Novell.Directory.Ldap.Rfc2251;
36 using Novell.Directory.Ldap.Utilclass;
37 #if !TARGET_JVM
38 using Mono.Security.Protocol.Tls;
39 #else
40 using org.ietf.jgss;
41 using javax.security.auth;
42 using javax.security.auth.login;
43 using java.security;
44
45 using Novell.Directory.Ldap.Security;
46 using System.Collections.Specialized;
47 using System.Configuration;
48 #endif
49 using System.Security.Cryptography.X509Certificates;
50
51 namespace Novell.Directory.Ldap
52 {
53         
54         /// <summary> The central class that encapsulates the connection
55         /// to a directory server through the Ldap protocol.
56         /// LdapConnection objects are used to perform common Ldap
57         /// operations such as search, modify and add.
58         /// 
59         /// In addition, LdapConnection objects allow you to bind to an
60         /// Ldap server, set connection and search constraints, and perform
61         /// several other tasks.
62         /// 
63         /// An LdapConnection object is not connected on
64         /// construction and can only be connected to one server at one
65         /// port. Multiple threads may share this single connection, typically
66         /// by cloning the connection object, one for each thread. An
67         /// application may have more than one LdapConnection object, connected
68         /// to the same or different directory servers.
69         /// 
70         /// 
71         /// </summary>
72         public class LdapConnection : System.ICloneable
73         {
74                 private void  InitBlock()
75                 {
76                         defSearchCons = new LdapSearchConstraints();
77                         responseCtlSemaphore = new System.Object();
78                 }
79                 /// <summary> Returns the protocol version uses to authenticate.
80                 /// 
81                 ///  0 is returned if no authentication has been performed.
82                 /// 
83                 /// </summary>
84                 /// <returns> The protol version used for authentication or 0
85                 /// not authenticated.
86                 /// 
87                 /// </returns>
88                 virtual public int ProtocolVersion
89                 {
90                         get
91                         {
92                                 BindProperties prop = conn.BindProperties;
93                                 if (prop == null)
94                                 {
95                                         return Ldap_V3;
96                                 }
97                                 return prop.ProtocolVersion;
98                         }
99                         
100                 }
101                 /// <summary> Returns the distinguished name (DN) used for as the bind name during
102                 /// the last successful bind operation.  <code>null</code> is returned
103                 /// if no authentication has been performed or if the bind resulted in
104                 /// an aonymous connection.
105                 /// 
106                 /// </summary>
107                 /// <returns> The distinguished name if authenticated; otherwise, null.
108                 /// 
109                 /// </returns>
110                 virtual public System.String AuthenticationDN
111                 {
112                         get
113                         {
114                                 BindProperties prop = conn.BindProperties;
115                                 if (prop == null)
116                                 {
117                                         return null;
118                                 }
119                                 if (prop.Anonymous)
120                                 {
121                                         return null;
122                                 }
123                                 return prop.AuthenticationDN;
124                         }
125                         
126                 }
127                 /// <summary> Returns the method used to authenticate the connection. The return
128                 /// value is one of the following:
129                 /// 
130                 /// <ul>
131                 /// <li>"none" indicates the connection is not authenticated.</li>
132                 /// 
133                 /// 
134                 /// <li>"simple" indicates simple authentication was used or that a null
135                 /// or empty authentication DN was specified.</li>
136                 /// 
137                 /// <li>"sasl" indicates that a SASL mechanism was used to authenticate</li>
138                 /// </ul>
139                 /// 
140                 /// </summary>
141                 /// <returns> The method used to authenticate the connection.
142                 /// </returns>
143                 virtual public System.String AuthenticationMethod
144                 {
145                         get
146                         {
147                                 BindProperties prop = conn.BindProperties;
148                                 if (prop == null)
149                                 {
150                                         return "simple";
151                                 }
152                                 return conn.BindProperties.AuthenticationMethod;
153                         }
154                         
155                 }
156                 /// <summary> Returns the properties if any specified on binding with a
157                 /// SASL mechanism.
158                 /// 
159                 ///  Null is returned if no authentication has been performed
160                 /// or no authentication Map is present.
161                 /// 
162                 /// </summary>
163                 /// <returns> The bind properties Map Object used for SASL bind or null if
164                 /// the connection is not present or not authenticated.
165                 /// 
166                 /// </returns>
167                 virtual public System.Collections.IDictionary SaslBindProperties
168                 {
169                         get
170                         {
171                                 BindProperties prop = conn.BindProperties;
172                                 if (prop == null)
173                                 {
174                                         return null;
175                                 }
176                                 return conn.BindProperties.SaslBindProperties;
177                         }
178                         
179                 }
180                 /// <summary> Returns the call back handler if any specified on binding with a
181                 /// SASL mechanism.
182                 /// 
183                 ///  Null is returned if no authentication has been performed
184                 /// or no authentication call back handler is present.
185                 /// 
186                 /// </summary>
187                 /// <returns> The call back handler used for SASL bind or null if the
188                 /// object is not present or not authenticated.
189                 /// 
190                 /// </returns>
191                 virtual public System.Object SaslBindCallbackHandler
192                 {
193                         get
194                         {
195                                 BindProperties prop = conn.BindProperties;
196                                 if (prop == null)
197                                 {
198                                         return null;
199                                 }
200                                 return conn.BindProperties.SaslCallbackHandler;
201                         }
202                         
203                 }
204                 /// <summary> Returns a copy of the set of constraints associated with this
205                 /// connection. These constraints apply to all operations performed
206                 /// through this connection (unless a different set of constraints is
207                 /// specified when calling an operation method).
208                 /// 
209                 /// </summary>
210                 /// <returns> The set of default contraints that apply to this connection.
211                 /// 
212                 /// </returns>
213                 /// <summary> Sets the constraints that apply to all operations performed through
214                 /// this connection (unless a different set of constraints is specified
215                 /// when calling an operation method).  An LdapSearchConstraints object
216                 /// which is passed to this method sets all constraints, while an
217                 /// LdapConstraints object passed to this method sets only base constraints.
218                 /// 
219                 /// </summary>
220                 /// <param name="cons"> An LdapConstraints or LdapSearchConstraints Object
221                 /// containing the contstraint values to set.
222                 /// 
223                 /// </param>
224                 /// <seealso cref="Constraints()">
225                 /// </seealso>
226                 /// <seealso cref="SearchConstraints()">
227                 /// </seealso>
228                 virtual public LdapConstraints Constraints
229                 {
230                         get
231                         {
232                                 return (LdapConstraints) (this.defSearchCons).Clone();
233                         }
234                         
235                         set
236                         {
237                                 // Set all constraints, replace the object with a new one
238                                 if (value is LdapSearchConstraints)
239                                 {
240                                         defSearchCons = (LdapSearchConstraints) value.Clone();
241                                         return ;
242                                 }
243                                 
244                                 // We set the constraints this way, so a thread doesn't get an
245                                 // conconsistant view of the referrals.
246                                 LdapSearchConstraints newCons = (LdapSearchConstraints) defSearchCons.Clone();
247                                 newCons.HopLimit = value.HopLimit;
248                                 newCons.TimeLimit = value.TimeLimit;
249                                 newCons.setReferralHandler(value.getReferralHandler());
250                                 newCons.ReferralFollowing = value.ReferralFollowing;
251                                 LdapControl[] lsc = value.getControls();
252                                 if (lsc != null)
253                                 {
254                                         newCons.setControls(lsc);
255                                 }
256                                 System.Collections.Hashtable lp = newCons.Properties;
257                                 if (lp != null)
258                                 {
259                                         newCons.Properties = lp;
260                                 }
261                                 defSearchCons = newCons;
262                                 return ;
263                         }
264                         
265                 }
266                 /// <summary> Returns the host name of the Ldap server to which the object is or
267                 /// was last connected, in the format originally specified.
268                 /// 
269                 /// </summary>
270                 /// <returns> The host name of the Ldap server to which the object last
271                 /// connected or null if the object has never connected.
272                 /// 
273                 /// </returns>
274                 virtual public System.String Host
275                 {
276                         get
277                         {
278                                 return conn.Host;
279                         }
280                         
281                 }
282                 /// <summary> Returns the port number of the Ldap server to which the object is or
283                 /// was last connected.
284                 /// 
285                 /// </summary>
286                 /// <returns> The port number of the Ldap server to which the object last
287                 /// connected or -1 if the object has never connected.
288                 /// 
289                 /// </returns>
290                 virtual public int Port
291                 {
292                         get
293                         {
294                                 return conn.Port;
295                         }
296                         
297                 }
298                 /// <summary> Returns a copy of the set of search constraints associated with this
299                 /// connection. These constraints apply to search operations performed
300                 /// through this connection (unless a different set of
301                 /// constraints is specified when calling the search operation method).
302                 /// 
303                 /// </summary>
304                 /// <returns> The set of default search contraints that apply to
305                 /// this connection.
306                 /// 
307                 /// </returns>
308                 /// <seealso cref="Constraints">
309                 /// </seealso>
310                 /// <seealso cref="LdapSearchConstraints">
311                 /// </seealso>
312                 virtual public LdapSearchConstraints SearchConstraints
313                 {
314                         get
315                         {
316                                 return (LdapSearchConstraints) this.defSearchCons.Clone();
317                         }
318                         
319                 }
320
321
322                 ///<summary>  Indicates whther the perform Secure Operation or not
323                 ///</summary>
324                 ///
325                 ///<returns> 
326                 /// True if SSL is on
327                 /// False if its not on 
328                 ///</returns>
329                 public bool SecureSocketLayer
330                 {
331                         get
332                         {
333                                 return conn.Ssl;
334                         }
335                         set
336                         {
337                                 conn.Ssl=value;
338                         }
339                 }
340
341
342
343
344                 /// <summary> Indicates whether the object has authenticated to the connected Ldap
345                 /// server.
346                 /// 
347                 /// </summary>
348                 /// <returns> True if the object has authenticated; false if it has not
349                 /// authenticated.
350                 /// 
351                 /// </returns>
352                 virtual public bool Bound
353                 {
354                         get
355                         {
356                                 return conn.Bound;
357                         }
358                         
359                 }
360                 /// <summary> Indicates whether the connection represented by this object is open
361                 /// at this time.
362                 /// 
363                 /// </summary>
364                 /// <returns>  True if connection is open; false if the connection is closed.
365                 /// </returns>
366                 virtual public bool Connected
367                 {
368                         get
369                         {
370                                 return conn.Connected;
371                         }
372                         
373                 }
374
375                 /// <summary> Indicatates if the connection is protected by TLS.
376                 ///
377                 /// </summary>
378                 /// <returns> If startTLS has completed this method returns true.
379                 /// If stopTLS has completed or start tls failed, this method returns false.
380                 /// </returns>
381                 /// <returns>  True if the connection is protected by TLS.
382                 ///
383                 /// </returns>
384                 virtual public bool TLS
385                 {
386                         get
387                         {
388                                 return conn.TLS;
389                         }
390             
391                 }
392
393
394                 /// <summary>  Returns the Server Controls associated with the most recent response
395                 /// to a synchronous request on this connection object, or null
396                 /// if the latest response contained no Server Controls. The method
397                 /// always returns null for asynchronous requests. For asynchronous
398                 /// requests, the response controls are available in LdapMessage.
399                 /// 
400                 /// </summary>
401                 /// <returns> The server controls associated with the most recent response
402                 /// to a synchronous request or null if the response contains no server
403                 /// controls.
404                 /// 
405                 /// </returns>
406                 /// <seealso cref="LdapMessage.Controls">
407                 /// </seealso>
408                 virtual public LdapControl[] ResponseControls
409                 {
410                         get
411                         {
412                                 if (responseCtls == null)
413                                 {
414                                         return null;
415                                 }
416                                 
417                                 
418                                 // We have to clone the control just in case
419                                 // we have two client threads that end up retreiving the
420                                 // same control.
421                                 LdapControl[] clonedControl = new LdapControl[responseCtls.Length];
422                                 
423                                 // Also note we synchronize access to the local response
424                                 // control object just in case another message containing controls
425                                 // comes in from the server while we are busy duplicating
426                                 // this one.
427                                 lock (responseCtlSemaphore)
428                                 {
429                                         for (int i = 0; i < responseCtls.Length; i++)
430                                         {
431                                                 clonedControl[i] = (LdapControl) (responseCtls[i]).Clone();
432                                         }
433                                 }
434                                 
435                                 // Return the cloned copy.  Note we have still left the
436                                 // control in the local responseCtls variable just in case
437                                 // somebody requests it again.
438                                 return clonedControl;
439                         }
440                         
441                 }
442                 /// <summary> Return the Connection object associated with this LdapConnection
443                 /// 
444                 /// </summary>
445                 /// <returns> the Connection object
446                 /// </returns>
447                 virtual internal Connection Connection
448                 {
449                         /* package */
450                         
451                         get
452                         {
453                                 return conn;
454                         }
455                         
456                 }
457                 /// <summary> Return the Connection object name associated with this LdapConnection
458                 /// 
459                 /// </summary>
460                 /// <returns> the Connection object name
461                 /// </returns>
462                 virtual internal System.String ConnectionName
463                 {
464                         /* package */
465                         
466                         get
467                         {
468                                 return name;
469                         }
470                         
471                 }
472                 private LdapSearchConstraints defSearchCons;
473                 private LdapControl[] responseCtls = null;
474                 
475                 // Synchronization Object used to synchronize access to responseCtls
476                 private System.Object responseCtlSemaphore;
477                 
478                 private Connection conn = null;
479                 
480                 private static System.Object nameLock; // protect agentNum
481                 private static int lConnNum = 0; // Debug, LdapConnection number
482                 private System.String name; // String name for debug
483                 
484                 /// <summary> Used with search to specify that the scope of entrys to search is to
485                 /// search only the base obect.
486                 /// 
487                 /// SCOPE_BASE = 0
488                 /// </summary>
489                 public const int SCOPE_BASE = 0;
490                 
491                 /// <summary> Used with search to specify that the scope of entrys to search is to
492                 /// search only the immediate subordinates of the base obect.
493                 /// 
494                 /// SCOPE_ONE = 1
495                 /// </summary>
496                 public const int SCOPE_ONE = 1;
497                 
498                 /// <summary> Used with search to specify that the scope of entrys to search is to
499                 /// search the base object and all entries within its subtree.
500                 /// 
501                 /// SCOPE_ONE = 2
502                 /// </summary>
503                 public const int SCOPE_SUB = 2;
504                 
505                 /// <summary> Used with search instead of an attribute list to indicate that no
506                 /// attributes are to be returned.
507                 /// 
508                 /// NO_ATTRS = "1.1"
509                 /// </summary>
510                 public const System.String NO_ATTRS = "1.1";
511                 
512                 /// <summary> Used with search instead of an attribute list to indicate that all
513                 /// attributes are to be returned.
514                 /// 
515                 /// ALL_USER_ATTRS = "*"
516                 /// </summary>
517                 public const System.String ALL_USER_ATTRS = "*";
518                 
519                 /// <summary> Specifies the Ldapv3 protocol version when performing a bind operation.
520                 /// 
521                 /// Specifies Ldap version V3 of the protocol, and is specified
522                 /// when performing bind operations.
523                 /// You can use this identifier in the version parameter
524                 /// of the bind method to specify an Ldapv3 bind.
525                 /// Ldap_V3 is the default protocol version
526                 /// 
527                 /// Ldap_V3 = 3
528                 /// 
529                 /// </summary>
530                 public const int Ldap_V3 = 3;
531                 
532                 /// <summary> The default port number for Ldap servers.
533                 /// 
534                 /// You can use this identifier to specify the port when establishing
535                 /// a clear text connection to a server.  This the default port.
536                 /// 
537                 /// DEFAULT_PORT = 389
538                 /// 
539                 /// </summary>
540                 public const int DEFAULT_PORT = 389;
541                 
542                 
543                 /// <summary> The default SSL port number for Ldap servers.
544                 /// 
545                 /// DEFAULT_SSL_PORT = 636
546                 /// 
547                 /// You can use this identifier to specify the port when establishing
548                 /// a an SSL connection to a server..
549                 /// </summary>
550                 public const int DEFAULT_SSL_PORT = 636;
551                 
552                 /// <summary> A string that can be passed in to the getProperty method.
553                 /// 
554                 /// Ldap_PROPERTY_SDK = "version.sdk"
555                 /// 
556                 /// You can use this string to request the version of the SDK.
557                 /// </summary>
558                 public const System.String Ldap_PROPERTY_SDK = "version.sdk";
559                 
560                 /// <summary> A string that can be passed in to the getProperty method.
561                 /// 
562                 /// Ldap_PROPERTY_PROTOCOL = "version.protocol"
563                 /// 
564                 /// You can use this string to request the version of the
565                 /// Ldap protocol.
566                 /// </summary>
567                 public const System.String Ldap_PROPERTY_PROTOCOL = "version.protocol";
568                 
569                 /// <summary> A string that can be passed in to the getProperty method.
570                 /// 
571                 /// Ldap_PROPERTY_SECURITY = "version.security"
572                 /// 
573                 /// You can use this string to request the type of security
574                 /// being used.
575                 /// </summary>
576                 public const System.String Ldap_PROPERTY_SECURITY = "version.security";
577                 
578                 /// <summary> A string that corresponds to the server shutdown notification OID.
579                 /// This notification may be used by the server to advise the client that
580                 /// the server is about to close the connection due to an error
581                 /// condition.
582                 /// 
583                 /// SERVER_SHUTDOWN_OID = "1.3.6.1.4.1.1466.20036"
584                 /// </summary>
585                 public const System.String SERVER_SHUTDOWN_OID = "1.3.6.1.4.1.1466.20036";
586                 
587                 /// <summary> The OID string that identifies a StartTLS request and response.</summary>
588                 private const System.String START_TLS_OID = "1.3.6.1.4.1.1466.20037";
589                 
590                 public event CertificateValidationCallback UserDefinedServerCertValidationDelegate
591                 {
592                         add
593                         {
594                                 this.conn.OnCertificateValidation += value;
595                         }
596
597                         remove
598                         {
599                                 this.conn.OnCertificateValidation -= value;
600                         }
601                 }
602                 /*
603                 * Constructors
604                 */
605                 
606                 
607                 /// <summary> Constructs a new LdapConnection object, which will use the supplied
608                 /// class factory to construct a socket connection during
609                 /// LdapConnection.connect method.
610                 /// 
611                 /// </summary>
612                 /// <param name="factory">    An object capable of producing a Socket.
613                 /// 
614                 /// </param>
615                 public LdapConnection()
616                 {
617                         InitBlock();
618                         // Get a unique connection name for debug
619                         conn = new Connection();
620                         return ;
621                 }
622                 
623 /*              public LdapConnection(X509Certificate cert)
624                 {
625                         InitBlock();
626                         // Get a unique connection name for debug
627                         conn = new Connection();
628                         conn.Cert = cert;
629                         return ;
630                 }
631 */
632                 /*
633                 * The following are methods that affect the operation of
634                 * LdapConnection, but are not Ldap requests.
635                 */
636                 
637                 /// <summary> Returns a copy of the object with a private context, but sharing the
638                 /// network connection if there is one.
639                 /// 
640                 /// The network connection remains open until all clones have
641                 /// disconnected or gone out of scope. Any connection opened after
642                 /// cloning is private to the object making the connection.
643                 /// 
644                 /// The clone can issue requests and freely modify options and search
645                 /// constraints, and , without affecting the source object or other clones.
646                 /// If the clone disconnects or reconnects, it is completely dissociated
647                 /// from the source object and other clones. Reauthenticating in a clone,
648                 /// however, is a global operation which will affect the source object
649                 /// and all associated clones, because it applies to the single shared
650                 /// physical connection. Any request by an associated object after one
651                 /// has reauthenticated will carry the new identity.
652                 /// 
653                 /// </summary>
654                 /// <returns> A of the object.
655                 /// </returns>
656                 public System.Object Clone()
657                 {
658                         LdapConnection newClone;
659                         System.Object newObj;
660                         try
661                         {
662                                 newObj = base.MemberwiseClone();
663                                 newClone = (LdapConnection) newObj;
664                         }
665                         catch (System.Exception ce)
666                         {
667                                 throw new System.SystemException("Internal error, cannot create clone");
668                         }
669                         newClone.conn = conn; // same underlying connection
670                         
671                         //now just duplicate the defSearchCons and responseCtls
672                         if (defSearchCons != null)
673                         {
674                                 newClone.defSearchCons = (LdapSearchConstraints) defSearchCons.Clone();
675                         }
676                         else
677                         {
678                                 newClone.defSearchCons = null;
679                         }
680                         if (responseCtls != null)
681                         {
682                                 newClone.responseCtls = new LdapControl[responseCtls.Length];
683                                 for (int i = 0; i < responseCtls.Length; i++)
684                                 {
685                                         newClone.responseCtls[i] = (LdapControl) responseCtls[i].Clone();
686                                 }
687                         }
688                         else
689                         {
690                                 newClone.responseCtls = null;
691                         }
692                         conn.incrCloneCount(); // Increment the count of clones
693                         return newObj;
694                 }
695                 
696                 /// <summary> Closes the connection, if open, and releases any other resources held
697                 /// by the object.
698                 /// 
699                 /// </summary>
700                 /// <exception> LdapException A general exception which includes an error
701                 /// message and an Ldap error code.
702                 /// 
703                 /// </exception>
704                 /// <seealso cref="Disconnect">
705                 /// </seealso>
706                 ~LdapConnection()
707                 {
708                         // Disconnect did not come from user API call
709                         Disconnect(defSearchCons, false);
710                         return ;
711                 }
712                 
713                 /// <summary> Returns a property of a connection object.
714                 /// 
715                 /// </summary>
716                 /// <param name="name">  Name of the property to be returned.
717                 /// 
718                 /// The following read-only properties are available
719                 /// for any given connection:
720                 /// <ul>
721                 /// <li>Ldap_PROPERTY_SDK returns the version of this SDK,
722                 /// as a Float data type.</li>
723                 /// 
724                 /// <li>Ldap_PROPERTY_PROTOCOL returns the highest supported version of
725                 /// the Ldap protocol, as a Float data type.</li>
726                 /// 
727                 /// <li>Ldap_PROPERTY_SECURITY returns a comma-separated list of the
728                 /// types of authentication supported, as a
729                 /// string.</li>
730                 /// </ul>
731                 /// 
732                 /// A deep copy of the property is provided where applicable; a
733                 /// client does not need to clone the object received.
734                 /// 
735                 /// </param>
736                 /// <returns> The object associated with the requested property,
737                 /// or null if the property is not defined.
738                 /// 
739                 /// </returns>
740                 /// <seealso cref="LdapConstraints.getProperty">
741                 /// </seealso>
742                 /// <seealso cref="Object">
743                 /// </seealso>
744                 public virtual System.Object getProperty(System.String name)
745                 {
746                         if (name.ToUpper().Equals(Ldap_PROPERTY_SDK.ToUpper()))
747                                 return Connection.sdk;
748                         else if (name.ToUpper().Equals(Ldap_PROPERTY_PROTOCOL.ToUpper()))
749                                 return Connection.protocol;
750                         else if (name.ToUpper().Equals(Ldap_PROPERTY_SECURITY.ToUpper()))
751                                 return Connection.security;
752                         else
753                         {
754                                 return null;
755                         }
756                 }
757                 
758                 /// <summary> Registers an object to be notified on arrival of an unsolicited
759                 /// message from a server.
760                 /// 
761                 /// An unsolicited message has the ID 0. A new thread is created and
762                 /// the method "messageReceived" in each registered object is called in
763                 /// turn.
764                 /// 
765                 /// </summary>
766                 /// <param name="listener"> An object to be notified on arrival of an
767                 /// unsolicited message from a server.  This object must
768                 /// implement the LdapUnsolicitedNotificationListener interface.
769                 /// 
770                 /// </param>
771                 public virtual void  AddUnsolicitedNotificationListener(LdapUnsolicitedNotificationListener listener)
772                 {
773                         if (listener != null)
774                                 conn.AddUnsolicitedNotificationListener(listener);
775                 }
776                 
777                 
778                 
779                 /// <summary> Deregisters an object so that it will no longer be notified on
780                 /// arrival of an unsolicited message from a server. If the object is
781                 /// null or was not previously registered for unsolicited notifications,
782                 /// the method does nothing.
783                 /// 
784                 /// 
785                 /// </summary>
786                 /// <param name="listener"> An object to no longer be notified on arrival of
787                 /// an unsolicited message from a server.
788                 /// 
789                 /// </param>
790                 public virtual void  RemoveUnsolicitedNotificationListener(LdapUnsolicitedNotificationListener listener)
791                 {
792                         
793                         if (listener != null)
794                                 conn.RemoveUnsolicitedNotificationListener(listener);
795                 }
796                 
797                 /// <summary> Starts Transport Layer Security (TLS) protocol on this connection
798                 /// to enable session privacy.
799                 /// 
800                 /// This affects the LdapConnection object and all cloned objects. A
801                 /// socket factory that implements LdapTLSSocketFactory must be set on the
802                 /// connection.
803                 /// 
804                 /// </summary>
805                 /// <exception> LdapException Thrown if TLS cannot be started.  If a
806                 /// SocketFactory has been specified that does not implement
807                 /// LdapTLSSocketFactory an LdapException is thrown.
808                 /// 
809                 /// </exception>
810
811         public virtual void  startTLS()
812         {
813  
814             LdapMessage startTLS = MakeExtendedOperation(new LdapExtendedOperation(LdapConnection.START_TLS_OID, null), null);
815                                                                                 
816             int tlsID = startTLS.MessageID;
817                                                                                 
818             conn.acquireWriteSemaphore(tlsID);
819             try
820             {
821                 if (!conn.areMessagesComplete())
822                 {
823                     throw new LdapLocalException(ExceptionMessages.OUTSTANDING_OPERATIONS, LdapException.OPERATIONS_ERROR);
824                 }
825                 // Stop reader when response to startTLS request received
826                 conn.stopReaderOnReply(tlsID);
827                                                                                 
828                 // send tls message
829                 LdapResponseQueue queue = SendRequestToServer(startTLS, defSearchCons.TimeLimit, null, null);
830                                                                                 
831                 LdapExtendedResponse response = (LdapExtendedResponse) queue.getResponse();
832                 response.chkResultCode();
833                                                                                 
834                 conn.startTLS();
835             }
836             finally
837             {
838                 //Free this semaphore no matter what exceptions get thrown
839                 conn.startReader();
840                 conn.freeWriteSemaphore(tlsID);
841            }
842             return ;
843         }
844                                                                                 
845         /// <summary> Stops Transport Layer Security(TLS) on the LDAPConnection and reverts
846         /// back to an anonymous state.
847         ///
848         /// @throws LDAPException This can occur for the following reasons: 
849         /// <UL>        
850         /// <LI>StartTLS has not been called before stopTLS</LI>
851         /// <LI>There exists outstanding messages that have not received all
852         /// responses</LI>
853         /// <LI>The sever was not able to support the operation</LI></UL>
854         ///
855         /// <p>Note: The Sun and IBM implementions of JSSE do not currently allow
856         /// stopping TLS on an open Socket.  In order to produce the same results
857         /// this method currently disconnects the socket and reconnects, giving
858         /// the application an anonymous connection to the server, as required
859         /// by StopTLS</p>
860         /// </summary>
861         public virtual void  stopTLS()
862         {
863                                                                                 
864             if (!TLS)
865             {
866                 throw new LdapLocalException(ExceptionMessages.NO_STARTTLS, LdapException.OPERATIONS_ERROR);
867             }
868                                                                                 
869             int semaphoreID = conn.acquireWriteSemaphore();
870             try
871             {
872                 if (!conn.areMessagesComplete())
873                {
874                     throw new LdapLocalException(ExceptionMessages.OUTSTANDING_OPERATIONS, LdapException.OPERATIONS_ERROR);
875                 }
876                 //stopTLS stops and starts the reader thread for us.
877                 conn.stopTLS();
878             }
879             finally
880             {
881                 conn.freeWriteSemaphore(semaphoreID);
882                                                                                 
883                 /* Now that the TLS socket is closed, reset everything.  This next
884                 line is temporary until JSSE is fixed to properly handle TLS stop */
885                 this.Connect(this.Host, this.Port);
886             }
887             return ;
888         }
889                                                                                 
890
891                 //*************************************************************************
892                 // Below are all of the Ldap protocol operation methods
893                 //*************************************************************************
894                 
895                 //*************************************************************************
896                 // abandon methods
897                 //*************************************************************************
898                 
899                 /// <summary> 
900                 /// 
901                 /// Notifies the server not to send additional results associated with
902                 /// this LdapSearchResults object, and discards any results already
903                 /// received.
904                 /// 
905                 /// </summary>
906                 /// <param name="results">  An object returned from a search.
907                 /// 
908                 /// </param>
909                 /// <exception> LdapException A general exception which includes an error
910                 /// message and an Ldap error code.
911                 /// </exception>
912                 public virtual void  Abandon(LdapSearchResults results)
913                 {
914                         Abandon(results, defSearchCons);
915                         return ;
916                 }
917                 
918                 /// <summary> 
919                 /// 
920                 /// Notifies the server not to send additional results associated with
921                 /// this LdapSearchResults object, and discards any results already
922                 /// received.
923                 /// 
924                 /// </summary>
925                 /// <param name="results">  An object returned from a search.
926                 /// 
927                 /// </param>
928                 /// <param name="cons">    The contraints specific to the operation.
929                 /// 
930                 /// </param>
931                 /// <exception> LdapException A general exception which includes an error
932                 /// message and an Ldap error code.
933                 /// </exception>
934                 public virtual void  Abandon(LdapSearchResults results, LdapConstraints cons)
935                 {
936                         results.Abandon();
937                         return ;
938                 }
939                 
940                 /// <summary> 
941                 /// Abandons an asynchronous operation.
942                 /// 
943                 /// </summary>
944                 /// <param name="id">     The ID of the asynchronous operation to abandon. The ID
945                 /// can be obtained from the response queue for the
946                 /// operation.
947                 /// 
948                 /// </param>
949                 /// <exception> LdapException A general exception which includes an error
950                 /// message and an Ldap error code.
951                 /// </exception>
952                 public virtual void  Abandon(int id)
953                 {
954                         Abandon(id, defSearchCons);
955                         return ;
956                 }
957                 
958                 /// <summary>  Abandons an asynchronous operation, using the specified
959                 /// constraints.
960                 /// 
961                 /// </summary>
962                 /// <param name="id">The ID of the asynchronous operation to abandon.
963                 /// The ID can be obtained from the search
964                 /// queue for the operation.
965                 /// 
966                 /// </param>
967                 /// <param name="cons">The contraints specific to the operation.
968                 /// 
969                 /// </param>
970                 /// <exception> LdapException A general exception which includes an error
971                 /// message and an Ldap error code.
972                 /// </exception>
973                 public virtual void  Abandon(int id, LdapConstraints cons)
974                 {
975                         // We need to inform the Message Agent which owns this messageID to
976                         // remove it from the queue.
977                         try
978                         {
979                                 MessageAgent agent = conn.getMessageAgent(id);
980                                 agent.Abandon(id, cons);
981                                 return ;
982                         }
983                         catch (System.FieldAccessException ex)
984                         {
985                                 return ; // Ignore error
986                         }
987                 }
988                 
989                 /// <summary> Abandons all outstanding operations managed by the queue.
990                 /// 
991                 /// All operations in progress, which are managed by the specified queue,
992                 /// are abandoned.
993                 /// 
994                 /// </summary>
995                 /// <param name="queue">    The queue returned from an asynchronous request.
996                 /// All outstanding operations managed by the queue
997                 /// are abandoned, and the queue is emptied.
998                 /// 
999                 /// </param>
1000                 /// <exception> LdapException A general exception which includes an error
1001                 /// message and an Ldap error code.
1002                 /// </exception>
1003                 public virtual void  Abandon(LdapMessageQueue queue)
1004                 {
1005                         Abandon(queue, defSearchCons);
1006                         return ;
1007                 }
1008                 
1009                 /// <summary> Abandons all outstanding operations managed by the queue.
1010                 /// 
1011                 /// All operations in progress, which are managed by the specified
1012                 /// queue, are abandoned.
1013                 /// 
1014                 /// </summary>
1015                 /// <param name="queue">    The queue returned from an asynchronous request.
1016                 /// All outstanding operations managed by the queue
1017                 /// are abandoned, and the queue is emptied.
1018                 /// 
1019                 /// </param>
1020                 /// <param name="cons">    The contraints specific to the operation.
1021                 /// 
1022                 /// </param>
1023                 /// <exception> LdapException A general exception which includes an error
1024                 /// message and an Ldap error code.
1025                 /// </exception>
1026                 public virtual void  Abandon(LdapMessageQueue queue, LdapConstraints cons)
1027                 {
1028                         if (queue != null)
1029                         {
1030                                 MessageAgent agent;
1031                                 if (queue is LdapSearchQueue)
1032                                 {
1033                                         agent = queue.MessageAgent;
1034                                 }
1035                                 else
1036                                 {
1037                                         agent = queue.MessageAgent;
1038                                 }
1039                                 int[] msgIds = agent.MessageIDs;
1040                                 for (int i = 0; i < msgIds.Length; i++)
1041                                 {
1042                                         agent.Abandon(msgIds[i], cons);
1043                                 }
1044                         }
1045                         return ;
1046                 }
1047                 
1048                 //*************************************************************************
1049                 // add methods
1050                 //*************************************************************************
1051                 
1052                 /// <summary> Synchronously adds an entry to the directory.
1053                 /// 
1054                 /// </summary>
1055                 /// <param name="entry">   LdapEntry object specifying the distinguished
1056                 /// name and attributes of the new entry.
1057                 /// 
1058                 /// </param>
1059                 /// <exception> LdapException A general exception which includes an error
1060                 /// message and an Ldap error code.
1061                 /// </exception>
1062                 public virtual void  Add(LdapEntry entry)
1063                 {
1064                         Add(entry, defSearchCons);
1065                         return ;
1066                 }
1067                 
1068                 /// <summary> 
1069                 /// Synchronously adds an entry to the directory, using the specified
1070                 /// constraints.
1071                 /// 
1072                 /// </summary>
1073                 /// <param name="entry">  LdapEntry object specifying the distinguished
1074                 /// name and attributes of the new entry.
1075                 /// 
1076                 /// </param>
1077                 /// <param name="cons">   Constraints specific to the operation.
1078                 /// 
1079                 /// </param>
1080                 /// <exception> LdapException A general exception which includes an error
1081                 /// message and an Ldap error code.
1082                 /// </exception>
1083                 
1084                 public virtual void  Add(LdapEntry entry, LdapConstraints cons)
1085                 {
1086                         LdapResponseQueue queue = Add(entry, null, cons);
1087                         
1088                         // Get a handle to the add response
1089                         LdapResponse addResponse = (LdapResponse) (queue.getResponse());
1090                         
1091                         // Set local copy of responseControls synchronously if there were any
1092                         lock (responseCtlSemaphore)
1093                         {
1094                                 responseCtls = addResponse.Controls;
1095                         }
1096                         chkResultCode(queue, cons, addResponse);
1097                         return ;
1098                 }
1099                 
1100                 /// <summary> Asynchronously adds an entry to the directory.
1101                 /// 
1102                 /// </summary>
1103                 /// <param name="entry">  LdapEntry object specifying the distinguished
1104                 /// name and attributes of the new entry.
1105                 /// 
1106                 /// </param>
1107                 /// <param name="queue">  Handler for messages returned from a server in
1108                 /// response to this request. If it is null, a
1109                 /// queue object is created internally.
1110                 /// 
1111                 /// </param>
1112                 /// <exception> LdapException A general exception which includes an error
1113                 /// message and an Ldap error code.
1114                 /// </exception>
1115                 public virtual LdapResponseQueue Add(LdapEntry entry, LdapResponseQueue queue)
1116                 {
1117                         return Add(entry, queue, defSearchCons);
1118                 }
1119                 
1120                 /// <summary> Asynchronously adds an entry to the directory, using the specified
1121                 /// constraints.
1122                 /// 
1123                 /// </summary>
1124                 /// <param name="entry">  LdapEntry object specifying the distinguished
1125                 /// name and attributes of the new entry.
1126                 /// 
1127                 /// </param>
1128                 /// <param name="queue"> Handler for messages returned from a server in
1129                 /// response to this request. If it is null, a
1130                 /// queue object is created internally.
1131                 /// 
1132                 /// </param>
1133                 /// <param name="cons">  Constraints specific to the operation.
1134                 /// 
1135                 /// </param>
1136                 /// <exception> LdapException A general exception which includes an error
1137                 /// message and an Ldap error code.
1138                 /// </exception>
1139                 public virtual LdapResponseQueue Add(LdapEntry entry, LdapResponseQueue queue, LdapConstraints cons)
1140                 {
1141                         if (cons == null)
1142                                 cons = defSearchCons;
1143                         
1144                         // error check the parameters
1145                         if (entry == null)
1146                         {
1147                                 throw new System.ArgumentException("The LdapEntry parameter" + " cannot be null");
1148                         }
1149                         if ((System.Object) entry.DN == null)
1150                         {
1151                                 throw new System.ArgumentException("The DN value must be present" + " in the LdapEntry object");
1152                         }
1153                         
1154                         LdapMessage msg = new LdapAddRequest(entry, cons.getControls());
1155                         
1156                         return SendRequestToServer(msg, cons.TimeLimit, queue, null);
1157                 }
1158                 
1159                 //*************************************************************************
1160                 // bind methods
1161                 //*************************************************************************
1162                 
1163                 /// <summary> Synchronously authenticates to the Ldap server (that the object is
1164                 /// currently connected to) as an Ldapv3 bind, using the specified name and
1165                 /// password.
1166                 /// 
1167                 /// If the object has been disconnected from an Ldap server,
1168                 /// this method attempts to reconnect to the server. If the object
1169                 /// has already authenticated, the old authentication is discarded.
1170                 /// 
1171                 /// </summary>
1172                 /// <param name="dn">     If non-null and non-empty, specifies that the
1173                 /// connection and all operations through it should
1174                 /// be authenticated with dn as the distinguished
1175                 /// name.
1176                 /// 
1177                 /// </param>
1178                 /// <param name="passwd"> If non-null and non-empty, specifies that the
1179                 /// connection and all operations through it should
1180                 /// be authenticated with dn as the distinguished
1181                 /// name and passwd as password.
1182                 /// 
1183                 /// Note: the application should use care in the use
1184                 /// of String password objects.  These are long lived
1185                 /// objects, and may expose a security risk, especially
1186                 /// in objects that are serialized.  The LdapConnection
1187                 /// keeps no long lived instances of these objects.
1188                 /// 
1189                 /// </param>
1190                 /// <exception> LdapException A general exception which includes an error
1191                 /// message and an Ldap error code.
1192                 /// 
1193                 /// </exception>
1194                 public virtual void  Bind(System.String dn, System.String passwd)
1195                 {
1196                         Bind(dn, passwd, AuthenticationTypes.None);
1197                         return ;
1198                 }
1199                 
1200                 public virtual void  Bind(System.String dn, System.String passwd, AuthenticationTypes authenticationTypes)
1201                 {
1202 #if TARGET_JVM
1203                         if (authenticationTypes != AuthenticationTypes.None &&
1204                                 authenticationTypes != AuthenticationTypes.ServerBind &&
1205                                 authenticationTypes != AuthenticationTypes.Anonymous)
1206                                 BindSecure(dn, passwd, authenticationTypes);
1207                         else
1208 #endif
1209                                 Bind(Ldap_V3, dn, passwd, defSearchCons);               
1210
1211                         return ;
1212                 }
1213                 
1214                 /// <summary> Synchronously authenticates to the Ldap server (that the object is
1215                 /// currently connected to) using the specified name, password,
1216                 /// and Ldap version.
1217                 /// 
1218                 /// If the object has been disconnected from an Ldap server,
1219                 /// this method attempts to reconnect to the server. If the object
1220                 /// has already authenticated, the old authentication is discarded.
1221                 /// 
1222                 /// </summary>
1223                 /// <param name="version"> The Ldap protocol version, use Ldap_V3.
1224                 /// Ldap_V2 is not supported.
1225                 /// 
1226                 /// </param>
1227                 /// <param name="dn">     If non-null and non-empty, specifies that the
1228                 /// connection and all operations through it should
1229                 /// be authenticated with dn as the distinguished
1230                 /// name.
1231                 /// 
1232                 /// </param>
1233                 /// <param name="passwd"> If non-null and non-empty, specifies that the
1234                 /// connection and all operations through it should
1235                 /// be authenticated with dn as the distinguished
1236                 /// name and passwd as password.
1237                 /// 
1238                 /// Note: the application should use care in the use
1239                 /// of String password objects.  These are long lived
1240                 /// objects, and may expose a security risk, especially
1241                 /// in objects that are serialized.  The LdapConnection
1242                 /// keeps no long lived instances of these objects.
1243                 /// 
1244                 /// </param>
1245                 /// <exception> LdapException A general exception which includes an error
1246                 /// message and an Ldap error code.
1247                 /// 
1248                 /// </exception>
1249                 public virtual void  Bind(int version, System.String dn, System.String passwd)
1250                 {
1251                         Bind(version, dn, passwd, defSearchCons);
1252                         return ;
1253                 }
1254                 
1255                 /// <summary> Synchronously authenticates to the Ldap server (that the object is
1256                 /// currently connected to) as an Ldapv3 bind, using the specified name,
1257                 /// password, and constraints.
1258                 /// 
1259                 /// If the object has been disconnected from an Ldap server,
1260                 /// this method attempts to reconnect to the server. If the object
1261                 /// has already authenticated, the old authentication is discarded.
1262                 /// 
1263                 /// </summary>
1264                 /// <param name="dn">     If non-null and non-empty, specifies that the
1265                 /// connection and all operations through it should
1266                 /// be authenticated with dn as the distinguished
1267                 /// name.
1268                 /// 
1269                 /// </param>
1270                 /// <param name="passwd"> If non-null and non-empty, specifies that the
1271                 /// connection and all operations through it should
1272                 /// be authenticated with dn as the distinguished
1273                 /// name and passwd as password.
1274                 /// Note: the application should use care in the use
1275                 /// of String password objects.  These are long lived
1276                 /// objects, and may expose a security risk, especially
1277                 /// in objects that are serialized.  The LdapConnection
1278                 /// keeps no long lived instances of these objects.
1279                 /// 
1280                 /// </param>
1281                 /// <param name="cons">    Constraints specific to the operation.
1282                 /// 
1283                 /// </param>
1284                 /// <exception> LdapException A general exception which includes an error
1285                 /// message and an Ldap error code.
1286                 /// 
1287                 /// </exception>
1288                 public virtual void  Bind(System.String dn, System.String passwd, LdapConstraints cons)
1289                 {
1290                         Bind(Ldap_V3, dn, passwd, cons);
1291                         return ;
1292                 }
1293                 
1294                 /// <summary> Synchronously authenticates to the Ldap server (that the object is
1295                 /// currently connected to) using the specified name, password, Ldap version,
1296                 /// and constraints.
1297                 /// 
1298                 /// If the object has been disconnected from an Ldap server,
1299                 /// this method attempts to reconnect to the server. If the object
1300                 /// has already authenticated, the old authentication is discarded.
1301                 /// 
1302                 /// </summary>
1303                 /// <param name="version"> The Ldap protocol version, use Ldap_V3.
1304                 /// Ldap_V2 is not supported.
1305                 /// 
1306                 /// </param>
1307                 /// <param name="dn">      If non-null and non-empty, specifies that the
1308                 /// connection and all operations through it should
1309                 /// be authenticated with dn as the distinguished
1310                 /// name.
1311                 /// 
1312                 /// </param>
1313                 /// <param name="passwd"> If non-null and non-empty, specifies that the
1314                 /// connection and all operations through it should
1315                 /// be authenticated with dn as the distinguished
1316                 /// name and passwd as password.
1317                 /// 
1318                 /// Note: the application should use care in the use
1319                 /// of String password objects.  These are long lived
1320                 /// objects, and may expose a security risk, especially
1321                 /// in objects that are serialized.  The LdapConnection
1322                 /// keeps no long lived instances of these objects.
1323                 /// 
1324                 /// </param>
1325                 /// <param name="cons">   The constraints specific to the operation.
1326                 /// 
1327                 /// </param>
1328                 /// <exception> LdapException A general exception which includes an error
1329                 /// message and an Ldap error code.
1330                 /// 
1331                 /// </exception>
1332                 public virtual void  Bind(int version, System.String dn, System.String passwd, LdapConstraints cons)
1333                 {
1334                         sbyte[] pw = null;
1335                         if ((System.Object) passwd != null)
1336                         {
1337                                 try
1338                                 {
1339                                         System.Text.Encoding encoder = System.Text.Encoding.GetEncoding("utf-8"); 
1340                                         byte[] ibytes = encoder.GetBytes(passwd);
1341                                         pw=SupportClass.ToSByteArray(ibytes);
1342
1343                                         //                                      pw = passwd.getBytes("UTF8");
1344                                         passwd = null; // Keep no reference to String object
1345                                 }
1346                                 catch (System.IO.IOException ex)
1347                                 {
1348                                         passwd = null; // Keep no reference to String object
1349                                         throw new System.SystemException(ex.ToString());
1350                                 }
1351                         }
1352                         Bind(version, dn, pw, cons);
1353                         return ;
1354                 }
1355                 
1356                 /// <summary> Synchronously authenticates to the Ldap server (that the object is
1357                 /// currently connected to) using the specified name, password,
1358                 /// and Ldap version.
1359                 /// 
1360                 /// If the object has been disconnected from an Ldap server,
1361                 /// this method attempts to reconnect to the server. If the object
1362                 /// has already authenticated, the old authentication is discarded.
1363                 /// 
1364                 /// </summary>
1365                 /// <param name="version"> The version of the Ldap protocol to use
1366                 /// in the bind, use Ldap_V3.  Ldap_V2 is not supported.
1367                 /// 
1368                 /// </param>
1369                 /// <param name="dn">     If non-null and non-empty, specifies that the
1370                 /// connection and all operations through it should
1371                 /// be authenticated with dn as the distinguished
1372                 /// name.
1373                 /// 
1374                 /// </param>
1375                 /// <param name="passwd"> If non-null and non-empty, specifies that the
1376                 /// connection and all operations through it should
1377                 /// be authenticated with dn as the distinguished
1378                 /// name and passwd as password.
1379                 /// 
1380                 /// </param>
1381                 /// <exception> LdapException A general exception which includes an error
1382                 /// message and an Ldap error code.
1383                 /// </exception>
1384                 [CLSCompliantAttribute(false)]
1385                 public virtual void  Bind(int version, System.String dn, sbyte[] passwd)
1386                 {
1387                         Bind(version, dn, passwd, defSearchCons);
1388                         return ;
1389                 }
1390                 
1391                 /// <summary> 
1392                 /// Synchronously authenticates to the Ldap server (that the object is
1393                 /// currently connected to) using the specified name, password, Ldap version,
1394                 /// and constraints.
1395                 /// 
1396                 /// If the object has been disconnected from an Ldap server,
1397                 /// this method attempts to reconnect to the server. If the object
1398                 /// has already authenticated, the old authentication is discarded.
1399                 /// 
1400                 /// </summary>
1401                 /// <param name="version"> The Ldap protocol version, use Ldap_V3.
1402                 /// Ldap_V2 is not supported.
1403                 /// 
1404                 /// </param>
1405                 /// <param name="dn">      If non-null and non-empty, specifies that the
1406                 /// connection and all operations through it should
1407                 /// be authenticated with dn as the distinguished
1408                 /// name.
1409                 /// 
1410                 /// </param>
1411                 /// <param name="passwd"> If non-null and non-empty, specifies that the
1412                 /// connection and all operations through it should
1413                 /// be authenticated with dn as the distinguished
1414                 /// name and passwd as password.
1415                 /// 
1416                 /// </param>
1417                 /// <param name="cons">   The constraints specific to the operation.
1418                 /// 
1419                 /// </param>
1420                 /// <exception> LdapException A general exception which includes an error
1421                 /// message and an Ldap error code.
1422                 /// </exception>
1423                 [CLSCompliantAttribute(false)]
1424                 public virtual void  Bind(int version, System.String dn, sbyte[] passwd, LdapConstraints cons)
1425                 {
1426                         LdapResponseQueue queue = Bind(version, dn, passwd, null, cons, null);
1427                         LdapResponse res = (LdapResponse) queue.getResponse();
1428                         if (res != null)
1429                         {
1430                                 // Set local copy of responseControls synchronously if any
1431                                 lock (responseCtlSemaphore)
1432                                 {
1433                                         responseCtls = res.Controls;
1434                                 }
1435                                 
1436                                 chkResultCode(queue, cons, res);
1437                         }
1438                         return ;
1439                 }
1440                 
1441                 /// <summary> Asynchronously authenticates to the Ldap server (that the object is
1442                 /// currently connected to) using the specified name, password, Ldap
1443                 /// version, and queue.
1444                 /// 
1445                 /// If the object has been disconnected from an Ldap server,
1446                 /// this method attempts to reconnect to the server. If the object
1447                 /// has already authenticated, the old authentication is discarded.
1448                 /// 
1449                 /// 
1450                 /// </summary>
1451                 /// <param name="version"> The Ldap protocol version, use Ldap_V3.
1452                 /// Ldap_V2 is not supported.
1453                 /// 
1454                 /// </param>
1455                 /// <param name="dn">     If non-null and non-empty, specifies that the
1456                 /// connection and all operations through it should
1457                 /// be authenticated with dn as the distinguished
1458                 /// name.
1459                 /// 
1460                 /// </param>
1461                 /// <param name="passwd"> If non-null and non-empty, specifies that the
1462                 /// connection and all operations through it should
1463                 /// be authenticated with dn as the distinguished
1464                 /// name and passwd as password.
1465                 /// 
1466                 /// </param>
1467                 /// <param name="queue">  Handler for messages returned from a server in
1468                 /// response to this request. If it is null, a
1469                 /// queue object is created internally.
1470                 /// 
1471                 /// </param>
1472                 /// <exception> LdapException A general exception which includes an error
1473                 /// message and an Ldap error code.
1474                 /// </exception>
1475                 [CLSCompliantAttribute(false)]
1476                 public virtual LdapResponseQueue Bind(int version, System.String dn, sbyte[] passwd, LdapResponseQueue queue)
1477                 {
1478                         return Bind(version, dn, passwd, queue, defSearchCons, null);
1479                 }
1480                 
1481                 /// <summary> Asynchronously authenticates to the Ldap server (that the object is
1482                 /// currently connected to) using the specified name, password, Ldap
1483                 /// version, queue, and constraints.
1484                 /// 
1485                 /// If the object has been disconnected from an Ldap server,
1486                 /// this method attempts to reconnect to the server. If the object
1487                 /// had already authenticated, the old authentication is discarded.
1488                 /// 
1489                 /// </summary>
1490                 /// <param name="version"> The Ldap protocol version, use Ldap_V3.
1491                 /// Ldap_V2 is not supported.
1492                 /// 
1493                 /// </param>
1494                 /// <param name="dn">     If non-null and non-empty, specifies that the
1495                 /// connection and all operations through it should
1496                 /// be authenticated with dn as the distinguished
1497                 /// name.
1498                 /// 
1499                 /// </param>
1500                 /// <param name="passwd"> If non-null and non-empty, specifies that the
1501                 /// connection and all operations through it should
1502                 /// be authenticated with dn as the distinguished
1503                 /// name and passwd as password.
1504                 /// 
1505                 /// </param>
1506                 /// <param name="queue">  Handler for messages returned from a server in
1507                 /// response to this request. If it is null, a
1508                 /// queue object is created internally.
1509                 /// 
1510                 /// </param>
1511                 /// <param name="cons">     Constraints specific to the operation.
1512                 /// 
1513                 /// </param>
1514                 /// <exception> LdapException A general exception which includes an error
1515                 /// message and an Ldap error code.
1516                 /// </exception>
1517                 [CLSCompliantAttribute(false)]
1518                 public virtual LdapResponseQueue Bind(int version, System.String dn, sbyte[] passwd, LdapResponseQueue queue, LdapConstraints cons, string mech)
1519                 {
1520                         int msgId;
1521                         BindProperties bindProps;
1522                         if (cons == null)
1523                                 cons = defSearchCons;
1524                         
1525                         if ((System.Object) dn == null)
1526                         {
1527                                 dn = "";
1528                         }
1529                         else
1530                         {
1531                                 dn = dn.Trim();
1532                         }
1533                         
1534                         if (passwd == null)
1535                                 passwd = new sbyte[]{};
1536                         
1537                         bool anonymous = false;
1538                         if (passwd.Length == 0)
1539                         {
1540                                 anonymous = true; // anonymous, passwd length zero with simple bind
1541                                 dn = ""; // set to null if anonymous
1542                         }
1543
1544                         LdapMessage msg;
1545 #if TARGET_JVM
1546                         if (mech != null)
1547                                 msg = new LdapBindRequest(version, "", mech, passwd, cons.getControls());
1548                         else
1549 #endif
1550                                 msg = new LdapBindRequest(version, dn, passwd, cons.getControls());
1551                         
1552                         msgId = msg.MessageID;
1553                         bindProps = new BindProperties(version, dn, "simple", anonymous, null, null);
1554                         
1555                         // For bind requests, if not connected, attempt to reconnect
1556                         if (!conn.Connected)
1557                         {
1558                                 if ((System.Object) conn.Host != null)
1559                                 {
1560                                         conn.connect(conn.Host, conn.Port);
1561                                 }
1562                                 else
1563                                 {
1564                                         throw new LdapException(ExceptionMessages.CONNECTION_IMPOSSIBLE, LdapException.CONNECT_ERROR, null);
1565                                 }
1566                         }
1567                         
1568 #if TARGET_JVM
1569                         // stopping reader to enable stream replace after secure binding is complete, see Connection.ReplaceStreams()
1570                         if (mech != null)
1571                         {
1572                                 if (conn.BindSemIdClear) {
1573                                         // need to acquire a semaphore only if bindSemId is clear
1574                                         // because if we receive SASL_BIND_IN_PROGRESS the semaphore is not
1575                                         // released when the response is queued
1576                                         conn.acquireWriteSemaphore(msgId);
1577                                         conn.BindSemId = msgId;
1578                                 }
1579                                 conn.stopReaderOnReply(msgId);
1580                         }
1581                         else
1582 #endif
1583                         // The semaphore is released when the bind response is queued.
1584                         conn.acquireWriteSemaphore(msgId);
1585                         
1586                         return SendRequestToServer(msg, cons.TimeLimit, queue, bindProps);
1587                 }
1588
1589 #if TARGET_JVM
1590                 private void BindSecure(System.String username, System.String password, AuthenticationTypes authenticationTypes)
1591                 {
1592                         if ((authenticationTypes & AuthenticationTypes.Secure) != 0) {                  
1593                                 LoginContext loginContext = null;
1594                                 try {                                   
1595                                         if (username != null && password != null) {
1596                                                 AuthenticationCallbackHandler callbackHandler = new AuthenticationCallbackHandler (username,password);
1597                                                 loginContext = new LoginContext (SecurityAppName, callbackHandler);
1598                                         }
1599                                         else
1600                                                 loginContext = new LoginContext (SecurityAppName);
1601
1602                                         loginContext.login ();
1603                                 }
1604                                 catch (Exception e) {
1605                                         throw new LdapException ("Failed to create login security context", 80, "", e);
1606                                 }
1607
1608                                 Krb5Helper krb5Helper = null;
1609                                 try {
1610                                         krb5Helper = new Krb5Helper ("ldap@" + conn.Host, username, loginContext.getSubject (), authenticationTypes, SecurityMech);
1611                                 }
1612                                 finally {
1613                                         loginContext.logout();
1614                                 }
1615                                 sbyte [] token = krb5Helper.ExchangeTokens (Krb5Helper.EmptyToken);
1616
1617                                 for (;;) {
1618                                         LdapResponseQueue queue = Bind(LdapConnection.Ldap_V3, username, token, null, null, AuthenticationMech);
1619                                         LdapResponse res = (LdapResponse) queue.getResponse ();
1620                                         if (res.ResultCode != LdapException.SASL_BIND_IN_PROGRESS &&
1621                                                 res.ResultCode != LdapException.SUCCESS) {
1622                                                 krb5Helper.Dispose();
1623                                                 throw new LdapException(ExceptionMessages.CONNECTION_ERROR, res.ResultCode, res.ErrorMessage);
1624                                         }
1625                                         Asn1OctetString serverSaslCreds = ((RfcBindResponse)res.Asn1Object.Response).ServerSaslCreds;
1626                                         token = serverSaslCreds != null ? serverSaslCreds.byteValue () : null;
1627
1628                                         token = krb5Helper.ExchangeTokens(token == null ? Krb5Helper.EmptyToken : token);
1629
1630                                         if (res.ResultCode != LdapException.SASL_BIND_IN_PROGRESS)
1631                                                 break;
1632
1633                                         conn.ReplaceStreams (conn.InputStream,conn.OutputStream);
1634                                 }
1635
1636                                 System.IO.Stream inStream = conn.InputStream;
1637                                 System.IO.Stream newIn = new SecureStream (inStream, krb5Helper);
1638                                 System.IO.Stream outStream = conn.OutputStream;
1639                                 System.IO.Stream newOut = new SecureStream (outStream, krb5Helper);
1640                                 conn.ReplaceStreams (newIn,newOut);
1641                         }               
1642                 }
1643
1644                 static string SecurityMech
1645                 {
1646                         get {
1647                                 string securityMech = null;
1648                                         NameValueCollection config = (NameValueCollection) ConfigurationSettings.GetConfig ("System.DirectoryServices/Settings");
1649                                         if (config != null) 
1650                                                 securityMech = config ["securitymech"];
1651
1652                                         if (securityMech == null) 
1653                                                 throw new ArgumentException("Security mechanism id not found in application settings");
1654
1655                                 return securityMech;
1656                         }
1657                 }
1658
1659                 static string SecurityAppName
1660                 {
1661                         get {
1662                                 string securityAppName = null; 
1663                                         NameValueCollection config = (NameValueCollection) ConfigurationSettings.GetConfig ("System.DirectoryServices/Settings");
1664                                         if (config != null) 
1665                                                 securityAppName = config ["securityappname"];
1666
1667                                         if (securityAppName == null) 
1668                                                 throw new ArgumentException("Application section name not found in application settings");
1669
1670                                 return securityAppName;
1671                         }
1672                 }
1673
1674                 static string AuthenticationMech
1675                 {
1676                         get {
1677                                 string authenticationMech = null;
1678                                 NameValueCollection config = (NameValueCollection) ConfigurationSettings.GetConfig ("System.DirectoryServices/Settings");
1679                                 if (config != null) 
1680                                         authenticationMech = config ["authenticationmech"];
1681
1682                                 if (authenticationMech == null) 
1683                                         throw new ArgumentException("Authentication mechanism not found in application settings");
1684
1685                                 return authenticationMech;
1686                         }
1687                 }
1688 #endif
1689                 
1690                 //*************************************************************************
1691                 // compare methods
1692                 //*************************************************************************
1693                 
1694                 /// <summary> 
1695                 /// Synchronously checks to see if an entry contains an attribute
1696                 /// with a specified value.
1697                 /// 
1698                 /// </summary>
1699                 /// <param name="dn">     The distinguished name of the entry to use in the
1700                 /// comparison.
1701                 /// 
1702                 /// </param>
1703                 /// <param name="attr">   The attribute to compare against the entry. The
1704                 /// method checks to see if the entry has an
1705                 /// attribute with the same name and value as this
1706                 /// attribute.
1707                 /// 
1708                 /// </param>
1709                 /// <returns> True if the entry has the value,
1710                 /// and false if the entry does not
1711                 /// have the value or the attribute.
1712                 /// 
1713                 /// </returns>
1714                 /// <exception> LdapException A general exception which includes an error
1715                 /// message and an Ldap error code.
1716                 /// </exception>
1717                 public virtual bool Compare(System.String dn, LdapAttribute attr)
1718                 {
1719                         return Compare(dn, attr, defSearchCons);
1720                 }
1721                 
1722                 /// <summary> 
1723                 /// Synchronously checks to see if an entry contains an attribute with a
1724                 /// specified value, using the specified constraints.
1725                 /// 
1726                 /// </summary>
1727                 /// <param name="dn">     The distinguished name of the entry to use in the
1728                 /// comparison.
1729                 /// 
1730                 /// </param>
1731                 /// <param name="attr">   The attribute to compare against the entry. The
1732                 /// method checks to see if the entry has an
1733                 /// attribute with the same name and value as this
1734                 /// attribute.
1735                 /// 
1736                 /// </param>
1737                 /// <param name="cons">   Constraints specific to the operation.
1738                 /// 
1739                 /// </param>
1740                 /// <returns> True if the entry has the value,
1741                 /// and false if the entry does not
1742                 /// have the value or the attribute.
1743                 /// 
1744                 /// </returns>
1745                 /// <exception> LdapException A general exception which includes an error
1746                 /// message and an Ldap error code.
1747                 /// </exception>
1748                 public virtual bool Compare(System.String dn, LdapAttribute attr, LdapConstraints cons)
1749                 {
1750                         bool ret = false;
1751                         
1752                         LdapResponseQueue queue = Compare(dn, attr, null, cons);
1753                         
1754                         LdapResponse res = (LdapResponse) queue.getResponse();
1755                         
1756                         // Set local copy of responseControls synchronously - if there were any
1757                         lock (responseCtlSemaphore)
1758                         {
1759                                 responseCtls = res.Controls;
1760                         }
1761                         
1762                         if (res.ResultCode == LdapException.COMPARE_TRUE)
1763                         {
1764                                 ret = true;
1765                         }
1766                         else if (res.ResultCode == LdapException.COMPARE_FALSE)
1767                         {
1768                                 ret = false;
1769                         }
1770                         else
1771                         {
1772                                 chkResultCode(queue, cons, res);
1773                         }
1774                         return ret;
1775                 }
1776                 
1777                 /// <summary> Asynchronously compares an attribute value with one in the directory,
1778                 /// using the specified queue.
1779                 /// 
1780                 /// Please note that a successful completion of this command results in
1781                 /// one of two status codes: LdapException.COMPARE_TRUE if the entry
1782                 /// has the value, and LdapException.COMPARE_FALSE if the entry
1783                 /// does not have the value or the attribute.
1784                 /// 
1785                 /// </summary>
1786                 /// <param name="dn">     The distinguished name of the entry containing an
1787                 /// attribute to compare.
1788                 /// 
1789                 /// </param>
1790                 /// <param name="attr">   An attribute to compare.
1791                 /// 
1792                 /// </param>
1793                 /// <param name="queue">  The queue for messages returned from a server in
1794                 /// response to this request. If it is null, a
1795                 /// queue object is created internally.
1796                 /// 
1797                 /// </param>
1798                 /// <exception> LdapException A general exception which includes an error
1799                 /// message and an Ldap error code.
1800                 /// 
1801                 /// </exception>
1802                 /// <seealso cref="LdapException.COMPARE_TRUE">
1803                 /// </seealso>
1804                 /// <seealso cref="LdapException.COMPARE_FALSE">
1805                 /// </seealso>
1806                 public virtual LdapResponseQueue Compare(System.String dn, LdapAttribute attr, LdapResponseQueue queue)
1807                 {
1808                         return Compare(dn, attr, queue, defSearchCons);
1809                 }
1810                 
1811                 /// <summary> Asynchronously compares an attribute value with one in the directory,
1812                 /// using the specified queue and contraints.
1813                 /// 
1814                 /// Please note that a successful completion of this command results in
1815                 /// one of two status codes: LdapException.COMPARE_TRUE if the entry
1816                 /// has the value, and LdapException.COMPARE_FALSE if the entry
1817                 /// does not have the value or the attribute.
1818                 /// 
1819                 /// </summary>
1820                 /// <param name="dn">     The distinguished name of the entry containing an
1821                 /// attribute to compare.
1822                 /// 
1823                 /// </param>
1824                 /// <param name="attr">   An attribute to compare.
1825                 /// 
1826                 /// </param>
1827                 /// <param name="queue">    Handler for messages returned from a server in
1828                 /// response to this request. If it is null, a
1829                 /// queue object is created internally.
1830                 /// 
1831                 /// </param>
1832                 /// <param name="cons">     Constraints specific to the operation.
1833                 /// 
1834                 /// </param>
1835                 /// <exception> LdapException A general exception which includes an error
1836                 /// message and an Ldap error code.
1837                 /// 
1838                 /// </exception>
1839                 /// <seealso cref="LdapException.COMPARE_TRUE">
1840                 /// </seealso>
1841                 /// <seealso cref="LdapException.COMPARE_FALSE">
1842                 /// </seealso>
1843                 public virtual LdapResponseQueue Compare(System.String dn, LdapAttribute attr, LdapResponseQueue queue, LdapConstraints cons)
1844                 {
1845                         if (attr.size() != 1)
1846                         {
1847                                 throw new System.ArgumentException("compare: Exactly one value " + "must be present in the LdapAttribute");
1848                         }
1849                         
1850                         if ((System.Object) dn == null)
1851                         {
1852                                 // Invalid parameter
1853                                 throw new System.ArgumentException("compare: DN cannot be null");
1854                         }
1855                         
1856                         if (cons == null)
1857                                 cons = defSearchCons;
1858                         
1859                         LdapMessage msg = new LdapCompareRequest(dn, attr.Name, attr.ByteValue, cons.getControls());
1860                         
1861                         return SendRequestToServer(msg, cons.TimeLimit, queue, null);
1862                 }
1863                 
1864                 //*************************************************************************
1865                 // connect methods
1866                 //*************************************************************************
1867                 
1868                 /// <summary> 
1869                 /// Connects to the specified host and port.
1870                 /// 
1871                 /// If this LdapConnection object represents an open connection, the
1872                 /// connection is closed first before the new connection is opened.
1873                 /// At this point, there is no authentication, and any operations are
1874                 /// conducted as an anonymous client.
1875                 /// 
1876                 ///  When more than one host name is specified, each host is contacted
1877                 /// in turn until a connection can be established.
1878                 /// 
1879                 /// </summary>
1880                 /// <param name="host">A host name or a dotted string representing the IP address
1881                 /// of a host running an Ldap server. It may also
1882                 /// contain a list of host names, space-delimited. Each host
1883                 /// name can include a trailing colon and port number.
1884                 /// 
1885                 /// </param>
1886                 /// <param name="port">The TCP or UDP port number to connect to or contact.
1887                 /// The default Ldap port is 389. The port parameter is
1888                 /// ignored for any host hame which includes a colon and
1889                 /// port number.
1890                 /// 
1891                 /// </param>
1892                 /// <exception> LdapException A general exception which includes an error
1893                 /// message and an Ldap error code.
1894                 /// 
1895                 /// </exception>
1896                 public virtual void  Connect(System.String host, int port)
1897                 {
1898                         // connect doesn't affect other clones
1899                         // If not a clone, destroys old connection.
1900                         // Step through the space-delimited list
1901                         SupportClass.Tokenizer hostList = new SupportClass.Tokenizer(host, " ");
1902                         System.String address = null;
1903                         
1904                         int specifiedPort;
1905                         int colonIndex; //after the colon is the port
1906                         while (hostList.HasMoreTokens())
1907                         {
1908                                 try
1909                                 {
1910                                         specifiedPort = port;
1911                                         address = hostList.NextToken();
1912                                         colonIndex = address.IndexOf((System.Char) ':');
1913                                         if (colonIndex != - 1 && colonIndex + 1 != address.Length)
1914                                         {
1915                                                 //parse Port out of address
1916                                                 try
1917                                                 {
1918                                                         specifiedPort = System.Int32.Parse(address.Substring(colonIndex + 1));
1919                                                         address = address.Substring(0, (colonIndex) - (0));
1920                                                 }
1921                                                 catch (System.Exception e)
1922                                                 {
1923                                                         throw new System.ArgumentException(ExceptionMessages.INVALID_ADDRESS);
1924                                                 }
1925                                         }
1926                                         // This may return a different conn object
1927                                         // Disassociate this clone with the underlying connection.
1928                                         conn = conn.destroyClone(true);
1929                                         conn.connect(address, specifiedPort);
1930                                         break;
1931                                 }
1932                                 catch (LdapException LE)
1933                                 {
1934                                         if (!hostList.HasMoreTokens())
1935                                                 throw LE;
1936                                 }
1937                         }
1938                         return ;
1939                 }
1940                 
1941                 //*************************************************************************
1942                 // delete methods
1943                 //*************************************************************************
1944                 
1945                 /// <summary> 
1946                 /// Synchronously deletes the entry with the specified distinguished name
1947                 /// from the directory.
1948                 /// 
1949                 /// Note: A Delete operation will not remove an entry that contains
1950                 /// subordinate entries, nor will it dereference alias entries. 
1951                 /// 
1952                 /// </summary>
1953                 /// <param name="dn">     The distinguished name of the entry to delete.
1954                 /// 
1955                 /// </param>
1956                 /// <exception> LdapException A general exception which includes an error
1957                 /// message and an Ldap error code.
1958                 /// </exception>
1959                 public virtual void  Delete(System.String dn)
1960                 {
1961                         Delete(dn, defSearchCons);
1962                         return ;
1963                 }
1964                 
1965                 
1966                 /// <summary> Synchronously deletes the entry with the specified distinguished name
1967                 /// from the directory, using the specified constraints.
1968                 /// 
1969                 /// Note: A Delete operation will not remove an entry that contains
1970                 /// subordinate entries, nor will it dereference alias entries. 
1971                 /// 
1972                 /// </summary>
1973                 /// <param name="dn">     The distinguished name of the entry to delete.
1974                 /// 
1975                 /// </param>
1976                 /// <param name="cons">   Constraints specific to the operation.
1977                 /// 
1978                 /// </param>
1979                 /// <exception> LdapException A general exception which includes an error
1980                 /// message and an Ldap error code.
1981                 /// </exception>
1982                 public virtual void  Delete(System.String dn, LdapConstraints cons)
1983                 {
1984                         LdapResponseQueue queue = Delete(dn, null, cons);
1985                         
1986                         // Get a handle to the delete response
1987                         LdapResponse deleteResponse = (LdapResponse) (queue.getResponse());
1988                         
1989                         // Set local copy of responseControls synchronously - if there were any
1990                         lock (responseCtlSemaphore)
1991                         {
1992                                 responseCtls = deleteResponse.Controls;
1993                         }
1994                         chkResultCode(queue, cons, deleteResponse);
1995                         return ;
1996                 }
1997                 
1998                 /// <summary> Asynchronously deletes the entry with the specified distinguished name
1999                 /// from the directory and returns the results to the specified queue.
2000                 /// 
2001                 /// Note: A Delete operation will not remove an entry that contains
2002                 /// subordinate entries, nor will it dereference alias entries. 
2003                 /// 
2004                 /// </summary>
2005                 /// <param name="dn">     The distinguished name of the entry to modify.
2006                 /// 
2007                 /// </param>
2008                 /// <param name="queue">    The queue for messages returned from a server in
2009                 /// response to this request. If it is null, a
2010                 /// queue object is created internally.
2011                 /// 
2012                 /// </param>
2013                 /// <exception> LdapException A general exception which includes an error
2014                 /// message and an Ldap error code.
2015                 /// 
2016                 /// </exception>
2017                 public virtual LdapResponseQueue Delete(System.String dn, LdapResponseQueue queue)
2018                 {
2019                         return Delete(dn, queue, defSearchCons);
2020                 }
2021                 
2022                 /// <summary> Asynchronously deletes the entry with the specified distinguished name
2023                 /// from the directory, using the specified contraints and queue.
2024                 /// 
2025                 /// Note: A Delete operation will not remove an entry that contains
2026                 /// subordinate entries, nor will it dereference alias entries. 
2027                 /// 
2028                 /// </summary>
2029                 /// <param name="dn">     The distinguished name of the entry to delete.
2030                 /// 
2031                 /// </param>
2032                 /// <param name="queue">     The queue for messages returned from a server in
2033                 /// response to this request. If it is null, a
2034                 /// queue object is created internally.
2035                 /// 
2036                 /// </param>
2037                 /// <param name="cons">   The constraints specific to the operation.
2038                 /// 
2039                 /// </param>
2040                 /// <exception> LdapException A general exception which includes an error
2041                 /// message and an Ldap error code.
2042                 /// 
2043                 /// </exception>
2044                 public virtual LdapResponseQueue Delete(System.String dn, LdapResponseQueue queue, LdapConstraints cons)
2045                 {
2046                         if ((System.Object) dn == null)
2047                         {
2048                                 // Invalid DN parameter
2049                                 throw new System.ArgumentException(ExceptionMessages.DN_PARAM_ERROR);
2050                         }
2051                         
2052                         if (cons == null)
2053                                 cons = defSearchCons;
2054                         
2055                         LdapMessage msg = new LdapDeleteRequest(dn, cons.getControls());
2056                         
2057                         return SendRequestToServer(msg, cons.TimeLimit, queue, null);
2058                 }
2059                 
2060                 //*************************************************************************
2061                 // disconnect method
2062                 //*************************************************************************
2063                 
2064                 /// <summary> 
2065                 /// Synchronously disconnects from the Ldap server.
2066                 /// 
2067                 /// Before the object can perform Ldap operations again, it must
2068                 /// reconnect to the server by calling connect.
2069                 /// 
2070                 /// The disconnect method abandons any outstanding requests, issues an
2071                 /// unbind request to the server, and then closes the socket.
2072                 /// 
2073                 /// </summary>
2074                 /// <exception> LdapException A general exception which includes an error
2075                 /// message and an Ldap error code.
2076                 /// 
2077                 /// </exception>
2078                 public virtual void  Disconnect()
2079                 {
2080                         // disconnect from API call
2081                         Disconnect(defSearchCons, true);
2082                         return ;
2083                 }
2084                 
2085                 /// <summary> Synchronously disconnects from the Ldap server.
2086                 /// 
2087                 /// Before the object can perform Ldap operations again, it must
2088                 /// reconnect to the server by calling connect.
2089                 /// 
2090                 /// The disconnect method abandons any outstanding requests, issues an
2091                 /// unbind request to the server, and then closes the socket.
2092                 /// 
2093                 /// </summary>
2094                 /// <param name="cons">LDPConstraints to be set with the unbind request
2095                 /// 
2096                 /// </param>
2097                 /// <exception> LdapException A general exception which includes an error
2098                 /// message and an Ldap error code.
2099                 /// </exception>
2100                 public virtual void  Disconnect(LdapConstraints cons)
2101                 {
2102                         // disconnect from API call
2103                         Disconnect(cons, true);
2104                         return ;
2105                 }
2106                 
2107                 /// <summary> Synchronously disconnect from the server
2108                 /// 
2109                 /// </summary>
2110                 /// <param name="how">true if application call disconnect API, false if finalize.
2111                 /// </param>
2112                 private void  Disconnect(LdapConstraints cons, bool how)
2113                 {
2114                         // disconnect doesn't affect other clones
2115                         // If not a clone, distroys connection
2116                         conn = conn.destroyClone(how);
2117                         return ;
2118                 }
2119                 
2120                 //*************************************************************************
2121                 // extendedOperation methods
2122                 //*************************************************************************
2123                 
2124                 /// <summary> Provides a synchronous means to access extended, non-mandatory
2125                 /// operations offered by a particular Ldapv3 compliant server.
2126                 /// 
2127                 /// </summary>
2128                 /// <param name="op"> The object which contains (1) an identifier of an extended
2129                 /// operation which should be recognized by the particular Ldap
2130                 /// server this client is connected to and (2)
2131                 /// an operation-specific sequence of octet strings
2132                 /// or BER-encoded values.
2133                 /// 
2134                 /// </param>
2135                 /// <returns> An operation-specific object, containing an ID and either an octet
2136                 /// string or BER-encoded values.
2137                 /// 
2138                 /// </returns>
2139                 /// <exception> LdapException A general exception which includes an error
2140                 /// message and an Ldap error code.
2141                 /// </exception>
2142                 public virtual LdapExtendedResponse ExtendedOperation(LdapExtendedOperation op)
2143                 {
2144                         return ExtendedOperation(op, defSearchCons);
2145                 }
2146                 
2147                 /*
2148                 *  Synchronous Ldap extended request with SearchConstraints
2149                 */
2150                 
2151                 /// <summary> 
2152                 /// Provides a synchronous means to access extended, non-mandatory
2153                 /// operations offered by a particular Ldapv3 compliant server.
2154                 /// 
2155                 /// </summary>
2156                 /// <param name="op"> The object which contains (1) an identifier of an extended
2157                 /// operation which should be recognized by the particular Ldap
2158                 /// server this client is connected to and (2) an
2159                 /// operation-specific sequence of octet strings
2160                 /// or BER-encoded values.
2161                 /// 
2162                 /// </param>
2163                 /// <param name="cons">The constraints specific to the operation.
2164                 /// 
2165                 /// </param>
2166                 /// <returns> An operation-specific object, containing an ID and either an
2167                 /// octet string or BER-encoded values.
2168                 /// 
2169                 /// </returns>
2170                 /// <exception> LdapException A general exception which includes an error
2171                 /// message and an Ldap error code.
2172                 /// </exception>
2173                 
2174                 public virtual LdapExtendedResponse ExtendedOperation(LdapExtendedOperation op, LdapConstraints cons)
2175                 {
2176                         
2177                         // Call asynchronous API and get back handler to reponse queue
2178                         LdapResponseQueue queue = ExtendedOperation(op, cons, null);
2179                         LdapExtendedResponse response = (LdapExtendedResponse) queue.getResponse();
2180                         
2181                         // Set local copy of responseControls synchronously - if there were any
2182                         lock (responseCtlSemaphore)
2183                         {
2184                                 responseCtls = response.Controls;
2185                         }
2186                         
2187                         chkResultCode(queue, cons, response);
2188                         return response;
2189                 }
2190                 
2191                 
2192                 /*
2193                 * Asynchronous Ldap extended request
2194                 */
2195                 
2196                 /// <summary> Provides an asynchronous means to access extended, non-mandatory
2197                 /// operations offered by a particular Ldapv3 compliant server.
2198                 /// 
2199                 /// </summary>
2200                 /// <param name="op"> The object which contains (1) an identifier of an extended
2201                 /// operation which should be recognized by the particular Ldap
2202                 /// server this client is connected to and (2) an
2203                 /// operation-specific sequence of octet strings
2204                 /// or BER-encoded values.
2205                 /// 
2206                 /// </param>
2207                 /// <param name="queue">    The queue for messages returned from a server in
2208                 /// response to this request. If it is null, a queue
2209                 /// object is created internally.
2210                 /// 
2211                 /// </param>
2212                 /// <returns> An operation-specific object, containing an ID and either an octet
2213                 /// string or BER-encoded values.
2214                 /// 
2215                 /// </returns>
2216                 /// <exception> LdapException A general exception which includes an error
2217                 /// message and an Ldap error code.
2218                 /// </exception>
2219                 
2220                 public virtual LdapResponseQueue ExtendedOperation(LdapExtendedOperation op, LdapResponseQueue queue)
2221                 {
2222                         
2223                         return ExtendedOperation(op, defSearchCons, queue);
2224                 }
2225                 
2226                 
2227                 /*
2228                 *  Asynchronous Ldap extended request with SearchConstraints
2229                 */
2230                 
2231                 /// <summary> Provides an asynchronous means to access extended, non-mandatory
2232                 /// operations offered by a particular Ldapv3 compliant server.
2233                 /// 
2234                 /// </summary>
2235                 /// <param name="op"> The object which contains (1) an identifier of an extended
2236                 /// operation which should be recognized by the particular Ldap
2237                 /// server this client is connected to and (2) an operation-
2238                 /// specific sequence of octet strings or BER-encoded values.
2239                 /// 
2240                 /// </param>
2241                 /// <param name="queue">    The queue for messages returned from a server in
2242                 /// response to this request. If it is null, a queue
2243                 /// object is created internally.
2244                 /// 
2245                 /// </param>
2246                 /// <param name="cons">     The constraints specific to this operation.
2247                 /// 
2248                 /// </param>
2249                 /// <returns> An operation-specific object, containing an ID and either an
2250                 /// octet string or BER-encoded values.
2251                 /// 
2252                 /// </returns>
2253                 /// <exception> LdapException A general exception which includes an error
2254                 /// message and an Ldap error code.
2255                 /// </exception>
2256                 
2257                 public virtual LdapResponseQueue ExtendedOperation(LdapExtendedOperation op, LdapConstraints cons, LdapResponseQueue queue)
2258                 {
2259                         // Use default constraints if none-specified
2260                         if (cons == null)
2261                                 cons = defSearchCons;
2262                         LdapMessage msg = MakeExtendedOperation(op, cons);
2263                         return SendRequestToServer(msg, cons.TimeLimit, queue, null);
2264                 }
2265                 
2266                 /// <summary> Formulates the extended operation, constraints into an
2267                 /// LdapMessage and returns the LdapMessage.  This is used by
2268                 /// extendedOperation and startTLS which needs the LdapMessage to
2269                 /// get the MessageID.
2270                 /// </summary>
2271                 protected internal virtual LdapMessage MakeExtendedOperation(LdapExtendedOperation op, LdapConstraints cons)
2272                 {
2273                         // Use default constraints if none-specified
2274                         if (cons == null)
2275                                 cons = defSearchCons;
2276                         
2277                         // error check the parameters
2278                         if ((System.Object) op.getID() == null)
2279                         {
2280                                 // Invalid extended operation parameter, no OID specified
2281                                 throw new System.ArgumentException(ExceptionMessages.OP_PARAM_ERROR);
2282                         }
2283                         
2284                         return new LdapExtendedRequest(op, cons.getControls());
2285                 }
2286                 
2287                 //*************************************************************************
2288                 // getResponseControls method
2289                 //*************************************************************************
2290                 
2291                 //*************************************************************************
2292                 // modify methods
2293                 //*************************************************************************
2294                 
2295                 /// <summary> Synchronously makes a single change to an existing entry in the
2296                 /// directory.
2297                 /// 
2298                 /// For example, this modify method changes the value of an attribute,
2299                 /// adds a new attribute value, or removes an existing attribute value. 
2300                 /// 
2301                 /// The LdapModification object specifies both the change to be made and
2302                 /// the LdapAttribute value to be changed.
2303                 /// 
2304                 /// If the request fails with {@link LdapException.CONNECT_ERROR},
2305                 /// it is indeterminate whether or not the server made the modification.
2306                 /// 
2307                 /// </summary>
2308                 /// <param name="dn">    The distinguished name of the entry to modify.
2309                 /// 
2310                 /// </param>
2311                 /// <param name="mod">   A single change to be made to the entry.
2312                 /// 
2313                 /// </param>
2314                 /// <exception> LdapException A general exception which includes an error
2315                 /// message and an Ldap error code.
2316                 /// </exception>
2317                 public virtual void  Modify(System.String dn, LdapModification mod)
2318                 {
2319                         Modify(dn, mod, defSearchCons);
2320                         return ;
2321                 }
2322                 
2323                 /// <summary> 
2324                 /// Synchronously makes a single change to an existing entry in the
2325                 /// directory, using the specified constraints.
2326                 /// 
2327                 /// For example, this modify method changes the value of an attribute,
2328                 /// adds a new attribute value, or removes an existing attribute value.
2329                 /// 
2330                 /// The LdapModification object specifies both the change to be
2331                 /// made and the LdapAttribute value to be changed.
2332                 /// 
2333                 /// If the request fails with {@link LdapException.CONNECT_ERROR},
2334                 /// it is indeterminate whether or not the server made the modification.
2335                 /// 
2336                 /// </summary>
2337                 /// <param name="dn">      The distinguished name of the entry to modify.
2338                 /// 
2339                 /// </param>
2340                 /// <param name="mod">     A single change to be made to the entry.
2341                 /// 
2342                 /// </param>
2343                 /// <param name="cons">    The constraints specific to the operation.
2344                 /// 
2345                 /// </param>
2346                 /// <exception> LdapException A general exception which includes an error
2347                 /// message and an Ldap error code.
2348                 /// </exception>
2349                 public virtual void  Modify(System.String dn, LdapModification mod, LdapConstraints cons)
2350                 {
2351                         LdapModification[] mods = new LdapModification[1];
2352                         mods[0] = mod;
2353                         Modify(dn, mods, cons);
2354                         return ;
2355                 }
2356                 
2357                 /// <summary> 
2358                 /// Synchronously makes a set of changes to an existing entry in the
2359                 /// directory.
2360                 /// 
2361                 /// For example, this modify method changes attribute values, adds
2362                 /// new attribute values, or removes existing attribute values.
2363                 /// 
2364                 /// Because the server applies all changes in an LdapModification array
2365                 /// atomically, the application can expect that no changes
2366                 /// have been performed if an error is returned.
2367                 /// If the request fails with {@link LdapException.CONNECT_ERROR},
2368                 /// it is indeterminate whether or not the server made the modifications.
2369                 /// 
2370                 /// </summary>
2371                 /// <param name="dn">    Distinguished name of the entry to modify.
2372                 /// 
2373                 /// </param>
2374                 /// <param name="mods">  The changes to be made to the entry.
2375                 /// 
2376                 /// </param>
2377                 /// <exception> LdapException A general exception which includes an error
2378                 /// message and an Ldap error code.
2379                 /// </exception>
2380                 public virtual void  Modify(System.String dn, LdapModification[] mods)
2381                 {
2382                         Modify(dn, mods, defSearchCons);
2383                         return ;
2384                 }
2385                 
2386                 /// <summary> Synchronously makes a set of changes to an existing entry in the
2387                 /// directory, using the specified constraints.
2388                 /// 
2389                 /// For example, this modify method changes attribute values, adds new
2390                 /// attribute values, or removes existing attribute values.
2391                 /// 
2392                 /// Because the server applies all changes in an LdapModification array
2393                 /// atomically, the application can expect that no changes
2394                 /// have been performed if an error is returned.
2395                 /// If the request fails with {@link LdapException.CONNECT_ERROR},
2396                 /// it is indeterminate whether or not the server made the modifications.
2397                 /// 
2398                 /// </summary>
2399                 /// <param name="dn">     The distinguished name of the entry to modify.
2400                 /// 
2401                 /// </param>
2402                 /// <param name="mods">   The changes to be made to the entry.
2403                 /// 
2404                 /// </param>
2405                 /// <param name="cons">   The constraints specific to the operation.
2406                 /// 
2407                 /// </param>
2408                 /// <exception> LdapException A general exception which includes an
2409                 /// error message and an Ldap error code.
2410                 /// </exception>
2411                 public virtual void  Modify(System.String dn, LdapModification[] mods, LdapConstraints cons)
2412                 {
2413                         LdapResponseQueue queue = Modify(dn, mods, null, cons);
2414                         
2415                         // Get a handle to the modify response
2416                         LdapResponse modifyResponse = (LdapResponse) (queue.getResponse());
2417                         
2418                         // Set local copy of responseControls synchronously - if there were any
2419                         lock (responseCtlSemaphore)
2420                         {
2421                                 responseCtls = modifyResponse.Controls;
2422                         }
2423                         
2424                         chkResultCode(queue, cons, modifyResponse);
2425                         
2426                         return ;
2427                 }
2428                 
2429                 /// <summary> Asynchronously makes a single change to an existing entry in the
2430                 /// directory.
2431                 /// 
2432                 /// For example, this modify method can change the value of an attribute,
2433                 /// add a new attribute value, or remove an existing attribute value.
2434                 /// 
2435                 /// The LdapModification object specifies both the change to be made and
2436                 /// the LdapAttribute value to be changed.
2437                 /// 
2438                 /// If the request fails with {@link LdapException.CONNECT_ERROR},
2439                 /// it is indeterminate whether or not the server made the modification.
2440                 /// 
2441                 /// </summary>
2442                 /// <param name="dn">        Distinguished name of the entry to modify.
2443                 /// 
2444                 /// </param>
2445                 /// <param name="mod">       A single change to be made to the entry.
2446                 /// 
2447                 /// </param>
2448                 /// <param name="queue">     Handler for messages returned from a server in
2449                 /// response to this request. If it is null, a
2450                 /// queue object is created internally.
2451                 /// 
2452                 /// </param>
2453                 /// <exception> LdapException A general exception which includes an error
2454                 /// message and an Ldap error code.
2455                 /// </exception>
2456                 public virtual LdapResponseQueue Modify(System.String dn, LdapModification mod, LdapResponseQueue queue)
2457                 {
2458                         return Modify(dn, mod, queue, defSearchCons);
2459                 }
2460                 
2461                 /// <summary> Asynchronously makes a single change to an existing entry in the
2462                 /// directory, using the specified constraints and queue.
2463                 /// 
2464                 /// For example, this modify method can change the value of an attribute,
2465                 /// add a new attribute value, or remove an existing attribute value.
2466                 /// 
2467                 /// The LdapModification object specifies both the change to be made
2468                 /// and the LdapAttribute value to be changed.
2469                 /// 
2470                 /// If the request fails with {@link LdapException.CONNECT_ERROR},
2471                 /// it is indeterminate whether or not the server made the modification.
2472                 /// 
2473                 /// </summary>
2474                 /// <param name="dn">         Distinguished name of the entry to modify.
2475                 /// 
2476                 /// </param>
2477                 /// <param name="mod">        A single change to be made to the entry.
2478                 /// 
2479                 /// </param>
2480                 /// <param name="queue">      Handler for messages returned from a server in
2481                 /// response to this request. If it is null, a
2482                 /// queue object is created internally.
2483                 /// 
2484                 /// </param>
2485                 /// <param name="cons">       Constraints specific to the operation.
2486                 /// 
2487                 /// </param>
2488                 /// <exception> LdapException A general exception which includes an error
2489                 /// message and an Ldap error code.
2490                 /// </exception>
2491                 public virtual LdapResponseQueue Modify(System.String dn, LdapModification mod, LdapResponseQueue queue, LdapConstraints cons)
2492                 {
2493                         LdapModification[] mods = new LdapModification[1];
2494                         mods[0] = mod;
2495                         return Modify(dn, mods, queue, cons);
2496                 }
2497                 
2498                 /// <summary> Asynchronously makes a set of changes to an existing entry in the
2499                 /// directory.
2500                 /// 
2501                 /// For example, this modify method can change attribute values, add new
2502                 /// attribute values, or remove existing attribute values.
2503                 /// 
2504                 /// Because the server applies all changes in an LdapModification array
2505                 /// atomically, the application can expect that no changes
2506                 /// have been performed if an error is returned.
2507                 /// If the request fails with {@link LdapException.CONNECT_ERROR},
2508                 /// it is indeterminate whether or not the server made the modifications.
2509                 /// 
2510                 /// </summary>
2511                 /// <param name="dn">        The distinguished name of the entry to modify.
2512                 /// 
2513                 /// </param>
2514                 /// <param name="mods">      The changes to be made to the entry.
2515                 /// 
2516                 /// </param>
2517                 /// <param name="queue">     The queue for messages returned from a server in
2518                 /// response to this request. If it is null, a
2519                 /// queue object is created internally.
2520                 /// 
2521                 /// </param>
2522                 /// <exception> LdapException A general exception which includes an error
2523                 /// message and an Ldap error code.
2524                 /// </exception>
2525                 public virtual LdapResponseQueue Modify(System.String dn, LdapModification[] mods, LdapResponseQueue queue)
2526                 {
2527                         return Modify(dn, mods, queue, defSearchCons);
2528                 }
2529                 
2530                 /// <summary> Asynchronously makes a set of changes to an existing entry in the
2531                 /// directory, using the specified constraints and queue.
2532                 /// 
2533                 /// For example, this modify method can change attribute values, add new
2534                 /// attribute values, or remove existing attribute values.
2535                 /// 
2536                 /// Because the server applies all changes in an LdapModification array
2537                 /// atomically, the application can expect that no changes
2538                 /// have been performed if an error is returned.
2539                 /// If the request fails with {@link LdapException.CONNECT_ERROR},
2540                 /// it is indeterminate whether or not the server made the modifications.
2541                 /// 
2542                 /// </summary>
2543                 /// <param name="dn">        The distinguished name of the entry to modify.
2544                 /// 
2545                 /// </param>
2546                 /// <param name="mods">      The changes to be made to the entry.
2547                 /// 
2548                 /// </param>
2549                 /// <param name="queue">     The queue for messages returned from a server in
2550                 /// response to this request. If it is null, a
2551                 /// queue object is created internally.
2552                 /// 
2553                 /// </param>
2554                 /// <param name="cons">      Constraints specific to the operation.
2555                 /// 
2556                 /// </param>
2557                 /// <exception> LdapException A general exception which includes an error
2558                 /// message and an Ldap error code.
2559                 /// </exception>
2560                 public virtual LdapResponseQueue Modify(System.String dn, LdapModification[] mods, LdapResponseQueue queue, LdapConstraints cons)
2561                 {
2562                         if ((System.Object) dn == null)
2563                         {
2564                                 // Invalid DN parameter
2565                                 throw new System.ArgumentException(ExceptionMessages.DN_PARAM_ERROR);
2566                         }
2567                         
2568                         if (cons == null)
2569                                 cons = defSearchCons;
2570                         
2571                         LdapMessage msg = new LdapModifyRequest(dn, mods, cons.getControls());
2572                         
2573                         return SendRequestToServer(msg, cons.TimeLimit, queue, null);
2574                 }
2575                 
2576                 //*************************************************************************
2577                 // read methods
2578                 //*************************************************************************
2579                 
2580                 /// <summary> Synchronously reads the entry for the specified distiguished name (DN)
2581                 /// and retrieves all attributes for the entry.
2582                 /// 
2583                 /// </summary>
2584                 /// <param name="dn">       The distinguished name of the entry to retrieve.
2585                 /// 
2586                 /// </param>
2587                 /// <returns> the LdapEntry read from the server.
2588                 /// 
2589                 /// </returns>
2590                 /// <exception> LdapException if the object was not found
2591                 /// </exception>
2592                 public virtual LdapEntry Read(System.String dn)
2593                 {
2594                         return Read(dn, defSearchCons);
2595                 }
2596                 
2597                 
2598                 /// <summary> 
2599                 /// Synchronously reads the entry for the specified distiguished name (DN),
2600                 /// using the specified constraints, and retrieves all attributes for the
2601                 /// entry.
2602                 /// 
2603                 /// </summary>
2604                 /// <param name="dn">        The distinguished name of the entry to retrieve.
2605                 /// 
2606                 /// </param>
2607                 /// <param name="cons">      The constraints specific to the operation.
2608                 /// 
2609                 /// </param>
2610                 /// <returns> the LdapEntry read from the server
2611                 /// 
2612                 /// </returns>
2613                 /// <exception> LdapException if the object was not found
2614                 /// </exception>
2615                 public virtual LdapEntry Read(System.String dn, LdapSearchConstraints cons)
2616                 {
2617                         return Read(dn, null, cons);
2618                 }
2619                 
2620                 /// <summary> 
2621                 /// Synchronously reads the entry for the specified distinguished name (DN)
2622                 /// and retrieves only the specified attributes from the entry.
2623                 /// 
2624                 /// </summary>
2625                 /// <param name="dn">        The distinguished name of the entry to retrieve.
2626                 /// 
2627                 /// </param>
2628                 /// <param name="attrs">     The names of the attributes to retrieve.
2629                 /// 
2630                 /// </param>
2631                 /// <returns> the LdapEntry read from the server
2632                 /// 
2633                 /// </returns>
2634                 /// <exception> LdapException if the object was not found
2635                 /// </exception>
2636                 public virtual LdapEntry Read(System.String dn, System.String[] attrs)
2637                 {
2638                         return Read(dn, attrs, defSearchCons);
2639                 }
2640                 
2641                 /// <summary> Synchronously reads the entry for the specified distinguished name (DN),
2642                 /// using the specified constraints, and retrieves only the specified
2643                 /// attributes from the entry.
2644                 /// 
2645                 /// </summary>
2646                 /// <param name="dn">      The distinguished name of the entry to retrieve.
2647                 /// 
2648                 /// </param>
2649                 /// <param name="attrs">   The names of the attributes to retrieve.
2650                 /// 
2651                 /// </param>
2652                 /// <param name="cons">    The constraints specific to the operation.
2653                 /// 
2654                 /// </param>
2655                 /// <returns> the LdapEntry read from the server
2656                 /// 
2657                 /// </returns>
2658                 /// <exception> LdapException if the object was not found
2659                 /// </exception>
2660                 public virtual LdapEntry Read(System.String dn, System.String[] attrs, LdapSearchConstraints cons)
2661                 {
2662                         LdapSearchResults sr = Search(dn, SCOPE_BASE, null, attrs, false, cons);
2663                         
2664                         LdapEntry ret = null;
2665                         if (sr.hasMore())
2666                         {
2667                                 ret = sr.next();
2668                                 if (sr.hasMore())
2669                                 {
2670                                         // "Read response is ambiguous, multiple entries returned"
2671                                         throw new LdapLocalException(ExceptionMessages.READ_MULTIPLE, LdapException.AMBIGUOUS_RESPONSE);
2672                                 }
2673                         }
2674                         return ret;
2675                 }
2676                 
2677                 /// <summary> Synchronously reads the entry specified by the Ldap URL.
2678                 /// 
2679                 /// When this read method is called, a new connection is created
2680                 /// automatically, using the host and port specified in the URL. After
2681                 /// finding the entry, the method closes the connection (in other words,
2682                 /// it disconnects from the Ldap server).
2683                 /// 
2684                 /// If the URL specifies a filter and scope, they are not used. Of the
2685                 /// information specified in the URL, this method only uses the Ldap host
2686                 /// name and port number, the base distinguished name (DN), and the list
2687                 /// of attributes to return.
2688                 /// 
2689                 /// </summary>
2690                 /// <param name="toGet">          Ldap URL specifying the entry to read.
2691                 /// 
2692                 /// </param>
2693                 /// <returns> The entry specified by the base DN.
2694                 /// 
2695                 /// </returns>
2696                 /// <exception> LdapException if the object was not found
2697                 /// </exception>
2698                 public static LdapEntry Read(LdapUrl toGet)
2699                 {
2700                         LdapConnection lconn = new LdapConnection();
2701                         lconn.Connect(toGet.Host, toGet.Port);
2702                         LdapEntry toReturn = lconn.Read(toGet.getDN(), toGet.AttributeArray);
2703                         lconn.Disconnect();
2704                         return toReturn;
2705                 }
2706                 
2707                 /// <summary> Synchronously reads the entry specified by the Ldap URL, using the
2708                 /// specified constraints.
2709                 /// 
2710                 /// When this method is called, a new connection is created
2711                 /// automatically, using the host and port specified in the URL. After
2712                 /// finding the entry, the method closes the connection (in other words,
2713                 /// it disconnects from the Ldap server).
2714                 /// 
2715                 /// If the URL specifies a filter and scope, they are not used. Of the
2716                 /// information specified in the URL, this method only uses the Ldap host
2717                 /// name and port number, the base distinguished name (DN), and the list
2718                 /// of attributes to return.
2719                 /// 
2720                 /// </summary>
2721                 /// <returns> The entry specified by the base DN.
2722                 /// 
2723                 /// </returns>
2724                 /// <param name="toGet">      Ldap URL specifying the entry to read.
2725                 /// 
2726                 /// </param>
2727                 /// <param name="cons">      Constraints specific to the operation.
2728                 /// 
2729                 /// </param>
2730                 /// <exception> LdapException if the object was not found
2731                 /// </exception>
2732                 public static LdapEntry Read(LdapUrl toGet, LdapSearchConstraints cons)
2733                 {
2734                         LdapConnection lconn = new LdapConnection();
2735                         lconn.Connect(toGet.Host, toGet.Port);
2736                         LdapEntry toReturn = lconn.Read(toGet.getDN(), toGet.AttributeArray, cons);
2737                         lconn.Disconnect();
2738                         return toReturn;
2739                 }
2740                 
2741                 //*************************************************************************
2742                 // rename methods
2743                 //*************************************************************************
2744                 
2745                 /// <summary> 
2746                 /// Synchronously renames an existing entry in the directory.
2747                 /// 
2748                 /// </summary>
2749                 /// <param name="dn">      The current distinguished name of the entry.
2750                 /// 
2751                 /// </param>
2752                 /// <param name="newRdn">  The new relative distinguished name for the entry.
2753                 /// 
2754                 /// </param>
2755                 /// <param name="deleteOldRdn">  If true, the old name is not retained as an
2756                 /// attribute value. If false, the old name is
2757                 /// retained as an attribute value.
2758                 /// 
2759                 /// </param>
2760                 /// <exception> LdapException A general exception which includes an error
2761                 /// message and an Ldap error code.
2762                 /// </exception>
2763                 public virtual void  Rename(System.String dn, System.String newRdn, bool deleteOldRdn)
2764                 {
2765                         Rename(dn, newRdn, deleteOldRdn, defSearchCons);
2766                         return ;
2767                 }
2768                 
2769                 /// <summary> 
2770                 /// Synchronously renames an existing entry in the directory, using the
2771                 /// specified constraints.
2772                 /// 
2773                 /// </summary>
2774                 /// <param name="dn">            The current distinguished name of the entry.
2775                 /// 
2776                 /// </param>
2777                 /// <param name="newRdn">        The new relative distinguished name for the entry.
2778                 /// 
2779                 /// </param>
2780                 /// <param name="deleteOldRdn">  If true, the old name is not retained as an
2781                 /// attribute value. If false, the old name is
2782                 /// retained as an attribute value.
2783                 /// 
2784                 /// </param>
2785                 /// <param name="cons">          The constraints specific to the operation.
2786                 /// 
2787                 /// </param>
2788                 /// <exception> LdapException A general exception which includes an error
2789                 /// message and an Ldap error code.
2790                 /// </exception>
2791                 public virtual void  Rename(System.String dn, System.String newRdn, bool deleteOldRdn, LdapConstraints cons)
2792                 {
2793                         // null for newParentdn means that this is originating as an Ldapv2 call
2794                         Rename(dn, newRdn, null, deleteOldRdn, cons);
2795                         return ;
2796                 }
2797                 
2798                 /// <summary> Synchronously renames an existing entry in the directory, possibly
2799                 /// repositioning the entry in the directory tree.
2800                 /// 
2801                 /// </summary>
2802                 /// <param name="dn">            The current distinguished name of the entry.
2803                 /// 
2804                 /// </param>
2805                 /// <param name="newRdn">        The new relative distinguished name for the entry.
2806                 /// 
2807                 /// </param>
2808                 /// <param name="newParentdn">   The distinguished name of an existing entry which
2809                 /// is to be the new parent of the entry.
2810                 /// 
2811                 /// </param>
2812                 /// <param name="deleteOldRdn">  If true, the old name is not retained as an
2813                 /// attribute value. If false, the old name is
2814                 /// retained as an attribute value.
2815                 /// 
2816                 /// </param>
2817                 /// <exception> LdapException A general exception which includes an error
2818                 /// message and an Ldap error code.
2819                 /// </exception>
2820                 public virtual void  Rename(System.String dn, System.String newRdn, System.String newParentdn, bool deleteOldRdn)
2821                 {
2822                         Rename(dn, newRdn, newParentdn, deleteOldRdn, defSearchCons);
2823                         return ;
2824                 }
2825                 
2826                 /// <summary> 
2827                 /// Synchronously renames an existing entry in the directory, using the
2828                 /// specified constraints and possibly repositioning the entry in the
2829                 /// directory tree.
2830                 /// 
2831                 /// </summary>
2832                 /// <param name="dn">            The current distinguished name of the entry.
2833                 /// 
2834                 /// </param>
2835                 /// <param name="newRdn">        The new relative distinguished name for the entry.
2836                 /// 
2837                 /// </param>
2838                 /// <param name="newParentdn">   The distinguished name of an existing entry which
2839                 /// is to be the new parent of the entry.
2840                 /// 
2841                 /// </param>
2842                 /// <param name="deleteOldRdn">  If true, the old name is not retained as an
2843                 /// attribute value. If false, the old name is
2844                 /// retained as an attribute value.
2845                 /// 
2846                 /// </param>
2847                 /// <param name="cons">          The constraints specific to the operation.
2848                 /// 
2849                 /// </param>
2850                 /// <exception> LdapException A general exception which includes an error
2851                 /// message and an Ldap error code.
2852                 /// </exception>
2853                 public virtual void  Rename(System.String dn, System.String newRdn, System.String newParentdn, bool deleteOldRdn, LdapConstraints cons)
2854                 {
2855                         LdapResponseQueue queue = Rename(dn, newRdn, newParentdn, deleteOldRdn, null, cons);
2856                         
2857                         // Get a handle to the rename response
2858                         LdapResponse renameResponse = (LdapResponse) (queue.getResponse());
2859                         
2860                         // Set local copy of responseControls synchronously - if there were any
2861                         lock (responseCtlSemaphore)
2862                         {
2863                                 responseCtls = renameResponse.Controls;
2864                         }
2865                         
2866                         chkResultCode(queue, cons, renameResponse);
2867                         return ;
2868                 }
2869                 
2870                 /*
2871                 * rename
2872                 */
2873                 
2874                 /// <summary> Asynchronously renames an existing entry in the directory.
2875                 /// 
2876                 /// </summary>
2877                 /// <param name="dn">            The current distinguished name of the entry.
2878                 /// 
2879                 /// </param>
2880                 /// <param name="newRdn">        The new relative distinguished name for the entry.
2881                 /// 
2882                 /// </param>
2883                 /// <param name="deleteOldRdn">  If true, the old name is not retained as an
2884                 /// attribute value. If false, the old name is
2885                 /// retained as an attribute value.
2886                 /// 
2887                 /// </param>
2888                 /// <param name="queue">         The queue for messages returned from a server in
2889                 /// response to this request. If it is null, a
2890                 /// queue object is created internally.
2891                 /// 
2892                 /// </param>
2893                 /// <exception> LdapException A general exception which includes an error
2894                 /// message and an Ldap error code.
2895                 /// </exception>
2896                 public virtual LdapResponseQueue Rename(System.String dn, System.String newRdn, bool deleteOldRdn, LdapResponseQueue queue)
2897                 {
2898                         return Rename(dn, newRdn, deleteOldRdn, queue, defSearchCons);
2899                 }
2900                 
2901                 /// <summary> Asynchronously renames an existing entry in the directory, using the
2902                 /// specified constraints.
2903                 /// 
2904                 /// </summary>
2905                 /// <param name="dn">            The current distinguished name of the entry.
2906                 /// 
2907                 /// </param>
2908                 /// <param name="newRdn">        The new relative distinguished name for the entry.
2909                 /// 
2910                 /// </param>
2911                 /// <param name="deleteOldRdn">  If true, the old name is not retained as an
2912                 /// attribute value. If false, the old name is
2913                 /// retained as an attribute value.
2914                 /// 
2915                 /// </param>
2916                 /// <param name="queue">         The queue for messages returned from a server in
2917                 /// response to this request. If it is null, a
2918                 /// queue object is created internally.
2919                 /// 
2920                 /// </param>
2921                 /// <param name="cons">          The constraints specific to the operation.
2922                 /// 
2923                 /// </param>
2924                 /// <exception> LdapException A general exception which includes an error
2925                 /// message and an Ldap error code.
2926                 /// </exception>
2927                 public virtual LdapResponseQueue Rename(System.String dn, System.String newRdn, bool deleteOldRdn, LdapResponseQueue queue, LdapConstraints cons)
2928                 {
2929                         return Rename(dn, newRdn, null, deleteOldRdn, queue, cons);
2930                 }
2931                 
2932                 /// <summary> Asynchronously renames an existing entry in the directory, possibly
2933                 /// repositioning the entry in the directory.
2934                 /// 
2935                 /// </summary>
2936                 /// <param name="dn">            The current distinguished name of the entry.
2937                 /// 
2938                 /// </param>
2939                 /// <param name="newRdn">        The new relative distinguished name for the entry.
2940                 /// 
2941                 /// </param>
2942                 /// <param name="newParentdn">   The distinguished name of an existing entry which
2943                 /// is to be the new parent of the entry.
2944                 /// 
2945                 /// </param>
2946                 /// <param name="deleteOldRdn">  If true, the old name is not retained as an
2947                 /// attribute value. If false, the old name is
2948                 /// retained as an attribute value.
2949                 /// 
2950                 /// </param>
2951                 /// <param name="queue">         The queue for messages returned from a server in
2952                 /// response to this request. If it is null, a
2953                 /// queue object is created internally.
2954                 /// 
2955                 /// </param>
2956                 /// <exception> LdapException A general exception which includes an error
2957                 /// message and an Ldap error code.
2958                 /// </exception>
2959                 public virtual LdapResponseQueue Rename(System.String dn, System.String newRdn, System.String newParentdn, bool deleteOldRdn, LdapResponseQueue queue)
2960                 {
2961                         return Rename(dn, newRdn, newParentdn, deleteOldRdn, queue, defSearchCons);
2962                 }
2963                 
2964                 /// <summary> Asynchronously renames an existing entry in the directory, using the
2965                 /// specified constraints and possibily repositioning the entry in the
2966                 /// directory.
2967                 /// 
2968                 /// </summary>
2969                 /// <param name="dn">            The current distinguished name of the entry.
2970                 /// 
2971                 /// </param>
2972                 /// <param name="newRdn">        The new relative distinguished name for the entry.
2973                 /// 
2974                 /// </param>
2975                 /// <param name="newParentdn">   The distinguished name of an existing entry which
2976                 /// is to be the new parent of the entry.
2977                 /// 
2978                 /// </param>
2979                 /// <param name="deleteOldRdn">  If true, the old name is not retained as an
2980                 /// attribute value. If false, the old name is
2981                 /// retained as an attribute value.
2982                 /// 
2983                 /// </param>
2984                 /// <param name="queue">         The queue for messages returned from a server in
2985                 /// response to this request. If it is null, a
2986                 /// queue object is created internally.
2987                 /// 
2988                 /// </param>
2989                 /// <param name="cons">          The constraints specific to the operation.
2990                 /// 
2991                 /// </param>
2992                 /// <exception> LdapException A general exception which includes an error
2993                 /// message and an Ldap error code.
2994                 /// </exception>
2995                 public virtual LdapResponseQueue Rename(System.String dn, System.String newRdn, System.String newParentdn, bool deleteOldRdn, LdapResponseQueue queue, LdapConstraints cons)
2996                 {
2997                         if ((System.Object) dn == null || (System.Object) newRdn == null)
2998                         {
2999                                 // Invalid DN or RDN parameter
3000                                 throw new System.ArgumentException(ExceptionMessages.RDN_PARAM_ERROR);
3001                         }
3002                         
3003                         if (cons == null)
3004                                 cons = defSearchCons;
3005                         
3006                         LdapMessage msg = new LdapModifyDNRequest(dn, newRdn, newParentdn, deleteOldRdn, cons.getControls());
3007                         
3008                         return SendRequestToServer(msg, cons.TimeLimit, queue, null);
3009                 }
3010                 
3011                 //*************************************************************************
3012                 // search methods
3013                 //*************************************************************************
3014                 
3015                 /// <summary> 
3016                 /// Synchronously performs the search specified by the parameters.
3017                 /// 
3018                 /// </summary>
3019                 /// <param name="base">          The base distinguished name to search from.
3020                 /// 
3021                 /// </param>
3022                 /// <param name="scope">         The scope of the entries to search. The following
3023                 /// are the valid options:
3024                 /// <ul>
3025                 /// <li>SCOPE_BASE - searches only the base DN</li>
3026                 /// 
3027                 /// <li>SCOPE_ONE - searches only entries under the base DN</li>
3028                 /// 
3029                 /// <li>SCOPE_SUB - searches the base DN and all entries
3030                 /// within its subtree</li>
3031                 /// </ul>
3032                 /// </param>
3033                 /// <param name="filter">        Search filter specifying the search criteria.
3034                 /// 
3035                 /// </param>
3036                 /// <param name="attrs">         Names of attributes to retrieve.
3037                 /// 
3038                 /// </param>
3039                 /// <param name="typesOnly">     If true, returns the names but not the values of
3040                 /// the attributes found. If false, returns the
3041                 /// names and values for attributes found.
3042                 /// 
3043                 /// </param>
3044                 /// <exception> LdapException A general exception which includes an error
3045                 /// message and an Ldap error code.
3046                 /// </exception>
3047                 public virtual LdapSearchResults Search(System.String base_Renamed, int scope, System.String filter, System.String[] attrs, bool typesOnly)
3048                 {
3049                         return Search(base_Renamed, scope, filter, attrs, typesOnly, defSearchCons);
3050                 }
3051                 
3052                 /// <summary> 
3053                 /// Synchronously performs the search specified by the parameters,
3054                 /// using the specified search constraints (such as the
3055                 /// maximum number of entries to find or the maximum time to wait for
3056                 /// search results).
3057                 /// 
3058                 /// As part of the search constraints, the method allows specifying
3059                 /// whether or not the results are to be delivered all at once or in
3060                 /// smaller batches. If specified that the results are to be delivered in
3061                 /// smaller batches, each iteration blocks only until the next batch of
3062                 /// results is returned.
3063                 /// 
3064                 /// </summary>
3065                 /// <param name="base">          The base distinguished name to search from.
3066                 /// 
3067                 /// </param>
3068                 /// <param name="scope">         The scope of the entries to search. The following
3069                 /// are the valid options:
3070                 /// <ul>
3071                 /// <li>SCOPE_BASE - searches only the base DN</li>
3072                 /// 
3073                 /// <li>SCOPE_ONE - searches only entries under the base DN</li>
3074                 /// 
3075                 /// <li>SCOPE_SUB - searches the base DN and all entries
3076                 /// within its subtree</li>
3077                 /// </ul>
3078                 /// </param>
3079                 /// <param name="filter">        The search filter specifying the search criteria.
3080                 /// 
3081                 /// </param>
3082                 /// <param name="attrs">         The names of attributes to retrieve.
3083                 /// 
3084                 /// </param>
3085                 /// <param name="typesOnly">     If true, returns the names but not the values of
3086                 /// the attributes found.  If false, returns the
3087                 /// names and values for attributes found.
3088                 /// 
3089                 /// </param>
3090                 /// <param name="cons">          The constraints specific to the search.
3091                 /// 
3092                 /// </param>
3093                 /// <exception> LdapException A general exception which includes an error
3094                 /// message and an Ldap error code.
3095                 /// </exception>
3096                 public virtual LdapSearchResults Search(System.String base_Renamed, int scope, System.String filter, System.String[] attrs, bool typesOnly, LdapSearchConstraints cons)
3097                 {
3098                         LdapSearchQueue queue = Search(base_Renamed, scope, filter, attrs, typesOnly, null, cons);
3099                         
3100                         if (cons == null)
3101                                 cons = defSearchCons;
3102                         return new LdapSearchResults(this, queue, cons);
3103                 }
3104                 
3105                 /// <summary> Asynchronously performs the search specified by the parameters.
3106                 /// 
3107                 /// </summary>
3108                 /// <param name="base">          The base distinguished name to search from.
3109                 /// 
3110                 /// </param>
3111                 /// <param name="scope">         The scope of the entries to search. The following
3112                 /// are the valid options:
3113                 /// <ul>
3114                 /// <li>SCOPE_BASE - searches only the base DN</li>
3115                 /// 
3116                 /// <li>SCOPE_ONE - searches only entries under the base DN</li>
3117                 /// 
3118                 /// <li>SCOPE_SUB - searches the base DN and all entries
3119                 /// within its subtree</li>
3120                 /// </ul>
3121                 /// </param>
3122                 /// <param name="filter">        Search filter specifying the search criteria.
3123                 /// 
3124                 /// </param>
3125                 /// <param name="attrs">         Names of attributes to retrieve.
3126                 /// 
3127                 /// </param>
3128                 /// <param name="typesOnly">     If true, returns the names but not the values of
3129                 /// the attributes found.  If false, returns the
3130                 /// names and values for attributes found.
3131                 /// 
3132                 /// </param>
3133                 /// <param name="queue">         Handler for messages returned from a server in
3134                 /// response to this request. If it is null, a
3135                 /// queue object is created internally.
3136                 /// 
3137                 /// </param>
3138                 /// <exception> LdapException A general exception which includes an error
3139                 /// message and an Ldap error code.
3140                 /// </exception>
3141                 public virtual LdapSearchQueue Search(System.String base_Renamed, int scope, System.String filter, System.String[] attrs, bool typesOnly, LdapSearchQueue queue)
3142                 {
3143                         return Search(base_Renamed, scope, filter, attrs, typesOnly, queue, defSearchCons);
3144                 }
3145                 
3146                 /// <summary> Asynchronously performs the search specified by the parameters,
3147                 /// also allowing specification of constraints for the search (such
3148                 /// as the maximum number of entries to find or the maximum time to
3149                 /// wait for search results).
3150                 /// 
3151                 /// </summary>
3152                 /// <param name="base">          The base distinguished name to search from.
3153                 /// 
3154                 /// </param>
3155                 /// <param name="scope">         The scope of the entries to search. The following
3156                 /// are the valid options:
3157                 /// <ul>
3158                 /// <li>SCOPE_BASE - searches only the base DN</li>
3159                 /// 
3160                 /// <li>SCOPE_ONE - searches only entries under the base DN</li>
3161                 /// 
3162                 /// <li>SCOPE_SUB - searches the base DN and all entries
3163                 /// within its subtree</li>
3164                 /// </ul>
3165                 /// </param>
3166                 /// <param name="filter">        The search filter specifying the search criteria.
3167                 /// 
3168                 /// </param>
3169                 /// <param name="attrs">         The names of attributes to retrieve.
3170                 /// 
3171                 /// </param>
3172                 /// <param name="typesOnly">     If true, returns the names but not the values of
3173                 /// the attributes found.  If false, returns the
3174                 /// names and values for attributes found.
3175                 /// 
3176                 /// </param>
3177                 /// <param name="queue">         The queue for messages returned from a server in
3178                 /// response to this request. If it is null, a
3179                 /// queue object is created internally.
3180                 /// 
3181                 /// </param>
3182                 /// <param name="cons">          The constraints specific to the search.
3183                 /// 
3184                 /// </param>
3185                 /// <exception> LdapException A general exception which includes an error
3186                 /// message and an Ldap error code.
3187                 /// </exception>
3188                 public virtual LdapSearchQueue Search(System.String base_Renamed, int scope, System.String filter, System.String[] attrs, bool typesOnly, LdapSearchQueue queue, LdapSearchConstraints cons)
3189                 {
3190                         if ((System.Object) filter == null)
3191                         {
3192                                 filter = "objectclass=*";
3193                         }
3194                         if (cons == null)
3195                                 cons = defSearchCons;
3196                         
3197                         LdapMessage msg = new LdapSearchRequest(base_Renamed, scope, filter, attrs, cons.Dereference, cons.MaxResults, cons.ServerTimeLimit, typesOnly, cons.getControls());
3198                         MessageAgent agent;
3199                         LdapSearchQueue myqueue = queue;
3200                         if (myqueue == null)
3201                         {
3202                                 agent = new MessageAgent();
3203                                 myqueue = new LdapSearchQueue(agent);
3204                         }
3205                         else
3206                         {
3207                                 agent = queue.MessageAgent;
3208                         }
3209                         
3210                         try
3211                         {
3212                                 agent.sendMessage(conn, msg, cons.TimeLimit, myqueue, null);
3213                         }
3214                         catch (LdapException lex)
3215                         {
3216                                 throw lex;
3217                         }
3218                         return myqueue;
3219                 }
3220                 
3221                 /*
3222                 * Ldap URL search
3223                 */
3224                 
3225                 /// <summary> Synchronously performs the search specified by the Ldap URL, returning
3226                 /// an enumerable LdapSearchResults object.
3227                 /// 
3228                 /// </summary>
3229                 /// <param name="toGet">The Ldap URL specifying the entry to read.
3230                 /// 
3231                 /// </param>
3232                 /// <exception> LdapException A general exception which includes an error
3233                 /// message and an Ldap error code.
3234                 /// </exception>
3235                 public static LdapSearchResults Search(LdapUrl toGet)
3236                 {
3237                         // Get a clone of default search constraints, method alters batchSize
3238                         return Search(toGet, null);
3239                 }
3240                 
3241                 /*
3242                 * Ldap URL search
3243                 */
3244                 
3245                 /// <summary> Synchronously perfoms the search specified by the Ldap URL, using
3246                 /// the specified search constraints (such as the maximum number of
3247                 /// entries to find or the maximum time to wait for search results).
3248                 /// 
3249                 /// When this method is called, a new connection is created
3250                 /// automatically, using the host and port specified in the URL. After
3251                 /// all search results have been received from the server, the method
3252                 /// closes the connection (in other words, it disconnects from the Ldap
3253                 /// server).
3254                 /// 
3255                 /// As part of the search constraints, a choice can be made as to whether
3256                 /// to have the results delivered all at once or in smaller batches. If
3257                 /// the results are to be delivered in smaller batches, each iteration
3258                 /// blocks only until the next batch of results is returned.
3259                 /// 
3260                 /// 
3261                 /// </summary>
3262                 /// <param name="toGet">         Ldap URL specifying the entry to read.
3263                 /// 
3264                 /// </param>
3265                 /// <param name="cons">          The constraints specific to the search.
3266                 /// 
3267                 /// </param>
3268                 /// <exception> LdapException A general exception which includes an error
3269                 /// message and an Ldap error code.
3270                 /// </exception>
3271                 public static LdapSearchResults Search(LdapUrl toGet, LdapSearchConstraints cons)
3272                 {
3273                         LdapConnection lconn = new LdapConnection();
3274                         lconn.Connect(toGet.Host, toGet.Port);
3275                         if (cons == null)
3276                         {
3277                                 // This is a clone, so we already have our own copy
3278                                 cons = lconn.SearchConstraints;
3279                         }
3280                         else
3281                         {
3282                                 // get our own copy of user's constraints because we modify it
3283                                 cons = (LdapSearchConstraints) cons.Clone();
3284                         }
3285                         cons.BatchSize = 0; // Must wait until all results arrive
3286                         LdapSearchResults toReturn = lconn.Search(toGet.getDN(), toGet.Scope, toGet.Filter, toGet.AttributeArray, false, cons);
3287                         lconn.Disconnect();
3288                         return toReturn;
3289                 }
3290                 
3291                 /// <summary> Sends an Ldap request to a directory server.
3292                 /// 
3293                 /// The specified the Ldap request is sent to the directory server
3294                 /// associated with this connection using default constraints. An Ldap
3295                 /// request object is a subclass {@link LdapMessage} with the operation
3296                 /// type set to one of the request types. You can build a request by using
3297                 /// the request classes found in this package
3298                 /// 
3299                 /// You should note that, since Ldap requests sent to the server
3300                 /// using sendRequest are asynchronous, automatic referral following
3301                 /// does not apply to these requests.
3302                 /// 
3303                 /// </summary>
3304                 /// <param name="request">The Ldap request to send to the directory server.
3305                 /// </param>
3306                 /// <param name="queue">   The queue for messages returned from a server in
3307                 /// response to this request. If it is null, a
3308                 /// queue object is created internally.
3309                 /// </param>
3310                 /// <exception>     LdapException A general exception which includes an error
3311                 /// message and an Ldap error code.
3312                 /// 
3313                 /// </exception>
3314                 /// <seealso cref="LdapMessage.Type">
3315                 /// </seealso>
3316                 /// <seealso cref="RfcLdapMessage.isRequest">
3317                 /// </seealso>
3318                 public virtual LdapMessageQueue SendRequest(LdapMessage request, LdapMessageQueue queue)
3319                 {
3320                         return SendRequest(request, queue, null);
3321                 }
3322                 
3323                 /// <summary> Sends an Ldap request to a directory server.
3324                 /// 
3325                 /// The specified the Ldap request is sent to the directory server
3326                 /// associated with this connection. An Ldap request object is an
3327                 /// {@link LdapMessage} with the operation type set to one of the request
3328                 /// types. You can build a request by using the request classes found in this
3329                 /// package
3330                 /// 
3331                 /// You should note that, since Ldap requests sent to the server
3332                 /// using sendRequest are asynchronous, automatic referral following
3333                 /// does not apply to these requests.
3334                 /// 
3335                 /// </summary>
3336                 /// <param name="request">The Ldap request to send to the directory server.
3337                 /// </param>
3338                 /// <param name="queue">   The queue for messages returned from a server in
3339                 /// response to this request. If it is null, a
3340                 /// queue object is created internally.
3341                 /// </param>
3342                 /// <param name="cons">   The constraints that apply to this request
3343                 /// </param>
3344                 /// <exception>     LdapException A general exception which includes an error
3345                 /// message and an Ldap error code.
3346                 /// 
3347                 /// </exception>
3348                 /// <seealso cref="LdapMessage.Type">
3349                 /// </seealso>
3350                 /// <seealso cref="RfcLdapMessage.isRequest">
3351                 /// </seealso>
3352                 public virtual LdapMessageQueue SendRequest(LdapMessage request, LdapMessageQueue queue, LdapConstraints cons)
3353                 {
3354                         
3355                         
3356                         if (!request.Request)
3357                         {
3358                                 throw new System.SystemException("Object is not a request message");
3359                         }
3360                         
3361                         if (cons == null)
3362                         {
3363                                 cons = defSearchCons;
3364                         }
3365                         
3366                         // Get the correct queue for a search request
3367                         MessageAgent agent;
3368                         LdapMessageQueue myqueue = queue;
3369                         if (myqueue == null)
3370                         {
3371                                 agent = new MessageAgent();
3372                                 if (request.Type == LdapMessage.SEARCH_REQUEST)
3373                                 {
3374                                         myqueue = new LdapSearchQueue(agent);
3375                                 }
3376                                 else
3377                                 {
3378                                         myqueue = new LdapResponseQueue(agent);
3379                                 }
3380                         }
3381                         else
3382                         {
3383                                 if (request.Type == LdapMessage.SEARCH_REQUEST)
3384                                 {
3385                                         agent = queue.MessageAgent;
3386                                 }
3387                                 else
3388                                 {
3389                                         agent = queue.MessageAgent;
3390                                 }
3391                         }
3392                         
3393                         try
3394                         {
3395                                 agent.sendMessage(conn, request, cons.TimeLimit, myqueue, null);
3396                         }
3397                         catch (LdapException lex)
3398                         {
3399                                 throw lex;
3400                         }
3401                         return myqueue;
3402                 }
3403                 
3404                 //*************************************************************************
3405                 // helper methods
3406                 //*************************************************************************
3407                 
3408                 /// <summary> Locates the appropriate message agent and sends
3409                 /// the Ldap request to a directory server.
3410                 /// 
3411                 /// </summary>
3412                 /// <param name="msg">the message to send
3413                 /// 
3414                 /// </param>
3415                 /// <param name="timeout">the timeout value
3416                 /// 
3417                 /// </param>
3418                 /// <param name="queue">the response queue or null
3419                 /// 
3420                 /// </param>
3421                 /// <returns> the LdapResponseQueue for this request
3422                 /// 
3423                 /// </returns>
3424                 /// <exception> LdapException A general exception which includes an error
3425                 /// message and an Ldap error code.
3426                 /// </exception>
3427                 private LdapResponseQueue SendRequestToServer(LdapMessage msg, int timeout, LdapResponseQueue queue, BindProperties bindProps)
3428                 {
3429                         MessageAgent agent;
3430                         if (queue == null)
3431                         {
3432                                 agent = new MessageAgent();
3433                                 queue = new LdapResponseQueue(agent);
3434                         }
3435                         else
3436                         {
3437                                 agent = queue.MessageAgent;
3438                         }
3439                         
3440                         agent.sendMessage(conn, msg, timeout, queue, bindProps);
3441                         return queue;
3442                 }
3443                 
3444                 /// <summary> get an LdapConnection object so that we can follow a referral.
3445                 /// This function is never called if cons.getReferralFollowing() returns
3446                 /// false.
3447                 /// 
3448                 /// </summary>
3449                 /// <param name="referrals">the array of referral strings
3450                 /// 
3451                 /// 
3452                 /// </param>
3453                 /// <returns> The referralInfo object
3454                 /// 
3455                 /// </returns>
3456                 /// <exception> LdapReferralException A general exception which includes
3457                 /// an error message and an Ldap error code.
3458                 /// </exception>
3459                 private ReferralInfo getReferralConnection(System.String[] referrals)
3460                 {
3461                         ReferralInfo refInfo = null;
3462                         System.Exception ex = null;
3463                         LdapConnection rconn = null;
3464                         LdapReferralHandler rh = defSearchCons.getReferralHandler();
3465                         int i = 0;
3466                         // Check if we use LdapRebind to get authentication credentials
3467                         if ((rh == null) || (rh is LdapAuthHandler))
3468                         {
3469                                 for (i = 0; i < referrals.Length; i++)
3470                                 {
3471                                         // dn, pw are null in the default case (anonymous bind)
3472                                         System.String dn = null;
3473                                         sbyte[] pw = null;
3474                                         try
3475                                         {
3476                                                 rconn = new LdapConnection();
3477                                                 rconn.Constraints = defSearchCons;
3478                                                 LdapUrl url = new LdapUrl(referrals[i]);
3479                                                 rconn.Connect(url.Host, url.Port);
3480                                                 if (rh != null)
3481                                                 {
3482                                                         if (rh is LdapAuthHandler)
3483                                                         {
3484                                                                 // Get application supplied dn and pw
3485                                                                 LdapAuthProvider ap = ((LdapAuthHandler) rh).getAuthProvider(url.Host, url.Port);
3486                                                                 dn = ap.DN;
3487                                                                 pw = ap.Password;
3488                                                         }
3489                                                 }
3490                                                 rconn.Bind(Ldap_V3, dn, pw);
3491                                                 ex = null;
3492                                                 refInfo = new ReferralInfo(rconn, referrals, url);
3493                                                 // Indicate this connection created to follow referral
3494                                                 rconn.Connection.ActiveReferral = refInfo;
3495                                                 break;
3496                                         }
3497                                         catch (System.Exception lex)
3498                                         {
3499                                                 if (rconn != null)
3500                                                 {
3501                                                         try
3502                                                         {
3503                                                                 rconn.Disconnect();
3504                                                                 rconn = null;
3505                                                                 ex = lex;
3506                                                         }
3507                                                         catch (LdapException e)
3508                                                         {
3509                                                                 ; // ignore
3510                                                         }
3511                                                 }
3512                                         }
3513                                 }
3514                         }
3515                                 // Check if application gets connection and does bind
3516                         else
3517                         {
3518                                 //  rh instanceof LdapBind
3519                                 try
3520                                 {
3521                                         rconn = ((LdapBindHandler) rh).Bind(referrals, this);
3522                                         if (rconn == null)
3523                                         {
3524                                                 LdapReferralException rex = new LdapReferralException(ExceptionMessages.REFERRAL_ERROR);
3525                                                 rex.setReferrals(referrals);
3526                                                 throw rex;
3527                                         }
3528                                         // Figure out which Url belongs to the connection
3529                                         for (int idx = 0; idx < referrals.Length; idx++)
3530                                         {
3531                                                 try
3532                                                 {
3533                                                         LdapUrl url = new LdapUrl(referrals[idx]);
3534                                                         if (url.Host.ToUpper().Equals(rconn.Host.ToUpper()) && (url.Port == rconn.Port))
3535                                                         {
3536                                                                 refInfo = new ReferralInfo(rconn, referrals, url);
3537                                                                 break;
3538                                                         }
3539                                                 }
3540                                                 catch (System.Exception e)
3541                                                 {
3542                                                         ; // ignore
3543                                                 }
3544                                         }
3545                                         if (refInfo == null)
3546                                         {
3547                                                 // Could not match LdapBind.bind() connecction with URL list
3548                                                 ex = new LdapLocalException(ExceptionMessages.REFERRAL_BIND_MATCH, LdapException.CONNECT_ERROR);
3549                                         }
3550                                 }
3551                                 catch (System.Exception lex)
3552                                 {
3553                                         rconn = null;
3554                                         ex = lex;
3555                                 }
3556                         }
3557                         if (ex != null)
3558                         {
3559                                 // Could not connect to any server, throw an exception
3560                                 LdapException ldapex;
3561                                 if (ex is LdapReferralException)
3562                                 {
3563                                         throw (LdapReferralException) ex;
3564                                 }
3565                                 else if (ex is LdapException)
3566                                 {
3567                                         ldapex = (LdapException) ex;
3568                                 }
3569                                 else
3570                                 {
3571                                         ldapex = new LdapLocalException(ExceptionMessages.SERVER_CONNECT_ERROR, new System.Object[]{conn.Host}, LdapException.CONNECT_ERROR, ex);
3572                                 }
3573                                 // Error attempting to follow a referral
3574                                 LdapReferralException rex = new LdapReferralException(ExceptionMessages.REFERRAL_ERROR, ldapex);
3575                                 rex.setReferrals(referrals);
3576                                 // Use last URL string for the failed referral
3577                                 rex.FailedReferral = referrals[referrals.Length - 1];
3578                                 throw rex;
3579                         }
3580                         
3581                         // We now have an authenticated connection
3582                         // to be used to follow the referral.
3583                         return refInfo;
3584                 }
3585                 
3586                 /// <summary> Check the result code and throw an exception if needed.
3587                 /// 
3588                 /// If referral following is enabled, checks if we need to
3589                 /// follow a referral
3590                 /// 
3591                 /// </summary>
3592                 /// <param name="queue">- the message queue of the current response
3593                 /// 
3594                 /// </param>
3595                 /// <param name="cons">- the constraints that apply to the request
3596                 /// 
3597                 /// </param>
3598                 /// <param name="response">- the LdapResponse to check
3599                 /// </param>
3600                 private void  chkResultCode(LdapMessageQueue queue, LdapConstraints cons, LdapResponse response)
3601                 {
3602                         if ((response.ResultCode == LdapException.REFERRAL) && cons.ReferralFollowing)
3603                         {
3604                                 // Perform referral following and return
3605                                 System.Collections.ArrayList refConn = null;
3606                                 try
3607                                 {
3608                                         chaseReferral(queue, cons, response, response.Referrals, 0, false, null);
3609                                 }
3610                                 finally
3611                                 {
3612                                         releaseReferralConnections(refConn);
3613                                 }
3614                         }
3615                         else
3616                         {
3617                                 // Throws exception for non success result
3618                                 response.chkResultCode();
3619                         }
3620                         return ;
3621                 }
3622                 
3623                 /// <summary> Follow referrals if necessary referral following enabled.
3624                 /// This function is called only by synchronous requests.
3625                 /// Search responses come here only if referral following is
3626                 /// enabled and if we are processing a SearchResultReference
3627                 /// or a Response with a status of REFERRAL, i.e. we are
3628                 /// going to follow a referral.
3629                 /// 
3630                 /// This functions recursively follows a referral until a result
3631                 /// is returned or until the hop limit is reached.
3632                 /// 
3633                 /// </summary>
3634                 /// <param name="queue">The LdapResponseQueue for this request
3635                 /// 
3636                 /// </param>
3637                 /// <param name="cons">The constraints that apply to the request
3638                 /// 
3639                 /// </param>
3640                 /// <param name="msg">The referral or search reference response message
3641                 /// 
3642                 /// </param>
3643                 /// <param name="initialReferrals">The referral array returned from the
3644                 /// initial request.
3645                 /// 
3646                 /// </param>
3647                 /// <param name="hopCount">the number of hops already used while
3648                 /// following this referral
3649                 /// 
3650                 /// </param>
3651                 /// <param name="searchReference">true if the message is a search reference
3652                 /// 
3653                 /// </param>
3654                 /// <param name="connectionList">An optional array list used to store
3655                 /// the LdapConnection objects used in following the referral.
3656                 /// 
3657                 /// </param>
3658                 /// <returns> The array list used to store the all LdapConnection objects
3659                 /// used in following the referral.  The list will be empty
3660                 /// if there were none.
3661                 /// 
3662                 /// </returns>
3663                 /// <exception> LdapException A general exception which includes an error
3664                 /// message and an Ldap error code.
3665                 /// </exception>
3666                 /* package */
3667                 internal virtual System.Collections.ArrayList chaseReferral(LdapMessageQueue queue, LdapConstraints cons, LdapMessage msg, System.String[] initialReferrals, int hopCount, bool searchReference, System.Collections.ArrayList connectionList)
3668                 {
3669                         System.Collections.ArrayList connList = connectionList;
3670                         LdapConnection rconn = null; // new conn for following referral
3671                         ReferralInfo rinfo = null; // referral info
3672                         LdapMessage origMsg;
3673                         
3674                         // Get a place to store new connections
3675                         if (connList == null)
3676                         {
3677                                 connList = new System.Collections.ArrayList(cons.HopLimit);
3678                         }
3679                         // Following referrals or search reference
3680                         System.String[] refs; // referral list
3681                         if (initialReferrals != null)
3682                         {
3683                                 // Search continuation reference from a search request
3684                                 refs = initialReferrals;
3685                                 origMsg = msg.RequestingMessage;
3686                         }
3687                         else
3688                         {
3689                                 // Not a search request
3690                                 LdapResponse resp = (LdapResponse) queue.getResponse();
3691                                 if (resp.ResultCode != LdapException.REFERRAL)
3692                                 {
3693                                         // Not referral result,throw Exception if nonzero result
3694                                         resp.chkResultCode();
3695                                         return connList;
3696                                 }
3697                                 // We have a referral response
3698                                 refs = resp.Referrals;
3699                                 origMsg = resp.RequestingMessage;
3700                         }
3701                         LdapUrl refUrl; // referral represented as URL
3702                         try
3703                         {
3704                                 // increment hop count, check max hops
3705                                 if (hopCount++ > cons.HopLimit)
3706                                 {
3707                                         throw new LdapLocalException("Max hops exceeded", LdapException.REFERRAL_LIMIT_EXCEEDED);
3708                                 }
3709                                 // Get a connection to follow the referral
3710                                 rinfo = getReferralConnection(refs);
3711                                 rconn = rinfo.ReferralConnection;
3712                                 refUrl = rinfo.ReferralUrl;
3713                                 connList.Add(rconn);
3714                                 
3715                                 
3716                                 // rebuild msg into new msg changing msgID,dn,scope,filter
3717                                 LdapMessage newMsg = rebuildRequest(origMsg, refUrl, searchReference);
3718                                 
3719                                 
3720                                 // Send new message on new connection
3721                                 try
3722                                 {
3723                                         MessageAgent agent;
3724                                         if (queue is LdapResponseQueue)
3725                                         {
3726                                                 agent = queue.MessageAgent;
3727                                         }
3728                                         else
3729                                         {
3730                                                 agent = queue.MessageAgent;
3731                                         }
3732                                         agent.sendMessage(rconn.Connection, newMsg, defSearchCons.TimeLimit, queue, null);
3733                                 }
3734                                 catch (InterThreadException ex)
3735                                 {
3736                                         // Error ending request to referred server
3737                                         LdapReferralException rex = new LdapReferralException(ExceptionMessages.REFERRAL_SEND, LdapException.CONNECT_ERROR, null, ex);
3738                                         rex.setReferrals(initialReferrals);
3739                                         ReferralInfo ref_Renamed = rconn.Connection.ActiveReferral;
3740                                         rex.FailedReferral = ref_Renamed.ReferralUrl.ToString();
3741                                         throw rex;
3742                                 }
3743                                 
3744                                 if (initialReferrals == null)
3745                                 {
3746                                         // For operation results, when all responses are complete,
3747                                         // the stack unwinds back to the original and returns
3748                                         // to the application.
3749                                         // An exception is thrown for an error
3750                                         connList = chaseReferral(queue, cons, null, null, hopCount, false, connList);
3751                                 }
3752                                 else
3753                                 {
3754                                         // For search, just return to LdapSearchResults object
3755                                         return connList;
3756                                 }
3757                         }
3758                         catch (System.Exception ex)
3759                         {
3760                                 
3761                                 if (ex is LdapReferralException)
3762                                 {
3763                                         throw (LdapReferralException) ex;
3764                                 }
3765                                 else
3766                                 {
3767                                         
3768                                         // Set referral list and failed referral
3769                                         LdapReferralException rex = new LdapReferralException(ExceptionMessages.REFERRAL_ERROR, ex);
3770                                         rex.setReferrals(refs);
3771                                         if (rinfo != null)
3772                                         {
3773                                                 rex.FailedReferral = rinfo.ReferralUrl.ToString();
3774                                         }
3775                                         else
3776                                         {
3777                                                 rex.FailedReferral = refs[refs.Length - 1];
3778                                         }
3779                                         throw rex;
3780                                 }
3781                         }
3782                         return connList;
3783                 }
3784                 
3785                 /// <summary> Builds a new request replacing dn, scope, and filter where approprate
3786                 /// 
3787                 /// </summary>
3788                 /// <param name="msg">the original LdapMessage to build the new request from
3789                 /// 
3790                 /// </param>
3791                 /// <param name="url">the referral url
3792                 /// 
3793                 /// </param>
3794                 /// <returns> a new LdapMessage with appropriate information replaced
3795                 /// 
3796                 /// </returns>
3797                 /// <exception> LdapException A general exception which includes an error
3798                 /// message and an Ldap error code.
3799                 /// </exception>
3800                 private LdapMessage rebuildRequest(LdapMessage msg, LdapUrl url, bool reference)
3801                 {
3802                         
3803                         System.String dn = url.getDN(); // new base
3804                         System.String filter = null;
3805                         
3806                         switch (msg.Type)
3807                         {
3808                                 
3809                                 case LdapMessage.SEARCH_REQUEST: 
3810                                         if (reference)
3811                                         {
3812                                                 filter = url.Filter;
3813                                         }
3814                                         break;
3815                                         // We are allowed to get a referral for the following
3816                                 
3817                                 case LdapMessage.ADD_REQUEST: 
3818                                 case LdapMessage.BIND_REQUEST: 
3819                                 case LdapMessage.COMPARE_REQUEST: 
3820                                 case LdapMessage.DEL_REQUEST: 
3821                                 case LdapMessage.EXTENDED_REQUEST: 
3822                                 case LdapMessage.MODIFY_RDN_REQUEST: 
3823                                 case LdapMessage.MODIFY_REQUEST: 
3824                                         break;
3825                                         // The following return no response
3826                                 
3827                                 case LdapMessage.ABANDON_REQUEST: 
3828                                 case LdapMessage.UNBIND_REQUEST: 
3829                                 default: 
3830                                         throw new LdapLocalException(ExceptionMessages.IMPROPER_REFERRAL, new System.Object[]{msg.Type}, LdapException.LOCAL_ERROR);
3831                         }
3832                         
3833                         return msg.Clone(dn, filter, reference);
3834                 }
3835                 
3836                 /*
3837                 * Release connections acquired by following referrals
3838                 *
3839                 * @param list the list of the connections
3840                 */
3841                 /* package */
3842                 internal virtual void  releaseReferralConnections(System.Collections.ArrayList list)
3843                 {
3844                         if (list == null)
3845                         {
3846                                 return ;
3847                         }
3848                         // Release referral connections
3849                         for (int i = list.Count - 1; i >= 0; i--)
3850                         {
3851                                 LdapConnection rconn = null;
3852                                 try
3853                                 {
3854                                         rconn=(LdapConnection)list[i];
3855                                         list.RemoveAt(i);
3856 //                                      rconn = (LdapConnection) list.RemoveAt(i);
3857                                         rconn.Disconnect();
3858                                 }
3859                                 catch (System.IndexOutOfRangeException ex)
3860                                 {
3861                                         continue;
3862                                 }
3863                                 catch (LdapException lex)
3864                                 {
3865                                         continue;
3866                                 }
3867                         }
3868                         return ;
3869                 }
3870
3871                 //*************************************************************************
3872                 // Schema Related methods
3873                 //*************************************************************************
3874                 
3875                 /// <summary> Retrieves the schema associated with a particular schema DN in the
3876                 /// directory server.
3877                 /// The schema DN for a particular entry is obtained by calling the
3878                 /// getSchemaDN method of LDAPConnection
3879                 /// 
3880                 /// </summary>
3881                 /// <param name="schemaDN">The schema DN used to fetch the schema.
3882                 /// 
3883                 /// </param>
3884                 /// <returns>    An LDAPSchema entry containing schema attributes.  If the
3885                 /// entry contains no schema attributes then the returned LDAPSchema object
3886                 /// will be empty.
3887                 /// 
3888                 /// </returns>
3889                 /// <exception> LDAPException     This exception occurs if the schema entry
3890                 /// cannot be retrieved with this connection.
3891                 /// </exception>
3892                 /// <seealso cref="GetSchemaDN()">
3893                 /// </seealso>
3894                 /// <seealso cref="GetSchemaDN(String)">
3895                 /// </seealso>
3896                 public virtual LdapSchema FetchSchema(System.String schemaDN)
3897                 {
3898                         LdapEntry ent = Read(schemaDN, LdapSchema.schemaTypeNames);
3899                         return new LdapSchema(ent);
3900                 }
3901                 
3902                 /// <summary> Retrieves the Distiguished Name (DN) for the schema advertised in the
3903                 /// root DSE of the Directory Server.
3904                 /// 
3905                 /// The DN can be used with the methods fetchSchema and modify to retreive
3906                 /// and extend schema definitions.  The schema entry is located by reading
3907                 /// subschemaSubentry attribute of the root DSE.  This is equivalent to
3908                 /// calling {@link #getSchemaDN(String) } with the DN parameter as an empty
3909                 /// string: <code>getSchemaDN("")</code>.
3910                 /// 
3911                 /// 
3912                 /// </summary>
3913                 /// <returns>     Distinguished Name of a schema entry in effect for the
3914                 /// Directory.
3915                 /// </returns>
3916                 /// <exception> LDAPException     This exception occurs if the schema DN
3917                 /// cannot be retrieved, or if the subschemaSubentry attribute associated
3918                 /// with the root DSE contains multiple values.
3919                 /// 
3920                 /// </exception>
3921                 /// <seealso cref="FetchSchema">
3922                 /// </seealso>
3923                 /// <seealso cref="Modify">
3924                 /// </seealso>
3925                 public virtual System.String GetSchemaDN()
3926                 {
3927                         return GetSchemaDN("");
3928                 }
3929                 
3930                 /// <summary> Retrieves the Distiguished Name (DN) of the schema associated with a
3931                 /// entry in the Directory.
3932                 /// 
3933                 /// The DN can be used with the methods fetchSchema and modify to retreive
3934                 /// and extend schema definitions.  Reads the subschemaSubentry of the entry
3935                 /// specified.
3936                 /// 
3937                 /// </summary>
3938                 /// <param name="dn">    Distinguished Name of any entry.  The subschemaSubentry
3939                 /// attribute is queried from this entry.
3940                 /// 
3941                 /// </param>
3942                 /// <returns>      Distinguished Name of a schema entry in effect for the entry
3943                 /// identified by <code>dn</code>.
3944                 /// 
3945                 /// </returns>
3946                 /// <exception> LDAPException     This exception occurs if a null or empty
3947                 /// value is passed as dn, if the subschemasubentry attribute cannot
3948                 /// be retrieved, or the subschemasubentry contains multiple values.
3949                 /// 
3950                 /// </exception>
3951                 /// <seealso cref="FetchSchema">
3952                 /// </seealso>
3953                 /// <seealso cref="Modify">
3954                 /// </seealso>
3955                 public virtual System.String GetSchemaDN(System.String dn)
3956                 {
3957                         System.String[] attrSubSchema = new System.String[]{"subschemaSubentry"};
3958                         
3959                         /* Read the entries subschemaSubentry attribute. Throws an exception if
3960                         * no entries are returned. */
3961                         LdapEntry ent = this.Read(dn, attrSubSchema);
3962                         
3963                         LdapAttribute attr = ent.getAttribute(attrSubSchema[0]);
3964                         System.String[] values = attr.StringValueArray;
3965                         if (values == null || values.Length < 1)
3966                         {
3967                                 throw new LdapLocalException(ExceptionMessages.NO_SCHEMA, new System.Object[]{dn}, LdapException.NO_RESULTS_RETURNED);
3968                         }
3969                         else if (values.Length > 1)
3970                         {
3971                                 throw new LdapLocalException(ExceptionMessages.MULTIPLE_SCHEMA, new System.Object[]{dn}, LdapException.CONSTRAINT_VIOLATION);
3972                         }
3973                         return values[0];
3974                 }
3975
3976         }
3977 }