Passed
Push — master ( c6b62f...37aad3 )
by Zaahid
03:17
created

Message::getAllAttachmentParts()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 9
dl 0
loc 13
ccs 9
cts 9
cp 1
rs 9.9666
c 0
b 0
f 0
cc 2
nc 1
nop 0
crap 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 GuzzleHttp\Psr7\StreamWrapper;
10
use Psr\Http\Message\StreamInterface;
11
use ZBateson\MailMimeParser\Header\HeaderFactory;
12
use ZBateson\MailMimeParser\Message\Helper\MessageHelperService;
13
use ZBateson\MailMimeParser\Message\Part\MimePart;
14
use ZBateson\MailMimeParser\Message\Part\PartBuilder;
15
use ZBateson\MailMimeParser\Message\Part\PartStreamFilterManager;
16
use ZBateson\MailMimeParser\Message\PartFilter;
17
use ZBateson\MailMimeParser\Message\PartFilterFactory;
18
use ZBateson\MailMimeParser\Stream\StreamFactory;
19
20
/**
21
 * A parsed mime message with optional mime parts depending on its type.
22
 * 
23
 * A mime message may have any number of mime parts, and each part may have any
24
 * number of sub-parts, etc...
25
 *
26
 * @author Zaahid Bateson
27
 */
