Private
Server IP : 195.201.23.43  /  Your IP : 18.116.74.246
Web Server : Apache
System : Linux webserver2.vercom.be 5.4.0-192-generic #212-Ubuntu SMP Fri Jul 5 09:47:39 UTC 2024 x86_64
User : kdecoratie ( 1041)
PHP Version : 7.1.33-63+ubuntu20.04.1+deb.sury.org+1
Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : ON
Directory :  /home/kdecoratie/public_html/plugins/system/admintools/util/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ HOME SHELL ]     

Current File : /home/kdecoratie/public_html/plugins/system/admintools/util/exceptionshandler.php
<?php
/**
 * @package   admintools
 * @copyright Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
 * @license   GNU General Public License version 3, or later
 */

defined('_JEXEC') or die;

use Akeeba\AdminTools\Admin\Helper\Storage;
use FOF30\Container\Container;
use FOF30\Date\Date;
use FOF30\Utils\TimezoneWrangler;

class AtsystemUtilExceptionshandler
{
	/** @var   JRegistry  Plugin parameters */
	protected $params = null;

	/** @var   Storage  Component parameters */
	protected $cparams = null;

	/** @var   Container  The component's container */
	protected $container;

	public function __construct(JRegistry &$params, Storage &$cparams)
	{
		$this->params    = $params;
		$this->cparams   = $cparams;
		$this->container = Container::getInstance('com_admintools');
	}

	/**
	 * Logs security exceptions and processes the IP auto-ban for this IP
	 *
	 * @param string $reason                   Block reason code
	 * @param string $extraLogInformation      Extra information to be written to the text log file
	 * @param string $extraLogTableInformation Extra information to be written to the extradata field of the log table (useful for JSON format)
	 *
	 * @return bool
	 */
	public function logAndAutoban($reason, $extraLogInformation = '', $extraLogTableInformation = '')
	{
		$ret = $this->logBreaches($reason, $extraLogInformation, $extraLogTableInformation);

		$autoban = $this->cparams->getValue('tsrenable', 0);

		if ($autoban)
		{
			$this->autoBan($reason);
		}

		return $ret;
	}

	/**
	 * Blocks the request in progress and, optionally, logs the details of the
	 * blocked request for the admin to review later
	 *
	 * @param string $reason                   Block reason code
	 * @param string $message                  The message to be shown to the user
	 * @param string $extraLogInformation      Extra information to be written to the text log file
	 * @param string $extraLogTableInformation Extra information to be written to the extradata field of the log table (useful for JSON format)
	 *
	 * @throws Exception
	 */
	public function blockRequest($reason = 'other', $message = '', $extraLogInformation = '', $extraLogTableInformation = '')
	{
		// Rescue URL check
		AtsystemUtilRescueurl::processRescueURL($this);

		if (empty($message))
		{
			$customMessage = $this->cparams->getValue('custom403msg', '');

			if (!empty($customMessage))
			{
				$message = $customMessage;
			}
			else
			{
				$message = 'ADMINTOOLS_BLOCKED_MESSAGE';
			}
		}

		$r = $this->logBreaches($reason, $extraLogInformation, $extraLogTableInformation);

		if (!$r)
		{
			return;
		}

		$autoban = $this->cparams->getValue('tsrenable', 0);

		if ($autoban)
		{
			$this->autoBan($reason);
		}

		// Merge the default translation with the current translation
		$jlang = JFactory::getLanguage();
		// Front-end translation
		$jlang->load('plg_system_admintools', JPATH_ADMINISTRATOR, 'en-GB', true);
		$jlang->load('plg_system_admintools', JPATH_ADMINISTRATOR, $jlang->getDefault(), true);
		$jlang->load('plg_system_admintools', JPATH_ADMINISTRATOR, null, true);

		if ((JText::_('ADMINTOOLS_BLOCKED_MESSAGE') == 'ADMINTOOLS_BLOCKED_MESSAGE') && ($message == 'ADMINTOOLS_BLOCKED_MESSAGE'))
		{
			$message = "Access Denied";
		}
		else
		{
			$message = JText::_($message);
		}

		$message = AtsystemUtilRescueurl::processBlockMessage($message);

		// Show the 403 message
		$use403View = $this->cparams->getValue('use403view', 0);
		$isFrontend = $this->container->platform->isFrontend();

		if ($use403View && $isFrontend)
		{
			// Using a view
			if (!$this->container->platform->getSessionVar('block', false, 'com_admintools'))
			{

				// This is inside an if-block so that we don't end up in an infinite redirection loop
				$this->container->platform->setSessionVar('block', true, 'com_admintools');
				$this->container->platform->setSessionVar('message', $message, 'com_admintools');

				if (!$this->container->platform->isCli())
				{
					JFactory::getSession()->close();
				}

				$this->container->platform->redirect(JUri::base());
			}
		}
		else
		{
			// Using Joomla!'s error page
			JFactory::getApplication()->input->set('template', null);

			throw new Exception($message, 403);
		}
	}

