如何从多部分电子邮件中获取文本内容?

时间:2010-09-07 20:10:07

标签: php email-parsing

    #!/usr/bin/php -q
    <?php
    $savefile = "savehere.txt";
    $sf = fopen($savefile, 'a') or die("can't open file");
    ob_start();

    // read from stdin
    $fd = fopen("php://stdin", "r");
    $email = "";
    while (!feof($fd)) {
        $email .= fread($fd, 1024);
    }
    fclose($fd);
    // 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;
        }
    }
/*$headers is ONLY included in the result at the last section of my question here*/
    fwrite($sf,"$message");
    ob_end_clean();
    fclose($sf);
    ?>

这是我尝试的一个例子。问题是我在文件中得到了太多。 这是写入文件的内容:(我只是向你发送了一堆垃圾)

From xxxxxxxxxxxxx Tue Sep 07 16:26:51 2010
Received: from xxxxxxxxxxxxxxx ([xxxxxxxxxxx]:3184 helo=xxxxxxxxxxx)
    by xxxxxxxxxxxxx with esmtpa (Exim 4.69)
    (envelope-from <xxxxxxxxxxxxxxxx>)
    id 1Ot4kj-000115-SP
    for xxxxxxxxxxxxxxxxxxx; Tue, 07 Sep 2010 16:26:50 -0400
Message-ID: <EE3B7E26298140BE8700D9AE77CB339D@xxxxxxxxxxx>
From: "xxxxxxxxxxxxx" <xxxxxxxxxxxxxx>
To: <xxxxxxxxxxxxxxxxxxxxx>
Subject: stackoverflow is helping me
Date: Tue, 7 Sep 2010 16:26:46 -0400
MIME-Version: 1.0
Content-Type: multipart/alternative;
    boundary="----=_NextPart_000_0169_01CB4EA9.773DF5E0"
X-Priority: 3
X-MSMail-Priority: Normal
Importance: Normal
X-Mailer: Microsoft Windows Live Mail 14.0.8089.726
X-MIMEOLE: Produced By Microsoft MimeOLE V14.0.8089.726

This is a multi-part message in MIME format.

------=_NextPart_000_0169_01CB4EA9.773DF5E0
Content-Type: text/plain;
    charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable

111
222
333
444
------=_NextPart_000_0169_01CB4EA9.773DF5E0
Content-Type: text/html;
    charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META content=3Dtext/html;charset=3Diso-8859-1 =
http-equiv=3DContent-Type>
<META name=3DGENERATOR content=3D"MSHTML 8.00.6001.18939"></HEAD>
<BODY style=3D"PADDING-LEFT: 10px; PADDING-RIGHT: 10px; PADDING-TOP: =
15px"=20
id=3DMailContainerBody leftMargin=3D0 topMargin=3D0 =
CanvasTabStop=3D"true"=20
name=3D"Compose message area">
<DIV><FONT face=3DCalibri>111</FONT></DIV>
<DIV><FONT face=3DCalibri>222</FONT></DIV>
<DIV><FONT face=3DCalibri>333</FONT></DIV>
<DIV><FONT face=3DCalibri>444</FONT></DIV></BODY></HTML>

------=_NextPart_000_0169_01CB4EA9.773DF5E0--

我在搜索时发现了这一点,但不知道如何实现或在我的代码中插入或者是否可以工作。

preg_match("/boundary=\".*?\"/i", $headers, $boundary);
$boundaryfulltext = $boundary[0];

if ($boundaryfulltext!="")
{
$find = array("/boundary=\"/i", "/\"/i");
$boundarytext = preg_replace($find, "", $boundaryfulltext);
$splitmessage = explode("--" . $boundarytext, $message);
$fullmessage = ltrim($splitmessage[1]);
preg_match('/\n\n(.*)/is', $fullmessage, $splitmore);

if (substr(ltrim($splitmore[0]), 0, 2)=="--")
{
$actualmessage = $splitmore[0];
}
else
{
$actualmessage = ltrim($splitmore[0]);
}

}
else
{
$actualmessage = ltrim($message);
}

$clean = array("/\n--.*/is", "/=3D\n.*/s");
$cleanmessage = trim(preg_replace($clean, "", $actualmessage)); 

那么,如何才能将电子邮件的纯文本区域放入我的文件或脚本中进行更深入的处理?

提前致谢。 stackoverflow很棒!

2 个答案:

答案 0 :(得分:15)

为了隔离电子邮件正文的纯文本部分,您必须执行以下四个步骤:

<强> 1。获取MIME边界字符串

我们可以使用正则表达式来搜索标题(我们假设它们位于单独的变量$headers中):

$matches = array();
preg_match('#Content-Type: multipart\/[^;]+;\s*boundary="([^"]+)"#i', $headers, $matches);
list(, $boundary) = $matches;

正则表达式将搜索包含边界字符串的Content-Type标头,然后将其捕获到第一个capture group中。然后,我们将该捕获组复制到变量$boundary

<强> 2。将电子邮件正文拆分为细分

一旦我们有了边界,我们就可以将身体分成不同的部分(在你的信息体中,身体每次出现时都会以--开头)。根据{{​​3}},应忽略第一个边界之前的所有内容。

$email_segments = explode('--' . $boundary, $message);
array_shift($email_segments); // drop everything before the first boundary

这将为我们留下一个包含所有段的数组,忽略第一个边界之前的所有内容。

第3。确定哪个段是纯文本。

纯文本段将具有MIME类型Content-Type的{​​{1}}标头。我们现在可以使用该标题搜索每个段的第一个段:

text/plain

由于我们要查找的是常量,我们可以使用MIME spec(在字符串中查找子字符串的第一个实例,不区分大小写)而不是正则表达式。如果找到foreach ($email_segments as $segment) { if (stristr($segment, "Content-Type: text/plain") !== false) { // We found the segment we're looking for! } } 标题,我们就会得到我们的细分。

<强> 4。从细分中删除所有标题

现在我们需要从我们找到的细分中删除任何标头,因为我们只需要实际的邮件内容。可以在此处显示四个stristr:我们之前看到的Content-TypeContent-TypeContent-IDContent-Disposition。标题由Content-Transfer-Encoding终止,因此我们可以使用它来确定标题的结尾:

\r\n

正则表达式末尾的$text = preg_replace('/Content-(Type|ID|Disposition|Transfer-Encoding):.*?\r\n/is', "", $segment); MIME headers使得点匹配任何换行符。 s将收集尽可能少的字符(即。.*?之前的所有内容); \r\n?上的modifier

在此之后,.*将包含您的电子邮件内容。

所以要把它与你的代码放在一起:

$text

答案 1 :(得分:0)

有一个答案here

您只需要更改这两行:

require_once('/path/to/class/rfc822_addresses.php');
require_once('/path/to/class/mime_parser.php');