<?php
/**
 * YANA library
 *
 * @package yana
 * @licence http://creativecommons.org/licenses/by-nc-sa/2.5/
 */

/**
 * «Singleton» PluginManager
 *
 * @access      public
 * @name        pluginmanager
 * @package     yana
 * @subpackage  core
 */
class PluginManager extends Singleton
{

    /**#@+
     * @ignore
     * @access  private
     */

    /** @var string */  var $pluginDir = "";
    /** @var string */  var $filename  = "";
    /** @var bool   */  var $isLoaded  = false;
    /** @var array  */  var $config    = array(); /* configuration */
    /** @var array  */  var $drive     = array(); /* virtual drive */
    /** @var array  */  var $plugins   = array(); /* plugin objects */
    /** @var array  */  var $interface = array(); /* interface settings */
    
    /**#@-*/

    /**
     * «Singleton» Constructor
     *
     * @name    pluginmanager::__construct()
     * @param   string  $pluginDir
     * @param   string  $filename
     *
     * @ignore
     */
    function PluginManager($pluginDir, $filename)
    {
        /* check input vars */
        $this->checkSingletonConstraint();
        assert('is_string($pluginDir) && is_dir($pluginDir);');
        assert('is_string($filename) && is_file($filename) && is_readable($filename);');
        settype($pluginDir, "string");
        settype($filename, "string");

        if (is_dir($pluginDir)) {
            $this->pluginDir = $pluginDir;
        } else {                  
            $this->pluginDir = "plugins/";
        }
        clearstatcache();
        $this->filename = $filename;

        $this->config = getConfigFile($filename,CASE_MIXED);
        foreach ($this->config as $plugin => $element)
        {
            if (empty($element['PRIORITY'])) {
                $this->config[$plugin]['PRIORITY'] = 0;
            } else if ($element['PRIORITY'] > 3) {
                $this->config[$plugin]['PRIORITY'] = 3;
            } /* end if */
        } /* end for */
    }

    /**
     * broadcast an event to all plugins
     *
     * @access   public
     * @param    string  $event
     * @param    array   $ARGUMENTS
     * @param    string  $criteria  (optional)
     * @return   boolean
     */
    function handle($event, $ARGUMENTS, $criteria = "")
    {
        settype($event,     "string");
        settype($ARGUMENTS, "array");
        settype($criteria,  "string");
        global $YANA;

        $event = strtoupper($event);

        if (empty($criteria)) {
            $criteria = $this->getEventType($event);
            assert('is_string($criteria);');
            settype($criteria, "string");
        }

        $package = "";
        if (isset($this->config["INTERFACE"][$event]["PACKAGE"])) {
            $package = $this->config["INTERFACE"][$event]["PACKAGE"];
        }

        $REF = $this->_sort($criteria, $package);
        $this->_loadPlugins($REF);
        $PLUGIN_CLASS_METHODS = get_class_methods("plugin");
        $test = true;
        foreach ($this->plugins as $key => $element)
        {
            if (preg_match("/^[a-zA-Z][a-zA-Z0-9_\x7f-\xff]*$/", $event)) {
                if (!in_array(strtolower($event), $PLUGIN_CLASS_METHODS) && strtolower($event) != strtolower(get_class($element)) && method_exists($element, $event)) {
                    if (!$element->$event($ARGUMENTS)) {
                        $test = false;
                        break;
                    }
                } else {
                    if (!$element->_default(strtolower($event),$ARGUMENTS)) {
                        $test = false;
                        break;
                    }
                }
            }
        }

        if ($test === true) {
            if (isset($this->config["INTERFACE"][$event]["ONSUCCESS"])) {
                $YANA->setVar("SETTINGS.EVENT_ROUTING", $this->config["INTERFACE"][$event]["ONSUCCESS"]);
            }
        } else{
            if (isset($this->config["INTERFACE"][$event]["ONERROR"])) {
                $YANA->setVar("SETTINGS.EVENT_ROUTING", $this->config["INTERFACE"][$event]["ONERROR"]);
            }
        }
        
        $YANA->setVar("SETTINGS.LAST_RESULT", $test);
        return $test;
    }

