001/* 002 * Copyright 2017-2022 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2017-2022 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2017-2022 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.ldap.sdk.unboundidds.tools; 037 038 039 040import java.io.BufferedReader; 041import java.io.File; 042import java.io.FileOutputStream; 043import java.io.FileReader; 044import java.io.IOException; 045import java.io.OutputStream; 046import java.io.PrintStream; 047import java.util.ArrayList; 048import java.util.Arrays; 049import java.util.Collections; 050import java.util.EnumSet; 051import java.util.Iterator; 052import java.util.LinkedHashMap; 053import java.util.List; 054import java.util.Map; 055import java.util.Set; 056import java.util.StringTokenizer; 057import java.util.concurrent.atomic.AtomicLong; 058import java.util.zip.GZIPOutputStream; 059 060import com.unboundid.asn1.ASN1OctetString; 061import com.unboundid.ldap.sdk.Control; 062import com.unboundid.ldap.sdk.DN; 063import com.unboundid.ldap.sdk.DereferencePolicy; 064import com.unboundid.ldap.sdk.ExtendedResult; 065import com.unboundid.ldap.sdk.Filter; 066import com.unboundid.ldap.sdk.LDAPConnectionOptions; 067import com.unboundid.ldap.sdk.LDAPConnection; 068import com.unboundid.ldap.sdk.LDAPConnectionPool; 069import com.unboundid.ldap.sdk.LDAPException; 070import com.unboundid.ldap.sdk.LDAPResult; 071import com.unboundid.ldap.sdk.LDAPSearchException; 072import com.unboundid.ldap.sdk.LDAPURL; 073import com.unboundid.ldap.sdk.ResultCode; 074import com.unboundid.ldap.sdk.SearchRequest; 075import com.unboundid.ldap.sdk.SearchResult; 076import com.unboundid.ldap.sdk.SearchScope; 077import com.unboundid.ldap.sdk.UnsolicitedNotificationHandler; 078import com.unboundid.ldap.sdk.Version; 079import com.unboundid.ldap.sdk.controls.AssertionRequestControl; 080import com.unboundid.ldap.sdk.controls.AuthorizationIdentityRequestControl; 081import com.unboundid.ldap.sdk.controls.DraftLDUPSubentriesRequestControl; 082import com.unboundid.ldap.sdk.controls.ManageDsaITRequestControl; 083import com.unboundid.ldap.sdk.controls.MatchedValuesFilter; 084import com.unboundid.ldap.sdk.controls.MatchedValuesRequestControl; 085import com.unboundid.ldap.sdk.controls.PersistentSearchChangeType; 086import com.unboundid.ldap.sdk.controls.PersistentSearchRequestControl; 087import com.unboundid.ldap.sdk.controls.ProxiedAuthorizationV1RequestControl; 088import com.unboundid.ldap.sdk.controls.ProxiedAuthorizationV2RequestControl; 089import com.unboundid.ldap.sdk.controls.RFC3672SubentriesRequestControl; 090import com.unboundid.ldap.sdk.controls.ServerSideSortRequestControl; 091import com.unboundid.ldap.sdk.controls.SimplePagedResultsControl; 092import com.unboundid.ldap.sdk.controls.SortKey; 093import com.unboundid.ldap.sdk.controls.VirtualListViewRequestControl; 094import com.unboundid.ldap.sdk.persist.PersistUtils; 095import com.unboundid.ldap.sdk.transformations.EntryTransformation; 096import com.unboundid.ldap.sdk.transformations.ExcludeAttributeTransformation; 097import com.unboundid.ldap.sdk.transformations.MoveSubtreeTransformation; 098import com.unboundid.ldap.sdk.transformations.RedactAttributeTransformation; 099import com.unboundid.ldap.sdk.transformations.RenameAttributeTransformation; 100import com.unboundid.ldap.sdk.transformations.ScrambleAttributeTransformation; 101import com.unboundid.ldap.sdk.unboundidds.controls.AccountUsableRequestControl; 102import com.unboundid.ldap.sdk.unboundidds.controls.ExcludeBranchRequestControl; 103import com.unboundid.ldap.sdk.unboundidds.controls. 104 GetAuthorizationEntryRequestControl; 105import com.unboundid.ldap.sdk.unboundidds.controls. 106 GetBackendSetIDRequestControl; 107import com.unboundid.ldap.sdk.unboundidds.controls. 108 GetEffectiveRightsRequestControl; 109import com.unboundid.ldap.sdk.unboundidds.controls. 110 GetRecentLoginHistoryRequestControl; 111import com.unboundid.ldap.sdk.unboundidds.controls.GetServerIDRequestControl; 112import com.unboundid.ldap.sdk.unboundidds.controls. 113 GetUserResourceLimitsRequestControl; 114import com.unboundid.ldap.sdk.unboundidds.controls.JoinBaseDN; 115import com.unboundid.ldap.sdk.unboundidds.controls.JoinRequestControl; 116import com.unboundid.ldap.sdk.unboundidds.controls.JoinRequestValue; 117import com.unboundid.ldap.sdk.unboundidds.controls.JoinRule; 118import com.unboundid.ldap.sdk.unboundidds.controls. 119 MatchingEntryCountRequestControl; 120import com.unboundid.ldap.sdk.unboundidds.controls. 121 MatchingEntryCountRequestControlProperties; 122import com.unboundid.ldap.sdk.unboundidds.controls. 123 OperationPurposeRequestControl; 124import com.unboundid.ldap.sdk.unboundidds.controls. 125 OverrideSearchLimitsRequestControl; 126import com.unboundid.ldap.sdk.unboundidds.controls.PasswordPolicyRequestControl; 127import com.unboundid.ldap.sdk.unboundidds.controls. 128 PermitUnindexedSearchRequestControl; 129import com.unboundid.ldap.sdk.unboundidds.controls. 130 RealAttributesOnlyRequestControl; 131import com.unboundid.ldap.sdk.unboundidds.controls. 132 RejectUnindexedSearchRequestControl; 133import com.unboundid.ldap.sdk.unboundidds.controls. 134 ReturnConflictEntriesRequestControl; 135import com.unboundid.ldap.sdk.unboundidds.controls. 136 RouteToBackendSetRequestControl; 137import com.unboundid.ldap.sdk.unboundidds.controls.RouteToServerRequestControl; 138import com.unboundid.ldap.sdk.unboundidds.controls. 139 SoftDeletedEntryAccessRequestControl; 140import com.unboundid.ldap.sdk.unboundidds.controls. 141 SuppressOperationalAttributeUpdateRequestControl; 142import com.unboundid.ldap.sdk.unboundidds.controls.SuppressType; 143import com.unboundid.ldap.sdk.unboundidds.controls. 144 VirtualAttributesOnlyRequestControl; 145import com.unboundid.ldap.sdk.unboundidds.extensions. 146 StartAdministrativeSessionExtendedRequest; 147import com.unboundid.ldap.sdk.unboundidds.extensions. 148 StartAdministrativeSessionPostConnectProcessor; 149import com.unboundid.ldif.LDIFWriter; 150import com.unboundid.util.Debug; 151import com.unboundid.util.FilterFileReader; 152import com.unboundid.util.FixedRateBarrier; 153import com.unboundid.util.LDAPCommandLineTool; 154import com.unboundid.util.NotNull; 155import com.unboundid.util.Nullable; 156import com.unboundid.util.OutputFormat; 157import com.unboundid.util.PassphraseEncryptedOutputStream; 158import com.unboundid.util.StaticUtils; 159import com.unboundid.util.TeeOutputStream; 160import com.unboundid.util.ThreadSafety; 161import com.unboundid.util.ThreadSafetyLevel; 162import com.unboundid.util.args.ArgumentException; 163import com.unboundid.util.args.ArgumentParser; 164import com.unboundid.util.args.BooleanArgument; 165import com.unboundid.util.args.BooleanValueArgument; 166import com.unboundid.util.args.ControlArgument; 167import com.unboundid.util.args.DNArgument; 168import com.unboundid.util.args.FileArgument; 169import com.unboundid.util.args.FilterArgument; 170import com.unboundid.util.args.IntegerArgument; 171import com.unboundid.util.args.ScopeArgument; 172import com.unboundid.util.args.StringArgument; 173 174import static com.unboundid.ldap.sdk.unboundidds.tools.ToolMessages.*; 175 176 177 178/** 179 * This class provides an implementation of an LDAP command-line tool that may 180 * be used to issue searches to a directory server. Matching entries will be 181 * output in the LDAP data interchange format (LDIF), to standard output and/or 182 * to a specified file. This is a much more full-featured tool than the 183 * {@link com.unboundid.ldap.sdk.examples.LDAPSearch} tool, and includes a 184 * number of features only intended for use with Ping Identity, UnboundID, and 185 * Nokia/Alcatel-Lucent 8661 server products. 186 * <BR> 187 * <BLOCKQUOTE> 188 * <B>NOTE:</B> This class, and other classes within the 189 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 190 * supported for use against Ping Identity, UnboundID, and 191 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 192 * for proprietary functionality or for external specifications that are not 193 * considered stable or mature enough to be guaranteed to work in an 194 * interoperable way with other types of LDAP servers. 195 * </BLOCKQUOTE> 196 */ 197@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 198public final class LDAPSearch 199 extends LDAPCommandLineTool 200 implements UnsolicitedNotificationHandler 201{ 202 /** 203 * The column at which to wrap long lines. 204 */ 205 private static int WRAP_COLUMN = StaticUtils.TERMINAL_WIDTH_COLUMNS - 1; 206 207 208 209 // The set of arguments supported by this program. 210 @Nullable private BooleanArgument accountUsable = null; 211 @Nullable private BooleanArgument authorizationIdentity = null; 212 @Nullable private BooleanArgument compressOutput = null; 213 @Nullable private BooleanArgument continueOnError = null; 214 @Nullable private BooleanArgument countEntries = null; 215 @Nullable private BooleanArgument dontWrap = null; 216 @Nullable private BooleanArgument draftLDUPSubentries = null; 217 @Nullable private BooleanArgument dryRun = null; 218 @Nullable private BooleanArgument encryptOutput = null; 219 @Nullable private BooleanArgument followReferrals = null; 220 @Nullable private BooleanArgument getBackendSetID = null; 221 @Nullable private BooleanArgument getServerID = null; 222 @Nullable private BooleanArgument getRecentLoginHistory = null; 223 @Nullable private BooleanArgument hideRedactedValueCount = null; 224 @Nullable private BooleanArgument getUserResourceLimits = null; 225 @Nullable private BooleanArgument includeReplicationConflictEntries = null; 226 @Nullable private BooleanArgument joinRequireMatch = null; 227 @Nullable private BooleanArgument manageDsaIT = null; 228 @Nullable private BooleanArgument permitUnindexedSearch = null; 229 @Nullable private BooleanArgument realAttributesOnly = null; 230 @Nullable private BooleanArgument rejectUnindexedSearch = null; 231 @Nullable private BooleanArgument requireMatch = null; 232 @Nullable private BooleanArgument retryFailedOperations = null; 233 @Nullable private BooleanArgument separateOutputFilePerSearch = null; 234 @Nullable private BooleanArgument suppressBase64EncodedValueComments = null; 235 @Nullable private BooleanArgument teeResultsToStandardOut = null; 236 @Nullable private BooleanArgument useAdministrativeSession = null; 237 @Nullable private BooleanArgument usePasswordPolicyControl = null; 238 @Nullable private BooleanArgument terse = null; 239 @Nullable private BooleanArgument typesOnly = null; 240 @Nullable private BooleanArgument verbose = null; 241 @Nullable private BooleanArgument virtualAttributesOnly = null; 242 @Nullable private BooleanValueArgument rfc3672Subentries = null; 243 @Nullable private ControlArgument bindControl = null; 244 @Nullable private ControlArgument searchControl = null; 245 @Nullable private DNArgument baseDN = null; 246 @Nullable private DNArgument excludeBranch = null; 247 @Nullable private DNArgument moveSubtreeFrom = null; 248 @Nullable private DNArgument moveSubtreeTo = null; 249 @Nullable private DNArgument proxyV1As = null; 250 @Nullable private FileArgument encryptionPassphraseFile = null; 251 @Nullable private FileArgument filterFile = null; 252 @Nullable private FileArgument ldapURLFile = null; 253 @Nullable private FileArgument outputFile = null; 254 @Nullable private FilterArgument assertionFilter = null; 255 @Nullable private FilterArgument filter = null; 256 @Nullable private FilterArgument joinFilter = null; 257 @Nullable private FilterArgument matchedValuesFilter = null; 258 @Nullable private IntegerArgument joinSizeLimit = null; 259 @Nullable private IntegerArgument ratePerSecond = null; 260 @Nullable private IntegerArgument scrambleRandomSeed = null; 261 @Nullable private IntegerArgument simplePageSize = null; 262 @Nullable private IntegerArgument sizeLimit = null; 263 @Nullable private IntegerArgument timeLimitSeconds = null; 264 @Nullable private IntegerArgument wrapColumn = null; 265 @Nullable private ScopeArgument joinScope = null; 266 @Nullable private ScopeArgument scope = null; 267 @Nullable private StringArgument dereferencePolicy = null; 268 @Nullable private StringArgument excludeAttribute = null; 269 @Nullable private StringArgument getAuthorizationEntryAttribute = null; 270 @Nullable private StringArgument getEffectiveRightsAttribute = null; 271 @Nullable private StringArgument getEffectiveRightsAuthzID = null; 272 @Nullable private StringArgument includeSoftDeletedEntries = null; 273 @Nullable private StringArgument joinBaseDN = null; 274 @Nullable private StringArgument joinRequestedAttribute = null; 275 @Nullable private StringArgument joinRule = null; 276 @Nullable private StringArgument matchingEntryCountControl = null; 277 @Nullable private StringArgument operationPurpose = null; 278 @Nullable private StringArgument outputFormat = null; 279 @Nullable private StringArgument overrideSearchLimit = null; 280 @Nullable private StringArgument persistentSearch = null; 281 @Nullable private StringArgument proxyAs = null; 282 @Nullable private StringArgument redactAttribute = null; 283 @Nullable private StringArgument renameAttributeFrom = null; 284 @Nullable private StringArgument renameAttributeTo = null; 285 @Nullable private StringArgument requestedAttribute = null; 286 @Nullable private StringArgument routeToBackendSet = null; 287 @Nullable private StringArgument routeToServer = null; 288 @Nullable private StringArgument scrambleAttribute = null; 289 @Nullable private StringArgument scrambleJSONField = null; 290 @Nullable private StringArgument sortOrder = null; 291 @Nullable private StringArgument suppressOperationalAttributeUpdates = null; 292 @Nullable private StringArgument virtualListView = null; 293 294 // The argument parser used by this tool. 295 @Nullable private volatile ArgumentParser parser = null; 296 297 // Controls that should be sent to the server but need special validation. 298 @Nullable private volatile JoinRequestControl joinRequestControl = null; 299 @NotNull private final List<RouteToBackendSetRequestControl> 300 routeToBackendSetRequestControls = new ArrayList<>(10); 301 @Nullable private volatile MatchedValuesRequestControl 302 matchedValuesRequestControl = null; 303 @Nullable private volatile MatchingEntryCountRequestControl 304 matchingEntryCountRequestControl = null; 305 @Nullable private volatile OverrideSearchLimitsRequestControl 306 overrideSearchLimitsRequestControl = null; 307 @Nullable private volatile PersistentSearchRequestControl 308 persistentSearchRequestControl = null; 309 @Nullable private volatile ServerSideSortRequestControl sortRequestControl = 310 null; 311 @Nullable private volatile VirtualListViewRequestControl vlvRequestControl = 312 null; 313 314 // Other values decoded from arguments. 315 @Nullable private volatile DereferencePolicy derefPolicy = null; 316 317 // The print streams used for standard output and error. 318 @NotNull private final AtomicLong outputFileCounter = new AtomicLong(1); 319 @Nullable private volatile PrintStream errStream = null; 320 @Nullable private volatile PrintStream outStream = null; 321 322 // The LDAP result writer for this tool. 323 @NotNull private volatile LDAPResultWriter resultWriter; 324 325 // The list of entry transformations to apply. 326 @Nullable private volatile List<EntryTransformation> entryTransformations = 327 null; 328 329 // The encryption passphrase to use if the output is to be encrypted. 330 @Nullable private String encryptionPassphrase = null; 331 332 333 334 /** 335 * Runs this tool with the provided command-line arguments. It will use the 336 * JVM-default streams for standard input, output, and error. 337 * 338 * @param args The command-line arguments to provide to this program. 339 */ 340 public static void main(@NotNull final String... args) 341 { 342 final ResultCode resultCode = main(System.out, System.err, args); 343 if (resultCode != ResultCode.SUCCESS) 344 { 345 System.exit(Math.min(resultCode.intValue(), 255)); 346 } 347 } 348 349 350 351 /** 352 * Runs this tool with the provided streams and command-line arguments. 353 * 354 * @param out The output stream to use for standard output. If this is 355 * {@code null}, then standard output will be suppressed. 356 * @param err The output stream to use for standard error. If this is 357 * {@code null}, then standard error will be suppressed. 358 * @param args The command-line arguments provided to this program. 359 * 360 * @return The result code obtained when running the tool. Any result code 361 * other than {@link ResultCode#SUCCESS} indicates an error. 362 */ 363 @NotNull() 364 public static ResultCode main(@Nullable final OutputStream out, 365 @Nullable final OutputStream err, 366 @NotNull final String... args) 367 { 368 final LDAPSearch tool = new LDAPSearch(out, err); 369 return tool.runTool(args); 370 } 371 372 373 374 /** 375 * Creates a new instance of this tool with the provided streams. 376 * 377 * @param out The output stream to use for standard output. If this is 378 * {@code null}, then standard output will be suppressed. 379 * @param err The output stream to use for standard error. If this is 380 * {@code null}, then standard error will be suppressed. 381 */ 382 public LDAPSearch(@Nullable final OutputStream out, 383 @Nullable final OutputStream err) 384 { 385 super(out, err); 386 387 resultWriter = new LDIFLDAPResultWriter(getOut(), WRAP_COLUMN); 388 } 389 390 391 392 /** 393 * {@inheritDoc} 394 */ 395 @Override() 396 @NotNull() 397 public String getToolName() 398 { 399 return "ldapsearch"; 400 } 401 402 403 404 /** 405 * {@inheritDoc} 406 */ 407 @Override() 408 @NotNull() 409 public String getToolDescription() 410 { 411 return INFO_LDAPSEARCH_TOOL_DESCRIPTION.get(); 412 } 413 414 415 416 /** 417 * {@inheritDoc} 418 */ 419 @Override() 420 @NotNull() 421 public List<String> getAdditionalDescriptionParagraphs() 422 { 423 return Arrays.asList( 424 INFO_LDAPSEARCH_ADDITIONAL_DESCRIPTION_PARAGRAPH_1.get(), 425 INFO_LDAPSEARCH_ADDITIONAL_DESCRIPTION_PARAGRAPH_2.get()); 426 } 427 428 429 430 /** 431 * {@inheritDoc} 432 */ 433 @Override() 434 @NotNull() 435 public String getToolVersion() 436 { 437 return Version.NUMERIC_VERSION_STRING; 438 } 439 440 441 442 /** 443 * {@inheritDoc} 444 */ 445 @Override() 446 public int getMinTrailingArguments() 447 { 448 return 0; 449 } 450 451 452 453 /** 454 * {@inheritDoc} 455 */ 456 @Override() 457 public int getMaxTrailingArguments() 458 { 459 return -1; 460 } 461 462 463 464 /** 465 * {@inheritDoc} 466 */ 467 @Override() 468 @NotNull() 469 public String getTrailingArgumentsPlaceholder() 470 { 471 return INFO_LDAPSEARCH_TRAILING_ARGS_PLACEHOLDER.get(); 472 } 473 474 475 476 /** 477 * {@inheritDoc} 478 */ 479 @Override() 480 public boolean supportsInteractiveMode() 481 { 482 return true; 483 } 484 485 486 487 /** 488 * {@inheritDoc} 489 */ 490 @Override() 491 public boolean defaultsToInteractiveMode() 492 { 493 return true; 494 } 495 496 497 498 /** 499 * {@inheritDoc} 500 */ 501 @Override() 502 public boolean supportsPropertiesFile() 503 { 504 return true; 505 } 506 507 508 509 /** 510 * {@inheritDoc} 511 */ 512 @Override() 513 protected boolean defaultToPromptForBindPassword() 514 { 515 return true; 516 } 517 518 519 520 /** 521 * {@inheritDoc} 522 */ 523 @Override() 524 protected boolean includeAlternateLongIdentifiers() 525 { 526 return true; 527 } 528 529 530 531 /** 532 * {@inheritDoc} 533 */ 534 @Override() 535 protected boolean supportsSSLDebugging() 536 { 537 return true; 538 } 539 540 541 542 /** 543 * {@inheritDoc} 544 */ 545 @Override() 546 @NotNull() 547 protected Set<Character> getSuppressedShortIdentifiers() 548 { 549 return Collections.singleton('T'); 550 } 551 552 553 554 /** 555 * {@inheritDoc} 556 */ 557 @Override() 558 public void addNonLDAPArguments(@NotNull final ArgumentParser parser) 559 throws ArgumentException 560 { 561 this.parser = parser; 562 563 baseDN = new DNArgument('b', "baseDN", false, 1, null, 564 INFO_LDAPSEARCH_ARG_DESCRIPTION_BASE_DN.get()); 565 baseDN.addLongIdentifier("base-dn", true); 566 baseDN.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 567 parser.addArgument(baseDN); 568 569 scope = new ScopeArgument('s', "scope", false, null, 570 INFO_LDAPSEARCH_ARG_DESCRIPTION_SCOPE.get(), SearchScope.SUB); 571 scope.addLongIdentifier("searchScope", true); 572 scope.addLongIdentifier("search-scope", true); 573 scope.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 574 parser.addArgument(scope); 575 576 sizeLimit = new IntegerArgument('z', "sizeLimit", false, 1, null, 577 INFO_LDAPSEARCH_ARG_DESCRIPTION_SIZE_LIMIT.get(), 0, 578 Integer.MAX_VALUE, 0); 579 sizeLimit.addLongIdentifier("size-limit", true); 580 sizeLimit.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 581 parser.addArgument(sizeLimit); 582 583 timeLimitSeconds = new IntegerArgument('l', "timeLimitSeconds", false, 1, 584 null, INFO_LDAPSEARCH_ARG_DESCRIPTION_TIME_LIMIT.get(), 0, 585 Integer.MAX_VALUE, 0); 586 timeLimitSeconds.addLongIdentifier("timeLimit", true); 587 timeLimitSeconds.addLongIdentifier("time-limit-seconds", true); 588 timeLimitSeconds.addLongIdentifier("time-limit", true); 589 timeLimitSeconds.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 590 parser.addArgument(timeLimitSeconds); 591 592 final Set<String> derefAllowedValues = 593 StaticUtils.setOf("never", "always", "search", "find"); 594 dereferencePolicy = new StringArgument('a', "dereferencePolicy", false, 1, 595 "{never|always|search|find}", 596 INFO_LDAPSEARCH_ARG_DESCRIPTION_DEREFERENCE_POLICY.get(), 597 derefAllowedValues, "never"); 598 dereferencePolicy.addLongIdentifier("dereference-policy", true); 599 dereferencePolicy.setArgumentGroupName( 600 INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 601 parser.addArgument(dereferencePolicy); 602 603 typesOnly = new BooleanArgument('A', "typesOnly", 1, 604 INFO_LDAPSEARCH_ARG_DESCRIPTION_TYPES_ONLY.get()); 605 typesOnly.addLongIdentifier("types-only", true); 606 typesOnly.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 607 parser.addArgument(typesOnly); 608 609 requestedAttribute = new StringArgument(null, "requestedAttribute", false, 610 0, INFO_PLACEHOLDER_ATTR.get(), 611 INFO_LDAPSEARCH_ARG_DESCRIPTION_REQUESTED_ATTR.get()); 612 requestedAttribute.addLongIdentifier("requested-attribute", true); 613 requestedAttribute.setArgumentGroupName( 614 INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 615 parser.addArgument(requestedAttribute); 616 617 filter = new FilterArgument(null, "filter", false, 0, 618 INFO_PLACEHOLDER_FILTER.get(), 619 INFO_LDAPSEARCH_ARG_DESCRIPTION_FILTER.get()); 620 filter.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 621 parser.addArgument(filter); 622 623 filterFile = new FileArgument('f', "filterFile", false, 0, null, 624 INFO_LDAPSEARCH_ARG_DESCRIPTION_FILTER_FILE.get(), true, true, 625 true, false); 626 filterFile.addLongIdentifier("filename", true); 627 filterFile.addLongIdentifier("filter-file", true); 628 filterFile.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 629 parser.addArgument(filterFile); 630 631 ldapURLFile = new FileArgument(null, "ldapURLFile", false, 0, null, 632 INFO_LDAPSEARCH_ARG_DESCRIPTION_LDAP_URL_FILE.get(), true, true, 633 true, false); 634 ldapURLFile.addLongIdentifier("ldap-url-file", true); 635 ldapURLFile.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 636 parser.addArgument(ldapURLFile); 637 638 followReferrals = new BooleanArgument(null, "followReferrals", 1, 639 INFO_LDAPSEARCH_ARG_DESCRIPTION_FOLLOW_REFERRALS.get()); 640 followReferrals.addLongIdentifier("follow-referrals", true); 641 followReferrals.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 642 parser.addArgument(followReferrals); 643 644 retryFailedOperations = new BooleanArgument(null, "retryFailedOperations", 645 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_RETRY_FAILED_OPERATIONS.get()); 646 retryFailedOperations.addLongIdentifier("retry-failed-operations", true); 647 retryFailedOperations.setArgumentGroupName( 648 INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 649 parser.addArgument(retryFailedOperations); 650 651 continueOnError = new BooleanArgument('c', "continueOnError", 1, 652 INFO_LDAPSEARCH_ARG_DESCRIPTION_CONTINUE_ON_ERROR.get()); 653 continueOnError.addLongIdentifier("continue-on-error", true); 654 continueOnError.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 655 parser.addArgument(continueOnError); 656 657 ratePerSecond = new IntegerArgument('r', "ratePerSecond", false, 1, 658 INFO_PLACEHOLDER_NUM.get(), 659 INFO_LDAPSEARCH_ARG_DESCRIPTION_RATE_PER_SECOND.get(), 1, 660 Integer.MAX_VALUE); 661 ratePerSecond.addLongIdentifier("rate-per-second", true); 662 ratePerSecond.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 663 parser.addArgument(ratePerSecond); 664 665 useAdministrativeSession = new BooleanArgument(null, 666 "useAdministrativeSession", 1, 667 INFO_LDAPSEARCH_ARG_DESCRIPTION_USE_ADMIN_SESSION.get()); 668 useAdministrativeSession.addLongIdentifier("use-administrative-session", 669 true); 670 useAdministrativeSession.setArgumentGroupName( 671 INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 672 parser.addArgument(useAdministrativeSession); 673 674 dryRun = new BooleanArgument('n', "dryRun", 1, 675 INFO_LDAPSEARCH_ARG_DESCRIPTION_DRY_RUN.get()); 676 dryRun.addLongIdentifier("dry-run", true); 677 dryRun.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 678 parser.addArgument(dryRun); 679 680 wrapColumn = new IntegerArgument(null, "wrapColumn", false, 1, null, 681 INFO_LDAPSEARCH_ARG_DESCRIPTION_WRAP_COLUMN.get(), 0, 682 Integer.MAX_VALUE); 683 wrapColumn.addLongIdentifier("wrap-column", true); 684 wrapColumn.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); 685 parser.addArgument(wrapColumn); 686 687 dontWrap = new BooleanArgument('T', "dontWrap", 1, 688 INFO_LDAPSEARCH_ARG_DESCRIPTION_DONT_WRAP.get()); 689 dontWrap.addLongIdentifier("doNotWrap", true); 690 dontWrap.addLongIdentifier("dont-wrap", true); 691 dontWrap.addLongIdentifier("do-not-wrap", true); 692 dontWrap.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); 693 parser.addArgument(dontWrap); 694 695 suppressBase64EncodedValueComments = new BooleanArgument(null, 696 "suppressBase64EncodedValueComments", 1, 697 INFO_LDAPSEARCH_ARG_DESCRIPTION_SUPPRESS_BASE64_COMMENTS.get()); 698 suppressBase64EncodedValueComments.addLongIdentifier( 699 "suppress-base64-encoded-value-comments", true); 700 suppressBase64EncodedValueComments.setArgumentGroupName( 701 INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); 702 parser.addArgument(suppressBase64EncodedValueComments); 703 704 countEntries = new BooleanArgument(null, "countEntries", 1, 705 INFO_LDAPSEARCH_ARG_DESCRIPTION_COUNT_ENTRIES.get()); 706 countEntries.addLongIdentifier("count-entries", true); 707 countEntries.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 708 countEntries.setHidden(true); 709 parser.addArgument(countEntries); 710 711 outputFile = new FileArgument(null, "outputFile", false, 1, null, 712 INFO_LDAPSEARCH_ARG_DESCRIPTION_OUTPUT_FILE.get(), false, true, true, 713 false); 714 outputFile.addLongIdentifier("output-file", true); 715 outputFile.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); 716 parser.addArgument(outputFile); 717 718 compressOutput = new BooleanArgument(null, "compressOutput", 1, 719 INFO_LDAPSEARCH_ARG_DESCRIPTION_COMPRESS_OUTPUT.get()); 720 compressOutput.addLongIdentifier("compress-output", true); 721 compressOutput.addLongIdentifier("compress", true); 722 compressOutput.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); 723 parser.addArgument(compressOutput); 724 725 encryptOutput = new BooleanArgument(null, "encryptOutput", 1, 726 INFO_LDAPSEARCH_ARG_DESCRIPTION_ENCRYPT_OUTPUT.get()); 727 encryptOutput.addLongIdentifier("encrypt-output", true); 728 encryptOutput.addLongIdentifier("encrypt", true); 729 encryptOutput.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); 730 parser.addArgument(encryptOutput); 731 732 encryptionPassphraseFile = new FileArgument(null, 733 "encryptionPassphraseFile", false, 1, null, 734 INFO_LDAPSEARCH_ARG_DESCRIPTION_ENCRYPTION_PW_FILE.get(), true, true, 735 true, false); 736 encryptionPassphraseFile.addLongIdentifier("encryption-passphrase-file", 737 true); 738 encryptionPassphraseFile.addLongIdentifier("encryptionPasswordFile", true); 739 encryptionPassphraseFile.addLongIdentifier("encryption-password-file", 740 true); 741 encryptionPassphraseFile.setArgumentGroupName( 742 INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); 743 parser.addArgument(encryptionPassphraseFile); 744 745 separateOutputFilePerSearch = new BooleanArgument(null, 746 "separateOutputFilePerSearch", 1, 747 INFO_LDAPSEARCH_ARG_DESCRIPTION_SEPARATE_OUTPUT_FILES.get()); 748 separateOutputFilePerSearch.addLongIdentifier( 749 "separate-output-file-per-search", true); 750 separateOutputFilePerSearch.setArgumentGroupName( 751 INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); 752 parser.addArgument(separateOutputFilePerSearch); 753 754 teeResultsToStandardOut = new BooleanArgument(null, 755 "teeResultsToStandardOut", 1, 756 INFO_LDAPSEARCH_ARG_DESCRIPTION_TEE.get("outputFile")); 757 teeResultsToStandardOut.addLongIdentifier( 758 "tee-results-to-standard-out", true); 759 teeResultsToStandardOut.setArgumentGroupName( 760 INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); 761 parser.addArgument(teeResultsToStandardOut); 762 763 final Set<String> outputFormatAllowedValues = StaticUtils.setOf("ldif", 764 "json", "csv", "multi-valued-csv", "tab-delimited", 765 "multi-valued-tab-delimited", "dns-only", "values-only"); 766 outputFormat = new StringArgument(null, "outputFormat", false, 1, 767 "{ldif|json|csv|multi-valued-csv|tab-delimited|" + 768 "multi-valued-tab-delimited|dns-only|values-only}", 769 INFO_LDAPSEARCH_ARG_DESCRIPTION_OUTPUT_FORMAT.get( 770 requestedAttribute.getIdentifierString(), 771 ldapURLFile.getIdentifierString()), 772 outputFormatAllowedValues, "ldif"); 773 outputFormat.addLongIdentifier("output-format", true); 774 outputFormat.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); 775 parser.addArgument(outputFormat); 776 777 requireMatch = new BooleanArgument(null, "requireMatch", 1, 778 INFO_LDAPSEARCH_ARG_DESCRIPTION_REQUIRE_MATCH.get( 779 getToolName(), 780 String.valueOf(ResultCode.NO_RESULTS_RETURNED))); 781 requireMatch.addLongIdentifier("require-match", true); 782 requireMatch.addLongIdentifier("requireMatchingEntry", true); 783 requireMatch.addLongIdentifier("require-matching-entry", true); 784 requireMatch.addLongIdentifier("requireMatchingEntries", true); 785 requireMatch.addLongIdentifier("require-matching-entries", true); 786 requireMatch.addLongIdentifier("requireEntry", true); 787 requireMatch.addLongIdentifier("require-entry", true); 788 requireMatch.addLongIdentifier("requireEntries", true); 789 requireMatch.addLongIdentifier("require-entries", true); 790 requireMatch.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); 791 parser.addArgument(requireMatch); 792 793 terse = new BooleanArgument(null, "terse", 1, 794 INFO_LDAPSEARCH_ARG_DESCRIPTION_TERSE.get()); 795 terse.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); 796 parser.addArgument(terse); 797 798 verbose = new BooleanArgument('v', "verbose", 1, 799 INFO_LDAPSEARCH_ARG_DESCRIPTION_VERBOSE.get()); 800 verbose.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); 801 parser.addArgument(verbose); 802 803 bindControl = new ControlArgument(null, "bindControl", false, 0, null, 804 INFO_LDAPSEARCH_ARG_DESCRIPTION_BIND_CONTROL.get()); 805 bindControl.addLongIdentifier("bind-control", true); 806 bindControl.setArgumentGroupName( 807 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 808 parser.addArgument(bindControl); 809 810 searchControl = new ControlArgument('J', "control", false, 0, null, 811 INFO_LDAPSEARCH_ARG_DESCRIPTION_SEARCH_CONTROL.get()); 812 searchControl.addLongIdentifier("searchControl", true); 813 searchControl.addLongIdentifier("search-control", true); 814 searchControl.setArgumentGroupName( 815 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 816 parser.addArgument(searchControl); 817 818 authorizationIdentity = new BooleanArgument('E', "authorizationIdentity", 819 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_AUTHZ_IDENTITY.get()); 820 authorizationIdentity.addLongIdentifier("reportAuthzID", true); 821 authorizationIdentity.addLongIdentifier("authorization-identity", true); 822 authorizationIdentity.addLongIdentifier("report-authzid", true); 823 authorizationIdentity.setArgumentGroupName( 824 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 825 parser.addArgument(authorizationIdentity); 826 827 assertionFilter = new FilterArgument(null, "assertionFilter", false, 1, 828 INFO_PLACEHOLDER_FILTER.get(), 829 INFO_LDAPSEARCH_ARG_DESCRIPTION_ASSERTION_FILTER.get()); 830 assertionFilter.addLongIdentifier("assertion-filter", true); 831 assertionFilter.setArgumentGroupName( 832 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 833 parser.addArgument(assertionFilter); 834 835 accountUsable = new BooleanArgument(null, "accountUsable", 1, 836 INFO_LDAPSEARCH_ARG_DESCRIPTION_ACCOUNT_USABLE.get()); 837 accountUsable.addLongIdentifier("account-usable", true); 838 accountUsable.setArgumentGroupName( 839 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 840 parser.addArgument(accountUsable); 841 842 excludeBranch = new DNArgument(null, "excludeBranch", false, 0, null, 843 INFO_LDAPSEARCH_ARG_DESCRIPTION_EXCLUDE_BRANCH.get()); 844 excludeBranch.addLongIdentifier("exclude-branch", true); 845 excludeBranch.setArgumentGroupName( 846 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 847 parser.addArgument(excludeBranch); 848 849 getAuthorizationEntryAttribute = new StringArgument(null, 850 "getAuthorizationEntryAttribute", false, 0, 851 INFO_PLACEHOLDER_ATTR.get(), 852 INFO_LDAPSEARCH_ARG_DESCRIPTION_GET_AUTHZ_ENTRY_ATTR.get()); 853 getAuthorizationEntryAttribute.addLongIdentifier( 854 "get-authorization-entry-attribute", true); 855 getAuthorizationEntryAttribute.setArgumentGroupName( 856 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 857 parser.addArgument(getAuthorizationEntryAttribute); 858 859 getBackendSetID = new BooleanArgument(null, "getBackendSetID", 860 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_GET_BACKEND_SET_ID.get()); 861 getBackendSetID.addLongIdentifier("get-backend-set-id", true); 862 getBackendSetID.setArgumentGroupName( 863 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 864 parser.addArgument(getBackendSetID); 865 866 getEffectiveRightsAuthzID = new StringArgument('g', 867 "getEffectiveRightsAuthzID", false, 1, 868 INFO_PLACEHOLDER_AUTHZID.get(), 869 INFO_LDAPSEARCH_ARG_DESCRIPTION_GET_EFFECTIVE_RIGHTS_AUTHZID.get( 870 "getEffectiveRightsAttribute")); 871 getEffectiveRightsAuthzID.addLongIdentifier( 872 "get-effective-rights-authzid", true); 873 getEffectiveRightsAuthzID.setArgumentGroupName( 874 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 875 parser.addArgument(getEffectiveRightsAuthzID); 876 877 getEffectiveRightsAttribute = new StringArgument('e', 878 "getEffectiveRightsAttribute", false, 0, 879 INFO_PLACEHOLDER_ATTR.get(), 880 INFO_LDAPSEARCH_ARG_DESCRIPTION_GET_EFFECTIVE_RIGHTS_ATTR.get()); 881 getEffectiveRightsAttribute.addLongIdentifier( 882 "get-effective-rights-attribute", true); 883 getEffectiveRightsAttribute.setArgumentGroupName( 884 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 885 parser.addArgument(getEffectiveRightsAttribute); 886 887 getRecentLoginHistory = new BooleanArgument(null, "getRecentLoginHistory", 888 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_GET_RECENT_LOGIN_HISTORY.get()); 889 getRecentLoginHistory.addLongIdentifier("get-recent-login-history", true); 890 getRecentLoginHistory.setArgumentGroupName( 891 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 892 parser.addArgument(getRecentLoginHistory); 893 894 getServerID = new BooleanArgument(null, "getServerID", 895 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_GET_SERVER_ID.get()); 896 getServerID.addLongIdentifier("get-server-id", true); 897 getServerID.setArgumentGroupName( 898 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 899 parser.addArgument(getServerID); 900 901 getUserResourceLimits = new BooleanArgument(null, "getUserResourceLimits", 902 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_GET_USER_RESOURCE_LIMITS.get()); 903 getUserResourceLimits.addLongIdentifier("get-user-resource-limits", true); 904 getUserResourceLimits.setArgumentGroupName( 905 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 906 parser.addArgument(getUserResourceLimits); 907 908 includeReplicationConflictEntries = new BooleanArgument(null, 909 "includeReplicationConflictEntries", 1, 910 INFO_LDAPSEARCH_ARG_DESCRIPTION_INCLUDE_REPL_CONFLICTS.get()); 911 includeReplicationConflictEntries.addLongIdentifier( 912 "include-replication-conflict-entries", true); 913 includeReplicationConflictEntries.setArgumentGroupName( 914 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 915 parser.addArgument(includeReplicationConflictEntries); 916 917 final Set<String> softDeleteAllowedValues = StaticUtils.setOf( 918 "with-non-deleted-entries", "without-non-deleted-entries", 919 "deleted-entries-in-undeleted-form"); 920 includeSoftDeletedEntries = new StringArgument(null, 921 "includeSoftDeletedEntries", false, 1, 922 "{with-non-deleted-entries|without-non-deleted-entries|" + 923 "deleted-entries-in-undeleted-form}", 924 INFO_LDAPSEARCH_ARG_DESCRIPTION_INCLUDE_SOFT_DELETED.get(), 925 softDeleteAllowedValues); 926 includeSoftDeletedEntries.addLongIdentifier( 927 "include-soft-deleted-entries", true); 928 includeSoftDeletedEntries.setArgumentGroupName( 929 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 930 parser.addArgument(includeSoftDeletedEntries); 931 932 draftLDUPSubentries = new BooleanArgument(null, "draftLDUPSubentries", 1, 933 INFO_LDAPSEARCH_ARG_DESCRIPTION_INCLUDE_DRAFT_LDUP_SUBENTRIES.get()); 934 draftLDUPSubentries.addLongIdentifier("draftIETFLDUPSubentries", true); 935 draftLDUPSubentries.addLongIdentifier("includeSubentries", true); 936 draftLDUPSubentries.addLongIdentifier("includeLDAPSubentries", true); 937 draftLDUPSubentries.addLongIdentifier("draft-ldup-subentries", true); 938 draftLDUPSubentries.addLongIdentifier("draft-ietf-ldup-subentries", true); 939 draftLDUPSubentries.addLongIdentifier("include-subentries", true); 940 draftLDUPSubentries.addLongIdentifier("include-ldap-subentries", true); 941 draftLDUPSubentries.setArgumentGroupName( 942 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 943 parser.addArgument(draftLDUPSubentries); 944 945 rfc3672Subentries = new BooleanValueArgument(null, "rfc3672Subentries", 946 false, 947 INFO_LDAPSEARCH_ARG_PLACEHOLDER_INCLUDE_RFC_3672_SUBENTRIES.get(), 948 INFO_LDAPSEARCH_ARG_DESCRIPTION_INCLUDE_RFC_3672_SUBENTRIES.get()); 949 rfc3672Subentries.addLongIdentifier("rfc-3672-subentries", true); 950 rfc3672Subentries.addLongIdentifier("rfc3672-subentries", true); 951 rfc3672Subentries.setArgumentGroupName( 952 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 953 parser.addArgument(rfc3672Subentries); 954 955 joinRule = new StringArgument(null, "joinRule", false, 1, 956 "{dn:sourceAttr|reverse-dn:targetAttr|equals:sourceAttr:targetAttr|" + 957 "contains:sourceAttr:targetAttr }", 958 INFO_LDAPSEARCH_ARG_DESCRIPTION_JOIN_RULE.get()); 959 joinRule.addLongIdentifier("join-rule", true); 960 joinRule.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 961 parser.addArgument(joinRule); 962 963 joinBaseDN = new StringArgument(null, "joinBaseDN", false, 1, 964 "{search-base|source-entry-dn|{dn}}", 965 INFO_LDAPSEARCH_ARG_DESCRIPTION_JOIN_BASE_DN.get()); 966 joinBaseDN.addLongIdentifier("join-base-dn", true); 967 joinBaseDN.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 968 parser.addArgument(joinBaseDN); 969 970 joinScope = new ScopeArgument(null, "joinScope", false, null, 971 INFO_LDAPSEARCH_ARG_DESCRIPTION_JOIN_SCOPE.get()); 972 joinScope.addLongIdentifier("join-scope", true); 973 joinScope.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 974 parser.addArgument(joinScope); 975 976 joinSizeLimit = new IntegerArgument(null, "joinSizeLimit", false, 1, 977 INFO_PLACEHOLDER_NUM.get(), 978 INFO_LDAPSEARCH_ARG_DESCRIPTION_JOIN_SIZE_LIMIT.get(), 0, 979 Integer.MAX_VALUE); 980 joinSizeLimit.addLongIdentifier("join-size-limit", true); 981 joinSizeLimit.setArgumentGroupName( 982 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 983 parser.addArgument(joinSizeLimit); 984 985 joinFilter = new FilterArgument(null, "joinFilter", false, 1, null, 986 INFO_LDAPSEARCH_ARG_DESCRIPTION_JOIN_FILTER.get()); 987 joinFilter.addLongIdentifier("join-filter", true); 988 joinFilter.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 989 parser.addArgument(joinFilter); 990 991 joinRequestedAttribute = new StringArgument(null, "joinRequestedAttribute", 992 false, 0, INFO_PLACEHOLDER_ATTR.get(), 993 INFO_LDAPSEARCH_ARG_DESCRIPTION_JOIN_ATTR.get()); 994 joinRequestedAttribute.addLongIdentifier("join-requested-attribute", true); 995 joinRequestedAttribute.setArgumentGroupName( 996 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 997 parser.addArgument(joinRequestedAttribute); 998 999 joinRequireMatch = new BooleanArgument(null, "joinRequireMatch", 1, 1000 INFO_LDAPSEARCH_ARG_DESCRIPTION_JOIN_REQUIRE_MATCH.get()); 1001 joinRequireMatch.addLongIdentifier("join-require-match", true); 1002 joinRequireMatch.setArgumentGroupName( 1003 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 1004 parser.addArgument(joinRequireMatch); 1005 1006 manageDsaIT = new BooleanArgument(null, "manageDsaIT", 1, 1007 INFO_LDAPSEARCH_ARG_DESCRIPTION_MANAGE_DSA_IT.get()); 1008 manageDsaIT.addLongIdentifier("manage-dsa-it", true); 1009 manageDsaIT.setArgumentGroupName( 1010 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 1011 parser.addArgument(manageDsaIT); 1012 1013 matchedValuesFilter = new FilterArgument(null, "matchedValuesFilter", 1014 false, 0, INFO_PLACEHOLDER_FILTER.get(), 1015 INFO_LDAPSEARCH_ARG_DESCRIPTION_MATCHED_VALUES_FILTER.get()); 1016 matchedValuesFilter.addLongIdentifier("matched-values-filter", true); 1017 matchedValuesFilter.setArgumentGroupName( 1018 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 1019 parser.addArgument(matchedValuesFilter); 1020 1021 matchingEntryCountControl = new StringArgument(null, 1022 "matchingEntryCountControl", false, 1, 1023 "{examineCount=NNN[:alwaysExamine][:allowUnindexed]" + 1024 "[:skipResolvingExplodedIndexes]" + 1025 "[:fastShortCircuitThreshold=NNN]" + 1026 "[:slowShortCircuitThreshold=NNN][:extendedResponseData]" + 1027 "[:debug]}", 1028 INFO_LDAPSEARCH_ARG_DESCRIPTION_MATCHING_ENTRY_COUNT_CONTROL.get()); 1029 matchingEntryCountControl.addLongIdentifier("matchingEntryCount", true); 1030 matchingEntryCountControl.addLongIdentifier( 1031 "matching-entry-count-control", true); 1032 matchingEntryCountControl.addLongIdentifier("matching-entry-count", true); 1033 matchingEntryCountControl.setArgumentGroupName( 1034 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 1035 parser.addArgument(matchingEntryCountControl); 1036 1037 operationPurpose = new StringArgument(null, "operationPurpose", false, 1, 1038 INFO_PLACEHOLDER_PURPOSE.get(), 1039 INFO_LDAPSEARCH_ARG_DESCRIPTION_OPERATION_PURPOSE.get()); 1040 operationPurpose.addLongIdentifier("operation-purpose", true); 1041 operationPurpose.setArgumentGroupName( 1042 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 1043 parser.addArgument(operationPurpose); 1044 1045 overrideSearchLimit = new StringArgument(null, "overrideSearchLimit", 1046 false, 0, INFO_LDAPSEARCH_NAME_VALUE_PLACEHOLDER.get(), 1047 INFO_LDAPSEARCH_ARG_DESCRIPTION_OVERRIDE_SEARCH_LIMIT.get()); 1048 overrideSearchLimit.addLongIdentifier("overrideSearchLimits", true); 1049 overrideSearchLimit.addLongIdentifier("override-search-limit", true); 1050 overrideSearchLimit.addLongIdentifier("override-search-limits", true); 1051 overrideSearchLimit.setArgumentGroupName( 1052 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 1053 parser.addArgument(overrideSearchLimit); 1054 1055 persistentSearch = new StringArgument('C', "persistentSearch", false, 1, 1056 "ps[:changetype[:changesonly[:entrychgcontrols]]]", 1057 INFO_LDAPSEARCH_ARG_DESCRIPTION_PERSISTENT_SEARCH.get()); 1058 persistentSearch.addLongIdentifier("persistent-search", true); 1059 persistentSearch.setArgumentGroupName( 1060 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 1061 parser.addArgument(persistentSearch); 1062 1063 permitUnindexedSearch = new BooleanArgument(null, "permitUnindexedSearch", 1064 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_PERMIT_UNINDEXED_SEARCH.get()); 1065 permitUnindexedSearch.addLongIdentifier("permitUnindexedSearches", true); 1066 permitUnindexedSearch.addLongIdentifier("permitUnindexed", true); 1067 permitUnindexedSearch.addLongIdentifier("permitIfUnindexed", true); 1068 permitUnindexedSearch.addLongIdentifier("permit-unindexed-search", true); 1069 permitUnindexedSearch.addLongIdentifier("permit-unindexed-searches", true); 1070 permitUnindexedSearch.addLongIdentifier("permit-unindexed", true); 1071 permitUnindexedSearch.addLongIdentifier("permit-if-unindexed", true); 1072 permitUnindexedSearch.setArgumentGroupName( 1073 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 1074 parser.addArgument(permitUnindexedSearch); 1075 1076 proxyAs = new StringArgument('Y', "proxyAs", false, 1, 1077 INFO_PLACEHOLDER_AUTHZID.get(), 1078 INFO_LDAPSEARCH_ARG_DESCRIPTION_PROXY_AS.get()); 1079 proxyAs.addLongIdentifier("proxy-as", true); 1080 proxyAs.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 1081 parser.addArgument(proxyAs); 1082 1083 proxyV1As = new DNArgument(null, "proxyV1As", false, 1, null, 1084 INFO_LDAPSEARCH_ARG_DESCRIPTION_PROXY_V1_AS.get()); 1085 proxyV1As.addLongIdentifier("proxy-v1-as", true); 1086 proxyV1As.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 1087 parser.addArgument(proxyV1As); 1088 1089 rejectUnindexedSearch = new BooleanArgument(null, "rejectUnindexedSearch", 1090 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_REJECT_UNINDEXED_SEARCH.get()); 1091 rejectUnindexedSearch.addLongIdentifier("rejectUnindexedSearches", true); 1092 rejectUnindexedSearch.addLongIdentifier("rejectUnindexed", true); 1093 rejectUnindexedSearch.addLongIdentifier("rejectIfUnindexed", true); 1094 rejectUnindexedSearch.addLongIdentifier("reject-unindexed-search", true); 1095 rejectUnindexedSearch.addLongIdentifier("reject-unindexed-searches", true); 1096 rejectUnindexedSearch.addLongIdentifier("reject-unindexed", true); 1097 rejectUnindexedSearch.addLongIdentifier("reject-if-unindexed", true); 1098 rejectUnindexedSearch.setArgumentGroupName( 1099 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 1100 parser.addArgument(rejectUnindexedSearch); 1101 1102 routeToBackendSet = new StringArgument(null, "routeToBackendSet", 1103 false, 0, 1104 INFO_LDAPSEARCH_ARG_PLACEHOLDER_ROUTE_TO_BACKEND_SET.get(), 1105 INFO_LDAPSEARCH_ARG_DESCRIPTION_ROUTE_TO_BACKEND_SET.get()); 1106 routeToBackendSet.addLongIdentifier("route-to-backend-set", true); 1107 routeToBackendSet.setArgumentGroupName( 1108 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 1109 parser.addArgument(routeToBackendSet); 1110 1111 routeToServer = new StringArgument(null, "routeToServer", false, 1, 1112 INFO_LDAPSEARCH_ARG_PLACEHOLDER_ROUTE_TO_SERVER.get(), 1113 INFO_LDAPSEARCH_ARG_DESCRIPTION_ROUTE_TO_SERVER.get()); 1114 routeToServer.addLongIdentifier("route-to-server", true); 1115 routeToServer.setArgumentGroupName( 1116 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 1117 parser.addArgument(routeToServer); 1118 1119 final Set<String> suppressOperationalAttributeUpdatesAllowedValues = 1120 StaticUtils.setOf("last-access-time", "last-login-time", 1121 "last-login-ip", "lastmod"); 1122 suppressOperationalAttributeUpdates = new StringArgument(null, 1123 "suppressOperationalAttributeUpdates", false, -1, 1124 INFO_PLACEHOLDER_ATTR.get(), 1125 INFO_LDAPSEARCH_ARG_DESCRIPTION_SUPPRESS_OP_ATTR_UPDATES.get(), 1126 suppressOperationalAttributeUpdatesAllowedValues); 1127 suppressOperationalAttributeUpdates.addLongIdentifier( 1128 "suppress-operational-attribute-updates", true); 1129 suppressOperationalAttributeUpdates.setArgumentGroupName( 1130 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 1131 parser.addArgument(suppressOperationalAttributeUpdates); 1132 1133 usePasswordPolicyControl = new BooleanArgument(null, 1134 "usePasswordPolicyControl", 1, 1135 INFO_LDAPSEARCH_ARG_DESCRIPTION_PASSWORD_POLICY.get()); 1136 usePasswordPolicyControl.addLongIdentifier("use-password-policy-control", 1137 true); 1138 usePasswordPolicyControl.setArgumentGroupName( 1139 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 1140 parser.addArgument(usePasswordPolicyControl); 1141 1142 realAttributesOnly = new BooleanArgument(null, "realAttributesOnly", 1, 1143 INFO_LDAPSEARCH_ARG_DESCRIPTION_REAL_ATTRS_ONLY.get()); 1144 realAttributesOnly.addLongIdentifier("real-attributes-only", true); 1145 realAttributesOnly.setArgumentGroupName( 1146 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 1147 parser.addArgument(realAttributesOnly); 1148 1149 sortOrder = new StringArgument('S', "sortOrder", false, 1, null, 1150 INFO_LDAPSEARCH_ARG_DESCRIPTION_SORT_ORDER.get()); 1151 sortOrder.addLongIdentifier("sort-order", true); 1152 sortOrder.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 1153 parser.addArgument(sortOrder); 1154 1155 simplePageSize = new IntegerArgument(null, "simplePageSize", false, 1, 1156 null, INFO_LDAPSEARCH_ARG_DESCRIPTION_PAGE_SIZE.get(), 1, 1157 Integer.MAX_VALUE); 1158 simplePageSize.addLongIdentifier("simple-page-size", true); 1159 simplePageSize.setArgumentGroupName( 1160 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 1161 parser.addArgument(simplePageSize); 1162 1163 virtualAttributesOnly = new BooleanArgument(null, 1164 "virtualAttributesOnly", 1, 1165 INFO_LDAPSEARCH_ARG_DESCRIPTION_VIRTUAL_ATTRS_ONLY.get()); 1166 virtualAttributesOnly.addLongIdentifier("virtual-attributes-only", true); 1167 virtualAttributesOnly.setArgumentGroupName( 1168 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 1169 parser.addArgument(virtualAttributesOnly); 1170 1171 virtualListView = new StringArgument('G', "virtualListView", false, 1, 1172 "{before:after:index:count | before:after:value}", 1173 INFO_LDAPSEARCH_ARG_DESCRIPTION_VLV.get("sortOrder")); 1174 virtualListView.addLongIdentifier("vlv", true); 1175 virtualListView.addLongIdentifier("virtual-list-view", true); 1176 virtualListView.setArgumentGroupName( 1177 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 1178 parser.addArgument(virtualListView); 1179 1180 excludeAttribute = new StringArgument(null, "excludeAttribute", false, 0, 1181 INFO_PLACEHOLDER_ATTR.get(), 1182 INFO_LDAPSEARCH_ARG_DESCRIPTION_EXCLUDE_ATTRIBUTE.get()); 1183 excludeAttribute.addLongIdentifier("exclude-attribute", true); 1184 excludeAttribute.setArgumentGroupName( 1185 INFO_LDAPSEARCH_ARG_GROUP_TRANSFORMATIONS.get()); 1186 parser.addArgument(excludeAttribute); 1187 1188 redactAttribute = new StringArgument(null, "redactAttribute", false, 0, 1189 INFO_PLACEHOLDER_ATTR.get(), 1190 INFO_LDAPSEARCH_ARG_DESCRIPTION_REDACT_ATTRIBUTE.get()); 1191 redactAttribute.addLongIdentifier("redact-attribute", true); 1192 redactAttribute.setArgumentGroupName( 1193 INFO_LDAPSEARCH_ARG_GROUP_TRANSFORMATIONS.get()); 1194 parser.addArgument(redactAttribute); 1195 1196 hideRedactedValueCount = new BooleanArgument(null, "hideRedactedValueCount", 1197 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_HIDE_REDACTED_VALUE_COUNT.get()); 1198 hideRedactedValueCount.addLongIdentifier("hide-redacted-value-count", true); 1199 hideRedactedValueCount.setArgumentGroupName( 1200 INFO_LDAPSEARCH_ARG_GROUP_TRANSFORMATIONS.get()); 1201 parser.addArgument(hideRedactedValueCount); 1202 1203 scrambleAttribute = new StringArgument(null, "scrambleAttribute", false, 0, 1204 INFO_PLACEHOLDER_ATTR.get(), 1205 INFO_LDAPSEARCH_ARG_DESCRIPTION_SCRAMBLE_ATTRIBUTE.get()); 1206 scrambleAttribute.addLongIdentifier("scramble-attribute", true); 1207 scrambleAttribute.setArgumentGroupName( 1208 INFO_LDAPSEARCH_ARG_GROUP_TRANSFORMATIONS.get()); 1209 parser.addArgument(scrambleAttribute); 1210 1211 scrambleJSONField = new StringArgument(null, "scrambleJSONField", false, 0, 1212 INFO_PLACEHOLDER_FIELD_NAME.get(), 1213 INFO_LDAPSEARCH_ARG_DESCRIPTION_SCRAMBLE_JSON_FIELD.get()); 1214 scrambleJSONField.addLongIdentifier("scramble-json-field", true); 1215 scrambleJSONField.setArgumentGroupName( 1216 INFO_LDAPSEARCH_ARG_GROUP_TRANSFORMATIONS.get()); 1217 parser.addArgument(scrambleJSONField); 1218 1219 scrambleRandomSeed = new IntegerArgument(null, "scrambleRandomSeed", false, 1220 1, null, INFO_LDAPSEARCH_ARG_DESCRIPTION_SCRAMBLE_RANDOM_SEED.get()); 1221 scrambleRandomSeed.addLongIdentifier("scramble-random-seed", true); 1222 scrambleRandomSeed.setArgumentGroupName( 1223 INFO_LDAPSEARCH_ARG_GROUP_TRANSFORMATIONS.get()); 1224 parser.addArgument(scrambleRandomSeed); 1225 1226 renameAttributeFrom = new StringArgument(null, "renameAttributeFrom", false, 1227 0, INFO_PLACEHOLDER_ATTR.get(), 1228 INFO_LDAPSEARCH_ARG_DESCRIPTION_RENAME_ATTRIBUTE_FROM.get()); 1229 renameAttributeFrom.addLongIdentifier("rename-attribute-from", true); 1230 renameAttributeFrom.setArgumentGroupName( 1231 INFO_LDAPSEARCH_ARG_GROUP_TRANSFORMATIONS.get()); 1232 parser.addArgument(renameAttributeFrom); 1233 1234 renameAttributeTo = new StringArgument(null, "renameAttributeTo", false, 1235 0, INFO_PLACEHOLDER_ATTR.get(), 1236 INFO_LDAPSEARCH_ARG_DESCRIPTION_RENAME_ATTRIBUTE_TO.get()); 1237 renameAttributeTo.addLongIdentifier("rename-attribute-to", true); 1238 renameAttributeTo.setArgumentGroupName( 1239 INFO_LDAPSEARCH_ARG_GROUP_TRANSFORMATIONS.get()); 1240 parser.addArgument(renameAttributeTo); 1241 1242 moveSubtreeFrom = new DNArgument(null, "moveSubtreeFrom", false, 0, 1243 INFO_PLACEHOLDER_ATTR.get(), 1244 INFO_LDAPSEARCH_ARG_DESCRIPTION_MOVE_SUBTREE_FROM.get()); 1245 moveSubtreeFrom.addLongIdentifier("move-subtree-from", true); 1246 moveSubtreeFrom.setArgumentGroupName( 1247 INFO_LDAPSEARCH_ARG_GROUP_TRANSFORMATIONS.get()); 1248 parser.addArgument(moveSubtreeFrom); 1249 1250 moveSubtreeTo = new DNArgument(null, "moveSubtreeTo", false, 0, 1251 INFO_PLACEHOLDER_ATTR.get(), 1252 INFO_LDAPSEARCH_ARG_DESCRIPTION_MOVE_SUBTREE_TO.get()); 1253 moveSubtreeTo.addLongIdentifier("move-subtree-to", true); 1254 moveSubtreeTo.setArgumentGroupName( 1255 INFO_LDAPSEARCH_ARG_GROUP_TRANSFORMATIONS.get()); 1256 parser.addArgument(moveSubtreeTo); 1257 1258 1259 // The "--scriptFriendly" argument is provided for compatibility with legacy 1260 // ldapsearch tools, but is not actually used by this tool. 1261 final BooleanArgument scriptFriendly = new BooleanArgument(null, 1262 "scriptFriendly", 1, 1263 INFO_LDAPSEARCH_ARG_DESCRIPTION_SCRIPT_FRIENDLY.get()); 1264 scriptFriendly.addLongIdentifier("script-friendly", true); 1265 scriptFriendly.setHidden(true); 1266 parser.addArgument(scriptFriendly); 1267 1268 1269 // The "-V" / "--ldapVersion" argument is provided for compatibility with 1270 // legacy ldapsearch tools, but is not actually used by this tool. 1271 final IntegerArgument ldapVersion = new IntegerArgument('V', "ldapVersion", 1272 false, 1, null, INFO_LDAPSEARCH_ARG_DESCRIPTION_LDAP_VERSION.get()); 1273 ldapVersion.addLongIdentifier("ldap-version", true); 1274 ldapVersion.setHidden(true); 1275 parser.addArgument(ldapVersion); 1276 1277 1278 // The baseDN and ldapURLFile arguments can't be used together. 1279 parser.addExclusiveArgumentSet(baseDN, ldapURLFile); 1280 1281 // The scope and ldapURLFile arguments can't be used together. 1282 parser.addExclusiveArgumentSet(scope, ldapURLFile); 1283 1284 // The requestedAttribute and ldapURLFile arguments can't be used together. 1285 parser.addExclusiveArgumentSet(requestedAttribute, ldapURLFile); 1286 1287 // The filter and ldapURLFile arguments can't be used together. 1288 parser.addExclusiveArgumentSet(filter, ldapURLFile); 1289 1290 // The filterFile and ldapURLFile arguments can't be used together. 1291 parser.addExclusiveArgumentSet(filterFile, ldapURLFile); 1292 1293 // The followReferrals and manageDsaIT arguments can't be used together. 1294 parser.addExclusiveArgumentSet(followReferrals, manageDsaIT); 1295 1296 // The persistent search argument can't be used with either the filterFile 1297 // or ldapURLFile arguments. 1298 parser.addExclusiveArgumentSet(persistentSearch, filterFile); 1299 parser.addExclusiveArgumentSet(persistentSearch, ldapURLFile); 1300 1301 // The draft-ietf-ldup-subentry and RFC 3672 subentries controls cannot be 1302 // used together. 1303 parser.addExclusiveArgumentSet(draftLDUPSubentries, rfc3672Subentries); 1304 1305 // The realAttributesOnly and virtualAttributesOnly arguments can't be used 1306 // together. 1307 parser.addExclusiveArgumentSet(realAttributesOnly, virtualAttributesOnly); 1308 1309 // The simplePageSize and virtualListView arguments can't be used together. 1310 parser.addExclusiveArgumentSet(simplePageSize, virtualListView); 1311 1312 // The terse and verbose arguments can't be used together. 1313 parser.addExclusiveArgumentSet(terse, verbose); 1314 1315 // The getEffectiveRightsAttribute argument requires the 1316 // getEffectiveRightsAuthzID argument. 1317 parser.addDependentArgumentSet(getEffectiveRightsAttribute, 1318 getEffectiveRightsAuthzID); 1319 1320 // The virtualListView argument requires the sortOrder argument. 1321 parser.addDependentArgumentSet(virtualListView, sortOrder); 1322 1323 // The rejectUnindexedSearch and permitUnindexedSearch arguments can't be 1324 // used together. 1325 parser.addExclusiveArgumentSet(rejectUnindexedSearch, 1326 permitUnindexedSearch); 1327 1328 // The separateOutputFilePerSearch argument requires the outputFile 1329 // argument. It also requires either the filter, filterFile or ldapURLFile 1330 // argument. 1331 parser.addDependentArgumentSet(separateOutputFilePerSearch, outputFile); 1332 parser.addDependentArgumentSet(separateOutputFilePerSearch, filter, 1333 filterFile, ldapURLFile); 1334 1335 // The teeResultsToStandardOut argument requires the outputFile argument. 1336 parser.addDependentArgumentSet(teeResultsToStandardOut, outputFile); 1337 1338 // The wrapColumn and dontWrap arguments must not be used together. 1339 parser.addExclusiveArgumentSet(wrapColumn, dontWrap); 1340 1341 // All arguments that specifically pertain to join processing can only be 1342 // used if the joinRule argument is provided. 1343 parser.addDependentArgumentSet(joinBaseDN, joinRule); 1344 parser.addDependentArgumentSet(joinScope, joinRule); 1345 parser.addDependentArgumentSet(joinSizeLimit, joinRule); 1346 parser.addDependentArgumentSet(joinFilter, joinRule); 1347 parser.addDependentArgumentSet(joinRequestedAttribute, joinRule); 1348 parser.addDependentArgumentSet(joinRequireMatch, joinRule); 1349 1350 // The countEntries argument must not be used in conjunction with the 1351 // filter, filterFile, LDAPURLFile, or persistentSearch arguments. 1352 parser.addExclusiveArgumentSet(countEntries, filter); 1353 parser.addExclusiveArgumentSet(countEntries, filterFile); 1354 parser.addExclusiveArgumentSet(countEntries, ldapURLFile); 1355 parser.addExclusiveArgumentSet(countEntries, persistentSearch); 1356 1357 1358 // The hideRedactedValueCount argument requires the redactAttribute 1359 // argument. 1360 parser.addDependentArgumentSet(hideRedactedValueCount, redactAttribute); 1361 1362 // The scrambleJSONField and scrambleRandomSeed arguments require the 1363 // scrambleAttribute argument. 1364 parser.addDependentArgumentSet(scrambleJSONField, scrambleAttribute); 1365 parser.addDependentArgumentSet(scrambleRandomSeed, scrambleAttribute); 1366 1367 // The renameAttributeFrom and renameAttributeTo arguments must be provided 1368 // together. 1369 parser.addDependentArgumentSet(renameAttributeFrom, renameAttributeTo); 1370 parser.addDependentArgumentSet(renameAttributeTo, renameAttributeFrom); 1371 1372 // The moveSubtreeFrom and moveSubtreeTo arguments must be provided 1373 // together. 1374 parser.addDependentArgumentSet(moveSubtreeFrom, moveSubtreeTo); 1375 parser.addDependentArgumentSet(moveSubtreeTo, moveSubtreeFrom); 1376 1377 1378 // The compressOutput argument can only be used if an output file is 1379 // specified and results aren't going to be teed. 1380 parser.addDependentArgumentSet(compressOutput, outputFile); 1381 parser.addExclusiveArgumentSet(compressOutput, teeResultsToStandardOut); 1382 1383 1384 // The encryptOutput argument can only be used if an output file is 1385 // specified and results aren't going to be teed. 1386 parser.addDependentArgumentSet(encryptOutput, outputFile); 1387 parser.addExclusiveArgumentSet(encryptOutput, teeResultsToStandardOut); 1388 1389 1390 // The encryptionPassphraseFile argument can only be used if the 1391 // encryptOutput argument is also provided. 1392 parser.addDependentArgumentSet(encryptionPassphraseFile, encryptOutput); 1393 } 1394 1395 1396 1397 /** 1398 * {@inheritDoc} 1399 */ 1400 @Override() 1401 @NotNull() 1402 protected List<Control> getBindControls() 1403 { 1404 final ArrayList<Control> bindControls = new ArrayList<>(10); 1405 1406 if (bindControl.isPresent()) 1407 { 1408 bindControls.addAll(bindControl.getValues()); 1409 } 1410 1411 if (authorizationIdentity.isPresent()) 1412 { 1413 bindControls.add(new AuthorizationIdentityRequestControl(false)); 1414 } 1415 1416 if (getAuthorizationEntryAttribute.isPresent()) 1417 { 1418 bindControls.add(new GetAuthorizationEntryRequestControl(true, true, 1419 getAuthorizationEntryAttribute.getValues())); 1420 } 1421 1422 if (getRecentLoginHistory.isPresent()) 1423 { 1424 bindControls.add(new GetRecentLoginHistoryRequestControl()); 1425 } 1426 1427 if (getUserResourceLimits.isPresent()) 1428 { 1429 bindControls.add(new GetUserResourceLimitsRequestControl()); 1430 } 1431 1432 if (usePasswordPolicyControl.isPresent()) 1433 { 1434 bindControls.add(new PasswordPolicyRequestControl()); 1435 } 1436 1437 if (suppressOperationalAttributeUpdates.isPresent()) 1438 { 1439 final EnumSet<SuppressType> suppressTypes = 1440 EnumSet.noneOf(SuppressType.class); 1441 for (final String s : suppressOperationalAttributeUpdates.getValues()) 1442 { 1443 if (s.equalsIgnoreCase("last-access-time")) 1444 { 1445 suppressTypes.add(SuppressType.LAST_ACCESS_TIME); 1446 } 1447 else if (s.equalsIgnoreCase("last-login-time")) 1448 { 1449 suppressTypes.add(SuppressType.LAST_LOGIN_TIME); 1450 } 1451 else if (s.equalsIgnoreCase("last-login-ip")) 1452 { 1453 suppressTypes.add(SuppressType.LAST_LOGIN_IP); 1454 } 1455 } 1456 1457 bindControls.add(new SuppressOperationalAttributeUpdateRequestControl( 1458 suppressTypes)); 1459 } 1460 1461 return bindControls; 1462 } 1463 1464 1465 1466 /** 1467 * {@inheritDoc} 1468 */ 1469 @Override() 1470 protected boolean supportsMultipleServers() 1471 { 1472 // We will support providing information about multiple servers. This tool 1473 // will not communicate with multiple servers concurrently, but it can 1474 // accept information about multiple servers in the event that multiple 1475 // searches are to be performed and a server goes down in the middle of 1476 // those searches. In this case, we can resume processing on a 1477 // newly-created connection, possibly to a different server. 1478 return true; 1479 } 1480 1481 1482 1483 /** 1484 * {@inheritDoc} 1485 */ 1486 @Override() 1487 public void doExtendedNonLDAPArgumentValidation() 1488 throws ArgumentException 1489 { 1490 // If wrapColumn was provided, then use its value. Otherwise, if dontWrap 1491 // was provided, then use that. 1492 if (wrapColumn.isPresent()) 1493 { 1494 final int wc = wrapColumn.getValue(); 1495 if (wc <= 0) 1496 { 1497 WRAP_COLUMN = Integer.MAX_VALUE; 1498 } 1499 else 1500 { 1501 WRAP_COLUMN = wc; 1502 } 1503 } 1504 else if (dontWrap.isPresent()) 1505 { 1506 WRAP_COLUMN = Integer.MAX_VALUE; 1507 } 1508 1509 1510 // If the ldapURLFile argument was provided, then there must not be any 1511 // trailing arguments. 1512 final List<String> trailingArgs = parser.getTrailingArguments(); 1513 if (ldapURLFile.isPresent()) 1514 { 1515 if (! trailingArgs.isEmpty()) 1516 { 1517 throw new ArgumentException( 1518 ERR_LDAPSEARCH_TRAILING_ARGS_WITH_URL_FILE.get( 1519 ldapURLFile.getIdentifierString())); 1520 } 1521 } 1522 1523 1524 // If the filter or filterFile argument was provided, then there may 1525 // optionally be trailing arguments, but the first trailing argument must 1526 // not be a filter. 1527 if (filter.isPresent() || filterFile.isPresent()) 1528 { 1529 if (! trailingArgs.isEmpty()) 1530 { 1531 try 1532 { 1533 Filter.create(trailingArgs.get(0)); 1534 throw new ArgumentException( 1535 ERR_LDAPSEARCH_TRAILING_FILTER_WITH_FILTER_FILE.get( 1536 filterFile.getIdentifierString())); 1537 } 1538 catch (final LDAPException le) 1539 { 1540 // This is the normal condition. Not even worth debugging the 1541 // exception. 1542 } 1543 } 1544 } 1545 1546 1547 // If none of the ldapURLFile, filter, or filterFile arguments was provided, 1548 // then there must be at least one trailing argument, and the first trailing 1549 // argument must be a valid search filter. 1550 if (! (ldapURLFile.isPresent() || filter.isPresent() || 1551 filterFile.isPresent())) 1552 { 1553 if (trailingArgs.isEmpty()) 1554 { 1555 throw new ArgumentException(ERR_LDAPSEARCH_NO_TRAILING_ARGS.get( 1556 filterFile.getIdentifierString(), 1557 ldapURLFile.getIdentifierString())); 1558 } 1559 1560 try 1561 { 1562 Filter.create(trailingArgs.get(0)); 1563 } 1564 catch (final Exception e) 1565 { 1566 Debug.debugException(e); 1567 throw new ArgumentException( 1568 ERR_LDAPSEARCH_FIRST_TRAILING_ARG_NOT_FILTER.get( 1569 trailingArgs.get(0)), 1570 e); 1571 } 1572 } 1573 1574 1575 // There should never be a case in which a trailing argument starts with a 1576 // dash, and it's probably an attempt to use a named argument but that was 1577 // inadvertently put after the filter. Warn about the problem, but don't 1578 // fail. 1579 for (final String s : trailingArgs) 1580 { 1581 if (s.startsWith("-")) 1582 { 1583 commentToErr(WARN_LDAPSEARCH_TRAILING_ARG_STARTS_WITH_DASH.get(s)); 1584 break; 1585 } 1586 } 1587 1588 1589 // If any matched values filters are specified, then validate them and 1590 // pre-create the matched values request control. 1591 if (matchedValuesFilter.isPresent()) 1592 { 1593 final List<Filter> filterList = matchedValuesFilter.getValues(); 1594 final MatchedValuesFilter[] matchedValuesFilters = 1595 new MatchedValuesFilter[filterList.size()]; 1596 for (int i=0; i < matchedValuesFilters.length; i++) 1597 { 1598 try 1599 { 1600 matchedValuesFilters[i] = 1601 MatchedValuesFilter.create(filterList.get(i)); 1602 } 1603 catch (final Exception e) 1604 { 1605 Debug.debugException(e); 1606 throw new ArgumentException( 1607 ERR_LDAPSEARCH_INVALID_MATCHED_VALUES_FILTER.get( 1608 filterList.get(i).toString()), 1609 e); 1610 } 1611 } 1612 1613 matchedValuesRequestControl = 1614 new MatchedValuesRequestControl(true, matchedValuesFilters); 1615 } 1616 1617 1618 // If we should use the matching entry count request control, then validate 1619 // the argument value and pre-create the control. 1620 if (matchingEntryCountControl.isPresent()) 1621 { 1622 final MatchingEntryCountRequestControlProperties properties = 1623 new MatchingEntryCountRequestControlProperties(); 1624 1625 Integer examineCount = null; 1626 try 1627 { 1628 for (final String element : 1629 matchingEntryCountControl.getValue().toLowerCase().split(":")) 1630 { 1631 if (element.startsWith("examinecount=")) 1632 { 1633 examineCount = Integer.parseInt(element.substring(13)); 1634 } 1635 else if (element.equals("allowunindexed")) 1636 { 1637 properties.setProcessSearchIfUnindexed(true); 1638 } 1639 else if (element.equals("alwaysexamine")) 1640 { 1641 properties.setAlwaysExamineCandidates(true); 1642 } 1643 else if (element.equals("skipresolvingexplodedindexes")) 1644 { 1645 properties.setSkipResolvingExplodedIndexes(true); 1646 } 1647 else if (element.startsWith("fastshortcircuitthreshold=")) 1648 { 1649 properties.setFastShortCircuitThreshold( 1650 Long.parseLong(element.substring(26))); 1651 } 1652 else if (element.startsWith("slowshortcircuitthreshold=")) 1653 { 1654 properties.setSlowShortCircuitThreshold( 1655 Long.parseLong(element.substring(26))); 1656 } 1657 else if (element.equals("extendedresponsedata")) 1658 { 1659 properties.setIncludeExtendedResponseData(true); 1660 } 1661 else if (element.equals("debug")) 1662 { 1663 properties.setIncludeDebugInfo(true); 1664 } 1665 else 1666 { 1667 throw new ArgumentException( 1668 ERR_LDAPSEARCH_MATCHING_ENTRY_COUNT_INVALID_VALUE.get( 1669 matchingEntryCountControl.getIdentifierString())); 1670 } 1671 } 1672 } 1673 catch (final ArgumentException ae) 1674 { 1675 Debug.debugException(ae); 1676 throw ae; 1677 } 1678 catch (final Exception e) 1679 { 1680 Debug.debugException(e); 1681 throw new ArgumentException( 1682 ERR_LDAPSEARCH_MATCHING_ENTRY_COUNT_INVALID_VALUE.get( 1683 matchingEntryCountControl.getIdentifierString()), 1684 e); 1685 } 1686 1687 if (examineCount == null) 1688 { 1689 throw new ArgumentException( 1690 ERR_LDAPSEARCH_MATCHING_ENTRY_COUNT_INVALID_VALUE.get( 1691 matchingEntryCountControl.getIdentifierString())); 1692 } 1693 else 1694 { 1695 properties.setMaxCandidatesToExamine(examineCount); 1696 } 1697 1698 matchingEntryCountRequestControl = 1699 new MatchingEntryCountRequestControl(true, properties); 1700 } 1701 1702 1703 // If we should include the override search limits request control, then 1704 // validate the provided values. 1705 if (overrideSearchLimit.isPresent()) 1706 { 1707 final LinkedHashMap<String,String> properties = 1708 new LinkedHashMap<>(StaticUtils.computeMapCapacity(10)); 1709 for (final String value : overrideSearchLimit.getValues()) 1710 { 1711 final int equalPos = value.indexOf('='); 1712 if (equalPos < 0) 1713 { 1714 throw new ArgumentException( 1715 ERR_LDAPSEARCH_OVERRIDE_LIMIT_NO_EQUAL.get( 1716 overrideSearchLimit.getIdentifierString())); 1717 } 1718 else if (equalPos == 0) 1719 { 1720 throw new ArgumentException( 1721 ERR_LDAPSEARCH_OVERRIDE_LIMIT_EMPTY_PROPERTY_NAME.get( 1722 overrideSearchLimit.getIdentifierString())); 1723 } 1724 1725 final String propertyName = value.substring(0, equalPos); 1726 if (properties.containsKey(propertyName)) 1727 { 1728 throw new ArgumentException( 1729 ERR_LDAPSEARCH_OVERRIDE_LIMIT_DUPLICATE_PROPERTY_NAME.get( 1730 overrideSearchLimit.getIdentifierString(), propertyName)); 1731 } 1732 1733 if (equalPos == (value.length() - 1)) 1734 { 1735 throw new ArgumentException( 1736 ERR_LDAPSEARCH_OVERRIDE_LIMIT_EMPTY_PROPERTY_VALUE.get( 1737 overrideSearchLimit.getIdentifierString(), propertyName)); 1738 } 1739 1740 properties.put(propertyName, value.substring(equalPos+1)); 1741 } 1742 1743 overrideSearchLimitsRequestControl = 1744 new OverrideSearchLimitsRequestControl(properties, false); 1745 } 1746 1747 1748 // If we should use the persistent search request control, then validate 1749 // the argument value and pre-create the control. 1750 if (persistentSearch.isPresent()) 1751 { 1752 boolean changesOnly = true; 1753 boolean returnECs = true; 1754 EnumSet<PersistentSearchChangeType> changeTypes = 1755 EnumSet.allOf(PersistentSearchChangeType.class); 1756 try 1757 { 1758 final String[] elements = 1759 persistentSearch.getValue().toLowerCase().split(":"); 1760 if (elements.length == 0) 1761 { 1762 throw new ArgumentException( 1763 ERR_LDAPSEARCH_PERSISTENT_SEARCH_INVALID_VALUE.get( 1764 persistentSearch.getIdentifierString())); 1765 } 1766 1767 final String header = StaticUtils.toLowerCase(elements[0]); 1768 if (! (header.equals("ps") || header.equals("persist") || 1769 header.equals("persistent") || header.equals("psearch") || 1770 header.equals("persistentsearch"))) 1771 { 1772 throw new ArgumentException( 1773 ERR_LDAPSEARCH_PERSISTENT_SEARCH_INVALID_VALUE.get( 1774 persistentSearch.getIdentifierString())); 1775 } 1776 1777 if (elements.length > 1) 1778 { 1779 final String ctString = StaticUtils.toLowerCase(elements[1]); 1780 if (ctString.equals("any")) 1781 { 1782 changeTypes = EnumSet.allOf(PersistentSearchChangeType.class); 1783 } 1784 else 1785 { 1786 changeTypes.clear(); 1787 for (final String t : ctString.split(",")) 1788 { 1789 if (t.equals("add")) 1790 { 1791 changeTypes.add(PersistentSearchChangeType.ADD); 1792 } 1793 else if (t.equals("del") || t.equals("delete")) 1794 { 1795 changeTypes.add(PersistentSearchChangeType.DELETE); 1796 } 1797 else if (t.equals("mod") || t.equals("modify")) 1798 { 1799 changeTypes.add(PersistentSearchChangeType.MODIFY); 1800 } 1801 else if (t.equals("moddn") || t.equals("modrdn") || 1802 t.equals("modifydn") || t.equals("modifyrdn")) 1803 { 1804 changeTypes.add(PersistentSearchChangeType.MODIFY_DN); 1805 } 1806 else 1807 { 1808 throw new ArgumentException( 1809 ERR_LDAPSEARCH_PERSISTENT_SEARCH_INVALID_VALUE.get( 1810 persistentSearch.getIdentifierString())); 1811 } 1812 } 1813 } 1814 } 1815 1816 if (elements.length > 2) 1817 { 1818 if (elements[2].equalsIgnoreCase("true") || elements[2].equals("1")) 1819 { 1820 changesOnly = true; 1821 } 1822 else if (elements[2].equalsIgnoreCase("false") || 1823 elements[2].equals("0")) 1824 { 1825 changesOnly = false; 1826 } 1827 else 1828 { 1829 throw new ArgumentException( 1830 ERR_LDAPSEARCH_PERSISTENT_SEARCH_INVALID_VALUE.get( 1831 persistentSearch.getIdentifierString())); 1832 } 1833 } 1834 1835 if (elements.length > 3) 1836 { 1837 if (elements[3].equalsIgnoreCase("true") || elements[3].equals("1")) 1838 { 1839 returnECs = true; 1840 } 1841 else if (elements[3].equalsIgnoreCase("false") || 1842 elements[3].equals("0")) 1843 { 1844 returnECs = false; 1845 } 1846 else 1847 { 1848 throw new ArgumentException( 1849 ERR_LDAPSEARCH_PERSISTENT_SEARCH_INVALID_VALUE.get( 1850 persistentSearch.getIdentifierString())); 1851 } 1852 } 1853 } 1854 catch (final ArgumentException ae) 1855 { 1856 Debug.debugException(ae); 1857 throw ae; 1858 } 1859 catch (final Exception e) 1860 { 1861 Debug.debugException(e); 1862 throw new ArgumentException( 1863 ERR_LDAPSEARCH_PERSISTENT_SEARCH_INVALID_VALUE.get( 1864 persistentSearch.getIdentifierString()), 1865 e); 1866 } 1867 1868 persistentSearchRequestControl = new PersistentSearchRequestControl( 1869 changeTypes, changesOnly, returnECs, true); 1870 } 1871 1872 1873 // If we should use the server-side sort request control, then validate the 1874 // sort order and pre-create the control. 1875 if (sortOrder.isPresent()) 1876 { 1877 final ArrayList<SortKey> sortKeyList = new ArrayList<>(5); 1878 final StringTokenizer tokenizer = 1879 new StringTokenizer(sortOrder.getValue(), ", "); 1880 while (tokenizer.hasMoreTokens()) 1881 { 1882 final String token = tokenizer.nextToken(); 1883 1884 final boolean ascending; 1885 String attributeName; 1886 if (token.startsWith("-")) 1887 { 1888 ascending = false; 1889 attributeName = token.substring(1); 1890 } 1891 else if (token.startsWith("+")) 1892 { 1893 ascending = true; 1894 attributeName = token.substring(1); 1895 } 1896 else 1897 { 1898 ascending = true; 1899 attributeName = token; 1900 } 1901 1902 final String matchingRuleID; 1903 final int colonPos = attributeName.indexOf(':'); 1904 if (colonPos >= 0) 1905 { 1906 matchingRuleID = attributeName.substring(colonPos+1); 1907 attributeName = attributeName.substring(0, colonPos); 1908 } 1909 else 1910 { 1911 matchingRuleID = null; 1912 } 1913 1914 final StringBuilder invalidReason = new StringBuilder(); 1915 if (! PersistUtils.isValidLDAPName(attributeName, false, invalidReason)) 1916 { 1917 throw new ArgumentException( 1918 ERR_LDAPSEARCH_SORT_ORDER_INVALID_VALUE.get( 1919 sortOrder.getIdentifierString())); 1920 } 1921 1922 sortKeyList.add( 1923 new SortKey(attributeName, matchingRuleID, (! ascending))); 1924 } 1925 1926 if (sortKeyList.isEmpty()) 1927 { 1928 throw new ArgumentException( 1929 ERR_LDAPSEARCH_SORT_ORDER_INVALID_VALUE.get( 1930 sortOrder.getIdentifierString())); 1931 } 1932 1933 final SortKey[] sortKeyArray = new SortKey[sortKeyList.size()]; 1934 sortKeyList.toArray(sortKeyArray); 1935 1936 sortRequestControl = new ServerSideSortRequestControl(sortKeyArray); 1937 } 1938 1939 1940 // If we should use the virtual list view request control, then validate the 1941 // argument value and pre-create the control. 1942 if (virtualListView.isPresent()) 1943 { 1944 try 1945 { 1946 final String[] elements = virtualListView.getValue().split(":"); 1947 if (elements.length == 4) 1948 { 1949 vlvRequestControl = new VirtualListViewRequestControl( 1950 Integer.parseInt(elements[2]), Integer.parseInt(elements[0]), 1951 Integer.parseInt(elements[1]), Integer.parseInt(elements[3]), 1952 null); 1953 } 1954 else if (elements.length == 3) 1955 { 1956 vlvRequestControl = new VirtualListViewRequestControl(elements[2], 1957 Integer.parseInt(elements[0]), Integer.parseInt(elements[1]), 1958 null); 1959 } 1960 else 1961 { 1962 throw new ArgumentException( 1963 ERR_LDAPSEARCH_VLV_INVALID_VALUE.get( 1964 virtualListView.getIdentifierString())); 1965 } 1966 } 1967 catch (final ArgumentException ae) 1968 { 1969 Debug.debugException(ae); 1970 throw ae; 1971 } 1972 catch (final Exception e) 1973 { 1974 Debug.debugException(e); 1975 throw new ArgumentException( 1976 ERR_LDAPSEARCH_VLV_INVALID_VALUE.get( 1977 virtualListView.getIdentifierString()), 1978 e); 1979 } 1980 } 1981 1982 1983 // If we should use the LDAP join request control, then validate and 1984 // pre-create that control. 1985 if (joinRule.isPresent()) 1986 { 1987 final JoinRule rule; 1988 try 1989 { 1990 final String[] elements = joinRule.getValue().toLowerCase().split(":"); 1991 final String ruleName = StaticUtils.toLowerCase(elements[0]); 1992 if (ruleName.equals("dn")) 1993 { 1994 rule = JoinRule.createDNJoin(elements[1]); 1995 } 1996 else if (ruleName.equals("reverse-dn") || ruleName.equals("reversedn")) 1997 { 1998 rule = JoinRule.createReverseDNJoin(elements[1]); 1999 } 2000 else if (ruleName.equals("equals") || ruleName.equals("equality")) 2001 { 2002 rule = JoinRule.createEqualityJoin(elements[1], elements[2], false); 2003 } 2004 else if (ruleName.equals("contains") || ruleName.equals("substring")) 2005 { 2006 rule = JoinRule.createContainsJoin(elements[1], elements[2], false); 2007 } 2008 else 2009 { 2010 throw new ArgumentException( 2011 ERR_LDAPSEARCH_JOIN_RULE_INVALID_VALUE.get( 2012 joinRule.getIdentifierString())); 2013 } 2014 } 2015 catch (final ArgumentException ae) 2016 { 2017 Debug.debugException(ae); 2018 throw ae; 2019 } 2020 catch (final Exception e) 2021 { 2022 Debug.debugException(e); 2023 throw new ArgumentException( 2024 ERR_LDAPSEARCH_JOIN_RULE_INVALID_VALUE.get( 2025 joinRule.getIdentifierString()), 2026 e); 2027 } 2028 2029 final JoinBaseDN joinBase; 2030 if (joinBaseDN.isPresent()) 2031 { 2032 final String s = StaticUtils.toLowerCase(joinBaseDN.getValue()); 2033 if (s.equals("search-base") || s.equals("search-base-dn")) 2034 { 2035 joinBase = JoinBaseDN.createUseSearchBaseDN(); 2036 } 2037 else if (s.equals("source-entry-dn") || s.equals("source-dn")) 2038 { 2039 joinBase = JoinBaseDN.createUseSourceEntryDN(); 2040 } 2041 else 2042 { 2043 try 2044 { 2045 final DN dn = new DN(joinBaseDN.getValue()); 2046 joinBase = JoinBaseDN.createUseCustomBaseDN(joinBaseDN.getValue()); 2047 } 2048 catch (final Exception e) 2049 { 2050 Debug.debugException(e); 2051 throw new ArgumentException( 2052 ERR_LDAPSEARCH_JOIN_BASE_DN_INVALID_VALUE.get( 2053 joinBaseDN.getIdentifierString()), 2054 e); 2055 } 2056 } 2057 } 2058 else 2059 { 2060 joinBase = JoinBaseDN.createUseSearchBaseDN(); 2061 } 2062 2063 final String[] joinAttrs; 2064 if (joinRequestedAttribute.isPresent()) 2065 { 2066 final List<String> valueList = joinRequestedAttribute.getValues(); 2067 joinAttrs = new String[valueList.size()]; 2068 valueList.toArray(joinAttrs); 2069 } 2070 else 2071 { 2072 joinAttrs = null; 2073 } 2074 2075 joinRequestControl = new JoinRequestControl(new JoinRequestValue(rule, 2076 joinBase, joinScope.getValue(), DereferencePolicy.NEVER, 2077 joinSizeLimit.getValue(), joinFilter.getValue(), joinAttrs, 2078 joinRequireMatch.isPresent(), null)); 2079 } 2080 2081 2082 // If we should use the route to backend set request control, then validate 2083 // and pre-create those controls. 2084 if (routeToBackendSet.isPresent()) 2085 { 2086 final List<String> values = routeToBackendSet.getValues(); 2087 final Map<String,List<String>> idsByRP = new LinkedHashMap<>( 2088 StaticUtils.computeMapCapacity(values.size())); 2089 for (final String value : values) 2090 { 2091 final int colonPos = value.indexOf(':'); 2092 if (colonPos <= 0) 2093 { 2094 throw new ArgumentException( 2095 ERR_LDAPSEARCH_ROUTE_TO_BACKEND_SET_INVALID_FORMAT.get(value, 2096 routeToBackendSet.getIdentifierString())); 2097 } 2098 2099 final String rpID = value.substring(0, colonPos); 2100 final String bsID = value.substring(colonPos+1); 2101 2102 List<String> idsForRP = idsByRP.get(rpID); 2103 if (idsForRP == null) 2104 { 2105 idsForRP = new ArrayList<>(values.size()); 2106 idsByRP.put(rpID, idsForRP); 2107 } 2108 idsForRP.add(bsID); 2109 } 2110 2111 for (final Map.Entry<String,List<String>> e : idsByRP.entrySet()) 2112 { 2113 final String rpID = e.getKey(); 2114 final List<String> bsIDs = e.getValue(); 2115 routeToBackendSetRequestControls.add( 2116 RouteToBackendSetRequestControl.createAbsoluteRoutingRequest(true, 2117 rpID, bsIDs)); 2118 } 2119 } 2120 2121 2122 // Parse the dereference policy. 2123 final String derefStr = 2124 StaticUtils.toLowerCase(dereferencePolicy.getValue()); 2125 if (derefStr.equals("always")) 2126 { 2127 derefPolicy = DereferencePolicy.ALWAYS; 2128 } 2129 else if (derefStr.equals("search")) 2130 { 2131 derefPolicy = DereferencePolicy.SEARCHING; 2132 } 2133 else if (derefStr.equals("find")) 2134 { 2135 derefPolicy = DereferencePolicy.FINDING; 2136 } 2137 else 2138 { 2139 derefPolicy = DereferencePolicy.NEVER; 2140 } 2141 2142 2143 // See if any entry transformations need to be applied. 2144 final ArrayList<EntryTransformation> transformations = new ArrayList<>(5); 2145 if (excludeAttribute.isPresent()) 2146 { 2147 transformations.add(new ExcludeAttributeTransformation(null, 2148 excludeAttribute.getValues())); 2149 } 2150 2151 if (redactAttribute.isPresent()) 2152 { 2153 transformations.add(new RedactAttributeTransformation(null, true, 2154 (! hideRedactedValueCount.isPresent()), 2155 redactAttribute.getValues())); 2156 } 2157 2158 if (scrambleAttribute.isPresent()) 2159 { 2160 final Long randomSeed; 2161 if (scrambleRandomSeed.isPresent()) 2162 { 2163 randomSeed = scrambleRandomSeed.getValue().longValue(); 2164 } 2165 else 2166 { 2167 randomSeed = null; 2168 } 2169 2170 transformations.add(new ScrambleAttributeTransformation(null, randomSeed, 2171 true, scrambleAttribute.getValues(), scrambleJSONField.getValues())); 2172 } 2173 2174 if (renameAttributeFrom.isPresent()) 2175 { 2176 if (renameAttributeFrom.getNumOccurrences() != 2177 renameAttributeTo.getNumOccurrences()) 2178 { 2179 throw new ArgumentException( 2180 ERR_LDAPSEARCH_RENAME_ATTRIBUTE_MISMATCH.get()); 2181 } 2182 2183 final Iterator<String> sourceIterator = 2184 renameAttributeFrom.getValues().iterator(); 2185 final Iterator<String> targetIterator = 2186 renameAttributeTo.getValues().iterator(); 2187 while (sourceIterator.hasNext()) 2188 { 2189 transformations.add(new RenameAttributeTransformation(null, 2190 sourceIterator.next(), targetIterator.next(), true)); 2191 } 2192 } 2193 2194 if (moveSubtreeFrom.isPresent()) 2195 { 2196 if (moveSubtreeFrom.getNumOccurrences() != 2197 moveSubtreeTo.getNumOccurrences()) 2198 { 2199 throw new ArgumentException(ERR_LDAPSEARCH_MOVE_SUBTREE_MISMATCH.get()); 2200 } 2201 2202 final Iterator<DN> sourceIterator = 2203 moveSubtreeFrom.getValues().iterator(); 2204 final Iterator<DN> targetIterator = moveSubtreeTo.getValues().iterator(); 2205 while (sourceIterator.hasNext()) 2206 { 2207 transformations.add(new MoveSubtreeTransformation(sourceIterator.next(), 2208 targetIterator.next())); 2209 } 2210 } 2211 2212 if (! transformations.isEmpty()) 2213 { 2214 entryTransformations = transformations; 2215 } 2216 2217 2218 // Create the result writer. 2219 final String outputFormatStr = 2220 StaticUtils.toLowerCase(outputFormat.getValue()); 2221 if (outputFormatStr.equals("json")) 2222 { 2223 resultWriter = new JSONLDAPResultWriter(getOutStream()); 2224 } 2225 else if (outputFormatStr.equals("csv") || 2226 outputFormatStr.equals("multi-valued-csv") || 2227 outputFormatStr.equals("tab-delimited") || 2228 outputFormatStr.equals("multi-valued-tab-delimited")) 2229 { 2230 // These output formats cannot be used with the --ldapURLFile argument. 2231 if (ldapURLFile.isPresent()) 2232 { 2233 throw new ArgumentException( 2234 ERR_LDAPSEARCH_OUTPUT_FORMAT_NOT_SUPPORTED_WITH_URLS.get( 2235 outputFormat.getValue(), ldapURLFile.getIdentifierString())); 2236 } 2237 2238 // These output formats require the requested attributes to be specified 2239 // via the --requestedAttribute argument rather than as unnamed trailing 2240 // arguments. 2241 final List<String> requestedAttributes = requestedAttribute.getValues(); 2242 if ((requestedAttributes == null) || requestedAttributes.isEmpty()) 2243 { 2244 throw new ArgumentException( 2245 ERR_LDAPSEARCH_OUTPUT_FORMAT_REQUIRES_REQUESTED_ATTR_ARG.get( 2246 outputFormat.getValue(), 2247 requestedAttribute.getIdentifierString())); 2248 } 2249 2250 switch (trailingArgs.size()) 2251 { 2252 case 0: 2253 // This is fine. 2254 break; 2255 2256 case 1: 2257 // Make sure that the trailing argument is a filter rather than a 2258 // requested attribute. It's sufficient to ensure that neither the 2259 // filter nor filterFile argument was provided. 2260 if (filter.isPresent() || filterFile.isPresent()) 2261 { 2262 throw new ArgumentException( 2263 ERR_LDAPSEARCH_OUTPUT_FORMAT_REQUIRES_REQUESTED_ATTR_ARG.get( 2264 outputFormat.getValue(), 2265 requestedAttribute.getIdentifierString())); 2266 } 2267 break; 2268 2269 default: 2270 throw new ArgumentException( 2271 ERR_LDAPSEARCH_OUTPUT_FORMAT_REQUIRES_REQUESTED_ATTR_ARG.get( 2272 outputFormat.getValue(), 2273 requestedAttribute.getIdentifierString())); 2274 } 2275 2276 final OutputFormat format; 2277 final boolean includeAllValues; 2278 switch (outputFormatStr) 2279 { 2280 case "multi-valued-csv": 2281 format = OutputFormat.CSV; 2282 includeAllValues = true; 2283 break; 2284 case "tab-delimited": 2285 format = OutputFormat.TAB_DELIMITED_TEXT; 2286 includeAllValues = false; 2287 break; 2288 case "multi-valued-tab-delimited": 2289 format = OutputFormat.TAB_DELIMITED_TEXT; 2290 includeAllValues = true; 2291 break; 2292 case "csv": 2293 default: 2294 format = OutputFormat.CSV; 2295 includeAllValues = false; 2296 break; 2297 } 2298 2299 2300 resultWriter = new ColumnBasedLDAPResultWriter(getOutStream(), 2301 format, requestedAttributes, WRAP_COLUMN, includeAllValues); 2302 } 2303 else if (outputFormatStr.equals("dns-only")) 2304 { 2305 resultWriter = new DNsOnlyLDAPResultWriter(getOutStream()); 2306 } 2307 else if (outputFormatStr.equals("values-only")) 2308 { 2309 resultWriter = new ValuesOnlyLDAPResultWriter(getOutStream()); 2310 } 2311 else 2312 { 2313 resultWriter = new LDIFLDAPResultWriter(getOutStream(), WRAP_COLUMN); 2314 } 2315 } 2316 2317 2318 2319 /** 2320 * {@inheritDoc} 2321 */ 2322 @Override() 2323 @NotNull() 2324 public LDAPConnectionOptions getConnectionOptions() 2325 { 2326 final LDAPConnectionOptions options = new LDAPConnectionOptions(); 2327 2328 options.setUseSynchronousMode(true); 2329 options.setFollowReferrals(followReferrals.isPresent()); 2330 options.setUnsolicitedNotificationHandler(this); 2331 options.setResponseTimeoutMillis(0L); 2332 2333 return options; 2334 } 2335 2336 2337 2338 /** 2339 * {@inheritDoc} 2340 */ 2341 @Override() 2342 @NotNull() 2343 public ResultCode doToolProcessing() 2344 { 2345 // If we should encrypt the output, then get the encryption passphrase. 2346 if (encryptOutput.isPresent()) 2347 { 2348 if (encryptionPassphraseFile.isPresent()) 2349 { 2350 try 2351 { 2352 encryptionPassphrase = ToolUtils.readEncryptionPassphraseFromFile( 2353 encryptionPassphraseFile.getValue()); 2354 } 2355 catch (final LDAPException e) 2356 { 2357 Debug.debugException(e); 2358 wrapErr(0, WRAP_COLUMN, e.getMessage()); 2359 return e.getResultCode(); 2360 } 2361 } 2362 else 2363 { 2364 try 2365 { 2366 encryptionPassphrase = ToolUtils.promptForEncryptionPassphrase(false, 2367 true, getOut(), getErr()); 2368 } 2369 catch (final LDAPException e) 2370 { 2371 Debug.debugException(e); 2372 wrapErr(0, WRAP_COLUMN, e.getMessage()); 2373 return e.getResultCode(); 2374 } 2375 } 2376 } 2377 2378 2379 // If we should use an output file, then set that up now. Otherwise, write 2380 // the header to standard output. 2381 if (outputFile.isPresent()) 2382 { 2383 if (! separateOutputFilePerSearch.isPresent()) 2384 { 2385 try 2386 { 2387 OutputStream s = new FileOutputStream(outputFile.getValue()); 2388 2389 if (encryptOutput.isPresent()) 2390 { 2391 s = new PassphraseEncryptedOutputStream(encryptionPassphrase, s); 2392 } 2393 2394 if (compressOutput.isPresent()) 2395 { 2396 s = new GZIPOutputStream(s); 2397 } 2398 2399 if (teeResultsToStandardOut.isPresent()) 2400 { 2401 outStream = new PrintStream(new TeeOutputStream(s, getOut())); 2402 } 2403 else 2404 { 2405 outStream = new PrintStream(s); 2406 } 2407 resultWriter.updateOutputStream(outStream); 2408 errStream = outStream; 2409 } 2410 catch (final Exception e) 2411 { 2412 Debug.debugException(e); 2413 wrapErr(0, WRAP_COLUMN, ERR_LDAPSEARCH_CANNOT_OPEN_OUTPUT_FILE.get( 2414 outputFile.getValue().getAbsolutePath(), 2415 StaticUtils.getExceptionMessage(e))); 2416 return ResultCode.LOCAL_ERROR; 2417 } 2418 2419 resultWriter.writeHeader(); 2420 } 2421 } 2422 else 2423 { 2424 resultWriter.writeHeader(); 2425 } 2426 2427 2428 // Examine the arguments to determine the sets of controls to use for each 2429 // type of request. 2430 final List<Control> searchControls = getSearchControls(); 2431 2432 2433 // If appropriate, ensure that any search result entries that include 2434 // base64-encoded attribute values will also include comments that attempt 2435 // to provide a human-readable representation of that value. 2436 final boolean originalCommentAboutBase64EncodedValues = 2437 LDIFWriter.commentAboutBase64EncodedValues(); 2438 LDIFWriter.setCommentAboutBase64EncodedValues( 2439 ! suppressBase64EncodedValueComments.isPresent()); 2440 2441 2442 LDAPConnectionPool pool = null; 2443 try 2444 { 2445 // Create a connection pool that will be used to communicate with the 2446 // directory server. 2447 if (! dryRun.isPresent()) 2448 { 2449 try 2450 { 2451 final StartAdministrativeSessionPostConnectProcessor p; 2452 if (useAdministrativeSession.isPresent()) 2453 { 2454 p = new StartAdministrativeSessionPostConnectProcessor( 2455 new StartAdministrativeSessionExtendedRequest(getToolName(), 2456 true)); 2457 } 2458 else 2459 { 2460 p = null; 2461 } 2462 2463 pool = getConnectionPool(1, 1, 0, p, null, true, 2464 new ReportBindResultLDAPConnectionPoolHealthCheck(this, true, 2465 false)); 2466 } 2467 catch (final LDAPException le) 2468 { 2469 // This shouldn't happen since the pool won't throw an exception if an 2470 // attempt to create an initial connection fails. 2471 Debug.debugException(le); 2472 commentToErr(ERR_LDAPSEARCH_CANNOT_CREATE_CONNECTION_POOL.get( 2473 StaticUtils.getExceptionMessage(le))); 2474 return le.getResultCode(); 2475 } 2476 2477 if (retryFailedOperations.isPresent()) 2478 { 2479 pool.setRetryFailedOperationsDueToInvalidConnections(true); 2480 } 2481 } 2482 2483 2484 // If appropriate, create a rate limiter. 2485 final FixedRateBarrier rateLimiter; 2486 if (ratePerSecond.isPresent()) 2487 { 2488 rateLimiter = new FixedRateBarrier(1000L, ratePerSecond.getValue()); 2489 } 2490 else 2491 { 2492 rateLimiter = null; 2493 } 2494 2495 2496 // If one or more LDAP URL files are provided, then construct search 2497 // requests from those URLs. 2498 if (ldapURLFile.isPresent()) 2499 { 2500 return searchWithLDAPURLs(pool, rateLimiter, searchControls); 2501 } 2502 2503 2504 // Get the set of requested attributes, as a combination of the 2505 // requestedAttribute argument values and any trailing arguments. 2506 final ArrayList<String> attrList = new ArrayList<>(10); 2507 if (requestedAttribute.isPresent()) 2508 { 2509 attrList.addAll(requestedAttribute.getValues()); 2510 } 2511 2512 final List<String> trailingArgs = parser.getTrailingArguments(); 2513 if (! trailingArgs.isEmpty()) 2514 { 2515 final Iterator<String> trailingArgIterator = trailingArgs.iterator(); 2516 if (! (filter.isPresent() || filterFile.isPresent())) 2517 { 2518 trailingArgIterator.next(); 2519 } 2520 2521 while (trailingArgIterator.hasNext()) 2522 { 2523 attrList.add(trailingArgIterator.next()); 2524 } 2525 } 2526 2527 final String[] attributes = new String[attrList.size()]; 2528 attrList.toArray(attributes); 2529 2530 2531 // If either or both the filter or filterFile arguments are provided, then 2532 // use them to get the filters to process. Otherwise, the first trailing 2533 // argument should be a filter. 2534 ResultCode resultCode = ResultCode.SUCCESS; 2535 if (filter.isPresent() || filterFile.isPresent()) 2536 { 2537 if (filter.isPresent()) 2538 { 2539 for (final Filter f : filter.getValues()) 2540 { 2541 final ResultCode rc = searchWithFilter(pool, f, attributes, 2542 rateLimiter, searchControls); 2543 if (rc != ResultCode.SUCCESS) 2544 { 2545 if (resultCode == ResultCode.SUCCESS) 2546 { 2547 resultCode = rc; 2548 } 2549 2550 if (! continueOnError.isPresent()) 2551 { 2552 return resultCode; 2553 } 2554 } 2555 } 2556 } 2557 2558 if (filterFile.isPresent()) 2559 { 2560 final ResultCode rc = searchWithFilterFile(pool, attributes, 2561 rateLimiter, searchControls); 2562 if (rc != ResultCode.SUCCESS) 2563 { 2564 if (resultCode == ResultCode.SUCCESS) 2565 { 2566 resultCode = rc; 2567 } 2568 2569 if (! continueOnError.isPresent()) 2570 { 2571 return resultCode; 2572 } 2573 } 2574 } 2575 } 2576 else 2577 { 2578 final Filter f; 2579 try 2580 { 2581 final String filterStr = 2582 parser.getTrailingArguments().iterator().next(); 2583 f = Filter.create(filterStr); 2584 } 2585 catch (final LDAPException le) 2586 { 2587 // This should never happen. 2588 Debug.debugException(le); 2589 displayResult(le.toLDAPResult()); 2590 return le.getResultCode(); 2591 } 2592 2593 resultCode = 2594 searchWithFilter(pool, f, attributes, rateLimiter, searchControls); 2595 } 2596 2597 return resultCode; 2598 } 2599 finally 2600 { 2601 if (pool != null) 2602 { 2603 try 2604 { 2605 pool.close(); 2606 } 2607 catch (final Exception e) 2608 { 2609 Debug.debugException(e); 2610 } 2611 } 2612 2613 if (outStream != null) 2614 { 2615 try 2616 { 2617 outStream.close(); 2618 outStream = null; 2619 } 2620 catch (final Exception e) 2621 { 2622 Debug.debugException(e); 2623 } 2624 } 2625 2626 if (errStream != null) 2627 { 2628 try 2629 { 2630 errStream.close(); 2631 errStream = null; 2632 } 2633 catch (final Exception e) 2634 { 2635 Debug.debugException(e); 2636 } 2637 } 2638 2639 LDIFWriter.setCommentAboutBase64EncodedValues( 2640 originalCommentAboutBase64EncodedValues); 2641 } 2642 } 2643 2644 2645 2646 /** 2647 * Processes a set of searches using LDAP URLs read from one or more files. 2648 * 2649 * @param pool The connection pool to use to communicate with the 2650 * directory server. 2651 * @param rateLimiter An optional fixed-rate barrier that can be used for 2652 * request rate limiting. 2653 * @param searchControls The set of controls to include in search requests. 2654 * 2655 * @return A result code indicating the result of the processing. 2656 */ 2657 @NotNull() 2658 private ResultCode searchWithLDAPURLs(@NotNull final LDAPConnectionPool pool, 2659 @Nullable final FixedRateBarrier rateLimiter, 2660 @NotNull final List<Control> searchControls) 2661 { 2662 ResultCode resultCode = ResultCode.SUCCESS; 2663 for (final File f : ldapURLFile.getValues()) 2664 { 2665 BufferedReader reader = null; 2666 2667 try 2668 { 2669 reader = new BufferedReader(new FileReader(f)); 2670 while (true) 2671 { 2672 final String line = reader.readLine(); 2673 if (line == null) 2674 { 2675 break; 2676 } 2677 2678 if ((line.length() == 0) || line.startsWith("#")) 2679 { 2680 continue; 2681 } 2682 2683 final LDAPURL url; 2684 try 2685 { 2686 url = new LDAPURL(line); 2687 } 2688 catch (final LDAPException le) 2689 { 2690 Debug.debugException(le); 2691 2692 commentToErr(ERR_LDAPSEARCH_MALFORMED_LDAP_URL.get( 2693 f.getAbsolutePath(), line)); 2694 if (resultCode == ResultCode.SUCCESS) 2695 { 2696 resultCode = le.getResultCode(); 2697 } 2698 2699 if (continueOnError.isPresent()) 2700 { 2701 continue; 2702 } 2703 else 2704 { 2705 return resultCode; 2706 } 2707 } 2708 2709 final SearchRequest searchRequest = new SearchRequest( 2710 new LDAPSearchListener(resultWriter, entryTransformations), 2711 url.getBaseDN().toString(), url.getScope(), derefPolicy, 2712 sizeLimit.getValue(), timeLimitSeconds.getValue(), 2713 typesOnly.isPresent(), url.getFilter(), url.getAttributes()); 2714 final ResultCode rc = 2715 doSearch(pool, searchRequest, rateLimiter, searchControls); 2716 if (rc != ResultCode.SUCCESS) 2717 { 2718 if (resultCode == ResultCode.SUCCESS) 2719 { 2720 resultCode = rc; 2721 } 2722 2723 if (! continueOnError.isPresent()) 2724 { 2725 return resultCode; 2726 } 2727 } 2728 } 2729 } 2730 catch (final IOException ioe) 2731 { 2732 commentToErr(ERR_LDAPSEARCH_CANNOT_READ_LDAP_URL_FILE.get( 2733 f.getAbsolutePath(), StaticUtils.getExceptionMessage(ioe))); 2734 return ResultCode.LOCAL_ERROR; 2735 } 2736 finally 2737 { 2738 if (reader != null) 2739 { 2740 try 2741 { 2742 reader.close(); 2743 } 2744 catch (final Exception e) 2745 { 2746 Debug.debugException(e); 2747 } 2748 } 2749 } 2750 } 2751 2752 return resultCode; 2753 } 2754 2755 2756 2757 /** 2758 * Processes a set of searches using filters read from one or more files. 2759 * 2760 * @param pool The connection pool to use to communicate with the 2761 * directory server. 2762 * @param attributes The set of attributes to request that the server 2763 * include in matching entries. 2764 * @param rateLimiter An optional fixed-rate barrier that can be used for 2765 * request rate limiting. 2766 * @param searchControls The set of controls to include in search requests. 2767 * 2768 * @return A result code indicating the result of the processing. 2769 */ 2770 @NotNull() 2771 private ResultCode searchWithFilterFile( 2772 @NotNull final LDAPConnectionPool pool, 2773 @NotNull final String[] attributes, 2774 @Nullable final FixedRateBarrier rateLimiter, 2775 @NotNull final List<Control> searchControls) 2776 { 2777 ResultCode resultCode = ResultCode.SUCCESS; 2778 for (final File f : filterFile.getValues()) 2779 { 2780 FilterFileReader reader = null; 2781 2782 try 2783 { 2784 reader = new FilterFileReader(f); 2785 while (true) 2786 { 2787 final Filter searchFilter; 2788 try 2789 { 2790 searchFilter = reader.readFilter(); 2791 } 2792 catch (final LDAPException le) 2793 { 2794 Debug.debugException(le); 2795 commentToErr(ERR_LDAPSEARCH_MALFORMED_FILTER.get( 2796 f.getAbsolutePath(), le.getMessage())); 2797 if (resultCode == ResultCode.SUCCESS) 2798 { 2799 resultCode = le.getResultCode(); 2800 } 2801 2802 if (continueOnError.isPresent()) 2803 { 2804 continue; 2805 } 2806 else 2807 { 2808 return resultCode; 2809 } 2810 } 2811 2812 if (searchFilter == null) 2813 { 2814 break; 2815 } 2816 2817 final ResultCode rc = searchWithFilter(pool, searchFilter, attributes, 2818 rateLimiter, searchControls); 2819 if (rc != ResultCode.SUCCESS) 2820 { 2821 if (resultCode == ResultCode.SUCCESS) 2822 { 2823 resultCode = rc; 2824 } 2825 2826 if (! continueOnError.isPresent()) 2827 { 2828 return resultCode; 2829 } 2830 } 2831 } 2832 } 2833 catch (final IOException ioe) 2834 { 2835 Debug.debugException(ioe); 2836 commentToErr(ERR_LDAPSEARCH_CANNOT_READ_FILTER_FILE.get( 2837 f.getAbsolutePath(), StaticUtils.getExceptionMessage(ioe))); 2838 return ResultCode.LOCAL_ERROR; 2839 } 2840 finally 2841 { 2842 if (reader != null) 2843 { 2844 try 2845 { 2846 reader.close(); 2847 } 2848 catch (final Exception e) 2849 { 2850 Debug.debugException(e); 2851 } 2852 } 2853 } 2854 } 2855 2856 return resultCode; 2857 } 2858 2859 2860 2861 /** 2862 * Processes a search using the provided filter. 2863 * 2864 * @param pool The connection pool to use to communicate with the 2865 * directory server. 2866 * @param filter The filter to use for the search. 2867 * @param attributes The set of attributes to request that the server 2868 * include in matching entries. 2869 * @param rateLimiter An optional fixed-rate barrier that can be used for 2870 * request rate limiting. 2871 * @param searchControls The set of controls to include in search requests. 2872 * 2873 * @return A result code indicating the result of the processing. 2874 */ 2875 @NotNull() 2876 private ResultCode searchWithFilter(@NotNull final LDAPConnectionPool pool, 2877 @NotNull final Filter filter, 2878 @NotNull final String[] attributes, 2879 @Nullable final FixedRateBarrier rateLimiter, 2880 @NotNull final List<Control> searchControls) 2881 { 2882 final String baseDNString; 2883 if (baseDN.isPresent()) 2884 { 2885 baseDNString = baseDN.getStringValue(); 2886 } 2887 else 2888 { 2889 baseDNString = ""; 2890 } 2891 2892 final SearchRequest searchRequest = new SearchRequest( 2893 new LDAPSearchListener(resultWriter, entryTransformations), 2894 baseDNString, scope.getValue(), derefPolicy, sizeLimit.getValue(), 2895 timeLimitSeconds.getValue(), typesOnly.isPresent(), filter, 2896 attributes); 2897 return doSearch(pool, searchRequest, rateLimiter, searchControls); 2898 } 2899 2900 2901 2902 /** 2903 * Processes a search with the provided information. 2904 * 2905 * @param pool The connection pool to use to communicate with the 2906 * directory server. 2907 * @param searchRequest The search request to process. 2908 * @param rateLimiter An optional fixed-rate barrier that can be used for 2909 * request rate limiting. 2910 * @param searchControls The set of controls to include in search requests. 2911 * 2912 * @return A result code indicating the result of the processing. 2913 */ 2914 @NotNull() 2915 private ResultCode doSearch(@NotNull final LDAPConnectionPool pool, 2916 @NotNull final SearchRequest searchRequest, 2917 @Nullable final FixedRateBarrier rateLimiter, 2918 @NotNull final List<Control> searchControls) 2919 { 2920 if (separateOutputFilePerSearch.isPresent()) 2921 { 2922 try 2923 { 2924 final String path = outputFile.getValue().getAbsolutePath() + '.' + 2925 outputFileCounter.getAndIncrement(); 2926 2927 OutputStream s = new FileOutputStream(path); 2928 2929 if (encryptOutput.isPresent()) 2930 { 2931 s = new PassphraseEncryptedOutputStream(encryptionPassphrase, s); 2932 } 2933 2934 if (compressOutput.isPresent()) 2935 { 2936 s = new GZIPOutputStream(s); 2937 } 2938 2939 if (teeResultsToStandardOut.isPresent()) 2940 { 2941 outStream = new PrintStream(new TeeOutputStream(s, getOut())); 2942 } 2943 else 2944 { 2945 outStream = new PrintStream(s); 2946 } 2947 resultWriter.updateOutputStream(outStream); 2948 errStream = outStream; 2949 } 2950 catch (final Exception e) 2951 { 2952 Debug.debugException(e); 2953 wrapErr(0, WRAP_COLUMN, ERR_LDAPSEARCH_CANNOT_OPEN_OUTPUT_FILE.get( 2954 outputFile.getValue().getAbsolutePath(), 2955 StaticUtils.getExceptionMessage(e))); 2956 return ResultCode.LOCAL_ERROR; 2957 } 2958 2959 resultWriter.writeHeader(); 2960 } 2961 2962 try 2963 { 2964 if (rateLimiter != null) 2965 { 2966 rateLimiter.await(); 2967 } 2968 2969 2970 ASN1OctetString pagedResultsCookie = null; 2971 boolean multiplePages = false; 2972 long totalEntries = 0; 2973 long totalReferences = 0; 2974 2975 SearchResult searchResult; 2976 try 2977 { 2978 while (true) 2979 { 2980 searchRequest.setControls(searchControls); 2981 if (simplePageSize.isPresent()) 2982 { 2983 searchRequest.addControl(new SimplePagedResultsControl( 2984 simplePageSize.getValue(), pagedResultsCookie)); 2985 } 2986 2987 if (dryRun.isPresent()) 2988 { 2989 searchResult = new SearchResult(-1, ResultCode.SUCCESS, 2990 INFO_LDAPSEARCH_DRY_RUN_REQUEST_NOT_SENT.get( 2991 dryRun.getIdentifierString(), 2992 String.valueOf(searchRequest)), 2993 null, null, 0, 0, null); 2994 break; 2995 } 2996 else 2997 { 2998 if (! terse.isPresent()) 2999 { 3000 if (verbose.isPresent() || persistentSearch.isPresent() || 3001 filterFile.isPresent() || ldapURLFile.isPresent() || 3002 (filter.isPresent() && (filter.getNumOccurrences() > 1))) 3003 { 3004 commentToOut(INFO_LDAPSEARCH_SENDING_SEARCH_REQUEST.get( 3005 String.valueOf(searchRequest))); 3006 } 3007 } 3008 searchResult = pool.search(searchRequest); 3009 } 3010 3011 if (searchResult.getEntryCount() > 0) 3012 { 3013 totalEntries += searchResult.getEntryCount(); 3014 } 3015 3016 if (searchResult.getReferenceCount() > 0) 3017 { 3018 totalReferences += searchResult.getReferenceCount(); 3019 } 3020 3021 if (simplePageSize.isPresent()) 3022 { 3023 final SimplePagedResultsControl pagedResultsControl; 3024 try 3025 { 3026 pagedResultsControl = SimplePagedResultsControl.get(searchResult); 3027 if (pagedResultsControl == null) 3028 { 3029 throw new LDAPSearchException(new SearchResult( 3030 searchResult.getMessageID(), ResultCode.CONTROL_NOT_FOUND, 3031 ERR_LDAPSEARCH_MISSING_PAGED_RESULTS_RESPONSE_CONTROL. 3032 get(), 3033 searchResult.getMatchedDN(), 3034 searchResult.getReferralURLs(), 3035 searchResult.getSearchEntries(), 3036 searchResult.getSearchReferences(), 3037 searchResult.getEntryCount(), 3038 searchResult.getReferenceCount(), 3039 searchResult.getResponseControls())); 3040 } 3041 3042 if (pagedResultsControl.moreResultsToReturn()) 3043 { 3044 if (verbose.isPresent()) 3045 { 3046 commentToOut( 3047 INFO_LDAPSEARCH_INTERMEDIATE_PAGED_SEARCH_RESULT.get()); 3048 displayResult(searchResult); 3049 } 3050 3051 multiplePages = true; 3052 pagedResultsCookie = pagedResultsControl.getCookie(); 3053 } 3054 else 3055 { 3056 break; 3057 } 3058 } 3059 catch (final LDAPException le) 3060 { 3061 Debug.debugException(le); 3062 throw new LDAPSearchException(new SearchResult( 3063 searchResult.getMessageID(), ResultCode.CONTROL_NOT_FOUND, 3064 ERR_LDAPSEARCH_CANNOT_DECODE_PAGED_RESULTS_RESPONSE_CONTROL. 3065 get(StaticUtils.getExceptionMessage(le)), 3066 searchResult.getMatchedDN(), searchResult.getReferralURLs(), 3067 searchResult.getSearchEntries(), 3068 searchResult.getSearchReferences(), 3069 searchResult.getEntryCount(), 3070 searchResult.getReferenceCount(), 3071 searchResult.getResponseControls())); 3072 } 3073 } 3074 else 3075 { 3076 break; 3077 } 3078 } 3079 } 3080 catch (final LDAPSearchException lse) 3081 { 3082 Debug.debugException(lse); 3083 searchResult = lse.toLDAPResult(); 3084 3085 if (searchResult.getEntryCount() > 0) 3086 { 3087 totalEntries += searchResult.getEntryCount(); 3088 } 3089 3090 if (searchResult.getReferenceCount() > 0) 3091 { 3092 totalReferences += searchResult.getReferenceCount(); 3093 } 3094 } 3095 3096 if ((searchResult.getResultCode() != ResultCode.SUCCESS) || 3097 (searchResult.getDiagnosticMessage() != null) || 3098 (! terse.isPresent())) 3099 { 3100 displayResult(searchResult); 3101 } 3102 3103 if (multiplePages && (! terse.isPresent())) 3104 { 3105 commentToOut(INFO_LDAPSEARCH_TOTAL_SEARCH_ENTRIES.get(totalEntries)); 3106 3107 if (totalReferences > 0) 3108 { 3109 commentToOut(INFO_LDAPSEARCH_TOTAL_SEARCH_REFERENCES.get( 3110 totalReferences)); 3111 } 3112 } 3113 3114 if (countEntries.isPresent()) 3115 { 3116 return ResultCode.valueOf((int) Math.min(totalEntries, 255)); 3117 } 3118 else if (requireMatch.isPresent() && (totalEntries == 0)) 3119 { 3120 return ResultCode.NO_RESULTS_RETURNED; 3121 } 3122 else 3123 { 3124 return searchResult.getResultCode(); 3125 } 3126 } 3127 finally 3128 { 3129 if (separateOutputFilePerSearch.isPresent()) 3130 { 3131 try 3132 { 3133 outStream.close(); 3134 } 3135 catch (final Exception e) 3136 { 3137 Debug.debugException(e); 3138 } 3139 3140 outStream = null; 3141 errStream = null; 3142 } 3143 } 3144 } 3145 3146 3147 3148 /** 3149 * Retrieves a list of the controls that should be used when processing search 3150 * operations. 3151 * 3152 * @return A list of the controls that should be used when processing search 3153 * operations. 3154 * 3155 * @throws LDAPException If a problem is encountered while generating the 3156 * controls for a search request. 3157 */ 3158 @NotNull() 3159 private List<Control> getSearchControls() 3160 { 3161 final ArrayList<Control> controls = new ArrayList<>(10); 3162 3163 if (searchControl.isPresent()) 3164 { 3165 controls.addAll(searchControl.getValues()); 3166 } 3167 3168 if (joinRequestControl != null) 3169 { 3170 controls.add(joinRequestControl); 3171 } 3172 3173 if (matchedValuesRequestControl != null) 3174 { 3175 controls.add(matchedValuesRequestControl); 3176 } 3177 3178 if (matchingEntryCountRequestControl != null) 3179 { 3180 controls.add(matchingEntryCountRequestControl); 3181 } 3182 3183 if (overrideSearchLimitsRequestControl != null) 3184 { 3185 controls.add(overrideSearchLimitsRequestControl); 3186 } 3187 3188 if (persistentSearchRequestControl != null) 3189 { 3190 controls.add(persistentSearchRequestControl); 3191 } 3192 3193 if (sortRequestControl != null) 3194 { 3195 controls.add(sortRequestControl); 3196 } 3197 3198 if (vlvRequestControl != null) 3199 { 3200 controls.add(vlvRequestControl); 3201 } 3202 3203 controls.addAll(routeToBackendSetRequestControls); 3204 3205 if (accountUsable.isPresent()) 3206 { 3207 controls.add(new AccountUsableRequestControl(true)); 3208 } 3209 3210 if (getBackendSetID.isPresent()) 3211 { 3212 controls.add(new GetBackendSetIDRequestControl(false)); 3213 } 3214 3215 if (getServerID.isPresent()) 3216 { 3217 controls.add(new GetServerIDRequestControl(false)); 3218 } 3219 3220 if (includeReplicationConflictEntries.isPresent()) 3221 { 3222 controls.add(new ReturnConflictEntriesRequestControl(true)); 3223 } 3224 3225 if (includeSoftDeletedEntries.isPresent()) 3226 { 3227 final String valueStr = 3228 StaticUtils.toLowerCase(includeSoftDeletedEntries.getValue()); 3229 if (valueStr.equals("with-non-deleted-entries")) 3230 { 3231 controls.add(new SoftDeletedEntryAccessRequestControl(true, true, 3232 false)); 3233 } 3234 else if (valueStr.equals("without-non-deleted-entries")) 3235 { 3236 controls.add(new SoftDeletedEntryAccessRequestControl(true, false, 3237 false)); 3238 } 3239 else 3240 { 3241 controls.add(new SoftDeletedEntryAccessRequestControl(true, false, 3242 true)); 3243 } 3244 } 3245 3246 if (draftLDUPSubentries.isPresent()) 3247 { 3248 controls.add(new DraftLDUPSubentriesRequestControl(true)); 3249 } 3250 3251 if (rfc3672Subentries.isPresent()) 3252 { 3253 controls.add(new RFC3672SubentriesRequestControl( 3254 rfc3672Subentries.getValue())); 3255 } 3256 3257 if (manageDsaIT.isPresent()) 3258 { 3259 controls.add(new ManageDsaITRequestControl(true)); 3260 } 3261 3262 if (realAttributesOnly.isPresent()) 3263 { 3264 controls.add(new RealAttributesOnlyRequestControl(true)); 3265 } 3266 3267 if (routeToServer.isPresent()) 3268 { 3269 controls.add(new RouteToServerRequestControl(false, 3270 routeToServer.getValue(), false, false, false)); 3271 } 3272 3273 if (virtualAttributesOnly.isPresent()) 3274 { 3275 controls.add(new VirtualAttributesOnlyRequestControl(true)); 3276 } 3277 3278 if (excludeBranch.isPresent()) 3279 { 3280 final ArrayList<String> dns = 3281 new ArrayList<>(excludeBranch.getValues().size()); 3282 for (final DN dn : excludeBranch.getValues()) 3283 { 3284 dns.add(dn.toString()); 3285 } 3286 controls.add(new ExcludeBranchRequestControl(true, dns)); 3287 } 3288 3289 if (assertionFilter.isPresent()) 3290 { 3291 controls.add(new AssertionRequestControl( 3292 assertionFilter.getValue(), true)); 3293 } 3294 3295 if (getEffectiveRightsAuthzID.isPresent()) 3296 { 3297 final String[] attributes; 3298 if (getEffectiveRightsAttribute.isPresent()) 3299 { 3300 attributes = new String[getEffectiveRightsAttribute.getValues().size()]; 3301 for (int i=0; i < attributes.length; i++) 3302 { 3303 attributes[i] = getEffectiveRightsAttribute.getValues().get(i); 3304 } 3305 } 3306 else 3307 { 3308 attributes = StaticUtils.NO_STRINGS; 3309 } 3310 3311 controls.add(new GetEffectiveRightsRequestControl(true, 3312 getEffectiveRightsAuthzID.getValue(), attributes)); 3313 } 3314 3315 if (operationPurpose.isPresent()) 3316 { 3317 controls.add(new OperationPurposeRequestControl(true, "ldapsearch", 3318 Version.NUMERIC_VERSION_STRING, "LDAPSearch.getSearchControls", 3319 operationPurpose.getValue())); 3320 } 3321 3322 if (proxyAs.isPresent()) 3323 { 3324 controls.add(new ProxiedAuthorizationV2RequestControl( 3325 proxyAs.getValue())); 3326 } 3327 3328 if (proxyV1As.isPresent()) 3329 { 3330 controls.add(new ProxiedAuthorizationV1RequestControl( 3331 proxyV1As.getValue())); 3332 } 3333 3334 if (suppressOperationalAttributeUpdates.isPresent()) 3335 { 3336 final EnumSet<SuppressType> suppressTypes = 3337 EnumSet.noneOf(SuppressType.class); 3338 for (final String s : suppressOperationalAttributeUpdates.getValues()) 3339 { 3340 if (s.equalsIgnoreCase("last-access-time")) 3341 { 3342 suppressTypes.add(SuppressType.LAST_ACCESS_TIME); 3343 } 3344 else if (s.equalsIgnoreCase("last-login-time")) 3345 { 3346 suppressTypes.add(SuppressType.LAST_LOGIN_TIME); 3347 } 3348 else if (s.equalsIgnoreCase("last-login-ip")) 3349 { 3350 suppressTypes.add(SuppressType.LAST_LOGIN_IP); 3351 } 3352 } 3353 3354 controls.add(new SuppressOperationalAttributeUpdateRequestControl( 3355 suppressTypes)); 3356 } 3357 3358 if (rejectUnindexedSearch.isPresent()) 3359 { 3360 controls.add(new RejectUnindexedSearchRequestControl()); 3361 } 3362 3363 if (permitUnindexedSearch.isPresent()) 3364 { 3365 controls.add(new PermitUnindexedSearchRequestControl()); 3366 } 3367 3368 return controls; 3369 } 3370 3371 3372 3373 /** 3374 * Displays information about the provided result, including special 3375 * processing for a number of supported response controls. 3376 * 3377 * @param result The result to examine. 3378 */ 3379 private void displayResult(@NotNull final LDAPResult result) 3380 { 3381 resultWriter.writeResult(result); 3382 } 3383 3384 3385 3386 /** 3387 * Writes the provided message to the output stream. 3388 * 3389 * @param message The message to be written. 3390 */ 3391 void writeOut(@NotNull final String message) 3392 { 3393 if (outStream == null) 3394 { 3395 out(message); 3396 } 3397 else 3398 { 3399 outStream.println(message); 3400 } 3401 } 3402 3403 3404 3405 /** 3406 * Writes the provided message to the error stream. 3407 * 3408 * @param message The message to be written. 3409 */ 3410 private void writeErr(@NotNull final String message) 3411 { 3412 if (errStream == null) 3413 { 3414 err(message); 3415 } 3416 else 3417 { 3418 errStream.println(message); 3419 } 3420 } 3421 3422 3423 3424 /** 3425 * Writes a line-wrapped, commented version of the provided message to 3426 * standard output. 3427 * 3428 * @param message The message to be written. 3429 */ 3430 private void commentToOut(@NotNull final String message) 3431 { 3432 if (terse.isPresent()) 3433 { 3434 return; 3435 } 3436 3437 for (final String line : StaticUtils.wrapLine(message, (WRAP_COLUMN - 2))) 3438 { 3439 writeOut("# " + line); 3440 } 3441 } 3442 3443 3444 3445 /** 3446 * Writes a line-wrapped, commented version of the provided message to 3447 * standard error. 3448 * 3449 * @param message The message to be written. 3450 */ 3451 private void commentToErr(@NotNull final String message) 3452 { 3453 for (final String line : StaticUtils.wrapLine(message, (WRAP_COLUMN - 2))) 3454 { 3455 writeErr("# " + line); 3456 } 3457 } 3458 3459 3460 3461 /** 3462 * Retrieves the tool's output stream. 3463 * 3464 * @return The tool's output stream. 3465 */ 3466 @NotNull() 3467 PrintStream getOutStream() 3468 { 3469 if (outStream == null) 3470 { 3471 return getOut(); 3472 } 3473 else 3474 { 3475 return outStream; 3476 } 3477 } 3478 3479 3480 3481 /** 3482 * Retrieves the tool's error stream. 3483 * 3484 * @return The tool's error stream. 3485 */ 3486 @NotNull() 3487 PrintStream getErrStream() 3488 { 3489 if (errStream == null) 3490 { 3491 return getErr(); 3492 } 3493 else 3494 { 3495 return errStream; 3496 } 3497 } 3498 3499 3500 3501 /** 3502 * Sets the output handler that should be used by this tool This is primarily 3503 * intended for testing purposes. 3504 * 3505 * @param resultWriter The result writer that should be used by this tool. 3506 */ 3507 void setResultWriter(@NotNull final LDAPResultWriter resultWriter) 3508 { 3509 this.resultWriter = resultWriter; 3510 } 3511 3512 3513 3514 /** 3515 * {@inheritDoc} 3516 */ 3517 @Override() 3518 public void handleUnsolicitedNotification( 3519 @NotNull final LDAPConnection connection, 3520 @NotNull final ExtendedResult notification) 3521 { 3522 resultWriter.writeUnsolicitedNotification(connection, notification); 3523 } 3524 3525 3526 3527 /** 3528 * {@inheritDoc} 3529 */ 3530 @Override() 3531 @NotNull() 3532 public LinkedHashMap<String[],String> getExampleUsages() 3533 { 3534 final LinkedHashMap<String[],String> examples = 3535 new LinkedHashMap<>(StaticUtils.computeMapCapacity(5)); 3536 3537 String[] args = 3538 { 3539 "--hostname", "directory.example.com", 3540 "--port", "389", 3541 "--bindDN", "uid=jdoe,ou=People,dc=example,dc=com", 3542 "--bindPassword", "password", 3543 "--baseDN", "ou=People,dc=example,dc=com", 3544 "--scope", "sub", 3545 "(uid=jqpublic)", 3546 "givenName", 3547 "sn", 3548 "mail" 3549 }; 3550 examples.put(args, INFO_LDAPSEARCH_EXAMPLE_1.get()); 3551 3552 3553 args = new String[] 3554 { 3555 "--hostname", "directory.example.com", 3556 "--port", "636", 3557 "--useSSL", 3558 "--saslOption", "mech=PLAIN", 3559 "--saslOption", "authID=u:jdoe", 3560 "--bindPasswordFile", "/path/to/password/file", 3561 "--baseDN", "ou=People,dc=example,dc=com", 3562 "--scope", "sub", 3563 "--filterFile", "/path/to/filter/file", 3564 "--outputFile", "/path/to/base/output/file", 3565 "--separateOutputFilePerSearch", 3566 "--requestedAttribute", "*", 3567 "--requestedAttribute", "+" 3568 }; 3569 examples.put(args, INFO_LDAPSEARCH_EXAMPLE_2.get()); 3570 3571 3572 args = new String[] 3573 { 3574 "--hostname", "directory.example.com", 3575 "--port", "389", 3576 "--useStartTLS", 3577 "--trustStorePath", "/path/to/truststore/file", 3578 "--baseDN", "", 3579 "--scope", "base", 3580 "--outputFile", "/path/to/output/file", 3581 "--teeResultsToStandardOut", 3582 "(objectClass=*)", 3583 "*", 3584 "+" 3585 }; 3586 examples.put(args, INFO_LDAPSEARCH_EXAMPLE_3.get()); 3587 3588 3589 args = new String[] 3590 { 3591 "--hostname", "directory.example.com", 3592 "--port", "389", 3593 "--bindDN", "uid=admin,dc=example,dc=com", 3594 "--baseDN", "dc=example,dc=com", 3595 "--scope", "sub", 3596 "--outputFile", "/path/to/output/file", 3597 "--simplePageSize", "100", 3598 "(objectClass=*)", 3599 "*", 3600 "+" 3601 }; 3602 examples.put(args, INFO_LDAPSEARCH_EXAMPLE_4.get()); 3603 3604 3605 args = new String[] 3606 { 3607 "--hostname", "directory.example.com", 3608 "--port", "389", 3609 "--bindDN", "uid=admin,dc=example,dc=com", 3610 "--baseDN", "dc=example,dc=com", 3611 "--scope", "sub", 3612 "(&(givenName=John)(sn=Doe))", 3613 "debugsearchindex" 3614 }; 3615 examples.put(args, INFO_LDAPSEARCH_EXAMPLE_5.get()); 3616 3617 return examples; 3618 } 3619}