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