cleanupBlocks.php 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  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. $max = $db->selectField( 'ipblocks', 'MAX(ipb_user)' );
  39. // Step 1: Clean up any duplicate user blocks
  40. for ( $from = 1; $from <= $max; $from += $this->mBatchSize ) {
  41. $to = min( $max, $from + $this->mBatchSize - 1 );
  42. $this->output( "Cleaning up duplicate ipb_user ($from-$to of $max)\n" );
  43. $delete = [];
  44. $res = $db->select(
  45. 'ipblocks',
  46. [ 'ipb_user' ],
  47. [
  48. "ipb_user >= $from",
  49. "ipb_user <= $to",
  50. ],
  51. __METHOD__,
  52. [
  53. 'GROUP BY' => 'ipb_user',
  54. 'HAVING' => 'COUNT(*) > 1',
  55. ]
  56. );
  57. foreach ( $res as $row ) {
  58. $bestBlock = null;
  59. $res2 = $db->select(
  60. 'ipblocks',
  61. '*',
  62. [
  63. 'ipb_user' => $row->ipb_user,
  64. ]
  65. );
  66. foreach ( $res2 as $row2 ) {
  67. $block = Block::newFromRow( $row2 );
  68. if ( !$bestBlock ) {
  69. $bestBlock = $block;
  70. continue;
  71. }
  72. // Find the most-restrictive block. Can't use
  73. // Block::chooseBlock because that's for IP blocks, not
  74. // user blocks.
  75. $keep = null;
  76. if ( $keep === null && $block->getExpiry() !== $bestBlock->getExpiry() ) {
  77. // This works for infinite blocks because 'infinity' > '20141024234513'
  78. $keep = $block->getExpiry() > $bestBlock->getExpiry();
  79. }
  80. if ( $keep === null ) {
  81. foreach ( [ 'createaccount', 'sendemail', 'editownusertalk' ] as $action ) {
  82. if ( $block->prevents( $action ) xor $bestBlock->prevents( $action ) ) {
  83. $keep = $block->prevents( $action );
  84. break;
  85. }
  86. }
  87. }
  88. if ( $keep ) {
  89. $delete[] = $bestBlock->getId();
  90. $bestBlock = $block;
  91. } else {
  92. $delete[] = $block->getId();
  93. }
  94. }
  95. }
  96. if ( $delete ) {
  97. $db->delete(
  98. 'ipblocks',
  99. [ 'ipb_id' => $delete ],
  100. __METHOD__
  101. );
  102. }
  103. }
  104. // Step 2: Update the user name in any blocks where it doesn't match
  105. for ( $from = 1; $from <= $max; $from += $this->mBatchSize ) {
  106. $to = min( $max, $from + $this->mBatchSize - 1 );
  107. $this->output( "Cleaning up mismatched user name ($from-$to of $max)\n" );
  108. $res = $db->select(
  109. [ 'ipblocks', 'user' ],
  110. [ 'ipb_id', 'user_name' ],
  111. [
  112. 'ipb_user = user_id',
  113. "ipb_user >= $from",
  114. "ipb_user <= $to",
  115. 'ipb_address != user_name',
  116. ],
  117. __METHOD__
  118. );
  119. foreach ( $res as $row ) {
  120. $db->update(
  121. 'ipblocks',
  122. [ 'ipb_address' => $row->user_name ],
  123. [ 'ipb_id' => $row->ipb_id ],
  124. __METHOD__
  125. );
  126. }
  127. }
  128. $this->output( "Done!\n" );
  129. }
  130. }
  131. $maintClass = "CleanupBlocks";
  132. require_once RUN_MAINTENANCE_IF_MAIN;