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