Completed
Push — master ( a08b08...57d461 )
by Frederik
03:39
created

TryTlsUpgradeNegotiation::negotiate()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 37

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 37
ccs 18
cts 18
cp 1
rs 9.0168
c 0
b 0
f 0
cc 5
nc 6
nop 1
crap 5
1
<?php
2
declare(strict_types=1);
3
4
namespace Genkgo\Mail\Protocol\Smtp\Negotiation;
5
6
use Genkgo\Mail\Exception\AssertionFailedException;
7
use Genkgo\Mail\Exception\ConnectionInsecureException;
8
use Genkgo\Mail\Exception\SecureConnectionUpgradeException;
9
use Genkgo\Mail\Protocol\ConnectionInterface;
10
use Genkgo\Mail\Protocol\Smtp\Client;
11
use Genkgo\Mail\Protocol\Smtp\NegotiationInterface;
12
use Genkgo\Mail\Protocol\Smtp\Request\EhloCommand;
13
use Genkgo\Mail\Protocol\Smtp\Request\HeloCommand;
14
use Genkgo\Mail\Protocol\Smtp\Request\RsetCommand;
15
use Genkgo\Mail\Protocol\Smtp\Request\StartTlsCommand;
16
use Genkgo\Mail\Protocol\Smtp\Response\EhloResponse;
17
18
final class TryTlsUpgradeNegotiation implements NegotiationInterface
19
{
20
    /**
21
     * @var ConnectionInterface
22
     */
23
    private $connection;
24
25
    /**
26
     * @var string
27
     */
28
    private $ehlo;
29
30
    /**
31
     * @var int
32
     */
33
    private $crypto;
34
35
    /**
36
     * @param ConnectionInterface $connection
37
     * @param string $ehlo
38
     * @param int $crypto
39
     */
40 7
    public function __construct(
41
        ConnectionInterface $connection,
42
        string $ehlo,
43
        int $crypto
44
    ) {
45 7
        $this->connection = $connection;
46 7
        $this->ehlo = $ehlo;
47 7
        $this->crypto = $crypto;
48 7
    }
49
50
    /**
51
     * @param Client $client
52
     * @throws ConnectionInsecureException
53
     */
54 7
    public function negotiate(Client $client): void
55
    {
56 7
        if (!empty($this->connection->getMetaData(['crypto']))) {
57 1
            return;
58
        }
59
60 6
        $reply = $client->request(new EhloCommand($this->ehlo));
61
62 6
        if ($reply->isCommandNotImplemented()) {
63
            // since EHLO is not implemented, let's try HELO and then STARTTLS
64 3
            $reply = $client->request(new HeloCommand($this->ehlo));
65 3
            $reply->assertCompleted();
66
67
            try {
68
                $client
69 3
                    ->request(new StartTlsCommand())
70 3
                    ->assertCompleted();
71
72 2
                $this->connection->upgrade($this->crypto);
73 3
            } catch (AssertionFailedException $e) {
74
                // apparently HELO OR STARTTLS command is also not implemented
75
                // but failure of STARTTLS is allowed
76
            }
77
        } else {
78 3
            $reply->assertCompleted();
79
80 3
            $ehloResponse = new EhloResponse($reply);
81
82 3
            if ($ehloResponse->isAdvertising('STARTTLS')) {
83
                $client
84 2
                    ->request(new StartTlsCommand())
85 2
                    ->assertCompleted();
86
87 2
                $this->connection->upgrade($this->crypto);
88
            }
89
        }
90 4
    }
91
}
92