001/* 002 * Copyright 2015-2020 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2015-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) 2015-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.util.args; 037 038 039 040import java.util.ArrayList; 041import java.util.Collections; 042import java.util.HashMap; 043import java.util.Iterator; 044import java.util.List; 045import java.util.Map; 046 047import com.unboundid.asn1.ASN1OctetString; 048import com.unboundid.ldap.sdk.Control; 049import com.unboundid.ldap.sdk.controls.AuthorizationIdentityRequestControl; 050import com.unboundid.ldap.sdk.controls.DontUseCopyRequestControl; 051import com.unboundid.ldap.sdk.controls.ManageDsaITRequestControl; 052import com.unboundid.ldap.sdk.controls.PermissiveModifyRequestControl; 053import com.unboundid.ldap.sdk.controls.SubentriesRequestControl; 054import com.unboundid.ldap.sdk.controls.SubtreeDeleteRequestControl; 055import com.unboundid.ldap.sdk.experimental. 056 DraftBeheraLDAPPasswordPolicy10RequestControl; 057import com.unboundid.ldap.sdk.experimental. 058 DraftZeilengaLDAPNoOp12RequestControl; 059import com.unboundid.util.Base64; 060import com.unboundid.util.Debug; 061import com.unboundid.util.Mutable; 062import com.unboundid.util.StaticUtils; 063import com.unboundid.util.ThreadSafety; 064import com.unboundid.util.ThreadSafetyLevel; 065 066import static com.unboundid.util.args.ArgsMessages.*; 067 068 069 070/** 071 * This class defines an argument that is intended to hold information about one 072 * or more LDAP controls. Values for this argument must be in one of the 073 * following formats: 074 * <UL> 075 * <LI> 076 * oid -- The numeric OID for the control. The control will not be critical 077 * and will not have a value. 078 * </LI> 079 * <LI> 080 * oid:criticality -- The numeric OID followed by a colon and the 081 * criticality. The control will be critical if the criticality value is 082 * any of the following: {@code true}, {@code t}, {@code yes}, {@code y}, 083 * {@code on}, or {@code 1}. The control will be non-critical if the 084 * criticality value is any of the following: {@code false}, {@code f}, 085 * {@code no}, {@code n}, {@code off}, or {@code 0}. No other criticality 086 * values will be accepted. 087 * </LI> 088 * <LI> 089 * oid:criticality:value -- The numeric OID followed by a colon and the 090 * criticality, then a colon and then a string that represents the value for 091 * the control. 092 * </LI> 093 * <LI> 094 * oid:criticality::base64value -- The numeric OID followed by a colon and 095 * the criticality, then two colons and then a string that represents the 096 * base64-encoded value for the control. 097 * </LI> 098 * </UL> 099 */ 100@Mutable() 101@ThreadSafety(level=ThreadSafetyLevel.NOT_THREADSAFE) 102public final class ControlArgument 103 extends Argument 104{ 105 /** 106 * A map of human-readable names to the corresponding numeric OIDs. 107 */ 108 private static final Map<String,String> OIDS_BY_NAME; 109 static 110 { 111 final HashMap<String,String> oidsByName = 112 new HashMap<>(StaticUtils.computeMapCapacity(100)); 113 114 // The authorization identity request control. 115 oidsByName.put("authzid", 116 AuthorizationIdentityRequestControl. 117 AUTHORIZATION_IDENTITY_REQUEST_OID); 118 oidsByName.put("authorizationidentity", 119 AuthorizationIdentityRequestControl. 120 AUTHORIZATION_IDENTITY_REQUEST_OID); 121 oidsByName.put("authorization-identity", 122 AuthorizationIdentityRequestControl. 123 AUTHORIZATION_IDENTITY_REQUEST_OID); 124 125 // The don't use copy request control. 126 oidsByName.put("nocopy", 127 DontUseCopyRequestControl.DONT_USE_COPY_REQUEST_OID); 128 oidsByName.put("dontusecopy", 129 DontUseCopyRequestControl.DONT_USE_COPY_REQUEST_OID); 130 oidsByName.put("no-copy", 131 DontUseCopyRequestControl.DONT_USE_COPY_REQUEST_OID); 132 oidsByName.put("dont-use-copy", 133 DontUseCopyRequestControl.DONT_USE_COPY_REQUEST_OID); 134 135 // The LDAP no-operation request control. 136 oidsByName.put("noop", 137 DraftZeilengaLDAPNoOp12RequestControl.NO_OP_REQUEST_OID); 138 oidsByName.put("nooperation", 139 DraftZeilengaLDAPNoOp12RequestControl.NO_OP_REQUEST_OID); 140 oidsByName.put("no-op", 141 DraftZeilengaLDAPNoOp12RequestControl.NO_OP_REQUEST_OID); 142 oidsByName.put("no-operation", 143 DraftZeilengaLDAPNoOp12RequestControl.NO_OP_REQUEST_OID); 144 145 // The LDAP subentries request control. 146 oidsByName.put("subentries", 147 SubentriesRequestControl.SUBENTRIES_REQUEST_OID); 148 oidsByName.put("ldapsubentries", 149 SubentriesRequestControl.SUBENTRIES_REQUEST_OID); 150 oidsByName.put("ldap-subentries", 151 SubentriesRequestControl.SUBENTRIES_REQUEST_OID); 152 153 // The manage DSA IT request control. 154 oidsByName.put("managedsait", 155 ManageDsaITRequestControl.MANAGE_DSA_IT_REQUEST_OID); 156 oidsByName.put("manage-dsa-it", 157 ManageDsaITRequestControl.MANAGE_DSA_IT_REQUEST_OID); 158 159 // The permissive modify request control. 160 oidsByName.put("permissivemodify", 161 PermissiveModifyRequestControl.PERMISSIVE_MODIFY_REQUEST_OID); 162 oidsByName.put("permissive-modify", 163 PermissiveModifyRequestControl.PERMISSIVE_MODIFY_REQUEST_OID); 164 165 // The password policy request control. 166 oidsByName.put("pwpolicy", 167 DraftBeheraLDAPPasswordPolicy10RequestControl. 168 PASSWORD_POLICY_REQUEST_OID); 169 oidsByName.put("passwordpolicy", 170 DraftBeheraLDAPPasswordPolicy10RequestControl. 171 PASSWORD_POLICY_REQUEST_OID); 172 oidsByName.put("pw-policy", 173 DraftBeheraLDAPPasswordPolicy10RequestControl. 174 PASSWORD_POLICY_REQUEST_OID); 175 oidsByName.put("password-policy", 176 DraftBeheraLDAPPasswordPolicy10RequestControl. 177 PASSWORD_POLICY_REQUEST_OID); 178 179 // The subtree delete request control. 180 oidsByName.put("subtreedelete", 181 SubtreeDeleteRequestControl.SUBTREE_DELETE_REQUEST_OID); 182 oidsByName.put("treedelete", 183 SubtreeDeleteRequestControl.SUBTREE_DELETE_REQUEST_OID); 184 oidsByName.put("subtree-delete", 185 SubtreeDeleteRequestControl.SUBTREE_DELETE_REQUEST_OID); 186 oidsByName.put("tree-delete", 187 SubtreeDeleteRequestControl.SUBTREE_DELETE_REQUEST_OID); 188 189 // The account usable request control. 190 oidsByName.put("accountusable", "1.3.6.1.4.1.42.2.27.9.5.8"); 191 oidsByName.put("accountusability", "1.3.6.1.4.1.42.2.27.9.5.8"); 192 oidsByName.put("account-usable", "1.3.6.1.4.1.42.2.27.9.5.8"); 193 oidsByName.put("account-usability", "1.3.6.1.4.1.42.2.27.9.5.8"); 194 195 // The generate password request control. 196 oidsByName.put("generatepassword", "1.3.6.1.4.1.30221.2.5.58"); 197 oidsByName.put("generate-password", "1.3.6.1.4.1.30221.2.5.58"); 198 oidsByName.put("generatepw", "1.3.6.1.4.1.30221.2.5.58"); 199 oidsByName.put("generate-pw", "1.3.6.1.4.1.30221.2.5.58"); 200 201 // The get backend set ID request control. 202 oidsByName.put("backendsetid", "1.3.6.1.4.1.30221.2.5.33"); 203 oidsByName.put("getbackendsetid", "1.3.6.1.4.1.30221.2.5.33"); 204 oidsByName.put("backendset-id", "1.3.6.1.4.1.30221.2.5.33"); 205 oidsByName.put("backend-set-id", "1.3.6.1.4.1.30221.2.5.33"); 206 oidsByName.put("get-backendset-id", "1.3.6.1.4.1.30221.2.5.33"); 207 oidsByName.put("get-backend-set-id", "1.3.6.1.4.1.30221.2.5.33"); 208 209 // The get effective rights request control. 210 oidsByName.put("effectiverights", "1.3.6.1.4.1.42.2.27.9.5.2"); 211 oidsByName.put("geteffectiverights", "1.3.6.1.4.1.42.2.27.9.5.2"); 212 oidsByName.put("effective-rights", "1.3.6.1.4.1.42.2.27.9.5.2"); 213 oidsByName.put("get-effective-rights", "1.3.6.1.4.1.42.2.27.9.5.2"); 214 215 // The get password policy state issues request control. 216 oidsByName.put("pwpolicystateissues", "1.3.6.1.4.1.30221.2.5.46"); 217 oidsByName.put("getpwpolicystateissues", "1.3.6.1.4.1.30221.2.5.46"); 218 oidsByName.put("passwordpolicystateissues", "1.3.6.1.4.1.30221.2.5.46"); 219 oidsByName.put("getpasswordpolicystateissues", "1.3.6.1.4.1.30221.2.5.46"); 220 oidsByName.put("pw-policy-state-issues", "1.3.6.1.4.1.30221.2.5.46"); 221 oidsByName.put("get-pw-policy-state-issues", "1.3.6.1.4.1.30221.2.5.46"); 222 oidsByName.put("password-policy-state-issues", "1.3.6.1.4.1.30221.2.5.46"); 223 oidsByName.put("get-password-policy-state-issues", 224 "1.3.6.1.4.1.30221.2.5.46"); 225 226 // The get server ID request control. 227 oidsByName.put("serverid", "1.3.6.1.4.1.30221.2.5.14"); 228 oidsByName.put("getserverid", "1.3.6.1.4.1.30221.2.5.14"); 229 oidsByName.put("server-id", "1.3.6.1.4.1.30221.2.5.14"); 230 oidsByName.put("get-server-id", "1.3.6.1.4.1.30221.2.5.14"); 231 232 // The get user resource limits request control. 233 oidsByName.put("userresourcelimits", "1.3.6.1.4.1.30221.2.5.25"); 234 oidsByName.put("getuserresourcelimits", "1.3.6.1.4.1.30221.2.5.25"); 235 oidsByName.put("user-resource-limits", "1.3.6.1.4.1.30221.2.5.25"); 236 oidsByName.put("get-user-resource-limits", "1.3.6.1.4.1.30221.2.5.25"); 237 238 // The hard delete request control. 239 oidsByName.put("harddelete", "1.3.6.1.4.1.30221.2.5.22"); 240 oidsByName.put("hard-delete", "1.3.6.1.4.1.30221.2.5.22"); 241 242 // The ignore NO-USER-MODIFICATION request control. 243 oidsByName.put("ignorenousermod", "1.3.6.1.4.1.30221.2.5.5"); 244 oidsByName.put("ignorenousermodification", "1.3.6.1.4.1.30221.2.5.5"); 245 oidsByName.put("ignore-no-user-mod", "1.3.6.1.4.1.30221.2.5.5"); 246 oidsByName.put("ignore-no-user-modification", "1.3.6.1.4.1.30221.2.5.5"); 247 248 // The purge retired password request control. 249 oidsByName.put("purgepassword", "1.3.6.1.4.1.30221.2.5.32"); 250 oidsByName.put("purgeretiredpassword", "1.3.6.1.4.1.30221.2.5.32"); 251 oidsByName.put("purge-password", "1.3.6.1.4.1.30221.2.5.32"); 252 oidsByName.put("purge-retired-password", "1.3.6.1.4.1.30221.2.5.32"); 253 254 // The real attributes only request control. 255 oidsByName.put("realattrsonly", "2.16.840.1.113730.3.4.17"); 256 oidsByName.put("realattributesonly", "2.16.840.1.113730.3.4.17"); 257 oidsByName.put("real-attrs-only", "2.16.840.1.113730.3.4.17"); 258 oidsByName.put("real-attributes-only", "2.16.840.1.113730.3.4.17"); 259 260 // The replication repair request control. 261 oidsByName.put("replrepair", "1.3.6.1.4.1.30221.1.5.2"); 262 oidsByName.put("replicationrepair", "1.3.6.1.4.1.30221.1.5.2"); 263 oidsByName.put("repl-repair", "1.3.6.1.4.1.30221.1.5.2"); 264 oidsByName.put("replication-repair", "1.3.6.1.4.1.30221.1.5.2"); 265 266 // The retain identity request control. 267 oidsByName.put("retainidentity", "1.3.6.1.4.1.30221.2.5.3"); 268 oidsByName.put("retain-identity", "1.3.6.1.4.1.30221.2.5.3"); 269 270 // The retire password request control. 271 oidsByName.put("retirepassword", "1.3.6.1.4.1.30221.2.5.31"); 272 oidsByName.put("retire-password", "1.3.6.1.4.1.30221.2.5.31"); 273 274 // The return conflict entries request control. 275 oidsByName.put("returnconflictentries", "1.3.6.1.4.1.30221.2.5.13"); 276 oidsByName.put("return-conflict-entries", "1.3.6.1.4.1.30221.2.5.13"); 277 278 // The soft delete request control. 279 oidsByName.put("softdelete", "1.3.6.1.4.1.30221.2.5.20"); 280 oidsByName.put("soft-delete", "1.3.6.1.4.1.30221.2.5.20"); 281 282 // The soft-deleted entry access request control. 283 oidsByName.put("softdeleteentryaccess", "1.3.6.1.4.1.30221.2.5.24"); 284 oidsByName.put("softdeletedentryaccess", "1.3.6.1.4.1.30221.2.5.24"); 285 oidsByName.put("soft-delete-entry-access", "1.3.6.1.4.1.30221.2.5.24"); 286 oidsByName.put("soft-deleted-entry-access", "1.3.6.1.4.1.30221.2.5.24"); 287 288 // The suppress referential integrity updates request control. 289 oidsByName.put("suppressreferentialintegrity", "1.3.6.1.4.1.30221.2.5.30"); 290 oidsByName.put("suppressreferentialintegrityupdates", 291 "1.3.6.1.4.1.30221.2.5.30"); 292 oidsByName.put("suppress-referential-integrity", 293 "1.3.6.1.4.1.30221.2.5.30"); 294 oidsByName.put("suppress-referential-integrity-updates", 295 "1.3.6.1.4.1.30221.2.5.30"); 296 297 // The undelete request control. 298 oidsByName.put("undelete", "1.3.6.1.4.1.30221.2.5.23"); 299 300 // The virtual attributes only request control. 301 oidsByName.put("virtualattrsonly", "2.16.840.1.113730.3.4.19"); 302 oidsByName.put("virtualattributesonly", "2.16.840.1.113730.3.4.19"); 303 oidsByName.put("virtual-attrs-only", "2.16.840.1.113730.3.4.19"); 304 oidsByName.put("virtual-attributes-only", "2.16.840.1.113730.3.4.19"); 305 306 OIDS_BY_NAME = Collections.unmodifiableMap(oidsByName); 307 } 308 309 310 311 /** 312 * The serial version UID for this serializable class. 313 */ 314 private static final long serialVersionUID = -1889200072476038957L; 315 316 317 318 // The argument value validators that have been registered for this argument. 319 private final List<ArgumentValueValidator> validators; 320 321 // The list of default values for this argument. 322 private final List<Control> defaultValues; 323 324 // The set of values assigned to this argument. 325 private final List<Control> values; 326 327 328 329 /** 330 * Creates a new control argument with the provided information. It will not 331 * be required, will be allowed any number of times, will use a default 332 * placeholder, and will not have a default value. 333 * 334 * @param shortIdentifier The short identifier for this argument. It may 335 * not be {@code null} if the long identifier is 336 * {@code null}. 337 * @param longIdentifier The long identifier for this argument. It may 338 * not be {@code null} if the short identifier is 339 * {@code null}. 340 * @param description A human-readable description for this argument. 341 * It must not be {@code null}. 342 * 343 * @throws ArgumentException If there is a problem with the definition of 344 * this argument. 345 */ 346 public ControlArgument(final Character shortIdentifier, 347 final String longIdentifier, final String description) 348 throws ArgumentException 349 { 350 this(shortIdentifier, longIdentifier, false, 0, null, description); 351 } 352 353 354 355 /** 356 * Creates a new control argument with the provided information. It will not 357 * have a default value. 358 * 359 * @param shortIdentifier The short identifier for this argument. It may 360 * not be {@code null} if the long identifier is 361 * {@code null}. 362 * @param longIdentifier The long identifier for this argument. It may 363 * not be {@code null} if the short identifier is 364 * {@code null}. 365 * @param isRequired Indicates whether this argument is required to 366 * be provided. 367 * @param maxOccurrences The maximum number of times this argument may be 368 * provided on the command line. A value less than 369 * or equal to zero indicates that it may be present 370 * any number of times. 371 * @param valuePlaceholder A placeholder to display in usage information to 372 * indicate that a value must be provided. It may 373 * be {@code null} to use a default placeholder that 374 * describes the expected syntax for values. 375 * @param description A human-readable description for this argument. 376 * It must not be {@code null}. 377 * 378 * @throws ArgumentException If there is a problem with the definition of 379 * this argument. 380 */ 381 public ControlArgument(final Character shortIdentifier, 382 final String longIdentifier, final boolean isRequired, 383 final int maxOccurrences, 384 final String valuePlaceholder, 385 final String description) 386 throws ArgumentException 387 { 388 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 389 valuePlaceholder, description, (List<Control>) null); 390 } 391 392 393 394 /** 395 * Creates a new control argument with the provided information. 396 * 397 * @param shortIdentifier The short identifier for this argument. It may 398 * not be {@code null} if the long identifier is 399 * {@code null}. 400 * @param longIdentifier The long identifier for this argument. It may 401 * not be {@code null} if the short identifier is 402 * {@code null}. 403 * @param isRequired Indicates whether this argument is required to 404 * be provided. 405 * @param maxOccurrences The maximum number of times this argument may be 406 * provided on the command line. A value less than 407 * or equal to zero indicates that it may be present 408 * any number of times. 409 * @param valuePlaceholder A placeholder to display in usage information to 410 * indicate that a value must be provided. It may 411 * be {@code null} to use a default placeholder that 412 * describes the expected syntax for values. 413 * @param description A human-readable description for this argument. 414 * It must not be {@code null}. 415 * @param defaultValue The default value to use for this argument if no 416 * values were provided. It may be {@code null} if 417 * there should be no default values. 418 * 419 * @throws ArgumentException If there is a problem with the definition of 420 * this argument. 421 */ 422 public ControlArgument(final Character shortIdentifier, 423 final String longIdentifier, final boolean isRequired, 424 final int maxOccurrences, 425 final String valuePlaceholder, 426 final String description, final Control defaultValue) 427 throws ArgumentException 428 { 429 this(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 430 valuePlaceholder, description, 431 ((defaultValue == null) 432 ? null : 433 Collections.singletonList(defaultValue))); 434 } 435 436 437 438 /** 439 * Creates a new control argument with the provided information. 440 * 441 * @param shortIdentifier The short identifier for this argument. It may 442 * not be {@code null} if the long identifier is 443 * {@code null}. 444 * @param longIdentifier The long identifier for this argument. It may 445 * not be {@code null} if the short identifier is 446 * {@code null}. 447 * @param isRequired Indicates whether this argument is required to 448 * be provided. 449 * @param maxOccurrences The maximum number of times this argument may be 450 * provided on the command line. A value less than 451 * or equal to zero indicates that it may be present 452 * any number of times. 453 * @param valuePlaceholder A placeholder to display in usage information to 454 * indicate that a value must be provided. It may 455 * be {@code null} to use a default placeholder that 456 * describes the expected syntax for values. 457 * @param description A human-readable description for this argument. 458 * It must not be {@code null}. 459 * @param defaultValues The set of default values to use for this 460 * argument if no values were provided. 461 * 462 * @throws ArgumentException If there is a problem with the definition of 463 * this argument. 464 */ 465 public ControlArgument(final Character shortIdentifier, 466 final String longIdentifier, final boolean isRequired, 467 final int maxOccurrences, 468 final String valuePlaceholder, 469 final String description, 470 final List<Control> defaultValues) 471 throws ArgumentException 472 { 473 super(shortIdentifier, longIdentifier, isRequired, maxOccurrences, 474 (valuePlaceholder == null) 475 ? INFO_PLACEHOLDER_CONTROL.get() 476 : valuePlaceholder, 477 description); 478 479 if ((defaultValues == null) || defaultValues.isEmpty()) 480 { 481 this.defaultValues = null; 482 } 483 else 484 { 485 this.defaultValues = Collections.unmodifiableList(defaultValues); 486 } 487 488 values = new ArrayList<>(5); 489 validators = new ArrayList<>(5); 490 } 491 492 493 494 /** 495 * Creates a new control argument that is a "clean" copy of the provided 496 * source argument. 497 * 498 * @param source The source argument to use for this argument. 499 */ 500 private ControlArgument(final ControlArgument source) 501 { 502 super(source); 503 504 defaultValues = source.defaultValues; 505 validators = new ArrayList<>(source.validators); 506 values = new ArrayList<>(5); 507 } 508 509 510 511 /** 512 * Retrieves the list of default values for this argument, which will be used 513 * if no values were provided. 514 * 515 * @return The list of default values for this argument, or {@code null} if 516 * there are no default values. 517 */ 518 public List<Control> getDefaultValues() 519 { 520 return defaultValues; 521 } 522 523 524 525 /** 526 * Updates this argument to ensure that the provided validator will be invoked 527 * for any values provided to this argument. This validator will be invoked 528 * after all other validation has been performed for this argument. 529 * 530 * @param validator The argument value validator to be invoked. It must not 531 * be {@code null}. 532 */ 533 public void addValueValidator(final ArgumentValueValidator validator) 534 { 535 validators.add(validator); 536 } 537 538 539 540 /** 541 * {@inheritDoc} 542 */ 543 @Override() 544 protected void addValue(final String valueString) 545 throws ArgumentException 546 { 547 String oid = null; 548 boolean isCritical = false; 549 ASN1OctetString value = null; 550 551 final int firstColonPos = valueString.indexOf(':'); 552 if (firstColonPos < 0) 553 { 554 oid = valueString; 555 } 556 else 557 { 558 oid = valueString.substring(0, firstColonPos); 559 560 final String criticalityStr; 561 final int secondColonPos = valueString.indexOf(':', (firstColonPos+1)); 562 if (secondColonPos < 0) 563 { 564 criticalityStr = valueString.substring(firstColonPos+1); 565 } 566 else 567 { 568 criticalityStr = valueString.substring(firstColonPos+1, secondColonPos); 569 570 final int doubleColonPos = valueString.indexOf("::"); 571 if (doubleColonPos == secondColonPos) 572 { 573 try 574 { 575 value = new ASN1OctetString( 576 Base64.decode(valueString.substring(doubleColonPos+2))); 577 } 578 catch (final Exception e) 579 { 580 Debug.debugException(e); 581 throw new ArgumentException( 582 ERR_CONTROL_ARG_INVALID_BASE64_VALUE.get(valueString, 583 getIdentifierString(), 584 valueString.substring(doubleColonPos+2)), 585 e); 586 } 587 } 588 else 589 { 590 value = new ASN1OctetString(valueString.substring(secondColonPos+1)); 591 } 592 } 593 594 final String lowerCriticalityStr = 595 StaticUtils.toLowerCase(criticalityStr); 596 if (lowerCriticalityStr.equals("true") || 597 lowerCriticalityStr.equals("t") || 598 lowerCriticalityStr.equals("yes") || 599 lowerCriticalityStr.equals("y") || 600 lowerCriticalityStr.equals("on") || 601 lowerCriticalityStr.equals("1")) 602 { 603 isCritical = true; 604 } 605 else if (lowerCriticalityStr.equals("false") || 606 lowerCriticalityStr.equals("f") || 607 lowerCriticalityStr.equals("no") || 608 lowerCriticalityStr.equals("n") || 609 lowerCriticalityStr.equals("off") || 610 lowerCriticalityStr.equals("0")) 611 { 612 isCritical = false; 613 } 614 else 615 { 616 throw new ArgumentException(ERR_CONTROL_ARG_INVALID_CRITICALITY.get( 617 valueString, getIdentifierString(), criticalityStr)); 618 } 619 } 620 621 if (! StaticUtils.isNumericOID(oid)) 622 { 623 final String providedOID = oid; 624 oid = OIDS_BY_NAME.get(StaticUtils.toLowerCase(providedOID)); 625 if (oid == null) 626 { 627 throw new ArgumentException(ERR_CONTROL_ARG_INVALID_OID.get( 628 valueString, getIdentifierString(), providedOID)); 629 } 630 } 631 632 if (values.size() >= getMaxOccurrences()) 633 { 634 throw new ArgumentException(ERR_ARG_MAX_OCCURRENCES_EXCEEDED.get( 635 getIdentifierString())); 636 } 637 638 for (final ArgumentValueValidator v : validators) 639 { 640 v.validateArgumentValue(this, valueString); 641 } 642 643 values.add(new Control(oid, isCritical, value)); 644 } 645 646 647 648 /** 649 * Retrieves the value for this argument, or the default value if none was 650 * provided. If there are multiple values, then the first will be returned. 651 * 652 * @return The value for this argument, or the default value if none was 653 * provided, or {@code null} if there is no value and no default 654 * value. 655 */ 656 public Control getValue() 657 { 658 if (values.isEmpty()) 659 { 660 if ((defaultValues == null) || defaultValues.isEmpty()) 661 { 662 return null; 663 } 664 else 665 { 666 return defaultValues.get(0); 667 } 668 } 669 else 670 { 671 return values.get(0); 672 } 673 } 674 675 676 677 /** 678 * Retrieves the set of values for this argument, or the default values if 679 * none were provided. 680 * 681 * @return The set of values for this argument, or the default values if none 682 * were provided. 683 */ 684 public List<Control> getValues() 685 { 686 if (values.isEmpty() && (defaultValues != null)) 687 { 688 return defaultValues; 689 } 690 691 return Collections.unmodifiableList(values); 692 } 693 694 695 696 /** 697 * {@inheritDoc} 698 */ 699 @Override() 700 public List<String> getValueStringRepresentations(final boolean useDefault) 701 { 702 final List<Control> controls; 703 if (values.isEmpty()) 704 { 705 if (useDefault) 706 { 707 controls = defaultValues; 708 } 709 else 710 { 711 return Collections.emptyList(); 712 } 713 } 714 else 715 { 716 controls = values; 717 } 718 719 if ((controls == null) || controls.isEmpty()) 720 { 721 return Collections.emptyList(); 722 } 723 724 final StringBuilder buffer = new StringBuilder(); 725 final ArrayList<String> valueStrings = new ArrayList<>(controls.size()); 726 for (final Control c : controls) 727 { 728 buffer.setLength(0); 729 buffer.append(c.getOID()); 730 buffer.append(':'); 731 buffer.append(c.isCritical()); 732 733 if (c.hasValue()) 734 { 735 final byte[] valueBytes = c.getValue().getValue(); 736 if (StaticUtils.isPrintableString(valueBytes)) 737 { 738 buffer.append(':'); 739 buffer.append(c.getValue().stringValue()); 740 } 741 else 742 { 743 buffer.append("::"); 744 Base64.encode(valueBytes, buffer); 745 } 746 } 747 748 valueStrings.add(buffer.toString()); 749 } 750 751 return Collections.unmodifiableList(valueStrings); 752 } 753 754 755 756 /** 757 * {@inheritDoc} 758 */ 759 @Override() 760 protected boolean hasDefaultValue() 761 { 762 return ((defaultValues != null) && (! defaultValues.isEmpty())); 763 } 764 765 766 767 /** 768 * {@inheritDoc} 769 */ 770 @Override() 771 public String getDataTypeName() 772 { 773 return INFO_CONTROL_TYPE_NAME.get(); 774 } 775 776 777 778 /** 779 * {@inheritDoc} 780 */ 781 @Override() 782 public String getValueConstraints() 783 { 784 return INFO_CONTROL_CONSTRAINTS.get(); 785 } 786 787 788 789 /** 790 * {@inheritDoc} 791 */ 792 @Override() 793 protected void reset() 794 { 795 super.reset(); 796 values.clear(); 797 } 798 799 800 801 /** 802 * {@inheritDoc} 803 */ 804 @Override() 805 public ControlArgument getCleanCopy() 806 { 807 return new ControlArgument(this); 808 } 809 810 811 812 /** 813 * {@inheritDoc} 814 */ 815 @Override() 816 protected void addToCommandLine(final List<String> argStrings) 817 { 818 if (values != null) 819 { 820 final StringBuilder buffer = new StringBuilder(); 821 for (final Control c : values) 822 { 823 argStrings.add(getIdentifierString()); 824 825 if (isSensitive()) 826 { 827 argStrings.add("***REDACTED***"); 828 continue; 829 } 830 831 buffer.setLength(0); 832 buffer.append(c.getOID()); 833 buffer.append(':'); 834 buffer.append(c.isCritical()); 835 836 if (c.hasValue()) 837 { 838 final byte[] valueBytes = c.getValue().getValue(); 839 if (StaticUtils.isPrintableString(valueBytes)) 840 { 841 buffer.append(':'); 842 buffer.append(c.getValue().stringValue()); 843 } 844 else 845 { 846 buffer.append("::"); 847 Base64.encode(valueBytes, buffer); 848 } 849 } 850 851 argStrings.add(buffer.toString()); 852 } 853 } 854 } 855 856 857 858 /** 859 * {@inheritDoc} 860 */ 861 @Override() 862 public void toString(final StringBuilder buffer) 863 { 864 buffer.append("ControlArgument("); 865 appendBasicToStringInfo(buffer); 866 867 if ((defaultValues != null) && (! defaultValues.isEmpty())) 868 { 869 if (defaultValues.size() == 1) 870 { 871 buffer.append(", defaultValue='"); 872 buffer.append(defaultValues.get(0).toString()); 873 } 874 else 875 { 876 buffer.append(", defaultValues={"); 877 878 final Iterator<Control> iterator = defaultValues.iterator(); 879 while (iterator.hasNext()) 880 { 881 buffer.append('\''); 882 buffer.append(iterator.next().toString()); 883 buffer.append('\''); 884 885 if (iterator.hasNext()) 886 { 887 buffer.append(", "); 888 } 889 } 890 891 buffer.append('}'); 892 } 893 } 894 895 buffer.append(')'); 896 } 897}