From e0960f6365b4b0af314d955847b9422067c83eb2 Mon Sep 17 00:00:00 2001
From: alecpl <alec@alec.pl>
Date: Fri, 25 Nov 2011 08:47:07 -0500
Subject: [PATCH] - Prevent from memory_limit exceeding when trying to parse big messages bodies (#1487424):   don't try to parse it, display notice with a link to download it directly

---
 skins/default/common.css                |    1 
 CHANGELOG                               |    1 
 program/include/main.inc                |   15 +++++++
 skins/default/mail.css                  |   14 +++++-
 program/localization/en_US/messages.inc |    1 
 program/steps/mail/func.inc             |   45 +++++++++++++++-------
 program/localization/en_US/labels.inc   |    1 
 program/steps/mail/get.inc              |   24 +++++++++---
 8 files changed, 77 insertions(+), 25 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index b0b2c8c..2426591 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
 CHANGELOG Roundcube Webmail
 ===========================
 
+- Prevent from memory_limit exceeding when trying to parse big messages bodies (#1487424)
 - Add possibility to add SASL mechanisms for SMTP in smtp_connect hook (#1487937)
 - Mark (with different color) folders with recent messages (#1486234)
 - Fix possible infinite redirect on attachment preview (#1488199)
diff --git a/program/include/main.inc b/program/include/main.inc
index 0025018..8a08125 100644
--- a/program/include/main.inc
+++ b/program/include/main.inc
@@ -1846,6 +1846,21 @@
 
 
 /**
+ * Check if we can process not exceeding memory_limit
+ *
+ * @param integer Required amount of memory
+ * @return boolean
+ */
+function rcmail_mem_check($need)
+{
+  $mem_limit = parse_bytes(ini_get('memory_limit'));
+  $memory    = function_exists('memory_get_usage') ? memory_get_usage() : 16*1024*1024; // safe value: 16MB
+
+  return $mem_limit && $memory + $need > $mem_limit ? false : true;
+}
+
+
+/**
  * Check if working in SSL mode
  *
  * @param integer HTTPS port number
diff --git a/program/localization/en_US/labels.inc b/program/localization/en_US/labels.inc
index a0f1c31..a05ba82 100644
--- a/program/localization/en_US/labels.inc
+++ b/program/localization/en_US/labels.inc
@@ -152,7 +152,6 @@
 $labels['deleted'] = 'Deleted';
 $labels['invert'] = 'Invert';
 $labels['filter'] = 'Filter';
-
 $labels['list'] = 'List';
 $labels['threads'] = 'Threads';
 $labels['expand-all'] = 'Expand All';
diff --git a/program/localization/en_US/messages.inc b/program/localization/en_US/messages.inc
index e3d914d..aae086e 100644
--- a/program/localization/en_US/messages.inc
+++ b/program/localization/en_US/messages.inc
@@ -157,5 +157,6 @@
 $messages['invalidimageformat'] = 'Not a valid image format.';
 $messages['mispellingsfound'] = 'Spelling errors detected in the message.';
 $messages['parentnotwritable'] = 'Unable to create/move folder into selected parent folder. No access rights.';
+$messages['messagetoobig'] = 'The message part is too big to process it.';
 
 ?>
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index 22ad7d5..b06feda 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -972,7 +972,7 @@
  * @return string HTML content showing the message body
  */
 function rcmail_message_body($attrib)
-  {
+{
   global $CONFIG, $OUTPUT, $MESSAGE, $IMAP, $RCMAIL, $REMOTE_OBJECTS;
 
   if (!is_array($MESSAGE->parts) && empty($MESSAGE->body))
@@ -989,14 +989,20 @@
     if (preg_match('/^headertable([a-z]+)$/i', $attr, $regs))
       $header_attrib[$regs[1]] = $value;
 
-  if (!empty($MESSAGE->parts))
-    {
-    foreach ($MESSAGE->parts as $i => $part)
-      {
+  if (!empty($MESSAGE->parts)) {
+    foreach ($MESSAGE->parts as $i => $part) {
       if ($part->type == 'headers')
         $out .= rcmail_message_headers(sizeof($header_attrib) ? $header_attrib : NULL, $part->headers);
-      else if ($part->type == 'content' && $part->size)
-        {
+      else if ($part->type == 'content' && $part->size) {
+        // Check if we have enough memory to handle the message in it
+        // #1487424: we need up to 10x more memory than the body
+        if (!rcmail_mem_check($part->size * 10)) {
+          $out .= html::span('part-notice', rcube_label('messagetoobig'). ' '
+            . html::a('?_task=mail&_action=get&_download=1&_uid='.$MESSAGE->uid.'&_part='.$part->mime_id
+              .'&_mbox='. urlencode($IMAP->get_mailbox_name()), rcube_label('download')));
+          continue;
+        }
+
         if (empty($part->ctype_parameters) || empty($part->ctype_parameters['charset']))
           $part->ctype_parameters['charset'] = $MESSAGE->headers->charset;
 
@@ -1030,16 +1036,25 @@
         }
         else
           $out .= html::div('message-part', $plugin['prefix'] . $body);
-        }
       }
     }
+  }
   else {
-    $plugin = $RCMAIL->plugins->exec_hook('message_body_prefix', array(
-      'part' => $MESSAGE, 'prefix' => ''));
-
-    $out .= html::div('message-part', $plugin['prefix'] . html::tag('pre', array(),
-      rcmail_plain_body(Q($MESSAGE->body, 'strict', false))));
+    // Check if we have enough memory to handle the message in it
+    // #1487424: we need up to 10x more memory than the body
+    if (!rcmail_mem_check(strlen($MESSAGE->body) * 10)) {
+      $out .= html::span('part-notice', rcube_label('messagetoobig'). ' '
+        . html::a('?_task=mail&_action=get&_download=1&_uid='.$MESSAGE->uid.'&_part=0'
+          .'&_mbox='. urlencode($IMAP->get_mailbox_name()), rcube_label('download')));
     }
+    else {
+      $plugin = $RCMAIL->plugins->exec_hook('message_body_prefix', array(
+        'part' => $MESSAGE, 'prefix' => ''));
+
+      $out .= html::div('message-part', $plugin['prefix'] . html::tag('pre', array(),
+        rcmail_plain_body(Q($MESSAGE->body, 'strict', false))));
+    }
+  }
 
   // list images after mail body
   if ($CONFIG['inline_images'] && !empty($MESSAGE->attachments)) {
@@ -1057,7 +1072,7 @@
             'title' => $attach_prop->filename,
             'alt' => $attach_prop->filename,
           )));
-        }
+      }
     }
   }
 
@@ -1066,7 +1081,7 @@
     $OUTPUT->set_env('blockedobjects', true);
 
   return html::div($attrib, $out);
-  }
+}
 
 
 /**
diff --git a/program/steps/mail/get.inc b/program/steps/mail/get.inc
index 828f8de..a0ea3e1 100644
--- a/program/steps/mail/get.inc
+++ b/program/steps/mail/get.inc
@@ -70,7 +70,7 @@
   exit;
 }
 
-else if ($pid = get_input_value('_part', RCUBE_INPUT_GET)) {
+else if (strlen($pid = get_input_value('_part', RCUBE_INPUT_GET))) {
 
   if ($part = $MESSAGE->mime_parts[$pid]) {
     $ctype_primary = strtolower($part->ctype_primary);
@@ -109,18 +109,30 @@
 
     // deliver part content
     if ($ctype_primary == 'text' && $ctype_secondary == 'html' && empty($plugin['download'])) {
-      // get part body if not available
-      if (!$part->body)
-        $part->body = $MESSAGE->get_part_content($part->mime_id);
+      // Check if we have enough memory to handle the message in it
+      // #1487424: we need up to 10x more memory than the body
+      if (!rcmail_mem_check($part->size * 10)) {
+        $out = '<body>' . rcube_label('messagetoobig'). ' '
+          . html::a('?_task=mail&_action=get&_download=1&_uid='.$MESSAGE->uid.'&_part='.$part->mime_id
+            .'&_mbox='. urlencode($IMAP->get_mailbox_name()), rcube_label('download')) . '</body></html>';
+      }
+      else {
+        // get part body if not available
+        if (!$part->body)
+          $part->body = $MESSAGE->get_part_content($part->mime_id);
+
+        $out = rcmail_print_body($part, array('safe' => $MESSAGE->is_safe, 'inline_html' => false));
+      }
 
       $OUTPUT = new rcube_html_page();
-      $OUTPUT->write(rcmail_print_body($part, array('safe' => $MESSAGE->is_safe, 'inline_html' => false)));
+      $OUTPUT->write($out);
     }
     else {
       // don't kill the connection if download takes more than 30 sec.
       @set_time_limit(0);
 
-      $filename = $part->filename ? $part->filename : ($MESSAGE->subject ? $MESSAGE->subject : 'roundcube') . '.'.$ctype_secondary;
+      $ext      = '.' . ($mimetype == 'text/plain' ? 'txt' : $ctype_secondary);
+      $filename = $part->filename ? $part->filename : ($MESSAGE->subject ? $MESSAGE->subject : 'roundcube') . $ext;
       $filename = preg_replace('[\r\n]', '', $filename);
 
       if ($browser->ie && $browser->ver < 7)
diff --git a/skins/default/common.css b/skins/default/common.css
index bf00dc3..0597d21 100644
--- a/skins/default/common.css
+++ b/skins/default/common.css
@@ -232,6 +232,7 @@
 }
 
 #message div.notice,
+#messagebody .part-notice,
 #message-objects div.notice
 {
   background: url(images/display/icons.png) 6px 3px no-repeat;
diff --git a/skins/default/mail.css b/skins/default/mail.css
index 42a62de..accf3fd 100644
--- a/skins/default/mail.css
+++ b/skins/default/mail.css
@@ -1203,20 +1203,28 @@
   margin: 8px;
 }
 
-#message-objects div
+#messagebody span.part-notice
+{
+  display: block;
+}
+
+#message-objects div,
+#messagebody span.part-notice
 {
   margin: 8px;
   min-height: 20px;
   padding: 10px 10px 6px 46px;
 }
 
-#message-objects div a
+#message-objects div a,
+#messagebody span.part-notice a
 {
   color: #666666;
   padding-left: 10px;
 }
 
-#message-objects div a:hover
+#message-objects div a:hover,
+#messagebody span.part-notice a:hover
 {
   color: #333333;
 }

--
Gitblit v1.9.1