From 117c150b2f41710749dc62779e6b01a37936b99c Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Mon, 2 Oct 2017 17:49:14 +0200 Subject: [PATCH 01/16] Fix bug where mail search could return empty result on servers without SORT capability (#5973) --- CHANGELOG | 1 + program/lib/Roundcube/rcube_imap.php | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 71e87b155..c341b246e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -51,6 +51,7 @@ CHANGELOG Roundcube Webmail - Enigma: Fix decryption of messages encoded with non-ascii charset (#5962) - Fix missing cursor in HTML editor on mail reply (#5969) - Fix (again) bug where image data URIs in css style were treated as evil/remote in mail preview (#5580) +- Fix bug where mail search could return empty result on servers without SORT capability (#5973) RELEASE 1.3.1 ------------- diff --git a/program/lib/Roundcube/rcube_imap.php b/program/lib/Roundcube/rcube_imap.php index cf84f84c5..accdd3965 100644 --- a/program/lib/Roundcube/rcube_imap.php +++ b/program/lib/Roundcube/rcube_imap.php @@ -1390,8 +1390,7 @@ class rcube_imap extends rcube_storage $index = new rcube_result_index($folder, '* ESEARCH ALL ' . $search); } else { - $index = $this->index_direct($folder, $this->search_charset, - $this->sort_field, $this->search_set); + $index = $this->index_direct($folder, $this->sort_field, $this->sort_order, $this->search_set); } } From 5d08580b86bc26d4db2009aad132c3ab7b930a78 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 3 Oct 2017 08:30:52 +0200 Subject: [PATCH 02/16] Enigma: Add options to set PGP cipher/digest algorithms (#5645) --- CHANGELOG | 1 + INSTALL | 2 +- composer.json-dist | 2 +- plugins/enigma/composer.json | 2 +- plugins/enigma/config.inc.php.dist | 8 ++++++++ plugins/enigma/lib/enigma_driver_gnupg.php | 3 +++ 6 files changed, 15 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index c341b246e..766af52c7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -5,6 +5,7 @@ CHANGELOG Roundcube Webmail - Changed defaults for smtp_user (%u), smtp_pass (%p) and smtp_port (587) - Composer: Fix certificate validation errors by using packagist only (#5148) - Enigma: Add button to send mail unencrypted if no key was found (#5913) +- Enigma: Add options to set PGP cipher/digest algorithms (#5645) - Add --get and --extract arguments and CACHEDIR env-variable support to install-jsdeps.sh (#5882) - Update to jquery-minicolors 2.2.6 - Support _filter and _scope as GET arguments for opening mail UI (#5825) diff --git a/INSTALL b/INSTALL index 54d4eee9a..2d8aee18f 100644 --- a/INSTALL +++ b/INSTALL @@ -24,7 +24,7 @@ REQUIREMENTS - Net_IDNA2 0.1.1 or newer - Auth_SASL 1.0.6 or newer - Net_Sieve 1.3.2 or newer (for managesieve plugin) - - Crypt_GPG 1.6.0 or newer (for enigma plugin) + - Crypt_GPG 1.6.2 or newer (for enigma plugin) - Endroid/QrCode 1.6.0 or newer (https://github.com/endroid/QrCode) * php.ini options (see .htaccess file): - error_reporting E_ALL & ~E_NOTICE & ~E_STRICT diff --git a/composer.json-dist b/composer.json-dist index 2df69dc80..59cbefc5f 100644 --- a/composer.json-dist +++ b/composer.json-dist @@ -16,7 +16,7 @@ "pear/net_idna2": "~0.2.0", "pear/mail_mime": "~1.10.0", "pear/net_smtp": "~1.7.1", - "pear/crypt_gpg": "~1.6.0", + "pear/crypt_gpg": "~1.6.2", "pear/net_sieve": "~1.4.0", "roundcube/plugin-installer": "~0.1.6", "endroid/qrcode": "~1.6.5" diff --git a/plugins/enigma/composer.json b/plugins/enigma/composer.json index 7357c01d6..348b39983 100644 --- a/plugins/enigma/composer.json +++ b/plugins/enigma/composer.json @@ -20,6 +20,6 @@ "require": { "php": ">=5.3.0", "roundcube/plugin-installer": "~0.1.6", - "pear/crypt_gpg": "~1.6.0" + "pear/crypt_gpg": "~1.6.2" } } diff --git a/plugins/enigma/config.inc.php.dist b/plugins/enigma/config.inc.php.dist index aa4280f41..852c999f5 100644 --- a/plugins/enigma/config.inc.php.dist +++ b/plugins/enigma/config.inc.php.dist @@ -28,6 +28,14 @@ $config['enigma_pgp_agent'] = ''; // It's used with GnuPG >= 2.1. $config['enigma_pgp_gpgconf'] = ''; +// Name of the PGP symmetric cipher algorithm. +// Run gpg --version to see the list of supported algorithms +$config['enigma_pgp_cipher_algo'] = null; + +// Name of the PGP digest (hash) algorithm. +// Run gpg --version to see the list of supported algorithms +$config['enigma_pgp_digest_algo'] = null; + // Enables signatures verification feature. $config['enigma_signatures'] = true; diff --git a/plugins/enigma/lib/enigma_driver_gnupg.php b/plugins/enigma/lib/enigma_driver_gnupg.php index a8d84ebea..c2bf016ae 100644 --- a/plugins/enigma/lib/enigma_driver_gnupg.php +++ b/plugins/enigma/lib/enigma_driver_gnupg.php @@ -94,6 +94,9 @@ class enigma_driver_gnupg extends enigma_driver $options['gpgconf'] = $gpgconf; } + $options['cipher-algo'] = $this->rc->config->get('enigma_pgp_cipher_algo'); + $options['digest-algo'] = $this->rc->config->get('enigma_pgp_digest_algo'); + // Create Crypt_GPG object try { $this->gpg = new Crypt_GPG($options); From 5d3add78aa6e880114e686de94c61a17d92a3020 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 3 Oct 2017 08:49:17 +0200 Subject: [PATCH 03/16] Add Message-ID to the sendmail log (#5871) --- CHANGELOG | 1 + program/lib/Roundcube/rcube.php | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 766af52c7..736ce73cc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Add Message-ID to the sendmail log (#5871) - Managesieve: Add ability to disable filter sets and other actions (#5496, #5898) - Changed defaults for smtp_user (%u), smtp_pass (%p) and smtp_port (587) - Composer: Fix certificate validation errors by using packagist only (#5148) diff --git a/program/lib/Roundcube/rcube.php b/program/lib/Roundcube/rcube.php index 9330083ac..a1a4d8b94 100644 --- a/program/lib/Roundcube/rcube.php +++ b/program/lib/Roundcube/rcube.php @@ -1686,9 +1686,10 @@ class rcube $mailto = implode(',', $a_recipients); $mailto = rcube_mime::decode_address_list($mailto, null, false, null, true); - self::write_log('sendmail', sprintf("User %s [%s]; Message for %s; %s", + self::write_log('sendmail', sprintf("User %s [%s]; Message %s for %s; %s", $this->user->get_username(), rcube_utils::remote_addr(), + $headers['Message-ID'], implode(', ', $mailto), !empty($response) ? join('; ', $response) : '')); } From 22b30de5d96609fae5354cf93c76580af8bc986a Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 4 Oct 2017 09:27:23 +0200 Subject: [PATCH 04/16] Fix bug where assets_path wasn't added to some watermark frames --- CHANGELOG | 1 + program/include/rcmail_output_html.php | 11 +++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 736ce73cc..13b3b1bce 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -54,6 +54,7 @@ CHANGELOG Roundcube Webmail - Fix missing cursor in HTML editor on mail reply (#5969) - Fix (again) bug where image data URIs in css style were treated as evil/remote in mail preview (#5580) - Fix bug where mail search could return empty result on servers without SORT capability (#5973) +- Fix bug where assets_path wasn't added to some watermark frames RELEASE 1.3.1 ------------- diff --git a/program/include/rcmail_output_html.php b/program/include/rcmail_output_html.php index e5422d836..eb3d5fa79 100644 --- a/program/include/rcmail_output_html.php +++ b/program/include/rcmail_output_html.php @@ -1156,10 +1156,13 @@ EOF; // we are calling a class/method if (($handler = $this->object_handlers[$object]) && is_array($handler)) { - if ((is_object($handler[0]) && method_exists($handler[0], $handler[1])) || - (is_string($handler[0]) && class_exists($handler[0]))) - $content = call_user_func($handler, $attrib); - $external = true; + if (is_callable($handler)) { + // We assume that objects with src attribute are internal (in most + // cases this is a watermark frame). We need this to make sure assets_path + // is added to the internal assets paths + $external = empty($attrib['src']); + $content = call_user_func($handler, $attrib); + } } // execute object handler function else if (function_exists($handler)) { From f8fc01b800452b6c40f3ee77bd51699b2e5e61d0 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 6 Oct 2017 09:23:17 +0200 Subject: [PATCH 05/16] Fix so untagged COPYUID responses are also supported according to RFC6851 (#5982) --- CHANGELOG | 1 + program/lib/Roundcube/rcube_imap_generic.php | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 13b3b1bce..75d351cf8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -55,6 +55,7 @@ CHANGELOG Roundcube Webmail - Fix (again) bug where image data URIs in css style were treated as evil/remote in mail preview (#5580) - Fix bug where mail search could return empty result on servers without SORT capability (#5973) - Fix bug where assets_path wasn't added to some watermark frames +- Fix so untagged COPYUID responses are also supported according to RFC6851 (#5982) RELEASE 1.3.1 ------------- diff --git a/program/lib/Roundcube/rcube_imap_generic.php b/program/lib/Roundcube/rcube_imap_generic.php index c46f9d36d..f1c87d562 100644 --- a/program/lib/Roundcube/rcube_imap_generic.php +++ b/program/lib/Roundcube/rcube_imap_generic.php @@ -3709,9 +3709,17 @@ class rcube_imap_generic // Parse response do { $line = $this->readLine(4096); + if ($response !== null) { $response .= $line; } + + // parse untagged response for [COPYUID 1204196876 3456:3457 123:124] (RFC6851) + if ($line && $command == 'UID MOVE' && substr_compare($line, '* OK', 0, 4, true)) { + if (preg_match("/^\* OK \[COPYUID [0-9]+ ([0-9,:]+) ([0-9,:]+)\]/i", $line, $m)) { + $this->data['COPYUID'] = array($m[1], $m[2]); + } + } } while (!$this->startsWith($line, $tag . ' ', true, true)); From 403d8453c8401fd016eadf91269ad26ed2a0cc2c Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Mon, 9 Oct 2017 10:22:00 +0200 Subject: [PATCH 06/16] Fix issue caused by non-default session.cookie_lifetime setting (#5961) --- CHANGELOG | 1 + program/lib/Roundcube/rcube.php | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 75d351cf8..0546900f7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -56,6 +56,7 @@ CHANGELOG Roundcube Webmail - Fix bug where mail search could return empty result on servers without SORT capability (#5973) - Fix bug where assets_path wasn't added to some watermark frames - Fix so untagged COPYUID responses are also supported according to RFC6851 (#5982) +- Fix issue caused by non-default session.cookie_lifetime setting (#5961) RELEASE 1.3.1 ------------- diff --git a/program/lib/Roundcube/rcube.php b/program/lib/Roundcube/rcube.php index a1a4d8b94..fc5cc0837 100644 --- a/program/lib/Roundcube/rcube.php +++ b/program/lib/Roundcube/rcube.php @@ -515,6 +515,8 @@ class rcube ini_set('session.gc_maxlifetime', $lifetime * 2); } + // set session cookie lifetime so it never expires (#5961) + ini_set('session.cookie_lifetime', 0); ini_set('session.cookie_secure', $is_secure); ini_set('session.name', $sess_name ?: 'roundcube_sessid'); ini_set('session.use_cookies', 1); From 3196d656dba49e645bb1becb9d605e9286f92cad Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 12 Oct 2017 10:48:54 +0200 Subject: [PATCH 07/16] Fix css conflicts in user interface and e-mail content (#5891) ... by adding prefix to element/class identifiers Also cleaned up some code and removed global variable use. --- CHANGELOG | 1 + program/lib/Roundcube/rcube_utils.php | 51 ++++++++++++++--------- program/lib/Roundcube/rcube_washtml.php | 11 ++++- program/steps/mail/func.inc | 54 ++++++++++++++++--------- program/steps/utils/modcss.inc | 4 +- tests/Framework/Utils.php | 31 ++++++++++++++ tests/Framework/Washtml.php | 15 +++++++ tests/MailFunc.php | 11 ++--- 8 files changed, 134 insertions(+), 44 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 0546900f7..9b354a962 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -41,6 +41,7 @@ CHANGELOG Roundcube Webmail - Localized timezone selector (#4983) - Use 7bit encoding for ISO-2022-* charsets in sent mail (#5640) - Handle inline images also inside multipart/mixed messages (#5905) +- Fix css conflicts in user interface and e-mail content (#5891) - Fix duplicated signature when using Back button in Chrome (#5809) - Fix touch event issue on messages list in IE/Edge (#5781) - Fix so links over images are not removed in plain text signatures converted from HTML (#4473) diff --git a/program/lib/Roundcube/rcube_utils.php b/program/lib/Roundcube/rcube_utils.php index 679e2924c..128578012 100644 --- a/program/lib/Roundcube/rcube_utils.php +++ b/program/lib/Roundcube/rcube_utils.php @@ -373,10 +373,11 @@ class rcube_utils * @param string CSS source code * @param string Container ID to use as prefix * @param bool Allow remote content + * @param string Prefix to be added to id/class identifier * * @return string Modified CSS source */ - public static function mod_css_styles($source, $container_id, $allow_remote = false) + public static function mod_css_styles($source, $container_id, $allow_remote = false, $prefix = '') { $last_pos = 0; $replacements = new rcube_string_replacer; @@ -432,23 +433,37 @@ class rcube_utils $last_pos = $pos2 - ($length - strlen($repl)); } - // remove html comments and add #container to each tag selector. - // also replace body definition because we also stripped off the tag - $source = preg_replace( - array( - '/(^\s*<\!--)|(-->\s*$)/m', - // (?!##str) below is to not match with ##str_replacement_0## - // from rcube_string_replacer used above, this is needed for - // cases like @media { body { position: fixed; } } (#5811) - '/(^\s*|,\s*|\}\s*|\{\s*)((?!##str)[a-z0-9\._#\*][a-z0-9\.\-_]*)/im', - '/'.preg_quote($container_id, '/').'\s+body/i', - ), - array( - '', - "\\1#$container_id \\2", - $container_id, - ), - $source); + // remove html comments + $source = preg_replace('/(^\s*<\!--)|(-->\s*$)/m', '', $source); + + // add #container to each tag selector and prefix to id/class identifiers + if ($container_id || $prefix) { + // (?!##str) below is to not match with ##str_replacement_0## + // from rcube_string_replacer used above, this is needed for + // cases like @media { body { position: fixed; } } (#5811) + $regexp = '/(^\s*|,\s*|\}\s*|\{\s*)((?!##str)[a-z0-9\._#\*\[][a-z0-9\._:\(\)#=~ \[\]"\|\>\+\$\^-]*)/im'; + $callback = function($matches) use ($container_id, $prefix) { + $replace = $matches[2]; + + if ($prefix) { + $replace = str_replace(array('.', '#'), array(".$prefix", "#$prefix"), $replace); + } + + if ($container_id) { + $replace = "#$container_id " . $replace; + } + + return str_replace($matches[2], $replace, $matches[0]); + }; + + $source = preg_replace_callback($regexp, $callback, $source); + } + + // replace body definition because we also stripped off the tag + if ($container_id) { + $regexp = '/#' . preg_quote($container_id, '/') . '\s+body/i'; + $source = preg_replace($regexp, "#$container_id", $source); + } // put block contents back in $source = $replacements->resolve($source); diff --git a/program/lib/Roundcube/rcube_washtml.php b/program/lib/Roundcube/rcube_washtml.php index 0880764a0..d45a88087 100644 --- a/program/lib/Roundcube/rcube_washtml.php +++ b/program/lib/Roundcube/rcube_washtml.php @@ -132,9 +132,9 @@ class rcube_washtml 'bordercolordark', 'face', 'marginwidth', 'marginheight', 'axis', 'border', 'abbr', 'char', 'charoff', 'clear', 'compact', 'coords', 'vspace', 'hspace', 'cellborder', 'size', 'lang', 'dir', 'usemap', 'shape', 'media', - 'background', 'src', 'poster', 'href', + 'background', 'src', 'poster', 'href', 'headers', // attributes of form elements - 'type', 'rows', 'cols', 'disabled', 'readonly', 'checked', 'multiple', 'value', + 'type', 'rows', 'cols', 'disabled', 'readonly', 'checked', 'multiple', 'value', 'for', // SVG 'accent-height', 'accumulate', 'additive', 'alignment-baseline', 'alphabetic', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseprofile', @@ -203,6 +203,9 @@ class rcube_washtml /* Allowed HTML attributes */ private $_html_attribs = array(); + /* A prefix to be added to id/class/for attribute values */ + private $_css_prefix; + /* Max nesting level */ private $max_nesting_level; @@ -218,6 +221,7 @@ class rcube_washtml $this->_html_attribs = array_flip((array)$p['html_attribs']) + array_flip(self::$html_attribs); $this->_ignore_elements = array_flip((array)$p['ignore_elements']) + array_flip(self::$ignore_elements); $this->_void_elements = array_flip((array)$p['void_elements']) + array_flip(self::$void_elements); + $this->_css_prefix = is_string($p['css_prefix']) && strlen($p['css_prefix']) ? $p['css_prefix'] : null; unset($p['html_elements'], $p['html_attribs'], $p['ignore_elements'], $p['void_elements']); @@ -338,6 +342,9 @@ class rcube_washtml $out = $value; } } + else if ($this->_css_prefix !== null && in_array($key, array('id', 'class', 'for'))) { + $out = preg_replace('/(\S+)/', $this->_css_prefix . '\1', $value); + } else if ($key) { $out = $value; } diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index eb6afd327..5e302c97b 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -875,6 +875,8 @@ function rcmail_wash_html($html, $p, $cid_replaces = array()) 'charset' => RCUBE_CHARSET, 'cid_map' => $cid_replaces, 'html_elements' => array('body'), + 'css_prefix' => $p['css_prefix'], + 'container_id' => $p['container_id'], ); if (!$p['inline_html']) { @@ -886,10 +888,12 @@ function rcmail_wash_html($html, $p, $cid_replaces = array()) } // overwrite washer options with options from plugins - if (isset($p['html_elements'])) + if (isset($p['html_elements'])) { $wash_opts['html_elements'] = $p['html_elements']; - if (isset($p['html_attribs'])) + } + if (isset($p['html_attribs'])) { $wash_opts['html_attribs'] = $p['html_attribs']; + } // initialize HTML washer $washer = new rcube_washtml($wash_opts); @@ -908,8 +912,9 @@ function rcmail_wash_html($html, $p, $cid_replaces = array()) $washer->add_callback('a', 'rcmail_washtml_link_callback'); $washer->add_callback('area', 'rcmail_washtml_link_callback'); - if ($p['safe']) + if ($p['safe']) { $washer->add_callback('link', 'rcmail_washtml_link_callback'); + } } // Remove non-UTF8 characters (#1487813) @@ -1314,11 +1319,17 @@ function rcmail_message_body($attrib) $container_id = $container_class . (++$part_no); $container_attrib = array('class' => $container_class, 'id' => $container_id); - // Assign container ID to a global variable for use in rcmail_washtml_link_callback() - $GLOBALS['rcmail_html_container_id'] = $container_id; + $body_args = array( + 'safe' => $safe_mode, + 'plain' => !$RCMAIL->config->get('prefer_html'), + 'css_prefix' => 'v' . $part_no, + 'body_class' => 'rcmBody', + 'container_id' => $container_id, + 'container_attrib' => $container_attrib, + ); // Parse the part content for display - $body = rcmail_print_body($body, $part, array('safe' => $safe_mode, 'plain' => !$RCMAIL->config->get('prefer_html'))); + $body = rcmail_print_body($body, $part, $body_args); // check if the message body is PGP encrypted if (strpos($body, '-----BEGIN PGP MESSAGE-----') !== false) { @@ -1326,7 +1337,7 @@ function rcmail_message_body($attrib) } if ($part->ctype_secondary == 'html') { - $body = rcmail_html4inline($body, $container_id, 'rcmBody', $container_attrib, $safe_mode); + $body = rcmail_html4inline($body, $body_args); } $out .= html::div($container_attrib, $plugin['prefix'] . $body); @@ -1469,22 +1480,22 @@ function rcmail_part_image_type($part) /** * Modify a HTML message that it can be displayed inside a HTML page */ -function rcmail_html4inline($body, $container_id, $body_class='', &$attributes=array(), $allow_remote=false) +function rcmail_html4inline($body, $args) { - $last_style_pos = 0; - $cont_id = $container_id . ($body_class ? ' div.'.$body_class : ''); + $last_pos = 0; + $cont_id = $args['container_id'] . ($args['body_class'] ? ' div.' . $args['body_class'] : ''); // find STYLE tags - while (($pos = stripos($body, '', $pos))) { + while (($pos = stripos($body, '', $pos))) { $pos = strpos($body, '>', $pos) + 1; $len = $pos2 - $pos; // replace all css definitions with #container [def] $styles = substr($body, $pos, $len); - $styles = rcube_utils::mod_css_styles($styles, $cont_id, $allow_remote); + $styles = rcube_utils::mod_css_styles($styles, $cont_id, $args['safe'], $args['css_prefix']); - $body = substr_replace($body, $styles, $pos, $len); - $last_style_pos = $pos2 + strlen($styles) - $len; + $body = substr_replace($body, $styles, $pos, $len); + $last_pos = $pos2 + strlen($styles) - $len; } $body = preg_replace(array( @@ -1511,13 +1522,13 @@ function rcmail_html4inline($body, $container_id, $body_class='', &$attributes=a '', '<?', '?>', - '
', + '
', '
', ), $body); // Handle body attributes that doesn't play nicely with div elements - $regexp = '/
]*)/'; + $regexp = '/
]*)/'; if (preg_match($regexp, $body, $m)) { $style = array(); $attrs = $m[0]; @@ -1563,7 +1574,7 @@ function rcmail_html4inline($body, $container_id, $body_class='', &$attributes=a // make sure there's 'rcmBody' div, we need it for proper css modification // its name is hardcoded in rcmail_message_body() also else { - $body = '
' . $body . '
'; + $body = '
' . $body . '
'; } return $body; @@ -1586,8 +1597,15 @@ function rcmail_washtml_link_callback($tag, $attribs, $content, $washtml) if ($tag == 'link' && preg_match('/^https?:\/\//i', $attrib['href'])) { $tempurl = 'tmp-' . md5($attrib['href']) . '.css'; $_SESSION['modcssurls'][$tempurl] = $attrib['href']; - $attrib['href'] = $RCMAIL->url(array('task' => 'utils', 'action' => 'modcss', 'u' => $tempurl, 'c' => $GLOBALS['rcmail_html_container_id'])); + $attrib['href'] = $RCMAIL->url(array( + 'task' => 'utils', + 'action' => 'modcss', + 'u' => $tempurl, + 'c' => $washtml->get_config('container_id'), + 'p' => $washtml->get_config('css_prefix'), + )); $end = ' />'; + $content = null; } else if (preg_match('/^mailto:(.+)/i', $attrib['href'], $mailto)) { list($mailto, $url) = explode('?', html_entity_decode($mailto[1], ENT_QUOTES, 'UTF-8'), 2); diff --git a/program/steps/utils/modcss.inc b/program/steps/utils/modcss.inc index 09e7c93fe..19fefe05e 100644 --- a/program/steps/utils/modcss.inc +++ b/program/steps/utils/modcss.inc @@ -72,10 +72,12 @@ else { } $ctype_regexp = '~Content-Type:\s+text/(css|plain)~i'; +$container_id = preg_replace('/[^a-z0-9]/i', '', $_GET['_c']); +$css_prefix = preg_replace('/[^a-z0-9]/i', '', $_GET['_p']); if ($source !== false && preg_match($ctype_regexp, $headers)) { header('Content-Type: text/css'); - echo rcube_utils::mod_css_styles($source, preg_replace('/[^a-z0-9]/i', '', $_GET['_c'])); + echo rcube_utils::mod_css_styles($source, $container, false, $css_prefix); exit; } diff --git a/tests/Framework/Utils.php b/tests/Framework/Utils.php index 2a5c090d9..4022a3440 100644 --- a/tests/Framework/Utils.php +++ b/tests/Framework/Utils.php @@ -223,6 +223,37 @@ class Framework_Utils extends PHPUnit_Framework_TestCase $this->assertContains("#rcmbody { background-image: url(data:image/png;base64,123);", $mod, "Data URIs in url() allowed [2]"); } + /** + * rcube_utils::mod_css_styles()'s prefix argument handling + */ + function test_mod_css_styles_prefix() + { + $css = ' + .one { font-size: 10pt; } + .three.four { font-weight: bold; } + #id1 { color: red; } + #id2.class:focus { color: white; } + .five:not(.test), { background: transparent; } + div .six { position: absolute; } + p > i { font-size: 120%; } + div#some { color: yellow; } + @media screen and (max-width: 699px) and (min-width: 520px) { + li a.button { padding-left: 30px; } + } + '; + $mod = rcube_utils::mod_css_styles($css, 'rc', true, 'test'); + + $this->assertContains('#rc .testone', $mod); + $this->assertContains('#rc .testthree.testfour', $mod); + $this->assertContains('#rc #testid1', $mod); + $this->assertContains('#rc #testid2.testclass:focus', $mod); + $this->assertContains('#rc .testfive:not(.testtest)', $mod); + $this->assertContains('#rc div .testsix', $mod); + $this->assertContains('#rc p > i ', $mod); + $this->assertContains('#rc div#testsome', $mod); + $this->assertContains('#rc li a.testbutton', $mod); + } + function test_xss_entity_decode() { $mod = rcube_utils::xss_entity_decode("<img/src=x onerror=alert(1)// "); diff --git a/tests/Framework/Washtml.php b/tests/Framework/Washtml.php index 90cc477df..cddbf4bb6 100644 --- a/tests/Framework/Washtml.php +++ b/tests/Framework/Washtml.php @@ -369,4 +369,19 @@ class Framework_Washtml extends PHPUnit_Framework_TestCase $this->assertNotContains('onerror=alert(1)>', $washed); $this->assertContains('<p style="x:', $washed); } + + /** + * Test css_prefix feature + */ + function test_css_prefix() + { + $washer = new rcube_washtml(array('css_prefix' => 'test')); + + $html = '

