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