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