diff --git a/CHANGELOG b/CHANGELOG index 0e698307324..fc683ef037c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,10 @@ CHANGELOG Roundcube Webmail =========================== +- Add missing sql upgrade file for 'ip' column resize in session table (#5465) +- Do not show inline images of unsupported mimetype (#5463) - Password: Added LDAP PPolicy driver (#5364) +- Password: Added possibility to nicely redirect from other plugins on password expiration (#5468) - Implement separate action to mark all messages in a folder as \Seen (#5006) - Implement marking as \Seen in all folders or in a folder and its subfolders (#5076) - Archive: Don't reload messages list when it's not needed (#5225) @@ -48,11 +51,16 @@ CHANGELOG Roundcube Webmail - Managesieve: Unhide advanced rule controls if there are inputs with errors - Managesieve: Display warning message when filter form contains errors - Control search engine crawlers via X-Robots-Tag header instead of and robots.txt (#5098) +- Fix flickering of header topline in min-mode (#5426) - Fix bug where folders list would scroll to top when clicking on subscription checkbox (#5447) - Fix decoding of GB2312/GBK text when iconv is not installed (#5448) - Fix regression where creation of default folders wasn't functioning without prefix (#5460) - Enigma: Fix bug where last records on keys list were hidden (#5461) - Enigma: Fix key search with keyword containing non-ascii characters (#5459) +- Fix bug where deleting folders with subfolders could fail in some cases (#5466) +- Fix bug where IMAP password could be exposed via error message (#5472) +- Fix bug where it wasn't possible to store more that 2MB objects in memcache/apc, + Added memcache_max_allowed_packet and apc_max_allowed_packet settings (#5452) RELEASE 1.2.2 ------------- diff --git a/SQL/mssql.initial.sql b/SQL/mssql.initial.sql index ac9d93f281e..e0f8763487f 100644 --- a/SQL/mssql.initial.sql +++ b/SQL/mssql.initial.sql @@ -393,6 +393,6 @@ CREATE TRIGGER [contact_delete_member] ON [dbo].[contacts] WHERE [contact_id] IN (SELECT [contact_id] FROM deleted) GO -INSERT INTO [dbo].[system] ([name], [value]) VALUES ('roundcube-version', '2016081200') +INSERT INTO [dbo].[system] ([name], [value]) VALUES ('roundcube-version', '2016100900') GO \ No newline at end of file diff --git a/SQL/mssql/2016100900.sql b/SQL/mssql/2016100900.sql new file mode 100644 index 00000000000..38b359a4808 --- /dev/null +++ b/SQL/mssql/2016100900.sql @@ -0,0 +1,2 @@ +ALTER TABLE [dbo].[session] ALTER COLUMN [ip] [varchar] (40) COLLATE Latin1_General_CI_AI NOT NULL +GO diff --git a/SQL/mysql.initial.sql b/SQL/mysql.initial.sql index 22ffcd0432d..cb2e75f260a 100644 --- a/SQL/mysql.initial.sql +++ b/SQL/mysql.initial.sql @@ -210,4 +210,4 @@ CREATE TABLE `system` ( /*!40014 SET FOREIGN_KEY_CHECKS=1 */; -INSERT INTO system (name, value) VALUES ('roundcube-version', '2016081200'); +INSERT INTO system (name, value) VALUES ('roundcube-version', '2016100900'); diff --git a/SQL/mysql/2016100900.sql b/SQL/mysql/2016100900.sql new file mode 100644 index 00000000000..ea096e9f90a --- /dev/null +++ b/SQL/mysql/2016100900.sql @@ -0,0 +1 @@ +ALTER TABLE `session` MODIFY `ip` varchar(40) NOT NULL; diff --git a/SQL/oracle.initial.sql b/SQL/oracle.initial.sql index fabd2606bb4..668b7133353 100644 --- a/SQL/oracle.initial.sql +++ b/SQL/oracle.initial.sql @@ -219,4 +219,4 @@ CREATE TABLE "system" ( "value" long ); -INSERT INTO "system" ("name", "value") VALUES ('roundcube-version', '2016081200'); +INSERT INTO "system" ("name", "value") VALUES ('roundcube-version', '2016100900'); diff --git a/SQL/oracle/2016100900.sql b/SQL/oracle/2016100900.sql new file mode 100644 index 00000000000..e92e7eb91fc --- /dev/null +++ b/SQL/oracle/2016100900.sql @@ -0,0 +1 @@ +ALTER TABLE session MODIFY ip varchar(41) NOT NULL; diff --git a/SQL/postgres.initial.sql b/SQL/postgres.initial.sql index 6b2c93902b4..965bc54bd8b 100644 --- a/SQL/postgres.initial.sql +++ b/SQL/postgres.initial.sql @@ -291,4 +291,4 @@ CREATE TABLE "system" ( value text ); -INSERT INTO system (name, value) VALUES ('roundcube-version', '2016081200'); +INSERT INTO system (name, value) VALUES ('roundcube-version', '2016100900'); diff --git a/SQL/postgres/2016100900.sql b/SQL/postgres/2016100900.sql new file mode 100644 index 00000000000..8eb7eedacb3 --- /dev/null +++ b/SQL/postgres/2016100900.sql @@ -0,0 +1 @@ +ALTER TABLE session ALTER COLUMN ip TYPE character varying(41); diff --git a/SQL/sqlite.initial.sql b/SQL/sqlite.initial.sql index 081fe6ab890..83d6881338f 100644 --- a/SQL/sqlite.initial.sql +++ b/SQL/sqlite.initial.sql @@ -202,4 +202,4 @@ CREATE TABLE system ( value text NOT NULL ); -INSERT INTO system (name, value) VALUES ('roundcube-version', '2016081200'); +INSERT INTO system (name, value) VALUES ('roundcube-version', '2016100900'); diff --git a/SQL/sqlite/2016100900.sql b/SQL/sqlite/2016100900.sql new file mode 100644 index 00000000000..e69de29bb2d diff --git a/config/config.inc.php.sample b/config/config.inc.php.sample index 2b72cc6d140..77f367265f9 100644 --- a/config/config.inc.php.sample +++ b/config/config.inc.php.sample @@ -41,7 +41,6 @@ $config['default_host'] = 'localhost'; // SMTP server host (for sending mails). // To use SSL/TLS connection, enter hostname with prefix ssl:// or tls:// -// If left blank, the PHP mail() function is used // Supported replacement variables: // %h - user's IMAP hostname // %n - hostname ($_SERVER['SERVER_NAME']) @@ -49,7 +48,7 @@ $config['default_host'] = 'localhost'; // %d - domain (http hostname $_SERVER['HTTP_HOST'] without the first part) // %z - IMAP domain (IMAP hostname without the first part) // For example %n = mail.domain.tld, %t = domain.tld -$config['smtp_server'] = ''; +$config['smtp_server'] = 'localhost'; // SMTP port (default is 25; use 587 for STARTTLS or 465 for the // deprecated SSL over SMTP (aka SMTPS)) @@ -57,11 +56,11 @@ $config['smtp_port'] = 25; // SMTP username (if required) if you use %u as the username Roundcube // will use the current username for login -$config['smtp_user'] = ''; +$config['smtp_user'] = '%u'; // SMTP password (if required) if you use %p as the password Roundcube // will use the current user's password for login -$config['smtp_pass'] = ''; +$config['smtp_pass'] = '%p'; // provide an URL where a user can get support for this Roundcube installation // PLEASE DO NOT LINK TO THE ROUNDCUBE.NET WEBSITE HERE! diff --git a/config/defaults.inc.php b/config/defaults.inc.php index df5340462b7..74cd611e73e 100644 --- a/config/defaults.inc.php +++ b/config/defaults.inc.php @@ -308,6 +308,40 @@ $config['ldap_cache_ttl'] = '10m'; +// ---------------------------------- +// CACHE(S) +// ---------------------------------- + +// Use these hosts for accessing memcached +// Define any number of hosts in the form of hostname:port or unix:///path/to/socket.file +$config['memcache_hosts'] = null; // e.g. array( 'localhost:11211', '192.168.1.12:11211', 'unix:///var/tmp/memcached.sock' ); + +// Controls the use of a persistent connections to memcache servers +// See http://php.net/manual/en/memcache.addserver.php +$config['memcache_pconnect'] = true; + +// Value in seconds which will be used for connecting to the daemon +// See http://php.net/manual/en/memcache.addserver.php +$config['memcache_timeout'] = 1; + +// Controls how often a failed server will be retried (value in seconds). +// Setting this parameter to -1 disables automatic retry. +// See http://php.net/manual/en/memcache.addserver.php +$config['memcache_retry_interval'] = 15; + +// use these hosts for accessing Redis. +// Currently only one host is supported. cluster support may come in a future release. +// You can pass 4 fields, host, port, database and password. +// Unset fields will be set to the default values host=127.0.0.1, port=6379, database=0, password= (empty) +$config['redis_hosts'] = null; // e.g. array( 'localhost:6379' ); array( '192.168.1.1:6379:1:secret' ); + +// Maximum size of an object in memcache (in bytes). Default: 2MB +$config['memcache_max_allowed_packet'] = '2M'; + +// Maximum size of an object in APC cache (in bytes). Default: 2MB +$config['apc_max_allowed_packet'] = '2M'; + + // ---------------------------------- // SYSTEM // ---------------------------------- @@ -423,30 +457,6 @@ // Setting this value to 'php' will use the default session save handler configured in PHP $config['session_storage'] = 'db'; -// Use these hosts for accessing memcached -// Define any number of hosts in the form of hostname:port or unix:///path/to/socket.file -$config['memcache_hosts'] = null; // e.g. array( 'localhost:11211', '192.168.1.12:11211', 'unix:///var/tmp/memcached.sock' ); - -// Controls the use of a persistent connections to memcache servers -// See http://php.net/manual/en/memcache.addserver.php -$config['memcache_pconnect'] = true; - -// Value in seconds which will be used for connecting to the daemon -// See http://php.net/manual/en/memcache.addserver.php -$config['memcache_timeout'] = 1; - -// Controls how often a failed server will be retried (value in seconds). -// Setting this parameter to -1 disables automatic retry. -// See http://php.net/manual/en/memcache.addserver.php -$config['memcache_retry_interval'] = 15; - -// use this for accessing redis -// currently only one host is supported. cluster support may come in a future release. -// you can pass 4 fields, host, port, database and password. -// unset fields will be set to the default values host=127.0.0.1, port=6379, database=0, password= (empty) - -$config['redis_hosts'] = null; // e.g. array( 'localhost:6379' ); array( '192.168.1.1:6379:1:secret' ); - // check client IP in session authorization $config['ip_check'] = false; diff --git a/plugins/database_attachments/config.inc.php.dist b/plugins/database_attachments/config.inc.php.dist index c371cbbb73a..b47615b7908 100644 --- a/plugins/database_attachments/config.inc.php.dist +++ b/plugins/database_attachments/config.inc.php.dist @@ -3,6 +3,10 @@ // By default this plugin stores attachments in filesystem // and copies them into sql database. // You can change it to use 'memcache' or 'apc'. +// ----------------------------------------------------------- +// WARNING: Remember to set max_allowed_packet in database or +// config to match with expected max attachment size. +// ----------------------------------------------------------- $config['database_attachments_cache'] = 'db'; // Attachment data expires after specied TTL time in seconds (max.2592000). diff --git a/plugins/enigma/lib/enigma_ui.php b/plugins/enigma/lib/enigma_ui.php index b7fc011611d..7f2bf9b8e65 100644 --- a/plugins/enigma/lib/enigma_ui.php +++ b/plugins/enigma/lib/enigma_ui.php @@ -547,7 +547,7 @@ private function key_import() else if ($err = $_FILES['_file']['error']) { if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) { $this->rc->output->show_message('filesizeerror', 'error', - array('size' => $this->rc->show_bytes(parse_bytes(ini_get('upload_max_filesize'))))); + array('size' => $this->rc->show_bytes(rcube_utils::max_upload_size()))); } else { $this->rc->output->show_message('fileuploaderror', 'error'); } diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php b/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php index db50330eb71..0ceb7a72bc6 100644 --- a/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php +++ b/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php @@ -562,7 +562,7 @@ function save() if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) { $msg = $this->rc->gettext(array('name' => 'filesizeerror', 'vars' => array('size' => - $this->rc->show_bytes(parse_bytes(ini_get('upload_max_filesize')))))); + $this->rc->show_bytes(rcube_utils::max_upload_size())))); } else { $this->errors['file'] = $this->plugin->gettext('fileuploaderror'); diff --git a/plugins/password/composer.json b/plugins/password/composer.json index 8c6848ca7ae..3cc0c397fca 100644 --- a/plugins/password/composer.json +++ b/plugins/password/composer.json @@ -3,7 +3,7 @@ "type": "roundcube-plugin", "description": "Password Change for Roundcube. Plugin adds a possibility to change user password using many methods (drivers) via Settings/Password tab.", "license": "GPLv3+", - "version": "4.1", + "version": "4.2", "authors": [ { "name": "Aleksander Machniak", diff --git a/plugins/password/localization/en_GB.inc b/plugins/password/localization/en_GB.inc index 94e7657d3f3..ddd0c52de09 100644 --- a/plugins/password/localization/en_GB.inc +++ b/plugins/password/localization/en_GB.inc @@ -31,8 +31,8 @@ $messages['passwordweak'] = 'Password must include at least one number and one s $messages['passwordforbidden'] = 'Password contains forbidden characters.'; $messages['firstloginchange'] = 'This is your first login. Please change your password.'; $messages['disablednotice'] = 'The system is currently under maintenance and password change is not possible at the moment. Everything should be back to normal soon. We apologise for any inconvenience.'; -$messages['passwdinhistory'] = 'This password has already been used previously'; -$messages['samepasswd'] = 'New password have to be different from the old one'; -$messages['passwdexpirewarning'] = 'Warning! Your password will expire soon, change it before $expirationdatetime'; -$messages['passwdexpired'] = 'Your password has expired, you have to change it now !'; +$messages['passwdinhistory'] = 'This password has already been used previously.'; +$messages['samepasswd'] = 'New password have to be different from the old one.'; +$messages['passwdexpirewarning'] = 'Warning! Your password will expire soon, change it before $expirationdatetime.'; +$messages['passwdexpired'] = 'Your password has expired, you have to change it now!'; diff --git a/plugins/password/localization/en_US.inc b/plugins/password/localization/en_US.inc index fb9f3912bc4..b25495a33f1 100644 --- a/plugins/password/localization/en_US.inc +++ b/plugins/password/localization/en_US.inc @@ -35,8 +35,8 @@ $messages['passwordweak'] = 'Password must include at least one number and one p $messages['passwordforbidden'] = 'Password contains forbidden characters.'; $messages['firstloginchange'] = 'This is your first login. Please change your password.'; $messages['disablednotice'] = 'The system is currently under maintenance and password change is not possible at the moment. Everything should be back to normal soon. We apologize for any inconvenience.'; -$messages['passwinhistory'] = 'This password has already been used previously'; -$messages['samepasswd'] = 'New password have to be different from the old one'; -$messages['passwdexpirewarning'] = 'Warning! Your password will expire soon, change it before $expirationdatetime'; -$messages['passwdexpired'] = 'Your password has expired, you have to change it now !'; +$messages['passwinhistory'] = 'This password has already been used previously.'; +$messages['samepasswd'] = 'New password have to be different from the old one.'; +$messages['passwdexpirewarning'] = 'Warning! Your password will expire soon, change it before $expirationdatetime.'; +$messages['passwdexpired'] = 'Your password has expired, you have to change it now!'; diff --git a/plugins/password/localization/pl_PL.inc b/plugins/password/localization/pl_PL.inc index f2b9324c3ae..a12574d7941 100644 --- a/plugins/password/localization/pl_PL.inc +++ b/plugins/password/localization/pl_PL.inc @@ -33,5 +33,5 @@ $messages['firstloginchange'] = 'To jest twoje pierwsze logowanie. Proszę zmie $messages['disablednotice'] = 'System jest w trakcie konserwacji i zmiana hasła w tym momencie nie jest możliwa. Wszystko powinno wrócić do normy w niedługim czasie. Przepraszamy za wszelkie niedogodności'; $messages['passwdinhistory'] = 'To hasło było już użyte poprzednio.'; $messages['samepasswd'] = 'Nowe hasło musi być inne niż obecne.'; -$messages['passwdexpirewarning'] = 'UWAGA! Twoje hasło niebawem wygaśnie, zmień je przed $expirationdatetime'; -$messages['passwdexpired'] = 'Twoje hasło wygasło, musisz je natychmiast zmienić !'; +$messages['passwdexpirewarning'] = 'UWAGA! Twoje hasło niebawem wygaśnie, zmień je przed $expirationdatetime.'; +$messages['passwdexpired'] = 'Twoje hasło wygasło, musisz je natychmiast zmienić!'; diff --git a/plugins/password/password.php b/plugins/password/password.php index 4666ab3a8c7..2c594df801e 100644 --- a/plugins/password/password.php +++ b/plugins/password/password.php @@ -95,17 +95,18 @@ function password_init() if (rcube_utils::get_input_value('_first', rcube_utils::INPUT_GET)) { $rcmail->output->command('display_message', $this->gettext('firstloginchange'), 'notice'); } - - if (isset ($_SESSION['passwdexpdatetime'])) { - if ($_SESSION['passwdexpdatetime'] == 1){ + else if (!empty($_SESSION['password_expires'])) { + if ($_SESSION['password_expires'] == 1) { $rcmail->output->command('display_message', $this->gettext('passwdexpired'), 'error'); } else { - $rcmail->output->command('display_message', $this->gettext( - array('name' => 'passwdexpirewarning', 'vars' => array('expirationdatetime' => $_SESSION['passwdexpdatetime']))), 'warning'); - } + $rcmail->output->command('display_message', $this->gettext(array( + 'name' => 'passwdexpirewarning', + 'vars' => array('expirationdatetime' => $_SESSION['password_expires']) + )), 'warning'); + } } - + $rcmail->output->send('plugin'); } @@ -183,8 +184,9 @@ function password_save() rcube::write_log('password', sprintf('Password changed for user %s (ID: %d) from %s', $rcmail->get_user_name(), $rcmail->user->ID, rcube_utils::remote_ip())); } - // Remove expiration date/time - $rcmail->session->remove('passwdexpdatetime'); + + // Remove expiration date/time + $rcmail->session->remove('password_expires'); } else { $rcmail->output->command('display_message', $res, 'error'); diff --git a/plugins/redundant_attachments/config.inc.php.dist b/plugins/redundant_attachments/config.inc.php.dist index 47e84014b6b..9cc1b0034eb 100644 --- a/plugins/redundant_attachments/config.inc.php.dist +++ b/plugins/redundant_attachments/config.inc.php.dist @@ -4,6 +4,10 @@ // and copies them into sql database. // In environments with replicated database it is possible // to use memcache as a fallback when write-master is unavailable. +// ------------------------------------------------------------ +// WARNING: Remember to also set memcache_max_allowed_packet in +// config to match with expected max attachment size. +// ------------------------------------------------------------ $config['redundant_attachments_memcache'] = false; // Attachment data expires after specified TTL time in seconds (max.2592000). diff --git a/program/include/rcmail.php b/program/include/rcmail.php index fc6ea575d30..4402da0f6c2 100644 --- a/program/include/rcmail.php +++ b/program/include/rcmail.php @@ -2083,13 +2083,7 @@ public function upload_init($max_size = null) } // find max filesize value - $max_filesize = parse_bytes(ini_get('upload_max_filesize')); - $max_postsize = parse_bytes(ini_get('post_max_size')); - - if ($max_postsize && $max_postsize < $max_filesize) { - $max_filesize = $max_postsize; - } - + $max_filesize = rcube_utils::max_upload_size(); if ($max_size && $max_size < $max_filesize) { $max_filesize = $max_size; } diff --git a/program/js/editor.js b/program/js/editor.js index f2fe604cf01..6eb473b6211 100644 --- a/program/js/editor.js +++ b/program/js/editor.js @@ -407,27 +407,41 @@ function rcube_text_editor(config, id) }; // replace selection with text snippet - this.replace = function(text) + // input can be a string or object with 'text' and 'html' properties + this.replace = function(input) { - var ed = this.editor; + var format, ed = this.editor; // insert into tinymce editor if (ed) { ed.getWin().focus(); // correct focus in IE & Chrome - ed.selection.setContent(rcmail.quote_html(text).replace(/\r?\n/g, '
'), { format:'text' }); + + if ($.type(input) == 'object') { + input = input.html; + format = 'html'; + } + else { + input = rcmail.quote_html(input).replace(/\r?\n/g, '
'); + format = 'text'; + } + + ed.selection.setContent(input, {format: format}); } // replace selection in compose textarea else if (ed = rcube_find_object(this.id)) { - var selection = $(ed).is(':focus') ? rcmail.get_input_selection(ed) : { start:0, end:0 }, - inp_value = ed.value; - pre = inp_value.substring(0, selection.start), - end = inp_value.substring(selection.end, inp_value.length); + var selection = $(ed).is(':focus') ? rcmail.get_input_selection(ed) : {start: 0, end: 0}, + value = ed.value; + pre = value.substring(0, selection.start), + end = value.substring(selection.end, value.length); + + if ($.type(input) == 'object') + input = input.text; // insert response text - ed.value = pre + text + end; + ed.value = pre + input + end; // set caret after inserted text - rcmail.set_caret_pos(ed, selection.start + text.length); + rcmail.set_caret_pos(ed, selection.start + input.length); ed.focus(); } }; diff --git a/program/lib/Roundcube/html.php b/program/lib/Roundcube/html.php index 73b9a85d7a5..2cad9df9953 100644 --- a/program/lib/Roundcube/html.php +++ b/program/lib/Roundcube/html.php @@ -163,7 +163,7 @@ public static function img($attr = null) } return self::tag('img', $attr + array('alt' => ''), null, array_merge(self::$common_attrib, - array('src','alt','width','height','border','usemap','onclick','onerror'))); + array('src','alt','width','height','border','usemap','onclick','onerror','onload'))); } /** diff --git a/program/lib/Roundcube/rcube_cache.php b/program/lib/Roundcube/rcube_cache.php index 076a9ab0720..05e858cf0ba 100644 --- a/program/lib/Roundcube/rcube_cache.php +++ b/program/lib/Roundcube/rcube_cache.php @@ -645,16 +645,9 @@ private function max_packet_size() } $this->max_packet -= 2000; } - else if ($this->type == 'memcache') { - if ($stats = $this->db->getStats()) { - $remaining = $stats['limit_maxbytes'] - $stats['bytes']; - $this->max_packet = min($remaining / 5, $this->max_packet); - } - } - else if ($this->type == 'apc' && function_exists('apc_sma_info')) { - if ($stats = apc_sma_info()) { - $this->max_packet = min($stats['avail_mem'] / 5, $this->max_packet); - } + else { + $max_packet = rcube::get_instance()->config->get($this->type . '_max_allowed_packet'); + $this->max_packet = parse_bytes($max_packet) ?: $this->max_packet; } } diff --git a/program/lib/Roundcube/rcube_cache_shared.php b/program/lib/Roundcube/rcube_cache_shared.php index cbf87a4e24b..00d6345b3c7 100644 --- a/program/lib/Roundcube/rcube_cache_shared.php +++ b/program/lib/Roundcube/rcube_cache_shared.php @@ -632,16 +632,9 @@ private function max_packet_size() } $this->max_packet -= 2000; } - else if ($this->type == 'memcache') { - if ($stats = $this->db->getStats()) { - $remaining = $stats['limit_maxbytes'] - $stats['bytes']; - $this->max_packet = min($remaining / 5, $this->max_packet); - } - } - else if ($this->type == 'apc' && function_exists('apc_sma_info')) { - if ($stats = apc_sma_info()) { - $this->max_packet = min($stats['avail_mem'] / 5, $this->max_packet); - } + else { + $max_packet = rcube::get_instance()->config->get($this->type . '_max_allowed_packet'); + $this->max_packet = parse_bytes($max_packet) ?: $this->max_packet; } } diff --git a/program/lib/Roundcube/rcube_imap.php b/program/lib/Roundcube/rcube_imap.php index cbc9f3b1c60..2d137e4dade 100644 --- a/program/lib/Roundcube/rcube_imap.php +++ b/program/lib/Roundcube/rcube_imap.php @@ -3293,49 +3293,48 @@ public function rename_folder($folder, $new_name) } /** - * Remove folder from server + * Remove folder (with subfolders) from the server * * @param string $folder Folder name * - * @return boolean True on success + * @return boolean True on success, False on failure */ function delete_folder($folder) { - $delm = $this->get_hierarchy_delimiter(); - if (!$this->check_connection()) { return false; } - // get list of folders - if ((strpos($folder, '%') === false) && (strpos($folder, '*') === false)) { - $sub_mboxes = $this->list_folders('', $folder . $delm . '*'); - } - else { - $sub_mboxes = $this->list_folders(); - } - - // send delete command to server - $result = $this->conn->deleteFolder($folder); - - if ($result) { - // unsubscribe folder - $this->conn->unsubscribe($folder); + $delm = $this->get_hierarchy_delimiter(); - foreach ($sub_mboxes as $c_mbox) { - if (strpos($c_mbox, $folder.$delm) === 0) { - $this->conn->unsubscribe($c_mbox); - if ($this->conn->deleteFolder($c_mbox)) { - $this->clear_message_cache($c_mbox); + // get list of sub-folders or all folders + // if folder name contains special characters + $path = strspn($folder, '%*') > 0 ? ($folder . $delm) : ''; + $sub_mboxes = $this->list_folders('', $path . '*'); + + // According to RFC3501 deleting a \Noselect folder + // with subfolders may fail. To workaround this we delete + // subfolders first (in reverse order) (#5466) + if (!empty($sub_mboxes)) { + foreach (array_reverse($sub_mboxes) as $mbox) { + if (strpos($mbox, $folder . $delm) === 0) { + if ($this->conn->deleteFolder($mbox)) { + $this->conn->unsubscribe($mbox); + $this->clear_message_cache($mbox); } } } + } - // clear folder-related cache + // delete the folder + if ($result = $this->conn->deleteFolder($folder)) { + // and unsubscribe it + $this->conn->unsubscribe($folder); $this->clear_message_cache($folder); - $this->clear_cache('mailboxes', true); } + $this->clear_cache('mailboxes', true); + return $result; } diff --git a/program/lib/Roundcube/rcube_imap_generic.php b/program/lib/Roundcube/rcube_imap_generic.php index 98352c27509..4751d15fa8e 100644 --- a/program/lib/Roundcube/rcube_imap_generic.php +++ b/program/lib/Roundcube/rcube_imap_generic.php @@ -2028,7 +2028,7 @@ public function fetchHeaderIndex($mailbox, $message_set, $index_field = '', $ski $request = "$key $cmd $message_set (" . implode(' ', $fields) . ")"; if (!$this->putLine($request)) { - $this->setError(self::ERROR_COMMAND, "Unable to send command: $request"); + $this->setError(self::ERROR_COMMAND, "Failed to send $cmd command"); return false; } @@ -2355,15 +2355,15 @@ public function fetch($mailbox, $message_set, $is_uid = false, $query_items = ar $result = array(); $key = $this->nextTag(); - $request = $key . ($is_uid ? ' UID' : '') . " FETCH $message_set "; - $request .= "(" . implode(' ', $query_items) . ")"; + $cmd = ($is_uid ? 'UID ' : '') . 'FETCH'; + $request = "$key $cmd $message_set (" . implode(' ', $query_items) . ")"; if ($mod_seq !== null && $this->hasCapability('CONDSTORE')) { $request .= " (CHANGEDSINCE $mod_seq" . ($vanished ? " VANISHED" : '') .")"; } if (!$this->putLine($request)) { - $this->setError(self::ERROR_COMMAND, "Unable to send command: $request"); + $this->setError(self::ERROR_COMMAND, "Failed to send $cmd command"); return false; } @@ -2714,7 +2714,7 @@ public function fetchMIMEHeaders($mailbox, $uid, $parts, $mime = true) // send request if (!$this->putLine($request)) { - $this->setError(self::ERROR_COMMAND, "Unable to send command: $request"); + $this->setError(self::ERROR_COMMAND, "Failed to send UID FETCH command"); return false; } @@ -2782,14 +2782,15 @@ public function handlePartBody($mailbox, $id, $is_uid=false, $part='', $encoding // format request $key = $this->nextTag(); - $request = $key . ($is_uid ? ' UID' : '') . " FETCH $id ($fetch_mode.PEEK[$part]$partial)"; + $cmd = ($is_uid ? 'UID ' : '') . 'FETCH'; + $request = "$key $cmd $id ($fetch_mode.PEEK[$part]$partial)"; $result = false; $found = false; $initiated = true; // send request if (!$this->putLine($request)) { - $this->setError(self::ERROR_COMMAND, "Unable to send command: $request"); + $this->setError(self::ERROR_COMMAND, "Failed to send $cmd command"); return false; } @@ -2990,65 +2991,66 @@ public function append($mailbox, &$message, $flags = array(), $date = null, $bin $request .= ' ' . ($binary ? '~' : '') . '{' . $len . ($literal_plus ? '+' : '') . '}'; // send APPEND command - if ($this->putLine($request)) { - // Do not wait when LITERAL+ is supported - if (!$literal_plus) { - $line = $this->readReply(); + if (!$this->putLine($request)) { + $this->setError(self::ERROR_COMMAND, "Failed to send APPEND command"); + return false; + } - if ($line[0] != '+') { - $this->parseResult($line, 'APPEND: '); - return false; - } + // Do not wait when LITERAL+ is supported + if (!$literal_plus) { + $line = $this->readReply(); + + if ($line[0] != '+') { + $this->parseResult($line, 'APPEND: '); + return false; } + } - foreach ($msg as $msg_part) { - // file pointer - if (is_resource($msg_part)) { - rewind($msg_part); - while (!feof($msg_part) && $this->fp) { - $buffer = fread($msg_part, $chunk_size); - $this->putLine($buffer, false); - } - fclose($msg_part); + foreach ($msg as $msg_part) { + // file pointer + if (is_resource($msg_part)) { + rewind($msg_part); + while (!feof($msg_part) && $this->fp) { + $buffer = fread($msg_part, $chunk_size); + $this->putLine($buffer, false); } - // string - else { - $size = strlen($msg_part); + fclose($msg_part); + } + // string + else { + $size = strlen($msg_part); - // Break up the data by sending one chunk (up to 512k) at a time. - // This approach reduces our peak memory usage - for ($offset = 0; $offset < $size; $offset += $chunk_size) { - $chunk = substr($msg_part, $offset, $chunk_size); - if (!$this->putLine($chunk, false)) { - return false; - } + // Break up the data by sending one chunk (up to 512k) at a time. + // This approach reduces our peak memory usage + for ($offset = 0; $offset < $size; $offset += $chunk_size) { + $chunk = substr($msg_part, $offset, $chunk_size); + if (!$this->putLine($chunk, false)) { + return false; } } } + } - if (!$this->putLine('')) { // \r\n - return false; - } + if (!$this->putLine('')) { // \r\n + return false; + } - do { - $line = $this->readLine(); - } while (!$this->startsWith($line, $key, true, true)); + do { + $line = $this->readLine(); + } while (!$this->startsWith($line, $key, true, true)); - // Clear internal status cache - unset($this->data['STATUS:'.$mailbox]); + // Clear internal status cache + unset($this->data['STATUS:'.$mailbox]); - if ($this->parseResult($line, 'APPEND: ') != self::ERROR_OK) - return false; - else if (!empty($this->data['APPENDUID'])) - return $this->data['APPENDUID']; - else - return true; + if ($this->parseResult($line, 'APPEND: ') != self::ERROR_OK) { + return false; } - else { - $this->setError(self::ERROR_COMMAND, "Unable to send command: $request"); + + if (!empty($this->data['APPENDUID'])) { + return $this->data['APPENDUID']; } - return false; + return true; } /** @@ -3707,7 +3709,10 @@ public function execute($command, $arguments=array(), $options=0) // Send command if (!$this->putLineC($query, true, ($options & self::COMMAND_ANONYMIZED))) { - $this->setError(self::ERROR_COMMAND, "Unable to send command: $query"); + preg_match('/^[A-Z0-9]+ ((UID )?[A-Z]+)/', $query, $matches); + $cmd = $matches[1] ?: 'UNKNOWN'; + $this->setError(self::ERROR_COMMAND, "Failed to send $cmd command"); + return $noresp ? self::ERROR_COMMAND : array(self::ERROR_COMMAND, ''); } diff --git a/program/lib/Roundcube/rcube_utils.php b/program/lib/Roundcube/rcube_utils.php index 30168f884f1..d52c62115d7 100644 --- a/program/lib/Roundcube/rcube_utils.php +++ b/program/lib/Roundcube/rcube_utils.php @@ -1274,4 +1274,22 @@ public static function parse_socket_options(&$options, $host = null) $options = $options[$host]; } } + + /** + * Get maximum upload size + * + * @return int Maximum size in bytes + */ + public static function max_upload_size() + { + // find max filesize value + $max_filesize = parse_bytes(ini_get('upload_max_filesize')); + $max_postsize = parse_bytes(ini_get('post_max_size')); + + if ($max_postsize && $max_postsize < $max_filesize) { + $max_filesize = $max_postsize; + } + + return $max_filesize; + } } diff --git a/program/steps/addressbook/import.inc b/program/steps/addressbook/import.inc index 4425498712c..2161d9170ac 100644 --- a/program/steps/addressbook/import.inc +++ b/program/steps/addressbook/import.inc @@ -77,7 +77,7 @@ if (is_array($_FILES['_file'])) { // no vcards detected if (!count($vcards)) { if ($upload_error == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) { - $size = $RCMAIL->show_bytes(parse_bytes(ini_get('upload_max_filesize'))); + $size = $RCMAIL->show_bytes(rcube_utils::max_upload_size()); $OUTPUT->show_message('filesizeerror', 'error', array('size' => $size)); } else if ($upload_error) { diff --git a/program/steps/addressbook/upload_photo.inc b/program/steps/addressbook/upload_photo.inc index 4661ed2d2b9..6a5395b91b9 100644 --- a/program/steps/addressbook/upload_photo.inc +++ b/program/steps/addressbook/upload_photo.inc @@ -64,7 +64,7 @@ if ($filepath = $_FILES['_photo']['tmp_name']) { } else { // upload failed $err = $_FILES['_photo']['error']; - $size = $RCMAIL->show_bytes(parse_bytes(ini_get('upload_max_filesize'))); + $size = $RCMAIL->show_bytes(rcube_utils::max_upload_size()); if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) $msg = $RCMAIL->gettext(array('name' => 'filesizeerror', 'vars' => array('size' => $size))); diff --git a/program/steps/mail/attachments.inc b/program/steps/mail/attachments.inc index 8c1993e670d..b620cd8f72d 100644 --- a/program/steps/mail/attachments.inc +++ b/program/steps/mail/attachments.inc @@ -160,7 +160,7 @@ if (is_array($_FILES['_attachments']['tmp_name'])) { } else { // upload failed if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) { - $size = $RCMAIL->show_bytes(parse_bytes(ini_get('upload_max_filesize'))); + $size = $RCMAIL->show_bytes(rcube_utils::max_upload_size()); $msg = $RCMAIL->gettext(array('name' => 'filesizeerror', 'vars' => array('size' => $size))); } else if ($attachment['error']) { diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index dc6b4d24c99..b3f60c93885 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -1322,6 +1322,7 @@ function rcmail_message_body($attrib) if ($mimetype = rcmail_part_image_type($attach_prop)) { // display thumbnails if ($thumbnail_size) { + $supported = in_array($mimetype, $client_mimetypes); $show_link = array( 'href' => $MESSAGE->get_part_url($attach_prop->mime_id, false), 'onclick' => sprintf( @@ -1329,7 +1330,8 @@ function rcmail_message_body($attrib) rcmail_output::JS_OBJECT_NAME, $attach_prop->mime_id) ); - $out .= html::p('image-attachment', + + $out .= html::p(array('class' => 'image-attachment', 'style' => $supported ? '' : 'display:none'), html::a($show_link + array('class' => 'image-link', 'style' => sprintf('width:%dpx', $thumbnail_size)), html::img(array( 'class' => 'image-thumbnail', @@ -1337,12 +1339,13 @@ function rcmail_message_body($attrib) 'title' => $attach_prop->filename, 'alt' => $attach_prop->filename, 'style' => sprintf('max-width:%dpx; max-height:%dpx', $thumbnail_size, $thumbnail_size), + 'onload' => $supported ? '' : '$(this).parents(\'p.image-attachment\').show()', )) ) . html::span('image-filename', rcube::Q($attach_prop->filename)) . html::span('image-filesize', rcube::Q($RCMAIL->message_part_size($attach_prop))) . html::span('attachment-links', - (in_array($mimetype, $client_mimetypes) ? html::a($show_link, $RCMAIL->gettext('showattachment')) . ' ' : '') . + ($supported ? html::a($show_link, $RCMAIL->gettext('showattachment')) . ' ' : '') . html::a($show_link['href'] . '&_download=1', $RCMAIL->gettext('download')) ) . html::br(array('style' => 'clear:both')) diff --git a/program/steps/mail/import.inc b/program/steps/mail/import.inc index 55941e8a273..9c4eb83a5af 100644 --- a/program/steps/mail/import.inc +++ b/program/steps/mail/import.inc @@ -92,7 +92,7 @@ if (is_array($_FILES['_file'])) { } if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) { - $size = $RCMAIL->show_bytes(parse_bytes(ini_get('upload_max_filesize'))); + $size = $RCMAIL->show_bytes(rcube_utils::max_upload_size()); $msg = $RCMAIL->gettext(array('name' => 'filesizeerror', 'vars' => array('size' => $size))); } else if ($err) { diff --git a/program/steps/settings/upload.inc b/program/steps/settings/upload.inc index dc81982a0df..99b398d3c8a 100644 --- a/program/steps/settings/upload.inc +++ b/program/steps/settings/upload.inc @@ -47,7 +47,7 @@ $IMAGE_TYPES = explode(',', 'jpeg,jpg,jp2,tiff,tif,bmp,eps,gif,png,png8,png24,pn $OUTPUT->reset(); $max_size = $RCMAIL->config->get($type . '_image_size', 64) * 1024; -$post_size = $RCMAIL->show_bytes(parse_bytes(ini_get('upload_max_filesize'))); +$post_size = $RCMAIL->show_bytes(rcube_utils::max_upload_size()); $uploadid = rcube_utils::get_input_value('_uploadid', rcube_utils::INPUT_GET); diff --git a/skins/larry/images/filetypes.png b/skins/larry/images/filetypes.png index 020c37d97c4..1294718cdf0 100644 Binary files a/skins/larry/images/filetypes.png and b/skins/larry/images/filetypes.png differ diff --git a/skins/larry/includes/footer.html b/skins/larry/includes/footer.html index 6cd3e62d133..cade440bd6b 100644 --- a/skins/larry/includes/footer.html +++ b/skins/larry/includes/footer.html @@ -1,13 +1,2 @@ - diff --git a/skins/larry/includes/header.html b/skins/larry/includes/header.html index 72a02f26803..5efe5b60043 100644 --- a/skins/larry/includes/header.html +++ b/skins/larry/includes/header.html @@ -1,3 +1,15 @@ + +