'; + $washed = $washer->wash($html); + + $this->assertContains('id="testmy-id"', $washed); + $this->assertContains('for="testmy-other-id"', $washed); + $this->assertContains('class="testmy-class1 testmy-class2"', $washed); + } } diff --git a/tests/MailFunc.php b/tests/MailFunc.php index 69b8a3517..8f951863a 100644 --- a/tests/MailFunc.php +++ b/tests/MailFunc.php @@ -42,7 +42,7 @@ class MailFunc extends PHPUnit_Framework_TestCase $part->replaces = array('ex1.jpg' => 'part_1.2.jpg', 'ex2.jpg' => 'part_1.2.jpg'); // render HTML in normal mode - $html = rcmail_html4inline(rcmail_print_body($part->body, $part, array('safe' => false)), 'foo'); + $html = rcmail_html4inline(rcmail_print_body($part->body, $part, array('safe' => false)), array('container_id' => 'foo')); $this->assertRegExp('/src="'.$part->replaces['ex1.jpg'].'"/', $html, "Replace reference to inline image"); $this->assertRegExp('#background="program/resources/blocked.gif"#', $html, "Replace external background image"); @@ -56,7 +56,7 @@ class MailFunc extends PHPUnit_Framework_TestCase $this->assertTrue($GLOBALS['REMOTE_OBJECTS'], "Remote object detected"); // render HTML in safe mode - $html2 = rcmail_html4inline(rcmail_print_body($part->body, $part, array('safe' => true)), 'foo'); + $html2 = rcmail_html4inline(rcmail_print_body($part->body, $part, array('safe' => true)), array('container_id' => 'foo')); $this->assertRegExp('/