从原始正文发送邮件以进行测试

Posted

技术标签:

【中文标题】从原始正文发送邮件以进行测试【英文标题】:Send Mail from raw body for testing purposes 【发布时间】:2011-06-29 23:23:17 【问题描述】:

我正在开发一个 php 应用程序,该应用程序需要从电子邮件服务器检索任意电子邮件。然后,消息被完全解析并存储在数据库中。

当然,我必须做很多测试,因为在太阳下所有不同的邮件格式下,这项任务并不是微不足道的。因此,我开始“收集”来自某些客户和不同内容的电子邮件。

我想要一个脚本,以便我可以将这些电子邮件自动发送到我的应用程序以测试邮件处理。

因此,我需要一种发送原始电子邮件的方法 - 以便结构与来自相应客户端的完全相同。我将电子邮件存储为 .eml 文件。

有人知道如何通过提供原始正文来发送电子邮件吗?

编辑: 更具体地说:我正在寻找一种使用其源代码发送多部分电子邮件的方法。例如,我希望能够使用类似的东西(一封带有纯 html 部分的电子邮件,HTML 部分有一个内联附件)。

 --Apple-Mail-159-396126150
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain;

The plain text email!

--=20    

=20
=20

--Apple-Mail-159-396126150
Content-Type: multipart/related;
    type="text/html";
    boundary=Apple-Mail-160-396126150


--Apple-Mail-160-396126150
Content-Transfer-Encoding: quoted-printable
Content-Type: text/html;
    charset=iso-8859-1

<html><head>
    <title>Daisies</title>=20
</head><body style=3D"background-attachment: initial; background-origin: =
initial; background-image: =
url(cid:4BFF075A-09D1-4118-9AE5-2DA8295BDF33/bg_pattern.jpg); =
background-position: 50% 0px; ">

[ - snip - the html email content ]


</body></html>=

--Apple-Mail-160-396126150
Content-Transfer-Encoding: base64
Content-Disposition: inline;
    filename=bg_pattern.jpg
Content-Type: image/jpg;
    x-apple-mail-type=stationery;
    name="bg_pattern.jpg"
Content-Id: <4BFF075A-09D1-4118-9AE5-2DA8295BDF33/tbg.jpg>

/9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHVja3kAAQAEAAAASAAA/+IFOElDQ19QUk9GSUxFAAEB
[ - snip - the image content ]
nU4IGsoTr47IczxmCMvPypi6XZOWKYz/AB42mcaD/9k=

--Apple-Mail-159-396126150--

【问题讨论】:

你找到解决这个问题的办法了吗? 不,很遗憾没有。 我认为您缺少主要部分,即主题标题和多部分部分,因此您的 MTA 将知道该怎么做。因此,您的 .eml 文件不完整。在多部分电子邮件中,根部分(您缺少的部分)必须包含类似“Content-Type: multipart/alternative; boundary="_------------=_MCPart_465790590"”的内容。这将指定首先读取的任何 mimeparser 部分,依此类推。 一些反馈会很好。您尝试了哪些答案,出了什么问题,您是否遇到错误或意外结果?上周有 3 个人试图帮助您,但似乎没有人得到任何信任或回应。如果它不能立即起作用,我们也许可以提供帮助。 【参考方案1】:

使用PHPMailer,可以直接设置消息正文:

$mail->Body = 'the contents of one of your .eml files here'

如果您的邮件包含任何 MIME 附件,这很可能无法正常工作,因为某些 MIME 内容必须进入邮件的标题。您必须按摩 .eml 以提取那些特定的标头并将它们作为自定义标头添加到 PHPMailer 邮件中

【讨论】:

不幸的是,这并没有多大帮助。例如,一个有趣的测试案例是 Apple Mail 中的附件通常具有“内联”处置方式,而大多数其他客户端仅使用“附件”。因此我想发送类似:--Apple-Mail-4-698704782 Content-Disposition: inline; filename="bla.pdf" .... 我知道某些标题元素也需要进入新邮件,但这应该不是什么大问题:只需过滤重要的东西(接收、发件人、发件人、. ..) 并再次使用剩余的标题字段。【参考方案2】:

您可以使用telnet 程序发送这些电子邮件:

$ telnet <host> <port>                  // execute telnet
HELO my.domain.com                      // enter HELO command
MAIL FROM: sender@address.com           // enter MAIL FROM command
RCPT TO: recipient@address.com          // enter RCPT TO command
<past here, without adding a newline>   // enter the raw content of the message
[ctrl]+d                                // hit [ctrl] and d simultaneously to end the message

如果你真的想在 PHP 中做到这一点,你可以使用fsockopen() 或stream_socket_client() 系列。基本上你做同样的事情:直接与邮件服务器交谈。

// open connection
$stream = @stream_socket_client($host . ':' . $port);

// write HELO command
fwrite($stream, "HELO my.domain.com\r\n");

// read response
$data = '';
while (!feof($stream)) 
    $data += fgets($stream, 1024);


// repeat for other steps
// ...

// close connection
fclose($stream);

【讨论】:

是的,我需要在 PHP 中执行此操作。你能详细说明它是如何与 PHP 一起工作的吗?直接与邮件服务器对话时有哪些潜在的陷阱?我可以想象有一些...... 这种方法实际上比使用mail()更可靠。缺点是您需要自己进行一些错误处理,但mail() 失败了,所以也许这根本不是缺点。而且因为你需要它来进行测试,所以你不需要它完美:)【参考方案3】:

您可以使用 PHP 中的内置函数 mail 来实现它。 body 部分不必只是文本,它还可以包含混合部分数据。

