<?php
///////////////////////////////////////////////////////////////////////////////////////////////////////
// BotBanish 9.0.02
// Randem Systems: https://randemsystems.support
// BotBanish Common Subroutines
// Date: 07/15/2026
// Usage:
//
///////////////////////////////////////////////////////////////////////////////////////////////////////

function BotBanishSendPostData($url, $data = '', $clearbefore = false, $clearafter = false, $report_errors = true, $domain_name = '') {

	global $sourcedir, $BotBanishSettings, $BotBanishText;

	if ($clearbefore)
		BotBanishFlushOutputBuffer();

	if (!is_array($data)) {

		parse_str($data, $row);	// Get all passed parameters
		$data = BotBanishDecodeData($row);
	}

	if (function_exists('curl_init'))	{

		$routine = 'curl';

		//open connection

		try {

			$dir = dirname(__FILE__);
			$ch = curl_init();

			curl_setopt($ch, CURLOPT_URL, $url);
			curl_setopt($ch, CURLOPT_POST, true);
			curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
			curl_setopt($ch, CURLOPT_HEADER, false);
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
			curl_setopt($ch, CURLOPT_USERAGENT, $BotBanishSettings['BOTBANISH_HTTP_USER_AGENT']);

			//execute post

			$returned_data = curl_exec($ch);

		} catch (Exception $e) {

			$datasent = PHP_EOL . 'Curl Error: ' . curl_error($ch) . PHP_EOL . 'Curl Error Code: ' . curl_errno($ch);
			BotBanishLogError(sprintf($BotBanishText['BotBanishClient_CollectionFailed'], $url, $datasent), $e, 'Error', $domain_name);
		}

	} else {

		if (isset($sourcedir)) {

			$routine = 'fetch_web_data';

			require_once($sourcedir . '/Subs-Package.php');
			$returned_data = fetch_web_data($url . '?' . var_export($data, true));

		} else {

			$routine = 'file_get_contents';

			$returned_data = file_get_contents($url . '?' . var_export($data, true));
		}
	}

	if (($returned_data === false) && $report_errors) {

		$datasent = PHP_EOL . 'Regular Error: ' . $routine . PHP_EOL . var_export($data, true);
		BotBanishLogError(sprintf($BotBanishText['BotBanishClient_CollectionFailed'], $url, $datasent), '', 'Error', $domain_name);
	}

	if ($clearafter)
		BotBanishFlushOutputBuffer();

	return $returned_data;
}

function BotBanishDecodeData($row) {

	global $BotBanishSettings;

	$replace = isset($BotBanishSettings['REPLACE']) ? $BotBanishSettings['REPLACE'] : true;
	$name = 'BOTBANISH_';

	foreach ($row as $key => $value) {

		if ($replace && strpos($key, $name) !== false) {

			unset($row[$key]);
			$row[str_replace($name, '', $key)] = urldecode($value);

		} else {

			$row[$key] = urldecode($value);
		}
	}

	return $row;
}

function BotBanishSendPostDataHTTP($url, $data, $clearbefore = false, $clearafter = false, $report_errors = true) {

	global $sourcedir, $BotBanishText;

	if ($clearbefore)
		BotBanishFlushOutputBuffer();

	$data = array('key1' => 'value1', 'key2' => 'value2');

	$options = array(
	  'http' => array(
		'header'  => "Content-type: application/x-www-form-urlencoded",
		'method'  => 'POST',
		'content' => http_build_query($data)
	  )
	);
	$context  = stream_context_create($options);
	$returned_data = file_get_contents($url, false, $context);

	if ($clearafter)
		BotBanishFlushOutputBuffer();

	return $returned_data;
}

function BotBanishRetrieveURLData($url) {

	global $sourcedir, $BotBanishSettings;

	BotBanishFlushOutputBuffer();

	if (function_exists('curl_init'))	{
		$ch = curl_init();
	    curl_setopt($ch, CURLOPT_AUTOREFERER, TRUE);
	    curl_setopt($ch, CURLOPT_HEADER, TRUE);
	    curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
	    curl_setopt($ch, CURLOPT_URL, $url);
	    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
	    curl_setopt($ch, CURLOPT_USERAGENT, $BotBanishSettings['BOTBANISH_HTTP_USER_AGENT']);
		curl_setopt($ch, CURLOPT_TIMEOUT, 400);

		$returned_data = curl_exec($ch);
		curl_close($ch);

	} else {

		if (isset($sourcedir)){
			require_once($sourcedir . '/Subs-Package.php');
			$returned_data = fetch_web_data($url);
		}else{
			$returned_data = file_get_contents($url);
		}
	}

	BotBanishFlushOutputBuffer();
	return $returned_data;
}

