随笔- 22  文章- 1  评论- 13 

Imports System
Imports System.Text
Imports System.IO
Imports System.Net
Imports System.Net.Sockets
Imports System.Collections


Namespace MailList.Data

    Public Class ESmtpMail

#Region "内部变量"

        '设定换行及回车
        Private _enter As String = vbTab & vbCr

        ' 设定语言代码,默认设定为GB2312,如不需要可设置为""
        Private _Charset As String = "GB2312"

        ' 发件人地址
        Private _From As String

        ' 发件人姓名
        Private _FromName As String = "aaa"

        ' 回复邮件地址
        'public string ReplyTo="";

        ' 收件人姓名
        Private _RecipientName As String = ""

        ' 收件人列表
        Private _Recipient As New Hashtable

        ' 邮件服务器域名
        Private _mailserver As String = ""

        ' 邮件服务器端口号
        Private _mailserverport As Integer = 25

        ' SMTP认证时使用的用户名
        Private _username As String = ""

        ' SMTP认证时使用的密码
        Private _password As String = ""

        ' 是否需要SMTP验证
        Private _ESmtp As Boolean = False

        ' 是否Html邮件
        Private _Html As Boolean = False

        ' 邮件附件列表
        Private _Attachments As System.Collections.ArrayList

        ' 邮件发送优先级,可设置为"High","Normal","Low"或"1","3","5"
        Private _priority As String = "Normal"

        ' 邮件主题
        Private _Subject As String = ""

        ' 邮件正文
        Private _Body As String = ""

        ' 收件人数量
        Private _RecipientNum As Integer = 0

        ' 最多收件人数量
        Private _recipientmaxnum As Integer = 1

        ' 密件收件人数量
        'private int RecipientBCCNum=0;

        ' 错误消息反馈
        Private _errmsg As String

        ' TcpClient对象,用于连接服务器
        Private _tc As TcpClient

        ' NetworkStream对象
        Private _ns As NetworkStream

        ' SMTP错误代码哈希表
        Private _ErrCodeHT As New Hashtable

        ' SMTP正确代码哈希表
        Private _RightCodeHT As New Hashtable

        ' 服务器交互记录
        Private _logs As String = ""
