#title(.NETプログラミング研究 第12号)

#navi(.NETプログラミング研究)

#contents

*.NETプログラミング研究 第12号 [#f09746d4]

**.NET Tips [#z6bd4a84]

***POP3メールサーバーからメールを受信する [#uc0638db]

#column(注意){{
この記事の最新版は「[[POP3メールサーバーからメールを受信する>http://dobon.net/vb/dotnet/internet/receivepop3mail.html]]」で公開しています。
}}

.NET Frameworkでは、System.Web.Mail.SmtpMailクラスを使用すると、簡単にメールを送信することが出来ます。多分そのために、「メールの受信も簡単に出来るのでは?」と思ったり、「メーラーを作りたい」と思い立つ人が多いようで、「メールの受信はどうするの?」といった質問を多く受けるようになりました。

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

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

-[[IETF RFC Page>http://www.ietf.org/rfc]]
-[[Post Office Protocol - Version 3>http://www.ietf.org/rfc/rfc1939.txt]]

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

-[[Baba Centerfolds>http://www.hi-ho.ne.jp/babaq/index.html]]

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

#code(csharp){{
using System;
using System.Text;
using System.Net.Sockets;

namespace Dobon.Net.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;

            //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
            {
                //切断
                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 = "";

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

            //"-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);
        }
    }
}
}}

#code(vbnet){{
'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.Net.Mail
    Public Class Pop3Mail
        'エントリポイント
        Public Shared Sub Main()
            Dim mails() As String
            mails = Receive("localhost", 110, "userid", "password", True)

            Console.ReadLine()
        End Sub 'Main

        '/ <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
            Dim msg As String = ""
            Dim stream As NetworkStream

            '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) {}

                'すべてのメールの内容を受信
                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
            Finally
                '切断
                client.Close()
            End Try

            Return mails
        End Function 'Receive

        'データを受信する
        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) As Byte
            Dim len As Integer
            Dim msg As String = ""

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

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

            Return msg
        End Function 'ReceiveData
        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 'ReceiveData

        Private Overloads Shared Function ReceiveData( _
                ByVal stream As NetworkStream, _
                ByVal multiLines As Boolean) As String
            Return ReceiveData(stream, multiLines, 256)
        End Function 'ReceiveData

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

        'データを送信する
        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 'SendData

        Private Overloads Shared Sub SendData( _
                ByVal stream As NetworkStream, _
                ByVal msg As String)
            SendData(stream, msg, Encoding.ASCII)
        End Sub 'SendData
    End Class 'Pop3Mail
End Namespace 'Dobon.Net.Mail
}}

**ピンポイントリンク [#d2384479]

***C#のコードをVB.NETへ変換する(及びその逆) [#od439a9b]
 前回の補足と修正

前回、C#のコードをVB.NETのコードへ、VB.NETのコードをC#のコードへ変換するのに役立つツールをいくつか紹介しましたが、今回はその記事の修正と補足をさせていただきます。

まず前回「[[Convert C# to VB .NET>http://www.kamalpatel.net/ConvertCSharp2VB.aspx]]」
に関して、
「日本語を含むコードを変換しようとすると、エラーになることがあります。」
と書きましたが、これは間違いのようで、実際には";"の前で改行されている(文の途中で改行されている)とエラーが発生することがあるようです。また「Convert C# to VB .NET」の場合、エラーが発生しなくても、文の途中で改行されているコードは正しく変換されないことが非常に多いため、「Convert C# to VB .NET」を使用する際は文の途中で改行していないコードを入力することをお勧めします。

さらに前回は「Convert C# to VB .NET」と「[[C# to VB.NET Translator>http://www.aspalliance.com/aldotnet/examples/translate.aspx]]」
のどちらが優れているか分からないと書きましたが、何度となく使っているうちに、「C# to VB.NET Translator」の方が圧倒的に優れていると感じるようになりました。

例えば上記「.NET Tips」で紹介したコードを「Convert C# to VB .NET」で変換すると、文の途中の改行を削除してから変換しても、何十箇所と修正しなければならないほど散々な結果でした。それに比べて「C# to VB.NET Translator」では、1つのエラーも出ることがありませんでした。しかし厄介なことに、do...while文の条件式で括弧のつけ方を間違えて、エラーが出なくても正常に機能しないコードになってしまいました。この一箇所を除けば、他は完璧に変換できたようです。(なんとも惜しい...。)

また、C#からVB.NETのコードへの変換で役に立つツールに関して、「VBCS翻訳機」というツールがあることを教えていただきました。「VBCS翻訳機」はsanta martaさんのフリーウェアで、「Visual Basic .NET で記述されたソースコードをVisual C# .NET に翻訳するためのツール」とのことです。

-[[VBCS翻訳機>http://santamartaofthepeace.hp.infoseek.co.jp/products/vbcstranslator.html]]

この「VBCS翻訳機」や前回紹介した「[[Convert Visual Basic .NET to C#>http://www.ellkay.com/ConvertVB2CSharp.htm]]」
を使ってみると、正直なところ、C#からVB.NETへの変換ツールと比べて、まだまだ実際に使用できるレベルになっていないというのが実感です。VB.NETからC#への変換は簡単にはいかないようです。

**コメント [#qab4ce34]
#comment

//これより下は編集しないでください
#pageinfo([[:Category/.NET]],2003-07-14 (月) 06:00:00,DOBON!,2010-03-21 (日) 00:25:41,DOBON!)

[ トップ ]   [ 新規 | 子ページ作成 | 一覧 | 単語検索 | 最終更新 | ヘルプ ]