<?php
/**
 * CodeIgniter Controller
 *
 * @category CodeIgniter
 * @license /_licenses/untermStrich/agb_normal.pdf
 * @author Christian Koller
 * @copyright 2009 untermStrich
 * @package controllers
 * @version $Id$
*/

/**
 * The common functions controller of untermStrich
 *
 * Place everything into this controller, which does not need authentication
 */
class Common extends My_Controller
{
    public function __construct()
    {
        $this->repair_session_table = 'nightly';
        parent::__construct();
    }
    
    /**
     * Do not call the index in common. It does not do anything the forward to start
     * 
     * @return void
     */
    public function index()
    {
        redirect('/start/');
    }
    
    /**
     * Allows the user to change the language
     * 
     * @return void
     */
    public function change_language()
    {
        $this->load->helper('cookie');

        $this->login->restrict_xsrf($this->input->post('xjxcookie'), self::M_DIRECT);

        $language = $this->input->post('language');
        set_cookie('on_login_language', $language, 2600000);
        redirect('/start/');
    }

    /**
     * Init and show start
     * 
     * @param   string  $text   Optional Text to show
     * @return  void
     */
    private function _init_start($text='Start')
    {
        $this->load->helper('system');
        $this->load->helper('process');

        ignore_user_abort(true);
        set_us_memory_limit();
        restart_timout_counter(true);

        process_start_flush();
        us_echo_console($text);
    }
    
    /**
     * Print date and time
     * 
     * @param   string  $text       Optional Text to show
     * @param   string  $prefix     Optional Prefix text to show
     * @return  void
     */
    private function _print_time($text='', $prefix='')
    {
        $startt = new UsDateTime('now');
        if (!empty($text)) {
            us_echo_console('');
        }
        us_echo_console($prefix.'  '.$startt->us_date().'  '.$startt->us_ltime().'  '.$text);
    }

    /**
     * Nightly script
     */
    public function nightly(): void
    {
        $this->load->library('US_Nightly', '', 'nightly');

        $this->_init_start('Start nightly');
        
        if (!$this->nightly->shall_run(US_Nightly::WHAT_NIGHTLY, 0.25)) {
            us_echo_console('Blocked');
            log_message('info', 'NIGHTLY may only run once every quarter hour');
            return;
        }
        
        $this->nightly->run();
        $this->nightly->run_reset_log_threshold();
        us_echo_console('Done');

        log_message('info', 'NIGHTLY processed successfully');
    }
    
    /**
     * Nightly check script
     */
    public function nightlycheck(): void
    {
        $this->load->library('US_Nightly', '', 'nightly');

        $this->_nightlycheck_run_check();
        $this->_nightlycheck_transfer_action_log();
    }

    /**
     * Nightly check run check only
     */
    private function _nightlycheck_run_check(): void
    {
        $this->_init_start('Start nightly check');

        if (!$this->nightly->shall_run(US_Nightly::WHAT_NIGHTLYCHECK, 0.25)) {
            us_echo_console('Blocked');
            log_message('info', 'NIGHTLYCHECK may only run once every quarter hour');
            return;
        }

        @ob_start(); //Catch output - just in case
        try {
            $this->nightly->run_check();
        } catch (Exception $exc) {
            echo $exc->getTraceAsString();
        }
        $reply = ob_get_contents();
        @ob_end_clean(); //Catch output - just in case

        us_echo_console('Done check');

        log_message('info', 'NIGHTLYCHECK processed successfully. Reply: '.$reply);
    }

