<?php
///////////////////////////////////////////////////////////////////////////////////////////////////////
// BotBanish 9.0.02
// Randem Systems: https://randemsystems.support
// BotBanish IP Routines
// Date: 07/15/2026
//
///////////////////////////////////////////////////////////////////////////////////////////////////////

function BotBanishGetIP() {

	// Check for proxy and forwarding addresses
	// HTTP_X_FORWARDED_FOR may contain multiple IP's separated by commas
	// HTTP_CDN_LOOP may contain elements such as Cloudfare

	$search = array('REMOTE_ADDR', 'HTTP_X_REAL_IP', 'HTTP_CF_CONNECTING_IP', 'HTTP_CLIENT_IP');
	$ip = '';

	foreach ($search as $find) {

		if (isset($_SERVER[$find]))
			$ip = $_SERVER[$find];
	}

	if (empty($ip)) return $ip;

	if (!BotBanishCheckIfValidIP($ip))
		return $ip; // Not a valid IP address

	$debug_server = explode(',', '::1,127.0.0.1');

	if (in_array($ip, $debug_server)) {

		$ip = getHostByName(getHostName());	// This only happens in the debugging environment

	} else {

		if (empty($ip) || (strlen($ip) < 8))
			$ip = getHostByName(getHostName());	// This only happens in the debugging environment
	}

//	$ip = '79.112.102.110';	// Test ONLY!!!!
	
	return $ip;
}

function BotBanishSelectIPData($ip, $table) {

	// Find IP in our local database

	$sql = 'SELECT `ip_id` FROM `' . $table . '` WHERE `bot_ip` = "' . $ip . '" LIMIT 1';
	$row = BotBanishExecuteSQL($sql);
	return $row;
}

function BotBanishBlockedIPInsert($ip_list, $table, $forcelockout = 1) {

	global $BotBanishSettings, $BotBanishSession;

	if (!is_array($ip_list))
		$ip_list = array($ip_list);

	$date =  BotBanishGetCorrectTimeB();
	$user_agent = $BotBanishSettings['BOTBANISH_HTTP_USER_AGENT'];
//	$row['user_agent'] = isset($_SERVER['HTTP_USER_AGENT']) ? addslashes(BotBanishFixUserAgent($_SERVER['HTTP_USER_AGENT'])) : '';

	// Add blocked IP to our local database (Ignore duplicates)

	foreach ($ip_list as $ip) {

		$row = BotBanishRetrieveSessionInfo($ip);

		$country_info = BotBanishGetCountryInfo($ip);
		$geo_data = $BotBanishSession->getSessionKey('geo_data');

		$row['country'] = $country_info['country'] !== 'null' ? $country_info['country'] : '';
		$row['country_code'] = $country_info['country_code'] !== 'null' ? $country_info['country_code'] : '';
		$row['geo_info'] = BotBanishSafeSerialize($geo_data);

		$row['bot_ip'] = $ip;
		$row['forcelockout'] = $forcelockout;
		$row['domain_name'] = BotBanishGetDomainName($ip);
		$row['user_agent'] = $user_agent;
		$row['domain'] = BotBanishGetDomainOrigin($ip, true);

		$params = BotBanishBuildSQLStatementInsert($row, $table);

		$sql = 'INSERT INTO `' . $table . '` (' . $params['columns'] . ') VALUES (' . $params['values'] . ')';

		if (isset($params['updates']) && !empty($params['updates']))
			$sql .= ' ON DUPLICATE KEY UPDATE ' . $params['updates'];

		BotBanishExecuteSQL($sql);
	}
}

/**
 * Convert a range of given IP number into a single string.
 * It's practically the reverse function of ip2range().
 *
 * @example
 * range2ip(array(10, 10, 10, 0), array(10, 10, 20, 255)) returns '10.10.10-20.*
 *
 * @param array $low The low end of the range in IPv4 format
 * @param array $high The high end of the range in IPv4 format
 * @return string A string indicating the range
 */

function BotBanish_Range2ip($low, $high)
{
	$low = inet_dtop($low);
	$high = inet_dtop($high);

	if ($low == '255.255.255.255') return 'unknown';
	if ($low == $high)
		return $low;
	else
		return $low . '-' . $high;
}

