我正在寻找好的/工作的/简单的使用 php 代码来将原始电子邮件解析为多个部分。

我已经编写了几个强力解决方案,但每次,一个小的更改/标题/空格/某些东西都会出现,我的整个解析器就会失败,项目就会崩溃。

在我了解 PEAR/PECL 之前,我需要实际的代码。我的主机有一些奇怪的配置或其他东西,我似乎永远无法正确构建 .so。如果我确实制作了 .so,路径/环境/php.ini 中的一些差异并不总是使其可用(apache vs cron vs cli)。

哦,最后一件事,我正在解析原始电子邮件文本,而不是 POP3,也不是 IMAP。它通过 .qmail 电子邮件重定向输送到 php 脚本中。

我并不指望 SOF 为我写它,我正在寻找一些“正确”做这件事的技巧/起点。这是我知道已经解决的“轮子”问题之一。

有帮助吗?

解决方案

你希望最终得到什么?正文、主题、发件人、附件?你应该花一些时间 RFC2822 了解邮件的格式,但以下是格式正确的电子邮件的最简单规则:

HEADERS\n
\n
BODY

也就是说,第一个空行(双换行符)是 HEADERS 和 BODY 之间的分隔符。标题看起来像这样:

HSTRING:HTEXT

HSTRING 始终从行首开始,并且不包含任何空格或冒号。HTEXT 可以包含各种文本,包括换行符,只要换行符后面跟着空格即可。

“BODY”实际上只是第一个双换行符后面的任何数据。(如果您通过 SMTP 传输邮件,则有不同的规则,但通过管道处理邮件则不必担心)。

所以,很简单,大约在 1982 年 RFC822 术语,电子邮件如下所示:

HEADER: HEADER TEXT
HEADER: MORE HEADER TEXT
  INCLUDING A LINE CONTINUATION
HEADER: LAST HEADER

THIS IS ANY
ARBITRARY DATA
(FOR THE MOST PART)

但大多数现代电子邮件都比这更复杂。标头可以编码为字符集或 RFC2047 哑剧的话,或者一大堆我现在没有想到的其他东西。如果你想让它们有意义的话,现在很难编写自己的代码。几乎所有由 MUA 生成的电子邮件都会 哑剧 编码。这可能是 uuencoded 文本,可能是 html,也可能是 uuencoded excel 电子表格。

我希望这有助于提供一个框架来理解电子邮件的一些非常基本的部分。如果您提供更多有关您尝试如何处理数据的背景信息,我(或其他人)可能能够提供更好的方向。

其他提示

尝试 Plancake PHP 电子邮件解析器:https://github.com/plancake/official-library-php-email-parser

我已将它用于我的项目。它工作得很好,它只是一个类,而且是开源的。

我把它拼凑在一起,有些代码不是我的,但我不知道它来自哪里......后来我采用了更强大的“MimeMailParser”,但这工作正常,我使用 cPanel 将我的默认电子邮件传输到它,它工作得很好。

#!/usr/bin/php -q
<?php
// Config
$dbuser = 'emlusr';
$dbpass = 'pass';
$dbname = 'email';
$dbhost = 'localhost';
$notify= 'services@.com'; // an email address required in case of errors
function mailRead($iKlimit = "") 
    { 
        // Purpose: 
        //   Reads piped mail from STDIN 
        // 
        // Arguements: 
        //   $iKlimit (integer, optional): specifies after how many kilobytes reading of mail should stop 
        //   Defaults to 1024k if no value is specified 
        //     A value of -1 will cause reading to continue until the entire message has been read 
        // 
        // Return value: 
        //   A string containing the entire email, headers, body and all. 

        // Variable perparation         
            // Set default limit of 1024k if no limit has been specified 
            if ($iKlimit == "") { 
                $iKlimit = 1024; 
            } 

            // Error strings 
            $sErrorSTDINFail = "Error - failed to read mail from STDIN!"; 

        // Attempt to connect to STDIN 
        $fp = fopen("php://stdin", "r"); 

        // Failed to connect to STDIN? (shouldn't really happen) 
        if (!$fp) { 
            echo $sErrorSTDINFail; 
            exit(); 
        } 

        // Create empty string for storing message 
        $sEmail = ""; 

        // Read message up until limit (if any) 
        if ($iKlimit == -1) { 
            while (!feof($fp)) { 
                $sEmail .= fread($fp, 1024); 
            }                     
        } else { 
            while (!feof($fp) && $i_limit < $iKlimit) { 
                $sEmail .= fread($fp, 1024); 
                $i_limit++; 
            }         
        } 

        // Close connection to STDIN 
        fclose($fp); 

        // Return message 
        return $sEmail; 
    }  
