All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.unboundid.ldap.sdk.unboundidds.tools.LDAPSearch Maven / Gradle / Ivy

/*
 * Copyright 2017-2024 Ping Identity Corporation
 * All Rights Reserved.
 */
/*
 * Copyright 2017-2024 Ping Identity Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * Copyright (C) 2017-2024 Ping Identity Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License (GPLv2 only)
 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see .
 */
package com.unboundid.ldap.sdk.unboundidds.tools;



import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.atomic.AtomicLong;
import java.util.zip.GZIPOutputStream;

import com.unboundid.asn1.ASN1OctetString;
import com.unboundid.ldap.sdk.Control;
import com.unboundid.ldap.sdk.DN;
import com.unboundid.ldap.sdk.DereferencePolicy;
import com.unboundid.ldap.sdk.ExtendedResult;
import com.unboundid.ldap.sdk.Filter;
import com.unboundid.ldap.sdk.LDAPConnectionOptions;
import com.unboundid.ldap.sdk.LDAPConnection;
import com.unboundid.ldap.sdk.LDAPConnectionPool;
import com.unboundid.ldap.sdk.LDAPException;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.LDAPSearchException;
import com.unboundid.ldap.sdk.LDAPURL;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.ldap.sdk.SearchRequest;
import com.unboundid.ldap.sdk.SearchResult;
import com.unboundid.ldap.sdk.SearchScope;
import com.unboundid.ldap.sdk.UnsolicitedNotificationHandler;
import com.unboundid.ldap.sdk.Version;
import com.unboundid.ldap.sdk.controls.AssertionRequestControl;
import com.unboundid.ldap.sdk.controls.AuthorizationIdentityRequestControl;
import com.unboundid.ldap.sdk.controls.DraftLDUPSubentriesRequestControl;
import com.unboundid.ldap.sdk.controls.ManageDsaITRequestControl;
import com.unboundid.ldap.sdk.controls.MatchedValuesFilter;
import com.unboundid.ldap.sdk.controls.MatchedValuesRequestControl;
import com.unboundid.ldap.sdk.controls.PersistentSearchChangeType;
import com.unboundid.ldap.sdk.controls.PersistentSearchRequestControl;
import com.unboundid.ldap.sdk.controls.ProxiedAuthorizationV1RequestControl;
import com.unboundid.ldap.sdk.controls.ProxiedAuthorizationV2RequestControl;
import com.unboundid.ldap.sdk.controls.RFC3672SubentriesRequestControl;
import com.unboundid.ldap.sdk.controls.ServerSideSortRequestControl;
import com.unboundid.ldap.sdk.controls.SimplePagedResultsControl;
import com.unboundid.ldap.sdk.controls.SortKey;
import com.unboundid.ldap.sdk.controls.VirtualListViewRequestControl;
import com.unboundid.ldap.sdk.persist.PersistUtils;
import com.unboundid.ldap.sdk.transformations.EntryTransformation;
import com.unboundid.ldap.sdk.transformations.ExcludeAttributeTransformation;
import com.unboundid.ldap.sdk.transformations.MoveSubtreeTransformation;
import com.unboundid.ldap.sdk.transformations.RedactAttributeTransformation;
import com.unboundid.ldap.sdk.transformations.RenameAttributeTransformation;
import com.unboundid.ldap.sdk.transformations.ScrambleAttributeTransformation;
import com.unboundid.ldap.sdk.unboundidds.controls.AccessLogFieldRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.AccountUsableRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.ExcludeBranchRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
            GenerateAccessTokenRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
            GetAuthorizationEntryRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
            GetBackendSetIDRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
            GetEffectiveRightsRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
            GetRecentLoginHistoryRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.GetServerIDRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
            GetUserResourceLimitsRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
            JSONFormattedControlDecodeBehavior;
import com.unboundid.ldap.sdk.unboundidds.controls.JSONFormattedRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.JSONFormattedResponseControl;
import com.unboundid.ldap.sdk.unboundidds.controls.JoinBaseDN;
import com.unboundid.ldap.sdk.unboundidds.controls.JoinRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.JoinRequestValue;
import com.unboundid.ldap.sdk.unboundidds.controls.JoinRule;
import com.unboundid.ldap.sdk.unboundidds.controls.
            MatchingEntryCountRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
            MatchingEntryCountRequestControlProperties;
import com.unboundid.ldap.sdk.unboundidds.controls.
            OperationPurposeRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
            OverrideSearchLimitsRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.PasswordPolicyRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
            PermitUnindexedSearchRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
            RealAttributesOnlyRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
            RejectUnindexedSearchRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
            ReturnConflictEntriesRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
            RouteToBackendSetRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.RouteToServerRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
            SoftDeletedEntryAccessRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.
            SuppressOperationalAttributeUpdateRequestControl;
import com.unboundid.ldap.sdk.unboundidds.controls.SuppressType;
import com.unboundid.ldap.sdk.unboundidds.controls.
            VirtualAttributesOnlyRequestControl;
import com.unboundid.ldap.sdk.unboundidds.extensions.
            StartAdministrativeSessionExtendedRequest;
import com.unboundid.ldap.sdk.unboundidds.extensions.
            StartAdministrativeSessionPostConnectProcessor;
import com.unboundid.ldif.LDIFWriter;
import com.unboundid.util.Debug;
import com.unboundid.util.FilterFileReader;
import com.unboundid.util.FixedRateBarrier;
import com.unboundid.util.LDAPCommandLineTool;
import com.unboundid.util.NotNull;
import com.unboundid.util.Nullable;
import com.unboundid.util.OutputFormat;
import com.unboundid.util.PassphraseEncryptedOutputStream;
import com.unboundid.util.StaticUtils;
import com.unboundid.util.TeeOutputStream;
import com.unboundid.util.ThreadSafety;
import com.unboundid.util.ThreadSafetyLevel;
import com.unboundid.util.args.ArgumentException;
import com.unboundid.util.args.ArgumentParser;
import com.unboundid.util.args.BooleanArgument;
import com.unboundid.util.args.BooleanValueArgument;
import com.unboundid.util.args.ControlArgument;
import com.unboundid.util.args.DNArgument;
import com.unboundid.util.args.FileArgument;
import com.unboundid.util.args.FilterArgument;
import com.unboundid.util.args.IntegerArgument;
import com.unboundid.util.args.ScopeArgument;
import com.unboundid.util.args.StringArgument;
import com.unboundid.util.json.JSONBoolean;
import com.unboundid.util.json.JSONNumber;
import com.unboundid.util.json.JSONObject;
import com.unboundid.util.json.JSONString;
import com.unboundid.util.json.JSONValue;

import static com.unboundid.ldap.sdk.unboundidds.tools.ToolMessages.*;



