1 /******************************************************************************
3 * Copyright (c) 2003 Novell Inc., www.novell.com
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:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
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
22 *******************************************************************************/
25 // System.DirectoryServices.DirectorySearcher.cs
28 // Sunil Kumar (sunilk@novell.com)
29 // Andreas Nahr (ClassDevelopment@A-SoftTech.com)
34 using System.ComponentModel;
35 using Novell.Directory.Ldap;
36 using Novell.Directory.Ldap.Utilclass;
37 using System.Collections.Specialized;
39 namespace System.DirectoryServices
43 ///Performs queries against Ldap directory.
45 public class DirectorySearcher : Component
47 private static readonly TimeSpan DefaultTimeSpan = new TimeSpan(-TimeSpan.TicksPerSecond);
48 private DirectoryEntry _SearchRoot=null;
49 private bool _CacheResults=true;
50 private TimeSpan _ClientTimeout = DefaultTimeSpan;
51 private string _Filter="(objectClass=*)";
52 private int _PageSize=0;
53 private StringCollection _PropertiesToLoad=new StringCollection();
54 private bool _PropertyNamesOnly=false;
55 private ReferralChasingOption _ReferralChasing=
56 System.DirectoryServices.ReferralChasingOption.External;
57 private SearchScope _SearchScope=
58 System.DirectoryServices.SearchScope.Subtree;
59 private TimeSpan _ServerPageTimeLimit = DefaultTimeSpan;
60 private TimeSpan _serverTimeLimit = DefaultTimeSpan;
61 private int _SizeLimit=0;
62 private LdapConnection _conn = null;
63 private string _Host=null;
64 private int _Port=389;
65 private SearchResultCollection _SrchColl=null;
67 internal SearchResultCollection SrchColl
71 if (_SrchColl == null)
73 _SrchColl = new SearchResultCollection();
80 private void InitBlock()
82 _conn = new LdapConnection();
83 LdapUrl lUrl=new LdapUrl(SearchRoot.ADsPath);
86 _conn.Connect(_Host,_Port);
87 _conn.Bind(SearchRoot.Username,SearchRoot.Password,(Novell.Directory.Ldap.AuthenticationTypes)SearchRoot.AuthenticationType);
92 /// Gets or sets a value indicating whether the result is
93 /// cached on the client computer.
96 /// true if the result is cached on the client computer; otherwise,
97 /// false. The default is true
100 /// If the search returns a large result set, it is better to set
101 /// this property to false.
103 [DSDescription ("The cacheability of results.")]
104 [DefaultValue (true)]
105 public bool CacheResults
109 return _CacheResults;
113 _CacheResults = value;
118 /// Gets or sets the maximum amount of time that the client waits for
119 /// the server to return results. If the server does not respond
120 /// within this time, the search is aborted and no results are
124 /// A TimeSpan that represents the maximum amount of time (in seconds)
125 /// for the client to wait for the server to return results. The
126 /// default is -1, which means to wait indefinitely.
129 /// If the ServerTimeLimit is reached before the client times out,
130 /// the server returns its results and the client stops waiting. The
131 /// maximum server time limit is 120 seconds.
133 [DSDescription ("The maximum amount of time that the client waits for the server to return results.")]
134 public TimeSpan ClientTimeout
138 return _ClientTimeout;
142 _ClientTimeout = value;
147 /// Gets or sets the Lightweight Directory Access Protocol (Ldap)
148 /// format filter string.
151 /// The search filter string in Ldap format, such as
152 /// "(objectClass=user)". The default is "(objectClass=*)", which
153 /// retrieves all objects.
156 /// The filter uses the following guidelines:
157 /// 1. The string must be enclosed in parentheses.
158 /// 2. Expressions can use the relational operators: <, <=, =, >=,
159 /// and >. An example is "(objectClass=user)". Another example is
160 /// "(lastName>=Davis)".
161 /// 3. Compound expressions are formed with the prefix operators &
162 /// and |. Anexampleis"(&(objectClass=user)(lastName= Davis))".
163 /// Anotherexampleis"(&(objectClass=printer)(|(building=42)
164 /// (building=43)))".
166 [DSDescription ("The Lightweight Directory Access Protocol (Ldap) format filter string.")]
167 [DefaultValue ("(objectClass=*)")]
168 [RecommendedAsConfigurable (true)]
169 [TypeConverter ("System.Diagnostics.Design.StringValueConverter, " + Consts.AssemblySystem_Design)]
179 ClearCachedResults();
184 /// Gets or sets the page size in a paged search.
187 /// The maximum number of objects the server can return in a paged
188 /// search. The default is zero, which means do not do a paged search.
191 /// After the server has found a PageSize object, it will stop
192 /// searching and return the results to the client. When the client
193 /// requests more data, the server will restart the search where it
196 [DSDescription ("The page size in a paged search.")]
211 /// Gets the set of properties retrieved during the search.
214 /// The set of properties retrieved during the search. The default is
215 /// an empty StringCollection, which retrieves all properties.
218 /// To retrieve specific properties, add them to this collection
219 /// before you begin the search. For example, searcher.
220 /// PropertiesToLoad.Add("phone");.
222 [DSDescription ("The set of properties retrieved during the search.")]
223 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
224 [Editor ("System.Windows.Forms.Design.StringCollectionEditor, " + Consts.AssemblySystem_Design, "System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)]
225 public StringCollection PropertiesToLoad
229 return _PropertiesToLoad;
234 /// Gets or sets a value indicating whether the search retrieves only the
235 /// names of attributes to which values have been assigned.
238 /// true if the search obtains only the names of attributes to which
239 /// values have been assigned; false if the search obtains the names
240 /// and values for all the requested attributes. The default is false.
242 [DSDescription ("A value indicating whether the search retrieves only the names of attributes to which values have been assigned.")]
243 [DefaultValue (false)]
244 public bool PropertyNamesOnly
248 return _PropertyNamesOnly;
252 _PropertyNamesOnly = value;
257 /// Gets or sets how referrals are chased.
260 /// One of the ReferralChasingOption values. The default is External.
263 /// If the root search is not specified in the naming context of the
264 /// server or when the search results cross a naming context (for
265 /// example, when you have child domains and search in the parent
266 /// domain), the server sends a referral message to the client that
267 /// the client can choose to ignore or chase.
269 [DSDescription ("How referrals are chased.")]
270 [DefaultValue (ReferralChasingOption.External)]
271 public ReferralChasingOption ReferralChasing
275 return _ReferralChasing;
279 _ReferralChasing = value;
284 /// Gets or sets the node in the Ldap Directory hierarchy where the
288 /// The DirectoryEntry in the Ldap Directory hierarchy where the
289 /// search starts. The default is a null reference
291 [DSDescription ("The node in the Ldap Directory hierarchy where the search starts.")]
292 [DefaultValue (null)]
293 public DirectoryEntry SearchRoot
302 ClearCachedResults();
307 /// Gets or sets the scope of the search that is observed by the
311 /// One of the SearchScope values. The default is Subtree.
313 [DSDescription ("The scope of the search that is observed by the server.")]
314 [DefaultValue (SearchScope.Subtree)]
315 [RecommendedAsConfigurable (true)]
316 public SearchScope SearchScope
324 _SearchScope = value;
325 ClearCachedResults();
330 /// Gets or sets the time limit the server should observe to search an
331 /// individual page of results (as opposed to the time limit for the
335 /// A TimeSpan that represents the amount of time the server should
336 /// observe to search a page of results. The default is -1, which
337 /// means to search indefinitely.
340 /// When the time limit is reached, the server stops searching and
341 /// returns the result obtained up to that point, along with a cookie
342 /// containing the information about where to resume searching.
343 /// A negative value means to search indefinitely.
344 /// Note: This property only applies to searches where PageSize
345 /// is set to a value that is not the default of -1.
347 [DSDescription ("The time limit the server should observe to search an individual page of results.")]
348 public TimeSpan ServerPageTimeLimit
352 return _ServerPageTimeLimit;
356 _ServerPageTimeLimit = value;
361 /// Gets or sets the time limit the server should observe to search.
364 /// A TimeSpan that represents the amount of time the server should
365 /// observe to search.
370 [DSDescription ("The time limit the server should observe to search.")]
371 public TimeSpan ServerTimeLimit
376 return _serverTimeLimit;
381 _serverTimeLimit = value;
386 /// Gets or sets the maximum number of objects the server returns in
390 /// The maximum number of objects the server returns in a search. The
391 /// default of zero means to use the server-determined default size
392 /// limit of 1000 entries.
395 /// The server stops searching after the size limit is reached and
396 /// returns the results accumulated up to that point.
397 /// Note: If you set SizeLimit to a value that is larger
398 /// than the server-determined default of 1000 entries, the
399 /// server-determined default is used.
401 [DSDescription ("The maximum number of objects the server returns in a search.")]
412 throw new ArgumentException();
418 [DSDescription ("An object that defines how the data should be sorted.")]
419 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
420 [TypeConverter (typeof (ExpandableObjectConverter))]
421 public SortOption Sort
426 throw new NotImplementedException();
431 throw new NotImplementedException();
436 /// Initializes a new instance of the DirectorySearcher class with
437 /// SearchRoot, Filter, PropertiesToLoad, and SearchScope set to the
440 public DirectorySearcher()
445 /// Initializes a new instance of the DirectorySearcher class with
446 /// Filter, PropertiesToLoad, and SearchScope set to the default
447 /// values. SearchRoot is set to the specified value.
449 /// <param name="searchRoot">
450 /// The node in the Active Directory hierarchy where the search starts.
451 /// The SearchRoot property is initialized to this value.
453 public DirectorySearcher(DirectoryEntry searchRoot)
455 _SearchRoot = searchRoot;
459 /// Initializes a new instance of the DirectorySearcher class with
460 /// SearchRoot, PropertiesToLoad, and SearchScope set to the default
461 /// values. Filter is set to the specified value.
463 /// <param name="filter">
464 /// The search filter string in Lightweight Directory Access Protocol
465 /// (Ldap) format. The Filter property is initialized to this value.
467 public DirectorySearcher(string filter)
473 /// Initializes a new instance of the DirectorySearcher class with
474 /// PropertiesToLoad and SearchScope set to the default values.
475 /// SearchRoot and Filter are set to the specified values.
477 /// <param name="searchRoot">
478 /// The node in the Active Directory hierarchy where the search starts.
479 /// The SearchRoot property is initialized to this value.
481 /// <param name="filter">
482 /// The search filter string in Lightweight Directory Access Protocol
483 /// (Ldap) format. The Filter property is initialized to this value.
485 public DirectorySearcher( DirectoryEntry searchRoot,
488 _SearchRoot = searchRoot;
493 /// Initializes a new instance of the DirectorySearcher class with
494 /// SearchRoot and SearchScope set to the default values. Filter and
495 /// PropertiesToLoad are set to the specified values.
497 /// <param name="filter">
498 /// The search filter string in Lightweight Directory Access Protocol
499 /// (Ldap) format. The Filter property is initialized to this value.
501 /// <param name="propertiesToLoad">
502 /// The set of properties to retrieve during the search. The
503 /// PropertiesToLoad property is initialized to this value.
505 public DirectorySearcher( string filter,
506 string[] propertiesToLoad )
509 PropertiesToLoad.AddRange(propertiesToLoad);
513 /// Initializes a new instance of the DirectorySearcher class with
514 /// SearchScope set to its default value. SearchRoot, Filter, and
515 /// PropertiesToLoad are set to the specified values.
517 /// <param name="searchRoot">
518 /// The node in the Active Directory hierarchy where the search starts.
519 /// The SearchRoot property is initialized to this value
521 /// <param name="filter">
522 /// The search filter string in Lightweight Directory Access Protocol
523 /// (Ldap) format. The Filter property is initialized to this value.
525 /// <param name="propertiesToLoad">
526 /// The set of properties retrieved during the search. The
527 /// PropertiesToLoad property is initialized to this value.
529 public DirectorySearcher( DirectoryEntry searchRoot,
531 string[] propertiesToLoad )
533 _SearchRoot = searchRoot;
535 PropertiesToLoad.AddRange(propertiesToLoad);
539 /// Initializes a new instance of the DirectorySearcher class with
540 /// SearchRoot set to its default value. Filter, PropertiesToLoad,
541 /// and SearchScope are set to the specified values
543 /// <param name="filter">
544 /// The search filter string in Lightweight Directory Access Protocol
545 /// (Ldap) format. The Filter property is initialized to this value.
547 /// <param name="propertiesToLoad">
548 /// The set of properties to retrieve during the search. The
549 /// PropertiesToLoad property is initialized to this value.
551 /// <param name="scope">
552 /// The scope of the search that is observed by the server. The
553 /// SearchScope property is initialized to this value.
555 public DirectorySearcher( string filter,
556 string[] propertiesToLoad,
559 _SearchScope = scope;
561 PropertiesToLoad.AddRange(propertiesToLoad);
565 /// Initializes a new instance of the DirectorySearcher class with the
566 /// SearchRoot, Filter, PropertiesToLoad, and SearchScope properties
567 /// set to the specified values
569 /// <param name="searchRoot">
570 /// The node in the Active Directory hierarchy where the search starts.
571 /// The SearchRoot property is initialized to this value.
573 /// <param name="filter">
574 /// The search filter string in Lightweight Directory Access Protocol
575 /// (Ldap) format. The Filter property is initialized to this value.
577 /// <param name="propertiesToLoad">
578 /// The set of properties to retrieve during the search. The
579 /// PropertiesToLoad property is initialized to this value.
581 /// <param name="scope">
582 /// The scope of the search that is observed by the server. The
583 /// SearchScope property is initialized to this value.
585 public DirectorySearcher( DirectoryEntry searchRoot,
587 string[] propertiesToLoad,
590 _SearchRoot = searchRoot;
591 _SearchScope = scope;
593 PropertiesToLoad.AddRange(propertiesToLoad);
598 /// Executes the Search and returns only the first entry found
601 /// A SearchResult that is the first entry found during the Search
603 public SearchResult FindOne()
605 // TBD : should search for no more than single result
606 if (SrchColl.Count == 0) {
613 /// Executes the Search and returns a collection of the entries that are found
616 /// A SearchResultCollection collection of entries from the director
618 public SearchResultCollection FindAll()
623 private void DoSearch()
626 String[] attrs= new String[PropertiesToLoad.Count];
627 PropertiesToLoad.CopyTo(attrs,0);
629 LdapSearchConstraints cons = _conn.SearchConstraints;
631 cons.MaxResults = SizeLimit;
633 if (ServerTimeLimit != DefaultTimeSpan) {
634 cons.ServerTimeLimit = (int)ServerTimeLimit.TotalSeconds;
637 int connScope = LdapConnection.SCOPE_SUB;
\r
638 switch (_SearchScope)
\r
640 case SearchScope.Base:
\r
641 connScope = LdapConnection.SCOPE_BASE;
\r
644 case SearchScope.OneLevel:
\r
645 connScope = LdapConnection.SCOPE_ONE;
\r
648 case SearchScope.Subtree:
\r
649 connScope = LdapConnection.SCOPE_SUB;
\r
653 connScope = LdapConnection.SCOPE_SUB;
\r
656 LdapSearchResults lsc=_conn.Search( SearchRoot.Fdn,
660 PropertyNamesOnly,cons);
664 LdapEntry nextEntry = null;
667 nextEntry = lsc.next();
669 catch(LdapException e)
671 switch (e.ResultCode) {
672 // in case of this return codes exception should not be thrown
673 case LdapException.SIZE_LIMIT_EXCEEDED:
674 case LdapException.TIME_LIMIT_EXCEEDED:
680 DirectoryEntry de = new DirectoryEntry(_conn);
681 PropertyCollection pcoll = new PropertyCollection();
682 // de.SetProperties();
683 de.Path = DirectoryEntry.GetLdapUrlString(_Host,_Port,nextEntry.DN);
684 LdapAttributeSet attributeSet = nextEntry.getAttributeSet();
685 System.Collections.IEnumerator ienum=attributeSet.GetEnumerator();
688 while(ienum.MoveNext())
690 LdapAttribute attribute=(LdapAttribute)ienum.Current;
691 string attributeName = attribute.Name;
692 pcoll[attributeName].AddRange(attribute.StringValueArray);
693 // de.Properties[attributeName].AddRange(attribute.StringValueArray);
694 // de.Properties[attributeName].Mbit=false;
697 if (!pcoll.Contains("ADsPath")) {
698 pcoll["ADsPath"].Add(de.Path);
700 // _SrchColl.Add(new SearchResult(de,PropertiesToLoad));
701 _SrchColl.Add(new SearchResult(de,pcoll));
707 protected override void Dispose(bool disposing)
710 if(_conn != null && _conn.Connected) {
\r
711 _conn.Disconnect();
\r
714 base.Dispose(disposing);
717 private void ClearCachedResults()