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