1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.Runtime.InteropServices; 6 using System.Collections; 7 using System.Collections.Specialized; 8 using System.DirectoryServices.Interop; 9 using System.ComponentModel; 10 11 using INTPTR_INTPTRCAST = System.IntPtr; 12 13 namespace System.DirectoryServices 14 { 15 /// <devdoc> 16 /// Performs queries against the Active Directory hierarchy. 17 /// </devdoc> 18 public class DirectorySearcher : Component 19 { 20 private DirectoryEntry _searchRoot; 21 private string _filter = defaultFilter; 22 private StringCollection _propertiesToLoad; 23 private bool _disposed = false; 24 25 private static readonly TimeSpan s_minusOneSecond = new TimeSpan(0, 0, -1); 26 27 // search preference variables 28 private SearchScope _scope = System.DirectoryServices.SearchScope.Subtree; 29 private bool _scopeSpecified = false; 30 private int _sizeLimit = 0; 31 private TimeSpan _serverTimeLimit = s_minusOneSecond; 32 private TimeSpan _clientTimeout = s_minusOneSecond; 33 private int _pageSize = 0; 34 private TimeSpan _serverPageTimeLimit = s_minusOneSecond; 35 private ReferralChasingOption _referralChasing = ReferralChasingOption.External; 36 private SortOption _sort = new SortOption(); 37 private bool _cacheResults = true; 38 private bool _cacheResultsSpecified = false; 39 private bool _rootEntryAllocated = false; // true: if a temporary entry inside Searcher has been created 40 private string _assertDefaultNamingContext = null; 41 private string _attributeScopeQuery = ""; 42 private bool _attributeScopeQuerySpecified = false; 43 private DereferenceAlias _derefAlias = DereferenceAlias.Never; 44 private SecurityMasks _securityMask = SecurityMasks.None; 45 private ExtendedDN _extendedDN = ExtendedDN.None; 46 private DirectorySynchronization _sync = null; 47 internal bool directorySynchronizationSpecified = false; 48 private DirectoryVirtualListView _vlv = null; 49 internal bool directoryVirtualListViewSpecified = false; 50 internal SearchResultCollection searchResult = null; 51 52 private const string defaultFilter = "(objectClass=*)"; 53 54 /// <devdoc> 55 /// Initializes a new instance of the <see cref='System.DirectoryServices.DirectorySearcher'/> class with <see cref='System.DirectoryServices.DirectorySearcher.SearchRoot'/>, 56 /// <see cref='System.DirectoryServices.DirectorySearcher.Filter'/>, <see cref='System.DirectoryServices.DirectorySearcher.PropertiesToLoad'/>, and <see cref='System.DirectoryServices.DirectorySearcher.SearchScope'/> set to their default values. 57 /// </devdoc> DirectorySearcher()58 public DirectorySearcher() : this(null, defaultFilter, null, System.DirectoryServices.SearchScope.Subtree) 59 { 60 _scopeSpecified = false; 61 } 62 63 /// <devdoc> 64 /// Initializes a new instance of the <see cref='System.DirectoryServices.DirectorySearcher'/> class with 65 /// <see cref='System.DirectoryServices.DirectorySearcher.Filter'/>, <see cref='System.DirectoryServices.DirectorySearcher.PropertiesToLoad'/>, and <see cref='System.DirectoryServices.DirectorySearcher.SearchScope'/> set to their default 66 /// values, and <see cref='System.DirectoryServices.DirectorySearcher.SearchRoot'/> set to the given value. 67 /// </devdoc> DirectorySearcher(DirectoryEntry searchRoot)68 public DirectorySearcher(DirectoryEntry searchRoot) : this(searchRoot, defaultFilter, null, System.DirectoryServices.SearchScope.Subtree) 69 { 70 _scopeSpecified = false; 71 } 72 73 /// <devdoc> 74 /// Initializes a new instance of the <see cref='System.DirectoryServices.DirectorySearcher'/> class with 75 /// <see cref='System.DirectoryServices.DirectorySearcher.PropertiesToLoad'/> and <see cref='System.DirectoryServices.DirectorySearcher.SearchScope'/> set to their default 76 /// values, and <see cref='System.DirectoryServices.DirectorySearcher.SearchRoot'/> and <see cref='System.DirectoryServices.DirectorySearcher.Filter'/> set to the respective given values. 77 /// </devdoc> DirectorySearcher(DirectoryEntry searchRoot, string filter)78 public DirectorySearcher(DirectoryEntry searchRoot, string filter) : this(searchRoot, filter, null, System.DirectoryServices.SearchScope.Subtree) 79 { 80 _scopeSpecified = false; 81 } 82 83 /// <devdoc> 84 /// Initializes a new instance of the <see cref='System.DirectoryServices.DirectorySearcher'/> class with 85 /// <see cref='System.DirectoryServices.DirectorySearcher.SearchScope'/> set to its default 86 /// value, and <see cref='System.DirectoryServices.DirectorySearcher.SearchRoot'/>, <see cref='System.DirectoryServices.DirectorySearcher.Filter'/>, and <see cref='System.DirectoryServices.DirectorySearcher.PropertiesToLoad'/> set to the respective given values. 87 /// </devdoc> DirectorySearcher(DirectoryEntry searchRoot, string filter, string[] propertiesToLoad)88 public DirectorySearcher(DirectoryEntry searchRoot, string filter, string[] propertiesToLoad) : this(searchRoot, filter, propertiesToLoad, System.DirectoryServices.SearchScope.Subtree) 89 { 90 _scopeSpecified = false; 91 } 92 93 /// <devdoc> 94 /// Initializes a new instance of the <see cref='System.DirectoryServices.DirectorySearcher'/> class with <see cref='System.DirectoryServices.DirectorySearcher.SearchRoot'/>, 95 /// <see cref='System.DirectoryServices.DirectorySearcher.PropertiesToLoad'/>, and <see cref='System.DirectoryServices.DirectorySearcher.SearchScope'/> set to their default 96 /// values, and <see cref='System.DirectoryServices.DirectorySearcher.Filter'/> set to the given value. 97 /// </devdoc> DirectorySearcher(string filter)98 public DirectorySearcher(string filter) : this(null, filter, null, System.DirectoryServices.SearchScope.Subtree) 99 { 100 _scopeSpecified = false; 101 } 102 103 /// <devdoc> 104 /// Initializes a new instance of the <see cref='System.DirectoryServices.DirectorySearcher'/> class with <see cref='System.DirectoryServices.DirectorySearcher.SearchRoot'/> 105 /// and <see cref='System.DirectoryServices.DirectorySearcher.SearchScope'/> set to their default 106 /// values, and <see cref='System.DirectoryServices.DirectorySearcher.Filter'/> and <see cref='System.DirectoryServices.DirectorySearcher.PropertiesToLoad'/> set to the respective given values. 107 /// </devdoc> DirectorySearcher(string filter, string[] propertiesToLoad)108 public DirectorySearcher(string filter, string[] propertiesToLoad) : this(null, filter, propertiesToLoad, System.DirectoryServices.SearchScope.Subtree) 109 { 110 _scopeSpecified = false; 111 } 112 113 /// <devdoc> 114 /// Initializes a new instance of the <see cref='System.DirectoryServices.DirectorySearcher'/> class with <see cref='System.DirectoryServices.DirectorySearcher.SearchRoot'/> set to its default 115 /// value, and <see cref='System.DirectoryServices.DirectorySearcher.Filter'/>, <see cref='System.DirectoryServices.DirectorySearcher.PropertiesToLoad'/>, and <see cref='System.DirectoryServices.DirectorySearcher.SearchScope'/> set to the respective given values.</para> 116 /// </devdoc> DirectorySearcher(string filter, string[] propertiesToLoad, SearchScope scope)117 public DirectorySearcher(string filter, string[] propertiesToLoad, SearchScope scope) : this(null, filter, propertiesToLoad, scope) 118 { 119 } 120 121 /// <devdoc> 122 /// Initializes a new instance of the <see cref='System.DirectoryServices.DirectorySearcher'/> class with the <see cref='System.DirectoryServices.DirectorySearcher.SearchRoot'/>, <see cref='System.DirectoryServices.DirectorySearcher.Filter'/>, <see cref='System.DirectoryServices.DirectorySearcher.PropertiesToLoad'/>, and <see cref='System.DirectoryServices.DirectorySearcher.SearchScope'/> properties set to the given 123 /// values. 124 /// </devdoc> DirectorySearcher(DirectoryEntry searchRoot, string filter, string[] propertiesToLoad, SearchScope scope)125 public DirectorySearcher(DirectoryEntry searchRoot, string filter, string[] propertiesToLoad, SearchScope scope) 126 { 127 _searchRoot = searchRoot; 128 _filter = filter; 129 if (propertiesToLoad != null) 130 PropertiesToLoad.AddRange(propertiesToLoad); 131 this.SearchScope = scope; 132 } 133 Dispose(bool disposing)134 protected override void Dispose(bool disposing) 135 { 136 // safe to call while finalizing or disposing 137 // 138 if (!_disposed && disposing) 139 { 140 if (_rootEntryAllocated) 141 _searchRoot.Dispose(); 142 _rootEntryAllocated = false; 143 _disposed = true; 144 } 145 base.Dispose(disposing); 146 } 147 148 /// <devdoc> 149 /// Gets or sets a value indicating whether the result should be cached on the 150 /// client machine. 151 /// </devdoc> 152 [DefaultValue(true)] 153 public bool CacheResults 154 { 155 get => _cacheResults; 156 set 157 { 158 // user explicitly set CacheResults to true and also want VLV 159 if (directoryVirtualListViewSpecified == true && value == true) 160 throw new ArgumentException(SR.DSBadCacheResultsVLV); 161 162 _cacheResults = value; 163 164 _cacheResultsSpecified = true; 165 } 166 } 167 168 /// <devdoc> 169 /// Gets or sets the maximum amount of time that the client waits for 170 /// the server to return results. If the server does not respond within this time, 171 /// the search is aborted, and no results are returned.</para> 172 /// </devdoc> 173 public TimeSpan ClientTimeout 174 { 175 get => _clientTimeout; 176 set 177 { 178 // prevent integer overflow 179 if (value.TotalSeconds > Int32.MaxValue) 180 { 181 throw new ArgumentException(SR.TimespanExceedMax, "value"); 182 } 183 184 _clientTimeout = value; 185 } 186 } 187 188 /// <devdoc> 189 /// Gets or sets a value indicating whether the search should retrieve only the names of requested 190 /// properties or the names and values of requested properties.</para> 191 /// </devdoc> 192 [DefaultValue(false)] 193 public bool PropertyNamesOnly { get; set; } 194 195 /// <devdoc> 196 /// Gets or sets the Lightweight Directory Access Protocol (LDAP) filter string format. 197 /// </devdoc> 198 [ 199 DefaultValue(defaultFilter), 200 // CoreFXPort - Remove design support 201 // TypeConverter("System.Diagnostics.Design.StringValueConverter, " + AssemblyRef.SystemDesign) 202 ] 203 public string Filter 204 { 205 get => _filter; 206 set 207 { 208 if (value == null || value.Length == 0) 209 value = defaultFilter; 210 _filter = value; 211 } 212 } 213 214 /// <devdoc> 215 /// Gets or sets the page size in a paged search. 216 /// </devdoc> 217 [DefaultValue(0)] 218 public int PageSize 219 { 220 get => _pageSize; 221 set 222 { 223 if (value < 0) 224 throw new ArgumentException(SR.DSBadPageSize); 225 226 // specify non-zero pagesize explicitly and also want dirsync 227 if (directorySynchronizationSpecified == true && value != 0) 228 throw new ArgumentException(SR.DSBadPageSizeDirsync); 229 230 _pageSize = value; 231 } 232 } 233 234 /// <devdoc> 235 /// Gets the set of properties retrieved during the search. By default, the <see cref='System.DirectoryServices.DirectoryEntry.Path'/> 236 /// and <see cref='System.DirectoryServices.DirectoryEntry.Name'/> properties are retrieved. 237 /// </devdoc> 238 public StringCollection PropertiesToLoad 239 { 240 get 241 { 242 if (_propertiesToLoad == null) 243 { 244 _propertiesToLoad = new StringCollection(); 245 } 246 return _propertiesToLoad; 247 } 248 } 249 250 /// <devdoc> 251 /// Gets or sets how referrals are chased. 252 /// </devdoc> 253 [DefaultValue(ReferralChasingOption.External)] 254 public ReferralChasingOption ReferralChasing 255 { 256 get => _referralChasing; 257 set 258 { 259 if (value != ReferralChasingOption.None && 260 value != ReferralChasingOption.Subordinate && 261 value != ReferralChasingOption.External && 262 value != ReferralChasingOption.All) 263 throw new InvalidEnumArgumentException("value", (int)value, typeof(ReferralChasingOption)); 264 265 _referralChasing = value; 266 } 267 } 268 269 /// <devdoc> 270 /// Gets or sets the scope of the search that should be observed by the server. 271 /// </devdoc> 272 [DefaultValue(SearchScope.Subtree)] 273 public SearchScope SearchScope 274 { 275 get => _scope; 276 set 277 { 278 if (value < SearchScope.Base || value > SearchScope.Subtree) 279 throw new InvalidEnumArgumentException("value", (int)value, typeof(SearchScope)); 280 281 // user explicitly set SearchScope to something other than Base and also want to do ASQ, it is not supported 282 if (_attributeScopeQuerySpecified == true && value != SearchScope.Base) 283 { 284 throw new ArgumentException(SR.DSBadASQSearchScope); 285 } 286 287 _scope = value; 288 289 _scopeSpecified = true; 290 } 291 } 292 293 /// <devdoc> 294 /// Gets or sets the time limit that the server should observe to search a page of results (as 295 /// opposed to the time limit for the entire search). 296 /// </devdoc> 297 public TimeSpan ServerPageTimeLimit 298 { 299 get => _serverPageTimeLimit; 300 set 301 { 302 // prevent integer overflow 303 if (value.TotalSeconds > Int32.MaxValue) 304 { 305 throw new ArgumentException(SR.TimespanExceedMax, "value"); 306 } 307 308 _serverPageTimeLimit = value; 309 } 310 } 311 312 /// <devdoc> 313 /// Gets or sets the maximum amount of time the server spends searching. If the 314 /// time limit is reached, only entries found up to that point will be returned. 315 /// </devdoc> 316 public TimeSpan ServerTimeLimit 317 { 318 get => _serverTimeLimit; 319 set 320 { 321 // prevent integer overflow 322 if (value.TotalSeconds > Int32.MaxValue) 323 { 324 throw new ArgumentException(SR.TimespanExceedMax, "value"); 325 } 326 327 _serverTimeLimit = value; 328 } 329 } 330 331 /// <devdoc> 332 /// Gets or sets the maximum number of objects that the 333 /// server should return in a search. 334 /// </devdoc> 335 [DefaultValue(0)] 336 public int SizeLimit 337 { 338 get => _sizeLimit; 339 set 340 { 341 if (value < 0) 342 throw new ArgumentException(SR.DSBadSizeLimit); 343 _sizeLimit = value; 344 } 345 } 346 347 /// <devdoc> 348 /// Gets or sets the node in the Active Directory hierarchy 349 /// at which the search will start. 350 /// </devdoc> 351 [DefaultValue(null)] 352 public DirectoryEntry SearchRoot 353 { 354 get 355 { 356 if (_searchRoot == null && !DesignMode) 357 { 358 // get the default naming context. This should be the default root for the search. 359 DirectoryEntry rootDSE = new DirectoryEntry("LDAP://RootDSE", true, null, null, AuthenticationTypes.Secure); 360 361 //SECREVIEW: Searching the root of the DS will demand browse permissions 362 // on "*" or "LDAP://RootDSE". 363 string defaultNamingContext = (string)rootDSE.Properties["defaultNamingContext"][0]; 364 rootDSE.Dispose(); 365 366 _searchRoot = new DirectoryEntry("LDAP://" + defaultNamingContext, true, null, null, AuthenticationTypes.Secure); 367 _rootEntryAllocated = true; 368 _assertDefaultNamingContext = "LDAP://" + defaultNamingContext; 369 } 370 return _searchRoot; 371 } 372 set 373 { 374 if (_rootEntryAllocated) 375 _searchRoot.Dispose(); 376 _rootEntryAllocated = false; 377 378 _assertDefaultNamingContext = null; 379 _searchRoot = value; 380 } 381 } 382 383 /// <devdoc> 384 /// Gets the property on which the results should be sorted. 385 /// </devdoc> 386 [TypeConverter(typeof(ExpandableObjectConverter))] 387 public SortOption Sort 388 { 389 get => _sort; 390 set => _sort = value ?? throw new ArgumentNullException(nameof(value)); 391 } 392 393 /// <devdoc> 394 /// Gets or sets a value indicating whether searches should be carried out in an asynchronous 395 /// way. 396 /// </devdoc> 397 [DefaultValue(false)] 398 public bool Asynchronous { get; set; } 399 400 /// <devdoc> 401 /// Gets or sets a value indicating whether the search should also return deleted objects that match the search 402 /// filter. 403 /// </devdoc> 404 [DefaultValue(false)] 405 public bool Tombstone { get; set; } 406 407 /// <devdoc> 408 /// Gets or sets an attribute name to indicate that an attribute-scoped query search should be 409 /// performed. 410 /// </devdoc> 411 [ 412 DefaultValue(""), 413 // CoreFXPort - Remove design support 414 // TypeConverter("System.Diagnostics.Design.StringValueConverter, " + AssemblyRef.SystemDesign) 415 ] 416 public string AttributeScopeQuery 417 { 418 get => _attributeScopeQuery; 419 set 420 { 421 if (value == null) 422 value = ""; 423 424 // user explicitly set AttributeScopeQuery and value is not null or empty string 425 if (value.Length != 0) 426 { 427 if (_scopeSpecified == true && SearchScope != SearchScope.Base) 428 { 429 throw new ArgumentException(SR.DSBadASQSearchScope); 430 } 431 432 // if user did not explicitly set search scope 433 _scope = SearchScope.Base; 434 435 _attributeScopeQuerySpecified = true; 436 } 437 else 438 // user explicitly sets the value to default one and doesn't want to do asq 439 { 440 _attributeScopeQuerySpecified = false; 441 } 442 443 _attributeScopeQuery = value; 444 } 445 } 446 447 /// <devdoc> 448 /// Gets or sets a value to indicate how the aliases of found objects are to be 449 /// resolved. 450 /// </devdoc> 451 [DefaultValue(DereferenceAlias.Never)] 452 public DereferenceAlias DerefAlias 453 { 454 get => _derefAlias; 455 set 456 { 457 if (value < DereferenceAlias.Never || value > DereferenceAlias.Always) 458 throw new InvalidEnumArgumentException("value", (int)value, typeof(DereferenceAlias)); 459 460 _derefAlias = value; 461 } 462 } 463 464 /// <devdoc> 465 /// Gets or sets a value to indicate the search should return security access information for the specified 466 /// attributes. 467 /// </devdoc> 468 [DefaultValue(SecurityMasks.None)] 469 public SecurityMasks SecurityMasks 470 { 471 get => _securityMask; 472 set 473 { 474 // make sure the behavior is consistent with native ADSI 475 if (value > (SecurityMasks.None | SecurityMasks.Owner | SecurityMasks.Group | SecurityMasks.Dacl | SecurityMasks.Sacl)) 476 throw new InvalidEnumArgumentException("value", (int)value, typeof(SecurityMasks)); 477 478 _securityMask = value; 479 } 480 } 481 482 /// <devdoc> 483 /// Gets or sets a value to return extended DNs according to the requested 484 /// format. 485 /// </devdoc> 486 [DefaultValue(ExtendedDN.None)] 487 public ExtendedDN ExtendedDN 488 { 489 get => _extendedDN; 490 set 491 { 492 if (value < ExtendedDN.None || value > ExtendedDN.Standard) 493 throw new InvalidEnumArgumentException("value", (int)value, typeof(ExtendedDN)); 494 495 _extendedDN = value; 496 } 497 } 498 499 /// <devdoc> 500 /// Gets or sets a value to indicate a directory synchronization search, which returns all changes since a specified 501 /// state. 502 /// </devdoc> 503 [DefaultValue(null)] 504 public DirectorySynchronization DirectorySynchronization 505 { 506 get 507 { 508 // if user specifies dirsync search preference and search is executed 509 if (directorySynchronizationSpecified && searchResult != null) 510 { 511 _sync.ResetDirectorySynchronizationCookie(searchResult.DirsyncCookie); 512 } 513 return _sync; 514 } 515 516 set 517 { 518 // specify non-zero pagesize explicitly and also want dirsync 519 if (value != null) 520 { 521 if (PageSize != 0) 522 throw new ArgumentException(SR.DSBadPageSizeDirsync); 523 524 directorySynchronizationSpecified = true; 525 } 526 else 527 // user explicitly sets the value to default one and doesn't want to do dirsync 528 { 529 directorySynchronizationSpecified = false; 530 } 531 532 _sync = value; 533 } 534 } 535 536 /// <devdoc> 537 /// Gets or sets a value to indicate the search should use the LDAP virtual list view (VLV) 538 /// control. 539 /// </devdoc> 540 [DefaultValue(null)] 541 public DirectoryVirtualListView VirtualListView 542 { 543 get 544 { 545 // if user specifies dirsync search preference and search is executed 546 if (directoryVirtualListViewSpecified && searchResult != null) 547 { 548 DirectoryVirtualListView tempval = searchResult.VLVResponse; 549 _vlv.Offset = tempval.Offset; 550 _vlv.ApproximateTotal = tempval.ApproximateTotal; 551 _vlv.DirectoryVirtualListViewContext = tempval.DirectoryVirtualListViewContext; 552 if (_vlv.ApproximateTotal != 0) 553 _vlv.TargetPercentage = (int)((double)_vlv.Offset / _vlv.ApproximateTotal * 100); 554 else 555 _vlv.TargetPercentage = 0; 556 } 557 return _vlv; 558 } 559 set 560 { 561 // if user explicitly set CacheResults to true and also want to set VLV 562 if (value != null) 563 { 564 if (_cacheResultsSpecified == true && CacheResults == true) 565 throw new ArgumentException(SR.DSBadCacheResultsVLV); 566 567 directoryVirtualListViewSpecified = true; 568 // if user does not explicit specify cache results to true and also do vlv, then cache results is default to false 569 _cacheResults = false; 570 } 571 else 572 // user explicitly sets the value to default one and doesn't want to do vlv 573 { 574 directoryVirtualListViewSpecified = false; 575 } 576 577 _vlv = value; 578 } 579 } 580 581 /// <devdoc> 582 /// Executes the search and returns only the first entry that is found. 583 /// </devdoc> FindOne()584 public SearchResult FindOne() 585 { 586 DirectorySynchronization tempsync = null; 587 DirectoryVirtualListView tempvlv = null; 588 SearchResult resultEntry = null; 589 590 SearchResultCollection results = FindAll(false); 591 592 try 593 { 594 foreach (SearchResult entry in results) 595 { 596 // need to get the dirsync cookie 597 if (directorySynchronizationSpecified) 598 tempsync = DirectorySynchronization; 599 600 // need to get the vlv response 601 if (directoryVirtualListViewSpecified) 602 tempvlv = VirtualListView; 603 604 resultEntry = entry; 605 break; 606 } 607 } 608 finally 609 { 610 searchResult = null; 611 612 // still need to properly release the resource 613 results.Dispose(); 614 } 615 616 return resultEntry; 617 } 618 619 /// <devdoc> 620 /// Executes the search and returns a collection of the entries that are found. 621 /// </devdoc> FindAll()622 public SearchResultCollection FindAll() => FindAll(true); 623 FindAll(bool findMoreThanOne)624 private SearchResultCollection FindAll(bool findMoreThanOne) 625 { 626 searchResult = null; 627 628 DirectoryEntry clonedRoot = null; 629 if (_assertDefaultNamingContext == null) 630 { 631 clonedRoot = SearchRoot.CloneBrowsable(); 632 } 633 else 634 { 635 clonedRoot = SearchRoot.CloneBrowsable(); 636 } 637 638 UnsafeNativeMethods.IAds adsObject = clonedRoot.AdsObject; 639 if (!(adsObject is UnsafeNativeMethods.IDirectorySearch)) 640 throw new NotSupportedException(SR.Format(SR.DSSearchUnsupported , SearchRoot.Path)); 641 642 // this is a little bit hacky, but we need to perform a bind here, so we make sure the LDAP connection that we hold has more than 643 // one reference count, one by SearchResultCollection object, one by DirectorySearcher object. In this way, when user calls 644 // Dispose on SearchResultCollection, the connection is still there instead of reference count dropping to zero and being closed. 645 // It is especially important for virtuallistview case, in order to reuse the vlv response, the search must be performed on the same ldap connection 646 647 // only do it when vlv is used 648 if (directoryVirtualListViewSpecified) 649 { 650 SearchRoot.Bind(true); 651 } 652 653 UnsafeNativeMethods.IDirectorySearch adsSearch = (UnsafeNativeMethods.IDirectorySearch)adsObject; 654 SetSearchPreferences(adsSearch, findMoreThanOne); 655 656 string[] properties = null; 657 if (PropertiesToLoad.Count > 0) 658 { 659 if (!PropertiesToLoad.Contains("ADsPath")) 660 { 661 // if we don't get this property, we won't be able to return a list of DirectoryEntry objects! 662 PropertiesToLoad.Add("ADsPath"); 663 } 664 properties = new string[PropertiesToLoad.Count]; 665 PropertiesToLoad.CopyTo(properties, 0); 666 } 667 668 IntPtr resultsHandle; 669 if (properties != null) 670 adsSearch.ExecuteSearch(Filter, properties, properties.Length, out resultsHandle); 671 else 672 { 673 adsSearch.ExecuteSearch(Filter, null, -1, out resultsHandle); 674 properties = new string[0]; 675 } 676 677 SearchResultCollection result = new SearchResultCollection(clonedRoot, resultsHandle, properties, this); 678 searchResult = result; 679 return result; 680 } 681 SetSearchPreferences(UnsafeNativeMethods.IDirectorySearch adsSearch, bool findMoreThanOne)682 private unsafe void SetSearchPreferences(UnsafeNativeMethods.IDirectorySearch adsSearch, bool findMoreThanOne) 683 { 684 ArrayList prefList = new ArrayList(); 685 AdsSearchPreferenceInfo info; 686 687 // search scope 688 info = new AdsSearchPreferenceInfo(); 689 info.dwSearchPref = (int)AdsSearchPreferences.SEARCH_SCOPE; 690 info.vValue = new AdsValueHelper((int)SearchScope).GetStruct(); 691 prefList.Add(info); 692 693 // size limit 694 if (_sizeLimit != 0 || !findMoreThanOne) 695 { 696 info = new AdsSearchPreferenceInfo(); 697 info.dwSearchPref = (int)AdsSearchPreferences.SIZE_LIMIT; 698 info.vValue = new AdsValueHelper(findMoreThanOne ? SizeLimit : 1).GetStruct(); 699 prefList.Add(info); 700 } 701 702 // time limit 703 if (ServerTimeLimit >= new TimeSpan(0)) 704 { 705 info = new AdsSearchPreferenceInfo(); 706 info.dwSearchPref = (int)AdsSearchPreferences.TIME_LIMIT; 707 info.vValue = new AdsValueHelper((int)ServerTimeLimit.TotalSeconds).GetStruct(); 708 prefList.Add(info); 709 } 710 711 // propertyNamesOnly 712 info = new AdsSearchPreferenceInfo(); 713 info.dwSearchPref = (int)AdsSearchPreferences.ATTRIBTYPES_ONLY; 714 info.vValue = new AdsValueHelper(PropertyNamesOnly).GetStruct(); 715 prefList.Add(info); 716 717 // Timeout 718 if (ClientTimeout >= new TimeSpan(0)) 719 { 720 info = new AdsSearchPreferenceInfo(); 721 info.dwSearchPref = (int)AdsSearchPreferences.TIMEOUT; 722 info.vValue = new AdsValueHelper((int)ClientTimeout.TotalSeconds).GetStruct(); 723 prefList.Add(info); 724 } 725 726 // page size 727 if (PageSize != 0) 728 { 729 info = new AdsSearchPreferenceInfo(); 730 info.dwSearchPref = (int)AdsSearchPreferences.PAGESIZE; 731 info.vValue = new AdsValueHelper(PageSize).GetStruct(); 732 prefList.Add(info); 733 } 734 735 // page time limit 736 if (ServerPageTimeLimit >= new TimeSpan(0)) 737 { 738 info = new AdsSearchPreferenceInfo(); 739 info.dwSearchPref = (int)AdsSearchPreferences.PAGED_TIME_LIMIT; 740 info.vValue = new AdsValueHelper((int)ServerPageTimeLimit.TotalSeconds).GetStruct(); 741 prefList.Add(info); 742 } 743 744 // chase referrals 745 info = new AdsSearchPreferenceInfo(); 746 info.dwSearchPref = (int)AdsSearchPreferences.CHASE_REFERRALS; 747 info.vValue = new AdsValueHelper((int)ReferralChasing).GetStruct(); 748 prefList.Add(info); 749 750 // asynchronous 751 if (Asynchronous == true) 752 { 753 info = new AdsSearchPreferenceInfo(); 754 info.dwSearchPref = (int)AdsSearchPreferences.ASYNCHRONOUS; 755 info.vValue = new AdsValueHelper(Asynchronous).GetStruct(); 756 prefList.Add(info); 757 } 758 759 // tombstone 760 if (Tombstone == true) 761 { 762 info = new AdsSearchPreferenceInfo(); 763 info.dwSearchPref = (int)AdsSearchPreferences.TOMBSTONE; 764 info.vValue = new AdsValueHelper(Tombstone).GetStruct(); 765 prefList.Add(info); 766 } 767 768 // attributescopequery 769 if (_attributeScopeQuerySpecified) 770 { 771 info = new AdsSearchPreferenceInfo(); 772 info.dwSearchPref = (int)AdsSearchPreferences.ATTRIBUTE_QUERY; 773 info.vValue = new AdsValueHelper(AttributeScopeQuery, AdsType.ADSTYPE_CASE_IGNORE_STRING).GetStruct(); 774 prefList.Add(info); 775 } 776 777 // derefalias 778 if (DerefAlias != DereferenceAlias.Never) 779 { 780 info = new AdsSearchPreferenceInfo(); 781 info.dwSearchPref = (int)AdsSearchPreferences.DEREF_ALIASES; 782 info.vValue = new AdsValueHelper((int)DerefAlias).GetStruct(); 783 prefList.Add(info); 784 } 785 786 // securitymask 787 if (SecurityMasks != SecurityMasks.None) 788 { 789 info = new AdsSearchPreferenceInfo(); 790 info.dwSearchPref = (int)AdsSearchPreferences.SECURITY_MASK; 791 info.vValue = new AdsValueHelper((int)SecurityMasks).GetStruct(); 792 prefList.Add(info); 793 } 794 795 // extendeddn 796 if (ExtendedDN != ExtendedDN.None) 797 { 798 info = new AdsSearchPreferenceInfo(); 799 info.dwSearchPref = (int)AdsSearchPreferences.EXTENDED_DN; 800 info.vValue = new AdsValueHelper((int)ExtendedDN).GetStruct(); 801 prefList.Add(info); 802 } 803 804 // dirsync 805 if (directorySynchronizationSpecified) 806 { 807 info = new AdsSearchPreferenceInfo(); 808 info.dwSearchPref = (int)AdsSearchPreferences.DIRSYNC; 809 info.vValue = new AdsValueHelper(DirectorySynchronization.GetDirectorySynchronizationCookie(), AdsType.ADSTYPE_PROV_SPECIFIC).GetStruct(); 810 prefList.Add(info); 811 812 if (DirectorySynchronization.Option != DirectorySynchronizationOptions.None) 813 { 814 info = new AdsSearchPreferenceInfo(); 815 info.dwSearchPref = (int)AdsSearchPreferences.DIRSYNC_FLAG; 816 info.vValue = new AdsValueHelper((int)DirectorySynchronization.Option).GetStruct(); 817 prefList.Add(info); 818 } 819 } 820 821 IntPtr ptrToFree = (IntPtr)0; 822 IntPtr ptrVLVToFree = (IntPtr)0; 823 IntPtr ptrVLVContexToFree = (IntPtr)0; 824 825 try 826 { 827 // sort 828 if (Sort.PropertyName != null && Sort.PropertyName.Length > 0) 829 { 830 info = new AdsSearchPreferenceInfo(); 831 info.dwSearchPref = (int)AdsSearchPreferences.SORT_ON; 832 AdsSortKey sortKey = new AdsSortKey(); 833 sortKey.pszAttrType = Marshal.StringToCoTaskMemUni(Sort.PropertyName); 834 ptrToFree = sortKey.pszAttrType; // so we can free it later. 835 sortKey.pszReserved = (IntPtr)0; 836 sortKey.fReverseOrder = (Sort.Direction == SortDirection.Descending) ? -1 : 0; 837 byte[] sortKeyBytes = new byte[Marshal.SizeOf(sortKey)]; 838 Marshal.Copy((INTPTR_INTPTRCAST)(&sortKey), sortKeyBytes, 0, sortKeyBytes.Length); 839 info.vValue = new AdsValueHelper(sortKeyBytes, AdsType.ADSTYPE_PROV_SPECIFIC).GetStruct(); 840 prefList.Add(info); 841 } 842 843 // vlv 844 if (directoryVirtualListViewSpecified) 845 { 846 info = new AdsSearchPreferenceInfo(); 847 info.dwSearchPref = (int)AdsSearchPreferences.VLV; 848 AdsVLV vlvValue = new AdsVLV(); 849 vlvValue.beforeCount = _vlv.BeforeCount; 850 vlvValue.afterCount = _vlv.AfterCount; 851 vlvValue.offset = _vlv.Offset; 852 //we need to treat the empty string as null here 853 if (_vlv.Target.Length != 0) 854 vlvValue.target = Marshal.StringToCoTaskMemUni(_vlv.Target); 855 else 856 vlvValue.target = IntPtr.Zero; 857 ptrVLVToFree = vlvValue.target; 858 if (_vlv.DirectoryVirtualListViewContext == null) 859 { 860 vlvValue.contextIDlength = 0; 861 vlvValue.contextID = (IntPtr)0; 862 } 863 else 864 { 865 vlvValue.contextIDlength = _vlv.DirectoryVirtualListViewContext._context.Length; 866 vlvValue.contextID = Marshal.AllocCoTaskMem(vlvValue.contextIDlength); 867 ptrVLVContexToFree = vlvValue.contextID; 868 Marshal.Copy(_vlv.DirectoryVirtualListViewContext._context, 0, vlvValue.contextID, vlvValue.contextIDlength); 869 } 870 IntPtr vlvPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(AdsVLV))); 871 byte[] vlvBytes = new byte[Marshal.SizeOf(vlvValue)]; 872 try 873 { 874 Marshal.StructureToPtr(vlvValue, vlvPtr, false); 875 Marshal.Copy(vlvPtr, vlvBytes, 0, vlvBytes.Length); 876 } 877 finally 878 { 879 Marshal.FreeHGlobal(vlvPtr); 880 } 881 info.vValue = new AdsValueHelper(vlvBytes, AdsType.ADSTYPE_PROV_SPECIFIC).GetStruct(); 882 prefList.Add(info); 883 } 884 885 // cacheResults 886 if (_cacheResultsSpecified) 887 { 888 info = new AdsSearchPreferenceInfo(); 889 info.dwSearchPref = (int)AdsSearchPreferences.CACHE_RESULTS; 890 info.vValue = new AdsValueHelper(CacheResults).GetStruct(); 891 prefList.Add(info); 892 } 893 894 // 895 // now make the call 896 // 897 AdsSearchPreferenceInfo[] prefs = new AdsSearchPreferenceInfo[prefList.Count]; 898 for (int i = 0; i < prefList.Count; i++) 899 { 900 prefs[i] = (AdsSearchPreferenceInfo)prefList[i]; 901 } 902 903 DoSetSearchPrefs(adsSearch, prefs); 904 } 905 finally 906 { 907 if (ptrToFree != (IntPtr)0) 908 Marshal.FreeCoTaskMem(ptrToFree); 909 910 if (ptrVLVToFree != (IntPtr)0) 911 Marshal.FreeCoTaskMem(ptrVLVToFree); 912 913 if (ptrVLVContexToFree != (IntPtr)0) 914 Marshal.FreeCoTaskMem(ptrVLVContexToFree); 915 } 916 } 917 DoSetSearchPrefs(UnsafeNativeMethods.IDirectorySearch adsSearch, AdsSearchPreferenceInfo[] prefs)918 private static void DoSetSearchPrefs(UnsafeNativeMethods.IDirectorySearch adsSearch, AdsSearchPreferenceInfo[] prefs) 919 { 920 int structSize = Marshal.SizeOf(typeof(AdsSearchPreferenceInfo)); 921 IntPtr ptr = Marshal.AllocHGlobal((IntPtr)(structSize * prefs.Length)); 922 try 923 { 924 IntPtr tempPtr = ptr; 925 for (int i = 0; i < prefs.Length; i++) 926 { 927 Marshal.StructureToPtr(prefs[i], tempPtr, false); 928 tempPtr = IntPtr.Add(tempPtr, structSize); 929 } 930 931 adsSearch.SetSearchPreference(ptr, prefs.Length); 932 933 // Check for the result status for all preferences 934 tempPtr = ptr; 935 for (int i = 0; i < prefs.Length; i++) 936 { 937 int status = Marshal.ReadInt32(tempPtr, 32); 938 if (status != 0) 939 { 940 int prefIndex = prefs[i].dwSearchPref; 941 string property = ""; 942 switch (prefIndex) 943 { 944 case (int)AdsSearchPreferences.SEARCH_SCOPE: 945 property = "SearchScope"; 946 break; 947 case (int)AdsSearchPreferences.SIZE_LIMIT: 948 property = "SizeLimit"; 949 break; 950 case (int)AdsSearchPreferences.TIME_LIMIT: 951 property = "ServerTimeLimit"; 952 break; 953 case (int)AdsSearchPreferences.ATTRIBTYPES_ONLY: 954 property = "PropertyNamesOnly"; 955 break; 956 case (int)AdsSearchPreferences.TIMEOUT: 957 property = "ClientTimeout"; 958 break; 959 case (int)AdsSearchPreferences.PAGESIZE: 960 property = "PageSize"; 961 break; 962 case (int)AdsSearchPreferences.PAGED_TIME_LIMIT: 963 property = "ServerPageTimeLimit"; 964 break; 965 case (int)AdsSearchPreferences.CHASE_REFERRALS: 966 property = "ReferralChasing"; 967 break; 968 case (int)AdsSearchPreferences.SORT_ON: 969 property = "Sort"; 970 break; 971 case (int)AdsSearchPreferences.CACHE_RESULTS: 972 property = "CacheResults"; 973 break; 974 case (int)AdsSearchPreferences.ASYNCHRONOUS: 975 property = "Asynchronous"; 976 break; 977 case (int)AdsSearchPreferences.TOMBSTONE: 978 property = "Tombstone"; 979 break; 980 case (int)AdsSearchPreferences.ATTRIBUTE_QUERY: 981 property = "AttributeScopeQuery"; 982 break; 983 case (int)AdsSearchPreferences.DEREF_ALIASES: 984 property = "DerefAlias"; 985 break; 986 case (int)AdsSearchPreferences.SECURITY_MASK: 987 property = "SecurityMasks"; 988 break; 989 case (int)AdsSearchPreferences.EXTENDED_DN: 990 property = "ExtendedDn"; 991 break; 992 case (int)AdsSearchPreferences.DIRSYNC: 993 property = "DirectorySynchronization"; 994 break; 995 case (int)AdsSearchPreferences.DIRSYNC_FLAG: 996 property = "DirectorySynchronizationFlag"; 997 break; 998 case (int)AdsSearchPreferences.VLV: 999 property = "VirtualListView"; 1000 break; 1001 } 1002 throw new InvalidOperationException(SR.Format(SR.DSSearchPreferencesNotAccepted , property)); 1003 } 1004 1005 tempPtr = IntPtr.Add(tempPtr, structSize); 1006 } 1007 } 1008 finally 1009 { 1010 Marshal.FreeHGlobal(ptr); 1011 } 1012 } 1013 } 1014 } 1015