StreamFile.php 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. <?php
  2. /**
  3. * Functions related to the output of file content.
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along
  16. * with this program; if not, write to the Free Software Foundation, Inc.,
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. * http://www.gnu.org/copyleft/gpl.html
  19. *
  20. * @file
  21. */
  22. /**
  23. * Functions related to the output of file content
  24. */
  25. class StreamFile {
  26. // Do not send any HTTP headers unless requested by caller (e.g. body only)
  27. /** @deprecated since 1.34 */
  28. const STREAM_HEADLESS = HTTPFileStreamer::STREAM_HEADLESS;
  29. // Do not try to tear down any PHP output buffers
  30. /** @deprecated since 1.34 */
  31. const STREAM_ALLOW_OB = HTTPFileStreamer::STREAM_ALLOW_OB;
  32. /**
  33. * Stream a file to the browser, adding all the headings and fun stuff.
  34. * Headers sent include: Content-type, Content-Length, Last-Modified,
  35. * and Content-Disposition.
  36. *
  37. * @param string $fname Full name and path of the file to stream
  38. * @param array $headers Any additional headers to send if the file exists
  39. * @param bool $sendErrors Send error messages if errors occur (like 404)
  40. * @param array $optHeaders HTTP request header map (e.g. "range") (use lowercase keys)
  41. * @param int $flags Bitfield of STREAM_* constants
  42. * @throws MWException
  43. * @return bool Success
  44. */
  45. public static function stream(
  46. $fname, $headers = [], $sendErrors = true, $optHeaders = [], $flags = 0
  47. ) {
  48. if ( FileBackend::isStoragePath( $fname ) ) { // sanity
  49. throw new InvalidArgumentException( __FUNCTION__ . " given storage path '$fname'." );
  50. }
  51. $streamer = new HTTPFileStreamer(
  52. $fname,
  53. [
  54. 'obResetFunc' => 'wfResetOutputBuffers',
  55. 'streamMimeFunc' => [ __CLASS__, 'contentTypeFromPath' ]
  56. ]
  57. );
  58. return $streamer->stream( $headers, $sendErrors, $optHeaders, $flags );
  59. }
  60. /**
  61. * Send out a standard 404 message for a file
  62. *
  63. * @param string $fname Full name and path of the file to stream
  64. * @param int $flags Bitfield of STREAM_* constants
  65. * @since 1.24
  66. * @deprecated since 1.34, use HTTPFileStreamer::send404Message() instead
  67. */
  68. public static function send404Message( $fname, $flags = 0 ) {
  69. wfDeprecated( __METHOD__, '1.34' );
  70. HTTPFileStreamer::send404Message( $fname, $flags );
  71. }
  72. /**
  73. * Convert a Range header value to an absolute (start, end) range tuple
  74. *
  75. * @param string $range Range header value
  76. * @param int $size File size
  77. * @return array|string Returns error string on failure (start, end, length)
  78. * @since 1.24
  79. * @deprecated since 1.34, use HTTPFileStreamer::parseRange() instead
  80. */
  81. public static function parseRange( $range, $size ) {
  82. wfDeprecated( __METHOD__, '1.34' );
  83. return HTTPFileStreamer::parseRange( $range, $size );
  84. }
  85. /**
  86. * Determine the file type of a file based on the path
  87. *
  88. * @param string $filename Storage path or file system path
  89. * @param bool $safe Whether to do retroactive upload blacklist checks
  90. * @return null|string
  91. */
  92. public static function contentTypeFromPath( $filename, $safe = true ) {
  93. global $wgTrivialMimeDetection;
  94. $ext = strrchr( $filename, '.' );
  95. $ext = $ext === false ? '' : strtolower( substr( $ext, 1 ) );
  96. # trivial detection by file extension,
  97. # used for thumbnails (thumb.php)
  98. if ( $wgTrivialMimeDetection ) {
  99. switch ( $ext ) {
  100. case 'gif':
  101. return 'image/gif';
  102. case 'png':
  103. return 'image/png';
  104. case 'jpg':
  105. case 'jpeg':
  106. return 'image/jpeg';
  107. }
  108. return 'unknown/unknown';
  109. }
  110. $magic = MediaWiki\MediaWikiServices::getInstance()->getMimeAnalyzer();
  111. // Use the extension only, rather than magic numbers, to avoid opening
  112. // up vulnerabilities due to uploads of files with allowed extensions
  113. // but disallowed types.
  114. $type = $magic->guessTypesForExtension( $ext );
  115. /**
  116. * Double-check some security settings that were done on upload but might
  117. * have changed since.
  118. */
  119. if ( $safe ) {
  120. global $wgFileBlacklist, $wgCheckFileExtensions, $wgStrictFileExtensions,
  121. $wgFileExtensions, $wgVerifyMimeType, $wgMimeTypeBlacklist;
  122. list( , $extList ) = UploadBase::splitExtensions( $filename );
  123. if ( UploadBase::checkFileExtensionList( $extList, $wgFileBlacklist ) ) {
  124. return 'unknown/unknown';
  125. }
  126. if ( $wgCheckFileExtensions && $wgStrictFileExtensions
  127. && !UploadBase::checkFileExtensionList( $extList, $wgFileExtensions )
  128. ) {
  129. return 'unknown/unknown';
  130. }
  131. if ( $wgVerifyMimeType && in_array( strtolower( $type ), $wgMimeTypeBlacklist ) ) {
  132. return 'unknown/unknown';
  133. }
  134. }
  135. return $type;
  136. }
  137. }