<?php
///////////////////////////////////////////////////////////////////////////////////////////////////////
// BotBanish 7.1.06
// Randem Systems: https://randemsystems.support
// BotBanish HTACCESS Subroutines
// Date: 04/22/2026
// Usage:
//
///////////////////////////////////////////////////////////////////////////////////////////////////////

function BotBanishPrepareHTACCESS() {

	$htaccess_array = BotBanishGetHTACCESSLocations();
	BotBanishDefineGlobals('BOTBANISH_HTACCESS_ARRAY', $htaccess_array);

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

	$system = BotBanishCheckSystem();

	foreach ($htaccess_array as $htaccessFolder) {

		// Backup .htaccess file is it exists

		$filename = $htaccessFolder . '.htaccess';

		if (file_exists($filename))
			copy($filename, $filename . BotBanishGetTimeExtension());

		switch ($system['system']) {

			case 'CLIENT':

				// if no a favicon.ico file; give them one (Critically needed to prevent doc errors)

				if (!file_exists($htaccessFolder . 'favicon.ico')) {

					foreach ($icons as $icon)
						copy($BotBanishSettings['BOTBANISH_IMAGES_DIR'] . 'icons/' . $icon, $htaccessFolder . $icon);
				}
				break;
		}

		// if a robots.txt file does not exist; give them one (Critically needed to prevent doc errors)

		if (!file_exists($htaccessFolder . 'robots.txt'))
			BotBanishWriteFile($htaccessFolder . 'robots.txt', 'User-agent: *' . PHP_EOL . 'Crawl-delay: 10' . PHP_EOL . 'Allow: /' . PHP_EOL);
	}

	// Add our code to the .htaccess files to handle error processing etc.

	BotBanishHTACCESSModify('add');
}

function BotBanishGetHTACCESSLocations() {

	global $BotBanishSettings, $boarddir;

	$htaccess_array = array();

	// If we are protecting the root and the application folder, we need to modify the htaccess file in both locations
	// Do we need to protect the root folder? Some installations may not be in the root and may not want root protection.
	// Root protection is needed to help protect from Denial of Service attacks
	//
	// Note: If the instalation is in the root location, the protect root option should not be allowed or shown.

	$protectRoot = isset($BotBanishSettings['BOTBANISH_PROTECT_ROOT_ACTIVE']) ? boolval($BotBanishSettings['BOTBANISH_PROTECT_ROOT_ACTIVE']) : false;

	$system = BotBanishCheckSystem();

	switch ($system['system']) {

		case 'SMF':

			$htaccess_array[] = $boarddir . '/';
			break;

		case 'WORDPRESS':

			$htaccess_array[] = ABSPATH;
			break;

		case 'WEBSITE':

			$protectRoot = false;	// Never protect the root folder for the installation is most likely is in the root
			break;

		case 'SERVER':

			$protectRoot = false;	// Never protect the root folder on server for multiple versions exist
			break;
	}

	$htaccess_array[] = $BotBanishSettings['BOTBANISH_DIR'];

	// If the root is already in the array, then the installation is in the root
	// and we should not Attempt to protect the root because it is already protected.
	// We should no show it as an option in the settings either.

	$root = rtrim(realpath($_SERVER['DOCUMENT_ROOT']), '/')  . '/';

	if (in_array($root, $htaccess_array)) {

		$protectRoot = false;
		$BotBanishSettings['BOTBANISH_PROTECT_ROOT_ACTIVE'] = false;
		$BotBanishSettings['BOTBANISH_PROTECT_ROOT_ACTIVE_NOSHOW'] = true;
	}

	if ($protectRoot)
		$htaccess_array[] = $root;

	// No duplicate folders!
	// Insure proper slash usage!!!

	foreach ($htaccess_array as $key => $value)
		$htaccess_array[$key] = str_ireplace('\\', '/', $value);

	$htaccess_array = array_unique($htaccess_array);

	return $htaccess_array;
}

function BotBanishProcessHTACCESS($dataArray) {

	global $BotBanishSettings;

	//================================================================
	// .htaccess rule modifications
	//================================================================

	$BotBanishSettings['BOTBANISH_PROTECT_ROOT_ACTIVE'] = isset($dataArray['BOTBANISH_PROTECT_ROOT_ACTIVE']) ? boolval($dataArray['BOTBANISH_PROTECT_ROOT_ACTIVE']) : false;
	$dataArray['BOTBANISH_HTACCESS_ARRAY'] = BotBanishGetHTACCESSLocations();

	BotBanishSettingsTablePutValues($dataArray);
	$BotBanishSettings = BotBanishSettingsTableGetValues();
	
	// Add the new values into the .htaccess file
	// 'add' will clean before adding
	
	BotBanishHTACCESSModify('add');
}

function BotBanishHTACCESSModify($func = 'add', $file = '') {

	if (empty($file)) {
		
		$htaccess = BotBanishGetHTACCESSLocations();

		foreach ($htaccess as $htaccess_folder)
			BotBanishHTACCESSMaintenance($htaccess_folder . '.htaccess', $func);

	} else {
		
		BotBanishHTACCESSMaintenance($file, $func);		
	}
}

