Merge pull request #12085 from owncloud/add-swift-mailer

Migrate OC_Mail to SwiftMailer
This commit is contained in:
Lukas Reschke 2015-03-17 11:15:39 +01:00
commit b1c19f74a1
19 changed files with 1028 additions and 320 deletions

@ -1 +1 @@
Subproject commit 7c536152a16f62797b60c6c06f8c2e23eb8b755e
Subproject commit 08b820614ae99477d7c9319630f400d22b20e2fd

View File

@ -46,7 +46,8 @@ class Application extends App {
$c->query('Config'),
$c->query('SecureRandom'),
$c->query('DefaultEmailAddress'),
$c->query('IsEncryptionEnabled')
$c->query('IsEncryptionEnabled'),
$c->query('Mailer')
);
});
$container->registerService('UserController', function(SimpleContainer $c) {
@ -104,6 +105,9 @@ class Application extends App {
$container->registerService('Defaults', function() {
return new \OC_Defaults;
});
$container->registerService('Mailer', function(SimpleContainer $c) {
return $c->query('ServerContainer')->getMailer();
});
$container->registerService('DefaultEmailAddress', function() {
return Util::getDefaultEmailAddress('lostpassword-noreply');
});

View File

@ -15,6 +15,7 @@ use \OCP\IRequest;
use \OCP\IL10N;
use \OCP\IConfig;
use OCP\IUserManager;
use OCP\Mail\IMailer;
use OCP\Security\ISecureRandom;
use \OC_Defaults;
use OCP\Security\StringUtils;
@ -32,6 +33,7 @@ class LostController extends Controller {
protected $urlGenerator;
/** @var IUserManager */
protected $userManager;
// FIXME: Inject a non-static factory of OC_Defaults for better unit-testing
/** @var OC_Defaults */
protected $defaults;
/** @var IL10N */
@ -44,6 +46,8 @@ class LostController extends Controller {
protected $config;
/** @var ISecureRandom */
protected $secureRandom;
/** @var IMailer */
protected $mailer;
/**
* @param string $appName
@ -56,6 +60,7 @@ class LostController extends Controller {
* @param ISecureRandom $secureRandom
* @param string $from
* @param string $isDataEncrypted
* @param IMailer $mailer
*/
public function __construct($appName,
IRequest $request,
@ -66,7 +71,8 @@ class LostController extends Controller {
IConfig $config,
ISecureRandom $secureRandom,
$from,
$isDataEncrypted) {
$isDataEncrypted,
IMailer $mailer) {
parent::__construct($appName, $request);
$this->urlGenerator = $urlGenerator;
$this->userManager = $userManager;
@ -76,6 +82,7 @@ class LostController extends Controller {
$this->from = $from;
$this->isDataEncrypted = $isDataEncrypted;
$this->config = $config;
$this->mailer = $mailer;
}
/**
@ -200,15 +207,12 @@ class LostController extends Controller {
$msg = $tmpl->fetchPage();
try {
// FIXME: should be added to the container and injected in here
\OC_Mail::send(
$email,
$user,
$this->l10n->t('%s password reset', array($this->defaults->getName())),
$msg,
$this->from,
$this->defaults->getName()
);
$message = $this->mailer->createMessage();
$message->setTo([$email => $user]);
$message->setSubject($this->l10n->t('%s password reset', [$this->defaults->getName()]));
$message->setPlainBody($msg);
$message->setFrom([$this->from => $this->defaults->getName()]);
$this->mailer->send($message);
} catch (\Exception $e) {
throw new \Exception($this->l10n->t(
'Couldn\'t send reset email. Please contact your administrator.'

View File

@ -1,153 +0,0 @@
<?php
/**
* Copyright (c) 2012 Frank Karlitschek <frank@owncloud.org>
* 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";
}
}

192
lib/private/mail/mailer.php Normal file
View File

@ -0,0 +1,192 @@
<?php
/**
* Copyright (c) 2014-2015 Lukas Reschke <lukas@owncloud.com>
* 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;
use OCP\ILogger;
/**
* 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 ILogger */
private $logger;
/** @var \OC_Defaults */
private $defaults;
/**
* @param IConfig $config
* @param ILogger $logger
* @param \OC_Defaults $defaults
*/
function __construct(IConfig $config,
ILogger $logger,
\OC_Defaults $defaults) {
$this->config = $config;
$this->logger = $logger;
$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([\OCP\Util::getDefaultEmailAddress($this->defaults->getName())]);
}
$failedRecipients = [];
$this->getInstance()->send($message->getSwiftMessage(), $failedRecipients);
$logMessage = sprintf('Sent mail to "%s" with subject "%s"', print_r($message->getTo(), true), $message->getSubject());
$this->logger->debug($logMessage, ['app' => 'core']);
return $failedRecipients;
}
/**
* 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 function validateMailAddress($email) {
return \Swift_Validate::email($this->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 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;
}
/**
* 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);
}
$transport->start();
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();
}
}

View File

@ -0,0 +1,206 @@
<?php
/**
* Copyright (c) 2014 Lukas Reschke <lukas@owncloud.com>
* 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;
}
}

View File

@ -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,13 @@ class Server extends SimpleContainer implements IServerContainer {
$stream
);
});
$this->registerService('Mailer', function(Server $c) {
return new Mailer(
$c->getConfig(),
$c->getLogger(),
new \OC_Defaults()
);
});
}
/**
@ -712,6 +720,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
*

View File

@ -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();
}

View File

@ -0,0 +1,57 @@
<?php
/**
* Copyright (c) 2014 Lukas Reschke <lukas@owncloud.com>
* 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);
/**
* 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 function validateMailAddress($email);
}

View File

@ -63,12 +63,40 @@ 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([$toaddress => $toname]);
$message->setSubject($subject);
$message->setPlainBody($mailtext);
$message->setFrom([$fromaddress => $fromname]);
if($html === 1) {
$message->setHTMLBody($altbody);
}
if($altbody === '') {
$message->setHTMLBody($mailtext);
$message->setPlainBody('');
} else {
$message->setHtmlBody($mailtext);
$message->setPlainBody($altbody);
}
if(!empty($ccaddress)) {
if(!empty($ccname)) {
$message->setCc([$ccaddress => $ccname]);
} else {
$message->setCc([$ccaddress]);
}
}
if(!empty($bcc)) {
$message->setBcc([$bcc]);
}
$mailer->send($message);
}
/**
@ -275,7 +303,8 @@ class Util {
$host_name = \OC_Config::getValue('mail_domain', $host_name);
$defaultEmailAddress = $user_part.'@'.$host_name;
if (\OC_Mail::validateAddress($defaultEmailAddress)) {
$mailer = \OC::$server->getMailer();
if ($mailer->validateMailAddress($defaultEmailAddress)) {
return $defaultEmailAddress;
}

View File

@ -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;

View File

@ -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;
}
@ -133,19 +135,19 @@ class MailSettingsController extends Controller {
$email = $this->config->getUserValue($this->userSession->getUser()->getUID(), $this->appName, 'email', '');
if (!empty($email)) {
try {
$this->mail->send($email, $this->userSession->getUser()->getDisplayName(),
$this->l10n->t('test email settings'),
$this->l10n->t('If you received this email, the settings seem to be correct.'),
$this->defaultMailAddress,
$this->defaults->getName()
);
$message = $this->mailer->createMessage();
$message->setTo([$email => $this->userSession->getUser()->getDisplayName()]);
$message->setFrom([$this->defaultMailAddress]);
$message->setSubject($this->l10n->t('test email settings'));
$message->setPlainBody('If you received this email, the settings seem to be correct.');
$this->mailer->send($message);
} catch (\Exception $e) {
return array('data' =>
array('message' =>
(string) $this->l10n->t('A problem occurred while sending the email. Please revise your settings.'),
),
'status' => 'error'
);
return [
'data' => [
'message' => (string) $this->l10n->t('A problem occurred while sending the email. Please revise your settings. (Error: %s)', [$e->getMessage()]),
],
'status' => 'error',
];
}
return array('data' =>

View File

@ -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;
@ -262,8 +263,7 @@ class UsersController extends Controller {
* @return DataResponse
*/
public function create($username, $password, array $groups=array(), $email='') {
if($email !== '' && !$this->mail->validateAddress($email)) {
if($email !== '' && !$this->mailer->validateMailAddress($email)) {
return new DataResponse(
array(
'message' => (string)$this->l10n->t('Invalid mail address')
@ -329,15 +329,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 +442,7 @@ class UsersController extends Controller {
);
}
if($mailAddress !== '' && !$this->mail->validateAddress($mailAddress)) {
if($mailAddress !== '' && !$this->mailer->validateMailAddress($mailAddress)) {
return new DataResponse(
array(
'status' => 'error',

View File

@ -1,6 +1,6 @@
<?php
/**
* Copyright (c) 2014 Lukas Reschke <lukas@owncloud.com>
* Copyright (c) 2014-2015 Lukas Reschke <lukas@owncloud.com>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
@ -43,6 +43,8 @@ class LostControllerTest extends \PHPUnit_Framework_TestCase {
->disableOriginalConstructor()->getMock();
$this->container['URLGenerator'] = $this->getMockBuilder('\OCP\IURLGenerator')
->disableOriginalConstructor()->getMock();
$this->container['Mailer'] = $this->getMockBuilder('\OCP\Mail\IMailer')
->disableOriginalConstructor()->getMock();
$this->container['SecureRandom'] = $this->getMockBuilder('\OCP\Security\ISecureRandom')
->disableOriginalConstructor()->getMock();
$this->container['IsEncryptionEnabled'] = true;
@ -103,14 +105,6 @@ class LostControllerTest extends \PHPUnit_Framework_TestCase {
}
public function testEmailSuccessful() {
/**
* FIXME: Disable test for systems where no sendmail is available since code is static.
* @link https://github.com/owncloud/core/pull/12085
*/
if (is_null(\OC_Helper::findBinaryPath('sendmail'))) {
$this->markTestSkipped('sendmail is not available');
}
$randomToken = $this->container['SecureRandom'];
$this->container['SecureRandom']
->expects($this->once())
@ -140,12 +134,101 @@ class LostControllerTest extends \PHPUnit_Framework_TestCase {
->method('linkToRouteAbsolute')
->with('core.lost.resetform', array('userId' => 'ExistingUser', 'token' => 'ThisIsMaybeANotSoSecretToken!'))
->will($this->returnValue('https://ownCloud.com/index.php/lostpassword/'));
$message = $this->getMockBuilder('\OC\Mail\Message')
->disableOriginalConstructor()->getMock();
$message
->expects($this->at(0))
->method('setTo')
->with(['test@example.com' => 'ExistingUser']);
$message
->expects($this->at(1))
->method('setSubject')
->with(' password reset');
$message
->expects($this->at(2))
->method('setPlainBody')
->with('Use the following link to reset your password: https://ownCloud.com/index.php/lostpassword/');
$message
->expects($this->at(3))
->method('setFrom')
->with(['lostpassword-noreply@localhost' => 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);
$response = $this->lostController->email('ExistingUser');
$expectedResponse = array('status' => 'success');
$this->assertSame($expectedResponse, $response);
}
public function testEmailCantSendException() {
$randomToken = $this->container['SecureRandom'];
$this->container['SecureRandom']
->expects($this->once())
->method('generate')
->with('21')
->will($this->returnValue('ThisIsMaybeANotSoSecretToken!'));
$this->container['UserManager']
->expects($this->once())
->method('userExists')
->with('ExistingUser')
->will($this->returnValue(true));
$this->container['Config']
->expects($this->once())
->method('getUserValue')
->with('ExistingUser', 'settings', 'email')
->will($this->returnValue('test@example.com'));
$this->container['SecureRandom']
->expects($this->once())
->method('getMediumStrengthGenerator')
->will($this->returnValue($randomToken));
$this->container['Config']
->expects($this->once())
->method('setUserValue')
->with('ExistingUser', 'owncloud', 'lostpassword', 'ThisIsMaybeANotSoSecretToken!');
$this->container['URLGenerator']
->expects($this->once())
->method('linkToRouteAbsolute')
->with('core.lost.resetform', array('userId' => 'ExistingUser', 'token' => 'ThisIsMaybeANotSoSecretToken!'))
->will($this->returnValue('https://ownCloud.com/index.php/lostpassword/'));
$message = $this->getMockBuilder('\OC\Mail\Message')
->disableOriginalConstructor()->getMock();
$message
->expects($this->at(0))
->method('setTo')
->with(['test@example.com' => 'ExistingUser']);
$message
->expects($this->at(1))
->method('setSubject')
->with(' password reset');
$message
->expects($this->at(2))
->method('setPlainBody')
->with('Use the following link to reset your password: https://ownCloud.com/index.php/lostpassword/');
$message
->expects($this->at(3))
->method('setFrom')
->with(['lostpassword-noreply@localhost' => 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)
->will($this->throwException(new \Exception()));
$response = $this->lostController->email('ExistingUser');
$expectedResponse = ['status' => 'error', 'msg' => 'Couldn\'t send reset email. Please contact your administrator.'];
$this->assertSame($expectedResponse, $response);
}
public function testSetPasswordUnsuccessful() {
$this->container['Config']
->expects($this->once())

View File

@ -1,53 +0,0 @@
<?php
/**
* Copyright (c) 2014 Thomas Müller <deepdiver@owncloud.com>
* 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);
}
}

121
tests/lib/mail/mailer.php Normal file
View File

@ -0,0 +1,121 @@
<?php
/**
* Copyright (c) 2014-2015 Lukas Reschke <lukas@owncloud.com>
* 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;
use OCP\ILogger;
class MailerTest extends TestCase {
/** @var IConfig */
private $config;
/** @var OC_Defaults */
private $defaults;
/** @var ILogger */
private $logger;
/** @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->logger = $this->getMockBuilder('\OCP\ILogger')
->disableOriginalConstructor()->getMock();
$this->mailer = new Mailer($this->config, $this->logger, $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 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 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);
}
/**
* @return array
*/
public function mailAddressProvider() {
return [
['lukas@owncloud.com', true],
['lukas@localhost', true],
['lukas@192.168.1.1', true],
['lukas@éxämplè.com', true],
['asdf', false],
['lukas@owncloud.org@owncloud.com', false],
];
}
/**
* @dataProvider mailAddressProvider
*/
public function testValidateMailAddress($email, $expected) {
$this->assertSame($expected, $this->mailer->validateMailAddress($email));
}
}

161
tests/lib/mail/message.php Normal file
View File

@ -0,0 +1,161 @@
<?php
/**
* Copyright (c) 2014 Lukas Reschke <lukas@owncloud.com>
* 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('<blink>Fancy Body</blink>', 'text/html');
$this->message->setHtmlBody('<blink>Fancy Body</blink>');
}
}

View File

@ -30,7 +30,10 @@ 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['Mailer'] = $this->getMockBuilder('\OC\Mail\Mailer')
->setMethods(['send'])
->disableOriginalConstructor()->getMock();
$this->container['Defaults'] = $this->getMockBuilder('\OC_Defaults')
->disableOriginalConstructor()->getMock();
@ -152,12 +155,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();

View File

@ -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,84 @@ class UsersControllerTest extends \Test\TestCase {
*/
public function testCreateSuccessfulWithValidEmailAdmin() {
$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(true));
$this->container['Mail']
->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()
$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'
);
$this->container['Logger']
->expects($this->never())
->method('error');
$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]);
$response = $this->container['UsersController']->create('foo', 'password', array(), 'validMail@Adre.ss');
$this->container['Mailer']
->expects($this->at(0))
->method('validateMailAddress')
->with('validMail@Adre.ss')
->will($this->returnValue(true));
$this->container['Mailer']
->expects($this->at(1))
->method('createMessage')
->will($this->returnValue($message));
$this->container['Mailer']
->expects($this->at(2))
->method('send')
->with($message);
$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('getBackendClassName')
->will($this->returnValue('bar'));
$this->container['UserManager']
->expects($this->once())
->method('createUser')
->will($this->onConsecutiveCalls($user));
$response = $this->container['UsersController']->create('foo', 'password', [], 'validMail@Adre.ss');
$this->assertEquals(Http::STATUS_CREATED, $response->getStatus());
}