From 1642b853ee0f251480ff4b417790f1c011f38015 Mon Sep 17 00:00:00 2001 From: Avinash Gusain Date: Wed, 16 Jul 2025 13:07:34 +0530 Subject: [PATCH 1/9] fix: handle null email by catching Throwable in hasActiveSubscription to ensure spam check loop continues --- lib/Service/RecoveryEmailService.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/Service/RecoveryEmailService.php b/lib/Service/RecoveryEmailService.php index 3503419..714587a 100644 --- a/lib/Service/RecoveryEmailService.php +++ b/lib/Service/RecoveryEmailService.php @@ -621,8 +621,13 @@ class RecoveryEmailService { $email = $user->getEMailAddress(); - if ($this->hasActiveSubscription($email)) { - $this->logger->info("User $userId has an active subscription. Skipping spam flag for <$recoveryEmail>."); + try { + if ($this->hasActiveSubscription($email)) { + $this->logger->info("User $userId has an active subscription. Skipping spam flag for <$recoveryEmail>."); + continue; + } + } catch (\Throwable $e) { + $this->logger->error("Error checking subscription for $userId <$email>: " . $e->getMessage()); continue; } -- GitLab From c8541e60f4f32d5fefb436a4d5f9815e90c94fba Mon Sep 17 00:00:00 2001 From: Avinash Gusain Date: Wed, 16 Jul 2025 14:33:39 +0530 Subject: [PATCH 2/9] feat: support real-time spam user output via callback in getAllSpamEmails --- lib/Service/RecoveryEmailService.php | 34 +++++++++++++++++++--------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/lib/Service/RecoveryEmailService.php b/lib/Service/RecoveryEmailService.php index 714587a..5bf994c 100644 --- a/lib/Service/RecoveryEmailService.php +++ b/lib/Service/RecoveryEmailService.php @@ -596,31 +596,34 @@ class RecoveryEmailService { * - Already taken by another user * - Unverifiable or unreachable (e.g., failed MX or domain lookup) * - * Returns an array of spam account entries, each including: - * - userId: the username - * - recoveryEmail: the flagged recovery email address + * Optionally, a callback can be passed to handle each detected spam user in real-time. + * This is useful for streaming output during long-running operations (e.g., CLI). * + * @param callable|null $onSpamDetected A callback function with signature fn(string $userId, string $recoveryEmail): void * @return array */ - public function getAllSpamEmails(): array { + public function getAllSpamEmails(?callable $onSpamDetected = null): array { $verifiedEmails = $this->configMapper->getAllVerifiedRecoveryEmails(); $spamAccounts = []; - + foreach ($verifiedEmails as $entry) { $recoveryEmail = strtolower(trim($entry['configvalue'])); $userId = strtolower(trim($entry['userid'])); - + if ($recoveryEmail === '' || $userId === '') { continue; } - + $user = $this->userManager->get($userId); if ($user === null) { continue; } - + $email = $user->getEMailAddress(); - + if (empty($email)) { + continue; + } + try { if ($this->hasActiveSubscription($email)) { $this->logger->info("User $userId has an active subscription. Skipping spam flag for <$recoveryEmail>."); @@ -630,25 +633,34 @@ class RecoveryEmailService { $this->logger->error("Error checking subscription for $userId <$email>: " . $e->getMessage()); continue; } - + try { if (!$this->validateRecoveryEmail($recoveryEmail, $userId)) { $spamAccounts[] = [ 'userId' => $userId, 'recoveryEmail' => $recoveryEmail, ]; + + if (is_callable($onSpamDetected)) { + $onSpamDetected($userId, $recoveryEmail); + } } } catch (BlacklistedEmailException | InvalidRecoveryEmailException $e) { $this->logger->info("Validation failed (spam) for $userId <$recoveryEmail>: " . $e->getMessage()); + $spamAccounts[] = [ 'userId' => $userId, 'recoveryEmail' => $recoveryEmail, ]; + + if (is_callable($onSpamDetected)) { + $onSpamDetected($userId, $recoveryEmail); + } } catch (\Throwable $e) { $this->logger->info("Error while checking $userId <$recoveryEmail>: " . $e->getMessage()); } } - + return $spamAccounts; } } -- GitLab From 9a53050ed46152a41a700be155a70662c9c2a7c9 Mon Sep 17 00:00:00 2001 From: Avinash Gusain Date: Wed, 16 Jul 2025 14:34:36 +0530 Subject: [PATCH 3/9] chore: add missing file from previous commit for spam email callback support --- lib/Command/SpamAccountDetection.php | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/Command/SpamAccountDetection.php b/lib/Command/SpamAccountDetection.php index 72e18e6..98085a7 100644 --- a/lib/Command/SpamAccountDetection.php +++ b/lib/Command/SpamAccountDetection.php @@ -32,19 +32,18 @@ class SpamAccountDetection extends Command { protected function execute(InputInterface $input, OutputInterface $output): int { try { - $spamUsers = $this->recoveryEmailService->getAllSpamEmails(); - $output->writeln('Spam user list:'); - foreach ($spamUsers as $user) { - $output->writeln($user['userId']); - //$output->writeln("User ID: {$user['userId']}, Recovery Email: {$user['recoveryEmail']}"); - } + + $this->recoveryEmailService->getAllSpamEmails(function (string $userId, string $recoveryEmail) use ($output) { + $output->writeln("User ID: $userId, Recovery Email: $recoveryEmail"); + }); + } catch (\Throwable $th) { - $this->logger->error('Error while fetching domains. ' . $th->getMessage()); - $output->writeln('Error while fetching domains: ' . $th->getMessage()); + $this->logger->error('Error while fetching domains: ' . $th->getMessage()); return Command::FAILURE; } - + return Command::SUCCESS; } + } -- GitLab From 87d7dc474e961a6eaa48b1ae9d7f00efd9ec0631 Mon Sep 17 00:00:00 2001 From: Avinash Gusain Date: Wed, 16 Jul 2025 14:38:27 +0530 Subject: [PATCH 4/9] refactor: update spam output to print only userId instead of full recovery email --- lib/Command/SpamAccountDetection.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Command/SpamAccountDetection.php b/lib/Command/SpamAccountDetection.php index 98085a7..f1dc867 100644 --- a/lib/Command/SpamAccountDetection.php +++ b/lib/Command/SpamAccountDetection.php @@ -35,7 +35,8 @@ class SpamAccountDetection extends Command { $output->writeln('Spam user list:'); $this->recoveryEmailService->getAllSpamEmails(function (string $userId, string $recoveryEmail) use ($output) { - $output->writeln("User ID: $userId, Recovery Email: $recoveryEmail"); + //$output->writeln("User ID: $userId, Recovery Email: $recoveryEmail"); + $output->writeln($userId); }); } catch (\Throwable $th) { -- GitLab From daa41fd76b322ab6c5be13cdce89696759235604 Mon Sep 17 00:00:00 2001 From: Avinash Gusain Date: Wed, 16 Jul 2025 14:40:45 +0530 Subject: [PATCH 5/9] fix lint issues --- lib/Command/SpamAccountDetection.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/Command/SpamAccountDetection.php b/lib/Command/SpamAccountDetection.php index f1dc867..da8238b 100644 --- a/lib/Command/SpamAccountDetection.php +++ b/lib/Command/SpamAccountDetection.php @@ -38,7 +38,6 @@ class SpamAccountDetection extends Command { //$output->writeln("User ID: $userId, Recovery Email: $recoveryEmail"); $output->writeln($userId); }); - } catch (\Throwable $th) { $this->logger->error('Error while fetching domains: ' . $th->getMessage()); return Command::FAILURE; @@ -46,5 +45,4 @@ class SpamAccountDetection extends Command { return Command::SUCCESS; } - } -- GitLab From b437c5cb64eda5cf8ab3cf6fd3fa547a6d4da0ed Mon Sep 17 00:00:00 2001 From: Avinash Gusain Date: Wed, 16 Jul 2025 16:25:56 +0530 Subject: [PATCH 6/9] refactor: make spam detection callback mandatory --- lib/Service/RecoveryEmailService.php | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/lib/Service/RecoveryEmailService.php b/lib/Service/RecoveryEmailService.php index 5bf994c..6b2bca0 100644 --- a/lib/Service/RecoveryEmailService.php +++ b/lib/Service/RecoveryEmailService.php @@ -599,10 +599,10 @@ class RecoveryEmailService { * Optionally, a callback can be passed to handle each detected spam user in real-time. * This is useful for streaming output during long-running operations (e.g., CLI). * - * @param callable|null $onSpamDetected A callback function with signature fn(string $userId, string $recoveryEmail): void + * @param callable $onSpamDetected A callback function with signature fn(string $userId, string $recoveryEmail): void * @return array */ - public function getAllSpamEmails(?callable $onSpamDetected = null): array { + public function getAllSpamEmails(callable $onSpamDetected = null): array { $verifiedEmails = $this->configMapper->getAllVerifiedRecoveryEmails(); $spamAccounts = []; @@ -640,10 +640,7 @@ class RecoveryEmailService { 'userId' => $userId, 'recoveryEmail' => $recoveryEmail, ]; - - if (is_callable($onSpamDetected)) { - $onSpamDetected($userId, $recoveryEmail); - } + $onSpamDetected($userId, $recoveryEmail); } } catch (BlacklistedEmailException | InvalidRecoveryEmailException $e) { $this->logger->info("Validation failed (spam) for $userId <$recoveryEmail>: " . $e->getMessage()); @@ -653,9 +650,7 @@ class RecoveryEmailService { 'recoveryEmail' => $recoveryEmail, ]; - if (is_callable($onSpamDetected)) { - $onSpamDetected($userId, $recoveryEmail); - } + $onSpamDetected($userId, $recoveryEmail); } catch (\Throwable $e) { $this->logger->info("Error while checking $userId <$recoveryEmail>: " . $e->getMessage()); } -- GitLab From 06a2fbd5b6d4bd87a303dad283b99f6cee93fb34 Mon Sep 17 00:00:00 2001 From: Avinash Gusain Date: Fri, 18 Jul 2025 10:55:11 +0530 Subject: [PATCH 7/9] vendor/bin/php-cs-fixer --config=.php-cs-fixer.dist.php fix . --- lib/Command/SpamAccountDetection.php | 2 +- lib/Service/RecoveryEmailService.php | 15 +-------------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/lib/Command/SpamAccountDetection.php b/lib/Command/SpamAccountDetection.php index da8238b..0e4e2ba 100644 --- a/lib/Command/SpamAccountDetection.php +++ b/lib/Command/SpamAccountDetection.php @@ -39,7 +39,7 @@ class SpamAccountDetection extends Command { $output->writeln($userId); }); } catch (\Throwable $th) { - $this->logger->error('Error while fetching domains: ' . $th->getMessage()); + $this->logger->error('Error while fetching spam email list: ' . $th->getMessage()); return Command::FAILURE; } diff --git a/lib/Service/RecoveryEmailService.php b/lib/Service/RecoveryEmailService.php index 6b2bca0..9be0a05 100644 --- a/lib/Service/RecoveryEmailService.php +++ b/lib/Service/RecoveryEmailService.php @@ -596,7 +596,7 @@ class RecoveryEmailService { * - Already taken by another user * - Unverifiable or unreachable (e.g., failed MX or domain lookup) * - * Optionally, a callback can be passed to handle each detected spam user in real-time. + * A callback can be passed to handle each detected spam user in real-time. * This is useful for streaming output during long-running operations (e.g., CLI). * * @param callable $onSpamDetected A callback function with signature fn(string $userId, string $recoveryEmail): void @@ -604,7 +604,6 @@ class RecoveryEmailService { */ public function getAllSpamEmails(callable $onSpamDetected = null): array { $verifiedEmails = $this->configMapper->getAllVerifiedRecoveryEmails(); - $spamAccounts = []; foreach ($verifiedEmails as $entry) { $recoveryEmail = strtolower(trim($entry['configvalue'])); @@ -636,26 +635,14 @@ class RecoveryEmailService { try { if (!$this->validateRecoveryEmail($recoveryEmail, $userId)) { - $spamAccounts[] = [ - 'userId' => $userId, - 'recoveryEmail' => $recoveryEmail, - ]; $onSpamDetected($userId, $recoveryEmail); } } catch (BlacklistedEmailException | InvalidRecoveryEmailException $e) { $this->logger->info("Validation failed (spam) for $userId <$recoveryEmail>: " . $e->getMessage()); - - $spamAccounts[] = [ - 'userId' => $userId, - 'recoveryEmail' => $recoveryEmail, - ]; - $onSpamDetected($userId, $recoveryEmail); } catch (\Throwable $e) { $this->logger->info("Error while checking $userId <$recoveryEmail>: " . $e->getMessage()); } } - - return $spamAccounts; } } -- GitLab From 5e93a54aac50ca0b0039223d6a753a152de80ab5 Mon Sep 17 00:00:00 2001 From: Avinash Gusain Date: Fri, 18 Jul 2025 16:15:56 +0530 Subject: [PATCH 8/9] fix: require non-null spam callback in getAllSpamEmails Remove the default null value for $onSpamDetected to ensure a valid callable is always passed. This prevents potential TypeErrors due to null callbacks --- lib/Service/RecoveryEmailService.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Service/RecoveryEmailService.php b/lib/Service/RecoveryEmailService.php index 9be0a05..5ecefdb 100644 --- a/lib/Service/RecoveryEmailService.php +++ b/lib/Service/RecoveryEmailService.php @@ -600,9 +600,9 @@ class RecoveryEmailService { * This is useful for streaming output during long-running operations (e.g., CLI). * * @param callable $onSpamDetected A callback function with signature fn(string $userId, string $recoveryEmail): void - * @return array + * @return void */ - public function getAllSpamEmails(callable $onSpamDetected = null): array { + public function getAllSpamEmails(callable $onSpamDetected): void { $verifiedEmails = $this->configMapper->getAllVerifiedRecoveryEmails(); foreach ($verifiedEmails as $entry) { -- GitLab From 6d593c4f45bcd3783110ff4438aaeef7f66392d1 Mon Sep 17 00:00:00 2001 From: Avinash Gusain Date: Mon, 21 Jul 2025 19:00:50 +0530 Subject: [PATCH 9/9] Version bump to newer version for release --- appinfo/info.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appinfo/info.xml b/appinfo/info.xml index f59eebc..c5c7a83 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -5,7 +5,7 @@ Email Recovery Email Recovery App - 10.2.0 + 10.2.1 agpl MURENA SAS EmailRecovery -- GitLab