Passed
Push — 1.0.0 ( d49389...cbb558 )
by Zaahid
03:21
created

Message   A

Complexity

Total Complexity 39

Size/Duplication

Total Lines 476
Duplicated Lines 0 %

Test Coverage

Coverage 50.68%

Importance

Changes 0
Metric Value
eloc 117
dl 0
loc 476
ccs 75
cts 148
cp 0.5068
rs 9.28
c 0
b 0
f 0
wmc 39

27 Methods

Rating   Name   Duplication   Size   Complexity  
A removeAllTextParts() 0 6 1
A getAllAttachmentParts() 0 13 2
A removeAllHtmlParts() 0 6 1
A getAttachmentPart() 0 7 2
A isMime() 0 5 2
A addAttachmentPart() 0 10 2
A setTextPart() 0 6 1
A getHtmlPartCount() 0 4 1
A addAttachmentPartFromFile() 0 7 2
A __construct() 0 20 1
A getTextPartCount() 0 4 1
A getHtmlStream() 0 7 2
A getTextPart() 0 5 1
A getHtmlContent() 0 7 2
A removeTextPart() 0 6 1
A setAsMultipartSigned() 0 12 2
A setHtmlPart() 0 6 1
A getSignaturePart() 0 7 2
A getSignedMessageAsString() 0 12 2
A getHtmlPart() 0 5 1
A setSignature() 0 4 1
A removeHtmlPart() 0 6 1
A from() 0 4 1
A getTextStream() 0 7 2
A getAttachmentCount() 0 3 1
A removeAttachmentPart() 0 4 1
A getTextContent() 0 7 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
namespace ZBateson\MailMimeParser;
8
9
use Psr\Http\Message\StreamInterface;
10
use ZBateson\MailMimeParser\Header\HeaderFactory;
11
use ZBateson\MailMimeParser\Message\Helper\MessageHelperService;
12
use ZBateson\MailMimeParser\Message\Part\MimePart;
13
use ZBateson\MailMimeParser\Message\Part\PartBuilder;
14
use ZBateson\MailMimeParser\Message\Part\PartStreamFilterManager;
15
use ZBateson\MailMimeParser\Message\PartFilter;
16
use ZBateson\MailMimeParser\Message\PartFilterFactory;
17
use ZBateson\MailMimeParser\Stream\StreamFactory;
18
19
/**
20
 * A parsed mime message with optional mime parts depending on its type.
21
 * 
22
 * A mime message may have any number of mime parts, and each part may have any
23
 * number of sub-parts, etc...
24
 *
25
 * @author Zaahid Bateson
26
 */
