diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a013a3df57..fba1881ded 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -184,7 +184,7 @@ jobs: run: echo "SELECT * FROM mysql.slow_log WHERE sql_text LIKE '%oc_mail%' AND sql_text NOT LIKE '%information_schema%'" | mysql -h 127.0.0.1 -u root -pmy-secret-pw - name: Print debug logs if: ${{ always() }} - run: cat nextcloud/data/horde_*.log + run: cat nextcloud/data/mail-*-*-imap.log - name: Report coverage uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4.6.0 if: ${{ always() && matrix.db == 'mysql' }} diff --git a/appinfo/info.xml b/appinfo/info.xml index 7d2bcbbbd9..24ce87ff96 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -80,6 +80,7 @@ Learn more about the Nextcloud Ethical AI Rating [in our blog](https://nextcloud OCA\Mail\Command\CleanUp OCA\Mail\Command\CreateAccount OCA\Mail\Command\CreateTagMigrationJobEntry + OCA\Mail\Command\DebugAccount OCA\Mail\Command\DeleteAccount OCA\Mail\Command\DiagnoseAccount OCA\Mail\Command\ExportAccount diff --git a/lib/Account.php b/lib/Account.php index f234e4886f..e294e75af2 100644 --- a/lib/Account.php +++ b/lib/Account.php @@ -61,6 +61,13 @@ public function getUserId() { return $this->account->getUserId(); } + /** + * @return int + */ + public function getDebug(): int { + return $this->account->getDebug(); + } + /** * Set the quota percentage * @param Quota $quota diff --git a/lib/Command/DebugAccount.php b/lib/Command/DebugAccount.php new file mode 100644 index 0000000000..25d53bcdf7 --- /dev/null +++ b/lib/Command/DebugAccount.php @@ -0,0 +1,78 @@ +setName('mail:account:debug'); + $this->setDescription('Enable or Disable IMAP/SMTP debugging on a account'); + $this->addArgument(self::ARGUMENT_ACCOUNT_ID, InputArgument::REQUIRED); + $this->addOption(self::OPTION_IMAP_DEFAULT, null, InputOption::VALUE_NONE); + $this->addOption(self::OPTION_IMAP_FULL, null, InputOption::VALUE_NONE); + $this->addOption(self::OPTION_SMTP_DEFAULT, null, InputOption::VALUE_NONE); + } + + protected function execute(InputInterface $input, OutputInterface $output): int { + $accountId = (int)$input->getArgument(self::ARGUMENT_ACCOUNT_ID); + $imapDefault = $input->getOption(self::OPTION_IMAP_DEFAULT); + $imapFull = $input->getOption(self::OPTION_IMAP_FULL); + $smtpDefault = $input->getOption(self::OPTION_SMTP_DEFAULT); + $debug = 0; + $debugImapDefault = 1 << 0; // 1 (0000 0001) + $debugImapFull = 1 << 1; // 2 (0000 0010) + $debugSmtpDefault = 1 << 4; // 16 (0001 0000) + + try { + $account = $this->accountService->findById($accountId)->getMailAccount(); + } catch (DoesNotExistException $e) { + $output->writeln("Account $accountId does not exist"); + return 1; + } + + if ($imapDefault) { + $debug += $debugImapDefault; + } elseif ($imapFull) { + $debug += $debugImapFull; + } + + if ($smtpDefault) { + $debug += $debugSmtpDefault; + } + + $account->setDebug($debug); + $this->accountService->save($account); + + return 0; + } +} diff --git a/lib/Db/MailAccount.php b/lib/Db/MailAccount.php index 17099f0123..b99b8000b8 100644 --- a/lib/Db/MailAccount.php +++ b/lib/Db/MailAccount.php @@ -101,6 +101,8 @@ * @method void setSearchBody(bool $searchBody) * @method bool|null getOooFollowsSystem() * @method void setOooFollowsSystem(bool $oooFollowsSystem) + * @method int getDebug() + * @method void setDebug(int $debug) */ class MailAccount extends Entity { public const SIGNATURE_MODE_PLAIN = 0; @@ -183,6 +185,8 @@ class MailAccount extends Entity { /** @var bool|null */ protected $oooFollowsSystem; + protected int $debug = 0; + /** * @param array $params */ @@ -240,6 +244,9 @@ public function __construct(array $params = []) { if (isset($params['outOfOfficeFollowsSystem'])) { $this->setOutOfOfficeFollowsSystem($params['outOfOfficeFollowsSystem']); } + if (isset($params['debug'])) { + $this->setDebug($params['debug']); + } $this->addType('inboundPort', 'integer'); $this->addType('outboundPort', 'integer'); @@ -263,6 +270,7 @@ public function __construct(array $params = []) { $this->addType('junkMailboxId', 'integer'); $this->addType('searchBody', 'boolean'); $this->addType('oooFollowsSystem', 'boolean'); + $this->addType('debug', 'integer'); } public function getOutOfOfficeFollowsSystem(): bool { @@ -310,6 +318,7 @@ public function toJson() { 'junkMailboxId' => $this->getJunkMailboxId(), 'searchBody' => $this->getSearchBody(), 'outOfOfficeFollowsSystem' => $this->getOutOfOfficeFollowsSystem(), + 'debug' => $this->getDebug(), ]; if (!is_null($this->getOutboundHost())) { diff --git a/lib/IMAP/IMAPClientFactory.php b/lib/IMAP/IMAPClientFactory.php index 3f6d5d2c45..a3ccefd059 100644 --- a/lib/IMAP/IMAPClientFactory.php +++ b/lib/IMAP/IMAPClientFactory.php @@ -40,6 +40,9 @@ class IMAPClientFactory { private ITimeFactory $timeFactory; private HordeCacheFactory $hordeCacheFactory; + private int $debugDefault = 1 << 0; // 1 (0000 0001) + private int $debugFull = 1 << 1; // 2 (0000 0010) + public function __construct(ICrypto $crypto, IConfig $config, ICacheFactory $cacheFactory, @@ -117,8 +120,13 @@ public function getClient(Account $account, bool $useCache = true): Horde_Imap_C 'backend' => $this->hordeCacheFactory->newCache($account), ]; } - if ($this->config->getSystemValue('debug', false)) { - $params['debug'] = $this->config->getSystemValue('datadirectory') . '/horde_imap.log'; + $debug = $account->getDebug(); + if ($debug & $this->debugDefault || $debug & $this->debugFull) { + $fn = 'mail-' . $account->getUserId() . '-' . $account->getId() . '-imap.log'; + $params['debug'] = $this->config->getSystemValue('datadirectory') . '/' . $fn; + if ($debug & $this->debugFull) { + $params['debug_literal'] = true; + } } $client = new HordeImapClient($params); diff --git a/lib/Migration/Version4100Date20241028000000.php b/lib/Migration/Version4100Date20241028000000.php new file mode 100644 index 0000000000..69eae2dab9 --- /dev/null +++ b/lib/Migration/Version4100Date20241028000000.php @@ -0,0 +1,38 @@ +getTable('mail_accounts'); + if (!$accountsTable->hasColumn('debug')) { + $accountsTable->addColumn('debug', Types::SMALLINT, [ + 'notnull' => false, + 'default' => 0, + ]); + } + return $schema; + } +} diff --git a/lib/SMTP/SmtpClientFactory.php b/lib/SMTP/SmtpClientFactory.php index 3d63195fba..14c9da3b05 100644 --- a/lib/SMTP/SmtpClientFactory.php +++ b/lib/SMTP/SmtpClientFactory.php @@ -28,6 +28,8 @@ class SmtpClientFactory { /** @var HostNameFactory */ private $hostNameFactory; + private int $debugDefault = 1 << 4; // 16 (0001 0000) + public function __construct(IConfig $config, ICrypto $crypto, HostNameFactory $hostNameFactory) { @@ -77,8 +79,9 @@ public function create(Account $account): Horde_Mail_Transport { $decryptedAccessToken, ); } - if ($this->config->getSystemValue('debug', false)) { - $params['debug'] = $this->config->getSystemValue('datadirectory') . '/horde_smtp.log'; + if ($account->getDebug() & $this->debugDefault) { + $fn = 'mail-' . $account->getUserId() . '-' . $account->getId() . '-smtp.log'; + $params['debug'] = $this->config->getSystemValue('datadirectory') . '/' . $fn; } return new Horde_Mail_Transport_Smtphorde($params); } diff --git a/tests/Integration/Db/MailAccountTest.php b/tests/Integration/Db/MailAccountTest.php index d960e46b3a..632d275ed5 100644 --- a/tests/Integration/Db/MailAccountTest.php +++ b/tests/Integration/Db/MailAccountTest.php @@ -54,7 +54,7 @@ public function testToAPI() { 'editorMode' => 'html', 'provisioningId' => null, 'order' => 13, - 'showSubscribedOnly' => null, + 'showSubscribedOnly' => false, 'personalNamespace' => null, 'draftsMailboxId' => null, 'sentMailboxId' => null, @@ -70,6 +70,7 @@ public function testToAPI() { 'snoozeMailboxId' => null, 'searchBody' => false, 'outOfOfficeFollowsSystem' => true, + 'debug' => 0, ], $a->toJson()); } @@ -107,6 +108,7 @@ public function testMailAccountConstruct() { 'snoozeMailboxId' => null, 'searchBody' => false, 'outOfOfficeFollowsSystem' => false, + 'debug' => 0, ]; $a = new MailAccount($expected); // TODO: fix inconsistency diff --git a/tests/Integration/Framework/ImapTestAccount.php b/tests/Integration/Framework/ImapTestAccount.php index c55941ae6a..2017bb465e 100644 --- a/tests/Integration/Framework/ImapTestAccount.php +++ b/tests/Integration/Framework/ImapTestAccount.php @@ -47,6 +47,7 @@ public function createTestAccount(?string $userId = null) { $mailAccount->setOutboundUser('user@domain.tld'); $mailAccount->setOutboundPassword(OC::$server->getCrypto()->encrypt('mypassword')); $mailAccount->setOutboundSslMode('none'); + $mailAccount->setDebug(1); $acc = $accountService->save($mailAccount); /** @var MailboxSync $mbSync */