提交时出现 SMTP 邮件服务器 (sendgrid) 错误

Posted

技术标签:

【中文标题】提交时出现 SMTP 邮件服务器 (sendgrid) 错误【英文标题】:SMTP mail server (sendgrid) error on submission 【发布时间】:2017-02-15 12:17:18 【问题描述】:

我有一个应用程序通过 SMTP 服务器 (sendgrid) 发送带有图像附件的电子邮件。

当应用程序启动时,它会初始化一个 Socket 连接并验证用户(应用程序。我看到从 Sendgrid 返回的以下消息

SG ESMTP service ready at<foo..sendgrid.net

我也得到了成功的身份验证。

但是,在发送电子邮件时,我收到以下 3 个错误:

error 1 550  Unauthenticated senders not allowed
error 2 503 Must have sender before recipient
error 3 503 Must have valid receiver and originator

所以这向我表明我必须在我的 SMTP 模块中更改发件人和收件人的顺序。在内部,我正在接收传入的 ByteArray 并转换为 base64 字符串,以便发送带有附件的电子邮件。 那么我将如何更改以下代码摘录?

        writeUTFBytes ("MAIL FROM: <"+pFrom+">\r\n");
        writeUTFBytes ("RCPT TO: <"+pDest+">\r\n");
        writeUTFBytes ("DATA\r\n");
        writeUTFBytes ("From: "+pFrom+"\r\n");
        writeUTFBytes ("To: "+pDest+"\r\n");

这是我正在使用的整个课程。我在类初始化后只调用一次身份验证,然后在发送带有图像附件的电子邮件时调用 sendAttachedMail。并且在使用本地 SMTP 服务器进行测试时,无需身份验证,一切正常(发送的电子邮件和图像附件)

package org.bytearray.smtp.mailer

    import flash.events.ProgressEvent;
    import flash.net.Socket;
    import flash.utils.ByteArray;
    import flash.utils.getTimer;

    import org.bytearray.smtp.crypto.MD5;
    import org.bytearray.smtp.encoding.Base64;
    import org.bytearray.smtp.events.SMTPEvent;
    import org.bytearray.smtp.infos.SMTPInfos;

    public class SMTPMailer extends Socket 
    
        private var sHost:String;
        private var buffer:Array = new Array();

        // regexp pattern
        private var reg:RegExp = /^\d3/img;

        // PNG, JPEG header values
        private static const PNG:Number = 0x89504E47;
        private static const JPEG:Number = 0xFFD8;

        // common SMTP server response codes
        // other codes could be added to add fonctionalities and more events
        private static const ACTION_OK:Number = 0xFA;
        private static const AUTHENTICATED:Number = 0xEB;
        private static const DISCONNECTED:Number = 0xDD;
        private static const READY:Number = 0xDC;
        private static const DATA:Number = 0x162;
        private static const BAD_SEQUENCE:Number = 0x1F7;

        public function SMTPMailer ( pHost:String, pPort:int) 
        
            super ( pHost, pPort );         
            sHost = pHost;
            addEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler,false,0,true);
        

        public function reset():void
            removeEventListener(ProgressEvent.SOCKET_DATA, socketDataHandler);
        

        /*
        * This method lets you authenticate, just pass a login and password
        */
        public function authenticate ( pLogin:String, pPass:String ):void
        
            writeUTFBytes ("EHLO "+sHost+"\r\n");
            writeUTFBytes ("AUTH LOGIN\r\n");
            writeUTFBytes (Base64.encode64String (pLogin)+"\r\n");
            writeUTFBytes (Base64.encode64String (pPass)+"\r\n");
            flush();
        

        /*
        * This method is used to send emails with attached files and html 
        * takes an incoming Bytearray and convert it to base64 string
        * for instance pass a JPEG ByteArray stream to get a picture attached in the mail ;)
        */
        public function sendAttachedMail ( pFrom:String, pDest:String, pSubject:String, pMess:String, pByteArray:ByteArray, pFileName:String ) :void
        
            try 

                writeUTFBytes ("HELO "+sHost+"\r\n");
                writeUTFBytes ("MAIL FROM: <"+pFrom+">\r\n");
                writeUTFBytes ("RCPT TO: <"+pDest+">\r\n");
                writeUTFBytes ("DATA\r\n");
                writeUTFBytes ("From: "+pFrom+"\r\n");
                writeUTFBytes ("To: "+pDest+"\r\n");
                writeUTFBytes ("Date : "+new Date().toString()+"\r\n");
                writeUTFBytes ("Subject: "+pSubject+"\r\n");
                writeUTFBytes ("Mime-Version: 1.0\r\n");

                var md5Boundary:String = MD5.hash ( String ( getTimer() ) );

                writeUTFBytes ("Content-Type: multipart/mixed; boundary=------------"+md5Boundary+"\r\n");
                writeUTFBytes("\r\n");
                writeUTFBytes ("This is a multi-part message in MIME format.\r\n");
                writeUTFBytes ("--------------"+md5Boundary+"\r\n");
                writeUTFBytes ("Content-Type: text/html; charset=UTF-8; format=flowed\r\n");
                writeUTFBytes("\r\n");
                writeUTFBytes (pMess+"\r\n");
                writeUTFBytes ("--------------"+md5Boundary+"\r\n");
                writeUTFBytes ( readHeader (pByteArray, pFileName) );
                writeUTFBytes ("Content-Transfer-Encoding: base64\r\n");
                writeUTFBytes ("\r\n");

                var base64String:String = Base64.encode64 ( pByteArray, true );

                writeUTFBytes ( base64String+"\r\n");
                writeUTFBytes ("--------------"+md5Boundary+"-\r\n");
                writeUTFBytes (".\r\n");
                flush();

             catch ( pError:Error )
            
                trace("Error : Socket error, please check the sendAttachedMail() method parameters");
                trace("Arguments : " + arguments );     
            
        

        /*
        * This method is used to send HTML emails
        * just pass the HTML string to pMess
        */
        public function sendHTMLMail ( pFrom:String, pDest:String, pSubject:String, pMess:String ):void
        
            try 
                       
                writeUTFBytes ("HELO "+sHost+"\r\n");
                writeUTFBytes ("MAIL FROM: <"+pFrom+">\r\n");
                writeUTFBytes ("RCPT TO: <"+pDest+">\r\n");
                writeUTFBytes ("DATA\r\n");
                writeUTFBytes ("From: "+pFrom+"\r\n");
                writeUTFBytes ("To: "+pDest+"\r\n");
                writeUTFBytes ("Subject: "+pSubject+"\r\n");
                writeUTFBytes ("Mime-Version: 1.0\r\n");
                writeUTFBytes ("Content-Type: text/html; charset=UTF-8; format=flowed\r\n");
                writeUTFBytes("\r\n");
                writeUTFBytes (pMess+"\r\n");
                writeUTFBytes (".\r\n");
                flush();

             catch ( pError:Error )
               
                trace("Error : Socket error, please check the sendHTMLMail() method parameters");
                trace("Arguments : " + arguments ); 
            
        

        /*
        * This method automatically detects the header of the binary stream and returns appropriate headers (jpg, png)
        * classic application/octet-stream content type is added for different kind of files
        */
        private function readHeader ( pByteArray:ByteArray, pFileName:String ):String 
        
            pByteArray.position = 0;

            var sOutput:String = null;

            if ( pByteArray.readUnsignedInt () == SMTPMailer.PNG ) 
            
                sOutput = "Content-Type: image/png; name="+pFileName+"\r\n";
                sOutput += "Content-Disposition: attachment filename="+pFileName+"\r\n";
                return sOutput; 
            

            pByteArray.position = 0;

            if ( pByteArray.readUnsignedShort() == SMTPMailer.JPEG ) 
            
                sOutput = "Content-Type: image/jpeg; name="+pFileName+"\r\n";
                sOutput += "Content-Disposition: attachment filename="+pFileName+"\r\n";
                return sOutput; 
            

            sOutput = "Content-Type: application/octet-stream; name="+pFileName+"\r\n";
            sOutput += "Content-Disposition: attachment filename="+pFileName+"\r\n";

            return sOutput;
        

        // check SMTP response and dispatch proper events
        // Keep in mind SMTP servers can have different result messages the detection can be modified to match some specific SMTP servers
        private function socketDataHandler ( pEvt:ProgressEvent ):void
        
            var response:String = pEvt.target.readUTFBytes ( pEvt.target.bytesAvailable );
            buffer.length = 0;
            var result:Array = reg.exec(response);

            while (result != null)
               
                buffer.push (result[0]);
                result = reg.exec(response);
            

            var smtpReturn:Number = buffer[buffer.length-1];
            var smtpInfos:SMTPInfos = new SMTPInfos ( smtpReturn, response );

            if ( smtpReturn == SMTPMailer.READY ) 
                dispatchEvent ( new SMTPEvent ( SMTPEvent.CONNECTED, smtpInfos ) );

            else if ( smtpReturn == SMTPMailer.ACTION_OK && (response.toLowerCase().indexOf ("queued") != -1 || response.toLowerCase().indexOf ("accepted") != -1 ||
                    response.toLowerCase().indexOf ("qp") != -1) ) dispatchEvent ( new SMTPEvent ( SMTPEvent.MAIL_SENT, smtpInfos ) );
            else if ( smtpReturn == SMTPMailer.AUTHENTICATED ) 
                dispatchEvent ( new SMTPEvent ( SMTPEvent.AUTHENTICATED, smtpInfos ) );
            else if ( smtpReturn == SMTPMailer.DISCONNECTED ) 
                dispatchEvent ( new SMTPEvent ( SMTPEvent.DISCONNECTED, smtpInfos ) );
            else if ( smtpReturn == SMTPMailer.BAD_SEQUENCE ) 
                dispatchEvent ( new SMTPEvent ( SMTPEvent.BAD_SEQUENCE, smtpInfos ) );
            else if ( smtpReturn != SMTPMailer.DATA ) 
                dispatchEvent ( new SMTPEvent ( SMTPEvent.MAIL_ERROR, smtpInfos ) );    
        
    

