|
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
|
|
|
namespace ZBateson\MailMimeParser; |
|
8
|
|
|
|
|
9
|
|
|
use ZBateson\MailMimeParser\Message\Part\MimePart; |
|
10
|
|
|
use ZBateson\MailMimeParser\Message\PartFilter; |
|
11
|
|
|
|
|
12
|
|
|
/** |
|
13
|
|
|
* A parsed mime message with optional mime parts depending on its type. |
|
14
|
|
|
* |
|
15
|
|
|
* A mime message may have any number of mime parts, and each part may have any |
|
16
|
|
|
* number of sub-parts, etc... |
|
17
|
|
|
* |
|
18
|
|
|
* @author Zaahid Bateson |
|
19
|
|
|
*/ |
|
20
|
|
|
class Message extends MimePart |
|
21
|
|
|
{ |
|
22
|
|
|
/** |
|
23
|
|
|
* Convenience method to parse a handle or string into a Message without |
|
24
|
|
|
* requiring including MailMimeParser, instantiating it, and calling parse. |
|
25
|
|
|
* |
|
26
|
|
|
* @param resource|string $handleOrString the resource handle to the input |
|
27
|
|
|
* stream of the mime message, or a string containing a mime message |
|
28
|
|
|
*/ |
|
29
|
|
|
public static function from($handleOrString) |
|
30
|
|
|
{ |
|
31
|
|
|
$mmp = new MailMimeParser(); |
|
32
|
|
|
return $mmp->parse($handleOrString); |
|
33
|
|
|
} |
|
34
|
|
|
|
|
35
|
|
|
/** |
|
36
|
|
|
* Returns the text/plain part at the given index (or null if not found.) |
|
37
|
|
|
* |
|
38
|
|
|
* @param int $index |
|
39
|
|
|
* @return \ZBateson\MailMimeParser\Message\Part\MimePart |
|
40
|
|
|
*/ |
|
41
|
1 |
|
public function getTextPart($index = 0) |
|
42
|
|
|
{ |
|
43
|
1 |
|
return $this->getPart( |
|
44
|
1 |
|
$index, |
|
45
|
1 |
|
$this->partFilterFactory->newFilterFromInlineContentType('text/plain') |
|
46
|
1 |
|
); |
|
47
|
|
|
} |
|
48
|
|
|
|
|
49
|
|
|
/** |
|
50
|
|
|
* Returns the number of text/plain parts in this message. |
|
51
|
|
|
* |
|
52
|
|
|
* @return int |
|
53
|
|
|
*/ |
|
54
|
1 |
|
public function getTextPartCount() |
|
55
|
|
|
{ |
|
56
|
1 |
|
return $this->getPartCount( |
|
57
|
1 |
|
$this->partFilterFactory->newFilterFromInlineContentType('text/plain') |
|
58
|
1 |
|
); |
|
59
|
|
|
} |
|
60
|
|
|
|
|
61
|
|
|
/** |
|
62
|
|
|
* Returns the text/html part at the given index (or null if not found.) |
|
63
|
|
|
* |
|
64
|
|
|
* @param $index |
|
65
|
|
|
* @return \ZBateson\MailMimeParser\Message\Part\MimePart |
|
66
|
|
|
*/ |
|
67
|
1 |
|
public function getHtmlPart($index = 0) |
|
68
|
|
|
{ |
|
69
|
1 |
|
return $this->getPart( |
|
70
|
1 |
|
$index, |
|
71
|
1 |
|
$this->partFilterFactory->newFilterFromInlineContentType('text/html') |
|
72
|
1 |
|
); |
|
73
|
|
|
} |
|
74
|
|
|
|
|
75
|
|
|
/** |
|
76
|
|
|
* Returns the number of text/html parts in this message. |
|
77
|
|
|
* |
|
78
|
|
|
* @return int |
|
79
|
|
|
*/ |
|
80
|
1 |
|
public function getHtmlPartCount() |
|
81
|
|
|
{ |
|
82
|
1 |
|
return $this->getPartCount( |
|
83
|
1 |
|
$this->partFilterFactory->newFilterFromInlineContentType('text/html') |
|
84
|
1 |
|
); |
|
85
|
|
|
} |
|
86
|
|
|
|
|
87
|
|
|
/** |
|
88
|
|
|
* Returns a string containing the entire body of a signed message for |
|
89
|
|
|
* verification. |
|
90
|
|
|
* |
|
91
|
|
|
* @return string or null if the message doesn't have any children, or the |
|
92
|
|
|
* child returns null for getHandle |
|
93
|
|
|
*/ |
|
94
|
2 |
|
public function getMessageStringForSignatureVerification() |
|
95
|
|
|
{ |
|
96
|
2 |
|
$child = $this->getChild(0); |
|
97
|
2 |
|
if ($child !== null && $child->getHandle() !== null) { |
|
98
|
1 |
|
$normalized = preg_replace( |
|
99
|
1 |
|
'/\r\n|\r|\n/', |
|
100
|
1 |
|
"\r\n", |
|
101
|
1 |
|
stream_get_contents($child->getHandle()) |
|
102
|
1 |
|
); |
|
103
|
1 |
|
return $normalized; |
|
104
|
|
|
} |
|
105
|
1 |
|
return null; |
|
106
|
|
|
} |
|
107
|
|
|
|
|
108
|
|
|
/** |
|
109
|
|
|
* Returns the signature part of a multipart/signed message. |
|
110
|
|
|
* |
|
111
|
|
|
* The part returned is the part containing a Content-Type matching the one |
|
112
|
|
|
* defined in the multipart/signed part's "protocol" parameter. |
|
113
|
|
|
* |
|
114
|
|
|
* @return MimePart |
|
115
|
|
|
*/ |
|
116
|
|
|
public function getSignaturePart() |
|
117
|
|
|
{ |
|
118
|
|
|
return $this->getChild( |
|
119
|
|
|
0, |
|
120
|
|
|
$this->partFilterFactory->newFilterFromArray([ |
|
121
|
|
|
'signedpart' => PartFilter::FILTER_INCLUDE |
|
122
|
|
|
]) |
|
123
|
|
|
); |
|
124
|
|
|
} |
|
125
|
|
|
|
|
126
|
|
|
/** |
|
127
|
|
|
* Returns the attachment part at the given 0-based index, or null if none |
|
128
|
|
|
* is set. |
|
129
|
|
|
* |
|
130
|
|
|
* @param int $index |
|
131
|
|
|
* @return MessagePart |
|
132
|
|
|
*/ |
|
133
|
1 |
|
public function getAttachmentPart($index) |
|
134
|
|
|
{ |
|
135
|
1 |
|
$attachments = $this->getAllAttachmentParts(); |
|
136
|
1 |
|
if (!isset($attachments[$index])) { |
|
137
|
1 |
|
return null; |
|
138
|
|
|
} |
|
139
|
1 |
|
return $attachments[$index]; |
|
140
|
|
|
} |
|
141
|
|
|
|
|
142
|
|
|
/** |
|
143
|
|
|
* Returns all attachment parts. |
|
144
|
|
|
* |
|
145
|
|
|
* "Attachments" are any non-multipart, non-signature and any text or html |
|
146
|
|
|
* html part witha Content-Disposition set to 'attachment'. |
|
147
|
|
|
* |
|
148
|
|
|
* @return MessagePart[] |
|
149
|
|
|
*/ |
|
150
|
1 |
|
public function getAllAttachmentParts() |
|
151
|
|
|
{ |
|
152
|
1 |
|
$parts = $this->getAllParts( |
|
153
|
1 |
|
$this->partFilterFactory->newFilterFromArray([ |
|
154
|
|
|
'multipart' => PartFilter::FILTER_EXCLUDE |
|
155
|
1 |
|
]) |
|
156
|
1 |
|
); |
|
157
|
1 |
|
return array_values(array_filter( |
|
158
|
1 |
|
$parts, |
|
159
|
1 |
|
function ($part) { |
|
160
|
|
|
return !( |
|
161
|
1 |
|
$part->isTextPart() |
|
162
|
1 |
|
&& $part->getContentDisposition() === 'inline' |
|
163
|
1 |
|
); |
|
164
|
|
|
} |
|
165
|
1 |
|
)); |
|
166
|
|
|
} |
|
167
|
|
|
|
|
168
|
|
|
/** |
|
169
|
|
|
* Returns the number of attachments available. |
|
170
|
|
|
* |
|
171
|
|
|
* @return int |
|
172
|
|
|
*/ |
|
173
|
1 |
|
public function getAttachmentCount() |
|
174
|
|
|
{ |
|
175
|
1 |
|
return count($this->getAllAttachmentParts()); |
|
176
|
|
|
} |
|
177
|
|
|
|
|
178
|
|
|
/** |
|
179
|
|
|
* Returns a resource handle where the 'inline' text/plain content at the |
|
180
|
|
|
* passed $index can be read or null if unavailable. |
|
181
|
|
|
* |
|
182
|
|
|
* @param int $index |
|
183
|
|
|
* @param string $transferEncoding |
|
184
|
|
|
* @param string $charset |
|
185
|
|
|
* @return resource |
|
186
|
|
|
*/ |
|
187
|
1 |
|
public function getTextStream($index = 0, $transferEncoding = null, $charset = null) |
|
188
|
|
|
{ |
|
189
|
1 |
|
$textPart = $this->getTextPart($index); |
|
190
|
1 |
|
if ($textPart !== null) { |
|
191
|
1 |
|
return $textPart->getContentResourceHandle($transferEncoding, $charset); |
|
192
|
|
|
} |
|
193
|
1 |
|
return null; |
|
194
|
|
|
} |
|
195
|
|
|
|
|
196
|
|
|
/** |
|
197
|
|
|
* Returns the content of the inline text/plain part at the given index. |
|
198
|
|
|
* |
|
199
|
|
|
* Reads the entire stream content into a string and returns it. Returns |
|
200
|
|
|
* null if the message doesn't have an inline text part. |
|
201
|
|
|
* |
|
202
|
|
|
* @param int $index |
|
203
|
|
|
* @param string $transferEncoding |
|
204
|
|
|
* @param string $charset |
|
205
|
|
|
* @return string |
|
206
|
|
|
*/ |
|
207
|
1 |
View Code Duplication |
public function getTextContent($index = 0, $transferEncoding = null, $charset = null) |
|
|
|
|
|
|
208
|
|
|
{ |
|
209
|
1 |
|
$part = $this->getTextPart($index); |
|
210
|
1 |
|
if ($part !== null) { |
|
211
|
1 |
|
return $part->getContent($transferEncoding, $charset); |
|
212
|
|
|
} |
|
213
|
1 |
|
return null; |
|
214
|
|
|
} |
|
215
|
|
|
|
|
216
|
|
|
/** |
|
217
|
|
|
* Returns a resource handle where the 'inline' text/html content at the |
|
218
|
|
|
* passed $index can be read or null if unavailable. |
|
219
|
|
|
* |
|
220
|
|
|
* @param int $index |
|
221
|
|
|
* @param string $transferEncoding |
|
222
|
|
|
* @param string $charset |
|
223
|
|
|
* @return resource |
|
224
|
|
|
*/ |
|
225
|
1 |
View Code Duplication |
public function getHtmlStream($index = 0, $transferEncoding = null, $charset = null) |
|
|
|
|
|
|
226
|
|
|
{ |
|
227
|
1 |
|
$htmlPart = $this->getHtmlPart($index); |
|
228
|
1 |
|
if ($htmlPart !== null) { |
|
229
|
1 |
|
return $htmlPart->getContentResourceHandle($transferEncoding, $charset); |
|
230
|
|
|
} |
|
231
|
1 |
|
return null; |
|
232
|
|
|
} |
|
233
|
|
|
|
|
234
|
|
|
/** |
|
235
|
|
|
* Returns the content of the inline text/html part at the given index. |
|
236
|
|
|
* |
|
237
|
|
|
* Reads the entire stream content into a string and returns it. Returns |
|
238
|
|
|
* null if the message doesn't have an inline html part. |
|
239
|
|
|
* |
|
240
|
|
|
* @param int $index |
|
241
|
|
|
* @param string $transferEncoding |
|
242
|
|
|
* @param string $charset |
|
243
|
|
|
* @return string |
|
244
|
|
|
*/ |
|
245
|
1 |
View Code Duplication |
public function getHtmlContent($index = 0, $transferEncoding = null, $charset = null) |
|
|
|
|
|
|
246
|
|
|
{ |
|
247
|
1 |
|
$part = $this->getHtmlPart($index); |
|
248
|
1 |
|
if ($part !== null) { |
|
249
|
1 |
|
return $part->getContent($transferEncoding, $charset); |
|
250
|
|
|
} |
|
251
|
1 |
|
return null; |
|
252
|
|
|
} |
|
253
|
|
|
|
|
254
|
|
|
/** |
|
255
|
|
|
* Returns true if either a Content-Type or Mime-Version header are defined |
|
256
|
|
|
* in this Message. |
|
257
|
|
|
* |
|
258
|
|
|
* @return bool |
|
259
|
|
|
*/ |
|
260
|
3 |
|
public function isMime() |
|
261
|
|
|
{ |
|
262
|
3 |
|
$contentType = $this->getHeaderValue('Content-Type'); |
|
263
|
3 |
|
$mimeVersion = $this->getHeaderValue('Mime-Version'); |
|
264
|
3 |
|
return ($contentType !== null || $mimeVersion !== null); |
|
265
|
|
|
} |
|
266
|
|
|
|
|
267
|
|
|
/** |
|
268
|
|
|
* Saves the message as a MIME message to the passed resource handle. |
|
269
|
|
|
* |
|
270
|
|
|
* @param resource $handle |
|
271
|
|
|
*/ |
|
272
|
1 |
|
public function save($handle) |
|
273
|
|
|
{ |
|
274
|
1 |
|
if (is_resource($this->handle)) { |
|
275
|
1 |
|
rewind($this->handle); |
|
276
|
1 |
|
stream_copy_to_stream($this->handle, $handle); |
|
277
|
1 |
|
} |
|
278
|
1 |
|
} |
|
279
|
|
|
|
|
280
|
|
|
/** |
|
281
|
|
|
* Shortcut to call Message::save with a php://temp stream and return the |
|
282
|
|
|
* written email message as a string. |
|
283
|
|
|
* |
|
284
|
|
|
* @return string |
|
285
|
|
|
*/ |
|
286
|
1 |
|
public function __toString() |
|
287
|
|
|
{ |
|
288
|
1 |
|
$handle = fopen('php://temp', 'r+'); |
|
289
|
1 |
|
$this->save($handle); |
|
290
|
1 |
|
rewind($handle); |
|
291
|
1 |
|
$str = stream_get_contents($handle); |
|
292
|
1 |
|
fclose($handle); |
|
293
|
1 |
|
return $str; |
|
294
|
|
|
} |
|
295
|
|
|
} |
|
296
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.