Exim应该将失败的消息的ID保存到数据库(或者至less将其pipe理到一个shell脚本)

安装程序

我们在Debian Squeeze 6(Squeeze)(LTS)上运行exim 4.72版本。

Exim仅用于外发电子邮件 – 它不处理来自其他服务器/域的电子邮件 – 它只处理来自本地运行的Java(Web)应用程序的(外发)消息。

对于匿名,让我们假设我们用于公司电子邮件地址的域名,以及我们的java web应用程序运行的域名称为example.com

的背景

我们有一个用java框架编写的web应用程序,它允许用户发送电子邮件。 这些电子邮件的envelope-from地址属于我们的域名: [email protected]
从技术上讲,这些电子邮件是创build的,并从Java Web应用程序(我们使用JavaMail API )本地发送,然后发送到他们的目标地址。
传入我们网域的电子邮件由Google Apps / Gmail处理(我们还设置了SPFlogging等)
这个设置工作正常,没有任何问题。

为了完成,这里是我们的(匿名) update-exim4.conf.conf

 root@server:~# cat /etc/exim4/update-exim4.conf.conf # ... comments are skipped ... dc_eximconfig_configtype='internet' dc_other_hostnames='node1.example.com' dc_local_interfaces='127.0.0.1 ; ::1' dc_readhost='' dc_relay_domains='' dc_minimaldns='false' dc_relay_nets='' dc_smarthost='' CFILEMODE='644' dc_use_split_config='true' dc_hide_mailname='' dc_mailname_in_oh='true' dc_localdelivery='maildir_home' 

问题

最近我们收到反弹电子邮件,因为我们的Java Web应用程序的用户试图发送电子邮件到不存在的电子邮件地址。

让我们看看(匿名)日志文件中的这种情况:

 root@server:~# cat /var/log/exim4/mainlog # try to send an email to an unrouteable address 2014-06-01 17:34:07 1Wr7lv-0000CR-G0 <= [email protected] H=localhost (node1.example.com) [127.0.0.1] P=esmtp S=620363 id=484648301.663.1401636847496.JavaMail.webapp@node1 2014-06-01 17:34:07 1Wr7lv-0000CR-G0 ** [email protected]: Unrouteable address 2014-06-01 17:34:07 1Wr7lv-0000CR-G0 Completed # Immediately a bounce message is generated and gets send to the the origin sender 2014-06-01 17:34:07 1Wr7lv-0000CU-OZ <= <> R=1Wr7lv-0000CR-G0 U=Debian-exim P=local S=108065 2014-06-01 17:34:09 1Wr7lv-0000CU-OZ => [email protected] R=dnslookup T=remote_smtp H=aspmx.l.google.com [74.125.136.26] X=TLS1.0:RSA_ARCFOUR_SHA1:16 DN="C=US,ST=California,L=Mountain View,O=Google Inc,CN=mx.google.com" 2014-06-01 17:34:09 1Wr7lv-0000CU-OZ Completed 

我们想要达到什么

我们现在想在java web应用程序中显示(或者通知 – 不pipe)他/她想要发送的电子邮件的每个java web应用程序用户实际上没有发送。
(切记:退回电子邮件目前发回到[email protected]不是 Web应用程序用户的电子邮件地址 – 所以这些用户不知道他们的电子邮件没有得到发送)

我们现在有一个基本的(简单的)想法:
使exim尽快将每个失败消息的id (在上述情况下为484648301.663.1401636847496.JavaMail.webapp@node1 )写入本地正在运行的PostgreSQL数据库中,我们知道消息发送失败(或者:尽快相应的退回消息被生成和/或发送)。
如果这是可能的,我们可以很容易地从我们的Java Web应用程序中的数据库中读取这些“失败”的id ,并且 – voilà! – 我们可以向用户显示他们的信息已被反弹。
简单的想法,不是吗?

为了更好地理解:在JavaMail API将消息发送到exim 之前 ,已经为java web应用程序中的每个消息生成了所提及的(唯一的) id ,所以我们可以确定我们的java web应用程序知道每个消息的每个id当然也知道哪个用户发出了特定的消息)。

可能的解决scheme?

我做了一些关于如何解决这个问题的研究,这是我现在提出来的(如果我错了,请纠正一下,这些只是我现在的想法):

方法1:pipe道失败的消息,它的id到bash脚本。 这样一个bash脚本就可以很容易地将id存储在本地的PostgreSQL数据库中。 这是可能的,怎么能这样做呢? 我看到的问题是,一旦消息失败,没有其他的exim路由器被执行 – 这意味着我不能写一个处理失败的消息的路由器 – 失败的消息永远不会到达路由器。 还是我错了?

方法2:我正在考虑的另一种可能性是没有看到失败的原始消息,而是新产生的退回消息。 据我所知,这个新的反弹信息也是通过所有的路由器,直到接受它。 所以,也许我们可以写一个路由器,检查消息是否是退回消息,然后将其pipe理到bash脚本? 但是,我怎么知道,如果一条消息是一个退回消息? 它有一个特殊的标题或其他重要的东西吗? (请记住:因为我们只使用exim 传出电子邮件,所以我们可以确定这将始终是由我们的Java Web应用程序用户“生成”的反弹信息)。

方法3:与方法2相似,但是不用编写路由器,我们可以使用系统filter,它会查看退回消息,然后执行命令/pipe道。

方法4:是否可以configurationerror_copy ,甚至是error_to不是一个电子邮件地址,而是一个pipe道到shell脚本(这当然收到了id和/或整个失败的消息)?

还有两件事:似乎exim也可以直接与数据库直接对话(我在某处读过) – 但我不知道如何利用这个来解决我描述的问题。 Plus:不过这个问题已经解决了,我不想修改任何configuration文件,当通过apt-get更新exim(我不想在每次更新后重新configurationexim)时,这些configuration文件被覆盖。

你需要帮助

你将如何解决这个问题(保存失败的消息id到数据库中)? 请纠正我,如果我错了某处 – 我没有深入进入eximconfiguration。 另外,正如你所看到的,我所考虑的解决scheme更多的是理论上的 – 如果有人能够向我展示真实世界的configuration以及在何处放置这些configuration来使它们工作,那将是非常好的。

非常感谢您的帮助!

Exim版本4.82.1有一个叫做TPDA(Transport Post Delivery Action)的function,它可以让你做到你想要的。 你应该能够切换你的资料库来源并安装那个更新版本的exim,它会给你一个具有这个能力的版本。

如果你坚持使用目前提供的发行版本,我认为这些都不是很好的select。

  1. 最好的解决scheme是为每个消息创build一个唯一的发件人。 例如,拿到收件人,追加时间,然后创build一个MD5散列,并使其成为sender @ support.example.com。 把这些信息放在一个表格中,包括发送给谁的信息以及发送电子邮件的人。 处理收件人到域support.example.com的电子邮件时,如果收件人完全匹配其中一个散列,则可以执行查找以查看是谁发送的,通知发件人,禁止将来发送的收件人等。
  2. 下一种方法是将支持@电子邮件地址发送到两个地方:支持人员可以阅读的邮箱,第二个是您编写的脚本,它可以确定谁发送了邮件,通知发件人并禁用了收件人从未来派遣等等
  3. 你没有build议的一个select是编写一个应用程序来追踪你的日志文件,parsing这些行并提取交付信息并把它放在数据库中,然后通知问题的发送者,并且禁止接收者将来发送等等。