<?php

///////////////////////////////////////////////////////////////////////////////////////////////////////
// BotBanish 7.0.10
// Randem Systems: https://randemsystems.support
// BotBanish Black and White List Management For SMF
// Date: 01/03/2026
// Usage: 
//
// Function: Shows Black / White Lists
//
//	This file contains the functions for displaying and searching in the
//	spider list.  It does so with these functions:
//
//	void BlackWhiteList()
//		- shows a list of Black / White listed spiders.
//		- if a subaction is not specified, lists all spiders.
//		- allows searching for spiders with the 'search' sub action.
//		- calls MLAll or MLSearch depending on the sub action.
//		- uses the BlackWhitelist template with the main sub template.
//		- requires the view_blackwhitelist permission.
//		- is accessed via ?action=blackwhitelist.
//
//	void BWLAll()
//		- used to display all spiders on a page by page basis with sorting.
//		- called from BlackWhiteList().
//		- can be passed a sort parameter, to order the display of spiders.
//
//	void BWLSearch()
//		- used to search for spiders or display search results.
//		- called by BlackWhiteList().
//		- if variable 'search' is empty displays search dialog box, using the
//		  search sub template.
//
///////////////////////////////////////////////////////////////////////////////////////////////////////

// Show a listing of the Black / White listed spiders.

function BlackWhiteList()
{
	global $scripturl, $BotBanishText, $modSettings, $context, $settings, $modSettings, $user_info;

	// Make sure they can view the list.

	if (!$user_info['is_admin'])
		return;

	loadTemplate('BotBanishBlackWhiteSpiderList');

	$context['listing_by'] = !empty($_GET['sa']) ? $_GET['sa'] : 'all';

	// $subActions array format:
	// 'subaction' => array('label', 'function', 'is_selected')
	$subActions = array(
		'all' => array($BotBanishText['view_all_BlackWhiteLists'], 'BWLAll', $context['listing_by'] == 'all'),
		'search' => array($BotBanishText['blackwhitelist_search'], 'BWLSearch', $context['listing_by'] == 'search'),
	);

	// Set up the sort links.
	$context['sort_links'] = array();
	foreach ($subActions as $act => $text)
		$context['sort_links'][] = array(
			'label' => $text[0],
			'action' => $act,
			'selected' => $text[2],
		);

	$context['num_BlackWhiteLists'] = $modSettings['totalBlackWhiteLists'];

	// Set up the columns...

	$context['columns'] = array(
		'spider_id' => array(
			'label' => $BotBanishText['spider_id'],
			'width' => '10',
			'class' => 'first_th',
		),
		'spider_name' => array(
			'label' => $BotBanishText['spider_name'],
			'width' => '60',
		),
		'user_agent' => array(
			'label' => $BotBanishText['user_agent_part'],
			'width' => '25'
		),
		'active' => array(
			'label' => $BotBanishText['active'],
			'width' => '10'
		),
	);

	$context['colspan'] = 0;

	// Aesthetic stuff.
	end($context['columns']);
	$context['columns'][key($context['columns'])]['class'] = 'last_th';

	$context['linktree'][] = array(
		'url' => $scripturl . '?action=botbanish',
		'name' => $BotBanishText['admin_blackwhitespiderlist']
	);

	// Jump to the sub action.
	if (isset($subActions[$context['listing_by']]))
		$subActions[$context['listing_by']][1]();
	else
		$subActions['all'][1]();
}

