Adding functions to HTMLForm for explicitly setting the content of the header and...
[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 }
12 require( $configPath );
13
14 function wfHandleThumb404() {
15 global $thgThumb404File;
16
17 # lighttpd puts the original request in REQUEST_URI, while
18 # sjs sets that to the 404 handler, and puts the original
19 # request in REDIRECT_URL.
20 if ( isset( $_SERVER['REDIRECT_URL'] ) ) {
21 # The URL is un-encoded, so put it back how it was.
22 $uri = str_replace( "%2F", "/", urlencode( $_SERVER['REDIRECT_URL'] ) );
23 } else {
24 $uri = $_SERVER['REQUEST_URI'];
25 }
26
27 # Extract thumb.php params from the URI.
28 if ( function_exists( 'wfCustomExtractThumbParams' ) ) {
29 $params = wfCustomExtractThumbParams( $uri ); // overridden by configuration
30 } else {
31 $params = wfExtractThumbParams( $uri ); // basic wiki URL param extracting
32 }
33 if ( $params === null ) { // not a valid thumb request
34 header( 'X-Debug: no regex match' ); // useful for debugging
35 require_once( $thgThumb404File ); // standard 404 message
36 return;
37 }
38
39 # Do some basic checks on the filename...
40 if ( preg_match( '/[\x80-\xff]/', $uri ) ) {
41 header( 'HTTP/1.0 400 Bad request' );
42 header( 'Content-Type: text/html' );
43 echo "<html><head><title>Bad request</title></head><body>" .
44 "The URI contained bytes with the high bit set, this is not allowed." .
45 "</body></html>";
46 return;
47 } elseif ( strpos( $params['f'], '%20' ) !== false ) {
48 header( 'HTTP/1.0 404 Not found' );
49 header( 'Content-Type: text/html' );
50 header( 'X-Debug: filename contains a space' ); // useful for debugging
51 echo "<html><head><title>Not found</title></head><body>" .
52 "The URL contained spaces, we don't have any thumbnail files with spaces." .
53 "</body></html>";
54 return;
55 }
56
57 wfStreamThumbViaCurl( $params, $uri );
58 }
59
60 /**
61 * Extract the required params for thumb.php from the thumbnail request URI.
62 * At least 'width' and 'f' should be set if the result is an array.
63 *
64 * @param $uri String Thumbnail request URI
65 * @return Array|null associative params array or null
66 */
67 function wfExtractThumbParams( $uri ) {
68 global $thgThumbServer, $thgThumbFragment, $thgThumbHashFragment;
69
70 $thumbRegex = '!^(?:' . preg_quote( $thgThumbServer ) . ')?/' .
71 preg_quote( $thgThumbFragment ) . '(/archive|/temp|)/' .
72 $thgThumbHashFragment . '([^/]*)/' . '(page(\d*)-)*(\d*)px-([^/]*)$!';
73
74 # Is this a thumbnail?
75 if ( preg_match( $thumbRegex, $uri, $matches ) ) {
76 list( $all, $archOrTemp, $filename, $pagefull, $pagenum, $size, $fn2 ) = $matches;
77 $params = array( 'f' => $filename, 'width' => $size );
78 if ( $pagenum ) {
79 $params['page'] = $pagenum;
80 }
81 if ( $archOrTemp == '/archive' ) {
82 $params['archived'] = 1;
83 } elseif ( $archOrTemp == '/temp' ) {
84 $params['temp'] = 1;
85 }
86 } else {
87 $params = null;
88 }
89
90 return $params;
91 }
92
93 /**
94 * cURL to thumb.php and stream back the resulting file or give an error message.
95 *
96 * @param $params Array Parameters to thumb.php
97 * @param $uri String Thumbnail request URI
98 * @return void
99 */
100 function wfStreamThumbViaCurl( array $params, $uri ) {
101 global $thgThumbScriptPath, $thgThumbCurlProxy, $thgThumbCurlTimeout;
102
103 if ( !function_exists( 'curl_init' ) ) {
104 header( 'HTTP/1.0 404 Not found' );
105 header( 'Content-Type: text/html' );
106 header( 'X-Debug: cURL is not enabled' ); // useful for debugging
107 echo "<html><head><title>Not found</title></head><body>" .
108 "cURL is not enabled for PHP on this wiki. Unable to send request thumb.php." .
109 "</body></html>";
110 return;
111 }
112
113 # Build up the request URL to use with CURL...
114 $reqURL = "{$thgThumbScriptPath}?";
115 $first = true;
116 foreach ( $params as $name => $value ) {
117 if ( $first ) {
118 $first = false;
119 } else {
120 $reqURL .= '&';
121 }
122 // Note: value is already urlencoded
123 $reqURL .= "$name=$value";
124 }
125
126 $ch = curl_init( $reqURL );
127 if ( $thgThumbCurlProxy ) {
128 curl_setopt( $ch, CURLOPT_PROXY, $thgThumbCurlProxy );
129 }
130
131 $headers = array(); // HTTP headers
132 # Set certain headers...
133 $headers[] = "X-Original-URI: " . str_replace( "\n", '', $uri );
134 if ( function_exists( 'wfCustomThumbRequestHeaders' ) ) {
135 wfCustomThumbRequestHeaders( $headers ); // add on any custom headers (like XFF)
136 }
137 # Pass through some other headers...
138 $passthrough = array( 'If-Modified-Since', 'Referer', 'User-Agent' );
139 foreach ( $passthrough as $headerName ) {
140 $serverVarName = 'HTTP_' . str_replace( '-', '_', strtoupper( $headerName ) );
141 if ( !empty( $_SERVER[$serverVarName] ) ) {
142 $headers[] = $headerName . ': ' .
143 str_replace( "\n", '', $_SERVER[$serverVarName] );
144 }
145 }
146
147 curl_setopt( $ch, CURLOPT_HTTPHEADER, $headers );
148 curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
149 curl_setopt( $ch, CURLOPT_TIMEOUT, $thgThumbCurlTimeout );
150
151 # Actually make the request
152 $text = curl_exec( $ch );
153
154 # Send it on to the client
155 $errno = curl_errno( $ch );
156 $contentType = curl_getinfo( $ch, CURLINFO_CONTENT_TYPE );
157 $httpCode = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
158 if ( $errno ) {
159 header( 'HTTP/1.1 500 Internal server error' );
160 header( 'Cache-Control: no-cache' );
161 list( $text, $contentType ) = wfCurlErrorText( $ch );
162 } elseif ( $httpCode == 304 ) {
163 header( 'HTTP/1.1 304 Not modified' );
164 $contentType = '';
165 $text = '';
166 } elseif ( strval( $text ) == '' ) {
167 header( 'HTTP/1.1 500 Internal server error' );
168 header( 'Cache-Control: no-cache' );
169 list( $text, $contentType ) = wfCurlEmptyText( $ch );
170 } elseif ( $httpCode == 404 ) {
171 header( 'HTTP/1.1 404 Not found' );
172 header( 'Cache-Control: s-maxage=300, must-revalidate, max-age=0' );
173 } elseif ( $httpCode != 200
174 || substr( $contentType, 0, 9 ) == 'text/html'
175 || substr( $text, 0, 5 ) == '<html' )
176 {
177 # Error message, suppress cache
178 header( 'HTTP/1.1 500 Internal server error' );
179 header( 'Cache-Control: no-cache' );
180 }
181
182 if ( !$contentType ) {
183 header( 'Content-Type:' );
184 } else {
185 header( "Content-Type: $contentType" );
186 }
187
188 print $text; // thumb data or error text
189
190 curl_close( $ch );
191 }
192
193 /**
194 * Get error message and content type for when the cURL response is empty.
195 *
196 * @param $ch cURL handle
197 * @return Array (error html, content type)
198 */
199 function wfCurlErrorText( $ch ) {
200 $contentType = 'text/html';
201 $error = htmlspecialchars( curl_error( $ch ) );
202 $text = <<<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 return array( $text, $contentType );
209 }
210
211 /**
212 * Get error message and content type for when the cURL response is an error.
213 *
214 * @param $ch cURL handle
215 * @return Array (error html, content type)
216 */
217 function wfCurlEmptyText( $ch ) {
218 $contentType = 'text/html';
219 $error = htmlspecialchars( curl_error( $ch ) );
220 $text = <<<EOT
221 <html>
222 <head><title>Thumbnail error</title></head>
223 <body>Error retrieving thumbnail from scaling server: empty response</body>
224 </html>
225 EOT;
226 return array( $text, $contentType );
227 }
228
229 # Entry point
230 wfHandleThumb404();