    /**
     * refreshPluginFile
     *
     * @access public
     * @static
     * @return boolean
     *
     * @ignore
     */ 
    function refreshPluginFile()
    {
        $pluginFile = new configfile($this->filename);
        $pluginFile->create();
        $pluginFile->init();
        $REF = array();
                
        $dirlist = dirlist($this->pluginDir, ".config");
        for ($i=0;$i<count($dirlist);$i++)
        {
            $valid = true;
            $NEW_PLUGIN  = array();
            $INTERFACE   = array();

            $plugin = getConfigFile($this->pluginDir.$dirlist[$i],CASE_MIXED);
            if (!empty($this->config[$plugin['INFO']['ID']]['ACTIVE'])) {
                if (isset($plugin['INTERFACE'])) {
                    $INTERFACE = $plugin['INTERFACE'];
                }
            } else {
                $INTERFACE = array();
            }
            if (isset($plugin)) {
                $plugin = $plugin['INFO'];
            } else {
                trigger_error("Plugin file \"".$dirlist[$i]."\" is corrupt. Root-element \"INFO\" missing. Plugin may not be installed.", E_USER_WARNING);
                $valid = false;
            }
            if ($valid === true && $plugin['ID'] && $plugin['TYPE']) {
                $plugin['ID'] = strtolower($plugin['ID']);

                if (!empty($plugin['NAME'])) {
                    $NEW_PLUGIN['NAME'] = $plugin['NAME'];
                }
                $NEW_PLUGIN['FILENAME'] = $this->pluginDir.$dirlist[$i];
                if (!empty($this->config[$plugin['ID']]['ACTIVE'])) {
                    $NEW_PLUGIN['ACTIVE'] = $this->config[$plugin['ID']]['ACTIVE'];
                } else {
                    $NEW_PLUGIN['ACTIVE'] = 0;
                }
                settype($NEW_PLUGIN['ACTIVE'], "integer");
                if (!empty($plugin["PACKAGE"])) {
                    $NEW_PLUGIN["PACKAGE"] = $plugin["PACKAGE"];
                }
                $NEW_PLUGIN['TYPE'] = ( ($plugin['TYPE']) ? $plugin['TYPE'] : "default" );
                if (!empty($plugin["PARENT"])) {
                    $NEW_PLUGIN["PARENT"] = $plugin["PARENT"];
                }
                if (!empty($plugin['SETTINGS'])) {
                    $NEW_PLUGIN['SETTINGS'] = $plugin['SETTINGS'];
                }
                if (!empty($plugin['CONFIG_TEMPLATE'])) {
                    $NEW_PLUGIN['CONFIG_TEMPLATE']= $plugin['CONFIG_TEMPLATE'];
                }
                if (isset($plugin['PRIORITY'])) {
                    $NEW_PLUGIN['PRIORITY'] = $plugin['PRIORITY'];
                } else {
                    $NEW_PLUGIN['PRIORITY'] = 0;
                }
                if (!empty($plugin["START"])) {
                    $NEW_PLUGIN["START"]      = $plugin["START"];
                }
                if (!empty($plugin["ONSUCCESS"])) {
                    $NEW_PLUGIN["ONSUCCESS"]  = $plugin["ONSUCCESS"];
                }
                if (!empty($plugin["ONERROR"])) {
                    $NEW_PLUGIN["ONERROR"]    = $plugin["ONERROR"];
                }
                if (is_array($INTERFACE) && count($INTERFACE) > 0) {
                    $INTERFACE = array_change_key_case($INTERFACE, CASE_UPPER);
                    foreach ($INTERFACE as $a => $b)
                    {
                        if (!empty($plugin["PACKAGE"])){
                            $INTERFACE[$a]["PACKAGE"] = $plugin["PACKAGE"];
                        }
                        if (isset($REF['INTERFACE'][$a]))   {
                            $valid = false;
                            trigger_error("Interface conflict found. Plugin \"".$plugin['ID']."\" collides in event-key \"$a\" with another installed plugin and could not be loaded. Please check your configuration (and possibly inform the author of the plugin to solve this problem).", E_USER_WARNING);
                        }
                    }
                }
                if ($valid === true) {
                    $REF[$plugin['ID']] = $NEW_PLUGIN;
                    if (is_array($INTERFACE)) {
                        if (isset($REF['INTERFACE'])) {
                            settype($REF['INTERFACE'], "array");
                            $REF['INTERFACE']  += $INTERFACE;
                        } else {
                            $REF['INTERFACE']   = $INTERFACE;
                        }
                    }
                }
            }
        }
        if (isset($REF) && is_array($REF)) {
            ksort($REF);
        }
        if (isset($REF['INTERFACE']) && is_array($REF['INTERFACE'])) {
            ksort($REF['INTERFACE']);
        }
        $pluginFile->set($REF);
        return $pluginFile->write();
    }

