sync from fr.wikipedia.org
[lhc/web/wiklou.git] / includes / Block.php
1 <?php
2 /**
3 * Blocks and bans object
4 * @package MediaWiki
5 */
6
7 /**
8 * Some globals
9 */
10 define ( 'EB_KEEP_EXPIRED', 1 );
11 define ( 'EB_FOR_UPDATE', 2 );
12 define ( 'EB_RANGE_ONLY', 4 );
13
14 /**
15 * The block class
16 * All the functions in this class assume the object is either explicitly
17 * loaded or filled. It is not load-on-demand. There are no accessors.
18 *
19 * To use delete(), you only need to fill $mAddress
20 * Globals used: $wgBlockCache, $wgAutoblockExpiry
21 *
22 * @todo This could be used everywhere, but it isn't.
23 * @package MediaWiki
24 */
25 class Block
26 {
27 /* public*/ var $mAddress, $mUser, $mBy, $mReason, $mTimestamp, $mAuto, $mId, $mExpiry;
28 /* private */ var $mNetworkBits, $mIntegerAddr, $mForUpdate, $mByName;
29
30 function Block( $address = '', $user = '', $by = 0, $reason = '',
31 $timestamp = '' , $auto = 0, $expiry = '' )
32 {
33 $this->mAddress = $address;
34 $this->mUser = $user;
35 $this->mBy = $by;
36 $this->mReason = $reason;
37 $this->mTimestamp = wfTimestamp(TS_MW,$timestamp);
38 $this->mAuto = $auto;
39 if( empty( $expiry ) ) {
40 $this->mExpiry = $expiry;
41 } else {
42 $this->mExpiry = wfTimestamp( TS_MW, $expiry );
43 }
44
45 $this->mForUpdate = false;
46 $this->mByName = false;
47 $this->initialiseRange();
48 }
49
50 /*static*/ function newFromDB( $address, $user = 0, $killExpired = true )
51 {
52 $ban = new Block();
53 $ban->load( $address, $user, $killExpired );
54 return $ban;
55 }
56
57 function clear()
58 {
59 $this->mAddress = $this->mReason = $this->mTimestamp = '';
60 $this->mUser = $this->mBy = 0;
61 $this->mByName = false;
62
63 }
64
65 /**
66 * Get a ban from the DB, with either the given address or the given username
67 */
68 function load( $address = '', $user = 0, $killExpired = true )
69 {
70 global $wgDBmysql4, $wgAntiLockFlags;
71 $fname = 'Block::load';
72 wfDebug( "Block::load: '$address', '$user', $killExpired\n" );
73
74 $ret = false;
75 $killed = false;
76 if ( $this->forUpdate() ) {
77 $db =& wfGetDB( DB_MASTER );
78 if ( $wgAntiLockFlags & ALF_NO_BLOCK_LOCK ) {
79 $options = '';
80 } else {
81 $options = 'FOR UPDATE';
82 }
83 } else {
84 $db =& wfGetDB( DB_SLAVE );
85 $options = '';
86 }
87 $ipblocks = $db->tableName( 'ipblocks' );
88
89 if ( 0 == $user && $address=='' ) {
90 $sql = "SELECT * from $ipblocks $options";
91 } elseif ($address=="") {
92 $sql = "SELECT * FROM $ipblocks WHERE ipb_user={$user} $options";
93 } elseif ($user=="") {
94 $sql = "SELECT * FROM $ipblocks WHERE ipb_address='" . $db->strencode( $address ) . "' $options";
95 } elseif ( $options=='' && $wgDBmysql4 ) {
96 # If there are no optiones (e.g. FOR UPDATE), use a UNION
97 # so that the query can make efficient use of indices
98 $sql = "SELECT * FROM $ipblocks WHERE ipb_address='" . $db->strencode( $address ) .
99 "' UNION SELECT * FROM $ipblocks WHERE ipb_user={$user}";
100 } else {
101 # If there are options, a UNION can not be used, use one
102 # SELECT instead. Will do a full table scan.
103 $sql = "SELECT * FROM $ipblocks WHERE (ipb_address='" . $db->strencode( $address ) .
104 "' OR ipb_user={$user}) $options";
105 }
106
107 $res = $db->query( $sql, $fname );
108 if ( 0 == $db->numRows( $res ) ) {
109 # User is not blocked
110 $this->clear();
111 } else {
112 # Get first block
113 $row = $db->fetchObject( $res );
114 $this->initFromRow( $row );
115
116 if ( $killExpired ) {
117 # If requested, delete expired rows
118 do {
119 $killed = $this->deleteIfExpired();
120 if ( $killed ) {
121 $row = $db->fetchObject( $res );
122 if ( $row ) {
123 $this->initFromRow( $row );
124 }
125 }
126 } while ( $killed && $row );
127
128 # If there were any left after the killing finished, return true
129 if ( !$row ) {
130 $ret = false;
131 $this->clear();
132 } else {
133 $ret = true;
134 }
135 } else {
136 $ret = true;
137 }
138 }
139 $db->freeResult( $res );
140 return $ret;
141 }
142
143 function initFromRow( $row )
144 {
145 $this->mAddress = $row->ipb_address;
146 $this->mReason = $row->ipb_reason;
147 $this->mTimestamp = wfTimestamp(TS_MW,$row->ipb_timestamp);
148 $this->mUser = $row->ipb_user;
149 $this->mBy = $row->ipb_by;
150 $this->mAuto = $row->ipb_auto;
151 $this->mId = $row->ipb_id;
152 $this->mExpiry = $row->ipb_expiry ?
153 wfTimestamp(TS_MW,$row->ipb_expiry) :
154 $row->ipb_expiry;
155 if ( isset( $row->user_name ) ) {
156 $this->mByName = $row->user_name;
157 } else {
158 $this->mByName = false;
159 }
160
161 $this->initialiseRange();
162 }
163
164 function initialiseRange()
165 {
166 if ( $this->mUser == 0 ) {
167 $rangeParts = explode( '/', $this->mAddress );
168 if ( count( $rangeParts ) == 2 ) {
169 $this->mNetworkBits = $rangeParts[1];
170 } else {
171 $this->mNetworkBits = 32;
172 }
173 $this->mIntegerAddr = ip2long( $rangeParts[0] );
174 } else {
175 $this->mNetworkBits = false;
176 $this->mIntegerAddr = false;
177 }
178 }
179
180 /**
181 * Callback with a Block object for every block
182 * @return integer number of blocks;
183 */
184 /*static*/ function enumBlocks( $callback, $tag, $flags = 0 )
185 {
186 global $wgAntiLockFlags;
187
188 $block = new Block();
189 if ( $flags & EB_FOR_UPDATE ) {
190 $db =& wfGetDB( DB_MASTER );
191 if ( $wgAntiLockFlags & ALF_NO_BLOCK_LOCK ) {
192 $options = '';
193 } else {
194 $options = 'FOR UPDATE';
195 }
196 $block->forUpdate( true );
197 } else {
198 $db =& wfGetDB( DB_SLAVE );
199 $options = '';
200 }
201 if ( $flags & EB_RANGE_ONLY ) {
202 $cond = " AND ipb_address LIKE '%/%'";
203 } else {
204 $cond = '';
205 }
206
207 extract( $db->tableNames( 'ipblocks', 'user' ) );
208
209 $sql = "SELECT $ipblocks.*,user_name FROM $ipblocks,$user " .
210 "WHERE user_id=ipb_by $cond ORDER BY ipb_timestamp DESC $options";
211 $res = $db->query( $sql, 'Block::enumBans' );
212 $num_rows = $db->numRows( $res );
213
214 while ( $row = $db->fetchObject( $res ) ) {
215 $block->initFromRow( $row );
216 if ( ( $flags & EB_RANGE_ONLY ) && $block->getNetworkBits() == 32 ) {
217 continue;
218 }
219
220 if ( !( $flags & EB_KEEP_EXPIRED ) ) {
221 if ( !$block->deleteIfExpired() ) {
222 $callback( $block, $tag );
223 }
224 } else {
225 $callback( $block, $tag );
226 }
227 }
228 wfFreeResult( $res );
229 return $num_rows;
230 }
231
232 function delete()
233 {
234 $fname = 'Block::delete';
235 if (wfReadOnly()) {
236 return;
237 }
238 $dbw =& wfGetDB( DB_MASTER );
239
240 if ( $this->mAddress == '' ) {
241 $condition = array( 'ipb_id' => $this->mId );
242 } else {
243 $condition = array( 'ipb_address' => $this->mAddress );
244 }
245 $dbw->delete( 'ipblocks', $condition, $fname );
246 $this->clearCache();
247 }
248
249 function insert()
250 {
251 wfDebug( "Block::insert; timestamp {$this->mTimestamp}\n" );
252 $dbw =& wfGetDB( DB_MASTER );
253 $ipb_id = $dbw->nextSequenceValue('ipblocks_ipb_id_val');
254 $dbw->insert( 'ipblocks',
255 array(
256 'ipb_id' => $ipb_id,
257 'ipb_address' => $this->mAddress,
258 'ipb_user' => $this->mUser,
259 'ipb_by' => $this->mBy,
260 'ipb_reason' => $this->mReason,
261 'ipb_timestamp' => $dbw->timestamp($this->mTimestamp),
262 'ipb_auto' => $this->mAuto,
263 'ipb_expiry' => $this->mExpiry ?
264 $dbw->timestamp($this->mExpiry) :
265 $this->mExpiry,
266 ), 'Block::insert'
267 );
268
269 $this->clearCache();
270 }
271
272 function deleteIfExpired()
273 {
274 if ( $this->isExpired() ) {
275 wfDebug( "Block::deleteIfExpired() -- deleting\n" );
276 $this->delete();
277 return true;
278 } else {
279 wfDebug( "Block::deleteIfExpired() -- not expired\n" );
280 return false;
281 }
282 }
283
284 function isExpired()
285 {
286 wfDebug( "Block::isExpired() checking current " . wfTimestampNow() . " vs $this->mExpiry\n" );
287 if ( !$this->mExpiry ) {
288 return false;
289 } else {
290 return wfTimestampNow() > $this->mExpiry;
291 }
292 }
293
294 function isValid()
295 {
296 return $this->mAddress != '';
297 }
298
299 function updateTimestamp()
300 {
301 if ( $this->mAuto ) {
302 $this->mTimestamp = wfTimestamp();
303 $this->mExpiry = Block::getAutoblockExpiry( $this->mTimestamp );
304
305 $dbw =& wfGetDB( DB_MASTER );
306 $dbw->update( 'ipblocks',
307 array( /* SET */
308 'ipb_timestamp' => $dbw->timestamp($this->mTimestamp),
309 'ipb_expiry' => $dbw->timestamp($this->mExpiry),
310 ), array( /* WHERE */
311 'ipb_address' => $this->mAddress
312 ), 'Block::updateTimestamp'
313 );
314
315 $this->clearCache();
316 }
317 }
318
319 /* private */ function clearCache()
320 {
321 global $wgBlockCache;
322 if ( is_object( $wgBlockCache ) ) {
323 $wgBlockCache->loadFromDB();
324 }
325 }
326
327 function getIntegerAddr()
328 {
329 return $this->mIntegerAddr;
330 }
331
332 function getNetworkBits()
333 {
334 return $this->mNetworkBits;
335 }
336
337 function getByName()
338 {
339 if ( $this->mByName === false ) {
340 $this->mByName = User::whoIs( $this->mBy );
341 }
342 return $this->mByName;
343 }
344
345 function forUpdate( $x = NULL ) {
346 return wfSetVar( $this->mForUpdate, $x );
347 }
348
349 /* static */ function getAutoblockExpiry( $timestamp )
350 {
351 global $wgAutoblockExpiry;
352 return wfTimestamp( TS_MW, wfTimestamp( TS_UNIX, $timestamp ) + $wgAutoblockExpiry );
353 }
354
355 /* static */ function normaliseRange( $range )
356 {
357 $parts = explode( '/', $range );
358 if ( count( $parts ) == 2 ) {
359 $shift = 32 - $parts[1];
360 $ipint = ip2long( $parts[0] );
361 $ipint = $ipint >> $shift << $shift;
362 $newip = long2ip( $ipint );
363 $range = "$newip/{$parts[1]}";
364 }
365 return $range;
366 }
367
368 }
369 ?>