001    /*
002     * Copyright 2013-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2013-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.experimental;
022    
023    
024    
025    import com.unboundid.asn1.ASN1Element;
026    import com.unboundid.asn1.ASN1Integer;
027    import com.unboundid.asn1.ASN1OctetString;
028    import com.unboundid.asn1.ASN1Sequence;
029    import com.unboundid.ldap.sdk.Control;
030    import com.unboundid.ldap.sdk.DecodeableControl;
031    import com.unboundid.ldap.sdk.LDAPException;
032    import com.unboundid.ldap.sdk.ResultCode;
033    import com.unboundid.ldap.sdk.SearchResult;
034    import com.unboundid.util.Debug;
035    import com.unboundid.util.NotMutable;
036    import com.unboundid.util.StaticUtils;
037    import com.unboundid.util.ThreadSafety;
038    import com.unboundid.util.ThreadSafetyLevel;
039    
040    import static com.unboundid.ldap.sdk.experimental.ExperimentalMessages.*;
041    
042    
043    
044    /**
045     * This class provides support for a control that may be used to poll an Active
046     * Directory Server for information about changes that have been processed.  Use
047     * of this control is documented at
048     * <A HREF="http://support.microsoft.com/kb/891995">
049     * http://support.microsoft.com/kb/891995</A> and at
050     * <A HREF="http://msdn.microsoft.com/en-us/library/ms677626.aspx">
051     * http://msdn.microsoft.com/en-us/library/ms677626.aspx</A>.  The control OID
052     * and value format are described at
053     * <A HREF="http://msdn.microsoft.com/en-us/library/aa366978%28VS.85%29.aspx">
054     * http://msdn.microsoft.com/en-us/library/aa366978%28VS.85%29.aspx</A> and the
055     * values of the flags are documented at
056     * <A HREF="http://msdn.microsoft.com/en-us/library/cc223347.aspx">
057     * http://msdn.microsoft.com/en-us/library/cc223347.aspx</A>.
058     * <BR><BR>
059     * <H2>Example</H2>
060     * The following example demonstrates the process for using the DirSync control
061     * to identify changes to user entries below "dc=example,dc=com":
062     * <PRE>
063     * // Create a search request that will be used to identify all users below
064     * // "dc=example,dc=com".
065     * final SearchRequest searchRequest = new SearchRequest("dc=example,dc=com",
066     *      SearchScope.SUB, Filter.createEqualityFilter("objectClass", "User"));
067     *
068     * // Define the components that will be included in the DirSync request
069     * // control.
070     * ASN1OctetString cookie = null;
071     * final int flags = ActiveDirectoryDirSyncControl.FLAG_INCREMENTAL_VALUES |
072     *      ActiveDirectoryDirSyncControl.FLAG_OBJECT_SECURITY;
073     *
074     * // Create a loop that will be used to keep polling for changes.
075     * while (keepLooping)
076     * {
077     *   // Update the controls that will be used for the search request.
078     *   searchRequest.setControls(new ActiveDirectoryDirSyncControl(true, flags,
079     *        50, cookie));
080     *
081     *   // Process the search and get the response control.
082     *   final SearchResult searchResult = connection.search(searchRequest);
083     *   ActiveDirectoryDirSyncControl dirSyncResponse =
084     *        ActiveDirectoryDirSyncControl.get(searchResult);
085     *   cookie = dirSyncResponse.getCookie();
086     *
087     *   // Process the search result entries because they represent entries that
088     *   // have been created or modified.
089     *   for (final SearchResultEntry updatedEntry :
090     *        searchResult.getSearchEntries())
091     *   {
092     *     // Do something with the entry.
093     *   }
094     *
095     *   // If the client might want to continue the search even after shutting
096     *   // down and starting back up later, then persist the cookie now.
097     * }
098     * </PRE>
099     */
100    @NotMutable()
101    @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
102    public final class ActiveDirectoryDirSyncControl
103           extends Control
104           implements DecodeableControl
105    {
106      /**
107       * The OID (1.2.840.113556.1.4.841) for the DirSync control.
108       */
109      public static final String DIRSYNC_OID = "1.2.840.113556.1.4.841";
110    
111    
112    
113      /**
114       * The value of the flag that indicates that the client should only be allowed
115       * to view objects and attributes that are otherwise accessible to the client.
116       */
117      public static final int FLAG_OBJECT_SECURITY = 0x00000001;
118    
119    
120    
121      /**
122       * The value of the flag that indicates the server should return parent
123       * objects before child objects.
124       */
125      public static final int FLAG_ANCESTORS_FIRST_ORDER = 0x00000800;
126    
127    
128    
129      /**
130       * The value of the flag that indicates that the server should not return
131       * private data in search results.
132       */
133      public static final int FLAG_PUBLIC_DATA_ONLY = 0x00002000;
134    
135    
136    
137      /**
138       * The value of the flag that indicates that only changed values of attributes
139       * should be included in search results.
140       */
141      public static final int FLAG_INCREMENTAL_VALUES = 0x80000000;
142    
143    
144    
145      /**
146       * The serial version UID for this serializable class.
147       */
148      private static final long serialVersionUID = -2871267685237800654L;
149    
150    
151    
152      // A cookie that may be used to resume a previous DirSync search.
153      private final ASN1OctetString cookie;
154    
155      // The value of the flags that should be used for DirSync operation.
156      private final int flags;
157    
158      // The maximum number of attributes to return.
159      private final int maxAttributeCount;
160    
161    
162    
163      /**
164       * Creates a new empty control instance that is intended to be used only for
165       * decoding controls via the {@code DecodeableControl} interface.
166       */
167      ActiveDirectoryDirSyncControl()
168      {
169        this(true, 0, 0, null);
170      }
171    
172    
173    
174      /**
175       * Creates a new DirSync control with the provided information.
176       *
177       * @param  isCritical         Indicates whether this control should be marked
178       *                            critical.
179       * @param  flags              The value of the flags that should be used for
180       *                            DirSync operation.  This should be zero if no
181       *                            special flags or needed, or a bitwise OR of the
182       *                            values of the individual flags that are desired.
183       * @param  maxAttributeCount  The maximum number of attributes to return.
184       * @param  cookie             A cookie that may be used to resume a previous
185       *                            DirSync search.  This may be {@code null} if
186       *                            no previous cookie is available.
187       */
188      public ActiveDirectoryDirSyncControl(final boolean isCritical,
189                                           final int flags,
190                                           final int maxAttributeCount,
191                                           final ASN1OctetString cookie)
192      {
193        super(DIRSYNC_OID, isCritical,
194             encodeValue(flags, maxAttributeCount, cookie));
195    
196        this.flags = flags;
197        this.maxAttributeCount = maxAttributeCount;
198    
199        if (cookie == null)
200        {
201          this.cookie = new ASN1OctetString();
202        }
203        else
204        {
205          this.cookie = cookie;
206        }
207      }
208    
209    
210    
211      /**
212       * Creates a new DirSync control with settings decoded from the provided
213       * control information.
214       *
215       * @param  oid         The OID of the control to be decoded.
216       * @param  isCritical  The criticality of the control to be decoded.
217       * @param  value       The value of the control to be decoded.
218       *
219       * @throws LDAPException  If a problem is encountered while attempting to
220       *                         decode the control value as appropriate for a
221       *                         DirSync control.
222       */
223      public ActiveDirectoryDirSyncControl(final String oid,
224                                           final boolean isCritical,
225                                           final ASN1OctetString value)
226           throws LDAPException
227      {
228        super(oid, isCritical, value);
229    
230        if (value == null)
231        {
232          throw new LDAPException(ResultCode.DECODING_ERROR,
233               ERR_DIRSYNC_CONTROL_NO_VALUE.get());
234        }
235    
236        try
237        {
238          final ASN1Element[] elements =
239               ASN1Sequence.decodeAsSequence(value.getValue()).elements();
240          flags = ASN1Integer.decodeAsInteger(elements[0]).intValue();
241          maxAttributeCount = ASN1Integer.decodeAsInteger(elements[1]).intValue();
242          cookie = ASN1OctetString.decodeAsOctetString(elements[2]);
243        }
244        catch (final Exception e)
245        {
246          Debug.debugException(e);
247    
248          throw new LDAPException(ResultCode.DECODING_ERROR,
249               ERR_DIRSYNC_CONTROL_DECODE_ERROR.get(
250                    StaticUtils.getExceptionMessage(e)),
251               e);
252        }
253      }
254    
255    
256    
257      /**
258       * Encodes the provided information into a format appropriate for use as the
259       * value of a DirSync control.
260       *
261       * @param  flags              The value of the flags that should be used for
262       *                            DirSync operation.  This should be zero if no
263       *                            special flags or needed, or a bitwise OR of the
264       *                            values of the individual flags that are desired.
265       * @param  maxAttributeCount  The maximum number of attributes to return.
266       * @param  cookie             A cookie that may be used to resume a previous
267       *                            DirSync search.  This may be {@code null} if
268       *                            no previous cookie is available.
269       *
270       * @return  An ASN.1 octet string containing the encoded control value.
271       */
272      private static ASN1OctetString encodeValue(final int flags,
273                                                 final int maxAttributeCount,
274                                                 final ASN1OctetString cookie)
275      {
276        final ASN1Element[] valueElements = new ASN1Element[3];
277        valueElements[0] = new ASN1Integer(flags);
278        valueElements[1] = new ASN1Integer(maxAttributeCount);
279    
280        if (cookie == null)
281        {
282          valueElements[2] = new ASN1OctetString();
283        }
284        else
285        {
286          valueElements[2] = cookie;
287        }
288    
289        return new ASN1OctetString(new ASN1Sequence(valueElements).encode());
290      }
291    
292    
293    
294      /**
295       * {@inheritDoc}
296       */
297      public ActiveDirectoryDirSyncControl decodeControl(final String oid,
298                                                final boolean isCritical,
299                                                final ASN1OctetString value)
300              throws LDAPException
301      {
302        return new ActiveDirectoryDirSyncControl(oid, isCritical, value);
303      }
304    
305    
306    
307    
308      /**
309       * Retrieves the value of the flags that should be used for DirSync operation.
310       *
311       * @return  The value of the flags that should be used for DirSync operation.
312       */
313      public int getFlags()
314      {
315        return flags;
316      }
317    
318    
319    
320      /**
321       * Retrieves the maximum number of attributes to return.
322       *
323       * @return  The maximum number of attributes to return.
324       */
325      public int getMaxAttributeCount()
326      {
327        return maxAttributeCount;
328      }
329    
330    
331    
332      /**
333       * Retrieves a cookie that may be used to resume a previous DirSync search,
334       * if available.
335       *
336       * @return  A cookie that may be used to resume a previous DirSync search, or
337       *          a zero-length cookie if there is none.
338       */
339      public ASN1OctetString getCookie()
340      {
341        return cookie;
342      }
343    
344    
345    
346      /**
347       * Extracts a DirSync response control from the provided result.
348       *
349       * @param  result  The result from which to retrieve the DirSync response
350       *                 control.
351       *
352       * @return  The DirSync response control contained in the provided result, or
353       *          {@code null} if the result did not include a DirSync response
354       *          control.
355       *
356       * @throws  LDAPException  If a problem is encountered while attempting to
357       *                         decode the DirSync response control contained in
358       *                         the provided result.
359       */
360      public static ActiveDirectoryDirSyncControl get(final SearchResult result)
361             throws LDAPException
362      {
363        final Control c = result.getResponseControl(DIRSYNC_OID);
364        if (c == null)
365        {
366          return null;
367        }
368    
369        if (c instanceof ActiveDirectoryDirSyncControl)
370        {
371          return (ActiveDirectoryDirSyncControl) c;
372        }
373        else
374        {
375          return new ActiveDirectoryDirSyncControl(c.getOID(), c.isCritical(),
376               c.getValue());
377        }
378      }
379    
380    
381    
382      /**
383       * {@inheritDoc}
384       */
385      @Override()
386      public String getControlName()
387      {
388        return INFO_CONTROL_NAME_DIRSYNC.get();
389      }
390    
391    
392    
393      /**
394       * {@inheritDoc}
395       */
396      @Override()
397      public void toString(final StringBuilder buffer)
398      {
399        buffer.append("ActiveDirectoryDirSyncControl(isCritical=");
400        buffer.append(isCritical());
401        buffer.append(", flags=");
402        buffer.append(flags);
403        buffer.append(", maxAttributeCount=");
404        buffer.append(maxAttributeCount);
405        buffer.append(", cookie=byte[");
406        buffer.append(cookie.getValueLength());
407        buffer.append("])");
408      }
409    }