<?php
/**
 * @package     Joomla.Platform
 * @subpackage  Updater
 *
 * @copyright   Copyright (C) 2005 - 2014 Open Source Matters, Inc. All rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE
 */

defined('JPATH_PLATFORM') or die;

jimport('joomla.filesystem.file');
jimport('joomla.filesystem.folder');
jimport('joomla.filesystem.archive');
jimport('joomla.filesystem.path');
jimport('joomla.base.adapter');
jimport('joomla.utilities.arrayhelper');

/**
 * Updater Class
 *
 * @package     Joomla.Platform
 * @subpackage  Updater
 * @since       11.1
 */
class JUpdater extends JAdapter
{
	/**
	 * @var    JUpdater  JUpdater instance container.
	 * @since  11.3
	 */
	protected static $instance;

	/**
	 * Constructor
	 *
	 * @since   11.1
	 */
	public function __construct()
	{
		// Adapter base path, class prefix
		parent::__construct(dirname(__FILE__), 'JUpdater');
	}

	/**
	 * Returns a reference to the global Installer object, only creating it
	 * if it doesn't already exist.
	 *
	 * @return  object  An installer object
	 *
	 * @since   11.1
	 */
	public static function &getInstance()
	{
		if (!isset(self::$instance))
		{
			self::$instance = new JUpdater;
		}
		return self::$instance;
	}

	/**
	 * Finds an update for an extension
	 *
	 * @param   integer  $eid           Extension Identifier; if zero use all sites
	 * @param   integer  $cacheTimeout  How many seconds to cache update information; if zero, force reload the update information
	 *
	 * @return  boolean True if there are updates
	 *
	 * @since   11.1
	 */
	public function findUpdates($eid = 0, $cacheTimeout = 0)
	{
		$dbo = $this->getDBO();
		$retval = false;

		// Push it into an array
		if (!is_array($eid))
		{
			$query = 'SELECT DISTINCT update_site_id, type, location, last_check_timestamp FROM #__update_sites WHERE enabled = 1';
		}
		else
		{
			$query = 'SELECT DISTINCT update_site_id, type, location, last_check_timestamp FROM #__update_sites' .
				' WHERE update_site_id IN' .
				'  (SELECT update_site_id FROM #__update_sites_extensions WHERE extension_id IN (' . implode(',', $eid) . '))';
		}
		$dbo->setQuery($query);
		$results = $dbo->loadAssocList();
		$result_count = count($results);
		$now = time();
		for ($i = 0; $i < $result_count; $i++)
		{
			$result = &$results[$i];
			$this->setAdapter($result['type']);
			if (!isset($this->_adapters[$result['type']]))
			{
				// Ignore update sites requiring adapters we don't have installed
				continue;
			}
			if ($cacheTimeout > 0)
			{
				if (isset($result['last_check_timestamp']) && ($now - $result['last_check_timestamp'] <= $cacheTimeout))
				{
					// Ignore update sites whose information we have fetched within
					// the cache time limit
					$retval = true;
					continue;
				}
			}
			$update_result = $this->_adapters[$result['type']]->findUpdate($result);
			if (is_array($update_result))
			{
				if (array_key_exists('update_sites', $update_result) && count($update_result['update_sites']))
				{
					$results = JArrayHelper::arrayUnique(array_merge($results, $update_result['update_sites']));
					$result_count = count($results);
				}
				if (array_key_exists('updates', $update_result) && count($update_result['updates']))
				{
					for ($k = 0, $count = count($update_result['updates']); $k < $count; $k++)
					{
						$current_update = &$update_result['updates'][$k];
						$update = JTable::getInstance('update');
						$extension = JTable::getInstance('extension');
						$uid = $update
							->find(
							array(
								'element' => strtolower($current_update->get('element')), 'type' => strtolower($current_update->get('type')),
								'client_id' => strtolower($current_update->get('client_id')),
								'folder' => strtolower($current_update->get('folder'))
							)
						);

						$eid = $extension
							->find(
							array(
								'element' => strtolower($current_update->get('element')), 'type' => strtolower($current_update->get('type')),
								'client_id' => strtolower($current_update->get('client_id')),
								'folder' => strtolower($current_update->get('folder'))
							)
						);
						if (!$uid)
						{
							// Set the extension id
							if ($eid)
							{
								// We have an installed extension, check the update is actually newer
								$extension->load($eid);
								$data = json_decode($extension->manifest_cache, true);
								if (version_compare($current_update->version, $data['version'], '>') == 1)
								{
									$current_update->extension_id = $eid;
									$current_update->store();
								}

								// Store compatibility information into the extension table
								if (!empty($update_result['compatibility']))
								{
									$system_data = json_decode($extension->system_data);
									$system_data->compatibility = new stdClass();

									$compatibility = $update_result['compatibility'];
									ksort($compatibility);

									// Check data for current installed version
									if (!empty($compatibility[$data['version']]))
									{
										$system_data->compatibility->installed          = new stdClass();
										$system_data->compatibility->installed->value   = $compatibility[$data['version']];
										$system_data->compatibility->installed->version = $data['version'];
									}

									// Get last item in array which is highest release
									$system_data->compatibility->available          = new stdClass();
									$system_data->compatibility->available->value   = end($compatibility);
									$system_data->compatibility->available->version = key($compatibility);
									$extension->system_data = json_encode($system_data);
									$extension->store();
								}
							}
							else
							{
								// A potentially new extension to be installed
								$current_update->store();
							}
						}
						else
						{
							$update->load($uid);
							// if there is an update, check that the version is newer then replaces
							if (version_compare($current_update->version, $update->version, '>') == 1)
							{
								$current_update->store();
							}
						}
					}
				}
				$update_result = true;
			}
			elseif ($retval)
			{
				$update_result = true;
			}

			// Finally, update the last update check timestamp
			$query = $dbo->getQuery(true);
			$query->update($dbo->quoteName('#__update_sites'));
			$query->set($dbo->quoteName('last_check_timestamp') . ' = ' . $dbo->quote($now));
			$query->where($dbo->quoteName('update_site_id') . ' = ' . $dbo->quote($result['update_site_id']));
			$dbo->setQuery($query);
			$dbo->execute();
		}
		return $retval;
	}

	/**
	 * Multidimensional array safe unique test
	 *
	 * @param   array  $myArray  The source array.
	 *
	 * @return  array
	 *
	 * @deprecated    12.1
	 * @note    Use JArrayHelper::arrayUnique() instead.
	 * @note    Borrowed from PHP.net
	 * @see     http://au2.php.net/manual/en/function.array-unique.php
	 * @since   11.1
	 *
	 */
	public function arrayUnique($myArray)
	{
		JLog::add('JUpdater::arrayUnique() is deprecated. See JArrayHelper::arrayUnique() . ', JLog::WARNING, 'deprecated');
		return JArrayHelper::arrayUnique($myArray);
	}

	/**
	 * Finds an update for an extension
	 *
	 * @param   integer  $id  Id of the extension
	 *
	 * @return  mixed
	 *
	 * @since   11.1
	 */
	public function update($id)
	{
		$updaterow = JTable::getInstance('update');
		$updaterow->load($id);
		$update = new JUpdate;
		if ($update->loadFromXML($updaterow->detailsurl))
		{
			return $update->install();
		}
		return false;
	}
}
