Skip to content

Commit 336c6d2

Browse files
committed
feat(ocp): add email address validator
Signed-off-by: Daniel Kesselberg <mail@danielkesselberg.de>
1 parent e0a21e5 commit 336c6d2

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>
@@ -864,6 +873,11 @@
864873
<code><![CDATA[getUserFolder]]></code>
865874
</DeprecatedMethod>
866875
</file>
876+
<file src="apps/dav/lib/Listener/CalendarContactInteractionListener.php">
877+
<DeprecatedMethod>
878+
<code><![CDATA[validateMailAddress]]></code>
879+
</DeprecatedMethod>
880+
</file>
867881
<file src="apps/dav/lib/Migration/BuildCalendarSearchIndex.php">
868882
<DeprecatedMethod>
869883
<code><![CDATA[getAppValue]]></code>
@@ -1572,6 +1586,7 @@
15721586
</DeprecatedClass>
15731587
<DeprecatedMethod>
15741588
<code><![CDATA[getAppValue]]></code>
1589+
<code><![CDATA[validateMailAddress]]></code>
15751590
</DeprecatedMethod>
15761591
<RedundantCast>
15771592
<code><![CDATA[(int)$code]]></code>
@@ -2079,6 +2094,7 @@
20792094
<code><![CDATA[getAppValue]]></code>
20802095
<code><![CDATA[getAppValue]]></code>
20812096
<code><![CDATA[setAppValue]]></code>
2097+
<code><![CDATA[validateMailAddress]]></code>
20822098
</DeprecatedMethod>
20832099
</file>
20842100
<file src="apps/settings/lib/Controller/WebAuthnController.php">
@@ -2201,6 +2217,10 @@
22012217
</DeprecatedMethod>
22022218
</file>
22032219
<file src="apps/sharebymail/lib/ShareByMailProvider.php">
2220+
<DeprecatedMethod>
2221+
<code><![CDATA[validateMailAddress]]></code>
2222+
<code><![CDATA[validateMailAddress]]></code>
2223+
</DeprecatedMethod>
22042224
<InvalidArgument>
22052225
<code><![CDATA[$share->getId()]]></code>
22062226
<code><![CDATA[(int)$data['id']]]></code>
@@ -3053,6 +3073,11 @@
30533073
<code><![CDATA[listen]]></code>
30543074
</DeprecatedMethod>
30553075
</file>
3076+
<file src="core/Command/User/Add.php">
3077+
<DeprecatedMethod>
3078+
<code><![CDATA[validateMailAddress]]></code>
3079+
</DeprecatedMethod>
3080+
</file>
30563081
<file src="core/Command/User/AuthTokens/Add.php">
30573082
<DeprecatedClass>
30583083
<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',
@@ -1820,6 +1821,7 @@
18201821
'OC\\Log\\Systemdlog' => $baseDir . '/lib/private/Log/Systemdlog.php',
18211822
'OC\\Mail\\Attachment' => $baseDir . '/lib/private/Mail/Attachment.php',
18221823
'OC\\Mail\\EMailTemplate' => $baseDir . '/lib/private/Mail/EMailTemplate.php',
1824+
'OC\\Mail\\EmailValidator' => $baseDir . '/lib/private/Mail/EmailValidator.php',
18231825
'OC\\Mail\\Mailer' => $baseDir . '/lib/private/Mail/Mailer.php',
18241826
'OC\\Mail\\Message' => $baseDir . '/lib/private/Mail/Message.php',
18251827
'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',
@@ -1861,6 +1862,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
18611862
'OC\\Log\\Systemdlog' => __DIR__ . '/../../..' . '/lib/private/Log/Systemdlog.php',
18621863
'OC\\Mail\\Attachment' => __DIR__ . '/../../..' . '/lib/private/Mail/Attachment.php',
18631864
'OC\\Mail\\EMailTemplate' => __DIR__ . '/../../..' . '/lib/private/Mail/EMailTemplate.php',
1865+
'OC\\Mail\\EmailValidator' => __DIR__ . '/../../..' . '/lib/private/Mail/EmailValidator.php',
18641866
'OC\\Mail\\Mailer' => __DIR__ . '/../../..' . '/lib/private/Mail/Mailer.php',
18651867
'OC\\Mail\\Message' => __DIR__ . '/../../..' . '/lib/private/Mail/Message.php',
18661868
'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;
@@ -916,6 +918,9 @@ public function __construct($webRoot, \OC\Config $config) {
916918
);
917919
});
918920

921+
/** @since 32.0.0 */
922+
$this->registerAlias(IEmailValidator::class, EmailValidator::class);
923+
919924
$this->registerService(IMailer::class, function (Server $c) {
920925
return new Mailer(
921926
$c->get(\OCP\IConfig::class),
@@ -924,7 +929,8 @@ public function __construct($webRoot, \OC\Config $config) {
924929
$c->get(IURLGenerator::class),
925930
$c->getL10N('lib'),
926931
$c->get(IEventDispatcher::class),
927-
$c->get(IFactory::class)
932+
$c->get(IFactory::class),
933+
$c->get(IEmailValidator::class),
928934
);
929935
});
930936

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+
['lukas@nextcloud.com', true, false],
33+
['lukas@localhost', true, false],
34+
['lukas@192.168.1.1', true, false],
35+
['lukas@éxämplè.com', true, false],
36+
['asdf', false, false],
37+
['', false, false],
38+
['lukas@nextcloud.org@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-
['lukas@owncloud.com', true, false],
235-
['lukas@localhost', true, false],
236-
['lukas@192.168.1.1', true, false],
237-
['lukas@éxämplè.com', true, false],
238-
['asdf', false, false],
239-
['', false, false],
240-
['lukas@owncloud.org@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)