001/*
002 * Copyright 2009-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2009-2024 Ping Identity Corporation
007 *
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 *
012 *    http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 */
020/*
021 * Copyright (C) 2009-2024 Ping Identity Corporation
022 *
023 * This program is free software; you can redistribute it and/or modify
024 * it under the terms of the GNU General Public License (GPLv2 only)
025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
026 * as published by the Free Software Foundation.
027 *
028 * This program is distributed in the hope that it will be useful,
029 * but WITHOUT ANY WARRANTY; without even the implied warranty of
030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
031 * GNU General Public License for more details.
032 *
033 * You should have received a copy of the GNU General Public License
034 * along with this program; if not, see <http://www.gnu.org/licenses>.
035 */
036package com.unboundid.ldap.protocol;
037
038
039
040import java.util.ArrayList;
041
042import com.unboundid.asn1.ASN1Buffer;
043import com.unboundid.asn1.ASN1BufferSequence;
044import com.unboundid.asn1.ASN1Element;
045import com.unboundid.asn1.ASN1OctetString;
046import com.unboundid.asn1.ASN1Sequence;
047import com.unboundid.asn1.ASN1StreamReader;
048import com.unboundid.asn1.ASN1StreamReaderSequence;
049import com.unboundid.ldap.sdk.Control;
050import com.unboundid.ldap.sdk.IntermediateResponse;
051import com.unboundid.ldap.sdk.LDAPException;
052import com.unboundid.ldap.sdk.ResultCode;
053import com.unboundid.util.Debug;
054import com.unboundid.util.InternalUseOnly;
055import com.unboundid.util.NotMutable;
056import com.unboundid.util.NotNull;
057import com.unboundid.util.Nullable;
058import com.unboundid.util.StaticUtils;
059import com.unboundid.util.ThreadSafety;
060import com.unboundid.util.ThreadSafetyLevel;
061
062import static com.unboundid.ldap.protocol.ProtocolMessages.*;
063
064
065
066/**
067 * This class provides an implementation of an LDAP intermediate response
068 * protocol op.
069 */
070@InternalUseOnly()
071@NotMutable()
072@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
073public final class IntermediateResponseProtocolOp
074       implements ProtocolOp
075{
076  /**
077   * The BER type for the OID element.
078   */
079  public static final byte TYPE_OID = (byte) 0x80;
080
081
082
083  /**
084   * The BER type for the value element.
085   */
086  public static final byte TYPE_VALUE = (byte) 0x81;
087
088
089
090  /**
091   * The serial version UID for this serializable class.
092   */
093  private static final long serialVersionUID = 118549806265654465L;
094
095
096
097  // The value for this intermediate response.
098  @Nullable private final ASN1OctetString value;
099
100  // The OID for this intermediate response.
101  @Nullable private final String oid;
102
103
104
105  /**
106   * Creates a new intermediate response protocol op with the provided
107   * information.
108   *
109   * @param  oid    The OID for this intermediate response, or {@code null} if
110   *                there should not be an OID.
111   * @param  value  The value for this intermediate response, or {@code null} if
112   *                there should not be a value.
113   */
114  public IntermediateResponseProtocolOp(@Nullable final String oid,
115                                        @Nullable final ASN1OctetString value)
116  {
117    this.oid = oid;
118
119    if (value == null)
120    {
121      this.value = null;
122    }
123    else
124    {
125      this.value = new ASN1OctetString(TYPE_VALUE, value.getValue());
126    }
127  }
128
129
130
131  /**
132   * Creates a new intermediate response protocol op from the provided
133   * intermediate response object.
134   *
135   * @param  response  The intermediate response object to use to create this
136   *                   protocol op.
137   */
138  public IntermediateResponseProtocolOp(
139              @NotNull final IntermediateResponse response)
140  {
141    oid = response.getOID();
142
143    final ASN1OctetString responseValue = response.getValue();
144    if (responseValue == null)
145    {
146      value = null;
147    }
148    else
149    {
150      value = new ASN1OctetString(TYPE_VALUE, responseValue.getValue());
151    }
152  }
153
154
155
156  /**
157   * Creates a new intermediate response protocol op read from the provided
158   * ASN.1 stream reader.
159   *
160   * @param  reader  The ASN.1 stream reader from which to read the intermediate
161   *                 response protocol op.
162   *
163   * @throws  LDAPException  If a problem occurs while reading or parsing the
164   *                         intermediate response.
165   */
166  IntermediateResponseProtocolOp(@NotNull final ASN1StreamReader reader)
167       throws LDAPException
168  {
169    try
170    {
171      final ASN1StreamReaderSequence opSequence = reader.beginSequence();
172
173      String o = null;
174      ASN1OctetString v = null;
175      while (opSequence.hasMoreElements())
176      {
177        final byte type = (byte) reader.peek();
178        if (type == TYPE_OID)
179        {
180          o = reader.readString();
181        }
182        else if (type == TYPE_VALUE)
183        {
184          v = new ASN1OctetString(type, reader.readBytes());
185        }
186        else
187        {
188          throw new LDAPException(ResultCode.DECODING_ERROR,
189               ERR_INTERMEDIATE_RESPONSE_INVALID_ELEMENT.get(
190                    StaticUtils.toHex(type)));
191        }
192      }
193
194      oid = o;
195      value = v;
196    }
197    catch (final LDAPException le)
198    {
199      Debug.debugException(le);
200      throw le;
201    }
202    catch (final Exception e)
203    {
204      Debug.debugException(e);
205
206      throw new LDAPException(ResultCode.DECODING_ERROR,
207           ERR_INTERMEDIATE_RESPONSE_CANNOT_DECODE.get(
208                StaticUtils.getExceptionMessage(e)),
209           e);
210    }
211  }
212
213
214
215  /**
216   * Retrieves the OID for this intermediate response, if any.
217   *
218   * @return  The OID for this intermediate response, or {@code null} if there
219   *          is no response OID.
220   */
221  @Nullable()
222  public String getOID()
223  {
224    return oid;
225  }
226
227
228
229  /**
230   * Retrieves the value for this intermediate response, if any.
231   *
232   * @return  The value for this intermediate response, or {@code null} if there
233   *          is no response value.
234   */
235  @Nullable()
236  public ASN1OctetString getValue()
237  {
238    return value;
239  }
240
241
242
243  /**
244   * {@inheritDoc}
245   */
246  @Override()
247  public byte getProtocolOpType()
248  {
249    return LDAPMessage.PROTOCOL_OP_TYPE_INTERMEDIATE_RESPONSE;
250  }
251
252
253
254  /**
255   * {@inheritDoc}
256   */
257  @Override()
258  @NotNull()
259  public ASN1Element encodeProtocolOp()
260  {
261    final ArrayList<ASN1Element> elements = new ArrayList<>(2);
262
263    if (oid != null)
264    {
265      elements.add(new ASN1OctetString(TYPE_OID, oid));
266    }
267
268    if (value != null)
269    {
270      elements.add(value);
271    }
272
273    return new ASN1Sequence(LDAPMessage.PROTOCOL_OP_TYPE_INTERMEDIATE_RESPONSE,
274         elements);
275  }
276
277
278
279  /**
280   * Decodes the provided ASN.1 element as a intermediate response protocol op.
281   *
282   * @param  element  The ASN.1 element to be decoded.
283   *
284   * @return  The decoded intermediate response protocol op.
285   *
286   * @throws  LDAPException  If the provided ASN.1 element cannot be decoded as
287   *                         a intermediate response protocol op.
288   */
289  @NotNull()
290  public static IntermediateResponseProtocolOp decodeProtocolOp(
291                     @NotNull final ASN1Element element)
292         throws LDAPException
293  {
294    try
295    {
296      String oid = null;
297      ASN1OctetString value = null;
298      for (final ASN1Element e :
299           ASN1Sequence.decodeAsSequence(element).elements())
300      {
301        switch (e.getType())
302        {
303          case TYPE_OID:
304            oid = ASN1OctetString.decodeAsOctetString(e).stringValue();
305            break;
306          case TYPE_VALUE:
307            value = ASN1OctetString.decodeAsOctetString(e);
308            break;
309          default:
310            throw new LDAPException(ResultCode.DECODING_ERROR,
311                 ERR_INTERMEDIATE_RESPONSE_INVALID_ELEMENT.get(
312                      StaticUtils.toHex(e.getType())));
313        }
314      }
315
316      return new IntermediateResponseProtocolOp(oid, value);
317    }
318    catch (final LDAPException le)
319    {
320      Debug.debugException(le);
321      throw le;
322    }
323    catch (final Exception e)
324    {
325      Debug.debugException(e);
326      throw new LDAPException(ResultCode.DECODING_ERROR,
327           ERR_COMPARE_REQUEST_CANNOT_DECODE.get(
328                StaticUtils.getExceptionMessage(e)),
329           e);
330    }
331  }
332
333
334
335  /**
336   * {@inheritDoc}
337   */
338  @Override()
339  public void writeTo(@NotNull final ASN1Buffer buffer)
340  {
341    final ASN1BufferSequence opSequence = buffer.beginSequence(
342         LDAPMessage.PROTOCOL_OP_TYPE_INTERMEDIATE_RESPONSE);
343
344    if (oid != null)
345    {
346      buffer.addOctetString(TYPE_OID, oid);
347    }
348
349    if (value != null)
350    {
351      buffer.addElement(value);
352    }
353
354    opSequence.end();
355  }
356
357
358
359  /**
360   * Creates a intermediate response from this protocol op.
361   *
362   * @param  controls  The set of controls to include in the intermediate
363   *                   response.  It may be empty or {@code null} if no controls
364   *                   should be included.
365   *
366   * @return  The intermediate response that was created.
367   */
368  @NotNull()
369  public IntermediateResponse toIntermediateResponse(
370                                   @Nullable final Control... controls)
371  {
372    return new IntermediateResponse(-1, oid, value, controls);
373  }
374
375
376
377  /**
378   * Retrieves a string representation of this protocol op.
379   *
380   * @return  A string representation of this protocol op.
381   */
382  @Override()
383  @NotNull()
384  public String toString()
385  {
386    final StringBuilder buffer = new StringBuilder();
387    toString(buffer);
388    return buffer.toString();
389  }
390
391
392
393  /**
394   * {@inheritDoc}
395   */
396  @Override()
397  public void toString(@NotNull final StringBuilder buffer)
398  {
399    buffer.append("IntermediateResponseProtocolOp(");
400
401    if (oid != null)
402    {
403      buffer.append("oid='");
404      buffer.append(oid);
405      buffer.append('\'');
406    }
407
408    buffer.append(')');
409  }
410}