response.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. <?php
  2. /**
  3. * Copyright (c) 2011 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_Response {
  9. const STATUS_FOUND = 304;
  10. const STATUS_NOT_MODIFIED = 304;
  11. const STATUS_TEMPORARY_REDIRECT = 307;
  12. const STATUS_BAD_REQUEST = 400;
  13. const STATUS_NOT_FOUND = 404;
  14. const STATUS_INTERNAL_SERVER_ERROR = 500;
  15. const STATUS_SERVICE_UNAVAILABLE = 503;
  16. /**
  17. * Enable response caching by sending correct HTTP headers
  18. * @param integer $cache_time time to cache the response
  19. * >0 cache time in seconds
  20. * 0 and <0 enable default browser caching
  21. * null cache indefinitly
  22. */
  23. static public function enableCaching($cache_time = null) {
  24. if (is_numeric($cache_time)) {
  25. header('Pragma: public');// enable caching in IE
  26. if ($cache_time > 0) {
  27. self::setExpiresHeader('PT'.$cache_time.'S');
  28. header('Cache-Control: max-age='.$cache_time.', must-revalidate');
  29. }
  30. else {
  31. self::setExpiresHeader(0);
  32. header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
  33. }
  34. }
  35. else {
  36. header('Cache-Control: cache');
  37. header('Pragma: cache');
  38. }
  39. }
  40. /**
  41. * disable browser caching
  42. * @see enableCaching with cache_time = 0
  43. */
  44. static public function disableCaching() {
  45. self::enableCaching(0);
  46. }
  47. /**
  48. * Set response status
  49. * @param int $status a HTTP status code, see also the STATUS constants
  50. */
  51. static public function setStatus($status) {
  52. $protocol = $_SERVER['SERVER_PROTOCOL'];
  53. switch($status) {
  54. case self::STATUS_NOT_MODIFIED:
  55. $status = $status . ' Not Modified';
  56. break;
  57. case self::STATUS_TEMPORARY_REDIRECT:
  58. if ($protocol == 'HTTP/1.1') {
  59. $status = $status . ' Temporary Redirect';
  60. break;
  61. } else {
  62. $status = self::STATUS_FOUND;
  63. // fallthrough
  64. }
  65. case self::STATUS_FOUND;
  66. $status = $status . ' Found';
  67. break;
  68. case self::STATUS_NOT_FOUND;
  69. $status = $status . ' Not Found';
  70. break;
  71. case self::STATUS_INTERNAL_SERVER_ERROR;
  72. $status = $status . ' Internal Server Error';
  73. break;
  74. case self::STATUS_SERVICE_UNAVAILABLE;
  75. $status = $status . ' Service Unavailable';
  76. break;
  77. }
  78. header($protocol.' '.$status);
  79. }
  80. /**
  81. * Send redirect response
  82. * @param string $location to redirect to
  83. */
  84. static public function redirect($location) {
  85. self::setStatus(self::STATUS_TEMPORARY_REDIRECT);
  86. header('Location: '.$location);
  87. }
  88. /**
  89. * Set reponse expire time
  90. * @param string|DateTime $expires date-time when the response expires
  91. * string for DateInterval from now
  92. * DateTime object when to expire response
  93. */
  94. static public function setExpiresHeader($expires) {
  95. if (is_string($expires) && $expires[0] == 'P') {
  96. $interval = $expires;
  97. $expires = new DateTime('now');
  98. $expires->add(new DateInterval($interval));
  99. }
  100. if ($expires instanceof DateTime) {
  101. $expires->setTimezone(new DateTimeZone('GMT'));
  102. $expires = $expires->format(DateTime::RFC2822);
  103. }
  104. header('Expires: '.$expires);
  105. }
  106. /**
  107. * Checks and set ETag header, when the request matches sends a
  108. * 'not modified' response
  109. * @param string $etag token to use for modification check
  110. */
  111. static public function setETagHeader($etag) {
  112. if (empty($etag)) {
  113. return;
  114. }
  115. $etag = '"'.$etag.'"';
  116. if (isset($_SERVER['HTTP_IF_NONE_MATCH']) &&
  117. trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag) {
  118. self::setStatus(self::STATUS_NOT_MODIFIED);
  119. exit;
  120. }
  121. header('ETag: '.$etag);
  122. }
  123. /**
  124. * Checks and set Last-Modified header, when the request matches sends a
  125. * 'not modified' response
  126. * @param int|DateTime|string $lastModified time when the reponse was last modified
  127. */
  128. static public function setLastModifiedHeader($lastModified) {
  129. if (empty($lastModified)) {
  130. return;
  131. }
  132. if (is_int($lastModified)) {
  133. $lastModified = gmdate(DateTime::RFC2822, $lastModified);
  134. }
  135. if ($lastModified instanceof DateTime) {
  136. $lastModified = $lastModified->format(DateTime::RFC2822);
  137. }
  138. if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) &&
  139. trim($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $lastModified) {
  140. self::setStatus(self::STATUS_NOT_MODIFIED);
  141. exit;
  142. }
  143. header('Last-Modified: '.$lastModified);
  144. }
  145. /**
  146. * Sets the content disposition header (with possible workarounds)
  147. * @param string $filename file name
  148. * @param string $type disposition type, either 'attachment' or 'inline'
  149. */
  150. static public function setContentDispositionHeader( $filename, $type = 'attachment' ) {
  151. if (\OC::$server->getRequest()->isUserAgent(
  152. [
  153. \OC\AppFramework\Http\Request::USER_AGENT_IE,
  154. \OC\AppFramework\Http\Request::USER_AGENT_ANDROID_MOBILE_CHROME,
  155. \OC\AppFramework\Http\Request::USER_AGENT_FREEBOX,
  156. ])) {
  157. header( 'Content-Disposition: ' . rawurlencode($type) . '; filename="' . rawurlencode( $filename ) . '"' );
  158. } else {
  159. header( 'Content-Disposition: ' . rawurlencode($type) . '; filename*=UTF-8\'\'' . rawurlencode( $filename )
  160. . '; filename="' . rawurlencode( $filename ) . '"' );
  161. }
  162. }
  163. /**
  164. * Send file as response, checking and setting caching headers
  165. * @param string $filepath of file to send
  166. */
  167. static public function sendFile($filepath) {
  168. $fp = fopen($filepath, 'rb');
  169. if ($fp) {
  170. self::setLastModifiedHeader(filemtime($filepath));
  171. self::setETagHeader(md5_file($filepath));
  172. header('Content-Length: '.filesize($filepath));
  173. fpassthru($fp);
  174. }
  175. else {
  176. self::setStatus(self::STATUS_NOT_FOUND);
  177. }
  178. }
  179. /**
  180. * This function adds some security related headers to all requests served via base.php
  181. * The implementation of this function has to happen here to ensure that all third-party
  182. * components (e.g. SabreDAV) also benefit from this headers.
  183. */
  184. public static function addSecurityHeaders() {
  185. header('X-XSS-Protection: 1; mode=block'); // Enforce browser based XSS filters
  186. header('X-Content-Type-Options: nosniff'); // Disable sniffing the content type for IE
  187. // iFrame Restriction Policy
  188. $xFramePolicy = OC_Config::getValue('xframe_restriction', true);
  189. if ($xFramePolicy) {
  190. header('X-Frame-Options: Sameorigin'); // Disallow iFraming from other domains
  191. }
  192. /**
  193. * FIXME: Content Security Policy for legacy ownCloud components. This
  194. * can be removed once \OCP\AppFramework\Http\Response from the AppFramework
  195. * is used everywhere.
  196. * @see \OCP\AppFramework\Http\Response::getHeaders
  197. */
  198. $policy = 'default-src \'self\'; '
  199. . 'script-src \'self\' \'unsafe-eval\'; '
  200. . 'style-src \'self\' \'unsafe-inline\'; '
  201. . 'frame-src *; '
  202. . 'img-src *; '
  203. . 'font-src \'self\' data:; '
  204. . 'media-src *; '
  205. . 'connect-src *';
  206. header('Content-Security-Policy:' . $policy);
  207. // https://developers.google.com/webmasters/control-crawl-index/docs/robots_meta_tag
  208. header('X-Robots-Tag: none');
  209. }
  210. }