001    /*
002     * Copyright 2008-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2015 UnboundID Corp.
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     */
021    package com.unboundid.ldap.sdk.unboundidds.extensions;
022    
023    
024    
025    import java.util.ArrayList;
026    import java.util.Collection;
027    import java.util.Collections;
028    import java.util.Date;
029    import java.util.Iterator;
030    import java.util.List;
031    
032    import com.unboundid.asn1.ASN1Element;
033    import com.unboundid.asn1.ASN1Enumerated;
034    import com.unboundid.asn1.ASN1OctetString;
035    import com.unboundid.asn1.ASN1Sequence;
036    import com.unboundid.ldap.sdk.Control;
037    import com.unboundid.ldap.sdk.ExtendedResult;
038    import com.unboundid.ldap.sdk.LDAPException;
039    import com.unboundid.ldap.sdk.ResultCode;
040    import com.unboundid.util.NotMutable;
041    import com.unboundid.util.StaticUtils;
042    import com.unboundid.util.ThreadSafety;
043    import com.unboundid.util.ThreadSafetyLevel;
044    
045    import static com.unboundid.ldap.sdk.unboundidds.extensions.ExtOpMessages.*;
046    import static com.unboundid.util.Debug.*;
047    
048    
049    
050    /**
051     * <BLOCKQUOTE>
052     *   <B>NOTE:</B>  This class is part of the Commercial Edition of the UnboundID
053     *   LDAP SDK for Java.  It is not available for use in applications that
054     *   include only the Standard Edition of the LDAP SDK, and is not supported for
055     *   use in conjunction with non-UnboundID products.
056     * </BLOCKQUOTE>
057     * This class provides an implementation of an extended result that holds
058     * information about the response returned from a
059     * {@link GetSubtreeAccessibilityExtendedRequest}.  It has an OID of
060     * 1.3.6.1.4.1.30221.1.6.21, and successful responses will have a value with the
061     * following encoding:
062     * <BR><BR>
063     * <PRE>
064     *   GetSubtreeAccessibilityResultValue ::= SEQUENCE OF SEQUENCE {
065     *        subtreeBaseDN            [0] LDAPDN,
066     *        subtreeAccessibility     [1] ENUMERATED {
067     *             accessible                 (0),
068     *             read-only-bind-allowed     (1),
069     *             read-only-bind-denied      (2),
070     *             hidden                     (3),
071     *             ... },
072     *        bypassUserDN             [2] LDAPDN OPTIONAL,
073     *        effectiveTime            [3] OCTET STRING,
074     *        ... }
075     * </PRE>
076     */
077    @NotMutable()
078    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
079    public final class GetSubtreeAccessibilityExtendedResult
080           extends ExtendedResult
081    {
082      /**
083       * The OID (1.3.6.1.4.1.30221.1.6.21) for the get subtree accessibility
084       * extended result.
085       */
086      public static final String GET_SUBTREE_ACCESSIBILITY_RESULT_OID =
087           "1.3.6.1.4.1.30221.1.6.21";
088    
089    
090    
091      /**
092       * The BER type for the element that holds the base DN for a subtree
093       * accessibility restriction.
094       */
095      private static final byte TYPE_BASE_DN = (byte) 0x80;
096    
097    
098    
099      /**
100       * The BER type for the element that holds the accessibility state for a
101       * subtree accessibility restriction.
102       */
103      private static final byte TYPE_STATE = (byte) 0x81;
104    
105    
106    
107      /**
108       * The BER type for the element that holds the bypass user DN for a subtree
109       * accessibility restriction.
110       */
111      private static final byte TYPE_BYPASS_USER = (byte) 0x82;
112    
113    
114    
115      /**
116       * The BER type for the element that holds the effective time for a subtree
117       * accessibility restriction.
118       */
119      private static final byte TYPE_EFFECTIVE_TIME = (byte) 0x83;
120    
121    
122    
123      /**
124       * The serial version UID for this serializable class.
125       */
126      private static final long serialVersionUID = -3163306122775326749L;
127    
128    
129    
130      // A list of the subtree accessibility restrictions defined in the server.
131      private final List<SubtreeAccessibilityRestriction> accessibilityRestrictions;
132    
133    
134    
135      /**
136       * Creates a new get subtree accessibility extended result from the provided
137       * generic extended result.
138       *
139       * @param  extendedResult  The generic extended result to be decoded.
140       *
141       * @throws  LDAPException  If a problem occurs while attempting to decode the
142       *                         provided extended result as a get connection ID
143       *                         result.
144       */
145      public GetSubtreeAccessibilityExtendedResult(
146                  final ExtendedResult extendedResult)
147             throws LDAPException
148      {
149        super(extendedResult);
150    
151        final ASN1OctetString value = extendedResult.getValue();
152        if (value == null)
153        {
154          accessibilityRestrictions = null;
155          return;
156        }
157    
158        try
159        {
160          final ASN1Element[] restrictionElements =
161               ASN1Sequence.decodeAsSequence(value.getValue()).elements();
162          final ArrayList<SubtreeAccessibilityRestriction> restrictionList =
163               new ArrayList<SubtreeAccessibilityRestriction>(
164                    restrictionElements.length);
165    
166          for (final ASN1Element e : restrictionElements)
167          {
168            String baseDN = null;
169            SubtreeAccessibilityState state = null;
170            String bypassDN = null;
171            Date effectiveTime = null;
172    
173            for (final ASN1Element re : ASN1Sequence.decodeAsSequence(e).elements())
174            {
175              switch (re.getType())
176              {
177                case TYPE_BASE_DN:
178                  baseDN = ASN1OctetString.decodeAsOctetString(re).stringValue();
179                  break;
180                case TYPE_STATE:
181                  state = SubtreeAccessibilityState.valueOf(
182                       ASN1Enumerated.decodeAsEnumerated(re).intValue());
183                  if (state == null)
184                  {
185                    throw new LDAPException(ResultCode.DECODING_ERROR,
186                         ERR_GET_SUBTREE_ACCESSIBILITY_RESULT_UNEXPECTED_STATE.get(
187                              ASN1Enumerated.decodeAsEnumerated(re).intValue()));
188                  }
189                  break;
190                case TYPE_BYPASS_USER:
191                  bypassDN = ASN1OctetString.decodeAsOctetString(re).stringValue();
192                  break;
193                case TYPE_EFFECTIVE_TIME:
194                  effectiveTime = StaticUtils.decodeGeneralizedTime(
195                       ASN1OctetString.decodeAsOctetString(re).stringValue());
196                  break;
197                default:
198                  throw new LDAPException(ResultCode.DECODING_ERROR,
199                       ERR_GET_SUBTREE_ACCESSIBILITY_RESULT_UNEXPECTED_TYPE.get(
200                            StaticUtils.toHex(re.getType())));
201              }
202            }
203    
204            if (baseDN == null)
205            {
206              throw new LDAPException(ResultCode.DECODING_ERROR,
207                   ERR_GET_SUBTREE_ACCESSIBILITY_RESULT_MISSING_BASE.get());
208            }
209    
210            if (state == null)
211            {
212              throw new LDAPException(ResultCode.DECODING_ERROR,
213                   ERR_GET_SUBTREE_ACCESSIBILITY_RESULT_MISSING_STATE.get());
214            }
215    
216            if (effectiveTime == null)
217            {
218              throw new LDAPException(ResultCode.DECODING_ERROR,
219                   ERR_GET_SUBTREE_ACCESSIBILITY_RESULT_MISSING_TIME.get());
220            }
221    
222            restrictionList.add(new SubtreeAccessibilityRestriction(baseDN, state,
223                 bypassDN, effectiveTime));
224          }
225    
226          accessibilityRestrictions = Collections.unmodifiableList(restrictionList);
227        }
228        catch (final LDAPException le)
229        {
230          debugException(le);
231          throw le;
232        }
233        catch (Exception e)
234        {
235          debugException(e);
236          throw new LDAPException(ResultCode.DECODING_ERROR,
237               ERR_GET_SUBTREE_ACCESSIBILITY_RESULT_DECODE_ERROR.get(
238                    StaticUtils.getExceptionMessage(e)),
239               e);
240        }
241      }
242    
243    
244    
245      /**
246       * Creates a new get subtree accessibility extended result with the provided
247       * information.
248       *
249       * @param  messageID          The message ID for the LDAP message that is
250       *                            associated with this LDAP result.
251       * @param  resultCode         The result code from the response.
252       * @param  diagnosticMessage  The diagnostic message from the response, if
253       *                            available.
254       * @param  matchedDN          The matched DN from the response, if available.
255       * @param  referralURLs       The set of referral URLs from the response, if
256       *                            available.
257       * @param  restrictions       The set of subtree accessibility restrictions
258       *                            to include in the response.  It may be
259       *                            {@code null} if this represents an error
260       *                            response, or it may be empty if there are no
261       *                            subtree accessibility restrictions defined in
262       *                            the server.
263       * @param  responseControls   The set of controls from the response, if
264       *                            available.
265       */
266      public GetSubtreeAccessibilityExtendedResult(final int messageID,
267                  final ResultCode resultCode, final String diagnosticMessage,
268                  final String matchedDN, final String[] referralURLs,
269                  final Collection<SubtreeAccessibilityRestriction> restrictions,
270                  final Control... responseControls)
271      {
272        super(messageID, resultCode, diagnosticMessage, matchedDN, referralURLs,
273              null, encodeValue(restrictions), responseControls);
274    
275        if (restrictions == null)
276        {
277          accessibilityRestrictions = null;
278        }
279        else
280        {
281          accessibilityRestrictions = Collections.unmodifiableList(
282               new ArrayList<SubtreeAccessibilityRestriction>(restrictions));
283        }
284      }
285    
286    
287    
288      /**
289       * Encodes the value for this extended result using the provided information.
290       *
291       * @param  restrictions  The set of subtree accessibility restrictions to
292       *                       include in the response.  It may be {@code null} if
293       *                       this represents an error response, or it may be empty
294       *                       if there are no subtree accessibility restrictions
295       *                       defined in the server.
296       *
297       * @return  An ASN.1 octet string containing the properly-encoded value, or
298       *          {@code null} if there should be no value.
299       */
300      private static ASN1OctetString encodeValue(
301                   final Collection<SubtreeAccessibilityRestriction> restrictions)
302      {
303        if (restrictions == null)
304        {
305          return null;
306        }
307    
308        final ArrayList<ASN1Element> elements =
309             new ArrayList<ASN1Element>(restrictions.size());
310        for (final SubtreeAccessibilityRestriction r : restrictions)
311        {
312          final ArrayList<ASN1Element> restrictionElements =
313               new ArrayList<ASN1Element>(4);
314          restrictionElements.add(new ASN1OctetString(TYPE_BASE_DN,
315               r.getSubtreeBaseDN()));
316          restrictionElements.add(new ASN1Enumerated(TYPE_STATE,
317               r.getAccessibilityState().intValue()));
318    
319          if (r.getBypassUserDN() != null)
320          {
321            restrictionElements.add(new ASN1OctetString(TYPE_BYPASS_USER,
322                 r.getBypassUserDN()));
323          }
324    
325          restrictionElements.add(new ASN1OctetString(TYPE_EFFECTIVE_TIME,
326               StaticUtils.encodeGeneralizedTime(r.getEffectiveTime())));
327    
328          elements.add(new ASN1Sequence(restrictionElements));
329        }
330    
331        return new ASN1OctetString(new ASN1Sequence(elements).encode());
332      }
333    
334    
335    
336      /**
337       * Retrieves a list of the subtree accessibility restrictions defined in the
338       * server.
339       *
340       * @return  A list of the subtree accessibility restrictions defined in the
341       *          server, an empty list if there are no restrictions defined, or
342       *          {@code null} if no restriction data was included in the response
343       *          from the server (e.g., because it was an error response).
344       */
345      public List<SubtreeAccessibilityRestriction> getAccessibilityRestrictions()
346      {
347        return accessibilityRestrictions;
348      }
349    
350    
351    
352      /**
353       * {@inheritDoc}
354       */
355      @Override()
356      public String getExtendedResultName()
357      {
358        return INFO_EXTENDED_RESULT_NAME_GET_SUBTREE_ACCESSIBILITY.get();
359      }
360    
361    
362    
363      /**
364       * {@inheritDoc}
365       */
366      @Override()
367      public void toString(final StringBuilder buffer)
368      {
369        buffer.append("GetSubtreeAccessibilityExtendedResult(resultCode=");
370        buffer.append(getResultCode());
371    
372        final int messageID = getMessageID();
373        if (messageID >= 0)
374        {
375          buffer.append(", messageID=");
376          buffer.append(messageID);
377        }
378    
379        final String diagnosticMessage = getDiagnosticMessage();
380        if (diagnosticMessage != null)
381        {
382          buffer.append(", diagnosticMessage='");
383          buffer.append(diagnosticMessage);
384          buffer.append('\'');
385        }
386    
387        final String matchedDN = getMatchedDN();
388        if (matchedDN != null)
389        {
390          buffer.append(", matchedDN='");
391          buffer.append(matchedDN);
392          buffer.append('\'');
393        }
394    
395        final String[] referralURLs = getReferralURLs();
396        if ((referralURLs != null) && (referralURLs.length > 0))
397        {
398          buffer.append(", referralURLs={ '");
399          for (int i=0; i < referralURLs.length; i++)
400          {
401            if (i > 0)
402            {
403              buffer.append("', '");
404            }
405            buffer.append(referralURLs[i]);
406          }
407    
408          buffer.append("' }");
409        }
410    
411        if (accessibilityRestrictions != null)
412        {
413          buffer.append(", accessibilityRestrictions={");
414    
415          final Iterator<SubtreeAccessibilityRestriction> iterator =
416               accessibilityRestrictions.iterator();
417          while (iterator.hasNext())
418          {
419            iterator.next().toString(buffer);
420            if (iterator.hasNext())
421            {
422              buffer.append(", ");
423            }
424          }
425    
426          buffer.append('}');
427        }
428    
429        final Control[] controls = getResponseControls();
430        if (controls.length > 0)
431        {
432          buffer.append(", controls={");
433          for (int i=0; i < controls.length; i++)
434          {
435            if (i > 0)
436            {
437              buffer.append(", ");
438            }
439    
440            buffer.append(controls[i]);
441          }
442          buffer.append('}');
443        }
444    
445        buffer.append(')');
446      }
447    }