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

POP3メールサーバーからメールを受信する

残念ながら.NET FrameworkはPOP3サーバーから簡単にメールを受信する方法を用意してくれていません。つまりPOP3メールサーバーからメールを受信するには、ソケットを使ってやり取りをする必要があります。それだけならまだしも、受信したメールを正しく解釈するには、MIMEなどのややこしい知識が要ります。

以下にTcpClientクラスを使用してPOP3メールサーバーからすべてのメールを受信するごく簡単な例を紹介しますが(受信するだけです)、多少まともなPOP3クライアントを作成するつもりであれば、より多くのPOP3やMIME等の知識が絶対に必要であることを認識しておいてください。つまり最低でも、RFCの該当箇所(「Post Office Protocol - Version 3」など)を読んで理解する必要があります。

もしちゃんと勉強するつもりがなく、それでもメールを受信したいというのであれば、第三者の作成したライブラリや、COMなどに頼るのがよいでしょう。Tatsuo BabaさんのBASP21 DLLが有名で、お勧めです。

以上のように初心者の方には全く勧められませんが、ごく簡単な例を以下に紹介します。次のコードは、コンソールアプリケーションとして、POP3サーバー(サーバー名:localhost、ポート番号:110)のメールボックス(ユーザー名:userid、パスワード:password)からメールを受信する(さらにメールは削除する)例です。Pop3MailクラスのReceiveメソッドは受信したすべてのメールをstring型配列として返します。

VB.NET
コードを隠すコードを選択
'C#のコードを'C# to VB.NET Translator'で変換し、修正したコードです
'http://www.aspalliance.com/aldotnet/examples/translate.aspx
Imports System
Imports System.Text
Imports System.Net.Sockets

