Completed
Push — master ( aab6ae...03b37f )
by Zaahid
04:44
created

MessageParser::findNextUUEncodedPartPosition()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 14
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 14
rs 9.4285
cc 3
eloc 11
nc 2
nop 1
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
/**
10
 * Parses a mail mime message into its component parts.  To invoke, call
11
 * MailMimeParser::parse.
12
 *
13
 * @author Zaahid Bateson
14
 */
15
class MessageParser
16
{
17
    /**
18
     * @var \ZBateson\MailMimeParser\Message the Message object that the read
19
     * mail mime message will be parsed into
20
     */
21
    protected $message;
22
    
23
    /**
24
     * @var \ZBateson\MailMimeParser\MimePartFactory the MimePartFactory object
25
     * used to create parts.
26
     */
27
    protected $partFactory;
28
    
29
    /**
30
     * @var \ZBateson\MailMimeParser\PartStreamRegistry the PartStreamRegistry 
31
     * object used to register stream parts.
32
     */
33
    protected $partStreamRegistry;
34
    
35
    /**
36
     * Sets up the parser with its dependencies.
37
     * 
38
     * @param \ZBateson\MailMimeParser\Message $m
39
     * @param \ZBateson\MailMimeParser\MimePartFactory $pf
40
     * @param \ZBateson\MailMimeParser\PartStreamRegistry $psr
41
     */
42
    public function __construct(Message $m, MimePartFactory $pf, PartStreamRegistry $psr)
43
    {
44
        $this->message = $m;
45
        $this->partFactory = $pf;
46
        $this->partStreamRegistry = $psr;
47
    }
48
    
49
    /**
50
     * Parses the passed stream handle into the ZBateson\MailMimeParser\Message
51
     * object and returns it.
52
     * 
53
     * @param resource $fhandle the resource handle to the input stream of the
54
     *        mime message
55
     * @return \ZBateson\MailMimeParser\Message
56
     */
57
    public function parse($fhandle)
58
    {
59
        $this->partStreamRegistry->register($this->message->getObjectId(), $fhandle);
60
        $this->read($fhandle, $this->message);
61
        return $this->message;
62
    }
63
    
64
    /**
65
     * Reads header lines up to an empty line, adding them to the passed $part.
66
     * 
67
     * @param resource $handle the resource handle to read from
68
     * @param \ZBateson\MailMimeParser\MimePart $part the current part to add
69
     *        headers to
70
     */
71
    protected function readHeaders($handle, MimePart $part)
72
    {
73
        $header = '';
74
        do {
75
            $line = fgets($handle, 1000);
76
            if ($line[0] !== "\t" && $line[0] !== ' ') {
77
                if (!empty($header) && strpos($header, ':') !== false) {
78
                    $a = explode(':', $header, 2);
79
                    $part->setRawHeader($a[0], trim($a[1]));
80
                }
81
                $header = '';
82
            } else {
83
                $line = ' ' . ltrim($line);
84
            }
85
            $header .= rtrim($line, "\r\n");
86
        } while (!empty($header));
87
    }
88
    
89
    /**
90
     * Reads the content of a mime part up to a boundary, or the entire message
91
     * if no boundary is specified.
92
     * 
93
     * readPartContent may be called to skip to the first boundary to read its
94
     * headers, in which case $skipPart should be true.
95
     * 
96
     * If the end boundary is found, the method returns true.
97
     * 
98
     * @param resource $handle the input stream resource
99
     * @param \ZBateson\MailMimeParser\Message $message the current Message
100
     *        object
101
     * @param \ZBateson\MailMimeParser\MimePart $part the current MimePart
102
     *        object to load the content into.
103
     * @param string $boundary the MIME boundary
104
     * @param boolean $skipPart pass true if the intention is to read up to the
105
     *        beginning MIME boundary's headers
106
     * @return boolean if the end boundary is found
107
     */
108
    protected function readPartContent($handle, Message $message, MimePart $part, $boundary, $skipPart)
109
    {
110
        $start = ftell($handle);
111
        $boundaryLength = 0;
112
        $endBoundaryFound = false;
113
        do {
114
            $line = fgets($handle);
115
            if (!empty($boundary)) {
116
                $boundaryLength = strlen($line);
117
                $test = rtrim($line);
118
                if ($test === "--$boundary") {
119
                    break;
120
                } elseif ($test === "--$boundary--") {
121
                    $endBoundaryFound = true;
122
                    break;
123
                }
124
            }
125
        } while (!feof($handle));
126
        
127
        if (!$skipPart) {
128
            $end = ftell($handle) - $boundaryLength;
129
            $this->partStreamRegistry->attachPartStreamHandle($part, $message, $start, $end);
130
            $message->addPart($part);
131
        }
132
        return $endBoundaryFound;
133
    }
134
    
135
    /**
136
     * Finds the boundaries for the current MimePart, reads its content and
137
     * creates and returns the next part, setting its parent part accordingly.
138
     * 
139
     * @param resource $handle The handle to read from
140
     * @param \ZBateson\MailMimeParser\Message $message The current Message
141
     * @param \ZBateson\MailMimeParser\MimePart $part 
142
     * @return type
143
     */
144
    protected function readMimeMessagePart($handle, Message $message, MimePart $part)
145
    {
146
        $boundary = $part->getHeaderParameter('Content-Type', 'boundary');
147
        $skipPart = true;
148
        $parent = $part;
149
150
        if (empty($boundary) || !preg_match('~multipart/\w+~i', $part->getHeaderValue('Content-Type'))) {
151
            // either there is no boundary (possibly no parent boundary either) and message is read
152
            // till the end, or we're in a boundary already and content should be read till the parent
153
            // boundary is reached
154
            if ($part->getParent() !== null) {
155
                $parent = $part->getParent();
156
                $boundary = $parent->getHeaderParameter('Content-Type', 'boundary');
157
            }
158
            $skipPart = false;
159
        }
160
        // keep reading if an end boundary is found, to find the parent's boundary
161
        while ($this->readPartContent($handle, $message, $part, $boundary, $skipPart) && $parent !== null) {
162
            $parent = $parent->getParent();
163
            if ($parent !== null) {
164
                $boundary = $parent->getHeaderParameter('Content-Type', 'boundary');
165
            }
166
            $skipPart = true;
167
        }
168
        $nextPart = $this->partFactory->newMimePart();
169
        if ($parent === null) {
170
            $parent = $message;
171
        }
172
        $nextPart->setParent($parent);
173
        return $nextPart;
174
    }
175
    
176
    /**
177
     * Extracts the filename and end position of a UUEncoded part.
178
     * 
179
     * The filename is set to the passed $nextFilename parameter.  The end
180
     * position is returned.
181
     * 
182
     * @param resource $handle the current file handle
183
     * @param int &$nextMode is assigned the value of the next file mode or null
184
     *        if not found
185
     * @param string &$nextFilename is assigned the value of the next filename
186
     *        or null if not found
187
     * @param int &$end assigned the offset position within the passed resource
188
     *        $handle of the end of the uuencoded part
189
     */
190
    private function findNextUUEncodedPartPosition($handle)
191
    {
192
        $end = ftell($handle);
193
        do {
194
            $line = trim(fgets($handle));
195
            $matches = null;
196
            if (preg_match('/^begin [0-7]{3} .*$/', $line, $matches)) {
197
                fseek($handle, $end);
198
                break;
199
            }
200
            $end = ftell($handle);
201
        } while (!feof($handle));
202
        return $end;
203
    }
204
    
205
    /**
206
     * Reads one part of a UUEncoded message and adds it to the passed Message
207
     * as a MimePart.
208
     * 
209
     * The method reads up to the first 'begin' part of the message, or to the
210
     * end of the message if no 'begin' exists.
211
     * 
212
     * @param resource $handle
213
     * @param \ZBateson\MailMimeParser\Message $message
214
     * @return string
215
     */
216
    protected function readUUEncodedOrPlainTextPart($handle, Message $message)
217
    {
218
        $start = ftell($handle);
219
        $line = trim(fgets($handle));
220
        $end = $this->findNextUUEncodedPartPosition($handle);
221
        
222
        $part = null;
1 ignored issue
show
Unused Code introduced by
$part is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
223
        if (preg_match('/^begin ([0-7]{3}) (.*)$/', $line, $matches)) {
224
            $mode = $matches[1];
225
            $filename = $matches[2];
226
            $part = $this->partFactory->newUUEncodedPart($mode, $filename);
227
            $this->partStreamRegistry->attachPartStreamHandle($part, $message, $start, $end);
228
        } else {
229
            $part = $this->partFactory->newNonMimePart();
230
            $this->partStreamRegistry->attachPartStreamHandle($part, $message, $start, $end);
231
        }
232
        $message->addPart($part);
233
    }
234
    
235
    /**
236
     * Reads the message from the input stream $handle into $message.
237
     * 
238
     * The method will loop to read headers and find and parse multipart-mime
239
     * message parts and uuencoded attachments (as mime-parts), adding them to
240
     * the passed Message object.
241
     * 
242
     * @param resource $handle
243
     * @param \ZBateson\MailMimeParser\Message $message
244
     */
245
    protected function read($handle, Message $message)
246
    {
247
        $part = $message;
248
        $this->readHeaders($handle, $message);
249
        $contentType = $part->getHeaderValue('Content-Type');
250
        $mimeVersion = $part->getHeaderValue('Mime-Version');
251
        do {
252
            if ($part === $message && $contentType === null && $mimeVersion === null) {
253
                $this->readUUEncodedOrPlainTextPart($handle, $message);
254
            } else {
255
                $part = $this->readMimeMessagePart($handle, $message, $part);
256
                $this->readHeaders($handle, $part);
257
            }
258
        } while (!feof($handle));
259
    }
260
}
261