You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

340 lines
10 KiB

12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
  1. <?php
  2. /**
  3. * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
  4. * This file is licensed under the Affero General Public License version 3 or
  5. * later.
  6. * See the COPYING-README file.
  7. */
  8. class OC_Request {
  9. const USER_AGENT_IE = '/MSIE/';
  10. // Android Chrome user agent: https://developers.google.com/chrome/mobile/docs/user-agent
  11. const USER_AGENT_ANDROID_MOBILE_CHROME = '#Android.*Chrome/[.0-9]*#';
  12. const USER_AGENT_FREEBOX = '#^Mozilla/5\.0$#';
  13. const REGEX_LOCALHOST = '/^(127\.0\.0\.1|localhost)$/';
  14. static protected $reqId;
  15. /**
  16. * Returns the remote address, if the connection came from a trusted proxy and `forwarded_for_headers` has been configured
  17. * then the IP address specified in this header will be returned instead.
  18. * Do always use this instead of $_SERVER['REMOTE_ADDR']
  19. * @return string IP address
  20. */
  21. public static function getRemoteAddress() {
  22. $remoteAddress = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '';
  23. $trustedProxies = \OC::$server->getConfig()->getSystemValue('trusted_proxies', array());
  24. if(is_array($trustedProxies) && in_array($remoteAddress, $trustedProxies)) {
  25. $forwardedForHeaders = \OC::$server->getConfig()->getSystemValue('forwarded_for_headers', array());
  26. foreach($forwardedForHeaders as $header) {
  27. if (array_key_exists($header, $_SERVER) === true) {
  28. foreach (explode(',', $_SERVER[$header]) as $IP) {
  29. $IP = trim($IP);
  30. if (filter_var($IP, FILTER_VALIDATE_IP) !== false) {
  31. return $IP;
  32. }
  33. }
  34. }
  35. }
  36. }
  37. return $remoteAddress;
  38. }
  39. /**
  40. * Returns an ID for the request, value is not guaranteed to be unique and is mostly meant for logging
  41. * @return string
  42. */
  43. public static function getRequestID() {
  44. if(self::$reqId === null) {
  45. self::$reqId = hash('md5', microtime().\OC::$server->getSecureRandom()->getLowStrengthGenerator()->generate(20));
  46. }
  47. return self::$reqId;
  48. }
  49. /**
  50. * Check overwrite condition
  51. * @param string $type
  52. * @return bool
  53. */
  54. private static function isOverwriteCondition($type = '') {
  55. $regex = '/' . OC_Config::getValue('overwritecondaddr', '') . '/';
  56. return $regex === '//' or preg_match($regex, $_SERVER['REMOTE_ADDR']) === 1
  57. or ($type !== 'protocol' and OC_Config::getValue('forcessl', false));
  58. }
  59. /**
  60. * Strips a potential port from a domain (in format domain:port)
  61. * @param $host
  62. * @return string $host without appended port
  63. */
  64. public static function getDomainWithoutPort($host) {
  65. $pos = strrpos($host, ':');
  66. if ($pos !== false) {
  67. $port = substr($host, $pos + 1);
  68. if (is_numeric($port)) {
  69. $host = substr($host, 0, $pos);
  70. }
  71. }
  72. return $host;
  73. }
  74. /**
  75. * Checks whether a domain is considered as trusted from the list
  76. * of trusted domains. If no trusted domains have been configured, returns
  77. * true.
  78. * This is used to prevent Host Header Poisoning.
  79. * @param string $domainWithPort
  80. * @return bool true if the given domain is trusted or if no trusted domains
  81. * have been configured
  82. */
  83. public static function isTrustedDomain($domainWithPort) {
  84. // Extract port from domain if needed
  85. $domain = self::getDomainWithoutPort($domainWithPort);
  86. // FIXME: Empty config array defaults to true for now. - Deprecate this behaviour with ownCloud 8.
  87. $trustedList = \OC::$server->getConfig()->getSystemValue('trusted_domains', array());
  88. if (empty($trustedList)) {
  89. return true;
  90. }
  91. // FIXME: Workaround for older instances still with port applied. Remove for ownCloud 9.
  92. if(in_array($domainWithPort, $trustedList)) {
  93. return true;
  94. }
  95. // Always allow access from localhost
  96. if (preg_match(self::REGEX_LOCALHOST, $domain) === 1) {
  97. return true;
  98. }
  99. return in_array($domain, $trustedList);
  100. }
  101. /**
  102. * Returns the unverified server host from the headers without checking
  103. * whether it is a trusted domain
  104. * @return string the server host
  105. *
  106. * Returns the server host, even if the website uses one or more
  107. * reverse proxies
  108. */
  109. public static function insecureServerHost() {
  110. $host = null;
  111. if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {
  112. if (strpos($_SERVER['HTTP_X_FORWARDED_HOST'], ",") !== false) {
  113. $parts = explode(',', $_SERVER['HTTP_X_FORWARDED_HOST']);
  114. $host = trim(current($parts));
  115. } else {
  116. $host = $_SERVER['HTTP_X_FORWARDED_HOST'];
  117. }
  118. } else {
  119. if (isset($_SERVER['HTTP_HOST'])) {
  120. $host = $_SERVER['HTTP_HOST'];
  121. } else if (isset($_SERVER['SERVER_NAME'])) {
  122. $host = $_SERVER['SERVER_NAME'];
  123. }
  124. }
  125. return $host;
  126. }
  127. /**
  128. * Returns the overwritehost setting from the config if set and
  129. * if the overwrite condition is met
  130. * @return string|null overwritehost value or null if not defined or the defined condition
  131. * isn't met
  132. */
  133. public static function getOverwriteHost() {
  134. if(OC_Config::getValue('overwritehost', '') !== '' and self::isOverwriteCondition()) {
  135. return OC_Config::getValue('overwritehost');
  136. }
  137. return null;
  138. }
  139. /**
  140. * Returns the server host from the headers, or the first configured
  141. * trusted domain if the host isn't in the trusted list
  142. * @return string the server host
  143. *
  144. * Returns the server host, even if the website uses one or more
  145. * reverse proxies
  146. */
  147. public static function serverHost() {
  148. if (OC::$CLI && defined('PHPUNIT_RUN')) {
  149. return 'localhost';
  150. }
  151. // overwritehost is always trusted
  152. $host = self::getOverwriteHost();
  153. if ($host !== null) {
  154. return $host;
  155. }
  156. // get the host from the headers
  157. $host = self::insecureServerHost();
  158. // Verify that the host is a trusted domain if the trusted domains
  159. // are defined
  160. // If no trusted domain is provided the first trusted domain is returned
  161. if (self::isTrustedDomain($host)) {
  162. return $host;
  163. } else {
  164. $trustedList = \OC_Config::getValue('trusted_domains', array(''));
  165. return $trustedList[0];
  166. }
  167. }
  168. /**
  169. * Returns the server protocol
  170. * @return string the server protocol
  171. *
  172. * Returns the server protocol. It respects reverse proxy servers and load balancers
  173. */
  174. public static function serverProtocol() {
  175. if(OC_Config::getValue('overwriteprotocol', '') !== '' and self::isOverwriteCondition('protocol')) {
  176. return OC_Config::getValue('overwriteprotocol');
  177. }
  178. if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
  179. $proto = strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']);
  180. // Verify that the protocol is always HTTP or HTTPS
  181. // default to http if an invalid value is provided
  182. return $proto === 'https' ? 'https' : 'http';
  183. }
  184. if (isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') {
  185. return 'https';
  186. }
  187. return 'http';
  188. }
  189. /**
  190. * Returns the request uri
  191. * @return string the request uri
  192. *
  193. * Returns the request uri, even if the website uses one or more
  194. * reverse proxies
  195. * @return string
  196. */
  197. public static function requestUri() {
  198. $uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
  199. if (OC_Config::getValue('overwritewebroot', '') !== '' and self::isOverwriteCondition()) {
  200. $uri = self::scriptName() . substr($uri, strlen($_SERVER['SCRIPT_NAME']));
  201. }
  202. return $uri;
  203. }
  204. /**
  205. * Returns the script name
  206. * @return string the script name
  207. *
  208. * Returns the script name, even if the website uses one or more
  209. * reverse proxies
  210. */
  211. public static function scriptName() {
  212. $name = $_SERVER['SCRIPT_NAME'];
  213. $overwriteWebRoot = OC_Config::getValue('overwritewebroot', '');
  214. if ($overwriteWebRoot !== '' and self::isOverwriteCondition()) {
  215. $serverroot = str_replace("\\", '/', substr(__DIR__, 0, -strlen('lib/private/')));
  216. $suburi = str_replace("\\", "/", substr(realpath($_SERVER["SCRIPT_FILENAME"]), strlen($serverroot)));
  217. $name = '/' . ltrim($overwriteWebRoot . $suburi, '/');
  218. }
  219. return $name;
  220. }
  221. /**
  222. * get Path info from request
  223. * @return string Path info or false when not found
  224. */
  225. public static function getPathInfo() {
  226. if (array_key_exists('PATH_INFO', $_SERVER)) {
  227. $path_info = $_SERVER['PATH_INFO'];
  228. }else{
  229. $path_info = self::getRawPathInfo();
  230. // following is taken from \Sabre\DAV\URLUtil::decodePathSegment
  231. $path_info = rawurldecode($path_info);
  232. $encoding = mb_detect_encoding($path_info, array('UTF-8', 'ISO-8859-1'));
  233. switch($encoding) {
  234. case 'ISO-8859-1' :
  235. $path_info = utf8_encode($path_info);
  236. }
  237. // end copy
  238. }
  239. return $path_info;
  240. }
  241. /**
  242. * get Path info from request, not urldecoded
  243. * @throws Exception
  244. * @return string Path info or false when not found
  245. */
  246. public static function getRawPathInfo() {
  247. $requestUri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
  248. // remove too many leading slashes - can be caused by reverse proxy configuration
  249. if (strpos($requestUri, '/') === 0) {
  250. $requestUri = '/' . ltrim($requestUri, '/');
  251. }
  252. // Remove the query string from REQUEST_URI
  253. if ($pos = strpos($requestUri, '?')) {
  254. $requestUri = substr($requestUri, 0, $pos);
  255. }
  256. $scriptName = $_SERVER['SCRIPT_NAME'];
  257. $path_info = $requestUri;
  258. // strip off the script name's dir and file name
  259. list($path, $name) = \Sabre\DAV\URLUtil::splitPath($scriptName);
  260. if (!empty($path)) {
  261. if( $path === $path_info || strpos($path_info, $path.'/') === 0) {
  262. $path_info = substr($path_info, strlen($path));
  263. } else {
  264. throw new Exception("The requested uri($requestUri) cannot be processed by the script '$scriptName')");
  265. }
  266. }
  267. if (strpos($path_info, '/'.$name) === 0) {
  268. $path_info = substr($path_info, strlen($name) + 1);
  269. }
  270. if (strpos($path_info, $name) === 0) {
  271. $path_info = substr($path_info, strlen($name));
  272. }
  273. if($path_info === '/'){
  274. return '';
  275. } else {
  276. return $path_info;
  277. }
  278. }
  279. /**
  280. * Check if the requester sent along an mtime
  281. * @return false or an mtime
  282. */
  283. static public function hasModificationTime () {
  284. if (isset($_SERVER['HTTP_X_OC_MTIME'])) {
  285. return $_SERVER['HTTP_X_OC_MTIME'];
  286. } else {
  287. return false;
  288. }
  289. }
  290. /**
  291. * Checks whether the user agent matches a given regex
  292. * @param string|array $agent agent name or array of agent names
  293. * @return boolean true if at least one of the given agent matches,
  294. * false otherwise
  295. */
  296. static public function isUserAgent($agent) {
  297. if (!is_array($agent)) {
  298. $agent = array($agent);
  299. }
  300. foreach ($agent as $regex) {
  301. if (preg_match($regex, $_SERVER['HTTP_USER_AGENT'])) {
  302. return true;
  303. }
  304. }
  305. return false;
  306. }
  307. }