function BotBanishHTACCESSMaintenance($htaccess_name, $func = 'clean') {

	global $BotBanishSettings;

	// Make sure comments in .htaccess file are not taken as commands.

	$BotBanishSettings['REMOVE_HTACCESS_COMMENTS'] = true;	// Gets unset when .htaccess file is rewritten

	if ($func == 'add')
		BotBanishMaintenanceNormal($htaccess_name, 'clean');

	BotBanishMaintenanceNormal($htaccess_name, $func);

}

function BotBanishMaintenanceNormal($htaccess_name, $func = 'clean') {

	global $BotBanishSettings;

	switch ($func) {

		case 'add':

			//=================================================================================================
			// Add the new rules in the .htaccess file
			//=================================================================================================

			$htaccess_data = BotBanishHTACCESSReadFile($htaccess_name);
			$htaccess_data_org = $htaccess_data;
			$htaccess_data = array();

			$url = $BotBanishSettings['BOTBANISH_INSTALL_URL'];

			// SQL Injection Rules

			$Warning = PHP_EOL . PHP_EOL . '# DO NOT REMOVE OR CHANGE!!! - MUST BE FIRST IN .htaccess FILE FOR PROTECTION!!!' . PHP_EOL . '# Everything between the BotBanish Start and End markers will be removed when the BotBanish is uninstalled';
/*
			$SQL_Injection_Entry = PHP_EOL . 'Options All -Indexes' . PHP_EOL . 'RewriteEngine On' . PHP_EOL . PHP_EOL .
			'RewriteCond %{QUERY_STRING} ^.*(loopback|127\.0\.0\.1).* [NC,OR]' . PHP_EOL .
			'RewriteCond %{QUERY_STRING} ^.*(md5|benchmark|union|insert|drop|alter).* [NC]' . PHP_EOL;

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

				$SQL_Injection_Entry .= 'RewriteRule ^(.*)$ ' . $url . 'Subs/BotBanish_Redirect.php?docerror=401 [L,QSA]' . PHP_EOL;
			else
				$SQL_Injection_Entry .= 'RewriteRule ^(.*)$ ' . $url . 'Subs/BotBanish_AccessError.php?docerror=401 [L,QSA]' . PHP_EOL;

			// PHP Injection Rules

			$PHP_Injection_Entry = PHP_EOL .
			'RewriteCond %{QUERY_STRING} ^.*(else|require|include|isset|foreach|global).* [NC]' . PHP_EOL;

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

				$PHP_Injection_Entry .= 'RewriteRule ^(.*)$ ' . $url . 'Subs/BotBanish_Redirect.php?docerror=400 [L,QSA]' . PHP_EOL;
			else
				$PHP_Injection_Entry .= 'RewriteRule ^(.*)$ ' . $url . 'Subs/BotBanish_AccessError.php?docerror=400 [L,QSA]' . PHP_EOL;

			// Error Document Rules
*/
			$errortypes = BotBanishFixExplode(',', $BotBanishSettings['BOTBANISH_DOC_ERRORS']);
			$ErrorDoc_Entry = PHP_EOL;

			$htaccess_folder = dirname($htaccess_name);
			$relloc = str_ireplace($htaccess_folder, '', $BotBanishSettings['BOTBANISH_SUBS_DIR']);

			foreach ($errortypes as $type) {

				if (defined('ABSPATH') && $type == 404) 	// Let WordPress handle file not found errors
					continue;

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

					$ErrorDoc_Entry .= 'ErrorDocument ' . $type . ' ' . $relloc .  'BotBanish_Redirect.php' . PHP_EOL;
				else
					$ErrorDoc_Entry .= 'ErrorDocument ' . $type . ' ' . $relloc .  'BotBanish_AccessError.php' . PHP_EOL;
			}

			// Place all entries in code blocks for easy extraction on uninstall

//			$htaccess_rule_entry = $Warning . PHP_EOL . $SQL_Injection_Entry . $PHP_Injection_Entry . $ErrorDoc_Entry . PHP_EOL;
			$htaccess_rule_entry = $Warning . PHP_EOL . $ErrorDoc_Entry . PHP_EOL;

			if ($BotBanishSettings['BOTBANISH_HTACCESS_ACTIVE'])
				$htaccess_data = BotBanishHTACCESSInsertRuleBlock($BotBanishSettings['BOTBANISH_HTACCESS_START'], $BotBanishSettings['BOTBANISH_HTACCESS_END'], $BotBanishSettings['BOTBANISH_HTACCESS_DATA'], $htaccess_data);

			if ($BotBanishSettings['BOTBANISH_XMLRPC_ACTIVE'] && defined('ABSPATH'))
				$htaccess_data = BotBanishHTACCESSInsertRuleBlock($BotBanishSettings['BOTBANISH_XMLRPC_START'], $BotBanishSettings['BOTBANISH_XMLRPC_END'], $BotBanishSettings['BOTBANISH_XMLRPC_DATA'], $htaccess_data);

			if ($BotBanishSettings['BOTBANISH_WPCONFIG_ACTIVE'] && defined('ABSPATH'))
				$htaccess_data = BotBanishHTACCESSInsertRuleBlock($BotBanishSettings['BOTBANISH_WPCONFIG_START'], $BotBanishSettings['BOTBANISH_WPCONFIG_END'], $BotBanishSettings['BOTBANISH_WPCONFIG_DATA'], $htaccess_data);

			$htaccess_data = BotBanishHTACCESSInsertRuleBlock($BotBanishSettings['BOTBANISH_RULE_START'], $BotBanishSettings['BOTBANISH_RULE_END'], $htaccess_rule_entry, $htaccess_data);	// Write our new firewall rule to stop SQL injections
//			$htaccess_data = BotBanishHTACCESSInsertRule(PHP_EOL . $BotBanishSettings['BOTBANISH_CODE'] . PHP_EOL, $htaccess_data);	// Write our new Apache version finder

			$rules = array('items' => BotBanishSpiderGetList('bbc_botbanishclient_spider_good'),
							'entry' => $BotBanishSettings['HTACCESS_ENTRY_BOT_WHITELIST_2_4'],
							'data' => $htaccess_data,
						);
						
			$htaccess_data = BotBanishHTACCESSAddRuleBlock($rules);

			$rules = array('items' => BotBanishSpiderGetList('bbc_botbanishclient_spider_bad'),
							'entry' => $BotBanishSettings['HTACCESS_ENTRY_BOT_BLACKLIST_2_4'],
							'data' => $htaccess_data,
						);

			$htaccess_data = BotBanishHTACCESSAddRuleBlock($rules);

			if (isset($BotBanishSettings['BOTBANISH_REDIRECT_ONLY_ACTIVE']) && !$BotBanishSettings['BOTBANISH_REDIRECT_ONLY_ACTIVE']) {
				
				$rules = array('items' => BotBanishIPGetList('bbc_botbanishclient_ip_bad'),
								'entry' => $BotBanishSettings['HTACCESS_ENTRY_IP_BLACKLIST_2_4'],
								'data' => $htaccess_data,
							);

				$htaccess_data = BotBanishHTACCESSAddRuleBlock($rules);
			}
			
			$rules = array('items' => BotBanishIPGetList('bbc_botbanishclient_ip_bad_default'),
							'entry' => $BotBanishSettings['HTACCESS_ENTRY_IP_BLACKLIST_2_4'],
							'data' => $htaccess_data,
						);

			$htaccess_data = BotBanishHTACCESSAddRuleBlock($rules);

			$htaccess_data = array_merge($htaccess_data, $htaccess_data_org,);

			break;

		case 'clean':
		default:

			if (!file_exists($htaccess_name))
				return;

			//=================================================================================================
			// Check to see if we have information in the .htaccess file, if so we will try to remove it
			//=================================================================================================

			$htaccess_data = BotBanishHTACCESSReadFile($htaccess_name);

			// Attempt the group entry

			$htaccess_data = BotBanishHTACCESSRemoveRuleBlock($BotBanishSettings['BOTBANISH_HTACCESS_START'], $BotBanishSettings['BOTBANISH_HTACCESS_END'], $htaccess_data);
			$htaccess_data = BotBanishHTACCESSRemoveRuleBlock($BotBanishSettings['BOTBANISH_XMLRPC_START'], $BotBanishSettings['BOTBANISH_XMLRPC_END'], $htaccess_data);
			$htaccess_data = BotBanishHTACCESSRemoveRuleBlock($BotBanishSettings['BOTBANISH_WPCONFIG_START'], $BotBanishSettings['BOTBANISH_WPCONFIG_END'], $htaccess_data);

			$htaccess_data = BotBanishHTACCESSRemoveRuleBlock($BotBanishSettings['BOTBANISH_RULE_START'], $BotBanishSettings['BOTBANISH_RULE_END'], $htaccess_data);
			$htaccess_data = BotBanishHTACCESSRemoveRuleBlock($BotBanishSettings['BOTBANISH_CODE_START'], $BotBanishSettings['BOTBANISH_CODE_END'], $htaccess_data);

			$htaccess_data = BotBanishHTACCESSRemoveRuleBlock($BotBanishSettings['BOTBANISH_TAG_IP_WHITELIST_START_2_4'], $BotBanishSettings['BOTBANISH_TAG_IP_WHITELIST_END_2_4'], $htaccess_data);

			$htaccess_data = BotBanishHTACCESSRemoveRuleBlock($BotBanishSettings['BOTBANISH_TAG_IP_BLACKLIST_START'], $BotBanishSettings['BOTBANISH_TAG_IP_BLACKLIST_END'], $htaccess_data);

			$htaccess_data = BotBanishHTACCESSRemoveRuleBlock($BotBanishSettings['BOTBANISH_TAG_BOT_REWRITE_WHITELIST_START'], $BotBanishSettings['BOTBANISH_TAG_BOT_REWRITE_WHITELIST_END'], $htaccess_data);
			$htaccess_data = BotBanishHTACCESSRemoveRuleBlock($BotBanishSettings['BOTBANISH_TAG_BOT_WHITELIST_START_2_4'], $BotBanishSettings['BOTBANISH_TAG_BOT_WHITELIST_END_2_4'], $htaccess_data);

			$htaccess_data = BotBanishHTACCESSRemoveRuleBlock($BotBanishSettings['BOTBANISH_TAG_BOT_REWRITE_BLACKLIST_START'], $BotBanishSettings['BOTBANISH_TAG_BOT_REWRITE_BLACKLIST_END'], $htaccess_data);
			$htaccess_data = BotBanishHTACCESSRemoveRuleBlock($BotBanishSettings['BOTBANISH_TAG_BOT_BLACKLIST_START'], $BotBanishSettings['BOTBANISH_TAG_BOT_BLACKLIST_END'], $htaccess_data);

			$htaccess_data = BotBanishHTACCESSRemoveRuleBlock($BotBanishSettings['BOTBANISH_TAG_IP_BLACKLIST_START_2_4'], $BotBanishSettings['BOTBANISH_TAG_IP_BLACKLIST_END_2_4'], $htaccess_data);
			$htaccess_data = BotBanishHTACCESSRemoveRuleBlock($BotBanishSettings['BOTBANISH_TAG_BOT_BLACKLIST_START_2_4'], $BotBanishSettings['BOTBANISH_TAG_BOT_BLACKLIST_END_2_4'], $htaccess_data);

			break;
	}

	BotBanishHTACCESSWriteFile($htaccess_data, array(), $htaccess_name);
	return;
	
//	return $htaccess_data;
}

