001 /*
002 * Copyright 2009-2014 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2009-2014 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 }