Viewing File: /usr/local/cpanel/base/3rdparty/roundcube/plugins/cpanelchecks/cpanelchecks.php

<?php

/**
 +-----------------------------------------------------------------------+
 |                                                                       |
 | cpanelchecks                            Copyright 2022 cPanel, L.L.C. |
 |                                                  All rights reserved. |
 | copyright@cpanel.net                               https://cpanel.net |
 | This code is subject to the cPanel license.                           |
 | Unauthorized copying is prohibited.                                   |
 |                                                                       |
 +-----------------------------------------------------------------------+
*/

class cpanelchecks extends rcube_plugin
{
    private const PLUGIN_VERSION = 'v11.120';

    /**
     * Information about this plugin that is queried by roundcube.
     */
    private const PLUGIN_INFO = [
        'name' => 'cpanelchecks',
        'vendor' => 'cPanel, LLC',
        'version' => self::PLUGIN_VERSION,
        'license' => 'cPanel License',
        'uri' => 'https://cpanel.net'
    ];

    const LOG_DISABLED = 0;
    const LOG_FAILURE = 1;
    const LOG_VERBOSE = 2;
    const TOUCH_FILE = '.skip_cpanel_checks';

    public $task = 'login';
    private $rc;
    private $rcmail;
    private $log_level;

    private $db_schema_updates = [ '2022120700', '2023030100' ];

    function init()
    {
        $this->add_hook('login_after', array($this, 'performChecks'));
    }

    function performChecks($params)
    {
        $this->initialize();
        // read database config
        $db = $this->rc->get_dbh();
        $dbtype = $db->db_provider;
        // Install/upgrade schema if needed
        $this->_check_schema($dbtype);
        return $params;
    }

    /**
     * Initialize an rcmail instance for our user
     * and load our configuration
     */
    private function initialize()
    {
        $this->rc = rcube::get_instance();
        $this->rcmail = rcmail::get_instance();
        // Load plugin's config file
        $this->load_config();
        $this->log_level = $this->rcmail->config->get('cpanel_log_level');
    }

    private function _check_schema($dbtype) {
        $this->_schema_check($dbtype, 'libkolab-version');
        $this->_schema_check($dbtype, 'calendar-database-version');
        $this->_schema_check($dbtype, 'calendar-caldav-version');
        return;
    }

    private function _schema_check($dbtype, $pluginkey) {
        $query = $this->rc->db->query(
            "SELECT `value` FROM `system` WHERE name=?",
            $pluginkey,
        );

        // RCube db accessor has no simple "fetch", so SELECT count(*) actually
        // is counterproductive. Just do an empty check on no hits
        $record = $this->rc->db->fetch_assoc();

        // If you don't do this, you'll die later due to db locked.
        // Tolerate it being a bool, as that's another fail state of query :(
        if( !is_bool($query) ) {
            $query->closeCursor();
        }

        if(empty($record)) {
            $this->_schema_install($dbtype,$pluginkey);
        } else {
            foreach( $this->db_schema_updates as $db_schema_version ) {
                if( intval($record['value']) >= intval($db_schema_version) ) continue;
                $this->_schema_update($dbtype, $db_schema_version, $pluginkey);
            }
        }
        return;
    }

    private function _schema_install($dbtype, $pluginkey) {
        $specifics = explode('-', preg_replace('/-version$/', '', $pluginkey));
        if(!empty($specifics[1])) {
            $file = realpath( "/usr/local/cpanel/base/3rdparty/roundcube/plugins/" . $specifics[0] . "/drivers/" . $specifics[1] . "/SQL/" . $dbtype . ".initial.sql" );
        } else {
            $file = realpath( "/usr/local/cpanel/base/3rdparty/roundcube/plugins/" . $specifics[0] . "/SQL/" . $dbtype . ".initial.sql" );
        }
        return $this->_do_schema_sql($file, $dbtype);
    }

    private function _schema_update($dbtype, $db_schema_version, $pluginkey) {
        $specifics = explode('-', preg_replace('/-version$/', '', $pluginkey));
        if(!empty($specifics[1])) {
            $file = realpath( "/usr/local/cpanel/base/3rdparty/roundcube/plugins/" . $specifics[0] . "/drivers/" . $specifics[1] . "/SQL/" . $dbtype . '.' . $db_schema_version . '.migration.sql' );
        } else {
            $file = realpath( "/usr/local/cpanel/base/3rdparty/roundcube/plugins/" . $specifics[0] . "/SQL/" . $dbtype . '.' . $db_schema_version . '.migration.sql' );
        }
        if(empty($file)) return;
        return $this->_do_schema_sql($file, $dbtype);
    }

    private function _do_schema_sql($file, $dbtype, $retry_on_lock=true) {
        if(file_exists($file)) {
            error_log("Schema updates detected in $file, applying now...");
            $query_raw = file_get_contents($file);
            $this->rc->db->dbh->exec($query_raw);
            $error = $this->rc->db->dbh->errorInfo();
            if ($error[0] != '00000') {
                if( $dbtype == 'sqlite' && $error[1] == 6 && $retry_on_lock ) {
                    # retry once on DB locked
                    //return $this->_do_schema_sql($file, $dbtype, false);
                }
                error_log("Schema update query from $file failed with code " . $error[1] . ": " . $error[2]);
                return false;
            }
        } else {
            error_log("Couldn't find schema update file: $file");
        }
        return true;
    }

    private function log($message, $level = cpanelchecks::LOG_VERBOSE)
    {
        if ($this->log_level && $level <= $this->log_level) {
            error_log("[CPANELSQLCHECK: {$message}]");
        }
    }
}
Back to Directory File Manager