<?php
/**
 * @category 	Atmail
 * @package 	Models
 * @subpackage	Plugins
 * @copyright 	Copyright (c) 2009-2011 ATMAIL. All rights reserved
 * @license 	See http://atmail.com/license.php for license agreement
 * @author		Brad Kowalczyk brad@staff.atmail.com
 * @author		Atmail (http://atmail.com)
 */
class plugins
{
	
	/**
	 * Gets information about a plugin
	 *
	 * @param Object $plugin
	 * @return Array
	 */
	public static function getPluginInfo($plugin)
	{
		if( !$plugin instanceof Atmail_Controller_Plugin ) {
			return false;	
		}
		
		return $plugin->getPluginInfoArray();
	}
	
	
	/**
	 * Fetches required info from Plugins table
	 * then creates the plugin object and passes it
	 * too self::getPluginInfo() to extract the
	 * plugin info
	 * 
	 * @param Integer $id The id for the plugin in the Plugin table
	 * @return Array
	 */
	public function getInfoFromDb($id)
	{
		if (!is_numeric($id)) {
			throw new Exception("id must be numeric in plugins::getInfoFromDb()");
		}	
		
		$dbAdapter = Zend_Registry::get('dbAdapter');
		$row = $dbAdapter->fetchRow("select module, company, name from Plugins where id = ?", array($id));
		$path = APP_ROOT . "application/modules/{$row['module']}/plugins/{$row['company']}/{$row['name']}/Plugin.php";
		$className = "{$row['company']}_{$row['name']}_Plugin";
		
		if (!include_once($path)) {
			throw new Exception("$path is not a file");
		}
		
		return self::getPluginInfo(new $className);
	}
	
	
	/**
	 * Gets Plugin info from the plugin tgz
	 *
	 * Extracts the tgz into a temp folder then
	 * uses getPluginInfo() to gain info about the plugin
	 *
	 * @param String $path Path to plugin tgz
	 * @return Array Plugin information
	 */
	public static function getInfoFromTgz($path)
	{
		$tmpExtractionFolder = tempnam("/tmp", "");
        unlink($tmpExtractionFolder);
        if (!mkdir($tmpExtractionFolder)) {
            return false;
        }

		$result = self::extractPlugin( $path, $tmpExtractionFolder );
		
		if( $result !== true ) {
			return $result;
		}
		
		$pluginFile = trim(`find $tmpExtractionFolder -name Plugin.php`);

        preg_match("|([a-z0-9]+)/([a-z0-9]+)/Plugin\.php$|i", $pluginFile, $m);
		$className = $m[1]."_".$m[2]."_Plugin";

		if( class_exists($className, false) ) {
			return 'Plugin name already in use. Unable to get plugin info from file.';
		}
		
		require($pluginFile);
		//TODO: revise this rationale to rather unpack the plugin into a permanaent sandbox (until installed) so that can access preview.php etc.
		if (preg_match("|/tmp/[a-z0-9]+|i", $tmpExtractionFolder)) {
            `rm -rf $tmpExtractionFolder`;
        }

		return self::getPluginInfo(new $className);
	}
	
	/**
	 * Extracts a plugin tgz to specified location and
	 * returns the path to the Plugin.php class file
	 *
	 * @param String $path Path to plugin tgz
	 * @param String $extractToFolder Path for extraction
	 * @return String Path to Plugin.php class file
	 */
	//TODO: translate by getting translate adaptor from registry
	