#End Region


        '构造函数
        Public Sub New()
            _Attachments = New System.Collections.ArrayList
        End Sub

        '公共属性
        '- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        '邮件服务器域名和验证信息
        '形如:"user:pass@www.server.com:25",也可省略次要信息。
        '如"user:pass@www.server.com"或"www.server.com"
        Public WriteOnly Property MailDomain() As String
            Set(ByVal Value As String)
                Dim maidomain As String = Value.Trim()
                Dim tempint As Integer

                If maidomain <> "" Then
                    tempint = maidomain.IndexOf("@")
                    If tempint <> -1 Then
                        Dim str As String = maidomain.Substring(0, tempint)
                        MailServerUserName = str.Substring(0, str.IndexOf(":"))
                        MailServerPassWord = str.Substring(str.IndexOf(":") + 1, str.Length - str.IndexOf(":") - 1)
                        maidomain = maidomain.Substring(tempint + 1, maidomain.Length - tempint - 1)
                    End If

                    tempint = maidomain.IndexOf(":")
                    If tempint <> -1 Then
                        _mailserver = maidomain.Substring(0, tempint)
                        _mailserverport = System.Convert.ToInt32(maidomain.Substring(tempint + 1, maidomain.Length - tempint - 1))
                    Else
                        _mailserver = maidomain
                    End If
                End If
            End Set
        End Property

        ' 邮件服务器端口号
        Public WriteOnly Property MailDomainPort() As Integer
            Set(ByVal Value As Integer)
                _mailserverport = Value
            End Set
        End Property

        ' SMTP认证时使用的用户名
        Public WriteOnly Property MailServerUserName() As String
            Set(ByVal Value As String)
                If Value.Trim() <> "" Then
                    _username = Value.Trim()
                    _ESmtp = True
                Else
                    _username = ""
                    _ESmtp = False
                End If
            End Set
        End Property

        ' SMTP认证时使用的密码
        Public WriteOnly Property MailServerPassWord() As String
            Set(ByVal Value As String)
                _password = Value
            End Set
        End Property

        ' 邮件发送优先级,可设置为"High","Normal","Low"或"1","3","5"
        Public WriteOnly Property Priority() As String
            Set(ByVal Value As String)
                Select Case Value.ToLower()
                    Case "high"
                        _priority = "High"

                    Case "1"
                        _priority = "High"

                    Case "normal"
                        _priority = "Normal"

                    Case "3"
                        _priority = "Normal"

                    Case "low"
                        _priority = "Low"

                    Case "5"
                        _priority = "Low"

                    Case Else
                        _priority = "Normal"
                End Select
            End Set
        End Property

        ' 错误消息反馈
        Public ReadOnly Property ErrorMessage() As String
            Get
                Return _errmsg
            End Get
        End Property

        ' 服务器交互记录,如发现本组件不能使用的SMTP服务器,
        '请将出错时的Logs发给我(lion-a@sohu.com),我将尽快查明原因。
        Public ReadOnly Property Logs() As String
            Get
                Return _logs
            End Get
        End Property

        ' 最多收件人数量
        Public WriteOnly Property RecipientMaxNum() As Integer
            Set(ByVal Value As Integer)
                _recipientmaxnum = Value
            End Set
        End Property

        '邮件主题
        Public WriteOnly Property MailSubject() As String
            Set(ByVal Value As String)
                _Subject = Value
            End Set
        End Property

        '邮件正文
        Public WriteOnly Property MailBody() As String
            Set(ByVal Value As String)
                _Body = Value
            End Set
        End Property

        '是否HTML类型邮件
        Public WriteOnly Property IsHTML() As Boolean
            Set(ByVal Value As Boolean)
                _Html = Value
            End Set
        End Property

        '是否需要验证
        Public WriteOnly Property ESmtp() As Boolean
            Set(ByVal Value As Boolean)
                _ESmtp = Value
            End Set
        End Property

        '发件人地址
        Public WriteOnly Property From() As String
            Set(ByVal Value As String)
                _From = Value
            End Set
        End Property

        '发件人姓名
        Public WriteOnly Property FromName() As String
            Set(ByVal Value As String)
                _FromName = Value
            End Set
        End Property

        '公共方法
        '- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        ' 添加邮件附件
        ' <param name="path">附件绝对路径</param>
        Public Sub AddAttachment(ByVal path As String)
            _Attachments.Add(path)
        End Sub

        ' 添加一个收件人
        ' str参数为收件人地址
        Public Overloads Function AddRecipient(ByVal str As String) As Boolean
            str = str.Trim()
            If str Is Nothing Or str = "" Or str.IndexOf("@") = -1 Then
                Return True
            End If
            If _RecipientNum < _recipientmaxnum Then
                _Recipient.Add(_RecipientNum, str)
                _RecipientNum += 1
                Return True
            Else
                _errmsg += "收件人过多"
                Return False
            End If
        End Function

        ' 添加一组收件人(不超过recipientmaxnum个),参数为字符串数组
        ' 参数str是保存有收件人地址的字符串数组(不超过recipientmaxnum个)
        Public Overloads Function AddRecipient(ByVal str() As String) As Boolean
            Dim i As Integer
            For i = 0 To str.Length - 1
                If Not AddRecipient(str(i)) Then
                    Return False
                End If
            Next i
            Return True
        End Function

        ' 发送邮件方法,所有参数均通过属性设置。
        Public Overloads Function Send() As Boolean
            If _Recipient.Count = 0 Then
                _errmsg = "收件人列表不能为空"
                Return False
            End If

            If _mailserver.Trim() = "" Then
                _errmsg = "必须指定SMTP服务器"
                Return False
            End If

            Return SendEmail()
        End Function

        ' 发送邮件方法
        ' <param name="smtpserver">smtp服务器信息,如"username:password@www.smtpserver.com:25",也可去掉部分次要信息,如" www.smtpserver.com"</param>
        Public Overloads Function Send(ByVal smtpserver As String) As Boolean
            MailDomain = smtpserver
            Return Send()
        End Function

        ' 发送邮件方法
        ' <param name="smtpserver">smtp服务器信息,如"username:password@www.smtpserver.com:25",也可去掉部分次要信息,如" www.smtpserver.com"</param>
        ' <param name="from">发件人mail地址</param>
        ' <param name="fromname">发件人姓名</param>
        ' <param name="to">收件人地址</param>
        ' <param name="toname">收件人姓名</param>
        ' <param name="html">是否HTML邮件</param>
        ' <param name="subject">邮件主题</param>
        ' <param name="body">邮件正文</param>
        Public Overloads Function Send(ByVal smtpserver As String, ByVal from As String, _
        ByVal fromname As String, ByVal [to] As String, ByVal toname As String, _
        ByVal html As Boolean, ByVal subject As String, ByVal body As String) As Boolean
            MailDomain = smtpserver
            from = from
            fromname = fromname
            AddRecipient([to])
            _RecipientName = toname
            html = html
            subject = subject
            body = body
            Return Send()
        End Function

        '私有方法
        ' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        ' SMTP回应代码哈希表
        Private Sub SMTPCodeAdd()
            _ErrCodeHT.Add("500", "邮箱地址错误")
            _ErrCodeHT.Add("501", "参数格式错误")
            _ErrCodeHT.Add("502", "命令不可实现")
            _ErrCodeHT.Add("503", "服务器需要SMTP验证")
            _ErrCodeHT.Add("504", "命令参数不可实现")
            _ErrCodeHT.Add("421", "服务未就绪,关闭传输信道")
            _ErrCodeHT.Add("450", "要求的邮件操作未完成,邮箱不可用(例如,邮箱忙)")
            _ErrCodeHT.Add("550", "要求的邮件操作未完成,邮箱不可用(例如,邮箱未找到,或不可访问)")
            _ErrCodeHT.Add("451", "放弃要求的操作;处理过程中出错")
            _ErrCodeHT.Add("551", "用户非本地,请尝试<forward-path>")
            _ErrCodeHT.Add("452", "系统存储不足,要求的操作未执行")
            _ErrCodeHT.Add("552", "过量的存储分配,要求的操作未执行")
            _ErrCodeHT.Add("553", "邮箱名不可用,要求的操作未执行(例如邮箱格式错误)")
            _ErrCodeHT.Add("432", "需要一个密码转换")
            _ErrCodeHT.Add("534", "认证机制过于简单")
            _ErrCodeHT.Add("538", "当前请求的认证机制需要加密")
            _ErrCodeHT.Add("454", "临时认证失败")
            _ErrCodeHT.Add("530", "需要认证")

            _RightCodeHT.Add("220", "服务就绪")
            _RightCodeHT.Add("250", "要求的邮件操作完成")
            _RightCodeHT.Add("251", "用户非本地,将转发向<forward-path>")
            _RightCodeHT.Add("354", "开始邮件输入,以<enter>.<enter>结束")
            _RightCodeHT.Add("221", "服务关闭传输信道")
            _RightCodeHT.Add("334", "服务器响应验证Base64字符串")
            _RightCodeHT.Add("235", "验证成功")
        End Sub

        ' 将字符串编码为Base64字符串
        ' <param name="estr">要编码的字符串</param>
        Private Function Base64Encode(ByVal str As String) As String
            Dim barray() As Byte
            barray = Encoding.Default.GetBytes(str)
            Return Convert.ToBase64String(barray)
        End Function

        ' 将Base64字符串解码为普通字符串
        ' <param name="dstr">要解码的字符串</param>
        Private Function Base64Decode(ByVal str As String) As String
            Dim barray() As Byte
            barray = Convert.FromBase64String(str)
            Return Encoding.Default.GetString(barray)
        End Function

        ' 得到上传附件的文件流
        ' <param name="FilePath">附件的绝对路径</param>
        Private Function GetStream(ByVal FilePath As String) As String
            '建立文件流对象
            Dim FileStr As New System.IO.FileStream(FilePath, System.IO.FileMode.Open)
            Dim by(System.Convert.ToInt32(FileStr.Length)) As Byte
            FileStr.Read(by, 0, by.Length)
            FileStr.Close()
            Return System.Convert.ToBase64String(by)
        End Function

        ' 发送SMTP命令
        Private Function SendCommand(ByVal str As String) As Boolean
            Dim WriteBuffer() As Byte
            If str Is Nothing Or str.Trim() = "" Then
                Return True
            End If
            _logs += str
            WriteBuffer = Encoding.Default.GetBytes(str)
            Try
                _ns.Write(WriteBuffer, 0, WriteBuffer.Length)
            Catch
            End Try
            Return True
        End Function

        ' 接收SMTP服务器回应
        Private Function RecvResponse() As String
            Dim StreamSize As Integer
            Dim ReturnValue As String = ""
            Dim ReadBuffer(1024) As Byte
            Try
                StreamSize = _ns.Read(ReadBuffer, 0, ReadBuffer.Length)
            Catch
            End Try

            If StreamSize = 0 Then
                Return ReturnValue
            Else
                ReturnValue = Encoding.Default.GetString(ReadBuffer).Substring(0, StreamSize)
                _logs += ReturnValue
                Return ReturnValue
            End If
        End Function

        ' 与服务器交互,发送一条命令并接收回应。
        ' <param name="Command">一个要发送的命令</param>
        ' <param name="errstr">如果错误,要反馈的信息</param>
        Private Overloads Function Dialog(ByVal str As String, ByVal errstr As String) As Boolean
            If str Is Nothing Or str.Trim() = "" Then
                Return True
            End If
            If SendCommand(str) Then
                Dim RR As String = RecvResponse()
                If RR = "false" Then
                    Return False
                End If
                Dim RRCode As String = RR.Substring(0, 3)
                If Not (_RightCodeHT(RRCode) Is Nothing) Then
                    Return True
                Else
                    If Not (_ErrCodeHT(RRCode) Is Nothing) Then
                        _errmsg += RRCode + _ErrCodeHT(RRCode).ToString()
                        _errmsg += _enter
                    Else
                        _errmsg += RR
                    End If
                    _errmsg += errstr
                    Return False
                End If
            Else
                Return False
            End If
        End Function

        ' 与服务器交互,发送一组命令并接收回应。
        Private Overloads Function Dialog(ByVal str() As String, ByVal errstr As String) As Boolean
            Dim i As Integer
            For i = 0 To str.Length - 1
                If Not Dialog(str(i), "") Then
                    _errmsg += _enter
                    _errmsg += errstr
                    Return False
                End If
            Next i
            Return True
        End Function

        Private Function SendEmail() As Boolean
            '连接网络
            Try
                _tc = New TcpClient(_mailserver, _mailserverport)
            Catch e As Exception
                _errmsg = e.ToString()
                Return False
            End Try

            _ns = _tc.GetStream()
            SMTPCodeAdd()

            '验证网络连接是否正确
            If _RightCodeHT(RecvResponse().Substring(0, 3)) Is Nothing Then
                _errmsg = "网络连接失败"
                Return False
            End If


            Dim SendBuffer() As String
            Dim SendBufferstr As String

            '进行SMTP验证
            If _ESmtp Then
                SendBuffer = New [String](4) {}
                SendBuffer(0) = "EHLO " + _mailserver + _enter
                SendBuffer(1) = "AUTH LOGIN" + _enter
                SendBuffer(2) = Base64Encode(_username) + _enter
                SendBuffer(3) = Base64Encode(_password) + _enter
                If Not Dialog(SendBuffer, "SMTP服务器验证失败,请核对用户名和密码。") Then
                    Return False
                End If
            Else
                SendBufferstr = "HELO " + _mailserver + _enter
                If Not Dialog(SendBufferstr, "") Then
                    Return False
                End If
            End If
            '
            SendBufferstr = "MAIL FROM:<" + _From + ">" + _enter
            If Not Dialog(SendBufferstr, "发件人地址错误,或不能为空") Then
                Return False
            End If
            '
            SendBuffer = New String(_recipientmaxnum) {}
            Dim i As Integer
            For i = 0 To _Recipient.Count - 1

                SendBuffer(i) = "RCPT TO:<" + _Recipient(i).ToString() + ">" + _enter
            Next i

            If Not Dialog(SendBuffer, "收件人地址有误") Then
                Return False
            End If

            SendBufferstr = "DATA" + _enter
            If Not Dialog(SendBufferstr, "") Then
                Return False
            End If
            SendBufferstr = "From:" + _FromName + "<" + _From + ">" + _enter

            SendBufferstr += "To:=?" + _Charset.ToUpper() + "?B?" + Base64Encode(_RecipientName) + "?=" + "<" + _Recipient(0) + ">" + _enter
            SendBufferstr += "CC:"

            Dim k As Integer
            For k = 0 To _Recipient.Count - 1
                SendBufferstr += _Recipient(i).ToString() + "<" + _Recipient(i).ToString() + ">,"
            Next k
            SendBufferstr += _enter


            If _Charset = "" Then
                SendBufferstr += "Subject:" + _Subject + _enter
            Else
                SendBufferstr += "Subject:" + "=?" + _Charset.ToUpper() + "?B?" + Base64Encode(_Subject) + "?=" + _enter
            End If

            SendBufferstr += "X-Priority:" + _priority + _enter
            SendBufferstr += "X-MSMail-Priority:" + _priority + _enter
            SendBufferstr += "Importance:" + _priority + _enter
            SendBufferstr += "X-Mailer: Huolx.Pubclass" + _enter
            SendBufferstr += "MIME-Version: 1.0" + _enter

            SendBufferstr += "Content-Type: multipart/mixed;" + _enter '内容格式和分隔符
            SendBufferstr += "   boundary=""----=_NextPart_000_00D6_01C29593.AAB31770""" + _enter
            SendBufferstr += "------=_NextPart_000_00D6_01C29593.AAB31770" + _enter

            If _Html Then
                SendBufferstr += "Content-Type: text/html;" + _enter
            Else
                SendBufferstr += "Content-Type: text/plain;" + _enter
            End If

            If _Charset = "" Then
                SendBufferstr += "   charset=""iso-8859-1""" + _enter
            Else
                SendBufferstr += "   charset=""" + _Charset.ToLower() + """" + _enter
            End If
            'SendBufferstr += "Content-Transfer-Encoding: base64"+enter;
            SendBufferstr += "Content-Transfer-Encoding: base64" + _enter + _enter

            SendBufferstr += Base64Encode(_Body) + _enter
            If _Attachments.Count <> 0 Then
                Dim filepath As String
                For Each filepath In _Attachments

                    SendBufferstr += "------=_NextPart_000_00D6_01C29593.AAB31770" + _enter
                    SendBufferstr += "Content-Type: application/octet-stream" + _enter
                    SendBufferstr += "   name=""=?" + _Charset.ToUpper() + "?B?" + Base64Encode(filepath.Substring((filepath.LastIndexOf("\") + 1))) + "?=""" + _enter
                    SendBufferstr += "Content-Transfer-Encoding: base64" + _enter
                    SendBufferstr += "Content-Disposition: attachment;" + _enter
                    SendBufferstr += "   filename=""=?" + _Charset.ToUpper() + "?B?" + Base64Encode(filepath.Substring((filepath.LastIndexOf("\") + 1))) + "?=""" + _enter + _enter
                    SendBufferstr += GetStream(filepath) + _enter + _enter
                Next filepath
            End If
            SendBufferstr += "------=_NextPart_000_00D6_01C29593.AAB31770--" + _enter + _enter


            SendBufferstr += _enter + "." + _enter

            If Not Dialog(SendBufferstr, "错误信件信息") Then
                Return False
            End If

            SendBufferstr = "QUIT" + _enter
            If Not Dialog(SendBufferstr, "断开连接时错误") Then
                Return False
            End If

            _ns.Close()
            _tc.Close()
            Return True
        End Function
    End Class
End Namespace

 posted on 2006-11-11 16:13 gamebaby 阅读(196) 评论(0) 编辑 收藏