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