Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 146
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
ApiQueryGlobalBlocks
0.00% covered (danger)
0.00%
0 / 146
0.00% covered (danger)
0.00%
0 / 6
1122
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 90
0.00% covered (danger)
0.00%
0 / 1
756
 getCacheMode
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getAllowedParams
0.00% covered (danger)
0.00%
0 / 44
0.00% covered (danger)
0.00%
0 / 1
2
 getDB
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getExamplesMessages
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3/**
4 * Created on Nov 1, 2008
5 *
6 * GlobalBlocking extension
7 *
8 * Copyright (C) 2008 Roan Kattouw <Firstname>.<Lastname>@home.nl
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 * http://www.gnu.org/copyleft/gpl.html
24 */
25
26namespace MediaWiki\Extension\GlobalBlocking\Api;
27
28use ApiBase;
29use ApiQuery;
30use ApiQueryBase;
31use ApiResult;
32use CentralIdLookup;
33use MediaWiki\Extension\GlobalBlocking\GlobalBlocking;
34use Wikimedia\IPUtils;
35use Wikimedia\ParamValidator\ParamValidator;
36use Wikimedia\ParamValidator\TypeDef\IntegerDef;
37use Wikimedia\Rdbms\IDatabase;
38use Wikimedia\Rdbms\IExpression;
39use Wikimedia\Rdbms\LikeValue;
40
41/**
42 * Query module to enumerate all global blocks.
43 *
44 * @ingroup API
45 * @ingroup Extensions
46 */
47class ApiQueryGlobalBlocks extends ApiQueryBase {
48
49    /**
50     * @var IDatabase
51     */
52    private $globalBlockingDb;
53
54    /**
55     * @var CentralIdLookup
56     */
57    private $lookup;
58
59    /**
60     * @param ApiQuery $query
61     * @param string $moduleName
62     * @param CentralIdLookup $lookup
63     */
64    public function __construct(
65        ApiQuery $query,
66        $moduleName,
67        CentralIdLookup $lookup
68    ) {
69        parent::__construct( $query, $moduleName, 'bg' );
70        $this->lookup = $lookup;
71    }
72
73    public function execute() {
74        $params = $this->extractRequestParams();
75        $this->requireMaxOneParameter( $params, 'addresses', 'ip' );
76
77        $prop = array_flip( $params['prop'] );
78        $fld_id = isset( $prop['id'] );
79        $fld_address = isset( $prop['address'] );
80        $fld_by = isset( $prop['by'] );
81        $fld_timestamp = isset( $prop['timestamp'] );
82        $fld_expiry = isset( $prop['expiry'] );
83        $fld_reason = isset( $prop['reason'] );
84        $fld_range = isset( $prop['range'] );
85
86        $result = $this->getResult();
87        $data = [];
88
89        $this->addTables( 'globalblocks' );
90        if ( $fld_id ) {
91            $this->addFields( 'gb_id' );
92        }
93        if ( $fld_address ) {
94            $this->addFields( [ 'gb_address', 'gb_anon_only' ] );
95        }
96        if ( $fld_by ) {
97            $this->addFields( [ 'gb_by_central_id', 'gb_by_wiki' ] );
98        }
99
100        $this->addFields( 'gb_timestamp' );
101
102        if ( $fld_expiry ) {
103            $this->addFields( 'gb_expiry' );
104        }
105        if ( $fld_reason ) {
106            $this->addFields( 'gb_reason' );
107        }
108        if ( $fld_range ) {
109            $this->addFields( [ 'gb_range_start', 'gb_range_end' ] );
110        }
111
112        $dbr = $this->getDB();
113        $this->addOption( 'LIMIT', $params['limit'] + 1 );
114        $this->addWhereRange( 'gb_timestamp', $params['dir'], $params['start'], $params['end'] );
115        $this->addWhere( $dbr->expr( 'gb_expiry', '>', $dbr->timestamp() ) );
116        if ( isset( $params['ids'] ) ) {
117            $this->addWhereFld( 'gb_id', $params['ids'] );
118        }
119        if ( isset( $params['addresses'] ) ) {
120            $addresses = [];
121            foreach ( (array)$params['addresses'] as $address ) {
122                if ( !IPUtils::isIPAddress( $address ) ) {
123                    $this->dieWithError(
124                        [ 'globalblocking-apierror-badip', wfEscapeWikiText( $address ) ],
125                        'param_addresses'
126                    );
127                }
128                $addresses[] = $address;
129            }
130            $this->addWhereFld( 'gb_address', $addresses );
131        }
132        if ( isset( $params['ip'] ) ) {
133            if ( IPUtils::isIPv4( $params['ip'] ) ) {
134                $type = 'IPv4';
135                $cidrLimit = 16; // @todo Make this configurable
136                $prefixLen = 0;
137            } elseif ( IPUtils::isIPv6( $params['ip'] ) ) {
138                $type = 'IPv6';
139                $cidrLimit = 16; // @todo Make this configurable
140                $prefixLen = 3; // IPUtils::toHex output is prefixed with "v6-"
141            } else {
142                $this->dieWithError( 'apierror-badip', 'param_ip' );
143            }
144
145            # Check range validity, if it's a CIDR
146            [ $ip, $range ] = IPUtils::parseCIDR( $params['ip'] );
147            if ( $ip !== false && $range !== false && $range < $cidrLimit ) {
148                $this->dieWithError( [ 'apierror-cidrtoobroad', $type, $cidrLimit ] );
149            }
150
151            # Let IPUtils::parseRange handle calculating $upper, instead of duplicating the logic here.
152            [ $lower, $upper ] = IPUtils::parseRange( $params['ip'] );
153
154            # Extract the common prefix to any rangeblock affecting this IP/CIDR
155            $prefix = substr( $lower, 0, $prefixLen + $cidrLimit / 4 );
156
157            $this->addWhere( [
158                $dbr->expr( 'gb_range_start', IExpression::LIKE, new LikeValue( $prefix, $dbr->anyString() ) ),
159                $dbr->expr( 'gb_range_start', '<=', $lower ),
160                $dbr->expr( 'gb_range_end', '>=', $upper ),
161            ] );
162        }
163
164        $res = $this->select( __METHOD__ );
165
166        $count = 0;
167        foreach ( $res as $row ) {
168            if ( ++$count > $params['limit'] ) {
169                // We've had enough
170                $this->setContinueEnumParameter( 'start', wfTimestamp( TS_ISO_8601, $row->gb_timestamp ) );
171                break;
172            }
173            $block = [];
174            if ( $fld_id ) {
175                $block['id'] = $row->gb_id;
176            }
177            if ( $fld_address ) {
178                $block['address'] = $row->gb_address;
179                if ( $row->gb_anon_only ) {
180                    $block['anononly'] = '';
181                }
182            }
183            if ( $fld_by ) {
184                $block['by'] = $this->lookup->nameFromCentralId( $row->gb_by_central_id );
185                $block['bywiki'] = $row->gb_by_wiki;
186            }
187            if ( $fld_timestamp ) {
188                $block['timestamp'] = wfTimestamp( TS_ISO_8601, $row->gb_timestamp );
189            }
190            if ( $fld_expiry ) {
191                $block['expiry'] = ApiResult::formatExpiry( $row->gb_expiry );
192            }
193            if ( $fld_reason ) {
194                $block['reason'] = $row->gb_reason;
195            }
196            if ( $fld_range ) {
197                $block['rangestart'] = IPUtils::hexToQuad( $row->gb_range_start );
198                $block['rangeend'] = IPUtils::hexToQuad( $row->gb_range_end );
199            }
200            $data[] = $block;
201        }
202        $result->setIndexedTagName( $data, 'block' );
203        $result->addValue( 'query', $this->getModuleName(), $data );
204    }
205
206    public function getCacheMode( $params ) {
207        return 'public';
208    }
209
210    public function getAllowedParams() {
211        return [
212            'start' => [
213                ParamValidator::PARAM_TYPE => 'timestamp'
214            ],
215            'end' => [
216                ParamValidator::PARAM_TYPE => 'timestamp',
217            ],
218            'dir' => [
219                ParamValidator::PARAM_TYPE => [
220                    'newer',
221                    'older'
222                ],
223                ParamValidator::PARAM_DEFAULT => 'older',
224                ApiBase::PARAM_HELP_MSG => 'api-help-param-direction',
225            ],
226            'ids' => [
227                ParamValidator::PARAM_TYPE => 'integer',
228                ParamValidator::PARAM_ISMULTI => true
229            ],
230            'addresses' => [
231                ParamValidator::PARAM_ISMULTI => true
232            ],
233            'ip' => null,
234            'limit' => [
235                ParamValidator::PARAM_DEFAULT => 10,
236                ParamValidator::PARAM_TYPE => 'limit',
237                IntegerDef::PARAM_MIN => 1,
238                IntegerDef::PARAM_MAX => ApiBase::LIMIT_BIG1,
239                IntegerDef::PARAM_MAX2 => ApiBase::LIMIT_BIG2
240            ],
241            'prop' => [
242                ParamValidator::PARAM_DEFAULT => 'id|address|by|timestamp|expiry|reason',
243                ParamValidator::PARAM_TYPE => [
244                    'id',
245                    'address',
246                    'by',
247                    'timestamp',
248                    'expiry',
249                    'reason',
250                    'range',
251                ],
252                ParamValidator::PARAM_ISMULTI => true
253            ]
254        ];
255    }
256
257    protected function getDB() {
258        if ( $this->globalBlockingDb === null ) {
259            $this->globalBlockingDb = GlobalBlocking::getReplicaGlobalBlockingDatabase();
260        }
261        return $this->globalBlockingDb;
262    }
263
264    /**
265     * @see ApiBase::getExamplesMessages()
266     * @return array
267     */
268    protected function getExamplesMessages() {
269        return [
270            'action=query&list=globalblocks'
271                => 'apihelp-query+globalblocks-example-1',
272            'action=query&list=globalblocks&bgip=192.0.2.18'
273                => 'apihelp-query+globalblocks-example-2',
274        ];
275    }
276}