blob: 4eac74b3be3366d2727897cb165cf660d39ec9aa [file] [log] [blame]
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001<?php
2
3namespace Adldap\Models\Attributes;
4
5use InvalidArgumentException;
6
7class TSPropertyArray
8{
9 /**
10 * Represents that the TSPropertyArray data is valid.
11 */
12 const VALID_SIGNATURE = 'P';
13
14 /**
15 * The default values for the TSPropertyArray structure.
16 *
17 * @var array
18 */
19 const DEFAULTS = [
20 'CtxCfgPresent' => 2953518677,
21 'CtxWFProfilePath' => '',
22 'CtxWFProfilePathW' => '',
23 'CtxWFHomeDir' => '',
24 'CtxWFHomeDirW' => '',
25 'CtxWFHomeDirDrive' => '',
26 'CtxWFHomeDirDriveW' => '',
27 'CtxShadow' => 1,
28 'CtxMaxDisconnectionTime' => 0,
29 'CtxMaxConnectionTime' => 0,
30 'CtxMaxIdleTime' => 0,
31 'CtxWorkDirectory' => '',
32 'CtxWorkDirectoryW' => '',
33 'CtxCfgFlags1' => 2418077696,
34 'CtxInitialProgram' => '',
35 'CtxInitialProgramW' => '',
36 ];
37
38 /**
39 * @var string The default data that occurs before the TSPropertyArray (CtxCfgPresent with a bunch of spaces...?)
40 */
41 protected $defaultPreBinary = '43747843666750726573656e742020202020202020202020202020202020202020202020202020202020202020202020';
42
43 /**
44 * @var TSProperty[]
45 */
46 protected $tsProperty = [];
47
48 /**
49 * @var string
50 */
51 protected $signature = self::VALID_SIGNATURE;
52
53 /**
54 * Binary data that occurs before the TSPropertyArray data in userParameters.
55 *
56 * @var string
57 */
58 protected $preBinary = '';
59
60 /**
61 * Binary data that occurs after the TSPropertyArray data in userParameters.
62 *
63 * @var string
64 */
65 protected $postBinary = '';
66
67 /**
68 * Construct in one of the following ways:.
69 *
70 * - Pass an array of TSProperty key => value pairs (See DEFAULTS constant).
71 * - Pass the userParameters binary value. The object representation of that will be decoded and constructed.
72 * - Pass nothing and a default set of TSProperty key => value pairs will be used (See DEFAULTS constant).
73 *
74 * @param mixed $tsPropertyArray
75 */
76 public function __construct($tsPropertyArray = null)
77 {
78 $this->preBinary = hex2bin($this->defaultPreBinary);
79
80 if (is_null($tsPropertyArray) || is_array($tsPropertyArray)) {
81 $tsPropertyArray = $tsPropertyArray ?: self::DEFAULTS;
82
83 foreach ($tsPropertyArray as $key => $value) {
84 $tsProperty = new TSProperty();
85
86 $this->tsProperty[$key] = $tsProperty->setName($key)->setValue($value);
87 }
88 } else {
89 $this->decodeUserParameters($tsPropertyArray);
90 }
91 }
92
93 /**
94 * Check if a specific TSProperty exists by its property name.
95 *
96 * @param string $propName
97 *
98 * @return bool
99 */
100 public function has($propName)
101 {
102 return array_key_exists(strtolower($propName), array_change_key_case($this->tsProperty));
103 }
104
105 /**
106 * Get a TSProperty object by its property name (ie. CtxWFProfilePath).
107 *
108 * @param string $propName
109 *
110 * @return TSProperty
111 */
112 public function get($propName)
113 {
114 $this->validateProp($propName);
115
116 return $this->getTsPropObj($propName);
117 }
118
119 /**
120 * Add a TSProperty object. If it already exists, it will be overwritten.
121 *
122 * @param TSProperty $tsProperty
123 *
124 * @return $this
125 */
126 public function add(TSProperty $tsProperty)
127 {
128 $this->tsProperty[$tsProperty->getName()] = $tsProperty;
129
130 return $this;
131 }
132
133 /**
134 * Remove a TSProperty by its property name (ie. CtxMinEncryptionLevel).
135 *
136 * @param string $propName
137 *
138 * @return $this
139 */
140 public function remove($propName)
141 {
142 foreach (array_keys($this->tsProperty) as $property) {
143 if (strtolower($propName) == strtolower($property)) {
144 unset($this->tsProperty[$property]);
145 }
146 }
147
148 return $this;
149 }
150
151 /**
152 * Set the value for a specific TSProperty by its name.
153 *
154 * @param string $propName
155 * @param mixed $propValue
156 *
157 * @return $this
158 */
159 public function set($propName, $propValue)
160 {
161 $this->validateProp($propName);
162
163 $this->getTsPropObj($propName)->setValue($propValue);
164
165 return $this;
166 }
167
168 /**
169 * Get the full binary representation of the userParameters containing the TSPropertyArray data.
170 *
171 * @return string
172 */
173 public function toBinary()
174 {
175 $binary = $this->preBinary;
176
177 $binary .= hex2bin(str_pad(dechex(MbString::ord($this->signature)), 2, 0, STR_PAD_LEFT));
178
179 $binary .= hex2bin(str_pad(dechex(count($this->tsProperty)), 2, 0, STR_PAD_LEFT));
180
181 foreach ($this->tsProperty as $tsProperty) {
182 $binary .= $tsProperty->toBinary();
183 }
184
185 return $binary.$this->postBinary;
186 }
187
188 /**
189 * Get a simple associative array containing of all TSProperty names and values.
190 *
191 * @return array
192 */
193 public function toArray()
194 {
195 $userParameters = [];
196
197 foreach ($this->tsProperty as $property => $tsPropObj) {
198 $userParameters[$property] = $tsPropObj->getValue();
199 }
200
201 return $userParameters;
202 }
203
204 /**
205 * Get all TSProperty objects.
206 *
207 * @return TSProperty[]
208 */
209 public function getTSProperties()
210 {
211 return $this->tsProperty;
212 }
213
214 /**
215 * Validates that the given property name exists.
216 *
217 * @param string $propName
218 */
219 protected function validateProp($propName)
220 {
221 if (!$this->has($propName)) {
222 throw new InvalidArgumentException(sprintf('TSProperty for "%s" does not exist.', $propName));
223 }
224 }
225
226 /**
227 * @param string $propName
228 *
229 * @return TSProperty
230 */
231 protected function getTsPropObj($propName)
232 {
233 return array_change_key_case($this->tsProperty)[strtolower($propName)];
234 }
235
236 /**
237 * Get an associative array with all of the userParameters property names and values.
238 *
239 * @param string $userParameters
240 *
241 * @return void
242 */
243 protected function decodeUserParameters($userParameters)
244 {
245 $userParameters = bin2hex($userParameters);
246
247 // Save the 96-byte array of reserved data, so as to not ruin anything that may be stored there.
248 $this->preBinary = hex2bin(substr($userParameters, 0, 96));
249 // The signature is a 2-byte unicode character at the front
250 $this->signature = MbString::chr(hexdec(substr($userParameters, 96, 2)));
251 // This asserts the validity of the tsPropertyArray data. For some reason 'P' means valid...
252 if ($this->signature != self::VALID_SIGNATURE) {
253 throw new InvalidArgumentException('Invalid TSPropertyArray data');
254 }
255
256 // The property count is a 2-byte unsigned integer indicating the number of elements for the tsPropertyArray
257 // It starts at position 98. The actual variable data begins at position 100.
258 $length = $this->addTSPropData(substr($userParameters, 100), hexdec(substr($userParameters, 98, 2)));
259
260 // Reserved data length + (count and sig length == 4) + the added lengths of the TSPropertyArray
261 // This saves anything after that variable TSPropertyArray data, so as to not squash anything stored there
262 if (strlen($userParameters) > (96 + 4 + $length)) {
263 $this->postBinary = hex2bin(substr($userParameters, (96 + 4 + $length)));
264 }
265 }
266
267 /**
268 * Given the start of TSPropertyArray hex data, and the count for the number
269 * of TSProperty structures in contains, parse and split out the
270 * individual TSProperty structures. Return the full length
271 * of the TSPropertyArray data.
272 *
273 * @param string $tsPropertyArray
274 * @param int $tsPropCount
275 *
276 * @return int The length of the data in the TSPropertyArray
277 */
278 protected function addTSPropData($tsPropertyArray, $tsPropCount)
279 {
280 $length = 0;
281
282 for ($i = 0; $i < $tsPropCount; $i++) {
283 // Prop length = name length + value length + type length + the space for the length data.
284 $propLength = hexdec(substr($tsPropertyArray, $length, 2)) + (hexdec(substr($tsPropertyArray, $length + 2, 2)) * 3) + 6;
285
286 $tsProperty = new TSProperty(hex2bin(substr($tsPropertyArray, $length, $propLength)));
287
288 $this->tsProperty[$tsProperty->getName()] = $tsProperty;
289
290 $length += $propLength;
291 }
292
293 return $length;
294 }
295}