Spade

Mini Shell

Directory:~$ /proc/self/root/home/lmsyaran/www/pusher/
Upload File

[Home] [System Details] [Kill Me]
Current File:~$ //proc/self/root/home/lmsyaran/www/pusher/utils.tar

array/array.php000064400000030761151167373210007526 0ustar00<?php
/**
 * @package     FrameworkOnFramework
 * @subpackage  utils
 * @copyright   Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('FOF_INCLUDED') or die;

/**
 * A utility class to handle array manipulation.
 *
 * Based on the JArrayHelper class as found in Joomla! 3.2.0
 */
abstract class FOFUtilsArray
{
	/**
	 * Option to perform case-sensitive sorts.
	 *
	 * @var    mixed  Boolean or array of booleans.
	 */
	protected static $sortCase;

	/**
	 * Option to set the sort direction.
	 *
	 * @var    mixed  Integer or array of integers.
	 */
	protected static $sortDirection;

	/**
	 * Option to set the object key to sort on.
	 *
	 * @var    string
	 */
	protected static $sortKey;

	/**
	 * Option to perform a language aware sort.
	 *
	 * @var    mixed  Boolean or array of booleans.
	 */
	protected static $sortLocale;

	/**
	 * Function to convert array to integer values
	 *
	 * @param   array  &$array   The source array to convert
	 * @param   mixed  $default  A default value (int|array) to assign if
$array is not an array
	 *
	 * @return  void
	 */
	public static function toInteger(&$array, $default = null)
	{
		if (is_array($array))
		{
			foreach ($array as $i => $v)
			{
				$array[$i] = (int) $v;
			}
		}
		else
		{
			if ($default === null)
			{
				$array = array();
			}
			elseif (is_array($default))
			{
				self::toInteger($default, null);
				$array = $default;
			}
			else
			{
				$array = array((int) $default);
			}
		}
	}

	/**
	 * Utility function to map an array to a stdClass object.
	 *
	 * @param   array   &$array  The array to map.
	 * @param   string  $class   Name of the class to create
	 *
	 * @return  object   The object mapped from the given array
	 */
	public static function toObject(&$array, $class =
'stdClass')
	{
		$obj = null;

		if (is_array($array))
		{
			$obj = new $class;

			foreach ($array as $k => $v)
			{
				if (is_array($v))
				{
					$obj->$k = self::toObject($v, $class);
				}
				else
				{
					$obj->$k = $v;
				}
			}
		}
		return $obj;
	}

	/**
	 * Utility function to map an array to a string.
	 *
	 * @param   array    $array         The array to map.
	 * @param   string   $inner_glue    The glue (optional, defaults to
'=') between the key and the value.
	 * @param   string   $outer_glue    The glue (optional, defaults to '
') between array elements.
	 * @param   boolean  $keepOuterKey  True if final key should be kept.
	 *
	 * @return  string   The string mapped from the given array
	 */
	public static function toString($array = null, $inner_glue =
'=', $outer_glue = ' ', $keepOuterKey = false)
	{
		$output = array();

		if (is_array($array))
		{
			foreach ($array as $key => $item)
			{
				if (is_array($item))
				{
					if ($keepOuterKey)
					{
						$output[] = $key;
					}
					// This is value is an array, go and do it again!
					$output[] = self::toString($item, $inner_glue, $outer_glue,
$keepOuterKey);
				}
				else
				{
					$output[] = $key . $inner_glue . '"' . $item .
'"';
				}
			}
		}

		return implode($outer_glue, $output);
	}

	/**
	 * Utility function to map an object to an array
	 *
	 * @param   object   $p_obj    The source object
	 * @param   boolean  $recurse  True to recurse through multi-level objects
	 * @param   string   $regex    An optional regular expression to match on
field names
	 *
	 * @return  array    The array mapped from the given object
	 */
	public static function fromObject($p_obj, $recurse = true, $regex = null)
	{
		if (is_object($p_obj))
		{
			return self::_fromObject($p_obj, $recurse, $regex);
		}
		else
		{
			return null;
		}
	}

	/**
	 * Utility function to map an object or array to an array
	 *
	 * @param   mixed    $item     The source object or array
	 * @param   boolean  $recurse  True to recurse through multi-level objects
	 * @param   string   $regex    An optional regular expression to match on
field names
	 *
	 * @return  array  The array mapped from the given object
	 */
	protected static function _fromObject($item, $recurse, $regex)
	{
		if (is_object($item))
		{
			$result = array();

			foreach (get_object_vars($item) as $k => $v)
			{
				if (!$regex || preg_match($regex, $k))
				{
					if ($recurse)
					{
						$result[$k] = self::_fromObject($v, $recurse, $regex);
					}
					else
					{
						$result[$k] = $v;
					}
				}
			}
		}
		elseif (is_array($item))
		{
			$result = array();

			foreach ($item as $k => $v)
			{
				$result[$k] = self::_fromObject($v, $recurse, $regex);
			}
		}
		else
		{
			$result = $item;
		}
		return $result;
	}

	/**
	 * Extracts a column from an array of arrays or objects
	 *
	 * @param   array   &$array  The source array
	 * @param   string  $index   The index of the column or name of object
property
	 *
	 * @return  array  Column of values from the source array
	 */
	public static function getColumn(&$array, $index)
	{
		$result = array();

		if (is_array($array))
		{
			foreach ($array as &$item)
			{
				if (is_array($item) && isset($item[$index]))
				{
					$result[] = $item[$index];
				}
				elseif (is_object($item) && isset($item->$index))
				{
					$result[] = $item->$index;
				}
				// Else ignore the entry
			}
		}
		return $result;
	}

	/**
	 * Utility function to return a value from a named array or a specified
default
	 *
	 * @param   array   &$array   A named array
	 * @param   string  $name     The key to search for
	 * @param   mixed   $default  The default value to give if no key found
	 * @param   string  $type     Return type for the variable (INT, FLOAT,
STRING, WORD, BOOLEAN, ARRAY)
	 *
	 * @return  mixed  The value from the source array
	 */
	public static function getValue(&$array, $name, $default = null, $type
= '')
	{
		$result = null;

		if (isset($array[$name]))
		{
			$result = $array[$name];
		}

		// Handle the default case
		if (is_null($result))
		{
			$result = $default;
		}

		// Handle the type constraint
		switch (strtoupper($type))
		{
			case 'INT':
			case 'INTEGER':
				// Only use the first integer value
				@preg_match('/-?[0-9]+/', $result, $matches);
				$result = @(int) $matches[0];
				break;

			case 'FLOAT':
			case 'DOUBLE':
				// Only use the first floating point value
				@preg_match('/-?[0-9]+(\.[0-9]+)?/', $result, $matches);
				$result = @(float) $matches[0];
				break;

			case 'BOOL':
			case 'BOOLEAN':
				$result = (bool) $result;
				break;

			case 'ARRAY':
				if (!is_array($result))
				{
					$result = array($result);
				}
				break;

			case 'STRING':
				$result = (string) $result;
				break;

			case 'WORD':
				$result = (string) preg_replace('#\W#', '',
$result);
				break;

			case 'NONE':
			default:
				// No casting necessary
				break;
		}
		return $result;
	}

	/**
	 * Takes an associative array of arrays and inverts the array keys to
values using the array values as keys.
	 *
	 * Example:
	 * $input = array(
	 *     'New' => array('1000', '1500',
'1750'),
	 *     'Used' => array('3000', '4000',
'5000', '6000')
	 * );
	 * $output = FOFUtilsArray::invert($input);
	 *
	 * Output would be equal to:
	 * $output = array(
	 *     '1000' => 'New',
	 *     '1500' => 'New',
	 *     '1750' => 'New',
	 *     '3000' => 'Used',
	 *     '4000' => 'Used',
	 *     '5000' => 'Used',
	 *     '6000' => 'Used'
	 * );
	 *
	 * @param   array  $array  The source array.
	 *
	 * @return  array  The inverted array.
	 */
	public static function invert($array)
	{
		$return = array();

		foreach ($array as $base => $values)
		{
			if (!is_array($values))
			{
				continue;
			}

			foreach ($values as $key)
			{
				// If the key isn't scalar then ignore it.
				if (is_scalar($key))
				{
					$return[$key] = $base;
				}
			}
		}
		return $return;
	}

	/**
	 * Method to determine if an array is an associative array.
	 *
	 * @param   array  $array  An array to test.
	 *
	 * @return  boolean  True if the array is an associative array.
	 */
	public static function isAssociative($array)
	{
		if (is_array($array))
		{
			foreach (array_keys($array) as $k => $v)
			{
				if ($k !== $v)
				{
					return true;
				}
			}
		}

		return false;
	}

	/**
	 * Pivots an array to create a reverse lookup of an array of scalars,
arrays or objects.
	 *
	 * @param   array   $source  The source array.
	 * @param   string  $key     Where the elements of the source array are
objects or arrays, the key to pivot on.
	 *
	 * @return  array  An array of arrays pivoted either on the value of the
keys, or an individual key of an object or array.
	 */
	public static function pivot($source, $key = null)
	{
		$result = array();
		$counter = array();

		foreach ($source as $index => $value)
		{
			// Determine the name of the pivot key, and its value.
			if (is_array($value))
			{
				// If the key does not exist, ignore it.
				if (!isset($value[$key]))
				{
					continue;
				}

				$resultKey = $value[$key];
				$resultValue = &$source[$index];
			}
			elseif (is_object($value))
			{
				// If the key does not exist, ignore it.
				if (!isset($value->$key))
				{
					continue;
				}

				$resultKey = $value->$key;
				$resultValue = &$source[$index];
			}
			else
			{
				// Just a scalar value.
				$resultKey = $value;
				$resultValue = $index;
			}

			// The counter tracks how many times a key has been used.
			if (empty($counter[$resultKey]))
			{
				// The first time around we just assign the value to the key.
				$result[$resultKey] = $resultValue;
				$counter[$resultKey] = 1;
			}
			elseif ($counter[$resultKey] == 1)
			{
				// If there is a second time, we convert the value into an array.
				$result[$resultKey] = array(
					$result[$resultKey],
					$resultValue,
				);
				$counter[$resultKey]++;
			}
			else
			{
				// After the second time, no need to track any more. Just append to the
existing array.
				$result[$resultKey][] = $resultValue;
			}
		}

		unset($counter);

		return $result;
	}

	/**
	 * Utility function to sort an array of objects on a given field
	 *
	 * @param   array  &$a             An array of objects
	 * @param   mixed  $k              The key (string) or a array of key to
sort on
	 * @param   mixed  $direction      Direction (integer) or an array of
direction to sort in [1 = Ascending] [-1 = Descending]
	 * @param   mixed  $caseSensitive  Boolean or array of booleans to let
sort occur case sensitive or insensitive
	 * @param   mixed  $locale         Boolean or array of booleans to let
sort occur using the locale language or not
	 *
	 * @return  array  The sorted array of objects
	 */
	public static function sortObjects(&$a, $k, $direction = 1,
$caseSensitive = true, $locale = false)
	{
		if (!is_array($locale) || !is_array($locale[0]))
		{
			$locale = array($locale);
		}

		self::$sortCase = (array) $caseSensitive;
		self::$sortDirection = (array) $direction;
		self::$sortKey = (array) $k;
		self::$sortLocale = $locale;

		usort($a, array(__CLASS__, '_sortObjects'));

		self::$sortCase = null;
		self::$sortDirection = null;
		self::$sortKey = null;
		self::$sortLocale = null;

		return $a;
	}

	/**
	 * Callback function for sorting an array of objects on a key
	 *
	 * @param   array  &$a  An array of objects
	 * @param   array  &$b  An array of objects
	 *
	 * @return  integer  Comparison status
	 *
	 * @see     FOFUtilsArray::sortObjects()
	 */
	protected static function _sortObjects(&$a, &$b)
	{
		$key = self::$sortKey;

		for ($i = 0, $count = count($key); $i < $count; $i++)
		{
			if (isset(self::$sortDirection[$i]))
			{
				$direction = self::$sortDirection[$i];
			}

			if (isset(self::$sortCase[$i]))
			{
				$caseSensitive = self::$sortCase[$i];
			}

			if (isset(self::$sortLocale[$i]))
			{
				$locale = self::$sortLocale[$i];
			}

			$va = $a->{$key[$i]};
			$vb = $b->{$key[$i]};

			if ((is_bool($va) || is_numeric($va)) && (is_bool($vb) ||
is_numeric($vb)))
			{
				$cmp = $va - $vb;
			}
			elseif ($caseSensitive)
			{
				$cmp = JString::strcmp($va, $vb, $locale);
			}
			else
			{
				$cmp = JString::strcasecmp($va, $vb, $locale);
			}

			if ($cmp > 0)
			{

				return $direction;
			}

			if ($cmp < 0)
			{
				return -$direction;
			}
		}

		return 0;
	}

	/**
	 * Multidimensional array safe unique test
	 *
	 * @param   array  $myArray  The array to make unique.
	 *
	 * @return  array
	 *
	 * @see     http://php.net/manual/en/function.array-unique.php
	 */
	public static function arrayUnique($myArray)
	{
		if (!is_array($myArray))
		{
			return $myArray;
		}

		foreach ($myArray as &$myvalue)
		{
			$myvalue = serialize($myvalue);
		}

		$myArray = array_unique($myArray);

		foreach ($myArray as &$myvalue)
		{
			$myvalue = unserialize($myvalue);
		}

		return $myArray;
	}
}
cache/cleaner.php000064400000004353151167373210007744 0ustar00<?php
/**
 * @package     FrameworkOnFramework
 * @subpackage  utils
 * @copyright   Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('FOF_INCLUDED') or die;

/**
 * A utility class to help you quickly clean the Joomla! cache
 */
class FOFUtilsCacheCleaner
{
	/**
	 * Clears the com_modules and com_plugins cache. You need to call this
whenever you alter the publish state or
	 * parameters of a module or plugin from your code.
	 *
	 * @return  void
	 */
	public static function clearPluginsAndModulesCache()
	{
		self::clearPluginsCache();
		self::clearModulesCache();
	}

	/**
	 * Clears the com_plugins cache. You need to call this whenever you alter
the publish state or parameters of a
	 * plugin from your code.
	 *
	 * @return  void
	 */
	public static function clearPluginsCache()
	{
		self::clearCacheGroups(array('com_plugins'), array(0,1));
	}

	/**
	 * Clears the com_modules cache. You need to call this whenever you alter
the publish state or parameters of a
	 * module from your code.
	 *
	 * @return  void
	 */
	public static function clearModulesCache()
	{
		self::clearCacheGroups(array('com_modules'), array(0,1));
	}

	/**
	 * Clears the specified cache groups.
	 *
	 * @param   array $clearGroups    Which cache groups to clear. Usually
this is com_yourcomponent to clear your
	 *                                component's cache.
	 * @param   array   $cacheClients Which cache clients to clear. 0 is the
back-end, 1 is the front-end. If you do not
	 *                                specify anything, both cache clients
will be cleared.
	 *
	 * @return  void
	 */
	public static function clearCacheGroups(array $clearGroups, array
$cacheClients = array(0, 1))
	{
		$conf = JFactory::getConfig();

		foreach ($clearGroups as $group)
		{
			foreach ($cacheClients as $client_id)
			{
				try
				{
					$options = array(
						'defaultgroup' => $group,
						'cachebase' => ($client_id) ? JPATH_ADMINISTRATOR .
'/cache' : $conf->get('cache_path', JPATH_SITE .
'/cache')
					);

					$cache = JCache::getInstance('callback', $options);
					$cache->clean();
				}
				catch (Exception $e)
				{
					// suck it up
				}
			}
		}
	}
} config/helper.php000064400000005143151167373210010012 0ustar00<?php
/**
 * @package     FrameworkOnFramework
 * @subpackage  utils
 * @copyright   Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('FOF_INCLUDED') or die;

/**
 * A utility class to help you fetch component parameters without going
through JComponentHelper
 */
class FOFUtilsConfigHelper
{
	/**
	 * Caches the component parameters without going through JComponentHelper.
This is necessary since JComponentHelper
	 * cannot be reset or updated once you update parameters in the database.
	 *
	 * @var array
	 */
	private static $componentParams = array();

	/**
	 * Loads the component's configuration parameters so they can be
accessed by getComponentConfigurationValue
	 *
	 * @param   string  $component  The component for loading the parameters
	 * @param   bool    $force      Should I force-reload the configuration
information?
	 */
	public final static function loadComponentConfig($component, $force =
false)
	{
		if (isset(self::$componentParams[$component]) &&
!is_null(self::$componentParams[$component]) && !$force)
		{
			return;
		}

		$db = FOFPlatform::getInstance()->getDbo();

		$sql = $db->getQuery(true)
				  ->select($db->qn('params'))
				  ->from($db->qn('#__extensions'))
				  ->where($db->qn('type') . ' = ' .
$db->q('component'))
				  ->where($db->qn('element') . " = " .
$db->q($component));
		$db->setQuery($sql);
		$config_ini = $db->loadResult();

		// OK, Joomla! 1.6 stores values JSON-encoded so, what do I do? Right!
		$config_ini = trim($config_ini);

		if ((substr($config_ini, 0, 1) == '{') &&
substr($config_ini, -1) == '}')
		{
			$config_ini = json_decode($config_ini, true);
		}
		else
		{
			$config_ini = FOFUtilsIniParser::parse_ini_file($config_ini, false,
true);
		}

		if (is_null($config_ini) || empty($config_ini))
		{
			$config_ini = array();
		}

		self::$componentParams[$component] = $config_ini;
	}

	/**
	 * Retrieves the value of a component configuration parameter without
going through JComponentHelper
	 *
	 * @param   string  $component  The component for loading the parameter
value
	 * @param   string  $key        The key to retrieve
	 * @param   mixed   $default    The default value to use in case the key
is missing
	 *
	 * @return  mixed
	 */
	public final static function getComponentConfigurationValue($component,
$key, $default = null)
	{
		self::loadComponentConfig($component, false);

		if (array_key_exists($key, self::$componentParams[$component]))
		{
			return self::$componentParams[$component][$key];
		}
		else
		{
			return $default;
		}
	}
} filescheck/filescheck.php000064400000015641151167373210011472
0ustar00<?php
/**
 * @package     FrameworkOnFramework
 * @subpackage  utils
 * @copyright   Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('FOF_INCLUDED') or die;

/**
 * A utility class to check that your extension's files are not
missing and have not been tampered with.
 *
 * You need a file called fileslist.php in your component's
administrator root directory with the following contents:
 *
 * $phpFileChecker = array(
 *   'version' => 'revCEE2DAB',
 *   'date' => '2014-10-16',
 *   'directories' => array(
 *     'administrator/components/com_foobar',
 *     ....
 *   ),
 *   'files' => array(
 *     'administrator/components/com_foobar/access.xml' =>
array('705', '09aa0351a316bf011ecc8c1145134761',
'b95f00c7b49a07a60570dc674f2497c45c4e7152'),
 *     ....
 *   )
 * );
 *
 * All directory and file paths are relative to the site's root
 *
 * The directories array is a list of
 */
class FOFUtilsFilescheck
{
	/** @var string The name of the component */
	protected $option = '';

	/** @var string Current component version */
	protected $version = null;

	/** @var string Current component release date */
	protected $date = null;

	/** @var array List of files to check as filepath => (filesize, md5,
sha1) */
	protected $fileList = array();

	/** @var array List of directories to check that exist */
	protected $dirList = array();

	/** @var bool Is the reported component version different than the version
of the #__extensions table? */
	protected $wrongComponentVersion = false;

	/** @var bool Is the fileslist.php reporting a version different than the
reported component version? */
	protected $wrongFilesVersion = false;

	/**
	 * Create and initialise the object
	 *
	 * @param string $option Component name, e.g. com_foobar
	 * @param string $version The current component version, as reported by
the component
	 * @param string $date The current component release date, as reported by
the component
	 */
	public function __construct($option, $version, $date)
	{
		// Initialise from parameters
		$this->option = $option;
		$this->version = $version;
		$this->date = $date;

		// Retrieve the date and version from the #__extensions table
		$db = FOFPlatform::getInstance()->getDbo();
		$query =
$db->getQuery(true)->select('*')->from($db->qn('#__extensions'))
					->where($db->qn('element') . ' = ' .
$db->q($this->option))
					->where($db->qn('type') . ' = ' .
$db->q('component'));
		$extension = $db->setQuery($query)->loadObject();

		// Check the version and date against those from #__extensions. I hate
heavily nested IFs as much as the next
		// guy, but what can you do...
		if (!is_null($extension))
		{
			$manifestCache = $extension->manifest_cache;

			if (!empty($manifestCache))
			{
				$manifestCache = json_decode($manifestCache, true);

				if (is_array($manifestCache) &&
isset($manifestCache['creationDate']) &&
isset($manifestCache['version']))
				{
					// Make sure the fileslist.php version and date match the
component's version
					if ($this->version != $manifestCache['version'])
					{
						$this->wrongComponentVersion = true;
					}

					if ($this->date != $manifestCache['creationDate'])
					{
						$this->wrongComponentVersion = true;
					}
				}
			}
		}

		// Try to load the fileslist.php file from the component's back-end
root
		$filePath = JPATH_ADMINISTRATOR . '/components/' .
$this->option . '/fileslist.php';

		if (!file_exists($filePath))
		{
			return;
		}

		include $filePath;

		// Make sure the fileslist.php version and date match the
component's version
		if ($this->version != $phpFileChecker['version'])
		{
			$this->wrongFilesVersion = true;
		}

		if ($this->date != $phpFileChecker['date'])
		{
			$this->wrongFilesVersion = true;
		}

		// Initialise the files and directories lists
		$this->fileList = $phpFileChecker['files'];
		$this->dirList = $phpFileChecker['directories'];
	}

	/**
	 * Is the reported component version different than the version of the
#__extensions table?
	 *
	 * @return boolean
	 */
	public function isWrongComponentVersion()
	{
		return $this->wrongComponentVersion;
	}

	/**
	 * Is the fileslist.php reporting a version different than the reported
component version?
	 *
	 * @return boolean
	 */
	public function isWrongFilesVersion()
	{
		return $this->wrongFilesVersion;
	}

	/**
	 * Performs a fast check of file and folders. If even one of the
files/folders doesn't exist, or even one file has
	 * the wrong file size it will return false.
	 *
	 * @return bool False when there are mismatched files and directories
	 */
	public function fastCheck()
	{
		// Check that all directories exist
		foreach ($this->dirList as $directory)
		{
			$directory = JPATH_ROOT . '/' . $directory;

			if (!@is_dir($directory))
			{
				return false;
			}
		}

		// Check that all files exist and have the right size
		foreach ($this->fileList as $filePath => $fileData)
		{
			$filePath = JPATH_ROOT . '/' . $filePath;

			if (!@file_exists($filePath))
			{
				return false;
			}

			$fileSize = @filesize($filePath);

			if ($fileSize != $fileData[0])
			{
				return false;
			}
		}

		return true;
	}