/**
 * Convert a single IP to a ranged IP.
 * internal function used to convert a user-readable format to a format suitable for the database.
 *
 * @param string $fullip The full IP
 * @return array An array of IP parts
 */

function BotBanish_ip2Range($fullip)
{
	// Pretend that 'unknown' is 255.255.255.255. (since that can't be an IP anyway.)
	if ($fullip == 'unknown')
		$fullip = '255.255.255.255';

	$ip_parts = explode('-', $fullip);
	$ip_array = array();

	// if ip 22.12.31.21
	if (count($ip_parts) == 1 && BotBanish_isValidIP($fullip))
	{
		$ip_array['low'] = $fullip;
		$ip_array['high'] = $fullip;
		return $ip_array;
	} // if ip 22.12.* -> 22.12.* - 22.12.*
	elseif (count($ip_parts) == 1)
	{
		$ip_parts[0] = $fullip;
		$ip_parts[1] = $fullip;
	}

	// if ip 22.12.31.21-12.21.31.21
	if (count($ip_parts) == 2 && BotBanish_isValidIP($ip_parts[0]) && BotBanish_isValidIP($ip_parts[1]))
	{
		$ip_array['low'] = $ip_parts[0];
		$ip_array['high'] = $ip_parts[1];
		return $ip_array;
	}
	elseif (count($ip_parts) == 2) // if ip 22.22.*-22.22.*
	{
		$valid_low = BotBanish_isValidIP($ip_parts[0]);
		$valid_high = BotBanish_isValidIP($ip_parts[1]);
		$count = 0;
		$mode = (preg_match('/:/', $ip_parts[0]) > 0 ? ':' : '.');
		$max = ($mode == ':' ? 'ffff' : '255');
		$min = 0;
		if (!$valid_low)
		{
			$ip_parts[0] = preg_replace('/\*/', '0', $ip_parts[0]);
			$valid_low = BotBanish_isValidIP($ip_parts[0]);
			while (!$valid_low)
			{
				$ip_parts[0] .= $mode . $min;
				$valid_low = BotBanish_isValidIP($ip_parts[0]);
				$count++;
				if ($count > 9) break;
			}
		}

		$count = 0;
		if (!$valid_high)
		{
			$ip_parts[1] = preg_replace('/\*/', $max, $ip_parts[1]);
			$valid_high = BotBanish_isValidIP($ip_parts[1]);
			while (!$valid_high)
			{
				$ip_parts[1] .= $mode . $max;
				$valid_high = BotBanish_isValidIP($ip_parts[1]);
				$count++;
				if ($count > 9) break;
			}
		}

		if ($valid_high && $valid_low)
		{
			$ip_array['low'] = $ip_parts[0];
			$ip_array['high'] = $ip_parts[1];
		}
	}

	return $ip_array;
}

function BotBanish_isValidIP($IPString)
{
	return filter_var($IPString, FILTER_VALIDATE_IP) !== false;
}

function BotBanishCheckForIPBlock($ip) {

	$BotBanishSession = BotBanishGetSession();
	
	$status = BotBanishCheckForBadIP($ip);

	if (!empty($BotBanishSession->getSessionKey('locked')))
		return true;

	return false;
}

function BotBanishLockoutIPRange($ip) {

	global $BotBanishSettings;

	$ip_items = BotBanishGetIPRangeList($ip);
	$table = (isset($BotBanishSettings['BOTBANISH_SERVER']) && $BotBanishSettings['BOTBANISH_SERVER']) ? 'bbs_botbanishserver_ip_bad' : 'bbc_botbanishclient_ip_bad';

	foreach ($ip_items as $ip_item) {

		$sql = 'SELECT * FROM `' . $table . '`
					WHERE `bot_ip` LIKE "' . $ip_item . '%"';

		$rows = BotBanishExecuteSQL($sql);	// Multiple rows can be returned

		if (is_array($rows) && (count($rows) >= $BotBanishSettings['BOTBANISH_MAX_IP_RANGE_COUNT'])) {

			foreach ($rows as $row) {

				if (!empty($row['forcelockout']))
					continue;

				$row['type'] = 'ip';
				$row['forcelockout'] = $BotBanishSettings['BOTBANISH_IP_RANGE_LOCKOUT'];

				if ($BotBanishSettings['BOTBANISH_SERVER'])

					BotBanishServerIPLockOut($row);
				else
					BotBanishInsertLocal($row);
			}
		}
	}
}

