Skip to content

Commit 09607f4

Browse files
authored
Merge pull request nextcloud#53834 from nextcloud/feat/imailaddressvalidator
feat(ocp): add email address validator
2 parents b9da14b + 336c6d2 commit 09607f4

File tree

11 files changed

+190
-43
lines changed

11 files changed

+190
-43
lines changed

build/psalm-baseline.xml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,12 @@
371371
</UndefinedMethod>
372372
</file>
373373
<file src="apps/dav/lib/CalDAV/Reminder/NotificationProvider/EmailProvider.php">
374+
<DeprecatedMethod>
375+
<code><![CDATA[validateMailAddress]]></code>
376+
<code><![CDATA[validateMailAddress]]></code>
377+
<code><![CDATA[validateMailAddress]]></code>
378+
<code><![CDATA[validateMailAddress]]></code>
379+
</DeprecatedMethod>
374380
<LessSpecificReturnStatement>
375381
<code><![CDATA[$emailAddresses]]></code>
376382
</LessSpecificReturnStatement>
@@ -452,6 +458,9 @@
452458
</DeprecatedMethod>
453459
</file>
454460
<file src="apps/dav/lib/CalDAV/Schedule/IMipPlugin.php">
461+
<DeprecatedMethod>
462+
<code><![CDATA[validateMailAddress]]></code>
463+
</DeprecatedMethod>
455464
<RedundantCast>
456465
<code><![CDATA[(string)$iTipMessage->recipientName]]></code>
457466
</RedundantCast>
@@ -859,6 +868,11 @@
859868
<code><![CDATA[getUserFolder]]></code>
860869
</DeprecatedMethod>
861870
</file>
871+
<file src="apps/dav/lib/Listener/CalendarContactInteractionListener.php">
872+
<DeprecatedMethod>
873+
<code><![CDATA[validateMailAddress]]></code>
874+
</DeprecatedMethod>
875+
</file>
862876
<file src="apps/dav/lib/Migration/BuildCalendarSearchIndex.php">
863877
<DeprecatedMethod>
864878
<code><![CDATA[getAppValue]]></code>
@@ -1566,6 +1580,7 @@
15661580
</DeprecatedClass>
15671581
<DeprecatedMethod>
15681582
<code><![CDATA[getAppValue]]></code>
1583+
<code><![CDATA[validateMailAddress]]></code>
15691584
</DeprecatedMethod>
15701585
<RedundantCast>
15711586
<code><![CDATA[(int)$code]]></code>
@@ -2073,6 +2088,7 @@
20732088
<code><![CDATA[getAppValue]]></code>
20742089
<code><![CDATA[getAppValue]]></code>
20752090
<code><![CDATA[setAppValue]]></code>
2091+
<code><![CDATA[validateMailAddress]]></code>
20762092
</DeprecatedMethod>
20772093
</file>
20782094
<file src="apps/settings/lib/Controller/WebAuthnController.php">
@@ -2195,6 +2211,10 @@
21952211
</DeprecatedMethod>
21962212
</file>
21972213
<file src="apps/sharebymail/lib/ShareByMailProvider.php">
2214+
<DeprecatedMethod>
2215+
<code><![CDATA[validateMailAddress]]></code>
2216+
<code><![CDATA[validateMailAddress]]></code>
2217+
</DeprecatedMethod>
21982218
<InvalidArgument>
21992219
<code><![CDATA[$share->getId()]]></code>
22002220
<code><![CDATA[(int)$data['id']]]></code>
@@ -3044,6 +3064,11 @@
30443064
<code><![CDATA[listen]]></code>
30453065
</DeprecatedMethod>
30463066
</file>
3067+
<file src="core/Command/User/Add.php">
3068+
<DeprecatedMethod>
3069+
<code><![CDATA[validateMailAddress]]></code>
3070+
</DeprecatedMethod>
3071+
</file>
30473072
<file src="core/Command/User/AuthTokens/Add.php">
30483073
<DeprecatedClass>
30493074
<code><![CDATA[IToken::DO_NOT_REMEMBER]]></code>

