1<?php
2/**
3 * This file is part of the FreeDSx LDAP package.
4 *
5 * (c) Chad Sikorra <Chad.Sikorra@gmail.com>
6 *
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
9 */
10
11namespace FreeDSx\Ldap\Control\Ad;
12
13use FreeDSx\Asn1\Asn1;
14use FreeDSx\Asn1\Type\AbstractType;
15use FreeDSx\Asn1\Type\IntegerType;
16use FreeDSx\Asn1\Type\OctetStringType;
17use FreeDSx\Asn1\Type\SequenceType;
18use FreeDSx\Ldap\Control\Control;
19use FreeDSx\Ldap\Exception\ProtocolException;
20
21/**
22 * Represents a DirSync Request. Defined in MS-ADTS 3.1.1.3.4.1.3. The control value request definition is:
23 *
24 *  DirSyncRequestValue ::= SEQUENCE {
25 *      Flags       INTEGER
26 *      MaxBytes    INTEGER
27 *      Cookie      OCTET STRING
28 *  }
29 *
30 * @see https://msdn.microsoft.com/en-us/library/cc223347.aspx
31 * @author Chad Sikorra <Chad.Sikorra@gmail.com>
32 */
33class DirSyncRequestControl extends Control
34{
35    /**
36     * If this flag is present, the client can only view objects and attributes that are otherwise accessible to the client.
37     * If this flag is not present, the server checks if the client has access rights to read the changes in the NC.
38     */
39    public const FLAG_OBJECT_SECURITY = 0x00000001;
40
41    /**
42     * The server returns parent objects before child objects.
43     */
44    public const FLAG_ANCESTORS_FIRST_ORDER = 0x00000800;
45
46    /**
47     * This flag can optionally be passed to the DC, but it has no effect.
48     */
49    public const FLAG_PUBLIC_DATA_ONLY = 0x00002000;
50
51    /**
52     * If this flag is not present, all of the values, up to a server-specified limit, in a multivalued attribute are
53     * returned when any value changes. If this flag is present, only the changed values are returned, provided the
54     * attribute is a forward link value.
55     *
56     * Note: This flag needs to be encoded as a negative, due to how AD interprets the flags value.
57     */
58    public const FLAG_INCREMENTAL_VALUES = -0x80000000;
59
60    /**
61     * @var int
62     */
63    protected $flags;
64
65    /**
66     * @var int
67     */
68    protected $maxBytes;
69
70    /**
71     * @var string
72     */
73    protected $cookie;
74
75    /**
76     * @param int $flags
77     * @param int $maxBytes
78     * @param string $cookie
79     */
80    public function __construct(int $flags = self::FLAG_INCREMENTAL_VALUES, string $cookie = '', int $maxBytes = 2147483647)
81    {
82        $this->flags = $flags;
83        $this->maxBytes = $maxBytes;
84        $this->cookie = $cookie;
85        parent::__construct(self::OID_DIR_SYNC, true);
86    }
87
88    /**
89     * @return int
90     */
91    public function getFlags(): int
92    {
93        return $this->flags;
94    }
95
96    /**
97     * @param int $flags
98     * @return $this
99     */
100    public function setFlags(int $flags)
101    {
102        $this->flags = $flags;
103
104        return $this;
105    }
106
107    /**
108     * @return int
109     */
110    public function getMaxBytes(): int
111    {
112        return $this->maxBytes;
113    }
114
115    /**
116     * @param int $maxBytes
117     * @return $this
118     */
119    public function setMaxBytes(int $maxBytes)
120    {
121        $this->maxBytes = $maxBytes;
122
123        return $this;
124    }
125
126    /**
127     * @return string
128     */
129    public function getCookie(): string
130    {
131        return $this->cookie;
132    }
133
134    /**
135     * @param string $cookie
136     * @return $this
137     */
138    public function setCookie(string $cookie)
139    {
140        $this->cookie = $cookie;
141
142        return $this;
143    }
144
145    /**
146     * {@inheritdoc}
147     */
148    public static function fromAsn1(AbstractType $type)
149    {
150        $request = self::decodeEncodedValue($type);
151        if (!$request instanceof SequenceType) {
152            throw new ProtocolException('A DirSyncRequest control value must be a sequence type with 3 children.');
153        }
154        $flags = $request->getChild(0);
155        $cookie = $request->getChild(2);
156        $maxBytes = $request->getChild(1);
157        if (!$flags instanceof IntegerType) {
158            throw new ProtocolException('A DirSyncRequest control value sequence 0 must be an integer type.');
159        }
160        if (!$maxBytes instanceof IntegerType) {
161            throw new ProtocolException('A DirSyncRequest control value sequence 1 must be an integer type.');
162        }
163        if (!$cookie instanceof OctetStringType) {
164            throw new ProtocolException('A DirSyncRequest control value sequence 2 must be an octet string type.');
165        }
166
167        /** @var SequenceType $request */
168        $control = new self(
169            $flags->getValue(),
170            $cookie->getValue(),
171            $maxBytes->getValue()
172        );
173
174        return self::mergeControlData($control, $type);
175    }
176
177    /**
178     * {@inheritdoc}
179     */
180    public function toAsn1(): AbstractType
181    {
182        $this->controlValue = Asn1::sequence(
183            Asn1::integer($this->flags),
184            Asn1::integer($this->maxBytes),
185            Asn1::octetString($this->cookie)
186        );
187
188        return parent::toAsn1();
189    }
190}
191