1 | <?php |
||
20 | class MimePartWriter |
||
21 | { |
||
22 | /** |
||
23 | * @var array default params for stream filters in |
||
24 | * setTransferEncodingFilterOnStream |
||
25 | */ |
||
26 | private static $defaultStreamFilterParams = [ |
||
27 | 'line-length' => 76, |
||
28 | 'line-break-chars' => "\r\n", |
||
29 | ]; |
||
30 | |||
31 | /** |
||
32 | * @var array map of transfer-encoding types to registered stream filter |
||
33 | * names used in setTransferEncodingFilterOnStream |
||
34 | */ |
||
35 | private static $typeToEncodingMap = [ |
||
36 | 'quoted-printable' => 'mmp-convert.quoted-printable-encode', |
||
37 | 'base64' => 'mmp-convert.base64-encode', |
||
38 | 'x-uuencode' => 'mailmimeparser-uuencode', |
||
39 | ]; |
||
40 | |||
41 | /** |
||
42 | * Returns the singleton instance for the class, instantiating it if not |
||
43 | * already created. |
||
44 | */ |
||
45 | public static function getInstance() |
||
46 | { |
||
47 | static $instances = []; |
||
48 | $class = get_called_class(); |
||
49 | if (!isset($instances[$class])) { |
||
50 | $instances[$class] = new static(); |
||
51 | } |
||
52 | return $instances[$class]; |
||
53 | } |
||
54 | |||
55 | /** |
||
56 | * Writes out the headers of the passed MimePart and follows them with an |
||
57 | * empty line. |
||
58 | * |
||
59 | * @param MimePart $part |
||
60 | * @param resource $handle |
||
61 | */ |
||
62 | public function writePartHeadersTo(MimePart $part, $handle) |
||
70 | |||
71 | /** |
||
72 | * Sets up a mailmimeparser-encode stream filter on the content resource |
||
73 | * handle of the passed MimePart if applicable and returns a reference to |
||
74 | * the filter. |
||
75 | * |
||
76 | * @param MimePart $part |
||
77 | * @return resource a reference to the appended stream filter or null |
||
78 | */ |
||
79 | private function setCharsetStreamFilterOnPartStream(MimePart $part) |
||
99 | |||
100 | /** |
||
101 | * Appends a stream filter on the passed MimePart's content resource handle |
||
102 | * based on the type of encoding for the passed part. |
||
103 | * |
||
104 | * @param MimePart $part |
||
105 | * @param resource $handle |
||
106 | * @param StreamLeftover $leftovers |
||
107 | * @return resource the stream filter |
||
108 | */ |
||
109 | private function setTransferEncodingFilterOnStream(MimePart $part, $handle, StreamLeftover $leftovers) |
||
140 | |||
141 | /** |
||
142 | * Trims out any starting and ending CRLF characters in the stream. |
||
143 | * |
||
144 | * @param string $read the read string, and where the result will be written |
||
145 | * to |
||
146 | * @param bool $first set to true if this is the first set of read |
||
147 | * characters from the stream (ltrims CRLF) |
||
148 | * @param string $lastChars contains any CRLF characters from the last $read |
||
149 | * line if it ended with a CRLF (because they're trimmed from the |
||
150 | * end, and get prepended to $read). |
||
151 | */ |
||
152 | private function trimTextBeforeCopying(&$read, &$first, &$lastChars) |
||
153 | { |
||
154 | if ($first) { |
||
155 | $first = false; |
||
156 | $read = ltrim($read, "\r\n"); |
||
157 | } |
||
158 | $read = $lastChars . $read; |
||
159 | $lastChars = ''; |
||
160 | $matches = null; |
||
161 | if (preg_match('/[\r\n]+$/', $read, $matches)) { |
||
162 | $lastChars = $matches[0]; |
||
163 | $read = rtrim($read, "\r\n"); |
||
164 | } |
||
165 | } |
||
166 | |||
167 | /** |
||
168 | * Copies the content of the $fromHandle stream into the $toHandle stream, |
||
169 | * maintaining the current read position in $fromHandle. The passed |
||
170 | * MimePart is where $fromHandle originated after setting up filters on |
||
171 | * $fromHandle. |
||
172 | * |
||
173 | * @param MimePart $part |
||
174 | * @param resource $fromHandle |
||
175 | * @param resource $toHandle |
||
176 | */ |
||
177 | private function copyContentStream(MimePart $part, $fromHandle, $toHandle) |
||
178 | { |
||
179 | $pos = ftell($fromHandle); |
||
180 | rewind($fromHandle); |
||
181 | // changed from stream_copy_to_stream because hhvm seems to stop before |
||
182 | // end of file for some reason |
||
183 | $lastChars = ''; |
||
184 | $first = true; |
||
185 | while (!feof($fromHandle)) { |
||
186 | $read = fread($fromHandle, 1024); |
||
187 | if (strcasecmp($part->getHeaderValue('Content-Encoding'), '8bit') !== 0) { |
||
188 | $read = preg_replace('/\r\n|\r|\n/', "\r\n", $read); |
||
189 | } |
||
190 | if ($part->isTextPart()) { |
||
191 | $this->trimTextBeforeCopying($read, $first, $lastChars); |
||
192 | } |
||
193 | fwrite($toHandle, $read); |
||
194 | } |
||
195 | fseek($fromHandle, $pos); |
||
196 | } |
||
197 | |||
198 | /** |
||
199 | * Writes out the content portion of the mime part based on the headers that |
||
200 | * are set on the part, taking care of character/content-transfer encoding. |
||
201 | * |
||
202 | * @param MimePart $part |
||
203 | * @param resource $handle |
||
204 | */ |
||
205 | public function writePartContentTo(MimePart $part, $handle) |
||
229 | |||
230 | /** |
||
231 | * Writes out the MimePart to the passed resource. |
||
232 | * |
||
233 | * Takes care of character and content transfer encoding on the output based |
||
234 | * on what headers are set. |
||
235 | * |
||
236 | * @param MimePart $part |
||
237 | * @param resource $handle |
||
238 | */ |
||
239 | public function writePartTo(MimePart $part, $handle) |
||
244 | } |
||
245 |