diff options
44 files changed, 327 insertions, 156 deletions
@@ -62,6 +62,7 @@ following names for their contribution to the product. * Tim Starling * Tom Gries * Victor Vasiliev +* Yuri Astrakhan == Patch Contributors == * Agbad diff --git a/RELEASE-NOTES b/RELEASE-NOTES index d3983380..4afdef47 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -1,15 +1,17 @@ = MediaWiki release notes = -== MediaWiki 1.16.0 == +== MediaWiki 1.16.1 == -2010-07-28 +2011-01-04 -This is a stable release of the MediaWiki 1.16 branch. +This is a security and maintenance release of the MediaWiki 1.16 branch. === Summary of selected changes in 1.16 === Selected changes since MediaWiki 1.15 that may be of interest: +* A new skin called Vector was added + * Watchlists now have RSS/Atom feeds. RSS feeds generally are now hidden, since Atom is a better protocol and is supported by virtually all clients. @@ -42,6 +44,23 @@ set $wgCacheDirectory to a writable path on the local filesystem. Make sure you have the DBA extension for PHP installed, this will improve performance further. +== Changes since 1.16.0 == + +* (bug 24981) Allow extensions to access SpecialUpload variables again +* (bug 24724) list=allusers was out by 1 (shows total users - 1) +* (bug 24166) Fixed API error when using rvprop=tags +* For wikis using French as a content language, Special:Téléchargement works + again as an alias for Special:Upload. +* (bug 25167) Correctly load JS fixes for IE6 (fixing a regression in 1.16.0) +* (bug 25248) Fixed paraminfo errors in certain API modules. +* The installer now has improved handling for situations where safe_mode is + active or exec() and similar functions are disabled. +* (bug 19593) Specifying --server in now works for all maintenance scripts. +* Fixed $wgLicenseTerms register globals. +* (bug 26561) Fixed clickjacking vulnerabilities by introducing support for + X-Frame-Options. The header value can be configured using $wgBreakFrames and + $wgEditPageFrameOptions. + == Changes since 1.16 beta 3 == * (bug 23769) Disabled HTML 5 client-side form validation. Was introduced in @@ -206,6 +225,7 @@ further. === New features in 1.16 === +* A new skin called Vector was added * Add CSS defintion of the 'wikitable' class to shared.css * (bug 17163) Added MediaWiki:Talkpageheader which will be displayed when viewing talk pages @@ -856,7 +876,6 @@ comment from another wiki. * (bug 17790) Users instantly logged off on HughesNet == API changes in 1.16 == - * Added uiprop=changeablegroups to meta=userinfo * Added usprop=gender to list=users * (bug 18311) action=purge now works for images too diff --git a/config/Installer.php b/config/Installer.php index 293a1a6c..f00a850d 100644 --- a/config/Installer.php +++ b/config/Installer.php @@ -25,6 +25,7 @@ if( !defined( 'MEDIAWIKI_INSTALL' ) ) { error_reporting( E_ALL | E_STRICT ); header( "Content-type: text/html; charset=utf-8" ); +header( 'X-Frame-Options: DENY' ); @ini_set( "display_errors", true ); # In case of errors, let output be clean. @@ -47,6 +48,8 @@ require_once( "$IP/includes/Exception.php" ); require_once( "$IP/includes/json/Services_JSON.php" ); require_once( "$IP/includes/json/FormatJson.php" ); +$wgMaxShellMemory = 0; + # If we get an exception, the user needs to know # all the details $wgShowExceptionDetails = true; @@ -2148,7 +2151,7 @@ function locate_executable($loc, $names, $versioninfo = false) { return $command; $file = str_replace('$1', $command, $versioninfo[0]); - if (strstr(`$file`, $versioninfo[1]) !== false) + if ( strstr( wfShellExec( $file ), $versioninfo[1]) !== false ) return $command; } } @@ -2239,12 +2242,12 @@ function getShellLocale( $wikiLang ) { # Get a list of available locales $lines = $ret = false; - exec( '/usr/bin/locale -a', $lines, $ret ); + $lines = wfShellExec( '/usr/bin/locale -a', $ret, true ); if ( $ret ) { return false; } - $lines = wfArrayMap( 'trim', $lines ); + $lines = wfArrayMap( 'trim', explode( "\n", $lines ) ); $candidatesByLocale = array(); $candidatesByLang = array(); foreach ( $lines as $line ) { diff --git a/includes/Article.php b/includes/Article.php index d3863c77..5edfc10d 100644 --- a/includes/Article.php +++ b/includes/Article.php @@ -792,6 +792,9 @@ class Article { return; } + # Allow frames by default + $wgOut->allowClickjacking(); + # Should the parser cache be used? $useParserCache = $this->useParserCache( $oldid ); wfDebug( 'Article::view using parser cache: ' . ( $useParserCache ? 'yes' : 'no' ) . "\n" ); @@ -1452,6 +1455,8 @@ class Article { ); if ( !$dbr->numRows( $tbs ) ) return; + $wgOut->preventClickjacking(); + $tbtext = ""; while ( $o = $dbr->fetchObject( $tbs ) ) { $rmvtxt = ""; diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index a369fccd..c118075e 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -33,7 +33,7 @@ if ( !defined( 'MW_PHP4' ) ) { } /** MediaWiki version number */ -$wgVersion = '1.16.0'; +$wgVersion = '1.16.1'; /** Name of the site. It must be changed in LocalSettings.php */ $wgSitename = 'MediaWiki'; @@ -2502,6 +2502,11 @@ $wgRightsUrl = null; $wgRightsText = null; $wgRightsIcon = null; +/** + * Set to an array of metadata terms. Else they will be loaded based on $wgRightsUrl + */ +$wgLicenseTerms = false; + /** Set this to some HTML to override the rights icon with an arbitrary logo */ $wgCopyrightIcon = null; @@ -3981,12 +3986,33 @@ $wgParserTestFiles = array( $wgParserTestRemote = false; /** - * Break out of framesets. This can be used to prevent external sites from - * framing your site with ads. + * Break out of framesets. This can be used to prevent clickjacking attacks, + * or to prevent external sites from framing your site with ads. */ $wgBreakFrames = false; /** + * The X-Frame-Options header to send on pages sensitive to clickjacking + * attacks, such as edit pages. This prevents those pages from being displayed + * in a frame or iframe. The options are: + * + * - 'DENY': Do not allow framing. This is recommended for most wikis. + * + * - 'SAMEORIGIN': Allow framing by pages on the same domain. This can be used + * to allow framing within a trusted domain. This is insecure if there + * is a page on the same domain which allows framing of arbitrary URLs. + * + * - false: Allow all framing. This opens up the wiki to XSS attacks and thus + * full compromise of local user accounts. Private wikis behind a + * corporate firewall are especially vulnerable. This is not + * recommended. + * + * For extra safety, set $wgBreakFrames = true, to prevent framing on all pages, + * not just edit pages. + */ +$wgEditPageFrameOptions = 'DENY'; + +/** * Set this to an array of special page names to prevent * maintenance/updateSpecialPages.php from updating those pages. */ diff --git a/includes/HTMLForm.php b/includes/HTMLForm.php index fddc887b..12687dc4 100644 --- a/includes/HTMLForm.php +++ b/includes/HTMLForm.php @@ -311,6 +311,9 @@ class HTMLForm { $this->displayErrors( $submitResult ); } + # For good measure (it is the default) + $wgOut->preventClickjacking(); + $html = '' . $this->mHeader . $this->getBody() diff --git a/includes/HistoryPage.php b/includes/HistoryPage.php index e515d3dd..8f5c2dda 100644 --- a/includes/HistoryPage.php +++ b/includes/HistoryPage.php @@ -166,6 +166,7 @@ class HistoryPage { $pager->getBody() . $pager->getNavigationBar() ); + $wgOut->preventClickjacking( $pager->getPreventClickjacking() ); wfProfileOut( __METHOD__ ); } @@ -301,6 +302,7 @@ class HistoryPage { class HistoryPager extends ReverseChronologicalPager { public $lastRow = false, $counter, $historyPage, $title, $buttons, $conds; protected $oldIdChecked; + protected $preventClickjacking = false; function __construct( $historyPage, $year='', $month='', $tagFilter = '', $conds = array() ) { parent::__construct(); @@ -382,6 +384,7 @@ class HistoryPager extends ReverseChronologicalPager { $this->buttons = '<div>'; if( $wgUser->isAllowed('deleterevision') ) { + $this->preventClickjacking(); $float = $wgContLang->alignEnd(); # Note bug #20966, <button> is non-standard in IE<8 $this->buttons .= Xml::element( 'button', @@ -488,6 +491,7 @@ class HistoryPager extends ReverseChronologicalPager { $del = ''; // User can delete revisions... if( $wgUser->isAllowed( 'deleterevision' ) ) { + $this->preventClickjacking(); // If revision was hidden from sysops, disable the checkbox if( !$rev->userCan( Revision::DELETED_RESTRICTED ) ) { $del = Xml::check( 'deleterevisions', false, array( 'disabled' => 'disabled' ) ); @@ -534,6 +538,7 @@ class HistoryPager extends ReverseChronologicalPager { # Rollback and undo links if( !is_null( $next ) && is_object( $next ) ) { if( $latest && $this->title->userCan( 'rollback' ) && $this->title->userCan( 'edit' ) ) { + $this->preventClickjacking(); $tools[] = '<span class="mw-rollback-link">'. $this->getSkin()->buildRollbackLink( $rev ).'</span>'; } @@ -721,6 +726,20 @@ class HistoryPager extends ReverseChronologicalPager { return ''; } } + + /** + * This is called if a write operation is possible from the generated HTML + */ + function preventClickjacking( $enable = true ) { + $this->preventClickjacking = $enable; + } + + /** + * Get the "prevent clickjacking" flag + */ + function getPreventClickjacking() { + return $this->preventClickjacking; + } } /** diff --git a/includes/ImagePage.php b/includes/ImagePage.php index dd2c2ab1..f16acc33 100644 --- a/includes/ImagePage.php +++ b/includes/ImagePage.php @@ -600,6 +600,7 @@ EOT $this->loadFile(); $pager = new ImageHistoryPseudoPager( $this ); $wgOut->addHTML( $pager->getBody() ); + $wgOut->preventClickjacking( $pager->getPreventClickjacking() ); $this->img->resetHistory(); // free db resources @@ -803,6 +804,7 @@ EOT class ImageHistoryList { protected $imagePage, $img, $skin, $title, $repo, $showThumb; + protected $preventClickjacking = false; public function __construct( $imagePage ) { global $wgUser, $wgShowArchiveThumbnails; @@ -929,6 +931,7 @@ class ImageHistoryList { # Don't link to unviewable files $row .= '<span class="history-deleted">' . $wgLang->timeAndDate( $timestamp, true ) . '</span>'; } elseif( $file->isDeleted(File::DELETED_FILE) ) { + $this->preventClickjacking(); $revdel = SpecialPage::getTitleFor( 'Revisiondelete' ); # Make a link to review the image $url = $this->skin->link( @@ -1015,9 +1018,19 @@ class ImageHistoryList { return wfMsgHtml( 'filehist-nothumb' ); } } + + protected function preventClickjacking( $enable = true ) { + $this->preventClickjacking = $enable; + } + + public function getPreventClickjacking() { + return $this->preventClickjacking; + } } class ImageHistoryPseudoPager extends ReverseChronologicalPager { + protected $preventClickjacking = false; + function __construct( $imagePage ) { parent::__construct(); $this->mImagePage = $imagePage; @@ -1058,6 +1071,10 @@ class ImageHistoryPseudoPager extends ReverseChronologicalPager { $s .= $list->imageHistoryLine( !$file->isOld(), $file ); } $s .= $list->endImageHistoryList($navLink); + + if ( $list->getPreventClickjacking() ) { + $this->preventClickjacking(); + } } return $s; } @@ -1140,4 +1157,13 @@ class ImageHistoryPseudoPager extends ReverseChronologicalPager { } $this->mQueryDone = true; } + + protected function preventClickjacking( $enable = true ) { + $this->preventClickjacking = $enable; + } + + public function getPreventClickjacking() { + return $this->preventClickjacking; + } + } diff --git a/includes/OutputPage.php b/includes/OutputPage.php index 4333383c..97e26110 100644 --- a/includes/OutputPage.php +++ b/includes/OutputPage.php @@ -37,6 +37,7 @@ class OutputPage { var $mPageTitleActionText = ''; var $mParseWarnings = array(); var $mSquidMaxage = 0; + var $mPreventClickjacking = true; var $mRevisionId = null; protected $mTitle = null; @@ -1348,6 +1349,41 @@ class OutputPage { } /** + * Set a flag which will cause an X-Frame-Options header appropriate for + * edit pages to be sent. The header value is controlled by + * $wgEditPageFrameOptions. + * + * This is the default for special pages. If you display a CSRF-protected + * form on an ordinary view page, then you need to call this function. + */ + public function preventClickjacking( $enable = true ) { + $this->mPreventClickjacking = $enable; + } + + /** + * Turn off frame-breaking. Alias for $this->preventClickjacking(false). + * This can be called from pages which do not contain any CSRF-protected + * HTML form. + */ + public function allowClickjacking() { + $this->mPreventClickjacking = false; + } + + /** + * Get the X-Frame-Options header value (without the name part), or false + * if there isn't one. This is used by Skin to determine whether to enable + * JavaScript frame-breaking, for clients that don't support X-Frame-Options. + */ + public function getFrameOptions() { + global $wgBreakFrames, $wgEditPageFrameOptions; + if ( $wgBreakFrames ) { + return 'DENY'; + } elseif ( $this->mPreventClickjacking && $wgEditPageFrameOptions ) { + return $wgEditPageFrameOptions; + } + } + + /** * Send cache control HTTP headers */ public function sendCacheControl() { @@ -1561,6 +1597,13 @@ class OutputPage { $wgRequest->response()->header( "Content-type: $wgMimeType; charset={$wgOutputEncoding}" ); $wgRequest->response()->header( 'Content-language: '.$wgContLanguageCode ); + // Prevent framing, if requested + $frameOptions = $this->getFrameOptions(); + if ( $frameOptions ) { + $wgRequest->response()->header( "X-Frame-Options: $frameOptions" ); + } + + if ($this->mArticleBodyOnly) { $this->out($this->mBodytext); } else { diff --git a/includes/Skin.php b/includes/Skin.php index d1a0016d..18867cbe 100644 --- a/includes/Skin.php +++ b/includes/Skin.php @@ -413,7 +413,7 @@ class Skin extends Linker { 'wgUserGroups' => $wgUser->isAnon() ? null : $wgUser->getEffectiveGroups(), 'wgUserLanguage' => $wgLang->getCode(), 'wgContentLanguage' => $wgContLang->getCode(), - 'wgBreakFrames' => $wgBreakFrames, + 'wgBreakFrames' => $wgOut->getFrameOptions() == 'DENY', 'wgCurRevisionId' => isset( $wgArticle ) ? $wgArticle->getLatest() : 0, 'wgVersion' => $wgVersion, 'wgEnableAPI' => $wgEnableAPI, diff --git a/includes/Title.php b/includes/Title.php index 8d7275ff..be41a85a 100644 --- a/includes/Title.php +++ b/includes/Title.php @@ -2606,9 +2606,8 @@ class Title { // purge variant urls as well if($wgContLang->hasVariants()){ $variants = $wgContLang->getVariants(); - foreach($variants as $vCode){ - if($vCode==$wgContLang->getCode()) continue; // we don't want default variant - $urls[] = $this->getInternalURL('',$vCode); + foreach ( $variants as $vCode ) { + $urls[] = $this->getInternalURL( '', $vCode ); } } diff --git a/includes/api/ApiBase.php b/includes/api/ApiBase.php index b703ab4f..708a3a40 100644 --- a/includes/api/ApiBase.php +++ b/includes/api/ApiBase.php @@ -1006,6 +1006,14 @@ abstract class ApiBase { } /** + * Returns whether this module requires a Token to execute + * @returns bool + */ + public function needsToken() { + return false; + } + + /** * Returns the token salt if there is one, '' if the module doesn't require a salt, else false if the module doesn't need a token * @returns bool */ @@ -1033,7 +1041,7 @@ abstract class ApiBase { $ret[] = array( 'writedisabled' ); } - if ( $this->getTokenSalt() !== false ) { + if ( $this->needsToken() ) { $ret[] = array( 'missingparam', 'token' ); $ret[] = array( 'sessionfailure' ); } @@ -1184,6 +1192,6 @@ abstract class ApiBase { * @return string */ public static function getBaseVersion() { - return __CLASS__ . ': $Id: ApiBase.php 70066 2010-07-28 05:52:32Z tstarling $'; + return __CLASS__ . ': $Id: ApiBase.php 79562 2011-01-04 06:15:54Z tstarling $'; } } diff --git a/includes/api/ApiBlock.php b/includes/api/ApiBlock.php index 91bbaf6d..23de07d6 100644 --- a/includes/api/ApiBlock.php +++ b/includes/api/ApiBlock.php @@ -175,6 +175,10 @@ class ApiBlock extends ApiBase { ) ); } + public function needsToken() { + return true; + } + public function getTokenSalt() { return ''; } @@ -187,6 +191,6 @@ class ApiBlock extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiBlock.php 62766 2010-02-21 12:32:46Z ashley $'; + return __CLASS__ . ': $Id: ApiBlock.php 74217 2010-10-03 15:53:07Z reedy $'; } } diff --git a/includes/api/ApiDelete.php b/includes/api/ApiDelete.php index 2b349bd7..c4550a96 100644 --- a/includes/api/ApiDelete.php +++ b/includes/api/ApiDelete.php @@ -230,6 +230,10 @@ class ApiDelete extends ApiBase { ) ); } + public function needsToken() { + return true; + } + public function getTokenSalt() { return ''; } @@ -242,6 +246,6 @@ class ApiDelete extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiDelete.php 62703 2010-02-19 12:54:09Z ashley $'; + return __CLASS__ . ': $Id: ApiDelete.php 74217 2010-10-03 15:53:07Z reedy $'; } }
\ No newline at end of file diff --git a/includes/api/ApiEditPage.php b/includes/api/ApiEditPage.php index 50a9836a..e78f66bc 100644 --- a/includes/api/ApiEditPage.php +++ b/includes/api/ApiEditPage.php @@ -454,6 +454,10 @@ class ApiEditPage extends ApiBase { ); } + public function needsToken() { + return true; + } + public function getTokenSalt() { return ''; } @@ -470,6 +474,6 @@ class ApiEditPage extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiEditPage.php 62600 2010-02-16 22:01:38Z reedy $'; + return __CLASS__ . ': $Id: ApiEditPage.php 74217 2010-10-03 15:53:07Z reedy $'; } } diff --git a/includes/api/ApiEmailUser.php b/includes/api/ApiEmailUser.php index 912480ef..66f2dff5 100644 --- a/includes/api/ApiEmailUser.php +++ b/includes/api/ApiEmailUser.php @@ -112,6 +112,10 @@ class ApiEmailUser extends ApiBase { ) ); } + public function needsToken() { + return true; + } + public function getTokenSalt() { return ''; } @@ -123,7 +127,7 @@ class ApiEmailUser extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiEmailUser.php 62599 2010-02-16 21:59:16Z reedy $'; + return __CLASS__ . ': $Id: ApiEmailUser.php 74217 2010-10-03 15:53:07Z reedy $'; } }
\ No newline at end of file diff --git a/includes/api/ApiImport.php b/includes/api/ApiImport.php index 032b684c..d33a472a 100644 --- a/includes/api/ApiImport.php +++ b/includes/api/ApiImport.php @@ -149,6 +149,10 @@ class ApiImport extends ApiBase { ) ); } + public function needsToken() { + return true; + } + public function getTokenSalt() { return ''; } @@ -161,7 +165,7 @@ class ApiImport extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiImport.php 62599 2010-02-16 21:59:16Z reedy $'; + return __CLASS__ . ': $Id: ApiImport.php 74217 2010-10-03 15:53:07Z reedy $'; } } diff --git a/includes/api/ApiMove.php b/includes/api/ApiMove.php index 71010de7..c234f084 100644 --- a/includes/api/ApiMove.php +++ b/includes/api/ApiMove.php @@ -219,6 +219,10 @@ class ApiMove extends ApiBase { ) ); } + public function needsToken() { + return true; + } + public function getTokenSalt() { return ''; } @@ -230,6 +234,6 @@ class ApiMove extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiMove.php 62810 2010-02-22 03:34:56Z mah $'; + return __CLASS__ . ': $Id: ApiMove.php 74217 2010-10-03 15:53:07Z reedy $'; } } diff --git a/includes/api/ApiPatrol.php b/includes/api/ApiPatrol.php index 3b2b2046..79916117 100644 --- a/includes/api/ApiPatrol.php +++ b/includes/api/ApiPatrol.php @@ -92,6 +92,10 @@ class ApiPatrol extends ApiBase { ) ); } + public function needsToken() { + return true; + } + public function getTokenSalt() { return ''; } @@ -103,6 +107,6 @@ class ApiPatrol extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiPatrol.php 69578 2010-07-20 02:46:20Z tstarling $'; + return __CLASS__ . ': $Id: ApiPatrol.php 74217 2010-10-03 15:53:07Z reedy $'; } }
\ No newline at end of file diff --git a/includes/api/ApiProtect.php b/includes/api/ApiProtect.php index ca47c1b8..0b1ae4c8 100644 --- a/includes/api/ApiProtect.php +++ b/includes/api/ApiProtect.php @@ -184,8 +184,12 @@ class ApiProtect extends ApiBase { ) ); } + public function needsToken() { + return true; + } + public function getTokenSalt() { - return null; + return ''; } protected function getExamples() { @@ -196,6 +200,6 @@ class ApiProtect extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiProtect.php 62557 2010-02-15 23:53:43Z reedy $'; + return __CLASS__ . ': $Id: ApiProtect.php 74217 2010-10-03 15:53:07Z reedy $'; } } diff --git a/includes/api/ApiQueryAllUsers.php b/includes/api/ApiQueryAllUsers.php index f8d475cc..611fc98c 100644 --- a/includes/api/ApiQueryAllUsers.php +++ b/includes/api/ApiQueryAllUsers.php @@ -184,7 +184,14 @@ class ApiQueryAllUsers extends ApiQueryBase { } } - $db->freeResult( $res ); + if ( is_array( $lastUserData ) ) { + $fit = $result->addValue( array( 'query', $this->getModuleName() ), + null, $lastUserData ); + if ( !$fit ) { + $this->setContinueEnumParameter( 'from', + $this->keyToTitle( $lastUserData['name'] ) ); + } + } $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'u' ); } @@ -244,6 +251,6 @@ class ApiQueryAllUsers extends ApiQueryBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryAllUsers.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryAllUsers.php 79562 2011-01-04 06:15:54Z tstarling $'; } } diff --git a/includes/api/ApiQueryRevisions.php b/includes/api/ApiQueryRevisions.php index 6166b6a2..3992d6a9 100644 --- a/includes/api/ApiQueryRevisions.php +++ b/includes/api/ApiQueryRevisions.php @@ -125,7 +125,7 @@ class ApiQueryRevisions extends ApiQueryBase { } $db = $this->getDB(); - $this->addTables( array( 'page', 'revision' ) ); + $this->addTables( 'page' ); $this->addFields( Revision::selectFields() ); $this->addWhere( 'page_id = rev_page' ); @@ -189,6 +189,9 @@ class ApiQueryRevisions extends ApiQueryBase { $this->section = false; } + //Bug 24166 - API error when using rvprop=tags + $this->addTables( 'revision' ); + $userMax = ( $this->fld_content ? ApiBase::LIMIT_SML1 : ApiBase::LIMIT_BIG1 ); $botMax = ( $this->fld_content ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_BIG2 ); $limit = $params['limit']; @@ -603,6 +606,6 @@ class ApiQueryRevisions extends ApiQueryBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiQueryRevisions.php 69932 2010-07-26 08:03:21Z tstarling $'; + return __CLASS__ . ': $Id: ApiQueryRevisions.php 72117 2010-09-01 16:50:07Z reedy $'; } } diff --git a/includes/api/ApiUnblock.php b/includes/api/ApiUnblock.php index 2ffae504..1c4a4ade 100644 --- a/includes/api/ApiUnblock.php +++ b/includes/api/ApiUnblock.php @@ -116,6 +116,10 @@ class ApiUnblock extends ApiBase { ) ); } + public function needsToken() { + return true; + } + public function getTokenSalt() { return ''; } @@ -128,6 +132,6 @@ class ApiUnblock extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiUnblock.php 62599 2010-02-16 21:59:16Z reedy $'; + return __CLASS__ . ': $Id: ApiUnblock.php 74217 2010-10-03 15:53:07Z reedy $'; } } diff --git a/includes/api/ApiUndelete.php b/includes/api/ApiUndelete.php index 9efba5f3..ae705b69 100644 --- a/includes/api/ApiUndelete.php +++ b/includes/api/ApiUndelete.php @@ -125,6 +125,10 @@ class ApiUndelete extends ApiBase { ) ); } + public function needsToken() { + return true; + } + public function getTokenSalt() { return ''; } @@ -137,6 +141,6 @@ class ApiUndelete extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiUndelete.php 62599 2010-02-16 21:59:16Z reedy $'; + return __CLASS__ . ': $Id: ApiUndelete.php 74217 2010-10-03 15:53:07Z reedy $'; } } diff --git a/includes/api/ApiUpload.php b/includes/api/ApiUpload.php index 6b91b223..06688997 100644 --- a/includes/api/ApiUpload.php +++ b/includes/api/ApiUpload.php @@ -306,6 +306,10 @@ class ApiUpload extends ApiBase { ) ); } + public function needsToken() { + return true; + } + public function getTokenSalt() { return ''; } diff --git a/includes/api/ApiUserrights.php b/includes/api/ApiUserrights.php index 6296a8f8..be0750d6 100644 --- a/includes/api/ApiUserrights.php +++ b/includes/api/ApiUserrights.php @@ -101,6 +101,10 @@ class ApiUserrights extends ApiBase { array( 'missingparam', 'user' ), ) ); } + + public function needsToken() { + return true; + } public function getTokenSalt() { $params = $this->extractRequestParams(); @@ -123,6 +127,6 @@ class ApiUserrights extends ApiBase { } public function getVersion() { - return __CLASS__ . ': $Id: ApiUserrights.php 62686 2010-02-19 01:25:57Z reedy $'; + return __CLASS__ . ': $Id: ApiUserrights.php 74217 2010-10-03 15:53:07Z reedy $'; } } diff --git a/includes/diff/DifferenceInterface.php b/includes/diff/DifferenceInterface.php index d7d36799..0e9ca9f6 100644 --- a/includes/diff/DifferenceInterface.php +++ b/includes/diff/DifferenceInterface.php @@ -112,6 +112,8 @@ class DifferenceEngine { global $wgUser, $wgOut, $wgUseExternalEditor, $wgUseRCPatrol; wfProfileIn( __METHOD__ ); + # Allow frames except in certain special cases + $wgOut->allowClickjacking(); # If external diffs are enabled both globally and for the user, # we'll use the application/x-external-editor interface to call @@ -199,6 +201,7 @@ CONTROL; // Check if page is editable $editable = $this->mNewRev->getTitle()->userCan( 'edit' ); if ( $editable && $this->mNewRev->isCurrent() && $wgUser->isAllowed( 'rollback' ) ) { + $wgOut->preventClickjacking(); $rollback = ' ' . $sk->generateRollback( $this->mNewRev ); } else { $rollback = ''; diff --git a/includes/json/Services_JSON.php b/includes/json/Services_JSON.php index 94233520..588ece9c 100644 --- a/includes/json/Services_JSON.php +++ b/includes/json/Services_JSON.php @@ -50,7 +50,7 @@ * @author Matt Knapp <mdknapp[at]gmail[dot]com> * @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com> * @copyright 2005 Michal Migurski -* @version CVS: $Id: Services_JSON.php 65683 2010-04-30 05:56:15Z tstarling $ +* @version CVS: $Id: Services_JSON.php 79562 2011-01-04 06:15:54Z tstarling $ * @license http://www.opensource.org/licenses/bsd-license.php * @see http://pear.php.net/pepr/pepr-proposal-show.php?id=198 */ diff --git a/includes/specials/SpecialAllpages.php b/includes/specials/SpecialAllpages.php index a36cdca7..19816dcd 100644 --- a/includes/specials/SpecialAllpages.php +++ b/includes/specials/SpecialAllpages.php @@ -40,6 +40,7 @@ class SpecialAllpages extends IncludableSpecialPage { $this->setHeaders(); $this->outputHeader(); + $wgOut->allowClickjacking(); # GET values $from = $wgRequest->getVal( 'from', null ); diff --git a/includes/specials/SpecialCategories.php b/includes/specials/SpecialCategories.php index a649eafd..eb49fdbc 100644 --- a/includes/specials/SpecialCategories.php +++ b/includes/specials/SpecialCategories.php @@ -12,6 +12,7 @@ function wfSpecialCategories( $par=null ) { } else { $from = $par; } + $wgOut->allowClickjacking(); $cap = new CategoryPager( $from ); $cap->doQuery(); $wgOut->addHTML( diff --git a/includes/specials/SpecialContributions.php b/includes/specials/SpecialContributions.php index 392f4332..b5d6107a 100644 --- a/includes/specials/SpecialContributions.php +++ b/includes/specials/SpecialContributions.php @@ -107,6 +107,7 @@ class SpecialContributions extends SpecialPage { '<p>' . $pager->getNavigationBar() . '</p>' ); } + $wgOut->preventClickjacking( $pager->getPreventClickjacking() ); # Show the appropriate "footer" message - WHOIS tools, etc. @@ -428,6 +429,7 @@ class ContribsPager extends ReverseChronologicalPager { public $mDefaultDirection = true; var $messages, $target; var $namespace = '', $mDb; + var $preventClickjacking = false; function __construct( $target, $namespace = false, $year = false, $month = false, $tagFilter = false ) { parent::__construct(); @@ -565,6 +567,7 @@ class ContribsPager extends ReverseChronologicalPager { if( !$row->page_is_new && $page->quickUserCan( 'rollback' ) && $page->quickUserCan( 'edit' ) ) { + $this->preventClickjacking(); $topmarktext .= ' '.$sk->generateRollback( $rev ); } } @@ -671,4 +674,11 @@ class ContribsPager extends ReverseChronologicalPager { return $this->mDb; } + protected function preventClickjacking() { + $this->preventClickjacking = true; + } + + public function getPreventClickjacking() { + return $this->preventClickjacking; + } } diff --git a/includes/specials/SpecialLinkSearch.php b/includes/specials/SpecialLinkSearch.php index 5913f4b4..70b2257a 100644 --- a/includes/specials/SpecialLinkSearch.php +++ b/includes/specials/SpecialLinkSearch.php @@ -44,8 +44,10 @@ function wfSpecialLinkSearch( $par ) { $protocol = ''; } - $self = Title::makeTitle( NS_SPECIAL, 'Linksearch' ); + $wgOut->allowClickjacking(); + $self = Title::makeTitle( NS_SPECIAL, 'Linksearch' ); + $wgOut->addWikiMsg( 'linksearch-text', '<nowiki>' . $wgLang->commaList( $wgUrlProtocols ) . '</nowiki>' ); $s = Xml::openElement( 'form', array( 'id' => 'mw-linksearch-form', 'method' => 'get', 'action' => $GLOBALS['wgScript'] ) ) . Xml::hidden( 'title', $self->getPrefixedDbKey() ) . diff --git a/includes/specials/SpecialSearch.php b/includes/specials/SpecialSearch.php index da054e02..40b28236 100644 --- a/includes/specials/SpecialSearch.php +++ b/includes/specials/SpecialSearch.php @@ -364,6 +364,7 @@ class SpecialSearch { $wgOut->setRobotPolicy( 'noindex,nofollow' ); // add javascript specific to special:search $wgOut->addScriptFile( 'search.js' ); + $wgOut->allowClickjacking(); } /** diff --git a/includes/specials/SpecialSpecialpages.php b/includes/specials/SpecialSpecialpages.php index 84ab689a..8e97f9b7 100644 --- a/includes/specials/SpecialSpecialpages.php +++ b/includes/specials/SpecialSpecialpages.php @@ -13,6 +13,7 @@ function wfSpecialSpecialpages() { $wgMessageCache->loadAllMessages(); $wgOut->setRobotPolicy( 'noindex,nofollow' ); # Is this really needed? + $wgOut->allowClickjacking(); $sk = $wgUser->getSkin(); $pages = SpecialPage::getUsablePages(); diff --git a/includes/specials/SpecialUpload.php b/includes/specials/SpecialUpload.php index 9569945d..68ee8efc 100644 --- a/includes/specials/SpecialUpload.php +++ b/includes/specials/SpecialUpload.php @@ -23,30 +23,30 @@ class SpecialUpload extends SpecialPage { } /** Misc variables **/ - protected $mRequest; // The WebRequest or FauxRequest this form is supposed to handle - protected $mSourceType; - protected $mUpload; - protected $mLocalFile; - protected $mUploadClicked; + public $mRequest; // The WebRequest or FauxRequest this form is supposed to handle + public $mSourceType; + public $mUpload; + public $mLocalFile; + public $mUploadClicked; /** User input variables from the "description" section **/ - public $mDesiredDestName; // The requested target file name - protected $mComment; - protected $mLicense; - + public $mDesiredDestName; // The requested target file name + public $mComment; + public $mLicense; + /** User input variables from the root section **/ - protected $mIgnoreWarning; - protected $mWatchThis; - protected $mCopyrightStatus; - protected $mCopyrightSource; + public $mIgnoreWarning; + public $mWatchThis; + public $mCopyrightStatus; + public $mCopyrightSource; /** Hidden variables **/ - protected $mDestWarningAck; - protected $mForReUpload; // The user followed an "overwrite this file" link - protected $mCancelUpload; // The user clicked "Cancel and return to upload form" button - protected $mTokenOk; - protected $mUploadSuccessful = false; // Subclasses can use this to determine whether a file was uploaded - + public $mDestWarningAck; + public $mForReUpload; // The user followed an "overwrite this file" link + public $mCancelUpload; // The user clicked "Cancel and return to upload form" button + public $mTokenOk; + public $mUploadSuccessful = false; // Subclasses can use this to determine whether a file was uploaded + /** Text injection points for hooks not using HTMLForm **/ public $uploadFormTextTop; public $uploadFormTextAfterSummary; diff --git a/includes/specials/SpecialVersion.php b/includes/specials/SpecialVersion.php index 7da6023e..ebc50bab 100644 --- a/includes/specials/SpecialVersion.php +++ b/includes/specials/SpecialVersion.php @@ -32,6 +32,7 @@ class SpecialVersion extends SpecialPage { $this->setHeaders(); $this->outputHeader(); + $wgOut->allowClickjacking(); $wgOut->addHTML( Xml::openElement( 'div', array( 'dir' => $wgContLang->getDir() ) ) ); diff --git a/languages/LanguageConverter.php b/languages/LanguageConverter.php index 75075fee..ebf1ab43 100644 --- a/languages/LanguageConverter.php +++ b/languages/LanguageConverter.php @@ -20,7 +20,6 @@ class LanguageConverter { var $mVariants, $mVariantFallbacks, $mVariantNames; var $mTablesLoaded = false; var $mTables; - var $mNamespaceTables; // 'bidirectional' 'unidirectional' 'disable' for each variant var $mManualLevel; var $mCacheKey; @@ -87,7 +86,6 @@ class LanguageConverter { } else { $this->mManualLevel[$v] = 'bidirectional'; } - $this->mNamespaceTables[$v] = array(); $this->mFlags[$v] = $v; } } @@ -492,9 +490,14 @@ class LanguageConverter { * @private */ function applyManualConv( $convRule ) { - // use syntax -{T|zh:TitleZh;zh-tw:TitleTw}- for custom - // conversion in title - $this->mConvRuleTitle = $convRule->getTitle(); + // Use syntax -{T|zh-cn:TitleCN; zh-tw:TitleTw}- to custom + // title conversion. + // Bug 24072: mConvRuleTitle won't work if the title conversion + // rule was followed by other manual conversion rule(s). + $newConvRuleTitle = $convRule->getTitle(); + if( $newConvRuleTitle ) { + $this->mConvRuleTitle = $newConvRuleTitle; + } // apply manual conversion table to global table $convTable = $convRule->getConvTable(); @@ -547,12 +550,19 @@ class LanguageConverter { */ public function convertTitle( $title ) { $variant = $this->getPreferredVariant(); - if ( $title->getNamespace() === NS_MAIN ) { + $index = $title->getNamespace(); + if ( $index === NS_MAIN ) { $text = ''; } else { - $text = $title->getNsText(); - if ( isset( $this->mNamespaceTables[$variant][$text] ) ) { - $text = $this->mNamespaceTables[$variant][$text]; + // first let's check if a message has given us a converted name + $nsConvKey = 'conversion-ns' . $index; + if ( !wfEmptyMsg( $nsConvKey ) ) { + $text = wfMsgForContentNoTrans( $nsConvKey ); + } else { + // the message does not exist, try retrieve it from the current + // variant's namespace names. + $langObj = $this->mLangObj->factory( $variant ); + $text = $langObj->getFormattedNsText( $index ); } $text .= ':'; } diff --git a/languages/classes/LanguageGan.php b/languages/classes/LanguageGan.php index 3c8b5fdb..54202e48 100644 --- a/languages/classes/LanguageGan.php +++ b/languages/classes/LanguageGan.php @@ -26,47 +26,6 @@ class GanConverter extends LanguageConverter { 'gan-hant' => '繁體', ); $this->mVariantNames = array_merge($this->mVariantNames,$names); - $this->loadNamespaceTables(); - } - - function loadNamespaceTables() { - global $wgMetaNamespace; - $nsproject = $wgMetaNamespace; - $projecttable = array( - 'Wikipedia' => '维基百科', - 'Wikisource' => '维基文库', - 'Wikinews' => '维基新闻', - 'Wiktionary' => '维基词典', - 'Wikibooks' => '维基教科书', - 'Wikiquote' => '维基语录', - ); - $this->mNamespaceTables['gan-hans'] = array( - 'Media' => '媒体', - 'Special' => '特殊', - 'Talk' => '談詑', - 'User' => '用户', - 'User talk' => '用户談詑', - $nsproject - => isset($projecttable[$nsproject]) ? - $projecttable[$nsproject] : $nsproject, - $nsproject . ' talk' - => isset($projecttable[$nsproject]) ? - $projecttable[$nsproject] . '談詑' : $nsproject . '談詑', - 'File' => '文件', - 'File talk' => '文件談詑', - 'MediaWiki' => 'MediaWiki', - 'MediaWiki talk' => 'MediaWiki談詑', - 'Template' => '模板', - 'Template talk' => '模板談詑', - 'Help' => '帮助', - 'Help talk' => '帮助談詑', - 'Category' => '分类', - 'Category talk' => '分类談詑', - ); - $this->mNamespaceTables['gan-hant'] = array_merge($this->mNamespaceTables['gan-hans']); - $this->mNamespaceTables['gan-hant']['File'] = '檔案'; - $this->mNamespaceTables['gan-hant']['File talk'] = '檔案談詑'; - $this->mNamespaceTables['gan'] = array_merge($this->mNamespaceTables['gan-hans']); } function loadDefaultTables() { diff --git a/languages/classes/LanguageZh.php b/languages/classes/LanguageZh.php index 4a73c665..0b88dbb2 100644 --- a/languages/classes/LanguageZh.php +++ b/languages/classes/LanguageZh.php @@ -32,53 +32,6 @@ class ZhConverter extends LanguageConverter { 'zh-my' => '大马', ); $this->mVariantNames = array_merge($this->mVariantNames,$names); - $this->loadNamespaceTables(); - } - - function loadNamespaceTables() { - global $wgMetaNamespace; - $nsproject = $wgMetaNamespace; - $projecttable = array( - 'Wikipedia' => '维基百科', - 'Wikisource' => '维基文库', - 'Wikinews' => '维基新闻', - 'Wiktionary' => '维基词典', - 'Wikibooks' => '维基教科书', - 'Wikiquote' => '维基语录', - ); - $this->mNamespaceTables['zh-hans'] = array( - 'Media' => '媒体', - 'Special' => '特殊', - 'Talk' => '讨论', - 'User' => '用户', - 'User talk' => '用户讨论', - $nsproject - => isset($projecttable[$nsproject]) ? - $projecttable[$nsproject] : $nsproject, - $nsproject . ' talk' - => isset($projecttable[$nsproject]) ? - $projecttable[$nsproject] . '讨论' : $nsproject . '讨论', - 'File' => '文件', - 'File talk' => '文件讨论', - 'MediaWiki' => 'MediaWiki', - 'MediaWiki talk' => 'MediaWiki讨论', - 'Template' => '模板', - 'Template talk' => '模板讨论', - 'Help' => '帮助', - 'Help talk' => '帮助讨论', - 'Category' => '分类', - 'Category talk' => '分类讨论', - ); - $this->mNamespaceTables['zh-hant'] = array_merge($this->mNamespaceTables['zh-hans']); - $this->mNamespaceTables['zh-hant']['File'] = '檔案'; - $this->mNamespaceTables['zh-hant']['File talk'] = '檔案討論'; - $this->mNamespaceTables['zh'] = array_merge($this->mNamespaceTables['zh-hans']); - $this->mNamespaceTables['zh-cn'] = array_merge($this->mNamespaceTables['zh-hans']); - $this->mNamespaceTables['zh-hk'] = array_merge($this->mNamespaceTables['zh-hant']); - $this->mNamespaceTables['zh-mo'] = array_merge($this->mNamespaceTables['zh-hant']); - $this->mNamespaceTables['zh-my'] = array_merge($this->mNamespaceTables['zh-hans']); - $this->mNamespaceTables['zh-sg'] = array_merge($this->mNamespaceTables['zh-hans']); - $this->mNamespaceTables['zh-tw'] = array_merge($this->mNamespaceTables['zh-hant']); } function loadDefaultTables() { diff --git a/languages/messages/MessagesFr.php b/languages/messages/MessagesFr.php index 3479c1a7..c0cd327a 100644 --- a/languages/messages/MessagesFr.php +++ b/languages/messages/MessagesFr.php @@ -254,7 +254,7 @@ $specialPageAliases = array( 'Preferences' => array( 'Préférences' ), 'Watchlist' => array( 'Liste de suivi', 'ListeDeSuivi', 'Suivi' ), 'Recentchanges' => array( 'Modifications récentes', 'Modifications recentes', 'ModificationsRécentes', 'ModificationsRecentes' ), - 'Upload' => array( 'Téléverser', 'Televerser', 'Téléversement', 'Televersement' ), + 'Upload' => array( 'Téléverser', 'Televerser', 'Téléversement', 'Televersement', 'Téléchargement', 'Telechargement' ), 'Listfiles' => array( 'Liste des fichiers', 'ListeDesFichiers', 'Liste des images', 'ListeDesImages' ), 'Newimages' => array( 'Nouveaux fichiers', 'NouveauxFichiers', 'Nouvelles images', 'NouvellesImages' ), 'Listusers' => array( 'Liste des utilisateurs', 'ListeDesUtilisateurs', 'Utilisateurs' ), diff --git a/maintenance/Maintenance.php b/maintenance/Maintenance.php index 277278c0..ee35df7c 100644 --- a/maintenance/Maintenance.php +++ b/maintenance/Maintenance.php @@ -312,6 +312,9 @@ abstract class Maintenance { $this->addOption( 'conf', "Location of LocalSettings.php, if not default", false, true ); $this->addOption( 'wiki', "For specifying the wiki ID", false, true ); $this->addOption( 'globals', "Output globals at the end of processing for debugging" ); + $this->addOption( 'server', "The protocol and server name to use in URLs, e.g.\n" . + "\t\thttp://en.wikipedia.org. This is sometimes necessary because\n" . + "\t\tserver name detection may fail in command line scripts.", false, true ); // If we support a DB, show the options if( $this->getDbType() > 0 ) { $this->addOption( 'dbuser', "The DB user to use for this script", false, true ); @@ -612,7 +615,7 @@ abstract class Maintenance { * Handle some last-minute setup here. */ public function finalSetup() { - global $wgCommandLineMode, $wgShowSQLErrors; + global $wgCommandLineMode, $wgShowSQLErrors, $wgServer; global $wgTitle, $wgProfiling, $IP, $wgDBadminuser, $wgDBadminpassword; global $wgDBuser, $wgDBpassword, $wgDBservers, $wgLBFactoryConf; @@ -623,6 +626,11 @@ abstract class Maintenance { # Same with these $wgCommandLineMode = true; + # Override $wgServer + if( $this->hasOption( 'server') ) { + $wgServer = $this->getOption( 'server', $wgServer ); + } + # If these were passed, use them if( $this->mDbUser ) $wgDBadminuser = $this->mDbUser; diff --git a/maintenance/generateSitemap.php b/maintenance/generateSitemap.php index 8ca79341..04dbbc4d 100644 --- a/maintenance/generateSitemap.php +++ b/maintenance/generateSitemap.php @@ -131,9 +131,6 @@ class GenerateSitemap extends Maintenance { $this->mDescription = "Creates a sitemap for the site"; $this->addOption( 'fspath', 'The file system path to save to, e.g. /tmp/sitemap' . "\n\t\tdefaults to current directory", false, true ); - $this->addOption( 'server', "The protocol and server name to use in URLs, e.g.\n" . - "\t\thttp://en.wikipedia.org. This is sometimes necessary because\n" . - "\t\tserver name detection may fail in command line scripts.", false, true ); $this->addOption( 'compress', 'Compress the sitemap files, can take value yes|no, default yes', false, true ); } diff --git a/maintenance/parserTests.txt b/maintenance/parserTests.txt index 500341d1..4515943a 100644 --- a/maintenance/parserTests.txt +++ b/maintenance/parserTests.txt @@ -7018,6 +7018,20 @@ China !! end !! test +Bug 24072: more test on conversion rule for title +!! options +language=zh variant=zh-tw showtitle +!! input +This should be stripped-{T|zh:China;zh-tw:Taiwan}-! +This won't take interferes with the title rule-{H|zh:Beijing;zh-tw:Taipei}-. +!! result +Taiwan +<p>This should be stripped! +This won't take interferes with the title rule. +</p> +!! end + +!! test Raw output of variant escape tags (R flag) !! options language=zh variant=zh-tw diff --git a/skins/common/wikibits.js b/skins/common/wikibits.js index acdd7281..94f23a9c 100644 --- a/skins/common/wikibits.js +++ b/skins/common/wikibits.js @@ -29,7 +29,7 @@ if (clientPC.indexOf('opera') != -1) { // avoiding false positives from moronic extensions that append to the IE UA // string (bug 23171) var ie6_bugs = false; -if ( /MSIE ([0-9]{1,}[\.0-9]{0,})/.exec( clientPC ) != null +if ( /msie ([0-9]{1,}[\.0-9]{0,})/.exec( clientPC ) != null && parseFloat( RegExp.$1 ) <= 6.0 ) { ie6_bugs = true; } |