Index: trunk/phase3/thumb.php |
— | — | @@ -15,8 +15,6 @@ |
16 | 16 | |
17 | 17 | $wgTrivialMimeDetection = true; //don't use fancy mime detection, just check the file extension for jpg/gif/png. |
18 | 18 | |
19 | | -require_once( "$IP/includes/StreamFile.php" ); |
20 | | - |
21 | 19 | wfThumbMain(); |
22 | 20 | wfLogProfilingData(); |
23 | 21 | |
— | — | @@ -126,7 +124,7 @@ |
127 | 125 | $thumbPath = $img->getThumbPath( $thumbName ); |
128 | 126 | |
129 | 127 | if ( is_file( $thumbPath ) ) { |
130 | | - wfStreamFile( $thumbPath, $headers ); |
| 128 | + StreamFile::stream( $thumbPath, $headers ); |
131 | 129 | wfProfileOut( __METHOD__ ); |
132 | 130 | return; |
133 | 131 | } |
— | — | @@ -155,7 +153,7 @@ |
156 | 154 | $errorMsg = wfMsgHtml( 'thumbnail_error', 'Image was not scaled, ' . |
157 | 155 | 'is the requested width bigger than the source?' ); |
158 | 156 | } else { |
159 | | - wfStreamFile( $thumb->getPath(), $headers ); |
| 157 | + StreamFile::stream( $thumb->getPath(), $headers ); |
160 | 158 | } |
161 | 159 | if ( $errorMsg !== false ) { |
162 | 160 | wfThumbError( 500, $errorMsg ); |
Index: trunk/phase3/includes/GlobalFunctions.php |
— | — | @@ -3197,6 +3197,14 @@ |
3198 | 3198 | } |
3199 | 3199 | |
3200 | 3200 | /** |
| 3201 | + * Stream a file to the browser. Back-compat alias for StreamFile::stream() |
| 3202 | + * @deprecated since 1.19 |
| 3203 | + */ |
| 3204 | +function wfStreamFile( $fname, $headers = array() ) { |
| 3205 | + StreamFile::stream( $fname, $headers ); |
| 3206 | +} |
| 3207 | + |
| 3208 | +/** |
3201 | 3209 | * Should low-performance queries be disabled? |
3202 | 3210 | * |
3203 | 3211 | * @return Boolean |
Index: trunk/phase3/includes/AutoLoader.php |
— | — | @@ -213,6 +213,7 @@ |
214 | 214 | 'SquidPurgeClient' => 'includes/SquidPurgeClient.php', |
215 | 215 | 'SquidPurgeClientPool' => 'includes/SquidPurgeClient.php', |
216 | 216 | 'Status' => 'includes/Status.php', |
| 217 | + 'StreamFile' => 'includes/StreamFile.php', |
217 | 218 | 'StringUtils' => 'includes/StringUtils.php', |
218 | 219 | 'StubContLang' => 'includes/StubObject.php', |
219 | 220 | 'StubObject' => 'includes/StubObject.php', |
Index: trunk/phase3/includes/StreamFile.php |
— | — | @@ -4,119 +4,122 @@ |
5 | 5 | * |
6 | 6 | * @file |
7 | 7 | */ |
| 8 | +class StreamFile { |
| 9 | + /** |
| 10 | + * Stream a file to the browser, adding all the headings and fun stuff |
| 11 | + * @param $fname string Full name and path of the file to stream |
| 12 | + * @param $headers array Any additional headers to send |
| 13 | + */ |
| 14 | + public static function stream( $fname, $headers = array(), $request = null ) { |
| 15 | + wfSuppressWarnings(); |
| 16 | + $stat = stat( $fname ); |
| 17 | + wfRestoreWarnings(); |
| 18 | + if ( !$stat ) { |
| 19 | + header( 'HTTP/1.0 404 Not Found' ); |
| 20 | + header( 'Cache-Control: no-cache' ); |
| 21 | + header( 'Content-Type: text/html; charset=utf-8' ); |
| 22 | + $encFile = htmlspecialchars( $fname ); |
| 23 | + $encScript = htmlspecialchars( $_SERVER['SCRIPT_NAME'] ); |
| 24 | + echo "<html><body> |
| 25 | + <h1>File not found</h1> |
| 26 | + <p>Although this PHP script ($encScript) exists, the file requested for output |
| 27 | + ($encFile) does not.</p> |
| 28 | + </body></html> |
| 29 | + "; |
| 30 | + return; |
| 31 | + } |
8 | 32 | |
9 | | -/** |
10 | | - * @param $fname string |
11 | | - * @param $headers array |
12 | | - */ |
13 | | -function wfStreamFile( $fname, $headers = array() ) { |
14 | | - wfSuppressWarnings(); |
15 | | - $stat = stat( $fname ); |
16 | | - wfRestoreWarnings(); |
17 | | - if ( !$stat ) { |
18 | | - header( 'HTTP/1.0 404 Not Found' ); |
19 | | - header( 'Cache-Control: no-cache' ); |
20 | | - header( 'Content-Type: text/html; charset=utf-8' ); |
21 | | - $encFile = htmlspecialchars( $fname ); |
22 | | - $encScript = htmlspecialchars( $_SERVER['SCRIPT_NAME'] ); |
23 | | - echo "<html><body> |
24 | | -<h1>File not found</h1> |
25 | | -<p>Although this PHP script ($encScript) exists, the file requested for output |
26 | | -($encFile) does not.</p> |
27 | | -</body></html> |
28 | | -"; |
29 | | - return; |
30 | | - } |
| 33 | + header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', $stat['mtime'] ) . ' GMT' ); |
31 | 34 | |
32 | | - header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', $stat['mtime'] ) . ' GMT' ); |
| 35 | + // Cancel output buffering and gzipping if set |
| 36 | + wfResetOutputBuffers(); |
33 | 37 | |
34 | | - // Cancel output buffering and gzipping if set |
35 | | - wfResetOutputBuffers(); |
| 38 | + $type = self::getType( $fname ); |
| 39 | + if ( $type && $type != 'unknown/unknown' ) { |
| 40 | + header( "Content-type: $type" ); |
| 41 | + } else { |
| 42 | + header( 'Content-type: application/x-wiki' ); |
| 43 | + } |
36 | 44 | |
37 | | - $type = wfGetType( $fname ); |
38 | | - if ( $type and $type!="unknown/unknown") { |
39 | | - header("Content-type: $type"); |
40 | | - } else { |
41 | | - header('Content-type: application/x-wiki'); |
42 | | - } |
43 | | - |
44 | | - // Don't stream it out as text/html if there was a PHP error |
45 | | - if ( headers_sent() ) { |
46 | | - echo "Headers already sent, terminating.\n"; |
47 | | - return; |
48 | | - } |
49 | | - |
50 | | - global $wgLanguageCode; |
51 | | - header( "Content-Disposition: inline;filename*=utf-8'$wgLanguageCode'" . urlencode( basename( $fname ) ) ); |
52 | | - |
53 | | - foreach ( $headers as $header ) { |
54 | | - header( $header ); |
55 | | - } |
56 | | - |
57 | | - if ( !empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) { |
58 | | - $modsince = preg_replace( '/;.*$/', '', $_SERVER['HTTP_IF_MODIFIED_SINCE'] ); |
59 | | - $sinceTime = strtotime( $modsince ); |
60 | | - if ( $stat['mtime'] <= $sinceTime ) { |
61 | | - ini_set('zlib.output_compression', 0); |
62 | | - header( "HTTP/1.0 304 Not Modified" ); |
| 45 | + // Don't stream it out as text/html if there was a PHP error |
| 46 | + if ( headers_sent() ) { |
| 47 | + echo "Headers already sent, terminating.\n"; |
63 | 48 | return; |
64 | 49 | } |
65 | | - } |
66 | 50 | |
67 | | - header( 'Content-Length: ' . $stat['size'] ); |
| 51 | + global $wgLanguageCode; |
| 52 | + header( "Content-Disposition: inline;filename*=utf-8'$wgLanguageCode'" . urlencode( basename( $fname ) ) ); |
68 | 53 | |
69 | | - readfile( $fname ); |
70 | | -} |
| 54 | + foreach ( $headers as $header ) { |
| 55 | + header( $header ); |
| 56 | + } |
71 | 57 | |
72 | | -/** |
73 | | - * @param $filename string |
74 | | - * @param $safe bool |
75 | | - * @return null|string |
76 | | - */ |
77 | | -function wfGetType( $filename, $safe = true ) { |
78 | | - global $wgTrivialMimeDetection; |
| 58 | + if ( !empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) { |
| 59 | + $modsince = preg_replace( '/;.*$/', '', $_SERVER['HTTP_IF_MODIFIED_SINCE'] ); |
| 60 | + $sinceTime = strtotime( $modsince ); |
| 61 | + if ( $stat['mtime'] <= $sinceTime ) { |
| 62 | + ini_set( 'zlib.output_compression', 0 ); |
| 63 | + header( "HTTP/1.0 304 Not Modified" ); |
| 64 | + return; |
| 65 | + } |
| 66 | + } |
79 | 67 | |
80 | | - $ext = strrchr($filename, '.'); |
81 | | - $ext = $ext === false ? '' : strtolower( substr( $ext, 1 ) ); |
| 68 | + header( 'Content-Length: ' . $stat['size'] ); |
82 | 69 | |
83 | | - # trivial detection by file extension, |
84 | | - # used for thumbnails (thumb.php) |
85 | | - if ($wgTrivialMimeDetection) { |
86 | | - switch ($ext) { |
87 | | - case 'gif': return 'image/gif'; |
88 | | - case 'png': return 'image/png'; |
89 | | - case 'jpg': return 'image/jpeg'; |
90 | | - case 'jpeg': return 'image/jpeg'; |
91 | | - } |
92 | | - |
93 | | - return 'unknown/unknown'; |
| 70 | + readfile( $fname ); |
94 | 71 | } |
95 | | - |
96 | | - $magic = MimeMagic::singleton(); |
97 | | - // Use the extension only, rather than magic numbers, to avoid opening |
98 | | - // up vulnerabilities due to uploads of files with allowed extensions |
99 | | - // but disallowed types. |
100 | | - $type = $magic->guessTypesForExtension( $ext ); |
101 | 72 | |
102 | 73 | /** |
103 | | - * Double-check some security settings that were done on upload but might |
104 | | - * have changed since. |
| 74 | + * Determine the filetype we're dealing with |
| 75 | + * @param $filename string |
| 76 | + * @param $safe bool |
| 77 | + * @return null|string |
105 | 78 | */ |
106 | | - if ( $safe ) { |
107 | | - global $wgFileBlacklist, $wgCheckFileExtensions, $wgStrictFileExtensions, |
108 | | - $wgFileExtensions, $wgVerifyMimeType, $wgMimeTypeBlacklist; |
109 | | - list( , $extList ) = UploadBase::splitExtensions( $filename ); |
110 | | - if ( UploadBase::checkFileExtensionList( $extList, $wgFileBlacklist ) ) { |
| 79 | + private static function getType( $filename, $safe = true ) { |
| 80 | + global $wgTrivialMimeDetection; |
| 81 | + |
| 82 | + $ext = strrchr( $filename, '.' ); |
| 83 | + $ext = $ext === false ? '' : strtolower( substr( $ext, 1 ) ); |
| 84 | + |
| 85 | + # trivial detection by file extension, |
| 86 | + # used for thumbnails (thumb.php) |
| 87 | + if ( $wgTrivialMimeDetection ) { |
| 88 | + switch ( $ext ) { |
| 89 | + case 'gif': return 'image/gif'; |
| 90 | + case 'png': return 'image/png'; |
| 91 | + case 'jpg': return 'image/jpeg'; |
| 92 | + case 'jpeg': return 'image/jpeg'; |
| 93 | + } |
| 94 | + |
111 | 95 | return 'unknown/unknown'; |
112 | 96 | } |
113 | | - if ( $wgCheckFileExtensions && $wgStrictFileExtensions |
114 | | - && !UploadBase::checkFileExtensionList( $extList, $wgFileExtensions ) ) |
115 | | - { |
116 | | - return 'unknown/unknown'; |
| 97 | + |
| 98 | + $magic = MimeMagic::singleton(); |
| 99 | + // Use the extension only, rather than magic numbers, to avoid opening |
| 100 | + // up vulnerabilities due to uploads of files with allowed extensions |
| 101 | + // but disallowed types. |
| 102 | + $type = $magic->guessTypesForExtension( $ext ); |
| 103 | + |
| 104 | + /** |
| 105 | + * Double-check some security settings that were done on upload but might |
| 106 | + * have changed since. |
| 107 | + */ |
| 108 | + if ( $safe ) { |
| 109 | + global $wgFileBlacklist, $wgCheckFileExtensions, $wgStrictFileExtensions, |
| 110 | + $wgFileExtensions, $wgVerifyMimeType, $wgMimeTypeBlacklist; |
| 111 | + list( , $extList ) = UploadBase::splitExtensions( $filename ); |
| 112 | + if ( UploadBase::checkFileExtensionList( $extList, $wgFileBlacklist ) ) { |
| 113 | + return 'unknown/unknown'; |
| 114 | + } |
| 115 | + if ( $wgCheckFileExtensions && $wgStrictFileExtensions |
| 116 | + && !UploadBase::checkFileExtensionList( $extList, $wgFileExtensions ) ) |
| 117 | + { |
| 118 | + return 'unknown/unknown'; |
| 119 | + } |
| 120 | + if ( $wgVerifyMimeType && in_array( strtolower( $type ), $wgMimeTypeBlacklist ) ) { |
| 121 | + return 'unknown/unknown'; |
| 122 | + } |
117 | 123 | } |
118 | | - if ( $wgVerifyMimeType && in_array( strtolower( $type ), $wgMimeTypeBlacklist ) ) { |
119 | | - return 'unknown/unknown'; |
120 | | - } |
| 124 | + return $type; |
121 | 125 | } |
122 | | - return $type; |
123 | 126 | } |
Index: trunk/phase3/includes/specials/SpecialUndelete.php |
— | — | @@ -1013,11 +1013,9 @@ |
1014 | 1014 | $response->header( 'Cache-Control: no-cache, no-store, max-age=0, must-revalidate' ); |
1015 | 1015 | $response->header( 'Pragma: no-cache' ); |
1016 | 1016 | |
1017 | | - global $IP; |
1018 | | - require_once( "$IP/includes/StreamFile.php" ); |
1019 | 1017 | $repo = RepoGroup::singleton()->getLocalRepo(); |
1020 | 1018 | $path = $repo->getZonePath( 'deleted' ) . '/' . $repo->getDeletedHashPath( $key ) . $key; |
1021 | | - wfStreamFile( $path ); |
| 1019 | + StreamFile::stream( $path ); |
1022 | 1020 | } |
1023 | 1021 | |
1024 | 1022 | private function showHistory() { |
Index: trunk/phase3/includes/specials/SpecialRevisiondelete.php |
— | — | @@ -316,12 +316,9 @@ |
317 | 317 | $this->getRequest()->response()->header( 'Cache-Control: no-cache, no-store, max-age=0, must-revalidate' ); |
318 | 318 | $this->getRequest()->response()->header( 'Pragma: no-cache' ); |
319 | 319 | |
320 | | - # Stream the file to the client |
321 | | - global $IP; |
322 | | - require_once( "$IP/includes/StreamFile.php" ); |
323 | 320 | $key = $oimage->getStorageKey(); |
324 | 321 | $path = $repo->getZonePath( 'deleted' ) . '/' . $repo->getDeletedHashPath( $key ) . $key; |
325 | | - wfStreamFile( $path ); |
| 322 | + StreamFile::stream( $path ); |
326 | 323 | } |
327 | 324 | |
328 | 325 | /** |
Index: trunk/phase3/img_auth.php |
— | — | @@ -32,7 +32,6 @@ |
33 | 33 | require ( dirname( __FILE__ ) . '/includes/WebStart.php' ); |
34 | 34 | } |
35 | 35 | wfProfileIn( 'img_auth.php' ); |
36 | | -require_once( dirname( __FILE__ ) . '/includes/StreamFile.php' ); |
37 | 36 | |
38 | 37 | $wgActionPaths[] = $_SERVER['SCRIPT_NAME']; |
39 | 38 | // See if this is a public Wiki (no protections) |
— | — | @@ -95,7 +94,7 @@ |
96 | 95 | |
97 | 96 | // Stream the requested file |
98 | 97 | wfDebugLog( 'img_auth', "Streaming `".$filename."`." ); |
99 | | -wfStreamFile( $filename, array( 'Cache-Control: private', 'Vary: Cookie' ) ); |
| 98 | +StreamFile::stream( $filename, array( 'Cache-Control: private', 'Vary: Cookie' ) ); |
100 | 99 | wfLogProfilingData(); |
101 | 100 | |
102 | 101 | /** |