Completed
Pull Request — master (#32)
by Gawain
01:35
created

ClientFactory::withRfc821ConnectionAllowed()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

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