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