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