从电子邮件正文

时间:2015-10-20 15:33:30

标签: php email line-breaks

发送电子邮件时,许多服务器会添加额外的换行符以限制每行的长度。

在PHP脚本中提取电子邮件时,如何恢复原始换行符?

实施例

假设我发送以下内容:

  

Lorem ipsum Dolore incididunt in culpa ea ea sed quis sint voluptate quis laborum ullamco Excepteur do adipisicing consequat ex in reprehenderit officia in ad deserunt magna nulla dolor laborum occaecat reprehenderit aliquip dolor ea anim ea in veniam adipisicing culpa tempor qui elit voluptate consectetur elit劳动人民共和国劳动力肛门肛门U su mol mol mol mol mol mol mol mol c c c c c c c c c c c c conse in in in in in in in in in in in in in in in in in in in in in in in in in in in in in in in in in in in in in in in in in in in in in in in in   非威廉姆尼姆非精英和广告宣传文化广告宣传文化广告文化广告文化广告宣传文化广告文化广告文化广告文化广告文化广告文化广告文化广告文化广告文化广告文章广告文章sed quis nisi fugiat dolor ea commodo ut sunt in consequat consectetur ut nulla pariatur est dolor dolore non ut occaecat officia Duis Ut ex exercitation esse ullamco nulla incididunt commodo pariatur dolore nostrud fugiat id dolor minim non sint amet adipisicing occaecat enim non Ut ad iris sint aliquip nisi ut commodo minim proident elit nulla quis ut ad dolor Excepteur dolore Duis。

请注意,本文中只有一个换行符!

使用Thunderbird在接收端检查电子邮件的源代码,或通过PHP获取电子邮件正文,内容格式如下:

  

Lorem ipsum Dolore incididunt in culpa ea ea sed quis sint voluptate
  quis laborum ullamco Excepteur做的adipisicing consequat ex in
  adhenderit officia in ad deserunt magna nulla dolor laborum occaecat
  rephenderit aliquip dolor ea anim ea in veniam adipisicing culpa
  tempor qui elit voluptate consectetur elit laboris minim consectetur
  劳动力肛门incididunt Ut sunt sunt mollit elit irure do cillum dolore
  在多洛尔的比赛中,在多洛尔的比赛   Laborum reprehenderit dolore ut。
  不属于威廉姆斯的微不足道的广告和广告   劳动力不稳定的莫尔利特多洛尔运动eiusmod ea non ea
  ullamco nostrud cillum nostrud laborum commodo esse reprehenderit ut
  deinunt officia do in anim dolore ullamco pariatur ex amet nulla
  Excepteur mollit officia fugiat eu sed quis nisi fugiat dolor ea commodo
  ut sunt in consequat consectetur ut nulla pariatur est dolor dolore non
  ut occaecat officia Duis Ut ex exercitation esse ullamco nulla
  incididunt commodo pariatur dolore nostrud fugiat id dolor minim non
  sint amet adipisicing occaecat enim non Ut ad irure sint aliquip nisi ut
  commodo minim proident elit nulla quis ut ad dolor Excepteur dolore Duis。

请注意,每行限制为一定长度,因此存在16个额外的换行符。这些额外的换行符会自动添加到导致我收到电子邮件的事件链中的某个位置。

我希望我的电子邮件提取PHP脚本删除其他换行符以恢复内容的原始双行格式。

我知道新的换行符不是由PHP脚本添加的,我知道它们来自哪里,我不知道是怎么让我的PHP脚本删除那些换行符。

以下是用于获取电子邮件正文的代码:

$connection = imap_open(
    sprintf(
        '{%s:110/pop3}INBOX',
        Configure::read('Email.Inbox.host')
    ),
    Configure::read('Email.Inbox.email'),
    Configure::read('Email.Inbox.password')
);

$mailbox = imap_check($connection);
$messages = imap_fetch_overview($connection, '1:' . $mailbox->Nmsgs); 

foreach($messages as $message) {
    $content = imap_fetchbody($connection, $message->msgno, 1);
}

我尝试了什么?

我尝试使用imap_body代替imap_fetchbody,因为前者不处理电子邮件正文。但是在此之前已经存在额外的换行符,并且与常规换行符无法区分。两者都包含\r\n

我认为必须有一种方法可以做到这一点,因为Thunderbird以正确的格式显示收到的电子邮件,没有额外的16个换行符,尽管它们出现在显示消息的源代码中。因此,可能必须有一种方法可以从电子邮件中删除额外的16个换行符。

以下是Thunderbird的截图,其中显示了顶部电子邮件的源代码以及底部显示的纯文本显示。

What is this magic? Teach me, master!

1 个答案:

答案 0 :(得分:1)

即使这个问题很老,当我遇到这个完全相同的问题时,它也是热门歌曲之一。正如Marc在评论中指出的,它确实与format=flowed有关。因此,我深入研究RFC 2646,发现section 4.1 Generate Format = Flowed

  

因为软换行符是SP C​​RLF序列,所以生成代理通过在空格出现后插入CRLF来创建一个。      

生成代理不应在单词中插入空格(一串可打印的字符,不包含空格)。如果遇到单词超过79个字符(但少于998个字符,即行长度的[SMTP]限制),则业务代表应按原样发送该单词并超过行长度限制为79个字符。

因此,为了获得最初编写的电子邮件,只需搜索所有SP + CRLF出现并将其替换为空即可。然后,您可能还想撤消空格填充,同时还要考虑带引号的文本(以任意数量的>字符开头的行,后跟一个空格)。根据RFC,测试的顺序是引号>空格填充>流线:

  

在接收时,如果一行的第一个字符是空格,则在逻辑上将其删除。在对引号行进行测试之后,对流线进行测试之前,会发生这种情况。

我自己厨房里的粗PoC:

// I'm using fetchmime() because I want to be sure I'm getting the proper MIME type for the relevant section
$mimes = imap_fetchmime($connection, $message->msgno, $section);

// I don't want to store all headers in an array since I just want to know the Content-Type
// [ \t]* is probably not necessary but it's there in case of broken clients/servers
if(preg_match('/^[ \t]*Content-Type.*format=flowed\b/mi', $mimes)) {
    // First, let's undo space stuffing but don't touch stuffed lines with quotes
    $content = preg_replace('/^ +(?!>+ )/m', '', $content);

    // Then, remove flowed SP+(CR)LF sequences as well as any possible quotation marks that might appear after it to reform one long line of text
    $content = preg_replace('/( )\r?\n(>+ +)?/', '$1', $content);

    // Remove empty quoted lines at *the end of the string only*, keeping any such lines anywhere else as-is for readability
    $content = preg_replace('/(\r?\n>+\s*)+$/', '', $content);
}
// And finally trim the entire thing (regardless of formatting)
$content = trim($content);
// Or when outputting to browsers:
//$content = nl2br(trim($content));

对我来说,这种方法适用于:

  • 简单的单行电子邮件
  • OP给出的带有2个段落的lorem ipsum示例
  • 单行,后接2个换行符和一个由2行组成的签名
  • 电子邮件的报价最多为4级(可能超出了,但我没有花那么多时间检查)