From 7e3298753a9f93405ef44b46ba4db4ca98553b51 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak <alec@alec.pl> Date: Sat, 14 Nov 2015 04:08:07 -0500 Subject: [PATCH] Use ternary operator where aplicable --- program/lib/Roundcube/rcube.php | 501 +++++++++++++++++++++++++++++-------------------------- 1 files changed, 262 insertions(+), 239 deletions(-) diff --git a/program/lib/Roundcube/rcube.php b/program/lib/Roundcube/rcube.php index e3e26d8..3b17d99 100644 --- a/program/lib/Roundcube/rcube.php +++ b/program/lib/Roundcube/rcube.php @@ -1,6 +1,6 @@ <?php -/* +/** +-----------------------------------------------------------------------+ | This file is part of the Roundcube Webmail client | | Copyright (C) 2008-2014, The Roundcube Dev Team | @@ -18,7 +18,6 @@ +-----------------------------------------------------------------------+ */ - /** * Base class of the Roundcube Framework * implemented as singleton @@ -28,8 +27,16 @@ */ class rcube { - const INIT_WITH_DB = 1; + // Init options + const INIT_WITH_DB = 1; const INIT_WITH_PLUGINS = 2; + + // Request status + const REQUEST_VALID = 0; + const REQUEST_ERROR_URL = 1; + const REQUEST_ERROR_TOKEN = 2; + + const DEBUG_LINE_LENGTH = 4096; /** * Singleton instace of rcube @@ -101,6 +108,12 @@ */ public $user; + /** + * Request status + * + * @var int + */ + public $request_status = 0; /* private/protected vars */ protected $texts; @@ -111,8 +124,8 @@ /** * This implements the 'singleton' design pattern * - * @param integer Options to initialize with this instance. See rcube::INIT_WITH_* constants - * @param string Environment name to run (e.g. live, dev, test) + * @param integer $mode Options to initialize with this instance. See rcube::INIT_WITH_* constants + * @param string $env Environment name to run (e.g. live, dev, test) * * @return rcube The one and only instance */ @@ -126,7 +139,6 @@ return self::$instance; } - /** * Private constructor */ @@ -138,7 +150,6 @@ register_shutdown_function(array($this, 'shutdown')); } - /** * Initial startup function @@ -163,7 +174,6 @@ } } - /** * Get the current database connection * @@ -184,7 +194,6 @@ return $this->db; } - /** * Get global handle for memcache access * @@ -203,7 +212,10 @@ $this->mc_available = 0; // add all configured hosts to pool - $pconnect = $this->config->get('memcache_pconnect', true); + $pconnect = $this->config->get('memcache_pconnect', true); + $timeout = $this->config->get('memcache_timeout', 1); + $retry_interval = $this->config->get('memcache_retry_interval', 15); + foreach ($this->config->get('memcache_hosts', array()) as $host) { if (substr($host, 0, 7) != 'unix://') { list($host, $port) = explode(':', $host); @@ -214,7 +226,7 @@ } $this->mc_available += intval($this->memcache->addServer( - $host, $port, $pconnect, 1, 1, 15, false, array($this, 'memcache_failure'))); + $host, $port, $pconnect, 1, $timeout, $retry_interval, false, array($this, 'memcache_failure'))); } // test connection and failover (will result in $this->mc_available == 0 on complete failure) @@ -227,7 +239,6 @@ return $this->memcache; } - /** * Callback for memcache failure @@ -247,7 +258,6 @@ } } - /** * Initialize and get cache object * @@ -266,7 +276,6 @@ return $this->caches[$name]; } - /** * Initialize and get shared cache object @@ -300,11 +309,10 @@ return $this->caches[$shared_name]; } - /** * Create SMTP object and connect to server * - * @param boolean True if connection should be established + * @param boolean $connect True if connection should be established */ public function smtp_init($connect = false) { @@ -314,7 +322,6 @@ $this->smtp->connect(); } } - /** * Initialize and get storage object @@ -330,7 +337,6 @@ return $this->storage; } - /** * Initialize storage object @@ -389,8 +395,12 @@ $this->storage->set_options($options); $this->set_storage_prop(); - } + // subscribe to 'storage_connected' hook for session logging + if ($this->config->get('imap_log_session', false)) { + $this->plugins->register_hook('storage_connected', array($this, 'storage_log_session')); + } + } /** * Set storage parameters. @@ -426,7 +436,6 @@ } } - /** * Set special folders type association. * This must be done AFTER connecting to the server! @@ -456,6 +465,15 @@ } } + /** + * Callback for IMAP connection events to log session identifiers + */ + public function storage_log_session($args) + { + if (!empty($args['session']) && session_id()) { + $this->write_log('imap_session', $args['session']); + } + } /** * Create session object and start the session. @@ -487,28 +505,20 @@ } ini_set('session.cookie_secure', $is_secure); - ini_set('session.name', $sess_name ? $sess_name : 'roundcube_sessid'); + ini_set('session.name', $sess_name ?: 'roundcube_sessid'); ini_set('session.use_cookies', 1); ini_set('session.use_only_cookies', 1); ini_set('session.cookie_httponly', 1); - // use database for storing session data - $this->session = new rcube_session($this->get_dbh(), $this->config); - + // get session driver instance + $this->session = rcube_session::factory($this->config); $this->session->register_gc_handler(array($this, 'gc')); - $this->session->set_secret($this->config->get('des_key') . dirname($_SERVER['SCRIPT_NAME'])); - $this->session->set_ip_check($this->config->get('ip_check')); - - if ($this->config->get('session_auth_name')) { - $this->session->set_cookiename($this->config->get('session_auth_name')); - } // start PHP session (if not in CLI mode) if ($_SERVER['REMOTE_ADDR']) { $this->session->start(); } } - /** * Garbage collector - cache/temp cleaner @@ -521,7 +531,6 @@ $this->gc_temp(); } - /** * Garbage collector function for temp files. @@ -554,7 +563,6 @@ } } - /** * Runs garbage collector with probability based on * session settings. This is intended for environments @@ -572,7 +580,6 @@ } } } - /** * Get localized text in the desired language @@ -594,7 +601,7 @@ $attrib = array('name' => $attrib); } - $name = $attrib['name'] ? $attrib['name'] : ''; + $name = (string) $attrib['name']; // attrib contain text values: use them from now if (($setval = $attrib[strtolower($_SESSION['language'])]) || ($setval = $attrib['en_us'])) { @@ -612,7 +619,7 @@ // replace vars in text if (is_array($attrib['vars'])) { foreach ($attrib['vars'] as $var_key => $var_value) { - $text = str_replace($var_key[0]!='$' ? '$'.$var_key : $var_key, $var_value, $text); + $text = str_replace($var_key[0] != '$' ? '$'.$var_key : $var_key, $var_value, $text); } } @@ -629,7 +636,6 @@ return strtr($text, array('\n' => "\n")); } - /** * Check if the given text label exists @@ -670,7 +676,6 @@ return false; } - /** * Load a localization package * @@ -680,7 +685,7 @@ */ public function load_language($lang = null, $add = array(), $merge = array()) { - $lang = $this->language_prop(($lang ? $lang : $_SESSION['language'])); + $lang = $this->language_prop($lang ?: $_SESSION['language']); // load localized texts if (empty($this->texts) || $lang != $_SESSION['language']) { @@ -725,11 +730,10 @@ } } - /** * Check the given string and return a valid language code * - * @param string Language code + * @param string $lang Language code * * @return string Valid language code */ @@ -776,7 +780,6 @@ return $lang; } - /** * Read directory program/localization and return a list of available languages * @@ -806,73 +809,38 @@ return $sa_languages; } - /** - * Encrypt using 3DES + * Encrypt a string * - * @param string $clear clear text input - * @param string $key encryption key to retrieve from the configuration, defaults to 'des_key' - * @param boolean $base64 whether or not to base64_encode() the result before returning + * @param string $clear Clear text input + * @param string $key Encryption key to retrieve from the configuration, defaults to 'des_key' + * @param boolean $base64 Whether or not to base64_encode() the result before returning * - * @return string encrypted text + * @return string Encrypted text */ public function encrypt($clear, $key = 'des_key', $base64 = true) { - if (!$clear) { + if (!is_string($clear) || !strlen($clear)) { return ''; } - /*- - * Add a single canary byte to the end of the clear text, which - * will help find out how much of padding will need to be removed - * upon decryption; see http://php.net/mcrypt_generic#68082 - */ - $clear = pack("a*H2", $clear, "80"); - - if (function_exists('openssl_encrypt')) { - $method = 'DES-EDE3-CBC'; - $opts = defined('OPENSSL_RAW_DATA') ? OPENSSL_RAW_DATA : true; - $iv = $this->create_iv(openssl_cipher_iv_length($method)); - $cipher = $iv . openssl_encrypt($clear, $method, $ckey, $opts, $iv); - } - else if (function_exists('mcrypt_module_open') && - ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, "")) - ) { - $iv = $this->create_iv(mcrypt_enc_get_iv_size($td)); - mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv); - $cipher = $iv . mcrypt_generic($td, $clear); - mcrypt_generic_deinit($td); - mcrypt_module_close($td); - } - else { - @include_once 'des.inc'; - - if (function_exists('des')) { - $des_iv_size = 8; - $iv = $this->create_iv($des_iv_size); - $cipher = $iv . des($this->config->get_crypto_key($key), $clear, 1, 1, $iv); - } - else { - self::raise_error(array( - 'code' => 500, 'type' => 'php', - 'file' => __FILE__, 'line' => __LINE__, - 'message' => "Could not perform encryption; make sure OpenSSL or Mcrypt or lib/des.inc is available" - ), true, true); - } - } + $ckey = $this->config->get_crypto_key($key); + $method = $this->config->get_crypto_method(); + $opts = defined('OPENSSL_RAW_DATA') ? OPENSSL_RAW_DATA : true; + $iv = rcube_utils::random_bytes(openssl_cipher_iv_length($method), true); + $cipher = $iv . openssl_encrypt($clear, $method, $ckey, $opts, $iv); return $base64 ? base64_encode($cipher) : $cipher; } - /** - * Decrypt 3DES-encrypted string + * Decrypt a string * - * @param string $cipher encrypted text - * @param string $key encryption key to retrieve from the configuration, defaults to 'des_key' - * @param boolean $base64 whether or not input is base64-encoded + * @param string $cipher Encrypted text + * @param string $key Encryption key to retrieve from the configuration, defaults to 'des_key' + * @param boolean $base64 Whether or not input is base64-encoded * - * @return string decrypted text + * @return string Decrypted text */ public function decrypt($cipher, $key = 'des_key', $base64 = true) { @@ -880,91 +848,122 @@ return ''; } - $cipher = $base64 ? base64_decode($cipher) : $cipher; + $cipher = $base64 ? base64_decode($cipher) : $cipher; + $ckey = $this->config->get_crypto_key($key); + $method = $this->config->get_crypto_method(); + $opts = defined('OPENSSL_RAW_DATA') ? OPENSSL_RAW_DATA : true; + $iv_size = openssl_cipher_iv_length($method); + $iv = substr($cipher, 0, $iv_size); - if (function_exists('openssl_decrypt')) { - $method = 'DES-EDE3-CBC'; - $opts = defined('OPENSSL_RAW_DATA') ? OPENSSL_RAW_DATA : true; - $iv_size = openssl_cipher_iv_length($method); - $iv = substr($cipher, 0, $iv_size); - - // session corruption? (#1485970) - if (strlen($iv) < $iv_size) { - return ''; - } - - $cipher = substr($cipher, $iv_size); - $clear = openssl_decrypt($cipher, $method, $ckey, $opts, $iv); - } - else if (function_exists('mcrypt_module_open') && - ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, "")) - ) { - $iv_size = mcrypt_enc_get_iv_size($td); - $iv = substr($cipher, 0, $iv_size); - - // session corruption? (#1485970) - if (strlen($iv) < $iv_size) { - return ''; - } - - $cipher = substr($cipher, $iv_size); - mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv); - $clear = mdecrypt_generic($td, $cipher); - mcrypt_generic_deinit($td); - mcrypt_module_close($td); - } - else { - @include_once 'des.inc'; - - if (function_exists('des')) { - $des_iv_size = 8; - $iv = substr($cipher, 0, $des_iv_size); - $cipher = substr($cipher, $des_iv_size); - $clear = des($this->config->get_crypto_key($key), $cipher, 0, 1, $iv); - } - else { - self::raise_error(array( - 'code' => 500, 'type' => 'php', - 'file' => __FILE__, 'line' => __LINE__, - 'message' => "Could not perform decryption; make sure Mcrypt is installed or lib/des.inc is available" - ), true, true); - } + // session corruption? (#1485970) + if (strlen($iv) < $iv_size) { + return ''; } - /*- - * Trim PHP's padding and the canary byte; see note in - * rcube::encrypt() and http://php.net/mcrypt_generic#68082 - */ - $clear = substr(rtrim($clear, "\0"), 0, -1); + $cipher = substr($cipher, $iv_size); + $clear = openssl_decrypt($cipher, $method, $ckey, $opts, $iv); return $clear; } - /** - * Generates encryption initialization vector (IV) + * Returns session token for secure URLs * - * @param int Vector size + * @param bool $generate Generate token if not exists in session yet * - * @return string Vector string + * @return string|bool Token string, False when disabled */ - private function create_iv($size) + public function get_secure_url_token($generate = false) { - // mcrypt_create_iv() can be slow when system lacks entrophy - // we'll generate IV vector manually - $iv = ''; - for ($i = 0; $i < $size; $i++) { - $iv .= chr(mt_rand(0, 255)); + if ($len = $this->config->get('use_secure_urls')) { + if (empty($_SESSION['secure_token']) && $generate) { + // generate x characters long token + $length = $len > 1 ? $len : 16; + $token = rcube_utils::random_bytes($length); + + $plugin = $this->plugins->exec_hook('secure_token', + array('value' => $token, 'length' => $length)); + + $_SESSION['secure_token'] = $plugin['value']; + } + + return $_SESSION['secure_token']; } - return $iv; + return false; } + /** + * Generate a unique token to be used in a form request + * + * @return string The request token + */ + public function get_request_token() + { + if (empty($_SESSION['request_token'])) { + $plugin = $this->plugins->exec_hook('request_token', array( + 'value' => rcube_utils::random_bytes(32))); + + $_SESSION['request_token'] = $plugin['value']; + } + + return $_SESSION['request_token']; + } + + /** + * Check if the current request contains a valid token. + * Empty requests aren't checked until use_secure_urls is set. + * + * @param int $mode Request method + * + * @return boolean True if request token is valid false if not + */ + public function check_request($mode = rcube_utils::INPUT_POST) + { + // check secure token in URL if enabled + if ($token = $this->get_secure_url_token()) { + foreach (explode('/', preg_replace('/[?#&].*$/', '', $_SERVER['REQUEST_URI'])) as $tok) { + if ($tok == $token) { + return true; + } + } + + $this->request_status = self::REQUEST_ERROR_URL; + + return false; + } + + $sess_tok = $this->get_request_token(); + + // ajax requests + if (rcube_utils::request_header('X-Roundcube-Request') == $sess_tok) { + return true; + } + + // skip empty requests + if (($mode == rcube_utils::INPUT_POST && empty($_POST)) + || ($mode == rcube_utils::INPUT_GET && empty($_GET)) + ) { + return true; + } + + // default method of securing requests + $token = rcube_utils::get_input_value('_token', $mode); + $sess_id = $_COOKIE[ini_get('session.name')]; + + if (empty($sess_id) || $token != $sess_tok) { + $this->request_status = self::REQUEST_ERROR_TOKEN; + return false; + } + + return true; + } /** * Build a valid URL to this instance of Roundcube * - * @param mixed Either a string with the action or url parameters as key-value pairs + * @param mixed $p Either a string with the action or url parameters as key-value pairs + * * @return string Valid application URL */ public function url($p) @@ -972,7 +971,6 @@ // STUB: should be overloaded by the application return ''; } - /** * Function to be executed in script shutdown @@ -1007,7 +1005,6 @@ } } - /** * Registers shutdown function to be executed on shutdown. * The functions will be executed before destroying any @@ -1020,7 +1017,6 @@ $this->shutdown_functions[] = $function; } - /** * Quote a given string. * Shortcut function for rcube_utils::rep_specialchars_output() @@ -1031,7 +1027,6 @@ { return rcube_utils::rep_specialchars_output($str, 'html', $mode, $newlines); } - /** * Quote a given string for javascript output. @@ -1044,12 +1039,11 @@ return rcube_utils::rep_specialchars_output($str, 'js'); } - /** * Construct shell command, execute it and return output as string. * Keywords {keyword} are replaced with arguments * - * @param $cmd Format string with {keywords} to be replaced + * @param $cmd Format string with {keywords} to be replaced * @param $values (zero, one or more arrays can be passed) * * @return output of command. shell errors not detectable @@ -1097,7 +1091,6 @@ return (string)shell_exec($cmd); } - /** * Print or write debug messages * @@ -1108,12 +1101,13 @@ $args = func_get_args(); if (class_exists('rcube', false)) { - $rcube = self::get_instance(); + $rcube = self::get_instance(); $plugin = $rcube->plugins->exec_hook('console', array('args' => $args)); if ($plugin['abort']) { return; } - $args = $plugin['args']; + + $args = $plugin['args']; } $msg = array(); @@ -1124,13 +1118,12 @@ self::write_log('console', join(";\n", $msg)); } - /** * Append a line to a logfile in the logs directory. * Date will be added automatically to the line. * - * @param $name name of log file - * @param line Line to append + * @param string $name Name of the log file + * @param mixed $line Line to append */ public static function write_log($name, $line) { @@ -1138,14 +1131,14 @@ $line = var_export($line, true); } - $date_format = self::$instance ? self::$instance->config->get('log_date_format') : null; - $log_driver = self::$instance ? self::$instance->config->get('log_driver') : null; - - if (empty($date_format)) { - $date_format = 'd-M-Y H:i:s O'; + $date_format = $log_driver = $session_key = null; + if (self::$instance) { + $date_format = self::$instance->config->get('log_date_format'); + $log_driver = self::$instance->config->get('log_driver'); + $session_key = intval(self::$instance->config->get('log_session_id', 8)); } - $date = date($date_format); + $date = rcube_utils::date_format($date_format); // trigger logging hook if (is_object(self::$instance) && is_object(self::$instance->plugins)) { @@ -1158,8 +1151,8 @@ } // add session ID to the log - if ($sess = session_id()) { - $line = '<' . substr($sess, 0, 8) . '> ' . $line; + if ($session_key > 0 && ($sess = session_id())) { + $line = '<' . substr($sess, 0, $session_key) . '> ' . $line; } if ($log_driver == 'syslog') { @@ -1204,18 +1197,17 @@ return false; } - /** * Throw system error (and show error page). * - * @param array Named parameters + * @param array $arg Named parameters * - code: Error code (required) * - type: Error type [php|db|imap|javascript] (required) * - message: Error message * - file: File where error occurred * - line: Line where error occurred - * @param boolean True to log the error - * @param boolean Terminate script execution + * @param boolean $log True to log the error + * @param boolean $terminate Terminate script execution */ public static function raise_error($arg = array(), $log = false, $terminate = false) { @@ -1262,18 +1254,20 @@ exit(1); } + else if ($cli) { + fwrite(STDERR, 'ERROR: ' . $arg['message']); + } } - /** * Report error according to configured debug_level * - * @param array Named parameters + * @param array $arg_arr Named parameters * @see self::raise_error() */ public static function log_bug($arg_arr) { - $program = strtoupper(!empty($arg_arr['type']) ? $arg_arr['type'] : 'php'); + $program = strtoupper($arg_arr['type'] ?: 'php'); $level = self::get_instance()->config->get('debug_level'); // disable errors for ajax requests, write to log instead (#1487831) @@ -1323,6 +1317,30 @@ } } + /** + * Write debug info to the log + * + * @param string $engine Engine type - file name (memcache, apc) + * @param string $data Data string to log + * @param bool $result Operation result + */ + public static function debug($engine, $data, $result = null) + { + static $debug_counter; + + $line = '[' . (++$debug_counter[$engine]) . '] ' . $data; + + if (($len = strlen($line)) > self::DEBUG_LINE_LENGTH) { + $diff = $len - self::DEBUG_LINE_LENGTH; + $line = substr($line, 0, self::DEBUG_LINE_LENGTH) . "... [truncated $diff bytes]"; + } + + if ($result !== null) { + $line .= ' [' . ($result ? 'TRUE' : 'FALSE') . ']'; + } + + self::write_log($engine, $line); + } /** * Returns current time (with microseconds). @@ -1334,13 +1352,12 @@ return microtime(true); } - /** * Logs time difference according to provided timer * - * @param float $timer Timer (self::timer() result) - * @param string $label Log line prefix - * @param string $dest Log file name + * @param float $timer Timer (self::timer() result) + * @param string $label Log line prefix + * @param string $dest Log file name * * @see self::timer() */ @@ -1391,7 +1408,6 @@ return null; } - /** * Getter for logged user name. * @@ -1407,7 +1423,6 @@ } } - /** * Getter for logged user email (derived from user name not identity). * @@ -1419,7 +1434,6 @@ return $this->user->get_username('mail'); } } - /** * Getter for logged user password. @@ -1489,13 +1503,13 @@ /** * 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 $error SMTP error array (reference) - * @param string $body_file Location of file with saved message body (reference), - * used when delay_file_io is enabled - * @param array $options SMTP options (e.g. DSN request) + * @param object $message Reference to Mail_MIME object + * @param string $from Sender address string + * @param array $mailto Array of recipient address strings + * @param array $error SMTP error array (reference) + * @param string $body_file Location of file with saved message body (reference), + * used when delay_file_io is enabled + * @param array $options SMTP options (e.g. DSN request) * * @return boolean Send status. */ @@ -1528,32 +1542,30 @@ // send thru SMTP server using custom SMTP library if ($this->config->get('smtp_server')) { // generate list of recipients - $a_recipients = array($mailto); + $a_recipients = (array) $mailto; if (strlen($headers['Cc'])) $a_recipients[] = $headers['Cc']; if (strlen($headers['Bcc'])) $a_recipients[] = $headers['Bcc']; - // clean Bcc from header for recipients - $send_headers = $headers; - unset($send_headers['Bcc']); - // here too, it because txtHeaders() below use $message->_headers not only $send_headers - unset($message->_headers['Bcc']); - - $smtp_headers = $message->txtHeaders($send_headers, true); + // remove Bcc header and get the whole head of the message as string + $smtp_headers = $this->message_head($message, array('Bcc')); if ($message->getParam('delay_file_io')) { // use common temp dir - $temp_dir = $this->config->get('temp_dir'); - $body_file = tempnam($temp_dir, 'rcmMsg'); - if (PEAR::isError($mime_result = $message->saveMessageBody($body_file))) { + $temp_dir = $this->config->get('temp_dir'); + $body_file = tempnam($temp_dir, 'rcmMsg'); + $mime_result = $message->saveMessageBody($body_file); + + if (is_a($mime_result, 'PEAR_Error')) { self::raise_error(array('code' => 650, 'type' => 'php', 'file' => __FILE__, 'line' => __LINE__, 'message' => "Could not create message: ".$mime_result->getMessage()), - TRUE, FALSE); + true, false); return false; } + $msg_body = fopen($body_file, 'r'); } else { @@ -1573,39 +1585,33 @@ if (!$sent) { self::raise_error(array('code' => 800, 'type' => 'smtp', 'line' => __LINE__, 'file' => __FILE__, - 'message' => "SMTP error: ".join("\n", $response)), TRUE, FALSE); + 'message' => join("\n", $response)), true, false); } } // send mail using PHP's mail() function else { - // unset some headers because they will be added by the mail() function - $headers_enc = $message->headers($headers); - $headers_php = $message->_headers; - unset($headers_php['To'], $headers_php['Subject']); - - // reset stored headers and overwrite - $message->_headers = array(); - $header_str = $message->txtHeaders($headers_php); + // unset To,Subject headers because they will be added by the mail() function + $header_str = $this->message_head($message, array('To', 'Subject')); // #1485779 if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { - if (preg_match_all('/<([^@]+@[^>]+)>/', $headers_enc['To'], $m)) { - $headers_enc['To'] = implode(', ', $m[1]); + if (preg_match_all('/<([^@]+@[^>]+)>/', $headers['To'], $m)) { + $headers['To'] = implode(', ', $m[1]); } } $msg_body = $message->get(); - if (PEAR::isError($msg_body)) { + if (is_a($msg_body, 'PEAR_Error')) { self::raise_error(array('code' => 650, 'type' => 'php', 'file' => __FILE__, 'line' => __LINE__, 'message' => "Could not create message: ".$msg_body->getMessage()), - TRUE, FALSE); + true, false); } else { $delim = $this->config->header_delimiter(); - $to = $headers_enc['To']; - $subject = $headers_enc['Subject']; + $to = $headers['To']; + $subject = $headers['Subject']; $header_str = rtrim($header_str); if ($delim != "\r\n") { @@ -1628,19 +1634,24 @@ // remove MDN headers after sending unset($headers['Return-Receipt-To'], $headers['Disposition-Notification-To']); - // get all recipients - if ($headers['Cc']) - $mailto .= $headers['Cc']; - if ($headers['Bcc']) - $mailto .= $headers['Bcc']; - if (preg_match_all('/<([^@]+@[^>]+)>/', $mailto, $m)) - $mailto = implode(', ', array_unique($m[1])); - if ($this->config->get('smtp_log')) { + // get all recipient addresses + if (is_array($mailto)) { + $mailto = implode(',', $mailto); + } + if ($headers['Cc']) { + $mailto .= ',' . $headers['Cc']; + } + if ($headers['Bcc']) { + $mailto .= ',' . $headers['Bcc']; + } + + $mailto = rcube_mime::decode_address_list($mailto, null, false, null, true); + self::write_log('sendmail', sprintf("User %s [%s]; Message for %s; %s", $this->user->get_username(), - $_SERVER['REMOTE_ADDR'], - $mailto, + rcube_utils::remote_addr(), + implode(', ', $mailto), !empty($response) ? join('; ', $response) : '')); } } @@ -1653,19 +1664,31 @@ fclose($msg_body); } - $message->_headers = array(); - $message->headers($headers); + $message->headers($headers, true); return $sent; } + /** + * Return message headers as a string + */ + protected function message_head($message, $unset = array()) + { + // requires Mail_mime >= 1.9.0 + $headers = array(); + foreach ((array) $unset as $header) { + $headers[$header] = null; + } + + return $message->txtHeaders($headers, true); + } } /** * Lightweight plugin API class serving as a dummy if plugins are not enabled * - * @package Framework + * @package Framework * @subpackage Core */ class rcube_dummy_plugin_api -- Gitblit v1.9.1