27
class Message extends MimePart
28
{
29
    /**
30
     * @var MessageHelperService helper class with various message manipulation
31
     *      routines.
32
     */
33
    protected $messageHelperService;
34
35
    /**
36
     * @param PartStreamFilterManager $partStreamFilterManager
37
     * @param StreamFactory $streamFactory
38
     * @param PartFilterFactory $partFilterFactory
39
     * @param HeaderFactory $headerFactory
40
     * @param PartBuilder $partBuilder
41
     * @param MessageHelperService $messageHelperService
42
     * @param StreamInterface $stream
43
     * @param StreamInterface $contentStream
44
     */
45 10
    public function __construct(
46
        PartStreamFilterManager $partStreamFilterManager,
47
        StreamFactory $streamFactory,
48
        PartFilterFactory $partFilterFactory,
49
        HeaderFactory $headerFactory,
50
        PartBuilder $partBuilder,
51
        MessageHelperService $messageHelperService,
52
        StreamInterface $stream = null,
53
        StreamInterface $contentStream = null
54
    ) {
55 10
        parent::__construct(
56 10
            $partStreamFilterManager,
57 10
            $streamFactory,
58 10
            $partFilterFactory,
59 10
            $headerFactory,
60 10
            $partBuilder,
61 10
            $stream,
62 10
            $contentStream
63
        );
64 10
        $this->messageHelperService = $messageHelperService;
65 10
    }
66
67
    /**
68
     * Convenience method to parse a handle or string into a Message without
69
     * requiring including MailMimeParser, instantiating it, and calling parse.
70
     * 
71
     * @param resource|string $handleOrString the resource handle to the input
72
     *        stream of the mime message, or a string containing a mime message
73
     */
74
    public static function from($handleOrString)
75
    {
76
        $mmp = new MailMimeParser();
77
        return $mmp->parse($handleOrString);
78
    }
79
80
    /**
81
     * Returns the text/plain part at the given index (or null if not found.)
82
     * 
83
     * @param int $index
84
     * @return \ZBateson\MailMimeParser\Message\Part\MimePart
85
     */
86 1
    public function getTextPart($index = 0)
87
    {
88 1
        return $this->getPart(
89 1
            $index,
90 1
            $this->partFilterFactory->newFilterFromInlineContentType('text/plain')
91
        );
92
    }
93
    
94
    /**
95
     * Returns the number of text/plain parts in this message.
96
     * 
97
     * @return int
98
     */
99 1
    public function getTextPartCount()
100
    {
101 1
        return $this->getPartCount(
102 1
            $this->partFilterFactory->newFilterFromInlineContentType('text/plain')
103
        );
104
    }
105
    
106
    /**
107
     * Returns the text/html part at the given index (or null if not found.)
108
     * 
109
     * @param $index
110
     * @return \ZBateson\MailMimeParser\Message\Part\MimePart
111
     */
112 1
    public function getHtmlPart($index = 0)
113
    {
114 1
        return $this->getPart(
115 1
            $index,
116 1
            $this->partFilterFactory->newFilterFromInlineContentType('text/html')
117
        );
118
    }
119
    
120
    /**
121
     * Returns the number of text/html parts in this message.
122
     * 
123
     * @return int
124
     */
125 1
    public function getHtmlPartCount()
126
    {
127 1
        return $this->getPartCount(
128 1
            $this->partFilterFactory->newFilterFromInlineContentType('text/html')
129
        );
130
    }
131
132
    /**
133
     * Returns the attachment part at the given 0-based index, or null if none
134
     * is set.
135
     * 
136
     * @param int $index
137
     * @return MessagePart
0 ignored issues
show
Bug introduced by
The type ZBateson\MailMimeParser\MessagePart was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
138
     */
139 1
    public function getAttachmentPart($index)
140
    {
141 1
        $attachments = $this->getAllAttachmentParts();
142 1
        if (!isset($attachments[$index])) {
143 1
            return null;
144
        }
145 1
        return $attachments[$index];
146
    }
147
148
    /**
149
     * Returns all attachment parts.
150
     * 
151
     * "Attachments" are any non-multipart, non-signature and any text or html
152
     * html part witha Content-Disposition set to  'attachment'.
153
     * 
154
     * @return MessagePart[]
155
     */
156 1
    public function getAllAttachmentParts()
157
    {
158 1
        $parts = $this->getAllParts(
159 1
            $this->partFilterFactory->newFilterFromArray([
160 1
                'multipart' => PartFilter::FILTER_EXCLUDE
161
            ])
162
        );
163 1
        return array_values(array_filter(
164 1
            $parts,
165 1
            function ($part) {
166
                return !(
167 1
                    $part->isTextPart()
168 1
                    && $part->getContentDisposition() === 'inline'
169
                );
170 1
            }
171
        ));
172
    }
173
174
    /**
175
     * Returns the number of attachments available.
176
     * 
177
     * @return int
178
     */
179 1
    public function getAttachmentCount()
180
    {
181 1
        return count($this->getAllAttachmentParts());
182
    }
183
184
    /**
185
     * Returns a resource handle where the 'inline' text/plain content at the
186
     * passed $index can be read or null if unavailable.
187
     * 
188
     * @param int $index
189
     * @param string $charset
190
     * @return resource
191
     */
192 1
    public function getTextStream($index = 0, $charset = MailMimeParser::DEFAULT_CHARSET)
193
    {
194 1
        $textPart = $this->getTextPart($index);
195 1
        if ($textPart !== null) {
196 1
            return $textPart->getContentResourceHandle($charset);
197
        }
198 1
        return null;
199
    }
200
201
    /**
202
     * Returns the content of the inline text/plain part at the given index.
203
     * 
204
     * Reads the entire stream content into a string and returns it.  Returns
205
     * null if the message doesn't have an inline text part.
206
     * 
207
     * @param int $index
208
     * @param string $charset
209
     * @return string
210
     */
211 1
    public function getTextContent($index = 0, $charset = MailMimeParser::DEFAULT_CHARSET)
212
    {
213 1
        $part = $this->getTextPart($index);
214 1
        if ($part !== null) {
215 1
            return $part->getContent($charset);
216
        }
217 1
        return null;
218
    }
219
220
    /**
221
     * Returns a resource handle where the 'inline' text/html content at the
222
     * passed $index can be read or null if unavailable.
223
     * 
224
     * @param int $index
225
     * @param string $charset
226
     * @return resource
227
     */
228 1
    public function getHtmlStream($index = 0, $charset = MailMimeParser::DEFAULT_CHARSET)
229
    {
230 1
        $htmlPart = $this->getHtmlPart($index);
231 1
        if ($htmlPart !== null) {
232 1
            return $htmlPart->getContentResourceHandle($charset);
233
        }
234 1
        return null;
235
    }
236
237
    /**
238
     * Returns the content of the inline text/html part at the given index.
239
     * 
240
     * Reads the entire stream content into a string and returns it.  Returns
241
     * null if the message doesn't have an inline html part.
242
     * 
243
     * @param int $index
244
     * @param string $charset
245
     * @return string
246
     */
247 1
    public function getHtmlContent($index = 0, $charset = MailMimeParser::DEFAULT_CHARSET)
248
    {
249 1
        $part = $this->getHtmlPart($index);
250 1
        if ($part !== null) {
251 1
            return $part->getContent($charset);
252
        }
253 1
        return null;
254
    }
255
256
    /**
257
     * Returns true if either a Content-Type or Mime-Version header are defined
258
     * in this Message.
259
     * 
260
     * @return bool
261
     */
262 3
    public function isMime()
263
    {
264 3
        $contentType = $this->getHeaderValue('Content-Type');
265 3
        $mimeVersion = $this->getHeaderValue('Mime-Version');
266 3
        return ($contentType !== null || $mimeVersion !== null);
267
    }
268
269
    /**
270
     * Sets the text/plain part of the message to the passed $stringOrHandle,
271
     * either creating a new part if one doesn't exist for text/plain, or
272
     * assigning the value of $stringOrHandle to an existing text/plain part.
273
     *
274
     * The optional $charset parameter is the charset for saving to.
275
     * $stringOrHandle is expected to be in UTF-8 regardless of the target
276
     * charset.
277
     *
278
     * @param string|resource $stringOrHandle
279
     * @param string $charset
280
     */
281
    public function setTextPart($stringOrHandle, $charset = 'UTF-8')
282
    {
283
        $this->messageHelperService
284
            ->getMultipartHelper()
285
            ->setContentPartForMimeType(
286
                $this, 'text/plain', $stringOrHandle, $charset
287
            );
288
    }
289
290
    /**
291
     * Sets the text/html part of the message to the passed $stringOrHandle,
292
     * either creating a new part if one doesn't exist for text/html, or
293
     * assigning the value of $stringOrHandle to an existing text/html part.
294
     *
295
     * The optional $charset parameter is the charset for saving to.
296
     * $stringOrHandle is expected to be in UTF-8 regardless of the target
297
     * charset.
298
     *
299
     * @param string|resource $stringOrHandle
300
     * @param string $charset
301
     */
302
    public function setHtmlPart($stringOrHandle, $charset = 'UTF-8')
303
    {
304
        $this->messageHelperService
305
            ->getMultipartHelper()
306
            ->setContentPartForMimeType(
307
                $this, 'text/html', $stringOrHandle, $charset
308
            );
309
    }
310
311
    /**
312
     * Removes the text/plain part of the message at the passed index if one
313
     * exists.  Returns true on success.
314
     *
315
     * @return bool true on success
316
     */
317
    public function removeTextPart($index = 0)
318
    {
319
        return $this->messageHelperService
320
            ->getMultipartHelper()
321
            ->removePartByMimeType(
322
                $this, 'text/plain', $index
323
            );
324
    }
325
326
    /**
327
     * Removes all text/plain inline parts in this message, optionally keeping
328
     * other inline parts as attachments on the main message (defaults to
329
     * keeping them).
330
     *
331
     * @param bool $keepOtherPartsAsAttachments
332
     * @return bool true on success
333
     */
334
    public function removeAllTextParts($keepOtherPartsAsAttachments = true)
335
    {
336
        return $this->messageHelperService
337
            ->getMultipartHelper()
338
            ->removeAllContentPartsByMimeType(
339
                $this, 'text/plain', $keepOtherPartsAsAttachments
340
            );
341
    }
342
343
    /**
344
     * Removes the html part of the message if one exists.  Returns true on
345
     * success.
346
     *
347
     * @return bool true on success
348
     */
349
    public function removeHtmlPart($index = 0)
350
    {
351
        return $this->messageHelperService
352
            ->getMultipartHelper()
353
            ->removePartByMimeType(
354
                $this, 'text/html', $index
355
            );
356
    }
357
358
    /**
359
     * Removes all text/html inline parts in this message, optionally keeping
360
     * other inline parts as attachments on the main message (defaults to
361
     * keeping them).
362
     *
363
     * @param bool $keepOtherPartsAsAttachments
364
     * @return bool true on success
365
     */
366
    public function removeAllHtmlParts($keepOtherPartsAsAttachments = true)
367
    {
368
        return $this->messageHelperService
369
            ->getMultipartHelper()
370
            ->removeAllContentPartsByMimeType(
371
                $this, 'text/html', $keepOtherPartsAsAttachments
372
            );
373
    }
374
375
    /**
376
     * Removes the attachment with the given index
377
     *
378
     * @param int $index
379
     */
380
    public function removeAttachmentPart($index)
381
    {
382
        $part = $this->getAttachmentPart($index);
383
        $this->removePart($part);
384
    }
385
386
    /**
387
     * Adds an attachment part for the passed raw data string or handle and
388
     * given parameters.
389
     *
390
     * @param string|handle $stringOrHandle
0 ignored issues
show
Bug introduced by
The type ZBateson\MailMimeParser\handle was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
391
     * @param strubg $mimeType
0 ignored issues
show
Bug introduced by
The type ZBateson\MailMimeParser\strubg was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
392
     * @param string $filename
393
     * @param string $disposition
394
     */
395
    public function addAttachmentPart($stringOrHandle, $mimeType, $filename = null, $disposition = 'attachment')
396
    {
397
        if ($filename === null) {
398
            $filename = 'file' . uniqid();
399
        }
400
        $part = $this->messageHelperService
401
            ->getMultipartHelper()
402
            ->createPartForAttachment($this, $mimeType, $filename, $disposition);
403
        $part->setContent($stringOrHandle);
404
        $this->addChild($part);
405
    }
406
407
    /**
408
     * Adds an attachment part using the passed file.
409
     *
410
     * Essentially creates a file stream and uses it.
411
     *
412
     * @param string $file
413
     * @param string $mimeType
414
     * @param string $filename
415
     * @param string $disposition
416
     */
417
    public function addAttachmentPartFromFile($file, $mimeType, $filename = null, $disposition = 'attachment')
418
    {
419
        $handle = fopen($file, 'r');
420
        if ($filename === null) {
421
            $filename = basename($file);
422
        }
423
        $this->addAttachmentPart($handle, $mimeType, $filename, $disposition);
0 ignored issues
show
Bug introduced by
$handle of type resource|false is incompatible with the type ZBateson\MailMimeParser\handle|string expected by parameter $stringOrHandle of ZBateson\MailMimeParser\...ge::addAttachmentPart(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

423
        $this->addAttachmentPart(/** @scrutinizer ignore-type */ $handle, $mimeType, $filename, $disposition);
Loading history...
Bug introduced by
$mimeType of type string is incompatible with the type ZBateson\MailMimeParser\strubg expected by parameter $mimeType of ZBateson\MailMimeParser\...ge::addAttachmentPart(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

423
        $this->addAttachmentPart($handle, /** @scrutinizer ignore-type */ $mimeType, $filename, $disposition);
Loading history...
424
    }
425
426
    /**
427
     * Returns a string containing the entire body of a signed message for
428
     * verification or calculating a signature.
429
     *
430
     * @return string or null if the message doesn't have any children, or the
431
     *      child returns null for getHandle
432
     */
433 2
    public function getSignedMessageAsString()
434
    {
435 2
        $child = $this->getChild(0);
436 2
        if ($child !== null) {
437 1
            $normalized = preg_replace(
438 1
                '/\r\n|\r|\n/',
439 1
                "\r\n",
440 1
                $child->getStream()->getContents()
441
            );
442 1
            return $normalized;
443
        }
444 1
        return null;
445
    }
446
447
    /**
448
     * Returns the signature part of a multipart/signed message or null.
449
     *
450
     * The signature part is determined to always be the 2nd child of a
451
     * multipart/signed message, the first being the 'body'.
452
     *
453
     * Using the 'protocol' parameter of the Content-Type header is unreliable
454
     * in some instances (for instance a difference of x-pgp-signature versus
455
     * pgp-signature).
456
     *
457
     * @return MimePart
458
     */
459
    public function getSignaturePart()
460
    {
461
        $contentType = $this->getHeaderValue('Content-Type', 'text/plain');
462
        if (strcasecmp($contentType, 'multipart/signed') === 0) {
463
            return $this->getChild(1);
464
        } else {
465
            return null;
466
        }
467
    }
468
469
    /**
470
     * Turns the message into a multipart/signed message, moving the actual
471
     * message into a child part, sets the content-type of the main message to
472
     * multipart/signed and adds an empty signature part as well.
473
     *
474
     * After calling setAsMultipartSigned, call get
475
     *
476
     * @param string $micalg The Message Integrity Check algorithm being used
477
     * @param string $protocol The mime-type of the signature body
478
     */
479
    public function setAsMultipartSigned($micalg, $protocol)
480
    {
481
        $contentType = $this->getHeaderValue('Content-Type', 'text/plain');
482
        if (strcasecmp($contentType, 'multipart/signed') !== 0) {
483
            $this->messageHelperService->getPrivacyHelper()
484
                ->setMessageAsMultipartSigned($this, $micalg, $protocol);
485
        }
486
        $this->messageHelperService->getPrivacyHelper()
487
            ->overwrite8bitContentEncoding($this);
488
        $this->messageHelperService->getPrivacyHelper()
489
            ->ensureHtmlPartFirstForSignedMessage($this);
490
        $this->setSignature('Not set');
491
    }
492
493
    /**
494
     * Sets the signature body of the message to the passed $body for a
495
     * multipart/signed message.
496
     *
497
     * @param string $body
498
     */
499
    public function setSignature($body)
500
    {
501
        $this->messageHelperService->getPrivacyHelper()
502
            ->setSignature($this, $body);
503
    }
504
}
505