// Forcibly write to the HTACCESS file the group range block information

function BotBanishBlockedIPRangeCheck($ip) {

	global $BotBanishSettings;

	$ip_items = BotBanishGetIPRangeList($ip);
	if (count($ip_items) > 1) unset($ip_items[0]);
	$table = ($BotBanishSettings['BOTBANISH_SERVER'] == 1) ? 'bbs_botbanishserver_ip_bad' : 'bbc_botbanishclient_ip_bad';

	foreach ($ip_items as $ip_item) {

		$sql = 'SELECT `bot_ip` FROM `' . $table . '`
					WHERE `bot_ip` = "'.$ip_item.'"';

		if ($BotBanishSettings['BOTBANISH_SERVER'])
			$sql .=	' AND `forcelockout` > 0';

		$rows = BotBanishExecuteSQL($sql);	// only 1 rows can be returned

		if ($rows != false) {

			// We are doing range blocking so override HTACCESS processing
			$GLOBALS['HTACCESS_OVERRIDE'] = 1;
			BotBanishHTACCESSAddEntry($ip_item, 'ip');
			unset($GLOBALS['HTACCESS_OVERRIDE']);
		}
	}
}

function BotBanishIPGetList($table) {

	$sql = 'SELECT `bot_ip` FROM `' . $table . '` WHERE `active` = 1';
	$rows = BotBanishExecuteSQL($sql);
	$ip_list = array();

	if (!empty($rows) && is_array($rows)) {

		foreach ($rows as $row) {

//			if (BotBanishCheckIfValidIP($row['bot_ip']))
				$ip_list[] = $row['bot_ip'];
		}
	}

	return $ip_list;
}

function BotBanishDeleteIPRange($ip_item, $table, $ip_list) {

	global $BotBanishSettings;

	$date =  BotBanishGetCorrectTime();

	if (!is_array($ip_list))
		$ip_list = array($ip_list);

	if (isset($BotBanishSettings['BOTBANISH_SERVER']) && ($BotBanishSettings['BOTBANISH_SERVER'])) {

		$sql = 'SELECT * FROM `' . $table . '`
					WHERE `bot_ip` LIKE "%' . $ip_item . '%"';

		$rows = BotBanishExecuteSQL($sql);

		if (is_array($rows) && !empty($rows)) {

			foreach ($rows as $row) {

				if (isset($row['updated'])) unset($row['updated']);
				$ip_list[] = $row['bot_ip'];
				$params = BotBanishBuildSQLStatementInsert($row, $table);

				$sql = 'INSERT INTO `' . 'bbs_botbanishserver_ip_removed` (' . $params['columns'] . ') VALUES (' . $params['values'] . ')';

				if (isset($params['updates']) && !empty($params['updates']))
					$sql .= ' ON DUPLICATE KEY UPDATE ' . $params['updates'];

				BotBanishExecuteSQL($sql);
			}
		}
	}

	$sql = 'DELETE FROM `' . $table . '` WHERE `bot_ip` LIKE "' . $ip_item . '%"';
	BotBanishExecuteSQL($sql);
	BotBanishHTACCESSRemoveEntry($ip_list, 'ip');
}

