From 187833dff94146a9b176a1ca4a5c9996809fd4a7 Mon Sep 17 00:00:00 2001
From: alecpl <alec@alec.pl>
Date: Thu, 02 Jun 2011 08:36:32 -0400
Subject: [PATCH] - Apply fixes from trunk

---
 program/steps/mail/func.inc |  267 +++++++++++++++++++++++++---------------------------
 1 files changed, 129 insertions(+), 138 deletions(-)

diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index a4f945f..d738322 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -22,11 +22,11 @@
 // setup some global vars used by mail steps
 $SENT_MBOX = $RCMAIL->config->get('sent_mbox');
 $DRAFTS_MBOX = $RCMAIL->config->get('drafts_mbox');
-$SEARCH_MODS_DEFAULT = array('*' => array('subject'=>1, 'from'=>1), $SENT_MBOX => array('subject'=>1, 'to'=>1), $DRAFTS_MBOX => array('subject'=>1, 'to'=>1));
-
-// Simplified for IDN in Unicode
-//$EMAIL_ADDRESS_PATTERN = '([a-z0-9][a-z0-9\-\.\+\_]*@[a-z0-9][a-z0-9\-\.]*\\.[a-z]{2,5})';
-$EMAIL_ADDRESS_PATTERN = '([a-z0-9][a-z0-9\-\.\+\_]*@[^&@"\'.][^@&"\']*\\.[a-z]{2,5})';
+$SEARCH_MODS_DEFAULT = array(
+    '*'         => array('subject'=>1, 'from'=>1),
+    $SENT_MBOX  => array('subject'=>1, 'to'=>1),
+    $DRAFTS_MBOX => array('subject'=>1, 'to'=>1)
+);
 
 // actions that do not require imap connection here
 $NOIMAP_ACTIONS = array('addcontact', 'autocomplete', 'upload', 'display-attachment', 'remove-attachment', 'get');
@@ -46,7 +46,7 @@
 }
 
 // set imap properties and session vars
-if ($mbox = get_input_value('_mbox', RCUBE_INPUT_GPC))
+if (strlen(trim($mbox = get_input_value('_mbox', RCUBE_INPUT_GPC, true))))
   $IMAP->set_mailbox(($_SESSION['mbox'] = $mbox));
 else if ($IMAP)
   $_SESSION['mbox'] = $IMAP->get_mailbox_name();
@@ -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;
 }
 
 
@@ -618,7 +575,7 @@
     '',
     '<html>',
   );
-  $html = preg_replace($html_search, $html_replace, $html);
+  $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) {
@@ -650,6 +607,7 @@
       $html = '<head></head>'. $html;
     $html = substr_replace($html, '<meta http-equiv="Content-Type" content="text/html; charset='.RCMAIL_CHARSET.'" />', intval(stripos($html, '<head>')+6), 0);
   }
+
   // turn relative into absolute urls
   $html = rcmail_resolve_base($html);
 
@@ -708,7 +666,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 +700,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']);
 }
@@ -767,72 +727,79 @@
   $body = preg_replace_callback($replacer->mailto_pattern, array($replacer, 'mailto_callback'), $body);
 
   // split body into single lines
-  $a_lines = preg_split('/\r?\n/', $body);
+  $body = preg_split('/\r?\n/', $body);
   $quote_level = 0;
   $last = -1;
 
   // find/mark quoted lines...