	/**
	 * Logs security exceptions
	 *
	 * @param string $reason                   Block reason code
	 * @param string $extraLogInformation      Extra information to be written to the text log file
	 * @param string $extraLogTableInformation Extra information to be written to the extradata field of the log table (useful for JSON format)
	 *
	 * @return bool
	 */
	public function logBreaches($reason, $extraLogInformation = '', $extraLogTableInformation = '')
	{
		$ip = $this->getVisitorIPAddress();

		// No point continuing if I cannot get the visitor's IP address
		if ($ip === false)
		{
			return false;
		}

		// Make sure this IP is not in the "Do not block these IPs" list
		if ($this->isSafeIP())
		{
			return false;
		}

		// Make sure this IP is not in the administrator white list
		if ($this->isIPInAdminWhitelist())
		{
			return false;
		}

		// Make sure this IP doesn't resolve to a whitelisted domain
		if ($this->isWhitelistedDomain($ip))
		{
			return true;
		}

		// Is this a private network IP and IP workaround is off? If so let's raise the flag so we can notify the user
		$this->flagPrivateNetworkIPs();

		// Get the human readable blocking reason
		$txtReason = $this->getBlockingReasonHumanReadable($reason, $extraLogTableInformation);

		// Get the email tokens, also used for logging
		$tokens = $this->getEmailVariables($txtReason);

		// Log the security exception to file and the database, if necessary
		$this->logSecurityException($reason, $extraLogInformation, $extraLogTableInformation, $txtReason, $tokens);

		// Email the security exception, if necessary
		$this->emailSecurityException($reason, $tokens);

		return true;
	}

