diff --git a/app/Mailgateway/Types/PleskType.php b/app/Mailgateway/Types/PleskType.php new file mode 100644 index 00000000..6ae69dac --- /dev/null +++ b/app/Mailgateway/Types/PleskType.php @@ -0,0 +1,128 @@ +url = data_get($params, 'url'); + $this->user = data_get($params, 'user'); + $this->password = data_get($params, 'password'); + + return $this; + } + + public static function name(): string + { + return 'Plesk'; + } + + public function works(): bool + { + $response = $this->client()->get('/server'); + + return 200 === $response->status() && $response->json('panel_version'); + } + + /** + * {@inheritdoc} + */ + public static function fields(): array + { + return [ + [ + 'name' => 'url', + 'label' => 'URL', + 'type' => 'text', + 'storeValidator' => 'required|max:255', + 'updateValidator' => 'required|max:255', + 'default' => '', + ], + [ + 'name' => 'user', + 'label' => 'Benutzer', + 'type' => 'text', + 'storeValidator' => 'required|max:255', + 'updateValidator' => 'required|max:255', + 'default' => '', + ], + [ + 'name' => 'password', + 'label' => 'Passwort', + 'type' => 'password', + 'storeValidator' => 'required|max:255', + 'updateValidator' => 'nullable|max:255', + 'default' => '', + ], + ]; + } + + public function search(string $name, string $domain, string $email): ?MailEntry + { + $list = $this->service()->getLists()->first(fn ($list) => $list->fqdnListname === "{$name}@{$domain}"); + throw_if(!$list, MailmanServiceException::class, "List for {$name}@{$domain} not found"); + $member = $this->service()->members($list)->first(fn ($member) => $member->email === $email); + + return $member ? MailEntry::from(['email' => $member->email]) : null; + } + + public function add(string $name, string $domain, string $email): void + { + $list = $this->service()->getLists()->first(fn ($list) => $list->fqdnListname === "{$name}@{$domain}"); + throw_if(!$list, MailmanServiceException::class, "List for {$name}@{$domain} not found"); + $this->service()->addMember($list, $email); + } + + /** + * {@inheritdoc} + */ + public function list(string $name, string $domain): Collection + { + $response = $this->client()->post('cli/mail/call', [ + 'params' => [ + '--info', + "{$name}@{$domain}", + ], + ]); + + throw_unless(200 === $response->status(), PleskException::class, "Mail-Adressen für {$name}@{$domain} konnten nicht aus Plesk abgerufen werden."); + + $output = collect(explode("\n", $response->json('stdout')))->first(fn ($line) => 1 === preg_match('/^Group member\(s\):/', $line)); + throw_unless(null !== $output, PleskException::class, 'Mail-Adressen nicht in Response vorhanden'); + + $mails = trim(preg_replace('/^Group member\(s\):/', '', $output)); + + return collect(explode(' ', $mails))->map(fn ($mail) => MailEntry::from(['email' => $mail])); + } + + public function remove(string $name, string $domain, string $email): void + { + $list = $this->service()->getLists()->first(fn ($list) => $list->fqdnListname === "{$name}@{$domain}"); + throw_if(!$list, MailmanServiceException::class, "List for {$name}@{$domain} not found"); + $member = $this->service()->members($list)->first(fn ($member) => $member->email === $email); + throw_if(!$member, MailmanServiceException::class, 'Member for removing not found'); + $this->service()->removeMember($member); + } + + private function service(): MailmanService + { + return app(MailmanService::class)->setCredentials($this->url, $this->user, $this->password); + } + + private function client(): PendingRequest + { + return Http::withOptions(['base_uri' => $this->url])->withBasicAuth($this->user, $this->password); + } +} diff --git a/tests/Feature/Mailgateway/PleskTypeTest.php b/tests/Feature/Mailgateway/PleskTypeTest.php new file mode 100644 index 00000000..7f76ba8d --- /dev/null +++ b/tests/Feature/Mailgateway/PleskTypeTest.php @@ -0,0 +1,43 @@ + Http::response('{ "platform": "Unix", "hostname": "cateringpro.de", "guid": "bbc63535-00f4-4cf5-b1ca-d4e53a87c1c0", "panel_version": "18.0.52", "panel_revision": "a3b74dbc9de2e47afd4e532d02fa7759b29d3fa5", "panel_build_date": "2023-05-16", "panel_update_version": "3", "extension_version": "1.6.6", "extension_release": "215" }', 200), + ]); + $this->withoutExceptionHandling(); + $type = app(PleskType::class)->setParams(['url' => 'https://example.com:8443/api/v2/', 'user' => 'root', 'password' => 'secret']); + $this->assertTrue($type->works()); + + Http::assertSentCount(1); + Http::assertSent(fn ($request) => $request->hasHeader('Authorization', 'Basic '.base64_encode('root:secret')) && 'https://example.com:8443/api/v2/server' === $request->url()); + } + + public function testItGetsEmailAddresses(): void + { + Http::fake([ + 'https://example.com:8443/api/v2/cli/mail/call' => Http::response('{"code": 0, "stdout": "Mailname: test\nDomain: praxis-marketingagentur.de\nMailbox: true\nPassword type: sym\nMbox quota: Default value (Unlimited)\nMailgroup: true\nGroup member(s): philipp@zoomyboy.de philipp+test@zoomyboy.de \nAttachment files: Empty\nAutoresponders: Disabled\nAntivirus mail checking: Disabled\nDescription: \n\nSUCCESS: Gathering information for \'test@praxis-marketingagentur.de\' complete", "stderr": "" }', 200), + ]); + $this->withoutExceptionHandling(); + $type = app(PleskType::class)->setParams(['url' => 'https://example.com:8443/api/v2/', 'user' => 'root', 'password' => 'secret']); + $mails = $type->list('text', 'example.com'); + + $this->assertCount(2, $mails); + $this->assertEquals('philipp@zoomyboy.de', $mails->get(0)->email); + $this->assertEquals('philipp+test@zoomyboy.de', $mails->get(1)->email); + + Http::assertSentCount(1); + Http::assertSent(fn ($request) => $request->hasHeader('Authorization', 'Basic '.base64_encode('root:secret')) && 'https://example.com:8443/api/v2/cli/mail/call' === $request->url() && ['--info', 'text@example.com'] === $request['params']); + } +}