From 91790e41f3fa307658077043bc2fa5f71e270cf4 Mon Sep 17 00:00:00 2001
From: alecpl <alec@alec.pl>
Date: Tue, 09 Feb 2010 08:10:12 -0500
Subject: [PATCH] - Fix attachment excessive memory use, support messages of any size (#1484660)

---
 program/include/rcube_smtp.php     |   28 ++-
 CHANGELOG                          |    1 
 /dev/null                          |  190 -----------------------
 INSTALL                            |    4 
 program/include/rcube_imap.php     |   20 +
 program/steps/mail/attachments.inc |    7 
 program/steps/mail/func.inc        |   69 ++++++--
 program/lib/imap.inc               |   20 +
 program/steps/mail/sendmail.inc    |  132 ++++++++++-----
 9 files changed, 197 insertions(+), 274 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 8578fdc..02cce9f 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
 CHANGELOG RoundCube Webmail
 ===========================
 
+- Fix attachment excessive memory use, support messages of any size (#1484660)
 - Fix setting task name according to auth state
 - Password: fix vpopmaild driver (#1486478)
 - Add workaround for MySQL bug [http://bugs.mysql.com/bug.php?id=46293] (#1486474)
diff --git a/INSTALL b/INSTALL
index 00fb083..b2156e5 100644
--- a/INSTALL
+++ b/INSTALL
@@ -15,6 +15,10 @@
    - PCRE, DOM, JSON, XML, Session, Sockets (required)
    - libiconv (recommended)
    - mbstring, fileinfo, mcrypt (optional)
+* PEAR packages distributed with Roundcube or external:
+   - MDB2 2.5.0 or newer
+   - Mail_Mime 1.6.0 or newer
+   - Net_SMTP 1.4.1 or newer
 * php.ini options (see .htaccess file):
    - error_reporting E_ALL & ~E_NOTICE (or lower)
    - memory_limit>16MB (increase as suitable to support large attachments)
diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php
index 6ab0b6a..ecde6f9 100644
--- a/program/include/rcube_imap.php
+++ b/program/include/rcube_imap.php
@@ -1699,17 +1699,27 @@
   /**
    * Append a mail message (source) to a specific mailbox
    *
-   * @param string Target mailbox
-   * @param string Message source
+   * @param string   Target mailbox
+   * @param string   The message source string or filename
+   * @param string   Headers string if $message contains only the body
+   * @param boolean  True if $message is a filename
+   *
    * @return boolean True on success, False on error
    */
-  function save_message($mbox_name, &$message)
+  function save_message($mbox_name, &$message, $headers='', $is_file=false)
     {
     $mailbox = $this->mod_mailbox($mbox_name);
 
     // make sure mailbox exists
-    if (($mailbox == 'INBOX') || in_array($mailbox, $this->_list_mailboxes()))
-      $saved = iil_C_Append($this->conn, $mailbox, $message);
+    if (($mailbox == 'INBOX') || in_array($mailbox, $this->_list_mailboxes())) {
+      if ($is_file) {
+        $separator = rcmail::get_instance()->config->header_delimiter();
+        $saved = iil_C_AppendFromFile($this->conn, $mailbox, $message,
+          $headers, $separator.$separator);
+        }
+      else
+        $saved = iil_C_Append($this->conn, $mailbox, $message);
+      }
 
     if ($saved)
       {
diff --git a/program/include/rcube_mail_mime.php b/program/include/rcube_mail_mime.php
deleted file mode 100644
index 2176e64..0000000
--- a/program/include/rcube_mail_mime.php
+++ /dev/null
@@ -1,190 +0,0 @@
-<?php
-
-/*
- +-----------------------------------------------------------------------+
- | program/include/rcube_mail_mime.php                                   |
- |                                                                       |
- | This file is part of the RoundCube Webmail client                     |
- | Copyright (C) 2007-2009, RoundCube Dev. - Switzerland                 |
- | Licensed under the GNU GPL                                            |
- |                                                                       |
- | PURPOSE:                                                              |
- |   Extend PEAR:Mail_mime class and override encodeHeaders method       |
- |                                                                       |
- +-----------------------------------------------------------------------+
- | Author: Thomas Bruederli <roundcube@gmail.com>                        |
- +-----------------------------------------------------------------------+
-
- $Id$
-
-*/
-
-
-/**
- * Replacement PEAR:Mail_mime with some additional or overloaded methods
- *
- * @package Mail
- */
-class rcube_mail_mime extends Mail_mime
-{
-
-  protected $mime_content;
-
-  /**
-   * Set build parameters
-   */
-  function setParam($param)
-  {
-    if (is_array($param)) {
-      $this->_build_params = array_merge($this->_build_params, $param);
-    }
-  }
-  
-  /**
-   * Adds an image to the list of embedded images.
-   *
-   * @param  string  $file       The image file name OR image data itself
-   * @param  string  $c_type     The content type
-   * @param  string  $name       The filename of the image.
-   *                             Only use if $file is the image data
-   * @param  bool    $isfilename Whether $file is a filename or not
-   *                             Defaults to true
-   * @param  string  $contentid  Desired Content-ID of MIME part
-   *                             Defaults to generated unique ID
-   * @return mixed   true on success or PEAR_Error object
-   * @access public
-   */
-  function addHTMLImage($file, $c_type='application/octet-stream', $name = '', $isfilename = true, $contentid = '')
-  {
-    $filedata = ($isfilename === true) ? $this->_file2str($file) : $file;
-    if ($isfilename === true) {
-      $filename = ($name == '' ? $file : $name);
-    }
-    else {
-      $filename = $name;
-    }
-
-    if (PEAR::isError($filedata)) {
-        return $filedata;
-    }
-
-    if ($contentid == '') {
-       $contentid = md5(uniqid(time()));
-    }
-
-    $this->_html_images[] = array(
-      'body'   => $filedata,
-      'name'   => $filename,
-      'c_type' => $c_type,
-      'cid'    => $contentid
-    );
-
-    return true;
-  }
-  
-  
-  /**
-  * returns the HTML body portion of the message
-  * @return string HTML body of the message
-  * @access public
-  */
-  function getHTMLBody()
-  {
-     return $this->_htmlbody;
-  }
-  
-  
-  /**
-   * Encodes a header as per RFC2047
-   *
-   * @param  array $input The header data to encode
-   * @param  array $params Extra build parameters
-   * @return array Encoded data
-   * @access private
-   * @override
-   */
-  function _encodeHeaders($input, $params = array())
-  {
-    $maxlen = 73;
-    $params += $this->_build_params;
-    
-    foreach ($input as $hdr_name => $hdr_value)
-    {
-      // if header contains e-mail addresses
-      if (preg_match('/\s<.+@[a-z0-9\-\.]+\.[a-z]+>/U', $hdr_value)) {
-        $chunks = rcube_explode_quoted_string(',', $hdr_value);
-      }
-      else {
-        $chunks = array($hdr_value);
-      }
-
-      $hdr_value = '';
-      $line_len = 0;
-
-      foreach ($chunks as $i => $value) {
-        $value = trim($value);
-
-        //This header contains non ASCII chars and should be encoded.
-        if (preg_match('/[\x80-\xFF]{1}/', $value)) {
-          $suffix = '';
-          // Don't encode e-mail address
-          if (preg_match('/(.+)\s(<.+@[a-z0-9\-\.]+>)$/Ui', $value, $matches)) {
-            $value = $matches[1];
-            $suffix = ' '.$matches[2];
-          }
-
-          switch ($params['head_encoding']) {
-            case 'base64':
-            // Base64 encoding has been selected.
-            $mode = 'B';
-            $encoded = base64_encode($value);
-            break;
-
-            case 'quoted-printable':
-            default:
-            // quoted-printable encoding has been selected
-            $mode = 'Q';
-            // replace ?, =, _ and spaces
-            $encoded = str_replace(array('=','_','?',' '), array('=3D','=5F','=3F','_'), $value);
-            $encoded = preg_replace('/([\x80-\xFF])/e', "'='.sprintf('%02X', ord('\\1'))", $encoded);
-          }
-
-          $value = '=?' . $params['head_charset'] . '?' . $mode . '?' . $encoded . '?=' . $suffix;
-        }
-
-        // add chunk to output string by regarding the header maxlen
-        $len = strlen($value);
-        if ($i == 0 || $line_len + $len < $maxlen) {
-          $hdr_value .= ($i>0?', ':'') . $value;
-          $line_len += $len + ($i>0?2:0);
-        }
-        else {
-          $hdr_value .= ($i>0?', ':'') . "\n " . $value;
-          $line_len = $len;
-        }
-      }
-
-      $input[$hdr_name] = wordwrap($hdr_value, 990, "\n", true);  // hard limit header length
-    }
-
-    return $input;
-  }
-
-
-  /**
-   * Provides caching of body of constructed MIME Message to avoid 
-   * duplicate construction of message and damage of MIME headers
-   *
-   * @return string The mime content
-   * @access public
-   * @override
-   */
-  public function &get($build_params = null)
-  {
-    if(empty($this->mime_content))
-      $this->mime_content = parent::get($build_params);
-    return $this->mime_content;
-  }
-
-}
-
diff --git a/program/include/rcube_smtp.php b/program/include/rcube_smtp.php
index 9253468..2991262 100644
--- a/program/include/rcube_smtp.php
+++ b/program/include/rcube_smtp.php
@@ -141,7 +141,8 @@
    *               Either as an associative array or a finally
    *               formatted string
    *
-   * @param string The full text of the message body, including any Mime parts, etc.
+   * @param mixed  The full text of the message body, including any Mime parts
+   *               or file handle
    *
    * @return bool  Returns true on success, or false on error
    */
@@ -206,17 +207,24 @@
       }
     }
 
-    // Concatenate headers and body so it can be passed by reference to SMTP_CONN->data
-    // so preg_replace in SMTP_CONN->quotedata will store a reference instead of a copy. 
-    // We are still forced to make another copy here for a couple ticks so we don't really 
-    // get to save a copy in the method call.
-    $data = $text_headers . "\r\n" . $body;
+    if (is_resource($body))
+    {
+      // file handle
+      $data = $body;
+      $text_headers = preg_replace('/[\r\n]+$/', '', $text_headers);
+    } else {
+      // Concatenate headers and body so it can be passed by reference to SMTP_CONN->data
+      // so preg_replace in SMTP_CONN->quotedata will store a reference instead of a copy. 
+      // We are still forced to make another copy here for a couple ticks so we don't really 
+      // get to save a copy in the method call.
+      $data = $text_headers . "\r\n" . $body;
 
-    // unset old vars to save data and so we can pass into SMTP_CONN->data by reference.
-    unset($text_headers, $body);
-   
+      // unset old vars to save data and so we can pass into SMTP_CONN->data by reference.
+      unset($text_headers, $body);
+    }
+
     // Send the message's headers and the body as SMTP data.
-    if (PEAR::isError($result = $this->conn->data($data)))
+    if (PEAR::isError($result = $this->conn->data($data, $text_headers)))
     {
       $this->error = array('label' => 'smtperror', 'vars' => array('msg' => $result->getMessage()));
       $this->response[] .= "Failed to send data";
diff --git a/program/lib/imap.inc b/program/lib/imap.inc
index 7607a1c..ed30937 100644
--- a/program/lib/imap.inc
+++ b/program/lib/imap.inc
@@ -2418,7 +2418,7 @@
 	return false;
 }
 
-function iil_C_AppendFromFile(&$conn, $folder, $path) {
+function iil_C_AppendFromFile(&$conn, $folder, $path, $headers=null, $separator="\n\n") {
 	if (!$folder) {
 	    return false;
 	}
@@ -2438,7 +2438,12 @@
 	if (!$len) {
 		return false;
 	}
-    
+
+        if ($headers) {
+                $headers = preg_replace('/[\r\n]+$/', '', $headers);
+                $len += strlen($headers) + strlen($separator);
+        }
+
 	//send APPEND command
 	$request    = 'a APPEND "' . iil_Escape($folder) . '" (\\Seen) {' . $len . '}';
 	if (iil_PutLine($fp, $request)) {
@@ -2450,16 +2455,21 @@
 			return false;
 		}
 
-		//send file
+                // send headers with body separator
+                if ($headers) {
+			iil_PutLine($fp, $headers . $separator, false);
+                }
+
+		// send file
 		while (!feof($in_fp)) {
-			$buffer      = fgets($in_fp, 4096);
+			$buffer = fgets($in_fp, 4096);
 			iil_PutLine($fp, $buffer, false);
 		}
 		fclose($in_fp);
 
 		iil_PutLine($fp, ''); // \r\n
 
-		//read response
+		// read response
 		do {
 			$line = iil_ReadLine($fp);
 		} while (!iil_StartsWith($line, 'a ', true));
diff --git a/program/steps/mail/attachments.inc b/program/steps/mail/attachments.inc
index d0808d0..2b4a590 100644
--- a/program/steps/mail/attachments.inc
+++ b/program/steps/mail/attachments.inc
@@ -53,9 +53,11 @@
     $attachment = $RCMAIL->plugins->exec_hook('display_attachment', $attachment);
     
   if ($attachment['status']) {
-    $size = $attachment['data'] ? strlen($attachment['data']) : @filesize($attachment['path']);
+    if (empty($attachment['size']))
+      $attachment['size'] = $attachment['data'] ? strlen($attachment['data']) : @filesize($attachment['path']);
+
     header('Content-Type: ' . $attachment['mimetype']);
-    header('Content-Length: ' . $size);
+    header('Content-Length: ' . $attachment['size']);
     
     if ($attachment['data'])
       echo $attachment['data'];
@@ -80,6 +82,7 @@
   foreach ($_FILES['_attachments']['tmp_name'] as $i => $filepath) {
     $attachment = array(
       'path' => $filepath,
+      'size' => $_FILES['_attachments']['size'][$i],
       'name' => $_FILES['_attachments']['name'][$i],
       'mimetype' => rc_mime_content_type($filepath, $_FILES['_attachments']['name'][$i], $_FILES['_attachments']['type'][$i])
     );
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index dea85c2..73919d3 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -1332,13 +1332,20 @@
   
 
 /**
- * Send the given message compose object using the configured method
+ * Send the given message using the configured method
+ *
+ * @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 string $body_file  Location of file with saved message body (reference)
+ *
+ * @return boolean Send status.
  */
-function rcmail_deliver_message(&$message, $from, $mailto, &$smtp_error)
+function rcmail_deliver_message(&$message, $from, $mailto, &$smtp_error, &$body_file)
 {
   global $CONFIG, $RCMAIL;
 
-  $msg_body = $message->get();
   $headers = $message->headers();
 
   // send thru SMTP server using custom SMTP library
@@ -1357,13 +1364,35 @@
     // here too, it because txtHeaders() below use $message->_headers not only $send_headers
     unset($message->_headers['Bcc']);
 
+    $smtp_headers = $message->txtHeaders($send_headers, true);
+
+    if ($message->getParam('delay_file_io')) {
+      // use common temp dir
+      $temp_dir = $RCMAIL->config->get('temp_dir');
+      $body_file = tempnam($temp_dir, 'rcmMsg');
+      if (PEAR::isError($mime_result = $message->saveMessageBody($body_file))) {
+        raise_error(array('code' => 600, 'type' => 'php',
+            'file' => __FILE__, 'line' => __LINE__,
+            'message' => "Could not create message: ".$mime_result->getMessage()),
+            TRUE, FALSE);
+        return false;
+      }
+      $msg_body = fopen($body_file, 'r');
+    } else {
+      $msg_body = $message->get();
+    }
+
     // send message
     if (!is_object($RCMAIL->smtp))
       $RCMAIL->smtp_init(true);
      
-    $sent = $RCMAIL->smtp->send_mail($from, $a_recipients, ($foo = $message->txtHeaders($send_headers, true)), $msg_body);
+    $sent = $RCMAIL->smtp->send_mail($from, $a_recipients, $smtp_headers, $msg_body);
     $smtp_response = $RCMAIL->smtp->get_response();
     $smtp_error = $RCMAIL->smtp->get_error();
+
+    if (is_resource($msg_body)) {
+      fclose($msg_body);
+    }
 
     // log error
     if (!$sent)
@@ -1387,8 +1416,15 @@
         $headers_enc['To'] = implode(', ', $m[1]);
         }
       }
-       
-    if (ini_get('safe_mode'))
+    
+    $msg_body = $message->get();
+
+    if (PEAR::isError($msg_body))
+      raise_error(array('code' => 600, 'type' => 'php',
+            'file' => __FILE__, 'line' => __LINE__,
+            'message' => "Could not create message: ".$msg_body->getMessage()),
+            TRUE, FALSE);
+    else if (ini_get('safe_mode'))
       $sent = mail($headers_enc['To'], $headers_enc['Subject'], $msg_body, $header_str);
     else
       $sent = mail($headers_enc['To'], $headers_enc['Subject'], $msg_body, $header_str, "-f$from");
@@ -1408,7 +1444,7 @@
         !empty($smtp_response) ? join('; ', $smtp_response) : ''));
     }
   }
-  
+
   $message->_headers = array();
   $message->headers($headers);
   
@@ -1430,15 +1466,14 @@
     $recipient = array_shift($IMAP->decode_address_list($message->headers->mdn_to));
     $mailto = $recipient['mailto'];
 
-    $compose = new rcube_mail_mime($RCMAIL->config->header_delimiter());
-    $compose->setParam(array(
-      'text_encoding' => 'quoted-printable',
-      'html_encoding' => 'quoted-printable',
-      'head_encoding' => 'quoted-printable',
-      'head_charset'  => RCMAIL_CHARSET,
-      'html_charset'  => RCMAIL_CHARSET,
-      'text_charset'  => RCMAIL_CHARSET,
-    ));
+    $compose = new Mail_mime($RCMAIL->config->header_delimiter());
+
+    $compose->setParam('text_encoding', 'quoted-printable');
+    $compose->setParam('html_encoding', 'quoted-printable');
+    $compose->setParam('head_encoding', 'quoted-printable');
+    $compose->setParam('head_charset', RCMAIL_CHARSET);
+    $compose->setParam('html_charset', RCMAIL_CHARSET);
+    $compose->setParam('text_charset', RCMAIL_CHARSET);
     
     // compose headers array
     $headers = array(
@@ -1474,7 +1509,7 @@
     $compose->setTXTBody(rc_wordwrap($body, 75, "\r\n"));
     $compose->addAttachment($report, 'message/disposition-notification', 'MDNPart2.txt', false, '7bit', 'inline');
 
-    $sent = rcmail_deliver_message($compose, $identity['email'], $mailto, $smtp_error);
+    $sent = rcmail_deliver_message($compose, $identity['email'], $mailto, $smtp_error, $body_file);
 
     if ($sent)
     {
diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc
index 724feb8..13c162d 100644
--- a/program/steps/mail/sendmail.inc
+++ b/program/steps/mail/sendmail.inc
@@ -20,7 +20,6 @@
 
 */
 
-
 // remove all scripts and act as called in frame
 $OUTPUT->reset();
 $OUTPUT->framed = TRUE;
@@ -111,7 +110,7 @@
 {
   global $CONFIG;
 
-  $body = $mime_message->getHtmlBody();
+  $body = $mime_message->getHTMLBody();
 
   // remove any null-byte characters before parsing
   $body = preg_replace('/\x00/', '', $body);
@@ -134,7 +133,7 @@
 
         if (! in_array($image_name, $included_images)) {
           // add the image to the MIME message
-          if(! $mime_message->addHTMLImage($img_file, 'image/gif', '', true, $image_name))
+          if (! $mime_message->addHTMLImage($img_file, 'image/gif', '', true, $image_name))
             $OUTPUT->show_message("emoticonerror", 'error');
           array_push($included_images, $image_name);
         }
@@ -360,8 +359,26 @@
 // set line length for body wrapping
 $LINE_LENGTH = $RCMAIL->config->get('line_length', 75);
 
-// create extended PEAR::Mail_mime instance
-$MAIL_MIME = new rcube_mail_mime($RCMAIL->config->header_delimiter());
+// Since we can handle big messages with disk usage, we need more time to work
+@set_time_limit(0);
+
+// create PEAR::Mail_mime instance
+$MAIL_MIME = new Mail_mime($RCMAIL->config->header_delimiter());
+
+// Check if we have enough memory to handle the message in it
+// It's faster than using files, so we'll do this if we only can
+if (is_array($_SESSION['compose']['attachments']) && $CONFIG['smtp_server']
+  && ($mem_limit = parse_bytes(ini_get('memory_limit'))))
+{
+  $memory = function_exists('memory_get_usage') ? memory_get_usage() : 16*1024*1024; // safe value: 16MB
+
+  foreach ($_SESSION['compose']['attachments'] as $id => $attachment)
+    $memory += $attachment['size'];
+
+  // Yeah, Net_SMTP needs up to 12x more memory, 1.33 is for base64
+  if ($memory * 1.33 * 12 > $mem_limit)
+    $MAIL_MIME->setParam('delay_file_io', true);
+}
 
 // For HTML-formatted messages, construct the MIME message with both
 // the HTML part and the plain-text part
@@ -403,7 +420,8 @@
 $transfer_encoding = in_array(strtoupper($message_charset), $charset_7bit) ? '7bit' : '8bit';
 
 // add stored attachments, if any
-if (is_array($_SESSION['compose']['attachments'])) {
+if (is_array($_SESSION['compose']['attachments']))
+{
   foreach ($_SESSION['compose']['attachments'] as $id => $attachment) {
     // This hook retrieves the attachment contents from the file storage backend
     $attachment = $RCMAIL->plugins->exec_hook('get_attachment', $attachment);
@@ -438,30 +456,13 @@
   }
 }
 
-// add submitted attachments
-if (is_array($_FILES['_attachments']['tmp_name'])) {
-  foreach ($_FILES['_attachments']['tmp_name'] as $i => $filepath) {
-    $ctype = $files['type'][$i];
-    $ctype = str_replace('image/pjpeg', 'image/jpeg', $ctype); // #1484914
-    
-    $MAIL_MIME->addAttachment($filepath, $ctype, $files['name'][$i], true,
-      $ctype == 'message/rfc822' ? $transfer_encoding : 'base64',
-      'attachment', $message_charset, '', '', 
-      $CONFIG['mime_param_folding'] ? 'quoted-printable' : NULL,
-      $CONFIG['mime_param_folding'] == 2 ? 'quoted-printable' : NULL
-    );
-  }
-}
-
 // encoding settings for mail composing
-$MAIL_MIME->setParam(array(
-  'text_encoding' => $transfer_encoding,
-  'html_encoding' => 'quoted-printable',
-  'head_encoding' => 'quoted-printable',
-  'head_charset'  => $message_charset,
-  'html_charset'  => $message_charset,
-  'text_charset'  => $message_charset,
-));
+$MAIL_MIME->setParam('text_encoding', $transfer_encoding);
+$MAIL_MIME->setParam('html_encoding', 'quoted-printable');
+$MAIL_MIME->setParam('head_encoding', 'quoted-printable');
+$MAIL_MIME->setParam('head_charset', $message_charset);
+$MAIL_MIME->setParam('html_charset', $message_charset);
+$MAIL_MIME->setParam('text_charset', $message_charset);
 
 $data = $RCMAIL->plugins->exec_hook('outgoing_message_headers', array('headers' => $headers));
 $headers = $data['headers'];
@@ -487,11 +488,16 @@
     $OUTPUT->send('iframe'); 
   }
 
-  $sent = rcmail_deliver_message($MAIL_MIME, $from, $mailto, $smtp_error);
-  
+  $sent = rcmail_deliver_message($MAIL_MIME, $from, $mailto, $smtp_error, $mailbody_file);
+
   // return to compose page if sending failed
   if (!$sent)
     {
+    // remove temp file
+    if ($mailbody_file) {
+      unlink($mailbody_file);
+      }
+
     if ($smtp_error)
       $OUTPUT->show_message($smtp_error['label'], 'error', $smtp_error['vars']); 
     else
@@ -512,7 +518,6 @@
 } // End of SMTP Delivery Block
 
 
-
 // Determine which folder to save message
 if ($savedraft)
   $store_target = $CONFIG['drafts_mbox'];
@@ -522,31 +527,63 @@
 if ($store_target)
   {
   // check if mailbox exists
-  if (!in_array_nocase($store_target, $IMAP->list_mailboxes()))
+  if (!in_array($store_target, $IMAP->list_mailboxes()))
     {
       // folder may be existing but not subscribed (#1485241)
-      if (!in_array_nocase($store_target, $IMAP->list_unsubscribed()))
+      if (!in_array($store_target, $IMAP->list_unsubscribed()))
         $store_folder = $IMAP->create_mailbox($store_target, TRUE);
       else if ($IMAP->subscribe($store_target))
         $store_folder = TRUE;
     }
   else
     $store_folder = TRUE;
-  
-  // append message to sent box
-  if ($store_folder)
-    $saved = $IMAP->save_message($store_target, $MAIL_MIME->getMessage());
 
-  // raise error if saving failed
-  if (!$saved)
-    {
-    raise_error(array('code' => 800, 'type' => 'imap',
+  // append message to sent box
+  if ($store_folder) {
+
+    // message body in file
+    if ($mailbody_file || $MAIL_MIME->getParam('delay_file_io')) {
+      $headers = $MAIL_MIME->txtHeaders();
+      
+      // file already created
+      if ($mailbody_file)
+        $msg = $mailbody_file;
+      else {
+        $temp_dir = $RCMAIL->config->get('temp_dir');
+        $mailbody_file = tempnam($temp_dir, 'rcmMsg');
+        if (!PEAR::isError($msg = $MAIL_MIME->saveMessageBody($mailbody_file)))
+          $msg = $mailbody_file;
+        }
+      }
+    else {
+      $msg = $MAIL_MIME->getMessage();
+      $headers = '';
+      }
+
+    if (PEAR::isError($msg))
+      raise_error(array('code' => 600, 'type' => 'php',
+	    'file' => __FILE__, 'line' => __LINE__,
+            'message' => "Could not create message: ".$msg->getMessage()),
+            TRUE, FALSE);
+    else {
+      $saved = $IMAP->save_message($store_target, $msg, $headers, $mailbody_file ? true : false);
+      }
+
+    if ($mailbody_file) {
+      unlink($mailbody_file);
+      $mailbody_file = null;
+      }
+
+    // raise error if saving failed
+    if (!$saved) {
+      raise_error(array('code' => 800, 'type' => 'imap',
 	    'file' => __FILE__, 'line' => __LINE__,
             'message' => "Could not save message in $store_target"), TRUE, FALSE);
     
-    if ($savedraft) {
-      $OUTPUT->show_message('errorsaving', 'error');
-      $OUTPUT->send('iframe');
+      if ($savedraft) {
+        $OUTPUT->show_message('errorsaving', 'error');
+        $OUTPUT->send('iframe');
+        }
       }
     }
 
@@ -564,6 +601,11 @@
                 'message' => "Could not delete message from ".$CONFIG['drafts_mbox']), TRUE, FALSE);
     }
   }
+// remove temp file
+else if ($mailbody_file) {
+  unlink($mailbody_file);
+  }
+
 
 if ($savedraft)
   {

--
Gitblit v1.9.1