-  for ($n=0, $cnt=count($a_lines); $n < $cnt; $n++) {
-    if ($a_lines[$n][0] == '>' && preg_match('/^(>+\s*)+/', $a_lines[$n], $regs)) {
+  for ($n=0, $cnt=count($body); $n < $cnt; $n++) {
+    if ($body[$n][0] == '>' && preg_match('/^(>+\s*)+/', $body[$n], $regs)) {
       $q = strlen(preg_replace('/\s/', '', $regs[0]));
-      $a_lines[$n] = substr($a_lines[$n], strlen($regs[0]));
+      $body[$n] = substr($body[$n], strlen($regs[0]));
 
-      if ($q > $quote_level)
-        $a_lines[$n] = $replacer->get_replacement($replacer->add(
-          str_repeat('<blockquote>', $q - $quote_level))) . $a_lines[$n];
-      else if ($q < $quote_level)
-        $a_lines[$n] = $replacer->get_replacement($replacer->add(
-          str_repeat('</blockquote>', $quote_level - $q))) . $a_lines[$n];
+      if ($q > $quote_level) {
+        $body[$n] = $replacer->get_replacement($replacer->add(
+          str_repeat('<blockquote>', $q - $quote_level))) . $body[$n];
+      }
+      else if ($q < $quote_level) {
+        $body[$n] = $replacer->get_replacement($replacer->add(
+          str_repeat('</blockquote>', $quote_level - $q))) . $body[$n];
+      }
       else if ($flowed) {
         // previous line is flowed
-        if (isset($a_lines[$last]) && $a_lines[$n]
-          && $a_lines[$last][strlen($a_lines[$last])-1] == ' ') {
+        if (isset($body[$last]) && $body[$n]
+          && $body[$last][strlen($body[$last])-1] == ' ') {
           // merge lines
-          $a_lines[$last] .= $a_lines[$n];
-          unset($a_lines[$n]);
+          $body[$last] .= $body[$n];
+          unset($body[$n]);
         }
-        else
+        else {
           $last = $n;
+        }
       }
     }
     else {
       $q = 0;
       if ($flowed) {
         // sig separator - line is fixed
-        if ($a_lines[$n] == '-- ') {
-          $last = $n;
+        if ($body[$n] == '-- ') {
+          $last = $last_sig = $n;
         }
         else {
           // remove space-stuffing
-          if ($a_lines[$n][0] == ' ')
-            $a_lines[$n] = substr($a_lines[$n], 1);
+          if ($body[$n][0] == ' ')
+            $body[$n] = substr($body[$n], 1);
 
           // previous line is flowed?
-          if (isset($a_lines[$last]) && $a_lines[$n]
-            && $a_lines[$last] != '-- '
-            && $a_lines[$last][strlen($a_lines[$last])-1] == ' '
+          if (isset($body[$last]) && $body[$n]
+            && $last != $last_sig
+            && $body[$last][strlen($body[$last])-1] == ' '
           ) {
-            $a_lines[$last] .= $a_lines[$n];
-            unset($a_lines[$n]);
+            $body[$last] .= $body[$n];
+            unset($body[$n]);
           }
           else {
             $last = $n;
           }
         }
         if ($quote_level > 0)
-          $a_lines[$last] = $replacer->get_replacement($replacer->add(
-            str_repeat('</blockquote>', $quote_level))) . $a_lines[$last];
+          $body[$last] = $replacer->get_replacement($replacer->add(
+            str_repeat('</blockquote>', $quote_level))) . $body[$last];
       }
       else if ($quote_level > 0)
-        $a_lines[$n] = $replacer->get_replacement($replacer->add(
-          str_repeat('</blockquote>', $quote_level))) . $a_lines[$n];
+        $body[$n] = $replacer->get_replacement($replacer->add(
+          str_repeat('</blockquote>', $quote_level))) . $body[$n];
     }
 
     $quote_level = $q;
   }
 
-  // quote plain text
-  $body = Q(join("\n", $a_lines), 'dummy', false);
+  $body = join("\n", $body);
+
+  // 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 +833,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,29 +929,42 @@
     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));
 
   foreach ($plugin['output'] as $hkey => $row) {
     $table->add(array('class' => 'header-title'), Q($row['title']));
-    $table->add(array('class' => $hkey, 'width' => "90%"), Q($row['value'], ($hkey == 'subject' ? 'strict' : 'show')));
+    $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);
+}
 
 
 /**
@@ -1107,8 +1087,8 @@
     $replacer = new rcube_base_replacer($regs[2]);
 
     // replace all relative paths
-    $body = preg_replace_callback('/(src|background|href)=(["\']?)([\.\/]+[^"\'\s]+)(\2|\s|>)/Ui', array($replacer, 'callback'), $body);
-    $body = preg_replace_callback('/(url\s*\()(["\']?)([\.\/]+[^"\'\)\s]+)(\2)\)/Ui', array($replacer, 'callback'), $body);
+    $body = preg_replace_callback('/(src|background|href)=(["\']?)([^"\'\s]+)(\2|\s|>)/Ui', array($replacer, 'callback'), $body);
+    $body = preg_replace_callback('/(url\s*\()(["\']?)([^"\'\)\s]+)(\2)\)/Ui', array($replacer, 'callback'), $body);
   }
 
   return $body;
@@ -1174,20 +1154,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 +1185,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,23 +1198,30 @@
  */
 function rcmail_alter_html_link($matches)
 {
-  global $EMAIL_ADDRESS_PATTERN;
+  global $RCMAIL;
+
+  // Support unicode/punycode in top-level domain part
+  $EMAIL_PATTERN = '([a-z0-9][a-z0-9\-\.\+\_]*@[^&@"\'.][^@&"\']*\\.([^\\x00-\\x40\\x5b-\\x60\\x7b-\\x7f]{2,}|xn--[a-z0-9]{2,}))';
 
   $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&amp;_action=modcss&amp;u=" . urlencode($attrib['href'])
-        . "&amp;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)) {
+  else if (preg_match('/^mailto:'.$EMAIL_PATTERN.'(\?[^"\'>]+)?/i', $attrib['href'], $mailto)) {
     $attrib['href'] = $mailto[0];
     $attrib['onclick'] = sprintf(
       "return %s.command('compose','%s',this)",
       JS_OBJECT_NAME,
-      JQ($mailto[1].$mailto[2]));
+      JQ($mailto[1].$mailto[3]));
   }
   else if (!empty($attrib['href']) && $attrib['href'][0] != '#') {
     $attrib['target'] = '_blank';
@@ -1271,10 +1261,10 @@
 
     // 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 &lt;%s&gt;', Q($name), $mailto);
@@ -1781,6 +1771,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',

--
Gitblit v1.9.1