001/*
002 * Copyright 2008-2017 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2015-2017 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                      String.valueOf(e)), e);
385          }
386          break;
387
388        case TYPE_UPSTREAM_SERVER_ADDRESS:
389          upstreamServerAddress =
390               ASN1OctetString.decodeAsOctetString(element).stringValue();
391          break;
392
393        case TYPE_UPSTREAM_SERVER_SECURE:
394          try
395          {
396            upstreamServerSecure =
397                 ASN1Boolean.decodeAsBoolean(element).booleanValue();
398          }
399          catch (final Exception e)
400          {
401            debugException(e);
402            throw new LDAPException(ResultCode.DECODING_ERROR,
403                 ERR_ICRESP_CANNOT_DECODE_UPSTREAM_SECURE.get(
404                      String.valueOf(e)), e);
405          }
406          break;
407
408        case TYPE_SERVER_NAME:
409          serverName =
410               ASN1OctetString.decodeAsOctetString(element).stringValue();
411          break;
412
413        case TYPE_SERVER_SESSION_ID:
414          serverSessionID =
415               ASN1OctetString.decodeAsOctetString(element).stringValue();
416          break;
417
418        case TYPE_SERVER_RESPONSE_ID:
419          serverResponseID =
420               ASN1OctetString.decodeAsOctetString(element).stringValue();
421          break;
422
423        default:
424          throw new LDAPException(ResultCode.DECODING_ERROR,
425               ERR_ICRESP_INVALID_ELEMENT_TYPE.get(toHex(element.getType())));
426      }
427    }
428
429    return new IntermediateClientResponseValue(upstreamResponse,
430                                               upstreamServerAddress,
431                                               upstreamServerSecure,
432                                               serverName, serverSessionID,
433                                               serverResponseID);
434  }
435
436
437
438  /**
439   * Generates a hash code for this intermediate client response value.
440   *
441   * @return  A hash code for this intermediate client response value.
442   */
443  @Override()
444  public int hashCode()
445  {
446    int hashCode = 0;
447
448    if (upstreamResponse != null)
449    {
450      hashCode += upstreamResponse.hashCode();
451    }
452
453    if (upstreamServerAddress != null)
454    {
455      hashCode += upstreamServerAddress.hashCode();
456    }
457
458    if (upstreamServerSecure != null)
459    {
460      hashCode += upstreamServerSecure.hashCode();
461    }
462
463    if (serverName != null)
464    {
465      hashCode += serverName.hashCode();
466    }
467
468    if (serverSessionID != null)
469    {
470      hashCode += serverSessionID.hashCode();
471    }
472
473    if (serverResponseID != null)
474    {
475      hashCode += serverResponseID.hashCode();
476    }
477
478    return hashCode;
479  }
480
481
482
483  /**
484   * Indicates whether the provided object is equal to this intermediate client
485   * response value.  It will only be considered equal if the provided object is
486   * also an intermediate client response value with all the same fields.
487   *
488   * @param  o  The object for which to make the determination.
489   *
490   * @return  {@code true} if the provided object is considered equal to this
491   *          intermediate client response value, or {@code false} if not.
492   */
493  @Override()
494  public boolean equals(final Object o)
495  {
496    if (o == this)
497    {
498      return true;
499    }
500    else if (o == null)
501    {
502      return false;
503    }
504    else if (! (o instanceof IntermediateClientResponseValue))
505    {
506      return false;
507    }
508
509    final IntermediateClientResponseValue v =
510         (IntermediateClientResponseValue) o;
511
512    if (upstreamResponse == null)
513    {
514      if (v.upstreamResponse != null)
515      {
516        return false;
517      }
518    }
519    else
520    {
521      if (! upstreamResponse.equals(v.upstreamResponse))
522      {
523        return false;
524      }
525    }
526
527    if (upstreamServerAddress == null)
528    {
529      if (v.upstreamServerAddress != null)
530      {
531        return false;
532      }
533    }
534    else
535    {
536      if (! upstreamServerAddress.equals(v.upstreamServerAddress))
537      {
538        return false;
539      }
540    }
541
542    if (upstreamServerSecure == null)
543    {
544      if (v.upstreamServerSecure != null)
545      {
546        return false;
547      }
548    }
549    else
550    {
551      if (! upstreamServerSecure.equals(v.upstreamServerSecure))
552      {
553        return false;
554      }
555    }
556
557    if (serverName == null)
558    {
559      if (v.serverName != null)
560      {
561        return false;
562      }
563    }
564    else
565    {
566      if (! serverName.equals(v.serverName))
567      {
568        return false;
569      }
570    }
571
572    if (serverSessionID == null)
573    {
574      if (v.serverSessionID != null)
575      {
576        return false;
577      }
578    }
579    else
580    {
581      if (! serverSessionID.equals(v.serverSessionID))
582      {
583        return false;
584      }
585    }
586
587    if (serverResponseID == null)
588    {
589      if (v.serverResponseID != null)
590      {
591        return false;
592      }
593    }
594    else
595    {
596      if (! serverResponseID.equals(v.serverResponseID))
597      {
598        return false;
599      }
600    }
601
602    return true;
603  }
604
605
606
607  /**
608   * Retrieves a string representation of this intermediate client response
609   * value.
610   *
611   * @return  A string representation of this intermediate client response
612   *          value.
613   */
614  @Override()
615  public String toString()
616  {
617    final StringBuilder buffer = new StringBuilder();
618    toString(buffer);
619    return buffer.toString();
620  }
621
622
623
624  /**
625   * Appends a string representation of this intermediate client response value
626   * to the provided buffer.
627   *
628   * @param  buffer  The buffer to which the information is to be appended.
629   */
630  public void toString(final StringBuilder buffer)
631  {
632    buffer.append("IntermediateClientResponseValue(");
633
634    boolean added = false;
635    if (upstreamResponse != null)
636    {
637      buffer.append("upstreamResponse=");
638      upstreamResponse.toString(buffer);
639      added = true;
640    }
641
642    if (upstreamServerAddress != null)
643    {
644      if (added)
645      {
646        buffer.append(", ");
647      }
648      else
649      {
650        added = true;
651      }
652
653      buffer.append("upstreamServerAddress='");
654      buffer.append(upstreamServerAddress);
655      buffer.append('\'');
656    }
657
658    if (upstreamServerSecure != null)
659    {
660      if (added)
661      {
662        buffer.append(", ");
663      }
664      else
665      {
666        added = true;
667      }
668
669      buffer.append("upstreamServerSecure='");
670      buffer.append(upstreamServerSecure);
671      buffer.append('\'');
672    }
673
674    if (serverName != null)
675    {
676      if (added)
677      {
678        buffer.append(", ");
679      }
680      else
681      {
682        added = true;
683      }
684
685      buffer.append("serverName='");
686      buffer.append(serverName);
687      buffer.append('\'');
688    }
689
690    if (serverSessionID != null)
691    {
692      if (added)
693      {
694        buffer.append(", ");
695      }
696      else
697      {
698        added = true;
699      }
700
701      buffer.append("serverSessionID='");
702      buffer.append(serverSessionID);
703      buffer.append('\'');
704    }
705
706    if (serverResponseID != null)
707    {
708      if (added)
709      {
710        buffer.append(", ");
711      }
712      else
713      {
714        added = true;
715      }
716
717      buffer.append("serverResponseID='");
718      buffer.append(serverResponseID);
719      buffer.append('\'');
720    }
721
722    buffer.append(')');
723  }
724}