Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/Connection/ImapConnection.php
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,11 @@ public function startTls(): void

$this->assertTaggedResponse($tag);

$this->stream->setSocketSetCrypto(true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
if ($this->stream->setSocketSetCrypto(true, STREAM_CRYPTO_METHOD_TLS_CLIENT) !== true) {
$this->disconnect();

throw new ImapConnectionFailedException('Unable to establish TLS encryption.');
}
}

/**
Expand Down
26 changes: 26 additions & 0 deletions tests/Unit/Connection/ImapConnectionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,32 @@
$connection->startTls();
})->throws(ImapCommandException::class, 'IMAP command "TAG1 STARTTLS" failed. Response: "TAG1 BAD TLS negotiation failed"');

test('start tls disconnects when encryption is not established', function (bool|int $result) {
$stream = new class($result) extends FakeStream
{
public function __construct(protected bool|int $result) {}

public function setSocketSetCrypto(bool $enabled, ?int $method): bool|int
{
return $this->result;
}
};

$stream->feed([
'* OK Welcome to IMAP',
'TAG1 OK Begin TLS negotiation now',
]);

$connection = new ImapConnection($stream);
$connection->connect('imap.example.com');

expect(fn () => $connection->startTls())->toThrow(ImapConnectionFailedException::class);
expect($connection->connected())->toBeFalse();

$stream->assertWritten('TAG1 STARTTLS');
$stream->assertNotWritten('LOGOUT');
})->with([false, 0]);

test('done', function () {
$stream = new FakeStream;
$stream->open();
Expand Down
37 changes: 37 additions & 0 deletions tests/Unit/MailboxTest.php
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
<?php

use DirectoryTree\ImapEngine\Connection\ImapConnection;
use DirectoryTree\ImapEngine\Connection\Streams\FakeStream;
use DirectoryTree\ImapEngine\Exceptions\ImapCommandException;
use DirectoryTree\ImapEngine\Exceptions\ImapConnectionFailedException;
use DirectoryTree\ImapEngine\Folder;
use DirectoryTree\ImapEngine\Mailbox;

Expand Down Expand Up @@ -99,6 +101,41 @@
]));
})->throws(ImapCommandException::class, 'IMAP command "TAG1 LOGIN [redacted] [redacted]" failed. Response: "TAG1 BAD Authentication failed"');

test('connect does not authenticate when the tls handshake fails', function (bool|int $result, string $authentication) {
$stream = new class($result) extends FakeStream
{
public function __construct(protected bool|int $result) {}

public function setSocketSetCrypto(bool $enabled, ?int $method): bool|int
{
return $this->result;
}
};

$stream->feed([
'* OK Welcome to IMAP',
'TAG1 OK Begin TLS negotiation now',
'TAG2 OK Authenticated',
]);

$mailbox = Mailbox::make([
'host' => 'imap.example.com',
'encryption' => 'starttls',
'authentication' => $authentication,
'username' => 'foo',
'password' => 'bar',
]);

expect(fn () => $mailbox->connect(new ImapConnection($stream)))
->toThrow(ImapConnectionFailedException::class);

expect($mailbox->connected())->toBeFalse();

$stream->assertWritten('TAG1 STARTTLS');
$stream->assertNotWritten('LOGIN');
$stream->assertNotWritten('AUTHENTICATE');
})->with([false, 0])->with(['plain', 'oauth']);

test('folders', function () {
$mailbox = Mailbox::make();

Expand Down
Loading