function BotBanishHTACCESSRemoveComments($htaccess_data) {

	global $BotBanishSettings;

	if (empty($htaccess_data)) return $htaccess_data;

	if (!isset($BotBanishSettings['REMOVE_HTACCESS_COMMENTS'])
			|| $BotBanishSettings['REMOVE_HTACCESS_COMMENTS'] == false
				|| !isset($GLOBALS['replaceArray'])
					|| empty($GLOBALS['replaceArray']))
		return $htaccess_data;

	unset($BotBanishSettings['REMOVE_HTACCESS_COMMENTS']);
	$GLOBALS['replaceArray'] = array();

	$lines = explode(PHP_EOL, $htaccess_data);
	$i = 0;

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

		$line = trim($value);

		// Do not remove BotBanish Comments. We will need them to identify instructions to add/remove

		if (stripos($line, '#') === 0 && stripos($line, 'BotBanish') === false) {

			$item = 'BotBanish_' . $i;
			$GLOBALS['replaceArray'][] = array('find' => $item, 'repl' => $value);
			$lines[$key] = $item;
			$i++;
		}
	}

	$htaccess_data = implode(PHP_EOL, $lines);
	return $htaccess_data;
}

function BotBanishHTACCESSInsertRule($entry, $htaccess_data, $type = 'bot') {

//	if (empty($htaccess_data)) return $htaccess_data;

	if (!BotBanishHTACCESSCheckActive($type))
		return;

	$lines = array(PHP_EOL, $entry, PHP_EOL);
	$htaccess_data = array_merge($lines, $htaccess_data);
	return $htaccess_data;
}

