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