0

我基本上想检查收到的电子邮件中传入的“发件人”,然后要么

  1. 如果电子邮件与指定的 MySQL/PostgreSQL 数据库用户匹配(例如,从存在的用户中选择电子邮件('来自电子邮件地址')),请保留并使其发送到预期邮箱

  2. 如果“发件人”地址为空或在数据库中未找到,则应丢弃该电子邮件

在将电子邮件传递到预期邮箱之前,我有什么办法可以做到这一点?

我正在使用 Procmail + Virtualmin + Webmin + PostgreSQL

PS:我不想将此过滤器应用于 wole 服务器,而是应用于某些指定的邮箱/用户(我假设 1 个用户 = 1 个邮箱)

4

1 回答 1

0

Procmail 可以轻松地在条件下运行外部命令并对其退出状态做出反应。如何让您的特定 SQL 客户端设置其退出代码将取决于您使用的是哪一个;例如,当查询产生空结果集时,它的man页面可能会显示一个选项以使其退出并出现错误?或者编写一个外壳包装器来查找空输出。

一个复杂的问题是 Procmail(或者更确切地说,配套实用程序)可以轻松地从例如标题formail中提取字符串;From:但是您想将其减少到仅电子邮件终端。这是一个很常见的任务,很容易找到一个固定的解决方案——生成一个回复,然后从中提取To:地址(原文如此!)。

FROM=`formail -rtzxTo:`

:0
* FROM ?? ^(one@example\.com|two@site\.example\.net|third@example\.org)$
{
  :0
  * ? yoursql --no-headers --fail-if-empty-result \
      --batch --query databasename \
      --eval "select yada yada where address = '$FROM'"
  { }
  :0E
  /dev/null
}

第一个条件检查变量并在它包含一个地址时成功(我的原始答案只是具有.匹配的正则表达式,如果字符串包含至少一个字符,任何字符;我不相信这实际上是必要或有用的;那里应该是没有办法为From:空的)。如果为真,Procmail 进入大括号;如果没有,它们将被跳过。

大括号内的第一个配方运行一个外部命令并检查其退出代码。我想象您的 SQL 客户端被调用yoursql,并且它具有关闭人类友好格式(表头等)和直接从特定数据库上的命令行运行查询的选项。我们使用双引号,以便 shellFROM在运行此命令之前插入变量(也许有一种更安全的方法来传递可能包含 SQL 注入尝试的字符串变量,--variable from="$FROM"然后在查询中使用该变量?见下文。)

如果没有直接设置退出代码的选项,但是可以确保标准输出在没有结果的情况下完全为空,将命令传递给grep -q .将产生正确的退出代码。在更复杂的情况下,可能会编写一个简单的 Awk 脚本来识别空结果集并相应地设置其退出状态。

从https://www.postgresql.org/docs/current/app-psql.html抓取信息 , 如何在 psql 中使用脚本变量?从 psql和您的问题中生成一个空输出,我最终尝试在以下中实现它psql;但由于我没有要测试的 Postgres 实例,也没有关于您的数据库模式的任何信息,所以这充其量只是近似值。

* ? psql --no-align --tuples-only --quiet \
    --dbname=databasename --username=something --no-password \
    --variable=from="$FROM" \
    --command="select email from users where email = :'from'" \
    | grep -q .

(我们仍然不能在 SQL 查询周围使用单引号,以完全保护它免受 shell 的影响,因为 Postgres 坚持在 周围使用单引号:'from',而 shell 没有提供将文字单引号嵌入单引号内的工具。)

周围的 Procmail 代码应该是不言自明的,但无论如何。在大括号内的第一个配方中,如果条件为真,则其操作行中的空大括号是无操作的;下一个配方上的E标志是一个条件,仅当前一个配方上的任何条件失败时才为真。这是避免使用大量否定的常用习语;也许查一下“德摩根定律”。最终结果是,/dev/null如果第一个配方中的任何一个条件失败,我们就会通过将消息传递给它来丢弃消息;否则,我们只是简单地传递它,Procmail 最终会将它传递到它的默认目的地。

配方已根据您对问题的更新进行了重构;也许现在用前面psql的 a否定退出代码会更有意义:!

FROM=`formail -rtzxTo:`

:0
* FROM ?? ^(one@example\.com|two@site\.example\.net|third@example\.org)$
* ! ? psql --no-align --tuples-only --quiet \
      --dbname=databasename --username=something --no-password \
      --variable=from="$FROM" \
      --command="select email from users where email = :'from'" \
    | grep -q .
/dev/null

切线地,也许注意到 Procmail 的语法如何利用前导?或加倍??在正则表达式中无效的事实。所以解析器可以明确地告诉这些条件不是正则表达式;他们将变量与 之后的正则表达式进行比较??,或分别检查外部命令的退出状态。Procmail 中还有一些其他类似的特殊条件;可以说,所有这些都相当晦涩难懂。

shell 脚本的新手还应该注意到,每个 shell 命令管道都有两个不同的结果:打印在标准输出上的任何内容,以及与此完全分开的退出代码,它显示命令是否成功完成。(通常,零退出状态表示成功,其他任何内容都是错误。例如,grep如果找到至少一个匹配项,则退出状态为 0,如果没有,则通常为其他非零退出代码传入了无效的正则表达式,或者您没有读取输入文件的权限等)

有关更多详细信息,也许还可以参见http://www.iki.fi/era/procmail/,它有一个旧的“迷你常见问题解答”,其中涵盖了这里的几个主题,以及一个用于查找详细信息的“快速参考”句法。

我对 Virtualmin 不熟悉,但https://docs.virtualmin.com/Webmin/PostgreSQL_Database_Server显示了如何设置 Postgres 并按照https://docs.virtualmin.com/Webmin/Procmail_Mail_Filter我猜你会想要使用将此代码放入include文件中的选项。

于 2020-05-01T06:09:20.853 回答