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