From 2b05c5e9ecef2c377065261c4327c3cbaed9fdd5 Mon Sep 17 00:00:00 2001
From: Thomas Bruederli <thomas@roundcube.net>
Date: Mon, 31 Mar 2014 13:02:24 -0400
Subject: [PATCH] Render 'now' dates in short form, too

---
 installer/rcube_install.php |  405 +++++++++++++++++++++++++++++++++++++--------------------
 1 files changed, 261 insertions(+), 144 deletions(-)

diff --git a/installer/rcube_install.php b/installer/rcube_install.php
index 7671835..2fae3c5 100644
--- a/installer/rcube_install.php
+++ b/installer/rcube_install.php
@@ -28,12 +28,13 @@
   var $failures = 0;
   var $config = array();
   var $configured = false;
+  var $legacy_config = 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', 'double_auth');
+  var $local_config = array('db_dsnw', 'default_host', 'support_url', 'des_key', 'plugins');
+  var $obsolete_config = array('db_backend', 'db_max_length', 'double_auth');
   var $replaced_config = array(
     'skin_path'            => 'skin',
     'locale_string'        => 'language',
@@ -43,12 +44,8 @@
     'pagesize'             => 'mail_pagesize',
     'default_imap_folders' => 'default_folders',
     'top_posting'          => 'reply_mode',
-  );
-
-  // these config options are required for a working system
-  var $required_config = array(
-    'db_dsnw', 'db_table_contactgroups', 'db_table_contactgroupmembers',
-    'des_key', 'session_lifetime', 'support_url',
+    'keep_alive'           => 'refresh_interval',
+    'min_keep_alive'       => 'min_refresh_interval',
   );
 
   // list of supported database drivers
@@ -65,63 +62,95 @@
   /**
    * Constructor
    */
-  function rcube_install()
+  function __construct()
   {
     $this->step = intval($_REQUEST['_step']);
     $this->is_post = $_SERVER['REQUEST_METHOD'] == 'POST';
   }
-  
+
   /**
    * Singleton getter
    */
-  function get_instance()
+  static function get_instance()
   {
     static $inst;
-    
+
     if (!$inst)
       $inst = new rcube_install();
-    
+
     return $inst;
   }
-  
-  /**
-   * Read the default config files and store properties
-   */
-  function load_defaults()
-  {
-    $this->_load_config('.php.dist');
-  }
-
 
   /**
    * Read the local config files and store properties
    */
   function load_config()
   {
-    $this->config = array();
-    $this->_load_config('.php');
-    $this->configured = !empty($this->config);
+    // defaults
+    if ($config = $this->load_config_file(RCUBE_CONFIG_DIR . 'defaults.inc.php')) {
+        $this->config = (array) $config;
+        $this->defaults = $this->config;
+    }
+
+    $config = null;
+
+    // config
+    if ($config = $this->load_config_file(RCUBE_CONFIG_DIR . 'config.inc.php')) {
+        $this->config = array_merge($this->config, $config);
+    }
+    else {
+      if ($config = $this->load_config_file(RCUBE_CONFIG_DIR . 'main.inc.php')) {
+        $this->config = array_merge($this->config, $config);
+        $this->legacy_config = true;
+      }
+      if ($config = $this->load_config_file(RCUBE_CONFIG_DIR . 'db.inc.php')) {
+        $this->config = array_merge($this->config, $config);
+        $this->legacy_config = true;
+      }
+    }
+
+    $this->configured = !empty($config);
   }
 
   /**
    * Read the default config file and store properties
-   * @access private
    */