    /**
     * Nightly check run transfer action log
     */
    private function _nightlycheck_transfer_action_log(): void
    {
        $this->_init_start('Start action log');

        $this->load->model('System_settings_model_readonly', 'system_settings_readonly');
        if (!$this->config->item('send_action_log')) {
            us_echo_console('Disabled');
            log_message('info', 'TRANSFER ACTION LOG is disabled');
            return;
        } else {
            us_echo_console('Enabled');
        }

        try {
            $min_age_hours = random_int(24, 72);
        } catch (Exception $e) {
            $min_age_hours = 48;
        }

        if (!$this->nightly->shall_run(US_Nightly::WHAT_TRANSFER_ACTION_LOG, $min_age_hours)) {
            us_echo_console('Blocked');
            log_message('info', 'TRANSFER ACTION LOG may only run once every '.$min_age_hours.' hours');
            return;
        }

        @ob_start(); //Catch output - just in case
        try {
            $this->nightly->run_transfer_action_log();
        } catch (Exception $exc) {
            echo $exc->getTraceAsString();
        }
        $reply = ob_get_contents();
        @ob_end_clean(); //Catch output - just in case

        us_echo_console('Done action log');

        log_message('info', 'TRANSFER ACTION LOG processed successfully. Reply: '.$reply);
    }

    /**
     * Nightly workhours script
     */
    public function nightlyworkhours(): void
    {
        $this->load->library('US_Nightly', '', 'nightly');

        $this->_init_start('Start nightly workhours');
        
        if (!$this->nightly->shall_run(US_Nightly::WHAT_NIGHTLYWORKHOURS, 0.25)) {
            us_echo_console('Blocked');
            log_message('info', 'NIGHTLYWORKHOURS may only run once every quarter hour');
            return;
        }
        
        $this->nightly->run_workhours();
        us_echo_console('Done');

        log_message('info', 'NIGHTLYWORKHOURS processed successfully');
    }
    
    /**
     * Different update actions
     */
    public function installupdate(): void
    {
        if (php_sapi_name()!=='cli') {
            echo "Please call this function using php update.php \n\n";
            die(1);
        }
        define('US_CLI_MODE', 1);
        
        
        $call_action_ok = false;
        if (isset($GLOBALS['argv']) && isset($GLOBALS['argv'][1])) {
            $call_action_ok = $this->installupdate_call_action($GLOBALS['argv'][1]);
        } else {
            $this->_init_start('Start install update');
            us_echo_console('');
            us_echo_console('  You may also start the action using a command line switch.');
            us_echo_console('  e.g.: php update.php -u');
        }
        
        while (!$call_action_ok) {
            us_echo_console('');
            us_echo_console('  What action do you want to perform?');
            us_echo_console('    u = install u_pdate');
            us_echo_console('    c = c_heck for a new update and install it');
            us_echo_console('    f = download new update and f_orce install it');
            us_echo_console('    d = dual update database and portlets');
            us_echo_console('    db = update database');
            us_echo_console('    p = update portlets');
            us_echo_console('    b = b_ackup database');
            us_echo_console('    drop = create drop script for views, functions and procedures.');
            us_echo_console('    workhours = workhours Write workhours');
            us_echo_console('    q = q_uit');
            us_echo_console('  :','');
            $call_action_ok = $this->installupdate_call_action(fgets(STDIN));
        }
        
        die(0);
    }
    
    /**
     * Try to call an action
     *
     * @param   string  $string     Command
     * @return  boolean Command ok
     */
    protected function installupdate_call_action($string)
    {
        $clean = strtolower( trim(str_ireplace('-', '', $string)) );
        switch ($clean) {
            case 'u':
                $this->installupdate_update();
                return true;
            case 'c':
                $this->installupdate_downloadupdate(false);
                return true;
            case 'f':
                $this->installupdate_downloadupdate(true);
                return true;
            case 'db':
                $this->installupdate_dbupdate();
                return true;
            case 'p':
                $this->installupdate_portletupdate();
                return true;
            case 'd':
                $this->installupdate_dbupdate();
                $this->installupdate_portletupdate();
                return true;
            case 'b':
                $this->installupdate_backupdb();
                return true;
            case 'initsaas':
                $this->installupdate_initsaas();
                return true;
            case 'workhours':
                $this->nightlyworkhours();
                return true;
            case 'sendhours':
                $this->sendhours();
                return true;
            case 'drop':
                $this->create_drop_script();
                return true;
            case 'q':
                return true;
        }
        return false;
    }
    
