From ec603f7da6e0dcae398169efe03b52d199427d11 Mon Sep 17 00:00:00 2001
From: alecpl <alec@alec.pl>
Date: Wed, 18 Feb 2009 14:15:36 -0500
Subject: [PATCH] - Fix html body washing on reply/forward + fix attachments handling (#1485676)

---
 CHANGELOG                      |    4 
 program/steps/mail/compose.inc |   81 +++++++++++-----
 program/steps/mail/func.inc    |  187 ++++++++++++++++++++++--------------
 program/steps/mail/show.inc    |   18 ---
 4 files changed, 174 insertions(+), 116 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 825fbb2..0f81775 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,10 @@
 CHANGELOG RoundCube Webmail
 ---------------------------
 
+2009/02/18 (alec)
+----------
+- Fix html body washing on reply/forward + fix attachments handling (#1485676)
+
 2009/02/13 (alec)
 ----------
 - Fix multiple recipients input parsing (#1485733)
diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc
index 7dfa967..1b83b1e 100644
--- a/program/steps/mail/compose.inc
+++ b/program/steps/mail/compose.inc
@@ -381,16 +381,6 @@
     // load draft message body
     else if ($compose_mode == RCUBE_COMPOSE_DRAFT)
       $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']))
   {
@@ -506,13 +496,19 @@
   }
   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",
       $MESSAGE->headers->date,
       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%">';
     $suffix = "</blockquote><p></p>";
-
-    rcmail_write_inline_attachments($MESSAGE);
   }
 
   return $prefix.$body.$suffix;
@@ -522,6 +518,10 @@
 function rcmail_create_forward_body($body, $bodyIsHtml)
 {
   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)
   {
@@ -536,6 +536,11 @@
   }
   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(
       "<br><br>-------- Original Message --------" .
         "<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tbody>" .
@@ -554,10 +559,6 @@
 
     $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;
 }
@@ -565,7 +566,7 @@
 
 function rcmail_create_draft_body($body, $bodyIsHtml)
 {
-  global $MESSAGE;
+  global $MESSAGE, $OUTPUT;
   
   /**
    * add attachments
@@ -574,39 +575,67 @@
   if (!isset($_SESSION['compose']['forward_attachments'])
       && is_array($MESSAGE->mime_parts)
       && 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;
 }
   
   
 function rcmail_write_compose_attachments(&$message, $bodyIsHtml)
 {
+  global $OUTPUT;
+  
+  $cid_map = array();
+  $id = 0;
+  
   foreach ((array)$message->mime_parts as $pid => $part)
   {
     if (($part->ctype_primary != 'message' || !$bodyIsHtml) &&
         ($part->disposition=='attachment' || $part->disposition=='inline' || $part->headers['content-id']
          || (empty($part->disposition) && $part->filename)))
     {
-      if ($attachment = rcmail_save_attachment($message, $pid))
-        $_SESSION['compose']['attachments'][] = $attachment;
+      if ($attachment = rcmail_save_attachment($message, $pid)) {
+        $_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;
+
+  return $cid_map;
 }
 
 
 function rcmail_write_inline_attachments(&$message)
 {
-  foreach ((array)$message->mime_parts as $pid => $part)
-  {
-    if ($part->content_id && $part->filename)
-    {
-      if ($attachment = rcmail_save_attachment($message, $pid))
-        $_SESSION['compose']['attachments'][] = $attachment;
+  global $OUTPUT;
+
+  $cid_map = array();
+  $id = 0;
+  
+  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)
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index f54d4fa..2e85a40 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -19,7 +19,6 @@
 
 */
 
-require_once('lib/enriched.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';
@@ -611,22 +610,128 @@
     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
  * which can be displayed the message view
  *
  * @param object rcube_message_part Message part
- * @param bool  True if external objects (ie. images ) are allowed
- * @param bool  True if part should be converted to plaintext
+ * @param array  Display parameters array 
  * @return string Formatted HTML string
  */
 function rcmail_print_body($part, $p = array())
 {
-  global $REMOTE_OBJECTS;
-  
   $p += array('safe' => false, 'plain' => false, 'inline_html' => true);
-  
+
   // convert html to text/plain
   if ($part->ctype_secondary == 'html' && $p['plain']) {
     $txt = new html2text($part->body, false, true);
@@ -635,77 +740,12 @@
   }
   // text/html
   else if ($part->ctype_secondary == 'html') {
-    $html = $part->body;
-
-    // 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;
+    return rcmail_wash_html($part->body, $p, $part->replaces);
   }
   // text/enriched
   else if ($part->ctype_secondary=='enriched') {
     $part->ctype_secondary = 'html';
+    require_once('lib/enriched.inc');
     return Q(enriched_to_html($part->body), 'show');
   }
   else
@@ -757,6 +797,7 @@
   return html::tag('pre', array(), $body);
 }
 
+
 /**
  * add a string to the replacement array and return a replacement string
  */
diff --git a/program/steps/mail/show.inc b/program/steps/mail/show.inc
index 7ae0ae0..fd31fa9 100644
--- a/program/steps/mail/show.inc
+++ b/program/steps/mail/show.inc
@@ -43,23 +43,7 @@
   $mbox_name = $IMAP->get_mailbox_name();
   
   // show images?
-  $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;
-    }
-  }
+  rcmail_check_safe($MESSAGE);
 
   // calculate Etag for this request
   $etag = md5($MESSAGE->uid.$mbox_name.session_id()

--
Gitblit v1.9.1