	/**
	 * Checks if an IP address should be automatically banned for raising too many security exceptions over a predefined
	 * time period.
	 *
	 * @param   string $reason The reason of the ban
	 *
	 * @return  void
	 */
	public function autoBan($reason = 'other')
	{
		// We need to be able to get our own IP, right?
		if (!function_exists('inet_pton'))
		{
			return;
		}

		// Get the IP
		$ip = AtsystemUtilFilter::getIp();

		// No point continuing if we can't get an address, right?
		if (empty($ip) || ($ip == '0.0.0.0'))
		{
			return;
		}

		// Check for repeat offenses
		$db = $this->container->db;
		$strikes = $this->cparams->getValue('tsrstrikes', 3);
		$numfreq = $this->cparams->getValue('tsrnumfreq', 1);
		$frequency = $this->cparams->getValue('tsrfrequency', 'hour');
		$mindatestamp = 0;

		switch ($frequency)
		{
			case 'second':
				break;

			case 'minute':
				$numfreq *= 60;
				break;

			case 'hour':
				$numfreq *= 3600;
				break;

			case 'day':
				$numfreq *= 86400;
				break;

			case 'ever':
				$mindatestamp = 946706400; // January 1st, 2000
				break;
		}

		JLoader::import('joomla.utilities.date');
		$jNow = new Date();

		if ($mindatestamp == 0)
		{
			$mindatestamp = $jNow->toUnix() - $numfreq;
		}

		$jMinDate = new Date($mindatestamp);
		$minDate = $jMinDate->toSql();

		$sql = $db->getQuery(true)
			->select('COUNT(*)')
			->from($db->qn('#__admintools_log'))
			->where($db->qn('logdate') . ' >= ' . $db->q($minDate))
			->where($db->qn('ip') . ' = ' . $db->q($ip));
		$db->setQuery($sql);
		try
		{
			$numOffenses = $db->loadResult();
		}
		catch (Exception $e)
		{
			$numOffenses = 0;
		}

		if ($numOffenses < $strikes)
		{
			return;
		}

		// Block the IP
		$myIP = @inet_pton($ip);

		if ($myIP === false)
		{
			return;
		}

		$myIP = inet_ntop($myIP);

		$until     = $jNow->toUnix();
		$numfreq   = $this->cparams->getValue('tsrbannum', 1);
		$frequency = $this->cparams->getValue('tsrbanfrequency', 'hour');

		switch ($frequency)
		{
			case 'second':
				$until += $numfreq;
				break;

			case 'minute':
				$numfreq *= 60;
				$until += $numfreq;
				break;

			case 'hour':
				$numfreq *= 3600;
				$until += $numfreq;
				break;

			case 'day':
				$numfreq *= 86400;
				$until += $numfreq;
				break;

			case 'ever':
				$until = 2145938400; // January 1st, 2038 (mind you, UNIX epoch runs out on January 19, 2038!)
				break;
		}

		JLoader::import('joomla.utilities.date');

		$jMinDate = new Date($until);
		$minDate = $jMinDate->toSql();

		$record = (object)array(
			'ip'     => $myIP,
			'reason' => $reason,
			'until'  => $minDate
		);

		// If I'm here it means that we have to ban the user. Let's see if this is a simple autoban or
		// we have to issue a permaban as a result of several attacks
		if ($this->cparams->getValue('permaban', 0))
		{
			// Ok I have to check the number of autoban
			$query = $db->getQuery(true)
				->select('COUNT(*)')
				->from($db->qn('#__admintools_ipautobanhistory'))
				->where($db->qn('ip') . ' = ' . $db->q($myIP));

			try
			{
				$bans = $db->setQuery($query)->loadResult();
			}
			catch (Exception $e)
			{
				$bans = 0;
			}

			$limit = (int)$this->cparams->getValue('permabannum', 0);

			if ($limit && ($bans >= $limit))
			{
				$block = (object)array(
					'ip'          => $myIP,
					'description' => 'IP automatically blocked after being banned automatically ' . $bans . ' times'
				);

				try
				{
					$db->insertObject('#__admintools_ipblock', $block);
				}
				catch (Exception $e)
				{
					// This should never happen, however let's prevent a white page if anything goes wrong
				}
			}
		}

		try
		{
			$db->insertObject('#__admintools_ipautoban', $record);
		}
		catch (Exception $e)
		{
			// If the IP was already blocked and I have to block it again, I'll have to update the current record
			$db->updateObject('#__admintools_ipautoban', $record, 'ip');
		}

		// Send an optional email
		if ($this->cparams->getValue('emailafteripautoban', ''))
		{
			// Load the component's administrator translation files
			$jlang = JFactory::getLanguage();
			$jlang->load('com_admintools', JPATH_ADMINISTRATOR, 'en-GB', true);
			$jlang->load('com_admintools', JPATH_ADMINISTRATOR, $jlang->getDefault(), true);
			$jlang->load('com_admintools', JPATH_ADMINISTRATOR, null, true);

			// Get the site name
			$config = $this->container->platform->getConfig();

			$substitutions = $this->getEmailVariables(JText::_('COM_ADMINTOOLS_WAFEMAILTEMPLATE_REASON_IPAUTOBAN'), [
				'[UNTIL]'     => $minDate
			]);

			// Load the component's administrator translation files
			$jlang = JFactory::getLanguage();
			$jlang->load('com_admintools', JPATH_ADMINISTRATOR, 'en-GB', true);
			$jlang->load('com_admintools', JPATH_ADMINISTRATOR, $jlang->getDefault(), true);
			$jlang->load('com_admintools', JPATH_ADMINISTRATOR, null, true);

			// Let's get the most suitable email template
			$template = $this->getEmailTemplate('ipautoban', true);

			// Got no template, the user didn't published any email template, or the template doesn't want us to
			// send a notification email. Anyway, let's stop here.
			if (!$template)
			{
				return;
			}
			else
			{
				$subject = $template[0];
				$body    = $template[1];
			}

			foreach ($substitutions as $k => $v)
			{
				$subject = str_replace($k, $v, $subject);
				$body = str_replace($k, $v, $body);
			}

			// Send the email
			try
			{
				$mailer = JFactory::getMailer();

				$mailfrom = $config->get('mailfrom');
				$fromname = $config->get('fromname');

				$recipients = explode(',', $this->cparams->getValue('emailafteripautoban', ''));
				$recipients = array_map('trim', $recipients);

				foreach ($recipients as $recipient)
				{
					if (empty($recipient))
					{
						continue;
					}

					// This line is required because SpamAssassin is BROKEN
					$mailer->Priority = 3;

					$mailer->isHtml(true);
					$mailer->setSender(array($mailfrom, $fromname));

					// Resets the recipients, otherwise they will pile up
					$mailer->clearAllRecipients();

					if ($mailer->addRecipient($recipient) === false)
					{
						// Failed to add a recipient?
						continue;
					}

					$mailer->setSubject($subject);
					$mailer->setBody(AtsystemUtilRescueurl::processBlockMessage($body, $recipient));
					$mailer->Send();
				}
			}
			catch (\Exception $e)
			{
				// Joomla! 3.5 and later throw an exception when crap happens instead of suppressing it and returning false
			}
		}
	}