/**
 * This class provides an implementation of an LDAP command-line tool that may
 * be used to issue searches to a directory server.  Matching entries will be
 * output in the LDAP data interchange format (LDIF), to standard output and/or
 * to a specified file.  This is a much more full-featured tool than the
 * {@link com.unboundid.ldap.sdk.examples.LDAPSearch} tool, and includes a
 * number of features only intended for use with Ping Identity, UnboundID, and
 * Nokia/Alcatel-Lucent 8661 server products.
 * 
*
* NOTE: This class, and other classes within the * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only * supported for use against Ping Identity, UnboundID, and * Nokia/Alcatel-Lucent 8661 server products. These classes provide support * for proprietary functionality or for external specifications that are not * considered stable or mature enough to be guaranteed to work in an * interoperable way with other types of LDAP servers. *
*/ @ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) public final class LDAPSearch extends LDAPCommandLineTool implements UnsolicitedNotificationHandler { /** * The column at which to wrap long lines. */ private static int WRAP_COLUMN = StaticUtils.TERMINAL_WIDTH_COLUMNS - 1; // The set of arguments supported by this program. @Nullable private BooleanArgument accountUsable = null; @Nullable private BooleanArgument authorizationIdentity = null; @Nullable private BooleanArgument compressOutput = null; @Nullable private BooleanArgument continueOnError = null; @Nullable private BooleanArgument countEntries = null; @Nullable private BooleanArgument dontWrap = null; @Nullable private BooleanArgument draftLDUPSubentries = null; @Nullable private BooleanArgument dryRun = null; @Nullable private BooleanArgument encryptOutput = null; @Nullable private BooleanArgument followReferrals = null; @Nullable private BooleanArgument generateAccessToken = null; @Nullable private BooleanArgument getBackendSetID = null; @Nullable private BooleanArgument getServerID = null; @Nullable private BooleanArgument getRecentLoginHistory = null; @Nullable private BooleanArgument hideRedactedValueCount = null; @Nullable private BooleanArgument getUserResourceLimits = null; @Nullable private BooleanArgument includeReplicationConflictEntries = null; @Nullable private BooleanArgument joinRequireMatch = null; @Nullable private BooleanArgument manageDsaIT = null; @Nullable private BooleanArgument permitUnindexedSearch = null; @Nullable private BooleanArgument realAttributesOnly = null; @Nullable private BooleanArgument rejectUnindexedSearch = null; @Nullable private BooleanArgument requireMatch = null; @Nullable private BooleanArgument retryFailedOperations = null; @Nullable private BooleanArgument separateOutputFilePerSearch = null; @Nullable private BooleanArgument suppressBase64EncodedValueComments = null; @Nullable private BooleanArgument teeResultsToStandardOut = null; @Nullable private BooleanArgument useAdministrativeSession = null; @Nullable private BooleanArgument useJSONFormattedRequestControls = null; @Nullable private BooleanArgument usePasswordPolicyControl = null; @Nullable private BooleanArgument terse = null; @Nullable private BooleanArgument typesOnly = null; @Nullable private BooleanArgument verbose = null; @Nullable private BooleanArgument virtualAttributesOnly = null; @Nullable private BooleanValueArgument rfc3672Subentries = null; @Nullable private ControlArgument bindControl = null; @Nullable private ControlArgument searchControl = null; @Nullable private DNArgument baseDN = null; @Nullable private DNArgument excludeBranch = null; @Nullable private DNArgument moveSubtreeFrom = null; @Nullable private DNArgument moveSubtreeTo = null; @Nullable private DNArgument proxyV1As = null; @Nullable private FileArgument encryptionPassphraseFile = null; @Nullable private FileArgument filterFile = null; @Nullable private FileArgument ldapURLFile = null; @Nullable private FileArgument outputFile = null; @Nullable private FilterArgument assertionFilter = null; @Nullable private FilterArgument filter = null; @Nullable private FilterArgument joinFilter = null; @Nullable private FilterArgument matchedValuesFilter = null; @Nullable private IntegerArgument joinSizeLimit = null; @Nullable private IntegerArgument ratePerSecond = null; @Nullable private IntegerArgument scrambleRandomSeed = null; @Nullable private IntegerArgument simplePageSize = null; @Nullable private IntegerArgument sizeLimit = null; @Nullable private IntegerArgument timeLimitSeconds = null; @Nullable private IntegerArgument wrapColumn = null; @Nullable private ScopeArgument joinScope = null; @Nullable private ScopeArgument scope = null; @Nullable private StringArgument accessLogField = null; @Nullable private StringArgument dereferencePolicy = null; @Nullable private StringArgument excludeAttribute = null; @Nullable private StringArgument getAuthorizationEntryAttribute = null; @Nullable private StringArgument getEffectiveRightsAttribute = null; @Nullable private StringArgument getEffectiveRightsAuthzID = null; @Nullable private StringArgument includeSoftDeletedEntries = null; @Nullable private StringArgument joinBaseDN = null; @Nullable private StringArgument joinRequestedAttribute = null; @Nullable private StringArgument joinRule = null; @Nullable private StringArgument matchingEntryCountControl = null; @Nullable private StringArgument operationPurpose = null; @Nullable private StringArgument outputFormat = null; @Nullable private StringArgument overrideSearchLimit = null; @Nullable private StringArgument persistentSearch = null; @Nullable private StringArgument proxyAs = null; @Nullable private StringArgument redactAttribute = null; @Nullable private StringArgument renameAttributeFrom = null; @Nullable private StringArgument renameAttributeTo = null; @Nullable private StringArgument requestedAttribute = null; @Nullable private StringArgument routeToBackendSet = null; @Nullable private StringArgument routeToServer = null; @Nullable private StringArgument scrambleAttribute = null; @Nullable private StringArgument scrambleJSONField = null; @Nullable private StringArgument sortOrder = null; @Nullable private StringArgument suppressOperationalAttributeUpdates = null; @Nullable private StringArgument virtualListView = null; // The argument parser used by this tool. @Nullable private volatile ArgumentParser parser = null; // Controls that should be sent to the server but need special validation. @Nullable private volatile JoinRequestControl joinRequestControl = null; @NotNull private final List routeToBackendSetRequestControls = new ArrayList<>(10); @Nullable private volatile MatchedValuesRequestControl matchedValuesRequestControl = null; @Nullable private volatile MatchingEntryCountRequestControl matchingEntryCountRequestControl = null; @Nullable private volatile OverrideSearchLimitsRequestControl overrideSearchLimitsRequestControl = null; @Nullable private volatile PersistentSearchRequestControl persistentSearchRequestControl = null; @Nullable private volatile ServerSideSortRequestControl sortRequestControl = null; @Nullable private volatile VirtualListViewRequestControl vlvRequestControl = null; // Other values decoded from arguments. @Nullable private volatile DereferencePolicy derefPolicy = null; // The print streams used for standard output and error. @NotNull private final AtomicLong outputFileCounter = new AtomicLong(1); @Nullable private volatile PrintStream errStream = null; @Nullable private volatile PrintStream outStream = null; // The LDAP result writer for this tool. @NotNull private volatile LDAPResultWriter resultWriter; // The list of entry transformations to apply. @Nullable private volatile List entryTransformations = null; // The encryption passphrase to use if the output is to be encrypted. @Nullable private String encryptionPassphrase = null; /** * Runs this tool with the provided command-line arguments. It will use the * JVM-default streams for standard input, output, and error. * * @param args The command-line arguments to provide to this program. */ public static void main(@NotNull final String... args) { final ResultCode resultCode = main(System.out, System.err, args); if (resultCode != ResultCode.SUCCESS) { System.exit(Math.min(resultCode.intValue(), 255)); } } /** * Runs this tool with the provided streams and command-line arguments. * * @param out The output stream to use for standard output. If this is * {@code null}, then standard output will be suppressed. * @param err The output stream to use for standard error. If this is * {@code null}, then standard error will be suppressed. * @param args The command-line arguments provided to this program. * * @return The result code obtained when running the tool. Any result code * other than {@link ResultCode#SUCCESS} indicates an error. */ @NotNull() public static ResultCode main(@Nullable final OutputStream out, @Nullable final OutputStream err, @NotNull final String... args) { final LDAPSearch tool = new LDAPSearch(out, err); return tool.runTool(args); } /** * Creates a new instance of this tool with the provided streams. * * @param out The output stream to use for standard output. If this is * {@code null}, then standard output will be suppressed. * @param err The output stream to use for standard error. If this is * {@code null}, then standard error will be suppressed. */ public LDAPSearch(@Nullable final OutputStream out, @Nullable final OutputStream err) { super(out, err); resultWriter = new LDIFLDAPResultWriter(getOut(), WRAP_COLUMN); } /** * {@inheritDoc} */ @Override() @NotNull() public String getToolName() { return "ldapsearch"; } /** * {@inheritDoc} */ @Override() @NotNull() public String getToolDescription() { return INFO_LDAPSEARCH_TOOL_DESCRIPTION.get(); } /** * {@inheritDoc} */ @Override() @NotNull() public List getAdditionalDescriptionParagraphs() { return Arrays.asList( INFO_LDAPSEARCH_ADDITIONAL_DESCRIPTION_PARAGRAPH_1.get(), INFO_LDAPSEARCH_ADDITIONAL_DESCRIPTION_PARAGRAPH_2.get()); } /** * {@inheritDoc} */ @Override() @NotNull() public String getToolVersion() { return Version.NUMERIC_VERSION_STRING; } /** * {@inheritDoc} */ @Override() public int getMinTrailingArguments() { return 0; } /** * {@inheritDoc} */ @Override() public int getMaxTrailingArguments() { return -1; } /** * {@inheritDoc} */ @Override() @NotNull() public String getTrailingArgumentsPlaceholder() { return INFO_LDAPSEARCH_TRAILING_ARGS_PLACEHOLDER.get(); } /** * {@inheritDoc} */ @Override() public boolean supportsInteractiveMode() { return true; } /** * {@inheritDoc} */ @Override() public boolean defaultsToInteractiveMode() { return true; } /** * {@inheritDoc} */ @Override() public boolean supportsPropertiesFile() { return true; } /** * {@inheritDoc} */ @Override() protected boolean supportsDebugLogging() { return true; } /** * {@inheritDoc} */ @Override() protected boolean defaultToPromptForBindPassword() { return true; } /** * {@inheritDoc} */ @Override() protected boolean includeAlternateLongIdentifiers() { return true; } /** * {@inheritDoc} */ @Override() protected boolean supportsSSLDebugging() { return true; } /** * {@inheritDoc} */ @Override() @NotNull() protected Set getSuppressedShortIdentifiers() { return Collections.singleton('T'); } /** * {@inheritDoc} */ @Override() public void addNonLDAPArguments(@NotNull final ArgumentParser parser) throws ArgumentException { this.parser = parser; baseDN = new DNArgument('b', "baseDN", false, 1, null, INFO_LDAPSEARCH_ARG_DESCRIPTION_BASE_DN.get()); baseDN.addLongIdentifier("base-dn", true); baseDN.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); parser.addArgument(baseDN); scope = new ScopeArgument('s', "scope", false, null, INFO_LDAPSEARCH_ARG_DESCRIPTION_SCOPE.get(), SearchScope.SUB); scope.addLongIdentifier("searchScope", true); scope.addLongIdentifier("search-scope", true); scope.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); parser.addArgument(scope); sizeLimit = new IntegerArgument('z', "sizeLimit", false, 1, null, INFO_LDAPSEARCH_ARG_DESCRIPTION_SIZE_LIMIT.get(), 0, Integer.MAX_VALUE, 0); sizeLimit.addLongIdentifier("size-limit", true); sizeLimit.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); parser.addArgument(sizeLimit); timeLimitSeconds = new IntegerArgument('l', "timeLimitSeconds", false, 1, null, INFO_LDAPSEARCH_ARG_DESCRIPTION_TIME_LIMIT.get(), 0, Integer.MAX_VALUE, 0); timeLimitSeconds.addLongIdentifier("timeLimit", true); timeLimitSeconds.addLongIdentifier("time-limit-seconds", true); timeLimitSeconds.addLongIdentifier("time-limit", true); timeLimitSeconds.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); parser.addArgument(timeLimitSeconds); final Set derefAllowedValues = StaticUtils.setOf("never", "always", "search", "find"); dereferencePolicy = new StringArgument('a', "dereferencePolicy", false, 1, "{never|always|search|find}", INFO_LDAPSEARCH_ARG_DESCRIPTION_DEREFERENCE_POLICY.get(), derefAllowedValues, "never"); dereferencePolicy.addLongIdentifier("dereference-policy", true); dereferencePolicy.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); parser.addArgument(dereferencePolicy); typesOnly = new BooleanArgument('A', "typesOnly", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_TYPES_ONLY.get()); typesOnly.addLongIdentifier("types-only", true); typesOnly.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); parser.addArgument(typesOnly); requestedAttribute = new StringArgument(null, "requestedAttribute", false, 0, INFO_PLACEHOLDER_ATTR.get(), INFO_LDAPSEARCH_ARG_DESCRIPTION_REQUESTED_ATTR.get()); requestedAttribute.addLongIdentifier("requested-attribute", true); requestedAttribute.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); parser.addArgument(requestedAttribute); filter = new FilterArgument(null, "filter", false, 0, INFO_PLACEHOLDER_FILTER.get(), INFO_LDAPSEARCH_ARG_DESCRIPTION_FILTER.get()); filter.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); parser.addArgument(filter); filterFile = new FileArgument('f', "filterFile", false, 0, null, INFO_LDAPSEARCH_ARG_DESCRIPTION_FILTER_FILE.get(), true, true, true, false); filterFile.addLongIdentifier("filename", true); filterFile.addLongIdentifier("filter-file", true); filterFile.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); parser.addArgument(filterFile); ldapURLFile = new FileArgument(null, "ldapURLFile", false, 0, null, INFO_LDAPSEARCH_ARG_DESCRIPTION_LDAP_URL_FILE.get(), true, true, true, false); ldapURLFile.addLongIdentifier("ldap-url-file", true); ldapURLFile.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); parser.addArgument(ldapURLFile); followReferrals = new BooleanArgument(null, "followReferrals", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_FOLLOW_REFERRALS.get()); followReferrals.addLongIdentifier("follow-referrals", true); followReferrals.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); parser.addArgument(followReferrals); retryFailedOperations = new BooleanArgument(null, "retryFailedOperations", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_RETRY_FAILED_OPERATIONS.get()); retryFailedOperations.addLongIdentifier("retry-failed-operations", true); retryFailedOperations.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); parser.addArgument(retryFailedOperations); continueOnError = new BooleanArgument('c', "continueOnError", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_CONTINUE_ON_ERROR.get()); continueOnError.addLongIdentifier("continue-on-error", true); continueOnError.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); parser.addArgument(continueOnError); ratePerSecond = new IntegerArgument('r', "ratePerSecond", false, 1, INFO_PLACEHOLDER_NUM.get(), INFO_LDAPSEARCH_ARG_DESCRIPTION_RATE_PER_SECOND.get(), 1, Integer.MAX_VALUE); ratePerSecond.addLongIdentifier("rate-per-second", true); ratePerSecond.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); parser.addArgument(ratePerSecond); useAdministrativeSession = new BooleanArgument(null, "useAdministrativeSession", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_USE_ADMIN_SESSION.get()); useAdministrativeSession.addLongIdentifier("use-administrative-session", true); useAdministrativeSession.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); parser.addArgument(useAdministrativeSession); dryRun = new BooleanArgument('n', "dryRun", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_DRY_RUN.get()); dryRun.addLongIdentifier("dry-run", true); dryRun.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); parser.addArgument(dryRun); wrapColumn = new IntegerArgument(null, "wrapColumn", false, 1, null, INFO_LDAPSEARCH_ARG_DESCRIPTION_WRAP_COLUMN.get(), 0, Integer.MAX_VALUE); wrapColumn.addLongIdentifier("wrap-column", true); wrapColumn.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); parser.addArgument(wrapColumn); dontWrap = new BooleanArgument('T', "dontWrap", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_DONT_WRAP.get()); dontWrap.addLongIdentifier("doNotWrap", true); dontWrap.addLongIdentifier("dont-wrap", true); dontWrap.addLongIdentifier("do-not-wrap", true); dontWrap.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); parser.addArgument(dontWrap); suppressBase64EncodedValueComments = new BooleanArgument(null, "suppressBase64EncodedValueComments", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_SUPPRESS_BASE64_COMMENTS.get()); suppressBase64EncodedValueComments.addLongIdentifier( "suppress-base64-encoded-value-comments", true); suppressBase64EncodedValueComments.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); parser.addArgument(suppressBase64EncodedValueComments); countEntries = new BooleanArgument(null, "countEntries", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_COUNT_ENTRIES.get()); countEntries.addLongIdentifier("count-entries", true); countEntries.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_OPS.get()); countEntries.setHidden(true); parser.addArgument(countEntries); outputFile = new FileArgument(null, "outputFile", false, 1, null, INFO_LDAPSEARCH_ARG_DESCRIPTION_OUTPUT_FILE.get(), false, true, true, false); outputFile.addLongIdentifier("output-file", true); outputFile.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); parser.addArgument(outputFile); compressOutput = new BooleanArgument(null, "compressOutput", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_COMPRESS_OUTPUT.get()); compressOutput.addLongIdentifier("compress-output", true); compressOutput.addLongIdentifier("compress", true); compressOutput.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); parser.addArgument(compressOutput); encryptOutput = new BooleanArgument(null, "encryptOutput", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_ENCRYPT_OUTPUT.get()); encryptOutput.addLongIdentifier("encrypt-output", true); encryptOutput.addLongIdentifier("encrypt", true); encryptOutput.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); parser.addArgument(encryptOutput); encryptionPassphraseFile = new FileArgument(null, "encryptionPassphraseFile", false, 1, null, INFO_LDAPSEARCH_ARG_DESCRIPTION_ENCRYPTION_PW_FILE.get(), true, true, true, false); encryptionPassphraseFile.addLongIdentifier("encryption-passphrase-file", true); encryptionPassphraseFile.addLongIdentifier("encryptionPasswordFile", true); encryptionPassphraseFile.addLongIdentifier("encryption-password-file", true); encryptionPassphraseFile.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); parser.addArgument(encryptionPassphraseFile); separateOutputFilePerSearch = new BooleanArgument(null, "separateOutputFilePerSearch", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_SEPARATE_OUTPUT_FILES.get()); separateOutputFilePerSearch.addLongIdentifier( "separate-output-file-per-search", true); separateOutputFilePerSearch.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); parser.addArgument(separateOutputFilePerSearch); teeResultsToStandardOut = new BooleanArgument(null, "teeResultsToStandardOut", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_TEE.get("outputFile")); teeResultsToStandardOut.addLongIdentifier( "tee-results-to-standard-out", true); teeResultsToStandardOut.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); parser.addArgument(teeResultsToStandardOut); final Set outputFormatAllowedValues = StaticUtils.setOf("ldif", "json", "csv", "multi-valued-csv", "tab-delimited", "multi-valued-tab-delimited", "dns-only", "values-only"); outputFormat = new StringArgument(null, "outputFormat", false, 1, "{ldif|json|csv|multi-valued-csv|tab-delimited|" + "multi-valued-tab-delimited|dns-only|values-only}", INFO_LDAPSEARCH_ARG_DESCRIPTION_OUTPUT_FORMAT.get( requestedAttribute.getIdentifierString(), ldapURLFile.getIdentifierString()), outputFormatAllowedValues, "ldif"); outputFormat.addLongIdentifier("output-format", true); outputFormat.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); parser.addArgument(outputFormat); requireMatch = new BooleanArgument(null, "requireMatch", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_REQUIRE_MATCH.get( getToolName(), String.valueOf(ResultCode.NO_RESULTS_RETURNED))); requireMatch.addLongIdentifier("require-match", true); requireMatch.addLongIdentifier("requireMatchingEntry", true); requireMatch.addLongIdentifier("require-matching-entry", true); requireMatch.addLongIdentifier("requireMatchingEntries", true); requireMatch.addLongIdentifier("require-matching-entries", true); requireMatch.addLongIdentifier("requireEntry", true); requireMatch.addLongIdentifier("require-entry", true); requireMatch.addLongIdentifier("requireEntries", true); requireMatch.addLongIdentifier("require-entries", true); requireMatch.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); parser.addArgument(requireMatch); terse = new BooleanArgument(null, "terse", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_TERSE.get()); terse.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); parser.addArgument(terse); verbose = new BooleanArgument('v', "verbose", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_VERBOSE.get()); verbose.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_DATA.get()); parser.addArgument(verbose); bindControl = new ControlArgument(null, "bindControl", false, 0, null, INFO_LDAPSEARCH_ARG_DESCRIPTION_BIND_CONTROL.get()); bindControl.addLongIdentifier("bind-control", true); bindControl.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(bindControl); searchControl = new ControlArgument('J', "control", false, 0, null, INFO_LDAPSEARCH_ARG_DESCRIPTION_SEARCH_CONTROL.get()); searchControl.addLongIdentifier("searchControl", true); searchControl.addLongIdentifier("search-control", true); searchControl.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(searchControl); accessLogField = new StringArgument(null, "accessLogField", false, 0, INFO_LDAPSEARCH_ARG_PLACEHOLDER_NAME_VALUE.get(), INFO_LDAPSEARCH_ARG_DESCRIPTION_ACCESS_LOG_FIELD.get()); accessLogField.addLongIdentifier("access-log-field", true); accessLogField.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(accessLogField); accountUsable = new BooleanArgument(null, "accountUsable", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_ACCOUNT_USABLE.get()); accountUsable.addLongIdentifier("account-usable", true); accountUsable.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(accountUsable); authorizationIdentity = new BooleanArgument('E', "authorizationIdentity", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_AUTHZ_IDENTITY.get()); authorizationIdentity.addLongIdentifier("reportAuthzID", true); authorizationIdentity.addLongIdentifier("authorization-identity", true); authorizationIdentity.addLongIdentifier("report-authzid", true); authorizationIdentity.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(authorizationIdentity); assertionFilter = new FilterArgument(null, "assertionFilter", false, 1, INFO_PLACEHOLDER_FILTER.get(), INFO_LDAPSEARCH_ARG_DESCRIPTION_ASSERTION_FILTER.get()); assertionFilter.addLongIdentifier("assertion-filter", true); assertionFilter.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(assertionFilter); excludeBranch = new DNArgument(null, "excludeBranch", false, 0, null, INFO_LDAPSEARCH_ARG_DESCRIPTION_EXCLUDE_BRANCH.get()); excludeBranch.addLongIdentifier("exclude-branch", true); excludeBranch.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(excludeBranch); generateAccessToken = new BooleanArgument(null, "generateAccessToken", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_GENERATE_ACCESS_TOKEN.get()); generateAccessToken.addLongIdentifier("generate-access-token", true); generateAccessToken.addLongIdentifier("requestAccessToken", true); generateAccessToken.addLongIdentifier("request-access-token", true); generateAccessToken.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(generateAccessToken); getAuthorizationEntryAttribute = new StringArgument(null, "getAuthorizationEntryAttribute", false, 0, INFO_PLACEHOLDER_ATTR.get(), INFO_LDAPSEARCH_ARG_DESCRIPTION_GET_AUTHZ_ENTRY_ATTR.get()); getAuthorizationEntryAttribute.addLongIdentifier( "get-authorization-entry-attribute", true); getAuthorizationEntryAttribute.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(getAuthorizationEntryAttribute); getBackendSetID = new BooleanArgument(null, "getBackendSetID", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_GET_BACKEND_SET_ID.get()); getBackendSetID.addLongIdentifier("get-backend-set-id", true); getBackendSetID.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(getBackendSetID); getEffectiveRightsAuthzID = new StringArgument('g', "getEffectiveRightsAuthzID", false, 1, INFO_PLACEHOLDER_AUTHZID.get(), INFO_LDAPSEARCH_ARG_DESCRIPTION_GET_EFFECTIVE_RIGHTS_AUTHZID.get( "getEffectiveRightsAttribute")); getEffectiveRightsAuthzID.addLongIdentifier( "get-effective-rights-authzid", true); getEffectiveRightsAuthzID.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(getEffectiveRightsAuthzID); getEffectiveRightsAttribute = new StringArgument('e', "getEffectiveRightsAttribute", false, 0, INFO_PLACEHOLDER_ATTR.get(), INFO_LDAPSEARCH_ARG_DESCRIPTION_GET_EFFECTIVE_RIGHTS_ATTR.get()); getEffectiveRightsAttribute.addLongIdentifier( "get-effective-rights-attribute", true); getEffectiveRightsAttribute.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(getEffectiveRightsAttribute); getRecentLoginHistory = new BooleanArgument(null, "getRecentLoginHistory", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_GET_RECENT_LOGIN_HISTORY.get()); getRecentLoginHistory.addLongIdentifier("get-recent-login-history", true); getRecentLoginHistory.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(getRecentLoginHistory); getServerID = new BooleanArgument(null, "getServerID", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_GET_SERVER_ID.get()); getServerID.addLongIdentifier("get-server-id", true); getServerID.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(getServerID); getUserResourceLimits = new BooleanArgument(null, "getUserResourceLimits", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_GET_USER_RESOURCE_LIMITS.get()); getUserResourceLimits.addLongIdentifier("get-user-resource-limits", true); getUserResourceLimits.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(getUserResourceLimits); includeReplicationConflictEntries = new BooleanArgument(null, "includeReplicationConflictEntries", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_INCLUDE_REPL_CONFLICTS.get()); includeReplicationConflictEntries.addLongIdentifier( "include-replication-conflict-entries", true); includeReplicationConflictEntries.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(includeReplicationConflictEntries); final Set softDeleteAllowedValues = StaticUtils.setOf( "with-non-deleted-entries", "without-non-deleted-entries", "deleted-entries-in-undeleted-form"); includeSoftDeletedEntries = new StringArgument(null, "includeSoftDeletedEntries", false, 1, "{with-non-deleted-entries|without-non-deleted-entries|" + "deleted-entries-in-undeleted-form}", INFO_LDAPSEARCH_ARG_DESCRIPTION_INCLUDE_SOFT_DELETED.get(), softDeleteAllowedValues); includeSoftDeletedEntries.addLongIdentifier( "include-soft-deleted-entries", true); includeSoftDeletedEntries.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(includeSoftDeletedEntries); draftLDUPSubentries = new BooleanArgument(null, "draftLDUPSubentries", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_INCLUDE_DRAFT_LDUP_SUBENTRIES.get()); draftLDUPSubentries.addLongIdentifier("draftIETFLDUPSubentries", true); draftLDUPSubentries.addLongIdentifier("includeSubentries", true); draftLDUPSubentries.addLongIdentifier("includeLDAPSubentries", true); draftLDUPSubentries.addLongIdentifier("draft-ldup-subentries", true); draftLDUPSubentries.addLongIdentifier("draft-ietf-ldup-subentries", true); draftLDUPSubentries.addLongIdentifier("include-subentries", true); draftLDUPSubentries.addLongIdentifier("include-ldap-subentries", true); draftLDUPSubentries.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(draftLDUPSubentries); rfc3672Subentries = new BooleanValueArgument(null, "rfc3672Subentries", false, INFO_LDAPSEARCH_ARG_PLACEHOLDER_INCLUDE_RFC_3672_SUBENTRIES.get(), INFO_LDAPSEARCH_ARG_DESCRIPTION_INCLUDE_RFC_3672_SUBENTRIES.get()); rfc3672Subentries.addLongIdentifier("rfc-3672-subentries", true); rfc3672Subentries.addLongIdentifier("rfc3672-subentries", true); rfc3672Subentries.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(rfc3672Subentries); joinRule = new StringArgument(null, "joinRule", false, 1, "{dn:sourceAttr|reverse-dn:targetAttr|equals:sourceAttr:targetAttr|" + "contains:sourceAttr:targetAttr }", INFO_LDAPSEARCH_ARG_DESCRIPTION_JOIN_RULE.get()); joinRule.addLongIdentifier("join-rule", true); joinRule.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(joinRule); joinBaseDN = new StringArgument(null, "joinBaseDN", false, 1, "{search-base|source-entry-dn|{dn}}", INFO_LDAPSEARCH_ARG_DESCRIPTION_JOIN_BASE_DN.get()); joinBaseDN.addLongIdentifier("join-base-dn", true); joinBaseDN.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(joinBaseDN); joinScope = new ScopeArgument(null, "joinScope", false, null, INFO_LDAPSEARCH_ARG_DESCRIPTION_JOIN_SCOPE.get()); joinScope.addLongIdentifier("join-scope", true); joinScope.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(joinScope); joinSizeLimit = new IntegerArgument(null, "joinSizeLimit", false, 1, INFO_PLACEHOLDER_NUM.get(), INFO_LDAPSEARCH_ARG_DESCRIPTION_JOIN_SIZE_LIMIT.get(), 0, Integer.MAX_VALUE); joinSizeLimit.addLongIdentifier("join-size-limit", true); joinSizeLimit.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(joinSizeLimit); joinFilter = new FilterArgument(null, "joinFilter", false, 1, null, INFO_LDAPSEARCH_ARG_DESCRIPTION_JOIN_FILTER.get()); joinFilter.addLongIdentifier("join-filter", true); joinFilter.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(joinFilter); joinRequestedAttribute = new StringArgument(null, "joinRequestedAttribute", false, 0, INFO_PLACEHOLDER_ATTR.get(), INFO_LDAPSEARCH_ARG_DESCRIPTION_JOIN_ATTR.get()); joinRequestedAttribute.addLongIdentifier("join-requested-attribute", true); joinRequestedAttribute.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(joinRequestedAttribute); joinRequireMatch = new BooleanArgument(null, "joinRequireMatch", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_JOIN_REQUIRE_MATCH.get()); joinRequireMatch.addLongIdentifier("join-require-match", true); joinRequireMatch.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(joinRequireMatch); manageDsaIT = new BooleanArgument(null, "manageDsaIT", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_MANAGE_DSA_IT.get()); manageDsaIT.addLongIdentifier("manage-dsa-it", true); manageDsaIT.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(manageDsaIT); matchedValuesFilter = new FilterArgument(null, "matchedValuesFilter", false, 0, INFO_PLACEHOLDER_FILTER.get(), INFO_LDAPSEARCH_ARG_DESCRIPTION_MATCHED_VALUES_FILTER.get()); matchedValuesFilter.addLongIdentifier("matched-values-filter", true); matchedValuesFilter.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(matchedValuesFilter); matchingEntryCountControl = new StringArgument(null, "matchingEntryCountControl", false, 1, "{examineCount=NNN[:alwaysExamine][:allowUnindexed]" + "[:skipResolvingExplodedIndexes]" + "[:fastShortCircuitThreshold=NNN]" + "[:slowShortCircuitThreshold=NNN][:extendedResponseData]" + "[:debug]}", INFO_LDAPSEARCH_ARG_DESCRIPTION_MATCHING_ENTRY_COUNT_CONTROL.get()); matchingEntryCountControl.addLongIdentifier("matchingEntryCount", true); matchingEntryCountControl.addLongIdentifier( "matching-entry-count-control", true); matchingEntryCountControl.addLongIdentifier("matching-entry-count", true); matchingEntryCountControl.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(matchingEntryCountControl); operationPurpose = new StringArgument(null, "operationPurpose", false, 1, INFO_PLACEHOLDER_PURPOSE.get(), INFO_LDAPSEARCH_ARG_DESCRIPTION_OPERATION_PURPOSE.get()); operationPurpose.addLongIdentifier("operation-purpose", true); operationPurpose.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(operationPurpose); overrideSearchLimit = new StringArgument(null, "overrideSearchLimit", false, 0, INFO_LDAPSEARCH_NAME_VALUE_PLACEHOLDER.get(), INFO_LDAPSEARCH_ARG_DESCRIPTION_OVERRIDE_SEARCH_LIMIT.get()); overrideSearchLimit.addLongIdentifier("overrideSearchLimits", true); overrideSearchLimit.addLongIdentifier("override-search-limit", true); overrideSearchLimit.addLongIdentifier("override-search-limits", true); overrideSearchLimit.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(overrideSearchLimit); persistentSearch = new StringArgument('C', "persistentSearch", false, 1, "ps[:changetype[:changesonly[:entrychgcontrols]]]", INFO_LDAPSEARCH_ARG_DESCRIPTION_PERSISTENT_SEARCH.get()); persistentSearch.addLongIdentifier("persistent-search", true); persistentSearch.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(persistentSearch); permitUnindexedSearch = new BooleanArgument(null, "permitUnindexedSearch", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_PERMIT_UNINDEXED_SEARCH.get()); permitUnindexedSearch.addLongIdentifier("permitUnindexedSearches", true); permitUnindexedSearch.addLongIdentifier("permitUnindexed", true); permitUnindexedSearch.addLongIdentifier("permitIfUnindexed", true); permitUnindexedSearch.addLongIdentifier("permit-unindexed-search", true); permitUnindexedSearch.addLongIdentifier("permit-unindexed-searches", true); permitUnindexedSearch.addLongIdentifier("permit-unindexed", true); permitUnindexedSearch.addLongIdentifier("permit-if-unindexed", true); permitUnindexedSearch.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(permitUnindexedSearch); proxyAs = new StringArgument('Y', "proxyAs", false, 1, INFO_PLACEHOLDER_AUTHZID.get(), INFO_LDAPSEARCH_ARG_DESCRIPTION_PROXY_AS.get()); proxyAs.addLongIdentifier("proxy-as", true); proxyAs.addLongIdentifier("proxyV2As", true); proxyAs.addLongIdentifier("proxy-v2-as", true); proxyAs.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(proxyAs); proxyV1As = new DNArgument(null, "proxyV1As", false, 1, null, INFO_LDAPSEARCH_ARG_DESCRIPTION_PROXY_V1_AS.get()); proxyV1As.addLongIdentifier("proxy-v1-as", true); proxyV1As.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(proxyV1As); rejectUnindexedSearch = new BooleanArgument(null, "rejectUnindexedSearch", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_REJECT_UNINDEXED_SEARCH.get()); rejectUnindexedSearch.addLongIdentifier("rejectUnindexedSearches", true); rejectUnindexedSearch.addLongIdentifier("rejectUnindexed", true); rejectUnindexedSearch.addLongIdentifier("rejectIfUnindexed", true); rejectUnindexedSearch.addLongIdentifier("reject-unindexed-search", true); rejectUnindexedSearch.addLongIdentifier("reject-unindexed-searches", true); rejectUnindexedSearch.addLongIdentifier("reject-unindexed", true); rejectUnindexedSearch.addLongIdentifier("reject-if-unindexed", true); rejectUnindexedSearch.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(rejectUnindexedSearch); routeToBackendSet = new StringArgument(null, "routeToBackendSet", false, 0, INFO_LDAPSEARCH_ARG_PLACEHOLDER_ROUTE_TO_BACKEND_SET.get(), INFO_LDAPSEARCH_ARG_DESCRIPTION_ROUTE_TO_BACKEND_SET.get()); routeToBackendSet.addLongIdentifier("route-to-backend-set", true); routeToBackendSet.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(routeToBackendSet); routeToServer = new StringArgument(null, "routeToServer", false, 1, INFO_LDAPSEARCH_ARG_PLACEHOLDER_ROUTE_TO_SERVER.get(), INFO_LDAPSEARCH_ARG_DESCRIPTION_ROUTE_TO_SERVER.get()); routeToServer.addLongIdentifier("route-to-server", true); routeToServer.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(routeToServer); final Set suppressOperationalAttributeUpdatesAllowedValues = StaticUtils.setOf("last-access-time", "last-login-time", "last-login-ip", "lastmod"); suppressOperationalAttributeUpdates = new StringArgument(null, "suppressOperationalAttributeUpdates", false, -1, INFO_PLACEHOLDER_ATTR.get(), INFO_LDAPSEARCH_ARG_DESCRIPTION_SUPPRESS_OP_ATTR_UPDATES.get(), suppressOperationalAttributeUpdatesAllowedValues); suppressOperationalAttributeUpdates.addLongIdentifier( "suppress-operational-attribute-updates", true); suppressOperationalAttributeUpdates.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(suppressOperationalAttributeUpdates); usePasswordPolicyControl = new BooleanArgument(null, "usePasswordPolicyControl", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_PASSWORD_POLICY.get()); usePasswordPolicyControl.addLongIdentifier("use-password-policy-control", true); usePasswordPolicyControl.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(usePasswordPolicyControl); realAttributesOnly = new BooleanArgument(null, "realAttributesOnly", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_REAL_ATTRS_ONLY.get()); realAttributesOnly.addLongIdentifier("real-attributes-only", true); realAttributesOnly.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(realAttributesOnly); sortOrder = new StringArgument('S', "sortOrder", false, 1, null, INFO_LDAPSEARCH_ARG_DESCRIPTION_SORT_ORDER.get()); sortOrder.addLongIdentifier("sort-order", true); sortOrder.setArgumentGroupName(INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(sortOrder); simplePageSize = new IntegerArgument(null, "simplePageSize", false, 1, null, INFO_LDAPSEARCH_ARG_DESCRIPTION_PAGE_SIZE.get(), 1, Integer.MAX_VALUE); simplePageSize.addLongIdentifier("simple-page-size", true); simplePageSize.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(simplePageSize); virtualAttributesOnly = new BooleanArgument(null, "virtualAttributesOnly", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_VIRTUAL_ATTRS_ONLY.get()); virtualAttributesOnly.addLongIdentifier("virtual-attributes-only", true); virtualAttributesOnly.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(virtualAttributesOnly); virtualListView = new StringArgument('G', "virtualListView", false, 1, "{before:after:index:count | before:after:value}", INFO_LDAPSEARCH_ARG_DESCRIPTION_VLV.get("sortOrder")); virtualListView.addLongIdentifier("vlv", true); virtualListView.addLongIdentifier("virtual-list-view", true); virtualListView.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(virtualListView); useJSONFormattedRequestControls = new BooleanArgument(null, "useJSONFormattedRequestControls", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_USE_JSON_FORMATTED_CONTROLS.get()); useJSONFormattedRequestControls.addLongIdentifier( "use-json-formatted-request-controls", true); useJSONFormattedRequestControls.addLongIdentifier( "useJSONFormattedControls", true); useJSONFormattedRequestControls.addLongIdentifier( "use-json-formatted-controls", true); useJSONFormattedRequestControls.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); parser.addArgument(useJSONFormattedRequestControls); excludeAttribute = new StringArgument(null, "excludeAttribute", false, 0, INFO_PLACEHOLDER_ATTR.get(), INFO_LDAPSEARCH_ARG_DESCRIPTION_EXCLUDE_ATTRIBUTE.get()); excludeAttribute.addLongIdentifier("exclude-attribute", true); excludeAttribute.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_TRANSFORMATIONS.get()); parser.addArgument(excludeAttribute); redactAttribute = new StringArgument(null, "redactAttribute", false, 0, INFO_PLACEHOLDER_ATTR.get(), INFO_LDAPSEARCH_ARG_DESCRIPTION_REDACT_ATTRIBUTE.get()); redactAttribute.addLongIdentifier("redact-attribute", true); redactAttribute.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_TRANSFORMATIONS.get()); parser.addArgument(redactAttribute); hideRedactedValueCount = new BooleanArgument(null, "hideRedactedValueCount", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_HIDE_REDACTED_VALUE_COUNT.get()); hideRedactedValueCount.addLongIdentifier("hide-redacted-value-count", true); hideRedactedValueCount.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_TRANSFORMATIONS.get()); parser.addArgument(hideRedactedValueCount); scrambleAttribute = new StringArgument(null, "scrambleAttribute", false, 0, INFO_PLACEHOLDER_ATTR.get(), INFO_LDAPSEARCH_ARG_DESCRIPTION_SCRAMBLE_ATTRIBUTE.get()); scrambleAttribute.addLongIdentifier("scramble-attribute", true); scrambleAttribute.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_TRANSFORMATIONS.get()); parser.addArgument(scrambleAttribute); scrambleJSONField = new StringArgument(null, "scrambleJSONField", false, 0, INFO_PLACEHOLDER_FIELD_NAME.get(), INFO_LDAPSEARCH_ARG_DESCRIPTION_SCRAMBLE_JSON_FIELD.get()); scrambleJSONField.addLongIdentifier("scramble-json-field", true); scrambleJSONField.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_TRANSFORMATIONS.get()); parser.addArgument(scrambleJSONField); scrambleRandomSeed = new IntegerArgument(null, "scrambleRandomSeed", false, 1, null, INFO_LDAPSEARCH_ARG_DESCRIPTION_SCRAMBLE_RANDOM_SEED.get()); scrambleRandomSeed.addLongIdentifier("scramble-random-seed", true); scrambleRandomSeed.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_TRANSFORMATIONS.get()); parser.addArgument(scrambleRandomSeed); renameAttributeFrom = new StringArgument(null, "renameAttributeFrom", false, 0, INFO_PLACEHOLDER_ATTR.get(), INFO_LDAPSEARCH_ARG_DESCRIPTION_RENAME_ATTRIBUTE_FROM.get()); renameAttributeFrom.addLongIdentifier("rename-attribute-from", true); renameAttributeFrom.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_TRANSFORMATIONS.get()); parser.addArgument(renameAttributeFrom); renameAttributeTo = new StringArgument(null, "renameAttributeTo", false, 0, INFO_PLACEHOLDER_ATTR.get(), INFO_LDAPSEARCH_ARG_DESCRIPTION_RENAME_ATTRIBUTE_TO.get()); renameAttributeTo.addLongIdentifier("rename-attribute-to", true); renameAttributeTo.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_TRANSFORMATIONS.get()); parser.addArgument(renameAttributeTo); moveSubtreeFrom = new DNArgument(null, "moveSubtreeFrom", false, 0, INFO_PLACEHOLDER_ATTR.get(), INFO_LDAPSEARCH_ARG_DESCRIPTION_MOVE_SUBTREE_FROM.get()); moveSubtreeFrom.addLongIdentifier("move-subtree-from", true); moveSubtreeFrom.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_TRANSFORMATIONS.get()); parser.addArgument(moveSubtreeFrom); moveSubtreeTo = new DNArgument(null, "moveSubtreeTo", false, 0, INFO_PLACEHOLDER_ATTR.get(), INFO_LDAPSEARCH_ARG_DESCRIPTION_MOVE_SUBTREE_TO.get()); moveSubtreeTo.addLongIdentifier("move-subtree-to", true); moveSubtreeTo.setArgumentGroupName( INFO_LDAPSEARCH_ARG_GROUP_TRANSFORMATIONS.get()); parser.addArgument(moveSubtreeTo); // The "--scriptFriendly" argument is provided for compatibility with legacy // ldapsearch tools, but is not actually used by this tool. final BooleanArgument scriptFriendly = new BooleanArgument(null, "scriptFriendly", 1, INFO_LDAPSEARCH_ARG_DESCRIPTION_SCRIPT_FRIENDLY.get()); scriptFriendly.addLongIdentifier("script-friendly", true); scriptFriendly.setHidden(true); parser.addArgument(scriptFriendly); // The "-V" / "--ldapVersion" argument is provided for compatibility with // legacy ldapsearch tools, but is not actually used by this tool. final IntegerArgument ldapVersion = new IntegerArgument('V', "ldapVersion", false, 1, null, INFO_LDAPSEARCH_ARG_DESCRIPTION_LDAP_VERSION.get()); ldapVersion.addLongIdentifier("ldap-version", true); ldapVersion.setHidden(true); parser.addArgument(ldapVersion); // The baseDN and ldapURLFile arguments can't be used together. parser.addExclusiveArgumentSet(baseDN, ldapURLFile); // The scope and ldapURLFile arguments can't be used together. parser.addExclusiveArgumentSet(scope, ldapURLFile); // The requestedAttribute and ldapURLFile arguments can't be used together. parser.addExclusiveArgumentSet(requestedAttribute, ldapURLFile); // The filter and ldapURLFile arguments can't be used together. parser.addExclusiveArgumentSet(filter, ldapURLFile); // The filterFile and ldapURLFile arguments can't be used together. parser.addExclusiveArgumentSet(filterFile, ldapURLFile); // The followReferrals and manageDsaIT arguments can't be used together. parser.addExclusiveArgumentSet(followReferrals, manageDsaIT); // The persistent search argument can't be used with either the filterFile // or ldapURLFile arguments. parser.addExclusiveArgumentSet(persistentSearch, filterFile); parser.addExclusiveArgumentSet(persistentSearch, ldapURLFile); // The draft-ietf-ldup-subentry and RFC 3672 subentries controls cannot be // used together. parser.addExclusiveArgumentSet(draftLDUPSubentries, rfc3672Subentries); // The realAttributesOnly and virtualAttributesOnly arguments can't be used // together. parser.addExclusiveArgumentSet(realAttributesOnly, virtualAttributesOnly); // The simplePageSize and virtualListView arguments can't be used together. parser.addExclusiveArgumentSet(simplePageSize, virtualListView); // The terse and verbose arguments can't be used together. parser.addExclusiveArgumentSet(terse, verbose); // The getEffectiveRightsAttribute argument requires the // getEffectiveRightsAuthzID argument. parser.addDependentArgumentSet(getEffectiveRightsAttribute, getEffectiveRightsAuthzID); // The virtualListView argument requires the sortOrder argument. parser.addDependentArgumentSet(virtualListView, sortOrder); // The rejectUnindexedSearch and permitUnindexedSearch arguments can't be // used together. parser.addExclusiveArgumentSet(rejectUnindexedSearch, permitUnindexedSearch); // The separateOutputFilePerSearch argument requires the outputFile // argument. It also requires either the filter, filterFile or ldapURLFile // argument. parser.addDependentArgumentSet(separateOutputFilePerSearch, outputFile); parser.addDependentArgumentSet(separateOutputFilePerSearch, filter, filterFile, ldapURLFile); // The teeResultsToStandardOut argument requires the outputFile argument. parser.addDependentArgumentSet(teeResultsToStandardOut, outputFile); // The wrapColumn and dontWrap arguments must not be used together. parser.addExclusiveArgumentSet(wrapColumn, dontWrap); // All arguments that specifically pertain to join processing can only be // used if the joinRule argument is provided. parser.addDependentArgumentSet(joinBaseDN, joinRule); parser.addDependentArgumentSet(joinScope, joinRule); parser.addDependentArgumentSet(joinSizeLimit, joinRule); parser.addDependentArgumentSet(joinFilter, joinRule); parser.addDependentArgumentSet(joinRequestedAttribute, joinRule); parser.addDependentArgumentSet(joinRequireMatch, joinRule); // The countEntries argument must not be used in conjunction with the // filter, filterFile, LDAPURLFile, or persistentSearch arguments. parser.addExclusiveArgumentSet(countEntries, filter); parser.addExclusiveArgumentSet(countEntries, filterFile); parser.addExclusiveArgumentSet(countEntries, ldapURLFile); parser.addExclusiveArgumentSet(countEntries, persistentSearch); // The hideRedactedValueCount argument requires the redactAttribute // argument. parser.addDependentArgumentSet(hideRedactedValueCount, redactAttribute); // The scrambleJSONField and scrambleRandomSeed arguments require the // scrambleAttribute argument. parser.addDependentArgumentSet(scrambleJSONField, scrambleAttribute); parser.addDependentArgumentSet(scrambleRandomSeed, scrambleAttribute); // The renameAttributeFrom and renameAttributeTo arguments must be provided // together. parser.addDependentArgumentSet(renameAttributeFrom, renameAttributeTo); parser.addDependentArgumentSet(renameAttributeTo, renameAttributeFrom); // The moveSubtreeFrom and moveSubtreeTo arguments must be provided // together. parser.addDependentArgumentSet(moveSubtreeFrom, moveSubtreeTo); parser.addDependentArgumentSet(moveSubtreeTo, moveSubtreeFrom); // The compressOutput argument can only be used if an output file is // specified and results aren't going to be teed. parser.addDependentArgumentSet(compressOutput, outputFile); parser.addExclusiveArgumentSet(compressOutput, teeResultsToStandardOut); // The encryptOutput argument can only be used if an output file is // specified and results aren't going to be teed. parser.addDependentArgumentSet(encryptOutput, outputFile); parser.addExclusiveArgumentSet(encryptOutput, teeResultsToStandardOut); // The encryptionPassphraseFile argument can only be used if the // encryptOutput argument is also provided. parser.addDependentArgumentSet(encryptionPassphraseFile, encryptOutput); } /** * {@inheritDoc} */ @Override() @NotNull() protected List getBindControls() { final ArrayList bindControls = new ArrayList<>(10); if (bindControl.isPresent()) { bindControls.addAll(bindControl.getValues()); } if (authorizationIdentity.isPresent()) { bindControls.add(new AuthorizationIdentityRequestControl(false)); } if (generateAccessToken.isPresent()) { bindControls.add(new GenerateAccessTokenRequestControl()); } if (getAuthorizationEntryAttribute.isPresent()) { bindControls.add(new GetAuthorizationEntryRequestControl(true, true, getAuthorizationEntryAttribute.getValues())); } if (getRecentLoginHistory.isPresent()) { bindControls.add(new GetRecentLoginHistoryRequestControl()); } if (getUserResourceLimits.isPresent()) { bindControls.add(new GetUserResourceLimitsRequestControl()); } if (usePasswordPolicyControl.isPresent()) { bindControls.add(new PasswordPolicyRequestControl()); } if (suppressOperationalAttributeUpdates.isPresent()) { final EnumSet suppressTypes = EnumSet.noneOf(SuppressType.class); for (final String s : suppressOperationalAttributeUpdates.getValues()) { if (s.equalsIgnoreCase("last-access-time")) { suppressTypes.add(SuppressType.LAST_ACCESS_TIME); } else if (s.equalsIgnoreCase("last-login-time")) { suppressTypes.add(SuppressType.LAST_LOGIN_TIME); } else if (s.equalsIgnoreCase("last-login-ip")) { suppressTypes.add(SuppressType.LAST_LOGIN_IP); } } bindControls.add(new SuppressOperationalAttributeUpdateRequestControl( suppressTypes)); } if (useJSONFormattedRequestControls.isPresent()) { final JSONFormattedRequestControl jsonFormattedRequestControl = JSONFormattedRequestControl.createWithControls(true, bindControls); bindControls.clear(); bindControls.add(jsonFormattedRequestControl); } return bindControls; } /** * {@inheritDoc} */ @Override() protected boolean supportsMultipleServers() { // We will support providing information about multiple servers. This tool // will not communicate with multiple servers concurrently, but it can // accept information about multiple servers in the event that multiple // searches are to be performed and a server goes down in the middle of // those searches. In this case, we can resume processing on a // newly-created connection, possibly to a different server. return true; } /** * {@inheritDoc} */ @Override() public void doExtendedNonLDAPArgumentValidation() throws ArgumentException { // If wrapColumn was provided, then use its value. Otherwise, if dontWrap // was provided, then use that. if (wrapColumn.isPresent()) { final int wc = wrapColumn.getValue(); if (wc <= 0) { WRAP_COLUMN = Integer.MAX_VALUE; } else { WRAP_COLUMN = wc; } } else if (dontWrap.isPresent()) { WRAP_COLUMN = Integer.MAX_VALUE; } // If the ldapURLFile argument was provided, then there must not be any // trailing arguments. final List trailingArgs = parser.getTrailingArguments(); if (ldapURLFile.isPresent()) { if (! trailingArgs.isEmpty()) { throw new ArgumentException( ERR_LDAPSEARCH_TRAILING_ARGS_WITH_URL_FILE.get( ldapURLFile.getIdentifierString())); } } // If the filter or filterFile argument was provided, then there may // optionally be trailing arguments, but the first trailing argument must // not be a filter. if (filter.isPresent() || filterFile.isPresent()) { if (! trailingArgs.isEmpty()) { try { Filter.create(trailingArgs.get(0)); throw new ArgumentException( ERR_LDAPSEARCH_TRAILING_FILTER_WITH_FILTER_FILE.get( filterFile.getIdentifierString())); } catch (final LDAPException le) { // This is the normal condition. Not even worth debugging the // exception. } } } // If none of the ldapURLFile, filter, or filterFile arguments was provided, // then there must be at least one trailing argument, and the first trailing // argument must be a valid search filter. if (! (ldapURLFile.isPresent() || filter.isPresent() || filterFile.isPresent())) { if (trailingArgs.isEmpty()) { throw new ArgumentException(ERR_LDAPSEARCH_NO_TRAILING_ARGS.get( filterFile.getIdentifierString(), ldapURLFile.getIdentifierString())); } try { Filter.create(trailingArgs.get(0)); } catch (final Exception e) { Debug.debugException(e); throw new ArgumentException( ERR_LDAPSEARCH_FIRST_TRAILING_ARG_NOT_FILTER.get( trailingArgs.get(0)), e); } } // There should never be a case in which a trailing argument starts with a // dash, and it's probably an attempt to use a named argument but that was // inadvertently put after the filter. Warn about the problem, but don't // fail. for (final String s : trailingArgs) { if (s.startsWith("-")) { commentToErr(WARN_LDAPSEARCH_TRAILING_ARG_STARTS_WITH_DASH.get(s)); break; } } // If any matched values filters are specified, then validate them and // pre-create the matched values request control. if (matchedValuesFilter.isPresent()) { final List filterList = matchedValuesFilter.getValues(); final MatchedValuesFilter[] matchedValuesFilters = new MatchedValuesFilter[filterList.size()]; for (int i=0; i < matchedValuesFilters.length; i++) { try { matchedValuesFilters[i] = MatchedValuesFilter.create(filterList.get(i)); } catch (final Exception e) { Debug.debugException(e); throw new ArgumentException( ERR_LDAPSEARCH_INVALID_MATCHED_VALUES_FILTER.get( filterList.get(i).toString()), e); } } matchedValuesRequestControl = new MatchedValuesRequestControl(true, matchedValuesFilters); } // If we should use the matching entry count request control, then validate // the argument value and pre-create the control. if (matchingEntryCountControl.isPresent()) { final MatchingEntryCountRequestControlProperties properties = new MatchingEntryCountRequestControlProperties(); Integer examineCount = null; try { for (final String element : matchingEntryCountControl.getValue().toLowerCase().split(":")) { if (element.startsWith("examinecount=")) { examineCount = Integer.parseInt(element.substring(13)); } else if (element.equals("allowunindexed")) { properties.setProcessSearchIfUnindexed(true); } else if (element.equals("alwaysexamine")) { properties.setAlwaysExamineCandidates(true); } else if (element.equals("skipresolvingexplodedindexes")) { properties.setSkipResolvingExplodedIndexes(true); } else if (element.startsWith("fastshortcircuitthreshold=")) { properties.setFastShortCircuitThreshold( Long.parseLong(element.substring(26))); } else if (element.startsWith("slowshortcircuitthreshold=")) { properties.setSlowShortCircuitThreshold( Long.parseLong(element.substring(26))); } else if (element.equals("extendedresponsedata")) { properties.setIncludeExtendedResponseData(true); } else if (element.equals("debug")) { properties.setIncludeDebugInfo(true); } else { throw new ArgumentException( ERR_LDAPSEARCH_MATCHING_ENTRY_COUNT_INVALID_VALUE.get( matchingEntryCountControl.getIdentifierString())); } } } catch (final ArgumentException ae) { Debug.debugException(ae); throw ae; } catch (final Exception e) { Debug.debugException(e); throw new ArgumentException( ERR_LDAPSEARCH_MATCHING_ENTRY_COUNT_INVALID_VALUE.get( matchingEntryCountControl.getIdentifierString()), e); } if (examineCount == null) { throw new ArgumentException( ERR_LDAPSEARCH_MATCHING_ENTRY_COUNT_INVALID_VALUE.get( matchingEntryCountControl.getIdentifierString())); } else { properties.setMaxCandidatesToExamine(examineCount); } matchingEntryCountRequestControl = new MatchingEntryCountRequestControl(true, properties); } // If we should include the override search limits request control, then // validate the provided values. if (overrideSearchLimit.isPresent()) { final LinkedHashMap properties = new LinkedHashMap<>(StaticUtils.computeMapCapacity(10)); for (final String value : overrideSearchLimit.getValues()) { final int equalPos = value.indexOf('='); if (equalPos < 0) { throw new ArgumentException( ERR_LDAPSEARCH_OVERRIDE_LIMIT_NO_EQUAL.get( overrideSearchLimit.getIdentifierString())); } else if (equalPos == 0) { throw new ArgumentException( ERR_LDAPSEARCH_OVERRIDE_LIMIT_EMPTY_PROPERTY_NAME.get( overrideSearchLimit.getIdentifierString())); } final String propertyName = value.substring(0, equalPos); if (properties.containsKey(propertyName)) { throw new ArgumentException( ERR_LDAPSEARCH_OVERRIDE_LIMIT_DUPLICATE_PROPERTY_NAME.get( overrideSearchLimit.getIdentifierString(), propertyName)); } if (equalPos == (value.length() - 1)) { throw new ArgumentException( ERR_LDAPSEARCH_OVERRIDE_LIMIT_EMPTY_PROPERTY_VALUE.get( overrideSearchLimit.getIdentifierString(), propertyName)); } properties.put(propertyName, value.substring(equalPos+1)); } overrideSearchLimitsRequestControl = new OverrideSearchLimitsRequestControl(properties, false); } // If we should use the persistent search request control, then validate // the argument value and pre-create the control. if (persistentSearch.isPresent()) { boolean changesOnly = true; boolean returnECs = true; EnumSet changeTypes = EnumSet.allOf(PersistentSearchChangeType.class); try { final String[] elements = persistentSearch.getValue().toLowerCase().split(":"); if (elements.length == 0) { throw new ArgumentException( ERR_LDAPSEARCH_PERSISTENT_SEARCH_INVALID_VALUE.get( persistentSearch.getIdentifierString())); } final String header = StaticUtils.toLowerCase(elements[0]); if (! (header.equals("ps") || header.equals("persist") || header.equals("persistent") || header.equals("psearch") || header.equals("persistentsearch"))) { throw new ArgumentException( ERR_LDAPSEARCH_PERSISTENT_SEARCH_INVALID_VALUE.get( persistentSearch.getIdentifierString())); } if (elements.length > 1) { final String ctString = StaticUtils.toLowerCase(elements[1]); if (ctString.equals("any")) { changeTypes = EnumSet.allOf(PersistentSearchChangeType.class); } else { changeTypes.clear(); for (final String t : ctString.split(",")) { if (t.equals("add")) { changeTypes.add(PersistentSearchChangeType.ADD); } else if (t.equals("del") || t.equals("delete")) { changeTypes.add(PersistentSearchChangeType.DELETE); } else if (t.equals("mod") || t.equals("modify")) { changeTypes.add(PersistentSearchChangeType.MODIFY); } else if (t.equals("moddn") || t.equals("modrdn") || t.equals("modifydn") || t.equals("modifyrdn")) { changeTypes.add(PersistentSearchChangeType.MODIFY_DN); } else { throw new ArgumentException( ERR_LDAPSEARCH_PERSISTENT_SEARCH_INVALID_VALUE.get( persistentSearch.getIdentifierString())); } } } } if (elements.length > 2) { if (elements[2].equalsIgnoreCase("true") || elements[2].equals("1")) { changesOnly = true; } else if (elements[2].equalsIgnoreCase("false") || elements[2].equals("0")) { changesOnly = false; } else { throw new ArgumentException( ERR_LDAPSEARCH_PERSISTENT_SEARCH_INVALID_VALUE.get( persistentSearch.getIdentifierString())); } } if (elements.length > 3) { if (elements[3].equalsIgnoreCase("true") || elements[3].equals("1")) { returnECs = true; } else if (elements[3].equalsIgnoreCase("false") || elements[3].equals("0")) { returnECs = false; } else { throw new ArgumentException( ERR_LDAPSEARCH_PERSISTENT_SEARCH_INVALID_VALUE.get( persistentSearch.getIdentifierString())); } } } catch (final ArgumentException ae) { Debug.debugException(ae); throw ae; } catch (final Exception e) { Debug.debugException(e); throw new ArgumentException( ERR_LDAPSEARCH_PERSISTENT_SEARCH_INVALID_VALUE.get( persistentSearch.getIdentifierString()), e); } persistentSearchRequestControl = new PersistentSearchRequestControl( changeTypes, changesOnly, returnECs, true); } // If we should use the server-side sort request control, then validate the // sort order and pre-create the control. if (sortOrder.isPresent()) { final ArrayList sortKeyList = new ArrayList<>(5); final StringTokenizer tokenizer = new StringTokenizer(sortOrder.getValue(), ", "); while (tokenizer.hasMoreTokens()) { final String token = tokenizer.nextToken(); final boolean ascending; String attributeName; if (token.startsWith("-")) { ascending = false; attributeName = token.substring(1); } else if (token.startsWith("+")) { ascending = true; attributeName = token.substring(1); } else { ascending = true; attributeName = token; } final String matchingRuleID; final int colonPos = attributeName.indexOf(':'); if (colonPos >= 0) { matchingRuleID = attributeName.substring(colonPos+1); attributeName = attributeName.substring(0, colonPos); } else { matchingRuleID = null; } final StringBuilder invalidReason = new StringBuilder(); if (! PersistUtils.isValidLDAPName(attributeName, false, invalidReason)) { throw new ArgumentException( ERR_LDAPSEARCH_SORT_ORDER_INVALID_VALUE.get( sortOrder.getIdentifierString())); } sortKeyList.add( new SortKey(attributeName, matchingRuleID, (! ascending))); } if (sortKeyList.isEmpty()) { throw new ArgumentException( ERR_LDAPSEARCH_SORT_ORDER_INVALID_VALUE.get( sortOrder.getIdentifierString())); } final SortKey[] sortKeyArray = new SortKey[sortKeyList.size()]; sortKeyList.toArray(sortKeyArray); sortRequestControl = new ServerSideSortRequestControl(sortKeyArray); } // If we should use the virtual list view request control, then validate the // argument value and pre-create the control. if (virtualListView.isPresent()) { try { final String[] elements = virtualListView.getValue().split(":"); if (elements.length == 4) { vlvRequestControl = new VirtualListViewRequestControl( Integer.parseInt(elements[2]), Integer.parseInt(elements[0]), Integer.parseInt(elements[1]), Integer.parseInt(elements[3]), null); } else if (elements.length == 3) { vlvRequestControl = new VirtualListViewRequestControl(elements[2], Integer.parseInt(elements[0]), Integer.parseInt(elements[1]), null); } else { throw new ArgumentException( ERR_LDAPSEARCH_VLV_INVALID_VALUE.get( virtualListView.getIdentifierString())); } } catch (final ArgumentException ae) { Debug.debugException(ae); throw ae; } catch (final Exception e) { Debug.debugException(e); throw new ArgumentException( ERR_LDAPSEARCH_VLV_INVALID_VALUE.get( virtualListView.getIdentifierString()), e); } } // If we should use the LDAP join request control, then validate and // pre-create that control. if (joinRule.isPresent()) { final JoinRule rule; try { final String[] elements = joinRule.getValue().toLowerCase().split(":"); final String ruleName = StaticUtils.toLowerCase(elements[0]); if (ruleName.equals("dn")) { rule = JoinRule.createDNJoin(elements[1]); } else if (ruleName.equals("reverse-dn") || ruleName.equals("reversedn")) { rule = JoinRule.createReverseDNJoin(elements[1]); } else if (ruleName.equals("equals") || ruleName.equals("equality")) { rule = JoinRule.createEqualityJoin(elements[1], elements[2], false); } else if (ruleName.equals("contains") || ruleName.equals("substring")) { rule = JoinRule.createContainsJoin(elements[1], elements[2], false); } else { throw new ArgumentException( ERR_LDAPSEARCH_JOIN_RULE_INVALID_VALUE.get( joinRule.getIdentifierString())); } } catch (final ArgumentException ae) { Debug.debugException(ae); throw ae; } catch (final Exception e) { Debug.debugException(e); throw new ArgumentException( ERR_LDAPSEARCH_JOIN_RULE_INVALID_VALUE.get( joinRule.getIdentifierString()), e); } final JoinBaseDN joinBase; if (joinBaseDN.isPresent()) { final String s = StaticUtils.toLowerCase(joinBaseDN.getValue()); if (s.equals("search-base") || s.equals("search-base-dn")) { joinBase = JoinBaseDN.createUseSearchBaseDN(); } else if (s.equals("source-entry-dn") || s.equals("source-dn")) { joinBase = JoinBaseDN.createUseSourceEntryDN(); } else { try { final DN dn = new DN(joinBaseDN.getValue()); joinBase = JoinBaseDN.createUseCustomBaseDN(joinBaseDN.getValue()); } catch (final Exception e) { Debug.debugException(e); throw new ArgumentException( ERR_LDAPSEARCH_JOIN_BASE_DN_INVALID_VALUE.get( joinBaseDN.getIdentifierString()), e); } } } else { joinBase = JoinBaseDN.createUseSearchBaseDN(); } final String[] joinAttrs; if (joinRequestedAttribute.isPresent()) { final List valueList = joinRequestedAttribute.getValues(); joinAttrs = new String[valueList.size()]; valueList.toArray(joinAttrs); } else { joinAttrs = null; } joinRequestControl = new JoinRequestControl(new JoinRequestValue(rule, joinBase, joinScope.getValue(), DereferencePolicy.NEVER, joinSizeLimit.getValue(), joinFilter.getValue(), joinAttrs, joinRequireMatch.isPresent(), null)); } // If we should use the route to backend set request control, then validate // and pre-create those controls. if (routeToBackendSet.isPresent()) { final List values = routeToBackendSet.getValues(); final Map> idsByRP = new LinkedHashMap<>( StaticUtils.computeMapCapacity(values.size())); for (final String value : values) { final int colonPos = value.indexOf(':'); if (colonPos <= 0) { throw new ArgumentException( ERR_LDAPSEARCH_ROUTE_TO_BACKEND_SET_INVALID_FORMAT.get(value, routeToBackendSet.getIdentifierString())); } final String rpID = value.substring(0, colonPos); final String bsID = value.substring(colonPos+1); List idsForRP = idsByRP.get(rpID); if (idsForRP == null) { idsForRP = new ArrayList<>(values.size()); idsByRP.put(rpID, idsForRP); } idsForRP.add(bsID); } for (final Map.Entry> e : idsByRP.entrySet()) { final String rpID = e.getKey(); final List bsIDs = e.getValue(); routeToBackendSetRequestControls.add( RouteToBackendSetRequestControl.createAbsoluteRoutingRequest(true, rpID, bsIDs)); } } // Parse the dereference policy. final String derefStr = StaticUtils.toLowerCase(dereferencePolicy.getValue()); if (derefStr.equals("always")) { derefPolicy = DereferencePolicy.ALWAYS; } else if (derefStr.equals("search")) { derefPolicy = DereferencePolicy.SEARCHING; } else if (derefStr.equals("find")) { derefPolicy = DereferencePolicy.FINDING; } else { derefPolicy = DereferencePolicy.NEVER; } // If the --proxyAs argument was provided, then make sure its value is // properly formatted. if (proxyAs.isPresent()) { final String proxyAsValue = proxyAs.getValue(); final String lowerProxyAsValue = StaticUtils.toLowerCase(proxyAsValue); if (lowerProxyAsValue.startsWith("dn:")) { final String dnString = proxyAsValue.substring(3); if (! DN.isValidDN(dnString)) { throw new ArgumentException(ERR_LDAPSEARCH_PROXY_AS_DN_NOT_DN.get( proxyAs.getIdentifierString(), dnString)); } } else if (! lowerProxyAsValue.startsWith("u:")) { throw new ArgumentException( ERR_LDAPSEARCH_PROXY_AS_VALUE_MISSING_PREFIX.get( proxyAs.getIdentifierString())); } } // See if any entry transformations need to be applied. final ArrayList transformations = new ArrayList<>(5); if (excludeAttribute.isPresent()) { transformations.add(new ExcludeAttributeTransformation(null, excludeAttribute.getValues())); } if (redactAttribute.isPresent()) { transformations.add(new RedactAttributeTransformation(null, true, (! hideRedactedValueCount.isPresent()), redactAttribute.getValues())); } if (scrambleAttribute.isPresent()) { final Long randomSeed; if (scrambleRandomSeed.isPresent()) { randomSeed = scrambleRandomSeed.getValue().longValue(); } else { randomSeed = null; } transformations.add(new ScrambleAttributeTransformation(null, randomSeed, true, scrambleAttribute.getValues(), scrambleJSONField.getValues())); } if (renameAttributeFrom.isPresent()) { if (renameAttributeFrom.getNumOccurrences() != renameAttributeTo.getNumOccurrences()) { throw new ArgumentException( ERR_LDAPSEARCH_RENAME_ATTRIBUTE_MISMATCH.get()); } final Iterator sourceIterator = renameAttributeFrom.getValues().iterator(); final Iterator targetIterator = renameAttributeTo.getValues().iterator(); while (sourceIterator.hasNext()) { transformations.add(new RenameAttributeTransformation(null, sourceIterator.next(), targetIterator.next(), true)); } } if (moveSubtreeFrom.isPresent()) { if (moveSubtreeFrom.getNumOccurrences() != moveSubtreeTo.getNumOccurrences()) { throw new ArgumentException(ERR_LDAPSEARCH_MOVE_SUBTREE_MISMATCH.get()); } final Iterator sourceIterator = moveSubtreeFrom.getValues().iterator(); final Iterator targetIterator = moveSubtreeTo.getValues().iterator(); while (sourceIterator.hasNext()) { transformations.add(new MoveSubtreeTransformation(sourceIterator.next(), targetIterator.next())); } } if (! transformations.isEmpty()) { entryTransformations = transformations; } // Create the result writer. final String outputFormatStr = StaticUtils.toLowerCase(outputFormat.getValue()); if (outputFormatStr.equals("json")) { resultWriter = new JSONLDAPResultWriter(getOutStream()); } else if (outputFormatStr.equals("csv") || outputFormatStr.equals("multi-valued-csv") || outputFormatStr.equals("tab-delimited") || outputFormatStr.equals("multi-valued-tab-delimited")) { // These output formats cannot be used with the --ldapURLFile argument. if (ldapURLFile.isPresent()) { throw new ArgumentException( ERR_LDAPSEARCH_OUTPUT_FORMAT_NOT_SUPPORTED_WITH_URLS.get( outputFormat.getValue(), ldapURLFile.getIdentifierString())); } // These output formats require the requested attributes to be specified // via the --requestedAttribute argument rather than as unnamed trailing // arguments. final List requestedAttributes = requestedAttribute.getValues(); if ((requestedAttributes == null) || requestedAttributes.isEmpty()) { throw new ArgumentException( ERR_LDAPSEARCH_OUTPUT_FORMAT_REQUIRES_REQUESTED_ATTR_ARG.get( outputFormat.getValue(), requestedAttribute.getIdentifierString())); } switch (trailingArgs.size()) { case 0: // This is fine. break; case 1: // Make sure that the trailing argument is a filter rather than a // requested attribute. It's sufficient to ensure that neither the // filter nor filterFile argument was provided. if (filter.isPresent() || filterFile.isPresent()) { throw new ArgumentException( ERR_LDAPSEARCH_OUTPUT_FORMAT_REQUIRES_REQUESTED_ATTR_ARG.get( outputFormat.getValue(), requestedAttribute.getIdentifierString())); } break; default: throw new ArgumentException( ERR_LDAPSEARCH_OUTPUT_FORMAT_REQUIRES_REQUESTED_ATTR_ARG.get( outputFormat.getValue(), requestedAttribute.getIdentifierString())); } final OutputFormat format; final boolean includeAllValues; switch (outputFormatStr) { case "multi-valued-csv": format = OutputFormat.CSV; includeAllValues = true; break; case "tab-delimited": format = OutputFormat.TAB_DELIMITED_TEXT; includeAllValues = false; break; case "multi-valued-tab-delimited": format = OutputFormat.TAB_DELIMITED_TEXT; includeAllValues = true; break; case "csv": default: format = OutputFormat.CSV; includeAllValues = false; break; } resultWriter = new ColumnBasedLDAPResultWriter(getOutStream(), format, requestedAttributes, WRAP_COLUMN, includeAllValues); } else if (outputFormatStr.equals("dns-only")) { resultWriter = new DNsOnlyLDAPResultWriter(getOutStream()); } else if (outputFormatStr.equals("values-only")) { resultWriter = new ValuesOnlyLDAPResultWriter(getOutStream()); } else { resultWriter = new LDIFLDAPResultWriter(getOutStream(), WRAP_COLUMN); } } /** * {@inheritDoc} */ @Override() @NotNull() public LDAPConnectionOptions getConnectionOptions() { final LDAPConnectionOptions options = new LDAPConnectionOptions(); options.setUseSynchronousMode(true); options.setFollowReferrals(followReferrals.isPresent()); options.setUnsolicitedNotificationHandler(this); options.setResponseTimeoutMillis(0L); return options; } /** * {@inheritDoc} */ @Override() @NotNull() public ResultCode doToolProcessing() { // If we should encrypt the output, then get the encryption passphrase. if (encryptOutput.isPresent()) { if (encryptionPassphraseFile.isPresent()) { try { encryptionPassphrase = ToolUtils.readEncryptionPassphraseFromFile( encryptionPassphraseFile.getValue()); } catch (final LDAPException e) { Debug.debugException(e); wrapErr(0, WRAP_COLUMN, e.getMessage()); return e.getResultCode(); } } else { try { encryptionPassphrase = ToolUtils.promptForEncryptionPassphrase(false, true, getOut(), getErr()); } catch (final LDAPException e) { Debug.debugException(e); wrapErr(0, WRAP_COLUMN, e.getMessage()); return e.getResultCode(); } } } // If we should use an output file, then set that up now. Otherwise, write // the header to standard output. if (outputFile.isPresent()) { if (! separateOutputFilePerSearch.isPresent()) { try { OutputStream s = new FileOutputStream(outputFile.getValue()); if (encryptOutput.isPresent()) { s = new PassphraseEncryptedOutputStream(encryptionPassphrase, s); } if (compressOutput.isPresent()) { s = new GZIPOutputStream(s); } if (teeResultsToStandardOut.isPresent()) { outStream = new PrintStream(new TeeOutputStream(s, getOut())); } else { outStream = new PrintStream(s); } resultWriter.updateOutputStream(outStream); errStream = outStream; } catch (final Exception e) { Debug.debugException(e); wrapErr(0, WRAP_COLUMN, ERR_LDAPSEARCH_CANNOT_OPEN_OUTPUT_FILE.get( outputFile.getValue().getAbsolutePath(), StaticUtils.getExceptionMessage(e))); return ResultCode.LOCAL_ERROR; } resultWriter.writeHeader(); } } else { resultWriter.writeHeader(); } // Examine the arguments to determine the sets of controls to use for each // type of request. final List searchControls; try { searchControls = getSearchControls(); } catch (final LDAPException e) { Debug.debugException(e); wrapErr(0, WRAP_COLUMN, e.getMessage()); return e.getResultCode(); } // If appropriate, ensure that any search result entries that include // base64-encoded attribute values will also include comments that attempt // to provide a human-readable representation of that value. final boolean originalCommentAboutBase64EncodedValues = LDIFWriter.commentAboutBase64EncodedValues(); LDIFWriter.setCommentAboutBase64EncodedValues( ! suppressBase64EncodedValueComments.isPresent()); LDAPConnectionPool pool = null; try { // Create a connection pool that will be used to communicate with the // directory server. if (! dryRun.isPresent()) { try { final StartAdministrativeSessionPostConnectProcessor p; if (useAdministrativeSession.isPresent()) { p = new StartAdministrativeSessionPostConnectProcessor( new StartAdministrativeSessionExtendedRequest(getToolName(), true)); } else { p = null; } pool = getConnectionPool(1, 1, 0, p, null, true, new ReportBindResultLDAPConnectionPoolHealthCheck(this, true, false)); } catch (final LDAPException le) { // This shouldn't happen since the pool won't throw an exception if an // attempt to create an initial connection fails. Debug.debugException(le); commentToErr(ERR_LDAPSEARCH_CANNOT_CREATE_CONNECTION_POOL.get( StaticUtils.getExceptionMessage(le))); return le.getResultCode(); } if (retryFailedOperations.isPresent()) { pool.setRetryFailedOperationsDueToInvalidConnections(true); } } // If appropriate, create a rate limiter. final FixedRateBarrier rateLimiter; if (ratePerSecond.isPresent()) { rateLimiter = new FixedRateBarrier(1000L, ratePerSecond.getValue()); } else { rateLimiter = null; } // If one or more LDAP URL files are provided, then construct search // requests from those URLs. if (ldapURLFile.isPresent()) { return searchWithLDAPURLs(pool, rateLimiter, searchControls); } // Get the set of requested attributes, as a combination of the // requestedAttribute argument values and any trailing arguments. final ArrayList attrList = new ArrayList<>(10); if (requestedAttribute.isPresent()) { attrList.addAll(requestedAttribute.getValues()); } final List trailingArgs = parser.getTrailingArguments(); if (! trailingArgs.isEmpty()) { final Iterator trailingArgIterator = trailingArgs.iterator(); if (! (filter.isPresent() || filterFile.isPresent())) { trailingArgIterator.next(); } while (trailingArgIterator.hasNext()) { attrList.add(trailingArgIterator.next()); } } final String[] attributes = new String[attrList.size()]; attrList.toArray(attributes); // If either or both the filter or filterFile arguments are provided, then // use them to get the filters to process. Otherwise, the first trailing // argument should be a filter. ResultCode resultCode = ResultCode.SUCCESS; if (filter.isPresent() || filterFile.isPresent()) { if (filter.isPresent()) { for (final Filter f : filter.getValues()) { final ResultCode rc = searchWithFilter(pool, f, attributes, rateLimiter, searchControls); if (rc != ResultCode.SUCCESS) { if (resultCode == ResultCode.SUCCESS) { resultCode = rc; } if (! continueOnError.isPresent()) { return resultCode; } } } } if (filterFile.isPresent()) { final ResultCode rc = searchWithFilterFile(pool, attributes, rateLimiter, searchControls); if (rc != ResultCode.SUCCESS) { if (resultCode == ResultCode.SUCCESS) { resultCode = rc; } if (! continueOnError.isPresent()) { return resultCode; } } } } else { final Filter f; try { final String filterStr = parser.getTrailingArguments().iterator().next(); f = Filter.create(filterStr); } catch (final LDAPException le) { // This should never happen. Debug.debugException(le); displayResult(le.toLDAPResult()); return le.getResultCode(); } resultCode = searchWithFilter(pool, f, attributes, rateLimiter, searchControls); } return resultCode; } finally { if (pool != null) { try { pool.close(); } catch (final Exception e) { Debug.debugException(e); } } if (outStream != null) { try { outStream.close(); outStream = null; } catch (final Exception e) { Debug.debugException(e); } } if (errStream != null) { try { errStream.close(); errStream = null; } catch (final Exception e) { Debug.debugException(e); } } LDIFWriter.setCommentAboutBase64EncodedValues( originalCommentAboutBase64EncodedValues); } } /** * Processes a set of searches using LDAP URLs read from one or more files. * * @param pool The connection pool to use to communicate with the * directory server. * @param rateLimiter An optional fixed-rate barrier that can be used for * request rate limiting. * @param searchControls The set of controls to include in search requests. * * @return A result code indicating the result of the processing. */ @NotNull() private ResultCode searchWithLDAPURLs(@NotNull final LDAPConnectionPool pool, @Nullable final FixedRateBarrier rateLimiter, @NotNull final List searchControls) { ResultCode resultCode = ResultCode.SUCCESS; for (final File f : ldapURLFile.getValues()) { BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(f)); while (true) { final String line = reader.readLine(); if (line == null) { break; } if ((line.length() == 0) || line.startsWith("#")) { continue; } final LDAPURL url; try { url = new LDAPURL(line); } catch (final LDAPException le) { Debug.debugException(le); commentToErr(ERR_LDAPSEARCH_MALFORMED_LDAP_URL.get( f.getAbsolutePath(), line)); if (resultCode == ResultCode.SUCCESS) { resultCode = le.getResultCode(); } if (continueOnError.isPresent()) { continue; } else { return resultCode; } } final SearchRequest searchRequest = new SearchRequest( new LDAPSearchListener(resultWriter, entryTransformations), url.getBaseDN().toString(), url.getScope(), derefPolicy, sizeLimit.getValue(), timeLimitSeconds.getValue(), typesOnly.isPresent(), url.getFilter(), url.getAttributes()); final ResultCode rc = doSearch(pool, searchRequest, rateLimiter, searchControls); if (rc != ResultCode.SUCCESS) { if (resultCode == ResultCode.SUCCESS) { resultCode = rc; } if (! continueOnError.isPresent()) { return resultCode; } } } } catch (final IOException ioe) { commentToErr(ERR_LDAPSEARCH_CANNOT_READ_LDAP_URL_FILE.get( f.getAbsolutePath(), StaticUtils.getExceptionMessage(ioe))); return ResultCode.LOCAL_ERROR; } finally { if (reader != null) { try { reader.close(); } catch (final Exception e) { Debug.debugException(e); } } } } return resultCode; } /** * Processes a set of searches using filters read from one or more files. * * @param pool The connection pool to use to communicate with the * directory server. * @param attributes The set of attributes to request that the server * include in matching entries. * @param rateLimiter An optional fixed-rate barrier that can be used for * request rate limiting. * @param searchControls The set of controls to include in search requests. * * @return A result code indicating the result of the processing. */ @NotNull() private ResultCode searchWithFilterFile( @NotNull final LDAPConnectionPool pool, @NotNull final String[] attributes, @Nullable final FixedRateBarrier rateLimiter, @NotNull final List searchControls) { ResultCode resultCode = ResultCode.SUCCESS; for (final File f : filterFile.getValues()) { FilterFileReader reader = null; try { reader = new FilterFileReader(f); while (true) { final Filter searchFilter; try { searchFilter = reader.readFilter(); } catch (final LDAPException le) { Debug.debugException(le); commentToErr(ERR_LDAPSEARCH_MALFORMED_FILTER.get( f.getAbsolutePath(), le.getMessage())); if (resultCode == ResultCode.SUCCESS) { resultCode = le.getResultCode(); } if (continueOnError.isPresent()) { continue; } else { return resultCode; } } if (searchFilter == null) { break; } final ResultCode rc = searchWithFilter(pool, searchFilter, attributes, rateLimiter, searchControls); if (rc != ResultCode.SUCCESS) { if (resultCode == ResultCode.SUCCESS) { resultCode = rc; } if (! continueOnError.isPresent()) { return resultCode; } } } } catch (final IOException ioe) { Debug.debugException(ioe); commentToErr(ERR_LDAPSEARCH_CANNOT_READ_FILTER_FILE.get( f.getAbsolutePath(), StaticUtils.getExceptionMessage(ioe))); return ResultCode.LOCAL_ERROR; } finally { if (reader != null) { try { reader.close(); } catch (final Exception e) { Debug.debugException(e); } } } } return resultCode; } /** * Processes a search using the provided filter. * * @param pool The connection pool to use to communicate with the * directory server. * @param filter The filter to use for the search. * @param attributes The set of attributes to request that the server * include in matching entries. * @param rateLimiter An optional fixed-rate barrier that can be used for * request rate limiting. * @param searchControls The set of controls to include in search requests. * * @return A result code indicating the result of the processing. */ @NotNull() private ResultCode searchWithFilter(@NotNull final LDAPConnectionPool pool, @NotNull final Filter filter, @NotNull final String[] attributes, @Nullable final FixedRateBarrier rateLimiter, @NotNull final List searchControls) { final String baseDNString; if (baseDN.isPresent()) { baseDNString = baseDN.getStringValue(); } else { baseDNString = ""; } final SearchRequest searchRequest = new SearchRequest( new LDAPSearchListener(resultWriter, entryTransformations), baseDNString, scope.getValue(), derefPolicy, sizeLimit.getValue(), timeLimitSeconds.getValue(), typesOnly.isPresent(), filter, attributes); return doSearch(pool, searchRequest, rateLimiter, searchControls); } /** * Processes a search with the provided information. * * @param pool The connection pool to use to communicate with the * directory server. * @param searchRequest The search request to process. * @param rateLimiter An optional fixed-rate barrier that can be used for * request rate limiting. * @param searchControls The set of controls to include in search requests. * * @return A result code indicating the result of the processing. */ @NotNull() private ResultCode doSearch(@NotNull final LDAPConnectionPool pool, @NotNull final SearchRequest searchRequest, @Nullable final FixedRateBarrier rateLimiter, @NotNull final List searchControls) { if (separateOutputFilePerSearch.isPresent()) { try { final String path = outputFile.getValue().getAbsolutePath() + '.' + outputFileCounter.getAndIncrement(); OutputStream s = new FileOutputStream(path); if (encryptOutput.isPresent()) { s = new PassphraseEncryptedOutputStream(encryptionPassphrase, s); } if (compressOutput.isPresent()) { s = new GZIPOutputStream(s); } if (teeResultsToStandardOut.isPresent()) { outStream = new PrintStream(new TeeOutputStream(s, getOut())); } else { outStream = new PrintStream(s); } resultWriter.updateOutputStream(outStream); errStream = outStream; } catch (final Exception e) { Debug.debugException(e); wrapErr(0, WRAP_COLUMN, ERR_LDAPSEARCH_CANNOT_OPEN_OUTPUT_FILE.get( outputFile.getValue().getAbsolutePath(), StaticUtils.getExceptionMessage(e))); return ResultCode.LOCAL_ERROR; } resultWriter.writeHeader(); } try { if (rateLimiter != null) { rateLimiter.await(); } ASN1OctetString pagedResultsCookie = null; boolean multiplePages = false; long totalEntries = 0; long totalReferences = 0; SearchResult searchResult; try { while (true) { searchRequest.setControls(searchControls); if (simplePageSize.isPresent()) { searchRequest.addControl(new SimplePagedResultsControl( simplePageSize.getValue(), pagedResultsCookie)); } if (dryRun.isPresent()) { searchResult = new SearchResult(-1, ResultCode.SUCCESS, INFO_LDAPSEARCH_DRY_RUN_REQUEST_NOT_SENT.get( dryRun.getIdentifierString(), String.valueOf(searchRequest)), null, null, 0, 0, null); break; } else { if (! terse.isPresent()) { if (verbose.isPresent() || persistentSearch.isPresent() || filterFile.isPresent() || ldapURLFile.isPresent() || (filter.isPresent() && (filter.getNumOccurrences() > 1))) { commentToOut(INFO_LDAPSEARCH_SENDING_SEARCH_REQUEST.get( String.valueOf(searchRequest))); } } searchResult = pool.search(searchRequest); } searchResult = handleJSONEncodedResponseControls(searchResult); if (searchResult.getEntryCount() > 0) { totalEntries += searchResult.getEntryCount(); } if (searchResult.getReferenceCount() > 0) { totalReferences += searchResult.getReferenceCount(); } if (simplePageSize.isPresent()) { final SimplePagedResultsControl pagedResultsControl; try { pagedResultsControl = SimplePagedResultsControl.get(searchResult); if (pagedResultsControl == null) { throw new LDAPSearchException(new SearchResult( searchResult.getMessageID(), ResultCode.CONTROL_NOT_FOUND, ERR_LDAPSEARCH_MISSING_PAGED_RESULTS_RESPONSE_CONTROL. get(), searchResult.getMatchedDN(), searchResult.getReferralURLs(), searchResult.getSearchEntries(), searchResult.getSearchReferences(), searchResult.getEntryCount(), searchResult.getReferenceCount(), searchResult.getResponseControls())); } if (pagedResultsControl.moreResultsToReturn()) { if (verbose.isPresent()) { commentToOut( INFO_LDAPSEARCH_INTERMEDIATE_PAGED_SEARCH_RESULT.get()); displayResult(searchResult); } multiplePages = true; pagedResultsCookie = pagedResultsControl.getCookie(); } else { break; } } catch (final LDAPException le) { Debug.debugException(le); throw new LDAPSearchException(new SearchResult( searchResult.getMessageID(), ResultCode.CONTROL_NOT_FOUND, ERR_LDAPSEARCH_CANNOT_DECODE_PAGED_RESULTS_RESPONSE_CONTROL. get(StaticUtils.getExceptionMessage(le)), searchResult.getMatchedDN(), searchResult.getReferralURLs(), searchResult.getSearchEntries(), searchResult.getSearchReferences(), searchResult.getEntryCount(), searchResult.getReferenceCount(), searchResult.getResponseControls())); } } else { break; } } } catch (final LDAPSearchException lse) { Debug.debugException(lse); searchResult = lse.toLDAPResult(); if (searchResult.getEntryCount() > 0) { totalEntries += searchResult.getEntryCount(); } if (searchResult.getReferenceCount() > 0) { totalReferences += searchResult.getReferenceCount(); } } if ((searchResult.getResultCode() != ResultCode.SUCCESS) || (searchResult.getDiagnosticMessage() != null) || (! terse.isPresent())) { displayResult(searchResult); } if (multiplePages && (! terse.isPresent())) { commentToOut(INFO_LDAPSEARCH_TOTAL_SEARCH_ENTRIES.get(totalEntries)); if (totalReferences > 0) { commentToOut(INFO_LDAPSEARCH_TOTAL_SEARCH_REFERENCES.get( totalReferences)); } } if (countEntries.isPresent()) { return ResultCode.valueOf((int) Math.min(totalEntries, 255)); } else if (requireMatch.isPresent() && (totalEntries == 0)) { return ResultCode.NO_RESULTS_RETURNED; } else { return searchResult.getResultCode(); } } finally { if (separateOutputFilePerSearch.isPresent()) { try { outStream.close(); } catch (final Exception e) { Debug.debugException(e); } outStream = null; errStream = null; } } } /** * Retrieves a list of the controls that should be used when processing search * operations. * * @return A list of the controls that should be used when processing search * operations. * * @throws LDAPException If a problem is encountered while generating the * controls for a search request. */ @NotNull() private List getSearchControls() throws LDAPException { final ArrayList controls = new ArrayList<>(10); if (searchControl.isPresent()) { controls.addAll(searchControl.getValues()); } if (joinRequestControl != null) { controls.add(joinRequestControl); } if (matchedValuesRequestControl != null) { controls.add(matchedValuesRequestControl); } if (matchingEntryCountRequestControl != null) { controls.add(matchingEntryCountRequestControl); } if (overrideSearchLimitsRequestControl != null) { controls.add(overrideSearchLimitsRequestControl); } if (persistentSearchRequestControl != null) { controls.add(persistentSearchRequestControl); } if (sortRequestControl != null) { controls.add(sortRequestControl); } if (vlvRequestControl != null) { controls.add(vlvRequestControl); } controls.addAll(routeToBackendSetRequestControls); if (accessLogField.isPresent()) { final Map fields = new LinkedHashMap<>(); for (final String nameValueStr : accessLogField.getValues()) { final int colonPos = nameValueStr.indexOf(':'); if (colonPos < 0) { throw new LDAPException(ResultCode.PARAM_ERROR, ERR_LDAPSEARCH_ACCESS_LOG_FIELD_NO_COLON.get( accessLogField.getIdentifierString(), nameValueStr)); } final String fieldName = nameValueStr.substring(0, colonPos); if (fields.containsKey(fieldName)) { throw new LDAPException(ResultCode.PARAM_ERROR, ERR_LDAPSEARCH_ACCESS_LOG_FIELD_DUPLICATE_FIELD.get( accessLogField.getIdentifierString(), fieldName)); } final String valueStr = nameValueStr.substring(colonPos + 1); if (valueStr.equalsIgnoreCase("true")) { fields.put(fieldName, JSONBoolean.TRUE); } else if (valueStr.equalsIgnoreCase("false")) { fields.put(fieldName, JSONBoolean.FALSE); } else { try { final BigDecimal d = new BigDecimal(valueStr); fields.put(fieldName, new JSONNumber(d)); } catch (final Exception e) { Debug.debugException(e); fields.put(fieldName, new JSONString(valueStr)); } } } controls.add(new AccessLogFieldRequestControl(false, new JSONObject(fields))); } if (accountUsable.isPresent()) { controls.add(new AccountUsableRequestControl(true)); } if (getBackendSetID.isPresent()) { controls.add(new GetBackendSetIDRequestControl(false)); } if (getServerID.isPresent()) { controls.add(new GetServerIDRequestControl(false)); } if (includeReplicationConflictEntries.isPresent()) { controls.add(new ReturnConflictEntriesRequestControl(true)); } if (includeSoftDeletedEntries.isPresent()) { final String valueStr = StaticUtils.toLowerCase(includeSoftDeletedEntries.getValue()); if (valueStr.equals("with-non-deleted-entries")) { controls.add(new SoftDeletedEntryAccessRequestControl(true, true, false)); } else if (valueStr.equals("without-non-deleted-entries")) { controls.add(new SoftDeletedEntryAccessRequestControl(true, false, false)); } else { controls.add(new SoftDeletedEntryAccessRequestControl(true, false, true)); } } if (draftLDUPSubentries.isPresent()) { controls.add(new DraftLDUPSubentriesRequestControl(true)); } if (rfc3672Subentries.isPresent()) { controls.add(new RFC3672SubentriesRequestControl( rfc3672Subentries.getValue())); } if (manageDsaIT.isPresent()) { controls.add(new ManageDsaITRequestControl(true)); } if (realAttributesOnly.isPresent()) { controls.add(new RealAttributesOnlyRequestControl(true)); } if (routeToServer.isPresent()) { controls.add(new RouteToServerRequestControl(false, routeToServer.getValue(), false, false, false)); } if (virtualAttributesOnly.isPresent()) { controls.add(new VirtualAttributesOnlyRequestControl(true)); } if (excludeBranch.isPresent()) { final ArrayList dns = new ArrayList<>(excludeBranch.getValues().size()); for (final DN dn : excludeBranch.getValues()) { dns.add(dn.toString()); } controls.add(new ExcludeBranchRequestControl(true, dns)); } if (assertionFilter.isPresent()) { controls.add(new AssertionRequestControl( assertionFilter.getValue(), true)); } if (getEffectiveRightsAuthzID.isPresent()) { final String[] attributes; if (getEffectiveRightsAttribute.isPresent()) { attributes = new String[getEffectiveRightsAttribute.getValues().size()]; for (int i=0; i < attributes.length; i++) { attributes[i] = getEffectiveRightsAttribute.getValues().get(i); } } else { attributes = StaticUtils.NO_STRINGS; } controls.add(new GetEffectiveRightsRequestControl(true, getEffectiveRightsAuthzID.getValue(), attributes)); } if (operationPurpose.isPresent()) { controls.add(new OperationPurposeRequestControl(true, "ldapsearch", Version.NUMERIC_VERSION_STRING, "LDAPSearch.getSearchControls", operationPurpose.getValue())); } if (proxyAs.isPresent()) { final String proxyAsValue = proxyAs.getValue(); final String lowerProxyAsValue = StaticUtils.toLowerCase(proxyAsValue); if (lowerProxyAsValue.startsWith("dn:")) { final String dnString = proxyAsValue.substring(3); if (! DN.isValidDN(dnString)) { throw new LDAPException(ResultCode.PARAM_ERROR, ERR_LDAPSEARCH_PROXY_AS_DN_NOT_DN.get( proxyAs.getIdentifierString(), dnString)); } } else if (! lowerProxyAsValue.startsWith("u:")) { throw new LDAPException(ResultCode.PARAM_ERROR, ERR_LDAPSEARCH_PROXY_AS_VALUE_MISSING_PREFIX.get( proxyAs.getIdentifierString())); } controls.add(new ProxiedAuthorizationV2RequestControl(proxyAsValue)); } if (proxyV1As.isPresent()) { controls.add(new ProxiedAuthorizationV1RequestControl( proxyV1As.getValue())); } if (suppressOperationalAttributeUpdates.isPresent()) { final EnumSet suppressTypes = EnumSet.noneOf(SuppressType.class); for (final String s : suppressOperationalAttributeUpdates.getValues()) { if (s.equalsIgnoreCase("last-access-time")) { suppressTypes.add(SuppressType.LAST_ACCESS_TIME); } else if (s.equalsIgnoreCase("last-login-time")) { suppressTypes.add(SuppressType.LAST_LOGIN_TIME); } else if (s.equalsIgnoreCase("last-login-ip")) { suppressTypes.add(SuppressType.LAST_LOGIN_IP); } } controls.add(new SuppressOperationalAttributeUpdateRequestControl( suppressTypes)); } if (rejectUnindexedSearch.isPresent()) { controls.add(new RejectUnindexedSearchRequestControl()); } if (permitUnindexedSearch.isPresent()) { controls.add(new PermitUnindexedSearchRequestControl()); } if (useJSONFormattedRequestControls.isPresent()) { final JSONFormattedRequestControl jsonFormattedRequestControl = JSONFormattedRequestControl.createWithControls(true, controls); controls.clear(); controls.add(jsonFormattedRequestControl); } return controls; } /** * Displays information about the provided result, including special * processing for a number of supported response controls. * * @param result The result to examine. */ private void displayResult(@NotNull final LDAPResult result) { resultWriter.writeResult(result); } /** * Writes the provided message to the output stream. * * @param message The message to be written. */ void writeOut(@NotNull final String message) { if (outStream == null) { out(message); } else { outStream.println(message); } } /** * Writes the provided message to the error stream. * * @param message The message to be written. */ private void writeErr(@NotNull final String message) { if (errStream == null) { err(message); } else { errStream.println(message); } } /** * Writes a line-wrapped, commented version of the provided message to * standard output. * * @param message The message to be written. */ private void commentToOut(@NotNull final String message) { if (terse.isPresent()) { return; } for (final String line : StaticUtils.wrapLine(message, (WRAP_COLUMN - 2))) { writeOut("# " + line); } } /** * Writes a line-wrapped, commented version of the provided message to * standard error. * * @param message The message to be written. */ private void commentToErr(@NotNull final String message) { for (final String line : StaticUtils.wrapLine(message, (WRAP_COLUMN - 2))) { writeErr("# " + line); } } /** * Retrieves the tool's output stream. * * @return The tool's output stream. */ @NotNull() PrintStream getOutStream() { if (outStream == null) { return getOut(); } else { return outStream; } } /** * Retrieves the tool's error stream. * * @return The tool's error stream. */ @NotNull() PrintStream getErrStream() { if (errStream == null) { return getErr(); } else { return errStream; } } /** * Sets the output handler that should be used by this tool This is primarily * intended for testing purposes. * * @param resultWriter The result writer that should be used by this tool. */ void setResultWriter(@NotNull final LDAPResultWriter resultWriter) { this.resultWriter = resultWriter; } /** * {@inheritDoc} */ @Override() public void handleUnsolicitedNotification( @NotNull final LDAPConnection connection, @NotNull final ExtendedResult notification) { resultWriter.writeUnsolicitedNotification(connection, notification); } /** * Examines the provided search result to see if it includes a JSONf-formatted * response control. If so, then its embedded controls will be extracted and * a new search result will be returned with those extracted controls instead * of the JSON-formatted response control. Otherwise, the provided search * result will be returned. * * @param searchResult The search result to be handled. It must not be * {@code null}. * * @return A new search result with the controls extracted from a * JSON-formatted response control, or the original search result if * it did not include a JSON-formatted response control. */ @NotNull() static SearchResult handleJSONEncodedResponseControls( @NotNull final SearchResult searchResult) { try { final JSONFormattedResponseControl jsonFormattedResponseControl = JSONFormattedResponseControl.get(searchResult); if (jsonFormattedResponseControl == null) { return searchResult; } final JSONFormattedControlDecodeBehavior decodeBehavior = new JSONFormattedControlDecodeBehavior(); decodeBehavior.setThrowOnUnparsableObject(false); decodeBehavior.setThrowOnInvalidCriticalControl(false); decodeBehavior.setThrowOnInvalidNonCriticalControl(false); decodeBehavior.setThrowOnInvalidNonCriticalControl(false); decodeBehavior.setAllowEmbeddedJSONFormattedControl(true); decodeBehavior.setStrict(false); final List decodedControls = jsonFormattedResponseControl.decodeEmbeddedControls( decodeBehavior, null); return new SearchResult(searchResult.getMessageID(), searchResult.getResultCode(), searchResult.getDiagnosticMessage(), searchResult.getMatchedDN(), searchResult.getReferralURLs(), searchResult.getSearchEntries(), searchResult.getSearchReferences(), searchResult.getEntryCount(), searchResult.getReferenceCount(), StaticUtils.toArray(decodedControls, Control.class)); } catch (final LDAPException e) { Debug.debugException(e); return searchResult; } } /** * {@inheritDoc} */ @Override() @NotNull() public LinkedHashMap getExampleUsages() { final LinkedHashMap examples = new LinkedHashMap<>(StaticUtils.computeMapCapacity(5)); String[] args = { "--hostname", "directory.example.com", "--port", "389", "--bindDN", "uid=jdoe,ou=People,dc=example,dc=com", "--bindPassword", "password", "--baseDN", "ou=People,dc=example,dc=com", "--scope", "sub", "(uid=jqpublic)", "givenName", "sn", "mail" }; examples.put(args, INFO_LDAPSEARCH_EXAMPLE_1.get()); args = new String[] { "--hostname", "directory.example.com", "--port", "636", "--useSSL", "--saslOption", "mech=PLAIN", "--saslOption", "authID=u:jdoe", "--bindPasswordFile", "/path/to/password/file", "--baseDN", "ou=People,dc=example,dc=com", "--scope", "sub", "--filterFile", "/path/to/filter/file", "--outputFile", "/path/to/base/output/file", "--separateOutputFilePerSearch", "--requestedAttribute", "*", "--requestedAttribute", "+" }; examples.put(args, INFO_LDAPSEARCH_EXAMPLE_2.get()); args = new String[] { "--hostname", "directory.example.com", "--port", "389", "--useStartTLS", "--trustStorePath", "/path/to/truststore/file", "--baseDN", "", "--scope", "base", "--outputFile", "/path/to/output/file", "--teeResultsToStandardOut", "(objectClass=*)", "*", "+" }; examples.put(args, INFO_LDAPSEARCH_EXAMPLE_3.get()); args = new String[] { "--hostname", "directory.example.com", "--port", "389", "--bindDN", "uid=admin,dc=example,dc=com", "--baseDN", "dc=example,dc=com", "--scope", "sub", "--outputFile", "/path/to/output/file", "--simplePageSize", "100", "(objectClass=*)", "*", "+" }; examples.put(args, INFO_LDAPSEARCH_EXAMPLE_4.get()); args = new String[] { "--hostname", "directory.example.com", "--port", "389", "--bindDN", "uid=admin,dc=example,dc=com", "--baseDN", "dc=example,dc=com", "--scope", "sub", "(&(givenName=John)(sn=Doe))", "debugsearchindex" }; examples.put(args, INFO_LDAPSEARCH_EXAMPLE_5.get()); return examples; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy