* Changed custom functions to work via a $thgThumbCallbacks variable * Added 'checkCache' and 'fillCache' hooks to $thgThumbCallbacks * Minor regex cleanup to wfExtractThumbParams() * Re-organized a bit of the wfStreamThumbViaCurl() code
252 lines
7.7 KiB
PHP
252 lines
7.7 KiB
PHP
<?php
|
|
|
|
# Valid web server entry point
|
|
define( 'THUMB_HANDLER', true );
|
|
|
|
# Load thumb-handler configuration. We don't want to use
|
|
# WebStart.php or the like as it would kill performance.
|
|
$configPath = dirname( __FILE__ ) . "/thumb.config.php";
|
|
if ( !file_exists( $configPath ) ) {
|
|
die( "Thumb-handler.php is not enabled for this wiki.\n" );
|
|
}
|
|
require( $configPath );
|
|
|
|
function wfHandleThumb404() {
|
|
global $thgThumbCallbacks, $thgThumb404File;
|
|
|
|
# lighttpd puts the original request in REQUEST_URI, while
|
|
# sjs sets that to the 404 handler, and puts the original
|
|
# request in REDIRECT_URL.
|
|
if ( isset( $_SERVER['REDIRECT_URL'] ) ) {
|
|
# The URL is un-encoded, so put it back how it was.
|
|
$uri = str_replace( "%2F", "/", urlencode( $_SERVER['REDIRECT_URL'] ) );
|
|
} else {
|
|
$uri = $_SERVER['REQUEST_URI'];
|
|
}
|
|
|
|
# Extract thumb.php params from the URI...
|
|
if ( isset( $thgThumbCallbacks['extractParams'] )
|
|
&& is_callable( $thgThumbCallbacks['extractParams'] ) )
|
|
{
|
|
# Overridden by configuration
|
|
$params = call_user_func_array( $thgThumbCallbacks['extractParams'], array( $uri ) );
|
|
} else {
|
|
$params = wfExtractThumbParams( $uri ); // basic wiki URL param extracting
|
|
}
|
|
if ( $params === null ) { // not a valid thumb request
|
|
header( 'X-Debug: no regex match' ); // useful for debugging
|
|
require_once( $thgThumb404File ); // standard 404 message
|
|
return;
|
|
}
|
|
|
|
# Do some basic checks on the filename...
|
|
if ( preg_match( '/[\x80-\xff]/', $uri ) ) {
|
|
header( 'HTTP/1.0 400 Bad request' );
|
|
header( 'Content-Type: text/html' );
|
|
print "<html><head><title>Bad request</title></head><body>" .
|
|
"The URI contained bytes with the high bit set, this is not allowed." .
|
|
"</body></html>";
|
|
return;
|
|
} elseif ( strpos( $params['f'], '%20' ) !== false ) {
|
|
header( 'HTTP/1.0 404 Not found' );
|
|
header( 'Content-Type: text/html' );
|
|
header( 'X-Debug: filename contains a space' ); // useful for debugging
|
|
print "<html><head><title>Not found</title></head><body>" .
|
|
"The URL contained spaces, we don't have any thumbnail files with spaces." .
|
|
"</body></html>";
|
|
return;
|
|
}
|
|
|
|
# Check any backend caches for the thumbnail...
|
|
if ( isset( $thgThumbCallbacks['checkCache'] )
|
|
&& is_callable( $thgThumbCallbacks['checkCache'] ) )
|
|
{
|
|
if ( call_user_func_array( $thgThumbCallbacks['checkCache'], array( $uri, $params ) ) ) {
|
|
return; // file streamed from backend thumb cache
|
|
}
|
|
}
|
|
|
|
wfStreamThumbViaCurl( $params, $uri );
|
|
}
|
|
|
|
/**
|
|
* Extract the required params for thumb.php from the thumbnail request URI.
|
|
* At least 'width' and 'f' should be set if the result is an array.
|
|
*
|
|
* @param $uri String Thumbnail request URI
|
|
* @return Array|null associative params array or null
|
|
*/
|
|
function wfExtractThumbParams( $uri ) {
|
|
global $thgThumbServer, $thgThumbFragment, $thgThumbHashFragment;
|
|
|
|
$thumbRegex = '!^(?:' . preg_quote( $thgThumbServer ) . ')?/' .
|
|
preg_quote( $thgThumbFragment ) . '(/archive|/temp|)/' .
|
|
$thgThumbHashFragment . '([^/]*)/(page(\d*)-)*(\d*)px-[^/]*$!';
|
|
|
|
# Is this a thumbnail?
|
|
if ( preg_match( $thumbRegex, $uri, $matches ) ) {
|
|
list( $all, $archOrTemp, $filename, $pagefull, $pagenum, $size ) = $matches;
|
|
$params = array( 'f' => $filename, 'width' => $size );
|
|
if ( $pagenum ) {
|
|
$params['page'] = $pagenum;
|
|
}
|
|
if ( $archOrTemp == '/archive' ) {
|
|
$params['archived'] = 1;
|
|
} elseif ( $archOrTemp == '/temp' ) {
|
|
$params['temp'] = 1;
|
|
}
|
|
} else {
|
|
$params = null;
|
|
}
|
|
|
|
return $params;
|
|
}
|
|
|
|
/**
|
|
* cURL to thumb.php and stream back the resulting file or give an error message.
|
|
*
|
|
* @param $params Array Parameters to thumb.php
|
|
* @param $uri String Thumbnail request URI
|
|
* @return void
|
|
*/
|
|
function wfStreamThumbViaCurl( array $params, $uri ) {
|
|
global $thgThumbCallbacks, $thgThumbScriptPath, $thgThumbCurlProxy, $thgThumbCurlTimeout;
|
|
|
|
if ( !function_exists( 'curl_init' ) ) {
|
|
header( 'HTTP/1.0 404 Not found' );
|
|
header( 'Content-Type: text/html' );
|
|
header( 'X-Debug: cURL is not enabled' ); // useful for debugging
|
|
print "<html><head><title>Not found</title></head><body>" .
|
|
"cURL is not enabled for PHP on this wiki. Unable to send request thumb.php." .
|
|
"</body></html>";
|
|
return;
|
|
}
|
|
|
|
# Build up the request URL to use with CURL...
|
|
$reqURL = "{$thgThumbScriptPath}?";
|
|
$first = true;
|
|
foreach ( $params as $name => $value ) {
|
|
if ( $first ) {
|
|
$first = false;
|
|
} else {
|
|
$reqURL .= '&';
|
|
}
|
|
$reqURL .= "$name=$value"; // Note: value is already urlencoded
|
|
}
|
|
|
|
# Set relevant HTTP headers...
|
|
$headers = array();
|
|
$headers[] = "X-Original-URI: " . str_replace( "\n", '', $uri );
|
|
if ( isset( $thgThumbCallbacks['curlHeaders'] )
|
|
&& is_callable( $thgThumbCallbacks['curlHeaders'] ) )
|
|
{
|
|
# Add on any custom headers (like XFF)
|
|
call_user_func_array( $thgThumbCallbacks['curlHeaders'], array( &$headers ) );
|
|
}
|
|
|
|
# Pass through some other headers...
|
|
$passThrough = array( 'If-Modified-Since', 'Referer', 'User-Agent' );
|
|
foreach ( $passThrough as $headerName ) {
|
|
$serverVarName = 'HTTP_' . str_replace( '-', '_', strtoupper( $headerName ) );
|
|
if ( !empty( $_SERVER[$serverVarName] ) ) {
|
|
$headers[] = $headerName . ': ' .
|
|
str_replace( "\n", '', $_SERVER[$serverVarName] );
|
|
}
|
|
}
|
|
|
|
$ch = curl_init( $reqURL );
|
|
if ( $thgThumbCurlProxy ) {
|
|
curl_setopt( $ch, CURLOPT_PROXY, $thgThumbCurlProxy );
|
|
}
|
|
|
|
curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
|
|
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
|
|
curl_setopt( $ch, CURLOPT_TIMEOUT, $thgThumbCurlTimeout );
|
|
|
|
# Actually make the request
|
|
$text = curl_exec( $ch );
|
|
|
|
# Send it on to the client
|
|
$errno = curl_errno( $ch );
|
|
$contentType = curl_getinfo( $ch, CURLINFO_CONTENT_TYPE );
|
|
$httpCode = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
|
|
if ( $errno ) {
|
|
header( 'HTTP/1.1 500 Internal server error' );
|
|
header( 'Cache-Control: no-cache' );
|
|
list( $text, $contentType ) = wfCurlErrorText( $ch );
|
|
} elseif ( $httpCode == 304 ) {
|
|
header( 'HTTP/1.1 304 Not modified' );
|
|
$contentType = '';
|
|
$text = '';
|
|
} elseif ( strval( $text ) == '' ) {
|
|
header( 'HTTP/1.1 500 Internal server error' );
|
|
header( 'Cache-Control: no-cache' );
|
|
list( $text, $contentType ) = wfCurlEmptyText( $ch );
|
|
} elseif ( $httpCode == 404 ) {
|
|
header( 'HTTP/1.1 404 Not found' );
|
|
header( 'Cache-Control: s-maxage=300, must-revalidate, max-age=0' );
|
|
} elseif ( $httpCode != 200
|
|
|| substr( $contentType, 0, 9 ) == 'text/html'
|
|
|| substr( $text, 0, 5 ) == '<html' )
|
|
{
|
|
# Error message, suppress cache
|
|
header( 'HTTP/1.1 500 Internal server error' );
|
|
header( 'Cache-Control: no-cache' );
|
|
} else {
|
|
# OK thumbnail; save to any backend caches...
|
|
if ( isset( $thgThumbCallbacks['fillCache'] )
|
|
&& is_callable( $thgThumbCallbacks['fillCache'] ) )
|
|
{
|
|
call_user_func_array( $thgThumbCallbacks['fillCache'], array( $uri, $text ) );
|
|
}
|
|
}
|
|
|
|
if ( !$contentType ) {
|
|
header( 'Content-Type:' );
|
|
} else {
|
|
header( "Content-Type: $contentType" );
|
|
}
|
|
|
|
print $text; // thumb data or error text
|
|
|
|
curl_close( $ch );
|
|
}
|
|
|
|
/**
|
|
* Get error message and content type for when the cURL response is empty.
|
|
*
|
|
* @param $ch cURL handle
|
|
* @return Array (error html, content type)
|
|
*/
|
|
function wfCurlErrorText( $ch ) {
|
|
$contentType = 'text/html';
|
|
$error = htmlspecialchars( curl_error( $ch ) );
|
|
$text = <<<EOT
|
|
<html>
|
|
<head><title>Thumbnail error</title></head>
|
|
<body>Error retrieving thumbnail from scaling server: $error</body>
|
|
</html>
|
|
EOT;
|
|
return array( $text, $contentType );
|
|
}
|
|
|
|
/**
|
|
* Get error message and content type for when the cURL response is an error.
|
|
*
|
|
* @param $ch cURL handle
|
|
* @return Array (error html, content type)
|
|
*/
|
|
function wfCurlEmptyText( $ch ) {
|
|
$contentType = 'text/html';
|
|
$error = htmlspecialchars( curl_error( $ch ) );
|
|
$text = <<<EOT
|
|
<html>
|
|
<head><title>Thumbnail error</title></head>
|
|
<body>Error retrieving thumbnail from scaling server: empty response</body>
|
|
</html>
|
|
EOT;
|
|
return array( $text, $contentType );
|
|
}
|
|
|
|
# Entry point
|
|
wfHandleThumb404();
|