/* See LICENSE for licensing and NOTICE for copyright. */
package org.ldaptive.ad.control;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import org.ldaptive.LdapUtils;
import org.ldaptive.asn1.AbstractParseHandler;
import org.ldaptive.asn1.ConstructedDEREncoder;
import org.ldaptive.asn1.DERParser;
import org.ldaptive.asn1.DERPath;
import org.ldaptive.asn1.IntegerType;
import org.ldaptive.asn1.OctetStringType;
import org.ldaptive.asn1.UniversalDERTag;
import org.ldaptive.control.AbstractControl;
import org.ldaptive.control.RequestControl;
import org.ldaptive.control.ResponseControl;
/**
* Request/response control for active directory synchronization. Control is defined as:
*
* <pre>
dirSyncValue ::= SEQUENCE {
flags INTEGER,
maxAttributeCount INTEGER,
cookie OCTET STRING
}
* </pre>
*
* <p>See http://msdn.microsoft.com/en-us/library/cc223347.aspx</p>
*
* @author Middleware Services
*/
public class DirSyncControl extends AbstractControl implements RequestControl, ResponseControl
{
/** OID of this control. */
public static final String OID = "1.2.840.113556.1.4.841";
/** hash value seed. */
private static final int HASH_CODE_SEED = 907;
/** Empty byte array used for null cookies. */
private static final byte[] EMPTY_COOKIE = new byte[0];
/** Types of flags. */
public enum Flag {
/** object security. */
OBJECT_SECURITY(1L),
/** ancestors first order. */
ANCESTORS_FIRST_ORDER(2048L),
/** public data only. */
PUBLIC_DATA_ONLY(8192L),
/** incremental values. */
INCREMENTAL_VALUES(2147483648L);
/** underlying value. */
private final long value;
/**
* Creates a new flag.
*
* @param l value
*/
Flag(final long l)
{
value = l;
}
/**
* Returns the value.
*
* @return enum value
*/
public long value()
{
return value;
}
/**
* Returns the flag for the supplied integer constant.
*
* @param l to find flag for
*
* @return flag
*/
public static Flag valueOf(final long l)
{
for (Flag f : Flag.values()) {
if (f.value() == l) {
return f;
}
}
return null;
}
}
/** flags. */
private long flags;
/** maximum attribute count. */
private int maxAttributeCount;
/** server generated cookie. */
private byte[] cookie;
/** Default constructor. */
public DirSyncControl()
{
super(OID);
}
/**
* Creates a new dir sync control.
*
* @param critical whether this control is critical
*/
public DirSyncControl(final boolean critical)
{
this(null, null, critical);
}
/**
* Creates a new dir sync control.
*
* @param f request flags
*/
public DirSyncControl(final Flag[] f)
{
this(f, false);
}
/**
* Creates a new dir sync control.
*
* @param f request flags
* @param critical whether this control is critical
*/
public DirSyncControl(final Flag[] f, final boolean critical)
{
this(f, null, critical);
}
/**
* Creates a new dir sync control.
*
* @param f request flags
* @param count maximum attribute count
*/
public DirSyncControl(final Flag[] f, final int count)
{
this(f, null, count, false);
}
/**
* Creates a new dir sync control.
*
* @param f request flags
* @param count maximum attribute count
* @param critical whether this control is critical
*/
public DirSyncControl(final Flag[] f, final int count, final boolean critical)
{
this(f, null, count, critical);
}
/**
* Creates a new dir sync control.
*
* @param f request flags
* @param value dir sync cookie
* @param critical whether this control is critical
*/
public DirSyncControl(final Flag[] f, final byte[] value, final boolean critical)
{
this(f, value, 0, critical);
}
/**
* Creates a new dir sync control.
*
* @param f request flags
* @param value dir sync cookie
* @param count maximum attribute count
* @param critical whether this control is critical
*/
public DirSyncControl(final Flag[] f, final byte[] value, final int count, final boolean critical)
{
super(OID, critical);
if (f != null) {
long l = 0;
for (Flag flag : f) {
if (flag != null) {
l += flag.value();
}
}
setFlags(l);
}
setCookie(value);
setMaxAttributeCount(count);
}
/**
* Returns the flags value.
*
* @return flags value
*/
public long getFlags()
{
return flags;
}
/**
* Sets the flags.
*
* @param l flags value
*/
public void setFlags(final long l)
{
flags = l;
}
/**
* Returns the maximum attribute count.
*
* @return maximum attribute count
*/
public int getMaxAttributeCount()
{
return maxAttributeCount;
}
/**
* Sets the maximum attribute count.
*
* @param count maximum attribute count
*/
public void setMaxAttributeCount(final int count)
{
maxAttributeCount = count;
}
/**
* Returns the sync request cookie.
*
* @return sync request cookie
*/
public byte[] getCookie()
{
return cookie;
}
/**
* Sets the sync request cookie.
*
* @param value sync request cookie
*/
public void setCookie(final byte[] value)
{
cookie = value;
}
@Override
public boolean equals(final Object o)
{
if (o == this) {
return true;
}
if (o instanceof DirSyncControl && super.equals(o)) {
final DirSyncControl v = (DirSyncControl) o;
return LdapUtils.areEqual(flags, v.flags) &&
LdapUtils.areEqual(maxAttributeCount, v.maxAttributeCount) &&
LdapUtils.areEqual(cookie, v.cookie);
}
return false;
}
@Override
public int hashCode()
{
return LdapUtils.computeHashCode(HASH_CODE_SEED, getOID(), getCriticality(), flags, maxAttributeCount, cookie);
}
@Override
public String toString()
{
return
String.format(
"[%s@%d::criticality=%s, flags=%s, maxAttributeCount=%s, cookie=%s]",
getClass().getName(),
hashCode(),
getCriticality(),
flags,
maxAttributeCount,
LdapUtils.base64Encode(cookie));
}
@Override
public byte[] encode()
{
final ConstructedDEREncoder se = new ConstructedDEREncoder(
UniversalDERTag.SEQ,
new IntegerType(BigInteger.valueOf(getFlags())),
new IntegerType(getMaxAttributeCount()),
new OctetStringType(getCookie() != null ? getCookie() : EMPTY_COOKIE));
return se.encode();
}
@Override
public void decode(final byte[] berValue)
{
logger.trace("decoding control: {}", LdapUtils.base64Encode(berValue));
final DERParser parser = new DERParser();
parser.registerHandler(FlagHandler.PATH, new FlagHandler(this));
parser.registerHandler(MaxAttrCountHandler.PATH, new MaxAttrCountHandler(this));
parser.registerHandler(CookieHandler.PATH, new CookieHandler(this));
parser.parse(ByteBuffer.wrap(berValue));
}
/** Parse handler implementation for the flag. */
private static class FlagHandler extends AbstractParseHandler<DirSyncControl>
{
/** DER path to flag. */
public static final DERPath PATH = new DERPath("/SEQ/INT[0]");
/**
* Creates a new flag handler.
*
* @param control to configure
*/
FlagHandler(final DirSyncControl control)
{
super(control);
}
@Override
public void handle(final DERParser parser, final ByteBuffer encoded)
{
getObject().setFlags(IntegerType.decode(encoded).longValue());
}
}
/** Parse handler implementation for the maxAttributeCount. */
private static class MaxAttrCountHandler extends AbstractParseHandler<DirSyncControl>
{
/** DER path to cookie value. */
public static final DERPath PATH = new DERPath("/SEQ/INT[1]");
/**
* Creates a new max attr handler handler.
*
* @param control to configure
*/
MaxAttrCountHandler(final DirSyncControl control)
{
super(control);
}
@Override
public void handle(final DERParser parser, final ByteBuffer encoded)
{
getObject().setMaxAttributeCount(IntegerType.decode(encoded).intValue());
}
}
/** Parse handler implementation for the cookie. */
private static class CookieHandler extends AbstractParseHandler<DirSyncControl>
{
/** DER path to cookie value. */
public static final DERPath PATH = new DERPath("/SEQ/OCTSTR");
/**
* Creates a new cookie handler.
*
* @param control to configure
*/
CookieHandler(final DirSyncControl control)
{
super(control);
}
@Override
public void handle(final DERParser parser, final ByteBuffer encoded)
{
final byte[] cookie = OctetStringType.readBuffer(encoded);
if (cookie != null && cookie.length > 0) {
getObject().setCookie(cookie);
}
}
}
}