function BotBanishSendURLData($url, $data, $clearbefore = false, $clearafter = false) {

	global $sourcedir, $BotBanishURL;

	if ($clearbefore)
		BotBanishFlushOutputBuffer();

	$arr = !is_array($data) ? parse_str($data, $row) : BotBanishDecodeData($data);	// Get all passed parameters

	$query = $url . '?' . $arr;
	$BotBanishURL = $query;

	$botbanish_system = BotBanishCheckSystem();

	switch ($botbanish_system['system']) {

		case 'WORDPRESS':

			if (function_exists('wp_remote_get')) {

				$response = wp_remote_get($url, array(
														'timeout' => 45,
														'redirection' => 5,
														'httpversion' => '1.0',
														'blocking' => true,
														'headers' => array(),
														'body' => $arr,
														'cookies' => array(),
														'sslverify' => false,
														'user-agent' => $_SERVER['HTTP_USER_AGENT'],
													   )
													);

				if (is_wp_error($response)) {

					$datasent = !empty($arr) ? $arr : 'none';
					$error_message = $response->get_error_message();
					$errormsg = PHP_EOL . 'WordPress Error: ' . var_export($error_message, true) . PHP_EOL;
					BotBanishLogError(sprintf($BotBanishText['BotBanishClient_CollectionFailed'] . PHP_EOL . $errormsg, $url, $datasent), '', 'Error');
					$returned_data = $errmsg;

				} else {

					$returned_data = wp_remote_retrieve_body($response);
				}

				$returned_data = empty($returned_data) ? var_export($response, true) : $returned_data;
			}
			break;

		default:

			if (function_exists('curl_init')) {

				$ch = curl_init($query);
				curl_setopt($ch, CURLOPT_HEADER, false);
				curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
				$returned_data = curl_exec($ch);
				curl_close($ch);

			} else {

				if (isset($sourcedir)) {

					require_once($sourcedir . '/Subs-Package.php');
					$returned_data = fetch_web_data($query);

				} else {

					$returned_data = file_get_contents($query);
				}
			}
			break;
	}

	if ($clearafter)
		BotBanishFlushOutputBuffer();

	return $returned_data;
}

function BotBanishFindArrayInStringRaw($string, $cmd_array, $caseSensitive = true) {

	if (empty($string))
		return false;

	$i = 0;

	foreach ($cmd_array as $cmd){

		$pos = $caseSensitive ? strpos($string, $cmd) : stripos($string, $cmd);

		if ($pos !== false)
			break;

		$i++;
	}

	if ($pos === false)
		return false;

	return $cmd;
}

function BotBanishFindArrayInString($string, $cmd_array, $caseSensitive = true) {

	if (empty($string))
		return false;

	$str = $string;

	$stripedString = $caseSensitive ? str_replace($cmd_array, '', $str) : str_ireplace($cmd_array, '', $str);
	return strlen($stripedString) !== strlen($string);
}

function BotBanishArrayCheck($array_type) {

	global $BotBanishText;

	$error_array = array(
		'error' =>  '',
		'valid' => 1
	);

	if (!isset($array_type)){

		$error_array['error'] .= $BotBanishText['BotBanish_NotDefined'];
		$error_array['valid'] = 0;
	}

	if (empty($array_type)){

		$error_array['error'] .= $BotBanishText['BotBanish_NoValue'];
		$error_array['valid'] = 0;
	}

	if (!is_array($array_type)){

		$error_array['error'] .= $BotBanishText['BotBanish_NotArray'];
		$error_array['valid'] = 0;
	}

	if (!$error_array['valid'])
		$error_array['error'] = BotBanishDebugBackTrace();

	if (!empty($error_array['error']))
		BotBanishReturnData($error_array['error']);

	return $error_array;
}

function BotBanish_HTMLHoneyPotCreate() {

	global $BotBanishSettings;

	$htaccess_array = $BotBanishSettings['BOTBANISH_HTACCESS_ARRAY'];

	foreach ($htaccess_array as $htaccess_folder) {

		$filename = $htaccess_folder . $BotBanishSettings['BOTBANISH_HONEYPOT_FILE_HTML'];

		if (file_exists($filename) && (filesize($filename) >= strlen($filename)))
			return;

		BotBanishWriteFile($filename, sprintf($BotBanishSettings['BOTBANISH_HONEYPOT_HTML'], $BotBanishSettings['BOTBANISH_HONEYPOT_URL']));
	}
}

function BotBanish_Fatal_Error($error_index, $quit = true, $row = array()) {

	global $BotBanishSettings;

	$system = BotBanishCheckSystem();

	switch ($system) {

		case 'SMF':

			loadLanguage('Errors', 'BotBanish/BotBanishLanguage', true, true);		// Change SMF error messages to our selected language
			fatal_lang_error($error_index, 'BotBanish');	// Stop this guy in his tracks! You are not welcome here!
			break;

		case 'WORDPRESS':

			$user_id = BotBanish_GetWPUser();

			if (!empty($user_id)) {

				require_once($BotBanishSettings['BOTBANISH_INSTALL_DIR'] . 'wp-admin/includes/user.php');
				wp_delete_user($user_id);
			}
			break;
	}

	$status = (isset($row['forcelockout']) && !$row['forcelockout']) ? $row['forcelockout'] : $BotBanishSettings['BOTBANISH_ABUSE_LOCKOUT'];
	BotBanishRedirectExit($status, BotBanishGetIP());
}

function BotBanishReturnData($rows, $exit = true, $action = '') {

	global $BotBanishSettings;

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

	$rows = !is_array($rows) ? array($rows) : $rows;
	$data = BotBanishBuildReturn($rows);

//	if ($action == 'verify')
		BotBanishTraceIt('BotBanishReturnData - $data', $data, false);

	echo $data;

	BotBanishExit($exit);
}