function BotBanishHTACCESSInsertRuleBlock($start_entry, $end_entry, $entry, $htaccess_data, $type = '') {

//	if (empty($htaccess_data)) return $htaccess_data;

	if (!BotBanishHTACCESSCheckActive($type))
		return;

	$lines = explode(PHP_EOL, PHP_EOL . $start_entry . PHP_EOL . $entry . PHP_EOL . $end_entry . PHP_EOL);
	$htaccess_data = array_merge($lines, $htaccess_data);
	return $htaccess_data;
}

function BotBanishHTACCESSRemoveRule($entry, $htaccess_data, $type = '') {

	// if rule is in the file, we will remove it

	if (empty($htaccess_data)) return $htaccess_data;

	if (!BotBanishHTACCESSCheckActive($type))
		return;

	$key = array_search($entry, $htaccess_data);

	if (!$key) {

		// We have to convert the data to a string, remove the entry then convert it back to an array

		$data = implode(PHP_EOL, $htaccess_data);
		$data = str_ireplace($entry, '', $data);
		$htaccess_data = explode(PHP_EOL, $data);

	} else {

		if ($key !== false)
			unset($htaccess_data[$key]);
	}

	return $htaccess_data ;
}

function BotBanishHTACCESSRemoveRuleBlock($start_entry, $end_entry, $htaccess_data) {

	if (empty($htaccess_data)) return $htaccess_data;

	$entries = array($start_entry, $end_entry);
	$tags = array();

	foreach ($entries as $key => $value)
		$tags[$key] = array_keys($htaccess_data, $value, false);

	$num = count($tags[0]);
	
	if (count($tags[0]) != count($tags[1])) {

		BotBanishLogError('Mis-Matched Begin/End Markers Found - Begin:', $start_entry . ' - End: ' . $end_entry, 'Error');
		return;
	}

	$start = 0;
	$end = 0;

	for ($x = 0; $x < $num; $x++) {
		
		$start = $tags[0][$x];
		$end = $tags[1][$x];
		
		for ($i = $start; $i <= $end; $i++)
			unset ($htaccess_data[$i]);	
	}
/*	
	foreach ($index as $key => $value) {

		$end = empty($end) && !empty($start) ? $value : $end;
		$start = empty($start) ? $value : $start;

		if (!empty($end)) {

			for ($i = $start; $i <= $end; $i++)
				unset ($htaccess_data[$i]);

			$start = 0;
			$end = 0;
		}
	}
*/
	return $htaccess_data;
}

