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