使用 PHP 脚本转发电子邮件

Posted

技术标签:

【中文标题】使用 PHP 脚本转发电子邮件【英文标题】:Forwarding emails with a PHP script 【发布时间】:2011-05-29 02:05:06 【问题描述】:

我们有一个 cron'ed php 脚本,它每十分钟检查一次收件箱。此脚本的目的是为我们提供的 SMS 通知服务处理“停止退出”功能。如果脚本发现任何电子邮件开头带有“STOP”字样的电子邮件,我们会将用户从通知数据库中删除。

为了覆盖我们的基础,我们希望将任何不符合上述条件的电子邮件转发到另一个电子邮件地址(这是一个别名),每小时会有几个人接收和检查。但是,我们在从这个 PHP 脚本转发电子邮件时遇到了问题。

了解了 PHP 的 mail 函数是如何工作的,很明显我们需要在发送邮件之前重新插入标头。但是,MIME 多部分电子邮件总是以乱码的形式发送,包括障碍和任何 base64 编码的附件。

有谁知道使用 PHP 脚本获取电子邮件并正确转发的简单方法?

我们使用内置于 PHP 5 的本地 IMAP 函数。我们还可以访问 PEAR Mail 模块。但是,我们无法通过搜索 Google 找到任何示例或执行类似任务的人。

【问题讨论】:

我的问题仍然没有很好的解决方案。向可以提供概念验证代码或为创建可以通过 PHP 转发电子邮件同时保留 MIME 边界和附件的代码提供良好方向的人添加 +50 赏金。 您使用的是什么 MTA(Postfix、sendmail 等)? 【参考方案1】:

我很久以前就编写过这个方法来使用 IMAP 将电子邮件信息解析成适当的部分:

function Message_Parse($id)

    if (is_resource($this->connection))
    
        $result = array
        (
            'text' => null,
            'html' => null,
            'attachments' => array(),
        );

        $structure = imap_fetchstructure($this->connection, $id, FT_UID);

        if (array_key_exists('parts', $structure))
        
            foreach ($structure->parts as $key => $part)
            
                if (($part->type >= 2) || (($part->ifdisposition == 1) && ($part->disposition == 'ATTACHMENT')))
                
                    $filename = null;

                    if ($part->ifparameters == 1)
                    
                        $total_parameters = count($part->parameters);

                        for ($i = 0; $i < $total_parameters; $i++)
                        
                            if (($part->parameters[$i]->attribute == 'NAME') || ($part->parameters[$i]->attribute == 'FILENAME'))
                            
                                $filename = $part->parameters[$i]->value;

                                break;
                            
                        

                        if (is_null($filename))
                        
                            if ($part->ifdparameters == 1)
                            
                                $total_dparameters = count($part->dparameters);

                                for ($i = 0; $i < $total_dparameters; $i++)
                                
                                    if (($part->dparameters[$i]->attribute == 'NAME') || ($part->dparameters[$i]->attribute == 'FILENAME'))
                                    
                                        $filename = $part->dparameters[$i]->value;

                                        break;
                                    
                                
                            
                        
                    

                    $result['attachments'][] = array
                    (
                        'filename' => $filename,
                        'content' => str_replace(array("\r", "\n"), '', trim(imap_fetchbody($this->connection, $id, ($key + 1), FT_UID))),
                    );
                

                else
                
                    if ($part->subtype == 'PLAIN')
                    
                        $result['text'] = imap_fetchbody($this->connection, $id, ($key + 1), FT_UID);
                    

                    else if ($part->subtype == 'HTML')
                    
                        $result['html'] = imap_fetchbody($this->connection, $id, ($key + 1), FT_UID);
                    

                    else
                    
                        foreach ($part->parts as $alternative_key => $alternative_part)
                        
                            if ($alternative_part->subtype == 'PLAIN')
                            
                                echo '<h2>' . $alternative_part->subtype . ' ' . $alternative_part->encoding . '</h2>';

                                $result['text'] = imap_fetchbody($this->connection, $id, ($key + 1) . '.' . ($alternative_key + 1), FT_UID);
                            

                            else if ($alternative_part->subtype == 'HTML')
                            
                                echo '<h2>' . $alternative_part->subtype . ' ' . $alternative_part->encoding . '</h2>';

                                $result['html'] = imap_fetchbody($this->connection, $id, ($key + 1) . '.' . ($alternative_key + 1), FT_UID);
                            
                        
                    
                
            
        

        else
        
            $result['text'] = imap_body($this->connection, $id, FT_UID);
        

        $result['text'] = imap_qprint($result['text']);
        $result['html'] = imap_qprint(imap_8bit($result['html']));

        return $result;
    

    return false;

