Completed
Push — master ( 4edb1e...e1389e )
by Frederik
02:12
created

ClientFactory::withCrypto()   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
c 0
b 0
f 0
rs 9.4285
ccs 4
cts 4
cp 1
cc 1
eloc 4
nc 1
nop 1
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\TryTlsUpgradeNegotiation;
14
use Genkgo\Mail\Protocol\SslConnection;
15
use Genkgo\Mail\Protocol\TlsConnection;
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 string
57
     */
58
    private $reconnectAfter = 'PT300S';
59
    /**
60
     * @var int
61
     */
62
    private $crypto = CryptoConstant::TYPE_BEST_PRACTISE;
63
64
    /**
65
     * ClientFactory constructor.
66
     * @param ConnectionInterface $connection
67
     */
68 9
    public function __construct(ConnectionInterface $connection)
69
    {
70 9
        $this->connection = $connection;
71 9
    }
72
73
    /**
74
     * @param float $connectionTimeout
75
     * @return ClientFactory
76
     */
77 1
    public function withTimeout(float $connectionTimeout): ClientFactory
78
    {
79 1
        $clone = clone $this;
80 1
        $clone->timeout = $connectionTimeout;
81 1
        return $clone;
82
    }
83
84
    /**
85
     * @param int $method
86
     * @param string $password
87
     * @param string $username
88
     * @return ClientFactory
89
     */
90 3
    public function withAuthentication(int $method, string $username, string $password): ClientFactory
91
    {
92 3
        if (!in_array($method, self::AUTH_ENUM)) {
93 1
            throw new \InvalidArgumentException('Invalid authentication method');
94
        }
95
96 2
        $clone = clone $this;
97 2
        $clone->authMethod = $method;
98 2
        $clone->username = $username;
99 2
        $clone->password = $password;
100 2
        return $clone;
101
    }
102
103
    /**
104
     * @param string $ehlo
105
     * @return ClientFactory
106
     */
107 4
    public function withEhlo(string $ehlo): ClientFactory
108
    {
109 4
        $clone = clone $this;
110 4
        $clone->ehlo = $ehlo;
111 4
        return $clone;
112
    }
113
114
    /**
115
     * @return ClientFactory
116
     */
117 2
    public function withInsecureConnectionAllowed(): ClientFactory
118
    {
119 2
        $clone = clone $this;
120 2
        $clone->insecureConnectionAllowed = true;
121 2
        return $clone;
122
    }
123
124
    /**
125
     * @param int $crypto
126
     * @return ClientFactory
127
     */
128 1
    public function withCrypto(int $crypto): ClientFactory
129
    {
130 1
        $clone = clone $this;
131 1
        $clone->crypto = $crypto;
132 1
        return $clone;
133
    }
134
135
    /**
136
     * @return Client
137
     */
138 7
    public function newClient(): Client
139
    {
140 7
        $negotiators = [];
141
142 7
        if ($this->crypto !== 0) {
143 6
            if ($this->insecureConnectionAllowed) {
144 1
                $negotiators[] = new TryTlsUpgradeNegotiation(
145 1
                    $this->connection,
146 1
                    $this->ehlo,
147 1
                    $this->crypto
148
                );
149
            } else {
150 5
                $negotiators[] = new ForceTlsUpgradeNegotiation(
151 5
                    $this->connection,
152 5
                    $this->ehlo,
153 5
                    $this->crypto
154
                );
155
            }
156
        }
157
158 7
        if ($this->authMethod !== Client::AUTH_NONE) {
159 2
            $negotiators[] = new AuthNegotiation(
160 2
                $this->ehlo,
161 2
                $this->authMethod,
162 2
                $this->username,
163 2
                $this->password
164
            );
165
        }
166
167 7
        return new Client(
168 7
            new AutomaticConnection(
169 7
                $this->connection,
170 7
                new \DateInterval($this->reconnectAfter)
171
            ),
172 7
            $negotiators
173
        );
174
    }
175
176
    /**
177
     * @param string $dataSourceName
178
     * @return ClientFactory
179
     */
180 6
    public static function fromString(string $dataSourceName):ClientFactory
181
    {
182 6
        $components = parse_url($dataSourceName);
183 6
        if (!isset($components['scheme']) || !isset($components['host'])) {
184 1
            throw new \InvalidArgumentException('Scheme and host are required');
185
        }
186
187 5
        $insecureConnectionAllowed = false;
188 5
        switch ($components['scheme']) {
189 5
            case 'smtp+tls':
190 1
                $connection = new TlsConnection(
191 1
                    $components['host'],
192 1
                    $components['port'] ?? 465,
193 1
                    new SecureConnectionOptions()
194
                );
195 1
                break;
196 4
            case 'smtp+ssl':
197 1
                $connection = new SslConnection(
198 1
                    $components['host'],
199 1
                    $components['port'] ?? 465,
200 1
                    new SecureConnectionOptions()
201
                );
202 1
                break;
203 3
            case 'smtp+plain':
204 1
                $insecureConnectionAllowed = true;
205 1
                $connection = new PlainTcpConnection(
206 1
                    $components['host'],
207 1
                    $components['port'] ?? 25
208
                );
209 1
                break;
210 2
            case 'smtp':
211 1
                $connection = new PlainTcpConnection(
212 1
                    $components['host'],
213 1
                    $components['port'] ?? 587
214
                );
215 1
                break;
216
            default:
217 1
                throw new \InvalidArgumentException(
218 1
                    'Use smtp:// smtp+tls:// smtp+ssl:// smtp+plain://'
219
                );
220
        }
221
222 4
        $factory = new self($connection);
223 4
        $factory->insecureConnectionAllowed = $insecureConnectionAllowed;
224
225 4
        if (isset($components['user']) && isset($components['pass'])) {
226 1
            $factory->authMethod = Client::AUTH_AUTO;
227 1
            $factory->username = urldecode($components['user']);
228 1
            $factory->password = urldecode($components['pass']);
229
        }
230
231 4
        if (isset($components['query'])) {
232 1
            parse_str($components['query'], $query);
233
234 1
            if (isset($query['ehlo'])) {
235 1
                $factory->ehlo = $query['ehlo'];
236
            }
237
238 1
            if (isset($query['timeout'])) {
239 1
                $factory->timeout = (float)$query['timeout'];
240
            }
241
242 1
            if (isset($query['reconnectAfter'])) {
243 1
                $factory->reconnectAfter = $query['reconnectAfter'];
244
            }
245
246 1
            if (isset($query['crypto'])) {
247
                // @codeCoverageIgnoreStart
248
                switch ($query['crypto']) {
249
                    case 'best':
250
                        $factory->crypto = CryptoConstant::TYPE_BEST_PRACTISE;
251
                        break;
252
                    case 'secure':
253
                        $factory->crypto = CryptoConstant::TYPE_SECURE;
254
                        break;
255
                    case 'none':
256
                        $factory->crypto = CryptoConstant::TYPE_NONE;
257
                        break;
258
                    default:
259
                        $factory->crypto = (int)$query['crypto'];
260
                        break;
261
                }
262
                // @codeCoverageIgnoreEnd
263
            }
264
        }
265
266 4
        return $factory;
267
    }
268
}
269