File indexing completed on 2024-12-22 05:36:48
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_Ldap 0017 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0018 * @license http://framework.zend.com/license/new-bsd New BSD License 0019 * @version $Id$ 0020 */ 0021 0022 /** 0023 * Zend_Ldap_Converter is a collection of useful LDAP related conversion functions. 0024 * 0025 * @category Zend 0026 * @package Zend_Ldap 0027 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0028 * @license http://framework.zend.com/license/new-bsd New BSD License 0029 */ 0030 class Zend_Ldap_Converter 0031 { 0032 const STANDARD = 0; 0033 const BOOLEAN = 1; 0034 const GENERALIZED_TIME = 2; 0035 0036 /** 0037 * Converts all ASCII chars < 32 to "\HEX" 0038 * 0039 * @see Net_LDAP2_Util::asc2hex32() from Benedikt Hallinger <beni@php.net> 0040 * @link http://pear.php.net/package/Net_LDAP2 0041 * @author Benedikt Hallinger <beni@php.net> 0042 * 0043 * @param string $string String to convert 0044 * @return string 0045 */ 0046 public static function ascToHex32($string) 0047 { 0048 for ($i = 0; $i<strlen($string); $i++) { 0049 $char = substr($string, $i, 1); 0050 if (ord($char)<32) { 0051 $hex = dechex(ord($char)); 0052 if (strlen($hex) == 1) $hex = '0' . $hex; 0053 $string = str_replace($char, '\\' . $hex, $string); 0054 } 0055 } 0056 return $string; 0057 } 0058 0059 /** 0060 * Converts all Hex expressions ("\HEX") to their original ASCII characters 0061 * 0062 * @see Net_LDAP2_Util::hex2asc() from Benedikt Hallinger <beni@php.net>, 0063 * heavily based on work from DavidSmith@byu.net 0064 * @link http://pear.php.net/package/Net_LDAP2 0065 * @author Benedikt Hallinger <beni@php.net>, heavily based on work from DavidSmith@byu.net 0066 * 0067 * @param string $string String to convert 0068 * @return string 0069 */ 0070 public static function hex32ToAsc($string) 0071 { 0072 // Using a callback, since PHP 5.5 has deprecated the /e modifier in preg_replace. 0073 $string = preg_replace_callback("/\\\([0-9A-Fa-f]{2})/", array('Zend_Ldap_Converter', '_charHex32ToAsc'), $string); 0074 return $string; 0075 } 0076 0077 /** 0078 * Convert a single slash-prefixed character from Hex32 to ASCII. 0079 * Used as a callback in @see hex32ToAsc() 0080 * @param array $matches 0081 * 0082 * @return string 0083 */ 0084 private static function _charHex32ToAsc(array $matches) 0085 { 0086 return chr(hexdec($matches[0])); 0087 } 0088 0089 /** 0090 * Convert any value to an LDAP-compatible value. 0091 * 0092 * By setting the <var>$type</var>-parameter the conversion of a certain 0093 * type can be forced 0094 * 0095 * @todo write more tests 0096 * 0097 * @param mixed $value The value to convert 0098 * @param int $ytpe The conversion type to use 0099 * @return string 0100 * @throws Zend_Ldap_Converter_Exception 0101 */ 0102 public static function toLdap($value, $type = self::STANDARD) 0103 { 0104 try { 0105 switch ($type) { 0106 case self::BOOLEAN: 0107 return self::toldapBoolean($value); 0108 break; 0109 case self::GENERALIZED_TIME: 0110 return self::toLdapDatetime($value); 0111 break; 0112 default: 0113 if (is_string($value)) { 0114 return $value; 0115 } else if (is_int($value) || is_float($value)) { 0116 return (string)$value; 0117 } else if (is_bool($value)) { 0118 return self::toldapBoolean($value); 0119 } else if (is_object($value)) { 0120 if ($value instanceof DateTime) { 0121 return self::toLdapDatetime($value); 0122 } else if ($value instanceof Zend_Date) { 0123 return self::toLdapDatetime($value); 0124 } else { 0125 return self::toLdapSerialize($value); 0126 } 0127 } else if (is_array($value)) { 0128 return self::toLdapSerialize($value); 0129 } else if (is_resource($value) && get_resource_type($value) === 'stream') { 0130 return stream_get_contents($value); 0131 } else { 0132 return null; 0133 } 0134 break; 0135 } 0136 } catch (Exception $e) { 0137 throw new Zend_Ldap_Converter_Exception($e->getMessage(), $e->getCode(), $e); 0138 } 0139 } 0140 0141 /** 0142 * Converts a date-entity to an LDAP-compatible date-string 0143 * 0144 * The date-entity <var>$date</var> can be either a timestamp, a 0145 * DateTime Object, a string that is parseable by strtotime() or a Zend_Date 0146 * Object. 0147 * 0148 * @param integer|string|DateTimt|Zend_Date $date The date-entity 0149 * @param boolean $asUtc Whether to return the LDAP-compatible date-string 0150 * as UTC or as local value 0151 * @return string 0152 * @throws InvalidArgumentException 0153 */ 0154 public static function toLdapDateTime($date, $asUtc = true) 0155 { 0156 if (!($date instanceof DateTime)) { 0157 if (is_int($date)) { 0158 $date = new DateTime('@' . $date); 0159 $date->setTimezone(new DateTimeZone(date_default_timezone_get())); 0160 } else if (is_string($date)) { 0161 $date = new DateTime($date); 0162 } else if ($date instanceof Zend_Date) { 0163 $date = new DateTime($date->get(Zend_Date::ISO_8601)); 0164 } else { 0165 throw new InvalidArgumentException('Parameter $date is not of the expected type'); 0166 } 0167 } 0168 $timezone = $date->format('O'); 0169 if (true === $asUtc) { 0170 $date->setTimezone(new DateTimeZone('UTC')); 0171 $timezone = 'Z'; 0172 } 0173 if ( '+0000' === $timezone ) { 0174 $timezone = 'Z'; 0175 } 0176 return $date->format('YmdHis') . $timezone; 0177 } 0178 0179 /** 0180 * Convert a boolean value to an LDAP-compatible string 0181 * 0182 * This converts a boolean value of TRUE, an integer-value of 1 and a 0183 * case-insensitive string 'true' to an LDAP-compatible 'TRUE'. All other 0184 * other values are converted to an LDAP-compatible 'FALSE'. 0185 * 0186 * @param boolean|integer|string $value The boolean value to encode 0187 * @return string 0188 */ 0189 public static function toLdapBoolean($value) 0190 { 0191 $return = 'FALSE'; 0192 if (!is_scalar($value)) { 0193 return $return; 0194 } 0195 if (true === $value || 'true' === strtolower($value) || 1 === $value) { 0196 $return = 'TRUE'; 0197 } 0198 return $return; 0199 } 0200 0201 /** 0202 * Serialize any value for storage in LDAP 0203 * 0204 * @param mixed $value The value to serialize 0205 * @return string 0206 */ 0207 public static function toLdapSerialize($value) 0208 { 0209 return serialize($value); 0210 } 0211 0212 /** 0213 * Convert an LDAP-compatible value to a corresponding PHP-value. 0214 * 0215 * By setting the <var>$type</var>-parameter the conversion of a certain 0216 * type can be forced 0217 * . 0218 * @param string $value The value to convert 0219 * @param int $ytpe The conversion type to use 0220 * @param boolean $dateTimeAsUtc Return DateTime values in UTC timezone 0221 * @return mixed 0222 * @throws Zend_Ldap_Converter_Exception 0223 */ 0224 public static function fromLdap($value, $type = self::STANDARD, $dateTimeAsUtc = true) 0225 { 0226 switch ($type) { 0227 case self::BOOLEAN: 0228 return self::fromldapBoolean($value); 0229 break; 0230 case self::GENERALIZED_TIME: 0231 return self::fromLdapDateTime($value); 0232 break; 0233 default: 0234 if (is_numeric($value)) { 0235 // prevent numeric values to be treated as date/time 0236 return $value; 0237 } else if ('TRUE' === $value || 'FALSE' === $value) { 0238 return self::fromLdapBoolean($value); 0239 } 0240 if (preg_match('/^\d{4}[\d\+\-Z\.]*$/', $value)) { 0241 return self::fromLdapDateTime($value, $dateTimeAsUtc); 0242 } 0243 try { 0244 return self::fromLdapUnserialize($value); 0245 } catch (UnexpectedValueException $e) { } 0246 break; 0247 } 0248 return $value; 0249 } 0250 0251 /** 0252 * Convert an LDAP-Generalized-Time-entry into a DateTime-Object 0253 * 0254 * CAVEAT: The DateTime-Object returned will alwasy be set to UTC-Timezone. 0255 * 0256 * @param string $date The generalized-Time 0257 * @param boolean $asUtc Return the DateTime with UTC timezone 0258 * @return DateTime 0259 * @throws InvalidArgumentException if a non-parseable-format is given 0260 */ 0261 public static function fromLdapDateTime($date, $asUtc = true) 0262 { 0263 $datepart = array (); 0264 if (!preg_match('/^(\d{4})/', $date, $datepart) ) { 0265 throw new InvalidArgumentException('Invalid date format found'); 0266 } 0267 0268 if ($datepart[1] < 4) { 0269 throw new InvalidArgumentException('Invalid date format found (too short)'); 0270 } 0271 0272 $time = array ( 0273 // The year is mandatory! 0274 'year' => $datepart[1], 0275 'month' => 1, 0276 'day' => 1, 0277 'hour' => 0, 0278 'minute' => 0, 0279 'second' => 0, 0280 'offdir' => '+', 0281 'offsethours' => 0, 0282 'offsetminutes' => 0 0283 ); 0284 0285 $length = strlen($date); 0286 0287 // Check for month. 0288 if ($length >= 6) { 0289 $month = substr($date, 4, 2); 0290 if ($month < 1 || $month > 12) { 0291 throw new InvalidArgumentException('Invalid date format found (invalid month)'); 0292 } 0293 $time['month'] = $month; 0294 } 0295 0296 // Check for day 0297 if ($length >= 8) { 0298 $day = substr($date, 6, 2); 0299 if ($day < 1 || $day > 31) { 0300 throw new InvalidArgumentException('Invalid date format found (invalid day)'); 0301 } 0302 $time['day'] = $day; 0303 } 0304 0305 // Check for Hour 0306 if ($length >= 10) { 0307 $hour = substr($date, 8, 2); 0308 if ($hour < 0 || $hour > 23) { 0309 throw new InvalidArgumentException('Invalid date format found (invalid hour)'); 0310 } 0311 $time['hour'] = $hour; 0312 } 0313 0314 // Check for minute 0315 if ($length >= 12) { 0316 $minute = substr($date, 10, 2); 0317 if ($minute < 0 || $minute > 59) { 0318 throw new InvalidArgumentException('Invalid date format found (invalid minute)'); 0319 } 0320 $time['minute'] = $minute; 0321 } 0322 0323 // Check for seconds 0324 if ($length >= 14) { 0325 $second = substr($date, 12, 2); 0326 if ($second < 0 || $second > 59) { 0327 throw new InvalidArgumentException('Invalid date format found (invalid second)'); 0328 } 0329 $time['second'] = $second; 0330 } 0331 0332 // Set Offset 0333 $offsetRegEx = '/([Z\-\+])(\d{2}\'?){0,1}(\d{2}\'?){0,1}$/'; 0334 $off = array (); 0335 if (preg_match($offsetRegEx, $date, $off)) { 0336 $offset = $off[1]; 0337 if ($offset == '+' || $offset == '-') { 0338 $time['offdir'] = $offset; 0339 // we have an offset, so lets calculate it. 0340 if (isset($off[2])) { 0341 $offsetHours = substr($off[2], 0, 2); 0342 if ($offsetHours < 0 || $offsetHours > 12) { 0343 throw new InvalidArgumentException('Invalid date format found (invalid offset hour)'); 0344 } 0345 $time['offsethours'] = $offsetHours; 0346 } 0347 if (isset($off[3])) { 0348 $offsetMinutes = substr($off[3], 0, 2); 0349 if ($offsetMinutes < 0 || $offsetMinutes > 59) { 0350 throw new InvalidArgumentException('Invalid date format found (invalid offset minute)'); 0351 } 0352 $time['offsetminutes'] = $offsetMinutes; 0353 } 0354 } 0355 } 0356 0357 // Raw-Data is present, so lets create a DateTime-Object from it. 0358 $offset = $time['offdir'] 0359 . str_pad($time['offsethours'],2,'0',STR_PAD_LEFT) 0360 . str_pad($time['offsetminutes'],2,'0',STR_PAD_LEFT); 0361 $timestring = $time['year'] . '-' 0362 . str_pad($time['month'], 2, '0', STR_PAD_LEFT) . '-' 0363 . str_pad($time['day'], 2, '0', STR_PAD_LEFT) . ' ' 0364 . str_pad($time['hour'], 2, '0', STR_PAD_LEFT) . ':' 0365 . str_pad($time['minute'], 2, '0', STR_PAD_LEFT) . ':' 0366 . str_pad($time['second'], 2, '0', STR_PAD_LEFT) 0367 . $time['offdir'] 0368 . str_pad($time['offsethours'], 2, '0', STR_PAD_LEFT) 0369 . str_pad($time['offsetminutes'], 2, '0', STR_PAD_LEFT); 0370 $date = new DateTime($timestring); 0371 if ($asUtc) { 0372 $date->setTimezone(new DateTimeZone('UTC')); 0373 } 0374 return $date; 0375 } 0376 0377 /** 0378 * Convert an LDAP-compatible boolean value into a PHP-compatible one 0379 * 0380 * @param string $value The value to convert 0381 * @return boolean 0382 * @throws InvalidArgumentException 0383 */ 0384 public static function fromLdapBoolean($value) 0385 { 0386 if ( 'TRUE' === $value ) { 0387 return true; 0388 } else if ( 'FALSE' === $value ) { 0389 return false; 0390 } else { 0391 throw new InvalidArgumentException('The given value is not a boolean value'); 0392 } 0393 } 0394 0395 /** 0396 * Unserialize a serialized value to return the corresponding object 0397 * 0398 * @param string $value The value to convert 0399 * @return mixed 0400 * @throws UnexpectedValueException 0401 */ 0402 public static function fromLdapUnserialize($value) 0403 { 0404 $v = @unserialize($value); 0405 if (false===$v && $value != 'b:0;') { 0406 throw new UnexpectedValueException('The given value could not be unserialized'); 0407 } 0408 return $v; 0409 } 0410 }