    /**
     * Create the drop script for views, functions and procedures
     */
    protected function create_drop_script(): void
    {
        $this->_init_start('-- Drop script for views, functions and procedures');//Date time info
        
        $this->load->model('Db_show_model', 'db_show');
        
        echo "\n";
        $functions = $this->db_show->get_functions();
        foreach ($functions as $function) {
            echo 'DROP FUNCTION IF EXISTS `'.$function.'`;'."\n";
        }
        echo "\n";
        
        echo "\n";
        $procedures = $this->db_show->get_procedures();
        foreach ($procedures as $procedure) {
            echo 'DROP PROCEDURE IF EXISTS `'.$procedure.'`;'."\n";
        }
        echo "\n";
         
        echo "\n";
        $views = $this->db_show->get_views();
        foreach ($views as $view) {
            echo 'DROP VIEW IF EXISTS `'.$view.'`;'."\n";
        }
        echo "\n";
        
        $this->_print_time('Done', '--');//Date time info
    }
    
    /**
     * Update the db
     */
    protected function installupdate_dbupdate(): void
    {
        $this->_init_start('Update Database');//Date time info
        
        $this->load->library('US_Db_update', '', 'db_update');
        
        $this->session->force_logoff_all();
        
        $path_to_from_current = INSTALLPATH.'data/from_current.sql';
        $ok = $this->db_update->update($path_to_from_current, null, function($msg) {
            us_echo_console($msg);
        });
        if ($ok!==true) {
            log_message('error', $ok);
            us_echo_console('ERROR: DB UPDATE - '.$ok);
            die(1);
        } else {
            log_message('error', "US UPDATE => US VERSION: ".$this->config->item('us_version'));
        }
        
        $this->_print_time('Done');//Date time info
    }

    /**
     * Update the portlets
     */
    protected function installupdate_portletupdate(): void
    {
        $this->_init_start('Update Portlets');//Date time info

        $this->load->library('US_Portlet_update', '', 'us_portlet_update');
        
        $ok = $this->us_portlet_update->update_portlets();
        if ($ok!==true) {
            log_message('error', $ok);
            us_echo_console('ERROR: PORTLETS UPDATE - '.$ok);
            die(1);
        }
        
        $this->_print_time('Done');//Date time info
    }
    
    /**
     * Backup the db
     */
    protected function installupdate_backupdb(): void
    {
        $this->_init_start('Backup Database');//Date time info
        @set_time_limit(36000);
        
        $this->load->library('US_Db_update', '', 'db_update');
        
        $path_to_from_current = INSTALLPATH.'data/from_current.sql';
        $backup_name = $this->config->item('us_updatesdir').'dbbackup_'.time().'.sql';
        
        $ok = $this->db_update->backup_db($path_to_from_current, $backup_name);
        if ($ok!==true) {
            log_message('error', $ok);
            us_echo_console('ERROR: BACKUP DB - '.$ok);
            die(1);
        }
        
        $this->_print_time('Done. Output file: '.$backup_name);//Date time info
    }

