зеркало из https://github.com/nextcloud/server.git
Merge pull request #30349 from nextcloud/enhancement/migrate_to_symfony_mailer
Migrate to Symfony Mailer
This commit is contained in:
Коммит
bbd3e2b04a
2
3rdparty
2
3rdparty
|
@ -1 +1 @@
|
||||||
Subproject commit 1d53ed4d3282427854fca7ee6ecbb945304272db
|
Subproject commit b31aba0505a3daf84b016f52873794b618694dff
|
|
@ -172,9 +172,14 @@ class IMipPlugin extends SabreIMipPlugin {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$senderName = $iTipMessage->senderName ?: null;
|
|
||||||
$recipientName = $iTipMessage->recipientName ?: null;
|
$recipientName = $iTipMessage->recipientName ?: null;
|
||||||
|
|
||||||
|
/** @var Parameter|string|null $senderName */
|
||||||
|
$senderName = $iTipMessage->senderName ?: null;
|
||||||
|
if($senderName instanceof Parameter) {
|
||||||
|
$senderName = $senderName->getValue() ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
if ($senderName === null || empty(trim($senderName))) {
|
if ($senderName === null || empty(trim($senderName))) {
|
||||||
$senderName = $this->userManager->getDisplayName($this->userId);
|
$senderName = $this->userManager->getDisplayName($this->userId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,6 @@ class MailSettingsController extends Controller {
|
||||||
* @param string $mail_smtpmode
|
* @param string $mail_smtpmode
|
||||||
* @param string $mail_smtpsecure
|
* @param string $mail_smtpsecure
|
||||||
* @param string $mail_smtphost
|
* @param string $mail_smtphost
|
||||||
* @param string $mail_smtpauthtype
|
|
||||||
* @param int $mail_smtpauth
|
* @param int $mail_smtpauth
|
||||||
* @param string $mail_smtpport
|
* @param string $mail_smtpport
|
||||||
* @return DataResponse
|
* @return DataResponse
|
||||||
|
@ -95,7 +94,6 @@ class MailSettingsController extends Controller {
|
||||||
$mail_smtpmode,
|
$mail_smtpmode,
|
||||||
$mail_smtpsecure,
|
$mail_smtpsecure,
|
||||||
$mail_smtphost,
|
$mail_smtphost,
|
||||||
$mail_smtpauthtype,
|
|
||||||
$mail_smtpauth,
|
$mail_smtpauth,
|
||||||
$mail_smtpport,
|
$mail_smtpport,
|
||||||
$mail_sendmailmode) {
|
$mail_sendmailmode) {
|
||||||
|
|
|
@ -61,7 +61,6 @@ class Mail implements IDelegatedSettings {
|
||||||
'mail_smtpsecure' => $this->config->getSystemValue('mail_smtpsecure', ''),
|
'mail_smtpsecure' => $this->config->getSystemValue('mail_smtpsecure', ''),
|
||||||
'mail_smtphost' => $this->config->getSystemValue('mail_smtphost', ''),
|
'mail_smtphost' => $this->config->getSystemValue('mail_smtphost', ''),
|
||||||
'mail_smtpport' => $this->config->getSystemValue('mail_smtpport', ''),
|
'mail_smtpport' => $this->config->getSystemValue('mail_smtpport', ''),
|
||||||
'mail_smtpauthtype' => $this->config->getSystemValue('mail_smtpauthtype', ''),
|
|
||||||
'mail_smtpauth' => $this->config->getSystemValue('mail_smtpauth', false),
|
'mail_smtpauth' => $this->config->getSystemValue('mail_smtpauth', false),
|
||||||
'mail_smtpname' => $this->config->getSystemValue('mail_smtpname', ''),
|
'mail_smtpname' => $this->config->getSystemValue('mail_smtpname', ''),
|
||||||
'mail_smtppassword' => $this->config->getSystemValue('mail_smtppassword', ''),
|
'mail_smtppassword' => $this->config->getSystemValue('mail_smtppassword', ''),
|
||||||
|
|
|
@ -24,17 +24,9 @@
|
||||||
/** @var \OCP\IL10N $l */
|
/** @var \OCP\IL10N $l */
|
||||||
/** @var array $_ */
|
/** @var array $_ */
|
||||||
|
|
||||||
$mail_smtpauthtype = [
|
|
||||||
'' => $l->t('None'),
|
|
||||||
'LOGIN' => $l->t('Login'),
|
|
||||||
'PLAIN' => $l->t('Plain'),
|
|
||||||
'NTLM' => $l->t('NT LAN Manager'),
|
|
||||||
];
|
|
||||||
|
|
||||||
$mail_smtpsecure = [
|
$mail_smtpsecure = [
|
||||||
'' => $l->t('None'),
|
'' => $l->t('None'),
|
||||||
'ssl' => $l->t('SSL/TLS'),
|
'ssl' => $l->t('SSL/TLS')
|
||||||
'tls' => $l->t('STARTTLS'),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
$mail_smtpmode = [
|
$mail_smtpmode = [
|
||||||
|
@ -112,26 +104,7 @@ $mail_sendmailmode = [
|
||||||
value="<?php p($_['mail_domain']) ?>" />
|
value="<?php p($_['mail_domain']) ?>" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p id="setting_smtpauth" <?php if ($_['mail_smtpmode'] !== 'smtp') {
|
<!--lo-->
|
||||||
print_unescaped(' class="hidden"');
|
|
||||||
} ?>>
|
|
||||||
<label for="mail_smtpauthtype"><?php p($l->t('Authentication method')); ?></label>
|
|
||||||
<select name="mail_smtpauthtype" id="mail_smtpauthtype">
|
|
||||||
<?php foreach ($mail_smtpauthtype as $authtype => $name):
|
|
||||||
$selected = '';
|
|
||||||
if ($authtype == $_['mail_smtpauthtype']):
|
|
||||||
$selected = 'selected="selected"';
|
|
||||||
endif; ?>
|
|
||||||
<option value="<?php p($authtype)?>" <?php p($selected) ?>><?php p($name) ?></option>
|
|
||||||
<?php endforeach;?>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<input type="checkbox" name="mail_smtpauth" id="mail_smtpauth" class="checkbox" value="1"
|
|
||||||
<?php if ($_['mail_smtpauth']) {
|
|
||||||
print_unescaped('checked="checked"');
|
|
||||||
} ?> />
|
|
||||||
<label for="mail_smtpauth"><?php p($l->t('Authentication required')); ?></label>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p id="setting_smtphost" <?php if ($_['mail_smtpmode'] !== 'smtp') {
|
<p id="setting_smtphost" <?php if ($_['mail_smtpmode'] !== 'smtp') {
|
||||||
print_unescaped(' class="hidden"');
|
print_unescaped(' class="hidden"');
|
||||||
|
@ -145,7 +118,7 @@ $mail_sendmailmode = [
|
||||||
</p>
|
</p>
|
||||||
</form>
|
</form>
|
||||||
<form class="mail_settings" id="mail_credentials_settings">
|
<form class="mail_settings" id="mail_credentials_settings">
|
||||||
<p id="mail_credentials" <?php if (!$_['mail_smtpauth'] || $_['mail_smtpmode'] !== 'smtp') {
|
<p id="mail_credentials" <?php if ($_['mail_smtpmode'] !== 'smtp') {
|
||||||
print_unescaped(' class="hidden"');
|
print_unescaped(' class="hidden"');
|
||||||
} ?>>
|
} ?>>
|
||||||
<label for="mail_smtpname"><?php p($l->t('Credentials')); ?></label>
|
<label for="mail_smtpname"><?php p($l->t('Credentials')); ?></label>
|
||||||
|
|
|
@ -91,7 +91,6 @@ class MailSettingsControllerTest extends \Test\TestCase {
|
||||||
'mail_smtpmode' => 'smtp',
|
'mail_smtpmode' => 'smtp',
|
||||||
'mail_smtpsecure' => 'ssl',
|
'mail_smtpsecure' => 'ssl',
|
||||||
'mail_smtphost' => 'mx.nextcloud.org',
|
'mail_smtphost' => 'mx.nextcloud.org',
|
||||||
'mail_smtpauthtype' => 'NTLM',
|
|
||||||
'mail_smtpauth' => 1,
|
'mail_smtpauth' => 1,
|
||||||
'mail_smtpport' => '25',
|
'mail_smtpport' => '25',
|
||||||
'mail_sendmailmode' => null,
|
'mail_sendmailmode' => null,
|
||||||
|
@ -102,7 +101,6 @@ class MailSettingsControllerTest extends \Test\TestCase {
|
||||||
'mail_smtpmode' => 'smtp',
|
'mail_smtpmode' => 'smtp',
|
||||||
'mail_smtpsecure' => 'ssl',
|
'mail_smtpsecure' => 'ssl',
|
||||||
'mail_smtphost' => 'mx.nextcloud.org',
|
'mail_smtphost' => 'mx.nextcloud.org',
|
||||||
'mail_smtpauthtype' => 'NTLM',
|
|
||||||
'mail_smtpauth' => null,
|
'mail_smtpauth' => null,
|
||||||
'mail_smtpport' => '25',
|
'mail_smtpport' => '25',
|
||||||
'mail_smtpname' => null,
|
'mail_smtpname' => null,
|
||||||
|
@ -118,7 +116,6 @@ class MailSettingsControllerTest extends \Test\TestCase {
|
||||||
'smtp',
|
'smtp',
|
||||||
'ssl',
|
'ssl',
|
||||||
'mx.nextcloud.org',
|
'mx.nextcloud.org',
|
||||||
'NTLM',
|
|
||||||
1,
|
1,
|
||||||
'25',
|
'25',
|
||||||
null
|
null
|
||||||
|
@ -132,7 +129,6 @@ class MailSettingsControllerTest extends \Test\TestCase {
|
||||||
'smtp',
|
'smtp',
|
||||||
'ssl',
|
'ssl',
|
||||||
'mx.nextcloud.org',
|
'mx.nextcloud.org',
|
||||||
'NTLM',
|
|
||||||
0,
|
0,
|
||||||
'25',
|
'25',
|
||||||
null
|
null
|
||||||
|
|
|
@ -64,7 +64,6 @@ class MailTest extends TestCase {
|
||||||
['mail_smtpsecure', '', true],
|
['mail_smtpsecure', '', true],
|
||||||
['mail_smtphost', '', 'smtp.nextcloud.com'],
|
['mail_smtphost', '', 'smtp.nextcloud.com'],
|
||||||
['mail_smtpport', '', 25],
|
['mail_smtpport', '', 25],
|
||||||
['mail_smtpauthtype', '', 'login'],
|
|
||||||
['mail_smtpauth', false, true],
|
['mail_smtpauth', false, true],
|
||||||
['mail_smtpname', '', 'smtp.sender.com'],
|
['mail_smtpname', '', 'smtp.sender.com'],
|
||||||
['mail_smtppassword', '', 'mypassword'],
|
['mail_smtppassword', '', 'mypassword'],
|
||||||
|
@ -82,7 +81,6 @@ class MailTest extends TestCase {
|
||||||
'mail_smtpsecure' => true,
|
'mail_smtpsecure' => true,
|
||||||
'mail_smtphost' => 'smtp.nextcloud.com',
|
'mail_smtphost' => 'smtp.nextcloud.com',
|
||||||
'mail_smtpport' => 25,
|
'mail_smtpport' => 25,
|
||||||
'mail_smtpauthtype' => 'login',
|
|
||||||
'mail_smtpauth' => true,
|
'mail_smtpauth' => true,
|
||||||
'mail_smtpname' => 'smtp.sender.com',
|
'mail_smtpname' => 'smtp.sender.com',
|
||||||
'mail_smtppassword' => '********',
|
'mail_smtppassword' => '********',
|
||||||
|
|
|
@ -463,14 +463,17 @@ $CONFIG = [
|
||||||
'mail_smtptimeout' => 10,
|
'mail_smtptimeout' => 10,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This depends on ``mail_smtpmode``. Specify when you are using ``ssl`` for SSL/TLS or
|
* This depends on ``mail_smtpmode``. Specify ``ssl`` when you are using SSL/TLS. Any other value will be ignored.
|
||||||
* ``tls`` for STARTTLS, or leave empty for no encryption.
|
*
|
||||||
|
* If the server advertises STARTTLS capabilities, they might be used, but they cannot be enforced by
|
||||||
|
* this config option.
|
||||||
*
|
*
|
||||||
* Defaults to ``''`` (empty string)
|
* Defaults to ``''`` (empty string)
|
||||||
*/
|
*/
|
||||||
'mail_smtpsecure' => '',
|
'mail_smtpsecure' => '',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
*
|
||||||
* This depends on ``mail_smtpmode``. Change this to ``true`` if your mail
|
* This depends on ``mail_smtpmode``. Change this to ``true`` if your mail
|
||||||
* server requires authentication.
|
* server requires authentication.
|
||||||
*
|
*
|
||||||
|
@ -478,14 +481,6 @@ $CONFIG = [
|
||||||
*/
|
*/
|
||||||
'mail_smtpauth' => false,
|
'mail_smtpauth' => false,
|
||||||
|
|
||||||
/**
|
|
||||||
* This depends on ``mail_smtpmode``. If SMTP authentication is required, choose
|
|
||||||
* the authentication type as ``LOGIN`` or ``PLAIN``.
|
|
||||||
*
|
|
||||||
* Defaults to ``LOGIN``
|
|
||||||
*/
|
|
||||||
'mail_smtpauthtype' => 'LOGIN',
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This depends on ``mail_smtpauth``. Specify the username for authenticating to
|
* This depends on ``mail_smtpauth``. Specify the username for authenticating to
|
||||||
* the SMTP server.
|
* the SMTP server.
|
||||||
|
@ -1190,14 +1185,14 @@ $CONFIG = [
|
||||||
'preview_office_cl_parameters' =>
|
'preview_office_cl_parameters' =>
|
||||||
' --headless --nologo --nofirststartwizard --invisible --norestore '.
|
' --headless --nologo --nofirststartwizard --invisible --norestore '.
|
||||||
'--convert-to png --outdir ',
|
'--convert-to png --outdir ',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* custom path for ffmpeg binary
|
* custom path for ffmpeg binary
|
||||||
*
|
*
|
||||||
* Defaults to ``null`` and falls back to searching ``avconv`` and ``ffmpeg`` in the configured ``PATH`` environment
|
* Defaults to ``null`` and falls back to searching ``avconv`` and ``ffmpeg`` in the configured ``PATH`` environment
|
||||||
*/
|
*/
|
||||||
'preview_ffmpeg_path' => '/usr/bin/ffmpeg',
|
'preview_ffmpeg_path' => '/usr/bin/ffmpeg',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the URL of the Imaginary service to send image previews to.
|
* Set the URL of the Imaginary service to send image previews to.
|
||||||
* Also requires the ``OC\Preview\Imaginary`` provider to be enabled.
|
* Also requires the ``OC\Preview\Imaginary`` provider to be enabled.
|
||||||
|
|
|
@ -27,6 +27,7 @@ declare(strict_types=1);
|
||||||
namespace OC\Mail;
|
namespace OC\Mail;
|
||||||
|
|
||||||
use OCP\Mail\IAttachment;
|
use OCP\Mail\IAttachment;
|
||||||
|
use Symfony\Component\Mime\Email;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class Attachment
|
* Class Attachment
|
||||||
|
@ -35,11 +36,21 @@ use OCP\Mail\IAttachment;
|
||||||
* @since 13.0.0
|
* @since 13.0.0
|
||||||
*/
|
*/
|
||||||
class Attachment implements IAttachment {
|
class Attachment implements IAttachment {
|
||||||
/** @var \Swift_Mime_Attachment */
|
private ?string $body;
|
||||||
protected $swiftAttachment;
|
private ?string $name;
|
||||||
|
private ?string $contentType;
|
||||||
|
private ?string $path;
|
||||||
|
|
||||||
public function __construct(\Swift_Mime_Attachment $attachment) {
|
public function __construct(
|
||||||
$this->swiftAttachment = $attachment;
|
?string $body,
|
||||||
|
?string $name,
|
||||||
|
?string $contentType,
|
||||||
|
?string $path = null
|
||||||
|
) {
|
||||||
|
$this->body = $body;
|
||||||
|
$this->name = $name;
|
||||||
|
$this->contentType = $contentType;
|
||||||
|
$this->path = $path;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -48,7 +59,7 @@ class Attachment implements IAttachment {
|
||||||
* @since 13.0.0
|
* @since 13.0.0
|
||||||
*/
|
*/
|
||||||
public function setFilename(string $filename): IAttachment {
|
public function setFilename(string $filename): IAttachment {
|
||||||
$this->swiftAttachment->setFilename($filename);
|
$this->name = $filename;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +69,7 @@ class Attachment implements IAttachment {
|
||||||
* @since 13.0.0
|
* @since 13.0.0
|
||||||
*/
|
*/
|
||||||
public function setContentType(string $contentType): IAttachment {
|
public function setContentType(string $contentType): IAttachment {
|
||||||
$this->swiftAttachment->setContentType($contentType);
|
$this->contentType = $contentType;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,14 +79,15 @@ class Attachment implements IAttachment {
|
||||||
* @since 13.0.0
|
* @since 13.0.0
|
||||||
*/
|
*/
|
||||||
public function setBody(string $body): IAttachment {
|
public function setBody(string $body): IAttachment {
|
||||||
$this->swiftAttachment->setBody($body);
|
$this->body = $body;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function attach(Email $symfonyEmail): void {
|
||||||
* @return \Swift_Mime_Attachment
|
if ($this->path !== null) {
|
||||||
*/
|
$symfonyEmail->attachFromPath($this->path, $this->name, $this->contentType);
|
||||||
public function getSwiftAttachment(): \Swift_Mime_Attachment {
|
} else {
|
||||||
return $this->swiftAttachment;
|
$symfonyEmail->attach($this->body, $this->name, $this->contentType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,15 @@ use OCP\Mail\IEMailTemplate;
|
||||||
use OCP\Mail\IMailer;
|
use OCP\Mail\IMailer;
|
||||||
use OCP\Mail\IMessage;
|
use OCP\Mail\IMessage;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
|
||||||
|
use Symfony\Component\Mailer\Mailer as SymfonyMailer;
|
||||||
|
use Symfony\Component\Mailer\MailerInterface;
|
||||||
|
use Symfony\Component\Mailer\Transport\SendmailTransport;
|
||||||
|
use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport;
|
||||||
|
use Symfony\Component\Mailer\Transport\Smtp\Stream\SocketStream;
|
||||||
|
use Symfony\Component\Mime\Email;
|
||||||
|
use Symfony\Component\Mime\Exception\InvalidArgumentException;
|
||||||
|
use Symfony\Component\Mime\Exception\RfcComplianceException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class Mailer provides some basic functions to create a mail message that can be used in combination with
|
* Class Mailer provides some basic functions to create a mail message that can be used in combination with
|
||||||
|
@ -70,12 +79,10 @@ use Psr\Log\LoggerInterface;
|
||||||
* @package OC\Mail
|
* @package OC\Mail
|
||||||
*/
|
*/
|
||||||
class Mailer implements IMailer {
|
class Mailer implements IMailer {
|
||||||
/** @var \Swift_Mailer Cached mailer */
|
private ?MailerInterface $instance = null;
|
||||||
private $instance = null;
|
|
||||||
private IConfig $config;
|
private IConfig $config;
|
||||||
private LoggerInterface $logger;
|
private LoggerInterface $logger;
|
||||||
/** @var Defaults */
|
private Defaults $defaults;
|
||||||
private $defaults;
|
|
||||||
private IURLGenerator $urlGenerator;
|
private IURLGenerator $urlGenerator;
|
||||||
private IL10N $l10n;
|
private IL10N $l10n;
|
||||||
private IEventDispatcher $dispatcher;
|
private IEventDispatcher $dispatcher;
|
||||||
|
@ -100,11 +107,11 @@ class Mailer implements IMailer {
|
||||||
/**
|
/**
|
||||||
* Creates a new message object that can be passed to send()
|
* Creates a new message object that can be passed to send()
|
||||||
*
|
*
|
||||||
* @return IMessage
|
* @return Message
|
||||||
*/
|
*/
|
||||||
public function createMessage(): IMessage {
|
public function createMessage(): Message {
|
||||||
$plainTextOnly = $this->config->getSystemValue('mail_send_plaintext_only', false);
|
$plainTextOnly = $this->config->getSystemValue('mail_send_plaintext_only', false);
|
||||||
return new Message(new \Swift_Message(), $plainTextOnly);
|
return new Message(new Email(), $plainTextOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -115,7 +122,7 @@ class Mailer implements IMailer {
|
||||||
* @since 13.0.0
|
* @since 13.0.0
|
||||||
*/
|
*/
|
||||||
public function createAttachment($data = null, $filename = null, $contentType = null): IAttachment {
|
public function createAttachment($data = null, $filename = null, $contentType = null): IAttachment {
|
||||||
return new Attachment(new \Swift_Attachment($data, $filename, $contentType));
|
return new Attachment($data, $filename, $contentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -125,7 +132,7 @@ class Mailer implements IMailer {
|
||||||
* @since 13.0.0
|
* @since 13.0.0
|
||||||
*/
|
*/
|
||||||
public function createAttachmentFromPath(string $path, $contentType = null): IAttachment {
|
public function createAttachmentFromPath(string $path, $contentType = null): IAttachment {
|
||||||
return new Attachment(\Swift_Attachment::fromPath($path, $contentType));
|
return new Attachment(null, null, $contentType, $path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -162,49 +169,82 @@ class Mailer implements IMailer {
|
||||||
* Send the specified message. Also sets the from address to the value defined in config.php
|
* Send the specified message. Also sets the from address to the value defined in config.php
|
||||||
* if no-one has been passed.
|
* if no-one has been passed.
|
||||||
*
|
*
|
||||||
* @param IMessage|Message $message Message to send
|
* If sending failed, the recipients that failed will be returned (to, cc and bcc).
|
||||||
* @return string[] Array with failed recipients. Be aware that this depends on the used mail backend and
|
* Will output additional debug info if 'mail_smtpdebug' => 'true' is set in config.php
|
||||||
* therefore should be considered
|
*
|
||||||
* @throws \Exception In case it was not possible to send the message. (for example if an invalid mail address
|
* @param IMessage $message Message to send
|
||||||
* has been supplied.)
|
* @return string[] $failedRecipients
|
||||||
*/
|
*/
|
||||||
public function send(IMessage $message): array {
|
public function send(IMessage $message): array {
|
||||||
$debugMode = $this->config->getSystemValue('mail_smtpdebug', false);
|
$debugMode = $this->config->getSystemValue('mail_smtpdebug', false);
|
||||||
|
|
||||||
|
if (!($message instanceof Message)) {
|
||||||
|
throw new InvalidArgumentException('Object not of type ' . Message::class);
|
||||||
|
}
|
||||||
|
|
||||||
if (empty($message->getFrom())) {
|
if (empty($message->getFrom())) {
|
||||||
$message->setFrom([\OCP\Util::getDefaultEmailAddress('no-reply') => $this->defaults->getName()]);
|
$message->setFrom([\OCP\Util::getDefaultEmailAddress('no-reply') => $this->defaults->getName()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$failedRecipients = [];
|
|
||||||
|
|
||||||
$mailer = $this->getInstance();
|
$mailer = $this->getInstance();
|
||||||
|
|
||||||
// Enable logger if debug mode is enabled
|
|
||||||
if ($debugMode) {
|
|
||||||
$mailLogger = new \Swift_Plugins_Loggers_ArrayLogger();
|
|
||||||
$mailer->registerPlugin(new \Swift_Plugins_LoggerPlugin($mailLogger));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$this->dispatcher->dispatchTyped(new BeforeMessageSent($message));
|
$this->dispatcher->dispatchTyped(new BeforeMessageSent($message));
|
||||||
|
|
||||||
$mailer->send($message->getSwiftMessage(), $failedRecipients);
|
try {
|
||||||
|
$message->setRecipients();
|
||||||
|
} catch (InvalidArgumentException|RfcComplianceException $e) {
|
||||||
|
$logMessage = sprintf(
|
||||||
|
'Could not send mail to "%s" with subject "%s" as validation for address failed',
|
||||||
|
print_r(array_merge($message->getTo(), $message->getCc(), $message->getBcc()), true),
|
||||||
|
$message->getSubject()
|
||||||
|
);
|
||||||
|
$this->logger->debug($logMessage, ['app' => 'core', 'exception' => $e]);
|
||||||
|
$recipients = array_merge($message->getTo(), $message->getCc(), $message->getBcc());
|
||||||
|
$failedRecipients = [];
|
||||||
|
|
||||||
|
array_walk($recipients, function ($value, $key) use (&$failedRecipients) {
|
||||||
|
if (is_numeric($key)) {
|
||||||
|
$failedRecipients[] = $value;
|
||||||
|
} else {
|
||||||
|
$failedRecipients[] = $key;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return $failedRecipients;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$mailer->send($message->getSymfonyEmail());
|
||||||
|
} catch (TransportExceptionInterface $e) {
|
||||||
|
$logMessage = sprintf('Sending mail to "%s" with subject "%s" failed', print_r($message->getTo(), true), $message->getSubject());
|
||||||
|
$this->logger->debug($logMessage, ['app' => 'core', 'exception' => $e]);
|
||||||
|
if ($debugMode) {
|
||||||
|
$this->logger->debug($e->getDebug(), ['app' => 'core']);
|
||||||
|
}
|
||||||
|
$recipients = array_merge($message->getTo(), $message->getCc(), $message->getBcc());
|
||||||
|
$failedRecipients = [];
|
||||||
|
|
||||||
|
array_walk($recipients, function ($value, $key) use (&$failedRecipients) {
|
||||||
|
if (is_numeric($key)) {
|
||||||
|
$failedRecipients[] = $value;
|
||||||
|
} else {
|
||||||
|
$failedRecipients[] = $key;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return $failedRecipients;
|
||||||
|
}
|
||||||
|
|
||||||
// Debugging logging
|
// Debugging logging
|
||||||
$logMessage = sprintf('Sent mail to "%s" with subject "%s"', print_r($message->getTo(), true), $message->getSubject());
|
$logMessage = sprintf('Sent mail to "%s" with subject "%s"', print_r($message->getTo(), true), $message->getSubject());
|
||||||
if (!empty($failedRecipients)) {
|
|
||||||
$logMessage .= sprintf(' (failed for "%s")', print_r($failedRecipients, true));
|
|
||||||
}
|
|
||||||
$this->logger->debug($logMessage, ['app' => 'core']);
|
$this->logger->debug($logMessage, ['app' => 'core']);
|
||||||
if ($debugMode && isset($mailLogger)) {
|
|
||||||
$this->logger->debug($mailLogger->dump(), ['app' => 'core']);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $failedRecipients;
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if an e-mail address is valid
|
* @deprecated 26.0.0 Implicit validation is done in \OC\Mail\Message::setRecipients
|
||||||
|
* via \Symfony\Component\Mime\Address::__construct
|
||||||
*
|
*
|
||||||
* @param string $email Email address to be validated
|
* @param string $email Email address to be validated
|
||||||
* @return bool True if the mail address is valid, false otherwise
|
* @return bool True if the mail address is valid, false otherwise
|
||||||
|
@ -217,28 +257,10 @@ class Mailer implements IMailer {
|
||||||
$validator = new EmailValidator();
|
$validator = new EmailValidator();
|
||||||
$validation = new RFCValidation();
|
$validation = new RFCValidation();
|
||||||
|
|
||||||
return $validator->isValid($this->convertEmail($email), $validation);
|
return $validator->isValid($email, $validation);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected function getInstance(): MailerInterface {
|
||||||
* 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(string $email): string {
|
|
||||||
if (!function_exists('idn_to_ascii') || !defined('INTL_IDNA_VARIANT_UTS46') || strpos($email, '@') === false) {
|
|
||||||
return $email;
|
|
||||||
}
|
|
||||||
|
|
||||||
[$name, $domain] = explode('@', $email, 2);
|
|
||||||
$domain = idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46);
|
|
||||||
return $name.'@'.$domain;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getInstance(): \Swift_Mailer {
|
|
||||||
if (!is_null($this->instance)) {
|
if (!is_null($this->instance)) {
|
||||||
return $this->instance;
|
return $this->instance;
|
||||||
}
|
}
|
||||||
|
@ -255,31 +277,47 @@ class Mailer implements IMailer {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new \Swift_Mailer($transport);
|
return new SymfonyMailer($transport);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the SMTP transport
|
* Returns the SMTP transport
|
||||||
*
|
*
|
||||||
* @return \Swift_SmtpTransport
|
* Only supports ssl/tls
|
||||||
|
* starttls is not enforcable with Symfony Mailer but might be available
|
||||||
|
* via the automatic config (Symfony Mailer internal)
|
||||||
|
*
|
||||||
|
* @return EsmtpTransport
|
||||||
*/
|
*/
|
||||||
protected function getSmtpInstance(): \Swift_SmtpTransport {
|
protected function getSmtpInstance(): EsmtpTransport {
|
||||||
$transport = new \Swift_SmtpTransport();
|
// either null or true - if nothing is passed, let the symfony mailer figure out the configuration by itself
|
||||||
$transport->setTimeout($this->config->getSystemValue('mail_smtptimeout', 10));
|
$mailSmtpsecure = ($this->config->getSystemValue('mail_smtpsecure', null) === 'ssl') ? true : null;
|
||||||
$transport->setHost($this->config->getSystemValue('mail_smtphost', '127.0.0.1'));
|
$transport = new EsmtpTransport(
|
||||||
$transport->setPort($this->config->getSystemValue('mail_smtpport', 25));
|
$this->config->getSystemValue('mail_smtphost', '127.0.0.1'),
|
||||||
|
(int)$this->config->getSystemValue('mail_smtpport', 25),
|
||||||
|
$mailSmtpsecure,
|
||||||
|
null,
|
||||||
|
$this->logger
|
||||||
|
);
|
||||||
|
/** @var SocketStream $stream */
|
||||||
|
$stream = $transport->getStream();
|
||||||
|
/** @psalm-suppress InternalMethod */
|
||||||
|
$stream->setTimeout($this->config->getSystemValue('mail_smtptimeout', 10));
|
||||||
|
|
||||||
if ($this->config->getSystemValue('mail_smtpauth', false)) {
|
if ($this->config->getSystemValue('mail_smtpauth', false)) {
|
||||||
$transport->setUsername($this->config->getSystemValue('mail_smtpname', ''));
|
$transport->setUsername($this->config->getSystemValue('mail_smtpname', ''));
|
||||||
$transport->setPassword($this->config->getSystemValue('mail_smtppassword', ''));
|
$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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$streamingOptions = $this->config->getSystemValue('mail_smtpstreamoptions', []);
|
$streamingOptions = $this->config->getSystemValue('mail_smtpstreamoptions', []);
|
||||||
if (is_array($streamingOptions) && !empty($streamingOptions)) {
|
if (is_array($streamingOptions) && !empty($streamingOptions)) {
|
||||||
$transport->setStreamOptions($streamingOptions);
|
/** @psalm-suppress InternalMethod */
|
||||||
|
$currentStreamingOptions = $stream->getStreamOptions();
|
||||||
|
|
||||||
|
$currentStreamingOptions = array_merge_recursive($currentStreamingOptions, $streamingOptions);
|
||||||
|
|
||||||
|
/** @psalm-suppress InternalMethod */
|
||||||
|
$stream->setStreamOptions($currentStreamingOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
$overwriteCliUrl = parse_url(
|
$overwriteCliUrl = parse_url(
|
||||||
|
@ -297,9 +335,9 @@ class Mailer implements IMailer {
|
||||||
/**
|
/**
|
||||||
* Returns the sendmail transport
|
* Returns the sendmail transport
|
||||||
*
|
*
|
||||||
* @return \Swift_SendmailTransport
|
* @return SendmailTransport
|
||||||
*/
|
*/
|
||||||
protected function getSendMailInstance(): \Swift_SendmailTransport {
|
protected function getSendMailInstance(): SendmailTransport {
|
||||||
switch ($this->config->getSystemValue('mail_smtpmode', 'smtp')) {
|
switch ($this->config->getSystemValue('mail_smtpmode', 'smtp')) {
|
||||||
case 'qmail':
|
case 'qmail':
|
||||||
$binaryPath = '/var/qmail/bin/sendmail';
|
$binaryPath = '/var/qmail/bin/sendmail';
|
||||||
|
@ -322,6 +360,6 @@ class Mailer implements IMailer {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new \Swift_SendmailTransport($binaryPath . $binaryParam);
|
return new SendmailTransport($binaryPath . $binaryParam, null, $this->logger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,65 +35,68 @@ use OCP\Mail\Headers\AutoSubmitted;
|
||||||
use OCP\Mail\IAttachment;
|
use OCP\Mail\IAttachment;
|
||||||
use OCP\Mail\IEMailTemplate;
|
use OCP\Mail\IEMailTemplate;
|
||||||
use OCP\Mail\IMessage;
|
use OCP\Mail\IMessage;
|
||||||
use Swift_Message;
|
use Symfony\Component\Mime\Address;
|
||||||
|
use Symfony\Component\Mime\Email;
|
||||||
|
use Symfony\Component\Mime\Exception\InvalidArgumentException;
|
||||||
|
use Symfony\Component\Mime\Exception\RfcComplianceException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class Message provides a wrapper around SwiftMail
|
* Class Message provides a wrapper around Symfony\Component\Mime\Email (Used to be around SwiftMail)
|
||||||
*
|
*
|
||||||
* @package OC\Mail
|
* @package OC\Mail
|
||||||
*/
|
*/
|
||||||
class Message implements IMessage {
|
class Message implements IMessage {
|
||||||
/** @var Swift_Message */
|
private Email $symfonyEmail;
|
||||||
private $swiftMessage;
|
private bool $plainTextOnly;
|
||||||
/** @var bool */
|
|
||||||
private $plainTextOnly;
|
|
||||||
|
|
||||||
public function __construct(Swift_Message $swiftMessage, bool $plainTextOnly) {
|
private array $to;
|
||||||
$this->swiftMessage = $swiftMessage;
|
private array $from;
|
||||||
|
private array $replyTo;
|
||||||
|
private array $cc;
|
||||||
|
private array $bcc;
|
||||||
|
|
||||||
|
public function __construct(Email $symfonyEmail, bool $plainTextOnly) {
|
||||||
|
$this->symfonyEmail = $symfonyEmail;
|
||||||
$this->plainTextOnly = $plainTextOnly;
|
$this->plainTextOnly = $plainTextOnly;
|
||||||
|
$this->to = [];
|
||||||
|
$this->from = [];
|
||||||
|
$this->replyTo = [];
|
||||||
|
$this->cc = [];
|
||||||
|
$this->bcc = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param IAttachment $attachment
|
|
||||||
* @return $this
|
* @return $this
|
||||||
* @since 13.0.0
|
* @since 13.0.0
|
||||||
*/
|
*/
|
||||||
public function attach(IAttachment $attachment): IMessage {
|
public function attach(IAttachment $attachment): IMessage {
|
||||||
/** @var Attachment $attachment */
|
/** @var Attachment $attachment */
|
||||||
$this->swiftMessage->attach($attachment->getSwiftAttachment());
|
$attachment->attach($this->symfonyEmail);
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SwiftMailer does currently not work with IDN domains, this function therefore converts the domains
|
* Converts the [['displayName' => 'email'], ['displayName2' => 'email2']] arrays to valid Adresses
|
||||||
* FIXME: Remove this once SwiftMailer supports IDN
|
|
||||||
*
|
*
|
||||||
* @param array $addresses Array of mail addresses, key will get converted
|
* @param array $addresses Array of mail addresses
|
||||||
* @return array Converted addresses if `idn_to_ascii` exists
|
* @return Address[]
|
||||||
|
* @throws RfcComplianceException|InvalidArgumentException
|
||||||
*/
|
*/
|
||||||
protected function convertAddresses(array $addresses): array {
|
protected function convertAddresses(array $addresses): array {
|
||||||
if (!function_exists('idn_to_ascii') || !defined('INTL_IDNA_VARIANT_UTS46')) {
|
|
||||||
return $addresses;
|
|
||||||
}
|
|
||||||
|
|
||||||
$convertedAddresses = [];
|
$convertedAddresses = [];
|
||||||
|
|
||||||
foreach ($addresses as $email => $readableName) {
|
if (empty($addresses)) {
|
||||||
$parsableEmail = is_numeric($email) ? $readableName : $email;
|
return [];
|
||||||
if (strpos($parsableEmail, '@') === false) {
|
|
||||||
$convertedAddresses[$parsableEmail] = $readableName;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
[$name, $domain] = explode('@', $parsableEmail, 2);
|
|
||||||
$domain = idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46);
|
|
||||||
if (is_numeric($email)) {
|
|
||||||
$convertedAddresses[] = $name . '@' . $domain;
|
|
||||||
} else {
|
|
||||||
$convertedAddresses[$name . '@' . $domain] = $readableName;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
array_walk($addresses, function ($readableName, $email) use (&$convertedAddresses) {
|
||||||
|
if (is_numeric($email)) {
|
||||||
|
$convertedAddresses[] = new Address($readableName);
|
||||||
|
} else {
|
||||||
|
$convertedAddresses[] = new Address($email, $readableName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return $convertedAddresses;
|
return $convertedAddresses;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,41 +109,32 @@ class Message implements IMessage {
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setFrom(array $addresses): IMessage {
|
public function setFrom(array $addresses): IMessage {
|
||||||
$addresses = $this->convertAddresses($addresses);
|
$this->from = $addresses;
|
||||||
|
|
||||||
$this->swiftMessage->setFrom($addresses);
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the from address of this message.
|
* Get the from address of this message.
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
*/
|
||||||
public function getFrom(): array {
|
public function getFrom(): array {
|
||||||
return $this->swiftMessage->getFrom() ?? [];
|
return $this->from;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the Reply-To address of this message
|
* Set the Reply-To address of this message
|
||||||
*
|
*
|
||||||
* @param array $addresses
|
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setReplyTo(array $addresses): IMessage {
|
public function setReplyTo(array $addresses): IMessage {
|
||||||
$addresses = $this->convertAddresses($addresses);
|
$this->replyTo = $addresses;
|
||||||
|
|
||||||
$this->swiftMessage->setReplyTo($addresses);
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the Reply-To address of this message
|
* Returns the Reply-To address of this message
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
*/
|
||||||
public function getReplyTo(): string {
|
public function getReplyTo(): array {
|
||||||
return $this->swiftMessage->getReplyTo();
|
return $this->replyTo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -150,19 +144,15 @@ class Message implements IMessage {
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setTo(array $recipients): IMessage {
|
public function setTo(array $recipients): IMessage {
|
||||||
$recipients = $this->convertAddresses($recipients);
|
$this->to = $recipients;
|
||||||
|
|
||||||
$this->swiftMessage->setTo($recipients);
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the to address of this message.
|
* Get the to address of this message.
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
*/
|
||||||
public function getTo(): array {
|
public function getTo(): array {
|
||||||
return $this->swiftMessage->getTo() ?? [];
|
return $this->to;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -172,19 +162,15 @@ class Message implements IMessage {
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setCc(array $recipients): IMessage {
|
public function setCc(array $recipients): IMessage {
|
||||||
$recipients = $this->convertAddresses($recipients);
|
$this->cc = $recipients;
|
||||||
|
|
||||||
$this->swiftMessage->setCc($recipients);
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the cc address of this message.
|
* Get the cc address of this message.
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
*/
|
||||||
public function getCc(): array {
|
public function getCc(): array {
|
||||||
return $this->swiftMessage->getCc() ?? [];
|
return $this->cc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -194,104 +180,119 @@ class Message implements IMessage {
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setBcc(array $recipients): IMessage {
|
public function setBcc(array $recipients): IMessage {
|
||||||
$recipients = $this->convertAddresses($recipients);
|
$this->bcc = $recipients;
|
||||||
|
|
||||||
$this->swiftMessage->setBcc($recipients);
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the Bcc address of this message.
|
* Get the Bcc address of this message.
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
*/
|
||||||
public function getBcc(): array {
|
public function getBcc(): array {
|
||||||
return $this->swiftMessage->getBcc() ?? [];
|
return $this->bcc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the subject of this message.
|
* Set the subject of this message.
|
||||||
*
|
*
|
||||||
* @param string $subject
|
* @return $this
|
||||||
* @return IMessage
|
|
||||||
*/
|
*/
|
||||||
public function setSubject(string $subject): IMessage {
|
public function setSubject(string $subject): IMessage {
|
||||||
$this->swiftMessage->setSubject($subject);
|
$this->symfonyEmail->subject($subject);
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the from subject of this message.
|
* Get the from subject of this message.
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
*/
|
||||||
public function getSubject(): string {
|
public function getSubject(): string {
|
||||||
return $this->swiftMessage->getSubject();
|
return $this->symfonyEmail->getSubject() ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the plain-text body of this message.
|
* Set the plain-text body of this message.
|
||||||
*
|
|
||||||
* @param string $body
|
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setPlainBody(string $body): IMessage {
|
public function setPlainBody(string $body): IMessage {
|
||||||
$this->swiftMessage->setBody($body);
|
$this->symfonyEmail->text($body);
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the plain body of this message.
|
* Get the plain body of this message.
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
*/
|
||||||
public function getPlainBody(): string {
|
public function getPlainBody(): string {
|
||||||
return $this->swiftMessage->getBody();
|
/** @var string $body */
|
||||||
|
$body = $this->symfonyEmail->getTextBody() ?? '';
|
||||||
|
return $body;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the HTML body of this message. Consider also sending a plain-text body instead of only an HTML one.
|
* 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
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setHtmlBody($body) {
|
public function setHtmlBody(string $body): IMessage {
|
||||||
if (!$this->plainTextOnly) {
|
if (!$this->plainTextOnly) {
|
||||||
$this->swiftMessage->addPart($body, 'text/html');
|
$this->symfonyEmail->html($body);
|
||||||
}
|
}
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get's the underlying SwiftMessage
|
* Set the underlying Email intance
|
||||||
* @param Swift_Message $swiftMessage
|
|
||||||
*/
|
*/
|
||||||
public function setSwiftMessage(Swift_Message $swiftMessage): void {
|
public function setSymfonyEmail(Email $symfonyEmail): void {
|
||||||
$this->swiftMessage = $swiftMessage;
|
$this->symfonyEmail = $symfonyEmail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get's the underlying SwiftMessage
|
* Get the underlying Email instance
|
||||||
* @return Swift_Message
|
|
||||||
*/
|
*/
|
||||||
public function getSwiftMessage(): Swift_Message {
|
public function getSymfonyEmail(): Email {
|
||||||
return $this->swiftMessage;
|
return $this->symfonyEmail;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $body
|
|
||||||
* @param string $contentType
|
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setBody($body, $contentType) {
|
public function setBody(string $body, string $contentType): IMessage {
|
||||||
if (!$this->plainTextOnly || $contentType !== 'text/html') {
|
if (!$this->plainTextOnly || $contentType !== 'text/html') {
|
||||||
$this->swiftMessage->setBody($body, $contentType);
|
if ($contentType === 'text/html') {
|
||||||
|
$this->symfonyEmail->html($body);
|
||||||
|
} else {
|
||||||
|
$this->symfonyEmail->text($body);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param IEMailTemplate $emailTemplate
|
* Set the recipients on the symphony email
|
||||||
|
*
|
||||||
|
* Since
|
||||||
|
*
|
||||||
|
* setTo
|
||||||
|
* setFrom
|
||||||
|
* setReplyTo
|
||||||
|
* setCc
|
||||||
|
* setBcc
|
||||||
|
*
|
||||||
|
* could throw a \Symfony\Component\Mime\Exception\RfcComplianceException
|
||||||
|
* or a \Symfony\Component\Mime\Exception\InvalidArgumentException
|
||||||
|
* we wrap the calls here. We then have the validation errors all in one place and can
|
||||||
|
* throw shortly before \OC\Mail\Mailer::send
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @throws InvalidArgumentException|RfcComplianceException
|
||||||
|
*/
|
||||||
|
public function setRecipients() {
|
||||||
|
$this->symfonyEmail->to(...$this->convertAddresses($this->getTo()));
|
||||||
|
$this->symfonyEmail->from(...$this->convertAddresses($this->getFrom()));
|
||||||
|
$this->symfonyEmail->replyTo(...$this->convertAddresses($this->getReplyTo()));
|
||||||
|
$this->symfonyEmail->cc(...$this->convertAddresses($this->getCc()));
|
||||||
|
$this->symfonyEmail->bcc(...$this->convertAddresses($this->getBcc()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function useTemplate(IEMailTemplate $emailTemplate): IMessage {
|
public function useTemplate(IEMailTemplate $emailTemplate): IMessage {
|
||||||
|
@ -311,7 +312,7 @@ class Message implements IMessage {
|
||||||
* @return $this
|
* @return $this
|
||||||
*/
|
*/
|
||||||
public function setAutoSubmitted(string $value): IMessage {
|
public function setAutoSubmitted(string $value): IMessage {
|
||||||
$headers = $this->swiftMessage->getHeaders();
|
$headers = $this->symfonyEmail->getHeaders();
|
||||||
|
|
||||||
if ($headers->has(AutoSubmitted::HEADER)) {
|
if ($headers->has(AutoSubmitted::HEADER)) {
|
||||||
// if the header already exsists, remove it.
|
// if the header already exsists, remove it.
|
||||||
|
@ -319,6 +320,7 @@ class Message implements IMessage {
|
||||||
// of the interface \Swift_Mime_Header, however the
|
// of the interface \Swift_Mime_Header, however the
|
||||||
// interface doesn't, and this makes the static-code
|
// interface doesn't, and this makes the static-code
|
||||||
// analysis unhappy.
|
// analysis unhappy.
|
||||||
|
// @todo check if symfony mailer can modify the autosubmitted header
|
||||||
$headers->remove(AutoSubmitted::HEADER);
|
$headers->remove(AutoSubmitted::HEADER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,9 +336,9 @@ class Message implements IMessage {
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getAutoSubmitted(): string {
|
public function getAutoSubmitted(): string {
|
||||||
$headers = $this->swiftMessage->getHeaders();
|
$headers = $this->symfonyEmail->getHeaders();
|
||||||
|
|
||||||
return $headers->has(AutoSubmitted::HEADER) ?
|
return $headers->has(AutoSubmitted::HEADER) ?
|
||||||
$headers->get(AutoSubmitted::HEADER)->toString() : AutoSubmitted::VALUE_NO;
|
$headers->get(AutoSubmitted::HEADER)->getBodyAsString() : AutoSubmitted::VALUE_NO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,8 +96,6 @@ interface IMailer {
|
||||||
public function send(IMessage $message): array;
|
public function send(IMessage $message): array;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if an e-mail address is valid
|
|
||||||
*
|
|
||||||
* @param string $email Email address to be validated
|
* @param string $email Email address to be validated
|
||||||
* @return bool True if the mail address is valid, false otherwise
|
* @return bool True if the mail address is valid, false otherwise
|
||||||
* @since 8.1.0
|
* @since 8.1.0
|
||||||
|
|
|
@ -22,19 +22,23 @@ use OCP\IURLGenerator;
|
||||||
use OCP\L10N\IFactory;
|
use OCP\L10N\IFactory;
|
||||||
use OCP\Mail\Events\BeforeMessageSent;
|
use OCP\Mail\Events\BeforeMessageSent;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Swift_SwiftException;
|
|
||||||
use Test\TestCase;
|
use Test\TestCase;
|
||||||
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
|
use Symfony\Component\Mailer\Mailer as SymfonyMailer;
|
||||||
|
use Symfony\Component\Mime\Email;
|
||||||
|
use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport;
|
||||||
|
use Symfony\Component\Mailer\Transport\SendmailTransport;
|
||||||
|
|
||||||
class MailerTest extends TestCase {
|
class MailerTest extends TestCase {
|
||||||
/** @var IConfig|\PHPUnit\Framework\MockObject\MockObject */
|
/** @var IConfig|MockObject */
|
||||||
private $config;
|
private $config;
|
||||||
/** @var Defaults|\PHPUnit\Framework\MockObject\MockObject */
|
/** @var Defaults|MockObject */
|
||||||
private $defaults;
|
private $defaults;
|
||||||
/** @var LoggerInterface|\PHPUnit\Framework\MockObject\MockObject */
|
/** @var LoggerInterface|MockObject */
|
||||||
private $logger;
|
private $logger;
|
||||||
/** @var IURLGenerator|\PHPUnit\Framework\MockObject\MockObject */
|
/** @var IURLGenerator|MockObject */
|
||||||
private $urlGenerator;
|
private $urlGenerator;
|
||||||
/** @var IL10N|\PHPUnit\Framework\MockObject\MockObject */
|
/** @var IL10N|MockObject */
|
||||||
private $l10n;
|
private $l10n;
|
||||||
/** @var Mailer */
|
/** @var Mailer */
|
||||||
private $mailer;
|
private $mailer;
|
||||||
|
@ -91,7 +95,7 @@ class MailerTest extends TestCase {
|
||||||
$path = '/usr/sbin/sendmail';
|
$path = '/usr/sbin/sendmail';
|
||||||
}
|
}
|
||||||
|
|
||||||
$expected = new \Swift_SendmailTransport($path . $binaryParam);
|
$expected = new SendmailTransport($path . $binaryParam, null, $this->logger);
|
||||||
$this->assertEquals($expected, self::invokePrivate($this->mailer, 'getSendMailInstance'));
|
$this->assertEquals($expected, self::invokePrivate($this->mailer, 'getSendMailInstance'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,13 +113,22 @@ class MailerTest extends TestCase {
|
||||||
['mail_sendmailmode', 'smtp', $sendmailMode],
|
['mail_sendmailmode', 'smtp', $sendmailMode],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertEquals(new \Swift_SendmailTransport('/var/qmail/bin/sendmail' . $binaryParam), self::invokePrivate($this->mailer, 'getSendMailInstance'));
|
$sendmail = new SendmailTransport('/var/qmail/bin/sendmail' . $binaryParam, null, $this->logger);
|
||||||
|
$this->assertEquals($sendmail, self::invokePrivate($this->mailer, 'getSendMailInstance'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetInstanceDefault() {
|
public function testGetInstanceDefault() {
|
||||||
|
$this->config
|
||||||
|
->method('getSystemValue')
|
||||||
|
->willReturnMap([
|
||||||
|
['mail_smtphost', '127.0.0.1', '127.0.0.1'],
|
||||||
|
['mail_smtpport', 25, 25],
|
||||||
|
['mail_smtptimeout', 10, 10],
|
||||||
|
]);
|
||||||
$mailer = self::invokePrivate($this->mailer, 'getInstance');
|
$mailer = self::invokePrivate($this->mailer, 'getInstance');
|
||||||
$this->assertInstanceOf(\Swift_Mailer::class, $mailer);
|
$this->assertInstanceOf(SymfonyMailer::class, $mailer);
|
||||||
$this->assertInstanceOf(\Swift_SmtpTransport::class, $mailer->getTransport());
|
$transport = self::invokePrivate($mailer, 'transport');
|
||||||
|
$this->assertInstanceOf(EsmtpTransport::class, $transport);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetInstanceSendmail() {
|
public function testGetInstanceSendmail() {
|
||||||
|
@ -127,11 +140,33 @@ class MailerTest extends TestCase {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$mailer = self::invokePrivate($this->mailer, 'getInstance');
|
$mailer = self::invokePrivate($this->mailer, 'getInstance');
|
||||||
$this->assertInstanceOf(\Swift_Mailer::class, $mailer);
|
$this->assertInstanceOf(SymfonyMailer::class, $mailer);
|
||||||
$this->assertInstanceOf(\Swift_SendmailTransport::class, $mailer->getTransport());
|
$transport = self::invokePrivate($mailer, 'transport');
|
||||||
|
$this->assertInstanceOf(SendmailTransport::class, $transport);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testEvents() {
|
public function testEvents() {
|
||||||
|
$this->config
|
||||||
|
->method('getSystemValue')
|
||||||
|
->willReturnMap([
|
||||||
|
['mail_smtphost', '127.0.0.1', '127.0.0.1'],
|
||||||
|
['mail_smtpport', 25, 25],
|
||||||
|
]);
|
||||||
|
$this->mailer = $this->getMockBuilder(Mailer::class)
|
||||||
|
->setMethods(['getInstance'])
|
||||||
|
->setConstructorArgs(
|
||||||
|
[
|
||||||
|
$this->config,
|
||||||
|
$this->logger,
|
||||||
|
$this->defaults,
|
||||||
|
$this->urlGenerator,
|
||||||
|
$this->l10n,
|
||||||
|
$this->dispatcher,
|
||||||
|
$this->createMock(IFactory::class)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
->getMock();
|
||||||
|
|
||||||
$message = $this->createMock(Message::class);
|
$message = $this->createMock(Message::class);
|
||||||
|
|
||||||
$event = new BeforeMessageSent($message);
|
$event = new BeforeMessageSent($message);
|
||||||
|
@ -139,11 +174,7 @@ class MailerTest extends TestCase {
|
||||||
->method('dispatchTyped')
|
->method('dispatchTyped')
|
||||||
->with($this->equalTo($event));
|
->with($this->equalTo($event));
|
||||||
|
|
||||||
# We do not care at this point about errors in Swiftmailer
|
$this->mailer->send($message);
|
||||||
try {
|
|
||||||
$this->mailer->send($message);
|
|
||||||
} catch (Swift_SwiftException $e) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCreateMessage() {
|
public function testCreateMessage() {
|
||||||
|
@ -157,13 +188,20 @@ class MailerTest extends TestCase {
|
||||||
|
|
||||||
|
|
||||||
public function testSendInvalidMailException() {
|
public function testSendInvalidMailException() {
|
||||||
|
$this->config
|
||||||
|
->method('getSystemValue')
|
||||||
|
->willReturnMap([
|
||||||
|
['mail_smtphost', '127.0.0.1', '127.0.0.1'],
|
||||||
|
['mail_smtpport', 25, 25],
|
||||||
|
['mail_smtptimeout', 10, 10],
|
||||||
|
]);
|
||||||
$this->expectException(\Exception::class);
|
$this->expectException(\Exception::class);
|
||||||
|
|
||||||
$message = $this->getMockBuilder('\OC\Mail\Message')
|
$message = $this->getMockBuilder('\OC\Mail\Message')
|
||||||
->disableOriginalConstructor()->getMock();
|
->disableOriginalConstructor()->getMock();
|
||||||
$message->expects($this->once())
|
$message->expects($this->once())
|
||||||
->method('getSwiftMessage')
|
->method('getSymfonyEmail')
|
||||||
->willReturn(new \Swift_Message());
|
->willReturn(new Email());
|
||||||
|
|
||||||
$this->mailer->send($message);
|
$this->mailer->send($message);
|
||||||
}
|
}
|
||||||
|
@ -202,58 +240,76 @@ class MailerTest extends TestCase {
|
||||||
$this->config->method('getSystemValue')
|
$this->config->method('getSystemValue')
|
||||||
->willReturnMap([
|
->willReturnMap([
|
||||||
['mail_smtpmode', 'smtp', 'smtp'],
|
['mail_smtpmode', 'smtp', 'smtp'],
|
||||||
['mail_smtpstreamoptions', [], ['foo' => 1]]
|
['mail_smtpstreamoptions', [], ['foo' => 1]],
|
||||||
|
['mail_smtphost', '127.0.0.1', '127.0.0.1'],
|
||||||
|
['mail_smtpport', 25, 25],
|
||||||
|
['mail_smtptimeout', 10, 10],
|
||||||
]);
|
]);
|
||||||
$mailer = self::invokePrivate($this->mailer, 'getInstance');
|
$mailer = self::invokePrivate($this->mailer, 'getInstance');
|
||||||
$this->assertEquals(1, count($mailer->getTransport()->getStreamOptions()));
|
/** @var EsmtpTransport $transport */
|
||||||
$this->assertTrue(isset($mailer->getTransport()->getStreamOptions()['foo']));
|
$transport = self::invokePrivate($mailer, 'transport');
|
||||||
|
$this->assertInstanceOf(EsmtpTransport::class, $transport);
|
||||||
|
$this->assertEquals(1, count($transport->getStream()->getStreamOptions()));
|
||||||
|
$this->assertTrue(isset($transport->getStream()->getStreamOptions()['foo']));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testStreamingOptionsWrongType() {
|
public function testStreamingOptionsWrongType() {
|
||||||
$this->config->method('getSystemValue')
|
$this->config->method('getSystemValue')
|
||||||
->willReturnMap([
|
->willReturnMap([
|
||||||
['mail_smtpmode', 'smtp', 'smtp'],
|
['mail_smtpmode', 'smtp', 'smtp'],
|
||||||
['mail_smtpstreamoptions', [], 'bar']
|
['mail_smtpstreamoptions', [], 'bar'],
|
||||||
|
['mail_smtphost', '127.0.0.1', '127.0.0.1'],
|
||||||
|
['mail_smtpport', 25, 25],
|
||||||
|
['mail_smtptimeout', 10, 10],
|
||||||
]);
|
]);
|
||||||
$mailer = self::invokePrivate($this->mailer, 'getInstance');
|
$mailer = self::invokePrivate($this->mailer, 'getInstance');
|
||||||
$this->assertEquals(0, count($mailer->getTransport()->getStreamOptions()));
|
/** @var EsmtpTransport $transport */
|
||||||
|
$transport = self::invokePrivate($mailer, 'transport');
|
||||||
|
$this->assertInstanceOf(EsmtpTransport::class, $transport);
|
||||||
|
$this->assertEquals(0, count($transport->getStream()->getStreamOptions()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testLocalDomain(): void {
|
public function testLocalDomain(): void {
|
||||||
$this->config->method('getSystemValue')
|
$this->config->method('getSystemValue')
|
||||||
->willReturnMap([
|
->willReturnMap([
|
||||||
['mail_smtpmode', 'smtp', 'smtp']
|
['mail_smtpmode', 'smtp', 'smtp'],
|
||||||
|
['mail_smtphost', '127.0.0.1', '127.0.0.1'],
|
||||||
|
['mail_smtpport', 25, 25],
|
||||||
|
['mail_smtptimeout', 10, 10],
|
||||||
]);
|
]);
|
||||||
$this->config->method('getSystemValueString')
|
$this->config->method('getSystemValueString')
|
||||||
->with('overwrite.cli.url', '')
|
->with('overwrite.cli.url', '')
|
||||||
->willReturn('https://some.valid.url.com:8080');
|
->willReturn('https://some.valid.url.com:8080');
|
||||||
|
|
||||||
/** @var \Swift_Mailer $mailer */
|
/** @var SymfonyMailer $mailer */
|
||||||
$mailer = self::invokePrivate($this->mailer, 'getInstance');
|
$mailer = self::invokePrivate($this->mailer, 'getInstance');
|
||||||
self::assertInstanceOf(\Swift_Mailer::class, $mailer);
|
self::assertInstanceOf(SymfonyMailer::class, $mailer);
|
||||||
|
|
||||||
/** @var \Swift_Transport_EsmtpTransport $transport */
|
/** @var EsmtpTransport $transport */
|
||||||
$transport = $mailer->getTransport();
|
$transport = self::invokePrivate($mailer, 'transport');
|
||||||
self::assertInstanceOf(\Swift_Transport_EsmtpTransport::class, $transport);
|
self::assertInstanceOf(EsmtpTransport::class, $transport);
|
||||||
self::assertEquals('some.valid.url.com', $transport->getLocalDomain());
|
self::assertEquals('some.valid.url.com', $transport->getLocalDomain());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testLocalDomainInvalidUrl(): void {
|
public function testLocalDomainInvalidUrl(): void {
|
||||||
$this->config->method('getSystemValue')
|
$this->config->method('getSystemValue')
|
||||||
->willReturnMap([
|
->willReturnMap([
|
||||||
['mail_smtpmode', 'smtp', 'smtp']
|
['mail_smtpmode', 'smtp', 'smtp'],
|
||||||
|
['mail_smtphost', '127.0.0.1', '127.0.0.1'],
|
||||||
|
['mail_smtpport', 25, 25],
|
||||||
|
['mail_smtptimeout', 10, 10],
|
||||||
]);
|
]);
|
||||||
$this->config->method('getSystemValueString')
|
$this->config->method('getSystemValueString')
|
||||||
->with('overwrite.cli.url', '')
|
->with('overwrite.cli.url', '')
|
||||||
->willReturn('https:only.slash.does.not.work:8080');
|
->willReturn('https:only.slash.does.not.work:8080');
|
||||||
|
|
||||||
/** @var \Swift_Mailer $mailer */
|
/** @var SymfonyMailer $mailer */
|
||||||
$mailer = self::invokePrivate($this->mailer, 'getInstance');
|
$mailer = self::invokePrivate($this->mailer, 'getInstance');
|
||||||
self::assertInstanceOf(\Swift_Mailer::class, $mailer);
|
self::assertInstanceOf(SymfonyMailer::class, $mailer);
|
||||||
|
|
||||||
/** @var \Swift_Transport_EsmtpTransport $transport */
|
/** @var EsmtpTransport $transport */
|
||||||
$transport = $mailer->getTransport();
|
$transport = self::invokePrivate($mailer, 'transport');
|
||||||
self::assertInstanceOf(\Swift_Transport_EsmtpTransport::class, $transport);
|
self::assertInstanceOf(EsmtpTransport::class, $transport);
|
||||||
self::assertEquals('[127.0.0.1]', $transport->getLocalDomain());
|
self::assertEquals('[127.0.0.1]', $transport->getLocalDomain());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,12 +11,17 @@ namespace Test\Mail;
|
||||||
use OC\Mail\Message;
|
use OC\Mail\Message;
|
||||||
use OCP\Mail\Headers\AutoSubmitted;
|
use OCP\Mail\Headers\AutoSubmitted;
|
||||||
use OCP\Mail\IEMailTemplate;
|
use OCP\Mail\IEMailTemplate;
|
||||||
use Swift_Message;
|
use Symfony\Component\Mime\Address;
|
||||||
|
use Symfony\Component\Mime\Email;
|
||||||
|
use Symfony\Component\Mime\Exception\RfcComplianceException;
|
||||||
|
use Symfony\Component\Mime\Header\HeaderInterface;
|
||||||
|
use Symfony\Component\Mime\Header\Headers;
|
||||||
use Test\TestCase;
|
use Test\TestCase;
|
||||||
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
|
|
||||||
class MessageTest extends TestCase {
|
class MessageTest extends TestCase {
|
||||||
/** @var Swift_Message */
|
/** @var Email */
|
||||||
private $swiftMessage;
|
private $symfonyEmail;
|
||||||
/** @var Message */
|
/** @var Message */
|
||||||
private $message;
|
private $message;
|
||||||
|
|
||||||
|
@ -25,10 +30,26 @@ class MessageTest extends TestCase {
|
||||||
*/
|
*/
|
||||||
public function mailAddressProvider() {
|
public function mailAddressProvider() {
|
||||||
return [
|
return [
|
||||||
[['lukas@owncloud.com' => 'Lukas Reschke'], ['lukas@owncloud.com' => 'Lukas Reschke']],
|
[
|
||||||
[['lukas@owncloud.com' => 'Lukas Reschke', 'lukas@öwnclöüd.com', 'lukäs@owncloud.örg' => 'Lükäs Réschke'],
|
['lukas@owncloud.com' => 'Lukas Reschke'],
|
||||||
['lukas@owncloud.com' => 'Lukas Reschke', 'lukas@xn--wncld-iuae2c.com', 'lukäs@owncloud.xn--rg-eka' => 'Lükäs Réschke']],
|
[new Address('lukas@owncloud.com', 'Lukas Reschke')]
|
||||||
[['lukas@öwnclöüd.com'], ['lukas@xn--wncld-iuae2c.com']],
|
],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'lukas@owncloud.com' => 'Lukas Reschke',
|
||||||
|
'lukas@öwnclöüd.com',
|
||||||
|
'lukäs@owncloud.örg' => 'Lükäs Réschke'
|
||||||
|
],
|
||||||
|
[
|
||||||
|
new Address('lukas@owncloud.com', 'Lukas Reschke'),
|
||||||
|
new Address('lukas@öwnclöüd.com'),
|
||||||
|
new Address('lukäs@owncloud.örg', 'Lükäs Réschke')
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
['lukas@öwnclöüd.com'],
|
||||||
|
[new Address('lukas@öwnclöüd.com')]
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +58,7 @@ class MessageTest extends TestCase {
|
||||||
*/
|
*/
|
||||||
public function getMailAddressProvider() {
|
public function getMailAddressProvider() {
|
||||||
return [
|
return [
|
||||||
[null, []],
|
[[], []],
|
||||||
[['lukas@owncloud.com' => 'Lukas Reschke'], ['lukas@owncloud.com' => 'Lukas Reschke']],
|
[['lukas@owncloud.com' => 'Lukas Reschke'], ['lukas@owncloud.com' => 'Lukas Reschke']],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -45,189 +66,158 @@ class MessageTest extends TestCase {
|
||||||
protected function setUp(): void {
|
protected function setUp(): void {
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
$this->swiftMessage = $this->getMockBuilder('\Swift_Message')
|
$this->symfonyEmail = $this->getMockBuilder(Email::class)
|
||||||
->disableOriginalConstructor()->getMock();
|
->disableOriginalConstructor()->getMock();
|
||||||
|
|
||||||
$this->message = new Message($this->swiftMessage, false);
|
$this->message = new Message($this->symfonyEmail, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @requires function idn_to_ascii
|
|
||||||
* @dataProvider mailAddressProvider
|
* @dataProvider mailAddressProvider
|
||||||
*
|
*
|
||||||
* @param string $unconverted
|
* @param string $unconverted
|
||||||
* @param string $expected
|
* @param string $expected
|
||||||
*/
|
*/
|
||||||
public function testConvertAddresses($unconverted, $expected) {
|
public function testConvertAddresses($unconverted, $expected) {
|
||||||
$this->assertSame($expected, self::invokePrivate($this->message, 'convertAddresses', [$unconverted]));
|
$this->assertEquals($expected, self::invokePrivate($this->message, 'convertAddresses', [$unconverted]));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSetFrom() {
|
public function testSetRecipients(): void {
|
||||||
$this->swiftMessage
|
$this->message = $this->message->setFrom(['pierres-general-store@stardewvalley.com' => 'Pierres General Store']);
|
||||||
|
$this->message = $this->message->setTo(['lewis-tent@stardewvalley.com' => "Lewis' Tent Life"]);
|
||||||
|
$this->message = $this->message->setReplyTo(['penny@stardewvalley-library.co.edu' => 'Penny']);
|
||||||
|
$this->message = $this->message->setCc(['gunther@stardewvalley-library.co.edu' => 'Gunther']);
|
||||||
|
$this->message = $this->message->setBcc(['pam@stardewvalley-bus.com' => 'Pam']);
|
||||||
|
|
||||||
|
$this->symfonyEmail
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('setFrom')
|
->method('from')
|
||||||
->with(['lukas@owncloud.com']);
|
->willReturn(new Address('pierres-general-store@stardewvalley.com', 'Pierres General Store'));
|
||||||
$this->message->setFrom(['lukas@owncloud.com']);
|
$this->symfonyEmail
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider getMailAddressProvider
|
|
||||||
*
|
|
||||||
* @param $swiftresult
|
|
||||||
* @param $return
|
|
||||||
*/
|
|
||||||
public function testGetFrom($swiftresult, $return) {
|
|
||||||
$this->swiftMessage
|
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('getFrom')
|
->method('to')
|
||||||
->willReturn($swiftresult);
|
->willReturn(new Address('lewis-tent@stardewvalley.com', "Lewis' Tent Life"));
|
||||||
|
$this->symfonyEmail
|
||||||
$this->assertSame($return, $this->message->getFrom());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testSetReplyTo() {
|
|
||||||
$this->swiftMessage
|
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('setReplyTo')
|
->method('replyTo')
|
||||||
->with(['lukas@owncloud.com']);
|
->willReturn(new Address('penny@stardewvalley-library.co.edu', 'Penny'));
|
||||||
$this->message->setReplyTo(['lukas@owncloud.com']);
|
$this->symfonyEmail
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetReplyTo() {
|
|
||||||
$this->swiftMessage
|
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('getReplyTo')
|
->method('cc')
|
||||||
->willReturn('lukas@owncloud.com');
|
->willReturn(new Address('gunther@stardewvalley-library.co.edu', 'Gunther'));
|
||||||
|
$this->symfonyEmail
|
||||||
$this->assertSame('lukas@owncloud.com', $this->message->getReplyTo());
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @dataProvider dataSetTo */
|
|
||||||
public function testSetTo(array $to, array $expected) {
|
|
||||||
$this->swiftMessage
|
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('setTo')
|
->method('bcc')
|
||||||
->with($expected);
|
->willReturn(new Address('pam@stardewvalley-bus.com', 'Pam'));
|
||||||
$this->message->setTo($to);
|
|
||||||
|
$this->message->setRecipients();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function dataSetTo(): array {
|
public function testSetTo() {
|
||||||
return [
|
$expected = ['pierres-general-store@stardewvalley.com' => 'Pierres General Store'];
|
||||||
[['robot@example.com'], ['robot@example.com']],
|
|
||||||
[['robot'], ['robot' => 'robot']],
|
|
||||||
[['robot' => 'robot display name'], ['robot' => 'robot display name']],
|
|
||||||
[['example@🤖.com'], ['example@xn--yp9h.com']],
|
|
||||||
[['example@🤖.com' => 'A robot'], ['example@xn--yp9h.com' => 'A robot']],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
$message = $this->message->setTo(['pierres-general-store@stardewvalley.com' => 'Pierres General Store']);
|
||||||
* @dataProvider getMailAddressProvider
|
|
||||||
*/
|
$this->assertEquals($expected, $message->getTo());
|
||||||
public function testGetTo($swiftresult, $return) {
|
}
|
||||||
$this->swiftMessage
|
public function testSetRecipientsException(): void {
|
||||||
|
$message = $this->message->setTo(['lewis-tent@~~~~.com' => "Lewis' Tent Life"]);
|
||||||
|
|
||||||
|
$this->symfonyEmail
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('getTo')
|
->method('to')
|
||||||
->willReturn($swiftresult);
|
->willThrowException(new RfcComplianceException());
|
||||||
|
|
||||||
$this->assertSame($return, $this->message->getTo());
|
$this->expectException(RfcComplianceException::class);
|
||||||
|
$message->setRecipients();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSetCc() {
|
public function testSetRecipientsEmptyValues(): void {
|
||||||
$this->swiftMessage
|
$message = $this->message->setTo([]);
|
||||||
|
|
||||||
|
$this->symfonyEmail
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('setCc')
|
->method('to');
|
||||||
->with(['lukas@owncloud.com']);
|
|
||||||
$this->message->setCc(['lukas@owncloud.com']);
|
$message->setRecipients();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function testSetGetFrom() {
|
||||||
* @dataProvider getMailAddressProvider
|
$expected = ['pierres-general-store@stardewvalley.com' => 'Pierres General Store'];
|
||||||
*/
|
|
||||||
public function testGetCc($swiftresult, $return) {
|
|
||||||
$this->swiftMessage
|
|
||||||
->expects($this->once())
|
|
||||||
->method('getCc')
|
|
||||||
->willReturn($swiftresult);
|
|
||||||
|
|
||||||
$this->assertSame($return, $this->message->getCc());
|
$message = $this->message->setFrom(['pierres-general-store@stardewvalley.com' => 'Pierres General Store']);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $message->getFrom());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSetBcc() {
|
public function testSetGetTo() {
|
||||||
$this->swiftMessage
|
$expected = ['lewis-tent@stardewvalley.com' => "Lewis' Tent Life"];
|
||||||
->expects($this->once())
|
|
||||||
->method('setBcc')
|
$message = $this->message->setTo(['lewis-tent@stardewvalley.com' => "Lewis' Tent Life"]);
|
||||||
->with(['lukas@owncloud.com']);
|
|
||||||
$this->message->setBcc(['lukas@owncloud.com']);
|
$this->assertEquals($expected, $message->getTo());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function testSetGetReplyTo() {
|
||||||
* @dataProvider getMailAddressProvider
|
$expected = ['penny@stardewvalley-library.co.edu' => 'Penny'];
|
||||||
*/
|
|
||||||
public function testGetBcc($swiftresult, $return) {
|
|
||||||
$this->swiftMessage
|
|
||||||
->expects($this->once())
|
|
||||||
->method('getBcc')
|
|
||||||
->willReturn($swiftresult);
|
|
||||||
|
|
||||||
$this->assertSame($return, $this->message->getBcc());
|
$message = $this->message->setReplyTo(['penny@stardewvalley-library.co.edu' => 'Penny']);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $message->getReplyTo());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSetSubject() {
|
public function testSetGetCC() {
|
||||||
$this->swiftMessage
|
$expected = ['gunther@stardewvalley-library.co.edu' => 'Gunther'];
|
||||||
->expects($this->once())
|
|
||||||
->method('setSubject')
|
|
||||||
->with('Fancy Subject');
|
|
||||||
|
|
||||||
$this->message->setSubject('Fancy Subject');
|
$message = $this->message->setCc(['gunther@stardewvalley-library.co.edu' => 'Gunther']);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $message->getCc());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetSubject() {
|
public function testSetGetBCC() {
|
||||||
$this->swiftMessage
|
$expected = ['pam@stardewvalley-bus.com' => 'Pam'];
|
||||||
->expects($this->once())
|
|
||||||
->method('getSubject')
|
|
||||||
->willReturn('Fancy Subject');
|
|
||||||
|
|
||||||
$this->assertSame('Fancy Subject', $this->message->getSubject());
|
$message = $this->message->setBcc(['pam@stardewvalley-bus.com' => 'Pam']);
|
||||||
|
|
||||||
|
$this->assertEquals($expected, $message->getBcc());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSetPlainBody() {
|
public function testSetPlainBody() {
|
||||||
$this->swiftMessage
|
$this->symfonyEmail
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('setBody')
|
->method('text')
|
||||||
->with('Fancy Body');
|
->with('Fancy Body');
|
||||||
|
|
||||||
$this->message->setPlainBody('Fancy Body');
|
$this->message->setPlainBody('Fancy Body');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetPlainBody() {
|
public function testGetPlainBody() {
|
||||||
$this->swiftMessage
|
$this->symfonyEmail
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('getBody')
|
->method('getTextBody')
|
||||||
->willReturn('Fancy Body');
|
->willReturn('Fancy Body');
|
||||||
|
|
||||||
$this->assertSame('Fancy Body', $this->message->getPlainBody());
|
$this->assertSame('Fancy Body', $this->message->getPlainBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSetHtmlBody() {
|
public function testSetHtmlBody() {
|
||||||
$this->swiftMessage
|
$this->symfonyEmail
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
->method('addPart')
|
->method('html')
|
||||||
->with('<blink>Fancy Body</blink>', 'text/html');
|
->with('<blink>Fancy Body</blink>', 'utf-8');
|
||||||
|
|
||||||
$this->message->setHtmlBody('<blink>Fancy Body</blink>');
|
$this->message->setHtmlBody('<blink>Fancy Body</blink>');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPlainTextRenderOption() {
|
public function testPlainTextRenderOption() {
|
||||||
/** @var \PHPUnit\Framework\MockObject\MockObject|Swift_Message $swiftMessage */
|
/** @var MockObject|Email $symfonyEmail */
|
||||||
$swiftMessage = $this->getMockBuilder('\Swift_Message')
|
$symfonyEmail = $this->getMockBuilder(Email::class)
|
||||||
->disableOriginalConstructor()->getMock();
|
->disableOriginalConstructor()->getMock();
|
||||||
/** @var \PHPUnit\Framework\MockObject\MockObject|IEMailTemplate $template */
|
/** @var MockObject|IEMailTemplate $template */
|
||||||
$template = $this->getMockBuilder('\OCP\Mail\IEMailTemplate')
|
$template = $this->getMockBuilder(IEMailTemplate::class)
|
||||||
->disableOriginalConstructor()->getMock();
|
->disableOriginalConstructor()->getMock();
|
||||||
|
|
||||||
$message = new Message($swiftMessage, true);
|
$message = new Message($symfonyEmail, true);
|
||||||
|
|
||||||
$template
|
$template
|
||||||
->expects($this->never())
|
->expects($this->never())
|
||||||
|
@ -243,14 +233,14 @@ class MessageTest extends TestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testBothRenderingOptions() {
|
public function testBothRenderingOptions() {
|
||||||
/** @var \PHPUnit\Framework\MockObject\MockObject|Swift_Message $swiftMessage */
|
/** @var MockObject|Email $symfonyEmail */
|
||||||
$swiftMessage = $this->getMockBuilder('\Swift_Message')
|
$symfonyEmail = $this->getMockBuilder(Email::class)
|
||||||
->disableOriginalConstructor()->getMock();
|
->disableOriginalConstructor()->getMock();
|
||||||
/** @var \PHPUnit\Framework\MockObject\MockObject|IEMailTemplate $template */
|
/** @var MockObject|IEMailTemplate $template */
|
||||||
$template = $this->getMockBuilder('\OCP\Mail\IEMailTemplate')
|
$template = $this->getMockBuilder(IEMailTemplate::class)
|
||||||
->disableOriginalConstructor()->getMock();
|
->disableOriginalConstructor()->getMock();
|
||||||
|
|
||||||
$message = new Message($swiftMessage, false);
|
$message = new Message($symfonyEmail, false);
|
||||||
|
|
||||||
$template
|
$template
|
||||||
->expects($this->once())
|
->expects($this->once())
|
||||||
|
@ -266,108 +256,40 @@ class MessageTest extends TestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSetAutoSubmitted1() {
|
public function testSetAutoSubmitted1() {
|
||||||
$swiftMimeSimpleHeaderSet = $this->getMockBuilder('\Swift_Mime_SimpleHeaderSet')
|
$headers = new Headers($this->createMock(HeaderInterface::class));
|
||||||
->disableOriginalConstructor()
|
$headers->addTextHeader(AutoSubmitted::HEADER, "yes");
|
||||||
->getMock();
|
$symfonyEmail = $this->createMock(Email::class);
|
||||||
$swiftMessage = $this->getMockBuilder('\Swift_Message')
|
|
||||||
->disableOriginalConstructor()
|
|
||||||
->disableOriginalClone()
|
|
||||||
->disableArgumentCloning()
|
|
||||||
->disallowMockingUnknownTypes()
|
|
||||||
->getMock();
|
|
||||||
|
|
||||||
$swiftMessage->method('getHeaders')->willReturn($swiftMimeSimpleHeaderSet);
|
$symfonyEmail->method('getHeaders')
|
||||||
|
->willReturn($headers);
|
||||||
|
|
||||||
$swiftMimeSimpleHeaderSet->expects($this->once())
|
$message = new Message($symfonyEmail, false);
|
||||||
->method('has')
|
|
||||||
->with('Auto-Submitted');
|
|
||||||
$swiftMimeSimpleHeaderSet->expects($this->never())
|
|
||||||
->method('remove');
|
|
||||||
$swiftMimeSimpleHeaderSet->expects($this->once())
|
|
||||||
->method('addTextHeader')
|
|
||||||
->with('Auto-Submitted', AutoSubmitted::VALUE_AUTO_GENERATED);
|
|
||||||
|
|
||||||
$message = new Message($swiftMessage, false);
|
|
||||||
$message->setAutoSubmitted(AutoSubmitted::VALUE_AUTO_GENERATED);
|
$message->setAutoSubmitted(AutoSubmitted::VALUE_AUTO_GENERATED);
|
||||||
|
$this->assertNotSame('no', $message->getAutoSubmitted());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testSetAutoSubmitted2() {
|
public function testSetAutoSubmitted2() {
|
||||||
$swiftMimeSimpleHeaderSet = $this->getMockBuilder('\Swift_Mime_SimpleHeaderSet')
|
$headers = new Headers($this->createMock(HeaderInterface::class));
|
||||||
->disableOriginalConstructor()
|
$headers->addTextHeader(AutoSubmitted::HEADER, 'no');
|
||||||
->getMock();
|
$symfonyEmail = $this->createMock(Email::class);
|
||||||
$swiftMessage = $this->getMockBuilder('\Swift_Message')
|
|
||||||
->disableOriginalConstructor()
|
|
||||||
->disableOriginalClone()
|
|
||||||
->disableArgumentCloning()
|
|
||||||
->disallowMockingUnknownTypes()
|
|
||||||
->getMock();
|
|
||||||
|
|
||||||
$swiftMessage->method('getHeaders')->willReturn($swiftMimeSimpleHeaderSet);
|
$symfonyEmail->method('getHeaders')
|
||||||
|
->willReturn($headers);
|
||||||
|
|
||||||
$swiftMimeSimpleHeaderSet->expects($this->once())
|
$message = new Message($symfonyEmail, false);
|
||||||
->method('has')
|
|
||||||
->with('Auto-Submitted')
|
|
||||||
->willReturn(true);
|
|
||||||
$swiftMimeSimpleHeaderSet->expects($this->once())
|
|
||||||
->method('remove')
|
|
||||||
->with('Auto-Submitted');
|
|
||||||
$swiftMimeSimpleHeaderSet->expects($this->once())
|
|
||||||
->method('addTextHeader')
|
|
||||||
->with('Auto-Submitted', AutoSubmitted::VALUE_AUTO_GENERATED);
|
|
||||||
|
|
||||||
$message = new Message($swiftMessage, false);
|
|
||||||
$message->setAutoSubmitted(AutoSubmitted::VALUE_AUTO_GENERATED);
|
$message->setAutoSubmitted(AutoSubmitted::VALUE_AUTO_GENERATED);
|
||||||
|
$this->assertSame('auto-generated', $message->getAutoSubmitted());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetAutoSubmitted1() {
|
public function testGetAutoSubmitted() {
|
||||||
$swiftMimeSimpleHeaderSet = $this->getMockBuilder('\Swift_Mime_SimpleHeaderSet')
|
$headers = new Headers($this->createMock(HeaderInterface::class));
|
||||||
->disableOriginalConstructor()
|
$headers->addTextHeader(AutoSubmitted::HEADER, 'no');
|
||||||
->getMock();
|
$symfonyEmail = $this->createMock(Email::class);
|
||||||
$swiftMessage = $this->getMockBuilder('\Swift_Message')
|
|
||||||
->disableOriginalConstructor()
|
|
||||||
->disableOriginalClone()
|
|
||||||
->disableArgumentCloning()
|
|
||||||
->disallowMockingUnknownTypes()
|
|
||||||
->getMock();
|
|
||||||
|
|
||||||
$swiftMessage->method('getHeaders')->willReturn($swiftMimeSimpleHeaderSet);
|
$symfonyEmail->method('getHeaders')
|
||||||
|
->willReturn($headers);
|
||||||
|
|
||||||
$swiftMimeSimpleHeaderSet->expects($this->once())
|
$message = new Message($symfonyEmail, false);
|
||||||
->method('has')
|
|
||||||
->with('Auto-Submitted');
|
|
||||||
$swiftMimeSimpleHeaderSet->expects($this->never())
|
|
||||||
->method('get');
|
|
||||||
|
|
||||||
$message = new Message($swiftMessage, false);
|
|
||||||
$this->assertSame("no", $message->getAutoSubmitted());
|
$this->assertSame("no", $message->getAutoSubmitted());
|
||||||
}
|
}
|
||||||
public function testGetAutoSubmitted2() {
|
|
||||||
$swiftMimeHeader = $this->getMockBuilder('\Swift_Mime_Header')
|
|
||||||
->disableOriginalConstructor()
|
|
||||||
->getMockForAbstractClass();
|
|
||||||
$swiftMimeSimpleHeaderSet = $this->getMockBuilder('\Swift_Mime_SimpleHeaderSet')
|
|
||||||
->disableOriginalConstructor()
|
|
||||||
->getMock();
|
|
||||||
$swiftMessage = $this->getMockBuilder('\Swift_Message')
|
|
||||||
->disableOriginalConstructor()
|
|
||||||
->disableOriginalClone()
|
|
||||||
->disableArgumentCloning()
|
|
||||||
->disallowMockingUnknownTypes()
|
|
||||||
->getMock();
|
|
||||||
|
|
||||||
|
|
||||||
$swiftMessage->method('getHeaders')->willReturn($swiftMimeSimpleHeaderSet);
|
|
||||||
$swiftMimeHeader->method('toString')->willReturn(AutoSubmitted::VALUE_AUTO_GENERATED);
|
|
||||||
|
|
||||||
$swiftMimeSimpleHeaderSet->expects($this->once())
|
|
||||||
->method('has')
|
|
||||||
->with('Auto-Submitted')
|
|
||||||
->willReturn(true);
|
|
||||||
$swiftMimeSimpleHeaderSet->expects($this->once())
|
|
||||||
->method('get')
|
|
||||||
->willReturn($swiftMimeHeader);
|
|
||||||
|
|
||||||
$message = new Message($swiftMessage, false);
|
|
||||||
$this->assertSame(AutoSubmitted::VALUE_AUTO_GENERATED, $message->getAutoSubmitted());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче