diff --git a/app/Config/email.php.default b/app/Config/email.php.default index bf2b4b51ad5..0d5d11a5b9b 100644 --- a/app/Config/email.php.default +++ b/app/Config/email.php.default @@ -30,6 +30,7 @@ * transport => The name of a supported transport; valid options are as follows: * Mail - Send using PHP mail function * Smtp - Send using SMTP + * Debug - Do not send the email, just return the result * * You can add custom transports (or override existing transports) by adding the * appropriate file to app/Network/Email. Transports should be named 'YourTransport.php', @@ -43,7 +44,9 @@ class EmailConfig { public $default = array( 'transport' => 'Mail', - 'from' => 'you@localhost' + 'from' => 'you@localhost', + //'charset' => 'utf-8', + //'headerCharset' => 'utf-8', ); public $smtp = array( @@ -54,7 +57,10 @@ class EmailConfig { 'timeout' => 30, 'username' => 'user', 'password' => 'secret', - 'client' => null + 'client' => null, + 'log' => false + //'charset' => 'utf-8', + //'headerCharset' => 'utf-8', ); public $fast = array( @@ -82,7 +88,10 @@ class EmailConfig { 'timeout' => 30, 'username' => 'user', 'password' => 'secret', - 'client' => null + 'client' => null, + 'log' => true, + //'charset' => 'utf-8', + //'headerCharset' => 'utf-8', ); } diff --git a/lib/Cake/Console/Templates/skel/Config/email.php.default b/lib/Cake/Console/Templates/skel/Config/email.php.default index bf2b4b51ad5..0d5d11a5b9b 100644 --- a/lib/Cake/Console/Templates/skel/Config/email.php.default +++ b/lib/Cake/Console/Templates/skel/Config/email.php.default @@ -30,6 +30,7 @@ * transport => The name of a supported transport; valid options are as follows: * Mail - Send using PHP mail function * Smtp - Send using SMTP + * Debug - Do not send the email, just return the result * * You can add custom transports (or override existing transports) by adding the * appropriate file to app/Network/Email. Transports should be named 'YourTransport.php', @@ -43,7 +44,9 @@ class EmailConfig { public $default = array( 'transport' => 'Mail', - 'from' => 'you@localhost' + 'from' => 'you@localhost', + //'charset' => 'utf-8', + //'headerCharset' => 'utf-8', ); public $smtp = array( @@ -54,7 +57,10 @@ class EmailConfig { 'timeout' => 30, 'username' => 'user', 'password' => 'secret', - 'client' => null + 'client' => null, + 'log' => false + //'charset' => 'utf-8', + //'headerCharset' => 'utf-8', ); public $fast = array( @@ -82,7 +88,10 @@ class EmailConfig { 'timeout' => 30, 'username' => 'user', 'password' => 'secret', - 'client' => null + 'client' => null, + 'log' => true, + //'charset' => 'utf-8', + //'headerCharset' => 'utf-8', ); } diff --git a/lib/Cake/Network/Email/CakeEmail.php b/lib/Cake/Network/Email/CakeEmail.php index 84220cd3cba..a72a45ceb9d 100644 --- a/lib/Cake/Network/Email/CakeEmail.php +++ b/lib/Cake/Network/Email/CakeEmail.php @@ -239,12 +239,26 @@ class CakeEmail { protected $_transportClass = null; /** - * charset the email is sent in + * Charset the email body is sent in + * * * @var string */ public $charset = 'utf-8'; +/** + * Charset the email header is sent in + * If null, the $charset property will be used as default + * @var string + */ + public $headerCharset = null; + +/** + * The application wide charset, used to encode headers and body + * @var string + */ + public $_appCharset = null; + /** * List of files that should be attached to the email. * @@ -274,13 +288,16 @@ class CakeEmail { * */ public function __construct($config = null) { - $charset = Configure::read('App.encoding'); - if ($charset !== null) { - $this->charset = $charset; + $this->_appCharset = Configure::read('App.encoding'); + if ($this->_appCharset !== null) { + $this->charset = $this->_appCharset; } if ($config) { $this->config($config); } + if (empty($this->headerCharset)) { + $this->headerCharset = $this->charset; + } } /** @@ -994,15 +1011,24 @@ public static function deliver($to = null, $subject = null, $message = null, $tr protected function _applyConfig($config) { if (is_string($config)) { if (!class_exists('EmailConfig') && !config('email')) { - throw new SocketException(__d('cake_dev', '%s not found.', APP . 'Config' . DS . 'email.php')); + throw new ConfigureException(__d('cake_dev', '%s not found.', APP . 'Config' . DS . 'email.php')); } $configs = new EmailConfig(); if (!isset($configs->{$config})) { - throw new SocketException(__d('cake_dev', 'Unknown email configuration "%s".', $config)); + throw new ConfigureException(__d('cake_dev', 'Unknown email configuration "%s".', $config)); } $config = $configs->{$config}; } $this->_config += $config; + if (!empty($config['charset'])) { + $this->charset = $config['charset']; + } + if (!empty($config['headerCharset'])) { + $this->headerCharset = $config['headerCharset']; + } + if (empty($this->headerCharset)) { + $this->headerCharset = $this->charset; + } $simpleMethods = array( 'from', 'sender', 'to', 'replyTo', 'readReceipt', 'returnPath', 'cc', 'bcc', 'messageId', 'subject', 'viewRender', 'viewVars', 'attachments', @@ -1073,15 +1099,31 @@ protected function _encode($text) { $internalEncoding = function_exists('mb_internal_encoding'); if ($internalEncoding) { $restore = mb_internal_encoding(); - mb_internal_encoding($this->charset); + mb_internal_encoding($this->_appCharset); } - $return = mb_encode_mimeheader($text, $this->charset, 'B'); + $text = $this->_encodeString($text, $this->headerCharset); + $return = mb_encode_mimeheader($text, $this->headerCharset, 'B'); if ($internalEncoding) { mb_internal_encoding($restore); } return $return; } +/** + * Translates a string for one charset to another if the App.encoding value + * differs and the mb_convert_encoding function exists + * + * @param string $text The text to be converted + * @param string $charset the target encoding + * @return string + */ + protected function _encodeString($text, $charset) { + if ($this->_appCharset === $charset || !function_exists('mb_convert_encoding')) { + return $text; + } + return mb_convert_encoding($text, $charset, $this->_appCharset); + } + /** * Wrap the message to follow the RFC 2822 - 2.1.1 * @@ -1322,7 +1364,7 @@ protected function _render($content) { $View->viewPath = $View->layoutPath = 'Emails' . DS . $this->_emailFormat; $View->viewVars['content'] = $content; - $rendered = $View->render($template, $layout); + $rendered = $this->_encodeString($View->render($template, $layout), $this->charset); $content = explode("\n", $rendered); if ($this->_emailFormat === 'html') { diff --git a/lib/Cake/Test/Case/Network/Email/CakeEmailTest.php b/lib/Cake/Test/Case/Network/Email/CakeEmailTest.php index ebce09cef05..c4523eb290c 100644 --- a/lib/Cake/Test/Case/Network/Email/CakeEmailTest.php +++ b/lib/Cake/Test/Case/Network/Email/CakeEmailTest.php @@ -1120,4 +1120,67 @@ public function testEmailFormat() { $result = $this->CakeEmail->emailFormat('invalid'); } +/** + * Tests that it is possible to add charset configuration to a CakeEmail object + * + * @return void + */ + public function testConfigCharset() { + $email = new CakeEmail(); + $this->assertEquals($email->charset, Configure::read('App.encoding')); + $this->assertEquals($email->headerCharset, Configure::read('App.encoding')); + + $email = new CakeEmail(array('charset' => 'iso-2022-jp', 'headerCharset' => 'iso-2022-jp-ms')); + $this->assertEquals($email->charset, 'iso-2022-jp'); + $this->assertEquals($email->headerCharset, 'iso-2022-jp-ms'); + + $email = new CakeEmail(array('charset' => 'iso-2022-jp')); + $this->assertEquals($email->charset, 'iso-2022-jp'); + $this->assertEquals($email->headerCharset, 'iso-2022-jp'); + + $email = new CakeEmail(array('headerCharset' => 'iso-2022-jp-ms')); + $this->assertEquals($email->charset, Configure::read('App.encoding')); + $this->assertEquals($email->headerCharset, 'iso-2022-jp-ms'); + } + +/** + * Tests that the header is encoded using the configured headerCharset + * + * @return void + */ + public function testHeaderEncoding() { + $this->skipIf(!function_exists('mb_convert_encoding')); + $email = new CakeEmail(array('headerCharset' => 'iso-2022-jp-ms', 'transport' => 'Debug')); + $email->subject('あれ?もしかしての前と'); + $headers = $email->getHeaders(array('subject')); + $expected = "?ISO-2022-JP?B?GyRCJCIkbCEpJGIkNyQrJDckRiROQTAkSBsoQg==?="; + $this->assertContains($expected, $headers['Subject']); + + $email->to('someone@example.com')->from('someone@example.com'); + $result = $email->send('ってテーブルを作ってやってたらう'); + $this->assertContains('ってテーブルを作ってやってたらう', $result['message']); + } + +/** + * Tests that the body is encoded using the configured charset + * + * @return void + */ + public function testBodyEncoding() { + $this->skipIf(!function_exists('mb_convert_encoding')); + $email = new CakeEmail(array( + 'charset' => 'iso-2022-jp', + 'headerCharset' => 'iso-2022-jp-ms', + 'transport' => 'Debug' + )); + $email->subject('あれ?もしかしての前と'); + $headers = $email->getHeaders(array('subject')); + $expected = "?ISO-2022-JP?B?GyRCJCIkbCEpJGIkNyQrJDckRiROQTAkSBsoQg==?="; + $this->assertContains($expected, $headers['Subject']); + + $email->to('someone@example.com')->from('someone@example.com'); + $result = $email->send('ってテーブルを作ってやってたらう'); + $this->assertContains('Content-Type: text/plain; charset=iso-2022-jp', $result['headers']); + $this->assertContains('ってテーブルを作ってやってたらう', $result['message']); + } }