function BotBanishBuildReturn($rows) {

	global $BotBanishSettings;

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

	$rows = !is_array($rows) ? array($rows) : $rows;
	$str = array();
	$contains_array = false;

	// If any value in array contains an array, we must process differently

	foreach($rows as $key => $value) {

		if (is_array($value)) {

			$contains_array = true;
			break;
		}
	}

	if ($contains_array) {

		foreach($rows as $key => $value)
			$str[$key] = is_array($value) ? $key . '=' . BotBanishSafeSerialize($value) : $key . '=' . $value;

	} else {

		$str['returned_data'] = is_array($rows) ? BotBanishSafeSerialize($rows) : $rows;
	}

	$params = implode('|', $str);

	return $params;
}

function BotBanishExtractData($returned_data, $err_array) {

	if (!isset($returned_data) || empty($returned_data))
		return false;

	$data = BotBanishSafeUnserialize($returned_data);

	if (is_array($data))
		return $data;

	// See if return has errors

	$str = str_ireplace($err_array, '', $data);

	if (strlen($str) !== strlen($data))
		return $data;

	// Multi-dimensional array (at least two rows MUST exist!)

	$rows = array();
	$lines = explode('|', $data);

	foreach ($lines as $line) {

		if (stripos($line, '=') !== false) {

			$key = explode('=', $line);

			if (BotBanishIsSerialized($key[1])) {

				$rows[$key[0]] = BotBanishSafeUnserialize($key[1]);

			} else {

				parse_str($key[1], $elements);
				$rows[$key[0]] = $elements;
			}

		} else {

			parse_str($line, $row[]);
		}
	}

	return $rows;
}

function BotBanishDisplayErrorExit($msg) {

	$msg = str_replace("\n", '<br />', $msg);
	echo $msg."<br />";

	return BotBanishExit(true);
}

function BotBanishURLFormatData($row, $alldata = false) {

	$data = '';
	$error_check = BotBanishArrayCheck($row);

	if (isset($error_check['valid']) && $error_check['valid']) {

		Foreach ($row as $item => $value){

			if (!empty($value) || $alldata == true) {

				if (!is_array($value)) {

						$data .= '&' . $item . '=' . urlencode($value);
				}else{

					foreach ($value as $key => $val) {

						$data .= '&' . $key . '=' . urlencode($val);
					}
				}
			}
		}
	}

	return $data;
}

function BotBanishGetArrayInfo($arraydata = array(), $errData = '') {

	// Check if processing an Error Document Redirection

	$data = '';
	$cause = '';

	if (empty($arraydata) || !is_array($arraydata))
		return $data;

	global $BotBanishSettings, $BotBanishText, $BotBanishSQL, $BotBanishURL;

	foreach ($arraydata as $key => $value) {
		$array_name = $key;
		break;
	}

	if ($array_name == 'SERVER_SOFTWARE') {

		$cause = isset($_SERVER['REDIRECT_URI']) ? $_SERVER['REDIRECT_URI'] : '';
		$cause = empty($cause) && isset($_SERVER['REDIRECT_QUERY_STRING']) ? $_SERVER['REDIRECT_QUERY_STRING']  : '';
		$cause = empty($cause) && isset($_SERVER['SCRIPT_URL']) ? $_SERVER['SCRIPT_URL']  : '';
		$cause = !empty($cause) ? $cause . PHP_EOL . $BotBanishText['filenotfound'] : '';

		if (isset($_SERVER['REMOTE_ADDR']) && !empty($cause)) {

			$country_info = BotBanishGetCountryInfo(BotBanishGetIP());
			$cause .= PHP_EOL . $BotBanishText['offending_country'] . ': ' . $country_info['country'];
		}
	}

	$data .= $BotBanishSettings['BOTBANISH_VERSION'] . PHP_EOL;
	$data .= !empty($BotBanishURL) ? $BotBanishText['last_url'] . ': ' . $BotBanishURL . PHP_EOL : '';
	$data .= !empty($BotBanishSQL) ? $BotBanishText['last_sql'] . ': ' . $BotBanishSQL . PHP_EOL : '';

	$data = PHP_EOL . '==============================' . $array_name . '=======================================' . PHP_EOL;
	$data .= !empty($cause) ? $BotBanishText['cause'] . ': ' . str_replace('&', PHP_EOL, urldecode($cause)) . PHP_EOL : '';
	$data = !empty($errData) ? $errData . PHP_EOL . PHP_EOL : '';
	$data .= PHP_EOL . BotBanishFormatArray($arraydata) . PHP_EOL;
	$data .= '=====================================================================' . PHP_EOL;
	return $data;
}

function BotBanishFormatArray($data = array()) {

	// if we encounter a string, see if it may be from a passed url string

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

		if ((stripos($data, '&') !== false) || (stripos($data, '?') !== false)) {

			parse_str($data, $dataarray);
			$data = $dataarray;

		}
	}

	$data = var_export($data, true);

	return $data;
}

function BotBanishRemoveDirectory($path) {

	if (!is_dir($path) || $path === '\\' || $path === '/')
		return;

	$files = scandir($path . '/');

    foreach ($files as $file) {

		if (($file !== '.') && ($file !== '..')) {
			$item = $path . '/' . $file;
			is_dir($item) ? BotBanishRemoveDirectory($item) : unlink($item);
		}
    }

    @rmdir($path);
    return;
}

