executeStatement('CREATE TABLE mailgateway_settings (id INTEGER PRIMARY KEY, config_encrypted TEXT NOT NULL)'); $di = new \Pimple\Container(); $di['dbal'] = $connection; $di['crypt'] = new class extends \Box_Crypt { public function encrypt(string $text, ?string $pass = null): string { return parent::encrypt($text, 'isolated-settings-test-key'); } public function decrypt(?string $text, ?string $pass = null) { return parent::decrypt($text, 'isolated-settings-test-key'); } }; $desk = new \Box\Mod\Support\Entity\Helpdesk(); (new ReflectionProperty($desk, 'id'))->setValue($desk, 2); $ticket = new \Box\Mod\Support\Entity\SupportTicket(); (new ReflectionProperty($ticket, 'id'))->setValue($ticket, 61); $support = new class($desk, $ticket) { public array $created = []; public function __construct(public $desk, public $ticket) {} public function getHelpdeskRepository() { return $this; } public function find($id) { return $id === 2 ? $this->desk : null; } public function findBy(...$args) { throw new RuntimeException('Configured helpdesk was ignored'); } public function ticketCreateForGuest($data) { $this->created[] = $data; return 'guest-hash'; } public function findOneByHash($hash) { return $this->ticket; } }; $di['mod_service'] = $di->protect(fn ($module, $sub = '') => $support); $store = new \Box\Mod\Mailgateway\Settings($di); if ($store->get() !== \Box\Mod\Mailgateway\Settings::DEFAULTS || $store->getPublic()['password_configured'] || $store->get()['host'] !== '' || $store->get()['username'] !== '' || $store->get()['mailbox'] !== '') { throw new RuntimeException('Settings defaults incorrect'); } $input = array_replace(\Box\Mod\Mailgateway\Settings::DEFAULTS, [ 'host' => 'mail.example.com', 'username' => 'support@example.com', 'mailbox' => 'support@example.com', 'enabled' => '0', 'support_helpdesk_id' => '2', 'password' => 'test-only-password', ]); $store->save($input); $stored = $connection->fetchOne('SELECT config_encrypted FROM mailgateway_settings WHERE id = 1'); if (!str_starts_with($stored, 'v2:') || str_contains($stored, 'test-only-password') || str_contains($stored, 'support@example.com') || array_key_exists('password', $store->getPublic())) { throw new RuntimeException('Settings stored or exposed in plaintext'); } $input['password'] = ''; $store->save($input); if ($store->get()['password'] !== 'test-only-password' || !$store->getPublic()['password_configured']) { throw new RuntimeException('Blank password did not preserve saved credential'); } foreach ([ ['host' => 'https://example.com'], ['port' => '0'], ['port' => '65536'], ['encryption' => 'none'], ['mailbox' => 'bad-email'], ['enabled' => 'maybe'], ['inbox_folder' => 'Processed'], ['username' => "bad\r\nuser"], ['support_helpdesk_id' => '999'], ['password' => ['invalid']], ['clear_password' => '1', 'password' => 'conflicting-password'], ] as $changes) { try { $store->save(array_replace($input, $changes)); throw new RuntimeException('Invalid settings accepted'); } catch (\FOSSBilling\InformationException) {} } $service = new \Box\Mod\Mailgateway\Service(); $service->setDi($di); $result = $service->import(['sender_email' => 'new-guest@example.com', 'subject' => 'Configured helpdesk', 'content' => 'Test body']); if ($result['support_helpdesk_id'] !== 2 || $support->created[0]['support_helpdesk_id'] !== 2) { throw new RuntimeException('Selected helpdesk not used for import'); } $input['enabled'] = '0'; $input['clear_password'] = '1'; $store->save($input); if ($store->getPublic()['password_configured']) { throw new RuntimeException('Password not cleared'); } try { $store->save(array_replace($input, ['enabled' => '1', 'clear_password' => '0'])); throw new RuntimeException('Enabled imports without a password'); } catch (\FOSSBilling\InformationException) {} $input['clear_password'] = '0'; $input['password'] = 'test-only-password'; $store->save($input); $source = file_get_contents(dirname(__DIR__) . '/templates/admin/mod_mailgateway_settings.html.twig'); $twig = new \Twig\Environment(new \Twig\Loader\ArrayLoader([ 'settings' => $source, 'layout_default.html.twig' => '{% block content %}{% endblock %}', 'layout_blank.html.twig' => '{% block content %}{% endblock %}', ]), ['autoescape' => 'html', 'strict_variables' => true]); $twig->addFilter(new \Twig\TwigFilter('trans', fn ($text) => $text)); $twig->addFilter(new \Twig\TwigFilter('api_url', fn ($path, $role = 'admin') => '/api/admin/' . $path)); $twig->addFunction(new \Twig\TwigFunction('fb_api_form', fn ($options) => 'class="api-form"', ['is_safe' => ['html']])); $admin = new class($store) { public function __construct(public $store) {} public function mailgateway_settings_get($data) { return ['settings' => $this->store->getPublic(), 'helpdesks' => [2 => 'Helpdesk