001    /*
002     * Copyright 2009-2012 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2009-2012 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.ASN1OctetString;
033    import com.unboundid.asn1.ASN1StreamReader;
034    import com.unboundid.asn1.ASN1StreamReaderSequence;
035    import com.unboundid.ldap.sdk.LDAPException;
036    import com.unboundid.ldap.sdk.ResultCode;
037    import com.unboundid.util.NotMutable;
038    import com.unboundid.util.InternalUseOnly;
039    import com.unboundid.util.ThreadSafety;
040    import com.unboundid.util.ThreadSafetyLevel;
041    
042    import static com.unboundid.ldap.protocol.ProtocolMessages.*;
043    import static com.unboundid.util.Debug.*;
044    import static com.unboundid.util.StaticUtils.*;
045    import static com.unboundid.util.Validator.*;
046    
047    
048    
049    /**
050     * This class provides an implementation of a bind response protocol op.
051     */
052    @InternalUseOnly()
053    @NotMutable()
054    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
055    public final class BindResponseProtocolOp
056           implements ProtocolOp
057    {
058      /**
059       * The BER type for the server SASL credentials element.
060       */
061      public static final byte TYPE_SERVER_SASL_CREDENTIALS = (byte) 0x87;
062    
063    
064    
065      /**
066       * The serial version UID for this serializable class.
067       */
068      private static final long serialVersionUID = -7757619031268544913L;
069    
070    
071    
072      // The server SASL credentials for this bind response.
073      private final ASN1OctetString serverSASLCredentials;
074    
075      // The result code for this bind response.
076      private final int resultCode;
077    
078      // The referral URLs for this bind response.
079      private final List<String> referralURLs;
080    
081      // The diagnostic message for this bind response.
082      private final String diagnosticMessage;
083    
084      // The matched DN for this bind response.
085      private final String matchedDN;
086    
087    
088    
089      /**
090       * Creates a new instance of this bind response protocol op with the provided
091       * information.
092       *
093       * @param  resultCode             The result code for this response.
094       * @param  matchedDN              The matched DN for this response, if
095       *                                available.
096       * @param  diagnosticMessage      The diagnostic message for this response, if
097       *                                any.
098       * @param  referralURLs           The list of referral URLs for this response,
099       *                                if any.
100       * @param  serverSASLCredentials  The server SASL credentials for this
101       *                                response, if available.
102       */
103      public BindResponseProtocolOp(final int resultCode, final String matchedDN,
104                                    final String diagnosticMessage,
105                                    final List<String> referralURLs,
106                                    final ASN1OctetString serverSASLCredentials)
107      {
108        this.resultCode            = resultCode;
109        this.matchedDN             = matchedDN;
110        this.diagnosticMessage     = diagnosticMessage;
111    
112        if (referralURLs == null)
113        {
114          this.referralURLs = Collections.emptyList();
115        }
116        else
117        {
118          this.referralURLs = Collections.unmodifiableList(referralURLs);
119        }
120    
121        if (serverSASLCredentials == null)
122        {
123          this.serverSASLCredentials = null;
124        }
125        else
126        {
127          this.serverSASLCredentials = new ASN1OctetString(
128               TYPE_SERVER_SASL_CREDENTIALS, serverSASLCredentials.getValue());
129        }
130      }
131    
132    
133    
134      /**
135       * Creates a new bind response protocol op read from the provided ASN.1 stream
136       * reader.
137       *
138       * @param  reader  The ASN.1 stream reader from which to read the bind
139       *                 response.
140       *
141       * @throws  LDAPException  If a problem occurs while reading or parsing the
142       *                         bind response.
143       */
144      BindResponseProtocolOp(final ASN1StreamReader reader)
145           throws LDAPException
146      {
147        try
148        {
149          final ASN1StreamReaderSequence opSequence = reader.beginSequence();
150          resultCode = reader.readEnumerated();
151    
152          String s = reader.readString();
153          ensureNotNull(s);
154          if (s.length() == 0)
155          {
156            matchedDN = null;
157          }
158          else
159          {
160            matchedDN = s;
161          }
162    
163          s = reader.readString();
164          ensureNotNull(s);
165          if (s.length() == 0)
166          {
167            diagnosticMessage = null;
168          }
169          else
170          {
171            diagnosticMessage = s;
172          }
173    
174          ASN1OctetString creds = null;
175          final ArrayList<String> refs = new ArrayList<String>(1);
176          while (opSequence.hasMoreElements())
177          {
178            final byte type = (byte) reader.peek();
179            if (type == GenericResponseProtocolOp.TYPE_REFERRALS)
180            {
181              final ASN1StreamReaderSequence refSequence = reader.beginSequence();
182              while (refSequence.hasMoreElements())
183              {
184                refs.add(reader.readString());
185              }
186            }
187            else if (type == TYPE_SERVER_SASL_CREDENTIALS)
188            {
189              creds = new ASN1OctetString(type, reader.readBytes());
190            }
191            else
192            {
193              throw new LDAPException(ResultCode.DECODING_ERROR,
194                   ERR_BIND_RESPONSE_INVALID_ELEMENT.get(toHex(type)));
195            }
196          }
197    
198          referralURLs = Collections.unmodifiableList(refs);
199          serverSASLCredentials = creds;
200        }
201        catch (LDAPException le)
202        {
203          debugException(le);
204          throw le;
205        }
206        catch (Exception e)
207        {
208          debugException(e);
209          throw new LDAPException(ResultCode.DECODING_ERROR,
210               ERR_BIND_RESPONSE_CANNOT_DECODE.get(getExceptionMessage(e)), e);
211        }
212      }
213    
214    
215    
216      /**
217       * Retrieves the result code for this bind response.
218       *
219       * @return  The result code for this bind response.
220       */
221      public int getResultCode()
222      {
223        return resultCode;
224      }
225    
226    
227    
228      /**
229       * Retrieves the matched DN for this bind response, if any.
230       *
231       * @return  The matched DN for this bind response, or {@code null} if there is
232       *          no matched DN.
233       */
234      public String getMatchedDN()
235      {
236        return matchedDN;
237      }
238    
239    
240    
241      /**
242       * Retrieves the diagnostic message for this bind response, if any.
243       *
244       * @return  The diagnostic message for this bind response, or {@code null} if
245       *          there is no diagnostic message.
246       */
247      public String getDiagnosticMessage()
248      {
249        return diagnosticMessage;
250      }
251    
252    
253    
254      /**
255       * Retrieves the list of referral URLs for this bind response.
256       *
257       * @return  The list of referral URLs for this bind response, or an empty list
258       *          if there are no referral URLs.
259       */
260      public List<String> getReferralURLs()
261      {
262        return referralURLs;
263      }
264    
265    
266    
267      /**
268       * Retrieves the server SASL credentials for this bind response, if any.
269       *
270       * @return  The server SASL credentials for this bind response, or
271       *          {@code null} if there are no server SASL credentials.
272       */
273      public ASN1OctetString getServerSASLCredentials()
274      {
275        return serverSASLCredentials;
276      }
277    
278    
279    
280      /**
281       * {@inheritDoc}
282       */
283      public byte getProtocolOpType()
284      {
285        return LDAPMessage.PROTOCOL_OP_TYPE_BIND_RESPONSE;
286      }
287    
288    
289    
290      /**
291       * {@inheritDoc}
292       */
293      public void writeTo(final ASN1Buffer buffer)
294      {
295        final ASN1BufferSequence opSequence =
296             buffer.beginSequence(LDAPMessage.PROTOCOL_OP_TYPE_BIND_RESPONSE);
297        buffer.addEnumerated(resultCode);
298        buffer.addOctetString(matchedDN);
299        buffer.addOctetString(diagnosticMessage);
300    
301        if (! referralURLs.isEmpty())
302        {
303          final ASN1BufferSequence refSequence =
304               buffer.beginSequence(GenericResponseProtocolOp.TYPE_REFERRALS);
305          for (final String s : referralURLs)
306          {
307            buffer.addOctetString(s);
308          }
309          refSequence.end();
310        }
311    
312        if (serverSASLCredentials != null)
313        {
314          buffer.addElement(serverSASLCredentials);
315        }
316    
317        opSequence.end();
318      }
319    
320    
321    
322      /**
323       * Retrieves a string representation of this protocol op.
324       *
325       * @return  A string representation of this protocol op.
326       */
327      @Override()
328      public String toString()
329      {
330        final StringBuilder buffer = new StringBuilder();
331        toString(buffer);
332        return buffer.toString();
333      }
334    
335    
336    
337      /**
338       * {@inheritDoc}
339       */
340      public void toString(final StringBuilder buffer)
341      {
342        buffer.append("BindResponseProtocolOp(resultCode=");
343        buffer.append(resultCode);
344    
345        if (matchedDN != null)
346        {
347          buffer.append(", matchedDN='");
348          buffer.append(matchedDN);
349          buffer.append('\'');
350        }
351    
352        if (diagnosticMessage != null)
353        {
354          buffer.append(", diagnosticMessage='");
355          buffer.append(diagnosticMessage);
356          buffer.append('\'');
357        }
358    
359        if (! referralURLs.isEmpty())
360        {
361          buffer.append(", referralURLs={");
362    
363          final Iterator<String> iterator = referralURLs.iterator();
364          while (iterator.hasNext())
365          {
366            buffer.append('\'');
367            buffer.append(iterator.next());
368            buffer.append('\'');
369            if (iterator.hasNext())
370            {
371              buffer.append(',');
372            }
373          }
374    
375          buffer.append('}');
376        }
377        buffer.append(')');
378      }
379    }