PageRenderTime 48ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/inc/libs/clearbricks/common/lib.http.php

https://bitbucket.org/dotclear/dotclear/
PHP | 448 lines | 275 code | 40 blank | 133 comment | 47 complexity | 3dd266ad79378c9f35c9031fb23115f1 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, Apache-2.0
  1. <?php
  2. # -- BEGIN LICENSE BLOCK ---------------------------------------
  3. #
  4. # This file is part of Clearbricks.
  5. #
  6. # Copyright (c) 2003-2011 Olivier Meunier & Association Dotclear
  7. # Licensed under the GPL version 2.0 license.
  8. # See LICENSE file or
  9. # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  10. #
  11. # -- END LICENSE BLOCK -----------------------------------------
  12. /**
  13. * HTTP utilities
  14. *
  15. * @package Clearbricks
  16. * @subpackage Common
  17. */
  18. class http
  19. {
  20. /** @var boolean Force HTTPS scheme on server port 443 in {@link getHost()} */
  21. public static $https_scheme_on_443 = false;
  22. /** @var integer Cache max age for {@link cache()} */
  23. public static $cache_max_age = 0;
  24. /**
  25. * Self root URI
  26. *
  27. * Returns current scheme, host and port.
  28. *
  29. * @return string
  30. */
  31. public static function getHost()
  32. {
  33. $server_name = explode(':',$_SERVER['HTTP_HOST']);
  34. $server_name = $server_name[0];
  35. if (self::$https_scheme_on_443 && $_SERVER['SERVER_PORT'] == '443')
  36. {
  37. $scheme = 'https';
  38. $port = '';
  39. }
  40. elseif (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on')
  41. {
  42. $scheme = 'https';
  43. $port = ($_SERVER['SERVER_PORT'] != '443') ? ':'.$_SERVER['SERVER_PORT'] : '';
  44. }
  45. else
  46. {
  47. $scheme = 'http';
  48. $port = ($_SERVER['SERVER_PORT'] != '80') ? ':'.$_SERVER['SERVER_PORT'] : '';
  49. }
  50. return $scheme.'://'.$server_name.$port;
  51. }
  52. /**
  53. * Self URI
  54. *
  55. * Returns current URI with full hostname.
  56. *
  57. * @return string
  58. */
  59. public static function getSelfURI()
  60. {
  61. return self::getHost().$_SERVER['REQUEST_URI'];
  62. }
  63. /**
  64. * Redirect
  65. *
  66. * Performs a conforming HTTP redirect for a relative URL.
  67. *
  68. * @param string $page Relative URL
  69. */
  70. public static function redirect($page)
  71. {
  72. if (preg_match('%^http[s]?://%',$page))
  73. {
  74. $redir = $page;
  75. }
  76. else
  77. {
  78. $host = self::getHost();
  79. $dir = str_replace(DIRECTORY_SEPARATOR,'/',dirname($_SERVER['PHP_SELF']));
  80. if (substr($page,0,1) == '/') {
  81. $redir = $host.$page;
  82. } else {
  83. if (substr($dir,-1) == '/') {
  84. $dir = substr($dir,0,-1);
  85. }
  86. $redir = $host.$dir.'/'.$page;
  87. }
  88. }
  89. # Close session if exists
  90. if (session_id()) {
  91. session_write_close();
  92. }
  93. header('Location: '.$redir);
  94. exit;
  95. }
  96. /**
  97. * Concat URL and path
  98. *
  99. * Appends a path to a given URL. If path begins with "/" it will replace the
  100. * original URL path.
  101. *
  102. * @param string $url URL
  103. * @param string $path Path to append
  104. * @return string
  105. */
  106. public static function concatURL($url,$path)
  107. {
  108. if (substr($path,0,1) != '/') {
  109. return $url.$path;
  110. }
  111. return preg_replace('#^(.+?//.+?)/(.*)$#','$1'.$path,$url);
  112. }
  113. /**
  114. * Real IP
  115. *
  116. * Returns the real client IP (or tries to do its best).
  117. *
  118. * @return string
  119. */
  120. public static function realIP()
  121. {
  122. return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null;
  123. }
  124. /**
  125. * Client unique ID
  126. *
  127. * Returns a "almost" safe client unique ID.
  128. *
  129. * @param string $key HMAC key
  130. * @return string
  131. */
  132. public static function browserUID($key)
  133. {
  134. $uid = '';
  135. $uid .= isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
  136. $uid .= isset($_SERVER['HTTP_ACCEPT_ENCODING']) ? $_SERVER['HTTP_ACCEPT_ENCODING'] : '';
  137. $uid .= isset($_SERVER['HTTP_ACCEPT_CHARSET']) ? $_SERVER['HTTP_ACCEPT_CHARSET'] : '';
  138. return crypt::hmac($key,$uid);
  139. }
  140. /**
  141. * Client language
  142. *
  143. * Returns a two letters language code take from HTTP_ACCEPT_LANGUAGE.
  144. *
  145. * @return string
  146. */
  147. public static function getAcceptLanguage()
  148. {
  149. $dlang = '';
  150. if (!empty($_SERVER['HTTP_ACCEPT_LANGUAGE']))
  151. {
  152. $acclang = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
  153. $L = explode(';', $acclang[0]);
  154. $dlang = substr(trim($L[0]),0,2);
  155. }
  156. return $dlang;
  157. }
  158. /**
  159. * Client languages
  160. *
  161. * Returns an array of accepted langages ordered by priority.
  162. * can be a two letters language code or a xx-xx variant.
  163. *
  164. * @return array
  165. */
  166. public static function getAcceptLanguages()
  167. {
  168. $acclang = array();
  169. if (!empty($_SERVER['HTTP_ACCEPT_LANGUAGE']))
  170. {
  171. $pattern = '/(?P<lang>[a-z]{2}(?:-[a-z]{2})?)(?:;q=(?P<priority>[.0-9]*))?/';
  172. if (preg_match_all($pattern,$_SERVER['HTTP_ACCEPT_LANGUAGE'],$acclang) !== false)
  173. {
  174. foreach($acclang['priority'] as $i => $p)
  175. {
  176. if ($p == '') $acclang['priority'][$i]=1;
  177. }
  178. array_multisort($acclang['priority'], SORT_DESC,$acclang['lang']);
  179. }
  180. return $acclang['lang'];
  181. } else {
  182. return $acclang;
  183. }
  184. }
  185. /**
  186. * HTTP Cache
  187. *
  188. * Sends HTTP cache headers (304) according to a list of files and an optionnal.
  189. * list of timestamps.
  190. *
  191. * @param array $files Files on which check mtime
  192. * @param array $mod_ts List of timestamps
  193. */
  194. public static function cache($files,$mod_ts=array())
  195. {
  196. if (empty($files) || !is_array($files)) {
  197. return;
  198. }
  199. array_walk($files,create_function('&$v','$v = filemtime($v);'));
  200. $array_ts = array_merge($mod_ts,$files);
  201. rsort($array_ts);
  202. $now = time();
  203. $ts = min($array_ts[0],$now);
  204. $since = null;
  205. if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
  206. $since = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
  207. $since = preg_replace ('/^(.*)(Mon|Tue|Wed|Thu|Fri|Sat|Sun)(.*)(GMT)(.*)/', '$2$3 GMT', $since);
  208. $since = strtotime($since);
  209. $since = ($since <= $now) ? $since : null;
  210. }
  211. # Common headers list
  212. $headers[] = 'Last-Modified: '.gmdate('D, d M Y H:i:s',$ts).' GMT';
  213. $headers[] = 'Cache-Control: must-revalidate, max-age='.abs((integer) self::$cache_max_age);
  214. $headers[] = 'Pragma:';
  215. if ($since >= $ts)
  216. {
  217. self::head(304,'Not Modified');
  218. foreach ($headers as $v) {
  219. header($v);
  220. }
  221. exit;
  222. }
  223. else
  224. {
  225. header('Date: '.gmdate('D, d M Y H:i:s',$now).' GMT');
  226. foreach ($headers as $v) {
  227. header($v);
  228. }
  229. }
  230. }
  231. /**
  232. * HTTP Etag
  233. *
  234. * Sends HTTP cache headers (304) according to a list of etags in client request.
  235. *
  236. * @param string $p_content Response page content
  237. */
  238. public static function etag()
  239. {
  240. # We create an etag from all arguments
  241. $args = func_get_args();
  242. if (empty($args)) {
  243. return;
  244. }
  245. $etag = '"'.md5(implode('',$args)).'"';
  246. unset($args);
  247. header('ETag: '.$etag);
  248. # Do we have a previously sent content?
  249. if (!empty($_SERVER['HTTP_IF_NONE_MATCH']))
  250. {
  251. foreach (explode(',',$_SERVER['HTTP_IF_NONE_MATCH']) as $i)
  252. {
  253. if (stripslashes(trim($i)) == $etag) {
  254. self::head(304,'Not Modified');
  255. exit;
  256. }
  257. }
  258. }
  259. }
  260. /**
  261. * HTTP Header
  262. *
  263. * Sends an HTTP code and message to client.
  264. *
  265. * @param string $code HTTP code
  266. * @param string $msg Message
  267. */
  268. public static function head($code,$msg=null)
  269. {
  270. $status_mode = preg_match('/cgi/',PHP_SAPI);
  271. if (!$msg)
  272. {
  273. $msg_codes = array(
  274. 100 => 'Continue',
  275. 101 => 'Switching Protocols',
  276. 200 => 'OK',
  277. 201 => 'Created',
  278. 202 => 'Accepted',
  279. 203 => 'Non-Authoritative Information',
  280. 204 => 'No Content',
  281. 205 => 'Reset Content',
  282. 206 => 'Partial Content',
  283. 300 => 'Multiple Choices',
  284. 301 => 'Moved Permanently',
  285. 302 => 'Found',
  286. 303 => 'See Other',
  287. 304 => 'Not Modified',
  288. 305 => 'Use Proxy',
  289. 307 => 'Temporary Redirect',
  290. 400 => 'Bad Request',
  291. 401 => 'Unauthorized',
  292. 402 => 'Payment Required',
  293. 403 => 'Forbidden',
  294. 404 => 'Not Found',
  295. 405 => 'Method Not Allowed',
  296. 406 => 'Not Acceptable',
  297. 407 => 'Proxy Authentication Required',
  298. 408 => 'Request Timeout',
  299. 409 => 'Conflict',
  300. 410 => 'Gone',
  301. 411 => 'Length Required',
  302. 412 => 'Precondition Failed',
  303. 413 => 'Request Entity Too Large',
  304. 414 => 'Request-URI Too Long',
  305. 415 => 'Unsupported Media Type',
  306. 416 => 'Requested Range Not Satisfiable',
  307. 417 => 'Expectation Failed',
  308. 500 => 'Internal Server Error',
  309. 501 => 'Not Implemented',
  310. 502 => 'Bad Gateway',
  311. 503 => 'Service Unavailable',
  312. 504 => 'Gateway Timeout',
  313. 505 => 'HTTP Version Not Supported'
  314. );
  315. $msg = isset($msg_codes[$code]) ? $msg_codes[$code] : '-';
  316. }
  317. if ($status_mode) {
  318. header('Status: '.$code.' '.$msg);
  319. } else {
  320. header($msg, true, $code);
  321. }
  322. }
  323. /**
  324. * Trim request
  325. *
  326. * Trims every value in GET, POST, REQUEST and COOKIE vars.
  327. * Removes magic quotes if magic_quote_gpc is on.
  328. */
  329. public static function trimRequest()
  330. {
  331. if(!empty($_GET)) {
  332. array_walk($_GET,array('self','trimRequestInVar'));
  333. }
  334. if(!empty($_POST)) {
  335. array_walk($_POST,array('self','trimRequestInVar'));
  336. }
  337. if(!empty($_REQUEST)) {
  338. array_walk($_REQUEST,array('self','trimRequestInVar'));
  339. }
  340. if(!empty($_COOKIE)) {
  341. array_walk($_COOKIE,array('self','trimRequestInVar'));
  342. }
  343. }
  344. /*
  345. *
  346. * To be deleted?
  347. * -- saymonz, 29.04.2011
  348. *
  349. //*
  350. private static function trimRequestHandler(&$v,$key)
  351. {
  352. $v = self::trimRequestInVar($v);
  353. }
  354. //*/
  355. private static function trimRequestInVar(&$value,$key)
  356. {
  357. if (is_array($value))
  358. {
  359. foreach ($value as $k => &$v)
  360. {
  361. if (is_array($v)) {
  362. self::trimRequestInVar($v,$k);
  363. } else {
  364. if (get_magic_quotes_gpc()) {
  365. $v = stripslashes($v);
  366. }
  367. $v = trim($v);
  368. }
  369. }
  370. }
  371. else
  372. {
  373. if (get_magic_quotes_gpc()) {
  374. $value = stripslashes($value);
  375. }
  376. $value = trim($value);
  377. }
  378. }
  379. /**
  380. * Unset global variables
  381. *
  382. * If register_globals is on, removes every GET, POST, COOKIE, REQUEST, SERVER,
  383. * ENV, FILES vars from GLOBALS.
  384. */
  385. public static function unsetGlobals()
  386. {
  387. if (!ini_get('register_globals')) {
  388. return;
  389. }
  390. if (isset($_REQUEST['GLOBALS'])) {
  391. throw new Exception('GLOBALS overwrite attempt detected');
  392. }
  393. # Variables that shouldn't be unset
  394. $no_unset = array('GLOBALS','_GET','_POST','_COOKIE','_REQUEST',
  395. '_SERVER','_ENV','_FILES');
  396. $input = array_merge($_GET,$_POST,$_COOKIE,$_SERVER,$_ENV,$_FILES,
  397. (isset($_SESSION) && is_array($_SESSION) ? $_SESSION : array()));
  398. foreach ($input as $k => $v) {
  399. if (!in_array($k,$no_unset) && isset($GLOBALS[$k]) ) {
  400. $GLOBALS[$k] = null;
  401. unset($GLOBALS[$k]);
  402. }
  403. }
  404. }
  405. }
  406. ?>