function BotBanishMakeTree($srcPath, $mode) {

	@mkdir($srcPath, 0755, true);

	$path = str_replace('\\', '/', $srcPath);
    $paths = explode('/', $path);
	$fullPath = '';

	foreach ($paths as $path) {

		$fullPath .= $path;

		if (!is_dir($fullPath))
			@mkdir($fullPath, 0755);

		$fullPath .= '/';
	}
}

function BotBanishRecursiveCopy($source, $dst) {

	$src = realpath($source);

	if (is_dir($src)) {

		$dir = opendir($src);

		if (!is_dir($dst))
			@mkdir($dst, 0755, true);

		while (false !== ($file = readdir($dir))) {

			if (($file != '.') && ($file != '..')) {

				if (is_dir($src . '/' . $file)) {

					BotBanishRecursiveCopy($src . '/' . $file, $dst . '/' . $file);
				}

				else {

					@copy($src . '/' . $file, $dst . '/' . $file);
				}
			}
		}

		closedir($dir);

	}else{

		BotBanishLogError('Source folder does not exist: ' . $src, '', 'Error');
	}
}

function BotBanishCollectData($domain_name = '') {

	// Attempt to run the data collection script on the client for debugging purposes

	if (!empty($domain_name)){

		global $BotBanishSettings, $BotBanishURL;

		$url = $BotBanishSettings['BOTBANISH_INSTALL_URL'] . 'Client/BotBanishClientCheck.php';

		$row = array();
		$row['domain'] = $domain_name;
		$row['apikey'] = isset($BotBanishSettings['BOTBANISH_APIKEY']) ? $BotBanishSettings['BOTBANISH_APIKEY'] : 'Free';
		$row['system'] = BotBanishSafeSerialize(BotBanishCheckSystem());

		$data = BotBanishURLFormatData($row);
		$BotBanishURL = $data;
		$returned_data = BotBanishSendPostData($url, $data);
	}
}

function BotBanishFlushOutputBuffer() {

	$level = ob_get_level();

	while (ob_get_level() > $level)	{
		ob_end_flush();
	}
}

function BotBanishGetRedirectData() {

	global $BotBanishSettings, $client_info;

	$row = array();
	$docerrors = explode(',', $BotBanishSettings['BOTBANISH_DOC_ERRORS']);

	if (in_array($client_info['REDIRECT_STATUS'], $docerrors)) {

		if (isset($client_info['REDIRECT_QUERY_STRING'])) {

			parse_str($client_info['REDIRECT_QUERY_STRING'], $row);	// Get all passed parameters
			BotBanishDecodeURL($row);
		}
	}

	return $row;
}

function BotBanishReceivePostData($type = array()) {

	global $client_info;

	// Separate out passed information into $client_info

	$row = array();
	$client_info = array();

	if (empty($type))

		$method = isset($_POST) && !empty($_POST) ? $_POST : $_GET;
	else
		$method = $type;

	foreach($method as $key => $value) {

		if (stripos($key, 'BOTBANISH_') !== false) {

			$client_info[str_replace('BOTBANISH_', '', $key)] = urldecode($value);

		} else {

			// Preserve version formatting

			$str = urldecode($value);

			if ($key == 'version')

				$row[$key] = $str;
			else
				$row[$key] = strtolower($str);
		}
	}

	return $row;
}

function BotBanishCheckForAvailableUpdate() {

	global $BotBanishSettings;

	if (!$BotBanishSettings['BOTBANISH_CHECK_UPDATES'])
		return;

	$system = BotBanishCheckSystem();
	$row = BotBanishGetClientUpdateInfo();
	$html = $row['html'];
	
	$BotBanishHTML = '';

	// If we have an available update, alert the user. HTML code borrowed from w3schools.com

	if (!empty($html)) {

		$BotBanishHTML = '

			<style>
				.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}
				.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}
				.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}
				.btn .caret{margin-left:0}
			</style>

			<div class="container" align="center">
			  <button type="button" class="btn btn-info btn-lg">' . $html . '</button>
			</div>';
	}

	return $BotBanishHTML;
}

function BotBanishGetClientUpdateInfo() {

	global $BotBanishSettings;

	require $BotBanishSettings['BOTBANISH_SUB_DIR'] . 'BotBanishUpdateCheck.php';
	$html = BotBanishCheckForUpdate();
	return array('html' => $html);
}

function BotBanishCopyCheckIt($source, $destination, $file) {

	clearstatcache();

	if (!is_dir($destination))
		BotBanishMakeTree($destination, 0755);

	if (!is_dir($destination))
		return false;

	copy($source . $file, $destination . $file);

	if (!file_exists($destination . $file)) {

		BotBanishLogError('Copy did not complete - ' . $destination . $file, '', 'Error');
		return false;

	}else{

		BotBanishLogError('Not a source file / does not exist: ' . $source . $file, '', 'Error');
		return false;
	}

	return true;
}

function BotBanishAddClientData($row) {

	global $client_info;

	$client_info = !is_array($client_info) ? $_SERVER : $client_info;
//	$client_info = !is_array($client_info) && !empty($client_info) ? $_SERVER : $client_info;

	foreach ($client_info as $key => $value)
		$row['BOTBANISH_' . $key] = $value;

	return $row;
}