function BotBanishInsertLocal($row) {

	global $BotBanishSettings, $BotBanishSession;

	if (isset($BotBanishSettings['BOTBANISH_REDIRECT_ONLY_ACTIVE']) && $BotBanishSettings['BOTBANISH_REDIRECT_ONLY_ACTIVE'])
		return;

	$table = 'bbc_botbanishclient_ip_bad';

	if (!isset($row['country']) || empty($row['country'])) {

		$country_info = $BotBanishSession->getSessionKey('botip');
		$geo_data = $BotBanishSession->getSessionKey('geodata');

		$row['country'] = $country_info['country'];
		$row['country_code'] = $country_info['country_code'];
		$row['geo_info'] = BotBanishSafeSerialize($geo_data);
	}

	$ip = $row['bot_ip'];
	$date = BotBanishGetCorrectTimeB();

	$session = BotBanishRetrieveSessionInfo($ip);

	$row['first_hit'] = $session['first_hit'];
	$row['last_hit'] = $date;
	$row['hit_count'] = $session['hit_count'];

	BotBanishUpdateSessionInfo($ip, $row);

	$sql = 'SELECT `ip_id` FROM `' . $table . '` WHERE `bot_ip` = "' . $ip . '" LIMIT 1';
	$update = BotBanishExecuteSQL($sql);
	$update = BotBanishLimitReturn($update);

	$row['domain_name'] = (!isset($row['domain_name']) || empty($row['domain_name'])) ? BotBanishGetDomainName($_SERVER) : $row['domain_name'];
	$row['domain'] = (!isset($row['domain']) || empty($row['domain'])) ? BotBanishGetDomainOrigin($ip, true) : $row['domain'];

	$params = BotBanishBuildSQLStatementUpdate($row, $table);
	$sql = 'INSERT INTO `' . $table . '` ('.$params['columns'].') VALUES ('.$params['values'].')';

	if (isset($params['updates']) && !empty($params['updates']))
		$sql .= ' ON DUPLICATE KEY UPDATE ' . $params['updates'];

	BotBanishExecuteSQL($sql);

	if (!empty($row['forcelockout']))
		$BotBanishSession->setSessionKey('locked', $row['forcelockout']);
}

function BotBanishRemoveLocalIP($ip) {

	$table = 'bbc_botbanishclient_ip_bad';
	$sql = 'DELETE FROM `' . $table . '` WHERE `bot_ip` = "' . $ip . '"';
	BotBanishExecuteSQL($sql);
}

function BotBanishGetIPRangeList($ip) {

	$ip_list = array();

	// Only for IPv4 addresses

	if (BotBanishCheckIfValidIPv4($ip)) {

		$parts = explode('.', $ip);

		$ip_list[] = $ip;
		$ip_list[] = $parts[0] . '.' . $parts[1] . '.' . $parts[2] . '.';
		$ip_list[] = $parts[0] . '.' . $parts[1] . '.';

	} else {

		if (BotBanishCheckIfValidIPv6($ip)) {

			$parts = explode('::', $ip);
			$ip_list[] = $ip;

			if (count($parts) > 2) {

				$ip_list[] = $parts[0] . '::' . $parts[1] . '::' . $parts[2] . '::' . $parts[3];
				$ip_list[] = $parts[0] . '::' . $parts[1] . '::' . $parts[2];
			}

		} else {

			$error = 'IP is not a valid IP4 / IP6 address: ' . $ip;
			BotBanishLogError($error, $ip, 'IPRange');
		}
	}

	return $ip_list;
}

function BotBanishCheckIfValidIP($ip) {

	// Check if a valid IP address format

	if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {

		if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6))
				return false; // Not a valid IP address
	}

	return true;	// A valid IPv4 or IPv6 address
}

function BotBanishCheckIfValidIPv4($ip) {

	// Check if a valid IPv4 address format

	if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4))
		return false; // Not a valid IPv4 address

	return true;	// A valid IPv4 address
}

function BotBanishCheckIfValidIPv6($ip) {

	// Check if a valid IPv6 address format

	if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6))
		return false; // Not a valid IPv6 address

	return true;	// A valid IPv6 address
}

