You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
roundcubemail/program/steps/mail/bounce.inc

251 lines
7.9 KiB
PHP

<?php
/**
+-----------------------------------------------------------------------+
| program/steps/mail/bounce.inc |
| |
| This file is part of the Roundcube Webmail client |
| Copyright (C) 2005-2017, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
| PURPOSE: |
| Bounce/resend an email message |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
| Author: Aleksander Machniak <alec@alec.pl> |
+-----------------------------------------------------------------------+
*/
/**
* Mail_mime wrapper to handle mail bounce
*
* @FIXME: For some reason this class definition
* cannot be put at the end of the file
*/
class rcmail_bounce_mail extends Mail_mime
{
/**
* Constructor function
*/
public function __construct($params = array())
{
// To make the code simpler always use delay_file_io=true
$params['delay_file_io'] = true;
$params['eol'] = "\r\n";
parent::__construct($params);
}
/**
* Returns/Sets message headers
*/
public function headers($headers = array(), $overwrite = false, $skip_content = false)
{
// headers() wrapper that returns Resent-Cc, Resent-Bcc instead of Cc,Bcc
// it's also called to re-add Resent-Bcc after it has been sent (to store in Sent)
if (array_key_exists('Bcc', $headers)) {
$this->build_params['bounce_headers']['Resent-Bcc'] = $headers['Bcc'];
}
foreach ($this->build_params['bounce_headers'] as $key => $val) {
$headers[str_replace('Resent-', '', $key)] = $val;
}
return $headers;
}
/**
* Returns all message headers as string
*/
public function txtHeaders($headers = array(), $overwrite = false, $skip_content = false)
{
// i.e. add Resent-* headers on top of the original message head
$this->init_message();
$result = array();
foreach ($this->build_params['bounce_headers'] as $name => $value) {
$key = str_replace('Resent-', '', $name);
// txtHeaders() can be used to unset Bcc header
if (array_key_exists($key, $headers)) {
$value = $headers[$key];
$this->build_params['bounce_headers']['Resent-'.$key] = $value;
}
if ($value) {
$result[] = "$name: $value";
}
}
$result = implode($this->build_params['eol'], $result);
if (strlen($this->bounce_head)) {
$result .= $this->build_params['eol'] . $this->bounce_head;
}
return $result;
}
/**
* Save the message body to a file (if delay_file_io=true)
*/
public function saveMessageBody($file, $params = null)
{
$this->init_message();
// this will be called only once, so let just move the file
rename($this->bounce_body, $file);
$this->bounce_head = null;
}
protected function init_message()
{
if ($this->bounce_head !== null) {
return;
}
$rcmail = rcmail::get_instance();
$storage = $rcmail->get_storage();
$message = $this->build_params['bounce_message'];
$temp_dir = unslashify($rcmail->config->get('temp_dir'));
$path = tempnam($temp_dir, 'rcmBounce');
// We'll write the body to the file and the headers to a variable
if ($fp = fopen($path, 'w')) {
stream_filter_register('bounce_source', 'rcmail_bounce_stream_filter');
stream_filter_append($fp, 'bounce_source');
// message part
if ($message->context) {
$message->get_part_body($message->context, false, 0, $fp);
}
// complete message
else {
$storage->set_folder($message->folder);
$storage->get_raw_body($message->uid, $fp);
}
fclose($fp);
$this->bounce_head = rcmail_bounce_stream_filter::$headers;
$this->bounce_body = $path;
}
}
}
/**
* Stream filter to remove message headers from the streamed
* message source (and store them in a variable)
*/
class rcmail_bounce_stream_filter extends php_user_filter
{
public static $headers;
protected $in_body = false;
public function onCreate()
{
self::$headers = '';
}
public function filter($in, $out, &$consumed, $closing)
{
while ($bucket = stream_bucket_make_writeable($in)) {
if (!$this->in_body) {
self::$headers .= $bucket->data;
if (($pos = strpos(self::$headers, "\r\n\r\n")) === false) {
continue;
}
$bucket->data = substr(self::$headers, $pos + 4);
$bucket->datalen = strlen($bucket->data);
self::$headers = substr(self::$headers, 0, $pos);
$this->in_body = true;
}
$consumed += $bucket->datalen;
stream_bucket_append($out, $bucket);
}
return PSFS_PASS_ON;
}
}
$msg_uid = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_GP);
$msg_folder = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_GP, true);
$MESSAGE = new rcube_message($msg_uid, $msg_folder);
if (!$MESSAGE->headers) {
$OUTPUT->show_message('messageopenerror', 'error');
$OUTPUT->send('iframe');
}
// Display Bounce form
if (empty($_POST)) {
if (!empty($MESSAGE->headers->charset)) {
$RCMAIL->storage->set_charset($MESSAGE->headers->charset);
}
// Initialize helper class to build the UI
$SENDMAIL = new rcmail_sendmail(
array('mode' => rcmail_sendmail::MODE_FORWARD),
array('message' => $MESSAGE)
);
$OUTPUT->set_env('mailbox', $msg_folder);
$OUTPUT->set_env('uid', $msg_uid);
$OUTPUT->send('bounce');
}
// Initialize helper class to send the message
$SENDMAIL = new rcmail_sendmail(array('mode' => rcmail_sendmail::MODE_FORWARD), array(
'sendmail' => true,
'error_handler' => function() use ($OUTPUT) {
call_user_func_array(array($OUTPUT, 'show_message'), func_get_args());
$OUTPUT->send('iframe');
}
));
// Handle the form input
$input_headers = $SENDMAIL->headers_input();
// Set Resent-* headers, these will be added on top of the bounced message
$headers = array_filter(array(
// 'Received' => $input_headers['Received'],
'Resent-From' => $input_headers['From'],
'Resent-To' => $input_headers['To'],
'Resent-Cc' => $input_headers['Cc'],
'Resent-Bcc' => $input_headers['Bcc'],
'Resent-Date' => $input_headers['Date'],
'Resent-Message-ID' => $input_headers['Message-ID'],
));
// Create the bounce message
$BOUNCE = new rcmail_bounce_mail(array(
'bounce_message' => $MESSAGE,
'bounce_headers' => $headers,
));
// Send the bounce message
$SENDMAIL->deliver_message($BOUNCE);
// Save in Sent (if requested)
$saved = $SENDMAIL->save_message($BOUNCE);
if (!$saved && strlen($SENDMAIL->options['store_target'])) {
$RCMAIL->display_server_error('errorsaving');
}
$OUTPUT->show_message('messagesent', 'confirmation', null, false);
$OUTPUT->send('iframe');