<?php
/**
 * @package   AdminTools
 * @copyright Copyright (c)2010-2015 Nicholas K. Dionysopoulos
 * @license   GNU General Public License version 3, or later
 * @version   $Id$
 */

// Protect from unauthorized access
defined('_JEXEC') or die;

JLoader::import('joomla.application.component.model');

class AdmintoolsModelHtmaker extends F0FModel
{
	var $defaultConfig = array(
		// == System configuration ==
		// Host name for HTTPS requests (without https://)
		'httpshost'      => '',
		// Host name for HTTP requests (without http://)
		'httphost'       => '',
		// Follow symlinks (may cause a blank page or 500 Internal Server Error)
		'symlinks'       => 0,
		// Base directory of your site (/ for domain's root)
		'rewritebase'    => '',

		// == Optimization and utility ==
		// Force index.php parsing before index.html
		'fileorder'      => 1,
		// Set default expiration time to 1 hour
		'exptime'        => 0,
		// Automatically compress static resources
		'autocompress'   => 0,
		// Redirect index.php to root
		'autoroot'       => 1,
		// Redirect www and non-www addresses
		'wwwredir'       => 0,
		// Redirect old to new domain
		'olddomain'      => '',
		// Force HTTPS for these URLs
		'httpsurls'      => array(),
		// HSTS Header (for HTTPS-only sites)
		'hstsheader'     => 0,
		// Forbid displaying in FRAME (for HTTPS-only sites)
		'noframe'        => 0,
		// Disable HTTP methods TRACE and TRACK (protect against XST)
		'notracetrack'   => 0,

		// == Basic security ==
		// Disable directory listings
		'nodirlists'     => 1,
		// Protect against common file injection attacks
		'fileinj'        => 1,
		// Disable PHP Easter Eggs
		'phpeaster'      => 1,
		// Block access from specific user agents
		'nohoggers'      => 0,
		// Block access to configuration.php-dist and htaccess.txt
		'leftovers'      => 1,
		// User agents to block (one per line)
		'hoggeragents'   => array(
			'WebBandit',
			'webbandit',
			'Acunetix',
			'binlar',
			'BlackWidow',
			'Bolt 0',
			'Bot mailto:craftbot@yahoo.com',
			'BOT for JCE',
			'casper',
			'checkprivacy',
			'ChinaClaw',
			'clshttp',
			'cmsworldmap',
			'comodo',
			'Custo',
			'Default Browser 0',
			'diavol',
			'DIIbot',
			'DISCo',
			'dotbot',
			'Download Demon',
			'eCatch',
			'EirGrabber',
			'EmailCollector',
			'EmailSiphon',
			'EmailWolf',
			'Express WebPictures',
			'extract',
			'ExtractorPro',
			'EyeNetIE',
			'feedfinder',
			'FHscan',
			'FlashGet',
			'flicky',
			'GetRight',
			'GetWeb!',
			'Go-Ahead-Got-It',
			'Go!Zilla',
			'grab',
			'GrabNet',
			'Grafula',
			'harvest',
			'HMView',
			'ia_archiver',
			'Image Stripper',
			'Image Sucker',
			'InterGET',
			'Internet Ninja',
			'InternetSeer.com',
			'jakarta',
			'Java',
			'JetCar',
			'JOC Web Spider',
			'kmccrew',
			'larbin',
			'LeechFTP',
			'libwww',
			'Mass Downloader',
			'Maxthon$',
			'microsoft.url',
			'MIDown tool',
			'miner',
			'Mister PiX',
			'NEWT',
			'MSFrontPage',
			'Navroad',
			'NearSite',
			'Net Vampire',
			'NetAnts',
			'NetSpider',
			'NetZIP',
			'nutch',
			'Octopus',
			'Offline Explorer',
			'Offline Navigator',
			'PageGrabber',
			'Papa Foto',
			'pavuk',
			'pcBrowser',
			'PeoplePal',
			'planetwork',
			'psbot',
			'purebot',
			'pycurl',
			'RealDownload',
			'ReGet',
			'Rippers 0',
			'SeaMonkey$',
			'sitecheck.internetseer.com',
			'SiteSnagger',
			'skygrid',
			'SmartDownload',
			'sucker',
			'SuperBot',
			'SuperHTTP',
			'Surfbot',
			'tAkeOut',
			'Teleport Pro',
			'Toata dragostea mea pentru diavola',
			'turnit',
			'vikspider',
			'VoidEYE',
			'Web Image Collector',
			'Web Sucker',
			'WebAuto',
			'WebCopier',
			'WebFetch',
			'WebGo IS',
			'WebLeacher',
			'WebReaper',
			'WebSauger',
			'Website eXtractor',
			'Website Quester',
			'WebStripper',
			'WebWhacker',
			'WebZIP',
			'Wget',
			'Widow',
			'WWW-Mechanize',
			'WWWOFFLE',
			'Xaldon WebSpider',
			'Yandex',
			'Zeus',
			'zmeu',
			'CazoodleBot',
			'discobot',
			'ecxi',
			'GT::WWW',
			'heritrix',
			'HTTP::Lite',
			'HTTrack',
			'ia_archiver',
			'id-search',
			'id-search.org',
			'IDBot',
			'Indy Library',
			'IRLbot',
			'ISC Systems iRc Search 2.1',
			'LinksManager.com_bot',
			'linkwalker',
			'lwp-trivial',
			'MFC_Tear_Sample',
			'Microsoft URL Control',
			'Missigua Locator',
			'panscient.com',
			'PECL::HTTP',
			'PHPCrawl',
			'PleaseCrawl',
			'SBIder',
			'Snoopy',
			'Steeler',
			'URI::Fetch',
			'urllib',
			'Web Sucker',
			'webalta',
			'WebCollage',
			'Wells Search II',
			'WEP Search',
			'zermelo',
			'ZyBorg',
			'Indy Library',
			'libwww-perl',
			'Go!Zilla',
			'TurnitinBot',
		),

		// == Server protection ==
		// -- Toggle protection
		// Back-end protection
		'backendprot'    => 1,
		// Back-end protection
		'frontendprot'   => 1,
		// -- Fine-tuning
		// Back-end directories where file type exceptions are allowed
		'bepexdirs'      => array('components', 'modules', 'templates', 'images', 'plugins'),
		// Back-end file types allowed in selected directories
		'bepextypes'     => array(
			'jpe', 'jpg', 'jpeg', 'jp2', 'jpe2', 'png', 'gif', 'bmp', 'css', 'js',
			'swf', 'html', 'mpg', 'mp3', 'mpeg', 'mp4', 'avi', 'wav', 'ogg', 'ogv',
			'xls', 'xlsx', 'doc', 'docx', 'ppt', 'pptx', 'zip', 'rar', 'pdf', 'xps',
			'txt', '7z', 'svg', 'odt', 'ods', 'odp', 'flv', 'mov', 'htm', 'ttf',
			'woff', 'eot',
			'JPG', 'JPEG', 'PNG', 'GIF', 'CSS', 'JS', 'TTF', 'WOFF', 'EOT'
		),
		// Front-end directories where file type exceptions are allowed
		'fepexdirs'      => array('components', 'modules', 'templates', 'images', 'plugins', 'media', 'libraries', 'media/jui/fonts'),
		// Front-end file types allowed in selected directories
		'fepextypes'     => array(
			'jpe', 'jpg', 'jpeg', 'jp2', 'jpe2', 'png', 'gif', 'bmp', 'css', 'js',
			'swf', 'html', 'mpg', 'mp3', 'mpeg', 'mp4', 'avi', 'wav', 'ogg', 'ogv',
			'xls', 'xlsx', 'doc', 'docx', 'ppt', 'pptx', 'zip', 'rar', 'pdf', 'xps',
			'txt', '7z', 'svg', 'odt', 'ods', 'odp', 'flv', 'mov', 'ico', 'htm',
			'ttf', 'woff', 'eot',
			'JPG', 'JPEG', 'PNG', 'GIF', 'CSS', 'JS', 'TTF', 'WOFF', 'EOT'
		),
		// -- Exceptions
		// Allow direct access to these files
		'exceptionfiles' => array(
			"administrator/components/com_akeeba/restore.php",
			"administrator/components/com_admintools/restore.php",
			"administrator/components/com_joomlaupdate/restore.php"
		),
		// Allow direct access, except .php files, to these directories
		'exceptiondirs'  => array(),
		// Allow direct access, including .php files, to these directories
		'fullaccessdirs' => array(
			"templates/your_template_name_here"
		),

		// == Custom .htaccess rules ==
		// At the top of the file
		'custhead'       => '',
		// At the bottom of the file
		'custfoot'       => ''
	);