function BotBanishGetPassedData() {

	$row = array();

	switch ($_SERVER['REQUEST_METHOD']){

		case 'POST':
		case 'GET':
			$row = BotBanishReceivePostData();			// Get all Posted data
			break;

		default:

			// Special processing if using wget with special formatted parameter string

			$str = substr_count($_SERVER['QUERY_STRING'], '*') > 0 ? str_replace('*', '&', $_SERVER['QUERY_STRING']) : $_SERVER['QUERY_STRING'];
			parse_str($str, $row);	// Get all passed parameters
			BotBanishDecodeURL($row);
			$row['bot_ip'] = !isset($row['bot_ip']) ? $_SERVER['REMOTE_ADDR'] : $row['bot_ip'];
			break;
	}

	return $row;
}

function BotBanishGrabDump($var) {

	return '<pre>' . var_export($var, true) . '</pre>';
}

function BotBanishGrabArray($var) {

	$str = '<br>';

	foreach ($var as $item) {

		$str .= $item . '<br>';
	}

	return $str;
}

function BotBanishCheckForEOL($line, &$sql, &$start) {

	$sql .= ' ' . $line;

	// Line terminator MUST be at the very end of the line!

	if (strpos(rtrim($line), ';') === (strlen(rtrim($line)) - 1)) {
		return true;	// Found EOL
	}else{
		return false;
	}
}

function BotBanish_stripos($haystack, $needle) {

	$repstr = '  ';
	$findstr = array("\r\n", "\r", "\n");

	$haystack = str_ireplace($findstr, $repstr, $haystack);
	$needle = str_ireplace($findstr, $repstr, $needle);

	$pos = stripos($haystack, $needle);
	$haystack = '';
	$needle = '';

	return $pos;
}

function BotBanish_str_ireplace($find, $replace, $haystack) {

	$repstr = '~`~';
	$newstr = array("\r\n", "\r", "\n");

	$find = str_ireplace($newstr, $repstr, $find);
	$replace = str_ireplace($newstr, $repstr, $replace);
	$haystack = str_ireplace($newstr, $repstr, $haystack);

	$str = str_ireplace($find, $replace, $haystack);
	$str = str_ireplace($repstr, PHP_EOL, $str);

	$haystack = '';
	$find = '';
	$replace = '';

	return $str;
}

function BotBanishRemoveExtraCRLF($data) {

	$data = str_replace(PHP_EOL . PHP_EOL . PHP_EOL, PHP_EOL, $data);
	return $data;
}

function BotBanishFindConfigLocation($config = 'config.php') {

	$dir = rtrim(dirname(__FILE__), '/') . '/';
	$dir = str_replace('\\', '/', $dir);
	$found = false;

	while (true){

		if (file_exists($dir . $config)) {
			$found = true;
			break;
		}

		if ($dir == '/')
			break;

		$pre = $dir;
		$dir = rtrim($dir, '/');

		if ($dir === $pre)
			break;

		$dir = substr($dir, 0, strrpos($dir, '/') + 1);
	}

	if (!$found)
		$dir = '';

	return $dir;
}

function BotBanishFindSettingsLocation($folder = 'Client') {

	$dir = rtrim(dirname(__FILE__), '/') . '/';
	$dir = str_replace('\\', '/', $dir);
	$found = false;

	while (true){

		if (file_exists($dir . $folder) && is_dir($dir . $folder)) {
			$found = true;
			break;
		}

		if ($dir == '/')
			break;

		$pre = $dir;
		$dir = rtrim($dir, '/');

		if ($dir === $pre)
			break;

		$dir = substr($dir, 0, strrpos($dir, '/') + 1);
	}

	if (!$found)
		$dir = '';

	return $dir;
}

function BotBanishSetupDatabaseEntry(&$seq, $value, $type, $htaccess_entry, $skipipcheck) {

	// We can identify these characters in a UA but CANNOT use them in the .htaccess file alone or the server will crash!
	if ((strlen($value) === 1) && in_array($value, array('|', '[', '+', '*', '^', '(', ')', '\\', '\/', ':', ';', '?', '$')))
		return;

	if ($skipipcheck == false && $type == 'ip') {

		// If not a valid ip; we don't want it
		if (!BotBanishCheckIfValidIP($value))
			return;
	}

	$entry = stripslashes(sprintf($htaccess_entry['str'], $value));

	$row = array();
	$row['data'] = $entry;
	$row['type'] = $type;

	switch ($type) {

		case 'ip':
		case 'bot':

			$row['seq'] = 0;
			break;

		default:

			$row['seq'] = $seq++;
			break;
	}

	return $row;
}

function BotBanishAddRuleBlocks($filename, $items) {

	if (empty($items)) return;

	$source = BotBanishReadFile($filename);

	foreach ($items as $item) {

		switch($item['type']) {

			case 'data':

				foreach ($item['data'] as $data)
					$source = BotBanishAddDataBlock($item , $source);

				break;

			case 'rules':

				foreach ($item['rules'] as $rule)
					$source = BotBanishAddRuleBlock($rule['rule_start'], $rule['rule_end'], $rule['data'], $source);

				break;
		}
	}

	BotBanishWriteFile($filename, $source);
}

