alecpl
2011-11-28 77449d011b2367a9f3d7bb179534aa09865aa26e
- Applied fixes from trunk up to r5498


13 files modified
417 ■■■■■ changed files
CHANGELOG 7 ●●●●● patch | view | raw | blame | history
config/main.inc.php.dist 4 ●●●● patch | view | raw | blame | history
installer/rcube_install.php 43 ●●●●● patch | view | raw | blame | history
program/include/main.inc 43 ●●●●● patch | view | raw | blame | history
program/include/rcube_browser.php 24 ●●●● patch | view | raw | blame | history
program/include/rcube_config.php 7 ●●●● patch | view | raw | blame | history
program/include/rcube_smtp.php 2 ●●● patch | view | raw | blame | history
program/js/app.js 7 ●●●● patch | view | raw | blame | history
program/lib/html2text.php 96 ●●●●● patch | view | raw | blame | history
program/steps/addressbook/func.inc 2 ●●● patch | view | raw | blame | history
program/steps/mail/search.inc 20 ●●●●● patch | view | raw | blame | history
skins/default/functions.js 160 ●●●●● patch | view | raw | blame | history
tests/mailfunc.php 2 ●●● patch | view | raw | blame | history
CHANGELOG
@@ -1,6 +1,13 @@
CHANGELOG Roundcube Webmail
===========================
- Use strpos() instead of strstr() when possible (#1488211)
- Fix handling HTML entities when converting HTML to text (#1488212)
- Fix fit_string_to_size() renders browser and ui unresponsive (#1488207)
- Fix handling of invalid characters in request (#1488124)
- Fix merging some configuration options in update.sh script (#1485864)
- Fix so TEXT key will remove all HEADER keys in IMAP SEARCH (#1488208)
- Fix handling contact photo url with https:// prefix (#1488202)
- Fix possible infinite redirect on attachment preview (#1488199)
- Improved clickjacking protection for browsers which don't support X-Frame-Options headers
- Fixed bug where similiar folder names were highlighted wrong (#1487860)
config/main.inc.php.dist
@@ -653,8 +653,8 @@
// use this timezone to display date/time
$rcmail_config['timezone'] = 'auto';
// is daylight saving On?
$rcmail_config['dst_active'] = (bool)date('I');
// is daylight saving On? Default: (bool)date('I');
$rcmail_config['dst_active'] = null;
// prefer displaying HTML messages
$rcmail_config['prefer_html'] = true;
installer/rcube_install.php
@@ -142,20 +142,22 @@
    foreach ($this->config as $prop => $default) {
      $value = (isset($_POST["_$prop"]) || $this->bool_config_props[$prop]) ? $_POST["_$prop"] : $default;
      $is_default = !isset($_POST["_$prop"]);
      $value      = !$is_default || $this->bool_config_props[$prop] ? $_POST["_$prop"] : $default;
      // convert some form data
      if ($prop == 'debug_level') {
        $val = 0;
        if (is_array($value))
      if ($prop == 'debug_level' && !$is_default) {
        if (is_array($value)) {
          $val = 0;
          foreach ($value as $dbgval)
            $val += intval($dbgval);
        $value = $val;
          $value = $val;
        }
      }
      else if ($which == 'db' && $prop == 'db_dsnw' && !empty($_POST['_dbtype'])) {
        if ($_POST['_dbtype'] == 'sqlite')
          $value = sprintf('%s://%s?mode=0646', $_POST['_dbtype'], $_POST['_dbname']{0} == '/' ? '/' . $_POST['_dbname'] : $_POST['_dbname']);
        else
        else if ($_POST['_dbtype'])
          $value = sprintf('%s://%s:%s@%s/%s', $_POST['_dbtype'], 
            rawurlencode($_POST['_dbuser']), rawurlencode($_POST['_dbpass']), $_POST['_dbhost'], $_POST['_dbname']);
      }
@@ -177,9 +179,9 @@
        $value = '%p';
      }
      else if ($prop == 'default_imap_folders') {
        $value = Array();
        $value = array();
        foreach ($this->config['default_imap_folders'] as $_folder) {
          switch($_folder) {
          switch ($_folder) {
          case 'Drafts': $_folder = $this->config['drafts_mbox']; break;
          case 'Sent':   $_folder = $this->config['sent_mbox']; break;
          case 'Junk':   $_folder = $this->config['junk_mbox']; break;
@@ -206,7 +208,7 @@
      // replace the matching line in config file
      $out = preg_replace(
        '/(\$rcmail_config\[\''.preg_quote($prop).'\'\])\s+=\s+(.+);/Uie',
        "'\\1 = ' . rcube_install::_dump_var(\$value) . ';'",
        "'\\1 = ' . rcube_install::_dump_var(\$value, \$prop) . ';'",
        $out);
    }
@@ -299,7 +301,7 @@
    $current = $this->config;
    $this->config = array();
    $this->load_defaults();
    foreach ($this->replaced_config as $prop => $replacement) {
      if (isset($current[$prop])) {
        if ($prop == 'skin_path')
@@ -328,9 +330,9 @@
    
    if ($current['keep_alive'] && $current['session_lifetime'] < $current['keep_alive'])
      $current['session_lifetime'] = max(10, ceil($current['keep_alive'] / 60) * 2);
    $this->config  = array_merge($this->config, $current);
    foreach ((array)$current['ldap_public'] as $key => $values) {
      $this->config['ldap_public'][$key] = $current['ldap_public'][$key];
    }
@@ -614,7 +616,22 @@
  }
  
  
  static function _dump_var($var) {
  static function _dump_var($var, $name=null) {
    // special values
    switch ($name) {
    case 'syslog_facility':
      $list = array(32 => 'LOG_AUTH', 80 => 'LOG_AUTHPRIV', 72 => ' LOG_CRON',
                    24 => 'LOG_DAEMON', 0 => 'LOG_KERN', 128 => 'LOG_LOCAL0',
                    136 => 'LOG_LOCAL1', 144 => 'LOG_LOCAL2', 152 => 'LOG_LOCAL3',
                    160 => 'LOG_LOCAL4', 168 => 'LOG_LOCAL5', 176 => 'LOG_LOCAL6',
                    184 => 'LOG_LOCAL7', 48 => 'LOG_LPR', 16 => 'LOG_MAIL',
                    56 => 'LOG_NEWS', 40 => 'LOG_SYSLOG', 8 => 'LOG_USER', 64 => 'LOG_UUCP');
      if ($val = $list[$var])
        return $val;
      break;
    }
    if (is_array($var)) {
      if (empty($var)) {
        return 'array()';
program/include/main.inc
@@ -640,20 +640,23 @@
function get_input_value($fname, $source, $allow_html=FALSE, $charset=NULL)
{
  $value = NULL;
  if ($source==RCUBE_INPUT_GET && isset($_GET[$fname]))
    $value = $_GET[$fname];
  else if ($source==RCUBE_INPUT_POST && isset($_POST[$fname]))
    $value = $_POST[$fname];
  else if ($source==RCUBE_INPUT_GPC)
    {
  if ($source == RCUBE_INPUT_GET) {
    if (isset($_GET[$fname]))
      $value = $_GET[$fname];
  }
  else if ($source == RCUBE_INPUT_POST) {
    if (isset($_POST[$fname]))
      $value = $_POST[$fname];
  }
  else if ($source == RCUBE_INPUT_GPC) {
    if (isset($_POST[$fname]))
      $value = $_POST[$fname];
    else if (isset($_GET[$fname]))
      $value = $_GET[$fname];
    else if (isset($_COOKIE[$fname]))
      $value = $_COOKIE[$fname];
    }
  }
  return parse_input_value($value, $allow_html, $charset);
}
@@ -661,7 +664,7 @@
/**
 * Parse/validate input value. See get_input_value()
 * Performs stripslashes() and charset conversion if necessary
 *
 *
 * @param  string   Input value
 * @param  boolean  Allow HTML tags in field value
 * @param  string   Charset to convert into
@@ -687,15 +690,21 @@
  else if (get_magic_quotes_gpc() || get_magic_quotes_runtime())
    $value = stripslashes($value);
  // remove HTML tags if not allowed
  // remove HTML tags if not allowed
  if (!$allow_html)
    $value = strip_tags($value);
  $output_charset = is_object($OUTPUT) ? $OUTPUT->get_charset() : null;
  // remove invalid characters (#1488124)
  if ($output_charset == 'UTF-8')
    $value = rc_utf8_clean($value);
  // convert to internal charset
  if (is_object($OUTPUT) && $charset)
    return rcube_charset_convert($value, $OUTPUT->get_charset(), $charset);
  else
    return $value;
  if ($charset && $output_charset)
    $value = rcube_charset_convert($value, $output_charset, $charset);
  return $value;
}
/**
@@ -711,10 +720,10 @@
  $src = $mode == RCUBE_INPUT_GET ? $_GET : ($mode == RCUBE_INPUT_POST ? $_POST : $_REQUEST);
  foreach ($src as $key => $value) {
    $fname = $key[0] == '_' ? substr($key, 1) : $key;
    if ($ignore && !preg_match("/($ignore)/", $fname))
    if ($ignore && !preg_match('/^(' . $ignore . ')$/', $fname))
      $out[$fname] = get_input_value($key, $mode);
  }
  return $out;
}
program/include/rcube_browser.php
@@ -33,19 +33,19 @@
        $HTTP_USER_AGENT = strtolower($_SERVER['HTTP_USER_AGENT']);
        $this->ver = 0;
        $this->win = strstr($HTTP_USER_AGENT, 'win');
        $this->mac = strstr($HTTP_USER_AGENT, 'mac');
        $this->linux = strstr($HTTP_USER_AGENT, 'linux');
        $this->unix  = strstr($HTTP_USER_AGENT, 'unix');
        $this->win = strpos($HTTP_USER_AGENT, 'win') != false;
        $this->mac = strpos($HTTP_USER_AGENT, 'mac') != false;
        $this->linux = strpos($HTTP_USER_AGENT, 'linux') != false;
        $this->unix  = strpos($HTTP_USER_AGENT, 'unix') != false;
        $this->opera = strstr($HTTP_USER_AGENT, 'opera');
        $this->ns4 = strstr($HTTP_USER_AGENT, 'mozilla/4') && !stristr($HTTP_USER_AGENT, 'msie');
        $this->ns  = ($this->ns4 || strstr($HTTP_USER_AGENT, 'netscape'));
        $this->ie  = !$this->opera && stristr($HTTP_USER_AGENT, 'compatible; msie');
        $this->mz  = !$this->ie && strstr($HTTP_USER_AGENT, 'mozilla/5');
        $this->chrome = strstr($HTTP_USER_AGENT, 'chrome');
        $this->khtml = strstr($HTTP_USER_AGENT, 'khtml');
        $this->safari = !$this->chrome && ($this->khtml || strstr($HTTP_USER_AGENT, 'safari'));
        $this->opera = strpos($HTTP_USER_AGENT, 'opera') !== false;
        $this->ns4 = strpos($HTTP_USER_AGENT, 'mozilla/4') !== false && strpos($HTTP_USER_AGENT, 'msie') === false;
        $this->ns  = ($this->ns4 || strpos($HTTP_USER_AGENT, 'netscape') !== false);
        $this->ie  = !$this->opera && strpos($HTTP_USER_AGENT, 'compatible; msie') !== false;
        $this->mz  = !$this->ie && strpos($HTTP_USER_AGENT, 'mozilla/5') !== false;
        $this->chrome = strpos($HTTP_USER_AGENT, 'chrome') !== false;
        $this->khtml = strpos($HTTP_USER_AGENT, 'khtml') !== false;
        $this->safari = !$this->chrome && ($this->khtml || strpos($HTTP_USER_AGENT, 'safari') !== false);
        if ($this->ns || $this->chrome) {
            $test = preg_match('/(mozilla|chrome)\/([0-9.]+)/', $HTTP_USER_AGENT, $regs);
program/include/rcube_config.php
@@ -90,11 +90,14 @@
        // enable display_errors in 'show' level, but not for ajax requests
        ini_set('display_errors', intval(empty($_REQUEST['_remote']) && ($this->prop['debug_level'] & 4)));
        // set timezone auto settings values
        if ($this->prop['timezone'] == 'auto') {
          $this->prop['dst_active'] = intval(date('I'));
          $this->prop['_timezone_value']   = date('Z') / 3600 - $this->prop['dst_active'];
          $this->prop['_timezone_value'] = date('Z') / 3600 - $this->prop['dst_active'];
        }
        else if ($this->prop['dst_active'] === null) {
          $this->prop['dst_active'] = intval(date('I'));
        }
        // export config data
program/include/rcube_smtp.php
@@ -381,7 +381,7 @@
          $from = $addresses[0];
        // Reject envelope From: addresses with spaces.
        if (strstr($from, ' '))
        if (strpos($from, ' ') !== false)
          return false;
        $lines[] = $key . ': ' . $value;
program/js/app.js
@@ -5743,10 +5743,13 @@
    });
  };
  this.plain2html = function(plainText, id)
  this.plain2html = function(plain, id)
  {
    var lock = this.set_busy(true, 'converting');
    $('#'+id).val(plainText ? '<pre>'+plainText+'</pre>' : '');
    plain = plain.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
    $('#'+id).val(plain ? '<pre>'+plain+'</pre>' : '');
    this.set_busy(false, null, lock);
  };
program/lib/html2text.php
@@ -145,7 +145,6 @@
    var $search = array(
        "/\r/",                                  // Non-legal carriage return
        "/[\n\t]+/",                             // Newlines and tabs
        '/[ ]{2,}/',                             // Runs of spaces, pre-handling
        '/<script[^>]*>.*?<\/script>/i',         // <script>s -- which strip_tags supposedly has problems with
        '/<style[^>]*>.*?<\/style>/i',           // <style>s -- which strip_tags supposedly has problems with
        '/<p[^>]*>/i',                           // <P>
@@ -161,22 +160,6 @@
        '/(<table[^>]*>|<\/table>)/i',           // <table> and </table>
        '/(<tr[^>]*>|<\/tr>)/i',                 // <tr> and </tr>
        '/<td[^>]*>(.*?)<\/td>/i',               // <td> and </td>
        '/&(nbsp|#160);/i',                      // Non-breaking space
        '/&(quot|rdquo|ldquo|#8220|#8221|#147|#148);/i',
                                                 // Double quotes
        '/&(apos|rsquo|lsquo|#8216|#8217);/i',   // Single quotes
        '/&gt;/i',                               // Greater-than
        '/&lt;/i',                               // Less-than
        '/&(copy|#169);/i',                      // Copyright
        '/&(trade|#8482|#153);/i',               // Trademark
        '/&(reg|#174);/i',                       // Registered
        '/&(mdash|#151|#8212);/i',               // mdash
        '/&(ndash|minus|#8211|#8722);/i',        // ndash
        '/&(bull|#149|#8226);/i',                // Bullet
        '/&(pound|#163);/i',                     // Pound sign
        '/&(euro|#8364);/i',                     // Euro sign
        '/&(amp|#38);/i',                        // Ampersand: see _converter()
        '/[ ]{2,}/'                              // Runs of spaces, post-handling
    );
    /**
@@ -189,7 +172,6 @@
    var $replace = array(
        '',                                     // Non-legal carriage return
        ' ',                                    // Newlines and tabs
        ' ',                                    // Runs of spaces, pre-handling
        '',                                     // <script>s -- which strip_tags supposedly has problems with
        '',                                     // <style>s -- which strip_tags supposedly has problems with
        "\n\n",                                 // <P>
@@ -205,6 +187,43 @@
        "\n\n",                                 // <table> and </table>
        "\n",                                   // <tr> and </tr>
        "\t\t\\1\n",                            // <td> and </td>
    );
    /**
     *  List of preg* regular expression patterns to search for,
     *  used in conjunction with $ent_replace.
     *
     *  @var array $ent_search
     *  @access public
     *  @see $ent_replace
     */
    var $ent_search = array(
        '/&(nbsp|#160);/i',                      // Non-breaking space
        '/&(quot|rdquo|ldquo|#8220|#8221|#147|#148);/i',
                                                 // Double quotes
        '/&(apos|rsquo|lsquo|#8216|#8217);/i',   // Single quotes
        '/&gt;/i',                               // Greater-than
        '/&lt;/i',                               // Less-than
        '/&(copy|#169);/i',                      // Copyright
        '/&(trade|#8482|#153);/i',               // Trademark
        '/&(reg|#174);/i',                       // Registered
        '/&(mdash|#151|#8212);/i',               // mdash
        '/&(ndash|minus|#8211|#8722);/i',        // ndash
        '/&(bull|#149|#8226);/i',                // Bullet
        '/&(pound|#163);/i',                     // Pound sign
        '/&(euro|#8364);/i',                     // Euro sign
        '/&(amp|#38);/i',                        // Ampersand: see _converter()
        '/[ ]{2,}/',                             // Runs of spaces, post-handling
    );
    /**
     *  List of pattern replacements corresponding to patterns searched.
     *
     *  @var array $ent_replace
     *  @access public
     *  @see $ent_search
     */
    var $ent_replace = array(
        ' ',                                    // Non-breaking space
        '"',                                    // Double quotes
        "'",                                    // Single quotes
@@ -219,7 +238,7 @@
        '£',
        'EUR',                                  // Euro sign. € ?
        '|+|amp|+|',                            // Ampersand: see _converter()
        ' '                                     // Runs of spaces, post-handling
        ' ',                                    // Runs of spaces, post-handling
    );
    /**
@@ -303,7 +322,7 @@
     *  @see _build_link_list()
     */
    var $_link_list = '';
    /**
     *  Number of valid links detected in the text, used for plain text
     *  display (rendered similar to footnotes).
@@ -314,15 +333,15 @@
     */
    var $_link_count = 0;
    /**
     * Boolean flag, true if a table of link URLs should be listed after the text.
     *
     * @var boolean $_do_links
     * @access private
     * @see html2text()
    /**
     * Boolean flag, true if a table of link URLs should be listed after the text.
     *
     * @var boolean $_do_links
     * @access private
     * @see html2text()
     */
    var $_do_links = true;
    /**
     *  Constructor.
     *
@@ -492,14 +511,20 @@
        // Convert <PRE>
        $this->_convert_pre($text);
        // Run our defined search-and-replace
        // Run our defined tags search-and-replace
        $text = preg_replace($this->search, $this->replace, $text);
        // Run our defined tags search-and-replace with callback
        $text = preg_replace_callback($this->callback_search, array('html2text', '_preg_callback'), $text);
        // Strip any other HTML tags
        $text = strip_tags($text, $this->allowed_tags);
        // Run our defined entities/characters search-and-replace
        $text = preg_replace($this->ent_search, $this->ent_replace, $text);
        // Replace known html entities
        $text = html_entity_decode($text, ENT_COMPAT, 'UTF-8');
        // Run our defined search-and-replace with callback
        $text = preg_replace_callback($this->callback_search, array('html2text', '_preg_callback'), $text);
        // Remove unknown/unhandled entities (this cannot be done in search-and-replace block)
        $text = preg_replace('/&([a-zA-Z0-9]{2,6}|#[0-9]{2,4});/', '', $text);
@@ -508,15 +533,12 @@
        // This properly handles situation of "&amp;quot;" in input string
        $text = str_replace('|+|amp|+|', '&', $text);
        // Strip any other HTML tags
        $text = strip_tags($text, $this->allowed_tags);
        // Bring down number of empty lines to 2 max
        $text = preg_replace("/\n\s+\n/", "\n\n", $text);
        $text = preg_replace("/[\n]{3,}/", "\n\n", $text);
        // remove leading empty lines (can be produced by eg. P tag on the beginning)
        $text = preg_replace('/^\n+/', '', $text);
        $text = ltrim($text, "\n");
        // Wrap the text to a readable format
        // for PHP versions >= 4.0.2. Default width is 75
@@ -544,9 +566,7 @@
        if ( !$this->_do_links )
            return $display;
        if ( substr($link, 0, 7) == 'http://' || substr($link, 0, 8) == 'https://' ||
            substr($link, 0, 7) == 'mailto:'
        ) {
        if ( preg_match('!^(https?://|mailto:)!', $link) ) {
            $this->_link_count++;
            $this->_link_list .= '[' . $this->_link_count . "] $link\n";
            $additional = ' [' . $this->_link_count . ']';
program/steps/addressbook/func.inc
@@ -705,7 +705,7 @@
    $RCMAIL->output->set_env('photo_placeholder', $photo_img);
    unset($attrib['placeholder']);
    if (strpos($record['photo'], 'http:') === 0)
    if (preg_match('!^https?://!i', $record['photo']))
        $photo_img = $record['photo'];
    else if ($record['photo'])
        $photo_img = $RCMAIL->url(array('_action' => 'photo', '_cid' => $record['ID'], '_source' => $SOURCE_ID));
program/steps/mail/search.inc
@@ -31,6 +31,7 @@
$mbox    = get_input_value('_mbox', RCUBE_INPUT_GET, true);
$filter  = get_input_value('_filter', RCUBE_INPUT_GET);
$headers = get_input_value('_headers', RCUBE_INPUT_GET);
$subject = array();
$search_request = md5($mbox.$filter.$str);
@@ -70,14 +71,19 @@
  list(,$srch) = explode(":", $str);
  $subject['text'] = "TEXT";
}
else if(trim($str))
else if (strlen(trim($str)))
{
  if ($headers) {
    foreach(explode(',', $headers) as $header)
      switch ($header) {
        case 'text': $subject['text'] = 'TEXT'; break;
        default:     $subject[$header] = 'HEADER '.strtoupper($header);
    foreach (explode(',', $headers) as $header) {
      if ($header == 'text') {
        // #1488208: get rid of other headers when searching by "TEXT"
        $subject = array('text' => 'TEXT');
        break;
      }
      else {
        $subject[$header] = 'HEADER '.strtoupper($header);
      }
    }
    // save search modifiers for the current folder to user prefs
    $search_mods = $RCMAIL->config->get('search_mods', $SEARCH_MODS_DEFAULT);
@@ -89,9 +95,9 @@
  }
}
$search = $srch ? trim($srch) : trim($str);
$search = isset($srch) ? trim($srch) : trim($str);
if ($subject) {
if (!empty($subject)) {
  $search_str .= str_repeat(' OR', count($subject)-1);
  foreach ($subject as $sub)
    $search_str .= sprintf(" %s {%d}\r\n%s", $sub, strlen($search), $search);
skins/default/functions.js
@@ -192,28 +192,31 @@
  if (show && ref) {
    var pos = $(ref).offset();
    obj.css({ left:pos.left, top:(pos.top + ref.offsetHeight + 2)})
        .find(':checked').prop('checked', false);
    obj.css({left:pos.left, top:(pos.top + ref.offsetHeight + 2)});
    if (rcmail.env.search_mods) {
      var n, mbox = rcmail.env.mailbox, mods = rcmail.env.search_mods;
      var n, all,
        list = $('input:checkbox[name="s_mods[]"]', obj),
        mbox = rcmail.env.mailbox,
        mods = rcmail.env.search_mods;
      if (rcmail.env.task != 'addressbook') {
      if (rcmail.env.task == 'mail') {
        mods = mods[mbox] ? mods[mbox] : mods['*'];
        for (n in mods)
          $('#s_mod_' + n).prop('checked', true);
        all = 'text';
      }
      else {
        if (mods['*'])
          $('input:checkbox[name="s_mods[]"]').map(function() {
            this.checked = true;
            this.disabled = this.value != '*';
          });
        else {
          for (n in mods)
            $('#s_mod_' + n).prop('checked', true);
        }
        all = '*';
      }
      if (mods[all])
        list.map(function() {
          this.checked = true;
          this.disabled = this.value != all;
        });
      else {
        list.prop('disabled', false).prop('checked', false);
        for (n in mods)
          $('#s_mod_' + n).prop('checked', true);
      }
    }
  }
@@ -222,7 +225,7 @@
set_searchmod: function(elem)
{
  var task = rcmail.env.task,
  var all, m, task = rcmail.env.task,
    mods = rcmail.env.search_mods,
    mbox = rcmail.env.mailbox;
@@ -232,36 +235,37 @@
  if (task == 'mail') {
    if (!mods[mbox])
      mods[mbox] = rcube_clone_object(mods['*']);
    if (!elem.checked)
      delete(mods[mbox][elem.value]);
    else
      mods[mbox][elem.value] = 1;
    m = mods[mbox];
    all = 'text';
  }
  else { //addressbook
    if (!elem.checked)
      delete(mods[elem.value]);
    else
      mods[elem.value] = 1;
    // mark all fields
    if (elem.value == '*') {
      $('input:checkbox[name="s_mods[]"]').map(function() {
        if (this == elem)
          return;
        if (elem.checked) {
          mods[this.value] = 1;
          this.checked = true;
          this.disabled = true;
        }
        else {
          this.disabled = false;
        }
      });
    }
    m = mods;
    all = '*';
  }
  rcmail.env.search_mods = mods;
  if (!elem.checked)
    delete(m[elem.value]);
  else
    m[elem.value] = 1;
  // mark all fields
  if (elem.value != all)
    return;
  $('input:checkbox[name="s_mods[]"]').map(function() {
    if (this == elem)
      return;
    this.checked = true;
    if (elem.checked) {
      this.disabled = true;
      delete m[this.value];
    }
    else {
      this.disabled = false;
      m[this.value] = 1;
    }
  });
},
listmenu: function(show)
@@ -566,7 +570,6 @@
      rcmail.addEventListener('responseaftergetunread', rcube_render_mailboxlist);
      rcmail.addEventListener('responseaftercheck-recent', rcube_render_mailboxlist);
      rcmail.addEventListener('aftercollapse-folder', rcube_render_mailboxlist);
      rcube_render_mailboxlist();
    }
    if (rcmail.env.action == 'compose')
@@ -588,12 +591,16 @@
// Abbreviate mailbox names to fit width of the container
function rcube_render_mailboxlist()
{
  if (bw.ie6)  // doesn't work well on IE6
  var list = $('#mailboxlist > li a, #mailboxlist ul:visible > li a');
  // it's too slow with really big number of folders, especially on IE
  if (list.length > 500 * (bw.ie ? 0.2 : 1))
    return;
  $('#mailboxlist > li a, #mailboxlist ul:visible > li a').each(function(){
    var elem = $(this);
    var text = elem.data('text');
  list.each(function(){
    var elem = $(this),
      text = elem.data('text');
    if (!text) {
      text = elem.text().replace(/\s+\(.+$/, '');
      elem.data('text', text);
@@ -611,34 +618,45 @@
// inspired by https://gist.github.com/24261/7fdb113f1e26111bd78c0c6fe515f6c0bf418af5
function fit_string_to_size(str, elem, len)
{
    var result = str;
    var ellip = '...';
    var span = $('<b>').css({ visibility:'hidden', padding:'0px' }).appendTo(elem).get(0);
  var w, span, result = str, ellip = '...';
    // on first run, check if string fits into the length already.
    span.innerHTML = result;
    if (span.offsetWidth > len) {
        var cut = Math.max(1, Math.floor(str.length * ((span.offsetWidth - len) / span.offsetWidth) / 2)),
          mid = Math.floor(str.length / 2);
        var offLeft = mid, offRight = mid;
        while (true) {
            offLeft = mid - cut;
            offRight = mid + cut;
            span.innerHTML = str.substring(0,offLeft) + ellip + str.substring(offRight);
  if (!rcmail.env.tmp_span) {
    // it should be appended to elem to use the same css style
    // but for performance reasons we'll append it to body (once)
    span = $('<b>').css({visibility: 'hidden', padding: '0px'})
      .appendTo($('body', document)).get(0);
    rcmail.env.tmp_span = span;
  }
  else {
    span = rcmail.env.tmp_span;
  }
  span.innerHTML = result;
            // break loop if string fits size
            if (span.offsetWidth <= len || offLeft < 3)
              break;
  // on first run, check if string fits into the length already.
  w = span.offsetWidth;
  if (w > len) {
    var cut = Math.max(1, Math.floor(str.length * ((w - len) / w) / 2)),
      mid = Math.floor(str.length / 2),
      offLeft = mid,
      offRight = mid;
            cut++;
        }
    while (true) {
      offLeft = mid - cut;
      offRight = mid + cut;
      span.innerHTML = str.substring(0,offLeft) + ellip + str.substring(offRight);
        // build resulting string
        result = str.substring(0,offLeft) + ellip + str.substring(offRight);
      // break loop if string fits size
      if (offLeft < 3 || span.offsetWidth)
        break;
      cut++;
    }
    span.parentNode.removeChild(span);
    return result;
    // build resulting string
    result = str.substring(0,offLeft) + ellip + str.substring(offRight);
  }
  return result;
}
// Optional parameters used by TinyMCE
tests/mailfunc.php
@@ -106,7 +106,7 @@
    $part = $this->get_html_part('src/invalidchars.html');
    $washed = rcmail_print_body($part);
    $this->assertPattern('/<p>сОЌвПл<\/p>/', $washed, "Remove non-unicode characters from HTML message body");
    $this->assertPattern('/<p>символ<\/p>/', $washed, "Remove non-unicode characters from HTML message body");
  }
  /**