<?php
/**
 * @package LiveUpdate
 * @copyright Copyright (c)2010-2012 Nicholas K. Dionysopoulos / AkeebaBackup.com
 * @license GNU LGPLv3 or later <http://www.gnu.org/copyleft/lesser.html>
 */

defined('_JEXEC') or die();

class LiveUpdateXMLSlurp extends JObject
{
	private $_info = array();

	public function getInfo($extensionName, $xmlName)
	{
		if(!array_key_exists($extensionName, $this->_info)) {
			$this->_info[$extensionName] = $this->fetchInfo($extensionName, $xmlName);
		}

		return $this->_info[$extensionName];
	}

	/**
	 * Gets the version information of an extension by reading its XML file
	 * @param string $extensionName The name of the extension, e.g. com_foobar, mod_foobar, plg_foobar or tpl_foobar.
	 * @param string $xmlName The name of the XML manifest filename. If empty uses $extensionName.xml
	 */
	private function fetchInfo($extensionName, $xmlName)
	{
		$type = strtolower(substr($extensionName,0,3));
		switch($type) {
			case 'com':
				return $this->getComponentData($extensionName, $xmlName);
				break;
			case 'mod':
				return $this->getModuleData($extensionName, $xmlName);
				break;
			case 'plg':
				return $this->getPluginData($extensionName, $xmlName);
				break;
			case 'tpl':
				return $this->getTemplateData($extensionName, $xmlName);
				break;
			case 'pkg':
				return $this->getPackageData($extensionName, $xmlName);
				break;
			case 'lib':
				return $this->getPackageData($extensionName, $xmlName);
				break;
			default:
				if(strtolower(substr($extensionName, 0, 4)) == 'file') {
					return $this->getPackageData($extensionName, $xmlName);
				} else {
					return array('version'=>'', 'date'=>'');
				}
		}
	}

	/**
	 * Gets the version information of a component by reading its XML file
	 * @param string $extensionName The name of the extension, e.g. com_foobar
	 * @param string $xmlName The name of the XML manifest filename. If empty uses $extensionName.xml
	 */
	private function getComponentData($extensionName, $xmlName)
	{
		$extensionName = strtolower($extensionName);
		$path = JPATH_ADMINISTRATOR.'/components/'.$extensionName;
		$altExtensionName = substr($extensionName,4);

		jimport('joomla.filesystem.file');
		if(JFile::exists("$path/$xmlName")) {
			$filename = "$path/$xmlName";
		} elseif(JFile::exists("$path/$extensionName.xml")) {
			$filename = "$path/$extensionName.xml";
		} elseif(JFile::exists("$path/$altExtensionName.xml")) {
			$filename = "$path/$altExtensionName.xml";
		} elseif(JFile::exists("$path/manifest.xml")) {
			$filename = "$path/manifest.xml";
		} else {
			$filename = $this->searchForManifest($path);
			if($filename === false)	$filename = null;
		}

		if(empty($filename)) {
			return array('version' => '', 'date' => '', 'xmlfile' => '');
		}

		try {
			$xml = new SimpleXMLElement($filename, LIBXML_NONET, true);
		} catch(Exception $e) {
			return array('version' => '', 'date' => '', 'xmlfile' => '');
		}

		// Need to check for extension (since 1.6) and install (supported through 2.5)
		if ($xml->getName() != 'extension' && $xml->getName() != 'install') {
			unset($xml);
			return array('version' => '', 'date' => '', 'xmlfile' => '');
		}

		$data['version'] = $xml->version ? (string) $xml->version : '';
		$data['date'] = $xml->creationDate ? (string) $xml->creationDate : '';
		$data['xmlfile'] = $filename;

		return $data;
	}