28
class Message extends MimePart
29
{
30
    /**
31
     * @var MessageHelperService helper class with various message manipulation
32
     *      routines.
33
     */
34
    protected $messageHelperService;
35
36
    /**
37
     * @param PartStreamFilterManager $partStreamFilterManager
38
     * @param StreamFactory $streamFactory
39
     * @param PartFilterFactory $partFilterFactory
40
     * @param HeaderFactory $headerFactory
41
     * @param PartBuilder $partBuilder
42
     * @param MessageHelperService $messageHelperService
43
     * @param StreamInterface $stream
44
     * @param StreamInterface $contentStream
45
     */
46 10
    public function __construct(
47
        PartStreamFilterManager $partStreamFilterManager,
48
        StreamFactory $streamFactory,
49
        PartFilterFactory $partFilterFactory,
50
        HeaderFactory $headerFactory,
51
        PartBuilder $partBuilder,
52
        MessageHelperService $messageHelperService,
53
        StreamInterface $stream = null,
54
        StreamInterface $contentStream = null
55
    ) {
56 10
        parent::__construct(
57 10
            $partStreamFilterManager,
58 10
            $streamFactory,
59 10
            $partFilterFactory,
60 10
            $headerFactory,
61 10
            $partBuilder,
62 10
            $stream,
63 10
            $contentStream
64
        );
65 10
        $this->messageHelperService = $messageHelperService;
66 10
    }
67
68
    /**
69
     * Convenience method to parse a handle or string into a Message without
70
     * requiring including MailMimeParser, instantiating it, and calling parse.
71
     * 
72
     * @param resource|string $handleOrString the resource handle to the input
73
     *        stream of the mime message, or a string containing a mime message
74
     */
75
    public static function from($handleOrString)
76
    {
77
        $mmp = new MailMimeParser();
78
        return $mmp->parse($handleOrString);
79
    }
80
81
    /**
82
     * Returns the text/plain part at the given index (or null if not found.)
83
     * 
84
     * @param int $index
85
     * @return \ZBateson\MailMimeParser\Message\Part\MimePart
86
     */
87 1
    public function getTextPart($index = 0)
88
    {
89 1
        return $this->getPart(
90 1
            $index,
91 1
            $this->partFilterFactory->newFilterFromInlineContentType('text/plain')
92
        );
93
    }
94
    
95
    /**
96
     * Returns the number of text/plain parts in this message.
97
     * 
98
     * @return int
99
     */
100 1
    public function getTextPartCount()
101
    {
102 1
        return $this->getPartCount(
103 1
            $this->partFilterFactory->newFilterFromInlineContentType('text/plain')
104
        );
105
    }
106
    
107
    /**
108
     * Returns the text/html part at the given index (or null if not found.)
109
     * 
110
     * @param $index
111
     * @return \ZBateson\MailMimeParser\Message\Part\MimePart
112
     */
113 1
    public function getHtmlPart($index = 0)
114
    {
115 1
        return $this->getPart(
116 1
            $index,
117 1
            $this->partFilterFactory->newFilterFromInlineContentType('text/html')
118
        );
119
    }
120
    
121
    /**
122
     * Returns the number of text/html parts in this message.
123
     * 
124
     * @return int
125
     */
126 1
    public function getHtmlPartCount()
127
    {
128 1
        return $this->getPartCount(
129 1
            $this->partFilterFactory->newFilterFromInlineContentType('text/html')
130
        );
131
    }
132
133
    /**
134
     * Returns the attachment part at the given 0-based index, or null if none
135
     * is set.
136
     * 
137
     * @param int $index
138
     * @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...
139
     */
140 1
    public function getAttachmentPart($index)
141
    {
142 1
        $attachments = $this->getAllAttachmentParts();
143 1
        if (!isset($attachments[$index])) {
144 1
            return null;
145
        }
146 1
        return $attachments[$index];
147
    }
148
149
    /**
150
     * Returns all attachment parts.
151
     * 
152
     * "Attachments" are any non-multipart, non-signature and any text or html
153
     * html part witha Content-Disposition set to  'attachment'.
154
     * 
155
     * @return MessagePart[]
156
     */
157 1
    public function getAllAttachmentParts()
158
    {
159 1
        $parts = $this->getAllParts(
160 1
            $this->partFilterFactory->newFilterFromArray([
161 1
                'multipart' => PartFilter::FILTER_EXCLUDE
162
            ])
163
        );
164 1
        return array_values(array_filter(
165 1
            $parts,
166 1
            function ($part) {
167
                return !(
168 1
                    $part->isTextPart()
169 1
                    && $part->getContentDisposition() === 'inline'
170
                );
171 1
            }
172
        ));
173
    }
174
175
    /**
176
     * Returns the number of attachments available.
177
     * 
178
     * @return int
179
     */
180 1
    public function getAttachmentCount()
181
    {
182 1
        return count($this->getAllAttachmentParts());
183
    }
184
185
    /**
186
     * Returns a Psr7 Stream for the 'inline' text/plain content at the passed
187
     * $index, or null if unavailable.
188
     * 
189
     * @param int $index
190
     * @param string $charset
191
     * @return StreamInterface
192
     */
193 1
    public function getTextStream($index = 0, $charset = MailMimeParser::DEFAULT_CHARSET)
194
    {
195 1
        $textPart = $this->getTextPart($index);
196 1
        if ($textPart !== null) {
197 1
            return $textPart->getContentStream($charset);
198
        }
199 1
        return null;
200
    }
201
202
    /**
203
     * Returns a resource handle for the 'inline' text/plain content at the
204
     * passed $index, or null if unavailable.
205
     *
206
     * @param int $index
207
     * @param string $charset
208
     * @return resource
209
     */
210
    public function getTextResourceHandle($index = 0, $charset = MailMimeParser::DEFAULT_CHARSET)
211
    {
212
        $stream = $this->getTextStream($index, $charset);
213
        if ($stream !== null) {
214
            return StreamWrapper::getResource($stream);
215
        }
216
        return null;
217
    }
218
219
    /**
220
     * Returns the content of the inline text/plain part at the given index.
221
     * 
222
     * Reads the entire stream content into a string and returns it.  Returns
223
     * null if the message doesn't have an inline text part.
224
     * 
225
     * @param int $index
226
     * @param string $charset
227
     * @return string
228
     */
229 1
    public function getTextContent($index = 0, $charset = MailMimeParser::DEFAULT_CHARSET)
230
    {
231 1
        $part = $this->getTextPart($index);
232 1
        if ($part !== null) {
233 1
            return $part->getContent($charset);
234
        }
235 1
        return null;
236
    }
237
238
    /**
239
     * Returns a Psr7 Stream for the 'inline' text/html content at the passed
240
     * $index, or null if unavailable.
241
     * 
242
     * @param int $index
243
     * @param string $charset
244
     * @return resource
245
     */
246 1
    public function getHtmlStream($index = 0, $charset = MailMimeParser::DEFAULT_CHARSET)
247
    {
248 1
        $htmlPart = $this->getHtmlPart($index);
249 1
        if ($htmlPart !== null) {
250 1
            return $htmlPart->getContentStream($charset);
251
        }
252 1
        return null;
253
    }
254
255
    /**
256
     * Returns a resource handle for the 'inline' text/html content at the
257
     * passed $index, or null if unavailable.
258
     *
259
     * @param int $index
260
     * @param string $charset
261
     * @return resource
262
     */
263
    public function getHtmlResourceHandle($index = 0, $charset = MailMimeParser::DEFAULT_CHARSET)
264
    {
265
        $stream = $this->getHtmlStream($index, $charset);
266
        if ($stream !== null) {
0 ignored issues
show
introduced by
The condition $stream !== null is always true.
Loading history...
267
            return StreamWrapper::getResource($stream);
0 ignored issues
show
Bug introduced by
$stream of type resource is incompatible with the type Psr\Http\Message\StreamInterface expected by parameter $stream of GuzzleHttp\Psr7\StreamWrapper::getResource(). ( Ignorable by Annotation )

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

267
            return StreamWrapper::getResource(/** @scrutinizer ignore-type */ $stream);
Loading history...
268
        }
269
        return null;
270
    }
271
272
    /**
273
     * Returns the content of the inline text/html part at the given index.
274
     * 
275
     * Reads the entire stream content into a string and returns it.  Returns
276
     * null if the message doesn't have an inline html part.
277
     * 
278
     * @param int $index
279
     * @param string $charset
280
     * @return string
281
     */
282 1
    public function getHtmlContent($index = 0, $charset = MailMimeParser::DEFAULT_CHARSET)
283
    {
284 1
        $part = $this->getHtmlPart($index);
285 1
        if ($part !== null) {
286 1
            return $part->getContent($charset);
287
        }
288 1
        return null;
289
    }
290
291
    /**
292
     * Returns true if either a Content-Type or Mime-Version header are defined
293
     * in this Message.
294
     * 
295
     * @return bool
296
     */
297 3
    public function isMime()
298
    {
299 3
        $contentType = $this->getHeaderValue('Content-Type');
300 3
        $mimeVersion = $this->getHeaderValue('Mime-Version');
301 3
        return ($contentType !== null || $mimeVersion !== null);
302
    }
303
304
    /**
305
     * Sets the text/plain part of the message to the passed $stringOrHandle,
306
     * either creating a new part if one doesn't exist for text/plain, or
307
     * assigning the value of $stringOrHandle to an existing text/plain part.
308
     *
309
     * The optional $charset parameter is the charset for saving to.
310
     * $stringOrHandle is expected to be in UTF-8 regardless of the target
311
     * charset.
312
     *
313
     * @param string|resource|StreamInterface $resource
314
     * @param string $charset
315
     */
316
    public function setTextPart($resource, $charset = 'UTF-8')
317
    {
318
        $this->messageHelperService
319
            ->getMultipartHelper()
320
            ->setContentPartForMimeType(
321
                $this, 'text/plain', $resource, $charset
322
            );
323
    }
324
325
    /**
326
     * Sets the text/html part of the message to the passed $stringOrHandle,
327
     * either creating a new part if one doesn't exist for text/html, or
328
     * assigning the value of $stringOrHandle to an existing text/html part.
329
     *
330
     * The optional $charset parameter is the charset for saving to.
331
     * $stringOrHandle is expected to be in UTF-8 regardless of the target
332
     * charset.
333
     *
334
     * @param string|resource|StreamInterface $resource
335
     * @param string $charset
336
     */
337
    public function setHtmlPart($resource, $charset = 'UTF-8')
338
    {
339
        $this->messageHelperService
340
            ->getMultipartHelper()
341
            ->setContentPartForMimeType(
342
                $this, 'text/html', $resource, $charset
343
            );
344
    }
345
346
    /**
347
     * Removes the text/plain part of the message at the passed index if one
348
     * exists.  Returns true on success.
349
     *
350
     * @return bool true on success
351
     */
352
    public function removeTextPart($index = 0)
353
    {
354
        return $this->messageHelperService
355
            ->getMultipartHelper()
356
            ->removePartByMimeType(
357
                $this, 'text/plain', $index
358
            );
359
    }
360
361
    /**
362
     * Removes all text/plain inline parts in this message, optionally keeping
363
     * other inline parts as attachments on the main message (defaults to
364
     * keeping them).
365
     *
366
     * @param bool $keepOtherPartsAsAttachments
367
     * @return bool true on success
368
     */
369
    public function removeAllTextParts($keepOtherPartsAsAttachments = true)
370
    {
371
        return $this->messageHelperService
372
            ->getMultipartHelper()
373
            ->removeAllContentPartsByMimeType(
374
                $this, 'text/plain', $keepOtherPartsAsAttachments
375
            );
376
    }
377
378
    /**
379
     * Removes the html part of the message if one exists.  Returns true on
380
     * success.
381
     *
382
     * @return bool true on success
383
     */
384
    public function removeHtmlPart($index = 0)
385
    {
386
        return $this->messageHelperService
387
            ->getMultipartHelper()
388
            ->removePartByMimeType(
389
                $this, 'text/html', $index
390
            );
391
    }
392
393
    /**
394
     * Removes all text/html inline parts in this message, optionally keeping
395
     * other inline parts as attachments on the main message (defaults to
396
     * keeping them).
397
     *
398
     * @param bool $keepOtherPartsAsAttachments
399
     * @return bool true on success
400
     */
401
    public function removeAllHtmlParts($keepOtherPartsAsAttachments = true)
402
    {
403
        return $this->messageHelperService
404
            ->getMultipartHelper()
405
            ->removeAllContentPartsByMimeType(
406
                $this, 'text/html', $keepOtherPartsAsAttachments
407
            );
408
    }
409
410
    /**
411
     * Removes the attachment with the given index
412
     *
413
     * @param int $index
414
     */
415
    public function removeAttachmentPart($index)
416
    {
417
        $part = $this->getAttachmentPart($index);
418
        $this->removePart($part);
419
    }
420
421
    /**
422
     * Adds an attachment part for the passed raw data string or handle and
423
     * given parameters.
424
     *
425
     * @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...
426
     * @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...
427
     * @param string $filename
428
     * @param string $disposition
429
     */
430
    public function addAttachmentPart($stringOrHandle, $mimeType, $filename = null, $disposition = 'attachment')
431
    {
432
        if ($filename === null) {
433
            $filename = 'file' . uniqid();
434
        }
435
        $part = $this->messageHelperService
436
            ->getMultipartHelper()
437
            ->createPartForAttachment($this, $mimeType, $filename, $disposition);
438
        $part->setContent($stringOrHandle);
439
        $this->addChild($part);
440
    }
441
442
    /**
443
     * Adds an attachment part using the passed file.
444
     *
445
     * Essentially creates a file stream and uses it.
446
     *
447
     * @param string $file
448
     * @param string $mimeType
449
     * @param string $filename
450
     * @param string $disposition
451
     */
452
    public function addAttachmentPartFromFile($file, $mimeType, $filename = null, $disposition = 'attachment')
453
    {
454
        $handle = fopen($file, 'r');
455
        if ($filename === null) {
456
            $filename = basename($file);
457
        }
458
        $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

458
        $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

458
        $this->addAttachmentPart($handle, /** @scrutinizer ignore-type */ $mimeType, $filename, $disposition);
Loading history...
459
    }
460
461
    /**
462
     * Returns a string containing the entire body of a signed message for
463
     * verification or calculating a signature.
464
     *
465
     * @return string or null if the message doesn't have any children
466
     */
467 2
    public function getSignedMessageAsString()
468
    {
469 2
        $child = $this->getChild(0);
470 2
        if ($child !== null) {
471 1
            $normalized = preg_replace(
472 1
                '/\r\n|\r|\n/',
473 1
                "\r\n",
474 1
                $child->getStream()->getContents()
475
            );
476 1
            return $normalized;
477
        }
478 1
        return null;
479
    }
480
481
    /**
482
     * Returns the signature part of a multipart/signed message or null.
483
     *
484
     * The signature part is determined to always be the 2nd child of a
485
     * multipart/signed message, the first being the 'body'.
486
     *
487
     * Using the 'protocol' parameter of the Content-Type header is unreliable
488
     * in some instances (for instance a difference of x-pgp-signature versus
489
     * pgp-signature).
490
     *
491
     * @return MimePart
492
     */
493
    public function getSignaturePart()
494
    {
495
        $contentType = $this->getHeaderValue('Content-Type', 'text/plain');
496
        if (strcasecmp($contentType, 'multipart/signed') === 0) {
497
            return $this->getChild(1);
498
        } else {
499
            return null;
500
        }
501
    }
502
503
    /**
504
     * Turns the message into a multipart/signed message, moving the actual
505
     * message into a child part, sets the content-type of the main message to
506
     * multipart/signed and adds an empty signature part as well.
507
     *
508
     * After calling setAsMultipartSigned, call get
509
     *
510
     * @param string $micalg The Message Integrity Check algorithm being used
511
     * @param string $protocol The mime-type of the signature body
512
     */
513
    public function setAsMultipartSigned($micalg, $protocol)
514
    {
515
        $contentType = $this->getHeaderValue('Content-Type', 'text/plain');
516
        if (strcasecmp($contentType, 'multipart/signed') !== 0) {
517
            $this->messageHelperService->getPrivacyHelper()
518
                ->setMessageAsMultipartSigned($this, $micalg, $protocol);
519
        }
520
        $this->messageHelperService->getPrivacyHelper()
521
            ->overwrite8bitContentEncoding($this);
522
        $this->messageHelperService->getPrivacyHelper()
523
            ->ensureHtmlPartFirstForSignedMessage($this);
524
        $this->setSignature('Not set');
525
    }
526
527
    /**
528
     * Sets the signature body of the message to the passed $body for a
529
     * multipart/signed message.
530
     *
531
     * @param string $body
532
     */
533
    public function setSignature($body)
534
    {
535
        $this->messageHelperService->getPrivacyHelper()
536
            ->setSignature($this, $body);
537
    }
538
}
539