FU r100535: renamed thumb-handler.php to match the other files here
[lhc/web/wiklou.git] / thumb_handler.php
1 <?php
2
3 # Valid web server entry point
4 define( 'THUMB_HANDLER', true );
5
6 # Load thumb-handler configuration. We don't want to use
7 # WebStart.php or the like as it would kill performance.
8 $configPath = dirname( __FILE__ ) . "/thumb.config.php";
9 if ( !file_exists( $configPath ) ) {
10 die( "Thumb-handler.php is not enabled for this wiki.\n" );
11 } elseif ( !extension_loaded( 'curl' ) ) {
12 die( "cURL is not enabled for PHP on this wiki.\n" ); // sanity
13 }
14 require( $configPath );
15
16 wfHandleThumb404Main();
17
18 function wfHandleThumb404Main() {
19 global $thgThumbCallbacks, $thgThumb404File;
20
21 # lighttpd puts the original request in REQUEST_URI, while
22 # sjs sets that to the 404 handler, and puts the original
23 # request in REDIRECT_URL.
24 if ( isset( $_SERVER['REDIRECT_URL'] ) ) {
25 # The URL is un-encoded, so put it back how it was.
26 $uri = str_replace( "%2F", "/", urlencode( $_SERVER['REDIRECT_URL'] ) );
27 } else {
28 $uri = $_SERVER['REQUEST_URI'];
29 }
30
31 # Extract thumb.php params from the URI...
32 if ( isset( $thgThumbCallbacks['extractParams'] )
33 && is_callable( $thgThumbCallbacks['extractParams'] ) ) // overridden by configuration?
34 {
35 $params = call_user_func_array( $thgThumbCallbacks['extractParams'], array( $uri ) );
36 } else {
37 $params = wfExtractThumbParams( $uri ); // basic wiki URL param extracting
38 }
39
40 # Show 404 error if this is not a valid thumb request...
41 if ( !is_array( $params ) ) {
42 header( 'X-Debug: no regex match' ); // useful for debugging
43 if ( $thgThumb404File ) { // overridden by configuration?
44 require( $thgThumb404File );
45 } else {
46 wfDisplay404Error(); // standard 404 message
47 }
48 return;
49 }
50
51 # Check any backend caches for the thumbnail...
52 if ( isset( $thgThumbCallbacks['checkCache'] )
53 && is_callable( $thgThumbCallbacks['checkCache'] ) )
54 {
55 if ( call_user_func_array( $thgThumbCallbacks['checkCache'], array( $uri, $params ) ) ) {
56 return; // file streamed from backend thumb cache
57 }
58 }
59
60 wfStreamThumbViaCurl( $params, $uri );
61 }
62
63 /**
64 * Extract the required params for thumb.php from the thumbnail request URI.
65 * At least 'width' and 'f' should be set if the result is an array.
66 *
67 * @param $uri String Thumbnail request URI
68 * @return Array|null associative params array or null
69 */
70 function wfExtractThumbParams( $uri ) {
71 global $thgThumbServer, $thgThumbFragment, $thgThumbHashFragment;
72
73 $thumbRegex = '!^(?:' . preg_quote( $thgThumbServer ) . ')?/' .
74 preg_quote( $thgThumbFragment ) . '(/archive|/temp|)/' .
75 $thgThumbHashFragment . '([^/]*)/(page(\d*)-)*(\d*)px-[^/]*$!';
76
77 if ( preg_match( $thumbRegex, $uri, $matches ) ) {
78 list( $all, $archOrTemp, $filename, $pagefull, $pagenum, $size ) = $matches;
79 $params = array( 'f' => $filename, 'width' => $size );
80 if ( $pagenum ) {
81 $params['page'] = $pagenum;
82 }
83 if ( $archOrTemp == '/archive' ) {
84 $params['archived'] = 1;
85 } elseif ( $archOrTemp == '/temp' ) {
86 $params['temp'] = 1;
87 }
88 } else {
89 $params = null; // not a valid thumbnail URL
90 }
91
92 return $params;
93 }
94
95 /**
96 * cURL to thumb.php and stream back the resulting file or give an error message.
97 *
98 * @param $params Array Parameters to thumb.php
99 * @param $uri String Thumbnail request URI
100 * @return void
101 */
102 function wfStreamThumbViaCurl( array $params, $uri ) {
103 global $thgThumbCallbacks, $thgThumbScriptPath, $thgThumbCurlProxy, $thgThumbCurlTimeout;
104
105 # Build up the request URL to use with CURL...
106 $reqURL = "{$thgThumbScriptPath}?";
107 $first = true;
108 foreach ( $params as $name => $value ) {
109 if ( $first ) {
110 $first = false;
111 } else {
112 $reqURL .= '&';
113 }
114 $reqURL .= "$name=$value"; // Note: value is already urlencoded
115 }
116
117 # Set relevant HTTP headers...
118 $headers = array();
119 $headers[] = "X-Original-URI: " . str_replace( "\n", '', $uri );
120 if ( isset( $thgThumbCallbacks['curlHeaders'] )
121 && is_callable( $thgThumbCallbacks['curlHeaders'] ) )
122 {
123 # Add on any custom headers (like XFF)
124 call_user_func_array( $thgThumbCallbacks['curlHeaders'], array( &$headers ) );
125 }
126
127 # Pass through some other headers...
128 $passThrough = array( 'If-Modified-Since', 'Referer', 'User-Agent' );
129 foreach ( $passThrough as $headerName ) {
130 $serverVarName = 'HTTP_' . str_replace( '-', '_', strtoupper( $headerName ) );
131 if ( !empty( $_SERVER[$serverVarName] ) ) {
132 $headers[] = $headerName . ': ' .
133 str_replace( "\n", '', $_SERVER[$serverVarName] );
134 }
135 }
136
137 $ch = curl_init( $reqURL );
138 if ( $thgThumbCurlProxy ) {
139 curl_setopt( $ch, CURLOPT_PROXY, $thgThumbCurlProxy );
140 }
141
142 curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
143 curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
144 curl_setopt( $ch, CURLOPT_TIMEOUT, $thgThumbCurlTimeout );
145
146 # Actually make the request
147 $text = curl_exec( $ch );
148
149 # Send it on to the client...
150 $errno = curl_errno( $ch );
151 $contentType = curl_getinfo( $ch, CURLINFO_CONTENT_TYPE );
152 $httpCode = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
153 if ( $errno ) {
154 header( 'HTTP/1.1 500 Internal server error' );
155 header( 'Cache-Control: no-cache' );
156 $contentType = 'text/html';
157 $text = wfCurlErrorText( $ch );
158 } elseif ( $httpCode == 304 ) { // OK
159 header( 'HTTP/1.1 304 Not modified' );
160 $contentType = '';
161 $text = '';
162 } elseif ( strval( $text ) == '' ) {
163 header( 'HTTP/1.1 500 Internal server error' );
164 header( 'Cache-Control: no-cache' );
165 $contentType = 'text/html';
166 $text = wfCurlEmptyText( $ch );
167 } elseif ( $httpCode == 404 ) {
168 header( 'HTTP/1.1 404 Not found' );
169 header( 'Cache-Control: s-maxage=300, must-revalidate, max-age=0' );
170 } elseif ( $httpCode != 200 || substr( $contentType, 0, 9 ) == 'text/html' ) {
171 # Error message, suppress cache
172 header( 'HTTP/1.1 500 Internal server error' );
173 header( 'Cache-Control: no-cache' );
174 } else {
175 # OK thumbnail; save to any backend caches...
176 if ( isset( $thgThumbCallbacks['fillCache'] )
177 && is_callable( $thgThumbCallbacks['fillCache'] ) )
178 {
179 call_user_func_array( $thgThumbCallbacks['fillCache'], array( $uri, $text ) );
180 }
181 }
182
183 if ( !$contentType ) {
184 header( 'Content-Type:' );
185 } else {
186 header( "Content-Type: $contentType" );
187 }
188
189 print $text; // thumb data or error text
190
191 curl_close( $ch );
192 }
193
194 /**
195 * Get error message HTML for when the cURL response is an error.
196 *
197 * @param $ch cURL handle
198 * @return string
199 */
200 function wfCurlErrorText( $ch ) {
201 $error = htmlspecialchars( curl_error( $ch ) );
202 return <<<EOT
203 <html>
204 <head><title>Thumbnail error</title></head>
205 <body>Error retrieving thumbnail from scaling server: $error</body>
206 </html>
207 EOT;
208 }
209
210 /**
211 * Get error message HTML for when the cURL response is empty.
212 *
213 * @param $ch cURL handle
214 * @return string
215 */
216 function wfCurlEmptyText( $ch ) {
217 return <<<EOT
218 <html>
219 <head><title>Thumbnail error</title></head>
220 <body>Error retrieving thumbnail from scaling server: empty response</body>
221 </html>
222 EOT;
223 }
224
225 /**
226 * Print out a generic 404 error message.
227 *
228 * @return void
229 */
230 function wfDisplay404Error() {
231 header( 'HTTP/1.1 404 Not Found' );
232 header( 'Content-Type: text/html;charset=utf-8' );
233
234 $prot = isset( $_SERVER['HTTPS'] ) ? "https://" : "http://";
235 $serv = strlen( $_SERVER['HTTP_HOST'] ) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'];
236 $loc = $_SERVER["REQUEST_URI"];
237
238 $encUrl = htmlspecialchars( $prot . $serv . $loc );
239
240 // Looks like a typical apache2 error
241 $standard_404 = <<<ENDTEXT
242 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
243 <html><head>
244 <title>404 Not Found</title>
245 </head><body>
246 <h1>Not Found</h1>
247 <p>The requested URL $encUrl was not found on this server.</p>
248 </body></html>
249 ENDTEXT;
250
251 print $standard_404;
252 }