From f070da7c27e151353efd6dd7a639418991f73c7c Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Sun, 22 Feb 2015 11:47:14 +0100 Subject: Fix fatal errors on systems without mbstring extension or mb_regex_encoding() function (#1490280) --- program/include/iniset.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'program/include') diff --git a/program/include/iniset.php b/program/include/iniset.php index ca1e6ad75..d91b31436 100644 --- a/program/include/iniset.php +++ b/program/include/iniset.php @@ -68,11 +68,14 @@ spl_autoload_register('rcmail_autoload'); // backward compatybility (to be removed) require_once INSTALL_PATH . 'program/include/bc.php'; -// load the UTF-8 portablity layer from Patchwork -if (!function_exists('iconv') || !function_exists('utf8_encode') || !extension_loaded('mbstring')) { - \Patchwork\Utf8\Bootup::initAll(); +// load the UTF-8 portability layers from Patchwork +// don't load mbstring layer as it conflicts with Roundcube Framework (#1490280) +if (!function_exists('iconv')) { + \Patchwork\Utf8\Bootup::initIconv(); +} +if (!function_exists('utf8_encode')) { + \Patchwork\Utf8\Bootup::initUtf8Encode(); } - /** * PHP5 autoloader routine for dynamic class loading -- cgit v1.2.3 From 216b31dd99b54e7be3df8feebeafae72e423bb1c Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 25 Feb 2015 05:24:05 -0500 Subject: Fix so "over quota" errors are displayed also in message compose page This also fixes over quota responses on cyrus imap which uses "Over quota" string and no error identifier. --- CHANGELOG | 1 + program/include/rcmail.php | 17 +++++++++++++---- program/steps/mail/sendmail.inc | 5 +++-- 3 files changed, 17 insertions(+), 6 deletions(-) (limited to 'program/include') diff --git a/CHANGELOG b/CHANGELOG index 064023187..9c6e2b99a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -14,6 +14,7 @@ CHANGELOG Roundcube Webmail - Fix missing or not up-to-date CATEGORIES entry in vCard export (#1490277) - Fix fatal errors on systems without mbstring extension or mb_regex_encoding() function (#1490280) - Fix cursor position on reply below the quote in HTML mode (#1490263) +- Fix so "over quota" errors are displayed also in message compose page RELEASE 1.1.0 ------------- diff --git a/program/include/rcmail.php b/program/include/rcmail.php index 6e74560cb..2a154d9de 100644 --- a/program/include/rcmail.php +++ b/program/include/rcmail.php @@ -1793,8 +1793,9 @@ class rcmail extends rcube * @param string $fallback Fallback message label * @param array $fallback_args Fallback message label arguments * @param string $suffix Message label suffix + * @param array $params Additional parameters (type, prefix) */ - public function display_server_error($fallback = null, $fallback_args = null, $suffix = '') + public function display_server_error($fallback = null, $fallback_args = null, $suffix = '', $params = array()) { $err_code = $this->storage->get_error_code(); $res_code = $this->storage->get_response_code(); @@ -1815,8 +1816,8 @@ class rcmail extends rcube $error = 'errornoperm'; } // try to detect full mailbox problem and display appropriate message - // there can be e.g. "Quota exceeded" or "quotum would exceed" - else if (stripos($err_str, 'quot') !== false && stripos($err_str, 'exceed') !== false) { + // there can be e.g. "Quota exceeded" / "quotum would exceed" / "Over quota" + else if (stripos($err_str, 'quot') !== false && preg_match('/exceed|over/i', $err_str)) { $error = 'erroroverquota'; } else { @@ -1830,13 +1831,21 @@ class rcmail extends rcube else if ($fallback) { $error = $fallback; $args = $fallback_args; + $params['prefix'] = false; } if ($error) { if ($suffix && $this->text_exists($error . $suffix)) { $error .= $suffix; } - $this->output->show_message($error, 'error', $args); + + $msg = $this->gettext(array('name' => $error, 'vars' => $args)); + + if ($params['prefix'] && $fallback) { + $msg = $this->gettext(array('name' => $fallback, 'vars' => $fallback_args)) . ' ' . $msg; + } + + $this->output->show_message($msg, $params['type'] ?: 'error'); } } diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc index 5843de43f..4f672ac8b 100644 --- a/program/steps/mail/sendmail.inc +++ b/program/steps/mail/sendmail.inc @@ -635,7 +635,8 @@ if ($store_target) { 'message' => "Could not save message in $store_target"), true, false); if ($savedraft) { - $OUTPUT->show_message('errorsaving', 'error'); + $RCMAIL->display_server_error('errorsaving'); + // start the auto-save timer again $OUTPUT->command('auto_save_start'); $OUTPUT->send('iframe'); @@ -699,7 +700,7 @@ else { $OUTPUT->command('remove_compose_data', $COMPOSE_ID); if ($store_folder && !$saved) { - $OUTPUT->command('sent_successfully', 'error', $RCMAIL->gettext('errorsavingsent'), $folders); + $RCMAIL->display_server_error('errorsavingsent', null, null, array('prefix' => true)); } else if ($store_folder) { $folders[] = $store_target; -- cgit v1.2.3 From 0bd99db08d1660e02e3b7589c78785ab6be0794d Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Mon, 23 Mar 2015 18:33:40 +0100 Subject: Localize common error messages; improve explanation for CSRF check failures --- program/include/rcmail_output_html.php | 2 +- program/localization/en_US/messages.inc | 9 +++++++ program/steps/utils/error.inc | 46 ++++++++++++++++++++------------- 3 files changed, 38 insertions(+), 19 deletions(-) (limited to 'program/include') diff --git a/program/include/rcmail_output_html.php b/program/include/rcmail_output_html.php index c6c43b532..365c403e4 100644 --- a/program/include/rcmail_output_html.php +++ b/program/include/rcmail_output_html.php @@ -584,7 +584,7 @@ EOF; // read template file if (!$path || ($templ = @file_get_contents($path)) === false) { rcube::raise_error(array( - 'code' => 501, + 'code' => 404, 'type' => 'php', 'line' => __LINE__, 'file' => __FILE__, diff --git a/program/localization/en_US/messages.inc b/program/localization/en_US/messages.inc index e0de3654e..bcf89a441 100644 --- a/program/localization/en_US/messages.inc +++ b/program/localization/en_US/messages.inc @@ -180,5 +180,14 @@ $messages['messagetoobig'] = 'The message part is too big to process it.'; $messages['attachmentvalidationerror'] = 'WARNING! This attachment is suspicious because its type doesn\'t match the type declared in the message. If you do not trust the sender, you shouldn\'t open it in the browser because it may contain malicious contents.

Expected: $expected; found: $detected'; $messages['noscriptwarning'] = 'Warning: This webmail service requires Javascript! In order to use it please enable Javascript in your browser\'s settings.'; $messages['messageissent'] = 'The message was already sent, but not saved yet. Do you want to save it now?'; +$messages['errnotfound'] = 'File Not Found'; +$messages['errnotfoundexplain'] = 'The requested resource was not found!'; +$messages['errfailedrequest'] = 'Failed request'; +$messages['errauthorizationfailed'] = 'Authorization Failed'; +$messages['errunauthorizedexplain'] = 'Could not verify that you are authorized to access this service!'; +$messages['errrequestcheckfailed'] = 'Request Check Failed'; +$messages['errcsrfprotectionexplain'] = "For your protection, access to this resource is secured against CSRF.\nYou probably didn't log out before leaving the web application.\n\nHuman interaction is now required to continue."; +$messages['errcontactserveradmin'] = 'Please contact your server-administrator.'; +$messages['clicktoresumesession'] = 'Click here to resume your previous session'; ?> diff --git a/program/steps/utils/error.inc b/program/steps/utils/error.inc index 6bbc57fda..16fbb03d9 100644 --- a/program/steps/utils/error.inc +++ b/program/steps/utils/error.inc @@ -5,7 +5,7 @@ | program/steps/utils/error.inc | | | | This file is part of the Roundcube Webmail client | - | Copyright (C) 2005-2013, The Roundcube Dev Team | + | 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. | @@ -43,37 +43,33 @@ EOF; // authorization error else if ($ERROR_CODE == 401) { - $__error_title = "AUTHORIZATION FAILED"; - $__error_text = "Could not verify that you are authorized to access this service!
\n" - . "Please contact your server-administrator."; + $__error_title = strtoupper($rcmail->gettext('errauthorizationfailed')); + $__error_text = nl2br($rcmail->gettext('errunauthorizedexplain') . "\n" . + $rcmail->gettext('errcontactserveradmin')); } // forbidden due to request check else if ($ERROR_CODE == 403) { if ($_SERVER['REQUEST_METHOD'] == 'GET' && $rcmail->request_status == rcube::REQUEST_ERROR_URL) { - parse_str($_SERVER['QUERY_STRING'], $url); - $url = $rcmail->url($url, true, false, true); - $add = "
Click here to try again."; + $url = $rcmail->url($_GET, true, false, true); + $add = html::a($url, $rcmail->gettext('clicktoresumesession')); } else { - $add = "Please contact your server-administrator."; + $add = $rcmail->gettext('errcontactserveradmin'); } - $__error_title = "REQUEST CHECK FAILED"; - $__error_text = "Access to this service was denied due to failing security checks!
\n$add"; + $__error_title = strtoupper($rcmail->gettext('errrequestcheckfailed')); + $__error_text = nl2br($rcmail->gettext('errcsrfprotectionexplain')) . '

' . $add . '

'; } // failed request (wrong step in URL) else if ($ERROR_CODE == 404) { $request_url = htmlentities($_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']); - $__error_title = "REQUEST FAILED/FILE NOT FOUND"; - $__error_text = << -Please contact your server-administrator. + $__error_title = strtoupper($rcmail->gettext('errnotfound')); + $__error_text = nl2br($rcmail->gettext('errnotfoundexplain') . "\n" . + $rcmail->gettext('errcontactserveradmin')); -

