| | |
| | | var $config = array(); |
| | | var $configured = false; |
| | | var $last_error = null; |
| | | var $db_map = array('pgsql' => 'postgres', 'mysqli' => 'mysql', 'sqlsrv' => 'mssql'); |
| | | var $email_pattern = '([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9])'; |
| | | var $bool_config_props = array(); |
| | | |
| | | var $obsolete_config = array('db_backend'); |
| | | var $obsolete_config = array('db_backend', 'double_auth'); |
| | | var $replaced_config = array( |
| | | 'skin_path' => 'skin', |
| | | 'locale_string' => 'language', |
| | |
| | | ); |
| | | |
| | | // these config options are required for a working system |
| | | var $required_config = array('db_dsnw', 'db_table_contactgroups', 'db_table_contactgroupmembers', 'des_key'); |
| | | var $required_config = array( |
| | | 'db_dsnw', 'db_table_contactgroups', 'db_table_contactgroupmembers', |
| | | 'des_key', 'session_lifetime', |
| | | ); |
| | | |
| | | /** |
| | | * Constructor |
| | |
| | | $this->config = array(); |
| | | $this->load_defaults(); |
| | | |
| | | foreach ($this->replaced_config as $prop => $replacement) |
| | | foreach ($this->replaced_config as $prop => $replacement) { |
| | | if (isset($current[$prop])) { |
| | | if ($prop == 'skin_path') |
| | | $this->config[$replacement] = preg_replace('#skins/(\w+)/?$#', '\\1', $current[$prop]); |
| | |
| | | $this->config[$replacement] = $current[$prop] ? 2 : 0; |
| | | else |
| | | $this->config[$replacement] = $current[$prop]; |
| | | |
| | | unset($current[$prop]); |
| | | } |
| | | unset($current[$prop]); |
| | | } |
| | | |
| | | foreach ($this->obsolete_config as $prop) { |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | if ($current['keep_alive'] && $current['session_lifetime'] < $current['keep_alive']) |
| | | $current['session_lifetime'] = max(10, ceil($current['keep_alive'] / 60) * 2); |
| | | |
| | | $this->config = array_merge($this->config, $current); |
| | | |
| | |
| | | if (!$this->configured) |
| | | return false; |
| | | |
| | | // simple ad hand-made db schema |
| | | $db_schema = array( |
| | | 'users' => array(), |
| | | 'identities' => array(), |
| | | 'contacts' => array(), |
| | | 'contactgroups' => array(), |
| | | 'contactgroupmembers' => array(), |
| | | 'cache' => array(), |
| | | 'messages' => array(), |
| | | 'session' => array(), |
| | | ); |
| | | |
| | | // read reference schema from mysql.initial.sql |
| | | $db_schema = $this->db_read_schema(INSTALL_PATH . 'SQL/mysql.initial.sql'); |
| | | $errors = array(); |
| | | |
| | | // check list of tables |
| | |
| | | |
| | | foreach ($db_schema as $table => $cols) { |
| | | $table = !empty($this->config['db_table_'.$table]) ? $this->config['db_table_'.$table] : $table; |
| | | if (!in_array($table, $existing_tables)) |
| | | $errors[] = "Missing table ".$table; |
| | | // TODO: check cols and indices |
| | | if (!in_array($table, $existing_tables)) { |
| | | $errors[] = "Missing table '".$table."'"; |
| | | } |
| | | else { // compare cols |
| | | $db_cols = $DB->list_cols($table); |
| | | $diff = array_diff(array_keys($cols), $db_cols); |
| | | if (!empty($diff)) |
| | | $errors[] = "Missing columns in table '$table': " . join(',', $diff); |
| | | } |
| | | } |
| | | |
| | | return !empty($errors) ? $errors : false; |
| | | } |
| | | |
| | | /** |
| | | * Utility function to read database schema from an .sql file |
| | | */ |
| | | private function db_read_schema($schemafile) |
| | | { |
| | | $lines = file($schemafile); |
| | | $table_block = false; |
| | | $schema = array(); |
| | | foreach ($lines as $line) { |
| | | if (preg_match('/^\s*create table `?([a-z0-9_]+)`?/i', $line, $m)) { |
| | | $table_block = $m[1]; |
| | | } |
| | | else if ($table_block && preg_match('/^\s*`?([a-z0-9_-]+)`?\s+([a-z]+)/', $line, $m)) { |
| | | $col = $m[1]; |
| | | if (!in_array(strtoupper($col), array('PRIMARY','KEY','INDEX','UNIQUE','CONSTRAINT','REFERENCES','FOREIGN'))) { |
| | | $schema[$table_block][$col] = $m[2]; |
| | | } |
| | | } |
| | | } |
| | | |
| | | return $schema; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Compare the local database schema with the reference schema |
| | |
| | | } |
| | | |
| | | return $out; |
| | | } |
| | | |
| | | /** |
| | | * Create a HTML dropdown to select a previous version of Roundcube |
| | | */ |
| | | function versions_select($attrib = array()) |
| | | { |
| | | $select = new html_select($attrib); |
| | | $select->add(array('0.1-stable', '0.1.1', '0.2-alpha', '0.2-beta', '0.2-stable', '0.3-stable', '0.3.1', '0.4-beta', '0.4.2', '0.5-beta', '0.5', '0.5.1')); |
| | | return $select; |
| | | } |
| | | |
| | | |
| | |
| | | */ |
| | | function init_db($DB) |
| | | { |
| | | $db_map = array('pgsql' => 'postgres', 'mysqli' => 'mysql'); |
| | | $engine = isset($db_map[$DB->db_provider]) ? $db_map[$DB->db_provider] : $DB->db_provider; |
| | | $engine = isset($this->db_map[$DB->db_provider]) ? $this->db_map[$DB->db_provider] : $DB->db_provider; |
| | | |
| | | // read schema file from /SQL/* |
| | | $fname = "../SQL/$engine.initial.sql"; |
| | | if ($lines = @file($fname, FILE_SKIP_EMPTY_LINES)) { |
| | | $buff = ''; |
| | | foreach ($lines as $i => $line) { |
| | | if (preg_match('/^--/', $line)) |
| | | continue; |
| | | |
| | | $buff .= $line . "\n"; |
| | | if (preg_match('/;$/', trim($line))) { |
| | | $DB->query($buff); |
| | | $buff = ''; |
| | | if ($this->get_error()) |
| | | break; |
| | | } |
| | | } |
| | | $fname = INSTALL_PATH . "SQL/$engine.initial.sql"; |
| | | if ($sql = @file_get_contents($fname)) { |
| | | $this->exec_sql($sql, $DB); |
| | | } |
| | | else { |
| | | $this->fail('DB Schema', "Cannot read the schema file: $fname"); |
| | |
| | | return true; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Update database with SQL statements from SQL/*.update.sql |
| | | * |
| | | * @param object rcube_db Database connection |
| | | * @param string Version to update from |
| | | * @return boolen True on success, False on error |
| | | */ |
| | | function update_db($DB, $version) |
| | | { |
| | | $version = strtolower($version); |
| | | $engine = isset($this->db_map[$DB->db_provider]) ? $this->db_map[$DB->db_provider] : $DB->db_provider; |
| | | |
| | | // read schema file from /SQL/* |
| | | $fname = INSTALL_PATH . "SQL/$engine.update.sql"; |
| | | if ($lines = @file($fname, FILE_SKIP_EMPTY_LINES)) { |
| | | $from = false; $sql = ''; |
| | | foreach ($lines as $line) { |
| | | $is_comment = preg_match('/^--/', $line); |
| | | if (!$from && $is_comment && preg_match('/from version\s([0-9.]+[a-z-]*)/', $line, $m)) { |
| | | $v = strtolower($m[1]); |
| | | if ($v == $version || version_compare($version, $v, '<=')) |
| | | $from = true; |
| | | } |
| | | if ($from && !$is_comment) |
| | | $sql .= $line. "\n"; |
| | | } |
| | | |
| | | if ($sql) |
| | | $this->exec_sql($sql, $DB); |
| | | } |
| | | else { |
| | | $this->fail('DB Schema', "Cannot read the update file: $fname"); |
| | | return false; |
| | | } |
| | | |
| | | if ($err = $this->get_error()) { |
| | | $this->fail('DB Schema', "Error updating database: $err"); |
| | | return false; |
| | | } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Execute the given SQL queries on the database connection |
| | | * |
| | | * @param string SQL queries to execute |
| | | * @param object rcube_db Database connection |
| | | * @return boolen True on success, False on error |
| | | */ |
| | | function exec_sql($sql, $DB) |
| | | { |
| | | $buff = ''; |
| | | foreach (explode("\n", $sql) as $line) { |
| | | if (preg_match('/^--/', $line) || trim($line) == '') |
| | | continue; |
| | | |
| | | $buff .= $line . "\n"; |
| | | if (preg_match('/(;|^GO)$/', trim($line))) { |
| | | $DB->query($buff); |
| | | $buff = ''; |
| | | if ($DB->is_error()) |
| | | break; |
| | | } |
| | | } |
| | | |
| | | return !$DB->is_error(); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Handler for Roundcube errors |
| | | */ |