$email = mailRead();

// handle email
$lines = explode("\n", $email);

// empty vars
$from = "";
$subject = "";
$headers = "";
$message = "";
$splittingheaders = true;
for ($i=0; $i < count($lines); $i++) {
    if ($splittingheaders) {
        // this is a header
        $headers .= $lines[$i]."\n";

        // look out for special headers
        if (preg_match("/^Subject: (.*)/", $lines[$i], $matches)) {
            $subject = $matches[1];
        }
        if (preg_match("/^From: (.*)/", $lines[$i], $matches)) {
            $from = $matches[1];
        }
        if (preg_match("/^To: (.*)/", $lines[$i], $matches)) {
            $to = $matches[1];
        }
    } else {
        // not a header, but message
        $message .= $lines[$i]."\n";
    }

    if (trim($lines[$i])=="") {
        // empty line, header section has ended
        $splittingheaders = false;
    }
}

if ($conn = @mysql_connect($dbhost,$dbuser,$dbpass)) {
  if(!@mysql_select_db($dbname,$conn))
    mail($email,'Email Logger Error',"There was an error selecting the email logger database.\n\n".mysql_error());
  $from    = mysql_real_escape_string($from);
  $to    = mysql_real_escape_string($to);
  $subject = mysql_real_escape_string($subject);
  $headers = mysql_real_escape_string($headers);
  $message = mysql_real_escape_string($message);
  $email   = mysql_real_escape_string($email);
  $result = @mysql_query("INSERT INTO email_log (`to`,`from`,`subject`,`headers`,`message`,`source`) VALUES('$to','$from','$subject','$headers','$message','$email')");
  if (mysql_affected_rows() == 0)
    mail($notify,'Email Logger Error',"There was an error inserting into the email logger database.\n\n".mysql_error());
} else {
  mail($notify,'Email Logger Error',"There was an error connecting the email logger database.\n\n".mysql_error());
}
?>

您可以尝试以下 Mailparse 功能: http://php.net/manual/en/book.mailparse.php, ,但是默认的 php conf 中没有。

有一个库用于将原始电子邮件消息解析为 php 数组 - http://flourishlib.com/api/fMailbox#parseMessage.

静态方法parsemessage()可用于将完整的MIME电子邮件将其解析为与FetchMessage()返回相同的格式,减去UID键。

$ parsed_message = fmailbox :: parsemessage(file_get_contents('/path/to/email'));

以下是已解析消息的示例:

array(
    'received' => '28 Apr 2010 22:00:38 -0400',
    'headers'  => array(
        'received' => array(
            0 => '(qmail 25838 invoked from network); 28 Apr 2010 22:00:38 -0400',
            1 => 'from example.com (HELO ?192.168.10.2?) (example) by example.com with (DHE-RSA-AES256-SHA encrypted) SMTP; 28 Apr 2010 22:00:38 -0400'
        ),
        'message-id' => '<4BD8E815.1050209@flourishlib.com>',
        'date' => 'Wed, 28 Apr 2010 21:59:49 -0400',
        'from' => array(
            'personal' => 'Will Bond',
            'mailbox'  => 'tests',
            'host'     => 'flourishlib.com'
        ),
        'user-agent'   => 'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.9) Gecko/20100317 Thunderbird/3.0.4',
        'mime-version' => '1.0',
        'to' => array(
            0 => array(
                'mailbox' => 'tests',
                'host'    => 'flourishlib.com'
            )
        ),
        'subject' => 'This message is encrypted'
    ),
    'text'      => 'This message is encrypted',
    'decrypted' => TRUE,
    'uid'       => 15
);

https://github.com/zbateson/MailMimeParser 对我有用,并且不需要 mailparse 扩展。

<?php
echo $message->getHeaderValue('from');          // user@example.com
echo $message
    ->getHeader('from')
    ->getPersonName();                          // Person Name
echo $message->getHeaderValue('subject');       // The email's subject

echo $message->getTextContent();                // or getHtmlContent

Pear lib Mail_mimeDecode 是用纯 PHP 编写的,您可以在此处查看: Mail_mimeDecode源

您可能不会从编写自己的 MIME 解析器中获得太多乐趣。您发现“过度开发的邮件处理包”的原因是因为 MIME 是一组非常复杂的规则/格式/编码。MIME 部分可以是递归的,这是乐趣的一部分。我认为你最好的选择是编写最好的 MIME 处理程序,解析消息,丢弃所有非 text/plain 或 text/html 的内容,然后强制传入字符串中的命令以 COMMAND 为前缀:或者类似的东西,这样你就可以在淤泥中找到它。如果您从这样的规则开始,您就有很大的机会处理新的提供商,但是如果出现新的提供商(或者如果您当前的提供商选择更改其消息传递架构),您应该准备好进行调整。

