001/* 002 * Copyright 2021-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2021-2024 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) 2021-2024 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.extensions; 037 038 039 040import java.util.ArrayList; 041import java.util.List; 042 043import com.unboundid.asn1.ASN1Element; 044import com.unboundid.asn1.ASN1OctetString; 045import com.unboundid.asn1.ASN1Sequence; 046import com.unboundid.ldap.sdk.Control; 047import com.unboundid.ldap.sdk.ExtendedResult; 048import com.unboundid.ldap.sdk.LDAPException; 049import com.unboundid.ldap.sdk.ResultCode; 050import com.unboundid.util.Debug; 051import com.unboundid.util.NotExtensible; 052import com.unboundid.util.NotNull; 053import com.unboundid.util.Nullable; 054import com.unboundid.util.StaticUtils; 055import com.unboundid.util.ThreadSafety; 056import com.unboundid.util.ThreadSafetyLevel; 057 058import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*; 059 060 061 062/** 063 * This class defines the superclass for extended results that may be returned 064 * in response to the replace certificate extended requests, including 065 * {@link ReplaceListenerCertificateExtendedRequest}, 066 * {@link ReplaceInterServerCertificateExtendedRequest}, 067 * {@link PurgeRetiredListenerCertificatesExtendedRequest}, and 068 * {@link PurgeRetiredInterServerCertificatesExtendedRequest}. 069 * <BR> 070 * <BLOCKQUOTE> 071 * <B>NOTE:</B> This class, and other classes within the 072 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 073 * supported for use against Ping Identity, UnboundID, and 074 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 075 * for proprietary functionality or for external specifications that are not 076 * considered stable or mature enough to be guaranteed to work in an 077 * interoperable way with other types of LDAP servers. 078 * </BLOCKQUOTE> 079 * <BR> 080 * The extended result may have an OID that matches that of the associated 081 * extended result, and it may have a value with the following encoding: 082 * <PRE> 083 * ReplaceCertificateResponseValue ::= SEQUENCE { 084 * toolOutput [16] OCTET STRING OPTIONAL, 085 * ... } 086 * </PRE> 087 * <BR><BR> 088 */ 089@NotExtensible() 090@ThreadSafety(level=ThreadSafetyLevel.INTERFACE_THREADSAFE) 091public abstract class ReplaceCertificateExtendedResult 092 extends ExtendedResult 093{ 094 /** 095 * The BER type for the response value element that holds the output of the 096 * {@code replace-certificate} tool. 097 */ 098 private static final byte TYPE_TOOL_OUTPUT = (byte) 0x91; 099 100 101 102 /** 103 * The serial version UID for this serializable class. 104 */ 105 private static final long serialVersionUID = -4865907062468450991L; 106 107 108 109 // The output obtained when running the replace-certificate tool. 110 @Nullable private final String toolOutput; 111 112 113 114 /** 115 * Creates a new replace certificate extended result that is decoded from the 116 * provided extended result. 117 * 118 * @param extendedResult The generic extended result to decode as a replace 119 * certificate extended result. It must not be 120 * {@code null}. 121 * 122 * @throws LDAPException If the provided extended result cannot be decoded 123 * as a replace certificate extended result. 124 */ 125 protected ReplaceCertificateExtendedResult( 126 @NotNull final ExtendedResult extendedResult) 127 throws LDAPException 128 { 129 super(extendedResult); 130 131 String output = null; 132 final ASN1OctetString value = extendedResult.getValue(); 133 if (value != null) 134 { 135 try 136 { 137 for (final ASN1Element element : 138 ASN1Sequence.decodeAsSequence(value.getValue()).elements()) 139 { 140 switch (element.getType()) 141 { 142 case TYPE_TOOL_OUTPUT: 143 output = element.decodeAsOctetString().stringValue(); 144 break; 145 } 146 } 147 } 148 catch (final Exception e) 149 { 150 Debug.debugException(e); 151 throw new LDAPException(ResultCode.DECODING_ERROR, 152 ERR_REPLACE_CERT_RESULT_CANNOT_DECODE_VALUE.get( 153 StaticUtils.getExceptionMessage(e)), 154 e); 155 } 156 } 157 158 toolOutput = output; 159 } 160 161 162 163 /** 164 * Creates a new replace certificate extended result with the provided 165 * information. 166 * 167 * @param messageID The message ID for the LDAP message that is 168 * associated with this LDAP result. 169 * @param resultCode The result code from the response. 170 * @param diagnosticMessage The diagnostic message from the response, if 171 * available. 172 * @param matchedDN The matched DN from the response, if available. 173 * @param referralURLs The set of referral URLs from the response, if 174 * available. 175 * @param oid The OID to use for the extended result. It may 176 * be {@code null} if no OID should be used. 177 * @param toolOutput The output (a combined representation of both 178 * standard output and standard error) obtained 179 * from running the {@code replace-certificate} 180 * tool. It may be {@code null} if request 181 * processing failed before running the tool. 182 * @param responseControls The set of controls to include in the extended 183 * result. It may be {@code null} or empty if no 184 * response controls should be included. 185 */ 186 protected ReplaceCertificateExtendedResult(final int messageID, 187 @NotNull final ResultCode resultCode, 188 @Nullable final String diagnosticMessage, 189 @Nullable final String matchedDN, 190 @Nullable final String[] referralURLs, 191 @Nullable final String oid, 192 @Nullable final String toolOutput, 193 @Nullable final Control... responseControls) 194 { 195 super(messageID, resultCode, diagnosticMessage, matchedDN, referralURLs, 196 oid, encodeValue(oid, toolOutput), responseControls); 197 198 this.toolOutput = toolOutput; 199 } 200 201 202 203 /** 204 * Encodes a value for this extended result, if appropriate. 205 * 206 * @param oid The OID to use for the extended result. It may be 207 * {@code null} if no OID should be used. 208 * @param toolOutput The output obtained from running the 209 * {@code replace-certificate} tool. It may be 210 * {@code null} if request processing failed before 211 * running the tool. 212 * 213 * @return The encoded value for this extended result, or {@code null} if 214 * no value should be included. 215 */ 216 @Nullable() 217 public static ASN1OctetString encodeValue(@Nullable final String oid, 218 @Nullable final String toolOutput) 219 { 220 if ((oid == null) && (toolOutput == null)) 221 { 222 return null; 223 } 224 225 final List<ASN1Element> valueElements = new ArrayList<>(1); 226 if (toolOutput != null) 227 { 228 valueElements.add(new ASN1OctetString(TYPE_TOOL_OUTPUT, toolOutput)); 229 } 230 231 final ASN1Sequence valueSequence = new ASN1Sequence(valueElements); 232 return new ASN1OctetString(valueSequence.encode()); 233 } 234 235 236 237 /** 238 * Retrieves the output (a combined representation of both standard output and 239 * standard error) obtained from running the {@code replace-certificate} tool, 240 * if available. 241 * 242 * @return The output obtained from running the {@code replace-certificate} 243 * tool, or {@code null} if no output is available (e.g., because 244 * an error occurred before the tool could be invoked). 245 */ 246 @Nullable() 247 public String getToolOutput() 248 { 249 return toolOutput; 250 } 251 252 253 254 /** 255 * Appends a string representation of this replace certificate result to the 256 * provided buffer. 257 * 258 * @param buffer The buffer to which the information should be appended. It 259 * must not be {@code null}. 260 */ 261 @Override() 262 public final void toString(@NotNull final StringBuilder buffer) 263 { 264 buffer.append("ReplaceCertificateExtendedResult(resultCode="); 265 buffer.append(getResultCode()); 266 267 final int messageID = getMessageID(); 268 if (messageID >= 0) 269 { 270 buffer.append(", messageID="); 271 buffer.append(messageID); 272 } 273 274 final String diagnosticMessage = getDiagnosticMessage(); 275 if (diagnosticMessage != null) 276 { 277 buffer.append(", diagnosticMessage='"); 278 buffer.append(diagnosticMessage); 279 buffer.append('\''); 280 } 281 282 final String matchedDN = getMatchedDN(); 283 if (matchedDN != null) 284 { 285 buffer.append(", matchedDN='"); 286 buffer.append(matchedDN); 287 buffer.append('\''); 288 } 289 290 final String[] referralURLs = getReferralURLs(); 291 if (referralURLs.length > 0) 292 { 293 buffer.append(", referralURLs={"); 294 for (int i=0; i < referralURLs.length; i++) 295 { 296 if (i > 0) 297 { 298 buffer.append(", "); 299 } 300 301 buffer.append('\''); 302 buffer.append(referralURLs[i]); 303 buffer.append('\''); 304 } 305 buffer.append('}'); 306 } 307 308 final String oid = getOID(); 309 if (oid != null) 310 { 311 buffer.append(", oid='"); 312 buffer.append(oid); 313 buffer.append('\''); 314 } 315 316 if (toolOutput != null) 317 { 318 buffer.append(", toolOutput='"); 319 escapeOutput(toolOutput, buffer); 320 buffer.append('\''); 321 } 322 323 final Control[] responseControls = getResponseControls(); 324 if (responseControls.length > 0) 325 { 326 buffer.append(", responseControls={"); 327 for (int i=0; i < responseControls.length; i++) 328 { 329 if (i > 0) 330 { 331 buffer.append(", "); 332 } 333 334 buffer.append(responseControls[i]); 335 } 336 buffer.append('}'); 337 } 338 339 buffer.append(')'); 340 } 341 342 343 344 /** 345 * Appends an escaped representation of the provided output to the given 346 * buffer. 347 * 348 * @param toolOutput The output to be escaped. It must not be {@code null}. 349 * @param buffer The buffer to which the escaped representation should 350 * be appended. It must not be {@code null}. 351 */ 352 private static void escapeOutput(@NotNull final String toolOutput, 353 @NotNull final StringBuilder buffer) 354 { 355 for (final char c : toolOutput.toCharArray()) 356 { 357 switch (c) 358 { 359 case '\\': 360 buffer.append("\\\\"); 361 break; 362 case '\n': 363 buffer.append("\\n"); 364 break; 365 case '\r': 366 buffer.append("\\r"); 367 break; 368 case '\'': 369 buffer.append("\\'"); 370 break; 371 case '"': 372 buffer.append("\\\""); 373 break; 374 default: 375 buffer.append(c); 376 break; 377 } 378 } 379 } 380}