ReceivedHeader   A
last analyzed

Complexity

Total Complexity 18

Size/Duplication

Total Lines 153
Duplicated Lines 0 %

Test Coverage

Coverage 94.29%

Importance

Changes 3
Bugs 0 Features 0
Metric Value
wmc 18
eloc 28
c 3
b 0
f 0
dl 0
loc 153
ccs 33
cts 35
cp 0.9429
rs 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
A getFromName() 0 4 2
A getFromHostname() 0 4 2
A __construct() 0 12 1
A getDateTime() 0 11 4
A getByAddress() 0 4 2
A getByName() 0 4 2
A getValue() 0 3 1
A getFromAddress() 0 4 2
A getByHostname() 0 4 2
1
<?php
2
/**
3
 * This file is part of the ZBateson\MailMimeParser project.
4
 *
5
 * @license http://opensource.org/licenses/bsd-license.php BSD
6
 */
7
8
namespace ZBateson\MailMimeParser\Header;
9
10
use DateTime;
11
use Psr\Log\LoggerInterface;
12
use ZBateson\MailMimeParser\Header\Consumer\ReceivedConsumerService;
13
use ZBateson\MailMimeParser\Header\Part\DatePart;
14
use ZBateson\MailMimeParser\MailMimeParser;
15
16
/**
17
 * Represents a Received header.
18
 *
19
 * The returned header value (as returned by a call to {@see
20
 * ReceivedHeader::getValue()}) for a
21
 * ReceivedHeader is the same as the raw value (as returned by a call to
22
 * {@see ReceivedHeader::getRawValue()}) since the header doesn't have a single
23
 * 'value' to consider 'the value'.
24
 *
25
 * The parsed parts of a Received header can be accessed as parameters.  To
26
 * check if a part exists, call {@see ReceivedHeader::hasParameter()} with the
27
 * name of the part, for example: ```php $header->hasParameter('from') ``` or
28
 * ```php $header->hasParameter('id') ```.  The value of the part can be obtained
29
 * by calling {@see ReceivedHeader::getValueFor()}, for example
30
 * ```php $header->getValueFor('with'); ```.
31
 *
32
 * Additional parsing is performed on the "FROM" and "BY" parts of a received
33
 * header in an attempt to extract the self-identified name of the server, its
34
 * hostname, and its address (depending on what's included).  These can be
35
 * accessed directly from the ReceivedHeader object by calling one of the
36
 * following methods:
37
 *
38
 * o {@see ReceivedHeader::getFromName()} -- the name portion of the FROM part
39
 * o {@see ReceivedHeader::getFromHostname()} -- the hostname of the FROM part
40
 * o {@see ReceivedHeader::getFromAddress()} -- the adddress portion of the FROM
41
 *   part
42
 * o {@see ReceivedHeader::getByName()} -- same as getFromName, but for the BY
43
 *   part, and etc... below
44
 * o {@see ReceivedHeader::getByHostname()}
45
 * o {@see ReceivedHeader::getByAddress()}
46
 *
47
 * The parsed parts of the FROM and BY parts are determined as follows:
48
 *
49
 * o Anything outside and before a parenthesized expression is considered "the
50
 *   name", for example "FROM AlainDeBotton", "AlainDeBotton" would be the name,
51
 *   but also if the name is an address, but exists outside the parenthesized
52
 *   expression, it's still considered "the name".  For example:
53
 *   "From [1.2.3.4]", getFromName would return "[1.2.3.4]".
54
 * o A parenthesized expression MUST match what looks like either a domain name
55
 *   on its own, or a domain name and an address.  Otherwise the parenthesized
56
 *   expression is considered a comment, and not parsed into hostname and
57
 *   address.  The rules are defined loosely because many implementations differ
58
 *   in how strictly they follow the standard.  For a domain, it's enough that
59
 *   the expression starts with any alphanumeric character and contains at least
60
 *   one '.', followed by any number of '.', '-' and alphanumeric characters.
61
 *   The address portion must be surrounded in square brackets, and contain any
62
 *   sequence of '.', ':', numbers, and characters 'a' through 'f'.  In addition
63
 *   the string 'ipv6' may start the expression (for instance, '[ipv6:::1]'
64
 *   would be valid).  A port number may also be considered valid as part of the
65
 *   address, for example: [1.2.3.4:3231].  No additional validation on the
66
 *   address is done, and so an invalid address such as '....' could be
67
 *   returned, so users using the 'address' header are encouraged to validate it
68
 *   before using it.  The square brackets are parsed out of the returned
69
 *   address, so the value returned by getFromAddress() would be "2.2.2.2", not
70
 *   "[2.2.2.2]".
71
 *
72
 * The date/time stamp can be accessed as a DateTime object by calling
73
 * {@see ReceivedHeader::getDateTime()}.
74
 *
75
 * Parsed comments can be accessed by calling {@see
76
 * ReceivedHeader::getComments()}.  Some implementations may include connection
77
 * encryption information or other details in non-standardized comments.
78
 *
79
 * @author Zaahid Bateson
80
 */