function BotBanishHTACCESSAddRuleBlock($rules = array()) {

	$type = $rules['entry']['type'];
	$items = $rules['items'];

	if (!BotBanishHTACCESSCheckActive($type))
		return;

	if (empty($rules['items']))
		return $rules['data'];

	if (!is_array($rules['items']))
		$items = array($rules['items']);

	global $BotBanishSettings;

	BotBanishHTACCESSChangeLockStatus($BotBanishSettings['BOTBANISH_HTACCESS_LOCK']);

	// Let's build our new data element block to insert

	$adddata = array();

	foreach ($items as $value) {

		if (empty($value))
			continue;
		
		// Attempt to get rid of strange entries.
		
		$str = preg_replace('/[A-Za-z0-9 !@#$%^&*().~_+-={};:<>?,|]/', '', $value);
		$str = str_ireplace(array('\\', '/'), '', $str);

		if (!empty($str))
			continue;
			
		if ($type == 'bot') {
	
			if (((strlen($value) < 4) || empty($value)))
				continue;
		}

		$adddata[] = $value;
	}

	// No new data to be added? We have nothing to do

	if (empty($adddata))
		return $rules['data'];

	$htaccess_entry = array();
	$htaccess_entry['info'] = $rules['entry'];

	$newlines = array();

	foreach ($adddata as $key => $value)
		$newlines[] = stripslashes(trim(sprintf($htaccess_entry['info']['str'], $value), PHP_EOL));

	// Eliminate duplicates

	$lines = BotBanish_Array_iunique($newlines);
	natcasesort($lines);

	// Place proper termination for bot block section

	if ($type == 'bot') {

		$lines = array_values($lines);
		$key = count($lines) - 1;
		$lines[$key] = str_ireplace('[OR,NC]', '[NC]', $lines[$key]);
	}

	$newdata =  trim(implode(PHP_EOL, $lines));

	$lines = BotBanishHTACCESSPlaceHeaderInfo($htaccess_entry, $newdata);

	BotBanishHTACCESSChangeLockStatus($BotBanishSettings['BOTBANISH_HTACCESS_UNLOCK']);
	return array_merge($rules['data'], $lines);
}

function BotBanishHTACCESSUpdate($row, $type) {

	global $BotBanishSettings;

	if (!BotBanishHTACCESSCheckActive($type))
		return;

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

	// ------------------------------------------------------------
	// .htaccess file updates. We still need to protect our server
	// ------------------------------------------------------------

	switch ($type) {

		case 'bot':

			// Never block full user agents for BOTs

			if (isset($row['user_agent']) && strlen($row['user_agent']) < 25) {

				// Write our new firewall rule

				if (!empty($row['user_agent']))
					BotBanishHTACCESSAddEntry($row['user_agent'], $type);
			}

			// If a spider, update the ip table also just in case User-Agent denial (does not work properly on the server).

			sleep(1); // Give the server time to update

			break;


		case 'ip':
		default:

			if (isset($row['bot_ip'])) {

				// Must be a valid IP regardless!
				if (!BotBanishCheckIfValidIP($row['bot_ip']))
					break;

				// We can only support IPv4 in Apache 2.2. If IPv6 then disregard it

				if (isset($row['bot_ip'])){

					// Write our new firewall rule

					if (!empty($row['bot_ip']))
						BotBanishHTACCESSAddEntry($row['bot_ip'], $type);	// Make sure we have the proper headers for our firewall rules
				}
			}

			break;
	}
}

function BotBanishHTACCESSAddEntry($items, $type) {

	if (!BotBanishHTACCESSCheckActive($type) || empty($typeArr) || empty($items))
		return;

	if (!is_array($items))
		$items = array($items);
	
	global $BotBanishSettings;

	BotBanishHTACCESSChangeLockStatus($BotBanishSettings['BOTBANISH_HTACCESS_LOCK']);

	// Lets add these items to our bad IP/Bot tables
		
	foreach ($items as $item) {
		
		switch (strtolower($type)) {
			
			case 'bot':
			
				$table = 'bbc_botbanishclient_spider_bad';
				$sql = 'INSERT INTO `' . $table . '` (`spider_name`, `user_agent_part`) VALUES ("' . $item . '", "' . $item . '")';
				break;
				
			case 'ip':

				$table = 'bbc_botbanishclient_ip_bad';
				$sql = 'INSERT INTO `' . $table . '` (`bot_ip`) VALUES ("' . $item . '")';
				break;
		}
		
		BotBanishExecuteSQL($sql, true, true);
	}

	BotBanishHTACCESSModify('add', $filename);
	BotBanishHTACCESSChangeLockStatus($BotBanishSettings['BOTBANISH_HTACCESS_UNLOCK']);
}