Namespace Dobon.Samples.Mail
    Public Class Pop3Mail
        'エントリポイント
        Public Shared Sub Main()
            Dim mails() As String
            mails = Receive("localhost", 110, "userid", "password", True)

            Console.ReadLine()
        End Sub

        ''' <summary>
        ''' POP3サーバーからメールをすべて受信する
        ''' </summary>
        ''' <param name="hostName">POP3サーバー名</param>
        ''' <param name="portNumber">POP3サーバーのポート番号</param>
        ''' <param name="userId">ユーザーID</param>
        ''' <param name="passWord">パスワード</param>
        ''' <param name="deleteMails">メールを削除するか</param>
        ''' <returns>取得したメールの配列</returns>
        Public Shared Function Receive( _
                ByVal hostName As String, _
                ByVal portNumber As Integer, _
                ByVal userId As String, _
                ByVal passWord As String, _
                ByVal deleteMails As Boolean) As String()
            Dim mails As String() = Nothing
            Dim msg As String = ""
            Dim stream As NetworkStream = Nothing

            'TcpClientの作成
            Dim client As New TcpClient
            'タイムアウトの設定
            client.ReceiveTimeout = 10000
            client.SendTimeout = 10000

            Try
                'サーバーに接続
                client.Connect(hostName, portNumber)
                'ストリームの取得
                stream = client.GetStream()
                '受信
                msg = ReceiveData(stream)

                'USERの送信
                SendData(stream, "USER " + userId + vbCrLf)
                '受信
                msg = ReceiveData(stream)

                'PASSの送信
                SendData(stream, "PASS " + passWord + vbCrLf)
                '受信
                msg = ReceiveData(stream)

                'STATの送信
                SendData(stream, "STAT" + vbCrLf)
                '受信
                msg = ReceiveData(stream)
                'メール数の取得
                Dim mailsCount As Integer = Integer.Parse(msg.Split(" "c)(1))
                mails = New String(mailsCount - 1) {}

                'すべてのメールの内容を受信
                Dim i As Integer
                For i = 1 To mailsCount
                    'RETRの送信(メール本文を受信)
                    SendData(stream, "RETR " + i.ToString() + vbCrLf)
                    '受信
                    msg = ReceiveData(stream, True)
                    mails((i - 1)) = msg.Substring((msg.IndexOf(vbCrLf) + 2))

                    'メールを削除するか
                    If deleteMails Then
                        'DELEの送信(メールに削除マークを付ける)
                        SendData(stream, "DELE " + i.ToString() + vbCrLf)
                        '受信
                        msg = ReceiveData(stream)
                    End If
                Next i

                'QUITの送信
                SendData(stream, "QUIT" + vbCrLf)
                '受信
                msg = ReceiveData(stream)
            Catch
                Throw
            Finally
                '切断
                If Not stream Is Nothing Then
                    stream.Close()
                End If
                client.Close()
            End Try

            Return mails
        End Function

        'データを受信する
        Private Overloads Shared Function ReceiveData( _
                ByVal stream As NetworkStream, _
                ByVal multiLines As Boolean, _
                ByVal bufferSize As Integer, _
                ByVal enc As Encoding) As String
            Dim data(bufferSize - 1) As Byte
            Dim len As Integer
            Dim msg As String = ""
            Dim ms As New System.IO.MemoryStream

            'すべて受信する
            '(無限ループに陥る恐れあり)
            Do
                '受信
                len = stream.Read(data, 0, data.Length)
                ms.Write(data, 0, len)
                '文字列に変換する
                msg = enc.GetString(ms.ToArray())
            Loop While stream.DataAvailable OrElse _
                ((Not multiLines OrElse msg.StartsWith("-ERR")) AndAlso _
                    Not msg.EndsWith(vbCrLf)) OrElse _
                (multiLines AndAlso Not msg.EndsWith(vbCrLf + "." + vbCrLf))

            ms.Close()

            '"-ERR"を受け取った時は例外をスロー
            If msg.StartsWith("-ERR") Then
                Throw New ApplicationException("Received Error")
            End If
            '表示
            Console.Write(("S: " + msg))

            Return msg
        End Function
        Private Overloads Shared Function ReceiveData( _
                ByVal stream As NetworkStream, _
                ByVal multiLines As Boolean, _
                ByVal bufferSize As Integer) As String
            Return ReceiveData(stream, multiLines, bufferSize, _
                Encoding.GetEncoding(50220))
        End Function
        Private Overloads Shared Function ReceiveData( _
                ByVal stream As NetworkStream, _
                ByVal multiLines As Boolean) As String
            Return ReceiveData(stream, multiLines, 256)
        End Function

        Private Overloads Shared Function ReceiveData( _
                ByVal stream As NetworkStream) As String
            Return ReceiveData(stream, False)
        End Function

        'データを送信する
        Private Overloads Shared Sub SendData( _
                ByVal stream As NetworkStream, _
                ByVal msg As String, _
                ByVal enc As Encoding)
            'byte型配列に変換
            Dim data As Byte() = enc.GetBytes(msg)
            '送信
            stream.Write(data, 0, data.Length)

            '表示
            Console.Write(("C: " + msg))
        End Sub
        Private Overloads Shared Sub SendData( _
                ByVal stream As NetworkStream, _
                ByVal msg As String)
            SendData(stream, msg, Encoding.ASCII)
        End Sub
    End Class
End Namespace
C#
コードを隠すコードを選択
using System;
using System.Text;
using System.Net.Sockets;

namespace Dobon.Samples.Mail
{
    public class Pop3Mail
    {
        //エントリポイント
        public static void Main()
        {
            string[] mails;
            mails = Receive("localhost", 110, "userid", "password", true);

            Console.ReadLine();
        }

        /// <summary>
        /// POP3サーバーからメールをすべて受信する
        /// </summary>
        /// <param name="hostName">POP3サーバー名</param>
        /// <param name="portNumber">POP3サーバーのポート番号</param>
        /// <param name="userId">ユーザーID</param>
        /// <param name="passWord">パスワード</param>
        /// <param name="deleteMails">メールを削除するか</param>
        /// <returns>取得したメールの配列</returns>
        public static string[] Receive(string hostName, int portNumber,
            string userId, string passWord, bool deleteMails)
        {
            string[] mails;
            string msg = "";
            NetworkStream stream = null;

            //TcpClientの作成
            TcpClient client = new TcpClient();
            //タイムアウトの設定
            client.ReceiveTimeout = 10000;
            client.SendTimeout = 10000;

            try
            {
                //サーバーに接続
                client.Connect(hostName, portNumber);
                //ストリームの取得
                stream = client.GetStream();
                //受信
                msg = ReceiveData(stream);

                //USERの送信
                SendData(stream, "USER " + userId + "\r\n");
                //受信
                msg = ReceiveData(stream);

                //PASSの送信
                SendData(stream, "PASS " + passWord + "\r\n");
                //受信
                msg = ReceiveData(stream);

                //STATの送信
                SendData(stream, "STAT\r\n");
                //受信
                msg = ReceiveData(stream);
                //メール数の取得
                int mailsCount = int.Parse(msg.Split(' ')[1]);
                mails = new string[mailsCount];

                //すべてのメールの内容を受信
                for (int i = 1; i <= mailsCount; i++)
                {
                    //RETRの送信(メール本文を受信)
                    SendData(stream, "RETR " + i.ToString() + "\r\n");
                    //受信
                    msg = ReceiveData(stream, true);
                    mails[i - 1] = msg.Substring(msg.IndexOf("\r\n") + 2);

                    //メールを削除するか
                    if (deleteMails)
                    {
                        //DELEの送信(メールに削除マークを付ける)
                        SendData(stream, "DELE " + i.ToString() + "\r\n");
                        //受信
                        msg = ReceiveData(stream);
                    }
                }

                //QUITの送信
                SendData(stream, "QUIT\r\n");
                //受信
                msg = ReceiveData(stream);
            }
            catch
            {
                throw;
            }
            finally
            {
                //切断
                if (stream != null)
                {
                    stream.Close();
                }
                client.Close();
            }

            return mails;
        }

        //データを受信する
        private static string ReceiveData(NetworkStream stream,
            bool multiLines, int bufferSize, Encoding enc)
        {
            byte[] data = new byte[bufferSize];
            int len;
            string msg = "";
            System.IO.MemoryStream ms = new System.IO.MemoryStream();

            //すべて受信する
            //(無限ループに陥る恐れあり)
            do
            {
                //受信
                len = stream.Read(data, 0, data.Length);
                ms.Write(data, 0, len);
                //文字列に変換する
                msg = enc.GetString(ms.ToArray());
            }
            while (stream.DataAvailable ||
                ((!multiLines || msg.StartsWith("-ERR")) &&
                !msg.EndsWith("\r\n")) ||
                (multiLines && !msg.EndsWith("\r\n.\r\n")));

            ms.Close();

            //"-ERR"を受け取った時は例外をスロー
            if (msg.StartsWith("-ERR"))
            {
                throw new ApplicationException("Received Error");
            }

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

            return msg;
        }
        private static string ReceiveData(NetworkStream stream,
            bool multiLines, int bufferSize)
        {
            return ReceiveData(stream, multiLines, bufferSize,
                Encoding.GetEncoding(50220));
        }
        private static string ReceiveData(NetworkStream stream,
            bool multiLines)
        {
            return ReceiveData(stream, multiLines, 256);
        }
        private static string ReceiveData(NetworkStream stream)
        {
            return ReceiveData(stream, false);
        }

        //データを送信する
        private static void SendData(NetworkStream stream,
            string msg, Encoding enc)
        {
            //byte型配列に変換
            byte[] data = enc.GetBytes(msg);
            //送信
            stream.Write(data, 0, data.Length);

            //表示
            Console.Write("C: " + msg);
        }
        private static void SendData(NetworkStream stream,
            string msg)
        {
            SendData(stream, msg, Encoding.ASCII);
        }
    }
}
  • 履歴:
  • 2004/3/15 ReceiveDataメソッドを修正しました。
  • 2011/3/1 NetworkStream.Closeを呼び出すように修正。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • .NET Tipsをご利用いただく際は、注意事項をお守りください。