function BotBanishAddRuleBlock($start_entry, $end_entry, $rule_data, $source_data) {

	$str =  $start_entry . PHP_EOL . $rule_data . PHP_EOL . $end_entry . PHP_EOL . $source_data;

	return $str;
}

function BotBanishAddDataBlock($item, $source) {

}

function BotBanishRemoveRuleBlocks($filename, $items) {

	if (empty($items)) return;

	if (!file_exists($filename))
		return;

	$source = BotBanishReadFile($filename);

	if (empty($source))
		return;

	foreach ($items as $item) {

		switch($item['type']) {

			case 'data':

				foreach ($item['data'] as $data)
					$source = BotBanishRemoveDataBlock($data['source'], $data['destination'] , $source);

				break;

			case 'rules':

				foreach ($item['rules'] as $rule)
					$source = BotBanishRemoveRuleBlock($rule['rule_start'], $rule['rule_end'], $source);

				break;
		}
	}

	BotBanishWriteFile($filename, $source);
}

function BotBanishRemoveRuleBlock($start_entry, $end_entry, $source_data) {

	$lines = explode($start_entry, $source_data);

	$str = '';

	foreach ($lines as $line) {

		$items = explode($end_entry, $line);

		if (count($items) < 2)

			$str .= $line;
		else
			$str .= ltrim($items[1], " \n\r\t\v\0");
	}

	return $str;
}

function BotBanishRemoveDataBlock($source, $destination, $data) {

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

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

	str_replace($source, $destination, $data);
	return $data;
}

function BotBanishModData(&$source, $data_array) {
//==========================================================================================
// Example of a ModData call array
//
//	$items[] = array(
//		'finddata' => "require_once('config.php');",
//		'moddata' => PHP_EOL . "	require_once('BotBanish_Settings.php');" . PHP_EOL,
//		'location' => 'after'
//	);
//
// ModData ALWAYS add the code in a code block for easy removal
//==========================================================================================

	global $BotBanishSettings;

	// Break the data into two parts (before and after) to insert the new data string after the header
	// Encase the inserted data with tags so we can find it to remove it on uninstall

	if (($pos = stripos($source, $data_array['finddata'])) === false)
		return;

	$len = strlen($data_array['finddata']);

	switch ($data_array['location']){

		case 'after':

			$start_pos = $pos + strlen($data_array['finddata']) + 1;
			$data_start = substr($source, 0, $start_pos);
			$data_end = substr($source, $start_pos);
			break;

		case 'before':

			$start_pos = $pos;
			$data_start = substr($source, 0, $start_pos);
			$data_end = substr($source, $start_pos);
			break;

		case 'replace':
		default:

			$start_pos = $pos;
			$data_start = substr($source, 0, $start_pos);
			$data_end = substr($source, $len, $start_pos);
			break;
	}

	$source = $data_start . PHP_EOL . $BotBanishSettings['BOTBANISH_CODE_START'] . PHP_EOL . $data_array['moddata'] . PHP_EOL . $BotBanishSettings['BOTBANISH_CODE_END'] . PHP_EOL . $data_end;

	return;
}

function BotBanishCheckImage($source, $filename, $alt_filename) {

//	global $BotBanishSession;
	$BotBanishSession = BotBanishGetSession();
	
	$image = $source . $filename;

	$headers = BotBanishGetHeaders($image);

	if (!isset($headers['Status']) || stripos($headers['Status'], '200 OK') === false)
		$image = $source . $alt_filename;

	return $image;
}

function BotBanishFindSimilar(&$list, $item) {

	$rows = array();

	foreach ($list as $key => $value) {

		if (substr($value, 0, strlen($item)) === $item)
			$rows[$key] = $value;
	}

	return $rows;
}

function BotBanishGetTimeExtension() {

	$ext = str_ireplace(array('/', '-', ':', ' '), '', BotBanishGetCorrectTime());
	return $ext;
}

function BotBanishExtendTime($sleep = 0) {

	if (function_exists('apache_reset_timeout'))
		@apache_reset_timeout();

	if ($sleep !== 0) sleep($sleep);
}

function BotBanishCleanOutput() {

	// Get rid any output already present.

	if(ob_get_level() > 0)
		ob_end_clean();

	// If we can, clean anything already sent from the output buffer...

	if (function_exists('ob_clean') && ob_get_length() != 0)
		ob_clean();
}

function BotBanishAddDays($date, $days) {

//	global $BotBanishSettings;

//	date_default_timezone_set($BotBanishSettings['BOTBANISH_TIMEZONE']);
	$new_date = date_create($date);
	date_add($new_date, date_interval_create_from_date_string($days . ' days'));
	return date_format($new_date, "Y-m-d H:i:s");
}

function BotBanish_Array_iunique($array) {

    return array_intersect_key(

        $array,
        array_unique( array_map( "strtolower", $array ) )
    );
}

