我之前一直使用Google Analytics来统计网站访问量。近期,我切换到了Matomo(原名Piwik),一个开源的网站统计系统。
Matomo提供了一个“电子邮件报表”功能,可以定期生成一份统计报表,定时或手动发送到管理员的邮箱中。
问题
我将Matomo设置为通过SMTP发送邮件。Matomo提供的SMTP设置项目如下:
当我完成上述设置并试图手动发送报表邮件时(我使用的是腾讯企业邮箱(exmail.qq.com)),Matomo报出如下的错误:
An error occurred while sending 'HTML Email Report - 1.yesterday.1.zh-cn - uVaOTY3L5iUoeOShF6i3fgNLI02LedrbUthoibfL.html' to [email protected]. Error was 'mail from address must be same as authorization user'
原因
我搜索了GitHub上的issues,发现有人遇到了和我一样的问题,但是维护者并没有给出有效的答复。
Mail from must equal authorized user · Issue #12426 · matomo-org/matomo
我在本地部署了一个Matomo实例,在手动发送邮件的同时使用WireShark抓包。为了方便抓包,我使用了未加密的25端口。
从图中可见,Matomo默认使用当前实例所使用的域名加上noreply前缀作为发件地址。SMTP服务器报出5xx错误,提示发件地址与认证邮箱不符,随后客户端自动终止连接。
接下来我使用telnet来试图手动复现此问题。当传入一个不正确的发件地址时,服务器报出了和刚才一样的错误。
当传入正确的发件地址时,服务器返回250,表示请求正常完成。
综上,该问题的成因在于Matomo的邮件设置中没有提供“发件地址”字段的设置。当该字段没有被设置时,Matomo采用的邮件库Zend Mail默认使用当前Matomo实例所使用的域名加上noreply前缀作为发件地址,导致一些邮件提供商(例如QQ邮箱,腾讯企业邮箱)认为发件地址与用户认证邮箱不符而发信失败。
分析
我克隆了代码,然后定位到控制发送邮件的部分:
https://github.com/matomo-org/matomo/blob/3.x-dev/core/Mail.php#L53
public function setDefaultFromPiwik()
{
$customLogo = new CustomLogo();
/** @var Translator $translator */
$translator = StaticContainer::get('Piwik\Translation\Translator');
$fromEmailName = Config::getInstance()->General['noreply_email_name'];
if (empty($fromEmailName) && $customLogo->isEnabled()) {
$fromEmailName = $translator->translate('CoreHome_WebAnalyticsReports');
} elseif (empty($fromEmailName)) {
$fromEmailName = $translator->translate('TagManager_MatomoTagName');
}
$fromEmailAddress = Config::getInstance()->General['noreply_email_address'];
$this->setFrom($fromEmailAddress, $fromEmailName);
}
从代码中我们可以看出,Matomo有从配置文件提供发件地址的设定,但是诡异的是,这个字段是从General
区域,而非mail
区域读取的。
通过提交记录,我找到了这次提交相关的Issue及Pull Request,可以看出该提交的作者的初衷是为了使邮件中能够显示发件人的姓名(alias)。但是说实话,将这两个设置字段放在配置文件的General
区域并不合适,文档中也没有足够的提示。
这样我们可以得出如下的简易解决方案。
简易解决方案
在config.ini.php
的[General]
区域添加一行内容即可:
noreply_email_address = "[email protected]"
;;如果你愿意,可以加上下面这行
;;noreply_email_name = "yourname"
进一步修复
为了让这个问题得到更直观和更彻底的解决,我进行了如下工作:
- 将
noreply_email_address
和noreply_email_name
两个设置字段从General
区域移动到mail
区域。在读取时首先试图从mail
区域读取,如果读取不到则从General
读取。 - 修改设置页面的代码,使上面两个字段可以从设置界面修改。
- 在
global.ini.php
中添加默认值,以避免报错。 - 添加相关翻译。
我已经将代码修改提交了Pull Request,等待维护者Review。
参考资料
501 mail from address must be same as authorization user · Issue #92 · swiftmailer/swiftmailer
发表回复/Leave a Reply