Completed
Push — master ( 682097...a08b08 )
by Frederik
03:19
created

ClientFactory::withInsecureConnectionAllowed()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 4
cts 4
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php
2
declare(strict_types=1);
3
4
namespace Genkgo\Mail\Protocol\Smtp;
5
6
use Genkgo\Mail\Protocol\AutomaticConnection;
7
use Genkgo\Mail\Protocol\ConnectionInterface;
8
use Genkgo\Mail\Protocol\CryptoConstant;
9
use Genkgo\Mail\Protocol\PlainTcpConnection;
10
use Genkgo\Mail\Protocol\SecureConnection;
11
use Genkgo\Mail\Protocol\SecureConnectionOptions;
12
use Genkgo\Mail\Protocol\Smtp\Negotiation\AuthNegotiation;
13
use Genkgo\Mail\Protocol\Smtp\Negotiation\EhloOnlyNegotiation;
14
use Genkgo\Mail\Protocol\Smtp\Negotiation\ForceTlsUpgradeNegotiation;
15
use Genkgo\Mail\Protocol\Smtp\Negotiation\TryTlsUpgradeNegotiation;
16
17
final class ClientFactory
18
{
19
    private const AUTH_ENUM = [Client::AUTH_NONE, Client::AUTH_PLAIN, Client::AUTH_LOGIN, Client::AUTH_AUTO];
20
21
    /**
22
     * @var ConnectionInterface
23
     */
24
    private $connection;
25
26
    /**
27
     * @var string
28
     */
29
    private $password = '';
30
31
    /**
32
     * @var float
33
     */
34
    private $timeout = 1;
35
36
    /**
37
     * @var string
38
     */
39
    private $username = '';
40
41
    /**
42
     * @var string
43
     */
44
    private $ehlo = '127.0.0.1';
45
46
    /**
47
     * @var int
48
     */
49
    private $authMethod = Client::AUTH_NONE;
50
51
    /**
52
     * @var bool
53
     */
54
    private $insecureConnectionAllowed = false;
55
56
    /**
57
     * @var string
58
     */
59
    private $reconnectAfter = 'PT300S';
60
61
    /**
62
     * @var int
63
     */
64
    private $startTls;
65
66
    /**
67
     * @param ConnectionInterface $connection
68
     */
69 9
    public function __construct(ConnectionInterface $connection)
70
    {
71 9
        $this->connection = $connection;
72 9
        $this->startTls = CryptoConstant::getDefaultMethod(PHP_VERSION);
73 9
    }
74
75
    /**
76
     * @param float $connectionTimeout
77
     * @return ClientFactory
78
     */
79 1
    public function withTimeout(float $connectionTimeout): ClientFactory
80
    {
81 1
        $clone = clone $this;
82 1
        $clone->timeout = $connectionTimeout;
83 1
        return $clone;
84
    }
85
86
    /**
87
     * @param int $method
88
     * @param string $password
89
     * @param string $username
90
     * @return ClientFactory
91
     */
92 3
    public function withAuthentication(int $method, string $username, string $password): ClientFactory
93
    {
94 3
        if (!\in_array($method, self::AUTH_ENUM)) {
95 1
            throw new \InvalidArgumentException('Invalid authentication method');
96
        }
97
98 2
        $clone = clone $this;
99 2
        $clone->authMethod = $method;
100 2
        $clone->username = $username;
101 2
        $clone->password = $password;
102 2
        return $clone;
103
    }
104
105
    /**
106
     * @param string $ehlo
107
     * @return ClientFactory
108
     */
109 4
    public function withEhlo(string $ehlo): ClientFactory
110
    {
111 4
        $clone = clone $this;
112 4
        $clone->ehlo = $ehlo;
113 4
        return $clone;
114
    }
115
116
    /**
117
     * @return ClientFactory
118
     */
119 2
    public function withInsecureConnectionAllowed(): ClientFactory
120
    {
121 2
        $clone = clone $this;
122 2
        $clone->insecureConnectionAllowed = true;
123 2
        return $clone;
124
    }
125
126
    /**
127
     * @param int $crypto
128
     * @return ClientFactory
129
     */
130 1
    public function withStartTls(int $crypto): ClientFactory
131
    {
132 1
        $clone = clone $this;
133 1
        $clone->startTls = $crypto;
134 1
        return $clone;
135
    }
136
137
    /**
138
     * @return ClientFactory
139
     */
140 1
    public function withoutStartTls(): ClientFactory
141
    {
142 1
        $clone = clone $this;
143 1
        $clone->startTls = 0;
144 1
        return $clone;
145
    }
146
147
    /**
148
     * @return Client
149
     */
150 7
    public function newClient(): Client
151
    {
152 7
        $negotiators = [];
153
154 7
        if ($this->startTls === 0 && $this->authMethod === Client::AUTH_NONE) {
155 2
            $negotiators[] = new EhloOnlyNegotiation($this->connection, $this->ehlo);
156 5
        } elseif ($this->startTls !== 0) {
157 5
            if ($this->insecureConnectionAllowed) {
158
                $negotiators[] = new TryTlsUpgradeNegotiation(
159
                    $this->connection,
160
                    $this->ehlo,
161
                    $this->startTls
162
                );
163
            } else {
164 5
                $negotiators[] = new ForceTlsUpgradeNegotiation(
165 5
                    $this->connection,
166 5
                    $this->ehlo,
167 5
                    $this->startTls
168
                );
169
            }
170
        }
171
172 7
        if ($this->authMethod !== Client::AUTH_NONE) {
173 2
            $negotiators[] = new AuthNegotiation(
174 2
                $this->ehlo,
175 2
                $this->authMethod,
176 2
                $this->username,
177 2
                $this->password
178
            );
179
        }
180
181 7
        return new Client(
182 7
            new AutomaticConnection(
183 7
                $this->connection,
184 7
                new \DateInterval($this->reconnectAfter)
185
            ),
186
            $negotiators
187
        );
188
    }
189
190
    /**
191
     * @param string $dataSourceName
192
     * @return ClientFactory
193
     */
194 6
    public static function fromString(string $dataSourceName):ClientFactory
195
    {
196 6
        $components = \parse_url($dataSourceName);
197 6
        if ($components === false || !isset($components['scheme']) || !isset($components['host'])) {
198 1
            throw new \InvalidArgumentException('Scheme and host are required');
199
        }
200
201 5
        if (isset($components['query'])) {
202 2
            \parse_str($components['query'], $query);
203
        } else {
204 3
            $query = [];
205
        }
206
207 5
        $insecureConnectionAllowed = false;
208 5
        $startTls = true;
209 5
        switch ($components['scheme']) {
210 5
            case 'smtp':
211 1
                $connection = new PlainTcpConnection(
212 1
                    $components['host'],
213 1
                    $components['port'] ?? 587
214
                );
215 1
                break;
216 4
            case 'smtps':
217 2
                $connection = new SecureConnection(
218 2
                    $components['host'],
219 2
                    $components['port'] ?? 465,
220 2
                    new SecureConnectionOptions(
221 2
                        (int)($query['crypto'] ?? CryptoConstant::getDefaultMethod(PHP_VERSION))
222
                    )
223
                );
224 2
                break;
225 2
            case 'smtp-starttls':
226 1
                $insecureConnectionAllowed = true;
227 1
                $startTls = false;
228
229 1
                $connection = new PlainTcpConnection(
230 1
                    $components['host'],
231 1
                    $components['port'] ?? 25
232
                );
233 1
                break;
234 1
            case 'smtp+starttls':
235
                $insecureConnectionAllowed = true;
236
                $startTls = true;
237
238
                $connection = new PlainTcpConnection(
239
                    $components['host'],
240
                    $components['port'] ?? 25
241
                );
242
                break;
243
            default:
244 1
                throw new \InvalidArgumentException(\sprintf(
245 1
                    'Provided scheme "%s://" is invalid. Only smtp:// smtps:// and smtp-starttls:// are supported',
246 1
                    $components['scheme']
247
                ));
248
        }
249
250 4
        $factory = new self($connection);
251 4
        $factory->insecureConnectionAllowed = $insecureConnectionAllowed;
252
253 4
        if (isset($components['user'], $components['pass'])) {
254 1
            $factory->authMethod = Client::AUTH_AUTO;
255 1
            $factory->username = \urldecode($components['user']);
256 1
            $factory->password = \urldecode($components['pass']);
257
        }
258
259 4
        if (isset($query['ehlo'])) {
260 1
            $factory->ehlo = $query['ehlo'];
261
        }
262
263 4
        if (isset($query['timeout'])) {
264 1
            $factory->timeout = (float)$query['timeout'];
265
        }
266
267 4
        if (isset($query['reconnectAfter'])) {
268 1
            $factory->reconnectAfter = $query['reconnectAfter'];
269
        }
270
271 4
        if (!$startTls) {
272 1
            $factory->startTls = 0;
273 3
        } elseif (isset($query['crypto'])) {
274 1
            $factory->startTls = (int)$query['crypto'];
275
        }
276
277 4
        return $factory;
278
    }
279
}
280