summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoralecpl <alec@alec.pl>2010-09-14 08:40:51 +0000
committeralecpl <alec@alec.pl>2010-09-14 08:40:51 +0000
commit99b8c1e2ac619665c0514b15d8efb5defabb8090 (patch)
tree049d9580c1f5c8b4aefd40c7a584da7f7b2fa9c2
parentfc552950d30d752f856a7c686c4cbc74e4b0da4b (diff)
- Fix format=flowed handling (#1486989) + small improvements in plain messages parsing
-rw-r--r--CHANGELOG1
-rw-r--r--program/include/rcube_message.php86
-rw-r--r--program/steps/mail/func.inc113
-rw-r--r--program/steps/mail/sendmail.inc6
4 files changed, 139 insertions, 67 deletions
diff --git a/CHANGELOG b/CHANGELOG
index e0739b6cc..f6a99a2d8 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -21,6 +21,7 @@ CHANGELOG RoundCube Webmail
- Re-added 'Close' button in upload form (#1486930, #1486823)
- Fix handling of charsets with LATIN-* label
- Fix messages background image handling in some cases (#1486990)
+- Fix format=flowed handling (#1486989)
RELEASE 0.4
-----------
diff --git a/program/include/rcube_message.php b/program/include/rcube_message.php
index ba59bae07..a15117e19 100644
--- a/program/include/rcube_message.php
+++ b/program/include/rcube_message.php
@@ -214,23 +214,22 @@ class rcube_message
if ($mimetype == 'text/plain') {
$out = $this->imap->get_message_part($this->uid, $mime_id, $part);
-
+
// re-format format=flowed content
if ($part->ctype_secondary == 'plain' && $part->ctype_parameters['format'] == 'flowed')
$out = self::unfold_flowed($out);
break;
}
else if ($mimetype == 'text/html') {
- $html_part = $this->imap->get_message_part($this->uid, $mime_id, $part);
+ $out = $this->imap->get_message_part($this->uid, $mime_id, $part);
// remove special chars encoding
$trans = array_flip(get_html_translation_table(HTML_ENTITIES));
- $html_part = strtr($html_part, $trans);
+ $out = strtr($out, $trans);
// create instance of html2text class
- $txt = new html2text($html_part);
+ $txt = new html2text($out);
$out = $txt->get_text();
- break;
}
}
@@ -604,10 +603,51 @@ class rcube_message
*/
public static function unfold_flowed($text)
{
- return preg_replace(
- array('/-- (\r?\n)/', '/^ /m', '/(.) \r?\n/', '/--%SIGEND%(\r?\n)/'),
- array('--%SIGEND%\\1', '', '\\1 ', '-- \\1'),
- $text);
+ $text = preg_split('/\r?\n/', $text);
+ $last = -1;
+ $q_level = 0;
+
+ foreach ($text as $idx => $line) {
+ if ($line[0] == '>' && preg_match('/^(>+\s*)/', $line, $regs)) {
+ $q = strlen(str_replace(' ', '', $regs[0]));
+ $line = substr($line, strlen($regs[0]));
+
+ if ($q == $q_level && isset($text[$last])
+ && $text[$last][strlen($text[$last])-1] == ' '
+ ) {
+ $text[$last] .= $line;
+ unset($text[$idx]);
+ }
+ else {
+ $last = $idx;
+ }
+ }
+ else {
+ $q = 0;
+ if ($line == '-- ') {
+ $last = $idx;
+ }
+ else {
+ // remove space-stuffing
+ $line = preg_replace('/^\s/', '', $line);
+
+ if (isset($text[$last])
+ && $text[$last] != '-- '
+ && $text[$last][strlen($text[$last])-1] == ' '
+ ) {
+ $text[$last] .= $line;
+ unset($text[$idx]);
+ }
+ else {
+ $text[$idx] = $line;
+ $last = $idx;
+ }
+ }
+ }
+ $q_level = $q;
+ }
+
+ return implode("\r\n", $text);
}
@@ -616,17 +656,25 @@ class rcube_message
*/
public static function format_flowed($text, $length = 72)
{
- $out = '';
-
- foreach (preg_split('/\r?\n/', trim($text)) as $line) {
- // don't wrap quoted lines (to avoid wrapping problems)
- if ($line[0] != '>')
- $line = rc_wordwrap(rtrim($line, "\r\n"), $length - 1, " \r\n");
-
- $out .= $line . "\r\n";
+ $text = preg_split('/\r?\n/', $text);
+
+ foreach ($text as $idx => $line) {
+ if ($line != '-- ') {
+ if ($line[0] == '>' && preg_match('/^(>+)/', $line, $regs)) {
+ $prefix = $regs[0];
+ $level = strlen($prefix);
+ $line = rtrim(substr($line, $level));
+ $line = $prefix . rc_wordwrap($line, $length - $level - 2, " \r\n$prefix ");
+ }
+ else {
+ $line = ' ' . rc_wordwrap(rtrim($line), $length - 2, " \r\n ");
+ }
+
+ $text[$idx] = $line;
+ }
}
-
- return $out;
+
+ return implode("\r\n", $text);
}
}
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index 68b790cd9..75d7b9098 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -735,7 +735,7 @@ function rcmail_print_body($part, $p = array())
// plaintext postprocessing
if ($part->ctype_secondary == 'plain')
- $body = rcmail_plain_body($body);
+ $body = rcmail_plain_body($body, $part->ctype_parameters['format'] == 'flowed');
// allow post-processing of the message body
$data = $RCMAIL->plugins->exec_hook('message_part_after', array('type' => $part->ctype_secondary, 'body' => $body) + $data);
@@ -747,75 +747,102 @@ function rcmail_print_body($part, $p = array())
/**
* Handle links and citation marks in plain text message
*
- * @param string Plain text string
+ * @param string Plain text string
+ * @param boolean Text uses format=flowed
+ *
* @return string Formatted HTML string
*/
-function rcmail_plain_body($body)
+function rcmail_plain_body($body, $flowed=false)
{
// make links and email-addresses clickable
- $replacements = new rcube_string_replacer;
+ $replacer = new rcube_string_replacer;
// search for patterns like links and e-mail addresses
- $body = preg_replace_callback($replacements->link_pattern, array($replacements, 'link_callback'), $body);
- $body = preg_replace_callback($replacements->mailto_pattern, array($replacements, 'mailto_callback'), $body);
+ $body = preg_replace_callback($replacer->link_pattern, array($replacer, 'link_callback'), $body);
+ $body = preg_replace_callback($replacer->mailto_pattern, array($replacer, 'mailto_callback'), $body);
// split body into single lines
$a_lines = preg_split('/\r?\n/', $body);
- $q_lines = array();
$quote_level = 0;
+ $last = -1;
// find/mark quoted lines...
for ($n=0, $cnt=count($a_lines); $n < $cnt; $n++) {
- $q = 0;
-
if ($a_lines[$n][0] == '>' && preg_match('/^(>+\s*)+/', $a_lines[$n], $regs)) {
$q = strlen(preg_replace('/\s/', '', $regs[0]));
- $a_lines[$n] = substr($a_lines[$n], strlen($regs[0]));
+ $a_lines[$n] = substr($a_lines[$n], strlen($regs[0]));
if ($q > $quote_level)
- $q_lines[$n]['quote'] = $q - $quote_level;
+ $a_lines[$n] = $replacer->get_replacement($replacer->add(
+ str_repeat('<blockquote>', $q - $quote_level))) . $a_lines[$n];
else if ($q < $quote_level)
- $q_lines[$n]['endquote'] = $quote_level - $q;
+ $a_lines[$n] = $replacer->get_replacement($replacer->add(
+ str_repeat('</blockquote>', $quote_level - $q))) . $a_lines[$n];
+ else if ($flowed) {
+ // previous line is flowed
+ if (isset($a_lines[$last])
+ && $a_lines[$last][strlen($a_lines[$last])-1] == ' ') {
+ // merge lines (and remove space-stuffing)
+ $a_lines[$last] .= $a_lines[$n];
+ unset($a_lines[$n]);
+ }
+ else
+ $last = $n;
+ }
+ }
+ else {
+ $q = 0;
+ if ($flowed) {
+ // sig separator - line is fixed
+ if ($a_lines[$n] == '-- ') {
+ $last = $n;
+ }
+ else {
+ // remove space-stuffing
+ if ($a_lines[$n][0] == ' ')
+ $a_lines[$n] = substr($a_lines[$n], 1);
+
+ // previous line is flowed?
+ if (isset($a_lines[$last])
+ && $a_lines[$last] != '-- '
+ && $a_lines[$last][strlen($a_lines[$last])-1] == ' '
+ ) {
+ $a_lines[$last] .= $a_lines[$n];
+ unset($a_lines[$n]);
+ }
+ else {
+ $last = $n;
+ }
+ }
+ if ($quote_level > 0)
+ $a_lines[$last] = $replacer->get_replacement($replacer->add(
+ str_repeat('</blockquote>', $quote_level))) . $a_lines[$last];
+ }
+ else if ($quote_level > 0)
+ $a_lines[$n] = $replacer->get_replacement($replacer->add(
+ str_repeat('</blockquote>', $quote_level))) . $a_lines[$n];
}
- else if ($quote_level > 0)
- $q_lines[$n]['endquote'] = $quote_level;
$quote_level = $q;
}
// quote plain text
- $body = Q(join("\n", $a_lines), 'replace', false);
+ $body = Q(join("\n", $a_lines), '', false);
// colorize signature
- if (($sp = strrpos($body, '-- ')) !== false)
- if (($sp == 0 || $body[$sp-1] == "\n") && $body[$sp+3] == "\n") {
- $body = substr($body, 0, max(0, $sp))
- .'<span class="sig">'.substr($body, $sp).'</span>';
+ if (($sp = strrpos($body, "-- \n")) !== false) {
+ if (($sp == 0 || $body[$sp-1] == "\n")) {
+ // do not touch blocks with more that 10 lines
+ if (substr_count($body, "\n", $sp) < 10)
+ $body = substr($body, 0, max(0, $sp))
+ .'<span class="sig">'.substr($body, $sp).'</span>';
}
+ }
- // colorize quoted lines
- $a_lines = preg_split('/\n/', $body);
- foreach ($q_lines as $i => $q)
- if ($q['quote'])
- $a_lines[$i] = str_repeat('<blockquote>', $q['quote']) . $a_lines[$i];
- else if ($q['endquote'])
- $a_lines[$i] = str_repeat('</blockquote>', $q['endquote']) . $a_lines[$i];
-
- // insert the links for urls and mailtos
- $body = $replacements->resolve(join("\n", $a_lines));
-
- return $body;
-}
-
+ // insert url/mailto links and citation tags
+ $body = $replacer->resolve($body);
-/**
- * add a string to the replacement array and return a replacement string
- */
-function rcmail_str_replacement($str, &$rep)
-{
- static $count = 0;
- $rep[$count] = stripslashes($str);
- return "##string_replacement{".($count++)."}##";
+ return $body;
}
@@ -983,10 +1010,6 @@ function rcmail_message_body($attrib)
$plugin = $RCMAIL->plugins->exec_hook('message_body_prefix', array(
'part' => $part, 'prefix' => ''));
- // re-format format=flowed content
- if ($part->ctype_secondary == "plain" && $part->ctype_parameters['format'] == "flowed")
- $part->body = rcube_message::unfold_flowed($part->body);
-
$body = rcmail_print_body($part, array('safe' => $safe_mode, 'plain' => !$CONFIG['prefer_html']));
if ($part->ctype_secondary == 'html') {
diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc
index 23ff571b2..3902b9ad3 100644
--- a/program/steps/mail/sendmail.inc
+++ b/program/steps/mail/sendmail.inc
@@ -461,9 +461,9 @@ else {
$message_body = $plugin['body'];
- // compose format=flowed content if enabled and not a reply message
- if (empty($_SESSION['compose']['reply_msgid']) && ($flowed = $RCMAIL->config->get('send_format_flowed', true)))
- $message_body = rcube_message::format_flowed($message_body, $LINE_LENGTH);
+ // compose format=flowed content if enabled
+ if ($flowed = $RCMAIL->config->get('send_format_flowed', true))
+ $message_body = rcube_message::format_flowed($message_body, min($LINE_LENGTH+2, 79));
else
$message_body = rc_wordwrap($message_body, $LINE_LENGTH, "\r\n");