Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Render each mime-part into an individual iframe #9519

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
87 changes: 47 additions & 40 deletions plugins/hide_blockquote/hide_blockquote.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,57 +17,64 @@

if (window.rcmail) {
rcmail.addEventListener('init', function () {
hide_blockquote();
});
}
var limit = rcmail.env.blockquote_limit;

function hide_blockquote() {
var limit = rcmail.env.blockquote_limit;
if (limit <= 0) {
return;
}

if (limit <= 0) {
return;
}
$('.framed-message-part').each(function (_id, iframe) {
$(iframe).on('load', function () {
$(iframe.contentDocument).find('.message-part div.pre > blockquote').each(function (_id, elem) {
hide_blockquote(elem, limit);
});
window.dispatchEvent(new Event('resize'));
});
});
});
}

$('div.message-part div.pre > blockquote', $('#messagebody')).each(function () {
var res, text, div, link, q = $(this);
function hide_blockquote(elem, limit) {
var res, text, div, link, q = $(elem);

// Add new-line character before each blockquote
// This fixes counting lines of text, it also prevents
// from merging lines from different quoting level
$('blockquote').before(document.createTextNode('\n'));
// Add new-line character before each blockquote
// This fixes counting lines of text, it also prevents
// from merging lines from different quoting level
$('blockquote').before(document.createTextNode('\n'));

text = q.text().trim();
res = text.split(/\n/);
text = q.text().trim();
res = text.split(/\n/);

if (res.length <= limit) {
// there can be also a block with very long wrapped line
// assume line height = 15px
if (q.height() <= limit * 15) {
return;
}
if (res.length <= limit) {
// there can be also a block with very long wrapped line
// assume line height = 15px
if (q.height() <= limit * 15) {
return;
}
}

div = $('<blockquote class="blockquote-header">')
.css({ 'white-space': 'nowrap', overflow: 'hidden', position: 'relative' })
.text(res[0]);
div = $('<blockquote class="blockquote-header">')
.css({ 'white-space': 'nowrap', overflow: 'hidden', position: 'relative' })
.text(res[0]);

link = $('<span class="blockquote-link"></span>')
.css({ position: 'absolute', 'z-Index': 2 })
.text(rcmail.get_label('hide_blockquote.show'))
.data('parent', div)
.click(function () {
var t = $(this), parent = t.data('parent'), visible = parent.is(':visible');
link = $('<span class="blockquote-link"></span>')
.css({ position: 'absolute', 'z-Index': 2 })
.text(rcmail.get_label('hide_blockquote.show'))
.data('parent', div)
.click(function () {
var t = $(this), parent = t.data('parent'), visible = parent.is(':visible');

t.text(rcmail.get_label(visible ? 'hide' : 'show', 'hide_blockquote'))
.detach().appendTo(visible ? q : parent).toggleClass('collapsed');
t.text(rcmail.get_label(visible ? 'hide' : 'show', 'hide_blockquote'))
.detach().appendTo(visible ? q : parent).toggleClass('collapsed');

parent[visible ? 'hide' : 'show']();
q[visible ? 'show' : 'hide']();
});
parent[visible ? 'hide' : 'show']();
q[visible ? 'show' : 'hide']();

link.appendTo(div);
window.dispatchEvent(new Event('resize'));
});

// Modify blockquote
q.hide().css({ position: 'relative' }).before(div);
});
link.appendTo(div);

// Modify blockquote
q.hide().css({ position: 'relative' }).before(div);
}
58 changes: 22 additions & 36 deletions program/actions/mail/get.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,6 @@ public function run($args = [])
// This resets X-Frame-Options for framed output (#6688)
$rcmail->output->page_headers();

// show loading page
if (!empty($_GET['_preload'])) {
unset($_GET['_preload']);
unset($_GET['_safe']);

$url = $rcmail->url($_GET + ['_mimewarning' => 1, '_embed' => 1]);
$message = $rcmail->gettext('loadingdata');

header('Content-Type: text/html; charset=' . RCUBE_CHARSET);
echo "<html>\n<head>\n"
. '<meta http-equiv="refresh" content="0; url=' . rcube::Q($url) . '">' . "\n"
. '<meta http-equiv="content-type" content="text/html; charset=' . RCUBE_CHARSET . '">' . "\n"
. "</head>\n<body>\n{$message}\n</body>\n</html>";
exit;
}

$attachment = new rcmail_attachment_handler();
$mimetype = $attachment->mimetype;
$filename = $attachment->filename;
Expand Down Expand Up @@ -117,7 +101,7 @@ public function run($args = [])
readfile($cache_file);
}

exit;
$rcmail->output->sendExit();
}
}

Expand Down Expand Up @@ -213,7 +197,7 @@ public function run($args = [])
$rcmail->output->write();
}

exit;
$rcmail->output->sendExit();
}
}

Expand All @@ -232,6 +216,21 @@ public function run($args = [])
}
}

// Deliver plaintext with HTML-markup
if ($mimetype == 'text/plain' && empty($_GET['_download'])) {
$body = $attachment->print_body();
// Don't use rcmail_html_page here, because that always loads
// embed.css and blocks loading other css files (though calling
// reset() in write()). Also we don't need all the processing that it
// brings.
$styles_path = $rcmail->output->get_skin_file('/styles/styles.css', $path, null, true);
$body = html::tag('html', [],
html::tag('head', [], html::tag('link', ['rel' => 'stylesheet', 'href' => $styles_path]))
. html::tag('body', ['class' => 'message-part'], $body)
);
$rcmail->output->sendExit($body);
}