-  function _load_config($suffix)
+  public function load_config_file($file)
   {
-    if (is_readable($main_inc = RCMAIL_CONFIG_DIR . '/main.inc' . $suffix)) {
-      include($main_inc);
-      if (is_array($rcmail_config))
-        $this->config += $rcmail_config;
-    }
-    if (is_readable($db_inc = RCMAIL_CONFIG_DIR . '/db.inc'. $suffix)) {
-      include($db_inc);
-      if (is_array($rcmail_config))
-        $this->config += $rcmail_config;
+    if (is_readable($file)) {
+      include $file;
+
+      // read comments from config file
+      if (function_exists('token_get_all')) {
+        $tokens = token_get_all(file_get_contents($file));
+        $in_config = false;
+        $buffer = '';
+        for ($i=0; $i < count($tokens); $i++) {
+          $token = $tokens[$i];
+          if ($token[0] == T_VARIABLE && $token[1] == '$config' || $token[1] == '$rcmail_config') {
+            $in_config = true;
+            if ($buffer && $tokens[$i+1] == '[' && $tokens[$i+2][0] == T_CONSTANT_ENCAPSED_STRING) {
+              $propname = trim($tokens[$i+2][1], "'\"");
+              $this->comments[$propname] = $buffer;
+              $buffer = '';
+              $i += 3;
+            }
+          }
+          else if ($in_config && $token[0] == T_COMMENT) {
+            $buffer .= strtr($token[1], array('\n' => "\n"));
+          }
+        }
+      }
+
+      // deprecated name of config variable
+      if (is_array($rcmail_config)) {
+        return $rcmail_config;
+      }
+
+      return $config;
     }
   }
-  
-  
+
   /**
    * Getter for a certain config property
    *
@@ -132,32 +161,39 @@
   function getprop($name, $default = '')
   {
     $value = $this->config[$name];
-    
+
     if ($name == 'des_key' && !$this->configured && !isset($_REQUEST["_$name"]))
       $value = rcube_install::random_key(24);
-    
+
     return $value !== null && $value !== '' ? $value : $default;
   }
 
 
   /**
-   * Take the default config file and replace the parameters
-   * with the submitted form data
+   * Create configuration file that contains parameters
+   * that differ from default values.
    *
-   * @param string Which config file (either 'main' or 'db')
    * @return string The complete config file content
    */
-  function create_config($which, $force = false)
+  function create_config()
   {
-    $out = @file_get_contents(RCMAIL_CONFIG_DIR . "/{$which}.inc.php.dist");
-
-    if (!$out)
-      return '[Warning: could not read the config template file]';
+    $config = array();
 
     foreach ($this->config as $prop => $default) {
-
       $is_default = !isset($_POST["_$prop"]);
       $value      = !$is_default || $this->bool_config_props[$prop] ? $_POST["_$prop"] : $default;
+
+      // always disable installer
+      if ($prop == 'enable_installer')
+        $value = false;
+
+      // reset useragent to default (keeps version up-to-date)
+      if ($prop == 'useragent' && stripos($value, 'Roundcube Webmail/') !== false)
+        $value = $this->defaults[$prop];
+
+      // generate new encryption key, never use the default value
+      if ($prop == 'des_key' && $value == $this->defaults[$prop])
+        $value = $this->random_key(24);
 
       // convert some form data
       if ($prop == 'debug_level' && !$is_default) {
@@ -168,7 +204,7 @@
           $value = $val;
         }
       }
-      else if ($which == 'db' && $prop == 'db_dsnw' && !empty($_POST['_dbtype'])) {
+      else if ($prop == 'db_dsnw' && !empty($_POST['_dbtype'])) {
         if ($_POST['_dbtype'] == 'sqlite')
           $value = sprintf('%s://%s?mode=0646', $_POST['_dbtype'], $_POST['_dbname']{0} == '/' ? '/' . $_POST['_dbname'] : $_POST['_dbname']);
         else if ($_POST['_dbtype'])
@@ -193,16 +229,16 @@
         $value = '%p';
       }
       else if ($prop == 'default_folders') {
-	    $value = array();
-	    foreach ($this->config['default_folders'] as $_folder) {
-	      switch ($_folder) {
-	      case 'Drafts': $_folder = $this->config['drafts_mbox']; break;
-	      case 'Sent':   $_folder = $this->config['sent_mbox']; break;
-	      case 'Junk':   $_folder = $this->config['junk_mbox']; break;
-	      case 'Trash':  $_folder = $this->config['trash_mbox']; break;
+        $value = array();
+        foreach ($this->config['default_folders'] as $_folder) {
+          switch ($_folder) {
+          case 'Drafts': $_folder = $this->config['drafts_mbox']; break;
+          case 'Sent':   $_folder = $this->config['sent_mbox']; break;
+          case 'Junk':   $_folder = $this->config['junk_mbox']; break;
+          case 'Trash':  $_folder = $this->config['trash_mbox']; break;
           }
-	    if (!in_array($_folder, $value))
-	      $value[] = $_folder;
+        if (!in_array($_folder, $value))
+          $value[] = $_folder;
         }
       }
       else if (is_bool($default)) {
@@ -213,22 +249,42 @@
       }
 
       // skip this property
-      if (!$force && !$this->configured && ($value == $default))
+      if (($value == $this->defaults[$prop]) && !in_array($prop, $this->local_config)
+          || in_array($prop, array_merge($this->obsolete_config, array_keys($this->replaced_config)))
+          || preg_match('/^db_(table|sequence)_/', $prop)) {
         continue;
+      }
 
       // save change
       $this->config[$prop] = $value;
-
-      // replace the matching line in config file
-      $out = preg_replace(
-        '/(\$rcmail_config\[\''.preg_quote($prop).'\'\])\s+=\s+(.+);/Uie',
-        "'\\1 = ' . rcube_install::_dump_var(\$value, \$prop) . ';'",
-        $out);
+      $config[$prop] = $value;
     }
 
-    return trim($out);
+    $out = "<?php\n\n";
+    $out .= "/* Local configuration for Roundcube Webmail */\n\n";
+    foreach ($config as $prop => $value) {
+      // copy option descriptions from existing config or defaults.inc.php
+      $out .= $this->comments[$prop];
+      $out .= "\$config['$prop'] = " . rcube_install::_dump_var($value, $prop) . ";\n\n";
+    }
+
+    return $out;
   }
 
+
+  /**
+   * save generated config file in RCUBE_CONFIG_DIR
+   *
+   * @return boolean True if the file was saved successfully, false if not
+   */
+  function save_configfile($config)
+  {
+    if (is_writable(RCUBE_CONFIG_DIR)) {
+      return file_put_contents(RCUBE_CONFIG_DIR . 'config.inc.php', $config);
+    }
+
+    return false;
+  }
 
   /**
    * Check the current configuration for missing properties
@@ -238,17 +294,14 @@
    */
   function check_config()
   {
-    $this->config = array();
-    $this->load_defaults();
-    $defaults = $this->config;
-    
     $this->load_config();
-    if (!$this->configured)
+
+    if (!$this->configured) {
       return null;
-    
+    }
+
     $out = $seen = array();
-    $required = array_flip($this->required_config);
-    
+
     // iterate over the current configuration
     foreach ($this->config as $prop => $value) {
       if ($replacement = $this->replaced_config[$prop]) {
@@ -260,11 +313,10 @@
         $seen[$prop] = true;
       }
     }
-    
-    // iterate over default config
-    foreach ($defaults as $prop => $value) {
-      if (!isset($seen[$prop]) && isset($required[$prop]) && !(is_bool($this->config[$prop]) || strlen($this->config[$prop])))
-        $out['missing'][] = array('prop' => $prop);
+
+    // the old default mime_magic reference is obsolete
+    if ($this->config['mime_magic'] == '/usr/share/misc/magic') {
+        $out['obsolete'][] = array('prop' => 'mime_magic', 'explain' => "Set value to null in order to use system default");
     }
 
     // check config dependencies and contradictions
@@ -280,18 +332,18 @@
               'explain' => "You are missing pspell support for language $lang ($descr)");
       }
     }
-    
+
     if ($this->config['log_driver'] == 'syslog') {
       if (!function_exists('openlog')) {
         $out['dependencies'][] = array('prop' => 'log_driver',
-          'explain' => 'This requires the <tt>sylog</tt> extension which could not be loaded.');
+          'explain' => 'This requires the <tt>syslog</tt> extension which could not be loaded.');
       }
       if (empty($this->config['syslog_id'])) {
         $out['dependencies'][] = array('prop' => 'syslog_id',
           'explain' => 'Using <tt>syslog</tt> for logging requires a syslog ID to be configured');
       }
     }
-    
+
     // check ldap_public sources having global_search enabled
     if (is_array($this->config['ldap_public']) && !is_array($this->config['autocomplete_addressbooks'])) {
       foreach ($this->config['ldap_public'] as $ldap_public) {
@@ -301,11 +353,11 @@
         }
       }
     }
