Completed
Pull Request — master (#18)
by Frederik
01:37
created

HeaderV1Factory::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 5
cts 5
cp 1
rs 9.6666
c 0
b 0
f 0
cc 1
eloc 7
nc 1
nop 3
crap 1
1
<?php
2
declare(strict_types=1);
3
4
namespace Genkgo\Mail\Dkim;
5
6
use Genkgo\Mail\Header\HeaderName;
7
use Genkgo\Mail\Header\HeaderValue;
8
use Genkgo\Mail\Header\HeaderValueParameter;
9
use Genkgo\Mail\HeaderInterface;
10
use Genkgo\Mail\MessageInterface;
11
12
final class HeaderV1Factory
13
{
14
    /**
15
     *
16
     */
17
    public CONST HEADER_NAME = 'DKIM-Signature';
18
    /**
19
     * @var array
20
     */
21
    private CONST HEADERS_IGNORED = [
22
        'return-path' => true,
23
        'received' => true,
24
        'comments' => true,
25
        'keywords' => true,
26
        'bcc' => true,
27
        'resent-bcc' => true,
28
    ];
29
    /**
30
     * @var SignInterface
31
     */
32
    private $sign;
33
    /**
34
     * @var CanonicalizeHeaderInterface
35
     */
36
    private $canonicalizeHeader;
37
    /**
38
     * @var CanonicalizeBodyInterface
39
     */
40
    private $canonicalizeBody;
41
42
    /**
43
     * SignedTransport constructor.
44
     * @param SignInterface $sign
45
     * @param CanonicalizeHeaderInterface $canonicalizeHeader
46
     * @param CanonicalizeBodyInterface $canonicalizeBody
47
     */
48 5
    public function __construct(
49
        SignInterface $sign,
50
        CanonicalizeHeaderInterface $canonicalizeHeader,
51
        CanonicalizeBodyInterface $canonicalizeBody
52
    ) {
53 5
        $this->sign = $sign;
54 5
        $this->canonicalizeHeader = $canonicalizeHeader;
55 5
        $this->canonicalizeBody = $canonicalizeBody;
56 5
    }
57
58
    /**
59
     * @param MessageInterface $message
60
     * @param Parameters $parameters
61
     * @return HeaderInterface
62
     */
63 5
    public function factory(
64
        MessageInterface $message,
65
        Parameters $parameters
66
    ): HeaderInterface
67
    {
68 5
        $headerCanonicalized = [];
69 5
        $headerNames = [];
70 5
        foreach ($message->getHeaders() as $headers) {
71
            /** @var HeaderInterface $header */
72 5
            foreach ($headers as $header) {
73 5
                $headerName = strtolower((string)$header->getName());
74 5
                if (isset(self::HEADERS_IGNORED[$headerName])) {
75 1
                    break;
76
                }
77
78
                // make sure we sign only the last header in the list
79 5
                $headerCanonicalized[$headerName] = $this->canonicalizeHeader->canonicalize($header);
80 5
                $headerNames[$headerName] = (string)$header->getName();
81
            }
82
        }
83
84 5
        $bodyHash = $this->sign->hashBody(
85 5
            $this->canonicalizeBody->canonicalize($message->getBody())
86
        );
87
88 5
        $canonicalization = [$this->canonicalizeHeader->name(), $this->canonicalizeBody->name()];
89
90 5
        $headerValue = $parameters->newHeaderValue()
91 5
            ->withParameter($this->newUnquotedParameter('a', $this->sign->name()))
92 5
            ->withParameter($this->newUnquotedParameter('c', implode('/', $canonicalization)))
93 5
            ->withParameter($this->newUnquotedParameter('h', implode(':', $headerNames)), true)
94 5
            ->withParameter($this->newUnquotedParameter('bh', base64_encode($bodyHash)), true)
95 5
            ->withParameter($this->newUnquotedParameter('b', ''), true);
96
97 5
        $headerCanonicalized[strtolower(self::HEADER_NAME)] = $this->canonicalizeHeader->canonicalize(
98 5
            $this->newHeader($headerValue)
99
        );
100
101 5
        $headers = implode("\r\n", $headerCanonicalized);
102 5
        $signature = base64_encode($this->sign->signHeaders($headers));
103 5
        $headerValue = $headerValue->withParameter(new HeaderValueParameter('b', $signature), true);
104
105 5
        return $this->newHeader($headerValue);
106
    }
107
108
    /**
109
     * @param string $name
110
     * @param string $value
111
     * @return HeaderValueParameter
112
     */
113 5
    private function newUnquotedParameter(string $name, string $value): HeaderValueParameter
114
    {
115 5
        return (new HeaderValueParameter($name, $value))->withSpecials('');
116
    }
117
118
    /**
119
     * @param HeaderValue $headerValue
120
     * @return HeaderInterface
121
     */
122
    private function newHeader(HeaderValue $headerValue): HeaderInterface
123
    {
124
        return new class ($headerValue) implements HeaderInterface
125
        {
126
            /**
127
             * @var HeaderValue
128
             */
129
            private $headerValue;
130
131
            /**
132
             * DkimSignature constructor.
133
             * @param HeaderValue $headerValue
134
             */
135 5
            public function __construct(HeaderValue $headerValue)
136
            {
137 5
                $this->headerValue = $headerValue;
138 5
            }
139
140
            /**
141
             * @return HeaderName
142
             */
143 4
            public function getName(): HeaderName
144
            {
145 4
                return new HeaderName(HeaderV1Factory::HEADER_NAME);
146
            }
147
148
            /**
149
             * @return HeaderValue
150
             */
151 5
            public function getValue(): HeaderValue
152
            {
153 5
                return $this->headerValue;
154
            }
155
        };
156
    }
157
158
}