From 99f904adcc37d93c90defcd8ce898598e25be212 Mon Sep 17 00:00:00 2001 From: Hugues Hiegel Date: Wed, 11 Mar 2015 16:55:04 +0100 Subject: Lot of plugins --- db_version/db_version.php | 474 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 474 insertions(+) create mode 100644 db_version/db_version.php (limited to 'db_version/db_version.php') diff --git a/db_version/db_version.php b/db_version/db_version.php new file mode 100644 index 0000000..6b43e98 --- /dev/null +++ b/db_version/db_version.php @@ -0,0 +1,474 @@ + 'MySQL', 'pgsql' => 'PostGreSQL', 'sqlite' => 'Sqlite v.3', 'mssql' => 'MSSQL'); + + /* unified plugin properties */ + static private $plugin = 'db_version'; + static private $author = 'myroundcube@mail4us.net'; + static private $authors_comments = 'Documentation'; + static private $download = 'http://myroundcube.googlecode.com'; + static private $version = '3.1.10'; + static private $date = '21-01-2015'; + static private $licence = 'All Rights reserved'; + static private $requirements = array( + 'Roundcube' => '1.0', + 'PHP' => '5.3', + 'required_plugins' => array( + 'codemirror_ui' => 'require_plugin', + ), + ); + static private $prefs = array( + ); + static private $config_dist = null; + static private $db_map = array( + 'sqlite' => 'sqlite', + 'sqlite2' => 'sqlite', + 'sqlite3' => 'sqlite', + 'sybase' => 'mssql', + 'dblib' => 'mssql', + 'sqlsrv' => 'mssql', + 'mssql' => 'mssql', + 'mysql' => 'mysql', + 'mysqli' => 'mysql', + 'pgsql' => 'pgsql', + 'postgresql' => 'pgsql', + ); + static private $default_tables = array( + 'users', + 'attachments', + 'cache', + 'cache_index', + 'cache_thread', + 'cache_tables', + 'cache_messages', + 'cache_shared', + 'contactgroupmembers', + 'contactgroups', + 'contacts', + 'dictionary', + 'identities', + 'searches', + 'system' + ); + static private $f; + + function init(){ + self::$f = $this; + } + + static public function about($keys = false){ + $requirements = self::$requirements; + foreach(array('required_', 'recommended_') as $prefix){ + if(is_array($requirements[$prefix.'plugins'])){ + foreach($requirements[$prefix.'plugins'] as $plugin => $method){ + if(class_exists($plugin) && method_exists($plugin, 'about')){ + /* PHP 5.2.x workaround for $plugin::about() */ + $class = new $plugin(false); + $requirements[$prefix.'plugins'][$plugin] = array( + 'method' => $method, + 'plugin' => $class->about($keys), + ); + } + else{ + $requirements[$prefix.'plugins'][$plugin] = array( + 'method' => $method, + 'plugin' => $plugin, + ); + } + } + } + } + return array( + 'plugin' => self::$plugin, + 'version' => self::$version, + 'date' => self::$date, + 'author' => self::$author, + 'comments' => self::$authors_comments, + 'licence' => self::$licence, + 'requirements' => $requirements, + ); + } + + function form($p){ + return self::$out; + } + + static public function exec($plugin, $tables, $dbversion){ + $rcmail = rcmail::get_instance(); + /* + if($rcmail->action == 'plugin.plugin_manager_update' || $rcmail->action == 'about' || $rcmail->action == 'alive' || $rcmail->action == 'refresh'){ + return true; + } + */ + if($rcmail->action == 'about'){ // Do not force database adjustments on about request + return true; + } + if(get_input_value('_load_pm_settings', RCUBE_INPUT_GET)){ + if($plugin = get_input_value('_plugin', RCUBE_INPUT_GET)){ + $append = '&_plugin=' . $plugin; + } + else{ + $append = ''; + } + if(!self::$done){ + self::$done = true; + if($rcmail->config->get('skin', 'classic') == 'classic'){ + $frame = 'prefs-frame'; + } + else{ + $frame = 'preferences-frame'; + } + $rcmail->output->add_script('$("#'. $frame . '").attr("src", "./?_task=settings&_action=edit-prefs&_section=plugin_manager_settings&_framed=1' . $append . '");', 'docready'); + } + return false; + } + $return = false; + $dbversion = implode('|', $dbversion); + $query = 'SELECT * FROM ' . get_table_name('system') . ' WHERE ' . $rcmail->db->quoteIdentifier('name') . '=?'; + $res = $rcmail->db->limitquery($query, 0, 1, 'myrc_' . $plugin); + $version = $rcmail->db->fetch_assoc($res); + if($error = $rcmail->db->is_error()){ + write_log('db_version', $error); + return false; + } + if($version['value'] == $dbversion){ + $return = true; + } + else if($plugin == 'plugin_manager' || $rcmail->config->get('plugin_manager_hash')){ + $hash = $rcmail->config->get('plugin_manager_hash'); + if(($plugin == 'plugin_manager' && !is_array($version)) || ($rcmail->task != 'logout' && file_exists(INSTALL_PATH . $hash . '.myrc'))){ + if(get_input_value('_framed', RCUBE_INPUT_GPC)){ + return false; + } + $type = current(explode(':', $rcmail->config->get('db_dsnw'), 2)); + $type = self::$db_map[$type]; + if(!$type){ + if(!file_exists(slashify($rcmail->config->get('log_dir', 'logs/')) . 'dbtypefailure')){ + write_log('dbtypefailure', 'MyRoundcube Fatal Error'); + write_log('dbtypefailure', '-----------------------'); + write_log('dbtypefailure', 'Failed to detect database type.'); + write_log('dbtypefailure', 'Please contact MyRoundcube Dev Team (dev-team@myroundcube.com) and mail the following string (don\'t miss to mask password and to clear your Browser cache):'); + write_log('dbtypefailure', ' '); + write_log('dbtypefailure', $rcmail->config->get('db_dsnw')); + } + $content = file_get_contents(slashify($rcmail->config->get('log_dir', 'logs/')) . 'dbtypefailure'); + $content = explode("\n", $content); + $out = ''; + foreach($content as $nb => $line){ + $line = explode("]: ", $line); + $out .= $line[1] . "\n"; + } + $rcmail->output->nocacheing_headers(); + die('MyRoundcube Fatal Error
' . $out . '
'); + } + if(get_input_value('_remote', RCUBE_INPUT_GPC) && get_input_value('_action', RCUBE_INPUT_GPC) != 'plugin.plugin_manager_update_notifier' && !$_SESSION['db_version_lock']){ + $db_version = explode('|', $dbversion); + $redirect = true; + $reward = false; + $missing = ''; + foreach($db_version as $script){ + if(!file_exists(INSTALL_PATH . 'plugins/' . $plugin . '/SQL/' . $type . '.' . $script . '.sql') && !file_exists(INSTALL_PATH . 'plugins/' . $plugin . '/sql/' . $type . '.' . $script . '.sql')){ + $rcmail->output->show_message('Plugin "' . $plugin . '": Can\'t load plugin. ' . self::$map[strtolower($type)] . ' database scripts are incomplete.', 'error'); + $redirect = false; + $missing .= $plugin . ': plugins/' . $plugin . '/SQL/' . $type . '.' . $script . '.sql does not exist!'; + //$reward = '--> REWARD MyRC$ 5: Create missing scripts and mail it to dev-team@myroundcube.com (include your customer ID)!'; + } + } + if($reward && !$_SESSION['db_version_missing_logged_' . $plugin]){ + $_SESSION['db_version_missing_logged_' . $plugin] = true; + write_log('errors', $missing); + //write_log('errors', $reward); + } + if($redirect){ + echo json_encode(array('exec' => 'document.location.href="./"')); + exit; + } + } + $return = db_version::db_versioning($plugin, array_merge(array('system'), $tables)); + } + } + else if($plugin == 'db_config'){ + $return = true; + } + return $return; + } + + static public function test_permissions($type, $db){ + if($type == 'sqlite'){ + return true; + } + $sql = @file_get_contents(INSTALL_PATH . 'plugins/db_version/SQL/' . $type . '.initial.sql'); + if(!$sql){ + $sql = @file_get_contents(INSTALL_PATH . 'plugins/db_version/sql/' . $type . '.initial.sql'); + } + if($sql){ + $sql = trim($sql) . "\r\n"; + $sql = self::fix_table_names($sql, array('myrc')); + $sql = explode(';', $sql); + foreach($sql as $line){ + if(trim($line)){ + if(!$db->query($line)){ + $command = explode(' ', $line); + return $command[0]; + } + } + } + return true; + } + else{ + return $type; + } + } + + static public function db_versioning($plugin, $tables){ + $rcmail = rcmail::get_instance(); + /* PHP 5.2.x workaround for $plugin::about() */ + $class = new $plugin(false); + $db_version = $class->about(array('db_version')); + $db_version = $db_version['db_version']; + if(is_array($db_version)){ + $database = parse_url($rcmail->config->get('db_dsnw')); + if(!$database){ + $database = parse_url(end(explode(':', $rcmail->config->get('db_dsnw'), 2))); + $database['scheme'] = current(explode(':', $rcmail->config->get('db_dsnw'), 2)); + } + $type = self::$db_map[$database['scheme']]; + $sql = 'SELECT * FROM ' . get_table_name('system') . ' WHERE ' . $rcmail->db->quote_identifier('name') . '=?'; + $res = $rcmail->db->limitquery($sql, 0, 1, 'myrc_' . $plugin); + $system = $rcmail->db->fetch_assoc($res); + if($system){ + $versions = explode('|', $system['value']); + } + if(implode('|', $db_version) == $system['value']){ + return true; + } + if(count($_POST) == 0){ + $sql = ''; + foreach($db_version as $script){ + if(!is_array($versions) || !in_array($script, $versions)){ + if(file_exists(INSTALL_PATH . 'plugins/' . $plugin . '/SQL/' . $type . '.' . $script . '.sql')){ + $sql .= trim(file_get_contents(INSTALL_PATH . 'plugins/' . $plugin . '/SQL/' . $type . '.' . $script . '.sql')) . "\r\n"; + } + else if(file_exists(INSTALL_PATH . 'plugins/' . $plugin . '/sql/' . $type . '.' . $script . '.sql')){ + $sql .= trim(file_get_contents(INSTALL_PATH . 'plugins/' . $plugin . '/sql/' . $type . '.' . $script . '.sql')) . "\r\n"; + } + else{ + return false; + } + } + } + if($sql){ + $disable = ''; + if($plugin != 'plugin_manager'){ + $res = $rcmail->db->limitquery('SELECT * FROM ' . get_table_name('plugin_manager') . ' WHERE ' . $rcmail->db->quote_identifier('conf') . '=?', 0, 1, '_plugin_manager_file_based_config'); + $fb = $rcmail->db->fetch_assoc($res); + if($fb['value'] == 0){ + $disable = ' ' . html::tag('input', array('onclick' => 'document.location.href="./?_task=settings&_load_pm_settings=1&_plugin=' . $plugin . '"', 'type' => 'button', 'class' => 'button', 'value' => 'Disable Plugin')); + } + } + if($database['user']){ + $dsnw = $database['scheme'] . '://' . $database['user'] . ':***masked***@' . $database['host'] . $database['path']; + } + else{ + $dsnw = $rcmail->config->get('db_dsnw'); + } + $v = explode('-', RCMAIL_VERSION); + $v = trim($v[0]); + if(version_compare($v, '0.9', '>=')){ + $out = + html::tag('div', array('style' => 'font-size: 12px; text-align: justify; position: absolute; margin-left: auto; left: 50%; margin-left: -400px; width: 800px;'), + html::tag('h3', null, 'Plugin Manager database versioning: Plugin "' . $plugin . '"') . + 'Plugin Manager has detected required missing adjustments in your database. The following commands will be executed against your database ' . + html::tag('b', null, $dsnw . ':') . html::tag('br') . html::tag('br') . + html::tag('textarea', array('readonly' => true, 'rows' => '18', 'cols' => '95', 'id' => 'code'), self::fix_table_names($sql, $tables)) . + html::tag('table', null, + html::tag('tr', null, html::tag('td', null, html::tag('input', array('type' => 'radio', 'name' => '_dbadjust_agreed', 'checked' => true, 'value' => 1, 'onclick' => '$("#submitbutton").show();$("#donebox").hide()')) . + html::tag('td', null, 'I have a recent database backup. I understand the code and I agree to execute it. '))) . + html::tag('tr', null, html::tag('td', null, html::tag('input', array('type' => 'radio', 'name' => '_dbadjust_agreed', 'value' => 0, 'onclick' => '$("#submitbutton").hide();$("#donebox").show()')) . + html::tag('td', null, 'I will take care of the necessary ' . + html::tag('a', array('href' => 'http://myroundcube.com/myroundcube-plugins/faqs/myroundcube-plugins-database-versioning-support', 'target' => '_new'), 'database adjustments') . ' by myself. ' . + 'I\'m aware that MyRoundcube "' . $plugin . '" plugin does not work without these database adjustments. '))) + ) . + html::tag('center', null, html::tag('input', array('id' => 'submitbutton', 'type' => 'submit', 'value' => 'Submit')) . $disable) . html::tag('br') . + html::tag('center', array('style' => 'display: none;', 'id' => 'donebox'), html::tag('input', array('type' => 'checkbox', 'onclick' => '$("#submitbutton").show();$("#donebox").hide();$(this).prop("checked", false)')) . html::tag('span', null, ' ' . + html::tag('b', null, 'I have unregistered this plugin or I have already applied database adjustments manually.'))) + ); + $hidden = new html_hiddenfield(array('name' => '_token', 'value' => $rcmail->get_request_token())); + $out .= "\n" . $hidden->show(); + $out .= html::tag('script', array('type' => 'text/javascript'), '$("#taskbar.topright").hide();'); + $section = ''; + if($section = get_input_value('_plugin_manager_settings_section', RCUBE_INPUT_GET)){ + $section = '&_plugin_manager_settings_section=' . $section . '&_expand=' . $plugin; + } + $form = html::tag('form', array('method' => 'post', 'action' => './?_task=' . $rcmail->task . $section), $out); + } + else{ + $out = + html::tag('div', array('style' => 'font-size: 12px; text-align: justify; position: absolute; margin-left: auto; left: 50%; margin-left: -400px; width: 800px;'), + html::tag('h3', null, 'Plugin Manager database versioning: Plugin "' . $plugin . '"') . + 'Plugin Manager has detected required missing adjustments in your database. Please execute the following commands against your database ' . + html::tag('b', null, $database['scheme'] . '://' . $database['user'] . ':***masked***@' . $database['host'] . $database['path'] . ':') . html::tag('br') . html::tag('br') . + html::tag('textarea', array('readonly' => true, 'rows' => '18', 'cols' => '95', 'id' => 'code'), self::fix_table_names($sql, $tables)) . + html::tag('table', null, + html::tag('tr', null, html::tag('td', null, + html::tag('td', null, 'Please backup your database. ' . + 'Please note, that MyRoundcube "' . $plugin . '" plugin does not work without these database adjustments.'))) + ) . + html::tag('center', null, html::tag('input', array('id' => 'submitbutton', 'type' => 'submit', 'value' => 'Database has been backed up and commands have been executed.'))) + ); + $hidden = new html_hiddenfield(array('name' => '_token', 'value' => $rcmail->get_request_token())); + $out .= "\n" . $hidden->show(); + $out .= html::tag('script', array('type' => 'text/javascript'), '$("#taskbar.topright").hide();'); + $section = ''; + if($section = get_input_value('_plugin_manager_settings_section', RCUBE_INPUT_GET)){ + $section = '&_plugin_manager_settings_section=' . $section . '&_expand=' . $plugin; + } + $form = html::tag('form', array('method' => 'post', 'action' => './?_task=' . $rcmail->task . $section), $out); + } + $out = str_replace('##FORM##', $form, @file_get_contents(INSTALL_PATH . 'plugins/db_version/skins/' . $rcmail->config->get('skin', 'classic') . '/sql.html')); + if(method_exists($rcmail->output, 'just_parse')){ + send_nocacheing_headers(); + header('Content-Type: text/html; charset=' . RCMAIL_CHARSET); + echo $rcmail->output->just_parse($out); + exit; + } + } + } + else{ + if(get_input_value('_dbadjust_agreed', RCUBE_INPUT_POST)){ + unset($_POST); + $rcmail->session->remove('db_version_lock'); + if($dsn = $rcmail->config->get('db_dsnw_superadmin')){ + $rcmail->db = rcube_db::factory($dsn . '?new_link=true', '', false); + $rcmail->db = $rcmail->db->factory($dsn . '?new_link=true'); + $rcmail->db->set_debug((bool)$rcmail->config->get('sql_debug')); + $rcmail->db->db_connect('r'); + } + $ret = self::test_permissions($type, $rcmail->db); + $sversions = is_array($system) ? $system['value'] : ''; + $sversions = explode('|', $sversions); + if($ret !== true){ + $sql = ''; + foreach($db_version as $script){ + if(!in_array($script, $sversions)){ + if(file_exists(INSTALL_PATH . 'plugins/' . $plugin . '/SQL/' . $type . '.' . $script . '.sql')){ + $sql .= self::fix_table_names(trim(file_get_contents(INSTALL_PATH . 'plugins/' . $plugin . '/SQL/' . $type . '.' . $script . '.sql')) . "\r\n", $tables); + } + else if(file_exists(INSTALL_PATH . 'plugins/' . $plugin . '/sql/' . $type . '.' . $script . '.sql')){ + $sql .= self::fix_table_names(trim(file_get_contents(INSTALL_PATH . 'plugins/' . $plugin . '/sql/' . $type . '.' . $script . '.sql')) . "\r\n", $tables); + } + } + } + if($ret === $type){ + $rcmail->output->show_message('Plugin "db_version": ' . self::$map[strtolower($type)] . ' database is not supported!', 'error'); + return false; + } + self::html_output($sql, $ret); + } + foreach($db_version as $script){ + if(!in_array($script, $sversions)){ + if(file_exists(INSTALL_PATH . 'plugins/' . $plugin . '/SQL/' . $type . '.' . $script . '.sql')){ + $sql = self::fix_table_names(trim(file_get_contents(INSTALL_PATH . 'plugins/' . $plugin . '/SQL/' . $type . '.' . $script . '.sql')) . "\r\n", $tables); + $lines = explode(';', $sql); + foreach($lines as $line){ + if(trim($line)){ + $rcmail->db->query(trim($line)); + } + } + } + else if(file_exists(INSTALL_PATH . 'plugins/' . $plugin . '/sql/' . $type . '.' . $script . '.sql')){ + $sql = self::fix_table_names(trim(file_get_contents(INSTALL_PATH . 'plugins/' . $plugin . '/sql/' . $type . '.' . $script . '.sql')) . "\r\n", $tables); + $lines = explode(';', $sql); + foreach($lines as $line){ + if(trim($line)){ + $rcmail->db->query(trim($line)); + } + } + } + } + } + $sql = 'DELETE from ' . get_table_name('system') . ' WHERE ' . $rcmail->db->quoteIdentifier('name') . '=?'; + $rcmail->db->query($sql, 'myrc_' . $plugin); + $sql = 'INSERT INTO ' . get_table_name('system') . ' (' . $rcmail->db->quote_identifier('name') . ', ' . $rcmail->db->quote_identifier('value') . ') VALUES(?, ?)'; + $res = $rcmail->db->query($sql, 'myrc_' . $plugin, implode('|', $db_version)); + if($rcmail->db->affected_rows($res)){ + return true; + } + else{ + return false; + } + } + } + } + } + + static public function html_output($script, $command = false){ + $rcmail = rcmail::get_instance(); + $config_var = '$config'; + $config_file = 'config.inc.php'; + $v = current(explode('-', RCMAIL_VERSION)); + if(version_compare($v, '1.0', '<')){ + $config_var = '$rcmail_config'; + $config_file = 'db.inc.php'; + } + $dbconfig = parse_url($rcmail->config->get('db_dsnw')); + $out = html::tag('div', array('style' => 'font-size: 12px; text-align: justify; position: absolute; margin-left: auto; left: 50%; margin-left: -400px; width: 800px;'), + html::tag('h3', null, sprintf('Database adjustment failed (Command: %s)', $command)) . + html::tag('span', null, 'The user ' . + html::tag('b', null, '"' . ($dbconfig['user'] ? $dbconfig['user'] : get_current_user()) . '"') .' does not seem to have sufficient permissions on the database ' . + html::tag('b', null, '"' . ($dbconfig['path'] ? substr($dbconfig['path'], 1) : $rcmail->config->get('db_dsnw')) . '"') . ':' . html::tag('br') . html::tag('br') . + ' ' . html::tag('center', null, html::tag('i', null, 'Required permissions: SELECT / INSERT / UPDATE / DELETE / CREATE / ALTER / INDEX / DROP')) . + html::tag('br') . + 'Please, grant the user sufficient permissions on the database or execute the following database script manually:') . html::tag('br') . html::tag('br') . + html::tag('textarea', array('readonly' => true, 'rows' => '10', 'cols' => '95', 'id' => 'code'), self::fix_table_names($script, array('plugin_manager'))) . + html::tag('br') . html::tag('br') . + html::tag('div', array('style' => $display_superadmin ? 'block' : 'none'), + $dbconfig['user'] ? + ( + html::tag('left', null, html::tag('b', null, 'Alternatively') . ' add a different database user with sufficient permissions to your configuration in ' . + html::tag('b', null, '"./config/' . $config_file . '"') . ':' . html::tag('br') . html::tag('br') . + html::tag('textarea', array('readonly' => true, 'cols' => '95', 'rows' => '2'), + $config_var . '[\'db_dsnw_superadmin\'] = \'' . $dbconfig['scheme'] . '://root:password@' . $dbconfig['host'] . $dbconfig['path'] . '\';')) . + html::tag('br') . html::tag('br') + ) : '' + ) . + html::tag('div', array('style' => 'display: inline; float: left'), + html::tag('a', array('href' => 'javascript:void(0)', 'onclick' => 'document.location.href=\'./\''), 'Done') + ) + ); + $out .= html::tag('script', array('type' => 'text/javascript'), '$("#taskbar.topright").hide();'); + $out = str_replace('##FORM##', $out, @file_get_contents(INSTALL_PATH . 'plugins/db_version/skins/' . $rcmail->config->get('skin', 'classic') . '/sql.html')); + send_nocacheing_headers(); + header('Content-Type: text/html; charset=' . RCMAIL_CHARSET); + echo $rcmail->output->just_parse($out); + exit; + } + + static public function fix_table_names($sql, $tables){ + $tables = array_merge(self::$default_tables, $tables); + $DB = rcmail::get_instance()->db; + if(is_array($tables)){ + foreach($tables as $table){ + $real_table = $DB->table_name($table); + if($real_table != $table){ + $sql = preg_replace("/([^a-z0-9_'])$table([^a-z0-9_'])/i", "\\1$real_table\\2", $sql); + } + } + } + return $sql; + } +} +?> \ No newline at end of file -- cgit v1.2.3