	/**
	 * Performs a slow, thorough check of all files and folders (including
MD5/SHA1 sum checks)
	 *
	 * @param int $idx The index from where to start
	 *
	 * @return array Progress report
	 */
	public function slowCheck($idx = 0)
	{
		$ret = array(
			'done'	=> false,
			'files'	=> array(),
			'folders'	=> array(),
			'idx'	=> $idx
		);

		$totalFiles = count($this->fileList);
		$totalFolders = count($this->dirList);
		$fileKeys = array_keys($this->fileList);

		$timer = new FOFUtilsTimer(3.0, 75.0);

		while ($timer->getTimeLeft() && (($idx < $totalFiles) ||
($idx < $totalFolders)))
		{
			if ($idx < $totalFolders)
			{
				$directory = JPATH_ROOT . '/' . $this->dirList[$idx];

				if (!@is_dir($directory))
				{
					$ret['folders'][] = $directory;
				}
			}

			if ($idx < $totalFiles)
			{
				$fileKey = $fileKeys[$idx];
				$filePath = JPATH_ROOT . '/' . $fileKey;
				$fileData = $this->fileList[$fileKey];

				if (!@file_exists($filePath))
				{
					$ret['files'][] = $fileKey . ' (missing)';
				}
				elseif (@filesize($filePath) != $fileData[0])
				{
					$ret['files'][] = $fileKey . ' (size ' .
@filesize($filePath) . ' ≠ ' . $fileData[0] . ')';
				}
				else
				{
					if (function_exists('sha1_file'))
					{
						$fileSha1 = @sha1_file($filePath);

						if ($fileSha1 != $fileData[2])
						{
							$ret['files'][] = $fileKey . ' (SHA1 ' .
$fileSha1 . ' ≠ ' . $fileData[2] . ')';
						}
					}
					elseif (function_exists('md5_file'))
					{
						$fileMd5 = @md5_file($filePath);

						if ($fileMd5 != $fileData[1])
						{
							$ret['files'][] = $fileKey . ' (MD5 ' . $fileMd5
. ' ≠ ' . $fileData[1] . ')';
						}
					}
				}
			}

			$idx++;
		}

		if (($idx >= $totalFiles) && ($idx >= $totalFolders))
		{
			$ret['done'] = true;
		}

		$ret['idx'] = $idx;

		return $ret;
	}
}ini/parser.php000064400000011061151167373210007335 0ustar00<?php
/**
 * @package     FrameworkOnFramework
 * @subpackage  utils
 * @copyright   Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 * @note        This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
 */

defined('FOF_INCLUDED') or die;

/**
 * A utility class to parse INI files. This monstrosity is only required
because some impossibly misguided individuals
 * who misrepresent themselves as hosts have disabled PHP's
parse_ini_file() function for "security reasons". Apparently
 * their blatant ignorance doesn't allow them to discern between the
innocuous parse_ini_file and the _potentially_
 * dangerous ini_set functions, leading them to disable the former and let
the latter enabled. In other words, THIS
 * CLASS IS HERE TO FIX STUPID.
 */
class FOFUtilsIniParser
{
	/**
	 * Parse an INI file and return an associative array.
	 *
	 * @param    string  $file              The file to process
	 * @param    bool    $process_sections  True to also process INI sections
	 *
	 * @return   array    An associative array of sections, keys and values
	 */
	public static function parse_ini_file($file, $process_sections, $rawdata =
false)
	{
		$isMoronHostFile = !function_exists('parse_ini_file');
		$isMoronHostString = !function_exists('parse_ini_string');

		if ($rawdata)
		{
			if ($isMoronHostString)
			{
				return self::parse_ini_file_php($file, $process_sections, $rawdata);
			}
			else
			{
				return parse_ini_string($file, $process_sections);
			}
		}
		else
		{
			if ($isMoronHostFile)
			{
				return self::parse_ini_file_php($file, $process_sections);
			}
			else
			{
				return parse_ini_file($file, $process_sections);
			}
		}
	}

	/**
	 * A PHP based INI file parser.
	 *
	 * Thanks to asohn ~at~ aircanopy ~dot~ net for posting this handy
function on
	 * the parse_ini_file page on http://gr.php.net/parse_ini_file
	 *
	 * @param    string $file             Filename to process
	 * @param    bool   $process_sections True to also process INI sections
	 * @param    bool   $rawdata          If true, the $file contains raw INI
data, not a filename
	 *
	 * @return    array    An associative array of sections, keys and values
	 */
	static function parse_ini_file_php($file, $process_sections = false,
$rawdata = false)
	{
		$process_sections = ($process_sections !== true) ? false : true;

		if (!$rawdata)
		{
			$ini = file($file);
		}
		else
		{
			$file = str_replace("\r", "", $file);
			$ini = explode("\n", $file);
		}

		if (count($ini) == 0)
		{
			return array();
		}

		$sections = array();
		$values = array();
		$result = array();
		$globals = array();
		$i = 0;
		foreach ($ini as $line)
		{
			$line = trim($line);
			$line = str_replace("\t", " ", $line);

			// Comments
			if (!preg_match('/^[a-zA-Z0-9[]/', $line))
			{
				continue;
			}

			// Sections
			if ($line[0] == '[')
			{
				$tmp = explode(']', $line);
				$sections[] = trim(substr($tmp[0], 1));
				$i++;
				continue;
			}

			// Key-value pair
			$lineParts = explode('=', $line, 2);
			if (count($lineParts) != 2)
			{
				continue;
			}
			$key = trim($lineParts[0]);
			$value = trim($lineParts[1]);
			unset($lineParts);

			if (strstr($value, ";"))
			{
				$tmp = explode(';', $value);
				if (count($tmp) == 2)
				{
					if ((($value[0] != '"') && ($value[0] !=
"'")) ||
							preg_match('/^".*"\s*;/', $value) ||
preg_match('/^".*;[^"]*$/', $value) ||
							preg_match("/^'.*'\s*;/", $value) ||
preg_match("/^'.*;[^']*$/", $value)
					)
					{
						$value = $tmp[0];
					}
				}
				else
				{
					if ($value[0] == '"')
					{
						$value = preg_replace('/^"(.*)".*/',
'$1', $value);
					}
					elseif ($value[0] == "'")
					{
						$value = preg_replace("/^'(.*)'.*/",
'$1', $value);
					}
					else
					{
						$value = $tmp[0];
					}
				}
			}
			$value = trim($value);
			$value = trim($value, "'\"");

			if ($i == 0)
			{
				if (substr($line, -1, 2) == '[]')
				{
					$globals[$key][] = $value;
				}
				else
				{
					$globals[$key] = $value;
				}
			}
			else
			{
				if (substr($line, -1, 2) == '[]')
				{
					$values[$i - 1][$key][] = $value;
				}
				else
				{
					$values[$i - 1][$key] = $value;
				}
			}
		}

		for ($j = 0; $j < $i; $j++)
		{
			if ($process_sections === true)
			{
				if (isset($sections[$j]) && isset($values[$j]))
				{
					$result[$sections[$j]] = $values[$j];
				}
			}
			else
			{
				if (isset($values[$j]))
				{
					$result[] = $values[$j];
				}
			}
		}

		return $result + $globals;
	}
} installscript/installscript.php000064400000200207151167373210013052
0ustar00<?php
/**
 * @package     FrameworkOnFramework
 * @subpackage  utils
 * @copyright   Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 * @note	This file has been modified by the Joomla! Project and no longer
reflects the original work of its author.
 */

defined('FOF_INCLUDED') or die;

JLoader::import('joomla.filesystem.folder');
JLoader::import('joomla.filesystem.file');
JLoader::import('joomla.installer.installer');
JLoader::import('joomla.utilities.date');

/**
 * A helper class which you can use to create component installation
scripts
 */
abstract class FOFUtilsInstallscript
{
	/**
	 * The component's name
	 *
	 * @var   string
	 */
	protected $componentName = 'com_foobar';

	/**
	 * The title of the component (printed on installation and uninstallation
messages)
	 *
	 * @var string
	 */
	protected $componentTitle = 'Foobar Component';

