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