使用7BIT内容传输编码解析电子邮件正文 - PHP

时间:2012-10-01 22:48:26

标签: php email imap decode

我最近一直在实现一些基于PHP / IMAP的电子邮件处理功能,并且除了消息体解码(在某些情况下)之外,大多数工作都很好用。

我认为,到目前为止,我已经记下了RFC 2822(“互联网邮件格式”文档指南),通过电子邮件处理代码阅读了六个开源CMS,并阅读了一些内容处理PHP中处理电子邮件的论坛帖子,博客文章等。

我还分叉并完全重写了PHP的类Imap,并且该类处理好电子邮件 - 我有一些有用的方法来检测自动回复(对于不在办公室,旧地址等) 。),解码base64和8bit消息等。

然而,有一件事我无法可靠地(或者有时甚至根本不能)工作,当有Content-Transfer-Encoding: 7bit消息时。

似乎不同的电子邮件客户端/服务将7BIT解释为不同的东西。我收到了一些据称7BIT 实际 Base64编码的电子邮件。我已经得到了一些实际引用 - 可打印 - 编码。还有一些不以任何方式编码。有些是HTML,但未标明为HTML,它们也被列为7BIT ...

以下是使用7Bit编码接收的消息实体的一些示例(剪辑):

1:

A random message=20

Sent from my iPhone

2:

PGh0bWwgeG1sbnM6dj0idXJuOnNjaGVtYXMtbWljcm9zb2Z0LWNvbTp2bWwi
IHhtbG5zOm89InVybjpzY2hlbWFzLW1pY3Jvc29mdC1jb206b2ZmaWNlOm9m

3:

tangerine apricot pepper.=0A=C2=A0=0ALet me know if you have any availabili=
ty over the next month or so. =0A=C2=A0=0AThank you,=0ANames Withheld=0A908=
-319-5916=0A=C2=A0=0A=C2=A0=0A=C2=A0=0A=0A=0A______________________________=
__=0AFrom: Names Witheld =0ATo: Names Withheld=

这些全部与'7Bit'编码一起发送(好吧,至少根据PHP / imap_*),但是在我传递它们之前它们显然需要更多解码作为明文。有没有办法可靠地将所有带有7Bit编码的消息转换为纯文本?

3 个答案:

答案 0 :(得分:9)

花了一点时间之后,我决定写一些启发式检测,正如Max在我原来问题的评论中所建议的那样。

我在Imap.php中构建了一个更健壮的decode7Bit()方法,它通过一堆常见的编码字符(如=A0)并用它们的UTF-8等效替换它们,如果消息看起来像是base64编码的话,还会对消息进行解码:

/**
 * Decodes 7-Bit text.
 *
 * PHP seems to think that most emails are 7BIT-encoded, therefore this
 * decoding method assumes that text passed through may actually be base64-
 * encoded, quoted-printable encoded, or just plain text. Instead of passing
 * the email directly through a particular decoding function, this method
 * runs through a bunch of common encoding schemes to try to decode everything
 * and simply end up with something *resembling* plain text.
 *
 * Results are not guaranteed, but it's pretty good at what it does.
 *
 * @param $text (string)
 *   7-Bit text to convert.
 *
 * @return (string)
 *   Decoded text.
 */
public function decode7Bit($text) {
  // If there are no spaces on the first line, assume that the body is
  // actually base64-encoded, and decode it.
  $lines = explode("\r\n", $text);
  $first_line_words = explode(' ', $lines[0]);
  if ($first_line_words[0] == $lines[0]) {
    $text = base64_decode($text);
  }

  // Manually convert common encoded characters into their UTF-8 equivalents.
  $characters = array(
    '=20' => ' ', // space.
    '=E2=80=99' => "'", // single quote.
    '=0A' => "\r\n", // line break.
    '=A0' => ' ', // non-breaking space.
    '=C2=A0' => ' ', // non-breaking space.
    "=\r\n" => '', // joined line.
    '=E2=80=A6' => '…', // ellipsis.
    '=E2=80=A2' => '•', // bullet.
  );

  // Loop through the encoded characters and replace any that are found.
  foreach ($characters as $key => $value) {
    $text = str_replace($key, $value, $text);
  }

  return $text;
}

这取自我在GitHub上的Imap class for PHP版本1.0-beta2。

如果您对提高效率有任何想法,请告诉我。我最初尝试通过quoted_printable_decode()运行所有内容,但有时PHP会抛出含糊不清的异常,所以我放弃了这种方法。

答案 1 :(得分:5)

我知道这是一个老问题....但我现在遇到这个问题,现在看来PHP已经有了解决方案。

此函数imap_fetchstructure()将为您提供编码类型。

0   7BIT
1   8BIT
2   BINARY
3   BASE64
4   QUOTED-PRINTABLE
5   OTHER

从那里你应该能够创建这样的函数来解码消息

function _encodeMessage($msg, $type){

            if($type == 0){
                return mb_convert_encoding($msg, "UTF-8", "auto");
            } elseif($type == 1){
                return imap_8bit($msg); //imap_utf8
            } elseif($type == 2){
                return imap_base64(imap_binary($msg));
            } elseif($type == 3){
                return imap_base64($msg);
            } elseif($type == 4){
                return imap_qprint($msg);
                //return quoted_printable_decode($msg);
            } else {
                return $msg;
            }
        }

你可以像这样调用这个函数

$struct = imap_fetchstructure($conn, $messageNumber, 0);
$message = imap_fetchbody($conn, $messageNumber, 1);
$message = _encodeMessage($message, $struct->encoding);
echo $message;

我希望这有助于某人:)

答案 2 :(得分:0)

$structure = imap_fetchstructure; 不是$encoding = $structure->encoding 但是$encoding = $structure->parts[ $p ]->encoding

我想我遇到了同样的问题,现在它已经解决了。 (7bit没有转换为UTF-8,不断获得ASCII)我认为我有7bit,但是将代码更改为" BUT"我得到了$encoding=4,而不是$encoding=0,这意味着我必须imap_qprint($body)mb_convert_encoding($body, 'UTF-8', $charset)才能得到我想要的内容。

无论如何检查编码号码!! (应该是4而不是零)