MediaWiki REL1_37
ApiQueryBlocks.php
Go to the documentation of this file.
1<?php
27use Wikimedia\IPUtils;
29
36
39
42
45
48
57 public function __construct(
58 ApiQuery $query,
59 $moduleName,
64 ) {
65 parent::__construct( $query, $moduleName, 'bk' );
66 $this->blockActionInfo = $blockActionInfo;
67 $this->blockRestrictionStore = $blockRestrictionStore;
68 $this->commentStore = $commentStore;
69 $this->userNameUtils = $userNameUtils;
70 }
71
72 public function execute() {
73 $db = $this->getDB();
74 $params = $this->extractRequestParams();
75 $this->requireMaxOneParameter( $params, 'users', 'ip' );
76
77 $prop = array_fill_keys( $params['prop'], true );
78 $fld_id = isset( $prop['id'] );
79 $fld_user = isset( $prop['user'] );
80 $fld_userid = isset( $prop['userid'] );
81 $fld_by = isset( $prop['by'] );
82 $fld_byid = isset( $prop['byid'] );
83 $fld_timestamp = isset( $prop['timestamp'] );
84 $fld_expiry = isset( $prop['expiry'] );
85 $fld_reason = isset( $prop['reason'] );
86 $fld_range = isset( $prop['range'] );
87 $fld_flags = isset( $prop['flags'] );
88 $fld_restrictions = isset( $prop['restrictions'] );
89
90 $result = $this->getResult();
91
92 $this->addTables( 'ipblocks' );
93 $this->addFields( [ 'ipb_auto', 'ipb_id', 'ipb_timestamp' ] );
94
95 $this->addFieldsIf( [ 'ipb_address', 'ipb_user' ], $fld_user || $fld_userid );
96 if ( $fld_by || $fld_byid ) {
97 $this->addTables( 'actor' );
98 $this->addFields( [ 'actor_user', 'actor_name' ] );
99 $this->addJoinConds( [ 'actor' => [ 'JOIN', 'actor_id=ipb_by_actor' ] ] );
100 }
101 $this->addFieldsIf( 'ipb_expiry', $fld_expiry );
102 $this->addFieldsIf( [ 'ipb_range_start', 'ipb_range_end' ], $fld_range );
103 $this->addFieldsIf( [ 'ipb_anon_only', 'ipb_create_account', 'ipb_enable_autoblock',
104 'ipb_block_email', 'ipb_deleted', 'ipb_allow_usertalk', 'ipb_sitewide' ],
105 $fld_flags );
106 $this->addFieldsIf( 'ipb_sitewide', $fld_restrictions );
107
108 if ( $fld_reason ) {
109 $commentQuery = $this->commentStore->getJoin( 'ipb_reason' );
110 $this->addTables( $commentQuery['tables'] );
111 $this->addFields( $commentQuery['fields'] );
112 $this->addJoinConds( $commentQuery['joins'] );
113 }
114
115 $this->addOption( 'LIMIT', $params['limit'] + 1 );
117 'ipb_timestamp',
118 $params['dir'],
119 $params['start'],
120 $params['end']
121 );
122 // Include in ORDER BY for uniqueness
123 $this->addWhereRange( 'ipb_id', $params['dir'], null, null );
124
125 if ( $params['continue'] !== null ) {
126 $cont = explode( '|', $params['continue'] );
127 $this->dieContinueUsageIf( count( $cont ) != 2 );
128 $op = ( $params['dir'] == 'newer' ? '>' : '<' );
129 $continueTimestamp = $db->addQuotes( $db->timestamp( $cont[0] ) );
130 $continueId = (int)$cont[1];
131 $this->dieContinueUsageIf( $continueId != $cont[1] );
132 $this->addWhere( "ipb_timestamp $op $continueTimestamp OR " .
133 "(ipb_timestamp = $continueTimestamp AND " .
134 "ipb_id $op= $continueId)"
135 );
136 }
137
138 if ( isset( $params['ids'] ) ) {
139 $this->addWhereIDsFld( 'ipblocks', 'ipb_id', $params['ids'] );
140 }
141 if ( isset( $params['users'] ) ) {
142 $usernames = [];
143 foreach ( (array)$params['users'] as $u ) {
144 $usernames[] = $this->prepareUsername( $u );
145 }
146 $this->addWhereFld( 'ipb_address', $usernames );
147 $this->addWhereFld( 'ipb_auto', 0 );
148 }
149 if ( isset( $params['ip'] ) ) {
150 $blockCIDRLimit = $this->getConfig()->get( 'BlockCIDRLimit' );
151 if ( IPUtils::isIPv4( $params['ip'] ) ) {
152 $type = 'IPv4';
153 $cidrLimit = $blockCIDRLimit['IPv4'];
154 $prefixLen = 0;
155 } elseif ( IPUtils::isIPv6( $params['ip'] ) ) {
156 $type = 'IPv6';
157 $cidrLimit = $blockCIDRLimit['IPv6'];
158 $prefixLen = 3; // IPUtils::toHex output is prefixed with "v6-"
159 } else {
160 $this->dieWithError( 'apierror-badip', 'param_ip' );
161 }
162
163 # Check range validity, if it's a CIDR
164 list( $ip, $range ) = IPUtils::parseCIDR( $params['ip'] );
165 if ( $ip !== false && $range !== false && $range < $cidrLimit ) {
166 $this->dieWithError( [ 'apierror-cidrtoobroad', $type, $cidrLimit ] );
167 }
168
169 # Let IPUtils::parseRange handle calculating $upper, instead of duplicating the logic here.
170 list( $lower, $upper ) = IPUtils::parseRange( $params['ip'] );
171
172 # Extract the common prefix to any rangeblock affecting this IP/CIDR
173 $prefix = substr( $lower, 0, $prefixLen + floor( $cidrLimit / 4 ) );
174
175 # Fairly hard to make a malicious SQL statement out of hex characters,
176 # but it is good practice to add quotes
177 $lower = $db->addQuotes( $lower );
178 $upper = $db->addQuotes( $upper );
179
180 $this->addWhere( [
181 'ipb_range_start' . $db->buildLike( $prefix, $db->anyString() ),
182 'ipb_range_start <= ' . $lower,
183 'ipb_range_end >= ' . $upper,
184 'ipb_auto' => 0
185 ] );
186 }
187
188 if ( $params['show'] !== null ) {
189 $show = array_fill_keys( $params['show'], true );
190
191 /* Check for conflicting parameters. */
192 if ( ( isset( $show['account'] ) && isset( $show['!account'] ) )
193 || ( isset( $show['ip'] ) && isset( $show['!ip'] ) )
194 || ( isset( $show['range'] ) && isset( $show['!range'] ) )
195 || ( isset( $show['temp'] ) && isset( $show['!temp'] ) )
196 ) {
197 $this->dieWithError( 'apierror-show' );
198 }
199
200 $this->addWhereIf( 'ipb_user = 0', isset( $show['!account'] ) );
201 $this->addWhereIf( 'ipb_user != 0', isset( $show['account'] ) );
202 $this->addWhereIf( 'ipb_user != 0 OR ipb_range_end > ipb_range_start', isset( $show['!ip'] ) );
203 $this->addWhereIf( 'ipb_user = 0 AND ipb_range_end = ipb_range_start', isset( $show['ip'] ) );
204 $this->addWhereIf( 'ipb_expiry = ' .
205 $db->addQuotes( $db->getInfinity() ), isset( $show['!temp'] ) );
206 $this->addWhereIf( 'ipb_expiry != ' .
207 $db->addQuotes( $db->getInfinity() ), isset( $show['temp'] ) );
208 $this->addWhereIf( 'ipb_range_end = ipb_range_start', isset( $show['!range'] ) );
209 $this->addWhereIf( 'ipb_range_end > ipb_range_start', isset( $show['range'] ) );
210 }
211
212 if ( !$this->getAuthority()->isAllowed( 'hideuser' ) ) {
213 $this->addWhereFld( 'ipb_deleted', 0 );
214 }
215
216 # Filter out expired rows
217 $this->addWhere( 'ipb_expiry > ' . $db->addQuotes( $db->timestamp() ) );
218
219 $res = $this->select( __METHOD__ );
220
221 $restrictions = [];
222 if ( $fld_restrictions ) {
223 $restrictions = $this->getRestrictionData( $res, $params['limit'] );
224 }
225
226 $count = 0;
227 foreach ( $res as $row ) {
228 if ( ++$count > $params['limit'] ) {
229 // We've had enough
230 $this->setContinueEnumParameter( 'continue', "$row->ipb_timestamp|$row->ipb_id" );
231 break;
232 }
233 $block = [
234 ApiResult::META_TYPE => 'assoc',
235 ];
236 if ( $fld_id ) {
237 $block['id'] = (int)$row->ipb_id;
238 }
239 if ( $fld_user && !$row->ipb_auto ) {
240 $block['user'] = $row->ipb_address;
241 }
242 if ( $fld_userid && !$row->ipb_auto ) {
243 $block['userid'] = (int)$row->ipb_user;
244 }
245 if ( $fld_by ) {
246 $block['by'] = $row->actor_name;
247 }
248 if ( $fld_byid ) {
249 $block['byid'] = (int)$row->actor_user;
250 }
251 if ( $fld_timestamp ) {
252 $block['timestamp'] = wfTimestamp( TS_ISO_8601, $row->ipb_timestamp );
253 }
254 if ( $fld_expiry ) {
255 $block['expiry'] = ApiResult::formatExpiry( $row->ipb_expiry );
256 }
257 if ( $fld_reason ) {
258 $block['reason'] = $this->commentStore->getComment( 'ipb_reason', $row )->text;
259 }
260 if ( $fld_range && !$row->ipb_auto ) {
261 $block['rangestart'] = IPUtils::formatHex( $row->ipb_range_start );
262 $block['rangeend'] = IPUtils::formatHex( $row->ipb_range_end );
263 }
264 if ( $fld_flags ) {
265 // For clarity, these flags use the same names as their action=block counterparts
266 $block['automatic'] = (bool)$row->ipb_auto;
267 $block['anononly'] = (bool)$row->ipb_anon_only;
268 $block['nocreate'] = (bool)$row->ipb_create_account;
269 $block['autoblock'] = (bool)$row->ipb_enable_autoblock;
270 $block['noemail'] = (bool)$row->ipb_block_email;
271 $block['hidden'] = (bool)$row->ipb_deleted;
272 $block['allowusertalk'] = (bool)$row->ipb_allow_usertalk;
273 $block['partial'] = !(bool)$row->ipb_sitewide;
274 }
275
276 if ( $fld_restrictions ) {
277 $block['restrictions'] = [];
278 if ( !$row->ipb_sitewide && isset( $restrictions[$row->ipb_id] ) ) {
279 $block['restrictions'] = $restrictions[$row->ipb_id];
280 }
281 }
282
283 $fit = $result->addValue( [ 'query', $this->getModuleName() ], null, $block );
284 if ( !$fit ) {
285 $this->setContinueEnumParameter( 'continue', "$row->ipb_timestamp|$row->ipb_id" );
286 break;
287 }
288 }
289 $result->addIndexedTagName( [ 'query', $this->getModuleName() ], 'block' );
290 }
291
292 protected function prepareUsername( $user ) {
293 if ( !$user ) {
294 $encParamName = $this->encodeParamName( 'users' );
295 $this->dieWithError( [ 'apierror-baduser', $encParamName, wfEscapeWikiText( $user ) ],
296 "baduser_{$encParamName}"
297 );
298 }
299 $name = $this->userNameUtils->isIP( $user ) || IPUtils::isIPv6( $user )
300 ? $user
301 : $this->userNameUtils->getCanonical( $user );
302 if ( $name === false ) {
303 $encParamName = $this->encodeParamName( 'users' );
304 $this->dieWithError( [ 'apierror-baduser', $encParamName, wfEscapeWikiText( $user ) ],
305 "baduser_{$encParamName}"
306 );
307 }
308 return $name;
309 }
310
319 private function getRestrictionData( IResultWrapper $result, $limit ) {
320 $partialIds = [];
321 $count = 0;
322 foreach ( $result as $row ) {
323 if ( ++$count <= $limit && !$row->ipb_sitewide ) {
324 $partialIds[] = (int)$row->ipb_id;
325 }
326 }
327
328 $restrictions = $this->blockRestrictionStore->loadByBlockId( $partialIds );
329
330 $data = [];
331 $keys = [
332 'page' => 'pages',
333 'ns' => 'namespaces',
334 ];
335 if ( $this->getConfig()->get( 'EnablePartialActionBlocks' ) ) {
336 $keys['action'] = 'actions';
337 }
338
339 foreach ( $restrictions as $restriction ) {
340 $key = $keys[$restriction->getType()];
341 $id = $restriction->getBlockId();
342 switch ( $restriction->getType() ) {
343 case 'page':
345 '@phan-var \MediaWiki\Block\Restriction\PageRestriction $restriction';
346 $value = [ 'id' => $restriction->getValue() ];
347 if ( $restriction->getTitle() ) {
348 self::addTitleInfo( $value, $restriction->getTitle() );
349 }
350 break;
351 case 'action':
352 $value = $this->blockActionInfo->getActionFromId( $restriction->getValue() );
353 break;
354 default:
355 $value = $restriction->getValue();
356 }
357
358 if ( !isset( $data[$id][$key] ) ) {
359 $data[$id][$key] = [];
360 ApiResult::setIndexedTagName( $data[$id][$key], $restriction->getType() );
361 }
362 $data[$id][$key][] = $value;
363 }
364
365 return $data;
366 }
367
368 public function getAllowedParams() {
369 $blockCIDRLimit = $this->getConfig()->get( 'BlockCIDRLimit' );
370
371 return [
372 'start' => [
373 ApiBase::PARAM_TYPE => 'timestamp'
374 ],
375 'end' => [
376 ApiBase::PARAM_TYPE => 'timestamp',
377 ],
378 'dir' => [
380 'newer',
381 'older'
382 ],
383 ApiBase::PARAM_DFLT => 'older',
384 ApiBase::PARAM_HELP_MSG => 'api-help-param-direction',
385 ],
386 'ids' => [
387 ApiBase::PARAM_TYPE => 'integer',
389 ],
390 'users' => [
391 ApiBase::PARAM_TYPE => 'user',
392 UserDef::PARAM_ALLOWED_USER_TYPES => [ 'name', 'ip', 'cidr' ],
394 ],
395 'ip' => [
397 'apihelp-query+blocks-param-ip',
398 $blockCIDRLimit['IPv4'],
399 $blockCIDRLimit['IPv6'],
400 ],
401 ],
402 'limit' => [
404 ApiBase::PARAM_TYPE => 'limit',
408 ],
409 'prop' => [
410 ApiBase::PARAM_DFLT => 'id|user|by|timestamp|expiry|reason|flags',
412 'id',
413 'user',
414 'userid',
415 'by',
416 'byid',
417 'timestamp',
418 'expiry',
419 'reason',
420 'range',
421 'flags',
422 'restrictions',
423 ],
426 ],
427 'show' => [
429 'account',
430 '!account',
431 'temp',
432 '!temp',
433 'ip',
434 '!ip',
435 'range',
436 '!range',
437 ],
439 ],
440 'continue' => [
441 ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
442 ],
443 ];
444 }
445
446 protected function getExamplesMessages() {
447 return [
448 'action=query&list=blocks'
449 => 'apihelp-query+blocks-example-simple',
450 'action=query&list=blocks&bkusers=Alice|Bob'
451 => 'apihelp-query+blocks-example-users',
452 ];
453 }
454
455 public function getHelpUrls() {
456 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Blocks';
457 }
458}
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
dieWithError( $msg, $code=null, $data=null, $httpCode=0)
Abort execution with an error.
Definition ApiBase.php:1436
const PARAM_MAX2
Definition ApiBase.php:89
encodeParamName( $paramName)
This method mangles parameter name based on the prefix supplied to the constructor.
Definition ApiBase.php:742
const PARAM_MAX
Definition ApiBase.php:85
dieContinueUsageIf( $condition)
Die with the 'badcontinue' error.
Definition ApiBase.php:1620
const PARAM_TYPE
Definition ApiBase.php:81
const PARAM_DFLT
Definition ApiBase.php:73
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:195
const PARAM_MIN
Definition ApiBase.php:93
const LIMIT_BIG1
Fast query, standard limit.
Definition ApiBase.php:220
requireMaxOneParameter( $params,... $required)
Die if more than one of a certain set of parameters is set and not false.
Definition ApiBase.php:936
getResult()
Get the result object.
Definition ApiBase.php:628
extractRequestParams( $options=[])
Using getAllowedParams(), this function makes an array of the values provided by the user,...
Definition ApiBase.php:764
const PARAM_HELP_MSG
(string|array|Message) Specify an alternative i18n documentation message for this parameter.
Definition ApiBase.php:162
const LIMIT_BIG2
Fast query, apihighlimits limit.
Definition ApiBase.php:222
getModuleName()
Get the name of the module being executed by this instance.
Definition ApiBase.php:497
const PARAM_ISMULTI
Definition ApiBase.php:77
This is a base class for all Query modules.
static addTitleInfo(&$arr, $title, $prefix='')
Add information (title and namespace) about a Title object to a result array.
setContinueEnumParameter( $paramName, $paramValue)
Set a query-continue value.
addWhereIf( $value, $condition)
Same as addWhere(), but add the WHERE clauses only if a condition is met.
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.
addFields( $value)
Add a set of fields to select to the internal array.
addOption( $name, $value=null)
Add an option such as LIMIT or USE INDEX.
addTables( $tables, $alias=null)
Add a set of tables to the internal array.
addTimestampWhereRange( $field, $dir, $start, $end, $sort=true)
Add a WHERE clause corresponding to a range, similar to addWhereRange, but converts $start and $end t...
getDB()
Get the Query database connection (read-only)
select( $method, $extraQuery=[], array &$hookData=null)
Execute a SELECT query based on the values in the internal arrays.
addFieldsIf( $value, $condition)
Same as addFields(), but add the fields only if a condition is met.
addWhereIDsFld( $table, $field, $ids)
Like addWhereFld for an integer list of IDs.
addJoinConds( $join_conds)
Add a set of JOIN conditions to the internal array.
addWhereFld( $field, $value)
Equivalent to addWhere( [ $field => $value ] )
addWhere( $value)
Add a set of WHERE clauses to the internal array.
Query module to enumerate all user blocks.
execute()
Evaluates the parameters, performs the requested query, and sets up the result.
BlockActionInfo $blockActionInfo
BlockRestrictionStore $blockRestrictionStore
getRestrictionData(IResultWrapper $result, $limit)
Retrieves the restrictions based on the query result.
getAllowedParams()
Returns an array of allowed parameters (parameter name) => (default value) or (parameter name) => (ar...
__construct(ApiQuery $query, $moduleName, BlockActionInfo $blockActionInfo, BlockRestrictionStore $blockRestrictionStore, CommentStore $commentStore, UserNameUtils $userNameUtils)
CommentStore $commentStore
UserNameUtils $userNameUtils
getExamplesMessages()
Returns usage examples for this module.
getHelpUrls()
Return links to more detailed help pages about the module.
This is the main query class.
Definition ApiQuery.php:37
Handle database storage of comments such as edit summaries and log reasons.
Defines the actions that can be blocked by a partial block.
Type definition for user types.
Definition UserDef.php:25
UserNameUtils service.
Result wrapper for grabbing data queried from an IDatabase object.
return true
Definition router.php:92