: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
$blocksTable = wfBlock::blocksTable();
$wpdb->query("DELETE FROM `{$blocksTable}` WHERE `expiration` <= UNIX_TIMESTAMP() AND `expiration` != " . self::DURATION_FOREVER);
* Imports all valid blocks in $blocks. If $replaceExisting is true, this will remove all permanent blocks prior to the import.
* @param bool $replaceExisting
public static function importBlocks($blocks, $replaceExisting = true) {
$blocksTable = wfBlock::blocksTable();
$wpdb->query("DELETE FROM `{$blocksTable}` WHERE `expiration` = " . self::DURATION_FOREVER);
foreach ($blocks as $b) {
if (!WFWAF_SUBDIRECTORY_INSTALL && class_exists('wfWAFIPBlocksController')) {
wfWAFIPBlocksController::setNeedsSynchronizeConfigSettings();
* Validates the block import record and inserts it if valid. This validation is identical to what is applied to adding one through the UI.
private static function _importBlock($b) {
$blocksTable = wfBlock::blocksTable();
if (!isset($b['type']) || !isset($b['IP']) || !isset($b['blockedTime']) || !isset($b['reason']) || !isset($b['lastAttempt']) || !isset($b['blockedHits'])) { return false; }
if (empty($b['IP']) || empty($b['reason'])) { return false; }
$ip = @wfUtils::inet_ntop(wfUtils::hex2bin($b['IP']));
if (!wfUtils::isValidIP($ip)) { return false; }
case self::TYPE_IP_MANUAL:
case self::TYPE_IP_AUTOMATIC_TEMPORARY:
case self::TYPE_IP_AUTOMATIC_PERMANENT:
case self::TYPE_WFSN_TEMPORARY:
case self::TYPE_RATE_BLOCK:
case self::TYPE_RATE_THROTTLE:
if (self::isWhitelisted($ip)) { return false; }
$ipHex = wfDB::binaryValueToSQLHex(wfUtils::inet_pton($ip));
return $wpdb->query($wpdb->prepare("INSERT INTO `{$blocksTable}` (`type`, `IP`, `blockedTime`, `reason`, `lastAttempt`, `blockedHits`, `expiration`, `parameters`) VALUES (%d, {$ipHex}, %d, %s, %d, %d, %d, NULL)", (int) $b['type'], (int) $b['blockedTime'], $b['reason'], (int) $b['lastAttempt'], (int) $b['blockedHits'], self::DURATION_FOREVER)) !== false;
if (!isset($b['parameters'])) { return false; }
if (wfUtils::inet_pton($ip) != self::MARKER_COUNTRY) { return false; }
$parameters = @json_decode($b['parameters'], true);
if (!isset($parameters['blockLogin']) || !isset($parameters['blockSite']) || !isset($parameters['countries'])) { return false; }
$parameters['blockLogin'] = wfUtils::truthyToInt($parameters['blockLogin']);
$parameters['blockSite'] = wfUtils::truthyToInt($parameters['blockSite']);
require(WORDFENCE_PATH . 'lib/wfBulkCountries.php'); /** @var array $wfBulkCountries */
foreach ($parameters['countries'] as $code) {
if (!isset($wfBulkCountries[$code])) {
$parameters = array('blockLogin' => $parameters['blockLogin'], 'blockSite' => $parameters['blockSite'], 'countries' => $parameters['countries']);
$ipHex = wfDB::binaryValueToSQLHex(self::MARKER_COUNTRY);
return $wpdb->query($wpdb->prepare("INSERT INTO `{$blocksTable}` (`type`, `IP`, `blockedTime`, `reason`, `lastAttempt`, `blockedHits`, `expiration`, `parameters`) VALUES (%d, {$ipHex}, %d, %s, %d, %d, %d, %s)", self::TYPE_COUNTRY, (int) $b['blockedTime'], $b['reason'], (int) $b['lastAttempt'], (int) $b['blockedHits'], self::DURATION_FOREVER, json_encode($parameters))) !== false;
if (!isset($b['parameters'])) { return false; }
if (wfUtils::inet_pton($ip) != self::MARKER_PATTERN) { return false; }
$parameters = @json_decode($b['parameters'], true);
if (!isset($parameters['ipRange']) || !isset($parameters['hostname']) || !isset($parameters['userAgent']) || !isset($parameters['referrer'])) { return false; }
if (!empty($parameters['ipRange'])) {
$ipRange = new wfUserIPRange($parameters['ipRange']);
if ($ipRange->isValidRange()) {
if ($ipRange->isMixedRange()) {
if (!empty($parameters['hostname'])) {
if (preg_match('/^[a-z0-9\.\*\-]+$/i', $parameters['hostname'])) {
if (!empty($parameters['userAgent'])) { $hasOne = true; }
if (!empty($parameters['referrer'])) { $hasOne = true; }
if (!$hasOne) { return false; }
if (!empty($parameters['ipRange'])) {
$ipRange = new wfUserIPRange($parameters['ipRange']);
$ipRange = $ipRange->getIPString();
'hostname' => $parameters['hostname'],
'userAgent' => $parameters['userAgent'],
'referrer' => $parameters['referrer'],
$ipHex = wfDB::binaryValueToSQLHex(self::MARKER_PATTERN);
return $wpdb->query($wpdb->prepare("INSERT INTO `{$blocksTable}` (`type`, `IP`, `blockedTime`, `reason`, `lastAttempt`, `blockedHits`, `expiration`, `parameters`) VALUES (%d, {$ipHex}, %d, %s, %d, %d, %d, %s)", self::TYPE_PATTERN, (int) $b['blockedTime'], $b['reason'], (int) $b['lastAttempt'], (int) $b['blockedHits'], self::DURATION_FOREVER, json_encode($parameters))) !== false;
* Returns an array suitable for JSON output of all permanent blocks.
public static function exportBlocks() {
$blocksTable = wfBlock::blocksTable();
$query = "SELECT `type`, HEX(`IP`) AS `IP`, `blockedTime`, `reason`, `lastAttempt`, `blockedHits`, `parameters` FROM `{$blocksTable}` WHERE `expiration` = " . self::DURATION_FOREVER;
$rows = $wpdb->get_results($query, ARRAY_A);
* Returns all unexpired blocks (including lockouts by default), optionally only of the specified types. These are sorted descending by the time created.
* @param bool $prefetch If true, the full data for the block is fetched rather than using lazy loading.
* @param array $ofTypes An optional array of block types to restrict the returned array of blocks to.
* @param int $offset The offset to start the result fetch at.
* @param int $limit The maximum number of results to return. -1 for all.
* @param string $sortColumn The column to sort by.
* @param string $sortDirection The direction to sort.
* @param string $filter An optional value to filter by.
public static function allBlocks($prefetch = false, $ofTypes = array(), $offset = 0, $limit = -1, $sortColumn = 'type', $sortDirection = 'ascending', $filter = '') {
$blocksTable = wfBlock::blocksTable();
switch ($sortColumn) { //Match the display table column to the corresponding schema column
if ($sortDirection == 'descending') {
$query = "SELECT {$columns}, CASE
WHEN `type` = " . self::TYPE_COUNTRY . " THEN 0
WHEN `type` = " . self::TYPE_PATTERN . " THEN 1
WHEN `type` = " . self::TYPE_LOCKOUT . " THEN 2
WHEN `type` = " . self::TYPE_RATE_THROTTLE . " THEN 3
WHEN `type` = " . self::TYPE_RATE_BLOCK . " THEN 4
WHEN `type` = " . self::TYPE_IP_AUTOMATIC_PERMANENT . " THEN 5
WHEN `type` = " . self::TYPE_IP_AUTOMATIC_TEMPORARY . " THEN 6
WHEN `type` = " . self::TYPE_WFSN_TEMPORARY . " THEN 7
WHEN `type` = " . self::TYPE_IP_MANUAL . " THEN 8
WHEN `type` = " . self::TYPE_COUNTRY . " THEN `parameters`
WHEN `type` = " . self::TYPE_PATTERN . " THEN `parameters`
WHEN `type` = " . self::TYPE_IP_MANUAL . " THEN `IP`
WHEN `type` = " . self::TYPE_IP_AUTOMATIC_PERMANENT . " THEN `IP`
WHEN `type` = " . self::TYPE_RATE_BLOCK . " THEN `IP`
WHEN `type` = " . self::TYPE_RATE_THROTTLE . " THEN `IP`
WHEN `type` = " . self::TYPE_LOCKOUT . " THEN `IP`
WHEN `type` = " . self::TYPE_WFSN_TEMPORARY . " THEN `IP`
WHEN `type` = " . self::TYPE_IP_AUTOMATIC_TEMPORARY . " THEN `IP`
FROM `{$blocksTable}` WHERE ";
$sanitizedTypes = array_map('intval', $ofTypes);
$query .= "`type` IN (" . implode(', ', $sanitizedTypes) . ') AND ';
$query .= '(`expiration` = ' . self::DURATION_FOREVER . " OR `expiration` > UNIX_TIMESTAMP()) ORDER BY `{$sort}` {$order}, `id` DESC";
$query .= " LIMIT {$offset},{$limit}";
$rows = $wpdb->get_results($query, ARRAY_A);
if ($r['type'] == self::TYPE_COUNTRY || $r['type'] == self::TYPE_PATTERN) {
$ip = wfUtils::inet_ntop($r['IP']);
if ($r['type'] == self::TYPE_PATTERN || $r['type'] == self::TYPE_COUNTRY) {
$parameters = @json_decode($r['parameters'], true);
$result[] = new wfBlock($r['id'], $r['type'], $ip, $r['blockedTime'], $r['reason'], $r['lastAttempt'], $r['blockedHits'], $r['expiration'], $parameters);
$result[] = new wfBlock($r['id']);
* Functions identically to wfBlock::allBlocks except that it filters the result. The filtering is done within PHP rather than MySQL, so this will impose a performance penalty and should only
* be used when filtering is actually wanted.
* @param string $sortColumn
* @param string $sortDirection
public static function filteredBlocks($prefetch = false, $ofTypes = array(), $offset = 0, $limit = -1, $sortColumn = 'type', $sortDirection = 'ascending', $filter = '') {
return self::allBlocks($prefetch, $ofTypes, $offset, $limit, $sortColumn, $sortDirection);
else if (wfUtils::isValidIP($filter)) { //e.g., 4.5.6.7, ffe0::, ::0
$matchValue = wfUtils::inet_ntop(wfUtils::inet_pton($filter));
if (empty($matchType) && preg_match('/^(?:[0-9]+|\*)\.(?:(?:[0-9]+|\*)\.(?!$))*(?:(?:[0-9]+|\*))?$/', trim($filter, '.'))) { //e.g., possible wildcard IPv4 like 4.5.*
$components = explode('.', trim($filter, '.'));
if (count($components) <= 4) {
$components = array_pad($components, 4, '*');
foreach ($components as $c) {
if (empty($c) || $c == '*') {
$matchValue = substr($matchValue, 0, -2);
if (empty($matchType) && preg_match('/^(?:[0-9a-f]+\:)(?:[0-9a-f]+\:|\*){1,2}(?:[0-9a-f]+|\*)?$/i', $filter)) { //e.g., possible wildcard IPv6 like ffe0:*
$components = explode(':', $filter);
for ($i = 0; $i < 4; $i++) {
if (isset($components[$i])) {
$matchValue .= strtoupper(str_pad(dechex($components[$i]), 4, '0', STR_PAD_LEFT));
$matchValue .= '[0-9a-f]{4}';
$matchValue = substr($matchValue, 0, -1);
for ($i = 0; true; $i += WORDFENCE_BLOCKED_IPS_PER_PAGE) {
$blocks = wfBlock::allBlocks(true, $ofTypes, $i, WORDFENCE_BLOCKED_IPS_PER_PAGE, $sortColumn, $sortDirection);
foreach ($blocks as $b) {
if (stripos($b->reason, $filter) !== false) {
if (!$include && $b->type == self::TYPE_PATTERN) {
if (stripos($b->hostname, $filter) !== false) { $include = true; }
else if (stripos($b->userAgent, $filter) !== false) { $include = true; }
else if (stripos($b->referrer, $filter) !== false) { $include = true; }
else if (stripos($b->ipRange, $filter) !== false) { $include = true; }
if (!$include && stripos(self::nameForType($b->type), $filter) !== false) {
if ($b->matchRequest($matchValue, '', '') != self::MATCH_NONE) {
else if ($b->type == self::TYPE_LOCKOUT && wfUtils::inet_pton($matchValue) == wfUtils::inet_pton($b->ip)) {
if (preg_match('/' . $matchValue . '/i', $b->ip)) {
if ($offsetProcessed < $offset) { //Still searching for the start offset
if ($limit != -1 && $limitProcessed >= $limit) {
* Returns all unexpired blocks of types wfBlock::TYPE_IP_MANUAL, wfBlock::TYPE_IP_AUTOMATIC_TEMPORARY, wfBlock::TYPE_IP_AUTOMATIC_PERMANENT, wfBlock::TYPE_WFSN_TEMPORARY, wfBlock::TYPE_RATE_BLOCK, and wfBlock::TYPE_RATE_THROTTLE.
* @param bool $prefetch If true, the full data for the block is fetched rather than using lazy loading.
public static function ipBlocks($prefetch = false) {
return self::allBlocks($prefetch, array(self::TYPE_IP_MANUAL, self::TYPE_IP_AUTOMATIC_TEMPORARY, self::TYPE_IP_AUTOMATIC_PERMANENT, self::TYPE_WFSN_TEMPORARY, self::TYPE_RATE_BLOCK, self::TYPE_RATE_THROTTLE));
* Finds an IP block matching the given IP, returning it if found. Returns false if none are found.
public static function findIPBlock($ip) {
$blocksTable = wfBlock::blocksTable();
$ipHex = wfDB::binaryValueToSQLHex(wfUtils::inet_pton($ip));
$query = "SELECT * FROM `{$blocksTable}` WHERE ";
$ofTypes = array(self::TYPE_IP_MANUAL, self::TYPE_IP_AUTOMATIC_TEMPORARY, self::TYPE_IP_AUTOMATIC_PERMANENT, self::TYPE_WFSN_TEMPORARY, self::TYPE_RATE_BLOCK, self::TYPE_RATE_THROTTLE);
$query .= "`type` IN (" . implode(', ', $ofTypes) . ') AND ';
$query .= "`IP` = {$ipHex} AND ";
$query .= '(`expiration` = ' . self::DURATION_FOREVER . ' OR `expiration` > UNIX_TIMESTAMP()) ORDER BY `blockedTime` DESC LIMIT 1';
$r = $wpdb->get_row($query, ARRAY_A);
$ip = wfUtils::inet_ntop($r['IP']);
return new wfBlock($r['id'], $r['type'], $ip, $r['blockedTime'], $r['reason'], $r['lastAttempt'], $r['blockedHits'], $r['expiration'], null);
* Returns all unexpired blocks of type wfBlock::TYPE_COUNTRY.
* @param bool $prefetch If true, the full data for the block is fetched rather than using lazy loading.
public static function countryBlocks($prefetch = false) {
return self::allBlocks($prefetch, array(self::TYPE_COUNTRY));
* Returns whether or not there is a country block rule.
public static function hasCountryBlock() {
$countryBlocks = self::countryBlocks();
return !empty($countryBlocks);
* Returns the value for the country blocking bypass cookie.
public static function countryBlockingBypassCookieValue() {
$val = wfConfig::get('cbl_cookieVal', false);
wfConfig::set('cbl_cookieVal', $val);
* Returns all unexpired blocks of type wfBlock::TYPE_PATTERN.
* @param bool $prefetch If true, the full data for the block is fetched rather than using lazy loading.
public static function patternBlocks($prefetch = false) {
return self::allBlocks($prefetch, array(self::TYPE_PATTERN));
* Returns all unexpired lockouts (type wfBlock::TYPE_LOCKOUT).
* @param bool $prefetch If true, the full data for the block is fetched rather than using lazy loading.
public static function lockouts($prefetch = false) {
return self::allBlocks($prefetch, array(self::TYPE_LOCKOUT));
* Returns the lockout record for the given IP if it exists.
public static function lockoutForIP($ip) {
$blocksTable = wfBlock::blocksTable();
$ipHex = wfDB::binaryValueToSQLHex(wfUtils::inet_pton($ip));
$row = $wpdb->get_row($wpdb->prepare("SELECT * FROM `{$blocksTable}` WHERE `IP` = {$ipHex} AND `type` = %d AND (`expiration` = %d OR `expiration` > UNIX_TIMESTAMP())", self::TYPE_LOCKOUT, self::DURATION_FOREVER), ARRAY_A);
return new wfBlock($row['id'], $row['type'], wfUtils::inet_ntop($row['IP']), $row['blockedTime'], $row['reason'], $row['lastAttempt'], $row['blockedHits'], $row['expiration'], null);
* Removes all blocks whose ID is in the given array.