From 6b6f2e83de0e5b48ba48583206bd456508554540 Mon Sep 17 00:00:00 2001 From: thomascube Date: Thu, 20 May 2010 21:28:30 +0000 Subject: [PATCH] Display and send messages with format=flowed (#1484370), fixes word wrapping issues (#1486543) --- CHANGELOG | 1 + config/main.inc.php.dist | 13 +++++++--- program/include/rcube_message.php | 36 ++++++++++++++++++++++++++ program/steps/mail/compose.inc | 27 ++++--------------- program/steps/mail/func.inc | 43 ++++++++++++++++--------------- program/steps/mail/sendmail.inc | 15 +++++++---- 6 files changed, 84 insertions(+), 51 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 909b8bb55..125c98028 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG RoundCube Webmail =========================== +- Read and send messages with format=flowed (#1484370), fixes word wrapping issues (#1486543) - Fix duplicated attachments when forwarding a message (#1486487) - Fix message/rfc822 attachments containing only attachments are not parsed properly (#1486743) - Fix %00 character in winmail.dat attachments names (#1486738) diff --git a/config/main.inc.php.dist b/config/main.inc.php.dist index 07f18730e..8af724817 100644 --- a/config/main.inc.php.dist +++ b/config/main.inc.php.dist @@ -5,7 +5,7 @@ | Main configuration file | | | | This file is part of the RoundCube Webmail client | - | Copyright (C) 2005-2009, RoundCube Dev. - Switzerland | + | Copyright (C) 2005-2010, RoundCube Dev. - Switzerland | | Licensed under the GNU GPL | | | +-----------------------------------------------------------------------+ @@ -188,10 +188,10 @@ $rcmail_config['max_recipients'] = 0; $rcmail_config['max_group_members'] = 0; // add this user-agent to message headers when sending -$rcmail_config['useragent'] = 'RoundCube Webmail/'.RCMAIL_VERSION; +$rcmail_config['useragent'] = 'Roundcube Webmail/'.RCMAIL_VERSION; // use this name to compose page titles -$rcmail_config['product_name'] = 'RoundCube Webmail'; +$rcmail_config['product_name'] = 'Roundcube Webmail'; // try to load host-specific configuration // see http://trac.roundcube.net/wiki/Howto_Config for more details @@ -214,6 +214,13 @@ $rcmail_config['http_received_header_encrypt'] = false; // leave empty for auto-detection $rcmail_config['mail_header_delimiter'] = NULL; +// number of chars allowed for line when wrapping text. +// text wrapping is done when composing/sending messages +$rcmail_config['line_length'] = 66; + +// send plaintext messages as format=flowed +$rcmail_config['send_format_flowed'] = true; + // session domain: .example.org $rcmail_config['session_domain'] = ''; diff --git a/program/include/rcube_message.php b/program/include/rcube_message.php index 05b0151ef..47fe3d7c1 100644 --- a/program/include/rcube_message.php +++ b/program/include/rcube_message.php @@ -198,6 +198,10 @@ 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') { @@ -477,5 +481,37 @@ class rcube_message } + /** + * Interpret a format=flowed message body according to RFC 2646 + * + * @param string Raw body formatted as flowed text + * @return string Interpreted text with unwrapped lines and stuffed space removed + */ + public static function unfold_flowed($text) + { + return preg_replace( + array('/-- (\r?\n)/', '/^ /m', '/(.) \r?\n/', '/--%SIGEND%(\r?\n)/'), + array('--%SIGEND%\\1', '', '\\1 ', '-- \\1'), + $text); + } + + /** + * Wrap the given text to comply with RFC 2646 + */ + 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), $length - 1, " \r\n"); + + $out .= $line . "\r\n"; + } + + return $out; + } + } diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index 50f11a361..0b18d122a 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -134,7 +134,7 @@ else $OUTPUT->set_env('show_sig', false); // set line length for body wrapping -$LINE_LENGTH = $RCMAIL->config->get('line_length', 75); +$LINE_LENGTH = $RCMAIL->config->get('line_length', 72); if (!empty($msg_uid)) { @@ -597,30 +597,13 @@ function rcmail_create_reply_body($body, $bodyIsHtml) $body = substr($body, 0, max(0, $sp-1)); } - // soft-wrap message first - $body = rcmail_wrap_quoted($body, $LINE_LENGTH); - - $body = rtrim($body, "\r\n"); - - if ($body) { - // split body into single lines - $a_lines = preg_split('/\r?\n/', $body); - - // add > to each line - for ($n=0; $n')===0) - $a_lines[$n] = '>'.$a_lines[$n]; - else - $a_lines[$n] = '> '.$a_lines[$n]; - } - - $body = join("\n", $a_lines); - } + // soft-wrap and quote message text + $body = rcmail_wrap_and_quote(rtrim($body, "\r\n"), $LINE_LENGTH); // add title line(s) - $prefix = rc_wordwrap(sprintf("On %s, %s wrote:\n", + $prefix = sprintf("On %s, %s wrote:\n", $MESSAGE->headers->date, - $MESSAGE->get_header('from')), $LINE_LENGTH); + $MESSAGE->get_header('from')); $suffix = ''; diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index 951e777b1..fcb0229df 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -933,6 +933,10 @@ function rcmail_message_body($attrib) if (!isset($part->body)) $part->body = $MESSAGE->get_part_content($part->mime_id); + // 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') @@ -1161,40 +1165,37 @@ function rcmail_address_string($input, $max=null, $linked=false, $addicon=null) /** * Wrap text to a given number of characters per line - * but respect the mail quotation of replies messages (>) + * but respect the mail quotation of replies messages (>). + * Finally add another quotation level by prpending the lines + * with > * * @param string Text to wrap * @param int The line width * @return string The wrapped text */ -function rcmail_wrap_quoted($text, $max = 76) +function rcmail_wrap_and_quote($text, $length = 72) { // Rebuild the message body with a maximum of $max chars, while keeping quoted message. + $max = min(78, $length + 8); $lines = preg_split('/\r?\n/', trim($text)); $out = ''; foreach ($lines as $line) { - if (strlen($line) > $max) { - if (preg_match('/^([>\s]+)/', $line, $regs)) { - $length = strlen($regs[0]); - $prefix = substr($line, 0, $length); - - // Remove '> ' from the line, then wordwrap() the line - $line = rc_wordwrap(substr($line, $length), $max - $length); - - // Rebuild the line with '> ' at the beginning of each 'subline' - $newline = ''; - foreach (explode("\n", $line) as $l) { - $newline .= $prefix . $l . "\n"; - } - - // Remove the righest newline char - $line = rtrim($newline); - } - else { - $line = rc_wordwrap($line, $max); + // don't wrap already quoted lines + if ($line[0] == '>') + $line = '>' . rtrim($line); + else if (mb_strlen($line) > $max) { + $newline = ''; + foreach(explode("\n", rc_wordwrap($line, $length - 2)) as $l) { + if (strlen($l)) + $newline .= '> ' . $l . "\n"; + else + $newline .= ">\n"; } + $line = rtrim($newline); } + else + $line = '> ' . $line; // Append the line $out .= $line . "\n"; diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc index e7f5b9557..8e43b37a0 100644 --- a/program/steps/mail/sendmail.inc +++ b/program/steps/mail/sendmail.inc @@ -5,7 +5,7 @@ | program/steps/mail/sendmail.inc | | | | This file is part of the RoundCube Webmail client | - | Copyright (C) 2005-2009, RoundCube Dev. - Switzerland | + | Copyright (C) 2005-2010, RoundCube Dev. - Switzerland | | Licensed under the GNU GPL | | | | PURPOSE: | @@ -440,11 +440,16 @@ if ($isHtml) { // look for "emoticon" images from TinyMCE and copy into message as attachments $message_body = rcmail_attach_emoticons($MAIL_MIME); } -else - { - $message_body = rc_wordwrap($message_body, $LINE_LENGTH, "\r\n"); +else { if ($footer) $message_body .= "\r\n" . $footer; + + // 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); + else + $message_body = rc_wordwrap($message_body, min(79, $LINE_LENGTH + 8), "\r\n"); // +8: be generous with quoted lines + $message_body = wordwrap($message_body, 998, "\r\n", true); if (!strlen($message_body)) { // empty message body breaks attachment handling in drafts @@ -503,7 +508,7 @@ $MAIL_MIME->setParam('html_encoding', 'quoted-printable'); $MAIL_MIME->setParam('head_encoding', 'quoted-printable'); $MAIL_MIME->setParam('head_charset', $message_charset); $MAIL_MIME->setParam('html_charset', $message_charset); -$MAIL_MIME->setParam('text_charset', $message_charset); +$MAIL_MIME->setParam('text_charset', $message_charset . ($flowed ? ";\r\n format=flowed" : '')); // encoding subject header with mb_encode provides better results with asian characters if (function_exists('mb_encode_mimeheader'))