summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG8
-rw-r--r--plugins/acl/acl.php10
-rw-r--r--plugins/acl/config.inc.php.dist3
-rw-r--r--plugins/enigma/lib/enigma_driver_phpssl.php2
-rw-r--r--plugins/new_user_identity/config.inc.php.dist10
-rw-r--r--plugins/new_user_identity/new_user_identity.php31
-rw-r--r--plugins/password/README20
-rw-r--r--plugins/password/config.inc.php.dist5
-rw-r--r--plugins/password/drivers/ldap.php25
-rw-r--r--plugins/password/drivers/plesk.php2
-rw-r--r--plugins/password/helpers/dovecot_hmacmd5.php191
-rw-r--r--plugins/vcard_attachments/vcard_attachments.php2
-rw-r--r--plugins/zipdownload/zipdownload.php20
-rw-r--r--program/include/rcmail.php8
-rw-r--r--program/js/list.js4
-rw-r--r--program/lib/Roundcube/rcube.php16
-rw-r--r--program/lib/Roundcube/rcube_image.php2
-rw-r--r--program/lib/Roundcube/rcube_imap_cache.php10
-rw-r--r--program/lib/Roundcube/rcube_imap_generic.php87
-rw-r--r--program/lib/Roundcube/rcube_message.php270
-rw-r--r--program/steps/mail/compose.inc17
-rw-r--r--program/steps/mail/func.inc32
-rw-r--r--program/steps/mail/get.inc40
-rw-r--r--program/steps/mail/sendmail.inc3
-rw-r--r--skins/classic/print.css2
-rw-r--r--skins/larry/mail.css1
-rw-r--r--skins/larry/print.css2
-rw-r--r--tests/MailFunc.php16
28 files changed, 621 insertions, 218 deletions
diff --git a/CHANGELOG b/CHANGELOG
index d3c9aba90..3bf5a4378 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -49,8 +49,16 @@ CHANGELOG Roundcube Webmail
- Don't remove links when html signature is converted to text (#1489621)
- Fix page title when using search filter (#1490023)
- Fix mbox files import
+- Fix so attachment charset is set in headers of forward/draft message (#1490109)
+- Fix bug where wrong charset could be used for text attachment preview page (#1490106)
- Fix setting flags on servers with no PERMANENTFLAGS response (#1490087)
- Fix regression in SHAA password generation in ldap driver of password plugin (#1490094)
+- Fix displaying of HTML messages with absolutely positioned elements in Larry skin (#1490103)
+- Fix font style display issue in HTML messages with styled <span> elements (#1490101)
+- Fix download of attachments that are part of TNEF message (#1490091)
+- Fix handling of uuencoded messages if messages_cache is enabled (#1490108)
+- Fix handling of base64-encoded attachments with extra spaces (#1490111)
+- Fix handling of UNKNOWN-CTE response, try do decode content client-side (#1490046)
RELEASE 1.0.3
-------------
diff --git a/plugins/acl/acl.php b/plugins/acl/acl.php
index 33bd91e22..349f7e518 100644
--- a/plugins/acl/acl.php
+++ b/plugins/acl/acl.php
@@ -114,14 +114,16 @@ class acl extends rcube_plugin
}
if ($this->rc->config->get('acl_groups')) {
- $prefix = $this->rc->config->get('acl_group_prefix');
- $result = $this->ldap->list_groups($search, $mode);
+ $prefix = $this->rc->config->get('acl_group_prefix');
+ $group_field = $this->rc->config->get('acl_group_field', 'name');
+ $result = $this->ldap->list_groups($search, $mode);
foreach ($result as $record) {
- $group = $record['name'];
+ $group = $record['name'];
+ $group_id = is_array($record[$group_field]) ? $record[$group_field][0] : $record[$group_field];
if ($group) {
- $users[] = array('name' => ($prefix ? $prefix : '') . $group, 'display' => $group);
+ $users[] = array('name' => ($prefix ? $prefix : '') . $group_id, 'display' => $group);
$keys[] = $group;
}
}
diff --git a/plugins/acl/config.inc.php.dist b/plugins/acl/config.inc.php.dist
index de1f8b5d2..ed7000267 100644
--- a/plugins/acl/config.inc.php.dist
+++ b/plugins/acl/config.inc.php.dist
@@ -23,6 +23,9 @@ $config['acl_groups'] = false;
// Prefix added to the group name to build IMAP ACL identifier
$config['acl_group_prefix'] = 'group:';
+// The LDAP attribute (or field name) which will be used as ACL group identifier
+$config['acl_group_field'] = 'name';
+
// Include the following 'special' access control subjects in the ACL dialog;
// Defaults to array('anyone', 'anonymous') (not when set to an empty array)
// Example: array('anyone') to exclude 'anonymous'.
diff --git a/plugins/enigma/lib/enigma_driver_phpssl.php b/plugins/enigma/lib/enigma_driver_phpssl.php
index 50af44762..fcd15db73 100644
--- a/plugins/enigma/lib/enigma_driver_phpssl.php
+++ b/plugins/enigma/lib/enigma_driver_phpssl.php
@@ -95,7 +95,7 @@ class enigma_driver_phpssl extends enigma_driver
$fh = fopen($msg_file, "w");
if ($struct->mime_id) {
- $message->get_part_content($struct->mime_id, $fh, true, 0, false);
+ $message->get_part_body($struct->mime_id, false, 0, $fh);
}
else {
$this->rc->storage->get_raw_body($message->uid, $fh);
diff --git a/plugins/new_user_identity/config.inc.php.dist b/plugins/new_user_identity/config.inc.php.dist
new file mode 100644
index 000000000..b2fd76408
--- /dev/null
+++ b/plugins/new_user_identity/config.inc.php.dist
@@ -0,0 +1,10 @@
+<?php
+
+// The id of the address book to use to automatically set a new
+// user's full name in their new identity. (This should be an
+// string, which refers to the $config['ldap_public'] array.)
+$config['new_user_identity_addressbook'] = 'People';
+
+// When automatically setting a new users's full name in their
+// new identity, match the user's login name against this field.
+$config['new_user_identity_match'] = 'uid';
diff --git a/plugins/new_user_identity/new_user_identity.php b/plugins/new_user_identity/new_user_identity.php
index 3943134b2..d1d1d9f24 100644
--- a/plugins/new_user_identity/new_user_identity.php
+++ b/plugins/new_user_identity/new_user_identity.php
@@ -9,17 +9,6 @@
* @version @package_version@
* @author Kris Steinhoff
* @license GNU GPLv3+
- *
- * Example configuration:
- *
- * // The id of the address book to use to automatically set a new
- * // user's full name in their new identity. (This should be an
- * // string, which refers to the $config['ldap_public'] array.)
- * $config['new_user_identity_addressbook'] = 'People';
- *
- * // When automatically setting a new users's full name in their
- * // new identity, match the user's login name against this field.
- * $config['new_user_identity_match'] = 'uid';
*/
class new_user_identity extends rcube_plugin
{
@@ -36,16 +25,33 @@ class new_user_identity extends rcube_plugin
{
if ($this->init_ldap($args['host'])) {
$results = $this->ldap->search('*', $args['user'], true);
+
if (count($results->records) == 1) {
$user_name = is_array($results->records[0]['name']) ? $results->records[0]['name'][0] : $results->records[0]['name'];
$user_email = is_array($results->records[0]['email']) ? $results->records[0]['email'][0] : $results->records[0]['email'];
- $args['user_name'] = $user_name;
+ $args['user_name'] = $user_name;
+ $args['email_list'] = array();
+
if (!$args['user_email'] && strpos($user_email, '@')) {
$args['user_email'] = rcube_utils::idn_to_ascii($user_email);
}
+
+ foreach (array_keys($results[0]) as $key) {
+ if (!preg_match('/^email($|:)/', $key)) {
+ continue;
+ }
+
+ foreach ((array) $results->records[0][$key] as $alias) {
+ if (strpos($alias, '@')) {
+ $args['email_list'][] = rcube_utils::idn_to_ascii($alias);
+ }
+ }
+ }
+
}
}
+
return $args;
}
@@ -56,6 +62,7 @@ class new_user_identity extends rcube_plugin
}
$rcmail = rcmail::get_instance();
+ $this->load_config();
$addressbook = $rcmail->config->get('new_user_identity_addressbook');
$ldap_config = (array)$rcmail->config->get('ldap_public');
diff --git a/plugins/password/README b/plugins/password/README
index 936aa53cc..8c3a2afbd 100644
--- a/plugins/password/README
+++ b/plugins/password/README
@@ -317,31 +317,25 @@
2.20. Plesk (Plesk RPC-API)
---------------------------
-
+
Driver for changing Passwords via Plesk RPC-API. This Driver also works with
Parallels Plesk Automation (PPA).
-
+
You need to allow the IP of the Roundcube-Server for RPC-Calls in the Panel.
-
-
+
Set $config['password_plesk_host'] to the Hostname / IP where Plesk runs
-
Set your Admin or RPC User: $config['password_plesk_user']
-
Set the Password of the User: $config['password_plesk_pass']
-
Set $config['password_plesk_rpc_port'] for the RPC-Port. Usually its 8443
-
- Set the RPC-Path in $config['password_plesk_rpc_path']. Normally this is: enterprise/control/agent.php;
+ Set the RPC-Path in $config['password_plesk_rpc_path']. Normally this is: enterprise/control/agent.php.
-
3. Driver API
-------------
- Driver file (<driver_name>.php) must define 'password_save' function with
- two arguments. First - current password, second - new password. Function
- should return PASSWORD_SUCCESS on success or any of PASSWORD_CONNECT_ERROR,
+ Driver file (<driver_name>.php) must define rcube_<driver_name>_password class
+ with public save() method that has two arguments. First - current password, second - new password.
+ This method should return PASSWORD_SUCCESS on success or any of PASSWORD_CONNECT_ERROR,
PASSWORD_CRYPT_ERROR, PASSWORD_ERROR when driver was unable to change password.
Extended result (as a hash-array with 'message' and 'code' items) can be returned
too. See existing drivers in drivers/ directory for examples.
diff --git a/plugins/password/config.inc.php.dist b/plugins/password/config.inc.php.dist
index 6610b4dae..94c4368fe 100644
--- a/plugins/password/config.inc.php.dist
+++ b/plugins/password/config.inc.php.dist
@@ -204,10 +204,11 @@ $config['password_ldap_search_filter'] = '(uid=%login)';
// LDAP password hash type
// Standard LDAP encryption type which must be one of: crypt,
-// ext_des, md5crypt, blowfish, md5, sha, smd5, ssha, ad or clear.
+// ext_des, md5crypt, blowfish, md5, sha, smd5, ssha, ad, cram-md5 (dovecot style) or clear.
// Please note that most encodage types require external libraries
// to be included in your PHP installation, see function hashPassword in drivers/ldap.php for more info.
-// Default: 'crypt'
+// Multiple password Values can be generated by concatenating encodings with a +. E.g. 'cram-md5+crypt'
+// Default: 'crypt'.
$config['password_ldap_encodage'] = 'crypt';
// LDAP password attribute
diff --git a/plugins/password/drivers/ldap.php b/plugins/password/drivers/ldap.php
index 340dd29f8..ac2ea3bd3 100644
--- a/plugins/password/drivers/ldap.php
+++ b/plugins/password/drivers/ldap.php
@@ -38,7 +38,8 @@ class rcube_ldap_password
// Building user DN
if ($userDN = $rcmail->config->get('password_ldap_userDN_mask')) {
$userDN = self::substitute_vars($userDN);
- } else {
+ }
+ else {
$userDN = $this->search_userdn($rcmail);
}
@@ -78,13 +79,25 @@ class rcube_ldap_password
return PASSWORD_CONNECT_ERROR;
}
- $crypted_pass = self::hash_password($passwd, $rcmail->config->get('password_ldap_encodage'));
$force = $rcmail->config->get('password_ldap_force_replace');
$pwattr = $rcmail->config->get('password_ldap_pwattr');
$lchattr = $rcmail->config->get('password_ldap_lchattr');
$smbpwattr = $rcmail->config->get('password_ldap_samba_pwattr');
$smblchattr = $rcmail->config->get('password_ldap_samba_lchattr');
$samba = $rcmail->config->get('password_ldap_samba');
+ $encodage = $rcmail->config->get('password_ldap_encodage');
+
+ // Support multiple userPassword values where desired.
+ // multiple encodings can be specified separated by '+' (e.g. "cram-md5+ssha")
+ $encodages = explode('+', $encodage);
+ $crypted_pass = array();
+
+ foreach ($encodages as $enc) {
+ $cpw = self::hash_password($passwd, $enc);
+ if (!empty($cpw)) {
+ $crypted_pass[] = $cpw;
+ }
+ }
// Support password_ldap_samba option for backward compat.
if ($samba && !$smbpwattr) {
@@ -93,7 +106,7 @@ class rcube_ldap_password
}
// Crypt new password
- if (!$crypted_pass) {
+ if (empty($crypted_pass)) {
return PASSWORD_CRYPT_ERROR;
}
@@ -297,6 +310,7 @@ class rcube_ldap_password
}
break;
+
case 'smd5':
mt_srand((double) microtime() * 1000000);
$salt = substr(pack('h*', md5(mt_rand())), 0, 8);
@@ -332,6 +346,11 @@ class rcube_ldap_password
$crypted_password = rcube_charset::convert('"' . $password_clear . '"', RCUBE_CHARSET, 'UTF-16LE');
break;
+ case 'cram-md5':
+ require_once __DIR__ . '/../helpers/dovecot_hmacmd5.php';
+ return dovecot_hmacmd5($password_clear);
+ break;
+
case 'clear':
default:
$crypted_password = $password_clear;
diff --git a/plugins/password/drivers/plesk.php b/plugins/password/drivers/plesk.php
index d0a511fbe..db9ad9efd 100644
--- a/plugins/password/drivers/plesk.php
+++ b/plugins/password/drivers/plesk.php
@@ -49,7 +49,7 @@ class rcube_plesk_password
* @param string $newpass New password
* @returns int PASSWORD_SUCCESS|PASSWORD_ERROR
*/
- function save($currpass, $newpass)\
+ function save($currpass, $newpass)
{
// get config
$rcmail = rcmail::get_instance();
diff --git a/plugins/password/helpers/dovecot_hmacmd5.php b/plugins/password/helpers/dovecot_hmacmd5.php
new file mode 100644
index 000000000..644b5377e
--- /dev/null
+++ b/plugins/password/helpers/dovecot_hmacmd5.php
@@ -0,0 +1,191 @@
+<?php
+
+/**
+ *
+ * dovecot_hmacmd5.php V1.01
+ *
+ * Generates HMAC-MD5 'contexts' for Dovecot's password files.
+ *
+ * (C) 2008 Hajo Noerenberg
+ *
+ * http://www.noerenberg.de/hajo/pub/dovecot_hmacmd5.php.txt
+ *
+ * Most of the code has been shamelessly stolen from various sources:
+ *
+ * (C) Paul Johnston 1999 - 2000 / http://pajhome.org.uk/crypt/md5/
+ * (C) William K. Cole 2008 / http://www.scconsult.com/bill/crampass.pl
+ * (C) Borfast 2002 / http://www.zend.com/code/codex.php?ozid=962&single=1
+ * (C) Thomas Weber / http://pajhome.org.uk/crypt/md5/contrib/md5.java.txt
+ *
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3.0 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/gpl-3.0.txt>.
+ *
+ */
+
+/* Convert a 32-bit number to a hex string with ls-byte first
+ */
+
+function rhex($n) {
+ $hex_chr = "0123456789abcdef"; $r = '';
+ for($j = 0; $j <= 3; $j++)
+ $r .= $hex_chr[($n >> ($j * 8 + 4)) & 0x0F] . $hex_chr[($n >> ($j * 8)) & 0x0F];
+ return $r;
+}
+
+/* zeroFill() is needed because PHP doesn't have a zero-fill
+ * right shift operator like JavaScript's >>>
+ */
+
+function zeroFill($a, $b) {
+ $z = hexdec(80000000);
+ if ($z & $a) {
+ $a >>= 1;
+ $a &= (~$z);
+ $a |= 0x40000000;
+ $a >>= ($b-1);
+ } else {
+ $a >>= $b;
+ }
+ return $a;
+}
+
+/* Bitwise rotate a 32-bit number to the left
+ */
+
+function bit_rol($num, $cnt) {
+ return ($num << $cnt) | (zeroFill($num, (32 - $cnt)));
+}
+
+/* Add integers, wrapping at 2^32
+ */
+
+function safe_add($x, $y) {
+ return (($x&0x7FFFFFFF) + ($y&0x7FFFFFFF)) ^ ($x&0x80000000) ^ ($y&0x80000000);
+}
+
+/* These functions implement the four basic operations the algorithm uses.
+ */
+
+function md5_cmn($q, $a, $b, $x, $s, $t) {
+ return safe_add(bit_rol(safe_add(safe_add($a, $q), safe_add($x, $t)), $s), $b);
+}
+function md5_ff($a, $b, $c, $d, $x, $s, $t) {
+ return md5_cmn(($b & $c) | ((~$b) & $d), $a, $b, $x, $s, $t);
+}
+function md5_gg($a, $b, $c, $d, $x, $s, $t) {
+ return md5_cmn(($b & $d) | ($c & (~$d)), $a, $b, $x, $s, $t);
+}
+function md5_hh($a, $b, $c, $d, $x, $s, $t) {
+ return md5_cmn($b ^ $c ^ $d, $a, $b, $x, $s, $t);
+}
+function md5_ii($a, $b, $c, $d, $x, $s, $t) {
+ return md5_cmn($c ^ ($b | (~$d)), $a, $b, $x, $s, $t);
+}
+
+/* Calculate the first round of the MD5 algorithm
+ */
+
+function md5_oneround($s, $io) {
+
+ $s = str_pad($s, 64, chr(0x00));
+
+ $x = array_fill(0, 16, 0);
+
+ for($i = 0; $i < 64; $i++)
+ $x[$i >> 2] |= (($io ? 0x36 : 0x5c) ^ ord($s[$i])) << (($i % 4) * 8);
+
+ $a = $olda = 1732584193;
+ $b = $oldb = -271733879;
+ $c = $oldc = -1732584194;
+ $d = $oldd = 271733878;
+
+ $a = md5_ff($a, $b, $c, $d, $x[ 0], 7 , -680876936);
+ $d = md5_ff($d, $a, $b, $c, $x[ 1], 12, -389564586);
+ $c = md5_ff($c, $d, $a, $b, $x[ 2], 17, 606105819);
+ $b = md5_ff($b, $c, $d, $a, $x[ 3], 22, -1044525330);
+ $a = md5_ff($a, $b, $c, $d, $x[ 4], 7 , -176418897);
+ $d = md5_ff($d, $a, $b, $c, $x[ 5], 12, 1200080426);
+ $c = md5_ff($c, $d, $a, $b, $x[ 6], 17, -1473231341);
+ $b = md5_ff($b, $c, $d, $a, $x[ 7], 22, -45705983);
+ $a = md5_ff($a, $b, $c, $d, $x[ 8], 7 , 1770035416);
+ $d = md5_ff($d, $a, $b, $c, $x[ 9], 12, -1958414417);
+ $c = md5_ff($c, $d, $a, $b, $x[10], 17, -42063);
+ $b = md5_ff($b, $c, $d, $a, $x[11], 22, -1990404162);
+ $a = md5_ff($a, $b, $c, $d, $x[12], 7 , 1804603682);
+ $d = md5_ff($d, $a, $b, $c, $x[13], 12, -40341101);
+ $c = md5_ff($c, $d, $a, $b, $x[14], 17, -1502002290);
+ $b = md5_ff($b, $c, $d, $a, $x[15], 22, 1236535329);
+
+ $a = md5_gg($a, $b, $c, $d, $x[ 1], 5 , -165796510);
+ $d = md5_gg($d, $a, $b, $c, $x[ 6], 9 , -1069501632);
+ $c = md5_gg($c, $d, $a, $b, $x[11], 14, 643717713);
+ $b = md5_gg($b, $c, $d, $a, $x[ 0], 20, -373897302);
+ $a = md5_gg($a, $b, $c, $d, $x[ 5], 5 , -701558691);
+ $d = md5_gg($d, $a, $b, $c, $x[10], 9 , 38016083);
+ $c = md5_gg($c, $d, $a, $b, $x[15], 14, -660478335);
+ $b = md5_gg($b, $c, $d, $a, $x[ 4], 20, -405537848);
+ $a = md5_gg($a, $b, $c, $d, $x[ 9], 5 , 568446438);
+ $d = md5_gg($d, $a, $b, $c, $x[14], 9 , -1019803690);
+ $c = md5_gg($c, $d, $a, $b, $x[ 3], 14, -187363961);
+ $b = md5_gg($b, $c, $d, $a, $x[ 8], 20, 1163531501);
+ $a = md5_gg($a, $b, $c, $d, $x[13], 5 , -1444681467);
+ $d = md5_gg($d, $a, $b, $c, $x[ 2], 9 , -51403784);
+ $c = md5_gg($c, $d, $a, $b, $x[ 7], 14, 1735328473);
+ $b = md5_gg($b, $c, $d, $a, $x[12], 20, -1926607734);
+
+ $a = md5_hh($a, $b, $c, $d, $x[ 5], 4 , -378558);
+ $d = md5_hh($d, $a, $b, $c, $x[ 8], 11, -2022574463);
+ $c = md5_hh($c, $d, $a, $b, $x[11], 16, 1839030562);
+ $b = md5_hh($b, $c, $d, $a, $x[14], 23, -35309556);
+ $a = md5_hh($a, $b, $c, $d, $x[ 1], 4 , -1530992060);
+ $d = md5_hh($d, $a, $b, $c, $x[ 4], 11, 1272893353);
+ $c = md5_hh($c, $d, $a, $b, $x[ 7], 16, -155497632);
+ $b = md5_hh($b, $c, $d, $a, $x[10], 23, -1094730640);
+ $a = md5_hh($a, $b, $c, $d, $x[13], 4 , 681279174);
+ $d = md5_hh($d, $a, $b, $c, $x[ 0], 11, -358537222);
+ $c = md5_hh($c, $d, $a, $b, $x[ 3], 16, -722521979);
+ $b = md5_hh($b, $c, $d, $a, $x[ 6], 23, 76029189);
+ $a = md5_hh($a, $b, $c, $d, $x[ 9], 4 , -640364487);
+ $d = md5_hh($d, $a, $b, $c, $x[12], 11, -421815835);
+ $c = md5_hh($c, $d, $a, $b, $x[15], 16, 530742520);
+ $b = md5_hh($b, $c, $d, $a, $x[ 2], 23, -995338651);
+
+ $a = md5_ii($a, $b, $c, $d, $x[ 0], 6 , -198630844);
+ $d = md5_ii($d, $a, $b, $c, $x[ 7], 10, 1126891415);
+ $c = md5_ii($c, $d, $a, $b, $x[14], 15, -1416354905);
+ $b = md5_ii($b, $c, $d, $a, $x[ 5], 21, -57434055);
+ $a = md5_ii($a, $b, $c, $d, $x[12], 6 , 1700485571);
+ $d = md5_ii($d, $a, $b, $c, $x[ 3], 10, -1894986606);
+ $c = md5_ii($c, $d, $a, $b, $x[10], 15, -1051523);
+ $b = md5_ii($b, $c, $d, $a, $x[ 1], 21, -2054922799);
+ $a = md5_ii($a, $b, $c, $d, $x[ 8], 6 , 1873313359);
+ $d = md5_ii($d, $a, $b, $c, $x[15], 10, -30611744);
+ $c = md5_ii($c, $d, $a, $b, $x[ 6], 15, -1560198380);
+ $b = md5_ii($b, $c, $d, $a, $x[13], 21, 1309151649);
+ $a = md5_ii($a, $b, $c, $d, $x[ 4], 6 , -145523070);
+ $d = md5_ii($d, $a, $b, $c, $x[11], 10, -1120210379);
+ $c = md5_ii($c, $d, $a, $b, $x[ 2], 15, 718787259);
+ $b = md5_ii($b, $c, $d, $a, $x[ 9], 21, -343485551);
+
+ $a = safe_add($a, $olda);
+ $b = safe_add($b, $oldb);
+ $c = safe_add($c, $oldc);
+ $d = safe_add($d, $oldd);
+
+ return rhex($a) . rhex($b) . rhex($c) . rhex($d);
+}
+
+function dovecot_hmacmd5 ($s) {
+ if (strlen($s) > 64) $s=pack("H*", md5($s));
+ return "{CRAM-MD5}" . md5_oneround($s, 0) . md5_oneround($s, 1);
+}
diff --git a/plugins/vcard_attachments/vcard_attachments.php b/plugins/vcard_attachments/vcard_attachments.php
index cf7e22d3a..74718be6f 100644
--- a/plugins/vcard_attachments/vcard_attachments.php
+++ b/plugins/vcard_attachments/vcard_attachments.php
@@ -65,7 +65,7 @@ class vcard_attachments extends rcube_plugin
$attach_script = false;
foreach ($this->vcard_parts as $part) {
- $vcards = rcube_vcard::import($this->message->get_part_content($part, null, true));
+ $vcards = rcube_vcard::import($this->message->get_part_body($part, true));
// successfully parsed vcards?
if (empty($vcards)) {
diff --git a/plugins/zipdownload/zipdownload.php b/plugins/zipdownload/zipdownload.php
index edb8188cc..2e103ceb0 100644
--- a/plugins/zipdownload/zipdownload.php
+++ b/plugins/zipdownload/zipdownload.php
@@ -144,20 +144,14 @@ class zipdownload extends rcube_plugin
}
}
- $disp_name = $this->_convert_filename($filename);
+ $disp_name = $this->_convert_filename($filename);
+ $tmpfn = tempnam($temp_dir, 'zipattach');
+ $tmpfp = fopen($tmpfn, 'w');
+ $tempfiles[] = $tmpfn;
- if ($part->body) {
- $orig_message_raw = $part->body;
- $zip->addFromString($disp_name, $orig_message_raw);
- }
- else {
- $tmpfn = tempnam($temp_dir, 'zipattach');
- $tmpfp = fopen($tmpfn, 'w');
- $imap->get_message_part($message->uid, $part->mime_id, $part, null, $tmpfp, true);
- $tempfiles[] = $tmpfn;
- fclose($tmpfp);
- $zip->addFile($tmpfn, $disp_name);
- }
+ $message->get_part_body($part->mime_id, false, 0, $tmpfp);
+ $zip->addFile($tmpfn, $disp_name);
+ fclose($tmpfp);
}
$zip->close();
diff --git a/program/include/rcmail.php b/program/include/rcmail.php
index 221b50afe..8ea42b600 100644
--- a/program/include/rcmail.php
+++ b/program/include/rcmail.php
@@ -106,14 +106,14 @@ class rcmail extends rcube
// reset some session parameters when changing task
if ($this->task != 'utils') {
// we reset list page when switching to another task
- // but only to the main task interface - empty action (#1489076)
+ // but only to the main task interface - empty action (#1489076, #1490116)
// this will prevent from unintentional page reset on cross-task requests
if ($this->session && $_SESSION['task'] != $this->task && empty($this->action)) {
$this->session->remove('page');
- }
- // set current task to session
- $_SESSION['task'] = $this->task;
+ // set current task to session
+ $_SESSION['task'] = $this->task;
+ }
}
// init output class (not in CLI mode)
diff --git a/program/js/list.js b/program/js/list.js
index d1dbbcb6c..dbc14d6b8 100644
--- a/program/js/list.js
+++ b/program/js/list.js
@@ -150,9 +150,9 @@ init_row: function(row)
var self = this, uid = row.uid;
this.rows[uid] = {uid:uid, id:row.id, obj:row};
- // set eventhandlers to table row
+ // set eventhandlers to table row (only left-button-clicks in mouseup)
row.onmousedown = function(e){ return self.drag_row(e, this.uid); };
- row.onmouseup = function(e){ return self.click_row(e, this.uid); };
+ row.onmouseup = function(e){ if (e.which == 1) return self.click_row(e, this.uid); };
if (bw.touch) {
row.addEventListener('touchstart', function(e) {
diff --git a/program/lib/Roundcube/rcube.php b/program/lib/Roundcube/rcube.php
index 3ab650cb1..03f49637c 100644
--- a/program/lib/Roundcube/rcube.php
+++ b/program/lib/Roundcube/rcube.php
@@ -842,6 +842,7 @@ class rcube
* upon decryption; see http://php.net/mcrypt_generic#68082
*/
$clear = pack("a*H2", $clear, "80");
+ $ckey = $this->config->get_crypto_key($key);
if (function_exists('openssl_encrypt')) {
$method = 'DES-EDE3-CBC';
@@ -853,7 +854,7 @@ class rcube
($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, ""))
) {
$iv = $this->create_iv(mcrypt_enc_get_iv_size($td));
- mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv);
+ mcrypt_generic_init($td, $ckey, $iv);
$cipher = $iv . mcrypt_generic($td, $clear);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
@@ -864,7 +865,7 @@ class rcube
if (function_exists('des')) {
$des_iv_size = 8;
$iv = $this->create_iv($des_iv_size);
- $cipher = $iv . des($this->config->get_crypto_key($key), $clear, 1, 1, $iv);
+ $cipher = $iv . des($ckey, $clear, 1, 1, $iv);
}
else {
self::raise_error(array(
@@ -895,6 +896,7 @@ class rcube
}
$cipher = $base64 ? base64_decode($cipher) : $cipher;
+ $ckey = $this->config->get_crypto_key($key);
if (function_exists('openssl_decrypt')) {
$method = 'DES-EDE3-CBC';
@@ -914,7 +916,7 @@ class rcube
($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, ""))
) {
$iv_size = mcrypt_enc_get_iv_size($td);
- $iv = substr($cipher, 0, $iv_size);
+ $iv = substr($cipher, 0, $iv_size);
// session corruption? (#1485970)
if (strlen($iv) < $iv_size) {
@@ -922,7 +924,7 @@ class rcube
}
$cipher = substr($cipher, $iv_size);
- mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv);
+ mcrypt_generic_init($td, $ckey, $iv);
$clear = mdecrypt_generic($td, $cipher);
mcrypt_generic_deinit($td);
mcrypt_module_close($td);
@@ -932,15 +934,15 @@ class rcube
if (function_exists('des')) {
$des_iv_size = 8;
- $iv = substr($cipher, 0, $des_iv_size);
+ $iv = substr($cipher, 0, $des_iv_size);
$cipher = substr($cipher, $des_iv_size);
- $clear = des($this->config->get_crypto_key($key), $cipher, 0, 1, $iv);
+ $clear = des($ckey, $cipher, 0, 1, $iv);
}
else {
self::raise_error(array(
'code' => 500, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
- 'message' => "Could not perform decryption; make sure Mcrypt is installed or lib/des.inc is available"
+ 'message' => "Could not perform decryption; make sure OpenSSL or Mcrypt or lib/des.inc is available"
), true, true);
}
}
diff --git a/program/lib/Roundcube/rcube_image.php b/program/lib/Roundcube/rcube_image.php
index d0d0c7437..77ce1647d 100644
--- a/program/lib/Roundcube/rcube_image.php
+++ b/program/lib/Roundcube/rcube_image.php
@@ -229,7 +229,7 @@ class rcube_image
$image = $new_image;
// fix rotation of image if EXIF data exists and specifies rotation (GD strips the EXIF data)
- if ($this->image_file && function_exists('exif_read_data')) {
+ if ($this->image_file && $type == 'jpg' && function_exists('exif_read_data')) {
$exif = exif_read_data($this->image_file);
if ($exif && $exif['Orientation']) {
switch ($exif['Orientation']) {
diff --git a/program/lib/Roundcube/rcube_imap_cache.php b/program/lib/Roundcube/rcube_imap_cache.php
index 95498e364..81df07641 100644
--- a/program/lib/Roundcube/rcube_imap_cache.php
+++ b/program/lib/Roundcube/rcube_imap_cache.php
@@ -1245,13 +1245,15 @@ class rcube_imap_cache
private function message_object_prepare(&$msg, &$size = 0)
{
// Remove body too big
- if ($msg->body && ($length = strlen($msg->body))) {
- $size += $length;
+ if (isset($msg->body)) {
+ $length = strlen($msg->body);
- if ($size > $this->threshold * 1024) {
- $size -= $length;
+ if ($msg->body_modified || $size + $length > $this->threshold * 1024) {
unset($msg->body);
}
+ else {
+ $size += $length;
+ }
}
// Fix mimetype which might be broken by some code when message is displayed
diff --git a/program/lib/Roundcube/rcube_imap_generic.php b/program/lib/Roundcube/rcube_imap_generic.php
index 734a9311e..d78b526dd 100644
--- a/program/lib/Roundcube/rcube_imap_generic.php
+++ b/program/lib/Roundcube/rcube_imap_generic.php
@@ -2569,52 +2569,63 @@ class rcube_imap_generic
return false;
}
- switch ($encoding) {
- case 'base64':
- $mode = 1;
- break;
- case 'quoted-printable':
- $mode = 2;
- break;
- case 'x-uuencode':
- case 'x-uue':
- case 'uue':
- case 'uuencode':
- $mode = 3;
- break;
- default:
- $mode = 0;
- }
-
- // Use BINARY extension when possible (and safe)
- $binary = $mode && preg_match('/^[0-9.]+$/', $part) && $this->hasCapability('BINARY');
- $fetch_mode = $binary ? 'BINARY' : 'BODY';
- $partial = $max_bytes ? sprintf('<0.%d>', $max_bytes) : '';
+ $binary = true;
- // format request
- $key = $this->nextTag();
- $request = $key . ($is_uid ? ' UID' : '') . " FETCH $id ($fetch_mode.PEEK[$part]$partial)";
- $result = false;
- $found = false;
+ do {
+ if (!$initiated) {
+ switch ($encoding) {
+ case 'base64':
+ $mode = 1;
+ break;
+ case 'quoted-printable':
+ $mode = 2;
+ break;
+ case 'x-uuencode':
+ case 'x-uue':
+ case 'uue':
+ case 'uuencode':
+ $mode = 3;
+ break;
+ default:
+ $mode = 0;
+ }
- // send request
- if (!$this->putLine($request)) {
- $this->setError(self::ERROR_COMMAND, "Unable to send command: $request");
- return false;
- }
+ // Use BINARY extension when possible (and safe)
+ $binary = $binary && $mode && preg_match('/^[0-9.]+$/', $part) && $this->hasCapability('BINARY');
+ $fetch_mode = $binary ? 'BINARY' : 'BODY';
+ $partial = $max_bytes ? sprintf('<0.%d>', $max_bytes) : '';
+
+ // format request
+ $key = $this->nextTag();
+ $request = $key . ($is_uid ? ' UID' : '') . " FETCH $id ($fetch_mode.PEEK[$part]$partial)";
+ $result = false;
+ $found = false;
+ $initiated = true;
+
+ // send request
+ if (!$this->putLine($request)) {
+ $this->setError(self::ERROR_COMMAND, "Unable to send command: $request");
+ return false;
+ }
- if ($binary) {
- // WARNING: Use $formatted argument with care, this may break binary data stream
- $mode = -1;
- }
+ if ($binary) {
+ // WARNING: Use $formatted argument with care, this may break binary data stream
+ $mode = -1;
+ }
+ }
- do {
$line = trim($this->readLine(1024));
if (!$line) {
break;
}
+ // handle UNKNOWN-CTE response - RFC 3516, try again with standard BODY request
+ if ($binary && !$found && preg_match('/^' . $key . ' NO \[UNKNOWN-CTE\]/i', $line)) {
+ $binary = $initiated = false;
+ continue;
+ }
+
// skip irrelevant untagged responses (we have a result already)
if ($found || !preg_match('/^\* ([0-9]+) FETCH (.*)$/', $line, $m)) {
continue;
@@ -2675,7 +2686,7 @@ class rcube_imap_generic
// BASE64
if ($mode == 1) {
- $line = rtrim($line, "\t\r\n\0\x0B");
+ $line = preg_replace('|[^a-zA-Z0-9+=/]|', '', $line);
// create chunks with proper length for base64 decoding
$line = $prev.$line;
$length = strlen($line);
@@ -2720,7 +2731,7 @@ class rcube_imap_generic
}
}
}
- } while (!$this->startsWith($line, $key, true));
+ } while (!$this->startsWith($line, $key, true) || !$initiated);
if ($result !== false) {
if ($file) {
diff --git a/program/lib/Roundcube/rcube_message.php b/program/lib/Roundcube/rcube_message.php
index a648ae722..0d33ba411 100644
--- a/program/lib/Roundcube/rcube_message.php
+++ b/program/lib/Roundcube/rcube_message.php
@@ -3,7 +3,7 @@
/*
+-----------------------------------------------------------------------+
| This file is part of the Roundcube Webmail client |
- | Copyright (C) 2008-2010, The Roundcube Dev Team |
+ | Copyright (C) 2008-2014, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
@@ -61,6 +61,8 @@ class rcube_message
public $sender = null;
public $is_safe = false;
+ const BODY_MAX_SIZE = 1048576; // 1MB
+
/**
* __construct
@@ -176,6 +178,7 @@ class rcube_message
* @param boolean $formatted Enables formatting of text/* parts bodies
*
* @return string Part content
+ * @deprecated
*/
public function get_part_content($mime_id, $fp = null, $skip_charset_conv = false, $max_bytes = 0, $formatted = true)
{
@@ -198,6 +201,125 @@ class rcube_message
/**
+ * Get content of a specific part of this message
+ *
+ * @param string $mime_id Part ID
+ * @param boolean $formatted Enables formatting of text/* parts bodies
+ * @param int $max_bytes Only return/read this number of bytes
+ * @param mixed $mode NULL to return a string, -1 to print body
+ * or file pointer to save the body into
+ *
+ * @return string|bool Part content or operation status
+ */
+ public function get_part_body($mime_id, $formatted = false, $max_bytes = 0, $mode = null)
+ {
+ if (!($part = $this->mime_parts[$mime_id])) {
+ return;
+ }
+
+ // only text parts can be formatted
+ $formatted = $formatted && $part->ctype_primary == 'text';
+
+ // part body not fetched yet... save in memory if it's small enough
+ if ($part->body === null && is_numeric($mime_id) && $part->size < self::BODY_MAX_SIZE) {
+ // Warning: body here should be always unformatted
+ $part->body = $this->storage->get_message_part($this->uid, $mime_id, $part,
+ null, null, true, 0, false);
+ }
+
+ // body stored in message structure (winmail/inline-uuencode)
+ if ($part->body !== null || $part->encoding == 'stream') {
+ $body = $part->body;
+
+ if ($formatted && $body) {
+ $body = self::format_part_body($body, $part, $this->headers->charset);
+ }
+
+ if ($max_bytes && strlen($body) > $max_bytes) {
+ $body = substr($body, 0, $max_bytes);
+ }
+
+ if (is_resource($mode)) {
+ if ($body !== false) {
+ fwrite($mode, $body);
+ rewind($mode);
+ }
+
+ return $body !== false;
+ }
+
+ if ($mode === -1) {
+ if ($body !== false) {
+ print($body);
+ }
+
+ return $body !== false;
+ }
+
+ return $body;
+ }
+
+ // get the body from IMAP
+ $this->storage->set_folder($this->folder);
+
+ $body = $this->storage->get_message_part($this->uid, $mime_id, $part,
+ $mode === -1, is_resource($mode) ? $mode : null, !$formatted, $max_bytes, $formatted);
+
+ if (!$mode && $body && $formatted) {
+ $body = self::format_part_body($body, $part, $this->headers->charset);
+ }
+
+ if (is_resource($mode)) {
+ rewind($mode);
+ return $body !== false;
+ }
+
+ return $body;
+ }
+
+
+ /**
+ * Format text message part for display
+ *
+ * @param string $body Part body
+ * @param rcube_message_part $part Part object
+ * @param string $default_charset Fallback charset if part charset is not specified
+ *
+ * @return string Formatted body
+ */
+ public static function format_part_body($body, $part, $default_charset = null)
+ {
+ // remove useless characters
+ $body = preg_replace('/[\t\r\0\x0B]+\n/', "\n", $body);
+
+ // remove NULL characters if any (#1486189)
+ if (strpos($body, "\x00") !== false) {
+ $body = str_replace("\x00", '', $body);
+ }
+
+ // detect charset...
+ if (!$part->charset || strtoupper($part->charset) == 'US-ASCII') {
+ // try to extract charset information from HTML meta tag (#1488125)
+ if ($part->ctype_secondary == 'html' && preg_match('/<meta[^>]+charset=([a-z0-9-_]+)/i', $body, $m)) {
+ $part->charset = strtoupper($m[1]);
+ }
+ else if ($default_charset) {
+ $part->charset = $default_charset;
+ }
+ else {
+ $rcube = rcube::get_instance();
+ $part->charset = $rcube->config->get('default_charset', RCUBE_CHARSET);
+ }
+ }
+
+ // ..convert charset encoding
+ $body = rcube_charset::convert($body, $part->charset);
+
+ return $body;
+ }
+
+
+ /**
* Determine if the message contains a HTML part. This must to be
* a real part not an attachment (or its part)
*
@@ -293,7 +415,7 @@ class rcube_message
// check all message parts
foreach ($this->mime_parts as $pid => $part) {
if ($part->mimetype == 'text/html') {
- return $this->get_part_content($pid);
+ return $this->get_part_body($pid, true);
}
}
}
@@ -314,10 +436,10 @@ class rcube_message
// check all message parts
foreach ($this->mime_parts as $mime_id => $part) {
if ($part->mimetype == 'text/plain') {
- return $this->get_part_content($mime_id);
+ return $this->get_part_body($mime_id, true);
}
else if ($part->mimetype == 'text/html') {
- $out = $this->get_part_content($mime_id);
+ $out = $this->get_part_body($mime_id, true);
// create instance of html2text class
$txt = new rcube_html2text($out);
@@ -371,7 +493,7 @@ class rcube_message
// parse headers from message/rfc822 part
if (!isset($structure->headers['subject']) && !isset($structure->headers['from'])) {
- list($headers, ) = explode("\r\n\r\n", $this->get_part_content($structure->mime_id, null, true, 32768));
+ list($headers, ) = explode("\r\n\r\n", $this->get_part_body($structure->mime_id, false, 32768));
$structure->headers = rcube_mime::parse_headers($headers);
}
}
@@ -725,20 +847,18 @@ class rcube_message
*/
function tnef_decode(&$part)
{
- // @TODO: attachment may be huge, hadle it via file
- if (!isset($part->body)) {
- $this->storage->set_folder($this->folder);
- $part->body = $this->storage->get_message_part($this->uid, $part->mime_id, $part);
- }
+ // @TODO: attachment may be huge, handle body via file
+ $body = $this->get_part_body($part->mime_id);
+ $tnef = new tnef_decoder;
+ $tnef_arr = $tnef->decompress($body);
+ $parts = array();
- $parts = array();
- $tnef = new tnef_decoder;
- $tnef_arr = $tnef->decompress($part->body);
+ unset($body);
foreach ($tnef_arr as $pid => $winatt) {
$tpart = new rcube_message_part;
- $tpart->filename = trim($winatt['name']);
+ $tpart->filename = $this->fix_attachment_name(trim($winatt['name']), $part);
$tpart->encoding = 'stream';
$tpart->ctype_primary = trim(strtolower($winatt['type']));
$tpart->ctype_secondary = trim(strtolower($winatt['subtype']));
@@ -763,55 +883,109 @@ class rcube_message
*/
function uu_decode(&$part)
{
- // @TODO: messages may be huge, hadle body via file
- if (!isset($part->body)) {
- $this->storage->set_folder($this->folder);
- $part->body = $this->storage->get_message_part($this->uid, $part->mime_id, $part);
- }
+ // @TODO: messages may be huge, handle body via file
+ $part->body = $this->get_part_body($part->mime_id);
+ $parts = array();
+ $pid = 0;
- $parts = array();
// FIXME: line length is max.65?
- $uu_regexp = '/begin [0-7]{3,4} ([^\n]+)\n/s';
+ $uu_regexp_begin = '/begin [0-7]{3,4} ([^\r\n]+)\r?\n/s';
+ $uu_regexp_end = '/`\r?\nend((\r?\n)|($))/s';
- if (preg_match_all($uu_regexp, $part->body, $matches, PREG_SET_ORDER)) {
- // update message content-type
- $part->ctype_primary = 'multipart';
- $part->ctype_secondary = 'mixed';
- $part->mimetype = $part->ctype_primary . '/' . $part->ctype_secondary;
- $uu_endstring = "`\nend\n";
+ while (preg_match($uu_regexp_begin, $part->body, $matches, PREG_OFFSET_CAPTURE)) {
+ $startpos = $matches[0][1];
+
+ if (!preg_match($uu_regexp_end, $part->body, $m, PREG_OFFSET_CAPTURE, $startpos)) {
+ break;
+ }
+
+ $endpos = $m[0][1];
+ $begin_len = strlen($matches[0][0]);
+ $end_len = strlen($m[0][0]);
+
+ // extract attachment body
+ $filebody = substr($part->body, $startpos + $begin_len, $endpos - $startpos - $begin_len - 1);
+ $filebody = str_replace("\r\n", "\n", $filebody);
+
+ // remove attachment body from the message body
+ $part->body = substr_replace($part->body, '', $startpos, $endpos + $end_len - $startpos);
+ // mark body as modified so it will not be cached by rcube_imap_cache
+ $part->body_modified = true;
// add attachments to the structure
- foreach ($matches as $pid => $att) {
- $startpos = strpos($part->body, $att[1]) + strlen($att[1]) + 1; // "\n"
- $endpos = strpos($part->body, $uu_endstring);
- $filebody = substr($part->body, $startpos, $endpos-$startpos);
+ $uupart = new rcube_message_part;
+ $uupart->filename = trim($matches[1][0]);
+ $uupart->encoding = 'stream';
+ $uupart->body = convert_uudecode($filebody);
+ $uupart->size = strlen($uupart->body);
+ $uupart->mime_id = 'uu.' . $part->mime_id . '.' . $pid;
+
+ $ctype = rcube_mime::file_content_type($uupart->body, $uupart->filename, 'application/octet-stream', true);
+ $uupart->mimetype = $ctype;
+ list($uupart->ctype_primary, $uupart->ctype_secondary) = explode('/', $ctype);
+
+ $parts[] = $uupart;
+ $pid++;
+ }
- // remove attachments bodies from the message body
- $part->body = substr_replace($part->body, "", $startpos, $endpos+strlen($uu_endstring)-$startpos);
+ return $parts;
+ }
- $uupart = new rcube_message_part;
+ /**
+ * Fix attachment name encoding if needed/possible
+ */
+ protected function fix_attachment_name($name, $part)
+ {
+ if ($name == rcube_charset::clean($name)) {
+ return $name;
+ }
- $uupart->filename = trim($att[1]);
- $uupart->encoding = 'stream';
- $uupart->body = convert_uudecode($filebody);
- $uupart->size = strlen($uupart->body);
- $uupart->mime_id = 'uu.' . $part->mime_id . '.' . $pid;
+ // find charset from part or its parent(s)
+ if ($part->charset) {
+ $charsets[] = $part->charset;
+ }
+ else {
+ // check first part (common case)
+ $n = strpos($part->mime_id, '.') ? preg_replace('/\.[0-9]+$/', '', $part->mime_id) . '.1' : 1;
+ if (($_part = $this->mime_parts[$n]) && $_part->charset) {
+ $charsets[] = $_part->charset;
+ }
- $ctype = rcube_mime::file_content_type($uupart->body, $uupart->filename, 'application/octet-stream', true);
- $uupart->mimetype = $ctype;
- list($uupart->ctype_primary, $uupart->ctype_secondary) = explode('/', $ctype);
+ // check parents' charset
+ $items = explode('.', $part->mime_id);
+ for ($i = count($items)-1; $i > 0; $i--) {
+ $last = array_pop($items);
+ $parent = $this->mime_parts[join('.', $items)];
- $parts[] = $uupart;
- unset($matches[$pid]);
+ if ($parent && $parent->charset) {
+ $charsets[] = $parent->charset;
+ }
}
+ }
- // remove attachments bodies from the message body
- $part->body = preg_replace($uu_regexp, '', $part->body);
+ if ($this->headers->charset) {
+ $charsets[] = $this->headers->charset;
}
- return $parts;
- }
+ if (empty($charsets)) {
+ $rcube = rcube::get_instance();
+ $charsets[] = rcube_charset::detect($name, $rcube->config->get('default_charset', RCUBE_CHARSET));
+ }
+
+ foreach (array_unique($charsets) as $charset) {
+ $_name = rcube_charset::convert($name, $charset);
+
+ if ($_name == rcube_charset::clean($_name)) {
+ if (!$part->charset) {
+ $part->charset = $charset;
+ }
+ return $_name;
+ }
+ }
+
+ return $name;
+ }
/**
* Deprecated methods (to be removed)
diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc
index 1770a1bcb..9b6d0dcc9 100644
--- a/program/steps/mail/compose.inc
+++ b/program/steps/mail/compose.inc
@@ -802,22 +802,14 @@ function rcmail_compose_part_body($part, $isHtml = false)
return '';
}
- if (empty($part->ctype_parameters) || empty($part->ctype_parameters['charset'])) {
- $part->ctype_parameters['charset'] = $MESSAGE->headers->charset;
- }
-
// fetch part if not available
- if (!isset($part->body)) {
- $part->body = $MESSAGE->get_part_content($part->mime_id);
- }
+ $body = $MESSAGE->get_part_body($part->mime_id, true);
// message is cached but not exists (#1485443), or other error
- if ($part->body === false) {
+ if ($body === false) {
return '';
}
- $body = $part->body;
-
if ($isHtml) {
if ($part->ctype_secondary == 'html') {
}
@@ -1363,7 +1355,7 @@ function rcmail_save_attachment(&$message, $pid)
$path = tempnam($temp_dir, 'rcmAttmnt');
if ($fp = fopen($path, 'w')) {
- $message->get_part_content($pid, $fp, true, 0, false);
+ $message->get_part_body($pid, false, 0, $fp);
fclose($fp);
}
else {
@@ -1371,7 +1363,7 @@ function rcmail_save_attachment(&$message, $pid)
}
}
else {
- $data = $message->get_part_content($pid, null, true, 0, false);
+ $data = $message->get_part_body($pid);
}
$mimetype = $part->ctype_primary . '/' . $part->ctype_secondary;
@@ -1385,6 +1377,7 @@ function rcmail_save_attachment(&$message, $pid)
'data' => $data,
'path' => $path,
'size' => $path ? filesize($path) : strlen($data),
+ 'charset' => $part->charset,
);
$attachment = $rcmail->plugins->exec_hook('attachment_save', $attachment);
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index cbeeb05fb..8dced9b18 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -864,17 +864,19 @@ function rcmail_wash_html($html, $p, $cid_replaces)
* Convert the given message part to proper HTML
* which can be displayed the message view
*
- * @param object rcube_message_part Message part
- * @param array Display parameters array
+ * @param string Message part body
+ * @param rcube_message_part Message part
+ * @param array Display parameters array
+ *
* @return string Formatted HTML string
*/
-function rcmail_print_body($part, $p = array())
+function rcmail_print_body($body, $part, $p = array())
{
global $RCMAIL;
// trigger plugin hook
$data = $RCMAIL->plugins->exec_hook('message_part_before',
- array('type' => $part->ctype_secondary, 'body' => $part->body, 'id' => $part->mime_id)
+ array('type' => $part->ctype_secondary, 'body' => $body, 'id' => $part->mime_id)
+ $p + array('safe' => false, 'plain' => false, 'inline_html' => true));
// convert html to text/plain
@@ -900,7 +902,7 @@ function rcmail_print_body($part, $p = array())
}
else {
// assert plaintext
- $body = $part->body;
+ $body = $data['body'];
$part->ctype_secondary = $data['type'] = 'plain';
}
@@ -1072,8 +1074,10 @@ function rcmail_message_headers($attrib, $headers=null)
}
else if ($hkey == 'subject' && empty($value))
$header_value = $RCMAIL->gettext('nosubject');
- else
+ else {
+ $value = is_array($value) ? implode(' ', $value) : $value;
$header_value = trim(rcube_mime::decode_header($value, $headers['charset']));
+ }
$output_headers[$hkey] = array(
'title' => $header_title,
@@ -1204,18 +1208,12 @@ function rcmail_message_body($attrib)
continue;
}
- if (empty($part->ctype_parameters) || empty($part->ctype_parameters['charset'])) {
- $part->ctype_parameters['charset'] = $MESSAGE->headers->charset;
- }
-
- // fetch part if not available
- if (!isset($part->body)) {
- $part->body = $MESSAGE->get_part_content($part->mime_id);
- }
+ // fetch part body
+ $body = $MESSAGE->get_part_body($part->mime_id, true);
// extract headers from message/rfc822 parts
if ($part->mimetype == 'message/rfc822') {
- $msgpart = rcube_mime::parse_message($part->body);
+ $msgpart = rcube_mime::parse_message($body);
if (!empty($msgpart->headers)) {
$part = $msgpart;
$out .= html::div('message-partheaders', rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : null, $part->headers));
@@ -1223,14 +1221,14 @@ function rcmail_message_body($attrib)
}
// message is cached but not exists (#1485443), or other error
- if ($part->body === false) {
+ if ($body === false) {
rcmail_message_error($MESSAGE->uid);
}
$plugin = $RCMAIL->plugins->exec_hook('message_body_prefix',
array('part' => $part, 'prefix' => ''));
- $body = rcmail_print_body($part, array('safe' => $safe_mode, 'plain' => !$RCMAIL->config->get('prefer_html')));
+ $body = rcmail_print_body($body, $part, array('safe' => $safe_mode, 'plain' => !$RCMAIL->config->get('prefer_html')));
if ($part->ctype_secondary == 'html') {
$body = rcmail_html4inline($body, $attrib['id'], 'rcmBody', $attrs, $safe_mode);
diff --git a/program/steps/mail/get.inc b/program/steps/mail/get.inc
index b260d2c85..948465604 100644
--- a/program/steps/mail/get.inc
+++ b/program/steps/mail/get.inc
@@ -130,7 +130,7 @@ else if (strlen($part_id)) {
$extensions = rcube_mime::get_mime_extensions($mimetype);
if ($plugin['body']) {
- $part->body = $plugin['body'];
+ $body = $plugin['body'];
}
// compare file mimetype with the stated content-type headers and file extension to avoid malicious operations
@@ -142,15 +142,10 @@ else if (strlen($part_id)) {
// 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' || stripos($mimetype, 'text/') === 0) {
- 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
- $body = $part->body = $MESSAGE->get_part_content($part->mime_id);
- else // fetch the first 2K of the message part
- $body = $MESSAGE->get_part_content($part->mime_id, null, true, 2048);
+ $tmp_body = $body ?: $MESSAGE->get_part_body($part->mime_id, false, 2048);
// detect message part mimetype
- $real_mimetype = rcube_mime::file_content_type($body, $part->filename, $mimetype, true, true);
+ $real_mimetype = rcube_mime::file_content_type($tmp_body, $part->filename, $mimetype, true, true);
list($real_ctype_primary, $real_ctype_secondary) = explode('/', $real_mimetype);
// accept text/plain with any extension
@@ -251,15 +246,15 @@ else if (strlen($part_id)) {
}
else {
// get part body if not available
- if (!$part->body) {
- $part->body = $MESSAGE->get_part_content($part->mime_id);
+ if (!isset($body)) {
+ $body = $MESSAGE->get_part_body($part->mime_id, true);
}
// show images?
rcmail_check_safe($MESSAGE);
// render HTML body
- $out = rcmail_print_body($part, array('safe' => $MESSAGE->is_safe, 'inline_html' => false));
+ $out = rcmail_print_body($body, $part, array('safe' => $MESSAGE->is_safe, 'inline_html' => false));
// insert remote objects warning into HTML body
if ($REMOTE_OBJECTS) {
@@ -280,7 +275,7 @@ else if (strlen($part_id)) {
}
// check connection status
- if ($part->size && empty($part->body)) {
+ if ($part->size && empty($body)) {
check_storage_status();
}
@@ -320,12 +315,12 @@ else if (strlen($part_id)) {
$file_path = tempnam($temp_dir, 'rcmAttmnt');
// write content to temp file
- if ($part->body) {
- $saved = file_put_contents($file_path, $part->body);
+ if ($body) {
+ $saved = file_put_contents($file_path, $body);
}
else if ($part->size) {
$fd = fopen($file_path, 'w');
- $saved = $RCMAIL->storage->get_message_part($MESSAGE->uid, $part->mime_id, $part, false, $fd);
+ $saved = $MESSAGE->get_part_body($part->mime_id, false, 0, $fd);
fclose($fd);
}
@@ -341,22 +336,22 @@ else if (strlen($part_id)) {
}
// do content filtering to avoid XSS through fake images
else if (!empty($_REQUEST['_embed']) && $browser->ie && $browser->ver <= 8) {
- if ($part->body) {
- echo preg_match('/<(script|iframe|object)/i', $part->body) ? '' : $part->body;
+ if ($body) {
+ echo preg_match('/<(script|iframe|object)/i', $body) ? '' : $body;
$sent = true;
}
else if ($part->size) {
$stdout = fopen('php://output', 'w');
stream_filter_register('rcube_content', 'rcube_content_filter') or die('Failed to register content filter');
stream_filter_append($stdout, 'rcube_content');
- $sent = $RCMAIL->storage->get_message_part($MESSAGE->uid, $part->mime_id, $part, false, $stdout);
+ $sent = $MESSAGE->get_part_body($part->mime_id, true, 0, $stdout);
}
}
// send part as-it-is
else {
- if ($part->body && empty($plugin['download'])) {
- header("Content-Length: " . strlen($part->body));
- echo $part->body;
+ if ($body && empty($plugin['download'])) {
+ header("Content-Length: " . strlen($body));
+ echo $body;
$sent = true;
}
else if ($part->size) {
@@ -364,8 +359,7 @@ else if (strlen($part_id)) {
header("Content-Length: $size");
}
- // 8th argument disables re-formatting of text/* parts (#1489267)
- $sent = $RCMAIL->storage->get_message_part($MESSAGE->uid, $part->mime_id, $part, true, null, false, 0, false);
+ $sent = $MESSAGE->get_part_body($part->mime_id, false, 0, -1);
}
}
diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc
index bac751298..68878d790 100644
--- a/program/steps/mail/sendmail.inc
+++ b/program/steps/mail/sendmail.inc
@@ -469,7 +469,8 @@ if (is_array($COMPOSE['attachments'])) {
$attachment['data'] ? false : true,
$ctype == 'message/rfc822' ? '8bit' : 'base64',
'attachment',
- '', '', '',
+ $attachment['charset'],
+ '', '',
$folding ? 'quoted-printable' : NULL,
$folding == 2 ? 'quoted-printable' : NULL,
'', RCUBE_CHARSET
diff --git a/skins/classic/print.css b/skins/classic/print.css
index 8eac41f53..349b224cb 100644
--- a/skins/classic/print.css
+++ b/skins/classic/print.css
@@ -8,7 +8,7 @@ body
margin: 2mm;
}
-body, td, th, span, div, p
+body, td, th, div, p
{
font-size: 9pt;
color: #000000;
diff --git a/skins/larry/mail.css b/skins/larry/mail.css
index 4ec258552..0f1eaa8a7 100644
--- a/skins/larry/mail.css
+++ b/skins/larry/mail.css
@@ -857,7 +857,6 @@ div.hide-headers {
#messagecontent .leftcol,
#messagepreview .leftcol {
margin-right: 252px;
- overflow-x: auto;
}
#messagecontent .rightcol,
diff --git a/skins/larry/print.css b/skins/larry/print.css
index 22d6c5288..d3a6cd802 100644
--- a/skins/larry/print.css
+++ b/skins/larry/print.css
@@ -15,7 +15,7 @@ body {
margin: 2mm;
}
-body, td, th, span, div, p {
+body, td, th, div, p {
font-size: 9pt;
color: #000;
}
diff --git a/tests/MailFunc.php b/tests/MailFunc.php
index 05f26324e..7fb78ef9e 100644
--- a/tests/MailFunc.php
+++ b/tests/MailFunc.php
@@ -42,7 +42,7 @@ class MailFunc extends PHPUnit_Framework_TestCase
$part->replaces = array('ex1.jpg' => 'part_1.2.jpg', 'ex2.jpg' => 'part_1.2.jpg');
// render HTML in normal mode
- $html = rcmail_html4inline(rcmail_print_body($part, array('safe' => false)), 'foo');
+ $html = rcmail_html4inline(rcmail_print_body($part->body, $part, array('safe' => false)), 'foo');
$this->assertRegExp('/src="'.$part->replaces['ex1.jpg'].'"/', $html, "Replace reference to inline image");
$this->assertRegExp('#background="./program/resources/blocked.gif"#', $html, "Replace external background image");
@@ -56,7 +56,7 @@ class MailFunc extends PHPUnit_Framework_TestCase
$this->assertTrue($GLOBALS['REMOTE_OBJECTS'], "Remote object detected");
// render HTML in safe mode
- $html2 = rcmail_html4inline(rcmail_print_body($part, array('safe' => true)), 'foo');
+ $html2 = rcmail_html4inline(rcmail_print_body($part->body, $part, array('safe' => true)), 'foo');
$this->assertRegExp('/<style [^>]+>/', $html2, "Allow styles in safe mode");
$this->assertRegExp('#src="http://evilsite.net/mailings/ex3.jpg"#', $html2, "Allow external images in HTML (safe mode)");
@@ -71,7 +71,7 @@ class MailFunc extends PHPUnit_Framework_TestCase
function test_html_xss()
{
$part = $this->get_html_part('src/htmlxss.txt');
- $washed = rcmail_print_body($part, array('safe' => true));
+ $washed = rcmail_print_body($part->body, $part, array('safe' => true));
$this->assertNotRegExp('/src="skins/', $washed, "Remove local references");
$this->assertNotRegExp('/\son[a-z]+/', $washed, "Remove on* attributes");
@@ -88,7 +88,7 @@ class MailFunc extends PHPUnit_Framework_TestCase
function test_html_xss2()
{
$part = $this->get_html_part('src/BID-26800.txt');
- $washed = rcmail_html4inline(rcmail_print_body($part, array('safe' => true)), 'dabody', '', $attr, true);
+ $washed = rcmail_html4inline(rcmail_print_body($part->body, $part, array('safe' => true)), 'dabody', '', $attr, true);
$this->assertNotRegExp('/alert|expression|javascript|xss/', $washed, "Remove evil style blocks");
$this->assertNotRegExp('/font-style:italic/', $washed, "Allow valid styles");
@@ -114,7 +114,7 @@ class MailFunc extends PHPUnit_Framework_TestCase
function test_washtml_utf8()
{
$part = $this->get_html_part('src/invalidchars.html');
- $washed = rcmail_print_body($part);
+ $washed = rcmail_print_body($part->body, $part);
$this->assertRegExp('/<p>символ<\/p>/', $washed, "Remove non-unicode characters from HTML message body");
}
@@ -128,7 +128,7 @@ class MailFunc extends PHPUnit_Framework_TestCase
$part->ctype_primary = 'text';
$part->ctype_secondary = 'plain';
$part->body = quoted_printable_decode(file_get_contents(TESTS_DIR . 'src/plainbody.txt'));
- $html = rcmail_print_body($part, array('safe' => true));
+ $html = rcmail_print_body($part->body, $part, array('safe' => true));
$this->assertRegExp('/<a href="mailto:nobody@roundcube.net" onclick="return rcmail.command\(\'compose\',\'nobody@roundcube.net\',this\)">nobody@roundcube.net<\/a>/', $html, "Mailto links with onclick");
$this->assertRegExp('#<a rel="noreferrer" target="_blank" href="http://www.apple.com/legal/privacy">http://www.apple.com/legal/privacy</a>#', $html, "Links with target=_blank");
@@ -143,7 +143,7 @@ class MailFunc extends PHPUnit_Framework_TestCase
$part = $this->get_html_part('src/mailto.txt');
// render HTML in normal mode
- $html = rcmail_html4inline(rcmail_print_body($part, array('safe' => false)), 'foo');
+ $html = rcmail_html4inline(rcmail_print_body($part->body, $part, array('safe' => false)), 'foo');
$mailto = '<a href="mailto:me@me.com"'
.' onclick="return rcmail.command(\'compose\',\'me@me.com?subject=this is the subject&amp;body=this is the body\',this)" rel="noreferrer">e-mail</a>';
@@ -157,7 +157,7 @@ class MailFunc extends PHPUnit_Framework_TestCase
function test_html_comments()
{
$part = $this->get_html_part('src/htmlcom.txt');
- $washed = rcmail_print_body($part, array('safe' => true));
+ $washed = rcmail_print_body($part->body, $part, array('safe' => true));
// #1487759
$this->assertRegExp('|<p>test1</p>|', $washed, "Buggy HTML comments");