我正在尝试使用 postfix 从电子邮件的“收件人”或“抄送”字段中完全删除特定收件人,但还没有弄清楚如何操作。
我将 postfix 配置为中继主机。我使用它从 Office 365 上的 Exchange 中继到smtp.gmail.com
。这是为了允许特定用户从他们的 Office 365 帐户通过他们的旧gmail.com
电子邮件地址发送邮件。我们在 Exchange 中设置了一个出站连接器以路由到 postfix 中继服务器,并设置了一条规则以将此用户的出站邮件发送到该连接器。
然后设置 Postfix 中继以使用正常 SMTP AUTH 将邮件中继到smtp.gmail.com
。
这一切都很完美。假设用户的 gmail 是[email protected]
,他们的 exchange 邮箱是[email protected]
。要发送他们的 '[电子邮件保护]'邮件到他们的 Office 365 帐户,我们在 gmail 中设置了一个简单的转发器,将所有邮件转发给 user@ domain.com
。
我们正在尝试改进的一个问题是,如果用户使用 Outlook 回复 Exchange 收件箱中的任何转发邮件,他们的[email protected]
地址将显示为“收件人”。由于原始邮件已发送到他们的user@gmail address
,并且该邮件随后被转发到[email protected]
,因此 Outlook 连接到 会[email protected]
认为他们的 gmail 地址是另一个需要回复的用户。我不知道有什么方法可以阻止 Outlook 这样做。
为了防止他们不断给自己发邮件,我们只想在 postfix 中继中使用一条简单规则,将他们自己从“收件人”(或“抄送”)字段中删除。我在 main.cf 中为收件人设置了一条规范规则:
recipient_canonical_maps = hash:/etc/postfix/recipient_canonical
然后我尝试用[email protected]
...某种东西来替换规范的 ap,以便将其从电子邮件收件人中彻底删除。
我可以得到所需的重写以匹配[email protected]
收件人字段,但我无论如何也想不出一个哈希或正则表达式规则(如果我切换到正则表达式映射),它将消除电子邮件地址。我尝试过空白,当我尝试创建数据库时,postmap 会抱怨这不是有效key whitespace value
条目。有人使用规则从电子邮件中完全删除特定收件人吗?
请注意,我也在 reddit 上交叉发布了此内容,因为我在任何地方都找不到与删除(而不仅仅是重写)收件人相关的任何内容:https://www.reddit.com/r/postfix/comments/128plg8/using_postfix_address_rewriting_to_entirely/
答案1
对于那些感兴趣的人,我的解决方案是创建一个队列后内容过滤器,调用该过滤器[email protected]
从邮件的收件人以及邮件中的“收件人:”和“抄送:”电子邮件标头中删除地址。前者是必需的,这样我们就不会向发送实际的电子邮件[email protected]
,而后者只是为了[email protected]
在电子邮件到达收件人时不会出现在这些标头中。
master.cf 中添加的内容:
myhook unix - n n - - pipe
flags=F user=filter argv=/etc/postfix/mail-cleaner/mail-cleaner.php ${sender} ${size} ${recipient}
smtp inet n - - - - smtpd
-o content_filter=myhook:dummy
还有 mail-cleaner.php,它使用 ZBateson MailMimeParser 来干净地修改电子邮件标题。
#!/usr/bin/php
<?php
require_once 'vendor/autoload.php';
use ZBateson\MailMimeParser\MailMimeParser;
use ZBateson\MailMimeParser\Message;
use ZBateson\MailMimeParser\Header\Part\HeaderPartFactory;
use ZBateson\MailMimeParser\Header\Part\MimeLiteralPartFactory;
use ZBateson\MailMimeParser\Header\HeaderFactory;
use ZBateson\MailMimeParser\Header\AddressHeader;
use ZBateson\MailMimeParser\Header\Consumer\ConsumerService;
use ZBateson\MbWrapper\MbWrapper;
$removeAddresses = ["[email protected]", "[email protected]"];
$fileOut = fopen("/tmp/postfix/postfixtest-".date("Y-m-d"), "a");
fwrite($fileOut, "==============================================\n");
fwrite($fileOut, "New running at ".date("Y-m-d H:i:s")."\n");
fwrite($fileOut, "Receipients to remove: ".implode(",",$removeAddresses)."\n");
// Print the command line argument
for ($i = 1; $i < count($argv); $i++) {
fwrite($fileOut, " Arg $i: " . $argv[$i] . "\n");
}
// Get receipients
$args = $argv; // Copy all command line arguments to $args array
$from = $argv[1];
array_shift($args); // Remove first argument which is the script name
array_shift($args); // Remove second argument
$recepients = array_slice($args, 1); // Copy the third and all the rest of command line arguments into $result array
fwrite($fileOut, "From: ".$from."\nReceipients: ".implode(",",$recepients)."\n");
// Remove receipients
foreach ($recepients as $key => $value) {
foreach ($removeAddresses as $remove) {
if (strpos($value, $remove) !== false) {
fwrite($fileOut, " Removing: ".$recepients[$key]."\n");
unset($recepients[$key]);
}
}
}
fwrite($fileOut, " -> New recipients: ".implode(",",$recepients)."\n");
// Read the email from stdin
$email = stream_get_contents(STDIN);
// Parse the email using MailMimeParser
$parser = new MailMimeParser();
$message = $parser->parse($email, true);
fwrite($fileOut,"Message-ID: ".$message->getHeader("Message-ID")->getRawValue()."\nSubject: ".$message->getHeader("Subject")->getRawValue()."\n");
$mbWrapper = new MbWrapper();
$headerPartFactory = new HeaderPartFactory($mbWrapper);
$mimeLiteralPartFactory = new MimeLiteralPartFactory($mbWrapper);
$consumerService = new ConsumerService($headerPartFactory, $mimeLiteralPartFactory);
$headerFactory = new HeaderFactory($consumerService, $mimeLiteralPartFactory);
// Get the "To" header
removeAddress($consumerService, $message, "To", $removeAddresses, $fileOut);
removeAddress($consumerService, $message, "Cc", $removeAddresses, $fileOut);
// Send the modified email using sendmail
$sendmailPath = '/usr/sbin/sendmail'; // or wherever your sendmail binary is located
$sendmailArgs = '-G -i '.implode(' ',$recepients);
fwrite($fileOut,"Calling Sendmail: ".$sendmailPath." ".$sendmailArgs."\n");
$process = proc_open("$sendmailPath $sendmailArgs", [
0 => ['pipe', 'r'], // stdin
1 => ['pipe', 'w'], // stdout
2 => ['pipe', 'w'], // stderr
], $pipes);
if (is_resource($process)) {
$message->save($pipes[0]);
fclose($pipes[0]);
$stdout = stream_get_contents($pipes[1]);
fclose($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
fclose($pipes[2]);
$exitCode = proc_close($process);
if ($exitCode !== 0) {
$err = "ERROR: Sendmail failed with exit code $exitCode:\n$stdout\n$stderr";
fwrite($fileOut,$err."\n");
throw new Exception($err);
}
} else {
$err = "Failed to start sendmail process";
fwrite($fileOut,$err."\n");
throw new Exception($err);
}
fwrite($fileOut,"-> Success\n");
fclose($fileOut);
exit(0);
// Function to remove values from header
function removeAddress($consumerService, $message, $fieldName, $removeAddresses, $fileOut) {
$header = $message->getHeader($fieldName);
if ($header === null) {
fwrite($fileOut,$fieldName ." is empty\n");
return;
}
if ($header instanceof AddressHeader) {
$addresses = $header->getAddresses();
fwrite($fileOut, $fieldName.": ".implode(",", $addresses)."\n");
foreach ($addresses as $key => $value) {
foreach ($removeAddresses as $remove) {
if (strpos($value, $remove) !== false) {
fwrite($fileOut, " Removing: ".$addresses[$key]."\n");
unset($addresses[$key]);
}
}
}
$message->removeHeader($fieldName);
if (count($addresses) > 0) {
$newHeader = new AddressHeader($consumerService,$fieldName,implode(",",$addresses));
$message->setRawHeader($fieldName, $newHeader->getRawValue());
fwrite($fileOut, " -> New ".$fieldName.": ".$message->getHeader($fieldName)->getRawValue()."\n");
}
else {
fwrite($fileOut, " -> ".$fieldName." is now empty\n");
}
return;
}
throw new Exception($fieldName." header invalid");
}
?>