Last modified: 2006-12-25 04:25:28 UTC

Wikimedia Bugzilla is closed!

Wikimedia migrated from Bugzilla to Phabricator. Bug reports are handled in Wikimedia Phabricator.
This static website is read-only and for historical purposes. It is not possible to log in and except for displaying bug reports and their history, links might be broken. See T6627, the corresponding Phabricator task for complete and up-to-date bug report information.
Bug 4627 - User summary on IP checks
User summary on IP checks
Status: RESOLVED FIXED
Product: MediaWiki extensions
Classification: Unclassified
CheckUser (Other open bugs)
unspecified
All All
: Normal enhancement with 4 votes (vote)
: ---
Assigned To: Nobody - You can work on this!
:
: 5979 (view as bug list)
Depends on:
Blocks:
  Show dependency treegraph
 
Reported: 2006-01-15 21:24 UTC by Kelly Martin
Modified: 2006-12-25 04:25 UTC (History)
1 user (show)

See Also:
Web browser: ---
Mobile Platform: ---
Assignee Huggle Beta Tester: ---


Attachments
A patch to add the list of users that used an IP and an autoblock checker (9.92 KB, patch)
2006-12-02 04:19 UTC, Aaron Schulz
Details
update; a bit shorter, log wording change (9.81 KB, patch)
2006-12-02 07:14 UTC, Aaron Schulz
Details
Autoblock IPs listed with link to checkip (9.81 KB, patch)
2006-12-02 18:13 UTC, Aaron Schulz
Details
"list users only" option, replaced a useless extra option with a reason box (8.69 KB, text/plain)
2006-12-03 01:51 UTC, Aaron Schulz
Details

Description Kelly Martin 2006-01-15 21:24:10 UTC
When doing an IP check, it would be nice if there was a summary listing (top or
bottom) that listed all the users who edited from that IP.  Right now I have to
read through the listing and it's easy to miss one, especially on high traffic IPs.
Comment 1 David Gerard 2006-01-31 12:52:35 UTC
Seconded. It's very easy to miss on a busy listing or on an IP range check.
Comment 2 Aaron Schulz 2006-12-02 04:19:59 UTC
Created attachment 2806 [details]
A patch to add the list of users that used an IP and an autoblock checker

