001/* 002 * Copyright 2009-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2009-2018 Ping Identity Corporation 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.ldap.protocol; 022 023 024 025import java.util.ArrayList; 026import java.util.Collections; 027import java.util.Iterator; 028import java.util.List; 029 030import com.unboundid.asn1.ASN1Buffer; 031import com.unboundid.asn1.ASN1BufferSequence; 032import com.unboundid.asn1.ASN1Element; 033import com.unboundid.asn1.ASN1Enumerated; 034import com.unboundid.asn1.ASN1OctetString; 035import com.unboundid.asn1.ASN1Sequence; 036import com.unboundid.asn1.ASN1StreamReader; 037import com.unboundid.asn1.ASN1StreamReaderSequence; 038import com.unboundid.ldap.sdk.Control; 039import com.unboundid.ldap.sdk.ExtendedResult; 040import com.unboundid.ldap.sdk.LDAPException; 041import com.unboundid.ldap.sdk.LDAPResult; 042import com.unboundid.ldap.sdk.ResultCode; 043import com.unboundid.util.NotMutable; 044import com.unboundid.util.InternalUseOnly; 045import com.unboundid.util.ThreadSafety; 046import com.unboundid.util.ThreadSafetyLevel; 047 048import static com.unboundid.ldap.protocol.ProtocolMessages.*; 049import static com.unboundid.util.Debug.*; 050import static com.unboundid.util.StaticUtils.*; 051import static com.unboundid.util.Validator.*; 052 053 054 055/** 056 * This class provides an implementation of a extended response protocol op. 057 */ 058@InternalUseOnly() 059@NotMutable() 060@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 061public final class ExtendedResponseProtocolOp 062 implements ProtocolOp 063{ 064 /** 065 * The BER type for the response OID element. 066 */ 067 public static final byte TYPE_RESPONSE_OID = (byte) 0x8A; 068 069 070 071 /** 072 * The BER type for the response value element. 073 */ 074 public static final byte TYPE_RESPONSE_VALUE = (byte) 0x8B; 075 076 077 078 /** 079 * The serial version UID for this serializable class. 080 */ 081 private static final long serialVersionUID = -7757619031268544913L; 082 083 084 085 // The value for this extended response. 086 private final ASN1OctetString responseValue; 087 088 // The result code for this extended response. 089 private final int resultCode; 090 091 // The referral URLs for this extended response. 092 private final List<String> referralURLs; 093 094 // The diagnostic message for this extended response. 095 private final String diagnosticMessage; 096 097 // The matched DN for this extended response. 098 private final String matchedDN; 099 100 // The OID for this extended response. 101 private final String responseOID; 102 103 104 105 /** 106 * Creates a new instance of this extended response protocol op with the 107 * provided information. 108 * 109 * @param resultCode The result code for this response. 110 * @param matchedDN The matched DN for this response, if available. 111 * @param diagnosticMessage The diagnostic message for this response, if 112 * any. 113 * @param referralURLs The list of referral URLs for this response, if 114 * any. 115 * @param responseOID The response OID for this response, if any. 116 * @param responseValue The value for this response, if any. 117 */ 118 public ExtendedResponseProtocolOp(final int resultCode, 119 final String matchedDN, 120 final String diagnosticMessage, 121 final List<String> referralURLs, 122 final String responseOID, 123 final ASN1OctetString responseValue) 124 { 125 this.resultCode = resultCode; 126 this.matchedDN = matchedDN; 127 this.diagnosticMessage = diagnosticMessage; 128 this.responseOID = responseOID; 129 130 if (referralURLs == null) 131 { 132 this.referralURLs = Collections.emptyList(); 133 } 134 else 135 { 136 this.referralURLs = Collections.unmodifiableList(referralURLs); 137 } 138 139 if (responseValue == null) 140 { 141 this.responseValue = null; 142 } 143 else 144 { 145 this.responseValue = 146 new ASN1OctetString(TYPE_RESPONSE_VALUE, responseValue.getValue()); 147 } 148 } 149 150 151 152 /** 153 * Creates a new extended response protocol op from the provided extended 154 * result object. 155 * 156 * @param result The extended result object to use to create this protocol 157 * op. 158 */ 159 public ExtendedResponseProtocolOp(final LDAPResult result) 160 { 161 resultCode = result.getResultCode().intValue(); 162 matchedDN = result.getMatchedDN(); 163 diagnosticMessage = result.getDiagnosticMessage(); 164 referralURLs = toList(result.getReferralURLs()); 165 166 if (result instanceof ExtendedResult) 167 { 168 final ExtendedResult r = (ExtendedResult) result; 169 responseOID = r.getOID(); 170 responseValue = r.getValue(); 171 } 172 else 173 { 174 responseOID = null; 175 responseValue = null; 176 } 177 } 178 179 180 181 /** 182 * Creates a new extended response protocol op read from the provided ASN.1 183 * stream reader. 184 * 185 * @param reader The ASN.1 stream reader from which to read the extended 186 * response. 187 * 188 * @throws LDAPException If a problem occurs while reading or parsing the 189 * extended response. 190 */ 191 ExtendedResponseProtocolOp(final ASN1StreamReader reader) 192 throws LDAPException 193 { 194 try 195 { 196 final ASN1StreamReaderSequence opSequence = reader.beginSequence(); 197 resultCode = reader.readEnumerated(); 198 199 String s = reader.readString(); 200 ensureNotNull(s); 201 if (s.length() == 0) 202 { 203 matchedDN = null; 204 } 205 else 206 { 207 matchedDN = s; 208 } 209 210 s = reader.readString(); 211 ensureNotNull(s); 212 if (s.length() == 0) 213 { 214 diagnosticMessage = null; 215 } 216 else 217 { 218 diagnosticMessage = s; 219 } 220 221 ASN1OctetString value = null; 222 String oid = null; 223 final ArrayList<String> refs = new ArrayList<String>(1); 224 while (opSequence.hasMoreElements()) 225 { 226 final byte type = (byte) reader.peek(); 227 if (type == GenericResponseProtocolOp.TYPE_REFERRALS) 228 { 229 final ASN1StreamReaderSequence refSequence = reader.beginSequence(); 230 while (refSequence.hasMoreElements()) 231 { 232 refs.add(reader.readString()); 233 } 234 } 235 else if (type == TYPE_RESPONSE_OID) 236 { 237 oid = reader.readString(); 238 } 239 else if (type == TYPE_RESPONSE_VALUE) 240 { 241 value = new ASN1OctetString(type, reader.readBytes()); 242 } 243 else 244 { 245 throw new LDAPException(ResultCode.DECODING_ERROR, 246 ERR_EXTENDED_RESPONSE_INVALID_ELEMENT.get(toHex(type))); 247 } 248 } 249 250 referralURLs = Collections.unmodifiableList(refs); 251 responseOID = oid; 252 responseValue = value; 253 } 254 catch (final LDAPException le) 255 { 256 debugException(le); 257 throw le; 258 } 259 catch (final Exception e) 260 { 261 debugException(e); 262 throw new LDAPException(ResultCode.DECODING_ERROR, 263 ERR_EXTENDED_RESPONSE_CANNOT_DECODE.get(getExceptionMessage(e)), e); 264 } 265 } 266 267 268 269 /** 270 * Retrieves the result code for this extended response. 271 * 272 * @return The result code for this extended response. 273 */ 274 public int getResultCode() 275 { 276 return resultCode; 277 } 278 279 280 281 /** 282 * Retrieves the matched DN for this extended response, if any. 283 * 284 * @return The matched DN for this extended response, or {@code null} if 285 * there is no matched DN. 286 */ 287 public String getMatchedDN() 288 { 289 return matchedDN; 290 } 291 292 293 294 /** 295 * Retrieves the diagnostic message for this extended response, if any. 296 * 297 * @return The diagnostic message for this extended response, or {@code null} 298 * if there is no diagnostic message. 299 */ 300 public String getDiagnosticMessage() 301 { 302 return diagnosticMessage; 303 } 304 305 306 307 /** 308 * Retrieves the list of referral URLs for this extended response. 309 * 310 * @return The list of referral URLs for this extended response, or an empty 311 * list if there are no referral URLs. 312 */ 313 public List<String> getReferralURLs() 314 { 315 return referralURLs; 316 } 317 318 319 320 /** 321 * Retrieves the OID for this extended response, if any. 322 * 323 * @return The OID for this extended response, or {@code null} if there is no 324 * response OID. 325 */ 326 public String getResponseOID() 327 { 328 return responseOID; 329 } 330 331 332 333 /** 334 * Retrieves the value for this extended response, if any. 335 * 336 * @return The value for this extended response, or {@code null} if there is 337 * no response value. 338 */ 339 public ASN1OctetString getResponseValue() 340 { 341 return responseValue; 342 } 343 344 345 346 /** 347 * {@inheritDoc} 348 */ 349 @Override() 350 public byte getProtocolOpType() 351 { 352 return LDAPMessage.PROTOCOL_OP_TYPE_EXTENDED_RESPONSE; 353 } 354 355 356 357 /** 358 * {@inheritDoc} 359 */ 360 @Override() 361 public ASN1Element encodeProtocolOp() 362 { 363 final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(6); 364 elements.add(new ASN1Enumerated(getResultCode())); 365 366 final String mdn = getMatchedDN(); 367 if (mdn == null) 368 { 369 elements.add(new ASN1OctetString()); 370 } 371 else 372 { 373 elements.add(new ASN1OctetString(mdn)); 374 } 375 376 final String dm = getDiagnosticMessage(); 377 if (dm == null) 378 { 379 elements.add(new ASN1OctetString()); 380 } 381 else 382 { 383 elements.add(new ASN1OctetString(dm)); 384 } 385 386 final List<String> refs = getReferralURLs(); 387 if (! refs.isEmpty()) 388 { 389 final ArrayList<ASN1Element> refElements = 390 new ArrayList<ASN1Element>(refs.size()); 391 for (final String r : refs) 392 { 393 refElements.add(new ASN1OctetString(r)); 394 } 395 elements.add(new ASN1Sequence(GenericResponseProtocolOp.TYPE_REFERRALS, 396 refElements)); 397 } 398 399 if (responseOID != null) 400 { 401 elements.add(new ASN1OctetString(TYPE_RESPONSE_OID, responseOID)); 402 } 403 404 if (responseValue != null) 405 { 406 elements.add(responseValue); 407 } 408 409 return new ASN1Sequence(LDAPMessage.PROTOCOL_OP_TYPE_EXTENDED_RESPONSE, 410 elements); 411 } 412 413 414 415 /** 416 * Decodes the provided ASN.1 element as an extended response protocol op. 417 * 418 * @param element The ASN.1 element to be decoded. 419 * 420 * @return The decoded extended response protocol op. 421 * 422 * @throws LDAPException If the provided ASN.1 element cannot be decoded as 423 * an extended response protocol op. 424 */ 425 public static ExtendedResponseProtocolOp decodeProtocolOp( 426 final ASN1Element element) 427 throws LDAPException 428 { 429 try 430 { 431 final ASN1Element[] elements = 432 ASN1Sequence.decodeAsSequence(element).elements(); 433 final int resultCode = 434 ASN1Enumerated.decodeAsEnumerated(elements[0]).intValue(); 435 436 final String matchedDN; 437 final String md = 438 ASN1OctetString.decodeAsOctetString(elements[1]).stringValue(); 439 if (md.length() > 0) 440 { 441 matchedDN = md; 442 } 443 else 444 { 445 matchedDN = null; 446 } 447 448 final String diagnosticMessage; 449 final String dm = 450 ASN1OctetString.decodeAsOctetString(elements[2]).stringValue(); 451 if (dm.length() > 0) 452 { 453 diagnosticMessage = dm; 454 } 455 else 456 { 457 diagnosticMessage = null; 458 } 459 460 ASN1OctetString responseValue = null; 461 List<String> referralURLs = null; 462 String responseOID = null; 463 if (elements.length > 3) 464 { 465 for (int i=3; i < elements.length; i++) 466 { 467 switch (elements[i].getType()) 468 { 469 case GenericResponseProtocolOp.TYPE_REFERRALS: 470 final ASN1Element[] refElements = 471 ASN1Sequence.decodeAsSequence(elements[3]).elements(); 472 referralURLs = new ArrayList<String>(refElements.length); 473 for (final ASN1Element e : refElements) 474 { 475 referralURLs.add( 476 ASN1OctetString.decodeAsOctetString(e).stringValue()); 477 } 478 break; 479 480 case TYPE_RESPONSE_OID: 481 responseOID = ASN1OctetString.decodeAsOctetString(elements[i]). 482 stringValue(); 483 break; 484 485 case TYPE_RESPONSE_VALUE: 486 responseValue = ASN1OctetString.decodeAsOctetString(elements[i]); 487 break; 488 489 default: 490 throw new LDAPException(ResultCode.DECODING_ERROR, 491 ERR_EXTENDED_RESPONSE_INVALID_ELEMENT.get( 492 toHex(elements[i].getType()))); 493 } 494 } 495 } 496 497 return new ExtendedResponseProtocolOp(resultCode, matchedDN, 498 diagnosticMessage, referralURLs, responseOID, responseValue); 499 } 500 catch (final LDAPException le) 501 { 502 debugException(le); 503 throw le; 504 } 505 catch (final Exception e) 506 { 507 debugException(e); 508 throw new LDAPException(ResultCode.DECODING_ERROR, 509 ERR_EXTENDED_RESPONSE_CANNOT_DECODE.get(getExceptionMessage(e)), 510 e); 511 } 512 } 513 514 515 516 /** 517 * {@inheritDoc} 518 */ 519 @Override() 520 public void writeTo(final ASN1Buffer buffer) 521 { 522 final ASN1BufferSequence opSequence = 523 buffer.beginSequence(LDAPMessage.PROTOCOL_OP_TYPE_EXTENDED_RESPONSE); 524 buffer.addEnumerated(resultCode); 525 buffer.addOctetString(matchedDN); 526 buffer.addOctetString(diagnosticMessage); 527 528 if (! referralURLs.isEmpty()) 529 { 530 final ASN1BufferSequence refSequence = 531 buffer.beginSequence(GenericResponseProtocolOp.TYPE_REFERRALS); 532 for (final String s : referralURLs) 533 { 534 buffer.addOctetString(s); 535 } 536 refSequence.end(); 537 } 538 539 if (responseOID != null) 540 { 541 buffer.addOctetString(TYPE_RESPONSE_OID, responseOID); 542 } 543 544 if (responseValue != null) 545 { 546 buffer.addOctetString(TYPE_RESPONSE_VALUE, responseValue.getValue()); 547 } 548 549 opSequence.end(); 550 } 551 552 553 554 /** 555 * Creates a extended result from this protocol op. 556 * 557 * @param controls The set of controls to include in the extended result. 558 * It may be empty or {@code null} if no controls should be 559 * included. 560 * 561 * @return The extended result that was created. 562 */ 563 public ExtendedResult toExtendedResult(final Control... controls) 564 { 565 final String[] referralArray = new String[referralURLs.size()]; 566 referralURLs.toArray(referralArray); 567 568 return new ExtendedResult(-1, ResultCode.valueOf(resultCode), 569 diagnosticMessage, matchedDN, referralArray, responseOID, 570 responseValue, controls); 571 } 572 573 574 575 /** 576 * Retrieves a string representation of this protocol op. 577 * 578 * @return A string representation of this protocol op. 579 */ 580 @Override() 581 public String toString() 582 { 583 final StringBuilder buffer = new StringBuilder(); 584 toString(buffer); 585 return buffer.toString(); 586 } 587 588 589 590 /** 591 * {@inheritDoc} 592 */ 593 @Override() 594 public void toString(final StringBuilder buffer) 595 { 596 buffer.append("ExtendedResponseProtocolOp(resultCode="); 597 buffer.append(resultCode); 598 599 if (matchedDN != null) 600 { 601 buffer.append(", matchedDN='"); 602 buffer.append(matchedDN); 603 buffer.append('\''); 604 } 605 606 if (diagnosticMessage != null) 607 { 608 buffer.append(", diagnosticMessage='"); 609 buffer.append(diagnosticMessage); 610 buffer.append('\''); 611 } 612 613 if (! referralURLs.isEmpty()) 614 { 615 buffer.append(", referralURLs={"); 616 617 final Iterator<String> iterator = referralURLs.iterator(); 618 while (iterator.hasNext()) 619 { 620 buffer.append('\''); 621 buffer.append(iterator.next()); 622 buffer.append('\''); 623 if (iterator.hasNext()) 624 { 625 buffer.append(','); 626 } 627 } 628 629 buffer.append('}'); 630 } 631 632 if (responseOID != null) 633 { 634 buffer.append(", responseOID='"); 635 buffer.append(responseOID); 636 buffer.append('\''); 637 } 638 639 buffer.append(')'); 640 } 641}