我从未对它进行过深入测试,我确信它有一些错误,但这可能是一个开始......修改此代码后,您应该能够使用 $result 索引(text,@ 987654324@, attachments) 与您的转发脚本(例如使用 SwiftMailer),无需担心保持 MIME 边界完整。

【讨论】:

使用您的代码,我能够进行轻微修改,以获得与我现有代码一起使用的“概念证明”脚本。这绝对有助于让我朝着正确的方向前进。谢谢! @mririgo:太好了,很高兴我能帮上忙! =) 仅用于转发电子邮件的代码就相当多,我相信它可以小得多。 :) 虽然我不使用 imap 功能,但我只使用邮件。我知道我的决定的缺点,但我作为机器人而不是我发送。 :) @JamesM:这既不是电子邮件发件人也不是电子邮件转发器,它是一个解析器。关于尺寸:也许......你想试一试吗? 那个好像有很多“html”和“text”之类的神奇常量,它似乎不太可能准确复制所有邮件。【参考方案2】:

这并不是真正的答案,而是对替代方法的建议。我认为,如果您只是将邮件移动到现有帐户中的不同文件夹,它会更简单且更不容易出错(即没有传递问题)。即,cron 运行并处理收件箱中的所有电子邮件。如果找到 STOP,它会执行所需的工作,然后(通过 IMAP 函数)简单地将电子邮件移动到名为“Processed”或类似名称的子文件夹中。否则,它将电子邮件移动到名为“检查这些”或类似名称的子文件夹中。这样您就无需担心转发、进一步投递或第二个帐户,每个人都可以直接监控已处理、未处理和待处理的邮件。

【讨论】:

我们实际上已经在脚本中这样做了。我们为这些 STOP 消息提供了一个“未订阅”文件夹,并为其他所有内容提供了一个“已处理”文件夹。但是,我们需要转发,因为接收端的大多数人不(也不会)知道此电子邮件帐户的密码。 (有少数学生帮助我们不想访问此帐户但收到转发。)【参考方案3】:

您是否了解过使用 Swiftmailer 库的功能?

http://swiftmailer.org/

我过去曾使用过它并取得了很好的效果,尽管不是在您描述的应用程序中,但我已将它用于基于 PHP 的“邮件列表”,我在其中检查主题并发送到适当的组。

但是我创建了一条新消息,没有转发。希望对您有所帮助。

【讨论】:

Swiftmailer 是半途而废的,在一般情况下它不会处理嵌套的多部分。所以它不能用于重新发送来历不明的电子邮件。【参考方案4】:

我以前也遇到过这种情况。为了修复它,我必须在使用 imap_fetchbody() 后在电子邮件正文上执行 imap_base64()。

$body = imap_fetchbody($imap, 1, 1);
$headers = imap_headerinfo($imap, 1);
$body = imap_base64($body);

【讨论】:

是的,imap 对正文进行解码,因此您应该检查原始文件是否已编码并重新编码。 thios 整个 php 扩展只是对“c-client”的绑定,大多数 c-client 的文档都适用。【参考方案5】:

使用 IO 处理程序将电子邮件的内容捕获为字符串,拆分标题,然后使用 php 'mail()' 函数发送。

如果你真的想用 php-imap 来做这个,

php-imap 扩展名是 libc-client,它是 pine 电子邮件客户端软件的一部分 找出使用 pine 执行此操作所需的手动步骤,然后查看 pine 执行此操作的 c-client 调用。这将为您提供 php 所需的步骤。

c-client 文档非常少,访问 pine 源代码是获取使用详细信息的最佳方式。

我想您可能会发现 php 扩展的作者“为了您的方便或保护”省略或更改了阻止此路径的内容。

【讨论】:

以上是关于使用 PHP 脚本转发电子邮件的主要内容,如果未能解决你的问题,请参考以下文章

如何添加php脚本来转发表单的副本

使用 PHP 脚本处理传入的电子邮件

PHP IMAP POP 转发脚本发送以某种方式更改的图像附件

电子邮件管道到 PHP 脚本并转发到另一封电子邮件

PHP 邮件不在“收件人”标题中显示“收件人”

电子邮件转发会删除 Google 应用脚本中的原始发件人电子邮件地址