请记住,这是一个概念证明。 sendEmlFile 函数可以使用更多检查,例如“文件是否存在”和“是否有边界集”。正如你提到的,它是用于测试/开发的,我没有包含它。

<?php
function sendmail($body,$subject,$to, $boundry='') 
  define ("CRLF", "\r\n");

  //basic settings
  $from = "Example mail<info@example.com>";

  //define headers
  $sHeaders  = "From: ".$from.CRLF;
  $sHeaders .= "X-Mailer: PHP/".phpversion().CRLF;
  $sHeaders .= "MIME-Version: 1.0".CRLF;


  //if you supply a boundry, it will be send with your own data
  //else it will be send as regular html email
    if (strlen($boundry)>0)
        $sHeaders .= "Content-Type: multipart/mixed; boundary=\"".$boundry."\"".CRLF;
    else
    
      $sHeaders .= "Content-type: text/html;".CRLF."\tcharset=\"iso-8859-1\"".CRLF;
      $sHeaders .= "Content-Transfer-Encoding: 7bit".CRLF."Content-Disposition: inline";
    

  mail($to,$subject,$body,$sHeaders);


function sendEmlFile($subject, $to, $filename) 
  $body = file_get_contents($filename);
  //get first line "--Apple-Mail-159-396126150"
  $boundry = $str = strtok($body, "\n");

  sendmail($body,$subject,$to, $boundry);

?>

更新:

经过更多测试后,我发现所有.eml 文件都不同。可能有一个标准,但在导出到.eml 时我有很多选择。我不得不使用单独的工具来创建文件,因为默认情况下使用 Outlook 无法保存到 .eml

您可以下载example of the mail script。它包含两个版本。

简单版有两个文件,一个是index.php文件,发送test.eml文件。这只是一个文件,我在其中粘贴了您在问题中发布的示例代码。

高级版使用我创建的实际.eml 文件发送电子邮件。它将从它自己的文件中获取所需的标题。请记住,这也会设置邮件的ToFrom 部分,因此请更改它以匹配您自己的/服务器设置。

高级代码的工作方式如下:

<?php
function sendEmlFile($filename) 
    //define a clear line
    define ("CRLF", "\r\n");

    //eml content to array.
    $file = file($filename);

    //var to store the headers
    $headers = "";
    $to = "";
    $subject = "";

    //loop trough each line
    //the first part are the headers, until you reach a white line
    while(true) 
        //get the first line and remove it from the file
        $line = array_shift($file);
        if (strlen(trim($line))==0) 
            //headers are complete
            break;
        

        //is it the To header
        if (substr(strtolower($line), 0,3)=="to:") 
            $to = trim(substr($line, 3));
            continue;
        

        //Is it the subject header
        if (substr(strtolower($line), 0,8)=="subject:") 
            $subject = trim(substr($line, 8));
            continue;
        

        $headers .= $line . CRLF;
    

    //implode the remaining content into the body and trim it, incase the headers where seperated with multiple white lines
    $body = trim(implode('', $file));

    //echo content for debugging
    /*
    echo $headers;
    echo '<hr>';
    echo $to;
    echo '<hr>';
    echo $subject;
    echo '<hr>';
    echo $body;
    */

  //send the email
  mail($to,$subject,$body,$headers);



//initiate a test with the test file
sendEmlFile("Test.eml");
?>

【讨论】:

你确定这样可以吗?如我所见,您在 headers 参数中包含了一些 MIME 信息。但是我会使用具有多个主体和附件部分的复杂结构化、多级 MIME 吗?我只是不确定如何拆分它。你能详细说明一下吗? 这就是为什么你在标题中设置boundary。正文的每个部分(html/纯文本/附件/内联图像/等)都用边界分割。从苹果看你自己的例子。 而且您不必拆分它。 EG:您的整个示例(带有`--Apple-Mail-159-396126150`等)将是电子邮件的正文。如果您按原样发送,您将收到一封带有附件的电子邮件。但是,由于您包含的 [snip],附件会失败 如果有任何不清楚的地方或者您需要示例 zip 或其他任何内容,请告诉我。 是的,我真的想要一个可以直接测试的例子。目前我无法真正解决这个问题。【参考方案4】:

你可以从这里开始

http://www.dreamincode.net/forums/topic/36108-send-emails-using-php-smtp-direct/

我不知道这段代码有多好,但它会成为一个起点。

您所做的是直接连接到远程计算机上的端口 25,就像您使用 telnet 一样,并发出 smtp 命令。请参阅例如 http://www.yuki-onna.co.uk/email/smtp.html 了解正在发生的事情(或参阅 Jasper N. Brouwer 的回答)。

【讨论】:

【参考方案5】:

只需制作一个快速的 shell 脚本来处理目录并在需要时调用它,例如使用atcrontab

我在ls /mydir/cat I | awk .. | sendmail -options

http://www.manpagez.com/man/1/awk/

您也可以使用脚本与邮件服务器通信,发送带有模板正文的emls..

【讨论】:

重点是如何批处理 eml 的问题 - 它是电子邮件客户端处理基于 eml 文件的实际发送。 可以用awk拆分emls?

以上是关于从原始正文发送邮件以进行测试的主要内容,如果未能解决你的问题,请参考以下文章

电子邮件正文在文件中发送 [Gmail API, Node.js]

如何从电子邮件正文发送电子邮件?

向自己发送 HTML 电子邮件以测试它们的快速、简单的方法是啥?

从用户输入生成电子邮件正文,从控制器发送电子邮件?

Datafram 实现作为正文发送邮件

EWS 电子邮件正文返回为空?