[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
1 <?php 2 3 require_once ('attachment.class.php'); 4 5 /** 6 * Fast Mime Mail parser Class using PHP's MailParse Extension 7 * @author [email protected] 8 * @url http://www.fijiwebdesign.com/ 9 * @license http://creativecommons.org/licenses/by-sa/3.0/us/ 10 * @version $Id$ 11 */ 12 class MimeMailParser { 13 14 /** 15 * PHP MimeParser Resource ID 16 */ 17 public $resource; 18 19 /** 20 * A file pointer to email 21 */ 22 public $stream; 23 24 /** 25 * A text of an email 26 */ 27 public $data; 28 29 /** 30 * Stream Resources for Attachments 31 */ 32 public $attachment_streams; 33 34 /** 35 * Inialize some stuff 36 * @return 37 */ 38 public function __construct() { 39 $this->attachment_streams = array(); 40 } 41 42 /** 43 * Free the held resouces 44 * @return void 45 */ 46 public function __destruct() { 47 // clear the email file resource 48 if (is_resource($this->stream)) { 49 fclose($this->stream); 50 } 51 // clear the MailParse resource 52 if (is_resource($this->resource)) { 53 mailparse_msg_free($this->resource); 54 } 55 // remove attachment resources 56 foreach($this->attachment_streams as $stream) { 57 fclose($stream); 58 } 59 } 60 61 /** 62 * Set the file path we use to get the email text 63 * @return Object MimeMailParser Instance 64 * @param $mail_path Object 65 */ 66 public function setPath($path) { 67 // should parse message incrementally from file 68 $this->resource = mailparse_msg_parse_file($path); 69 $this->stream = fopen($path, 'r'); 70 $this->parse(); 71 return $this; 72 } 73 74 /** 75 * Set the Stream resource we use to get the email text 76 * @return Object MimeMailParser Instance 77 * @param $stream Resource 78 */ 79 public function setStream($stream) { 80 81 // streams have to be cached to file first 82 if (get_resource_type($stream) == 'stream') { 83 $tmp_fp = tmpfile(); 84 if ($tmp_fp) { 85 while(!feof($stream)) { 86 fwrite($tmp_fp, fread($stream, 2028)); 87 } 88 fseek($tmp_fp, 0); 89 $this->stream =& $tmp_fp; 90 } else { 91 throw new Exception('Could not create temporary files for attachments. Your tmp directory may be unwritable by PHP.'); 92 return false; 93 } 94 fclose($stream); 95 } else { 96 $this->stream = $stream; 97 } 98 99 $this->resource = mailparse_msg_create(); 100 // parses the message incrementally low memory usage but slower 101 while(!feof($this->stream)) { 102 mailparse_msg_parse($this->resource, fread($this->stream, 2082)); 103 } 104 $this->parse(); 105 return $this; 106 } 107 108 /** 109 * Set the email text 110 * @return Object MimeMailParser Instance 111 * @param $data String 112 */ 113 public function setText($data) { 114 $this->resource = mailparse_msg_create(); 115 // does not parse incrementally, fast memory hog might explode 116 mailparse_msg_parse($this->resource, $data); 117 $this->data = $data; 118 $this->parse(); 119 return $this; 120 } 121 122 /** 123 * Parse the Message into parts 124 * @return void 125 * @private 126 */ 127 private function parse() { 128 $structure = mailparse_msg_get_structure($this->resource); 129 $this->parts = array(); 130 foreach($structure as $part_id) { 131 $part = mailparse_msg_get_part($this->resource, $part_id); 132 $this->parts[$part_id] = mailparse_msg_get_part_data($part); 133 } 134 } 135 136 /** 137 * Retrieve the Email Headers 138 * @return Array 139 */ 140 public function getHeaders() { 141 if (isset($this->parts[1])) { 142 return $this->getPartHeaders($this->parts[1]); 143 } else { 144 throw new Exception('MimeMailParser::setPath() or MimeMailParser::setText() must be called before retrieving email headers.'); 145 } 146 return false; 147 } 148 /** 149 * Retrieve the raw Email Headers 150 * @return string 151 */ 152 public function getHeadersRaw() { 153 if (isset($this->parts[1])) { 154 return $this->getPartHeaderRaw($this->parts[1]); 155 } else { 156 throw new Exception('MimeMailParser::setPath() or MimeMailParser::setText() must be called before retrieving email headers.'); 157 } 158 return false; 159 } 160 161 /** 162 * Retrieve a specific Email Header 163 * @return String 164 * @param $name String Header name 165 */ 166 public function getHeader($name) { 167 if (isset($this->parts[1])) { 168 $headers = $this->getPartHeaders($this->parts[1]); 169 if (isset($headers[$name])) { 170 return $headers[$name]; 171 } 172 } else { 173 throw new Exception('MimeMailParser::setPath() or MimeMailParser::setText() must be called before retrieving email headers.'); 174 } 175 return false; 176 } 177 178 /** 179 * Returns the email message body in the specified format 180 * @return Mixed String Body or False if not found 181 * @param $type Object[optional] 182 */ 183 public function getMessageBody($type = 'text') { 184 185 // NOTE: This function has been modified for Phabricator. The default 186 // implementation returns the last matching part, which throws away text 187 // for many emails. Instead, we concatenate all matching parts. See 188 // issue 22 for discussion: 189 // http://code.google.com/p/php-mime-mail-parser/issues/detail?id=22 190 191 $body = false; 192 $mime_types = array( 193 'text'=> 'text/plain', 194 'html'=> 'text/html' 195 ); 196 if (in_array($type, array_keys($mime_types))) { 197 foreach($this->parts as $part) { 198 $disposition = $this->getPartContentDisposition($part); 199 if ($disposition == 'attachment') { 200 // text/plain parts with "Content-Disposition: attachment" are 201 // attachments, not part of the text body. 202 continue; 203 } 204 if ($this->getPartContentType($part) == $mime_types[$type]) { 205 $headers = $this->getPartHeaders($part); 206 // Concatenate all the matching parts into the body text. For example, 207 // if a user sends a message with some text, then an image, and then 208 // some more text, the text body of the email gets split over several 209 // attachments. 210 $body .= $this->decode( 211 $this->getPartBody($part), 212 array_key_exists('content-transfer-encoding', $headers) 213 ? $headers['content-transfer-encoding'] 214 : ''); 215 } 216 } 217 } else { 218 throw new Exception('Invalid type specified for MimeMailParser::getMessageBody. "type" can either be text or html.'); 219 } 220 return $body; 221 } 222 223 /** 224 * get the headers for the message body part. 225 * @return Array 226 * @param $type Object[optional] 227 */ 228 public function getMessageBodyHeaders($type = 'text') { 229 $headers = false; 230 $mime_types = array( 231 'text'=> 'text/plain', 232 'html'=> 'text/html' 233 ); 234 if (in_array($type, array_keys($mime_types))) { 235 foreach($this->parts as $part) { 236 if ($this->getPartContentType($part) == $mime_types[$type]) { 237 $headers = $this->getPartHeaders($part); 238 } 239 } 240 } else { 241 throw new Exception('Invalid type specified for MimeMailParser::getMessageBody. "type" can either be text or html.'); 242 } 243 return $headers; 244 } 245 246 247 /** 248 * Returns the attachments contents in order of appearance 249 * @return Array 250 * @param $type Object[optional] 251 */ 252 public function getAttachments() { 253 $attachments = array(); 254 $dispositions = array("attachment","inline"); 255 foreach($this->parts as $part) { 256 $disposition = $this->getPartContentDisposition($part); 257 if (in_array($disposition, $dispositions)) { 258 $attachments[] = new MimeMailParser_attachment( 259 $part['disposition-filename'], 260 $this->getPartContentType($part), 261 $this->getAttachmentStream($part), 262 $disposition, 263 $this->getPartHeaders($part) 264 ); 265 } 266 } 267 return $attachments; 268 } 269 270 /** 271 * Return the Headers for a MIME part 272 * @return Array 273 * @param $part Array 274 */ 275 private function getPartHeaders($part) { 276 if (isset($part['headers'])) { 277 return $part['headers']; 278 } 279 return false; 280 } 281 282 /** 283 * Return a Specific Header for a MIME part 284 * @return Array 285 * @param $part Array 286 * @param $header String Header Name 287 */ 288 private function getPartHeader($part, $header) { 289 if (isset($part['headers'][$header])) { 290 return $part['headers'][$header]; 291 } 292 return false; 293 } 294 295 /** 296 * Return the ContentType of the MIME part 297 * @return String 298 * @param $part Array 299 */ 300 private function getPartContentType($part) { 301 if (isset($part['content-type'])) { 302 return $part['content-type']; 303 } 304 return false; 305 } 306 307 /** 308 * Return the Content Disposition 309 * @return String 310 * @param $part Array 311 */ 312 private function getPartContentDisposition($part) { 313 if (isset($part['content-disposition'])) { 314 return $part['content-disposition']; 315 } 316 return false; 317 } 318 319 /** 320 * Retrieve the raw Header of a MIME part 321 * @return String 322 * @param $part Object 323 */ 324 private function getPartHeaderRaw(&$part) { 325 $header = ''; 326 if ($this->stream) { 327 $header = $this->getPartHeaderFromFile($part); 328 } else if ($this->data) { 329 $header = $this->getPartHeaderFromText($part); 330 } else { 331 throw new Exception('MimeMailParser::setPath() or MimeMailParser::setText() must be called before retrieving email parts.'); 332 } 333 return $header; 334 } 335 /** 336 * Retrieve the Body of a MIME part 337 * @return String 338 * @param $part Object 339 */ 340 private function getPartBody(&$part) { 341 $body = ''; 342 if ($this->stream) { 343 $body = $this->getPartBodyFromFile($part); 344 } else if ($this->data) { 345 $body = $this->getPartBodyFromText($part); 346 } else { 347 throw new Exception('MimeMailParser::setPath() or MimeMailParser::setText() must be called before retrieving email parts.'); 348 } 349 return $body; 350 } 351 352 /** 353 * Retrieve the Header from a MIME part from file 354 * @return String Mime Header Part 355 * @param $part Array 356 */ 357 private function getPartHeaderFromFile(&$part) { 358 $start = $part['starting-pos']; 359 $end = $part['starting-pos-body']; 360 fseek($this->stream, $start, SEEK_SET); 361 $header = fread($this->stream, $end-$start); 362 return $header; 363 } 364 /** 365 * Retrieve the Body from a MIME part from file 366 * @return String Mime Body Part 367 * @param $part Array 368 */ 369 private function getPartBodyFromFile(&$part) { 370 $start = $part['starting-pos-body']; 371 $end = $part['ending-pos-body']; 372 fseek($this->stream, $start, SEEK_SET); 373 $body = fread($this->stream, $end-$start); 374 return $body; 375 } 376 377 /** 378 * Retrieve the Header from a MIME part from text 379 * @return String Mime Header Part 380 * @param $part Array 381 */ 382 private function getPartHeaderFromText(&$part) { 383 $start = $part['starting-pos']; 384 $end = $part['starting-pos-body']; 385 $header = substr($this->data, $start, $end-$start); 386 return $header; 387 } 388 /** 389 * Retrieve the Body from a MIME part from text 390 * @return String Mime Body Part 391 * @param $part Array 392 */ 393 private function getPartBodyFromText(&$part) { 394 $start = $part['starting-pos-body']; 395 $end = $part['ending-pos-body']; 396 $body = substr($this->data, $start, $end-$start); 397 return $body; 398 } 399 400 /** 401 * Read the attachment Body and save temporary file resource 402 * @return String Mime Body Part 403 * @param $part Array 404 */ 405 private function getAttachmentStream(&$part) { 406 $temp_fp = tmpfile(); 407 408 array_key_exists('content-transfer-encoding', $part['headers']) ? $encoding = $part['headers']['content-transfer-encoding'] : $encoding = ''; 409 410 if ($temp_fp) { 411 if ($this->stream) { 412 $start = $part['starting-pos-body']; 413 $end = $part['ending-pos-body']; 414 fseek($this->stream, $start, SEEK_SET); 415 $len = $end-$start; 416 $written = 0; 417 $write = 2028; 418 $body = ''; 419 while($written < $len) { 420 if (($written+$write < $len )) { 421 $write = $len - $written; 422 } 423 $part = fread($this->stream, $write); 424 fwrite($temp_fp, $this->decode($part, $encoding)); 425 $written += $write; 426 } 427 } else if ($this->data) { 428 $attachment = $this->decode($this->getPartBodyFromText($part), $encoding); 429 fwrite($temp_fp, $attachment, strlen($attachment)); 430 } 431 fseek($temp_fp, 0, SEEK_SET); 432 } else { 433 throw new Exception('Could not create temporary files for attachments. Your tmp directory may be unwritable by PHP.'); 434 return false; 435 } 436 return $temp_fp; 437 } 438 439 440 /** 441 * Decode the string depending on encoding type. 442 * @return String the decoded string. 443 * @param $encodedString The string in its original encoded state. 444 * @param $encodingType The encoding type from the Content-Transfer-Encoding header of the part. 445 */ 446 private function decode($encodedString, $encodingType) { 447 if (strtolower($encodingType) == 'base64') { 448 return base64_decode($encodedString); 449 } else if (strtolower($encodingType) == 'quoted-printable') { 450 return quoted_printable_decode($encodedString); 451 } else { 452 return $encodedString; 453 } 454 } 455 456 } 457 458 459 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |