]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/OutputHandler.php
MediaWiki 1.16.0
[autoinstallsdev/mediawiki.git] / includes / OutputHandler.php
1 <?php
2
3 /**
4  * Standard output handler for use with ob_start
5  */
6 function wfOutputHandler( $s ) {
7         global $wgDisableOutputCompression, $wgValidateAllHtml;
8         $s = wfMangleFlashPolicy( $s );
9         if ( $wgValidateAllHtml ) {
10                 $headers = apache_response_headers();
11                 $isHTML = true;
12                 foreach ( $headers as $name => $value ) {
13                         if ( strtolower( $name ) == 'content-type' && strpos( $value, 'text/html' ) === false && strpos( $value, 'application/xhtml+xml' ) === false ) {
14                                 $isHTML = false;
15                                 break;
16                         }
17                 }
18                 if ( $isHTML ) {
19                         $s = wfHtmlValidationHandler( $s );
20                 }
21         }
22         if ( !$wgDisableOutputCompression && !ini_get( 'zlib.output_compression' ) ) {
23                 if ( !defined( 'MW_NO_OUTPUT_COMPRESSION' ) ) {
24                         $s = wfGzipHandler( $s );
25                 }
26                 if ( !ini_get( 'output_handler' ) ) {
27                         wfDoContentLength( strlen( $s ) );
28                 }
29         }
30         return $s;
31 }
32
33 /**
34  * Get the "file extension" that some client apps will estimate from
35  * the currently-requested URL.
36  * This isn't on WebRequest because we need it when things aren't initialized
37  * @private
38  */
39 function wfRequestExtension() {
40         /// @todo Fixme: this sort of dupes some code in WebRequest::getRequestUrl()
41         if( isset( $_SERVER['REQUEST_URI'] ) ) {
42                 // Strip the query string...
43                 list( $path ) = explode( '?', $_SERVER['REQUEST_URI'], 2 );
44         } elseif( isset( $_SERVER['SCRIPT_NAME'] ) ) {
45                 // Probably IIS. QUERY_STRING appears separately.
46                 $path = $_SERVER['SCRIPT_NAME'];
47         } else {
48                 // Can't get the path from the server? :(
49                 return '';
50         }
51
52         $period = strrpos( $path, '.' );
53         if( $period !== false ) {
54                 return strtolower( substr( $path, $period ) );
55         }
56         return '';
57 }
58
59 /**
60  * Handler that compresses data with gzip if allowed by the Accept header.
61  * Unlike ob_gzhandler, it works for HEAD requests too.
62  */
63 function wfGzipHandler( $s ) {
64         if( !function_exists( 'gzencode' ) || headers_sent() ) {
65                 return $s;
66         }
67
68         $ext = wfRequestExtension();
69         if( $ext == '.gz' || $ext == '.tgz' ) {
70                 // Don't do gzip compression if the URL path ends in .gz or .tgz
71                 // This confuses Safari and triggers a download of the page,
72                 // even though it's pretty clearly labeled as viewable HTML.
73                 // Bad Safari! Bad!
74                 return $s;
75         }
76
77         if( wfClientAcceptsGzip() ) {
78                 header( 'Content-Encoding: gzip' );
79                 $s = gzencode( $s, 6 );
80         }
81
82         // Set vary header if it hasn't been set already
83         $headers = headers_list();
84         $foundVary = false;
85         foreach ( $headers as $header ) {
86                 if ( substr( $header, 0, 5 ) == 'Vary:' ) {
87                         $foundVary = true;
88                         break;
89                 }
90         }
91         if ( !$foundVary ) {
92                 header( 'Vary: Accept-Encoding' );
93                 global $wgUseXVO;
94                 if ( $wgUseXVO ) {
95                         header( 'X-Vary-Options: Accept-Encoding;list-contains=gzip' );
96                 }
97         }
98         return $s;
99 }
100
101 /**
102  * Mangle flash policy tags which open up the site to XSS attacks.
103  */
104 function wfMangleFlashPolicy( $s ) {
105         # Avoid weird excessive memory usage in PCRE on big articles
106         if ( preg_match( '/\<\s*cross-domain-policy\s*\>/i', $s ) ) {
107                 return preg_replace( '/\<\s*cross-domain-policy\s*\>/i', '<NOT-cross-domain-policy>', $s );
108         } else {
109                 return $s;
110         }
111 }
112
113 /**
114  * Add a Content-Length header if possible. This makes it cooperate with squid better.
115  */
116 function wfDoContentLength( $length ) {
117         if ( !headers_sent() && $_SERVER['SERVER_PROTOCOL'] == 'HTTP/1.0' ) {
118                 header( "Content-Length: $length" );
119         }
120 }
121
122 /**
123  * Replace the output with an error if the HTML is not valid
124  */
125 function wfHtmlValidationHandler( $s ) {
126
127         $errors = '';
128         if ( MWTidy::checkErrors( $s, $errors ) ) {
129                 return $s;
130         }
131
132         header( 'Cache-Control: no-cache' );
133
134         $out = <<<EOT
135 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
136 <html xmlns="http://www.w3.org/1999/xhtml" lang="en" dir="ltr">
137 <head>
138 <title>HTML validation error</title>
139 <style>
140 .highlight { background-color: #ffc }
141 li { white-space: pre }
142 </style>
143 </head>
144 <body>
145 <h1>HTML validation error</h1>
146 <ul>
147 EOT;
148
149         $error = strtok( $errors, "\n" );
150         $badLines = array();
151         while ( $error !== false ) {
152                 if ( preg_match( '/^line (\d+)/', $error, $m ) ) {
153                         $lineNum = intval( $m[1] );
154                         $badLines[$lineNum] = true;
155                         $out .= "<li><a href=\"#line-{$lineNum}\">" . htmlspecialchars( $error ) . "</a></li>\n";
156                 }
157                 $error = strtok( "\n" );
158         }
159
160         $out .= '</ul>';
161         $out .= '<pre>' . htmlspecialchars( $errors ) . '</pre>';
162         $out .= "<ol>\n";
163         $line = strtok( $s, "\n" );
164         $i = 1;
165         while ( $line !== false ) {
166                 if ( isset( $badLines[$i] ) ) {
167                         $out .= "<li class=\"highlight\" id=\"line-$i\">";
168                 } else {
169                         $out .= '<li>';
170                 }
171                 $out .= htmlspecialchars( $line ) . "</li>\n";
172                 $line = strtok( "\n" );
173                 $i++;
174         }
175         $out .= '</ol></body></html>';
176         return $out;
177 }