Thomas Bruederli
2013-08-14 341fa8b9a6207000d057e00678d96ada6136f783
Merge branch 'release-0.9' of github.com:roundcube/roundcubemail into release-0.9
13 files modified
128 ■■■■ changed files
.htaccess 2 ●●● patch | view | raw | blame | history
CHANGELOG 8 ●●●●● patch | view | raw | blame | history
program/js/app.js 2 ●●● patch | view | raw | blame | history
program/lib/Roundcube/rcube_base_replacer.php 4 ●●●● patch | view | raw | blame | history
program/lib/Roundcube/rcube_charset.php 14 ●●●●● patch | view | raw | blame | history
program/lib/Roundcube/rcube_image.php 4 ●●●● patch | view | raw | blame | history
program/lib/Roundcube/rcube_string_replacer.php 2 ●●● patch | view | raw | blame | history
program/steps/mail/compose.inc 16 ●●●● patch | view | raw | blame | history
program/steps/mail/func.inc 8 ●●●● patch | view | raw | blame | history
program/steps/settings/edit_identity.inc 3 ●●●● patch | view | raw | blame | history
program/steps/settings/save_identity.inc 46 ●●●●● patch | view | raw | blame | history
tests/Framework/BaseReplacer.php 14 ●●●●● patch | view | raw | blame | history
tests/Framework/StringReplacer.php 5 ●●●●● patch | view | raw | blame | history
.htaccess
@@ -30,7 +30,7 @@
RewriteEngine On
RewriteRule ^favicon\.ico$ skins/larry/images/favicon.ico
# security rules
RewriteRule .git - [F]
RewriteRule \.git - [F]
RewriteRule ^/?(README(.md)?|INSTALL|LICENSE|CHANGELOG|UPGRADING)$ - [F]
RewriteRule ^/?(SQL|bin) - [F]
</IfModule>
CHANGELOG
@@ -1,6 +1,14 @@
CHANGELOG Roundcube Webmail
===========================
- Fix purge action in folder manager (#1489280)
- 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 saving HTML signatures (#1489251)
- Fix XSS vulnerability when editing a message "as new" or draft (#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)
- Fix parsing of links with ... in URL (#1489192)
- Fix compose priority selector when opening in new window (#1489257)
program/js/app.js
@@ -388,7 +388,7 @@
        }
        else if (this.env.action == 'edit-folder' && this.gui_objects.editform) {
          this.enable_command('save', 'folder-size', true);
          parent.rcmail.env.messagecount = this.env.messagecount;
          parent.rcmail.env.exists = this.env.messagecount;
          parent.rcmail.enable_command('purge', this.env.messagecount);
          $("input[type='text']").first().select();
        }
program/lib/Roundcube/rcube_base_replacer.php
@@ -44,8 +44,8 @@
    public function replace($body)
    {
        return preg_replace_callback(array(
            '/(src|background|href)=(["\']?)([^"\'\s]+)(\2|\s|>)/Ui',
            '/(url\s*\()(["\']?)([^"\'\)\s]+)(\2)\)/Ui',
            '/(src|background|href)=(["\']?)([^"\'\s>]+)(\2|\s|>)/i',
            '/(url\s*\()(["\']?)([^"\'\)\s]+)(\2)\)/i',
        ),
        array($this, 'callback'), $body);
    }
program/lib/Roundcube/rcube_charset.php
@@ -674,23 +674,27 @@
            // Prioritize charsets according to current language (#1485669)
            switch ($language) {
            case 'ja_JP': // for Japanese
            case 'ja_JP':
                $prio = array('ISO-2022-JP', 'JIS', 'UTF-8', 'EUC-JP', 'eucJP-win', 'SJIS', 'SJIS-win');
                break;
            case 'zh_CN': // for Chinese (Simplified)
            case 'zh_TW': // for Chinese (Traditional)
            case 'zh_CN':
            case 'zh_TW':
                $prio = array('UTF-8', 'BIG-5', 'GB2312', 'EUC-TW');
                break;
            case 'ko_KR': // for Korean
            case 'ko_KR':
                $prio = array('UTF-8', 'EUC-KR', 'ISO-2022-KR');
                break;
            case 'ru_RU': // for Russian
            case 'ru_RU':
                $prio = array('UTF-8', 'WINDOWS-1251', 'KOI8-R');
                break;
            case 'tr_TR':
                $prio = array('UTF-8', 'ISO-8859-9', 'WINDOWS-1254');
                break;
            default:
                $prio = array('UTF-8', 'SJIS', 'GB2312',
                    'ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4',
program/lib/Roundcube/rcube_image.php
@@ -120,7 +120,7 @@
            $p['-opts'] = array('-resize' => $p['size'].'>');
            if (in_array($type, explode(',', $p['types']))) { // Valid type?
                $result = rcube::exec($convert . ' 2>&1 -flatten -auto-orient -colorspace RGB -quality {quality} {-opts} {intype}:{in} {type}:{out}', $p);
                $result = rcube::exec($convert . ' 2>&1 -flatten -auto-orient -colorspace sRGB -quality {quality} {-opts} {intype}:{in} {type}:{out}', $p);
            }
            if ($result === '') {
@@ -222,7 +222,7 @@
            $p['out']  = $filename;
            $p['type'] = self::$extensions[$type];
            $result = rcube::exec($convert . ' 2>&1 -colorspace RGB -quality 75 {in} {type}:{out}', $p);
            $result = rcube::exec($convert . ' 2>&1 -colorspace sRGB -quality 75 {in} {type}:{out}', $p);
            if ($result === '') {
                @chmod($filename, 0600);
program/lib/Roundcube/rcube_string_replacer.php
@@ -36,7 +36,7 @@
        // Support unicode/punycode in top-level domain part
        $utf_domain = '[^?&@"\'\\/()<>\s\r\t\n]+\\.?([^\\x00-\\x2f\\x3b-\\x40\\x5b-\\x60\\x7b-\\x7f]{2,}|xn--[a-zA-Z0-9]{2,})';
        $url1       = '.:;,';
        $url2       = 'a-zA-Z0-9%=#$@+?!&\\/_~\\[\\]\\(\\){}\*-';
        $url2       = 'a-zA-Z0-9%=#$@+?|!&\\/_~\\[\\]\\(\\){}\*-';
        $this->link_pattern = "/([\w]+:\/\/|\W[Ww][Ww][Ww]\.|^[Ww][Ww][Ww]\.)($utf_domain([$url1]*[$url2]+)*)/";
        $this->mailto_pattern = "/("
program/steps/mail/compose.inc
@@ -198,7 +198,10 @@
  if (!empty($MESSAGE->headers->charset))
    $RCMAIL->storage->set_charset($MESSAGE->headers->charset);
  if ($compose_mode == RCUBE_COMPOSE_REPLY) {
  if (!$MESSAGE->headers) {
    // error
  }
  else if ($compose_mode == RCUBE_COMPOSE_REPLY) {
    $COMPOSE['reply_uid'] = $msg_uid;
    $COMPOSE['reply_msgid'] = $MESSAGE->headers->messageID;
    $COMPOSE['references']  = trim($MESSAGE->headers->references . " " . $MESSAGE->headers->messageID);
@@ -980,10 +983,19 @@
      && count($MESSAGE->mime_parts) > 0)
  {
    $cid_map = rcmail_write_compose_attachments($MESSAGE, $bodyIsHtml);
  }
  // clean up HTML tags - XSS prevention (#1489251)
  if ($bodyIsHtml) {
    $body = rcmail_wash_html($body, array('safe' => 1), $cid_map);
    // remove comments (produced by washtml)
    $body = preg_replace('/<!--[^>]+-->/', '', $body);
    // replace cid with href in inline images links
    if ($cid_map)
    if (!empty($cid_map)) {
      $body = str_replace(array_keys($cid_map), array_values($cid_map), $body);
    }
  }
  return $body;
program/steps/mail/func.inc
@@ -1803,8 +1803,14 @@
    // Try Return-Path
    if ($from_idx === null && ($return_path = $MESSAGE->headers->others['return-path'])) {
        foreach ($identities as $idx => $ident) {
            // Return-Path header contains an email address, but on some mailing list
            // it can be e.g. <pear-dev-return-55250-local=domain.tld@lists.php.net>
            // where local@domain.tld is the address we're looking for (#1489241)
            $ident1 = $ident['email_ascii'];
            $ident2 = str_replace('@', '=', $ident1);
            foreach ((array)$return_path as $path) {
                if (stripos($path, $ident['email_ascii']) !== false) {
                if (stripos($path, $ident1) !== false || stripos($path, $ident2)) {
                    $from_idx = $idx;
                    break 2;
                }
program/steps/settings/edit_identity.inc
@@ -77,7 +77,7 @@
    'signature' => array(
      'name' => rcube_label('signature'),
      'content' => array(
        'signature'         => array('type' => 'textarea', 'size' => $t_cols, 'rows' => $t_rows,
        'signature'      => array('type' => 'textarea', 'size' => $t_cols, 'rows' => $t_rows,
            'spellcheck' => true),
        'html_signature' => array('type' => 'checkbox', 'label' => rcube_label('htmlsignature'),
            'onclick' => 'return rcmail_toggle_editor(this, \'rcmfd_signature\');'),
@@ -138,6 +138,7 @@
        $label = !empty($colprop['label']) ? $colprop['label'] :
            rcube_label(str_replace('-', '', $col));
        $value = !empty($colprop['value']) ? $colprop['value'] :
            rcmail_get_edit_field($col, $IDENTITY_RECORD[$col], $colprop, $colprop['type']);
program/steps/settings/save_identity.inc
@@ -76,6 +76,15 @@
  }
}
// XSS protection in HTML signature (#1489251)
if (!empty($save_data['signature']) && !empty($save_data['html_signature'])) {
  $save_data['signature'] = rcmail_wash_html($save_data['signature']);
  // clear POST data of signature, we want to use safe content
  // when the form is displayed again
  unset($_POST['_signature']);
}
// update an existing contact
if ($_POST['_iid']) {
  $iid = get_input_value('_iid', RCUBE_INPUT_POST);
@@ -167,3 +176,40 @@
}
else
  rcmail_overwrite_action('identities');
/**
 * Sanity checks/cleanups on HTML body of signature
 */
function rcmail_wash_html($html)
{
    // Add header with charset spec., washtml cannot work without that
    $html = '<html><head>'
        . '<meta http-equiv="Content-Type" content="text/html; charset='.RCMAIL_CHARSET.'" />'
        . '</head><body>' . $html . '</body></html>';
    // clean HTML with washhtml by Frederic Motte
    $wash_opts = array(
        'show_washed' => false,
        'allow_remote' => 1,
        'charset' => RCMAIL_CHARSET,
        'html_elements' => array('body', 'link'),
        'html_attribs' => array('rel', 'type'),
    );
    // initialize HTML washer
    $washer = new rcube_washtml($wash_opts);
    //$washer->add_callback('form', 'rcmail_washtml_callback');
    //$washer->add_callback('style', 'rcmail_washtml_callback');
    // Remove non-UTF8 characters (#1487813)
    $html = rc_utf8_clean($html);
    $html = $washer->wash($html);
    // remove unwanted comments and tags (produced by washtml)
    $html = preg_replace(array('/<!--[^>]+-->/', '/<\/?body>/'), '', $html);
  return $html;
}
tests/Framework/BaseReplacer.php
@@ -17,4 +17,18 @@
        $this->assertInstanceOf('rcube_base_replacer', $object, "Class constructor");
    }
    /**
     * Test replace()
     */
    function test_replace()
    {
        $base = 'http://thisshouldntbetheurl.bob.com/';
        $html = '<A href=http://shouldbethislink.com>Test URL</A>';
        $replacer = new rcube_base_replacer($base);
        $response = $replacer->replace($html);
        $this->assertSame('<A href="http://shouldbethislink.com">Test URL</A>', $response);
    }
}
tests/Framework/StringReplacer.php
@@ -27,7 +27,7 @@
            array('http://domain.tld/path*path2', '<a href="http://domain.tld/path*path2" target="_blank">http://domain.tld/path*path2</a>'),
            array("Click this link:\nhttps://mail.xn--brderli-o2a.ch/rc/ EOF", "Click this link:\n<a href=\"https://mail.xn--brderli-o2a.ch/rc/\" target=\"_blank\">https://mail.xn--brderli-o2a.ch/rc/</a> EOF"),
            array('Start http://localhost/?foo End', 'Start <a href="http://localhost/?foo" target="_blank">http://localhost/?foo</a> End'),
            array('http://localhost/?foo=bar. Period', '<a href="http://localhost/?foo=bar">http://localhost/?foo=bar</a>. Period'),
            array('http://localhost/?foo=bar. Period', '<a href="http://localhost/?foo=bar" target="_blank">http://localhost/?foo=bar</a>. Period'),
            array('www.domain.tld', '<a href="http://www.domain.tld" target="_blank">www.domain.tld</a>'),
            array('WWW.DOMAIN.TLD', '<a href="http://WWW.DOMAIN.TLD" target="_blank">WWW.DOMAIN.TLD</a>'),
            array('[http://link.com]', '[<a href="http://link.com" target="_blank">http://link.com</a>]'),
@@ -36,11 +36,12 @@
            array('(http://link.com)', '(<a href="http://link.com" target="_blank">http://link.com</a>)'),
            array('http://link.com?a(b)c', '<a href="http://link.com?a(b)c" target="_blank">http://link.com?a(b)c</a>'),
            array('http://link.com?(link)', '<a href="http://link.com?(link)" target="_blank">http://link.com?(link)</a>'),
            array('https://github.com/a/b/compare/3a0f82...1f4b2a after', '<a href="https://github.com/a/b/compare/3a0f82...1f4b2a">https://github.com/a/b/compare/3a0f82...1f4b2a</a> after'),
            array('https://github.com/a/b/compare/3a0f82...1f4b2a after', '<a href="https://github.com/a/b/compare/3a0f82...1f4b2a" target="_blank">https://github.com/a/b/compare/3a0f82...1f4b2a</a> after'),
            array('http://<test>', 'http://<test>'),
            array('http://', 'http://'),
            array('1@1.com www.domain.tld', '<a href="mailto:1@1.com">1@1.com</a> <a href="http://www.domain.tld" target="_blank">www.domain.tld</a>'),
            array(' www.domain.tld ', ' <a href="http://www.domain.tld" target="_blank">www.domain.tld</a> '),
            array(' www.domain.tld/#!download|856p1|2 ', ' <a href="http://www.domain.tld/#!download|856p1|2" target="_blank">www.domain.tld/#!download|856p1|2</a> '),
        );
    }