Merge branch 'master' of github.com:roundcube/roundcubemail
| | |
| | | CHANGELOG Roundcube Webmail |
| | | =========================== |
| | | |
| | | - Fix storing X-ANNIVERSARY date in vCard format (#1488527) |
| | | - Update to Mail_Mime-1.8.5 (#1488521) |
| | | - Fix Shift + delete button does not permanently delete messages (#1488243) |
| | | - Add Content-Length for attachments where possible (#1485478) |
| | | - Fix attachment sizes in message print page and attachment preview page (#1488515) |
| | | - Fix XSS vulnerability in message subject handling using Larry skin (#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 |
| | | - Add mail attachments using drag & drop on HTML5 enabled browsers |
| | | - Add workaround for invalid BODYSTRUCTURE response - parse message with Mail_mimeDecode package (#1485585) |
| | | - Decode header value in rcube_mime::get() by default (#1488511) |
| | |
| | | [changed] [datetime] NOT NULL ,
|
| | | [del] [char] (1) COLLATE Latin1_General_CI_AI NOT NULL ,
|
| | | [name] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,
|
| | | [email] [text] COLLATE Latin1_General_CI_AI NOT NULL ,
|
| | | [email] [varchar] (8000) COLLATE Latin1_General_CI_AI NOT NULL ,
|
| | | [firstname] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,
|
| | | [surname] [varchar] (128) COLLATE Latin1_General_CI_AI NOT NULL ,
|
| | | [vcard] [text] COLLATE Latin1_General_CI_AI NULL ,
|
| | |
| | |
|
| | | -- Updates from version 0.7
|
| | |
|
| | | ALTER TABLE [dbo].[contacts] DROP CONSTRAINT [DF_contacts_email]
|
| | | GO
|
| | | ALTER TABLE [dbo].[contacts] ALTER COLUMN [email] [text] COLLATE Latin1_General_CI_AI NOT NULL
|
| | | GO
|
| | | ALTER TABLE [dbo].[contacts] ADD CONSTRAINT [DF_contacts_email] DEFAULT ('') FOR [email]
|
| | | GO
|
| | |
|
| | | -- Updates from version 0.8-rc
|
| | |
|
| | | ALTER TABLE [dbo].[contacts] DROP CONSTRAINT [DF_contacts_email]
|
| | | GO
|
| | | ALTER TABLE [dbo].[contacts] ALTER COLUMN [email] [varchar] (8000) COLLATE Latin1_General_CI_AI NOT NULL
|
| | | GO
|
| | | ALTER TABLE [dbo].[contacts] ADD CONSTRAINT [DF_contacts_email] DEFAULT ('') FOR [email]
|
| | | GO
|
| | |
|
| | |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Returns real size (calculated) of the message part |
| | | * |
| | | * @param rcube_message_part Message part |
| | | * |
| | | * @return string Part size (and unit) |
| | | */ |
| | | public function message_part_size($part) |
| | | { |
| | | if (isset($part->d_parameters['size'])) { |
| | | $size = $this->show_bytes((int)$part->d_parameters['size']); |
| | | } |
| | | else { |
| | | $size = $part->size; |
| | | if ($part->encoding == 'base64') { |
| | | $size = $size / 1.33; |
| | | } |
| | | |
| | | $size = '~' . $this->show_bytes($size); |
| | | } |
| | | |
| | | return $size; |
| | | } |
| | | |
| | | |
| | | /************************************************************************ |
| | | ********* Deprecated methods (to be removed) ********* |
| | | ***********************************************************************/ |
| | |
| | | // make valid href to specific buttons |
| | | if (in_array($attrib['command'], rcmail::$main_tasks)) { |
| | | $attrib['href'] = $this->app->url(array('task' => $attrib['command'])); |
| | | $attrib['onclick'] = sprintf("%s.command('switch-task','%s');return false", rcmail::JS_OBJECT_NAME, $attrib['command']); |
| | | $attrib['onclick'] = sprintf("%s.command('switch-task','%s',null,event); return false", rcmail::JS_OBJECT_NAME, $attrib['command']); |
| | | } |
| | | else if ($attrib['task'] && in_array($attrib['task'], rcmail::$main_tasks)) { |
| | | $attrib['href'] = $this->app->url(array('action' => $attrib['command'], 'task' => $attrib['task'])); |
| | |
| | | } |
| | | else if ($command && !$attrib['onclick']) { |
| | | $attrib['onclick'] = sprintf( |
| | | "return %s.command('%s','%s',this)", |
| | | "return %s.command('%s','%s',this,event)", |
| | | rcmail::JS_OBJECT_NAME, |
| | | $command, |
| | | $attrib['prop'] |
| | |
| | | if (empty($attrib['form'])) { |
| | | $out = $this->form_tag(array( |
| | | 'name' => "rcmqsearchform", |
| | | 'onsubmit' => rcmail::JS_OBJECT_NAME . ".command('search');return false;", |
| | | 'onsubmit' => rcmail::JS_OBJECT_NAME . ".command('search'); return false", |
| | | 'style' => "display:inline"), |
| | | $out); |
| | | } |
| | |
| | | break; |
| | | |
| | | case 'birthday': |
| | | if ($val = rcube_strtotime($value)) |
| | | $this->raw['BDAY'][] = array(0 => date('Y-m-d', $val), 'value' => array('date')); |
| | | case 'anniversary': |
| | | if (($val = rcube_strtotime($value)) && ($fn = self::$fieldmap[$field])) |
| | | $this->raw[$fn][] = array(0 => date('Y-m-d', $val), 'value' => array('date')); |
| | | break; |
| | | |
| | | case 'address': |
| | |
| | | /*********************************************************/ |
| | | |
| | | // execute a specific command on the web client |
| | | this.command = function(command, props, obj) |
| | | this.command = function(command, props, obj, event) |
| | | { |
| | | var ret, uid, cid, url, flag; |
| | | |
| | |
| | | case 'delete': |
| | | // mail task |
| | | if (this.task == 'mail') |
| | | this.delete_messages(); |
| | | this.delete_messages(event); |
| | | // addressbook task |
| | | else if (this.task == 'addressbook') |
| | | this.delete_contacts(); |
| | |
| | | html = '<span id="flagicn'+uid+'" class="'+css_class+'"> </span>'; |
| | | } |
| | | else if (c == 'attachment') { |
| | | if (/application\/|multipart\/m/.test(flags.ctype)) |
| | | if (/application\/|multipart\/(m|signed)/.test(flags.ctype)) |
| | | html = '<span class="attachment"> </span>'; |
| | | else if (/multipart\/report/.test(flags.ctype)) |
| | | html = '<span class="report"> </span>'; |
| | |
| | | }; |
| | | |
| | | // delete selected messages from the current mailbox |
| | | this.delete_messages = function() |
| | | this.delete_messages = function(event) |
| | | { |
| | | var uid, i, len, trash = this.env.trash_mailbox, |
| | | list = this.message_list, |
| | |
| | | // if there is a trash mailbox defined and we're not currently in it |
| | | else { |
| | | // if shift was pressed delete it immediately |
| | | if (list && list.modkey == SHIFT_KEY) { |
| | | if ((list && list.modkey == SHIFT_KEY) || (event && rcube_event.get_modifier(event) == SHIFT_KEY)) { |
| | | if (confirm(this.get_label('deletemessagesconfirm'))) |
| | | this.permanently_remove_messages(); |
| | | } |
| | |
| | | * @author Aleksander Machniak <alec@php.net> |
| | | * @copyright 2003-2006 PEAR <pear-group@php.net> |
| | | * @license http://www.opensource.org/licenses/bsd-license.php BSD License |
| | | * @version 1.8.4 |
| | | * @version 1.8.5 |
| | | * @link http://pear.php.net/package/Mail_mime |
| | | * |
| | | * This class is based on HTML Mime Mail class from |
| | |
| | | * @author Sean Coates <sean@php.net> |
| | | * @copyright 2003-2006 PEAR <pear-group@php.net> |
| | | * @license http://www.opensource.org/licenses/bsd-license.php BSD License |
| | | * @version Release: 1.8.4 |
| | | * @version Release: 1.8.5 |
| | | * @link http://pear.php.net/package/Mail_mime |
| | | */ |
| | | class Mail_mime |
| | |
| | | * @param string $description Content-Description header |
| | | * @param string $h_charset The character set of the headers e.g. filename |
| | | * If not specified, $charset will be used |
| | | * @param array $add_headers Additional part headers |
| | | * @param array $add_headers Additional part headers. Array keys can be in form |
| | | * of <header_name>:<parameter_name> |
| | | * |
| | | * @return mixed True on success or PEAR_Error object |
| | | * @access public |
| | |
| | | * @author Aleksander Machniak <alec@php.net> |
| | | * @copyright 2003-2006 PEAR <pear-group@php.net> |
| | | * @license http://www.opensource.org/licenses/bsd-license.php BSD License |
| | | * @version 1.8.4 |
| | | * @version 1.8.5 |
| | | * @link http://pear.php.net/package/Mail_mime |
| | | */ |
| | | |
| | |
| | | * @author Aleksander Machniak <alec@php.net> |
| | | * @copyright 2003-2006 PEAR <pear-group@php.net> |
| | | * @license http://www.opensource.org/licenses/bsd-license.php BSD License |
| | | * @version Release: 1.8.4 |
| | | * @version Release: 1.8.5 |
| | | * @link http://pear.php.net/package/Mail_mime |
| | | */ |
| | | class Mail_mimePart |
| | |
| | | * headers_charset - Charset of the headers e.g. filename, description. |
| | | * If not set, 'charset' will be used |
| | | * eol - End of line sequence. Default: "\r\n" |
| | | * headers - Hash array with additional part headers |
| | | * headers - Hash array with additional part headers. Array keys can be |
| | | * in form of <header_name>:<parameter_name> |
| | | * body_file - Location of file with part's body (instead of $body) |
| | | * |
| | | * @access public |
| | |
| | | $params['headers_charset'] = $params['charset']; |
| | | } |
| | | } |
| | | |
| | | // header values encoding parameters |
| | | $h_charset = !empty($params['headers_charset']) ? $params['headers_charset'] : 'US-ASCII'; |
| | | $h_language = !empty($params['language']) ? $params['language'] : null; |
| | | $h_encoding = !empty($params['name_encoding']) ? $params['name_encoding'] : null; |
| | | |
| | | |
| | | if (!empty($params['filename'])) { |
| | | $headers['Content-Type'] .= ';' . $this->_eol; |
| | | $headers['Content-Type'] .= $this->_buildHeaderParam( |
| | | 'name', $params['filename'], |
| | | !empty($params['headers_charset']) ? $params['headers_charset'] : 'US-ASCII', |
| | | !empty($params['language']) ? $params['language'] : null, |
| | | !empty($params['name_encoding']) ? $params['name_encoding'] : null |
| | | 'name', $params['filename'], $h_charset, $h_language, $h_encoding |
| | | ); |
| | | } |
| | | |
| | |
| | | if (!empty($params['filename'])) { |
| | | $headers['Content-Disposition'] .= ';' . $this->_eol; |
| | | $headers['Content-Disposition'] .= $this->_buildHeaderParam( |
| | | 'filename', $params['filename'], |
| | | !empty($params['headers_charset']) ? $params['headers_charset'] : 'US-ASCII', |
| | | !empty($params['language']) ? $params['language'] : null, |
| | | 'filename', $params['filename'], $h_charset, $h_language, |
| | | !empty($params['filename_encoding']) ? $params['filename_encoding'] : null |
| | | ); |
| | | } |
| | | |
| | | // add attachment size |
| | | $size = $this->_body_file ? filesize($this->_body_file) : strlen($body); |
| | | if ($size) { |
| | | $headers['Content-Disposition'] .= ';' . $this->_eol . ' size=' . $size; |
| | | } |
| | | } |
| | | |
| | | if (!empty($params['description'])) { |
| | | $headers['Content-Description'] = $this->encodeHeader( |
| | | 'Content-Description', $params['description'], |
| | | !empty($params['headers_charset']) ? $params['headers_charset'] : 'US-ASCII', |
| | | !empty($params['name_encoding']) ? $params['name_encoding'] : 'quoted-printable', |
| | | 'Content-Description', $params['description'], $h_charset, $h_encoding, |
| | | $this->_eol |
| | | ); |
| | | } |
| | | |
| | | // Search and add existing headers' parameters |
| | | foreach ($headers as $key => $value) { |
| | | $items = explode(':', $key); |
| | | if (count($items) == 2) { |
| | | $header = $items[0]; |
| | | $param = $items[1]; |
| | | if (isset($headers[$header])) { |
| | | $headers[$header] .= ';' . $this->_eol; |
| | | } |
| | | $headers[$header] .= $this->_buildHeaderParam( |
| | | $param, $value, $h_charset, $h_language, $h_encoding |
| | | ); |
| | | unset($headers[$key]); |
| | | } |
| | | } |
| | | |
| | | // Default encoding |
| | | if (!isset($this->_encoding)) { |
| | | $this->_encoding = '7bit'; |
| | |
| | | * @access public |
| | | */ |
| | | var $callback_search = array( |
| | | '/<(a) [^>]*href=("|\')([^"\']+)\2[^>]*>(.*?)<\/a>/i', |
| | | // <a href=""> |
| | | '/<(h)[123456][^>]*>(.*?)<\/h[123456]>/i', // H1 - H3 |
| | | '/<(b)[^>]*>(.*?)<\/b>/i', // <b> |
| | | '/<(strong)[^>]*>(.*?)<\/strong>/i', // <strong> |
| | | '/<(th)[^>]*>(.*?)<\/th>/i', // <th> and </th> |
| | | '/<(a) [^>]*href=("|\')([^"\']+)\2[^>]*>(.*?)<\/a>/i', // <a href=""> |
| | | '/<(h)[123456]( [^>]*)?>(.*?)<\/h[123456]>/i', // h1 - h6 |
| | | '/<(b)( [^>]*)?>(.*?)<\/b>/i', // <b> |
| | | '/<(strong)( [^>]*)?>(.*?)<\/strong>/i', // <strong> |
| | | '/<(th)( [^>]*)?>(.*?)<\/th>/i', // <th> and </th> |
| | | ); |
| | | |
| | | /** |
| | |
| | | function set_html( $source, $from_file = false ) |
| | | { |
| | | if ( $from_file && file_exists($source) ) { |
| | | $this->html = file_get_contents($source); |
| | | $this->html = file_get_contents($source); |
| | | } |
| | | else |
| | | $this->html = $source; |
| | |
| | | } |
| | | |
| | | // Ignored link types |
| | | if (preg_match('!^(javascript|mailto|#):!i', $link)) { |
| | | if (preg_match('!^(javascript:|mailto:|#)!i', $link)) { |
| | | return $display; |
| | | } |
| | | |
| | | if (preg_match('!^(https?://)!i', $link)) { |
| | | if (preg_match('!^([a-z][a-z0-9.+-]+:)!i', $link)) { |
| | | $url = $link; |
| | | } |
| | | else { |
| | |
| | | } |
| | | |
| | | if (($index = array_search($url, $this->_link_list)) === false) { |
| | | $this->_link_list[] = $url; |
| | | $index = count($this->_link_list); |
| | | $this->_link_list[] = $url; |
| | | } |
| | | |
| | | return $display . ' [' . ($index+1) . ']'; |
| | |
| | | { |
| | | // get the content of PRE element |
| | | while (preg_match('/<pre[^>]*>(.*)<\/pre>/ismU', $text, $matches)) { |
| | | $this->pre_content = $matches[1]; |
| | | |
| | | // Run our defined tags search-and-replace with callback |
| | | $this->pre_content = preg_replace_callback($this->callback_search, |
| | | array('html2text', '_preg_callback'), $this->pre_content); |
| | | |
| | | // convert the content |
| | | $this->pre_content = sprintf('<div><br>%s<br></div>', |
| | | preg_replace($this->pre_search, $this->pre_replace, $matches[1])); |
| | | preg_replace($this->pre_search, $this->pre_replace, $this->pre_content)); |
| | | |
| | | // replace the content (use callback because content can contain $0 variable) |
| | | $text = preg_replace_callback('/<pre[^>]*>.*<\/pre>/ismU', |
| | | $text = preg_replace_callback('/<pre[^>]*>.*<\/pre>/ismU', |
| | | array('html2text', '_preg_pre_callback'), $text, 1); |
| | | |
| | | // free memory |
| | | $this->pre_content = ''; |
| | | } |
| | |
| | | switch (strtolower($matches[1])) { |
| | | case 'b': |
| | | case 'strong': |
| | | return $this->_toupper($matches[2]); |
| | | return $this->_toupper($matches[3]); |
| | | case 'th': |
| | | return $this->_toupper("\t\t". $matches[2] ."\n"); |
| | | return $this->_toupper("\t\t". $matches[3] ."\n"); |
| | | case 'h': |
| | | return $this->_toupper("\n\n". $matches[2] ."\n\n"); |
| | | return $this->_toupper("\n\n". $matches[3] ."\n\n"); |
| | | case 'a': |
| | | // Remove spaces in URL (#1487805) |
| | | $url = str_replace(' ', '', $matches[3]); |
| | |
| | | $key = strtolower($key); |
| | | $value = $node->getAttribute($key); |
| | | if (isset($this->_html_attribs[$key]) || |
| | | ($key == 'href' && preg_match('!^(http:|https:|ftp:|mailto:|//|#).+!i', $value))) |
| | | ($key == 'href' && 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 ? "'" : '"'; |
| | |
| | | |
| | | // single header value is requested |
| | | if (!empty($attrib['valueof'])) |
| | | return Q($plugin['output'][$attrib['valueof']]['value'], ($hkey == 'subject' ? 'strict' : 'show')); |
| | | return Q($plugin['output'][$attrib['valueof']]['value'], ($attrib['valueof'] == 'subject' ? 'strict' : 'show')); |
| | | |
| | | // compose html table |
| | | $table = new html_table(array('cols' => 2)); |
| | |
| | | |
| | | function rcmail_message_part_controls($attrib) |
| | | { |
| | | global $MESSAGE; |
| | | global $MESSAGE, $RCMAIL; |
| | | |
| | | $part = asciiwords(get_input_value('_part', RCUBE_INPUT_GPC)); |
| | | if (!is_object($MESSAGE) || !is_array($MESSAGE->parts) || !($_GET['_uid'] && $_GET['_part']) || !$MESSAGE->mime_parts[$part]) |
| | | return ''; |
| | | |
| | | $part = $MESSAGE->mime_parts[$part]; |
| | | $part = $MESSAGE->mime_parts[$part]; |
| | | $table = new html_table(array('cols' => 3)); |
| | | |
| | | $filename = $part->filename; |
| | |
| | | $table->add('download-link', html::a(array('href' => './?'.str_replace('_frame=', '_download=', $_SERVER['QUERY_STRING'])), Q(rcube_label('download')))); |
| | | } |
| | | |
| | | if (!empty($part->size)) { |
| | | $table->add('title', Q(rcube_label('filesize'))); |
| | | $table->add('header', Q(show_bytes($part->size))); |
| | | } |
| | | $table->add('title', Q(rcube_label('filesize'))); |
| | | $table->add('header', Q($RCMAIL->message_part_size($part))); |
| | | |
| | | return $table->show($attrib); |
| | | } |
| | |
| | | $sent = $RCMAIL->storage->get_message_part($MESSAGE->uid, $part->mime_id, $part, false, $stdout); |
| | | } |
| | | } |
| | | // send part as-it-is |
| | | else { |
| | | // turn off output buffering and print part content |
| | | if ($part->body) { |
| | | header("Content-Length: " . sizeof($part->body)); |
| | | echo $part->body; |
| | | $sent = true; |
| | | } |
| | | else if ($part->size) { |
| | | if ($size = (int)$part->d_parameters['size']) { |
| | | header("Content-Length: $size"); |
| | | } |
| | | |
| | | $sent = $RCMAIL->storage->get_message_part($MESSAGE->uid, $part->mime_id, $part, true); |
| | | } |
| | | } |
| | |
| | | |
| | | function rcmail_message_attachments($attrib) |
| | | { |
| | | global $PRINT_MODE, $MESSAGE; |
| | | global $PRINT_MODE, $MESSAGE, $RCMAIL; |
| | | |
| | | $out = $ol = ''; |
| | | |
| | |
| | | } |
| | | |
| | | if ($PRINT_MODE) { |
| | | $ol .= html::tag('li', null, sprintf("%s (%s)", Q($filename), Q(show_bytes($attach_prop->size)))); |
| | | $size = $RCMAIL->message_part_size($attach_prop); |
| | | $ol .= html::tag('li', null, Q(sprintf("%s (%s)", $filename, $size))); |
| | | } |
| | | else { |
| | | if (mb_strlen($filename) > 50) { |
| | |
| | | |
| | | #messagelist tr td.size { |
| | | width: 60px; |
| | | text-align: right; |
| | | } |
| | | |
| | | #messagelist tr td.from, |