generated from Nodarx/template
ad6b7c5eba
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
137 lines
3.5 KiB
PHP
137 lines
3.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*/
|
|
|
|
namespace KTXM\ProviderImap\Smtp\Transport;
|
|
|
|
use KTXM\ProviderImap\Smtp\ConnectionConfig;
|
|
use KTXM\ProviderImap\Smtp\SmtpException;
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
final class SocketConnection implements ConnectionInterface
|
|
{
|
|
/** @var resource|null */
|
|
private $stream = null;
|
|
|
|
public function __construct(
|
|
private readonly ?LoggerInterface $logger = null,
|
|
) {}
|
|
|
|
public function connect(ConnectionConfig $config): void
|
|
{
|
|
if ($this->isConnected()) {
|
|
return;
|
|
}
|
|
|
|
$context = stream_context_create($config->streamContextOptions());
|
|
$errorCode = 0;
|
|
$errorMessage = '';
|
|
|
|
$stream = @stream_socket_client(
|
|
$config->endpoint(),
|
|
$errorCode,
|
|
$errorMessage,
|
|
$config->timeout(),
|
|
STREAM_CLIENT_CONNECT,
|
|
$context,
|
|
);
|
|
|
|
if (!is_resource($stream)) {
|
|
$this->logger?->error('SMTP socket connection failed', [
|
|
'endpoint' => $config->endpoint(),
|
|
'code' => $errorCode,
|
|
'message' => $errorMessage ?: 'unknown error',
|
|
]);
|
|
|
|
throw new SmtpException(sprintf(
|
|
'Unable to connect to %s (%d: %s)',
|
|
$config->endpoint(),
|
|
$errorCode,
|
|
$errorMessage ?: 'unknown error',
|
|
));
|
|
}
|
|
|
|
stream_set_timeout($stream, (int) $config->timeout());
|
|
$this->stream = $stream;
|
|
|
|
$this->logger?->info('SMTP socket connected to {endpoint} (timeout={timeout})', [
|
|
'endpoint' => $config->endpoint(),
|
|
'timeout' => $config->timeout(),
|
|
]);
|
|
}
|
|
|
|
public function disconnect(): void
|
|
{
|
|
if (!is_resource($this->stream)) {
|
|
return;
|
|
}
|
|
|
|
fclose($this->stream);
|
|
$this->stream = null;
|
|
|
|
$this->logger?->info('SMTP socket disconnected');
|
|
}
|
|
|
|
public function isConnected(): bool
|
|
{
|
|
return is_resource($this->stream);
|
|
}
|
|
|
|
public function write(string $payload): void
|
|
{
|
|
$stream = $this->stream();
|
|
$written = fwrite($stream, $payload);
|
|
|
|
if ($written === false || $written !== strlen($payload)) {
|
|
$this->logger?->error('SMTP socket write failed (bytes={bytes})', [
|
|
'bytes' => strlen($payload),
|
|
]);
|
|
|
|
throw new SmtpException('Failed to write complete payload to SMTP socket.');
|
|
}
|
|
}
|
|
|
|
public function readLine(): string
|
|
{
|
|
$stream = $this->stream();
|
|
$line = fgets($stream);
|
|
|
|
if ($line === false) {
|
|
$this->logger?->error('SMTP socket read failed');
|
|
throw new SmtpException('Failed to read line from SMTP socket.');
|
|
}
|
|
|
|
return $line;
|
|
}
|
|
|
|
public function upgradeToTls(): void
|
|
{
|
|
$stream = $this->stream();
|
|
$result = stream_socket_enable_crypto($stream, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
|
|
|
|
if ($result !== true) {
|
|
$this->logger?->error('SMTP TLS upgrade failed');
|
|
throw new SmtpException('Failed to enable TLS on SMTP socket.');
|
|
}
|
|
|
|
$this->logger?->info('SMTP socket upgraded to TLS');
|
|
}
|
|
|
|
/**
|
|
* @return resource
|
|
*/
|
|
private function stream()
|
|
{
|
|
if (!is_resource($this->stream)) {
|
|
throw new SmtpException('SMTP socket is not connected.');
|
|
}
|
|
|
|
return $this->stream;
|
|
}
|
|
}
|