See [http://www.mediawiki.org/wiki/User:Voice_of_All/CheckUser2.0].

I am working on adding anything useful and not too expensive that CheckUser
could use. I'd appretiate any suggestions.
Comment 3 Aaron Schulz 2006-12-02 04:24:40 UTC
*** Bug 5979 has been marked as a duplicate of this bug. ***
Comment 4 Aaron Schulz 2006-12-02 05:08:32 UTC
Comment on attachment 2806 [details]
A patch to add the list of users that used an IP and an autoblock checker

<?php

if ( !defined( 'MEDIAWIKI' ) ) {
    echo "CheckUser extension\n";
    exit( 1 );
}

# Add messages
global $wgMessageCache, $wgCheckUserMessages;
foreach( $wgCheckUserMessages as $key => $value ) {
	$wgMessageCache->addMessages( $wgCheckUserMessages[$key], $key );
}

class CheckUser extends SpecialPage
{
	function CheckUser() {
		SpecialPage::SpecialPage('CheckUser', 'checkuser');
	}

	function execute( $par ) {
		global $wgRequest, $wgOut, $wgTitle, $wgUser;

		if( !$wgUser->isAllowed( 'checkuser' ) ) {
			$wgOut->permissionRequired( 'checkuser' );
			return;
		}

		$this->setHeaders();

		$ip = $wgRequest->getText( 'ip' );
		$user = $wgRequest->getText( 'user' );
		$autoblock = $wgRequest->getText( 'autoblock' );
		$subip = $wgRequest->getBool( 'subip' );
		$subuser = $wgRequest->getBool( 'subuser' );
		$subautoblock = $wgRequest->getBool( 'subautoblock' );
		$wpNamesOnly = $wgRequest->getBool( 'wpNamesOnly' );

		$this->doTop( $ip, $user, $autoblock);
		if ( $subip ) {
			$this->doIPRequest( $ip, $wpNamesOnly);
		} else if ( $subuser ) {
			$this->doUserRequest( $user );
		} else if ( $subautoblock ) {
			$this->doAutoblockRequest( $autoblock );
		} else {
			$this->showLog();
		}
	}

	function doTop( $ip, $user, $autoblock ) {
		global $wgOut, $wgTitle;

		$action = $wgTitle->escapeLocalUrl();
		$encIp = htmlspecialchars( $ip );
		$encUser = htmlspecialchars( $user );
		$encAutoblock = htmlspecialchars( $autoblock );

		$wgOut->addHTML( wfmsg('checkuserheader'));
		$wgOut->addHTML( <<<EOT
<table border=0 cellpadding=5>
<form name="checkuser" action="$action" method=post>
<tr><td>
	IP address:
</td><td>
	<input type="text" name="ip" value="$encIp" width=50 /> <input
type="submit" name="subip" value="OK" />
	<br><input name="wpNamesOnly" type="checkbox" value="1"
id="wpNamesOnly" tabindex="4" /><label for="wpNamesOnly">List usernames
only</label>
</td></tr>
</form>

<form name="checkuser" action="$action" method=post>
<tr><td>
	Username:
</td><td>
	<input type="text" name="user" value="$encUser" width=50 /> <input
type="submit" name="subuser" value="OK" />
</td></tr>

<form name="checkuser" action="$action" method=post>
<tr><td>
	Autoblock#:
</td><td>
	<input type="text" name="autoblock" value="$encAutoblock" width=50 />
<input type="submit" name="subautoblock" value="OK" />
</td></tr>
</form>
</table>
EOT
		);
	}

	function doAutoblockRequest( $autoblock ) {
		global $wgUser, $wgOut, $wgLang, $wgTitle, $wgDBname;
		$fname = 'CheckUser::doAutoblockRequest';

		if ( !$this->addLogEntry( $wgLang->timeanddate(
wfTimestampNow() ) . ' ' .
		  $wgUser->getName() . ' got IP for autoblock #' .
htmlspecialchars( $autoblock ) . ' on ' . $wgDBname )) 
		{
			$wgOut->addHTML( '<p>Unable to add log entry</p>' );
		}

		$dbr =& wfGetDB( DB_SLAVE );
		$res = $dbr->select( 'ipblocks', array( 'ipb_address' ), array(
'ipb_id' => $autoblock , 'ipb_auto' => 1), $fname );
		if (!$dbr->numRows( $res )) {
			$s = "No results, the block may have expired\n";
		} else {
		    global $IP;
			require_once( $IP.'/includes/RecentChange.php' );
			require_once( $IP.'/includes/ChangesList.php' );
		    $row = $dbr->fetchObject($res);
		    $s = '<ul>';
		    $s .= '<li><a href="' . $wgTitle->escapeLocalURL( 'user=' .
urlencode( $row->ipb_address ) ) . '">' .
					htmlspecialchars( $row->ipb_address ) .
'</a></li>';
			$s .= '</ul>';
		}
		$wgOut->addHTML( $s );
		$dbr->freeResult( $res );
	}

	function doIPRequest( $ip, $wpNamesOnly) {
		global $wgUser, $wgOut, $wgLang, $wgTitle, $wgDBname;
		$fname = 'CheckUser::doIPRequest';
		if ($wpNamesOnly) $type='users';
		else $type='edits';

		if ( !$this->addLogEntry( $wgLang->timeanddate(
wfTimestampNow() ) . ' ' .
		  $wgUser->getName() . ' got ' . $type . ' for ' .
htmlspecialchars( $ip ) . ' on ' . $wgDBname ))
		{
			$wgOut->addHTML( '<p>Unable to add log entry</p>' );
		}

		$dbr =& wfGetDB( DB_SLAVE );
		if (!$wpNamesOnly) {
		$res = $dbr->select( 'recentchanges', array( '*' ),
$this->getIpConds( $dbr, $ip ), $fname, 
			array( 'ORDER BY' => 'rc_timestamp DESC' ) );
		} else { $res = $dbr->select( 'recentchanges', array(
'rc_user_text' ), $this->getIpConds( $dbr, $ip ), $fname, 
			array( 'ORDER BY' => 'rc_timestamp DESC' ) );
		}

		if ( !$dbr->numRows( $res ) ) {
			$s =  "No results\n";
		} else if ( !$wpNamesOnly ) {
			global $IP;
			require_once( $IP.'/includes/RecentChange.php' );
			require_once( $IP.'/includes/ChangesList.php' );

			if ( in_array( 'newfromuser', array_map( 'strtolower',
get_class_methods( 'ChangesList' ) ) ) ) {
				// MW >= 1.6
				$list = ChangesList::newFromUser( $wgUser );
			} else {
				// MW < 1.6
				$sk =& $wgUser->getSkin();
				$list = new ChangesList( $sk );
			}
			$s = $list->beginRecentChangesList();
			$counter = 1;
			while ( ($row = $dbr->fetchObject( $res ) ) != false )
{
				$rc = RecentChange::newFromRow( $row );
				$rc->counter = $counter++;
				$s .= $list->recentChangesLine( $rc, false );
			}
			$s .= $list->endRecentChangesList();
		} else {
		$uniqueUsers = array();
		$u_count = 0;
		$wgOut->addHTML( '<ul>' );
			while ( ($row = $dbr->fetchObject( $res ) ) != false )
{
			    if (!in_array( $row->rc_user_text, $uniqueUsers ))
{
			    $uniqueUsers[$u_count]=$row->rc_user_text;
			    $u_count++;
				$s .= '<li><a href="' .
$wgTitle->escapeLocalURL( 'user=' . urlencode( $row->rc_user_text ) ) . '">' .
					htmlspecialchars( $row->rc_user_text )
. '</a></li>';
				}
			}
		}
		$wgOut->addHTML( $s );
		$wgOut->addHTML( '</ul>' );
		$dbr->freeResult( $res );
	}

	/**
	 * Since we have stuff stored in text format, this only works easily
	 * for some simple cases, such as /16 and /24.
	 * @param Database $db
	 * @param string $ip
	 * @return array conditions
	 */
	function getIpConds( $db, $ip ) {
		// haaaack
		if( preg_match( '#^(\d+)\.(\d+)\.(\d+)\.(\d+)/(\d+)$#', $ip,
$matches ) ) {
			list( $junk, $a, $b, $c, $d, $bits ) = $matches;
			if( $bits == 32 ) {
				$match = "$a.$b.$c.$d";
			} elseif( $bits == 24 ) {
				$match = "$a.$b.$c.%";
			} elseif( $bits == 16 ) {
				$match = "$a.$b.%";
			} else {
				// Other sizes not supported. /8 is too big
				$match = $ip;
			}
			return array( 'rc_ip LIKE ' . $db->addQuotes( $match )
);
		} else {
			return array( 'rc_ip' => $ip );
		}
	}

	function doUserRequest( $user ) {
		global $wgOut, $wgTitle, $wgLang, $wgUser, $wgDBname;
		$fname = 'CheckUser::doUserRequest';

		$userTitle = Title::newFromText( $user, NS_USER );
		if( !is_null( $userTitle ) ) {
			// normalize the username
			$user = $userTitle->getText();
		}

		if ( !$this->addLogEntry( $wgLang->timeanddate(
wfTimestampNow() ) . ' ' .
		  $wgUser->getName() . ' got IPs for ' . htmlspecialchars(
$user ) . ' on ' . $wgDBname ) ) 
		{
			$wgOut->addHTML( '<p>Unable to add log entry</p>' );
		}

		$dbr =& wfGetDB( DB_SLAVE );
		$res = $dbr->select( 'recentchanges', array( 'DISTINCT rc_ip'
), array( 'rc_user_text' => $user ), $fname );
		if ( !$dbr->numRows( $res ) ) {
			$s =  "No results\n";
		} else {
			$s = '<ul>';
			while ( ($row = $dbr->fetchObject( $res ) ) != false )
{
				$s .= '<li><a href="' .
$wgTitle->escapeLocalURL( 'ip=' . urlencode( $row->rc_ip ) ) . '">' .
					htmlspecialchars( $row->rc_ip ) .
'</a></li>';
			}
			$s .= '</ul>';
		}
		$wgOut->addHTML( $s );
	}

	function showLog() {
		global $wgCheckUserLog;

		if( $wgCheckUserLog === false || !file_exists( $wgCheckUserLog
) ) {
			# No log
			return;
		} else {
			global $wgRequest, $wgOut;

			if( $wgRequest->getVal( 'log' ) == 1 ) {
				# Show the log
				list( $limit, $offset ) = wfCheckLimits();
				$log = $this->tail( $wgCheckUserLog, $limit,
$offset );
				if( !!$log ) {

					$scroller = wfViewPrevNext( $offset,
$limit,
						Title::makeTitle( NS_SPECIAL,
'CheckUser' ),
						'log=1',
						count( $log ) < $limit );

					$output = implode( "\n", $log );
					$wgOut->addHTML(
"$scroller\n<ul>$output</ul>\n$scroller\n" );
				} else {
					$wgOut->addHTML( "<p>The log contains
no items.</p>" );
				}
			} else {
				# Hide the log, show a link
				global $wgTitle, $wgUser;
				$skin = $wgUser->getSkin();
				$link = $skin->makeKnownLinkObj( $wgTitle,
'Show log', 'log=1' );
				$wgOut->addHTML( "<p>$link</p>" );
			}
		}
	}

	function tail( $filename, $limit, $offset ) {
		//wfSuppressWarnings();
		$file = fopen( $filename, "rt" );
		//wfRestoreWarnings();

		if( $file === false ) {
			return false;
		}

		$filePosition = filesize( $filename );
		if( $filePosition == 0 ) {
			return array();
		}

		$lines = array();
		$bufSize = 1024;
		$lineCount = 0;
		$total = $offset + $limit;
		$leftover = '';
		do {
			if( $filePosition < $bufSize ) {
				$bufSize = $filePosition;
			}
			$filePosition -= $bufSize;
			fseek( $file, $filePosition );
			$buffer = fread( $file, $bufSize );

			$parts = explode( "\n", $buffer );
			$num = count( $parts );

			if( $num > 0 ) {
				if( $lineCount++ > $offset ) {
					$lines[] = $parts[$num - 1] .
$leftover;
					if( $lineCount > $total ) {
						return $lines;
					}
				}
			}
			for( $i = $num - 2; $i > 0; $i-- ) {
				if( $lineCount++ > $offset ) {
					$lines[] = $parts[$i];
					if( $lineCount > $total ) {
						fclose( $file );
						return $lines;
					}
				}
			}
			if( $num > 1 ) {
				$leftover = $parts[0];
			} else {
				$leftover = '';
				break;
			}
		} while( $filePosition > 0 );

		if( $lineCount++ > $offset ) {
			$lines[] = $leftover;
		}
		fclose( $file );
		return $lines;
	}

	function addLogEntry( $entry ) {
		global $wgUser, $wgCheckUserLog;
		if ( $wgCheckUserLog === false ) {
			// No log required, this is not an error
			return true;
		}

		$f = fopen( $wgCheckUserLog, 'a' );
		if ( !$f ) {
			return false;
		}
		if ( !fwrite( $f, "<li>$entry</li>\n" ) ) {
			return false;
		}
		fclose( $f );
		return true;
	}
}
?>
Comment 5 Aaron Schulz 2006-12-02 05:10:11 UTC
Comment on attachment 2806 [details]
A patch to add the list of users that used an IP and an autoblock checker

<?php

if ( !defined( 'MEDIAWIKI' ) ) {
    echo "CheckUser extension\n";
    exit( 1 );
}

# Add messages
global $wgMessageCache, $wgCheckUserMessages;
foreach( $wgCheckUserMessages as $key => $value ) {
	$wgMessageCache->addMessages( $wgCheckUserMessages[$key], $key );
}

class CheckUser extends SpecialPage
{
	function CheckUser() {
		SpecialPage::SpecialPage('CheckUser', 'checkuser');
	}

	function execute( $par ) {
		global $wgRequest, $wgOut, $wgTitle, $wgUser;

		if( !$wgUser->isAllowed( 'checkuser' ) ) {
			$wgOut->permissionRequired( 'checkuser' );
			return;
		}

		$this->setHeaders();

		$ip = $wgRequest->getText( 'ip' );
		$user = $wgRequest->getText( 'user' );
		$autoblock = $wgRequest->getText( 'autoblock' );
		$subip = $wgRequest->getBool( 'subip' );
		$subuser = $wgRequest->getBool( 'subuser' );
		$subautoblock = $wgRequest->getBool( 'subautoblock' );
		$wpNamesOnly = $wgRequest->getBool( 'wpNamesOnly' );

		$this->doTop( $ip, $user, $autoblock);
		if ( $subip ) {
			$this->doIPRequest( $ip, $wpNamesOnly);
		} else if ( $subuser ) {
			$this->doUserRequest( $user );
		} else if ( $subautoblock ) {
			$this->doAutoblockRequest( $autoblock );
		} else {
			$this->showLog();
		}
	}

	function doTop( $ip, $user, $autoblock ) {
		global $wgOut, $wgTitle;

		$action = $wgTitle->escapeLocalUrl();
		$encIp = htmlspecialchars( $ip );
		$encUser = htmlspecialchars( $user );
		$encAutoblock = htmlspecialchars( $autoblock );

		$wgOut->addHTML( wfmsg('checkuserheader'));
		$wgOut->addHTML( <<<EOT
<table border=0 cellpadding=5>
<form name="checkuser" action="$action" method=post>
<tr><td>
	IP address:
</td><td>
	<input type="text" name="ip" value="$encIp" width=50 /> <input
type="submit" name="subip" value="OK" />
	<br><input name="wpNamesOnly" type="checkbox" value="1"
id="wpNamesOnly" tabindex="4" /><label for="wpNamesOnly">List usernames
only</label>
</td></tr>
</form>

<form name="checkuser" action="$action" method=post>
<tr><td>
	Username:
</td><td>
	<input type="text" name="user" value="$encUser" width=50 /> <input
type="submit" name="subuser" value="OK" />
</td></tr>

<form name="checkuser" action="$action" method=post>
<tr><td>
	Autoblock#:
</td><td>
	<input type="text" name="autoblock" value="$encAutoblock" width=50 />
<input type="submit" name="subautoblock" value="OK" />
</td></tr>
</form>
</table>
EOT
		);
	}

	function doAutoblockRequest( $autoblock ) {
		global $wgUser, $wgOut, $wgLang, $wgTitle, $wgDBname;
		$fname = 'CheckUser::doAutoblockRequest';

		if ( !$this->addLogEntry( $wgLang->timeanddate(
wfTimestampNow() ) . ' ' .
		  $wgUser->getName() . ' got IP for autoblock #' .
htmlspecialchars( $autoblock ) . ' on ' . $wgDBname )) 
		{
			$wgOut->addHTML( '<p>Unable to add log entry</p>' );
		}

		$dbr =& wfGetDB( DB_SLAVE );
		$res = $dbr->select( 'ipblocks', array( 'ipb_address' ), array(
'ipb_id' => $autoblock , 'ipb_auto' => 1), $fname );
		if (!$dbr->numRows( $res )) {
			$s = "No results, the block may have expired\n";
		} else {
		    global $IP;
			require_once( $IP.'/includes/RecentChange.php' );
			require_once( $IP.'/includes/ChangesList.php' );
		    $row = $dbr->fetchObject($res);
		    $s = '<ul>';
		    $s .= '<li><a href="' . $wgTitle->escapeLocalURL( 'user=' .
urlencode( $row->ipb_address ) ) . '">' .
					htmlspecialchars( $row->ipb_address ) .
'</a></li>';
			$s .= '</ul>';
		}
		$wgOut->addHTML( $s );
		$dbr->freeResult( $res );
	}

	function doIPRequest( $ip, $wpNamesOnly) {
		global $wgUser, $wgOut, $wgLang, $wgTitle, $wgDBname;
		$fname = 'CheckUser::doIPRequest';
		if ($wpNamesOnly) $type='users';
		else $type='edits';

		if ( !$this->addLogEntry( $wgLang->timeanddate(
wfTimestampNow() ) . ' ' .
		  $wgUser->getName() . ' got ' . $type . ' for ' .
htmlspecialchars( $ip ) . ' on ' . $wgDBname ))
		{
			$wgOut->addHTML( '<p>Unable to add log entry</p>' );
		}

		$dbr =& wfGetDB( DB_SLAVE );
		if (!$wpNamesOnly) {
		$res = $dbr->select( 'recentchanges', array( '*' ),
$this->getIpConds( $dbr, $ip ), $fname, 
			array( 'ORDER BY' => 'rc_timestamp DESC' ) );
		} else { $res = $dbr->select( 'recentchanges', array(
'rc_user_text' ), $this->getIpConds( $dbr, $ip ), $fname, 
			array( 'ORDER BY' => 'rc_timestamp DESC' ) );
		}

		if ( !$dbr->numRows( $res ) ) {
			$s =  "No results\n";
		} else if ( !$wpNamesOnly ) {
			global $IP;
			require_once( $IP.'/includes/RecentChange.php' );
			require_once( $IP.'/includes/ChangesList.php' );

			if ( in_array( 'newfromuser', array_map( 'strtolower',
get_class_methods( 'ChangesList' ) ) ) ) {
				// MW >= 1.6
				$list = ChangesList::newFromUser( $wgUser );
			} else {
				// MW < 1.6
				$sk =& $wgUser->getSkin();
				$list = new ChangesList( $sk );
			}
			$s = $list->beginRecentChangesList();
			$counter = 1;
			while ( ($row = $dbr->fetchObject( $res ) ) != false )
{
				$rc = RecentChange::newFromRow( $row );
				$rc->counter = $counter++;
				$s .= $list->recentChangesLine( $rc, false );
			}
			$s .= $list->endRecentChangesList();
		} else {
		$uniqueUsers = array();
		$u_count = 0;
		$wgOut->addHTML( '<ul>' );
			while ( ($row = $dbr->fetchObject( $res ) ) != false )
{
			    if (!in_array( $row->rc_user_text, $uniqueUsers ))
{
			    $uniqueUsers[$u_count]=$row->rc_user_text;
			    $u_count++;
				$s .= '<li><a href="' .
$wgTitle->escapeLocalURL( 'user=' . urlencode( $row->rc_user_text ) ) . '">' .
					htmlspecialchars( $row->rc_user_text )
. '</a></li>';
				}
			}
		}
		$wgOut->addHTML( $s );
		$wgOut->addHTML( '</ul>' );
		$dbr->freeResult( $res );
	}

	/**
	 * Since we have stuff stored in text format, this only works easily
	 * for some simple cases, such as /16 and /24.
	 * @param Database $db
	 * @param string $ip
	 * @return array conditions
	 */
	function getIpConds( $db, $ip ) {
		// haaaack
		if( preg_match( '#^(\d+)\.(\d+)\.(\d+)\.(\d+)/(\d+)$#', $ip,
$matches ) ) {
			list( $junk, $a, $b, $c, $d, $bits ) = $matches;
			if( $bits == 32 ) {
				$match = "$a.$b.$c.$d";
			} elseif( $bits == 24 ) {
				$match = "$a.$b.$c.%";
			} elseif( $bits == 16 ) {
				$match = "$a.$b.%";
			} else {
				// Other sizes not supported. /8 is too big
				$match = $ip;
			}
			return array( 'rc_ip LIKE ' . $db->addQuotes( $match )
);
		} else {
			return array( 'rc_ip' => $ip );
		}
	}

	function doUserRequest( $user ) {
		global $wgOut, $wgTitle, $wgLang, $wgUser, $wgDBname;
		$fname = 'CheckUser::doUserRequest';

		$userTitle = Title::newFromText( $user, NS_USER );
		if( !is_null( $userTitle ) ) {
			// normalize the username
			$user = $userTitle->getText();
		}

		if ( !$this->addLogEntry( $wgLang->timeanddate(
wfTimestampNow() ) . ' ' .
		  $wgUser->getName() . ' got IPs for ' . htmlspecialchars(
$user ) . ' on ' . $wgDBname ) ) 
		{
			$wgOut->addHTML( '<p>Unable to add log entry</p>' );
		}

		$dbr =& wfGetDB( DB_SLAVE );
		$res = $dbr->select( 'recentchanges', array( 'DISTINCT rc_ip'
), array( 'rc_user_text' => $user ), $fname );
		if ( !$dbr->numRows( $res ) ) {
			$s =  "No results\n";
		} else {
			$s = '<ul>';
			while ( ($row = $dbr->fetchObject( $res ) ) != false )
{
				$s .= '<li><a href="' .
$wgTitle->escapeLocalURL( 'ip=' . urlencode( $row->rc_ip ) ) . '">' .
					htmlspecialchars( $row->rc_ip ) .
'</a></li>';
			}
			$s .= '</ul>';
		}
		$wgOut->addHTML( $s );
	}

	function showLog() {
		global $wgCheckUserLog;

		if( $wgCheckUserLog === false || !file_exists( $wgCheckUserLog
) ) {
			# No log
			return;
		} else {
			global $wgRequest, $wgOut;

			if( $wgRequest->getVal( 'log' ) == 1 ) {
				# Show the log
				list( $limit, $offset ) = wfCheckLimits();
				$log = $this->tail( $wgCheckUserLog, $limit,
$offset );
				if( !!$log ) {

					$scroller = wfViewPrevNext( $offset,
$limit,
						Title::makeTitle( NS_SPECIAL,
'CheckUser' ),
						'log=1',
						count( $log ) < $limit );

					$output = implode( "\n", $log );
					$wgOut->addHTML(
"$scroller\n<ul>$output</ul>\n$scroller\n" );
				} else {
					$wgOut->addHTML( "<p>The log contains
no items.</p>" );
				}
			} else {
				# Hide the log, show a link
				global $wgTitle, $wgUser;
				$skin = $wgUser->getSkin();
				$link = $skin->makeKnownLinkObj( $wgTitle,
'Show log', 'log=1' );
				$wgOut->addHTML( "<p>$link</p>" );
			}
		}
	}

	function tail( $filename, $limit, $offset ) {
		//wfSuppressWarnings();
		$file = fopen( $filename, "rt" );
		//wfRestoreWarnings();

		if( $file === false ) {
			return false;
		}

		$filePosition = filesize( $filename );
		if( $filePosition == 0 ) {
			return array();
		}

		$lines = array();
		$bufSize = 1024;
		$lineCount = 0;
		$total = $offset + $limit;
		$leftover = '';
		do {
			if( $filePosition < $bufSize ) {
				$bufSize = $filePosition;
			}
			$filePosition -= $bufSize;
			fseek( $file, $filePosition );
			$buffer = fread( $file, $bufSize );

			$parts = explode( "\n", $buffer );
			$num = count( $parts );

			if( $num > 0 ) {
				if( $lineCount++ > $offset ) {
					$lines[] = $parts[$num - 1] .
$leftover;
					if( $lineCount > $total ) {
						return $lines;
					}
				}
			}
			for( $i = $num - 2; $i > 0; $i-- ) {
				if( $lineCount++ > $offset ) {
					$lines[] = $parts[$i];
					if( $lineCount > $total ) {
						fclose( $file );
						return $lines;
					}
				}
			}
			if( $num > 1 ) {
				$leftover = $parts[0];
			} else {
				$leftover = '';
				break;
			}
		} while( $filePosition > 0 );

		if( $lineCount++ > $offset ) {
			$lines[] = $leftover;
		}
		fclose( $file );
		return $lines;
	}

	function addLogEntry( $entry ) {
		global $wgUser, $wgCheckUserLog;
		if ( $wgCheckUserLog === false ) {
			// No log required, this is not an error
			return true;
		}

		$f = fopen( $wgCheckUserLog, 'a' );
		if ( !$f ) {
			return false;
		}
		if ( !fwrite( $f, "<li>$entry</li>\n" ) ) {
			return false;
		}
		fclose( $f );
		return true;
	}
}
?>
Comment 6 Aaron Schulz 2006-12-02 05:12:56 UTC
Comment on attachment 2806 [details]
A patch to add the list of users that used an IP and an autoblock checker

<?php

if ( !defined( 'MEDIAWIKI' ) ) {
    echo "CheckUser extension\n";
    exit( 1 );
}

# Add messages
global $wgMessageCache, $wgCheckUserMessages;
foreach( $wgCheckUserMessages as $key => $value ) {
	$wgMessageCache->addMessages( $wgCheckUserMessages[$key], $key );
}

class CheckUser extends SpecialPage
{
	function CheckUser() {
		SpecialPage::SpecialPage('CheckUser', 'checkuser');
	}

	function execute( $par ) {
		global $wgRequest, $wgOut, $wgTitle, $wgUser;

		if( !$wgUser->isAllowed( 'checkuser' ) ) {
			$wgOut->permissionRequired( 'checkuser' );
			return;
		}

		$this->setHeaders();

		$ip = $wgRequest->getText( 'ip' );
		$user = $wgRequest->getText( 'user' );
		$autoblock = $wgRequest->getText( 'autoblock' );
		$subip = $wgRequest->getBool( 'subip' );
		$subuser = $wgRequest->getBool( 'subuser' );
		$subautoblock = $wgRequest->getBool( 'subautoblock' );
		$wpNamesOnly = $wgRequest->getBool( 'wpNamesOnly' );

		$this->doTop( $ip, $user, $autoblock);
		if ( $subip ) {
			$this->doIPRequest( $ip, $wpNamesOnly);
		} else if ( $subuser ) {
			$this->doUserRequest( $user );
		} else if ( $subautoblock ) {
			$this->doAutoblockRequest( $autoblock );
		} else {
			$this->showLog();
		}
	}

	function doTop( $ip, $user, $autoblock ) {
		global $wgOut, $wgTitle;

		$action = $wgTitle->escapeLocalUrl();
		$encIp = htmlspecialchars( $ip );
		$encUser = htmlspecialchars( $user );
		$encAutoblock = htmlspecialchars( $autoblock );

		$wgOut->addHTML( wfmsg('checkuserheader'));
		$wgOut->addHTML( <<<EOT
<table border=0 cellpadding=5>
<form name="checkuser" action="$action" method=post>
<tr><td>
	IP address:
</td><td>
	<input type="text" name="ip" value="$encIp" width=50 /> <input
type="submit" name="subip" value="OK" />
	<br><input name="wpNamesOnly" type="checkbox" value="1"
id="wpNamesOnly" tabindex="4" /><label for="wpNamesOnly">List usernames
only</label>
</td></tr>
</form>

<form name="checkuser" action="$action" method=post>
<tr><td>
	Username:
</td><td>
	<input type="text" name="user" value="$encUser" width=50 /> <input
type="submit" name="subuser" value="OK" />
</td></tr>

<form name="checkuser" action="$action" method=post>
<tr><td>
	Autoblock#:
</td><td>
	<input type="text" name="autoblock" value="$encAutoblock" width=50 />
<input type="submit" name="subautoblock" value="OK" />
</td></tr>
</form>
</table>
EOT
		);
	}

	function doAutoblockRequest( $autoblock ) {
		global $wgUser, $wgOut, $wgLang, $wgTitle, $wgDBname;
		$fname = 'CheckUser::doAutoblockRequest';

		if ( !$this->addLogEntry( $wgLang->timeanddate(
wfTimestampNow() ) . ' ' .
		  $wgUser->getName() . ' got IP for autoblock #' .
htmlspecialchars( $autoblock ) . ' on ' . $wgDBname )) 
		{
			$wgOut->addHTML( '<p>Unable to add log entry</p>' );
		}

		$dbr =& wfGetDB( DB_SLAVE );
		$res = $dbr->select( 'ipblocks', array( 'ipb_address' ), array(
'ipb_id' => $autoblock , 'ipb_auto' => 1), $fname );
		if (!$dbr->numRows( $res )) {
			$s = "No results, the block may have expired\n";
		} else {
		    global $IP;
			require_once( $IP.'/includes/RecentChange.php' );
			require_once( $IP.'/includes/ChangesList.php' );
		    $row = $dbr->fetchObject($res);
		    $s = '<ul>';
		    $s .= '<li><a href="' . $wgTitle->escapeLocalURL( 'user=' .
urlencode( $row->ipb_address ) ) . '">' .
					htmlspecialchars( $row->ipb_address ) .
'</a></li>';
			$s .= '</ul>';
		}
		$wgOut->addHTML( $s );
		$dbr->freeResult( $res );
	}

	function doIPRequest( $ip, $wpNamesOnly) {
		global $wgUser, $wgOut, $wgLang, $wgTitle, $wgDBname;
		$fname = 'CheckUser::doIPRequest';
		if ($wpNamesOnly) $type='users';
		else $type='edits';

		if ( !$this->addLogEntry( $wgLang->timeanddate(
wfTimestampNow() ) . ' ' .
		  $wgUser->getName() . ' got ' . $type . ' for ' .
htmlspecialchars( $ip ) . ' on ' . $wgDBname ))
		{
			$wgOut->addHTML( '<p>Unable to add log entry</p>' );
		}

		$dbr =& wfGetDB( DB_SLAVE );
		if (!$wpNamesOnly) {
		$res = $dbr->select( 'recentchanges', array( '*' ),
$this->getIpConds( $dbr, $ip ), $fname, 
			array( 'ORDER BY' => 'rc_timestamp DESC' ) );
		} else { $res = $dbr->select( 'recentchanges', array(
'rc_user_text' ), $this->getIpConds( $dbr, $ip ), $fname, 
			array( 'ORDER BY' => 'rc_timestamp DESC' ) );
		}

		if ( !$dbr->numRows( $res ) ) {
			$s =  "No results\n";
		} else if ( !$wpNamesOnly ) {
			global $IP;
			require_once( $IP.'/includes/RecentChange.php' );
			require_once( $IP.'/includes/ChangesList.php' );

			if ( in_array( 'newfromuser', array_map( 'strtolower',
get_class_methods( 'ChangesList' ) ) ) ) {
				// MW >= 1.6
				$list = ChangesList::newFromUser( $wgUser );
			} else {
				// MW < 1.6
				$sk =& $wgUser->getSkin();
				$list = new ChangesList( $sk );
			}
			$s = $list->beginRecentChangesList();
			$counter = 1;
			while ( ($row = $dbr->fetchObject( $res ) ) != false )
{
				$rc = RecentChange::newFromRow( $row );
				$rc->counter = $counter++;
				$s .= $list->recentChangesLine( $rc, false );
			}
			$s .= $list->endRecentChangesList();
		} else {
		$uniqueUsers = array();
		$u_count = 0;
		$wgOut->addHTML( '<ul>' );
			while ( ($row = $dbr->fetchObject( $res ) ) != false )
{
			    if (!in_array( $row->rc_user_text, $uniqueUsers ))
{
			    $uniqueUsers[$u_count]=$row->rc_user_text;
			    $u_count++;
				$s .= '<li><a href="' .
$wgTitle->escapeLocalURL( 'user=' . urlencode( $row->rc_user_text ) ) . '">' .
					htmlspecialchars( $row->rc_user_text )
. '</a></li>';
				}
			}
		}
		$wgOut->addHTML( $s );
		$wgOut->addHTML( '</ul>' );
		$dbr->freeResult( $res );
	}

	/**
	 * Since we have stuff stored in text format, this only works easily
	 * for some simple cases, such as /16 and /24.
	 * @param Database $db
	 * @param string $ip
	 * @return array conditions
	 */
	function getIpConds( $db, $ip ) {
		// haaaack
		if( preg_match( '#^(\d+)\.(\d+)\.(\d+)\.(\d+)/(\d+)$#', $ip,
$matches ) ) {
			list( $junk, $a, $b, $c, $d, $bits ) = $matches;
			if( $bits == 32 ) {
				$match = "$a.$b.$c.$d";
			} elseif( $bits == 24 ) {
				$match = "$a.$b.$c.%";
			} elseif( $bits == 16 ) {
				$match = "$a.$b.%";
			} else {
				// Other sizes not supported. /8 is too big
				$match = $ip;
			}
			return array( 'rc_ip LIKE ' . $db->addQuotes( $match )
);
		} else {
			return array( 'rc_ip' => $ip );
		}
	}

	function doUserRequest( $user ) {
		global $wgOut, $wgTitle, $wgLang, $wgUser, $wgDBname;
		$fname = 'CheckUser::doUserRequest';

		$userTitle = Title::newFromText( $user, NS_USER );
		if( !is_null( $userTitle ) ) {
			// normalize the username
			$user = $userTitle->getText();
		}

		if ( !$this->addLogEntry( $wgLang->timeanddate(
wfTimestampNow() ) . ' ' .
		  $wgUser->getName() . ' got IPs for ' . htmlspecialchars(
$user ) . ' on ' . $wgDBname ) ) 
		{
			$wgOut->addHTML( '<p>Unable to add log entry</p>' );
		}

		$dbr =& wfGetDB( DB_SLAVE );
		$res = $dbr->select( 'recentchanges', array( 'DISTINCT rc_ip'
), array( 'rc_user_text' => $user ), $fname );
		if ( !$dbr->numRows( $res ) ) {
			$s =  "No results\n";
		} else {
			$s = '<ul>';
			while ( ($row = $dbr->fetchObject( $res ) ) != false )
{
				$s .= '<li><a href="' .
$wgTitle->escapeLocalURL( 'ip=' . urlencode( $row->rc_ip ) ) . '">' .
					htmlspecialchars( $row->rc_ip ) .
'</a></li>';
			}
			$s .= '</ul>';
		}
		$wgOut->addHTML( $s );
	}

	function showLog() {
		global $wgCheckUserLog;

		if( $wgCheckUserLog === false || !file_exists( $wgCheckUserLog
) ) {
			# No log
			return;
		} else {
			global $wgRequest, $wgOut;

			if( $wgRequest->getVal( 'log' ) == 1 ) {
				# Show the log
				list( $limit, $offset ) = wfCheckLimits();
				$log = $this->tail( $wgCheckUserLog, $limit,
$offset );
				if( !!$log ) {

					$scroller = wfViewPrevNext( $offset,
$limit,
						Title::makeTitle( NS_SPECIAL,
'CheckUser' ),
						'log=1',
						count( $log ) < $limit );

					$output = implode( "\n", $log );
					$wgOut->addHTML(
"$scroller\n<ul>$output</ul>\n$scroller\n" );
				} else {
					$wgOut->addHTML( "<p>The log contains
no items.</p>" );
				}
			} else {
				# Hide the log, show a link
				global $wgTitle, $wgUser;
				$skin = $wgUser->getSkin();
				$link = $skin->makeKnownLinkObj( $wgTitle,
'Show log', 'log=1' );
				$wgOut->addHTML( "<p>$link</p>" );
			}
		}
	}

	function tail( $filename, $limit, $offset ) {
		//wfSuppressWarnings();
		$file = fopen( $filename, "rt" );
		//wfRestoreWarnings();

		if( $file === false ) {
			return false;
		}

		$filePosition = filesize( $filename );
		if( $filePosition == 0 ) {
			return array();
		}

		$lines = array();
		$bufSize = 1024;
		$lineCount = 0;
		$total = $offset + $limit;
		$leftover = '';
		do {
			if( $filePosition < $bufSize ) {
				$bufSize = $filePosition;
			}
			$filePosition -= $bufSize;
			fseek( $file, $filePosition );
			$buffer = fread( $file, $bufSize );

			$parts = explode( "\n", $buffer );
			$num = count( $parts );

			if( $num > 0 ) {
				if( $lineCount++ > $offset ) {
					$lines[] = $parts[$num - 1] .
$leftover;
					if( $lineCount > $total ) {
						return $lines;
					}
				}
			}
			for( $i = $num - 2; $i > 0; $i-- ) {
				if( $lineCount++ > $offset ) {
					$lines[] = $parts[$i];
					if( $lineCount > $total ) {
						fclose( $file );
						return $lines;
					}
				}
			}
			if( $num > 1 ) {
				$leftover = $parts[0];
			} else {
				$leftover = '';
				break;
			}
		} while( $filePosition > 0 );

		if( $lineCount++ > $offset ) {
			$lines[] = $leftover;
		}
		fclose( $file );
		return $lines;
	}

	function addLogEntry( $entry ) {
		global $wgUser, $wgCheckUserLog;
		if ( $wgCheckUserLog === false ) {
			// No log required, this is not an error
			return true;
		}

		$f = fopen( $wgCheckUserLog, 'a' );
		if ( !$f ) {
			return false;
		}
		if ( !fwrite( $f, "<li>$entry</li>\n" ) ) {
			return false;
		}
		fclose( $f );
		return true;
	}
}
?>
Comment 7 Aaron Schulz 2006-12-02 05:15:23 UTC
Gah! I thought that was updating the attachment, sorry. I'd like if someone
could delete these extras.
Comment 8 Aaron Schulz 2006-12-02 07:14:05 UTC
Created attachment 2807 [details]
update; a bit shorter, log wording change

Update
Comment 9 Aaron Schulz 2006-12-02 18:13:32 UTC
Created attachment 2808 [details]
Autoblock IPs listed with link to checkip
Comment 10 Aaron Schulz 2006-12-03 01:51:56 UTC
Created attachment 2812 [details]
"list users only" option, replaced a useless extra option with a reason box

"list users only" option, replaced a useless extra option with a reason box
Comment 11 Rob Church 2006-12-03 03:12:35 UTC
Comment on attachment 2812 [details]
"list users only" option, replaced a useless extra option with a reason box

As with the other, not a true patch...
Comment 12 Aaron Schulz 2006-12-09 22:56:13 UTC
Comment on attachment 2812 [details]
"list users only" option, replaced a useless extra option with a reason box

"Patch" is obsolete. See better patch at bug 5044.
Comment 13 Aaron Schulz 2006-12-25 04:25:28 UTC
Resolved with bug 5044.

Note You need to log in before you can comment on or make changes to this bug.


Navigation
Links