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