function BotBanishHTACCESSRemoveEntry($items, $type) {

	if (!BotBanishHTACCESSCheckActive($type) || empty($typeArr) || empty($items))
		return;

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

	global $BotBanishSettings;

	BotBanishHTACCESSChangeLockStatus($BotBanishSettings['BOTBANISH_HTACCESS_LOCK']);
	
	foreach ($items as $item) {
		
		switch (strtolower($type)) {
			
			case 'bot':
			
				$table = 'bbc_botbanishclient_spider_bad';
				$sql = 'DELETE FROM `' . $table . '` WHERE `user_agent_part` = "' . $item . '")';
				break;
				
			case 'ip':

				$table = 'bbc_botbanishclient_ip_bad';
				$sql = 'DELETE FROM `' . $table . '` WHERE `bot_ip` = "' . $item . '")';
				break;
		}
		
		BotBanishExecuteSQL($sql, true, true);
	}	

	BotBanishHTACCESSModify('add', $filename);	
	BotBanishHTACCESSChangeLockStatus($BotBanishSettings['BOTBANISH_HTACCESS_UNLOCK']);
}

function BotBanishHTACCESSReadFile($htaccess_name = '') {

	$lines = array();

	if (file_exists($htaccess_name))
		$lines = file($htaccess_name, FILE_IGNORE_NEW_LINES);

	return $lines;
}

function BotBanishHTACCESSWriteFile($htaccess_data, $lines, $htaccess_name = '') {

	if (empty($htaccess_name))
		return;

	// If the files folder does not already exist, do NOTHING!!!

	$dir = str_ireplace(basename($htaccess_name), '', $htaccess_name);

	if (!is_dir($dir))
		return;

	$lines = !is_array($lines) ? array($lines) : $lines;
	$htaccess_data = !is_array($htaccess_data) ? array($htaccess_data) : $htaccess_data;

	// Write the new file

	$htaccess_data = array_merge($lines , $htaccess_data);
	$data = implode(PHP_EOL, $htaccess_data);

	$file = fopen($htaccess_name, 'w');

	if (isset($file)) {

		$output = BotBanish_UTF8_Encode ($data);
		$output = BotBanishRemoveExtraCRLF($output);

		fwrite ($file, $output);
		fflush($file);
		fclose($file);
	}
}

function BotBanishHTACCESSGetListData (&$htaccess_data, $htaccess_entry) {

	global $BotBanishSettings;

	// If no data was found, we skip this, nothing to do

	if ($htaccess_entry[0] === false)
		return array();

	$headers = array('<RequireAll>', '</RequireAll>', 'Require all granted', '<Files *>', '</Files>',
						'<Limit GET POST>', 'order allow,deny', 'allow from all', '</Limit>', '<Limit GET POST HEAD>',
						'Deny from env=bad_bot',
						$BotBanishSettings['BOTBANISH_TAG_IP_BLACKLIST_START'], $BotBanishSettings['BOTBANISH_TAG_IP_BLACKLIST_END'], $BotBanishSettings['BOTBANISH_TAG_BOT_BLACKLIST_END'], $BotBanishSettings['BOTBANISH_TAG_IP_BLACKLIST_END'],
						$BotBanishSettings['BOTBANISH_CODE_START'], $BotBanishSettings['BOTBANISH_CODE_END'], $BotBanishSettings['BOTBANISH_RULE_START'], $BotBanishSettings['BOTBANISH_RULE_END'],
						$BotBanishSettings['BOTBANISH_TAG_BOT_REWRITE_BLACKLIST_START'], $BotBanishSettings['BOTBANISH_TAG_BOT_REWRITE_BLACKLIST_END'], trim(PHP_EOL, $BotBanishSettings['BOTBANISH_REWRITECOND_END']),
					);

	// The old style headers are still out there so we need to remove them also

	$old_headers = array();

	foreach ($headers as $header) {

		if (stripos($header, '* * * * *') !== false)
			$old_headers[] = str_ireplace('* * * * *', '*****', $header);
	}

	$headers = array_merge($headers, $old_headers);

	// Gather the information requested

	$elements = array();
	$index = $htaccess_entry[0];
	$lth = ($htaccess_entry[1] - $htaccess_entry[0]) + 1;
	$elements = array_slice($htaccess_data, $index, $lth);

	// Remove all header information from this selection

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

		if (($i = array_search($value, $elements)) !== false)
			unset($elements[$i]);
	}

	// Remove the selection information from the original array.

	$index = $htaccess_entry[0];

	while ($index <= $htaccess_entry[1])
		unset($htaccess_data[$index++]);

	// Remove empty lines from our selection

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

		if (empty($value))

			unset($elements[$key]);
		else

		// make sure all bot elements indicate not the last in the chain

		if ($htaccess_entry['info']['type'] == 'bot')
			$elements[$key] = str_ireplace('[NC]', '[OR,NC]', $elements[$key]);
	}

	return $elements;
}

