Completed
Pull Request — master (#64)
by Frederik
03:38 queued 01:09
created

Server::start()   A

Complexity

Conditions 5
Paths 2

Size

Total Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 5.0073

Importance

Changes 0
Metric Value
dl 0
loc 26
ccs 14
cts 15
cp 0.9333
rs 9.1928
c 0
b 0
f 0
cc 5
nc 2
nop 0
crap 5.0073
1
<?php
2
declare(strict_types=1);
3
4
namespace Genkgo\Mail\Protocol\Smtp;
5
6
use Genkgo\Mail\Exception\ConnectionBrokenException;
7
use Genkgo\Mail\Exception\ConnectionTimeoutException;
8
use Genkgo\Mail\Exception\ConnectionClosedException;
9
use Genkgo\Mail\Exception\UnknownSmtpCommandException;
10
use Genkgo\Mail\Protocol\AppendCrlfConnection;
11
use Genkgo\Mail\Protocol\ConnectionInterface;
12
use Genkgo\Mail\Protocol\ConnectionListenerInterface;
13
use Genkgo\Mail\Protocol\Smtp\Capability\EhloCapability;
14
use Genkgo\Mail\Protocol\Smtp\Capability\QuitCapability;
15
use Genkgo\Mail\Protocol\Smtp\Capability\ResetCapability;
16
use Genkgo\Mail\Protocol\TrimCrlfConnection;
17
18
final class Server
19
{
20
    /**
21
     * @var ConnectionListenerInterface
22
     */
23
    private $listener;
24
25
    /**
26
     * @var CapabilityInterface[]
27
     */
28
    private $capabilities = [];
29
30
    /**
31
     * @var string
32
     */
33
    private $serverName;
34
35
    /**
36
     * @param ConnectionListenerInterface $connection
37
     * @param array|CapabilityInterface[] $capabilities
38
     * @param string $serverName
39
     */
40 6
    public function __construct(ConnectionListenerInterface $connection, array $capabilities, string $serverName)
41
    {
42 6
        $this->listener = $connection;
43 6
        $this->serverName = $serverName;
44
45 6
        $this->capabilities = \array_merge(
0 ignored issues
show
Documentation Bug introduced by
It seems like \array_merge($capabiliti...ity\ResetCapability())) of type array is incompatible with the declared type array<integer,object<Gen...p\CapabilityInterface>> of property $capabilities.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
46 6
            $capabilities,
47
            [
48 6
                new EhloCapability($this->serverName, $capabilities),
49 6
                new QuitCapability(),
50 6
                new ResetCapability()
51
            ]
52
        );
53 6
    }
54
    
55 6
    public function start(): void
56
    {
57 6
        while ($connection = $this->listener->listen()) {
58 6
            $connection = new TrimCrlfConnection(new AppendCrlfConnection($connection));
59 6
            $connection->send('220 Welcome to Genkgo Mail Server');
60
61 6
            $this->transport(
62 6
                $connection,
63
                function (ConnectionInterface $connection) {
64 6
                    $session = new Session();
65
66 6
                    while ($session = $session->withCommand($connection->receive())) {
67
                        try {
68 2
                            $session = $this->handleCommand($connection, $session);
69 1
                        } catch (UnknownSmtpCommandException $e) {
70 1
                            $connection->send('500 unrecognized command');
71
                        }
72
73 2
                        if ($session->getState() === Session::STATE_DISCONNECT) {
74 2
                            break;
75
                        }
76
                    }
77 6
                }
78
            );
79
        }
80
    }
81
82
    /**
83
     * @param ConnectionInterface $connection
84
     * @param \Closure $callback
85
     */
86 6
    private function transport(ConnectionInterface $connection, \Closure $callback): void
87
    {
88
        try {
89 6
            $callback($connection);
90 4
        } catch (ConnectionTimeoutException $e) {
91 1
            $connection->send('421 command timeout - closing connection');
92 1
            $connection->disconnect();
93 3
        } catch (ConnectionBrokenException $e) {
94
            try {
95 2
                $connection->send('554 transaction failed, unexpected value - closing connection');
96 1
            } catch (\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
97
            }
98
99 2
            $connection->disconnect();
100 1
        } catch (ConnectionClosedException $e) {
101 1
            $connection->disconnect();
102
        }
103 6
    }
104
105
    /**
106
     * @param ConnectionInterface $connection
107
     * @param Session $session
108
     * @return Session
109
     * @throws UnknownSmtpCommandException
110
     */
111 2
    private function handleCommand(ConnectionInterface $connection, Session $session): Session
112
    {
113 2
        $command = $session->getCommand();
114
115 2
        foreach ($this->capabilities as $capability) {
116 2
            $advertised = $capability->advertise();
117 2
            if (\substr($command, 0, \strlen($advertised)) === $advertised) {
118 2
                return $capability->manifest($connection, $session);
119
            }
120
        }
121
122 1
        throw new UnknownSmtpCommandException('Cannot handle command');
123
    }
124
}
125