	/**
	 * Gets the email template for a specific security exception reason
	 *
	 * @param   string  $reason  The security exception reason for which to fetch the email template
	 * @param   bool    $exact   Require an exact match of the reason
	 *
	 * @return  array
	 */
	public function getEmailTemplate($reason, $exact = false)
	{
		// Let's get the subject and the body from email templates
		$jlang = JFactory::getLanguage();
		$db = $this->container->db;
		$languages = array($db->q('*'), $db->q('en-GB'), $db->q($jlang->getDefault()));
		$stack = array();

		$query = $db->getQuery(true)
			->select('*')
			->from($db->qn('#__admintools_waftemplates'))
			->where($db->qn('reason') . ' IN(' . $db->q($reason) . ', ' . $db->q('all') . ')')
			->where($db->qn('language') . ' IN(' . implode(',', $languages) . ')')
			->where($db->qn('enabled') . ' = ' . $db->q('1'));

		if ($exact)
		{
			$query->where($db->qn('reason') . ' = ' . $db->q($reason));
		}

		try
		{
			$templates = $db->setQuery($query)->loadObjectList();
		}
		catch (Exception $e)
		{
			return array();
		}

		foreach ($templates as $template)
		{
			$score = 0;

			if ($template->reason == $reason)
			{
				$score += 10;
			}

			if ($template->language == $jlang->getDefault())
			{
				$score += 10;
			}
			elseif ($template->language == '*')
			{
				$score += 5;
			}
			elseif ($template->language == 'en-GB')
			{
				$score += 1;
			}

			$stack[$score] = $template;
		}

		ksort($stack);
		$best = array_pop($stack);

		if (!$best)
		{
			return array();
		}

		if ($this->cparams->getValue('email_throttle', 1))
		{
			// Ok I found out the best template, HOWEVER, should I really send out an email? Let's do some checks vs frequency limits
			$emails       = $best->email_num ? $best->email_num : 5;
			$numfreq      = $best->email_numfreq ? $best->email_numfreq : 1;
			$frequency    = $best->email_freq ? $best->email_freq : 'hour';
			$mindatestamp = 0;

			switch ($frequency)
			{
				case 'second':
					break;

				case 'minute':
					$numfreq *= 60;
					break;

				case 'hour':
					$numfreq *= 3600;
					break;

				case 'day':
					$numfreq *= 86400;
					break;

				case 'ever':
					$mindatestamp = 946706400; // January 1st, 2000
					break;
			}

			JLoader::import('joomla.utilities.date');
			$jNow = new Date();

			if ($mindatestamp == 0)
			{
				$mindatestamp = $jNow->toUnix() - $numfreq;
			}

			$jMinDate = new Date($mindatestamp);
			$minDate = $jMinDate->toSql();

			$sql = $db->getQuery(true)
				->select('COUNT(*)')
				->from($db->qn('#__admintools_log'))
				->where($db->qn('logdate') . ' >= ' . $db->q($minDate))
				->where($db->qn('reason') . ' = ' . $db->q($reason));
			$db->setQuery($sql);
			try
			{
				$numOffenses = $db->loadResult();
			}
			catch (Exception $e)
			{
				$numOffenses = 0;
			}

			if ($numOffenses > $emails)
			{
				return array();
			}
		}

		// Because SpamAssassin blacklists our domain when it misidentifies an email as spam.
		$replaceThat = array(
			'<p style=\"text-align: right; font-size: 7pt; color: #ccc;\">Powered by <a style=\"color: #ccf; text-decoration: none;\" href=\"https://www.akeebabackup.com/products/admin-tools.html\">Akeeba AdminTools</a></p>',
			'<p style=\"text-align: right; font-size: 7pt; color: #ccc;\">Powered by <a style=\"color: #ccf; text-decoration: none;\" href=\"https://www.akeebabackup.com/products/admin-tools.html\">Akeeba AdminTools</a></p>',
			'https://www.akeebabackup.com',
			'http://www.akeebabackup.com',
			'http://akeebabackup.com',
			'https://akeebabackup.com',
			'www.akeebabackup.com',
			'akeebabackup.com',
		);

		foreach ($replaceThat as $find)
		{
			$best->subject  = str_ireplace($find, '', $best->subject);
			$best->template = str_ireplace($find, '', $best->template);
		}

		// Because SpamAssassin demands there is a body and surrounding html tag even though it's not necessary.
		if (strpos($best->template, '<body') == false)
		{
			$best->template = '<body>' . $best->template . '</body>';
		}

		if (strpos($best->template, '<html') == false)
		{
			$best->template = <<< HTML
<html>
<head>
<title>{$best->subject}</title>
</head>
$best->template
</html>
HTML;

		}

		// Inject self-unblocking information to the default emails for security exceptions and IP autoban
		if ($best->reason == 'all')
		{
			$best->template = str_replace('Reason: [REASON]</p>', 'Reason: [REASON]</p><p>[RESCUEINFO]</p>', $best->template);
		}
		elseif ($best->reason == 'ipautoban')
		{
			$best->template = str_replace('Banned until: [UNTIL]</p>', 'Banned until: [UNTIL]</p><p>[RESCUEINFO]</p>', $best->template);
		}

		// And now return the template
		return array(
			$best->subject,
			$best->template
		);
	}

