summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG4
-rw-r--r--config/main.inc.php.dist5
-rw-r--r--plugins/password/config.inc.php.dist4
-rw-r--r--plugins/password/drivers/sql.php36
-rw-r--r--plugins/password/package.xml25
-rw-r--r--plugins/password/password.php6
-rw-r--r--program/include/main.inc31
-rw-r--r--program/include/rcmail.php11
-rw-r--r--program/include/rcube_imap.php79
-rw-r--r--program/js/common.js11
-rw-r--r--program/steps/mail/autocomplete.inc34
11 files changed, 183 insertions, 63 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 909a9f3cf..c585c1217 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,10 @@
CHANGELOG Roundcube Webmail
===========================
+- Fix listing of folders in hidden namespaces (#1486796)
+- Don't consider \Noselect flag when building folders tree (#1488004)
+- Fix sorting autocomplete results (#1488084)
+- Add option to set session name (#1486433)
- Add option to skip alternative email addresses in autocompletion
- Fix inconsistent behaviour of Compose button in Drafts folder, add Edit button for drafts
- Fix problem with parsing HTML message body with non-unicode characters (#1487813)
diff --git a/config/main.inc.php.dist b/config/main.inc.php.dist
index fe58350be..d07a3b3c2 100644
--- a/config/main.inc.php.dist
+++ b/config/main.inc.php.dist
@@ -222,6 +222,9 @@ $rcmail_config['session_lifetime'] = 10;
// session domain: .example.org
$rcmail_config['session_domain'] = '';
+// session name. Default: 'roundcube_sessid'
+$rcmail_config['session_name'] = null;
+
// Backend to use for session storage. Can either be 'db' (default) or 'memcache'
// If set to memcache, a list of servers need to be specified in 'memcache_hosts'
// Make sure the Memcache extension (http://pecl.php.net/package/memcache) version >= 2.0.0 is installed
@@ -418,7 +421,7 @@ $rcmail_config['trash_mbox'] = 'Trash';
// NOTE: Use folder names with namespace prefix (INBOX. on Courier-IMAP)
$rcmail_config['default_imap_folders'] = array('INBOX', 'Drafts', 'Sent', 'Junk', 'Trash');
-// automatically create the above listed default folders on login
+// automatically create the above listed default folders on first login
$rcmail_config['create_default_folders'] = false;
// protect the default folders from renames, deletes, and subscription changes
diff --git a/plugins/password/config.inc.php.dist b/plugins/password/config.inc.php.dist
index 94af6d776..313e47fda 100644
--- a/plugins/password/config.inc.php.dist
+++ b/plugins/password/config.inc.php.dist
@@ -47,6 +47,10 @@ $rcmail_config['password_db_dsn'] = '';
// Default: "SELECT update_passwd(%c, %u)"
$rcmail_config['password_query'] = 'SELECT update_passwd(%c, %u)';
+// By default domains in variables are using unicode.
+// Enable this option to use punycoded names
+$rcmail_config['password_idn_ascii'] = false;
+
// Path for dovecotpw (if not in $PATH)
// $rcmail_config['password_dovecotpw'] = '/usr/local/sbin/dovecotpw';
diff --git a/plugins/password/drivers/sql.php b/plugins/password/drivers/sql.php
index 836a58a44..06a6b75ff 100644
--- a/plugins/password/drivers/sql.php
+++ b/plugins/password/drivers/sql.php
@@ -37,16 +37,21 @@ function password_save($curpass, $passwd)
// crypted password
if (strpos($sql, '%c') !== FALSE) {
$salt = '';
- if (CRYPT_MD5) {
- $len = rand(3, CRYPT_SALT_LENGTH);
+ if (CRYPT_MD5) {
+ // Always use eight salt characters for MD5 (#1488136)
+ $len = 8;
} else if (CRYPT_STD_DES) {
$len = 2;
} else {
return PASSWORD_CRYPT_ERROR;
}
+
+ //Restrict the character set used as salt (#1488136)
+ $seedchars = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
for ($i = 0; $i < $len ; $i++) {
- $salt .= chr(rand(ord('.'), ord('z')));
+ $salt .= $seedchars[rand(0, 63)];
}
+
$sql = str_replace('%c', $db->quote(crypt($passwd, CRYPT_MD5 ? '$1$'.$salt.'$' : $salt)), $sql);
}
@@ -125,11 +130,28 @@ function password_save($curpass, $passwd)
}
}
+ $local_part = $rcmail->user->get_username('local');
+ $domain_part = $rcmail->user->get_username('domain');
+ $username = $_SESSION['username'];
+ $host = $_SESSION['imap_host'];
+
+ // convert domains to/from punnycode
+ if ($rcmail->config->get('password_idn_ascii')) {
+ $domain_part = rcube_idn_to_ascii($domain_part);
+ $username = rcube_idn_to_ascii($username);
+ $host = rcube_idn_to_ascii($host);
+ }
+ else {
+ $domain_part = rcube_idn_to_utf8($domain_part);
+ $username = rcube_idn_to_utf8($username);
+ $host = rcube_idn_to_utf8($host);
+ }
+
// at least we should always have the local part
- $sql = str_replace('%l', $db->quote($rcmail->user->get_username('local'), 'text'), $sql);
- $sql = str_replace('%d', $db->quote($rcmail->user->get_username('domain'), 'text'), $sql);
- $sql = str_replace('%u', $db->quote($_SESSION['username'],'text'), $sql);
- $sql = str_replace('%h', $db->quote($_SESSION['imap_host'],'text'), $sql);
+ $sql = str_replace('%l', $db->quote($local_part, 'text'), $sql);
+ $sql = str_replace('%d', $db->quote($domain_part, 'text'), $sql);
+ $sql = str_replace('%u', $db->quote($username, 'text'), $sql);
+ $sql = str_replace('%h', $db->quote($host, 'text'), $sql);
$res = $db->query($sql, $sql_vars);
diff --git a/plugins/password/package.xml b/plugins/password/package.xml
index 69cef072f..d3f4bea9b 100644
--- a/plugins/password/package.xml
+++ b/plugins/password/package.xml
@@ -27,10 +27,7 @@
</stability>
<license uri="http://www.gnu.org/licenses/gpl-2.0.html">GNU GPLv2</license>
<notes>
-- When old and new passwords are the same, do nothing, return success (#1487823)
-- Fixed Samba password hashing in 'ldap' driver
-- Added 'password_change' hook for plugin actions after successful password change
-- Fixed bug where 'doveadm pw' command was used as dovecotpw utility
+- Added option to use punycode or unicode for domain names (#1488103)
</notes>
<contents>
<dir baseinstalldir="/" name="/">
@@ -264,5 +261,25 @@
- Virtualmin driver: Add option for setting username format (#1487781)
</notes>
</release>
+ <release>
+ <date>2011-10-26</date>
+ <time>12:00</time>
+ <version>
+ <release>2.3</release>
+ <api>1.6</api>
+ </version>
+ <stability>
+ <release>stable</release>
+ <api>stable</api>
+ </stability>
+ <license uri="http://www.gnu.org/licenses/gpl-2.0.html">GNU GPLv2</license>
+ <notes>
+- When old and new passwords are the same, do nothing, return success (#1487823)
+- Fixed Samba password hashing in 'ldap' driver
+- Added 'password_change' hook for plugin actions after successful password change
+- Fixed bug where 'doveadm pw' command was used as dovecotpw utility
+- Improve generated crypt() passwords (#1488136)
+ </notes>
+ </release>
</changelog>
</package>
diff --git a/plugins/password/password.php b/plugins/password/password.php
index b1c7863fc..06e3448f0 100644
--- a/plugins/password/password.php
+++ b/plugins/password/password.php
@@ -223,7 +223,7 @@ class password extends rcube_plugin
{
$config = rcmail::get_instance()->config;
$driver = $this->home.'/drivers/'.$config->get('password_driver', 'sql').'.php';
-
+
if (!is_readable($driver)) {
raise_error(array(
'code' => 600,
@@ -233,7 +233,7 @@ class password extends rcube_plugin
), true, false);
return $this->gettext('internalerror');
}
-
+
include($driver);
if (!function_exists('password_save')) {
@@ -270,5 +270,5 @@ class password extends rcube_plugin
}
return $reason;
- }
+ }
}
diff --git a/program/include/main.inc b/program/include/main.inc
index 95d422d6a..c84e5ad6b 100644
--- a/program/include/main.inc
+++ b/program/include/main.inc
@@ -1281,11 +1281,6 @@ function rcmail_build_folder_tree(&$arrFolders, $folder, $delm='/', $path='')
$path .= $prefix.$currentFolder;
if (!isset($arrFolders[$currentFolder])) {
- // Check \Noselect attribute (if attributes are in cache)
- if (!$virtual && ($attrs = $RCMAIL->imap->mailbox_attributes($path))) {
- $virtual = in_array('\\Noselect', $attrs);
- }
-
$arrFolders[$currentFolder] = array(
'id' => $path,
'name' => rcube_charset_convert($currentFolder, 'UTF7-IMAP'),
@@ -1313,12 +1308,14 @@ function rcmail_render_folder_tree_html(&$arrFolders, &$mbox_name, &$jslist, $at
$realnames = (bool)$attrib['realnames'];
$msgcounts = $RCMAIL->imap->get_cache('messagecount');
- $idx = 0;
$out = '';
foreach ($arrFolders as $key => $folder) {
- $title = null;
+ $title = null;
+ $folder_class = rcmail_folder_classname($folder['id']);
+ $collapsed = strpos($CONFIG['collapsed_folders'], '&'.rawurlencode($folder['id']).'&') !== false;
+ $unread = $msgcounts ? intval($msgcounts[$folder['id']]['UNSEEN']) : 0;
- if (($folder_class = rcmail_folder_classname($folder['id'])) && !$realnames) {
+ if ($folder_class && !$realnames) {
$foldername = rcube_label($folder_class);
}
else {
@@ -1338,25 +1335,12 @@ function rcmail_render_folder_tree_html(&$arrFolders, &$mbox_name, &$jslist, $at
$classes = array('mailbox');
// set special class for Sent, Drafts, Trash and Junk
- if ($folder['id'] == $CONFIG['sent_mbox'])
- $classes[] = 'sent';
- else if ($folder['id'] == $CONFIG['drafts_mbox'])
- $classes[] = 'drafts';
- else if ($folder['id'] == $CONFIG['trash_mbox'])
- $classes[] = 'trash';
- else if ($folder['id'] == $CONFIG['junk_mbox'])
- $classes[] = 'junk';
- else if ($folder['id'] == 'INBOX')
- $classes[] = 'inbox';
- else
- $classes[] = '_'.asciiwords($folder_class ? $folder_class : strtolower($folder['id']), true);
+ if ($folder_class)
+ $classes[] = $folder_class;
if ($folder['id'] == $mbox_name)
$classes[] = 'selected';
- $collapsed = strpos($CONFIG['collapsed_folders'], '&'.rawurlencode($folder['id']).'&') !== false;
- $unread = $msgcounts ? intval($msgcounts[$folder['id']]['UNSEEN']) : 0;
-
if ($folder['virtual'])
$classes[] = 'virtual';
else if ($unread)
@@ -1390,7 +1374,6 @@ function rcmail_render_folder_tree_html(&$arrFolders, &$mbox_name, &$jslist, $at
}
$out .= "</li>\n";
- $idx++;
}
return $out;
diff --git a/program/include/rcmail.php b/program/include/rcmail.php
index 969e101f7..e06594fcd 100644
--- a/program/include/rcmail.php
+++ b/program/include/rcmail.php
@@ -678,18 +678,21 @@ class rcmail
if (session_id())
return;
+ $sess_name = $this->config->get('session_name');
+ $sess_domain = $this->config->get('session_domain');
+ $lifetime = $this->config->get('session_lifetime', 0) * 60;
+
// set session domain
- if ($domain = $this->config->get('session_domain')) {
- ini_set('session.cookie_domain', $domain);
+ if ($sess_domain) {
+ ini_set('session.cookie_domain', $sess_domain);
}
// set session garbage collecting time according to session_lifetime
- $lifetime = $this->config->get('session_lifetime', 0) * 60;
if ($lifetime) {
ini_set('session.gc_maxlifetime', $lifetime * 2);
}
ini_set('session.cookie_secure', rcube_https_check());
- ini_set('session.name', 'roundcube_sessid');
+ ini_set('session.name', $sess_name ? $sess_name : 'roundcube_sessid');
ini_set('session.use_cookies', 1);
ini_set('session.use_only_cookies', 1);
ini_set('session.serialize_handler', 'php');
diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php
index 8c1fab8cf..d56da3d26 100644
--- a/program/include/rcube_imap.php
+++ b/program/include/rcube_imap.php
@@ -1373,6 +1373,11 @@ class rcube_imap
$this->_messagecount($mailbox, 'ALL', true);
$result = 0;
+
+ if (empty($old)) {
+ return $result;
+ }
+
$new = $this->get_folder_stats($mailbox);
// got new messages
@@ -2999,14 +3004,14 @@ class rcube_imap
/**
- * Private method for mailbox listing
+ * Private method for mailbox listing (LSUB)
*
* @param string $root Optional root folder
* @param string $name Optional name pattern
* @param mixed $filter Optional filter
* @param string $rights Optional ACL requirements
*
- * @return array List of mailboxes/folders
+ * @return array List of subscribed folders
* @see rcube_imap::list_mailboxes()
* @access private
*/
@@ -3109,7 +3114,7 @@ class rcube_imap
}
else {
// retrieve list of folders from IMAP server
- $a_mboxes = $this->conn->listMailboxes($root, $name);
+ $a_mboxes = $this->_list_unsubscribed($root, $name);
}
if (!is_array($a_mboxes)) {
@@ -3144,6 +3149,70 @@ class rcube_imap
/**
+ * Private method for mailbox listing (LIST)
+ *
+ * @param string $root Optional root folder
+ * @param string $name Optional name pattern
+ *
+ * @return array List of folders
+ * @see rcube_imap::list_unsubscribed()
+ */
+ private function _list_unsubscribed($root='', $name='*')
+ {
+ $result = $this->conn->listMailboxes($root, $name);
+
+ if (!is_array($result)) {
+ return array();
+ }
+
+ // #1486796: some server configurations doesn't
+ // return folders in all namespaces, we'll try to detect that situation
+ // and ask for these namespaces separately
+ if ($root == '' && $name = '*') {
+ $delim = $this->get_hierarchy_delimiter();
+ $namespace = $this->get_namespace();
+ $search = array();
+
+ // build list of namespace prefixes
+ foreach ((array)$namespace as $ns) {
+ if (is_array($ns)) {
+ foreach ($ns as $ns_data) {
+ if (strlen($ns_data[0])) {
+ $search = $ns_data[0];
+ }
+ }
+ }
+ }
+
+ if (!empty($search)) {
+ // go through all folders detecting namespace usage
+ foreach ($result as $folder) {
+ foreach ($search as $idx => $prefix) {
+ if (strpos($folder, $prefix) === 0) {
+ unset($search[$idx]);
+ }
+ }
+ if (empty($search)) {
+ break;
+ }
+ }
+
+ // get folders in hidden namespaces and add to the result
+ foreach ($search as $prefix) {
+ $list = $this->conn->listMailboxes($prefix, $name);
+
+ if (!empty($list)) {
+ $result = array_merge($result, $list);
+ }
+ }
+ }
+ }
+
+ return $result;
+ }
+
+
+ /**
* Filter the given list of folders according to access rights
*/
private function filter_rights($a_folders, $rights)
@@ -3415,8 +3484,8 @@ class rcube_imap
foreach ($this->namespace as $type => $namespace) {
if (is_array($namespace)) {
foreach ($namespace as $ns) {
- if (strlen($ns[0])) {
- if ((strlen($ns[0])>1 && $mailbox == substr($ns[0], 0, -1))
+ if ($len = strlen($ns[0])) {
+ if (($len > 1 && $mailbox == substr($ns[0], 0, -1))
|| strpos($mailbox, $ns[0]) === 0
) {
return $type;
diff --git a/program/js/common.js b/program/js/common.js
index 831e44a21..c13d95e3d 100644
--- a/program/js/common.js
+++ b/program/js/common.js
@@ -542,10 +542,17 @@ function rcube_clone_object(obj)
return out;
};
-// make a string URL safe
+// make a string URL safe (and compatible with PHP's rawurlencode())
function urlencode(str)
{
- return window.encodeURIComponent ? encodeURIComponent(str) : escape(str);
+ if (window.encodeURIComponent)
+ return encodeURIComponent(str).replace('*', '%2A');
+
+ return escape(str)
+ .replace('+', '%2B')
+ .replace('*', '%2A')
+ .replace('/', '%2F')
+ .replace('@', '%40');
};
diff --git a/program/steps/mail/autocomplete.inc b/program/steps/mail/autocomplete.inc
index e40bb76c3..281a4e9f7 100644
--- a/program/steps/mail/autocomplete.inc
+++ b/program/steps/mail/autocomplete.inc
@@ -54,6 +54,7 @@ else
if (!empty($book_types) && strlen($search)) {
$contacts = array();
+ $sort_keys = array();
$books_num = count($book_types);
$search_lc = mb_strtolower($search);
@@ -66,6 +67,7 @@ if (!empty($book_types) && strlen($search)) {
// Contact can have more than one e-mail address
$email_arr = (array)$abook->get_col_values('email', $sql_arr, true);
$email_cnt = count($email_arr);
+ $idx = 0;
foreach ($email_arr as $email) {
if (empty($email)) {
continue;
@@ -80,7 +82,9 @@ if (!empty($book_types) && strlen($search)) {
// skip duplicates
if (!in_array($contact, $contacts)) {
- $contacts[] = $contact;
+ $contacts[] = $contact;
+ $sort_keys[] = sprintf('%s %03d', $sql_arr['name'] , $idx++);
+
if (count($contacts) >= $MAXNUM)
break 2;
}
@@ -102,15 +106,20 @@ if (!empty($book_types) && strlen($search)) {
// group (distribution list) with email address(es)
if ($group_prop['email']) {
+ $idx = 0;
foreach ((array)$group_prop['email'] as $email) {
- $contacts[] = format_email_recipient($email, $group['name']);
+ $contacts[] = format_email_recipient($email, $group['name']);
+ $sort_keys[] = sprintf('%s %03d', $group['name'] , $idx++);
+
if (count($contacts) >= $MAXNUM)
break 2;
}
}
// show group with count
else if (($result = $abook->count()) && $result->count) {
- $contacts[] = array('name' => $group['name'] . ' (' . intval($result->count) . ')', 'id' => $group['ID'], 'source' => $id);
+ $contacts[] = array('name' => $group['name'] . ' (' . intval($result->count) . ')', 'id' => $group['ID'], 'source' => $id);
+ $sort_keys[] = $group['name'];
+
if (count($contacts) >= $MAXNUM)
break;
}
@@ -118,17 +127,16 @@ if (!empty($book_types) && strlen($search)) {
}
}
- usort($contacts, 'contact_results_sort');
+ if (count($contacts)) {
+ // sort contacts index
+ asort($sort_keys, SORT_LOCALE_STRING);
+ // re-sort contacts according to index
+ foreach ($sort_keys as $idx => $val) {
+ $sort_keys[$idx] = $contacts[$idx];
+ }
+ $contacts = array_values($sort_keys);
+ }
}
$OUTPUT->command('ksearch_query_results', $contacts, $search, $sid);
$OUTPUT->send();
-
-
-function contact_results_sort($a, $b)
-{
- $name_a = is_array($a) ? $a['name'] : $a;
- $name_b = is_array($b) ? $b['name'] : $b;
- return strcoll(trim($name_a, '" '), trim($name_b, '" '));
-}
-