	private $config = null;

	public function  __construct($config = array())
	{
		parent::__construct($config);

		$myURI = JURI::getInstance();
		$path = $myURI->getPath();
		$path_parts = explode('/', $path);
		$path_parts = array_slice($path_parts, 0, count($path_parts) - 2);
		$path = implode('/', $path_parts);
		$myURI->setPath($path);
		// Unset any query parameters
		$myURI->setQuery('');

		$host = $myURI->toString();
		$host = substr($host, strpos($host, '://') + 3);

		$path = trim($path, '/');

		if (!empty($path))
		{
			$this->defaultConfig['rewritebase'] = $path;
		}
		else
		{
			$this->defaultConfig['rewritebase'] = '/';
		}
		$this->defaultConfig['httphost'] = $host;
		$this->defaultConfig['httpshost'] = $host;
		$this->defaultConfig = (object)$this->defaultConfig;
	}

	public function loadConfiguration()
	{
		if (is_null($this->config))
		{

			if (interface_exists('JModel'))
			{
				$params = JModelLegacy::getInstance('Storage', 'AdmintoolsModel');
			}
			else
			{
				$params = JModel::getInstance('Storage', 'AdmintoolsModel');
			}
			$savedConfig = $params->getValue('htconfig', '');
			if (!empty($savedConfig))
			{
				if (function_exists('base64_encode') && function_exists('base64_encode'))
				{
					$savedConfig = base64_decode($savedConfig);
				}
				$savedConfig = json_decode($savedConfig, true);
			}
			else
			{
				$savedConfig = array();
			}

			$config = $this->defaultConfig;
			if (!empty($savedConfig))
			{
				foreach ($savedConfig as $key => $value)
				{
					$config->$key = $value;
				}
			}

			$this->config = $config;
		}

		return $this->config;
	}

