Aleksander Machniak
2015-04-12 e7620812b01f3eda31ccf21f9dc5b1f17ba6ce57
Installer: Remove system() function use (#1490139)

Move some functionality of scripts from bin/ into rcmail_utils class
1 files added
8 files modified
663 ■■■■■ changed files
CHANGELOG 1 ●●●● patch | view | raw | blame | history
bin/cleandb.sh 49 ●●●●● patch | view | raw | blame | history
bin/indexcontacts.sh 27 ●●●●● patch | view | raw | blame | history
bin/initdb.sh 35 ●●●●● patch | view | raw | blame | history
bin/moduserprefs.sh 35 ●●●●● patch | view | raw | blame | history
bin/update.sh 8 ●●●●● patch | view | raw | blame | history
bin/updatedb.sh 138 ●●●●● patch | view | raw | blame | history
program/include/rcmail_install.php 8 ●●●● patch | view | raw | blame | history
program/include/rcmail_utils.php 362 ●●●●● patch | view | raw | blame | history
CHANGELOG
@@ -1,6 +1,7 @@
CHANGELOG Roundcube Webmail
===========================
- Installer: Remove system() function use (#1490139)
- Password plugin: Added 'kpasswd' driver by Peter Allgeyer
- Add initdb.sh to create database from initial.sql script with prefix support (#1490188)
- Plugin API: Added message_part_body hook
bin/cleandb.sh
@@ -5,7 +5,7 @@
 | bin/cleandb.sh                                                        |
 |                                                                       |
 | This file is part of the Roundcube Webmail client                     |
 | Copyright (C) 2010, The Roundcube Dev Team                            |
 | Copyright (C) 2010-2015, The Roundcube Dev Team                       |
 |                                                                       |
 | Licensed under the GNU General Public License version 3 or            |
 | any later version with exceptions for skins & plugins.                |
@@ -23,56 +23,11 @@
require INSTALL_PATH.'program/include/clisetup.php';
// mapping for table name => primary key
$primary_keys = array(
    'contacts' => "contact_id",
    'contactgroups' => "contactgroup_id",
);
// connect to DB
$RCMAIL = rcube::get_instance();
$db = $RCMAIL->get_dbh();
$db->db_connect('w');
if (!$db->is_connected() || $db->is_error()) {
    rcube::raise_error("No DB connection", false, true);
}
if (!empty($_SERVER['argv'][1]))
    $days = intval($_SERVER['argv'][1]);
else
    $days = 7;
// remove all deleted records older than two days
$threshold = date('Y-m-d 00:00:00', time() - $days * 86400);
foreach (array('contacts','contactgroups','identities') as $table) {
    $sqltable = $db->table_name($table, true);
    // also delete linked records
    // could be skipped for databases which respect foreign key constraints
    if ($db->db_provider == 'sqlite'
        && ($table == 'contacts' || $table == 'contactgroups')
    ) {
        $pk = $primary_keys[$table];
        $memberstable = $db->table_name('contactgroupmembers');
        $db->query(
            "DELETE FROM " . $db->quote_identifier($memberstable).
            " WHERE `$pk` IN (".
                "SELECT `$pk` FROM $sqltable".
                " WHERE `del` = 1 AND `changed` < ?".
            ")",
            $threshold);
        echo $db->affected_rows() . " records deleted from '$memberstable'\n";
    }
    // delete outdated records
    $db->query("DELETE FROM $sqltable WHERE `del` = 1 AND `changed` < ?", $threshold);
    echo $db->affected_rows() . " records deleted from '$table'\n";
}
rcmail_utils::db_clean($days);
?>
bin/indexcontacts.sh
@@ -24,31 +24,6 @@
require_once INSTALL_PATH.'program/include/clisetup.php';
ini_set('memory_limit', -1);
// connect to DB
$RCMAIL = rcube::get_instance();
$db = $RCMAIL->get_dbh();
$db->db_connect('w');
if (!$db->is_connected() || $db->is_error()) {
    rcube::raise_error("No DB connection", false, true);
}
// iterate over all users
$sql_result = $db->query("SELECT `user_id` FROM " . $db->table_name('users', true) . " ORDER BY `user_id`");
while ($sql_result && ($sql_arr = $db->fetch_assoc($sql_result))) {
    echo "Indexing contacts for user " . $sql_arr['user_id'] . "...";
    $contacts = new rcube_contacts($db, $sql_arr['user_id']);
    $contacts->set_pagesize(9999);
    $result = $contacts->list_records();
    while ($result->count && ($row = $result->next())) {
        unset($row['words']);
        $contacts->update($row['ID'], $row);
    }
    echo "done.\n";
}
rcmail_utils::indexcontacts();
?>
bin/initdb.sh
@@ -37,39 +37,6 @@
    rcube::raise_error("Specified database schema directory doesn't exist.", false, true);
}
$RC = rcube::get_instance();
$DB = rcube_db::factory($RC->config->get('db_dsnw'));
$DB->set_debug((bool)$RC->config->get('sql_debug'));
// Connect to database
$DB->db_connect('w');
if (!$DB->is_connected()) {
    rcube::raise_error("Error connecting to database: " . $DB->is_error(), false, true);
}
$file = $opts['dir'] . '/' . $DB->db_provider . '.initial.sql';
if (!file_exists($file)) {
    rcube::raise_error("DDL file $file not found", false, true);
}
echo "Creating database schema... ";
if ($sql = file_get_contents($file)) {
    if (!$DB->exec_script($sql)) {
        $error = $DB->is_error();
    }
}
else {
    $error = "Unable to read file $file or it is empty";
}
if ($error) {
    echo "[FAILED]\n";
    rcube::raise_error($error, false, true);
}
else {
    echo "[OK]\n";
}
rcmail_utils::db_init($opts['dir']);
?>
bin/moduserprefs.sh
@@ -5,7 +5,7 @@
 | bin/moduserprefs.sh                                                   |
 |                                                                       |
 | This file is part of the Roundcube Webmail client                     |
 | Copyright (C) 2012, The Roundcube Dev Team                            |
 | Copyright (C) 2012-2015, The Roundcube Dev Team                       |
 |                                                                       |
 | Licensed under the GNU General Public License version 3 or            |
 | any later version with exceptions for skins & plugins.                |
@@ -46,37 +46,6 @@
$pref_name  = trim($args[0]);
$pref_value = $args['delete'] ? null : trim($args[1]);
// connect to DB
$rcmail = rcube::get_instance();
$db = $rcmail->get_dbh();
$db->db_connect('w');
if (!$db->is_connected() || $db->is_error())
    die("No DB connection\n" . $db->is_error());
$query = '1=1';
if ($args['user'])
    $query = '`user_id` = ' . intval($args['user']);
// iterate over all users
$sql_result = $db->query("SELECT * FROM " . $db->table_name('users', true) . " WHERE $query");
while ($sql_result && ($sql_arr = $db->fetch_assoc($sql_result))) {
    echo "Updating prefs for user " . $sql_arr['user_id'] . "...";
    $user = new rcube_user($sql_arr['user_id'], $sql_arr);
    $prefs = $old_prefs = $user->get_prefs();
    $prefs[$pref_name] = $pref_value;
    if ($prefs != $old_prefs) {
        $user->save_prefs($prefs);
        echo "saved.\n";
    }
    else {
        echo "nothing changed.\n";
    }
}
rcmail_utils::mod_pref($pref_name, $pref_value, $args['user']);
?>
bin/update.sh
@@ -156,10 +156,8 @@
  // check database schema
  if ($RCI->config['db_dsnw']) {
    echo "Executing database schema update.\n";
    system("php " . INSTALL_PATH . "bin/updatedb.sh --package=roundcube --version=" . $opts['version']
      . " --dir=" . INSTALL_PATH . "SQL", $res);
    $success = !$res;
    $success = rcmail_utils::db_update(INSTALL_PATH . 'SQL', 'roundcube', $opts['version'],
        array('errors' => true));
  }
  // update composer dependencies
@@ -239,7 +237,7 @@
  // index contacts for fulltext searching
  if ($opts['version'] && version_compare(version_parse($opts['version']), '0.6.0', '<')) {
    system("php " . INSTALL_PATH . 'bin/indexcontacts.sh');
    rcmail_utils::indexcontacts();
  }
  if ($success) {
bin/updatedb.sh
@@ -37,142 +37,6 @@
    rcube::raise_error("Database schema package name not specified (--package).", false, true);
}
// Check if directory exists
if (!file_exists($opts['dir'])) {
    rcube::raise_error("Specified database schema directory doesn't exist.", false, true);
}
$RC = rcube::get_instance();
$DB = rcube_db::factory($RC->config->get('db_dsnw'));
$DB->set_debug((bool)$RC->config->get('sql_debug'));
// Connect to database
$DB->db_connect('w');
if (!$DB->is_connected()) {
    rcube::raise_error("Error connecting to database: " . $DB->is_error(), false, true);
}
// Read DB schema version from database (if 'system' table exists)
if (in_array($DB->table_name('system'), (array)$DB->list_tables())) {
    $DB->query("SELECT `value`"
        ." FROM " . $DB->table_name('system', true)
        ." WHERE `name` = ?",
        $opts['package'] . '-version');
    $row     = $DB->fetch_array();
    $version = preg_replace('/[^0-9]/', '', $row[0]);
}
// DB version not found, but release version is specified
if (!$version && $opts['version']) {
    // Map old release version string to DB schema version
    // Note: This is for backward compat. only, do not need to be updated
    $map = array(
        '0.1-stable' => 1,
        '0.1.1'      => 2008030300,
        '0.2-alpha'  => 2008040500,
        '0.2-beta'   => 2008060900,
        '0.2-stable' => 2008092100,
        '0.2.1'      => 2008092100,
        '0.2.2'      => 2008092100,
        '0.3-stable' => 2008092100,
        '0.3.1'      => 2009090400,
        '0.4-beta'   => 2009103100,
        '0.4'        => 2010042300,
        '0.4.1'      => 2010042300,
        '0.4.2'      => 2010042300,
        '0.5-beta'   => 2010100600,
        '0.5'        => 2010100600,
        '0.5.1'      => 2010100600,
        '0.5.2'      => 2010100600,
        '0.5.3'      => 2010100600,
        '0.5.4'      => 2010100600,
        '0.6-beta'   => 2011011200,
        '0.6'        => 2011011200,
        '0.7-beta'   => 2011092800,
        '0.7'        => 2011111600,
        '0.7.1'      => 2011111600,
        '0.7.2'      => 2011111600,
        '0.7.3'      => 2011111600,
        '0.7.4'      => 2011111600,
        '0.8-beta'   => 2011121400,
        '0.8-rc'     => 2011121400,
        '0.8.0'      => 2011121400,
        '0.8.1'      => 2011121400,
        '0.8.2'      => 2011121400,
        '0.8.3'      => 2011121400,
        '0.8.4'      => 2011121400,
        '0.8.5'      => 2011121400,
        '0.8.6'      => 2011121400,
        '0.9-beta'   => 2012080700,
    );
    $version = $map[$opts['version']];
}
// Assume last version before the 'system' table was added
if (empty($version)) {
    $version = 2012080700;
}
$dir = $opts['dir'] . '/' . $DB->db_provider;
if (!file_exists($dir)) {
    rcube::raise_error("DDL Upgrade files for " . $DB->db_provider . " driver not found.", false, true);
}
$dh     = opendir($dir);
$result = array();
while ($file = readdir($dh)) {
    if (preg_match('/^([0-9]+)\.sql$/', $file, $m) && $m[1] > $version) {
        $result[] = $m[1];
    }
}
sort($result, SORT_NUMERIC);
foreach ($result as $v) {
    echo "Updating database schema ($v)... ";
    $error = update_db_schema($opts['package'], $v, "$dir/$v.sql");
    if ($error) {
        echo "[FAILED]\n";
        rcube::raise_error("Error in DDL upgrade $v: $error", false, true);
    }
    echo "[OK]\n";
}
function update_db_schema($package, $version, $file)
{
    global $DB;
    // read DDL file
    if ($sql = file_get_contents($file)) {
        if (!$DB->exec_script($sql)) {
            return $DB->is_error();
        }
    }
    // escape if 'system' table does not exist
    if ($version < 2013011000) {
        return;
    }
    $system_table = $DB->table_name('system', true);
    $DB->query("UPDATE " . $system_table
        ." SET `value` = ?"
        ." WHERE `name` = ?",
        $version, $package . '-version');
    if (!$DB->is_error() && !$DB->affected_rows()) {
        $DB->query("INSERT INTO " . $system_table
            ." (`name`, `value`) VALUES (?, ?)",
            $package . '-version', $version);
    }
    return $DB->is_error();
}
rcmail_utils::db_update($opts['dir'], $opts['package'], $opts['version'], array('errors' => true));
?>
program/include/rcmail_install.php
@@ -773,12 +773,8 @@
   */
  function update_db($version)
  {
    system(INSTALL_PATH . "bin/updatedb.sh --package=roundcube"
      . " --version=" . escapeshellarg($version)
      . " --dir=" . INSTALL_PATH . "SQL"
      . " 2>&1", $result);
    return !$result;
    return rcmail_utils::db_update(INSTALL_PATH . 'SQL', 'roundcube', $version,
        array('quiet' => true));
  }
program/include/rcmail_utils.php
New file
@@ -0,0 +1,362 @@
<?php
/*
 +-----------------------------------------------------------------------+
 | program/include/rcmail_utils.php                                      |
 |                                                                       |
 | This file is part of the Roundcube PHP suite                          |
 | Copyright (C) 2005-2015 The Roundcube Dev Team                        |
 |                                                                       |
 | Licensed under the GNU General Public License version 3 or            |
 | any later version with exceptions for skins & plugins.                |
 | See the README file for a full license statement.                     |
 |                                                                       |
 | CONTENTS:                                                             |
 |   Roundcube utilities                                                 |
 |                                                                       |
 +-----------------------------------------------------------------------+
 | Author: Thomas Bruederli <roundcube@gmail.com>                        |
 | Author: Aleksander Machniak <alec@alec.pl>                            |
 +-----------------------------------------------------------------------+
*/
/**
 * Roundcube utilities
 *
 * @package Webmail
 * @subpackage Utils
 */
class rcmail_utils
{
    public static $db;
    /**
     * Initialize database object and connect
     *
     * @return rcube_db Database instance
     */
    public static function db()
    {
        if (self::$db === null) {
            $rc = rcube::get_instance();
            $db = rcube_db::factory($rc->config->get('db_dsnw'));
            $db->set_debug((bool)$rc->config->get('sql_debug'));
            // Connect to database
            $db->db_connect('w');
            if (!$db->is_connected()) {
                rcube::raise_error("Error connecting to database: " . $db->is_error(), false, true);
            }
            self::$db = $db;
        }
        return self::$db;
    }
    /**
     * Initialize database schema
     *
     * @param string Directory with sql files
     */
    public static function db_init($dir)
    {
        $db = self::db();
        $file = $dir . '/' . $db->db_provider . '.initial.sql';
        if (!file_exists($file)) {
            rcube::raise_error("DDL file $file not found", false, true);
        }
        echo "Creating database schema... ";
        if ($sql = file_get_contents($file)) {
            if (!$db->exec_script($sql)) {
                $error = $db->is_error();
            }
        }
        else {
            $error = "Unable to read file $file or it is empty";
        }
        if ($error) {
            echo "[FAILED]\n";
            rcube::raise_error($error, false, true);
        }
        else {
            echo "[OK]\n";
        }
    }
    /**
     * Update database schema
     *
     * @param string Directory with sql files
     * @param string Component name
     * @param string Optional current version number
     * @param array  Parameters (errors, quiet)
     *
     * @return True on success, False on failure
     */
    public static function db_update($dir, $package, $ver = null, $opts = array())
    {
        // Check if directory exists
        if (!file_exists($dir)) {
            if ($opts['errors']) {
                rcube::raise_error("Specified database schema directory doesn't exist.", false, true);
            }
            return false;
        }
        $db = self::db();
        // Read DB schema version from database (if 'system' table exists)
        if (in_array($db->table_name('system'), (array)$db->list_tables())) {
            $db->query("SELECT `value`"
                . " FROM " . $db->table_name('system', true)
                . " WHERE `name` = ?",
                $package . '-version');
            $row     = $db->fetch_array();
            $version = preg_replace('/[^0-9]/', '', $row[0]);
        }
        // DB version not found, but release version is specified
        if (!$version && $ver) {
            // Map old release version string to DB schema version
            // Note: This is for backward compat. only, do not need to be updated
            $map = array(
                '0.1-stable' => 1,
                '0.1.1'      => 2008030300,
                '0.2-alpha'  => 2008040500,
                '0.2-beta'   => 2008060900,
                '0.2-stable' => 2008092100,
                '0.2.1'      => 2008092100,
                '0.2.2'      => 2008092100,
                '0.3-stable' => 2008092100,
                '0.3.1'      => 2009090400,
                '0.4-beta'   => 2009103100,
                '0.4'        => 2010042300,
                '0.4.1'      => 2010042300,
                '0.4.2'      => 2010042300,
                '0.5-beta'   => 2010100600,
                '0.5'        => 2010100600,
                '0.5.1'      => 2010100600,
                '0.5.2'      => 2010100600,
                '0.5.3'      => 2010100600,
                '0.5.4'      => 2010100600,
                '0.6-beta'   => 2011011200,
                '0.6'        => 2011011200,
                '0.7-beta'   => 2011092800,
                '0.7'        => 2011111600,
                '0.7.1'      => 2011111600,
                '0.7.2'      => 2011111600,
                '0.7.3'      => 2011111600,
                '0.7.4'      => 2011111600,
                '0.8-beta'   => 2011121400,
                '0.8-rc'     => 2011121400,
                '0.8.0'      => 2011121400,
                '0.8.1'      => 2011121400,
                '0.8.2'      => 2011121400,
                '0.8.3'      => 2011121400,
                '0.8.4'      => 2011121400,
                '0.8.5'      => 2011121400,
                '0.8.6'      => 2011121400,
                '0.9-beta'   => 2012080700,
            );
            $version = $map[$ver];
        }
        // Assume last version before the 'system' table was added
        if (empty($version)) {
            $version = 2012080700;
        }
        $dir .= '/' . $db->db_provider;
        if (!file_exists($dir)) {
            if ($opts['errors']) {
                rcube::raise_error("DDL Upgrade files for " . $db->db_provider . " driver not found.", false, true);
            }
            return false;
        }
        $dh     = opendir($dir);
        $result = array();
        while ($file = readdir($dh)) {
            if (preg_match('/^([0-9]+)\.sql$/', $file, $m) && $m[1] > $version) {
                $result[] = $m[1];
            }
        }
        sort($result, SORT_NUMERIC);
        foreach ($result as $v) {
            if (!$opts['quiet']) {
                echo "Updating database schema ($v)... ";
            }
            $error = self::db_update_schema($package, $v, "$dir/$v.sql");
            if ($error) {
                if (!$opts['quiet']) {
                    echo "[FAILED]\n";
                }
                if ($opts['errors']) {
                    rcube::raise_error("Error in DDL upgrade $v: $error", false, true);
                }
                return false;
            }
            else if (!$opts['quiet']) {
                echo "[OK]\n";
            }
        }
        return true;
    }
    /**
     * Run database update from a single sql file
     */
    protected static function db_update_schema($package, $version, $file)
    {
        $db = self::db();
        // read DDL file
        if ($sql = file_get_contents($file)) {
            if (!$db->exec_script($sql)) {
                return $db->is_error();
            }
        }
        // escape if 'system' table does not exist
        if ($version < 2013011000) {
            return;
        }
        $system_table = $db->table_name('system', true);
        $db->query("UPDATE " . $system_table
            . " SET `value` = ?"
            . " WHERE `name` = ?",
            $version, $package . '-version');
        if (!$db->is_error() && !$db->affected_rows()) {
            $db->query("INSERT INTO " . $system_table
                ." (`name`, `value`) VALUES (?, ?)",
                $package . '-version', $version);
        }
        return $db->is_error();
    }
    /**
     * Removes all deleted records older than X days
     *
     * @param int Number of days
     */
    public static function db_clean($days)
    {
        // mapping for table name => primary key
        $primary_keys = array(
            'contacts'      => 'contact_id',
            'contactgroups' => 'contactgroup_id',
        );
        $db = self::db();
        $threshold = date('Y-m-d 00:00:00', time() - $days * 86400);
        foreach (array('contacts','contactgroups','identities') as $table) {
            $sqltable = $db->table_name($table, true);
            // also delete linked records
            // could be skipped for databases which respect foreign key constraints
            if ($db->db_provider == 'sqlite' && ($table == 'contacts' || $table == 'contactgroups')) {
                $pk           = $primary_keys[$table];
                $memberstable = $db->table_name('contactgroupmembers');
                $db->query(
                    "DELETE FROM " . $db->quote_identifier($memberstable)
                    . " WHERE `$pk` IN ("
                        . "SELECT `$pk` FROM $sqltable"
                        . " WHERE `del` = 1 AND `changed` < ?"
                    . ")",
                    $threshold);
                echo $db->affected_rows() . " records deleted from '$memberstable'\n";
            }
            // delete outdated records
            $db->query("DELETE FROM $sqltable WHERE `del` = 1 AND `changed` < ?", $threshold);
            echo $db->affected_rows() . " records deleted from '$table'\n";
        }
    }
    /**
     * Reindex contacts
     */
    public static function indexcontacts()
    {
        $db = self::db();
        // iterate over all users
        $sql_result = $db->query("SELECT `user_id` FROM " . $db->table_name('users', true) . " ORDER BY `user_id`");
        while ($sql_result && ($sql_arr = $db->fetch_assoc($sql_result))) {
            echo "Indexing contacts for user " . $sql_arr['user_id'] . "...\n";
            $contacts = new rcube_contacts($db, $sql_arr['user_id']);
            $contacts->set_pagesize(9999);
            $result = $contacts->list_records();
            while ($result->count && ($row = $result->next())) {
                unset($row['words']);
                $contacts->update($row['ID'], $row);
            }
        }
        echo "done.\n";
    }
    /**
     * Modify user preferences
     *
     * @param string Option name
     * @param string Option value
     * @param int    Optional user identifier
     */
    public static function mod_pref($name, $value, $userid = null)
    {
        $db = self::db();
        if ($userid) {
            $query = '`user_id` = ' . intval($userid);
        }
        else {
            $query = '1=1';
        }
        // iterate over all users
        $sql_result = $db->query("SELECT * FROM " . $db->table_name('users', true) . " WHERE $query");
        while ($sql_result && ($sql_arr = $db->fetch_assoc($sql_result))) {
            echo "Updating prefs for user " . $sql_arr['user_id'] . "...";
            $user  = new rcube_user($sql_arr['user_id'], $sql_arr);
            $prefs = $old_prefs = $user->get_prefs();
            $prefs[$name] = $value;
            if ($prefs != $old_prefs) {
                $user->save_prefs($prefs);
                echo "saved.\n";
            }
            else {
                echo "nothing changed.\n";
            }
        }
    }
}