MediaWiki  master
ApiQueryBlocks.php
Go to the documentation of this file.
1 <?php
24 use Wikimedia\IPUtils;
26 
33 
34  public function __construct( ApiQuery $query, $moduleName ) {
35  parent::__construct( $query, $moduleName, 'bk' );
36  }
37 
38  public function execute() {
39  $db = $this->getDB();
40  $commentStore = CommentStore::getStore();
41  $params = $this->extractRequestParams();
42  $this->requireMaxOneParameter( $params, 'users', 'ip' );
43 
44  $prop = array_flip( $params['prop'] );
45  $fld_id = isset( $prop['id'] );
46  $fld_user = isset( $prop['user'] );
47  $fld_userid = isset( $prop['userid'] );
48  $fld_by = isset( $prop['by'] );
49  $fld_byid = isset( $prop['byid'] );
50  $fld_timestamp = isset( $prop['timestamp'] );
51  $fld_expiry = isset( $prop['expiry'] );
52  $fld_reason = isset( $prop['reason'] );
53  $fld_range = isset( $prop['range'] );
54  $fld_flags = isset( $prop['flags'] );
55  $fld_restrictions = isset( $prop['restrictions'] );
56 
57  $result = $this->getResult();
58 
59  $this->addTables( 'ipblocks' );
60  $this->addFields( [ 'ipb_auto', 'ipb_id', 'ipb_timestamp' ] );
61 
62  $this->addFieldsIf( [ 'ipb_address', 'ipb_user' ], $fld_user || $fld_userid );
63  if ( $fld_by || $fld_byid ) {
64  $actorQuery = ActorMigration::newMigration()->getJoin( 'ipb_by' );
65  $this->addTables( $actorQuery['tables'] );
66  $this->addFields( $actorQuery['fields'] );
67  $this->addJoinConds( $actorQuery['joins'] );
68  }
69  $this->addFieldsIf( 'ipb_expiry', $fld_expiry );
70  $this->addFieldsIf( [ 'ipb_range_start', 'ipb_range_end' ], $fld_range );
71  $this->addFieldsIf( [ 'ipb_anon_only', 'ipb_create_account', 'ipb_enable_autoblock',
72  'ipb_block_email', 'ipb_deleted', 'ipb_allow_usertalk', 'ipb_sitewide' ],
73  $fld_flags );
74  $this->addFieldsIf( 'ipb_sitewide', $fld_restrictions );
75 
76  if ( $fld_reason ) {
77  $commentQuery = $commentStore->getJoin( 'ipb_reason' );
78  $this->addTables( $commentQuery['tables'] );
79  $this->addFields( $commentQuery['fields'] );
80  $this->addJoinConds( $commentQuery['joins'] );
81  }
82 
83  $this->addOption( 'LIMIT', $params['limit'] + 1 );
85  'ipb_timestamp',
86  $params['dir'],
87  $params['start'],
88  $params['end']
89  );
90  // Include in ORDER BY for uniqueness
91  $this->addWhereRange( 'ipb_id', $params['dir'], null, null );
92 
93  if ( $params['continue'] !== null ) {
94  $cont = explode( '|', $params['continue'] );
95  $this->dieContinueUsageIf( count( $cont ) != 2 );
96  $op = ( $params['dir'] == 'newer' ? '>' : '<' );
97  $continueTimestamp = $db->addQuotes( $db->timestamp( $cont[0] ) );
98  $continueId = (int)$cont[1];
99  $this->dieContinueUsageIf( $continueId != $cont[1] );
100  $this->addWhere( "ipb_timestamp $op $continueTimestamp OR " .
101  "(ipb_timestamp = $continueTimestamp AND " .
102  "ipb_id $op= $continueId)"
103  );
104  }
105 
106  if ( isset( $params['ids'] ) ) {
107  $this->addWhereIDsFld( 'ipblocks', 'ipb_id', $params['ids'] );
108  }
109  if ( isset( $params['users'] ) ) {
110  $usernames = [];
111  foreach ( (array)$params['users'] as $u ) {
112  $usernames[] = $this->prepareUsername( $u );
113  }
114  $this->addWhereFld( 'ipb_address', $usernames );
115  $this->addWhereFld( 'ipb_auto', 0 );
116  }
117  if ( isset( $params['ip'] ) ) {
118  $blockCIDRLimit = $this->getConfig()->get( 'BlockCIDRLimit' );
119  if ( IPUtils::isIPv4( $params['ip'] ) ) {
120  $type = 'IPv4';
121  $cidrLimit = $blockCIDRLimit['IPv4'];
122  $prefixLen = 0;
123  } elseif ( IPUtils::isIPv6( $params['ip'] ) ) {
124  $type = 'IPv6';
125  $cidrLimit = $blockCIDRLimit['IPv6'];
126  $prefixLen = 3; // IPUtils::toHex output is prefixed with "v6-"
127  } else {
128  $this->dieWithError( 'apierror-badip', 'param_ip' );
129  }
130 
131  # Check range validity, if it's a CIDR
132  list( $ip, $range ) = IPUtils::parseCIDR( $params['ip'] );
133  if ( $ip !== false && $range !== false && $range < $cidrLimit ) {
134  $this->dieWithError( [ 'apierror-cidrtoobroad', $type, $cidrLimit ] );
135  }
136 
137  # Let IPUtils::parseRange handle calculating $upper, instead of duplicating the logic here.
138  list( $lower, $upper ) = IPUtils::parseRange( $params['ip'] );
139 
140  # Extract the common prefix to any rangeblock affecting this IP/CIDR
141  $prefix = substr( $lower, 0, $prefixLen + floor( $cidrLimit / 4 ) );
142 
143  # Fairly hard to make a malicious SQL statement out of hex characters,
144  # but it is good practice to add quotes
145  $lower = $db->addQuotes( $lower );
146  $upper = $db->addQuotes( $upper );
147 
148  $this->addWhere( [
149  'ipb_range_start' . $db->buildLike( $prefix, $db->anyString() ),
150  'ipb_range_start <= ' . $lower,
151  'ipb_range_end >= ' . $upper,
152  'ipb_auto' => 0
153  ] );
154  }
155 
156  if ( $params['show'] !== null ) {
157  $show = array_flip( $params['show'] );
158 
159  /* Check for conflicting parameters. */
160  if ( ( isset( $show['account'] ) && isset( $show['!account'] ) )
161  || ( isset( $show['ip'] ) && isset( $show['!ip'] ) )
162  || ( isset( $show['range'] ) && isset( $show['!range'] ) )
163  || ( isset( $show['temp'] ) && isset( $show['!temp'] ) )
164  ) {
165  $this->dieWithError( 'apierror-show' );
166  }
167 
168  $this->addWhereIf( 'ipb_user = 0', isset( $show['!account'] ) );
169  $this->addWhereIf( 'ipb_user != 0', isset( $show['account'] ) );
170  $this->addWhereIf( 'ipb_user != 0 OR ipb_range_end > ipb_range_start', isset( $show['!ip'] ) );
171  $this->addWhereIf( 'ipb_user = 0 AND ipb_range_end = ipb_range_start', isset( $show['ip'] ) );
172  $this->addWhereIf( 'ipb_expiry = ' .
173  $db->addQuotes( $db->getInfinity() ), isset( $show['!temp'] ) );
174  $this->addWhereIf( 'ipb_expiry != ' .
175  $db->addQuotes( $db->getInfinity() ), isset( $show['temp'] ) );
176  $this->addWhereIf( 'ipb_range_end = ipb_range_start', isset( $show['!range'] ) );
177  $this->addWhereIf( 'ipb_range_end > ipb_range_start', isset( $show['range'] ) );
178  }
179 
180  if ( !$this->getPermissionManager()->userHasRight( $this->getUser(), 'hideuser' ) ) {
181  $this->addWhereFld( 'ipb_deleted', 0 );
182  }
183 
184  # Filter out expired rows
185  $this->addWhere( 'ipb_expiry > ' . $db->addQuotes( $db->timestamp() ) );
186 
187  $res = $this->select( __METHOD__ );
188 
189  $restrictions = [];
190  if ( $fld_restrictions ) {
191  $restrictions = self::getRestrictionData( $res, $params['limit'] );
192  }
193 
194  $count = 0;
195  foreach ( $res as $row ) {
196  if ( ++$count > $params['limit'] ) {
197  // We've had enough
198  $this->setContinueEnumParameter( 'continue', "$row->ipb_timestamp|$row->ipb_id" );
199  break;
200  }
201  $block = [
202  ApiResult::META_TYPE => 'assoc',
203  ];
204  if ( $fld_id ) {
205  $block['id'] = (int)$row->ipb_id;
206  }
207  if ( $fld_user && !$row->ipb_auto ) {
208  $block['user'] = $row->ipb_address;
209  }
210  if ( $fld_userid && !$row->ipb_auto ) {
211  $block['userid'] = (int)$row->ipb_user;
212  }
213  if ( $fld_by ) {
214  $block['by'] = $row->ipb_by_text;
215  }
216  if ( $fld_byid ) {
217  $block['byid'] = (int)$row->ipb_by;
218  }
219  if ( $fld_timestamp ) {
220  $block['timestamp'] = wfTimestamp( TS_ISO_8601, $row->ipb_timestamp );
221  }
222  if ( $fld_expiry ) {
223  $block['expiry'] = ApiResult::formatExpiry( $row->ipb_expiry );
224  }
225  if ( $fld_reason ) {
226  $block['reason'] = $commentStore->getComment( 'ipb_reason', $row )->text;
227  }
228  if ( $fld_range && !$row->ipb_auto ) {
229  $block['rangestart'] = IPUtils::formatHex( $row->ipb_range_start );
230  $block['rangeend'] = IPUtils::formatHex( $row->ipb_range_end );
231  }
232  if ( $fld_flags ) {
233  // For clarity, these flags use the same names as their action=block counterparts
234  $block['automatic'] = (bool)$row->ipb_auto;
235  $block['anononly'] = (bool)$row->ipb_anon_only;
236  $block['nocreate'] = (bool)$row->ipb_create_account;
237  $block['autoblock'] = (bool)$row->ipb_enable_autoblock;
238  $block['noemail'] = (bool)$row->ipb_block_email;
239  $block['hidden'] = (bool)$row->ipb_deleted;
240  $block['allowusertalk'] = (bool)$row->ipb_allow_usertalk;
241  $block['partial'] = !(bool)$row->ipb_sitewide;
242  }
243 
244  if ( $fld_restrictions ) {
245  $block['restrictions'] = [];
246  if ( !$row->ipb_sitewide && isset( $restrictions[$row->ipb_id] ) ) {
247  $block['restrictions'] = $restrictions[$row->ipb_id];
248  }
249  }
250 
251  $fit = $result->addValue( [ 'query', $this->getModuleName() ], null, $block );
252  if ( !$fit ) {
253  $this->setContinueEnumParameter( 'continue', "$row->ipb_timestamp|$row->ipb_id" );
254  break;
255  }
256  }
257  $result->addIndexedTagName( [ 'query', $this->getModuleName() ], 'block' );
258  }
259 
260  protected function prepareUsername( $user ) {
261  if ( !$user ) {
262  $encParamName = $this->encodeParamName( 'users' );
263  $this->dieWithError( [ 'apierror-baduser', $encParamName, wfEscapeWikiText( $user ) ],
264  "baduser_{$encParamName}"
265  );
266  }
267  $name = User::isIP( $user )
268  ? $user
269  : User::getCanonicalName( $user, 'valid' );
270  if ( $name === false ) {
271  $encParamName = $this->encodeParamName( 'users' );
272  $this->dieWithError( [ 'apierror-baduser', $encParamName, wfEscapeWikiText( $user ) ],
273  "baduser_{$encParamName}"
274  );
275  }
276  return $name;
277  }
278 
287  private static function getRestrictionData( IResultWrapper $result, $limit ) {
288  $partialIds = [];
289  $count = 0;
290  foreach ( $result as $row ) {
291  if ( ++$count <= $limit && !$row->ipb_sitewide ) {
292  $partialIds[] = (int)$row->ipb_id;
293  }
294  }
295 
296  $blockRestrictionStore = MediaWikiServices::getInstance()->getBlockRestrictionStore();
297  $restrictions = $blockRestrictionStore->loadByBlockId( $partialIds );
298 
299  $data = [];
300  $keys = [
301  'page' => 'pages',
302  'ns' => 'namespaces',
303  ];
304  foreach ( $restrictions as $restriction ) {
305  $key = $keys[$restriction->getType()];
306  $id = $restriction->getBlockId();
307  switch ( $restriction->getType() ) {
308  case 'page':
310  '@phan-var \MediaWiki\Block\Restriction\PageRestriction $restriction';
311  $value = [ 'id' => $restriction->getValue() ];
312  if ( $restriction->getTitle() ) {
313  self::addTitleInfo( $value, $restriction->getTitle() );
314  }
315  break;
316  default:
317  $value = $restriction->getValue();
318  }
319 
320  if ( !isset( $data[$id][$key] ) ) {
321  $data[$id][$key] = [];
322  ApiResult::setIndexedTagName( $data[$id][$key], $restriction->getType() );
323  }
324  $data[$id][$key][] = $value;
325  }
326 
327  return $data;
328  }
329 
330  public function getAllowedParams() {
331  $blockCIDRLimit = $this->getConfig()->get( 'BlockCIDRLimit' );
332 
333  return [
334  'start' => [
335  ApiBase::PARAM_TYPE => 'timestamp'
336  ],
337  'end' => [
338  ApiBase::PARAM_TYPE => 'timestamp',
339  ],
340  'dir' => [
342  'newer',
343  'older'
344  ],
345  ApiBase::PARAM_DFLT => 'older',
346  ApiBase::PARAM_HELP_MSG => 'api-help-param-direction',
347  ],
348  'ids' => [
349  ApiBase::PARAM_TYPE => 'integer',
351  ],
352  'users' => [
353  ApiBase::PARAM_TYPE => 'user',
355  ],
356  'ip' => [
358  'apihelp-query+blocks-param-ip',
359  $blockCIDRLimit['IPv4'],
360  $blockCIDRLimit['IPv6'],
361  ],
362  ],
363  'limit' => [
364  ApiBase::PARAM_DFLT => 10,
365  ApiBase::PARAM_TYPE => 'limit',
366  ApiBase::PARAM_MIN => 1,
369  ],
370  'prop' => [
371  ApiBase::PARAM_DFLT => 'id|user|by|timestamp|expiry|reason|flags',
373  'id',
374  'user',
375  'userid',
376  'by',
377  'byid',
378  'timestamp',
379  'expiry',
380  'reason',
381  'range',
382  'flags',
383  'restrictions',
384  ],
385  ApiBase::PARAM_ISMULTI => true,
387  ],
388  'show' => [
390  'account',
391  '!account',
392  'temp',
393  '!temp',
394  'ip',
395  '!ip',
396  'range',
397  '!range',
398  ],
400  ],
401  'continue' => [
402  ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
403  ],
404  ];
405  }
406 
407  protected function getExamplesMessages() {
408  return [
409  'action=query&list=blocks'
410  => 'apihelp-query+blocks-example-simple',
411  'action=query&list=blocks&bkusers=Alice|Bob'
412  => 'apihelp-query+blocks-example-users',
413  ];
414  }
415 
416  public function getHelpUrls() {
417  return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Blocks';
418  }
419 }
ContextSource\getConfig
getConfig()
Definition: ContextSource.php:63
ApiQueryBase\addWhereIDsFld
addWhereIDsFld( $table, $field, $ids)
Like addWhereFld for an integer list of IDs.
Definition: ApiQueryBase.php:294
ApiQueryBase\addFields
addFields( $value)
Add a set of fields to select to the internal array.
Definition: ApiQueryBase.php:193
ApiQuery
This is the main query class.
Definition: ApiQuery.php:37
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:130
ApiResult\META_TYPE
const META_TYPE
Key for the 'type' metadata item.
Definition: ApiResult.php:110
ApiBase\dieWithError
dieWithError( $msg, $code=null, $data=null, $httpCode=null)
Abort execution with an error.
Definition: ApiBase.php:2015
ApiQueryBase\addTimestampWhereRange
addTimestampWhereRange( $field, $dir, $start, $end, $sort=true)
Add a WHERE clause corresponding to a range, similar to addWhereRange, but converts $start and $end t...
Definition: ApiQueryBase.php:357
ApiBase\PARAM_HELP_MSG
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition: ApiBase.php:132
ApiQueryBlocks\getAllowedParams
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
Definition: ApiQueryBlocks.php:330
true
return true
Definition: router.php:92
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:1871
ApiBase\PARAM_TYPE
const PARAM_TYPE
(string|string[]) Either an array of allowed value strings, or a string type as described below.
Definition: ApiBase.php:95
ApiBase\getResult
getResult()
Get the result object.
Definition: ApiBase.php:644
ApiQueryBlocks\getExamplesMessages
getExamplesMessages()
Returns usage examples for this module.
Definition: ApiQueryBlocks.php:407
ApiQueryBlocks\getRestrictionData
static getRestrictionData(IResultWrapper $result, $limit)
Retrieves the restrictions based on the query result.
Definition: ApiQueryBlocks.php:287
ApiQueryBase\addOption
addOption( $name, $value=null)
Add an option such as LIMIT or USE INDEX.
Definition: ApiQueryBase.php:369
$res
$res
Definition: testCompression.php:54
ContextSource\getUser
getUser()
Definition: ContextSource.php:120
ActorMigration\newMigration
static newMigration()
Static constructor.
Definition: ActorMigration.php:139
ApiQueryBase\addFieldsIf
addFieldsIf( $value, $condition)
Same as addFields(), but add the fields only if a condition is met.
Definition: ApiQueryBase.php:207
ApiQueryBlocks\getHelpUrls
getHelpUrls()
Return links to more detailed help pages about the module.
Definition: ApiQueryBlocks.php:416
ApiQueryBlocks
Query module to enumerate all user blocks.
Definition: ApiQueryBlocks.php:32
ApiBase\PARAM_MIN
const PARAM_MIN
(integer) Lowest value allowed for the parameter, for PARAM_TYPE 'integer' and 'limit'.
Definition: ApiBase.php:107
Wikimedia\Rdbms\IResultWrapper
Result wrapper for grabbing data queried from an IDatabase object.
Definition: IResultWrapper.php:24
ApiQueryBase
This is a base class for all Query modules.
Definition: ApiQueryBase.php:34
ApiBase\LIMIT_BIG1
const LIMIT_BIG1
Fast query, standard limit.
Definition: ApiBase.php:260
ApiQueryBase\getDB
getDB()
Get the Query database connection (read-only)
Definition: ApiQueryBase.php:107
ApiBase\PARAM_MAX
const PARAM_MAX
(integer) Max value allowed for the parameter, for PARAM_TYPE 'integer' and 'limit'.
Definition: ApiBase.php:98
ApiQueryBlocks\__construct
__construct(ApiQuery $query, $moduleName)
Definition: ApiQueryBlocks.php:34
User\isIP
static isIP( $name)
Does the string match an anonymous IP address?
Definition: User.php:948
ApiQueryBase\addTables
addTables( $tables, $alias=null)
Add a set of tables to the internal array.
Definition: ApiQueryBase.php:161
ApiQueryBase\select
select( $method, $extraQuery=[], array &$hookData=null)
Execute a SELECT query based on the values in the internal arrays.
Definition: ApiQueryBase.php:394
ApiBase\extractRequestParams
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition: ApiBase.php:765
ApiQueryBlocks\prepareUsername
prepareUsername( $user)
Definition: ApiQueryBlocks.php:260
ApiQueryBase\addWhereRange
addWhereRange( $field, $dir, $start, $end, $sort=true)
Add a WHERE clause corresponding to a range, and an ORDER BY clause to sort in the right direction.
Definition: ApiQueryBase.php:322
ApiResult\setIndexedTagName
static setIndexedTagName(array &$arr, $tag)
Set the tag name for numeric-keyed values in XML format.
Definition: ApiResult.php:616
ApiBase\requireMaxOneParameter
requireMaxOneParameter( $params,... $required)
Die if more than one of a certain set of parameters is set and not false.
Definition: ApiBase.php:933
ApiBase\dieContinueUsageIf
dieContinueUsageIf( $condition)
Die with the 'badcontinue' error.
Definition: ApiBase.php:2210
ApiBase\encodeParamName
encodeParamName( $paramName)
This method mangles parameter name based on the prefix supplied to the constructor.
Definition: ApiBase.php:743
ApiBase\getPermissionManager
getPermissionManager()
Obtain a PermissionManager instance that subclasses may use in their authorization checks.
Definition: ApiBase.php:714
ApiQueryBase\addJoinConds
addJoinConds( $join_conds)
Add a set of JOIN conditions to the internal array.
Definition: ApiQueryBase.php:182
wfEscapeWikiText
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
Definition: GlobalFunctions.php:1550
ApiQueryBase\addWhereFld
addWhereFld( $field, $value)
Equivalent to addWhere( [ $field => $value ] )
Definition: ApiQueryBase.php:267
ApiQueryBlocks\execute
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
Definition: ApiQueryBlocks.php:38
ApiBase\LIMIT_BIG2
const LIMIT_BIG2
Fast query, apihighlimits limit.
Definition: ApiBase.php:262
User\getCanonicalName
static getCanonicalName( $name, $validate='valid')
Given unvalidated user input, return a canonical username, or false if the username is invalid.
Definition: User.php:1195
ApiBase\PARAM_DFLT
const PARAM_DFLT
(null|boolean|integer|string) Default value of the parameter.
Definition: ApiBase.php:56
ApiBase\getModuleName
getModuleName()
Get the name of the module being executed by this instance.
Definition: ApiBase.php:524
ApiBase\PARAM_ISMULTI
const PARAM_ISMULTI
(boolean) Accept multiple pipe-separated values for this parameter (e.g.
Definition: ApiBase.php:59
$keys
$keys
Definition: testCompression.php:69
ApiBase\PARAM_MAX2
const PARAM_MAX2
(integer) Max value allowed for the parameter for users with the apihighlimits right,...
Definition: ApiBase.php:104
ApiResult\formatExpiry
static formatExpiry( $expiry, $infinity='infinity')
Format an expiry timestamp for API output.
Definition: ApiResult.php:1205
ApiQueryBase\addWhere
addWhere( $value)
Add a set of WHERE clauses to the internal array.
Definition: ApiQueryBase.php:230
ApiQueryBase\setContinueEnumParameter
setContinueEnumParameter( $paramName, $paramValue)
Set a query-continue value.
Definition: ApiQueryBase.php:511
ApiBase\PARAM_HELP_MSG_PER_VALUE
const PARAM_HELP_MSG_PER_VALUE
((string|array|Message)[]) When PARAM_TYPE is an array, this is an array mapping those values to $msg...
Definition: ApiBase.php:165
CommentStore\getStore
static getStore()
Definition: CommentStore.php:139
ApiQueryBase\addWhereIf
addWhereIf( $value, $condition)
Same as addWhere(), but add the WHERE clauses only if a condition is met.
Definition: ApiQueryBase.php:248
ApiQueryBase\addTitleInfo
static addTitleInfo(&$arr, $title, $prefix='')
Add information (title and namespace) about a Title object to a result array.
Definition: ApiQueryBase.php:462
$type
$type
Definition: testCompression.php:50