- Fix html body washing on reply/forward + fix attachments handling (#1485676)

release-0.6
alecpl 16 years ago
parent f9a4bc4dfa
commit ec603f7da6

@ -1,6 +1,10 @@
CHANGELOG RoundCube Webmail CHANGELOG RoundCube Webmail
--------------------------- ---------------------------
2009/02/18 (alec)
----------
- Fix html body washing on reply/forward + fix attachments handling (#1485676)
2009/02/13 (alec) 2009/02/13 (alec)
---------- ----------
- Fix multiple recipients input parsing (#1485733) - Fix multiple recipients input parsing (#1485733)

@ -381,16 +381,6 @@ function rcmail_compose_body($attrib)
// load draft message body // load draft message body
else if ($compose_mode == RCUBE_COMPOSE_DRAFT) else if ($compose_mode == RCUBE_COMPOSE_DRAFT)
$body = rcmail_create_draft_body($body, $isHtml); $body = rcmail_create_draft_body($body, $isHtml);
if ($isHtml) {
// replace cid with href in inline images links
foreach ((array)$_SESSION['compose']['attachments'] as $pid => $attachment) {
if ($attachment['content_id']) {
$body = str_replace('cid:'. $attachment['content_id'],
$OUTPUT->app->comm_path.'&_action=display-attachment&_file=rcmfile'.$pid, $body);
}
}
}
} }
else if (!empty($_SESSION['compose']['param']['_body'])) else if (!empty($_SESSION['compose']['param']['_body']))
{ {
@ -506,13 +496,19 @@ function rcmail_create_reply_body($body, $bodyIsHtml)
} }
else else
{ {
// save inline images to files
$cid_map = rcmail_write_inline_attachments($MESSAGE);
// set is_safe flag (we need this for html body washing)
rcmail_check_safe($MESSAGE);
// clean up html tags
$body = rcmail_wash_html($body, array('safe' => $MESSAGE->is_safe), $cid_map);
// build reply (quote content)
$prefix = sprintf("On %s, %s wrote:<br />\n", $prefix = sprintf("On %s, %s wrote:<br />\n",
$MESSAGE->headers->date, $MESSAGE->headers->date,
htmlspecialchars(Q($MESSAGE->get_header('from'), 'replace'), ENT_COMPAT, $OUTPUT->get_charset())); htmlspecialchars(Q($MESSAGE->get_header('from'), 'replace'), ENT_COMPAT, $OUTPUT->get_charset()));
$prefix .= '<blockquote type="cite" style="padding-left:5px; border-left:#1010ff 2px solid; margin-left:5px; width:100%">'; $prefix .= '<blockquote type="cite" style="padding-left:5px; border-left:#1010ff 2px solid; margin-left:5px; width:100%">';
$suffix = "</blockquote><p></p>"; $suffix = "</blockquote><p></p>";
rcmail_write_inline_attachments($MESSAGE);
} }
return $prefix.$body.$suffix; return $prefix.$body.$suffix;
@ -523,6 +519,10 @@ function rcmail_create_forward_body($body, $bodyIsHtml)
{ {
global $IMAP, $MESSAGE, $OUTPUT; global $IMAP, $MESSAGE, $OUTPUT;
// add attachments
if (!isset($_SESSION['compose']['forward_attachments']) && is_array($MESSAGE->mime_parts))
$cid_map = rcmail_write_compose_attachments($MESSAGE, $bodyIsHtml);
if (!$bodyIsHtml) if (!$bodyIsHtml)
{ {
$prefix = "\n\n\n-------- Original Message --------\n"; $prefix = "\n\n\n-------- Original Message --------\n";
@ -536,6 +536,11 @@ function rcmail_create_forward_body($body, $bodyIsHtml)
} }
else else
{ {
// set is_safe flag (we need this for html body washing)
rcmail_check_safe($MESSAGE);
// clean up html tags
$body = rcmail_wash_html($body, array('safe' => $MESSAGE->is_safe), $cid_map);
$prefix = sprintf( $prefix = sprintf(
"<br><br>-------- Original Message --------" . "<br><br>-------- Original Message --------" .
"<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tbody>" . "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tbody>" .
@ -554,10 +559,6 @@ function rcmail_create_forward_body($body, $bodyIsHtml)
$prefix .= "</tbody></table><br>"; $prefix .= "</tbody></table><br>";
} }
// add attachments
if (!isset($_SESSION['compose']['forward_attachments']) && is_array($MESSAGE->mime_parts))
rcmail_write_compose_attachments($MESSAGE, $bodyIsHtml);
return $prefix.$body; return $prefix.$body;
} }
@ -565,7 +566,7 @@ function rcmail_create_forward_body($body, $bodyIsHtml)
function rcmail_create_draft_body($body, $bodyIsHtml) function rcmail_create_draft_body($body, $bodyIsHtml)
{ {
global $MESSAGE; global $MESSAGE, $OUTPUT;
/** /**
* add attachments * add attachments
@ -574,39 +575,67 @@ function rcmail_create_draft_body($body, $bodyIsHtml)
if (!isset($_SESSION['compose']['forward_attachments']) if (!isset($_SESSION['compose']['forward_attachments'])
&& is_array($MESSAGE->mime_parts) && is_array($MESSAGE->mime_parts)
&& count($MESSAGE->mime_parts) > 0) && count($MESSAGE->mime_parts) > 0)
rcmail_write_compose_attachments($MESSAGE, $bodyIsHtml); {
$cid_map = rcmail_write_compose_attachments($MESSAGE, $bodyIsHtml);
// replace cid with href in inline images links
if ($cid_map)
$body = str_replace(array_keys($cid_map), array_values($cid_map), $body);
}
return $body; return $body;
} }
function rcmail_write_compose_attachments(&$message, $bodyIsHtml) function rcmail_write_compose_attachments(&$message, $bodyIsHtml)
{ {
global $OUTPUT;
$cid_map = array();
$id = 0;
foreach ((array)$message->mime_parts as $pid => $part) foreach ((array)$message->mime_parts as $pid => $part)
{ {
if (($part->ctype_primary != 'message' || !$bodyIsHtml) && if (($part->ctype_primary != 'message' || !$bodyIsHtml) &&
($part->disposition=='attachment' || $part->disposition=='inline' || $part->headers['content-id'] ($part->disposition=='attachment' || $part->disposition=='inline' || $part->headers['content-id']
|| (empty($part->disposition) && $part->filename))) || (empty($part->disposition) && $part->filename)))
{ {
if ($attachment = rcmail_save_attachment($message, $pid)) if ($attachment = rcmail_save_attachment($message, $pid)) {
$_SESSION['compose']['attachments'][] = $attachment; $_SESSION['compose']['attachments'][$id] = $attachment;
if ($bodyIsHtml && $part->filename && $part->content_id) {
$cid_map['cid:'.$part->content_id] =
$OUTPUT->app->comm_path.'&_action=display-attachment&_file=rcmfile'.$id;
}
$id++;
}
} }
} }
$_SESSION['compose']['forward_attachments'] = true; $_SESSION['compose']['forward_attachments'] = true;
return $cid_map;
} }
function rcmail_write_inline_attachments(&$message) function rcmail_write_inline_attachments(&$message)
{ {
foreach ((array)$message->mime_parts as $pid => $part) global $OUTPUT;
{
if ($part->content_id && $part->filename) $cid_map = array();
{ $id = 0;
if ($attachment = rcmail_save_attachment($message, $pid))
$_SESSION['compose']['attachments'][] = $attachment; foreach ((array)$message->mime_parts as $pid => $part) {
if ($part->content_id && $part->filename) {
if ($attachment = rcmail_save_attachment($message, $pid)) {
$_SESSION['compose']['attachments'][$id] = $attachment;
$cid_map['cid:'.$part->content_id] =
$OUTPUT->app->comm_path.'&_action=display-attachment&_file=rcmfile'.$id;
$id++;
}
} }
} }
return $cid_map;
} }
function rcmail_save_attachment(&$message, $pid) function rcmail_save_attachment(&$message, $pid)

@ -19,7 +19,6 @@
*/ */
require_once('lib/enriched.inc');
require_once('include/rcube_smtp.inc'); require_once('include/rcube_smtp.inc');
$EMAIL_ADDRESS_PATTERN = '/([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9]\\.[a-z]{2,5})/i'; $EMAIL_ADDRESS_PATTERN = '/([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9]([a-z0-9\-][.]?)*[a-z0-9]\\.[a-z]{2,5})/i';
@ -611,22 +610,128 @@ function rcmail_get_mailbox_name_text()
return rcmail_localize_foldername($RCMAIL->imap->get_mailbox_name()); return rcmail_localize_foldername($RCMAIL->imap->get_mailbox_name());
} }
/**
* Sets message is_safe flag according to 'show_images' option value
*
* @param object rcube_message Message
*/
function rcmail_check_safe(&$message)
{
global $RCMAIL;
$show_images = $RCMAIL->config->get('show_images');
if (!$message->is_safe
&& !empty($show_images)
&& $message->has_html_part())
{
switch($show_images) {
case '1': // known senders only
$CONTACTS = new rcube_contacts($DB, $_SESSION['user_id']);
if ($CONTACTS->search('email', $message->sender['mailto'], true, false)->count) {
$message->set_safe(true);
}
break;
case '2': // always
$message->set_safe(true);
break;
}
}
}
/**
* Cleans up the given message HTML Body (for displaying)
*
* @param string HTML
* @param array Display parameters
* @param array CID map replaces (inline images)
* @return string Clean HTML
*/
function rcmail_wash_html($html, $p = array(), $cid_replaces)
{
global $REMOTE_OBJECTS;
$p += array('safe' => false, 'inline_html' => true);
// special replacements (not properly handled by washtml class)
$html_search = array(
'/(<\/nobr>)(\s+)(<nobr>)/i', // space(s) between <NOBR>
'/(<[\/]*st1:[^>]+>)/i', // Microsoft's Smart Tags <ST1>
'/<\/?rte_text>/i', // Rich Text Editor tags (#1485647)
'/<title>.*<\/title>/i', // PHP bug #32547 workaround: remove title tag
'/<html[^>]*>/im', // malformed html: remove html tags (#1485139)
'/<\/html>/i', // malformed html: remove html tags (#1485139)
'/^[\xFE\xFF\xBB\xBF\x00]+((?:<\!doctype|\<html))/im', // remove byte-order mark (only outlook?)
);
$html_replace = array(
'\\1'.' &nbsp; '.'\\3',
'',
'',
'',
'',
'',
'\\1',
);
$html = preg_replace($html_search, $html_replace, $html);
// charset was converted to UTF-8 in rcube_imap::get_message_part() -> change charset specification in HTML accordingly
$charset_pattern = '/(\s+content=[\'"]?\w+\/\w+;\s*charset)=([a-z0-9-_]+)/i';
if (preg_match($charset_pattern, $html)) {
$html = preg_replace($charset_pattern, '\\1='.RCMAIL_CHARSET, $html);
}
else {
// add head for malformed messages, washtml cannot work without that
if (!preg_match('/<head[^>]*>(.*)<\/head>/Uims', $html))
$html = '<head></head>'. $html;
$html = substr_replace($html, '<meta http-equiv="content-type" content="text/html; charset='.RCMAIL_CHARSET.'" />', intval(stripos($html, '<head>')+6), 0);
}
// turn relative into absolute urls
$html = rcmail_resolve_base($html);
// clean HTML with washhtml by Frederic Motte
$wash_opts = array(
'show_washed' => false,
'allow_remote' => $p['safe'],
'blocked_src' => "./program/blocked.gif",
'charset' => RCMAIL_CHARSET,
'cid_map' => $cid_replaces,
'html_elements' => array('body'),
);
if (!$p['inline_html']) {
$wash_opts['html_elements'] = array('html','head','title','body');
}
if ($p['safe']) {
$wash_opts['html_elements'][] = 'link';
$wash_opts['html_attribs'] = array('rel','type');
}
$washer = new washtml($wash_opts);
$washer->add_callback('form', 'rcmail_washtml_callback');
if ($p['safe']) { // allow CSS styles, will be sanitized by rcmail_washtml_callback()
$washer->add_callback('style', 'rcmail_washtml_callback');
}
$html = $washer->wash($html);
$REMOTE_OBJECTS = $washer->extlinks;
return $html;
}
/** /**
* Convert the given message part to proper HTML * Convert the given message part to proper HTML
* which can be displayed the message view * which can be displayed the message view
* *
* @param object rcube_message_part Message part * @param object rcube_message_part Message part
* @param bool True if external objects (ie. images ) are allowed * @param array Display parameters array
* @param bool True if part should be converted to plaintext
* @return string Formatted HTML string * @return string Formatted HTML string
*/ */
function rcmail_print_body($part, $p = array()) function rcmail_print_body($part, $p = array())
{ {
global $REMOTE_OBJECTS;
$p += array('safe' => false, 'plain' => false, 'inline_html' => true); $p += array('safe' => false, 'plain' => false, 'inline_html' => true);
// convert html to text/plain // convert html to text/plain
if ($part->ctype_secondary == 'html' && $p['plain']) { if ($part->ctype_secondary == 'html' && $p['plain']) {
$txt = new html2text($part->body, false, true); $txt = new html2text($part->body, false, true);
@ -635,77 +740,12 @@ function rcmail_print_body($part, $p = array())
} }
// text/html // text/html
else if ($part->ctype_secondary == 'html') { else if ($part->ctype_secondary == 'html') {
$html = $part->body; return rcmail_wash_html($part->body, $p, $part->replaces);
// special replacements (not properly handled by washtml class)
$html_search = array(
'/(<\/nobr>)(\s+)(<nobr>)/i', // space(s) between <NOBR>
'/(<[\/]*st1:[^>]+>)/i', // Microsoft's Smart Tags <ST1>
'/<\/?rte_text>/i', // Rich Text Editor tags (#1485647)
'/<title>.*<\/title>/i', // PHP bug #32547 workaround: remove title tag
'/<html[^>]*>/im', // malformed html: remove html tags (#1485139)
'/<\/html>/i', // malformed html: remove html tags (#1485139)
'/^[\xFE\xFF\xBB\xBF\x00]+((?:<\!doctype|\<html))/im', // remove byte-order mark (only outlook?)
);
$html_replace = array(
'\\1'.' &nbsp; '.'\\3',
'',
'',
'',
'',
'',
'\\1',
);
$html = preg_replace($html_search, $html_replace, $html);
// charset was converted to UTF-8 in rcube_imap::get_message_part() -> change charset specification in HTML accordingly
$charset_pattern = '/(\s+content=[\'"]?\w+\/\w+;\s*charset)=([a-z0-9-_]+)/i';
if (preg_match($charset_pattern, $html)) {
$html = preg_replace($charset_pattern, '\\1='.RCMAIL_CHARSET, $html);
}
else {
// add head for malformed messages, washtml cannot work without that
if (!preg_match('/<head[^>]*>(.*)<\/head>/Uims', $html))
$html = '<head></head>'. $html;
$html = substr_replace($html, '<meta http-equiv="content-type" content="text/html; charset='.RCMAIL_CHARSET.'" />', intval(stripos($html, '<head>')+6), 0);
}
// turn relative into absolute urls
$html = rcmail_resolve_base($html);
// clean HTML with washhtml by Frederic Motte
$wash_opts = array(
'show_washed' => false,
'allow_remote' => $p['safe'],
'blocked_src' => "./program/blocked.gif",
'charset' => RCMAIL_CHARSET,
'cid_map' => $part->replaces,
'html_elements' => array('body'),
);
if (!$p['inline_html']) {
$wash_opts['html_elements'] = array('html','head','title','body');
}
if ($p['safe']) {
$wash_opts['html_elements'][] = 'link';
$wash_opts['html_attribs'] = array('rel','type');
}
$washer = new washtml($wash_opts);
$washer->add_callback('form', 'rcmail_washtml_callback');
if ($p['safe']) { // allow CSS styles, will be sanitized by rcmail_washtml_callback()
$washer->add_callback('style', 'rcmail_washtml_callback');
}
$body = $washer->wash($html);
$REMOTE_OBJECTS = $washer->extlinks;
return $body;
} }
// text/enriched // text/enriched
else if ($part->ctype_secondary=='enriched') { else if ($part->ctype_secondary=='enriched') {
$part->ctype_secondary = 'html'; $part->ctype_secondary = 'html';
require_once('lib/enriched.inc');
return Q(enriched_to_html($part->body), 'show'); return Q(enriched_to_html($part->body), 'show');
} }
else else
@ -757,6 +797,7 @@ function rcmail_print_body($part, $p = array())
return html::tag('pre', array(), $body); return html::tag('pre', array(), $body);
} }
/** /**
* add a string to the replacement array and return a replacement string * add a string to the replacement array and return a replacement string
*/ */

@ -43,23 +43,7 @@ if ($_GET['_uid']) {
$mbox_name = $IMAP->get_mailbox_name(); $mbox_name = $IMAP->get_mailbox_name();
// show images? // show images?
$show_images = $RCMAIL->config->get('show_images'); rcmail_check_safe($MESSAGE);
if(!$MESSAGE->is_safe
&& !empty($show_images)
&& $MESSAGE->has_html_part())
{
switch($show_images) {
case '1': // known senders only
$CONTACTS = new rcube_contacts($DB, $_SESSION['user_id']);
if ($CONTACTS->search('email', $MESSAGE->sender['mailto'], true, false)->count) {
$MESSAGE->set_safe(true);
}
break;
case '2': // always
$MESSAGE->set_safe(true);
break;
}
}
// calculate Etag for this request // calculate Etag for this request
$etag = md5($MESSAGE->uid.$mbox_name.session_id() $etag = md5($MESSAGE->uid.$mbox_name.session_id()

Loading…
Cancel
Save