001    /*
002     * Copyright 2007-2016 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-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.sdk;
022    
023    
024    
025    import java.util.ArrayList;
026    
027    import com.unboundid.asn1.ASN1OctetString;
028    import com.unboundid.asn1.ASN1StreamReader;
029    import com.unboundid.asn1.ASN1StreamReaderSequence;
030    import com.unboundid.util.Extensible;
031    import com.unboundid.util.NotMutable;
032    import com.unboundid.util.ThreadSafety;
033    import com.unboundid.util.ThreadSafetyLevel;
034    
035    import static com.unboundid.ldap.sdk.LDAPMessages.*;
036    import static com.unboundid.util.Debug.*;
037    import 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)
050    public 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        serverSASLCredentials = null;
153      }
154    
155    
156    
157      /**
158       * Creates a new bind result from the provided bind result.  This constructor
159       * may be used in creating custom subclasses.
160       *
161       * @param  bindResult  The bind result to use to create this bind result.
162       */
163      protected BindResult(final BindResult bindResult)
164      {
165        super(bindResult);
166    
167        serverSASLCredentials = bindResult.serverSASLCredentials;
168      }
169    
170    
171    
172      /**
173       * Creates a new bind result object with the provided message ID and with the
174       * protocol op and controls read from the given ASN.1 stream reader.
175       *
176       * @param  messageID        The LDAP message ID for the LDAP message that is
177       *                          associated with this bind result.
178       * @param  messageSequence  The ASN.1 stream reader sequence used in the
179       *                          course of reading the LDAP message elements.
180       * @param  reader           The ASN.1 stream reader from which to read the
181       *                          protocol op and controls.
182       *
183       * @return  The decoded bind result.
184       *
185       * @throws  LDAPException  If a problem occurs while reading or decoding data
186       *                         from the ASN.1 stream reader.
187       */
188      static BindResult readBindResultFrom(final int messageID,
189                             final ASN1StreamReaderSequence messageSequence,
190                             final ASN1StreamReader reader)
191             throws LDAPException
192      {
193        try
194        {
195          final ASN1StreamReaderSequence protocolOpSequence =
196               reader.beginSequence();
197          final ResultCode resultCode = ResultCode.valueOf(reader.readEnumerated());
198    
199          String matchedDN = reader.readString();
200          if (matchedDN.length() == 0)
201          {
202            matchedDN = null;
203          }
204    
205          String diagnosticMessage = reader.readString();
206          if (diagnosticMessage.length() == 0)
207          {
208            diagnosticMessage = null;
209          }
210    
211          String[] referralURLs = null;
212          ASN1OctetString serverSASLCredentials = null;
213          while (protocolOpSequence.hasMoreElements())
214          {
215            final byte type = (byte) reader.peek();
216            switch (type)
217            {
218              case TYPE_REFERRAL_URLS:
219                final ArrayList<String> refList = new ArrayList<String>(1);
220                final ASN1StreamReaderSequence refSequence = reader.beginSequence();
221                while (refSequence.hasMoreElements())
222                {
223                  refList.add(reader.readString());
224                }
225                referralURLs = new String[refList.size()];
226                refList.toArray(referralURLs);
227                break;
228    
229              case TYPE_SERVER_SASL_CREDENTIALS:
230                serverSASLCredentials =
231                     new ASN1OctetString(type, reader.readBytes());
232                break;
233    
234              default:
235                throw new LDAPException(ResultCode.DECODING_ERROR,
236                     ERR_BIND_RESULT_INVALID_ELEMENT.get(toHex(type)));
237            }
238          }
239    
240          Control[] controls = NO_CONTROLS;
241          if (messageSequence.hasMoreElements())
242          {
243            final ArrayList<Control> controlList = new ArrayList<Control>(1);
244            final ASN1StreamReaderSequence controlSequence = reader.beginSequence();
245            while (controlSequence.hasMoreElements())
246            {
247              controlList.add(Control.readFrom(reader));
248            }
249    
250            controls = new Control[controlList.size()];
251            controlList.toArray(controls);
252          }
253    
254          return new BindResult(messageID, resultCode, diagnosticMessage, matchedDN,
255                                referralURLs, controls, serverSASLCredentials);
256        }
257        catch (LDAPException le)
258        {
259          debugException(le);
260          throw le;
261        }
262        catch (Exception e)
263        {
264          debugException(e);
265          throw new LDAPException(ResultCode.DECODING_ERROR,
266               ERR_BIND_RESULT_CANNOT_DECODE.get(getExceptionMessage(e)), e);
267        }
268      }
269    
270    
271    
272      /**
273       * Retrieves the server SASL credentials from the bind result, if available.
274       *
275       * @return  The server SASL credentials from the bind response, or
276       *          {@code null} if none were provided.
277       */
278      public ASN1OctetString getServerSASLCredentials()
279      {
280        return serverSASLCredentials;
281      }
282    
283    
284    
285      /**
286       * {@inheritDoc}
287       */
288      @Override()
289      public void toString(final StringBuilder buffer)
290      {
291        buffer.append("BindResult(resultCode=");
292        buffer.append(getResultCode());
293    
294        final int messageID = getMessageID();
295        if (messageID >= 0)
296        {
297          buffer.append(", messageID=");
298          buffer.append(messageID);
299        }
300    
301        final String diagnosticMessage = getDiagnosticMessage();
302        if (diagnosticMessage != null)
303        {
304          buffer.append(", diagnosticMessage='");
305          buffer.append(diagnosticMessage);
306          buffer.append('\'');
307        }
308    
309        final String matchedDN = getMatchedDN();
310        if (matchedDN != null)
311        {
312          buffer.append(", matchedDN='");
313          buffer.append(matchedDN);
314          buffer.append('\'');
315        }
316    
317        final String[] referralURLs = getReferralURLs();
318        if (referralURLs.length > 0)
319        {
320          buffer.append(", referralURLs={");
321          for (int i=0; i < referralURLs.length; i++)
322          {
323            if (i > 0)
324            {
325              buffer.append(", ");
326            }
327    
328            buffer.append('\'');
329            buffer.append(referralURLs[i]);
330            buffer.append('\'');
331          }
332          buffer.append('}');
333        }
334    
335        buffer.append(", hasServerSASLCredentials=");
336        buffer.append(serverSASLCredentials != null);
337    
338        final Control[] responseControls = getResponseControls();
339        if (responseControls.length > 0)
340        {
341          buffer.append(", responseControls={");
342          for (int i=0; i < responseControls.length; i++)
343          {
344            if (i > 0)
345            {
346              buffer.append(", ");
347            }
348    
349            buffer.append(responseControls[i]);
350          }
351          buffer.append('}');
352        }
353    
354        buffer.append(')');
355      }
356    }