001/* 002 * Copyright 2016-2022 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2016-2022 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2016-2022 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.ldap.sdk.unboundidds.tools; 037 038 039 040import java.io.ByteArrayInputStream; 041import java.io.File; 042import java.io.InputStream; 043import java.io.IOException; 044import java.io.OutputStream; 045import java.util.ArrayList; 046import java.util.EnumSet; 047import java.util.HashSet; 048import java.util.LinkedHashMap; 049import java.util.List; 050import java.util.Map; 051import java.util.Set; 052import java.util.SortedMap; 053import java.util.StringTokenizer; 054import java.util.concurrent.TimeUnit; 055import java.util.concurrent.atomic.AtomicBoolean; 056 057import com.unboundid.asn1.ASN1OctetString; 058import com.unboundid.ldap.sdk.AddRequest; 059import com.unboundid.ldap.sdk.Control; 060import com.unboundid.ldap.sdk.DeleteRequest; 061import com.unboundid.ldap.sdk.DN; 062import com.unboundid.ldap.sdk.Entry; 063import com.unboundid.ldap.sdk.ExtendedResult; 064import com.unboundid.ldap.sdk.Filter; 065import com.unboundid.ldap.sdk.LDAPConnectionOptions; 066import com.unboundid.ldap.sdk.LDAPConnection; 067import com.unboundid.ldap.sdk.LDAPConnectionPool; 068import com.unboundid.ldap.sdk.LDAPException; 069import com.unboundid.ldap.sdk.LDAPRequest; 070import com.unboundid.ldap.sdk.LDAPResult; 071import com.unboundid.ldap.sdk.LDAPSearchException; 072import com.unboundid.ldap.sdk.Modification; 073import com.unboundid.ldap.sdk.ModifyRequest; 074import com.unboundid.ldap.sdk.ModifyDNRequest; 075import com.unboundid.ldap.sdk.ResultCode; 076import com.unboundid.ldap.sdk.SearchRequest; 077import com.unboundid.ldap.sdk.SearchResult; 078import com.unboundid.ldap.sdk.SearchScope; 079import com.unboundid.ldap.sdk.UnsolicitedNotificationHandler; 080import com.unboundid.ldap.sdk.Version; 081import com.unboundid.ldap.sdk.controls.AssertionRequestControl; 082import com.unboundid.ldap.sdk.controls.AuthorizationIdentityRequestControl; 083import com.unboundid.ldap.sdk.controls.ManageDsaITRequestControl; 084import com.unboundid.ldap.sdk.controls.PermissiveModifyRequestControl; 085import com.unboundid.ldap.sdk.controls.PostReadRequestControl; 086import com.unboundid.ldap.sdk.controls.PreReadRequestControl; 087import com.unboundid.ldap.sdk.controls.ProxiedAuthorizationV1RequestControl; 088import com.unboundid.ldap.sdk.controls.ProxiedAuthorizationV2RequestControl; 089import com.unboundid.ldap.sdk.controls.SimplePagedResultsControl; 090import com.unboundid.ldap.sdk.controls.SubtreeDeleteRequestControl; 091import com.unboundid.ldap.sdk.controls.TransactionSpecificationRequestControl; 092import com.unboundid.ldap.sdk.extensions.StartTransactionExtendedRequest; 093import com.unboundid.ldap.sdk.extensions.StartTransactionExtendedResult; 094import com.unboundid.ldap.sdk.extensions.EndTransactionExtendedRequest; 095import com.unboundid.ldap.sdk.unboundidds.controls.AssuredReplicationLocalLevel; 096import com.unboundid.ldap.sdk.unboundidds.controls. 097 AssuredReplicationRequestControl; 098import com.unboundid.ldap.sdk.unboundidds.controls. 099 AssuredReplicationRemoteLevel; 100import com.unboundid.ldap.sdk.unboundidds.controls. 101 GeneratePasswordRequestControl; 102import com.unboundid.ldap.sdk.unboundidds.controls. 103 GetAuthorizationEntryRequestControl; 104import com.unboundid.ldap.sdk.unboundidds.controls. 105 GetBackendSetIDRequestControl; 106import com.unboundid.ldap.sdk.unboundidds.controls. 107 GetRecentLoginHistoryRequestControl; 108import com.unboundid.ldap.sdk.unboundidds.controls. 109 GetUserResourceLimitsRequestControl; 110import com.unboundid.ldap.sdk.unboundidds.controls.GetServerIDRequestControl; 111import com.unboundid.ldap.sdk.unboundidds.controls.HardDeleteRequestControl; 112import com.unboundid.ldap.sdk.unboundidds.controls. 113 IgnoreNoUserModificationRequestControl; 114import com.unboundid.ldap.sdk.unboundidds.controls. 115 JSONFormattedControlDecodeBehavior; 116import com.unboundid.ldap.sdk.unboundidds.controls.JSONFormattedRequestControl; 117import com.unboundid.ldap.sdk.unboundidds.controls.JSONFormattedResponseControl; 118import com.unboundid.ldap.sdk.unboundidds.controls. 119 NameWithEntryUUIDRequestControl; 120import com.unboundid.ldap.sdk.unboundidds.controls.NoOpRequestControl; 121import com.unboundid.ldap.sdk.unboundidds.controls. 122 OperationPurposeRequestControl; 123import com.unboundid.ldap.sdk.unboundidds.controls.PasswordPolicyRequestControl; 124import com.unboundid.ldap.sdk.unboundidds.controls. 125 PasswordUpdateBehaviorRequestControl; 126import com.unboundid.ldap.sdk.unboundidds.controls. 127 PasswordUpdateBehaviorRequestControlProperties; 128import com.unboundid.ldap.sdk.unboundidds.controls. 129 PasswordValidationDetailsRequestControl; 130import com.unboundid.ldap.sdk.unboundidds.controls.PurgePasswordRequestControl; 131import com.unboundid.ldap.sdk.unboundidds.controls. 132 ReplicationRepairRequestControl; 133import com.unboundid.ldap.sdk.unboundidds.controls.RetirePasswordRequestControl; 134import com.unboundid.ldap.sdk.unboundidds.controls. 135 RouteToBackendSetRequestControl; 136import com.unboundid.ldap.sdk.unboundidds.controls.RouteToServerRequestControl; 137import com.unboundid.ldap.sdk.unboundidds.controls.SoftDeleteRequestControl; 138import com.unboundid.ldap.sdk.unboundidds.controls. 139 SuppressOperationalAttributeUpdateRequestControl; 140import com.unboundid.ldap.sdk.unboundidds.controls. 141 SuppressReferentialIntegrityUpdatesRequestControl; 142import com.unboundid.ldap.sdk.unboundidds.controls. 143 UniquenessMultipleAttributeBehavior; 144import com.unboundid.ldap.sdk.unboundidds.controls.UniquenessRequestControl; 145import com.unboundid.ldap.sdk.unboundidds.controls. 146 UniquenessRequestControlProperties; 147import com.unboundid.ldap.sdk.unboundidds.controls.SuppressType; 148import com.unboundid.ldap.sdk.unboundidds.controls.UndeleteRequestControl; 149import com.unboundid.ldap.sdk.unboundidds.controls.UniquenessValidationLevel; 150import com.unboundid.ldap.sdk.unboundidds.extensions.MultiUpdateErrorBehavior; 151import com.unboundid.ldap.sdk.unboundidds.extensions.MultiUpdateExtendedRequest; 152import com.unboundid.ldap.sdk.unboundidds.extensions. 153 StartAdministrativeSessionExtendedRequest; 154import com.unboundid.ldap.sdk.unboundidds.extensions. 155 StartAdministrativeSessionPostConnectProcessor; 156import com.unboundid.ldif.LDIFAddChangeRecord; 157import com.unboundid.ldif.LDIFChangeRecord; 158import com.unboundid.ldif.LDIFDeleteChangeRecord; 159import com.unboundid.ldif.LDIFException; 160import com.unboundid.ldif.LDIFModifyChangeRecord; 161import com.unboundid.ldif.LDIFModifyDNChangeRecord; 162import com.unboundid.ldif.LDIFReader; 163import com.unboundid.ldif.LDIFWriter; 164import com.unboundid.ldif.TrailingSpaceBehavior; 165import com.unboundid.util.Debug; 166import com.unboundid.util.DNFileReader; 167import com.unboundid.util.FilterFileReader; 168import com.unboundid.util.FixedRateBarrier; 169import com.unboundid.util.LDAPCommandLineTool; 170import com.unboundid.util.NotNull; 171import com.unboundid.util.Nullable; 172import com.unboundid.util.StaticUtils; 173import com.unboundid.util.SubtreeDeleter; 174import com.unboundid.util.SubtreeDeleterResult; 175import com.unboundid.util.ThreadSafety; 176import com.unboundid.util.ThreadSafetyLevel; 177import com.unboundid.util.args.ArgumentException; 178import com.unboundid.util.args.ArgumentParser; 179import com.unboundid.util.args.BooleanArgument; 180import com.unboundid.util.args.ControlArgument; 181import com.unboundid.util.args.DNArgument; 182import com.unboundid.util.args.DurationArgument; 183import com.unboundid.util.args.FileArgument; 184import com.unboundid.util.args.FilterArgument; 185import com.unboundid.util.args.IntegerArgument; 186import com.unboundid.util.args.StringArgument; 187 188import static com.unboundid.ldap.sdk.unboundidds.tools.ToolMessages.*; 189 190 191 192/** 193 * This class provides an implementation of an LDAP command-line tool that may 194 * be used to apply changes to a directory server. The changes to apply (which 195 * may include add, delete, modify, and modify DN operations) will be read in 196 * LDIF form, either from standard input or a specified file or set of files. 197 * This is a much more full-featured tool than the 198 * {@link com.unboundid.ldap.sdk.examples.LDAPModify} tool 199 * <BR> 200 * <BLOCKQUOTE> 201 * <B>NOTE:</B> This class, and other classes within the 202 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 203 * supported for use against Ping Identity, UnboundID, and 204 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 205 * for proprietary functionality or for external specifications that are not 206 * considered stable or mature enough to be guaranteed to work in an 207 * interoperable way with other types of LDAP servers. 208 * </BLOCKQUOTE> 209 */ 210@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 211public final class LDAPModify 212 extends LDAPCommandLineTool 213 implements UnsolicitedNotificationHandler 214{ 215 /** 216 * The column at which output should be wrapped. 217 */ 218 private static final int WRAP_COLUMN = StaticUtils.TERMINAL_WIDTH_COLUMNS - 1; 219 220 221 222 /** 223 * The name of the attribute type used to specify a password in the 224 * authentication password syntax as described in RFC 3112. 225 */ 226 @NotNull private static final String ATTR_AUTH_PASSWORD = "authPassword"; 227 228 229 230 /** 231 * The name of the attribute type used to specify the DN of the soft-deleted 232 * entry to be restored via an undelete operation. 233 */ 234 @NotNull private static final String ATTR_UNDELETE_FROM_DN = 235 "ds-undelete-from-dn"; 236 237 238 239 /** 240 * The name of the attribute type used to specify a password in the 241 * userPassword syntax. 242 */ 243 @NotNull private static final String ATTR_USER_PASSWORD = "userPassword"; 244 245 246 247 /** 248 * The long identifier for the argument used to specify the desired assured 249 * replication local level. 250 */ 251 @NotNull private static final String ARG_ASSURED_REPLICATION_LOCAL_LEVEL = 252 "assuredReplicationLocalLevel"; 253 254 255 256 /** 257 * The long identifier for the argument used to specify the desired assured 258 * replication remote level. 259 */ 260 @NotNull private static final String ARG_ASSURED_REPLICATION_REMOTE_LEVEL = 261 "assuredReplicationRemoteLevel"; 262 263 264 265 /** 266 * The long identifier for the argument used to specify the desired assured 267 * timeout. 268 */ 269 @NotNull private static final String ARG_ASSURED_REPLICATION_TIMEOUT = 270 "assuredReplicationTimeout"; 271 272 273 274 /** 275 * The long identifier for the argument used to specify the path to an LDIF 276 * file containing changes to apply. 277 */ 278 @NotNull private static final String ARG_LDIF_FILE = "ldifFile"; 279 280 281 282 /** 283 * The long identifier for the argument used to specify the simple paged 284 * results page size to use when modifying entries that match a provided 285 * filter. 286 */ 287 @NotNull private static final String ARG_SEARCH_PAGE_SIZE = "searchPageSize"; 288 289 290 291 // The set of arguments supported by this program. 292 @Nullable private BooleanArgument allowUndelete = null; 293 @Nullable private BooleanArgument assuredReplication = null; 294 @Nullable private BooleanArgument authorizationIdentity = null; 295 @Nullable private BooleanArgument clientSideSubtreeDelete = null; 296 @Nullable private BooleanArgument continueOnError = null; 297 @Nullable private BooleanArgument defaultAdd = null; 298 @Nullable private BooleanArgument dryRun = null; 299 @Nullable private BooleanArgument followReferrals = null; 300 @Nullable private BooleanArgument generatePassword = null; 301 @Nullable private BooleanArgument getBackendSetID = null; 302 @Nullable private BooleanArgument getRecentLoginHistory = null; 303 @Nullable private BooleanArgument getServerID = null; 304 @Nullable private BooleanArgument getUserResourceLimits = null; 305 @Nullable private BooleanArgument hardDelete = null; 306 @Nullable private BooleanArgument ignoreNoUserModification = null; 307 @Nullable private BooleanArgument manageDsaIT = null; 308 @Nullable private BooleanArgument nameWithEntryUUID = null; 309 @Nullable private BooleanArgument neverRetry = null; 310 @Nullable private BooleanArgument noOperation = null; 311 @Nullable private BooleanArgument passwordValidationDetails = null; 312 @Nullable private BooleanArgument permissiveModify = null; 313 @Nullable private BooleanArgument purgeCurrentPassword = null; 314 @Nullable private BooleanArgument replicationRepair = null; 315 @Nullable private BooleanArgument retireCurrentPassword = null; 316 @Nullable private BooleanArgument retryFailedOperations = null; 317 @Nullable private BooleanArgument softDelete = null; 318 @Nullable private BooleanArgument stripTrailingSpaces = null; 319 @Nullable private BooleanArgument serverSideSubtreeDelete = null; 320 @Nullable private BooleanArgument suppressReferentialIntegrityUpdates = null; 321 @Nullable private BooleanArgument useAdministrativeSession = null; 322 @Nullable private BooleanArgument useJSONFormattedRequestControls = null; 323 @Nullable private BooleanArgument usePasswordPolicyControl = null; 324 @Nullable private BooleanArgument useTransaction = null; 325 @Nullable private BooleanArgument verbose = null; 326 @Nullable private ControlArgument addControl = null; 327 @Nullable private ControlArgument bindControl = null; 328 @Nullable private ControlArgument deleteControl = null; 329 @Nullable private ControlArgument modifyControl = null; 330 @Nullable private ControlArgument modifyDNControl = null; 331 @Nullable private ControlArgument operationControl = null; 332 @Nullable private DNArgument modifyEntryWithDN = null; 333 @Nullable private DNArgument proxyV1As = null; 334 @Nullable private DNArgument uniquenessBaseDN = null; 335 @Nullable private DurationArgument assuredReplicationTimeout = null; 336 @Nullable private FileArgument encryptionPassphraseFile = null; 337 @Nullable private FileArgument ldifFile = null; 338 @Nullable private FileArgument modifyEntriesMatchingFiltersFromFile = null; 339 @Nullable private FileArgument modifyEntriesWithDNsFromFile = null; 340 @Nullable private FileArgument rejectFile = null; 341 @Nullable private FilterArgument assertionFilter = null; 342 @Nullable private FilterArgument modifyEntriesMatchingFilter = null; 343 @Nullable private FilterArgument uniquenessFilter = null; 344 @Nullable private IntegerArgument ratePerSecond = null; 345 @Nullable private IntegerArgument searchPageSize = null; 346 @Nullable private StringArgument assuredReplicationLocalLevel = null; 347 @Nullable private StringArgument assuredReplicationRemoteLevel = null; 348 @Nullable private StringArgument characterSet = null; 349 @Nullable private StringArgument getAuthorizationEntryAttribute = null; 350 @Nullable private StringArgument multiUpdateErrorBehavior = null; 351 @Nullable private StringArgument operationPurpose = null; 352 @Nullable private StringArgument passwordUpdateBehavior = null; 353 @Nullable private StringArgument postReadAttribute = null; 354 @Nullable private StringArgument preReadAttribute = null; 355 @Nullable private StringArgument proxyAs = null; 356 @Nullable private StringArgument routeToBackendSet = null; 357 @Nullable private StringArgument routeToServer = null; 358 @Nullable private StringArgument suppressOperationalAttributeUpdates = null; 359 @Nullable private StringArgument uniquenessAttribute = null; 360 @Nullable private StringArgument uniquenessMultipleAttributeBehavior = null; 361 @Nullable private StringArgument uniquenessPostCommitValidationLevel = null; 362 @Nullable private StringArgument uniquenessPreCommitValidationLevel = null; 363 364 // Indicates whether we've written anything to the reject writer yet. 365 @NotNull private final AtomicBoolean rejectWritten; 366 367 // The input stream from to use for standard input. 368 @NotNull private final InputStream in; 369 370 // The route to backend set request controls to include in write requests. 371 @NotNull private final List<RouteToBackendSetRequestControl> 372 routeToBackendSetRequestControls = new ArrayList<>(10); 373 374 375 376 /** 377 * Runs this tool with the provided command-line arguments. It will use the 378 * JVM-default streams for standard input, output, and error. 379 * 380 * @param args The command-line arguments to provide to this program. 381 */ 382 public static void main(@NotNull final String... args) 383 { 384 final ResultCode resultCode = main(System.in, System.out, System.err, args); 385 if (resultCode != ResultCode.SUCCESS) 386 { 387 System.exit(Math.min(resultCode.intValue(), 255)); 388 } 389 } 390 391 392 393 /** 394 * Runs this tool with the provided streams and command-line arguments. 395 * 396 * @param in The input stream to use for standard input. If this is 397 * {@code null}, then no standard input will be used. 398 * @param out The output stream to use for standard output. If this is 399 * {@code null}, then standard output will be suppressed. 400 * @param err The output stream to use for standard error. If this is 401 * {@code null}, then standard error will be suppressed. 402 * @param args The command-line arguments provided to this program. 403 * 404 * @return The result code obtained when running the tool. Any result code 405 * other than {@link ResultCode#SUCCESS} indicates an error. 406 */ 407 @NotNull() 408 public static ResultCode main(@Nullable final InputStream in, 409 @Nullable final OutputStream out, 410 @Nullable final OutputStream err, 411 @NotNull final String... args) 412 { 413 final LDAPModify tool = new LDAPModify(in, out, err); 414 return tool.runTool(args); 415 } 416 417 418 419 /** 420 * Creates a new instance of this tool with the provided streams. Standard 421 * input will not be available. 422 * 423 * @param out The output stream to use for standard output. If this is 424 * {@code null}, then standard output will be suppressed. 425 * @param err The output stream to use for standard error. If this is 426 * {@code null}, then standard error will be suppressed. 427 */ 428 public LDAPModify(@Nullable final OutputStream out, 429 @Nullable final OutputStream err) 430 { 431 this(null, out, err); 432 } 433 434 435 436 /** 437 * Creates a new instance of this tool with the provided streams. 438 * 439 * @param in The input stream to use for standard input. If this is 440 * {@code null}, then no standard input will be used. 441 * @param out The output stream to use for standard output. If this is 442 * {@code null}, then standard output will be suppressed. 443 * @param err The output stream to use for standard error. If this is 444 * {@code null}, then standard error will be suppressed. 445 */ 446 public LDAPModify(@Nullable final InputStream in, 447 @Nullable final OutputStream out, 448 @Nullable final OutputStream err) 449 { 450 super(out, err); 451 452 if (in == null) 453 { 454 this.in = new ByteArrayInputStream(StaticUtils.NO_BYTES); 455 } 456 else 457 { 458 this.in = in; 459 } 460 461 462 rejectWritten = new AtomicBoolean(false); 463 } 464 465 466 467 /** 468 * {@inheritDoc} 469 */ 470 @Override() 471 @NotNull() 472 public String getToolName() 473 { 474 return "ldapmodify"; 475 } 476 477 478 479 /** 480 * {@inheritDoc} 481 */ 482 @Override() 483 @NotNull() 484 public String getToolDescription() 485 { 486 return INFO_LDAPMODIFY_TOOL_DESCRIPTION.get(ARG_LDIF_FILE); 487 } 488 489 490 491 /** 492 * {@inheritDoc} 493 */ 494 @Override() 495 @NotNull() 496 public String getToolVersion() 497 { 498 return Version.NUMERIC_VERSION_STRING; 499 } 500 501 502 503 /** 504 * {@inheritDoc} 505 */ 506 @Override() 507 public boolean supportsInteractiveMode() 508 { 509 return true; 510 } 511 512 513 514 /** 515 * {@inheritDoc} 516 */ 517 @Override() 518 public boolean defaultsToInteractiveMode() 519 { 520 return true; 521 } 522 523 524 525 /** 526 * {@inheritDoc} 527 */ 528 @Override() 529 public boolean supportsPropertiesFile() 530 { 531 return true; 532 } 533 534 535 536 /** 537 * {@inheritDoc} 538 */ 539 @Override() 540 public boolean supportsOutputFile() 541 { 542 return true; 543 } 544 545 546 547 /** 548 * {@inheritDoc} 549 */ 550 @Override() 551 protected boolean defaultToPromptForBindPassword() 552 { 553 return true; 554 } 555 556 557 558 /** 559 * {@inheritDoc} 560 */ 561 @Override() 562 protected boolean includeAlternateLongIdentifiers() 563 { 564 return true; 565 } 566 567 568 569 /** 570 * {@inheritDoc} 571 */ 572 @Override() 573 protected boolean supportsSSLDebugging() 574 { 575 return true; 576 } 577 578 579 580 /** 581 * {@inheritDoc} 582 */ 583 @Override() 584 protected boolean logToolInvocationByDefault() 585 { 586 return true; 587 } 588 589 590 591 /** 592 * {@inheritDoc} 593 */ 594 @Override() 595 public void addNonLDAPArguments(@NotNull final ArgumentParser parser) 596 throws ArgumentException 597 { 598 ldifFile = new FileArgument('f', ARG_LDIF_FILE, false, -1, null, 599 INFO_LDAPMODIFY_ARG_DESCRIPTION_LDIF_FILE.get(), true, true, true, 600 false); 601 ldifFile.addLongIdentifier("filename", true); 602 ldifFile.addLongIdentifier("ldif-file", true); 603 ldifFile.addLongIdentifier("file-name", true); 604 ldifFile.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_DATA.get()); 605 parser.addArgument(ldifFile); 606 607 608 encryptionPassphraseFile = new FileArgument(null, 609 "encryptionPassphraseFile", false, 1, null, 610 INFO_LDAPMODIFY_ARG_DESCRIPTION_ENCRYPTION_PW_FILE.get(), true, true, 611 true, false); 612 encryptionPassphraseFile.addLongIdentifier("encryption-passphrase-file", 613 true); 614 encryptionPassphraseFile.addLongIdentifier("encryptionPasswordFile", true); 615 encryptionPassphraseFile.addLongIdentifier("encryption-password-file", 616 true); 617 encryptionPassphraseFile.setArgumentGroupName( 618 INFO_LDAPMODIFY_ARG_GROUP_DATA.get()); 619 parser.addArgument(encryptionPassphraseFile); 620 621 622 characterSet = new StringArgument('i', "characterSet", false, 1, 623 INFO_LDAPMODIFY_PLACEHOLDER_CHARSET.get(), 624 INFO_LDAPMODIFY_ARG_DESCRIPTION_CHARACTER_SET.get(), "UTF-8"); 625 characterSet.addLongIdentifier("encoding", true); 626 characterSet.addLongIdentifier("character-set", true); 627 characterSet.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_DATA.get()); 628 parser.addArgument(characterSet); 629 630 631 rejectFile = new FileArgument('R', "rejectFile", false, 1, null, 632 INFO_LDAPMODIFY_ARG_DESCRIPTION_REJECT_FILE.get(), false, true, true, 633 false); 634 rejectFile.addLongIdentifier("reject-file", true); 635 rejectFile.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_DATA.get()); 636 parser.addArgument(rejectFile); 637 638 639 verbose = new BooleanArgument('v', "verbose", 1, 640 INFO_LDAPMODIFY_ARG_DESCRIPTION_VERBOSE.get()); 641 verbose.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_DATA.get()); 642 parser.addArgument(verbose); 643 644 645 modifyEntriesMatchingFilter = new FilterArgument(null, 646 "modifyEntriesMatchingFilter", false, 0, null, 647 INFO_LDAPMODIFY_ARG_DESCRIPTION_MODIFY_ENTRIES_MATCHING_FILTER.get( 648 ARG_SEARCH_PAGE_SIZE)); 649 modifyEntriesMatchingFilter.addLongIdentifier( 650 "modify-entries-matching-filter", true); 651 modifyEntriesMatchingFilter.setArgumentGroupName( 652 INFO_LDAPMODIFY_ARG_GROUP_DATA.get()); 653 parser.addArgument(modifyEntriesMatchingFilter); 654 655 656 modifyEntriesMatchingFiltersFromFile = new FileArgument(null, 657 "modifyEntriesMatchingFiltersFromFile", false, 0, null, 658 INFO_LDAPMODIFY_ARG_DESCRIPTION_MODIFY_FILTER_FILE.get( 659 ARG_SEARCH_PAGE_SIZE), true, false, true, false); 660 modifyEntriesMatchingFiltersFromFile.addLongIdentifier( 661 "modify-entries-matching-filters-from-file", true); 662 modifyEntriesMatchingFiltersFromFile.setArgumentGroupName( 663 INFO_LDAPMODIFY_ARG_GROUP_DATA.get()); 664 parser.addArgument(modifyEntriesMatchingFiltersFromFile); 665 666 667 modifyEntryWithDN = new DNArgument(null, "modifyEntryWithDN", false, 0, 668 null, INFO_LDAPMODIFY_ARG_DESCRIPTION_MODIFY_ENTRY_DN.get()); 669 modifyEntryWithDN.addLongIdentifier("modify-entry-with-dn", true); 670 modifyEntryWithDN.setArgumentGroupName( 671 INFO_LDAPMODIFY_ARG_GROUP_DATA.get()); 672 parser.addArgument(modifyEntryWithDN); 673 674 675 modifyEntriesWithDNsFromFile = new FileArgument(null, 676 "modifyEntriesWithDNsFromFile", false, 0, 677 null, INFO_LDAPMODIFY_ARG_DESCRIPTION_MODIFY_DN_FILE.get(), true, 678 false, true, false); 679 modifyEntriesWithDNsFromFile.addLongIdentifier( 680 "modify-entries-with-dns-from-file", true); 681 modifyEntriesWithDNsFromFile.setArgumentGroupName( 682 INFO_LDAPMODIFY_ARG_GROUP_DATA.get()); 683 parser.addArgument(modifyEntriesWithDNsFromFile); 684 685 686 searchPageSize = new IntegerArgument(null, ARG_SEARCH_PAGE_SIZE, false, 1, 687 null, 688 INFO_LDAPMODIFY_ARG_DESCRIPTION_SEARCH_PAGE_SIZE.get( 689 modifyEntriesMatchingFilter.getIdentifierString(), 690 modifyEntriesMatchingFiltersFromFile.getIdentifierString()), 691 1, Integer.MAX_VALUE); 692 searchPageSize.addLongIdentifier("search-page-size", true); 693 searchPageSize.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_DATA.get()); 694 parser.addArgument(searchPageSize); 695 696 697 // NOTE: The retryFailedOperations argument is now hidden, as we will retry 698 // operations by default. The neverRetry argument can be used to disable 699 // this. 700 retryFailedOperations = new BooleanArgument(null, "retryFailedOperations", 701 1, INFO_LDAPMODIFY_ARG_DESCRIPTION_RETRY_FAILED_OPERATIONS.get()); 702 retryFailedOperations.addLongIdentifier("retry-failed-operations", true); 703 retryFailedOperations.setArgumentGroupName( 704 INFO_LDAPMODIFY_ARG_GROUP_OPS.get()); 705 retryFailedOperations.setHidden(true); 706 parser.addArgument(retryFailedOperations); 707 708 709 neverRetry = new BooleanArgument(null, "neverRetry", 1, 710 INFO_LDAPMODIFY_ARG_DESC_NEVER_RETRY.get()); 711 neverRetry.addLongIdentifier("never-retry", true); 712 neverRetry.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_OPS.get()); 713 parser.addArgument(neverRetry); 714 715 716 dryRun = new BooleanArgument('n', "dryRun", 1, 717 INFO_LDAPMODIFY_ARG_DESCRIPTION_DRY_RUN.get()); 718 dryRun.addLongIdentifier("dry-run", true); 719 dryRun.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_OPS.get()); 720 parser.addArgument(dryRun); 721 722 723 defaultAdd = new BooleanArgument('a', "defaultAdd", 1, 724 INFO_LDAPMODIFY_ARG_DESCRIPTION_DEFAULT_ADD.get()); 725 defaultAdd.addLongIdentifier("default-add", true); 726 defaultAdd.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_OPS.get()); 727 parser.addArgument(defaultAdd); 728 729 730 continueOnError = new BooleanArgument('c', "continueOnError", 1, 731 INFO_LDAPMODIFY_ARG_DESCRIPTION_CONTINUE_ON_ERROR.get()); 732 continueOnError.addLongIdentifier("continue-on-error", true); 733 continueOnError.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_OPS.get()); 734 parser.addArgument(continueOnError); 735 736 737 stripTrailingSpaces = new BooleanArgument(null, "stripTrailingSpaces", 1, 738 INFO_LDAPMODIFY_ARG_DESCRIPTION_STRIP_TRAILING_SPACES.get()); 739 stripTrailingSpaces.addLongIdentifier("strip-trailing-spaces", true); 740 stripTrailingSpaces.setArgumentGroupName( 741 INFO_LDAPMODIFY_ARG_GROUP_DATA.get()); 742 parser.addArgument(stripTrailingSpaces); 743 744 745 746 followReferrals = new BooleanArgument(null, "followReferrals", 1, 747 INFO_LDAPMODIFY_ARG_DESCRIPTION_FOLLOW_REFERRALS.get()); 748 followReferrals.addLongIdentifier("follow-referrals", true); 749 followReferrals.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_OPS.get()); 750 parser.addArgument(followReferrals); 751 752 753 proxyAs = new StringArgument('Y', "proxyAs", false, 1, 754 INFO_PLACEHOLDER_AUTHZID.get(), 755 INFO_LDAPMODIFY_ARG_DESCRIPTION_PROXY_AS.get()); 756 proxyAs.addLongIdentifier("proxyV2As", true); 757 proxyAs.addLongIdentifier("proxy-as", true); 758 proxyAs.addLongIdentifier("proxy-v2-as", true); 759 proxyAs.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 760 parser.addArgument(proxyAs); 761 762 proxyV1As = new DNArgument(null, "proxyV1As", false, 1, null, 763 INFO_LDAPMODIFY_ARG_DESCRIPTION_PROXY_V1_AS.get()); 764 proxyV1As.addLongIdentifier("proxy-v1-as", true); 765 proxyV1As.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 766 parser.addArgument(proxyV1As); 767 768 769 useAdministrativeSession = new BooleanArgument(null, 770 "useAdministrativeSession", 1, 771 INFO_LDAPMODIFY_ARG_DESCRIPTION_USE_ADMIN_SESSION.get()); 772 useAdministrativeSession.addLongIdentifier("use-administrative-session", 773 true); 774 useAdministrativeSession.setArgumentGroupName( 775 INFO_LDAPMODIFY_ARG_GROUP_OPS.get()); 776 parser.addArgument(useAdministrativeSession); 777 778 779 operationPurpose = new StringArgument(null, "operationPurpose", false, 1, 780 INFO_PLACEHOLDER_PURPOSE.get(), 781 INFO_LDAPMODIFY_ARG_DESCRIPTION_OPERATION_PURPOSE.get()); 782 operationPurpose.addLongIdentifier("operation-purpose", true); 783 operationPurpose.setArgumentGroupName( 784 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 785 parser.addArgument(operationPurpose); 786 787 788 manageDsaIT = new BooleanArgument(null, "useManageDsaIT", 1, 789 INFO_LDAPMODIFY_ARG_DESCRIPTION_MANAGE_DSA_IT.get()); 790 manageDsaIT.addLongIdentifier("manageDsaIT", true); 791 manageDsaIT.addLongIdentifier("use-manage-dsa-it", true); 792 manageDsaIT.addLongIdentifier("manage-dsa-it", true); 793 manageDsaIT.setArgumentGroupName( 794 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 795 parser.addArgument(manageDsaIT); 796 797 798 useTransaction = new BooleanArgument(null, "useTransaction", 1, 799 INFO_LDAPMODIFY_ARG_DESCRIPTION_USE_TRANSACTION.get()); 800 useTransaction.addLongIdentifier("use-transaction", true); 801 useTransaction.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_OPS.get()); 802 parser.addArgument(useTransaction); 803 804 805 final Set<String> multiUpdateErrorBehaviorAllowedValues = 806 StaticUtils.setOf("atomic", "abort-on-error", "continue-on-error"); 807 multiUpdateErrorBehavior = new StringArgument(null, 808 "multiUpdateErrorBehavior", false, 1, 809 "{atomic|abort-on-error|continue-on-error}", 810 INFO_LDAPMODIFY_ARG_DESCRIPTION_MULTI_UPDATE_ERROR_BEHAVIOR.get(), 811 multiUpdateErrorBehaviorAllowedValues); 812 multiUpdateErrorBehavior.addLongIdentifier("multi-update-error-behavior", 813 true); 814 multiUpdateErrorBehavior.setArgumentGroupName( 815 INFO_LDAPMODIFY_ARG_GROUP_OPS.get()); 816 parser.addArgument(multiUpdateErrorBehavior); 817 818 819 assertionFilter = new FilterArgument(null, "assertionFilter", false, 1, 820 INFO_PLACEHOLDER_FILTER.get(), 821 INFO_LDAPMODIFY_ARG_DESCRIPTION_ASSERTION_FILTER.get()); 822 assertionFilter.addLongIdentifier("assertion-filter", true); 823 assertionFilter.setArgumentGroupName( 824 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 825 parser.addArgument(assertionFilter); 826 827 828 authorizationIdentity = new BooleanArgument('E', 829 "authorizationIdentity", 1, 830 INFO_LDAPMODIFY_ARG_DESCRIPTION_AUTHZ_IDENTITY.get()); 831 authorizationIdentity.addLongIdentifier("reportAuthzID", true); 832 authorizationIdentity.addLongIdentifier("authorization-identity", true); 833 authorizationIdentity.addLongIdentifier("report-authzID", true); 834 authorizationIdentity.addLongIdentifier("report-authz-id", true); 835 authorizationIdentity.setArgumentGroupName( 836 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 837 parser.addArgument(authorizationIdentity); 838 839 840 generatePassword = new BooleanArgument(null, "generatePassword", 1, 841 INFO_LDAPMODIFY_ARG_DESCRIPTION_GENERATE_PASSWORD.get()); 842 generatePassword.addLongIdentifier("generatePW", true); 843 generatePassword.addLongIdentifier("generate-password", true); 844 generatePassword.addLongIdentifier("generate-pw", true); 845 generatePassword.setArgumentGroupName( 846 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 847 parser.addArgument(generatePassword); 848 849 850 getAuthorizationEntryAttribute = new StringArgument(null, 851 "getAuthorizationEntryAttribute", false, 0, 852 INFO_PLACEHOLDER_ATTR.get(), 853 INFO_LDAPMODIFY_ARG_DESCRIPTION_GET_AUTHZ_ENTRY_ATTR.get()); 854 getAuthorizationEntryAttribute.addLongIdentifier( 855 "get-authorization-entry-attribute", true); 856 getAuthorizationEntryAttribute.setArgumentGroupName( 857 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 858 parser.addArgument(getAuthorizationEntryAttribute); 859 860 861 getBackendSetID = new BooleanArgument(null, "getBackendSetID", 862 1, INFO_LDAPMODIFY_ARG_DESCRIPTION_GET_BACKEND_SET_ID.get()); 863 getBackendSetID.addLongIdentifier("get-backend-set-id", true); 864 getBackendSetID.setArgumentGroupName( 865 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 866 parser.addArgument(getBackendSetID); 867 868 869 getRecentLoginHistory = new BooleanArgument(null, "getRecentLoginHistory", 870 1, INFO_LDAPMODIFY_ARG_DESCRIPTION_GET_RECENT_LOGIN_HISTORY.get()); 871 getRecentLoginHistory.addLongIdentifier("get-recent-login-history", true); 872 getRecentLoginHistory.setArgumentGroupName( 873 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 874 parser.addArgument(getRecentLoginHistory); 875 876 877 getServerID = new BooleanArgument(null, "getServerID", 878 1, INFO_LDAPMODIFY_ARG_DESCRIPTION_GET_SERVER_ID.get()); 879 getServerID.addLongIdentifier("get-server-id", true); 880 getServerID.setArgumentGroupName( 881 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 882 parser.addArgument(getServerID); 883 884 885 getUserResourceLimits = new BooleanArgument(null, "getUserResourceLimits", 886 1, INFO_LDAPMODIFY_ARG_DESCRIPTION_GET_USER_RESOURCE_LIMITS.get()); 887 getUserResourceLimits.addLongIdentifier("get-user-resource-limits", true); 888 getUserResourceLimits.setArgumentGroupName( 889 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 890 parser.addArgument(getUserResourceLimits); 891 892 893 ignoreNoUserModification = new BooleanArgument(null, 894 "ignoreNoUserModification", 1, 895 INFO_LDAPMODIFY_ARG_DESCRIPTION_IGNORE_NO_USER_MOD.get()); 896 ignoreNoUserModification.addLongIdentifier("ignore-no-user-modification", 897 true); 898 ignoreNoUserModification.setArgumentGroupName( 899 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 900 parser.addArgument(ignoreNoUserModification); 901 902 903 preReadAttribute = new StringArgument(null, "preReadAttribute", false, -1, 904 INFO_PLACEHOLDER_ATTR.get(), 905 INFO_LDAPMODIFY_ARG_DESCRIPTION_PRE_READ_ATTRIBUTE.get()); 906 preReadAttribute.addLongIdentifier("preReadAttributes", true); 907 preReadAttribute.addLongIdentifier("pre-read-attribute", true); 908 preReadAttribute.addLongIdentifier("pre-read-attributes", true); 909 preReadAttribute.setArgumentGroupName( 910 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 911 parser.addArgument(preReadAttribute); 912 913 914 postReadAttribute = new StringArgument(null, "postReadAttribute", false, 915 -1, INFO_PLACEHOLDER_ATTR.get(), 916 INFO_LDAPMODIFY_ARG_DESCRIPTION_POST_READ_ATTRIBUTE.get()); 917 postReadAttribute.addLongIdentifier("postReadAttributes", true); 918 postReadAttribute.addLongIdentifier("post-read-attribute", true); 919 postReadAttribute.addLongIdentifier("post-read-attributes", true); 920 postReadAttribute.setArgumentGroupName( 921 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 922 parser.addArgument(postReadAttribute); 923 924 925 routeToBackendSet = new StringArgument(null, "routeToBackendSet", 926 false, 0, 927 INFO_LDAPMODIFY_ARG_PLACEHOLDER_ROUTE_TO_BACKEND_SET.get(), 928 INFO_LDAPMODIFY_ARG_DESCRIPTION_ROUTE_TO_BACKEND_SET.get()); 929 routeToBackendSet.addLongIdentifier("route-to-backend-set", true); 930 routeToBackendSet.setArgumentGroupName( 931 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 932 parser.addArgument(routeToBackendSet); 933 934 935 routeToServer = new StringArgument(null, "routeToServer", false, 1, 936 INFO_LDAPMODIFY_ARG_PLACEHOLDER_ROUTE_TO_SERVER.get(), 937 INFO_LDAPMODIFY_ARG_DESCRIPTION_ROUTE_TO_SERVER.get()); 938 routeToServer.addLongIdentifier("route-to-server", true); 939 routeToServer.setArgumentGroupName( 940 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 941 parser.addArgument(routeToServer); 942 943 944 assuredReplication = new BooleanArgument(null, "useAssuredReplication", 1, 945 INFO_LDAPMODIFY_ARG_DESCRIPTION_ASSURED_REPLICATION.get( 946 ARG_ASSURED_REPLICATION_LOCAL_LEVEL, 947 ARG_ASSURED_REPLICATION_REMOTE_LEVEL, 948 ARG_ASSURED_REPLICATION_TIMEOUT)); 949 assuredReplication.addLongIdentifier("assuredReplication", true); 950 assuredReplication.addLongIdentifier("use-assured-replication", true); 951 assuredReplication.addLongIdentifier("assured-replication", true); 952 assuredReplication.setArgumentGroupName( 953 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 954 parser.addArgument(assuredReplication); 955 956 957 final Set<String> assuredReplicationLocalLevelAllowedValues = 958 StaticUtils.setOf("none", "received-any-server", 959 "processed-all-servers"); 960 assuredReplicationLocalLevel = new StringArgument(null, 961 ARG_ASSURED_REPLICATION_LOCAL_LEVEL, false, 1, 962 INFO_PLACEHOLDER_LEVEL.get(), 963 INFO_LDAPMODIFY_ARG_DESCRIPTION_ASSURED_REPL_LOCAL_LEVEL.get( 964 assuredReplication.getIdentifierString()), 965 assuredReplicationLocalLevelAllowedValues); 966 assuredReplicationLocalLevel.addLongIdentifier( 967 "assured-replication-local-level", true); 968 assuredReplicationLocalLevel.setArgumentGroupName( 969 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 970 parser.addArgument(assuredReplicationLocalLevel); 971 972 973 final Set<String> assuredReplicationRemoteLevelAllowedValues = 974 StaticUtils.setOf("none", "received-any-remote-location", 975 "received-all-remote-locations", "processed-all-remote-servers"); 976 assuredReplicationRemoteLevel = new StringArgument(null, 977 ARG_ASSURED_REPLICATION_REMOTE_LEVEL, false, 1, 978 INFO_PLACEHOLDER_LEVEL.get(), 979 INFO_LDAPMODIFY_ARG_DESCRIPTION_ASSURED_REPL_REMOTE_LEVEL.get( 980 assuredReplication.getIdentifierString()), 981 assuredReplicationRemoteLevelAllowedValues); 982 assuredReplicationRemoteLevel.addLongIdentifier( 983 "assured-replication-remote-level", true); 984 assuredReplicationRemoteLevel.setArgumentGroupName( 985 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 986 parser.addArgument(assuredReplicationRemoteLevel); 987 988 989 assuredReplicationTimeout = new DurationArgument(null, 990 ARG_ASSURED_REPLICATION_TIMEOUT, false, INFO_PLACEHOLDER_TIMEOUT.get(), 991 INFO_LDAPMODIFY_ARG_DESCRIPTION_ASSURED_REPL_TIMEOUT.get( 992 assuredReplication.getIdentifierString())); 993 assuredReplicationTimeout.setArgumentGroupName( 994 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 995 parser.addArgument(assuredReplicationTimeout); 996 997 998 replicationRepair = new BooleanArgument(null, "replicationRepair", 999 1, INFO_LDAPMODIFY_ARG_DESCRIPTION_REPLICATION_REPAIR.get()); 1000 replicationRepair.addLongIdentifier("replication-repair", true); 1001 replicationRepair.setArgumentGroupName( 1002 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1003 parser.addArgument(replicationRepair); 1004 1005 1006 nameWithEntryUUID = new BooleanArgument(null, "nameWithEntryUUID", 1, 1007 INFO_LDAPMODIFY_ARG_DESCRIPTION_NAME_WITH_ENTRY_UUID.get()); 1008 nameWithEntryUUID.addLongIdentifier("name-with-entryUUID", true); 1009 nameWithEntryUUID.addLongIdentifier("name-with-entry-uuid", true); 1010 nameWithEntryUUID.setArgumentGroupName( 1011 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1012 parser.addArgument(nameWithEntryUUID); 1013 1014 1015 noOperation = new BooleanArgument(null, "noOperation", 1, 1016 INFO_LDAPMODIFY_ARG_DESCRIPTION_NO_OPERATION.get()); 1017 noOperation.addLongIdentifier("noOp", true); 1018 noOperation.addLongIdentifier("no-operation", true); 1019 noOperation.addLongIdentifier("no-op", true); 1020 noOperation.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1021 parser.addArgument(noOperation); 1022 1023 1024 passwordUpdateBehavior = new StringArgument(null, 1025 "passwordUpdateBehavior", false, 0, 1026 INFO_LDAPMODIFY_PLACEHOLDER_NAME_EQUALS_VALUE.get(), 1027 INFO_LDAPMODIFY_ARG_DESCRIPTION_PW_UPDATE_BEHAVIOR.get()); 1028 passwordUpdateBehavior.addLongIdentifier("password-update-behavior", true); 1029 passwordUpdateBehavior.setArgumentGroupName( 1030 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1031 parser.addArgument(passwordUpdateBehavior); 1032 1033 passwordValidationDetails = new BooleanArgument(null, 1034 "getPasswordValidationDetails", 1, 1035 INFO_LDAPMODIFY_ARG_DESCRIPTION_PASSWORD_VALIDATION_DETAILS.get( 1036 ATTR_USER_PASSWORD, ATTR_AUTH_PASSWORD)); 1037 passwordValidationDetails.addLongIdentifier("passwordValidationDetails", 1038 true); 1039 passwordValidationDetails.addLongIdentifier( 1040 "get-password-validation-details", true); 1041 passwordValidationDetails.addLongIdentifier("password-validation-details", 1042 true); 1043 passwordValidationDetails.setArgumentGroupName( 1044 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1045 parser.addArgument(passwordValidationDetails); 1046 1047 1048 permissiveModify = new BooleanArgument(null, "permissiveModify", 1049 1, INFO_LDAPMODIFY_ARG_DESCRIPTION_PERMISSIVE_MODIFY.get()); 1050 permissiveModify.addLongIdentifier("permissive-modify", true); 1051 permissiveModify.setArgumentGroupName( 1052 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1053 parser.addArgument(permissiveModify); 1054 1055 1056 clientSideSubtreeDelete = new BooleanArgument(null, 1057 "clientSideSubtreeDelete", 1, 1058 INFO_LDAPMODIFY_ARG_DESCRIPTION_CLIENT_SIDE_SUBTREE_DELETE.get()); 1059 clientSideSubtreeDelete.addLongIdentifier("client-side-subtree-delete", 1060 true); 1061 clientSideSubtreeDelete.setArgumentGroupName( 1062 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1063 parser.addArgument(clientSideSubtreeDelete); 1064 1065 1066 serverSideSubtreeDelete = new BooleanArgument(null, 1067 "serverSideSubtreeDelete", 1, 1068 INFO_LDAPMODIFY_ARG_DESCRIPTION_SERVER_SIDE_SUBTREE_DELETE.get()); 1069 serverSideSubtreeDelete.addLongIdentifier("server-side-subtree-delete", 1070 true); 1071 serverSideSubtreeDelete.addLongIdentifier("subtreeDelete", true); 1072 serverSideSubtreeDelete.addLongIdentifier("subtree-delete", true); 1073 serverSideSubtreeDelete.addLongIdentifier("subtreeDeleteControl", true); 1074 serverSideSubtreeDelete.addLongIdentifier("subtree-delete-control", true); 1075 serverSideSubtreeDelete.addLongIdentifier("useSubtreeDeleteControl", true); 1076 serverSideSubtreeDelete.addLongIdentifier("use-subtree-delete-control", 1077 true); 1078 serverSideSubtreeDelete.setArgumentGroupName( 1079 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1080 parser.addArgument(serverSideSubtreeDelete); 1081 1082 1083 softDelete = new BooleanArgument('s', "softDelete", 1, 1084 INFO_LDAPMODIFY_ARG_DESCRIPTION_SOFT_DELETE.get()); 1085 softDelete.addLongIdentifier("useSoftDelete", true); 1086 softDelete.addLongIdentifier("soft-delete", true); 1087 softDelete.addLongIdentifier("use-soft-delete", true); 1088 softDelete.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1089 parser.addArgument(softDelete); 1090 1091 1092 hardDelete = new BooleanArgument(null, "hardDelete", 1, 1093 INFO_LDAPMODIFY_ARG_DESCRIPTION_HARD_DELETE.get()); 1094 hardDelete.addLongIdentifier("hard-delete", true); 1095 hardDelete.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1096 parser.addArgument(hardDelete); 1097 1098 1099 allowUndelete = new BooleanArgument(null, "allowUndelete", 1, 1100 INFO_LDAPMODIFY_ARG_DESCRIPTION_ALLOW_UNDELETE.get( 1101 ATTR_UNDELETE_FROM_DN)); 1102 allowUndelete.addLongIdentifier("allow-undelete", true); 1103 allowUndelete.setArgumentGroupName( 1104 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1105 parser.addArgument(allowUndelete); 1106 1107 1108 retireCurrentPassword = new BooleanArgument(null, "retireCurrentPassword", 1109 1, 1110 INFO_LDAPMODIFY_ARG_DESCRIPTION_RETIRE_CURRENT_PASSWORD.get( 1111 ATTR_USER_PASSWORD, ATTR_AUTH_PASSWORD)); 1112 retireCurrentPassword.addLongIdentifier("retire-current-password", true); 1113 retireCurrentPassword.setArgumentGroupName( 1114 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1115 parser.addArgument(retireCurrentPassword); 1116 1117 1118 purgeCurrentPassword = new BooleanArgument(null, "purgeCurrentPassword", 1, 1119 INFO_LDAPMODIFY_ARG_DESCRIPTION_PURGE_CURRENT_PASSWORD.get( 1120 ATTR_USER_PASSWORD, ATTR_AUTH_PASSWORD)); 1121 purgeCurrentPassword.addLongIdentifier("purge-current-password", true); 1122 purgeCurrentPassword.setArgumentGroupName( 1123 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1124 parser.addArgument(purgeCurrentPassword); 1125 1126 1127 final Set<String> suppressOperationalAttributeUpdatesAllowedValues = 1128 StaticUtils.setOf("last-access-time", "last-login-time", 1129 "last-login-ip", "lastmod"); 1130 suppressOperationalAttributeUpdates = new StringArgument(null, 1131 "suppressOperationalAttributeUpdates", false, -1, 1132 INFO_PLACEHOLDER_ATTR.get(), 1133 INFO_LDAPMODIFY_ARG_DESCRIPTION_SUPPRESS_OP_ATTR_UPDATES.get(), 1134 suppressOperationalAttributeUpdatesAllowedValues); 1135 suppressOperationalAttributeUpdates.addLongIdentifier( 1136 "suppress-operational-attribute-updates", true); 1137 suppressOperationalAttributeUpdates.setArgumentGroupName( 1138 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1139 parser.addArgument(suppressOperationalAttributeUpdates); 1140 1141 1142 suppressReferentialIntegrityUpdates = new BooleanArgument(null, 1143 "suppressReferentialIntegrityUpdates", 1, 1144 INFO_LDAPMODIFY_ARG_DESCRIPTION_SUPPRESS_REFERINT_UPDATES.get()); 1145 suppressReferentialIntegrityUpdates.addLongIdentifier( 1146 "suppress-referential-integrity-updates", true); 1147 suppressReferentialIntegrityUpdates.setArgumentGroupName( 1148 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1149 parser.addArgument(suppressReferentialIntegrityUpdates); 1150 1151 1152 usePasswordPolicyControl = new BooleanArgument(null, 1153 "usePasswordPolicyControl", 1, 1154 INFO_LDAPMODIFY_ARG_DESCRIPTION_PASSWORD_POLICY.get()); 1155 usePasswordPolicyControl.addLongIdentifier("use-password-policy-control", 1156 true); 1157 usePasswordPolicyControl.setArgumentGroupName( 1158 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1159 parser.addArgument(usePasswordPolicyControl); 1160 1161 1162 uniquenessAttribute = new StringArgument(null, "uniquenessAttribute", false, 1163 0, INFO_PLACEHOLDER_ATTR.get(), 1164 INFO_LDAPMODIFY_ARG_DESCRIPTION_UNIQUE_ATTR.get()); 1165 uniquenessAttribute.addLongIdentifier("uniquenessAttributeType", true); 1166 uniquenessAttribute.addLongIdentifier("uniqueAttribute", true); 1167 uniquenessAttribute.addLongIdentifier("uniqueAttributeType", true); 1168 uniquenessAttribute.addLongIdentifier("uniqueness-attribute", true); 1169 uniquenessAttribute.addLongIdentifier("uniqueness-attribute-type", true); 1170 uniquenessAttribute.addLongIdentifier("unique-attribute", true); 1171 uniquenessAttribute.addLongIdentifier("unique-attribute-type", true); 1172 uniquenessAttribute.setArgumentGroupName( 1173 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1174 parser.addArgument(uniquenessAttribute); 1175 1176 1177 uniquenessFilter = new FilterArgument(null, "uniquenessFilter", false, 1, 1178 null, INFO_LDAPMODIFY_ARG_DESCRIPTION_UNIQUE_FILTER.get()); 1179 uniquenessFilter.addLongIdentifier("uniqueness-filter", true); 1180 uniquenessFilter.setArgumentGroupName( 1181 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1182 parser.addArgument(uniquenessFilter); 1183 1184 1185 uniquenessBaseDN = new DNArgument(null, "uniquenessBaseDN", false, 1, null, 1186 INFO_LDAPMODIFY_ARG_DESCRIPTION_UNIQUE_BASE_DN.get()); 1187 uniquenessBaseDN.addLongIdentifier("uniqueness-base-dn", true); 1188 uniquenessBaseDN.setArgumentGroupName( 1189 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1190 parser.addArgument(uniquenessBaseDN); 1191 parser.addDependentArgumentSet(uniquenessBaseDN, uniquenessAttribute, 1192 uniquenessFilter); 1193 1194 1195 final Set<String> mabValues = StaticUtils.setOf( 1196 "unique-within-each-attribute", 1197 "unique-across-all-attributes-including-in-same-entry", 1198 "unique-across-all-attributes-except-in-same-entry", 1199 "unique-in-combination"); 1200 uniquenessMultipleAttributeBehavior = new StringArgument(null, 1201 "uniquenessMultipleAttributeBehavior", false, 1, 1202 INFO_LDAPMODIFY_PLACEHOLDER_BEHAVIOR.get(), 1203 INFO_LDAPMODIFY_ARG_DESCRIPTION_UNIQUE_MULTIPLE_ATTRIBUTE_BEHAVIOR. 1204 get(), 1205 mabValues); 1206 uniquenessMultipleAttributeBehavior.addLongIdentifier( 1207 "uniqueness-multiple-attribute-behavior", true); 1208 uniquenessMultipleAttributeBehavior.setArgumentGroupName( 1209 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1210 parser.addArgument(uniquenessMultipleAttributeBehavior); 1211 parser.addDependentArgumentSet(uniquenessMultipleAttributeBehavior, 1212 uniquenessAttribute); 1213 1214 1215 final Set<String> vlValues = StaticUtils.setOf("none", "all-subtree-views", 1216 "all-backend-sets", "all-available-backend-servers"); 1217 uniquenessPreCommitValidationLevel = new StringArgument(null, 1218 "uniquenessPreCommitValidationLevel", false, 1, 1219 INFO_LDAPMODIFY_PLACEHOLDER_LEVEL.get(), 1220 INFO_LDAPMODIFY_ARG_DESCRIPTION_UNIQUE_PRE_COMMIT_LEVEL.get(), 1221 vlValues); 1222 uniquenessPreCommitValidationLevel.addLongIdentifier( 1223 "uniqueness-pre-commit-validation-level", true); 1224 uniquenessPreCommitValidationLevel.setArgumentGroupName( 1225 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1226 parser.addArgument(uniquenessPreCommitValidationLevel); 1227 parser.addDependentArgumentSet(uniquenessPreCommitValidationLevel, 1228 uniquenessAttribute, uniquenessFilter); 1229 1230 1231 uniquenessPostCommitValidationLevel = new StringArgument(null, 1232 "uniquenessPostCommitValidationLevel", false, 1, 1233 INFO_LDAPMODIFY_PLACEHOLDER_LEVEL.get(), 1234 INFO_LDAPMODIFY_ARG_DESCRIPTION_UNIQUE_POST_COMMIT_LEVEL.get(), 1235 vlValues); 1236 uniquenessPostCommitValidationLevel.addLongIdentifier( 1237 "uniqueness-post-commit-validation-level", true); 1238 uniquenessPostCommitValidationLevel.setArgumentGroupName( 1239 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1240 parser.addArgument(uniquenessPostCommitValidationLevel); 1241 parser.addDependentArgumentSet(uniquenessPostCommitValidationLevel, 1242 uniquenessAttribute, uniquenessFilter); 1243 1244 operationControl = new ControlArgument('J', "control", false, 0, null, 1245 INFO_LDAPMODIFY_ARG_DESCRIPTION_OP_CONTROL.get()); 1246 operationControl.setArgumentGroupName( 1247 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1248 parser.addArgument(operationControl); 1249 1250 1251 addControl = new ControlArgument(null, "addControl", false, 0, null, 1252 INFO_LDAPMODIFY_ARG_DESCRIPTION_ADD_CONTROL.get()); 1253 addControl.addLongIdentifier("add-control", true); 1254 addControl.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1255 parser.addArgument(addControl); 1256 1257 1258 bindControl = new ControlArgument(null, "bindControl", false, 0, null, 1259 INFO_LDAPMODIFY_ARG_DESCRIPTION_BIND_CONTROL.get()); 1260 bindControl.addLongIdentifier("bind-control", true); 1261 bindControl.setArgumentGroupName( 1262 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1263 parser.addArgument(bindControl); 1264 1265 1266 deleteControl = new ControlArgument(null, "deleteControl", false, 0, null, 1267 INFO_LDAPMODIFY_ARG_DESCRIPTION_DELETE_CONTROL.get()); 1268 deleteControl.addLongIdentifier("delete-control", true); 1269 deleteControl.setArgumentGroupName( 1270 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1271 parser.addArgument(deleteControl); 1272 1273 1274 modifyControl = new ControlArgument(null, "modifyControl", false, 0, null, 1275 INFO_LDAPMODIFY_ARG_DESCRIPTION_MODIFY_CONTROL.get()); 1276 modifyControl.addLongIdentifier("modify-control", true); 1277 modifyControl.setArgumentGroupName( 1278 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1279 parser.addArgument(modifyControl); 1280 1281 1282 modifyDNControl = new ControlArgument(null, "modifyDNControl", false, 0, 1283 null, INFO_LDAPMODIFY_ARG_DESCRIPTION_MODIFY_DN_CONTROL.get()); 1284 modifyDNControl.addLongIdentifier("modify-dn-control", true); 1285 modifyDNControl.setArgumentGroupName( 1286 INFO_LDAPMODIFY_ARG_GROUP_CONTROLS.get()); 1287 parser.addArgument(modifyDNControl); 1288 1289 1290 useJSONFormattedRequestControls = new BooleanArgument(null, 1291 "useJSONFormattedRequestControls", 1, 1292 INFO_LDAPMODIFY_ARG_DESCRIPTION_USE_JSON_FORMATTED_CONTROLS.get()); 1293 useJSONFormattedRequestControls.addLongIdentifier( 1294 "use-json-formatted-request-controls", true); 1295 useJSONFormattedRequestControls.addLongIdentifier( 1296 "useJSONFormattedControls", true); 1297 useJSONFormattedRequestControls.addLongIdentifier( 1298 "use-json-formatted-controls", true); 1299 useJSONFormattedRequestControls.setArgumentGroupName( 1300 INFO_LDAPSEARCH_ARG_GROUP_CONTROLS.get()); 1301 parser.addArgument(useJSONFormattedRequestControls); 1302 1303 1304 ratePerSecond = new IntegerArgument('r', "ratePerSecond", false, 1, 1305 INFO_PLACEHOLDER_NUM.get(), 1306 INFO_LDAPMODIFY_ARG_DESCRIPTION_RATE_PER_SECOND.get(), 1, 1307 Integer.MAX_VALUE); 1308 ratePerSecond.addLongIdentifier("rate-per-second", true); 1309 ratePerSecond.setArgumentGroupName(INFO_LDAPMODIFY_ARG_GROUP_OPS.get()); 1310 parser.addArgument(ratePerSecond); 1311 1312 1313 // The "--scriptFriendly" argument is provided for compatibility with legacy 1314 // ldapmodify tools, but is not actually used by this tool. 1315 final BooleanArgument scriptFriendly = new BooleanArgument(null, 1316 "scriptFriendly", 1, 1317 INFO_LDAPMODIFY_ARG_DESCRIPTION_SCRIPT_FRIENDLY.get()); 1318 scriptFriendly.addLongIdentifier("script-friendly", true); 1319 scriptFriendly.setArgumentGroupName( 1320 INFO_LDAPMODIFY_ARG_GROUP_DATA.get()); 1321 scriptFriendly.setHidden(true); 1322 parser.addArgument(scriptFriendly); 1323 1324 1325 // The "-V" / "--ldapVersion" argument is provided for compatibility with 1326 // legacy ldapmodify tools, but is not actually used by this tool. 1327 final IntegerArgument ldapVersion = new IntegerArgument('V', "ldapVersion", 1328 false, 1, null, INFO_LDAPMODIFY_ARG_DESCRIPTION_LDAP_VERSION.get()); 1329 ldapVersion.addLongIdentifier("ldap-version", true); 1330 ldapVersion.setHidden(true); 1331 parser.addArgument(ldapVersion); 1332 1333 1334 // A few assured replication arguments will only be allowed if assured 1335 // replication is to be used. 1336 parser.addDependentArgumentSet(assuredReplicationLocalLevel, 1337 assuredReplication); 1338 parser.addDependentArgumentSet(assuredReplicationRemoteLevel, 1339 assuredReplication); 1340 parser.addDependentArgumentSet(assuredReplicationTimeout, 1341 assuredReplication); 1342 1343 // Transactions will be incompatible with a lot of settings. 1344 parser.addExclusiveArgumentSet(useTransaction, multiUpdateErrorBehavior); 1345 parser.addExclusiveArgumentSet(useTransaction, rejectFile); 1346 parser.addExclusiveArgumentSet(useTransaction, retryFailedOperations); 1347 parser.addExclusiveArgumentSet(useTransaction, continueOnError); 1348 parser.addExclusiveArgumentSet(useTransaction, dryRun); 1349 parser.addExclusiveArgumentSet(useTransaction, followReferrals); 1350 parser.addExclusiveArgumentSet(useTransaction, nameWithEntryUUID); 1351 parser.addExclusiveArgumentSet(useTransaction, noOperation); 1352 parser.addExclusiveArgumentSet(useTransaction, modifyEntriesMatchingFilter); 1353 parser.addExclusiveArgumentSet(useTransaction, 1354 modifyEntriesMatchingFiltersFromFile); 1355 parser.addExclusiveArgumentSet(useTransaction, modifyEntryWithDN); 1356 parser.addExclusiveArgumentSet(useTransaction, 1357 modifyEntriesWithDNsFromFile); 1358 parser.addExclusiveArgumentSet(useTransaction, 1359 clientSideSubtreeDelete); 1360 1361 // Multi-update is incompatible with a lot of settings. 1362 parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, ratePerSecond); 1363 parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, rejectFile); 1364 parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, 1365 retryFailedOperations); 1366 parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, continueOnError); 1367 parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, dryRun); 1368 parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, followReferrals); 1369 parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, nameWithEntryUUID); 1370 parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, noOperation); 1371 parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, 1372 modifyEntriesMatchingFilter); 1373 parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, 1374 modifyEntriesMatchingFiltersFromFile); 1375 parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, modifyEntryWithDN); 1376 parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, 1377 modifyEntriesWithDNsFromFile); 1378 parser.addExclusiveArgumentSet(multiUpdateErrorBehavior, 1379 clientSideSubtreeDelete); 1380 1381 // Client-side and server-side subtree deletes cannot be used together. 1382 parser.addExclusiveArgumentSet(clientSideSubtreeDelete, 1383 serverSideSubtreeDelete); 1384 1385 // Soft delete cannot be used with either hard delete or subtree delete. 1386 parser.addExclusiveArgumentSet(softDelete, hardDelete); 1387 parser.addExclusiveArgumentSet(softDelete, clientSideSubtreeDelete); 1388 parser.addExclusiveArgumentSet(softDelete, serverSideSubtreeDelete); 1389 1390 // Client-side subtree delete cannot be used in conjunction with a few 1391 // other settings. 1392 parser.addExclusiveArgumentSet(clientSideSubtreeDelete, followReferrals); 1393 parser.addExclusiveArgumentSet(clientSideSubtreeDelete, preReadAttribute); 1394 parser.addExclusiveArgumentSet(clientSideSubtreeDelete, getBackendSetID); 1395 parser.addExclusiveArgumentSet(clientSideSubtreeDelete, getServerID); 1396 parser.addExclusiveArgumentSet(clientSideSubtreeDelete, noOperation); 1397 parser.addExclusiveArgumentSet(clientSideSubtreeDelete, dryRun); 1398 1399 // Password retiring and purging can't be used together. 1400 parser.addExclusiveArgumentSet(retireCurrentPassword, purgeCurrentPassword); 1401 1402 // Referral following cannot be used in conjunction with the manageDsaIT 1403 // control. 1404 parser.addExclusiveArgumentSet(followReferrals, manageDsaIT); 1405 1406 // The proxyAs and proxyV1As arguments cannot be used together. 1407 parser.addExclusiveArgumentSet(proxyAs, proxyV1As); 1408 1409 // The modifyEntriesMatchingFilter argument is incompatible with a lot of 1410 // settings, since it can only be used for modify operations. 1411 parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, allowUndelete); 1412 parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, defaultAdd); 1413 parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, dryRun); 1414 parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, hardDelete); 1415 parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, 1416 ignoreNoUserModification); 1417 parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, 1418 nameWithEntryUUID); 1419 parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, softDelete); 1420 parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, 1421 clientSideSubtreeDelete); 1422 parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, 1423 serverSideSubtreeDelete); 1424 parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, 1425 suppressReferentialIntegrityUpdates); 1426 parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, addControl); 1427 parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, deleteControl); 1428 parser.addExclusiveArgumentSet(modifyEntriesMatchingFilter, 1429 modifyDNControl); 1430 1431 // The modifyEntriesMatchingFilterFromFile argument is incompatible with a 1432 // lot of settings, since it can only be used for modify operations. 1433 parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile, 1434 allowUndelete); 1435 parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile, 1436 defaultAdd); 1437 parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile, 1438 dryRun); 1439 parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile, 1440 hardDelete); 1441 parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile, 1442 ignoreNoUserModification); 1443 parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile, 1444 nameWithEntryUUID); 1445 parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile, 1446 softDelete); 1447 parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile, 1448 clientSideSubtreeDelete); 1449 parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile, 1450 serverSideSubtreeDelete); 1451 parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile, 1452 suppressReferentialIntegrityUpdates); 1453 parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile, 1454 addControl); 1455 parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile, 1456 deleteControl); 1457 parser.addExclusiveArgumentSet(modifyEntriesMatchingFiltersFromFile, 1458 modifyDNControl); 1459 1460 // The modifyEntryWithDN argument is incompatible with a lot of 1461 // settings, since it can only be used for modify operations. 1462 parser.addExclusiveArgumentSet(modifyEntryWithDN, allowUndelete); 1463 parser.addExclusiveArgumentSet(modifyEntryWithDN, defaultAdd); 1464 parser.addExclusiveArgumentSet(modifyEntryWithDN, dryRun); 1465 parser.addExclusiveArgumentSet(modifyEntryWithDN, hardDelete); 1466 parser.addExclusiveArgumentSet(modifyEntryWithDN, ignoreNoUserModification); 1467 parser.addExclusiveArgumentSet(modifyEntryWithDN, nameWithEntryUUID); 1468 parser.addExclusiveArgumentSet(modifyEntryWithDN, softDelete); 1469 parser.addExclusiveArgumentSet(modifyEntryWithDN, clientSideSubtreeDelete); 1470 parser.addExclusiveArgumentSet(modifyEntryWithDN, serverSideSubtreeDelete); 1471 parser.addExclusiveArgumentSet(modifyEntryWithDN, 1472 suppressReferentialIntegrityUpdates); 1473 parser.addExclusiveArgumentSet(modifyEntryWithDN, addControl); 1474 parser.addExclusiveArgumentSet(modifyEntryWithDN, deleteControl); 1475 parser.addExclusiveArgumentSet(modifyEntryWithDN, modifyDNControl); 1476 1477 // The modifyEntriesWithDNsFromFile argument is incompatible with a lot of 1478 // settings, since it can only be used for modify operations. 1479 parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, allowUndelete); 1480 parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, defaultAdd); 1481 parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, dryRun); 1482 parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, hardDelete); 1483 parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, 1484 ignoreNoUserModification); 1485 parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, 1486 nameWithEntryUUID); 1487 parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, softDelete); 1488 parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, 1489 clientSideSubtreeDelete); 1490 parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, 1491 serverSideSubtreeDelete); 1492 parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, 1493 suppressReferentialIntegrityUpdates); 1494 parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, addControl); 1495 parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, deleteControl); 1496 parser.addExclusiveArgumentSet(modifyEntriesWithDNsFromFile, 1497 modifyDNControl); 1498 } 1499 1500 1501 1502 /** 1503 * {@inheritDoc} 1504 */ 1505 @Override() 1506 public void doExtendedNonLDAPArgumentValidation() 1507 throws ArgumentException 1508 { 1509 // If we should use the route to backend set request control, then validate 1510 // and pre-create those controls. 1511 if (routeToBackendSet.isPresent()) 1512 { 1513 final List<String> values = routeToBackendSet.getValues(); 1514 final Map<String,List<String>> idsByRP = new LinkedHashMap<>( 1515 StaticUtils.computeMapCapacity(values.size())); 1516 for (final String value : values) 1517 { 1518 final int colonPos = value.indexOf(':'); 1519 if (colonPos <= 0) 1520 { 1521 throw new ArgumentException( 1522 ERR_LDAPMODIFY_ROUTE_TO_BACKEND_SET_INVALID_FORMAT.get(value, 1523 routeToBackendSet.getIdentifierString())); 1524 } 1525 1526 final String rpID = value.substring(0, colonPos); 1527 final String bsID = value.substring(colonPos+1); 1528 1529 List<String> idsForRP = idsByRP.get(rpID); 1530 if (idsForRP == null) 1531 { 1532 idsForRP = new ArrayList<>(values.size()); 1533 idsByRP.put(rpID, idsForRP); 1534 } 1535 idsForRP.add(bsID); 1536 } 1537 1538 for (final Map.Entry<String,List<String>> e : idsByRP.entrySet()) 1539 { 1540 final String rpID = e.getKey(); 1541 final List<String> bsIDs = e.getValue(); 1542 routeToBackendSetRequestControls.add( 1543 RouteToBackendSetRequestControl.createAbsoluteRoutingRequest(true, 1544 rpID, bsIDs)); 1545 } 1546 } 1547 } 1548 1549 1550 1551 /** 1552 * {@inheritDoc} 1553 */ 1554 @Override() 1555 @NotNull() 1556 protected List<Control> getBindControls() 1557 { 1558 final ArrayList<Control> bindControls = new ArrayList<>(10); 1559 1560 if (bindControl.isPresent()) 1561 { 1562 bindControls.addAll(bindControl.getValues()); 1563 } 1564 1565 if (authorizationIdentity.isPresent()) 1566 { 1567 bindControls.add(new AuthorizationIdentityRequestControl(false)); 1568 } 1569 1570 if (getAuthorizationEntryAttribute.isPresent()) 1571 { 1572 bindControls.add(new GetAuthorizationEntryRequestControl(true, true, 1573 getAuthorizationEntryAttribute.getValues())); 1574 } 1575 1576 if (getRecentLoginHistory.isPresent()) 1577 { 1578 bindControls.add(new GetRecentLoginHistoryRequestControl()); 1579 } 1580 1581 if (getUserResourceLimits.isPresent()) 1582 { 1583 bindControls.add(new GetUserResourceLimitsRequestControl()); 1584 } 1585 1586 if (usePasswordPolicyControl.isPresent()) 1587 { 1588 bindControls.add(new PasswordPolicyRequestControl()); 1589 } 1590 1591 if (suppressOperationalAttributeUpdates.isPresent()) 1592 { 1593 final EnumSet<SuppressType> suppressTypes = 1594 EnumSet.noneOf(SuppressType.class); 1595 for (final String s : suppressOperationalAttributeUpdates.getValues()) 1596 { 1597 if (s.equalsIgnoreCase("last-access-time")) 1598 { 1599 suppressTypes.add(SuppressType.LAST_ACCESS_TIME); 1600 } 1601 else if (s.equalsIgnoreCase("last-login-time")) 1602 { 1603 suppressTypes.add(SuppressType.LAST_LOGIN_TIME); 1604 } 1605 else if (s.equalsIgnoreCase("last-login-ip")) 1606 { 1607 suppressTypes.add(SuppressType.LAST_LOGIN_IP); 1608 } 1609 } 1610 1611 bindControls.add(new SuppressOperationalAttributeUpdateRequestControl( 1612 suppressTypes)); 1613 } 1614 1615 if (useJSONFormattedRequestControls.isPresent()) 1616 { 1617 final JSONFormattedRequestControl jsonFormattedRequestControl = 1618 JSONFormattedRequestControl.createWithControls(true, bindControls); 1619 bindControls.clear(); 1620 bindControls.add(jsonFormattedRequestControl); 1621 } 1622 1623 return bindControls; 1624 } 1625 1626 1627 1628 /** 1629 * {@inheritDoc} 1630 */ 1631 @Override() 1632 protected boolean supportsMultipleServers() 1633 { 1634 // We will support providing information about multiple servers. This tool 1635 // will not communicate with multiple servers concurrently, but it can 1636 // accept information about multiple servers in the event that a large set 1637 // of changes is to be processed and a server goes down in the middle of 1638 // those changes. In this case, we can resume processing on a newly-created 1639 // connection, possibly to a different server. 1640 return true; 1641 } 1642 1643 1644 1645 /** 1646 * {@inheritDoc} 1647 */ 1648 @Override() 1649 @NotNull() 1650 public LDAPConnectionOptions getConnectionOptions() 1651 { 1652 final LDAPConnectionOptions options = new LDAPConnectionOptions(); 1653 1654 options.setUseSynchronousMode(true); 1655 options.setFollowReferrals(followReferrals.isPresent()); 1656 options.setUnsolicitedNotificationHandler(this); 1657 options.setResponseTimeoutMillis(0L); 1658 1659 return options; 1660 } 1661 1662 1663 1664 /** 1665 * {@inheritDoc} 1666 */ 1667 @Override() 1668 @NotNull() 1669 public ResultCode doToolProcessing() 1670 { 1671 // Examine the arguments to determine the sets of controls to use for each 1672 // type of request. 1673 final ArrayList<Control> addControls = new ArrayList<>(10); 1674 final ArrayList<Control> deleteControls = new ArrayList<>(10); 1675 final ArrayList<Control> modifyControls = new ArrayList<>(10); 1676 final ArrayList<Control> modifyDNControls = new ArrayList<>(10); 1677 final ArrayList<Control> searchControls = new ArrayList<>(10); 1678 try 1679 { 1680 createRequestControls(addControls, deleteControls, modifyControls, 1681 modifyDNControls, searchControls); 1682 } 1683 catch (final LDAPException le) 1684 { 1685 Debug.debugException(le); 1686 for (final String line : 1687 ResultUtils.formatResult(le, true, 0, WRAP_COLUMN)) 1688 { 1689 err(line); 1690 } 1691 return le.getResultCode(); 1692 } 1693 1694 1695 // If an encryption passphrase file was specified, then read its value. 1696 String encryptionPassphrase = null; 1697 if (encryptionPassphraseFile.isPresent()) 1698 { 1699 try 1700 { 1701 encryptionPassphrase = ToolUtils.readEncryptionPassphraseFromFile( 1702 encryptionPassphraseFile.getValue()); 1703 } 1704 catch (final LDAPException e) 1705 { 1706 Debug.debugException(e); 1707 wrapErr(0, WRAP_COLUMN, e.getMessage()); 1708 return e.getResultCode(); 1709 } 1710 } 1711 1712 1713 LDAPConnectionPool connectionPool = null; 1714 LDIFReader ldifReader = null; 1715 LDIFWriter rejectWriter = null; 1716 try 1717 { 1718 // Create a connection pool that will be used to communicate with the 1719 // directory server. If we should use an administrative session, then 1720 // create a connect processor that will be used to start the session 1721 // before performing the bind. 1722 try 1723 { 1724 final StartAdministrativeSessionPostConnectProcessor p; 1725 if (useAdministrativeSession.isPresent()) 1726 { 1727 p = new StartAdministrativeSessionPostConnectProcessor( 1728 new StartAdministrativeSessionExtendedRequest(getToolName(), 1729 true)); 1730 } 1731 else 1732 { 1733 p = null; 1734 } 1735 1736 if (! dryRun.isPresent()) 1737 { 1738 connectionPool = getConnectionPool(1, 2, 0, p, null, true, 1739 new ReportBindResultLDAPConnectionPoolHealthCheck(this, true, 1740 verbose.isPresent())); 1741 } 1742 } 1743 catch (final LDAPException le) 1744 { 1745 Debug.debugException(le); 1746 1747 // Unable to create the connection pool, which means that either the 1748 // connection could not be established or the attempt to authenticate 1749 // the connection failed. If the bind failed, then the report bind 1750 // result health check should have already reported the bind failure. 1751 // If the failure was something else, then display that failure result. 1752 if (le.getResultCode() != ResultCode.INVALID_CREDENTIALS) 1753 { 1754 for (final String line : 1755 ResultUtils.formatResult(le, true, 0, WRAP_COLUMN)) 1756 { 1757 err(line); 1758 } 1759 } 1760 return le.getResultCode(); 1761 } 1762 1763 if (connectionPool != null) 1764 { 1765 connectionPool.setRetryFailedOperationsDueToInvalidConnections( 1766 (! neverRetry.isPresent())); 1767 } 1768 1769 1770 // Report that the connection was successfully established. 1771 if (connectionPool != null) 1772 { 1773 try 1774 { 1775 final LDAPConnection connection = connectionPool.getConnection(); 1776 final String hostPort = connection.getHostPort(); 1777 connectionPool.releaseConnection(connection); 1778 commentToOut(INFO_LDAPMODIFY_CONNECTION_ESTABLISHED.get(hostPort)); 1779 out(); 1780 } 1781 catch (final LDAPException le) 1782 { 1783 Debug.debugException(le); 1784 // This should never happen. 1785 } 1786 } 1787 1788 1789 // If we should process the operations in a transaction, then start that 1790 // now. 1791 final ASN1OctetString txnID; 1792 if (useTransaction.isPresent()) 1793 { 1794 final Control[] startTxnControls; 1795 if (proxyAs.isPresent()) 1796 { 1797 // In a transaction, the proxied authorization control must only be 1798 // used in the start transaction request and not in any of the 1799 // subsequent operation requests. 1800 startTxnControls = new Control[] 1801 { 1802 new ProxiedAuthorizationV2RequestControl(proxyAs.getValue()) 1803 }; 1804 } 1805 else if (proxyV1As.isPresent()) 1806 { 1807 // In a transaction, the proxied authorization control must only be 1808 // used in the start transaction request and not in any of the 1809 // subsequent operation requests. 1810 startTxnControls = new Control[] 1811 { 1812 new ProxiedAuthorizationV1RequestControl(proxyV1As.getValue()) 1813 }; 1814 } 1815 else 1816 { 1817 startTxnControls = StaticUtils.NO_CONTROLS; 1818 } 1819 1820 try 1821 { 1822 final StartTransactionExtendedResult startTxnResult = 1823 (StartTransactionExtendedResult) 1824 connectionPool.processExtendedOperation( 1825 new StartTransactionExtendedRequest(startTxnControls)); 1826 if (startTxnResult.getResultCode() == ResultCode.SUCCESS) 1827 { 1828 txnID = startTxnResult.getTransactionID(); 1829 1830 final TransactionSpecificationRequestControl c = 1831 new TransactionSpecificationRequestControl(txnID); 1832 addControls.add(c); 1833 deleteControls.add(c); 1834 modifyControls.add(c); 1835 modifyDNControls.add(c); 1836 1837 final String txnIDString; 1838 if (StaticUtils.isPrintableString(txnID.getValue())) 1839 { 1840 txnIDString = txnID.stringValue(); 1841 } 1842 else 1843 { 1844 final StringBuilder hexBuffer = new StringBuilder(); 1845 StaticUtils.toHex(txnID.getValue(), ":", hexBuffer); 1846 txnIDString = hexBuffer.toString(); 1847 } 1848 1849 commentToOut(INFO_LDAPMODIFY_STARTED_TXN.get(txnIDString)); 1850 } 1851 else 1852 { 1853 commentToErr(ERR_LDAPMODIFY_CANNOT_START_TXN.get( 1854 startTxnResult.getResultString())); 1855 return startTxnResult.getResultCode(); 1856 } 1857 } 1858 catch (final LDAPException le) 1859 { 1860 Debug.debugException(le); 1861 commentToErr(ERR_LDAPMODIFY_CANNOT_START_TXN.get( 1862 StaticUtils.getExceptionMessage(le))); 1863 return le.getResultCode(); 1864 } 1865 } 1866 else 1867 { 1868 txnID = null; 1869 } 1870 1871 1872 // Create an LDIF reader that will be used to read the changes to process. 1873 try 1874 { 1875 final InputStream ldifInputStream; 1876 if (ldifFile.isPresent()) 1877 { 1878 ldifInputStream = ToolUtils.getInputStreamForLDIFFiles( 1879 ldifFile.getValues(), encryptionPassphrase, getOut(), 1880 getErr()).getFirst(); 1881 } 1882 else 1883 { 1884 ldifInputStream = in; 1885 } 1886 1887 ldifReader = new LDIFReader(ldifInputStream, 0, null, null, 1888 characterSet.getValue()); 1889 } 1890 catch (final Exception e) 1891 { 1892 commentToErr(ERR_LDAPMODIFY_CANNOT_CREATE_LDIF_READER.get( 1893 StaticUtils.getExceptionMessage(e))); 1894 return ResultCode.LOCAL_ERROR; 1895 } 1896 1897 if (stripTrailingSpaces.isPresent()) 1898 { 1899 ldifReader.setTrailingSpaceBehavior(TrailingSpaceBehavior.STRIP); 1900 } 1901 1902 1903 // If appropriate, create a reject writer. 1904 if (rejectFile.isPresent()) 1905 { 1906 try 1907 { 1908 rejectWriter = new LDIFWriter(rejectFile.getValue()); 1909 1910 // Set the maximum allowed wrap column. This is better than setting a 1911 // wrap column of zero because it will ensure that comments don't get 1912 // wrapped either. 1913 rejectWriter.setWrapColumn(Integer.MAX_VALUE); 1914 } 1915 catch (final Exception e) 1916 { 1917 Debug.debugException(e); 1918 commentToErr(ERR_LDAPMODIFY_CANNOT_CREATE_REJECT_WRITER.get( 1919 rejectFile.getValue().getAbsolutePath(), 1920 StaticUtils.getExceptionMessage(e))); 1921 return ResultCode.LOCAL_ERROR; 1922 } 1923 } 1924 1925 1926 // If appropriate, create a rate limiter. 1927 final FixedRateBarrier rateLimiter; 1928 if (ratePerSecond.isPresent()) 1929 { 1930 rateLimiter = new FixedRateBarrier(1000L, ratePerSecond.getValue()); 1931 } 1932 else 1933 { 1934 rateLimiter = null; 1935 } 1936 1937 1938 // Iterate through the set of changes to process. 1939 boolean commitTransaction = true; 1940 ResultCode resultCode = null; 1941 final ArrayList<LDAPRequest> multiUpdateRequests = 1942 new ArrayList<>(10); 1943 final boolean isBulkModify = modifyEntriesMatchingFilter.isPresent() || 1944 modifyEntriesMatchingFiltersFromFile.isPresent() || 1945 modifyEntryWithDN.isPresent() || 1946 modifyEntriesWithDNsFromFile.isPresent(); 1947readChangeRecordLoop: 1948 while (true) 1949 { 1950 // If there is a rate limiter, then use it to sleep if necessary. 1951 if ((rateLimiter != null) && (! isBulkModify)) 1952 { 1953 rateLimiter.await(); 1954 } 1955 1956 1957 // Read the next LDIF change record. If we get an error then handle it 1958 // and abort if appropriate. 1959 final LDIFChangeRecord changeRecord; 1960 try 1961 { 1962 changeRecord = ldifReader.readChangeRecord(defaultAdd.isPresent()); 1963 } 1964 catch (final IOException ioe) 1965 { 1966 Debug.debugException(ioe); 1967 1968 final String message = ERR_LDAPMODIFY_IO_ERROR_READING_CHANGE.get( 1969 StaticUtils.getExceptionMessage(ioe)); 1970 commentToErr(message); 1971 writeRejectedChange(rejectWriter, message, null); 1972 commitTransaction = false; 1973 resultCode = ResultCode.LOCAL_ERROR; 1974 break; 1975 } 1976 catch (final LDIFException le) 1977 { 1978 Debug.debugException(le); 1979 1980 final StringBuilder buffer = new StringBuilder(); 1981 if (le.mayContinueReading() && (! useTransaction.isPresent())) 1982 { 1983 buffer.append( 1984 ERR_LDAPMODIFY_RECOVERABLE_LDIF_ERROR_READING_CHANGE.get( 1985 le.getLineNumber(), StaticUtils.getExceptionMessage(le))); 1986 } 1987 else 1988 { 1989 buffer.append( 1990 ERR_LDAPMODIFY_UNRECOVERABLE_LDIF_ERROR_READING_CHANGE.get( 1991 le.getLineNumber(), StaticUtils.getExceptionMessage(le))); 1992 } 1993 1994 if ((resultCode == null) || (resultCode == ResultCode.SUCCESS)) 1995 { 1996 resultCode = ResultCode.LOCAL_ERROR; 1997 } 1998 1999 if ((le.getDataLines() != null) && (! le.getDataLines().isEmpty())) 2000 { 2001 buffer.append(StaticUtils.EOL); 2002 buffer.append(StaticUtils.EOL); 2003 buffer.append(ERR_LDAPMODIFY_INVALID_LINES.get()); 2004 buffer.append(StaticUtils.EOL); 2005 for (final String s : le.getDataLines()) 2006 { 2007 buffer.append(s); 2008 buffer.append(StaticUtils.EOL); 2009 } 2010 } 2011 2012 final String message = buffer.toString(); 2013 commentToErr(message); 2014 writeRejectedChange(rejectWriter, message, null); 2015 2016 if (le.mayContinueReading() && (! useTransaction.isPresent())) 2017 { 2018 continue; 2019 } 2020 else 2021 { 2022 commitTransaction = false; 2023 resultCode = ResultCode.LOCAL_ERROR; 2024 break; 2025 } 2026 } 2027 2028 2029 // If we read a null change record, then there are no more changes to 2030 // process. Otherwise, treat it appropriately based on the operation 2031 // type. 2032 if (changeRecord == null) 2033 { 2034 break; 2035 } 2036 2037 2038 // If we should modify entries matching a specified filter, then convert 2039 // the change record into a set of modifications. 2040 if (modifyEntriesMatchingFilter.isPresent()) 2041 { 2042 for (final Filter filter : modifyEntriesMatchingFilter.getValues()) 2043 { 2044 final ResultCode rc = handleModifyMatchingFilter(connectionPool, 2045 changeRecord, 2046 modifyEntriesMatchingFilter.getIdentifierString(), 2047 filter, searchControls, modifyControls, rateLimiter, 2048 rejectWriter); 2049 if (rc != ResultCode.SUCCESS) 2050 { 2051 if ((resultCode == null) || (resultCode == ResultCode.SUCCESS) || 2052 (resultCode == ResultCode.NO_OPERATION)) 2053 { 2054 resultCode = rc; 2055 } 2056 } 2057 } 2058 } 2059 2060 if (modifyEntriesMatchingFiltersFromFile.isPresent()) 2061 { 2062 for (final File f : modifyEntriesMatchingFiltersFromFile.getValues()) 2063 { 2064 final FilterFileReader filterReader; 2065 try 2066 { 2067 filterReader = new FilterFileReader(f); 2068 } 2069 catch (final Exception e) 2070 { 2071 Debug.debugException(e); 2072 commentToErr(ERR_LDAPMODIFY_ERROR_OPENING_FILTER_FILE.get( 2073 f.getAbsolutePath(), StaticUtils.getExceptionMessage(e))); 2074 return ResultCode.LOCAL_ERROR; 2075 } 2076 2077 try 2078 { 2079 while (true) 2080 { 2081 final Filter filter; 2082 try 2083 { 2084 filter = filterReader.readFilter(); 2085 } 2086 catch (final IOException ioe) 2087 { 2088 Debug.debugException(ioe); 2089 commentToErr(ERR_LDAPMODIFY_IO_ERROR_READING_FILTER_FILE.get( 2090 f.getAbsolutePath(), 2091 StaticUtils.getExceptionMessage(ioe))); 2092 return ResultCode.LOCAL_ERROR; 2093 } 2094 catch (final LDAPException le) 2095 { 2096 Debug.debugException(le); 2097 commentToErr(le.getMessage()); 2098 if (continueOnError.isPresent()) 2099 { 2100 if ((resultCode == null) || 2101 (resultCode == ResultCode.SUCCESS) || 2102 (resultCode == ResultCode.NO_OPERATION)) 2103 { 2104 resultCode = le.getResultCode(); 2105 } 2106 continue; 2107 } 2108 else 2109 { 2110 return le.getResultCode(); 2111 } 2112 } 2113 2114 if (filter == null) 2115 { 2116 break; 2117 } 2118 2119 final ResultCode rc = handleModifyMatchingFilter(connectionPool, 2120 changeRecord, 2121 modifyEntriesMatchingFiltersFromFile.getIdentifierString(), 2122 filter, searchControls, modifyControls, rateLimiter, 2123 rejectWriter); 2124 if (rc != ResultCode.SUCCESS) 2125 { 2126 if ((resultCode == null) || 2127 (resultCode == ResultCode.SUCCESS) || 2128 (resultCode == ResultCode.NO_OPERATION)) 2129 { 2130 resultCode = rc; 2131 } 2132 } 2133 } 2134 } 2135 finally 2136 { 2137 try 2138 { 2139 filterReader.close(); 2140 } 2141 catch (final Exception e) 2142 { 2143 Debug.debugException(e); 2144 } 2145 } 2146 } 2147 } 2148 2149 if (modifyEntryWithDN.isPresent()) 2150 { 2151 for (final DN dn : modifyEntryWithDN.getValues()) 2152 { 2153 final ResultCode rc = handleModifyWithDN(connectionPool, 2154 changeRecord, modifyEntryWithDN.getIdentifierString(), dn, 2155 modifyControls, rateLimiter, rejectWriter); 2156 if (rc != ResultCode.SUCCESS) 2157 { 2158 if ((resultCode == null) || (resultCode == ResultCode.SUCCESS) || 2159 (resultCode == ResultCode.NO_OPERATION)) 2160 { 2161 resultCode = rc; 2162 } 2163 } 2164 } 2165 } 2166 2167 if (modifyEntriesWithDNsFromFile.isPresent()) 2168 { 2169 for (final File f : modifyEntriesWithDNsFromFile.getValues()) 2170 { 2171 final DNFileReader dnReader; 2172 try 2173 { 2174 dnReader = new DNFileReader(f); 2175 } 2176 catch (final Exception e) 2177 { 2178 Debug.debugException(e); 2179 commentToErr(ERR_LDAPMODIFY_ERROR_OPENING_DN_FILE.get( 2180 f.getAbsolutePath(), StaticUtils.getExceptionMessage(e))); 2181 return ResultCode.LOCAL_ERROR; 2182 } 2183 2184 try 2185 { 2186 while (true) 2187 { 2188 final DN dn; 2189 try 2190 { 2191 dn = dnReader.readDN(); 2192 } 2193 catch (final IOException ioe) 2194 { 2195 Debug.debugException(ioe); 2196 commentToErr(ERR_LDAPMODIFY_IO_ERROR_READING_DN_FILE.get( 2197 f.getAbsolutePath(), 2198 StaticUtils.getExceptionMessage(ioe))); 2199 return ResultCode.LOCAL_ERROR; 2200 } 2201 catch (final LDAPException le) 2202 { 2203 Debug.debugException(le); 2204 commentToErr(le.getMessage()); 2205 if (continueOnError.isPresent()) 2206 { 2207 if ((resultCode == null) || 2208 (resultCode == ResultCode.SUCCESS) || 2209 (resultCode == ResultCode.NO_OPERATION)) 2210 { 2211 resultCode = le.getResultCode(); 2212 } 2213 continue; 2214 } 2215 else 2216 { 2217 return le.getResultCode(); 2218 } 2219 } 2220 2221 if (dn == null) 2222 { 2223 break; 2224 } 2225 2226 final ResultCode rc = handleModifyWithDN(connectionPool, 2227 changeRecord, 2228 modifyEntriesWithDNsFromFile.getIdentifierString(), dn, 2229 modifyControls, rateLimiter, rejectWriter); 2230 if (rc != ResultCode.SUCCESS) 2231 { 2232 if ((resultCode == null) || 2233 (resultCode == ResultCode.SUCCESS) || 2234 (resultCode == ResultCode.NO_OPERATION)) 2235 { 2236 resultCode = rc; 2237 } 2238 } 2239 } 2240 } 2241 finally 2242 { 2243 try 2244 { 2245 dnReader.close(); 2246 } 2247 catch (final Exception e) 2248 { 2249 Debug.debugException(e); 2250 } 2251 } 2252 } 2253 } 2254 2255 if (isBulkModify) 2256 { 2257 continue; 2258 } 2259 2260 try 2261 { 2262 final ResultCode rc; 2263 if (changeRecord instanceof LDIFAddChangeRecord) 2264 { 2265 rc = doAdd((LDIFAddChangeRecord) changeRecord, addControls, 2266 connectionPool, multiUpdateRequests, rejectWriter); 2267 } 2268 else if (changeRecord instanceof LDIFDeleteChangeRecord) 2269 { 2270 rc = doDelete((LDIFDeleteChangeRecord) changeRecord, deleteControls, 2271 connectionPool, multiUpdateRequests, rejectWriter); 2272 } 2273 else if (changeRecord instanceof LDIFModifyChangeRecord) 2274 { 2275 rc = doModify((LDIFModifyChangeRecord) changeRecord, modifyControls, 2276 connectionPool, multiUpdateRequests, rejectWriter); 2277 } 2278 else if (changeRecord instanceof LDIFModifyDNChangeRecord) 2279 { 2280 rc = doModifyDN((LDIFModifyDNChangeRecord) changeRecord, 2281 modifyDNControls, connectionPool, multiUpdateRequests, 2282 rejectWriter); 2283 } 2284 else 2285 { 2286 // This should never happen. 2287 commentToErr(ERR_LDAPMODIFY_UNSUPPORTED_CHANGE_RECORD_HEADER.get()); 2288 for (final String line : changeRecord.toLDIF()) 2289 { 2290 err("# " + line); 2291 } 2292 throw new LDAPException(ResultCode.PARAM_ERROR, 2293 ERR_LDAPMODIFY_UNSUPPORTED_CHANGE_RECORD_HEADER.get() + 2294 changeRecord.toString()); 2295 } 2296 2297 if ((resultCode == null) && (rc != ResultCode.SUCCESS)) 2298 { 2299 resultCode = rc; 2300 } 2301 } 2302 catch (final LDAPException le) 2303 { 2304 Debug.debugException(le); 2305 2306 commitTransaction = false; 2307 if (continueOnError.isPresent()) 2308 { 2309 if ((resultCode == null) || (resultCode == ResultCode.SUCCESS) || 2310 (resultCode == ResultCode.NO_OPERATION)) 2311 { 2312 resultCode = le.getResultCode(); 2313 } 2314 } 2315 else 2316 { 2317 resultCode = le.getResultCode(); 2318 break; 2319 } 2320 } 2321 } 2322 2323 2324 // If the operations are part of a transaction, then commit or abort that 2325 // transaction now. Otherwise, if they should be part of a multi-update 2326 // operation, then process that now. 2327 if (useTransaction.isPresent()) 2328 { 2329 LDAPResult endTxnResult; 2330 final EndTransactionExtendedRequest endTxnRequest = 2331 new EndTransactionExtendedRequest(txnID, commitTransaction); 2332 try 2333 { 2334 endTxnResult = connectionPool.processExtendedOperation(endTxnRequest); 2335 } 2336 catch (final LDAPException le) 2337 { 2338 endTxnResult = le.toLDAPResult(); 2339 } 2340 2341 displayResult(endTxnResult, false); 2342 if (((resultCode == null) || (resultCode == ResultCode.SUCCESS)) && 2343 (endTxnResult.getResultCode() != ResultCode.SUCCESS)) 2344 { 2345 resultCode = endTxnResult.getResultCode(); 2346 } 2347 } 2348 else if (multiUpdateErrorBehavior.isPresent()) 2349 { 2350 final MultiUpdateErrorBehavior errorBehavior; 2351 if (multiUpdateErrorBehavior.getValue().equalsIgnoreCase("atomic")) 2352 { 2353 errorBehavior = MultiUpdateErrorBehavior.ATOMIC; 2354 } 2355 else if (multiUpdateErrorBehavior.getValue().equalsIgnoreCase( 2356 "abort-on-error")) 2357 { 2358 errorBehavior = MultiUpdateErrorBehavior.ABORT_ON_ERROR; 2359 } 2360 else 2361 { 2362 errorBehavior = MultiUpdateErrorBehavior.CONTINUE_ON_ERROR; 2363 } 2364 2365 final Control[] multiUpdateControls; 2366 if (proxyAs.isPresent()) 2367 { 2368 multiUpdateControls = new Control[] 2369 { 2370 new ProxiedAuthorizationV2RequestControl(proxyAs.getValue()) 2371 }; 2372 } 2373 else if (proxyV1As.isPresent()) 2374 { 2375 multiUpdateControls = new Control[] 2376 { 2377 new ProxiedAuthorizationV1RequestControl(proxyV1As.getValue()) 2378 }; 2379 } 2380 else 2381 { 2382 multiUpdateControls = StaticUtils.NO_CONTROLS; 2383 } 2384 2385 ExtendedResult multiUpdateResult; 2386 try 2387 { 2388 commentToOut(INFO_LDAPMODIFY_SENDING_MULTI_UPDATE_REQUEST.get()); 2389 final MultiUpdateExtendedRequest multiUpdateRequest = 2390 new MultiUpdateExtendedRequest(errorBehavior, 2391 multiUpdateRequests, multiUpdateControls); 2392 multiUpdateResult = 2393 connectionPool.processExtendedOperation(multiUpdateRequest); 2394 } 2395 catch (final LDAPException le) 2396 { 2397 multiUpdateResult = new ExtendedResult(le); 2398 } 2399 2400 displayResult(multiUpdateResult, false); 2401 resultCode = multiUpdateResult.getResultCode(); 2402 } 2403 2404 2405 if (resultCode == null) 2406 { 2407 return ResultCode.SUCCESS; 2408 } 2409 else 2410 { 2411 return resultCode; 2412 } 2413 } 2414 finally 2415 { 2416 if (rejectWriter != null) 2417 { 2418 try 2419 { 2420 rejectWriter.close(); 2421 } 2422 catch (final Exception e) 2423 { 2424 Debug.debugException(e); 2425 } 2426 } 2427 2428 if (ldifReader != null) 2429 { 2430 try 2431 { 2432 ldifReader.close(); 2433 } 2434 catch (final Exception e) 2435 { 2436 Debug.debugException(e); 2437 } 2438 } 2439 2440 if (connectionPool != null) 2441 { 2442 try 2443 { 2444 connectionPool.close(); 2445 } 2446 catch (final Exception e) 2447 { 2448 Debug.debugException(e); 2449 } 2450 } 2451 } 2452 } 2453 2454 2455 2456 /** 2457 * Handles the processing for a change record when the tool should modify 2458 * entries matching a given filter. 2459 * 2460 * @param connectionPool The connection pool to use to communicate with 2461 * the directory server. 2462 * @param changeRecord The LDIF change record to be processed. 2463 * @param argIdentifierString The identifier string for the argument used to 2464 * specify the filter to use to identify the 2465 * entries to modify. 2466 * @param filter The filter to use to identify the entries to 2467 * modify. 2468 * @param searchControls The set of controls to include in the search 2469 * request. 2470 * @param modifyControls The set of controls to include in the modify 2471 * requests. 2472 * @param rateLimiter The fixed-rate barrier to use for rate 2473 * limiting. It may be {@code null} if no rate 2474 * limiting is required. 2475 * @param rejectWriter The reject writer to use to record information 2476 * about any failed operations. 2477 * 2478 * @return A result code obtained from processing. 2479 */ 2480 @NotNull() 2481 private ResultCode handleModifyMatchingFilter( 2482 @NotNull final LDAPConnectionPool connectionPool, 2483 @NotNull final LDIFChangeRecord changeRecord, 2484 @NotNull final String argIdentifierString, 2485 @NotNull final Filter filter, 2486 @NotNull final List<Control> searchControls, 2487 @NotNull final List<Control> modifyControls, 2488 @Nullable final FixedRateBarrier rateLimiter, 2489 @Nullable final LDIFWriter rejectWriter) 2490 { 2491 // If the provided change record isn't a modify change record, then that's 2492 // an error. Reject it. 2493 if (! (changeRecord instanceof LDIFModifyChangeRecord)) 2494 { 2495 writeRejectedChange(rejectWriter, 2496 ERR_LDAPMODIFY_NON_MODIFY_WITH_BULK.get(argIdentifierString), 2497 changeRecord); 2498 return ResultCode.PARAM_ERROR; 2499 } 2500 2501 final LDIFModifyChangeRecord modifyChangeRecord = 2502 (LDIFModifyChangeRecord) changeRecord; 2503 final HashSet<DN> processedDNs = 2504 new HashSet<>(StaticUtils.computeMapCapacity(100)); 2505 2506 2507 // If we need to use the simple paged results control, then we may have to 2508 // issue multiple searches. 2509 ASN1OctetString pagedResultsCookie = null; 2510 long entriesProcessed = 0L; 2511 ResultCode resultCode = ResultCode.SUCCESS; 2512 while (true) 2513 { 2514 // Construct the search request to send. 2515 final LDAPModifySearchListener listener = 2516 new LDAPModifySearchListener(this, modifyChangeRecord, filter, 2517 modifyControls, connectionPool, rateLimiter, rejectWriter, 2518 processedDNs); 2519 2520 final SearchRequest searchRequest = 2521 new SearchRequest(listener, modifyChangeRecord.getDN(), 2522 SearchScope.SUB, filter, SearchRequest.NO_ATTRIBUTES); 2523 searchRequest.setControls(searchControls); 2524 if (searchPageSize.isPresent()) 2525 { 2526 searchRequest.addControl(new SimplePagedResultsControl( 2527 searchPageSize.getValue(), pagedResultsCookie)); 2528 } 2529 2530 2531 // The connection pool's automatic retry feature can't work for searches 2532 // that return one or more entries before encountering a failure. To get 2533 // around that, we'll check a connection out of the pool and use it to 2534 // process the search. If an error occurs that indicates the connection 2535 // is no longer valid, we can replace it with a newly-established 2536 // connection and try again. The search result listener will ensure that 2537 // no entry gets updated twice. 2538 LDAPConnection connection; 2539 try 2540 { 2541 connection = connectionPool.getConnection(); 2542 } 2543 catch (final LDAPException le) 2544 { 2545 Debug.debugException(le); 2546 2547 writeRejectedChange(rejectWriter, 2548 ERR_LDAPMODIFY_CANNOT_GET_SEARCH_CONNECTION.get( 2549 modifyChangeRecord.getDN(), String.valueOf(filter), 2550 StaticUtils.getExceptionMessage(le)), 2551 modifyChangeRecord, le.toLDAPResult()); 2552 return le.getResultCode(); 2553 } 2554 2555 SearchResult searchResult; 2556 boolean connectionValid = false; 2557 try 2558 { 2559 try 2560 { 2561 searchResult = connection.search(searchRequest); 2562 } 2563 catch (final LDAPSearchException lse) 2564 { 2565 searchResult = lse.getSearchResult(); 2566 } 2567 2568 if (searchResult.getResultCode() == ResultCode.SUCCESS) 2569 { 2570 connectionValid = true; 2571 } 2572 else if (searchResult.getResultCode().isConnectionUsable()) 2573 { 2574 connectionValid = true; 2575 writeRejectedChange(rejectWriter, 2576 ERR_LDAPMODIFY_SEARCH_FAILED.get(modifyChangeRecord.getDN(), 2577 String.valueOf(filter)), 2578 modifyChangeRecord, searchResult); 2579 return searchResult.getResultCode(); 2580 } 2581 else if (! neverRetry.isPresent()) 2582 { 2583 try 2584 { 2585 connection = connectionPool.replaceDefunctConnection(connection); 2586 } 2587 catch (final LDAPException le) 2588 { 2589 Debug.debugException(le); 2590 writeRejectedChange(rejectWriter, 2591 ERR_LDAPMODIFY_SEARCH_FAILED_CANNOT_RECONNECT.get( 2592 modifyChangeRecord.getDN(), String.valueOf(filter)), 2593 modifyChangeRecord, searchResult); 2594 return searchResult.getResultCode(); 2595 } 2596 2597 try 2598 { 2599 searchResult = connection.search(searchRequest); 2600 } 2601 catch (final LDAPSearchException lse) 2602 { 2603 Debug.debugException(lse); 2604 searchResult = lse.getSearchResult(); 2605 } 2606 2607 if (searchResult.getResultCode() == ResultCode.SUCCESS) 2608 { 2609 connectionValid = true; 2610 } 2611 else 2612 { 2613 connectionValid = searchResult.getResultCode().isConnectionUsable(); 2614 writeRejectedChange(rejectWriter, 2615 ERR_LDAPMODIFY_SEARCH_FAILED.get(modifyChangeRecord.getDN(), 2616 String.valueOf(filter)), 2617 modifyChangeRecord, searchResult); 2618 return searchResult.getResultCode(); 2619 } 2620 } 2621 else 2622 { 2623 writeRejectedChange(rejectWriter, 2624 ERR_LDAPMODIFY_SEARCH_FAILED.get(modifyChangeRecord.getDN(), 2625 String.valueOf(filter)), 2626 modifyChangeRecord, searchResult); 2627 return searchResult.getResultCode(); 2628 } 2629 } 2630 finally 2631 { 2632 if (connectionValid) 2633 { 2634 connectionPool.releaseConnection(connection); 2635 } 2636 else 2637 { 2638 connectionPool.releaseDefunctConnection(connection); 2639 } 2640 } 2641 2642 searchResult = LDAPSearch.handleJSONEncodedResponseControls(searchResult); 2643 2644 2645 // If we've gotten here, then the search was successful. Check to see if 2646 // any of the modifications failed, and if so then update the result code 2647 // accordingly. 2648 if ((resultCode == ResultCode.SUCCESS) && 2649 (listener.getResultCode() != ResultCode.SUCCESS)) 2650 { 2651 resultCode = listener.getResultCode(); 2652 } 2653 2654 2655 // If the search used the simple paged results control then we may need to 2656 // repeat the search to get the next page. 2657 entriesProcessed += searchResult.getEntryCount(); 2658 if (searchPageSize.isPresent()) 2659 { 2660 final SimplePagedResultsControl responseControl; 2661 try 2662 { 2663 responseControl = SimplePagedResultsControl.get(searchResult); 2664 } 2665 catch (final LDAPException le) 2666 { 2667 Debug.debugException(le); 2668 writeRejectedChange(rejectWriter, 2669 ERR_LDAPMODIFY_CANNOT_DECODE_PAGED_RESULTS_CONTROL.get( 2670 modifyChangeRecord.getDN(), String.valueOf(filter)), 2671 modifyChangeRecord, le.toLDAPResult()); 2672 return le.getResultCode(); 2673 } 2674 2675 if (responseControl == null) 2676 { 2677 writeRejectedChange(rejectWriter, 2678 ERR_LDAPMODIFY_MISSING_PAGED_RESULTS_RESPONSE.get( 2679 modifyChangeRecord.getDN(), String.valueOf(filter)), 2680 modifyChangeRecord); 2681 return ResultCode.CONTROL_NOT_FOUND; 2682 } 2683 else 2684 { 2685 pagedResultsCookie = responseControl.getCookie(); 2686 if (responseControl.moreResultsToReturn()) 2687 { 2688 if (verbose.isPresent()) 2689 { 2690 commentToOut(INFO_LDAPMODIFY_SEARCH_COMPLETED_MORE_PAGES.get( 2691 modifyChangeRecord.getDN(), String.valueOf(filter), 2692 entriesProcessed)); 2693 for (final String resultLine : 2694 ResultUtils.formatResult(searchResult, true, 0, WRAP_COLUMN)) 2695 { 2696 out(resultLine); 2697 } 2698 out(); 2699 } 2700 } 2701 else 2702 { 2703 commentToOut(INFO_LDAPMODIFY_SEARCH_COMPLETED.get( 2704 entriesProcessed, modifyChangeRecord.getDN(), 2705 String.valueOf(filter))); 2706 if (verbose.isPresent()) 2707 { 2708 for (final String resultLine : 2709 ResultUtils.formatResult(searchResult, true, 0, WRAP_COLUMN)) 2710 { 2711 out(resultLine); 2712 } 2713 } 2714 2715 out(); 2716 return resultCode; 2717 } 2718 } 2719 } 2720 else 2721 { 2722 commentToOut(INFO_LDAPMODIFY_SEARCH_COMPLETED.get( 2723 entriesProcessed, modifyChangeRecord.getDN(), 2724 String.valueOf(filter))); 2725 if (verbose.isPresent()) 2726 { 2727 for (final String resultLine : 2728 ResultUtils.formatResult(searchResult, true, 0, WRAP_COLUMN)) 2729 { 2730 out(resultLine); 2731 } 2732 } 2733 2734 out(); 2735 return resultCode; 2736 } 2737 } 2738 } 2739 2740 2741 2742 /** 2743 * Handles the processing for a change record when the tool should modify an 2744 * entry with a given DN instead of the DN contained in the change record. 2745 * 2746 * @param connectionPool The connection pool to use to communicate with 2747 * the directory server. 2748 * @param changeRecord The LDIF change record to be processed. 2749 * @param argIdentifierString The identifier string for the argument used to 2750 * specify the DN of the entry to modify. 2751 * @param dn The DN of the entry to modify. 2752 * @param modifyControls The set of controls to include in the modify 2753 * requests. 2754 * @param rateLimiter The fixed-rate barrier to use for rate 2755 * limiting. It may be {@code null} if no rate 2756 * limiting is required. 2757 * @param rejectWriter The reject writer to use to record information 2758 * about any failed operations. 2759 * 2760 * @return A result code obtained from processing. 2761 */ 2762 @NotNull() 2763 private ResultCode handleModifyWithDN( 2764 @NotNull final LDAPConnectionPool connectionPool, 2765 @NotNull final LDIFChangeRecord changeRecord, 2766 @NotNull final String argIdentifierString, 2767 @NotNull final DN dn, 2768 @NotNull final List<Control> modifyControls, 2769 @Nullable final FixedRateBarrier rateLimiter, 2770 @Nullable final LDIFWriter rejectWriter) 2771 { 2772 // If the provided change record isn't a modify change record, then that's 2773 // an error. Reject it. 2774 if (! (changeRecord instanceof LDIFModifyChangeRecord)) 2775 { 2776 writeRejectedChange(rejectWriter, 2777 ERR_LDAPMODIFY_NON_MODIFY_WITH_BULK.get(argIdentifierString), 2778 changeRecord); 2779 return ResultCode.PARAM_ERROR; 2780 } 2781 2782 2783 // Create a new modify change record with the provided DN instead of the 2784 // original DN. 2785 final LDIFModifyChangeRecord originalChangeRecord = 2786 (LDIFModifyChangeRecord) changeRecord; 2787 final LDIFModifyChangeRecord updatedChangeRecord = 2788 new LDIFModifyChangeRecord(dn.toString(), 2789 originalChangeRecord.getModifications(), 2790 originalChangeRecord.getControls()); 2791 2792 if (rateLimiter != null) 2793 { 2794 rateLimiter.await(); 2795 } 2796 2797 try 2798 { 2799 return doModify(updatedChangeRecord, modifyControls, connectionPool, null, 2800 rejectWriter); 2801 } 2802 catch (final LDAPException le) 2803 { 2804 Debug.debugException(le); 2805 return le.getResultCode(); 2806 } 2807 } 2808 2809 2810 2811 /** 2812 * Populates lists of request controls that should be included in requests 2813 * of various types. 2814 * 2815 * @param addControls The list of controls to include in add requests. 2816 * @param deleteControls The list of controls to include in delete 2817 * requests. 2818 * @param modifyControls The list of controls to include in modify 2819 * requests. 2820 * @param modifyDNControls The list of controls to include in modify DN 2821 * requests. 2822 * @param searchControls The list of controls to include in search 2823 * requests. 2824 * 2825 * @throws LDAPException If a problem is encountered while creating any of 2826 * the requested controls. 2827 */ 2828 private void createRequestControls( 2829 @NotNull final List<Control> addControls, 2830 @NotNull final List<Control> deleteControls, 2831 @NotNull final List<Control> modifyControls, 2832 @NotNull final List<Control> modifyDNControls, 2833 @NotNull final List<Control> searchControls) 2834 throws LDAPException 2835 { 2836 if (addControl.isPresent()) 2837 { 2838 addControls.addAll(addControl.getValues()); 2839 } 2840 2841 if (deleteControl.isPresent()) 2842 { 2843 deleteControls.addAll(deleteControl.getValues()); 2844 } 2845 2846 if (modifyControl.isPresent()) 2847 { 2848 modifyControls.addAll(modifyControl.getValues()); 2849 } 2850 2851 if (modifyDNControl.isPresent()) 2852 { 2853 modifyDNControls.addAll(modifyDNControl.getValues()); 2854 } 2855 2856 if (operationControl.isPresent()) 2857 { 2858 addControls.addAll(operationControl.getValues()); 2859 deleteControls.addAll(operationControl.getValues()); 2860 modifyControls.addAll(operationControl.getValues()); 2861 modifyDNControls.addAll(operationControl.getValues()); 2862 } 2863 2864 addControls.addAll(routeToBackendSetRequestControls); 2865 deleteControls.addAll(routeToBackendSetRequestControls); 2866 modifyControls.addAll(routeToBackendSetRequestControls); 2867 modifyDNControls.addAll(routeToBackendSetRequestControls); 2868 2869 if (noOperation.isPresent()) 2870 { 2871 final NoOpRequestControl c = new NoOpRequestControl(); 2872 addControls.add(c); 2873 deleteControls.add(c); 2874 modifyControls.add(c); 2875 modifyDNControls.add(c); 2876 } 2877 2878 if (generatePassword.isPresent()) 2879 { 2880 addControls.add(new GeneratePasswordRequestControl()); 2881 } 2882 2883 if (getBackendSetID.isPresent()) 2884 { 2885 final GetBackendSetIDRequestControl c = 2886 new GetBackendSetIDRequestControl(false); 2887 addControls.add(c); 2888 deleteControls.add(c); 2889 modifyControls.add(c); 2890 modifyDNControls.add(c); 2891 } 2892 2893 if (getServerID.isPresent()) 2894 { 2895 final GetServerIDRequestControl c = 2896 new GetServerIDRequestControl(false); 2897 addControls.add(c); 2898 deleteControls.add(c); 2899 modifyControls.add(c); 2900 modifyDNControls.add(c); 2901 } 2902 2903 if (ignoreNoUserModification.isPresent()) 2904 { 2905 addControls.add(new IgnoreNoUserModificationRequestControl(false)); 2906 modifyControls.add(new IgnoreNoUserModificationRequestControl(false)); 2907 } 2908 2909 if (nameWithEntryUUID.isPresent()) 2910 { 2911 addControls.add(new NameWithEntryUUIDRequestControl(true)); 2912 } 2913 2914 if (permissiveModify.isPresent()) 2915 { 2916 modifyControls.add(new PermissiveModifyRequestControl(false)); 2917 } 2918 2919 if (routeToServer.isPresent()) 2920 { 2921 final RouteToServerRequestControl c = 2922 new RouteToServerRequestControl(false, 2923 routeToServer.getValue(), false, false, false); 2924 addControls.add(c); 2925 deleteControls.add(c); 2926 modifyControls.add(c); 2927 modifyDNControls.add(c); 2928 } 2929 2930 if (suppressReferentialIntegrityUpdates.isPresent()) 2931 { 2932 final SuppressReferentialIntegrityUpdatesRequestControl c = 2933 new SuppressReferentialIntegrityUpdatesRequestControl(true); 2934 deleteControls.add(c); 2935 modifyDNControls.add(c); 2936 } 2937 2938 if (suppressOperationalAttributeUpdates.isPresent()) 2939 { 2940 final EnumSet<SuppressType> suppressTypes = 2941 EnumSet.noneOf(SuppressType.class); 2942 for (final String s : suppressOperationalAttributeUpdates.getValues()) 2943 { 2944 if (s.equalsIgnoreCase("last-access-time")) 2945 { 2946 suppressTypes.add(SuppressType.LAST_ACCESS_TIME); 2947 } 2948 else if (s.equalsIgnoreCase("last-login-time")) 2949 { 2950 suppressTypes.add(SuppressType.LAST_LOGIN_TIME); 2951 } 2952 else if (s.equalsIgnoreCase("last-login-ip")) 2953 { 2954 suppressTypes.add(SuppressType.LAST_LOGIN_IP); 2955 } 2956 else if (s.equalsIgnoreCase("lastmod")) 2957 { 2958 suppressTypes.add(SuppressType.LASTMOD); 2959 } 2960 } 2961 2962 final SuppressOperationalAttributeUpdateRequestControl c = 2963 new SuppressOperationalAttributeUpdateRequestControl(suppressTypes); 2964 addControls.add(c); 2965 deleteControls.add(c); 2966 modifyControls.add(c); 2967 modifyDNControls.add(c); 2968 } 2969 2970 if (usePasswordPolicyControl.isPresent()) 2971 { 2972 final PasswordPolicyRequestControl c = new PasswordPolicyRequestControl(); 2973 addControls.add(c); 2974 modifyControls.add(c); 2975 } 2976 2977 if (assuredReplication.isPresent()) 2978 { 2979 AssuredReplicationLocalLevel localLevel = null; 2980 if (assuredReplicationLocalLevel.isPresent()) 2981 { 2982 final String level = assuredReplicationLocalLevel.getValue(); 2983 if (level.equalsIgnoreCase("none")) 2984 { 2985 localLevel = AssuredReplicationLocalLevel.NONE; 2986 } 2987 else if (level.equalsIgnoreCase("received-any-server")) 2988 { 2989 localLevel = AssuredReplicationLocalLevel.RECEIVED_ANY_SERVER; 2990 } 2991 else if (level.equalsIgnoreCase("processed-all-servers")) 2992 { 2993 localLevel = AssuredReplicationLocalLevel.PROCESSED_ALL_SERVERS; 2994 } 2995 } 2996 2997 AssuredReplicationRemoteLevel remoteLevel = null; 2998 if (assuredReplicationRemoteLevel.isPresent()) 2999 { 3000 final String level = assuredReplicationRemoteLevel.getValue(); 3001 if (level.equalsIgnoreCase("none")) 3002 { 3003 remoteLevel = AssuredReplicationRemoteLevel.NONE; 3004 } 3005 else if (level.equalsIgnoreCase("received-any-remote-location")) 3006 { 3007 remoteLevel = 3008 AssuredReplicationRemoteLevel.RECEIVED_ANY_REMOTE_LOCATION; 3009 } 3010 else if (level.equalsIgnoreCase("received-all-remote-locations")) 3011 { 3012 remoteLevel = 3013 AssuredReplicationRemoteLevel.RECEIVED_ALL_REMOTE_LOCATIONS; 3014 } 3015 else if (level.equalsIgnoreCase("processed-all-remote-servers")) 3016 { 3017 remoteLevel = 3018 AssuredReplicationRemoteLevel.PROCESSED_ALL_REMOTE_SERVERS; 3019 } 3020 } 3021 3022 Long timeoutMillis = null; 3023 if (assuredReplicationTimeout.isPresent()) 3024 { 3025 timeoutMillis = 3026 assuredReplicationTimeout.getValue(TimeUnit.MILLISECONDS); 3027 } 3028 3029 final AssuredReplicationRequestControl c = 3030 new AssuredReplicationRequestControl(true, localLevel, localLevel, 3031 remoteLevel, remoteLevel, timeoutMillis, false); 3032 addControls.add(c); 3033 deleteControls.add(c); 3034 modifyControls.add(c); 3035 modifyDNControls.add(c); 3036 } 3037 3038 if (hardDelete.isPresent() && (! clientSideSubtreeDelete.isPresent())) 3039 { 3040 deleteControls.add(new HardDeleteRequestControl(true)); 3041 } 3042 3043 if (replicationRepair.isPresent()) 3044 { 3045 final ReplicationRepairRequestControl c = 3046 new ReplicationRepairRequestControl(); 3047 addControls.add(c); 3048 deleteControls.add(c); 3049 modifyControls.add(c); 3050 modifyDNControls.add(c); 3051 } 3052 3053 if (softDelete.isPresent()) 3054 { 3055 deleteControls.add(new SoftDeleteRequestControl(true, true)); 3056 } 3057 3058 if (serverSideSubtreeDelete.isPresent()) 3059 { 3060 deleteControls.add(new SubtreeDeleteRequestControl()); 3061 } 3062 3063 if (assertionFilter.isPresent()) 3064 { 3065 final AssertionRequestControl c = new AssertionRequestControl( 3066 assertionFilter.getValue(), true); 3067 addControls.add(c); 3068 deleteControls.add(c); 3069 modifyControls.add(c); 3070 modifyDNControls.add(c); 3071 } 3072 3073 if (operationPurpose.isPresent()) 3074 { 3075 final OperationPurposeRequestControl c = 3076 new OperationPurposeRequestControl(false, "ldapmodify", 3077 Version.NUMERIC_VERSION_STRING, 3078 LDAPModify.class.getName() + ".createRequestControls", 3079 operationPurpose.getValue()); 3080 addControls.add(c); 3081 deleteControls.add(c); 3082 modifyControls.add(c); 3083 modifyDNControls.add(c); 3084 } 3085 3086 if (manageDsaIT.isPresent()) 3087 { 3088 final ManageDsaITRequestControl c = new ManageDsaITRequestControl(true); 3089 addControls.add(c); 3090 if (! clientSideSubtreeDelete.isPresent()) 3091 { 3092 deleteControls.add(c); 3093 } 3094 modifyControls.add(c); 3095 modifyDNControls.add(c); 3096 } 3097 3098 if (passwordUpdateBehavior.isPresent()) 3099 { 3100 final PasswordUpdateBehaviorRequestControl c = 3101 createPasswordUpdateBehaviorRequestControl( 3102 passwordUpdateBehavior.getIdentifierString(), 3103 passwordUpdateBehavior.getValues()); 3104 addControls.add(c); 3105 modifyControls.add(c); 3106 } 3107 3108 if (preReadAttribute.isPresent()) 3109 { 3110 final ArrayList<String> attrList = new ArrayList<>(10); 3111 for (final String value : preReadAttribute.getValues()) 3112 { 3113 final StringTokenizer tokenizer = new StringTokenizer(value, ", "); 3114 while (tokenizer.hasMoreTokens()) 3115 { 3116 attrList.add(tokenizer.nextToken()); 3117 } 3118 } 3119 3120 final String[] attrArray = attrList.toArray(StaticUtils.NO_STRINGS); 3121 final PreReadRequestControl c = new PreReadRequestControl(attrArray); 3122 deleteControls.add(c); 3123 modifyControls.add(c); 3124 modifyDNControls.add(c); 3125 } 3126 3127 if (postReadAttribute.isPresent()) 3128 { 3129 final ArrayList<String> attrList = new ArrayList<>(10); 3130 for (final String value : postReadAttribute.getValues()) 3131 { 3132 final StringTokenizer tokenizer = new StringTokenizer(value, ", "); 3133 while (tokenizer.hasMoreTokens()) 3134 { 3135 attrList.add(tokenizer.nextToken()); 3136 } 3137 } 3138 3139 final String[] attrArray = attrList.toArray(StaticUtils.NO_STRINGS); 3140 final PostReadRequestControl c = new PostReadRequestControl(attrArray); 3141 addControls.add(c); 3142 modifyControls.add(c); 3143 modifyDNControls.add(c); 3144 } 3145 3146 if (proxyAs.isPresent() && (! useTransaction.isPresent()) && 3147 (! multiUpdateErrorBehavior.isPresent())) 3148 { 3149 final ProxiedAuthorizationV2RequestControl c = 3150 new ProxiedAuthorizationV2RequestControl(proxyAs.getValue()); 3151 addControls.add(c); 3152 deleteControls.add(c); 3153 modifyControls.add(c); 3154 modifyDNControls.add(c); 3155 searchControls.add(c); 3156 } 3157 3158 if (proxyV1As.isPresent() && (! useTransaction.isPresent()) && 3159 (! multiUpdateErrorBehavior.isPresent())) 3160 { 3161 final ProxiedAuthorizationV1RequestControl c = 3162 new ProxiedAuthorizationV1RequestControl(proxyV1As.getValue()); 3163 addControls.add(c); 3164 deleteControls.add(c); 3165 modifyControls.add(c); 3166 modifyDNControls.add(c); 3167 searchControls.add(c); 3168 } 3169 3170 if (uniquenessAttribute.isPresent() || uniquenessFilter.isPresent()) 3171 { 3172 final UniquenessRequestControlProperties uniquenessProperties; 3173 if (uniquenessAttribute.isPresent()) 3174 { 3175 uniquenessProperties = new UniquenessRequestControlProperties( 3176 uniquenessAttribute.getValues()); 3177 if (uniquenessFilter.isPresent()) 3178 { 3179 uniquenessProperties.setFilter(uniquenessFilter.getValue()); 3180 } 3181 } 3182 else 3183 { 3184 uniquenessProperties = new UniquenessRequestControlProperties( 3185 uniquenessFilter.getValue()); 3186 } 3187 3188 if (uniquenessBaseDN.isPresent()) 3189 { 3190 uniquenessProperties.setBaseDN(uniquenessBaseDN.getStringValue()); 3191 } 3192 3193 if (uniquenessMultipleAttributeBehavior.isPresent()) 3194 { 3195 final String value = 3196 uniquenessMultipleAttributeBehavior.getValue().toLowerCase(); 3197 switch (value) 3198 { 3199 case "unique-within-each-attribute": 3200 uniquenessProperties.setMultipleAttributeBehavior( 3201 UniquenessMultipleAttributeBehavior. 3202 UNIQUE_WITHIN_EACH_ATTRIBUTE); 3203 break; 3204 case "unique-across-all-attributes-including-in-same-entry": 3205 uniquenessProperties.setMultipleAttributeBehavior( 3206 UniquenessMultipleAttributeBehavior. 3207 UNIQUE_ACROSS_ALL_ATTRIBUTES_INCLUDING_IN_SAME_ENTRY); 3208 break; 3209 case "unique-across-all-attributes-except-in-same-entry": 3210 uniquenessProperties.setMultipleAttributeBehavior( 3211 UniquenessMultipleAttributeBehavior. 3212 UNIQUE_ACROSS_ALL_ATTRIBUTES_EXCEPT_IN_SAME_ENTRY); 3213 break; 3214 case "unique-in-combination": 3215 uniquenessProperties.setMultipleAttributeBehavior( 3216 UniquenessMultipleAttributeBehavior.UNIQUE_IN_COMBINATION); 3217 break; 3218 } 3219 } 3220 3221 if (uniquenessPreCommitValidationLevel.isPresent()) 3222 { 3223 final String value = 3224 uniquenessPreCommitValidationLevel.getValue().toLowerCase(); 3225 switch (value) 3226 { 3227 case "none": 3228 uniquenessProperties.setPreCommitValidationLevel( 3229 UniquenessValidationLevel.NONE); 3230 break; 3231 case "all-subtree-views": 3232 uniquenessProperties.setPreCommitValidationLevel( 3233 UniquenessValidationLevel.ALL_SUBTREE_VIEWS); 3234 break; 3235 case "all-backend-sets": 3236 uniquenessProperties.setPreCommitValidationLevel( 3237 UniquenessValidationLevel.ALL_BACKEND_SETS); 3238 break; 3239 case "all-available-backend-servers": 3240 uniquenessProperties.setPreCommitValidationLevel( 3241 UniquenessValidationLevel.ALL_AVAILABLE_BACKEND_SERVERS); 3242 break; 3243 } 3244 } 3245 3246 if (uniquenessPostCommitValidationLevel.isPresent()) 3247 { 3248 final String value = 3249 uniquenessPostCommitValidationLevel.getValue().toLowerCase(); 3250 switch (value) 3251 { 3252 case "none": 3253 uniquenessProperties.setPostCommitValidationLevel( 3254 UniquenessValidationLevel.NONE); 3255 break; 3256 case "all-subtree-views": 3257 uniquenessProperties.setPostCommitValidationLevel( 3258 UniquenessValidationLevel.ALL_SUBTREE_VIEWS); 3259 break; 3260 case "all-backend-sets": 3261 uniquenessProperties.setPostCommitValidationLevel( 3262 UniquenessValidationLevel.ALL_BACKEND_SETS); 3263 break; 3264 case "all-available-backend-servers": 3265 uniquenessProperties.setPostCommitValidationLevel( 3266 UniquenessValidationLevel.ALL_AVAILABLE_BACKEND_SERVERS); 3267 break; 3268 } 3269 } 3270 3271 final UniquenessRequestControl c = 3272 new UniquenessRequestControl(true, null, uniquenessProperties); 3273 addControls.add(c); 3274 modifyControls.add(c); 3275 modifyDNControls.add(c); 3276 } 3277 3278 3279 if (useJSONFormattedRequestControls.isPresent()) 3280 { 3281 final JSONFormattedRequestControl jsonFormattedAddRequestControl = 3282 JSONFormattedRequestControl.createWithControls(true, addControls); 3283 addControls.clear(); 3284 addControls.add(jsonFormattedAddRequestControl); 3285 3286 final JSONFormattedRequestControl jsonFormattedDeleteRequestControl = 3287 JSONFormattedRequestControl.createWithControls(true, deleteControls); 3288 deleteControls.clear(); 3289 deleteControls.add(jsonFormattedDeleteRequestControl); 3290 3291 final JSONFormattedRequestControl jsonFormattedModifyRequestControl = 3292 JSONFormattedRequestControl.createWithControls(true, modifyControls); 3293 modifyControls.clear(); 3294 modifyControls.add(jsonFormattedModifyRequestControl); 3295 3296 final JSONFormattedRequestControl jsonFormattedModifyDNRequestControl = 3297 JSONFormattedRequestControl.createWithControls(true, 3298 modifyDNControls); 3299 modifyDNControls.clear(); 3300 modifyDNControls.add(jsonFormattedModifyDNRequestControl); 3301 3302 final JSONFormattedRequestControl jsonFormattedSearchRequestControl = 3303 JSONFormattedRequestControl.createWithControls(true, searchControls); 3304 searchControls.clear(); 3305 searchControls.add(jsonFormattedSearchRequestControl); 3306 } 3307 } 3308 3309 3310 3311 /** 3312 * Creates the password update behavior request control that should be 3313 * included in add and modify requests. 3314 * 3315 * @param argIdentifier The identifier string for the argument used to 3316 * configure the password update behavior request 3317 * control. 3318 * @param argValues The set of values for the password update behavior 3319 * request control. 3320 * 3321 * @return The password update behavior request control that was created. 3322 * 3323 * @throws LDAPException If a problem is encountered while creating the 3324 * control. 3325 */ 3326 @NotNull() 3327 static PasswordUpdateBehaviorRequestControl 3328 createPasswordUpdateBehaviorRequestControl( 3329 @NotNull final String argIdentifier, 3330 @NotNull final List<String> argValues) 3331 throws LDAPException 3332 { 3333 final PasswordUpdateBehaviorRequestControlProperties properties = 3334 new PasswordUpdateBehaviorRequestControlProperties(); 3335 3336 for (final String argValue : argValues) 3337 { 3338 int delimiterPos = argValue.indexOf('='); 3339 if (delimiterPos < 0) 3340 { 3341 delimiterPos = argValue.indexOf(':'); 3342 } 3343 3344 if ((delimiterPos <= 0) || (delimiterPos >= (argValue.length() - 1))) 3345 { 3346 throw new LDAPException(ResultCode.PARAM_ERROR, 3347 ERR_LDAPMODIFY_MALFORMED_PW_UPDATE_BEHAVIOR.get(argValue, 3348 argIdentifier)); 3349 } 3350 3351 final String name = argValue.substring(0, delimiterPos).trim(); 3352 final String value = argValue.substring(delimiterPos+1).trim(); 3353 if (name.equalsIgnoreCase("is-self-change") || 3354 name.equalsIgnoreCase("self-change") || 3355 name.equalsIgnoreCase("isSelfChange") || 3356 name.equalsIgnoreCase("selfChange")) 3357 { 3358 properties.setIsSelfChange(parseBooleanValue(name, value)); 3359 } 3360 else if (name.equalsIgnoreCase("allow-pre-encoded-password") || 3361 name.equalsIgnoreCase("allow-pre-encoded-passwords") || 3362 name.equalsIgnoreCase("allow-pre-encoded") || 3363 name.equalsIgnoreCase("allowPreEncodedPassword") || 3364 name.equalsIgnoreCase("allowPreEncodedPasswords") || 3365 name.equalsIgnoreCase("allowPreEncoded")) 3366 { 3367 properties.setAllowPreEncodedPassword(parseBooleanValue(name, value)); 3368 } 3369 else if (name.equalsIgnoreCase("skip-password-validation") || 3370 name.equalsIgnoreCase("skip-password-validators") || 3371 name.equalsIgnoreCase("skip-validation") || 3372 name.equalsIgnoreCase("skip-validators") || 3373 name.equalsIgnoreCase("skipPasswordValidation") || 3374 name.equalsIgnoreCase("skipPasswordValidators") || 3375 name.equalsIgnoreCase("skipValidation") || 3376 name.equalsIgnoreCase("skipValidators")) 3377 { 3378 properties.setSkipPasswordValidation(parseBooleanValue(name, value)); 3379 } 3380 else if (name.equalsIgnoreCase("ignore-password-history") || 3381 name.equalsIgnoreCase("skip-password-history") || 3382 name.equalsIgnoreCase("ignore-history") || 3383 name.equalsIgnoreCase("skip-history") || 3384 name.equalsIgnoreCase("ignorePasswordHistory") || 3385 name.equalsIgnoreCase("skipPasswordHistory") || 3386 name.equalsIgnoreCase("ignoreHistory") || 3387 name.equalsIgnoreCase("skipHistory")) 3388 { 3389 properties.setIgnorePasswordHistory(parseBooleanValue(name, value)); 3390 } 3391 else if (name.equalsIgnoreCase("ignore-minimum-password-age") || 3392 name.equalsIgnoreCase("ignore-min-password-age") || 3393 name.equalsIgnoreCase("ignore-password-age") || 3394 name.equalsIgnoreCase("skip-minimum-password-age") || 3395 name.equalsIgnoreCase("skip-min-password-age") || 3396 name.equalsIgnoreCase("skip-password-age") || 3397 name.equalsIgnoreCase("ignoreMinimumPasswordAge") || 3398 name.equalsIgnoreCase("ignoreMinPasswordAge") || 3399 name.equalsIgnoreCase("ignorePasswordAge") || 3400 name.equalsIgnoreCase("skipMinimumPasswordAge") || 3401 name.equalsIgnoreCase("skipMinPasswordAge") || 3402 name.equalsIgnoreCase("skipPasswordAge")) 3403 { 3404 properties.setIgnoreMinimumPasswordAge(parseBooleanValue(name, value)); 3405 } 3406 else if (name.equalsIgnoreCase("password-storage-scheme") || 3407 name.equalsIgnoreCase("password-scheme") || 3408 name.equalsIgnoreCase("storage-scheme") || 3409 name.equalsIgnoreCase("scheme") || 3410 name.equalsIgnoreCase("passwordStorageScheme") || 3411 name.equalsIgnoreCase("passwordScheme") || 3412 name.equalsIgnoreCase("storageScheme")) 3413 { 3414 properties.setPasswordStorageScheme(value); 3415 } 3416 else if (name.equalsIgnoreCase("must-change-password") || 3417 name.equalsIgnoreCase("mustChangePassword")) 3418 { 3419 properties.setMustChangePassword(parseBooleanValue(name, value)); 3420 } 3421 } 3422 3423 return new PasswordUpdateBehaviorRequestControl(properties, true); 3424 } 3425 3426 3427 3428 /** 3429 * Parses the provided value as the Boolean value for a password update 3430 * behavior property. 3431 * 3432 * @param name The name of the password update behavior property being 3433 * parsed. 3434 * @param value The value to be parsed. 3435 * 3436 * @return The Boolean value that was parsed. 3437 * 3438 * @throws LDAPException If the provided value cannot be parsed as a 3439 * Boolean value. 3440 */ 3441 private static boolean parseBooleanValue(@NotNull final String name, 3442 @NotNull final String value) 3443 throws LDAPException 3444 { 3445 if (value.equalsIgnoreCase("true") || 3446 value.equalsIgnoreCase("t") || 3447 value.equalsIgnoreCase("yes") || 3448 value.equalsIgnoreCase("y") || 3449 value.equalsIgnoreCase("1")) 3450 { 3451 return true; 3452 } 3453 else if (value.equalsIgnoreCase("false") || 3454 value.equalsIgnoreCase("f") || 3455 value.equalsIgnoreCase("no") || 3456 value.equalsIgnoreCase("n") || 3457 value.equalsIgnoreCase("0")) 3458 { 3459 return false; 3460 } 3461 else 3462 { 3463 throw new LDAPException(ResultCode.PARAM_ERROR, 3464 ERR_LDAPMODIFY_INVALID_PW_UPDATE_BOOLEAN_VALUE.get(value, name)); 3465 } 3466 } 3467 3468 3469 3470 /** 3471 * Performs the appropriate processing for an LDIF add change record. 3472 * 3473 * @param changeRecord The LDIF add change record to process. 3474 * @param controls The set of controls to include in the request. 3475 * @param pool The connection pool to use to communicate with 3476 * the directory server. 3477 * @param multiUpdateRequests The list to which the request should be added 3478 * if it is to be processed as part of a 3479 * multi-update operation. It may be 3480 * {@code null} if the operation should not be 3481 * processed via the multi-update operation. 3482 * @param rejectWriter The LDIF writer to use for recording 3483 * information about rejected changes. It may be 3484 * {@code null} if no reject writer is 3485 * configured. 3486 * 3487 * @return The result code obtained from processing. 3488 * 3489 * @throws LDAPException If the operation did not complete successfully 3490 * and processing should not continue. 3491 */ 3492 @NotNull() 3493 private ResultCode doAdd(@NotNull final LDIFAddChangeRecord changeRecord, 3494 @NotNull final List<Control> controls, 3495 @NotNull final LDAPConnectionPool pool, 3496 @Nullable final List<LDAPRequest> multiUpdateRequests, 3497 @Nullable final LDIFWriter rejectWriter) 3498 throws LDAPException 3499 { 3500 // Create the add request to process. 3501 final AddRequest addRequest = changeRecord.toAddRequest(true); 3502 for (final Control c : controls) 3503 { 3504 addRequest.addControl(c); 3505 } 3506 3507 3508 // If we should provide support for undelete operations and the entry 3509 // includes the ds-undelete-from-dn attribute, then add the undelete request 3510 // control. 3511 if (allowUndelete.isPresent() && 3512 addRequest.hasAttribute(ATTR_UNDELETE_FROM_DN)) 3513 { 3514 addRequest.addControl(new UndeleteRequestControl()); 3515 } 3516 3517 3518 // If the entry to add includes a password, then add a password validation 3519 // details request control if appropriate. 3520 if (passwordValidationDetails.isPresent()) 3521 { 3522 final Entry entryToAdd = addRequest.toEntry(); 3523 if ((! entryToAdd.getAttributesWithOptions(ATTR_USER_PASSWORD, 3524 null).isEmpty()) || 3525 (! entryToAdd.getAttributesWithOptions(ATTR_AUTH_PASSWORD, 3526 null).isEmpty())) 3527 { 3528 addRequest.addControl(new PasswordValidationDetailsRequestControl()); 3529 } 3530 } 3531 3532 3533 // If the operation should be processed in a multi-update operation, then 3534 // just add the request to the list and return without doing anything else. 3535 if (multiUpdateErrorBehavior.isPresent()) 3536 { 3537 multiUpdateRequests.add(addRequest); 3538 commentToOut(INFO_LDAPMODIFY_ADD_ADDED_TO_MULTI_UPDATE.get( 3539 addRequest.getDN())); 3540 return ResultCode.SUCCESS; 3541 } 3542 3543 3544 // If the --dryRun argument was provided, then we'll stop here. 3545 if (dryRun.isPresent()) 3546 { 3547 commentToOut(INFO_LDAPMODIFY_DRY_RUN_ADD.get(addRequest.getDN(), 3548 dryRun.getIdentifierString())); 3549 return ResultCode.SUCCESS; 3550 } 3551 3552 3553 // Process the add operation and get the result. 3554 commentToOut(INFO_LDAPMODIFY_ADDING_ENTRY.get(addRequest.getDN())); 3555 if (verbose.isPresent()) 3556 { 3557 for (final String ldifLine : 3558 addRequest.toLDIFChangeRecord().toLDIF(WRAP_COLUMN)) 3559 { 3560 out(ldifLine); 3561 } 3562 out(); 3563 } 3564 3565 LDAPResult addResult; 3566 try 3567 { 3568 addResult = pool.add(addRequest); 3569 } 3570 catch (final LDAPException le) 3571 { 3572 Debug.debugException(le); 3573 addResult = le.toLDAPResult(); 3574 } 3575 3576 addResult = handleJSONEncodedResponseControls(addResult); 3577 3578 3579 // Display information about the result. 3580 displayResult(addResult, useTransaction.isPresent()); 3581 3582 3583 // See if the add operation succeeded or failed. If it failed, and we 3584 // should end all processing, then throw an exception. 3585 switch (addResult.getResultCode().intValue()) 3586 { 3587 case ResultCode.SUCCESS_INT_VALUE: 3588 case ResultCode.NO_OPERATION_INT_VALUE: 3589 break; 3590 3591 case ResultCode.ASSERTION_FAILED_INT_VALUE: 3592 writeRejectedChange(rejectWriter, 3593 INFO_LDAPMODIFY_ASSERTION_FAILED.get(addRequest.getDN(), 3594 String.valueOf(assertionFilter.getValue())), 3595 addRequest.toLDIFChangeRecord(), addResult); 3596 throw new LDAPException(addResult); 3597 3598 default: 3599 writeRejectedChange(rejectWriter, null, addRequest.toLDIFChangeRecord(), 3600 addResult); 3601 if (useTransaction.isPresent() || (! continueOnError.isPresent())) 3602 { 3603 throw new LDAPException(addResult); 3604 } 3605 break; 3606 } 3607 3608 return addResult.getResultCode(); 3609 } 3610 3611 3612 3613 /** 3614 * Performs the appropriate processing for an LDIF delete change record. 3615 * 3616 * @param changeRecord The LDIF delete change record to process. 3617 * @param controls The set of controls to include in the request. 3618 * @param pool The connection pool to use to communicate with 3619 * the directory server. 3620 * @param multiUpdateRequests The list to which the request should be added 3621 * if it is to be processed as part of a 3622 * multi-update operation. It may be 3623 * {@code null} if the operation should not be 3624 * processed via the multi-update operation. 3625 * @param rejectWriter The LDIF writer to use for recording 3626 * information about rejected changes. It may be 3627 * {@code null} if no reject writer is 3628 * configured. 3629 * 3630 * @return The result code obtained from processing. 3631 * 3632 * @throws LDAPException If the operation did not complete successfully 3633 * and processing should not continue. 3634 */ 3635 @NotNull() 3636 private ResultCode doDelete( 3637 @NotNull final LDIFDeleteChangeRecord changeRecord, 3638 @NotNull final List<Control> controls, 3639 @NotNull final LDAPConnectionPool pool, 3640 @Nullable final List<LDAPRequest> multiUpdateRequests, 3641 @Nullable final LDIFWriter rejectWriter) 3642 throws LDAPException 3643 { 3644 // If we should perform a client-side subtree delete, then do that 3645 // differently. 3646 if (clientSideSubtreeDelete.isPresent()) 3647 { 3648 return doClientSideSubtreeDelete(changeRecord, controls, pool, 3649 rejectWriter); 3650 } 3651 3652 3653 // Create the delete request to process. 3654 final DeleteRequest deleteRequest = changeRecord.toDeleteRequest(true); 3655 for (final Control c : controls) 3656 { 3657 deleteRequest.addControl(c); 3658 } 3659 3660 3661 // If the operation should be processed in a multi-update operation, then 3662 // just add the request to the list and return without doing anything else. 3663 if (multiUpdateErrorBehavior.isPresent()) 3664 { 3665 multiUpdateRequests.add(deleteRequest); 3666 commentToOut(INFO_LDAPMODIFY_DELETE_ADDED_TO_MULTI_UPDATE.get( 3667 deleteRequest.getDN())); 3668 return ResultCode.SUCCESS; 3669 } 3670 3671 3672 // If the --dryRun argument was provided, then we'll stop here. 3673 if (dryRun.isPresent()) 3674 { 3675 commentToOut(INFO_LDAPMODIFY_DRY_RUN_DELETE.get(deleteRequest.getDN(), 3676 dryRun.getIdentifierString())); 3677 return ResultCode.SUCCESS; 3678 } 3679 3680 3681 // Process the delete operation and get the result. 3682 commentToOut(INFO_LDAPMODIFY_DELETING_ENTRY.get(deleteRequest.getDN())); 3683 if (verbose.isPresent()) 3684 { 3685 for (final String ldifLine : 3686 deleteRequest.toLDIFChangeRecord().toLDIF(WRAP_COLUMN)) 3687 { 3688 out(ldifLine); 3689 } 3690 out(); 3691 } 3692 3693 3694 LDAPResult deleteResult; 3695 try 3696 { 3697 deleteResult = pool.delete(deleteRequest); 3698 } 3699 catch (final LDAPException le) 3700 { 3701 Debug.debugException(le); 3702 deleteResult = le.toLDAPResult(); 3703 } 3704 3705 deleteResult = handleJSONEncodedResponseControls(deleteResult); 3706 3707 3708 // Display information about the result. 3709 displayResult(deleteResult, useTransaction.isPresent()); 3710 3711 3712 // See if the delete operation succeeded or failed. If it failed, and we 3713 // should end all processing, then throw an exception. 3714 switch (deleteResult.getResultCode().intValue()) 3715 { 3716 case ResultCode.SUCCESS_INT_VALUE: 3717 case ResultCode.NO_OPERATION_INT_VALUE: 3718 break; 3719 3720 case ResultCode.ASSERTION_FAILED_INT_VALUE: 3721 writeRejectedChange(rejectWriter, 3722 INFO_LDAPMODIFY_ASSERTION_FAILED.get(deleteRequest.getDN(), 3723 String.valueOf(assertionFilter.getValue())), 3724 deleteRequest.toLDIFChangeRecord(), deleteResult); 3725 throw new LDAPException(deleteResult); 3726 3727 default: 3728 writeRejectedChange(rejectWriter, null, 3729 deleteRequest.toLDIFChangeRecord(), deleteResult); 3730 if (useTransaction.isPresent() || (! continueOnError.isPresent())) 3731 { 3732 throw new LDAPException(deleteResult); 3733 } 3734 break; 3735 } 3736 3737 return deleteResult.getResultCode(); 3738 } 3739 3740 3741 3742 /** 3743 * Performs the appropriate processing for an LDIF delete change record. 3744 * 3745 * @param changeRecord The LDIF delete change record to process. 3746 * @param controls The set of controls to include in the request. 3747 * @param pool The connection pool to use to communicate with the 3748 * directory server. 3749 * @param rejectWriter The LDIF writer to use for recording information 3750 * about rejected changes. It may be {@code null} if no 3751 * reject writer is configured. 3752 * 3753 * @return The result code obtained from processing. 3754 * 3755 * @throws LDAPException If the operation did not complete successfully 3756 * and processing should not continue. 3757 */ 3758 @NotNull() 3759 private ResultCode doClientSideSubtreeDelete( 3760 @NotNull final LDIFChangeRecord changeRecord, 3761 @NotNull final List<Control> controls, 3762 @NotNull final LDAPConnectionPool pool, 3763 @Nullable final LDIFWriter rejectWriter) 3764 throws LDAPException 3765 { 3766 // Create the subtree deleter with the provided set of controls. Make sure 3767 // to include any controls in the delete change record itself. 3768 final List<Control> additionalControls; 3769 if (changeRecord.getControls().isEmpty()) 3770 { 3771 additionalControls = controls; 3772 } 3773 else 3774 { 3775 additionalControls = new ArrayList<>(controls.size() + 3776 changeRecord.getControls().size()); 3777 additionalControls.addAll(changeRecord.getControls()); 3778 additionalControls.addAll(controls); 3779 } 3780 3781 final SubtreeDeleter subtreeDeleter = new SubtreeDeleter(); 3782 subtreeDeleter.setAdditionalDeleteControls(additionalControls); 3783 3784 3785 // Perform the subtree delete. 3786 commentToOut(INFO_LDAPMODIFY_CLIENT_SIDE_DELETING_SUBTREE.get( 3787 changeRecord.getDN())); 3788 final SubtreeDeleterResult subtreeDeleterResult = 3789 subtreeDeleter.delete(pool, changeRecord.getDN()); 3790 3791 3792 // Evaluate the result of the subtree delete. 3793 LDAPResult finalResult; 3794 if (subtreeDeleterResult.completelySuccessful()) 3795 { 3796 final long entriesDeleted = subtreeDeleterResult.getEntriesDeleted(); 3797 if (entriesDeleted == 0L) 3798 { 3799 // This means that the base entry did not exist. Even though the 3800 // subtree deleter returned a successful result, we'll use a final 3801 // result of "no such object". 3802 finalResult = new LDAPResult(-1, ResultCode.NO_SUCH_OBJECT, 3803 ERR_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_SUCCEEDED_WITH_0_ENTRIES.get( 3804 changeRecord.getDN()), 3805 null, StaticUtils.NO_STRINGS, StaticUtils.NO_CONTROLS); 3806 } 3807 else if (entriesDeleted == 1L) 3808 { 3809 // This means the base entry existed (and we deleted it successfully), 3810 // but did not have any subordinates. 3811 finalResult = new LDAPResult(-1, ResultCode.SUCCESS, 3812 INFO_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_SUCCEEDED_WITH_1_ENTRY.get( 3813 changeRecord.getDN()), 3814 null, StaticUtils.NO_STRINGS, StaticUtils.NO_CONTROLS); 3815 } 3816 else 3817 { 3818 // This means that the base entry existed and had subordinates, and we 3819 // deleted all of them successfully. 3820 finalResult = new LDAPResult(-1, ResultCode.SUCCESS, 3821 INFO_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_SUCCEEDED_WITH_ENTRIES.get( 3822 subtreeDeleterResult.getEntriesDeleted(), 3823 changeRecord.getDN()), 3824 null, StaticUtils.NO_STRINGS, StaticUtils.NO_CONTROLS); 3825 } 3826 } 3827 else 3828 { 3829 // If there was a search error, then display information about it. 3830 final SearchResult searchError = subtreeDeleterResult.getSearchError(); 3831 if (searchError != null) 3832 { 3833 commentToErr(ERR_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_SEARCH_ERROR.get()); 3834 displayResult(searchError, false); 3835 err("#"); 3836 } 3837 3838 final SortedMap<DN,LDAPResult> deleteErrors = 3839 subtreeDeleterResult.getDeleteErrorsDescendingMap(); 3840 for (final Map.Entry<DN,LDAPResult> deleteError : deleteErrors.entrySet()) 3841 { 3842 commentToErr(ERR_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_ERROR.get( 3843 String.valueOf(deleteError.getKey()))); 3844 displayResult(deleteError.getValue(), false); 3845 err("#"); 3846 } 3847 3848 ResultCode resultCode = ResultCode.OTHER; 3849 final StringBuilder buffer = new StringBuilder(); 3850 buffer.append(ERR_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_FINAL_ERR_BASE.get()); 3851 if (searchError != null) 3852 { 3853 resultCode = searchError.getResultCode(); 3854 buffer.append(" "); 3855 buffer.append( 3856 ERR_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_FINAL_SEARCH_ERR.get()); 3857 } 3858 3859 if (! deleteErrors.isEmpty()) 3860 { 3861 resultCode = deleteErrors.values().iterator().next().getResultCode(); 3862 buffer.append(" "); 3863 final int numDeleteErrors = deleteErrors.size(); 3864 if (numDeleteErrors == 1) 3865 { 3866 buffer.append( 3867 ERR_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_FINAL_DEL_ERR_COUNT_1.get()); 3868 } 3869 else 3870 { 3871 buffer.append( 3872 ERR_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_FINAL_DEL_ERR_COUNT.get( 3873 numDeleteErrors)); 3874 } 3875 } 3876 3877 buffer.append(" "); 3878 final long deletedCount = subtreeDeleterResult.getEntriesDeleted(); 3879 if (deletedCount == 1L) 3880 { 3881 buffer.append( 3882 ERR_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_FINAL_DEL_COUNT_1.get()); 3883 } 3884 else 3885 { 3886 buffer.append(ERR_LDAPMODIFY_CLIENT_SIDE_SUB_DEL_FINAL_DEL_COUNT.get( 3887 deletedCount)); 3888 } 3889 3890 finalResult = new LDAPResult(-1, resultCode, buffer.toString(), null, 3891 StaticUtils.NO_STRINGS, StaticUtils.NO_CONTROLS); 3892 } 3893 3894 finalResult = handleJSONEncodedResponseControls(finalResult); 3895 3896 3897 // Display information about the final result. 3898 displayResult(finalResult, useTransaction.isPresent()); 3899 3900 3901 // See if the delete operation succeeded or failed. If it failed, and we 3902 // should end all processing, then throw an exception. 3903 switch (finalResult.getResultCode().intValue()) 3904 { 3905 case ResultCode.SUCCESS_INT_VALUE: 3906 case ResultCode.NO_OPERATION_INT_VALUE: 3907 break; 3908 3909 default: 3910 writeRejectedChange(rejectWriter, null, changeRecord, finalResult); 3911 if (! continueOnError.isPresent()) 3912 { 3913 throw new LDAPException(finalResult); 3914 } 3915 break; 3916 } 3917 3918 return finalResult.getResultCode(); 3919 } 3920 3921 3922 3923 /** 3924 * Performs the appropriate processing for an LDIF modify change record. 3925 * 3926 * @param changeRecord The LDIF modify change record to process. 3927 * @param controls The set of controls to include in the request. 3928 * @param pool The connection pool to use to communicate with 3929 * the directory server. 3930 * @param multiUpdateRequests The list to which the request should be added 3931 * if it is to be processed as part of a 3932 * multi-update operation. It may be 3933 * {@code null} if the operation should not be 3934 * processed via the multi-update operation. 3935 * @param rejectWriter The LDIF writer to use for recording 3936 * information about rejected changes. It may be 3937 * {@code null} if no reject writer is 3938 * configured. 3939 * 3940 * @return The result code obtained from processing. 3941 * 3942 * @throws LDAPException If the operation did not complete successfully 3943 * and processing should not continue. 3944 */ 3945 @NotNull() 3946 ResultCode doModify(@NotNull final LDIFModifyChangeRecord changeRecord, 3947 @NotNull final List<Control> controls, 3948 @NotNull final LDAPConnectionPool pool, 3949 @Nullable final List<LDAPRequest> multiUpdateRequests, 3950 @Nullable final LDIFWriter rejectWriter) 3951 throws LDAPException 3952 { 3953 // Create the modify request to process. 3954 final ModifyRequest modifyRequest = changeRecord.toModifyRequest(true); 3955 for (final Control c : controls) 3956 { 3957 modifyRequest.addControl(c); 3958 } 3959 3960 3961 // If the modify request includes a password change, then add any controls 3962 // that are specific to that. 3963 if (retireCurrentPassword.isPresent() || purgeCurrentPassword.isPresent() || 3964 passwordValidationDetails.isPresent()) 3965 { 3966 for (final Modification m : modifyRequest.getModifications()) 3967 { 3968 final String baseName = m.getAttribute().getBaseName(); 3969 if (baseName.equalsIgnoreCase(ATTR_USER_PASSWORD) || 3970 baseName.equalsIgnoreCase(ATTR_AUTH_PASSWORD)) 3971 { 3972 if (retireCurrentPassword.isPresent()) 3973 { 3974 modifyRequest.addControl(new RetirePasswordRequestControl(false)); 3975 } 3976 else if (purgeCurrentPassword.isPresent()) 3977 { 3978 modifyRequest.addControl(new PurgePasswordRequestControl(false)); 3979 } 3980 3981 if (passwordValidationDetails.isPresent()) 3982 { 3983 modifyRequest.addControl( 3984 new PasswordValidationDetailsRequestControl()); 3985 } 3986 3987 break; 3988 } 3989 } 3990 } 3991 3992 3993 // If the operation should be processed in a multi-update operation, then 3994 // just add the request to the list and return without doing anything else. 3995 if (multiUpdateErrorBehavior.isPresent()) 3996 { 3997 multiUpdateRequests.add(modifyRequest); 3998 commentToOut(INFO_LDAPMODIFY_MODIFY_ADDED_TO_MULTI_UPDATE.get( 3999 modifyRequest.getDN())); 4000 return ResultCode.SUCCESS; 4001 } 4002 4003 4004 // If the --dryRun argument was provided, then we'll stop here. 4005 if (dryRun.isPresent()) 4006 { 4007 commentToOut(INFO_LDAPMODIFY_DRY_RUN_MODIFY.get(modifyRequest.getDN(), 4008 dryRun.getIdentifierString())); 4009 return ResultCode.SUCCESS; 4010 } 4011 4012 4013 // Process the modify operation and get the result. 4014 commentToOut(INFO_LDAPMODIFY_MODIFYING_ENTRY.get(modifyRequest.getDN())); 4015 if (verbose.isPresent()) 4016 { 4017 for (final String ldifLine : 4018 modifyRequest.toLDIFChangeRecord().toLDIF(WRAP_COLUMN)) 4019 { 4020 out(ldifLine); 4021 } 4022 out(); 4023 } 4024 4025 4026 LDAPResult modifyResult; 4027 try 4028 { 4029 modifyResult = pool.modify(modifyRequest); 4030 } 4031 catch (final LDAPException le) 4032 { 4033 Debug.debugException(le); 4034 modifyResult = le.toLDAPResult(); 4035 } 4036 4037 modifyResult = handleJSONEncodedResponseControls(modifyResult); 4038 4039 4040 // Display information about the result. 4041 displayResult(modifyResult, useTransaction.isPresent()); 4042 4043 4044 // See if the modify operation succeeded or failed. If it failed, and we 4045 // should end all processing, then throw an exception. 4046 switch (modifyResult.getResultCode().intValue()) 4047 { 4048 case ResultCode.SUCCESS_INT_VALUE: 4049 case ResultCode.NO_OPERATION_INT_VALUE: 4050 break; 4051 4052 case ResultCode.ASSERTION_FAILED_INT_VALUE: 4053 writeRejectedChange(rejectWriter, 4054 INFO_LDAPMODIFY_ASSERTION_FAILED.get(modifyRequest.getDN(), 4055 String.valueOf(assertionFilter.getValue())), 4056 modifyRequest.toLDIFChangeRecord(), modifyResult); 4057 throw new LDAPException(modifyResult); 4058 4059 default: 4060 writeRejectedChange(rejectWriter, null, 4061 modifyRequest.toLDIFChangeRecord(), modifyResult); 4062 if (useTransaction.isPresent() || (! continueOnError.isPresent())) 4063 { 4064 throw new LDAPException(modifyResult); 4065 } 4066 break; 4067 } 4068 4069 return modifyResult.getResultCode(); 4070 } 4071 4072 4073 4074 /** 4075 * Performs the appropriate processing for an LDIF modify DN change record. 4076 * 4077 * @param changeRecord The LDIF modify DN change record to process. 4078 * @param controls The set of controls to include in the request. 4079 * @param pool The connection pool to use to communicate with 4080 * the directory server. 4081 * @param multiUpdateRequests The list to which the request should be added 4082 * if it is to be processed as part of a 4083 * multi-update operation. It may be 4084 * {@code null} if the operation should not be 4085 * processed via the multi-update operation. 4086 * @param rejectWriter The LDIF writer to use for recording 4087 * information about rejected changes. It may be 4088 * {@code null} if no reject writer is 4089 * configured. 4090 * 4091 * @return The result code obtained from processing. 4092 * 4093 * @throws LDAPException If the operation did not complete successfully 4094 * and processing should not continue. 4095 */ 4096 @NotNull() 4097 private ResultCode doModifyDN( 4098 @NotNull final LDIFModifyDNChangeRecord changeRecord, 4099 @NotNull final List<Control> controls, 4100 @NotNull final LDAPConnectionPool pool, 4101 @Nullable final List<LDAPRequest> multiUpdateRequests, 4102 @Nullable final LDIFWriter rejectWriter) 4103 throws LDAPException 4104 { 4105 // Create the modify DN request to process. 4106 final ModifyDNRequest modifyDNRequest = 4107 changeRecord.toModifyDNRequest(true); 4108 for (final Control c : controls) 4109 { 4110 modifyDNRequest.addControl(c); 4111 } 4112 4113 4114 // If the operation should be processed in a multi-update operation, then 4115 // just add the request to the list and return without doing anything else. 4116 if (multiUpdateErrorBehavior.isPresent()) 4117 { 4118 multiUpdateRequests.add(modifyDNRequest); 4119 commentToOut(INFO_LDAPMODIFY_MODIFY_DN_ADDED_TO_MULTI_UPDATE.get( 4120 modifyDNRequest.getDN())); 4121 return ResultCode.SUCCESS; 4122 } 4123 4124 4125 // Try to determine the new DN that the entry will have after the operation. 4126 DN newDN = null; 4127 try 4128 { 4129 newDN = changeRecord.getNewDN(); 4130 } 4131 catch (final Exception e) 4132 { 4133 Debug.debugException(e); 4134 4135 // This should only happen if the provided DN, new RDN, or new superior DN 4136 // was malformed. Although we could reject the operation now, we'll go 4137 // ahead and send the request to the server in case it has some special 4138 // handling for the DN. 4139 } 4140 4141 4142 // If the --dryRun argument was provided, then we'll stop here. 4143 if (dryRun.isPresent()) 4144 { 4145 if (modifyDNRequest.getNewSuperiorDN() == null) 4146 { 4147 if (newDN == null) 4148 { 4149 commentToOut(INFO_LDAPMODIFY_DRY_RUN_RENAME.get( 4150 modifyDNRequest.getDN(), dryRun.getIdentifierString())); 4151 } 4152 else 4153 { 4154 commentToOut(INFO_LDAPMODIFY_DRY_RUN_RENAME_TO.get( 4155 modifyDNRequest.getDN(), newDN.toString(), 4156 dryRun.getIdentifierString())); 4157 } 4158 } 4159 else 4160 { 4161 if (newDN == null) 4162 { 4163 commentToOut(INFO_LDAPMODIFY_DRY_RUN_MOVE.get( 4164 modifyDNRequest.getDN(), dryRun.getIdentifierString())); 4165 } 4166 else 4167 { 4168 commentToOut(INFO_LDAPMODIFY_DRY_RUN_MOVE_TO.get( 4169 modifyDNRequest.getDN(), newDN.toString(), 4170 dryRun.getIdentifierString())); 4171 } 4172 } 4173 return ResultCode.SUCCESS; 4174 } 4175 4176 4177 // Process the modify DN operation and get the result. 4178 final String currentDN = modifyDNRequest.getDN(); 4179 if (modifyDNRequest.getNewSuperiorDN() == null) 4180 { 4181 if (newDN == null) 4182 { 4183 commentToOut(INFO_LDAPMODIFY_MOVING_ENTRY.get(currentDN)); 4184 } 4185 else 4186 { 4187 commentToOut(INFO_LDAPMODIFY_MOVING_ENTRY_TO.get(currentDN, 4188 newDN.toString())); 4189 } 4190 } 4191 else 4192 { 4193 if (newDN == null) 4194 { 4195 commentToOut(INFO_LDAPMODIFY_RENAMING_ENTRY.get(currentDN)); 4196 } 4197 else 4198 { 4199 commentToOut(INFO_LDAPMODIFY_RENAMING_ENTRY_TO.get(currentDN, 4200 newDN.toString())); 4201 } 4202 } 4203 4204 if (verbose.isPresent()) 4205 { 4206 for (final String ldifLine : 4207 modifyDNRequest.toLDIFChangeRecord().toLDIF(WRAP_COLUMN)) 4208 { 4209 out(ldifLine); 4210 } 4211 out(); 4212 } 4213 4214 4215 LDAPResult modifyDNResult; 4216 try 4217 { 4218 modifyDNResult = pool.modifyDN(modifyDNRequest); 4219 } 4220 catch (final LDAPException le) 4221 { 4222 Debug.debugException(le); 4223 modifyDNResult = le.toLDAPResult(); 4224 } 4225 4226 modifyDNResult = handleJSONEncodedResponseControls(modifyDNResult); 4227 4228 4229 // Display information about the result. 4230 displayResult(modifyDNResult, useTransaction.isPresent()); 4231 4232 4233 // See if the modify DN operation succeeded or failed. If it failed, and we 4234 // should end all processing, then throw an exception. 4235 switch (modifyDNResult.getResultCode().intValue()) 4236 { 4237 case ResultCode.SUCCESS_INT_VALUE: 4238 case ResultCode.NO_OPERATION_INT_VALUE: 4239 break; 4240 4241 case ResultCode.ASSERTION_FAILED_INT_VALUE: 4242 writeRejectedChange(rejectWriter, 4243 INFO_LDAPMODIFY_ASSERTION_FAILED.get(modifyDNRequest.getDN(), 4244 String.valueOf(assertionFilter.getValue())), 4245 modifyDNRequest.toLDIFChangeRecord(), modifyDNResult); 4246 throw new LDAPException(modifyDNResult); 4247 4248 default: 4249 writeRejectedChange(rejectWriter, null, 4250 modifyDNRequest.toLDIFChangeRecord(), modifyDNResult); 4251 if (useTransaction.isPresent() || (! continueOnError.isPresent())) 4252 { 4253 throw new LDAPException(modifyDNResult); 4254 } 4255 break; 4256 } 4257 4258 return modifyDNResult.getResultCode(); 4259 } 4260 4261 4262 4263 /** 4264 * Displays information about the provided result, including special 4265 * processing for a number of supported response controls. 4266 * 4267 * @param result The result to examine. 4268 * @param inTransaction Indicates whether the operation is part of a 4269 * transaction. 4270 */ 4271 private void displayResult(@NotNull final LDAPResult result, 4272 final boolean inTransaction) 4273 { 4274 final ArrayList<String> resultLines = new ArrayList<>(10); 4275 ResultUtils.formatResult(resultLines, result, true, inTransaction, 0, 4276 WRAP_COLUMN); 4277 4278 if (result.getResultCode() == ResultCode.SUCCESS) 4279 { 4280 for (final String line : resultLines) 4281 { 4282 out(line); 4283 } 4284 out(); 4285 } 4286 else 4287 { 4288 for (final String line : resultLines) 4289 { 4290 err(line); 4291 } 4292 err(); 4293 } 4294 } 4295 4296 4297 4298 /** 4299 * Writes a line-wrapped, commented version of the provided message to 4300 * standard output. 4301 * 4302 * @param message The message to be written. 4303 */ 4304 private void commentToOut(@NotNull final String message) 4305 { 4306 for (final String line : StaticUtils.wrapLine(message, WRAP_COLUMN - 2)) 4307 { 4308 out("# ", line); 4309 } 4310 } 4311 4312 4313 4314 /** 4315 * Writes a line-wrapped, commented version of the provided message to 4316 * standard error. 4317 * 4318 * @param message The message to be written. 4319 */ 4320 private void commentToErr(@NotNull final String message) 4321 { 4322 for (final String line : StaticUtils.wrapLine(message, WRAP_COLUMN - 2)) 4323 { 4324 err("# ", line); 4325 } 4326 } 4327 4328 4329 4330 /** 4331 * Writes information about the rejected change to the reject writer. 4332 * 4333 * @param writer The LDIF writer to which the information should be 4334 * written. It may be {@code null} if no reject file is 4335 * configured. 4336 * @param comment The comment to include before the change record, in 4337 * addition to the comment generated from the provided 4338 * LDAP result. It may be {@code null} if no additional 4339 * comment should be included. 4340 * @param changeRecord The LDIF change record to be written. It must not 4341 * be {@code null}. 4342 * @param ldapResult The LDAP result for the failed operation. It must 4343 * not be {@code null}. 4344 */ 4345 private void writeRejectedChange(@Nullable final LDIFWriter writer, 4346 @Nullable final String comment, 4347 @NotNull final LDIFChangeRecord changeRecord, 4348 @NotNull final LDAPResult ldapResult) 4349 { 4350 if (writer == null) 4351 { 4352 return; 4353 } 4354 4355 4356 final StringBuilder buffer = new StringBuilder(); 4357 if (comment != null) 4358 { 4359 buffer.append(comment); 4360 buffer.append(StaticUtils.EOL); 4361 buffer.append(StaticUtils.EOL); 4362 } 4363 4364 final ArrayList<String> resultLines = new ArrayList<>(10); 4365 ResultUtils.formatResult(resultLines, ldapResult, false, false, 0, 0); 4366 for (final String resultLine : resultLines) 4367 { 4368 buffer.append(resultLine); 4369 buffer.append(StaticUtils.EOL); 4370 } 4371 4372 writeRejectedChange(writer, buffer.toString(), changeRecord); 4373 } 4374 4375 4376 4377 /** 4378 * Writes information about the rejected change to the reject writer. 4379 * 4380 * @param writer The LDIF writer to which the information should be 4381 * written. It may be {@code null} if no reject file is 4382 * configured. 4383 * @param comment The comment to include before the change record. It 4384 * may be {@code null} if no comment should be included. 4385 * @param changeRecord The LDIF change record to be written. It may be 4386 * {@code null} if only a comment should be written. 4387 */ 4388 void writeRejectedChange(@Nullable final LDIFWriter writer, 4389 @Nullable final String comment, 4390 @Nullable final LDIFChangeRecord changeRecord) 4391 { 4392 if (writer == null) 4393 { 4394 return; 4395 } 4396 4397 if (rejectWritten.compareAndSet(false, true)) 4398 { 4399 try 4400 { 4401 writer.writeVersionHeader(); 4402 } 4403 catch (final Exception e) 4404 { 4405 Debug.debugException(e); 4406 } 4407 } 4408 4409 try 4410 { 4411 if (comment != null) 4412 { 4413 writer.writeComment(comment, true, false); 4414 } 4415 4416 if (changeRecord != null) 4417 { 4418 writer.writeChangeRecord(changeRecord); 4419 } 4420 } 4421 catch (final Exception e) 4422 { 4423 Debug.debugException(e); 4424 4425 commentToErr(ERR_LDAPMODIFY_UNABLE_TO_WRITE_REJECTED_CHANGE.get( 4426 rejectFile.getValue().getAbsolutePath(), 4427 StaticUtils.getExceptionMessage(e))); 4428 } 4429 } 4430 4431 4432 4433 /** 4434 * {@inheritDoc} 4435 */ 4436 @Override() 4437 public void handleUnsolicitedNotification( 4438 @NotNull final LDAPConnection connection, 4439 @NotNull final ExtendedResult notification) 4440 { 4441 final ArrayList<String> lines = new ArrayList<>(10); 4442 ResultUtils.formatUnsolicitedNotification(lines, notification, true, 0, 4443 WRAP_COLUMN); 4444 for (final String line : lines) 4445 { 4446 err(line); 4447 } 4448 err(); 4449 } 4450 4451 4452 4453 /** 4454 * Examines the provided LDAP result to see if it includes a JSONf-formatted 4455 * response control. If so, then its embedded controls will be extracted and 4456 * a new LDAP result will be returned with those extracted controls instead 4457 * of the JSON-formatted response control. Otherwise, the provided LDAP 4458 * result will be returned. 4459 * 4460 * @param ldapResult The LDAP result to be handled. It must not be 4461 * {@code null}. 4462 * 4463 * @return A new LDAP result with the controls extracted from a 4464 * JSON-formatted response control, or the original LDAP result if 4465 * it did not include a JSON-formatted response control. 4466 */ 4467 @NotNull() 4468 static LDAPResult handleJSONEncodedResponseControls( 4469 @NotNull final LDAPResult ldapResult) 4470 { 4471 try 4472 { 4473 final JSONFormattedResponseControl jsonFormattedResponseControl = 4474 JSONFormattedResponseControl.get(ldapResult); 4475 if (jsonFormattedResponseControl == null) 4476 { 4477 return ldapResult; 4478 } 4479 4480 final JSONFormattedControlDecodeBehavior decodeBehavior = 4481 new JSONFormattedControlDecodeBehavior(); 4482 decodeBehavior.setThrowOnUnparsableObject(false); 4483 decodeBehavior.setThrowOnInvalidCriticalControl(false); 4484 decodeBehavior.setThrowOnInvalidNonCriticalControl(false); 4485 decodeBehavior.setThrowOnInvalidNonCriticalControl(false); 4486 decodeBehavior.setAllowEmbeddedJSONFormattedControl(true); 4487 decodeBehavior.setStrict(false); 4488 4489 final List<Control> decodedControls = 4490 jsonFormattedResponseControl.decodeEmbeddedControls( 4491 decodeBehavior, null); 4492 4493 return new LDAPResult(ldapResult.getMessageID(), 4494 ldapResult.getResultCode(), 4495 ldapResult.getDiagnosticMessage(), 4496 ldapResult.getMatchedDN(), 4497 ldapResult.getReferralURLs(), 4498 StaticUtils.toArray(decodedControls, Control.class)); 4499 } 4500 catch (final LDAPException e) 4501 { 4502 Debug.debugException(e); 4503 return ldapResult; 4504 } 4505 } 4506 4507 4508 4509 /** 4510 * {@inheritDoc} 4511 */ 4512 @Override() 4513 @NotNull() 4514 public LinkedHashMap<String[],String> getExampleUsages() 4515 { 4516 final LinkedHashMap<String[],String> examples = 4517 new LinkedHashMap<>(StaticUtils.computeMapCapacity(2)); 4518 4519 final String[] args1 = 4520 { 4521 "--hostname", "ldap.example.com", 4522 "--port", "389", 4523 "--bindDN", "uid=admin,dc=example,dc=com", 4524 "--bindPassword", "password", 4525 "--defaultAdd" 4526 }; 4527 examples.put(args1, INFO_LDAPMODIFY_EXAMPLE_1.get()); 4528 4529 final String[] args2 = 4530 { 4531 "--hostname", "ds1.example.com", 4532 "--port", "636", 4533 "--hostname", "ds2.example.com", 4534 "--port", "636", 4535 "--useSSL", 4536 "--bindDN", "uid=admin,dc=example,dc=com", 4537 "--bindPassword", "password", 4538 "--ldifFile", "changes.ldif", 4539 "--modifyEntriesMatchingFilter", "(objectClass=person)", 4540 "--searchPageSize", "100" 4541 }; 4542 examples.put(args2, INFO_LDAPMODIFY_EXAMPLE_2.get()); 4543 4544 return examples; 4545 } 4546}