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