diff options
-rw-r--r-- | CHANGELOG | 4 | ||||
-rw-r--r-- | config/main.inc.php.dist | 5 | ||||
-rw-r--r-- | plugins/password/config.inc.php.dist | 4 | ||||
-rw-r--r-- | plugins/password/drivers/sql.php | 36 | ||||
-rw-r--r-- | plugins/password/package.xml | 25 | ||||
-rw-r--r-- | plugins/password/password.php | 6 | ||||
-rw-r--r-- | program/include/main.inc | 31 | ||||
-rw-r--r-- | program/include/rcmail.php | 11 | ||||
-rw-r--r-- | program/include/rcube_imap.php | 79 | ||||
-rw-r--r-- | program/js/common.js | 11 | ||||
-rw-r--r-- | program/steps/mail/autocomplete.inc | 34 |
11 files changed, 183 insertions, 63 deletions
@@ -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, '" ')); -} - |