|
1 <?php |
|
2 /** |
|
3 * Zend Framework |
|
4 * |
|
5 * LICENSE |
|
6 * |
|
7 * This source file is subject to the new BSD license that is bundled |
|
8 * with this package in the file LICENSE.txt. |
|
9 * It is also available through the world-wide-web at this URL: |
|
10 * http://framework.zend.com/license/new-bsd |
|
11 * If you did not receive a copy of the license and are unable to |
|
12 * obtain it through the world-wide-web, please send an email |
|
13 * to license@zend.com so we can send you a copy immediately. |
|
14 * |
|
15 * @category Zend |
|
16 * @package Zend_Ldap |
|
17 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
18 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
19 * @version $Id: Converter.php 22996 2010-09-22 17:01:46Z sgehrig $ |
|
20 */ |
|
21 |
|
22 /** |
|
23 * Zend_Ldap_Converter is a collection of useful LDAP related conversion functions. |
|
24 * |
|
25 * @category Zend |
|
26 * @package Zend_Ldap |
|
27 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
28 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
29 */ |
|
30 class Zend_Ldap_Converter |
|
31 { |
|
32 const STANDARD = 0; |
|
33 const BOOLEAN = 1; |
|
34 const GENERALIZED_TIME = 2; |
|
35 |
|
36 /** |
|
37 * Converts all ASCII chars < 32 to "\HEX" |
|
38 * |
|
39 * @see Net_LDAP2_Util::asc2hex32() from Benedikt Hallinger <beni@php.net> |
|
40 * @link http://pear.php.net/package/Net_LDAP2 |
|
41 * @author Benedikt Hallinger <beni@php.net> |
|
42 * |
|
43 * @param string $string String to convert |
|
44 * @return string |
|
45 */ |
|
46 public static function ascToHex32($string) |
|
47 { |
|
48 for ($i = 0; $i<strlen($string); $i++) { |
|
49 $char = substr($string, $i, 1); |
|
50 if (ord($char)<32) { |
|
51 $hex = dechex(ord($char)); |
|
52 if (strlen($hex) == 1) $hex = '0' . $hex; |
|
53 $string = str_replace($char, '\\' . $hex, $string); |
|
54 } |
|
55 } |
|
56 return $string; |
|
57 } |
|
58 |
|
59 /** |
|
60 * Converts all Hex expressions ("\HEX") to their original ASCII characters |
|
61 * |
|
62 * @see Net_LDAP2_Util::hex2asc() from Benedikt Hallinger <beni@php.net>, |
|
63 * heavily based on work from DavidSmith@byu.net |
|
64 * @link http://pear.php.net/package/Net_LDAP2 |
|
65 * @author Benedikt Hallinger <beni@php.net>, heavily based on work from DavidSmith@byu.net |
|
66 * |
|
67 * @param string $string String to convert |
|
68 * @return string |
|
69 */ |
|
70 public static function hex32ToAsc($string) |
|
71 { |
|
72 $string = preg_replace("/\\\([0-9A-Fa-f]{2})/e", "''.chr(hexdec('\\1')).''", $string); |
|
73 return $string; |
|
74 } |
|
75 |
|
76 /** |
|
77 * Convert any value to an LDAP-compatible value. |
|
78 * |
|
79 * By setting the <var>$type</var>-parameter the conversion of a certain |
|
80 * type can be forced |
|
81 * |
|
82 * @todo write more tests |
|
83 * |
|
84 * @param mixed $value The value to convert |
|
85 * @param int $ytpe The conversion type to use |
|
86 * @return string |
|
87 * @throws Zend_Ldap_Converter_Exception |
|
88 */ |
|
89 public static function toLdap($value, $type = self::STANDARD) |
|
90 { |
|
91 try { |
|
92 switch ($type) { |
|
93 case self::BOOLEAN: |
|
94 return self::toldapBoolean($value); |
|
95 break; |
|
96 case self::GENERALIZED_TIME: |
|
97 return self::toLdapDatetime($value); |
|
98 break; |
|
99 default: |
|
100 if (is_string($value)) { |
|
101 return $value; |
|
102 } else if (is_int($value) || is_float($value)) { |
|
103 return (string)$value; |
|
104 } else if (is_bool($value)) { |
|
105 return self::toldapBoolean($value); |
|
106 } else if (is_object($value)) { |
|
107 if ($value instanceof DateTime) { |
|
108 return self::toLdapDatetime($value); |
|
109 } else if ($value instanceof Zend_Date) { |
|
110 return self::toLdapDatetime($value); |
|
111 } else { |
|
112 return self::toLdapSerialize($value); |
|
113 } |
|
114 } else if (is_array($value)) { |
|
115 return self::toLdapSerialize($value); |
|
116 } else if (is_resource($value) && get_resource_type($value) === 'stream') { |
|
117 return stream_get_contents($value); |
|
118 } else { |
|
119 return null; |
|
120 } |
|
121 break; |
|
122 } |
|
123 } catch (Exception $e) { |
|
124 throw new Zend_Ldap_Converter_Exception($e->getMessage(), $e->getCode(), $e); |
|
125 } |
|
126 } |
|
127 |
|
128 /** |
|
129 * Converts a date-entity to an LDAP-compatible date-string |
|
130 * |
|
131 * The date-entity <var>$date</var> can be either a timestamp, a |
|
132 * DateTime Object, a string that is parseable by strtotime() or a Zend_Date |
|
133 * Object. |
|
134 * |
|
135 * @param integer|string|DateTimt|Zend_Date $date The date-entity |
|
136 * @param boolean $asUtc Whether to return the LDAP-compatible date-string |
|
137 * as UTC or as local value |
|
138 * @return string |
|
139 * @throws InvalidArgumentException |
|
140 */ |
|
141 public static function toLdapDateTime($date, $asUtc = true) |
|
142 { |
|
143 if (!($date instanceof DateTime)) { |
|
144 if (is_int($date)) { |
|
145 $date = new DateTime('@' . $date); |
|
146 $date->setTimezone(new DateTimeZone(date_default_timezone_get())); |
|
147 } else if (is_string($date)) { |
|
148 $date = new DateTime($date); |
|
149 } else if ($date instanceof Zend_Date) { |
|
150 $date = new DateTime($date->get(Zend_Date::ISO_8601)); |
|
151 } else { |
|
152 throw new InvalidArgumentException('Parameter $date is not of the expected type'); |
|
153 } |
|
154 } |
|
155 $timezone = $date->format('O'); |
|
156 if (true === $asUtc) { |
|
157 $date->setTimezone(new DateTimeZone('UTC')); |
|
158 $timezone = 'Z'; |
|
159 } |
|
160 if ( '+0000' === $timezone ) { |
|
161 $timezone = 'Z'; |
|
162 } |
|
163 return $date->format('YmdHis') . $timezone; |
|
164 } |
|
165 |
|
166 /** |
|
167 * Convert a boolean value to an LDAP-compatible string |
|
168 * |
|
169 * This converts a boolean value of TRUE, an integer-value of 1 and a |
|
170 * case-insensitive string 'true' to an LDAP-compatible 'TRUE'. All other |
|
171 * other values are converted to an LDAP-compatible 'FALSE'. |
|
172 * |
|
173 * @param boolean|integer|string $value The boolean value to encode |
|
174 * @return string |
|
175 */ |
|
176 public static function toLdapBoolean($value) |
|
177 { |
|
178 $return = 'FALSE'; |
|
179 if (!is_scalar($value)) { |
|
180 return $return; |
|
181 } |
|
182 if (true === $value || 'true' === strtolower($value) || 1 === $value) { |
|
183 $return = 'TRUE'; |
|
184 } |
|
185 return $return; |
|
186 } |
|
187 |
|
188 /** |
|
189 * Serialize any value for storage in LDAP |
|
190 * |
|
191 * @param mixed $value The value to serialize |
|
192 * @return string |
|
193 */ |
|
194 public static function toLdapSerialize($value) |
|
195 { |
|
196 return serialize($value); |
|
197 } |
|
198 |
|
199 /** |
|
200 * Convert an LDAP-compatible value to a corresponding PHP-value. |
|
201 * |
|
202 * By setting the <var>$type</var>-parameter the conversion of a certain |
|
203 * type can be forced |
|
204 * . |
|
205 * @param string $value The value to convert |
|
206 * @param int $ytpe The conversion type to use |
|
207 * @param boolean $dateTimeAsUtc Return DateTime values in UTC timezone |
|
208 * @return mixed |
|
209 * @throws Zend_Ldap_Converter_Exception |
|
210 */ |
|
211 public static function fromLdap($value, $type = self::STANDARD, $dateTimeAsUtc = true) |
|
212 { |
|
213 switch ($type) { |
|
214 case self::BOOLEAN: |
|
215 return self::fromldapBoolean($value); |
|
216 break; |
|
217 case self::GENERALIZED_TIME: |
|
218 return self::fromLdapDateTime($value); |
|
219 break; |
|
220 default: |
|
221 if (is_numeric($value)) { |
|
222 return (float)$value; |
|
223 } else if ('TRUE' === $value || 'FALSE' === $value) { |
|
224 return self::fromLdapBoolean($value); |
|
225 } |
|
226 if (preg_match('/^\d{4}[\d\+\-Z\.]*$/', $value)) { |
|
227 return self::fromLdapDateTime($value, $dateTimeAsUtc); |
|
228 } |
|
229 try { |
|
230 return self::fromLdapUnserialize($value); |
|
231 } catch (UnexpectedValueException $e) { } |
|
232 break; |
|
233 } |
|
234 return $value; |
|
235 } |
|
236 |
|
237 /** |
|
238 * Convert an LDAP-Generalized-Time-entry into a DateTime-Object |
|
239 * |
|
240 * CAVEAT: The DateTime-Object returned will alwasy be set to UTC-Timezone. |
|
241 * |
|
242 * @param string $date The generalized-Time |
|
243 * @param boolean $asUtc Return the DateTime with UTC timezone |
|
244 * @return DateTime |
|
245 * @throws InvalidArgumentException if a non-parseable-format is given |
|
246 */ |
|
247 public static function fromLdapDateTime($date, $asUtc = true) |
|
248 { |
|
249 $datepart = array (); |
|
250 if (!preg_match('/^(\d{4})/', $date, $datepart) ) { |
|
251 throw new InvalidArgumentException('Invalid date format found'); |
|
252 } |
|
253 |
|
254 if ($datepart[1] < 4) { |
|
255 throw new InvalidArgumentException('Invalid date format found (too short)'); |
|
256 } |
|
257 |
|
258 $time = array ( |
|
259 // The year is mandatory! |
|
260 'year' => $datepart[1], |
|
261 'month' => 1, |
|
262 'day' => 1, |
|
263 'hour' => 0, |
|
264 'minute' => 0, |
|
265 'second' => 0, |
|
266 'offdir' => '+', |
|
267 'offsethours' => 0, |
|
268 'offsetminutes' => 0 |
|
269 ); |
|
270 |
|
271 $length = strlen($date); |
|
272 |
|
273 // Check for month. |
|
274 if ($length >= 6) { |
|
275 $month = substr($date, 4, 2); |
|
276 if ($month < 1 || $month > 12) { |
|
277 throw new InvalidArgumentException('Invalid date format found (invalid month)'); |
|
278 } |
|
279 $time['month'] = $month; |
|
280 } |
|
281 |
|
282 // Check for day |
|
283 if ($length >= 8) { |
|
284 $day = substr($date, 6, 2); |
|
285 if ($day < 1 || $day > 31) { |
|
286 throw new InvalidArgumentException('Invalid date format found (invalid day)'); |
|
287 } |
|
288 $time['day'] = $day; |
|
289 } |
|
290 |
|
291 // Check for Hour |
|
292 if ($length >= 10) { |
|
293 $hour = substr($date, 8, 2); |
|
294 if ($hour < 0 || $hour > 23) { |
|
295 throw new InvalidArgumentException('Invalid date format found (invalid hour)'); |
|
296 } |
|
297 $time['hour'] = $hour; |
|
298 } |
|
299 |
|
300 // Check for minute |
|
301 if ($length >= 12) { |
|
302 $minute = substr($date, 10, 2); |
|
303 if ($minute < 0 || $minute > 59) { |
|
304 throw new InvalidArgumentException('Invalid date format found (invalid minute)'); |
|
305 } |
|
306 $time['minute'] = $minute; |
|
307 } |
|
308 |
|
309 // Check for seconds |
|
310 if ($length >= 14) { |
|
311 $second = substr($date, 12, 2); |
|
312 if ($second < 0 || $second > 59) { |
|
313 throw new InvalidArgumentException('Invalid date format found (invalid second)'); |
|
314 } |
|
315 $time['second'] = $second; |
|
316 } |
|
317 |
|
318 // Set Offset |
|
319 $offsetRegEx = '/([Z\-\+])(\d{2}\'?){0,1}(\d{2}\'?){0,1}$/'; |
|
320 $off = array (); |
|
321 if (preg_match($offsetRegEx, $date, $off)) { |
|
322 $offset = $off[1]; |
|
323 if ($offset == '+' || $offset == '-') { |
|
324 $time['offdir'] = $offset; |
|
325 // we have an offset, so lets calculate it. |
|
326 if (isset($off[2])) { |
|
327 $offsetHours = substr($off[2], 0, 2); |
|
328 if ($offsetHours < 0 || $offsetHours > 12) { |
|
329 throw new InvalidArgumentException('Invalid date format found (invalid offset hour)'); |
|
330 } |
|
331 $time['offsethours'] = $offsetHours; |
|
332 } |
|
333 if (isset($off[3])) { |
|
334 $offsetMinutes = substr($off[3], 0, 2); |
|
335 if ($offsetMinutes < 0 || $offsetMinutes > 59) { |
|
336 throw new InvalidArgumentException('Invalid date format found (invalid offset minute)'); |
|
337 } |
|
338 $time['offsetminutes'] = $offsetMinutes; |
|
339 } |
|
340 } |
|
341 } |
|
342 |
|
343 // Raw-Data is present, so lets create a DateTime-Object from it. |
|
344 $offset = $time['offdir'] |
|
345 . str_pad($time['offsethours'],2,'0',STR_PAD_LEFT) |
|
346 . str_pad($time['offsetminutes'],2,'0',STR_PAD_LEFT); |
|
347 $timestring = $time['year'] . '-' |
|
348 . str_pad($time['month'], 2, '0', STR_PAD_LEFT) . '-' |
|
349 . str_pad($time['day'], 2, '0', STR_PAD_LEFT) . ' ' |
|
350 . str_pad($time['hour'], 2, '0', STR_PAD_LEFT) . ':' |
|
351 . str_pad($time['minute'], 2, '0', STR_PAD_LEFT) . ':' |
|
352 . str_pad($time['second'], 2, '0', STR_PAD_LEFT) |
|
353 . $time['offdir'] |
|
354 . str_pad($time['offsethours'], 2, '0', STR_PAD_LEFT) |
|
355 . str_pad($time['offsetminutes'], 2, '0', STR_PAD_LEFT); |
|
356 $date = new DateTime($timestring); |
|
357 if ($asUtc) { |
|
358 $date->setTimezone(new DateTimeZone('UTC')); |
|
359 } |
|
360 return $date; |
|
361 } |
|
362 |
|
363 /** |
|
364 * Convert an LDAP-compatible boolean value into a PHP-compatible one |
|
365 * |
|
366 * @param string $value The value to convert |
|
367 * @return boolean |
|
368 * @throws InvalidArgumentException |
|
369 */ |
|
370 public static function fromLdapBoolean($value) |
|
371 { |
|
372 if ( 'TRUE' === $value ) { |
|
373 return true; |
|
374 } else if ( 'FALSE' === $value ) { |
|
375 return false; |
|
376 } else { |
|
377 throw new InvalidArgumentException('The given value is not a boolean value'); |
|
378 } |
|
379 } |
|
380 |
|
381 /** |
|
382 * Unserialize a serialized value to return the corresponding object |
|
383 * |
|
384 * @param string $value The value to convert |
|
385 * @return mixed |
|
386 * @throws UnexpectedValueException |
|
387 */ |
|
388 public static function fromLdapUnserialize($value) |
|
389 { |
|
390 $v = @unserialize($value); |
|
391 if (false===$v && $value != 'b:0;') { |
|
392 throw new UnexpectedValueException('The given value could not be unserialized'); |
|
393 } |
|
394 return $v; |
|
395 } |
|
396 } |