diff options
34 files changed, 286 insertions, 73 deletions
@@ -27,7 +27,7 @@ php_value session.gc_probability 1 RewriteEngine On RewriteRule ^favicon\.ico$ skins/larry/images/favicon.ico # security rules -RewriteRule .git - [F] +RewriteRule \.git - [F] RewriteRule ^/?(README(.md)?|INSTALL|LICENSE|CHANGELOG|UPGRADING)$ - [F] RewriteRule ^/?(SQL|bin) - [F] </IfModule> @@ -1,6 +1,13 @@ CHANGELOG Roundcube Webmail =========================== +- Fix XSS vulnerability when saving HTML signatures (#1489251) +- Move identity selection based on non-standard headers into (new) identity_select plugin (#1488553) +- Fix colorspace issue on image conversion using ImageMagick (#1489270) +- Fix XSS vulnerability when editing a message "as new" or draft (#1489251) +- Fix downloading binary files with (wrong) text/* content-type (#1489267) +- Fix rewrite rule in .htaccess (#1489240) +- Fix detecting Turkish language in ISO-8859-9 encoding (#1489252) - Fix identity-selection using Return-Path headers (#1489241) - Fix parsing of links with ... in URL (#1489192) - Fix compose priority selector when opening in new window (#1489257) diff --git a/plugins/attachment_reminder/attachment_reminder.js b/plugins/attachment_reminder/attachment_reminder.js index 50d661b3b..7ef2072c6 100755 --- a/plugins/attachment_reminder/attachment_reminder.js +++ b/plugins/attachment_reminder/attachment_reminder.js @@ -20,7 +20,7 @@ function rcmail_check_message(msg) { var i, rx, keywords = rcmail.gettext('keywords', 'attachment_reminder').split(",").concat([".doc", ".pdf"]); - $.each(keywords, function(n) { return RegExp.escape(n); }); + keywords = $.map(keywords, function(n) { return RegExp.escape(n); }); rx = new RegExp('(' + keywords.join('|') + ')', 'i'); return msg.search(rx) != -1; diff --git a/plugins/database_attachments/database_attachments.php b/plugins/database_attachments/database_attachments.php index 47e2b5222..5ec351404 100644 --- a/plugins/database_attachments/database_attachments.php +++ b/plugins/database_attachments/database_attachments.php @@ -13,7 +13,9 @@ * @author Aleksander Machniak <alec@alec.pl> * @version @package_version@ */ -require_once('plugins/filesystem_attachments/filesystem_attachments.php'); + +require_once INSTALL_PATH . 'plugins/filesystem_attachments/filesystem_attachments.php'; + class database_attachments extends filesystem_attachments { // Cache object @@ -84,14 +86,10 @@ class database_attachments extends filesystem_attachments */ function remove($args) { - $args['status'] = false; - $cache = $this->get_cache(); $status = $cache->remove($args['id']); - if ($status) { - $args['status'] = true; - } + $args['status'] = true; return $args; } diff --git a/plugins/identity_select/identity_select.php b/plugins/identity_select/identity_select.php new file mode 100644 index 000000000..203776725 --- /dev/null +++ b/plugins/identity_select/identity_select.php @@ -0,0 +1,68 @@ +<?php + +/** + * Identity selection based on additional message headers. + * + * On reply to a message user identity selection is based on + * content of standard headers i.e. From, To, Cc and Return-Path. + * Here you can add header(s) set by your SMTP server (e.g. + * Delivered-To, Envelope-To, X-Envelope-To, X-RCPT-TO) to make + * identity selection more accurate. + * + * Enable the plugin in config.inc.php and add your desired headers: + * $rcmail_config['identity_select_headers'] = array('Delivered-To'); + * + * @version @package_version@ + * @author Aleksander Machniak <alec@alec.pl> + * @license GNU GPLv3+ + */ +class identity_select extends rcube_plugin +{ + public $task = 'mail'; + + + function init() + { + $this->add_hook('identity_select', array($this, 'select')); + $this->add_hook('storage_init', array($this, 'storage_init')); + } + + /** + * Adds additional headers to supported headers list + */ + function storage_init($p) + { + $rcmail = rcmail::get_instance(); + + if ($add_headers = (array)$rcmail->config->get('identity_select_headers', array())) { + $p['fetch_headers'] = trim($p['fetch_headers'] . ' ' . strtoupper(join(' ', $add_headers))); + } + + return $p; + } + + /** + * Identity selection + */ + function select($p) + { + if ($p['selected'] !== null) { + return $p; + } + + $rcmail = rcmail::get_instance(); + + foreach ((array)$rcmail->config->get('identity_select_headers', array()) as $header) { + if ($header = $p['message']->headers->get($header, false)) { + foreach ($p['identities'] as $idx => $ident) { + if (in_array($ident['email_ascii'], (array)$header)) { + $p['selected'] = $idx; + break 2; + } + } + } + } + + return $p; + } +} diff --git a/plugins/identity_select/package.xml b/plugins/identity_select/package.xml new file mode 100644 index 000000000..425c28830 --- /dev/null +++ b/plugins/identity_select/package.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8"?> +<package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.9.0" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 + http://pear.php.net/dtd/tasks-1.0.xsd + http://pear.php.net/dtd/package-2.0 + http://pear.php.net/dtd/package-2.0.xsd"> + <name>identity_select</name> + <channel>pear.roundcube.net</channel> + <summary>Extended identity selection</summary> + <description> + On reply to a message user identity selection is based on + content of standard headers like From, To, Cc and Return-Path. + Here you can add header(s) set by your SMTP server (e.g. + Delivered-To, Envelope-To, X-Envelope-To, X-RCPT-TO) to make + identity selection more accurate. + </description> + <lead> + <name>Aleksander Machniak</name> + <user>alec</user> + <email>alec@alec.pl</email> + <active>yes</active> + </lead> + <date>2013-08-04</date> + <version> + <release>1.0</release> + <api>1.0</api> + </version> + <stability> + <release>stable</release> + <api>stable</api> + </stability> + <license uri="http://www.gnu.org/licenses/gpl.html">GNU GPLv3+</license> + <notes>-</notes> + <contents> + <dir baseinstalldir="/" name="/"> + <file name="identity_select.php" role="php"> + <tasks:replace from="@name@" to="name" type="package-info"/> + <tasks:replace from="@package_version@" to="version" type="package-info"/> + </file> + </dir> + <!-- / --> + </contents> + <dependencies> + <required> + <php> + <min>5.2.1</min> + </php> + <pearinstaller> + <min>1.7.0</min> + </pearinstaller> + </required> + </dependencies> + <phprelease/> +</package> diff --git a/plugins/identity_select/tests/IdentitySelect.php b/plugins/identity_select/tests/IdentitySelect.php new file mode 100644 index 000000000..3d7269711 --- /dev/null +++ b/plugins/identity_select/tests/IdentitySelect.php @@ -0,0 +1,22 @@ +<?php + +class IdentitySelect_Plugin extends PHPUnit_Framework_TestCase +{ + + function setUp() + { + include_once dirname(__FILE__) . '/../identity_select.php'; + } + + /** + * Plugin object construction test + */ + function test_constructor() + { + $rcube = rcube::get_instance(); + $plugin = new identity_select($rcube->api); + + $this->assertInstanceOf('identity_select', $plugin); + $this->assertInstanceOf('rcube_plugin', $plugin); + } +} diff --git a/plugins/managesieve/Changelog b/plugins/managesieve/Changelog index daee91a70..60b2f1831 100644 --- a/plugins/managesieve/Changelog +++ b/plugins/managesieve/Changelog @@ -4,6 +4,7 @@ - Support date, currendate and index tests - RFC5260 (#1488120) - Split plugin file into two files - Fix handling of &, <, > characters in scripts/filter names (#1489208) +- Support 'keep' action (#1489226) * version 6.2 [2013-02-17] ----------------------------------------------------------- diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php b/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php index e92ba04d6..bbbfa9d91 100644 --- a/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php +++ b/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php @@ -1550,6 +1550,7 @@ class rcube_sieve_engine if (in_array('enotify', $this->exts) || in_array('notify', $this->exts)) { $select_action->add(rcube::Q($this->plugin->gettext('notify')), 'notify'); } + $select_action->add(rcube::Q($this->plugin->gettext('messagekeep')), 'keep'); $select_action->add(rcube::Q($this->plugin->gettext('rulestop')), 'stop'); $select_type = $action['type']; diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php b/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php index a614c3b15..371b45d84 100644 --- a/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php +++ b/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php @@ -260,7 +260,8 @@ class rcube_sieve_script $this->add_index($test, $tests[$i], $exts); } - if (!empty($test['part'])) { + // :all address-part is optional, skip it + if (!empty($test['part']) && $test['part'] != 'all') { $tests[$i] .= ' :' . $test['part']; if ($test['part'] == 'user' || $test['part'] == 'detail') { array_push($exts, 'subaddress'); diff --git a/plugins/managesieve/localization/en_US.inc b/plugins/managesieve/localization/en_US.inc index 72bbf9d41..a37ea7db9 100644 --- a/plugins/managesieve/localization/en_US.inc +++ b/plugins/managesieve/localization/en_US.inc @@ -49,6 +49,7 @@ $labels['messagesendcopy'] = 'Send message copy to'; $labels['messagereply'] = 'Reply with message'; $labels['messagedelete'] = 'Delete message'; $labels['messagediscard'] = 'Discard with message'; +$labels['messagekeep'] = 'Keep message in Inbox'; $labels['messagesrules'] = 'For incoming mail:'; $labels['messagesactions'] = '...execute the following actions:'; $labels['add'] = 'Add'; diff --git a/plugins/managesieve/tests/src/parser.out b/plugins/managesieve/tests/src/parser.out index 366515b06..cb0bad5e7 100644 --- a/plugins/managesieve/tests/src/parser.out +++ b/plugins/managesieve/tests/src/parser.out @@ -39,7 +39,7 @@ if true } fileinto "Test"; # rule:[address test] -if address :all :is "From" "nagios@domain.tld" +if address :is "From" "nagios@domain.tld" { fileinto "domain.tld"; stop; diff --git a/plugins/managesieve/tests/src/parser_enotify_b b/plugins/managesieve/tests/src/parser_enotify_b index 9a17eaf0c..a3011bac2 100644 --- a/plugins/managesieve/tests/src/parser_enotify_b +++ b/plugins/managesieve/tests/src/parser_enotify_b @@ -1,6 +1,6 @@ require ["enotify","envelope","variables"]; # rule:[from] -if envelope :all :matches "from" "*" +if envelope :matches "from" "*" { set "env_from" " [really: ${1}]"; } @@ -10,7 +10,7 @@ if header :matches "Subject" "*" set "subject" "${1}"; } # rule:[from notify] -if address :all :matches "from" "*" +if address :matches "from" "*" { set "from_addr" "${1}"; notify :message "${from_addr}${env_from}: ${subject}" "mailto:alm@example.com"; diff --git a/plugins/managesieve/tests/src/parser_index b/plugins/managesieve/tests/src/parser_index index 78aba9a55..ca9f86d56 100644 --- a/plugins/managesieve/tests/src/parser_index +++ b/plugins/managesieve/tests/src/parser_index @@ -12,7 +12,7 @@ if header :index 2 :contains ["From","To"] "test@domain.tld" stop; } # rule:[index-address] -if address :index 1 :all :is "From" "nagios@domain.tld" +if address :index 1 :is "From" "nagios@domain.tld" { fileinto "domain.tld"; stop; diff --git a/plugins/managesieve/tests/src/parser_notify_b b/plugins/managesieve/tests/src/parser_notify_b index 9a3ca803c..ab90ed48c 100644 --- a/plugins/managesieve/tests/src/parser_notify_b +++ b/plugins/managesieve/tests/src/parser_notify_b @@ -1,6 +1,6 @@ require ["envelope","notify","variables"]; # rule:[from] -if envelope :all :matches "from" "*" +if envelope :matches "from" "*" { set "env_from" " [really: ${1}]"; } @@ -10,7 +10,7 @@ if header :matches "Subject" "*" set "subject" "${1}"; } # rule:[from notify] -if address :all :matches "from" "*" +if address :matches "from" "*" { set "from_addr" "${1}"; notify :message "${from_addr}${env_from}: ${subject}" :method "sms:1234567890"; diff --git a/program/include/rcmail_output_html.php b/program/include/rcmail_output_html.php index 998779509..a2ec29ca3 100644 --- a/program/include/rcmail_output_html.php +++ b/program/include/rcmail_output_html.php @@ -1531,9 +1531,9 @@ class rcmail_output_html extends rcmail_output $input_action = new html_hiddenfield(array('name' => '_action', 'value' => 'login')); $input_tzone = new html_hiddenfield(array('name' => '_timezone', 'id' => 'rcmlogintz', 'value' => '_default_')); $input_url = new html_hiddenfield(array('name' => '_url', 'id' => 'rcmloginurl', 'value' => $url)); - $input_user = new html_inputfield(array('name' => '_user', 'id' => 'rcmloginuser') + $input_user = new html_inputfield(array('name' => '_user', 'id' => 'rcmloginuser', 'required' => 'required') + $attrib + $user_attrib); - $input_pass = new html_passwordfield(array('name' => '_pass', 'id' => 'rcmloginpwd') + $input_pass = new html_passwordfield(array('name' => '_pass', 'id' => 'rcmloginpwd', 'required' => 'required') + $attrib + $pass_attrib); $input_host = null; diff --git a/program/lib/Roundcube/html.php b/program/lib/Roundcube/html.php index 3e6e47a56..a36711281 100644 --- a/program/lib/Roundcube/html.php +++ b/program/lib/Roundcube/html.php @@ -358,7 +358,7 @@ class html_inputfield extends html protected $tagname = 'input'; protected $type = 'text'; protected $allowed = array( - 'type','name','value','size','tabindex','autocapitalize', + 'type','name','value','size','tabindex','autocapitalize','required', 'autocomplete','checked','onchange','onclick','disabled','readonly', 'spellcheck','results','maxlength','src','multiple','accept', 'placeholder','autofocus', diff --git a/program/lib/Roundcube/rcube.php b/program/lib/Roundcube/rcube.php index ce97cd0a5..e0f889a87 100644 --- a/program/lib/Roundcube/rcube.php +++ b/program/lib/Roundcube/rcube.php @@ -498,11 +498,11 @@ class rcube if ($tmp && ($dir = opendir($tmp))) { while (($fname = readdir($dir)) !== false) { - if ($fname{0} == '.') { + if ($fname[0] == '.') { continue; } - if (filemtime($tmp.'/'.$fname) < $expire) { + if (@filemtime($tmp.'/'.$fname) < $expire) { @unlink($tmp.'/'.$fname); } } diff --git a/program/lib/Roundcube/rcube_charset.php b/program/lib/Roundcube/rcube_charset.php index a7f26a3f4..19dbf6cbc 100644 --- a/program/lib/Roundcube/rcube_charset.php +++ b/program/lib/Roundcube/rcube_charset.php @@ -674,23 +674,27 @@ class rcube_charset // Prioritize charsets according to current language (#1485669) switch ($language) { - case 'ja_JP': // for Japanese + case 'ja_JP': $prio = array('ISO-2022-JP', 'JIS', 'UTF-8', 'EUC-JP', 'eucJP-win', 'SJIS', 'SJIS-win'); break; - case 'zh_CN': // for Chinese (Simplified) - case 'zh_TW': // for Chinese (Traditional) + case 'zh_CN': + case 'zh_TW': $prio = array('UTF-8', 'BIG-5', 'GB2312', 'EUC-TW'); break; - case 'ko_KR': // for Korean + case 'ko_KR': $prio = array('UTF-8', 'EUC-KR', 'ISO-2022-KR'); break; - case 'ru_RU': // for Russian + case 'ru_RU': $prio = array('UTF-8', 'WINDOWS-1251', 'KOI8-R'); break; + case 'tr_TR': + $prio = array('UTF-8', 'ISO-8859-9', 'WINDOWS-1254'); + break; + default: $prio = array('UTF-8', 'SJIS', 'GB2312', 'ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4', diff --git a/program/lib/Roundcube/rcube_image.php b/program/lib/Roundcube/rcube_image.php index 09bb4e81b..c9a555300 100644 --- a/program/lib/Roundcube/rcube_image.php +++ b/program/lib/Roundcube/rcube_image.php @@ -124,7 +124,7 @@ class rcube_image $p['-opts'] = array('-resize' => $p['size'].'>'); if (in_array($type, explode(',', $p['types']))) { // Valid type? - $result = rcube::exec($convert . ' 2>&1 -flatten -auto-orient -colorspace RGB -quality {quality} {-opts} {intype}:{in} {type}:{out}', $p); + $result = rcube::exec($convert . ' 2>&1 -flatten -auto-orient -colorspace sRGB -strip -quality {quality} {-opts} {intype}:{in} {type}:{out}', $p); } if ($result === '') { @@ -230,7 +230,7 @@ class rcube_image $p['out'] = $filename; $p['type'] = self::$extensions[$type]; - $result = rcube::exec($convert . ' 2>&1 -colorspace RGB -quality 75 {in} {type}:{out}', $p); + $result = rcube::exec($convert . ' 2>&1 -colorspace sRGB -strip -quality 75 {in} {type}:{out}', $p); if ($result === '') { @chmod($filename, 0600); diff --git a/program/lib/Roundcube/rcube_imap.php b/program/lib/Roundcube/rcube_imap.php index b60aefc5f..c5346c8aa 100644 --- a/program/lib/Roundcube/rcube_imap.php +++ b/program/lib/Roundcube/rcube_imap.php @@ -2092,17 +2092,18 @@ class rcube_imap extends rcube_storage /** * Fetch message body of a specific message from the server * - * @param int $uid Message UID - * @param string $part Part number - * @param rcube_message_part $o_part Part object created by get_structure() - * @param mixed $print True to print part, ressource to write part contents in - * @param resource $fp File pointer to save the message part - * @param boolean $skip_charset_conv Disables charset conversion - * @param int $max_bytes Only read this number of bytes + * @param int Message UID + * @param string Part number + * @param rcube_message_part Part object created by get_structure() + * @param mixed True to print part, resource to write part contents in + * @param resource File pointer to save the message part + * @param boolean Disables charset conversion + * @param int Only read this number of bytes + * @param boolean Enables formatting of text/* parts bodies * * @return string Message/part body if not printed */ - public function get_message_part($uid, $part=1, $o_part=NULL, $print=NULL, $fp=NULL, $skip_charset_conv=false, $max_bytes=0) + public function get_message_part($uid, $part=1, $o_part=NULL, $print=NULL, $fp=NULL, $skip_charset_conv=false, $max_bytes=0, $formatted=true) { if (!$this->check_connection()) { return null; @@ -2121,8 +2122,9 @@ class rcube_imap extends rcube_storage } if ($o_part && $o_part->size) { + $formatted = $formatted && $o_part->ctype_primary == 'text'; $body = $this->conn->handlePartBody($this->folder, $uid, true, - $part ? $part : 'TEXT', $o_part->encoding, $print, $fp, $o_part->ctype_primary == 'text', $max_bytes); + $part ? $part : 'TEXT', $o_part->encoding, $print, $fp, $formatted, $max_bytes); } if ($fp || $print) { diff --git a/program/lib/Roundcube/rcube_imap_generic.php b/program/lib/Roundcube/rcube_imap_generic.php index 5787f36b6..e1193749b 100644 --- a/program/lib/Roundcube/rcube_imap_generic.php +++ b/program/lib/Roundcube/rcube_imap_generic.php @@ -2490,7 +2490,7 @@ class rcube_imap_generic } if ($binary) { - // WARNING: Use $formatting argument with care, this may break binary data stream + // WARNING: Use $formatted argument with care, this may break binary data stream $mode = -1; } diff --git a/program/lib/Roundcube/rcube_message.php b/program/lib/Roundcube/rcube_message.php index 797ca185e..0d33ea44d 100644 --- a/program/lib/Roundcube/rcube_message.php +++ b/program/lib/Roundcube/rcube_message.php @@ -168,10 +168,11 @@ class rcube_message * @param resource $fp File pointer to save the message part * @param boolean $skip_charset_conv Disables charset conversion * @param int $max_bytes Only read this number of bytes + * @param boolean $formatted Enables formatting of text/* parts bodies * * @return string Part content */ - public function get_part_content($mime_id, $fp = null, $skip_charset_conv = false, $max_bytes = 0) + public function get_part_content($mime_id, $fp = null, $skip_charset_conv = false, $max_bytes = 0, $formatted = true) { if ($part = $this->mime_parts[$mime_id]) { // stored in message structure (winmail/inline-uuencode) @@ -185,7 +186,8 @@ class rcube_message // get from IMAP $this->storage->set_folder($this->folder); - return $this->storage->get_message_part($this->uid, $mime_id, $part, NULL, $fp, $skip_charset_conv, $max_bytes); + return $this->storage->get_message_part($this->uid, $mime_id, $part, + NULL, $fp, $skip_charset_conv, $max_bytes, $formatted); } } diff --git a/program/lib/Roundcube/rcube_storage.php b/program/lib/Roundcube/rcube_storage.php index 4b336f210..de8334551 100644 --- a/program/lib/Roundcube/rcube_storage.php +++ b/program/lib/Roundcube/rcube_storage.php @@ -61,8 +61,6 @@ abstract class rcube_storage 'MAIL-FOLLOWUP-TO', 'MAIL-REPLY-TO', 'RETURN-PATH', - 'DELIVERED-TO', - 'ENVELOPE-TO', ); const UNKNOWN = 0; diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index 2330bc040..af84619f2 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -207,7 +207,10 @@ if (!empty($msg_uid) && empty($COMPOSE['as_attachment'])) if (!empty($MESSAGE->headers->charset)) $RCMAIL->storage->set_charset($MESSAGE->headers->charset); - if ($compose_mode == RCUBE_COMPOSE_REPLY) { + if (!$MESSAGE->headers) { + // error + } + else if ($compose_mode == RCUBE_COMPOSE_REPLY) { $COMPOSE['reply_uid'] = $msg_uid; $COMPOSE['reply_msgid'] = $MESSAGE->headers->messageID; $COMPOSE['references'] = trim($MESSAGE->headers->references . " " . $MESSAGE->headers->messageID); @@ -1000,10 +1003,19 @@ function rcmail_create_draft_body($body, $bodyIsHtml) && count($MESSAGE->mime_parts) > 0) { $cid_map = rcmail_write_compose_attachments($MESSAGE, $bodyIsHtml); + } + + // clean up HTML tags - XSS prevention (#1489251) + if ($bodyIsHtml) { + $body = rcmail_wash_html($body, array('safe' => 1), $cid_map); + + // remove comments (produced by washtml) + $body = preg_replace('/<!--[^>]+-->/', '', $body); // replace cid with href in inline images links - if ($cid_map) + if (!empty($cid_map)) { $body = str_replace(array_keys($cid_map), array_values($cid_map), $body); + } } return $body; diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index a41e3ffeb..e14d25ee3 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -1783,8 +1783,14 @@ function rcmail_identity_select($MESSAGE, $identities = null, $compose_mode = 'r // Try Return-Path if ($from_idx === null && ($return_path = $MESSAGE->headers->others['return-path'])) { foreach ($identities as $idx => $ident) { + // Return-Path header contains an email address, but on some mailing list + // it can be e.g. <pear-dev-return-55250-local=domain.tld@lists.php.net> + // where local@domain.tld is the address we're looking for (#1489241) + $ident1 = $ident['email_ascii']; + $ident2 = str_replace('@', '=', $ident1); + foreach ((array)$return_path as $path) { - if (stripos($path, $ident['email_ascii']) !== false) { + if (stripos($path, $ident1) !== false || stripos($path, $ident2)) { $from_idx = $idx; break 2; } @@ -1792,27 +1798,13 @@ function rcmail_identity_select($MESSAGE, $identities = null, $compose_mode = 'r } } - // Fallback using Delivered-To - if ($from_idx === null && ($delivered_to = $MESSAGE->headers->others['delivered-to'])) { - foreach ($identities as $idx => $ident) { - if (in_array($ident['email_ascii'], (array)$delivered_to)) { - $from_idx = $idx; - break; - } - } - } + // See identity_select plugin for example usage of this hook + $plugin = rcmail::get_instance()->plugins->exec_hook('identity_select', + array('message' => $MESSAGE, 'identities' => $identities, 'selected' => $from_idx)); - // Fallback using Envelope-To - if ($from_idx === null && ($envelope_to = $MESSAGE->headers->others['envelope-to'])) { - foreach ($identities as $idx => $ident) { - if (in_array($ident['email_ascii'], (array)$envelope_to)) { - $from_idx = $idx; - break; - } - } - } + $selected = $plugin['selected']; - return $identities[$from_idx !== null ? $from_idx : $default_identity]; + return $identities[$selected !== null ? $selected : $default_identity]; } // Fixes some content-type names diff --git a/program/steps/mail/get.inc b/program/steps/mail/get.inc index a27e788a3..e0c4e2911 100644 --- a/program/steps/mail/get.inc +++ b/program/steps/mail/get.inc @@ -134,7 +134,7 @@ else if (strlen($part_id)) { $valid = $file_extension && in_array($file_extension, (array)$extensions) || !empty($_REQUEST['_mimeclass']); // 2. detect the real mimetype of the attachment part and compare it with the stated mimetype and filename extension - if ($valid || !$file_extension || $mimetype == 'application/octet-stream' || $mimetype == 'text/plain') { + if ($valid || !$file_extension || $mimetype == 'application/octet-stream' || stripos($mimetype, 'text/') === 0) { if ($part->body) // part body is already loaded $body = $part->body; else if ($part->size && $part->size < 1024*1024) // load the entire part if it's small enough @@ -189,8 +189,8 @@ else if (strlen($part_id)) { rcube_label(array( 'name' => 'attachmentvalidationerror', 'vars' => array( - 'expected' => $mimetype . ($file_extension ? "(.$file_extension)" : ''), - 'detected' => $real_mimetype . ($extensions[0] ? "(.$extensions[0])" : ''), + 'expected' => $mimetype . ($file_extension ? " (.$file_extension)" : ''), + 'detected' => $real_mimetype . ($extensions[0] ? " (.$extensions[0])" : ''), ) )) . html::p(array('class' => 'rcmail-inline-buttons'), @@ -233,7 +233,6 @@ else if (strlen($part_id)) { header("Content-Transfer-Encoding: binary"); } - // deliver part content if ($ctype_primary == 'text' && $ctype_secondary == 'html' && empty($plugin['download'])) { // Check if we have enough memory to handle the message in it @@ -358,7 +357,8 @@ else if (strlen($part_id)) { header("Content-Length: $size"); } - $sent = $RCMAIL->storage->get_message_part($MESSAGE->uid, $part->mime_id, $part, true); + // 8th argument disables re-formatting of text/* parts (#1489267) + $sent = $RCMAIL->storage->get_message_part($MESSAGE->uid, $part->mime_id, $part, true, null, false, 0, false); } } diff --git a/program/steps/settings/edit_identity.inc b/program/steps/settings/edit_identity.inc index d70a7aef7..edd4ba60d 100644 --- a/program/steps/settings/edit_identity.inc +++ b/program/steps/settings/edit_identity.inc @@ -77,7 +77,7 @@ function rcube_identity_form($attrib) 'signature' => array( 'name' => rcube_label('signature'), 'content' => array( - 'signature' => array('type' => 'textarea', 'size' => $t_cols, 'rows' => $t_rows, + 'signature' => array('type' => 'textarea', 'size' => $t_cols, 'rows' => $t_rows, 'spellcheck' => true), 'html_signature' => array('type' => 'checkbox', 'label' => rcube_label('htmlsignature'), 'onclick' => 'return rcmail_toggle_editor(this, \'rcmfd_signature\');'), @@ -138,6 +138,7 @@ function rcube_identity_form($attrib) $label = !empty($colprop['label']) ? $colprop['label'] : rcube_label(str_replace('-', '', $col)); + $value = !empty($colprop['value']) ? $colprop['value'] : rcmail_get_edit_field($col, $IDENTITY_RECORD[$col], $colprop, $colprop['type']); diff --git a/program/steps/settings/func.inc b/program/steps/settings/func.inc index dbc9b3ce2..f6ea79ec6 100644 --- a/program/steps/settings/func.inc +++ b/program/steps/settings/func.inc @@ -546,7 +546,7 @@ function rcmail_user_prefs($current = null) $blocks['main']['options']['default_charset'] = array( 'title' => html::label($field_id, Q(rcube_label('defaultcharset'))), 'content' => $RCMAIL->output->charset_selector(array( - 'name' => '_default_charset', 'selected' => $config['default_charset'] + 'id' => $field_id, 'name' => '_default_charset', 'selected' => $config['default_charset'] ))); } diff --git a/program/steps/settings/save_identity.inc b/program/steps/settings/save_identity.inc index 34d8be268..d3b132f8b 100644 --- a/program/steps/settings/save_identity.inc +++ b/program/steps/settings/save_identity.inc @@ -76,6 +76,15 @@ foreach ($email_checks as $email) { } } +// XSS protection in HTML signature (#1489251) +if (!empty($save_data['signature']) && !empty($save_data['html_signature'])) { + $save_data['signature'] = rcmail_wash_html($save_data['signature']); + + // clear POST data of signature, we want to use safe content + // when the form is displayed again + unset($_POST['_signature']); +} + // update an existing contact if ($_POST['_iid']) { $iid = get_input_value('_iid', RCUBE_INPUT_POST); @@ -167,3 +176,40 @@ if (!empty($_REQUEST['_framed'])) { } else rcmail_overwrite_action('identities'); + + +/** + * Sanity checks/cleanups on HTML body of signature + */ +function rcmail_wash_html($html) +{ + // Add header with charset spec., washtml cannot work without that + $html = '<html><head>' + . '<meta http-equiv="Content-Type" content="text/html; charset='.RCMAIL_CHARSET.'" />' + . '</head><body>' . $html . '</body></html>'; + + // clean HTML with washhtml by Frederic Motte + $wash_opts = array( + 'show_washed' => false, + 'allow_remote' => 1, + 'charset' => RCMAIL_CHARSET, + 'html_elements' => array('body', 'link'), + 'html_attribs' => array('rel', 'type'), + ); + + // initialize HTML washer + $washer = new rcube_washtml($wash_opts); + + //$washer->add_callback('form', 'rcmail_washtml_callback'); + //$washer->add_callback('style', 'rcmail_washtml_callback'); + + // Remove non-UTF8 characters (#1487813) + $html = rc_utf8_clean($html); + + $html = $washer->wash($html); + + // remove unwanted comments and tags (produced by washtml) + $html = preg_replace(array('/<!--[^>]+-->/', '/<\/?body>/'), '', $html); + + return $html; +} diff --git a/skins/larry/includes/header.html b/skins/larry/includes/header.html index c8b3b26f6..5a934d89b 100644 --- a/skins/larry/includes/header.html +++ b/skins/larry/includes/header.html @@ -29,7 +29,7 @@ <roundcube:button command="logout" label="logout" class="button-logout" classSel="button-logout" innerClass="button-inner" /> <span class="minmodetoggle"></span> </div> - <roundcube:object name="logo" src="/images/roundcube_logo.png" id="toplogo" border="0" alt="Logo" onclick="rcmail.command('switch-task','mail');return false;" /> + <roundcube:object name="logo" src="/images/roundcube_logo.png" id="toplogo" alt="Logo" onclick="rcmail.command('switch-task','mail');return false;" /> </div> <roundcube:endif /> diff --git a/skins/larry/styles.css b/skins/larry/styles.css index ec4f3047c..288b010ed 100644 --- a/skins/larry/styles.css +++ b/skins/larry/styles.css @@ -50,6 +50,8 @@ textarea { input[type="text"]:focus, input[type="password"]:focus, +input[type="text"]:required, +input[type="password"]:required, input.button:focus, textarea:focus { border-color: #4787b1; @@ -611,6 +613,7 @@ a.iconlink.upload { #toplogo { padding-top: 2px; cursor: pointer; + border: none; } .topleft { @@ -1578,6 +1581,7 @@ ul.proplist li { #login-form #logo { margin-bottom: 20px; + border: none; } #login-form #message { diff --git a/skins/larry/templates/login.html b/skins/larry/templates/login.html index 6e56ee2fd..13e919ad3 100644 --- a/skins/larry/templates/login.html +++ b/skins/larry/templates/login.html @@ -9,7 +9,7 @@ <div id="login-form"> <div class="box-inner"> -<roundcube:object name="logo" src="/images/roundcube_logo.png" id="logo" border="0" /> +<roundcube:object name="logo" src="/images/roundcube_logo.png" id="logo" /> <roundcube:form name="form" method="post"> <roundcube:object name="loginform" form="form" size="40" /> diff --git a/skins/larry/templates/mail.html b/skins/larry/templates/mail.html index 6015054d3..7ef12bfdb 100644 --- a/skins/larry/templates/mail.html +++ b/skins/larry/templates/mail.html @@ -124,7 +124,7 @@ </div><!-- end mainscreen --> -<div><!-- end minwidth --> +</div><!-- end minwidth --> <div id="searchmenu" class="popupmenu"> <ul class="toolbarmenu"> |