	public static function extractPlugin($path, $extractToFolder)
	{
		if( !is_dir($extractToFolder) && !mkdir($extractToFolder) ) {
			return 'Unable to create temporary folder to unpack plugin.';
		}

		$path = escapeshellarg($path);
		$extractToFolder = escapeshellarg($extractToFolder);
		$files = shell_exec("tar xzf $path -C $extractToFolder");
        
		$findClassFileResult = trim(`find $extractToFolder -name Plugin.php`);
		if( strpos( $findClassFileResult, 'No such file or directory') !== false ) {
			return 'Extraction of plugin package failed or invalid plugin structure.';
		}
		
		if( strlen($findClassFileResult) < 10 ) {
			return 'Unable to find expected plugin class file.';
		}           
		
		return true;
	}
	
	
	/**
	 * Moves the plugin from the temp file upload
	 * location to a permanent location
	 *
	 * @param String $path Path to current location
	 * @param String $name The name to give the file
	 * @return String New location
	 */
	public static function savePluginTgz($path, $name)
	{
		$permanentDir = realpath(dirname(dirname(__FILE__)) . "/modules/admin/pluginPackages/");
		$basename = basename($path);
		move_uploaded_file($path, "$permanentDir/$name");
		return true;// $permanentDir . '/' . $name;
	}
	 
	 
	/**
	 * Returns an array of Plugin Packages that are
	 * available for installation
	 *
	 * @return Array
	 */
	public static function getAvailablePackages()
	{
		return array_map('basename', glob(realpath(dirname(dirname(__FILE__)) . "/modules/admin/pluginPackages/") . "/*.tgz"));
	}
	  
	  
	/**
	 * Install a plugin package
	 *
	 * @param String $file File to install
	 * @return Bool
	 */
	public static function install($file)
	{   
		$modulesPath = APP_ROOT . "application/modules";

		if( !file_exists("$modulesPath/admin/pluginPackages/$file") ) {
			return 'Plugin file does not exist';
		}
		
		$info = self::getInfoFromTgz("$modulesPath/admin/pluginPackages/$file");
		if( !is_array($info) ) {
			return 'File is not a valid Atmail Plugin';	
		}
		
		if( $info['module'] == '' ) {
			return 'Module name cannot be empty';
		}
		
		if( !is_dir("$modulesPath/{$info['module']}") ) {
			return 'Specified module does not exist';   
		}
		
		$tgzPath = escapeshellarg("$modulesPath/admin/pluginPackages/$file");
		$target = escapeshellarg("$modulesPath/{$info['module']}/plugins/");
		$extractionCommand = "tar xvzf $tgzPath -C $target";
		exec($extractionCommand, $output, $return);
		
		//TODO: add more sanity checking before going ahead with install
		if( $return == 0 ) {
			$dbAdapter = Zend_Registry::get('dbAdapter');
			if( $dbAdapter->fetchOne("select 1 from Plugins where company = ? and name = ? and module = ?", array($info['company'], $info['name'], $info['module'])) != 1 ) {
				$dbAdapter->insert('Plugins', array('module' => $info['module'], 'name' => $info['name'], 'company' => $info['company'], 'status' => 1));
			}

			// instantiate the plugin and call the setup() method
			// so that any custom installation procedures are carried out
			//include("$modulesPath/{$info['module']}/plugins/{$info['company']}/{$info['name']}/Plugin.php");
			$className = "{$info['company']}_{$info['name']}_Plugin";
   			$plugin = new $className;
			$message = $plugin->setup();
		
			// delete the Plugin Package
			unlink("$modulesPath/admin/pluginPackages/$file");
			return array(true, $message);
		}
		
		return array(false, "Could not extract plugin package");
	}
	
	
	public static function getInstalledPlugins($filter=null, $key=null)
	{
		$allowedFilters = array('company', 'module');
		$dbAdapter = Zend_Registry::get('dbAdapter');

		if (!is_null($filter) && in_array($filter, $allowedFilters) && !is_null($key)) {
			$where = $dbAdapter->quoteInto("where $filter = ?", $key); 
		} else {
			$where = '';
		}
		
		return $dbAdapter->fetchAll("select * from Plugins $where order by module, company, name");
	}
	
	
	/**
	 * Saves status for plugins as submited from the
	 * Plugin Settings page in webadmin
	 *
	 * @param Array an array of id => status pairs where
	 *			  'id' is Plugin.id and 'status' is Plugin.status
	 * @return void
	 */
	public function updateStatus($plugins)
	{
		if( !is_array($plugins) ) {
			throw new Exception("An array must be passed to plugins::updateStatus");
		}
		
		$dbAdapter = Zend_Registry::get('dbAdapter');
		foreach( $plugins as $k => $v ) {
			$dbAdapter->update('Plugins', array('status' => $v), $dbAdapter->quoteInto('id = ?', $k));
		}
	}

	public static function getEnabled($module = '')
	{
		$dbAdapter = Zend_Registry::get('dbAdapter');
		if (empty($module)) {
			return $dbAdapter->fetchAll("select company, name, module from Plugins where status = 1");
		}
		
		return $dbAdapter->fetchAll("select company, name, module from Plugins where status = 1 and (module = ? or module = 'global')", $module);
	}   
	
}