-    
+
     return $out;
   }
-  
-  
+
+
   /**
    * Merge the current configuration with the defaults
    * and copy replaced values to the new options.
@@ -314,7 +366,6 @@
   {
     $current = $this->config;
     $this->config = array();
-    $this->load_defaults();
 
     foreach ($this->replaced_config as $prop => $replacement) {
       if (isset($current[$prop])) {
@@ -327,11 +378,11 @@
       }
       unset($current[$prop]);
     }
-    
+
     foreach ($this->obsolete_config as $prop) {
       unset($current[$prop]);
     }
-    
+
     // add all ldap_public sources having global_search enabled to autocomplete_addressbooks
     if (is_array($current['ldap_public'])) {
       foreach ($current['ldap_public'] as $key => $ldap_public) {
@@ -341,38 +392,36 @@
         }
       }
     }
-    
-    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);
+    $this->config = array_merge($this->config, $current);
 
-    foreach ((array)$current['ldap_public'] as $key => $values) {
+    foreach (array_keys((array)$current['ldap_public']) as $key) {
       $this->config['ldap_public'][$key] = $current['ldap_public'][$key];
     }
   }
-  
+
   /**
    * Compare the local database schema with the reference schema
    * required for this version of Roundcube
    *
-   * @param boolean True if the schema schould be updated
-   * @return boolean True if the schema is up-to-date, false if not or an error occured
+   * @param rcube_db Database object
+   *
+   * @return boolean True if the schema is up-to-date, false if not or an error occurred
    */