function BotBanishLogTrace($ip) {

	global $BotBanishSettings;

	// This function gets called after receiving the data from the client to record what we have received.
	// it does not get written to the database until the server has answered the request in BotBanishReturnData.
	// That is when the sister routine BotBanishLogTraceResponse is called to write the information to the database.

	// Also added functionality to log basic information from other functions to help in the debugging of this system.
	// more functionality can be added with assorted globals in the IF statement.

	if ((isset($BotBanishSettings['BOTBANISH_DEBUG_TRACE_ALL']) && $BotBanishSettings['BOTBANISH_DEBUG_TRACE_ALL'])
		|| (isset($BotBanishSettings['BOTBANISH_DEBUG_FORCELOCKOUT']) && $BotBanishSettings['BOTBANISH_DEBUG_FORCELOCKOUT'])) {

		global $debugtracerow;

		$request = (isset($_POST) && !empty($_POST)) ? $_POST : $_GET;
		$date = BotBanishGetCorrectDateTimeB();
		$debugtracerow = BotBanishRetrieveSessionInfo($ip);
		$debugtracerow['bot_ip'] = $ip;
		$debugtracerow['datarow'] = BotBanishFormatArray($request);

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

			global $botbanish_info, $client_info;

			$debugtracerow['domain_name'] = isset($botbanish_info['DOMAIN_NAME']) && !empty($botbanish_info['DOMAIN_NAME']) ? $botbanish_info['DOMAIN_NAME'] : $_SERVER['REMOTE_ADDR'];
			$debugtracerow['server_info'] = BotBanishGetArrayInfo($_SERVER);
			$debugtracerow['client_info'] = BotBanishGetArrayInfo($client_info);
			$debugtracerow['botbanish_info'] = BotBanishGetArrayInfo($botbanish_info);

		} else {

			global  $BotBanishSettings;;

			$debugtracerow['domain_name'] = isset($_SERVER['SERVER_NAME']) && !empty($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : $_SERVER['REMOTE_ADDR'];
			$debugtracerow['server_info'] = '';
			$debugtracerow['client_info'] = BotBanishGetArrayInfo($_SERVER);
			$debugtracerow['botbanish_info'] = BotBanishGetArrayInfo($BotBanishSettings);
		}
	}
}

function BotBanishLogTraceResponse($datarow) {

	global $BotBanishSettings;

	// Sister routine to BotBanishLogTrace which sets up most of data that will be written to the database

	if ((isset($BotBanishSettings['BOTBANISH_DEBUG_TRACE_ALL']) && $BotBanishSettings['BOTBANISH_DEBUG_TRACE_ALL'])
		|| (isset($BotBanishSettings['BOTBANISH_DEBUG_FORCELOCKOUT']) && $BotBanishSettings['BOTBANISH_DEBUG_FORCELOCKOUT'])) {

		global $debugtracerow;

		if (!isset($debugtracerow['bot_ip']) || empty($datarow['bot_ip']))
			return;

		if (!isset($debugtracerow['bot_ip']))
			$debugtracerow['bot_ip'] = $row['bot_ip'];

		$debugtracerow['response'] = BotBanishFormatArray($datarow);

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

			$table = 'bbs_botbanishserver_debug_trace';

		} else {

			$table = 'bbc_botbanishclient_debug_trace';
			$debugtracerow['server_info'] = 'ForceLockout = ' . $datarow['forcelockout'] . PHP_EOL . BotBanishDebugBackTrace();
		}

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

		$sql = 'INSERT INTO `' . $table . '` (' . $params['columns'] . ') VALUES (' . $params['values'] . ')' .
				' ON DUPLICATE KEY UPDATE ' . $params['updates'];

		BotBanishExecuteSQL($sql, true);
	}
}

function BotBanishReplaceQuotes($data) {

	$str = str_ireplace(array('``', '`.', '.`'), array('`','.', '.'), $data);
	return $str;
}

function BotBanishStripQuotes($data) {

	$str = str_ireplace(array('`', '"', '"'), '', $data);
	return $str;
}

function BotBanishRemoveCodeBlock($start_entry, $end_entry, $filename) {

	// Find all code blocks and remove them

	$data = file_get_contents($filename);
	$lth = strlen($data);

	while (true) {

		// Look for the start with a new line at the begining

		$pos_start = stripos($data, PHP_EOL . $start_entry);

		// If not found then look for the start without the new line at the beginning

		if ($pos_start === false)
			$pos_start = stripos($data, $start_entry);

		$pos_end = stripos($data, $end_entry . PHP_EOL);

		if ($pos_end === false)
			$pos_end = stripos($data, $end_entry);

		if (($pos_start !== false) && ($pos_end !== false)) {

			if ($pos_end > $pos_start)
				$data = substr($data, 0, $pos_start) . substr($data, $pos_end + strlen($end_entry . PHP_EOL));
		}else{
			break;
		}
	}

	if (strlen($data) < $lth)
		file_put_contents($filename, $data);
}

function BotBanishSafeUnserialize($data) {

	if (BotBanishIsSerialized($data)) {

		$tmp = stripslashes($data);
		BotBanishRestoreErrorHandler();
		$str = @unserialize($tmp);
		BotBanishSetErrorHandler();

		if (!is_array($str))
			$str = unserialize($data);

		if (!is_array($str)) {

			BotBanishLogError('Unserialization Error: ' . $data, PHP_EOL . PHP_EOL . '$tmp = ' . $tmp . PHP_EOL . PHP_EOL . '$str = ' . $str, 'SerializeError');
			$str = array();
		}

	} else {

		$str = $data;
	}

	return $str;
}

