From 0fa54df638a0b0f514d1bfba3cefb93e38991a35 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Sat, 1 Dec 2012 20:02:34 +0100 Subject: enriched.inc -> rcube_enriched --- program/include/bc.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'program/include') diff --git a/program/include/bc.php b/program/include/bc.php index 5047e0a84..12110c0ad 100644 --- a/program/include/bc.php +++ b/program/include/bc.php @@ -399,7 +399,11 @@ function get_boolean($str) return rcube_utils::get_boolean($str); } +function enriched_to_html($data) +{ + return rcube_enriched::to_html($data); +} + class rcube_html_page extends rcmail_html_page { - } -- cgit v1.2.3 From b01d84d0f7c8b29c9dc52c717708f28a72ecf08e Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 11 Dec 2012 12:02:45 +0100 Subject: Extend just_parse() method with conditions parsing --- program/include/rcmail_output_html.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'program/include') diff --git a/program/include/rcmail_output_html.php b/program/include/rcmail_output_html.php index 1290e173e..76342c245 100644 --- a/program/include/rcmail_output_html.php +++ b/program/include/rcmail_output_html.php @@ -670,7 +670,10 @@ class rcmail_output_html extends rcmail_output */ public function just_parse($input) { - return $this->parse_xml($input); + $input = $this->parse_conditions($input); + $input = $this->parse_xml($input); + + return $input; } -- cgit v1.2.3 From a072247dde60497f879b2a00790a6ec0a64fab4c Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Sun, 16 Dec 2012 17:03:01 +0100 Subject: Fix package definitions and include framework classes in phpdoc --- bin/makedoc.sh | 5 +++-- program/include/bc.php | 1 + program/lib/Roundcube/rcube.php | 3 ++- program/lib/Roundcube/rcube_db.php | 2 +- program/lib/Roundcube/rcube_message_header.php | 3 ++- 5 files changed, 9 insertions(+), 5 deletions(-) (limited to 'program/include') diff --git a/bin/makedoc.sh b/bin/makedoc.sh index 40c75bf47..2a34254cb 100755 --- a/bin/makedoc.sh +++ b/bin/makedoc.sh @@ -1,10 +1,11 @@ #!/bin/sh -TITLE="Roundcube Classes" +TITLE="Roundcube Webmail" PACKAGES="Core" INSTALL_PATH="`dirname $0`/.." PATH_PROJECT=$INSTALL_PATH/program/include +PATH_FRAMEWORK=$INSTALL_PATH/program/lib/Roundcube PATH_DOCS=$INSTALL_PATH/doc/phpdoc BIN_PHPDOC="`/usr/bin/which phpdoc`" @@ -20,6 +21,6 @@ TEMPLATE=earthli PRIVATE=off # make documentation -$BIN_PHPDOC -d $PATH_PROJECT -t $PATH_DOCS -ti "$TITLE" -dn $PACKAGES \ +$BIN_PHPDOC -d $PATH_PROJECT,$PATH_FRAMEWORK -t $PATH_DOCS -ti "$TITLE" -dn $PACKAGES \ -o $OUTPUTFORMAT:$CONVERTER:$TEMPLATE -pp $PRIVATE diff --git a/program/include/bc.php b/program/include/bc.php index 12110c0ad..dc4d54fd7 100644 --- a/program/include/bc.php +++ b/program/include/bc.php @@ -23,6 +23,7 @@ * Roundcube Webmail deprecated functions * * @package Core + * @subpackage Legacy * @author Thomas Bruederli */ diff --git a/program/lib/Roundcube/rcube.php b/program/lib/Roundcube/rcube.php index cc4905a14..a127eeb4f 100644 --- a/program/lib/Roundcube/rcube.php +++ b/program/lib/Roundcube/rcube.php @@ -1266,7 +1266,8 @@ class rcube /** * Lightweight plugin API class serving as a dummy if plugins are not enabled * - * @package Core + * @package Framework + * @subpackage Core */ class rcube_dummy_plugin_api { diff --git a/program/lib/Roundcube/rcube_db.php b/program/lib/Roundcube/rcube_db.php index 2c471e74d..e6e8c2ede 100644 --- a/program/lib/Roundcube/rcube_db.php +++ b/program/lib/Roundcube/rcube_db.php @@ -25,7 +25,7 @@ * This is a wrapper for the PHP PDO. * * @package Framework - * @sbpackage Database + * @subpackage Database */ class rcube_db { diff --git a/program/lib/Roundcube/rcube_message_header.php b/program/lib/Roundcube/rcube_message_header.php index 7009a00af..16a0aaac9 100644 --- a/program/lib/Roundcube/rcube_message_header.php +++ b/program/lib/Roundcube/rcube_message_header.php @@ -257,7 +257,8 @@ class rcube_message_header /** * Class for sorting an array of rcube_message_header objects in a predetermined order. * - * @package Mail + * @package Framework + * @subpackage Storage * @author Aleksander Machniak */ class rcube_message_header_sorter -- cgit v1.2.3 From dfc57863d1b054534f8e0ce8e3babb38d4fe89cb Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 18 Dec 2012 09:45:20 +0100 Subject: Plugin API: Added message_before_send hook --- CHANGELOG | 1 + program/include/rcmail.php | 27 +++++++++++++++++++-------- 2 files changed, 20 insertions(+), 8 deletions(-) (limited to 'program/include') diff --git a/CHANGELOG b/CHANGELOG index 2deecd233..a89e02930 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Plugin API: Added message_before_send hook - Fix contact copy/add-to-group operations on search result (#1488862) - Use matching identity in MDN response (#1488864) - Fix unwanted horizontal scrollbar in message preview header (#1488866) diff --git a/program/include/rcmail.php b/program/include/rcmail.php index 8e01a2155..249bd0559 100644 --- a/program/include/rcmail.php +++ b/program/include/rcmail.php @@ -934,15 +934,26 @@ class rcmail extends rcube * @param object $message Reference to Mail_MIME object * @param string $from Sender address string * @param array $mailto Array of recipient address strings - * @param array $smtp_error SMTP error array (reference) + * @param array $error SMTP error array (reference) * @param string $body_file Location of file with saved message body (reference), * used when delay_file_io is enabled - * @param array $smtp_opts SMTP options (e.g. DSN request) + * @param array $options SMTP options (e.g. DSN request) * * @return boolean Send status. */ - public function deliver_message(&$message, $from, $mailto, &$smtp_error, &$body_file = null, $smtp_opts = null) + public function deliver_message(&$message, $from, $mailto, &$error, &$body_file = null, $options = null) { + $plugin = $this->plugins->exec_hook('message_before_send', array( + 'message' => $message, + 'from' => $from, + 'mailto' => $mailto, + 'options' => $options, + )); + + $from = $plugin['from']; + $mailto = $plugin['mailto']; + $options = $plugin['options']; + $message = $plugin['message']; $headers = $message->headers(); // send thru SMTP server using custom SMTP library @@ -985,15 +996,15 @@ class rcmail extends rcube $this->smtp_init(true); } - $sent = $this->smtp->send_mail($from, $a_recipients, $smtp_headers, $msg_body, $smtp_opts); - $smtp_response = $this->smtp->get_response(); - $smtp_error = $this->smtp->get_error(); + $sent = $this->smtp->send_mail($from, $a_recipients, $smtp_headers, $msg_body, $options); + $response = $this->smtp->get_response(); + $error = $this->smtp->get_error(); // log error if (!$sent) { self::raise_error(array('code' => 800, 'type' => 'smtp', 'line' => __LINE__, 'file' => __FILE__, - 'message' => "SMTP error: ".join("\n", $smtp_response)), TRUE, FALSE); + 'message' => "SMTP error: ".join("\n", $response)), TRUE, FALSE); } } // send mail using PHP's mail() function @@ -1061,7 +1072,7 @@ class rcmail extends rcube $this->user->get_username(), $_SERVER['REMOTE_ADDR'], $mailto, - !empty($smtp_response) ? join('; ', $smtp_response) : '')); + !empty($response) ? join('; ', $response) : '')); } } -- cgit v1.2.3 From 7ac94421bf85eb04c00c5ed05390e1ea0c6bcb0b Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 25 Dec 2012 18:06:17 +0100 Subject: Move washtml class into Roundcube Framework (rcube_washtml), add some improvements --- program/include/bc.php | 4 + program/lib/Roundcube/rcube_washtml.php | 451 ++++++++++++++++++++++++++++++++ program/lib/washtml.php | 330 ----------------------- program/steps/mail/func.inc | 68 +---- tests/Framework/Washtml.php | 28 ++ tests/MailFunc.php | 2 +- tests/phpunit.xml | 1 + 7 files changed, 486 insertions(+), 398 deletions(-) create mode 100644 program/lib/Roundcube/rcube_washtml.php delete mode 100644 program/lib/washtml.php create mode 100644 tests/Framework/Washtml.php (limited to 'program/include') diff --git a/program/include/bc.php b/program/include/bc.php index dc4d54fd7..05d15b9e3 100644 --- a/program/include/bc.php +++ b/program/include/bc.php @@ -408,3 +408,7 @@ function enriched_to_html($data) class rcube_html_page extends rcmail_html_page { } + +class washtml extends rcube_washtml +{ +} diff --git a/program/lib/Roundcube/rcube_washtml.php b/program/lib/Roundcube/rcube_washtml.php new file mode 100644 index 000000000..715c46047 --- /dev/null +++ b/program/lib/Roundcube/rcube_washtml.php @@ -0,0 +1,451 @@ + | + | Author: Aleksander Machniak | + | Author: Frederic Motte | + +-----------------------------------------------------------------------+ + */ + +/** + * Washtml, a HTML sanityzer. + * + * Copyright (c) 2007 Frederic Motte + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * OVERVIEW: + * + * Wahstml take an untrusted HTML and return a safe html string. + * + * SYNOPSIS: + * + * $washer = new washtml($config); + * $washer->wash($html); + * It return a sanityzed string of the $html parameter without html and head tags. + * $html is a string containing the html code to wash. + * $config is an array containing options: + * $config['allow_remote'] is a boolean to allow link to remote images. + * $config['blocked_src'] string with image-src to be used for blocked remote images + * $config['show_washed'] is a boolean to include washed out attributes as x-washed + * $config['cid_map'] is an array where cid urls index urls to replace them. + * $config['charset'] is a string containing the charset of the HTML document if it is not defined in it. + * $washer->extlinks is a reference to a boolean that is set to true if remote images were removed. (FE: show remote images link) + * + * INTERNALS: + * + * Only tags and attributes in the static lists $html_elements and $html_attributes + * are kept, inline styles are also filtered: all style identifiers matching + * /[a-z\-]/i are allowed. Values matching colors, sizes, /[a-z\-]/i and safe + * urls if allowed and cid urls if mapped are kept. + * + * Roundcube Changes: + * - added $block_elements + * - changed $ignore_elements behaviour + * - added RFC2397 support + * - base URL support + * - invalid HTML comments removal before parsing + * - "fixing" unitless CSS values for XHTML output + * - base url resolving + */ + +/** + * Utility class providing HTML sanityzer + * + * @package Framework + * @subpackage Utils + */ +class rcube_washtml +{ + /* Allowed HTML elements (default) */ + static $html_elements = array('a', 'abbr', 'acronym', 'address', 'area', 'b', + 'basefont', 'bdo', 'big', 'blockquote', 'br', 'caption', 'center', + 'cite', 'code', 'col', 'colgroup', 'dd', 'del', 'dfn', 'dir', 'div', 'dl', + 'dt', 'em', 'fieldset', 'font', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', + 'ins', 'label', 'legend', 'li', 'map', 'menu', 'nobr', 'ol', 'p', 'pre', 'q', + 's', 'samp', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'table', + 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'tt', 'u', 'ul', 'var', 'wbr', 'img', + // form elements + 'button', 'input', 'textarea', 'select', 'option', 'optgroup' + ); + + /* Ignore these HTML tags and their content */ + static $ignore_elements = array('script', 'applet', 'embed', 'object', 'style'); + + /* Allowed HTML attributes */ + static $html_attribs = array('name', 'class', 'title', 'alt', 'width', 'height', + 'align', 'nowrap', 'col', 'row', 'id', 'rowspan', 'colspan', 'cellspacing', + 'cellpadding', 'valign', 'bgcolor', 'color', 'border', 'bordercolorlight', + 'bordercolordark', 'face', 'marginwidth', 'marginheight', 'axis', 'border', + 'abbr', 'char', 'charoff', 'clear', 'compact', 'coords', 'vspace', 'hspace', + 'cellborder', 'size', 'lang', 'dir', 'usemap', 'shape', 'media', + // attributes of form elements + 'type', 'rows', 'cols', 'disabled', 'readonly', 'checked', 'multiple', 'value' + ); + + /* Block elements which could be empty but cannot be returned in short form () */ + static $block_elements = array('div', 'p', 'pre', 'blockquote', 'a', 'font', 'center', + 'table', 'ul', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ol', 'dl', 'strong', + 'i', 'b', 'u', 'span', + ); + + /* State for linked objects in HTML */ + public $extlinks = false; + + /* Current settings */ + private $config = array(); + + /* Registered callback functions for tags */ + private $handlers = array(); + + /* Allowed HTML elements */ + private $_html_elements = array(); + + /* Ignore these HTML tags but process their content */ + private $_ignore_elements = array(); + + /* Block elements which could be empty but cannot be returned in short form () */ + private $_block_elements = array(); + + /* Allowed HTML attributes */ + private $_html_attribs = array(); + + + /** + * Class constructor + */ + public function __construct($p = array()) + { + $this->_html_elements = array_flip((array)$p['html_elements']) + array_flip(self::$html_elements) ; + $this->_html_attribs = array_flip((array)$p['html_attribs']) + array_flip(self::$html_attribs); + $this->_ignore_elements = array_flip((array)$p['ignore_elements']) + array_flip(self::$ignore_elements); + $this->_block_elements = array_flip((array)$p['block_elements']) + array_flip(self::$block_elements); + + unset($p['html_elements'], $p['html_attribs'], $p['ignore_elements'], $p['block_elements']); + + $this->config = $p + array('show_washed' => true, 'allow_remote' => false, 'cid_map' => array()); + } + + /** + * Register a callback function for a certain tag + */ + public function add_callback($tagName, $callback) + { + $this->handlers[$tagName] = $callback; + } + + /** + * Check CSS style + */ + private function wash_style($style) + { + $s = ''; + + foreach (explode(';', $style) as $declaration) { + if (preg_match('/^\s*([a-z\-]+)\s*:\s*(.*)\s*$/i', $declaration, $match)) { + $cssid = $match[1]; + $str = $match[2]; + $value = ''; + + while (sizeof($str) > 0 && + preg_match('/^(url\(\s*[\'"]?([^\'"\)]*)[\'"]?\s*\)'./*1,2*/ + '|rgb\(\s*[0-9]+\s*,\s*[0-9]+\s*,\s*[0-9]+\s*\)'. + '|-?[0-9.]+\s*(em|ex|px|cm|mm|in|pt|pc|deg|rad|grad|ms|s|hz|khz|%)?'. + '|#[0-9a-f]{3,6}'. + '|[a-z0-9", -]+'. + ')\s*/i', $str, $match) + ) { + if ($match[2]) { + if (($src = $this->config['cid_map'][$match[2]]) + || ($src = $this->config['cid_map'][$this->config['base_url'].$match[2]]) + ) { + $value .= ' url('.htmlspecialchars($src, ENT_QUOTES) . ')'; + } + else if (preg_match('!^(https?:)?//[a-z0-9/._+-]+$!i', $match[2], $url)) { + if ($this->config['allow_remote']) { + $value .= ' url('.htmlspecialchars($url[0], ENT_QUOTES).')'; + } + else { + $this->extlinks = true; + } + } + else if (preg_match('/^data:.+/i', $match[2])) { // RFC2397 + $value .= ' url('.htmlspecialchars($match[2], ENT_QUOTES).')'; + } + } + else { + // whitelist ? + $value .= ' ' . $match[0]; + + // #1488535: Fix size units, so width:800 would be changed to width:800px + if (preg_match('/(left|right|top|bottom|width|height)/i', $cssid) + && preg_match('/^[0-9]+$/', $match[0]) + ) { + $value .= 'px'; + } + } + + $str = substr($str, strlen($match[0])); + } + + if (isset($value[0])) { + $s .= ($s?' ':'') . $cssid . ':' . $value . ';'; + } + } + } + + return $s; + } + + /** + * Take a node and return allowed attributes and check values + */ + private function wash_attribs($node) + { + $t = ''; + $washed = ''; + + foreach ($node->attributes as $key => $plop) { + $key = strtolower($key); + $value = $node->getAttribute($key); + + if (isset($this->_html_attribs[$key]) || + ($key == 'href' && !preg_match('!^(javascript|vbscript|data:text)!i', $value) + && preg_match('!^([a-z][a-z0-9.+-]+:|//|#).+!i', $value)) + ) { + $t .= ' ' . $key . '="' . htmlspecialchars($value, ENT_QUOTES) . '"'; + } + else if ($key == 'style' && ($style = $this->wash_style($value))) { + $quot = strpos($style, '"') !== false ? "'" : '"'; + $t .= ' style=' . $quot . $style . $quot; + } + else if ($key == 'background' || ($key == 'src' && strtolower($node->tagName) == 'img')) { //check tagName anyway + if (($src = $this->config['cid_map'][$value]) + || ($src = $this->config['cid_map'][$this->config['base_url'].$value]) + ) { + $t .= ' ' . $key . '="' . htmlspecialchars($src, ENT_QUOTES) . '"'; + } + else if (preg_match('/^(http|https|ftp):.+/i', $value)) { + if ($this->config['allow_remote']) { + $t .= ' ' . $key . '="' . htmlspecialchars($value, ENT_QUOTES) . '"'; + } + else { + $this->extlinks = true; + if ($this->config['blocked_src']) { + $t .= ' ' . $key . '="' . htmlspecialchars($this->config['blocked_src'], ENT_QUOTES) . '"'; + } + } + } + else if (preg_match('/^data:.+/i', $value)) { // RFC2397 + $t .= ' ' . $key . '="' . htmlspecialchars($value, ENT_QUOTES) . '"'; + } + } + else { + $washed .= ($washed ? ' ' : '') . $key; + } + } + + return $t . ($washed && $this->config['show_washed'] ? ' x-washed="'.$washed.'"' : ''); + } + + /** + * The main loop that recurse on a node tree. + * It output only allowed tags with allowed attributes + * and allowed inline styles + */ + private function dumpHtml($node) + { + if (!$node->hasChildNodes()) { + return ''; + } + + $node = $node->firstChild; + $dump = ''; + + do { + switch($node->nodeType) { + case XML_ELEMENT_NODE: //Check element + $tagName = strtolower($node->tagName); + if ($callback = $this->handlers[$tagName]) { + $dump .= call_user_func($callback, $tagName, + $this->wash_attribs($node), $this->dumpHtml($node), $this); + } + else if (isset($this->_html_elements[$tagName])) { + $content = $this->dumpHtml($node); + $dump .= '<' . $tagName . $this->wash_attribs($node) . + ($content != '' || isset($this->_block_elements[$tagName]) ? ">$content" : ' />'); + } + else if (isset($this->_ignore_elements[$tagName])) { + $dump .= ''; + } + else { + $dump .= ''; + $dump .= $this->dumpHtml($node); // ignore tags not its content + } + break; + + case XML_CDATA_SECTION_NODE: + $dump .= $node->nodeValue; + break; + + case XML_TEXT_NODE: + $dump .= htmlspecialchars($node->nodeValue); + break; + + case XML_HTML_DOCUMENT_NODE: + $dump .= $this->dumpHtml($node); + break; + + case XML_DOCUMENT_TYPE_NODE: + break; + + default: + $dump . ''; + } + } while($node = $node->nextSibling); + + return $dump; + } + + /** + * Main function, give it untrusted HTML, tell it if you allow loading + * remote images and give it a map to convert "cid:" urls. + */ + public function wash($html) + { + // Charset seems to be ignored (probably if defined in the HTML document) + $node = new DOMDocument('1.0', $this->config['charset']); + $this->extlinks = false; + + $html = $this->cleanup($html); + + // Find base URL for images + if (preg_match('/config['base_url'] = $matches[1]; + } + else { + $this->config['base_url'] = ''; + } + + @$node->loadHTML($html); + return $this->dumpHtml($node); + } + + /** + * Getter for config parameters + */ + public function get_config($prop) + { + return $this->config[$prop]; + } + + /** + * Clean HTML input + */ + private function cleanup($html) + { + // special replacements (not properly handled by washtml class) + $html_search = array( + '/(<\/nobr>)(\s+)()/i', // space(s) between + '/]*>[^<]*<\/title>/i', // PHP bug #32547 workaround: remove title tag + '/^(\0\0\xFE\xFF|\xFF\xFE\0\0|\xFE\xFF|\xFF\xFE|\xEF\xBB\xBF)/', // byte-order mark (only outlook?) + '/]+>/i', // washtml/DOMDocument cannot handle xml namespaces + ); + + $html_replace = array( + '\\1'.'   '.'\\3', + '', + '', + '', + ); + $html = preg_replace($html_search, $html_replace, trim($html)); + + // PCRE errors handling (#1486856), should we use something like for every preg_* use? + if ($html === null && ($preg_error = preg_last_error()) != PREG_NO_ERROR) { + $errstr = "Could not clean up HTML message! PCRE Error: $preg_error."; + + if ($preg_error == PREG_BACKTRACK_LIMIT_ERROR) { + $errstr .= " Consider raising pcre.backtrack_limit!"; + } + if ($preg_error == PREG_RECURSION_LIMIT_ERROR) { + $errstr .= " Consider raising pcre.recursion_limit!"; + } + + rcube::raise_error(array('code' => 620, 'type' => 'php', + 'line' => __LINE__, 'file' => __FILE__, + 'message' => $errstr), true, false); + return ''; + } + + // fix (unknown/malformed) HTML tags before "wash" + $html = preg_replace_callback('/(<[\/]*)([^\s>]+)/', array($this, 'html_tag_callback'), $html); + + // Remove invalid HTML comments (#1487759) + // Don't remove valid conditional comments + $html = preg_replace('/'; - } - else { - $dump .= ''; - $dump .= $this->dumpHtml($node); // ignore tags not its content - } - break; - case XML_CDATA_SECTION_NODE: - $dump .= $node->nodeValue; - break; - case XML_TEXT_NODE: - $dump .= htmlspecialchars($node->nodeValue); - break; - case XML_HTML_DOCUMENT_NODE: - $dump .= $this->dumpHtml($node); - break; - case XML_DOCUMENT_TYPE_NODE: - break; - default: - $dump . ''; - } - } while($node = $node->nextSibling); - - return $dump; - } - - /* Main function, give it untrusted HTML, tell it if you allow loading - * remote images and give it a map to convert "cid:" urls. */ - public function wash($html) - { - // Charset seems to be ignored (probably if defined in the HTML document) - $node = new DOMDocument('1.0', $this->config['charset']); - $this->extlinks = false; - - // Find base URL for images - if (preg_match('/config['base_url'] = $matches[1]; - else - $this->config['base_url'] = ''; - - // Remove invalid HTML comments (#1487759) - // Don't remove valid conditional comments - $html = preg_replace('/ + + diff --git a/skins/larry/templates/messagepreview.html b/skins/larry/templates/messagepreview.html index aef282ac9..dbfe2dc7b 100644 --- a/skins/larry/templates/messagepreview.html +++ b/skins/larry/templates/messagepreview.html @@ -51,6 +51,14 @@ + + diff --git a/skins/larry/ui.js b/skins/larry/ui.js index d2638bbca..6b2a5c7d0 100644 --- a/skins/larry/ui.js +++ b/skins/larry/ui.js @@ -17,6 +17,7 @@ function rcube_mail_ui() var popupconfig = { forwardmenu: { editable:1 }, searchmenu: { editable:1, callback:searchmenu }, + attachmentmenu: { }, listoptions: { editable:1 }, dragmessagemenu: { sticky:1 }, groupmenu: { above:1 }, @@ -81,8 +82,8 @@ function rcube_mail_ui() /*** mail task ***/ if (rcmail.env.task == 'mail') { - rcmail.addEventListener('menu-open', show_listoptions); - rcmail.addEventListener('menu-save', save_listoptions); + rcmail.addEventListener('menu-open', menu_open); + rcmail.addEventListener('menu-save', menu_save); rcmail.addEventListener('responseafterlist', function(e){ switch_view_mode(rcmail.env.threading ? 'thread' : 'list') }); var dragmenu = $('#dragmessagemenu'); @@ -95,6 +96,11 @@ function rcube_mail_ui() rcmail.addEventListener('aftershow-headers', function() { layout_messageview(); }); rcmail.addEventListener('afterhide-headers', function() { layout_messageview(); }); $('#previewheaderstoggle').click(function(e){ toggle_preview_headers(this); return false }); + + // add menu link for each attachment + $('#attachment-list > li').each(function() { + $(this).append($('').click(function() { attachmentmenu(this); })); + }); } else if (rcmail.env.action == 'compose') { rcmail.addEventListener('aftertoggle-editor', function(){ window.setTimeout(function(){ layout_composeview() }, 200); }); @@ -436,7 +442,7 @@ function rcube_mail_ui() { var obj = popups[popup], config = popupconfig[popup], - ref = $('#'+popup+'link'), + ref = $(config.link ? config.link : '#'+popup+'link'), above = config.above; if (!obj) { @@ -452,7 +458,7 @@ function rcube_mail_ui() else if (config.toggle && show && obj.is(':visible')) show = false; - if (show && ref) { + if (show && ref.length) { var parent = ref.parent(), win = $(window), pos; @@ -575,6 +581,19 @@ function rcube_mail_ui() /**** popup callbacks ****/ + function menu_open(p) + { + if (p && p.props && p.props.menu == 'attachmentmenu') + show_popupmenu('attachmentmenu'); + else + show_listoptions(); + } + + function menu_save(prop) + { + save_listoptions(); + } + function searchmenu(show) { if (show && rcmail.env.search_mods) { @@ -605,6 +624,21 @@ function rcube_mail_ui() } } + function attachmentmenu(elem) + { + var id = elem.parentNode.id.replace(/^attach/, ''); + + $('#attachmenuopen').unbind('click').attr('onclick', '').click(function(e) { + return rcmail.command('open-attachment', id, this); + }); + + $('#attachmenudownload').unbind('click').attr('onclick', '').click(function() { + rcmail.command('download-attachment', id, this); + }); + + popupconfig.attachmentmenu.link = elem; + rcmail.command('menu-open', {menu: 'attachmentmenu', id: id}); + } function spellmenu(show) { -- cgit v1.2.3 From 8b771646fadcde0abb27c2218a45942b95734838 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Sun, 10 Mar 2013 11:49:20 +0100 Subject: Fix so task name can really contain all from a-z0-9_- characters (#1488941) --- CHANGELOG | 1 + program/include/rcmail.php | 2 +- program/js/app.js | 6 +++--- program/lib/Roundcube/rcube_plugin.php | 2 +- program/lib/Roundcube/rcube_plugin_api.php | 4 ++-- 5 files changed, 8 insertions(+), 7 deletions(-) (limited to 'program/include') diff --git a/CHANGELOG b/CHANGELOG index baca86765..ef3830a42 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Fix so task name can really contain all from a-z0-9_- characters (#1488941) - Support IMAP MOVE extension [RFC 6851] - Fix javascript errors when working in a page opened with taget="_blank" - Mention SQLite database format change in UPGRADING file (#1488983) diff --git a/program/include/rcmail.php b/program/include/rcmail.php index b2d6966d0..1bde4034f 100644 --- a/program/include/rcmail.php +++ b/program/include/rcmail.php @@ -123,7 +123,7 @@ class rcmail extends rcube */ public function set_task($task) { - $task = asciiwords($task); + $task = asciiwords($task, true); if ($this->user && $this->user->ID) $task = !$task ? 'mail' : $task; diff --git a/program/js/app.js b/program/js/app.js index c2a7c1b79..9f76757a6 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -1235,7 +1235,7 @@ function rcube_webmail() if (!url) url = this.env.comm_path; - return url.replace(/_task=[a-z]+/, '_task='+task); + return url.replace(/_task=[a-z0-9_-]+/i, '_task='+task); }; this.reload = function(delay) @@ -5970,9 +5970,9 @@ function rcube_webmail() var base = this.env.comm_path, k, param = {}; // overwrite task name - if (query._action.match(/([a-z]+)\/([a-z0-9-_.]+)/)) { + if (query._action.match(/([a-z0-9_-]+)\/([a-z0-9-_.]+)/)) { query._action = RegExp.$2; - base = base.replace(/\_task=[a-z]+/, '_task='+RegExp.$1); + base = base.replace(/\_task=[a-z0-9_-]+/, '_task='+RegExp.$1); } // remove undefined values diff --git a/program/lib/Roundcube/rcube_plugin.php b/program/lib/Roundcube/rcube_plugin.php index 66e77cce2..9ea0f73d3 100644 --- a/program/lib/Roundcube/rcube_plugin.php +++ b/program/lib/Roundcube/rcube_plugin.php @@ -237,7 +237,7 @@ abstract class rcube_plugin /** * Register this plugin to be responsible for a specific task * - * @param string $task Task name (only characters [a-z0-9_.-] are allowed) + * @param string $task Task name (only characters [a-z0-9_-] are allowed) */ public function register_task($task) { diff --git a/program/lib/Roundcube/rcube_plugin_api.php b/program/lib/Roundcube/rcube_plugin_api.php index 8a4cce215..111c177d9 100644 --- a/program/lib/Roundcube/rcube_plugin_api.php +++ b/program/lib/Roundcube/rcube_plugin_api.php @@ -372,7 +372,7 @@ class rcube_plugin_api /** * Register this plugin to be responsible for a specific task * - * @param string $task Task name (only characters [a-z0-9_.-] are allowed) + * @param string $task Task name (only characters [a-z0-9_-] are allowed) * @param string $owner Plugin name that registers this action */ public function register_task($task, $owner) @@ -382,7 +382,7 @@ class rcube_plugin_api return true; } - if ($task != asciiwords($task)) { + if ($task != asciiwords($task, true)) { rcube::raise_error(array('code' => 526, 'type' => 'php', 'file' => __FILE__, 'line' => __LINE__, 'message' => "Invalid task name: $task." -- cgit v1.2.3 From bb080af14d285361ee078514a1d995c096e0f790 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 15 Mar 2013 12:53:02 +0100 Subject: Bump version number up to 1.0-git --- index.php | 2 +- program/include/iniset.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'program/include') diff --git a/index.php b/index.php index 3e398c00d..dc6827a87 100644 --- a/index.php +++ b/index.php @@ -2,7 +2,7 @@ /* +-------------------------------------------------------------------------+ | Roundcube Webmail IMAP Client | - | Version 0.9-git | + | Version 1.0-git | | | | Copyright (C) 2005-2013, The Roundcube Dev Team | | | diff --git a/program/include/iniset.php b/program/include/iniset.php index 35b5522fc..b32ae4e8e 100644 --- a/program/include/iniset.php +++ b/program/include/iniset.php @@ -21,7 +21,7 @@ */ // application constants -define('RCMAIL_VERSION', '0.9-git'); +define('RCMAIL_VERSION', '1.0-git'); define('RCMAIL_START', microtime(true)); $config = array( -- cgit v1.2.3 From 4d1fe2d230c4194aa99111c5f63dfb33ad35ab83 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Sun, 17 Mar 2013 09:59:48 +0100 Subject: Fix "rcmail is undefined" error in HTML attachment preview - regression from commit d30460ad2fc0f78ce44d474fa2c466d660596d27, small improvements --- program/include/rcmail_html_page.php | 2 +- program/include/rcmail_output_html.php | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) (limited to 'program/include') diff --git a/program/include/rcmail_html_page.php b/program/include/rcmail_html_page.php index 1d8391580..5d07b8d04 100644 --- a/program/include/rcmail_html_page.php +++ b/program/include/rcmail_html_page.php @@ -30,7 +30,7 @@ class rcmail_html_page extends rcmail_output_html { public function write($contents = '') { - self::reset(); + self::reset(true); // load embed.css from skin folder (if exists) if ($embed_css = $this->get_skin_file('/embed.css')) { diff --git a/program/include/rcmail_output_html.php b/program/include/rcmail_output_html.php index ade2bd4a4..6100269b9 100644 --- a/program/include/rcmail_output_html.php +++ b/program/include/rcmail_output_html.php @@ -307,17 +307,19 @@ class rcmail_output_html extends rcmail_output /** * Delete all stored env variables and commands + * + * @param bool $all Reset all env variables (including internal) */ - public function reset() + public function reset($all = false) { - $env = array_intersect_key($this->env, array('extwin'=>1, 'framed'=>1)); + $env = $all ? null : array_intersect_key($this->env, array('extwin'=>1, 'framed'=>1)); parent::reset(); // let some env variables survive $this->env = $this->js_env = $env; - $this->js_labels = array(); - $this->js_commands = array(); + $this->js_labels = array(); + $this->js_commands = array(); $this->script_files = array(); $this->scripts = array(); $this->header = ''; @@ -362,7 +364,7 @@ class rcmail_output_html extends rcmail_output $this->parse($templ, false); } else { - $this->framed = $templ == 'iframe' ? true : $this->framed; + $this->framed = true; $this->write(); } @@ -396,9 +398,11 @@ class rcmail_output_html extends rcmail_output $this->set_env('request_token', $this->app->get_request_token()); // write all env variables to client - $js = $this->framed ? "if(window.parent) {\n" : ''; - $js .= $this->get_js_commands() . ($this->framed ? ' }' : ''); - $this->add_script($js, 'head_top'); + if ($commands = $this->get_js_commands()) { + $js = $this->framed ? "if (window.parent) {\n" : ''; + $js .= $commands . ($this->framed ? ' }' : ''); + $this->add_script($js, 'head_top'); + } // send clickjacking protection headers $iframe = $this->framed || !empty($_REQUEST['_framed']); -- cgit v1.2.3 From 17573916bcb4281b15f1acb27e4fae782ad52d2a Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Sun, 24 Mar 2013 11:52:55 +0100 Subject: Ensure backwards compatibility by considering old-style skin_path attribute for tags --- program/include/rcmail_output_html.php | 1 + 1 file changed, 1 insertion(+) (limited to 'program/include') diff --git a/program/include/rcmail_output_html.php b/program/include/rcmail_output_html.php index 6100269b9..d8996edbf 100644 --- a/program/include/rcmail_output_html.php +++ b/program/include/rcmail_output_html.php @@ -873,6 +873,7 @@ class rcmail_output_html extends rcmail_output // include a file case 'include': $old_base_path = $this->base_path; + if (!empty($attrib['skin_path'])) $attrib['skinpath'] = $attrib['skin_path']; if ($path = $this->get_skin_file($attrib['file'], $skin_path, $attrib['skinpath'])) { $this->base_path = preg_replace('!plugins/\w+/!', '', $skin_path); // set base_path to core skin directory (not plugin's skin) $path = realpath($path); -- cgit v1.2.3