81
class ReceivedHeader extends ParameterHeader
82
{
83
    /**
84
     * @var DateTime the date/time stamp in the header.
85
     */
86
    private ?DateTime $date = null;
87
88
    /**
89
     * @var bool set to true once $date has been looked for
90
     */
91
    private bool $dateSet = false;
92
93 6
    public function __construct(
94
        string $name,
95
        string $value,
96
        ?LoggerInterface $logger = null,
97
        ?ReceivedConsumerService $consumerService = null
98
    ) {
99 6
        $di = MailMimeParser::getGlobalContainer();
100 6
        AbstractHeader::__construct(
101 6
            $logger ?? $di->get(LoggerInterface::class),
102 6
            $consumerService ?? $di->get(ReceivedConsumerService::class),
103 6
            $name,
104 6
            $value
105 6
        );
106
    }
107
108
    /**
109
     * Returns the raw, unparsed header value, same as {@see
110
     * ReceivedHeader::getRawValue()}.
111
     */
112
    public function getValue() : ?string
113
    {
114
        return $this->rawValue;
115
    }
116
117
    /**
118
     * Returns the name identified in the FROM part of the header or null if not
119
     * defined or the format wasn't parsed.
120
     *
121
     * The returned value may either be a name or an address in the form
122
     * "[1.2.3.4]".  Validation is not performed on this value, and so whatever
123
     * exists in this position is returned -- be it contains spaces, or invalid
124
     * characters, etc...
125
     *
126
     * @return ?string The 'FROM' name.
127
     */
128 5
    public function getFromName() : ?string
129
    {
130 5
        return (isset($this->parameters['from'])) ?
131 5
            $this->parameters['from']->getEhloName() : null;
132
    }
133
134
    /**
135
     * Returns the hostname part of a parenthesized FROM part or null if not
136
     * defined or the format wasn't parsed.
137
     *
138
     * For example, "FROM name (host.name)" would return the string "host.name".
139
     * Validation of the hostname is not performed, and the returned value may
140
     * not be valid.  More details on how the value is parsed and extracted can
141
     * be found in the class description for {@see ReceivedHeader}.
142
     *
143
     * @return ?string The 'FROM' hostname.
144
     */
145 4
    public function getFromHostname() : ?string
146
    {
147 4
        return (isset($this->parameters['from'])) ?
148 4
            $this->parameters['from']->getHostname() : null;
149
    }
150
151
    /**
152
     * Returns the address part of a parenthesized FROM part or null if not
153
     * defined or the format wasn't parsed.
154
     *
155
     * For example, "FROM name ([1.2.3.4])" would return the string "1.2.3.4".
156
     * Validation of the address is not performed, and the returned value may
157
     * not be valid.  More details on how the value is parsed and extracted can
158
     * be found in the class description for {@see ReceivedHeader}.
159
     *
160
     * @return ?string The 'FROM' address.
161
     */
162 5
    public function getFromAddress() : ?string
163
    {
164 5
        return (isset($this->parameters['from'])) ?
165 5
            $this->parameters['from']->getAddress() : null;
166
    }
167
168
    /**
169
     * Returns the name identified in the BY part of the header or null if not
170
     * defined or the format wasn't parsed.
171
     *
172
     * The returned value may either be a name or an address in the form
173
     * "[1.2.3.4]".  Validation is not performed on this value, and so whatever
174
     * exists in this position is returned -- be it contains spaces, or invalid
175
     * characters, etc...
176
     *
177
     * @return ?string The 'BY' name.
178
     */
179 3
    public function getByName() : ?string
180
    {
181 3
        return (isset($this->parameters['by'])) ?
182 3
            $this->parameters['by']->getEhloName() : null;
183
    }
184
185
    /**
186
     * Returns the hostname part of a parenthesized BY part or null if not
187
     * defined or the format wasn't parsed.
188
     *
189
     * For example, "BY name (host.name)" would return the string "host.name".
190
     * Validation of the hostname is not performed, and the returned value may
191
     * not be valid.  More details on how the value is parsed and extracted can
192
     * be found in the class description for {@see ReceivedHeader}.
193
     *
194
     * @return ?string The 'BY' hostname.
195
     */
196 3
    public function getByHostname() : ?string
197
    {
198 3
        return (isset($this->parameters['by'])) ?
199 3
            $this->parameters['by']->getHostname() : null;
200
    }
201
202
    /**
203
     * Returns the address part of a parenthesized BY part or null if not
204
     * defined or the format wasn't parsed.
205
     *
206
     * For example, "BY name ([1.2.3.4])" would return the string "1.2.3.4".
207
     * Validation of the address is not performed, and the returned value may
208
     * not be valid.  More details on how the value is parsed and extracted can
209
     * be found in the class description for {@see ReceivedHeader}.
210
     *
211
     * @return ?string The 'BY' address.
212
     */
213 3
    public function getByAddress() : ?string
214
    {
215 3
        return (isset($this->parameters['by'])) ?
216 3
            $this->parameters['by']->getAddress() : null;
217
    }
218
219
    /**
220
     * Returns the date/time stamp for the received header if set, or null
221
     * otherwise.
222
     */
223 4
    public function getDateTime() : ?DateTime
224
    {
225 4
        if ($this->dateSet === false) {
226 4
            foreach ($this->parts as $part) {
227 4
                if ($part instanceof DatePart) {
228 3
                    $this->date = $part->getDateTime();
229
                }
230
            }
231 4
            $this->dateSet = true;
232
        }
233 4
        return $this->date;
234
    }
235
}
236