无法理解为什么Zend_Mail :: addHeader()会删除换行符

8

(由于这是我的第一个SO问题,所以我希望它不是太专门化于Zend。据我所知,这不应该是个问题。虽然我可以在特定于Zend的论坛上发布它,但我觉得我至少有同样可能在这里得到一个好答案,特别是因为答案可能涉及超越Zend框架的MIME相关问题。我基本上想要理解的是,我面临的问题是否应该被视为ZF的一个bug,或者我是否误解了某些东西或者误用了它。)

我一直在使用Zend_Mail来构建一个MIME消息,将其通过SendGrid(一种电子邮件分发服务)发送。他们的平台允许您通过他们的SMTP服务器发送电子邮件,但如果您使用一个特殊的标题(X-SMTPAPI),则会提供附加功能,其值是专有参数的JSON编码字符串,这可能会非常长。

最终,我传递的标题变得太长了(我认为>1000个字符),并且我遇到了错误。我感到困惑,因为我知道它在传递给Zend_Mail :: addHeader()之前,已经通过PHP的本地wordwrap()函数进行了换行处理,所以我认为行长度永远不应该成为问题。

事实证明,addHeader()非常有意地剥离换行符,没有通过注释进行任何特别的解释。

// In Zend_Mail::addHeader()
$value = $this->_filterOther($value);


// In Zend_Mail::_filterOther()
$rule = array("\r" => '',
              "\n" => '',
              "\t" => '',
);
return strtr($data, $rule);

一开始看起来这个想法很合理——也许Zend框架想要完全控制格式和换行。在Zend_Mail::addHeader()方法中调用的下一个方法是:

$value = $this->_encodeHeader($value);

该方法将值进行编码(适当时使用quoted-printable或base64),并将其分成适当长度的行,但在它包含"不可打印字符"时才这样做,由Zend_Mime::isPrintable($value)决定。
查看该方法,换行符(\n)确实被视为不可打印字符!因此,如果它们没有在前一个方法调用中被剥离出字符串,那么长标题将被编码为QP,并分成72个字符的行,一切都会正常工作。事实上,我进行了一项测试,注释掉_filterOther()的调用,并且长头部得到编码并顺利通过。但现在,我只是无意识地对ZF进行了破坏,而没有真正理解我删除的那行代码的目的,因此这不能成为长期解决方案。
我的中期解决方案是扩展Zend_Mail并创建一个新方法addHeaderForceEncode(),它将始终对标头的值进行编码,从而始终将其分成短行。但我仍然不满意,因为我不明白为什么首先要进行_filterOther()调用-也许我根本不应该绕过它。
有人能向我解释为什么会存在去除换行符的行为吗?似乎这不可避免地会导致头部在不包含任何"非打印字符"(除了换行符)时变得过长。
我已经进行了多次关于此主题的搜索,并查看了一些ZF错误报告,但没有看到任何人讨论这个问题。令人惊讶的是,它似乎是一个非常晦涩的问题。FYI,我正在使用ZF 1.11.11。
更新:如果有人想关注我关于此事发起的ZF问题,请点击此处:Zend_Mail::addHeader() UNfolds long headers, then throws exception

1
StackOverflow可以回答任何问题 ;)对于你的第一个问题点赞。 - Flavius
感谢让我感到受欢迎。 - LinusR
1个回答

6
您可能会遇到一些问题。按照RFC 2821,SMTP中的文本行不能超过1000个字符:

文本行

文本行的最大总长度(包括用于透明性的前导点重复的)为1000个字符。通过使用SMTP服务扩展,可以增加此数字。

头部不能包含换行符,这可能是Zend会剥离它们的原因。对于长头部,通常会插入一个换行符(SMTP中的CRLF)和一个制表符来“换行”。 RFC 822摘录:
每个头字段都可以视为单个逻辑行的ASCII字符,包括字段名称和字段正文。为了方便起见,该概念实体的字段正文部分可以拆分为多行表示法;这称为“折叠”。一般规则是,在任何可能存在线性空格(而不仅仅是LWSP字符)的地方,可以立即插入一个紧随其后至少有一个LWSP字符的CRLF。我认为_encodeHeader()函数可能应该查看行长度,如果标题长于某些神奇值,则进行“换行和制表符”以使其跨越多行。

2
我也检查了一下,似乎Zend_Mail不支持标题折叠,也没有提供添加“原始标题”的方法。最好在Zend_Mail_Transport_Abstract_prepareHeaders方法中进行修改。如果您将第三个参数$append传递给addHeader,它将折叠附加的数据,但还会在行末添加逗号,这可能会破坏您的JSON。这可能值得创建一个问题 - drew010
@tomlogic,我不明白你为什么说“标题不能包含换行符,所以Zend可能会将其剥离”,因为CRLF中的换行符是折叠的必要部分(正如你通过RFC 822指出的那样)。但总的来说,我认为你同意addHeader()或_encodeHeader()缺少一些意识。 - LinusR
@LinusR:我认为Zend没有预料到折叠的头部,这就是为什么它会去掉换行符的原因。在我看来,Zend应该接受折叠的头部(其中每个CRLF后面跟随空格;没有裸露的CR或LF),或者尝试折叠长头部。 - tomlogic
感谢你们两位的想法。我会在ZF的跟踪器上开一个问题。 - LinusR

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接