- Applied fixes from trunk up to r5425
| | |
| | | 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) |
| | |
| | | // 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 |
| | |
| | | // 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 |
| | |
| | | // 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'; |
| | | |
| | |
| | | if (strpos($sql, '%c') !== FALSE) { |
| | | $salt = ''; |
| | | if (CRYPT_MD5) { |
| | | $len = rand(3, CRYPT_SALT_LENGTH); |
| | | // 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); |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | $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); |
| | | |
| | |
| | | </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="/"> |
| | |
| | | - 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> |
| | |
| | | $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'), |
| | |
| | | $realnames = (bool)$attrib['realnames']; |
| | | $msgcounts = $RCMAIL->imap->get_cache('messagecount'); |
| | | |
| | | $idx = 0; |
| | | $out = ''; |
| | | foreach ($arrFolders as $key => $folder) { |
| | | $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 { |
| | |
| | | $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'; |
| | |
| | | } |
| | | |
| | | $out .= "</li>\n"; |
| | | $idx++; |
| | | } |
| | | |
| | | return $out; |
| | |
| | | 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'); |
| | |
| | | $this->_messagecount($mailbox, 'ALL', true); |
| | | |
| | | $result = 0; |
| | | |
| | | if (empty($old)) { |
| | | return $result; |
| | | } |
| | | |
| | | $new = $this->get_folder_stats($mailbox); |
| | | |
| | | // got new messages |
| | |
| | | |
| | | |
| | | /** |
| | | * 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 |
| | | */ |
| | |
| | | } |
| | | 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)) { |
| | |
| | | $this->update_cache($cache_key, $a_mboxes); |
| | | |
| | | return $a_mboxes; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 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; |
| | | } |
| | | |
| | | |
| | |
| | | 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; |
| | |
| | | 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'); |
| | | }; |
| | | |
| | | |
| | |
| | | |
| | | if (!empty($book_types) && strlen($search)) { |
| | | $contacts = array(); |
| | | $sort_keys = array(); |
| | | $books_num = count($book_types); |
| | | $search_lc = mb_strtolower($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; |
| | |
| | | // skip duplicates |
| | | if (!in_array($contact, $contacts)) { |
| | | $contacts[] = $contact; |
| | | $sort_keys[] = sprintf('%s %03d', $sql_arr['name'] , $idx++); |
| | | |
| | | if (count($contacts) >= $MAXNUM) |
| | | break 2; |
| | | } |
| | |
| | | |
| | | // 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']); |
| | | $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); |
| | | $sort_keys[] = $group['name']; |
| | | |
| | | if (count($contacts) >= $MAXNUM) |
| | | break; |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | 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, '" ')); |
| | | } |
| | | |