Completed
Push — master ( 375ed7...6d3e15 )
by Frederik
02:34
created

TryTlsUpgradeNegotiation   A

Complexity

Total Complexity 8

Size/Duplication

Total Lines 87
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Test Coverage

Coverage 82.76%

Importance

Changes 0
Metric Value
wmc 8
lcom 1
cbo 8
dl 0
loc 87
ccs 24
cts 29
cp 0.8276
rs 10
c 0
b 0
f 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
B negotiate() 0 50 7
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 6
    public function __construct(
41
        ConnectionInterface $connection,
42
        string $ehlo,
43
        int $crypto
44
    ) {
45 6
        $this->connection = $connection;
46 6
        $this->ehlo = $ehlo;
47 6
        $this->crypto = $crypto;
48 6
    }
49
50
    /**
51
     * @param Client $client
52
     * @throws ConnectionInsecureException
53
     */
54 5
    public function negotiate(Client $client): void
55
    {
56 5
        if (!empty($this->connection->getMetaData(['crypto']))) {
57 1
            return;
58
        }
59
60 4
        $reply = $client->request(new EhloCommand($this->ehlo));
61
62 4
        if ($reply->isCommandNotImplemented()) {
63
            // since EHLO is not implemented, let's try HELO and then STARTTLS
64 2
            $reply = $client->request(new HeloCommand($this->ehlo));
65 2
            $reply->assertCompleted();
66
67
            try {
68
                $client
69 2
                    ->request(new StartTlsCommand())
70 2
                    ->assertCompleted();
71
72 1
                $this->connection->upgrade($this->crypto);
73 1
            } catch (AssertionFailedException $e) {
74
                // apparently HELO OR STARTTLS command is also not implemented
75
                // but failure of STARTTLS is allowed
76
            } catch (SecureConnectionUpgradeException $e) {
77
                // apparently STARTTLS command is implemented
78
                // but failed to start a secure connection
79
                $client
80
                    ->request(new RsetCommand())
81 2
                    ->assertCompleted();
82
            }
83
        } else {
84 2
            $reply->assertCompleted();
85
86 2
            $ehloResponse = new EhloResponse($reply);
87
88 2
            if ($ehloResponse->isAdvertising('STARTTLS')) {
89
                $client
90 1
                    ->request(new StartTlsCommand())
91 1
                    ->assertCompleted();
92
93
                try {
94 1
                    $this->connection->upgrade($this->crypto);
95
                } catch (SecureConnectionUpgradeException $e) {
96
                    // failed to start a secure connection
97
                    $client
98
                        ->request(new RsetCommand())
99
                        ->assertCompleted();
100
                }
101
            }
102
        }
103 4
    }
104
}
105