Ignore:
Timestamp:
Feb 25, 2011 11:37:22 AM (2 years ago)
Author:
thomasb
Message:

Create interactive update script with improved DB schema check; udated installer with new features and styles

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/roundcubemail/installer/rcube_install.php

    r4410 r4573  
    3030  var $configured = false; 
    3131  var $last_error = null; 
     32  var $db_map = array('pgsql' => 'postgres', 'mysqli' => 'mysql', 'sqlsrv' => 'mssql'); 
    3233  var $email_pattern = '([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9])'; 
    3334  var $bool_config_props = array(); 
    3435 
    35   var $obsolete_config = array('db_backend'); 
     36  var $obsolete_config = array('db_backend', 'double_auth'); 
    3637  var $replaced_config = array( 
    3738    'skin_path' => 'skin', 
     
    4344   
    4445  // these config options are required for a working system 
    45   var $required_config = array('db_dsnw', 'db_table_contactgroups', 'db_table_contactgroupmembers', 'des_key'); 
     46  var $required_config = array( 
     47    'db_dsnw', 'db_table_contactgroups', 'db_table_contactgroupmembers', 
     48    'des_key', 'session_lifetime', 
     49  ); 
    4650   
    4751  /** 
     
    295299    $this->load_defaults(); 
    296300     
    297     foreach ($this->replaced_config as $prop => $replacement) 
     301    foreach ($this->replaced_config as $prop => $replacement) { 
    298302      if (isset($current[$prop])) { 
    299303        if ($prop == 'skin_path') 
     
    303307        else 
    304308          $this->config[$replacement] = $current[$prop]; 
    305          
    306         unset($current[$prop]); 
     309      } 
     310      unset($current[$prop]); 
    307311    } 
    308312     
     
    321325    } 
    322326     
     327    if ($current['keep_alive'] && $current['session_lifetime'] < $current['keep_alive']) 
     328      $current['session_lifetime'] = max(10, ceil($current['keep_alive'] / 60) * 2); 
     329     
    323330    $this->config  = array_merge($this->config, $current); 
    324331     
     
    340347      return false; 
    341348     
    342     // simple ad hand-made db schema 
    343     $db_schema = array( 
    344       'users' => array(), 
    345       'identities' => array(), 
    346       'contacts' => array(), 
    347       'contactgroups' => array(), 
    348       'contactgroupmembers' => array(), 
    349       'cache' => array(), 
    350       'messages' => array(), 
    351       'session' => array(), 
    352     ); 
    353      
     349    // read reference schema from mysql.initial.sql 
     350    $db_schema = $this->db_read_schema(INSTALL_PATH . 'SQL/mysql.initial.sql'); 
    354351    $errors = array(); 
    355352     
     
    359356    foreach ($db_schema as $table => $cols) { 
    360357      $table = !empty($this->config['db_table_'.$table]) ? $this->config['db_table_'.$table] : $table; 
    361       if (!in_array($table, $existing_tables)) 
    362         $errors[] = "Missing table ".$table; 
    363       // TODO: check cols and indices 
     358      if (!in_array($table, $existing_tables)) { 
     359        $errors[] = "Missing table '".$table."'"; 
     360      } 
     361      else {  // compare cols 
     362        $db_cols = $DB->list_cols($table); 
     363        $diff = array_diff(array_keys($cols), $db_cols); 
     364        if (!empty($diff)) 
     365          $errors[] = "Missing columns in table '$table': " . join(',', $diff); 
     366      } 
    364367    } 
    365368     
    366369    return !empty($errors) ? $errors : false; 
    367370  } 
     371 
     372  /** 
     373   * Utility function to read database schema from an .sql file 
     374   */ 
     375  private function db_read_schema($schemafile) 
     376  { 
     377    $lines = file($schemafile); 
     378    $table_block = false; 
     379    $schema = array(); 
     380    foreach ($lines as $line) { 
     381      if (preg_match('/^\s*create table `?([a-z0-9_]+)`?/i', $line, $m)) { 
     382        $table_block = $m[1]; 
     383      } 
     384      else if ($table_block && preg_match('/^\s*`?([a-z0-9_-]+)`?\s+([a-z]+)/', $line, $m)) { 
     385        $col = $m[1]; 
     386        if (!in_array(strtoupper($col), array('PRIMARY','KEY','INDEX','UNIQUE','CONSTRAINT','REFERENCES','FOREIGN'))) { 
     387          $schema[$table_block][$col] = $m[2]; 
     388        } 
     389      } 
     390    } 
     391     
     392    return $schema; 
     393  } 
     394   
    368395   
    369396  /** 
     
    475502  } 
    476503   
     504  /** 
     505   * Create a HTML dropdown to select a previous version of Roundcube 
     506   */ 
     507  function versions_select($attrib = array()) 
     508  { 
     509    $select = new html_select($attrib); 
     510    $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')); 
     511    return $select; 
     512  } 
     513   
    477514   
    478515  /** 
     
    593630  function init_db($DB) 
    594631  { 
    595     $db_map = array('pgsql' => 'postgres', 'mysqli' => 'mysql'); 
    596     $engine = isset($db_map[$DB->db_provider]) ? $db_map[$DB->db_provider] : $DB->db_provider; 
     632    $engine = isset($this->db_map[$DB->db_provider]) ? $this->db_map[$DB->db_provider] : $DB->db_provider; 
    597633     
    598634    // read schema file from /SQL/* 
    599     $fname = "../SQL/$engine.initial.sql"; 
    600     if ($lines = @file($fname, FILE_SKIP_EMPTY_LINES)) { 
    601       $buff = ''; 
    602       foreach ($lines as $i => $line) { 
    603         if (preg_match('/^--/', $line)) 
    604           continue; 
    605            
    606         $buff .= $line . "\n"; 
    607         if (preg_match('/;$/', trim($line))) { 
    608           $DB->query($buff); 
    609           $buff = ''; 
    610           if ($this->get_error()) 
    611             break; 
    612         } 
    613       } 
     635    $fname = INSTALL_PATH . "SQL/$engine.initial.sql"; 
     636    if ($sql = @file_get_contents($fname)) { 
     637      $this->exec_sql($sql, $DB); 
    614638    } 
    615639    else { 
     
    626650  } 
    627651   
     652   
     653  /** 
     654   * Update database with SQL statements from SQL/*.update.sql 
     655   * 
     656   * @param object rcube_db Database connection 
     657   * @param string Version to update from 
     658   * @return boolen True on success, False on error 
     659   */ 
     660  function update_db($DB, $version) 
     661  { 
     662    $version = strtolower($version); 
     663    $engine = isset($this->db_map[$DB->db_provider]) ? $this->db_map[$DB->db_provider] : $DB->db_provider; 
     664     
     665    // read schema file from /SQL/* 
     666    $fname = INSTALL_PATH . "SQL/$engine.update.sql"; 
     667    if ($lines = @file($fname, FILE_SKIP_EMPTY_LINES)) { 
     668      $from = false; $sql = ''; 
     669      foreach ($lines as $line) { 
     670        $is_comment = preg_match('/^--/', $line); 
     671        if (!$from && $is_comment && preg_match('/from version\s([0-9.]+[a-z-]*)/', $line, $m)) { 
     672          $v = strtolower($m[1]); 
     673          if ($v == $version || version_compare($version, $v, '<=')) 
     674            $from = true; 
     675        } 
     676        if ($from && !$is_comment) 
     677          $sql .= $line. "\n"; 
     678      } 
     679       
     680      if ($sql) 
     681        $this->exec_sql($sql, $DB); 
     682    } 
     683    else { 
     684      $this->fail('DB Schema', "Cannot read the update file: $fname"); 
     685      return false; 
     686    } 
     687     
     688    if ($err = $this->get_error()) { 
     689      $this->fail('DB Schema', "Error updating database: $err"); 
     690      return false; 
     691    } 
     692 
     693    return true; 
     694  } 
     695   
     696   
     697  /** 
     698   * Execute the given SQL queries on the database connection 
     699   * 
     700   * @param string SQL queries to execute 
     701   * @param object rcube_db Database connection 
     702   * @return boolen True on success, False on error 
     703   */ 
     704  function exec_sql($sql, $DB) 
     705  { 
     706    $buff = ''; 
     707    foreach (explode("\n", $sql) as $line) { 
     708      if (preg_match('/^--/', $line) || trim($line) == '') 
     709        continue; 
     710         
     711      $buff .= $line . "\n"; 
     712      if (preg_match('/(;|^GO)$/', trim($line))) { 
     713        $DB->query($buff); 
     714        $buff = ''; 
     715        if ($DB->is_error()) 
     716          break; 
     717      } 
     718    } 
     719     
     720    return !$DB->is_error(); 
     721  } 
     722   
     723   
    628724  /** 
    629725   * Handler for Roundcube errors 
Note: See TracChangeset for help on using the changeset viewer.