Skip to content

Commit

Permalink
Add orphan inline images below the message body
Browse files Browse the repository at this point in the history
* In plaintext mode: all inline images will be displayed below the message body
* In HTML mode: all inline images that are **not** referenced in the message HTML body will be listed below the message body

This doesn't add more CPU usage as it is using the already called HTML filtering function to discover if an inline part is referenced or not.

This fixes issue roundcube#5051 and is a bit better than roundcube#9150
  • Loading branch information
bohwaz committed Jan 28, 2024
1 parent d363a6a commit c56b669
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 10 deletions.
13 changes: 9 additions & 4 deletions program/actions/mail/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -896,10 +896,11 @@ public static function check_safe($message)
* @param string $html HTML
* @param array $p Display parameters
* @param array $cid_replaces CID map replaces (inline images)
* @param array $used_cids List of CIDs appearing in message body
*
* @return string Clean HTML
*/
public static function wash_html($html, $p, $cid_replaces = [])
public static function wash_html($html, $p, $cid_replaces = [], &$used_cids = [])
{
$rcmail = rcmail::get_instance();

Expand Down Expand Up @@ -975,6 +976,7 @@ public static function wash_html($html, $p, $cid_replaces = [])

$html = $washer->wash($html);
self::$REMOTE_OBJECTS = $washer->extlinks;
$used_cids = $washer->get_used_cids();

return $html;
}
Expand All @@ -986,10 +988,11 @@ public static function wash_html($html, $p, $cid_replaces = [])
* @param string $body Message part body
* @param rcube_message_part $part Message part
* @param array $p Display parameters array
* @param array $used_cids List of CIDs appearing in message body
*
* @return string Formatted HTML string
*/
public static function print_body($body, $part, $p = [])
public static function print_body($body, $part, $p = [], &$used_cids = [])
{
$rcmail = rcmail::get_instance();

Expand All @@ -1006,6 +1009,8 @@ public static function print_body($body, $part, $p = [])
]
);

$used_cids = [];

// convert html to text/plain
if ($data['plain'] && ($data['type'] == 'html' || $data['type'] == 'enriched')) {
if ($data['type'] == 'enriched') {
Expand All @@ -1017,13 +1022,13 @@ public static function print_body($body, $part, $p = [])
}
// text/html
elseif ($data['type'] == 'html') {
$body = self::wash_html($data['body'], $data, $part->replaces);
$body = self::wash_html($data['body'], $data, $part->replaces, $used_cids);
$part->ctype_secondary = $data['type'];
}
// text/enriched
elseif ($data['type'] == 'enriched') {
$body = rcube_enriched::to_html($data['body']);
$body = self::wash_html($body, $data, $part->replaces);
$body = self::wash_html($body, $data, $part->replaces, $used_cids);
$part->ctype_secondary = 'html';
} else {
// assert plaintext
Expand Down
18 changes: 12 additions & 6 deletions program/actions/mail/show.php
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,8 @@ public static function message_body($attrib)
}
}

$used_cids = [];

if (!empty(self::$MESSAGE->parts)) {
foreach (self::$MESSAGE->parts as $part) {
if ($part->type == 'headers') {
Expand Down Expand Up @@ -715,7 +717,7 @@ public static function message_body($attrib)
];

// Parse the part content for display
$body = self::print_body($body, $part, $body_args);
$body = self::print_body($body, $part, $body_args, $used_cids);

// check if the message body is PGP encrypted
if (strpos($body, '-----BEGIN PGP MESSAGE-----') !== false) {
Expand Down Expand Up @@ -743,17 +745,21 @@ public static function message_body($attrib)
}
}

// list images after mail body
if ($rcmail->config->get('inline_images', true) && !empty(self::$MESSAGE->attachments)) {
// Fetch list of orphan inline parts
$orphan_parts = self::$MESSAGE->get_orphan_inline_parts($used_cids);
$attachments = array_merge(self::$MESSAGE->attachments, $orphan_parts);

// list images after mail body, if it's enabled or if message has orphan parts
if ((!empty($orphan_parts) || $rcmail->config->get('inline_images', true)) && !empty($attachments)) {
$thumbnail_size = $rcmail->config->get('image_thumbnail_size', 240);
$show_label = rcube::Q($rcmail->gettext('showattachment'));
$download_label = rcube::Q($rcmail->gettext('download'));

foreach (self::$MESSAGE->attachments as $attach_prop) {
foreach ($attachments as $attach_prop) {
// Content-Type: image/*...
if ($mimetype = self::part_image_type($attach_prop)) {
// Skip inline images
if (!self::is_attachment(self::$MESSAGE, $attach_prop)) {
// Skip inline images, unless they are not in the body
if (!self::is_attachment(self::$MESSAGE, $attach_prop) && !in_array($attach_prop, $orphan_parts)) {
continue;
}

Expand Down
32 changes: 32 additions & 0 deletions program/lib/Roundcube/rcube_message.php
Original file line number Diff line number Diff line change
Expand Up @@ -1030,6 +1030,38 @@ private function parse_structure($structure, $recursive = false)
}
}

/**
* Return list of orphan parts: inline images that are not included in the message HTML body
* @param array $used_cids List of used CIDs in body
* @return array
*/
public function get_orphan_inline_parts($used_cids)
{
$orphan_parts = [];

foreach ($this->inline_parts as $i => $part) {
$id = (string) $part->mime_id;

// The orphan part will not be returned if it is already in the attachments list
if (array_key_exists($id, $this->attachments)) {
continue;
}

if (isset($part->content_id)) {
$find = 'cid:' . $part->content_id;
}
elseif (!empty($part->content_location)) {
$find = $part->content_location;
}

if (!in_array($find, $used_cids)) {

Check failure on line 1057 in program/lib/Roundcube/rcube_message.php

View workflow job for this annotation

GitHub Actions / Static Analysis

Variable $find might not be defined.
$orphan_parts[$id] = $part;
}
}

return $orphan_parts;
}

/**
* Fill a flat array with references to all parts, indexed by part numbers
*
Expand Down
15 changes: 15 additions & 0 deletions program/lib/Roundcube/rcube_washtml.php
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,9 @@ class rcube_washtml
/** @var string A prefix to be added to id/class/for attribute values */
private $_css_prefix;

/** @var array List of CIDs used in body */
private $_used_cids = [];

/** @var int Max nesting level */
private $max_nesting_level;

Expand Down Expand Up @@ -229,6 +232,16 @@ public function __construct($p = [])
}
}

/**
* Return list of CIDs that haven't been used in HTML
*
* @return array
*/
public function get_used_cids()
{
return array_keys($this->_used_cids);
}

/**
* Register a callback function for a certain tag
*
Expand Down Expand Up @@ -387,11 +400,13 @@ private function wash_attribs($node)
private function wash_uri($uri, $blocked_source = false, $is_image = true)
{
if (!empty($this->config['cid_map'][$uri])) {
$this->_used_cids[$uri] = true;
return $this->config['cid_map'][$uri];
}

$key = $this->config['base_url'] . $uri;
if (!empty($this->config['cid_map'][$key])) {
$this->_used_cids[$key] = true;
return $this->config['cid_map'][$key];
}

Expand Down

0 comments on commit c56b669

Please sign in to comment.