Failed request:
-http://$request_url

-EOF; + $__error_text .= '

' . $rcmail->gettext('errfailedrequest') . ":
\n//$request_url

"; } // database connection error @@ -101,6 +97,20 @@ else { } } +// inform plugins +if ($rcmail && $rcmail->plugins) { + $plugin = $rcmail->plugins->exec_hook('error_page', array( + 'code' => $ERROR_CODE, + 'title' => $__error_title, + 'text' => $__error_text, + )); + + if (!empty($plugin['title'])) + $__error_title = $plugin['title']; + if (!empty($plugin['text'])) + $__error_text = $plugin['text']; +} + $HTTP_ERR_CODE = $ERROR_CODE && $ERROR_CODE < 600 ? $ERROR_CODE : 500; // Ajax request @@ -113,7 +123,7 @@ if ($rcmail->output && $rcmail->output->type == 'js') { $__page_content = <<

$__error_title

-

$__error_text

+
$__error_text
EOF; -- cgit v1.2.3 From e7620812b01f3eda31ccf21f9dc5b1f17ba6ce57 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Sun, 12 Apr 2015 09:24:25 +0200 Subject: Installer: Remove system() function use (#1490139) Move some functionality of scripts from bin/ into rcmail_utils class --- CHANGELOG | 1 + bin/cleandb.sh | 49 +---- bin/indexcontacts.sh | 27 +-- bin/initdb.sh | 35 +--- bin/moduserprefs.sh | 35 +--- bin/update.sh | 8 +- bin/updatedb.sh | 138 +------------- program/include/rcmail_install.php | 8 +- program/include/rcmail_utils.php | 362 +++++++++++++++++++++++++++++++++++++ 9 files changed, 375 insertions(+), 288 deletions(-) create mode 100644 program/include/rcmail_utils.php (limited to 'program/include') diff --git a/CHANGELOG b/CHANGELOG index 3096b6e68..6c584fce1 100644 --- a/CHANGELOG +++ b/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 diff --git a/bin/cleandb.sh b/bin/cleandb.sh index d811c8d01..0bf71ea62 100755 --- a/bin/cleandb.sh +++ b/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 @@ define('INSTALL_PATH', realpath(__DIR__ . '/..') . '/' ); 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); ?> diff --git a/bin/indexcontacts.sh b/bin/indexcontacts.sh index 2844742f7..760e53792 100755 --- a/bin/indexcontacts.sh +++ b/bin/indexcontacts.sh @@ -24,31 +24,6 @@ define('INSTALL_PATH', realpath(__DIR__ . '/..') . '/' ); 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(); ?> diff --git a/bin/initdb.sh b/bin/initdb.sh index bf3244876..fd22007d2 100755 --- a/bin/initdb.sh +++ b/bin/initdb.sh @@ -37,39 +37,6 @@ 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); -} - -$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']); ?> diff --git a/bin/moduserprefs.sh b/bin/moduserprefs.sh index 3d46baaa4..09f73983a 100755 --- a/bin/moduserprefs.sh +++ b/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 @@ else if (empty($args[0]) || (!isset($args[1]) && !$args['delete'])) { $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']); ?> diff --git a/bin/update.sh b/bin/update.sh index 1dfaa961d..bfb2148f7 100755 --- a/bin/update.sh +++ b/bin/update.sh @@ -156,10 +156,8 @@ if ($RCI->configured) { // 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 @@ if ($RCI->configured) { // 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) { diff --git a/bin/updatedb.sh b/bin/updatedb.sh index ffeac0ba8..7e4f0f526 100755 --- a/bin/updatedb.sh +++ b/bin/updatedb.sh @@ -37,142 +37,6 @@ if (empty($opts['package'])) { 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)); ?> diff --git a/program/include/rcmail_install.php b/program/include/rcmail_install.php index 0d5fbc5da..e16177954 100644 --- a/program/include/rcmail_install.php +++ b/program/include/rcmail_install.php @@ -773,12 +773,8 @@ class rcmail_install */ 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)); } diff --git a/program/include/rcmail_utils.php b/program/include/rcmail_utils.php new file mode 100644 index 000000000..50700b0e2 --- /dev/null +++ b/program/include/rcmail_utils.php @@ -0,0 +1,362 @@ + | + | Author: Aleksander Machniak | + +-----------------------------------------------------------------------+ +*/ + +/** + * 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"; + } + } + } +} -- cgit v1.2.3