8

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

我一直在使用 Zend_Mail 构建通过 SendGrid(一种电子邮件分发服务)发送的 MIME 消息。他们的平台允许您通过他们的 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);

好的,起初这似乎是合理的——也许 ZF 想要完全控制格式和换行。Zend_Mail::addHeader() 中调用的下一个方法是

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

这个方法对值进行编码(无论是quoted-printable 还是 base64,视情况而定)并将其分块成适当长度的行,但前提是它包含由 Zend_Mime::isPrintable($value) 确定的“不可打印字符”。

研究这种方法,换行符 (\n) 确实被认为是不可打印的字符!因此,如果在之前的方法调用中没有将它们从字符串中删除,那么长标头将被编码为 QP 并分块为 72 个字符的行,一切都会正常工作。事实上,我做了一个测试,我注释掉了对 _filterOther() 的调用,并且长标头被编码并且没有问题地通过。但是现在我只是对 ZF 进行了粗心的破解,并没有真正理解我删除的线路背后的目的,所以这不是一个长期的解决方案。

我的中期解决方案是扩展 Zend_Mail 并创建一个新方法 addHeaderForceEncode(),它将始终对标头的值进行编码,因此始终将其分块为短行。但我仍然不满意,因为我不明白为什么首先需要调用 _filterOther() —— 也许我根本不应该解决它。

谁能向我解释为什么存在这种剥离换行符的行为?如果标题不包含换行符以外的任何“不可打印字符”,它似乎不可避免地会导致标题变得太长的情况。

我已经对这个主题进行了很多不同的搜索,并查看了一些 ZF 错误报告,但没有看到任何人谈论这个。令人惊讶的是,这似乎是一个非常模糊的问题。仅供参考,我正在使用 ZF 1.11.11。


更新:如果有人想关注我打开的 ZF 问题,这里是:Zend_Mail::addHeader() 展开长标题,然后抛出异常

4

1 回答 1

6

你可能遇到了一些事情。根据RFC 2821,SMTP 中的文本行不能超过 1000 个字符:

文本行

包括 在内的文本行的最大总长度为 1000 个字符(不包括为透明度而复制的前导点)。这个数字可以通过使用 SMTP 服务扩展来增加。

标题不能包含换行符,所以这可能就是 Zend 剥离它们的原因。对于长标题,通常插入一个换行符(SMTP 中的 CRLF)和一个制表符来“换行”它们。

RFC 822的摘录:

每个标题字段可以被视为一个单一的 ASCII 字符逻辑行,包括一个字段名称和一个字段主体。为方便起见,这个概念实体的字段主体部分可以拆分为多行表示;这称为“折叠”。一般规则是,无论哪里可能存在线性空白(不仅仅是 LWSP 字符),都可以插入紧随其后的至少一个 LWSP 字符的 CRLF。

我会说该_encodeHeader()函数可能应该查看行长,如果标题长于某个魔术值,请执行“换行和制表符”以使其跨越多行。

于 2012-01-03T22:53:53.407 回答