From: Lukas Reschke Date: Thu, 12 Feb 2015 12:53:27 +0000 (+0100) Subject: Migrate to SwiftMail X-Git-Tag: v8.1.0alpha1~272^2~6 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=13486a5ada62e4355473ca01e07371c162951e84;p=nextcloud-server.git Migrate to SwiftMail Replaces the OC_Mail and phpmailer with SwiftMail allowing us to mock it properly. Fixes the unit test execution on master on local machines and https://github.com/owncloud/core/issues/12014 Conflicts: 3rdparty lib/private/server.php lib/public/iservercontainer.php tests/lib/mail.php tests/settings/controller/mailsettingscontrollertest.php Conflicts: 3rdparty lib/private/mail.php lib/private/server.php lib/public/iservercontainer.php settings/ajax/lostpassword.php settings/application.php --- diff --git a/3rdparty b/3rdparty index 7c536152a16..08b820614ae 160000 --- a/3rdparty +++ b/3rdparty @@ -1 +1 @@ -Subproject commit 7c536152a16f62797b60c6c06f8c2e23eb8b755e +Subproject commit 08b820614ae99477d7c9319630f400d22b20e2fd diff --git a/core/lostpassword/controller/lostcontroller.php b/core/lostpassword/controller/lostcontroller.php index c039c578b59..d1a4528d449 100644 --- a/core/lostpassword/controller/lostcontroller.php +++ b/core/lostpassword/controller/lostcontroller.php @@ -201,7 +201,7 @@ class LostController extends Controller { try { // FIXME: should be added to the container and injected in here - \OC_Mail::send( + \OCP\Util::sendMail( $email, $user, $this->l10n->t('%s password reset', array($this->defaults->getName())), diff --git a/lib/private/mail.php b/lib/private/mail.php deleted file mode 100644 index 6b7eec6e080..00000000000 --- a/lib/private/mail.php +++ /dev/null @@ -1,153 +0,0 @@ - - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - -/** - * OC_Mail - * - * A class to handle mail sending. - */ - -class OC_Mail { - - /** - * send an email - * - * @param string $toaddress - * @param string $toname - * @param string $subject - * @param string $mailtext - * @param string $fromaddress - * @param string $fromname - * @param integer $html - * @param string $altbody - * @param string $ccaddress - * @param string $ccname - * @param string $bcc - * @throws Exception - */ - public static function send($toaddress, $toname, $subject, $mailtext, $fromaddress, $fromname, - $html=0, $altbody='', $ccaddress='', $ccname='', $bcc='') { - - $SMTPMODE = OC_Config::getValue( 'mail_smtpmode', 'sendmail' ); - $SMTPHOST = OC_Config::getValue( 'mail_smtphost', '127.0.0.1' ); - $SMTPPORT = OC_Config::getValue( 'mail_smtpport', 25 ); - $SMTPAUTH = OC_Config::getValue( 'mail_smtpauth', false ); - $SMTPAUTHTYPE = OC_Config::getValue( 'mail_smtpauthtype', 'LOGIN' ); - $SMTPUSERNAME = OC_Config::getValue( 'mail_smtpname', '' ); - $SMTPPASSWORD = OC_Config::getValue( 'mail_smtppassword', '' ); - $SMTPDEBUG = OC_Config::getValue( 'mail_smtpdebug', false ); - $SMTPTIMEOUT = OC_Config::getValue( 'mail_smtptimeout', 10 ); - $SMTPSECURE = OC_Config::getValue( 'mail_smtpsecure', '' ); - - - $mailo = new PHPMailer(true); - if($SMTPMODE=='sendmail') { - $mailo->IsSendmail(); - }elseif($SMTPMODE=='smtp') { - $mailo->IsSMTP(); - }elseif($SMTPMODE=='qmail') { - $mailo->IsQmail(); - }else{ - $mailo->IsMail(); - } - - - $mailo->Host = $SMTPHOST; - $mailo->Port = $SMTPPORT; - $mailo->SMTPAuth = $SMTPAUTH; - $mailo->SMTPDebug = $SMTPDEBUG; - $mailo->Debugoutput = 'error_log'; - $mailo->SMTPSecure = $SMTPSECURE; - $mailo->AuthType = $SMTPAUTHTYPE; - $mailo->Username = $SMTPUSERNAME; - $mailo->Password = $SMTPPASSWORD; - $mailo->Timeout = $SMTPTIMEOUT; - - $mailo->From = $fromaddress; - $mailo->FromName = $fromname;; - $mailo->Sender = $fromaddress; - $mailo->XMailer = ' '; - try { - $toaddress = self::buildAsciiEmail($toaddress); - $mailo->AddAddress($toaddress, $toname); - - if($ccaddress != '') $mailo->AddCC($ccaddress, $ccname); - if($bcc != '') $mailo->AddBCC($bcc); - - $mailo->AddReplyTo($fromaddress, $fromname); - - $mailo->WordWrap = 78; - $mailo->IsHTML($html == 1); - - $mailo->Subject = $subject; - if($altbody == '') { - $mailo->Body = $mailtext.OC_MAIL::getfooter(); - $mailo->AltBody = ''; - }else{ - $mailo->Body = $mailtext; - $mailo->AltBody = $altbody; - } - $mailo->CharSet = 'UTF-8'; - - $mailo->Send(); - unset($mailo); - OC_Log::write('mail', - 'Mail from '.$fromname.' ('.$fromaddress.')'.' to: '.$toname.'('.$toaddress.')'.' subject: '.$subject, - OC_Log::DEBUG); - } catch (Exception $exception) { - OC_Log::write('mail', $exception->getMessage(), OC_Log::ERROR); - throw($exception); - } - } - - /** - * return the footer for a mail - * - */ - public static function getfooter() { - - $defaults = new OC_Defaults(); - - $txt="\n--\n"; - $txt.=$defaults->getName() . "\n"; - $txt.=$defaults->getSlogan() . "\n"; - - return($txt); - - } - - /** - * @param string $emailAddress a given email address to be validated - * @return bool - */ - public static function validateAddress($emailAddress) { - if (strpos($emailAddress, '@') === false) { - return false; - } - $emailAddress = self::buildAsciiEmail($emailAddress); - return PHPMailer::ValidateAddress($emailAddress); - } - - /** - * IDN domains will be properly converted to ascii domains. - * - * @param string $emailAddress - * @return string - */ - public static function buildAsciiEmail($emailAddress) { - if (!function_exists('idn_to_ascii')) { - return $emailAddress; - } - - list($name, $domain) = explode('@', $emailAddress, 2); - $domain = idn_to_ascii($domain); - - return "$name@$domain"; - } - -} diff --git a/lib/private/mail/mailer.php b/lib/private/mail/mailer.php new file mode 100644 index 00000000000..ff4b0a0ef62 --- /dev/null +++ b/lib/private/mail/mailer.php @@ -0,0 +1,154 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Mail; + +use OCP\IConfig; +use OCP\Mail\IMailer; + +/** + * Class Mailer provides some basic functions to create a mail message that can be used in combination with + * \OC\Mail\Message. + * + * Example usage: + * + * $mailer = \OC::$server->getMailer(); + * $message = $mailer->createMessage(); + * $message->setSubject('Your Subject'); + * $message->setFrom(array('cloud@domain.org' => 'ownCloud Notifier'); + * $message->setTo(array('recipient@domain.org' => 'Recipient'); + * $message->setBody('The message text'); + * $mailer->send($message); + * + * This message can then be passed to send() of \OC\Mail\Mailer + * + * @package OC\Mail + */ +class Mailer implements IMailer { + /** @var \Swift_SmtpTransport|\Swift_SendmailTransport|\Swift_MailTransport Cached transport */ + private $instance = null; + /** @var IConfig */ + private $config; + /** @var \OC_Defaults */ + private $defaults; + + /** + * @param IConfig $config + * @param \OC_Defaults $defaults + */ + function __construct(IConfig $config, \OC_Defaults $defaults) { + $this->config = $config; + $this->defaults = $defaults; + } + + /** + * Creates a new message object that can be passed to send() + * + * @return Message + */ + public function createMessage() { + return new Message(new \Swift_Message()); + } + + /** + * Send the specified message. Also sets the from address to the value defined in config.php + * if no-one has been passed. + * + * @param Message $message Message to send + * @return string[] Array with failed recipients. Be aware that this depends on the used mail backend and + * therefore should be considered + * @throws \Exception In case it was not possible to send the message. (for example if an invalid mail address + * has been supplied.) + */ + public function send(Message $message) { + if (sizeof($message->getFrom()) === 0) { + $message->setFrom(array(\OCP\Util::getDefaultEmailAddress($this->defaults->getName()))); + } + + $failedRecipients = array(); + + $this->getInstance()->send($message->getSwiftMessage(), $failedRecipients); + + return $failedRecipients; + } + + /** + * Returns whatever transport is configured within the config + * + * @return \Swift_SmtpTransport|\Swift_SendmailTransport|\Swift_MailTransport + */ + protected function getInstance() { + if (!is_null($this->instance)) { + return $this->instance; + } + + switch ($this->config->getSystemValue('mail_smtpmode', 'php')) { + case 'smtp': + $this->instance = $this->getSMTPInstance(); + break; + case 'sendmail': + $this->instance = $this->getSendMailInstance(); + break; + default: + $this->instance = $this->getMailInstance(); + break; + } + + return $this->instance; + } + + /** + * Returns the SMTP transport + * + * @return \Swift_SmtpTransport + */ + protected function getSmtpInstance() { + $transport = \Swift_SmtpTransport::newInstance(); + $transport->setTimeout($this->config->getSystemValue('mail_smtptimeout', 10)); + $transport->setHost($this->config->getSystemValue('mail_smtphost', '127.0.0.1')); + $transport->setPort($this->config->getSystemValue('mail_smtpport', 25)); + if ($this->config->getSystemValue('mail_smtpauth', false)) { + $transport->setUsername($this->config->getSystemValue('mail_smtpname', '')); + $transport->setPassword($this->config->getSystemValue('mail_smtppassword', '')); + $transport->setAuthMode($this->config->getSystemValue('mail_smtpauthtype', 'LOGIN')); + } + $smtpSecurity = $this->config->getSystemValue('mail_smtpsecure', ''); + if (!empty($smtpSecurity)) { + $transport->setEncryption($smtpSecurity); + } + return $transport; + } + + /** + * Returns the sendmail transport + * + * @return \Swift_SendmailTransport + */ + protected function getSendMailInstance() { + switch ($this->config->getSystemValue('mail_smtpmode', 'sendmail')) { + case 'qmail': + $binaryPath = '/var/qmail/bin/sendmail'; + break; + default: + $binaryPath = '/usr/sbin/sendmail'; + break; + } + + return \Swift_SendmailTransport::newInstance($binaryPath . ' -bs'); + } + + /** + * Returns the mail transport + * + * @return \Swift_MailTransport + */ + protected function getMailInstance() { + return \Swift_MailTransport::newInstance(); + } + +} diff --git a/lib/private/mail/message.php b/lib/private/mail/message.php new file mode 100644 index 00000000000..60700196c5f --- /dev/null +++ b/lib/private/mail/message.php @@ -0,0 +1,206 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Mail; + +use Swift_Message; + +/** + * Class Message provides a wrapper around SwiftMail + * + * @package OC\Mail + */ +class Message { + /** @var Swift_Message */ + private $swiftMessage; + + /** + * @param Swift_Message $swiftMessage + */ + function __construct(Swift_Message $swiftMessage) { + $this->swiftMessage = $swiftMessage; + } + + /** + * SwiftMailer does currently not work with IDN domains, this function therefore converts the domains + * FIXME: Remove this once SwiftMailer supports IDN + * + * @param array $addresses Array of mail addresses, key will get converted + * @return array Converted addresses if `idn_to_ascii` exists + */ + protected function convertAddresses($addresses) { + if (!function_exists('idn_to_ascii')) { + return $addresses; + } + + $convertedAddresses = array(); + + foreach($addresses as $email => $readableName) { + if(!is_numeric($email)) { + list($name, $domain) = explode('@', $email, 2); + $domain = idn_to_ascii($domain); + $convertedAddresses[$name.'@'.$domain] = $readableName; + } else { + list($name, $domain) = explode('@', $readableName, 2); + $domain = idn_to_ascii($domain); + $convertedAddresses[$email] = $name.'@'.$domain; + } + } + + return $convertedAddresses; + } + + /** + * Set the from address of this message. + * + * If no "From" address is used \OC\Mail\Mailer will use mail_from_address and mail_domain from config.php + * + * @param array $addresses Example: array('sender@domain.org', 'other@domain.org' => 'A name') + * @return $this + */ + public function setFrom(array $addresses) { + $addresses = $this->convertAddresses($addresses); + + $this->swiftMessage->setFrom($addresses); + return $this; + } + + /** + * Get the from address of this message. + * + * @return array + */ + public function getFrom() { + return $this->swiftMessage->getFrom(); + } + + /** + * Set the to addresses of this message. + * + * @param array $recipients Example: array('recipient@domain.org', 'other@domain.org' => 'A name') + * @return $this + */ + public function setTo(array $recipients) { + $recipients = $this->convertAddresses($recipients); + + $this->swiftMessage->setTo($recipients); + return $this; + } + + /** + * Get the to address of this message. + * + * @return array + */ + public function getTo() { + return $this->swiftMessage->getTo(); + } + + /** + * Set the CC recipients of this message. + * + * @param array $recipients Example: array('recipient@domain.org', 'other@domain.org' => 'A name') + * @return $this + */ + public function setCc(array $recipients) { + $recipients = $this->convertAddresses($recipients); + + $this->swiftMessage->setCc($recipients); + return $this; + } + + /** + * Get the cc address of this message. + * + * @return array + */ + public function getCc() { + return $this->swiftMessage->getCc(); + } + + /** + * Set the BCC recipients of this message. + * + * @param array $recipients Example: array('recipient@domain.org', 'other@domain.org' => 'A name') + * @return $this + */ + public function setBcc(array $recipients) { + $recipients = $this->convertAddresses($recipients); + + $this->swiftMessage->setBcc($recipients); + return $this; + } + + /** + * Get the Bcc address of this message. + * + * @return array + */ + public function getBcc() { + return $this->swiftMessage->getBcc(); + } + + /** + * Set the subject of this message. + * + * @param $subject + * @return $this + */ + public function setSubject($subject) { + $this->swiftMessage->setSubject($subject); + return $this; + } + + /** + * Get the from subject of this message. + * + * @return string + */ + public function getSubject() { + return $this->swiftMessage->getSubject(); + } + + /** + * Set the plain-text body of this message. + * + * @param string $body + * @return $this + */ + public function setPlainBody($body) { + $this->swiftMessage->setBody($body); + return $this; + } + + /** + * Get the plain body of this message. + * + * @return string + */ + public function getPlainBody() { + return $this->swiftMessage->getBody(); + } + + /** + * Set the HTML body of this message. Consider also sending a plain-text body instead of only an HTML one. + * + * @param string $body + * @return $this + */ + public function setHtmlBody($body) { + $this->swiftMessage->addPart($body, 'text/html'); + return $this; + } + + /** + * Get's the underlying SwiftMessage + * @return Swift_Message + */ + public function getSwiftMessage() { + return $this->swiftMessage; + } +} diff --git a/lib/private/mail/util.php b/lib/private/mail/util.php new file mode 100644 index 00000000000..b301e79d492 --- /dev/null +++ b/lib/private/mail/util.php @@ -0,0 +1,45 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Mail; + +/** + * Class Util + * + * @package OC\Mail + */ +class Util { + /** + * Checks if an e-mail address is valid + * + * @param string $email Email address to be validated + * @return bool True if the mail address is valid, false otherwise + */ + public static function validateMailAddress($email) { + return \Swift_Validate::email(self::convertEmail($email)); + } + + /** + * SwiftMailer does currently not work with IDN domains, this function therefore converts the domains + * + * FIXME: Remove this once SwiftMailer supports IDN + * + * @param string $email + * @return string Converted mail address if `idn_to_ascii` exists + */ + protected static function convertEmail($email) { + if (!function_exists('idn_to_ascii') || strpos($email, '@') === false) { + return $email; + } + + list($name, $domain) = explode('@', $email, 2); + $domain = idn_to_ascii($domain); + return $name.'@'.$domain; + } + +} diff --git a/lib/private/server.php b/lib/private/server.php index 896abf04a40..a3682dea6b3 100644 --- a/lib/private/server.php +++ b/lib/private/server.php @@ -11,6 +11,7 @@ use OC\Command\AsyncBus; use OC\Diagnostics\NullQueryLogger; use OC\Diagnostics\EventLogger; use OC\Diagnostics\QueryLogger; +use OC\Mail\Mailer; use OC\Security\CertificateManager; use OC\Files\Node\Root; use OC\Files\View; @@ -312,6 +313,9 @@ class Server extends SimpleContainer implements IServerContainer { $stream ); }); + $this->registerService('Mailer', function(Server $c) { + return new Mailer($c->getConfig(), new \OC_Defaults()); + }); } /** @@ -712,6 +716,15 @@ class Server extends SimpleContainer implements IServerContainer { return $this->query('AppManager'); } + /** + * Creates a new mailer + * + * @return \OCP\Mail\IMailer + */ + function getMailer() { + return $this->query('Mailer'); + } + /** * Get the webroot * diff --git a/lib/public/iservercontainer.php b/lib/public/iservercontainer.php index ec23cc52345..20345e61212 100644 --- a/lib/public/iservercontainer.php +++ b/lib/public/iservercontainer.php @@ -318,9 +318,15 @@ interface IServerContainer { * @return \bantu\IniGetWrapper\IniGetWrapper */ function getIniWrapper(); - /** * @return \OCP\Command\IBus */ function getCommandBus(); + + /** + * Creates a new mailer + * + * @return \OCP\Mail\IMailer + */ + function getMailer(); } diff --git a/lib/public/mail/imailer.php b/lib/public/mail/imailer.php new file mode 100644 index 00000000000..2a20ead612b --- /dev/null +++ b/lib/public/mail/imailer.php @@ -0,0 +1,49 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OCP\Mail; +use OC\Mail\Message; + +/** + * Class IMailer provides some basic functions to create a mail message that can be used in combination with + * \OC\Mail\Message. + * + * Example usage: + * + * $mailer = \OC::$server->getMailer(); + * $message = $mailer->createMessage(); + * $message->setSubject('Your Subject'); + * $message->setFrom(array('cloud@domain.org' => 'ownCloud Notifier'); + * $message->setTo(array('recipient@domain.org' => 'Recipient'); + * $message->setBody('The message text'); + * $mailer->send($message); + * + * This message can then be passed to send() of \OC\Mail\Mailer + * + * @package OCP\Mail + */ +interface IMailer { + /** + * Creates a new message object that can be passed to send() + * + * @return Message + */ + public function createMessage(); + + /** + * Send the specified message. Also sets the from address to the value defined in config.php + * if no-one has been passed. + * + * @param Message $message Message to send + * @return string[] Array with failed recipients. Be aware that this depends on the used mail backend and + * therefore should be considered + * @throws \Exception In case it was not possible to send the message. (for example if an invalid mail address + * has been supplied.) + */ + public function send(Message $message); +} diff --git a/lib/public/mail/util.php b/lib/public/mail/util.php new file mode 100644 index 00000000000..ea59649e620 --- /dev/null +++ b/lib/public/mail/util.php @@ -0,0 +1,26 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OCP\Mail; + +/** + * Class Util provides some helpers for mail addresses + * + * @package OCP\Mail + */ +class Util { + /** + * Checks if an e-mail address is valid + * + * @param string $email Email address to be validated + * @return bool True if the mail address is valid, false otherwise + */ + public static function validateMailAddress($email) { + return \OC\Mail\Util::validateMailAddress($email); + } +} diff --git a/lib/public/util.php b/lib/public/util.php index aa6cd5ba012..71ab8cabce5 100644 --- a/lib/public/util.php +++ b/lib/public/util.php @@ -63,12 +63,31 @@ class Util { * @param string $ccaddress * @param string $ccname * @param string $bcc + * @deprecated Use \OCP\Mail\IMailer instead */ public static function sendMail( $toaddress, $toname, $subject, $mailtext, $fromaddress, $fromname, $html = 0, $altbody = '', $ccaddress = '', $ccname = '', $bcc = '') { - // call the internal mail class - \OC_MAIL::send($toaddress, $toname, $subject, $mailtext, $fromaddress, $fromname, - $html, $altbody, $ccaddress, $ccname, $bcc); + $mailer = \OC::$server->getMailer(); + $message = $mailer->createMessage(); + $message->setTo(array($toaddress => $toname)); + $message->setSubject($subject); + $message->setPlainBody($mailtext); + $message->setFrom(array($fromaddress => $fromname)); + if($html === 1) { + $message->setHTMLBody($altbody); + } + if(!empty($ccaddress)) { + if(!empty($ccname)) { + $message->setCc(array($ccaddress => $ccname)); + } else { + $message->setCc(array($ccaddress)); + } + } + if(!empty($bcc)) { + $message->setBcc(array($bcc)); + } + + $mailer->send($message); } /** @@ -275,7 +294,7 @@ class Util { $host_name = \OC_Config::getValue('mail_domain', $host_name); $defaultEmailAddress = $user_part.'@'.$host_name; - if (\OC_Mail::validateAddress($defaultEmailAddress)) { + if (\OCP\Mail\Util::validateMailAddress($defaultEmailAddress)) { return $defaultEmailAddress; } diff --git a/settings/application.php b/settings/application.php index 6fe23447a72..ce47f155ebe 100644 --- a/settings/application.php +++ b/settings/application.php @@ -47,7 +47,7 @@ class Application extends App { $c->query('Config'), $c->query('UserSession'), $c->query('Defaults'), - $c->query('Mail'), + $c->query('Mailer'), $c->query('DefaultMailAddress') ); }); @@ -89,7 +89,7 @@ class Application extends App { $c->query('L10N'), $c->query('Logger'), $c->query('Defaults'), - $c->query('Mail'), + $c->query('Mailer'), $c->query('DefaultMailAddress'), $c->query('URLGenerator'), $c->query('OCP\\App\\IAppManager'), @@ -151,8 +151,8 @@ class Application extends App { $container->registerService('SubAdminFactory', function(IContainer $c) { return new SubAdminFactory(); }); - $container->registerService('Mail', function(IContainer $c) { - return new \OC_Mail; + $container->registerService('Mailer', function(IContainer $c) { + return $c->query('ServerContainer')->getMailer(); }); $container->registerService('Defaults', function(IContainer $c) { return new \OC_Defaults; diff --git a/settings/controller/mailsettingscontroller.php b/settings/controller/mailsettingscontroller.php index 5874e644abb..43715b82237 100644 --- a/settings/controller/mailsettingscontroller.php +++ b/settings/controller/mailsettingscontroller.php @@ -16,6 +16,8 @@ use \OCP\AppFramework\Controller; use OCP\IRequest; use OCP\IL10N; use OCP\IConfig; +use OCP\Mail\IMailer; +use OCP\Mail\IMessage; /** * @package OC\Settings\Controller @@ -30,8 +32,8 @@ class MailSettingsController extends Controller { private $userSession; /** @var \OC_Defaults */ private $defaults; - /** @var \OC_Mail */ - private $mail; + /** @var IMailer */ + private $mailer; /** @var string */ private $defaultMailAddress; @@ -42,7 +44,7 @@ class MailSettingsController extends Controller { * @param IConfig $config * @param Session $userSession * @param \OC_Defaults $defaults - * @param \OC_Mail $mail + * @param IMailer $mailer * @param string $defaultMailAddress */ public function __construct($appName, @@ -51,14 +53,14 @@ class MailSettingsController extends Controller { IConfig $config, Session $userSession, \OC_Defaults $defaults, - \OC_Mail $mail, + IMailer $mailer, $defaultMailAddress) { parent::__construct($appName, $request); $this->l10n = $l10n; $this->config = $config; $this->userSession = $userSession; $this->defaults = $defaults; - $this->mail = $mail; + $this->mailer = $mailer; $this->defaultMailAddress = $defaultMailAddress; } diff --git a/settings/controller/userscontroller.php b/settings/controller/userscontroller.php index a20cbb4050a..507e57ef940 100644 --- a/settings/controller/userscontroller.php +++ b/settings/controller/userscontroller.php @@ -26,6 +26,7 @@ use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserManager; use OCP\IUserSession; +use OCP\Mail\IMailer; /** * @package OC\Settings\Controller @@ -47,8 +48,8 @@ class UsersController extends Controller { private $log; /** @var \OC_Defaults */ private $defaults; - /** @var \OC_Mail */ - private $mail; + /** @var IMailer */ + private $mailer; /** @var string */ private $fromMailAddress; /** @var IURLGenerator */ @@ -71,7 +72,7 @@ class UsersController extends Controller { * @param IL10N $l10n * @param ILogger $log * @param \OC_Defaults $defaults - * @param \OC_Mail $mail + * @param IMailer $mailer * @param string $fromMailAddress * @param IURLGenerator $urlGenerator * @param IAppManager $appManager @@ -87,7 +88,7 @@ class UsersController extends Controller { IL10N $l10n, ILogger $log, \OC_Defaults $defaults, - \OC_Mail $mail, + IMailer $mailer, $fromMailAddress, IURLGenerator $urlGenerator, IAppManager $appManager, @@ -101,7 +102,7 @@ class UsersController extends Controller { $this->l10n = $l10n; $this->log = $log; $this->defaults = $defaults; - $this->mail = $mail; + $this->mailer = $mailer; $this->fromMailAddress = $fromMailAddress; $this->urlGenerator = $urlGenerator; $this->subAdminFactory = $subAdminFactory; @@ -263,7 +264,7 @@ class UsersController extends Controller { */ public function create($username, $password, array $groups=array(), $email='') { - if($email !== '' && !$this->mail->validateAddress($email)) { + if($email !== '' && !\OCP\Mail\Util::validateMailAddress($email)) { return new DataResponse( array( 'message' => (string)$this->l10n->t('Invalid mail address') @@ -329,15 +330,13 @@ class UsersController extends Controller { $subject = $this->l10n->t('Your %s account was created', [$this->defaults->getName()]); try { - $this->mail->send( - $email, - $username, - $subject, - $mailContent, - $this->fromMailAddress, - $this->defaults->getName(), - 1, - $plainTextMailContent); + $message = $this->mailer->createMessage(); + $message->setTo([$email => $username]); + $message->setSubject($subject); + $message->setHtmlBody($mailContent); + $message->setPlainBody($plainTextMailContent); + $message->setFrom([$this->fromMailAddress => $this->defaults->getName()]); + $this->mailer->send($message); } catch(\Exception $e) { $this->log->error("Can't send new user mail to $email: " . $e->getMessage(), array('app' => 'settings')); } @@ -444,7 +443,7 @@ class UsersController extends Controller { ); } - if($mailAddress !== '' && !$this->mail->validateAddress($mailAddress)) { + if($mailAddress !== '' && ! \OCP\Mail\Util::validateMailAddress($mailAddress)) { return new DataResponse( array( 'status' => 'error', diff --git a/tests/lib/mail.php b/tests/lib/mail.php deleted file mode 100644 index 813dde1944f..00000000000 --- a/tests/lib/mail.php +++ /dev/null @@ -1,53 +0,0 @@ - - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - -class Test_Mail extends \Test\TestCase { - - /** - * @dataProvider buildAsciiEmailProvider - * @param $expected - * @param $address - */ - public function testBuildAsciiEmail($expected, $address) { - if (!function_exists('idn_to_ascii')) { - $this->markTestSkipped( - 'The intl extension is not available.' - ); - } - - $actual = \OC_Mail::buildAsciiEmail($address); - $this->assertEquals($expected, $actual); - } - - public function buildAsciiEmailProvider() { - return array( - array('info@example.com', 'info@example.com'), - array('info@xn--cjr6vy5ejyai80u.com', 'info@國際化域名.com'), - array('info@xn--mller-kva.de', 'info@müller.de'), - array('info@xn--mller-kva.xn--mller-kva.de', 'info@müller.müller.de'), - ); - } - - public function validateMailProvider() { - return array( - array('infoatexample.com', false), - array('info', false), - ); - } - - /** - * @dataProvider validateMailProvider - * @param $address - * @param $expected - */ - public function testValidateEmail($address, $expected) { - $actual = \OC_Mail::validateAddress($address); - $this->assertEquals($expected, $actual); - } - -} diff --git a/tests/lib/mail/mailer.php b/tests/lib/mail/mailer.php new file mode 100644 index 00000000000..bd410dd3383 --- /dev/null +++ b/tests/lib/mail/mailer.php @@ -0,0 +1,118 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test; +use OC\Mail\Mailer; +use OCP\IConfig; +use OC_Defaults; + +class MailerTest extends TestCase { + /** @var IConfig */ + private $config; + /** @var OC_Defaults */ + private $defaults; + /** @var Mailer */ + private $mailer; + + function setUp() { + parent::setUp(); + + $this->config = $this->getMockBuilder('\OCP\IConfig') + ->disableOriginalConstructor()->getMock(); + $this->defaults = $this->getMockBuilder('\OC_Defaults') + ->disableOriginalConstructor()->getMock(); + $this->mailer = new Mailer($this->config, $this->defaults); + } + + public function testGetMailInstance() { + $this->assertEquals(\Swift_MailTransport::newInstance(), \Test_Helper::invokePrivate($this->mailer, 'getMailinstance')); + } + + public function testGetSendMailInstanceSendMail() { + $this->config + ->expects($this->once()) + ->method('getSystemValue') + ->with('mail_smtpmode', 'sendmail') + ->will($this->returnValue('sendmail')); + + $this->assertEquals(\Swift_SendmailTransport::newInstance('/usr/sbin/sendmail -bs'), \Test_Helper::invokePrivate($this->mailer, 'getSendMailInstance')); + } + + public function testGetSendMailInstanceSendMailQmail() { + $this->config + ->expects($this->once()) + ->method('getSystemValue') + ->with('mail_smtpmode', 'sendmail') + ->will($this->returnValue('qmail')); + + $this->assertEquals(\Swift_SendmailTransport::newInstance('/var/qmail/bin/sendmail -bs'), \Test_Helper::invokePrivate($this->mailer, 'getSendMailInstance')); + } + + public function testGetSmtpInstanceDefaults() { + $expected = \Swift_SmtpTransport::newInstance(); + $expected->setHost('127.0.0.1'); + $expected->setTimeout(10); + $expected->setPort(25); + + $this->config + ->expects($this->any()) + ->method('getSystemValue') + ->will($this->returnArgument(1)); + + $this->assertEquals($expected, \Test_Helper::invokePrivate($this->mailer, 'getSmtpInstance')); + } + + public function testGetInstanceDefault() { + $this->assertInstanceOf('\Swift_MailTransport', \Test_Helper::invokePrivate($this->mailer, 'getInstance')); + } + + public function testGetInstancePhp() { + $this->config + ->expects($this->any()) + ->method('getSystemValue') + ->will($this->returnValue('php')); + + $this->assertInstanceOf('\Swift_MailTransport', \Test_Helper::invokePrivate($this->mailer, 'getInstance')); + } + + public function testGetInstanceSmtp() { + $this->config + ->expects($this->any()) + ->method('getSystemValue') + ->will($this->returnValue('smtp')); + + $this->assertInstanceOf('\Swift_SmtpTransport', \Test_Helper::invokePrivate($this->mailer, 'getInstance')); + } + + public function testGetInstanceSendmail() { + $this->config + ->expects($this->any()) + ->method('getSystemValue') + ->will($this->returnValue('sendmail')); + + $this->assertInstanceOf('\Swift_SendmailTransport', \Test_Helper::invokePrivate($this->mailer, 'getInstance')); + } + + public function testCreateMessage() { + $this->assertInstanceOf('\OC\Mail\Message', $this->mailer->createMessage()); + } + + /** + * @expectedException \Exception + */ + public function testSendInvalidMailException() { + $message = $this->getMockBuilder('\OC\Mail\Message') + ->disableOriginalConstructor()->getMock(); + $message->expects($this->once()) + ->method('getSwiftMessage') + ->will($this->returnValue(new \Swift_Message())); + + $this->mailer->send($message); + } + +} diff --git a/tests/lib/mail/message.php b/tests/lib/mail/message.php new file mode 100644 index 00000000000..0db2017d81e --- /dev/null +++ b/tests/lib/mail/message.php @@ -0,0 +1,161 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test; + +use OC\Mail\Message; +use Swift_Message; + +class MessageTest extends TestCase { + /** @var Swift_Message */ + private $swiftMessage; + /** @var Message */ + private $message; + + /** + * @return array + */ + public function mailAddressProvider() { + return array( + array(array('lukas@owncloud.com' => 'Lukas Reschke'), array('lukas@owncloud.com' => 'Lukas Reschke')), + array(array('lukas@owncloud.com' => 'Lukas Reschke', 'lukas@öwnclöüd.com', 'lukäs@owncloud.örg' => 'Lükäs Réschke'), + array('lukas@owncloud.com' => 'Lukas Reschke', 'lukas@xn--wncld-iuae2c.com', 'lukäs@owncloud.xn--rg-eka' => 'Lükäs Réschke')), + array(array('lukas@öwnclöüd.com'), array('lukas@xn--wncld-iuae2c.com')) + ); + } + + function setUp() { + parent::setUp(); + + $this->swiftMessage = $this->getMockBuilder('\Swift_Message') + ->disableOriginalConstructor()->getMock(); + + $this->message = new Message($this->swiftMessage); + } + + /** + * @dataProvider mailAddressProvider + */ + public function testConvertAddresses($unconverted, $expected) { + $this->assertSame($expected, \Test_Helper::invokePrivate($this->message, 'convertAddresses', array($unconverted))); + } + + public function testSetFrom() { + $this->swiftMessage + ->expects($this->once()) + ->method('setFrom') + ->with(array('lukas@owncloud.com')); + $this->message->setFrom(array('lukas@owncloud.com')); + } + + public function testGetFrom() { + $this->swiftMessage + ->expects($this->once()) + ->method('getFrom') + ->will($this->returnValue(array('lukas@owncloud.com'))); + + $this->assertSame(array('lukas@owncloud.com'), $this->message->getFrom()); + } + + public function testSetTo() { + $this->swiftMessage + ->expects($this->once()) + ->method('setTo') + ->with(array('lukas@owncloud.com')); + $this->message->setTo(array('lukas@owncloud.com')); + } + + public function testGetTo() { + $this->swiftMessage + ->expects($this->once()) + ->method('getTo') + ->will($this->returnValue(array('lukas@owncloud.com'))); + + $this->assertSame(array('lukas@owncloud.com'), $this->message->getTo()); + } + + public function testSetCc() { + $this->swiftMessage + ->expects($this->once()) + ->method('setCc') + ->with(array('lukas@owncloud.com')); + $this->message->setCc(array('lukas@owncloud.com')); + } + + public function testGetCc() { + $this->swiftMessage + ->expects($this->once()) + ->method('getCc') + ->will($this->returnValue(array('lukas@owncloud.com'))); + + $this->assertSame(array('lukas@owncloud.com'), $this->message->getCc()); + } + + public function testSetBcc() { + $this->swiftMessage + ->expects($this->once()) + ->method('setBcc') + ->with(array('lukas@owncloud.com')); + $this->message->setBcc(array('lukas@owncloud.com')); + } + + public function testGetBcc() { + $this->swiftMessage + ->expects($this->once()) + ->method('getBcc') + ->will($this->returnValue(array('lukas@owncloud.com'))); + + $this->assertSame(array('lukas@owncloud.com'), $this->message->getBcc()); + } + + public function testSetSubject() { + $this->swiftMessage + ->expects($this->once()) + ->method('setSubject') + ->with('Fancy Subject'); + + $this->message->setSubject('Fancy Subject'); + } + + public function testGetSubject() { + $this->swiftMessage + ->expects($this->once()) + ->method('getSubject') + ->will($this->returnValue('Fancy Subject')); + + $this->assertSame('Fancy Subject', $this->message->getSubject()); + } + + public function testSetPlainBody() { + $this->swiftMessage + ->expects($this->once()) + ->method('setBody') + ->with('Fancy Body'); + + $this->message->setPlainBody('Fancy Body'); + } + + public function testGetPlainBody() { + $this->swiftMessage + ->expects($this->once()) + ->method('getBody') + ->will($this->returnValue('Fancy Body')); + + $this->assertSame('Fancy Body', $this->message->getPlainBody()); + } + + public function testSetHtmlBody() { + $this->swiftMessage + ->expects($this->once()) + ->method('addPart') + ->with('Fancy Body', 'text/html'); + + $this->message->setHtmlBody('Fancy Body'); + } + +} diff --git a/tests/lib/mail/util.php b/tests/lib/mail/util.php new file mode 100644 index 00000000000..04d9d5df256 --- /dev/null +++ b/tests/lib/mail/util.php @@ -0,0 +1,39 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test; +use OCP\Mail\Util; + +/** + * Class Util + * + * @package OC\Mail + */ +class UtilTest extends TestCase { + + /** + * @return array + */ + public function mailAddressProvider() { + return array( + array('lukas@owncloud.com', true), + array('lukas@localhost', true), + array('lukas@192.168.1.1', true), + array('lukas@éxämplè.com', true), + array('asdf', false), + array('lukas@owncloud.org@owncloud.com', false) + ); + } + + /** + * @dataProvider mailAddressProvider + */ + public function testValidateMailAddress($email, $expected) { + $this->assertSame($expected, Util::validateMailAddress($email)); + } +} diff --git a/tests/settings/controller/mailsettingscontrollertest.php b/tests/settings/controller/mailsettingscontrollertest.php index ed33d7fbe49..84432f656bf 100644 --- a/tests/settings/controller/mailsettingscontrollertest.php +++ b/tests/settings/controller/mailsettingscontrollertest.php @@ -30,7 +30,7 @@ class MailSettingsControllerTest extends \Test\TestCase { $this->container['AppName'] = 'settings'; $this->container['UserSession'] = $this->getMockBuilder('\OC\User\Session') ->disableOriginalConstructor()->getMock(); - $this->container['Mail'] = $this->getMockBuilder('\OC_Mail') + $this->container['MailMessage'] = $this->getMockBuilder('\OCP\Mail\IMessage') ->disableOriginalConstructor()->getMock(); $this->container['Defaults'] = $this->getMockBuilder('\OC_Defaults') ->disableOriginalConstructor()->getMock(); @@ -152,12 +152,6 @@ class MailSettingsControllerTest extends \Test\TestCase { } public function testSendTestMail() { - /** - * FIXME: Disabled due to missing DI on mail class. - * TODO: Re-enable when https://github.com/owncloud/core/pull/12085 is merged. - */ - $this->markTestSkipped('Disable test until OC_Mail is rewritten.'); - $user = $this->getMockBuilder('\OC\User\User') ->disableOriginalConstructor() ->getMock(); diff --git a/tests/settings/controller/userscontrollertest.php b/tests/settings/controller/userscontrollertest.php index b813da038a3..3f69d1e7d18 100644 --- a/tests/settings/controller/userscontrollertest.php +++ b/tests/settings/controller/userscontrollertest.php @@ -45,7 +45,7 @@ class UsersControllerTest extends \Test\TestCase { })); $this->container['Defaults'] = $this->getMockBuilder('\OC_Defaults') ->disableOriginalConstructor()->getMock(); - $this->container['Mail'] = $this->getMockBuilder('\OC_Mail') + $this->container['Mailer'] = $this->getMockBuilder('\OCP\Mail\IMailer') ->disableOriginalConstructor()->getMock(); $this->container['DefaultMailAddress'] = 'no-reply@owncloud.com'; $this->container['Logger'] = $this->getMockBuilder('\OCP\ILogger') @@ -1151,24 +1151,12 @@ class UsersControllerTest extends \Test\TestCase { public function testCreateUnsuccessfulWithInvalidEmailAdmin() { $this->container['IsAdmin'] = true; - /** - * FIXME: Disabled due to missing DI on mail class. - * TODO: Re-enable when https://github.com/owncloud/core/pull/12085 is merged. - */ - $this->markTestSkipped('Disable test until OC_Mail is rewritten.'); - - $this->container['Mail'] - ->expects($this->once()) - ->method('validateAddress') - ->will($this->returnValue(false)); - - $expectedResponse = new DataResponse( - array( - 'message' => 'Invalid mail address' - ), + $expectedResponse = new DataResponse([ + 'message' => 'Invalid mail address', + ], Http::STATUS_UNPROCESSABLE_ENTITY ); - $response = $this->container['UsersController']->create('foo', 'password', array(), 'invalidMailAdress'); + $response = $this->container['UsersController']->create('foo', 'password', [], 'invalidMailAdress'); $this->assertEquals($expectedResponse, $response); } @@ -1177,35 +1165,79 @@ class UsersControllerTest extends \Test\TestCase { */ public function testCreateSuccessfulWithValidEmailAdmin() { $this->container['IsAdmin'] = true; + $message = $this->getMockBuilder('\OC\Mail\Message') + ->disableOriginalConstructor()->getMock(); + $message + ->expects($this->at(0)) + ->method('setTo') + ->with(['validMail@Adre.ss' => 'foo']); + $message + ->expects($this->at(1)) + ->method('setSubject') + ->with('Your account was created'); + $htmlBody = new Http\TemplateResponse( + 'settings', + 'email.new_user', + [ + 'username' => 'foo', + 'url' => '', + ], + 'blank' + ); + $message + ->expects($this->at(2)) + ->method('setHtmlBody') + ->with($htmlBody->render()); + $plainBody = new Http\TemplateResponse( + 'settings', + 'email.new_user_plain_text', + [ + 'username' => 'foo', + 'url' => '', + ], + 'blank' + ); + $message + ->expects($this->at(3)) + ->method('setPlainBody') + ->with($plainBody->render()); + $message + ->expects($this->at(4)) + ->method('setFrom') + ->with(['no-reply@owncloud.com' => null]); + + $this->container['Mailer'] + ->expects($this->at(0)) + ->method('createMessage') + ->will($this->returnValue($message)); + $this->container['Mailer'] + ->expects($this->at(1)) + ->method('send') + ->with($message); - /** - * FIXME: Disabled due to missing DI on mail class. - * TODO: Re-enable when https://github.com/owncloud/core/pull/12085 is merged. - */ - $this->markTestSkipped('Disable test until OC_Mail is rewritten.'); - - $this->container['Mail'] + $user = $this->getMockBuilder('\OC\User\User') + ->disableOriginalConstructor()->getMock(); + $user + ->method('getHome') + ->will($this->returnValue('/home/user')); + $user + ->method('getHome') + ->will($this->returnValue('/home/user')); + $user + ->method('getUID') + ->will($this->returnValue('foo')); + $user ->expects($this->once()) - ->method('validateAddress') - ->will($this->returnValue(true)); - $this->container['Mail'] + ->method('getBackendClassName') + ->will($this->returnValue('bar')); + + $this->container['UserManager'] ->expects($this->once()) - ->method('send') - ->with( - $this->equalTo('validMail@Adre.ss'), - $this->equalTo('foo'), - $this->anything(), - $this->anything(), - $this->anything(), - $this->equalTo('no-reply@owncloud.com'), - $this->equalTo(1), - $this->anything() - ); - $this->container['Logger'] - ->expects($this->never()) - ->method('error'); + ->method('createUser') + ->will($this->onConsecutiveCalls($user)); + - $response = $this->container['UsersController']->create('foo', 'password', array(), 'validMail@Adre.ss'); + $response = $this->container['UsersController']->create('foo', 'password', [], 'validMail@Adre.ss'); $this->assertEquals(Http::STATUS_CREATED, $response->getStatus()); }