From e6bb836e053913ec8a5930a6d51af1fa55eb08e2 Mon Sep 17 00:00:00 2001 From: thomascube Date: Fri, 25 Feb 2011 16:37:22 +0000 Subject: [PATCH] Create interactive update script with improved DB schema check; udated installer with new features and styles --- CHANGELOG | 1 + SQL/mssql.upgrade.sql | 2 +- SQL/mysql.update.sql | 2 +- SQL/postgres.update.sql | 2 +- SQL/sqlite.update.sql | 2 +- UPGRADING | 33 +++- bin/installto.sh | 73 +++++++++ bin/update.sh | 55 ++++++- installer/check.php | 1 + installer/images/banner_bg.gif | Bin 587 -> 0 bytes installer/images/banner_gradient.gif | Bin 0 -> 506 bytes installer/images/banner_logo.gif | Bin 4345 -> 0 bytes installer/images/banner_right.gif | Bin 433 -> 0 bytes installer/images/banner_schraffur.gif | Bin 0 -> 12454 bytes installer/images/rcube_logo.gif | Bin 0 -> 4526 bytes installer/index.php | 17 +-- installer/rcube_install.php | 168 ++++++++++++++++----- installer/styles.css | 71 ++++----- installer/test.php | 21 ++- program/include/rcube_mdb2.php | 17 +++ program/lib/MDB2/Driver/Reverse/sqlite.php | 2 +- 21 files changed, 357 insertions(+), 110 deletions(-) create mode 100755 bin/installto.sh delete mode 100644 installer/images/banner_bg.gif create mode 100644 installer/images/banner_gradient.gif delete mode 100644 installer/images/banner_logo.gif delete mode 100644 installer/images/banner_right.gif create mode 100644 installer/images/banner_schraffur.gif create mode 100644 installer/images/rcube_logo.gif diff --git a/CHANGELOG b/CHANGELOG index 8d2077354..e2d27b9ff 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Interactive update script with improved DB schema check - jQuery 1.5.1 - Fix problem with contactgroupmembers table creation on MySQL 4.x, add index on contact_id column - Add LDAP SASL bind and proxy authentication (#1486692) diff --git a/SQL/mssql.upgrade.sql b/SQL/mssql.upgrade.sql index 8534afdd3..eacfcf11d 100644 --- a/SQL/mssql.upgrade.sql +++ b/SQL/mssql.upgrade.sql @@ -97,7 +97,7 @@ GO ALTER TABLE [dbo].[contacts] ALTER COLUMN [email] [varchar] (255) COLLATE Latin1_General_CI_AI NOT NULL GO --- Updates from version 0.5.x +-- Updates from version 0.5.1 ALTER TABLE [dbo].[contacts] ADD [words] [text] COLLATE Latin1_General_CI_AI NULL GO diff --git a/SQL/mysql.update.sql b/SQL/mysql.update.sql index d30f037c6..4cb087edd 100644 --- a/SQL/mysql.update.sql +++ b/SQL/mysql.update.sql @@ -133,7 +133,7 @@ ALTER TABLE `contacts` MODIFY `email` varchar(255) NOT NULL; TRUNCATE TABLE `messages`; --- Updates from version 0.5.* +-- Updates from version 0.5.1 ALTER TABLE `contacts` ADD `words` TEXT NULL AFTER `vcard`; ALTER TABLE `contactgroupmembers` ADD INDEX `contactgroupmembers_contact_index` (`contact_id`); diff --git a/SQL/postgres.update.sql b/SQL/postgres.update.sql index e3eb581f9..91c32f8ec 100644 --- a/SQL/postgres.update.sql +++ b/SQL/postgres.update.sql @@ -90,7 +90,7 @@ ALTER TABLE contacts ALTER email TYPE varchar(255); TRUNCATE messages; --- Updates from version 0.5.x +-- Updates from version 0.5.1 ALTER TABLE contacts ADD words TEXT NULL; CREATE INDEX contactgroupmembers_contact_id_idx ON contactgroupmembers (contact_id); diff --git a/SQL/sqlite.update.sql b/SQL/sqlite.update.sql index 7b5464c64..d8a657a98 100644 --- a/SQL/sqlite.update.sql +++ b/SQL/sqlite.update.sql @@ -183,7 +183,7 @@ DROP TABLE contacts_tmp; DELETE FROM messages; --- Updates from version 0.5.x +-- Updates from version 0.5.1 CREATE TABLE contacts_tmp ( contact_id integer NOT NULL PRIMARY KEY, diff --git a/UPGRADING b/UPGRADING index 329983d37..f20364345 100644 --- a/UPGRADING +++ b/UPGRADING @@ -5,6 +5,26 @@ Follow these instructions if upgrading from a previous version of Roundcube Webmail. We recommend to carefully backup the existing installation as well as the database before executig the following steps. +Using the update script +----------------------- +There is a shell script (for unix based systems) that does the job for you. +To use it, unpack the archive of the new Roundcube version to a temporary location (don't replace the Roundcube installation you want to update) +and cd into that directory. From there, run the following command in a shell: + + ./bin/installto.sh + +For you specify the path to the Roundcube installation +which should be updated. The update script will then copy all new files to the +target location and check and update the configuration and database schema. +After all is done, the temporary folder with the new Roundcube files can be +removed again. + + +Updating manually +----------------- +If you don't have shell access to the Roundcube instalaltion or if not running +it on a unix system, you need to do the following operations by hand: + 1. Replace index.php and all files in - ./bin/ - ./SQL/ @@ -14,14 +34,11 @@ installation as well as the database before executig the following steps. - ./plugins/ 2. Run ./bin/update.sh from the commandline OR open http://url-to-roundcube/installer/ in a browser and choose "3 Test config". - To enable the latter one, you have to temporary set 'enable_installer' to true - in your local config/main.inc.php file. + To enable the latter one, you have to temporary set 'enable_installer' + to true in your local config/main.inc.php file. 3. Let the update script/installer check your configuration and - update your config files as suggested by the updater. -4. If suggested by the update script, run all commands in - ./SQL/[yourdbtype].update.sql that are superscribed with the - currently installed version number. -5. Make sure 'enable_installer' is set to false again. -6. Check .htaccess settings (some php settings could become required) + update your config files and database schema as suggested by the updater. +4. Make sure 'enable_installer' is set to false again. +5. Check .htaccess settings (some php settings could become required) diff --git a/bin/installto.sh b/bin/installto.sh new file mode 100755 index 000000000..f953419ce --- /dev/null +++ b/bin/installto.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env php + | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + +if (php_sapi_name() != 'cli') { + die('Not on the "shell" (php-cli).'); +} +define('INSTALL_PATH', realpath(dirname(__FILE__) . '/..') . '/' ); + +require_once INSTALL_PATH . 'program/include/iniset.php'; + +$target_dir = unslashify($_SERVER['argv'][1]); + +if (empty($target_dir) || !is_dir(realpath($target_dir))) + die("Invalid target: not a directory\nUsage: installto.sh \n"); + +// read version from iniset.php +$iniset = @file_get_contents($target_dir . '/program/include/iniset.php'); +if (!preg_match('/define\(.RCMAIL_VERSION.,\s*.([0-9.]+[a-z-]*)/', $iniset, $m)) + die("No valid Roundcube installation found at $target_dir\n"); + +$oldversion = $m[1]; + +if (version_compare($oldversion, RCMAIL_VERSION, '>=')) + die("Installation at target location is up-to-date!\n"); + +echo "Upgrading from $oldversion. Do you want to continue? (y/N)\n"; +$input = trim(fgets(STDIN)); + +if (strtolower($input) == 'y') { + $err = false; + echo "Copying files to target location..."; + foreach (array('program','installer','bin','SQL','plugins','skins/default') as $dir) { + if (!system("rsync -avuC " . INSTALL_PATH . "$dir/* $target_dir/$dir/")) { + $err = true; + break; + } + } + foreach (array('index.php','.htaccess','config/main.inc.php.dist','config/db.inc.php.dist','CHANGELOG','README','UPGRADING') as $file) { + if (!system("rsync -avu " . INSTALL_PATH . "$file $target_dir/$file")) { + $err = true; + break; + } + } + echo "done.\n\n"; + + if (!$err) { + echo "Running update script at target...\n"; + system("cd $target_dir && bin/update.sh --version=$oldversion"); + echo "All done.\n"; + } +} +else + echo "Update cancelled. See ya!\n"; + +?> diff --git a/bin/update.sh b/bin/update.sh index a2988879c..094e5b72b 100755 --- a/bin/update.sh +++ b/bin/update.sh @@ -1,13 +1,46 @@ #!/usr/bin/env php | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + if (php_sapi_name() != 'cli') { die('Not on the "shell" (php-cli).'); } define('INSTALL_PATH', realpath(dirname(__FILE__) . '/..') . '/' ); -require_once INSTALL_PATH . 'program/include/iniset.php'; +require_once INSTALL_PATH . 'program/include/clisetup.php'; require_once INSTALL_PATH . 'installer/rcube_install.php'; +// get arguments +$opts = get_opt(array('v' => 'version')); + +// ask user if no version is specified +if (!$opts['version']) { + echo "What version are you upgrading from? Type '?' if you don't know.\n"; + if (($input = trim(fgets(STDIN))) && preg_match('/^[0-9.]+[a-z-]*$/', $input)) + $opts['version'] = $input; +} + +if ($opts['version'] && version_compare($opts['version'], RCMAIL_VERSION, '>')) + die("Nothing to be done here. Bye!\n"); + + $RCI = rcube_install::get_instance(); $RCI->load_config(); @@ -88,7 +121,7 @@ if ($RCI->configured) { } } else { - echo "Please update your config files manually according to the above messages.\n"; + echo "Please update your config files manually according to the above messages.\n\n"; } } @@ -113,12 +146,22 @@ if ($RCI->configured) { echo "Error connecting to database: $db_error_msg\n"; $success = false; } - else if ($RCI->db_schema_check($DB, false)) { - $db_map = array('pgsql' => 'postgres', 'mysqli' => 'mysql', 'sqlsrv' => 'mssql'); - $updatefile = INSTALL_PATH . 'SQL/' . (isset($db_map[$DB->db_provider]) ? $db_map[$DB->db_provider] : $DB->db_provider) . '.update.sql'; + else if ($err = $RCI->db_schema_check($DB, false)) { + $updatefile = INSTALL_PATH . 'SQL/' . (isset($RCI->db_map[$DB->db_provider]) ? $RCI->db_map[$DB->db_provider] : $DB->db_provider) . '.update.sql'; echo "WARNING: Database schema needs to be updated!\n"; - echo "Open $updatefile and execute all queries that are superscribed with the currently installed version number\n"; + echo join("\n", $err) . "\n\n"; $success = false; + + if ($opts['version']) { + echo "Do you want to run the update queries to get the schmea fixed? (y/N)\n"; + $input = trim(fgets(STDIN)); + if (strtolower($input) == 'y') { + $success = $RCI->update_db($DB, $opts['version']); + } + } + + if (!$success) + echo "Open $updatefile and execute all queries below the comment with the currently installed version number.\n"; } } diff --git a/installer/check.php b/installer/check.php index 0ba5f58de..944d3840f 100644 --- a/installer/check.php +++ b/installer/check.php @@ -22,6 +22,7 @@ $required_libs = array( 'PEAR' => 'PEAR.php', 'MDB2' => 'MDB2.php', 'Net_SMTP' => 'Net/SMTP.php', + 'Net_IDNA2' => 'Net/IDNA2.php', 'Mail_mime' => 'Mail/mime.php', ); diff --git a/installer/images/banner_bg.gif b/installer/images/banner_bg.gif deleted file mode 100644 index 9cef8a7c52912b495c9908d9367b3e4ed74ab120..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 587 zcmZ?wbhEHblw+`9xXQp_Zf6`Yik=A7#JKJoSK>%9UWa-T3TLS-q6tC z=H?a|8Ch9bnUs`dWo6~)=vYutP+VMGS67#lljGy#)6~?|-rj!k;>E12tk%|6PfySE z^z@pVnz*<)XJ_Y*jt)ORzm}Gk?(S}5W8?q-|1(ezDE?#t8>RyyL4IOj`|mKhz(Yr> z|HP7_lQ~xNFRUo>S{t?g#+tIzXDaqT*ii9Cb>{gOTdL0H?Y{qEM~&a~_IE$_)Scg- z{I8+0skxbtGlONxqrgMNt37ai%*|1bJpxR)4As_Sh#5MlKCRbSFBvMdd+gV z^&2*B+Pq~w-}W6lckSM@oooMrgNF_u+0S|W#K}{q&m0#%f8pY#%U8||UB7Yj*6lmj zMejd&_~`MI`%=$eynOZg&2!oJA3lEi{N+9S_a8rh{r>Zv|33pWmyE}U`|Zsf!dfvW zJ}5M`3n+Wd@%Wh7+$~|8b?3y#MGgH5&RsH|pB6Vx)(Bn|bMjM4<8*`MQ*%5&FYTCZ zQT*!8${uPKIqUAJuUReY6ApLD zdVkAq*qm{BRqW|+I*r>49-o@){av?Zcg5#dcTa!UYuMkw%q{2h<9);74q@%MGd~JC Vk55qcp6BzkaOvq8imZ$b)&Mnv1#AES diff --git a/installer/images/banner_gradient.gif b/installer/images/banner_gradient.gif new file mode 100644 index 0000000000000000000000000000000000000000..8ab1b06304d35a8ca4cec9d2822b535eed434d2c GIT binary patch literal 506 zcmVh}HS^!@Mn{p0fe-SGY2@crBF{o(TcmPE2?5Q_&dklu0SL?#*wGXT*xlRQ z+Y|%c*W=#e1LNuG78dCg?E~%b^6>5P_ZA2D`|bS)2Qp*;I56Nqfd~^S3}`T60fY|~ zW+?EmLWPJEGg#bsaic~89794N0J5Y>kPt?9l3baxWl91sN4|uq!KF->HFM@Puv3Fi z4LpBpxF8g0P@V=Z@Vr2j>4gnLH$0u{)G1V}8?tKE`cx|i1spbn&C22H*9{8Neg#`r w0ot%>)hhVV7VL+)W!I+tK({Pk4t((f4$QZ(;lqd%D_+dF@!`CX^M(KbJEvw58UO$Q literal 0 HcmV?d00001 diff --git a/installer/images/banner_logo.gif b/installer/images/banner_logo.gif deleted file mode 100644 index a7dd1142621ad02bdb995206f48b2fc58935b512..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4345 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 diff --git a/installer/images/banner_right.gif b/installer/images/banner_right.gif deleted file mode 100644 index 3248668ac0f8ede8adbc5631eec5dc68270a9c5e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 433 zcmV;i0Z#r$Nk%w1VF~~^0M!5hR#sM1Q&V?$cYJ(&aBy&dfPiIXWj{YZSy@?vf`Uv; zOi@u$TU%RaXJ>3|Y-(z1etv#+b#+NeNn&DRPEJmFd3kelb4Er+US3{9Lqmv&h=+%V zg@uJZJv~7|K{`4*H#avmH8nFcGttq}tE;Q#=H~bJ_kDeRkdTnx-rk0WhPAb|)z#Jf z{QNvTJn!%C@$vD}($eDM;*ye*o12^0*VnhVw~vpHMMXuiv9ZR+#`^mDgoK2Pi;MsN z{{R30A^8LV00000EC2ui015y&000KgK%a0(EEC#q0b334wxx1%-x(5Q&P51C5T46_JvY0+p7R z5t*8s7M-4-1fim%4yC5252>oF4Xv)P2C=fU6ScOt7`eK;486X;2*JX`0L8|~0m;hC z3(d~Y6w%Vt3f0!v64~0@7v0|98R6pMI3DKb=K%-n?Cb&^@bU3EANBV206QT2{2(qY bK0*i_ctD2`5eN~UkTIkW2^%nq1OWg$I9Jky diff --git a/installer/images/banner_schraffur.gif b/installer/images/banner_schraffur.gif new file mode 100644 index 0000000000000000000000000000000000000000..50182b4d6a6ddcb5c70c98f91e8e321daf408081 GIT binary patch literal 12454 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

s2rMFxXF$Z6qem7Doswy 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 literal 0 HcmV?d00001 diff --git a/installer/index.php b/installer/index.php index 1c157da7f..e09b82797 100644 --- a/installer/index.php +++ b/installer/index.php @@ -68,14 +68,13 @@ if ($RCI->configured && empty($_REQUEST['_step'])) {

+ + + + +
@@ -120,7 +119,7 @@ else {
diff --git a/installer/rcube_install.php b/installer/rcube_install.php index 3f4e607f5..3f2d1a48e 100644 --- a/installer/rcube_install.php +++ b/installer/rcube_install.php @@ -29,10 +29,11 @@ class rcube_install 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', @@ -42,7 +43,10 @@ class rcube_install ); // 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 @@ -294,7 +298,7 @@ class rcube_install $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]); @@ -302,8 +306,8 @@ class rcube_install $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) { @@ -320,6 +324,9 @@ class rcube_install } } + 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); foreach ((array)$current['ldap_public'] as $key => $values) { @@ -339,18 +346,8 @@ class rcube_install 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 @@ -358,13 +355,43 @@ class rcube_install 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 @@ -474,6 +501,16 @@ class rcube_install 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; + } + /** * Display OK status @@ -592,39 +629,98 @@ class rcube_install */ 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"; + $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 false; + } + + if ($err = $this->get_error()) { + $this->fail('DB Schema', "Error creating database schema: $err"); + return false; + } + + 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)) { - $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; + $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 schema file: $fname"); + $this->fail('DB Schema', "Cannot read the update file: $fname"); return false; } if ($err = $this->get_error()) { - $this->fail('DB Schema', "Error creating database schema: $err"); + $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 */ diff --git a/installer/styles.css b/installer/styles.css index 1acdc9cd0..06f49e370 100644 --- a/installer/styles.css +++ b/installer/styles.css @@ -1,62 +1,53 @@ - body { - margin: 1em 2em 2em 2em; - background-color: #fff; -} - -body, td, th, div, p { - font-family: "Lucida Grande", Verdana, Arial, Helvetica, sans-serif; - font-size: small; - color: #000; + background: white; + font-family: "Lucida Grande", Verdana, Arial, Helvetica, sans-serif; + font-size: small; + color: black; + margin: 0; } #banner { - position: relative; + position: relative; + height: 58px; + margin: 0 0 1em 0; + padding: 10px 20px; + background: url('images/banner_gradient.gif') top left repeat-x #d8edfd; + overflow: hidden; } -#header { - position: relative; - height: 56px; - background: url('images/banner_bg.gif') top left repeat-x #fff; +#banner .banner-bg { + position: absolute; + top: 0; + right: 0; + width: 630px; + height: 78px; + background: url('images/banner_schraffur.gif') top right no-repeat; + z-index: 0; } -#header div.banner-logo { - position: absolute; - top: 0px; - left: 0px; - width: 200px; - height: 56px; +#banner .banner-logo { + position: absolute; + top: 10px; + left: 20px; + z-index: 4; } -#header div.banner-right { - position: absolute; - right: 0px; - top: 0px; - width: 10px; - height: 56px; +#banner .banner-logo a { + border: 0; } #topnav { - position: absolute; - right: 20px; - bottom: 8px; - text-align: right; - color: #ebebeb; - font-size: smaller; + position: absolute; + top: 3.6em; + right: 20px; } #topnav a { - color: #ebebeb; - font-size: 11px; - text-decoration: none; -} - -#topnav a:hover { - text-decoration: underline; + color: #666; } #content { - margin: 8px 20px; + margin: 2em 20px; } #footer { diff --git a/installer/test.php b/installer/test.php index 713edfbb6..02a1cebe2 100644 --- a/installer/test.php +++ b/installer/test.php @@ -156,6 +156,14 @@ if ($db_working && $_POST['initdb']) { } } +else if ($db_working && $_POST['updatedb']) { + if (!($success = $RCI->update_db($DB, $_POST['version']))) { + $updatefile = INSTALL_PATH . 'SQL/' . (isset($RCI->db_map[$DB->db_provider]) ? $RCI->db_map[$DB->db_provider] : $DB->db_provider) . '.update.sql'; + echo '

Please manually execute the SQL statements from '.$updatefile.' on your database.
'; + echo 'See comments in the file and execute queries below the comment with the currently installed version number.

'; + } +} + // test database if ($db_working) { $db_read = $DB->query("SELECT count(*) FROM {$RCI->config['db_table_users']}"); @@ -164,12 +172,13 @@ if ($db_working) { echo '

'; $db_working = false; } - else if ($RCI->db_schema_check($DB, $update = !empty($_POST['updatedb']))) { + else if ($err = $RCI->db_schema_check($DB, $update = !empty($_POST['updatedb']))) { $RCI->fail('DB Schema', "Database schema differs"); - $db_map = array('pgsql' => 'postgres', 'mysqli' => 'mysql', 'sqlsrv' => 'mssql'); - $updatefile = INSTALL_PATH . 'SQL/' . (isset($db_map[$DB->db_provider]) ? $db_map[$DB->db_provider] : $DB->db_provider) . '.update.sql'; - echo '

Please manually execute the SQL statements from '.$updatefile.' on your database.
'; - echo 'See comments in the file and execute queries that are superscribed with the currently installed version number.

'; + echo '
  • ' . join("
  • \n
  • ", $err) . "
"; + $select = $RCI->versions_select(array('name' => 'version')); + echo '

You should run the update queries to get the schmea fixed.

Version to update from: ' . $select->show() . ' 

'; +// echo '

Please manually execute the SQL statements from '.$updatefile.' on your database.
'; +// echo 'See comments in the file and execute queries that are superscribed with the currently installed version number.

'; $db_working = false; } else { @@ -412,7 +421,7 @@ if (isset($_POST['imaptest']) && !empty($_POST['_host']) && !empty($_POST['_user After completing the installation and the final tests please remove the whole installer folder from the document root of the webserver or make sure that -enable_installer option in main.inc.php is disabled.
+enable_installer option in config/main.inc.php is disabled.

These files may expose sensitive configuration data like server passwords and encryption keys diff --git a/program/include/rcube_mdb2.php b/program/include/rcube_mdb2.php index c56a86e37..85a70cad3 100644 --- a/program/include/rcube_mdb2.php +++ b/program/include/rcube_mdb2.php @@ -412,6 +412,23 @@ class rcube_mdb2 } + /** + * Wrapper for SHOW COLUMNS command + * + * @param string Table name + * @return array List of table cols + */ + function list_cols($table) + { + $this->db_handle->loadModule('Manager'); + if (!PEAR::isError($result = $this->db_handle->listTableFields($table))) { + return $result; + } + + return null; + } + + /** * Formats input so it can be safely used in a query * diff --git a/program/lib/MDB2/Driver/Reverse/sqlite.php b/program/lib/MDB2/Driver/Reverse/sqlite.php index c88ad0ddb..43cec992a 100644 --- a/program/lib/MDB2/Driver/Reverse/sqlite.php +++ b/program/lib/MDB2/Driver/Reverse/sqlite.php @@ -95,7 +95,7 @@ class MDB2_Driver_Reverse_sqlite extends MDB2_Driver_Reverse_Common return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'unexpected empty table column definition list', __FUNCTION__); } - $regexp = '/^\s*([^\s]+) +(CHAR|VARCHAR|VARCHAR2|TEXT|BOOLEAN|SMALLINT|INT|INTEGER|DECIMAL|BIGINT|DOUBLE|FLOAT|DATETIME|DATE|TIME|LONGTEXT|LONGBLOB)( ?\(([1-9][0-9]*)(:([1-9][0-9]*))?\))?( NULL| NOT NULL)?( UNSIGNED)?( NULL| NOT NULL)?( PRIMARY KEY)?( DEFAULT (\'[^\']*\'|[^ ]+))?( NULL| NOT NULL)?( PRIMARY KEY)?(\s*\-\-.*)?$/i'; + $regexp = '/^\s*([^\s]+) +(CHAR|VARCHAR|VARCHAR2|TEXT|BOOLEAN|SMALLINT|INT|INTEGER|DECIMAL|TINYINT|BIGINT|DOUBLE|FLOAT|DATETIME|DATE|TIME|LONGTEXT|LONGBLOB)( ?\(([1-9][0-9]*)(:([1-9][0-9]*))?\))?( NULL| NOT NULL)?( UNSIGNED)?( NULL| NOT NULL)?( PRIMARY KEY)?( DEFAULT (\'[^\']*\'|[^ ]+))?( NULL| NOT NULL)?( PRIMARY KEY)?(\s*\-\-.*)?$/i'; $regexp2 = '/^\s*([^ ]+) +(PRIMARY|UNIQUE|CHECK)$/i'; for ($i=0, $j=0; $i<$count; ++$i) { if (!preg_match($regexp, trim($column_sql[$i]), $matches)) {