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

HeaderV1Factory   A

Complexity

Total Complexity 4

Size/Duplication

Total Lines 158
Duplicated Lines 0 %

Test Coverage

Coverage 95.45%

Importance

Changes 0
Metric Value
wmc 4
dl 0
loc 158
ccs 42
cts 44
cp 0.9545
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A hp$0 ➔ __construct() 0 4 1
A hp$0 ➔ getName() 0 4 1
A hp$0 ➔ getValue() 0 4 1
A __construct() 0 9 1
B factory() 0 53 5
A newUnquotedParameter() 0 4 1
B newHeader() 0 35 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 2
    public function __construct(
49
        SignInterface $sign,
50
        CanonicalizeHeaderInterface $canonicalizeHeader,
51
        CanonicalizeBodyInterface $canonicalizeBody
52
    ) {
53 2
        $this->sign = $sign;
54 2
        $this->canonicalizeHeader = $canonicalizeHeader;
55 2
        $this->canonicalizeBody = $canonicalizeBody;
56 2
    }
57
58
    /**
59
     * @param MessageInterface $message
60
     * @param string $domain
61
     * @param string $selector
62
     * @param array $parameters
63
     * @return HeaderInterface
64
     */
65 2
    public function factory(
66
        MessageInterface $message,
67
        string $domain,
68
        string $selector,
69
        array $parameters = []
70
    ): HeaderInterface
71
    {
72 2
        $bodyHash = $this->sign->hashBody(
73 2
            $this->canonicalizeBody->canonicalize($message->getBody())
74
        );
75
76 2
        $headerCanonicalized = [];
77 2
        $headerNames = [];
78 2
        foreach ($message->getHeaders() as $headers) {
79
            /** @var HeaderInterface $header */
80 2
            foreach ($headers as $header) {
81 2
                $headerName = strtolower((string)$header->getName());
82 2
                if (isset(self::HEADERS_IGNORED[$headerName])) {
83
                    break;
84
                }
85
86
                // make sure we sign only the last header in the list
87 2
                $headerCanonicalized[$headerName] = $this->canonicalizeHeader->canonicalize($header);
88 2
                $headerNames[$headerName] = (string)$header->getName();
89
            }
90
        }
91
92 2
        $canonicalization = [$this->canonicalizeHeader->name(), $this->canonicalizeBody->name()];
93
94 2
        $headerValue = (new HeaderValue('v=1'))
95 2
            ->withParameter($this->newUnquotedParameter('a', $this->sign->name()))
96 2
            ->withParameter($this->newUnquotedParameter('q', 'dns/txt'))
97 2
            ->withParameter($this->newUnquotedParameter('c', implode('/', $canonicalization)))
98 2
            ->withParameter($this->newUnquotedParameter('d', $domain), true)
99 2
            ->withParameter($this->newUnquotedParameter('s', $selector))
100 2
            ->withParameter($this->newUnquotedParameter('h', implode(':', $headerNames)), true)
101 2
            ->withParameter($this->newUnquotedParameter('bh', base64_encode($bodyHash)), true)
102 2
            ->withParameter($this->newUnquotedParameter('b', ''), true);
103
104 2
        foreach ($parameters as $key => $value) {
105
            $headerValue = $headerValue->withParameter(new HeaderValueParameter($key, $value));
106
        }
107
108 2
        $headerCanonicalized[strtolower(self::HEADER_NAME)] = $this->canonicalizeHeader->canonicalize(
109 2
            $this->newHeader($headerValue)
110
        );
111
112 2
        $headers = implode("\r\n", $headerCanonicalized);
113 2
        $signature = base64_encode($this->sign->signHeaders($headers));
114 2
        $headerValue = $headerValue->withParameter(new HeaderValueParameter('b', $signature), true);
115
116 2
        return $this->newHeader($headerValue);
117
    }
118
119
    /**
120
     * @param string $name
121
     * @param string $value
122
     * @return HeaderValueParameter
123
     */
124 2
    private function newUnquotedParameter(string $name, string $value): HeaderValueParameter
125
    {
126 2
        return (new HeaderValueParameter($name, $value))->withSpecials('');
127
    }
128
129
    /**
130
     * @param HeaderValue $headerValue
131
     * @return HeaderInterface
132
     */
133
    private function newHeader(HeaderValue $headerValue): HeaderInterface
134
    {
135
        return new class ($headerValue) implements HeaderInterface
136
        {
137
            /**
138
             * @var HeaderValue
139
             */
140
            private $headerValue;
141
142
            /**
143
             * DkimSignature constructor.
144
             * @param HeaderValue $headerValue
145
             */
146 2
            public function __construct(HeaderValue $headerValue)
147
            {
148 2
                $this->headerValue = $headerValue;
149 2
            }
150
151
            /**
152
             * @return HeaderName
153
             */
154 2
            public function getName(): HeaderName
155
            {
156 2
                return new HeaderName(HeaderV1Factory::HEADER_NAME);
157
            }
158
159
            /**
160
             * @return HeaderValue
161
             */
162 2
            public function getValue(): HeaderValue
163
            {
164 2
                return $this->headerValue;
165
            }
166
        };
167
    }
168
169
}