-  function db_schema_check($DB, $update = false)
+  function db_schema_check($DB)
   {
     if (!$this->configured)
       return false;
-    
+
     // 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
     $existing_tables = $DB->list_tables();
 
     foreach ($db_schema as $table => $cols) {
-      $table = !empty($this->config['db_table_'.$table]) ? $this->config['db_table_'.$table] : $table;
+      $table = $this->config['db_prefix'] . $table;
       if (!in_array($table, $existing_tables)) {
         $errors[] = "Missing table '".$table."'";
       }
@@ -410,6 +459,52 @@
     return $schema;
   }
 
+  /**
+   * Try to detect some file's mimetypes to test the correct behavior of fileinfo
+   */
+  function check_mime_detection()
+  {
+    $files = array(
+      'installer/images/roundcube_logo.png' => 'image/png',
+      'program/resources/blank.tif' => 'image/tiff',
+      'skins/larry/images/buttons.gif' => 'image/gif',
+      'skins/larry/README' => 'text/plain',
+    );
+
+    $errors = array();
+    foreach ($files as $path => $expected) {
+      $mimetype = rcube_mime::file_content_type(INSTALL_PATH . $path, basename($path));
+      if ($mimetype != $expected) {
+        $errors[] = array($path, $mimetype, $expected);
+      }
+    }
+
+    return $errors;
+  }
+
+  /**
+   * Check the correct configuration of the 'mime_types' mapping option
+   */
+  function check_mime_extensions()
+  {
+    $types = array(
+      'application/zip'   => 'zip',
+      'application/x-tar' => 'tar',
+      'application/java-archive' => 'jar',
+      'image/gif'     => 'gif',
+      'image/svg+xml' => 'svg',
+    );
+
+    $errors = array();
+    foreach ($types as $mimetype => $expected) {
+      $ext = rcube_mime::get_mime_extensions($mimetype);
+      if ($ext[0] != $expected) {
+        $errors[] = array($mimetype, $ext, $expected);
+      }
+    }
+
+    return $errors;
+  }
 
   /**
    * Getter for the last error message
@@ -451,10 +546,12 @@
         '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',
+        '0.5-beta', '0.5', '0.5.1', '0.5.2', '0.5.3', '0.5.4',
         '0.6-beta', '0.6',
-        '0.7-beta', '0.7', '0.7.1', '0.7.2', '0.7.3',
-        '0.8-beta', '0.8-rc', '0.8.0', '0.8.1',
+        '0.7-beta', '0.7', '0.7.1', '0.7.2', '0.7.3', '0.7.4',
+        '0.8-beta', '0.8-rc', '0.8.0', '0.8.1', '0.8.2', '0.8.3', '0.8.4', '0.8.5', '0.8.6',
+        '0.9-beta', '0.9-rc', '0.9-rc2',
+        // Note: Do not add newer versions here
     ));
     return $select;
   }
@@ -493,10 +590,13 @@
    * @param string Test name
    * @param string Error message
    * @param string URL for details
+   * @param bool   Do not count this failure
    */
-  function fail($name, $message = '', $url = '')
+  function fail($name, $message = '', $url = '', $optional=false)
   {
-    $this->failures++;
+    if (!$optional) {
+      $this->failures++;
+    }
 
     echo Q($name) . ':&nbsp; <span class="fail">NOT OK</span>';
     $this->_showhint($message, $url);
@@ -560,7 +660,8 @@
   }
 
 
-  static function _dump_var($var, $name=null) {
+  static function _dump_var($var, $name=null)
+  {
     // special values
     switch ($name) {
     case 'syslog_facility':
@@ -573,8 +674,20 @@
       if ($val = $list[$var])
         return $val;
       break;
-    }
 
+    case 'mail_header_delimiter':
+      $var = str_replace(array("\r", "\n"), array('\r', '\n'), $var);
+      return '"' . $var. '"';
+      break;
+/*
+    // RCMAIL_VERSION is undefined here
+    case 'useragent':
+      if (preg_match('|^(.*)/('.preg_quote(RCMAIL_VERSION, '|').')$|i', $var, $m)) {
+        return '"' . addcslashes($var, '"') . '/" . RCMAIL_VERSION';
+      }
+      break;
+*/
+    }
 
     if (is_array($var)) {
       if (empty($var)) {
@@ -582,7 +695,7 @@
       }
       else {  // check if all keys are numeric
         $isnum = true;
-        foreach ($var as $key => $value) {
+        foreach (array_keys($var) as $key) {
           if (!is_numeric($key)) {
             $isnum = false;
             break;
@@ -606,7 +719,7 @@
    */
   function init_db($DB)
   {
-    $engine = isset($this->db_map[$DB->db_provider]) ? $this->db_map[$DB->db_provider] : $DB->db_provider;
+    $engine = $DB->db_provider;
 
     // read schema file from /SQL/*
     $fname = INSTALL_PATH . "SQL/$engine.initial.sql";
@@ -628,46 +741,20 @@
 
 
   /**
-   * Update database with SQL statements from SQL/*.update.sql
+   * Update database schema
    *
-   * @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)
+  function update_db($version)
   {
-    $version = strtolower($version);
-    $engine = isset($this->db_map[$DB->db_provider]) ? $this->db_map[$DB->db_provider] : $DB->db_provider;
+    system(INSTALL_PATH . "bin/updatedb.sh --package=roundcube"
+      . " --version=" . escapeshellarg($version)
+      . " --dir=" . INSTALL_PATH . "SQL"
+      . " 2>&1", $result);
 
-    // 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;
+    return !$result;
   }
 
 
@@ -680,6 +767,7 @@
    */
   function exec_sql($sql, $DB)
   {
+    $sql = $this->fix_table_names($sql, $DB);
     $buff = '';
     foreach (explode("\n", $sql) as $line) {
       if (preg_match('/^--/', $line) || trim($line) == '')
@@ -699,6 +787,35 @@
 
 
   /**
+   * Parse SQL file and fix table names according to db_prefix
+   * Note: This need to be a complete database initial file
+   */
+  private function fix_table_names($sql, $DB)
+  {
+    if (empty($this->config['db_prefix'])) {
+        return $sql;
+    }
+
+    // replace table names
+    if (preg_match_all('/CREATE TABLE (\[dbo\]\.|IF NOT EXISTS )?[`"\[\]]*([^`"\[\] \r\n]+)/i', $sql, $matches)) {
+      foreach ($matches[2] as $table) {
+        $real_table = $this->config['db_prefix'] . $table;
+        $sql = preg_replace("/([^a-zA-Z0-9_])$table([^a-zA-Z0-9_])/", "\\1$real_table\\2", $sql);
+      }
+    }
+    // replace sequence names
+    if ($DB->db_provider == 'postgres' && preg_match_all('/CREATE SEQUENCE (IF NOT EXISTS )?"?([^" \n\r]+)/i', $sql, $matches)) {
+      foreach ($matches[2] as $sequence) {
+        $real_sequence = $this->config['db_prefix'] . $sequence;
+        $sql = preg_replace("/([^a-zA-Z0-9_])$sequence([^a-zA-Z0-9_])/", "\\1$real_sequence\\2", $sql);
+      }
+    }
+
+    return $sql;
+  }
+
+
+  /**
    * Handler for Roundcube errors
    */
   function raise_error($p)

--
Gitblit v1.9.1