Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 49 |
|
0.00% |
0 / 3 |
CRAP | |
0.00% |
0 / 1 |
StreamFile | |
0.00% |
0 / 48 |
|
0.00% |
0 / 3 |
380 | |
0.00% |
0 / 1 |
stream | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
6 | |||
setHeader | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
contentTypeFromPath | |
0.00% |
0 / 34 |
|
0.00% |
0 / 1 |
272 |
1 | <?php |
2 | |
3 | /** |
4 | * Functions related to the output of file content. |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation; either version 2 of the License, or |
9 | * (at your option) any later version. |
10 | * |
11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU General Public License along |
17 | * with this program; if not, write to the Free Software Foundation, Inc., |
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
19 | * http://www.gnu.org/copyleft/gpl.html |
20 | * |
21 | * @file |
22 | */ |
23 | |
24 | namespace MediaWiki\Output; |
25 | |
26 | use InvalidArgumentException; |
27 | use MediaWiki\Context\RequestContext; |
28 | use MediaWiki\MainConfigNames; |
29 | use MediaWiki\MediaWikiServices; |
30 | use UploadBase; |
31 | use Wikimedia\FileBackend\FileBackend; |
32 | use Wikimedia\FileBackend\HTTPFileStreamer; |
33 | |
34 | /** |
35 | * Functions related to the output of file content |
36 | */ |
37 | class StreamFile { |
38 | |
39 | private const UNKNOWN_CONTENT_TYPE = 'unknown/unknown'; |
40 | |
41 | /** |
42 | * Stream a file to the browser, adding all the headings and fun stuff. |
43 | * Headers sent include: Content-type, Content-Length, Last-Modified, |
44 | * and Content-Disposition. |
45 | * |
46 | * @param string $fname Full name and path of the file to stream |
47 | * @param array $headers Any additional headers to send if the file exists |
48 | * @param bool $sendErrors Send error messages if errors occur (like 404) |
49 | * @param array $optHeaders HTTP request header map (e.g. "range") (use lowercase keys) |
50 | * @param int $flags Bitfield of STREAM_* constants |
51 | * @return bool Success |
52 | */ |
53 | public static function stream( |
54 | $fname, |
55 | $headers = [], |
56 | $sendErrors = true, |
57 | $optHeaders = [], |
58 | $flags = 0 |
59 | ) { |
60 | if ( FileBackend::isStoragePath( $fname ) ) { |
61 | throw new InvalidArgumentException( __FUNCTION__ . " given storage path '$fname'." ); |
62 | } |
63 | |
64 | $streamer = new HTTPFileStreamer( |
65 | $fname, |
66 | [ |
67 | 'obResetFunc' => 'wfResetOutputBuffers', |
68 | 'streamMimeFunc' => [ __CLASS__, 'contentTypeFromPath' ], |
69 | 'headerFunc' => [ __CLASS__, 'setHeader' ], |
70 | ] |
71 | ); |
72 | |
73 | return $streamer->stream( $headers, $sendErrors, $optHeaders, $flags ); |
74 | } |
75 | |
76 | /** |
77 | * @param string $header |
78 | * |
79 | * @internal |
80 | */ |
81 | public static function setHeader( $header ) { |
82 | RequestContext::getMain()->getRequest()->response()->header( $header ); |
83 | } |
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 prevention checks |
90 | * @return null|string |
91 | */ |
92 | public static function contentTypeFromPath( $filename, $safe = true ) { |
93 | // NOTE: TrivialMimeDetection is forced by ThumbnailEntryPoint. When this |
94 | // code is moved to a non-static method in a service object, we can no |
95 | // longer rely on that. |
96 | $trivialMimeDetection = MediaWikiServices::getInstance()->getMainConfig() |
97 | ->get( MainConfigNames::TrivialMimeDetection ); |
98 | |
99 | $ext = strrchr( $filename, '.' ); |
100 | $ext = $ext ? strtolower( substr( $ext, 1 ) ) : ''; |
101 | |
102 | # trivial detection by file extension, |
103 | # used for thumbnails (thumb.php) |
104 | if ( $trivialMimeDetection ) { |
105 | switch ( $ext ) { |
106 | case 'gif': |
107 | return 'image/gif'; |
108 | case 'png': |
109 | return 'image/png'; |
110 | case 'jpg': |
111 | case 'jpeg': |
112 | return 'image/jpeg'; |
113 | case 'webp': |
114 | return 'image/webp'; |
115 | } |
116 | |
117 | return self::UNKNOWN_CONTENT_TYPE; |
118 | } |
119 | |
120 | $magic = MediaWikiServices::getInstance()->getMimeAnalyzer(); |
121 | // Use the extension only, rather than magic numbers, to avoid opening |
122 | // up vulnerabilities due to uploads of files with allowed extensions |
123 | // but disallowed types. |
124 | $type = $magic->getMimeTypeFromExtensionOrNull( $ext ); |
125 | |
126 | /** |
127 | * Double-check some security settings that were done on upload but might |
128 | * have changed since. |
129 | */ |
130 | if ( $safe ) { |
131 | $mainConfig = MediaWikiServices::getInstance()->getMainConfig(); |
132 | $prohibitedFileExtensions = $mainConfig->get( MainConfigNames::ProhibitedFileExtensions ); |
133 | $checkFileExtensions = $mainConfig->get( MainConfigNames::CheckFileExtensions ); |
134 | $strictFileExtensions = $mainConfig->get( MainConfigNames::StrictFileExtensions ); |
135 | $fileExtensions = $mainConfig->get( MainConfigNames::FileExtensions ); |
136 | $verifyMimeType = $mainConfig->get( MainConfigNames::VerifyMimeType ); |
137 | $mimeTypeExclusions = $mainConfig->get( MainConfigNames::MimeTypeExclusions ); |
138 | [ , $extList ] = UploadBase::splitExtensions( $filename ); |
139 | if ( UploadBase::checkFileExtensionList( $extList, $prohibitedFileExtensions ) ) { |
140 | return self::UNKNOWN_CONTENT_TYPE; |
141 | } |
142 | if ( |
143 | $checkFileExtensions && |
144 | $strictFileExtensions && |
145 | !UploadBase::checkFileExtensionList( $extList, $fileExtensions ) |
146 | ) { |
147 | return self::UNKNOWN_CONTENT_TYPE; |
148 | } |
149 | if ( $verifyMimeType && $type !== null && in_array( strtolower( $type ), $mimeTypeExclusions ) ) { |
150 | return self::UNKNOWN_CONTENT_TYPE; |
151 | } |
152 | } |
153 | return $type; |
154 | } |
155 | } |
156 | |
157 | /** @deprecated class alias since 1.41 */ |
158 | class_alias( StreamFile::class, 'StreamFile' ); |