MediaWiki  REL1_24
ApiQueryBlocks.php
Go to the documentation of this file.
00001 <?php
00032 class ApiQueryBlocks extends ApiQueryBase {
00033 
00037     protected $usernames;
00038 
00039     public function __construct( ApiQuery $query, $moduleName ) {
00040         parent::__construct( $query, $moduleName, 'bk' );
00041     }
00042 
00043     public function execute() {
00044         global $wgContLang;
00045 
00046         $db = $this->getDB();
00047         $params = $this->extractRequestParams();
00048         $this->requireMaxOneParameter( $params, 'users', 'ip' );
00049 
00050         $prop = array_flip( $params['prop'] );
00051         $fld_id = isset( $prop['id'] );
00052         $fld_user = isset( $prop['user'] );
00053         $fld_userid = isset( $prop['userid'] );
00054         $fld_by = isset( $prop['by'] );
00055         $fld_byid = isset( $prop['byid'] );
00056         $fld_timestamp = isset( $prop['timestamp'] );
00057         $fld_expiry = isset( $prop['expiry'] );
00058         $fld_reason = isset( $prop['reason'] );
00059         $fld_range = isset( $prop['range'] );
00060         $fld_flags = isset( $prop['flags'] );
00061 
00062         $result = $this->getResult();
00063 
00064         $this->addTables( 'ipblocks' );
00065         $this->addFields( array( 'ipb_auto', 'ipb_id' ) );
00066 
00067         $this->addFieldsIf( array( 'ipb_address', 'ipb_user' ), $fld_user || $fld_userid );
00068         $this->addFieldsIf( 'ipb_by_text', $fld_by );
00069         $this->addFieldsIf( 'ipb_by', $fld_byid );
00070         $this->addFieldsIf( 'ipb_timestamp', $fld_timestamp );
00071         $this->addFieldsIf( 'ipb_expiry', $fld_expiry );
00072         $this->addFieldsIf( 'ipb_reason', $fld_reason );
00073         $this->addFieldsIf( array( 'ipb_range_start', 'ipb_range_end' ), $fld_range );
00074         $this->addFieldsIf( array( 'ipb_anon_only', 'ipb_create_account', 'ipb_enable_autoblock',
00075             'ipb_block_email', 'ipb_deleted', 'ipb_allow_usertalk' ),
00076             $fld_flags );
00077 
00078         $this->addOption( 'LIMIT', $params['limit'] + 1 );
00079         $this->addTimestampWhereRange(
00080             'ipb_timestamp',
00081             $params['dir'],
00082             $params['start'],
00083             $params['end']
00084         );
00085         // Include in ORDER BY for uniqueness
00086         $this->addWhereRange( 'ipb_id', $params['dir'], null, null );
00087 
00088         if ( !is_null( $params['continue'] ) ) {
00089             $cont = explode( '|', $params['continue'] );
00090             $this->dieContinueUsageIf( count( $cont ) != 2 );
00091             $op = ( $params['dir'] == 'newer' ? '>' : '<' );
00092             $continueTimestamp = $db->addQuotes( $db->timestamp( $cont[0] ) );
00093             $continueId = (int)$cont[1];
00094             $this->dieContinueUsageIf( $continueId != $cont[1] );
00095             $this->addWhere( "ipb_timestamp $op $continueTimestamp OR " .
00096                 "(ipb_timestamp = $continueTimestamp AND " .
00097                 "ipb_id $op= $continueId)"
00098             );
00099         }
00100 
00101         if ( isset( $params['ids'] ) ) {
00102             $this->addWhereFld( 'ipb_id', $params['ids'] );
00103         }
00104         if ( isset( $params['users'] ) ) {
00105             foreach ( (array)$params['users'] as $u ) {
00106                 $this->prepareUsername( $u );
00107             }
00108             $this->addWhereFld( 'ipb_address', $this->usernames );
00109             $this->addWhereFld( 'ipb_auto', 0 );
00110         }
00111         if ( isset( $params['ip'] ) ) {
00112             $blockCIDRLimit = $this->getConfig()->get( 'BlockCIDRLimit' );
00113             if ( IP::isIPv4( $params['ip'] ) ) {
00114                 $type = 'IPv4';
00115                 $cidrLimit = $blockCIDRLimit['IPv4'];
00116                 $prefixLen = 0;
00117             } elseif ( IP::isIPv6( $params['ip'] ) ) {
00118                 $type = 'IPv6';
00119                 $cidrLimit = $blockCIDRLimit['IPv6'];
00120                 $prefixLen = 3; // IP::toHex output is prefixed with "v6-"
00121             } else {
00122                 $this->dieUsage( 'IP parameter is not valid', 'param_ip' );
00123             }
00124 
00125             # Check range validity, if it's a CIDR
00126             list( $ip, $range ) = IP::parseCIDR( $params['ip'] );
00127             if ( $ip !== false && $range !== false && $range < $cidrLimit ) {
00128                 $this->dieUsage(
00129                     "$type CIDR ranges broader than /$cidrLimit are not accepted",
00130                     'cidrtoobroad'
00131                 );
00132             }
00133 
00134             # Let IP::parseRange handle calculating $upper, instead of duplicating the logic here.
00135             list( $lower, $upper ) = IP::parseRange( $params['ip'] );
00136 
00137             # Extract the common prefix to any rangeblock affecting this IP/CIDR
00138             $prefix = substr( $lower, 0, $prefixLen + floor( $cidrLimit / 4 ) );
00139 
00140             # Fairly hard to make a malicious SQL statement out of hex characters,
00141             # but it is good practice to add quotes
00142             $lower = $db->addQuotes( $lower );
00143             $upper = $db->addQuotes( $upper );
00144 
00145             $this->addWhere( array(
00146                 'ipb_range_start' . $db->buildLike( $prefix, $db->anyString() ),
00147                 'ipb_range_start <= ' . $lower,
00148                 'ipb_range_end >= ' . $upper,
00149                 'ipb_auto' => 0
00150             ) );
00151         }
00152 
00153         if ( !is_null( $params['show'] ) ) {
00154             $show = array_flip( $params['show'] );
00155 
00156             /* Check for conflicting parameters. */
00157             if ( ( isset( $show['account'] ) && isset( $show['!account'] ) )
00158                 || ( isset( $show['ip'] ) && isset( $show['!ip'] ) )
00159                 || ( isset( $show['range'] ) && isset( $show['!range'] ) )
00160                 || ( isset( $show['temp'] ) && isset( $show['!temp'] ) )
00161             ) {
00162                 $this->dieUsageMsg( 'show' );
00163             }
00164 
00165             $this->addWhereIf( 'ipb_user = 0', isset( $show['!account'] ) );
00166             $this->addWhereIf( 'ipb_user != 0', isset( $show['account'] ) );
00167             $this->addWhereIf( 'ipb_user != 0 OR ipb_range_end > ipb_range_start', isset( $show['!ip'] ) );
00168             $this->addWhereIf( 'ipb_user = 0 AND ipb_range_end = ipb_range_start', isset( $show['ip'] ) );
00169             $this->addWhereIf( 'ipb_expiry = ' .
00170                 $db->addQuotes( $db->getInfinity() ), isset( $show['!temp'] ) );
00171             $this->addWhereIf( 'ipb_expiry != ' .
00172                 $db->addQuotes( $db->getInfinity() ), isset( $show['temp'] ) );
00173             $this->addWhereIf( 'ipb_range_end = ipb_range_start', isset( $show['!range'] ) );
00174             $this->addWhereIf( 'ipb_range_end > ipb_range_start', isset( $show['range'] ) );
00175         }
00176 
00177         if ( !$this->getUser()->isAllowed( 'hideuser' ) ) {
00178             $this->addWhereFld( 'ipb_deleted', 0 );
00179         }
00180 
00181         // Purge expired entries on one in every 10 queries
00182         if ( !mt_rand( 0, 10 ) ) {
00183             Block::purgeExpired();
00184         }
00185 
00186         $res = $this->select( __METHOD__ );
00187 
00188         $count = 0;
00189         foreach ( $res as $row ) {
00190             if ( ++$count > $params['limit'] ) {
00191                 // We've had enough
00192                 $this->setContinueEnumParameter( 'continue', "$row->ipb_timestamp|$row->ipb_id" );
00193                 break;
00194             }
00195             $block = array();
00196             if ( $fld_id ) {
00197                 $block['id'] = $row->ipb_id;
00198             }
00199             if ( $fld_user && !$row->ipb_auto ) {
00200                 $block['user'] = $row->ipb_address;
00201             }
00202             if ( $fld_userid && !$row->ipb_auto ) {
00203                 $block['userid'] = $row->ipb_user;
00204             }
00205             if ( $fld_by ) {
00206                 $block['by'] = $row->ipb_by_text;
00207             }
00208             if ( $fld_byid ) {
00209                 $block['byid'] = $row->ipb_by;
00210             }
00211             if ( $fld_timestamp ) {
00212                 $block['timestamp'] = wfTimestamp( TS_ISO_8601, $row->ipb_timestamp );
00213             }
00214             if ( $fld_expiry ) {
00215                 $block['expiry'] = $wgContLang->formatExpiry( $row->ipb_expiry, TS_ISO_8601 );
00216             }
00217             if ( $fld_reason ) {
00218                 $block['reason'] = $row->ipb_reason;
00219             }
00220             if ( $fld_range && !$row->ipb_auto ) {
00221                 $block['rangestart'] = IP::formatHex( $row->ipb_range_start );
00222                 $block['rangeend'] = IP::formatHex( $row->ipb_range_end );
00223             }
00224             if ( $fld_flags ) {
00225                 // For clarity, these flags use the same names as their action=block counterparts
00226                 if ( $row->ipb_auto ) {
00227                     $block['automatic'] = '';
00228                 }
00229                 if ( $row->ipb_anon_only ) {
00230                     $block['anononly'] = '';
00231                 }
00232                 if ( $row->ipb_create_account ) {
00233                     $block['nocreate'] = '';
00234                 }
00235                 if ( $row->ipb_enable_autoblock ) {
00236                     $block['autoblock'] = '';
00237                 }
00238                 if ( $row->ipb_block_email ) {
00239                     $block['noemail'] = '';
00240                 }
00241                 if ( $row->ipb_deleted ) {
00242                     $block['hidden'] = '';
00243                 }
00244                 if ( $row->ipb_allow_usertalk ) {
00245                     $block['allowusertalk'] = '';
00246                 }
00247             }
00248             $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $block );
00249             if ( !$fit ) {
00250                 $this->setContinueEnumParameter( 'continue', "$row->ipb_timestamp|$row->ipb_id" );
00251                 break;
00252             }
00253         }
00254         $result->setIndexedTagName_internal( array( 'query', $this->getModuleName() ), 'block' );
00255     }
00256 
00257     protected function prepareUsername( $user ) {
00258         if ( !$user ) {
00259             $this->dieUsage( 'User parameter may not be empty', 'param_user' );
00260         }
00261         $name = User::isIP( $user )
00262             ? $user
00263             : User::getCanonicalName( $user, 'valid' );
00264         if ( $name === false ) {
00265             $this->dieUsage( "User name {$user} is not valid", 'param_user' );
00266         }
00267         $this->usernames[] = $name;
00268     }
00269 
00270     public function getAllowedParams() {
00271         return array(
00272             'start' => array(
00273                 ApiBase::PARAM_TYPE => 'timestamp'
00274             ),
00275             'end' => array(
00276                 ApiBase::PARAM_TYPE => 'timestamp',
00277             ),
00278             'dir' => array(
00279                 ApiBase::PARAM_TYPE => array(
00280                     'newer',
00281                     'older'
00282                 ),
00283                 ApiBase::PARAM_DFLT => 'older'
00284             ),
00285             'ids' => array(
00286                 ApiBase::PARAM_TYPE => 'integer',
00287                 ApiBase::PARAM_ISMULTI => true
00288             ),
00289             'users' => array(
00290                 ApiBase::PARAM_ISMULTI => true
00291             ),
00292             'ip' => null,
00293             'limit' => array(
00294                 ApiBase::PARAM_DFLT => 10,
00295                 ApiBase::PARAM_TYPE => 'limit',
00296                 ApiBase::PARAM_MIN => 1,
00297                 ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
00298                 ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2
00299             ),
00300             'prop' => array(
00301                 ApiBase::PARAM_DFLT => 'id|user|by|timestamp|expiry|reason|flags',
00302                 ApiBase::PARAM_TYPE => array(
00303                     'id',
00304                     'user',
00305                     'userid',
00306                     'by',
00307                     'byid',
00308                     'timestamp',
00309                     'expiry',
00310                     'reason',
00311                     'range',
00312                     'flags'
00313                 ),
00314                 ApiBase::PARAM_ISMULTI => true
00315             ),
00316             'show' => array(
00317                 ApiBase::PARAM_TYPE => array(
00318                     'account',
00319                     '!account',
00320                     'temp',
00321                     '!temp',
00322                     'ip',
00323                     '!ip',
00324                     'range',
00325                     '!range',
00326                 ),
00327                 ApiBase::PARAM_ISMULTI => true
00328             ),
00329             'continue' => null,
00330         );
00331     }
00332 
00333     public function getParamDescription() {
00334         $blockCIDRLimit = $this->getConfig()->get( 'BlockCIDRLimit' );
00335         $p = $this->getModulePrefix();
00336 
00337         return array(
00338             'start' => 'The timestamp to start enumerating from',
00339             'end' => 'The timestamp to stop enumerating at',
00340             'dir' => $this->getDirectionDescription( $p ),
00341             'ids' => 'List of block IDs to list (optional)',
00342             'users' => 'List of users to search for (optional)',
00343             'ip' => array(
00344                 'Get all blocks applying to this IP or CIDR range, including range blocks.',
00345                 "Cannot be used together with bkusers. CIDR ranges broader than " .
00346                     "IPv4/{$blockCIDRLimit['IPv4']} or IPv6/{$blockCIDRLimit['IPv6']} " .
00347                     "are not accepted"
00348             ),
00349             'limit' => 'The maximum amount of blocks to list',
00350             'prop' => array(
00351                 'Which properties to get',
00352                 ' id         - Adds the ID of the block',
00353                 ' user       - Adds the username of the blocked user',
00354                 ' userid     - Adds the user ID of the blocked user',
00355                 ' by         - Adds the username of the blocking user',
00356                 ' byid       - Adds the user ID of the blocking user',
00357                 ' timestamp  - Adds the timestamp of when the block was given',
00358                 ' expiry     - Adds the timestamp of when the block expires',
00359                 ' reason     - Adds the reason given for the block',
00360                 ' range      - Adds the range of IPs affected by the block',
00361                 ' flags      - Tags the ban with (autoblock, anononly, etc)',
00362             ),
00363             'show' => array(
00364                 'Show only items that meet this criteria.',
00365                 "For example, to see only indefinite blocks on IPs, set {$p}show=ip|!temp"
00366             ),
00367             'continue' => 'When more results are available, use this to continue',
00368         );
00369     }
00370 
00371     public function getDescription() {
00372         return 'List all blocked users and IP addresses.';
00373     }
00374 
00375     public function getExamples() {
00376         return array(
00377             'api.php?action=query&list=blocks',
00378             'api.php?action=query&list=blocks&bkusers=Alice|Bob'
00379         );
00380     }
00381 
00382     public function getHelpUrls() {
00383         return 'https://www.mediawiki.org/wiki/API:Blocks';
00384     }
00385 }