PageRenderTime 38ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/slim/Http/Util.php

http://github.com/h9k/magirc
PHP | 375 lines | 170 code | 25 blank | 180 comment | 41 complexity | f40b1b2d85409f32a14f2c9d2bc62cf6 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.1, MPL-2.0-no-copyleft-exception
  1. <?php
  2. /**
  3. * Slim - a micro PHP 5 framework
  4. *
  5. * @author Josh Lockhart <info@slimframework.com>
  6. * @copyright 2011 Josh Lockhart
  7. * @link http://www.slimframework.com
  8. * @license http://www.slimframework.com/license
  9. * @version 1.6.0
  10. * @package Slim
  11. *
  12. * MIT LICENSE
  13. *
  14. * Permission is hereby granted, free of charge, to any person obtaining
  15. * a copy of this software and associated documentation files (the
  16. * "Software"), to deal in the Software without restriction, including
  17. * without limitation the rights to use, copy, modify, merge, publish,
  18. * distribute, sublicense, and/or sell copies of the Software, and to
  19. * permit persons to whom the Software is furnished to do so, subject to
  20. * the following conditions:
  21. *
  22. * The above copyright notice and this permission notice shall be
  23. * included in all copies or substantial portions of the Software.
  24. *
  25. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  26. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  27. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  28. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  29. * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  30. * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  31. * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  32. */
  33. /**
  34. * Slim HTTP Utilities
  35. *
  36. * This class provides useful methods for handling HTTP requests.
  37. *
  38. * @package Slim
  39. * @author Josh Lockhart
  40. * @since 1.0.0
  41. */
  42. class Slim_Http_Util {
  43. /**
  44. * Strip slashes from string or array
  45. *
  46. * This method strips slashes from its input. By default, this method will only
  47. * strip slashes from its input if magic quotes are enabled. Otherwise, you may
  48. * override the magic quotes setting with either TRUE or FALSE as the send argument
  49. * to force this method to strip or not strip slashes from its input.
  50. *
  51. * @var array|string $rawData
  52. * @return array|string
  53. */
  54. public static function stripSlashesIfMagicQuotes( $rawData, $overrideStripSlashes = null ) {
  55. $strip = is_null($overrideStripSlashes) ? get_magic_quotes_gpc() : $overrideStripSlashes;
  56. if ( $strip ) {
  57. return self::_stripSlashes($rawData);
  58. } else {
  59. return $rawData;
  60. }
  61. }
  62. /**
  63. * Strip slashes from string or array
  64. * @param array|string $rawData
  65. * @return array|string
  66. */
  67. protected static function _stripSlashes( $rawData ) {
  68. return is_array($rawData) ? array_map(array('self', '_stripSlashes'), $rawData) : stripslashes($rawData);
  69. }
  70. /**
  71. * Encrypt data
  72. *
  73. * This method will encrypt data using a given key, vector, and cipher.
  74. * By default, this will encrypt data using the RIJNDAEL/AES 256 bit cipher. You
  75. * may override the default cipher and cipher mode by passing your own desired
  76. * cipher and cipher mode as the final key-value array argument.
  77. *
  78. * @param string $data The unencrypted data
  79. * @param string $key The encryption key
  80. * @param string $iv The encryption initialization vector
  81. * @param array $settings Optional key-value array with custom algorithm and mode
  82. * @return string
  83. */
  84. public static function encrypt( $data, $key, $iv, $settings = array() ) {
  85. if ( $data === '' || !extension_loaded('mcrypt') ) {
  86. return $data;
  87. }
  88. //Merge settings with defaults
  89. $settings = array_merge(array(
  90. 'algorithm' => MCRYPT_RIJNDAEL_256,
  91. 'mode' => MCRYPT_MODE_CBC
  92. ), $settings);
  93. //Get module
  94. $module = mcrypt_module_open($settings['algorithm'], '', $settings['mode'], '');
  95. //Validate IV
  96. $ivSize = mcrypt_enc_get_iv_size($module);
  97. if ( strlen($iv) > $ivSize ) {
  98. $iv = substr($iv, 0, $ivSize);
  99. }
  100. //Validate key
  101. $keySize = mcrypt_enc_get_key_size($module);
  102. if ( strlen($key) > $keySize ) {
  103. $key = substr($key, 0, $keySize);
  104. }
  105. //Encrypt value
  106. mcrypt_generic_init($module, $key, $iv);
  107. $res = @mcrypt_generic($module, $data);
  108. mcrypt_generic_deinit($module);
  109. return $res;
  110. }
  111. /**
  112. * Decrypt data
  113. *
  114. * This method will decrypt data using a given key, vector, and cipher.
  115. * By default, this will decrypt data using the RIJNDAEL/AES 256 bit cipher. You
  116. * may override the default cipher and cipher mode by passing your own desired
  117. * cipher and cipher mode as the final key-value array argument.
  118. *
  119. * @param string $data The encrypted data
  120. * @param string $key The encryption key
  121. * @param string $iv The encryption initialization vector
  122. * @param array $settings Optional key-value array with custom algorithm and mode
  123. * @return string
  124. */
  125. public static function decrypt( $data, $key, $iv, $settings = array() ) {
  126. if ( $data === '' || !extension_loaded('mcrypt') ) {
  127. return $data;
  128. }
  129. //Merge settings with defaults
  130. $settings = array_merge(array(
  131. 'algorithm' => MCRYPT_RIJNDAEL_256,
  132. 'mode' => MCRYPT_MODE_CBC
  133. ), $settings);
  134. //Get module
  135. $module = mcrypt_module_open($settings['algorithm'], '', $settings['mode'], '');
  136. //Validate IV
  137. $ivSize = mcrypt_enc_get_iv_size($module);
  138. if ( strlen($iv) > $ivSize ) {
  139. $iv = substr($iv, 0, $ivSize);
  140. }
  141. //Validate key
  142. $keySize = mcrypt_enc_get_key_size($module);
  143. if ( strlen($key) > $keySize ) {
  144. $key = substr($key, 0, $keySize);
  145. }
  146. //Decrypt value
  147. mcrypt_generic_init($module, $key, $iv);
  148. $decryptedData = @mdecrypt_generic($module, $data);
  149. $res = str_replace("\x0", '', $decryptedData);
  150. mcrypt_generic_deinit($module);
  151. return $res;
  152. }
  153. /**
  154. * Encode secure cookie value
  155. *
  156. * This method will create the secure value of an HTTP cookie. The
  157. * cookie value is encrypted and hashed so that its value is
  158. * secure and checked for integrity when read in subsequent requests.
  159. *
  160. * @param string $value The unsecure HTTP cookie value
  161. * @param int $expires The UNIX timestamp at which this cookie will expire
  162. * @param string $secret The secret key used to hash the cookie value
  163. * @param int $algorithm The algorithm to use for encryption
  164. * @param int $mode The algorithm mode to use for encryption
  165. * @param string
  166. */
  167. public static function encodeSecureCookie( $value, $expires, $secret, $algorithm, $mode ) {
  168. $key = hash_hmac('sha1', $expires, $secret);
  169. $iv = self::get_iv($expires, $secret);
  170. $secureString = base64_encode(self::encrypt($value, $key, $iv, array(
  171. 'algorithm' => $algorithm,
  172. 'mode' => $mode
  173. )));
  174. $verificationString = hash_hmac('sha1', $expires . $value, $key);
  175. return implode('|', array($expires, $secureString, $verificationString));
  176. }
  177. /**
  178. * Decode secure cookie value
  179. *
  180. * This method will decode the secure value of an HTTP cookie. The
  181. * cookie value is encrypted and hashed so that its value is
  182. * secure and checked for integrity when read in subsequent requests.
  183. *
  184. * @param string $value The secure HTTP cookie value
  185. * @param int $expires The UNIX timestamp at which this cookie will expire
  186. * @param string $secret The secret key used to hash the cookie value
  187. * @param int $algorithm The algorithm to use for encryption
  188. * @param int $mode The algorithm mode to use for encryption
  189. * @param string
  190. */
  191. public static function decodeSecureCookie( $value, $secret, $algorithm, $mode ) {
  192. if ( $value ) {
  193. $value = explode('|', $value);
  194. if ( count($value) === 3 && ( (int)$value[0] === 0 || (int)$value[0] > time() ) ) {
  195. $key = hash_hmac('sha1', $value[0], $secret);
  196. $iv = self::get_iv($value[0], $secret);
  197. $data = self::decrypt(base64_decode($value[1]), $key, $iv, array(
  198. 'algorithm' => $algorithm,
  199. 'mode' => $mode
  200. ));
  201. $verificationString = hash_hmac('sha1', $value[0] . $data, $key);
  202. if ( $verificationString === $value[2] ) {
  203. return $data;
  204. }
  205. }
  206. }
  207. return false;
  208. }
  209. /**
  210. * Set HTTP cookie header
  211. *
  212. * This method will construct and set the HTTP `Set-Cookie` header. Slim
  213. * uses this method instead of PHP's native `setcookie` method. This allows
  214. * more control of the HTTP header irrespective of the native implementation's
  215. * dependency on PHP versions.
  216. *
  217. * This method accepts the Slim_Http_Headers object by reference as its
  218. * first argument; this method directly modifies this object instead of
  219. * returning a value.
  220. *
  221. * @param array $header
  222. * @param string $name
  223. * @param string $value
  224. * @return void
  225. */
  226. public static function setCookieHeader( &$header, $name, $value ) {
  227. //Build cookie header
  228. if ( is_array($value) ) {
  229. $domain = '';
  230. $path = '';
  231. $expires = '';
  232. $secure = '';
  233. $httponly = '';
  234. if ( isset($value['domain']) && $value['domain'] ) {
  235. $domain = '; domain=' . $value['domain'];
  236. }
  237. if ( isset($value['path']) && $value['path'] ) {
  238. $path = '; path=' . $value['path'];
  239. }
  240. if ( isset($value['expires']) ) {
  241. if ( is_string($value['expires']) ) {
  242. $timestamp = strtotime($value['expires']);
  243. } else {
  244. $timestamp = (int)$value['expires'];
  245. }
  246. if ( $timestamp !== 0 ) {
  247. $expires = '; expires=' . gmdate('D, d-M-Y H:i:s e', $timestamp);
  248. }
  249. }
  250. if ( isset($value['secure']) && $value['secure'] ) {
  251. $secure = '; secure';
  252. }
  253. if ( isset($value['httponly']) && $value['httponly'] ) {
  254. $httponly = '; HttpOnly';
  255. }
  256. $cookie = sprintf('%s=%s%s', urlencode($name), urlencode((string)$value['value']), $domain . $path . $expires . $secure . $httponly);
  257. } else {
  258. $cookie = sprintf('%s=%s', urlencode($name), urlencode((string)$value));
  259. }
  260. //Set cookie header
  261. if ( !isset($header['Set-Cookie']) || $header['Set-Cookie'] === '' ) {
  262. $header['Set-Cookie'] = $cookie;
  263. } else {
  264. $header['Set-Cookie'] = implode("\n", array($header['Set-Cookie'], $cookie));
  265. }
  266. }
  267. /**
  268. * Delete HTTP cookie header
  269. *
  270. * This method will construct and set the HTTP `Set-Cookie` header to invalidate
  271. * a client-side HTTP cookie. If a cookie with the same name (and, optionally, domain)
  272. * is already set in the HTTP response, it will also be removed. Slim uses this method
  273. * instead of PHP's native `setcookie` method. This allows more control of the HTTP header
  274. * irrespective of PHP's native implementation's dependency on PHP versions.
  275. *
  276. * This method accepts the Slim_Http_Headers object by reference as its
  277. * first argument; this method directly modifies this object instead of
  278. * returning a value.
  279. *
  280. * @param array $header
  281. * @param string $name
  282. * @param string $value
  283. * @return void
  284. */
  285. public static function deleteCookieHeader( &$header, $name, $value = array() ) {
  286. //Remove affected cookies from current response header
  287. $cookiesOld = array();
  288. $cookiesNew = array();
  289. if ( isset($header['Set-Cookie']) ) {
  290. $cookiesOld = explode("\n", $header['Set-Cookie']);
  291. }
  292. foreach ( $cookiesOld as $c ) {
  293. if ( isset($value['domain']) && $value['domain'] ) {
  294. $regex = sprintf('@%s=.*domain=%s@', urlencode($name), preg_quote($value['domain']));
  295. } else {
  296. $regex = sprintf('@%s=@', urlencode($name));
  297. }
  298. if ( preg_match($regex, $c) === 0 ) {
  299. $cookiesNew[] = $c;
  300. }
  301. }
  302. if ( $cookiesNew ) {
  303. $header['Set-Cookie'] = implode("\n", $cookiesNew);
  304. } else {
  305. unset($header['Set-Cookie']);
  306. }
  307. //Set invalidating cookie to clear client-side cookie
  308. self::setCookieHeader($header, $name, array_merge(array('value' => '', 'path' => null, 'domain' => null, 'expires' => time() - 100), $value));
  309. }
  310. /**
  311. * Parse cookie header
  312. *
  313. * This method will parse the HTTP requst's `Cookie` header
  314. * and extract cookies into an associative array.
  315. *
  316. * @param string
  317. * @return array
  318. */
  319. public static function parseCookieHeader( $header ) {
  320. $cookies = array();
  321. $header = rtrim($header, "\r\n");
  322. $headerPieces = preg_split('@\s*[;,]\s*@', $header);
  323. foreach ( $headerPieces as $c ) {
  324. $cParts = explode('=', $c);
  325. if ( count($cParts) === 2 ) {
  326. $key = urldecode($cParts[0]);
  327. $value = urldecode($cParts[1]);
  328. if ( !isset($cookies[$key]) ) {
  329. $cookies[$key] = $value;
  330. }
  331. }
  332. }
  333. return $cookies;
  334. }
  335. /**
  336. * Generate a random IV
  337. *
  338. * This method will generate a non-predictable IV for use with
  339. * the cookie encryption
  340. *
  341. * @param int $expires The UNIX timestamp at which this cookie will expire
  342. * @param string $secret The secret key used to hash the cookie value
  343. * @return binary string with length 40
  344. */
  345. private static function get_iv($expires, $secret) {
  346. $data1 = hash_hmac('sha1', 'a'.$expires.'b', $secret);
  347. $data2 = hash_hmac('sha1', 'z'.$expires.'y', $secret);
  348. return pack("h*", $data1.$data2);
  349. }
  350. }