DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

SMTP認証でメールを送信する

.NET Framework 2.0以降で、SmtpClientクラスを使用する方法

SmtpClientクラスのCredentialsプロパティを使うことにより、ユーザー名とパスワードを指定してメールを送信することができます。

VB.NET
コードを隠すコードを選択
'MailMessageの作成
Dim msg As New System.Net.Mail.MailMessage( _
    "from@xxx.xxx", "to@xxx.xxx", "題名", "本文")

Dim sc As New System.Net.Mail.SmtpClient()
'SMTPサーバーを指定する
sc.Host = "localhost"
'ユーザー名とパスワードを設定する
sc.Credentials = New System.Net.NetworkCredential("user", "pass")
'現在は、EnableSslがtrueでは失敗する
sc.EnableSsl = False
'Timeoutを指定しないと失敗するとの報告もあるようだが、不明
'sc.Timeout = 100000;
'メッセージを送信する
sc.Send(msg)

'後始末
msg.Dispose()
C#
コードを隠すコードを選択
//MailMessageの作成
System.Net.Mail.MailMessage msg = new System.Net.Mail.MailMessage(
    "from@xxx.xxx", "to@xxx.xxx", "題名", "本文");

System.Net.Mail.SmtpClient sc = new System.Net.Mail.SmtpClient();
//SMTPサーバーを指定する
sc.Host = "localhost";
//ユーザー名とパスワードを設定する
sc.Credentials = new System.Net.NetworkCredential("user", "pass");
//現在は、EnableSslがtrueでは失敗する
sc.EnableSsl = false;
//Timeoutを指定しないと失敗するとの報告もあるようだが、不明
//sc.Timeout = 100000;
//メッセージを送信する
sc.Send(msg);

//後始末
msg.Dispose();

SSLを使用するにはEnableSslプロパティをtrueにしますが、現在はCredentialsを設定し、EnableSslをtrueにすると、エラーとなるようです。

対応している認証方法は不明ですが、どうやらLOGINには対応しているようです(ご存知の方は、ぜひお知らせください)。

補足:ユーザー名とパスワードのデフォルト値をアプリケーション構成ファイルに記述しておくこともできます。詳しくは、こちらをご覧ください。

MailMessage.Fieldsプロパティを使う方法

.NET Framework 1.1からは、MailMessageクラスのFieldsプロパティを使用することにより、System.Web.Mail.SmtpMailクラスでもSMTP認証によるメールの送信ができます。この方法は「The Code Project - SMTP Authentication using System.Web.Mail (CDOSYS)」で紹介されています。

VB.NET
コードを隠すコードを選択
Dim mail As New System.Web.Mail.MailMessage
'From
mail.From = "xxx@xxx.xxx"
'To
mail.To = "xxx@xxx.xxx"
'Subject
mail.Subject = "テスト"
'本文
mail.Body = "これはテストです。"

'smtpauthenticateを使う時は必ず2にする
mail.Fields.Item( _
    "http://schemas.microsoft.com/cdo/configuration/sendusing") _
    = 2
'sendusingを2にした時は、必ずサーバーとポートを指定する
'SMTPサーバーを指定
mail.Fields.Item( _
    "http://schemas.microsoft.com/cdo/configuration/smtpserver") _
    = "xxx.xxx.xxx"
'ポート番号を指定する
mail.Fields.Item( _
    "http://schemas.microsoft.com/cdo/configuration/smtpserverport") _
    = 25