        /**
     * Download and install the update
     * 
     * @param   boolean     $force  Force update
     * @return  void
     */
    protected function installupdate_downloadupdate($force)
    {
        $this->_init_start('Download RSS');//Date time info
        
        $this->load->library('US_Update', '', 'update');
        
        process_update_progressbar(1, 100, false);
        
        try {
            $update_infos = $this->update->get_update_infos();
        } catch (Exception $exc) {
            $ok = 'Failed to download: '.$exc->getMessage();
            log_message('error', $ok);
            us_echo_console('ERROR: '.$ok);
            die(1);
        }
        
        if (!is_array($update_infos) || count($update_infos)<1) {
            $ok = 'Failed to download RSS or nothing to update';
            log_message('error', $ok);
            us_echo_console('ERROR: '.$ok);
            die(1);
        }
        
        $update_info = array_shift($update_infos);
        if ( empty($update_info['title']) or empty($update_info['link']) or empty($update_info['id']) ) {
            $ok = 'Downloaded RSS does not contain all necessary information';
            log_message('error', $ok);
            us_echo_console('ERROR: '.$ok);
            die(1);
        }
        
        process_update_progressbar(3, 100, false);
        
        if (!$force && $update_info['title']==$this->config->item('us_version')) {
            us_echo_console(sprintf(
                'Done: Already installed version %s equals the available version %s. Nothing to do.',
                $this->config->item('us_version'),
                $update_info['title']
            ));
            die(0);
        }

        $inst_info = false;
        list($maj_i, $min_i, $fix_i, $svn_i) = array_map('intval', explode('.',$this->config->item('us_version')));
        list($maj, $min, $fix, $svn) = array_map('intval', explode('.',$update_info['title']));
        if (    $maj_i < $maj ||
                ($maj_i === $maj && $min_i < $min) ||
                ($maj_i === $maj && $min_i === $min && $fix_i < $fix) ||
                ($maj_i === $maj && $min_i === $min && $fix_i === $fix && $svn_i <= $svn)
        ) {
            $inst_info = $update_info;
        } else {
            foreach ($update_infos as $update_info) {
                if ($update_info['title'] === $this->config->item('us_version')) {
                    $inst_info = $update_info;
                    break;
                }
            }
        }

        if ($inst_info === false) {
            us_echo_console(sprintf(
                'Update of Version %s not possible. There is no newer official version available yet.',
                $this->config->item('us_version')
            ));
            die(0);       
        }
        
        process_update_progressbar(4, 100, false);
        
        $length = ( !empty($inst_info['link_lengttext']) ) ? '('.$inst_info['link_lengttext'].')' : '';
        $this->_print_time('Download update '.$length);//Date time info
        if (!$this->update->get_update($inst_info['id'])) {
            $ok = 'Failed to download update';
            log_message('error', $ok);
            us_echo_console('ERROR: '.$ok);
            die(1);
        }
        
        $this->installupdate_update();
    }

    /**
     * Install the update
     */
    protected function installupdate_update(): void
    {
        $this->_init_start('Install Update');//Date time info
        
        $this->load->library('US_Update', '', 'update');
        
        $ok = $this->update->install_update(true);
        if ($ok===false) {
            $msg = 'Install failed - No file downloaded';
            log_message('error', $msg);
            us_echo_console('ERROR: '.$msg);
            die(1);
        } elseif ($ok!==true) {
            log_message('error', $ok);
            us_echo_console('ERROR: '.$ok);
            die(1);
        }
        
        $this->_print_time('Done');//Date time info
    }

    /**
     * Init SaaS
     */
    protected function installupdate_initsaas(): void
    {
        $this->_init_start('Init SaaS');//Date time info
        
        $this->load->helper('init_saas');
        
        init_saas();

        $this->_print_time('Done');//Date time info
    }

    protected function sendhours(): void
    {
        $this->_init_start('Send hours to external system');//Date time info

        $this->load->helper('sendhours_external');

        sendhours_external();

        $this->_print_time('Done');//Date time info
    }
    
    /**
     * Try to update the LDAP
     */
    public function ldapupdate(): void
    {
        if (php_sapi_name()!=='cli') {
            echo "Please call this function using php ldap.php \n\n";
            die(1);
        }
        define('US_CLI_MODE', 1);
        
        $this->_init_start('Start ldap update');
        $this->_print_time('LDAP update');//Date time info
        
        $this->load->model('Specific_config_model', 'specific_config');
        $config = array(
            'host'              =>  $this->specific_config->get('ldap_host', false),
            'port'              =>  $this->specific_config->get('ldap_port', false),
            'security'          =>  $this->specific_config->get('ldap_security', false),
            'bind_rdn'          =>  $this->specific_config->get('ldap_bind_rdn', false),
            'password'          =>  $this->specific_config->get('ldap_bind_password', false),
            'dn'                =>  $this->specific_config->get('ldap_dn', false),
            'mode'              =>  $this->specific_config->get('ldap_mode', false),
            'uidprefix'         =>  $this->specific_config->get('ldap_uidprefix', false),
        );
        $this->load->library('US_Ldap', $config, 'ldap');

        if (!$this->ldap->is_active()) {
            us_echo_console('ERROR: No LDAP support active');
            us_echo_console('ERROR: OR the parameters host, port, dn, bind_rdn, mode are not defined within configuration ldap.');
            die(1);
        }
        
        $this->ldap->upload_all();
        
        $this->_print_time('Done');//Date time info
        die(0);
    }

    
} // END class Common
