/** * Find the wikis with revisions for the specified users * * @param array $identifiers * @return array of wikiId => array of users with revisions on that wiki */ protected function getAffectedWikis(array $identifiers) { global $wgStatsDB, $wgStatsDBEnabled, $wgDevelEnvironment; $wikiIds = []; if (!$wgStatsDBEnabled) { // requires stats database to function // TODO log this? return $wikiIds; } // on devbox we have to fall back to static list // because we have no active read-write statsdb there if (!empty($wgDevelEnvironment)) { global $wgCityId; return array($wgCityId => $identifiers); } $sdb = wfGetDB(DB_SLAVE, [], $wgStatsDB); foreach ($identifiers as $identifier) { list($userId, $ip) = $this->getUserByIdentifier($identifier); $affectedWikis = (new WikiaSQL())->DISTINCT('wiki_id')->FROM('events')->WHERE('user_id')->EQUAL_TO($userId); if ($userId == 0 && isset($ip)) { $affectedWikis->AND_('ip')->EQUAL_TO(IP::toUnsigned($identifier)); } // TODO can userId be 0 and the identifier not be an IP? $wikis = $affectedWikis->runLoop($sdb, function ($_, $row) use(&$wikiIds, $identifier) { if (empty($wikiIds[$row->wiki_id])) { $wikiIds[$row->wiki_id] = []; } $wikiIds[$row->wiki_id][] = $identifier; }); } return $wikiIds; }
protected function findWikis($users) { global $wgStatsDB, $wgStatsDBEnabled, $wgDevelEnvironment; if (!$wgStatsDBEnabled) { return false; } // on devbox we have to fall back to static list // because we have no active read-write statsdb there if (!empty($wgDevelEnvironment)) { return array(165); } $dbr = wfGetDB(DB_SLAVE, array(), $wgStatsDB); $wikiIds = array(); foreach ($users as $user) { if ($user['id'] != 0) { // regular user $where = array('user_id' => intval($user['id'])); } else { // ip - anons $where = array('user_id' => 0, 'ip' => IP::toUnsigned($user['ip'])); } $res = $dbr->select('events', 'wiki_id', $where, __METHOD__, array('DISTINCT')); $userWikiIds = array(); while ($row = $dbr->fetchObject($res)) { $wikiIds[$row->wiki_id] = true; $userWikiIds[] = $row->wiki_id; } $this->log("User \"" . $user['canonical'] . "\": found " . count($userWikiIds) . " wikis: " . implode(', ', $userWikiIds)); } $wikiIds = array_keys($wikiIds); sort($wikiIds); return $wikiIds; }
public function execute() { $dbw = wfGetDB(DB_MASTER); // Perform this in a transaction so an failed load doesn't erase current data. $dbw->begin(); // Clear existing GeoIP data. try { $dbw->delete('geoip', '*', __METHOD__); } catch (DBQueryError $e) { $this->error('ERROR: Could not delete existing geographic data. Is the GeoIP schema loaded?', true); } // Load fresh data from the first (and only) argument. $filename = $this->getArg(0); $lines = exec('wc -l ' . $filename); $handle = fopen($filename, 'r'); $count = 0; while (($data = fgetcsv($handle, 256, ',')) !== false) { // Output a nice progress bar. if ($count % 1000 == 0) { $progress = ceil($count / $lines * 50); $this->output('[' . str_repeat('=', $progress) . str_repeat(' ', 50 - $progress) . '] ' . ceil($count / $lines * 100) . '%' . "\r"); } ++$count; $record = array('begin_ip_long' => IP::toUnsigned($data[0]), 'end_ip_long' => IP::toUnsigned($data[1]), 'country_code' => $data[4]); $dbw->insert('geoip', $record, __METHOD__); } $this->output("\n"); $dbw->commit(); $this->output('Successfully loaded ' . $count . ' geographic IP ranges.' . "\n"); }
function fnGetGeoIP($ip_address = null) { if (!isset($ip_address)) { $ip_address = IP::sanitizeIP(wfGetIP()); } if (isset($_GET['ip'])) { $ip_address = IP::sanitizeIP($_GET['ip']); } if (!IP::isIPv4($ip_address)) { throw new UnsupportedGeoIP('Only IPv4 addresses are supported.'); } $dbr = wfGetDB(DB_SLAVE); $long_ip = IP::toUnsigned($ip_address); $conditions = array('begin_ip_long <= ' . $long_ip, $long_ip . ' <= end_ip_long'); $country_code = $dbr->selectField('geoip', 'country_code', $conditions, __METHOD__); if (!$country_code) { throw new NotFoundGeoIP('Could not identify the country for the provided IP address.'); } return $country_code; }
/** * Determine if an IP address really is an IP address, and if it is public, * i.e. not RFC 1918 or similar * Comes from ProxyTools.php */ public static function isPublic($ip) { $n = IP::toUnsigned($ip); if (!$n) { return false; } // ip2long accepts incomplete addresses, as well as some addresses // followed by garbage characters. Check that it's really valid. if ($ip != long2ip($n)) { return false; } static $privateRanges = false; if (!$privateRanges) { $privateRanges = array(array('10.0.0.0', '10.255.255.255'), array('172.16.0.0', '172.31.255.255'), array('192.168.0.0', '192.168.255.255'), array('0.0.0.0', '0.255.255.255'), array('127.0.0.0', '127.255.255.255')); } foreach ($privateRanges as $r) { $start = IP::toUnsigned($r[0]); $end = IP::toUnsigned($r[1]); if ($n >= $start && $n <= $end) { return false; } } return true; }
/** * test wrapper around ip2long which might return -1 or false depending on PHP version * @covers IP::toUnsigned */ public function testip2longWrapper() { // @todo FIXME: Add more tests ? $this->assertEquals(pow(2, 32) - 1, IP::toUnsigned('255.255.255.255')); $i = 'IN.VA.LI.D'; $this->assertFalse(IP::toUnSigned($i)); }
/** * @covers IP::toUnsigned * @dataProvider provideToUnsigned */ public function testToUnsigned($expected, $input) { $result = IP::toUnsigned($input); $this->assertTrue($result === false || is_string($result) || is_int($result)); $this->assertEquals($expected, $result); }
function showList($msg) { global $wgOut; $wgOut->setPagetitle(wfMsg("ipblocklist")); if ("" != $msg) { $wgOut->setSubtitle($msg); } // Purge expired entries on one in every 10 queries if (!mt_rand(0, 10)) { Block::purgeExpired(); } $conds = array(); if ($this->ip == '') { // No extra conditions } elseif (substr($this->ip, 0, 1) == '#') { $conds['ipb_id'] = substr($this->ip, 1); } elseif (IP::toUnsigned($this->ip) !== false) { $conds['ipb_address'] = $this->ip; $conds['ipb_auto'] = 0; } elseif (preg_match("/^(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})\\/(\\d{1,2})\$/", $this->ip, $matches)) { $conds['ipb_address'] = Block::normaliseRange($this->ip); $conds['ipb_auto'] = 0; } else { $user = User::newFromName($this->ip); if ($user && ($id = $user->getID()) != 0) { $conds['ipb_user'] = $id; } else { // Uh...? $conds['ipb_address'] = $this->ip; $conds['ipb_auto'] = 0; } } $pager = new IPBlocklistPager($this, $conds); $s = $pager->getNavigationBar() . $this->searchForm(); if ($pager->getNumRows()) { $s .= "<ul>" . $pager->getBody() . "</ul>"; } else { $s .= '<p>' . wfMsgHTML('ipblocklistempty') . '</p>'; } $s .= $pager->getNavigationBar(); $wgOut->addHTML($s); }
/** * Gets rid of uneeded numbers in quad-dotted/octet IP strings * For example, 127.111.113.151/24 -> 127.111.113.0/24 */ static function normaliseRange($range) { $parts = explode('/', $range); if (count($parts) == 2) { // IPv6 if (IP::isIPv6($range) && $parts[1] >= 64 && $parts[1] <= 128) { $bits = $parts[1]; $ipint = IP::toUnsigned6($parts[0]); # Native 32 bit functions WONT work here!!! # Convert to a padded binary number $network = wfBaseConvert($ipint, 10, 2, 128); # Truncate the last (128-$bits) bits and replace them with zeros $network = str_pad(substr($network, 0, $bits), 128, 0, STR_PAD_RIGHT); # Convert back to an integer $network = wfBaseConvert($network, 2, 10); # Reform octet address $newip = IP::toOctet($network); $range = "{$newip}/{$parts[1]}"; } else { if (IP::isIPv4($range) && $parts[1] >= 16 && $parts[1] <= 32) { $shift = 32 - $parts[1]; $ipint = IP::toUnsigned($parts[0]); $ipint = $ipint >> $shift << $shift; $newip = long2ip($ipint); $range = "{$newip}/{$parts[1]}"; } } } return $range; }
function showList($msg) { global $wgOut, $wgUser; $wgOut->setPagetitle(wfMsg("ipblocklist")); if ("" != $msg) { $wgOut->setSubtitle($msg); } // Purge expired entries on one in every 10 queries if (!mt_rand(0, 10)) { Block::purgeExpired(); } $conds = array(); $matches = array(); // Is user allowed to see all the blocks? if (!$wgUser->isAllowed('oversight')) { $conds['ipb_deleted'] = 0; } if ($this->ip == '') { // No extra conditions } elseif (substr($this->ip, 0, 1) == '#') { $conds['ipb_id'] = substr($this->ip, 1); } elseif (IP::toUnsigned($this->ip) !== false) { $conds['ipb_address'] = $this->ip; $conds['ipb_auto'] = 0; } elseif (preg_match('/^(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})\\/(\\d{1,2})$/', $this->ip, $matches)) { $conds['ipb_address'] = Block::normaliseRange($this->ip); $conds['ipb_auto'] = 0; } else { $user = User::newFromName($this->ip); if ($user && ($id = $user->getID()) != 0) { $conds['ipb_user'] = $id; } else { // Uh...? $conds['ipb_address'] = $this->ip; $conds['ipb_auto'] = 0; } } $pager = new IPBlocklistPager($this, $conds); if ($pager->getNumRows()) { $wgOut->addHTML($this->searchForm() . $pager->getNavigationBar() . Xml::tags('ul', null, $pager->getBody()) . $pager->getNavigationBar()); } elseif ($this->ip != '') { $wgOut->addHTML($this->searchForm()); $wgOut->addWikiText(wfMsg('ipblocklist-no-results')); } else { $wgOut->addWikiText(wfMsg('ipblocklist-empty')); } }
static function normaliseRange($range) { $parts = explode('/', $range); if (count($parts) == 2) { $shift = 32 - $parts[1]; $ipint = IP::toUnsigned($parts[0]); $ipint = $ipint >> $shift << $shift; $newip = long2ip($ipint); $range = "{$newip}/{$parts[1]}"; } return $range; }
/** * Determine if a given integer IPv4 address is in a given CIDR network * @param $addr The address to check against the given range. * @param $range The range to check the given address against. * @return bool Whether or not the given address is in the given range. */ public static function isInRange($addr, $range) { $unsignedIP = IP::toUnsigned($addr); list($start, $end) = IP::parseRange($range); $start = hexdec($start); $end = hexdec($end); return $unsignedIP >= $start && $unsignedIP <= $end; }