--help ftw !
[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 $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=='' ) {
96 # If there are no options (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 $now = wfTimestampNow();
208
209 extract( $db->tableNames( 'ipblocks', 'user' ) );
210
211 $sql = "SELECT $ipblocks.*,user_name FROM $ipblocks,$user " .
212 "WHERE user_id=ipb_by $cond ORDER BY ipb_timestamp DESC $options";
213 $res = $db->query( $sql, 'Block::enumBlocks' );
214 $num_rows = $db->numRows( $res );
215
216 while ( $row = $db->fetchObject( $res ) ) {
217 $block->initFromRow( $row );
218 if ( ( $flags & EB_RANGE_ONLY ) && $block->getNetworkBits() == 32 ) {
219 continue;
220 }
221
222 if ( !( $flags & EB_KEEP_EXPIRED ) ) {
223 if ( $block->mExpiry && $now > $block->mExpiry ) {
224 $block->delete();
225 } else {
226 call_user_func( $callback, $block, $tag );
227 }
228 } else {
229 call_user_func( $callback, $block, $tag );
230 }
231 }
232 wfFreeResult( $res );
233 return $num_rows;
234 }
235
236 function delete()
237 {
238 $fname = 'Block::delete';
239 if (wfReadOnly()) {
240 return;
241 }
242 $dbw =& wfGetDB( DB_MASTER );
243
244 if ( $this->mAddress == '' ) {
245 $condition = array( 'ipb_id' => $this->mId );
246 } else {
247 $condition = array( 'ipb_address' => $this->mAddress );
248 }
249 $dbw->delete( 'ipblocks', $condition, $fname );
250 $this->clearCache();
251 }
252
253 function insert()
254 {
255 wfDebug( "Block::insert; timestamp {$this->mTimestamp}\n" );
256 $dbw =& wfGetDB( DB_MASTER );
257 $ipb_id = $dbw->nextSequenceValue('ipblocks_ipb_id_val');
258 $dbw->insert( 'ipblocks',
259 array(
260 'ipb_id' => $ipb_id,
261 'ipb_address' => $this->mAddress,
262 'ipb_user' => $this->mUser,
263 'ipb_by' => $this->mBy,
264 'ipb_reason' => $this->mReason,
265 'ipb_timestamp' => $dbw->timestamp($this->mTimestamp),
266 'ipb_auto' => $this->mAuto,
267 'ipb_expiry' => $this->mExpiry ?
268 $dbw->timestamp($this->mExpiry) :
269 $this->mExpiry,
270 ), 'Block::insert'
271 );
272
273 $this->clearCache();
274 }
275
276 function deleteIfExpired()
277 {
278 $fname = 'Block::deleteIfExpired';
279 wfProfileIn( $fname );
280 if ( $this->isExpired() ) {
281 wfDebug( "Block::deleteIfExpired() -- deleting\n" );
282 $this->delete();
283 $retVal = true;
284 } else {
285 wfDebug( "Block::deleteIfExpired() -- not expired\n" );
286 $retVal = false;
287 }
288 wfProfileOut( $fname );
289 return $retVal;
290 }
291
292 function isExpired()
293 {
294 wfDebug( "Block::isExpired() checking current " . wfTimestampNow() . " vs $this->mExpiry\n" );
295 if ( !$this->mExpiry ) {
296 return false;
297 } else {
298 return wfTimestampNow() > $this->mExpiry;
299 }
300 }
301
302 function isValid()
303 {
304 return $this->mAddress != '';
305 }
306
307 function updateTimestamp()
308 {
309 if ( $this->mAuto ) {
310 $this->mTimestamp = wfTimestamp();
311 $this->mExpiry = Block::getAutoblockExpiry( $this->mTimestamp );
312
313 $dbw =& wfGetDB( DB_MASTER );
314 $dbw->update( 'ipblocks',
315 array( /* SET */
316 'ipb_timestamp' => $dbw->timestamp($this->mTimestamp),
317 'ipb_expiry' => $dbw->timestamp($this->mExpiry),
318 ), array( /* WHERE */
319 'ipb_address' => $this->mAddress
320 ), 'Block::updateTimestamp'
321 );
322
323 $this->clearCache();
324 }
325 }
326
327 /* private */ function clearCache()
328 {
329 global $wgBlockCache;
330 if ( is_object( $wgBlockCache ) ) {
331 $wgBlockCache->loadFromDB();
332 }
333 }
334
335 function getIntegerAddr()
336 {
337 return $this->mIntegerAddr;
338 }
339
340 function getNetworkBits()
341 {
342 return $this->mNetworkBits;
343 }
344
345 function getByName()
346 {
347 if ( $this->mByName === false ) {
348 $this->mByName = User::whoIs( $this->mBy );
349 }
350 return $this->mByName;
351 }
352
353 function forUpdate( $x = NULL ) {
354 return wfSetVar( $this->mForUpdate, $x );
355 }
356
357 /* static */ function getAutoblockExpiry( $timestamp )
358 {
359 global $wgAutoblockExpiry;
360 return wfTimestamp( TS_MW, wfTimestamp( TS_UNIX, $timestamp ) + $wgAutoblockExpiry );
361 }
362
363 /* static */ function normaliseRange( $range )
364 {
365 $parts = explode( '/', $range );
366 if ( count( $parts ) == 2 ) {
367 $shift = 32 - $parts[1];
368 $ipint = ip2long( $parts[0] );
369 $ipint = $ipint >> $shift << $shift;
370 $newip = long2ip( $ipint );
371 $range = "$newip/{$parts[1]}";
372 }
373 return $range;
374 }
375
376 }
377 ?>