wikimedia /
mediawiki
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | /** |
||
| 3 | * Response handler for Ajax requests. |
||
| 4 | * |
||
| 5 | * This program is free software; you can redistribute it and/or modify |
||
| 6 | * it under the terms of the GNU General Public License as published by |
||
| 7 | * the Free Software Foundation; either version 2 of the License, or |
||
| 8 | * (at your option) any later version. |
||
| 9 | * |
||
| 10 | * This program is distributed in the hope that it will be useful, |
||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
| 13 | * GNU General Public License for more details. |
||
| 14 | * |
||
| 15 | * You should have received a copy of the GNU General Public License along |
||
| 16 | * with this program; if not, write to the Free Software Foundation, Inc., |
||
| 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||
| 18 | * http://www.gnu.org/copyleft/gpl.html |
||
| 19 | * |
||
| 20 | * @file |
||
| 21 | * @ingroup Ajax |
||
| 22 | */ |
||
| 23 | |||
| 24 | /** |
||
| 25 | * Handle responses for Ajax requests (send headers, print |
||
| 26 | * content, that sort of thing) |
||
| 27 | * |
||
| 28 | * @ingroup Ajax |
||
| 29 | */ |
||
| 30 | class AjaxResponse { |
||
| 31 | /** |
||
| 32 | * Number of seconds to get the response cached by a proxy |
||
| 33 | * @var int $mCacheDuration |
||
| 34 | */ |
||
| 35 | private $mCacheDuration; |
||
| 36 | |||
| 37 | /** |
||
| 38 | * HTTP header Content-Type |
||
| 39 | * @var string $mContentType |
||
| 40 | */ |
||
| 41 | private $mContentType; |
||
| 42 | |||
| 43 | /** |
||
| 44 | * Disables output. Can be set by calling $AjaxResponse->disable() |
||
| 45 | * @var bool $mDisabled |
||
| 46 | */ |
||
| 47 | private $mDisabled; |
||
| 48 | |||
| 49 | /** |
||
| 50 | * Date for the HTTP header Last-modified |
||
| 51 | * @var string|bool $mLastModified |
||
| 52 | */ |
||
| 53 | private $mLastModified; |
||
| 54 | |||
| 55 | /** |
||
| 56 | * HTTP response code |
||
| 57 | * @var string $mResponseCode |
||
| 58 | */ |
||
| 59 | private $mResponseCode; |
||
| 60 | |||
| 61 | /** |
||
| 62 | * HTTP Vary header |
||
| 63 | * @var string $mVary |
||
| 64 | */ |
||
| 65 | private $mVary; |
||
| 66 | |||
| 67 | /** |
||
| 68 | * Content of our HTTP response |
||
| 69 | * @var string $mText |
||
| 70 | */ |
||
| 71 | private $mText; |
||
| 72 | |||
| 73 | /** |
||
| 74 | * @var Config |
||
| 75 | */ |
||
| 76 | private $mConfig; |
||
| 77 | |||
| 78 | /** |
||
| 79 | * @param string|null $text |
||
| 80 | * @param Config|null $config |
||
| 81 | */ |
||
| 82 | function __construct( $text = null, Config $config = null ) { |
||
| 83 | $this->mCacheDuration = null; |
||
| 84 | $this->mVary = null; |
||
| 85 | $this->mConfig = $config ?: ConfigFactory::getDefaultInstance()->makeConfig( 'main' ); |
||
| 86 | |||
| 87 | $this->mDisabled = false; |
||
| 88 | $this->mText = ''; |
||
| 89 | $this->mResponseCode = 200; |
||
| 90 | $this->mLastModified = false; |
||
| 91 | $this->mContentType = 'application/x-wiki'; |
||
| 92 | |||
| 93 | if ( $text ) { |
||
|
0 ignored issues
–
show
|
|||
| 94 | $this->addText( $text ); |
||
| 95 | } |
||
| 96 | } |
||
| 97 | |||
| 98 | /** |
||
| 99 | * Set the number of seconds to get the response cached by a proxy |
||
| 100 | * @param int $duration |
||
| 101 | */ |
||
| 102 | function setCacheDuration( $duration ) { |
||
| 103 | $this->mCacheDuration = $duration; |
||
| 104 | } |
||
| 105 | |||
| 106 | /** |
||
| 107 | * Set the HTTP Vary header |
||
| 108 | * @param string $vary |
||
| 109 | */ |
||
| 110 | function setVary( $vary ) { |
||
| 111 | $this->mVary = $vary; |
||
| 112 | } |
||
| 113 | |||
| 114 | /** |
||
| 115 | * Set the HTTP response code |
||
| 116 | * @param string $code |
||
| 117 | */ |
||
| 118 | function setResponseCode( $code ) { |
||
| 119 | $this->mResponseCode = $code; |
||
| 120 | } |
||
| 121 | |||
| 122 | /** |
||
| 123 | * Set the HTTP header Content-Type |
||
| 124 | * @param string $type |
||
| 125 | */ |
||
| 126 | function setContentType( $type ) { |
||
| 127 | $this->mContentType = $type; |
||
| 128 | } |
||
| 129 | |||
| 130 | /** |
||
| 131 | * Disable output. |
||
| 132 | */ |
||
| 133 | function disable() { |
||
| 134 | $this->mDisabled = true; |
||
| 135 | } |
||
| 136 | |||
| 137 | /** |
||
| 138 | * Add content to the response |
||
| 139 | * @param string $text |
||
| 140 | */ |
||
| 141 | function addText( $text ) { |
||
| 142 | if ( !$this->mDisabled && $text ) { |
||
| 143 | $this->mText .= $text; |
||
| 144 | } |
||
| 145 | } |
||
| 146 | |||
| 147 | /** |
||
| 148 | * Output text |
||
| 149 | */ |
||
| 150 | function printText() { |
||
| 151 | if ( !$this->mDisabled ) { |
||
| 152 | print $this->mText; |
||
| 153 | } |
||
| 154 | } |
||
| 155 | |||
| 156 | /** |
||
| 157 | * Construct the header and output it |
||
| 158 | */ |
||
| 159 | function sendHeaders() { |
||
| 160 | if ( $this->mResponseCode ) { |
||
| 161 | // For back-compat, it is supported that mResponseCode be a string like " 200 OK" |
||
| 162 | // (with leading space and the status message after). Cast response code to an integer |
||
| 163 | // to take advantage of PHP's conversion rules which will turn " 200 OK" into 200. |
||
| 164 | // https://secure.php.net/manual/en/language.types.string.php#language.types.string.conversion |
||
| 165 | $n = intval( trim( $this->mResponseCode ) ); |
||
| 166 | HttpStatus::header( $n ); |
||
| 167 | } |
||
| 168 | |||
| 169 | header( "Content-Type: " . $this->mContentType ); |
||
| 170 | |||
| 171 | if ( $this->mLastModified ) { |
||
| 172 | header( "Last-Modified: " . $this->mLastModified ); |
||
| 173 | } else { |
||
| 174 | header( "Last-Modified: " . gmdate( "D, d M Y H:i:s" ) . " GMT" ); |
||
| 175 | } |
||
| 176 | |||
| 177 | if ( $this->mCacheDuration ) { |
||
| 178 | # If CDN caches are configured, tell them to cache the response, |
||
| 179 | # and tell the client to always check with the CDN. Otherwise, |
||
| 180 | # tell the client to use a cached copy, without a way to purge it. |
||
| 181 | |||
| 182 | if ( $this->mConfig->get( 'UseSquid' ) ) { |
||
| 183 | # Expect explicit purge of the proxy cache, but require end user agents |
||
| 184 | # to revalidate against the proxy on each visit. |
||
| 185 | # Surrogate-Control controls our CDN, Cache-Control downstream caches |
||
| 186 | |||
| 187 | if ( $this->mConfig->get( 'UseESI' ) ) { |
||
| 188 | header( 'Surrogate-Control: max-age=' . $this->mCacheDuration . ', content="ESI/1.0"' ); |
||
| 189 | header( 'Cache-Control: s-maxage=0, must-revalidate, max-age=0' ); |
||
| 190 | } else { |
||
| 191 | header( 'Cache-Control: s-maxage=' . $this->mCacheDuration . ', must-revalidate, max-age=0' ); |
||
| 192 | } |
||
| 193 | |||
| 194 | } else { |
||
| 195 | # Let the client do the caching. Cache is not purged. |
||
| 196 | header( "Expires: " . gmdate( "D, d M Y H:i:s", time() + $this->mCacheDuration ) . " GMT" ); |
||
| 197 | header( "Cache-Control: s-maxage={$this->mCacheDuration}," . |
||
| 198 | "public,max-age={$this->mCacheDuration}" ); |
||
| 199 | } |
||
| 200 | |||
| 201 | } else { |
||
| 202 | # always expired, always modified |
||
| 203 | header( "Expires: Mon, 26 Jul 1997 05:00:00 GMT" ); // Date in the past |
||
| 204 | header( "Cache-Control: no-cache, must-revalidate" ); // HTTP/1.1 |
||
| 205 | header( "Pragma: no-cache" ); // HTTP/1.0 |
||
| 206 | } |
||
| 207 | |||
| 208 | if ( $this->mVary ) { |
||
| 209 | header( "Vary: " . $this->mVary ); |
||
| 210 | } |
||
| 211 | } |
||
| 212 | |||
| 213 | /** |
||
| 214 | * checkLastModified tells the client to use the client-cached response if |
||
| 215 | * possible. If successful, the AjaxResponse is disabled so that |
||
| 216 | * any future call to AjaxResponse::printText() have no effect. |
||
| 217 | * |
||
| 218 | * @param string $timestamp |
||
| 219 | * @return bool Returns true if the response code was set to 304 Not Modified. |
||
| 220 | */ |
||
| 221 | function checkLastModified( $timestamp ) { |
||
| 222 | global $wgCachePages, $wgCacheEpoch, $wgUser; |
||
| 223 | $fname = 'AjaxResponse::checkLastModified'; |
||
| 224 | |||
| 225 | if ( !$timestamp || $timestamp == '19700101000000' ) { |
||
| 226 | wfDebug( "$fname: CACHE DISABLED, NO TIMESTAMP", 'private' ); |
||
| 227 | return false; |
||
| 228 | } |
||
| 229 | |||
| 230 | if ( !$wgCachePages ) { |
||
| 231 | wfDebug( "$fname: CACHE DISABLED", 'private' ); |
||
| 232 | return false; |
||
| 233 | } |
||
| 234 | |||
| 235 | $timestamp = wfTimestamp( TS_MW, $timestamp ); |
||
| 236 | $lastmod = wfTimestamp( TS_RFC2822, max( $timestamp, $wgUser->getTouched(), $wgCacheEpoch ) ); |
||
| 237 | |||
| 238 | if ( !empty( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) { |
||
| 239 | # IE sends sizes after the date like this: |
||
| 240 | # Wed, 20 Aug 2003 06:51:19 GMT; length=5202 |
||
| 241 | # this breaks strtotime(). |
||
| 242 | $modsince = preg_replace( '/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"] ); |
||
| 243 | $modsinceTime = strtotime( $modsince ); |
||
| 244 | $ismodsince = wfTimestamp( TS_MW, $modsinceTime ? $modsinceTime : 1 ); |
||
| 245 | wfDebug( "$fname: -- client send If-Modified-Since: $modsince", 'private' ); |
||
| 246 | wfDebug( "$fname: -- we might send Last-Modified : $lastmod", 'private' ); |
||
| 247 | |||
| 248 | if ( ( $ismodsince >= $timestamp ) |
||
| 249 | && $wgUser->validateCache( $ismodsince ) && |
||
| 250 | $ismodsince >= $wgCacheEpoch |
||
| 251 | ) { |
||
| 252 | ini_set( 'zlib.output_compression', 0 ); |
||
| 253 | $this->setResponseCode( 304 ); |
||
| 254 | $this->disable(); |
||
| 255 | $this->mLastModified = $lastmod; |
||
| 256 | |||
| 257 | wfDebug( "$fname: CACHED client: $ismodsince ; user: {$wgUser->getTouched()} ; " . |
||
| 258 | "page: $timestamp ; site $wgCacheEpoch", 'private' ); |
||
| 259 | |||
| 260 | return true; |
||
| 261 | } else { |
||
| 262 | wfDebug( "$fname: READY client: $ismodsince ; user: {$wgUser->getTouched()} ; " . |
||
| 263 | "page: $timestamp ; site $wgCacheEpoch", 'private' ); |
||
| 264 | $this->mLastModified = $lastmod; |
||
| 265 | } |
||
| 266 | } else { |
||
| 267 | wfDebug( "$fname: client did not send If-Modified-Since header", 'private' ); |
||
| 268 | $this->mLastModified = $lastmod; |
||
| 269 | } |
||
| 270 | return false; |
||
| 271 | } |
||
| 272 | |||
| 273 | /** |
||
| 274 | * @param string $mckey |
||
| 275 | * @param int $touched |
||
| 276 | * @return bool |
||
| 277 | */ |
||
| 278 | function loadFromMemcached( $mckey, $touched ) { |
||
| 279 | if ( !$touched ) { |
||
| 280 | return false; |
||
| 281 | } |
||
| 282 | |||
| 283 | $mcvalue = ObjectCache::getMainWANInstance()->get( $mckey ); |
||
| 284 | if ( $mcvalue ) { |
||
| 285 | # Check to see if the value has been invalidated |
||
| 286 | if ( $touched <= $mcvalue['timestamp'] ) { |
||
| 287 | wfDebug( "Got $mckey from cache" ); |
||
| 288 | $this->mText = $mcvalue['value']; |
||
| 289 | |||
| 290 | return true; |
||
| 291 | } else { |
||
| 292 | wfDebug( "$mckey has expired" ); |
||
| 293 | } |
||
| 294 | } |
||
| 295 | |||
| 296 | return false; |
||
| 297 | } |
||
| 298 | |||
| 299 | /** |
||
| 300 | * @param string $mckey |
||
| 301 | * @param int $expiry |
||
| 302 | * @return bool |
||
| 303 | */ |
||
| 304 | function storeInMemcached( $mckey, $expiry = 86400 ) { |
||
| 305 | ObjectCache::getMainWANInstance()->set( $mckey, |
||
| 306 | [ |
||
| 307 | 'timestamp' => wfTimestampNow(), |
||
| 308 | 'value' => $this->mText |
||
| 309 | ], $expiry |
||
| 310 | ); |
||
| 311 | |||
| 312 | return true; |
||
| 313 | } |
||
| 314 | } |
||
| 315 |
In PHP, under loose comparison (like
==, or!=, orswitchconditions), values of different types might be equal.For
stringvalues, the empty string''is a special case, in particular the following results might be unexpected: