Index: branches/REL1_15/phase3/RELEASE-NOTES |
— | — | @@ -3,10 +3,8 @@ |
4 | 4 | Security reminder: MediaWiki does not require PHP's register_globals |
5 | 5 | setting since version 1.2.0. If you have it on, turn it *off* if you can. |
6 | 6 | |
7 | | -== MediaWiki 1.15.4 == |
| 7 | +== MediaWiki 1.15.5 == |
8 | 8 | |
9 | | -2010-05-28 |
10 | | - |
11 | 9 | This is a security and maintenance release. |
12 | 10 | |
13 | 11 | MediaWiki is now using a "continuous integration" development model with |
— | — | @@ -20,6 +18,14 @@ |
21 | 19 | Those wishing to use the latest code instead of a branch release can obtain |
22 | 20 | it from source control: http://www.mediawiki.org/wiki/Download_from_SVN |
23 | 21 | |
| 22 | +== Changes since 1.15.4 == |
| 23 | + |
| 24 | +* (bug xxxxx) Fixed Cache-Control headers sent from API modules, to protect |
| 25 | + user privacy in the case where an attacker can access the wiki through the |
| 26 | + same HTTP proxy as a logged-in user. |
| 27 | +* Fixed a minor cookie header parsing issue causing incorrect Cache-Control |
| 28 | + headers to be sent. |
| 29 | + |
24 | 30 | == Changes since 1.15.3 == |
25 | 31 | |
26 | 32 | * (bug 23534) Fixed SQL query error in API list=allusers. |
Index: branches/REL1_15/phase3/includes/OutputPage.php |
— | — | @@ -733,7 +733,7 @@ |
734 | 734 | $cvCookies = $this->getCacheVaryCookies(); |
735 | 735 | foreach ( $cvCookies as $cookieName ) { |
736 | 736 | # Check for a simple string match, like the way squid does it |
737 | | - if ( strpos( $cookieHeader, $cookieName ) ) { |
| 737 | + if ( strpos( $cookieHeader, $cookieName ) !== false ) { |
738 | 738 | wfDebug( __METHOD__.": found $cookieName\n" ); |
739 | 739 | return true; |
740 | 740 | } |
Property changes on: branches/REL1_15/phase3/includes/OutputPage.php |
___________________________________________________________________ |
Added: svn:mergeinfo |
741 | 741 | Merged /trunk/phase3/includes/specials/OutputPage.php:r48993 |
742 | 742 | Merged /trunk/phase3/includes/OutputPage.php:r48836,48886,48892,48989,48992,49002,49051,49068,49086,49191-49192,49212,49682,49685,49730,49775,49954,49956,49999,50041,50054,50070,50132,50134,50169,50215,50218,50328,50470,50580,51587,54828,69771 |
Index: branches/REL1_15/phase3/includes/api/ApiQueryRandom.php |
— | — | @@ -121,6 +121,10 @@ |
122 | 122 | return $vals; |
123 | 123 | } |
124 | 124 | |
| 125 | + public function getCacheMode( $params ) { |
| 126 | + return 'public'; |
| 127 | + } |
| 128 | + |
125 | 129 | public function getAllowedParams() { |
126 | 130 | return array ( |
127 | 131 | 'namespace' => array( |
— | — | @@ -161,4 +165,4 @@ |
162 | 166 | public function getVersion() { |
163 | 167 | return __CLASS__ . ': $Id: ApiQueryRandom.php overlordq$'; |
164 | 168 | } |
165 | | -} |
\ No newline at end of file |
| 169 | +} |
Index: branches/REL1_15/phase3/includes/api/ApiQuerySiteinfo.php |
— | — | @@ -373,6 +373,9 @@ |
374 | 374 | return $this->getResult()->addValue( 'query', $property, $data ); |
375 | 375 | } |
376 | 376 | |
| 377 | + public function getCacheMode( $params ) { |
| 378 | + return 'public'; |
| 379 | + } |
377 | 380 | |
378 | 381 | public function getAllowedParams() { |
379 | 382 | return array( |
— | — | @@ -441,4 +444,4 @@ |
442 | 445 | public function getVersion() { |
443 | 446 | return __CLASS__ . ': $Id$'; |
444 | 447 | } |
445 | | -} |
\ No newline at end of file |
| 448 | +} |
Index: branches/REL1_15/phase3/includes/api/ApiQueryLangLinks.php |
— | — | @@ -92,6 +92,10 @@ |
93 | 93 | $db->freeResult($res); |
94 | 94 | } |
95 | 95 | |
| 96 | + public function getCacheMode( $params ) { |
| 97 | + return 'public'; |
| 98 | + } |
| 99 | + |
96 | 100 | public function getAllowedParams() { |
97 | 101 | return array( |
98 | 102 | 'limit' => array( |
— | — | @@ -126,4 +130,4 @@ |
127 | 131 | public function getVersion() { |
128 | 132 | return __CLASS__ . ': $Id$'; |
129 | 133 | } |
130 | | -} |
\ No newline at end of file |
| 134 | +} |
Index: branches/REL1_15/phase3/includes/api/ApiParse.php |
— | — | @@ -38,6 +38,9 @@ |
39 | 39 | } |
40 | 40 | |
41 | 41 | public function execute() { |
| 42 | + // The data is hot but user-dependent, like page views, so we set vary cookies |
| 43 | + $this->getMain()->setCacheMode( 'anon-public-user-private' ); |
| 44 | + |
42 | 45 | // Get parameters |
43 | 46 | $params = $this->extractRequestParams(); |
44 | 47 | $text = $params['text']; |
Index: branches/REL1_15/phase3/includes/api/ApiQueryAllpages.php |
— | — | @@ -43,6 +43,10 @@ |
44 | 44 | $this->run(); |
45 | 45 | } |
46 | 46 | |
| 47 | + public function getCacheMode( $params ) { |
| 48 | + return 'public'; |
| 49 | + } |
| 50 | + |
47 | 51 | public function executeGenerator($resultPageSet) { |
48 | 52 | if ($resultPageSet->isResolvingRedirects()) |
49 | 53 | $this->dieUsage('Use "gapfilterredir=nonredirects" option instead of "redirects" when using allpages as a generator', 'params'); |
— | — | @@ -270,4 +274,4 @@ |
271 | 275 | public function getVersion() { |
272 | 276 | return __CLASS__ . ': $Id$'; |
273 | 277 | } |
274 | | -} |
\ No newline at end of file |
| 278 | +} |
Index: branches/REL1_15/phase3/includes/api/ApiQueryImageInfo.php |
— | — | @@ -239,6 +239,10 @@ |
240 | 240 | return $retval; |
241 | 241 | } |
242 | 242 | |
| 243 | + public function getCacheMode( $params ) { |
| 244 | + return 'public'; |
| 245 | + } |
| 246 | + |
243 | 247 | private function getContinueStr($img) |
244 | 248 | { |
245 | 249 | return $img->getOriginalTitle()->getText() . |
Index: branches/REL1_15/phase3/includes/api/ApiMain.php |
— | — | @@ -121,7 +121,9 @@ |
122 | 122 | |
123 | 123 | |
124 | 124 | private $mPrinter, $mModules, $mModuleNames, $mFormats, $mFormatNames; |
125 | | - private $mResult, $mAction, $mShowVersions, $mEnableWrite, $mRequest, $mInternalMode, $mSquidMaxage; |
| 125 | + private $mResult, $mAction, $mShowVersions, $mEnableWrite, $mRequest, $mInternalMode; |
| 126 | + private $mCacheMode = 'private'; |
| 127 | + private $mCacheControl = array(); |
126 | 128 | |
127 | 129 | /** |
128 | 130 | * Constructs an instance of ApiMain that utilizes the module and format specified by $request. |
— | — | @@ -164,7 +166,6 @@ |
165 | 167 | |
166 | 168 | $this->mRequest = & $request; |
167 | 169 | |
168 | | - $this->mSquidMaxage = -1; // flag for executeActionWithErrorHandling() |
169 | 170 | $this->mCommit = false; |
170 | 171 | } |
171 | 172 | |
— | — | @@ -199,10 +200,92 @@ |
200 | 201 | * Set how long the response should be cached. |
201 | 202 | */ |
202 | 203 | public function setCacheMaxAge($maxage) { |
203 | | - $this->mSquidMaxage = $maxage; |
| 204 | + $this->setCacheControl( array( |
| 205 | + 'max-age' => $maxage, |
| 206 | + 's-maxage' => $maxage |
| 207 | + ) ); |
204 | 208 | } |
205 | 209 | |
206 | 210 | /** |
| 211 | + * Set the type of caching headers which will be sent. |
| 212 | + * |
| 213 | + * @param $mode One of: |
| 214 | + * - 'public': Cache this object in public caches, if the maxage or smaxage |
| 215 | + * parameter is set, or if setCacheMaxAge() was called. If a maximum age is |
| 216 | + * not provided by any of these means, the object will be private. |
| 217 | + * - 'private': Cache this object only in private client-side caches. |
| 218 | + * - 'anon-public-user-private': Make this object cacheable for logged-out |
| 219 | + * users, but private for logged-in users. IMPORTANT: If this is set, it must be |
| 220 | + * set consistently for a given URL, it cannot be set differently depending on |
| 221 | + * things like the contents of the database, or whether the user is logged in. |
| 222 | + * |
| 223 | + * If the wiki does not allow anonymous users to read it, the mode set here |
| 224 | + * will be ignored, and private caching headers will always be sent. In other words, |
| 225 | + * the "public" mode is equivalent to saying that the data sent is as public as a page |
| 226 | + * view. |
| 227 | + * |
| 228 | + * For user-dependent data, the private mode should generally be used. The |
| 229 | + * anon-public-user-private mode should only be used where there is a particularly |
| 230 | + * good performance reason for caching the anonymous response, but where the |
| 231 | + * response to logged-in users may differ, or may contain private data. |
| 232 | + * |
| 233 | + * If this function is never called, then the default will be the private mode. |
| 234 | + */ |
| 235 | + public function setCacheMode( $mode ) { |
| 236 | + if ( !in_array( $mode, array( 'private', 'public', 'anon-public-user-private' ) ) ) { |
| 237 | + wfDebug( __METHOD__.": unrecognised cache mode \"$mode\"\n" ); |
| 238 | + // Ignore for forwards-compatibility |
| 239 | + return; |
| 240 | + } |
| 241 | + |
| 242 | + if ( !in_array( 'read', User::getGroupPermissions( array( '*' ) ), true ) ) { |
| 243 | + // Private wiki, only private headers |
| 244 | + if ( $mode !== 'private' ) { |
| 245 | + wfDebug( __METHOD__.": ignoring request for $mode cache mode, private wiki\n" ); |
| 246 | + return; |
| 247 | + } |
| 248 | + } |
| 249 | + |
| 250 | + wfDebug( __METHOD__.": setting cache mode $mode\n" ); |
| 251 | + $this->mCacheMode = $mode; |
| 252 | + } |
| 253 | + |
| 254 | + /** |
| 255 | + * @deprecated Private caching is now the default, so there is usually no |
| 256 | + * need to call this function. If there is a need, you can use |
| 257 | + * $this->setCacheMode('private') |
| 258 | + */ |
| 259 | + public function setCachePrivate() { |
| 260 | + $this->setCacheMode( 'private' ); |
| 261 | + } |
| 262 | + |
| 263 | + /** |
| 264 | + * Set directives (key/value pairs) for the Cache-Control header. |
| 265 | + * Boolean values will be formatted as such, by including or omitting |
| 266 | + * without an equals sign. |
| 267 | + * |
| 268 | + * Cache control values set here will only be used if the cache mode is not |
| 269 | + * private, see setCacheMode(). |
| 270 | + */ |
| 271 | + public function setCacheControl( $directives ) { |
| 272 | + $this->mCacheControl = $directives + $this->mCacheControl; |
| 273 | + } |
| 274 | + |
| 275 | + /** |
| 276 | + * Make sure Vary: Cookie and friends are set. Use this when the output of a request |
| 277 | + * may be cached for anons but may not be cached for logged-in users. |
| 278 | + * |
| 279 | + * WARNING: This function must be called CONSISTENTLY for a given URL. This means that a |
| 280 | + * given URL must either always or never call this function; if it sometimes does and |
| 281 | + * sometimes doesn't, stuff will break. |
| 282 | + * |
| 283 | + * @deprecated Use setCacheMode( 'anon-public-user-private' ) |
| 284 | + */ |
| 285 | + public function setVaryCookie() { |
| 286 | + $this->setCacheMode( 'anon-public-user-private' ); |
| 287 | + } |
| 288 | + |
| 289 | + /** |
207 | 290 | * Create an instance of an output formatter by its name |
208 | 291 | */ |
209 | 292 | public function createPrinterByName($format) { |
— | — | @@ -251,7 +334,7 @@ |
252 | 335 | $errCode = $this->substituteResultWithError($e); |
253 | 336 | |
254 | 337 | // Error results should not be cached |
255 | | - $this->setCacheMaxAge(0); |
| 338 | + $this->setCacheMode( 'private' ); |
256 | 339 | |
257 | 340 | $headerStr = 'MediaWiki-API-Error: ' . $errCode; |
258 | 341 | if ($e->getCode() === 0) |
— | — | @@ -267,28 +350,81 @@ |
268 | 351 | $this->printResult(true); |
269 | 352 | } |
270 | 353 | |
271 | | - if($this->mSquidMaxage == -1) |
272 | | - { |
273 | | - # Nobody called setCacheMaxAge(), use the (s)maxage parameters |
274 | | - $smaxage = $this->getParameter('smaxage'); |
275 | | - $maxage = $this->getParameter('maxage'); |
276 | | - } |
277 | | - else |
278 | | - $smaxage = $maxage = $this->mSquidMaxage; |
| 354 | + // Send cache headers after any code which might generate an error, to |
| 355 | + // avoid sending public cache headers for errors. |
| 356 | + $this->sendCacheHeaders(); |
279 | 357 | |
280 | | - // Set the cache expiration at the last moment, as any errors may change the expiration. |
281 | | - // if $this->mSquidMaxage == 0, the expiry time is set to the first second of unix epoch |
282 | | - $exp = min($smaxage, $maxage); |
283 | | - $expires = ($exp == 0 ? 1 : time() + $exp); |
284 | | - header('Expires: ' . wfTimestamp(TS_RFC2822, $expires)); |
285 | | - header('Cache-Control: s-maxage=' . $smaxage . ', must-revalidate, max-age=' . $maxage); |
286 | | - |
287 | 358 | if($this->mPrinter->getIsHtml()) |
288 | 359 | echo wfReportTime(); |
289 | 360 | |
290 | 361 | ob_end_flush(); |
291 | 362 | } |
292 | 363 | |
| 364 | + protected function sendCacheHeaders() { |
| 365 | + if ( $this->mCacheMode == 'private' ) { |
| 366 | + header( 'Cache-Control: private' ); |
| 367 | + return; |
| 368 | + } |
| 369 | + |
| 370 | + if ( $this->mCacheMode == 'anon-public-user-private' ) { |
| 371 | + global $wgOut; |
| 372 | + header( 'Vary: Accept-Encoding, Cookie' ); |
| 373 | + header( $wgOut->getXVO() ); |
| 374 | + if ( session_id() != '' || $wgOut->haveCacheVaryCookies() ) { |
| 375 | + // Logged in, mark this request private |
| 376 | + header( 'Cache-Control: private' ); |
| 377 | + return; |
| 378 | + } |
| 379 | + // Logged out, send normal public headers below |
| 380 | + } else /* if public */ { |
| 381 | + // Give a debugging message if the user object is unstubbed on a public request |
| 382 | + global $wgUser; |
| 383 | + if ( !( $wgUser instanceof StubUser ) ) { |
| 384 | + wfDebug( __METHOD__." \$wgUser is unstubbed on a public request!\n" ); |
| 385 | + } |
| 386 | + } |
| 387 | + |
| 388 | + // If nobody called setCacheMaxAge(), use the (s)maxage parameters |
| 389 | + if ( !isset( $this->mCacheControl['s-maxage'] ) ) { |
| 390 | + $this->mCacheControl['s-maxage'] = $this->getParameter( 'smaxage' ); |
| 391 | + } |
| 392 | + if ( !isset( $this->mCacheControl['max-age'] ) ) { |
| 393 | + $this->mCacheControl['max-age'] = $this->getParameter( 'maxage' ); |
| 394 | + } |
| 395 | + |
| 396 | + if ( !$this->mCacheControl['s-maxage'] && !$this->mCacheControl['max-age'] ) { |
| 397 | + // Public cache not requested |
| 398 | + // Sending a Vary header in this case is harmless, and protects us |
| 399 | + // against conditional calls of setCacheMaxAge(). |
| 400 | + header( 'Cache-Control: private' ); |
| 401 | + return; |
| 402 | + } |
| 403 | + |
| 404 | + $this->mCacheControl['public'] = true; |
| 405 | + |
| 406 | + // Send an Expires header |
| 407 | + $maxAge = min( $this->mCacheControl['s-maxage'], $this->mCacheControl['max-age'] ); |
| 408 | + $expiryUnixTime = ( $maxAge == 0 ? 1 : time() + $maxAge ); |
| 409 | + header( 'Expires: ' . wfTimestamp( TS_RFC2822, $expiryUnixTime ) ); |
| 410 | + |
| 411 | + // Construct the Cache-Control header |
| 412 | + $ccHeader = ''; |
| 413 | + $separator = ''; |
| 414 | + foreach ( $this->mCacheControl as $name => $value ) { |
| 415 | + if ( is_bool( $value ) ) { |
| 416 | + if ( $value ) { |
| 417 | + $ccHeader .= $separator . $name; |
| 418 | + $separator = ', '; |
| 419 | + } |
| 420 | + } else { |
| 421 | + $ccHeader .= $separator . "$name=$value"; |
| 422 | + $separator = ', '; |
| 423 | + } |
| 424 | + } |
| 425 | + |
| 426 | + header( "Cache-Control: $ccHeader" ); |
| 427 | + } |
| 428 | + |
293 | 429 | /** |
294 | 430 | * Replace the result data with the information about an exception. |
295 | 431 | * Returns the error code |
Index: branches/REL1_15/phase3/includes/api/ApiOpenSearch.php |
— | — | @@ -53,6 +53,7 @@ |
54 | 54 | |
55 | 55 | // Open search results may be stored for a very long time |
56 | 56 | $this->getMain()->setCacheMaxAge(1200); |
| 57 | + $this->getMain()->setCacheMode( 'public' ); |
57 | 58 | |
58 | 59 | $srchres = PrefixSearch::titleSearch( $search, $limit, $namespaces ); |
59 | 60 | |
Index: branches/REL1_15/phase3/includes/api/ApiQueryWatchlist.php |
— | — | @@ -345,4 +345,4 @@ |
346 | 346 | public function getVersion() { |
347 | 347 | return __CLASS__ . ': $Id$'; |
348 | 348 | } |
349 | | -} |
\ No newline at end of file |
| 349 | +} |
Index: branches/REL1_15/phase3/includes/api/ApiExpandTemplates.php |
— | — | @@ -42,6 +42,9 @@ |
43 | 43 | } |
44 | 44 | |
45 | 45 | public function execute() { |
| 46 | + // Cache may vary on $wgUser because ParserOptions gets data from it |
| 47 | + $this->getMain()->setCacheMode( 'anon-public-user-private' ); |
| 48 | + |
46 | 49 | // Get parameters |
47 | 50 | $params = $this->extractRequestParams(); |
48 | 51 | |
Index: branches/REL1_15/phase3/includes/api/ApiQueryBase.php |
— | — | @@ -47,6 +47,17 @@ |
48 | 48 | } |
49 | 49 | |
50 | 50 | /** |
| 51 | + * Get the cache mode for the data generated by this module. Override this |
| 52 | + * in the module subclass. |
| 53 | + * |
| 54 | + * Public caching will only be allowed if *all* the modules that supply |
| 55 | + * data for a given request return a cache mode of public. |
| 56 | + */ |
| 57 | + public function getCacheMode( $params ) { |
| 58 | + return 'private'; |
| 59 | + } |
| 60 | + |
| 61 | + /** |
51 | 62 | * Blank the internal arrays with query parameters |
52 | 63 | */ |
53 | 64 | protected function resetQueryParams() { |
Index: branches/REL1_15/phase3/includes/api/ApiQueryRevisions.php |
— | — | @@ -411,6 +411,17 @@ |
412 | 412 | return $vals; |
413 | 413 | } |
414 | 414 | |
| 415 | + public function getCacheMode( $params ) { |
| 416 | + if ( isset( $params['token'] ) ) { |
| 417 | + return 'private'; |
| 418 | + } |
| 419 | + if ( !is_null( $params['prop'] ) && in_array( 'parsedcomment', $params['prop'] ) ) { |
| 420 | + // formatComment() calls wfMsg() among other things |
| 421 | + return 'anon-public-user-private'; |
| 422 | + } |
| 423 | + return 'public'; |
| 424 | + } |
| 425 | + |
415 | 426 | public function getAllowedParams() { |
416 | 427 | return array ( |
417 | 428 | 'prop' => array ( |
Index: branches/REL1_15/phase3/includes/api/ApiQueryExternalLinks.php |
— | — | @@ -81,6 +81,10 @@ |
82 | 82 | $db->freeResult($res); |
83 | 83 | } |
84 | 84 | |
| 85 | + public function getCacheMode( $params ) { |
| 86 | + return 'public'; |
| 87 | + } |
| 88 | + |
85 | 89 | public function getAllowedParams() { |
86 | 90 | return array( |
87 | 91 | 'limit' => array( |
— | — | @@ -115,4 +119,4 @@ |
116 | 120 | public function getVersion() { |
117 | 121 | return __CLASS__ . ': $Id$'; |
118 | 122 | } |
119 | | -} |
\ No newline at end of file |
| 123 | +} |
Index: branches/REL1_15/phase3/includes/api/ApiQueryCategoryInfo.php |
— | — | @@ -97,6 +97,10 @@ |
98 | 98 | $db->freeResult($res); |
99 | 99 | } |
100 | 100 | |
| 101 | + public function getCacheMode( $params ) { |
| 102 | + return 'public'; |
| 103 | + } |
| 104 | + |
101 | 105 | public function getAllowedParams() { |
102 | 106 | return array ( |
103 | 107 | 'continue' => null, |
— | — | @@ -120,4 +124,4 @@ |
121 | 125 | public function getVersion() { |
122 | 126 | return __CLASS__ . ': $Id$'; |
123 | 127 | } |
124 | | -} |
\ No newline at end of file |
| 128 | +} |
Index: branches/REL1_15/phase3/includes/api/ApiQueryUsers.php |
— | — | @@ -138,6 +138,10 @@ |
139 | 139 | return $this->getResult()->setIndexedTagName_internal(array('query', $this->getModuleName()), 'user'); |
140 | 140 | } |
141 | 141 | |
| 142 | + public function getCacheMode( $params ) { |
| 143 | + return 'public'; |
| 144 | + } |
| 145 | + |
142 | 146 | public function getAllowedParams() { |
143 | 147 | return array ( |
144 | 148 | 'prop' => array ( |
Index: branches/REL1_15/phase3/includes/api/ApiQueryUserContributions.php |
— | — | @@ -287,6 +287,12 @@ |
288 | 288 | wfTimestamp(TS_ISO_8601, $row->rev_timestamp); |
289 | 289 | } |
290 | 290 | |
| 291 | + public function getCacheMode( $params ) { |
| 292 | + // This module provides access to patrol flags if |
| 293 | + // the requester is logged in |
| 294 | + return 'anon-public-user-private'; |
| 295 | + } |
| 296 | + |
291 | 297 | public function getAllowedParams() { |
292 | 298 | return array ( |
293 | 299 | 'limit' => array ( |
— | — | @@ -372,4 +378,4 @@ |
373 | 379 | public function getVersion() { |
374 | 380 | return __CLASS__ . ': $Id$'; |
375 | 381 | } |
376 | | -} |
\ No newline at end of file |
| 382 | +} |
Index: branches/REL1_15/phase3/includes/api/ApiQuerySearch.php |
— | — | @@ -122,6 +122,10 @@ |
123 | 123 | } |
124 | 124 | } |
125 | 125 | |
| 126 | + public function getCacheMode( $params ) { |
| 127 | + return 'public'; |
| 128 | + } |
| 129 | + |
126 | 130 | public function getAllowedParams() { |
127 | 131 | return array ( |
128 | 132 | 'search' => null, |
Index: branches/REL1_15/phase3/includes/api/ApiQueryRecentChanges.php |
— | — | @@ -353,6 +353,20 @@ |
354 | 354 | } |
355 | 355 | } |
356 | 356 | |
| 357 | + public function getCacheMode( $params ) { |
| 358 | + if ( isset( $params['show'] ) ) { |
| 359 | + foreach ( $params['show'] as $show ) { |
| 360 | + if ( $show === 'patrolled' || $show === '!patrolled' ) { |
| 361 | + return 'private'; |
| 362 | + } |
| 363 | + } |
| 364 | + } |
| 365 | + if ( isset( $params['token'] ) ) { |
| 366 | + return 'private'; |
| 367 | + } |
| 368 | + return 'public'; |
| 369 | + } |
| 370 | + |
357 | 371 | public function getAllowedParams() { |
358 | 372 | return array ( |
359 | 373 | 'start' => array ( |
— | — | @@ -455,4 +469,4 @@ |
456 | 470 | public function getVersion() { |
457 | 471 | return __CLASS__ . ': $Id$'; |
458 | 472 | } |
459 | | -} |
\ No newline at end of file |
| 473 | +} |
Index: branches/REL1_15/phase3/includes/api/ApiQueryAllCategories.php |
— | — | @@ -44,6 +44,10 @@ |
45 | 45 | $this->run(); |
46 | 46 | } |
47 | 47 | |
| 48 | + public function getCacheMode( $params ) { |
| 49 | + return 'public'; |
| 50 | + } |
| 51 | + |
48 | 52 | public function executeGenerator($resultPageSet) { |
49 | 53 | $this->run($resultPageSet); |
50 | 54 | } |
— | — | @@ -177,4 +181,4 @@ |
178 | 182 | public function getVersion() { |
179 | 183 | return __CLASS__ . ': $Id$'; |
180 | 184 | } |
181 | | -} |
\ No newline at end of file |
| 185 | +} |
Index: branches/REL1_15/phase3/includes/api/ApiQuery.php |
— | — | @@ -209,11 +209,17 @@ |
210 | 210 | $this->InstantiateModules($modules, 'list', $this->mQueryListModules); |
211 | 211 | $this->InstantiateModules($modules, 'meta', $this->mQueryMetaModules); |
212 | 212 | |
| 213 | + $cacheMode = 'public'; |
| 214 | + |
213 | 215 | // |
214 | 216 | // If given, execute generator to substitute user supplied data with generated data. |
215 | 217 | // |
216 | | - if (isset ($this->params['generator'])) { |
217 | | - $this->executeGeneratorModule($this->params['generator'], $modules); |
| 218 | + if ( isset ( $this->params['generator'] ) ) { |
| 219 | + $generator = $this->newGenerator( $this->params['generator'] ); |
| 220 | + $params = $generator->extractRequestParams(); |
| 221 | + $cacheMode = $this->mergeCacheMode( $cacheMode, |
| 222 | + $generator->getCacheMode( $params ) ); |
| 223 | + $this->executeGeneratorModule( $generator, $modules ); |
218 | 224 | } else { |
219 | 225 | // Append custom fields and populate page/revision information |
220 | 226 | $this->addCustomFldsToPageSet($modules, $this->mPageSet); |
— | — | @@ -229,14 +235,38 @@ |
230 | 236 | // Execute all requested modules. |
231 | 237 | // |
232 | 238 | foreach ($modules as $module) { |
| 239 | + $params = $module->extractRequestParams(); |
| 240 | + $cacheMode = $this->mergeCacheMode( |
| 241 | + $cacheMode, $module->getCacheMode( $params ) ); |
233 | 242 | $module->profileIn(); |
234 | 243 | $module->execute(); |
235 | 244 | wfRunHooks('APIQueryAfterExecute', array(&$module)); |
236 | 245 | $module->profileOut(); |
237 | 246 | } |
| 247 | + |
| 248 | + // Set the cache mode |
| 249 | + $this->getMain()->setCacheMode( $cacheMode ); |
238 | 250 | } |
239 | 251 | |
240 | 252 | /** |
| 253 | + * Update a cache mode string, applying the cache mode of a new module to it. |
| 254 | + * The cache mode may increase in the level of privacy, but public modules |
| 255 | + * added to private data do not decrease the level of privacy. |
| 256 | + */ |
| 257 | + protected function mergeCacheMode( $cacheMode, $modCacheMode ) { |
| 258 | + if ( $modCacheMode === 'anon-public-user-private' ) { |
| 259 | + if ( $cacheMode !== 'private' ) { |
| 260 | + $cacheMode = 'anon-public-user-private'; |
| 261 | + } |
| 262 | + } elseif ( $modCacheMode === 'public' ) { |
| 263 | + // do nothing, if it's public already it will stay public |
| 264 | + } else { // private |
| 265 | + $cacheMode = 'private'; |
| 266 | + } |
| 267 | + return $cacheMode; |
| 268 | + } |
| 269 | + |
| 270 | + /** |
241 | 271 | * Query modules may optimize data requests through the $this->getPageSet() object |
242 | 272 | * by adding extra fields from the page table. |
243 | 273 | * This function will gather all the extra request fields from the modules. |
— | — | @@ -409,13 +439,9 @@ |
410 | 440 | } |
411 | 441 | |
412 | 442 | /** |
413 | | - * For generator mode, execute generator, and use its output as new |
414 | | - * ApiPageSet |
415 | | - * @param $generatorName string Module name |
416 | | - * @param $modules array of module objects |
| 443 | + * Create a generator object of the given type and return it |
417 | 444 | */ |
418 | | - protected function executeGeneratorModule($generatorName, $modules) { |
419 | | - |
| 445 | + public function newGenerator( $generatorName ) { |
420 | 446 | // Find class that implements requested generator |
421 | 447 | if (isset ($this->mQueryListModules[$generatorName])) { |
422 | 448 | $className = $this->mQueryListModules[$generatorName]; |
— | — | @@ -432,9 +458,20 @@ |
433 | 459 | $generator = new $className ($this, $generatorName); |
434 | 460 | if (!$generator instanceof ApiQueryGeneratorBase) |
435 | 461 | $this->dieUsage("Module $generatorName cannot be used as a generator", "badgenerator"); |
436 | | - |
437 | 462 | $generator->setGeneratorMode(); |
| 463 | + return $generator; |
| 464 | + } |
438 | 465 | |
| 466 | + /** |
| 467 | + * For generator mode, execute generator, and use its output as new |
| 468 | + * ApiPageSet |
| 469 | + * @param $generatorName string Module name |
| 470 | + * @param $modules array of module objects |
| 471 | + */ |
| 472 | + protected function executeGeneratorModule( $generator, $modules ) { |
| 473 | + // Generator results |
| 474 | + $resultPageSet = new ApiPageSet( $this, $this->redirects, $this->convertTitles ); |
| 475 | + |
439 | 476 | // Add any additional fields modules may need |
440 | 477 | $generator->requestExtraData($this->mPageSet); |
441 | 478 | $this->addCustomFldsToPageSet($modules, $resultPageSet); |
— | — | @@ -584,4 +621,4 @@ |
585 | 622 | $vers[] = $psModule->getVersion(); |
586 | 623 | return $vers; |
587 | 624 | } |
588 | | -} |
\ No newline at end of file |
| 625 | +} |
Index: branches/REL1_15/phase3/includes/api/ApiQueryLinks.php |
— | — | @@ -64,6 +64,10 @@ |
65 | 65 | $this->run(); |
66 | 66 | } |
67 | 67 | |
| 68 | + public function getCacheMode( $params ) { |
| 69 | + return 'public'; |
| 70 | + } |
| 71 | + |
68 | 72 | public function executeGenerator($resultPageSet) { |
69 | 73 | $this->run($resultPageSet); |
70 | 74 | } |
— | — | @@ -206,4 +210,4 @@ |
207 | 211 | public function getVersion() { |
208 | 212 | return __CLASS__ . ': $Id$'; |
209 | 213 | } |
210 | | -} |
\ No newline at end of file |
| 214 | +} |
Index: branches/REL1_15/phase3/includes/api/ApiQueryExtLinksUsage.php |
— | — | @@ -41,6 +41,10 @@ |
42 | 42 | $this->run(); |
43 | 43 | } |
44 | 44 | |
| 45 | + public function getCacheMode( $params ) { |
| 46 | + return 'public'; |
| 47 | + } |
| 48 | + |
45 | 49 | public function executeGenerator($resultPageSet) { |
46 | 50 | $this->run($resultPageSet); |
47 | 51 | } |
— | — | @@ -211,4 +215,4 @@ |
212 | 216 | public function getVersion() { |
213 | 217 | return __CLASS__ . ': $Id$'; |
214 | 218 | } |
215 | | -} |
\ No newline at end of file |
| 219 | +} |
Index: branches/REL1_15/phase3/includes/api/ApiQueryBacklinks.php |
— | — | @@ -92,6 +92,10 @@ |
93 | 93 | $this->run(); |
94 | 94 | } |
95 | 95 | |
| 96 | + public function getCacheMode( $params ) { |
| 97 | + return 'public'; |
| 98 | + } |
| 99 | + |
96 | 100 | public function executeGenerator($resultPageSet) { |
97 | 101 | $this->run($resultPageSet); |
98 | 102 | } |
Index: branches/REL1_15/phase3/includes/api/ApiQueryCategories.php |
— | — | @@ -43,6 +43,10 @@ |
44 | 44 | $this->run(); |
45 | 45 | } |
46 | 46 | |
| 47 | + public function getCacheMode( $params ) { |
| 48 | + return 'public'; |
| 49 | + } |
| 50 | + |
47 | 51 | public function executeGenerator($resultPageSet) { |
48 | 52 | $this->run($resultPageSet); |
49 | 53 | } |
Index: branches/REL1_15/phase3/includes/api/ApiQueryCategoryMembers.php |
— | — | @@ -43,6 +43,10 @@ |
44 | 44 | $this->run(); |
45 | 45 | } |
46 | 46 | |
| 47 | + public function getCacheMode( $params ) { |
| 48 | + return 'public'; |
| 49 | + } |
| 50 | + |
47 | 51 | public function executeGenerator($resultPageSet) { |
48 | 52 | $this->run($resultPageSet); |
49 | 53 | } |
— | — | @@ -264,4 +268,4 @@ |
265 | 269 | public function getVersion() { |
266 | 270 | return __CLASS__ . ': $Id$'; |
267 | 271 | } |
268 | | -} |
\ No newline at end of file |
| 272 | +} |
Index: branches/REL1_15/phase3/includes/api/ApiQueryAllUsers.php |
— | — | @@ -181,6 +181,10 @@ |
182 | 182 | $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'u'); |
183 | 183 | } |
184 | 184 | |
| 185 | + public function getCacheMode( $params ) { |
| 186 | + return 'public'; |
| 187 | + } |
| 188 | + |
185 | 189 | public function getAllowedParams() { |
186 | 190 | return array ( |
187 | 191 | 'from' => null, |
Property changes on: branches/REL1_15/phase3/includes/api/ApiQueryAllUsers.php |
___________________________________________________________________ |
Modified: svn:mergeinfo |
188 | 192 | Merged /branches/REL1_16/phase3/includes/api/ApiQueryAllUsers.php:r69932 |
189 | 193 | Merged /trunk/phase3/includes/api/ApiQueryAllUsers.php:r69339,69347,69350,69369,69379,69776,69931 |
Index: branches/REL1_15/phase3/includes/api/ApiQueryInfo.php |
— | — | @@ -499,6 +499,26 @@ |
500 | 500 | } |
501 | 501 | } |
502 | 502 | |
| 503 | + public function getCacheMode( $params ) { |
| 504 | + $publicProps = array( |
| 505 | + 'protection', |
| 506 | + 'talkid', |
| 507 | + 'subjectid', |
| 508 | + 'url', |
| 509 | + ); |
| 510 | + if ( !is_null( $params['prop'] ) ) { |
| 511 | + foreach ( $params['prop'] as $prop ) { |
| 512 | + if ( !in_array( $prop, $publicProps ) ) { |
| 513 | + return 'private'; |
| 514 | + } |
| 515 | + } |
| 516 | + } |
| 517 | + if ( !is_null( $params['token'] ) ) { |
| 518 | + return 'private'; |
| 519 | + } |
| 520 | + return 'public'; |
| 521 | + } |
| 522 | + |
503 | 523 | public function getAllowedParams() { |
504 | 524 | return array ( |
505 | 525 | 'prop' => array ( |
— | — | @@ -509,7 +529,9 @@ |
510 | 530 | 'talkid', |
511 | 531 | 'subjectid', |
512 | 532 | 'url', |
513 | | - 'readable', |
| 533 | + 'readable', # private |
| 534 | + // If you add more properties here, please consider whether they |
| 535 | + // need to be added to getCacheMode() |
514 | 536 | )), |
515 | 537 | 'token' => array ( |
516 | 538 | ApiBase :: PARAM_DFLT => NULL, |
Index: branches/REL1_15/phase3/includes/api/ApiQueryDuplicateFiles.php |
— | — | @@ -43,6 +43,10 @@ |
44 | 44 | $this->run(); |
45 | 45 | } |
46 | 46 | |
| 47 | + public function getCacheMode( $params ) { |
| 48 | + return 'public'; |
| 49 | + } |
| 50 | + |
47 | 51 | public function executeGenerator($resultPageSet) { |
48 | 52 | $this->run($resultPageSet); |
49 | 53 | } |
Index: branches/REL1_15/phase3/includes/api/ApiQueryAllimages.php |
— | — | @@ -44,6 +44,10 @@ |
45 | 45 | $this->run(); |
46 | 46 | } |
47 | 47 | |
| 48 | + public function getCacheMode( $params ) { |
| 49 | + return 'public'; |
| 50 | + } |
| 51 | + |
48 | 52 | public function executeGenerator($resultPageSet) { |
49 | 53 | if ($resultPageSet->isResolvingRedirects()) |
50 | 54 | $this->dieUsage('Use "gaifilterredir=nonredirects" option instead of "redirects" when using allimages as a generator', 'params'); |
Index: branches/REL1_15/phase3/includes/api/ApiQueryAllmessages.php |
— | — | @@ -104,6 +104,16 @@ |
105 | 105 | $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'message'); |
106 | 106 | } |
107 | 107 | |
| 108 | + public function getCacheMode( $params ) { |
| 109 | + if ( is_null( $params['lang'] ) ) { |
| 110 | + // Language not specified, will be fetched from preferences |
| 111 | + return 'anon-public-user-private'; |
| 112 | + } else { |
| 113 | + // OK to cache |
| 114 | + return 'public'; |
| 115 | + } |
| 116 | + } |
| 117 | + |
108 | 118 | public function getAllowedParams() { |
109 | 119 | return array ( |
110 | 120 | 'messages' => array ( |
— | — | @@ -138,4 +148,4 @@ |
139 | 149 | public function getVersion() { |
140 | 150 | return __CLASS__ . ': $Id$'; |
141 | 151 | } |
142 | | -} |
\ No newline at end of file |
| 152 | +} |
Index: branches/REL1_15/phase3/includes/api/ApiQueryImages.php |
— | — | @@ -121,6 +121,10 @@ |
122 | 122 | $db->freeResult($res); |
123 | 123 | } |
124 | 124 | |
| 125 | + public function getCacheMode( $params ) { |
| 126 | + return 'public'; |
| 127 | + } |
| 128 | + |
125 | 129 | public function getAllowedParams() { |
126 | 130 | return array( |
127 | 131 | 'limit' => array( |
— | — | @@ -157,4 +161,4 @@ |
158 | 162 | public function getVersion() { |
159 | 163 | return __CLASS__ . ': $Id$'; |
160 | 164 | } |
161 | | -} |
\ No newline at end of file |
| 165 | +} |
Index: branches/REL1_15/phase3/includes/api/ApiQueryLogEvents.php |
— | — | @@ -251,6 +251,10 @@ |
252 | 252 | } |
253 | 253 | |
254 | 254 | |
| 255 | + public function getCacheMode( $params ) { |
| 256 | + return 'public'; |
| 257 | + } |
| 258 | + |
255 | 259 | public function getAllowedParams() { |
256 | 260 | global $wgLogTypes; |
257 | 261 | return array ( |
Index: branches/REL1_15/phase3/includes/api/ApiQueryAllLinks.php |
— | — | @@ -43,6 +43,10 @@ |
44 | 44 | $this->run(); |
45 | 45 | } |
46 | 46 | |
| 47 | + public function getCacheMode( $params ) { |
| 48 | + return 'public'; |
| 49 | + } |
| 50 | + |
47 | 51 | public function executeGenerator($resultPageSet) { |
48 | 52 | $this->run($resultPageSet); |
49 | 53 | } |
— | — | @@ -198,4 +202,4 @@ |
199 | 203 | public function getVersion() { |
200 | 204 | return __CLASS__ . ': $Id$'; |
201 | 205 | } |
202 | | -} |
\ No newline at end of file |
| 206 | +} |
Index: branches/REL1_15/phase3/includes/api/ApiQueryProtectedTitles.php |
— | — | @@ -118,6 +118,10 @@ |
119 | 119 | $resultPageSet->populateFromTitles($titles); |
120 | 120 | } |
121 | 121 | |
| 122 | + public function getCacheMode( $params ) { |
| 123 | + return 'public'; |
| 124 | + } |
| 125 | + |
122 | 126 | public function getAllowedParams() { |
123 | 127 | global $wgRestrictionLevels; |
124 | 128 | return array ( |
— | — | @@ -188,4 +192,4 @@ |
189 | 193 | public function getVersion() { |
190 | 194 | return __CLASS__ . ': $Id$'; |
191 | 195 | } |
192 | | -} |
\ No newline at end of file |
| 196 | +} |
Property changes on: branches/REL1_15/phase3/includes/api |
___________________________________________________________________ |
Modified: svn:mergeinfo |
193 | 197 | Merged /branches/REL1_16/phase3/includes/api:r69932 |
194 | 198 | Merged /trunk/phase3/includes/api:r69339,69347,69350,69369,69379,69776,69931 |