	/**
	 * Flag security exceptions coming from private network IPs so we can notify the user
	 *
	 * @return  void
	 *
	 * @since   4.1.1
	 */
	private function flagPrivateNetworkIPs()
	{
		// Make sure FOF 3 can be loaded, or fail gracefuly
		if (!defined('FOF30_INCLUDED') && !@include_once(JPATH_LIBRARIES . '/fof30/include.php'))
		{
			return;
		}

		// I'll use the Container so I can easily set the flag and then save back to the database
		$params = $this->container->params;

		// Run the check only if IP workarounds are off AND the flag is set to 0 (ie not detected)
		// There's no need to run this check if the user decided to ignore the warning (value: -1) or we already detected something (value: 1)
		if (($this->cparams->getValue('ipworkarounds', -1) == -1) || ($params->get('detected_exceptions_from_private_network', 0) != 0))
		{
			return;
		}

		$privateNetwork = array(
			'10.0.0.0-10.255.255.255',
			'172.16.0.0-172.31.255.255',
			'192.168.0.0-192.168.255.255'
		);

		if (!AtsystemUtilFilter::IPinList($privateNetwork))
		{
			return;
		}

		// This IP belongs to a private network, let's raise the flag and then notify the user
		$params->set('detected_exceptions_from_private_network', 1);

		try
		{
			$params->save();
		}
		catch (Exception $e)
		{
			// Ignore any failures, they are not show stoppers
		}
	}

	public function getComponentParam($key, $default = null)
	{
		return $this->cparams->getValue($key, $default);
	}

