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.Collections; 034import java.util.EnumSet; 035import java.util.Iterator; 036import java.util.LinkedHashMap; 037import java.util.LinkedHashSet; 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 * 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 Alcatel-Lucent 8661 160 * server products. These classes provide support for proprietary 161 * functionality or for external specifications that are not considered stable 162 * or mature enough to be guaranteed to work in an interoperable way with 163 * 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 String getToolVersion() 372 { 373 return Version.NUMERIC_VERSION_STRING; 374 } 375 376 377 378 /** 379 * {@inheritDoc} 380 */ 381 @Override() 382 public int getMinTrailingArguments() 383 { 384 return 0; 385 } 386 387 388 389 /** 390 * {@inheritDoc} 391 */ 392 @Override() 393 public int getMaxTrailingArguments() 394 { 395 return -1; 396 } 397 398 399 400 /** 401 * {@inheritDoc} 402 */ 403 @Override() 404 public String getTrailingArgumentsPlaceholder() 405 { 406 return INFO_LDAPSEARCH_TRAILING_ARGS_PLACEHOLDER.get(); 407 } 408 409 410 411 /** 412 * {@inheritDoc} 413 */ 414 @Override() 415 public boolean supportsInteractiveMode() 416 { 417 return true; 418 } 419 420 421 422 /** 423 * {@inheritDoc} 424 */ 425 @Override() 426 public boolean defaultsToInteractiveMode() 427 { 428 return true; 429 } 430 431 432 433 /** 434 * {@inheritDoc} 435 */ 436 @Override() 437 public boolean supportsPropertiesFile() 438 { 439 return true; 440 } 441 442 443 444 /** 445 * {@inheritDoc} 446 */ 447 @Override() 448 protected boolean defaultToPromptForBindPassword() 449 { 450 return true; 451 } 452 453 454 455 /** 456 * {@inheritDoc} 457 */ 458 @Override() 459 protected boolean includeAlternateLongIdentifiers() 460 { 461 return true; 462 } 463 464 465 466 /** 467 * {@inheritDoc} 468 */ 469 @Override() 470 protected Set<Character> getSuppressedShortIdentifiers() 471 { 472 return Collections.singleton('T'); 473 } 474 475 476 477 /** 478 * {@inheritDoc} 479 */ 480 @Override() 481 public void addNonLDAPArguments(final ArgumentParser parser) 482 throws ArgumentException 483 { 484 this.parser = parser; 485 486 baseDN = new DNArgument('b', "baseDN", false, 1, null, 487 INFO_LDAPSEARCH_ARG_DESCRIPTION_BASE_DN.get()); 488 baseDN.addLongIdentifier("base-dn", true); 489 baseDN.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 490 parser.addArgument(baseDN); 491 492 scope = new ScopeArgument('s', "scope", false, null, 493 INFO_LDAPSEARCH_ARG_DESCRIPTION_SCOPE.get(), SearchScope.SUB); 494 scope.addLongIdentifier("searchScope", true); 495 scope.addLongIdentifier("search-scope", true); 496 scope.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 497 parser.addArgument(scope); 498 499 sizeLimit = new IntegerArgument('z', "sizeLimit", false, 1, null, 500 INFO_LDAPSEARCH_ARG_DESCRIPTION_SIZE_LIMIT.get(), 0, 501 Integer.MAX_VALUE, 0); 502 sizeLimit.addLongIdentifier("size-limit", true); 503 sizeLimit.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 504 parser.addArgument(sizeLimit); 505 506 timeLimitSeconds = new IntegerArgument('l', "timeLimitSeconds", false, 1, 507 null, INFO_LDAPSEARCH_ARG_DESCRIPTION_TIME_LIMIT.get(), 0, 508 Integer.MAX_VALUE, 0); 509 timeLimitSeconds.addLongIdentifier("timeLimit", true); 510 timeLimitSeconds.addLongIdentifier("time-limit-seconds", true); 511 timeLimitSeconds.addLongIdentifier("time-limit", true); 512 timeLimitSeconds.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 513 parser.addArgument(timeLimitSeconds); 514 515 final LinkedHashSet<String> derefAllowedValues = new LinkedHashSet<>(4); 516 derefAllowedValues.add("never"); 517 derefAllowedValues.add("always"); 518 derefAllowedValues.add("search"); 519 derefAllowedValues.add("find"); 520 dereferencePolicy = new StringArgument('a', "dereferencePolicy", false, 1, 521 "{never|always|search|find}", 522 INFO_LDAPSEARCH_ARG_DESCRIPTION_DEREFERENCE_POLICY.get(), 523 derefAllowedValues, "never"); 524 dereferencePolicy.addLongIdentifier("dereference-policy", true); 525 dereferencePolicy.setArgumentGroupName( 526 INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 527 parser.addArgument(dereferencePolicy); 528 529 typesOnly = new BooleanArgument('A', "typesOnly", 1, 530 INFO_LDAPSEARCH_ARG_DESCRIPTION_TYPES_ONLY.get()); 531 typesOnly.addLongIdentifier("types-only", true); 532 typesOnly.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 533 parser.addArgument(typesOnly); 534 535 requestedAttribute = new StringArgument(null, "requestedAttribute", false, 536 0, INFO_PLACEHOLDER_ATTR.get(), 537 INFO_LDAPSEARCH_ARG_DESCRIPTION_REQUESTED_ATTR.get()); 538 requestedAttribute.addLongIdentifier("requested-attribute", true); 539 requestedAttribute.setArgumentGroupName( 540 INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 541 parser.addArgument(requestedAttribute); 542 543 filter = new FilterArgument(null, "filter", false, 0, 544 INFO_PLACEHOLDER_FILTER.get(), 545 INFO_LDAPSEARCH_ARG_DESCRIPTION_FILTER.get()); 546 filter.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 547 parser.addArgument(filter); 548 549 filterFile = new FileArgument('f', "filterFile", false, 0, null, 550 INFO_LDAPSEARCH_ARG_DESCRIPTION_FILTER_FILE.get(), true, true, 551 true, false); 552 filterFile.addLongIdentifier("filename", true); 553 filterFile.addLongIdentifier("filter-file", true); 554 filterFile.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 555 parser.addArgument(filterFile); 556 557 ldapURLFile = new FileArgument(null, "ldapURLFile", false, 0, null, 558 INFO_LDAPSEARCH_ARG_DESCRIPTION_LDAP_URL_FILE.get(), true, true, 559 true, false); 560 ldapURLFile.addLongIdentifier("ldap-url-file", true); 561 ldapURLFile.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 562 parser.addArgument(ldapURLFile); 563 564 followReferrals = new BooleanArgument(null, "followReferrals", 1, 565 INFO_LDAPSEARCH_ARG_DESCRIPTION_FOLLOW_REFERRALS.get()); 566 followReferrals.addLongIdentifier("follow-referrals", true); 567 followReferrals.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 568 parser.addArgument(followReferrals); 569 570 retryFailedOperations = new BooleanArgument(null, "retryFailedOperations", 571 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_RETRY_FAILED_OPERATIONS.get()); 572 retryFailedOperations.addLongIdentifier("retry-failed-operations", true); 573 retryFailedOperations.setArgumentGroupName( 574 INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 575 parser.addArgument(retryFailedOperations); 576 577 continueOnError = new BooleanArgument('c', "continueOnError", 1, 578 INFO_LDAPSEARCH_ARG_DESCRIPTION_CONTINUE_ON_ERROR.get()); 579 continueOnError.addLongIdentifier("continue-on-error", true); 580 continueOnError.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 581 parser.addArgument(continueOnError); 582 583 ratePerSecond = new IntegerArgument('r', "ratePerSecond", false, 1, 584 INFO_PLACEHOLDER_NUM.get(), 585 INFO_LDAPSEARCH_ARG_DESCRIPTION_RATE_PER_SECOND.get(), 1, 586 Integer.MAX_VALUE); 587 ratePerSecond.addLongIdentifier("rate-per-second", true); 588 ratePerSecond.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 589 parser.addArgument(ratePerSecond); 590 591 useAdministrativeSession = new BooleanArgument(null, 592 "useAdministrativeSession", 1, 593 INFO_LDAPSEARCH_ARG_DESCRIPTION_USE_ADMIN_SESSION.get()); 594 useAdministrativeSession.addLongIdentifier("use-administrative-session", 595 true); 596 useAdministrativeSession.setArgumentGroupName( 597 INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 598 parser.addArgument(useAdministrativeSession); 599 600 dryRun = new BooleanArgument('n', "dryRun", 1, 601 INFO_LDAPSEARCH_ARG_DESCRIPTION_DRY_RUN.get()); 602 dryRun.addLongIdentifier("dry-run", true); 603 dryRun.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 604 parser.addArgument(dryRun); 605 606 wrapColumn = new IntegerArgument(null, "wrapColumn", false, 1, null, 607 INFO_LDAPSEARCH_ARG_DESCRIPTION_WRAP_COLUMN.get(), 0, 608 Integer.MAX_VALUE); 609 wrapColumn.addLongIdentifier("wrap-column", true); 610 wrapColumn.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); 611 parser.addArgument(wrapColumn); 612 613 dontWrap = new BooleanArgument('T', "dontWrap", 1, 614 INFO_LDAPSEARCH_ARG_DESCRIPTION_DONT_WRAP.get()); 615 dontWrap.addLongIdentifier("doNotWrap", true); 616 dontWrap.addLongIdentifier("dont-wrap", true); 617 dontWrap.addLongIdentifier("do-not-wrap", true); 618 dontWrap.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); 619 parser.addArgument(dontWrap); 620 621 suppressBase64EncodedValueComments = new BooleanArgument(null, 622 "suppressBase64EncodedValueComments", 1, 623 INFO_LDAPSEARCH_ARG_DESCRIPTION_SUPPRESS_BASE64_COMMENTS.get()); 624 suppressBase64EncodedValueComments.addLongIdentifier( 625 "suppress-base64-encoded-value-comments", true); 626 suppressBase64EncodedValueComments.setArgumentGroupName( 627 INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); 628 parser.addArgument(suppressBase64EncodedValueComments); 629 630 countEntries = new BooleanArgument(null, "countEntries", 1, 631 INFO_LDAPSEARCH_ARG_DESCRIPTION_COUNT_ENTRIES.get()); 632 countEntries.addLongIdentifier("count-entries", true); 633 countEntries.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); 634 countEntries.setHidden(true); 635 parser.addArgument(countEntries); 636 637 outputFile = new FileArgument(null, "outputFile", false, 1, null, 638 INFO_LDAPSEARCH_ARG_DESCRIPTION_OUTPUT_FILE.get(), false, true, true, 639 false); 640 outputFile.addLongIdentifier("output-file", true); 641 outputFile.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); 642 parser.addArgument(outputFile); 643 644 compressOutput = new BooleanArgument(null, "compressOutput", 1, 645 INFO_LDAPSEARCH_ARG_DESCRIPTION_COMPRESS_OUTPUT.get()); 646 compressOutput.addLongIdentifier("compress-output", true); 647 compressOutput.addLongIdentifier("compress", true); 648 compressOutput.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); 649 parser.addArgument(compressOutput); 650 651 encryptOutput = new BooleanArgument(null, "encryptOutput", 1, 652 INFO_LDAPSEARCH_ARG_DESCRIPTION_ENCRYPT_OUTPUT.get()); 653 encryptOutput.addLongIdentifier("encrypt-output", true); 654 encryptOutput.addLongIdentifier("encrypt", true); 655 encryptOutput.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); 656 parser.addArgument(encryptOutput); 657 658 encryptionPassphraseFile = new FileArgument(null, 659 "encryptionPassphraseFile", false, 1, null, 660 INFO_LDAPSEARCH_ARG_DESCRIPTION_ENCRYPTION_PW_FILE.get(), true, true, 661 true, false); 662 encryptionPassphraseFile.addLongIdentifier("encryption-passphrase-file", 663 true); 664 encryptionPassphraseFile.addLongIdentifier("encryptionPasswordFile", true); 665 encryptionPassphraseFile.addLongIdentifier("encryption-password-file", 666 true); 667 encryptionPassphraseFile.setArgumentGroupName( 668 INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); 669 parser.addArgument(encryptionPassphraseFile); 670 671 separateOutputFilePerSearch = new BooleanArgument(null, 672 "separateOutputFilePerSearch", 1, 673 INFO_LDAPSEARCH_ARG_DESCRIPTION_SEPARATE_OUTPUT_FILES.get()); 674 separateOutputFilePerSearch.addLongIdentifier( 675 "separate-output-file-per-search", true); 676 separateOutputFilePerSearch.setArgumentGroupName( 677 INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); 678 parser.addArgument(separateOutputFilePerSearch); 679 680 teeResultsToStandardOut = new BooleanArgument(null, 681 "teeResultsToStandardOut", 1, 682 INFO_LDAPSEARCH_ARG_DESCRIPTION_TEE.get("outputFile")); 683 teeResultsToStandardOut.addLongIdentifier( 684 "tee-results-to-standard-out", true); 685 teeResultsToStandardOut.setArgumentGroupName( 686 INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); 687 parser.addArgument(teeResultsToStandardOut); 688 689 final LinkedHashSet<String> outputFormatAllowedValues = 690 new LinkedHashSet<>(4); 691 outputFormatAllowedValues.add("ldif"); 692 outputFormatAllowedValues.add("json"); 693 outputFormatAllowedValues.add("csv"); 694 outputFormatAllowedValues.add("tab-delimited"); 695 outputFormat = new StringArgument(null, "outputFormat", false, 1, 696 "{ldif|json|csv|tab-delimited}", 697 INFO_LDAPSEARCH_ARG_DESCRIPTION_OUTPUT_FORMAT.get( 698 requestedAttribute.getIdentifierString(), 699 ldapURLFile.getIdentifierString()), 700 outputFormatAllowedValues, "ldif"); 701 outputFormat.addLongIdentifier("output-format", true); 702 outputFormat.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); 703 parser.addArgument(outputFormat); 704 705 terse = new BooleanArgument(null, "terse", 1, 706 INFO_LDAPSEARCH_ARG_DESCRIPTION_TERSE.get()); 707 terse.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); 708 parser.addArgument(terse); 709 710 verbose = new BooleanArgument('v', "verbose", 1, 711 INFO_LDAPSEARCH_ARG_DESCRIPTION_VERBOSE.get()); 712 verbose.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); 713 parser.addArgument(verbose); 714 715 bindControl = new ControlArgument(null, "bindControl", false, 0, null, 716 INFO_LDAPSEARCH_ARG_DESCRIPTION_BIND_CONTROL.get()); 717 bindControl.addLongIdentifier("bind-control", true); 718 bindControl.setArgumentGroupName( 719 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 720 parser.addArgument(bindControl); 721 722 searchControl = new ControlArgument('J', "control", false, 0, null, 723 INFO_LDAPSEARCH_ARG_DESCRIPTION_SEARCH_CONTROL.get()); 724 searchControl.addLongIdentifier("searchControl", true); 725 searchControl.addLongIdentifier("search-control", true); 726 searchControl.setArgumentGroupName( 727 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 728 parser.addArgument(searchControl); 729 730 authorizationIdentity = new BooleanArgument('E', "authorizationIdentity", 731 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_AUTHZ_IDENTITY.get()); 732 authorizationIdentity.addLongIdentifier("reportAuthzID", true); 733 authorizationIdentity.addLongIdentifier("authorization-identity", true); 734 authorizationIdentity.addLongIdentifier("report-authzid", true); 735 authorizationIdentity.setArgumentGroupName( 736 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 737 parser.addArgument(authorizationIdentity); 738 739 assertionFilter = new FilterArgument(null, "assertionFilter", false, 1, 740 INFO_PLACEHOLDER_FILTER.get(), 741 INFO_LDAPSEARCH_ARG_DESCRIPTION_ASSERTION_FILTER.get()); 742 assertionFilter.addLongIdentifier("assertion-filter", true); 743 assertionFilter.setArgumentGroupName( 744 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 745 parser.addArgument(assertionFilter); 746 747 getAuthorizationEntryAttribute = new StringArgument(null, 748 "getAuthorizationEntryAttribute", false, 0, 749 INFO_PLACEHOLDER_ATTR.get(), 750 INFO_LDAPSEARCH_ARG_DESCRIPTION_GET_AUTHZ_ENTRY_ATTR.get()); 751 getAuthorizationEntryAttribute.addLongIdentifier( 752 "get-authorization-entry-attribute", true); 753 getAuthorizationEntryAttribute.setArgumentGroupName( 754 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 755 parser.addArgument(getAuthorizationEntryAttribute); 756 757 getUserResourceLimits = new BooleanArgument(null, "getUserResourceLimits", 758 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_GET_USER_RESOURCE_LIMITS.get()); 759 getUserResourceLimits.addLongIdentifier("get-user-resource-limits", true); 760 getUserResourceLimits.setArgumentGroupName( 761 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 762 parser.addArgument(getUserResourceLimits); 763 764 accountUsable = new BooleanArgument(null, "accountUsable", 1, 765 INFO_LDAPSEARCH_ARG_DESCRIPTION_ACCOUNT_USABLE.get()); 766 accountUsable.addLongIdentifier("account-usable", true); 767 accountUsable.setArgumentGroupName( 768 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 769 parser.addArgument(accountUsable); 770 771 excludeBranch = new DNArgument(null, "excludeBranch", false, 0, null, 772 INFO_LDAPSEARCH_ARG_DESCRIPTION_EXCLUDE_BRANCH.get()); 773 excludeBranch.addLongIdentifier("exclude-branch", true); 774 excludeBranch.setArgumentGroupName( 775 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 776 parser.addArgument(excludeBranch); 777 778 getEffectiveRightsAuthzID = new StringArgument('g', 779 "getEffectiveRightsAuthzID", false, 1, 780 INFO_PLACEHOLDER_AUTHZID.get(), 781 INFO_LDAPSEARCH_ARG_DESCRIPTION_GET_EFFECTIVE_RIGHTS_AUTHZID.get( 782 "getEffectiveRightsAttribute")); 783 getEffectiveRightsAuthzID.addLongIdentifier( 784 "get-effective-rights-authzid", true); 785 getEffectiveRightsAuthzID.setArgumentGroupName( 786 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 787 parser.addArgument(getEffectiveRightsAuthzID); 788 789 getEffectiveRightsAttribute = new StringArgument('e', 790 "getEffectiveRightsAttribute", false, 0, 791 INFO_PLACEHOLDER_ATTR.get(), 792 INFO_LDAPSEARCH_ARG_DESCRIPTION_GET_EFFECTIVE_RIGHTS_ATTR.get()); 793 getEffectiveRightsAttribute.addLongIdentifier( 794 "get-effective-rights-attribute", true); 795 getEffectiveRightsAttribute.setArgumentGroupName( 796 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 797 parser.addArgument(getEffectiveRightsAttribute); 798 799 includeReplicationConflictEntries = new BooleanArgument(null, 800 "includeReplicationConflictEntries", 1, 801 INFO_LDAPSEARCH_ARG_DESCRIPTION_INCLUDE_REPL_CONFLICTS.get()); 802 includeReplicationConflictEntries.addLongIdentifier( 803 "include-replication-conflict-entries", true); 804 includeReplicationConflictEntries.setArgumentGroupName( 805 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 806 parser.addArgument(includeReplicationConflictEntries); 807 808 final LinkedHashSet<String> softDeleteAllowedValues = 809 new LinkedHashSet<>(3); 810 softDeleteAllowedValues.add("with-non-deleted-entries"); 811 softDeleteAllowedValues.add("without-non-deleted-entries"); 812 softDeleteAllowedValues.add("deleted-entries-in-undeleted-form"); 813 includeSoftDeletedEntries = new StringArgument(null, 814 "includeSoftDeletedEntries", false, 1, 815 "{with-non-deleted-entries|without-non-deleted-entries|" + 816 "deleted-entries-in-undeleted-form}", 817 INFO_LDAPSEARCH_ARG_DESCRIPTION_INCLUDE_SOFT_DELETED.get(), 818 softDeleteAllowedValues); 819 includeSoftDeletedEntries.addLongIdentifier( 820 "include-soft-deleted-entries", true); 821 includeSoftDeletedEntries.setArgumentGroupName( 822 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 823 parser.addArgument(includeSoftDeletedEntries); 824 825 includeSubentries = new BooleanArgument(null, "includeSubentries", 1, 826 INFO_LDAPSEARCH_ARG_DESCRIPTION_INCLUDE_SUBENTRIES.get()); 827 includeSubentries.addLongIdentifier("includeLDAPSubentries", true); 828 includeSubentries.addLongIdentifier("include-subentries", true); 829 includeSubentries.addLongIdentifier("include-ldap-subentries", true); 830 includeSubentries.setArgumentGroupName( 831 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 832 parser.addArgument(includeSubentries); 833 834 joinRule = new StringArgument(null, "joinRule", false, 1, 835 "{dn:sourceAttr|reverse-dn:targetAttr|equals:sourceAttr:targetAttr|" + 836 "contains:sourceAttr:targetAttr }", 837 INFO_LDAPSEARCH_ARG_DESCRIPTION_JOIN_RULE.get()); 838 joinRule.addLongIdentifier("join-rule", true); 839 joinRule.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 840 parser.addArgument(joinRule); 841 842 joinBaseDN = new StringArgument(null, "joinBaseDN", false, 1, 843 "{search-base|source-entry-dn|{dn}}", 844 INFO_LDAPSEARCH_ARG_DESCRIPTION_JOIN_BASE_DN.get()); 845 joinBaseDN.addLongIdentifier("join-base-dn", true); 846 joinBaseDN.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 847 parser.addArgument(joinBaseDN); 848 849 joinScope = new ScopeArgument(null, "joinScope", false, null, 850 INFO_LDAPSEARCH_ARG_DESCRIPTION_JOIN_SCOPE.get()); 851 joinScope.addLongIdentifier("join-scope", true); 852 joinScope.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 853 parser.addArgument(joinScope); 854 855 joinSizeLimit = new IntegerArgument(null, "joinSizeLimit", false, 1, 856 INFO_PLACEHOLDER_NUM.get(), 857 INFO_LDAPSEARCH_ARG_DESCRIPTION_JOIN_SIZE_LIMIT.get(), 0, 858 Integer.MAX_VALUE); 859 joinSizeLimit.addLongIdentifier("join-size-limit", true); 860 joinSizeLimit.setArgumentGroupName( 861 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 862 parser.addArgument(joinSizeLimit); 863 864 joinFilter = new FilterArgument(null, "joinFilter", false, 1, null, 865 INFO_LDAPSEARCH_ARG_DESCRIPTION_JOIN_FILTER.get()); 866 joinFilter.addLongIdentifier("join-filter", true); 867 joinFilter.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 868 parser.addArgument(joinFilter); 869 870 joinRequestedAttribute = new StringArgument(null, "joinRequestedAttribute", 871 false, 0, INFO_PLACEHOLDER_ATTR.get(), 872 INFO_LDAPSEARCH_ARG_DESCRIPTION_JOIN_ATTR.get()); 873 joinRequestedAttribute.addLongIdentifier("join-requested-attribute", true); 874 joinRequestedAttribute.setArgumentGroupName( 875 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 876 parser.addArgument(joinRequestedAttribute); 877 878 joinRequireMatch = new BooleanArgument(null, "joinRequireMatch", 1, 879 INFO_LDAPSEARCH_ARG_DESCRIPTION_JOIN_REQUIRE_MATCH.get()); 880 joinRequireMatch.addLongIdentifier("join-require-match", true); 881 joinRequireMatch.setArgumentGroupName( 882 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 883 parser.addArgument(joinRequireMatch); 884 885 manageDsaIT = new BooleanArgument(null, "manageDsaIT", 1, 886 INFO_LDAPSEARCH_ARG_DESCRIPTION_MANAGE_DSA_IT.get()); 887 manageDsaIT.addLongIdentifier("manage-dsa-it", true); 888 manageDsaIT.setArgumentGroupName( 889 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 890 parser.addArgument(manageDsaIT); 891 892 matchedValuesFilter = new FilterArgument(null, "matchedValuesFilter", 893 false, 0, INFO_PLACEHOLDER_FILTER.get(), 894 INFO_LDAPSEARCH_ARG_DESCRIPTION_MATCHED_VALUES_FILTER.get()); 895 matchedValuesFilter.addLongIdentifier("matched-values-filter", true); 896 matchedValuesFilter.setArgumentGroupName( 897 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 898 parser.addArgument(matchedValuesFilter); 899 900 matchingEntryCountControl = new StringArgument(null, 901 "matchingEntryCountControl", false, 1, 902 "{examineCount=NNN[:alwaysExamine][:allowUnindexed]" + 903 "[:skipResolvingExplodedIndexes]" + 904 "[:fastShortCircuitThreshold=NNN]" + 905 "[:slowShortCircuitThreshold=NNN][:debug]}", 906 INFO_LDAPSEARCH_ARG_DESCRIPTION_MATCHING_ENTRY_COUNT_CONTROL.get()); 907 matchingEntryCountControl.addLongIdentifier("matchingEntryCount", true); 908 matchingEntryCountControl.addLongIdentifier( 909 "matching-entry-count-control", true); 910 matchingEntryCountControl.addLongIdentifier("matching-entry-count", true); 911 matchingEntryCountControl.setArgumentGroupName( 912 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 913 parser.addArgument(matchingEntryCountControl); 914 915 operationPurpose = new StringArgument(null, "operationPurpose", false, 1, 916 INFO_PLACEHOLDER_PURPOSE.get(), 917 INFO_LDAPSEARCH_ARG_DESCRIPTION_OPERATION_PURPOSE.get()); 918 operationPurpose.addLongIdentifier("operation-purpose", true); 919 operationPurpose.setArgumentGroupName( 920 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 921 parser.addArgument(operationPurpose); 922 923 overrideSearchLimit = new StringArgument(null, "overrideSearchLimit", 924 false, 0, INFO_LDAPSEARCH_NAME_VALUE_PLACEHOLDER.get(), 925 INFO_LDAPSEARCH_ARG_DESCRIPTION_OVERRIDE_SEARCH_LIMIT.get()); 926 overrideSearchLimit.addLongIdentifier("overrideSearchLimits", true); 927 overrideSearchLimit.addLongIdentifier("override-search-limit", true); 928 overrideSearchLimit.addLongIdentifier("override-search-limits", true); 929 overrideSearchLimit.setArgumentGroupName( 930 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 931 parser.addArgument(overrideSearchLimit); 932 933 persistentSearch = new StringArgument('C', "persistentSearch", false, 1, 934 "ps[:changetype[:changesonly[:entrychgcontrols]]]", 935 INFO_LDAPSEARCH_ARG_DESCRIPTION_PERSISTENT_SEARCH.get()); 936 persistentSearch.addLongIdentifier("persistent-search", true); 937 persistentSearch.setArgumentGroupName( 938 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 939 parser.addArgument(persistentSearch); 940 941 proxyAs = new StringArgument('Y', "proxyAs", false, 1, 942 INFO_PLACEHOLDER_AUTHZID.get(), 943 INFO_LDAPSEARCH_ARG_DESCRIPTION_PROXY_AS.get()); 944 proxyAs.addLongIdentifier("proxy-as", true); 945 proxyAs.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 946 parser.addArgument(proxyAs); 947 948 proxyV1As = new DNArgument(null, "proxyV1As", false, 1, null, 949 INFO_LDAPSEARCH_ARG_DESCRIPTION_PROXY_V1_AS.get()); 950 proxyV1As.addLongIdentifier("proxy-v1-as", true); 951 proxyV1As.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 952 parser.addArgument(proxyV1As); 953 954 final LinkedHashSet<String> 955 suppressOperationalAttributeUpdatesAllowedValues = 956 new LinkedHashSet<>(4); 957 suppressOperationalAttributeUpdatesAllowedValues.add("last-access-time"); 958 suppressOperationalAttributeUpdatesAllowedValues.add("last-login-time"); 959 suppressOperationalAttributeUpdatesAllowedValues.add("last-login-ip"); 960 suppressOperationalAttributeUpdatesAllowedValues.add("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 = new LinkedHashMap<>(10); 1559 for (final String value : overrideSearchLimit.getValues()) 1560 { 1561 final int equalPos = value.indexOf('='); 1562 if (equalPos < 0) 1563 { 1564 throw new ArgumentException( 1565 ERR_LDAPSEARCH_OVERRIDE_LIMIT_NO_EQUAL.get( 1566 overrideSearchLimit.getIdentifierString())); 1567 } 1568 else if (equalPos == 0) 1569 { 1570 throw new ArgumentException( 1571 ERR_LDAPSEARCH_OVERRIDE_LIMIT_EMPTY_PROPERTY_NAME.get( 1572 overrideSearchLimit.getIdentifierString())); 1573 } 1574 1575 final String propertyName = value.substring(0, equalPos); 1576 if (properties.containsKey(propertyName)) 1577 { 1578 throw new ArgumentException( 1579 ERR_LDAPSEARCH_OVERRIDE_LIMIT_DUPLICATE_PROPERTY_NAME.get( 1580 overrideSearchLimit.getIdentifierString(), propertyName)); 1581 } 1582 1583 if (equalPos == (value.length() - 1)) 1584 { 1585 throw new ArgumentException( 1586 ERR_LDAPSEARCH_OVERRIDE_LIMIT_EMPTY_PROPERTY_VALUE.get( 1587 overrideSearchLimit.getIdentifierString(), propertyName)); 1588 } 1589 1590 properties.put(propertyName, value.substring(equalPos+1)); 1591 } 1592 1593 overrideSearchLimitsRequestControl = 1594 new OverrideSearchLimitsRequestControl(properties, false); 1595 } 1596 1597 1598 // If we should use the persistent search request control, then validate 1599 // the argument value and pre-create the control. 1600 if (persistentSearch.isPresent()) 1601 { 1602 boolean changesOnly = true; 1603 boolean returnECs = true; 1604 EnumSet<PersistentSearchChangeType> changeTypes = 1605 EnumSet.allOf(PersistentSearchChangeType.class); 1606 try 1607 { 1608 final String[] elements = 1609 persistentSearch.getValue().toLowerCase().split(":"); 1610 if (elements.length == 0) 1611 { 1612 throw new ArgumentException( 1613 ERR_LDAPSEARCH_PERSISTENT_SEARCH_INVALID_VALUE.get( 1614 persistentSearch.getIdentifierString())); 1615 } 1616 1617 final String header = StaticUtils.toLowerCase(elements[0]); 1618 if (! (header.equals("ps") || header.equals("persist") || 1619 header.equals("persistent") || header.equals("psearch") || 1620 header.equals("persistentsearch"))) 1621 { 1622 throw new ArgumentException( 1623 ERR_LDAPSEARCH_PERSISTENT_SEARCH_INVALID_VALUE.get( 1624 persistentSearch.getIdentifierString())); 1625 } 1626 1627 if (elements.length > 1) 1628 { 1629 final String ctString = StaticUtils.toLowerCase(elements[1]); 1630 if (ctString.equals("any")) 1631 { 1632 changeTypes = EnumSet.allOf(PersistentSearchChangeType.class); 1633 } 1634 else 1635 { 1636 changeTypes.clear(); 1637 for (final String t : ctString.split(",")) 1638 { 1639 if (t.equals("add")) 1640 { 1641 changeTypes.add(PersistentSearchChangeType.ADD); 1642 } 1643 else if (t.equals("del") || t.equals("delete")) 1644 { 1645 changeTypes.add(PersistentSearchChangeType.DELETE); 1646 } 1647 else if (t.equals("mod") || t.equals("modify")) 1648 { 1649 changeTypes.add(PersistentSearchChangeType.MODIFY); 1650 } 1651 else if (t.equals("moddn") || t.equals("modrdn") || 1652 t.equals("modifydn") || t.equals("modifyrdn")) 1653 { 1654 changeTypes.add(PersistentSearchChangeType.MODIFY_DN); 1655 } 1656 else 1657 { 1658 throw new ArgumentException( 1659 ERR_LDAPSEARCH_PERSISTENT_SEARCH_INVALID_VALUE.get( 1660 persistentSearch.getIdentifierString())); 1661 } 1662 } 1663 } 1664 } 1665 1666 if (elements.length > 2) 1667 { 1668 if (elements[2].equalsIgnoreCase("true") || elements[2].equals("1")) 1669 { 1670 changesOnly = true; 1671 } 1672 else if (elements[2].equalsIgnoreCase("false") || 1673 elements[2].equals("0")) 1674 { 1675 changesOnly = false; 1676 } 1677 else 1678 { 1679 throw new ArgumentException( 1680 ERR_LDAPSEARCH_PERSISTENT_SEARCH_INVALID_VALUE.get( 1681 persistentSearch.getIdentifierString())); 1682 } 1683 } 1684 1685 if (elements.length > 3) 1686 { 1687 if (elements[3].equalsIgnoreCase("true") || elements[3].equals("1")) 1688 { 1689 returnECs = true; 1690 } 1691 else if (elements[3].equalsIgnoreCase("false") || 1692 elements[3].equals("0")) 1693 { 1694 returnECs = false; 1695 } 1696 else 1697 { 1698 throw new ArgumentException( 1699 ERR_LDAPSEARCH_PERSISTENT_SEARCH_INVALID_VALUE.get( 1700 persistentSearch.getIdentifierString())); 1701 } 1702 } 1703 } 1704 catch (final ArgumentException ae) 1705 { 1706 Debug.debugException(ae); 1707 throw ae; 1708 } 1709 catch (final Exception e) 1710 { 1711 Debug.debugException(e); 1712 throw new ArgumentException( 1713 ERR_LDAPSEARCH_PERSISTENT_SEARCH_INVALID_VALUE.get( 1714 persistentSearch.getIdentifierString()), 1715 e); 1716 } 1717 1718 persistentSearchRequestControl = new PersistentSearchRequestControl( 1719 changeTypes, changesOnly, returnECs, true); 1720 } 1721 1722 1723 // If we should use the server-side sort request control, then validate the 1724 // sort order and pre-create the control. 1725 if (sortOrder.isPresent()) 1726 { 1727 final ArrayList<SortKey> sortKeyList = new ArrayList<>(5); 1728 final StringTokenizer tokenizer = 1729 new StringTokenizer(sortOrder.getValue(), ", "); 1730 while (tokenizer.hasMoreTokens()) 1731 { 1732 final String token = tokenizer.nextToken(); 1733 1734 final boolean ascending; 1735 String attributeName; 1736 if (token.startsWith("-")) 1737 { 1738 ascending = false; 1739 attributeName = token.substring(1); 1740 } 1741 else if (token.startsWith("+")) 1742 { 1743 ascending = true; 1744 attributeName = token.substring(1); 1745 } 1746 else 1747 { 1748 ascending = true; 1749 attributeName = token; 1750 } 1751 1752 final String matchingRuleID; 1753 final int colonPos = attributeName.indexOf(':'); 1754 if (colonPos >= 0) 1755 { 1756 matchingRuleID = attributeName.substring(colonPos+1); 1757 attributeName = attributeName.substring(0, colonPos); 1758 } 1759 else 1760 { 1761 matchingRuleID = null; 1762 } 1763 1764 final StringBuilder invalidReason = new StringBuilder(); 1765 if (! PersistUtils.isValidLDAPName(attributeName, false, invalidReason)) 1766 { 1767 throw new ArgumentException( 1768 ERR_LDAPSEARCH_SORT_ORDER_INVALID_VALUE.get( 1769 sortOrder.getIdentifierString())); 1770 } 1771 1772 sortKeyList.add( 1773 new SortKey(attributeName, matchingRuleID, (! ascending))); 1774 } 1775 1776 if (sortKeyList.isEmpty()) 1777 { 1778 throw new ArgumentException( 1779 ERR_LDAPSEARCH_SORT_ORDER_INVALID_VALUE.get( 1780 sortOrder.getIdentifierString())); 1781 } 1782 1783 final SortKey[] sortKeyArray = new SortKey[sortKeyList.size()]; 1784 sortKeyList.toArray(sortKeyArray); 1785 1786 sortRequestControl = new ServerSideSortRequestControl(sortKeyArray); 1787 } 1788 1789 1790 // If we should use the virtual list view request control, then validate the 1791 // argument value and pre-create the control. 1792 if (virtualListView.isPresent()) 1793 { 1794 try 1795 { 1796 final String[] elements = virtualListView.getValue().split(":"); 1797 if (elements.length == 4) 1798 { 1799 vlvRequestControl = new VirtualListViewRequestControl( 1800 Integer.parseInt(elements[2]), Integer.parseInt(elements[0]), 1801 Integer.parseInt(elements[1]), Integer.parseInt(elements[3]), 1802 null); 1803 } 1804 else if (elements.length == 3) 1805 { 1806 vlvRequestControl = new VirtualListViewRequestControl(elements[2], 1807 Integer.parseInt(elements[0]), Integer.parseInt(elements[1]), 1808 null); 1809 } 1810 else 1811 { 1812 throw new ArgumentException( 1813 ERR_LDAPSEARCH_VLV_INVALID_VALUE.get( 1814 virtualListView.getIdentifierString())); 1815 } 1816 } 1817 catch (final ArgumentException ae) 1818 { 1819 Debug.debugException(ae); 1820 throw ae; 1821 } 1822 catch (final Exception e) 1823 { 1824 Debug.debugException(e); 1825 throw new ArgumentException( 1826 ERR_LDAPSEARCH_VLV_INVALID_VALUE.get( 1827 virtualListView.getIdentifierString()), 1828 e); 1829 } 1830 } 1831 1832 1833 if (joinRule.isPresent()) 1834 { 1835 final JoinRule rule; 1836 try 1837 { 1838 final String[] elements = joinRule.getValue().toLowerCase().split(":"); 1839 final String ruleName = StaticUtils.toLowerCase(elements[0]); 1840 if (ruleName.equals("dn")) 1841 { 1842 rule = JoinRule.createDNJoin(elements[1]); 1843 } 1844 else if (ruleName.equals("reverse-dn") || ruleName.equals("reversedn")) 1845 { 1846 rule = JoinRule.createReverseDNJoin(elements[1]); 1847 } 1848 else if (ruleName.equals("equals") || ruleName.equals("equality")) 1849 { 1850 rule = JoinRule.createEqualityJoin(elements[1], elements[2], false); 1851 } 1852 else if (ruleName.equals("contains") || ruleName.equals("substring")) 1853 { 1854 rule = JoinRule.createContainsJoin(elements[1], elements[2], false); 1855 } 1856 else 1857 { 1858 throw new ArgumentException( 1859 ERR_LDAPSEARCH_JOIN_RULE_INVALID_VALUE.get( 1860 joinRule.getIdentifierString())); 1861 } 1862 } 1863 catch (final ArgumentException ae) 1864 { 1865 Debug.debugException(ae); 1866 throw ae; 1867 } 1868 catch (final Exception e) 1869 { 1870 Debug.debugException(e); 1871 throw new ArgumentException( 1872 ERR_LDAPSEARCH_JOIN_RULE_INVALID_VALUE.get( 1873 joinRule.getIdentifierString()), 1874 e); 1875 } 1876 1877 final JoinBaseDN joinBase; 1878 if (joinBaseDN.isPresent()) 1879 { 1880 final String s = StaticUtils.toLowerCase(joinBaseDN.getValue()); 1881 if (s.equals("search-base") || s.equals("search-base-dn")) 1882 { 1883 joinBase = JoinBaseDN.createUseSearchBaseDN(); 1884 } 1885 else if (s.equals("source-entry-dn") || s.equals("source-dn")) 1886 { 1887 joinBase = JoinBaseDN.createUseSourceEntryDN(); 1888 } 1889 else 1890 { 1891 try 1892 { 1893 final DN dn = new DN(joinBaseDN.getValue()); 1894 joinBase = JoinBaseDN.createUseCustomBaseDN(joinBaseDN.getValue()); 1895 } 1896 catch (final Exception e) 1897 { 1898 Debug.debugException(e); 1899 throw new ArgumentException( 1900 ERR_LDAPSEARCH_JOIN_BASE_DN_INVALID_VALUE.get( 1901 joinBaseDN.getIdentifierString()), 1902 e); 1903 } 1904 } 1905 } 1906 else 1907 { 1908 joinBase = JoinBaseDN.createUseSearchBaseDN(); 1909 } 1910 1911 final String[] joinAttrs; 1912 if (joinRequestedAttribute.isPresent()) 1913 { 1914 final List<String> valueList = joinRequestedAttribute.getValues(); 1915 joinAttrs = new String[valueList.size()]; 1916 valueList.toArray(joinAttrs); 1917 } 1918 else 1919 { 1920 joinAttrs = null; 1921 } 1922 1923 joinRequestControl = new JoinRequestControl(new JoinRequestValue(rule, 1924 joinBase, joinScope.getValue(), DereferencePolicy.NEVER, 1925 joinSizeLimit.getValue(), joinFilter.getValue(), joinAttrs, 1926 joinRequireMatch.isPresent(), null)); 1927 } 1928 1929 1930 // Parse the dereference policy. 1931 final String derefStr = 1932 StaticUtils.toLowerCase(dereferencePolicy.getValue()); 1933 if (derefStr.equals("always")) 1934 { 1935 derefPolicy = DereferencePolicy.ALWAYS; 1936 } 1937 else if (derefStr.equals("search")) 1938 { 1939 derefPolicy = DereferencePolicy.SEARCHING; 1940 } 1941 else if (derefStr.equals("find")) 1942 { 1943 derefPolicy = DereferencePolicy.FINDING; 1944 } 1945 else 1946 { 1947 derefPolicy = DereferencePolicy.NEVER; 1948 } 1949 1950 1951 // See if any entry transformations need to be applied. 1952 final ArrayList<EntryTransformation> transformations = new ArrayList<>(5); 1953 if (excludeAttribute.isPresent()) 1954 { 1955 transformations.add(new ExcludeAttributeTransformation(null, 1956 excludeAttribute.getValues())); 1957 } 1958 1959 if (redactAttribute.isPresent()) 1960 { 1961 transformations.add(new RedactAttributeTransformation(null, true, 1962 (! hideRedactedValueCount.isPresent()), 1963 redactAttribute.getValues())); 1964 } 1965 1966 if (scrambleAttribute.isPresent()) 1967 { 1968 final Long randomSeed; 1969 if (scrambleRandomSeed.isPresent()) 1970 { 1971 randomSeed = scrambleRandomSeed.getValue().longValue(); 1972 } 1973 else 1974 { 1975 randomSeed = null; 1976 } 1977 1978 transformations.add(new ScrambleAttributeTransformation(null, randomSeed, 1979 true, scrambleAttribute.getValues(), scrambleJSONField.getValues())); 1980 } 1981 1982 if (renameAttributeFrom.isPresent()) 1983 { 1984 if (renameAttributeFrom.getNumOccurrences() != 1985 renameAttributeTo.getNumOccurrences()) 1986 { 1987 throw new ArgumentException( 1988 ERR_LDAPSEARCH_RENAME_ATTRIBUTE_MISMATCH.get()); 1989 } 1990 1991 final Iterator<String> sourceIterator = 1992 renameAttributeFrom.getValues().iterator(); 1993 final Iterator<String> targetIterator = 1994 renameAttributeTo.getValues().iterator(); 1995 while (sourceIterator.hasNext()) 1996 { 1997 transformations.add(new RenameAttributeTransformation(null, 1998 sourceIterator.next(), targetIterator.next(), true)); 1999 } 2000 } 2001 2002 if (moveSubtreeFrom.isPresent()) 2003 { 2004 if (moveSubtreeFrom.getNumOccurrences() != 2005 moveSubtreeTo.getNumOccurrences()) 2006 { 2007 throw new ArgumentException(ERR_LDAPSEARCH_MOVE_SUBTREE_MISMATCH.get()); 2008 } 2009 2010 final Iterator<DN> sourceIterator = 2011 moveSubtreeFrom.getValues().iterator(); 2012 final Iterator<DN> targetIterator = moveSubtreeTo.getValues().iterator(); 2013 while (sourceIterator.hasNext()) 2014 { 2015 transformations.add(new MoveSubtreeTransformation(sourceIterator.next(), 2016 targetIterator.next())); 2017 } 2018 } 2019 2020 if (! transformations.isEmpty()) 2021 { 2022 entryTransformations = transformations; 2023 } 2024 2025 2026 // Create the output handler. 2027 final String outputFormatStr = 2028 StaticUtils.toLowerCase(outputFormat.getValue()); 2029 if (outputFormatStr.equals("json")) 2030 { 2031 outputHandler = new JSONLDAPSearchOutputHandler(this); 2032 } 2033 else if (outputFormatStr.equals("csv") || 2034 outputFormatStr.equals("tab-delimited")) 2035 { 2036 // These output formats cannot be used with the --ldapURLFile argument. 2037 if (ldapURLFile.isPresent()) 2038 { 2039 throw new ArgumentException( 2040 ERR_LDAPSEARCH_OUTPUT_FORMAT_NOT_SUPPORTED_WITH_URLS.get( 2041 outputFormat.getValue(), ldapURLFile.getIdentifierString())); 2042 } 2043 2044 // These output formats require the requested attributes to be specified 2045 // via the --requestedAttribute argument rather than as unnamed trailing 2046 // arguments. 2047 final List<String> requestedAttributes = requestedAttribute.getValues(); 2048 if ((requestedAttributes == null) || requestedAttributes.isEmpty()) 2049 { 2050 throw new ArgumentException( 2051 ERR_LDAPSEARCH_OUTPUT_FORMAT_REQUIRES_REQUESTED_ATTR_ARG.get( 2052 outputFormat.getValue(), 2053 requestedAttribute.getIdentifierString())); 2054 } 2055 2056 switch (trailingArgs.size()) 2057 { 2058 case 0: 2059 // This is fine. 2060 break; 2061 2062 case 1: 2063 // Make sure that the trailing argument is a filter rather than a 2064 // requested attribute. It's sufficient to ensure that neither the 2065 // filter nor filterFile argument was provided. 2066 if (filter.isPresent() || filterFile.isPresent()) 2067 { 2068 throw new ArgumentException( 2069 ERR_LDAPSEARCH_OUTPUT_FORMAT_REQUIRES_REQUESTED_ATTR_ARG.get( 2070 outputFormat.getValue(), 2071 requestedAttribute.getIdentifierString())); 2072 } 2073 break; 2074 2075 default: 2076 throw new ArgumentException( 2077 ERR_LDAPSEARCH_OUTPUT_FORMAT_REQUIRES_REQUESTED_ATTR_ARG.get( 2078 outputFormat.getValue(), 2079 requestedAttribute.getIdentifierString())); 2080 } 2081 2082 outputHandler = new ColumnFormatterLDAPSearchOutputHandler(this, 2083 (outputFormatStr.equals("csv") 2084 ? OutputFormat.CSV 2085 : OutputFormat.TAB_DELIMITED_TEXT), 2086 requestedAttributes, WRAP_COLUMN); 2087 } 2088 else 2089 { 2090 outputHandler = new LDIFLDAPSearchOutputHandler(this, WRAP_COLUMN); 2091 } 2092 } 2093 2094 2095 2096 /** 2097 * {@inheritDoc} 2098 */ 2099 @Override() 2100 public LDAPConnectionOptions getConnectionOptions() 2101 { 2102 final LDAPConnectionOptions options = new LDAPConnectionOptions(); 2103 2104 options.setUseSynchronousMode(true); 2105 options.setFollowReferrals(followReferrals.isPresent()); 2106 options.setUnsolicitedNotificationHandler(this); 2107 2108 return options; 2109 } 2110 2111 2112 2113 /** 2114 * {@inheritDoc} 2115 */ 2116 @Override() 2117 public ResultCode doToolProcessing() 2118 { 2119 // If we should encrypt the output, then get the encryption passphrase. 2120 if (encryptOutput.isPresent()) 2121 { 2122 if (encryptionPassphraseFile.isPresent()) 2123 { 2124 try 2125 { 2126 encryptionPassphrase = ToolUtils.readEncryptionPassphraseFromFile( 2127 encryptionPassphraseFile.getValue()); 2128 } 2129 catch (final LDAPException e) 2130 { 2131 Debug.debugException(e); 2132 wrapErr(0, WRAP_COLUMN, e.getMessage()); 2133 return e.getResultCode(); 2134 } 2135 } 2136 else 2137 { 2138 try 2139 { 2140 encryptionPassphrase = ToolUtils.promptForEncryptionPassphrase(false, 2141 true, getOut(), getErr()); 2142 } 2143 catch (final LDAPException e) 2144 { 2145 Debug.debugException(e); 2146 wrapErr(0, WRAP_COLUMN, e.getMessage()); 2147 return e.getResultCode(); 2148 } 2149 } 2150 } 2151 2152 2153 // If we should use an output file, then set that up now. Otherwise, write 2154 // the header to standard output. 2155 if (outputFile.isPresent()) 2156 { 2157 if (! separateOutputFilePerSearch.isPresent()) 2158 { 2159 try 2160 { 2161 OutputStream s = new FileOutputStream(outputFile.getValue()); 2162 2163 if (encryptOutput.isPresent()) 2164 { 2165 s = new PassphraseEncryptedOutputStream(encryptionPassphrase, s); 2166 } 2167 2168 if (compressOutput.isPresent()) 2169 { 2170 s = new GZIPOutputStream(s); 2171 } 2172 2173 if (teeResultsToStandardOut.isPresent()) 2174 { 2175 outStream = new PrintStream(new TeeOutputStream(s, getOut())); 2176 } 2177 else 2178 { 2179 outStream = new PrintStream(s); 2180 } 2181 errStream = outStream; 2182 } 2183 catch (final Exception e) 2184 { 2185 Debug.debugException(e); 2186 wrapErr(0, WRAP_COLUMN, ERR_LDAPSEARCH_CANNOT_OPEN_OUTPUT_FILE.get( 2187 outputFile.getValue().getAbsolutePath(), 2188 StaticUtils.getExceptionMessage(e))); 2189 return ResultCode.LOCAL_ERROR; 2190 } 2191 2192 outputHandler.formatHeader(); 2193 } 2194 } 2195 else 2196 { 2197 outputHandler.formatHeader(); 2198 } 2199 2200 2201 // Examine the arguments to determine the sets of controls to use for each 2202 // type of request. 2203 final List<Control> searchControls = getSearchControls(); 2204 2205 2206 // If appropriate, ensure that any search result entries that include 2207 // base64-encoded attribute values will also include comments that attempt 2208 // to provide a human-readable representation of that value. 2209 final boolean originalCommentAboutBase64EncodedValues = 2210 LDIFWriter.commentAboutBase64EncodedValues(); 2211 LDIFWriter.setCommentAboutBase64EncodedValues( 2212 ! suppressBase64EncodedValueComments.isPresent()); 2213 2214 2215 LDAPConnectionPool pool = null; 2216 try 2217 { 2218 // Create a connection pool that will be used to communicate with the 2219 // directory server. 2220 if (! dryRun.isPresent()) 2221 { 2222 try 2223 { 2224 final StartAdministrativeSessionPostConnectProcessor p; 2225 if (useAdministrativeSession.isPresent()) 2226 { 2227 p = new StartAdministrativeSessionPostConnectProcessor( 2228 new StartAdministrativeSessionExtendedRequest(getToolName(), 2229 true)); 2230 } 2231 else 2232 { 2233 p = null; 2234 } 2235 2236 pool = getConnectionPool(1, 1, 0, p, null, true, 2237 new ReportBindResultLDAPConnectionPoolHealthCheck(this, true, 2238 false)); 2239 } 2240 catch (final LDAPException le) 2241 { 2242 // This shouldn't happen since the pool won't throw an exception if an 2243 // attempt to create an initial connection fails. 2244 Debug.debugException(le); 2245 commentToErr(ERR_LDAPSEARCH_CANNOT_CREATE_CONNECTION_POOL.get( 2246 StaticUtils.getExceptionMessage(le))); 2247 return le.getResultCode(); 2248 } 2249 2250 if (retryFailedOperations.isPresent()) 2251 { 2252 pool.setRetryFailedOperationsDueToInvalidConnections(true); 2253 } 2254 } 2255 2256 2257 // If appropriate, create a rate limiter. 2258 final FixedRateBarrier rateLimiter; 2259 if (ratePerSecond.isPresent()) 2260 { 2261 rateLimiter = new FixedRateBarrier(1000L, ratePerSecond.getValue()); 2262 } 2263 else 2264 { 2265 rateLimiter = null; 2266 } 2267 2268 2269 // If one or more LDAP URL files are provided, then construct search 2270 // requests from those URLs. 2271 if (ldapURLFile.isPresent()) 2272 { 2273 return searchWithLDAPURLs(pool, rateLimiter, searchControls); 2274 } 2275 2276 2277 // Get the set of requested attributes, as a combination of the 2278 // requestedAttribute argument values and any trailing arguments. 2279 final ArrayList<String> attrList = new ArrayList<>(10); 2280 if (requestedAttribute.isPresent()) 2281 { 2282 attrList.addAll(requestedAttribute.getValues()); 2283 } 2284 2285 final List<String> trailingArgs = parser.getTrailingArguments(); 2286 if (! trailingArgs.isEmpty()) 2287 { 2288 final Iterator<String> trailingArgIterator = trailingArgs.iterator(); 2289 if (! (filter.isPresent() || filterFile.isPresent())) 2290 { 2291 trailingArgIterator.next(); 2292 } 2293 2294 while (trailingArgIterator.hasNext()) 2295 { 2296 attrList.add(trailingArgIterator.next()); 2297 } 2298 } 2299 2300 final String[] attributes = new String[attrList.size()]; 2301 attrList.toArray(attributes); 2302 2303 2304 // If either or both the filter or filterFile arguments are provided, then 2305 // use them to get the filters to process. Otherwise, the first trailing 2306 // argument should be a filter. 2307 ResultCode resultCode = ResultCode.SUCCESS; 2308 if (filter.isPresent() || filterFile.isPresent()) 2309 { 2310 if (filter.isPresent()) 2311 { 2312 for (final Filter f : filter.getValues()) 2313 { 2314 final ResultCode rc = searchWithFilter(pool, f, attributes, 2315 rateLimiter, searchControls); 2316 if (rc != ResultCode.SUCCESS) 2317 { 2318 if (resultCode == ResultCode.SUCCESS) 2319 { 2320 resultCode = rc; 2321 } 2322 2323 if (! continueOnError.isPresent()) 2324 { 2325 return resultCode; 2326 } 2327 } 2328 } 2329 } 2330 2331 if (filterFile.isPresent()) 2332 { 2333 final ResultCode rc = searchWithFilterFile(pool, attributes, 2334 rateLimiter, searchControls); 2335 if (rc != ResultCode.SUCCESS) 2336 { 2337 if (resultCode == ResultCode.SUCCESS) 2338 { 2339 resultCode = rc; 2340 } 2341 2342 if (! continueOnError.isPresent()) 2343 { 2344 return resultCode; 2345 } 2346 } 2347 } 2348 } 2349 else 2350 { 2351 final Filter f; 2352 try 2353 { 2354 final String filterStr = 2355 parser.getTrailingArguments().iterator().next(); 2356 f = Filter.create(filterStr); 2357 } 2358 catch (final LDAPException le) 2359 { 2360 // This should never happen. 2361 Debug.debugException(le); 2362 displayResult(le.toLDAPResult()); 2363 return le.getResultCode(); 2364 } 2365 2366 resultCode = 2367 searchWithFilter(pool, f, attributes, rateLimiter, searchControls); 2368 } 2369 2370 return resultCode; 2371 } 2372 finally 2373 { 2374 if (pool != null) 2375 { 2376 try 2377 { 2378 pool.close(); 2379 } 2380 catch (final Exception e) 2381 { 2382 Debug.debugException(e); 2383 } 2384 } 2385 2386 if (outStream != null) 2387 { 2388 try 2389 { 2390 outStream.close(); 2391 outStream = null; 2392 } 2393 catch (final Exception e) 2394 { 2395 Debug.debugException(e); 2396 } 2397 } 2398 2399 if (errStream != null) 2400 { 2401 try 2402 { 2403 errStream.close(); 2404 errStream = null; 2405 } 2406 catch (final Exception e) 2407 { 2408 Debug.debugException(e); 2409 } 2410 } 2411 2412 LDIFWriter.setCommentAboutBase64EncodedValues( 2413 originalCommentAboutBase64EncodedValues); 2414 } 2415 } 2416 2417 2418 2419 /** 2420 * Processes a set of searches using LDAP URLs read from one or more files. 2421 * 2422 * @param pool The connection pool to use to communicate with the 2423 * directory server. 2424 * @param rateLimiter An optional fixed-rate barrier that can be used for 2425 * request rate limiting. 2426 * @param searchControls The set of controls to include in search requests. 2427 * 2428 * @return A result code indicating the result of the processing. 2429 */ 2430 private ResultCode searchWithLDAPURLs(final LDAPConnectionPool pool, 2431 final FixedRateBarrier rateLimiter, 2432 final List<Control> searchControls) 2433 { 2434 ResultCode resultCode = ResultCode.SUCCESS; 2435 for (final File f : ldapURLFile.getValues()) 2436 { 2437 BufferedReader reader = null; 2438 2439 try 2440 { 2441 reader = new BufferedReader(new FileReader(f)); 2442 while (true) 2443 { 2444 final String line = reader.readLine(); 2445 if (line == null) 2446 { 2447 break; 2448 } 2449 2450 if ((line.length() == 0) || line.startsWith("#")) 2451 { 2452 continue; 2453 } 2454 2455 final LDAPURL url; 2456 try 2457 { 2458 url = new LDAPURL(line); 2459 } 2460 catch (final LDAPException le) 2461 { 2462 Debug.debugException(le); 2463 2464 commentToErr(ERR_LDAPSEARCH_MALFORMED_LDAP_URL.get( 2465 f.getAbsolutePath(), line)); 2466 if (resultCode == ResultCode.SUCCESS) 2467 { 2468 resultCode = le.getResultCode(); 2469 } 2470 2471 if (continueOnError.isPresent()) 2472 { 2473 continue; 2474 } 2475 else 2476 { 2477 return resultCode; 2478 } 2479 } 2480 2481 final SearchRequest searchRequest = new SearchRequest( 2482 new LDAPSearchListener(outputHandler, entryTransformations), 2483 url.getBaseDN().toString(), url.getScope(), derefPolicy, 2484 sizeLimit.getValue(), timeLimitSeconds.getValue(), 2485 typesOnly.isPresent(), url.getFilter(), url.getAttributes()); 2486 final ResultCode rc = 2487 doSearch(pool, searchRequest, rateLimiter, searchControls); 2488 if (rc != ResultCode.SUCCESS) 2489 { 2490 if (resultCode == ResultCode.SUCCESS) 2491 { 2492 resultCode = rc; 2493 } 2494 2495 if (! continueOnError.isPresent()) 2496 { 2497 return resultCode; 2498 } 2499 } 2500 } 2501 } 2502 catch (final IOException ioe) 2503 { 2504 commentToErr(ERR_LDAPSEARCH_CANNOT_READ_LDAP_URL_FILE.get( 2505 f.getAbsolutePath(), StaticUtils.getExceptionMessage(ioe))); 2506 return ResultCode.LOCAL_ERROR; 2507 } 2508 finally 2509 { 2510 if (reader != null) 2511 { 2512 try 2513 { 2514 reader.close(); 2515 } 2516 catch (final Exception e) 2517 { 2518 Debug.debugException(e); 2519 } 2520 } 2521 } 2522 } 2523 2524 return resultCode; 2525 } 2526 2527 2528 2529 /** 2530 * Processes a set of searches using filters read from one or more files. 2531 * 2532 * @param pool The connection pool to use to communicate with the 2533 * directory server. 2534 * @param attributes The set of attributes to request that the server 2535 * include in matching entries. 2536 * @param rateLimiter An optional fixed-rate barrier that can be used for 2537 * request rate limiting. 2538 * @param searchControls The set of controls to include in search requests. 2539 * 2540 * @return A result code indicating the result of the processing. 2541 */ 2542 private ResultCode searchWithFilterFile(final LDAPConnectionPool pool, 2543 final String[] attributes, 2544 final FixedRateBarrier rateLimiter, 2545 final List<Control> searchControls) 2546 { 2547 ResultCode resultCode = ResultCode.SUCCESS; 2548 for (final File f : filterFile.getValues()) 2549 { 2550 FilterFileReader reader = null; 2551 2552 try 2553 { 2554 reader = new FilterFileReader(f); 2555 while (true) 2556 { 2557 final Filter searchFilter; 2558 try 2559 { 2560 searchFilter = reader.readFilter(); 2561 } 2562 catch (final LDAPException le) 2563 { 2564 Debug.debugException(le); 2565 commentToErr(ERR_LDAPSEARCH_MALFORMED_FILTER.get( 2566 f.getAbsolutePath(), le.getMessage())); 2567 if (resultCode == ResultCode.SUCCESS) 2568 { 2569 resultCode = le.getResultCode(); 2570 } 2571 2572 if (continueOnError.isPresent()) 2573 { 2574 continue; 2575 } 2576 else 2577 { 2578 return resultCode; 2579 } 2580 } 2581 2582 if (searchFilter == null) 2583 { 2584 break; 2585 } 2586 2587 final ResultCode rc = searchWithFilter(pool, searchFilter, attributes, 2588 rateLimiter, searchControls); 2589 if (rc != ResultCode.SUCCESS) 2590 { 2591 if (resultCode == ResultCode.SUCCESS) 2592 { 2593 resultCode = rc; 2594 } 2595 2596 if (! continueOnError.isPresent()) 2597 { 2598 return resultCode; 2599 } 2600 } 2601 } 2602 } 2603 catch (final IOException ioe) 2604 { 2605 Debug.debugException(ioe); 2606 commentToErr(ERR_LDAPSEARCH_CANNOT_READ_FILTER_FILE.get( 2607 f.getAbsolutePath(), StaticUtils.getExceptionMessage(ioe))); 2608 return ResultCode.LOCAL_ERROR; 2609 } 2610 finally 2611 { 2612 if (reader != null) 2613 { 2614 try 2615 { 2616 reader.close(); 2617 } 2618 catch (final Exception e) 2619 { 2620 Debug.debugException(e); 2621 } 2622 } 2623 } 2624 } 2625 2626 return resultCode; 2627 } 2628 2629 2630 2631 /** 2632 * Processes a search using the provided filter. 2633 * 2634 * @param pool The connection pool to use to communicate with the 2635 * directory server. 2636 * @param filter The filter to use for the search. 2637 * @param attributes The set of attributes to request that the server 2638 * include in matching entries. 2639 * @param rateLimiter An optional fixed-rate barrier that can be used for 2640 * request rate limiting. 2641 * @param searchControls The set of controls to include in search requests. 2642 * 2643 * @return A result code indicating the result of the processing. 2644 */ 2645 private ResultCode searchWithFilter(final LDAPConnectionPool pool, 2646 final Filter filter, 2647 final String[] attributes, 2648 final FixedRateBarrier rateLimiter, 2649 final List<Control> searchControls) 2650 { 2651 final String baseDNString; 2652 if (baseDN.isPresent()) 2653 { 2654 baseDNString = baseDN.getStringValue(); 2655 } 2656 else 2657 { 2658 baseDNString = ""; 2659 } 2660 2661 final SearchRequest searchRequest = new SearchRequest( 2662 new LDAPSearchListener(outputHandler, entryTransformations), 2663 baseDNString, scope.getValue(), derefPolicy, sizeLimit.getValue(), 2664 timeLimitSeconds.getValue(), typesOnly.isPresent(), filter, 2665 attributes); 2666 return doSearch(pool, searchRequest, rateLimiter, searchControls); 2667 } 2668 2669 2670 2671 /** 2672 * Processes a search with the provided information. 2673 * 2674 * @param pool The connection pool to use to communicate with the 2675 * directory server. 2676 * @param searchRequest The search request to process. 2677 * @param rateLimiter An optional fixed-rate barrier that can be used for 2678 * request rate limiting. 2679 * @param searchControls The set of controls to include in search requests. 2680 * 2681 * @return A result code indicating the result of the processing. 2682 */ 2683 private ResultCode doSearch(final LDAPConnectionPool pool, 2684 final SearchRequest searchRequest, 2685 final FixedRateBarrier rateLimiter, 2686 final List<Control> searchControls) 2687 { 2688 if (separateOutputFilePerSearch.isPresent()) 2689 { 2690 try 2691 { 2692 final String path = outputFile.getValue().getAbsolutePath() + '.' + 2693 outputFileCounter.getAndIncrement(); 2694 2695 OutputStream s = new FileOutputStream(path); 2696 2697 if (encryptOutput.isPresent()) 2698 { 2699 s = new PassphraseEncryptedOutputStream(encryptionPassphrase, s); 2700 } 2701 2702 if (compressOutput.isPresent()) 2703 { 2704 s = new GZIPOutputStream(s); 2705 } 2706 2707 if (teeResultsToStandardOut.isPresent()) 2708 { 2709 outStream = new PrintStream(new TeeOutputStream(s, getOut())); 2710 } 2711 else 2712 { 2713 outStream = new PrintStream(s); 2714 } 2715 errStream = outStream; 2716 } 2717 catch (final Exception e) 2718 { 2719 Debug.debugException(e); 2720 wrapErr(0, WRAP_COLUMN, ERR_LDAPSEARCH_CANNOT_OPEN_OUTPUT_FILE.get( 2721 outputFile.getValue().getAbsolutePath(), 2722 StaticUtils.getExceptionMessage(e))); 2723 return ResultCode.LOCAL_ERROR; 2724 } 2725 2726 outputHandler.formatHeader(); 2727 } 2728 2729 try 2730 { 2731 if (rateLimiter != null) 2732 { 2733 rateLimiter.await(); 2734 } 2735 2736 2737 ASN1OctetString pagedResultsCookie = null; 2738 boolean multiplePages = false; 2739 long totalEntries = 0; 2740 long totalReferences = 0; 2741 2742 SearchResult searchResult; 2743 try 2744 { 2745 while (true) 2746 { 2747 searchRequest.setControls(searchControls); 2748 if (simplePageSize.isPresent()) 2749 { 2750 searchRequest.addControl(new SimplePagedResultsControl( 2751 simplePageSize.getValue(), pagedResultsCookie)); 2752 } 2753 2754 if (dryRun.isPresent()) 2755 { 2756 searchResult = new SearchResult(-1, ResultCode.SUCCESS, 2757 INFO_LDAPSEARCH_DRY_RUN_REQUEST_NOT_SENT.get( 2758 dryRun.getIdentifierString(), 2759 String.valueOf(searchRequest)), 2760 null, null, 0, 0, null); 2761 break; 2762 } 2763 else 2764 { 2765 if (! terse.isPresent()) 2766 { 2767 if (verbose.isPresent() || persistentSearch.isPresent() || 2768 filterFile.isPresent() || ldapURLFile.isPresent() || 2769 (filter.isPresent() && (filter.getNumOccurrences() > 1))) 2770 { 2771 commentToOut(INFO_LDAPSEARCH_SENDING_SEARCH_REQUEST.get( 2772 String.valueOf(searchRequest))); 2773 } 2774 } 2775 searchResult = pool.search(searchRequest); 2776 } 2777 2778 if (searchResult.getEntryCount() > 0) 2779 { 2780 totalEntries += searchResult.getEntryCount(); 2781 } 2782 2783 if (searchResult.getReferenceCount() > 0) 2784 { 2785 totalReferences += searchResult.getReferenceCount(); 2786 } 2787 2788 if (simplePageSize.isPresent()) 2789 { 2790 final SimplePagedResultsControl pagedResultsControl; 2791 try 2792 { 2793 pagedResultsControl = SimplePagedResultsControl.get(searchResult); 2794 if (pagedResultsControl == null) 2795 { 2796 throw new LDAPSearchException(new SearchResult( 2797 searchResult.getMessageID(), ResultCode.CONTROL_NOT_FOUND, 2798 ERR_LDAPSEARCH_MISSING_PAGED_RESULTS_RESPONSE_CONTROL. 2799 get(), 2800 searchResult.getMatchedDN(), 2801 searchResult.getReferralURLs(), 2802 searchResult.getSearchEntries(), 2803 searchResult.getSearchReferences(), 2804 searchResult.getEntryCount(), 2805 searchResult.getReferenceCount(), 2806 searchResult.getResponseControls())); 2807 } 2808 2809 if (pagedResultsControl.moreResultsToReturn()) 2810 { 2811 if (verbose.isPresent()) 2812 { 2813 commentToOut( 2814 INFO_LDAPSEARCH_INTERMEDIATE_PAGED_SEARCH_RESULT.get()); 2815 displayResult(searchResult); 2816 } 2817 2818 multiplePages = true; 2819 pagedResultsCookie = pagedResultsControl.getCookie(); 2820 } 2821 else 2822 { 2823 break; 2824 } 2825 } 2826 catch (final LDAPException le) 2827 { 2828 Debug.debugException(le); 2829 throw new LDAPSearchException(new SearchResult( 2830 searchResult.getMessageID(), ResultCode.CONTROL_NOT_FOUND, 2831 ERR_LDAPSEARCH_CANNOT_DECODE_PAGED_RESULTS_RESPONSE_CONTROL. 2832 get(StaticUtils.getExceptionMessage(le)), 2833 searchResult.getMatchedDN(), searchResult.getReferralURLs(), 2834 searchResult.getSearchEntries(), 2835 searchResult.getSearchReferences(), 2836 searchResult.getEntryCount(), 2837 searchResult.getReferenceCount(), 2838 searchResult.getResponseControls())); 2839 } 2840 } 2841 else 2842 { 2843 break; 2844 } 2845 } 2846 } 2847 catch (final LDAPSearchException lse) 2848 { 2849 Debug.debugException(lse); 2850 searchResult = lse.toLDAPResult(); 2851 2852 if (searchResult.getEntryCount() > 0) 2853 { 2854 totalEntries += searchResult.getEntryCount(); 2855 } 2856 2857 if (searchResult.getReferenceCount() > 0) 2858 { 2859 totalReferences += searchResult.getReferenceCount(); 2860 } 2861 } 2862 2863 if ((searchResult.getResultCode() != ResultCode.SUCCESS) || 2864 (searchResult.getDiagnosticMessage() != null) || 2865 (! terse.isPresent())) 2866 { 2867 displayResult(searchResult); 2868 } 2869 2870 if (multiplePages && (! terse.isPresent())) 2871 { 2872 commentToOut(INFO_LDAPSEARCH_TOTAL_SEARCH_ENTRIES.get(totalEntries)); 2873 2874 if (totalReferences > 0) 2875 { 2876 commentToOut(INFO_LDAPSEARCH_TOTAL_SEARCH_REFERENCES.get( 2877 totalReferences)); 2878 } 2879 } 2880 2881 if (countEntries.isPresent()) 2882 { 2883 return ResultCode.valueOf((int) Math.min(totalEntries, 255)); 2884 } 2885 else 2886 { 2887 return searchResult.getResultCode(); 2888 } 2889 } 2890 finally 2891 { 2892 if (separateOutputFilePerSearch.isPresent()) 2893 { 2894 try 2895 { 2896 outStream.close(); 2897 } 2898 catch (final Exception e) 2899 { 2900 Debug.debugException(e); 2901 } 2902 2903 outStream = null; 2904 errStream = null; 2905 } 2906 } 2907 } 2908 2909 2910 2911 /** 2912 * Retrieves a list of the controls that should be used when processing search 2913 * operations. 2914 * 2915 * @return A list of the controls that should be used when processing search 2916 * operations. 2917 */ 2918 private List<Control> getSearchControls() 2919 { 2920 final ArrayList<Control> controls = new ArrayList<>(10); 2921 2922 if (searchControl.isPresent()) 2923 { 2924 controls.addAll(searchControl.getValues()); 2925 } 2926 2927 if (joinRequestControl != null) 2928 { 2929 controls.add(joinRequestControl); 2930 } 2931 2932 if (matchedValuesRequestControl != null) 2933 { 2934 controls.add(matchedValuesRequestControl); 2935 } 2936 2937 if (matchingEntryCountRequestControl != null) 2938 { 2939 controls.add(matchingEntryCountRequestControl); 2940 } 2941 2942 if (overrideSearchLimitsRequestControl != null) 2943 { 2944 controls.add(overrideSearchLimitsRequestControl); 2945 } 2946 2947 if (persistentSearchRequestControl != null) 2948 { 2949 controls.add(persistentSearchRequestControl); 2950 } 2951 2952 if (sortRequestControl != null) 2953 { 2954 controls.add(sortRequestControl); 2955 } 2956 2957 if (vlvRequestControl != null) 2958 { 2959 controls.add(vlvRequestControl); 2960 } 2961 2962 if (accountUsable.isPresent()) 2963 { 2964 controls.add(new AccountUsableRequestControl(true)); 2965 } 2966 2967 if (includeReplicationConflictEntries.isPresent()) 2968 { 2969 controls.add(new ReturnConflictEntriesRequestControl(true)); 2970 } 2971 2972 if (includeSoftDeletedEntries.isPresent()) 2973 { 2974 final String valueStr = 2975 StaticUtils.toLowerCase(includeSoftDeletedEntries.getValue()); 2976 if (valueStr.equals("with-non-deleted-entries")) 2977 { 2978 controls.add(new SoftDeletedEntryAccessRequestControl(true, true, 2979 false)); 2980 } 2981 else if (valueStr.equals("without-non-deleted-entries")) 2982 { 2983 controls.add(new SoftDeletedEntryAccessRequestControl(true, false, 2984 false)); 2985 } 2986 else 2987 { 2988 controls.add(new SoftDeletedEntryAccessRequestControl(true, false, 2989 true)); 2990 } 2991 } 2992 2993 if (includeSubentries.isPresent()) 2994 { 2995 controls.add(new SubentriesRequestControl(true)); 2996 } 2997 2998 if (manageDsaIT.isPresent()) 2999 { 3000 controls.add(new ManageDsaITRequestControl(true)); 3001 } 3002 3003 if (realAttributesOnly.isPresent()) 3004 { 3005 controls.add(new RealAttributesOnlyRequestControl(true)); 3006 } 3007 3008 if (virtualAttributesOnly.isPresent()) 3009 { 3010 controls.add(new VirtualAttributesOnlyRequestControl(true)); 3011 } 3012 3013 if (excludeBranch.isPresent()) 3014 { 3015 final ArrayList<String> dns = 3016 new ArrayList<>(excludeBranch.getValues().size()); 3017 for (final DN dn : excludeBranch.getValues()) 3018 { 3019 dns.add(dn.toString()); 3020 } 3021 controls.add(new ExcludeBranchRequestControl(true, dns)); 3022 } 3023 3024 if (assertionFilter.isPresent()) 3025 { 3026 controls.add(new AssertionRequestControl( 3027 assertionFilter.getValue(), true)); 3028 } 3029 3030 if (getEffectiveRightsAuthzID.isPresent()) 3031 { 3032 final String[] attributes; 3033 if (getEffectiveRightsAttribute.isPresent()) 3034 { 3035 attributes = new String[getEffectiveRightsAttribute.getValues().size()]; 3036 for (int i=0; i < attributes.length; i++) 3037 { 3038 attributes[i] = getEffectiveRightsAttribute.getValues().get(i); 3039 } 3040 } 3041 else 3042 { 3043 attributes = StaticUtils.NO_STRINGS; 3044 } 3045 3046 controls.add(new GetEffectiveRightsRequestControl(true, 3047 getEffectiveRightsAuthzID.getValue(), attributes)); 3048 } 3049 3050 if (operationPurpose.isPresent()) 3051 { 3052 controls.add(new OperationPurposeRequestControl(true, "ldapsearch", 3053 Version.NUMERIC_VERSION_STRING, "LDAPSearch.getSearchControls", 3054 operationPurpose.getValue())); 3055 } 3056 3057 if (proxyAs.isPresent()) 3058 { 3059 controls.add(new ProxiedAuthorizationV2RequestControl( 3060 proxyAs.getValue())); 3061 } 3062 3063 if (proxyV1As.isPresent()) 3064 { 3065 controls.add(new ProxiedAuthorizationV1RequestControl( 3066 proxyV1As.getValue())); 3067 } 3068 3069 if (suppressOperationalAttributeUpdates.isPresent()) 3070 { 3071 final EnumSet<SuppressType> suppressTypes = 3072 EnumSet.noneOf(SuppressType.class); 3073 for (final String s : suppressOperationalAttributeUpdates.getValues()) 3074 { 3075 if (s.equalsIgnoreCase("last-access-time")) 3076 { 3077 suppressTypes.add(SuppressType.LAST_ACCESS_TIME); 3078 } 3079 else if (s.equalsIgnoreCase("last-login-time")) 3080 { 3081 suppressTypes.add(SuppressType.LAST_LOGIN_TIME); 3082 } 3083 else if (s.equalsIgnoreCase("last-login-ip")) 3084 { 3085 suppressTypes.add(SuppressType.LAST_LOGIN_IP); 3086 } 3087 } 3088 3089 controls.add(new SuppressOperationalAttributeUpdateRequestControl( 3090 suppressTypes)); 3091 } 3092 3093 if (rejectUnindexedSearch.isPresent()) 3094 { 3095 controls.add(new RejectUnindexedSearchRequestControl()); 3096 } 3097 3098 if (permitUnindexedSearch.isPresent()) 3099 { 3100 controls.add(new PermitUnindexedSearchRequestControl()); 3101 } 3102 3103 return controls; 3104 } 3105 3106 3107 3108 /** 3109 * Displays information about the provided result, including special 3110 * processing for a number of supported response controls. 3111 * 3112 * @param result The result to examine. 3113 */ 3114 void displayResult(final LDAPResult result) 3115 { 3116 outputHandler.formatResult(result); 3117 } 3118 3119 3120 3121 /** 3122 * Writes the provided message to the output stream. 3123 * 3124 * @param message The message to be written. 3125 */ 3126 void writeOut(final String message) 3127 { 3128 if (outStream == null) 3129 { 3130 out(message); 3131 } 3132 else 3133 { 3134 outStream.println(message); 3135 } 3136 } 3137 3138 3139 3140 /** 3141 * Writes the provided message to the error stream. 3142 * 3143 * @param message The message to be written. 3144 */ 3145 void writeErr(final String message) 3146 { 3147 if (errStream == null) 3148 { 3149 err(message); 3150 } 3151 else 3152 { 3153 errStream.println(message); 3154 } 3155 } 3156 3157 3158 3159 /** 3160 * Writes a line-wrapped, commented version of the provided message to 3161 * standard output. 3162 * 3163 * @param message The message to be written. 3164 */ 3165 private void commentToOut(final String message) 3166 { 3167 if (terse.isPresent()) 3168 { 3169 return; 3170 } 3171 3172 for (final String line : StaticUtils.wrapLine(message, (WRAP_COLUMN - 2))) 3173 { 3174 writeOut("# " + line); 3175 } 3176 } 3177 3178 3179 3180 /** 3181 * Writes a line-wrapped, commented version of the provided message to 3182 * standard error. 3183 * 3184 * @param message The message to be written. 3185 */ 3186 private void commentToErr(final String message) 3187 { 3188 for (final String line : StaticUtils.wrapLine(message, (WRAP_COLUMN - 2))) 3189 { 3190 writeErr("# " + line); 3191 } 3192 } 3193 3194 3195 3196 /** 3197 * Sets the output handler that should be used by this tool This is primarily 3198 * intended for testing purposes. 3199 * 3200 * @param outputHandler The output handler that should be used by this tool. 3201 */ 3202 void setOutputHandler(final LDAPSearchOutputHandler outputHandler) 3203 { 3204 this.outputHandler = outputHandler; 3205 } 3206 3207 3208 3209 /** 3210 * {@inheritDoc} 3211 */ 3212 @Override() 3213 public void handleUnsolicitedNotification(final LDAPConnection connection, 3214 final ExtendedResult notification) 3215 { 3216 outputHandler.formatUnsolicitedNotification(connection, notification); 3217 } 3218 3219 3220 3221 /** 3222 * {@inheritDoc} 3223 */ 3224 @Override() 3225 public LinkedHashMap<String[],String> getExampleUsages() 3226 { 3227 final LinkedHashMap<String[],String> examples = new LinkedHashMap<>(5); 3228 3229 String[] args = 3230 { 3231 "--hostname", "directory.example.com", 3232 "--port", "389", 3233 "--bindDN", "uid=jdoe,ou=People,dc=example,dc=com", 3234 "--bindPassword", "password", 3235 "--baseDN", "ou=People,dc=example,dc=com", 3236 "--searchScope", "sub", 3237 "(uid=jqpublic)", 3238 "givenName", 3239 "sn", 3240 "mail" 3241 }; 3242 examples.put(args, INFO_LDAPSEARCH_EXAMPLE_1.get()); 3243 3244 3245 args = new String[] 3246 { 3247 "--hostname", "directory.example.com", 3248 "--port", "636", 3249 "--useSSL", 3250 "--saslOption", "mech=PLAIN", 3251 "--saslOption", "authID=u:jdoe", 3252 "--bindPasswordFile", "/path/to/password/file", 3253 "--baseDN", "ou=People,dc=example,dc=com", 3254 "--searchScope", "sub", 3255 "--filterFile", "/path/to/filter/file", 3256 "--outputFile", "/path/to/base/output/file", 3257 "--separateOutputFilePerSearch", 3258 "--requestedAttribute", "*", 3259 "--requestedAttribute", "+" 3260 }; 3261 examples.put(args, INFO_LDAPSEARCH_EXAMPLE_2.get()); 3262 3263 3264 args = new String[] 3265 { 3266 "--hostname", "directory.example.com", 3267 "--port", "389", 3268 "--useStartTLS", 3269 "--trustStorePath", "/path/to/truststore/file", 3270 "--baseDN", "", 3271 "--searchScope", "base", 3272 "--outputFile", "/path/to/output/file", 3273 "--teeResultsToStandardOut", 3274 "(objectClass=*)", 3275 "*", 3276 "+" 3277 }; 3278 examples.put(args, INFO_LDAPSEARCH_EXAMPLE_3.get()); 3279 3280 3281 args = new String[] 3282 { 3283 "--hostname", "directory.example.com", 3284 "--port", "389", 3285 "--bindDN", "uid=admin,dc=example,dc=com", 3286 "--baseDN", "dc=example,dc=com", 3287 "--searchScope", "sub", 3288 "--outputFile", "/path/to/output/file", 3289 "--simplePageSize", "100", 3290 "(objectClass=*)", 3291 "*", 3292 "+" 3293 }; 3294 examples.put(args, INFO_LDAPSEARCH_EXAMPLE_4.get()); 3295 3296 3297 args = new String[] 3298 { 3299 "--hostname", "directory.example.com", 3300 "--port", "389", 3301 "--bindDN", "uid=admin,dc=example,dc=com", 3302 "--baseDN", "dc=example,dc=com", 3303 "--searchScope", "sub", 3304 "(&(givenName=John)(sn=Doe))", 3305 "debugsearchindex" 3306 }; 3307 examples.put(args, INFO_LDAPSEARCH_EXAMPLE_5.get()); 3308 3309 return examples; 3310 } 3311}