diff --git a/program/actions/mail/compose.php b/program/actions/mail/compose.php
index 2473894be1..9c01975403 100644
--- a/program/actions/mail/compose.php
+++ b/program/actions/mail/compose.php
@@ -989,22 +989,17 @@ public static function prepare_html_body($body, $wash_params = [])
{
static $part_no;
- // Set attributes of the part container
- $container_id = self::$COMPOSE['mode'] . 'body' . (++$part_no);
-
$wash_params += [
- 'safe' => self::$MESSAGE->is_safe,
- 'css_prefix' => 'v' . $part_no,
+ 'safe' => self::$MESSAGE && self::$MESSAGE->is_safe,
+ 'css_prefix' => 'v' . (++$part_no),
'add_comments' => false,
];
- if (self::$COMPOSE['mode'] == rcmail_sendmail::MODE_DRAFT) {
+ if (self::$COMPOSE && self::$COMPOSE['mode'] == rcmail_sendmail::MODE_DRAFT) {
// convert TinyMCE's empty-line sequence (#1490463)
$body = preg_replace('/
\xC2\xA0<\/p>/', '
', $body);
// remove tags (not their content)
$wash_params['ignore_elements'] = ['body'];
- } else {
- $wash_params['container_id'] = $container_id;
}
// Make the HTML content safe and clean
diff --git a/program/actions/mail/index.php b/program/actions/mail/index.php
index 802dd05e67..9dc53c75f9 100644
--- a/program/actions/mail/index.php
+++ b/program/actions/mail/index.php
@@ -949,7 +949,7 @@ public static function wash_html($html, $p, $cid_replaces = [])
'css_prefix' => $p['css_prefix'],
// internal configuration
'container_id' => $p['container_id'],
- 'body_class' => $p['body_class'] ?? '',
+ 'body_class' => $p['body_class'] ?? 'rcmBody',
];
if (empty($p['inline_html'])) {
@@ -974,14 +974,7 @@ public static function wash_html($html, $p, $cid_replaces = [])
if (!empty($p['inline_html'])) {
$washer->add_callback('body', 'rcmail_action_mail_index::washtml_callback');
-
- if ($wash_opts['body_class']) {
- self::$wash_html_body_attrs['class'] = $wash_opts['body_class'];
- }
-
- if ($wash_opts['container_id']) {
- self::$wash_html_body_attrs['id'] = $wash_opts['container_id'];
- }
+ self::$wash_html_body_attrs['class'] = $wash_opts['body_class'];
}
if (empty($p['skip_washer_form_callback'])) {
@@ -1149,11 +1142,10 @@ public static function washtml_callback($tagname, $attrib, $content, $washtml)
if (strlen($out)) {
$css_prefix = $washtml->get_config('css_prefix');
$is_safe = $washtml->get_config('allow_remote');
- $body_class = $washtml->get_config('body_class') ?: '';
+ $body_class = $washtml->get_config('body_class');
$cont_id = $washtml->get_config('container_id') ?: '';
- $cont_id = trim($cont_id . ($body_class ? " div.{$body_class}" : ''));
- $out = rcube_utils::mod_css_styles($out, $cont_id, $is_safe, $css_prefix);
+ $out = rcube_utils::mod_css_styles($out, $cont_id, $is_safe, $css_prefix, $body_class);
$out = html::tag('style', ['type' => 'text/css'], $out);
}
@@ -1183,11 +1175,20 @@ public static function washtml_callback($tagname, $attrib, $content, $washtml)
$style['background-image'] = "url({$value})";
}
break;
+ case 'class':
+ if (!empty($value)) {
+ $attrs['class'] = trim(($attrs['class'] ?? '') . ' ' . $value);
+ }
+ break;
default:
$attrs[$attr_name] = $value;
}
}
+ if (isset($attrs['class']) && empty($attrs['class'])) {
+ unset($attrs['class']);
+ }
+
if (!empty($style)) {
foreach ($style as $idx => $val) {
$style[$idx] = $idx . ': ' . $val;
@@ -1282,14 +1283,13 @@ public static function 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([
- 'task' => 'utils',
- 'action' => 'modcss',
- 'u' => $tempurl,
- 'c' => $washtml->get_config('container_id'),
- 'p' => $washtml->get_config('css_prefix'),
- ]);
+ $_SESSION['modcssurls'][$tempurl] = [
+ 'url' => $attrib['href'],
+ 'container_id' => $washtml->get_config('container_id'),
+ 'css_prefix' => $washtml->get_config('css_prefix'),
+ 'body_class' => $washtml->get_config('body_class'),
+ ];
+ $attrib['href'] = $rcmail->url(['task' => 'utils', 'action' => 'modcss', 'u' => $tempurl]);
$content = null;
} elseif (preg_match('/^mailto:(.+)/i', $attrib['href'], $mailto)) {
$url_parts = explode('?', html_entity_decode($mailto[1], \ENT_QUOTES, 'UTF-8'), 2);
diff --git a/program/actions/mail/show.php b/program/actions/mail/show.php
index 586453974f..1edb05c853 100644
--- a/program/actions/mail/show.php
+++ b/program/actions/mail/show.php
@@ -635,7 +635,6 @@ public static function message_body($attrib)
$rcmail = rcmail::get_instance();
$safe_mode = self::$MESSAGE->is_safe || !empty($_GET['_safe']);
$out = '';
- $part_no = 0;
$header_attrib = [];
foreach ($attrib as $attr => $value) {
@@ -692,32 +691,8 @@ public static function message_body($attrib)
self::message_error();
}
- $plugin = $rcmail->plugins->exec_hook('message_body_prefix',
- ['part' => $part, 'prefix' => '', 'message' => self::$MESSAGE]);
-
- // Set attributes of the part container
- $container_class = $part->ctype_secondary == 'html' ? 'message-htmlpart' : 'message-part';
- $container_id = $container_class . (++$part_no);
- $container_attrib = ['class' => $container_class, 'id' => $container_id];
-
- $body_args = [
- '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 = self::print_body($body, $part, $body_args);
-
- // check if the message body is PGP encrypted
- if (strpos($body, '-----BEGIN PGP MESSAGE-----') !== false) {
- $rcmail->output->set_env('is_pgp_content', '#' . $container_id);
- }
-
- $out .= html::div($body_args['container_attrib'], $plugin['prefix'] . $body);
+ // Process the part content for display
+ $out .= self::prepare_part_body($body, $part);
}
}
} else {
@@ -888,4 +863,40 @@ public static function mdn_request_handler($message)
}
}
}
+
+ /**
+ * Prepare message part content for display
+ */
+ protected static function prepare_part_body($body, $part)
+ {
+ static $part_no;
+
+ $rcmail = rcmail::get_instance();
+
+ $plugin = $rcmail->plugins->exec_hook('message_body_prefix',
+ ['part' => $part, 'prefix' => '', 'message' => self::$MESSAGE]);
+
+ // Set attributes of the part container
+ $container_class = $part->ctype_secondary == 'html' ? 'message-htmlpart' : 'message-part';
+ $container_id = $container_class . (++$part_no);
+ $container_attrib = ['class' => $container_class, 'id' => $container_id];
+
+ $body_args = [
+ 'safe' => (self::$MESSAGE && self::$MESSAGE->is_safe) || !empty($_GET['_safe']),
+ 'plain' => !$rcmail->config->get('prefer_html'),
+ 'css_prefix' => 'v' . $part_no,
+ 'container_id' => $container_id,
+ 'container_attrib' => $container_attrib,
+ ];
+
+ // Parse the part content for display
+ $body = self::print_body($body, $part, $body_args);
+
+ // check if the message body is PGP encrypted
+ if (strpos($body, '-----BEGIN PGP MESSAGE-----') !== false) {
+ $rcmail->output->set_env('is_pgp_content', '#' . $body_args['container_id']);
+ }
+
+ return html::div($body_args['container_attrib'], $plugin['prefix'] . $body);
+ }
}
diff --git a/program/actions/utils/modcss.php b/program/actions/utils/modcss.php
index aebeb90a9d..227c163300 100644
--- a/program/actions/utils/modcss.php
+++ b/program/actions/utils/modcss.php
@@ -37,7 +37,8 @@ public function run($args = [])
$rcmail->output->sendExitError(403, 'Unauthorized request');
}
- $realurl = $_SESSION['modcssurls'][$url];
+ $data = $_SESSION['modcssurls'][$url];
+ $realurl = $data['url'] ?? '';
// don't allow any other connections than http(s)
if (!preg_match('~^https?://~i', $realurl, $matches)) {
@@ -59,16 +60,13 @@ public function run($args = [])
rcube::raise_error($e, true, false);
}
- $cid = rcube_utils::get_input_string('_c', rcube_utils::INPUT_GET);
- $prefix = rcube_utils::get_input_string('_p', rcube_utils::INPUT_GET);
+ if ($source !== false && $ctype && preg_match('~^text/(css|plain)~i', $ctype)) {
+ $container_id = $data['container_id'] ?? '';
+ $css_prefix = $data['css_prefix'] ?? '';
+ $body_class = $data['body_class'] ?? '';
- $container_id = preg_replace('/[^a-z0-9]/i', '', $cid);
- $css_prefix = preg_replace('/[^a-z0-9]/i', '', $prefix);
- $ctype_regexp = '~^text/(css|plain)~i';
-
- if ($source !== false && $ctype && preg_match($ctype_regexp, $ctype)) {
$rcmail->output->sendExit(
- rcube_utils::mod_css_styles($source, $container_id, false, $css_prefix),
+ rcube_utils::mod_css_styles($source, $container_id, false, $css_prefix, $body_class),
['Content-Type: text/css']
);
}
diff --git a/program/lib/Roundcube/rcube_utils.php b/program/lib/Roundcube/rcube_utils.php
index db02400049..d85368c614 100644
--- a/program/lib/Roundcube/rcube_utils.php
+++ b/program/lib/Roundcube/rcube_utils.php
@@ -422,10 +422,11 @@ public static function html_identifier($str, $encode = false)
* @param string $container_id Container ID to use as prefix
* @param bool $allow_remote Allow remote content
* @param string $prefix Prefix to be added to id/class identifier
+ * @param string $body_class Optional class name for the used-to-be-body element
*
* @return string Modified CSS source
*/
- public static function mod_css_styles($source, $container_id, $allow_remote = false, $prefix = '')
+ public static function mod_css_styles($source, $container_id, $allow_remote = false, $prefix = '', $body_class = '')
{
$source = self::xss_entity_decode($source);
@@ -485,7 +486,7 @@ public static function mod_css_styles($source, $container_id, $allow_remote = fa
// for cases like @media { body { position: fixed; } } (#5811)
$excl = '(?!' . substr($replacements->pattern, 1, -1) . ')';
$regexp = '/(^\s*|,\s*|\}\s*|\{\s*)(' . $excl . ':?[a-z0-9\._#\*\[][a-z0-9\._:\(\)#=~ \[\]"\|\>\+\$\^-]*)/im';
- $callback = static function ($matches) use ($container_id, $prefix) {
+ $callback = static function ($matches) use ($container_id, $prefix, $body_class) {
$replace = $matches[2];
if (stripos($replace, ':root') === 0) {
@@ -497,6 +498,9 @@ public static function mod_css_styles($source, $container_id, $allow_remote = fa
}
if ($container_id) {
+ // replace body definition because we stripped off the tag
+ $replace = preg_replace('/^\s*body/i', $body_class ? ".{$body_class}" : '', $replace);
+
$replace = "#{$container_id} " . $replace;
}
@@ -509,12 +513,6 @@ public static function mod_css_styles($source, $container_id, $allow_remote = fa
$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/skins/elastic/styles/styles.less b/skins/elastic/styles/styles.less
index 560c574a27..d9bff7b577 100644
--- a/skins/elastic/styles/styles.less
+++ b/skins/elastic/styles/styles.less
@@ -334,7 +334,7 @@ body.task-error-login #layout {
margin-bottom: .5rem;
}
- div.rcmBody {
+ & > .rcmBody {
// Remove margins that can be set by the mail message styles
margin: 0 auto !important;
// Fix floating table issue (#9804)
diff --git a/tests/ActionTestCase.php b/tests/ActionTestCase.php
index ee40796b7f..9176c3e91c 100644
--- a/tests/ActionTestCase.php
+++ b/tests/ActionTestCase.php
@@ -2,6 +2,7 @@
namespace Roundcube\Tests;
+use Masterminds\HTML5;
use PHPUnit\Framework\TestCase;
/**
@@ -268,6 +269,15 @@ protected static function loadSQLScript($db, $name)
}
}
+ /**
+ * Parse HTML output into DOMXPath object
+ */
+ protected static function parseHtml($html)
+ {
+ $html5 = new HTML5(['disable_html_ns' => true]);
+ return new \DOMXPath($html5->loadHTML($html));
+ }
+
/**
* Call the action's run() method and handle exit exception
*/
diff --git a/tests/Actions/Mail/ComposeTest.php b/tests/Actions/Mail/ComposeTest.php
index 691e2b3ce2..cc9bec0efa 100644
--- a/tests/Actions/Mail/ComposeTest.php
+++ b/tests/Actions/Mail/ComposeTest.php
@@ -19,6 +19,44 @@ public function test_class()
$this->assertInstanceOf(\rcmail_action::class, $object);
}
+ /**
+ * Test prepare_html_body() method
+ */
+ public function test_prepare_html_body()
+ {
+ $action = new \rcmail_action_mail_compose();
+
+ $html = <<<'EOF'
+
+
+
+
+
+ Broken CSS selector
+
+
+ EOF;
+
+ $body = $action->prepare_html_body($html);
+ $xpath = self::parseHtml($body);
+
+ $this->assertCount(1, $xpath->query('//style'));
+ $this->assertSame('text/css', $xpath->query('//style')->item(0)->getAttribute('type'));
+ $this->assertSame(
+ '@media (min-width: 600px) { .v1body_class_name { color: red; } }',
+ trim(preg_replace('/(\s{2,}|\n)/', ' ', $xpath->query('//style')->item(0)->textContent))
+ );
+
+ $this->assertCount(1, $xpath->query('//div'));
+ $this->assertSame('v1bod', $xpath->query('//div')->item(0)->getAttribute('id'));
+ $this->assertSame('rcmBody v1body_class_name', $xpath->query('//div')->item(0)->getAttribute('class'));
+ $this->assertCount(1, $xpath->query('//div/p'));
+ }
+
/**
* Test quote_text() method
*/
diff --git a/tests/Actions/Mail/IndexTest.php b/tests/Actions/Mail/IndexTest.php
index 12be74c792..acbd584e2f 100644
--- a/tests/Actions/Mail/IndexTest.php
+++ b/tests/Actions/Mail/IndexTest.php
@@ -328,8 +328,6 @@ public function test_class()
*/
public function test_html()
{
- $this->initOutput(\rcmail_action::MODE_HTTP, 'mail', '');
-
$part = $this->get_html_part('src/htmlbody.txt');
$part->replaces = ['ex1.jpg' => 'part_1.2.jpg', 'ex2.jpg' => 'part_1.2.jpg'];
@@ -364,8 +362,6 @@ public function test_html()
*/
public function test_html_xss()
{
- $this->initOutput(\rcmail_action::MODE_HTTP, 'mail', '');
-
$part = $this->get_html_part('src/htmlxss.txt');
$params = ['container_id' => 'foo', 'safe' => true];
$html = \rcmail_action_mail_index::print_body($part->body, $part, $params);
@@ -383,8 +379,6 @@ public function test_html_xss()
*/
public function test_html_xss2()
{
- $this->initOutput(\rcmail_action::MODE_HTTP, 'mail', '');
-
$part = $this->get_html_part('src/BID-26800.txt');
$params = ['container_id' => 'dabody', 'safe' => true];
$washed = \rcmail_action_mail_index::print_body($part->body, $part, $params);
@@ -398,8 +392,6 @@ public function test_html_xss2()
*/
public function test_html_xss3()
{
- $this->initOutput(\rcmail_action::MODE_HTTP, 'mail', '');
-
// #1488850
$html = 'Firefox'
. 'Internet Explorer
';
@@ -417,7 +409,7 @@ public function test_html_body_attributes()
$part = $this->get_html_part();
$part->body = 'Foo';
- $params = ['safe' => true, 'add_comments' => false];
+ $params = ['safe' => true, 'add_comments' => false, 'body_class' => ''];
$washed = \rcmail_action_mail_index::print_body($part->body, $part, $params);
$this->assertSame(str_replace('body', 'div', $part->body), $washed);
@@ -449,15 +441,15 @@ public function test_wash_html()
public function test_wash_html_body_style()
{
$html = 'test
';
- $params = ['container_id' => 'foo', 'add_comments' => false, 'safe' => false];
+ $params = ['container_id' => 'foo', 'add_comments' => false, 'safe' => false, 'body_class' => ''];
$washed = \rcmail_action_mail_index::wash_html($html, $params, []);
- $this->assertSame('', $washed);
+ $this->assertSame('', $washed);
$params['safe'] = true;
$washed = \rcmail_action_mail_index::wash_html($html, $params, []);
- $this->assertSame('', $washed);
+ $this->assertSame('', $washed);
}
/**
@@ -468,8 +460,6 @@ public function test_wash_html_body_style()
#[Group('mbstring')]
public function test_washtml_utf8()
{
- $this->initOutput(\rcmail_action::MODE_HTTP, 'mail', '');
-
$part = $this->get_html_part('src/invalidchars.html');
$washed = \rcmail_action_mail_index::print_body($part->body, $part);
@@ -481,8 +471,6 @@ public function test_washtml_utf8()
*/
public function test_meta_insertion()
{
- $this->initOutput(\rcmail_action::MODE_HTTP, 'mail', '');
-
$meta = '';
$args = [
'inline_html' => false,
@@ -521,8 +509,6 @@ public function test_meta_insertion()
*/
public function test_plaintext()
{
- $this->initOutput(\rcmail_action::MODE_HTTP, 'mail', '');
-
$part = new \rcube_message_part();
$part->ctype_primary = 'text';
$part->ctype_secondary = 'plain';
@@ -551,8 +537,6 @@ public function test_plaintext()
*/
public function test_mailto()
{
- $this->initOutput(\rcmail_action::MODE_HTTP, 'mail', '');
-
$part = $this->get_html_part('src/mailto.txt');
$params = ['container_id' => 'foo', 'safe' => false];
@@ -570,8 +554,6 @@ public function test_mailto()
*/
public function test_html_comments()
{
- $this->initOutput(\rcmail_action::MODE_HTTP, 'mail', '');
-
$part = $this->get_html_part('src/htmlcom.txt');
$washed = \rcmail_action_mail_index::print_body($part->body, $part, ['safe' => true]);
@@ -586,8 +568,6 @@ public function test_html_comments()
*/
public function test_html_links()
{
- $this->initOutput(\rcmail_action::MODE_HTTP, 'mail', '');
-
// disable relative links
$html = 'test';
$body = \rcmail_action_mail_index::print_body($html, $this->get_html_part(), ['safe' => false, 'plain' => false]);
@@ -609,8 +589,6 @@ public function test_html_links()
*/
public function test_html_link_xss()
{
- $this->initOutput(\rcmail_action::MODE_HTTP, 'mail', '');
-
$html = 'test';
$body = \rcmail_action_mail_index::print_body($html, $this->get_html_part(), ['safe' => false, 'plain' => false]);
diff --git a/tests/Actions/Mail/ShowTest.php b/tests/Actions/Mail/ShowTest.php
index 9970edc214..abee72391b 100644
--- a/tests/Actions/Mail/ShowTest.php
+++ b/tests/Actions/Mail/ShowTest.php
@@ -4,6 +4,8 @@
use Roundcube\Tests\ActionTestCase;
+use function Roundcube\Tests\invokeMethod;
+
/**
* Test class to test rcmail_action_mail_show
*/
@@ -18,4 +20,104 @@ public function test_class()
$this->assertInstanceOf(\rcmail_action::class, $object);
}
+
+ /**
+ * Test prepare_part_body() method
+ */
+ public function test_prepare_part_body()
+ {
+ $action = new \rcmail_action_mail_show();
+
+ // HTML sample (from #9911) focusing on body attributes and styles
+ $html = <<<'EOF'
+
+
+
+
+
+ Test
+
+
+ EOF;
+
+ $body = invokeMethod($action, 'prepare_part_body', [$html, $this->get_html_part()]);
+ $xpath = self::parseHtml($body);
+
+ $this->assertCount(1, $xpath->query('/html/div'));
+ $this->assertSame('message-htmlpart1', $xpath->query('/html/div')->item(0)->getAttribute('id'));
+ $this->assertSame('message-htmlpart', $xpath->query('/html/div')->item(0)->getAttribute('class'));
+
+ $this->assertCount(1, $xpath->query('/html/div/style'));
+ $this->assertSame('text/css', $xpath->query('/html/div/style')->item(0)->getAttribute('type'));
+ $this->assertSame(
+ '@media (min-width: 600px) { #message-htmlpart1 .v1body_class_name { color: red; } }',
+ trim(preg_replace('/(\s{2,}|\n)/', ' ', $xpath->query('/html/div/style')->item(0)->textContent))
+ );
+
+ $this->assertCount(1, $xpath->query('/html/div/div'));
+ $this->assertSame('v1bod', $xpath->query('/html/div/div')->item(0)->getAttribute('id'));
+ $this->assertSame('rcmBody v1body_class_name', $xpath->query('/html/div/div')->item(0)->getAttribute('class'));
+ $this->assertSame('Test', $xpath->query('/html/div/div/p')->item(0)->textContent);
+
+ // Invoking the method again (for the next part) use a different ID/prefix
+ $body = invokeMethod($action, 'prepare_part_body', [$html, $this->get_html_part()]);
+ $xpath = self::parseHtml($body);
+
+ $this->assertCount(1, $xpath->query('/html/div'));
+ $this->assertSame('message-htmlpart2', $xpath->query('/html/div')->item(0)->getAttribute('id'));
+ $this->assertSame('message-htmlpart', $xpath->query('/html/div')->item(0)->getAttribute('class'));
+
+ $this->assertCount(1, $xpath->query('/html/div/style'));
+ $this->assertSame(
+ '@media (min-width: 600px) { #message-htmlpart2 .v2body_class_name { color: red; } }',
+ trim(preg_replace('/(\s{2,}|\n)/', ' ', $xpath->query('/html/div/style')->item(0)->textContent))
+ );
+
+ $this->assertCount(1, $xpath->query('/html/div/div'));
+ $this->assertSame('v2bod', $xpath->query('/html/div/div')->item(0)->getAttribute('id'));
+ $this->assertSame('rcmBody v2body_class_name', $xpath->query('/html/div/div')->item(0)->getAttribute('class'));
+
+ // Another HTML sample
+ $html = <<<'EOF'
+
+
+
+ Test
+
+
+ EOF;
+
+ $body = invokeMethod($action, 'prepare_part_body', [$html, $this->get_html_part()]);
+ $xpath = self::parseHtml($body);
+
+ $this->assertSame('message-htmlpart3', $xpath->query('/html/div')->item(0)->getAttribute('id'));
+ $this->assertSame(
+ '#message-htmlpart3 .rcmBody, #message-htmlpart3 .rcmBody > p { color: red; } '
+ . '#message-htmlpart3 .v3bt p, #message-htmlpart3 .rcmBody.v3bt { color: unset; }',
+ trim(preg_replace('/(\s{2,}|\n)/', ' ', $xpath->query('/html/div/style')->item(0)->textContent))
+ );
+ $this->assertSame('', $xpath->query('/html/div/div')->item(0)->getAttribute('id'));
+ $this->assertSame('rcmBody v3bt', $xpath->query('/html/div/div')->item(0)->getAttribute('class'));
+ }
+
+ /**
+ * Helper method to create a HTML message part object
+ */
+ protected function get_html_part($body = null)
+ {
+ $part = new \rcube_message_part();
+ $part->ctype_primary = 'text';
+ $part->ctype_secondary = 'html';
+ $part->body = $body ? file_get_contents(TESTS_DIR . $body) : null;
+ $part->replaces = [];
+
+ return $part;
+ }
}
diff --git a/tests/Actions/Utils/ModcssTest.php b/tests/Actions/Utils/ModcssTest.php
index 2f93d60361..4089a205ad 100644
--- a/tests/Actions/Utils/ModcssTest.php
+++ b/tests/Actions/Utils/ModcssTest.php
@@ -49,7 +49,7 @@ public function test_run()
$this->assertNull($output->getOutput());
// Valid url pointing to non-existing resource
- $_SESSION['modcssurls'][$key] = $url;
+ $_SESSION['modcssurls'][$key] = ['url' => $url];
HttpClientMock::setResponses([
[404],
@@ -64,9 +64,11 @@ public function test_run()
// Valid url pointing to an existing resource
$url = 'https://raw.githubusercontent.com/roundcube/roundcubemail/master/program/resources/tinymce/content.css';
- $_SESSION['modcssurls'][$key] = $url;
- $_GET['_p'] = 'prefix';
- $_GET['_c'] = 'cid';
+ $_SESSION['modcssurls'][$key] = [
+ 'url' => $url,
+ 'css_prefix' => 'prefix',
+ 'container_id' => 'cid',
+ ];
$this->runAndAssert($action, OutputHtmlMock::E_EXIT);
diff --git a/tests/Framework/UtilsTest.php b/tests/Framework/UtilsTest.php
index 9086d81593..1ea2a095e3 100644
--- a/tests/Framework/UtilsTest.php
+++ b/tests/Framework/UtilsTest.php
@@ -240,7 +240,7 @@ public function test_mod_css_styles_xss()
$this->assertSame('/* evil! */', $mod);
$mod = \rcube_utils::mod_css_styles("body.main2cols { background-image: url('../images/leftcol.png'); }", 'rcmbody');
- $this->assertSame('#rcmbody.main2cols {}', $mod);
+ $this->assertSame('#rcmbody .main2cols {}', $mod);
$mod = \rcube_utils::mod_css_styles('p { left:expression(document.body.offsetWidth-20); }', 'rcmbody');
$this->assertSame('#rcmbody p {}', $mod);
diff --git a/tests/MessageRendering/InlineImageTest.php b/tests/MessageRendering/InlineImageTest.php
index b96f089d3a..8d17fb00f1 100644
--- a/tests/MessageRendering/InlineImageTest.php
+++ b/tests/MessageRendering/InlineImageTest.php
@@ -24,7 +24,7 @@ public function testImageFromDataUri()
$this->assertStringContainsString('?_task=mail&_action=get&_mbox=INBOX&_uid=', $src);
$this->assertStringContainsString('&_part=2&_embed=1&_mimeclass=image', $src);
- $this->assertSame('v1signature', $divElements[2]->attributes->getNamedItem('class')->textContent);
+ $this->assertStringMatchesFormat('v%isignature', $divElements[2]->attributes->getNamedItem('class')->textContent);
// This matches a non-breakable space.
$this->assertMatchesRegularExpression('|^\x{00a0}$|u', $divElements[2]->textContent);