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;
28 use InvalidArgumentException;
36 use MWException;
37 use stdClass;
38 use Title;
39 use Wikimedia\IPUtils;
42 
56  public $mAuto;
57 
63 
65  private $mId;
66 
68  private $mFromPrimary;
69 
71  private $isAutoblocking;
72 
74  private $restrictions;
75 
77  private $blocker;
78 
98  public function __construct( array $options = [] ) {
99  parent::__construct( $options );
100 
101  $defaults = [
102  'user' => null,
103  'auto' => false,
104  'expiry' => '',
105  'createAccount' => false,
106  'enableAutoblock' => false,
107  'blockEmail' => false,
108  'allowUsertalk' => false,
109  'sitewide' => true,
110  'by' => null,
111  ];
112 
113  $options += $defaults;
114 
115  if ( $options['by'] && $options['by'] instanceof UserIdentity ) {
116  $this->setBlocker( $options['by'] );
117  }
118 
119  $this->setExpiry( $this->getDBConnection( DB_REPLICA )->decodeExpiry( $options['expiry'] ) );
120 
121  # Boolean settings
122  $this->mAuto = (bool)$options['auto'];
123  $this->isAutoblocking( (bool)$options['enableAutoblock'] );
124  $this->isSitewide( (bool)$options['sitewide'] );
125  $this->isEmailBlocked( (bool)$options['blockEmail'] );
126  $this->isCreateAccountBlocked( (bool)$options['createAccount'] );
127  $this->isUsertalkEditAllowed( (bool)$options['allowUsertalk'] );
128 
129  $this->mFromPrimary = false;
130  }
131 
138  public static function newFromID( $id ) {
139  $dbr = wfGetDB( DB_REPLICA );
140  $blockQuery = self::getQueryInfo();
141  $res = $dbr->selectRow(
142  $blockQuery['tables'],
143  $blockQuery['fields'],
144  [ 'ipb_id' => $id ],
145  __METHOD__,
146  [],
147  $blockQuery['joins']
148  );
149  if ( $res ) {
150  return self::newFromRow( $res );
151  } else {
152  return null;
153  }
154  }
155 
170  public static function getQueryInfo() {
171  $commentQuery = CommentStore::getStore()->getJoin( 'ipb_reason' );
172  return [
173  'tables' => [
174  'ipblocks',
175  'ipblocks_actor' => 'actor'
176  ] + $commentQuery['tables'],
177  'fields' => [
178  'ipb_id',
179  'ipb_address',
180  'ipb_timestamp',
181  'ipb_auto',
182  'ipb_anon_only',
183  'ipb_create_account',
184  'ipb_enable_autoblock',
185  'ipb_expiry',
186  'ipb_deleted',
187  'ipb_block_email',
188  'ipb_allow_usertalk',
189  'ipb_parent_block_id',
190  'ipb_sitewide',
191  'ipb_by_actor',
192  'ipb_by' => 'ipblocks_actor.actor_user',
193  'ipb_by_text' => 'ipblocks_actor.actor_name'
194  ] + $commentQuery['fields'],
195  'joins' => [
196  'ipblocks_actor' => [ 'JOIN', 'actor_id=ipb_by_actor' ]
197  ] + $commentQuery['joins'],
198  ];
199  }
200 
208  public function equals( DatabaseBlock $block ) {
209  return (
210  (string)$this->target == (string)$block->target
211  && $this->type == $block->type
212  && $this->mAuto == $block->mAuto
213  && $this->isHardblock() == $block->isHardblock()
214  && $this->isCreateAccountBlocked() == $block->isCreateAccountBlocked()
215  && $this->getExpiry() == $block->getExpiry()
216  && $this->isAutoblocking() == $block->isAutoblocking()
217  && $this->getHideName() == $block->getHideName()
218  && $this->isEmailBlocked() == $block->isEmailBlocked()
219  && $this->isUsertalkEditAllowed() == $block->isUsertalkEditAllowed()
220  && $this->getReasonComment()->text == $block->getReasonComment()->text
221  && $this->isSitewide() == $block->isSitewide()
222  // DatabaseBlock::getRestrictions() may perform a database query, so
223  // keep it at the end.
224  && $this->getBlockRestrictionStore()->equals(
225  $this->getRestrictions(), $block->getRestrictions()
226  )
227  );
228  }
229 
242  protected static function newLoad(
243  $specificTarget,
244  $specificType,
245  $fromPrimary,
246  $vagueTarget = null
247  ) {
248  $db = wfGetDB( $fromPrimary ? DB_PRIMARY : DB_REPLICA );
249 
250  if ( $specificType !== null ) {
251  $conds = [ 'ipb_address' => [ (string)$specificTarget ] ];
252  } else {
253  $conds = [ 'ipb_address' => [] ];
254  }
255 
256  # Be aware that the != '' check is explicit, since empty values will be
257  # passed by some callers (T31116)
258  if ( $vagueTarget != '' ) {
260  ->getBlockUtils()
261  ->parseBlockTarget( $vagueTarget );
262  switch ( $type ) {
263  case self::TYPE_USER:
264  # Slightly weird, but who are we to argue?
265  $conds['ipb_address'][] = (string)$target;
266  $conds = $db->makeList( $conds, LIST_OR );
267  break;
268 
269  case self::TYPE_IP:
270  $conds['ipb_address'][] = (string)$target;
271  $conds['ipb_address'] = array_unique( $conds['ipb_address'] );
272  $conds[] = self::getRangeCond( IPUtils::toHex( $target ) );
273  // @phan-suppress-next-line SecurityCheck-SQLInjection
274  $conds = $db->makeList( $conds, LIST_OR );
275  break;
276 
277  case self::TYPE_RANGE:
278  list( $start, $end ) = IPUtils::parseRange( $target );
279  $conds['ipb_address'][] = (string)$target;
280  $conds[] = self::getRangeCond( $start, $end );
281  // @phan-suppress-next-line SecurityCheck-SQLInjection
282  $conds = $db->makeList( $conds, LIST_OR );
283  break;
284 
285  default:
286  throw new MWException( "Tried to load block with invalid type" );
287  }
288  }
289 
290  $blockQuery = self::getQueryInfo();
291  $res = $db->select(
292  $blockQuery['tables'],
293  $blockQuery['fields'],
294  $conds,
295  __METHOD__,
296  [],
297  $blockQuery['joins']
298  );
299 
300  $blocks = [];
301  $blockIds = [];
302  $autoBlocks = [];
303  foreach ( $res as $row ) {
304  $block = self::newFromRow( $row );
305 
306  # Don't use expired blocks
307  if ( $block->isExpired() ) {
308  continue;
309  }
310 
311  # Don't use anon only blocks on users
312  if ( $specificType == self::TYPE_USER && !$block->isHardblock() ) {
313  continue;
314  }
315 
316  // Check for duplicate autoblocks
317  if ( $block->getType() === self::TYPE_AUTO ) {
318  $autoBlocks[] = $block;
319  } else {
320  $blocks[] = $block;
321  $blockIds[] = $block->getId();
322  }
323  }
324 
325  // Only add autoblocks that aren't duplicates
326  foreach ( $autoBlocks as $block ) {
327  if ( !in_array( $block->mParentBlockId, $blockIds ) ) {
328  $blocks[] = $block;
329  }
330  }
331 
332  return $blocks;
333  }
334 
345  protected static function chooseMostSpecificBlock( array $blocks ) {
346  if ( count( $blocks ) === 1 ) {
347  return $blocks[0];
348  }
349 
350  # This result could contain a block on the user, a block on the IP, and a russian-doll
351  # set of rangeblocks. We want to choose the most specific one, so keep a leader board.
352  $bestBlock = null;
353 
354  # Lower will be better
355  $bestBlockScore = 100;
356  foreach ( $blocks as $block ) {
357  if ( $block->getType() == self::TYPE_RANGE ) {
358  # This is the number of bits that are allowed to vary in the block, give
359  # or take some floating point errors
360  $target = $block->getTargetName();
361  $max = IPUtils::isIPv6( $target ) ? 128 : 32;
362  list( $network, $bits ) = IPUtils::parseCIDR( $target );
363  $size = $max - $bits;
364 
365  # Rank a range block covering a single IP equally with a single-IP block
366  $score = self::TYPE_RANGE - 1 + ( $size / $max );
367 
368  } else {
369  $score = $block->getType();
370  }
371 
372  if ( $score < $bestBlockScore ) {
373  $bestBlockScore = $score;
374  $bestBlock = $block;
375  }
376  }
377 
378  return $bestBlock;
379  }
380 
387  public static function getRangeCond( $start, $end = null ) {
388  if ( $end === null ) {
389  $end = $start;
390  }
391  # Per T16634, we want to include relevant active rangeblocks; for
392  # rangeblocks, we want to include larger ranges which enclose the given
393  # range. We know that all blocks must be smaller than $wgBlockCIDRLimit,
394  # so we can improve performance by filtering on a LIKE clause
395  $chunk = self::getIpFragment( $start );
396  $dbr = wfGetDB( DB_REPLICA );
397  $like = $dbr->buildLike( $chunk, $dbr->anyString() );
398 
399  # Fairly hard to make a malicious SQL statement out of hex characters,
400  # but stranger things have happened...
401  $safeStart = $dbr->addQuotes( $start );
402  $safeEnd = $dbr->addQuotes( $end );
403 
404  return $dbr->makeList(
405  [
406  "ipb_range_start $like",
407  "ipb_range_start <= $safeStart",
408  "ipb_range_end >= $safeEnd",
409  ],
410  LIST_AND
411  );
412  }
413 
420  protected static function getIpFragment( $hex ) {
421  $blockCIDRLimit = MediaWikiServices::getInstance()->getMainConfig()->get( 'BlockCIDRLimit' );
422  if ( substr( $hex, 0, 3 ) == 'v6-' ) {
423  return 'v6-' . substr( substr( $hex, 3 ), 0, floor( $blockCIDRLimit['IPv6'] / 4 ) );
424  } else {
425  return substr( $hex, 0, floor( $blockCIDRLimit['IPv4'] / 4 ) );
426  }
427  }
428 
434  protected function initFromRow( $row ) {
435  $this->setTarget( $row->ipb_address );
436 
437  $this->setTimestamp( wfTimestamp( TS_MW, $row->ipb_timestamp ) );
438  $this->mAuto = (bool)$row->ipb_auto;
439  $this->setHideName( (bool)$row->ipb_deleted );
440  $this->mId = (int)$row->ipb_id;
441  $this->mParentBlockId = (int)$row->ipb_parent_block_id;
442 
444  ->getActorNormalization()
445  ->newActorFromRowFields( $row->ipb_by, $row->ipb_by_text, $row->ipb_by_actor ) );
446 
447  // I wish I didn't have to do this
448  $db = $this->getDBConnection( DB_REPLICA );
449  $this->setExpiry( $db->decodeExpiry( $row->ipb_expiry ) );
450  $this->setReason(
452  // Legacy because $row may have come from self::selectFields()
453  ->getCommentLegacy( $db, 'ipb_reason', $row )
454  );
455 
456  $this->isHardblock( !$row->ipb_anon_only );
457  $this->isAutoblocking( (bool)$row->ipb_enable_autoblock );
458  $this->isSitewide( (bool)$row->ipb_sitewide );
459 
460  $this->isCreateAccountBlocked( (bool)$row->ipb_create_account );
461  $this->isEmailBlocked( (bool)$row->ipb_block_email );
462  $this->isUsertalkEditAllowed( (bool)$row->ipb_allow_usertalk );
463  }
464 
470  public static function newFromRow( $row ) {
471  $block = new DatabaseBlock;
472  $block->initFromRow( $row );
473  return $block;
474  }
475 
483  public function delete() {
485  ->getDatabaseBlockStore()
486  ->deleteBlock( $this );
487  }
488 
498  public function insert( IDatabase $dbw = null ) {
500  ->getDatabaseBlockStore()
501  ->insertBlock( $this, $dbw );
502  }
503 
512  public function update() {
514  ->getDatabaseBlockStore()
515  ->updateBlock( $this );
516  }
517 
527  public static function isExemptedFromAutoblocks( $ip ) {
528  // Try to get the ip-autoblock_exemption from the cache, as it's faster
529  // than getting the msg raw and explode()'ing it.
530  $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
531  $lines = $cache->getWithSetCallback(
532  $cache->makeKey( 'ip-autoblock', 'exemption' ),
533  $cache::TTL_DAY,
534  static function ( $curValue, &$ttl, array &$setOpts ) {
536 
537  return explode( "\n",
538  wfMessage( 'block-autoblock-exemptionlist' )->inContentLanguage()->plain()
539  );
540  }
541  );
542 
543  wfDebug( "Checking the autoblock exemption list.." );
544 
545  foreach ( $lines as $line ) {
546  # List items only
547  if ( substr( $line, 0, 1 ) !== '*' ) {
548  continue;
549  }
550 
551  $wlEntry = substr( $line, 1 );
552  $wlEntry = trim( $wlEntry );
553 
554  wfDebug( "Checking $ip against $wlEntry..." );
555 
556  # Is the IP in this range?
557  if ( IPUtils::isInRange( $ip, $wlEntry ) ) {
558  wfDebug( " IP $ip matches $wlEntry, not autoblocking" );
559  return true;
560  } else {
561  wfDebug( " No match" );
562  }
563  }
564 
565  return false;
566  }
567 
574  public function doAutoblock( $autoblockIP ) {
575  # If autoblocks are disabled, go away.
576  if ( !$this->isAutoblocking() ) {
577  return false;
578  }
579 
580  # Check if autoblock exempt.
581  if ( self::isExemptedFromAutoblocks( $autoblockIP ) ) {
582  return false;
583  }
584 
585  # Allow hooks to cancel the autoblock.
586  if ( !Hooks::runner()->onAbortAutoblock( $autoblockIP, $this ) ) {
587  wfDebug( "Autoblock aborted by hook." );
588  return false;
589  }
590 
591  # It's okay to autoblock. Go ahead and insert/update the block...
592 
593  # Do not add a *new* block if the IP is already blocked.
594  $ipblock = self::newFromTarget( $autoblockIP );
595  if ( $ipblock ) {
596  # Check if the block is an autoblock and would exceed the user block
597  # if renewed. If so, do nothing, otherwise prolong the block time...
598  if ( $ipblock->mAuto && // @todo Why not compare $ipblock->mExpiry?
599  $this->getExpiry() > self::getAutoblockExpiry( $ipblock->getTimestamp() )
600  ) {
601  # Reset block timestamp to now and its expiry to
602  # $wgAutoblockExpiry in the future
603  $ipblock->updateTimestamp();
604  }
605  return false;
606  }
607 
608  # Make a new block object with the desired properties.
609  $autoblock = new DatabaseBlock;
610  wfDebug( "Autoblocking {$this->getTargetName()}@" . $autoblockIP );
611  $autoblock->setTarget( $autoblockIP );
612  $autoblock->setBlocker( $this->getBlocker() );
613  $autoblock->setReason(
614  wfMessage(
615  'autoblocker',
616  $this->getTargetName(),
617  $this->getReasonComment()->text
618  )->inContentLanguage()->plain()
619  );
620  $timestamp = wfTimestampNow();
621  $autoblock->setTimestamp( $timestamp );
622  $autoblock->mAuto = true;
623  $autoblock->isCreateAccountBlocked( $this->isCreateAccountBlocked() );
624  # Continue suppressing the name if needed
625  $autoblock->setHideName( $this->getHideName() );
626  $autoblock->isUsertalkEditAllowed( $this->isUsertalkEditAllowed() );
627  $autoblock->mParentBlockId = $this->mId;
628  $autoblock->isSitewide( $this->isSitewide() );
629  $autoblock->setRestrictions( $this->getRestrictions() );
630 
631  if ( $this->getExpiry() == 'infinity' ) {
632  # Original block was indefinite, start an autoblock now
633  $autoblock->setExpiry( self::getAutoblockExpiry( $timestamp ) );
634  } else {
635  # If the user is already blocked with an expiry date, we don't
636  # want to pile on top of that.
637  $autoblock->setExpiry( min( $this->getExpiry(), self::getAutoblockExpiry( $timestamp ) ) );
638  }
639 
640  # Insert the block...
641  $status = MediaWikiServices::getInstance()->getDatabaseBlockStore()->insertBlock(
642  $autoblock
643  );
644  return $status
645  ? $status['id']
646  : false;
647  }
648 
653  public function isExpired() {
654  $timestamp = wfTimestampNow();
655  wfDebug( __METHOD__ . " checking current " . $timestamp . " vs $this->mExpiry" );
656 
657  return $this->getExpiry() && $timestamp > $this->getExpiry();
658  }
659 
663  public function updateTimestamp() {
664  if ( $this->mAuto ) {
665  $this->setTimestamp( wfTimestamp() );
666  $this->setExpiry( self::getAutoblockExpiry( $this->getTimestamp() ) );
667 
668  $dbw = $this->getDBConnection( DB_PRIMARY );
669  $dbw->update( 'ipblocks',
670  [ /* SET */
671  'ipb_timestamp' => $dbw->timestamp( $this->getTimestamp() ),
672  'ipb_expiry' => $dbw->timestamp( $this->getExpiry() ),
673  ],
674  [ /* WHERE */
675  'ipb_id' => $this->getId( $this->getWikiId() ),
676  ],
677  __METHOD__
678  );
679  }
680  }
681 
687  public function getRangeStart() {
688  switch ( $this->type ) {
689  case self::TYPE_USER:
690  return '';
691  case self::TYPE_IP:
692  return IPUtils::toHex( $this->target );
693  case self::TYPE_RANGE:
694  list( $start, /*...*/ ) = IPUtils::parseRange( $this->target );
695  return $start;
696  default:
697  throw new MWException( "Block with invalid type" );
698  }
699  }
700 
706  public function getRangeEnd() {
707  switch ( $this->type ) {
708  case self::TYPE_USER:
709  return '';
710  case self::TYPE_IP:
711  return IPUtils::toHex( $this->target );
712  case self::TYPE_RANGE:
713  list( /*...*/, $end ) = IPUtils::parseRange( $this->target );
714  return $end;
715  default:
716  throw new MWException( "Block with invalid type" );
717  }
718  }
719 
724  public function getReason() {
725  if ( $this->getType() === self::TYPE_AUTO ) {
726  return $this->reason->message->inContentLanguage()->plain();
727  }
728  return $this->reason->text;
729  }
730 
734  public function getId( $wikiId = self::LOCAL ): ?int {
735  // TODO: Enable deprecation warnings once cross-wiki accesses have been removed, see T274817
736  // $this->deprecateInvalidCrossWiki( $wikiId, '1.38' );
737  return $this->mId;
738  }
739 
747  public function setId( $blockId ) {
748  $this->mId = (int)$blockId;
749 
750  if ( is_array( $this->restrictions ) ) {
751  $this->restrictions = $this->getBlockRestrictionStore()->setBlockId(
752  $blockId, $this->restrictions
753  );
754  }
755 
756  return $this;
757  }
758 
763  public function getParentBlockId() {
764  return $this->mParentBlockId;
765  }
766 
772  public function isHardblock( $x = null ): bool {
773  wfSetVar( $this->isHardblock, $x );
774 
775  # You can't *not* hardblock a user
776  return $this->getType() == self::TYPE_USER
777  ? true
779  }
780 
785  public function isAutoblocking( $x = null ) {
786  wfSetVar( $this->isAutoblocking, $x );
787 
788  # You can't put an autoblock on an IP or range as we don't have any history to
789  # look over to get more IPs from
790  return $this->getType() == self::TYPE_USER
791  ? $this->isAutoblocking
792  : false;
793  }
794 
799  public function getRedactedName() {
800  if ( $this->mAuto ) {
801  return Html::element(
802  'span',
803  [ 'class' => 'mw-autoblockid' ],
804  wfMessage( 'autoblockid', $this->mId )->text()
805  );
806  } else {
807  return htmlspecialchars( $this->getTargetName() );
808  }
809  }
810 
817  public static function getAutoblockExpiry( $timestamp ) {
818  $autoblockExpiry = MediaWikiServices::getInstance()->getMainConfig()->get( 'AutoblockExpiry' );
819 
820  return wfTimestamp( TS_MW, (int)wfTimestamp( TS_UNIX, $timestamp ) + $autoblockExpiry );
821  }
822 
829  public static function purgeExpired() {
830  wfDeprecated( __METHOD__, '1.36' );
831  MediaWikiServices::getInstance()->getDatabaseBlockStore()->purgeExpiredBlocks();
832  }
833 
854  public static function newFromTarget( $specificTarget, $vagueTarget = null, $fromPrimary = false ) {
855  $blocks = self::newListFromTarget( $specificTarget, $vagueTarget, $fromPrimary );
856  return self::chooseMostSpecificBlock( $blocks );
857  }
858 
868  public static function newListFromTarget(
869  $specificTarget,
870  $vagueTarget = null,
871  $fromPrimary = false
872  ) {
874  ->getBlockUtils()
875  ->parseBlockTarget( $specificTarget );
876  if ( $type == self::TYPE_ID || $type == self::TYPE_AUTO ) {
877  $block = self::newFromID( $target );
878  return $block ? [ $block ] : [];
879  } elseif ( $target === null && $vagueTarget == '' ) {
880  # We're not going to find anything useful here
881  # Be aware that the == '' check is explicit, since empty values will be
882  # passed by some callers (T31116)
883  return [];
884  } elseif ( in_array(
885  $type,
886  [ self::TYPE_USER, self::TYPE_IP, self::TYPE_RANGE, null ] )
887  ) {
888  return self::newLoad( $target, $type, $fromPrimary, $vagueTarget );
889  }
890  return [];
891  }
892 
903  public static function getBlocksForIPList( array $ipChain, $isAnon, $fromPrimary = false ) {
904  if ( $ipChain === [] ) {
905  return [];
906  }
907 
908  $conds = [];
909  $proxyLookup = MediaWikiServices::getInstance()->getProxyLookup();
910  foreach ( array_unique( $ipChain ) as $ipaddr ) {
911  # Discard invalid IP addresses. Since XFF can be spoofed and we do not
912  # necessarily trust the header given to us, make sure that we are only
913  # checking for blocks on well-formatted IP addresses (IPv4 and IPv6).
914  # Do not treat private IP spaces as special as it may be desirable for wikis
915  # to block those IP ranges in order to stop misbehaving proxies that spoof XFF.
916  if ( !IPUtils::isValid( $ipaddr ) ) {
917  continue;
918  }
919  # Don't check trusted IPs (includes local CDNs which will be in every request)
920  if ( $proxyLookup->isTrustedProxy( $ipaddr ) ) {
921  continue;
922  }
923  # Check both the original IP (to check against single blocks), as well as build
924  # the clause to check for rangeblocks for the given IP.
925  $conds['ipb_address'][] = $ipaddr;
926  $conds[] = self::getRangeCond( IPUtils::toHex( $ipaddr ) );
927  }
928 
929  if ( $conds === [] ) {
930  return [];
931  }
932 
933  if ( $fromPrimary ) {
934  $db = wfGetDB( DB_PRIMARY );
935  } else {
936  $db = wfGetDB( DB_REPLICA );
937  }
938  $conds = $db->makeList( $conds, LIST_OR );
939  if ( !$isAnon ) {
940  $conds = [ $conds, 'ipb_anon_only' => 0 ];
941  }
942  $blockQuery = self::getQueryInfo();
943  $rows = $db->select(
944  $blockQuery['tables'],
945  array_merge( [ 'ipb_range_start', 'ipb_range_end' ], $blockQuery['fields'] ),
946  $conds,
947  __METHOD__,
948  [],
949  $blockQuery['joins']
950  );
951 
952  $blocks = [];
953  foreach ( $rows as $row ) {
954  $block = self::newFromRow( $row );
955  if ( !$block->isExpired() ) {
956  $blocks[] = $block;
957  }
958  }
959 
960  return $blocks;
961  }
962 
969  public function getType(): ?int {
970  return $this->mAuto
972  : parent::getType();
973  }
974 
978  public function getIdentifier( $wikiId = self::LOCAL ) {
979  return $this->getId( $wikiId );
980  }
981 
989  public function getRestrictions() {
990  if ( $this->restrictions === null ) {
991  // If the block id has not been set, then do not attempt to load the
992  // restrictions.
993  if ( !$this->mId ) {
994  return [];
995  }
996  $this->restrictions = $this->getBlockRestrictionStore()->loadByBlockId( $this->mId );
997  }
998 
999  return $this->restrictions;
1000  }
1001 
1008  public function getRawRestrictions(): ?array {
1009  return $this->restrictions;
1010  }
1011 
1017  public function setRestrictions( array $restrictions ) {
1018  $this->restrictions = array_filter( $restrictions, static function ( $restriction ) {
1019  return $restriction instanceof Restriction;
1020  } );
1021 
1022  return $this;
1023  }
1024 
1028  public function appliesToTitle( Title $title ) {
1029  if ( $this->isSitewide() ) {
1030  return true;
1031  }
1032 
1033  $restrictions = $this->getRestrictions();
1034  foreach ( $restrictions as $restriction ) {
1035  if ( $restriction->matches( $title ) ) {
1036  return true;
1037  }
1038  }
1039 
1040  return false;
1041  }
1042 
1046  public function appliesToNamespace( $ns ) {
1047  if ( $this->isSitewide() ) {
1048  return true;
1049  }
1050 
1051  // Blocks do not apply to virtual namespaces.
1052  if ( $ns < 0 ) {
1053  return false;
1054  }
1055 
1056  $restriction = $this->findRestriction( NamespaceRestriction::TYPE, $ns );
1057 
1058  return (bool)$restriction;
1059  }
1060 
1064  public function appliesToPage( $pageId ) {
1065  if ( $this->isSitewide() ) {
1066  return true;
1067  }
1068 
1069  // If the pageId is not over zero, the block cannot apply to it.
1070  if ( $pageId <= 0 ) {
1071  return false;
1072  }
1073 
1074  $restriction = $this->findRestriction( PageRestriction::TYPE, $pageId );
1075 
1076  return (bool)$restriction;
1077  }
1078 
1082  public function appliesToRight( $right ) {
1083  // Temporarily access service container until the feature flag is removed: T280532
1084  $config = MediaWikiServices::getInstance()->getMainConfig();
1085 
1086  $res = parent::appliesToRight( $right );
1087 
1088  if ( !$res && $config->get( 'EnablePartialActionBlocks' ) ) {
1089  $blockActions = MediaWikiServices::getInstance()->getBlockActionInfo()
1090  ->getAllBlockActions();
1091 
1092  if ( isset( $blockActions[$right] ) ) {
1093  $restriction = $this->findRestriction(
1095  $blockActions[$right]
1096  );
1097 
1098  // $res may be null or false. This should be preserved if there is no restriction.
1099  if ( $restriction ) {
1100  $res = true;
1101  }
1102  }
1103  }
1104 
1105  return $res;
1106  }
1107 
1115  private function findRestriction( $type, $value ) {
1116  $restrictions = $this->getRestrictions();
1117  foreach ( $restrictions as $restriction ) {
1118  if ( $restriction->getType() !== $type ) {
1119  continue;
1120  }
1121 
1122  if ( $restriction->getValue() === $value ) {
1123  return $restriction;
1124  }
1125  }
1126 
1127  return null;
1128  }
1129 
1136  return MediaWikiServices::getInstance()->getBlockRestrictionStore();
1137  }
1138 
1142  public function getBy( $wikiId = self::LOCAL ): int {
1143  $this->deprecateInvalidCrossWiki( $wikiId, '1.38' );
1144  return ( $this->blocker ) ? $this->blocker->getId( $wikiId ) : 0;
1145  }
1146 
1150  public function getByName() {
1151  return ( $this->blocker ) ? $this->blocker->getName() : '';
1152  }
1153 
1159  public function getBlocker(): ?UserIdentity {
1160  return $this->blocker;
1161  }
1162 
1168  public function setBlocker( $user ) {
1169  if ( !$user->isRegistered() &&
1170  MediaWikiServices::getInstance()->getUserNameUtils()->isUsable( $user->getName() )
1171  ) {
1172  // Temporarily log some block details to debug T192964
1173  $logger = LoggerFactory::getInstance( 'BlockManager' );
1174  $logger->warning(
1175  'Blocker is neither a local user nor an invalid username',
1176  [
1177  'blocker' => (string)$user,
1178  'blockId' => $this->getId( $this->getWikiId() ),
1179  ]
1180  );
1181  throw new InvalidArgumentException(
1182  'Blocker must be a local user or a name that cannot be a local user'
1183  );
1184  }
1185  if ( $user->getWikiId() !== $this->getWikiId() ) {
1186  // throw new InvalidArgumentException(
1187  $logger = LoggerFactory::getInstance( 'BlockManager' );
1188  $logger->warning(
1189  'Blocker domain \'' . $user->getWikiId() . '\' does not match \'' . $this->getWikiId() . '\''
1190  );
1191  }
1192 
1193  $this->blocker = $user;
1194  }
1195 
1200  private function getDBConnection( int $i ) {
1202  ->getDBLoadBalancerFactory()
1203  ->getMainLB( $this->getWikiId() )
1204  ->getConnectionRef( $i, [], $this->getWikiId() );
1205  }
1206 }
1207 
1211 class_alias( DatabaseBlock::class, 'Block' );
LIST_OR
const LIST_OR
Definition: Defines.php:46
MediaWiki\Block\DatabaseBlock\getDBConnection
getDBConnection(int $i)
Definition: DatabaseBlock.php:1200
MediaWiki\Block\DatabaseBlock\setBlocker
setBlocker( $user)
Set the user who implemented (or will implement) this block.
Definition: DatabaseBlock.php:1168
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:420
Wikimedia\Rdbms\Database
Relational database abstraction object.
Definition: Database.php:52
MediaWiki\DAO\deprecateInvalidCrossWiki
deprecateInvalidCrossWiki( $wikiId, string $since)
Emits a deprecation warning $since version if $wikiId is not the same as this wiki.
Definition: WikiAwareEntityTrait.php:71
MediaWiki\Block\DatabaseBlock\equals
equals(DatabaseBlock $block)
Check if two blocks are effectively equal.
Definition: DatabaseBlock.php:208
MediaWiki\Block
Definition: AbstractBlock.php:21
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:203
MediaWiki\Block\AbstractBlock\getTargetName
getTargetName()
Definition: AbstractBlock.php:332
MediaWiki\Block\DatabaseBlock\initFromRow
initFromRow( $row)
Given a database row from the ipblocks table, initialize member variables.
Definition: DatabaseBlock.php:434
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:1496
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:687
MediaWiki\Block\DatabaseBlock\$mParentBlockId
int $mParentBlockId
Definition: DatabaseBlock.php:62
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:1649
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:969
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:220
MediaWiki\Block\DatabaseBlock\getBlocker
getBlocker()
Get the user who implemented this block.
Definition: DatabaseBlock.php:1159
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:98
CommentStore
Handle database storage of comments such as edit summaries and log reasons.
Definition: CommentStore.php:42
MediaWiki\Block\AbstractBlock\$wikiId
string false $wikiId
Definition: AbstractBlock.php:87
MediaWiki\Block\DatabaseBlock\appliesToNamespace
appliesToNamespace( $ns)
Checks if a block applies to a particular namespace.1.33bool
Definition: DatabaseBlock.php:1046
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1167
MediaWiki\Block\AbstractBlock\getWikiId
getWikiId()
Definition: AbstractBlock.php:418
MediaWiki\Block\DatabaseBlock\getByName
getByName()
Get the username of the blocking sysop.string
Definition: DatabaseBlock.php:1150
MediaWiki\Block\DatabaseBlock\$mFromPrimary
bool $mFromPrimary
Definition: DatabaseBlock.php:68
$res
$res
Definition: testCompression.php:57
MediaWiki\Block\DatabaseBlock\getRawRestrictions
getRawRestrictions()
Get restrictions without loading from database if not yet loaded.
Definition: DatabaseBlock.php:1008
MediaWiki\Block\DatabaseBlock\getIdentifier
getIdentifier( $wikiId=self::LOCAL)
Get the information that identifies this block, such that a user could look up everything that can be...
Definition: DatabaseBlock.php:978
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:264
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:1064
MediaWiki\Block\DatabaseBlock
A DatabaseBlock (unlike a SystemBlock) is stored in the database, may give rise to autoblocks and may...
Definition: DatabaseBlock.php:51
MediaWiki\Block\DatabaseBlock\setRestrictions
setRestrictions(array $restrictions)
Definition: DatabaseBlock.php:1017
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:5354
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
Definition: GlobalFunctions.php:997
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:829
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2186
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:233
MediaWiki\Block\DatabaseBlock\newFromRow
static newFromRow( $row)
Create a new DatabaseBlock object from a database row.
Definition: DatabaseBlock.php:470
MediaWiki\Block\Restriction\Restriction
Definition: Restriction.php:25
MediaWiki\Block\AbstractBlock\getReasonComment
getReasonComment()
Get the reason for creating the block.
Definition: AbstractBlock.php:164
MediaWiki\Block\Restriction\PageRestriction\TYPE
const TYPE
Definition: PageRestriction.php:30
MediaWiki\Block\DatabaseBlock\getRangeEnd
getRangeEnd()
Get the IP address at the end of the range in Hex form.
Definition: DatabaseBlock.php:706
$title
$title
Definition: testCompression.php:38
MediaWiki\Block\DatabaseBlock\getBy
getBy( $wikiId=self::LOCAL)
Get the user id of the blocking sysop.(since 1.38) int (0 for foreign users)
Definition: DatabaseBlock.php:1142
MediaWiki\Block\DatabaseBlock\getId
getId( $wikiId=self::LOCAL)
Get the block ID.(since 1.38) ?int
Definition: DatabaseBlock.php:734
MediaWiki\Block\Block\TYPE_RANGE
const TYPE_RANGE
Definition: Block.php:46
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
wfTimestampNow
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
Definition: GlobalFunctions.php:1678
MediaWiki\Block\DatabaseBlock\isExpired
isExpired()
Has the block expired?
Definition: DatabaseBlock.php:653
MediaWiki\Block\AbstractBlock\isSitewide
isSitewide( $x=null)
Indicates that the block is a sitewide block.
Definition: AbstractBlock.php:207
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:72
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:903
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:387
MediaWiki\Block\DatabaseBlock\getBlockRestrictionStore
getBlockRestrictionStore()
Get a BlockRestrictionStore instance.
Definition: DatabaseBlock.php:1135
MediaWiki\Block\DatabaseBlock\getRedactedName
getRedactedName()
Get the block name, but with autoblocked IPs hidden as per standard privacy policy.
Definition: DatabaseBlock.php:799
MediaWiki\Block\DatabaseBlock\isAutoblocking
isAutoblocking( $x=null)
Definition: DatabaseBlock.php:785
MediaWiki\Block\AbstractBlock\setTimestamp
setTimestamp( $timestamp)
Set the timestamp indicating when the block was created.
Definition: AbstractBlock.php:389
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:854
MediaWiki\Block\DatabaseBlock\getParentBlockId
getParentBlockId()
Definition: DatabaseBlock.php:763
MediaWiki\Block\DatabaseBlock\$isAutoblocking
bool $isAutoblocking
Definition: DatabaseBlock.php:71
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:724
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:527
MediaWiki\Block\AbstractBlock\$target
UserIdentity string null $target
Definition: AbstractBlock.php:75
MediaWiki\Block\AbstractBlock\setExpiry
setExpiry( $expiry)
Set the block expiry time.
Definition: AbstractBlock.php:368
MediaWiki\Block\AbstractBlock\getExpiry
getExpiry()
Get the block expiry time.
Definition: AbstractBlock.php:358
MediaWiki\Block\Block\TYPE_AUTO
const TYPE_AUTO
Definition: Block.php:47
MediaWiki\Block\Block\TYPE_USER
const TYPE_USER
Definition: Block.php:44
MediaWiki\Block\DatabaseBlock\update
update()
Update a block in the DB with new parameters.
Definition: DatabaseBlock.php:512
$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:379
Title
Represents a title within MediaWiki.
Definition: Title.php:47
MediaWiki\Block\Block\isCreateAccountBlocked
isCreateAccountBlocked()
Get the flag indicating whether this block blocks the target from creating an account.
MediaWiki\Block\DatabaseBlock\setId
setId( $blockId)
Set the block ID.
Definition: DatabaseBlock.php:747
$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:345
MediaWiki\Block\Restriction\PageRestriction
Definition: PageRestriction.php:25
MediaWiki\Block\AbstractBlock\setReason
setReason( $reason)
Set the reason for creating the block.
Definition: AbstractBlock.php:174
MediaWiki\Block\DatabaseBlock\getRestrictions
getRestrictions()
Getting the restrictions will perform a database query if the restrictions are not already loaded.
Definition: DatabaseBlock.php:989
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:868
MediaWiki\Block\DatabaseBlock\updateTimestamp
updateTimestamp()
Update the timestamp on autoblocks.
Definition: DatabaseBlock.php:663
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:170
MediaWiki\Block\DatabaseBlock\doAutoblock
doAutoblock( $autoblockIP)
Autoblocks the given IP, referring to this block.
Definition: DatabaseBlock.php:574
MediaWiki\Block\DatabaseBlock\findRestriction
findRestriction( $type, $value)
Find Restriction by type and value.
Definition: DatabaseBlock.php:1115
MediaWiki\Block\DatabaseBlock\getAutoblockExpiry
static getAutoblockExpiry( $timestamp)
Get a timestamp of the expiry for autoblocks.
Definition: DatabaseBlock.php:817
MediaWiki\Block\AbstractBlock\getHideName
getHideName()
Get whether the block hides the target's username.
Definition: AbstractBlock.php:184
MediaWiki\Block\Restriction\ActionRestriction\TYPE
const TYPE
Definition: ActionRestriction.php:35
MediaWiki\Block\AbstractBlock\$type
int null $type
AbstractBlock::TYPE_ constant.
Definition: AbstractBlock.php:81
MediaWiki\Block\AbstractBlock
Definition: AbstractBlock.php:38
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:1082
Html\element
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:235
MediaWiki\Block\DatabaseBlock\$mAuto
bool $mAuto
Definition: DatabaseBlock.php:56
MediaWiki\Block\AbstractBlock\setTarget
setTarget( $target)
Set the target for this block, and update $this->type accordingly.
Definition: AbstractBlock.php:398
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:498
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:1028
MediaWiki\Block\DatabaseBlock\$blocker
UserIdentity null $blocker
Definition: DatabaseBlock.php:77
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:772
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:242
MediaWiki\Block\DatabaseBlock\$restrictions
Restriction[] $restrictions
Definition: DatabaseBlock.php:74
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:246
MediaWiki\Block\DatabaseBlock\$mId
int $mId
Definition: DatabaseBlock.php:65
MediaWiki\Block\Block\TYPE_IP
const TYPE_IP
Definition: Block.php:45
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:138
Html
This class is a collection of static functions that serve two purposes:
Definition: Html.php:50