	/**
	 * Get the variables we can use in emails as an associative list (variable => value).
	 *
	 * @param   string  $reason           The value for the [REASON] variable
	 * @param   array   $customVariables  An array of custom variables to add to the return.
	 *
	 * @return  array
	 */
	public function getEmailVariables($reason, $customVariables = array())
	{
		// Get our IP address
		$ip = AtsystemUtilFilter::getIp();

		if ((strpos($ip, '::') === 0) && (strstr($ip, '.') !== false))
		{
			$ip = substr($ip, strrpos($ip, ':') + 1);
		}

		// Get the site name
		$config = $this->container->platform->getConfig();

		$siteName = $config->get('sitename');

		// Create a link to lookup the IP
		$ipLookupURL = $this->cparams->getValue('iplookupscheme', 'http') . '://' . $this->cparams->getValue('iplookup', 'ip-lookup.net/index.php?ip={ip}');
		$ipLookupURL = str_replace('{ip}', $ip, $ipLookupURL);

		$uri = JUri::getInstance();
		$url = $uri->toString(['scheme', 'user', 'pass', 'host', 'port', 'path', 'query', 'fragment']);

		$user = $this->container->platform->getUser();

		if ($user->guest)
		{
			$username = 'Guest';
		}
		else
		{
			$username = $user->username . ' (' . $user->name . ' <' . $user->email . '>)';
		}

		$country   = '';
		$continent = '';

		if (class_exists('AkeebaGeoipProvider'))
		{
			$geoip     = new AkeebaGeoipProvider();
			$country   = $geoip->getCountryCode($ip);
			$continent = $geoip->getContinent($ip);
		}

		if (empty($country))
		{
			$country = '(unknown country)';
		}

		if (empty($continent))
		{
			$continent = '(unknown continent)';
		}

		$tzWrangler = new TimezoneWrangler($this->container);
		$email_timezone = $this->container->params->get('email_timezone', 'AKEEBA/DEFAULT');

		if (!empty($email_timezone) && ($email_timezone != 'AKEEBA/DEFAULT'))
		{
			try
			{
				$tzWrangler->setForcedTimezone($email_timezone);
			}
			catch (Exception $e)
			{
				// Just in case someone puts an invalid timezone in there (you can never be too paranoid).
			}
		}

		$noUser     = new JUser();

		$ret = array(
			'[SITENAME]'  => $siteName,
			'[REASON]'    => $reason,
			'[DATE]'      => $tzWrangler->getLocalTimeStamp('Y-m-d H:i:s T', $noUser),
			'[URL]'       => $url,
			'[USER]'      => $username,
			'[IP]'        => $ip,
			'[LOOKUP]'    => '<a href="' . $ipLookupURL . '">IP Lookup</a>',
			'[COUNTRY]'   => $country,
			'[CONTINENT]' => $continent,
			'[UA]'        => $_SERVER['HTTP_USER_AGENT'],
		);

		if (is_array($customVariables) && !empty($customVariables))
		{
			$ret = array_merge($ret, $customVariables);
		}

		return $ret;
	}

	/**
	 * Get the visitor IP address. Return false if we cannot get an IP address or if we get 0.0.0.0 (broken IP forwarding).
	 *
	 * @return  bool|string
	 */
	private function getVisitorIPAddress()
	{
		// Get our IP address
		$ip = AtsystemUtilFilter::getIp();

		if ((strpos($ip, '::') === 0) && (strstr($ip, '.') !== false))
		{
			$ip = substr($ip, strrpos($ip, ':') + 1);
		}

		// No point continuing if we can't get an address, right?
		if (empty($ip) || ($ip == '0.0.0.0'))
		{
			return false;
		}

		return $ip;
	}

	/**
	 * Is the IP address in the "Never block these IPs" (safe IPs) list?
	 *
	 * @return  bool
	 */
	private function isSafeIP()
	{
		$safeIPs = $this->cparams->getValue('neverblockips', '');

		if (!empty($safeIPs))
		{
			$safeIPs = explode(',', $safeIPs);

			if (!empty($safeIPs))
			{
				if (AtsystemUtilFilter::IPinList($safeIPs))
				{
					return true;
				}
			}
		}

		return false;
	}

	/**
	 * Is the IP address in the Administrator IP Whitelist?
	 *
	 * @return  bool
	 */
	private function isIPInAdminWhitelist()
	{
		if ($this->cparams->getValue('ipwl', 0) == 1)
		{
			$db  = $this->container->db;
			$sql = $db->getQuery(true)
			          ->select($db->qn('ip'))
			          ->from($db->qn('#__admintools_adminiplist'))
			;

			$db->setQuery($sql);

			try
			{
				$ipTable = $db->loadColumn();
			}
			catch (Exception $e)
			{
				$ipTable = null;
			}

			if (!empty($ipTable))
			{
				if (AtsystemUtilFilter::IPinList($ipTable))
				{
					return true;
				}
			}
		}

		return false;
	}

	/**
	 * Does the IP address resolve to one of the whitelisted domain names?
	 *
	 * @param   string  $ip
	 *
	 * @return  bool
	 */
	private function isWhitelistedDomain($ip)
	{
		static $whitelist_domains = null;

		if (is_null($whitelist_domains))
		{
			$whitelist_domains = $this->cparams->getValue('whitelist_domains', '.googlebot.com,.search.msn.com');
			$whitelist_domains = explode(',', $whitelist_domains);
		}

		if (!empty($whitelist_domains))
		{
			$remote_domain = @gethostbyaddr($ip);

			if (!empty($remote_domain))
			{
				foreach ($whitelist_domains as $domain)
				{
					$domain = trim($domain);

					if (strrpos($remote_domain, $domain) !== false)
					{
						return true;
					}
				}
			}
		}

		return false;
	}

