From 29c54229cfbc104930e7743cecc212f53aed8a15 Mon Sep 17 00:00:00 2001 From: alecpl <alec@alec.pl> Date: Mon, 28 Feb 2011 12:33:26 -0500 Subject: [PATCH] - Fix parsing links with non-printable characters inside (#1487805) --- program/steps/mail/func.inc | 206 +++++++++++++++++++++++++-------------------------- 1 files changed, 102 insertions(+), 104 deletions(-) diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index 747dce5..0903176 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -5,7 +5,7 @@ | program/steps/mail/func.inc | | | | This file is part of the Roundcube Webmail client | - | Copyright (C) 2005-2010, Roundcube Dev. - Switzerland | + | Copyright (C) 2005-2010, The Roundcube Dev Team | | Licensed under the GNU GPL | | | | PURPOSE: | @@ -56,9 +56,9 @@ // set default sort col/order to session if (!isset($_SESSION['sort_col'])) - $_SESSION['sort_col'] = $CONFIG['message_sort_col']; + $_SESSION['sort_col'] = !empty($CONFIG['message_sort_col']) ? $CONFIG['message_sort_col'] : ''; if (!isset($_SESSION['sort_order'])) - $_SESSION['sort_order'] = $CONFIG['message_sort_order']; + $_SESSION['sort_order'] = strtoupper($CONFIG['message_sort_order']) == 'ASC' ? 'ASC' : 'DESC'; // set threads mode $a_threading = $RCMAIL->config->get('message_threading', array()); @@ -102,14 +102,10 @@ // set current mailbox and some other vars in client environment $OUTPUT->set_env('mailbox', $mbox_name); $OUTPUT->set_env('pagesize', $IMAP->page_size); - $OUTPUT->set_env('quota', $IMAP->get_capability('quota')); + $OUTPUT->set_env('quota', $IMAP->get_capability('QUOTA')); $OUTPUT->set_env('delimiter', $IMAP->get_hierarchy_delimiter()); $OUTPUT->set_env('threading', (bool) $IMAP->threading); - $OUTPUT->set_env('threads', $IMAP->threading - || $IMAP->get_capability('thread=references') - || $IMAP->get_capability('thread=orderedsubject') - || $IMAP->get_capability('thread=refs') - ); + $OUTPUT->set_env('threads', $IMAP->threading || $IMAP->get_capability('THREAD')); if ($CONFIG['flag_for_deletion']) $OUTPUT->set_env('flag_for_deletion', true); @@ -277,7 +273,7 @@ if (in_array($col, array('from', 'to', 'cc', 'replyto'))) $cont = Q(rcmail_address_string($header->$col, 3), 'show'); else if ($col=='subject') { - $cont = abbreviate_string(trim($IMAP->decode_header($header->$col)), 160); + $cont = trim($IMAP->decode_header($header->$col)); if (!$cont) $cont = rcube_label('nosubject'); $cont = Q($cont); } @@ -436,65 +432,6 @@ } -function rcmail_quota_display($attrib) - { - global $OUTPUT; - - if (!$attrib['id']) - $attrib['id'] = 'rcmquotadisplay'; - - if(isset($attrib['display'])) - $_SESSION['quota_display'] = $attrib['display']; - - $OUTPUT->add_gui_object('quotadisplay', $attrib['id']); - - $quota = rcmail_quota_content($attrib); - - $OUTPUT->add_script('$(document).ready(function(){ - rcmail.set_quota('.json_serialize($quota).')});', 'foot'); - - return html::span($attrib, ''); - } - - -function rcmail_quota_content($attrib=NULL) - { - global $COMM_PATH, $RCMAIL; - - $quota = $RCMAIL->imap->get_quota(); - $quota = $RCMAIL->plugins->exec_hook('quota', $quota); - - $quota_result = (array) $quota; - $quota_result['type'] = isset($_SESSION['quota_display']) ? $_SESSION['quota_display'] : ''; - - if (!$quota['total'] && $RCMAIL->config->get('quota_zero_as_unlimited')) { - $quota_result['title'] = rcube_label('unlimited'); - $quota_result['percent'] = 0; - } - else if ($quota['total']) { - if (!isset($quota['percent'])) - $quota_result['percent'] = min(100, round(($quota['used']/max(1,$quota['total']))*100)); - - $title = sprintf('%s / %s (%.0f%%)', - show_bytes($quota['used'] * 1024), show_bytes($quota['total'] * 1024), - $quota_result['percent']); - - $quota_result['title'] = $title; - - if ($attrib['width']) - $quota_result['width'] = $attrib['width']; - if ($attrib['height']) - $quota_result['height'] = $attrib['height']; - } - else { - $quota_result['title'] = rcube_label('unknown'); - $quota_result['percent'] = 0; - } - - return $quota_result; - } - - function rcmail_get_messagecount_text($count=NULL, $page=NULL) { global $RCMAIL, $IMAP; @@ -545,7 +482,7 @@ { global $RCMAIL; - $old_unseen = $_SESSION['unseen_count'][$mbox_name]; + $old_unseen = rcmail_get_unseen_count($mbox_name); if ($count === null) $unseen = $RCMAIL->imap->messagecount($mbox_name, 'UNSEEN', $force); @@ -555,10 +492,30 @@ if ($unseen != $old_unseen || ($mbox_name == 'INBOX')) $RCMAIL->output->command('set_unread_count', $mbox_name, $unseen, ($mbox_name == 'INBOX')); - // @TODO: this data is doubled (session and cache tables) if caching is enabled - $_SESSION['unseen_count'][$mbox_name] = $unseen; + rcmail_set_unseen_count($mbox_name, $unseen); return $unseen; +} + + +function rcmail_set_unseen_count($mbox_name, $count) +{ + // @TODO: this data is doubled (session and cache tables) if caching is enabled + + // Make sure we have an array here (#1487066) + if (!is_array($_SESSION['unseen_count'])) + $_SESSION['unseen_count'] = array(); + + $_SESSION['unseen_count'][$mbox_name] = $count; +} + + +function rcmail_get_unseen_count($mbox_name) +{ + if (is_array($_SESSION['unseen_count']) && array_key_exists($mbox_name, $_SESSION['unseen_count'])) + return $_SESSION['unseen_count'][$mbox_name]; + else + return null; } @@ -708,7 +665,8 @@ // trigger plugin hook $data = $RCMAIL->plugins->exec_hook('message_part_before', - array('type' => $part->ctype_secondary, 'body' => $part->body) + $p + array('safe' => false, 'plain' => false, 'inline_html' => true)); + array('type' => $part->ctype_secondary, 'body' => $part->body, 'id' => $part->mime_id) + + $p + array('safe' => false, 'plain' => false, 'inline_html' => true)); // convert html to text/plain if ($data['type'] == 'html' && $data['plain']) { @@ -741,7 +699,8 @@ $body = rcmail_plain_body($body, $part->ctype_parameters['format'] == 'flowed'); // allow post-processing of the message body - $data = $RCMAIL->plugins->exec_hook('message_part_after', array('type' => $part->ctype_secondary, 'body' => $body) + $data); + $data = $RCMAIL->plugins->exec_hook('message_part_after', + array('type' => $part->ctype_secondary, 'body' => $body, 'id' => $part->mime_id) + $data); return $data['type'] == 'html' ? $data['body'] : html::tag('pre', array(), $data['body']); } @@ -831,8 +790,12 @@ $quote_level = $q; } - // quote plain text - $body = Q(join("\n", $a_lines), 'dummy', false); + $body = join("\n", $a_lines); + + // quote plain text (don't use Q() here, to display entities "as is") + $table = get_html_translation_table(HTML_SPECIALCHARS); + unset($table['?']); + $body = strtr($body, $table); // colorize signature (up to <sig_max_lines> lines) $len = strlen($body); @@ -866,10 +829,10 @@ case 'style': // decode all escaped entities and reduce to ascii strings - $stripped = preg_replace('/[^a-zA-Z\(:]/', '', rcmail_xss_entity_decode($content)); + $stripped = preg_replace('/[^a-zA-Z\(:;]/', '', rcmail_xss_entity_decode($content)); // now check for evil strings like expression, behavior or url() - if (!preg_match('/expression|behavior|url\(|import/', $stripped)) { + if (!preg_match('/expression|behavior|url\(|import[^a]/', $stripped)) { $out = html::tag('style', array('type' => 'text/css'), $content); break; } @@ -962,10 +925,14 @@ else $header_value = trim($IMAP->decode_header($value)); - $output_headers[$hkey] = array('title' => rcube_label($hkey), 'value' => $header_value, 'raw' => $value); + $output_headers[$hkey] = array( + 'title' => rcube_label(preg_replace('/(^mail-|-)/', '', $hkey)), + 'value' => $header_value, 'raw' => $value + ); } - $plugin = $RCMAIL->plugins->exec_hook('message_headers_output', array('output' => $output_headers, 'headers' => $MESSAGE->headers)); + $plugin = $RCMAIL->plugins->exec_hook('message_headers_output', + array('output' => $output_headers, 'headers' => $MESSAGE->headers)); // compose html table $table = new html_table(array('cols' => 2)); @@ -975,16 +942,25 @@ $table->add(array('class' => 'header '.$hkey), Q($row['value'], ($hkey == 'subject' ? 'strict' : 'show'))); } - // all headers division - $table->add(array('colspan' => 2, 'class' => "more-headers show-headers", 'onclick' => "return ".JS_OBJECT_NAME.".command('load-headers','',this)"), ''); - $table->add_row(array('id' => "all-headers")); - $table->add(array('colspan' => 2, 'class' => "all"), html::div(array('id' => 'headers-source'), '')); + return $table->show($attrib); +} + + +/** + * return block to show full message headers + */ +function rcmail_message_full_headers($attrib, $headers=NULL) +{ + global $OUTPUT; + + $html = html::div(array('class' => "more-headers show-headers", 'onclick' => "return ".JS_OBJECT_NAME.".command('load-headers','',this)"), ''); + $html .= html::div(array('id' => "all-headers", 'class' => "all", 'style' => 'display:none'), html::div(array('id' => 'headers-source'), '')); $OUTPUT->add_gui_object('all_headers_row', 'all-headers'); $OUTPUT->add_gui_object('all_headers_box', 'headers-source'); - - return $table->show($attrib); - } + + return html::div($attrib, $html); +} /** @@ -1174,20 +1150,22 @@ $attributes = array(); // Handle body attributes that doesn't play nicely with div elements - if (preg_match('/<div class="' . preg_quote($body_id, '/') . '" ([^>]+)/', $body, $m)) { + $regexp = '/<div class="' . preg_quote($body_id, '/') . '"([^>]*)/'; + if (preg_match($regexp, $body, $m)) { $attrs = $m[0]; // Get bgcolor, we'll set it as background-color of the message container - if (preg_match('/bgcolor=["\']*([a-z0-9#]+)["\']*/', $attrs, $mb)) { + if ($m[1] && preg_match('/bgcolor=["\']*([a-z0-9#]+)["\']*/', $attrs, $mb)) { $attributes['background-color'] = $mb[1]; $attrs = preg_replace('/bgcolor=["\']*([a-z0-9#]+)["\']*/', '', $attrs); } // Get background, we'll set it as background-image of the message container - if (preg_match('/background=["\']*([^"\'>\s]+)["\']*/', $attrs, $mb)) { + if ($m[1] && preg_match('/background=["\']*([^"\'>\s]+)["\']*/', $attrs, $mb)) { $attributes['background-image'] = 'url('.$mb[1].')'; $attrs = preg_replace('/background=["\']*([^"\'>\s]+)["\']*/', '', $attrs); } - if (!empty($attributes)) - $body = preg_replace('/<div class="' . preg_quote($body_id, '/') . '" [^>]+/', rtrim($attrs), $body, 1); + if (!empty($attributes)) { + $body = preg_replace($regexp, rtrim($attrs), $body, 1); + } // handle body styles related to background image if ($attributes['background-image']) { @@ -1203,8 +1181,9 @@ } // make sure there's 'rcmBody' div, we need it for proper css modification // its name is hardcoded in rcmail_message_body() also - else + else { $body = '<div class="' . $body_id . '">' . $body . '</div>'; + } return $body; } @@ -1215,15 +1194,19 @@ */ function rcmail_alter_html_link($matches) { - global $EMAIL_ADDRESS_PATTERN; + global $RCMAIL, $EMAIL_ADDRESS_PATTERN; $tag = $matches[1]; $attrib = parse_attrib_string($matches[2]); $end = '>'; + // Remove non-printable characters in URL (#1487805) + $attrib['href'] = preg_replace('/[\x00-\x1F]/', '', $attrib['href']); + if ($tag == 'link' && preg_match('/^https?:\/\//i', $attrib['href'])) { - $attrib['href'] = "?_task=utils&_action=modcss&u=" . urlencode($attrib['href']) - . "&c=" . urlencode($GLOBALS['rcmail_html_container_id']); + $tempurl = 'tmp-' . md5($attrib['href']) . '.css'; + $_SESSION['modcssurls'][$tempurl] = $attrib['href']; + $attrib['href'] = $RCMAIL->url(array('task' => 'utils', 'action' => 'modcss', 'u' => $tempurl, 'c' => $GLOBALS['rcmail_html_container_id'])); $end = ' />'; } else if (preg_match('/^mailto:'.$EMAIL_ADDRESS_PATTERN.'(\?[^"\'>]+)?/i', $attrib['href'], $mailto)) { @@ -1271,17 +1254,17 @@ // IDNA ASCII to Unicode if ($name == $mailto) - $name = idn_to_utf8($name); + $name = rcube_idn_to_utf8($name); if ($string == $mailto) - $string = idn_to_utf8($string); - $mailto = idn_to_utf8($mailto); + $string = rcube_idn_to_utf8($string); + $mailto = rcube_idn_to_utf8($mailto); if ($PRINT_MODE) { $out .= sprintf('%s <%s>', Q($name), $mailto); } else if (check_email($part['mailto'], false)) { if ($linked) { - $out .= html::a(array( + $address = html::a(array( 'href' => 'mailto:'.$mailto, 'onclick' => sprintf("return %s.command('compose','%s',this)", JS_OBJECT_NAME, JQ($mailto)), 'title' => $mailto, @@ -1290,12 +1273,12 @@ Q($name ? $name : $mailto)); } else { - $out .= html::span(array('title' => $mailto, 'class' => "rcmContactAddress"), + $address = html::span(array('title' => $mailto, 'class' => "rcmContactAddress"), Q($name ? $name : $mailto)); } if ($addicon && $got_writable_abook) { - $out .= ' ' . html::a(array( + $address = html::span(null, $address . html::a(array( 'href' => "#add", 'onclick' => sprintf("return %s.command('add-contact','%s',this)", JS_OBJECT_NAME, urlencode($string)), 'title' => rcube_label('addtoaddressbook'), @@ -1303,8 +1286,9 @@ html::img(array( 'src' => $CONFIG['skin_path'] . $addicon, 'alt' => "Add contact", - ))); + )))); } + $out .= $address; } else { if ($name) @@ -1781,6 +1765,7 @@ 'quotadisplay' => 'rcmail_quota_display', 'mailboxname' => 'rcmail_mailbox_name_display', 'messageheaders' => 'rcmail_message_headers', + 'messagefullheaders' => 'rcmail_message_full_headers', 'messagebody' => 'rcmail_message_body', 'messagecontentframe' => 'rcmail_messagecontent_frame', 'messagepartframe' => 'rcmail_message_part_frame', @@ -1789,4 +1774,17 @@ 'searchform' => array($OUTPUT, 'search_form'), )); - +// register action aliases +$RCMAIL->register_action_map(array( + 'preview' => 'show.inc', + 'print' => 'show.inc', + 'moveto' => 'move_del.inc', + 'delete' => 'move_del.inc', + 'send' => 'sendmail.inc', + 'expunge' => 'folders.inc', + 'purge' => 'folders.inc', + 'remove-attachment' => 'attachments.inc', + 'display-attachment' => 'attachments.inc', + 'upload' => 'attachments.inc', + 'group-expand' => 'autocomplete.inc', +)); -- Gitblit v1.9.1