'認証を使う
'1はbasic認証、2はNTLM認証
mail.Fields.Item( _
    "http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = 1
'ユーザー名
mail.Fields.Item( _
    "http://schemas.microsoft.com/cdo/configuration/sendusername") _
    = "xxx"
'パスワード
mail.Fields.Item( _
    "http://schemas.microsoft.com/cdo/configuration/sendpassword") _
    = "xxx"

System.Web.Mail.SmtpMail.SmtpServer = "xxx.xxx.xxx"

'メールを送信
System.Web.Mail.SmtpMail.Send(mail)
C#
コードを隠すコードを選択
System.Web.Mail.MailMessage mail = new System.Web.Mail.MailMessage();
//From
mail.From = "xxx@xxx.xxx";
//To
mail.To = "xxx@xxx.xxx";
//Subject
mail.Subject = "テスト";
//本文
mail.Body = "これはテストです。";

//smtpauthenticateを使う時は必ず2にする
mail.Fields[
    "http://schemas.microsoft.com/cdo/configuration/sendusing"]
    = 2;
//sendusingを2にした時は、必ずサーバーとポートを指定する
//SMTPサーバーを指定
mail.Fields[
    "http://schemas.microsoft.com/cdo/configuration/smtpserver"]
    = "xxx.xxx.xxx";
//ポート番号を指定する
mail.Fields[
    "http://schemas.microsoft.com/cdo/configuration/smtpserverport"]
    = 25;

//認証を使う
//1はbasic認証、2はNTLM認証
mail.Fields[
    "http://schemas.microsoft.com/cdo/configuration/smtpauthenticate"]
    = 1;
//ユーザー名
mail.Fields[
    "http://schemas.microsoft.com/cdo/configuration/sendusername"]
    = "xxx";
//パスワード
mail.Fields[
    "http://schemas.microsoft.com/cdo/configuration/sendpassword"]
    = "xxx";

System.Web.Mail.SmtpMail.SmtpServer = "xxx.xxx.xxx";

//メールを送信
System.Web.Mail.SmtpMail.Send(mail);

CDOを使えない環境では、この方法は無理です。その場合は、自分でコードを書くか、誰かが作ったコンポーネントやクラスを使わせてもらうかということになるでしょう。

Socketを使ってSMTPでメールを送る方法

自分でコードを書くということになると、ソケットを使用することになります。この場合はもちろん、SMTPやMIMEに関する知識が必要です。SMTPについてRFCではRFC821(SIMPLE MAIL TRANSFER PROTOCOL)で、さらにESMTPについてはRFC1869(SMTP Service Extensions)などで定義されています。

ここではソケットを使ってSMTPメール送信するサンプルを示します。しかしこれはあくまでただの簡単なサンプルでしかありませんので、何も理解せずに、このコードをそのまま利用することは絶対にしないでください。RFCを読み気がない、あるいは理解できないという方は、Tatsuo Babaさんの「BASP21 DLL」や市販品をご利用ください。

なおこのクラスの使い方に関しては、エントリポイントのSmtpクラスのMainメソッドを参考にしてください。

VB.NET
コードを隠すコードを選択
Imports System
Imports System.Net.Sockets
Imports System.Text
Imports System.Collections.Specialized

Namespace Dobon.Mail
    ''' <summary>
    ''' 送信用のメールメッセージ
    ''' </summary>
    Public Class SmtpMailMessage
        Private _from As String
        ''' <summary>
        ''' 送信者のメールアドレス
        ''' </summary>
        Public Property From() As String
            Get
                Return _from
            End Get
            Set(ByVal Value As String)
                _from = Value
            End Set
        End Property

        Private _to As String
        ''' <summary>
        ''' 送信先のメールアドレス
        ''' </summary>
        Public Property [To]() As String
            Get
                Return _to
            End Get
            Set(ByVal Value As String)
                _to = Value
            End Set
        End Property

        Private _subject As String
        ''' <summary>
        ''' メールの件名
        ''' </summary>
        Public Property Subject() As String
            Get
                Return _subject
            End Get
            Set(ByVal Value As String)
                _subject = Value
            End Set
        End Property

        Private _body As String
        ''' <summary>
        ''' メールの本文
        ''' </summary>
        Public Property Body() As String
            Get
                Return _body
            End Get
            Set(ByVal Value As String)
                _body = Value
            End Set
        End Property
    End Class

    ''' <summary>
    ''' SMTPでメールを送信する
    ''' </summary>
    Public Class Smtp
        ''' <summary>
        ''' エントリポイント
        ''' </summary>
        Public Shared Sub Main()
            Dim mail As New SmtpMailMessage
            'From
            mail.From = "xxx@xxx.xxx"
            'To
            mail.To = "xxx@xxx.xxx"
            'Subject
            mail.Subject = "テスト"
            '本文
            mail.Body = "これはテストです。"

            Dim smtp As New Smtp
            'SMTPサーバー名
            smtp.SmtpServer = "xxx.xxx.xxx"
            'ポート番号
            smtp.SmtpPort = 25
            '認証
            smtp.AuthMethod = SmtpAuthMethod.Plain
            smtp.AuthUserName = "xxxxx"
            smtp.AuthPassword = "xxxxx"
            '送信する
            smtp.Send(mail)

            Console.ReadLine()
        End Sub

        Private _smtpServer As String
        ''' <summary>
        ''' SMTPサーバー
        ''' </summary>
        Public Property SmtpServer() As String
            Get
                Return _smtpServer
            End Get
            Set(ByVal Value As String)
                _smtpServer = Value
            End Set
        End Property

        Private _smtpPort As Integer = 25
        ''' <summary>
        ''' ポート番号
        ''' </summary>
        Public Property SmtpPort() As Integer
            Get
                Return _smtpPort
            End Get
            Set(ByVal Value As Integer)
                _smtpPort = Value
            End Set
        End Property

        Private _authUserName As String
        ''' <summary>
        ''' 認証で用いるユーザー名
        ''' </summary>
        Public Property AuthUserName() As String
            Get
                Return _authUserName
            End Get
            Set(ByVal Value As String)
                _authUserName = Value
            End Set
        End Property

        Private _authPassword As String
        ''' <summary>
        ''' 認証で用いるパスワード
        ''' </summary>
        Public Property AuthPassword() As String
            Get
                Return _authPassword
            End Get
            Set(ByVal Value As String)
                _authPassword = Value
            End Set
        End Property

        ''' <summary>
        ''' SMTPの認証方法
        ''' </summary>
        Public Enum SmtpAuthMethod
            None
            Plain
            Login
        End Enum

        Private _authMethod As SmtpAuthMethod = SmtpAuthMethod.None
        ''' <summary>
        ''' 認証方法
        ''' </summary>
        Public Property AuthMethod() As SmtpAuthMethod
            Get
                Return _authMethod
            End Get
            Set(ByVal Value As SmtpAuthMethod)
                _authMethod = Value
            End Set
        End Property

        Private _stream As NetworkStream
        Private _recMsg As String
        Private boundary As String

        ''' <summary>
        ''' メールを送信する
        ''' </summary>
        ''' <param name="mail">送信するメール</param>
        Public Sub Send(ByVal mail As SmtpMailMessage)
            Dim soc As New TcpClient

            '接続
            soc.Connect(SmtpServer, SmtpPort)
            _stream = soc.GetStream()

            Try
                ReceiveData()
                If Not _recMsg.StartsWith("220") Then
                    Throw New ApplicationException("接続に失敗しました。")
                End If

                SendData("EHLO" + vbCrLf)
                ReceiveData()
                If Not _recMsg.StartsWith("250") Then
                    Throw New ApplicationException(_recMsg)
                End If

                '認証する
                If AuthMethod <> SmtpAuthMethod.None Then
                    Authenticate()
                End If

                SendData(("MAIL FROM:" + mail.From + vbCrLf))
                ReceiveData()
                If Not _recMsg.StartsWith("250") Then
                    Throw New ApplicationException(_recMsg)
                End If

                SendData(("RCPT TO:" + mail.To + vbCrLf))
                ReceiveData()
                If Not _recMsg.StartsWith("250") Then
                    Throw New ApplicationException(_recMsg)
                End If

                SendData("DATA" + vbCrLf)
                ReceiveData()
                If Not _recMsg.StartsWith("354") Then
                    Throw New ApplicationException(_recMsg)
                End If

                '送信データ
                Dim data As String = ""
                data += "From: " + mail.From + vbCrLf
                data += "To: " + mail.To + vbCrLf
                '件名をBase64でエンコード
                data += "Subject: =?ISO-2022-JP?B?" + _
                    GetBase64String(mail.Subject) + "?=" + vbCrLf
                data += "MIME-Version: 1.0" + vbCrLf
                data += "Content-Transfer-Encoding: 7bit" + vbCrLf

                data += "Content-Type: text/plain; charset=ISO-2022-JP" _
                    + vbCrLf

                data += vbCrLf + mail.Body.Replace( _
                    vbCrLf + "." + vbCrLf, vbCrLf + ".." + vbCrLf) _
                    + vbCrLf

                data += "." + vbCrLf

                SendData(data)
                ReceiveData()

                SendData("QUIT" + vbCrLf)
                ReceiveData()
            Finally
                '切断する
                If Not _stream Is Nothing Then
                    _stream.Close()
                End If
                soc.Close()
            End Try
        End Sub

        'データを受信する
        Private Sub ReceiveData()
            Dim enc As Encoding = Encoding.GetEncoding(50220)
            Dim data(1024) As Byte
            Dim len As Integer
            Dim str As String = ""
            Dim ms As New System.IO.MemoryStream

            'すべて受信する
            Do
                '受信
                len = _stream.Read(data, 0, data.Length)
                ms.Write(data, 0, len)
                '文字列に変換する
                str = enc.GetString(ms.ToArray())
            Loop While Not str.EndsWith(vbCrLf) OrElse _
                System.Text.RegularExpressions.Regex.IsMatch( _
                    str, "(?:^|\n)\d+-[^\r\n]*\r\n$")

            ms.Close()

            '表示
            Console.Write(("S: " + str))

            _recMsg = str
        End Sub

        'データを送信する
        Private Sub SendData(ByVal str As String)
            Dim enc As Encoding = Encoding.GetEncoding(50220)

            'byte型配列に変換
            Dim data As Byte() = enc.GetBytes(str)
            '送信
            _stream.Write(data, 0, data.Length)

            '表示
            Console.Write(("C: " + str))
        End Sub

        'JISでエンコードし、Base64に変換
        Private Function GetBase64String(ByVal str As String) As String
            Dim enc As Encoding = Encoding.GetEncoding(50220)
            Return Convert.ToBase64String(enc.GetBytes(str))
        End Function

        '認証を行う
        Private Sub Authenticate()
            If AuthMethod = SmtpAuthMethod.Plain Then
                'PLAIN
                SendData("AUTH PLAIN" + vbCrLf)
                ReceiveData()
                If Not _recMsg.StartsWith("334") Then
                    If _recMsg.StartsWith("502") Then
                        '認証の必要なし
                        Return
                    End If
                    Throw New ApplicationException(_recMsg)
                End If

                Dim str As String = AuthUserName + _
                    ControlChars.NullChar + AuthUserName + _
                    ControlChars.NullChar + AuthPassword
                SendData((GetBase64String(str) + vbCrLf))
                ReceiveData()
                If Not _recMsg.StartsWith("235") Then
                    Throw New ApplicationException(_recMsg)
                End If
            ElseIf AuthMethod = SmtpAuthMethod.Login Then
                'LOGIN
                SendData("AUTH LOGIN" + vbCrLf)
                ReceiveData()
                If Not _recMsg.StartsWith("334") Then
                    If _recMsg.StartsWith("502") Then
                        '認証の必要なし
                        Return
                    End If
                    Throw New ApplicationException(_recMsg)
                End If

                SendData((GetBase64String(AuthUserName) + vbCrLf))
                ReceiveData()
                If Not _recMsg.StartsWith("334") Then
                    Throw New ApplicationException(_recMsg)
                End If

                SendData((GetBase64String(AuthPassword) + vbCrLf))
                ReceiveData()
                If Not _recMsg.StartsWith("235") Then
                    Throw New ApplicationException(_recMsg)
                End If
            End If
        End Sub
    End Class
End Namespace
C#
コードを隠すコードを選択
using System;
using System.Net.Sockets;
using System.Text;
using System.Collections.Specialized;

namespace Dobon.Mail
{
    /// <summary>
    /// 送信用のメールメッセージ
    /// </summary>
    public class SmtpMailMessage
    {
        private string _from;
        /// <summary>
        /// 送信者のメールアドレス
        /// </summary>
        public string From
        {
            get
            {
                return _from;
            }
            set
            {
                _from = value;
            }
        }

        private string _to;
        /// <summary>
        /// 送信先のメールアドレス
        /// </summary>
        public string To
        {
            get
            {
                return _to;
            }
            set
            {
                _to = value;
            }
        }

        private string _subject;
        /// <summary>
        /// メールの件名
        /// </summary>
        public string Subject
        {
            get
            {
                return _subject;
            }
            set
            {
                _subject = value;
            }
        }

        private string _body;
        /// <summary>
        /// メールの本文
        /// </summary>
        public string Body
        {
            get
            {
                return _body;
            }
            set
            {
                _body = value;
            }
        }
    }

    /// <summary>
    /// SMTPでメールを送信する
    /// </summary>
    public class Smtp
    {
        /// <summary>
        /// エントリポイント
        /// </summary>
        public static void Main()
        {
            SmtpMailMessage mail = new SmtpMailMessage();
            //From
            mail.From = "xxx@xxx.xxx";
            //To
            mail.To = "xxx@xxx.xxx";
            //Subject
            mail.Subject = "テスト";
            //本文
            mail.Body = "これはテストです。";

            Smtp smtp = new Smtp();
            //SMTPサーバー名
            smtp.SmtpServer = "xxx.xxx.xxx";
            //ポート番号
            smtp.SmtpPort = 25;
            //認証
            smtp.AuthMethod = SmtpAuthMethod.Plain;
            smtp.AuthUserName = "xxxxx";
            smtp.AuthPassword = "xxxxx";
            //送信する
            smtp.Send(mail);

            Console.ReadLine();
        }

        private string _smtpServer;
        /// <summary>
        /// SMTPサーバー
        /// </summary>
        public string SmtpServer
        {
            get
            {
                return _smtpServer;
            }
            set
            {
                _smtpServer = value;
            }
        }

        private int _smtpPort = 25;
        /// <summary>
        /// ポート番号
        /// </summary>
        public int SmtpPort
        {
            get
            {
                return _smtpPort;
            }
            set
            {
                _smtpPort = value;
            }
        }

        private string _authUserName;
        /// <summary>
        /// 認証で用いるユーザー名
        /// </summary>
        public string AuthUserName
        {
            get
            {
                return _authUserName;
            }
            set
            {
                _authUserName = value;
            }
        }

        private string _authPassword;
        /// <summary>
        /// 認証で用いるパスワード
        /// </summary>
        public string AuthPassword
        {
            get
            {
                return _authPassword;
            }
            set
            {
                _authPassword = value;
            }
        }

        /// <summary>
        /// SMTPの認証方法
        /// </summary>
        public enum SmtpAuthMethod
        {
            None,
            Plain,
            Login
        }

        private SmtpAuthMethod _authMethod = SmtpAuthMethod.None;
        /// <summary>
        /// 認証方法
        /// </summary>
        public SmtpAuthMethod AuthMethod
        {
            get
            {
                return _authMethod;
            }
            set
            {
                _authMethod = value;
            }
        }

        private NetworkStream _stream;
        private string _recMsg;
        private string boundary;

        /// <summary>
        /// メールを送信する
        /// </summary>
        /// <param name="mail">送信するメール</param>
        public void Send(SmtpMailMessage mail)
        {
            TcpClient soc = new TcpClient();

            //接続
            soc.Connect(SmtpServer, SmtpPort);
            _stream = soc.GetStream();

            try
            {
                ReceiveData();
                if (!_recMsg.StartsWith("220"))
                    throw new ApplicationException("接続に失敗しました。");

                SendData("EHLO\r\n");
                ReceiveData();
                if (!_recMsg.StartsWith("250"))
                    throw new ApplicationException(_recMsg);

                //認証する
                if (AuthMethod != SmtpAuthMethod.None)
                    Authenticate();

                SendData("MAIL FROM:" + mail.From + "\r\n");
                ReceiveData();
                if (!_recMsg.StartsWith("250"))
                    throw new ApplicationException(_recMsg);

                SendData("RCPT TO:" + mail.To + "\r\n");
                ReceiveData();
                if (!_recMsg.StartsWith("250"))
                    throw new ApplicationException(_recMsg);

                SendData("DATA\r\n");
                ReceiveData();
                if (!_recMsg.StartsWith("354"))
                    throw new ApplicationException(_recMsg);

                //送信データ
                string data = "";
                data += "From: " + mail.From + "\r\n";
                data += "To: " + mail.To + "\r\n";
                //件名をBase64でエンコード
                data += "Subject: =?ISO-2022-JP?B?" +
                    GetBase64String(mail.Subject) + "?=" + "\r\n";
                data += "MIME-Version: 1.0\r\n";
                data += "Content-Transfer-Encoding: 7bit\r\n";

                data += "Content-Type: text/plain; charset=ISO-2022-JP\r\n";

                data += "\r\n" +
                    mail.Body.Replace("\r\n.\r\n", "\r\n..\r\n")
                    + "\r\n";

                data += ".\r\n";

                SendData(data);
                ReceiveData();

                SendData("QUIT\r\n");
                ReceiveData();
            }
            finally
            {
                //切断する
                if (_stream != null)
                {
                    _stream.Close();
                }
                soc.Close();
            }
        }

        //データを受信する
        private void ReceiveData()
        {
            Encoding enc = Encoding.GetEncoding(50220);
            byte[] data = new byte[1024];
            int len;
            string str = "";
            System.IO.MemoryStream ms = new System.IO.MemoryStream();

            //すべて受信する
            do
            {
                //受信
                len = _stream.Read(data, 0, data.Length);
                ms.Write(data, 0, len);
                //文字列に変換する
                str = enc.GetString(ms.ToArray());
            }
            while (!str.EndsWith("\r\n") ||
                System.Text.RegularExpressions.Regex.IsMatch(
                    str, @"(?:^|\n)\d+-[^\r\n]*\r\n$"));

            ms.Close();

            //表示
            Console.Write("S: " + str);

            _recMsg = str;
        }

        //データを送信する
        private void SendData(string str)
        {
            Encoding enc = Encoding.GetEncoding(50220);

            //byte型配列に変換
            byte[] data = enc.GetBytes(str);
            //送信
            _stream.Write(data, 0, data.Length);

            //表示
            Console.Write("C: " + str);
        }

        //JISでエンコードし、Base64に変換
        private string GetBase64String(string str)
        {
            Encoding enc = Encoding.GetEncoding(50220);
            return Convert.ToBase64String(enc.GetBytes(str));
        }

        //認証を行う
        private void Authenticate()
        {
            if (AuthMethod == SmtpAuthMethod.Plain)
            {
                //PLAIN
                SendData("AUTH PLAIN\r\n");
                ReceiveData();
                if (!_recMsg.StartsWith("334"))
                {
                    if (_recMsg.StartsWith("502"))
                        //認証の必要なし
                        return;
                    throw new ApplicationException(_recMsg);
                }

                string str = AuthUserName + '\0' + AuthUserName +

                    '\0' + AuthPassword;
                SendData(GetBase64String(str) + "\r\n");
                ReceiveData();
                if (!_recMsg.StartsWith("235"))
                    throw new ApplicationException(_recMsg);
            }
            else if (AuthMethod == SmtpAuthMethod.Login)
            {
                //LOGIN
                SendData("AUTH LOGIN\r\n");
                ReceiveData();
                if (!_recMsg.StartsWith("334"))
                {
                    if (_recMsg.StartsWith("502"))
                        //認証の必要なし
                        return;
                    throw new ApplicationException(_recMsg);
                }

                SendData(GetBase64String(AuthUserName) + "\r\n");
                ReceiveData();
                if (!_recMsg.StartsWith("334"))
                    throw new ApplicationException(_recMsg);

                SendData(GetBase64String(AuthPassword) + "\r\n");
                ReceiveData();
                if (!_recMsg.StartsWith("235"))
                    throw new ApplicationException(_recMsg);
            }
        }
    }
}
  • 履歴:
  • 2007/1/22 「.NET Framework 2.0以降で、SmtpClientクラスを使用する方法」を追加。
  • 2007/3/12 「MailMessage.Fieldsプロパティを使う方法」でSMTPサーバーを指定するFields.Item名が間違えていたのを修正。(arieさんからご指摘をいただきました。)
  • 2011/3/1 NetworkStream.Closeを呼び出すように修正。