1

我正在构建一个小接口,通过标准输入从 postfix 管道消息,并希望从电子邮件中删除附件/无论如何,我不想删除所有附件,只是那些太大而无法通过电子邮件发送的附件。

我发现 MimeMailParser 是一个很好的起点,并对它的代码进行了一些修改(公开了一些私有方法,以便将它们称为 seperatley)。这是我的版本中的课程:

<?php

require_once('attachment.class.php');

/**
 * Fast Mime Mail parser Class using PHP's MailParse Extension
 * @author gabe@fijiwebdesign.com
 * @url http://www.fijiwebdesign.com/
 * @license http://creativecommons.org/licenses/by-sa/3.0/us/
 * @version $Id$
 */
class MimeMailParser {

    /**
     * PHP MimeParser Resource ID
     */
    public $resource;

    /**
     * A file pointer to email
     */
    public $stream;

    /**
     * A text of an email
     */
    public $data;

    /**
     * Stream Resources for Attachments
     */
    public $attachment_streams;


    public $parts;
    /**
     * Inialize some stuff
     * @return
     */
    public function __construct() {
        $this->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 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;
        $mime_types = array(
            'text'=> 'text/plain',
            'html'=> 'text/html'
        );
        if (in_array($type, array_keys($mime_types))) {
            foreach($this->parts as $part) {
                if ($this->getPartContentType($part) == $mime_types[$type]) {
                    $headers = $this->getPartHeaders($part);
                    $body    = $this->getPartBody($part);
                    break;
                }
            }
        } else {
            throw new Exception('Invalid type specified for MimeMailParser::getMessageBody. "type" can either be text or html.');
        }
        return $body;
    }

    /**
     * get the headers for the message body part.
     * @return Array
     * @param $type Object[optional]
     */
    public function getMessageBodyHeaders($type = 'text') {
        $headers = false;
        $mime_types = array(
            'text'=> 'text/plain',
            'html'=> 'text/html'
        );
        if (in_array($type, array_keys($mime_types))) {
            foreach($this->parts as $part) {
                if ($this->getPartContentType($part) == $mime_types[$type]) {
                    $headers = $this->getPartHeaders($part);
                    break;
                }
            }
        } else {
            throw new Exception('Invalid type specified for MimeMailParser::getMessageBody. "type" can either be text or html.');
        }
        return $headers;
    }

    /**
     * Returns inline content files.
     * @return Array
     */
    public function getInlineContent() {
        $content = array();
        foreach($this->parts as $part) {
            $content_id = $this->getPartContentId($part);

            if ($content_id !== FALSE) {
                $content[] = new MimeMailParser_attachment(
                    $this->getPartContentName($part),
                    $this->getPartContentType($part),
                    $this->getAttachmentStream($part),
                    $this->getPartContentDisposition($part),
                    $this->getPartHeaders($part)
                );
            }
        }

        return $content;
    }

    /**
     * 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) === TRUE) {
                if (isset($part['disposition-filename']) === FALSE) {
                    $part['disposition-filename'] = md5(uniqid());
                }

                $attachments[] = new MimeMailParser_attachment(
                    $part['disposition-filename'],
                    $this->getPartContentType($part),
                    $this->getAttachmentStream($part),
                    $disposition,
                    $this->getPartHeaders($part)
                );
            }
        }

        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 ContentName of the MIME part
     * @return String
     * @param $part Array
     */
    private function getPartContentName($part) {
        if (isset($part['content-name'])) {
            return $part['content-name'];
        }
        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;
    }

    /**
     * Return the Content id
     * @return String
     * @param $part Array
     */
    private function getPartContentId($part) {
        if (isset($part['content-id'])) {
            return $part['content-id'];
        }
        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
     */
    public 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
     */
    public 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.
     */
    public 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;
        }
    }

}


?>

我调用代码并按如下方式使用它:

<?php
ini_set("display_errors","on");
error_reporting(E_ALL);
require_once('MimeMailParser.class.php');
$Parser = new MimeMailParser();
$Parser->setText(file_get_contents('php://stdin'));
$attachments = $Parser->getAttachments();
/*foreach($attachments as $attachment) {
    echo $attachment->filename; 
}*/


echo $Parser->getHeadersRaw(); # print email header
foreach ($Parser->parts as $xPart=>$yPart) {
    if ($xPart=='1') {
        continue;
    } 

        echo $Parser->getPartHeaderFromText($yPart);
        echo $Parser->getPartBodyFromText($yPart);

}
?>

发送消息并且所有内容都显示正常 - 除了一个问题 - 不显示附件。虽然,当我单击“显示原始”时,作为 base64 编码的附件确实出现在消息源上。

任何想法?

4

0 回答 0