	/**
	 * Gets the version information of a module by reading its XML file
	 * @param string $extensionName The name of the extension, e.g. mod_foobar
	 * @param string $xmlName The name of the XML manifest filename. If empty uses $extensionName.xml
	 */
	private function getModuleData($extensionName, $xmlName)
	{
		$extensionName = strtolower($extensionName);
		$altExtensionName = substr($extensionName,4);

		jimport('joomla.filesystem.folder');
		jimport('joomla.filesystem.file');
		$path = JPATH_SITE.'/modules/'.$extensionName;
		if(!JFolder::exists($path)) {
			$path = JPATH_ADMINISTRATOR.'/modules/'.$extensionName;
		}
		if(!JFolder::exists($path)) {
			// Joomla! 1.5
			// 1. Check front-end
			$path = JPATH_ADMINISTRATOR.'/modules';
			$filename = "$path/$xmlName";
			if(!JFile::exists($filename)) {
				$filename = "$path/$extensionName.xml";
			}
			if(!JFile::exists($filename)) {
				$filename = "$path/$altExtensionName.xml";
			}
			// 2. Check front-end
			if(!JFile::exists($filename)) {
				$path = JPATH_SITE.'/modules';
				$filename = "$path/$xmlName";
				if(!JFile::exists($filename)) {
					$filename = "$path/$extensionName.xml";
				}
				if(!JFile::exists($filename)) {
					$filename = "$path/$altExtensionName.xml";
				}
				if(!JFile::exists($filename)) {
					return array('version' => '', 'date' => '');
				}
			}
		} else {
			// Joomla! 1.6
			$filename = "$path/$xmlName";
			if(!JFile::exists($filename)) {
				$filename = "$path/$extensionName.xml";
			}
			if(!JFile::exists($filename)) {
				$filename = "$path/$altExtensionName.xml";
			}
			if(!JFile::exists($filename)) {
				return array('version' => '', 'date' => '');
			}
		}

		if(empty($filename)) {
			return array('version' => '', 'date' => '', 'xmlfile' => '');
		}

		try {
			$xml = new SimpleXMLElement($filename, LIBXML_NONET, true);
		} catch(Exception $e) {
			return array('version' => '', 'date' => '', 'xmlfile' => '');
		}

		// Need to check for extension (since 1.6) and install (supported through 2.5)
		if ($xml->getName() != 'extension' && $xml->getName() != 'install') {
			unset($xml);
			return array('version' => '', 'date' => '', 'xmlfile' => '');
		}

		$data['version'] = $xml->version ? (string) $xml->version : '';
		$data['date'] = $xml->creationDate ? (string) $xml->creationDate : '';
		$data['xmlfile'] = $filename;

		return $data;
	}

	/**
	 * Gets the version information of a plugin by reading its XML file
	 * @param string $extensionName The name of the plugin, e.g. plg_foobar
	 * @param string $xmlName The name of the XML manifest filename. If empty uses $extensionName.xml
	 */
	private function getPluginData($extensionName, $xmlName)
	{
		$extensionName = strtolower($extensionName);
		$altExtensionName = substr($extensionName,4);

		jimport('joomla.filesystem.folder');
		jimport('joomla.filesystem.file');

		$base = JPATH_PLUGINS;

		// Get a list of directories
		$stack = JFolder::folders($base,'.',true,true);
		foreach($stack as $path)
		{
			$filename = "$path/$xmlName";
			if(JFile::exists($filename)) break;
			$filename = "$path/$extensionName.xml";
			if(JFile::exists($filename)) break;
			$filename = "$path/$altExtensionName.xml";
			if(JFile::exists($filename)) break;
		}

		if(!JFile::exists($filename)) {
			return array('version' => '', 'date' => '', 'xmlfile' => '');
		}

		try {
			$xml = new SimpleXMLElement($filename, LIBXML_NONET, true);
		} catch(Exception $e) {
			return array('version' => '', 'date' => '', 'xmlfile' => '');
		}

		// Need to check for extension (since 1.6) and install (supported through 2.5)
		if ($xml->getName() != 'extension' && $xml->getName() != 'install') {
			unset($xml);
			return array('version' => '', 'date' => '', 'xmlfile' => '');
		}

		$data['version'] = $xml->version ? (string) $xml->version : '';
		$data['date'] = $xml->creationDate ? (string) $xml->creationDate : '';
		$data['xmlfile'] = $filename;
		$data['description'] = $xml->description ? (string) $xml->description : '';

		return $data;
	}

