001/*
002 * Copyright 2008-2023 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2008-2023 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) 2008-2023 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.sdk.unboundidds.controls;
037
038
039
040import java.io.Serializable;
041import java.util.ArrayList;
042
043import com.unboundid.asn1.ASN1Boolean;
044import com.unboundid.asn1.ASN1Constants;
045import com.unboundid.asn1.ASN1Element;
046import com.unboundid.asn1.ASN1OctetString;
047import com.unboundid.asn1.ASN1Sequence;
048import com.unboundid.ldap.sdk.LDAPException;
049import com.unboundid.ldap.sdk.ResultCode;
050import com.unboundid.util.Debug;
051import com.unboundid.util.NotMutable;
052import com.unboundid.util.NotNull;
053import com.unboundid.util.Nullable;
054import com.unboundid.util.StaticUtils;
055import com.unboundid.util.ThreadSafety;
056import com.unboundid.util.ThreadSafetyLevel;
057
058import static com.unboundid.ldap.sdk.unboundidds.controls.ControlMessages.*;
059
060
061
062/**
063 * This class implements a data structure which encapsulates the value of an
064 * intermediate client response value.  It may recursively embed intermediate
065 * client response values from upstream servers.
066 * <BR>
067 * <BLOCKQUOTE>
068 *   <B>NOTE:</B>  This class, and other classes within the
069 *   {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only
070 *   supported for use against Ping Identity, UnboundID, and
071 *   Nokia/Alcatel-Lucent 8661 server products.  These classes provide support
072 *   for proprietary functionality or for external specifications that are not
073 *   considered stable or mature enough to be guaranteed to work in an
074 *   interoperable way with other types of LDAP servers.
075 * </BLOCKQUOTE>
076 * <BR>
077 * See the documentation in the {@link IntermediateClientRequestControl} class
078 * for an example of using the intermediate client request and response
079 * controls.
080 */
081@NotMutable()
082@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
083public final class IntermediateClientResponseValue
084       implements Serializable
085{
086  /**
087   * The BER type for the upstreamResponse element.
088   */
089  private static final byte TYPE_UPSTREAM_RESPONSE = (byte) 0xA0;
090
091
092
093  /**
094   * The BER type for the upstreamServerAddress element.
095   */
096  private static final byte TYPE_UPSTREAM_SERVER_ADDRESS = (byte) 0x81;
097
098
099
100  /**
101   * The BER type for the upstreamServerSecure element.
102   */
103  private static final byte TYPE_UPSTREAM_SERVER_SECURE = (byte) 0x82;
104
105
106
107  /**
108   * The BER type for the serverName element.
109   */
110  private static final byte TYPE_SERVER_NAME = (byte) 0x83;
111
112
113
114  /**
115   * The BER type for the serverSessionID element.
116   */
117  private static final byte TYPE_SERVER_SESSION_ID = (byte) 0x84;
118
119
120
121  /**
122   * The BER type for the serverResponseID element.
123   */
124  private static final byte TYPE_SERVER_RESPONSE_ID = (byte) 0x85;
125
126
127
128  /**
129   * The serial version UID for this serializable class.
130   */
131  private static final long serialVersionUID = 5165171788442351399L;
132
133
134
135  // Indicates whether communication with the upstream server is secure.
136  @Nullable private final Boolean upstreamServerSecure;
137
138  // The upstream response, if available.
139  @Nullable private final IntermediateClientResponseValue upstreamResponse;
140
141  // The server name, which describes the server application, if present.
142  @Nullable private final String serverName;
143
144  // The server response ID, if present.
145  @Nullable private final String serverResponseID;
146
147  // The server session ID, if present.
148  @Nullable private final String serverSessionID;
149
150  // The address of the upstream server, if available.
151  @Nullable private final String upstreamServerAddress;
152
153
154
155  /**
156   * Creates a new intermediate client response value with the provided
157   * information.
158   *
159   * @param  upstreamResponse       A wrapped intermediate client response from
160   *                                an upstream server.  It may be {@code null}
161   *                                if there is no wrapped upstream response.
162   * @param  upstreamServerAddress  The IP address or resolvable name of the
163   *                                upstream server system.  It may be
164   *                                {@code null} if there is no upstream server
165   *                                or its address is not available.
166   * @param  upstreamServerSecure   Indicates whether communication with the
167   *                                upstream server is secure.  It may be
168   *                                {@code null} if there is no upstream server
169   *                                or it is not known whether the communication
170   *                                is secure.
171   * @param  serverName             An identifier string that summarizes the
172   *                                server application that created this
173   *                                intermediate client response.  It may be
174   *                                {@code null} if that information is not
175   *                                available.
176   * @param  serverSessionID        A string that may be used to identify the
177   *                                session in the server application.  It may
178   *                                be {@code null} if there is no available
179   *                                session identifier.
180   * @param  serverResponseID       A string that may be used to identify the
181   *                                response in the server application.  It may
182   *                                be {@code null} if there is no available
183   *                                response identifier.
184   */
185  public IntermediateClientResponseValue(
186              @Nullable final IntermediateClientResponseValue upstreamResponse,
187              @Nullable final String upstreamServerAddress,
188              @Nullable final Boolean upstreamServerSecure,
189              @Nullable final String serverName,
190              @Nullable final String serverSessionID,
191              @Nullable final String serverResponseID)
192  {
193    this.upstreamResponse      = upstreamResponse;
194    this.upstreamServerAddress = upstreamServerAddress;
195    this.upstreamServerSecure  = upstreamServerSecure;
196    this.serverName            = serverName;
197    this.serverSessionID       = serverSessionID;
198    this.serverResponseID      = serverResponseID;
199  }
200
201
202
203  /**
204   * Retrieves the wrapped response from an upstream server, if available.
205   *
206   * @return  The wrapped response from an upstream server, or {@code null} if
207   *          there is none.
208   */
209  @Nullable()
210  public IntermediateClientResponseValue getUpstreamResponse()
211  {
212    return upstreamResponse;
213  }
214
215
216
217  /**
218   * Retrieves the IP address or resolvable name of the upstream server system,
219   * if available.
220   *
221   * @return  The IP address or resolvable name of the upstream server system,
222   *          {@code null} if there is no upstream server or its address is not
223   *          available.
224   */
225  @Nullable()
226  public String getUpstreamServerAddress()
227  {
228    return upstreamServerAddress;
229  }
230
231
232
233  /**
234   * Indicates whether the communication with the communication with the
235   * upstream server is secure (i.e., whether communication between the
236   * server application and the upstream server is safe from interpretation or
237   * undetectable alteration by a third party observer or interceptor).
238   *
239   *
240   * @return  {@code Boolean.TRUE} if communication with the upstream server is
241   *          secure, {@code Boolean.FALSE} if it is not secure, or
242   *          {@code null} if there is no upstream server or it is not known
243   *          whether the communication is secure.
244   */
245  @Nullable()
246  public Boolean upstreamServerSecure()
247  {
248    return upstreamServerSecure;
249  }
250
251
252
253  /**
254   * Retrieves a string that identifies the server application that created this
255   * intermediate client response value.
256   *
257   * @return  A string that may be used to identify the server application that
258   *          created this intermediate client response value.
259   */
260  @Nullable()
261  public String getServerName()
262  {
263    return serverName;
264  }
265
266
267
268  /**
269   * Retrieves a string that may be used to identify the session in the server
270   * application.
271   *
272   * @return  A string that may be used to identify the session in the server
273   *          application, or {@code null} if there is none.
274   */
275  @Nullable()
276  public String getServerSessionID()
277  {
278    return serverSessionID;
279  }
280
281
282
283  /**
284   * Retrieves a string that may be used to identify the response in the server
285   * application.
286   *
287   * @return  A string that may be used to identify the response in the server
288   *          application, or {@code null} if there is none.
289   */
290  @Nullable()
291  public String getServerResponseID()
292  {
293    return serverResponseID;
294  }
295
296
297
298  /**
299   * Encodes this intermediate client response value to a form that may be
300   * included in the response control.
301   *
302   * @return  An ASN.1 octet string containing the encoded client response
303   *          value.
304   */
305  @NotNull()
306  public ASN1Sequence encode()
307  {
308    return encode(ASN1Constants.UNIVERSAL_SEQUENCE_TYPE);
309  }
310
311
312
313  /**
314   * Encodes this intermediate client response value to a form that may be
315   * included in the response control.
316   *
317   * @param  type  The BER type to use for this element.
318   *
319   * @return  An ASN.1 octet string containing the encoded client response
320   *          value.
321   */
322  @NotNull()
323  private ASN1Sequence encode(final byte type)
324  {
325    final ArrayList<ASN1Element> elements = new ArrayList<>(6);
326
327    if (upstreamResponse != null)
328    {
329      elements.add(upstreamResponse.encode(TYPE_UPSTREAM_RESPONSE));
330    }
331
332    if (upstreamServerAddress != null)
333    {
334      elements.add(new ASN1OctetString(TYPE_UPSTREAM_SERVER_ADDRESS,
335                                       upstreamServerAddress));
336    }
337
338    if (upstreamServerSecure != null)
339    {
340      elements.add(new ASN1Boolean(TYPE_UPSTREAM_SERVER_SECURE,
341                                   upstreamServerSecure));
342    }
343
344    if (serverName != null)
345    {
346      elements.add(new ASN1OctetString(TYPE_SERVER_NAME,  serverName));
347    }
348
349    if (serverSessionID != null)
350    {
351      elements.add(new ASN1OctetString(TYPE_SERVER_SESSION_ID,
352                                       serverSessionID));
353    }
354
355    if (serverResponseID != null)
356    {
357      elements.add(new ASN1OctetString(TYPE_SERVER_RESPONSE_ID,
358                                       serverResponseID));
359    }
360
361    return new ASN1Sequence(type, elements);
362  }
363
364
365
366  /**
367   * Decodes the provided ASN.1 sequence as an intermediate client response
368   * value.
369   *
370   * @param  sequence  The sequence to be decoded as an intermediate client
371   *                   response value.
372   *
373   * @return  The decoded intermediate client response value.
374   *
375   * @throws  LDAPException  If the provided sequence cannot be decoded as an
376   *                         intermediate client response value.
377   */
378  @NotNull()
379  public static IntermediateClientResponseValue decode(
380                     @NotNull final ASN1Sequence sequence)
381         throws LDAPException
382  {
383    Boolean                         upstreamServerSecure  = null;
384    IntermediateClientResponseValue upstreamResponse      = null;
385    String                          upstreamServerAddress = null;
386    String                          serverName            = null;
387    String                          serverResponseID      = null;
388    String                          serverSessionID       = null;
389
390    for (final ASN1Element element : sequence.elements())
391    {
392      switch (element.getType())
393      {
394        case TYPE_UPSTREAM_RESPONSE:
395          try
396          {
397            final ASN1Sequence s = ASN1Sequence.decodeAsSequence(element);
398            upstreamResponse = decode(s);
399          }
400          catch (final LDAPException le)
401          {
402            Debug.debugException(le);
403            throw new LDAPException(ResultCode.DECODING_ERROR,
404                 ERR_ICRESP_CANNOT_DECODE_UPSTREAM_RESPONSE.get(
405                      le.getMessage()), le);
406          }
407          catch (final Exception e)
408          {
409            Debug.debugException(e);
410            throw new LDAPException(ResultCode.DECODING_ERROR,
411                 ERR_ICRESP_CANNOT_DECODE_UPSTREAM_RESPONSE.get(
412                      StaticUtils.getExceptionMessage(e)),
413                 e);
414          }
415          break;
416
417        case TYPE_UPSTREAM_SERVER_ADDRESS:
418          upstreamServerAddress =
419               ASN1OctetString.decodeAsOctetString(element).stringValue();
420          break;
421
422        case TYPE_UPSTREAM_SERVER_SECURE:
423          try
424          {
425            upstreamServerSecure =
426                 ASN1Boolean.decodeAsBoolean(element).booleanValue();
427          }
428          catch (final Exception e)
429          {
430            Debug.debugException(e);
431            throw new LDAPException(ResultCode.DECODING_ERROR,
432                 ERR_ICRESP_CANNOT_DECODE_UPSTREAM_SECURE.get(
433                      StaticUtils.getExceptionMessage(e)),
434                 e);
435          }
436          break;
437
438        case TYPE_SERVER_NAME:
439          serverName =
440               ASN1OctetString.decodeAsOctetString(element).stringValue();
441          break;
442
443        case TYPE_SERVER_SESSION_ID:
444          serverSessionID =
445               ASN1OctetString.decodeAsOctetString(element).stringValue();
446          break;
447
448        case TYPE_SERVER_RESPONSE_ID:
449          serverResponseID =
450               ASN1OctetString.decodeAsOctetString(element).stringValue();
451          break;
452
453        default:
454          throw new LDAPException(ResultCode.DECODING_ERROR,
455               ERR_ICRESP_INVALID_ELEMENT_TYPE.get(
456                    StaticUtils.toHex(element.getType())));
457      }
458    }
459
460    return new IntermediateClientResponseValue(upstreamResponse,
461                                               upstreamServerAddress,
462                                               upstreamServerSecure,
463                                               serverName, serverSessionID,
464                                               serverResponseID);
465  }
466
467
468
469  /**
470   * Generates a hash code for this intermediate client response value.
471   *
472   * @return  A hash code for this intermediate client response value.
473   */
474  @Override()
475  public int hashCode()
476  {
477    int hashCode = 0;
478
479    if (upstreamResponse != null)
480    {
481      hashCode += upstreamResponse.hashCode();
482    }
483
484    if (upstreamServerAddress != null)
485    {
486      hashCode += upstreamServerAddress.hashCode();
487    }
488
489    if (upstreamServerSecure != null)
490    {
491      hashCode += upstreamServerSecure.hashCode();
492    }
493
494    if (serverName != null)
495    {
496      hashCode += serverName.hashCode();
497    }
498
499    if (serverSessionID != null)
500    {
501      hashCode += serverSessionID.hashCode();
502    }
503
504    if (serverResponseID != null)
505    {
506      hashCode += serverResponseID.hashCode();
507    }
508
509    return hashCode;
510  }
511
512
513
514  /**
515   * Indicates whether the provided object is equal to this intermediate client
516   * response value.  It will only be considered equal if the provided object is
517   * also an intermediate client response value with all the same fields.
518   *
519   * @param  o  The object for which to make the determination.
520   *
521   * @return  {@code true} if the provided object is considered equal to this
522   *          intermediate client response value, or {@code false} if not.
523   */
524  @Override()
525  public boolean equals(@Nullable final Object o)
526  {
527    if (o == this)
528    {
529      return true;
530    }
531    else if (o == null)
532    {
533      return false;
534    }
535    else if (! (o instanceof IntermediateClientResponseValue))
536    {
537      return false;
538    }
539
540    final IntermediateClientResponseValue v =
541         (IntermediateClientResponseValue) o;
542
543    if (upstreamResponse == null)
544    {
545      if (v.upstreamResponse != null)
546      {
547        return false;
548      }
549    }
550    else
551    {
552      if (! upstreamResponse.equals(v.upstreamResponse))
553      {
554        return false;
555      }
556    }
557
558    if (upstreamServerAddress == null)
559    {
560      if (v.upstreamServerAddress != null)
561      {
562        return false;
563      }
564    }
565    else
566    {
567      if (! upstreamServerAddress.equals(v.upstreamServerAddress))
568      {
569        return false;
570      }
571    }
572
573    if (upstreamServerSecure == null)
574    {
575      if (v.upstreamServerSecure != null)
576      {
577        return false;
578      }
579    }
580    else
581    {
582      if (! upstreamServerSecure.equals(v.upstreamServerSecure))
583      {
584        return false;
585      }
586    }
587
588    if (serverName == null)
589    {
590      if (v.serverName != null)
591      {
592        return false;
593      }
594    }
595    else
596    {
597      if (! serverName.equals(v.serverName))
598      {
599        return false;
600      }
601    }
602
603    if (serverSessionID == null)
604    {
605      if (v.serverSessionID != null)
606      {
607        return false;
608      }
609    }
610    else
611    {
612      if (! serverSessionID.equals(v.serverSessionID))
613      {
614        return false;
615      }
616    }
617
618    if (serverResponseID == null)
619    {
620      if (v.serverResponseID != null)
621      {
622        return false;
623      }
624    }
625    else
626    {
627      if (! serverResponseID.equals(v.serverResponseID))
628      {
629        return false;
630      }
631    }
632
633    return true;
634  }
635
636
637
638  /**
639   * Retrieves a string representation of this intermediate client response
640   * value.
641   *
642   * @return  A string representation of this intermediate client response
643   *          value.
644   */
645  @Override()
646  @NotNull()
647  public String toString()
648  {
649    final StringBuilder buffer = new StringBuilder();
650    toString(buffer);
651    return buffer.toString();
652  }
653
654
655
656  /**
657   * Appends a string representation of this intermediate client response value
658   * to the provided buffer.
659   *
660   * @param  buffer  The buffer to which the information is to be appended.
661   */
662  public void toString(@NotNull final StringBuilder buffer)
663  {
664    buffer.append("IntermediateClientResponseValue(");
665
666    boolean added = false;
667    if (upstreamResponse != null)
668    {
669      buffer.append("upstreamResponse=");
670      upstreamResponse.toString(buffer);
671      added = true;
672    }
673
674    if (upstreamServerAddress != null)
675    {
676      if (added)
677      {
678        buffer.append(", ");
679      }
680      else
681      {
682        added = true;
683      }
684
685      buffer.append("upstreamServerAddress='");
686      buffer.append(upstreamServerAddress);
687      buffer.append('\'');
688    }
689
690    if (upstreamServerSecure != null)
691    {
692      if (added)
693      {
694        buffer.append(", ");
695      }
696      else
697      {
698        added = true;
699      }
700
701      buffer.append("upstreamServerSecure='");
702      buffer.append(upstreamServerSecure);
703      buffer.append('\'');
704    }
705
706    if (serverName != null)
707    {
708      if (added)
709      {
710        buffer.append(", ");
711      }
712      else
713      {
714        added = true;
715      }
716
717      buffer.append("serverName='");
718      buffer.append(serverName);
719      buffer.append('\'');
720    }
721
722    if (serverSessionID != null)
723    {
724      if (added)
725      {
726        buffer.append(", ");
727      }
728      else
729      {
730        added = true;
731      }
732
733      buffer.append("serverSessionID='");
734      buffer.append(serverSessionID);
735      buffer.append('\'');
736    }
737
738    if (serverResponseID != null)
739    {
740      if (added)
741      {
742        buffer.append(", ");
743      }
744      else
745      {
746        added = true;
747      }
748
749      buffer.append("serverResponseID='");
750      buffer.append(serverResponseID);
751      buffer.append('\'');
752    }
753
754    buffer.append(')');
755  }
756}