lib/composer/composer/autoload_classmap.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,7 @@
655655
'OCP\\Mail\\Headers\\AutoSubmitted' => $baseDir . '/lib/public/Mail/Headers/AutoSubmitted.php',
656656
'OCP\\Mail\\IAttachment' => $baseDir . '/lib/public/Mail/IAttachment.php',
657657
'OCP\\Mail\\IEMailTemplate' => $baseDir . '/lib/public/Mail/IEMailTemplate.php',
658+
'OCP\\Mail\\IEmailValidator' => $baseDir . '/lib/public/Mail/IEmailValidator.php',
658659
'OCP\\Mail\\IMailer' => $baseDir . '/lib/public/Mail/IMailer.php',
659660
'OCP\\Mail\\IMessage' => $baseDir . '/lib/public/Mail/IMessage.php',
660661
'OCP\\Mail\\Provider\\Address' => $baseDir . '/lib/public/Mail/Provider/Address.php',
@@ -1825,6 +1826,7 @@
18251826
'OC\\Log\\Systemdlog' => $baseDir . '/lib/private/Log/Systemdlog.php',
18261827
'OC\\Mail\\Attachment' => $baseDir . '/lib/private/Mail/Attachment.php',
18271828
'OC\\Mail\\EMailTemplate' => $baseDir . '/lib/private/Mail/EMailTemplate.php',
1829+
'OC\\Mail\\EmailValidator' => $baseDir . '/lib/private/Mail/EmailValidator.php',
18281830
'OC\\Mail\\Mailer' => $baseDir . '/lib/private/Mail/Mailer.php',
18291831
'OC\\Mail\\Message' => $baseDir . '/lib/private/Mail/Message.php',
18301832
'OC\\Mail\\Provider\\Manager' => $baseDir . '/lib/private/Mail/Provider/Manager.php',

lib/composer/composer/autoload_static.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
696696
'OCP\\Mail\\Headers\\AutoSubmitted' => __DIR__ . '/../../..' . '/lib/public/Mail/Headers/AutoSubmitted.php',
697697
'OCP\\Mail\\IAttachment' => __DIR__ . '/../../..' . '/lib/public/Mail/IAttachment.php',
698698
'OCP\\Mail\\IEMailTemplate' => __DIR__ . '/../../..' . '/lib/public/Mail/IEMailTemplate.php',
699+
'OCP\\Mail\\IEmailValidator' => __DIR__ . '/../../..' . '/lib/public/Mail/IEmailValidator.php',
699700
'OCP\\Mail\\IMailer' => __DIR__ . '/../../..' . '/lib/public/Mail/IMailer.php',
700701
'OCP\\Mail\\IMessage' => __DIR__ . '/../../..' . '/lib/public/Mail/IMessage.php',
701702
'OCP\\Mail\\Provider\\Address' => __DIR__ . '/../../..' . '/lib/public/Mail/Provider/Address.php',
@@ -1866,6 +1867,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
18661867
'OC\\Log\\Systemdlog' => __DIR__ . '/../../..' . '/lib/private/Log/Systemdlog.php',
18671868
'OC\\Mail\\Attachment' => __DIR__ . '/../../..' . '/lib/private/Mail/Attachment.php',
18681869
'OC\\Mail\\EMailTemplate' => __DIR__ . '/../../..' . '/lib/private/Mail/EMailTemplate.php',
1870+
'OC\\Mail\\EmailValidator' => __DIR__ . '/../../..' . '/lib/private/Mail/EmailValidator.php',
18691871
'OC\\Mail\\Mailer' => __DIR__ . '/../../..' . '/lib/private/Mail/Mailer.php',
18701872
'OC\\Mail\\Message' => __DIR__ . '/../../..' . '/lib/private/Mail/Message.php',
18711873
'OC\\Mail\\Provider\\Manager' => __DIR__ . '/../../..' . '/lib/private/Mail/Provider/Manager.php',
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OC\Mail;
11+
12+
use Egulias\EmailValidator\EmailValidator as EquliasEmailValidator;
13+
use Egulias\EmailValidator\Validation\NoRFCWarningsValidation;
14+
use Egulias\EmailValidator\Validation\RFCValidation;
15+
use OCP\IAppConfig;
16+
use OCP\Mail\IEmailValidator;
17+
18+
class EmailValidator implements IEmailValidator {
19+
public function __construct(
20+
private IAppConfig $appConfig,
21+
) {
22+
}
23+
24+
public function isValid(string $email): bool {
25+
if ($email === '') {
26+
// Shortcut: empty addresses are never valid
27+
return false;
28+
}
29+
30+
$strictMailCheck = $this->appConfig->getValueString('core', 'enforce_strict_email_check', 'yes') === 'yes';
31+
32+
$validator = new EquliasEmailValidator();
33+
$validation = $strictMailCheck ? new NoRFCWarningsValidation() : new RFCValidation();
34+
35+
return $validator->isValid($email, $validation);
36+
}
37+
}