【问题讨论】:

请注意我正在使用这个 SMTP 库...bytearray.org/?p=27 code.google.com/archive/p/smtpmailer/downloads 如果你可以让它与 SendGrid 一起工作并提供答案,我将奖励 +500 【参考方案1】:

所以这向我表明我必须在我的 SMTP 模块中更改发件人和收件人的顺序。

您的命令顺序正确。相反,您应该查看第一条错误消息以了解问题的真正原因:

 error 1 550  Unauthenticated senders not allowed

这告诉您来自发件人的server requires authentication,即使用带有用户凭据的 SMTP AUTH 命令。尝试从未经身份验证的发件人发送邮件将被拒绝。因此,如果 MAIL FROM 失败,则 RCPT TO 失败。如果RCPT TO 失败,则DATA 失败。以此类推。

使用 SMTP EHLO 命令发现服务器支持的AUTH 方案(和其他功能),然后在发送MAIL FROM 之前发送适当的AUTH 命令。

【讨论】:

是的,我知道,但是当我的应用程序启动时,我可以看到身份验证成功。它是一个 XML 套接字连接。由于只有一个用户(应用程序)进行身份验证,我是否应该在每次发送电子邮件时都重新进行身份验证?我的假设是第一个错误消息与第二个和第三个错误直接相关,即如果我更改收件人,发件人订单正确 error1 应该消失。 @eco_bach:对每个 SMTP 连接(即 TCP 连接)进行 SMTP 身份验证。从您显示的代码中,我根本看不到任何身份验证。而且我也不知道“XML 套接字”是什么意思,SMTP 需要 TCP 套接字。也许您应该展示更多代码,尤其是与 SMTP 服务器的初始连接以及您如何成功通过 SMTP 服务器进行身份验证。 使用这个库 bytearray.org/?p=27 code.google.com/archive/p/smtpmailer/downloads 编辑我的初始帖子 @eco_bach:您的代码根本没有显示错误检查。虽然它实现了一个身份验证功能,但我看不到它被调用,也看不到结果,即身份验证是否成功。我什至根本看不到您从 SMTP 服务器获得响应,即您似乎只是在编写并且从不检查响应。缺少对 SMTP 会话的正确跟踪(即您发送什么,您得到什么)我假设您根本没有进行身份验证或身份验证失败。 这是主要逻辑,不是完整的应用程序代码。 authenticate 和 sendAttachedMail 是从另一个类调用的,我也设置了我的侦听器【参考方案2】:

