feat: initial version

Signed-off-by: Sebastian Krupinski <root@LAPTOP-7DVOR6NC>
This commit was merged in pull request #1.
This commit is contained in:
Sebastian Krupinski
2026-02-20 16:41:19 -05:00
committed by Sebastian Krupinski
parent a313767846
commit e51c65bf19
139 changed files with 11256 additions and 0 deletions

View File

@@ -0,0 +1,148 @@
<?php
declare(strict_types=1);
namespace Gricob\IMAP\Transport\Socket;
use Exception;
use Gricob\IMAP\Transport\Connection;
use Gricob\IMAP\Transport\ConnectionFailed;
use Gricob\IMAP\Transport\ResponseStream;
class SocketConnection implements Connection
{
private string $transport;
private string $host;
private int $port;
private float $timeout;
private bool $verifyPeer;
private bool $allowSelfSigned;
private bool $verifyPeerName;
/**
* @var resource|false
*/
private $stream = false;
public function __construct(
string $transport,
string $host,
int $port,
float $timeout,
bool $verifyPeer = true,
bool $verifyPeerName = true,
bool $allowSelfSigned = false,
) {
$this->port = $port;
$this->host = $host;
$this->transport = $transport;
$this->timeout = $timeout;
$this->verifyPeer = $verifyPeer;
$this->verifyPeerName = $verifyPeerName;
$this->allowSelfSigned = $allowSelfSigned;
}
public function __destruct()
{
$this->close();
}
public function isOpen(): bool
{
return false !== $this->stream;
}
public function open(): void
{
if ($this->isOpen()) {
return;
}
$this->stream = @stream_socket_client(
sprintf('%s://%s:%s', $this->transport, $this->host, $this->port),
$errorCode,
$errorMessage,
$this->timeout,
context: stream_context_create([
'ssl' => [
'verify_peer' => $this->verifyPeer,
'verify_peer_name' => $this->verifyPeerName,
'allow_self_signed' => $this->allowSelfSigned,
]
])
);
if (false === $this->stream) {
throw new ConnectionFailed(
sprintf('SocketConnection failed [%s]: %s', $errorCode, $errorMessage)
);
}
}
public function close(): void
{
if (!$this->stream) {
return;
}
fclose($this->stream);
$this->stream = false;
}
/**
* Upgrade an open plain-TCP socket to TLS in-place (STARTTLS patch).
*
* Must be called after the server has responded OK to a STARTTLS command.
*
* @throws ConnectionFailed
*/
public function upgradeTls(): void
{
if (!$this->stream) {
throw new ConnectionFailed('Cannot upgrade TLS: connection is not open');
}
stream_context_set_option($this->stream, [
'ssl' => [
'peer_name' => $this->host,
'verify_peer' => $this->verifyPeer,
'verify_peer_name' => $this->verifyPeerName,
'allow_self_signed' => $this->allowSelfSigned,
],
]);
$cryptoMethod = STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
| STREAM_CRYPTO_METHOD_TLSv1_3_CLIENT;
$result = @stream_socket_enable_crypto(
$this->stream,
true,
$cryptoMethod,
);
if ($result !== true) {
$last = error_get_last();
$detail = $last['message'] ?? 'unknown error';
throw new ConnectionFailed('STARTTLS upgrade failed: ' . $detail);
}
}
public function send(string $data): void
{
if (!$this->stream) {
throw new Exception('Unable to send data. SocketConnection is not open');
}
fwrite($this->stream, $data);
}
public function receive(): ResponseStream
{
if (!$this->stream) {
throw new Exception('Unable to receive data. SocketConnection is not open');
}
return new SocketResponseStream($this->stream);
}
}