	/**
	 * The list of extra modules and plugins to install on component
installation / update and remove on component
	 * uninstallation.
	 *
	 * @var   array
	 */
	protected $installation_queue = array(
		// modules => { (folder) => { (module) => { (position),
(published) } }* }*
		'modules' => array(
			'admin' => array(),
			'site'  => array()
		),
		// plugins => { (folder) => { (element) => (published) }* }*
		'plugins' => array(
			'system' => array(),
		)
	);

	/**
	 * The list of obsolete extra modules and plugins to uninstall on
component upgrade / installation.
	 *
	 * @var array
	 */
	protected $uninstallation_queue = array(
		// modules => { (folder) => { (module) }* }*
		'modules' => array(
			'admin' => array(),
			'site'  => array()
		),
		// plugins => { (folder) => { (element) }* }*
		'plugins' => array(
			'system' => array(),
		)
	);

	/**
	 * Obsolete files and folders to remove from the free version only. This
is used when you move a feature from the
	 * free version of your extension to its paid version. If you don't
have such a distinction you can ignore this.
	 *
	 * @var   array
	 */
	protected $removeFilesFree = array(
		'files'   => array(
			// Use pathnames relative to your site's root, e.g.
			// 'administrator/components/com_foobar/helpers/whatever.php'
		),
		'folders' => array(
			// Use pathnames relative to your site's root, e.g.
			// 'administrator/components/com_foobar/baz'
		)
	);

	/**
	 * Obsolete files and folders to remove from both paid and free releases.
This is used when you refactor code and
	 * some files inevitably become obsolete and need to be removed.
	 *
	 * @var   array
	 */
	protected $removeFilesAllVersions = array(
		'files'   => array(
			// Use pathnames relative to your site's root, e.g.
			// 'administrator/components/com_foobar/helpers/whatever.php'
		),
		'folders' => array(
			// Use pathnames relative to your site's root, e.g.
			// 'administrator/components/com_foobar/baz'
		)
	);

	/**
	 * A list of scripts to be copied to the "cli" directory of the
site
	 *
	 * @var   array
	 */
	protected $cliScriptFiles = array(
		// Use just the filename, e.g.
		// 'my-cron-script.php'
	);

	/**
	 * The path inside your package where cli scripts are stored
	 *
	 * @var   string
	 */
	protected $cliSourcePath = 'cli';

	/**
	 * The path inside your package where FOF is stored
	 *
	 * @var   string
	 */
	protected $fofSourcePath = 'fof';

	/**
	 * The path inside your package where Akeeba Strapper is stored
	 *
	 * @var   string
	 */
	protected $strapperSourcePath = 'strapper';

	/**
	 * The path inside your package where extra modules are stored
	 *
	 * @var   string
	 */
	protected $modulesSourcePath = 'modules';

	/**
	 * The path inside your package where extra plugins are stored
	 *
	 * @var   string
	 */
	protected $pluginsSourcePath = 'plugins';

	/**
	 * Is the schemaXmlPath class variable a relative path? If set to true the
schemaXmlPath variable contains a path
	 * relative to the component's back-end directory. If set to false
the schemaXmlPath variable contains an absolute
	 * filesystem path.
	 *
	 * @var   boolean
	 */
	protected $schemaXmlPathRelative = true;

	/**
	 * The path where the schema XML files are stored. Its contents depend on
the schemaXmlPathRelative variable above
	 * true        => schemaXmlPath contains a path relative to the
component's back-end directory
	 * false    => schemaXmlPath contains an absolute filesystem path
	 *
	 * @var string
	 */
	protected $schemaXmlPath = 'sql/xml';

	/**
	 * The minimum PHP version required to install this extension
	 *
	 * @var   string
	 */
	protected $minimumPHPVersion = '5.3.3';

	/**
	 * The minimum Joomla! version required to install this extension
	 *
	 * @var   string
	 */
	protected $minimumJoomlaVersion = '2.5.6';

	/**
	 * The maximum Joomla! version this extension can be installed on
	 *
	 * @var   string
	 */
	protected $maximumJoomlaVersion = '3.9.99';

	/**
	 * Is this the paid version of the extension? This only determines which
files / extensions will be removed.
	 *
	 * @var   boolean
	 */
	protected $isPaid = false;

	/**
	 * Post-installation message definitions for Joomla! 3.2 or later.
	 *
	 * This array contains the message definitions for the Post-installation
Messages component added in Joomla! 3.2 and
	 * later versions. Each element is also a hashed array. For the keys used
in these message definitions please
	 * @see FOFUtilsInstallscript::addPostInstallationMessage
	 *
	 * @var array
	 */
	protected $postInstallationMessages = array();

	/**
	 * Joomla! pre-flight event. This runs before Joomla! installs or updates
the component. This is our last chance to
	 * tell Joomla! if it should abort the installation.
	 *
	 * @param   string     $type   Installation type (install, update,
discover_install)
	 * @param   JInstaller $parent Parent object
	 *
	 * @return  boolean  True to let the installation proceed, false to halt
the installation
	 */
	public function preflight($type, $parent)
	{
		// Check the minimum PHP version
		if (!empty($this->minimumPHPVersion))
		{
			if (defined('PHP_VERSION'))
			{
				$version = PHP_VERSION;
			}
			elseif (function_exists('phpversion'))
			{
				$version = phpversion();
			}
			else
			{
				$version = '5.0.0'; // all bets are off!
			}

			if (!version_compare($version, $this->minimumPHPVersion,
'ge'))
			{
				$msg = "<p>You need PHP $this->minimumPHPVersion or later
to install this component</p>";

				if (version_compare(JVERSION, '3.0', 'gt'))
				{
					JLog::add($msg, JLog::WARNING, 'jerror');
				}
				else
				{
					JError::raiseWarning(100, $msg);
				}

				return false;
			}
		}

		// Check the minimum Joomla! version
		if (!empty($this->minimumJoomlaVersion) &&
!version_compare(JVERSION, $this->minimumJoomlaVersion, 'ge'))
		{
			$msg = "<p>You need Joomla! $this->minimumJoomlaVersion or
later to install this component</p>";

			if (version_compare(JVERSION, '3.0', 'gt'))
			{
				JLog::add($msg, JLog::WARNING, 'jerror');
			}
			else
			{
				JError::raiseWarning(100, $msg);
			}

			return false;
		}

		// Check the maximum Joomla! version
		if (!empty($this->maximumJoomlaVersion) &&
!version_compare(JVERSION, $this->maximumJoomlaVersion, 'le'))
		{
			$msg = "<p>You need Joomla! $this->maximumJoomlaVersion or
earlier to install this component</p>";

			if (version_compare(JVERSION, '3.0', 'gt'))
			{
				JLog::add($msg, JLog::WARNING, 'jerror');
			}
			else
			{
				JError::raiseWarning(100, $msg);
			}

			return false;
		}

		// Always reset the OPcache if it's enabled. Otherwise there's
a good chance the server will not know we are
		// replacing .php scripts. This is a major concern since PHP 5.5 included
and enabled OPcache by default.
		if (function_exists('opcache_reset'))
		{
			opcache_reset();
		}

		// Workarounds for JInstaller issues
		if (in_array($type, array('install',
'discover_install')))
		{
			// Bugfix for "Database function returned no error"
			$this->bugfixDBFunctionReturnedNoError();
		}
		else
		{
			// Bugfix for "Can not build admin menus"
			$this->bugfixCantBuildAdminMenus();
		}

		return true;
	}

	/**
	 * Runs after install, update or discover_update. In other words, it
executes after Joomla! has finished installing
	 * or updating your component. This is the last chance you've got to
perform any additional installations, clean-up,
	 * database updates and similar housekeeping functions.
	 *
	 * @param   string     $type   install, update or discover_update
	 * @param   JInstaller $parent Parent object
	 */
	public function postflight($type, $parent)
	{
		// Install or update database
		$dbInstaller = new FOFDatabaseInstaller(array(
			'dbinstaller_directory' =>
				($this->schemaXmlPathRelative ? JPATH_ADMINISTRATOR .
'/components/' . $this->componentName : '') .
'/' .
				$this->schemaXmlPath
		));
		$dbInstaller->updateSchema();

		// Install subextensions
		$status = $this->installSubextensions($parent);

		// Install FOF
		$fofInstallationStatus = $this->installFOF($parent);

		// Install Akeeba Straper
		$strapperInstallationStatus = $this->installStrapper($parent);

		// Make sure menu items are installed
		$this->_createAdminMenus($parent);

		// Make sure menu items are published (surprise goal in the 92' by
JInstaller wins the cup for "most screwed up
		// bug in the history of Joomla!")
		$this->_reallyPublishAdminMenuItems($parent);

		// Which files should I remove?
		if ($this->isPaid)
		{
			// This is the paid version, only remove the removeFilesAllVersions
files
			$removeFiles = $this->removeFilesAllVersions;
		}
		else
		{
			// This is the free version, remove the removeFilesAllVersions and
removeFilesFree files
			$removeFiles = array('files' => array(),
'folders' => array());

			if (isset($this->removeFilesAllVersions['files']))
			{
				if (isset($this->removeFilesFree['files']))
				{
					$removeFiles['files'] =
array_merge($this->removeFilesAllVersions['files'],
$this->removeFilesFree['files']);
				}
				else
				{
					$removeFiles['files'] =
$this->removeFilesAllVersions['files'];
				}
			}
			elseif (isset($this->removeFilesFree['files']))
			{
				$removeFiles['files'] =
$this->removeFilesFree['files'];
			}

			if (isset($this->removeFilesAllVersions['folders']))
			{
				if (isset($this->removeFilesFree['folders']))
				{
					$removeFiles['folders'] =
array_merge($this->removeFilesAllVersions['folders'],
$this->removeFilesFree['folders']);
				}
				else
				{
					$removeFiles['folders'] =
$this->removeFilesAllVersions['folders'];
				}
			}
			elseif (isset($this->removeFilesFree['folders']))
			{
				$removeFiles['folders'] =
$this->removeFilesFree['folders'];
			}
		}

		// Remove obsolete files and folders
		$this->removeFilesAndFolders($removeFiles);

		// Copy the CLI files (if any)
		$this->copyCliFiles($parent);

		// Show the post-installation page
		$this->renderPostInstallation($status, $fofInstallationStatus,
$strapperInstallationStatus, $parent);

		// Uninstall obsolete subextensions
		$uninstall_status = $this->uninstallObsoleteSubextensions($parent);

		// Clear the FOF cache
		$platform = FOFPlatform::getInstance();

		if (method_exists($platform, 'clearCache'))
		{
			FOFPlatform::getInstance()->clearCache();
		}

		// Make sure the Joomla! menu structure is correct
		$this->_rebuildMenu();

		// Add post-installation messages on Joomla! 3.2 and later
		$this->_applyPostInstallationMessages();
	}

	/**
	 * Runs on uninstallation
	 *
	 * @param   JInstaller $parent The parent object
	 */
	public function uninstall($parent)
	{
		// Uninstall database
		$dbInstaller = new FOFDatabaseInstaller(array(
			'dbinstaller_directory' =>
				($this->schemaXmlPathRelative ? JPATH_ADMINISTRATOR .
'/components/' . $this->componentName : '') .
'/' .
				$this->schemaXmlPath
		));
		$dbInstaller->removeSchema();

		// Uninstall modules and plugins
		$status = $this->uninstallSubextensions($parent);

		// Uninstall post-installation messages on Joomla! 3.2 and later
		$this->uninstallPostInstallationMessages();

		// Show the post-uninstallation page
		$this->renderPostUninstallation($status, $parent);
	}

	/**
	 * Copies the CLI scripts into Joomla!'s cli directory
	 *
	 * @param JInstaller $parent
	 */
	protected function copyCliFiles($parent)
	{
		$src = $parent->getParent()->getPath('source');

		$cliPath = JPATH_ROOT . '/cli';

		if (!JFolder::exists($cliPath))
		{
			JFolder::create($cliPath);
		}

		foreach ($this->cliScriptFiles as $script)
		{
			if (JFile::exists($cliPath . '/' . $script))
			{
				JFile::delete($cliPath . '/' . $script);
			}

			if (JFile::exists($src . '/' . $this->cliSourcePath .
'/' . $script))
			{
				JFile::copy($src . '/' . $this->cliSourcePath .
'/' . $script, $cliPath . '/' . $script);
			}
		}
	}

	/**
	 * Renders the message after installing or upgrading the component
	 */
	protected function renderPostInstallation($status, $fofInstallationStatus,
$strapperInstallationStatus, $parent)
	{
		$rows = 0;
		?>
		<table class="adminlist table table-striped"
width="100%">
			<thead>
			<tr>
				<th class="title"
colspan="2">Extension</th>
				<th width="30%">Status</th>
			</tr>
			</thead>
			<tfoot>
			<tr>
				<td colspan="3"></td>
			</tr>
			</tfoot>
			<tbody>
			<tr class="row<?php echo($rows++ % 2); ?>">
				<td class="key" colspan="2"><?php echo
$this->componentTitle ?></td>
				<td><strong style="color:
green">Installed</strong></td>
			</tr>
			<?php if ($fofInstallationStatus['required']): ?>
				<tr class="row<?php echo($rows++ % 2); ?>">
					<td class="key" colspan="2">
						<strong>Framework on Framework (FOF) <?php echo
$fofInstallationStatus['version'] ?></strong>
						[<?php echo $fofInstallationStatus['date'] ?>]
					</td>
					<td><strong>
							<span
								style="color: <?php echo
$fofInstallationStatus['required'] ?
($fofInstallationStatus['installed'] ? 'green' :
'red') : '#660' ?>; font-weight: bold;">
		<?php echo $fofInstallationStatus['required'] ?
($fofInstallationStatus['installed'] ? 'Installed' :
'Not Installed') : 'Already up-to-date'; ?>
							</span>
						</strong></td>
				</tr>
			<?php endif; ?>
			<?php if ($strapperInstallationStatus['required']): ?>
				<tr class="row<?php echo($rows++ % 2); ?>">
					<td class="key" colspan="2">
						<strong>Akeeba Strapper <?php echo
$strapperInstallationStatus['version'] ?></strong>
						[<?php echo $strapperInstallationStatus['date'] ?>]
					</td>
					<td><strong>
							<span
								style="color: <?php echo
$strapperInstallationStatus['required'] ?
($strapperInstallationStatus['installed'] ? 'green' :
'red') : '#660' ?>; font-weight: bold;">
				<?php echo $strapperInstallationStatus['required'] ?
($strapperInstallationStatus['installed'] ? 'Installed'
: 'Not Installed') : 'Already up-to-date'; ?>
							</span>
						</strong></td>
				</tr>
			<?php endif; ?>
			<?php if (count($status->modules)) : ?>
				<tr>
					<th>Module</th>
					<th>Client</th>
					<th></th>
				</tr>
				<?php foreach ($status->modules as $module) : ?>
					<tr class="row<?php echo($rows++ % 2); ?>">
						<td class="key"><?php echo
$module['name']; ?></td>
						<td class="key"><?php echo
ucfirst($module['client']); ?></td>
						<td><strong
								style="color: <?php echo ($module['result']) ?
"green" : "red" ?>"><?php echo
($module['result']) ? 'Installed' : 'Not
installed'; ?></strong>
						</td>
					</tr>
				<?php endforeach; ?>
			<?php endif; ?>
			<?php if (count($status->plugins)) : ?>
				<tr>
					<th>Plugin</th>
					<th>Group</th>
					<th></th>
				</tr>
				<?php foreach ($status->plugins as $plugin) : ?>
					<tr class="row<?php echo($rows++ % 2); ?>">
						<td class="key"><?php echo
ucfirst($plugin['name']); ?></td>
						<td class="key"><?php echo
ucfirst($plugin['group']); ?></td>
						<td><strong
								style="color: <?php echo ($plugin['result']) ?
"green" : "red" ?>"><?php echo
($plugin['result']) ? 'Installed' : 'Not
installed'; ?></strong>
						</td>
					</tr>
				<?php endforeach; ?>
			<?php endif; ?>
			</tbody>
		</table>
	<?php
	}

	/**
	 * Renders the message after uninstalling the component
	 */
	protected function renderPostUninstallation($status, $parent)
	{
		$rows = 1;
		?>
		<table class="adminlist table table-striped"
width="100%">
			<thead>
			<tr>
				<th class="title" colspan="2"><?php echo
JText::_('Extension'); ?></th>
				<th width="30%"><?php echo
JText::_('Status'); ?></th>
			</tr>
			</thead>
			<tfoot>
			<tr>
				<td colspan="3"></td>
			</tr>
			</tfoot>
			<tbody>
			<tr class="row<?php echo($rows++ % 2); ?>">
				<td class="key" colspan="2"><?php echo
$this->componentTitle; ?></td>
				<td><strong style="color:
green">Removed</strong></td>
			</tr>
			<?php if (count($status->modules)) : ?>
				<tr>
					<th>Module</th>
					<th>Client</th>
					<th></th>
				</tr>
				<?php foreach ($status->modules as $module) : ?>
					<tr class="row<?php echo($rows++ % 2); ?>">
						<td class="key"><?php echo
$module['name']; ?></td>
						<td class="key"><?php echo
ucfirst($module['client']); ?></td>
						<td><strong
								style="color: <?php echo ($module['result']) ?
"green" : "red" ?>"><?php echo
($module['result']) ? 'Removed' : 'Not
removed'; ?></strong>
						</td>
					</tr>
				<?php endforeach; ?>
			<?php endif; ?>
			<?php if (count($status->plugins)) : ?>
				<tr>
					<th>Plugin</th>
					<th>Group</th>
					<th></th>
				</tr>
				<?php foreach ($status->plugins as $plugin) : ?>
					<tr class="row<?php echo($rows++ % 2); ?>">
						<td class="key"><?php echo
ucfirst($plugin['name']); ?></td>
						<td class="key"><?php echo
ucfirst($plugin['group']); ?></td>
						<td><strong
								style="color: <?php echo ($plugin['result']) ?
"green" : "red" ?>"><?php echo
($plugin['result']) ? 'Removed' : 'Not
removed'; ?></strong>
						</td>
					</tr>
				<?php endforeach; ?>
			<?php endif; ?>
			</tbody>
		</table>
	<?php
	}

	/**
	 * Bugfix for "DB function returned no error"
	 */
	protected function bugfixDBFunctionReturnedNoError()
	{
		$db = FOFPlatform::getInstance()->getDbo();

		// Fix broken #__assets records
		$query = $db->getQuery(true);
		$query->select('id')
			->from('#__assets')
			->where($db->qn('name') . ' = ' .
$db->q($this->componentName));
		$db->setQuery($query);

		try
		{
			$ids = $db->loadColumn();
		}
		catch (Exception $exc)
		{
			return;
		}

		if (!empty($ids))
		{
			foreach ($ids as $id)
			{
				$query = $db->getQuery(true);
				$query->delete('#__assets')
					->where($db->qn('id') . ' = ' .
$db->q($id));
				$db->setQuery($query);

				try
				{
					$db->execute();
				}
				catch (Exception $exc)
				{
					// Nothing
				}
			}
		}

		// Fix broken #__extensions records
		$query = $db->getQuery(true);
		$query->select('extension_id')
			->from('#__extensions')
			->where($db->qn('type') . ' = ' .
$db->q('component'))
			->where($db->qn('element') . ' = ' .
$db->q($this->componentName));
		$db->setQuery($query);
		$ids = $db->loadColumn();

		if (!empty($ids))
		{
			foreach ($ids as $id)
			{
				$query = $db->getQuery(true);
				$query->delete('#__extensions')
					->where($db->qn('extension_id') . ' = ' .
$db->q($id));
				$db->setQuery($query);

				try
				{
					$db->execute();
				}
				catch (Exception $exc)
				{
					// Nothing
				}
			}
		}

		// Fix broken #__menu records
		$query = $db->getQuery(true);
		$query->select('id')
			->from('#__menu')
			->where($db->qn('type') . ' = ' .
$db->q('component'))
			->where($db->qn('menutype') . ' = ' .
$db->q('main'))
			->where($db->qn('link') . ' LIKE ' .
$db->q('index.php?option=' . $this->componentName));
		$db->setQuery($query);
		$ids = $db->loadColumn();

		if (!empty($ids))
		{
			foreach ($ids as $id)
			{
				$query = $db->getQuery(true);
				$query->delete('#__menu')
					->where($db->qn('id') . ' = ' .
$db->q($id));
				$db->setQuery($query);

				try
				{
					$db->execute();
				}
				catch (Exception $exc)
				{
					// Nothing
				}
			}
		}
	}

	/**
	 * Joomla! 1.6+ bugfix for "Can not build admin menus"
	 */
	protected function bugfixCantBuildAdminMenus()
	{
		$db = FOFPlatform::getInstance()->getDbo();

		// If there are multiple #__extensions record, keep one of them
		$query = $db->getQuery(true);
		$query->select('extension_id')
			->from('#__extensions')
			->where($db->qn('type') . ' = ' .
$db->q('component'))
			->where($db->qn('element') . ' = ' .
$db->q($this->componentName));
		$db->setQuery($query);

		try
		{
			$ids = $db->loadColumn();
		}
		catch (Exception $exc)
		{
			return;
		}


		if (count($ids) > 1)
		{
			asort($ids);
			$extension_id = array_shift($ids); // Keep the oldest id

			foreach ($ids as $id)
			{
				$query = $db->getQuery(true);
				$query->delete('#__extensions')
					->where($db->qn('extension_id') . ' = ' .
$db->q($id));
				$db->setQuery($query);

				try
				{
					$db->execute();
				}
				catch (Exception $exc)
				{
					// Nothing
				}
			}
		}

		// If there are multiple assets records, delete all except the oldest one
		$query = $db->getQuery(true);
		$query->select('id')
			->from('#__assets')
			->where($db->qn('name') . ' = ' .
$db->q($this->componentName));
		$db->setQuery($query);
		$ids = $db->loadObjectList();

		if (count($ids) > 1)
		{
			asort($ids);
			$asset_id = array_shift($ids); // Keep the oldest id

			foreach ($ids as $id)
			{
				$query = $db->getQuery(true);
				$query->delete('#__assets')
					->where($db->qn('id') . ' = ' .
$db->q($id));
				$db->setQuery($query);

				try
				{
					$db->execute();
				}
				catch (Exception $exc)
				{
					// Nothing
				}
			}
		}

		// Remove #__menu records for good measure! –– I think this is not
necessary and causes the menu item to
		// disappear on extension update.
		/**
		$query = $db->getQuery(true);
		$query->select('id')
			->from('#__menu')
			->where($db->qn('type') . ' = ' .
$db->q('component'))
			->where($db->qn('menutype') . ' = ' .
$db->q('main'))
			->where($db->qn('link') . ' LIKE ' .
$db->q('index.php?option=' . $this->componentName));
		$db->setQuery($query);

		try
		{
			$ids1 = $db->loadColumn();
		}
		catch (Exception $exc)
		{
			$ids1 = array();
		}

		if (empty($ids1))
		{
			$ids1 = array();
		}

		$query = $db->getQuery(true);
		$query->select('id')
			->from('#__menu')
			->where($db->qn('type') . ' = ' .
$db->q('component'))
			->where($db->qn('menutype') . ' = ' .
$db->q('main'))
			->where($db->qn('link') . ' LIKE ' .
$db->q('index.php?option=' . $this->componentName .
'&%'));
		$db->setQuery($query);

		try
		{
			$ids2 = $db->loadColumn();
		}
		catch (Exception $exc)
		{
			$ids2 = array();
		}

		if (empty($ids2))
		{
			$ids2 = array();
		}

		$ids = array_merge($ids1, $ids2);

		if (!empty($ids))
		{
			foreach ($ids as $id)
			{
				$query = $db->getQuery(true);
				$query->delete('#__menu')
					->where($db->qn('id') . ' = ' .
$db->q($id));
				$db->setQuery($query);

				try
				{
					$db->execute();
				}
				catch (Exception $exc)
				{
					// Nothing
				}
			}
		}
		/**/
	}

	/**
	 * Installs subextensions (modules, plugins) bundled with the main
extension
	 *
	 * @param JInstaller $parent
	 *
	 * @return JObject The subextension installation status
	 */
	protected function installSubextensions($parent)
	{
		$src = $parent->getParent()->getPath('source');

		$db = FOFPlatform::getInstance()->getDbo();;

		$status = new JObject();
		$status->modules = array();
		$status->plugins = array();

		// Modules installation
		if (isset($this->installation_queue['modules']) &&
count($this->installation_queue['modules']))
		{
			foreach ($this->installation_queue['modules'] as $folder
=> $modules)
			{
				if (count($modules))
				{
					foreach ($modules as $module => $modulePreferences)
					{
						// Install the module
						if (empty($folder))
						{
							$folder = 'site';
						}

						$path = "$src/" . $this->modulesSourcePath .
"/$folder/$module";

						if (!is_dir($path))
						{
							$path = "$src/" . $this->modulesSourcePath .
"/$folder/mod_$module";
						}

						if (!is_dir($path))
						{
							$path = "$src/" . $this->modulesSourcePath .
"/$module";
						}

						if (!is_dir($path))
						{
							$path = "$src/" . $this->modulesSourcePath .
"/mod_$module";
						}

						if (!is_dir($path))
						{
							continue;
						}

						// Was the module already installed?
						$sql = $db->getQuery(true)
							->select('COUNT(*)')
							->from('#__modules')
							->where($db->qn('module') . ' = ' .
$db->q('mod_' . $module));
						$db->setQuery($sql);

						try
						{
							$count = $db->loadResult();
						}
						catch (Exception $exc)
						{
							$count = 0;
						}

						$installer = new JInstaller;
						$result = $installer->install($path);
						$status->modules[] = array(
							'name'   => 'mod_' . $module,
							'client' => $folder,
							'result' => $result
						);

						// Modify where it's published and its published state
						if (!$count)
						{
							// A. Position and state
							list($modulePosition, $modulePublished) = $modulePreferences;

							$sql = $db->getQuery(true)
								->update($db->qn('#__modules'))
								->set($db->qn('position') . ' = ' .
$db->q($modulePosition))
								->where($db->qn('module') . ' = ' .
$db->q('mod_' . $module));

							if ($modulePublished)
							{
								$sql->set($db->qn('published') . ' = ' .
$db->q('1'));
							}

							$db->setQuery($sql);

							try
							{
								$db->execute();
							}
							catch (Exception $exc)
							{
								// Nothing
							}

							// B. Change the ordering of back-end modules to 1 + max ordering
							if ($folder == 'admin')
							{
								try
								{
									$query = $db->getQuery(true);
									$query->select('MAX(' .
$db->qn('ordering') . ')')
										->from($db->qn('#__modules'))
										->where($db->qn('position') . '=' .
$db->q($modulePosition));
									$db->setQuery($query);
									$position = $db->loadResult();
									$position++;

									$query = $db->getQuery(true);
									$query->update($db->qn('#__modules'))
										->set($db->qn('ordering') . ' = ' .
$db->q($position))
										->where($db->qn('module') . ' = ' .
$db->q('mod_' . $module));
									$db->setQuery($query);
									$db->execute();
								}
								catch (Exception $exc)
								{
									// Nothing
								}
							}

							// C. Link to all pages
							try
							{
								$query = $db->getQuery(true);
								$query->select('id')->from($db->qn('#__modules'))
									->where($db->qn('module') . ' = ' .
$db->q('mod_' . $module));
								$db->setQuery($query);
								$moduleid = $db->loadResult();

								$query = $db->getQuery(true);
								$query->select('*')->from($db->qn('#__modules_menu'))
									->where($db->qn('moduleid') . ' = ' .
$db->q($moduleid));
								$db->setQuery($query);
								$assignments = $db->loadObjectList();
								$isAssigned = !empty($assignments);

								if (!$isAssigned)
								{
									$o = (object)array(
										'moduleid' => $moduleid,
										'menuid'   => 0
									);
									$db->insertObject('#__modules_menu', $o);
								}
							}
							catch (Exception $exc)
							{
								// Nothing
							}
						}
					}
				}
			}
		}

		// Plugins installation
		if (isset($this->installation_queue['plugins']) &&
count($this->installation_queue['plugins']))
		{
			foreach ($this->installation_queue['plugins'] as $folder
=> $plugins)
			{
				if (count($plugins))
				{
					foreach ($plugins as $plugin => $published)
					{
						$path = "$src/" . $this->pluginsSourcePath .
"/$folder/$plugin";

						if (!is_dir($path))
						{
							$path = "$src/" . $this->pluginsSourcePath .
"/$folder/plg_$plugin";
						}

						if (!is_dir($path))
						{
							$path = "$src/" . $this->pluginsSourcePath .
"/$plugin";
						}

						if (!is_dir($path))
						{
							$path = "$src/" . $this->pluginsSourcePath .
"/plg_$plugin";
						}

						if (!is_dir($path))
						{
							continue;
						}

						// Was the plugin already installed?
						$query = $db->getQuery(true)
							->select('COUNT(*)')
							->from($db->qn('#__extensions'))
							->where($db->qn('element') . ' = ' .
$db->q($plugin))
							->where($db->qn('folder') . ' = ' .
$db->q($folder));
						$db->setQuery($query);

						try
						{
							$count = $db->loadResult();
						}
						catch (Exception $exc)
						{
							$count = 0;
						}

						$installer = new JInstaller;
						$result = $installer->install($path);

						$status->plugins[] = array('name' => 'plg_'
. $plugin, 'group' => $folder, 'result' =>
$result);

						if ($published && !$count)
						{
							$query = $db->getQuery(true)
								->update($db->qn('#__extensions'))
								->set($db->qn('enabled') . ' = ' .
$db->q('1'))
								->where($db->qn('element') . ' = ' .
$db->q($plugin))
								->where($db->qn('folder') . ' = ' .
$db->q($folder));
							$db->setQuery($query);

							try
							{
								$db->execute();
							}
							catch (Exception $exc)
							{
								// Nothing
							}
						}
					}
				}
			}
		}

		// Clear com_modules and com_plugins cache (needed when we alter
module/plugin state)
		FOFUtilsCacheCleaner::clearPluginsAndModulesCache();

		return $status;
	}

	/**
	 * Uninstalls subextensions (modules, plugins) bundled with the main
extension
	 *
	 * @param   JInstaller $parent The parent object
	 *
	 * @return  stdClass  The subextension uninstallation status
	 */
	protected function uninstallSubextensions($parent)
	{
		$db = FOFPlatform::getInstance()->getDbo();

		$status = new stdClass();
		$status->modules = array();
		$status->plugins = array();

		$src = $parent->getParent()->getPath('source');

		// Modules uninstallation
		if (isset($this->installation_queue['modules']) &&
count($this->installation_queue['modules']))
		{
			foreach ($this->installation_queue['modules'] as $folder
=> $modules)
			{
				if (count($modules))
				{
					foreach ($modules as $module => $modulePreferences)
					{
						// Find the module ID
						$sql = $db->getQuery(true)
							->select($db->qn('extension_id'))
							->from($db->qn('#__extensions'))
							->where($db->qn('element') . ' = ' .
$db->q('mod_' . $module))
							->where($db->qn('type') . ' = ' .
$db->q('module'));
						$db->setQuery($sql);

						try
						{
							$id = $db->loadResult();
						}
						catch (Exception $exc)
						{
							$id = 0;
						}

						// Uninstall the module
						if ($id)
						{
							$installer = new JInstaller;
							$result = $installer->uninstall('module', $id, 1);
							$status->modules[] = array(
								'name'   => 'mod_' . $module,
								'client' => $folder,
								'result' => $result
							);
						}
					}
				}
			}
		}

		// Plugins uninstallation
		if (isset($this->installation_queue['plugins']) &&
count($this->installation_queue['plugins']))
		{
			foreach ($this->installation_queue['plugins'] as $folder
=> $plugins)
			{
				if (count($plugins))
				{
					foreach ($plugins as $plugin => $published)
					{
						$sql = $db->getQuery(true)
							->select($db->qn('extension_id'))
							->from($db->qn('#__extensions'))
							->where($db->qn('type') . ' = ' .
$db->q('plugin'))
							->where($db->qn('element') . ' = ' .
$db->q($plugin))
							->where($db->qn('folder') . ' = ' .
$db->q($folder));
						$db->setQuery($sql);

						try
						{
							$id = $db->loadResult();
						}
						catch (Exception $exc)
						{
							$id = 0;
						}

						if ($id)
						{
							$installer = new JInstaller;
							$result = $installer->uninstall('plugin', $id, 1);
							$status->plugins[] = array(
								'name'   => 'plg_' . $plugin,
								'group'  => $folder,
								'result' => $result
							);
						}
					}
				}
			}
		}

		// Clear com_modules and com_plugins cache (needed when we alter
module/plugin state)
		FOFUtilsCacheCleaner::clearPluginsAndModulesCache();

		return $status;
	}

	/**
	 * Removes obsolete files and folders
	 *
	 * @param   array $removeList The files and directories to remove
	 */
	protected function removeFilesAndFolders($removeList)
	{
		// Remove files
		if (isset($removeList['files']) &&
!empty($removeList['files']))
		{
			foreach ($removeList['files'] as $file)
			{
				$f = JPATH_ROOT . '/' . $file;

				if (!JFile::exists($f))
				{
					continue;
				}

				JFile::delete($f);
			}
		}

		// Remove folders
		if (isset($removeList['folders']) &&
!empty($removeList['folders']))
		{
			foreach ($removeList['folders'] as $folder)
			{
				$f = JPATH_ROOT . '/' . $folder;

				if (!JFolder::exists($f))
				{
					continue;
				}

				JFolder::delete($f);
			}
		}
	}

	/**
	 * Installs FOF if necessary
	 *
	 * @param   JInstaller $parent The parent object
	 *
	 * @return  array  The installation status
	 */
	protected function installFOF($parent)
	{
		// Get the source path
		$src = $parent->getParent()->getPath('source');
		$source = $src . '/' . $this->fofSourcePath;

		if (!JFolder::exists($source))
		{
			return array(
				'required'  => false,
				'installed' => false,
				'version'   => '0.0.0',
				'date'      => '2011-01-01',
			);
		}

		// Get the target path
		if (!defined('JPATH_LIBRARIES'))
		{
			$target = JPATH_ROOT . '/libraries/f0f';
		}
		else
		{
			$target = JPATH_LIBRARIES . '/f0f';
		}

		// Do I have to install FOF?
		$haveToInstallFOF = false;

		if (!JFolder::exists($target))
		{
			// FOF is not installed; install now
			$haveToInstallFOF = true;
		}
		else
		{
			// FOF is already installed; check the version
			$fofVersion = array();

			if (JFile::exists($target . '/version.txt'))
			{
				$rawData = JFile::read($target . '/version.txt');
				$rawData = ($rawData === false) ? "0.0.0\n2011-01-01\n" :
$rawData;
				$info = explode("\n", $rawData);
				$fofVersion['installed'] = array(
					'version' => trim($info[0]),
					'date'    => new JDate(trim($info[1]))
				);
			}
			else
			{
				$fofVersion['installed'] = array(
					'version' => '0.0',
					'date'    => new JDate('2011-01-01')
				);
			}

			$rawData = @file_get_contents($source . '/version.txt');
			$rawData = ($rawData === false) ? "0.0.0\n2011-01-01\n" :
$rawData;
			$info = explode("\n", $rawData);

			$fofVersion['package'] = array(
				'version' => trim($info[0]),
				'date'    => new JDate(trim($info[1]))
			);

			$haveToInstallFOF =
$fofVersion['package']['date']->toUNIX() >
$fofVersion['installed']['date']->toUNIX();
		}

		$installedFOF = false;

		if ($haveToInstallFOF)
		{
			$versionSource = 'package';
			$installer = new JInstaller;
			$installedFOF = $installer->install($source);
		}
		else
		{
			$versionSource = 'installed';
		}

		if (!isset($fofVersion))
		{
			$fofVersion = array();

			if (JFile::exists($target . '/version.txt'))
			{
				$rawData = @file_get_contents($source . '/version.txt');
				$rawData = ($rawData === false) ? "0.0.0\n2011-01-01\n" :
$rawData;
				$info = explode("\n", $rawData);
				$fofVersion['installed'] = array(
					'version' => trim($info[0]),
					'date'    => new JDate(trim($info[1]))
				);
			}
			else
			{
				$fofVersion['installed'] = array(
					'version' => '0.0',
					'date'    => new JDate('2011-01-01')
				);
			}

			$rawData = @file_get_contents($source . '/version.txt');
			$rawData = ($rawData === false) ? "0.0.0\n2011-01-01\n" :
$rawData;
			$info = explode("\n", $rawData);

			$fofVersion['package'] = array(
				'version' => trim($info[0]),
				'date'    => new JDate(trim($info[1]))
			);

			$versionSource = 'installed';
		}

		if (!($fofVersion[$versionSource]['date'] instanceof JDate))
		{
			$fofVersion[$versionSource]['date'] = new JDate();
		}

		return array(
			'required'  => $haveToInstallFOF,
			'installed' => $installedFOF,
			'version'   =>
$fofVersion[$versionSource]['version'],
			'date'      =>
$fofVersion[$versionSource]['date']->format('Y-m-d'),
		);
	}

	/**
	 * Installs Akeeba Strapper if necessary
	 *
	 * @param   JInstaller $parent The parent object
	 *
	 * @return  array  The installation status
	 */
	protected function installStrapper($parent)
	{
		$src = $parent->getParent()->getPath('source');
		$source = $src . '/' . $this->strapperSourcePath;

		$target = JPATH_ROOT . '/media/akeeba_strapper';

		if (!JFolder::exists($source))
		{
			return array(
				'required'  => false,
				'installed' => false,
				'version'   => '0.0.0',
				'date'      => '2011-01-01',
			);
		}

		$haveToInstallStrapper = false;

		if (!JFolder::exists($target))
		{
			$haveToInstallStrapper = true;
		}
		else
		{
			$strapperVersion = array();

			if (JFile::exists($target . '/version.txt'))
			{
				$rawData = JFile::read($target . '/version.txt');
				$rawData = ($rawData === false) ? "0.0.0\n2011-01-01\n" :
$rawData;
				$info = explode("\n", $rawData);
				$strapperVersion['installed'] = array(
					'version' => trim($info[0]),
					'date'    => new JDate(trim($info[1]))
				);
			}
			else
			{
				$strapperVersion['installed'] = array(
					'version' => '0.0',
					'date'    => new JDate('2011-01-01')
				);
			}

			$rawData = JFile::read($source . '/version.txt');
			$rawData = ($rawData === false) ? "0.0.0\n2011-01-01\n" :
$rawData;
			$info = explode("\n", $rawData);
			$strapperVersion['package'] = array(
				'version' => trim($info[0]),
				'date'    => new JDate(trim($info[1]))
			);

			$haveToInstallStrapper =
$strapperVersion['package']['date']->toUNIX() >
$strapperVersion['installed']['date']->toUNIX();
		}

		$installedStraper = false;

		if ($haveToInstallStrapper)
		{
			$versionSource = 'package';
			$installer = new JInstaller;
			$installedStraper = $installer->install($source);
		}
		else
		{
			$versionSource = 'installed';
		}

		if (!isset($strapperVersion))
		{
			$strapperVersion = array();

			if (JFile::exists($target . '/version.txt'))
			{
				$rawData = JFile::read($target . '/version.txt');
				$rawData = ($rawData === false) ? "0.0.0\n2011-01-01\n" :
$rawData;
				$info = explode("\n", $rawData);
				$strapperVersion['installed'] = array(
					'version' => trim($info[0]),
					'date'    => new JDate(trim($info[1]))
				);
			}
			else
			{
				$strapperVersion['installed'] = array(
					'version' => '0.0',
					'date'    => new JDate('2011-01-01')
				);
			}

			$rawData = JFile::read($source . '/version.txt');
			$rawData = ($rawData === false) ? "0.0.0\n2011-01-01\n" :
$rawData;
			$info = explode("\n", $rawData);

			$strapperVersion['package'] = array(
				'version' => trim($info[0]),
				'date'    => new JDate(trim($info[1]))
			);

			$versionSource = 'installed';
		}

		if (!($strapperVersion[$versionSource]['date'] instanceof
JDate))
		{
			$strapperVersion[$versionSource]['date'] = new JDate();
		}

		return array(
			'required'  => $haveToInstallStrapper,
			'installed' => $installedStraper,
			'version'   =>
$strapperVersion[$versionSource]['version'],
			'date'      =>
$strapperVersion[$versionSource]['date']->format('Y-m-d'),
		);
	}

	/**
	 * Uninstalls obsolete subextensions (modules, plugins) bundled with the
main extension
	 *
	 * @param   JInstaller $parent The parent object
	 *
	 * @return  stdClass The subextension uninstallation status
	 */
	protected function uninstallObsoleteSubextensions($parent)
	{
		JLoader::import('joomla.installer.installer');

		$db = FOFPlatform::getInstance()->getDbo();

		$status = new stdClass();
		$status->modules = array();
		$status->plugins = array();

		$src = $parent->getParent()->getPath('source');

		// Modules uninstallation
		if (isset($this->uninstallation_queue['modules']) &&
count($this->uninstallation_queue['modules']))
		{
			foreach ($this->uninstallation_queue['modules'] as $folder
=> $modules)
			{
				if (count($modules))
				{
					foreach ($modules as $module)
					{
						// Find the module ID
						$sql = $db->getQuery(true)
							->select($db->qn('extension_id'))
							->from($db->qn('#__extensions'))
							->where($db->qn('element') . ' = ' .
$db->q('mod_' . $module))
							->where($db->qn('type') . ' = ' .
$db->q('module'));
						$db->setQuery($sql);
						$id = $db->loadResult();
						// Uninstall the module
						if ($id)
						{
							$installer = new JInstaller;
							$result = $installer->uninstall('module', $id, 1);
							$status->modules[] = array(
								'name'   => 'mod_' . $module,
								'client' => $folder,
								'result' => $result
							);
						}
					}
				}
			}
		}

		// Plugins uninstallation
		if (isset($this->uninstallation_queue['plugins']) &&
count($this->uninstallation_queue['plugins']))
		{
			foreach ($this->uninstallation_queue['plugins'] as $folder
=> $plugins)
			{
				if (count($plugins))
				{
					foreach ($plugins as $plugin)
					{
						$sql = $db->getQuery(true)
							->select($db->qn('extension_id'))
							->from($db->qn('#__extensions'))
							->where($db->qn('type') . ' = ' .
$db->q('plugin'))
							->where($db->qn('element') . ' = ' .
$db->q($plugin))
							->where($db->qn('folder') . ' = ' .
$db->q($folder));
						$db->setQuery($sql);

						$id = $db->loadResult();
						if ($id)
						{
							$installer = new JInstaller;
							$result = $installer->uninstall('plugin', $id, 1);
							$status->plugins[] = array(
								'name'   => 'plg_' . $plugin,
								'group'  => $folder,
								'result' => $result
							);
						}
					}
				}
			}
		}

		return $status;
	}

	/**
	 * @param JInstallerAdapterComponent $parent
	 *
	 * @return bool
	 *
	 * @throws Exception When the Joomla! menu is FUBAR
	 */
	private function _createAdminMenus($parent)
	{
		$db = $db = FOFPlatform::getInstance()->getDbo();

		/** @var JTableMenu $table */
		$table = JTable::getInstance('menu');
		$option = $parent->get('element');

		// If a component exists with this option in the table then we don't
need to add menus - check only 'main' menutype
		$query = $db->getQuery(true)
			->select('m.id, e.extension_id')
			->from('#__menu AS m')
			->join('LEFT', '#__extensions AS e ON m.component_id =
e.extension_id')
			->where('m.parent_id = 1')
			->where('m.client_id = 1')
			->where('m.menutype = ' . $db->q('main'))
			->where($db->qn('e') . '.' .
$db->qn('type') . ' = ' .
$db->q('component'))
			->where('e.element = ' . $db->quote($option));

		$db->setQuery($query);

		$componentrow = $db->loadObject();

		// Check if menu items exist
		if ($componentrow)
		{
			// @todo Return if the menu item already exists to save some time
			//return true;
		}

		// Let's find the extension id
		$query->clear()
			->select('e.extension_id')
			->from('#__extensions AS e')
			->where('e.type = ' . $db->quote('component'))
			->where('e.element = ' . $db->quote($option));
		$db->setQuery($query);
		$component_id = $db->loadResult();

		// Ok, now its time to handle the menus.  Start with the component root
menu, then handle submenus.
		$menuElement =
$parent->get('manifest')->administration->menu;

		// We need to insert the menu item as the last child of Joomla!'s
menu root node. By default this is the
		// menu item with ID=1. However, some crappy upgrade scripts enjoy
screwing it up. Hey, ho, the workaround
		// way I go.
		$query = $db->getQuery(true)
			->select($db->qn('id'))
			->from($db->qn('#__menu'))
			->where($db->qn('id') . ' = ' . $db->q(1));
		$rootItemId = $db->setQuery($query)->loadResult();

		if (is_null($rootItemId))
		{
			// Guess what? The Problem has happened. Let's find the root node
by title.
			$rootItemId = null;
			$query = $db->getQuery(true)
				->select($db->qn('id'))
				->from($db->qn('#__menu'))
				->where($db->qn('title') . ' = ' .
$db->q('Menu_Item_Root'));
			$rootItemId = $db->setQuery($query, 0, 1)->loadResult();
		}

		if (is_null($rootItemId))
		{
			// For crying out loud, did that idiot changed the title too?!
Let's find it by alias.
			$rootItemId = null;
			$query = $db->getQuery(true)
				->select($db->qn('id'))
				->from($db->qn('#__menu'))
				->where($db->qn('alias') . ' = ' .
$db->q('root'));
			$rootItemId = $db->setQuery($query, 0, 1)->loadResult();
		}

		if (is_null($rootItemId))
		{
			// Dude. Dude! Duuuuuuude! The alias is screwed up, too?! Find it by
component ID.
			$rootItemId = null;
			$query = $db->getQuery(true)
				->select($db->qn('id'))
				->from($db->qn('#__menu'))
				->where($db->qn('component_id') . ' = ' .
$db->q('0'));
			$rootItemId = $db->setQuery($query, 0, 1)->loadResult();
		}

		if (is_null($rootItemId))
		{
			// Your site is more of a "shite" than a "site".
Let's try with minimum lft value.
			$rootItemId = null;
			$query = $db->getQuery(true)
				->select($db->qn('id'))
				->from($db->qn('#__menu'))
				->order($db->qn('lft') . ' ASC');
			$rootItemId = $db->setQuery($query, 0, 1)->loadResult();
		}

		if (is_null($rootItemId))
		{
			// I quit. Your site is broken. What the hell are you doing with it?
I'll just throw an error.
			throw new Exception("Your site is broken. There is no root menu
item. As a result it is impossible to create menu items. The installation
of this component has failed. Please fix your database and retry!",
500);
		}

		if ($menuElement)
		{
			$data = array();
			$data['menutype'] = 'main';
			$data['client_id'] = 1;
			$data['title'] = (string)trim($menuElement);
			$data['alias'] = (string)$menuElement;
			$data['link'] = 'index.php?option=' . $option;
			$data['type'] = 'component';
			$data['published'] = 0;
			$data['parent_id'] = 1;
			$data['component_id'] = $component_id;
			$data['img'] = ((string)$menuElement->attributes()->img)
? (string)$menuElement->attributes()->img :
'class:component';
			$data['home'] = 0;
			$data['path'] = '';
			$data['params'] = '';
		}
		// No menu element was specified, Let's make a generic menu item
		else
		{
			$data = array();
			$data['menutype'] = 'main';
			$data['client_id'] = 1;
			$data['title'] = $option;
			$data['alias'] = $option;
			$data['link'] = 'index.php?option=' . $option;
			$data['type'] = 'component';
			$data['published'] = 0;
			$data['parent_id'] = 1;
			$data['component_id'] = $component_id;
			$data['img'] = 'class:component';
			$data['home'] = 0;
			$data['path'] = '';
			$data['params'] = '';
		}

		try
		{
			$table->setLocation($rootItemId, 'last-child');
		}
		catch (InvalidArgumentException $e)
		{
			if (class_exists('JLog'))
			{
				JLog::add($e->getMessage(), JLog::WARNING, 'jerror');
			}

			return false;
		}

		if (!$table->bind($data) || !$table->check() ||
!$table->store())
		{
			// The menu item already exists. Delete it and retry instead of throwing
an error.
			$query->clear()
				->select('id')
				->from('#__menu')
				->where('menutype = ' . $db->quote('main'))
				->where('client_id = 1')
				->where('link = ' .
$db->quote('index.php?option=' . $option))
				->where('type = ' . $db->quote('component'))
				->where('parent_id = 1')
				->where('home = 0');

			$db->setQuery($query);
			$menu_ids_level1 = $db->loadColumn();

			if (empty($menu_ids_level1))
			{
				// Oops! Could not get the menu ID. Go back and rollback changes.
				JError::raiseWarning(1, $table->getError());

				return false;
			}
			else
			{
				$ids = implode(',', $menu_ids_level1);

				$query->clear()
					->select('id')
					->from('#__menu')
					->where('menutype = ' . $db->quote('main'))
					->where('client_id = 1')
					->where('type = ' . $db->quote('component'))
					->where('parent_id in (' . $ids . ')')
					->where('level = 2')
					->where('home = 0');

				$db->setQuery($query);
				$menu_ids_level2 = $db->loadColumn();

				$ids = implode(',', array_merge($menu_ids_level1,
$menu_ids_level2));

				// Remove the old menu item
				$query->clear()
					->delete('#__menu')
					->where('id in (' . $ids . ')');

				$db->setQuery($query);
				$db->query();

				// Retry creating the menu item
				$table->setLocation($rootItemId, 'last-child');

				if (!$table->bind($data) || !$table->check() ||
!$table->store())
				{
					// Install failed, warn user and rollback changes
					JError::raiseWarning(1, $table->getError());

					return false;
				}
			}
		}

		/*
		 * Since we have created a menu item, we add it to the installation step
stack
		 * so that if we have to rollback the changes we can undo it.
		 */
		$parent->getParent()->pushStep(array('type' =>
'menu', 'id' => $component_id));

		/*
		 * Process SubMenus
		 */

		if
(!$parent->get('manifest')->administration->submenu)
		{
			return true;
		}

		$parent_id = $table->id;

		foreach
($parent->get('manifest')->administration->submenu->menu
as $child)
		{
			$data = array();
			$data['menutype'] = 'main';
			$data['client_id'] = 1;
			$data['title'] = (string)trim($child);
			$data['alias'] = (string)$child;
			$data['type'] = 'component';
			$data['published'] = 0;
			$data['parent_id'] = $parent_id;
			$data['component_id'] = $component_id;
			$data['img'] = ((string)$child->attributes()->img) ?
(string)$child->attributes()->img : 'class:component';
			$data['home'] = 0;

			// Set the sub menu link
			if ((string)$child->attributes()->link)
			{
				$data['link'] = 'index.php?' .
$child->attributes()->link;
			}
			else
			{
				$request = array();

				if ((string)$child->attributes()->act)
				{
					$request[] = 'act=' . $child->attributes()->act;
				}

				if ((string)$child->attributes()->task)
				{
					$request[] = 'task=' . $child->attributes()->task;
				}

				if ((string)$child->attributes()->controller)
				{
					$request[] = 'controller=' .
$child->attributes()->controller;
				}

				if ((string)$child->attributes()->view)
				{
					$request[] = 'view=' . $child->attributes()->view;
				}

				if ((string)$child->attributes()->layout)
				{
					$request[] = 'layout=' . $child->attributes()->layout;
				}

				if ((string)$child->attributes()->sub)
				{
					$request[] = 'sub=' . $child->attributes()->sub;
				}

				$qstring = (count($request)) ? '&' .
implode('&', $request) : '';
				$data['link'] = 'index.php?option=' . $option .
$qstring;
			}

			$table = JTable::getInstance('menu');

			try
			{
				$table->setLocation($parent_id, 'last-child');
			}
			catch (InvalidArgumentException $e)
			{
				return false;
			}

			if (!$table->bind($data) || !$table->check() ||
!$table->store())
			{
				// Install failed, rollback changes
				return false;
			}

			/*
			 * Since we have created a menu item, we add it to the installation step
stack
			 * so that if we have to rollback the changes we can undo it.
			 */
			$parent->getParent()->pushStep(array('type' =>
'menu', 'id' => $component_id));
		}

		return true;
	}

	/**
	 * Make sure the Component menu items are really published!
	 *
	 * @param JInstallerAdapterComponent $parent
	 *
	 * @return bool
	 */
	private function _reallyPublishAdminMenuItems($parent)
	{
		$db = FOFPlatform::getInstance()->getDbo();

		$option = $parent->get('element');

		$query = $db->getQuery(true)
			->update('#__menu AS m')
			->join('LEFT', '#__extensions AS e ON m.component_id =
e.extension_id')
			->set($db->qn('published') . ' = ' .
$db->q(1))
			->where('m.parent_id = 1')
			->where('m.client_id = 1')
			->where('m.menutype = ' . $db->quote('main'))
			->where('e.type = ' . $db->quote('component'))
			->where('e.element = ' . $db->quote($option));

		$db->setQuery($query);

		try
		{
			$db->execute();
		}
		catch (Exception $e)
		{
			// If it fails, it fails. Who cares.
		}
	}

	/**
	 * Tells Joomla! to rebuild its menu structure to make triple-sure that
the Components menu items really do exist
	 * in the correct place and can really be rendered.
	 */
	private function _rebuildMenu()
	{
		/** @var JTableMenu $table */
		$table = JTable::getInstance('menu');
		$db = FOFPlatform::getInstance()->getDbo();

		// We need to rebuild the menu based on its root item. By default this is
the menu item with ID=1. However, some
		// crappy upgrade scripts enjoy screwing it up. Hey, ho, the workaround
way I go.
		$query = $db->getQuery(true)
			->select($db->qn('id'))
			->from($db->qn('#__menu'))
			->where($db->qn('id') . ' = ' . $db->q(1));
		$rootItemId = $db->setQuery($query)->loadResult();

		if (is_null($rootItemId))
		{
			// Guess what? The Problem has happened. Let's find the root node
by title.
			$rootItemId = null;
			$query = $db->getQuery(true)
				->select($db->qn('id'))
				->from($db->qn('#__menu'))
				->where($db->qn('title') . ' = ' .
$db->q('Menu_Item_Root'));
			$rootItemId = $db->setQuery($query, 0, 1)->loadResult();
		}

		if (is_null($rootItemId))
		{
			// For crying out loud, did that idiot changed the title too?!
Let's find it by alias.
			$rootItemId = null;
			$query = $db->getQuery(true)
				->select($db->qn('id'))
				->from($db->qn('#__menu'))
				->where($db->qn('alias') . ' = ' .
$db->q('root'));
			$rootItemId = $db->setQuery($query, 0, 1)->loadResult();
		}

		if (is_null($rootItemId))
		{
			// Dude. Dude! Duuuuuuude! The alias is screwed up, too?! Find it by
component ID.
			$rootItemId = null;
			$query = $db->getQuery(true)
				->select($db->qn('id'))
				->from($db->qn('#__menu'))
				->where($db->qn('component_id') . ' = ' .
$db->q('0'));
			$rootItemId = $db->setQuery($query, 0, 1)->loadResult();
		}

		if (is_null($rootItemId))
		{
			// Your site is more of a "shite" than a "site".
Let's try with minimum lft value.
			$rootItemId = null;
			$query = $db->getQuery(true)
				->select($db->qn('id'))
				->from($db->qn('#__menu'))
				->order($db->qn('lft') . ' ASC');
			$rootItemId = $db->setQuery($query, 0, 1)->loadResult();
		}

		if (is_null($rootItemId))
		{
			// I quit. Your site is broken.
			return false;
		}

		$table->rebuild($rootItemId);
	}

	/**
	 * Adds or updates a post-installation message (PIM) definition for
Joomla! 3.2 or later. You can use this in your
	 * post-installation script using this code:
	 *
	 * The $options array contains the following mandatory keys:
	 *
	 * extension_id        The numeric ID of the extension this message is for
(see the #__extensions table)
	 *
	 * type                One of message, link or action. Their meaning is:
	 *                    message        Informative message. The user can
dismiss it.
	 *                    link        The action button links to a URL. The
URL is defined in the action parameter.
	 *                  action      A PHP action takes place when the action
button is clicked. You need to specify the
	 *                              action_file (RAD path to the PHP file) and
action (PHP function name) keys. See
	 *                              below for more information.
	 *
	 * title_key        The JText language key for the title of this PIM
	 *                    Example: COM_FOOBAR_POSTINSTALL_MESSAGEONE_TITLE
	 *
	 * description_key    The JText language key for the main body
(description) of this PIM
	 *                    Example:
COM_FOOBAR_POSTINSTALL_MESSAGEONE_DESCRIPTION
	 *
	 * action_key        The JText language key for the action button. Ignored
and not required when type=message
	 *                    Example: COM_FOOBAR_POSTINSTALL_MESSAGEONE_ACTION
	 *
	 * language_extension    The extension name which holds the language keys
used above. For example, com_foobar,
	 *                    mod_something, plg_system_whatever, tpl_mytemplate
	 *
	 * language_client_id   Should we load the front-end (0) or back-end (1)
language keys?
	 *
	 * version_introduced   Which was the version of your extension where this
message appeared for the first time?
	 *                        Example: 3.2.1
	 *
	 * enabled              Must be 1 for this message to be enabled. If you
omit it, it defaults to 1.
	 *
	 * condition_file        The RAD path to a PHP file containing a PHP
function which determines whether this message
	 *                        should be shown to the user. @see
FOFTemplateUtils::parsePath() for RAD path format. Joomla!
	 *                        will include this file before calling the
condition_method.
	 *                      Example:  
admin://components/com_foobar/helpers/postinstall.php
	 *
	 * condition_method     The name of a PHP function which will be used to
determine whether to show this message to
	 *                      the user. This must be a simple PHP user function
(not a class method, static method etc)
	 *                        which returns true to show the message and false
to hide it. This function is defined in the
	 *                        condition_file.
	 *                        Example:
com_foobar_postinstall_messageone_condition
	 *
	 * When type=message no additional keys are required.
	 *
	 * When type=link the following additional keys are required:
	 *
	 * action                The URL which will open when the user clicks on
the PIM's action button
	 *                        Example:   
index.php?option=com_foobar&view=tools&task=installSampleData
	 *
	 * Then type=action the following additional keys are required:
	 *
	 * action_file            The RAD path to a PHP file containing a PHP
function which performs the action of this PIM.
	 *
	 * @see                   FOFTemplateUtils::parsePath() for RAD path
format. Joomla! will include this file
	 *                        before calling the function defined in the
action key below.
	 *                        Example:  
admin://components/com_foobar/helpers/postinstall.php
	 *
	 * action                The name of a PHP function which will be used to
run the action of this PIM. This must be a
	 *                      simple PHP user function (not a class method,
static method etc) which returns no result.
	 *                        Example:
com_foobar_postinstall_messageone_action
	 *
	 * @param array $options See description
	 *
	 * @return  void
	 *
	 * @throws Exception
	 */
	protected function addPostInstallationMessage(array $options)
	{
		// Make sure there are options set
		if (!is_array($options))
		{
			throw new Exception('Post-installation message definitions must be
of type array', 500);
		}

		// Initialise array keys
		$defaultOptions = array(
			'extension_id'       => '',
			'type'               => '',
			'title_key'          => '',
			'description_key'    => '',
			'action_key'         => '',
			'language_extension' => '',
			'language_client_id' => '',
			'action_file'        => '',
			'action'             => '',
			'condition_file'     => '',
			'condition_method'   => '',
			'version_introduced' => '',
			'enabled'            => '1',
		);

		$options = array_merge($defaultOptions, $options);

		// Array normalisation. Removes array keys not belonging to a definition.
		$defaultKeys = array_keys($defaultOptions);
		$allKeys = array_keys($options);
		$extraKeys = array_diff($allKeys, $defaultKeys);

		if (!empty($extraKeys))
		{
			foreach ($extraKeys as $key)
			{
				unset($options[$key]);
			}
		}

		// Normalisation of integer values
		$options['extension_id'] =
(int)$options['extension_id'];
		$options['language_client_id'] =
(int)$options['language_client_id'];
		$options['enabled'] = (int)$options['enabled'];

		// Normalisation of 0/1 values
		foreach (array('language_client_id', 'enabled') as
$key)
		{
			$options[$key] = $options[$key] ? 1 : 0;
		}

		// Make sure there's an extension_id
		if (!(int)$options['extension_id'])
		{
			throw new Exception('Post-installation message definitions need an
extension_id', 500);
		}

		// Make sure there's a valid type
		if (!in_array($options['type'], array('message',
'link', 'action')))
		{
			throw new Exception('Post-installation message definitions need to
declare a type of message, link or action', 500);
		}

		// Make sure there's a title key
		if (empty($options['title_key']))
		{
			throw new Exception('Post-installation message definitions need a
title key', 500);
		}

		// Make sure there's a description key
		if (empty($options['description_key']))
		{
			throw new Exception('Post-installation message definitions need a
description key', 500);
		}

		// If the type is anything other than message you need an action key
		if (($options['type'] != 'message') &&
empty($options['action_key']))
		{
			throw new Exception('Post-installation message definitions need an
action key when they are of type "' . $options['type']
. '"', 500);
		}

		// You must specify the language extension
		if (empty($options['language_extension']))
		{
			throw new Exception('Post-installation message definitions need to
specify which extension contains their language keys', 500);
		}

		// The action file and method are only required for the
"action" type
		if ($options['type'] == 'action')
		{
			if (empty($options['action_file']))
			{
				throw new Exception('Post-installation message definitions need an
action file when they are of type "action"', 500);
			}

			$file_path =
FOFTemplateUtils::parsePath($options['action_file'], true);

			if (!@is_file($file_path))
			{
				throw new Exception('The action file ' .
$options['action_file'] . ' of your post-installation
message definition does not exist', 500);
			}

			if (empty($options['action']))
			{
				throw new Exception('Post-installation message definitions need an
action (function name) when they are of type "action"',
500);
			}
		}

		if ($options['type'] == 'link')
		{
			if (empty($options['link']))
			{
				throw new Exception('Post-installation message definitions need an
action (URL) when they are of type "link"', 500);
			}
		}

		// The condition file and method are only required when the type is not
"message"
		if ($options['type'] != 'message')
		{
			if (empty($options['condition_file']))
			{
				throw new Exception('Post-installation message definitions need a
condition file when they are of type "' .
$options['type'] . '"', 500);
			}

			$file_path =
FOFTemplateUtils::parsePath($options['condition_file'], true);

			if (!@is_file($file_path))
			{
				throw new Exception('The condition file ' .
$options['condition_file'] . ' of your post-installation
message definition does not exist', 500);
			}

			if (empty($options['condition_method']))
			{
				throw new Exception('Post-installation message definitions need a
condition method (function name) when they are of type "' .
$options['type'] . '"', 500);
			}
		}

		// Check if the definition exists
		$tableName = '#__postinstall_messages';

		$db = FOFPlatform::getInstance()->getDbo();
		$query = $db->getQuery(true)
			->select('*')
			->from($db->qn($tableName))
			->where($db->qn('extension_id') . ' = ' .
$db->q($options['extension_id']))
			->where($db->qn('type') . ' = ' .
$db->q($options['type']))
			->where($db->qn('title_key') . ' = ' .
$db->q($options['title_key']));
		$existingRow = $db->setQuery($query)->loadAssoc();

		// Is the existing definition the same as the one we're trying to
save (ignore the enabled flag)?
		if (!empty($existingRow))
		{
			$same = true;

			foreach ($options as $k => $v)
			{
				if ($k == 'enabled')
				{
					continue;
				}

				if ($existingRow[$k] != $v)
				{
					$same = false;
					break;
				}
			}

			// Trying to add the same row as the existing one; quit
			if ($same)
			{
				return;
			}

			// Otherwise it's not the same row. Remove the old row before
insert a new one.
			$query = $db->getQuery(true)
				->delete($db->qn($tableName))
				->where($db->q('extension_id') . ' = ' .
$db->q($options['extension_id']))
				->where($db->q('type') . ' = ' .
$db->q($options['type']))
				->where($db->q('title_key') . ' = ' .
$db->q($options['title_key']));
			$db->setQuery($query)->execute();
		}

		// Insert the new row
		$options = (object)$options;
		$db->insertObject($tableName, $options);
	}

	/**
	 * Applies the post-installation messages for Joomla! 3.2 or later
	 *
	 * @return void
	 */
	protected function _applyPostInstallationMessages()
	{
		// Make sure it's Joomla! 3.2.0 or later
		if (!version_compare(JVERSION, '3.2.0', 'ge'))
		{
			return;
		}

		// Make sure there are post-installation messages
		if (empty($this->postInstallationMessages))
		{
			return;
		}

		// Get the extension ID for our component
		$db = FOFPlatform::getInstance()->getDbo();
		$query = $db->getQuery(true);
		$query->select('extension_id')
			->from('#__extensions')
			->where($db->qn('type') . ' = ' .
$db->q('component'))
			->where($db->qn('element') . ' = ' .
$db->q($this->componentName));
		$db->setQuery($query);

		try
		{
			$ids = $db->loadColumn();
		}
		catch (Exception $exc)
		{
			return;
		}

		if (empty($ids))
		{
			return;
		}

		$extension_id = array_shift($ids);

		foreach ($this->postInstallationMessages as $message)
		{
			$message['extension_id'] = $extension_id;
			$this->addPostInstallationMessage($message);
		}
	}

	protected function uninstallPostInstallationMessages()
	{
		// Make sure it's Joomla! 3.2.0 or later
		if (!version_compare(JVERSION, '3.2.0', 'ge'))
		{
			return;
		}

		// Make sure there are post-installation messages
		if (empty($this->postInstallationMessages))
		{
			return;
		}

		// Get the extension ID for our component
		$db = FOFPlatform::getInstance()->getDbo();
		$query = $db->getQuery(true);
		$query->select('extension_id')
			->from('#__extensions')
			->where($db->qn('type') . ' = ' .
$db->q('component'))
			->where($db->qn('element') . ' = ' .
$db->q($this->componentName));
		$db->setQuery($query);

		try
		{
			$ids = $db->loadColumn();
		}
		catch (Exception $exc)
		{
			return;
		}

		if (empty($ids))
		{
			return;
		}

		$extension_id = array_shift($ids);

		$query = $db->getQuery(true)
			->delete($db->qn('#__postinstall_messages'))
			->where($db->qn('extension_id') . ' = ' .
$db->q($extension_id));

		try
		{
			$db->setQuery($query)->execute();
		}
		catch (Exception $e)
		{
			return;
		}
	}
}
ip/ip.php000064400000026514151167373210006313 0ustar00<?php
/**
 * @package     FrameworkOnFramework
 * @subpackage  utils
 * @copyright   Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('FOF_INCLUDED') or die;

/**
 * IP address utilities
 */
abstract class FOFUtilsIp
{
	/** @var   string  The IP address of the current visitor */
	protected static $ip = null;

	/**
	 * Should I allow IP overrides through X-Forwarded-For or Client-Ip HTTP
headers?
	 *
	 * @var    bool
	 */
	protected static $allowIpOverrides = true;

	/**
	 * Get the current visitor's IP address
	 *
	 * @return string
	 */
	public static function getIp()
	{
		if (is_null(static::$ip))
		{
			$ip = self::detectAndCleanIP();

			if (!empty($ip) && ($ip != '0.0.0.0') &&
function_exists('inet_pton') &&
function_exists('inet_ntop'))
			{
				$myIP = @inet_pton($ip);

				if ($myIP !== false)
				{
					$ip = inet_ntop($myIP);
				}
			}

			static::setIp($ip);
		}

		return static::$ip;
	}

	/**
	 * Set the IP address of the current visitor
	 *
	 * @param   string  $ip
	 *
	 * @return  void
	 */
	public static function setIp($ip)
	{
		static::$ip = $ip;
	}

	/**
	 * Is it an IPv6 IP address?
	 *
	 * @param   string   $ip  An IPv4 or IPv6 address
	 *
	 * @return  boolean  True if it's IPv6
	 */
	public static function isIPv6($ip)
	{
		if (strstr($ip, ':'))
		{
			return true;
		}

		return false;
	}

	/**
	 * Checks if an IP is contained in a list of IPs or IP expressions
	 *
	 * @param   string        $ip       The IPv4/IPv6 address to check
	 * @param   array|string  $ipTable  An IP expression (or a comma-separated
or array list of IP expressions) to check against
	 *
	 * @return  null|boolean  True if it's in the list
	 */
	public static function IPinList($ip, $ipTable = '')
	{
		// No point proceeding with an empty IP list
		if (empty($ipTable))
		{
			return false;
		}

		// If the IP list is not an array, convert it to an array
		if (!is_array($ipTable))
		{
			if (strpos($ipTable, ',') !== false)
			{
				$ipTable = explode(',', $ipTable);
				$ipTable = array_map(function($x) { return trim($x); }, $ipTable);
			}
			else
			{
				$ipTable = trim($ipTable);
				$ipTable = array($ipTable);
			}
		}

		// If no IP address is found, return false
		if ($ip == '0.0.0.0')
		{
			return false;
		}

		// If no IP is given, return false
		if (empty($ip))
		{
			return false;
		}

		// Sanity check
		if (!function_exists('inet_pton'))
		{
			return false;
		}

		// Get the IP's in_adds representation
		$myIP = @inet_pton($ip);

		// If the IP is in an unrecognisable format, quite
		if ($myIP === false)
		{
			return false;
		}

		$ipv6 = self::isIPv6($ip);

		foreach ($ipTable as $ipExpression)
		{
			$ipExpression = trim($ipExpression);

			// Inclusive IP range, i.e. 123.123.123.123-124.125.126.127
			if (strstr($ipExpression, '-'))
			{
				list($from, $to) = explode('-', $ipExpression, 2);

				if ($ipv6 && (!self::isIPv6($from) || !self::isIPv6($to)))
				{
					// Do not apply IPv4 filtering on an IPv6 address
					continue;
				}
				elseif (!$ipv6 && (self::isIPv6($from) || self::isIPv6($to)))
				{
					// Do not apply IPv6 filtering on an IPv4 address
					continue;
				}

				$from = @inet_pton(trim($from));
				$to = @inet_pton(trim($to));

				// Sanity check
				if (($from === false) || ($to === false))
				{
					continue;
				}

				// Swap from/to if they're in the wrong order
				if ($from > $to)
				{
					list($from, $to) = array($to, $from);
				}

				if (($myIP >= $from) && ($myIP <= $to))
				{
					return true;
				}
			}
			// Netmask or CIDR provided
			elseif (strstr($ipExpression, '/'))
			{
				$binaryip = self::inet_to_bits($myIP);

				list($net, $maskbits) = explode('/', $ipExpression, 2);
				if ($ipv6 && !self::isIPv6($net))
				{
					// Do not apply IPv4 filtering on an IPv6 address
					continue;
				}
				elseif (!$ipv6 && self::isIPv6($net))
				{
					// Do not apply IPv6 filtering on an IPv4 address
					continue;
				}
				elseif ($ipv6 && strstr($maskbits, ':'))
				{
					// Perform an IPv6 CIDR check
					if (self::checkIPv6CIDR($myIP, $ipExpression))
					{
						return true;
					}

					// If we didn't match it proceed to the next expression
					continue;
				}
				elseif (!$ipv6 && strstr($maskbits, '.'))
				{
					// Convert IPv4 netmask to CIDR
					$long = ip2long($maskbits);
					$base = ip2long('255.255.255.255');
					$maskbits = 32 - log(($long ^ $base) + 1, 2);
				}

				// Convert network IP to in_addr representation
				$net = @inet_pton($net);

				// Sanity check
				if ($net === false)
				{
					continue;
				}

				// Get the network's binary representation
				$binarynet = self::inet_to_bits($net);
				$expectedNumberOfBits = $ipv6 ? 128 : 24;
				$binarynet = str_pad($binarynet, $expectedNumberOfBits, '0',
STR_PAD_RIGHT);

				// Check the corresponding bits of the IP and the network
				$ip_net_bits = substr($binaryip, 0, $maskbits);
				$net_bits = substr($binarynet, 0, $maskbits);

				if ($ip_net_bits == $net_bits)
				{
					return true;
				}
			}
			else
			{
				// IPv6: Only single IPs are supported
				if ($ipv6)
				{
					$ipExpression = trim($ipExpression);

					if (!self::isIPv6($ipExpression))
					{
						continue;
					}

					$ipCheck = @inet_pton($ipExpression);
					if ($ipCheck === false)
					{
						continue;
					}

					if ($ipCheck == $myIP)
					{
						return true;
					}
				}
				else
				{
					// Standard IPv4 address, i.e. 123.123.123.123 or partial IP address,
i.e. 123.[123.][123.][123]
					$dots = 0;
					if (substr($ipExpression, -1) == '.')
					{
						// Partial IP address. Convert to CIDR and re-match
						foreach (count_chars($ipExpression, 1) as $i => $val)
						{
							if ($i == 46)
							{
								$dots = $val;
							}
						}
						switch ($dots)
						{
							case 1:
								$netmask = '255.0.0.0';
								$ipExpression .= '0.0.0';
								break;

							case 2:
								$netmask = '255.255.0.0';
								$ipExpression .= '0.0';
								break;

							case 3:
								$netmask = '255.255.255.0';
								$ipExpression .= '0';
								break;

							default:
								$dots = 0;
						}

						if ($dots)
						{
							$binaryip = self::inet_to_bits($myIP);

							// Convert netmask to CIDR
							$long = ip2long($netmask);
							$base = ip2long('255.255.255.255');
							$maskbits = 32 - log(($long ^ $base) + 1, 2);

							$net = @inet_pton($ipExpression);

							// Sanity check
							if ($net === false)
							{
								continue;
							}

							// Get the network's binary representation
							$binarynet = self::inet_to_bits($net);
							$expectedNumberOfBits = $ipv6 ? 128 : 24;
							$binarynet = str_pad($binarynet, $expectedNumberOfBits,
'0', STR_PAD_RIGHT);

							// Check the corresponding bits of the IP and the network
							$ip_net_bits = substr($binaryip, 0, $maskbits);
							$net_bits = substr($binarynet, 0, $maskbits);

							if ($ip_net_bits == $net_bits)
							{
								return true;
							}
						}
					}
					if (!$dots)
					{
						$ip = @inet_pton(trim($ipExpression));
						if ($ip == $myIP)
						{
							return true;
						}
					}
				}
			}
		}

		return false;
	}

	/**
	 * Works around the REMOTE_ADDR not containing the user's IP
	 */
	public static function workaroundIPIssues()
	{
		$ip = self::getIp();

		if ($_SERVER['REMOTE_ADDR'] == $ip)
		{
			return;
		}

		if (array_key_exists('REMOTE_ADDR', $_SERVER))
		{
			$_SERVER['FOF_REMOTE_ADDR'] =
$_SERVER['REMOTE_ADDR'];
		}
		elseif (function_exists('getenv'))
		{
			if (getenv('REMOTE_ADDR'))
			{
				$_SERVER['FOF_REMOTE_ADDR'] =
getenv('REMOTE_ADDR');
			}
		}

		$_SERVER['REMOTE_ADDR'] = $ip;
	}

	/**
	 * Should I allow the remote client's IP to be overridden by an
X-Forwarded-For or Client-Ip HTTP header?
	 *
	 * @param   bool  $newState  True to allow the override
	 *
	 * @return  void
	 */
	public static function setAllowIpOverrides($newState)
	{
		self::$allowIpOverrides = $newState ? true : false;
	}

	/**
	 * Gets the visitor's IP address. Automatically handles reverse
proxies
	 * reporting the IPs of intermediate devices, like load balancers.
Examples:
	 *
https://www.akeebabackup.com/support/admin-tools/13743-double-ip-adresses-in-security-exception-log-warnings.html
	 *
http://stackoverflow.com/questions/2422395/why-is-request-envremote-addr-returning-two-ips
	 * The solution used is assuming that the last IP address is the external
one.
	 *
	 * @return  string
	 */
	protected static function detectAndCleanIP()
	{
		$ip = self::detectIP();

		if ((strstr($ip, ',') !== false) || (strstr($ip, ' ')
!== false))
		{
			$ip = str_replace(' ', ',', $ip);
			$ip = str_replace(',,', ',', $ip);
			$ips = explode(',', $ip);
			$ip = '';
			while (empty($ip) && !empty($ips))
			{
				$ip = array_pop($ips);
				$ip = trim($ip);
			}
		}
		else
		{
			$ip = trim($ip);
		}

		return $ip;
	}

	/**
	 * Gets the visitor's IP address
	 *
	 * @return  string
	 */
	protected static function detectIP()
	{
		// Normally the $_SERVER superglobal is set
		if (isset($_SERVER))
		{
			// Do we have an x-forwarded-for HTTP header (e.g. NginX)?
			if (self::$allowIpOverrides &&
array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER))
			{
				return $_SERVER['HTTP_X_FORWARDED_FOR'];
			}

			// Do we have a client-ip header (e.g. non-transparent proxy)?
			if (self::$allowIpOverrides &&
array_key_exists('HTTP_CLIENT_IP', $_SERVER))
			{
				return $_SERVER['HTTP_CLIENT_IP'];
			}

			// Normal, non-proxied server or server behind a transparent proxy
			return $_SERVER['REMOTE_ADDR'];
		}

		// This part is executed on PHP running as CGI, or on SAPIs which do
		// not set the $_SERVER superglobal
		// If getenv() is disabled, you're screwed
		if (!function_exists('getenv'))
		{
			return '';
		}

		// Do we have an x-forwarded-for HTTP header?
		if (self::$allowIpOverrides &&
getenv('HTTP_X_FORWARDED_FOR'))
		{
			return getenv('HTTP_X_FORWARDED_FOR');
		}

		// Do we have a client-ip header?
		if (self::$allowIpOverrides &&
getenv('HTTP_CLIENT_IP'))
		{
			return getenv('HTTP_CLIENT_IP');
		}

		// Normal, non-proxied server or server behind a transparent proxy
		if (getenv('REMOTE_ADDR'))
		{
			return getenv('REMOTE_ADDR');
		}

		// Catch-all case for broken servers, apparently
		return '';
	}

	/**
	 * Converts inet_pton output to bits string
	 *
	 * @param   string $inet The in_addr representation of an IPv4 or IPv6
address
	 *
	 * @return  string
	 */
	protected static function inet_to_bits($inet)
	{
		if (strlen($inet) == 4)
		{
			$unpacked = unpack('A4', $inet);
		}
		else
		{
			$unpacked = unpack('A16', $inet);
		}
		$unpacked = str_split($unpacked[1]);
		$binaryip = '';

		foreach ($unpacked as $char)
		{
			$binaryip .= str_pad(decbin(ord($char)), 8, '0',
STR_PAD_LEFT);
		}

		return $binaryip;
	}

	/**
	 * Checks if an IPv6 address $ip is part of the IPv6 CIDR block $cidrnet
	 *
	 * @param   string  $ip       The IPv6 address to check, e.g.
21DA:00D3:0000:2F3B:02AC:00FF:FE28:9C5A
	 * @param   string  $cidrnet  The IPv6 CIDR block, e.g.
21DA:00D3:0000:2F3B::/64
	 *
	 * @return  bool
	 */
	protected static function checkIPv6CIDR($ip, $cidrnet)
	{
		$ip = inet_pton($ip);
		$binaryip=self::inet_to_bits($ip);

		list($net,$maskbits)=explode('/',$cidrnet);
		$net=inet_pton($net);
		$binarynet=self::inet_to_bits($net);

		$ip_net_bits=substr($binaryip,0,$maskbits);
		$net_bits   =substr($binarynet,0,$maskbits);

		return $ip_net_bits === $net_bits;
	}
}object/object.php000064400000012143151167373210010000 0ustar00<?php
/**
 * @package     FrameworkOnFramework
 * @subpackage  utils
 * @copyright   Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('FOF_INCLUDED') or die;

/**
 * Temporary class for backwards compatibility. You should not be using
this
 * in your code. It is currently present to handle the validation error
stack
 * for FOFTable::check() and will be removed in an upcoming version.
 *
 * This class is based on JObject as found in Joomla! 3.2.1
 *
 * @deprecated  2.1
 * @codeCoverageIgnore
 */
class FOFUtilsObject
{
    /**
     * An array of error messages or Exception objects.
     *
     * @var    array
     */
    protected $_errors = array();

    /**
     * Class constructor, overridden in descendant classes.
     *
     * @param   mixed  $properties  Either and associative array or another
     *                              object to set the initial properties of
the object.
     */
    public function __construct($properties = null)
    {
        if ($properties !== null)
        {
            $this->setProperties($properties);
        }
    }

    /**
     * Magic method to convert the object to a string gracefully.
     *
     * @return  string  The classname.
     */
    public function __toString()
    {
        return get_class($this);
    }

    /**
     * Sets a default value if not alreay assigned
     *
     * @param   string  $property  The name of the property.
     * @param   mixed   $default   The default value.
     *
     * @return  mixed
     */
    public function def($property, $default = null)
    {
        $value = $this->get($property, $default);
        return $this->set($property, $value);
    }

    /**
     * Returns a property of the object or the default value if the
property is not set.
     *
     * @param   string  $property  The name of the property.
     * @param   mixed   $default   The default value.
     *
     * @return  mixed    The value of the property.
     */
    public function get($property, $default = null)
    {
        if (isset($this->$property))
        {
            return $this->$property;
        }
        return $default;
    }

    /**
     * Returns an associative array of object properties.
     *
     * @param   boolean  $public  If true, returns only the public
properties.
     *
     * @return  array
     */
    public function getProperties($public = true)
    {
        $vars = get_object_vars($this);
        if ($public)
        {
            foreach ($vars as $key => $value)
            {
                if ('_' == substr($key, 0, 1))
                {
                    unset($vars[$key]);
                }
            }
        }

        return $vars;
    }

    /**
     * Get the most recent error message.
     *
     * @param   integer  $i         Option error index.
     * @param   boolean  $toString  Indicates if JError objects should
return their error message.
     *
     * @return  string   Error message
     */
    public function getError($i = null, $toString = true)
    {
        // Find the error
        if ($i === null)
        {
            // Default, return the last message
            $error = end($this->_errors);
        }
        elseif (!array_key_exists($i, $this->_errors))
        {
            // If $i has been specified but does not exist, return false
            return false;
        }
        else
        {
            $error = $this->_errors[$i];
        }

        // Check if only the string is requested
        if ($error instanceof Exception && $toString)
        {
            return (string) $error;
        }

        return $error;
    }

    /**
     * Return all errors, if any.
     *
     * @return  array  Array of error messages or JErrors.
     */
    public function getErrors()
    {
        return $this->_errors;
    }

    /**
     * Modifies a property of the object, creating it if it does not
already exist.
     *
     * @param   string  $property  The name of the property.
     * @param   mixed   $value     The value of the property to set.
     *
     * @return  mixed  Previous value of the property.
     */
    public function set($property, $value = null)
    {
        $previous = isset($this->$property) ? $this->$property :
null;
        $this->$property = $value;
        return $previous;
    }

    /**
     * Set the object properties based on a named array/hash.
     *
     * @param   mixed  $properties  Either an associative array or another
object.
     *
     * @return  boolean
     */
    public function setProperties($properties)
    {
        if (is_array($properties) || is_object($properties))
        {
            foreach ((array) $properties as $k => $v)
            {
                // Use the set function which might be overridden.
                $this->set($k, $v);
            }
            return true;
        }

        return false;
    }

    /**
     * Add an error message.
     *
     * @param   string  $error  Error message.
     *
     * @return  void
     */
    public function setError($error)
    {
        array_push($this->_errors, $error);
    }
}
observable/dispatcher.php000064400000016452151167373210011545
0ustar00<?php
/**
 * @package     FrameworkOnFramework
 * @subpackage  utils
 * @copyright   Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('FOF_INCLUDED') or die;

/**
 * Class to handle dispatching of events.
 *
 * This is the Observable part of the Observer design pattern
 * for the event architecture.
 *
 * This class is based on JEventDispatcher as found in Joomla! 3.2.0
 */
class FOFUtilsObservableDispatcher extends FOFUtilsObject
{
    /**
     * An array of Observer objects to notify
     *
     * @var    array
     */
    protected $_observers = array();

    /**
     * The state of the observable object
     *
     * @var    mixed
     */
    protected $_state = null;

    /**
     * A multi dimensional array of [function][] = key for observers
     *
     * @var    array
     */
    protected $_methods = array();

    /**
     * Stores the singleton instance of the dispatcher.
     *
     * @var    FOFUtilsObservableDispatcher
     */
    protected static $instance = null;

    /**
     * Returns the global Event Dispatcher object, only creating it
     * if it doesn't already exist.
     *
     * @return  FOFUtilsObservableDispatcher  The EventDispatcher object.
     */
    public static function getInstance()
    {
        if (self::$instance === null)
        {
            self::$instance = new static;
        }

        return self::$instance;
    }

    /**
     * Get the state of the FOFUtilsObservableDispatcher object
     *
     * @return  mixed    The state of the object.
     */
    public function getState()
    {
        return $this->_state;
    }

    /**
     * Registers an event handler to the event dispatcher
     *
     * @param   string  $event    Name of the event to register handler for
     * @param   string  $handler  Name of the event handler
     *
     * @return  void
     *
     * @throws  InvalidArgumentException
     */
    public function register($event, $handler)
    {
        // Are we dealing with a class or callback type handler?
        if (is_callable($handler))
        {
            // Ok, function type event handler... let's attach it.
            $method = array('event' => $event,
'handler' => $handler);
            $this->attach($method);
        }
        elseif (class_exists($handler))
        {
            // Ok, class type event handler... let's instantiate and
attach it.
            $this->attach(new $handler($this));
        }
        else
        {
            throw new InvalidArgumentException('Invalid event
handler.');
        }
    }

    /**
     * Triggers an event by dispatching arguments to all observers that
handle
     * the event and returning their return values.
     *
     * @param   string  $event  The event to trigger.
     * @param   array   $args   An array of arguments.
     *
     * @return  array  An array of results from each function call.
     */
    public function trigger($event, $args = array())
    {
        $result = array();

        /*
         * If no arguments were passed, we still need to pass an empty
array to
         * the call_user_func_array function.
         */
        $args = (array) $args;

        $event = strtolower($event);

        // Check if any plugins are attached to the event.
        if (!isset($this->_methods[$event]) ||
empty($this->_methods[$event]))
        {
            // No Plugins Associated To Event!
            return $result;
        }

        // Loop through all plugins having a method matching our event
        foreach ($this->_methods[$event] as $key)
        {
            // Check if the plugin is present.
            if (!isset($this->_observers[$key]))
            {
                continue;
            }

            // Fire the event for an object based observer.
            if (is_object($this->_observers[$key]))
            {
                $args['event'] = $event;
                $value = $this->_observers[$key]->update($args);
            }
            // Fire the event for a function based observer.
            elseif (is_array($this->_observers[$key]))
            {
                $value =
call_user_func_array($this->_observers[$key]['handler'],
$args);
            }

            if (isset($value))
            {
                $result[] = $value;
            }
        }

        return $result;
    }

    /**
     * Attach an observer object
     *
     * @param   object  $observer  An observer object to attach
     *
     * @return  void
     */
    public function attach($observer)
    {
        if (is_array($observer))
        {
            if (!isset($observer['handler']) ||
!isset($observer['event']) ||
!is_callable($observer['handler']))
            {
                return;
            }

            // Make sure we haven't already attached this array as an
observer
            foreach ($this->_observers as $check)
            {
                if (is_array($check) && $check['event']
== $observer['event'] && $check['handler'] ==
$observer['handler'])
                {
                    return;
                }
            }

            $this->_observers[] = $observer;
            end($this->_observers);
            $methods = array($observer['event']);
        }
        else
        {
            if (!($observer instanceof FOFUtilsObservableEvent))
            {
                return;
            }

            // Make sure we haven't already attached this object as an
observer
            $class = get_class($observer);

            foreach ($this->_observers as $check)
            {
                if ($check instanceof $class)
                {
                    return;
                }
            }

            $this->_observers[] = $observer;

            // Required in PHP 7 since foreach() doesn't advance the
internal array counter, see
http://php.net/manual/en/migration70.incompatible.php
            end($this->_observers);

            $methods = array();

            foreach(get_class_methods($observer) as $obs_method)
            {
                // Magic methods are not allowed
                if(strpos('__', $obs_method) === 0)
                {
                    continue;
                }

                $methods[] = $obs_method;
            }

            //$methods = get_class_methods($observer);
        }

        $key = key($this->_observers);

        foreach ($methods as $method)
        {
            $method = strtolower($method);

            if (!isset($this->_methods[$method]))
            {
                $this->_methods[$method] = array();
            }

            $this->_methods[$method][] = $key;
        }
    }

    /**
     * Detach an observer object
     *
     * @param   object  $observer  An observer object to detach.
     *
     * @return  boolean  True if the observer object was detached.
     */
    public function detach($observer)
    {
        $retval = false;

        $key = array_search($observer, $this->_observers);

        if ($key !== false)
        {
            unset($this->_observers[$key]);
            $retval = true;

            foreach ($this->_methods as &$method)
            {
                $k = array_search($key, $method);

                if ($k !== false)
                {
                    unset($method[$k]);
                }
            }
        }

        return $retval;
    }
}
observable/event.php000064400000003631151167373210010533 0ustar00<?php
/**
 * @package     FrameworkOnFramework
 * @subpackage  utils
 * @copyright   Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('FOF_INCLUDED') or die;

/**
 * Defines an observable event.
 *
 * This class is based on JEvent as found in Joomla! 3.2.0
 */
abstract class FOFUtilsObservableEvent extends FOFUtilsObject
{
    /**
     * Event object to observe.
     *
     * @var    object
     */
    protected $_subject = null;

    /**
     * Constructor
     *
     * @param   object  &$subject  The object to observe.
     */
    public function __construct(&$subject)
    {
        // Register the observer ($this) so we can be notified
        $subject->attach($this);

        // Set the subject to observe
        $this->_subject = &$subject;
    }

    /**
     * Method to trigger events.
     * The method first generates the even from the argument array. Then it
unsets the argument
     * since the argument has no bearing on the event handler.
     * If the method exists it is called and returns its return value. If
it does not exist it
     * returns null.
     *
     * @param   array  &$args  Arguments
     *
     * @return  mixed  Routine return value
     */
    public function update(&$args)
    {
        // First let's get the event from the argument array.  Next we
will unset the
        // event argument as it has no bearing on the method to handle the
event.
        $event = $args['event'];
        unset($args['event']);

        /*
         * If the method to handle an event exists, call it and return its
return
         * value.  If it does not exist, return null.
         */
        if (method_exists($this, $event))
        {
            return call_user_func_array(array($this, $event), $args);
        }
        else
        {
            return null;
        }
    }
}
phpfunc/phpfunc.php000064400000001711151167373210010371 0ustar00<?php
/**
 * @package     FrameworkOnFramework
 * @subpackage  utils
 * @copyright   Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

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

/**
 * Intercept calls to PHP functions.
 *
 * @method  function_exists(string $function)
 * @method  mcrypt_list_algorithms()
 * @method  hash_algos()
 * @method  extension_loaded(string $ext)
 * @method  mcrypt_create_iv(int $bytes, int $source)
 * @method  openssl_get_cipher_methods()
 */
class FOFUtilsPhpfunc
{
	/**
	 *
	 * Magic call to intercept any function pass to it.
	 *
	 * @param string $func The function to call.
	 *
	 * @param array  $args Arguments passed to the function.
	 *
	 * @return mixed The result of the function call.
	 *
	 */
	public function __call($func, $args)
	{
		return call_user_func_array($func, $args);
	}
}
timer/timer.php000064400000003775151167373210007537 0ustar00<?php
/**
 * @package     FrameworkOnFramework
 * @subpackage  utils
 * @copyright   Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

defined('FOF_INCLUDED') or die;

/**
 * An execution timer monitor class
 */
class FOFUtilsTimer
{
	/** @var float Maximum execution time allowance */
	private $max_exec_time = null;

	/** @var float Timestamp of execution start */
	private $start_time = null;

	/**
	 * Public constructor, creates the timer object and calculates the
execution
	 * time limits.
	 *
	 * @param float $max_exec_time Maximum execution time allowance
	 * @param float $runtime_bias  Execution time bias (expressed as % of
$max_exec_time)
	 */
	public function __construct($max_exec_time = 5.0, $runtime_bias = 75.0)
	{
		// Initialize start time
		$this->start_time = $this->microtime_float();

		$this->max_exec_time = $max_exec_time * $runtime_bias / 100.0;
	}

	/**
	 * Wake-up function to reset internal timer when we get unserialized
	 */
	public function __wakeup()
	{
		// Re-initialize start time on wake-up
		$this->start_time = $this->microtime_float();
	}

	/**
	 * Gets the number of seconds left, before we hit the "must
break" threshold. Negative
	 * values mean that we have already crossed that threshold.
	 *
	 * @return  float
	 */
	public function getTimeLeft()
	{
		return $this->max_exec_time - $this->getRunningTime();
	}

	/**
	 * Gets the time elapsed since object creation/unserialization,
effectively
	 * how long we are running
	 *
	 * @return  float
	 */
	public function getRunningTime()
	{
		return $this->microtime_float() - $this->start_time;
	}

	/**
	 * Returns the current timestamp in decimal seconds
	 *
	 * @return  float
	 */
	private function microtime_float()
	{
		list($usec, $sec) = explode(" ", microtime());
		return ((float)$usec + (float)$sec);
	}

	/**
	 * Reset the timer
	 */
	public function resetTime()
	{
		$this->start_time = $this->microtime_float();
	}

}update/collection.php000064400000023630151167373210010704 0ustar00<?php
/**
 * @package     FrameworkOnFramework
 * @subpackage  utils
 * @copyright   Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 * @note        This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
 */

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

/**
 * A helper class to read and parse "collection" update XML files
over the web
 */
class FOFUtilsUpdateCollection
{
	/**
	 * Reads a "collection" XML update source and returns the
complete tree of categories
	 * and extensions applicable for platform version $jVersion
	 *
	 * @param   string  $url       The collection XML update source URL to
read from
	 * @param   string  $jVersion  Joomla! version to fetch updates for, or
null to use JVERSION
	 *
	 * @return  array  A list of update sources applicable to $jVersion
	 */
	public function getAllUpdates($url, $jVersion = null)
	{
		// Get the target platform
		if (is_null($jVersion))
		{
			$jVersion = JVERSION;
		}

		// Initialise return value
		$updates = array(
			'metadata'		=> array(
				'name'			=> '',
				'description'	=> '',
			),
			'categories'	=> array(),
			'extensions'	=> array(),
		);

		// Download and parse the XML file
		$donwloader = new FOFDownload();
		$xmlSource = $donwloader->getFromURL($url);

		try
		{
			$xml = new SimpleXMLElement($xmlSource, LIBXML_NONET);
		}
		catch(Exception $e)
		{
			return $updates;
		}

		// Sanity check
		if (($xml->getName() != 'extensionset'))
		{
			unset($xml);

			return $updates;
		}

		// Initialise return value with the stream metadata (name, description)
		$rootAttributes = $xml->attributes();
		foreach ($rootAttributes as $k => $v)
		{
			$updates['metadata'][$k] = (string)$v;
		}

		// Initialise the raw list of updates
		$rawUpdates = array(
			'categories'	=> array(),
			'extensions'	=> array(),
		);

		// Segregate the raw list to a hierarchy of extension and category
entries
		/** @var SimpleXMLElement $extension */
		foreach ($xml->children() as $extension)
		{
			switch ($extension->getName())
			{
				case 'category':
					// These are the parameters we expect in a category
					$params = array(
						'name'					=> '',
						'description'			=> '',
						'category'				=> '',
						'ref'					=> '',
						'targetplatformversion'	=> $jVersion,
					);

					// These are the attributes of the element
					$attributes = $extension->attributes();

					// Merge them all
					foreach ($attributes as $k => $v)
					{
						$params[$k] = (string)$v;
					}

					// We can't have a category with an empty category name
					if (empty($params['category']))
					{
						break;
					}

					// We can't have a category with an empty ref
					if (empty($params['ref']))
					{
						break;
					}

					if (empty($params['description']))
					{
						$params['description'] = $params['category'];
					}

					if (!array_key_exists($params['category'],
$rawUpdates['categories']))
					{
						$rawUpdates['categories'][$params['category']] =
array();
					}

					$rawUpdates['categories'][$params['category']][] =
$params;

					break;

				case 'extension':
					// These are the parameters we expect in a category
					$params = array(
						'element'				=> '',
						'type'					=> '',
						'version'				=> '',
						'name'					=> '',
						'detailsurl'			=> '',
						'targetplatformversion'	=> $jVersion,
					);

					// These are the attributes of the element
					$attributes = $extension->attributes();

					// Merge them all
					foreach ($attributes as $k => $v)
					{
						$params[$k] = (string)$v;
					}

					// We can't have an extension with an empty element
					if (empty($params['element']))
					{
						break;
					}

					// We can't have an extension with an empty type
					if (empty($params['type']))
					{
						break;
					}

					// We can't have an extension with an empty version
					if (empty($params['version']))
					{
						break;
					}

					if (empty($params['name']))
					{
						$params['name'] = $params['element'] . '
' . $params['version'];
					}

					if (!array_key_exists($params['type'],
$rawUpdates['extensions']))
					{
						$rawUpdates['extensions'][$params['type']] =
array();
					}

					if (!array_key_exists($params['element'],
$rawUpdates['extensions'][$params['type']]))
					{
						$rawUpdates['extensions'][$params['type']][$params['element']]
= array();
					}

					$rawUpdates['extensions'][$params['type']][$params['element']][]
= $params;
					break;

				default:
					break;
			}
		}

		unset($xml);

		if (!empty($rawUpdates['categories']))
		{
			foreach ($rawUpdates['categories'] as $category =>
$entries)
			{
				$update = $this->filterListByPlatform($entries, $jVersion);
				$updates['categories'][$category] = $update;
			}
		}

		if (!empty($rawUpdates['extensions']))
		{
			foreach ($rawUpdates['extensions'] as $type => $extensions)
			{
				$updates['extensions'][$type] = array();

				if (!empty($extensions))
				{
					foreach ($extensions as $element => $entries)
					{
						$update = $this->filterListByPlatform($entries, $jVersion);
						$updates['extensions'][$type][$element] = $update;
					}
				}
			}
		}

		return $updates;
	}

	/**
	 * Filters a list of updates, returning only those available for the
	 * specified platform version $jVersion
	 *
	 * @param   array   $updates   An array containing update definitions
(categories or extensions)
	 * @param   string  $jVersion  Joomla! version to fetch updates for, or
null to use JVERSION
	 *
	 * @return  array|null  The update definition that is compatible, or null
if none is compatible
	 */
	private function filterListByPlatform($updates, $jVersion = null)
	{
		// Get the target platform
		if (is_null($jVersion))
		{
			$jVersion = JVERSION;
		}

		$versionParts = explode('.', $jVersion, 4);
		$platformVersionMajor = $versionParts[0];
		$platformVersionMinor = (count($versionParts) > 1) ?
$platformVersionMajor . '.' . $versionParts[1] :
$platformVersionMajor;
		$platformVersionNormal = (count($versionParts) > 2) ?
$platformVersionMinor . '.' . $versionParts[2] :
$platformVersionMinor;
		$platformVersionFull = (count($versionParts) > 3) ?
$platformVersionNormal . '.' . $versionParts[3] :
$platformVersionNormal;

		$pickedExtension = null;
		$pickedSpecificity = -1;

		foreach ($updates as $update)
		{
			// Test the target platform
			$targetPlatform = (string)$update['targetplatformversion'];

			if ($targetPlatform === $platformVersionFull)
			{
				$pickedExtension = $update;
				$pickedSpecificity = 4;
			}
			elseif (($targetPlatform === $platformVersionNormal) &&
($pickedSpecificity <= 3))
			{
				$pickedExtension = $update;
				$pickedSpecificity = 3;
			}
			elseif (($targetPlatform === $platformVersionMinor) &&
($pickedSpecificity <= 2))
			{
				$pickedExtension = $update;
				$pickedSpecificity = 2;
			}
			elseif (($targetPlatform === $platformVersionMajor) &&
($pickedSpecificity <= 1))
			{
				$pickedExtension = $update;
				$pickedSpecificity = 1;
			}
		}

		return $pickedExtension;
	}

	/**
	 * Returns only the category definitions of a collection
	 *
	 * @param   string  $url       The URL of the collection update source
	 * @param   string  $jVersion  Joomla! version to fetch updates for, or
null to use JVERSION
	 *
	 * @return  array  An array of category update definitions
	 */
	public function getCategories($url, $jVersion = null)
	{
		$allUpdates = $this->getAllUpdates($url, $jVersion);

		return $allUpdates['categories'];
	}

	/**
	 * Returns the update source for a specific category
	 *
	 * @param   string  $url       The URL of the collection update source
	 * @param   string  $category  The category name you want to get the
update source URL of
	 * @param   string  $jVersion  Joomla! version to fetch updates for, or
null to use JVERSION
	 *
	 * @return  string|null  The update stream URL, or null if it's not
found
	 */
	public function getCategoryUpdateSource($url, $category, $jVersion = null)
	{
		$allUpdates = $this->getAllUpdates($url, $jVersion);

		if (array_key_exists($category, $allUpdates['categories']))
		{
			return $allUpdates['categories'][$category]['ref'];
		}
		else
		{
			return null;
		}
	}

	/**
	 * Get a list of updates for extensions only, optionally of a specific
type
	 *
	 * @param   string  $url       The URL of the collection update source
	 * @param   string  $type      The extension type you want to get the
update source URL of, empty to get all
	 *                             extension types
	 * @param   string  $jVersion  Joomla! version to fetch updates for, or
null to use JVERSION
	 *
	 * @return  array|null  An array of extension update definitions or null
if none is found
	 */
	public function getExtensions($url, $type = null, $jVersion = null)
	{
		$allUpdates = $this->getAllUpdates($url, $jVersion);

		if (empty($type))
		{
			return $allUpdates['extensions'];
		}
		elseif (array_key_exists($type, $allUpdates['extensions']))
		{
			return $allUpdates['extensions'][$type];
		}
		else
		{
			return null;
		}
	}

	/**
	 * Get the update source URL for a specific extension, based on the type
and element, e.g.
	 * type=file and element=joomla is Joomla! itself.
	 *
	 * @param   string  $url       The URL of the collection update source
	 * @param   string  $type      The extension type you want to get the
update source URL of
	 * @param   string  $element   The extension element you want to get the
update source URL of
	 * @param   string  $jVersion  Joomla! version to fetch updates for, or
null to use JVERSION
	 *
	 * @return  string|null  The update source URL or null if the extension is
not found
	 */
	public function getExtensionUpdateSource($url, $type, $element, $jVersion
= null)
	{
		$allUpdates = $this->getExtensions($url, $type, $jVersion);

		if (empty($allUpdates))
		{
			return null;
		}
		elseif (array_key_exists($element, $allUpdates))
		{
			return $allUpdates[$element]['detailsurl'];
		}
		else
		{
			return null;
		}
	}
}update/extension.php000064400000006465151167373220010575 0ustar00<?php
/**
 * @package     FrameworkOnFramework
 * @subpackage  utils
 * @copyright   Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

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

/**
 * A helper class to read and parse "extension" update XML files
over the web
 */
class FOFUtilsUpdateExtension
{
	/**
	 * Reads an "extension" XML update source and returns all listed
update entries.
	 *
	 * If you have a "collection" XML update source you should do
something like this:
	 * $collection = new FOFUtilsUpdateCollection();
	 * $extensionUpdateURL = $collection->getExtensionUpdateSource($url,
'component', 'com_foobar', JVERSION);
	 * $extension = new FOFUtilsUpdateExtension();
	 * $updates = $extension->getUpdatesFromExtension($extensionUpdateURL);
	 *
	 * @param   string  $url  The extension XML update source URL to read from
	 *
	 * @return  array  An array of update entries
	 */
	public function getUpdatesFromExtension($url)
	{
		// Initialise
		$ret = array();

		// Get and parse the XML source
		$downloader = new FOFDownload();
		$xmlSource = $downloader->getFromURL($url);

		try
		{
			$xml = new SimpleXMLElement($xmlSource, LIBXML_NONET);
		}
		catch(Exception $e)
		{
			return $ret;
		}

		// Sanity check
		if (($xml->getName() != 'updates'))
		{
			unset($xml);

			return $ret;
		}

		// Let's populate the list of updates
		/** @var SimpleXMLElement $update */
		foreach ($xml->children() as $update)
		{
			// Sanity check
			if ($update->getName() != 'update')
			{
				continue;
			}

			$entry = array(
				'infourl'			=> array('title' => '',
'url' => ''),
				'downloads'			=> array(),
				'tags'				=> array(),
				'targetplatform'	=> array(),
			);

			$properties = get_object_vars($update);

			foreach ($properties as $nodeName => $nodeContent)
			{
				switch ($nodeName)
				{
					default:
						$entry[$nodeName] = $nodeContent;
						break;

					case 'infourl':
					case 'downloads':
					case 'tags':
					case 'targetplatform':
						break;
				}
			}

			$infourlNode = $update->xpath('infourl');
			$entry['infourl']['title'] =
(string)$infourlNode[0]['title'];
			$entry['infourl']['url'] = (string)$infourlNode[0];

			$downloadNodes = $update->xpath('downloads/downloadurl');
			foreach ($downloadNodes as $downloadNode)
			{
				$entry['downloads'][] = array(
					'type'		=> (string)$downloadNode['type'],
					'format'	=> (string)$downloadNode['format'],
					'url'		=> (string)$downloadNode,
				);
			}

			$tagNodes = $update->xpath('tags/tag');
			foreach ($tagNodes as $tagNode)
			{
				$entry['tags'][] = (string)$tagNode;
			}

			/** @var SimpleXMLElement $targetPlatformNode */
			$targetPlatformNode = $update->xpath('targetplatform');

			$entry['targetplatform']['name'] =
(string)$targetPlatformNode[0]['name'];
			$entry['targetplatform']['version'] =
(string)$targetPlatformNode[0]['version'];
			$client = $targetPlatformNode[0]->xpath('client');
			$entry['targetplatform']['client'] =
(is_array($client) && count($client)) ? (string)$client[0] :
'';
			$folder = $targetPlatformNode[0]->xpath('folder');
			$entry['targetplatform']['folder'] =
is_array($folder) && count($folder) ? (string)$folder[0] :
'';

			$ret[] = $entry;
		}

		unset($xml);

		return $ret;
	}
}update/joomla.php000064400000040260151167373220010031 0ustar00<?php
/**
 * @package     FrameworkOnFramework
 * @subpackage  utils
 * @copyright   Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 * @note        This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
 */

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

/**
 * A helper class which provides update information for the Joomla! CMS
itself. This is slightly different than the
 * regular "extension" files as we need to know if a Joomla!
version is STS, LTS, testing, current and so on.
 */
class FOFUtilsUpdateJoomla extends FOFUtilsUpdateExtension
{
	/**
	 * The source for LTS updates
	 *
	 * @var  string
	 */
	protected static $lts_url =
'http://update.joomla.org/core/list.xml';

	/**
	 * The source for STS updates
	 *
	 * @var  string
	 */
	protected static $sts_url =
'http://update.joomla.org/core/sts/list_sts.xml';

	/**
	 * The source for test release updates
	 *
	 * @var  string
	 */
	protected static $test_url =
'http://update.joomla.org/core/test/list_test.xml';

	/**
	 * Reads an "extension" XML update source and returns all listed
update entries.
	 *
	 * If you have a "collection" XML update source you should do
something like this:
	 * $collection = new CmsupdateHelperCollection();
	 * $extensionUpdateURL = $collection->getExtensionUpdateSource($url,
'component', 'com_foobar', JVERSION);
	 * $extension = new CmsupdateHelperExtension();
	 * $updates = $extension->getUpdatesFromExtension($extensionUpdateURL);
	 *
	 * @param   string $url The extension XML update source URL to read from
	 *
	 * @return  array  An array of update entries
	 */
	public function getUpdatesFromExtension($url)
	{
		// Initialise
		$ret = array();

		// Get and parse the XML source
		$downloader = new FOFDownload();
		$xmlSource  = $downloader->getFromURL($url);

		try
		{
			$xml = new SimpleXMLElement($xmlSource, LIBXML_NONET);
		}
		catch (Exception $e)
		{
			return $ret;
		}

		// Sanity check
		if (($xml->getName() != 'updates'))
		{
			unset($xml);

			return $ret;
		}

		// Let's populate the list of updates
		/** @var SimpleXMLElement $update */
		foreach ($xml->children() as $update)
		{
			// Sanity check
			if ($update->getName() != 'update')
			{
				continue;
			}

			$entry = array(
				'infourl'        => array('title' =>
'', 'url' => ''),
				'downloads'      => array(),
				'tags'           => array(),
				'targetplatform' => array(),
			);

			$properties = get_object_vars($update);

			foreach ($properties as $nodeName => $nodeContent)
			{
				switch ($nodeName)
				{
					default:
						$entry[ $nodeName ] = $nodeContent;
						break;

					case 'infourl':
					case 'downloads':
					case 'tags':
					case 'targetplatform':
						break;
				}
			}

			$infourlNode               = $update->xpath('infourl');
			$entry['infourl']['title'] = (string)
$infourlNode[0]['title'];
			$entry['infourl']['url']   = (string)
$infourlNode[0];

			$downloadNodes = $update->xpath('downloads/downloadurl');
			foreach ($downloadNodes as $downloadNode)
			{
				$entry['downloads'][] = array(
					'type'   => (string) $downloadNode['type'],
					'format' => (string) $downloadNode['format'],
					'url'    => (string) $downloadNode,
				);
			}

			$tagNodes = $update->xpath('tags/tag');
			foreach ($tagNodes as $tagNode)
			{
				$entry['tags'][] = (string) $tagNode;
			}

			/** @var SimpleXMLElement[] $targetPlatformNode */
			$targetPlatformNode = $update->xpath('targetplatform');

			$entry['targetplatform']['name']    = (string)
$targetPlatformNode[0]['name'];
			$entry['targetplatform']['version'] = (string)
$targetPlatformNode[0]['version'];
			$client                             =
$targetPlatformNode[0]->xpath('client');
			$entry['targetplatform']['client']  =
(is_array($client) && count($client)) ? (string) $client[0] :
'';
			$folder                             =
$targetPlatformNode[0]->xpath('folder');
			$entry['targetplatform']['folder']  =
is_array($folder) && count($folder) ? (string) $folder[0] :
'';

			$ret[] = $entry;
		}

		unset($xml);

		return $ret;
	}

	/**
	 * Reads a "collection" XML update source and picks the correct
source URL
	 * for the extension update source.
	 *
	 * @param   string $url      The collection XML update source URL to read
from
	 * @param   string $jVersion Joomla! version to fetch updates for, or null
to use JVERSION
	 *
	 * @return  string  The URL of the extension update source, or empty if no
updates are provided / fetching failed
	 */
	public function getUpdateSourceFromCollection($url, $jVersion = null)
	{
		$provider = new FOFUtilsUpdateCollection();

		return $provider->getExtensionUpdateSource($url, 'file',
'joomla', $jVersion);
	}

	/**
	 * Determines the properties of a version: STS/LTS, normal or testing
	 *
	 * @param   string $jVersion       The version number to check
	 * @param   string $currentVersion The current Joomla! version number
	 *
	 * @return  array  The properties analysis
	 */
	public function getVersionProperties($jVersion, $currentVersion = null)
	{
		// Initialise
		$ret = array(
			'lts'     => true,
			// Is this an LTS release? False means STS.
			'current' => false,
			// Is this a release in the $currentVersion branch?
			'upgrade' => 'none',
			// Upgrade relation of $jVersion to $currentVersion: 'none'
(can't upgrade), 'lts' (next or current LTS),
'sts' (next or current STS) or 'current' (same release,
no upgrade available)
			'testing' => false,
			// Is this a testing (alpha, beta, RC) release?
		);

		// Get the current version if none is defined
		if (is_null($currentVersion))
		{
			$currentVersion = JVERSION;
		}

		// Sanitise version numbers
		$sameVersion    = $jVersion == $currentVersion;
		$jVersion       = $this->sanitiseVersion($jVersion);
		$currentVersion = $this->sanitiseVersion($currentVersion);
		$sameVersion    = $sameVersion || ($jVersion == $currentVersion);

		// Get the base version
		$baseVersion = substr($jVersion, 0, 3);

		// Get the minimum and maximum current version numbers
		$current_minimum = substr($currentVersion, 0, 3);
		$current_maximum = $current_minimum . '.9999';

		// Initialise STS/LTS version numbers
		$sts_minimum = false;
		$sts_maximum = false;
		$lts_minimum = false;

		// Is it an LTS or STS release?
		switch ($baseVersion)
		{
			case '1.5':
				$ret['lts'] = true;
				break;

			case '1.6':
				$ret['lts']  = false;
				$sts_minimum = '1.7';
				$sts_maximum = '1.7.999';
				$lts_minimum = '2.5';
				break;

			case '1.7':
				$ret['lts']  = false;
				$sts_minimum = false;
				$lts_minimum = '2.5';
				break;

			case '2.5':
				$ret['lts']  = true;
				$sts_minimum = false;
				$lts_minimum = '2.5';
				break;

			default:
				$majorVersion = (int) substr($jVersion, 0, 1);
				//$minorVersion = (int) substr($jVersion, 2, 1);

				$ret['lts']  = true;
				$sts_minimum = false;
				$lts_minimum = $majorVersion . '.0';
				break;
		}

		// Is it a current release?
		if (version_compare($jVersion, $current_minimum, 'ge')
&& version_compare($jVersion, $current_maximum, 'le'))
		{
			$ret['current'] = true;
		}

		// Is this a testing release?
		$versionParts    = explode('.', $jVersion);
		$lastVersionPart = array_pop($versionParts);

		if (in_array(substr($lastVersionPart, 0, 1), array('a',
'b')))
		{
			$ret['testing'] = true;
		}
		elseif (substr($lastVersionPart, 0, 2) == 'rc')
		{
			$ret['testing'] = true;
		}
		elseif (substr($lastVersionPart, 0, 3) == 'dev')
		{
			$ret['testing'] = true;
		}

		// Find the upgrade relation of $jVersion to $currentVersion
		if (version_compare($jVersion, $currentVersion, 'eq'))
		{
			$ret['upgrade'] = 'current';
		}
		elseif (($sts_minimum !== false) && version_compare($jVersion,
$sts_minimum, 'ge') && version_compare($jVersion,
$sts_maximum, 'le'))
		{
			$ret['upgrade'] = 'sts';
		}
		elseif (($lts_minimum !== false) && version_compare($jVersion,
$lts_minimum, 'ge'))
		{
			$ret['upgrade'] = 'lts';
		}
		elseif ($baseVersion == $current_minimum)
		{
			$ret['upgrade'] = $ret['lts'] ? 'lts' :
'sts';
		}
		else
		{
			$ret['upgrade'] = 'none';
		}

		if ($sameVersion)
		{
			$ret['upgrade'] = 'none';
		}

		return $ret;
	}


	/**
	 * Filters a list of updates, making sure they apply to the specified CMS
	 * release.
	 *
	 * @param   array  $updates  A list of update records returned by the
getUpdatesFromExtension method
	 * @param   string $jVersion The current Joomla! version number
	 *
	 * @return  array  A filtered list of updates. Each update record also
includes version relevance information.
	 */
	public function filterApplicableUpdates($updates, $jVersion = null)
	{
		if (empty($jVersion))
		{
			$jVersion = JVERSION;
		}

		$versionParts          = explode('.', $jVersion, 4);
		$platformVersionMajor  = $versionParts[0];
		$platformVersionMinor  = $platformVersionMajor . '.' .
$versionParts[1];
		$platformVersionNormal = $platformVersionMinor . '.' .
$versionParts[2];
		//$platformVersionFull   = (count($versionParts) > 3) ?
$platformVersionNormal . '.' . $versionParts[3] :
$platformVersionNormal;

		$ret = array();

		foreach ($updates as $update)
		{
			// Check each update for platform match
			if (strtolower($update['targetplatform']['name']) !=
'joomla')
			{
				continue;
			}

			$targetPlatformVersion =
$update['targetplatform']['version'];

			if (!preg_match('/' . $targetPlatformVersion . '/',
$platformVersionMinor))
			{
				continue;
			}

			// Get some information from the version number
			$updateVersion     = $update['version'];
			$versionProperties = $this->getVersionProperties($updateVersion,
$jVersion);

			if ($versionProperties['upgrade'] == 'none')
			{
				continue;
			}

			// The XML files are ill-maintained. Maybe we already have this update?
			if (!array_key_exists($updateVersion, $ret))
			{
				$ret[ $updateVersion ] = array_merge($update, $versionProperties);
			}
		}

		return $ret;
	}

	/**
	 * Joomla! has a lousy track record in naming its alpha, beta and release
	 * candidate releases. The convention used seems to be "what the hell
the
	 * current package maintainer thinks looks better". This method tries
to
	 * figure out what was in the mind of the maintainer and translate the
	 * funky version number to an actual PHP-format version string.
	 *
	 * @param   string $version The whatever-format version number
	 *
	 * @return  string  A standard formatted version number
	 */
	public function sanitiseVersion($version)
	{
		$test                   = strtolower($version);
		$alphaQualifierPosition = strpos($test, 'alpha-');
		$betaQualifierPosition  = strpos($test, 'beta-');
		$betaQualifierPosition2 = strpos($test, '-beta');
		$rcQualifierPosition    = strpos($test, 'rc-');
		$rcQualifierPosition2 = strpos($test, '-rc');
		$rcQualifierPosition3 = strpos($test, 'rc');
		$devQualifiedPosition   = strpos($test, 'dev');

		if ($alphaQualifierPosition !== false)
		{
			$betaRevision = substr($test, $alphaQualifierPosition + 6);
			if (!$betaRevision)
			{
				$betaRevision = 1;
			}
			$test = substr($test, 0, $alphaQualifierPosition) . '.a' .
$betaRevision;
		}
		elseif ($betaQualifierPosition !== false)
		{
			$betaRevision = substr($test, $betaQualifierPosition + 5);
			if (!$betaRevision)
			{
				$betaRevision = 1;
			}
			$test = substr($test, 0, $betaQualifierPosition) . '.b' .
$betaRevision;
		}
		elseif ($betaQualifierPosition2 !== false)
		{
			$betaRevision = substr($test, $betaQualifierPosition2 + 5);

			if (!$betaRevision)
			{
				$betaRevision = 1;
			}

			$test = substr($test, 0, $betaQualifierPosition2) . '.b' .
$betaRevision;
		}
		elseif ($rcQualifierPosition !== false)
		{
			$betaRevision = substr($test, $rcQualifierPosition + 5);
			if (!$betaRevision)
			{
				$betaRevision = 1;
			}
			$test = substr($test, 0, $rcQualifierPosition) . '.rc' .
$betaRevision;
		}
		elseif ($rcQualifierPosition2 !== false)
		{
			$betaRevision = substr($test, $rcQualifierPosition2 + 3);

			if (!$betaRevision)
			{
				$betaRevision = 1;
			}

			$test = substr($test, 0, $rcQualifierPosition2) . '.rc' .
$betaRevision;
		}
		elseif ($rcQualifierPosition3 !== false)
		{
			$betaRevision = substr($test, $rcQualifierPosition3 + 5);

			if (!$betaRevision)
			{
				$betaRevision = 1;
			}

			$test = substr($test, 0, $rcQualifierPosition3) . '.rc' .
$betaRevision;
		}
		elseif ($devQualifiedPosition !== false)
		{
			$betaRevision = substr($test, $devQualifiedPosition + 6);
			if (!$betaRevision)
			{
				$betaRevision = '';
			}
			$test = substr($test, 0, $devQualifiedPosition) . '.dev' .
$betaRevision;
		}

		return $test;
	}

	/**
	 * Reloads the list of all updates available for the specified Joomla!
version
	 * from the network.
	 *
	 * @param    array  $sources  The enabled sources to look into
	 * @param    string $jVersion The Joomla! version we are checking updates
for
	 *
	 * @return   array  A list of updates for the installed, current, lts and
sts versions
	 */
	public function getUpdates($sources = array(), $jVersion = null)
	{
		// Make sure we have a valid list of sources
		if (empty($sources) || !is_array($sources))
		{
			$sources = array();
		}

		$defaultSources = array('lts' => true, 'sts' =>
true, 'test' => true, 'custom' => '');

		$sources = array_merge($defaultSources, $sources);

		// Use the current JVERSION if none is specified
		if (empty($jVersion))
		{
			$jVersion = JVERSION;
		}

		// Get the current branch' min/max versions
		$versionParts      = explode('.', $jVersion, 4);
		$currentMinVersion = $versionParts[0] . '.' . $versionParts[1];
		$currentMaxVersion = $versionParts[0] . '.' . $versionParts[1]
. '.9999';


		// Retrieve all updates
		$allUpdates = array();

		foreach ($sources as $source => $value)
		{
			if (($value === false) || empty($value))
			{
				continue;
			}

			switch ($source)
			{
				case 'lts':
					$url = self::$lts_url;
					break;

				case 'sts':
					$url = self::$sts_url;
					break;

				case 'test':
					$url = self::$test_url;
					break;

				default:
				case 'custom':
					$url = $value;
					break;
			}

			$url = $this->getUpdateSourceFromCollection($url, $jVersion);

			if (!empty($url))
			{
				$updates = $this->getUpdatesFromExtension($url);

				if (!empty($updates))
				{
					$applicableUpdates = $this->filterApplicableUpdates($updates,
$jVersion);

					if (!empty($applicableUpdates))
					{
						$allUpdates = array_merge($allUpdates, $applicableUpdates);
					}
				}
			}
		}

		$ret = array(
			// Currently installed version (used to reinstall, if available)
			'installed' => array(
				'version' => '',
				'package' => '',
				'infourl' => '',
			),
			// Current branch
			'current'   => array(
				'version' => '',
				'package' => '',
				'infourl' => '',
			),
			// Upgrade to STS release
			'sts'       => array(
				'version' => '',
				'package' => '',
				'infourl' => '',
			),
			// Upgrade to LTS release
			'lts'       => array(
				'version' => '',
				'package' => '',
				'infourl' => '',
			),
			// Upgrade to LTS release
			'test'      => array(
				'version' => '',
				'package' => '',
				'infourl' => '',
			),
		);

		foreach ($allUpdates as $update)
		{
			$sections = array();

			if ($update['upgrade'] == 'current')
			{
				$sections[0] = 'installed';
			}
			elseif (version_compare($update['version'],
$currentMinVersion, 'ge') &&
version_compare($update['version'], $currentMaxVersion,
'le'))
			{
				$sections[0] = 'current';
			}
			else
			{
				$sections[0] = '';
			}

			$sections[1] = $update['lts'] ? 'lts' :
'sts';

			if ($update['testing'])
			{
				$sections = array('test');
			}

			foreach ($sections as $section)
			{
				if (empty($section))
				{
					continue;
				}

				$existingVersionForSection = $ret[ $section ]['version'];

				if (empty($existingVersionForSection))
				{
					$existingVersionForSection = '0.0.0';
				}

				if (version_compare($update['version'],
$existingVersionForSection, 'ge'))
				{
					$ret[ $section ]['version'] = $update['version'];
					$ret[ $section ]['package'] =
$update['downloads'][0]['url'];
					$ret[ $section ]['infourl'] =
$update['infourl']['url'];
				}
			}
		}

		// Catch the case when the latest current branch version is the installed
version (up to date site)
		if (empty($ret['current']['version']) &&
!empty($ret['installed']['version']))
		{
			$ret['current'] = $ret['installed'];
		}

		return $ret;
	}
}update/update.php000064400000101122151167373220010025 0ustar00<?php
/**
 * @package     FrameworkOnFramework
 * @subpackage  utils
 * @copyright   Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
 * @license     GNU General Public License version 2 or later; see
LICENSE.txt
 */

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

if (version_compare(JVERSION, '2.5.0', 'lt'))
{
	jimport('joomla.updater.updater');
}

/**
 * A helper Model to interact with Joomla!'s extensions update feature
 */
class FOFUtilsUpdate extends FOFModel
{
	/** @var JUpdater The Joomla! updater object */
	protected $updater = null;

	/** @var int The extension_id of this component */
	protected $extension_id = 0;

	/** @var string The currently installed version, as reported by the
#__extensions table */
	protected $version = 'dev';

	/** @var string The machine readable name of the component e.g.
com_something */
	protected $component = 'com_foobar';

	/** @var string The human readable name of the component e.g. Your
Component's Name. Used for emails. */
	protected $componentDescription = 'Foobar';

	/** @var string The URL to the component's update XML stream */
	protected $updateSite = null;

	/** @var string The name to the component's update site (description
of the update XML stream) */
	protected $updateSiteName = null;

	/** @var string The extra query to append to (commercial) components'
download URLs */
	protected $extraQuery = null;

	/** @var string The common parameters' key, used for storing data in
the #__akeeba_common table */
	protected $commonKey = 'foobar';

	/**
	 * The common parameters table. It's a simple table with key(VARCHAR)
and value(LONGTEXT) fields.
	 * Here is an example MySQL CREATE TABLE command to make this kind of
table:
	 *
	 * CREATE TABLE `#__akeeba_common` (
	 * 	`key` varchar(255) NOT NULL,
	 * 	`value` longtext NOT NULL,
	 * 	PRIMARY KEY (`key`)
	 * 	) DEFAULT COLLATE utf8_general_ci CHARSET=utf8;
	 *
	 * @var  string
	 */
	protected $commonTable = '#__akeeba_common';

	/**
	 * Subject of the component update emails
	 *
	 * @var  string
	 */
	protected $updateEmailSubject = 'THIS EMAIL IS SENT FROM YOUR SITE
"[SITENAME]" - Update available for [COMPONENT], new version
[VERSION]';

	/**
	 * Body of the component update email
	 *
	 * @var  string
	 */
	protected $updateEmailBody= <<< ENDBLOCK
This email IS NOT sent by the authors of [COMPONENT].
It is sent automatically by your own site, [SITENAME].

================================================================================
UPDATE INFORMATION
================================================================================

Your site has determined that there is an updated version of [COMPONENT]
available for download.

New version number: [VERSION]

This email is sent to you by your site to remind you of this fact. The
authors
of the software will never contact you about available updates.

================================================================================
WHY AM I RECEIVING THIS EMAIL?
================================================================================

This email has been automatically sent by a CLI script or Joomla! plugin
you, or
the person who built or manages your site, has installed and explicitly
activated. This script or plugin looks for updated versions of the software
and
sends an email notification to all Super Users. You will receive several
similar
emails from your site, up to 6 times per day, until you either update the
software or disable these emails.

To disable these emails, please contact your site administrator.

If you do not understand what this means, please do not contact the authors
of
the software. They are NOT sending you this email and they cannot help you.
Instead, please contact the person who built or manages your site.

================================================================================
WHO SENT ME THIS EMAIL?
================================================================================

This email is sent to you by your own site, [SITENAME]
ENDBLOCK;

	/**
	 * Public constructor. Initialises the protected members as well. Useful
$config keys:
	 * update_component		The component name, e.g. com_foobar
	 * update_version		The default version if the manifest cache is unreadable
	 * update_site			The URL to the component's update XML stream
	 * update_extraquery	The extra query to append to (commercial)
components' download URLs
	 * update_sitename		The update site's name (description)
	 *
	 * @param array $config
	 */
	public function __construct($config = array())
	{
		parent::__construct($config);

		// Get an instance of the updater class
		$this->updater = JUpdater::getInstance();

		// Get the component name
		if (isset($config['update_component']))
		{
			$this->component = $config['update_component'];
		}
		else
		{
			$this->component = $this->input->getCmd('option',
'');
		}

		// Get the component description
		if (isset($config['update_component_description']))
		{
			$this->component = $config['update_component_description'];
		}
		else
		{
			// Try to auto-translate (hopefully you've loaded the language
files)
			$key = strtoupper($this->component);
			$description = JText::_($key);
		}

		// Get the component version
		if (isset($config['update_version']))
		{
			$this->version = $config['update_version'];
		}

		// Get the common key
		if (isset($config['common_key']))
		{
			$this->commonKey = $config['common_key'];
		}
		else
		{
			// Convert com_foobar, pkg_foobar etc to "foobar"
			$this->commonKey = substr($this->component, 4);
		}

		// Get the update site
		if (isset($config['update_site']))
		{
			$this->updateSite = $config['update_site'];
		}

		// Get the extra query
		if (isset($config['update_extraquery']))
		{
			$this->extraQuery = $config['update_extraquery'];
		}

		// Get the update site's name
		if (isset($config['update_sitename']))
		{
			$this->updateSiteName = $config['update_sitename'];
		}

		// Find the extension ID
		$db = FOFPlatform::getInstance()->getDbo();
		$query = $db->getQuery(true)
			->select('*')
			->from($db->qn('#__extensions'))
			->where($db->qn('type') . ' = ' .
$db->q('component'))
			->where($db->qn('element') . ' = ' .
$db->q($this->component));
		$db->setQuery($query);
		$extension = $db->loadObject();

		if (is_object($extension))
		{
			$this->extension_id = $extension->extension_id;
			$data = json_decode($extension->manifest_cache, true);

			if (isset($data['version']))
			{
				$this->version = $data['version'];
			}
		}
	}

	/**
	 * Retrieves the update information of the component, returning an array
with the following keys:
	 *
	 * hasUpdate	True if an update is available
	 * version		The version of the available update
	 * infoURL		The URL to the download page of the update
	 *
	 * @param   bool    $force            Set to true if you want to forcibly
reload the update information
	 * @param   string  $preferredMethod  Preferred update method:
'joomla' or 'classic'
	 *
	 * @return  array  See the method description for more information
	 */
	public function getUpdates($force = false, $preferredMethod = null)
	{
		// Default response (no update)
		$updateResponse = array(
			'hasUpdate' => false,
			'version'   => '',
			'infoURL'   => '',
			'downloadURL' => '',
		);

		if (empty($this->extension_id))
		{
			return $updateResponse;
		}

		$updateRecord = $this->findUpdates($force, $preferredMethod);

		// If we have an update record in the database return the information
found there
		if (is_object($updateRecord))
		{
			$updateResponse = array(
				'hasUpdate' => true,
				'version'   => $updateRecord->version,
				'infoURL'   => $updateRecord->infourl,
				'downloadURL' => $updateRecord->downloadurl,
			);
		}

		return $updateResponse;
	}

	/**
	 * Find the available update record object. If we're at the latest
version it will return null.
	 *
	 * Please see getUpdateMethod for information on how the $preferredMethod
is handled and what it means.
	 *
	 * @param   bool    $force            Should I forcibly reload the updates
from the server?
	 * @param   string  $preferredMethod  Preferred update method:
'joomla' or 'classic'
	 *
	 * @return  \stdClass|null
	 */
	public function findUpdates($force, $preferredMethod = null)
	{
		$preferredMethod = $this->getUpdateMethod($preferredMethod);

		switch ($preferredMethod)
		{
			case 'joomla':
				return $this->findUpdatesJoomla($force);
				break;

			default:
			case 'classic':
				return $this->findUpdatesClassic($force);
				break;
		}
	}

	/**
	 * Gets the update site Ids for our extension.
	 *
	 * @return 	mixed	An array of Ids or null if the query failed.
	 */
	public function getUpdateSiteIds()
	{
		$db = FOFPlatform::getInstance()->getDbo();
		$query = $db->getQuery(true)
					->select($db->qn('update_site_id'))
					->from($db->qn('#__update_sites_extensions'))
					->where($db->qn('extension_id') . ' = ' .
$db->q($this->extension_id));
		$db->setQuery($query);
		$updateSiteIds = $db->loadColumn(0);

		return $updateSiteIds;
	}

	/**
	 * Get the currently installed version as reported by the #__extensions
table
	 *
	 * @return  string
	 */
	public function getVersion()
	{
		return $this->version;
	}

	/**
	 * Returns the name of the component, e.g. com_foobar
	 *
	 * @return  string
	 */
	public function getComponentName()
	{
		return $this->component;
	}

	/**
	 * Returns the human readable component name, e.g. Foobar Component
	 *
	 * @return  string
	 */
	public function getComponentDescription()
	{
		return $this->componentDescription;
	}

	/**
	 * Returns the numeric extension ID for the component
	 *
	 * @return  int
	 */
	public function getExtensionId()
	{
		return $this->extension_id;
	}

	/**
	 * Returns the update site URL, i.e. the URL to the XML update stream
	 *
	 * @return  string
	 */
	public function getUpdateSite()
	{
		return $this->updateSite;
	}

	/**
	 * Returns the human readable description of the update site
	 *
	 * @return  string
	 */
	public function getUpdateSiteName()
	{
		return $this->updateSiteName;
	}

	/**
	 * Override the currently installed version as reported by the
#__extensions table
	 *
	 * @param  string  $version
	 */
	public function setVersion($version)
	{
		$this->version = $version;
	}

	/**
	 * Refreshes the Joomla! update sites for this extension as needed
	 *
	 * @return  void
	 */
	public function refreshUpdateSite()
	{
		// Joomla! 1.5 does not have update sites.
		if (version_compare(JVERSION, '1.6.0', 'lt'))
		{
			return;
		}

		if (empty($this->extension_id))
		{
			return;
		}

		// Remove obsolete update sites that don't match our extension ID
but match our name or update site location
		$this->removeObsoleteUpdateSites();

		// Create the update site definition we want to store to the database
		$update_site = array(
				'name'		=> $this->updateSiteName,
				'type'		=> 'extension',
				'location'	=> $this->updateSite,
				'enabled'	=> 1,
				'last_check_timestamp'	=> 0,
				'extra_query'	=> $this->extraQuery
		);

		// Get a reference to the db driver
		$db = FOFPlatform::getInstance()->getDbo();

		// Get the #__update_sites columns
		$columns = $db->getTableColumns('#__update_sites', true);

		if (version_compare(JVERSION, '3.2.0', 'lt') ||
!array_key_exists('extra_query', $columns))
		{
			unset($update_site['extra_query']);
		}

		if (version_compare(JVERSION, '2.5.0', 'lt') ||
!array_key_exists('extra_query', $columns))
		{
			unset($update_site['last_check_timestamp']);
		}

		// Get the update sites for our extension
		$updateSiteIds = $this->getUpdateSiteIds();

		if (empty($updateSiteIds))
		{
			$updateSiteIds = array();
		}

		/** @var boolean $needNewUpdateSite Do I need to create a new update
site? */
		$needNewUpdateSite = true;

		/** @var int[] $deleteOldSites Old Site IDs to delete */
		$deleteOldSites = array();

		// Loop through all update sites
		foreach ($updateSiteIds as $id)
		{
			$query = $db->getQuery(true)
						->select('*')
						->from($db->qn('#__update_sites'))
						->where($db->qn('update_site_id') . ' = ' .
$db->q($id));
			$db->setQuery($query);
			$aSite = $db->loadObject();

			if (empty($aSite))
			{
				// Update site is now up-to-date, don't need to refresh it
anymore.
				continue;
			}

			// We have an update site that looks like ours
			if ($needNewUpdateSite && ($aSite->name ==
$update_site['name']) && ($aSite->location ==
$update_site['location']))
			{
				$needNewUpdateSite = false;
				$mustUpdate = false;

				// Is it enabled? If not, enable it.
				if (!$aSite->enabled)
				{
					$mustUpdate = true;
					$aSite->enabled = 1;
				}

				// Do we have the extra_query property (J 3.2+) and does it match?
				if (property_exists($aSite, 'extra_query') &&
isset($update_site['extra_query'])
					&& ($aSite->extra_query !=
$update_site['extra_query']))
				{
					$mustUpdate = true;
					$aSite->extra_query = $update_site['extra_query'];
				}

				// Update the update site if necessary
				if ($mustUpdate)
				{
					$db->updateObject('#__update_sites', $aSite,
'update_site_id', true);
				}

				continue;
			}

			// In any other case we need to delete this update site, it's
obsolete
			$deleteOldSites[] = $aSite->update_site_id;
		}

		if (!empty($deleteOldSites))
		{
			try
			{
				$obsoleteIDsQuoted = array_map(array($db, 'quote'),
$deleteOldSites);

				// Delete update sites
				$query = $db->getQuery(true)
							->delete('#__update_sites')
							->where($db->qn('update_site_id') . ' IN
(' . implode(',', $obsoleteIDsQuoted) . ')');
				$db->setQuery($query)->execute();

				// Delete update sites to extension ID records
				$query = $db->getQuery(true)
							->delete('#__update_sites_extensions')
							->where($db->qn('update_site_id') . ' IN
(' . implode(',', $obsoleteIDsQuoted) . ')');
				$db->setQuery($query)->execute();
			}
			catch (\Exception $e)
			{
				// Do nothing on failure
				return;
			}

		}

		// Do we still need to create a new update site?
		if ($needNewUpdateSite)
		{
			// No update sites defined. Create a new one.
			$newSite = (object)$update_site;
			$db->insertObject('#__update_sites', $newSite);

			$id                  = $db->insertid();
			$updateSiteExtension = (object)array(
				'update_site_id' => $id,
				'extension_id'   => $this->extension_id,
			);
			$db->insertObject('#__update_sites_extensions',
$updateSiteExtension);
		}
	}

	/**
	 * Removes any update sites which go by the same name or the same location
as our update site but do not match the
	 * extension ID.
	 */
	public function removeObsoleteUpdateSites()
	{
		$db = $this->getDbo();

		// Get update site IDs
		$updateSiteIDs = $this->getUpdateSiteIds();

		// Find update sites where the name OR the location matches BUT they are
not one of the update site IDs
		$query = $db->getQuery(true)
					->select($db->qn('update_site_id'))
					->from($db->qn('#__update_sites'))
					->where(
						'((' . $db->qn('name') . ' = ' .
$db->q($this->updateSiteName) . ') OR ' .
						'(' . $db->qn('location') . ' = ' .
$db->q($this->updateSite) . '))'
					);

		if (!empty($updateSiteIDs))
		{
			$updateSitesQuoted = array_map(array($db, 'quote'),
$updateSiteIDs);
			$query->where($db->qn('update_site_id') . ' NOT IN
(' . implode(',', $updateSitesQuoted) . ')');
		}

		try
		{
			$ids = $db->setQuery($query)->loadColumn();

			if (!empty($ids))
			{
				$obsoleteIDsQuoted = array_map(array($db, 'quote'), $ids);

				// Delete update sites
				$query = $db->getQuery(true)
							->delete('#__update_sites')
							->where($db->qn('update_site_id') . ' IN
(' . implode(',', $obsoleteIDsQuoted) . ')');
				$db->setQuery($query)->execute();

				// Delete update sites to extension ID records
				$query = $db->getQuery(true)
							->delete('#__update_sites_extensions')
							->where($db->qn('update_site_id') . ' IN
(' . implode(',', $obsoleteIDsQuoted) . ')');
				$db->setQuery($query)->execute();
			}
		}
		catch (\Exception $e)
		{
			// Do nothing on failure
			return;
		}
	}

	/**
	 * Get the update method we should use, 'joomla' or
'classic'
	 *
	 * You can defined the preferred update method: 'joomla' uses
JUpdater whereas 'classic' handles update caching and
	 * parsing internally. If you are on Joomla! 3.1 or earlier this option is
forced to 'classic' since these old
	 * Joomla! versions couldn't handle updates of commercial components
correctly (that's why I contributed the fix to
	 * that problem, the extra_query field that's present in Joomla! 3.2
onwards).
	 *
	 * If 'classic' is defined then it will be used in *all* Joomla!
versions. It's the most stable method for fetching
	 * update information.
	 *
	 * @param   string  $preferred  Preferred update method. One of
'joomla' or 'classic'.
	 *
	 * @return  string
	 */
	public function getUpdateMethod($preferred = null)
	{
		$method = $preferred;

		// Make sure the update fetch method is valid, otherwise load the
component's "update_method" parameter.
		$validMethods = array('joomla', 'classic');

		if (!in_array($method, $validMethods))
		{
			$method =
FOFUtilsConfigHelper::getComponentConfigurationValue($this->component,
'update_method', 'joomla');
		}

		// We can't handle updates using Joomla!'s extensions updater
in Joomla! 3.1 and earlier
		if (($method == 'joomla') && version_compare(JVERSION,
'3.2.0', 'lt'))
		{
			$method = 'classic';
		}

		return $method;
	}

	/**
	 * Find the available update record object. If we're at the latest
version it will return null.
	 *
	 * @param   bool  $force  Should I forcibly reload the updates from the
server?
	 *
	 * @return  \stdClass|null
	 */
	protected function findUpdatesJoomla($force = false)
	{
		$db = FOFPlatform::getInstance()->getDbo();

		// If we are forcing the reload, set the last_check_timestamp to 0
		// and remove cached component update info in order to force a reload
		if ($force)
		{
			// Find the update site IDs
			$updateSiteIds = $this->getUpdateSiteIds();

			if (empty($updateSiteIds))
			{
				return null;
			}

			// Set the last_check_timestamp to 0
			if (version_compare(JVERSION, '2.5.0', 'ge'))
			{
				$query = $db->getQuery(true)
							->update($db->qn('#__update_sites'))
							->set($db->qn('last_check_timestamp') . ' =
' . $db->q('0'))
							->where($db->qn('update_site_id') .' IN
('.implode(', ', $updateSiteIds).')');
				$db->setQuery($query);
				$db->execute();
			}

			// Remove cached component update info from #__updates
			$query = $db->getQuery(true)
						->delete($db->qn('#__updates'))
						->where($db->qn('update_site_id') .' IN
('.implode(', ', $updateSiteIds).')');
			$db->setQuery($query);
			$db->execute();
		}

		// Use the update cache timeout specified in com_installer
		$timeout = 3600 *
FOFUtilsConfigHelper::getComponentConfigurationValue('com_installer',
'cachetimeout', '6');

		// Load any updates from the network into the #__updates table
		$this->updater->findUpdates($this->extension_id, $timeout);

		// Get the update record from the database
		$query = $db->getQuery(true)
					->select('*')
					->from($db->qn('#__updates'))
					->where($db->qn('extension_id') . ' = ' .
$db->q($this->extension_id));
		$db->setQuery($query);

		try
		{
			$updateObject = $db->loadObject();
		}
		catch (Exception $e)
		{
			return null;
		}

		if (!is_object($updateObject))
		{
			return null;
		}

		$updateObject->downloadurl = '';

		JLoader::import('joomla.updater.update');

		if (class_exists('JUpdate'))
		{
			$update = new JUpdate();
			$update->loadFromXML($updateObject->detailsurl);

			if (isset($update->get('downloadurl')->_data))
			{
				$url = trim($update->downloadurl->_data);

				$extra_query = isset($updateObject->extra_query) ?
$updateObject->extra_query : $this->extraQuery;

				if ($extra_query)
				{
					if (strpos($url, '?') === false)
					{
						$url .= '?';
					}
					else
					{
						$url .= '&amp;';
					}

					$url .= $extra_query;
				}

				$updateObject->downloadurl = $url;
			}
		}

		return $updateObject;
	}

	/**
	 * Find the available update record object. If we're at the latest
version return null.
	 *
	 * @param   bool  $force  Should I forcibly reload the updates from the
server?
	 *
	 * @return  \stdClass|null
	 */
	protected function findUpdatesClassic($force = false)
	{
		$allUpdates = $this->loadUpdatesClassic($force);

		if (empty($allUpdates))
		{
			return null;
		}

		$bestVersion = '0.0.0';
		$bestUpdate = null;
		$bestUpdateObject = null;

		foreach($allUpdates as $update)
		{
			if (!isset($update['version']))
			{
				continue;
			}

			if (version_compare($bestVersion, $update['version'],
'lt'))
			{
				$bestVersion = $update['version'];
				$bestUpdate = $update;
			}
		}

        // If the current version is newer or equal to the best one, unset
it. Otherwise the user will be always prompted to update
        if(version_compare($this->version, $bestVersion,
'ge'))
        {
            $bestUpdate = null;
            $bestVersion = '0.0.0';
        }

		if (!is_null($bestUpdate))
		{
			$url = '';

			if (isset($bestUpdate['downloads']) &&
isset($bestUpdate['downloads'][0])
			&&
isset($bestUpdate['downloads'][0]['url']))
			{
				$url = $bestUpdate['downloads'][0]['url'];
			}

			if ($this->extraQuery)
			{
				if (strpos($url, '?') === false)
				{
					$url .= '?';
				}
				else
				{
					$url .= '&amp;';
				}

				$url .= $this->extraQuery;
			}

			$bestUpdateObject = (object)array(
					'update_id'      => 0,
					'update_site_id' => 0,
					'extension_id'   => $this->extension_id,
					'name'           => $this->updateSiteName,
					'description'    => $bestUpdate['description'],
					'element'        => $bestUpdate['element'],
					'type'           => $bestUpdate['type'],
					'folder'         =>
count($bestUpdate['folder']) ? $bestUpdate['folder'][0]
: '',
					'client_id'      =>
isset($bestUpdate['client']) ? $bestUpdate['client'] :
0,
					'version'        => $bestUpdate['version'],
					'data'           => '',
					'detailsurl'     => $this->updateSite,
					'infourl'        =>
$bestUpdate['infourl']['url'],
					'extra_query'    => $this->extraQuery,
					'downloadurl'	 => $url,
			);
		}

		return $bestUpdateObject;
	}

	/**
	 * Load all available updates without going through JUpdate
	 *
	 * @param   bool  $force  Should I forcibly reload the updates from the
server?
	 *
	 * @return  array
	 */
	protected function loadUpdatesClassic($force = false)
	{
		// Is the cache busted? If it is I set $force = true to make sure I
download fresh updates
		if (!$force)
		{
			// Get the cache timeout. On older Joomla! installations it will always
default to 6 hours.
			$timeout = 3600 *
FOFUtilsConfigHelper::getComponentConfigurationValue('com_installer',
'cachetimeout', '6');

			// Do I need to check for updates?
			$lastCheck = $this->getCommonParameter('lastcheck', 0);
			$now = time();

			if (($now - $lastCheck) >= $timeout)
			{
				$force = true;
			}
		}

		// Get the cached JSON-encoded updates list
		$rawUpdates = $this->getCommonParameter('allUpdates',
'');

		// Am I forced to reload the XML file (explicitly or because the cache is
busted)?
		if ($force)
		{
			// Set the timestamp
			$now = time();
			$this->setCommonParameter('lastcheck', $now);

			// Get all available updates
			$updateHelper = new FOFUtilsUpdateExtension();
			$updates =
$updateHelper->getUpdatesFromExtension($this->updateSite);

			// Save the raw updates list in the database
			$rawUpdates = json_encode($updates);
			$this->setCommonParameter('allUpdates', $rawUpdates);
		}

		// Decode the updates list
		$updates = json_decode($rawUpdates, true);

		// Walk through the updates and find the ones compatible with our Joomla!
and PHP version
		$compatibleUpdates = array();

		// Get the Joomla! version family (e.g. 2.5)
		$jVersion = JVERSION;
		$jVersionParts = explode('.', $jVersion);
		$jVersionShort = $jVersionParts[0] . '.' . $jVersionParts[1];

		// Get the PHP version family (e.g. 5.6)
		$phpVersion = PHP_VERSION;
		$phpVersionParts = explode('.', $phpVersion);
		$phpVersionShort = $phpVersionParts[0] . '.' .
$phpVersionParts[1];

		foreach ($updates as $update)
		{
			// No platform?
			if (!isset($update['targetplatform']))
			{
				continue;
			}

			// Wrong platform?
			if ($update['targetplatform']['name'] !=
'joomla')
			{
				continue;
			}

			// Get the target Joomla! version
			$targetJoomlaVersion =
$update['targetplatform']['version'];
			$targetVersionParts = explode('.', $targetJoomlaVersion);
			$targetVersionShort = $targetVersionParts[0] . '.' .
$targetVersionParts[1];

			// The target version MUST be in the same Joomla! branch
			if ($jVersionShort != $targetVersionShort)
			{
				continue;
			}

			// If the target version is major.minor.revision we must make sure our
current JVERSION is AT LEAST equal to that.
			if (version_compare($targetJoomlaVersion, JVERSION, 'gt'))
			{
				continue;
			}

			// Do I have target PHP versions?
			if (isset($update['ars-phpcompat']))
			{
				$phpCompatible = false;

				foreach ($update['ars-phpcompat'] as $entry)
				{
					// Get the target PHP version family
					$targetPHPVersion =
$entry['@attributes']['version'];
					$targetPHPVersionParts = explode('.', $targetPHPVersion);
					$targetPHPVersionShort = $targetPHPVersionParts[0] . '.' .
$targetPHPVersionParts[1];

					// The target PHP version MUST be in the same PHP branch
					if ($phpVersionShort != $targetPHPVersionShort)
					{
						continue;
					}

					// If the target version is major.minor.revision we must make sure our
current PHP_VERSION is AT LEAST equal to that.
					if (version_compare($targetPHPVersion, PHP_VERSION, 'gt'))
					{
						continue;
					}

					$phpCompatible = true;
					break;
				}

				if (!$phpCompatible)
				{
					continue;
				}
			}

			// All checks pass. Add this update to the list of compatible updates.
			$compatibleUpdates[] = $update;
		}

		return $compatibleUpdates;
	}

	/**
	 * Get a common parameter from the #__akeeba_common table
	 *
	 * @param   string  $key      The key to retrieve
	 * @param   mixed   $default  The default value in case none is set
	 *
	 * @return  mixed  The saved parameter value (or $default, if nothing is
currently set)
	 */
	protected function getCommonParameter($key, $default = null)
	{
		$dbKey = $this->commonKey . '_autoupdate_' . $key;

		$db = FOFPlatform::getInstance()->getDbo();

		$query = $db->getQuery(true)
					->select($db->qn('value'))
					->from($db->qn($this->commonTable))
					->where($db->qn('key') . ' = ' .
$db->q($dbKey));

		$result = $db->setQuery($query)->loadResult();

		if (!$result)
		{
			return $default;
		}

		return $result;
	}

	/**
	 * Set a common parameter from the #__akeeba_common table
	 *
	 * @param   string  $key    The key to set
	 * @param   mixed   $value  The value to set
	 *
	 * @return  void
	 */
	protected function setCommonParameter($key, $value)
	{
		$dbKey = $this->commonKey . '_autoupdate_' . $key;

		$db = FOFPlatform::getInstance()->getDbo();

		$query = $db->getQuery(true)
					->select('COUNT(*)')
					->from($db->qn($this->commonTable))
					->where($db->qn('key') . ' = ' .
$db->q($dbKey));
		$count = $db->setQuery($query)->loadResult();

		if ($count)
		{
			$query = $db->getQuery(true)
						->update($db->qn($this->commonTable))
						->set($db->qn('value') . ' = ' .
$db->q($value))
						->where($db->qn('key') . ' = ' .
$db->q($dbKey));
			$db->setQuery($query)->execute();
		}
		else
		{
			$data = (object)array(
					'key'   => $dbKey,
					'value' => $value,
			);

			$db->insertObject($this->commonTable, $data);
		}
	}

	/**
	 * Proxy to updateComponent(). Required since old versions of our software
had an updateComponent method declared
	 * private. If we set the updateComponent() method public we cause a fatal
error.
	 *
	 * @return  string
	 */
	public function doUpdateComponent()
	{
		return $this->updateComponent();
	}

	/**
	 * Automatically install the extension update under Joomla! 1.5.5 or later
(web) / 3.0 or later (CLI).
	 *
	 * @return  string  The update message
	 */
	private function updateComponent()
	{
		$isCli          = FOFPlatform::getInstance()->isCli();
		$minVersion     = $isCli ? '3.0.0' : '1.5.5';
		$errorQualifier = $isCli ? ' using an unattended CLI CRON script
' : ' ';

		if (version_compare(JVERSION, $minVersion, 'lt'))
		{
			return "Extension updates{$errorQualifier}only work with Joomla!
$minVersion and later.";
		}

		try
		{
			$updatePackagePath = $this->downloadUpdate();
		}
		catch (Exception $e)
		{
			return $e->getMessage();
		}

		// Unpack the downloaded package file
		jimport('joomla.installer.helper');
		jimport('cms.installer.helper');
		$package = JInstallerHelper::unpack($updatePackagePath);

		if (!$package)
		{
			// Clean up
			if (JFile::exists($updatePackagePath))
			{
				JFile::delete($updatePackagePath);
			}

			return "An error occurred while unpacking the file. Please double
check your Joomla temp-directory setting in Global Configuration.";
		}

		$installer = new JInstaller;
		$installed = $installer->install($package['extractdir']);

		// Let's cleanup the downloaded archive and the temp folder
		if (JFolder::exists($package['extractdir']))
		{
			JFolder::delete($package['extractdir']);
		}

		if (JFile::exists($package['packagefile']))
		{
			JFile::delete($package['packagefile']);
		}

		if ($installed)
		{
			return "Component successfully updated";
		}
		else
		{
			return "An error occurred while trying to update the
component";
		}
	}

	/**
	 * Downloads the latest update package to Joomla!'s temporary
directory
	 *
	 * @return  string  The absolute path to the downloaded update package.
	 */
	public function downloadUpdate()
	{
		// Get the update URL
		$updateInformation = $this->getUpdates();
		$url               = $updateInformation['downloadURL'];

		if (empty($url))
		{
			throw new RuntimeException("No download URL was provided in the
update information");
		}

		$config   = JFactory::getConfig();
		$tmp_dest = $config->get('tmp_path');

		if (!$tmp_dest)
		{
			throw new RuntimeException("You must set a non-empty Joomla!
temp-directory in Global Configuration before continuing.");
		}

		if (!JFolder::exists($tmp_dest))
		{
			throw new RuntimeException("Joomla!'s temp-directory does not
exist. Please set the correct path in Global Configuration before
continuing.");
		}

		// Get the target filename
		$filename = $this->component . '.zip';
		$filename = rtrim($tmp_dest, '\\/') . '/' .
$filename;

		try
		{
			$downloader = new FOFDownload();
			$data = $downloader->getFromURL($url);
		}
		catch (Exception $e)
		{
			$code =$e->getCode();
			$message =$e->getMessage();
			throw new RuntimeException("An error occurred while trying to
download the update package. Double check your Download ID and your
server's network settings. The error message was: #$code:
$message");
		}

		if (!JFile::write($filename, $data))
		{
			if (!file_put_contents($filename, $data))
			{
				throw new RuntimeException("Joomla!'s temp-directory is not
writeable. Please check its permissions or set a different, writeable path
in Global Configuration before continuing.");
			}
		}

		return $filename;
	}

	/**
	 * Gets a file name out of a url
	 *
	 * @param   string  $url  URL to get name from
	 *
	 * @return  mixed   String filename or boolean false if failed
	 */
	private function getFilenameFromURL($url)
	{
		if (is_string($url))
		{
			$parts = explode('/', $url);

			return $parts[count($parts) - 1];
		}

		return false;
	}

	/**
	 * Proxy to sendNotificationEmail(). Required since old versions of our
software had a sendNotificationEmail method
	 * declared private. If we set the sendNotificationEmail() method public
we cause a fatal error.
	 *
	 * @param   string  $version  The new version of our software
	 * @param   string  $email    The email address to send the notification
to
	 *
	 * @return  mixed  The result of JMail::send()
	 */
	public function doSendNotificationEmail($version, $email)
	{
		try
		{
			return $this->sendNotificationEmail($version, $email);
		}
		catch (\Exception $e)
		{
			// Joomla! 3.5 is buggy
		}
	}

	/**
	 * Sends an update notification email
	 *
	 * @param   string  $version  The new version of our software
	 * @param   string  $email    The email address to send the notification
to
	 *
	 * @return  mixed  The result of JMail::send()
	 */
	private function sendNotificationEmail($version, $email)
	{
		$email_subject	= $this->updateEmailSubject;
		$email_body = $this->updateEmailBody;

		$jconfig  = JFactory::getConfig();
		$sitename = $jconfig->get('sitename');

		$substitutions = array(
				'[VERSION]'			=> $version,
				'[SITENAME]'		=> $sitename,
				'[COMPONENT]'		=> $this->componentDescription,
		);

		$email_subject = str_replace(array_keys($substitutions),
array_values($substitutions), $email_subject);
		$email_body    = str_replace(array_keys($substitutions),
array_values($substitutions), $email_body);

		$mailer = JFactory::getMailer();

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

		$mailer->setSender(array( $mailfrom, $fromname ));
		$mailer->addRecipient($email);
		$mailer->setSubject($email_subject);
		$mailer->setBody($email_body);

		return $mailer->Send();
	}
}