用 PHP 解析电子邮件并不是一项不可能完成的任务。我的意思是,你不需要一个工程师团队来做这件事;你需要一个工程师团队来完成它。作为个人是可以实现的。实际上,我发现最难的部分是创建 FSM 来解析 IMAP BODYSTRUCTURE 结果。我在互联网上没有看到过这个,所以我自己写了一个。我的例程基本上根据命令输出创建一个嵌套数组的数组,数组中的深度大致对应于执行查找所需的部件号。因此它可以非常优雅地处理嵌套的 MIME 结构。

问题是 PHP 的默认 imap_* 函数没有提供太多粒度...所以我必须打开 IMAP 端口的套接字并编写函数来发送和检索必要的信息 (IMAP FETCH 1 BODY.PEEK[1.2]例如),这涉及查看 RFC 文档。

数据的编码(quoted-printable、base64、7bit、8bit 等)、消息长度、内容类型等。全部提供给您;用于附件、文本、html 等。您可能还必须弄清楚邮件服务器的细微差别,因为并非所有字段都始终 100% 实现。

宝石是 FSM...如果您有计算机科学背景,那么做这个会非常有趣(关键是括号不是常规语法;));否则,使用传统方法,这将是一场斗争和/或导致丑陋的代码。你还需要一些时间!

希望这可以帮助!

我不确定这是否对您有帮助 - 希望如此 - 但它肯定会帮助其他有兴趣了解更多有关电子邮件的人。 马库斯·博因顿 今年 3 月的 PHP 伦敦会议上做了题为“Mail() and life after Mail()”的最佳演讲之一, 幻灯片MP3 在线。他在电子邮件和 PHP 方面进行了深入的广泛研究,具有一定的权威性。

我的看法是,尝试编写一个真正通用的解析器时,您会陷入一个痛苦的世界。

编辑 - 这些文件似乎已在 PHP London 网站上删除;找到了马库斯的幻灯片 自己的网站: 第1部分 第2部分 但在任何地方都看不到 MP3

是的,我能够根据 rfc 和其他一些基本教程编写一个基本的解析器。但它的多部分哑剧嵌套边界一直让我感到困惑。

我发现从我的手机发送的彩信(不是短信)消息只是标准电子邮件,所以我有一个系统可以读取传入的电子邮件,检查发件人(仅允许来自我的手机),并使用正文部分运行不同的电子邮件我的服务器上的命令。它有点像通过电子邮件进行远程控制。

因为该系统是为了发送图片而设计的,所以它有一堆不同编码的部分。mms.smil.txt 部分、text/plain 部分(无用,只是说“这是一条 html 消息”)、application/smil 部分(手机会在其上显示的部分)、text/html 部分带有我的运营商的广告,然后是我的消息,但全部都用 html 封装,最后是带有我的纯消息的文本文件附件(这是我使用的部分)(如果我将图像作为附件推入消息中,则将其放在附件1,base64编码,然后我的文本部分作为附件2附加)

我让它按照我的运营商提供的确切邮件格式工作,但是当我通过它运行别人手机上的消息时,它以各种悲惨的方式失败了。

我还有其他项目想扩展这个电话->邮件->解析->命令系统,但我需要一个稳定/可靠/通用的解析器来从邮件中获取不同的部分来使用它。

我的最终目标是拥有一个函数,我可以将原始管道邮件输入其中,并返回一个大数组,其中包含标题 var:val 对的关联子数组,以及一个用于作为整个字符串的正文文本的函数

我对此搜索得越多,就越发现同样的事情:巨大的过度开发的邮件处理包,可以完成与邮件相关的所有事情,或者无用的(对我来说,在这个项目中)教程。

我想我必须硬着头皮,认真地自己写一些东西。

我遇到了同样的问题,所以我编写了以下课程:电子邮件_解析器。它接收原始电子邮件并将其变成一个漂亮的对象。

它需要 PEAR Mail_mimeDecode,但这应该很容易通过 WHM 或直接从命令行安装。

在这里获取: https://github.com/optimumweb/php-email-reader-parser

简单的 PhpMime 解析器 https://github.com/breakermind/PhpMimeParser 你可以从文件、字符串中剪切哑剧消息。获取文件、html 和内联图像。

$str = file_get_contents('mime-mixed-related-alternative.eml');

// MimeParser
$m = new PhpMimeParser($str);

// Emails
print_r($m->mTo);
print_r($m->mFrom);

// Message
echo $m->mSubject;
echo $m->mHtml;
echo $m->mText;

// Attachments and inline images
print_r($m->mFiles);
print_r($m->mInlineList);
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top