Test Failed
Branch 1.0.0 (84f469)
by Zaahid
05:36
created

Message::findOtherContentPartFor()   B

Complexity

Conditions 6
Paths 3

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 6.1666

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 14
ccs 10
cts 12
cp 0.8333
rs 8.8571
cc 6
eloc 9
nc 3
nop 1
crap 6.1666
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
    public function getTextPart($index = 0)
42
    {
43
        return $this->getPart(
44
            $index,
45
            $this->partFilterFactory->newFilterFromInlineContentType('text/plain')
46
        );
47
    }
48
    
49
    /**
50
     * Returns the number of text/plain parts in this message.
51
     * 
52
     * @return int
53
     */
54
    public function getTextPartCount()
55
    {
56
        return $this->getPartCount(
57
            $this->partFilterFactory->newFilterFromInlineContentType('text/plain')
58
        );
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
    public function getHtmlPart($index = 0)
68
    {
69
        return $this->getPart(
70
            $index,
71
            $this->partFilterFactory->newFilterFromInlineContentType('text/html')
72
        );
73
    }
74
    
75
    /**
76
     * Returns the number of text/html parts in this message.
77
     * 
78
     * @return int
79
     */
80
    public function getHtmlPartCount()
81
    {
82
        return $this->getPartCount(
83
            $this->partFilterFactory->newFilterFromInlineContentType('text/html')
84
        );
85
    }
86
    
87
    /**
88
     * Returns the content MimePart, which could be a text/plain part,
89
     * text/html part, multipart/alternative part, or null if none is set.
90
     * 
91
     * This function is deprecated in favour of getTextPart/getHtmlPart and 
92
     * getPartByMimeType.
93
     * 
94
     * @deprecated since version 0.4.2
95
     * @return \ZBateson\MailMimeParser\Message\Part\MimePart
96
     */
97
    public function getContentPart()
98
    {
99
        $alternative = $this->getPartByMimeType('multipart/alternative');
100
        if ($alternative !== null) {
101
            return $alternative;
102
        }
103
        $text = $this->getTextPart();
104
        return ($text !== null) ? $text : $this->getHtmlPart();
105
    }
106
    
107
    /**
108
     * Returns a string containing the original message's signed part, useful
109
     * for verifying the email.
110
     * 
111
     * If the signed part of the message ends in a final empty line, the line is
112
     * removed as it's considered part of the signature's mime boundary.  From
113
     * RFC-3156:
114
     * 
115
     * Note: The accepted OpenPGP convention is for signed data to end
116
     * with a <CR><LF> sequence.  Note that the <CR><LF> sequence
117
     * immediately preceding a MIME boundary delimiter line is considered
118
     * to be part of the delimiter in [3], 5.1.  Thus, it is not part of
119
     * the signed data preceding the delimiter line.  An implementation
120
     * which elects to adhere to the OpenPGP convention has to make sure
121
     * it inserts a <CR><LF> pair on the last line of the data to be
122
     * signed and transmitted (signed message and transmitted message
123
     * MUST be identical).
124
     * 
125
     * The additional line should be inserted by the signer -- for verification
126
     * purposes if it's missing, it would seem the content part would've been
127
     * signed without a last <CR><LF>.
128
     * 
129
     * @return string or null if the message doesn't have any children, or the
130
     *      child returns null for getOriginalStreamHandle
131
     */
132
    public function getMessageStringForSignatureVerification()
133
    {
134
        $child = $this->getChild(0);
135
        if ($child !== null && $child->getHandle() !== null) {
136
            $normalized = preg_replace(
137
                '/\r\n|\r|\n/',
138
                "\r\n",
139
                stream_get_contents($child->getHandle())
140
            );
141
            $len = strlen($normalized);
142
            if ($len > 0 && strrpos($normalized, "\r\n") == $len - 2) {
143
                return substr($normalized, 0, -2);
144
            }
145
            return $normalized;
146
        }
147
        return null;
148
    }
149
150
    /**
151
     * Returns the attachment part at the given 0-based index, or null if none
152
     * is set.
153
     * 
154
     * @param int $index
155
     * @return \ZBateson\MailMimeParser\Message\Part\MimePart
156
     */
157
    public function getAttachmentPart($index)
158
    {
159
        $attachments = $this->getAllAttachmentParts();
160
        if (!isset($attachments[$index])) {
161
            return null;
162
        }
163
        return $attachments[$index];
164
    }
165
166
    /**
167
     * Returns all attachment parts.
168
     * 
169
     * Attachments are any non-multipart, non-signature and non inline text or
170
     * html part (a text or html part with a Content-Disposition set to 
171
     * 'attachment' is considered an attachment).
172
     * 
173
     * @return \ZBateson\MailMimeParser\Message\Part\MimePart[]
174
     */
175
    public function getAllAttachmentParts()
176
    {
177
        $parts = $this->getAllParts(
178
            $this->partFilterFactory->newFilterFromArray([
179
                'multipart' => PartFilter::FILTER_EXCLUDE
180
            ])
181
        );
182
        return array_values(array_filter(
183
            $parts,
184
            function ($part) {
185
                return !(
186
                    $part->isTextPart()
187
                    && $part->getContentDisposition() === 'inline'
188
                );
189
            }
190
        ));
191
    }
192
193
    /**
194
     * Returns the number of attachments available.
195
     * 
196
     * @return int
197
     */
198
    public function getAttachmentCount()
199
    {
200
        return count($this->getAllAttachmentParts());
201
    }
202
203
    /**
204
     * Returns a resource handle where the 'inline' text/plain content at the
205
     * passed $index can be read or null if unavailable.
206
     * 
207
     * @param int $index
208
     * @return resource
209
     */
210
    public function getTextStream($index = 0)
211
    {
212
        $textPart = $this->getTextPart($index);
213
        if ($textPart !== null) {
214
            return $textPart->getContentResourceHandle();
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
     * @return string
227
     */
228
    public function getTextContent($index = 0)
229
    {
230
        $part = $this->getTextPart($index);
231
        if ($part !== null) {
232
            return $part->getContent();
233
        }
234
        return null;
235
    }
236
237
    /**
238
     * Returns a resource handle where the 'inline' text/html content at the
239
     * passed $index can be read or null if unavailable.
240
     * 
241
     * @return resource
242
     */
243
    public function getHtmlStream($index = 0)
244
    {
245
        $htmlPart = $this->getHtmlPart($index);
246
        if ($htmlPart !== null) {
247
            return $htmlPart->getContentResourceHandle();
248
        }
249
        return null;
250
    }
251
252
    /**
253
     * Returns the content of the inline text/html part at the given index.
254
     * 
255
     * Reads the entire stream content into a string and returns it.  Returns
256
     * null if the message doesn't have an inline html part.
257
     * 
258
     * @param int $index
259
     * @return string
260
     */
261
    public function getHtmlContent($index = 0)
262
    {
263
        $part = $this->getHtmlPart($index);
264
        if ($part !== null) {
265
            return $part->getContent();
266
        }
267
        return null;
268
    }
269
270
    /**
271
     * Returns true if either a Content-Type or Mime-Version header are defined
272
     * in this Message.
273
     * 
274
     * @return bool
275
     */
276
    public function isMime()
277
    {
278
        $contentType = $this->getHeaderValue('Content-Type');
279
        $mimeVersion = $this->getHeaderValue('Mime-Version');
280
        return ($contentType !== null || $mimeVersion !== null);
281
    }
282
283
    /**
284
     * Saves the message as a MIME message to the passed resource handle.
285
     * 
286
     * @param resource $handle
287
     */
288
    public function save($handle)
289
    {
290
        if (is_resource($this->handle)) {
291
            rewind($this->handle);
292
            stream_copy_to_stream($this->handle, $handle);
293
        }
294
    }
295
296
    /**
297
     * Shortcut to call Message::save with a php://temp stream and return the
298
     * written email message as a string.
299
     * 
300
     * @return string
301
     */
302
    public function __toString()
303
    {
304
        $handle = fopen('php://temp', 'r+');
305
        $this->save($handle);
306
        rewind($handle);
307
        $str = stream_get_contents($handle);
308
        fclose($handle);
309
        return $str;
310
    }
311
}
312