001/*
002 * Copyright 2007-2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-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.sdk;
022
023
024
025import java.util.ArrayList;
026
027import com.unboundid.asn1.ASN1OctetString;
028import com.unboundid.asn1.ASN1StreamReader;
029import com.unboundid.asn1.ASN1StreamReaderSequence;
030import com.unboundid.util.Extensible;
031import com.unboundid.util.NotMutable;
032import com.unboundid.util.ThreadSafety;
033import com.unboundid.util.ThreadSafetyLevel;
034
035import static com.unboundid.ldap.sdk.LDAPMessages.*;
036import static com.unboundid.util.Debug.*;
037import static com.unboundid.util.StaticUtils.*;
038
039
040
041/**
042 * This class provides a data structure for holding information about the result
043 * of processing a bind operation.  It provides generic bind response elements
044 * as described in the {@link LDAPResult} class, but may be overridden to
045 * provide more detailed information for specific types of bind requests.
046 */
047@Extensible()
048@NotMutable()
049@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
050public class BindResult
051       extends LDAPResult
052{
053  /**
054   * The BER type for the server SASL credentials element in the bind result.
055   */
056  private static final byte TYPE_SERVER_SASL_CREDENTIALS = (byte) 0x87;
057
058
059
060  /**
061   * The serial version UID for this serializable class.
062   */
063  private static final long serialVersionUID = 2211625049303605730L;
064
065
066
067  // The server SASL credentials from the response, if available.
068  private final ASN1OctetString serverSASLCredentials;
069
070
071
072  /**
073   * Creates a new bind result with the provided information.
074   *
075   * @param  messageID          The message ID for the LDAP message that is
076   *                            associated with this bind result.
077   * @param  resultCode         The result code from the response.
078   * @param  diagnosticMessage  The diagnostic message from the response, if
079   *                            available.
080   * @param  matchedDN          The matched DN from the response, if available.
081   * @param  referralURLs       The set of referral URLs from the response, if
082   *                            available.
083   * @param  responseControls   The set of controls from the response, if
084   *                            available.
085   */
086  public BindResult(final int messageID, final ResultCode resultCode,
087                    final String diagnosticMessage, final String matchedDN,
088                    final String[] referralURLs,
089                    final Control[] responseControls)
090  {
091    this(messageID, resultCode, diagnosticMessage, matchedDN, referralURLs,
092         responseControls, null);
093  }
094
095
096
097  /**
098   * Creates a new bind result with the provided information.
099   *
100   * @param  messageID              The message ID for the LDAP message that is
101   *                                associated with this bind result.
102   * @param  resultCode             The result code from the response.
103   * @param  diagnosticMessage      The diagnostic message from the response, if
104   *                                available.
105   * @param  matchedDN              The matched DN from the response, if
106   *                                available.
107   * @param  referralURLs           The set of referral URLs from the response,
108   *                                if available.
109   * @param  responseControls       The set of controls from the response, if
110   *                                available.
111   * @param  serverSASLCredentials  The server SASL credentials from the
112   *                                response, if available.
113   */
114  public BindResult(final int messageID, final ResultCode resultCode,
115                    final String diagnosticMessage, final String matchedDN,
116                    final String[] referralURLs,
117                    final Control[] responseControls,
118                    final ASN1OctetString serverSASLCredentials)
119  {
120    super(messageID, resultCode, diagnosticMessage, matchedDN, referralURLs,
121          responseControls);
122
123    this.serverSASLCredentials = serverSASLCredentials;
124  }
125
126
127
128  /**
129   * Creates a new bind result from the provided generic LDAP result.
130   *
131   * @param  ldapResult  The LDAP result to use to create this bind result.
132   */
133  public BindResult(final LDAPResult ldapResult)
134  {
135    super(ldapResult);
136
137    serverSASLCredentials = null;
138  }
139
140
141
142  /**
143   * Creates a new bind result from the provided {@code LDAPException}.
144   *
145   * @param  exception  The {@code LDAPException} to use to create this bind
146   *                    result.
147   */
148  public BindResult(final LDAPException exception)
149  {
150    super(exception.toLDAPResult());
151
152    if (exception instanceof LDAPBindException)
153    {
154      serverSASLCredentials =
155           ((LDAPBindException) exception).getServerSASLCredentials();
156    }
157    else
158    {
159      serverSASLCredentials = null;
160    }
161  }
162
163
164
165  /**
166   * Creates a new bind result from the provided bind result.  This constructor
167   * may be used in creating custom subclasses.
168   *
169   * @param  bindResult  The bind result to use to create this bind result.
170   */
171  protected BindResult(final BindResult bindResult)
172  {
173    super(bindResult);
174
175    serverSASLCredentials = bindResult.serverSASLCredentials;
176  }
177
178
179
180  /**
181   * Creates a new bind result object with the provided message ID and with the
182   * protocol op and controls read from the given ASN.1 stream reader.
183   *
184   * @param  messageID        The LDAP message ID for the LDAP message that is
185   *                          associated with this bind result.
186   * @param  messageSequence  The ASN.1 stream reader sequence used in the
187   *                          course of reading the LDAP message elements.
188   * @param  reader           The ASN.1 stream reader from which to read the
189   *                          protocol op and controls.
190   *
191   * @return  The decoded bind result.
192   *
193   * @throws  LDAPException  If a problem occurs while reading or decoding data
194   *                         from the ASN.1 stream reader.
195   */
196  static BindResult readBindResultFrom(final int messageID,
197                         final ASN1StreamReaderSequence messageSequence,
198                         final ASN1StreamReader reader)
199         throws LDAPException
200  {
201    try
202    {
203      final ASN1StreamReaderSequence protocolOpSequence =
204           reader.beginSequence();
205      final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
206
207      String matchedDN = reader.readString();
208      if (matchedDN.length() == 0)
209      {
210        matchedDN = null;
211      }
212
213      String diagnosticMessage = reader.readString();
214      if (diagnosticMessage.length() == 0)
215      {
216        diagnosticMessage = null;
217      }
218
219      String[] referralURLs = null;
220      ASN1OctetString serverSASLCredentials = null;
221      while (protocolOpSequence.hasMoreElements())
222      {
223        final byte type = (byte) reader.peek();
224        switch (type)
225        {
226          case TYPE_REFERRAL_URLS:
227            final ArrayList<String> refList = new ArrayList<String>(1);
228            final ASN1StreamReaderSequence refSequence = reader.beginSequence();
229            while (refSequence.hasMoreElements())
230            {
231              refList.add(reader.readString());
232            }
233            referralURLs = new String[refList.size()];
234            refList.toArray(referralURLs);
235            break;
236
237          case TYPE_SERVER_SASL_CREDENTIALS:
238            serverSASLCredentials =
239                 new ASN1OctetString(type, reader.readBytes());
240            break;
241
242          default:
243            throw new LDAPException(ResultCode.DECODING_ERROR,
244                 ERR_BIND_RESULT_INVALID_ELEMENT.get(toHex(type)));
245        }
246      }
247
248      Control[] controls = NO_CONTROLS;
249      if (messageSequence.hasMoreElements())
250      {
251        final ArrayList<Control> controlList = new ArrayList<Control>(1);
252        final ASN1StreamReaderSequence controlSequence = reader.beginSequence();
253        while (controlSequence.hasMoreElements())
254        {
255          controlList.add(Control.readFrom(reader));
256        }
257
258        controls = new Control[controlList.size()];
259        controlList.toArray(controls);
260      }
261
262      return new BindResult(messageID, resultCode, diagnosticMessage, matchedDN,
263                            referralURLs, controls, serverSASLCredentials);
264    }
265    catch (final LDAPException le)
266    {
267      debugException(le);
268      throw le;
269    }
270    catch (final Exception e)
271    {
272      debugException(e);
273      throw new LDAPException(ResultCode.DECODING_ERROR,
274           ERR_BIND_RESULT_CANNOT_DECODE.get(getExceptionMessage(e)), e);
275    }
276  }
277
278
279
280  /**
281   * Retrieves the server SASL credentials from the bind result, if available.
282   *
283   * @return  The server SASL credentials from the bind response, or
284   *          {@code null} if none were provided.
285   */
286  public ASN1OctetString getServerSASLCredentials()
287  {
288    return serverSASLCredentials;
289  }
290
291
292
293  /**
294   * {@inheritDoc}
295   */
296  @Override()
297  public void toString(final StringBuilder buffer)
298  {
299    buffer.append("BindResult(resultCode=");
300    buffer.append(getResultCode());
301
302    final int messageID = getMessageID();
303    if (messageID >= 0)
304    {
305      buffer.append(", messageID=");
306      buffer.append(messageID);
307    }
308
309    final String diagnosticMessage = getDiagnosticMessage();
310    if (diagnosticMessage != null)
311    {
312      buffer.append(", diagnosticMessage='");
313      buffer.append(diagnosticMessage);
314      buffer.append('\'');
315    }
316
317    final String matchedDN = getMatchedDN();
318    if (matchedDN != null)
319    {
320      buffer.append(", matchedDN='");
321      buffer.append(matchedDN);
322      buffer.append('\'');
323    }
324
325    final String[] referralURLs = getReferralURLs();
326    if (referralURLs.length > 0)
327    {
328      buffer.append(", referralURLs={");
329      for (int i=0; i < referralURLs.length; i++)
330      {
331        if (i > 0)
332        {
333          buffer.append(", ");
334        }
335
336        buffer.append('\'');
337        buffer.append(referralURLs[i]);
338        buffer.append('\'');
339      }
340      buffer.append('}');
341    }
342
343    buffer.append(", hasServerSASLCredentials=");
344    buffer.append(serverSASLCredentials != null);
345
346    final Control[] responseControls = getResponseControls();
347    if (responseControls.length > 0)
348    {
349      buffer.append(", responseControls={");
350      for (int i=0; i < responseControls.length; i++)
351      {
352        if (i > 0)
353        {
354          buffer.append(", ");
355        }
356
357        buffer.append(responseControls[i]);
358      }
359      buffer.append('}');
360    }
361
362    buffer.append(')');
363  }
364}