	/**
	 * Gets the version information of a template by reading its XML file
	 * @param string $extensionName The name of the template, e.g. tpl_foobar
	 * @param string $xmlName The name of the XML manifest filename. If empty uses $extensionName.xml or templateDetails.xml
	 */
	private function getTemplateData($extensionName, $xmlName)
	{
		$extensionName = strtolower($extensionName);
		$altExtensionName = substr($extensionName,4);

		jimport('joomla.filesystem.folder');
		jimport('joomla.filesystem.file');

		// First look for administrator templates
		$path = JPATH_THEMES.'/'.$altExtensionName;
		if(!JFolder::exists($path)) {
			// Then look for front-end templates
			$path = JPATH_SITE.'/templates/'.$altExtensionName;
			if(!JFolder::exists($path)) return array('version' => '', 'date' => '');
		}

		$filename = "$path/$xmlName";
		if(!JFile::exists($filename)) {
			$filename = "$path/templateDetails.xml";
		}
		if(!JFile::exists($filename)) {
			$filename = "$path/$extensionName.xml";
		}
		if(!JFile::exists($filename)) {
			$filename = "$path/$altExtensionName.xml";
		}
		if(!JFile::exists($filename)) {
			return array('version' => '', 'date' => '', 'xmlfile' => '');
		}

		try {
			$xml = new SimpleXMLElement($filename, LIBXML_NONET, true);
		} catch(Exception $e) {
			return array('version' => '', 'date' => '', 'xmlfile' => '');
		}

		// Need to check for extension (since 1.6) and install (supported through 2.5)
		if ($xml->getName() != 'extension' && $xml->getName() != 'install') {
			unset($xml);
			return array('version' => '', 'date' => '', 'xmlfile' => '');
		}

		$data['version'] = $xml->version ? (string) $xml->version : '';
		$data['date'] = $xml->creationDate ? (string) $xml->creationDate : '';
		$data['xmlfile'] = $filename;

		return $data;
	}

	/**
	 * This method parses the manifest information of package, library and file
	 * extensions. All of those extensions do not store their manifests in the
	 * extension's directory, but in administrator/manifests. Kudos to @mbabker
	 * for sharing this method!
	 *
	 * @param string $extensionName
	 * @param string $xmlName
	 * @return type
	 */
	private function getPackageData($extensionName, $xmlName)
	{
		$extensionName = strtolower($extensionName);
		$altExtensionName = substr($extensionName,4);

		jimport('joomla.filesystem.folder');
		jimport('joomla.filesystem.file');
		$path = JPATH_ADMINISTRATOR.'/manifests/packages';

		$filename = "$path/$xmlName";
		if(!JFile::exists($filename)) {
			$filename = "$path/$extensionName.xml";
		}
		if(!JFile::exists($filename)) {
			$filename = "$path/$altExtensionName.xml";
		}
		if(!JFile::exists($filename)) {
			return array('version' => '', 'date' => '');
		}

		if(empty($filename)) {
			return array('version' => '', 'date' => '', 'xmlfile' => '');
		}

		try {
			$xml = new SimpleXMLElement($filename, LIBXML_NONET, true);
		} catch(Exception $e) {
			return array('version' => '', 'date' => '', 'xmlfile' => '');
		}

		// Need to check for extension (since 1.6) and install (supported through 2.5)
		if ($xml->getName() != 'extension') {
			unset($xml);
			return array('version' => '', 'date' => '', 'xmlfile' => '');
		}

		$data['version'] = $xml->version ? (string) $xml->version : '';
		$data['date'] = $xml->creationDate ? (string) $xml->creationDate : '';
		$data['xmlfile'] = $filename;

		return $data;
	}

	/**
	 * Scans a directory for XML manifest files. The first XML file to be a
	 * manifest wins.
	 *
	 * @var $path string The path to look into
	 *
	 * @return string|bool The full path to a manifest file or false if not found
	 */
	private function searchForManifest($path)
	{
		jimport('joomla.filesystem.folder');
		$files = JFolder::files($path, '\.xml$', false, true);
		if(!empty($files)) foreach($files as $filename) {
			try {
				$xml = new SimpleXMLElement($filename, LIBXML_NONET, true);
			} catch(Exception $e) {
				continue;
			}

			// Check for extension (since 1.6) and install (supported through 2.5)
			if(($xml->getName() != 'extension' && $xml->getName() != 'install')) continue;
			unset($xml);
			return $filename;
		}

		return false;
	}
}
