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 */
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.BindResult;
039import com.unboundid.ldap.sdk.Control;
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 bind response protocol op.
057 */
058@InternalUseOnly()
059@NotMutable()
060@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
061public 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}