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