MediaWiki  master
DatabaseBlock.php
Go to the documentation of this file.
1 <?php
23 namespace MediaWiki\Block;
24 
25 use CommentStore;
26 use Hooks;
27 use Html;
37 use MWException;
38 use stdClass;
39 use Title;
40 use Wikimedia\IPUtils;
43 
57  public $mAuto;
58 
64 
66  private $mId;
67 
69  private $mFromPrimary;
70 
72  private $isAutoblocking;
73 
75  private $restrictions;
76 
78  private $blocker;
79 
102  public function __construct( array $options = [] ) {
103  parent::__construct( $options );
104 
105  $defaults = [
106  'user' => null,
107  'auto' => false,
108  'expiry' => '',
109  'createAccount' => false,
110  'enableAutoblock' => false,
111  'blockEmail' => false,
112  'allowUsertalk' => false,
113  'sitewide' => true,
114  'by' => null,
115  'byText' => '',
116  ];
117 
118  $options += $defaults;
119 
120  if ( $options['by'] ) {
121  if ( $options['by'] instanceof UserIdentity ) {
122  $this->setBlocker( $options['by'] );
123  } else {
124  // Local user, passed by ID. Deprecated case,
125  // callers should provide UserIdentity in the 'by'
126  // option.
127  wfDeprecatedMsg( __METHOD__ . ': $options[\'by\'] calling with ID is deprecated', '1.36' );
128  $localBlocker = MediaWikiServices::getInstance()
129  ->getUserFactory()
130  ->newFromId( $options['by'] );
131  $this->setBlocker( $localBlocker );
132  }
133  } elseif ( $options['byText'] ) {
134  // Foreign user. Deprecated case, callers should
135  // provide UserIdentity in the 'by' option.
136  wfDeprecatedMsg( __METHOD__ . ': $options[\'byText\'] is deprecated', '1.36' );
137  $foreignBlocker = MediaWikiServices::getInstance()
138  ->getActorStore()
139  ->getUserIdentityByName( $options['byText'] );
140  if ( !$foreignBlocker ) {
141  // An actor for an interwiki user might not exist on this wiki,
142  // so it's ok to create one. Interwiki actors are still local actors.
143  $foreignBlocker = new UserIdentityValue( 0, $options['byText'], UserIdentity::LOCAL );
144  }
145  $this->setBlocker( $foreignBlocker );
146  }
147 
148  $this->setExpiry( wfGetDB( DB_REPLICA )->decodeExpiry( $options['expiry'] ) );
149 
150  # Boolean settings
151  $this->mAuto = (bool)$options['auto'];
152  $this->isAutoblocking( (bool)$options['enableAutoblock'] );
153  $this->isSitewide( (bool)$options['sitewide'] );
154  $this->isEmailBlocked( (bool)$options['blockEmail'] );
155  $this->isCreateAccountBlocked( (bool)$options['createAccount'] );
156  $this->isUsertalkEditAllowed( (bool)$options['allowUsertalk'] );
157 
158  $this->mFromPrimary = false;
159  }
160 
167  public static function newFromID( $id ) {
168  $dbr = wfGetDB( DB_REPLICA );
169  $blockQuery = self::getQueryInfo();
170  $res = $dbr->selectRow(
171  $blockQuery['tables'],
172  $blockQuery['fields'],
173  [ 'ipb_id' => $id ],
174  __METHOD__,
175  [],
176  $blockQuery['joins']
177  );
178  if ( $res ) {
179  return self::newFromRow( $res );
180  } else {
181  return null;
182  }
183  }
184 
199  public static function getQueryInfo() {
200  $commentQuery = CommentStore::getStore()->getJoin( 'ipb_reason' );
201  return [
202  'tables' => [
203  'ipblocks',
204  'ipblocks_actor' => 'actor'
205  ] + $commentQuery['tables'],
206  'fields' => [
207  'ipb_id',
208  'ipb_address',
209  'ipb_timestamp',
210  'ipb_auto',
211  'ipb_anon_only',
212  'ipb_create_account',
213  'ipb_enable_autoblock',
214  'ipb_expiry',
215  'ipb_deleted',
216  'ipb_block_email',
217  'ipb_allow_usertalk',
218  'ipb_parent_block_id',
219  'ipb_sitewide',
220  'ipb_by_actor',
221  'ipb_by' => 'ipblocks_actor.actor_user',
222  'ipb_by_text' => 'ipblocks_actor.actor_name'
223  ] + $commentQuery['fields'],
224  'joins' => [
225  'ipblocks_actor' => [ 'JOIN', 'actor_id=ipb_by_actor' ]
226  ] + $commentQuery['joins'],
227  ];
228  }
229 
237  public function equals( DatabaseBlock $block ) {
238  return (
239  (string)$this->target == (string)$block->target
240  && $this->type == $block->type
241  && $this->mAuto == $block->mAuto
242  && $this->isHardblock() == $block->isHardblock()
243  && $this->isCreateAccountBlocked() == $block->isCreateAccountBlocked()
244  && $this->getExpiry() == $block->getExpiry()
245  && $this->isAutoblocking() == $block->isAutoblocking()
246  && $this->getHideName() == $block->getHideName()
247  && $this->isEmailBlocked() == $block->isEmailBlocked()
248  && $this->isUsertalkEditAllowed() == $block->isUsertalkEditAllowed()
249  && $this->getReasonComment()->text == $block->getReasonComment()->text
250  && $this->isSitewide() == $block->isSitewide()
251  // DatabaseBlock::getRestrictions() may perform a database query, so
252  // keep it at the end.
253  && $this->getBlockRestrictionStore()->equals(
254  $this->getRestrictions(), $block->getRestrictions()
255  )
256  );
257  }
258 
271  protected static function newLoad(
272  $specificTarget,
273  $specificType,
274  $fromPrimary,
275  $vagueTarget = null
276  ) {
277  $db = wfGetDB( $fromPrimary ? DB_PRIMARY : DB_REPLICA );
278 
279  if ( $specificType !== null ) {
280  $conds = [ 'ipb_address' => [ (string)$specificTarget ] ];
281  } else {
282  $conds = [ 'ipb_address' => [] ];
283  }
284 
285  # Be aware that the != '' check is explicit, since empty values will be
286  # passed by some callers (T31116)
287  if ( $vagueTarget != '' ) {
289  ->getBlockUtils()
290  ->parseBlockTarget( $vagueTarget );
291  switch ( $type ) {
292  case self::TYPE_USER:
293  # Slightly weird, but who are we to argue?
294  $conds['ipb_address'][] = (string)$target;
295  $conds = $db->makeList( $conds, LIST_OR );
296  break;
297 
298  case self::TYPE_IP:
299  $conds['ipb_address'][] = (string)$target;
300  $conds['ipb_address'] = array_unique( $conds['ipb_address'] );
301  $conds[] = self::getRangeCond( IPUtils::toHex( $target ) );
302  $conds = $db->makeList( $conds, LIST_OR );
303  break;
304 
305  case self::TYPE_RANGE:
306  list( $start, $end ) = IPUtils::parseRange( $target );
307  $conds['ipb_address'][] = (string)$target;
308  $conds[] = self::getRangeCond( $start, $end );
309  $conds = $db->makeList( $conds, LIST_OR );
310  break;
311 
312  default:
313  throw new MWException( "Tried to load block with invalid type" );
314  }
315  }
316 
317  $blockQuery = self::getQueryInfo();
318  $res = $db->select(
319  $blockQuery['tables'],
320  $blockQuery['fields'],
321  $conds,
322  __METHOD__,
323  [],
324  $blockQuery['joins']
325  );
326 
327  $blocks = [];
328  $blockIds = [];
329  $autoBlocks = [];
330  foreach ( $res as $row ) {
331  $block = self::newFromRow( $row );
332 
333  # Don't use expired blocks
334  if ( $block->isExpired() ) {
335  continue;
336  }
337 
338  # Don't use anon only blocks on users
339  if ( $specificType == self::TYPE_USER && !$block->isHardblock() ) {
340  continue;
341  }
342 
343  // Check for duplicate autoblocks
344  if ( $block->getType() === self::TYPE_AUTO ) {
345  $autoBlocks[] = $block;
346  } else {
347  $blocks[] = $block;
348  $blockIds[] = $block->getId();
349  }
350  }
351 
352  // Only add autoblocks that aren't duplicates
353  foreach ( $autoBlocks as $block ) {
354  if ( !in_array( $block->mParentBlockId, $blockIds ) ) {
355  $blocks[] = $block;
356  }
357  }
358 
359  return $blocks;
360  }
361 
372  protected static function chooseMostSpecificBlock( array $blocks ) {
373  if ( count( $blocks ) === 1 ) {
374  return $blocks[0];
375  }
376 
377  # This result could contain a block on the user, a block on the IP, and a russian-doll
378  # set of rangeblocks. We want to choose the most specific one, so keep a leader board.
379  $bestBlock = null;
380 
381  # Lower will be better
382  $bestBlockScore = 100;
383  foreach ( $blocks as $block ) {
384  if ( $block->getType() == self::TYPE_RANGE ) {
385  # This is the number of bits that are allowed to vary in the block, give
386  # or take some floating point errors
387  $target = $block->getTargetName();
388  $max = IPUtils::isIPv6( $target ) ? 128 : 32;
389  list( $network, $bits ) = IPUtils::parseCIDR( $target );
390  $size = $max - $bits;
391 
392  # Rank a range block covering a single IP equally with a single-IP block
393  $score = self::TYPE_RANGE - 1 + ( $size / $max );
394 
395  } else {
396  $score = $block->getType();
397  }
398 
399  if ( $score < $bestBlockScore ) {
400  $bestBlockScore = $score;
401  $bestBlock = $block;
402  }
403  }
404 
405  return $bestBlock;
406  }
407 
414  public static function getRangeCond( $start, $end = null ) {
415  if ( $end === null ) {
416  $end = $start;
417  }
418  # Per T16634, we want to include relevant active rangeblocks; for
419  # rangeblocks, we want to include larger ranges which enclose the given
420  # range. We know that all blocks must be smaller than $wgBlockCIDRLimit,
421  # so we can improve performance by filtering on a LIKE clause
422  $chunk = self::getIpFragment( $start );
423  $dbr = wfGetDB( DB_REPLICA );
424  $like = $dbr->buildLike( $chunk, $dbr->anyString() );
425 
426  # Fairly hard to make a malicious SQL statement out of hex characters,
427  # but stranger things have happened...
428  $safeStart = $dbr->addQuotes( $start );
429  $safeEnd = $dbr->addQuotes( $end );
430 
431  return $dbr->makeList(
432  [
433  "ipb_range_start $like",
434  "ipb_range_start <= $safeStart",
435  "ipb_range_end >= $safeEnd",
436  ],
437  LIST_AND
438  );
439  }
440 
447  protected static function getIpFragment( $hex ) {
448  global $wgBlockCIDRLimit;
449  if ( substr( $hex, 0, 3 ) == 'v6-' ) {
450  return 'v6-' . substr( substr( $hex, 3 ), 0, floor( $wgBlockCIDRLimit['IPv6'] / 4 ) );
451  } else {
452  return substr( $hex, 0, floor( $wgBlockCIDRLimit['IPv4'] / 4 ) );
453  }
454  }
455 
461  protected function initFromRow( $row ) {
462  $this->setTarget( $row->ipb_address );
463 
464  $this->setTimestamp( wfTimestamp( TS_MW, $row->ipb_timestamp ) );
465  $this->mAuto = (bool)$row->ipb_auto;
466  $this->setHideName( (bool)$row->ipb_deleted );
467  $this->mId = (int)$row->ipb_id;
468  $this->mParentBlockId = (int)$row->ipb_parent_block_id;
469 
471  ->getActorNormalization()
472  ->newActorFromRowFields( $row->ipb_by, $row->ipb_by_text, $row->ipb_by_actor ) );
473 
474  // I wish I didn't have to do this
475  $db = wfGetDB( DB_REPLICA );
476  $this->setExpiry( $db->decodeExpiry( $row->ipb_expiry ) );
477  $this->setReason(
479  // Legacy because $row may have come from self::selectFields()
480  ->getCommentLegacy( $db, 'ipb_reason', $row )
481  );
482 
483  $this->isHardblock( !$row->ipb_anon_only );
484  $this->isAutoblocking( (bool)$row->ipb_enable_autoblock );
485  $this->isSitewide( (bool)$row->ipb_sitewide );
486 
487  $this->isCreateAccountBlocked( (bool)$row->ipb_create_account );
488  $this->isEmailBlocked( (bool)$row->ipb_block_email );
489  $this->isUsertalkEditAllowed( (bool)$row->ipb_allow_usertalk );
490  }
491 
497  public static function newFromRow( $row ) {
498  $block = new DatabaseBlock;
499  $block->initFromRow( $row );
500  return $block;
501  }
502 
510  public function delete() {
512  ->getDatabaseBlockStore()
513  ->deleteBlock( $this );
514  }
515 
525  public function insert( IDatabase $dbw = null ) {
527  ->getDatabaseBlockStore()
528  ->insertBlock( $this, $dbw );
529  }
530 
539  public function update() {
541  ->getDatabaseBlockStore()
542  ->updateBlock( $this );
543  }
544 
553  public static function isWhitelistedFromAutoblocks( $ip ) {
554  // Hard-deprecated since MW 1.37.
555  wfDeprecated( __METHOD__, '1.36' );
556  return self::isExemptedFromAutoblocks( $ip );
557  }
558 
568  public static function isExemptedFromAutoblocks( $ip ) {
569  // Try to get the ip-autoblock_exemption from the cache, as it's faster
570  // than getting the msg raw and explode()'ing it.
571  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
572  $lines = $cache->getWithSetCallback(
573  $cache->makeKey( 'ip-autoblock', 'exemption' ),
574  $cache::TTL_DAY,
575  static function ( $curValue, &$ttl, array &$setOpts ) {
577 
578  return explode( "\n",
579  wfMessage( 'block-autoblock-exemptionlist' )->inContentLanguage()->plain()
580  );
581  }
582  );
583 
584  wfDebug( "Checking the autoblock exemption list.." );
585 
586  foreach ( $lines as $line ) {
587  # List items only
588  if ( substr( $line, 0, 1 ) !== '*' ) {
589  continue;
590  }
591 
592  $wlEntry = substr( $line, 1 );
593  $wlEntry = trim( $wlEntry );
594 
595  wfDebug( "Checking $ip against $wlEntry..." );
596 
597  # Is the IP in this range?
598  if ( IPUtils::isInRange( $ip, $wlEntry ) ) {
599  wfDebug( " IP $ip matches $wlEntry, not autoblocking" );
600  return true;
601  } else {
602  wfDebug( " No match" );
603  }
604  }
605 
606  return false;
607  }
608 
615  public function doAutoblock( $autoblockIP ) {
616  # If autoblocks are disabled, go away.
617  if ( !$this->isAutoblocking() ) {
618  return false;
619  }
620 
621  # Check if autoblock exempt.
622  if ( self::isExemptedFromAutoblocks( $autoblockIP ) ) {
623  return false;
624  }
625 
626  # Allow hooks to cancel the autoblock.
627  if ( !Hooks::runner()->onAbortAutoblock( $autoblockIP, $this ) ) {
628  wfDebug( "Autoblock aborted by hook." );
629  return false;
630  }
631 
632  # It's okay to autoblock. Go ahead and insert/update the block...
633 
634  # Do not add a *new* block if the IP is already blocked.
635  $ipblock = self::newFromTarget( $autoblockIP );
636  if ( $ipblock ) {
637  # Check if the block is an autoblock and would exceed the user block
638  # if renewed. If so, do nothing, otherwise prolong the block time...
639  if ( $ipblock->mAuto && // @todo Why not compare $ipblock->mExpiry?
640  $this->getExpiry() > self::getAutoblockExpiry( $ipblock->getTimestamp() )
641  ) {
642  # Reset block timestamp to now and its expiry to
643  # $wgAutoblockExpiry in the future
644  $ipblock->updateTimestamp();
645  }
646  return false;
647  }
648 
649  # Make a new block object with the desired properties.
650  $autoblock = new DatabaseBlock;
651  wfDebug( "Autoblocking {$this->getTargetName()}@" . $autoblockIP );
652  $autoblock->setTarget( $autoblockIP );
653  $autoblock->setBlocker( $this->getBlocker() );
654  $autoblock->setReason(
655  wfMessage(
656  'autoblocker',
657  $this->getTargetName(),
658  $this->getReasonComment()->text
659  )->inContentLanguage()->plain()
660  );
661  $timestamp = wfTimestampNow();
662  $autoblock->setTimestamp( $timestamp );
663  $autoblock->mAuto = true;
664  $autoblock->isCreateAccountBlocked( $this->isCreateAccountBlocked() );
665  # Continue suppressing the name if needed
666  $autoblock->setHideName( $this->getHideName() );
667  $autoblock->isUsertalkEditAllowed( $this->isUsertalkEditAllowed() );
668  $autoblock->mParentBlockId = $this->mId;
669  $autoblock->isSitewide( $this->isSitewide() );
670  $autoblock->setRestrictions( $this->getRestrictions() );
671 
672  if ( $this->getExpiry() == 'infinity' ) {
673  # Original block was indefinite, start an autoblock now
674  $autoblock->setExpiry( self::getAutoblockExpiry( $timestamp ) );
675  } else {
676  # If the user is already blocked with an expiry date, we don't
677  # want to pile on top of that.
678  $autoblock->setExpiry( min( $this->getExpiry(), self::getAutoblockExpiry( $timestamp ) ) );
679  }
680 
681  # Insert the block...
682  $status = MediaWikiServices::getInstance()->getDatabaseBlockStore()->insertBlock(
683  $autoblock
684  );
685  return $status
686  ? $status['id']
687  : false;
688  }
689 
694  public function isExpired() {
695  $timestamp = wfTimestampNow();
696  wfDebug( __METHOD__ . " checking current " . $timestamp . " vs $this->mExpiry" );
697 
698  if ( !$this->getExpiry() ) {
699  return false;
700  } else {
701  return $timestamp > $this->getExpiry();
702  }
703  }
704 
708  public function updateTimestamp() {
709  if ( $this->mAuto ) {
710  $this->setTimestamp( wfTimestamp() );
711  $this->setExpiry( self::getAutoblockExpiry( $this->getTimestamp() ) );
712 
713  $dbw = wfGetDB( DB_PRIMARY );
714  $dbw->update( 'ipblocks',
715  [ /* SET */
716  'ipb_timestamp' => $dbw->timestamp( $this->getTimestamp() ),
717  'ipb_expiry' => $dbw->timestamp( $this->getExpiry() ),
718  ],
719  [ /* WHERE */
720  'ipb_id' => $this->getId(),
721  ],
722  __METHOD__
723  );
724  }
725  }
726 
732  public function getRangeStart() {
733  switch ( $this->type ) {
734  case self::TYPE_USER:
735  return '';
736  case self::TYPE_IP:
737  return IPUtils::toHex( $this->target );
738  case self::TYPE_RANGE:
739  list( $start, /*...*/ ) = IPUtils::parseRange( $this->target );
740  return $start;
741  default:
742  throw new MWException( "Block with invalid type" );
743  }
744  }
745 
751  public function getRangeEnd() {
752  switch ( $this->type ) {
753  case self::TYPE_USER:
754  return '';
755  case self::TYPE_IP:
756  return IPUtils::toHex( $this->target );
757  case self::TYPE_RANGE:
758  list( /*...*/, $end ) = IPUtils::parseRange( $this->target );
759  return $end;
760  default:
761  throw new MWException( "Block with invalid type" );
762  }
763  }
764 
769  public function getReason() {
770  if ( $this->getType() === self::TYPE_AUTO ) {
771  return $this->reason->message->inContentLanguage()->plain();
772  }
773  return $this->reason->text;
774  }
775 
779  public function getId(): ?int {
780  return $this->mId;
781  }
782 
790  public function setId( $blockId ) {
791  $this->mId = (int)$blockId;
792 
793  if ( is_array( $this->restrictions ) ) {
794  $this->restrictions = $this->getBlockRestrictionStore()->setBlockId(
795  $blockId, $this->restrictions
796  );
797  }
798 
799  return $this;
800  }
801 
806  public function getParentBlockId() {
807  return $this->mParentBlockId;
808  }
809 
815  public function isHardblock( $x = null ): bool {
816  wfSetVar( $this->isHardblock, $x );
817 
818  # You can't *not* hardblock a user
819  return $this->getType() == self::TYPE_USER
820  ? true
822  }
823 
828  public function isAutoblocking( $x = null ) {
829  wfSetVar( $this->isAutoblocking, $x );
830 
831  # You can't put an autoblock on an IP or range as we don't have any history to
832  # look over to get more IPs from
833  return $this->getType() == self::TYPE_USER
834  ? $this->isAutoblocking
835  : false;
836  }
837 
842  public function getRedactedName() {
843  if ( $this->mAuto ) {
844  return Html::element(
845  'span',
846  [ 'class' => 'mw-autoblockid' ],
847  wfMessage( 'autoblockid', $this->mId )->text()
848  );
849  } else {
850  return htmlspecialchars( $this->getTargetName() );
851  }
852  }
853 
860  public static function getAutoblockExpiry( $timestamp ) {
861  global $wgAutoblockExpiry;
862 
863  return wfTimestamp( TS_MW, (int)wfTimestamp( TS_UNIX, $timestamp ) + $wgAutoblockExpiry );
864  }
865 
871  public static function purgeExpired() {
872  MediaWikiServices::getInstance()->getDatabaseBlockStore()->purgeExpiredBlocks();
873  }
874 
895  public static function newFromTarget( $specificTarget, $vagueTarget = null, $fromPrimary = false ) {
896  $blocks = self::newListFromTarget( $specificTarget, $vagueTarget, $fromPrimary );
897  return self::chooseMostSpecificBlock( $blocks );
898  }
899 
909  public static function newListFromTarget(
910  $specificTarget,
911  $vagueTarget = null,
912  $fromPrimary = false
913  ) {
915  ->getBlockUtils()
916  ->parseBlockTarget( $specificTarget );
917  if ( $type == self::TYPE_ID || $type == self::TYPE_AUTO ) {
918  $block = self::newFromID( $target );
919  return $block ? [ $block ] : [];
920  } elseif ( $target === null && $vagueTarget == '' ) {
921  # We're not going to find anything useful here
922  # Be aware that the == '' check is explicit, since empty values will be
923  # passed by some callers (T31116)
924  return [];
925  } elseif ( in_array(
926  $type,
927  [ self::TYPE_USER, self::TYPE_IP, self::TYPE_RANGE, null ] )
928  ) {
929  return self::newLoad( $target, $type, $fromPrimary, $vagueTarget );
930  }
931  return [];
932  }
933 
944  public static function getBlocksForIPList( array $ipChain, $isAnon, $fromPrimary = false ) {
945  if ( $ipChain === [] ) {
946  return [];
947  }
948 
949  $conds = [];
950  $proxyLookup = MediaWikiServices::getInstance()->getProxyLookup();
951  foreach ( array_unique( $ipChain ) as $ipaddr ) {
952  # Discard invalid IP addresses. Since XFF can be spoofed and we do not
953  # necessarily trust the header given to us, make sure that we are only
954  # checking for blocks on well-formatted IP addresses (IPv4 and IPv6).
955  # Do not treat private IP spaces as special as it may be desirable for wikis
956  # to block those IP ranges in order to stop misbehaving proxies that spoof XFF.
957  if ( !IPUtils::isValid( $ipaddr ) ) {
958  continue;
959  }
960  # Don't check trusted IPs (includes local CDNs which will be in every request)
961  if ( $proxyLookup->isTrustedProxy( $ipaddr ) ) {
962  continue;
963  }
964  # Check both the original IP (to check against single blocks), as well as build
965  # the clause to check for rangeblocks for the given IP.
966  $conds['ipb_address'][] = $ipaddr;
967  $conds[] = self::getRangeCond( IPUtils::toHex( $ipaddr ) );
968  }
969 
970  if ( $conds === [] ) {
971  return [];
972  }
973 
974  if ( $fromPrimary ) {
975  $db = wfGetDB( DB_PRIMARY );
976  } else {
977  $db = wfGetDB( DB_REPLICA );
978  }
979  $conds = $db->makeList( $conds, LIST_OR );
980  if ( !$isAnon ) {
981  $conds = [ $conds, 'ipb_anon_only' => 0 ];
982  }
983  $blockQuery = self::getQueryInfo();
984  $rows = $db->select(
985  $blockQuery['tables'],
986  array_merge( [ 'ipb_range_start', 'ipb_range_end' ], $blockQuery['fields'] ),
987  $conds,
988  __METHOD__,
989  [],
990  $blockQuery['joins']
991  );
992 
993  $blocks = [];
994  foreach ( $rows as $row ) {
995  $block = self::newFromRow( $row );
996  if ( !$block->isExpired() ) {
997  $blocks[] = $block;
998  }
999  }
1000 
1001  return $blocks;
1002  }
1003 
1010  public function getType(): ?int {
1011  return $this->mAuto
1012  ? self::TYPE_AUTO
1013  : parent::getType();
1014  }
1015 
1019  public function getIdentifier() {
1020  return $this->getId();
1021  }
1022 
1030  public function getRestrictions() {
1031  if ( $this->restrictions === null ) {
1032  // If the block id has not been set, then do not attempt to load the
1033  // restrictions.
1034  if ( !$this->mId ) {
1035  return [];
1036  }
1037  $this->restrictions = $this->getBlockRestrictionStore()->loadByBlockId( $this->mId );
1038  }
1039 
1040  return $this->restrictions;
1041  }
1042 
1049  public function getRawRestrictions(): ?array {
1050  return $this->restrictions;
1051  }
1052 
1058  public function setRestrictions( array $restrictions ) {
1059  $this->restrictions = array_filter( $restrictions, static function ( $restriction ) {
1060  return $restriction instanceof Restriction;
1061  } );
1062 
1063  return $this;
1064  }
1065 
1069  public function appliesToTitle( Title $title ) {
1070  if ( $this->isSitewide() ) {
1071  return true;
1072  }
1073 
1074  $restrictions = $this->getRestrictions();
1075  foreach ( $restrictions as $restriction ) {
1076  if ( $restriction->matches( $title ) ) {
1077  return true;
1078  }
1079  }
1080 
1081  return false;
1082  }
1083 
1087  public function appliesToNamespace( $ns ) {
1088  if ( $this->isSitewide() ) {
1089  return true;
1090  }
1091 
1092  // Blocks do not apply to virtual namespaces.
1093  if ( $ns < 0 ) {
1094  return false;
1095  }
1096 
1097  $restriction = $this->findRestriction( NamespaceRestriction::TYPE, $ns );
1098 
1099  return (bool)$restriction;
1100  }
1101 
1105  public function appliesToPage( $pageId ) {
1106  if ( $this->isSitewide() ) {
1107  return true;
1108  }
1109 
1110  // If the pageId is not over zero, the block cannot apply to it.
1111  if ( $pageId <= 0 ) {
1112  return false;
1113  }
1114 
1115  $restriction = $this->findRestriction( PageRestriction::TYPE, $pageId );
1116 
1117  return (bool)$restriction;
1118  }
1119 
1123  public function appliesToRight( $right ) {
1124  // Temporarily access service container until the feature flag is removed: T280532
1125  $config = MediaWikiServices::getInstance()->getMainConfig();
1126 
1127  $res = parent::appliesToRight( $right );
1128 
1129  if ( !$res && $config->get( 'EnablePartialActionBlocks' ) ) {
1130  $blockActions = MediaWikiServices::getInstance()->getBlockActionInfo()
1131  ->getAllBlockActions();
1132 
1133  if ( isset( $blockActions[$right] ) ) {
1134  $restriction = $this->findRestriction(
1136  $blockActions[$right]
1137  );
1138 
1139  // $res may be null or false. This should be preserved if there is no restriction.
1140  if ( $restriction ) {
1141  $res = true;
1142  }
1143  }
1144  }
1145 
1146  return $res;
1147  }
1148 
1156  private function findRestriction( $type, $value ) {
1157  $restrictions = $this->getRestrictions();
1158  foreach ( $restrictions as $restriction ) {
1159  if ( $restriction->getType() !== $type ) {
1160  continue;
1161  }
1162 
1163  if ( $restriction->getValue() === $value ) {
1164  return $restriction;
1165  }
1166  }
1167 
1168  return null;
1169  }
1170 
1177  return MediaWikiServices::getInstance()->getBlockRestrictionStore();
1178  }
1179 
1183  public function getBy() {
1184  return ( $this->blocker ) ? $this->blocker->getId() : 0;
1185  }
1186 
1190  public function getByName() {
1191  return ( $this->blocker ) ? $this->blocker->getName() : '';
1192  }
1193 
1199  public function getBlocker(): ?UserIdentity {
1200  return $this->blocker;
1201  }
1202 
1209  public function setBlocker( $user ) {
1210  if ( is_string( $user ) ) {
1211  wfDeprecatedMsg( 'Calling ' . __METHOD__ . ' with string as $user', '1.36' );
1213  ->getUserFactory()
1214  ->newFromName( $user, UserFactory::RIGOR_NONE );
1215  }
1216 
1217  if ( !$user->isRegistered() &&
1218  MediaWikiServices::getInstance()->getUserNameUtils()->isUsable( $user->getName() )
1219  ) {
1220  // Temporarily log some block details to debug T192964
1221  $logger = LoggerFactory::getInstance( 'BlockManager' );
1222  $logger->warning(
1223  'Blocker is neither a local user nor an invalid username',
1224  [
1225  'blocker' => (string)$user,
1226  'blockId' => $this->getId(),
1227  ]
1228  );
1229  throw new \InvalidArgumentException(
1230  'Blocker must be a local user or a name that cannot be a local user'
1231  );
1232  }
1233 
1234  $this->blocker = $user;
1235  }
1236 }
1237 
1241 class_alias( DatabaseBlock::class, 'Block' );
LIST_OR
const LIST_OR
Definition: Defines.php:46
MediaWiki\User\UserIdentityValue
Value object representing a user's identity.
Definition: UserIdentityValue.php:35
MediaWiki\Block\DatabaseBlock\setBlocker
setBlocker( $user)
Set the user who implemented (or will implement) this block.
Definition: DatabaseBlock.php:1209
MediaWiki\Block\DatabaseBlock\getIpFragment
static getIpFragment( $hex)
Get the component of an IP address which is certain to be the same between an IP address and a rangeb...
Definition: DatabaseBlock.php:447
Wikimedia\Rdbms\Database
Relational database abstraction object.
Definition: Database.php:52
MediaWiki\Block\DatabaseBlock\equals
equals(DatabaseBlock $block)
Check if two blocks are effectively equal.
Definition: DatabaseBlock.php:237
MediaWiki\Block
Definition: AbstractBlock.php:21
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:186
MediaWiki\Block\AbstractBlock\getTargetName
getTargetName()
Definition: AbstractBlock.php:359
MediaWiki\Block\DatabaseBlock\initFromRow
initFromRow( $row)
Given a database row from the ipblocks table, initialize member variables.
Definition: DatabaseBlock.php:461
wfSetVar
wfSetVar(&$dest, $source, $force=false)
Sets dest to source and returns the original value of dest If source is NULL, it just returns the val...
Definition: GlobalFunctions.php:1512
MediaWiki\Logger\LoggerFactory\getInstance
static getInstance( $channel)
Get a named logger instance from the currently configured logger factory.
Definition: LoggerFactory.php:92
MediaWiki\Block\DatabaseBlock\getRangeStart
getRangeStart()
Get the IP address at the start of the range in Hex form.
Definition: DatabaseBlock.php:732
MediaWiki\Block\DatabaseBlock\$mParentBlockId
int $mParentBlockId
Definition: DatabaseBlock.php:63
true
return true
Definition: router.php:90
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:1692
$wgAutoblockExpiry
$wgAutoblockExpiry
Number of seconds before autoblock entries expire.
Definition: DefaultSettings.php:5832
LIST_AND
const LIST_AND
Definition: Defines.php:43
MediaWiki\Block\DatabaseBlock\getType
getType()
Get the type of target for this particular block.int|null AbstractBlock::TYPE_ constant,...
Definition: DatabaseBlock.php:1010
MediaWiki\Block\Restriction\NamespaceRestriction\TYPE
const TYPE
Definition: NamespaceRestriction.php:30
MediaWiki\Block\AbstractBlock\isCreateAccountBlocked
isCreateAccountBlocked( $x=null)
Get or set the flag indicating whether this block blocks the target from creating an account.
Definition: AbstractBlock.php:210
MediaWiki\Block\DatabaseBlock\getBlocker
getBlocker()
Get the user who implemented this block.
Definition: DatabaseBlock.php:1199
MediaWiki\Block\DatabaseBlock\__construct
__construct(array $options=[])
Create a new block with specified option parameters on a user, IP or IP range.
Definition: DatabaseBlock.php:102
CommentStore
Handle database storage of comments such as edit summaries and log reasons.
Definition: CommentStore.php:42
MediaWiki\Block\DatabaseBlock\appliesToNamespace
appliesToNamespace( $ns)
Checks if a block applies to a particular namespace.1.33bool
Definition: DatabaseBlock.php:1087
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1182
MediaWiki\Block\DatabaseBlock\getByName
getByName()
Get the username of the blocking sysop.string
Definition: DatabaseBlock.php:1190
MediaWiki\Block\DatabaseBlock\$mFromPrimary
bool $mFromPrimary
Definition: DatabaseBlock.php:69
$res
$res
Definition: testCompression.php:57
MediaWiki\Block\DatabaseBlock\getRawRestrictions
getRawRestrictions()
Get restrictions without loading from database if not yet loaded.
Definition: DatabaseBlock.php:1049
MediaWiki\User\UserIdentity
Interface for objects representing user identity.
Definition: UserIdentity.php:39
Wikimedia\Rdbms\IDatabase
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:38
$dbr
$dbr
Definition: testCompression.php:54
MediaWiki\MediaWikiServices\getInstance
static getInstance()
Returns the global default instance of the top level service locator.
Definition: MediaWikiServices.php:247
MediaWiki\Block\DatabaseBlock\appliesToPage
appliesToPage( $pageId)
Checks if a block applies to a particular page.This check does not consider whether $this->isUsertalk...
Definition: DatabaseBlock.php:1105
MediaWiki\Block\DatabaseBlock
A DatabaseBlock (unlike a SystemBlock) is stored in the database, may give rise to autoblocks and may...
Definition: DatabaseBlock.php:52
MediaWiki\Block\DatabaseBlock\setRestrictions
setRestrictions(array $restrictions)
Definition: DatabaseBlock.php:1058
wfDeprecatedMsg
wfDeprecatedMsg( $msg, $version=false, $component=false, $callerOffset=2)
Log a deprecation warning with arbitrary message text.
Definition: GlobalFunctions.php:1028
MWException
MediaWiki exception.
Definition: MWException.php:29
Wikimedia\Rdbms\Database\getCacheSetOptions
static getCacheSetOptions(?IDatabase ... $dbs)
Merge the result of getSessionLagStatus() for several DBs using the most pessimistic values to estima...
Definition: Database.php:5143
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that $function is deprecated.
Definition: GlobalFunctions.php:996
MediaWiki\Logger\LoggerFactory
PSR-3 logger instance factory.
Definition: LoggerFactory.php:45
Config\get
get( $name)
Get a configuration variable such as "Sitename" or "UploadMaintenance.".
MediaWiki\Block\DatabaseBlock\purgeExpired
static purgeExpired()
Purge expired blocks from the ipblocks table.
Definition: DatabaseBlock.php:871
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2226
MediaWiki\Block\Block\isSitewide
isSitewide()
Indicates that the block is a sitewide block.
MediaWiki\Block\AbstractBlock\isEmailBlocked
isEmailBlocked( $x=null)
Get or set the flag indicating whether this block blocks the target from sending emails.
Definition: AbstractBlock.php:223
MediaWiki\Block\DatabaseBlock\newFromRow
static newFromRow( $row)
Create a new DatabaseBlock object from a database row.
Definition: DatabaseBlock.php:497
MediaWiki\Block\Restriction\Restriction
Definition: Restriction.php:25
MediaWiki\Block\AbstractBlock\getReasonComment
getReasonComment()
Get the reason for creating the block.
Definition: AbstractBlock.php:154
MediaWiki\Block\Restriction\PageRestriction\TYPE
const TYPE
Definition: PageRestriction.php:30
MediaWiki\Block\DatabaseBlock\isWhitelistedFromAutoblocks
static isWhitelistedFromAutoblocks( $ip)
Checks whether a given IP is on the autoblock exemption list.
Definition: DatabaseBlock.php:553
MediaWiki\Block\DatabaseBlock\getRangeEnd
getRangeEnd()
Get the IP address at the end of the range in Hex form.
Definition: DatabaseBlock.php:751
$title
$title
Definition: testCompression.php:38
MediaWiki\Block\Block\TYPE_RANGE
const TYPE_RANGE
Definition: Block.php:43
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
wfTimestampNow
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
Definition: GlobalFunctions.php:1721
MediaWiki\Block\DatabaseBlock\isExpired
isExpired()
Has the block expired?
Definition: DatabaseBlock.php:694
MediaWiki\Block\AbstractBlock\isSitewide
isSitewide( $x=null)
Indicates that the block is a sitewide block.
Definition: AbstractBlock.php:197
wfDebug
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:894
MediaWiki\Block\AbstractBlock\$isHardblock
bool $isHardblock
Definition: AbstractBlock.php:69
MediaWiki\Block\DatabaseBlock\getBlocksForIPList
static getBlocksForIPList(array $ipChain, $isAnon, $fromPrimary=false)
Get all blocks that match any IP from an array of IP addresses.
Definition: DatabaseBlock.php:944
MediaWiki\Block\DatabaseBlock\getRangeCond
static getRangeCond( $start, $end=null)
Get a set of SQL conditions which will select rangeblocks encompassing a given range.
Definition: DatabaseBlock.php:414
MediaWiki\Block\DatabaseBlock\getBlockRestrictionStore
getBlockRestrictionStore()
Get a BlockRestrictionStore instance.
Definition: DatabaseBlock.php:1176
MediaWiki\Block\DatabaseBlock\getRedactedName
getRedactedName()
Get the block name, but with autoblocked IPs hidden as per standard privacy policy.
Definition: DatabaseBlock.php:842
MediaWiki\Block\DatabaseBlock\isAutoblocking
isAutoblocking( $x=null)
Definition: DatabaseBlock.php:828
MediaWiki\Block\AbstractBlock\setTimestamp
setTimestamp( $timestamp)
Set the timestamp indicating when the block was created.
Definition: AbstractBlock.php:416
MediaWiki\Block\DatabaseBlock\getBy
getBy()
Get the user id of the blocking sysop.int (0 for foreign users)
Definition: DatabaseBlock.php:1183
MediaWiki\Block\Block\isHardblock
isHardblock()
Returns whether the block is a hardblock (affects logged-in users on a given IP/range)
MediaWiki\Block\DatabaseBlock\newFromTarget
static newFromTarget( $specificTarget, $vagueTarget=null, $fromPrimary=false)
Given a target and the target's type, get an existing block object if possible.
Definition: DatabaseBlock.php:895
MediaWiki\Block\DatabaseBlock\getIdentifier
getIdentifier()
Get the information that identifies this block, such that a user could look up everything that can be...
Definition: DatabaseBlock.php:1019
MediaWiki\Block\DatabaseBlock\getParentBlockId
getParentBlockId()
Definition: DatabaseBlock.php:806
MediaWiki\Block\DatabaseBlock\$isAutoblocking
bool $isAutoblocking
Definition: DatabaseBlock.php:72
DB_PRIMARY
const DB_PRIMARY
Definition: defines.php:27
MediaWiki\Block\DatabaseBlock\getReason
getReason()
Get the reason given for creating the block, as a string.Deprecated, since this gives the caller no c...
Definition: DatabaseBlock.php:769
Hooks\runner
static runner()
Get a HookRunner instance for calling hooks using the new interfaces.
Definition: Hooks.php:173
$line
$line
Definition: mcc.php:119
MediaWiki\Block\DatabaseBlock\isExemptedFromAutoblocks
static isExemptedFromAutoblocks( $ip)
Checks whether a given IP is on the autoblock exemption list.
Definition: DatabaseBlock.php:568
MediaWiki\Block\AbstractBlock\$target
UserIdentity string null $target
Definition: AbstractBlock.php:72
MediaWiki\Block\AbstractBlock\setExpiry
setExpiry( $expiry)
Set the block expiry time.
Definition: AbstractBlock.php:395
MediaWiki\Block\AbstractBlock\getExpiry
getExpiry()
Get the block expiry time.
Definition: AbstractBlock.php:385
MediaWiki\Block\Block\TYPE_AUTO
const TYPE_AUTO
Definition: Block.php:44
MediaWiki\Block\Block\TYPE_USER
const TYPE_USER
Definition: Block.php:41
MediaWiki\Block\DatabaseBlock\update
update()
Update a block in the DB with new parameters.
Definition: DatabaseBlock.php:539
$lines
if(!file_exists( $CREDITS)) $lines
Definition: updateCredits.php:45
MediaWiki\Block\Restriction\NamespaceRestriction
Definition: NamespaceRestriction.php:25
MediaWiki\Block\AbstractBlock\getTimestamp
getTimestamp()
Get the timestamp indicating when the block was created.
Definition: AbstractBlock.php:406
MediaWiki\Block\DatabaseBlock\getId
getId()
Get the block ID.int|null
Definition: DatabaseBlock.php:779
Title
Represents a title within MediaWiki.
Definition: Title.php:49
MediaWiki\Block\Block\isCreateAccountBlocked
isCreateAccountBlocked()
Get the flag indicating whether this block blocks the target from creating an account.
$wgBlockCIDRLimit
$wgBlockCIDRLimit
Limits on the possible sizes of range blocks.
Definition: DefaultSettings.php:5857
MediaWiki\Block\DatabaseBlock\setId
setId( $blockId)
Set the block ID.
Definition: DatabaseBlock.php:790
$cache
$cache
Definition: mcc.php:33
MediaWiki\Block\DatabaseBlock\chooseMostSpecificBlock
static chooseMostSpecificBlock(array $blocks)
Choose the most specific block from some combination of user, IP and IP range blocks.
Definition: DatabaseBlock.php:372
MediaWiki\Block\Restriction\PageRestriction
Definition: PageRestriction.php:25
MediaWiki\Block\AbstractBlock\setReason
setReason( $reason)
Set the reason for creating the block.
Definition: AbstractBlock.php:164
MediaWiki\Block\DatabaseBlock\getRestrictions
getRestrictions()
Getting the restrictions will perform a database query if the restrictions are not already loaded.
Definition: DatabaseBlock.php:1030
MediaWiki\$config
Config $config
Definition: MediaWiki.php:42
MediaWiki\Block\DatabaseBlock\newListFromTarget
static newListFromTarget( $specificTarget, $vagueTarget=null, $fromPrimary=false)
This is similar to DatabaseBlock::newFromTarget, but it returns all the relevant blocks.
Definition: DatabaseBlock.php:909
MediaWiki\Block\DatabaseBlock\updateTimestamp
updateTimestamp()
Update the timestamp on autoblocks.
Definition: DatabaseBlock.php:708
MediaWiki\Block\DatabaseBlock\getQueryInfo
static getQueryInfo()
Return the tables, fields, and join conditions to be selected to create a new block object.
Definition: DatabaseBlock.php:199
MediaWiki\Block\DatabaseBlock\doAutoblock
doAutoblock( $autoblockIP)
Autoblocks the given IP, referring to this block.
Definition: DatabaseBlock.php:615
MediaWiki\Block\DatabaseBlock\findRestriction
findRestriction( $type, $value)
Find Restriction by type and value.
Definition: DatabaseBlock.php:1156
MediaWiki\Block\DatabaseBlock\getAutoblockExpiry
static getAutoblockExpiry( $timestamp)
Get a timestamp of the expiry for autoblocks.
Definition: DatabaseBlock.php:860
MediaWiki\Block\AbstractBlock\getHideName
getHideName()
Get whether the block hides the target's username.
Definition: AbstractBlock.php:174
MediaWiki\Block\Restriction\ActionRestriction\TYPE
const TYPE
Definition: ActionRestriction.php:35
MediaWiki\Block\AbstractBlock\$type
int null $type
AbstractBlock::TYPE_ constant.
Definition: AbstractBlock.php:78
MediaWiki\Block\AbstractBlock
Definition: AbstractBlock.php:37
MediaWiki\Block\DatabaseBlock\appliesToRight
appliesToRight( $right)
Determine whether the block prevents a given right.A right may be allowed or disallowed by default,...
Definition: DatabaseBlock.php:1123
Html\element
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:234
MediaWiki\Block\DatabaseBlock\$mAuto
bool $mAuto
Definition: DatabaseBlock.php:57
MediaWiki\Block\AbstractBlock\setTarget
setTarget( $target)
Set the target for this block, and update $this->type accordingly.
Definition: AbstractBlock.php:425
CommentStore\getStore
static getStore()
Definition: CommentStore.php:120
MediaWiki\Block\DatabaseBlock\insert
insert(IDatabase $dbw=null)
Insert a block into the block table.
Definition: DatabaseBlock.php:525
MediaWiki\Block\Restriction\ActionRestriction
Restriction for partial blocks of actions.
Definition: ActionRestriction.php:30
MediaWiki\Block\DatabaseBlock\appliesToTitle
appliesToTitle(Title $title)
Checks if a block applies to a particular title.This check does not consider whether $this->isUsertal...
Definition: DatabaseBlock.php:1069
MediaWiki\Block\DatabaseBlock\$blocker
UserIdentity null $blocker
Definition: DatabaseBlock.php:78
MediaWiki\Block\DatabaseBlock\isHardblock
isHardblock( $x=null)
Get/set whether the block is a hardblock (affects logged-in users on a given IP/range)
Definition: DatabaseBlock.php:815
MediaWiki\User\UserFactory
Creates User objects.
Definition: UserFactory.php:41
MediaWiki\Block\DatabaseBlock\newLoad
static newLoad( $specificTarget, $specificType, $fromPrimary, $vagueTarget=null)
Load blocks from the database which target the specific target exactly, or which cover the vague targ...
Definition: DatabaseBlock.php:271
MediaWiki\Block\DatabaseBlock\$restrictions
Restriction[] $restrictions
Definition: DatabaseBlock.php:75
MediaWiki\Block\AbstractBlock\isUsertalkEditAllowed
isUsertalkEditAllowed( $x=null)
Get or set the flag indicating whether this block blocks the target from editing their own user talk ...
Definition: AbstractBlock.php:236
MediaWiki\Block\DatabaseBlock\$mId
int $mId
Definition: DatabaseBlock.php:66
MediaWiki\Block\Block\TYPE_IP
const TYPE_IP
Definition: Block.php:42
Hooks
Hooks class.
Definition: Hooks.php:38
MediaWiki\Block\BlockRestrictionStore
Definition: BlockRestrictionStore.php:35
MediaWiki\Block\DatabaseBlock\newFromID
static newFromID( $id)
Load a block from the block id.
Definition: DatabaseBlock.php:167
Html
This class is a collection of static functions that serve two purposes:
Definition: Html.php:49