【问题标题】:Can't understand why Zend_Mail::addHeader() strips newlines不明白为什么 Zend_Mail::addHeader() 去掉换行符
【发布时间】:2012-02-01 21:49:10
【问题描述】:

(因为这是我的第一个 SO 问题,所以我只想说我希望它不是 Zend 特定的。据我所知,这应该不是问题。虽然我可以将它发布在Zend-specific forum,我觉得我至少有可能在这里得到一个好的答案,特别是因为答案可能涉及超越 Zend Framework 的 MIME 相关问题。我基本上是想了解我的问题是否面对应该被认为是 ZF 错误,或者如果我误解或滥用它。)

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

最终,我传递的标头太长(我认为 >1000 个字符),并且出现错误。我很困惑,因为在我将值传递给 Zend_Mail::addHeader() 之前,我知道它是通过 PHP 的本机 wordwrap() 函数传递的,所以我认为行长应该永远不会成为问题。

事实证明,addHeader() 非常刻意地去除换行符,并没有通过 cmets 进行具体解释。

// 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 进行了粗心的 hack,并没有真正理解我删除的线路背后的目的,所以这不是一个长期的解决方案。

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

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

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


更新:如果有人想关注我打开的 ZF 问题,这里是:Zend_Mail::addHeader() UNfolds long headers, then throws exception

【问题讨论】:

  • StackOverflow 可以回答所有问题 ;) +1 对于您的第一个问题。
  • 感谢您让我感到宾至如归。

标签: php zend-framework smtp mime zend-mail


【解决方案1】:

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

文本行

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

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

摘自RFC 822

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

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

【讨论】:

  • 我也检查过,它似乎没有 Zend_Mail 支持标题折叠,也没有给您添加它不处理的“原始标题”的方法。看来这种修改最好在Zend_Mail_Transport_Abstract_prepareHeaders 方法中进行。如果您将第三个参数$append 传递给addHeader,它将折叠附加的数据,但还会在行尾添加一个逗号,我认为这会破坏您的 JSON。这可能值得创建一个issue
  • @tomlogic 我不明白你为什么说“标题不能包含换行符,所以这可能就是 Zend 剥离它们的原因”,因为作为 CRLF 一部分的换行符对于折叠是必要的(因为你也通过 RFC 822 指出)。但总的来说,我认为您同意 addHeader() 或 _encodeHeader() 缺少一些意识?
  • @LinusR:我认为 Zend 并不期待折叠标题,这就是它剥离换行符的原因。在我看来,Zend 应该要么接受折叠头(每个 CRLF 后跟空格;没有裸露的 CR 或 LF),要么尝试折叠长头。
  • 感谢你们两位的想法。我将在 ZF 的跟踪器上打开一个问题。
猜你喜欢
  • 2015-07-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-03
  • 2016-07-10
  • 2019-05-28
相关资源
最近更新 更多