From 09dfe32eb6b538225686fd6ed0220240010bc574 Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Mon, 1 Aug 2011 01:22:36 -0400 Subject: initial commit. Partway through a rewrite. I have some old files I didn't want to entirely delete. --- src/ext/MimeMailParser.class.php | 447 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 447 insertions(+) create mode 100644 src/ext/MimeMailParser.class.php (limited to 'src/ext/MimeMailParser.class.php') diff --git a/src/ext/MimeMailParser.class.php b/src/ext/MimeMailParser.class.php new file mode 100644 index 0000000..0080199 --- /dev/null +++ b/src/ext/MimeMailParser.class.php @@ -0,0 +1,447 @@ +attachment_streams = array(); + } + + /** + * Free the held resouces + * @return void + */ + public function __destruct() { + // clear the email file resource + if (is_resource($this->stream)) { + fclose($this->stream); + } + // clear the MailParse resource + if (is_resource($this->resource)) { + mailparse_msg_free($this->resource); + } + // remove attachment resources + foreach($this->attachment_streams as $stream) { + fclose($stream); + } + } + + /** + * Set the file path we use to get the email text + * @return Object MimeMailParser Instance + * @param $mail_path Object + */ + public function setPath($path) { + // should parse message incrementally from file + $this->resource = mailparse_msg_parse_file($path); + $this->stream = fopen($path, 'r'); + $this->parse(); + return $this; + } + + /** + * Set the Stream resource we use to get the email text + * @return Object MimeMailParser Instance + * @param $stream Resource + */ + public function setStream($stream) { + + // streams have to be cached to file first + if (get_resource_type($stream) == 'stream') { + $tmp_fp = tmpfile(); + if ($tmp_fp) { + while(!feof($stream)) { + fwrite($tmp_fp, fread($stream, 2028)); + } + fseek($tmp_fp, 0); + $this->stream =& $tmp_fp; + } else { + throw new Exception('Could not create temporary files for attachments. Your tmp directory may be unwritable by PHP.'); + return false; + } + fclose($stream); + } else { + $this->stream = $stream; + } + + $this->resource = mailparse_msg_create(); + // parses the message incrementally low memory usage but slower + while(!feof($this->stream)) { + mailparse_msg_parse($this->resource, fread($this->stream, 2082)); + } + $this->parse(); + return $this; + } + + /** + * Set the email text + * @return Object MimeMailParser Instance + * @param $data String + */ + public function setText($data) { + $this->resource = mailparse_msg_create(); + // does not parse incrementally, fast memory hog might explode + mailparse_msg_parse($this->resource, $data); + $this->data = $data; + $this->parse(); + return $this; + } + + /** + * Parse the Message into parts + * @return void + * @private + */ + private function parse() { + $structure = mailparse_msg_get_structure($this->resource); + $this->parts = array(); + foreach($structure as $part_id) { + $part = mailparse_msg_get_part($this->resource, $part_id); + $this->parts[$part_id] = mailparse_msg_get_part_data($part); + } + } + + /** + * Retrieve the Email Headers + * @return Array + */ + public function getHeaders() { + if (isset($this->parts[1])) { + return $this->getPartHeaders($this->parts[1]); + } else { + throw new Exception('MimeMailParser::setPath() or MimeMailParser::setText() must be called before retrieving email headers.'); + } + return false; + } + /** + * Retrieve the raw Email Headers + * @return string + */ + public function getHeadersRaw() { + if (isset($this->parts[1])) { + return $this->getPartHeaderRaw($this->parts[1]); + } else { + throw new Exception('MimeMailParser::setPath() or MimeMailParser::setText() must be called before retrieving email headers.'); + } + return false; + } + + /** + * Retrieve a specific Email Header + * @return String + * @param $name String Header name + */ + public function getHeader($name) { + if (isset($this->parts[1])) { + $headers = $this->getPartHeaders($this->parts[1]); + if (isset($headers[$name])) { + return $headers[$name]; + } + } else { + throw new Exception('MimeMailParser::setPath() or MimeMailParser::setText() must be called before retrieving email headers.'); + } + return false; + } + + /** + * Returns the part for the message body in the specified format + * @return Part or False if not found + * @param $type String[optional] + */ + public function getMessageBodyPart($type = 'text') { + $mime_types = array( + 'text'=> 'text/plain', + 'html'=> 'text/html' + ); + $attachment_dispositions = array("attachment","inline"); + if (in_array($type, array_keys($mime_types))) { + foreach($this->parts as $part) { + $disposition = $this->getPartContentDisposition($part); + $mime_type = $this->getPartContentType($part); + if ( (!in_array($disposition, $attachment_dispositions)) && + ($mime_type == $mime_types[$type]) ) { + return $part; + } + } + } else { + throw new Exception('Invalid type specified for MimeMailParser::getMessageBodyPart. "type" can either be text or html.'); + } + return false; + } + + /** + * Returns the email message body in the specified format + * @return Mixed String Body or False if not found + * @param $type Object[optional] + */ + public function getMessageBody($type = 'text') { + $body = false; + $part = $this->getMessageBodyPart($type); + if ($part!==false) { + $headers = $this->getPartHeaders($part); + $body = $this->decode($this->getPartBody($part), array_key_exists('content-transfer-encoding', $headers) ? $headers['content-transfer-encoding'] : ''); + } + return $body; + } + + /** + * get the headers for the message body part. + * @return Array + * @param $type Object[optional] + */ + public function getMessageBodyHeaders($type = 'text') { + $headers = false; + $part = $this->getMessageBodyPart($type); + if ($part!==false) { + $headers = $this->getPartHeaders($part); + } + return $headers; + } + + + /** + * Returns the attachments contents in order of appearance + * @return Array + * @param $type Object[optional] + */ + public function getAttachments() { + $attachments = array(); + $dispositions = array("attachment","inline"); + foreach($this->parts as $part) { + $disposition = $this->getPartContentDisposition($part); + if (in_array($disposition, $dispositions)) { + $headers = $this->getPartHeaders($part); + $attachments[] = new MimeMailParser_attachment( + $part['disposition-filename'], + $this->getPartContentType($part), + $this->getAttachmentStream($part), + $disposition, + $headers + ); + } + } + return $attachments; + } + + /** + * Return the Headers for a MIME part + * @return Array + * @param $part Array + */ + private function getPartHeaders($part) { + if (isset($part['headers'])) { + return $part['headers']; + } + return false; + } + + /** + * Return a Specific Header for a MIME part + * @return Array + * @param $part Array + * @param $header String Header Name + */ + private function getPartHeader($part, $header) { + if (isset($part['headers'][$header])) { + return $part['headers'][$header]; + } + return false; + } + + /** + * Return the ContentType of the MIME part + * @return String + * @param $part Array + */ + private function getPartContentType($part) { + if (isset($part['content-type'])) { + return $part['content-type']; + } + return false; + } + + /** + * Return the Content Disposition + * @return String + * @param $part Array + */ + private function getPartContentDisposition($part) { + if (isset($part['content-disposition'])) { + return $part['content-disposition']; + } + return false; + } + + /** + * Retrieve the raw Header of a MIME part + * @return String + * @param $part Object + */ + private function getPartHeaderRaw(&$part) { + $header = ''; + if ($this->stream) { + $header = $this->getPartHeaderFromFile($part); + } else if ($this->data) { + $header = $this->getPartHeaderFromText($part); + } else { + throw new Exception('MimeMailParser::setPath() or MimeMailParser::setText() must be called before retrieving email parts.'); + } + return $header; + } + /** + * Retrieve the Body of a MIME part + * @return String + * @param $part Object + */ + private function getPartBody(&$part) { + $body = ''; + if ($this->stream) { + $body = $this->getPartBodyFromFile($part); + } else if ($this->data) { + $body = $this->getPartBodyFromText($part); + } else { + throw new Exception('MimeMailParser::setPath() or MimeMailParser::setText() must be called before retrieving email parts.'); + } + return $body; + } + + /** + * Retrieve the Header from a MIME part from file + * @return String Mime Header Part + * @param $part Array + */ + private function getPartHeaderFromFile(&$part) { + $start = $part['starting-pos']; + $end = $part['starting-pos-body']; + fseek($this->stream, $start, SEEK_SET); + $header = fread($this->stream, $end-$start); + return $header; + } + /** + * Retrieve the Body from a MIME part from file + * @return String Mime Body Part + * @param $part Array + */ + private function getPartBodyFromFile(&$part) { + $start = $part['starting-pos-body']; + $end = $part['ending-pos-body']; + fseek($this->stream, $start, SEEK_SET); + $body = fread($this->stream, $end-$start); + return $body; + } + + /** + * Retrieve the Header from a MIME part from text + * @return String Mime Header Part + * @param $part Array + */ + private function getPartHeaderFromText(&$part) { + $start = $part['starting-pos']; + $end = $part['starting-pos-body']; + $header = substr($this->data, $start, $end-$start); + return $header; + } + /** + * Retrieve the Body from a MIME part from text + * @return String Mime Body Part + * @param $part Array + */ + private function getPartBodyFromText(&$part) { + $start = $part['starting-pos-body']; + $end = $part['ending-pos-body']; + $body = substr($this->data, $start, $end-$start); + return $body; + } + + /** + * Read the attachment Body and save temporary file resource + * @return String Mime Body Part + * @param $part Array + */ + private function getAttachmentStream(&$part) { + $temp_fp = tmpfile(); + + array_key_exists('content-transfer-encoding', $part['headers']) ? $encoding = $part['headers']['content-transfer-encoding'] : $encoding = ''; + + if ($temp_fp) { + if ($this->stream) { + $start = $part['starting-pos-body']; + $end = $part['ending-pos-body']; + fseek($this->stream, $start, SEEK_SET); + $len = $end-$start; + $written = 0; + $write = 2028; + $body = ''; + while($written < $len) { + if (($written+$write < $len )) { + $write = $len - $written; + } + $part = fread($this->stream, $write); + fwrite($temp_fp, $this->decode($part, $encoding)); + $written += $write; + } + } else if ($this->data) { + $attachment = $this->decode($this->getPartBodyFromText($part), $encoding); + fwrite($temp_fp, $attachment, strlen($attachment)); + } + fseek($temp_fp, 0, SEEK_SET); + } else { + throw new Exception('Could not create temporary files for attachments. Your tmp directory may be unwritable by PHP.'); + return false; + } + return $temp_fp; + } + + + /** + * Decode the string depending on encoding type. + * @return String the decoded string. + * @param $encodedString The string in its original encoded state. + * @param $encodingType The encoding type from the Content-Transfer-Encoding header of the part. + */ + private function decode($encodedString, $encodingType) { + if (strtolower($encodingType) == 'base64') { + return base64_decode($encodedString); + } else if (strtolower($encodingType) == 'quoted-printable') { + return quoted_printable_decode($encodedString); + } else { + return $encodedString; + } + } + +} + + +?> -- cgit v1.2.3-2-g168b