function BotBanishSafeSerialize($data) {

	$str = $data;

	if (!BotBanishIsSerialized($data)) {

		if (is_array($data)) {

			$str = addslashes(serialize($data));
			$tmp = unserialize(stripslashes($str));

			if (!is_array($tmp)) {

//				BotBanishLogError('Serialization Error: ', $data, 'SerializeError');
				BotBanishLogError('Serialization Error: ' . $data, PHP_EOL . PHP_EOL . '$tmp = ' . $tmp . PHP_EOL . PHP_EOL . '$str = ' . $str, 'SerializeError');
				$str = array();
			}
		}

	}

	return $str;
}

function BotBanishIsSerialized( $data, $strict = true ) {

    // If it isn't a string, it isn't serialized. : ''
    if ( ! is_string( $data ) ) {
        return false;
    }
    $data = trim( $data );
    if ( 'N;' === $data ) {
        return true;
    }
    if ( strlen( $data ) < 4 ) {
        return false;
    }
    if ( ':' !== $data[1] ) {
        return false;
    }
    if ( $strict ) {
        $lastc = substr( $data, -1 );
        if ( ';' !== $lastc && '}' !== $lastc ) {
            return false;
        }
    } else {
        $semicolon = strpos( $data, ';' );
        $brace     = strpos( $data, '}' );
        // Either ; or } must exist.
        if ( false === $semicolon && false === $brace ) {
            return false;
        }
        // But neither must be in the first X characters.
        if ( false !== $semicolon && $semicolon < 3 ) {
            return false;
        }
        if ( false !== $brace && $brace < 4 ) {
            return false;
        }
    }
    $token = $data[0];
    switch ( $token ) {
        case 's':
            if ( $strict ) {
                if ( '"' !== substr( $data, -2, 1 ) ) {
                    return false;
                }
            } elseif ( false === strpos( $data, '"' ) ) {
                return false;
            }
            // Or else fall through.
        case 'a':
        case 'O':
            return (bool) preg_match( "/^{$token}:[0-9]+:/s", $data );
        case 'b':
        case 'i':
        case 'd':
            $end = $strict ? '$' : '';
            return (bool) preg_match( "/^{$token}:[0-9.E+-]+;$end/", $data );
    }
    return false;
}

function BotBanishCreateSpecialSettings() {

	$system = BotBanishCheckSystem();

	switch ($system['system']) {

		case 'SMF':

			// Add BotBanish to Pretty URLs skipaction setting.
			// See if Pretty URLs is installed

			$name = 'pretty_';
			$tables = BotBanishGetTableNames($name);

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

				global $sourcedir, $db_prefix, $modSettings;

				include_once $sourcedir . '/Subs.php';
				$botSettings = array();
				$value = 'pretty_skipactions';

				if (isset($modSettings[$value]) && !empty($modSettings[$value])) {

					if (stripos($modSettings[$value], 'botbanish') === false) {

						$botSettings[$value] = $modSettings[$value] . ',botbanish';
						updateSettings($modSettings[$value], true);
					}

				} else {

					if (!isset($modSettings[$value]) || empty($modSettings[$value])) {

						$botSettings[$value]= 'botbanish';
						updateSettings($botSettings, false);
					}
				}
			}

			break;

		default:

			break;
	}
}

function BotBanish_UTF8_Encode(string $str): string {

	if (function_exists('mb_convert_encoding')) {

		$output = mb_convert_encoding($str, 'UTF-8', mb_list_encodings());

	} else {

		if (function_exists('iconv')) {

			$output = iconv('ISO-8859-1', 'UTF-8', $str);

		} else {

			$str .= $str;
			$len = strlen($str);

			for ($i = $len >> 1, $j = 0; $i < $len; ++$i, ++$j) {

				switch (true) {

					case $str[$i] < "\x80": $str[$j] = $str[$i]; break;
					case $str[$i] < "\xC0": $str[$j] = "\xC2"; $str[++$j] = $str[$i]; break;
					default: $str[$j] = "\xC3"; $str[++$j] = \chr(\ord($str[$i]) - 64); break;
				}
			}

			$output = substr($str, 0, $j);
		}
	}

    return $output;
}

function BotBanishTraceRoutine($func) {

	$args = BotBanish_GetArguments($func);
	BotBanishTraceIt($func, $args, true);
}

function BotBanish_GetArguments( $funcName ) {

    return array_map( function( $parameter ) { return $parameter->name; },
        (new ReflectionFunction($funcName))->getParameters() );
}

function BotBanishIsActive() {

	global $BotBanishSettings;

	if (isset($BotBanishSettings['BOTBANISH_ACTIVE']) && ($BotBanishSettings['BOTBANISH_ACTIVE']))

		return true;
	else
		return false;
}

function BotBanishFixExplode($delimiter, $str) {

	$arr = explode($delimiter, $str);
	$arr = array_map('trim', $arr);
	$arr = array_filter($arr);
	$arr = array_unique($arr);
	return $arr;
}

function BotBanish_Request_Method($row) {

	global $BotBanishSettings;

	$data = BotBanishURLFormatData($row);
	$returned_data = BotBanishProcessRequest($data, $BotBanishSettings['BOTBANISH_REQUEST_METHOD'], false, true);

	if (isset($returned_data['forcelockout']) && ($returned_data['forcelockout'] !== 0))
		BotBanishExit(true);
}
?>