function BotBanishHTACCESSGetHeaderType($htaccess_data, $type = 'ip') {

	if (!BotBanishHTACCESSCheckActive($type))
		return array();

	global $BotBanishSettings;

	$tags = array();

	switch ($type) {

		case 'bot':

			$entries = array($BotBanishSettings['BOTBANISH_TAG_BOT_REWRITE_BLACKLIST_START'], $BotBanishSettings['BOTBANISH_TAG_BOT_REWRITE_BLACKLIST_END']);

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

				$tags[$key] = array_search($value, $htaccess_data);
				$BotBanishSettings['BOTBANISH_HTACCESS_TAGS'][$key] = $tags[$key];
			}

			$tags['info'] = $BotBanishSettings['HTACCESS_ENTRY_BOT_BLACKLIST_2_4'];

			break;

		default:


			$entries = array($BotBanishSettings['BOTBANISH_TAG_IP_BLACKLIST_START_2_4'], $BotBanishSettings['BOTBANISH_TAG_IP_BLACKLIST_END_2_4']);

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

				$tags[$key] = array_search($value, $htaccess_data);
				$BotBanishSettings['BOTBANISH_HTACCESS_TAGS'][$key] = $tags[$key];
			}

			$tags['info'] = $BotBanishSettings['HTACCESS_ENTRY_IP_2_4'];
			break;
	}

	return $tags;
}

function BotBanishHTACCESSPlaceHeaderInfo($header_entry, $data) {

	if (empty($data))
		return array();

	$info = ($header_entry['info']['type'] == 'ip') ? PHP_EOL . $header_entry['info']['start'] : PHP_EOL;
	$data = $header_entry['info']['tag_start'] . $info . trim($data) . PHP_EOL . $header_entry['info']['end'] . PHP_EOL . $header_entry['info']['tag_end'];
	$lines = explode(PHP_EOL, $data);
	return $lines;
}

function BotBanishHTACCESSCombineBlockedIP() {

	if (!BotBanishHTACCESSCheckActive('ip'))
		return;

	global $BotBanishSettings;

	$ip_list = array();

	// Get IP's from the database
	$table = ($BotBanishSettings['BOTBANISH_SERVER'] == 1) ? 'bbs_botbanishserver_ip_bad' : 'bbc_botbanishclient_ip_bad';
	$ip_list = BotBanishIPGetList($table);

	// Let's build our new data element block to insert

	$htaccess_array = $BotBanishSettings['BOTBANISH_HTACCESS_ARRAY'];

	foreach ($htaccess_array as $htaccess_folder) {

		// Get IP's from the htaccess file

		$filename = $htaccess_folder . '.htaccess';
		$htaccess_data = BotBanishHTACCESSReadFile($filename);
		$htaccess_entry = BotBanishHTACCESSGetHeaderType($htaccess_data, 'ip');
		$oldlines = BotBanishHTACCESSGetListData($htaccess_data, $htaccess_entry);

		// Combine both IP lists

		$lines = array_merge($oldlines, $ip_list);
		$lines = array_unique($lines);
		natcasesort($lines);

		BotBanishHTACCESSAddEntry($lines, 'ip');
	}
}

function BotBanishHTACCESSGetCacheTable() {

	global $BotBanishSettings;

	$table = ($BotBanishSettings['BOTBANISH_SERVER'] == 1) ? 'bbs_botbanishserver_htaccess' : 'bbc_botbanishclient_htaccess';
	return $table;
}

