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