Aleksander Machniak
2013-09-20 b334a01791689a16c8d6b10955df850b891ff40e
Merge branch 'master' of github.com:roundcube/roundcubemail
24 files modified
458 ■■■■ changed files
CHANGELOG 27 ●●●●● patch | view | raw | blame | history
config/defaults.inc.php 6 ●●●●● patch | view | raw | blame | history
plugins/password/config.inc.php.dist 3 ●●●● patch | view | raw | blame | history
plugins/password/drivers/virtualmin.php 4 ●●●● patch | view | raw | blame | history
program/include/rcmail_output_html.php 17 ●●●● patch | view | raw | blame | history
program/include/rcmail_output_json.php 7 ●●●●● patch | view | raw | blame | history
program/js/app.js 36 ●●●●● patch | view | raw | blame | history
program/js/editor.js 3 ●●●●● patch | view | raw | blame | history
program/lib/Roundcube/rcube_config.php 2 ●●● patch | view | raw | blame | history
program/steps/mail/attachments.inc 5 ●●●● patch | view | raw | blame | history
program/steps/mail/compose.inc 27 ●●●● patch | view | raw | blame | history
program/steps/mail/sendmail.inc 5 ●●●●● patch | view | raw | blame | history
program/steps/mail/show.inc 5 ●●●●● patch | view | raw | blame | history
program/steps/settings/edit_prefs.inc 9 ●●●●● patch | view | raw | blame | history
program/steps/settings/func.inc 48 ●●●●● patch | view | raw | blame | history
program/steps/settings/save_prefs.inc 1 ●●●● patch | view | raw | blame | history
skins/classic/mail.css 6 ●●●● patch | view | raw | blame | history
skins/classic/templates/message.html 2 ●●● patch | view | raw | blame | history
skins/classic/templates/messagepreview.html 2 ●●● patch | view | raw | blame | history
skins/larry/addressbook.css 5 ●●●●● patch | view | raw | blame | history
skins/larry/settings.css 20 ●●●●● patch | view | raw | blame | history
skins/larry/templates/importcontacts.html 2 ●●● patch | view | raw | blame | history
skins/larry/ui.js 13 ●●●●● patch | view | raw | blame | history
tests/Framework/Browser.php 203 ●●●●● patch | view | raw | blame | history
CHANGELOG
@@ -1,6 +1,9 @@
CHANGELOG Roundcube Webmail
===========================
- Make default font size for HTML messages configurable (request #118)
- Display full attachment name using title attribute when name is too long to display (#1489320)
- Fix XSS issue in addressbook group name field [CVE-2013-5646] (#1489333)
- Fix attachment icon issue when rare font/language is used (#1489326)
- After message is sent refresh messages list of replied message folder (#1489249)
- Add option force specified domain in user login - username_domain_forced (#1489264)
@@ -96,8 +99,8 @@
- Fix base URL resolving on attribute values with no quotes (#1489275)
- Fix wrong handling of links with '|' character (#1489276)
- Fix colorspace issue on image conversion using ImageMagick (#1489270)
- Fix XSS vulnerability when editing a message "as new" or draft (#1489251)
- Fix XSS vulnerability when saving HTML signatures (#1489251)
- Fix XSS vulnerability when editing a message "as new" or draft [CVE-2013-5645] (#1489251)
- Fix XSS vulnerability when saving HTML signatures [CVE-2013-5645] (#1489251)
- Fix rewrite rule in .htaccess (#1489240)
- Fix detecting Turkish language in ISO-8859-9 encoding (#1489252)
- Fix identity-selection using Return-Path headers (#1489241)
@@ -317,7 +320,7 @@
- Fix #countcontrols issue in IE<=8 when text is very long (#1488890)
- Fix unwanted horizontal scrollbar in message preview header (#1488866)
- Add workaround for IE<=8 bug where Content-Disposition:inline was ignored (#1488844)
- Fix XSS vulnerability in vbscript: and data:text links handling (#1488850)
- Fix XSS vulnerability in vbscript: and data:text links handling [CVE-2012-6121] (#1488850)
- Fix absolute positioning in HTML messages (#1488819)
- Fix cache (in)validation after setting \Deleted flag
- Fix keybord events on messages list in opera browser (#1488823)
@@ -372,8 +375,8 @@
- Fix bug where domain name was converted to lower-case even with login_lc=false (#1488593)
- Fix lower-casing email address on replies (#1488598)
- Fix line separator in exported messages (#1488603)
- Fix XSS issue where plain signatures wasn't secured in HTML mode (#1488613)
- Fix XSS issue where href="javascript:" wasn't secured (#1488613)
- Fix XSS issue where plain signatures wasn't secured in HTML mode [CVE-2012-4668] (#1488613)
- Fix XSS issue where href="javascript:" wasn't secured [CVE-2012-3508] (#1488613)
- Fix impossible to create message with empty plain text part (#1488610)
- Fix stripped apostrophes when replying in plain text to HTML message (#1488606)
- Fix inactive Save search option after advanced search (#1488607)
@@ -408,7 +411,7 @@
- Fix removing contact photo using LDAP addressbook (#1488420)
- Fix storing X-ANNIVERSARY date in vCard format (#1488527)
- Update to Mail_Mime-1.8.5 (#1488521)
- Fix XSS vulnerability in message subject handling using Larry skin (#1488519)
- Fix XSS vulnerability in message subject handling using Larry skin [CVE-2012-3507] (#1488519)
- Fix handling of links with various URI schemes e.g. "skype:" (#1488106)
- Fix handling of links inside PRE elements on html to text conversion
- Fix indexing of links on html to text conversion
@@ -535,7 +538,7 @@
- Improved handling of some malformed values encoded with quoted-printable (#1488232)
- Add possibility to do LDAP bind before searching for bind DN
- Fix handling of empty <U> tags in HTML messages (#1488225)
- Add content filter for embedded attachments to protect from XSS on IE (#1487895)
- Add content filter for embedded attachments to protect from XSS on IE [CVE-2012-1253] (#1487895)
- 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)
@@ -703,7 +706,7 @@
RELEASE 0.5.4
-------------
- Fix XSS vulnerability in UI messages (#1488030)
- Fix XSS vulnerability in UI messages [CVE-2011-2937] (#1488030)
RELEASE 0.5.3
-------------
@@ -753,8 +756,8 @@
- Security: add optional referer check to prevent CSRF in GET requests
- Fix email_dns_check setting not used for identities/contacts (#1487740)
- Fix ICANN example addresses doesn't validate (#1487742)
- Security: protect login form submission from CSRF
- Security: prevent from relaying malicious requests through modcss.inc
- Security: protect login form submission from CSRF [CVE-2011-1491]
- Security: prevent from relaying malicious requests through modcss.inc [CVE-2011-1492]
- Fix handling of non-image attachments in multipart/related messages (#1487750)
- Fix IDNA support when IDN/INTL modules are in use (#1487742)
- Fix handling of invalid HTML comments in messages (#1487759)
@@ -1197,7 +1200,7 @@
---------------
- Fix import of vCard entries with params (#1485453)
- Fix HTML messages output with empty block elements (#1485974)
- Use request tokens to protect POST requests from CSRF
- Use request tokens to protect POST requests from CSRF [CVE-2009-4076, CVE-2009-4077]
- Added hook when killing a session
- Added hook to write_log function (#1485971)
- Performance improvements by use UID commands (#1485690)
@@ -1324,7 +1327,7 @@
- Fix large search results on server without SORT capability (#1485668)
- Get rid of preg_replace() with eval modifier and create_function usage (#1485686)
- Bring back <base> and <link> tags in HTML messages
- Fix XSS vulnerability through background attributes as reported by Julien Cayssol
- Fix XSS vulnerability through background attributes [CVE-2009-0413]
- Fix problems with backslash as IMAP hierarchy delimiter (#1484467)
- Secure vcard export by getting rid of preg's 'e' modifier use (#1485689)
- Fix authentication when submitting form with existing session (#1485679)
config/defaults.inc.php
@@ -245,6 +245,8 @@
// replace Roundcube logo with this image
// specify an URL relative to the document root of this Roundcube installation
// an array can be used to specify different logos for specific template files, '*' for default logo
// for example array("*" => "/images/roundcube_logo.png", "messageprint" => "/images/roundcube_logo_print.png")
$config['skin_logo'] = null;
// automatically create a new Roundcube user when log-in the first time.
@@ -982,5 +984,9 @@
// Georgia, Helvetica, Impact, Tahoma, Terminal, Times New Roman, Trebuchet MS, Verdana
$config['default_font'] = 'Verdana';
// Default font size for composed HTML message.
// Supported sizes: 8pt, 10pt, 12pt, 14pt, 18pt, 24pt, 36pt
$config['default_font_size'] = '10pt';
// Enables display of email address with name instead of a name (and address in title)
$config['message_show_email'] = false;
plugins/password/config.inc.php.dist
@@ -320,8 +320,7 @@
// 5: domain-username
// 6: username_domain
// 7: domain_username
// 8: username@domain; mbox.username
$config['password_virtualmin_format'] = 8;
$config['password_virtualmin_format'] = 0;
// pw_usermod Driver options
plugins/password/drivers/virtualmin.php
@@ -48,10 +48,6 @@
            $pieces = explode("_", $username);
            $domain = $pieces[0];
            break;
        case 8: // domain taken from alias, username left as it was
            $email = $rcmail->user->data['alias'];
            $domain = substr(strrchr($email, "@"), 1);
            break;
        default: // username@domain
            $domain = substr(strrchr($username, "@"), 1);
        }
program/include/rcmail_output_html.php
@@ -924,8 +924,21 @@
                }
                else if ($object == 'logo') {
                    $attrib += array('alt' => $this->xml_command(array('', 'object', 'name="productname"')));
                    if ($logo = $this->config->get('skin_logo'))
                        $attrib['src'] = $logo;
                    if ($logo = $this->config->get('skin_logo')) {
                        if (is_array($logo)) {
                            if ($template_logo = $logo[$this->template_name]) {
                                $attrib['src'] = $template_logo;
                            }
                            elseif ($template_logo = $logo['*']) {
                                $attrib['src'] = $template_logo;
                            }
                        }
                        else {
                            $attrib['src'] = $logo;
                        }
                    }
                    $content = html::img($attrib);
                }
                else if ($object == 'productname') {
program/include/rcmail_output_json.php
@@ -227,6 +227,13 @@
        if (!empty($this->callbacks))
            $response['callbacks'] = $this->callbacks;
        // trigger generic hook where plugins can put additional content to the response
        $hook = $this->app->plugins->exec_hook("render_response", array('response' => $response));
        // save some memory
        $response = $hook['response'];
        unset($hook['response']);
        echo self::json_serialize($response);
    }
program/js/app.js
@@ -1881,7 +1881,7 @@
        html = expando;
      else if (c == 'subject') {
        if (bw.ie) {
          col.onmouseover = function() { rcube_webmail.long_subject_title_ie(this, message.depth+1); };
          col.onmouseover = function() { rcube_webmail.long_subject_title_ex(this, message.depth+1); };
          if (bw.ie8)
            tree = '<span></span>' + tree; // #1487821
        }
@@ -3427,7 +3427,8 @@
      message = input_message.val(),
      is_html = ($("input[name='_is_html']").val() == '1'),
      sig = this.env.identity,
      delim = this.env.recipients_delimiter,
      delim = this.env.recipients_separator,
      rx_delim = RegExp.escape(delim),
      headers = ['replyto', 'bcc'];
    // update reply-to/bcc fields with addresses defined in identities
@@ -3444,16 +3445,18 @@
      }
      // cleanup
      rx = new RegExp(RegExp.escape(delim) + '\\s*' + RegExp(delim), 'g');
      input_val = input_val.replace(rx, delim)
      rx = new RegExp('^\\s*' + RegExp.escape(delim) + '\\s*$');
      input_val = input_val.replace(rx, '')
      rx = new RegExp(rx_delim + '\\s*' + rx_delim, 'g');
      input_val = input_val.replace(rx, delim);
      rx = new RegExp('^[\\s' + rx_delim + ']+');
      input_val = input_val.replace(rx, '');
      // add new address(es)
      if (new_val) {
        rx = new RegExp(RegExp.escape(delim) + '\\s*$');
        if (input_val && !rx.test(input_val))
          input_val += delim + ' ';
      if (new_val && input_val.indexOf(new_val) == -1 && input_val.indexOf(new_val.replace(/"/g, '')) == -1) {
        if (input_val) {
          rx = new RegExp('[' + rx_delim + '\\s]+$')
          input_val = input_val.replace(rx, '') + delim + ' ';
        }
        input_val += new_val + delim + ' ';
      }
@@ -3639,7 +3642,12 @@
      att.html = '<a title="'+this.get_label('cancel')+'" onclick="return rcmail.cancel_attachment_upload(\''+name+'\', \''+att.frame+'\');" href="#cancelupload" class="cancelupload">'
        + (this.env.cancelicon ? '<img src="'+this.env.cancelicon+'" alt="" />' : this.get_label('cancel')) + '</a>' + att.html;
    var indicator, li = $('<li>').attr('id', name).addClass(att.classname).html(att.html);
    var indicator, li = $('<li>');
    li.attr('id', name)
      .addClass(att.classname)
      .html(att.html)
      .on('mouseover', function() { rcube_webmail.long_subject_title_ex(this, 0); });
    // replace indicator's li
    if (upload_id && (indicator = document.getElementById(upload_id))) {
@@ -4345,7 +4353,7 @@
        boxtitle.append('&nbsp;&raquo;&nbsp;');
      }
      boxtitle.append($('<span>'+prop.name+'</span>'));
      boxtitle.append($('<span>').text(prop.name));
    }
    this.triggerEvent('groupupdate', prop);
@@ -6986,11 +6994,11 @@
  if (!elem.title) {
    var $elem = $(elem);
    if ($elem.width() + indent * 15 > $elem.parent().width())
      elem.title = $elem.html();
      elem.title = $elem.text();
  }
};
rcube_webmail.long_subject_title_ie = function(elem, indent)
rcube_webmail.long_subject_title_ex = function(elem, indent)
{
  if (!elem.title) {
    var $elem = $(elem),
program/js/editor.js
@@ -80,6 +80,9 @@
  if (rcmail.env.default_font)
    $(tinyMCE.get(rcmail.env.composebody).getBody()).css('font-family', rcmail.env.default_font);
  if (rcmail.env.default_font_size)
    $(tinyMCE.get(rcmail.env.composebody).getBody()).css('font-size', rcmail.env.default_font_size);
  if (elem && elem.type == 'select-one') {
    rcmail.change_identity(elem);
    // Focus previously focused element
program/lib/Roundcube/rcube_config.php
@@ -214,7 +214,7 @@
                    $success = true;
                }
                // deprecated name of config variable
                else if (is_array($rcmail_config)) {
                if (is_array($rcmail_config)) {
                    $this->merge($rcmail_config);
                    $success = true;
                }
program/steps/mail/attachments.inc
@@ -118,9 +118,12 @@
          'alt' => rcube_label('delete')
        ));
      }
      else {
      else if ($COMPOSE['textbuttons']) {
        $button = Q(rcube_label('delete'));
      }
      else {
        $button = '';
      }
      $content = html::a(array(
        'href' => "#delete",
program/steps/mail/compose.inc
@@ -148,6 +148,9 @@
  $OUTPUT->set_env('default_font', $font);
}
// default font size for HTML editor
$OUTPUT->set_env('default_font_size', $RCMAIL->config->get('default_font_size'));
// get reference message and set compose mode
if ($msg_uid = $COMPOSE['param']['draft_uid']) {
  $compose_mode = RCUBE_COMPOSE_DRAFT;
@@ -1370,8 +1373,9 @@
  if (!$attrib['id'])
    $attrib['id'] = 'rcmAttachmentList';
  $out = "\n";
  $out    = "\n";
  $jslist = array();
  $button = '';
  if (is_array($COMPOSE['attachments'])) {
    if ($attrib['deleteicon']) {
@@ -1380,27 +1384,38 @@
        'alt' => rcube_label('delete')
      ));
    }
    else
    else if (rcube_utils::get_boolean($attrib['textbuttons'])) {
      $button = Q(rcube_label('delete'));
    }
    foreach ($COMPOSE['attachments'] as $id => $a_prop) {
      if (empty($a_prop))
        continue;
      $out .= html::tag('li', array('id' => 'rcmfile'.$id, 'class' => rcmail_filetype2classname($a_prop['mimetype'], $a_prop['name'])),
      $out .= html::tag('li',
        array(
          'id'          => 'rcmfile'.$id,
          'class'       => rcmail_filetype2classname($a_prop['mimetype'], $a_prop['name']),
          'onmouseover' => "rcube_webmail.long_subject_title_ex(this, 0)",
        ),
        html::a(array(
            'href' => "#delete",
            'title' => rcube_label('delete'),
            'onclick' => sprintf("return %s.command('remove-attachment','rcmfile%s', this)", JS_OBJECT_NAME, $id),
            'class' => 'delete'),
          $button) . Q($a_prop['name']));
            'class' => 'delete'
          ),
          $button
        ) . Q($a_prop['name'])
      );
        $jslist['rcmfile'.$id] = array('name' => $a_prop['name'], 'complete' => true, 'mimetype' => $a_prop['mimetype']);
      $jslist['rcmfile'.$id] = array('name' => $a_prop['name'], 'complete' => true, 'mimetype' => $a_prop['mimetype']);
    }
  }
  if ($attrib['deleteicon'])
    $COMPOSE['deleteicon'] = $CONFIG['skin_path'] . $attrib['deleteicon'];
  else if (rcube_utils::get_boolean($attrib['textbuttons']))
    $COMPOSE['textbuttons'] = true;
  if ($attrib['cancelicon'])
    $OUTPUT->set_env('cancelicon', $CONFIG['skin_path'] . $attrib['cancelicon']);
  if ($attrib['loadingicon'])
program/steps/mail/sendmail.inc
@@ -473,8 +473,9 @@
$message_body = get_input_value('_message', RCUBE_INPUT_POST, TRUE, $message_charset);
if ($isHtml) {
  $font   = rcube_fontdefs($RCMAIL->config->get('default_font'));
  $bstyle = $font && is_string($font) ? " style='font-family: $font'" : '';
  $font_family = rcube_fontdefs($RCMAIL->config->get('default_font', 'Arial'));
  $font_size = $RCMAIL->config->get('default_font_size');
  $bstyle = ' style="font:' . $font_size . ' ' . $font_family . ';"';
  // append doctype and html/body wrappers
  $message_body = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN">' .
program/steps/mail/show.inc
@@ -175,9 +175,9 @@
        $ol .= html::tag('li', null, Q(sprintf("%s (%s)", $filename, $size)));
      }
      else {
        if (mb_strlen($filename) > 50) {
        if ($attrib['maxlength'] && mb_strlen($filename) > $attrib['maxlength']) {
          $title    = $filename;
          $filename = abbreviate_string($filename, 50);
          $filename = abbreviate_string($filename, $attrib['maxlength']);
        }
        else {
          $title = '';
@@ -190,6 +190,7 @@
            'href' => $MESSAGE->get_part_url($attach_prop->mime_id, false),
            'onclick' => sprintf('return %s.command(\'load-attachment\',\'%s\',this)',
              JS_OBJECT_NAME, $attach_prop->mime_id),
            'onmouseover' => $title ? '' : 'rcube_webmail.long_subject_title_ex(this, 0)',
            'title' => Q($title),
            ), Q($filename));
        $ol .= html::tag('li', array('class' => $class, 'id' => $id), $link);
program/steps/settings/edit_prefs.inc
@@ -40,24 +40,21 @@
  $out = $form_start;
  foreach ($SECTIONS[$CURR_SECTION]['blocks'] as $block) {
  foreach ($SECTIONS[$CURR_SECTION]['blocks'] as $class => $block) {
    if (!empty($block['options'])) {
      $table = new html_table(array('cols' => 2));
      foreach ($block['options'] as $option) {
        if ($option['advanced'])
          $table->set_row_attribs('advanced');
        if (isset($option['title'])) {
          $table->add('title', $option['title']);
            $table->add(null, $option['content']);
          $table->add(null, $option['content']);
        }
        else {
          $table->add(array('colspan' => 2), $option['content']);
        }
      }
      $out .= html::tag('fieldset', null, html::tag('legend', null, $block['name']) . $table->show($attrib));
      $out .= html::tag('fieldset', $class, html::tag('legend', null, $block['name']) . $table->show($attrib));
    }
    else if (!empty($block['content'])) {
      $out .= html::tag('fieldset', null, html::tag('legend', null, $block['name']) . $block['content']);
program/steps/settings/func.inc
@@ -158,6 +158,7 @@
                'main'    => array('name' => Q(rcube_label('mainoptions'))),
                'skin'    => array('name' => Q(rcube_label('skin'))),
                'browser' => array('name' => Q(rcube_label('browseroptions'))),
                'advanced'=> array('name' => Q(rcube_label('advancedoptions'))),
            );
            // language selection
@@ -367,6 +368,7 @@
            $blocks = array(
                'main'        => array('name' => Q(rcube_label('mainoptions'))),
                'new_message' => array('name' => Q(rcube_label('newmessage'))),
                'advanced'    => array('name' => Q(rcube_label('advancedoptions'))),
            );
            // show config parameter for preview pane
@@ -488,6 +490,7 @@
        case 'mailview':
            $blocks = array(
                'main' => array('name' => Q(rcube_label('mainoptions'))),
                'advanced'   => array('name' => Q(rcube_label('advancedoptions'))),
            );
            // show checkbox to open message view in new window
@@ -543,7 +546,7 @@
                $field_id = 'rcmfd_default_charset';
                $blocks['main']['options']['default_charset'] = array(
                $blocks['advanced']['options']['default_charset'] = array(
                    'title' => html::label($field_id, Q(rcube_label('defaultcharset'))),
                    'content' => $RCMAIL->output->charset_selector(array(
                        'id' => $field_id, 'name' => '_default_charset', 'selected' => $config['default_charset']
@@ -605,6 +608,7 @@
                'main'       => array('name' => Q(rcube_label('mainoptions'))),
                'sig'        => array('name' => Q(rcube_label('signatureoptions'))),
                'spellcheck' => array('name' => Q(rcube_label('spellcheckoptions'))),
                'advanced'   => array('name' => Q(rcube_label('advancedoptions'))),
            );
            // show checkbox to compose messages in a new window
@@ -673,8 +677,7 @@
                $select->add(rcube_label('miscfolding'), 1);
                $select->add(rcube_label('2047folding'), 2);
                $blocks['main']['options']['mime_param_folding'] = array(
                    'advanced' => true,
                $blocks['advanced']['options']['mime_param_folding'] = array(
                    'title'    => html::label($field_id, Q(rcube_label('mimeparamfolding'))),
                    'content'  => $select->show($config['mime_param_folding']),
                );
@@ -688,8 +691,7 @@
                $field_id = 'rcmfd_force_7bit';
                $input    = new html_checkbox(array('name' => '_force_7bit', 'id' => $field_id, 'value' => 1));
                $blocks['main']['options']['force_7bit'] = array(
                    'advanced' => true,
                $blocks['advanced']['options']['force_7bit'] = array(
                    'title'    => html::label($field_id, Q(rcube_label('force7bit'))),
                    'content'  => $input->show($config['force_7bit']?1:0),
                );
@@ -842,23 +844,28 @@
                    continue 2;
                }
                $field_id = 'rcmfd_default_font';
                $fonts    = rcube_fontdefs();
                $selected = $config['default_font'];
                // Default font size
                $field_id = 'rcmfd_default_font_size';
                $select_default_font_size = new html_select(array('name' => '_default_font_size', 'id' => $field_id));
                $select = '<select name="_default_font" id="'.$field_id.'">';
                $select .= '<option value=""' . (!$selected ? ' selected="selected"' : '') . '>---</option>';
                foreach ($fonts as $fname => $font) {
                    $select .= '<option value="'.$fname.'"'
                        . ($fname == $selected ? ' selected="selected"' : '')
                        . ' style=\'font-family: ' . $font . '\'>'
                        . Q($fname) . '</option>';
                $fontsizes = array('8pt', '10pt', '12pt', '14pt', '18pt', '24pt', '36pt');
                foreach ($fontsizes as $size) {
                    $select_default_font_size->add($size, $size);
                }
                $select .= '</select>';
                // Default font
                $field_id = 'rcmfd_default_font';
                $select_default_font = new html_select(array('name' => '_default_font', 'id' => $field_id));
                $fonts = rcube_fontdefs();
                foreach ($fonts as $fname => $font) {
                    $select_default_font->add($fname, $fname);
                }
                $blocks['main']['options']['default_font'] = array(
                    'title' => html::label($field_id, Q(rcube_label('defaultfont'))),
                    'content' => $select
                    'content' => $select_default_font->show($RCMAIL->config->get('default_font', 1)) .
                        $select_default_font_size->show($RCMAIL->config->get('default_font_size', 1))
                );
            }
        break;
@@ -866,7 +873,8 @@
        // Addressbook config
        case 'addressbook':
            $blocks = array(
                'main' => array('name' => Q(rcube_label('mainoptions'))),
                'main'     => array('name' => Q(rcube_label('mainoptions'))),
                'advanced' => array('name' => Q(rcube_label('advancedoptions'))),
            );
            if (!isset($no_override['default_addressbook'])
@@ -962,7 +970,8 @@
        // Special IMAP folders
        case 'folders':
            $blocks = array(
                'main' => array('name' => Q(rcube_label('mainoptions'))),
                'main'     => array('name' => Q(rcube_label('mainoptions'))),
                'advanced' => array('name' => Q(rcube_label('advancedoptions'))),
            );
            if (!isset($no_override['show_real_foldernames'])) {
@@ -1043,6 +1052,7 @@
            $blocks = array(
                'main'        => array('name' => Q(rcube_label('mainoptions'))),
                'maintenance' => array('name' => Q(rcube_label('maintenance'))),
                'advanced'    => array('name' => Q(rcube_label('advancedoptions'))),
            );
            if (!isset($no_override['read_when_deleted'])) {
program/steps/settings/save_prefs.inc
@@ -89,6 +89,7 @@
      'reply_mode'         => isset($_POST['_reply_mode']) ? intval($_POST['_reply_mode']) : 0,
      'strip_existing_sig' => isset($_POST['_strip_existing_sig']),
      'default_font'       => get_input_value('_default_font', RCUBE_INPUT_POST),
      'default_font_size'  => get_input_value('_default_font_size', RCUBE_INPUT_POST),
      'forward_attachment' => !empty($_POST['_forward_attachment']),
    );
skins/classic/mail.css
@@ -1594,9 +1594,7 @@
  height: 18px;
  line-height: 16px;
  font-size: 11px;
  padding-left: 2px;
  padding-top: 2px;
  padding-right: 4px;
  padding: 2px 2px 1px 2px;
  border-bottom: 1px solid #EBEBEB;
  white-space: nowrap;
  overflow: hidden;
@@ -1609,8 +1607,10 @@
  text-indent: -5000px;
  width: 17px;
  height: 16px;
  padding-bottom: 2px;
  display: inline-block;
  text-decoration: none;
  vertical-align: middle;
}
#compose-attachments li img
skins/classic/templates/message.html
@@ -49,7 +49,7 @@
  </div>
<roundcube:object name="messageHeaders" class="headers-table" cellspacing="0" cellpadding="2" addicon="/images/icons/silhouette.png" summary="Message headers" />
<roundcube:object name="messageFullHeaders" id="full-headers" />
<roundcube:object name="messageAttachments" id="attachment-list" />
<roundcube:object name="messageAttachments" id="attachment-list" maxlength="50" />
<roundcube:object name="messageObjects" id="message-objects" />
<roundcube:object name="messageBody" id="messagebody" />
</div>
skins/classic/templates/messagepreview.html
@@ -20,7 +20,7 @@
  </div>
<roundcube:object name="messageHeaders" class="headers-table" cellspacing="0" cellpadding="2" addicon="/images/icons/silhouette.png" summary="Message headers" />
<roundcube:object name="messageFullHeaders" id="full-headers" />
<roundcube:object name="messageAttachments" id="attachment-list" />
<roundcube:object name="messageAttachments" id="attachment-list" maxlength="50" />
</div>
<roundcube:object name="messageObjects" id="message-objects" />
skins/larry/addressbook.css
@@ -387,3 +387,8 @@
    overflow: auto;
    padding: 10px;
}
#import-box p,
#import-box .propform {
  max-width: 50em;
}
skins/larry/settings.css
@@ -48,6 +48,26 @@
    border-radius: 4px 4px 0 0;
}
#preferences-details fieldset.advanced legend {
    position: relative;
    display: block;
    width: 100%;
    cursor: pointer;
}
#preferences-details fieldset.advanced .propform {
    display: none;
}
#preferences-details fieldset.advanced .advanced-toggle {
    position: absolute;
    top: 2px;
    right: 6px;
    text-decoration: none;
    color: #666;
    font-size: 11px;
}
#sections-table tbody td.section,
#settings-sections span.listitem a,
#settings-sections span.tablink a {
skins/larry/templates/importcontacts.html
@@ -18,7 +18,7 @@
<h2 class="boxtitle"><roundcube:label name="importcontacts" /></h2>
<div id="import-box" class="boxcontent">
<roundcube:object name="importstep" />
<roundcube:object name="importstep" class="propform" />
<br/>
<p class="formbuttons">
    <roundcube:object name="importnav" class="button" />
skins/larry/ui.js
@@ -195,6 +195,19 @@
        new rcube_splitter({ id:'prefviewsplitter', p1:'#sectionslist', p2:'#preferences-box',
          orientation:'v', relative:true, start:266, min:180, size:12 }).init();
      }
      else if (rcmail.env.action == 'edit-prefs') {
        $('<a href="#toggle">&#9660;</a>')
            .addClass('advanced-toggle')
            .appendTo('#preferences-details fieldset.advanced legend');
          $('#preferences-details fieldset.advanced legend').click(function(e){
            var collapsed = $(this).hasClass('collapsed'),
              toggle = $('.advanced-toggle', this).html(collapsed ? '&#9650;' : '&#9660;');
            $(this)
              .toggleClass('collapsed')
              .closest('fieldset').children('.propform').toggle()
          }).addClass('collapsed')
      }
    }
    /***  addressbook task  ***/
    else if (rcmail.env.task == 'addressbook') {
tests/Framework/Browser.php
@@ -17,4 +17,207 @@
        $this->assertInstanceOf('rcube_browser', $object, "Class constructor");
    }
    /**
     * @dataProvider browsers
     */
    function test_browser($useragent, $opera, $chrome, $ie, $ns, $ns4, $khtml, $safari, $mz)
    {
        $object = $this->getBrowser($useragent);
        $this->assertEquals($opera, $object->opera, 'Check for Opera failed');
        $this->assertEquals($chrome, $object->chrome, 'Check for Chrome failed');
        $this->assertEquals($ie, $object->ie, 'Check for IE failed');
        $this->assertEquals($ns, $object->ns, 'Check for NS failed');
        $this->assertEquals($ns4, $object->ns4, 'Check for NS4 failed');
        $this->assertEquals($khtml, $object->khtml, 'Check for khtml failed');
        $this->assertEquals($safari, $object->safari, 'Check for Safari failed');
        $this->assertEquals($mz, $object->mz, 'Check for MZ failed');
    }
    /**
     * @dataProvider os
     */
    function test_os($useragent, $windows, $linux, $unix, $mac)
    {
        $object = $this->getBrowser($useragent);
        $this->assertEquals($windows, $object->win, 'Check Result of Windows');
        $this->assertEquals($linux, $object->linux, 'Check Result of Linux');
        $this->assertEquals($mac, $object->mac, 'Check Result of Mac');
        $this->assertEquals($unix, $object->unix, 'Check Result of Unix');
    }
    /**
     * @dataProvider versions
     */
    function test_version($useragent, $version)
    {
        $object = $this->getBrowser($useragent);
        $this->assertEquals($version, $object->ver);
    }
    /**
     * @dataProvider dom
     */
    function test_dom($useragent, $dom)
    {
        $object = $this->getBrowser($useragent);
        $this->assertEquals($dom, $object->dom);
    }
    /**
     * @dataProvider pngalpha
     */
    function test_pngalpha($useragent, $pngalpha)
    {
        $object = $this->getBrowser($useragent);
        $this->assertEquals($pngalpha, $object->pngalpha);
    }
    /**
     * @dataProvider imgdata
     */
    function test_imgdata($useragent, $imgdata)
    {
        $object = $this->getBrowser($useragent);
        $this->assertEquals($imgdata, $object->imgdata);
    }
    function versions()
    {
        return $this->extractDataSet(array('version'));
    }
    function pngalpha()
    {
        return $this->extractDataSet(array('canPNGALPHA'));
    }
    function imgdata()
    {
        return $this->extractDataSet(array('canIMGDATA'));
    }
    private function extractDataSet($keys)
    {
        $keys = array_merge(array('useragent'), $keys);
        $browser = $this->useragents();
        $extracted = array();
        foreach ($browser as $label => $data) {
            foreach($keys as $key) {
                $extracted[$data['useragent']][] = $data[$key];
            }
        }
        return $extracted;
    }
    function lang()
    {
        return $this->extractDataSet(array('lang'));
    }
    function dom()
    {
        return $this->extractDataSet(array('hasDOM'));
    }
    function browsers()
    {
        return $this->extractDataSet(array('isOpera','isChrome','isIE','isNS','isNS4','isKHTML','isSafari','isMZ'));
    }
    function useragents()
    {
        return array(
             'WIN: Mozilla Firefox ' => array(
                 'useragent'    => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.1) Gecko/20060111 Firefox/1.5.0.1',
                 'version'      => '1.8',                                                                                      //Version
                 'isWin'        => true,                                                                                           //isWindows
                 'isLinux'      => false,
                 'isMac'        => false,                                                                                           //isMac
                 'isUnix'       => false,                                                                                           //isUnix
                 'isOpera'      => false,                                                                                           //isOpera
                 'isChrome'     => false,                                                                                           //isChrome
                 'isIE'         => false,                                                                                           //isIE
                 'isNS'         => false,                                                                                           //isNS
                 'isNS4'        => false,                                                                                           //isNS4
                 'isKHTML'      => false,                                                                                           //isKHTML
                 'isSafari'     => false,                                                                                           //isSafari
                 'isMZ'         => true,                                                                                           //isMZ
                 'lang'         => 'en-US',                                                                               //lang
                 'hasDOM'       => true,                                                                                            //hasDOM
                 'canPNGALPHA'  => true,                                                                                            //canPNGALPHA
                 'canIMGDATA'   => true,                                                                                            //canIMGDATA
             ),
            'LINUX: Bon Echo ' => array(
                 'useragent'    => 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.1) Gecko/20070222 BonEcho/2.0.0.1',
                 'version'      => '1.8',                                                                                      //Version
                 'isWin'        => false,                                                                                           //isWindows
                 'isLinux'      => true,
                 'isMac'        => false,                                                                                           //isMac
                 'isUnix'       => false,                                                                                           //isUnix
                 'isOpera'      => false,                                                                                           //isOpera
                 'isChrome'     => false,                                                                                           //isChrome
                 'isIE'         => false,                                                                                           //isIE
                 'isNS'         => false,                                                                                           //isNS
                 'isNS4'        => false,                                                                                           //isNS4
                 'isKHTML'      => false,                                                                                           //isKHTML
                 'isSafari'     => false,                                                                                           //isSafari
                 'isMZ'         => true,                                                                                           //isMZ
                 'lang'         => 'en-US',                                                                               //lang
                 'hasDOM'       => true,                                                                                            //hasDOM
                 'canPNGALPHA'  => true,                                                                                            //canPNGALPHA
                 'canIMGDATA'   => true,                                                                                            //canIMGDATA
             ),
            'Chrome Mac' => array(
                 'useragent'    => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.461.0 Safari/534.3',
                 'version'      => '5',                                                                                      //Version
                 'isWin'        => false,                                                                                           //isWindows
                 'isLinux'      => false,
                 'isMac'        => true,                                                                                           //isMac
                 'isUnix'       => false,                                                                                           //isUnix
                 'isOpera'      => false,                                                                                           //isOpera
                 'isChrome'     => true,                                                                                           //isChrome
                 'isIE'         => false,                                                                                           //isIE
                 'isNS'         => false,                                                                                           //isNS
                 'isNS4'        => false,                                                                                           //isNS4
                 'isKHTML'      => true,                                                                                           //isKHTML
                 'isSafari'     => false,                                                                                           //isSafari
                 'isMZ'         => false,                                                                                           //isMZ
                 'lang'         => 'en-US',                                                                               //lang
                 'hasDOM'       => false,                                                                                            //hasDOM
                 'canPNGALPHA'  => false,                                                                                            //canPNGALPHA
                 'canIMGDATA'   => true,                                                                                            //canIMGDATA
             ),
        );
    }
    function os()
    {
        return $this->extractDataSet(array('isWin','isLinux','isUnix','isMac'));
    }
    /**
     * @param string $useragent
     * @return rcube_browser
     */
    private function getBrowser($useragent)
    {
        /** @var $object rcube_browser */
        $_SERVER['HTTP_USER_AGENT'] = $useragent;
        $object = new rcube_browser();
        return $object;
    }
}