	/**
	 * Get the blocking reason in a human readable format
	 *
	 * @param   string  $reason
	 * @param   string  $extraLogTableInformation
	 *
	 * @return  string
	 */
	private function getBlockingReasonHumanReadable($reason, $extraLogTableInformation)
	{
		// Load the component's administrator translation files
		$jlang = JFactory::getLanguage();
		$jlang->load('com_admintools', JPATH_ADMINISTRATOR, 'en-GB', true);
		$jlang->load('com_admintools', JPATH_ADMINISTRATOR, $jlang->getDefault(), true);
		$jlang->load('com_admintools', JPATH_ADMINISTRATOR, null, true);

		// Get the reason in human readable format
		$txtReason = JText::_('COM_ADMINTOOLS_LBL_SECURITYEXCEPTION_REASON_' . strtoupper($reason));

		if (empty($extraLogTableInformation))
		{
			return $txtReason;
		}

		// Get extra information
		list($logReason,) = explode('|', $extraLogTableInformation);

		return $txtReason . " ($logReason)";
	}

	/**
	 * Write a security exception to the log, as long as logging is enabled and the $reason is not one of the $reasons_nolog ones
	 *
	 * @param   string  $reason
	 * @param   string  $extraLogInformation
	 * @param   string  $extraLogTableInformation
	 * @param   string  $txtReason
	 * @param   array   $tokens
	 *
	 * @return  void
	 */
	private function logSecurityException($reason, $extraLogInformation, $extraLogTableInformation, $txtReason, $tokens)
	{
		$reasons_nolog = $this->cparams->getValue('reasons_nolog', 'geoblocking');
		$reasons_nolog = explode(',', $reasons_nolog);

		if (!$this->cparams->getValue('logbreaches', 0) || in_array($reason, $reasons_nolog))
		{
			return;
		}

		// Log to file
		$this->logSecurityExceptionToFile($reason, $extraLogInformation, $txtReason, $tokens);

		// Log to the database table
		$this->logSecurityExceptionToDatabase($reason, $extraLogTableInformation, $tokens);
	}

	/**
	 * Log a security exception to our log file
	 *
	 * @param   string  $reason
	 * @param   string  $extraLogInformation
	 * @param   string  $txtReason
	 * @param   array   $tokens
	 */
	private function logSecurityExceptionToFile($reason, $extraLogInformation, $txtReason, $tokens)
	{
		// Write to the log file only if we're told to
		if (!$this->cparams->getValue('logfile', 0))
		{
			return;
		}

		// Get the log filename
		$config = $this->container->platform->getConfig();
		$logpath = $config->get('log_path');
		$fname = $logpath . DIRECTORY_SEPARATOR . 'admintools_breaches.php';

		// -- Check the file size. If it's over 1Mb, archive and start a new log.
		if (@file_exists($fname))
		{
			$fsize = filesize($fname);

			if ($fsize > 1048756)
			{
				$altFile = substr($fname, 0, -4) . '.1.php';

				if (@file_exists($altFile))
				{
					unlink($altFile);
				}

				@copy($fname, $altFile);
				@unlink($fname);
			}
		}

		// If the main log file does not exist yet create a new one.
		if (!file_exists($fname))
		{
			$content = <<< END
php
/**
 * =====================================================================================================================
 * Admin Tools debug log file
 * =====================================================================================================================
 *
 * This file contains a dump of the requests which were blocked by Admin Tools. By definition, this file does contain
 * a lot of "hacking signatures" since this is what the Admin Tools component is designed to stop and this is the file
 * logging all these hacking attempts.
 *
 * You can disable the creation of this file by going to Components, Admin Tools, Web Application Firewall, Configure
 * WAF and setting the "Keep a debug log file" option to NO. This is the recommended setting. You should only set this
 * option to YES if you are troubleshooting an issue (Admin Tools is blocking access to your site).
 *
 * Some hosts will mistakenly report this file as suspicious or hacked. As a result they might issue an automated
 * warning and / or block access to your site. Should that happen please ask your host to look in this file and read
 * this header. This file is SAFE since the only executable statement is die() below which prevents the file from being
 * executed at all. If your host does not understand that this file is safe or does not know how to add an exception in
 * their automated scanner to exempt Joomla's log files (all files under this directory) from being flagged as hacked /
 * suspicious we strongly recommend going to a different host that understands how PHP works. It will be safer for you
 * as well. 
 */
 
die();
END;
			$content = "?$content?";
			$content .= ">\n\n";
			file_put_contents($fname, '<' . $content);
		}

		// -- Log the exception
		$fp = @fopen($fname, 'at');

		if ($fp === false)
		{
			return;
		}

		fwrite($fp, str_repeat('-', 79) . "\n");
		fwrite($fp, "Blocking reason: " . $reason . "\n" . str_repeat('-', 79) . "\n");
		fwrite($fp, "Reason     : " . $txtReason . "\n");
		fwrite($fp, 'Timestamp  : ' . gmdate('Y-m-d H:i:s') . " GMT\n");
		fwrite($fp, 'Local time : ' . $tokens['[DATE]'] . " \n");
		fwrite($fp, 'URL        : ' . $tokens['[URL]'] . "\n");
		fwrite($fp, 'User       : ' . $tokens['[USER]'] . "\n");
		fwrite($fp, 'IP         : ' . $tokens['[IP]'] . "\n");
		fwrite($fp, 'Country    : ' . $tokens['[COUNTRY]'] . "\n");
		fwrite($fp, 'Continent  : ' . $tokens['[CONTINENT]'] . "\n");
		fwrite($fp, 'UA         : ' . $tokens['[UA]'] . "\n");

		if (!empty($extraLogInformation))
		{
			fwrite($fp, $extraLogInformation . "\n");
		}

		fwrite($fp, "\n\n");
		fclose($fp);
	}