lib/private/Mail/Mailer.php

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@
88
*/
99
namespace OC\Mail;
1010

11-
use Egulias\EmailValidator\EmailValidator;
12-
use Egulias\EmailValidator\Validation\NoRFCWarningsValidation;
13-
use Egulias\EmailValidator\Validation\RFCValidation;
1411
use OCP\Defaults;
1512
use OCP\EventDispatcher\IEventDispatcher;
1613
use OCP\IBinaryFinder;
@@ -21,6 +18,7 @@
2118
use OCP\Mail\Events\BeforeMessageSent;
2219
use OCP\Mail\IAttachment;
2320
use OCP\Mail\IEMailTemplate;
21+
use OCP\Mail\IEmailValidator;
2422
use OCP\Mail\IMailer;
2523
use OCP\Mail\IMessage;
2624
use Psr\Log\LoggerInterface;
@@ -69,6 +67,7 @@ public function __construct(
6967
private IL10N $l10n,
7068
private IEventDispatcher $dispatcher,
7169
private IFactory $l10nFactory,
70+
private IEmailValidator $emailValidator,
7271
) {
7372
}
7473

@@ -234,18 +233,10 @@ public function send(IMessage $message): array {
234233
/**
235234
* @param string $email Email address to be validated
236235
* @return bool True if the mail address is valid, false otherwise
236+
* @deprecated 26.0.0 use IEmailValidator.isValid instead
237237
*/
238238
public function validateMailAddress(string $email): bool {
239-
if ($email === '') {
240-
// Shortcut: empty addresses are never valid
241-
return false;
242-
}
243-
244-
$strictMailCheck = $this->config->getAppValue('core', 'enforce_strict_email_check', 'yes') === 'yes';
245-
$validator = new EmailValidator();
246-
$validation = $strictMailCheck ? new NoRFCWarningsValidation() : new RFCValidation();
247-
248-
return $validator->isValid($email, $validation);
239+
return $this->emailValidator->isValid($email);
249240
}
250241

251242
protected function getInstance(): MailerInterface {

lib/private/Server.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
use OC\Lockdown\LockdownManager;
7878
use OC\Log\LogFactory;
7979
use OC\Log\PsrLoggerAdapter;
80+
use OC\Mail\EmailValidator;
8081
use OC\Mail\Mailer;
8182
use OC\Memcache\ArrayCache;
8283
use OC\Memcache\Factory;
@@ -195,6 +196,7 @@
195196
use OCP\Lock\ILockingProvider;
196197
use OCP\Lockdown\ILockdownManager;
197198
use OCP\Log\ILogFactory;
199+
use OCP\Mail\IEmailValidator;
198200
use OCP\Mail\IMailer;
199201
use OCP\OCM\ICapabilityAwareOCMProvider;
200202
use OCP\OCM\IOCMDiscoveryService;
@@ -891,6 +893,9 @@ public function __construct($webRoot, \OC\Config $config) {
891893
);
892894
});
893895