	public function saveConfiguration($data, $isConfigInput = false)
	{
		if ($isConfigInput)
		{
			$config = $data;
		}
		else
		{
			$config = $this->defaultConfig;
			if (!empty($data))
			{
				$ovars = get_object_vars($config);
				$okeys = array_keys($ovars);
				foreach ($data as $key => $value)
				{
					if (in_array($key, $okeys))
					{
						// Clean up array types coming from textareas
						if (in_array($key, array(
							'hoggeragents', 'bepexdirs',
							'bepextypes', 'fepexdirs', 'fepextypes',
							'exceptionfiles', 'exceptionfolders', 'exceptiondirs', 'fullaccessdirs',
							'httpsurls'
						))
						)
						{
							if (empty($value))
							{
								$value = array();
							}
							else
							{
								$value = trim($value);
								$value = explode("\n", $value);
								if (!empty($value))
								{
									$ret = array();
									foreach ($value as $v)
									{
										$vv = trim($v);
										if (!empty($vv))
										{
											$ret[] = $vv;
										}
									}
									if (!empty($ret))
									{
										$value = $ret;
									}
									else
									{
										$value = array();
									}
								}
							}
						}
						$config->$key = $value;
					}
				}
			}
		}

		$this->config = $config;
		$config = json_encode($config);
		// This keeps JRegistry from hapilly corrupting our data :@
		if (function_exists('base64_encode') && function_exists('base64_encode'))
		{
			$config = base64_encode($config);
		}
		if (interface_exists('JModel'))
		{
			$params = JModelLegacy::getInstance('Storage', 'AdmintoolsModel');
		}
		else
		{
			$params = JModel::getInstance('Storage', 'AdmintoolsModel');
		}
		$params->setValue('htconfig', $config);
		$params->save();
	}