// List all BlackWhiteLists, page by page.
function BWLAll()
{
	global $txt, $scripturl, $user_info;
	global $modSettings, $context, $smcFunc;

	// The chunk size for the cached index.
	$cache_step_size = 500;

	// Only use caching if:
	// 1. there are at least 2k members,
	// 2. the default sorting method (spider_name) is being used,
	// 3. the page shown is high enough to make a DB filesort unprofitable.
	$use_cache = $modSettings['totalBlackWhiteLists'] > 2000 && (!isset($_REQUEST['sort']) || $_REQUEST['sort'] === 'spider_name') && isset($_REQUEST['start']) && $_REQUEST['start'] > $cache_step_size;

	if ($use_cache)
	{
		// Maybe there's something cached already.
		if (!empty($modSettings['blackwhitelist_cache']))
//			$blackwhitelist_cache = safe_unserialize($modSettings['blackwhitelist_cache']);
			$blackwhitelist_cache = BotBanishSafeUnserialize($modSettings['blackwhitelist_cache']);

		// The chunk size for the cached index.
		$cache_step_size = 500;

		// Only update the cache if something changed or no cache existed yet.
		if (empty($blackwhitelist_cache) || empty($modSettings['blackwhitelist_updated']) || $blackwhitelist_cache['last_update'] < $modSettings['blackwhitelist_updated'])
		{
			$request = $smcFunc['db_query']('', '
				SELECT spider_name
				FROM {raw:table}
				WHERE active = {int:is_activated}
				ORDER BY spider_name',
				array(
					'is_activated' => 1,
					'table' => $context['BotBanishBlackWhiteListTable'],
				)
			);

			$blackwhitelist_cache = array(
				'last_update' => time(),
				'num_BlackWhiteLists' => $smcFunc['db_num_rows']($request),
				'index' => array(),
			);

			for ($i = 0, $n = $smcFunc['db_num_rows']($request); $i < $n; $i += $cache_step_size)
			{
				$smcFunc['db_data_seek']($request, $i);
				list($blackwhitelist_cache['index'][$i]) = $smcFunc['db_fetch_row']($request);
			}
			$smcFunc['db_data_seek']($request, $blackwhitelist_cache['num_BlackWhiteLists'] - 1);
			list ($blackwhitelist_cache['index'][$i]) = $smcFunc['db_fetch_row']($request);
			$smcFunc['db_free_result']($request);

			// Now we've got the cache...store it.
			updateSettings(array('blackwhitelist_cache' => BotBanishSafeSerialize($blackwhitelist_cache)));
		}

		$context['num_BlackWhiteLists'] = $blackwhitelist_cache['num_BlackWhiteLists'];
	}

	// Without cache we need an extra query to get the amount of blackwhitelist.
	else
	{
		$request = $smcFunc['db_query']('', '
			SELECT COUNT(*)
			FROM {raw:table}
			WHERE active = {int:is_activated}',
			array(
				'is_activated' => 1,
				'table' => $context['BotBanishBlackWhiteListTable'],
			)
		);
		list ($context['num_BlackWhiteLists']) = $smcFunc['db_fetch_row']($request);
		$smcFunc['db_free_result']($request);
	}

	// Set defaults for sort (spider_name) and start. (0)
	if (!isset($_REQUEST['sort']) || !isset($context['columns'][$_REQUEST['sort']]))
		$_REQUEST['sort'] = 'spider_name';

	if (!is_numeric($_REQUEST['start']))
	{
		if (preg_match('~^[^\'\\\\/]~' . ($context['utf8'] ? 'u' : ''), $smcFunc['strtolower']($_REQUEST['start']), $match) === 0)
			fatal_error('Hacker?', false);

		$_REQUEST['start'] = $match[0];

		$request = $smcFunc['db_query']('substring', '
			SELECT COUNT(*)
			FROM {raw:table}
			WHERE LOWER(SUBSTRING(spider_name, 1, 1)) < {string:first_letter}
				AND active = {int:is_activated}',
			array(
				'is_activated' => 1,
				'first_letter' => $_REQUEST['start'],
				'table' => $context['BotBanishBlackWhiteListTable'],
			)
		);
		list ($_REQUEST['start']) = $smcFunc['db_fetch_row']($request);
		$smcFunc['db_free_result']($request);
	}

	$context['letter_links'] = '';
	for ($i = 97; $i < 123; $i++)
		$context['letter_links'] .= '<a href="' . $scripturl . '?action=blackwhitelist;sa=all;start=' . chr($i) . '#letter' . chr($i) . '">' . strtoupper(chr($i)) . '</a> ';

	// Sort out the column information.
	foreach ($context['columns'] as $col => $column_details)
	{
		$context['columns'][$col]['href'] = $scripturl . '?action=blackwhitelist;sort=' . $col . ';start=0';

		if ((!isset($_REQUEST['desc']) && $col == $_REQUEST['sort']) || ($col != $_REQUEST['sort'] && !empty($column_details['default_sort_rev'])))
			$context['columns'][$col]['href'] .= ';desc';

		$context['columns'][$col]['link'] = '<a href="' . $context['columns'][$col]['href'] . '" rel="nofollow">' . $context['columns'][$col]['label'] . '</a>';
		$context['columns'][$col]['selected'] = $_REQUEST['sort'] == $col;
	}

	$context['sort_by'] = $_REQUEST['sort'];
	$context['sort_direction'] = !isset($_REQUEST['desc']) ? 'up' : 'down';

	// Construct the page index.
	$context['page_index'] = constructPageIndex($scripturl . '?action=blackwhitelist;sort=' . $_REQUEST['sort'] . (isset($_REQUEST['desc']) ? ';desc' : ''), $_REQUEST['start'], $context['num_BlackWhiteLists'], $modSettings['defaultMaxMembers']);

	// Send the data to the template.
	$context['start'] = $_REQUEST['start'] + 1;
	$context['end'] = min($_REQUEST['start'] + $modSettings['defaultMaxMembers'], $context['num_BlackWhiteLists']);

	$context['can_moderate_forum'] = allowedTo('moderate_forum');
	$context['page_title'] = sprintf($BotBanishText['viewing_blackwhitelist'], $context['start'], $context['end']);
	$context['linktree'][] = array(
		'url' => $scripturl . '?action=blackwhitelist;sort=' . $_REQUEST['sort'] . ';start=' . $_REQUEST['start'],
		'name' => &$context['page_title'],
		'extra_after' => ' (' . sprintf($BotBanishText['of_total_BlackWhiteLists'], $context['num_BlackWhiteLists']) . ')'
	);

	// List out the different sorting methods...

	$sort_methods = array(
		'spider_id' => array(
			'down' => 'spider_id ASC',
			'up' => 'spider_id DESC'
		),
		'spider_name' => array(
			'down' => 'spider_name DESC',
			'up' => 'spider_name ASC'
		),
		'user_agent' => array(
			'down' => 'user_agent_part DESC',
			'up' => 'user_agent_part ASC'
		),
		'active' => array(
			'down' => 'active DESC',
			'up' => 'active ASC'
		),
	);

	$limit = $_REQUEST['start'];
	$query_parameters = array(
		'is_activated' => 1,
		'sort' => $sort_methods[$_REQUEST['sort']][$context['sort_direction']],
		'table' => $context['BotBanishBlackWhiteListTable'],
	);

	$where = '';

	// Select the BlackWhiteLists from the database.

	$request = $smcFunc['db_query']('', '
		SELECT id_spider, spider_name, user_agent_part, active
		FROM {raw:table}
		WHERE active = {int:is_activated}
		ORDER BY {raw:sort}
		LIMIT ' . $limit . ', ' . $modSettings['defaultMaxMembers'],
		$query_parameters
	);

	$smcFunc['db_free_result']($request);

	// Add anchors at the start of each letter.
	if ($_REQUEST['sort'] == 'spider_name')
	{
		$last_letter = '';
		foreach ($context['BlackWhiteLists'] as $i => $dummy)
		{
			$this_letter = $smcFunc['strtolower']($smcFunc['substr']($context['BlackWhiteLists'][$i]['name'], 0, 1));

			if ($this_letter != $last_letter && preg_match('~[a-z]~', $this_letter) === 1)
			{
				$context['BlackWhiteLists'][$i]['sort_letter'] = htmlspecialchars($this_letter);
				$last_letter = $this_letter;
			}
		}
	}
}