896+
/** @since 32.0.0 */
897+
$this->registerAlias(IEmailValidator::class, EmailValidator::class);
898+
894899
$this->registerService(IMailer::class, function (Server $c) {
895900
return new Mailer(
896901
$c->get(\OCP\IConfig::class),
@@ -899,7 +904,8 @@ public function __construct($webRoot, \OC\Config $config) {
899904
$c->get(IURLGenerator::class),
900905
$c->getL10N('lib'),
901906
$c->get(IEventDispatcher::class),
902-
$c->get(IFactory::class)
907+
$c->get(IFactory::class),
908+
$c->get(IEmailValidator::class),
903909
);
904910
});
905911

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace OCP\Mail;
11+
12+
/**
13+
* Validator for email addresses
14+
*
15+
* @since 32.0.0
16+
*/
17+
interface IEmailValidator {
18+
/**
19+
* @param string $email Email address to be validated
20+
* @return bool True if the mail address is valid, false otherwise
21+
* @since 32.0.0
22+
*/
23+
public function isValid(string $email): bool;
24+
}

lib/public/Mail/IMailer.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ public function send(IMessage $message): array;
8080
* @param string $email Email address to be validated
8181
* @return bool True if the mail address is valid, false otherwise
8282
* @since 8.1.0
83+
* @deprecated 26.0.0 use IEmailValidator.isValid instead
8384
*/
8485
public function validateMailAddress(string $email): bool;
8586
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
10+
namespace Test\Mail;
11+
12+
use OC\Mail\EmailValidator;
13+
use OCP\IAppConfig;
14+
use PHPUnit\Framework\Attributes\DataProvider;
15+
use PHPUnit\Framework\MockObject\MockObject;
16+
use Test\TestCase;
17+
18+
class EmailValidatorTest extends TestCase {
19+
private IAppConfig&MockObject $appConfig;
20+
private EmailValidator $emailValidator;
21+
22+
23+
protected function setUp(): void {
24+
parent::setUp();
25+
26+
$this->appConfig = $this->createMock(IAppConfig::class);
27+
$this->emailValidator = new EmailValidator($this->appConfig);
28+
}
29+
30+
public static function mailAddressProvider(): array {
31+
return [
32+
['[email protected]', true, false],
33+
['lukas@localhost', true, false],
34+
['[email protected]', true, false],
35+
['lukas@éxämplè.com', true, false],
36+
['asdf', false, false],
37+
['', false, false],
38+
['[email protected]@nextcloud.com', false, false],
39+
['test@localhost', true, false],
40+
['test@localhost', false, true],
41+
];
42+
}
43+
44+
#[DataProvider('mailAddressProvider')]
45+
public function testIsValid($email, $expected, $strict): void {
46+
$this->appConfig
47+
->expects($this->atMost(1))
48+
->method('getValueString')
49+
->with('core', 'enforce_strict_email_check', 'yes')
50+
->willReturn($strict ? 'yes' : 'no');
51+
$this->assertSame($expected, $this->emailValidator->isValid($email));
52+
}
53+
}

tests/lib/Mail/MailerTest.php

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use OCP\IURLGenerator;
2020
use OCP\L10N\IFactory;
2121
use OCP\Mail\Events\BeforeMessageSent;
22+
use OCP\Mail\IEmailValidator;
2223
use OCP\Server;
2324
use PHPUnit\Framework\MockObject\MockObject;
2425
use Psr\Log\LoggerInterface;
@@ -61,7 +62,8 @@ protected function setUp(): void {
6162
$this->urlGenerator,
6263
$this->l10n,
6364
$this->dispatcher,
64-
$this->createMock(IFactory::class)
65+
$this->createMock(IFactory::class),
66+
$this->createMock(IEmailValidator::class),
6567
);
6668
}
6769

@@ -181,7 +183,8 @@ public function testEvents(): void {
181183
$this->urlGenerator,
182184
$this->l10n,
183185
$this->dispatcher,
184-
$this->createMock(IFactory::class)
186+
$this->createMock(IFactory::class),
187+
$this->createMock(IEmailValidator::class),
185188
]
186189
)
187190
->getMock();
@@ -226,33 +229,6 @@ public function testSendInvalidMailException(): void {
226229
$this->mailer->send($message);
227230
}
228231

229-
/**
230-
* @return array
231-
*/
232-
public static function mailAddressProvider(): array {
233-
return [
234-
['[email protected]', true, false],
235-
['lukas@localhost', true, false],
236-
['[email protected]', true, false],
237-
['lukas@éxämplè.com', true, false],
238-
['asdf', false, false],
239-
['', false, false],
240-
['[email protected]@owncloud.com', false, false],
241-
['test@localhost', true, false],
242-
['test@localhost', false, true],
243-
];
244-
}
245-
246-
#[\PHPUnit\Framework\Attributes\DataProvider('mailAddressProvider')]
247-
public function testValidateMailAddress($email, $expected, $strict): void {
248-
$this->config
249-
->expects($this->atMost(1))
250-
->method('getAppValue')
251-
->with('core', 'enforce_strict_email_check')
252-
->willReturn($strict ? 'yes' : 'no');
253-
$this->assertSame($expected, $this->mailer->validateMailAddress($email));
254-
}
255-
256232
public function testCreateEMailTemplate(): void {
257233
$this->config->method('getSystemValueString')
258234
->with('mail_template_class', '')

0 commit comments

Comments
 (0)