function BotBanishCleanUpClientTable() {

	global $BotBanishSettings;

	$day = (60 * 60 * 24);	// 1 day
	$cleanup_days = isset($BotBanishSettings['BOTBANISH_CLIENT_CLEANUP_DAYS_IP']) ? $BotBanishSettings['BOTBANISH_CLIENT_CLEANUP_DAYS_IP'] : 365;

	$seconds = $day * $cleanup_days;	// User assigned days

	BotBanishSaveOverrideState();

	if (defined('DIR_APPLICATION'))
		BotBanishCleanCustomerTable();

	// This should be run once a day to cleanup the IP table. This is because shared IP users can get locked out
	// by some other user with the same IP. No other way to achieve IP lockout that is fair.
	// If the IP lockout needs to be permanent it will need to be done in the .htaccess file.

	if (!isset($BotBanishSettings['BOTBANISH_CLIENT_CLEANUP_RUN']) || $BotBanishSettings['BOTBANISH_CLIENT_CLEANUP_RUN'] == 0)
		return;

	if (!isset($BotBanishSettings['BOTBANISH_CLIENT_CLEANUP']) || $BotBanishSettings['BOTBANISH_CLIENT_CLEANUP'] == 0)
		return;

	$statements = array();

	$statements[] =
		// Delete any old IP information from the IP table that we have not seen in over the set time period. We do this to preserve the checking integrity
		// so that if an attacking BOT/USER has stopped using the IP we will allow future legitimate users to use it on newer registered systems.
		// All the older systems will have locked the IP out so we can reset our system to be up to date.

		'DELETE FROM `bbc_botbanishclient_ip_bad` WHERE DATEDIFF(DATE(NOW()), FROM_UNIXTIME(`last_hit`)) > ' . $cleanup_days;

	foreach ($statements as $sql)
		BotBanishExecuteSQL($sql);

	BotBanishCleanupLogs($seconds);

	$BotBanishSettings['BOTBANISH_CLIENT_CLEANUP_RUN'] = true;
	$BotBanishSettings['BOTBANISH_CLIENT_CLEANUP'] = BotBanishGetCorrectDateTime();
	BotBanishRestoreOverrideState();
}

function BotBanishCleanCustomerTable() {

	// We need to delete customer accounts in OpenCart that have been created using bogus information

	$table = DB_PREFIX . 'customer';
	$sql = 'SELECT `customer_id`, LENGTH(`lastname`) - LENGTH(`firstname`) AS newlen FROM `' . $table . '` WHERE  LOCATE(`firstname`, `lastname`) = 1';
	$rows = BotBanishExecuteSQL($sql);

	if (is_array($rows) && !empty($rows)) {

		foreach ($rows as $row) {

			if ($row['newlen'] == 2) {

				$sql = 'DELETE FROM `' . $table . '` WHERE `customer_id` = ' . $row['customer_id'];
				BotBanishExecuteSQL($sql);
			}
		}
	}
}

function BotBanishCheckForBadIP($ip) {

	global $BotBanishSettings;

	if (isset($BotBanishSettings['BOTBANISH_REDIRECT_ONLY_ACTIVE']) && !$BotBanishSettings['BOTBANISH_REDIRECT_ONLY_ACTIVE'])
		return false;

	$sql = 'SELECT `ip_id` FROM `' . 'bbc_botbanishclient_ip_bad` WHERE `bot_ip` = "' . $ip . '" AND `active` = 1 LIMIT 1';
	$rows = BotBanishExecuteSQL($sql);
	$row = BotBanishLimitReturn($rows);

	$status =  (is_array($row) && !empty($row)) ? true : false;

	if (!$status) {

		$BotBanishSession = BotBanishGetSession();
	
		if (!empty($BotBanishSession->getSessionKey('locked')))
			return true;
	}

	return $status;
}

function BotBanishCheckForGoodIP($ip) {

	$table = 'bbc_botbanishclient_ip_good';

	$sql = 'SELECT `ip_id` FROM `' . $table . '` WHERE `bot_ip` = "' . $ip . '" AND `active` = 1 LIMIT 1';
	$rows = BotBanishExecuteSQL($sql);
	$row = BotBanishLimitReturn($rows);

	$status =  (is_array($row) && !empty($row)) ? true : false;
	return $status;
}

function BotBanishCheckForIPOverride($ip) {

	$lockout = false;

	if (!empty($ip)) {

		// If we should not block ip, do nothing

		if (!BotBanishCheckForGoodIP($ip)) {

			if (BotBanishCheckForBadIP($ip)) {
				
				global $BotBanishSettings;

				$lockout = $BotBanishSettings['BOTBANISH_IP_LOCKOUT']; // If found in the table, Force a local lockout!!
			}
		}
	}

	return $lockout;
}
?>