	public function makeHtaccess()
	{
		// Guess Apache features
		$apacheVersion = $this->apacheVersion();
		$serverCaps = (object)array(
			'customCodes' => version_compare($apacheVersion, '2.2', 'ge'), // Custom redirections, e.g. R=301
			'deflate'     => version_compare($apacheVersion, '2.0', 'ge') // mod_deflate support
		);
		$redirCode = $serverCaps->customCodes ? '[R=301,L]' : '[R,L]';

		JLoader::import('joomla.utilities.date');
		$date = new JDate();
		$d = $date->format('Y-m-d H:i:s', true);
		$version = ADMINTOOLS_VERSION;
		$htaccess = <<<END
### ===========================================================================
### Security Enhanced & Highly Optimized .htaccess File for Joomla!
### automatically generated by Admin Tools $version on $d GMT
### Auto-detected Apache version: $apacheVersion (best guess)
### ===========================================================================
###
### The contents of this file are based on the same author's work "Master
### .htaccess", published on http://snipt.net/nikosdion/the-master-htaccess
###
### Admin Tools is Free Software, distributed under the terms of the GNU
### General Public License version 3 or, at your option, any later version
### published by the Free Software Foundation.
###
### !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! IMPORTANT !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
### !!                                                                       !!
### !!  If you get an Internal Server Error 500 or a blank page when trying  !!
### !!  to access your site, remove this file and try tweaking its settings  !!
### !!  in the back-end of the Admin Tools component.                        !!
### !!                                                                       !!
### !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
###


END;

		$config = $this->loadConfiguration();
		$htaccess .= "##### RewriteEngine enabled - BEGIN\n";
		$htaccess .= "RewriteEngine On\n";
		$htaccess .= "##### RewriteEngine enabled - END\n\n";

		$rewritebase = $config->rewritebase;
		if (!empty($rewritebase))
		{
			$htaccess .= "##### RewriteBase set - BEGIN\n";
			$rewritebase = trim($rewritebase, '/');
			$htaccess .= "RewriteBase /$rewritebase\n";
			$htaccess .= "##### RewriteBase set - END\n\n";
		}

		if (!empty($config->custhead))
		{
			$htaccess .= "##### Custom Rules (Top of File) -- BEGIN\n";
			$htaccess .= $config->custhead . "\n";
			$htaccess .= "##### Custom Rules (Top of File) -- END\n\n";
		}

		if ($config->fileorder == 1)
		{
			$htaccess .= "##### File execution order -- BEGIN\n";
			$htaccess .= "DirectoryIndex index.php index.html\n";
			$htaccess .= "##### File execution order -- END\n\n";
		}

		if ($config->nodirlists == 1)
		{
			$htaccess .= "##### No directory listings -- BEGIN\n";
			$htaccess .= "IndexIgnore *\n";

			switch ($config->symlinks)
			{
				case 0:
					$htaccess .= "Options -Indexes\n";
					break;

				case 1:
					$htaccess .= "Options -Indexes +FollowSymLinks\n";
					break;

				case 2:
					$htaccess .= "Options -Indexes +SymLinksIfOwnerMatch\n";
					break;
			}
			$htaccess .= "##### No directory listings -- END\n\n";
		}
		elseif ($config->symlinks != 0)
		{
			$htaccess .= "##### Follow symlinks -- BEGIN\n";

			switch ($config->symlinks)
			{
				case 1:
					$htaccess .= "Options +FollowSymLinks\n";
					break;

				case 2:
					$htaccess .= "Options +SymLinksIfOwnerMatch\n";
					break;
			}

			$htaccess .= "##### Follow symlinks -- END\n\n";
		}

		if ($config->exptime == 1)
		{
			$htaccess .= <<<END
##### Optimal default expiration time - BEGIN
<IfModule mod_expires.c>
	# Enable expiration control
	ExpiresActive On

	# Default expiration: 1 hour after request
	ExpiresDefault "now plus 1 hour"

	# CSS and JS expiration: 1 week after request
	ExpiresByType text/css "now plus 1 week"
	ExpiresByType application/javascript "now plus 1 week"
	ExpiresByType application/x-javascript "now plus 1 week"

	# Image files expiration: 1 month after request
	ExpiresByType image/bmp "now plus 1 month"
	ExpiresByType image/gif "now plus 1 month"
	ExpiresByType image/jpeg "now plus 1 month"
	ExpiresByType image/jp2 "now plus 1 month"
	ExpiresByType image/pipeg "now plus 1 month"
	ExpiresByType image/png "now plus 1 month"
	ExpiresByType image/svg+xml "now plus 1 month"
	ExpiresByType image/tiff "now plus 1 month"
	ExpiresByType image/vnd.microsoft.icon "now plus 1 month"
	ExpiresByType image/x-icon "now plus 1 month"
	ExpiresByType image/ico "now plus 1 month"
	ExpiresByType image/icon "now plus 1 month"
	ExpiresByType text/ico "now plus 1 month"
	ExpiresByType application/ico "now plus 1 month"
	ExpiresByType image/vnd.wap.wbmp "now plus 1 month"
	ExpiresByType application/vnd.wap.wbxml "now plus 1 month"
	ExpiresByType application/smil "now plus 1 month"

	# Audio files expiration: 1 month after request
	ExpiresByType audio/basic "now plus 1 month"
	ExpiresByType audio/mid "now plus 1 month"
	ExpiresByType audio/midi "now plus 1 month"
	ExpiresByType audio/mpeg "now plus 1 month"
	ExpiresByType audio/x-aiff "now plus 1 month"
	ExpiresByType audio/x-mpegurl "now plus 1 month"
	ExpiresByType audio/x-pn-realaudio "now plus 1 month"
	ExpiresByType audio/x-wav "now plus 1 month"

	# Movie files expiration: 1 month after request
	ExpiresByType application/x-shockwave-flash "now plus 1 month"
	ExpiresByType x-world/x-vrml "now plus 1 month"
	ExpiresByType video/x-msvideo "now plus 1 month"
	ExpiresByType video/mpeg "now plus 1 month"
	ExpiresByType video/mp4 "now plus 1 month"
	ExpiresByType video/quicktime "now plus 1 month"
	ExpiresByType video/x-la-asf "now plus 1 month"
	ExpiresByType video/x-ms-asf "now plus 1 month"
</IfModule>
##### Optimal default expiration time - END


END;
		}

		if (!empty($config->hoggeragents) && ($config->nohoggers == 1))
		{
			$htaccess .= "##### Common hacking tools and bandwidth hoggers block -- BEGIN\n";
			foreach ($config->hoggeragents as $agent)
			{
				$htaccess .= "SetEnvIf user-agent \"$agent\" stayout=1\n";
			}
			$htaccess .= <<< HTACCESS
<IfModule !mod_authz_core.c>
deny from env=stayout
</IfModule>
<IfModule mod_authz_core.c>
  <RequireAll>
    Require all granted
    Require not env stayout
  </RequireAll>
</IfModule>
##### Common hacking tools and bandwidth hoggers block -- END


HTACCESS;
		}

		if (($config->autocompress == 1) && ($serverCaps->deflate))
		{
			$htaccess .= <<<ENDHTCODE
##### Automatic compression of resources -- BEGIN
<ifmodule mod_deflate.c>
AddOutputFilterByType DEFLATE text/plain text/xml text/css application/xml application/xhtml+xml application/rss+xml application/javascript application/x-javascript
</ifmodule>
<ifModule mod_gzip.c>
mod_gzip_on Yes
mod_gzip_dechunk Yes
mod_gzip_keep_workfiles No
mod_gzip_can_negotiate Yes
mod_gzip_add_header_count Yes
mod_gzip_send_vary Yes
mod_gzip_min_http 1000
mod_gzip_minimum_file_size 300
mod_gzip_maximum_file_size 512000
mod_gzip_maximum_inmem_size 60000
mod_gzip_handle_methods GET
mod_gzip_item_include file \.(html?|txt|css|js|php|pl|xml|rb|py)$
mod_gzip_item_include mime ^text/plain$
mod_gzip_item_include mime ^text/xml$
mod_gzip_item_include mime ^text/css$
mod_gzip_item_include mime ^application/xml$
mod_gzip_item_include mime ^application/xhtml+xml$
mod_gzip_item_include mime ^application/rss+xml$
mod_gzip_item_include mime ^application/javascript$
mod_gzip_item_include mime ^application/x-javascript$
mod_gzip_item_exclude rspheader ^Content-Encoding:.*gzip.*
mod_gzip_item_include handler ^cgi-script$
mod_gzip_item_include handler ^server-status$
mod_gzip_item_include handler ^server-info$
mod_gzip_item_include handler ^application/x-httpd-php
mod_gzip_item_exclude mime ^image/.*
</ifmodule>
##### Automatic compression of resources -- END

ENDHTCODE;
		}

		if ($config->autoroot)
		{
			$htaccess .= <<<END
##### Redirect index.php to / -- BEGIN
RewriteCond %{THE_REQUEST} !^POST
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /index\.php\ HTTP/
RewriteCond %{SERVER_PORT}>s ^(443>(s)|[0-9]+>s)$
RewriteRule ^index\.php$ http%2://{$config->httphost}/ $redirCode
##### Redirect index.php to / -- END

END;
		}

		switch ($config->wwwredir)
		{
			case 1:
				// non-www to www
				$htaccess .= <<<END
##### Redirect non-www to www -- BEGIN
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^(.*)$ http://www.%{HTTP_HOST}/$1 $redirCode
##### Redirect non-www to www -- END


END;
				break;

			case 2:
				// www to non-www
				$htaccess .= <<<END
##### Redirect www to non-www -- BEGIN
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^(.*)$ http://%1/$1 $redirCode
##### Redirect www to non-www -- END


END;
				break;
		}

		if (!empty($config->olddomain))
		{
			$htaccess .= "##### Redirect old to new domain -- BEGIN\n";
			$domains = trim($config->olddomain);
			$domains = explode(',', $domains);
			$newdomain = $config->httphost;
			foreach ($domains as $olddomain)
			{
				$olddomain = trim($olddomain);
				if (empty($olddomain))
				{
					continue;
				}

				$olddomain = $this->escape_string_for_regex($olddomain);
				$htaccess .= <<<END
RewriteCond %{HTTP_HOST} ^$olddomain [NC]
RewriteRule (.*) http://$newdomain/$1 $redirCode

END;
			}
			$htaccess .= "##### Redirect old to new domain -- END\n\n";
		}

		if (!empty($config->httpsurls))
		{
			$htaccess .= "##### Force HTTPS for certain pages -- BEGIN\n";
			foreach ($config->httpsurls as $url)
			{
				$urlesc = '^' . $this->escape_string_for_regex($url) . '$';
				$htaccess .= <<<END
RewriteCond %{HTTPS} ^off$ [NC,OR]
RewriteCond %{SERVER_PORT} !^443$
RewriteRule $urlesc https://{$config->httpshost}/$url $redirCode

END;
			}
			$htaccess .= "##### Force HTTPS for certain pages -- END\n\n";
		}

		$htaccess .= <<<END
##### Rewrite rules to block out some common exploits -- BEGIN
RewriteCond %{QUERY_STRING} proc/self/environ [OR]
RewriteCond %{QUERY_STRING} mosConfig_[a-zA-Z_]{1,21}(=|\%3D) [OR]
RewriteCond %{QUERY_STRING} base64_(en|de)code\(.*\) [OR]
RewriteCond %{QUERY_STRING} (<|%3C).*script.*(>|%3E) [NC,OR]
RewriteCond %{QUERY_STRING} GLOBALS(=|\[|\%[0-9A-Z]{0,2}) [OR]
RewriteCond %{QUERY_STRING} _REQUEST(=|\[|\%[0-9A-Z]{0,2})
RewriteRule .* index.php [F]
##### Rewrite rules to block out some common exploits -- END

END;

		if ($config->fileinj == 1)
		{
			$htaccess .= <<<END
##### File injection protection -- BEGIN
RewriteCond %{REQUEST_METHOD} GET
RewriteCond %{QUERY_STRING} [a-zA-Z0-9_]=http:// [OR]
RewriteCond %{QUERY_STRING} [a-zA-Z0-9_]=(\.\.//?)+ [OR]
RewriteCond %{QUERY_STRING} [a-zA-Z0-9_]=/([a-z0-9_.]//?)+ [NC]
RewriteRule .* - [F]
##### File injection protection -- END


END;
		}

		$htaccess .= "##### Advanced server protection rules exceptions -- BEGIN\n";

		if (!empty($config->exceptionfiles))
		{
			foreach ($config->exceptionfiles as $file)
			{
				$file = '^' . $this->escape_string_for_regex($file) . '$';
				$htaccess .= <<<END
RewriteRule $file - [L]

END;
			}
		}

		if (!empty($config->exceptiondirs))
		{
			foreach ($config->exceptiondirs as $dir)
			{
				$dir = trim($dir, '/');
				$dir = $this->escape_string_for_regex($dir);
				$htaccess .= <<<END
RewriteCond %{REQUEST_FILENAME} !(\.php)$
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^$dir/ - [L]

END;
			}
		}

		if (!empty($config->fullaccessdirs))
		{
			foreach ($config->fullaccessdirs as $dir)
			{
				$dir = trim($dir, '/');
				$dir = $this->escape_string_for_regex($dir);
				$htaccess .= <<<END
RewriteRule ^$dir/ - [L]

END;
			}
		}

		$htaccess .= "##### Advanced server protection rules exceptions -- END\n\n";

		$htaccess .= "##### Advanced server protection -- BEGIN\n\n";

		if ($config->phpeaster == 1)
		{
			$htaccess .= <<<END
RewriteCond %{QUERY_STRING} \=PHP[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12} [NC]
RewriteRule .* - [F]

END;
		}

		if ($config->backendprot == 1)
		{
			$bedirs = implode('|', $config->bepexdirs);
			$betypes = implode('|', $config->bepextypes);
			$htaccess .= <<<END
## Back-end protection
RewriteRule ^administrator/?$ - [L]
RewriteRule ^administrator/index\.(php|html?)$ - [L]
RewriteRule ^administrator/index[23]\.php$ - [L]
RewriteRule ^administrator/($bedirs)/.*\.($betypes)$ - [L]
RewriteRule ^administrator/ - [F]

END;
		}

		if ($config->frontendprot == 1)
		{
			$fedirs = implode('|', $config->fepexdirs);
			$fetypes = implode('|', $config->fepextypes);
			$htaccess .= <<<END
## Allow limited access for certain Joomla! system directories with client-accessible content
RewriteRule ^($fedirs)/.*\.($fetypes)$ - [L]
RewriteRule ^($fedirs)/ - [F]
## Disallow front-end access for certain Joomla! system directories (unless access to their files is allowed above)
RewriteRule ^includes/js/ - [L]
RewriteRule ^(cache|includes|language|logs|log|tmp)/ - [F]
RewriteRule ^(configuration\.php|CONTRIBUTING\.md|htaccess\.txt|joomla\.xml|LICENSE\.txt|phpunit\.xml|README\.txt|web\.config\.txt) - [F]

## Disallow access to rogue PHP files throughout the site, unless they are explicitly allowed
RewriteCond %{REQUEST_FILENAME} (\.php)$
RewriteCond %{REQUEST_FILENAME} !(/index[23]?\.php)$
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule (.*\.php)$ - [F]

END;
		}

		if ($config->leftovers == 1)
		{
			$htaccess .= <<<END
## Disallow access to htaccess.txt, php.ini and configuration.php-dist
RewriteRule ^(htaccess\.txt|configuration\.php-dist|php\.ini)$ - [F]

END;
		}

		$htaccess .= "##### Advanced server protection -- END\n\n";

		if ($config->hstsheader == 1)
		{
			$htaccess .= <<<END
## HSTS Header - See http://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security
Header always set Strict-Transport-Security "max-age=31536000"

END;
		}

		if ($config->noframe == 1)
		{
			$htaccess .= <<<END
## Forbid displaying in FRAME (for HTTPS-only sites)
Header always append X-Frame-Options SAMEORIGIN

END;
		}

		if ($config->notracetrack == 1)
		{
			$tmpRedirCode = $serverCaps->customCodes ? '[R=405,L]' : '[F,L]';
			$htaccess .= <<<END
## Disable HTTP methods TRACE and TRACK (protect against XST)
RewriteCond %{REQUEST_METHOD} ^(TRACE|TRACK)
RewriteRule .* - $tmpRedirCode
END;
		}

		$htaccess .= <<<END
##### Joomla! core SEF Section -- BEGIN
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteCond %{REQUEST_URI} !^/index\.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule .* index.php [L]
##### Joomla! core SEF Section -- END


END;

		$htaccess .= "\n\n" . $config->custfoot . "\n";

		return $htaccess;
	}

	public function writeHtaccess()
	{
		$htaccess = $this->makeHtaccess();

		JLoader::import('joomla.filesystem.file');

		if (@file_exists(JPATH_ROOT . '/.htaccess'))
		{
			JFile::copy('.htaccess', '.htaccess.admintools', JPATH_ROOT);
		}

		return JFile::write(JPATH_ROOT . DIRECTORY_SEPARATOR . '.htaccess', $htaccess);
	}

	private function escape_string_for_regex($str)
	{
		//All regex special chars (according to arkani at iol dot pt below):
		// \ ^ . $ | ( ) [ ]
		// * + ? { } , -

		$patterns = array(
			'/\//', '/\^/', '/\./', '/\$/', '/\|/',
			'/\(/', '/\)/', '/\[/', '/\]/', '/\*/', '/\+/',
			'/\?/', '/\{/', '/\}/', '/\,/', '/\-/'
		);
		$replace = array(
			'\/', '\^', '\.', '\$', '\|', '\(', '\)',
			'\[', '\]', '\*', '\+', '\?', '\{', '\}', '\,', '\-'
		);

		return preg_replace($patterns, $replace, $str);
	}

	/**
	 * Guesses and returns the Apache version family.
	 *
	 * @return string 1.1, 1.3, 2.0, 2.2, 2.5 or 0.0 (if no match)
	 */
	private function apacheVersion()
	{
		// Get the server string
		$serverString = $_SERVER['SERVER_SOFTWARE'];
		// Not defined? Assume Apache 2.0.
		if (empty($serverString))
		{
			return '2.0';
		}
		// LiteSpeed? Fake it.
		if (strtoupper(substr($serverString, 0, 9)) == 'LITESPEED')
		{
			return '2.0';
		}
		// Not Apache? Return 0.0
		if (strtoupper(substr($serverString, 0, 6)) !== 'APACHE')
		{
			return '0.0';
		}
		// No slash after Apache? Assume 2.5
		if (strlen($serverString) < 7)
		{
			return '2.5';
		}
		if (substr($serverString, 6, 1) != '/')
		{
			return '2.5';
		}
		// Strip part past the version string
		$serverString = substr($serverString, 7);

		$v = substr($serverString, 0, 3);
		switch ($v)
		{
			case '1.3':
			case '2.0':
			case '2.2':
			case '2.5':
				return $v;
				break;
			default:
				if (version_compare($v, '1.3', 'lt'))
				{
					return '1.1';
				}
				else
				{
					return '2.2';
				}
				break;
		}

		return $serverString;
	}
}