Merge branch 'release-0.9' of github.com:roundcube/roundcubemail into release-0.9
| | |
| | | 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 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) |
| | |
| | | } |
| | | 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(); |
| | | } |
| | |
| | | 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); |
| | | } |
| | |
| | | |
| | | // 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', |
| | |
| | | $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 === '') { |
| | |
| | | $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); |
| | |
| | | // 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 = "/(" |
| | |
| | | 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); |
| | |
| | | && 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; |
| | |
| | | // 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; |
| | | } |
| | |
| | | '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\');'), |
| | |
| | | |
| | | $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']); |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | // 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); |
| | |
| | | } |
| | | 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; |
| | | } |
| | |
| | | |
| | | $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); |
| | | } |
| | | } |
| | |
| | | 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>]'), |
| | |
| | | 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> '), |
| | | ); |
| | | } |
| | | |