MediaWiki
REL1_19
|
00001 <?php 00002 00009 define( 'MW_NO_OUTPUT_COMPRESSION', 1 ); 00010 if ( isset( $_SERVER['MW_COMPILED'] ) ) { 00011 require( 'phase3/includes/WebStart.php' ); 00012 } else { 00013 require( dirname( __FILE__ ) . '/includes/WebStart.php' ); 00014 } 00015 00016 // Don't use fancy mime detection, just check the file extension for jpg/gif/png 00017 $wgTrivialMimeDetection = true; 00018 00019 if ( defined( 'THUMB_HANDLER' ) ) { 00020 // Called from thumb_handler.php via 404; extract params from the URI... 00021 wfThumbHandle404(); 00022 } else { 00023 // Called directly, use $_REQUEST params 00024 wfThumbHandleRequest(); 00025 } 00026 wfLogProfilingData(); 00027 00028 //-------------------------------------------------------------------------- 00029 00035 function wfThumbHandleRequest() { 00036 $params = get_magic_quotes_gpc() 00037 ? array_map( 'stripslashes', $_REQUEST ) 00038 : $_REQUEST; 00039 00040 wfStreamThumb( $params ); // stream the thumbnail 00041 } 00042 00048 function wfThumbHandle404() { 00049 # lighttpd puts the original request in REQUEST_URI, while sjs sets 00050 # that to the 404 handler, and puts the original request in REDIRECT_URL. 00051 if ( isset( $_SERVER['REDIRECT_URL'] ) ) { 00052 # The URL is un-encoded, so put it back how it was 00053 $uri = str_replace( "%2F", "/", urlencode( $_SERVER['REDIRECT_URL'] ) ); 00054 # Just get the URI path (REDIRECT_URL is either a full URL or a path) 00055 if ( $uri[0] !== '/' ) { 00056 $bits = wfParseUrl( $uri ); 00057 if ( $bits && isset( $bits['path'] ) ) { 00058 $uri = $bits['path']; 00059 } 00060 } 00061 } else { 00062 $uri = $_SERVER['REQUEST_URI']; 00063 } 00064 00065 $params = wfExtractThumbParams( $uri ); // basic wiki URL param extracting 00066 if ( $params == null ) { 00067 wfThumbError( 404, 'The source file for the specified thumbnail does not exist.' ); 00068 return; 00069 } 00070 00071 wfStreamThumb( $params ); // stream the thumbnail 00072 } 00073 00080 function wfStreamThumb( array $params ) { 00081 wfProfileIn( __METHOD__ ); 00082 00083 $headers = array(); // HTTP headers to send 00084 00085 $fileName = isset( $params['f'] ) ? $params['f'] : ''; 00086 unset( $params['f'] ); 00087 00088 // Backwards compatibility parameters 00089 if ( isset( $params['w'] ) ) { 00090 $params['width'] = $params['w']; 00091 unset( $params['w'] ); 00092 } 00093 if ( isset( $params['p'] ) ) { 00094 $params['page'] = $params['p']; 00095 } 00096 unset( $params['r'] ); // ignore 'r' because we unconditionally pass File::RENDER 00097 00098 // Is this a thumb of an archived file? 00099 $isOld = ( isset( $params['archived'] ) && $params['archived'] ); 00100 unset( $params['archived'] ); 00101 00102 // Some basic input validation 00103 $fileName = strtr( $fileName, '\\/', '__' ); 00104 00105 // Actually fetch the image. Method depends on whether it is archived or not. 00106 if ( $isOld ) { 00107 // Format is <timestamp>!<name> 00108 $bits = explode( '!', $fileName, 2 ); 00109 if ( count( $bits ) != 2 ) { 00110 wfThumbError( 404, wfMsg( 'badtitletext' ) ); 00111 wfProfileOut( __METHOD__ ); 00112 return; 00113 } 00114 $title = Title::makeTitleSafe( NS_FILE, $bits[1] ); 00115 if ( !$title ) { 00116 wfThumbError( 404, wfMsg( 'badtitletext' ) ); 00117 wfProfileOut( __METHOD__ ); 00118 return; 00119 } 00120 $img = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName( $title, $fileName ); 00121 } else { 00122 $img = wfLocalFile( $fileName ); 00123 } 00124 00125 // Check permissions if there are read restrictions 00126 if ( !in_array( 'read', User::getGroupPermissions( array( '*' ) ), true ) ) { 00127 if ( !$img->getTitle()->userCan( 'read' ) ) { 00128 wfThumbError( 403, 'Access denied. You do not have permission to access ' . 00129 'the source file.' ); 00130 wfProfileOut( __METHOD__ ); 00131 return; 00132 } 00133 $headers[] = 'Cache-Control: private'; 00134 $headers[] = 'Vary: Cookie'; 00135 } 00136 00137 // Check the source file storage path 00138 if ( !$img ) { 00139 wfThumbError( 404, wfMsg( 'badtitletext' ) ); 00140 wfProfileOut( __METHOD__ ); 00141 return; 00142 } 00143 if ( !$img->exists() ) { 00144 wfThumbError( 404, 'The source file for the specified thumbnail does not exist.' ); 00145 wfProfileOut( __METHOD__ ); 00146 return; 00147 } 00148 $sourcePath = $img->getPath(); 00149 if ( $sourcePath === false ) { 00150 wfThumbError( 500, 'The source file is not locally accessible.' ); 00151 wfProfileOut( __METHOD__ ); 00152 return; 00153 } 00154 00155 // Check IMS against the source file 00156 // This means that clients can keep a cached copy even after it has been deleted on the server 00157 if ( !empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) { 00158 // Fix IE brokenness 00159 $imsString = preg_replace( '/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"] ); 00160 // Calculate time 00161 wfSuppressWarnings(); 00162 $imsUnix = strtotime( $imsString ); 00163 wfRestoreWarnings(); 00164 $sourceTsUnix = wfTimestamp( TS_UNIX, $img->getTimestamp() ); 00165 if ( $sourceTsUnix <= $imsUnix ) { 00166 header( 'HTTP/1.1 304 Not Modified' ); 00167 wfProfileOut( __METHOD__ ); 00168 return; 00169 } 00170 } 00171 00172 // Stream the file if it exists already... 00173 try { 00174 $thumbName = $img->thumbName( $params ); 00175 if ( strlen( $thumbName ) ) { // valid params? 00176 // For 404 handled thumbnails, we only use the the base name of the URI 00177 // for the thumb params and the parent directory for the source file name. 00178 // Check that the zone relative path matches up so squid caches won't pick 00179 // up thumbs that would not be purged on source file deletion (bug 34231). 00180 if ( isset( $params['rel404'] ) // thumbnail was handled via 404 00181 && urldecode( $params['rel404'] ) !== $img->getThumbRel( $thumbName ) ) 00182 { 00183 wfThumbError( 404, 'The source file for the specified thumbnail does not exist.' ); 00184 wfProfileOut( __METHOD__ ); 00185 return; 00186 } 00187 $thumbPath = $img->getThumbPath( $thumbName ); 00188 if ( $img->getRepo()->fileExists( $thumbPath ) ) { 00189 $img->getRepo()->streamFile( $thumbPath, $headers ); 00190 wfProfileOut( __METHOD__ ); 00191 return; 00192 } 00193 } 00194 } catch ( MWException $e ) { 00195 wfThumbError( 500, $e->getHTML() ); 00196 wfProfileOut( __METHOD__ ); 00197 return; 00198 } 00199 00200 // Thumbnail isn't already there, so create the new thumbnail... 00201 try { 00202 $thumb = $img->transform( $params, File::RENDER_NOW ); 00203 } catch ( Exception $ex ) { 00204 // Tried to select a page on a non-paged file? 00205 $thumb = false; 00206 } 00207 00208 // Check for thumbnail generation errors... 00209 $errorMsg = false; 00210 if ( !$thumb ) { 00211 $errorMsg = wfMsgHtml( 'thumbnail_error', 'File::transform() returned false' ); 00212 } elseif ( $thumb->isError() ) { 00213 $errorMsg = $thumb->getHtmlMsg(); 00214 } elseif ( !$thumb->hasFile() ) { 00215 $errorMsg = wfMsgHtml( 'thumbnail_error', 'No path supplied in thumbnail object' ); 00216 } elseif ( $thumb->fileIsSource() ) { 00217 $errorMsg = wfMsgHtml( 'thumbnail_error', 00218 'Image was not scaled, is the requested width bigger than the source?' ); 00219 } 00220 00221 if ( $errorMsg !== false ) { 00222 wfThumbError( 500, $errorMsg ); 00223 } else { 00224 // Stream the file if there were no errors 00225 $thumb->streamFile( $headers ); 00226 } 00227 00228 wfProfileOut( __METHOD__ ); 00229 } 00230 00238 function wfExtractThumbParams( $uri ) { 00239 $repo = RepoGroup::singleton()->getLocalRepo(); 00240 00241 $zoneURI = $repo->getZoneUrl( 'thumb' ); 00242 if ( substr( $zoneURI, 0, 1 ) !== '/' ) { 00243 $bits = wfParseUrl( $zoneURI ); 00244 if ( $bits && isset( $bits['path'] ) ) { 00245 $zoneURI = $bits['path']; 00246 } else { 00247 return null; 00248 } 00249 } 00250 $zoneUrlRegex = preg_quote( $zoneURI ); 00251 00252 $hashDirRegex = $subdirRegex = ''; 00253 for ( $i = 0; $i < $repo->getHashLevels(); $i++ ) { 00254 $subdirRegex .= '[0-9a-f]'; 00255 $hashDirRegex .= "$subdirRegex/"; 00256 } 00257 00258 $thumbUrlRegex = "!^$zoneUrlRegex/((archive/|temp/)?$hashDirRegex([^/]*)/([^/]*))$!"; 00259 00260 // Check if this is a valid looking thumbnail request... 00261 if ( preg_match( $thumbUrlRegex, $uri, $matches ) ) { 00262 list( /* all */, $rel, $archOrTemp, $filename, $thumbname ) = $matches; 00263 $filename = urldecode( $filename ); 00264 $thumbname = urldecode( $thumbname ); 00265 00266 $params = array( 'f' => $filename, 'rel404' => $rel ); 00267 if ( $archOrTemp == 'archive/' ) { 00268 $params['archived'] = 1; 00269 } elseif ( $archOrTemp == 'temp/' ) { 00270 $params['temp'] = 1; 00271 } 00272 00273 // Check if the parameters can be extracted from the thumbnail name... 00274 if ( preg_match( '!^(page(\d*)-)*(\d*)px-[^/]*$!', $thumbname, $matches ) ) { 00275 list( /* all */, $pagefull, $pagenum, $size ) = $matches; 00276 $params['width'] = $size; 00277 if ( $pagenum ) { 00278 $params['page'] = $pagenum; 00279 } 00280 return $params; // valid thumbnail URL 00281 // Hooks return false if they manage to *resolve* the parameters 00282 } elseif ( !wfRunHooks( 'ExtractThumbParameters', array( $thumbname, &$params ) ) ) { 00283 return $params; // valid thumbnail URL (via extension or config) 00284 } 00285 } 00286 00287 return null; // not a valid thumbnail URL 00288 } 00289 00297 function wfThumbError( $status, $msg ) { 00298 global $wgShowHostnames; 00299 00300 header( 'Cache-Control: no-cache' ); 00301 header( 'Content-Type: text/html; charset=utf-8' ); 00302 if ( $status == 404 ) { 00303 header( 'HTTP/1.1 404 Not found' ); 00304 } elseif ( $status == 403 ) { 00305 header( 'HTTP/1.1 403 Forbidden' ); 00306 header( 'Vary: Cookie' ); 00307 } else { 00308 header( 'HTTP/1.1 500 Internal server error' ); 00309 } 00310 if ( $wgShowHostnames ) { 00311 $url = htmlspecialchars( isset( $_SERVER['REQUEST_URI'] ) ? $_SERVER['REQUEST_URI'] : '' ); 00312 $hostname = htmlspecialchars( wfHostname() ); 00313 $debug = "<!-- $url -->\n<!-- $hostname -->\n"; 00314 } else { 00315 $debug = ""; 00316 } 00317 echo <<<EOT 00318 <html><head><title>Error generating thumbnail</title></head> 00319 <body> 00320 <h1>Error generating thumbnail</h1> 00321 <p> 00322 $msg 00323 </p> 00324 $debug 00325 </body> 00326 </html> 00327 00328 EOT; 00329 }