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