alecpl
2011-11-15 3fec6952ddbff1b5b487ea2927928338f39e4fef
- Applied fixes from trunk up to r5425


11 files modified
246 ■■■■ changed files
CHANGELOG 4 ●●●● patch | view | raw | blame | history
config/main.inc.php.dist 5 ●●●● patch | view | raw | blame | history
plugins/password/config.inc.php.dist 4 ●●●● patch | view | raw | blame | history
plugins/password/drivers/sql.php 36 ●●●● patch | view | raw | blame | history
plugins/password/package.xml 25 ●●●● patch | view | raw | blame | history
plugins/password/password.php 6 ●●●● patch | view | raw | blame | history
program/include/main.inc 31 ●●●● patch | view | raw | blame | history
program/include/rcmail.php 11 ●●●●● patch | view | raw | blame | history
program/include/rcube_imap.php 79 ●●●●● patch | view | raw | blame | history
program/js/common.js 11 ●●●● patch | view | raw | blame | history
program/steps/mail/autocomplete.inc 34 ●●●●● patch | view | raw | blame | history
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)
config/main.inc.php.dist
@@ -222,6 +222,9 @@
// 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 @@
// 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
plugins/password/config.inc.php.dist
@@ -47,6 +47,10 @@
// 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';
plugins/password/drivers/sql.php
@@ -37,16 +37,21 @@
    // 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 @@
        }
    }
    $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);
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>
plugins/password/password.php
@@ -223,7 +223,7 @@
    {
        $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 @@
            ), true, false);
            return $this->gettext('internalerror');
        }
        include($driver);
        if (!function_exists('password_save')) {
@@ -270,5 +270,5 @@
        }
        return $reason;
    }
    }
}
program/include/main.inc
@@ -1281,11 +1281,6 @@
  $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 @@
  $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,24 +1335,11 @@
    $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';
@@ -1390,7 +1374,6 @@
    }
    $out .= "</li>\n";
    $idx++;
  }
  return $out;
program/include/rcmail.php
@@ -678,18 +678,21 @@
    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');
program/include/rcube_imap.php
@@ -1373,6 +1373,11 @@
        $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 @@
    /**
     * 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 @@
        }
        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)) {
@@ -3140,6 +3145,70 @@
        $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;
    }
@@ -3415,8 +3484,8 @@
        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;
program/js/common.js
@@ -542,10 +542,17 @@
  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');
};
program/steps/mail/autocomplete.inc
@@ -54,6 +54,7 @@
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 @@
        // 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 @@
          // 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 @@
        // 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 @@
    }
  }
  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, '" '));
}