001    /*
002     * Copyright 2009-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2009-2015 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.InternalUseOnly;
044    
045    import static com.unboundid.ldap.protocol.ProtocolMessages.*;
046    import static com.unboundid.util.Debug.*;
047    import static com.unboundid.util.StaticUtils.*;
048    import static com.unboundid.util.Validator.*;
049    
050    
051    
052    /**
053     * This class provides an implementation of a bind response protocol op.
054     */
055    @InternalUseOnly()
056    public final class BindResponseProtocolOp
057           implements ProtocolOp
058    {
059      /**
060       * The BER type for the server SASL credentials element.
061       */
062      public static final byte TYPE_SERVER_SASL_CREDENTIALS = (byte) 0x87;
063    
064    
065    
066      /**
067       * The serial version UID for this serializable class.
068       */
069      private static final long serialVersionUID = -7757619031268544913L;
070    
071    
072    
073      // The server SASL credentials for this bind response.
074      private final ASN1OctetString serverSASLCredentials;
075    
076      // The result code for this bind response.
077      private final int resultCode;
078    
079      // The referral URLs for this bind response.
080      private final List<String> referralURLs;
081    
082      // The diagnostic message for this bind response.
083      private final String diagnosticMessage;
084    
085      // The matched DN for this bind response.
086      private final String matchedDN;
087    
088    
089    
090      /**
091       * Creates a new instance of this bind response protocol op with the provided
092       * information.
093       *
094       * @param  resultCode             The result code for this response.
095       * @param  matchedDN              The matched DN for this response, if
096       *                                available.
097       * @param  diagnosticMessage      The diagnostic message for this response, if
098       *                                any.
099       * @param  referralURLs           The list of referral URLs for this response,
100       *                                if any.
101       * @param  serverSASLCredentials  The server SASL credentials for this
102       *                                response, if available.
103       */
104      public BindResponseProtocolOp(final int resultCode, final String matchedDN,
105                                    final String diagnosticMessage,
106                                    final List<String> referralURLs,
107                                    final ASN1OctetString serverSASLCredentials)
108      {
109        this.resultCode            = resultCode;
110        this.matchedDN             = matchedDN;
111        this.diagnosticMessage     = diagnosticMessage;
112    
113        if (referralURLs == null)
114        {
115          this.referralURLs = Collections.emptyList();
116        }
117        else
118        {
119          this.referralURLs = Collections.unmodifiableList(referralURLs);
120        }
121    
122        if (serverSASLCredentials == null)
123        {
124          this.serverSASLCredentials = null;
125        }
126        else
127        {
128          this.serverSASLCredentials = new ASN1OctetString(
129               TYPE_SERVER_SASL_CREDENTIALS, serverSASLCredentials.getValue());
130        }
131      }
132    
133    
134    
135      /**
136       * Creates a new bind response protocol op from the provided bind result
137       * object.
138       *
139       * @param  result  The LDAP result object to use to create this protocol op.
140       */
141      public BindResponseProtocolOp(final LDAPResult result)
142      {
143        resultCode            = result.getResultCode().intValue();
144        matchedDN             = result.getMatchedDN();
145        diagnosticMessage     = result.getDiagnosticMessage();
146        referralURLs          = toList(result.getReferralURLs());
147    
148        if (result instanceof BindResult)
149        {
150          final BindResult br = (BindResult) result;
151          serverSASLCredentials = br.getServerSASLCredentials();
152        }
153        else
154        {
155          serverSASLCredentials = null;
156        }
157      }
158    
159    
160    
161      /**
162       * Creates a new bind response protocol op read from the provided ASN.1 stream
163       * reader.
164       *
165       * @param  reader  The ASN.1 stream reader from which to read the bind
166       *                 response.
167       *
168       * @throws  LDAPException  If a problem occurs while reading or parsing the
169       *                         bind response.
170       */
171      BindResponseProtocolOp(final ASN1StreamReader reader)
172           throws LDAPException
173      {
174        try
175        {
176          final ASN1StreamReaderSequence opSequence = reader.beginSequence();
177          resultCode = reader.readEnumerated();
178    
179          String s = reader.readString();
180          ensureNotNull(s);
181          if (s.length() == 0)
182          {
183            matchedDN = null;
184          }
185          else
186          {
187            matchedDN = s;
188          }
189    
190          s = reader.readString();
191          ensureNotNull(s);
192          if (s.length() == 0)
193          {
194            diagnosticMessage = null;
195          }
196          else
197          {
198            diagnosticMessage = s;
199          }
200    
201          ASN1OctetString creds = null;
202          final ArrayList<String> refs = new ArrayList<String>(1);
203          while (opSequence.hasMoreElements())
204          {
205            final byte type = (byte) reader.peek();
206            if (type == GenericResponseProtocolOp.TYPE_REFERRALS)
207            {
208              final ASN1StreamReaderSequence refSequence = reader.beginSequence();
209              while (refSequence.hasMoreElements())
210              {
211                refs.add(reader.readString());
212              }
213            }
214            else if (type == TYPE_SERVER_SASL_CREDENTIALS)
215            {
216              creds = new ASN1OctetString(type, reader.readBytes());
217            }
218            else
219            {
220              throw new LDAPException(ResultCode.DECODING_ERROR,
221                   ERR_BIND_RESPONSE_INVALID_ELEMENT.get(toHex(type)));
222            }
223          }
224    
225          referralURLs = Collections.unmodifiableList(refs);
226          serverSASLCredentials = creds;
227        }
228        catch (LDAPException le)
229        {
230          debugException(le);
231          throw le;
232        }
233        catch (Exception e)
234        {
235          debugException(e);
236          throw new LDAPException(ResultCode.DECODING_ERROR,
237               ERR_BIND_RESPONSE_CANNOT_DECODE.get(getExceptionMessage(e)), e);
238        }
239      }
240    
241    
242    
243      /**
244       * Retrieves the result code for this bind response.
245       *
246       * @return  The result code for this bind response.
247       */
248      public int getResultCode()
249      {
250        return resultCode;
251      }
252    
253    
254    
255      /**
256       * Retrieves the matched DN for this bind response, if any.
257       *
258       * @return  The matched DN for this bind response, or {@code null} if there is
259       *          no matched DN.
260       */
261      public String getMatchedDN()
262      {
263        return matchedDN;
264      }
265    
266    
267    
268      /**
269       * Retrieves the diagnostic message for this bind response, if any.
270       *
271       * @return  The diagnostic message for this bind response, or {@code null} if
272       *          there is no diagnostic message.
273       */
274      public String getDiagnosticMessage()
275      {
276        return diagnosticMessage;
277      }
278    
279    
280    
281      /**
282       * Retrieves the list of referral URLs for this bind response.
283       *
284       * @return  The list of referral URLs for this bind response, or an empty list
285       *          if there are no referral URLs.
286       */
287      public List<String> getReferralURLs()
288      {
289        return referralURLs;
290      }
291    
292    
293    
294      /**
295       * Retrieves the server SASL credentials for this bind response, if any.
296       *
297       * @return  The server SASL credentials for this bind response, or
298       *          {@code null} if there are no server SASL credentials.
299       */
300      public ASN1OctetString getServerSASLCredentials()
301      {
302        return serverSASLCredentials;
303      }
304    
305    
306    
307      /**
308       * {@inheritDoc}
309       */
310      public byte getProtocolOpType()
311      {
312        return LDAPMessage.PROTOCOL_OP_TYPE_BIND_RESPONSE;
313      }
314    
315    
316    
317      /**
318       * {@inheritDoc}
319       */
320      public ASN1Element encodeProtocolOp()
321      {
322        final ArrayList<ASN1Element> elements = new ArrayList<ASN1Element>(5);
323        elements.add(new ASN1Enumerated(getResultCode()));
324    
325        final String mDN = getMatchedDN();
326        if (mDN == null)
327        {
328          elements.add(new ASN1OctetString());
329        }
330        else
331        {
332          elements.add(new ASN1OctetString(mDN));
333        }
334    
335        final String dm = getDiagnosticMessage();
336        if (dm == null)
337        {
338          elements.add(new ASN1OctetString());
339        }
340        else
341        {
342          elements.add(new ASN1OctetString(dm));
343        }
344    
345        final List<String> refs = getReferralURLs();
346        if (! refs.isEmpty())
347        {
348          final ArrayList<ASN1Element> refElements =
349               new ArrayList<ASN1Element>(refs.size());
350          for (final String r : refs)
351          {
352            refElements.add(new ASN1OctetString(r));
353          }
354          elements.add(new ASN1Sequence(GenericResponseProtocolOp.TYPE_REFERRALS,
355               refElements));
356        }
357    
358        if (serverSASLCredentials != null)
359        {
360          elements.add(serverSASLCredentials);
361        }
362    
363        return new ASN1Sequence(LDAPMessage.PROTOCOL_OP_TYPE_BIND_RESPONSE,
364             elements);
365      }
366    
367    
368    
369      /**
370       * Decodes the provided ASN.1 element as a bind response protocol op.
371       *
372       * @param  element  The ASN.1 element to be decoded.
373       *
374       * @return  The decoded bind response protocol op.
375       *
376       * @throws  LDAPException  If the provided ASN.1 element cannot be decoded as
377       *                         a bind response protocol op.
378       */
379      public static BindResponseProtocolOp decodeProtocolOp(
380                                                final ASN1Element element)
381             throws LDAPException
382      {
383        try
384        {
385          final ASN1Element[] elements =
386               ASN1Sequence.decodeAsSequence(element).elements();
387          final int resultCode =
388               ASN1Enumerated.decodeAsEnumerated(elements[0]).intValue();
389    
390          final String matchedDN;
391          final String md =
392               ASN1OctetString.decodeAsOctetString(elements[1]).stringValue();
393          if (md.length() > 0)
394          {
395            matchedDN = md;
396          }
397          else
398          {
399            matchedDN = null;
400          }
401    
402          final String diagnosticMessage;
403          final String dm =
404               ASN1OctetString.decodeAsOctetString(elements[2]).stringValue();
405          if (dm.length() > 0)
406          {
407            diagnosticMessage = dm;
408          }
409          else
410          {
411            diagnosticMessage = null;
412          }
413    
414          ASN1OctetString serverSASLCredentials = null;
415          List<String> referralURLs = null;
416          if (elements.length > 3)
417          {
418            for (int i=3; i < elements.length; i++)
419            {
420              switch (elements[i].getType())
421              {
422                case GenericResponseProtocolOp.TYPE_REFERRALS:
423                  final ASN1Element[] refElements =
424                       ASN1Sequence.decodeAsSequence(elements[3]).elements();
425                  referralURLs = new ArrayList<String>(refElements.length);
426                  for (final ASN1Element e : refElements)
427                  {
428                    referralURLs.add(
429                         ASN1OctetString.decodeAsOctetString(e).stringValue());
430                  }
431                  break;
432    
433                case TYPE_SERVER_SASL_CREDENTIALS:
434                  serverSASLCredentials =
435                       ASN1OctetString.decodeAsOctetString(elements[i]);
436                  break;
437    
438                default:
439                  throw new LDAPException(ResultCode.DECODING_ERROR,
440                       ERR_BIND_RESPONSE_INVALID_ELEMENT.get(
441                            toHex(elements[i].getType())));
442              }
443            }
444          }
445    
446          return new BindResponseProtocolOp(resultCode, matchedDN,
447               diagnosticMessage, referralURLs, serverSASLCredentials);
448        }
449        catch (final LDAPException le)
450        {
451          debugException(le);
452          throw le;
453        }
454        catch (final Exception e)
455        {
456          debugException(e);
457          throw new LDAPException(ResultCode.DECODING_ERROR,
458               ERR_BIND_RESPONSE_CANNOT_DECODE.get(getExceptionMessage(e)),
459               e);
460        }
461      }
462    
463    
464    
465      /**
466       * {@inheritDoc}
467       */
468      public void writeTo(final ASN1Buffer buffer)
469      {
470        final ASN1BufferSequence opSequence =
471             buffer.beginSequence(LDAPMessage.PROTOCOL_OP_TYPE_BIND_RESPONSE);
472        buffer.addEnumerated(resultCode);
473        buffer.addOctetString(matchedDN);
474        buffer.addOctetString(diagnosticMessage);
475    
476        if (! referralURLs.isEmpty())
477        {
478          final ASN1BufferSequence refSequence =
479               buffer.beginSequence(GenericResponseProtocolOp.TYPE_REFERRALS);
480          for (final String s : referralURLs)
481          {
482            buffer.addOctetString(s);
483          }
484          refSequence.end();
485        }
486    
487        if (serverSASLCredentials != null)
488        {
489          buffer.addElement(serverSASLCredentials);
490        }
491    
492        opSequence.end();
493      }
494    
495    
496    
497      /**
498       * Creates a new LDAP result object from this response protocol op.
499       *
500       * @param  controls  The set of controls to include in the LDAP result.  It
501       *                   may be empty or {@code null} if no controls should be
502       *                   included.
503       *
504       * @return  The LDAP result that was created.
505       */
506      public BindResult toBindResult(final Control... controls)
507      {
508        final String[] refs;
509        if (referralURLs.isEmpty())
510        {
511          refs = NO_STRINGS;
512        }
513        else
514        {
515          refs = new String[referralURLs.size()];
516          referralURLs.toArray(refs);
517        }
518    
519        return new BindResult(-1, ResultCode.valueOf(resultCode), diagnosticMessage,
520             matchedDN, refs, controls, serverSASLCredentials);
521      }
522    
523    
524    
525      /**
526       * Retrieves a string representation of this protocol op.
527       *
528       * @return  A string representation of this protocol op.
529       */
530      @Override()
531      public String toString()
532      {
533        final StringBuilder buffer = new StringBuilder();
534        toString(buffer);
535        return buffer.toString();
536      }
537    
538    
539    
540      /**
541       * {@inheritDoc}
542       */
543      public void toString(final StringBuilder buffer)
544      {
545        buffer.append("BindResponseProtocolOp(resultCode=");
546        buffer.append(resultCode);
547    
548        if (matchedDN != null)
549        {
550          buffer.append(", matchedDN='");
551          buffer.append(matchedDN);
552          buffer.append('\'');
553        }
554    
555        if (diagnosticMessage != null)
556        {
557          buffer.append(", diagnosticMessage='");
558          buffer.append(diagnosticMessage);
559          buffer.append('\'');
560        }
561    
562        if (! referralURLs.isEmpty())
563        {
564          buffer.append(", referralURLs={");
565    
566          final Iterator<String> iterator = referralURLs.iterator();
567          while (iterator.hasNext())
568          {
569            buffer.append('\'');
570            buffer.append(iterator.next());
571            buffer.append('\'');
572            if (iterator.hasNext())
573            {
574              buffer.append(',');
575            }
576          }
577    
578          buffer.append('}');
579        }
580        buffer.append(')');
581      }
582    }