cleanupBlocks.php 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. <?php
  2. /**
  3. * Cleans up user blocks with user names not matching the 'user' table
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along
  16. * with this program; if not, write to the Free Software Foundation, Inc.,
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. * http://www.gnu.org/copyleft/gpl.html
  19. *
  20. * @file
  21. * @ingroup Maintenance
  22. */
  23. require_once __DIR__ . '/Maintenance.php';
  24. /**
  25. * Maintenance script to clean up user blocks with user names not matching the
  26. * 'user' table.
  27. *
  28. * @ingroup Maintenance
  29. */
  30. class CleanupBlocks extends Maintenance {
  31. public function __construct() {
  32. parent::__construct();
  33. $this->addDescription( "Cleanup user blocks with user names not matching the 'user' table" );
  34. $this->setBatchSize( 1000 );
  35. }
  36. public function execute() {
  37. $db = $this->getDB( DB_MASTER );
  38. $blockQuery = Block::getQueryInfo();
  39. $max = $db->selectField( 'ipblocks', 'MAX(ipb_user)' );
  40. // Step 1: Clean up any duplicate user blocks
  41. $batchSize = $this->getBatchSize();
  42. for ( $from = 1; $from <= $max; $from += $batchSize ) {
  43. $to = min( $max, $from + $batchSize - 1 );
  44. $this->output( "Cleaning up duplicate ipb_user ($from-$to of $max)\n" );
  45. $delete = [];
  46. $res = $db->select(
  47. 'ipblocks',
  48. [ 'ipb_user' ],
  49. [
  50. "ipb_user >= " . (int)$from,
  51. "ipb_user <= " . (int)$to,
  52. ],
  53. __METHOD__,
  54. [
  55. 'GROUP BY' => 'ipb_user',
  56. 'HAVING' => 'COUNT(*) > 1',
  57. ]
  58. );
  59. foreach ( $res as $row ) {
  60. $bestBlock = null;
  61. $res2 = $db->select(
  62. $blockQuery['tables'],
  63. $blockQuery['fields'],
  64. [
  65. 'ipb_user' => $row->ipb_user,
  66. ],
  67. __METHOD__,
  68. [],
  69. $blockQuery['joins']
  70. );
  71. foreach ( $res2 as $row2 ) {
  72. $block = Block::newFromRow( $row2 );
  73. if ( !$bestBlock ) {
  74. $bestBlock = $block;
  75. continue;
  76. }
  77. // Find the most-restrictive block. Can't use
  78. // Block::chooseBlock because that's for IP blocks, not
  79. // user blocks.
  80. $keep = null;
  81. if ( $keep === null && $block->getExpiry() !== $bestBlock->getExpiry() ) {
  82. // This works for infinite blocks because 'infinity' > '20141024234513'
  83. $keep = $block->getExpiry() > $bestBlock->getExpiry();
  84. }
  85. if ( $keep === null ) {
  86. foreach ( [ 'createaccount', 'sendemail', 'editownusertalk' ] as $action ) {
  87. if ( $block->prevents( $action ) xor $bestBlock->prevents( $action ) ) {
  88. $keep = $block->prevents( $action );
  89. break;
  90. }
  91. }
  92. }
  93. if ( $keep ) {
  94. $delete[] = $bestBlock->getId();
  95. $bestBlock = $block;
  96. } else {
  97. $delete[] = $block->getId();
  98. }
  99. }
  100. }
  101. if ( $delete ) {
  102. $db->delete(
  103. 'ipblocks',
  104. [ 'ipb_id' => $delete ],
  105. __METHOD__
  106. );
  107. }
  108. }
  109. // Step 2: Update the user name in any blocks where it doesn't match
  110. for ( $from = 1; $from <= $max; $from += $batchSize ) {
  111. $to = min( $max, $from + $batchSize - 1 );
  112. $this->output( "Cleaning up mismatched user name ($from-$to of $max)\n" );
  113. $res = $db->select(
  114. [ 'ipblocks', 'user' ],
  115. [ 'ipb_id', 'user_name' ],
  116. [
  117. 'ipb_user = user_id',
  118. "ipb_user >= " . (int)$from,
  119. "ipb_user <= " . (int)$to,
  120. 'ipb_address != user_name',
  121. ],
  122. __METHOD__
  123. );
  124. foreach ( $res as $row ) {
  125. $db->update(
  126. 'ipblocks',
  127. [ 'ipb_address' => $row->user_name ],
  128. [ 'ipb_id' => $row->ipb_id ],
  129. __METHOD__
  130. );
  131. }
  132. }
  133. $this->output( "Done!\n" );
  134. }
  135. }
  136. $maintClass = CleanupBlocks::class;
  137. require_once RUN_MAINTENANCE_IF_MAIN;