Changeset 3261 in subversion


Ignore:
Timestamp:
Feb 9, 2010 8:10:12 AM (3 years ago)
Author:
alec
Message:
  • Fix attachment excessive memory use, support messages of any size (#1484660)
Location:
trunk/roundcubemail
Files:
1 deleted
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/roundcubemail/CHANGELOG

    r3258 r3261  
    22=========================== 
    33 
     4- Fix attachment excessive memory use, support messages of any size (#1484660) 
    45- Fix setting task name according to auth state 
    56- Password: fix vpopmaild driver (#1486478) 
  • trunk/roundcubemail/INSTALL

    r3243 r3261  
    1616   - libiconv (recommended) 
    1717   - mbstring, fileinfo, mcrypt (optional) 
     18* PEAR packages distributed with Roundcube or external: 
     19   - MDB2 2.5.0 or newer 
     20   - Mail_Mime 1.6.0 or newer 
     21   - Net_SMTP 1.4.1 or newer 
    1822* php.ini options (see .htaccess file): 
    1923   - error_reporting E_ALL & ~E_NOTICE (or lower) 
  • trunk/roundcubemail/program/include/rcube_imap.php

    r3251 r3261  
    17001700   * Append a mail message (source) to a specific mailbox 
    17011701   * 
    1702    * @param string Target mailbox 
    1703    * @param string Message source 
     1702   * @param string   Target mailbox 
     1703   * @param string   The message source string or filename 
     1704   * @param string   Headers string if $message contains only the body 
     1705   * @param boolean  True if $message is a filename 
     1706   * 
    17041707   * @return boolean True on success, False on error 
    17051708   */ 
    1706   function save_message($mbox_name, &$message) 
     1709  function save_message($mbox_name, &$message, $headers='', $is_file=false) 
    17071710    { 
    17081711    $mailbox = $this->mod_mailbox($mbox_name); 
    17091712 
    17101713    // make sure mailbox exists 
    1711     if (($mailbox == 'INBOX') || in_array($mailbox, $this->_list_mailboxes())) 
    1712       $saved = iil_C_Append($this->conn, $mailbox, $message); 
     1714    if (($mailbox == 'INBOX') || in_array($mailbox, $this->_list_mailboxes())) { 
     1715      if ($is_file) { 
     1716        $separator = rcmail::get_instance()->config->header_delimiter(); 
     1717        $saved = iil_C_AppendFromFile($this->conn, $mailbox, $message, 
     1718          $headers, $separator.$separator); 
     1719        } 
     1720      else 
     1721        $saved = iil_C_Append($this->conn, $mailbox, $message); 
     1722      } 
    17131723 
    17141724    if ($saved) 
  • trunk/roundcubemail/program/include/rcube_smtp.php

    r3212 r3261  
    142142   *               formatted string 
    143143   * 
    144    * @param string The full text of the message body, including any Mime parts, etc. 
     144   * @param mixed  The full text of the message body, including any Mime parts 
     145   *               or file handle 
    145146   * 
    146147   * @return bool  Returns true on success, or false on error 
     
    207208    } 
    208209 
    209     // Concatenate headers and body so it can be passed by reference to SMTP_CONN->data 
    210     // so preg_replace in SMTP_CONN->quotedata will store a reference instead of a copy.  
    211     // We are still forced to make another copy here for a couple ticks so we don't really  
    212     // get to save a copy in the method call. 
    213     $data = $text_headers . "\r\n" . $body; 
    214  
    215     // unset old vars to save data and so we can pass into SMTP_CONN->data by reference. 
    216     unset($text_headers, $body); 
    217     
     210    if (is_resource($body)) 
     211    { 
     212      // file handle 
     213      $data = $body; 
     214      $text_headers = preg_replace('/[\r\n]+$/', '', $text_headers); 
     215    } else { 
     216      // Concatenate headers and body so it can be passed by reference to SMTP_CONN->data 
     217      // so preg_replace in SMTP_CONN->quotedata will store a reference instead of a copy.  
     218      // We are still forced to make another copy here for a couple ticks so we don't really  
     219      // get to save a copy in the method call. 
     220      $data = $text_headers . "\r\n" . $body; 
     221 
     222      // unset old vars to save data and so we can pass into SMTP_CONN->data by reference. 
     223      unset($text_headers, $body); 
     224    } 
     225 
    218226    // Send the message's headers and the body as SMTP data. 
    219     if (PEAR::isError($result = $this->conn->data($data))) 
     227    if (PEAR::isError($result = $this->conn->data($data, $text_headers))) 
    220228    { 
    221229      $this->error = array('label' => 'smtperror', 'vars' => array('msg' => $result->getMessage())); 
  • trunk/roundcubemail/program/lib/imap.inc

    r3247 r3261  
    24192419} 
    24202420 
    2421 function iil_C_AppendFromFile(&$conn, $folder, $path) { 
     2421function iil_C_AppendFromFile(&$conn, $folder, $path, $headers=null, $separator="\n\n") { 
    24222422        if (!$folder) { 
    24232423            return false; 
     
    24392439                return false; 
    24402440        } 
    2441      
     2441 
     2442        if ($headers) { 
     2443                $headers = preg_replace('/[\r\n]+$/', '', $headers); 
     2444                $len += strlen($headers) + strlen($separator); 
     2445        } 
     2446 
    24422447        //send APPEND command 
    24432448        $request    = 'a APPEND "' . iil_Escape($folder) . '" (\\Seen) {' . $len . '}'; 
     
    24512456                } 
    24522457 
    2453                 //send file 
     2458                // send headers with body separator 
     2459                if ($headers) { 
     2460                        iil_PutLine($fp, $headers . $separator, false); 
     2461                } 
     2462 
     2463                // send file 
    24542464                while (!feof($in_fp)) { 
    2455                         $buffer      = fgets($in_fp, 4096); 
     2465                        $buffer = fgets($in_fp, 4096); 
    24562466                        iil_PutLine($fp, $buffer, false); 
    24572467                } 
     
    24602470                iil_PutLine($fp, ''); // \r\n 
    24612471 
    2462                 //read response 
     2472                // read response 
    24632473                do { 
    24642474                        $line = iil_ReadLine($fp); 
  • trunk/roundcubemail/program/steps/mail/attachments.inc

    r3223 r3261  
    5454     
    5555  if ($attachment['status']) { 
    56     $size = $attachment['data'] ? strlen($attachment['data']) : @filesize($attachment['path']); 
     56    if (empty($attachment['size'])) 
     57      $attachment['size'] = $attachment['data'] ? strlen($attachment['data']) : @filesize($attachment['path']); 
     58 
    5759    header('Content-Type: ' . $attachment['mimetype']); 
    58     header('Content-Length: ' . $size); 
     60    header('Content-Length: ' . $attachment['size']); 
    5961     
    6062    if ($attachment['data']) 
     
    8183    $attachment = array( 
    8284      'path' => $filepath, 
     85      'size' => $_FILES['_attachments']['size'][$i], 
    8386      'name' => $_FILES['_attachments']['name'][$i], 
    8487      'mimetype' => rc_mime_content_type($filepath, $_FILES['_attachments']['name'][$i], $_FILES['_attachments']['type'][$i]) 
  • trunk/roundcubemail/program/steps/mail/func.inc

    r3258 r3261  
    13331333 
    13341334/** 
    1335  * Send the given message compose object using the configured method 
    1336  */ 
    1337 function rcmail_deliver_message(&$message, $from, $mailto, &$smtp_error) 
     1335 * Send the given message using the configured method 
     1336 * 
     1337 * @param object $message    Reference to Mail_MIME object 
     1338 * @param string $from       Sender address string 
     1339 * @param array  $mailto     Array of recipient address strings 
     1340 * @param array  $smtp_error SMTP error array (reference) 
     1341 * @param string $body_file  Location of file with saved message body (reference) 
     1342 * 
     1343 * @return boolean Send status. 
     1344 */ 
     1345function rcmail_deliver_message(&$message, $from, $mailto, &$smtp_error, &$body_file) 
    13381346{ 
    13391347  global $CONFIG, $RCMAIL; 
    13401348 
    1341   $msg_body = $message->get(); 
    13421349  $headers = $message->headers(); 
    13431350 
     
    13581365    unset($message->_headers['Bcc']); 
    13591366 
     1367    $smtp_headers = $message->txtHeaders($send_headers, true); 
     1368 
     1369    if ($message->getParam('delay_file_io')) { 
     1370      // use common temp dir 
     1371      $temp_dir = $RCMAIL->config->get('temp_dir'); 
     1372      $body_file = tempnam($temp_dir, 'rcmMsg'); 
     1373      if (PEAR::isError($mime_result = $message->saveMessageBody($body_file))) { 
     1374        raise_error(array('code' => 600, 'type' => 'php', 
     1375            'file' => __FILE__, 'line' => __LINE__, 
     1376            'message' => "Could not create message: ".$mime_result->getMessage()), 
     1377            TRUE, FALSE); 
     1378        return false; 
     1379      } 
     1380      $msg_body = fopen($body_file, 'r'); 
     1381    } else { 
     1382      $msg_body = $message->get(); 
     1383    } 
     1384 
    13601385    // send message 
    13611386    if (!is_object($RCMAIL->smtp)) 
    13621387      $RCMAIL->smtp_init(true); 
    13631388      
    1364     $sent = $RCMAIL->smtp->send_mail($from, $a_recipients, ($foo = $message->txtHeaders($send_headers, true)), $msg_body); 
     1389    $sent = $RCMAIL->smtp->send_mail($from, $a_recipients, $smtp_headers, $msg_body); 
    13651390    $smtp_response = $RCMAIL->smtp->get_response(); 
    13661391    $smtp_error = $RCMAIL->smtp->get_error(); 
     1392 
     1393    if (is_resource($msg_body)) { 
     1394      fclose($msg_body); 
     1395    } 
    13671396 
    13681397    // log error 
     
    13881417        } 
    13891418      } 
    1390         
    1391     if (ini_get('safe_mode')) 
     1419     
     1420    $msg_body = $message->get(); 
     1421 
     1422    if (PEAR::isError($msg_body)) 
     1423      raise_error(array('code' => 600, 'type' => 'php', 
     1424            'file' => __FILE__, 'line' => __LINE__, 
     1425            'message' => "Could not create message: ".$msg_body->getMessage()), 
     1426            TRUE, FALSE); 
     1427    else if (ini_get('safe_mode')) 
    13921428      $sent = mail($headers_enc['To'], $headers_enc['Subject'], $msg_body, $header_str); 
    13931429    else 
     
    14091445    } 
    14101446  } 
    1411    
     1447 
    14121448  $message->_headers = array(); 
    14131449  $message->headers($headers); 
     
    14311467    $mailto = $recipient['mailto']; 
    14321468 
    1433     $compose = new rcube_mail_mime($RCMAIL->config->header_delimiter()); 
    1434     $compose->setParam(array( 
    1435       'text_encoding' => 'quoted-printable', 
    1436       'html_encoding' => 'quoted-printable', 
    1437       'head_encoding' => 'quoted-printable', 
    1438       'head_charset'  => RCMAIL_CHARSET, 
    1439       'html_charset'  => RCMAIL_CHARSET, 
    1440       'text_charset'  => RCMAIL_CHARSET, 
    1441     )); 
     1469    $compose = new Mail_mime($RCMAIL->config->header_delimiter()); 
     1470 
     1471    $compose->setParam('text_encoding', 'quoted-printable'); 
     1472    $compose->setParam('html_encoding', 'quoted-printable'); 
     1473    $compose->setParam('head_encoding', 'quoted-printable'); 
     1474    $compose->setParam('head_charset', RCMAIL_CHARSET); 
     1475    $compose->setParam('html_charset', RCMAIL_CHARSET); 
     1476    $compose->setParam('text_charset', RCMAIL_CHARSET); 
    14421477     
    14431478    // compose headers array 
     
    14751510    $compose->addAttachment($report, 'message/disposition-notification', 'MDNPart2.txt', false, '7bit', 'inline'); 
    14761511 
    1477     $sent = rcmail_deliver_message($compose, $identity['email'], $mailto, $smtp_error); 
     1512    $sent = rcmail_deliver_message($compose, $identity['email'], $mailto, $smtp_error, $body_file); 
    14781513 
    14791514    if ($sent) 
  • trunk/roundcubemail/program/steps/mail/sendmail.inc

    r3250 r3261  
    2121*/ 
    2222 
    23  
    2423// remove all scripts and act as called in frame 
    2524$OUTPUT->reset(); 
     
    112111  global $CONFIG; 
    113112 
    114   $body = $mime_message->getHtmlBody(); 
     113  $body = $mime_message->getHTMLBody(); 
    115114 
    116115  // remove any null-byte characters before parsing 
     
    135134        if (! in_array($image_name, $included_images)) { 
    136135          // add the image to the MIME message 
    137           if(! $mime_message->addHTMLImage($img_file, 'image/gif', '', true, $image_name)) 
     136          if (! $mime_message->addHTMLImage($img_file, 'image/gif', '', true, $image_name)) 
    138137            $OUTPUT->show_message("emoticonerror", 'error'); 
    139138          array_push($included_images, $image_name); 
     
    361360$LINE_LENGTH = $RCMAIL->config->get('line_length', 75); 
    362361 
    363 // create extended PEAR::Mail_mime instance 
    364 $MAIL_MIME = new rcube_mail_mime($RCMAIL->config->header_delimiter()); 
     362// Since we can handle big messages with disk usage, we need more time to work 
     363@set_time_limit(0); 
     364 
     365// create PEAR::Mail_mime instance 
     366$MAIL_MIME = new Mail_mime($RCMAIL->config->header_delimiter()); 
     367 
     368// Check if we have enough memory to handle the message in it 
     369// It's faster than using files, so we'll do this if we only can 
     370if (is_array($_SESSION['compose']['attachments']) && $CONFIG['smtp_server'] 
     371  && ($mem_limit = parse_bytes(ini_get('memory_limit')))) 
     372{ 
     373  $memory = function_exists('memory_get_usage') ? memory_get_usage() : 16*1024*1024; // safe value: 16MB 
     374 
     375  foreach ($_SESSION['compose']['attachments'] as $id => $attachment) 
     376    $memory += $attachment['size']; 
     377 
     378  // Yeah, Net_SMTP needs up to 12x more memory, 1.33 is for base64 
     379  if ($memory * 1.33 * 12 > $mem_limit) 
     380    $MAIL_MIME->setParam('delay_file_io', true); 
     381} 
    365382 
    366383// For HTML-formatted messages, construct the MIME message with both 
     
    404421 
    405422// add stored attachments, if any 
    406 if (is_array($_SESSION['compose']['attachments'])) { 
     423if (is_array($_SESSION['compose']['attachments'])) 
     424{ 
    407425  foreach ($_SESSION['compose']['attachments'] as $id => $attachment) { 
    408426    // This hook retrieves the attachment contents from the file storage backend 
     
    439457} 
    440458 
    441 // add submitted attachments 
    442 if (is_array($_FILES['_attachments']['tmp_name'])) { 
    443   foreach ($_FILES['_attachments']['tmp_name'] as $i => $filepath) { 
    444     $ctype = $files['type'][$i]; 
    445     $ctype = str_replace('image/pjpeg', 'image/jpeg', $ctype); // #1484914 
    446      
    447     $MAIL_MIME->addAttachment($filepath, $ctype, $files['name'][$i], true, 
    448       $ctype == 'message/rfc822' ? $transfer_encoding : 'base64', 
    449       'attachment', $message_charset, '', '',  
    450       $CONFIG['mime_param_folding'] ? 'quoted-printable' : NULL, 
    451       $CONFIG['mime_param_folding'] == 2 ? 'quoted-printable' : NULL 
    452     ); 
    453   } 
    454 } 
    455  
    456459// encoding settings for mail composing 
    457 $MAIL_MIME->setParam(array( 
    458   'text_encoding' => $transfer_encoding, 
    459   'html_encoding' => 'quoted-printable', 
    460   'head_encoding' => 'quoted-printable', 
    461   'head_charset'  => $message_charset, 
    462   'html_charset'  => $message_charset, 
    463   'text_charset'  => $message_charset, 
    464 )); 
     460$MAIL_MIME->setParam('text_encoding', $transfer_encoding); 
     461$MAIL_MIME->setParam('html_encoding', 'quoted-printable'); 
     462$MAIL_MIME->setParam('head_encoding', 'quoted-printable'); 
     463$MAIL_MIME->setParam('head_charset', $message_charset); 
     464$MAIL_MIME->setParam('html_charset', $message_charset); 
     465$MAIL_MIME->setParam('text_charset', $message_charset); 
    465466 
    466467$data = $RCMAIL->plugins->exec_hook('outgoing_message_headers', array('headers' => $headers)); 
     
    488489  } 
    489490 
    490   $sent = rcmail_deliver_message($MAIL_MIME, $from, $mailto, $smtp_error); 
    491    
     491  $sent = rcmail_deliver_message($MAIL_MIME, $from, $mailto, $smtp_error, $mailbody_file); 
     492 
    492493  // return to compose page if sending failed 
    493494  if (!$sent) 
    494495    { 
     496    // remove temp file 
     497    if ($mailbody_file) { 
     498      unlink($mailbody_file); 
     499      } 
     500 
    495501    if ($smtp_error) 
    496502      $OUTPUT->show_message($smtp_error['label'], 'error', $smtp_error['vars']);  
     
    513519 
    514520 
    515  
    516521// Determine which folder to save message 
    517522if ($savedraft) 
     
    523528  { 
    524529  // check if mailbox exists 
    525   if (!in_array_nocase($store_target, $IMAP->list_mailboxes())) 
     530  if (!in_array($store_target, $IMAP->list_mailboxes())) 
    526531    { 
    527532      // folder may be existing but not subscribed (#1485241) 
    528       if (!in_array_nocase($store_target, $IMAP->list_unsubscribed())) 
     533      if (!in_array($store_target, $IMAP->list_unsubscribed())) 
    529534        $store_folder = $IMAP->create_mailbox($store_target, TRUE); 
    530535      else if ($IMAP->subscribe($store_target)) 
     
    533538  else 
    534539    $store_folder = TRUE; 
    535    
     540 
    536541  // append message to sent box 
    537   if ($store_folder) 
    538     $saved = $IMAP->save_message($store_target, $MAIL_MIME->getMessage()); 
    539  
    540   // raise error if saving failed 
    541   if (!$saved) 
    542     { 
    543     raise_error(array('code' => 800, 'type' => 'imap', 
     542  if ($store_folder) { 
     543 
     544    // message body in file 
     545    if ($mailbody_file || $MAIL_MIME->getParam('delay_file_io')) { 
     546      $headers = $MAIL_MIME->txtHeaders(); 
     547       
     548      // file already created 
     549      if ($mailbody_file) 
     550        $msg = $mailbody_file; 
     551      else { 
     552        $temp_dir = $RCMAIL->config->get('temp_dir'); 
     553        $mailbody_file = tempnam($temp_dir, 'rcmMsg'); 
     554        if (!PEAR::isError($msg = $MAIL_MIME->saveMessageBody($mailbody_file))) 
     555          $msg = $mailbody_file; 
     556        } 
     557      } 
     558    else { 
     559      $msg = $MAIL_MIME->getMessage(); 
     560      $headers = ''; 
     561      } 
     562 
     563    if (PEAR::isError($msg)) 
     564      raise_error(array('code' => 600, 'type' => 'php', 
     565            'file' => __FILE__, 'line' => __LINE__, 
     566            'message' => "Could not create message: ".$msg->getMessage()), 
     567            TRUE, FALSE); 
     568    else { 
     569      $saved = $IMAP->save_message($store_target, $msg, $headers, $mailbody_file ? true : false); 
     570      } 
     571 
     572    if ($mailbody_file) { 
     573      unlink($mailbody_file); 
     574      $mailbody_file = null; 
     575      } 
     576 
     577    // raise error if saving failed 
     578    if (!$saved) { 
     579      raise_error(array('code' => 800, 'type' => 'imap', 
    544580            'file' => __FILE__, 'line' => __LINE__, 
    545581            'message' => "Could not save message in $store_target"), TRUE, FALSE); 
    546582     
    547     if ($savedraft) { 
    548       $OUTPUT->show_message('errorsaving', 'error'); 
    549       $OUTPUT->send('iframe'); 
     583      if ($savedraft) { 
     584        $OUTPUT->show_message('errorsaving', 'error'); 
     585        $OUTPUT->send('iframe'); 
     586        } 
    550587      } 
    551588    } 
     
    565602    } 
    566603  } 
     604// remove temp file 
     605else if ($mailbody_file) { 
     606  unlink($mailbody_file); 
     607  } 
     608 
    567609 
    568610if ($savedraft) 
Note: See TracChangeset for help on using the changeset viewer.