* Fixed a whole lot of XSS vulnerabilities in the installer. All require a live installer, i.e. with no LocalSettings.php present.

* Implemented taint support in the installer and fixed some false positives (and false negatives)
This commit is contained in:
Tim Starling 2009-02-05 08:56:35 +00:00
parent 7460b7b7ec
commit 6237fe4bb5
8 changed files with 379 additions and 333 deletions

View file

@ -92,7 +92,8 @@ $ourdb['ibm_db2']['rootuser'] = 'db2admin';
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<title>MediaWiki <?php echo( $wgVersion ); ?> Installation</title>
<meta name="robots" content="noindex,nofollow"/>
<title>MediaWiki <?php echo htmlspecialchars( $wgVersion ); ?> Installation</title>
<style type="text/css">
@import "../skins/monobook/main.css";
@ -210,7 +211,7 @@ $ourdb['ibm_db2']['rootuser'] = 'db2admin';
<div id="content">
<div id="bodyContent">
<h1>MediaWiki <?php print $wgVersion ?> Installation</h1>
<h1>MediaWiki <?php print htmlspecialchars( $wgVersion ) ?> Installation</h1>
<?php
$mainListOpened = false; # Is the main list (environement checking) opend ? Used by dieout
@ -310,7 +311,7 @@ $conf = new ConfigData;
install_version_checks();
$self = 'Installer'; # Maintenance script name, to please Setup.php
print "<li>PHP " . phpversion() . " installed</li>\n";
print "<li>PHP " . htmlspecialchars( phpversion() ) . " installed</li>\n";
error_reporting( 0 );
$phpdatabases = array();
@ -410,7 +411,7 @@ if( wfIniGetBool( "safe_mode" ) ) {
$conf->safeMode = false;
}
$sapi = php_sapi_name();
$sapi = htmlspecialchars( php_sapi_name() );
print "<li>PHP server API is $sapi; ";
$script = defined('MW_INSTALL_PHP5_EXT') ? 'index.php5' : 'index.php';
if( $wgUsePathInfo ) {
@ -593,6 +594,9 @@ print "<li style='font-weight:bold;color:green;font-size:110%'>Environment check
: $_SERVER["SERVER_ADMIN"];
$conf->EmergencyContact = importPost( "EmergencyContact", $defaultEmail );
$conf->DBtype = importPost( "DBtype", $DefaultDBtype );
if ( !isset( $ourdb[$conf->DBtype] ) ) {
$conf->DBtype = $DefaultDBtype;
}
$conf->DBserver = importPost( "DBserver", "localhost" );
$conf->DBname = importPost( "DBname", "wikidb" );
@ -652,6 +656,8 @@ if( $conf->DBpassword != $conf->DBpassword2 ) {
}
if( !preg_match( '/^[A-Za-z_0-9]*$/', $conf->DBprefix ) ) {
$errs["DBprefix"] = "Invalid table prefix";
} else {
untaint( $conf->DBprefix, TC_MYSQL );
}
error_reporting( E_ALL );
@ -728,7 +734,7 @@ $conf->MCServers = importRequest( "MCServers" );
/* Test memcached servers */
if ( $conf->Shm == 'memcached' && $conf->MCServers ) {
$conf->MCServerArray = array_map( 'trim', explode( ',', $conf->MCServers ) );
$conf->MCServerArray = wfArrayMap( 'trim', explode( ',', $conf->MCServers ) );
foreach ( $conf->MCServerArray as $server ) {
$error = testMemcachedServer( $server );
if ( $error ) {
@ -781,7 +787,7 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
$errs["DBtype"] = "Unknown database type '$conf->DBtype'";
continue;
}
print "<li>Database type: {$conf->DBtypename}</li>\n";
print "<li>Database type: " . htmlspecialchars( $conf->DBtypename ) . "</li>\n";
$dbclass = 'Database'.ucfirst($conf->DBtype);
$wgDBtype = $conf->DBtype;
$wgDBadminuser = "root";
@ -812,7 +818,7 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
$wgTitle = Title::newFromText( "Installation script" );
error_reporting( E_ALL );
print "<li>Loading class: $dbclass</li>\n";
print "<li>Loading class: " . htmlspecialchars( $dbclass ) . "</li>\n";
$dbc = new $dbclass;
if( $conf->DBtype == 'mysql' ) {
@ -836,7 +842,7 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
}
# Attempt to connect
echo( "<li>Attempting to connect to database server as $db_user..." );
echo( "<li>Attempting to connect to database server as " . htmlspecialchars( $db_user ) . "..." );
$wgDatabase = Database::newFromParams( $wgDBserver, $db_user, $db_pass, '', 1 );
# Check the connection and respond to errors
@ -871,7 +877,7 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
case 2003:
default:
# General connection problem
echo( "failed with error [$errno] $errtx.</li>\n" );
echo( htmlspecialchars( "failed with error [$errno] $errtx." ) . "</li>\n" );
$errs["DBserver"] = "Connection failed";
break;
} # switch
@ -888,10 +894,11 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
$db_pass = $wgDBpassword;
}
echo( "<li>Attempting to connect to database \"$wgDBname\" as \"$db_user\"..." );
echo( "<li>Attempting to connect to database \"" . htmlspecialchars( $wgDBname ) .
"\" as \"" . htmlspecialchars( $db_user ) . "\"..." );
$wgDatabase = $dbc->newFromParams($wgDBserver, $db_user, $db_pass, $wgDBname, 1);
if (!$wgDatabase->isOpen()) {
print " error: " . $wgDatabase->lastError() . "</li>\n";
print " error: " . htmlspecialchars( $wgDatabase->lastError() ) . "</li>\n";
} else {
$myver = $wgDatabase->getServerVersion();
}
@ -904,10 +911,11 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
// Changed !mysql to postgres check since it seems to only apply to postgres
if( $useRoot && $conf->DBtype == 'postgres' ) {
$wgDBsuperuser = $conf->RootUser;
echo( "<li>Attempting to connect to database \"postgres\" as superuser \"$wgDBsuperuser\"..." );
echo( "<li>Attempting to connect to database \"postgres\" as superuser \"" .
htmlspecialchars( $wgDBsuperuser ) . "\"..." );
$wgDatabase = $dbc->newFromParams($wgDBserver, $wgDBsuperuser, $conf->RootPW, "postgres", 1);
if (!$wgDatabase->isOpen()) {
print " error: " . $wgDatabase->lastError() . "</li>\n";
print " error: " . htmlspecialchars( $wgDatabase->lastError() ) . "</li>\n";
$errs["DBserver"] = "Could not connect to database as superuser";
$errs["RootUser"] = "Check username";
$errs["RootPW"] = "and password";
@ -915,10 +923,11 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
}
$wgDatabase->initial_setup($conf->RootPW, 'postgres');
}
echo( "<li>Attempting to connect to database \"$wgDBname\" as \"$wgDBuser\"..." );
echo( "<li>Attempting to connect to database \"" . htmlspecialchars( $wgDBname ) .
"\" as \"" . htmlspecialchars( $wgDBuser ) . "\"..." );
$wgDatabase = $dbc->newFromParams($wgDBserver, $wgDBuser, $wgDBpassword, $wgDBname, 1);
if (!$wgDatabase->isOpen()) {
print " error: " . $wgDatabase->lastError() . "</li>\n";
print " error: " . htmlspecialchars( $wgDatabase->lastError() ) . "</li>\n";
} else {
$myver = $wgDatabase->getServerVersion();
}
@ -930,7 +939,7 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
continue;
}
print "<li>Connected to {$conf->DBtype} $myver";
print "<li>Connected to " . htmlspecialchars( "{$conf->DBtype} $myver" );
if ($conf->DBtype == 'mysql') {
if( version_compare( $myver, "4.0.14" ) < 0 ) {
print "</li>\n";
@ -1017,15 +1026,19 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
}
}
if ( $existingSchema && $existingSchema != $conf->DBschema ) {
print "<li><strong>Warning:</strong> you requested the {$conf->DBschema} schema, " .
"but the existing database has the $existingSchema schema. This upgrade script ".
"can't convert it, so it will remain $existingSchema.</li>\n";
$encExisting = htmlspecialchars( $existingSchema );
$encRequested = htmlspecialchars( $conf->DBschema );
print "<li><strong>Warning:</strong> you requested the $encRequested schema, " .
"but the existing database has the $encExisting schema. This upgrade script ".
"can't convert it, so it will remain $encExisting.</li>\n";
$conf->setSchema( $existingSchema, $conf->DBengine );
}
if ( $existingEngine && $existingEngine != $conf->DBengine ) {
print "<li><strong>Warning:</strong> you requested the {$conf->DBengine} storage " .
"engine, but the existing database uses the $existingEngine engine. This upgrade " .
"script can't convert it, so it will remain $existingEngine.</li>\n";
$encExisting = htmlspecialchars( $existingEngine );
$encRequested = htmlspecialchars( $conf->DBengine );
print "<li><strong>Warning:</strong> you requested the $encRequested storage " .
"engine, but the existing database uses the $encExisting engine. This upgrade " .
"script can't convert it, so it will remain $encExisting.</li>\n";
$conf->setSchema( $conf->DBschema, $existingEngine );
}
}
@ -1066,7 +1079,8 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
}
$wgDatabase->freeResult( $res );
if ( !$found && $conf->DBengine != 'MyISAM' ) {
echo "<li><strong>Warning:</strong> {$conf->DBengine} storage engine not available, " .
echo "<li><strong>Warning:</strong> " . htmlspecialchars( $conf->DBengine ) .
" storage engine not available, " .
"using MyISAM instead</li>\n";
$conf->setSchema( $conf->DBschema, 'MyISAM' );
}
@ -1105,10 +1119,10 @@ if( $conf->posted && ( 0 == count( $errs ) ) ) {
if( $wgDatabase2->isOpen() ) {
# Nope, just close the test connection and continue
$wgDatabase2->close();
echo( "<li>User $wgDBuser exists. Skipping grants.</li>\n" );
echo( "<li>User " . htmlspecialchars( $wgDBuser ) . " exists. Skipping grants.</li>\n" );
} else {
# Yes, so run the grants
echo( "<li>Granting user permissions to $wgDBuser on $wgDBname..." );
echo( "<li>" . htmlspecialchars( "Granting user permissions to $wgDBuser on $wgDBname..." ) );
dbsource( "../maintenance/users.sql", $wgDatabase );
echo( "success.</li>\n" );
}
@ -1213,7 +1227,9 @@ if( count( $errs ) ) {
$list = getLanguageList();
foreach( $list as $code => $name ) {
$sel = ($code == $conf->LanguageCode) ? 'selected="selected"' : '';
echo "\n\t\t<option value=\"$code\" $sel>$name</option>";
$encCode = htmlspecialchars( $code );
$encName = htmlspecialchars( $name );
echo "\n\t\t<option value=\"$encCode\" $sel>$encName</option>";
}
echo "\n";
?>
@ -1380,7 +1396,11 @@ if( count( $errs ) ) {
<div class="config-section">
<div class="config-input">
<label class='column'>Database type:</label>
<?php if (isset($errs['DBpicktype'])) print "\t<span class='error'>$errs[DBpicktype]</span>\n"; ?>
<?php
if (isset($errs['DBpicktype'])) {
print "\t<span class='error'>" . htmlspecialchars( $errs[DBpicktype] ) . "</span>\n";
}
?>
<ul class='plain'><?php
database_picker($conf);
?></ul>
@ -1524,7 +1544,7 @@ if( count( $errs ) ) {
</div>
</form>
<script type="text/javascript">
window.onload = toggleDBarea('<?php echo $conf->DBtype; ?>',
window.onload = toggleDBarea('<?php echo htmlspecialchars( Xml::encodeJsVar( $conf->DBtype ) ); ?>',
<?php
## If they passed in a root user name, don't populate it on page load
echo strlen(importPost('RootUser', '')) ? 0 : 1;
@ -1656,7 +1676,7 @@ function writeLocalSettings( $conf ) {
}
# Add slashes to strings for double quoting
$slconf = array_map( "escapePhpString", get_object_vars( $conf ) );
$slconf = wfArrayMap( "escapePhpString", get_object_vars( $conf ) );
if( $conf->License == 'gfdl1_2' || $conf->License == 'pd' || $conf->License == 'gfdl1_3' ) {
# Needs literal string interpolation for the current style path
$slconf['RightsIcon'] = $conf->RightsIcon;
@ -1841,6 +1861,7 @@ function importVar( &$var, $name, $default = "" ) {
} else {
$retval = $default;
}
taint( $retval );
return $retval;
}
@ -1856,10 +1877,8 @@ function importRequest( $name, $default = "" ) {
return importVar( $_REQUEST, $name, $default );
}
$radioCount = 0;
function aField( &$conf, $field, $text, $type = "text", $value = "", $onclick = '' ) {
global $radioCount;
static $radioCount = 0;
if( $type != "" ) {
$xtype = "type=\"$type\"";
} else {
@ -1899,7 +1918,9 @@ function aField( &$conf, $field, $text, $type = "text", $value = "", $onclick =
}
global $errs;
if(isset($errs[$field])) echo "<span class='error'>" . $errs[$field] . "</span>\n";
if(isset($errs[$field])) {
echo "<span class='error'>" . htmlspecialchars( $errs[$field] ) . "</span>\n";
}
}
function getLanguageList() {
@ -2042,7 +2063,7 @@ function getShellLocale( $wikiLang ) {
return false;
}
$lines = array_map( 'trim', $lines );
$lines = wfArrayMap( 'trim', $lines );
$candidatesByLocale = array();
$candidatesByLang = array();
foreach ( $lines as $line ) {
@ -2086,6 +2107,17 @@ function getShellLocale( $wikiLang ) {
return false;
}
function wfArrayMap( $function, $input ) {
$ret = array_map( $function, $input );
foreach ( $ret as $key => $value ) {
$taint = istainted( $input[$key] );
if ( $taint ) {
taint( $ret[$key], $taint );
}
}
return $ret;
}
?>
<div class="license">

View file

@ -2955,6 +2955,21 @@ function wfWaitForSlaves( $maxLag ) {
}
}
/**
* Output some plain text in command-line mode or in the installer (updaters.inc).
* Do not use it in any other context, its behaviour is subject to change.
*/
function wfOut( $s ) {
static $lineStarted = false;
global $wgCommandLineMode;
if ( $wgCommandLineMode && !defined( 'MEDIAWIKI_INSTALL' ) ) {
echo $s;
} else {
echo htmlspecialchars( $s );
}
flush();
}
/** Generate a random 32-character hexadecimal token.
* @param mixed $salt Some sort of salt, if necessary, to add to random characters before hashing.
*/

View file

@ -9,11 +9,11 @@
function convertLinks() {
global $wgDBtype;
if( $wgDBtype == 'postgres' ) {
print "Links table already ok on Postgres.\n";
wfOut( "Links table already ok on Postgres.\n" );
return;
}
print "Converting links table to ID-ID...\n";
wfOut( "Converting links table to ID-ID...\n" );
global $wgLang, $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname;
global $noKeys, $logPerformance, $fh;
@ -48,7 +48,7 @@ function convertLinks() {
$res = $dbw->query( "SELECT l_from FROM $links LIMIT 1" );
if ( $dbw->fieldType( $res, 0 ) == "int" ) {
print "Schema already converted\n";
wfOut( "Schema already converted\n" );
return;
}
@ -58,13 +58,13 @@ function convertLinks() {
$dbw->freeResult( $res );
if ( $numRows == 0 ) {
print "Updating schema (no rows to convert)...\n";
wfOut( "Updating schema (no rows to convert)...\n" );
createTempTable();
} else {
if ( $logPerformance ) { $fh = fopen ( $perfLogFilename, "w" ); }
$baseTime = $startTime = getMicroTime();
# Create a title -> cur_id map
print "Loading IDs from $cur table...\n";
wfOut( "Loading IDs from $cur table...\n" );
performanceLog ( "Reading $numRows rows from cur table...\n" );
performanceLog ( "rows read vs seconds elapsed:\n" );
@ -82,13 +82,13 @@ function convertLinks() {
if ($reportCurReadProgress) {
if (($curRowsRead % $curReadReportInterval) == 0) {
performanceLog( $curRowsRead . " " . (getMicroTime() - $baseTime) . "\n" );
print "\t$curRowsRead rows of $cur table read.\n";
wfOut( "\t$curRowsRead rows of $cur table read.\n" );
}
}
}
$dbw->freeResult( $res );
$dbw->bufferResults( true );
print "Finished loading IDs.\n\n";
wfOut( "Finished loading IDs.\n\n" );
performanceLog( "Took " . (getMicroTime() - $baseTime) . " seconds to load IDs.\n\n" );
#--------------------------------------------------------------------
@ -97,7 +97,7 @@ function convertLinks() {
createTempTable();
performanceLog( "Resetting timer.\n\n" );
$baseTime = getMicroTime();
print "Processing $numRows rows from $links table...\n";
wfOut( "Processing $numRows rows from $links table...\n" );
performanceLog( "Processing $numRows rows from $links table...\n" );
performanceLog( "rows inserted vs seconds elapsed:\n" );
@ -127,19 +127,19 @@ function convertLinks() {
}
}
$dbw->freeResult($res);
#print "rowOffset: $rowOffset\ttuplesAdded: $tuplesAdded\tnumBadLinks: $numBadLinks\n";
#wfOut( "rowOffset: $rowOffset\ttuplesAdded: $tuplesAdded\tnumBadLinks: $numBadLinks\n" );
if ( $tuplesAdded != 0 ) {
if ($reportLinksConvProgress) {
print "Inserting $tuplesAdded tuples into $links_temp...";
wfOut( "Inserting $tuplesAdded tuples into $links_temp..." );
}
$dbw->query( implode("",$sqlWrite) );
$totalTuplesInserted += $tuplesAdded;
if ($reportLinksConvProgress)
print " done. Total $totalTuplesInserted tuples inserted.\n";
wfOut( " done. Total $totalTuplesInserted tuples inserted.\n" );
performanceLog( $totalTuplesInserted . " " . (getMicroTime() - $baseTime) . "\n" );
}
}
print "$totalTuplesInserted valid titles and $numBadLinks invalid titles were processed.\n\n";
wfOut( "$totalTuplesInserted valid titles and $numBadLinks invalid titles were processed.\n\n" );
performanceLog( "$totalTuplesInserted valid titles and $numBadLinks invalid titles were processed.\n" );
performanceLog( "Total execution time: " . (getMicroTime() - $startTime) . " seconds.\n" );
if ( $logPerformance ) { fclose ( $fh ); }
@ -149,25 +149,25 @@ function convertLinks() {
if ( $overwriteLinksTable ) {
$dbConn = Database::newFromParams( $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname );
if (!($dbConn->isOpen())) {
print "Opening connection to database failed.\n";
wfOut( "Opening connection to database failed.\n" );
return;
}
# Check for existing links_backup, and delete it if it exists.
print "Dropping backup links table if it exists...";
wfOut( "Dropping backup links table if it exists..." );
$dbConn->query( "DROP TABLE IF EXISTS $links_backup", DB_MASTER);
print " done.\n";
wfOut( " done.\n" );
# Swap in the new table, and move old links table to links_backup
print "Swapping tables '$links' to '$links_backup'; '$links_temp' to '$links'...";
wfOut( "Swapping tables '$links' to '$links_backup'; '$links_temp' to '$links'..." );
$dbConn->query( "RENAME TABLE links TO $links_backup, $links_temp TO $links", DB_MASTER );
print " done.\n\n";
wfOut( " done.\n\n" );
$dbConn->close();
print "Conversion complete. The old table remains at $links_backup;\n";
print "delete at your leisure.\n";
wfOut( "Conversion complete. The old table remains at $links_backup;\n" );
wfOut( "delete at your leisure.\n" );
} else {
print "Conversion complete. The converted table is at $links_temp;\n";
print "the original links table is unchanged.\n";
wfOut( "Conversion complete. The converted table is at $links_temp;\n" );
wfOut( "the original links table is unchanged.\n" );
}
}
@ -179,16 +179,16 @@ function createTempTable() {
$dbConn = Database::newFromParams( $wgDBserver, $wgDBadminuser, $wgDBadminpassword, $wgDBname );
if (!($dbConn->isOpen())) {
print "Opening connection to database failed.\n";
wfOut( "Opening connection to database failed.\n" );
return;
}
$links_temp = $dbConn->tableName( 'links_temp' );
print "Dropping temporary links table if it exists...";
wfOut( "Dropping temporary links table if it exists..." );
$dbConn->query( "DROP TABLE IF EXISTS $links_temp");
print " done.\n";
wfOut( " done.\n" );
print "Creating temporary links table...";
wfOut( "Creating temporary links table..." );
if ( $noKeys ) {
$dbConn->query( "CREATE TABLE $links_temp ( " .
"l_from int(8) unsigned NOT NULL default '0', " .
@ -200,7 +200,7 @@ function createTempTable() {
"UNIQUE KEY l_from(l_from,l_to), " .
"KEY (l_to))");
}
print " done.\n\n";
wfOut( " done.\n\n" );
}
function performanceLog( $text ) {

View file

@ -7,34 +7,34 @@
function wfInitStats( $options=array() ) {
$dbr = wfGetDB( DB_SLAVE );
echo "Counting total edits...";
wfOut( "Counting total edits..." );
$edits = $dbr->selectField( 'revision', 'COUNT(*)', '', __METHOD__ );
$edits += $dbr->selectField( 'archive', 'COUNT(*)', '', __METHOD__ );
echo "{$edits}\nCounting number of articles...";
wfOut( "{$edits}\nCounting number of articles..." );
global $wgContentNamespaces;
$good = $dbr->selectField( 'page', 'COUNT(*)', array( 'page_namespace' => $wgContentNamespaces, 'page_is_redirect' => 0, 'page_len > 0' ), __METHOD__ );
echo "{$good}\nCounting total pages...";
wfOut( "{$good}\nCounting total pages..." );
$pages = $dbr->selectField( 'page', 'COUNT(*)', '', __METHOD__ );
echo "{$pages}\nCounting number of users...";
wfOut( "{$pages}\nCounting number of users..." );
$users = $dbr->selectField( 'user', 'COUNT(*)', '', __METHOD__ );
echo "{$users}\nCounting number of admins...";
wfOut( "{$users}\nCounting number of admins..." );
$admin = $dbr->selectField( 'user_groups', 'COUNT(*)', array( 'ug_group' => 'sysop' ), __METHOD__ );
echo "{$admin}\nCounting number of images...";
wfOut( "{$admin}\nCounting number of images..." );
$image = $dbr->selectField( 'image', 'COUNT(*)', '', __METHOD__ );
echo "{$image}\n";
wfOut( "{$image}\n" );
if( !isset( $options['noviews'] ) ) {
echo "Counting total page views...";
wfOut( "Counting total page views..." );
$views = $dbr->selectField( 'page', 'SUM(page_counter)', '', __METHOD__ );
echo "{$views}\n";
wfOut( "{$views}\n" );
}
echo "\nUpdating site statistics...";
wfOut( "\nUpdating site statistics..." );
$dbw = wfGetDB( DB_MASTER );
$values = array( 'ss_total_edits' => $edits,
@ -53,5 +53,5 @@ function wfInitStats( $options=array() ) {
$dbw->insert( 'site_stats', array_merge( $values, $conds, $views ), __METHOD__ );
}
echo( "done.\n" );
wfOut( "done.\n" );
}

View file

@ -18,9 +18,9 @@ function populateCategory( $begin, $maxlag, $throttle, $force ) {
__FUNCTION__
);
if( $row ) {
echo "Category table already populated. Use php ".
wfOut( "Category table already populated. Use php ".
"maintenance/populateCategory.php\n--force from the command line ".
"to override.\n";
"to override.\n" );
return true;
}
}
@ -56,14 +56,14 @@ function populateCategory( $begin, $maxlag, $throttle, $force ) {
# Use the row to update the category count
$cat = Category::newFromName( $name );
if( !is_object( $cat ) ) {
echo "The category named $name is not valid?!\n";
wfOut( "The category named $name is not valid?!\n" );
} else {
$cat->refreshCounts();
}
++$i;
if( !($i % REPORTING_INTERVAL) ) {
echo "$name\n";
wfOut( "$name\n" );
wfWaitForSlaves( $maxlag );
}
usleep( $throttle*1000 );
@ -76,10 +76,10 @@ function populateCategory( $begin, $maxlag, $throttle, $force ) {
'IGNORE'
)
) {
echo "Category population complete.\n";
wfOut( "Category population complete.\n" );
return true;
} else {
echo "Could not insert category population row.\n";
wfOut( "Could not insert category population row.\n" );
return false;
}
}

View file

@ -3,11 +3,11 @@
define( 'BATCH_SIZE', 200 );
function populate_rev_parent_id( $db ) {
echo "Populating rev_parent_id column\n";
wfOut( "Populating rev_parent_id column\n" );
$start = $db->selectField( 'revision', 'MIN(rev_id)', false, __FUNCTION__ );
$end = $db->selectField( 'revision', 'MAX(rev_id)', false, __FUNCTION__ );
if( is_null( $start ) || is_null( $end ) ){
echo "...revision table seems to be empty.\n";
wfOut( "...revision table seems to be empty.\n" );
$db->insert( 'updatelog',
array( 'ul_key' => 'populate rev_parent_id' ),
__FUNCTION__,
@ -16,12 +16,12 @@ function populate_rev_parent_id( $db ) {
}
# Do remaining chunk
$end += BATCH_SIZE - 1;
$blockStart = $start;
$blockEnd = $start + BATCH_SIZE - 1;
$blockStart = intval( $start );
$blockEnd = intval( $start ) + BATCH_SIZE - 1;
$count = 0;
$changed = 0;
while( $blockEnd <= $end ) {
echo "...doing rev_id from $blockStart to $blockEnd\n";
wfOut( "...doing rev_id from $blockStart to $blockEnd\n" );
$cond = "rev_id BETWEEN $blockStart AND $blockEnd";
$res = $db->select( 'revision',
array('rev_id','rev_page','rev_timestamp','rev_parent_id'),
@ -36,14 +36,14 @@ function populate_rev_parent_id( $db ) {
# as timestamp can only decrease and never loops with IDs (from parent to parent)
$previousID = $db->selectField( 'revision', 'rev_id',
array( 'rev_page' => $row->rev_page, 'rev_timestamp' => $row->rev_timestamp,
"rev_id < {$row->rev_id}" ),
"rev_id < " . intval( $row->rev_id ) ),
__FUNCTION__,
array( 'ORDER BY' => 'rev_id DESC' ) );
# If there are none, check the the highest ID with a lower timestamp
if( !$previousID ) {
# Get the highest older timestamp
$lastTimestamp = $db->selectField( 'revision', 'rev_timestamp',
array( 'rev_page' => $row->rev_page, "rev_timestamp < '{$row->rev_timestamp}'" ),
array( 'rev_page' => $row->rev_page, "rev_timestamp < " . $db->addQuotes( $row->rev_timestamp ) ),
__FUNCTION__,
array( 'ORDER BY' => 'rev_timestamp DESC' ) );
# If there is one, let the highest rev ID win
@ -73,10 +73,10 @@ function populate_rev_parent_id( $db ) {
__FUNCTION__,
'IGNORE' );
if( $logged ) {
echo "rev_parent_id population complete ... {$count} rows [{$changed} changed]\n";
wfOut( "rev_parent_id population complete ... {$count} rows [{$changed} changed]\n" );
return true;
} else {
echo "Could not insert rev_parent_id population row.\n";
wfOut( "Could not insert rev_parent_id population row.\n" );
return false;
}
}

File diff suppressed because it is too large Load diff

View file

@ -45,7 +45,7 @@ class UserDupes {
$fname = 'UserDupes::hasUniqueIndex';
$info = $this->db->indexInfo( 'user', 'user_name', $fname );
if( !$info ) {
echo "WARNING: doesn't seem to have user_name index at all!\n";
wfOut( "WARNING: doesn't seem to have user_name index at all!\n" );
return false;
}
@ -92,11 +92,11 @@ class UserDupes {
$this->lock();
echo "Checking for duplicate accounts...\n";
wfOut( "Checking for duplicate accounts...\n" );
$dupes = $this->getDupes();
$count = count( $dupes );
echo "Found $count accounts with duplicate records on ".wfWikiID().".\n";
wfOut( "Found $count accounts with duplicate records on ".wfWikiID().".\n" );
$this->trimmed = 0;
$this->reassigned = 0;
$this->failed = 0;
@ -106,34 +106,34 @@ class UserDupes {
$this->unlock();
echo "\n";
wfOut( "\n" );
if( $this->reassigned > 0 ) {
if( $doDelete ) {
echo "$this->reassigned duplicate accounts had edits reassigned to a canonical record id.\n";
wfOut( "$this->reassigned duplicate accounts had edits reassigned to a canonical record id.\n" );
} else {
echo "$this->reassigned duplicate accounts need to have edits reassigned.\n";
wfOut( "$this->reassigned duplicate accounts need to have edits reassigned.\n" );
}
}
if( $this->trimmed > 0 ) {
if( $doDelete ) {
echo "$this->trimmed duplicate user records were deleted from ".wfWikiID().".\n";
wfOut( "$this->trimmed duplicate user records were deleted from ".wfWikiID().".\n" );
} else {
echo "$this->trimmed duplicate user accounts were found on ".wfWikiID()." which can be removed safely.\n";
wfOut( "$this->trimmed duplicate user accounts were found on ".wfWikiID()." which can be removed safely.\n" );
}
}
if( $this->failed > 0 ) {
echo "Something terribly awry; $this->failed duplicate accounts were not removed.\n";
wfOut( "Something terribly awry; $this->failed duplicate accounts were not removed.\n" );
return false;
}
if( $this->trimmed == 0 || $doDelete ) {
echo "It is now safe to apply the unique index on user_name.\n";
wfOut( "It is now safe to apply the unique index on user_name.\n" );
return true;
} else {
echo "Run this script again with the --fix option to automatically delete them.\n";
wfOut( "Run this script again with the --fix option to automatically delete them.\n" );
return false;
}
}
@ -215,36 +215,36 @@ class UserDupes {
$firstRow = $this->db->fetchObject( $result );
$firstId = $firstRow->user_id;
echo "Record that will be used for '$name' is user_id=$firstId\n";
wfOut( "Record that will be used for '$name' is user_id=$firstId\n" );
while( $row = $this->db->fetchObject( $result ) ) {
$dupeId = $row->user_id;
echo "... dupe id $dupeId: ";
wfOut( "... dupe id $dupeId: " );
$edits = $this->editCount( $dupeId );
if( $edits > 0 ) {
$this->reassigned++;
echo "has $edits edits! ";
wfOut( "has $edits edits! " );
if( $doDelete ) {
$this->reassignEdits( $dupeId, $firstId );
$newEdits = $this->editCount( $dupeId );
if( $newEdits == 0 ) {
echo "confirmed cleaned. ";
wfOut( "confirmed cleaned. " );
} else {
$this->failed++;
echo "WARNING! $newEdits remaining edits for $dupeId; NOT deleting user.\n";
wfOut( "WARNING! $newEdits remaining edits for $dupeId; NOT deleting user.\n" );
continue;
}
} else {
echo "(will need to reassign edits on fix)";
wfOut( "(will need to reassign edits on fix)" );
}
} else {
echo "ok, no edits. ";
wfOut( "ok, no edits. " );
}
$this->trimmed++;
if( $doDelete ) {
$this->trimAccount( $dupeId );
}
echo "\n";
wfOut( "\n" );
}
$this->db->freeResult( $result );
}
@ -306,12 +306,12 @@ class UserDupes {
*/
function reassignEditsOn( $table, $field, $from, $to ) {
$fname = 'UserDupes::reassignEditsOn';
echo "reassigning on $table... ";
wfOut( "reassigning on $table... " );
$this->db->update( $table,
array( $field => $to ),
array( $field => $from ),
$fname );
echo "ok. ";
wfOut( "ok. " );
}
/**
@ -321,9 +321,9 @@ class UserDupes {
*/
function trimAccount( $userid ) {
$fname = 'UserDupes::trimAccount';
echo "deleting...";
wfOut( "deleting..." );
$this->db->delete( 'user', array( 'user_id' => $userid ), $fname );
echo " ok";
wfOut( " ok" );
}
}