[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/externals/mimemailparser/ -> MimeMailParser.class.php (source)

   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  ?>


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1