// deliver part content
if ($mimetype == 'text/html' && empty($_GET['_download'])) {
$rcmail->output = new rcmail_html_page();
Expand All @@ -248,24 +247,10 @@ public function run($args = [])
} else {
// render HTML body
$out = $attachment->html();

// insert remote objects warning into HTML body
if (self::$REMOTE_OBJECTS) {
$rcmail->output->register_inline_warning(
$rcmail->gettext('blockedresources'),
$rcmail->gettext('allow'),
$rcmail->url(array_merge($_GET, ['_safe' => 1]))
);
} else {
// Use strict security policy to make sure no javascript is executed
// TODO: Make the above "blocked resources button" working with strict policy
// TODO: Move this to rcmail_html_page::write()?
header("Content-Security-Policy: script-src 'none'");
}
}

$rcmail->output->write($out);
exit;
$rcmail->output->sendExit();
}

// add filename extension if missing
Expand Down Expand Up @@ -295,12 +280,12 @@ public function run($args = [])
$attachment->output($mimetype);
}

exit;
$rcmail->output->sendExit();
}

// if we arrive here, the requested part was not found
http_response_code(404);
exit;
$rcmail->output->sendExit();
}

/**
Expand Down Expand Up @@ -357,13 +342,14 @@ public static function message_part_frame($attrib)
} else {
$mimetype = $rcmail->output->get_env('mimetype');
$url = $_GET;
$url[strpos($mimetype, 'text/') === 0 ? '_embed' : '_preload'] = 1;
$url['_embed'] = 1;
unset($url['_frame']);
}

$url['_framed'] = 1; // For proper X-Frame-Options:deny handling

$attrib['src'] = $rcmail->url($url);
$attrib['sandbox'] = 'allow-same-origin';

$rcmail->output->add_gui_object('messagepartframe', $attrib['id']);

Expand Down
20 changes: 18 additions & 2 deletions program/actions/mail/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ class rcmail_action_mail_index extends rcmail_action
];

protected static $PRINT_MODE = false;
protected static $REMOTE_OBJECTS;
protected static $SUSPICIOUS_EMAIL = false;
protected static $wash_html_body_attrs = [];

Expand Down Expand Up @@ -157,6 +156,7 @@ public function run($args = [])
'searchfilter' => [$this, 'search_filter'],
'searchinterval' => [$this, 'search_interval'],
'searchform' => [$rcmail->output, 'search_form'],
'messageloadingnotice' => [$this, 'message_loading_notice'],
]);
}

Expand Down Expand Up @@ -1003,7 +1003,14 @@ public static function wash_html($html, $p, $cid_replaces = [])
$html = rcube_charset::clean($html);

$html = $washer->wash($html);
self::$REMOTE_OBJECTS = $washer->extlinks;
if ($washer->extlinks) {
// This is an ugly solution, but the least invasive I could think
// of. The problem is that the "washer" traverses the node tree
// from the top and produces a string containing HTML code - so
// after "washiing" we have only a big string, and before "washing"
// we don't yet know if any remote references are present.
$html = str_replace('<body ', '<body data-extlinks="true" ', $html);
}

// There was no <body>, but a wrapper element is required
if (!empty($p['inline_html']) && !empty(self::$wash_html_body_attrs)) {
Expand Down Expand Up @@ -1668,4 +1675,13 @@ public static function supported_mimetypes()

return array_values($mimetypes);
}

public static function message_loading_notice()
{
$rcmail = rcmail::get_instance();
return html::div(['class' => 'iframe-loading-message ui alert loading'], [
html::tag('i', ['class' => 'icon']),
html::span([], $rcmail->gettext('loadingdata')),
]);
}
}
31 changes: 12 additions & 19 deletions program/actions/mail/show.php
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ public static function remote_objects_msg()

$msg = html::span(null, rcube::Q($rcmail->gettext('blockedresources')));

// TODO: Avoid inline-event.
$buttons = html::a(
['href' => '#loadremote', 'onclick' => rcmail_output::JS_OBJECT_NAME . ".command('load-remote')"],
rcube::Q($rcmail->gettext('allow'))
Expand Down Expand Up @@ -698,26 +699,23 @@ public static function message_body($attrib)
// 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);
$out .= html::div(['class' => 'message-prefix'], $plugin['prefix']);
$out .= html::div(
['id' => $container_id], [
self::message_loading_notice(),
html::iframe([
'sandbox' => 'allow-same-origin',
'class' => "framed-message-part {$container_class}",
'src' => self::$MESSAGE->get_part_url($part->mime_id, false),
]),
]
);
}
}
} else {
Expand Down Expand Up @@ -797,11 +795,6 @@ public static function message_body($attrib)
}
}

// tell client that there are blocked remote objects
if (self::$REMOTE_OBJECTS && !$safe_mode) {
$rcmail->output->set_env('blockedobjects', true);
}

$rcmail->output->add_gui_object('messagebody', $attrib['id']);

return html::div($attrib, $out);
Expand Down
12 changes: 11 additions & 1 deletion program/include/rcmail_attachment_handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,16 @@ public function output($mimetype)
return $this->body(0, -1);
}

public function print_body()
{
$rcmail = rcmail::get_instance();
$body_args = [
'safe' => $this->message->is_safe,
'plain' => !$rcmail->config->get('prefer_html'),
];
return rcmail_action_mail_index::print_body($this->body(), $this->part, $body_args);
}

/**
* Returns formatted HTML if the attachment is HTML
*/
Expand All @@ -309,7 +319,7 @@ public function html()
// show images?
$is_safe = $this->is_safe();

return rcmail_action_mail_index::wash_html($body, ['safe' => $is_safe, 'inline_html' => false]);
return rcmail_action_mail_index::wash_html($body, ['safe' => $is_safe, 'inline_html' => false], $this->part->replaces);
}

/**
Expand Down
Loading
Loading