    /**
     * setActive
     * 
     * @access public
     * @param string $key
     * @param int $state
     * @return boolean
     *
     * @ignore
     */
    function setActive($key, $state)
    {
        settype($key,"string");
        settype($state,"integer");
        
        if (isset($this->config[$key])) {
            if ($this->config[$key]['ACTIVE'] != -1 && $this->config[$key]['ACTIVE'] != 2) {
                $this->config[$key]['ACTIVE'] = $state;
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }

    /**
     * getConfig
     * 
     * @access public
     * @param string $pluginName
     * @return string
     *
     * @ignore
     */
    function getConfig($pluginName)
    {
        settype($pluginName, "string");
        if (isset($this->drive[$pluginName])) {
            return $this->drive[$pluginName]->getOptions("");
        } else {
            trigger_error("There is no plugin named '".$pluginName."'.", E_USER_ERROR);
            return false;
        }
    }

    /**
     * get a file from a virtual drive
     *
     * Each plugin defines it's own virtual drive with
     * files that are required for it to function as intended.
     *
     * You may access the virtual drive of any plugin if you know
     * the plugin's name $pluginName and the name $key of the file you want.
     * This is usefull from plugins that extend the functionality of another.
     *
     * @access  public
     * @param   string $pluginName
     * @param   string $key
     * @return  string
     */
    function get($pluginName, $key)
    {
        settype($pluginName, "string");
        settype($key, "string");

        if (isset($this->drive[$pluginName])) {
            return $this->drive[$pluginName]->get($key);
        } else {
            trigger_error("There is no plugin named '".$pluginName."'.", E_USER_ERROR);
            return false;
        }
    }

    /**
     * getVar
     * 
     * @access public
     * @param string $pluginName
     * @param string $key
     * @return mixed
     *
     * @ignore
     */
    function getVar($pluginName = "", $key)
    {
        settype($pluginName, "string");
        settype($key, "string");

        if ($pluginName != "" && $pluginName != "*") {
            if (isset($this->drive[$pluginName]) && method_exists($this->drive[$pluginName], "getVar")) {
                return $this->drive[$pluginName]->getVar($key);
            } else {
                return false;
            }
        } else {
            $key = strtoupper($key);
            $path = explode("/", $key);
            if ($path[0] == "INTERFACE") {
                $path[0] = $path[1];
            }
            $path[0] = strtolower($path[0]);
            if (isset($this->config[$path[0]]) && isset($this->config[$path[0]]['ACTIVE']) && !$this->config[$path[0]]['ACTIVE']) {
                return false;
            }
            $result =& Hashtable::get($this->config, $key);
            if (is_null($result)) {
                global $YANA;
                if (isset($YANA)) {
                    return $YANA->getDefault($key);
                } else {
                    return NULL;
                }
            } else {
                return $result;
            }
        }
    }

    /**
     * setVar
     *
     * @access public
     * @param string $pluginName
     * @param string $key
     * @param mixed $value
     * @return boolean
     *
     * @ignore
     */
    function setVar($pluginName, $key, $value)
    {
        settype($pluginName, "string");
        settype($key, "string");

        if ($pluginName != "" && $pluginName != "*") {
            if (isset($this->drive[$pluginName]) && method_exists($this->drive[$pluginName], "setVar")) {
                if ($this->drive[$pluginName]->setVar($key, $value)) {
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        }
    }

    /**
     * write
     *
     * @access public
     * @return boolean
     *
     * @ignore
     */
    function write()
    {
        $file = new configfile($this->filename);
        $file->init();
        $file->set($this->config);
        return $file->write();
    }

    /**
     * check if a specific plugin is installed
     *
     * @access  public
     * @param   string  $pluginName
     * @return  boolean
     */ 
    function isInstalled($pluginName)
    {
        settype($pluginName, "String");
        assert('is_bool($this->isLoaded);');
        return $this->isLoaded && isset($this->plugins[$pluginName]);
    }

    /**
     * toString
     *
     * @access public
     * @return string
     *
     * @ignore
     */ 
    function toString()
    {
        if ($this->config) {
            $txt = "";
            foreach ($this->config as $plugin => $element)
            {
                $txt .= "Plugin \"$plugin\":\n\t- active = ".(($element['ACTIVE']>0)?"yes":"no")."\n\t- type = ".$element['TYPE']."\n\t- priority = ".$element['PRIORITY']."\n";
            }
            return $txt;
        } else {
            return "Plugin list is empty.\n";
        }
    }

    /**
     * get the name of the directory where plugins are installed
     *
     * @access public
     * @return string
     */
    function getPluginDir()
    {
        assert('is_string($this->pluginDir);');
        return $this->pluginDir;
    }

    /**
     * getPluginList
     *
     * @access public
     * @return array
     *
     * @ignore
     */
    function getPluginList()
    {
        $REF = array();
        $i = 0;
        foreach ($this->config as $a => $b)
        {
            if ($a != "INTERFACE") {
                assert('is_array($b);');
                $REF[$i]['PLUGIN'] = $b;
                $REF[$i]['PLUGIN']['ID'] = $a;
                $i++;
            }
        }
        return $REF;
    }

    /**
     * getPluginFilename
     *
     * @access public
     * @param string $pluginName
     * @return string
     *
     * @ignore
     */
    function getPluginFilename($pluginName)
    {
        if (!empty($this->config[$pluginName]["FILENAME"])) {
            $filename = $this->config[$pluginName]["FILENAME"];
        } else {
            $filename = "";
        }
        settype($filename, "string");
        return $filename;
    }
    
    /**
     * getEventType
     *
     * @access  public
     * @param   string  $event
     * @return  string  type of event
     *
     * @ignore
     */
    function getEventType($event)
    {
        assert('is_string($event);');
        $event = strtoupper($event);
        if (isset($this->config["INTERFACE"][$event]["TYPE"])) {
            /* String */ $type = $this->config["INTERFACE"][$event]["TYPE"];
        } else {
            global $YANA;
            /* array */  $DEFAULT_EVENT = $YANA->getDefault("EVENT");
            assert('is_array($DEFAULT_EVENT);');
            /* String */ $type          = (is_array($DEFAULT_EVENT['TYPE']) && isset($DEFAULT_EVENT['TYPE'])) ? $DEFAULT_EVENT['TYPE'] : "";
        }
        assert('is_string($type);');
        settype($type, "string");
        return $type;
    }

    /**
     * _sort
     *
     * @access private
     * @param string $criteria
     * @param string $package (optional)
     * @return array
     *
     * @ignore
     */
    function _sort($criteria, $package = "") 
    {
        settype($criteria,"string");
        settype($package, "string");
        global $YANA;

        $REF = array();
        foreach ($this->config as $plugin => $element)
        {
            if (!empty($element['ACTIVE']) && $element['ACTIVE']>0) {
                if ($element['ACTIVE']<3) {
                    $YANA->setVar("INSTALLED_PLUGINS.".strtoupper($plugin), true);
                }    
                settype($element['PRIORITY'], "integer");

                if (empty($package) || !isset($element["PACKAGE"]) || strcasecmp($element["PACKAGE"], $package) == 0) {
                    switch ($criteria) {

                        case "default":
                            $REF[$plugin] = $element['PRIORITY'];
                        break;
    
                        case "":
                            $REF[$plugin] = $element['PRIORITY'];
                        break;
    
                        case "read":
                            if ($element['TYPE'] == "read" || $element['TYPE'] == "default" || $element['TYPE'] == "primary" || $element['TYPE'] == "security") {
                                $REF[$plugin] = $element['PRIORITY'];
                            }
                        break;
    
                        case "write":
                            if ($element['TYPE'] == "write" || $element['TYPE'] == "default" || $element['TYPE'] == "primary" || $element['TYPE'] == "security") {
                                $REF[$plugin] = $element['PRIORITY'];
                            }
                        break;
    
                        case "security":
                            if ($element['TYPE'] == $criteria) {
                                $REF[$plugin] = 10; /* load security plugins first */
                            } else {
                                $REF[$plugin] = $element['PRIORITY'];
                            }
                        break;
    
                        case "config":
                            if ($element['TYPE'] == "security") {
                                $REF[$plugin] = 10; /* load security plugins first */
                            } else if ($element['TYPE'] == $criteria) {
                                $REF[$plugin] = $element['PRIORITY'];
                            }
                        break;
    
                        case "primary":
                            if ($element['TYPE'] == $criteria || $element['TYPE'] == "security") {
                                $REF[$plugin] = $element['PRIORITY'];
                            }
                        break;
    
                        default: /* do nothing */
                        break;

                    } /* end switch */
                } /* end if */
            } /* end if */
        } /* end for */
        arsort($REF);
        return $REF;
    }

    /**
     * _loadPlugins
     *
     * @access private
     * @param array $REF
     *
     * @ignore
     */
    function _loadPlugins($REF)
    {
        settype($REF, "array");
        global $YANA;
        $this->isLoaded = true;

        foreach ($REF as $key => $element)
        {
            $this->drive[$key] = new registry($this->pluginDir.$key."/_drive.config", $YANA->getVar("*"), $this->pluginDir.$key."/", $YANA->getMode());
            $this->drive[$key]->read();
            if (class_exists("plugin_$key")) {
                $class = "plugin_$key";
                $this->plugins[$key] = new $class($key);
            }
        }
    }
    
    /**
     * mountAll
     *
     * @access public
     *
     * @ignore
     */
    function mountAll()
    {
        global $YANA;

        foreach ($this->config as $key => $element)
        {
            if (!empty($element['ACTIVE']) && $element['ACTIVE'] > 0) {
                $this->drive[$key] = new registry($this->pluginDir.$key."/_drive.config", $YANA->getVar("<STRING>"), $this->pluginDir.$key."/", $YANA->getMode());
                $this->drive[$key]->read();
                if (class_exists("plugin_$key")) {
                    $class = "plugin_$key";
                    $this->plugins[$key] = new $class($key);
                }
            }
        }
    }
    
    /**
     * check
     *
     * @access public
     * @return string
     *
     * @ignore
     */
    function check()
    {
        /* String */ $txt = "";

        global $YANA;

        foreach ($this->config["INTERFACE"] as $key => $element)
        {
            if (!empty($element['TEMPLATE']) && $element['TEMPLATE'] != "NULL" && $element['TEMPLATE'] != "MESSAGE") {
                $file = $YANA->skin->getFile($element['TEMPLATE']);
                if (empty($file) || !file_exists($file)) {
                    $txt .= "<p>ERROR: The chosen base template \"".$element['TEMPLATE']."\" for the registered event \"$key\" is not available. Please check if reference and filename for this template is correct and all files have been installed correctly.</p>\n";
                }
            }
            if (!empty($element['INSERT'])   && $element['INSERT'] != "NULL"   && $element['INSERT'] != "MESSAGE") {
                $file = $YANA->skin->getFile($element['INSERT']);
                if (empty($file) || !file_exists($file)) {
                    $txt .= "<p>ERROR: The chosen template \"".$element['INSERT']."\" for the registered event \"$key\" is not available. Please check if reference and filename for this template is correct and all files have been installed correctly.</p>\n";
                }
            }
        }

        return $txt;
    }

    /**
     * getInfo
     *
     * @access public
     * @param string $filename
     * @return array
     *
     * @ignore
     */
    function getInfo($filename)
    {
        assert('is_string($filename);');
        settype($filename, "String");

        $file = ($this->pluginDir).$filename;
        if (is_file($file)) {
            $REF = getConfigFile($file);
            assert('is_array($REF["INFO"]);');
            if (is_array($REF['INFO'])) {
                $REF['INFO']['LAST_CHANGE'] = filemtime($file);
                clearstatcache();
                return $REF["INFO"];
            } else {
                return false;
            }
        } else {
            return false;
        }
    }
    

}

$instance_id=base64_decode('PHAgY2xhc3M9IktPTU1FTlRBUiIgc3R5bGU9InRleHQtYWxpZ246IGNlbnRlciI+V2ViVG9vbHMgcG93ZXJlZCBieSA8QSBIUkVGPSJodHRwOi8vd3d3LkFsbC1Db21tdW5pdHkuZGUiIHRhcmdldD0iX2JsYW5rIiB0aXRsZT0iRGllIENvbW11bml0eSBkZXIgV2VibWFzdGVyIj48c3BhbiBzdHlsZT0iY29sb3I6IzY0OTVFRCI+d3d3Ljwvc3Bhbj48c3BhbiBzdHlsZT0iY29sb3I6I0ZGQTUwMCI+IEFsbCAtIENvbW11bml0eSA8L3NwYW4+PHNwYW4gc3R5bGU9ImNvbG9yOiM2NDk1RUQiPi5kZTwvc3Bhbj48L0E+');

?>