developer's diary

最近はc#のエントリが多いです

c# ( dotnetcore ) で OpenPop.NET を利用して、POP3(Post Office Protocol)を操作してgmailを受信してみる

The Art of UNIX Programming (アスキードワンゴ)の第5章にある、「テキスト形式 :優れたプロトコルが優れた実践を生む」> 「アプリケーションプロトコルの設計」>「ケーススタディ:POP3(Post Office Protocol)」(P.157)をC#でもやりたい。

c#のレッスンで、メーラーを作りたいとのご要望がありましたので、 POP3プロトコルを利用したメール受信のサンプルを作ってみました。

注意事項

この方法だと、gmailの設定を安全性の低いアプリのアクセスを有効にする必要があります。

あくまで、POP3プロトコルを試すために、利用しています。 gmailを外部プログラムで利用する場合は、OAuth + IMAPを検討した方が良いでしょう。

また、この方法はgmailを利用していますが、gmail以外でもPOP3というプロトコルであれば利用できるとおもいます。 ただし、本番で利用しているPOP3サーバをプログラムからいきなり操作するのは絶対やめてください。メールが消えてしまう可能性があります。

新しいメールアドレスを用意して、メールサーバの情報がどのように動作しているか、確認し、理解した上で利用しましょう。

ライブラリはOpenPop.NETを利用

今回はpop3でnugetを検索した際に、一番ダウンロード数が多かった「OpenPop.NET」を利用します。 f:id:mitsugi-bb:20200818235650p:plain

ライセンスはcc0の著作権放棄でした。

ライブラリの開発ソースはgithubにあります。

github.com

OpenPop.NETをインストール

nugetパッケージマネージャーで、OpenPop.NETをインストール

Install-Package OpenPop.NET

POP3でメールを受信するプログラム

using System;
using OpenPop.Mime;
using OpenPop.Pop3;

namespace mail_console_example
{
    class Program
    {
        static void Main(string[] args)
        {
            //Pop3Clientを生成
            using (Pop3Client client = new Pop3Client())
            {
                //pop3サーバーへの接続先
                client.Connect("pop.gmail.com", 995, true);

                //認証
                client.Authenticate("gmailのメールアドレスを指定", "パスワードを設定",AuthenticationMethod.UsernameAndPassword);

                //メッセージ数取得
                int messageCount = client.GetMessageCount();

                //メッセージ数の数だけ繰り返す
                for (int i = messageCount; i > 0; i--)
                {
                    //メッセージを1件取得
                    Message message = client.GetMessage(i);

                    //From アドレス
                    Console.WriteLine("From Address:" + message.Headers.From.MailAddress.Address);

                    //From 表示名
                    Console.WriteLine("From DisplayName:" + message.Headers.From.MailAddress.DisplayName);

                    //To は複数のため繰り返しで表示
                    foreach (var address in message.Headers.To)
                    {
                        Console.WriteLine("To Address:" + address.Address);
                        Console.WriteLine("To DisplayName:" + address.DisplayName);
                    }

                    //Cc も複数のため繰り返しで表示
                    foreach (var address in message.Headers.Cc)
                    {
                        Console.WriteLine("Cc Address:" + address.Address);
                        Console.WriteLine("Cc DisplayName:" + address.DisplayName);
                    }

                    //件名を表示
                    Console.WriteLine("Subject:" + message.Headers.Subject);

                    //本文は、text/plainのオブジェクト(テキストパート)を取得 
                    var plainText = message.FindFirstPlainTextVersion();

                    //テキストパートのバイト配列を文字列にして出力
                    Console.WriteLine("Body:" + plainText.GetBodyAsText());

                    //添付ファイルは複数の可能性があるため、繰り返しで表示
                    foreach(var messagePart in message.FindAllAttachments())
                    {
                        Console.WriteLine("Attachments FileName:" + messagePart.FileName);
                        Console.WriteLine("Attachments ContentType Name:" + messagePart.ContentType.ToString());
                    }

                }
            }

        }
    }
}

gmailの設定( gmailを利用する場合の前提条件 )

(1) gmailの設定で、POP3を利用できるように設定しておくこと。

f:id:mitsugi-bb:20200819001920p:plain
POP3の設定の様子

(2) googleアカウントの設定で、安全性の低いアプリのアクセスを有効にしておくこと。

f:id:mitsugi-bb:20200819001959p:plain
安全性の低いアプリのアクセスを有効にする様子

使ってみる

以下のような感じで出力されました。

From Address:--伏せます--@gmail.com
From DisplayName:つつみみつぎ
To Address:--伏せます--@gmail.com
To DisplayName:
Subject:テスト件名
Body:テスト本文

Attachments FileName:test.php
Attachments ContentType Name:text/php; charset=UTF-8; name=test.php

gmailのPOPには、通常モードと最新モードがある(特別仕様)

support.google.com

詳しくは上記リンクのヘルプを参考にしていただきたいが、

要は

通常モードだと、1度ダウンロードしたデータは再ダウンロードできない。 最新モードだと、削除しないかぎり、過去 30 日間のメールは何度でもダウンロードできる。

通常モードと最新モードの切り替え方法

認証用のメールアドレスの前方に、「recent:」を付与すると、gmailのPOP3は最新モードとして動きます。

//通常モード認証方法
client.Authenticate("gmailのメールアドレスを指定", "パスワードを設定",AuthenticationMethod.UsernameAndPassword);
//最新モード認証方法(メールアドレスの前方に「recent:」を付与)
client.Authenticate("recent:gmailのメールアドレスを指定", "パスワードを設定",AuthenticationMethod.UsernameAndPassword);

デバッグしたい

(1) OpenPop.Common.Logging.ILogというインターフェースを継承したクラスを作成

    public class Logger : OpenPop.Common.Logging.ILog
    {
        public void LogError(string message)
        {
            Console.WriteLine("LogError: " + message);
        }

        public void LogDebug(string message)
        {
            Console.WriteLine("LogDebug: " + message);
        }
    }

(2) DefaultLoggerの静的メソッドSetLogに作成したクラスを設定する

OpenPop.Common.Logging.DefaultLogger.SetLog(new Logger());

(3) 実行すると以下のようなログがコンソールに出力されました。

LogDebug: Connect-Response: "+OK Gpop ready for requests from --- 伏せます --- "
LogDebug: SendCommand: "USER gmailのメールアドレス"
LogDebug: Server-Response: "+OK send PASS"
LogDebug: SendCommand: "PASS パスワード"
LogDebug: Server-Response: "+OK Welcome."
LogDebug: SendCommand: "STAT"
LogDebug: Server-Response: "+OK 1 10968"
LogDebug: SendCommand: "RETR 1"
LogDebug: Server-Response: "+OK message follows"

今回は標準出力でコンソールに出力しましたが、この仕組みを利用したら、 簡単にPOP3のセッションの状態がログに残せそうですね。

参考書籍