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