	/**
	 * Log a security exception to the database table
	 *
	 * @param   string  $reason
	 * @param   string  $extraLogInformation
	 * @param   array   $tokens
	 *
	 *
	 * @since version
	 */
	private function logSecurityExceptionToDatabase($reason, $extraLogTableInformation, $tokens)
	{
		try
		{
			$date     = new Date();
			$db       = $this->container->db;
			$url      = $tokens['[URL]'];

			if (strlen($url) > 10240)
			{
				$url = substr($url, 0, 10240);
			}

			$logEntry = (object) array(
				'logdate'   => $date->toSql(),
				'ip'        => $tokens['[IP]'],
				'url'       => $url,
				'reason'    => $reason,
				'extradata' => $extraLogTableInformation,
			);

			$db->insertObject('#__admintools_log', $logEntry);
		}
		catch (Exception $e)
		{
			// Do nothing if the query fails
		}
	}

	/**
	 * Sends information about the security exception by email
	 *
	 * @param   string  $reason
	 * @param   array   $tokens
	 *
	 * @return  bool
	 */
	private function emailSecurityException($reason, $tokens)
	{
		$emailbreaches   = $this->cparams->getValue('emailbreaches', '');
		$reasons_noemail = $this->cparams->getValue('reasons_noemail', 'geoblocking');
		$reasons_noemail = explode(',', $reasons_noemail);

		if (empty($emailbreaches) || in_array($reason, $reasons_noemail))
		{
			return true;
		}

		// Get the site name
		$config = $this->container->platform->getConfig();

		// Send the email
		try
		{
			$mailer = JFactory::getMailer();

			$mailfrom = $config->get('mailfrom');
			$fromname = $config->get('fromname');

			// Let's get the most suitable email template
			$template = $this->getEmailTemplate($reason);

			// Got no template, the user didn't published any email template, or the template doesn't want us to
			// send a notification email. Anyway, let's stop here
			if (!$template)
			{
				return true;
			}
			else
			{
				$subject = $template[0];
				$body    = $template[1];
			}

			$subject = str_replace(array_keys($tokens), array_values($tokens), $subject);
			$body    = str_replace(array_keys($tokens), array_values($tokens), $body);

			$recipients = explode(',', $emailbreaches);
			$recipients = array_map('trim', $recipients);

			foreach ($recipients as $recipient)
			{
				if (empty($recipient))
				{
					continue;
				}

				// This line is required because SpamAssassin is BROKEN
				$mailer->Priority = 3;

				$mailer->isHtml(true);
				$mailer->setSender(array($mailfrom, $fromname));

				// Resets the recipients, otherwise they will pile up
				$mailer->clearAllRecipients();

				if ($mailer->addRecipient($recipient) === false)
				{
					// Failed to add a recipient?
					continue;
				}

				$mailer->setSubject($subject);
				$mailer->setBody(AtsystemUtilRescueurl::processBlockMessage($body, $recipient));
				$mailer->Send();
			}
		}
		catch (\Exception $e)
		{
		}

		return true;
	}
}
Private