// Search for BlackWhiteLists...
function BWLSearch()
{
	global $txt, $scripturl, $context, $user_info, $modSettings, $smcFunc;

	$context['page_title'] = $BotBanishText['blackwhitelist_search'];
	$context['can_moderate_forum'] = allowedTo('moderate_forum');

	// They're searching..

	if (isset($_REQUEST['search']) && isset($_REQUEST['fields']))
	{
		$_POST['search'] = trim(isset($_GET['search']) ? $_GET['search'] : $_POST['search']);

		if (!get_magic_quotes_gpc())
		{
			// Escape things just in case...
			if (isset($_GET['fields']))
			{
				$_POST['fields'] = explode(',', addslashes($_GET['fields']));
			}
			else
			{
				$temp = implode(',', $_POST['fields']);
				$_POST['fields'] = explode(',', addslashes($temp));
			}
		}
		else
			$_POST['fields'] = isset($_GET['fields']) ? explode(',', $_GET['fields']) : $_POST['fields'];

		$context['old_search'] = $_REQUEST['search'];
		$context['old_search_value'] = urlencode($_REQUEST['search']);

		// No fields?  Use default...
		if (empty($_POST['fields']))
			$_POST['fields'] = array('spider_name');

		$query_parameters = array(
			'is_activated' => 1,
			'blank_string' => '',
			'search' => '%' . strtr($smcFunc['htmlspecialchars']($_POST['search'], ENT_QUOTES), array('_' => '\\_', '%' => '\\%', '*' => '%')) . '%',
			'table' => $context['BotBanishBlackWhiteListTable'],
		);

		$search_fields = array();

		// Search for a name?
		if (in_array('spider_name', $_POST['fields']))
		{
			$fields = array('spider_name');
			$search_fields[] = 'spider_name';
		}

		$condition = '';

		$search_fields[] = $_POST['fields'];

		// No search fields? That means you're trying to hack things
		if (empty($search_fields))
			fatal_lang_error('invalid_search_string', false);

		$query = $_POST['search'] == '' ? '= {string:blank_string}' : 'LIKE {string:search}';

		$request = $smcFunc['db_query']('', '
			SELECT COUNT(*)
			FROM {raw:table}
			WHERE (' . implode( ' ' . $query . ' OR ', $fields) . ' ' . $query . $condition . ')
				AND active = {int:is_activated}',
			$query_parameters
		);
		list ($numResults) = $smcFunc['db_fetch_row']($request);
		$smcFunc['db_free_result']($request);

		$context['page_index'] = constructPageIndex($scripturl . '?action=botbanish;area=blackwhitelist;sa=search;search=' . $_POST['search'] . ';fields=' . implode(',', $search_fields), $_REQUEST['start'], $numResults, $modSettings['defaultMaxMembers']);

		$request = $smcFunc['db_query']('', '
			SELECT id_spider. spider_name, user_agent_part, active
			FROM {raw:table}
			WHERE (' . implode( ' ' . $query . ' OR ', $fields) . ' ' . $query . $condition . ')
				AND is_active = {int:is_activated}
			LIMIT ' . $_REQUEST['start'] . ', ' . $modSettings['defaultMaxMembers'],
			$query_parameters
		);

		$smcFunc['db_free_result']($request);
	}
	else
	{
		// These are all the possible fields.
		$context['search_fields'] = array(
			'spider_name' => $BotBanishText['spider_name'],
			'user_agent_part' => $BotBanishText['user_agent_part'],
		);

		// What do we search for by default?
		$context['search_defaults'] = array('spider_name', 'user_agent_part');

		$context['sub_template'] = 'BotBanishBlackWhiteSpiderList';
		$context['old_search'] = isset($_GET['search']) ? $_GET['search'] : (isset($_POST['search']) ? htmlspecialchars($_POST['search']) : '');
	}

	$context['linktree'][] = array(
		'url' => $scripturl . '?action=botbanish;area=blackwhitelist;sa=search',
		'name' => &$context['page_title']
	);
}
?>