diff --git a/apps/dav/lib/CalDAV/Schedule/IMipPlugin.php b/apps/dav/lib/CalDAV/Schedule/IMipPlugin.php index ffe6930fdb4a..4fcbc95e535e 100644 --- a/apps/dav/lib/CalDAV/Schedule/IMipPlugin.php +++ b/apps/dav/lib/CalDAV/Schedule/IMipPlugin.php @@ -41,21 +41,12 @@ * @license http://sabre.io/license/ Modified BSD License */ class IMipPlugin extends SabreIMipPlugin { - /** @var IMailer */ - private $mailer; - - /** @var ILogger */ - private $logger; - - /** @var IRequest */ - private $request; + private IMailer $mailer; + private ILogger $logger; + private IRequest $request; /** * Creates the email handler. - * - * @param IMailer $mailer - * @param ILogger $logger - * @param IRequest $request */ public function __construct(IMailer $mailer, ILogger $logger, IRequest $request) { parent::__construct(''); @@ -123,14 +114,10 @@ public function schedule(ITip\Message $iTipMessage) { ->setFrom([$sender => $senderName]) ->setTo([$recipient => $recipientName]) ->setSubject($subject) - ->setBody($iTipMessage->message->serialize(), $contentType); + ->attach($iTipMessage->message->serialize(), "event.ics", $contentType); try { - $failed = $this->mailer->send($message); + $this->mailer->send($message); $iTipMessage->scheduleStatus = '1.1; Scheduling message is sent via iMip'; - if ($failed) { - $this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' => \implode(', ', $failed)]); - $iTipMessage->scheduleStatus = '5.0; EMail delivery failed'; - } } catch (\Exception $ex) { $this->logger->logException($ex, ['app' => 'dav']); $iTipMessage->scheduleStatus = '5.0; EMail delivery failed'; diff --git a/apps/dav/tests/unit/CalDAV/Schedule/IMipPluginTest.php b/apps/dav/tests/unit/CalDAV/Schedule/IMipPluginTest.php index c1902456caab..534cd97253e0 100644 --- a/apps/dav/tests/unit/CalDAV/Schedule/IMipPluginTest.php +++ b/apps/dav/tests/unit/CalDAV/Schedule/IMipPluginTest.php @@ -22,124 +22,78 @@ namespace OCA\DAV\Tests\unit\CalDAV\Schedule; +use Exception; use OC\Mail\Mailer; use OCA\DAV\CalDAV\Schedule\IMipPlugin; use OCP\ILogger; use OCP\IRequest; +use PHPUnit\Framework\MockObject\MockObject; use Sabre\VObject\Component\VCalendar; use Sabre\VObject\ITip\Message; +use Symfony\Component\Mime\Email; use Test\TestCase; use OC\Log; class IMipPluginTest extends TestCase { - public function testDelivery() { - $mailMessage = new \OC\Mail\Message(new \Swift_Message()); - /** @var Mailer | \PHPUnit\Framework\MockObject\MockObject $mailer */ - $mailer = $this->createMock(Mailer::class); - $mailer->method('createMessage')->willReturn($mailMessage); - $mailer->expects($this->once())->method('send'); - /** @var ILogger | \PHPUnit\Framework\MockObject\MockObject $logger */ - $logger = $this->createMock(Log::class); - /** @var IRequest| \PHPUnit\Framework\MockObject\MockObject $request */ + private \OC\Mail\Message $mailMessage; + /** + * @var Mailer|MockObject + */ + private $mailer; + private IMipPlugin $plugin; + /** @var ILogger|MockObject */ + private $logger; + + protected function setUp(): void { + parent::setUp(); + + $this->mailMessage = new \OC\Mail\Message(new Email()); + $this->mailer = $this->createMock(Mailer::class); + $this->mailer->method('createMessage')->willReturn($this->mailMessage); + + $this->logger = $this->createMock(Log::class); + /** @var IRequest| MockObject $request */ $request = $this->createMock(IRequest::class); - $plugin = new IMipPlugin($mailer, $logger, $request); - $message = new Message(); - $message->method = 'REQUEST'; - $message->message = new VCalendar(); - $message->message->add('VEVENT', [ - 'UID' => $message->uid, - 'SEQUENCE' => $message->sequence, - 'SUMMARY' => 'Fellowship meeting', - ]); - $message->sender = 'mailto:gandalf@wiz.ard'; - $message->recipient = 'mailto:frodo@hobb.it'; + $this->plugin = new IMipPlugin($this->mailer, $this->logger, $request); + } + + public function testDelivery(): void { + $this->mailer->expects($this->once())->method('send'); + + $message = $this->buildIMIPMessage('REQUEST'); - $plugin->schedule($message); + $this->plugin->schedule($message); $this->assertEquals('1.1', $message->getScheduleStatus()); - $this->assertEquals('Fellowship meeting', $mailMessage->getSubject()); - $this->assertEquals(['frodo@hobb.it' => null], $mailMessage->getTo()); - $this->assertEquals(['gandalf@wiz.ard' => null], $mailMessage->getReplyTo()); - $this->assertEquals('text/calendar; charset=UTF-8; method=REQUEST', $mailMessage->getSwiftMessage()->getContentType()); + $this->assertEquals('Fellowship meeting', $this->mailMessage->getSubject()); + $this->assertEquals(['frodo@hobb.it' => null], $this->mailMessage->getTo()); + $this->assertEquals(['gandalf@wiz.ard' => null], $this->mailMessage->getReplyTo()); + $this->assertStringContainsString('text/calendar; charset=UTF-8; method=REQUEST', $this->mailMessage->getMessage()->getBody()->bodyToString()); } - public function testFailedDeliveryWithException() { - $mailMessage = new \OC\Mail\Message(new \Swift_Message()); - /** @var Mailer | \PHPUnit\Framework\MockObject\MockObject $mailer */ - $mailer = $this->createMock(Mailer::class); - $mailer->method('createMessage')->willReturn($mailMessage); - $mailer->method('send')->willThrowException(new \Exception()); - /** @var ILogger | \PHPUnit\Framework\MockObject\MockObject $logger */ - $logger = $this->createMock(Log::class); - /** @var IRequest| \PHPUnit\Framework\MockObject\MockObject $request */ - $request = $this->createMock(IRequest::class); + public function testFailedDeliveryWithException(): void { + $ex = new Exception(); + $this->mailer->method('send')->willThrowException($ex); + $this->logger->expects(self::once())->method('logException')->with($ex, ['app' => 'dav']); - $plugin = new IMipPlugin($mailer, $logger, $request); - $message = new Message(); - $message->method = 'REQUEST'; - $message->message = new VCalendar(); - $message->message->add('VEVENT', [ - 'UID' => $message->uid, - 'SEQUENCE' => $message->sequence, - 'SUMMARY' => 'Fellowship meeting', - ]); - $message->sender = 'mailto:gandalf@wiz.ard'; - $message->recipient = 'mailto:frodo@hobb.it'; + $message = $this->buildIMIPMessage('REQUEST'); - $plugin->schedule($message); - $this->assertEquals('5.0', $message->getScheduleStatus()); - $this->assertEquals('Fellowship meeting', $mailMessage->getSubject()); - $this->assertEquals(['frodo@hobb.it' => null], $mailMessage->getTo()); - $this->assertEquals(['gandalf@wiz.ard' => null], $mailMessage->getReplyTo()); - $this->assertEquals('text/calendar; charset=UTF-8; method=REQUEST', $mailMessage->getSwiftMessage()->getContentType()); + $this->plugin->schedule($message); + $this->assertIMipState($message, '5.0', 'REQUEST', 'Fellowship meeting'); } - public function testFailedDelivery() { - $mailMessage = new \OC\Mail\Message(new \Swift_Message()); - /** @var Mailer | \PHPUnit\Framework\MockObject\MockObject $mailer */ - $mailer = $this->createMock(Mailer::class); - $mailer->method('createMessage')->willReturn($mailMessage); - $mailer->method('send')->willReturn(['foo@example.net']); - /** @var ILogger | \PHPUnit\Framework\MockObject\MockObject $logger */ - $logger = $this->createMock(Log::class); - $logger->expects(self::once())->method('error')->with('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' => 'foo@example.net']); - /** @var IRequest| \PHPUnit\Framework\MockObject\MockObject $request */ - $request = $this->createMock(IRequest::class); + public function testDeliveryOfCancel(): void { + $this->mailer->expects($this->once())->method('send'); - $plugin = new IMipPlugin($mailer, $logger, $request); - $message = new Message(); - $message->method = 'REQUEST'; - $message->message = new VCalendar(); - $message->message->add('VEVENT', [ - 'UID' => $message->uid, - 'SEQUENCE' => $message->sequence, - 'SUMMARY' => 'Fellowship meeting', - ]); - $message->sender = 'mailto:gandalf@wiz.ard'; - $message->recipient = 'mailto:frodo@hobb.it'; + $message = $this->buildIMIPMessage('CANCEL'); - $plugin->schedule($message); - $this->assertEquals('5.0', $message->getScheduleStatus()); - $this->assertEquals('Fellowship meeting', $mailMessage->getSubject()); - $this->assertEquals(['frodo@hobb.it' => null], $mailMessage->getTo()); - $this->assertEquals(['gandalf@wiz.ard' => null], $mailMessage->getReplyTo()); - $this->assertEquals('text/calendar; charset=UTF-8; method=REQUEST', $mailMessage->getSwiftMessage()->getContentType()); + $this->plugin->schedule($message); + $this->assertIMipState($message, '1.1', 'CANCEL', 'Cancelled: Fellowship meeting'); } - public function testDeliveryOfCancel() { - $mailMessage = new \OC\Mail\Message(new \Swift_Message()); - /** @var Mailer | \PHPUnit\Framework\MockObject\MockObject $mailer */ - $mailer = $this->createMock(Mailer::class); - $mailer->method('createMessage')->willReturn($mailMessage); - $mailer->expects($this->once())->method('send'); - /** @var ILogger | \PHPUnit\Framework\MockObject\MockObject $logger */ - $logger = $this->createMock(Log::class); - /** @var IRequest| \PHPUnit\Framework\MockObject\MockObject $request */ - $request = $this->createMock(IRequest::class); - - $plugin = new IMipPlugin($mailer, $logger, $request); + private function buildIMIPMessage(string $method): Message { $message = new Message(); - $message->method = 'CANCEL'; + $message->method = $method; $message->message = new VCalendar(); $message->message->add('VEVENT', [ 'UID' => $message->uid, @@ -148,13 +102,14 @@ public function testDeliveryOfCancel() { ]); $message->sender = 'mailto:gandalf@wiz.ard'; $message->recipient = 'mailto:frodo@hobb.it'; + return $message; + } - $plugin->schedule($message); - $this->assertEquals('1.1', $message->getScheduleStatus()); - $this->assertEquals('Cancelled: Fellowship meeting', $mailMessage->getSubject()); - $this->assertEquals(['frodo@hobb.it' => null], $mailMessage->getTo()); - $this->assertEquals(['gandalf@wiz.ard' => null], $mailMessage->getReplyTo()); - $this->assertEquals('text/calendar; charset=UTF-8; method=CANCEL', $mailMessage->getSwiftMessage()->getContentType()); - $this->assertEquals('CANCELLED', $message->message->VEVENT->STATUS->getValue()); + private function assertIMipState(Message $message, string $scheduleStatus, string $method, string $mailSubject): void { + $this->assertEquals($scheduleStatus, $message->getScheduleStatus()); + $this->assertEquals($mailSubject, $this->mailMessage->getSubject()); + $this->assertEquals(['frodo@hobb.it' => null], $this->mailMessage->getTo()); + $this->assertEquals(['gandalf@wiz.ard' => null], $this->mailMessage->getReplyTo()); + $this->assertStringContainsString("text/calendar; charset=UTF-8; method=$method", $this->mailMessage->getMessage()->getBody()->bodyToString()); } } diff --git a/composer.json b/composer.json index 78fb861248a4..ff77ab799f58 100644 --- a/composer.json +++ b/composer.json @@ -1,118 +1,118 @@ { - "name": "owncloud/core", - "description": "A safe home for all your data", - "license": "AGPL-1.0", - "config" : { - "vendor-dir": "lib/composer", - "optimize-autoloader": true, - "classmap-authoritative": false, - "platform": { - "php": "7.4" - }, - "allow-plugins": { - "bamarni/composer-bin-plugin": true, - "dg/composer-cleaner": true - } + "name": "owncloud/core", + "description": "A safe home for all your data", + "license": "AGPL-1.0", + "config" : { + "vendor-dir": "lib/composer", + "optimize-autoloader": true, + "classmap-authoritative": false, + "platform": { + "php": "7.4" }, - "autoload" : { - "psr-4": { - "OC\\": "lib/private", - "OC\\Core\\": "core/", - "OC\\Settings\\": "settings/", - "OCP\\": "lib/public" - }, - "classmap": ["lib/private/legacy"], - "files": [ - "apps/files_external/lib/config.php" - ] - - }, - "autoload-dev" : { - "files": [ - "tests/lib/TestCase.php" - ] - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.8", - "mikey179/vfsstream": "^1.6", - "phpunit/phpunit": "^9.5", - "roave/security-advisories": "dev-latest" - }, - "require": { - "php": ">=7.4", - "ext-apcu": "*", - "ext-ctype": "*", - "ext-curl": "*", - "ext-exif": "*", - "ext-fileinfo": "*", - "ext-gd": "*", - "ext-iconv": "*", - "ext-imagick": "*", - "ext-intl": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-memcached": "*", - "ext-pdo": "*", - "ext-posix": "*", - "ext-simplexml": "*", - "ext-zip": "*", - "bantu/ini-get-wrapper": "v1.0.1", - "christophwurst/id3parser": "^0.1.4", - "composer/semver": "^3.3", - "deepdiver/zipstreamer": "^2.0", - "dg/composer-cleaner": "^2.2", - "doctrine/dbal": "^2.13", - "firebase/php-jwt": "^6.8", - "google/apiclient": "^2.15", - "guzzlehttp/guzzle": "^7.7", - "icewind/smb": "^3.6", - "icewind/streams": "0.7.7", - "interfasys/lognormalizer": "^v1.0", - "laminas/laminas-inputfilter": "^2.21", - "laminas/laminas-servicemanager": "^3.17", - "laminas/laminas-validator": "^2.25", - "laravel/serializable-closure": "^1.3", - "league/flysystem": "^1.1", - "nikic/php-parser": "^4.15", - "owncloud/tarstreamer": "v2.1.0", - "pear/archive_tar": "1.4.14", - "pear/pear-core-minimal": "^v1.10", - "phpseclib/phpseclib": "^3.0", - "pimple/pimple": "^3.5", - "punic/punic": "^3.8", - "sabre/dav": "^4.4", - "sabre/http": "^5.1", - "sabre/vobject": "^4.5", - "swiftmailer/swiftmailer": "^6.3", - "symfony/console": "^5.4", - "symfony/event-dispatcher": "^5.4", - "symfony/process": "^5.4", - "symfony/routing": "^5.4", - "symfony/translation": "^5.4" + "allow-plugins": { + "bamarni/composer-bin-plugin": true, + "dg/composer-cleaner": true + } + }, + "autoload" : { + "psr-4": { + "OC\\": "lib/private", + "OC\\Core\\": "core/", + "OC\\Settings\\": "settings/", + "OCP\\": "lib/public" }, - "replace": { - "symfony/polyfill-ctype": "*", - "symfony/polyfill-iconv": "*", - "symfony/polyfill-intl-grapheme": "*", - "symfony/polyfill-intl-idn": "*", - "symfony/polyfill-intl-normalizer": "*", - "symfony/polyfill-mbstring": "*", - "symfony/polyfill-php72": "*", - "symfony/polyfill-php73": "*" + "classmap": ["lib/private/legacy"], + "files": [ + "apps/files_external/lib/config.php" + ] + + }, + "autoload-dev" : { + "files": [ + "tests/lib/TestCase.php" + ] + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8", + "mikey179/vfsstream": "^1.6", + "phpunit/phpunit": "^9.5", + "roave/security-advisories": "dev-latest" + }, + "require": { + "php": ">=7.4", + "ext-apcu": "*", + "ext-ctype": "*", + "ext-curl": "*", + "ext-exif": "*", + "ext-fileinfo": "*", + "ext-gd": "*", + "ext-iconv": "*", + "ext-imagick": "*", + "ext-intl": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-memcached": "*", + "ext-pdo": "*", + "ext-posix": "*", + "ext-simplexml": "*", + "ext-zip": "*", + "bantu/ini-get-wrapper": "v1.0.1", + "christophwurst/id3parser": "^0.1.4", + "composer/semver": "^3.3", + "deepdiver/zipstreamer": "^2.0", + "dg/composer-cleaner": "^2.2", + "doctrine/dbal": "^2.13", + "firebase/php-jwt": "^6.8", + "google/apiclient": "^2.15", + "guzzlehttp/guzzle": "^7.7", + "icewind/smb": "^3.6", + "icewind/streams": "0.7.7", + "interfasys/lognormalizer": "^v1.0", + "laminas/laminas-inputfilter": "^2.21", + "laminas/laminas-servicemanager": "^3.17", + "laminas/laminas-validator": "^2.25", + "laravel/serializable-closure": "^1.3", + "league/flysystem": "^1.1", + "nikic/php-parser": "^4.15", + "owncloud/tarstreamer": "v2.1.0", + "pear/archive_tar": "1.4.14", + "pear/pear-core-minimal": "^v1.10", + "phpseclib/phpseclib": "^3.0", + "pimple/pimple": "^3.5", + "punic/punic": "^3.8", + "sabre/dav": "^4.4", + "sabre/http": "^5.1", + "sabre/vobject": "^4.5", + "symfony/console": "^5.4", + "symfony/event-dispatcher": "^5.4", + "symfony/mailer": "^5.4", + "symfony/process": "^5.4", + "symfony/routing": "^5.4", + "symfony/translation": "^5.4" + }, + "replace": { + "symfony/polyfill-ctype": "*", + "symfony/polyfill-iconv": "*", + "symfony/polyfill-intl-grapheme": "*", + "symfony/polyfill-intl-idn": "*", + "symfony/polyfill-intl-normalizer": "*", + "symfony/polyfill-mbstring": "*", + "symfony/polyfill-php72": "*", + "symfony/polyfill-php73": "*" + }, + "scripts": { + "pre-autoload-dump": "Google\\Task\\Composer::cleanup" + }, + "extra": { + "bamarni-bin": { + "bin-links": false }, - "scripts": { - "pre-autoload-dump": "Google\\Task\\Composer::cleanup" + "cleaner-ignore": { + "phpunit/phpunit": true }, - "extra": { - "bamarni-bin": { - "bin-links": false - }, - "cleaner-ignore": { - "phpunit/phpunit": true - }, - "google/apiclient-services": [ - "Drive" - ] - } + "google/apiclient-services": [ + "Drive" + ] + } } diff --git a/composer.lock b/composer.lock index 2aa47439088a..b08904ec1d93 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a05f0ad17290b0a59773026392b39fa7", + "content-hash": "949e22ab13538c7505cdb4307e2d9f35", "packages": [ { "name": "bantu/ini-get-wrapper", @@ -3742,82 +3742,6 @@ }, "time": "2023-06-28T12:56:05+00:00" }, - { - "name": "swiftmailer/swiftmailer", - "version": "v6.3.0", - "source": { - "type": "git", - "url": "https://github.com/swiftmailer/swiftmailer.git", - "reference": "8a5d5072dca8f48460fce2f4131fcc495eec654c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/8a5d5072dca8f48460fce2f4131fcc495eec654c", - "reference": "8a5d5072dca8f48460fce2f4131fcc495eec654c", - "shasum": "" - }, - "require": { - "egulias/email-validator": "^2.0|^3.1", - "php": ">=7.0.0", - "symfony/polyfill-iconv": "^1.0", - "symfony/polyfill-intl-idn": "^1.10", - "symfony/polyfill-mbstring": "^1.0" - }, - "require-dev": { - "mockery/mockery": "^1.0", - "symfony/phpunit-bridge": "^4.4|^5.4" - }, - "suggest": { - "ext-intl": "Needed to support internationalized email addresses" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "6.2-dev" - } - }, - "autoload": { - "files": [ - "lib/swift_required.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Chris Corbyn" - }, - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - } - ], - "description": "Swiftmailer, free feature-rich PHP mailer", - "homepage": "https://swiftmailer.symfony.com", - "keywords": [ - "email", - "mail", - "mailer" - ], - "support": { - "issues": "https://github.com/swiftmailer/swiftmailer/issues", - "source": "https://github.com/swiftmailer/swiftmailer/tree/v6.3.0" - }, - "funding": [ - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/swiftmailer/swiftmailer", - "type": "tidelift" - } - ], - "abandoned": "symfony/mailer", - "time": "2021-10-18T15:26:12+00:00" - }, { "name": "symfony/console", "version": "v5.4.28", @@ -4148,6 +4072,166 @@ ], "time": "2022-01-02T09:53:40+00:00" }, + { + "name": "symfony/mailer", + "version": "v5.4.31", + "source": { + "type": "git", + "url": "https://github.com/symfony/mailer.git", + "reference": "5ca8a7628a5ee69767047dd0f4cf4c9521c999b8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mailer/zipball/5ca8a7628a5ee69767047dd0f4cf4c9521c999b8", + "reference": "5ca8a7628a5ee69767047dd0f4cf4c9521c999b8", + "shasum": "" + }, + "require": { + "egulias/email-validator": "^2.1.10|^3|^4", + "php": ">=7.2.5", + "psr/event-dispatcher": "^1", + "psr/log": "^1|^2|^3", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/event-dispatcher": "^4.4|^5.0|^6.0", + "symfony/mime": "^5.2.6|^6.0", + "symfony/polyfill-php80": "^1.16", + "symfony/service-contracts": "^1.1|^2|^3" + }, + "conflict": { + "symfony/http-kernel": "<4.4" + }, + "require-dev": { + "symfony/http-client": "^4.4|^5.0|^6.0", + "symfony/messenger": "^4.4|^5.0|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mailer\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Helps sending emails", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/mailer/tree/v5.4.31" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-11-03T16:16:43+00:00" + }, + { + "name": "symfony/mime", + "version": "v5.4.26", + "source": { + "type": "git", + "url": "https://github.com/symfony/mime.git", + "reference": "2ea06dfeee20000a319d8407cea1d47533d5a9d2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/mime/zipball/2ea06dfeee20000a319d8407cea1d47533d5a9d2", + "reference": "2ea06dfeee20000a319d8407cea1d47533d5a9d2", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-intl-idn": "^1.10", + "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php80": "^1.16" + }, + "conflict": { + "egulias/email-validator": "~3.0.0", + "phpdocumentor/reflection-docblock": "<3.2.2", + "phpdocumentor/type-resolver": "<1.4.0", + "symfony/mailer": "<4.4", + "symfony/serializer": "<5.4.26|>=6,<6.2.13|>=6.3,<6.3.2" + }, + "require-dev": { + "egulias/email-validator": "^2.1.10|^3.1|^4", + "phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/property-access": "^4.4|^5.1|^6.0", + "symfony/property-info": "^4.4|^5.1|^6.0", + "symfony/serializer": "^5.4.26|~6.2.13|^6.3.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Mime\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Allows manipulating MIME messages", + "homepage": "https://symfony.com", + "keywords": [ + "mime", + "mime-type" + ], + "support": { + "source": "https://github.com/symfony/mime/tree/v5.4.26" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-07-27T06:29:31+00:00" + }, { "name": "symfony/polyfill-php80", "version": "v1.28.0", @@ -7231,5 +7315,5 @@ "platform-overrides": { "php": "7.4" }, - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/composer.phar b/composer.phar new file mode 100755 index 000000000000..553efcce527c Binary files /dev/null and b/composer.phar differ diff --git a/config/config.sample.php b/config/config.sample.php index 0e6480cbf059..5d388f3a96e7 100644 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -445,7 +445,7 @@ /** * Define the SMTP security style - * Depends on `mail_smtpmode`. Specify when you are using `ssl` or `tls`. + * Depends on `mail_smtpmode`. Specify when you are using `ssl` or not. * Leave empty for no encryption. */ 'mail_smtpsecure' => '', @@ -456,13 +456,6 @@ */ 'mail_smtpauth' => false, -/** - * Define the SMTP authentication type - * Depends on `mail_smtpmode`. If SMTP authentication is required, - * choose the authentication type as `LOGIN` (default) or `PLAIN`. - */ -'mail_smtpauthtype' => 'LOGIN', - /** * Define the SMTP authentication username * Depends on `mail_smtpauth`. Specify the username for authenticating to the SMTP server. diff --git a/lib/private/Mail/Logger.php b/lib/private/Mail/Logger.php new file mode 100644 index 000000000000..17b51e36a39b --- /dev/null +++ b/lib/private/Mail/Logger.php @@ -0,0 +1,20 @@ +log[] = [$level, $message, $context]; + } + + /** + * @throws \JsonException + */ + public function toJSON(): string { + return json_encode($this->log, JSON_THROW_ON_ERROR); + } +} diff --git a/lib/private/Mail/Mailer.php b/lib/private/Mail/Mailer.php index 62b33d89f22d..8a5f3eed175c 100644 --- a/lib/private/Mail/Mailer.php +++ b/lib/private/Mail/Mailer.php @@ -23,9 +23,17 @@ use Egulias\EmailValidator\EmailValidator; use Egulias\EmailValidator\Validation\RFCValidation; +use OC_Defaults; use OCP\IConfig; use OCP\Mail\IMailer; use OCP\ILogger; +use Psr\Log\LoggerInterface; +use Symfony\Component\Mailer\Exception\TransportExceptionInterface; +use Symfony\Component\Mailer\Transport\SendmailTransport; +use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport; +use Symfony\Component\Mailer\Transport\Smtp\Stream\SocketStream; +use Symfony\Component\Mailer\Transport\TransportInterface; +use Symfony\Component\Mime\Email; /** * Class Mailer provides some basic functions to create a mail message that can be used in combination with @@ -46,24 +54,15 @@ * @package OC\Mail */ class Mailer implements IMailer { - /** @var \Swift_SmtpTransport|\Swift_SendmailTransport Cached transport */ - private $instance = null; - /** @var IConfig */ - private $config; - /** @var ILogger */ - private $logger; - /** @var \OC_Defaults */ - private $defaults; + private ?TransportInterface $instance = null; + private IConfig $config; + private ILogger $logger; + private OC_Defaults $defaults; - /** - * @param IConfig $config - * @param ILogger $logger - * @param \OC_Defaults $defaults - */ public function __construct( IConfig $config, ILogger $logger, - \OC_Defaults $defaults + OC_Defaults $defaults ) { $this->config = $config; $this->logger = $logger; @@ -72,11 +71,9 @@ public function __construct( /** * Creates a new message object that can be passed to send() - * - * @return Message */ - public function createMessage() { - return new Message(new \Swift_Message()); + public function createMessage(): Message { + return new Message(new Email()); } /** @@ -89,18 +86,34 @@ public function createMessage() { * @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) { - $debugMode = $this->config->getSystemValue('mail_smtpdebug', false); - + public function send(Message $message): array { if (!\is_array($message->getFrom()) || \count($message->getFrom()) === 0) { $message->setFrom([\OCP\Util::getDefaultEmailAddress($this->defaults->getName())]); } - $failedRecipients = []; - - $mailer = $this->getInstance(); - - $mailer->send($message->getSwiftMessage(), $failedRecipients); + $debugMode = $this->config->getSystemValue('mail_smtpdebug', false); + $logger = $debugMode ? new Logger() : null; + + try { + $this->getInstance($logger ?? null)->send($message->getMessage()); + } catch (TransportExceptionInterface $e) { + # in case of exception it is expected that none of the mails has been sent + $failedRecipients = []; + + $recipients = array_merge($message->getTo(), $message->getCc(), $message->getBcc()); + array_walk($recipients, static function ($value, $key) use (&$failedRecipients) { + if (is_numeric($key)) { + $failedRecipients[] = $value; + } else { + $failedRecipients[] = $key; + } + }); + + $this->logger->logException($e, ['failed-recipients' => $recipients]); + + # list of failed recipients is not added by intention to not accidentally disclose private data + throw new \RuntimeException("Failed to deliver email", 0, $e); + } $allRecipients = []; if (!empty($message->getTo())) { @@ -119,10 +132,11 @@ public function send(Message $message) { 'app' => 'core', 'from' => \json_encode($message->getFrom()), 'recipients' => \json_encode($allRecipients), - 'subject' => $message->getSubject() + 'subject' => $message->getSubject(), + 'mail_log' => ($logger !== null) ? $logger->toJSON() : null, ]); - return $failedRecipients; + return []; } /** @@ -131,9 +145,9 @@ public function send(Message $message) { * @param string $email Email address to be validated * @return bool True if the mail address is valid, false otherwise */ - public function validateMailAddress($email) { - $validator = new EmailValidator(); - return $validator->isValid($this->convertEmail($email), new RFCValidation()); + public function validateMailAddress(string $email): bool { + return (new EmailValidator()) + ->isValid($this->convertEmail($email), new RFCValidation()); } /** @@ -144,12 +158,12 @@ public function validateMailAddress($email) { * @param string $email * @return string Converted mail address if `idn_to_ascii` exists */ - protected function convertEmail($email) { + protected function convertEmail(string $email): string { if (!\function_exists('idn_to_ascii') || \strpos($email, '@') === false) { return $email; } - list($name, $domain) = \explode('@', $email, 2); + [$name, $domain] = \explode('@', $email, 2); if (\defined('INTL_IDNA_VARIANT_UTS46')) { $domain = \idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46); } else { @@ -158,80 +172,50 @@ protected function convertEmail($email) { return $name.'@'.$domain; } - /** - * Returns whatever transport is configured within the config - * - * @return \Swift_SmtpTransport|\Swift_SendmailTransport - */ - protected function getInstance() { + protected function getInstance(?LoggerInterface $logger = null): TransportInterface { if ($this->instance !== null) { return $this->instance; } $mailMode = $this->config->getSystemValue('mail_smtpmode', 'php'); if ($mailMode === 'smtp') { - $instance = $this->getSmtpInstance(); + $transport = $this->getSmtpInstance($logger ?? null); } else { - // FIXME: Move into the return statement but requires proper testing - // for SMTP and mail as well. Thus not really doable for a - // minor release. - $instance = new \Swift_Mailer($this->getSendMailInstance()); - } - - // Register plugins - - // Enable logger if debug mode is enabled - if ($this->config->getSystemValue('mail_smtpdebug', false)) { - $mailLogger = new \Swift_Plugins_Loggers_ArrayLogger(); - $instance->registerPlugin(new \Swift_Plugins_LoggerPlugin($mailLogger)); + $transport = $this->getSendMailInstance($logger ?? null); } - // Enable antiflood on smtp connection (defaults to 100 mails before reconnect) - $instance->registerPlugin(new \Swift_Plugins_AntiFloodPlugin()); - - $this->instance = $instance; + $this->instance = $transport; return $this->instance; } - /** - * Returns the SMTP transport - * - * @return \Swift_SmtpTransport - */ - protected function getSmtpInstance() { - $transport = new \Swift_SmtpTransport(); - $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)); + protected function getSmtpInstance(?LoggerInterface $logger): EsmtpTransport { + $timeout = $this->config->getSystemValue('mail_smtptimeout', 10); + $host = $this->config->getSystemValue('mail_smtphost', '127.0.0.1'); + $port = $this->config->getSystemValue('mail_smtpport', 25); + $smtpSecurity = $this->config->getSystemValue('mail_smtpsecure', ''); + $tls = $smtpSecurity === 'ssl' ? true : null; + $transport = new EsmtpTransport($host, $port, $tls, null, $logger); 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); + $stream = $transport->getStream(); + if ($stream instanceof SocketStream) { + $stream->setTimeout($timeout); } - $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; + protected function getSendMailInstance(?LoggerInterface $logger = null): SendmailTransport { + $i = $this->config->getSystemValue('mail_smtpmode', 'sendmail'); + if ($i === 'qmail') { + $binaryPath = '/var/qmail/bin/sendmail'; + } else { + $binaryPath = '/usr/sbin/sendmail'; } - return new \Swift_SendmailTransport($binaryPath . ' -bs'); + return new SendmailTransport($binaryPath . ' -bs', null, $logger); } } diff --git a/lib/private/Mail/Message.php b/lib/private/Mail/Message.php index 12633ff76ad6..88ce719abf07 100644 --- a/lib/private/Mail/Message.php +++ b/lib/private/Mail/Message.php @@ -22,55 +22,38 @@ namespace OC\Mail; -use Swift_Message; +use Symfony\Component\Mime\Address; +use Symfony\Component\Mime\Email; /** - * Class Message provides a wrapper around SwiftMail + * Class Message provides a wrapper around Symfony\Component\Mime\Email * * @package OC\Mail */ class Message { - /** @var Swift_Message */ - private $swiftMessage; + private Email $message; + private array $from = []; + private array $replyTo = []; + private array $to = []; + private array $cc = []; + private array $bcc = []; - /** - * @param Swift_Message $swiftMessage - */ - public function __construct(Swift_Message $swiftMessage) { - $this->swiftMessage = $swiftMessage; + public function __construct(Email $swiftMessage) { + $this->message = $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 + * @return Address[] Converted addresses if `idn_to_ascii` exists */ - protected function convertAddresses($addresses) { - if (!\function_exists('idn_to_ascii')) { - return $addresses; - } - + protected function convertAddresses(array $addresses): array { $convertedAddresses = []; foreach ($addresses as $email => $readableName) { - if (!\is_numeric($email)) { - list($name, $domain) = \explode('@', $email, 2); - if (\defined('INTL_IDNA_VARIANT_UTS46')) { - $domain = \idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46); - } else { - $domain = \idn_to_ascii($domain); - } - $convertedAddresses[$name.'@'.$domain] = $readableName; + if (\is_numeric($email)) { + $convertedAddresses[] = new Address($readableName); } else { - list($name, $domain) = \explode('@', $readableName, 2); - if (\defined('INTL_IDNA_VARIANT_UTS46')) { - $domain = \idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46); - } else { - $domain = \idn_to_ascii($domain); - } - $convertedAddresses[$email] = $name.'@'.$domain; + $convertedAddresses[] = new Address($email, $readableName ?? ''); } } @@ -83,12 +66,11 @@ protected function convertAddresses($addresses) { * 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); + public function setFrom(array $addresses): Message { + $this->message->from(...$this->convertAddresses($addresses)); - $this->swiftMessage->setFrom($addresses); + $this->from = $addresses; return $this; } @@ -97,42 +79,36 @@ public function setFrom(array $addresses) { * * @return array */ - public function getFrom() { - return $this->swiftMessage->getFrom(); + public function getFrom(): array { + return $this->from; } /** * Set the Reply-To address of this message - * - * @param array $addresses - * @return $this */ - public function setReplyTo(array $addresses) { - $addresses = $this->convertAddresses($addresses); + public function setReplyTo(array $addresses): Message { + $this->message->replyTo(...$this->convertAddresses($addresses)); - $this->swiftMessage->setReplyTo($addresses); + $this->replyTo = $addresses; return $this; } /** * Returns the Reply-To address of this message - * - * @return array */ - public function getReplyTo() { - return $this->swiftMessage->getReplyTo(); + public function getReplyTo(): array { + return $this->replyTo; } /** - * Set the to addresses of this message. + * 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); + public function setTo(array $recipients): Message { + $this->message->to(...$this->convertAddresses($recipients)); - $this->swiftMessage->setTo($recipients); + $this->to = $recipients; return $this; } @@ -141,82 +117,68 @@ public function setTo(array $recipients) { * * @return array */ - public function getTo() { - return $this->swiftMessage->getTo(); + public function getTo(): array { + return $this->to; } /** * 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); + public function setCc(array $recipients): Message { + $this->message->cc(...$this->convertAddresses($recipients)); - $this->swiftMessage->setCc($recipients); + $this->cc = $recipients; return $this; } /** * Get the cc address of this message. - * - * @return array */ - public function getCc() { - return $this->swiftMessage->getCc(); + public function getCc(): array { + return $this->cc; } /** * 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); + public function setBcc(array $recipients): Message { + $this->message->bcc(...$this->convertAddresses($recipients)); - $this->swiftMessage->setBcc($recipients); + $this->bcc = $recipients; return $this; } /** * Get the Bcc address of this message. - * - * @return array */ - public function getBcc() { - return $this->swiftMessage->getBcc(); + public function getBcc(): array { + return $this->bcc; } /** * Set the subject of this message. - * - * @param $subject - * @return $this */ - public function setSubject($subject) { - $this->swiftMessage->setSubject($subject); + public function setSubject(string $subject): Message { + $this->message->subject($subject); return $this; } /** - * Get the from subject of this message. - * - * @return string + * Get the subject of this message. */ - public function getSubject() { - return $this->swiftMessage->getSubject(); + public function getSubject(): string { + return $this->message->getSubject(); } /** * Set the plain-text body of this message. - * - * @param string $body - * @return $this */ - public function setPlainBody($body) { - $this->swiftMessage->setBody($body); + public function setPlainBody(string $body): Message { + $this->message->text($body); return $this; } @@ -225,8 +187,8 @@ public function setPlainBody($body) { * * @return string */ - public function getPlainBody() { - return $this->swiftMessage->getBody(); + public function getPlainBody(): string { + return $this->message->getTextBody() ?? ''; } /** @@ -235,26 +197,27 @@ public function getPlainBody() { * @param string $body * @return $this */ - public function setHtmlBody($body) { - $this->swiftMessage->addPart($body, 'text/html'); + public function setHtmlBody(string $body): Message { + $this->message->html($body); return $this; } - /** - * Get's the underlying SwiftMessage - * @return Swift_Message - */ - public function getSwiftMessage() { - return $this->swiftMessage; + public function getMessage(): Email { + return $this->message; } - /** - * @param string $body - * @param string $contentType - * @return $this - */ - public function setBody($body, $contentType) { - $this->swiftMessage->setBody($body, $contentType); + public function setBody(string $body, string $contentType): Message { + if ($contentType === 'text/html') { + $this->message->html($body); + } else { + $this->message->text($body); + } + + return $this; + } + + public function attach($body, string $name = null, string $contentType = null): self { + $this->message->attach($body, $name, $contentType); return $this; } } diff --git a/lib/public/Mail/IMailer.php b/lib/public/Mail/IMailer.php index 333e961d071c..070dc42737ea 100644 --- a/lib/public/Mail/IMailer.php +++ b/lib/public/Mail/IMailer.php @@ -51,7 +51,7 @@ interface IMailer { * @return Message * @since 8.1.0 */ - public function createMessage(); + public function createMessage(): Message; /** * Send the specified message. Also sets the from address to the value defined in config.php @@ -64,7 +64,7 @@ public function createMessage(); * has been supplied.) * @since 8.1.0 */ - public function send(Message $message); + public function send(Message $message): array; /** * Checks if an e-mail address is valid @@ -73,5 +73,5 @@ public function send(Message $message); * @return bool True if the mail address is valid, false otherwise * @since 8.1.0 */ - public function validateMailAddress($email); + public function validateMailAddress(string $email): bool; } diff --git a/settings/Controller/MailSettingsController.php b/settings/Controller/MailSettingsController.php index 9e6e3287e1d4..9f89b3e311d2 100644 --- a/settings/Controller/MailSettingsController.php +++ b/settings/Controller/MailSettingsController.php @@ -85,7 +85,6 @@ public function __construct( * @param string $mail_smtpmode * @param string $mail_smtpsecure * @param string $mail_smtphost - * @param string $mail_smtpauthtype * @param int $mail_smtpauth * @param string $mail_smtpport * @return array @@ -96,7 +95,6 @@ public function setMailSettings( $mail_smtpmode, $mail_smtpsecure, $mail_smtphost, - $mail_smtpauthtype, $mail_smtpauth, $mail_smtpport ) { @@ -162,36 +160,35 @@ public function sendTestMail() { $email = $this->userSession->getUser()->getEMailAddress(); } - if (!empty($email)) { - try { - $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 [ - 'data' => [ - 'message' => (string) $this->l10n->t('A problem occurred while sending the email. Please revise your settings. (Error: %s)', [$e->getMessage()]), - ], - 'status' => 'error', - ]; - } + if (empty($email)) { + return ['data' => + ['message' => + (string) $this->l10n->t('You need to set your user email before being able to send test emails.'), + ], + 'status' => 'error' + ]; + } + try { + $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); return ['data' => ['message' => (string) $this->l10n->t('Email sent') ], 'status' => 'success' ]; + } catch (\Exception $e) { + 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 ['data' => - ['message' => - (string) $this->l10n->t('You need to set your user email before being able to send test emails.'), - ], - 'status' => 'error' - ]; } } diff --git a/settings/Panels/Admin/Mail.php b/settings/Panels/Admin/Mail.php index 4a86cb8b34fc..0c75b63a9f61 100644 --- a/settings/Panels/Admin/Mail.php +++ b/settings/Panels/Admin/Mail.php @@ -61,7 +61,6 @@ public function getPanel() { $template->assign('mail_smtpsecure', $this->config->getSystemValue("mail_smtpsecure", '')); $template->assign('mail_smtphost', $this->config->getSystemValue("mail_smtphost", '')); $template->assign('mail_smtpport', $this->config->getSystemValue("mail_smtpport", '')); - $template->assign('mail_smtpauthtype', $this->config->getSystemValue("mail_smtpauthtype", '')); $template->assign('mail_smtpauth', $this->config->getSystemValue("mail_smtpauth", false)); $template->assign('mail_smtpname', $this->config->getSystemValue("mail_smtpname", '')); $template->assign('mail_user_email', $this->userSession->getUser()->getEMailAddress()); diff --git a/settings/templates/panels/admin/mail.php b/settings/templates/panels/admin/mail.php index 8604fb4d9a25..45394de5ffc9 100644 --- a/settings/templates/panels/admin/mail.php +++ b/settings/templates/panels/admin/mail.php @@ -1,15 +1,8 @@ $l->t('None'), - 'LOGIN' => $l->t('Login'), - 'PLAIN' => $l->t('Plain'), - 'NTLM' => $l->t('NT LAN Manager'), -]; $mail_smtpsecure = [ '' => $l->t('None'), 'ssl' => $l->t('SSL/TLS'), - 'tls' => $l->t('STARTTLS'), ]; $mail_smtpmode = [ 'php', @@ -80,17 +73,6 @@
> - - - container = $app->getContainer(); - $this->container['Config'] = $this->getMockBuilder('\OCP\IConfig') + $this->container['Config'] = $this->getMockBuilder(IConfig::class) ->disableOriginalConstructor()->getMock(); - $this->container['L10N'] = $this->getMockBuilder('\OCP\IL10N') + $this->container['L10N'] = $this->getMockBuilder(IL10N::class) ->disableOriginalConstructor()->getMock(); $this->container['AppName'] = 'settings'; - $this->container['UserSession'] = $this->getMockBuilder('\OC\User\Session') + $this->container['UserSession'] = $this->getMockBuilder(Session::class) ->disableOriginalConstructor()->getMock(); - $this->container['MailMessage'] = $this->getMockBuilder('\OCP\Mail\IMessage') - ->disableOriginalConstructor()->getMock(); - $this->container['Mailer'] = $this->getMockBuilder('\OC\Mail\Mailer') + $this->container['Mailer'] = $this->getMockBuilder(Mailer::class) ->setMethods(['send', 'validateMailAddress']) ->disableOriginalConstructor()->getMock(); - $this->container['Defaults'] = $this->getMockBuilder('\OC_Defaults') + $this->container['Defaults'] = $this->getMockBuilder(\OC_Defaults::class) ->disableOriginalConstructor()->getMock(); $this->container['DefaultMailAddress'] = 'no-reply@owncloud.com'; } - public function testSetInvalidMail() { + public function testSetInvalidMail(): void { $this->container['L10N'] ->expects($this->exactly(2)) ->method('t') - ->will($this->returnValue('Invalid email address')); + ->willReturn('Invalid email address'); $this->container['Mailer'] ->expects($this->exactly(2)) ->method('validateMailAddress') ->with('@@owncloud.com') - ->will($this->returnValue(false)); + ->willReturn(false); // With authentication $response = $this->container['MailSettingsController']->setMailSettings( @@ -77,54 +83,26 @@ public function testSetInvalidMail() { 0, '25' ); - $expectedResponse = ['data' => ['message' =>'Invalid email address'], 'status' => 'error']; + $this->assertSame($expectedResponse, $response); } - public function testSetMailSettings() { + public function testSetMailSettings(): void { $this->container['L10N'] ->expects($this->exactly(2)) ->method('t') - ->will($this->returnValue('Saved')); - - /** - * FIXME: Use the following block once Jenkins uses PHPUnit >= 4.1 - */ - /* - $this->container['Config'] - ->expects($this->exactly(15)) - ->method('setSystemValue') - ->withConsecutive( - array($this->equalTo('mail_domain'), $this->equalTo('owncloud.com')), - array($this->equalTo('mail_from_address'), $this->equalTo('demo')), - array($this->equalTo('mail_smtpmode'), $this->equalTo('smtp')), - array($this->equalTo('mail_smtpsecure'), $this->equalTo('ssl')), - array($this->equalTo('mail_smtphost'), $this->equalTo('mx.owncloud.com')), - array($this->equalTo('mail_smtpauthtype'), $this->equalTo('NTLM')), - array($this->equalTo('mail_smtpauth'), $this->equalTo(1)), - array($this->equalTo('mail_smtpport'), $this->equalTo('25')), - array($this->equalTo('mail_domain'), $this->equalTo('owncloud.com')), - array($this->equalTo('mail_from_address'), $this->equalTo('demo@owncloud.com')), - array($this->equalTo('mail_smtpmode'), $this->equalTo('smtp')), - array($this->equalTo('mail_smtpsecure'), $this->equalTo('ssl')), - array($this->equalTo('mail_smtphost'), $this->equalTo('mx.owncloud.com')), - array($this->equalTo('mail_smtpauthtype'), $this->equalTo('NTLM')), - array($this->equalTo('mail_smtpport'), $this->equalTo('25')) - ); - */ + ->willReturn('Saved'); $this->container['Mailer'] ->expects($this->exactly(2)) ->method('validateMailAddress') ->with('demo@owncloud.com') - ->will($this->returnValue(true)); + ->willReturn(true); - /** @var \PHPUnit\Framework\MockObject\MockObject $config */ + /** @var MockObject $config */ $config = $this->container['Config']; $config->expects($this->exactly(2)) - ->method('setSystemValues'); - /** - * FIXME: Use the following block once Jenkins uses PHPUnit >= 4.1 + ->method('setSystemValues') ->withConsecutive( [[ 'mail_domain' => 'owncloud.com', @@ -132,7 +110,6 @@ public function testSetMailSettings() { 'mail_smtpmode' => 'smtp', 'mail_smtpsecure' => 'ssl', 'mail_smtphost' => 'mx.owncloud.com', - 'mail_smtpauthtype' => 'NTLM', 'mail_smtpauth' => 1, 'mail_smtpport' => '25', ]], @@ -142,14 +119,12 @@ public function testSetMailSettings() { 'mail_smtpmode' => 'smtp', 'mail_smtpsecure' => 'ssl', 'mail_smtphost' => 'mx.owncloud.com', - 'mail_smtpauthtype' => 'NTLM', 'mail_smtpauth' => null, - 'mail_smtpport' => '25', + 'mail_smtpport' => '587', 'mail_smtpname' => null, - 'mail_smtppassword' => null, + 'mail_smtppassword' => null ]] ); - */ // With authentication $response = $this->container['MailSettingsController']->setMailSettings( @@ -158,7 +133,6 @@ public function testSetMailSettings() { 'smtp', 'ssl', 'mx.owncloud.com', - 'NTLM', 1, '25' ); @@ -172,19 +146,17 @@ public function testSetMailSettings() { 'smtp', 'ssl', 'mx.owncloud.com', - 'NTLM', 0, - '25' + '587' ); - $expectedResponse = ['data' => ['message' =>'Saved'], 'status' => 'success']; $this->assertSame($expectedResponse, $response); } - public function testStoreCredentials() { + public function testStoreCredentials(): void { $this->container['L10N'] ->expects($this->once()) ->method('t') - ->will($this->returnValue('Saved')); + ->willReturn('Saved'); $this->container['Config'] ->expects($this->once()) @@ -200,47 +172,43 @@ public function testStoreCredentials() { $this->assertSame($expectedResponse, $response); } - public function testSendTestMail() { - $user = $this->getMockBuilder('\OC\User\User') + public function testSendTestMail(): void { + $user = $this->getMockBuilder(User::class) ->disableOriginalConstructor() ->getMock(); - $user->expects($this->any()) + $user ->method('getUID') - ->will($this->returnValue('Werner')); - $user->expects($this->any()) + ->willReturn('Werner'); + $user ->method('getDisplayName') - ->will($this->returnValue('Werner Brösel')); + ->willReturn('Werner Brösel'); $this->container['L10N'] - ->expects($this->any()) ->method('t') - ->will( - $this->returnValueMap( - [ - ['You need to set your user email before being able to send test emails.', [], - 'You need to set your user email before being able to send test emails.'], - ['A problem occurred while sending the e-mail. Please revisit your settings.', [], - 'A problem occurred while sending the e-mail. Please revisit your settings.'], - ['Email sent', [], 'Email sent'], - ['test email settings', [], 'test email settings'], - ['If you received this email, the settings seem to be correct.', [], - 'If you received this email, the settings seem to be correct.'] - ] - ) + ->willReturnMap( + [ + ['You need to set your user email before being able to send test emails.', [], + 'You need to set your user email before being able to send test emails.'], + ['A problem occurred while sending the e-mail. Please revisit your settings.', [], + 'A problem occurred while sending the e-mail. Please revisit your settings.'], + ['Email sent', [], 'Email sent'], + ['test email settings', [], 'test email settings'], + ['If you received this email, the settings seem to be correct.', [], + 'If you received this email, the settings seem to be correct.'] + ] ); $this->container['UserSession'] - ->expects($this->any()) ->method('getUser') - ->will($this->returnValue($user)); + ->willReturn($user); // Ensure that it fails when no mail address has been specified $response = $this->container['MailSettingsController']->sendTestMail(); $expectedResponse = ['data' => ['message' =>'You need to set your user email before being able to send test emails.'], 'status' => 'error']; $this->assertSame($expectedResponse, $response); - $user->expects($this->any()) + $user ->method('getEMailAddress') - ->will($this->returnValue('mail@example.invalid')); + ->willReturn('mail@example.invalid'); $response = $this->container['MailSettingsController']->sendTestMail(); $expectedResponse = ['data' => ['message' =>'Email sent'], 'status' => 'success']; $this->assertSame($expectedResponse, $response); diff --git a/tests/Settings/Controller/UsersControllerTest.php b/tests/Settings/Controller/UsersControllerTest.php index ebe2b64b1462..0ce11dd3bc0b 100644 --- a/tests/Settings/Controller/UsersControllerTest.php +++ b/tests/Settings/Controller/UsersControllerTest.php @@ -3530,6 +3530,10 @@ public function testSetPassword(): void { ->method('send') ->with($message) ->willReturn([]); + $l10n->method('t') + ->willReturnCallback(function ($text, $parameters = []) { + return \vsprintf($text, $parameters); + }); $result = $usersController->setPassword('fooBaZ1', 'foo', '123'); $this->assertEquals(new Http\JSONResponse(['status' => 'success']), $result); diff --git a/tests/acceptance/features/lib/AdminGeneralSettingsPage.php b/tests/acceptance/features/lib/AdminGeneralSettingsPage.php index 38c4f5cbf3cb..2815d420159e 100644 --- a/tests/acceptance/features/lib/AdminGeneralSettingsPage.php +++ b/tests/acceptance/features/lib/AdminGeneralSettingsPage.php @@ -43,7 +43,6 @@ class AdminGeneralSettingsPage extends OwncloudPage { protected $encryptionTypeId = 'mail_smtpsecure'; protected $mailFromAddressFieldId = 'mail_from_address'; protected $mailDomainFieldId = 'mail_domain'; - protected $authMethodTypeId = 'mail_smtpauthtype'; protected $authRequiredCheckboxXpath = '//label[@for="mail_smtpauth"]'; protected $authRequiredCheckboxId = 'mail_smtpauth'; protected $serverAddressFieldId = 'mail_smtphost'; @@ -89,8 +88,6 @@ public function setEmailServerSettings(Session $session, TableNode $emailSetting $this->fillField($this->mailFromAddressFieldId, $row['value']); } elseif ($row['setting'] === 'mail domain') { $this->fillField($this->mailDomainFieldId, $row['value']); - } elseif ($row['setting'] === 'authentication method') { - $this->selectFieldOption($this->authMethodTypeId, $row['value']); } elseif ($row['setting'] === 'authentication required') { $this->checkRequiredAuthentication($row['value']); } elseif ($row['setting'] === 'server address') { diff --git a/tests/lib/Mail/MailerTest.php b/tests/lib/Mail/MailerTest.php index fc02360b0a6b..d1b50f8ae1e3 100644 --- a/tests/lib/Mail/MailerTest.php +++ b/tests/lib/Mail/MailerTest.php @@ -8,19 +8,25 @@ namespace Test\Mail; +use Exception; use OC\Mail\Mailer; use OC_Defaults; use OCP\IConfig; use OCP\ILogger; +use PHPUnit\Framework\MockObject\MockObject; +use Symfony\Component\Mailer\Transport\SendmailTransport; +use Symfony\Component\Mime\Email; use Test\TestCase; use OC\Mail\Message; +use function array_merge; +use function json_encode; class MailerTest extends TestCase { - /** @var IConfig | \PHPUnit\Framework\MockObject\MockObject */ + /** @var IConfig | MockObject */ private $config; /** @var OC_Defaults */ private $defaults; - /** @var ILogger | \PHPUnit\Framework\MockObject\MockObject */ + /** @var ILogger | MockObject */ private $logger; /** @var Mailer */ private $mailer; @@ -42,11 +48,12 @@ public function testGetSendMailInstanceSendMail(): void { ->expects($this->once()) ->method('getSystemValue') ->with('mail_smtpmode', 'sendmail') - ->will($this->returnValue('sendmail')); + ->willReturn('sendmail'); + /** @var SendmailTransport $mailer */ $mailer = self::invokePrivate($this->mailer, 'getSendMailInstance'); - $this->assertInstanceOf(\Swift_SendmailTransport::class, $mailer); - $this->assertEquals('/usr/sbin/sendmail -bs', $mailer->getCommand()); + $this->assertInstanceOf(SendmailTransport::class, $mailer); + $this->assertEquals('/usr/sbin/sendmail -bs', self::invokePrivate($mailer, 'command')); } public function testGetSendMailInstanceSendMailQmail(): void { @@ -54,39 +61,33 @@ public function testGetSendMailInstanceSendMailQmail(): void { ->expects($this->once()) ->method('getSystemValue') ->with('mail_smtpmode', 'sendmail') - ->will($this->returnValue('qmail')); + ->willReturn('qmail'); $mailer = self::invokePrivate($this->mailer, 'getSendMailInstance'); - $this->assertInstanceOf(\Swift_SendmailTransport::class, $mailer); - $this->assertEquals('/var/qmail/bin/sendmail -bs', $mailer->getCommand()); + $this->assertInstanceOf(SendmailTransport::class, $mailer); + $this->assertEquals('/var/qmail/bin/sendmail -bs', self::invokePrivate($mailer, 'command')); } public function testGetInstanceDefault(): void { - $this->assertInstanceOf(\Swift_Mailer::class, self::invokePrivate($this->mailer, 'getInstance')); + $this->assertInstanceOf(SendmailTransport::class, self::invokePrivate($this->mailer, 'getInstance')); } public function testGetInstanceSendmail(): void { $this->config ->method('getSystemValue') - ->will($this->returnValue('sendmail')); + ->willReturn('sendmail'); - $this->assertInstanceOf(\Swift_Mailer::class, self::invokePrivate($this->mailer, 'getInstance')); + $this->assertInstanceOf(SendmailTransport::class, self::invokePrivate($this->mailer, 'getInstance')); } - public function testCreateMessage(): void { - $this->assertInstanceOf(Message::class, $this->mailer->createMessage()); - } - - /** - */ public function testSendInvalidMailException(): void { - $this->expectException(\Exception::class); + $this->expectException(Exception::class); - /** @var Message | \PHPUnit\Framework\MockObject\MockObject $message */ + /** @var Message | MockObject $message */ $message = $this->getMockBuilder(Message::class) ->disableOriginalConstructor()->getMock(); $message->expects($this->once()) - ->method('getSwiftMessage') - ->will($this->returnValue(new \Swift_Message())); + ->method('getMessage') + ->willReturn(new Email()); $this->mailer->send($message); } @@ -119,14 +120,14 @@ public function testLogEntry(): void { ->setMethods(['getInstance']) ->getMock(); - $this->mailer->method('getInstance')->willReturn($this->createMock(\Swift_SendmailTransport::class)); + $this->mailer->method('getInstance')->willReturn($this->createMock(SendmailTransport::class)); - /** @var Message | \PHPUnit\Framework\MockObject\MockObject $message */ + /** @var Message | MockObject $message */ $message = $this->getMockBuilder(Message::class) ->disableOriginalConstructor()->getMock(); $message->expects($this->once()) - ->method('getSwiftMessage') - ->will($this->returnValue(new \Swift_Message())); + ->method('getMessage') + ->willReturn(new Email()); $from = ['from@example.org' => 'From Address']; $to = ['to1@example.org' => 'To Address 1', 'to2@example.org' => 'To Address 2']; @@ -143,9 +144,10 @@ public function testLogEntry(): void { ->method('debug') ->with('Sent mail from "{from}" to "{recipients}" with subject "{subject}"', [ 'app' => 'core', - 'from' => \json_encode($from), - 'recipients' => \json_encode(\array_merge($to, $cc, $bcc)), - 'subject' => 'Email subject' + 'from' => json_encode($from), + 'recipients' => json_encode(array_merge($to, $cc, $bcc)), + 'subject' => 'Email subject', + 'mail_log' => null ]); $this->mailer->send($message); diff --git a/tests/lib/Mail/MessageTest.php b/tests/lib/Mail/MessageTest.php index 8fd493883ac5..cc4dbe64bc93 100644 --- a/tests/lib/Mail/MessageTest.php +++ b/tests/lib/Mail/MessageTest.php @@ -9,174 +9,68 @@ namespace Test\Mail; use OC\Mail\Message; -use Swift_Message; +use Symfony\Component\Mime\Address; +use Symfony\Component\Mime\Email; use Test\TestCase; class MessageTest extends TestCase { - /** @var Swift_Message */ - private $swiftMessage; - /** @var Message */ - private $message; - - /** - * @return array - */ - public function mailAddressProvider() { - 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@xn--wncld-iuae2c.com', 'lukäs@owncloud.xn--rg-eka' => 'Lükäs Réschke']], - [['lukas@öwnclöüd.com'], ['lukas@xn--wncld-iuae2c.com']] - ]; - } + private Email $symfonyMail; + private Message $message; public function setUp(): void { parent::setUp(); - $this->swiftMessage = $this->getMockBuilder('\Swift_Message') - ->disableOriginalConstructor()->getMock(); - - $this->message = new Message($this->swiftMessage); - } - - /** - * @requires function idn_to_ascii - * @dataProvider mailAddressProvider - * - * @param string $unconverted - * @param string $expected - */ - public function testConvertAddresses($unconverted, $expected) { - $this->assertSame($expected, self::invokePrivate($this->message, 'convertAddresses', [$unconverted])); + $this->symfonyMail = new Email(); + $this->message = new Message($this->symfonyMail); } - public function testSetFrom() { - $this->swiftMessage - ->expects($this->once()) - ->method('setFrom') - ->with(['lukas@owncloud.com']); + public function testFrom(): void { $this->message->setFrom(['lukas@owncloud.com']); - } - - public function testGetFrom() { - $this->swiftMessage - ->expects($this->once()) - ->method('getFrom') - ->will($this->returnValue(['lukas@owncloud.com'])); - $this->assertSame(['lukas@owncloud.com'], $this->message->getFrom()); + $this->assertEquals([new Address('lukas@owncloud.com')], $this->symfonyMail->getFrom()); } - public function testSetReplyTo() { - $this->swiftMessage - ->expects($this->once()) - ->method('setReplyTo') - ->with(['lukas@owncloud.com']); - $this->message->setReplyTo(['lukas@owncloud.com']); - } - - public function testGetReplyTo() { - $this->swiftMessage - ->expects($this->once()) - ->method('getReplyTo') - ->will($this->returnValue(['lukas@owncloud.com'])); - - $this->assertSame(['lukas@owncloud.com'], $this->message->getReplyTo()); - } - - public function testSetTo() { - $this->swiftMessage - ->expects($this->once()) - ->method('setTo') - ->with(['lukas@owncloud.com']); + public function testTo(): void { $this->message->setTo(['lukas@owncloud.com']); - } - - public function testGetTo() { - $this->swiftMessage - ->expects($this->once()) - ->method('getTo') - ->will($this->returnValue(['lukas@owncloud.com'])); - $this->assertSame(['lukas@owncloud.com'], $this->message->getTo()); + $this->assertEquals([new Address('lukas@owncloud.com')], $this->symfonyMail->getTo()); } - public function testSetCc() { - $this->swiftMessage - ->expects($this->once()) - ->method('setCc') - ->with(['lukas@owncloud.com']); - $this->message->setCc(['lukas@owncloud.com']); + public function testReplyTo(): void { + $this->message->setReplyTo(['lukas@owncloud.com']); + $this->assertSame(['lukas@owncloud.com'], $this->message->getReplyTo()); + $this->assertEquals([new Address('lukas@owncloud.com')], $this->symfonyMail->getReplyTo()); } - public function testGetCc() { - $this->swiftMessage - ->expects($this->once()) - ->method('getCc') - ->will($this->returnValue(['lukas@owncloud.com'])); - + public function testCC(): void { + $this->message->setCc(['lukas@owncloud.com']); $this->assertSame(['lukas@owncloud.com'], $this->message->getCc()); + $this->assertEquals([new Address('lukas@owncloud.com')], $this->symfonyMail->getCc()); } - public function testSetBcc() { - $this->swiftMessage - ->expects($this->once()) - ->method('setBcc') - ->with(['lukas@owncloud.com']); + public function testBCC(): void { $this->message->setBcc(['lukas@owncloud.com']); - } - - public function testGetBcc() { - $this->swiftMessage - ->expects($this->once()) - ->method('getBcc') - ->will($this->returnValue(['lukas@owncloud.com'])); - $this->assertSame(['lukas@owncloud.com'], $this->message->getBcc()); + $this->assertEquals([new Address('lukas@owncloud.com')], $this->symfonyMail->getBcc()); } - - public function testSetSubject() { - $this->swiftMessage - ->expects($this->once()) - ->method('setSubject') - ->with('Fancy Subject'); - + public function testSubject(): void { $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()); + $this->assertEquals('Fancy Subject', $this->symfonyMail->getSubject()); } - public function testSetPlainBody() { - $this->swiftMessage - ->expects($this->once()) - ->method('setBody') - ->with('Fancy Body'); - + public function testSetPlainBody(): void { $this->message->setPlainBody('Fancy Body'); + self::assertEquals('Fancy Body', $this->symfonyMail->getTextBody()); } - public function testGetPlainBody() { - $this->swiftMessage - ->expects($this->once()) - ->method('getBody') - ->will($this->returnValue('Fancy Body')); - + public function testGetPlainBody(): void { + $this->symfonyMail->text('Fancy Body'); $this->assertSame('Fancy Body', $this->message->getPlainBody()); } - public function testSetHtmlBody() { - $this->swiftMessage - ->expects($this->once()) - ->method('addPart') - ->with('', 'text/html'); - + public function testSetHtmlBody(): void { $this->message->setHtmlBody(''); + self::assertEquals('', $this->symfonyMail->getHtmlBody()); } }