From 30cc01f89daea932d15a1a505d25b543913664ac Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 28 Nov 2012 20:21:09 +0100 Subject: Use Delivered-To header as a last resort for identity selection (#1488840) --- program/steps/mail/compose.inc | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'program/steps/mail') diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index 92ec88f1b..60662b382 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -298,7 +298,6 @@ else if (count($MESSAGE->identities)) { $from_idx = null; $found_idx = null; $default_identity = 0; // default identity is always first on the list - $return_path = $MESSAGE->headers->others['return-path']; // Select identity foreach ($MESSAGE->identities as $idx => $ident) { @@ -332,8 +331,8 @@ else if (count($MESSAGE->identities)) { $from_idx = $found_idx; } - // Fallback using Return-Path - if ($from_idx === null && $return_path) { + // Try Return-Path + if ($from_idx === null && ($return_path = $MESSAGE->headers->others['return-path'])) { foreach ($MESSAGE->identities as $idx => $ident) { if (strpos($return_path, str_replace('@', '=', $ident['email_ascii']).'@') !== false) { $from_idx = $idx; @@ -342,6 +341,16 @@ else if (count($MESSAGE->identities)) { } } + // Fallback using Delivered-To + if ($from_idx === null && ($delivered_to = $MESSAGE->headers->others['delivered-to'])) { + foreach ($MESSAGE->identities as $idx => $ident) { + if (in_array($ident['email_ascii'], $delivered_to)) { + $from_idx = $idx; + break; + } + } + } + $ident = $MESSAGE->identities[$from_idx !== null ? $from_idx : $default_identity]; $from_id = $ident['identity_id']; -- cgit v1.2.3 From 0247b89c38c7f7ef1a2111239c6a1c8c13394d93 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 28 Nov 2012 20:40:07 +0100 Subject: Move code for identity selection to function, move identities formatting to rcube_user::list_identities() --- program/lib/Roundcube/rcube_user.php | 15 ++- program/steps/mail/compose.inc | 185 +++++++++++++++++------------------ 2 files changed, 102 insertions(+), 98 deletions(-) (limited to 'program/steps/mail') diff --git a/program/lib/Roundcube/rcube_user.php b/program/lib/Roundcube/rcube_user.php index 864f2e098..b027506ac 100644 --- a/program/lib/Roundcube/rcube_user.php +++ b/program/lib/Roundcube/rcube_user.php @@ -240,10 +240,12 @@ class rcube_user /** * Return a list of all identities linked with this user * - * @param string $sql_add Optional WHERE clauses + * @param string $sql_add Optional WHERE clauses + * @param bool $formatted Format identity email and name + * * @return array List of identities */ - function list_identities($sql_add = '') + function list_identities($sql_add = '', $formatted = false) { $result = array(); @@ -255,6 +257,15 @@ class rcube_user $this->ID); while ($sql_arr = $this->db->fetch_assoc($sql_result)) { + if ($formatted) { + $ascii_email = format_email($sql_arr['email']); + $utf8_email = format_email(rcube_utils::idn_to_utf8($ascii_email)); + + $sql_arr['email_ascii'] = $ascii_email; + $sql_arr['email'] = $utf8_email; + $sql_arr['ident'] = format_email_recipient($ascii_email, $ident['name']); + } + $result[] = $sql_arr; } diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index 60662b382..c039e42c6 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -249,18 +249,7 @@ else { $MESSAGE->compose = array(); // get user's identities -$MESSAGE->identities = $RCMAIL->user->list_identities(); -if (count($MESSAGE->identities)) -{ - foreach ($MESSAGE->identities as $idx => $ident) { - $ident['email'] = format_email($ident['email']); - $email = format_email(rcube_idn_to_utf8($ident['email'])); - - $MESSAGE->identities[$idx]['email_ascii'] = $ident['email']; - $MESSAGE->identities[$idx]['ident'] = format_email_recipient($ident['email'], $ident['name']); - $MESSAGE->identities[$idx]['email'] = $email; - } -} +$MESSAGE->identities = $RCMAIL->user->list_identities(null, true); // Set From field value if (!empty($_POST['_from'])) { @@ -270,92 +259,10 @@ else if (!empty($COMPOSE['param']['from'])) { $MESSAGE->compose['from'] = $COMPOSE['param']['from']; } else if (count($MESSAGE->identities)) { - $a_recipients = array(); - $a_names = array(); - - // extract all recipients of the reply-message - if (is_object($MESSAGE->headers) && in_array($compose_mode, array(RCUBE_COMPOSE_REPLY, RCUBE_COMPOSE_FORWARD))) - { - $a_to = rcube_mime::decode_address_list($MESSAGE->headers->to, null, true, $MESSAGE->headers->charset); - foreach ($a_to as $addr) { - if (!empty($addr['mailto'])) { - $a_recipients[] = format_email($addr['mailto']); - $a_names[] = $addr['name']; - } - } - - if (!empty($MESSAGE->headers->cc)) { - $a_cc = rcube_mime::decode_address_list($MESSAGE->headers->cc, null, true, $MESSAGE->headers->charset); - foreach ($a_cc as $addr) { - if (!empty($addr['mailto'])) { - $a_recipients[] = format_email($addr['mailto']); - $a_names[] = $addr['name']; - } - } - } - } - - $from_idx = null; - $found_idx = null; - $default_identity = 0; // default identity is always first on the list - - // Select identity - foreach ($MESSAGE->identities as $idx => $ident) { - // use From header - if (in_array($compose_mode, array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT))) { - if ($MESSAGE->headers->from == $ident['ident']) { - $from_idx = $idx; - break; - } - } - // reply to yourself - else if ($compose_mode == RCUBE_COMPOSE_REPLY && $MESSAGE->headers->from == $ident['ident']) { - $from_idx = $idx; - break; - } - // use replied message recipients - else if (($found = array_search($ident['email_ascii'], $a_recipients)) !== false) { - if ($found_idx === null) { - $found_idx = $idx; - } - // match identity name - if ($a_names[$found] && $ident['name'] && $a_names[$found] == $ident['name']) { - $from_idx = $idx; - break; - } - } - } - - // If matching by name+address doesn't found any amtches, get first found address (identity) - if ($from_idx === null) { - $from_idx = $found_idx; - } - - // Try Return-Path - if ($from_idx === null && ($return_path = $MESSAGE->headers->others['return-path'])) { - foreach ($MESSAGE->identities as $idx => $ident) { - if (strpos($return_path, str_replace('@', '=', $ident['email_ascii']).'@') !== false) { - $from_idx = $idx; - break; - } - } - } - - // Fallback using Delivered-To - if ($from_idx === null && ($delivered_to = $MESSAGE->headers->others['delivered-to'])) { - foreach ($MESSAGE->identities as $idx => $ident) { - if (in_array($ident['email_ascii'], $delivered_to)) { - $from_idx = $idx; - break; - } - } - } - - $ident = $MESSAGE->identities[$from_idx !== null ? $from_idx : $default_identity]; - $from_id = $ident['identity_id']; + $ident = rcmail_identity_select($MESSAGE, $MESSAGE->identities, $compose_mode); $MESSAGE->compose['from_email'] = $ident['email']; - $MESSAGE->compose['from'] = $from_id; + $MESSAGE->compose['from'] = $ident['identity_id']; } // Set other headers @@ -472,6 +379,92 @@ $MESSAGE_BODY = rcmail_prepare_message_body(); /****** compose mode functions ********/ +function rcmail_identity_select($MESSAGE, $identities, $compose_mode) +{ + $a_recipients = array(); + $a_names = array(); + + // extract all recipients of the reply-message + if (is_object($MESSAGE->headers) && in_array($compose_mode, array(RCUBE_COMPOSE_REPLY, RCUBE_COMPOSE_FORWARD))) { + $a_to = rcube_mime::decode_address_list($MESSAGE->headers->to, null, true, $MESSAGE->headers->charset); + foreach ($a_to as $addr) { + if (!empty($addr['mailto'])) { + $a_recipients[] = format_email($addr['mailto']); + $a_names[] = $addr['name']; + } + } + + if (!empty($MESSAGE->headers->cc)) { + $a_cc = rcube_mime::decode_address_list($MESSAGE->headers->cc, null, true, $MESSAGE->headers->charset); + foreach ($a_cc as $addr) { + if (!empty($addr['mailto'])) { + $a_recipients[] = format_email($addr['mailto']); + $a_names[] = $addr['name']; + } + } + } + } + + $from_idx = null; + $found_idx = null; + $default_identity = 0; // default identity is always first on the list + + // Select identity + foreach ($identities as $idx => $ident) { + // use From header + if (in_array($compose_mode, array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT))) { + if ($MESSAGE->headers->from == $ident['ident']) { + $from_idx = $idx; + break; + } + } + // reply to yourself + else if ($compose_mode == RCUBE_COMPOSE_REPLY && $MESSAGE->headers->from == $ident['ident']) { + $from_idx = $idx; + break; + } + // use replied message recipients + else if (($found = array_search($ident['email_ascii'], $a_recipients)) !== false) { + if ($found_idx === null) { + $found_idx = $idx; + } + // match identity name + if ($a_names[$found] && $ident['name'] && $a_names[$found] == $ident['name']) { + $from_idx = $idx; + break; + } + } + } + + // If matching by name+address doesn't found any amtches, get first found address (identity) + if ($from_idx === null) { + $from_idx = $found_idx; + } + + // Try Return-Path + if ($from_idx === null && ($return_path = $MESSAGE->headers->others['return-path'])) { + foreach ($identities as $idx => $ident) { + if (strpos($return_path, str_replace('@', '=', $ident['email_ascii']).'@') !== false) { + $from_idx = $idx; + break; + } + } + } + + // Fallback using Delivered-To + if ($from_idx === null && ($delivered_to = $MESSAGE->headers->others['delivered-to'])) { + foreach ($identities as $idx => $ident) { + if (in_array($ident['email_ascii'], $delivered_to)) { + $from_idx = $idx; + break; + } + } + } + + return $identities[$from_idx !== null ? $from_idx : $default_identity]; +} + + function rcmail_compose_headers($attrib) { global $MESSAGE; -- cgit v1.2.3 From 8afbc8aeea10894bc97902912460cafd4681a42d Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 30 Nov 2012 11:13:43 +0100 Subject: Fix attachment Content-Length for in-memory files (#1488844) --- program/steps/mail/get.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'program/steps/mail') diff --git a/program/steps/mail/get.inc b/program/steps/mail/get.inc index 71a5e1b02..7b2f719c6 100644 --- a/program/steps/mail/get.inc +++ b/program/steps/mail/get.inc @@ -302,7 +302,7 @@ else if (strlen($pid = get_input_value('_part', RCUBE_INPUT_GET))) { // send part as-it-is else { if ($part->body) { - header("Content-Length: " . sizeof($part->body)); + header("Content-Length: " . strlen($part->body)); echo $part->body; $sent = true; } -- cgit v1.2.3 From 0fa54df638a0b0f514d1bfba3cefb93e38991a35 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Sat, 1 Dec 2012 20:02:34 +0100 Subject: enriched.inc -> rcube_enriched --- program/include/bc.php | 6 +- program/lib/Roundcube/rcube_enriched.php | 147 +++++++++++++++++++++++++++++++ program/lib/enriched.inc | 114 ------------------------ program/steps/mail/compose.inc | 9 +- program/steps/mail/func.inc | 3 +- tests/Framework/Enriched.php | 74 ++++++++++++++++ tests/phpunit.xml | 1 + 7 files changed, 231 insertions(+), 123 deletions(-) create mode 100644 program/lib/Roundcube/rcube_enriched.php delete mode 100644 program/lib/enriched.inc create mode 100644 tests/Framework/Enriched.php (limited to 'program/steps/mail') diff --git a/program/include/bc.php b/program/include/bc.php index 5047e0a84..12110c0ad 100644 --- a/program/include/bc.php +++ b/program/include/bc.php @@ -399,7 +399,11 @@ function get_boolean($str) return rcube_utils::get_boolean($str); } +function enriched_to_html($data) +{ + return rcube_enriched::to_html($data); +} + class rcube_html_page extends rcmail_html_page { - } diff --git a/program/lib/Roundcube/rcube_enriched.php b/program/lib/Roundcube/rcube_enriched.php new file mode 100644 index 000000000..8b64fe054 --- /dev/null +++ b/program/lib/Roundcube/rcube_enriched.php @@ -0,0 +1,147 @@ + | + | Author: Ryo Chijiiwa (IlohaMail) | + +-----------------------------------------------------------------------+ +*/ + + +/** + * Class for Enriched to HTML conversion + * + * @package Framework + * @subpackage Utils + */ +class rcube_enriched +{ + protected static function convert_newlines($body) + { + // remove single newlines, convert N newlines to N-1 + $body = str_replace("\r\n", "\n", $body); + $len = strlen($body); + $nl = 0; + $out = ''; + + for ($i=0; $i<$len; $i++) { + $c = $body[$i]; + if (ord($c) == 10) + $nl++; + if ($nl && ord($c) != 10) + $nl = 0; + if ($nl != 1) + $out .= $c; + else + $out .= ' '; + } + + return $out; + } + + protected static function convert_formatting($body) + { + $replace = array( + '' => '', '' => '', + '' => '', '' => '', + '' => '', '' => '', + '' => '', ''=> '', + '' => '', '' => '', + '' => '', '' => '', + '' => '', '' => '', + '' => '', '' => '', + '' => '', '' => '', + '' => '', '' => '', + '' => '', '' => '', + ); + + return str_ireplace(array_keys($replace), array_values($replace), $body); + } + + protected static function convert_font($body) + { + $pattern = '/(.*)\\(.*)\<\/param\>(.*)\<\/fontfamily\>(.*)/ims'; + + while (preg_match($pattern, $body, $a)) { + if (count($a) != 5) + continue; + + $body = $a[1].''.$a[3].''.$a[4]; + } + + return $body; + } + + protected static function convert_color($body) + { + $pattern = '/(.*)\\(.*)\<\/param\>(.*)\<\/color\>(.*)/ims'; + + while (preg_match($pattern, $body, $a)) { + if (count($a) != 5) + continue; + + // extract color (either by name, or ####,####,####) + if (strpos($a[2],',')) { + $rgb = explode(',',$a[2]); + $color = '#'; + for ($i=0; $i<3; $i++) + $color .= substr($rgb[$i], 0, 2); // just take first 2 bytes + } + else { + $color = $a[2]; + } + + // put it all together + $body = $a[1].''.$a[3].''.$a[4]; + } + + return $body; + } + + protected static function convert_excerpt($body) + { + $pattern = '/(.*)\(.*)\<\/excerpt\>(.*)/i'; + + while (preg_match($pattern, $body, $a)) { + if (count($a) != 4) + continue; + + $quoted = ''; + $lines = explode('
', $a[2]); + + foreach ($lines as $n => $line) + $quoted .= '>'.$line.'
'; + + $body = $a[1].''.$quoted.''.$a[3]; + } + + return $body; + } + + public static function to_html($body) + { + $body = str_replace('<<','<',$body); + $body = self::convert_newlines($body); + $body = str_replace("\n", '
', $body); + $body = self::convert_formatting($body); + $body = self::convert_color($body); + $body = self::convert_font($body); + $body = self::convert_excerpt($body); + //$body = nl2br($body); + + return $body; + } +} diff --git a/program/lib/enriched.inc b/program/lib/enriched.inc deleted file mode 100644 index e3abd8c4f..000000000 --- a/program/lib/enriched.inc +++ /dev/null @@ -1,114 +0,0 @@ -'=>'',''=>'',''=>'', - ''=>'',''=>'',''=>'', - ''=>'',''=>'', - ''=>'',''=>'', - ''=>'', - ''=>'', - ''=>'', - ''=>'', - ''=>'', - ''=>'', - ''=>'', - ''=>'', - ''=>'', - ''=>'', - ''=>'', - ''=>''); - - while(list($find,$replace)=each($a)){ - $body = preg_replace('#'.$find.'#i', $replace, $body); - } - return $body; -} - -function enriched_font($body){ - $pattern = '/(.*)\\(.*)\<\/param\>(.*)\<\/fontfamily\>(.*)/ims'; - while(preg_match($pattern,$body,$a)){ - //print_r($a); - if (count($a)!=5) continue; - $body=$a[1].''.$a[3].''.$a[4]; - } - - return $body; -} - - -function enriched_color($body){ - $pattern = '/(.*)\\(.*)\<\/param\>(.*)\<\/color\>(.*)/ims'; - while(preg_match($pattern,$body,$a)){ - //print_r($a); - if (count($a)!=5) continue; - - //extract color (either by name, or ####,####,####) - if (strpos($a[2],',')){ - $rgb = explode(',',$a[2]); - $color ='#'; - for($i=0;$i<3;$i++) $color.=substr($rgb[$i],0,2); //just take first 2 bytes - }else{ - $color = $a[2]; - } - - //put it all together - $body = $a[1].''.$a[3].''.$a[4]; - } - - return $body; -} - -function enriched_excerpt($body){ - - $pattern = '/(.*)\(.*)\<\/excerpt\>(.*)/i'; - while(preg_match($pattern,$body,$a)){ - //print_r($a); - if (count($a)!=4) continue; - $quoted = ''; - $lines = explode('
',$a[2]); - foreach($lines as $n=>$line) $quoted.='>'.$line.'
'; - $body=$a[1].''.$quoted.''.$a[3]; - } - - return $body; -} - -function enriched_to_html($body){ - $body = str_replace('<<','<',$body); - $body = enriched_convert_newlines($body); - $body = str_replace("\n", '
', $body); - $body = enriched_convert_formatting($body); - $body = enriched_color($body); - $body = enriched_font($body); - $body = enriched_excerpt($body); - //$body = nl2br($body); - return $body; -} - -?> \ No newline at end of file diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index c039e42c6..96391c88b 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -733,8 +733,7 @@ function rcmail_compose_part_body($part, $isHtml = false) if ($part->ctype_secondary == 'html') { } else if ($part->ctype_secondary == 'enriched') { - require_once(INSTALL_PATH . 'program/lib/enriched.inc'); - $body = enriched_to_html($body); + $body = rcube_enriched::to_html($body); } else { // try to remove the signature @@ -750,8 +749,7 @@ function rcmail_compose_part_body($part, $isHtml = false) } else { if ($part->ctype_secondary == 'enriched') { - require_once(INSTALL_PATH . 'program/lib/enriched.inc'); - $body = enriched_to_html($body); + $body = rcube_enriched::to_html($body); $part->ctype_secondary = 'html'; } @@ -763,8 +761,7 @@ function rcmail_compose_part_body($part, $isHtml = false) $body = $txt->get_text(); } else if ($part->ctype_secondary == 'enriched') { - require_once(INSTALL_PATH . 'program/lib/enriched.inc'); - $body = enriched_to_html($body); + $body = rcube_enriched::to_html($body); } else { if ($part->ctype_secondary == 'plain' && $part->ctype_parameters['format'] == 'flowed') { diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index cb1a5ddae..80dac716e 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -751,8 +751,7 @@ function rcmail_print_body($part, $p = array()) } // text/enriched else if ($data['type'] == 'enriched') { - require_once(INSTALL_PATH . 'program/lib/enriched.inc'); - $body = enriched_to_html($data['body']); + $body = rcube_enriched::to_html($data['body']); $body = rcmail_wash_html($body, $data, $part->replaces); $part->ctype_secondary = 'html'; } diff --git a/tests/Framework/Enriched.php b/tests/Framework/Enriched.php new file mode 100644 index 000000000..26bbc3b4e --- /dev/null +++ b/tests/Framework/Enriched.php @@ -0,0 +1,74 @@ +assertInstanceOf('rcube_enriched', $object, "Class constructor"); + } + + /** + * Test to_html() + */ + function test_to_html() + { + $enriched = 'the-text'; + $expected = 'the-text'; + $result = rcube_enriched::to_html($enriched); + + $this->assertSame($expected, $result); + } + + /** + * Data for test_formatting() + */ + function data_formatting() + { + return array( + array('', ''), + array('', ''), + array('', ''), + array('', ''), + array('', ''), + array('', ''), + array('', ''), + array('', ''), + array('', ''), + array('', ''), + array('', ''), + array('', ''), + array('', ''), + array('', ''), + array('', ''), + array('', ''), + array('', ''), + array('', ''), + array('', ''), + array('', ''), + array('', ''), + array('', ''), + ); + } + + /** + * Test formatting conversion + * @dataProvider data_formatting + */ + function test_formatting($enriched, $expected) + { + $result = rcube_enriched::to_html($enriched); + + $this->assertSame($expected, $result); + } +} diff --git a/tests/phpunit.xml b/tests/phpunit.xml index 36ab6d714..c9e229e97 100644 --- a/tests/phpunit.xml +++ b/tests/phpunit.xml @@ -10,6 +10,7 @@ Framework/Charset.php Framework/ContentFilter.php Framework/Csv2vcard.php + Framework/Enriched.php Framework/Html.php Framework/Imap.php Framework/ImapGeneric.php -- cgit v1.2.3 From c7ff6ec2cb985061438e5b68cfc5691b5a86422c Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 5 Dec 2012 09:46:03 +0100 Subject: Add workaround for IE<=8 bug where Content-Disposition:inline was ignored (#1488844) --- CHANGELOG | 1 + program/steps/mail/get.inc | 7 +++++++ 2 files changed, 8 insertions(+) (limited to 'program/steps/mail') diff --git a/CHANGELOG b/CHANGELOG index af7d29c04..5a1b1acd5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Add workaround for IE<=8 bug where Content-Disposition:inline was ignored (#1488844) - Fix XSS vulnerability in vbscript: and data:text links handling (#1488850) - Fix broken message/part bodies when FETCH response contains more untagged lines (#1488836) - Fix empty email on identities list after identity update (#1488834) diff --git a/program/steps/mail/get.inc b/program/steps/mail/get.inc index 7b2f719c6..7f06feb1a 100644 --- a/program/steps/mail/get.inc +++ b/program/steps/mail/get.inc @@ -259,6 +259,13 @@ else if (strlen($pid = get_input_value('_part', RCUBE_INPUT_GET))) { $disposition = !empty($plugin['download']) ? 'attachment' : 'inline'; + // Workaround for nasty IE bug (#1488844) + // If Content-Disposition header contains string "attachment" e.g. in filename + // IE handles data as attachment not inline + if ($disposition == 'inline' && $browser->ie && $browser->ver < 9) { + $filename = str_ireplace('attachment', 'attach', $filename); + } + header("Content-Disposition: $disposition; filename=\"$filename\""); // handle tiff to jpeg conversion -- cgit v1.2.3 From 176172c850a6836a9804c24b29b8ada13040670b Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 7 Dec 2012 09:44:23 +0100 Subject: Fix PHP warning when replied message contains exactly one Delivered-To header --- program/steps/mail/compose.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'program/steps/mail') diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index 96391c88b..908de950e 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -454,7 +454,7 @@ function rcmail_identity_select($MESSAGE, $identities, $compose_mode) // Fallback using Delivered-To if ($from_idx === null && ($delivered_to = $MESSAGE->headers->others['delivered-to'])) { foreach ($identities as $idx => $ident) { - if (in_array($ident['email_ascii'], $delivered_to)) { + if (in_array($ident['email_ascii'], (array)$delivered_to)) { $from_idx = $idx; break; } -- cgit v1.2.3 From 8b748de4ec5e48c7cc0e3f4942669c369710830e Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 7 Dec 2012 10:37:04 +0100 Subject: Plugin API: add 'check_recent' hook with possibility to control folders list used on check-recent action --- program/steps/mail/check_recent.inc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'program/steps/mail') diff --git a/program/steps/mail/check_recent.inc b/program/steps/mail/check_recent.inc index 90d17c15b..61693b4ed 100644 --- a/program/steps/mail/check_recent.inc +++ b/program/steps/mail/check_recent.inc @@ -25,7 +25,7 @@ if (empty($_REQUEST['_folderlist']) && empty($_REQUEST['_list'])) { return; } -$current = $RCMAIL->storage->get_folder(); +$current = $RCMAIL->storage->get_folder(); $check_all = $RCMAIL->action != 'refresh' || (bool)$RCMAIL->config->get('check_all_folders'); // list of folders to check @@ -34,10 +34,15 @@ if ($check_all) { } else { $a_mailboxes = (array) $current; - if ($a_mailboxes[0] != 'INBOX') + if ($current != 'INBOX') { $a_mailboxes[] = 'INBOX'; + } } +// Control folders list from a plugin +$plugin = $RCMAIL->plugins->exec_hook('check_recent', array('folders' => $a_mailboxes, 'all' => $check_all)); +$a_mailboxes = $plugin['folders']; + // check recent/unseen counts foreach ($a_mailboxes as $mbox_name) { $is_current = $mbox_name == $current; -- cgit v1.2.3 From a8a72e2e7ee89caa04f8f13b6067e1b4ad870612 Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Mon, 10 Dec 2012 22:26:45 +0100 Subject: Nicely render headers of message/rfc822 parts --- program/lib/Roundcube/rcube_message.php | 9 +++++++- program/lib/Roundcube/rcube_message_header.php | 16 +++++++++++++ program/steps/mail/func.inc | 31 +++++++++++++++++++------- skins/classic/mail.css | 11 +++++++++ skins/larry/mail.css | 24 ++++++++++++++++++-- skins/larry/templates/message.html | 2 +- skins/larry/templates/messagepreview.html | 2 +- 7 files changed, 82 insertions(+), 13 deletions(-) (limited to 'program/steps/mail') diff --git a/program/lib/Roundcube/rcube_message.php b/program/lib/Roundcube/rcube_message.php index 4ef534a0a..c626af08a 100644 --- a/program/lib/Roundcube/rcube_message.php +++ b/program/lib/Roundcube/rcube_message.php @@ -320,8 +320,15 @@ class rcube_message private function parse_structure($structure, $recursive = false) { // real content-type of message/rfc822 part - if ($structure->mimetype == 'message/rfc822' && $structure->real_mimetype) + if ($structure->mimetype == 'message/rfc822' && $structure->real_mimetype) { $mimetype = $structure->real_mimetype; + + // parse headers from message/rfc822 part + if (!isset($structure->headers['subject'])) { + list($headers, $dump) = explode("\r\n\r\n", $this->get_part_content($structure->mime_id, null, true, 4096)); + $structure->headers = rcube_mime::parse_headers($headers); + } + } else $mimetype = $structure->mimetype; diff --git a/program/lib/Roundcube/rcube_message_header.php b/program/lib/Roundcube/rcube_message_header.php index 445d0bd39..7009a00af 100644 --- a/program/lib/Roundcube/rcube_message_header.php +++ b/program/lib/Roundcube/rcube_message_header.php @@ -235,6 +235,22 @@ class rcube_message_header $this->others[$name] = $value; } } + + + /** + * Factory method to instantiate headers from a data array + * + * @param array Hash array with header values + * @return object rcube_message_header instance filled with headers values + */ + public static function from_array($arr) + { + $obj = new rcube_message_header; + foreach ($arr as $k => $v) + $obj->set($k, $v); + + return $obj; + } } diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index 80dac716e..8ae41017e 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -5,7 +5,7 @@ | program/steps/mail/func.inc | | | | This file is part of the Roundcube Webmail client | - | Copyright (C) 2005-2010, The Roundcube Dev Team | + | Copyright (C) 2005-2012, The Roundcube Dev Team | | | | Licensed under the GNU General Public License version 3 or | | any later version with exceptions for skins & plugins. | @@ -939,13 +939,13 @@ function rcmail_html_tag_callback($matches) /** * return table with message headers */ -function rcmail_message_headers($attrib, $headers=NULL) +function rcmail_message_headers($attrib, $headers=null) { global $OUTPUT, $MESSAGE, $PRINT_MODE, $RCMAIL; static $sa_attrib; // keep header table attrib - if (is_array($attrib) && !$sa_attrib) + if (is_array($attrib) && !$sa_attrib && !$attrib['valueof']) $sa_attrib = $attrib; else if (!is_array($attrib) && is_array($sa_attrib)) $attrib = $sa_attrib; @@ -954,8 +954,13 @@ function rcmail_message_headers($attrib, $headers=NULL) return FALSE; // get associative array of headers object - if (!$headers) - $headers = is_object($MESSAGE->headers) ? get_object_vars($MESSAGE->headers) : $MESSAGE->headers; + if (!$headers) { + $headers_obj = $MESSAGE->headers; + $headers = get_object_vars($MESSAGE->headers); + } + else { + $headers_obj = rcube_message_header::from_array($headers); + } // show these headers $standard_headers = array('subject', 'from', 'to', 'cc', 'bcc', 'replyto', @@ -1031,7 +1036,7 @@ function rcmail_message_headers($attrib, $headers=NULL) } $plugin = $RCMAIL->plugins->exec_hook('message_headers_output', - array('output' => $output_headers, 'headers' => $MESSAGE->headers, 'exclude' => $exclude_headers)); + array('output' => $output_headers, 'headers' => $headers_obj, 'exclude' => $exclude_headers)); // single header value is requested if (!empty($attrib['valueof'])) @@ -1110,8 +1115,9 @@ function rcmail_message_body($attrib) if (!empty($MESSAGE->parts)) { foreach ($MESSAGE->parts as $i => $part) { - if ($part->type == 'headers') - $out .= rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : NULL, $part->headers); + if ($part->type == 'headers') { + $out .= html::div('message-partheaders', rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : null, $part->headers)); + } else if ($part->type == 'content') { // unsapported if ($part->realtype) { @@ -1139,6 +1145,15 @@ function rcmail_message_body($attrib) if (!isset($part->body)) $part->body = $MESSAGE->get_part_content($part->mime_id); + // extract headers from message/rfc822 parts + if ($part->mimetype == 'message/rfc822') { + list($hdrs, $body) = explode("\r\n\r\n", $part->body, 2); + if ($hdrs && $body && preg_match('/^[\w-]+:\s/i', $hdrs)) { + $out .= html::div('message-partheaders', rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : null, rcube_mime::parse_headers($hdrs))); + $part->body = $body; + } + } + // message is cached but not exists (#1485443), or other error if ($part->body === false) { rcmail_message_error($MESSAGE->uid); diff --git a/skins/classic/mail.css b/skins/classic/mail.css index 85c53d569..98325d9d2 100644 --- a/skins/classic/mail.css +++ b/skins/classic/mail.css @@ -1070,6 +1070,17 @@ table.headers-table background-color: #F4F4F4; } +#messagebody table.headers-table +{ + margin: 16px 6px 6px 6px; +} + +div.message-partheaders + div.message-part +{ + border-top: 0; + padding-top: 4px; +} + table.headers-table tr td { font-size: 11px; diff --git a/skins/larry/mail.css b/skins/larry/mail.css index 6512e52a3..12a2b7a82 100644 --- a/skins/larry/mail.css +++ b/skins/larry/mail.css @@ -999,12 +999,14 @@ div.hide-headers { } div.message-part, -div.message-htmlpart { - padding: 0 2px 10px 2px; +div.message-htmlpart, +div.message-partheaders { + padding: 10px 2px; border-top: 1px solid #ccc; } #messagebody div:first-child { + padding-top: 0; border-top: 0; } @@ -1045,6 +1047,24 @@ div.message-part blockquote blockquote blockquote { border-right: 2px solid #bb0000; } +div.message-partheaders { + margin-top: 8px; + padding: 8px 0; +} + +div.message-partheaders .headers-table { + width: 100%; +} + +div.message-partheaders .headers-table td.header-title { + width: auto; + padding-left: 0; +} + +div.message-partheaders .headers-table td.header { + width: 88%; +} + #messagebody > hr { color: #fff; background: #fff; diff --git a/skins/larry/templates/message.html b/skins/larry/templates/message.html index f7e188f5f..04381f5e9 100644 --- a/skins/larry/templates/message.html +++ b/skins/larry/templates/message.html @@ -64,7 +64,7 @@
- +
diff --git a/skins/larry/templates/messagepreview.html b/skins/larry/templates/messagepreview.html index 9eb4d1e00..aef282ac9 100644 --- a/skins/larry/templates/messagepreview.html +++ b/skins/larry/templates/messagepreview.html @@ -47,7 +47,7 @@
- +
-- cgit v1.2.3 From 31521613e40bb57f430591bab30de2c202637db2 Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Mon, 10 Dec 2012 22:32:46 +0100 Subject: Append filename extension if missing --- program/steps/mail/get.inc | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'program/steps/mail') diff --git a/program/steps/mail/get.inc b/program/steps/mail/get.inc index 7f06feb1a..314a437e7 100644 --- a/program/steps/mail/get.inc +++ b/program/steps/mail/get.inc @@ -266,6 +266,11 @@ else if (strlen($pid = get_input_value('_part', RCUBE_INPUT_GET))) { $filename = str_ireplace('attachment', 'attach', $filename); } + // add filename extension if missing + if (!pathinfo($filename, PATHINFO_EXTENSION) && ($extensions = rcube_mime::get_mime_extensions($mimetype))) { + $filename .= '.' . $extensions[0]; + } + header("Content-Disposition: $disposition; filename=\"$filename\""); // handle tiff to jpeg conversion -- cgit v1.2.3 From d9f109b56af2015eae7aadc5e87c06365854eda0 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 11 Dec 2012 08:30:49 +0100 Subject: Allow forwarding of multiple emails (#1486854) --- CHANGELOG | 1 + program/js/app.js | 19 ++-- program/steps/mail/compose.inc | 147 +++++++++++++++++------------ program/steps/mail/sendmail.inc | 3 +- skins/classic/includes/messagetoolbar.html | 2 +- skins/larry/includes/mailtoolbar.html | 2 +- 6 files changed, 101 insertions(+), 73 deletions(-) (limited to 'program/steps/mail') diff --git a/CHANGELOG b/CHANGELOG index 79f19b904..ebc279622 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Allow forwarding of multiple emails (#1486854) - Fix big memory consumption of DB layer (#1488856) - Add workaround for IE<=8 bug where Content-Disposition:inline was ignored (#1488844) - Fix XSS vulnerability in vbscript: and data:text links handling (#1488850) diff --git a/program/js/app.js b/program/js/app.js index 955c77ff5..4db7fa0c8 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -224,9 +224,10 @@ function rcube_webmail() this.set_button_titles(); - this.env.message_commands = ['show', 'reply', 'reply-all', 'reply-list', 'forward', - 'moveto', 'copy', 'delete', 'open', 'mark', 'edit', 'viewsource', 'download', - 'print', 'load-attachment', 'show-headers', 'hide-headers', 'forward-attachment']; + this.env.message_commands = ['show', 'reply', 'reply-all', 'reply-list', + 'moveto', 'copy', 'delete', 'open', 'mark', 'edit', 'viewsource', + 'print', 'load-attachment', 'show-headers', 'hide-headers', 'download', + 'forward', 'forward-inline', 'forward-attachment']; if (this.env.action == 'show' || this.env.action == 'preview') { this.enable_command(this.env.message_commands, this.env.uid); @@ -999,10 +1000,12 @@ function rcube_webmail() break; case 'forward-attachment': + case 'forward-inline': case 'forward': - if (uid = this.get_single_uid()) { - url = { _forward_uid: uid, _mbox: this.env.mailbox }; - if (command == 'forward-attachment' || (!props && this.env.forward_attachment)) + var uids = this.env.uid ? [this.env.uid] : (this.message_list ? this.message_list.get_selection() : []); + if (uids.length) { + url = { _forward_uid: this.uids_to_list(uids), _mbox: this.env.mailbox }; + if (command == 'forward-attachment' || (!props && this.env.forward_attachment) || uids.length > 1) url._attachment = 1; this.open_compose_step(url); } @@ -1526,7 +1529,7 @@ function rcube_webmail() if (selected) { // Hide certain command buttons when Drafts folder is selected if (this.env.mailbox == this.env.drafts_mailbox) - this.enable_command('reply', 'reply-all', 'reply-list', 'forward', 'forward-attachment', false); + this.enable_command('reply', 'reply-all', 'reply-list', 'forward', 'forward-attachment', 'forward-inline', false); // Disable reply-list when List-Post header is not set else { var msg = this.env.messages[list.get_single_selection()]; @@ -1535,7 +1538,7 @@ function rcube_webmail() } } // Multi-message commands - this.enable_command('delete', 'moveto', 'copy', 'mark', (list.selection.length > 0 ? true : false)); + this.enable_command('delete', 'moveto', 'copy', 'mark', 'forward', 'forward-attachment', list.selection.length > 0); // reset all-pages-selection if (selected || (list.selection.length && list.selection.length != list.rowcount)) diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index 908de950e..d181a72e6 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -151,15 +151,22 @@ if ($font && !is_array($font)) { // get reference message and set compose mode if ($msg_uid = $COMPOSE['param']['draft_uid']) { - $RCMAIL->storage->set_folder($CONFIG['drafts_mbox']); $compose_mode = RCUBE_COMPOSE_DRAFT; + $RCMAIL->storage->set_folder($CONFIG['drafts_mbox']); } -else if ($msg_uid = $COMPOSE['param']['reply_uid']) +else if ($msg_uid = $COMPOSE['param']['reply_uid']) { $compose_mode = RCUBE_COMPOSE_REPLY; -else if ($msg_uid = $COMPOSE['param']['forward_uid']) + $OUTPUT->set_env('compose_mode', 'reply'); +} +else if ($msg_uid = $COMPOSE['param']['forward_uid']) { $compose_mode = RCUBE_COMPOSE_FORWARD; -else if ($msg_uid = $COMPOSE['param']['uid']) + $OUTPUT->set_env('compose_mode', 'forward'); + $COMPOSE['forward_uid'] = $msg_uid; + $COMPOSE['as_attachment'] = !empty($COMPOSE['param']['attachment']); +} +else if ($msg_uid = $COMPOSE['param']['uid']) { $compose_mode = RCUBE_COMPOSE_EDIT; +} $config_show_sig = $RCMAIL->config->get('show_sig', 1); if ($config_show_sig == 1) @@ -174,7 +181,7 @@ else // set line length for body wrapping $LINE_LENGTH = $RCMAIL->config->get('line_length', 72); -if (!empty($msg_uid)) +if (!empty($msg_uid) && empty($COMPOSE['as_attachment'])) { // similar as in program/steps/mail/show.inc // re-set 'prefer_html' to have possibility to use html part for compose @@ -188,8 +195,7 @@ if (!empty($msg_uid)) if (!empty($MESSAGE->headers->charset)) $RCMAIL->storage->set_charset($MESSAGE->headers->charset); - if ($compose_mode == RCUBE_COMPOSE_REPLY) - { + if ($compose_mode == RCUBE_COMPOSE_REPLY) { $COMPOSE['reply_uid'] = $msg_uid; $COMPOSE['reply_msgid'] = $MESSAGE->headers->messageID; $COMPOSE['references'] = trim($MESSAGE->headers->references . " " . $MESSAGE->headers->messageID); @@ -197,8 +203,6 @@ if (!empty($msg_uid)) if (!empty($COMPOSE['param']['all'])) $MESSAGE->reply_all = $COMPOSE['param']['all']; - $OUTPUT->set_env('compose_mode', 'reply'); - // Save the sent message in the same folder of the message being replied to if ($RCMAIL->config->get('reply_same_folder') && ($sent_folder = $COMPOSE['mailbox']) && rcmail_check_sent_folder($sent_folder, false) @@ -206,10 +210,8 @@ if (!empty($msg_uid)) $COMPOSE['param']['sent_mbox'] = $sent_folder; } } - else if ($compose_mode == RCUBE_COMPOSE_DRAFT) - { - if ($MESSAGE->headers->others['x-draft-info']) - { + else if ($compose_mode == RCUBE_COMPOSE_DRAFT) { + if ($MESSAGE->headers->others['x-draft-info']) { // get reply_uid/forward_uid to flag the original message when sending $info = rcmail_draftinfo_decode($MESSAGE->headers->others['x-draft-info']); @@ -233,14 +235,6 @@ if (!empty($msg_uid)) $COMPOSE['references'] = $MESSAGE->headers->references; } - else if ($compose_mode == RCUBE_COMPOSE_FORWARD) - { - $COMPOSE['forward_uid'] = $msg_uid; - $OUTPUT->set_env('compose_mode', 'forward'); - - if (!empty($COMPOSE['param']['attachment'])) - $MESSAGE->forward_attachment = true; - } } else { $MESSAGE = new stdClass(); @@ -643,11 +637,11 @@ function rcmail_prepare_message_body() $isHtml = false; } // forward as attachment - else if ($compose_mode == RCUBE_COMPOSE_FORWARD && $MESSAGE->forward_attachment) { + else if ($compose_mode == RCUBE_COMPOSE_FORWARD && $COMPOSE['as_attachment']) { $isHtml = rcmail_compose_editor_mode(); $body = ''; if (empty($COMPOSE['attachments'])) - rcmail_write_forward_attachment($MESSAGE); + rcmail_write_forward_attachments(); } // reply/edit/draft/forward else if ($compose_mode && ($compose_mode != RCUBE_COMPOSE_REPLY || $RCMAIL->config->get('reply_mode') != -1)) { @@ -1135,55 +1129,86 @@ function rcmail_write_inline_attachments(&$message) return $cid_map; } -// Creates an attachment from the forwarded message -function rcmail_write_forward_attachment(&$message) +// Creates attachment(s) from the forwarded message(s) +function rcmail_write_forward_attachments() { - global $RCMAIL, $COMPOSE; + global $RCMAIL, $COMPOSE, $MESSAGE; + + $storage = $RCMAIL->get_storage(); + $mem_limit = parse_bytes(ini_get('memory_limit')); + $curr_mem = function_exists('memory_get_usage') ? memory_get_usage() : 16*1024*1024; // safe value: 16MB + $names = array(); - if (strlen($message->subject)) { - $name = mb_substr($message->subject, 0, 64) . '.eml'; + if ($COMPOSE['forward_uid'] == '*') { + $index = $storage->index(null, rcmail_sort_column(), rcmail_sort_order()); + $COMPOSE['forward_uid'] = $index->get(); } else { - $name = 'message_rfc822.eml'; + $COMPOSE['forward_uid'] = explode(',', $COMPOSE['forward_uid']); } - $mem_limit = parse_bytes(ini_get('memory_limit')); - $curr_mem = function_exists('memory_get_usage') ? memory_get_usage() : 16*1024*1024; // safe value: 16MB - $data = $path = null; + foreach ((array)$COMPOSE['forward_uid'] as $uid) { + $message = new rcube_message($uid); - // don't load too big attachments into memory - if ($mem_limit > 0 && $message->size > $mem_limit - $curr_mem) { - $temp_dir = unslashify($RCMAIL->config->get('temp_dir')); - $path = tempnam($temp_dir, 'rcmAttmnt'); - if ($fp = fopen($path, 'w')) { - $RCMAIL->storage->get_raw_body($message->uid, $fp); - fclose($fp); - } else - return false; - } else { - $data = $RCMAIL->storage->get_raw_body($message->uid); - } + if (empty($message->headers)) { + continue; + } - $attachment = array( - 'group' => $COMPOSE['id'], - 'name' => $name, - 'mimetype' => 'message/rfc822', - 'data' => $data, - 'path' => $path, - 'size' => $path ? filesize($path) : strlen($data), - ); + if (!empty($message->headers->charset)) { + $storage->set_charset($message->headers->charset); + } - $attachment = $RCMAIL->plugins->exec_hook('attachment_save', $attachment); + if (empty($MESSAGE->subject)) { + $MESSAGE->subject = $message->subject; + } - if ($attachment['status']) { - unset($attachment['data'], $attachment['status'], $attachment['content_id'], $attachment['abort']); - $COMPOSE['attachments'][$attachment['id']] = $attachment; - return true; - } else if ($path) { - @unlink($path); - } + // generate (unique) attachment name + $name = strlen($message->subject) ? mb_substr($message->subject, 0, 64) : 'message_rfc822'; + if (!empty($names[$name])) { + $names[$name]++; + $name .= '_' . $names[$name]; + } + $names[$name] = 1; + $name .= '.eml'; + + $data = $path = null; + + // don't load too big attachments into memory + if ($mem_limit > 0 && $message->size > $mem_limit - $curr_mem) { + $temp_dir = unslashify($RCMAIL->config->get('temp_dir')); + $path = tempnam($temp_dir, 'rcmAttmnt'); + if ($fp = fopen($path, 'w')) { + $storage->get_raw_body($message->uid, $fp); + fclose($fp); + } + else { + return false; + } + } + else { + $data = $storage->get_raw_body($message->uid); + $curr_mem += $message->size; + } - return false; + $attachment = array( + 'group' => $COMPOSE['id'], + 'name' => $name, + 'mimetype' => 'message/rfc822', + 'data' => $data, + 'path' => $path, + 'size' => $path ? filesize($path) : strlen($data), + ); + + $attachment = $RCMAIL->plugins->exec_hook('attachment_save', $attachment); + + if ($attachment['status']) { + unset($attachment['data'], $attachment['status'], $attachment['content_id'], $attachment['abort']); + $COMPOSE['attachments'][$attachment['id']] = $attachment; + } + else if ($path) { + @unlink($path); + } + } } diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc index c26d774a2..d48834bb4 100644 --- a/program/steps/mail/sendmail.inc +++ b/program/steps/mail/sendmail.inc @@ -617,13 +617,12 @@ if (is_array($COMPOSE['attachments'])) $ctype = str_replace('image/pjpeg', 'image/jpeg', $attachment['mimetype']); // #1484914 $file = $attachment['data'] ? $attachment['data'] : $attachment['path']; - // .eml attachments send inline $MAIL_MIME->addAttachment($file, $ctype, $attachment['name'], ($attachment['data'] ? false : true), ($ctype == 'message/rfc822' ? '8bit' : 'base64'), - ($ctype == 'message/rfc822' ? 'inline' : 'attachment'), + 'attachment', '', '', '', $CONFIG['mime_param_folding'] ? 'quoted-printable' : NULL, $CONFIG['mime_param_folding'] == 2 ? 'quoted-printable' : NULL, diff --git a/skins/classic/includes/messagetoolbar.html b/skins/classic/includes/messagetoolbar.html index eebb55708..371c83fd9 100644 --- a/skins/classic/includes/messagetoolbar.html +++ b/skins/classic/includes/messagetoolbar.html @@ -27,7 +27,7 @@
    -
  • +
diff --git a/skins/larry/includes/mailtoolbar.html b/skins/larry/includes/mailtoolbar.html index 31472cbb3..8efcc762c 100644 --- a/skins/larry/includes/mailtoolbar.html +++ b/skins/larry/includes/mailtoolbar.html @@ -17,7 +17,7 @@
    -
  • +
-- cgit v1.2.3 From bb5d7282855dd83ccdd211cb77d0776dce71468e Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 12 Dec 2012 08:54:33 +0100 Subject: Use also Envelope-To for identity selection (#1488553) --- CHANGELOG | 2 +- program/lib/Roundcube/rcube_storage.php | 1 + program/steps/mail/compose.inc | 10 ++++++++++ 3 files changed, 12 insertions(+), 1 deletion(-) (limited to 'program/steps/mail') diff --git a/CHANGELOG b/CHANGELOG index ebc279622..8fd17b407 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -8,7 +8,7 @@ CHANGELOG Roundcube Webmail - Fix broken message/part bodies when FETCH response contains more untagged lines (#1488836) - Fix empty email on identities list after identity update (#1488834) - Add new identities_level: (4) one identity with possibility to edit only signature -- Use Delivered-To header as a last resort for identity selection (#1488840) +- Use Delivered-To and Envelope-To headers for identity selection (#1488840, #1488553) - Fix XSS vulnerability using Flash files (#1488828) - Fix absolute positioning in HTML messages (#1488819) - Fix cache (in)validation after setting \Deleted flag diff --git a/program/lib/Roundcube/rcube_storage.php b/program/lib/Roundcube/rcube_storage.php index 245d911c0..7ec05b7af 100644 --- a/program/lib/Roundcube/rcube_storage.php +++ b/program/lib/Roundcube/rcube_storage.php @@ -65,6 +65,7 @@ abstract class rcube_storage 'MAIL-REPLY-TO', 'RETURN-PATH', 'DELIVERED-TO', + 'ENVELOPE-TO', ); const UNKNOWN = 0; diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index d181a72e6..d764f5289 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -455,6 +455,16 @@ function rcmail_identity_select($MESSAGE, $identities, $compose_mode) } } + // Fallback using Envelope-To + if ($from_idx === null && ($envelope_to = $MESSAGE->headers->others['envelope-to'])) { + foreach ($identities as $idx => $ident) { + if (in_array($ident['email_ascii'], (array)$envelope_to)) { + $from_idx = $idx; + break; + } + } + } + return $identities[$from_idx !== null ? $from_idx : $default_identity]; } -- cgit v1.2.3 From 04689fa7ee89c29b57278a354b39d9ee5397a442 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 12 Dec 2012 19:59:08 +0100 Subject: Fix so compacting of non-empty folder is possible also when messages list is empty (#1488858) --- CHANGELOG | 1 + program/js/app.js | 11 ++++++----- program/steps/mail/check_recent.inc | 4 +++- program/steps/mail/folders.inc | 1 + program/steps/mail/list.inc | 1 + program/steps/mail/move_del.inc | 7 +++---- program/steps/mail/search.inc | 1 + 7 files changed, 16 insertions(+), 10 deletions(-) (limited to 'program/steps/mail') diff --git a/CHANGELOG b/CHANGELOG index 8fd17b407..095a240d4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Fix so compacting of non-empty folder is possible also when messages list is empty (#1488858) - Allow forwarding of multiple emails (#1486854) - Fix big memory consumption of DB layer (#1488856) - Add workaround for IE<=8 bug where Content-Disposition:inline was ignored (#1488844) diff --git a/program/js/app.js b/program/js/app.js index 4094a67ad..b5bf1840a 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -651,13 +651,13 @@ function rcube_webmail() break; case 'expunge': - if (this.env.messagecount) + if (this.env.exists) this.expunge_mailbox(this.env.mailbox); break; case 'purge': case 'empty-mailbox': - if (this.env.messagecount) + if (this.env.exists) this.purge_mailbox(this.env.mailbox); break; @@ -2971,7 +2971,7 @@ function rcube_webmail() // test if purge command is allowed this.purge_mailbox_test = function() { - return (this.env.messagecount && (this.env.mailbox == this.env.trash_mailbox || this.env.mailbox == this.env.junk_mailbox + return (this.env.exists && (this.env.mailbox == this.env.trash_mailbox || this.env.mailbox == this.env.junk_mailbox || this.env.mailbox.match('^' + RegExp.escape(this.env.trash_mailbox) + RegExp.escape(this.env.delimiter)) || this.env.mailbox.match('^' + RegExp.escape(this.env.junk_mailbox) + RegExp.escape(this.env.delimiter)))); }; @@ -6228,7 +6228,7 @@ function rcube_webmail() case 'purge': case 'expunge': if (this.task == 'mail') { - if (!this.env.messagecount) { + if (!this.env.exists) { // clear preview pane content if (this.env.contentframe) this.show_contentframe(false); @@ -6248,7 +6248,8 @@ function rcube_webmail() this.env.qsearch = null; case 'list': if (this.task == 'mail') { - this.enable_command('show', 'expunge', 'select-all', 'select-none', (this.env.messagecount > 0)); + this.enable_command('show', 'select-all', 'select-none', this.env.messagecount > 0); + this.enable_command('expunge', this.env.exists); this.enable_command('purge', this.purge_mailbox_test()); this.enable_command('expand-all', 'expand-unread', 'collapse-all', this.env.threading && this.env.messagecount); diff --git a/program/steps/mail/check_recent.inc b/program/steps/mail/check_recent.inc index 61693b4ed..4befbf275 100644 --- a/program/steps/mail/check_recent.inc +++ b/program/steps/mail/check_recent.inc @@ -75,13 +75,15 @@ foreach ($a_mailboxes as $mbox_name) { if (!empty($_GET['_quota'])) $OUTPUT->command('set_quota', rcmail_quota_content()); + $OUTPUT->set_env('exists', $RCMAIL->storage->count($mbox_name, 'EXISTS')); + // "No-list" mode, don't get messages if (empty($_GET['_list'])) continue; // get overall message count; allow caching because rcube_storage::folder_status() did a refresh $list_mode = $RCMAIL->storage->get_threading() ? 'THREADS' : 'ALL'; - $all_count = $RCMAIL->storage->count(null, $list_mode, false, false); + $all_count = $RCMAIL->storage->count($mbox_name, $list_mode, false, false); $page = $RCMAIL->storage->get_page(); $page_size = $RCMAIL->storage->get_pagesize(); diff --git a/program/steps/mail/folders.inc b/program/steps/mail/folders.inc index c56c914cd..574d6e975 100644 --- a/program/steps/mail/folders.inc +++ b/program/steps/mail/folders.inc @@ -65,6 +65,7 @@ else if ($RCMAIL->action == 'purge') if (!empty($_REQUEST['_reload'])) { $OUTPUT->set_env('messagecount', 0); $OUTPUT->set_env('pagecount', 0); + $OUTPUT->set_env('exists', 0); $OUTPUT->command('message_list.clear'); $OUTPUT->command('set_rowcount', rcmail_get_messagecount_text(), $mbox); $OUTPUT->command('set_unread_count', $mbox, 0); diff --git a/program/steps/mail/list.inc b/program/steps/mail/list.inc index b433f81fc..b8c3ee021 100644 --- a/program/steps/mail/list.inc +++ b/program/steps/mail/list.inc @@ -95,6 +95,7 @@ $OUTPUT->set_env('messagecount', $count); $OUTPUT->set_env('pagecount', $pages); $OUTPUT->set_env('threading', $threading); $OUTPUT->set_env('current_page', $count ? $RCMAIL->storage->get_page() : 1); +$OUTPUT->set_env('exists', $RCMAIL->storage->count($mbox_name, 'EXISTS')); $OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($count), $mbox_name); $OUTPUT->command('set_mailboxname', rcmail_get_mailbox_name_text()); diff --git a/program/steps/mail/move_del.inc b/program/steps/mail/move_del.inc index da43b4000..3e2252683 100644 --- a/program/steps/mail/move_del.inc +++ b/program/steps/mail/move_del.inc @@ -38,7 +38,7 @@ if ($RCMAIL->action=='moveto' && !empty($_POST['_uid']) && strlen($_POST['_targe if (!$moved) { // send error message - if ($_POST['_from'] != 'show') + if ($_POST['_from'] != 'show') $OUTPUT->command('list_mailbox'); rcmail_display_server_error('errormoving'); $OUTPUT->send(); @@ -59,7 +59,7 @@ else if ($RCMAIL->action=='delete' && !empty($_POST['_uid'])) { if (!$del) { // send error message - if ($_POST['_from'] != 'show') + if ($_POST['_from'] != 'show') $OUTPUT->command('list_mailbox'); rcmail_display_server_error('errordeleting'); $OUTPUT->send(); @@ -111,6 +111,7 @@ else $OUTPUT->set_env('messagecount', $msg_count); $OUTPUT->set_env('current_page', $page); $OUTPUT->set_env('pagecount', $pages); + $OUTPUT->set_env('exists', $RCMAIL->storage->count($mbox, 'EXISTS', true)); // update mailboxlist $mbox = $RCMAIL->storage->get_folder(); @@ -144,5 +145,3 @@ else // send response $OUTPUT->send(); - - diff --git a/program/steps/mail/search.inc b/program/steps/mail/search.inc index db5424b3b..f9b8f9e67 100644 --- a/program/steps/mail/search.inc +++ b/program/steps/mail/search.inc @@ -143,5 +143,6 @@ else { $OUTPUT->set_env('search_request', $search_str ? $search_request : ''); $OUTPUT->set_env('messagecount', $count); $OUTPUT->set_env('pagecount', ceil($count/$RCMAIL->storage->get_pagesize())); +$OUTPUT->set_env('exists', $RCMAIL->storage->count($mbox_name, 'EXISTS')); $OUTPUT->command('set_rowcount', rcmail_get_messagecount_text($count, 1), $mbox); $OUTPUT->send(); -- cgit v1.2.3 From a9bb50d8045e5a119ca5f66ec03753ca80aa4d4a Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 13 Dec 2012 15:14:56 +0100 Subject: Fix handling of signatures on draft edit: Don't add new signature and don't remove the old-one, the same for Edit as new (#1488798) --- CHANGELOG | 1 + program/steps/mail/compose.inc | 21 +++++++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) (limited to 'program/steps/mail') diff --git a/CHANGELOG b/CHANGELOG index 095a240d4..840c34901 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Fix handling of signatures on draft edit (#1488798) - Fix so compacting of non-empty folder is possible also when messages list is empty (#1488858) - Allow forwarding of multiple emails (#1486854) - Fix big memory consumption of DB layer (#1488856) diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index d764f5289..b4dbd8d9c 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -169,14 +169,15 @@ else if ($msg_uid = $COMPOSE['param']['uid']) { } $config_show_sig = $RCMAIL->config->get('show_sig', 1); -if ($config_show_sig == 1) +if ($compose_mode == RCUBE_COMPOSE_EDIT || $compose_mode == RCUBE_COMPOSE_DRAFT) { + // don't add signature in draft/edit mode, we'll also not remove the old-one +} +else if ($config_show_sig == 1) $OUTPUT->set_env('show_sig', true); -else if ($config_show_sig == 2 && (empty($compose_mode) || $compose_mode == RCUBE_COMPOSE_EDIT || $compose_mode == RCUBE_COMPOSE_DRAFT)) +else if ($config_show_sig == 2 && empty($compose_mode)) $OUTPUT->set_env('show_sig', true); else if ($config_show_sig == 3 && ($compose_mode == RCUBE_COMPOSE_REPLY || $compose_mode == RCUBE_COMPOSE_FORWARD)) $OUTPUT->set_env('show_sig', true); -else - $OUTPUT->set_env('show_sig', false); // set line length for body wrapping $LINE_LENGTH = $RCMAIL->config->get('line_length', 72); @@ -741,8 +742,10 @@ function rcmail_compose_part_body($part, $isHtml = false) } else { // try to remove the signature - if ($RCMAIL->config->get('strip_existing_sig', true)) { - $body = rcmail_remove_signature($body); + if ($compose_mode != RCUBE_COMPOSE_DRAFT && $compose_mode != RCUBE_COMPOSE_EDIT) { + if ($RCMAIL->config->get('strip_existing_sig', true)) { + $body = rcmail_remove_signature($body); + } } // add HTML formatting $body = rcmail_plain_body($body); @@ -773,8 +776,10 @@ function rcmail_compose_part_body($part, $isHtml = false) } // try to remove the signature - if ($RCMAIL->config->get('strip_existing_sig', true)) { - $body = rcmail_remove_signature($body); + if ($compose_mode != RCUBE_COMPOSE_DRAFT && $compose_mode != RCUBE_COMPOSE_EDIT) { + if ($RCMAIL->config->get('strip_existing_sig', true)) { + $body = rcmail_remove_signature($body); + } } } } -- cgit v1.2.3 From dc67f38cdfd4390a6f5b302ebc177565536e4447 Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Thu, 13 Dec 2012 19:56:22 +0100 Subject: Properly parse message/rfc822 parts including all headers, body decoding and charset conversion --- program/steps/mail/func.inc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'program/steps/mail') diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index 8ae41017e..7d6a51751 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -958,6 +958,10 @@ function rcmail_message_headers($attrib, $headers=null) $headers_obj = $MESSAGE->headers; $headers = get_object_vars($MESSAGE->headers); } + else if (is_object($headers)) { + $headers_obj = $headers; + $headers = get_object_vars($headers_obj); + } else { $headers_obj = rcube_message_header::from_array($headers); } @@ -1147,10 +1151,10 @@ function rcmail_message_body($attrib) // extract headers from message/rfc822 parts if ($part->mimetype == 'message/rfc822') { - list($hdrs, $body) = explode("\r\n\r\n", $part->body, 2); - if ($hdrs && $body && preg_match('/^[\w-]+:\s/i', $hdrs)) { - $out .= html::div('message-partheaders', rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : null, rcube_mime::parse_headers($hdrs))); - $part->body = $body; + $msgpart = rcube_mime::parse_message($part->body); + if (!empty($msgpart->headers)) { + $part = $msgpart; + $out .= html::div('message-partheaders', rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : null, $part->headers)); } } -- cgit v1.2.3 From 0f4e2f81342caed51709674c96ce5919cfc5913b Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 14 Dec 2012 12:38:42 +0100 Subject: Remove brackets for recipients without name --- program/steps/mail/sendmail.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'program/steps/mail') diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc index d48834bb4..36d850f8f 100644 --- a/program/steps/mail/sendmail.inc +++ b/program/steps/mail/sendmail.inc @@ -219,11 +219,11 @@ function rcmail_email_input_format($mailto, $count=false, $check=true) // address in brackets without name (do nothing) if (preg_match('/^<'.$email_regexp.'>$/', $item)) { $item = rcube_idn_to_ascii(trim($item, '<>')); - $result[] = '<' . $item . '>'; + $result[] = $item; // address without brackets and without name (add brackets) } else if (preg_match('/^'.$email_regexp.'$/', $item)) { $item = rcube_idn_to_ascii($item); - $result[] = '<' . $item . '>'; + $result[] = $item; // address with name (handle name) } else if (preg_match('/<*'.$email_regexp.'>*$/', $item, $matches)) { $address = $matches[0]; -- cgit v1.2.3 From a0e3dcf170502bd74f74c5ac5952cd7ad6de3863 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 14 Dec 2012 13:42:23 +0100 Subject: Use matching identity in MDN response (#1488864) --- CHANGELOG | 1 + program/steps/mail/compose.inc | 107 ++--------------------------------------- program/steps/mail/func.inc | 107 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 110 insertions(+), 105 deletions(-) (limited to 'program/steps/mail') diff --git a/CHANGELOG b/CHANGELOG index 43ba0c2e4..039d833c3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Use matching identity in MDN response (#1488864) - Fix unwanted horizontal scrollbar in message preview header (#1488866) - Fix handling of signatures on draft edit (#1488798) - Fix so compacting of non-empty folder is possible also when messages list is empty (#1488858) diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index b4dbd8d9c..d07cf587f 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -20,10 +20,10 @@ */ // define constants for message compose mode -define('RCUBE_COMPOSE_REPLY', 0x0106); -define('RCUBE_COMPOSE_FORWARD', 0x0107); -define('RCUBE_COMPOSE_DRAFT', 0x0108); -define('RCUBE_COMPOSE_EDIT', 0x0109); +define('RCUBE_COMPOSE_REPLY', 'reply'); +define('RCUBE_COMPOSE_FORWARD', 'forward'); +define('RCUBE_COMPOSE_DRAFT', 'draft'); +define('RCUBE_COMPOSE_EDIT', 'edit'); $MESSAGE_FORM = null; $COMPOSE_ID = get_input_value('_id', RCUBE_INPUT_GET); @@ -156,17 +156,16 @@ if ($msg_uid = $COMPOSE['param']['draft_uid']) { } else if ($msg_uid = $COMPOSE['param']['reply_uid']) { $compose_mode = RCUBE_COMPOSE_REPLY; - $OUTPUT->set_env('compose_mode', 'reply'); } else if ($msg_uid = $COMPOSE['param']['forward_uid']) { $compose_mode = RCUBE_COMPOSE_FORWARD; - $OUTPUT->set_env('compose_mode', 'forward'); $COMPOSE['forward_uid'] = $msg_uid; $COMPOSE['as_attachment'] = !empty($COMPOSE['param']['attachment']); } else if ($msg_uid = $COMPOSE['param']['uid']) { $compose_mode = RCUBE_COMPOSE_EDIT; } +$OUTPUT->set_env('compose_mode', $compose_mode); $config_show_sig = $RCMAIL->config->get('show_sig', 1); if ($compose_mode == RCUBE_COMPOSE_EDIT || $compose_mode == RCUBE_COMPOSE_DRAFT) { @@ -374,102 +373,6 @@ $MESSAGE_BODY = rcmail_prepare_message_body(); /****** compose mode functions ********/ -function rcmail_identity_select($MESSAGE, $identities, $compose_mode) -{ - $a_recipients = array(); - $a_names = array(); - - // extract all recipients of the reply-message - if (is_object($MESSAGE->headers) && in_array($compose_mode, array(RCUBE_COMPOSE_REPLY, RCUBE_COMPOSE_FORWARD))) { - $a_to = rcube_mime::decode_address_list($MESSAGE->headers->to, null, true, $MESSAGE->headers->charset); - foreach ($a_to as $addr) { - if (!empty($addr['mailto'])) { - $a_recipients[] = format_email($addr['mailto']); - $a_names[] = $addr['name']; - } - } - - if (!empty($MESSAGE->headers->cc)) { - $a_cc = rcube_mime::decode_address_list($MESSAGE->headers->cc, null, true, $MESSAGE->headers->charset); - foreach ($a_cc as $addr) { - if (!empty($addr['mailto'])) { - $a_recipients[] = format_email($addr['mailto']); - $a_names[] = $addr['name']; - } - } - } - } - - $from_idx = null; - $found_idx = null; - $default_identity = 0; // default identity is always first on the list - - // Select identity - foreach ($identities as $idx => $ident) { - // use From header - if (in_array($compose_mode, array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT))) { - if ($MESSAGE->headers->from == $ident['ident']) { - $from_idx = $idx; - break; - } - } - // reply to yourself - else if ($compose_mode == RCUBE_COMPOSE_REPLY && $MESSAGE->headers->from == $ident['ident']) { - $from_idx = $idx; - break; - } - // use replied message recipients - else if (($found = array_search($ident['email_ascii'], $a_recipients)) !== false) { - if ($found_idx === null) { - $found_idx = $idx; - } - // match identity name - if ($a_names[$found] && $ident['name'] && $a_names[$found] == $ident['name']) { - $from_idx = $idx; - break; - } - } - } - - // If matching by name+address doesn't found any amtches, get first found address (identity) - if ($from_idx === null) { - $from_idx = $found_idx; - } - - // Try Return-Path - if ($from_idx === null && ($return_path = $MESSAGE->headers->others['return-path'])) { - foreach ($identities as $idx => $ident) { - if (strpos($return_path, str_replace('@', '=', $ident['email_ascii']).'@') !== false) { - $from_idx = $idx; - break; - } - } - } - - // Fallback using Delivered-To - if ($from_idx === null && ($delivered_to = $MESSAGE->headers->others['delivered-to'])) { - foreach ($identities as $idx => $ident) { - if (in_array($ident['email_ascii'], (array)$delivered_to)) { - $from_idx = $idx; - break; - } - } - } - - // Fallback using Envelope-To - if ($from_idx === null && ($envelope_to = $MESSAGE->headers->others['envelope-to'])) { - foreach ($identities as $idx => $ident) { - if (in_array($ident['email_ascii'], (array)$envelope_to)) { - $from_idx = $idx; - break; - } - } - } - - return $identities[$from_idx !== null ? $from_idx : $default_identity]; -} - - function rcmail_compose_headers($attrib) { global $MESSAGE; diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index 7d6a51751..bd381d5c0 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -1720,11 +1720,11 @@ function rcmail_send_mdn($message, &$smtp_error) if ($message->headers->mdn_to && empty($message->headers->flags['MDNSENT']) && ($RCMAIL->storage->check_permflag('MDNSENT') || $RCMAIL->storage->check_permflag('*'))) { - $identity = $RCMAIL->user->get_identity(); - $sender = format_email_recipient($identity['email'], $identity['name']); + $identity = rcmail_identity_select($message); + $sender = format_email_recipient($identity['email'], $identity['name']); $recipient = array_shift(rcube_mime::decode_address_list( $message->headers->mdn_to, 1, true, $message->headers->charset)); - $mailto = $recipient['mailto']; + $mailto = $recipient['mailto']; $compose = new Mail_mime("\r\n"); @@ -1782,6 +1782,107 @@ function rcmail_send_mdn($message, &$smtp_error) return false; } +/** + * Detect recipient identity from specified message + */ +function rcmail_identity_select($MESSAGE, $identities = null, $compose_mode = 'reply') +{ + $a_recipients = array(); + $a_names = array(); + + if ($identities === null) { + $identities = rcmail::get_instance()->user->list_identities(null, true); + } + + // extract all recipients of the reply-message + if (is_object($MESSAGE->headers) && in_array($compose_mode, array('reply', 'forward'))) { + $a_to = rcube_mime::decode_address_list($MESSAGE->headers->to, null, true, $MESSAGE->headers->charset); + foreach ($a_to as $addr) { + if (!empty($addr['mailto'])) { + $a_recipients[] = format_email($addr['mailto']); + $a_names[] = $addr['name']; + } + } + + if (!empty($MESSAGE->headers->cc)) { + $a_cc = rcube_mime::decode_address_list($MESSAGE->headers->cc, null, true, $MESSAGE->headers->charset); + foreach ($a_cc as $addr) { + if (!empty($addr['mailto'])) { + $a_recipients[] = format_email($addr['mailto']); + $a_names[] = $addr['name']; + } + } + } + } + + $from_idx = null; + $found_idx = null; + $default_identity = 0; // default identity is always first on the list + + // Select identity + foreach ($identities as $idx => $ident) { + // use From header + if (in_array($compose_mode, array('draft', 'edit'))) { + if ($MESSAGE->headers->from == $ident['ident']) { + $from_idx = $idx; + break; + } + } + // reply to yourself + else if ($compose_mode == 'reply' && $MESSAGE->headers->from == $ident['ident']) { + $from_idx = $idx; + break; + } + // use replied message recipients + else if (($found = array_search($ident['email_ascii'], $a_recipients)) !== false) { + if ($found_idx === null) { + $found_idx = $idx; + } + // match identity name + if ($a_names[$found] && $ident['name'] && $a_names[$found] == $ident['name']) { + $from_idx = $idx; + break; + } + } + } + + // If matching by name+address doesn't found any matches, get first found address (identity) + if ($from_idx === null) { + $from_idx = $found_idx; + } + + // Try Return-Path + if ($from_idx === null && ($return_path = $MESSAGE->headers->others['return-path'])) { + foreach ($identities as $idx => $ident) { + if (strpos($return_path, str_replace('@', '=', $ident['email_ascii']).'@') !== false) { + $from_idx = $idx; + break; + } + } + } + + // Fallback using Delivered-To + if ($from_idx === null && ($delivered_to = $MESSAGE->headers->others['delivered-to'])) { + foreach ($identities as $idx => $ident) { + if (in_array($ident['email_ascii'], (array)$delivered_to)) { + $from_idx = $idx; + break; + } + } + } + + // Fallback using Envelope-To + if ($from_idx === null && ($envelope_to = $MESSAGE->headers->others['envelope-to'])) { + foreach ($identities as $idx => $ident) { + if (in_array($ident['email_ascii'], (array)$envelope_to)) { + $from_idx = $idx; + break; + } + } + } + + return $identities[$from_idx !== null ? $from_idx : $default_identity]; +} // Fixes some content-type names function rcmail_fix_mimetype($name) -- cgit v1.2.3 From c0208002a804aa36cf6891e9875e2f8adc0deb2d Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Sat, 15 Dec 2012 18:32:42 +0100 Subject: Fix escaping of add-contact arguments - fixes JS error in IE --- program/steps/mail/func.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'program/steps/mail') diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index bd381d5c0..88391b102 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -1528,7 +1528,7 @@ function rcmail_address_string($input, $max=null, $linked=false, $addicon=null, if ($addicon && $_SESSION['writeable_abook']) { $address .= html::a(array( 'href' => "#add", - 'onclick' => sprintf("return %s.command('add-contact','%s',this)", JS_OBJECT_NAME, $string), + 'onclick' => sprintf("return %s.command('add-contact','%s',this)", JS_OBJECT_NAME, JQ($string)), 'title' => rcube_label('addtoaddressbook'), 'class' => 'rcmaddcontact', ), -- cgit v1.2.3 From 83370e5ff14f55f6af435807713956160f91abfa Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 18 Dec 2012 12:54:38 +0100 Subject: Display 'Sender' header in message preview --- CHANGELOG | 1 + program/lib/Roundcube/rcube_storage.php | 1 + program/localization/en_US/labels.inc | 1 + program/steps/mail/func.inc | 10 +++++++++- 4 files changed, 12 insertions(+), 1 deletion(-) (limited to 'program/steps/mail') diff --git a/CHANGELOG b/CHANGELOG index a89e02930..8cfeaf89d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Display 'Sender' header in message preview - Plugin API: Added message_before_send hook - Fix contact copy/add-to-group operations on search result (#1488862) - Use matching identity in MDN response (#1488864) diff --git a/program/lib/Roundcube/rcube_storage.php b/program/lib/Roundcube/rcube_storage.php index 65de2660c..8a36f1f9d 100644 --- a/program/lib/Roundcube/rcube_storage.php +++ b/program/lib/Roundcube/rcube_storage.php @@ -53,6 +53,7 @@ abstract class rcube_storage protected $all_headers = array( 'IN-REPLY-TO', 'BCC', + 'SENDER', 'MESSAGE-ID', 'CONTENT-TRANSFER-ENCODING', 'REFERENCES', diff --git a/program/localization/en_US/labels.inc b/program/localization/en_US/labels.inc index abb0dca5d..730e6af09 100644 --- a/program/localization/en_US/labels.inc +++ b/program/localization/en_US/labels.inc @@ -41,6 +41,7 @@ $labels['junk'] = 'Junk'; // message listing $labels['subject'] = 'Subject'; $labels['from'] = 'From'; +$labels['sender'] = 'Sender'; $labels['to'] = 'To'; $labels['cc'] = 'Cc'; $labels['bcc'] = 'Bcc'; diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index 88391b102..f5165399b 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -967,7 +967,7 @@ function rcmail_message_headers($attrib, $headers=null) } // show these headers - $standard_headers = array('subject', 'from', 'to', 'cc', 'bcc', 'replyto', + $standard_headers = array('subject', 'from', 'sender', 'to', 'cc', 'bcc', 'replyto', 'mail-reply-to', 'mail-followup-to', 'date', 'priority'); $exclude_headers = $attrib['exclude'] ? explode(',', $attrib['exclude']) : array(); $output_headers = array(); @@ -1018,6 +1018,14 @@ function rcmail_message_headers($attrib, $headers=null) else continue; } + else if ($hkey == 'sender') { + if ($headers['sender'] != $headers['from']) { + $header_value = rcmail_address_string($value, $attrib['max'], true, $attrib['addicon'], $headers['charset'], $header_title); + $ishtml = true; + } + else + continue; + } else if ($hkey == 'mail-followup-to') { $header_value = rcmail_address_string($value, $attrib['max'], true, $attrib['addicon'], $headers['charset'], $header_title); $ishtml = true; -- cgit v1.2.3 From 090c49d4a3343d346677ed1687ae28697f5b1c6e Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Sat, 22 Dec 2012 16:39:47 +0100 Subject: Treat image/pjpeg as image/jpeg --- program/steps/mail/func.inc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'program/steps/mail') diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index f5165399b..70493766b 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -1290,7 +1290,7 @@ function rcmail_part_image_type($part) // Content-Type: image/*... if (preg_match($mime_regex, $part->mimetype)) { - return $part->mimetype; + return rcmail_fix_mimetype($part->mimetype); } // Many clients use application/octet-stream, we'll detect mimetype @@ -1900,6 +1900,10 @@ function rcmail_fix_mimetype($name) if (preg_match('/^application\/pdf.+/', $name)) $name = 'application/pdf'; + // treat image/pjpeg as image/jpeg + else if (preg_match('/^image\/p?jpe?g$/', $name)) + $name = 'image/jpeg'; + return $name; } -- cgit v1.2.3 From 5f0bd1fcb40eae92e747e0e855b77639fd17fc4a Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Sat, 22 Dec 2012 16:40:20 +0100 Subject: Make html button-links work in IE --- program/steps/mail/get.inc | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'program/steps/mail') diff --git a/program/steps/mail/get.inc b/program/steps/mail/get.inc index 314a437e7..803716d61 100644 --- a/program/steps/mail/get.inc +++ b/program/steps/mail/get.inc @@ -166,12 +166,11 @@ else if (strlen($pid = get_input_value('_part', RCUBE_INPUT_GET))) { 'vars' => array('expected' => "$mimetype (.$file_extension)", 'detected' => "$real_mimetype (.$extensions[0])") )) . html::p('buttons', - html::tag('button', null, - html::a(array( - 'href' => $RCMAIL->url(array_merge($_GET, array('_nocheck' => 1))), - 'style' => 'text-decoration:none;color:#000', - ), rcube_label('showanyway'))) - )) + html::a(array( + 'href' => $RCMAIL->url(array_merge($_GET, array('_nocheck' => 1))), + 'style' => 'text-decoration:none;color:#000', + ), html::tag('button', null, rcube_label('showanyway')))) + ) ))); exit; } -- cgit v1.2.3 From 7ac94421bf85eb04c00c5ed05390e1ea0c6bcb0b Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 25 Dec 2012 18:06:17 +0100 Subject: Move washtml class into Roundcube Framework (rcube_washtml), add some improvements --- program/include/bc.php | 4 + program/lib/Roundcube/rcube_washtml.php | 451 ++++++++++++++++++++++++++++++++ program/lib/washtml.php | 330 ----------------------- program/steps/mail/func.inc | 68 +---- tests/Framework/Washtml.php | 28 ++ tests/MailFunc.php | 2 +- tests/phpunit.xml | 1 + 7 files changed, 486 insertions(+), 398 deletions(-) create mode 100644 program/lib/Roundcube/rcube_washtml.php delete mode 100644 program/lib/washtml.php create mode 100644 tests/Framework/Washtml.php (limited to 'program/steps/mail') diff --git a/program/include/bc.php b/program/include/bc.php index dc4d54fd7..05d15b9e3 100644 --- a/program/include/bc.php +++ b/program/include/bc.php @@ -408,3 +408,7 @@ function enriched_to_html($data) class rcube_html_page extends rcmail_html_page { } + +class washtml extends rcube_washtml +{ +} diff --git a/program/lib/Roundcube/rcube_washtml.php b/program/lib/Roundcube/rcube_washtml.php new file mode 100644 index 000000000..715c46047 --- /dev/null +++ b/program/lib/Roundcube/rcube_washtml.php @@ -0,0 +1,451 @@ + | + | Author: Aleksander Machniak | + | Author: Frederic Motte | + +-----------------------------------------------------------------------+ + */ + +/** + * Washtml, a HTML sanityzer. + * + * Copyright (c) 2007 Frederic Motte + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * OVERVIEW: + * + * Wahstml take an untrusted HTML and return a safe html string. + * + * SYNOPSIS: + * + * $washer = new washtml($config); + * $washer->wash($html); + * It return a sanityzed string of the $html parameter without html and head tags. + * $html is a string containing the html code to wash. + * $config is an array containing options: + * $config['allow_remote'] is a boolean to allow link to remote images. + * $config['blocked_src'] string with image-src to be used for blocked remote images + * $config['show_washed'] is a boolean to include washed out attributes as x-washed + * $config['cid_map'] is an array where cid urls index urls to replace them. + * $config['charset'] is a string containing the charset of the HTML document if it is not defined in it. + * $washer->extlinks is a reference to a boolean that is set to true if remote images were removed. (FE: show remote images link) + * + * INTERNALS: + * + * Only tags and attributes in the static lists $html_elements and $html_attributes + * are kept, inline styles are also filtered: all style identifiers matching + * /[a-z\-]/i are allowed. Values matching colors, sizes, /[a-z\-]/i and safe + * urls if allowed and cid urls if mapped are kept. + * + * Roundcube Changes: + * - added $block_elements + * - changed $ignore_elements behaviour + * - added RFC2397 support + * - base URL support + * - invalid HTML comments removal before parsing + * - "fixing" unitless CSS values for XHTML output + * - base url resolving + */ + +/** + * Utility class providing HTML sanityzer + * + * @package Framework + * @subpackage Utils + */ +class rcube_washtml +{ + /* Allowed HTML elements (default) */ + static $html_elements = array('a', 'abbr', 'acronym', 'address', 'area', 'b', + 'basefont', 'bdo', 'big', 'blockquote', 'br', 'caption', 'center', + 'cite', 'code', 'col', 'colgroup', 'dd', 'del', 'dfn', 'dir', 'div', 'dl', + 'dt', 'em', 'fieldset', 'font', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', + 'ins', 'label', 'legend', 'li', 'map', 'menu', 'nobr', 'ol', 'p', 'pre', 'q', + 's', 'samp', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'table', + 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'tt', 'u', 'ul', 'var', 'wbr', 'img', + // form elements + 'button', 'input', 'textarea', 'select', 'option', 'optgroup' + ); + + /* Ignore these HTML tags and their content */ + static $ignore_elements = array('script', 'applet', 'embed', 'object', 'style'); + + /* Allowed HTML attributes */ + static $html_attribs = array('name', 'class', 'title', 'alt', 'width', 'height', + 'align', 'nowrap', 'col', 'row', 'id', 'rowspan', 'colspan', 'cellspacing', + 'cellpadding', 'valign', 'bgcolor', 'color', 'border', 'bordercolorlight', + 'bordercolordark', 'face', 'marginwidth', 'marginheight', 'axis', 'border', + 'abbr', 'char', 'charoff', 'clear', 'compact', 'coords', 'vspace', 'hspace', + 'cellborder', 'size', 'lang', 'dir', 'usemap', 'shape', 'media', + // attributes of form elements + 'type', 'rows', 'cols', 'disabled', 'readonly', 'checked', 'multiple', 'value' + ); + + /* Block elements which could be empty but cannot be returned in short form () */ + static $block_elements = array('div', 'p', 'pre', 'blockquote', 'a', 'font', 'center', + 'table', 'ul', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'dl', 'strong', + 'i', 'b', 'u', 'span', + ); + + /* State for linked objects in HTML */ + public $extlinks = false; + + /* Current settings */ + private $config = array(); + + /* Registered callback functions for tags */ + private $handlers = array(); + + /* Allowed HTML elements */ + private $_html_elements = array(); + + /* Ignore these HTML tags but process their content */ + private $_ignore_elements = array(); + + /* Block elements which could be empty but cannot be returned in short form () */ + private $_block_elements = array(); + + /* Allowed HTML attributes */ + private $_html_attribs = array(); + + + /** + * Class constructor + */ + public function __construct($p = array()) + { + $this->_html_elements = array_flip((array)$p['html_elements']) + array_flip(self::$html_elements) ; + $this->_html_attribs = array_flip((array)$p['html_attribs']) + array_flip(self::$html_attribs); + $this->_ignore_elements = array_flip((array)$p['ignore_elements']) + array_flip(self::$ignore_elements); + $this->_block_elements = array_flip((array)$p['block_elements']) + array_flip(self::$block_elements); + + unset($p['html_elements'], $p['html_attribs'], $p['ignore_elements'], $p['block_elements']); + + $this->config = $p + array('show_washed' => true, 'allow_remote' => false, 'cid_map' => array()); + } + + /** + * Register a callback function for a certain tag + */ + public function add_callback($tagName, $callback) + { + $this->handlers[$tagName] = $callback; + } + + /** + * Check CSS style + */ + private function wash_style($style) + { + $s = ''; + + foreach (explode(';', $style) as $declaration) { + if (preg_match('/^\s*([a-z\-]+)\s*:\s*(.*)\s*$/i', $declaration, $match)) { + $cssid = $match[1]; + $str = $match[2]; + $value = ''; + + while (sizeof($str) > 0 && + preg_match('/^(url\(\s*[\'"]?([^\'"\)]*)[\'"]?\s*\)'./*1,2*/ + '|rgb\(\s*[0-9]+\s*,\s*[0-9]+\s*,\s*[0-9]+\s*\)'. + '|-?[0-9.]+\s*(em|ex|px|cm|mm|in|pt|pc|deg|rad|grad|ms|s|hz|khz|%)?'. + '|#[0-9a-f]{3,6}'. + '|[a-z0-9", -]+'. + ')\s*/i', $str, $match) + ) { + if ($match[2]) { + if (($src = $this->config['cid_map'][$match[2]]) + || ($src = $this->config['cid_map'][$this->config['base_url'].$match[2]]) + ) { + $value .= ' url('.htmlspecialchars($src, ENT_QUOTES) . ')'; + } + else if (preg_match('!^(https?:)?//[a-z0-9/._+-]+$!i', $match[2], $url)) { + if ($this->config['allow_remote']) { + $value .= ' url('.htmlspecialchars($url[0], ENT_QUOTES).')'; + } + else { + $this->extlinks = true; + } + } + else if (preg_match('/^data:.+/i', $match[2])) { // RFC2397 + $value .= ' url('.htmlspecialchars($match[2], ENT_QUOTES).')'; + } + } + else { + // whitelist ? + $value .= ' ' . $match[0]; + + // #1488535: Fix size units, so width:800 would be changed to width:800px + if (preg_match('/(left|right|top|bottom|width|height)/i', $cssid) + && preg_match('/^[0-9]+$/', $match[0]) + ) { + $value .= 'px'; + } + } + + $str = substr($str, strlen($match[0])); + } + + if (isset($value[0])) { + $s .= ($s?' ':'') . $cssid . ':' . $value . ';'; + } + } + } + + return $s; + } + + /** + * Take a node and return allowed attributes and check values + */ + private function wash_attribs($node) + { + $t = ''; + $washed = ''; + + foreach ($node->attributes as $key => $plop) { + $key = strtolower($key); + $value = $node->getAttribute($key); + + if (isset($this->_html_attribs[$key]) || + ($key == 'href' && !preg_match('!^(javascript|vbscript|data:text)!i', $value) + && preg_match('!^([a-z][a-z0-9.+-]+:|//|#).+!i', $value)) + ) { + $t .= ' ' . $key . '="' . htmlspecialchars($value, ENT_QUOTES) . '"'; + } + else if ($key == 'style' && ($style = $this->wash_style($value))) { + $quot = strpos($style, '"') !== false ? "'" : '"'; + $t .= ' style=' . $quot . $style . $quot; + } + else if ($key == 'background' || ($key == 'src' && strtolower($node->tagName) == 'img')) { //check tagName anyway + if (($src = $this->config['cid_map'][$value]) + || ($src = $this->config['cid_map'][$this->config['base_url'].$value]) + ) { + $t .= ' ' . $key . '="' . htmlspecialchars($src, ENT_QUOTES) . '"'; + } + else if (preg_match('/^(http|https|ftp):.+/i', $value)) { + if ($this->config['allow_remote']) { + $t .= ' ' . $key . '="' . htmlspecialchars($value, ENT_QUOTES) . '"'; + } + else { + $this->extlinks = true; + if ($this->config['blocked_src']) { + $t .= ' ' . $key . '="' . htmlspecialchars($this->config['blocked_src'], ENT_QUOTES) . '"'; + } + } + } + else if (preg_match('/^data:.+/i', $value)) { // RFC2397 + $t .= ' ' . $key . '="' . htmlspecialchars($value, ENT_QUOTES) . '"'; + } + } + else { + $washed .= ($washed ? ' ' : '') . $key; + } + } + + return $t . ($washed && $this->config['show_washed'] ? ' x-washed="'.$washed.'"' : ''); + } + + /** + * The main loop that recurse on a node tree. + * It output only allowed tags with allowed attributes + * and allowed inline styles + */ + private function dumpHtml($node) + { + if (!$node->hasChildNodes()) { + return ''; + } + + $node = $node->firstChild; + $dump = ''; + + do { + switch($node->nodeType) { + case XML_ELEMENT_NODE: //Check element + $tagName = strtolower($node->tagName); + if ($callback = $this->handlers[$tagName]) { + $dump .= call_user_func($callback, $tagName, + $this->wash_attribs($node), $this->dumpHtml($node), $this); + } + else if (isset($this->_html_elements[$tagName])) { + $content = $this->dumpHtml($node); + $dump .= '<' . $tagName . $this->wash_attribs($node) . + ($content != '' || isset($this->_block_elements[$tagName]) ? ">$content" : ' />'); + } + else if (isset($this->_ignore_elements[$tagName])) { + $dump .= ''; + } + else { + $dump .= ''; + $dump .= $this->dumpHtml($node); // ignore tags not its content + } + break; + + case XML_CDATA_SECTION_NODE: + $dump .= $node->nodeValue; + break; + + case XML_TEXT_NODE: + $dump .= htmlspecialchars($node->nodeValue); + break; + + case XML_HTML_DOCUMENT_NODE: + $dump .= $this->dumpHtml($node); + break; + + case XML_DOCUMENT_TYPE_NODE: + break; + + default: + $dump . ''; + } + } while($node = $node->nextSibling); + + return $dump; + } + + /** + * Main function, give it untrusted HTML, tell it if you allow loading + * remote images and give it a map to convert "cid:" urls. + */ + public function wash($html) + { + // Charset seems to be ignored (probably if defined in the HTML document) + $node = new DOMDocument('1.0', $this->config['charset']); + $this->extlinks = false; + + $html = $this->cleanup($html); + + // Find base URL for images + if (preg_match('/config['base_url'] = $matches[1]; + } + else { + $this->config['base_url'] = ''; + } + + @$node->loadHTML($html); + return $this->dumpHtml($node); + } + + /** + * Getter for config parameters + */ + public function get_config($prop) + { + return $this->config[$prop]; + } + + /** + * Clean HTML input + */ + private function cleanup($html) + { + // special replacements (not properly handled by washtml class) + $html_search = array( + '/(<\/nobr>)(\s+)()/i', // space(s) between + '/]*>[^<]*<\/title>/i', // PHP bug #32547 workaround: remove title tag + '/^(\0\0\xFE\xFF|\xFF\xFE\0\0|\xFE\xFF|\xFF\xFE|\xEF\xBB\xBF)/', // byte-order mark (only outlook?) + '/]+>/i', // washtml/DOMDocument cannot handle xml namespaces + ); + + $html_replace = array( + '\\1'.'   '.'\\3', + '', + '', + '', + ); + $html = preg_replace($html_search, $html_replace, trim($html)); + + // PCRE errors handling (#1486856), should we use something like for every preg_* use? + if ($html === null && ($preg_error = preg_last_error()) != PREG_NO_ERROR) { + $errstr = "Could not clean up HTML message! PCRE Error: $preg_error."; + + if ($preg_error == PREG_BACKTRACK_LIMIT_ERROR) { + $errstr .= " Consider raising pcre.backtrack_limit!"; + } + if ($preg_error == PREG_RECURSION_LIMIT_ERROR) { + $errstr .= " Consider raising pcre.recursion_limit!"; + } + + rcube::raise_error(array('code' => 620, 'type' => 'php', + 'line' => __LINE__, 'file' => __FILE__, + 'message' => $errstr), true, false); + return ''; + } + + // fix (unknown/malformed) HTML tags before "wash" + $html = preg_replace_callback('/(<[\/]*)([^\s>]+)/', array($this, 'html_tag_callback'), $html); + + // Remove invalid HTML comments (#1487759) + // Don't remove valid conditional comments + $html = preg_replace('/'; - } - else { - $dump .= ''; - $dump .= $this->dumpHtml($node); // ignore tags not its content - } - break; - case XML_CDATA_SECTION_NODE: - $dump .= $node->nodeValue; - break; - case XML_TEXT_NODE: - $dump .= htmlspecialchars($node->nodeValue); - break; - case XML_HTML_DOCUMENT_NODE: - $dump .= $this->dumpHtml($node); - break; - case XML_DOCUMENT_TYPE_NODE: - break; - default: - $dump . ''; - } - } while($node = $node->nextSibling); - - return $dump; - } - - /* Main function, give it untrusted HTML, tell it if you allow loading - * remote images and give it a map to convert "cid:" urls. */ - public function wash($html) - { - // Charset seems to be ignored (probably if defined in the HTML document) - $node = new DOMDocument('1.0', $this->config['charset']); - $this->extlinks = false; - - // Find base URL for images - if (preg_match('/config['base_url'] = $matches[1]; - else - $this->config['base_url'] = ''; - - // Remove invalid HTML comments (#1487759) - // Don't remove valid conditional comments - $html = preg_replace('/

+
+ +
-- cgit v1.2.3 From 4223b1e38ff38d74101cc2e2d6b33bb1050cf716 Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Thu, 7 Feb 2013 17:52:22 +0100 Subject: Use encoded identifiers for address book lists (#1488381) --- program/steps/mail/compose.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'program/steps/mail') diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index 74c6d5f29..b082c2652 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -1558,7 +1558,7 @@ function rcmail_addressbook_list($attrib = array()) $class_name .= ' ' . $source['class_name']; $out .= sprintf($line_templ, - html_identifier($id), + html_identifier($id,true), $class_name, $source['id'], $js_id, (!empty($source['name']) ? $source['name'] : $id)); -- cgit v1.2.3 From 921115ed56cfcd0527b94bf599d4a80bc7f86616 Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Thu, 7 Feb 2013 18:17:25 +0100 Subject: Ignore file extension difference for text/plain attachments (#1488891) --- program/steps/mail/get.inc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'program/steps/mail') diff --git a/program/steps/mail/get.inc b/program/steps/mail/get.inc index fddb40ae8..84e666fbe 100644 --- a/program/steps/mail/get.inc +++ b/program/steps/mail/get.inc @@ -121,7 +121,7 @@ else if (strlen($pid = get_input_value('_part', RCUBE_INPUT_GET))) { $valid = $file_extension && in_array($file_extension, (array)$extensions); // 2. detect the real mimetype of the attachment part and compare it with the stated mimetype and filename extension - if ($valid || !$file_extension || $mimetype == 'application/octet-stream') { + if ($valid || !$file_extension || $mimetype == 'application/octet-stream' || $mimetype == 'text/plain') { if ($part->body) // part body is already loaded $body = $part->body; else if ($part->size && $part->size < 1024*1024) // load the entire part if it's small enough @@ -133,6 +133,10 @@ else if (strlen($pid = get_input_value('_part', RCUBE_INPUT_GET))) { $real_mimetype = rcube_mime::file_content_type($body, $part->filename, $mimetype, true, true); list($real_ctype_primary, $real_ctype_secondary) = explode('/', $real_mimetype); + // accept text/plain with any extension + if ($real_mimetype == 'text/plain' && $real_mimetype == $mimetype) + $file_extension = 'txt'; + // ignore differences in text/* mimetypes. Filetype detection isn't very reliable here if ($real_ctype_primary == 'text' && strpos($mimetype, $real_ctype_primary) === 0) $real_mimetype = $mimetype; -- cgit v1.2.3 From 726297e5f8d84cbb434f9c4185f3cd1aaebe8e6e Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 19 Feb 2013 16:56:02 +0100 Subject: Add workaround for invalid message charset detection by IMAP servers (#1488968) --- CHANGELOG | 3 ++- program/lib/Roundcube/rcube_imap.php | 30 +++++++++++++++++++++++------- program/steps/mail/func.inc | 2 +- 3 files changed, 26 insertions(+), 9 deletions(-) (limited to 'program/steps/mail') diff --git a/CHANGELOG b/CHANGELOG index 1ee72e953..cc83c6ab6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail -=========================== +========================a=== +- Add workaround for invalid message charset detection by IMAP servers (#1488968) - Fix NUL characters in content-type of ms-tnef attachment (#1488964) - Fix regression in handling LDAP contact identifiers (#1488959) - Updated translations from Transifex diff --git a/program/lib/Roundcube/rcube_imap.php b/program/lib/Roundcube/rcube_imap.php index 74c1f5324..18c6b12af 100644 --- a/program/lib/Roundcube/rcube_imap.php +++ b/program/lib/Roundcube/rcube_imap.php @@ -1634,9 +1634,15 @@ class rcube_imap extends rcube_storage // Example of structure for malformed MIME message: // ("text" "plain" NIL NIL NIL "7bit" 2154 70 NIL NIL NIL) if ($headers->ctype && !is_array($structure[0]) && $headers->ctype != 'text/plain' - && strtolower($structure[0].'/'.$structure[1]) == 'text/plain') { + && strtolower($structure[0].'/'.$structure[1]) == 'text/plain' + ) { + // A special known case "Content-type: text" (#1488968) + if ($headers->ctype == 'text') { + $structure[1] = 'plain'; + $headers->ctype = 'text/plain'; + } // we can handle single-part messages, by simple fix in structure (#1486898) - if (preg_match('/^(text|application)\/(.*)/', $headers->ctype, $m)) { + else if (preg_match('/^(text|application)\/(.*)/', $headers->ctype, $m)) { $structure[0] = $m[1]; $structure[1] = $m[2]; } @@ -1660,11 +1666,21 @@ class rcube_imap extends rcube_storage $struct = $this->structure_part($structure, 0, '', $headers); } - // don't trust given content-type - if (empty($struct->parts) && !empty($headers->ctype)) { - $struct->mime_id = '1'; - $struct->mimetype = strtolower($headers->ctype); - list($struct->ctype_primary, $struct->ctype_secondary) = explode('/', $struct->mimetype); + // some workarounds on simple messages... + if (empty($struct->parts)) { + // ...don't trust given content-type + if (!empty($headers->ctype)) { + $struct->mime_id = '1'; + $struct->mimetype = strtolower($headers->ctype); + list($struct->ctype_primary, $struct->ctype_secondary) = explode('/', $struct->mimetype); + } + + // ...and charset (there's a case described in #1488968 where invalid content-type + // results in invalid charset in BODYSTRUCTURE) + if (!empty($headers->charset) && $headers->charset != $struct->ctype_parameters['charset']) { + $struct->charset = $headers->charset; + $struct->ctype_parameters['charset'] = $headers->charset; + } } $headers->structure = $struct; diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index 6b8879dcf..36ac1aa2b 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -1854,7 +1854,7 @@ function rcmail_attachment_name($attachment, $display = false) $filename = rcube_label('htmlmessage'); } else { - $ext = rcube_mime::get_mime_extensions($attachment->mimetype); + $ext = (array) rcube_mime::get_mime_extensions($attachment->mimetype); $ext = array_shift($ext); $filename = rcube_label('messagepart') . ' ' . $attachment->mime_id; if ($ext) { -- cgit v1.2.3 From 7c851e669e660655b6d7bd59dd2cd7cfe7d5fff3 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 21 Feb 2013 09:38:21 +0100 Subject: Don't try to print file extension in a warning message if it's not available --- program/steps/mail/get.inc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'program/steps/mail') diff --git a/program/steps/mail/get.inc b/program/steps/mail/get.inc index 84e666fbe..54d1a4e77 100644 --- a/program/steps/mail/get.inc +++ b/program/steps/mail/get.inc @@ -162,7 +162,10 @@ else if (strlen($pid = get_input_value('_part', RCUBE_INPUT_GET))) { html::div(array('class' => 'rcmail-inline-message rcmail-inline-warning'), rcube_label(array( 'name' => 'attachmentvalidationerror', - 'vars' => array('expected' => "$mimetype (.$file_extension)", 'detected' => "$real_mimetype (.$extensions[0])") + 'vars' => array( + 'expected' => $mimetype . ($file_extension ? "(.$file_extension)" : ''), + 'detected' => $real_mimetype . ($extensions[0] ? "(.$extensions[0])" : ''), + ) )) . html::p(array('class' => 'rcmail-inline-buttons'), html::tag('button', -- cgit v1.2.3 From bd1e16f3436786875cb40074d27230a5428693e6 Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Thu, 28 Feb 2013 22:03:40 +0100 Subject: Don't skip message/* attachments when opening a draft. Restore check as it was before [fa57c988] --- program/steps/mail/compose.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'program/steps/mail') diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index b082c2652..de75c88d5 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -982,7 +982,7 @@ function rcmail_write_compose_attachments(&$message, $bodyIsHtml) foreach ((array)$message->mime_parts as $pid => $part) { if ($part->disposition == 'attachment' || ($part->disposition == 'inline' && $bodyIsHtml) || $part->filename) { - if ($part->ctype_primary == 'message' || $part->ctype_primary == 'multipart') { + if (($part->ctype_primary == 'message' && $bodyIsHtml) || $part->ctype_primary == 'multipart') { continue; } if ($part->mimetype == 'application/ms-tnef') { -- cgit v1.2.3 From f0a7159c401983e7dbc9620582124f90f3e4eadc Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Sat, 2 Mar 2013 00:10:54 +0100 Subject: Add methods to append certain nodes to session data in order to avoid session saving race conditions. Fixes #1488422 --- program/lib/Roundcube/rcube_session.php | 58 ++++++++++++++++++++++++++++++--- program/steps/mail/attachments.inc | 16 ++++----- 2 files changed, 61 insertions(+), 13 deletions(-) (limited to 'program/steps/mail') diff --git a/program/lib/Roundcube/rcube_session.php b/program/lib/Roundcube/rcube_session.php index 1aa5d5856..82ff8a804 100644 --- a/program/lib/Roundcube/rcube_session.php +++ b/program/lib/Roundcube/rcube_session.php @@ -32,6 +32,7 @@ class rcube_session private $ip; private $start; private $changed; + private $reloaded = false; private $unsets = array(); private $gc_handlers = array(); private $cookiename = 'roundcube_sessauth'; @@ -200,8 +201,13 @@ class rcube_session if ($oldvars !== null) { $a_oldvars = $this->unserialize($oldvars); if (is_array($a_oldvars)) { - foreach ((array)$this->unsets as $k) - unset($a_oldvars[$k]); + // remove unset keys on oldvars + foreach ((array)$this->unsets as $var) { + $path = explode('.', $var); + $k = array_pop($path); + $node = &$this->get_node($path, $a_oldvars); + unset($node[$k]); + } $newvars = $this->serialize(array_merge( (array)$a_oldvars, (array)$this->unserialize($vars))); @@ -370,10 +376,33 @@ class rcube_session } + /** + * Append the given value to the certain node in the session data array + * + * @param string Path denoting the session variable where to append the value + * @param string Key name under which to append the new value (use null for appending to an indexed list) + * @param mixed Value to append to the session data array + */ + public function append($path, $key, $value) + { + // re-read session data from DB because it might be outdated + if (!$this->reloaded && microtime(true) - $this->start > 0.5) { + $this->reload(); + $this->reloaded = true; + $this->start = microtime(true); + } + + $node = &$this->get_node(explode('.', $path), $_SESSION); + + if ($key !== null) $node[$key] = $value; + else $node[] = $value; + } + + /** * Unset a session variable * - * @param string Varibale name + * @param string Varibale name (can be a path denoting a certain node in the session array, e.g. compose.attachments.5) * @return boolean True on success */ public function remove($var=null) @@ -383,7 +412,11 @@ class rcube_session } $this->unsets[] = $var; - unset($_SESSION[$var]); + + $path = explode('.', $var); + $key = array_pop($path); + $node = &$this->get_node($path, $_SESSION); + unset($node[$key]); return true; } @@ -415,6 +448,23 @@ class rcube_session session_decode($data); } + /** + * Returns a reference to the node in data array referenced by the given path. + * e.g. ['compose','attachments'] will return $_SESSION['compose']['attachments'] + */ + private function &get_node($path, &$data_arr) + { + $node = &$data_arr; + if (!empty($path)) { + foreach ((array)$path as $key) { + if (!isset($node[$key])) + $node[$key] = array(); + $node = &$node[$key]; + } + } + + return $node; + } /** * Serialize session data diff --git a/program/steps/mail/attachments.inc b/program/steps/mail/attachments.inc index 180fc0bb9..f83f6892e 100644 --- a/program/steps/mail/attachments.inc +++ b/program/steps/mail/attachments.inc @@ -27,8 +27,10 @@ if (!empty($_GET['_progress'])) { $COMPOSE_ID = get_input_value('_id', RCUBE_INPUT_GPC); $COMPOSE = null; -if ($COMPOSE_ID && $_SESSION['compose_data_'.$COMPOSE_ID]) - $COMPOSE =& $_SESSION['compose_data_'.$COMPOSE_ID]; +if ($COMPOSE_ID && $_SESSION['compose_data_' . $COMPOSE_ID]) { + $SESSION_KEY = 'compose_data_' . $COMPOSE_ID; + $COMPOSE =& $_SESSION[$SESSION_KEY]; +} if (!$COMPOSE) { die("Invalid session var!"); @@ -45,7 +47,7 @@ if ($RCMAIL->action=='remove-attachment') $attachment = $RCMAIL->plugins->exec_hook('attachment_delete', $attachment); if ($attachment['status']) { if (is_array($COMPOSE['attachments'][$id])) { - unset($COMPOSE['attachments'][$id]); + $RCMAIL->session->remove($SESSION_KEY.'.attachments.'.$id); $OUTPUT->command('remove_from_attachment_list', "rcmfile$id"); } } @@ -77,11 +79,7 @@ if ($RCMAIL->action=='display-attachment') exit; } -// attachment upload action - -if (!is_array($COMPOSE['attachments'])) { - $COMPOSE['attachments'] = array(); -} +/***** attachment upload action *****/ // clear all stored output properties (like scripts and env vars) $OUTPUT->reset(); @@ -112,7 +110,7 @@ if (is_array($_FILES['_attachments']['tmp_name'])) { // store new attachment in session unset($attachment['status'], $attachment['abort']); - $COMPOSE['attachments'][$id] = $attachment; + $RCMAIL->session->append($SESSION_KEY.'.attachments', $id, $attachment); if (($icon = $COMPOSE['deleteicon']) && is_file($icon)) { $button = html::img(array( -- cgit v1.2.3 From 5688d5baa28e310467528c4f3c187bf8f6edf0fb Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Sat, 2 Mar 2013 00:12:04 +0100 Subject: Avoid loading already registered attachments when opening a draft message (on page reload) --- program/steps/mail/compose.inc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'program/steps/mail') diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index de75c88d5..25cf63f70 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -978,6 +978,11 @@ function rcmail_write_compose_attachments(&$message, $bodyIsHtml) { global $RCMAIL, $COMPOSE, $compose_mode; + $loaded_attachments = array(); + foreach ((array)$COMPOSE['attachments'] as $id => $attachment) { + $loaded_attachments[$attachment['name'] . $attachment['mimetype']] = $attachment; + } + $cid_map = $messages = array(); foreach ((array)$message->mime_parts as $pid => $part) { @@ -1005,7 +1010,8 @@ function rcmail_write_compose_attachments(&$message, $bodyIsHtml) } } - if (!$skip && ($attachment = rcmail_save_attachment($message, $pid))) { + if (!$skip && (($attachment = $loaded_attachments[rcmail_attachment_name($part) . $part->mimetype]) + || ($attachment = rcmail_save_attachment($message, $pid)))) { $COMPOSE['attachments'][$attachment['id']] = $attachment; if ($bodyIsHtml && ($part->content_id || $part->content_location)) { $url = sprintf('%s&_id=%s&_action=display-attachment&_file=rcmfile%s', -- cgit v1.2.3 From ee89c6dff6a309bd6d024a725eb24d79b4ac1236 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Sat, 2 Mar 2013 16:13:08 +0100 Subject: Display notice that message is encrypted also for application/pkcs7-mime messages (#1488526) --- program/lib/Roundcube/rcube_message.php | 11 +++++++++++ program/steps/mail/func.inc | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) (limited to 'program/steps/mail') diff --git a/program/lib/Roundcube/rcube_message.php b/program/lib/Roundcube/rcube_message.php index e0c3e3475..dc7080746 100644 --- a/program/lib/Roundcube/rcube_message.php +++ b/program/lib/Roundcube/rcube_message.php @@ -468,6 +468,17 @@ class rcube_message $this->parts[] = $p; } + // this is an S/MIME ecrypted message -> create a plaintext body with the according message + else if ($mimetype == 'application/pkcs7-mime') { + $p = new stdClass; + $p->type = 'content'; + $p->ctype_primary = 'text'; + $p->ctype_secondary = 'plain'; + $p->mimetype = 'text/plain'; + $p->realtype = 'application/pkcs7-mime'; + + $this->parts[] = $p; + } // message contains multiple parts else if (is_array($structure->parts) && !empty($structure->parts)) { // iterate over parts diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index 36ac1aa2b..4a3476320 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -1083,9 +1083,9 @@ function rcmail_message_body($attrib) $out .= html::div('message-partheaders', rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : null, $part->headers)); } else if ($part->type == 'content') { - // unsapported + // unsupported (e.g. encrypted) if ($part->realtype) { - if ($part->realtype == 'multipart/encrypted') { + if ($part->realtype == 'multipart/encrypted' || $part->realtype == 'application/pkcs7-mime') { $out .= html::span('part-notice', rcube_label('encryptedmessage')); } continue; -- cgit v1.2.3 From e7c1aad83208b343634a1b857b53474c97a74f61 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Sat, 2 Mar 2013 20:29:20 +0100 Subject: Even better message on over quota error in move to trash operation (#1484164) --- program/include/bc.php | 4 ++-- program/include/rcmail.php | 29 ++++++++++++++++++++--------- program/localization/en_US/messages.inc | 3 ++- program/steps/mail/move_del.inc | 9 +++++---- 4 files changed, 29 insertions(+), 16 deletions(-) (limited to 'program/steps/mail') diff --git a/program/include/bc.php b/program/include/bc.php index 3d9d46289..d8356338d 100644 --- a/program/include/bc.php +++ b/program/include/bc.php @@ -205,9 +205,9 @@ function rcmail_quota_content($attrib = null) return rcmail::get_instance()->quota_content($attrib); } -function rcmail_display_server_error($fallback=null, $fallback_args=null) +function rcmail_display_server_error($fallback=null, $fallback_args=null, $suffix='') { - rcmail::get_instance()->display_server_error($fallback, $fallback_args); + rcmail::get_instance()->display_server_error($fallback, $fallback_args, $suffix); } function rcmail_filetype2classname($mimetype, $filename) diff --git a/program/include/rcmail.php b/program/include/rcmail.php index 667be14bc..30d1fe84c 100644 --- a/program/include/rcmail.php +++ b/program/include/rcmail.php @@ -1798,39 +1798,50 @@ class rcmail extends rcube * * @param string $fallback Fallback message label * @param array $fallback_args Fallback message label arguments + * @param string $suffix Message label suffix */ - public function display_server_error($fallback = null, $fallback_args = null) + public function display_server_error($fallback = null, $fallback_args = null, $suffix = '') { $err_code = $this->storage->get_error_code(); $res_code = $this->storage->get_response_code(); + $args = array(); if ($res_code == rcube_storage::NOPERM) { - $this->output->show_message('errornoperm', 'error'); + $error = 'errornoperm'; } else if ($res_code == rcube_storage::READONLY) { - $this->output->show_message('errorreadonly', 'error'); + $error = 'errorreadonly'; } else if ($res_code == rcube_storage::OVERQUOTA) { - $this->output->show_message('errorroverquota', 'error'); + $error = 'errorroverquota'; } else if ($err_code && ($err_str = $this->storage->get_error_str())) { // try to detect access rights problem and display appropriate message if (stripos($err_str, 'Permission denied') !== false) { - $this->output->show_message('errornoperm', 'error'); + $error = 'errornoperm'; } // try to detect full mailbox problem and display appropriate message else if (stripos($err_str, 'Quota exceeded') !== false) { - $this->output->show_message('erroroverquota', 'error'); + $error = 'erroroverquota'; } else { - $this->output->show_message('servererrormsg', 'error', array('msg' => $err_str)); + $error = 'servererrormsg'; + $args = array('msg' => $err_str); } } else if ($err_code < 0) { - $this->output->show_message('storageerror', 'error'); + $error = 'storageerror'; } else if ($fallback) { - $this->output->show_message($fallback, 'error', $fallback_args); + $error = $fallback; + $args = $fallback_args; + } + + if ($error) { + if ($suffix && $this->text_exists($error . $suffix)) { + $error .= $suffix; + } + $this->output->show_message($error, 'error', $args); } } diff --git a/program/localization/en_US/messages.inc b/program/localization/en_US/messages.inc index 9262db8b2..f9b5e00a6 100644 --- a/program/localization/en_US/messages.inc +++ b/program/localization/en_US/messages.inc @@ -28,7 +28,8 @@ $messages['dberror'] = 'Database Error!'; $messages['requesttimedout'] = 'Request timed out'; $messages['errorreadonly'] = 'Unable to perform operation. Folder is read-only.'; $messages['errornoperm'] = 'Unable to perform operation. Permission denied.'; -$messages['erroroverquota'] = 'Unable to perform operation. No available disk space.'; +$messages['erroroverquota'] = 'Unable to perform operation. No free disk space.'; +$messages['erroroverquotadelete'] = 'No free disk space. Use SHIFT+DEL to delete a message.'; $messages['invalidrequest'] = 'Invalid request! No data was saved.'; $messages['invalidhost'] = 'Invalid server name.'; $messages['nomessagesfound'] = 'No messages found in this mailbox.'; diff --git a/program/steps/mail/move_del.inc b/program/steps/mail/move_del.inc index 3e2252683..3fc6ac5a7 100644 --- a/program/steps/mail/move_del.inc +++ b/program/steps/mail/move_del.inc @@ -29,10 +29,11 @@ $old_count = $RCMAIL->storage->count(NULL, $threading ? 'THREADS' : 'ALL'); $old_pages = ceil($old_count / $RCMAIL->storage->get_pagesize()); // move messages -if ($RCMAIL->action=='moveto' && !empty($_POST['_uid']) && strlen($_POST['_target_mbox'])) { - $count = sizeof(explode(',', ($uids = get_input_value('_uid', RCUBE_INPUT_POST)))); +if ($RCMAIL->action == 'moveto' && !empty($_POST['_uid']) && strlen($_POST['_target_mbox'])) { + $count = sizeof(explode(',', ($uids = get_input_value('_uid', RCUBE_INPUT_POST)))); $target = get_input_value('_target_mbox', RCUBE_INPUT_POST, true); - $mbox = get_input_value('_mbox', RCUBE_INPUT_POST, true); + $mbox = get_input_value('_mbox', RCUBE_INPUT_POST, true); + $trash = $RCMAIL->config->get('trash_mbox'); $moved = $RCMAIL->storage->move_message($uids, $target, $mbox); @@ -40,7 +41,7 @@ if ($RCMAIL->action=='moveto' && !empty($_POST['_uid']) && strlen($_POST['_targe // send error message if ($_POST['_from'] != 'show') $OUTPUT->command('list_mailbox'); - rcmail_display_server_error('errormoving'); + rcmail_display_server_error('errormoving', null, $target == $trash ? 'delete' : ''); $OUTPUT->send(); exit; } -- cgit v1.2.3 From 139635f18985d568ad76b9f101c19542eaee2349 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 5 Mar 2013 10:25:12 +0100 Subject: Fix thumbnail size when GD extension is used for image resize (#1488985) --- CHANGELOG | 1 + program/lib/Roundcube/rcube_image.php | 23 ++++++++++++++++++----- program/steps/mail/get.inc | 10 +++++----- 3 files changed, 24 insertions(+), 10 deletions(-) (limited to 'program/steps/mail') diff --git a/CHANGELOG b/CHANGELOG index a45a24758..c5d8c7687 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Fix thumbnail size when GD extension is used for image resize (#1488985) - Display user-friendly message on IMAP "over quota" errors (#1484164) - Display notice that message is encrypted also for application/pkcs7-mime messages (#1488526) - Extended archive plugin with user-configurable options to store messages into subfolders diff --git a/program/lib/Roundcube/rcube_image.php b/program/lib/Roundcube/rcube_image.php index 9695022da..a55ba1600 100644 --- a/program/lib/Roundcube/rcube_image.php +++ b/program/lib/Roundcube/rcube_image.php @@ -77,7 +77,8 @@ class rcube_image } /** - * Resize image to a given size + * Resize image to a given size. Use only to shrink an image. + * If an image is smaller than specified size it will be not resized. * * @param int $size Max width/height size * @param string $filename Output filename @@ -131,19 +132,30 @@ class rcube_image if ($props['gd_type']) { if ($props['gd_type'] == IMAGETYPE_JPEG && function_exists('imagecreatefromjpeg')) { $image = imagecreatefromjpeg($this->image_file); + $type = 'jpg'; } else if($props['gd_type'] == IMAGETYPE_GIF && function_exists('imagecreatefromgif')) { $image = imagecreatefromgif($this->image_file); + $type = 'gid'; } else if($props['gd_type'] == IMAGETYPE_PNG && function_exists('imagecreatefrompng')) { $image = imagecreatefrompng($this->image_file); + $type = 'png'; } else { // @TODO: print error to the log? return false; } - $scale = $size / max($props['width'], $props['height']); + $scale = $size / max($props['width'], $props['height']); + + // Imagemagick resize is implemented in shrinking mode (see -resize argument above) + // we do the same here, if an image is smaller than specified size + // we do nothing but copy original file to destination file + if ($scale > 1) { + return $this->image_file == $filename || copy($this->image_file, $filename) ? $type : false; + } + $width = $props['width'] * $scale; $height = $props['height'] * $scale; @@ -162,15 +174,12 @@ class rcube_image if ($props['gd_type'] == IMAGETYPE_JPEG) { $result = imagejpeg($image, $filename, 75); - $type = 'jpg'; } elseif($props['gd_type'] == IMAGETYPE_GIF) { $result = imagegif($image, $filename); - $type = 'gid'; } elseif($props['gd_type'] == IMAGETYPE_PNG) { $result = imagepng($image, $filename, 6, PNG_ALL_FILTERS); - $type = 'png'; } if ($result) { @@ -245,6 +254,10 @@ class rcube_image else if ($type == self::TYPE_PNG) { $result = imagepng($image, $filename, 6, PNG_ALL_FILTERS); } + + if ($result) { + return true; + } } // @TODO: print error to the log? diff --git a/program/steps/mail/get.inc b/program/steps/mail/get.inc index 54d1a4e77..23dc22b7c 100644 --- a/program/steps/mail/get.inc +++ b/program/steps/mail/get.inc @@ -60,11 +60,11 @@ else if ($_GET['_thumb']) { $pid = get_input_value('_part', RCUBE_INPUT_GET); if ($part = $MESSAGE->mime_parts[$pid]) { $thumbnail_size = $RCMAIL->config->get('image_thumbnail_size', 240); - $temp_dir = $RCMAIL->config->get('temp_dir'); - list(,$ext) = explode('/', $part->mimetype); + $temp_dir = $RCMAIL->config->get('temp_dir'); + list(,$ext) = explode('/', $part->mimetype); $cache_basename = $temp_dir . '/' . md5($MESSAGE->headers->messageID . $part->mime_id . ':' . $RCMAIL->user->ID . ':' . $thumbnail_size); - $cache_file = $cache_basename . '.' . $ext; - $mimetype = $part->mimetype; + $cache_file = $cache_basename . '.' . $ext; + $mimetype = $part->mimetype; // render thumbnail image if not done yet if (!is_file($cache_file)) { @@ -73,7 +73,7 @@ else if ($_GET['_thumb']) { fclose($fp); $image = new rcube_image($orig_name); - if ($imgtype = $image->resize($RCMAIL->config->get('image_thumbnail_size', 240), $cache_file, true)) { + if ($imgtype = $image->resize($thumbnail_size, $cache_file, true)) { $mimetype = 'image/' . $imgtype; unlink($orig_name); } -- cgit v1.2.3 From cbe07532fa4ed3794c872e348be0098575047927 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 5 Mar 2013 16:50:13 +0100 Subject: Fix so full filename is used for title attribute --- program/steps/mail/show.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'program/steps/mail') diff --git a/program/steps/mail/show.inc b/program/steps/mail/show.inc index 3495df9c0..437dbaafa 100644 --- a/program/steps/mail/show.inc +++ b/program/steps/mail/show.inc @@ -158,8 +158,8 @@ function rcmail_message_attachments($attrib) } else { if (mb_strlen($filename) > 50) { + $title = $filename; $filename = abbreviate_string($filename, 50); - $title = $filename; } else { $title = ''; -- cgit v1.2.3 From bc2c4380b5b754a3b13cc7d6663b2b81d2577e2e Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 6 Mar 2013 11:11:37 +0100 Subject: Add attachment menu with Open and Download options (#1488975) --- CHANGELOG | 1 + program/include/rcmail_output_html.php | 4 +++ program/js/app.js | 21 +++++++---- program/localization/en_US/labels.inc | 1 + program/steps/mail/show.inc | 23 ++++++------ skins/classic/functions.js | 53 +++++++++++++++++++++++----- skins/classic/mail.css | 20 +++++++++-- skins/classic/templates/message.html | 8 +++++ skins/classic/templates/messagepreview.html | 12 ++++++- skins/larry/images/buttons.gif | Bin 13054 -> 14997 bytes skins/larry/images/buttons.png | Bin 36693 -> 48308 bytes skins/larry/mail.css | 2 +- skins/larry/styles.css | 13 ++++++- skins/larry/templates/message.html | 8 +++++ skins/larry/templates/messagepreview.html | 8 +++++ skins/larry/ui.js | 42 +++++++++++++++++++--- 16 files changed, 181 insertions(+), 35 deletions(-) (limited to 'program/steps/mail') diff --git a/CHANGELOG b/CHANGELOG index c5d8c7687..fa5fb8e24 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Add attachment menu with Open and Download options (#1488975) - Fix thumbnail size when GD extension is used for image resize (#1488985) - Display user-friendly message on IMAP "over quota" errors (#1484164) - Display notice that message is encrypted also for application/pkcs7-mime messages (#1488526) diff --git a/program/include/rcmail_output_html.php b/program/include/rcmail_output_html.php index 2babe1cbb..ade2bd4a4 100644 --- a/program/include/rcmail_output_html.php +++ b/program/include/rcmail_output_html.php @@ -1175,6 +1175,10 @@ class rcmail_output_html extends rcmail_output $out = sprintf('%s', $attrib_str, $btn_content); } + if ($attrib['wrapper']) { + $out = html::tag($attrib['wrapper'], null, $out); + } + return $out; } diff --git a/program/js/app.js b/program/js/app.js index 4011fa593..55c71d776 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -179,7 +179,8 @@ function rcube_webmail() } // enable general commands - this.enable_command('close', 'logout', 'mail', 'addressbook', 'settings', 'save-pref', 'compose', 'undo', 'about', 'switch-task', true); + this.enable_command('close', 'logout', 'mail', 'addressbook', 'settings', 'save-pref', + 'compose', 'undo', 'about', 'switch-task', 'menu-open', 'menu-save', true); if (this.env.permaurl) this.enable_command('permaurl', 'extwin', true); @@ -211,7 +212,7 @@ function rcube_webmail() this.gui_objects.messagelist.parentNode.onmousedown = function(e){ return p.click_on_list(e); }; this.message_list.init(); - this.enable_command('toggle_status', 'toggle_flag', 'menu-open', 'menu-save', 'sort', true); + this.enable_command('toggle_status', 'toggle_flag', 'sort', true); // load messages this.command('list'); @@ -227,7 +228,7 @@ function rcube_webmail() this.env.message_commands = ['show', 'reply', 'reply-all', 'reply-list', 'moveto', 'copy', 'delete', 'open', 'mark', 'edit', 'viewsource', - 'print', 'load-attachment', 'show-headers', 'hide-headers', 'download', + 'print', 'load-attachment', 'download-attachment', 'show-headers', 'hide-headers', 'download', 'forward', 'forward-inline', 'forward-attachment']; if (this.env.action == 'show' || this.env.action == 'preview') { @@ -608,6 +609,11 @@ function rcube_webmail() break; case 'menu-open': + if (props && props.menu == 'attachmentmenu') { + var mimetype = this.env.attachments[props.id]; + this.enable_command('open-attachment', mimetype && this.env.mimetypes && $.inArray(mimetype, this.env.mimetypes) >= 0); + } + case 'menu-save': this.triggerEvent(command, {props:props}); return false; @@ -833,11 +839,14 @@ function rcube_webmail() break; case 'load-attachment': - var qstring = '_mbox='+urlencode(this.env.mailbox)+'&_uid='+this.env.uid+'&_part='+props.part; + case 'open-attachment': + case 'download-attachment': + var qstring = '_mbox='+urlencode(this.env.mailbox)+'&_uid='+this.env.uid+'&_part='+props, + mimetype = this.env.attachments[props]; // open attachment in frame if it's of a supported mimetype - if (this.env.uid && props.mimetype && this.env.mimetypes && $.inArray(props.mimetype, this.env.mimetypes) >= 0) { - var attachment_win = window.open(this.env.comm_path+'&_action=get&'+qstring+'&_frame=1', 'rcubemailattachment'+this.env.uid+props.part); + if (command != 'download-attachment' && mimetype && this.env.mimetypes && $.inArray(mimetype, this.env.mimetypes) >= 0) { + var attachment_win = window.open(this.env.comm_path+'&_action=get&'+qstring+'&_frame=1', 'rcubemailattachment'+this.env.uid+props); if (attachment_win) { setTimeout(function(){ attachment_win.focus(); }, 10); break; diff --git a/program/localization/en_US/labels.inc b/program/localization/en_US/labels.inc index 0750e7848..61a13e9ab 100644 --- a/program/localization/en_US/labels.inc +++ b/program/localization/en_US/labels.inc @@ -64,6 +64,7 @@ $labels['copy'] = 'Copy'; $labels['move'] = 'Move'; $labels['moveto'] = 'Move to...'; $labels['download'] = 'Download'; +$labels['open'] = 'Open'; $labels['showattachment'] = 'Show'; $labels['showanyway'] = 'Show it anyway'; diff --git a/program/steps/mail/show.inc b/program/steps/mail/show.inc index 437dbaafa..87555cbbe 100644 --- a/program/steps/mail/show.inc +++ b/program/steps/mail/show.inc @@ -147,6 +147,7 @@ function rcmail_message_attachments($attrib) global $PRINT_MODE, $MESSAGE, $RCMAIL; $out = $ol = ''; + $attachments = array(); if (sizeof($MESSAGE->attachments)) { foreach ($MESSAGE->attachments as $attach_prop) { @@ -165,21 +166,23 @@ function rcmail_message_attachments($attrib) $title = ''; } - $ol .= html::tag('li', rcmail_filetype2classname($attach_prop->mimetype, $filename), - html::a(array( + $mimetype = rcmail_fix_mimetype($attach_prop->mimetype); + $class = rcmail_filetype2classname($mimetype, $filename); + $id = 'attach' . $attach_prop->mime_id; + $link = html::a(array( 'href' => $MESSAGE->get_part_url($attach_prop->mime_id, false), - 'onclick' => sprintf( - 'return %s.command(\'load-attachment\',{part:\'%s\', mimetype:\'%s\'},this)', - JS_OBJECT_NAME, - $attach_prop->mime_id, - rcmail_fix_mimetype($attach_prop->mimetype)), - 'title' => Q($title), - ), - Q($filename))); + 'onclick' => sprintf('return %s.command(\'load-attachment\',\'%s\',this)', + JS_OBJECT_NAME, $attach_prop->mime_id), + 'title' => Q($title), + ), Q($filename)); + $ol .= html::tag('li', array('class' => $class, 'id' => $id), $link); + + $attachments[$attach_prop->mime_id] = $mimetype; } } $out = html::tag('ul', $attrib, $ol, html::$common_attrib); + $RCMAIL->output->set_env('attachments', $attachments); } return $out; diff --git a/skins/classic/functions.js b/skins/classic/functions.js index c59ea9bf8..499783b3f 100644 --- a/skins/classic/functions.js +++ b/skins/classic/functions.js @@ -92,6 +92,7 @@ function rcube_mail_ui() forwardmenu: {id:'forwardmenu', editable:1}, searchmenu: {id:'searchmenu', editable:1}, messagemenu: {id:'messagemenu'}, + attachmentmenu: {id:'attachmentmenu'}, listmenu: {id:'listmenu', editable:1}, dragmessagemenu:{id:'dragmessagemenu', sticky:1}, groupmenu: {id:'groupoptionsmenu', above:1}, @@ -133,24 +134,24 @@ show_popupmenu: function(popup, show) { var obj = this.popups[popup].obj, above = this.popups[popup].above, - ref = rcube_find_object(popup+'link'); + ref = $(this.popups[popup].link ? this.popups[popup].link : rcube_find_object(popup+'link')); if (typeof show == 'undefined') show = obj.is(':visible') ? false : true; else if (this.popups[popup].toggle && show && this.popups[popup].obj.is(':visible') ) show = false; - if (show && ref) { - var parent = $(ref).parent(), + if (show && ref.length) { + var parent = ref.parent(), win = $(window), - pos = parent.hasClass('dropbutton') ? parent.offset() : $(ref).offset(); + pos = parent.hasClass('dropbutton') ? parent.offset() : ref.offset(); - if (!above && pos.top + ref.offsetHeight + obj.height() > win.height()) + if (!above && pos.top + ref.height() + obj.height() > win.height()) above = true; if (pos.left + obj.width() > win.width()) pos.left = win.width() - obj.width() - 30; - obj.css({ left:pos.left, top:(pos.top + (above ? -obj.height() : ref.offsetHeight)) }); + obj.css({ left:pos.left, top:(pos.top + (above ? -obj.height() : ref.height())) }); } obj[show?'show':'hide'](); @@ -325,7 +326,7 @@ listmenu: function(show) }; }, -open_listmenu: function(e) +open_listmenu: function() { this.listmenu(); }, @@ -380,6 +381,35 @@ spellmenu: function(show) this.show_popupmenu('spellmenu', show); }, +show_attachmentmenu: function(elem) +{ + var id = elem.parentNode.id.replace(/^attach/, ''); + + $('#attachmenuopen').unbind('click').attr('onclick', '').click(function(e) { + return rcmail.command('open-attachment', id, this); + }); + + $('#attachmenudownload').unbind('click').attr('onclick', '').click(function() { + rcmail.command('download-attachment', id, this); + }); + + this.popups.attachmentmenu.link = elem; + rcmail.command('menu-open', {menu: 'attachmentmenu', id: id}); +}, + +menu_open: function(p) +{ + if (p && p.props && p.props.menu == 'attachmentmenu') + this.show_popup('attachmentmenu'); + else + this.open_listmenu(); +}, + +menu_save: function(prop) +{ + this.save_listmenu(); +}, + body_mouseup: function(evt, p) { var i, target = rcube_event.get_target(evt); @@ -800,8 +830,8 @@ function rcube_init_mail_ui() .contents().mouseup(function(e){rcmail_ui.body_mouseup(e)}); if (rcmail.env.task == 'mail') { - rcmail.addEventListener('menu-open', 'open_listmenu', rcmail_ui); - rcmail.addEventListener('menu-save', 'save_listmenu', rcmail_ui); + rcmail.addEventListener('menu-open', 'menu_open', rcmail_ui); + rcmail.addEventListener('menu-save', 'menu_save', rcmail_ui); rcmail.addEventListener('aftersend-attachment', 'uploadmenu', rcmail_ui); rcmail.addEventListener('aftertoggle-editor', 'resize_compose_body_ev', rcmail_ui); rcmail.gui_object('message_dragmenu', 'dragmessagemenu'); @@ -817,6 +847,11 @@ function rcube_init_mail_ui() if (rcmail.env.action == 'compose') rcmail_ui.init_compose_form(); + else if (rcmail.env.action == 'show' || rcmail.env.action == 'preview') + // add menu link for each attachment + $('#attachment-list > li[id^="attach"]').each(function() { + $(this).append($('').click(function() { rcmail_ui.show_attachmentmenu(this); })); + }); } else if (rcmail.env.task == 'addressbook') { rcmail.addEventListener('afterupload-photo', function(){ rcmail_ui.show_popup('uploadform', false); }); diff --git a/skins/classic/mail.css b/skins/classic/mail.css index 8be35aaa6..4d42d98ff 100644 --- a/skins/classic/mail.css +++ b/skins/classic/mail.css @@ -173,13 +173,15 @@ } #messagemenu li a.active:hover, +#attachmentmenu li a.active:hover, #markmessagemenu li a.active:hover { color: #fff; background-color: #c00; } -#messagemenu li a +#messagemenu li a, +#attachmentmenu li a { background: url(images/messageactions.png) no-repeat 7px 0; background-position: 7px 20px; @@ -190,7 +192,8 @@ background-position: 7px 1px; } -#messagemenu li a.downloadlink +#messagemenu li a.downloadlink, +#attachmentmenu li a.downloadlink { background-position: 7px -17px; } @@ -200,7 +203,8 @@ background-position: 7px -35px; } -#messagemenu li a.openlink +#messagemenu li a.openlink, +#attachmentmenu li a.openlink { background-position: 7px -53px; } @@ -1135,6 +1139,16 @@ table.headers-table tr td.header span text-decoration: underline; } +#attachment-list li a.drop { + background: url(images/icons/down_small.gif) no-repeat center 6px; + width: 12px; + height: 7px; + cursor: pointer; + padding: 5px 0 0; + margin-left: 3px; + display: inline-block; +} + #messagebody { position:relative; diff --git a/skins/classic/templates/message.html b/skins/classic/templates/message.html index d1594ea28..73dfcb976 100644 --- a/skins/classic/templates/message.html +++ b/skins/classic/templates/message.html @@ -65,5 +65,13 @@ +
+
    +
  • +
  • + +
+
+ diff --git a/skins/classic/templates/messagepreview.html b/skins/classic/templates/messagepreview.html index 78b2306f6..935238edf 100644 --- a/skins/classic/templates/messagepreview.html +++ b/skins/classic/templates/messagepreview.html @@ -3,8 +3,10 @@ <roundcube:object name="pagetitle" /> + + - +
@@ -16,5 +18,13 @@ +
+
    +
  • +
  • + +
+
+ diff --git a/skins/larry/images/buttons.gif b/skins/larry/images/buttons.gif index d8a33d6b2..8a4a78ee4 100644 Binary files a/skins/larry/images/buttons.gif and b/skins/larry/images/buttons.gif differ diff --git a/skins/larry/images/buttons.png b/skins/larry/images/buttons.png index 4438d9cbc..0ec061a78 100644 Binary files a/skins/larry/images/buttons.png and b/skins/larry/images/buttons.png differ diff --git a/skins/larry/mail.css b/skins/larry/mail.css index c99370830..e6529d118 100644 --- a/skins/larry/mail.css +++ b/skins/larry/mail.css @@ -868,7 +868,7 @@ div.more-headers { } div.hide-headers { - background-position: center -1589px; + background-position: center -1600px; } #all-headers { diff --git a/skins/larry/styles.css b/skins/larry/styles.css index 044a09e05..1e3e6f7d5 100644 --- a/skins/larry/styles.css +++ b/skins/larry/styles.css @@ -2219,7 +2219,7 @@ ul.toolbarmenu li span.conversation { display: block; color: #333; font-weight: bold; - padding: 8px 4px 3px 30px; + padding: 8px 15px 3px 30px; text-shadow: 0px 1px 1px #fff; text-decoration: none; white-space: nowrap; @@ -2227,6 +2227,17 @@ ul.toolbarmenu li span.conversation { text-overflow: ellipsis; } +.attachmentslist li a.drop { + background: url(images/buttons.png) no-repeat scroll center -1570px; + width: 14px; + height: 26px; + cursor: pointer; + position: absolute; + right: 0; + top: 0; + padding: 0; +} + #compose-attachments ul li { padding-right: 28px; } diff --git a/skins/larry/templates/message.html b/skins/larry/templates/message.html index b4ceb6a6c..36e0efa0a 100644 --- a/skins/larry/templates/message.html +++ b/skins/larry/templates/message.html @@ -73,6 +73,14 @@
+ + diff --git a/skins/larry/templates/messagepreview.html b/skins/larry/templates/messagepreview.html index aef282ac9..dbfe2dc7b 100644 --- a/skins/larry/templates/messagepreview.html +++ b/skins/larry/templates/messagepreview.html @@ -51,6 +51,14 @@
+ + diff --git a/skins/larry/ui.js b/skins/larry/ui.js index d2638bbca..6b2a5c7d0 100644 --- a/skins/larry/ui.js +++ b/skins/larry/ui.js @@ -17,6 +17,7 @@ function rcube_mail_ui() var popupconfig = { forwardmenu: { editable:1 }, searchmenu: { editable:1, callback:searchmenu }, + attachmentmenu: { }, listoptions: { editable:1 }, dragmessagemenu: { sticky:1 }, groupmenu: { above:1 }, @@ -81,8 +82,8 @@ function rcube_mail_ui() /*** mail task ***/ if (rcmail.env.task == 'mail') { - rcmail.addEventListener('menu-open', show_listoptions); - rcmail.addEventListener('menu-save', save_listoptions); + rcmail.addEventListener('menu-open', menu_open); + rcmail.addEventListener('menu-save', menu_save); rcmail.addEventListener('responseafterlist', function(e){ switch_view_mode(rcmail.env.threading ? 'thread' : 'list') }); var dragmenu = $('#dragmessagemenu'); @@ -95,6 +96,11 @@ function rcube_mail_ui() rcmail.addEventListener('aftershow-headers', function() { layout_messageview(); }); rcmail.addEventListener('afterhide-headers', function() { layout_messageview(); }); $('#previewheaderstoggle').click(function(e){ toggle_preview_headers(this); return false }); + + // add menu link for each attachment + $('#attachment-list > li').each(function() { + $(this).append($('').click(function() { attachmentmenu(this); })); + }); } else if (rcmail.env.action == 'compose') { rcmail.addEventListener('aftertoggle-editor', function(){ window.setTimeout(function(){ layout_composeview() }, 200); }); @@ -436,7 +442,7 @@ function rcube_mail_ui() { var obj = popups[popup], config = popupconfig[popup], - ref = $('#'+popup+'link'), + ref = $(config.link ? config.link : '#'+popup+'link'), above = config.above; if (!obj) { @@ -452,7 +458,7 @@ function rcube_mail_ui() else if (config.toggle && show && obj.is(':visible')) show = false; - if (show && ref) { + if (show && ref.length) { var parent = ref.parent(), win = $(window), pos; @@ -575,6 +581,19 @@ function rcube_mail_ui() /**** popup callbacks ****/ + function menu_open(p) + { + if (p && p.props && p.props.menu == 'attachmentmenu') + show_popupmenu('attachmentmenu'); + else + show_listoptions(); + } + + function menu_save(prop) + { + save_listoptions(); + } + function searchmenu(show) { if (show && rcmail.env.search_mods) { @@ -605,6 +624,21 @@ function rcube_mail_ui() } } + function attachmentmenu(elem) + { + var id = elem.parentNode.id.replace(/^attach/, ''); + + $('#attachmenuopen').unbind('click').attr('onclick', '').click(function(e) { + return rcmail.command('open-attachment', id, this); + }); + + $('#attachmenudownload').unbind('click').attr('onclick', '').click(function() { + rcmail.command('download-attachment', id, this); + }); + + popupconfig.attachmentmenu.link = elem; + rcmail.command('menu-open', {menu: 'attachmentmenu', id: id}); + } function spellmenu(show) { -- cgit v1.2.3 From 65de0018c8e34a24167a2cc5a46a72981886362a Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Sun, 10 Mar 2013 16:18:45 +0100 Subject: Trigger 'refresh' hook in mail view --- program/steps/mail/check_recent.inc | 3 +++ 1 file changed, 3 insertions(+) (limited to 'program/steps/mail') diff --git a/program/steps/mail/check_recent.inc b/program/steps/mail/check_recent.inc index 4befbf275..d3c14a38d 100644 --- a/program/steps/mail/check_recent.inc +++ b/program/steps/mail/check_recent.inc @@ -115,4 +115,7 @@ foreach ($a_mailboxes as $mbox_name) { } } +// trigger refresh hook +$RCMAIL->plugins->exec_hook('refresh', array()); + $OUTPUT->send(); -- cgit v1.2.3 From abf46755c9638ad27ac876787343fa4c1f0e8990 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Mon, 11 Mar 2013 16:20:01 +0100 Subject: Fix forward as attachment bug with editormode != 1 (#1488991) --- CHANGELOG | 1 + program/steps/mail/compose.inc | 13 +++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'program/steps/mail') diff --git a/CHANGELOG b/CHANGELOG index 55360ec14..855316238 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Fix forward as attachment bug with editormode != 1 (#1488991) - Fix LIMIT/OFFSET queries handling on MS SQL Server (#1488984) - Fix so task name can really contain all from a-z0-9_- characters (#1488941) - Support IMAP MOVE extension [RFC 6851] diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index 25cf63f70..d7cfe7ddd 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -509,7 +509,7 @@ function rcmail_compose_header_from($attrib) function rcmail_compose_editor_mode() { - global $RCMAIL, $MESSAGE, $compose_mode; + global $RCMAIL, $compose_mode; static $useHtml; if ($useHtml !== null) @@ -521,13 +521,13 @@ function rcmail_compose_editor_mode() $useHtml = !empty($_POST['_is_html']); } else if ($compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT) { - $useHtml = $MESSAGE->has_html_part(false, true); + $useHtml = rcmail_message_is_html(); } else if ($compose_mode == RCUBE_COMPOSE_REPLY) { - $useHtml = ($html_editor == 1 || ($html_editor >= 2 && $MESSAGE->has_html_part(false, true))); + $useHtml = ($html_editor == 1 || ($html_editor >= 2 && rcmail_message_is_html())); } else if ($compose_mode == RCUBE_COMPOSE_FORWARD) { - $useHtml = ($html_editor == 1 || ($html_editor == 3 && $MESSAGE->has_html_part(false, true))); + $useHtml = ($html_editor == 1 || ($html_editor == 3 && rcmail_message_is_html())); } else { $useHtml = ($html_editor == 1); @@ -536,6 +536,11 @@ function rcmail_compose_editor_mode() return $useHtml; } +function rcmail_message_is_html() +{ + global $MESSAGE; + return ($MESSAGE instanceof rcube_message) && $MESSAGE->has_html_part(false, true); +} function rcmail_prepare_message_body() { -- cgit v1.2.3 From ff7542bfb9648a8970bd6ff767bb62a647f705ad Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 12 Mar 2013 08:43:21 +0100 Subject: Don't show fake address - phishing prevention (#1488981) --- CHANGELOG | 1 + program/steps/mail/func.inc | 5 +++++ 2 files changed, 6 insertions(+) (limited to 'program/steps/mail') diff --git a/CHANGELOG b/CHANGELOG index 855316238..499d5ba07 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Don't show fake address - phishing prevention (#1488981) - Fix forward as attachment bug with editormode != 1 (#1488991) - Fix LIMIT/OFFSET queries handling on MS SQL Server (#1488984) - Fix so task name can really contain all from a-z0-9_- characters (#1488941) diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index 4a3476320..137e87d49 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -1444,6 +1444,11 @@ function rcmail_address_string($input, $max=null, $linked=false, $addicon=null, $mailto = $part['mailto']; $string = $part['string']; + // phishing email prevention (#1488981), e.g. "valid@email.addr " + if ($name && $name != $mailto && strpos($name, '@')) { + $name = ''; + } + // IDNA ASCII to Unicode if ($name == $mailto) $name = rcube_idn_to_utf8($name); -- cgit v1.2.3 From 5c26bd49b10a2666df9e4853b0740038b0cc3b88 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 14 Mar 2013 12:10:40 +0100 Subject: Added rcube_message::has_text_part(), simplified has_html_part() so it always works in "recursive mode" - removed $recursive argument. --- program/lib/Roundcube/rcube_message.php | 81 +++++++++++++++++++++++++-------- program/steps/mail/compose.inc | 2 +- 2 files changed, 62 insertions(+), 21 deletions(-) (limited to 'program/steps/mail') diff --git a/program/lib/Roundcube/rcube_message.php b/program/lib/Roundcube/rcube_message.php index 3f14266d4..7d58a8eb5 100644 --- a/program/lib/Roundcube/rcube_message.php +++ b/program/lib/Roundcube/rcube_message.php @@ -194,41 +194,82 @@ class rcube_message /** - * Determine if the message contains a HTML part + * Determine if the message contains a HTML part. This must to be + * a real part not an attachment (or its part) + * This must to be + * a real part not an attachment (or its part) * - * @param bool $recursive Enables checking in all levels of the structure - * @param bool $enriched Enables checking for text/enriched parts too + * @param bool $enriched Enables checking for text/enriched parts too * * @return bool True if a HTML is available, False if not */ - function has_html_part($recursive = true, $enriched = false) + function has_html_part($enriched = false) { // check all message parts foreach ($this->parts as $part) { if ($part->mimetype == 'text/html' || ($enriched && $part->mimetype == 'text/enriched')) { - // Level check, we'll skip e.g. HTML attachments - if (!$recursive) { - $level = explode('.', $part->mime_id); + // Skip if part is an attachment + if ($this->is_attachment($part)) { + continue; + } - // Skip if part is an attachment - if ($this->is_attachment($part)) { - continue; + $level = explode('.', $part->mime_id); + + // Check if the part belongs to higher-level's alternative/related + while (array_pop($level) !== null) { + if (!count($level)) { + return true; } - // Check if the part belongs to higher-level's alternative/related - while (array_pop($level) !== null) { - if (!count($level)) { - return true; - } + $parent = $this->mime_parts[join('.', $level)]; + if ($parent->mimetype != 'multipart/alternative' && $parent->mimetype != 'multipart/related') { + continue 2; + } + } - $parent = $this->mime_parts[join('.', $level)]; - if ($parent->mimetype != 'multipart/alternative' && $parent->mimetype != 'multipart/related') { - continue 2; - } + if ($part->size) { + return true; + } + } + } + + return false; + } + + + /** + * Determine if the message contains a text/plain part. This must to be + * a real part not an attachment (or its part) + * + * @return bool True if a plain text part is available, False if not + */ + function has_text_part() + { + // check all message parts + foreach ($this->parts as $part) { + if ($part->mimetype == 'text/plain') { + // Skip if part is an attachment + if ($this->is_attachment($part)) { + continue; + } + + $level = explode('.', $part->mime_id); + + // Check if the part belongs to higher-level's alternative/related + while (array_pop($level) !== null) { + if (!count($level)) { + return true; + } + + $parent = $this->mime_parts[join('.', $level)]; + if ($parent->mimetype != 'multipart/alternative' && $parent->mimetype != 'multipart/related') { + continue 2; } } - return true; + if ($part->size) { + return true; + } } } diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index d7cfe7ddd..dd6a1d88c 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -539,7 +539,7 @@ function rcmail_compose_editor_mode() function rcmail_message_is_html() { global $MESSAGE; - return ($MESSAGE instanceof rcube_message) && $MESSAGE->has_html_part(false, true); + return ($MESSAGE instanceof rcube_message) && $MESSAGE->has_html_part(true); } function rcmail_prepare_message_body() -- cgit v1.2.3 From 542f15bfece569ba3d07f57339aca22e535027dd Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 14 Mar 2013 12:17:38 +0100 Subject: Enriched content should be also converted to text if html is disabled --- program/steps/mail/compose.inc | 3 --- program/steps/mail/func.inc | 5 ++++- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'program/steps/mail') diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index dd6a1d88c..640272400 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -675,9 +675,6 @@ function rcmail_compose_part_body($part, $isHtml = false) $txt = new rcube_html2text($body, false, true, $len); $body = $txt->get_text(); } - else if ($part->ctype_secondary == 'enriched') { - $body = rcube_enriched::to_html($body); - } else { if ($part->ctype_secondary == 'plain' && $part->ctype_parameters['format'] == 'flowed') { $body = rcube_mime::unfold_flowed($body); diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index 137e87d49..3aea6322e 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -707,7 +707,10 @@ function rcmail_print_body($part, $p = array()) + $p + array('safe' => false, 'plain' => false, 'inline_html' => true)); // convert html to text/plain - if ($data['type'] == 'html' && $data['plain']) { + if ($data['plain'] && ($data['type'] == 'html' || $data['type'] == 'enriched')) { + if ($data['type'] == 'enriched') { + $data['body'] = rcube_enriched::to_html($data['body']); + } $txt = new rcube_html2text($data['body'], false, true); $body = $txt->get_text(); $part->ctype_secondary = 'plain'; -- cgit v1.2.3 From a02c77c584906f629d382409e76f0df4d2cfaf01 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 15 Mar 2013 10:30:53 +0100 Subject: Add ability to toggle between view as HTML and text while viewing a message (#1486939) --- CHANGELOG | 1 + program/js/app.js | 13 ++++++++++++- program/localization/en_US/labels.inc | 2 ++ program/steps/mail/compose.inc | 19 ++++++++++++++----- program/steps/mail/show.inc | 27 ++++++++++++++++++++++----- skins/classic/images/icons/html.png | Bin 0 -> 379 bytes skins/classic/images/icons/text.png | Bin 0 -> 372 bytes skins/classic/mail.css | 19 +++++++++++++------ skins/classic/templates/message.html | 8 ++++++++ skins/classic/templates/messagepreview.html | 10 +++++++++- 10 files changed, 81 insertions(+), 18 deletions(-) create mode 100644 skins/classic/images/icons/html.png create mode 100644 skins/classic/images/icons/text.png (limited to 'program/steps/mail') diff --git a/CHANGELOG b/CHANGELOG index 6d0a959a6..42d6cc72a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Add ability to toggle between HTML and text while viewing a message (#1486939) - Better handling of session errors in ajax requests (#1488960) - Fix HTML part detection for some specific message structures (#1488992) - Don't show fake address - phishing prevention (#1488981) diff --git a/program/js/app.js b/program/js/app.js index 637d6f5d8..d3c319ecd 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -229,7 +229,7 @@ function rcube_webmail() this.env.message_commands = ['show', 'reply', 'reply-all', 'reply-list', 'moveto', 'copy', 'delete', 'open', 'mark', 'edit', 'viewsource', 'print', 'load-attachment', 'download-attachment', 'show-headers', 'hide-headers', 'download', - 'forward', 'forward-inline', 'forward-attachment']; + 'forward', 'forward-inline', 'forward-attachment', 'change-format']; if (this.env.action == 'show' || this.env.action == 'preview') { this.enable_command(this.env.message_commands, this.env.uid); @@ -608,6 +608,17 @@ function rcube_webmail() } break; + case 'change-format': + url = this.env.permaurl + '&_format=' + props; + + if (this.env.action == 'preview') + url = url.replace(/_action=show/, '_action=preview') + '&_framed=1'; + if (this.env.extwin) + url += '&_extwin=1'; + + location.href = url; + break; + case 'menu-open': if (props && props.menu == 'attachmentmenu') { var mimetype = this.env.attachments[props.id]; diff --git a/program/localization/en_US/labels.inc b/program/localization/en_US/labels.inc index 61a13e9ab..252e0ce68 100644 --- a/program/localization/en_US/labels.inc +++ b/program/localization/en_US/labels.inc @@ -205,6 +205,8 @@ $labels['body'] = 'Body'; $labels['openinextwin'] = 'Open in new window'; $labels['emlsave'] = 'Download (.eml)'; +$labels['changeformattext'] = 'Display in plain text format'; +$labels['changeformathtml'] = 'Display in HTML format'; // message compose $labels['editasnew'] = 'Edit as new'; diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index 640272400..6a579f754 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -183,9 +183,18 @@ $LINE_LENGTH = $RCMAIL->config->get('line_length', 72); if (!empty($msg_uid) && empty($COMPOSE['as_attachment'])) { - // similar as in program/steps/mail/show.inc - // re-set 'prefer_html' to have possibility to use html part for compose - $CONFIG['prefer_html'] = $CONFIG['prefer_html'] || $CONFIG['htmleditor'] || $compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT; + $mbox_name = $RCMAIL->storage->get_folder(); + + // set format before rcube_message construction + // use the same format as for the message view + if (isset($_SESSION['msg_formats'][$mbox_name.':'.$msg_uid])) { + $RCMAIL->config->set('prefer_html', $_SESSION['msg_formats'][$mbox_name.':'.$msg_uid]); + } + else { + $prefer_html = $CONFIG['prefer_html'] || $CONFIG['htmleditor'] || $compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT; + $RCMAIL->config->set('prefer_html', $prefer_html); + } + $MESSAGE = new rcube_message($msg_uid); // make sure message is marked as read @@ -538,8 +547,8 @@ function rcmail_compose_editor_mode() function rcmail_message_is_html() { - global $MESSAGE; - return ($MESSAGE instanceof rcube_message) && $MESSAGE->has_html_part(true); + global $RCMAIL, $MESSAGE; + return $RCMAIL->config->get('prefer_html') && ($MESSAGE instanceof rcube_message) && $MESSAGE->has_html_part(true); } function rcmail_prepare_message_body() diff --git a/program/steps/mail/show.inc b/program/steps/mail/show.inc index 87555cbbe..552c180f5 100644 --- a/program/steps/mail/show.inc +++ b/program/steps/mail/show.inc @@ -19,7 +19,7 @@ +-----------------------------------------------------------------------+ */ -$PRINT_MODE = $RCMAIL->action=='print' ? TRUE : FALSE; +$PRINT_MODE = $RCMAIL->action == 'print' ? TRUE : FALSE; // Read browser capabilities and store them in session if ($caps = get_input_value('_caps', RCUBE_INPUT_GET)) { @@ -31,8 +31,21 @@ if ($caps = get_input_value('_caps', RCUBE_INPUT_GET)) { $_SESSION['browser_caps'] = $browser_caps; } +$uid = get_input_value('_uid', RCUBE_INPUT_GET); +$mbox_name = $RCMAIL->storage->get_folder(); + // similar code as in program/steps/mail/get.inc -if ($uid = get_input_value('_uid', RCUBE_INPUT_GET)) { +if ($uid) { + // set message format (need to be done before rcube_message construction) + if (!empty($_GET['_format'])) { + $prefer_html = $_GET['_format'] == 'html'; + $RCMAIL->config->set('prefer_html', $prefer_html); + $_SESSION['msg_formats'][$mbox_name.':'.$uid] = $prefer_html; + } + else if (isset($_SESSION['msg_formats'][$mbox_name.':'.$uid])) { + $RCMAIL->config->set('prefer_html', $_SESSION['msg_formats'][$mbox_name.':'.$uid]); + } + $MESSAGE = new rcube_message($uid); // if message not found (wrong UID)... @@ -40,7 +53,6 @@ if ($uid = get_input_value('_uid', RCUBE_INPUT_GET)) { rcmail_message_error($uid); } - $mbox_name = $RCMAIL->storage->get_folder(); // show images? rcmail_check_safe($MESSAGE); @@ -106,6 +118,11 @@ if ($uid = get_input_value('_uid', RCUBE_INPUT_GET)) { $OUTPUT->add_label('checkingmail', 'deletemessage', 'movemessagetotrash', 'movingmessage', 'deletingmessage', 'markingmessage'); + $prefer_html = $RCMAIL->config->get('prefer_html'); + if ($MESSAGE->has_html_part()) { + $OUTPUT->set_env('optional_format', $prefer_html ? 'text' : 'html'); + } + // check for unset disposition notification if ($MESSAGE->headers->mdn_to && empty($MESSAGE->headers->flags['MDNSENT']) @@ -288,9 +305,9 @@ $OUTPUT->add_handlers(array( )); -if ($RCMAIL->action=='print' && $OUTPUT->template_exists('messageprint')) +if ($RCMAIL->action == 'print' && $OUTPUT->template_exists('messageprint')) $OUTPUT->send('messageprint', false); -else if ($RCMAIL->action=='preview' && $OUTPUT->template_exists('messagepreview')) +else if ($RCMAIL->action == 'preview' && $OUTPUT->template_exists('messagepreview')) $OUTPUT->send('messagepreview', false); else $OUTPUT->send('message', false); diff --git a/skins/classic/images/icons/html.png b/skins/classic/images/icons/html.png new file mode 100644 index 000000000..3f022f678 Binary files /dev/null and b/skins/classic/images/icons/html.png differ diff --git a/skins/classic/images/icons/text.png b/skins/classic/images/icons/text.png new file mode 100644 index 000000000..94891be80 Binary files /dev/null and b/skins/classic/images/icons/text.png differ diff --git a/skins/classic/mail.css b/skins/classic/mail.css index 4d42d98ff..7c350ca3d 100644 --- a/skins/classic/mail.css +++ b/skins/classic/mail.css @@ -1312,20 +1312,27 @@ div.message-htmlpart div.rcmBody text-decoration: underline; } -#openextwinlink +#messagelinks { position: absolute; top: 8px; right: 10px; - width: 15px; - height: 15px; - border: 0; + height: 16px; + text-align: right; +} + +#messageframe #messagelinks +{ + top: 2px; + right: 2px; } #compose-headers #openextwinlink { - top: 4px; - right: 2px; + position: absolute; + height: 15px; + top: 4px; + right: 2px; } #full-headers diff --git a/skins/classic/templates/message.html b/skins/classic/templates/message.html index 73dfcb976..11e58c711 100644 --- a/skins/classic/templates/message.html +++ b/skins/classic/templates/message.html @@ -39,6 +39,14 @@
+ diff --git a/skins/classic/templates/messagepreview.html b/skins/classic/templates/messagepreview.html index 935238edf..80dbe381a 100644 --- a/skins/classic/templates/messagepreview.html +++ b/skins/classic/templates/messagepreview.html @@ -9,7 +9,15 @@
- + -- cgit v1.2.3 From 0e32d7032b54838607e071588fd30ff8b13fc7fe Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 15 Mar 2013 12:50:53 +0100 Subject: Fix load-attachment link --- program/steps/mail/func.inc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'program/steps/mail') diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index 3aea6322e..8c9743949 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -1185,10 +1185,9 @@ function rcmail_message_body($attrib) $show_link = array( 'href' => $MESSAGE->get_part_url($attach_prop->mime_id, false), 'onclick' => sprintf( - 'return %s.command(\'load-attachment\',{part:\'%s\', mimetype:\'%s\'},this)', + 'return %s.command(\'load-attachment\',\'%s\',this)', JS_OBJECT_NAME, - $attach_prop->mime_id, - $mimetype) + $attach_prop->mime_id) ); $out .= html::p('image-attachment', html::a($show_link + array('class' => 'image-link', 'style' => sprintf('width:%dpx', $thumbnail_size)), -- cgit v1.2.3 From 6e8f2a7448d9bf5a87603b197816027f3dd4bb4c Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Sat, 16 Mar 2013 19:01:10 +0100 Subject: Notify about a new mail only if it's UNSEEN (#1388965) --- plugins/newmail_notifier/newmail_notifier.php | 63 ++++++++++++++++----------- plugins/newmail_notifier/package.xml | 6 +-- program/lib/Roundcube/rcube_imap.php | 10 +++-- program/lib/Roundcube/rcube_storage.php | 5 ++- program/steps/mail/check_recent.inc | 4 +- 5 files changed, 52 insertions(+), 36 deletions(-) (limited to 'program/steps/mail') diff --git a/plugins/newmail_notifier/newmail_notifier.php b/plugins/newmail_notifier/newmail_notifier.php index e985c8b49..912ff4f66 100644 --- a/plugins/newmail_notifier/newmail_notifier.php +++ b/plugins/newmail_notifier/newmail_notifier.php @@ -35,6 +35,9 @@ class newmail_notifier extends rcube_plugin private $rc; private $notified; + private $opt = array(); + private $exceptions = array(); + /** * Plugin initialization @@ -49,13 +52,34 @@ class newmail_notifier extends rcube_plugin $this->add_hook('preferences_save', array($this, 'prefs_save')); } else { // if ($this->rc->task == 'mail') { - $this->add_hook('new_messages', array($this, 'notify')); // add script when not in ajax and not in frame if ($this->rc->output->type == 'html' && empty($_REQUEST['_framed'])) { $this->add_texts('localization/'); $this->rc->output->add_label('newmail_notifier.title', 'newmail_notifier.body'); $this->include_script('newmail_notifier.js'); } + + if ($this->rc->action == 'refresh') { + // Load configuration + $this->load_config(); + + $this->opt['basic'] = $this->rc->config->get('newmail_notifier_basic'); + $this->opt['sound'] = $this->rc->config->get('newmail_notifier_sound'); + $this->opt['desktop'] = $this->rc->config->get('newmail_notifier_desktop'); + + if (!empty($this->opt)) { + // Get folders to skip checking for + $exceptions = array('drafts_mbox', 'sent_mbox', 'trash_mbox'); + foreach ($exceptions as $folder) { + $folder = $this->rc->config->get($folder); + if (strlen($folder) && $folder != 'INBOX') { + $this->exceptions[] = $folder; + } + } + + $this->add_hook('new_messages', array($this, 'notify')); + } + } } } @@ -132,43 +156,30 @@ class newmail_notifier extends rcube_plugin */ function notify($args) { - // Already notified or non-automatic check - if ($this->notified || !empty($_GET['_refresh'])) { + // Already notified or unexpected input + if ($this->notified || empty($args['diff']['new'])) { return $args; } - // Get folders to skip checking for - if (empty($this->exceptions)) { - $this->delimiter = $this->rc->storage->get_hierarchy_delimiter(); - - $exceptions = array('drafts_mbox', 'sent_mbox', 'trash_mbox'); - foreach ($exceptions as $folder) { - $folder = $this->rc->config->get($folder); - if (strlen($folder) && $folder != 'INBOX') { - $this->exceptions[] = $folder; - } - } - } - - $mbox = $args['mailbox']; + $mbox = $args['mailbox']; + $storage = $this->rc->get_storage(); + $delimiter = $storage->get_hierarchy_delimiter(); // Skip exception (sent/drafts) folders (and their subfolders) foreach ($this->exceptions as $folder) { - if (strpos($mbox.$this->delimiter, $folder.$this->delimiter) === 0) { + if (strpos($mbox.$delimiter, $folder.$delimiter) === 0) { return $args; } } - $this->notified = true; - - // Load configuration - $this->load_config(); + // Check if any of new messages is UNSEEN + $deleted = $this->rc->config->get('skip_deleted') ? 'UNDELETED ' : ''; + $search = $deleted . 'UNSEEN UID ' . $args['diff']['new']; + $unseen = $storage->search_once($mbox, $search); - $basic = $this->rc->config->get('newmail_notifier_basic'); - $sound = $this->rc->config->get('newmail_notifier_sound'); - $desktop = $this->rc->config->get('newmail_notifier_desktop'); + if ($unseen->count()) { + $this->notified = true; - if ($basic || $sound || $desktop) { $this->rc->output->command('plugin.newmail_notifier', array('basic' => $basic, 'sound' => $sound, 'desktop' => $desktop)); } diff --git a/plugins/newmail_notifier/package.xml b/plugins/newmail_notifier/package.xml index d3de25fb3..ea0fcd9c1 100644 --- a/plugins/newmail_notifier/package.xml +++ b/plugins/newmail_notifier/package.xml @@ -19,10 +19,10 @@ alec@alec.pl yes - 2012-02-07 + 2013-03-16 - 0.4 - 0.3 + 0.5 + 0.5 stable diff --git a/program/lib/Roundcube/rcube_imap.php b/program/lib/Roundcube/rcube_imap.php index 8d9c37576..0aa059c26 100644 --- a/program/lib/Roundcube/rcube_imap.php +++ b/program/lib/Roundcube/rcube_imap.php @@ -1096,16 +1096,17 @@ class rcube_imap extends rcube_storage /** - * Returns current status of folder + * Returns current status of a folder (compared to the last time use) * * We compare the maximum UID to determine the number of * new messages because the RECENT flag is not reliable. * * @param string $folder Folder name + * @param array $diff Difference data * - * @return int Folder status + * @return int Folder status */ - public function folder_status($folder = null) + public function folder_status($folder = null, &$diff = array()) { if (!strlen($folder)) { $folder = $this->folder; @@ -1126,6 +1127,9 @@ class rcube_imap extends rcube_storage // got new messages if ($new['maxuid'] > $old['maxuid']) { $result += 1; + // get new message UIDs range, that can be used for example + // to get the data of these messages + $diff['new'] = ($old['maxuid'] + 1 < $new['maxuid'] ? ($old['maxuid']+1).':' : '') . $new['maxuid']; } // some messages has been deleted if ($new['cnt'] < $old['cnt']) { diff --git a/program/lib/Roundcube/rcube_storage.php b/program/lib/Roundcube/rcube_storage.php index 8a36f1f9d..700d12ffb 100644 --- a/program/lib/Roundcube/rcube_storage.php +++ b/program/lib/Roundcube/rcube_storage.php @@ -807,13 +807,14 @@ abstract class rcube_storage /** - * Returns current status of a folder + * Returns current status of a folder (compared to the last time use) * * @param string $folder Folder name + * @param array $diff Difference data * * @return int Folder status */ - abstract function folder_status($folder = null); + abstract function folder_status($folder = null, &$diff = array()); /** diff --git a/program/steps/mail/check_recent.inc b/program/steps/mail/check_recent.inc index d3c14a38d..3649d148c 100644 --- a/program/steps/mail/check_recent.inc +++ b/program/steps/mail/check_recent.inc @@ -52,12 +52,12 @@ foreach ($a_mailboxes as $mbox_name) { } // Get mailbox status - $status = $RCMAIL->storage->folder_status($mbox_name); + $status = $RCMAIL->storage->folder_status($mbox_name, $diff); if ($status & 1) { // trigger plugin hook $RCMAIL->plugins->exec_hook('new_messages', - array('mailbox' => $mbox_name, 'is_current' => $is_current)); + array('mailbox' => $mbox_name, 'is_current' => $is_current, 'diff' => $diff)); } rcmail_send_unread_count($mbox_name, true, null, -- cgit v1.2.3 From a8040cf0bd71aad66b6847e27d14fe302ef87e95 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 19 Mar 2013 09:11:56 +0100 Subject: Fix so forward as attachment works if additional attachment is added by message_compose hook (#1489000) Prevent from re-adding the same forward attachment on compose page refresh --- CHANGELOG | 1 + program/steps/mail/compose.inc | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'program/steps/mail') diff --git a/CHANGELOG b/CHANGELOG index 42d6cc72a..1e3eb77b5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Fix so forward as attachment works if additional attachment is added by message_compose hook (#1489000) - Add ability to toggle between HTML and text while viewing a message (#1486939) - Better handling of session errors in ajax requests (#1488960) - Fix HTML part detection for some specific message structures (#1488992) diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index 6a579f754..c166eb74e 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -568,8 +568,7 @@ function rcmail_prepare_message_body() else if ($compose_mode == RCUBE_COMPOSE_FORWARD && $COMPOSE['as_attachment']) { $isHtml = rcmail_compose_editor_mode(); $body = ''; - if (empty($COMPOSE['attachments'])) - rcmail_write_forward_attachments(); + rcmail_write_forward_attachments(); } // reply/edit/draft/forward else if ($compose_mode && ($compose_mode != RCUBE_COMPOSE_REPLY || $RCMAIL->config->get('reply_mode') != -1)) { @@ -1074,6 +1073,11 @@ function rcmail_write_forward_attachments() $curr_mem = function_exists('memory_get_usage') ? memory_get_usage() : 16*1024*1024; // safe value: 16MB $names = array(); + $loaded_attachments = array(); + foreach ((array)$COMPOSE['attachments'] as $id => $attachment) { + $loaded_attachments[$attachment['name'] . $attachment['mimetype']] = $attachment; + } + if ($COMPOSE['forward_uid'] == '*') { $index = $storage->index(null, rcmail_sort_column(), rcmail_sort_order()); $COMPOSE['forward_uid'] = $index->get(); @@ -1108,6 +1112,10 @@ function rcmail_write_forward_attachments() $data = $path = null; + if (!empty($loaded_attachments[$name . 'message/rfc822'])) { + continue; + } + // don't load too big attachments into memory if ($mem_limit > 0 && $message->size > $mem_limit - $curr_mem) { $temp_dir = unslashify($RCMAIL->config->get('temp_dir')); -- cgit v1.2.3 From 1e32540839683c1309db012c4d5b9aff35ec6ae3 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 19 Mar 2013 12:47:07 +0100 Subject: Add rel="noreferrer" for links in displayed messages (#1484686) --- CHANGELOG | 1 + program/lib/Roundcube/rcube_string_replacer.php | 13 ++++++++----- program/steps/mail/func.inc | 14 ++++++++++---- tests/Framework/StringReplacer.php | 22 +++++++++++----------- tests/MailFunc.php | 8 ++++---- 5 files changed, 34 insertions(+), 24 deletions(-) (limited to 'program/steps/mail') diff --git a/CHANGELOG b/CHANGELOG index 1e3eb77b5..ed0ea6ef3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Add rel="noreferrer" for links in displayed messages (#1484686) - Fix so forward as attachment works if additional attachment is added by message_compose hook (#1489000) - Add ability to toggle between HTML and text while viewing a message (#1486939) - Better handling of session errors in ajax requests (#1488960) diff --git a/program/lib/Roundcube/rcube_string_replacer.php b/program/lib/Roundcube/rcube_string_replacer.php index 49a378166..b8768bc98 100644 --- a/program/lib/Roundcube/rcube_string_replacer.php +++ b/program/lib/Roundcube/rcube_string_replacer.php @@ -28,9 +28,10 @@ class rcube_string_replacer public $mailto_pattern; public $link_pattern; private $values = array(); + private $options = array(); - function __construct() + function __construct($options = array()) { // Simplified domain expression for UTF8 characters handling // Support unicode/punycode in top-level domain part @@ -44,6 +45,8 @@ class rcube_string_replacer ."@$utf_domain" // domain-part ."(\?[$url1$url2]+)?" // e.g. ?subject=test... .")/"; + + $this->options = $options; } /** @@ -89,10 +92,10 @@ class rcube_string_replacer if ($url) { $suffix = $this->parse_url_brackets($url); - $i = $this->add($prefix . html::a(array( - 'href' => $url_prefix . $url, - 'target' => '_blank' - ), rcube::Q($url)) . $suffix); + $attrib = (array)$this->options['link_attribs']; + $attrib['href'] = $url_prefix . $url; + + $i = $this->add($prefix . html::a($attrib, rcube::Q($url)) . $suffix); } // Return valid link for recognized schemes, otherwise diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index 8c9743949..274c40b5c 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -760,7 +760,8 @@ function rcmail_plain_body($body, $flowed=false) global $RCMAIL; // make links and email-addresses clickable - $replacer = new rcmail_string_replacer; + $attribs = array('link_attribs' => array('rel' => 'noreferrer', 'target' => '_blank')); + $replacer = new rcmail_string_replacer($attribs); // search for patterns like links and e-mail addresses and replace with tokens $body = $replacer->replace($body); @@ -1373,7 +1374,7 @@ function rcmail_html4inline($body, $container_id, $body_id='', &$attributes=null /** - * parse link attributes and set correct target + * parse link (a, link, area) attributes and set correct target */ function rcmail_alter_html_link($matches) { @@ -1382,9 +1383,9 @@ function rcmail_alter_html_link($matches) // Support unicode/punycode in top-level domain part $EMAIL_PATTERN = '([a-z0-9][a-z0-9\-\.\+\_]*@[^&@"\'.][^@&"\']*\\.([^\\x00-\\x40\\x5b-\\x60\\x7b-\\x7f]{2,}|xn--[a-z0-9]{2,}))'; - $tag = $matches[1]; + $tag = strtolower($matches[1]); $attrib = parse_attrib_string($matches[2]); - $end = '>'; + $end = '>'; // Remove non-printable characters in URL (#1487805) if ($attrib['href']) @@ -1411,6 +1412,11 @@ function rcmail_alter_html_link($matches) $attrib['target'] = '_blank'; } + // Better security by adding rel="noreferrer" (#1484686) + if (($tag == 'a' || $tag == 'area') && $attrib['href'] && $attrib['href'][0] != '#') { + $attrib['rel'] = 'noreferrer'; + } + // allowed attributes for a|link|area tags $allow = array('href','name','target','onclick','id','class','style','title', 'rel','type','media','alt','coords','nohref','hreflang','shape'); diff --git a/tests/Framework/StringReplacer.php b/tests/Framework/StringReplacer.php index e630ebac0..95c59221b 100644 --- a/tests/Framework/StringReplacer.php +++ b/tests/Framework/StringReplacer.php @@ -24,17 +24,17 @@ class Framework_StringReplacer extends PHPUnit_Framework_TestCase function data_replace() { return array( - array('http://domain.tld/path*path2', 'http://domain.tld/path*path2'), - array("Click this link:\nhttps://mail.xn--brderli-o2a.ch/rc/ EOF", "Click this link:\nhttps://mail.xn--brderli-o2a.ch/rc/ EOF"), - array('Start http://localhost/?foo End', 'Start http://localhost/?foo End'), - array('www.domain.tld', 'www.domain.tld'), - array('WWW.DOMAIN.TLD', 'WWW.DOMAIN.TLD'), - array('[http://link.com]', '[http://link.com]'), - array('http://link.com?a[]=1', 'http://link.com?a[]=1'), - array('http://link.com?a[]', 'http://link.com?a[]'), - array('(http://link.com)', '(http://link.com)'), - array('http://link.com?a(b)c', 'http://link.com?a(b)c'), - array('http://link.com?(link)', 'http://link.com?(link)'), + array('http://domain.tld/path*path2', 'http://domain.tld/path*path2'), + array("Click this link:\nhttps://mail.xn--brderli-o2a.ch/rc/ EOF", "Click this link:\nhttps://mail.xn--brderli-o2a.ch/rc/ EOF"), + array('Start http://localhost/?foo End', 'Start http://localhost/?foo End'), + array('www.domain.tld', 'www.domain.tld'), + array('WWW.DOMAIN.TLD', 'WWW.DOMAIN.TLD'), + array('[http://link.com]', '[http://link.com]'), + array('http://link.com?a[]=1', 'http://link.com?a[]=1'), + array('http://link.com?a[]', 'http://link.com?a[]'), + array('(http://link.com)', '(http://link.com)'), + array('http://link.com?a(b)c', 'http://link.com?a(b)c'), + array('http://link.com?(link)', 'http://link.com?(link)'), array('http://', 'http://'), array('http://', 'http://'), ); diff --git a/tests/MailFunc.php b/tests/MailFunc.php index 38c0bac30..319075abd 100644 --- a/tests/MailFunc.php +++ b/tests/MailFunc.php @@ -54,7 +54,7 @@ class MailFunc extends PHPUnit_Framework_TestCase $this->assertNotRegExp('/
]+>/', $html, "No form tags allowed"); $this->assertRegExp('/Subscription form/', $html, "Include contents"); $this->assertRegExp('//', $html, "No external links allowed"); - $this->assertRegExp('/]+ target="_blank">/', $html, "Set target to _blank"); + $this->assertRegExp('/]+ target="_blank"/', $html, "Set target to _blank"); $this->assertTrue($GLOBALS['REMOTE_OBJECTS'], "Remote object detected"); // render HTML in safe mode @@ -133,8 +133,8 @@ class MailFunc extends PHPUnit_Framework_TestCase $html = rcmail_print_body($part, array('safe' => true)); $this->assertRegExp('/nobody@roundcube.net<\/a>/', $html, "Mailto links with onclick"); - $this->assertRegExp('#http://www.apple.com/legal/privacy#', $html, "Links with target=_blank"); - $this->assertRegExp('#\\[http://example.com/\\?tx\\[a\\]=5\\]#', $html, "Links with square brackets"); + $this->assertRegExp('#http://www.apple.com/legal/privacy#', $html, "Links with target=_blank"); + $this->assertRegExp('#\\[http://example.com/\\?tx\\[a\\]=5\\]#', $html, "Links with square brackets"); } /** @@ -148,7 +148,7 @@ class MailFunc extends PHPUnit_Framework_TestCase $html = rcmail_html4inline(rcmail_print_body($part, array('safe' => false)), 'foo'); $mailto = 'e-mail'; + .' onclick="return rcmail.command(\'compose\',\'me@me.com?subject=this is the subject&body=this is the body\',this)" rel="noreferrer">e-mail'; $this->assertRegExp('|'.preg_quote($mailto, '|').'|', $html, "Extended mailto links"); } -- cgit v1.2.3 From 3ca58cd22cae4cbc07479792f4ff4258aad785af Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 26 Mar 2013 19:06:11 +0100 Subject: Better fix for "saving draft just after entering compose window (#1489012)" --- program/js/app.js | 4 ++-- program/steps/mail/compose.inc | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'program/steps/mail') diff --git a/program/js/app.js b/program/js/app.js index 9e05233f1..b2a6a0409 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -986,7 +986,7 @@ function rcube_webmail() clearTimeout(this.save_timer); // compose form did not change (and draft wasn't saved already) - if (this.draft_saved && this.cmp_hash == this.compose_field_hash()) { + if (this.env.draft_id && this.cmp_hash == this.compose_field_hash()) { this.auto_save_start(); break; } @@ -3302,7 +3302,7 @@ function rcube_webmail() this.set_draft_id = function(id) { - this.draft_saved = id; + this.env.draft_id = id; $("input[name='_draft_saveid']").val(id); }; diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index c166eb74e..129d43701 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -152,6 +152,7 @@ if ($font && !is_array($font)) { // get reference message and set compose mode if ($msg_uid = $COMPOSE['param']['draft_uid']) { $compose_mode = RCUBE_COMPOSE_DRAFT; + $OUTPUT->set_env('draft_id', $msg_uid); $RCMAIL->storage->set_folder($CONFIG['drafts_mbox']); } else if ($msg_uid = $COMPOSE['param']['reply_uid']) { -- cgit v1.2.3 From 723f4e63aa2e60e0ef72cdfb506299512a9c3405 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 26 Mar 2013 19:16:03 +0100 Subject: Refresh current folder in opener window after draft save or message sent (#1488997) --- CHANGELOG | 1 + program/js/app.js | 20 ++++++++++++++++---- program/steps/mail/sendmail.inc | 2 +- 3 files changed, 18 insertions(+), 5 deletions(-) (limited to 'program/steps/mail') diff --git a/CHANGELOG b/CHANGELOG index 9d7c0d75f..c1032630b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Refresh current folder in opener window after draft save or message sent (#1488997) - Fix saving draft just after entering compose window (#1489012) - Call resize handler in intervals to prevent lags and double onresize calls in Chrome (#1489005) - Fix javascript error in IE9 when loading form with placeholders into an iframe (#1489008) diff --git a/program/js/app.js b/program/js/app.js index b2a6a0409..758e4b893 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -3302,6 +3302,14 @@ function rcube_webmail() this.set_draft_id = function(id) { + var rc; + + if (!this.env.draft_id && id && (rc = this.opener())) { + // refresh the drafts folder in opener window + if (rc.env.task == 'mail' && rc.env.action == '' && rc.env.mailbox == this.env.drafts_mailbox) + rc.command('checkmail'); + } + this.env.draft_id = id; $("input[name='_draft_saveid']").val(id); }; @@ -3678,15 +3686,19 @@ function rcube_webmail() this.env.search_id = null; }; - this.sent_successfully = function(type, msg) + this.sent_successfully = function(type, msg, target) { this.display_message(msg, type); if (this.env.extwin) { - var opener_rc = this.opener(); + var rc = this.opener(); this.lock_form(this.gui_objects.messageform); - if (opener_rc) - opener_rc.display_message(msg, type); + if (rc) { + rc.display_message(msg, type); + // refresh the folder where sent message was saved + if (target && rc.env.task == 'mail' && rc.env.action == '' && rc.env.mailbox == target) + rc.command('checkmail'); + } setTimeout(function(){ window.close() }, 1000); } else { diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc index eb0ba89c6..eca0b507d 100644 --- a/program/steps/mail/sendmail.inc +++ b/program/steps/mail/sendmail.inc @@ -820,6 +820,6 @@ else { if ($store_folder && !$saved) $OUTPUT->command('sent_successfully', 'error', rcube_label('errorsavingsent')); else - $OUTPUT->command('sent_successfully', 'confirmation', rcube_label('messagesent')); + $OUTPUT->command('sent_successfully', 'confirmation', rcube_label('messagesent'), $store_target); $OUTPUT->send('iframe'); } -- cgit v1.2.3 From 1d4c84f4d74217a6639b143c14c99e3473fc539a Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 26 Mar 2013 19:36:47 +0100 Subject: Remove sig_above configuration option, use reply_mode only (#1489001) --- CHANGELOG | 1 + config/main.inc.php.dist | 3 --- program/js/app.js | 6 +++--- program/localization/en_US/labels.inc | 3 --- program/steps/mail/compose.inc | 3 +-- program/steps/settings/func.inc | 15 +-------------- program/steps/settings/save_prefs.inc | 1 - 7 files changed, 6 insertions(+), 26 deletions(-) (limited to 'program/steps/mail') diff --git a/CHANGELOG b/CHANGELOG index c1032630b..0dc5d94cc 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Remove sig_above configuration option, use reply_mode only (#1489001) - Refresh current folder in opener window after draft save or message sent (#1488997) - Fix saving draft just after entering compose window (#1489012) - Call resize handler in intervals to prevent lags and double onresize calls in Chrome (#1489005) diff --git a/config/main.inc.php.dist b/config/main.inc.php.dist index b113b41a8..5a652a5b1 100644 --- a/config/main.inc.php.dist +++ b/config/main.inc.php.dist @@ -835,9 +835,6 @@ $rcmail_config['strip_existing_sig'] = true; // 3 - Forwards and Replies only $rcmail_config['show_sig'] = 1; -// When replying or forwarding place sender's signature above existing message -$rcmail_config['sig_above'] = false; - // Use MIME encoding (quoted-printable) for 8bit characters in message body $rcmail_config['force_7bit'] = false; diff --git a/program/js/app.js b/program/js/app.js index 758e4b893..ad82ba579 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -3376,7 +3376,7 @@ function rcube_webmail() sig = this.env.signatures[sig].text; sig = sig.replace(/\r\n/g, '\n'); - p = this.env.sig_above ? message.indexOf(sig) : message.lastIndexOf(sig); + p = this.env.top_posting ? message.indexOf(sig) : message.lastIndexOf(sig); if (p >= 0) message = message.substring(0, p) + message.substring(p+sig.length, message.length); } @@ -3385,7 +3385,7 @@ function rcube_webmail() sig = this.env.signatures[id].text; sig = sig.replace(/\r\n/g, '\n'); - if (this.env.sig_above) { + if (this.env.top_posting) { if (p >= 0) { // in place of removed signature message = message.substring(0, p) + sig + message.substring(p, message.length); cursor_pos = p - 1; @@ -3429,7 +3429,7 @@ function rcube_webmail() sigElem = doc.createElement('div'); sigElem.setAttribute('id', '_rc_sig'); - if (this.env.sig_above) { + if (this.env.top_posting) { // if no existing sig and top posting then insert at caret pos editor.getWin().focus(); // correct focus in IE & Chrome diff --git a/program/localization/en_US/labels.inc b/program/localization/en_US/labels.inc index 252e0ce68..0a4e329e5 100644 --- a/program/localization/en_US/labels.inc +++ b/program/localization/en_US/labels.inc @@ -454,9 +454,6 @@ $labels['replyremovesignature'] = 'When replying remove original signature from $labels['autoaddsignature'] = 'Automatically add signature'; $labels['newmessageonly'] = 'new message only'; $labels['replyandforwardonly'] = 'replies and forwards only'; -$labels['replysignaturepos'] = 'When replying or forwarding place signature'; -$labels['belowquote'] = 'below the quote'; -$labels['abovequote'] = 'above the quote'; $labels['insertsignature'] = 'Insert signature'; $labels['previewpanemarkread'] = 'Mark previewed messages as read'; $labels['afternseconds'] = 'after $n seconds'; diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index 129d43701..cc7cf687f 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -139,7 +139,6 @@ if (!empty($CONFIG['drafts_mbox'])) { } // set current mailbox in client environment $OUTPUT->set_env('mailbox', $RCMAIL->storage->get_folder()); -$OUTPUT->set_env('sig_above', $RCMAIL->config->get('sig_above', false)); $OUTPUT->set_env('top_posting', intval($RCMAIL->config->get('reply_mode')) > 0); $OUTPUT->set_env('recipients_separator', trim($RCMAIL->config->get('recipients_separator', ','))); @@ -462,7 +461,7 @@ function rcmail_compose_header_from($attrib) if (count($MESSAGE->identities)) { $a_signatures = array(); - $separator = $RCMAIL->config->get('sig_above') + $separator = intval($RCMAIL->config->get('reply_mode')) > 0 && ($compose_mode == RCUBE_COMPOSE_REPLY || $compose_mode == RCUBE_COMPOSE_FORWARD) ? '---' : '-- '; $field_attrib['onchange'] = JS_OBJECT_NAME.".change_identity(this)"; diff --git a/program/steps/settings/func.inc b/program/steps/settings/func.inc index 2f726c7e8..319c58db9 100644 --- a/program/steps/settings/func.inc +++ b/program/steps/settings/func.inc @@ -581,8 +581,7 @@ function rcmail_user_prefs($current=null) if (!isset($no_override['reply_mode'])) { $field_id = 'rcmfd_reply_mode'; - $select_replymode = new html_select(array('name' => '_reply_mode', 'id' => $field_id, - 'onchange' => "\$('#rcmfd_sig_above').attr('disabled',this.selectedIndex<2)")); + $select_replymode = new html_select(array('name' => '_reply_mode', 'id' => $field_id)); $select_replymode->add(rcube_label('replyempty'), -1); $select_replymode->add(rcube_label('replybottomposting'), 0); $select_replymode->add(rcube_label('replytopposting'), 1); @@ -631,18 +630,6 @@ function rcmail_user_prefs($current=null) ); } - if (!isset($no_override['sig_above'])) { - $field_id = 'rcmfd_sig_above'; - $select_sigabove = new html_select(array('name' => '_sig_above', 'id' => $field_id, 'disabled' => $config['reply_mode'] < 1)); - $select_sigabove->add(rcube_label('belowquote'), 0); - $select_sigabove->add(rcube_label('abovequote'), 1); - - $blocks['sig']['options']['sig_above'] = array( - 'title' => html::label($field_id, Q(rcube_label('replysignaturepos'))), - 'content' => $select_sigabove->show($config['sig_above']?1:0), - ); - } - if (!isset($no_override['strip_existing_sig'])) { $field_id = 'rcmfd_strip_existing_sig'; $input_stripexistingsig = new html_checkbox(array('name' => '_strip_existing_sig', 'id' => $field_id, 'value' => 1)); diff --git a/program/steps/settings/save_prefs.inc b/program/steps/settings/save_prefs.inc index 5daab0d24..140f173c6 100644 --- a/program/steps/settings/save_prefs.inc +++ b/program/steps/settings/save_prefs.inc @@ -86,7 +86,6 @@ switch ($CURR_SECTION) 'show_sig' => isset($_POST['_show_sig']) ? intval($_POST['_show_sig']) : 1, 'reply_mode' => isset($_POST['_reply_mode']) ? intval($_POST['_reply_mode']) : 0, 'strip_existing_sig' => isset($_POST['_strip_existing_sig']), - 'sig_above' => !empty($_POST['_sig_above']) && $_POST['_reply_mode'] > 0, 'default_font' => get_input_value('_default_font', RCUBE_INPUT_POST), 'forward_attachment' => !empty($_POST['_forward_attachment']), ); -- cgit v1.2.3 From 0fcb2b139bf0c50dec3b82898434f203c21d847f Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Wed, 27 Mar 2013 17:05:54 +0100 Subject: Sanity check the file path for generic message footer before adding it --- program/steps/mail/sendmail.inc | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) (limited to 'program/steps/mail') diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc index eca0b507d..f24f20789 100644 --- a/program/steps/mail/sendmail.inc +++ b/program/steps/mail/sendmail.inc @@ -255,6 +255,33 @@ function rcmail_email_input_format($mailto, $count=false, $check=true) } +function rcmail_generic_message_footer($isHtml) +{ + global $CONFIG; + + if ($isHtml && !empty($CONFIG['generic_message_footer_html'])) { + $file = $CONFIG['generic_message_footer_html']; + $html_footer = true; + } + else { + $file = $CONFIG['generic_message_footer']; + $html_footer = false; + } + + if ($file && realpath($file)) { + // sanity check + if (!preg_match('/\.(php|ini|conf)$/', $file) && strpos($file, '/etc/') === false) { + $footer = file_get_contents($file); + if ($isHtml && !$html_footer) + $footer = '
' . $footer . '
'; + return $footer; + } + } + + return false; +} + + /****** compose message ********/ if (strlen($_POST['_draft_saveid']) > 3) @@ -490,19 +517,10 @@ if (!$savedraft) { } // generic footer for all messages - if ($isHtml && !empty($CONFIG['generic_message_footer_html'])) { - $footer = file_get_contents(realpath($CONFIG['generic_message_footer_html'])); - $footer = rcube_charset_convert($footer, RCMAIL_CHARSET, $message_charset); - } - else if (!empty($CONFIG['generic_message_footer'])) { - $footer = file_get_contents(realpath($CONFIG['generic_message_footer'])); + if ($footer = rcmail_generic_message_footer($isHtml)) { $footer = rcube_charset_convert($footer, RCMAIL_CHARSET, $message_charset); - if ($isHtml) - $footer = '
'.$footer.'
'; - } - - if ($footer) $message_body .= "\r\n" . $footer; + } } if ($isHtml) { -- cgit v1.2.3 From db415c4afb8c4eb28b5840733c306d1383f06ce6 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 2 Apr 2013 10:15:51 +0200 Subject: Fix blockquote width in sent mail (#1489031) --- CHANGELOG | 1 + program/steps/mail/sendmail.inc | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'program/steps/mail') diff --git a/CHANGELOG b/CHANGELOG index 8240af5d7..fc408f199 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Fix blockquote width in sent mail (#1489031) - Fix keyboard events on list widgets in Internet Explorer (#1489025) - Call resize handler in intervals to prevent lags and double onresize calls in Chrome (#1489005) - Add rel="noreferrer" for links in displayed messages (#1484686) diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc index f24f20789..2f96e930f 100644 --- a/program/steps/mail/sendmail.inc +++ b/program/steps/mail/sendmail.inc @@ -493,7 +493,7 @@ if (!$savedraft) { $message_body = preg_replace('/\s*id="_rc_sig"/', '', $message_body); // add inline css for blockquotes - $bstyle = 'padding-left:5px; border-left:#1010ff 2px solid; margin-left:5px; width:100%'; + $bstyle = 'padding-left:5px; border-left:#1010ff 2px solid; margin-left:5px'; $message_body = preg_replace('/
/', '
', $message_body); } -- cgit v1.2.3 From a389ffbb46d51d70fda99b1a3e043cd4f3f41ea1 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 2 Apr 2013 19:48:32 +0200 Subject: Skip message/rfc822 attachments only in reply mode --- program/steps/mail/compose.inc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'program/steps/mail') diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index cc7cf687f..36c6d9622 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -997,10 +997,12 @@ function rcmail_write_compose_attachments(&$message, $bodyIsHtml) foreach ((array)$message->mime_parts as $pid => $part) { if ($part->disposition == 'attachment' || ($part->disposition == 'inline' && $bodyIsHtml) || $part->filename) { - if (($part->ctype_primary == 'message' && $bodyIsHtml) || $part->ctype_primary == 'multipart') { + // skip parts that aren't valid attachments + if ($part->ctype_primary == 'multipart' || $part->mimetype == 'application/ms-tnef') { continue; } - if ($part->mimetype == 'application/ms-tnef') { + // skip message attachments in reply mode + if ($part->ctype_primary == 'message' && $compose_mode == RCUBE_COMPOSE_REPLY) { continue; } // skip inline images when forwarding in plain text -- cgit v1.2.3