function BotBanishAddScheduledTasks() {

	$table = BotBanishHTACCESSGetCacheTable();
	BotBanishDeleteScheduledTasks();

	$row = array();
	$row['type'] = 'cache';
	$row['text'] = 'BotBanish_Cache_Flush';

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

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

function BotBanishCheckScheduledTasks() {

	global $BotBanishSettings;

	if (!isset($BotBanishSettings['BOTBANISH_HTACCESS_CACHING']) ||  (!$BotBanishSettings['BOTBANISH_HTACCESS_CACHING']))
		return false;

	$table = BotBanishHTACCESSGetCacheTable();
	$date = BotBanishGetCorrectTime();

	$sql = 'SELECT `updated` from `' . $table . '` WHERE `type` = "cache" LIMIT 1';
	$row = BotBanishExecuteSQL($sql);

	if (empty($row) || !is_array($row)) {

		BotBanishAddScheduledTasks();

	} else {

		if (isset($row['updated']) && (strtotime($date) >= (strtotime($row['updated']) + $BotBanishSettings['BOTBANISH_HTACCESS_FLUSH_TIME'] * 60))) {

			BotBanishAddScheduledTasks();
			return true;

		} else {

			$sql = 'SELECT COUNT(`updated`) AS items FROM ' . $table;
			$row = BotBanishExecuteSQL($sql);

			if (empty($row) || !is_array($row))	return false;

			if (isset($row[0]['items']) && $row[0]['items'] >= $BotBanishSettings['BOTBANISH_HTACCESS_FLUSH_COUNT']) {

				BotBanishAddScheduledTasks();
				return true;
			}
		}
	}
}

function BotBanishDeleteScheduledTasks() {

	$table = BotBanishHTACCESSGetCacheTable();

	$sql = 'DELETE FROM `' . $table . '` WHERE `type` = "cache"';
	BotBanishExecuteSQL($sql);
}

function BotBanishHTACCESSCheckLockStatus() {

	global $BotBanishSettings, $BotBanishSession;

	$table = BotBanishHTACCESSGetCacheTable();

	$sql = 'SELECT `id`, `text` FROM `' . $table . '` WHERE `type` = "' . $BotBanishSettings['BOTBANISH_HTACCESS_LOCK'] . '"';
	$row = BotBanishExecuteSQL($sql);

	if (!is_array($row) || empty($row)) {

		$BotBanishSession->setSessionKey('htaccesscache', 0);

	} else {

		$status = (isset($row['text']) && $row['text'] == $BotBanishSettings['BOTBANISH_HTACCESS_ACTIVE']) ? $row['id'] : 0;
		$BotBanishSession->getSessionKey('htaccesscache', $status);
	}

	return $BotBanishSession->getSessionKey('htaccesscache');
}

function BotBanishHTACCESSSetLockStatus() {
/*
	global $BotBanishSettings, $BotBanishSession;

	$table = BotBanishHTACCESSGetCacheTable();

	$sql = 'INSERT INTO `' . $table . '` (`type`, `text`) VALUES ("' . $BotBanishSettings['BOTBANISH_HTACCESS_LOCK'] . '", "' . $BotBanishSettings['BOTBANISH_HTACCESS_ACTIVE'] . '")';
	BotBanishExecuteSQL($sql);
	$BotBanishSession->setSessionKey('id', mysql_insert_id());
*/
}

function BotBanishHTACCESSChangeLockStatus($action = '') {
/*
	global $BotBanishSettings;

	if (empty($action))
		$action = $BotBanishSettings['BOTBANISH_HTACCESS_UNLOCK'];

	$table = BotBanishHTACCESSGetCacheTable();

	$state = ($action == $BotBanishSettings['BOTBANISH_HTACCESS_LOCK']) ? $BotBanishSettings['BOTBANISH_HTACCESS_ACTIVE'] : 0;
	$sql = 'UPDATE `' . $table . '` SET `text` = "' . $state . '", `updated` = NOW() WHERE `id` = "' . $BotBanishSession->getSessionKey('botbanishcache') . '"';
	BotBanishExecuteSQL($sql);
*/
}

function BotBanishDatabaseHTACCESSDeleteEntry($items, $type, $htaccess_entry) {
/*
	$table = BotBanishHTACCESSGetCacheTable();

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

		$entry = stripslashes(sprintf($htaccess_entry['str'], $value));
		$sql = 'DELETE FROM `' . $table . '` WHERE `data` = "' . $entry . '"';
		BotBanishExecuteSQL($sql);
	}
*/
}

function BotBanishDatabaseHTACCESSAddEntry($items, $type = '') {
/*
	if (!BotBanishHTACCESSCheckActive($type))
		return;

	global $BotBanishSettings;

	$table = BotBanishHTACCESSGetCacheTable();

	foreach ($items as $item) {

		$row = array();

		$row['type'] = $type;
		$row['text'] = $item;

		$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);
	}

	$sql = 'SELECT `id`, `text`, `updated` FROM `' . $table . '` WHERE `type` = "' . $type . '"';
	$rows = BotBanishExecuteSQL($sql);

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

		if (count($rows) >= $BotBanishSettings['BOTBANISH_HTACCESS_FLUSH_COUNT'])
			BotBanishDatabaseHTACCESSFlushCache();
	}

	BotBanishCheckScheduledTasks();
*/
}

function BotBanishDatabaseHTACCESSRemoveEntry($ids) {
/*
	$table = BotBanishHTACCESSGetCacheTable();

	if (empty($ids))
		return;

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

	$data = trim(implode(',', $ids), ',');
	$table = BotBanishHTACCESSGetCacheTable();

	$sql = 'DELETE FROM `' . $table . '` WHERE `id` IN (' . $data . ')';
	BotBanishExecuteSQL($sql);
*/
}

function BotBanishDatabaseHTACCESSFlushCache() {
/*
	$table = BotBanishHTACCESSGetCacheTable();

*/
}

function BotBanishHTACCESSCheckActive($type) {

	// If we are doing special HTACCESS overriding all is ok

//	if (isset($GLOBALS['HTACCESS_OVERRIDE']) && $GLOBALS['HTACCESS_OVERRIDE'])
//		return true;

	global $BotBanishSettings;

	// No .htaccess usage on Nginx, Lighttpd or LiteSpeed servers

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

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

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

	switch ($type) {

		case 'ip':

			$flag = (!isset($BotBanishSettings['BOTBANISH_HTACCESS_RECORD_IP']) || !$BotBanishSettings['BOTBANISH_HTACCESS_RECORD_IP']) ? false : true;
			break;

		case 'bot':

			$flag = (!isset($BotBanishSettings['BOTBANISH_HTACCESS_RECORD_BOT']) || !$BotBanishSettings['BOTBANISH_HTACCESS_RECORD_BOT']) ? false : true;
			break;

		default:

			$flag = true;
			break;
	}

	return $flag;
}

function BotBanishGetInfo($typeArr) {

	$type = isset($typeArr[0]) ? $typeArr[0] : 'ip';
	$rule = isset($typeArr[1]) ? $typeArr[1] : 'blacklist';
	$ruleType = isset($typeArr[2]) ? $typeArr[2] : 'all';

	return array('type' => $type, 'rule' => $rule, 'ruleType' => $ruleType);
}
?>