001    /*
002     * Copyright 2009-2016 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2009-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.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.NotMutable;
044    import com.unboundid.util.InternalUseOnly;
045    import com.unboundid.util.ThreadSafety;
046    import com.unboundid.util.ThreadSafetyLevel;
047    
048    import static com.unboundid.ldap.protocol.ProtocolMessages.*;
049    import static com.unboundid.util.Debug.*;
050    import static com.unboundid.util.StaticUtils.*;
051    import 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)
061    public 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    }