更新:抱歉,我没有仔细检查你的课程。您已经有了一个身份验证方法,它会做它应该做的事情。所以,很明显,当你认为你正在调用它时,要么它没有被调用,要么它失败了。

所以请仔细检查您正在验证的连接您发送邮件的连接实际上是同一个连接,而且您可能不会在不知不觉中重新创建一个新的、未经身份验证的连接。

此外,您确实需要检查您执行的操作的结果 - 在本例中是身份验证。真的成功了吗?

旧答案

根据您的示例和我找到的少量文档,您需要 - 或至少 看起来 需要 - 两个 身份验证。应该是with the same username and password。

您做对的第一个是“XML 套接字连接”,它将您带到 SMTP 层。

现在您需要第二次身份验证。否则所有命令都将失败,错误将一个接一个地出现,而正如 Steffen Ulrich 所注意到的,“真正的”错误是第一个。

writeUTFBytes ("HELO "+sHost+"\r\n");

// Inner authentication
writeUTFBytes ("AUTH LOGIN\r\n");
writeUTFBytes (Base64.encode64 (username) + "\r\n");
writeUTFBytes (Base64.encode64 (password) + "\r\n");
// HERE you really should read the stream and ensure it says 
// "Authentication OK" -- or something to that effect.

writeUTFBytes ("MAIL FROM: <"+pFrom+">\r\n");

【讨论】:

【参考方案3】:

此行表示错误:

error 1 550  Unauthenticated senders not allowed

可能您必须先在 sendgrid 中注册您的发件人电子邮件地址吗?见https://sendgrid.com/docs/User_Guide/Marketing_Campaigns/senders.html#-Create-Your-Sender-Identity

如果您已经这样做了,电子邮件活动页面会告诉您什么? (见https://sendgrid.com/docs/User_Guide/email_activity.html)

【讨论】:

以上是关于提交时出现 SMTP 邮件服务器 (sendgrid) 错误的主要内容,如果未能解决你的问题,请参考以下文章

使用 smtp 和 Python 发送电子邮件时出现 SSL 错误

尝试通过 Office 365 发送电子邮件时出现 SMTP 5.7.57 错误

从 docker 连接到 office365 SMTP 时出现异常

SMTP 错误:尝试使用 python 和 postfix 发送电子邮件时出现“收件人地址被拒绝”

在 Windows XP 中使用 Indy 10 发送带有内嵌图像的电子邮件时出现“SMTP 传入数据超时”

如何设置outlook office365邮箱的中继