Smdn.Net.Pop3のドキュメントとサンプルです。 ここに記載されているものはversion 0.92時点のもので、記載不備などがあるかもしれません。

サンプルコードはこのページの最後にあります。 ご質問などありましたら掲示板へどうぞ。


用語と表記

このドキュメントでは、以下の表記を用いています。

メッセージ
POPサーバ上のメールボックスにある個々のメールの本文または/および属性を表す意味で用いています。
メッセージ本文
POPサーバ上のメールボックスにある個々のメールの本文のみを表す意味で用いています。
SSL/TLS
SSLおよびTLSを特に区別なく表す場合、SSL/TLSと表記しています。
認証方式
認証に用いるメカニズムを表す意味で用いています。 ほとんどの場合、SASLメカニズムを表す意味で用いていますが、場合によってはPOP標準の平文による認証も含みます。
クライアント
ほとんどの場合、本ライブラリに含まれるクライアント実装を表す意味で用いていますが、場合によっては一般的なPOPクライアントを含みます。
一覧
リスト(IList)・コレクション(ICollection)・その他の集合(IEnumerable等)を表す意味で用いている場合があります。

上記以外は、RFC 1939および.NET Frameworkの用語に準じます。

ライブラリの構成

アセンブリ構成

本ライブラリはいくつかのアセンブリに分かれています。

アセンブリに含まれる型と用途
アセンブリ 含まれる型
Smdn.dll 他のアセンブリで共通して使用される型、ユーティリティクラス、.NET Framework 4.x, 3.x互換の型など
Smdn.Core.Standards.dll MIMEエンコード/デコード、URLエンコード/デコード等の標準に関するクラス群
Smdn.Net.MessageAccessProtocols.dll Smdn.Net.Pop3およびSmdn.Net.Imap4で共通して使用されるクラス群
Smdn.Security.Authentication.Sasl.dll Smdn.Security.Authentication.Sasl(SASLクライアントの実装)
Smdn.Net.Pop3.dll POP3で使用される基本型等の定義
Smdn.Net.Pop3.Client.dll PopClient, PopSessionを含むPOP3クライアント実装
Smdn.Net.Pop3.WebClients.dll PopWebRequest/PopWebResponseを含むPOP3クライアント実装

本ライブラリを使用する場合は、上記アセンブリへの参照を追加してください(PopWebRequest/PopWebResponseを使用しない場合は、Smdn.Net.Pop3.WebClients.dllへの参照は不要です)。

クライアント実装

クライアントの実装は次の3種類があります。

クライアント実装の種類と概要
名前空間 クラス 概要 解説
Smdn.Net.Pop3.Client
(Smdn.Net.Pop3.Client.dll)
PopClient
PopMessageInfo
POPの操作を抽象化したクラスです。 メッセージに対する操作をSystem.IO.FileInfoクラスに似たインターフェイスで行えるようにしてあります。 PopClient
Smdn.Net.Pop3.WebClients
(Smdn.Net.Pop3.WebClients.dll)
PopWebRequest
PopWebResponse
POP URL(RFC 2384)を用いたクライアントの実装です。 System.Net.WebRequest/System.Net.WebResponseを継承しているので、メッセージのダウンロードにWebClientクラスのメソッドを使うこともできます。
System.Net.FtpWebRequest等と同様、WebRequest.Methodプロパティで送信するコマンドを制御できます。
PopWebRequest/PopWebResponse
Smdn.Net.Pop3.Client.Session
(Smdn.Net.Pop3.Client.dll)
PopSession POPコマンドと1対1に対応するメソッドを持つクラスです。 POPの操作は抽象化していません。 仕様と1対1で対応するような実装にしてあります。 このクラスは上記2種類のクライアント実装で内部的に使用しています。
直接使用することもできますが、インターフェイスを変更することがあるので推奨はできません。
-

共通事項

ここでは各クライアント実装に共通する部分について解説します。 各クライアント実装の詳細については個別の解説を参照してください。

接続と認証

PopClientクラスおよびPopWebRequestクラスはPOP URLを使った接続に対応しています。 接続時の動作は、基本的にPOP URLの仕様に準じた動作となるようにしています※ただし、popsスキームを使用した場合にSSL/TLSでの接続を試みる動作は、仕様には無い、本ライブラリ独自の拡張です

ここでは接続時と認証時の動作について解説します。

接続

接続時の動作は、接続時のパラメータにより次のように変わります。

SSL/TLSを使用する (POP over SSL)
常にSSL/TLSを使用して接続を試みます。 デフォルトのポート番号は995です。
URLのスキーム*1"pops"を指定した場合、SecurePortプロパティ*2trueを指定した場合はこの動作になります。
可能ならSSL/TLSを使用する (STLS)
SSL/TLSを使用せず接続を試み、サーバがSTLSをサポートしていれば認証を開始する前にSSL/TLSへのアップグレードを試みます。 デフォルトのポート番号は110です。
URLのスキーム*1"pop"を指定した場合、SecurePortプロパティ*2falseを指定した場合で、UseTlsIfAvailableプロパティ*3trueを指定した場合はこの動作になります。
SSL/TLSを使用しない
常にSSL/TLSを使用せずに接続を試みます。 サーバがSTLSをサポートしていてもSSL/TLSへのアップグレードはしません。 デフォルトのポート番号は110です。
URLのスキーム*1"pop"を指定した場合、SecurePortプロパティ*2falseを指定した場合で、UseTlsIfAvailableプロパティ*3falseを指定した場合はこの動作になります。

URLもしくはパラメータでポート番号を明示的に指定しない限り、デフォルトポートへの接続を試みます。 なお、ポート番号の指定ではSSL/TLSを使用するかどうかの動作は変わりません※ポート番号に995を指定してもURLのスキームが"pop"なら、接続時にSSL/TLSは使用しません

例として接続先のURLと接続時の動作を表にまとめると以下のようになります。

URLと接続動作の例
URL 接続ポート SSL/TLS
pops://user@pop.example.net/ 995 SSL/TLSを使用
pops://user@pop.example.net:10110/ 10110 SSL/TLSを使用
pop://user@pop.example.net/ 110 SSL/TLSを使用しない
もしくは可能ならSSL/TLSへアップグレード
pop://user@pop.example.net:995/ 995 SSL/TLSを使用しない
もしくは可能ならSSL/TLSへアップグレード

これら接続時のパラメータは次の箇所で指定します。

URLのスキーム*1
PopClientクラスのコンストラクタに指定するURLのスキーム、もしくはPopWebRequestクラスに指定するリクエストURL
SecurePortプロパティ*2
PopClientクラスのコンストラクタで指定する引数securePort、もしくはPopClient.Profile.SecurePortプロパティ
UseTlsIfAvailableプロパティ*3
PopClient.Profile.UseTlsIfAvailableプロパティ、もしくはPopWebRequest.UseTlsIfAvailableプロパティ

認証

接続にPOP URLを用いる場合、認証に用いるユーザ名と認証方式はURLから取得します。 パスワードはICredentialsByHostインターフェイス*1を参照し、接続しようとしているホスト名・ポート番号および指定された認証メカニズムをもとに適切なものを取得します。 POP URLではFTPやHTTPのURLとは異なり、URLに平文パスワードを含めることが許可されていないので、URLからはパスワードを取得しません(指定されていてもパスワードとしては解釈しません)。

認証方式にAPOPを使用する場合は、"APOP"ではなく"+APOP"を指定してください。 認証方式を指定しない場合、もしくは"*"が指定されている場合は次の順で認証を試行します。

  1. AUTHコマンド (サーバ・クライアントが対応している認証メカニズムを順に試行)
  2. APOPコマンド (サーバがサポートしている場合のみ)
  3. USER/PASSコマンド

URLもしくはパラメータでユーザ名・認証メカニズムの両方とも指定されていない場合は、ANONYMOUS認証を行います。 認証方式にANONYMOUSを指定して認証を行う場合、ユーザ名の部分をログイントークンとして送信します。 ユーザ名が指定されていない場合は"anonymous@"をログイントークンとして送信します。 ログイントークンに@などの記号を含める場合はURLエスケープする必要があります。

認証方式の大文字小文字の違いは無視します(POP URLの場合は';AUTH='の部分も大文字小文字を無視します)。

例として接続先のURLと認証時の動作を表にまとめると以下のようになります。

URLと認証動作の例
URL ユーザ名 使用する認証メカニズム
pop://user;AUTH=+APOP@pop.example.net/ user APOP
pop://user;AUTH=DIGEST-MD5@pop.example.net/ user DIGEST-MD5
pop://;AUTH=DIGEST-MD5@pop.example.net/ ICredentialsByHostインターフェイスより取得 DIGEST-MD5
pop://user;AUTH=*@pop.example.net/ user サーバ・クライアントが対応しているものを順に試行
pop://user@pop.example.net/ user サーバ・クライアントが対応しているものを順に試行
pop://;AUTH=ANONYMOUS@pop.example.net/ 匿名ユーザ
(ログイントークン: anonymous@)
ANONYMOUS
pop://user@localhost;AUTH=ANONYMOUS@pop.example.net/ 匿名ユーザ
(ログイントークン: user@localhost)
ANONYMOUS
pop://pop.example.net/ 匿名ユーザ
(ログイントークン: anonymous@)
ANONYMOUSもしくはUSER/PASSコマンドを使用

試行する認証メカニズムを制御するには、UsingSaslMechanismsプロパティ*2の値を変更してください。 UsingSaslMechanismsプロパティにANONYMOUSが含まれていても匿名ログインは試行しません。 AllowInsecureLogin*3プロパティにfalseを指定した場合で、かつ現在の接続がSSL/TLSで保護されていない場合、平文およびAPOPによる認証は試行されません(デフォルトはfalseです)。

サーバが対応している認証方式と認証試行順の例
サーバが対応している認証方式 UsingSaslMechanismsの値 試行順序
(接続がSSL/TLSで保護されている、もしくはAllowInsecureLoginがtrueの場合)
試行順序
(接続がSSL/TLSで保護されていない、かつAllowInsecureLoginがfalseの場合)
DIGEST-MD5
CRAM-MD5
APOP
USER/PASS
{"DIGEST-MD5", "CRAM-MD5"} 1.DIGEST-MD5
2.CRAM-MD5
3.APOP
4.USER/PASS
1.DIGEST-MD5
2.CRAM-MD5
DIGEST-MD5
CRAM-MD5
PLAIN
{"PLAIN", "DIGEST-MD5"} 1.PLAIN
2.DIGEST-MD5
1.DIGEST-MD5
PLAIN
APOP
USER/PASS
null 1.APOP
2.USER/PASS
PopAuthenticationExceptionをスロー
(試行できる認証方式なし)

これら認証時のパラメータは次の箇所で指定します。

ICredentialsByHostインターフェイス*1
PopClient.Connect()メソッドに指定する引数credentials、もしくはPopWebRequest.Credentialsプロパティ
UsingSaslMechanismsプロパティ*2
PopClient.Profile.UsingSaslMechanismsプロパティ、もしくはPopWebRequest.UsingSaslMechanismsプロパティ
AllowInsecureLoginプロパティ*3
PopClient.Profile.AllowInsecureLoginプロパティ、もしくはPopWebRequest.AllowInsecureLoginプロパティ

APOPを使用した認証については脆弱性が指摘されているため、本ライブラリではAPOPは平文による認証と同程度のものとして扱います。 参考:情報処理推進機構:情報セキュリティ:脆弱性関連情報取扱い:APOP方式におけるセキュリティ上の弱点(脆弱性)の注意喚起について

資格情報

認証時に必要なユーザ名・パスワードはSystem.Net.ICredentialsByHostインターフェイスを通して取得します。 ICredentialsインターフェイスではなく、ICredentialsByHostインターフェイスを実装していて、GetCredentialメソッドが適切なSystem.Net.NetworkCredentialを返すクラスなら何でも設定できます。

PopWebRequest.CredentialsプロパティはWebRequestから継承しているためICredentialsインターフェイスを実装していることを要求しますが、設定されるインスタンスはICredentialsByHostも実装している必要があります。

現時点ではSecureStringに格納されたパスワードには対応していません。 NetworkCredential.SecurePasswordプロパティにパスワードが設定されていても無視します。

SSL/TLSを使用した接続

証明書の選択と検証

SSL/TLS接続時に使用する証明書はX509Certificate2Collectionで設定できます。 また、証明書の選択と検証にはRemoteCertificateValidationCallbackデリゲートLocalCertificateSelectionCallbackデリゲートを指定できます。 デフォルトの状態では、SSL/TLS接続時にこれらのコールバックメソッドを呼び出して証明書の検証と選択を行い、SslStreamを作成します。

具体的な記述例はPopClientでの例およびPopWebRequestでの例を参照してください。

SSL/TLS接続のカスタマイズ

(このドキュメントは作成中です)
SslStream以外の実装を使いたい場合や、より高度な検証が必要な場合など、SSL/TLS接続時にデフォルトの動作を変更してカスタマイズする場合は、UpgradeConnectionStreamCallbackデリゲートを使用してコールバックメソッドを指定してください。
コールバックメソッドはPopClient.Connect()メソッドの引数、またはPopSessionManager.CreateSslStreamCallbackプロパティに指定してください。 実装例はPopConnection.CreateSslStreamメソッドを参照してください。

例外

ライブラリからは主に以下の例外をスローします。 InnerExceptionプロパティに原因となった例外を設定した状態でスローする場合もあります。

ライブラリがスローする例外
例外クラス スローされる状況
PopInvalidOperationException
および派生クラス (Smdn.Net.Pop3)
プロトコル上不正な操作を行おうとした場合
認証に失敗した場合はPopAuthenticationException
サーバに不正な要求を行おうとした場合はPopProtocolViolationException
サーバがエラー応答を返した場合はPopErrorResponseException
PopConnectionException
および派生クラス (Smdn.Net.Pop3.Protocol)
接続に失敗した場合、ソケットエラーが発生した場合など
SSL/TLSに起因するエラーの場合はPopUpgradeConnectionException
TimeoutException (System) ソケット送受信中やコマンド処理中にタイムアウトした場合
InvalidOperationException (System) 現在のクライアントの状態に対して無効な操作を行おうとした場合
ArgumentException
および派生クラス (System)
nullや値域外の値など、メソッド呼び出し時の引数が不正な場合
WebException
またはProtocolViolationException (System.Net)
Smdn.Net.Pop3.WebClients名前空間で発生した例外は、これらの例外に変換した上でスローされます

サーバ/クライアントのバグなどにより上記以外の例外がスローされる可能性もあります。 ログ出力を有効にしている場合、発生した例外はログに記録されます。

ログ

シンボルTRACEを有効にしてビルドした場合、トレースにログを出力します。 ログ出力に使用するTraceSourceの名前と、出力内容は次のとおりです。

ログ出力に使用するTraceSourceの名前と出力内容
TraceSourceの名前 出力内容
"Smdn.Net.Pop3.Client" コマンドの送受信結果とセッション毎の動作ログ
"POP" 送信するコマンドと受信したレスポンスの内容

ログ出力先などの設定を行う場合は、以下の例のようなアプリケーション構成ファイルを作成してください。

アプリケーション構成ファイルの例
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.diagnostics>
    <sources>
      <source name="Smdn.Net.Pop3.Client" switchValue="Verbose">
        <listeners>
          <add name="console" type="System.Diagnostics.ConsoleTraceListener"/>
          <remove name="Default"/>
        </listeners>
      </source>
      <source name="POP" switchValue="Verbose">
        <listeners>
          <add name="log" type="System.Diagnostics.TextWriterTraceListener" initializeData="pop.log"/>
          <remove name="Default"/>
        </listeners>
      </source>
    </sources>
    <switches>
      <add name="switch" value="All"/>
    </switches>
  </system.diagnostics>
</configuration>


  
    
      
        
          
          
        
      
      
        
          
          
        
      
    
    
      
    
  
]]>

なお、SSL/TLSを使用しているかどうかに関わらず、ログにはパスワードを含む内容を平文で出力します。 ログの出力が不要な場合はTRACEを無効にしてリビルドするか、Trace.csなどを削除してください。

トレース内容のカスタマイズ

トレースにログを出力する際、出力内容はSmdn.Net.MessageAccessProtocols.Diagnosticsの各クラスのインスタンスとしてTraceSource.TraceData()メソッドに渡します。 トレース内容をカスタマイズする場合は、これらのクラスを使うことができます。

カスタマイズしたトレースリスナを使う例
using System;
using System.Diagnostics;
using System.Text;

using Smdn.Net;
using Smdn.Net.MessageAccessProtocols.Diagnostics;
using Smdn.Net.Pop3.Protocol.Client;

public class CustomTraceListener : TraceListener {
  public CustomTraceListener(string name)
    : base(name)
  {
  }

  public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, object data)
  {
    if (data is ReceiveTraceData) {
      var received = data as ReceiveTraceData;
      var recv = Encoding.ASCII.GetString(received.Data.Array, received.Data.Offset, received.Data.Count);

      Write("受信内容:\t");
      WriteLine(recv);
    }
    else if (data is SendTraceData) {
      var sent = data as SendTraceData;
      var snt = Encoding.ASCII.GetString(sent.Data.Array, sent.Data.Offset, sent.Data.Count);

      Write("送信内容:\t");
      WriteLine(snt);
    }
    else if (data is MessageTraceData) {
      WriteLine((data as MessageTraceData).Message);
    }
    else if (data is ExceptionTraceData) {
      WriteLine((data as ExceptionTraceData).Exception);
    }
    else {
      base.TraceData(eventCache, source, eventType, id, data);
    }
  }

  public override void Write(string message)
  {
    // TODO
  }

  public override void WriteLine(string message)
  {
    // TODO
  }
}

public static class Test {
  [STAThread]
  public static void Main(string[] args) {
    // カスタマイズしたトレースリスナを追加
    PopConnection.TraceSource.Listeners.Add(new CustomTraceListener("log"));
      :
      :
  }
}

PopClientクラス (Smdn.Net.Pop3.Client.dll)

PopClientクラスおよびSmdn.Net.Pop3.Client名前空間のクラスの使い方。

概説

基本的には、

  1. PopClientクラスのインスタンスを作成
  2. PopClient.Connect()メソッドでサーバに接続
  3. PopClient.GetMessage(), GetMessages()などのメソッドでPopMessageInfoクラスのインスタンスを取得
  4. PopMessageInfo.Open*(), Read*(), Save()などのメソッドでメッセージ本文を取得

の順でサーバ上のメッセージの取得と操作を行います。 以下でPopClientクラスの使い方について解説します。

接続・認証とSSL/TLS

接続と認証

接続・認証時の動作は、接続と認証で解説したとおりPOP URLに記述されるスキーム・ホスト名・ポート・認証方式・ユーザ名に基づいて決まります。 PopClientクラスではPOP URLは用いず、ホスト・ポート・認証方式・ユーザ名等を個々に指定することもできます。

以下はコード上での記述と接続・認証時の動作の例です。 実際にSSL/TLSを使用する場合は証明書の検証等が必要になります。

PopClient
/*
 * ホスト"pop.example.net"のPOPSデフォルトポート(995)にSSL/TLSを用いて接続。
 * ユーザ名に"user"、パスワードに"pass"を使用。 認証方式は対応しているものを順に試行。
 */
var client1 = new PopClient(new Uri("pops://user@pop.example.net/"));

client1.Connect("pass");

/*
 * ホスト"pop.example.net"のポート10110に接続、可能ならSSL/TLSにアップグレード。
 * ユーザ名に"user"を使用、パスワードはNetworkCredentialから取得。 認証方式はAPOPを試行。
 */
var client2 = new PopClient(new Uri("pop://user;AUTH=+APOP@pop.example.net:10110/"));

client2.Profile.UseTlsIfAvailable = true;
client2.Connect(new NetworkCredential("user", "pass"));

/*
 * ホスト"pop.example.net"のポート10110にSSL/TLSを用いて接続。
 * ユーザ名に"user"、パスワードに"pass"を使用。 認証方式はDIGEST-MD5を試行。
 */
var client3 = new PopClient("pop.example.net", 10110, true, "user", "DIGEST-MD5");

client3.Connect("pass");

/*
 * ホスト"pop.example.net"のPOPデフォルトポート(110)に接続、SSL/TLSは使用しない。
 * ユーザ名に"user"を使用、パスワードはNetworkCredentialから取得。 認証方式はDIGEST-MD5, CRAM-MD5, APOP, USER/PASSの順に試行。
 */
var client4 = new PopClient("pop.example.net", -1, false, null, "*");

client4.Profile.UseTlsIfAvailable = false;
client4.Profile.UsingSaslMechanisms = new[] {"DIGEST-MD5", "CRAM-MD5", "+APOP"};

client4.Connect(new NetworkCredential("user", "pass"));

/*
 * ホスト"pop.example.net"のPOPデフォルトポート(110)に接続、可能ならSSL/TLSを使用する。
 * ユーザ名に"user"を使用、パスワードはNetworkCredentialから取得。
 * 認証方式はCRAM-MD5, PLAIN, APOP, USER/PASSの順に試行。 ただしSSL/TLSが使用できない場合、平文による認証を許可しない。
 */
var client5 = new PopClient(new Uri("pop://user@pop.example.net/"));

client5.Profile.UseTlsIfAvailable = true;
client5.Profile.UsingSaslMechanisms = new[] {"CRAM-MD5", "PLAIN"};
client5.Profile.AllowInsecureLogin = false;

client5.Connect(new NetworkCredential("user", "pass"));

/*
 * GMailのアカウント"user"にPOPで接続。 パスワードは"pass"を使用。
 */
var client6 = new PopClient(new Uri("pops://user@pop.gmail.com/"));

client6.Connect("pass");

証明書の選択と検証

デフォルトでは、接続・認証時にPopConnectionクラス(Smdn.Net.Pop3.Protocol.Client名前空間)の以下のメンバを参照して証明書の選択と検証を行います。

SSL/TLS接続時に参照するメンバ(いずれもクラスプロパティ)
PopClientクラスが参照するメンバ
X509Certificate2Collection PopConnection.ClientCertificates
RemoteCertificateValidationCallback PopConnection.ServerCertificateValidationCallback
RemoteCertificateValidationCallback PopConnection.ClientCertificateSelectionCallback

以下は証明書の検証を行う簡単な例です。

証明書の検証を行う例
using System;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using Smdn.Net.Pop3;
using Smdn.Net.Pop3.Client;
using Smdn.Net.Pop3.Protocol.Client;

public class ValidateServerCerts {
  public static void Main(string[] args)
  {
    // 証明書の検証を行うコールバックメソッドを指定
    PopConnection.ServerCertificateValidationCallback += ValidateRemoteCertificate;

    using (var client = new PopClient(new Uri("pops://user@localhost/"))) {
      client.Connect("pass");
    }
  }

  private static bool ValidateRemoteCertificate(object sender,
                                                X509Certificate certificate,
                                                X509Chain chain,
                                                SslPolicyErrors sslPolicyErrors)
  {
#if DEBUG
    // デバッグ時のみSslPolicyErrors.RemoteCertificateNameMismatchを無視
    if ((int)(sslPolicyErrors & SslPolicyErrors.RemoteCertificateNameMismatch) != 0)
      sslPolicyErrors &= ~SslPolicyErrors.RemoteCertificateNameMismatch;
#endif

    if (sslPolicyErrors == SslPolicyErrors.None) {
      return true;
    }
    else {
      // エラーがあれば標準エラーに表示
      Console.Error.WriteLine(sslPolicyErrors);
      return false;
    }
  }
}

SSL/TLS接続時の動作をデフォルトからカスタマイズしたい場合は、PopClient.Connect()メソッドの引数に適切なコールバックメソッドを指定してください。
(このドキュメントは作成中です)

操作

接続・切断およびメールボックスに対する操作はPopClientクラス、個々のメッセージに対する操作はPopMessageInfoクラスのインスタンスを用いて行います。 PopMessageInfoクラスのインスタンスは、PopClient.GetMessage()メソッドなどで取得します(直接インスタンスを作成することはできません)。

接続・切断

以下は接続・切断に関するメソッドとプロパティです。

PopClientクラスのメソッド
メソッド 解説 対応するPOPコマンド
Connect(string)
Connect(ICredentialsByHost)
PopClient.Profileプロパティで指定された内容、および引数で指定されたパスワード(もしくはICredentialsByHost)を使って接続・認証を試みます。
接続・認証できた場合は、サーバの能力も取得します。
USER/PASS, STLS, APOP, AUTH, CAPA
BeginConnect(string)
BeginConnect(string, AsyncCallback, object)
BeginConnect(ICredentialsByHost)
BeginConnect(ICredentialsByHost, AsyncCallback, object)
Connect()メソッドと同じ処理を非同期的に実行します。 オプションでコールバックメソッドを指定できます。 USER/PASS, STLS, APOP, AUTH, CAPA
EndConnect(IAsyncResult) BeginConnecto()で開始した非同期接続を完了します。 BeginConnect()を呼び出した場合、EndConnect()で操作を完了する必要があります。 -
Logout() ログアウトします。 削除マークが付けられているメッセージは削除されます。 QUIT
Disconnect()
IDisposable.Dispose()
ログアウトせずに切断します。 削除マークが付けられているメッセージがあっても削除されません。 -
KeepAlive() 自動ログアウトタイマの更新、セッションの接続性確認などに使います。 NOOP
PopClientクラスのプロパティ
プロパティ 解説
Profile 接続先のホスト・ポート・ユーザ名などを取得/設定します。 コンストラクタで指定した内容はこのプロパティに反映されます。
Timeout コマンド処理のタイムアウト時間をミリ秒単位で取得/設定します。
SendTimeout ソケット送信時のタイムアウト時間をミリ秒単位で取得/設定します。
ReceiveTimeout ソケット受信時のタイムアウト時間をミリ秒単位で取得/設定します。
IsConnected 接続中かどうかを表す値を取得します。
ServerCapabilities サーバがサポートする機能の一覧(CAPAコマンドの結果)を取得します。
IDisposable.Dispose()
PopClientクラスはIDisposableインターフェイスを実装しています。 IDisposable.Dispose()を呼び出した場合、削除マークが付けられているメッセージを削除せずに切断します。 usingステートメントを使って記述する場合でメッセージを削除したい場合は、切断する前にLogout()メソッド呼び出すようにしてください。
Logout(), Disconnect(), IDisposable.Dispose()
これらのメソッドのいずれかを呼び出した後でも、再度PopClient.Connect()を呼び出すことで同じインスタンスを使って再度接続することはできます。
Timeout, SendTimeout, ReceiveTimeout
これらのプロパティはそれぞれPopClient.Profile.Timeout, PopClient.Profile.SendTimeout, PopClient.Profile.RecieveTimeoutを設定することと同じですが、接続後はPopClient.Profileの値を変更してもPopClientの動作には反映されません。 接続後はPopClient.Timeout, PopClient.SendTimeout, PopClient.ReceiveTimeoutプロパティを設定してください。
ServerCapabilities
切断されている状態で取得しようとした場合(PopClient.IsConnectedがfalseの場合)、例外をスローします。

メールボックスに対する操作

以下はメールボックス操作に関するメソッドとプロパティです。

PopClientクラスのメソッド
メソッド 解説 対応するPOPコマンド
CancelDelete() すべてのメッセージに対して削除マークを元に戻し、削除要求をキャンセルします。 PopMessageInfo.MarkAsDeleted()で行った操作がキャンセルされます。 RSET
PopClientクラスのプロパティ
プロパティ 解説
MessageCount メールボックスに存在するメッセージの数を取得します。
TotalSize メールボックスに存在するメッセージの総サイズをバイト単位で取得します。
MessageCount, TotalSize
このプロパティは、値が未取得の場合、もしくはPopClient.CancelDelete()やPopMessageInfo.MarkAsDeleted()で状態が更新されている場合、STATコマンドを発行して最新の値を取得します。 切断されている状態で取得しようとした場合(PopClient.IsConnectedがfalseの場合)、例外をスローします。

メッセージの取得

以下はメールボックスにあるメッセージを取得するメソッドの一覧です。

PopClientクラスのメソッド
メソッド 解説 対応するPOPコマンド
GetMessage(long)
GetMessage(long, bool)
指定された番号のメッセージを取得します。 オプションでメッセージのIDも取得します。 最初の番号は1です。 LIST, UIDL
GetMessage(string) 指定されたIDを持つメッセージを取得します。 LIST, UIDL
GetFirstMessage()
GetFirstMessage(bool)
メールボックスにある最初のメッセージを取得します。 オプションでメッセージのIDも取得します。
このメソッドを呼び出すことは、メッセージ番号に1を指定してGetMessage()を呼び出すこと同じです。
LIST, UIDL
GetLastMessage()
GetLastMessage(bool)
メールボックスにある最後のメッセージを取得します。 オプションでメッセージのIDも取得します。
このメソッドを呼び出すことは、メッセージ番号にPopClient.MessageCountを指定してGetMessage()を呼び出すこと同じです。
LIST, UIDL
GetMessages()
GetMessages(bool)
メールボックスにあるすべてのメッセージを取得します。 オプションでメッセージのIDも取得します。 LIST, UIDL
  • これらのメソッドは、接続してから切断するまでの間、同じ番号のメッセージに対しては常に同じPopMessageInfoインスタンスを返します。
  • これらのメソッドが返すPopMessageInfoインスタンスは、現在の接続の間のみ有効です。 切断した後にインスタンスのメンバを参照しようとした場合、例外をスローします。 また再接続しても有効にはならないので、再接続後にメソッドを呼び出して新たにインスタンスを取得しなおしてください。
  • これらのメソッドを呼び出し、PopMessageInfoインスタンスを取得した時点ではメッセージの本文は取得しません。 メッセージ本文を取得するには、取得したPopMessageInfoインスタンスに対してOpen*(), Read*(), Save()などのメソッドを呼び出す必要があります。
  • これらのメソッドを呼び出す際、該当するメッセージ存在しない場合はPopMessageNotFoundException、削除マークが付けられている場合はPopMessageDeletedExceptionをスローします。
  • メールボックスにあるメッセージが0件の場合でも、PopClient.GetMessages()は例外をスローせず空のIEnumerable<PopMessageInfo>を返します。

メッセージ本文の取得

以下はメッセージ本文の取得に関するメソッド・プロパティの一覧です。

PopMessageInfoクラスのメソッド
メソッド 解説 対応するPOPコマンド
OpenRead()
およびオーバーロード
メッセージ本文を読み込むためのStreamを取得します。 RETRまたはTOP
OpenText()
およびオーバーロード
メッセージ本文を読み込むためのStreamReaderを取得します。 RETRまたはTOP
ReadAllBytes()
およびオーバーロード
メッセージ本文をbyte[]で取得します。 RETRまたはTOP
ReadAllLines()
およびオーバーロード
メッセージ本文をstring[]で取得します。 RETRまたはTOP
ReadAllText()
およびオーバーロード
メッセージ本文をstringで取得します。 RETRまたはTOP
ReadLines()
およびオーバーロード
メッセージ本文をIEnumerable<string>で取得します。 RETRまたはTOP
ReadAs<TOutput>(Converter<Stream, TOutput>)
およびオーバーロード
メッセージ本文を読み込むためのStreamを取得し、指定されたConverter<Stream, TOutput>で変換された結果を取得します。 RETRまたはTOP
ReadAs<TOutput>(Converter<StreamReader, TOutput>)
およびオーバーロード
メッセージ本文を読み込むためのStreamReaderを取得し、指定されたConverter<StreamReader, TOutput>で変換された結果を取得します。 RETRまたはTOP
Save(string)
およびオーバーロード
メッセージ本文を指定されたファイルに保存します。 RETRまたはTOP
WriteTo(Stream)
およびオーバーロード
メッセージ本文を指定されたStreamに書き込みます。 RETRまたはTOP
WriteTo(BinaryWriter)
およびオーバーロード
メッセージ本文を指定されたBinaryWriterに書き込みます。 RETRまたはTOP
PopClientクラスのプロパティ
プロパティ 解説
DeleteAfterRetrieve PopMessageInfoクラスでメッセージ本文を取得する際、正常に取得できたらメッセージを削除マーク済みにするかどうかを取得/設定します。 このプロパティの値は、上記のメソッドの動作を変更します。
  • PopClient.DeleteAfterRetrieveプロパティにtrueが指定されている場合、メッセージの取得に成功した時点で自動的にMarkAsDeleted()メソッドが呼び出され、メッセージは削除マーク済みにされます。
  • すべてのメソッドは、既定ではRETRコマンドを用いてメッセージ本文全体を取得します。
    maxLinesを引数に取るバージョンのオーバーロードを使用することで、TOPコマンドを用いてメッセージ本文のうちヘッダ部分とボディ部分の指定した行数のみを取得することもできます。
  • すべてのメソッドは、取得したメッセージ本文をキャッシュしません。 メソッド呼び出しの度にメッセージ本文のダウンロードを行います。 同じメッセージを何度も処理する必要がある場合は、WriteTo()メソッドでMemoryStream等に書き出すか、Save()メソッドでファイルに保存するなどしてください。
  • インスタンスを作成したPopClientを切断した後にこれらのメソッド・プロパティを呼び出そうとした場合、PopProtocolViolationExceptionをスローします。
  • OpenText(), ReadAllLines()などのメソッドは、既定ではstringへのデコードにISO-8859-1を使用します。
    Encodingを引数に取るバージョンのオーバーロードを使用することで、指定したエンコーディングでデコードすることもできます。
    これらのメソッドはContent-Typeヘッダのcharsetパラメータや、Content-Transfer-Encodingヘッダなどに指定されている値は考慮せずにデコードを行います。 MIMEメッセージとしてパース・デコードするには、OpenRead()やReadAllBytes()などのメソッドで本文を取得した後、適切な処理を施してください。

メッセージに対する操作

以下はメッセージの操作に関するメソッドとプロパティです。

PopMessageInfoクラスのメソッド
メソッド 解説 対応するPOPコマンド
MarkAsDeleted() メッセージを削除マーク済みにします。 すでに削除マーク済み(IsMarkedAsDeletedがtrue)の場合は何もしません。 DELE
PopMessageInfoクラスのプロパティ
プロパティ 解説
IsMarkedAsDeleted メッセージが削除マーク済みかどうかを表す値を取得します。
MessageNumber メッセージの番号を取得します。
Length メッセージのサイズをバイト単位で取得します。
UniqueId メッセージのID(UIDLコマンドの結果)を取得します。
  • PopClient.GetMessage()等のメソッドでIDを取得するように指定しなかった場合、UniqueIDプロパティの値を参照する際、コマンドを発行してから結果を返します。
  • インスタンスを作成したPopClientを切断した後にこれらのメソッド・プロパティの値を参照しようとした場合、PopProtocolViolationExceptionをスローします。

例外

PopClientクラスおよびPopMessageInfoクラスからは以下の例外をスローします。 ここに明記している以外の例外クラス・状況でスローされる場合があります。

スローする例外と状況
例外クラス 状況 スローする可能性のあるメソッド
PopConnectionException 接続に失敗した PopClient.Connect()
PopUpgradeConnectionException SSL/TLSへのアップグレードに失敗した
証明書を検証した結果無効と判断した
PopClient.Connect()
PopAuthenticationException 認証に失敗した
試行できる認証メカニズムが無い
PopClient.Connect()
PopErrorResponseException サーバがエラー応答を返した PopClientおよびPopMessageInfoの各メソッド・プロパティ
PopProtocolViolationException プロトコル上不正な操作を行おうとした PopClientおよびPopMessageInfoの各メソッド・プロパティ
PopMessageDeletedException 削除マーク済み(PopMessageInfo.IsMarkedAsDeletedがtrue)のメッセージに対して操作を行おうとした PopClient.GetMessage()
PopMessageInfoの各メソッド・プロパティ
PopMessageNotFoundException 指定された番号もしくはIDを持つメッセージが存在しない PopClient.GetMessage()
PopClient.GetMessages()
InvalidOperationException 切断された状態(PopClient.IsConnectedがfalse)で操作を行おうとした
非同期接続中に新たに接続を開始しようとした
PopClientおよびPopMessageInfoの各メソッド・プロパティ
TimeoutException (System) PopClient.Timeout, SendTimeout, ReceiveTimeoutプロパティで指定されている時間内に操作が完了しなかった PopClientおよびPopMessageInfoの各メソッド・プロパティ

それぞれの例外クラスの継承関係は次のとおりです。

  • System.SystemException
    • Smdn.Net.Pop3.PopExcetion
      • Smdn.Net.Pop3.Protocol.PopConnectionException
        • Smdn.Net.Pop3.Protocol.PopUpgradeConnectionException
      • Smdn.Net.Pop3.PopInvalidOperationException
        • Smdn.Net.Pop3.PopErrorResponseException
          • Smdn.Net.Pop3.PopAuthenticationException
        • Smdn.Net.Pop3.PopProtocolViolationException
          • Smdn.Net.Pop3.Client.PopMessageDeletedException
      • Smdn.Net.Pop3.Client.PopMessageNotFoundException

非同期操作・スレッドセーフティ

現時点で非同期操作をサポートするメソッドは、PopClient.BeginConnect()/EndConnect()のみです。 PopMessageInfo.OpenRead()については、今後のバージョンで非同期操作をサポートする予定ですが、それ以外については予定はありません。

また現時点では、上記の非同期操作用のメソッドを除き、PopClientクラスおよびPopMessageInfoクラスはスレッドセーフではありません。 内部で使用している実装はスレッドセーフなので、処理内容によっては問題なく動作するかもしれませんが、保証はできません。 個々のインスタンスは同一スレッド内で使用してください。 今後のバージョンでスレッドセーフティを保証した実装に改善する予定です。

なお、アプリケーションドメインをまたがる使用については全く考慮していないため、動作および安全性の保証はできません。


PopWebRequest/PopWebResponseクラス (Smdn.Net.Pop3.WebClients.dll)

ここではPopWebRequest/PopWebResponseクラスおよびSmdn.Net.Pop3.WebClients名前空間のクラスの詳細と使い方を紹介します。

概説

基本的にはHttpWebRequest/HttpWebResponseやFtpWebRequest/FtpWebResponseを用いた操作と同様で、

  1. リクエストURLを指定してWebRequest.CreateメソッドでPopWebRequestのインスタンスを作成
  2. Method、Credentialsなどのプロパティを指定
  3. PopWebRequest.GetResponseでリクエストを実行、レスポンスを取得
  4. 取得したPopWebResponseインスタンスを参照、必要に応じてPopWebResponse.GetResponseStreamメソッドを呼び出す

の順でPOPコマンドを実行、レスポンスを取得します。 以下でPopWebRequest/PopWebResponseクラスに固有な部分について解説します。

pop, popsスキームの登録

WebRequestクラスがpopスキームおよびpopsスキームのURLを処理できるように、PopWebRequest/PopWebResponseクラスを使う前にPopWebRequestCreator.RegisterPrefixメソッドを呼び出しておく必要があります。

このメソッドはWebRequest.RegisterPrefixメソッドを呼び出し、popスキームおよびpopsスキームに対してPopWebRequestCreatorを関連付けます。

記述例
using System;
using System.Net;
using Smdn.Net.Pop3;
using Smdn.Net.Pop3.WebClients;

  :
  :
// popおよびpopsスキームの登録
PopWebRequestCreator.RegisterPrefix();

// PopWebRequestインスタンスの作成
var request = WebRequest.Create("pop://user@pop.example.net/");
  :
  :

リクエストURLと接続・認証時の動作

接続と認証

接続・認証時の動作は、接続と認証で解説したとおりリクエストURLに記述されるスキーム・ホスト名・ポート・認証方式・ユーザ名に基づいて決まります。

リクエストURLで記述されない接続・認証時の動作は、PopWebRequestクラスのプロパティで指定できますPopWebRequestのプロパティも合わせて参照してください

接続・認証時の動作を指定するPopWebRequestクラスのプロパティ
プロパティ 解説
Credentials 認証時に使用する資格情報(パスワード等)を指定します
UseTlsIfAvailable 認証を開始する前に、可能ならSSL/TLSへアップグレードするかどうかを指定します
UsingSaslMechanisms 認証時に試行する認証メカニズムを指定します
AllowInsecureLogin 接続がSSL/TLSで保護されていない場合でも、平文またはAPOPによる認証を許可するかどうか指定します

以下はコード上での記述と接続・認証時の動作の例です。 実際にSSL/TLSを使用する場合は証明書の検証等が必要になります。

記述例
// popおよびpopsスキームの登録
PopWebRequestCreator.RegisterPrefix();

/*
 * ホスト"pop.example.net"のPOPSデフォルトポート(995)にSSL/TLSを用いて接続。
 * ユーザ名に"user"、パスワードに"pass"を使用。 認証方式は対応しているものを順に試行。
 */
var request1 = WebRequest.Create(new Uri("pops://user@pop.example.net/"));

request1.Credentials = new NetworkCredential("user", "pass");

/*
 * ホスト"pop.example.net"のポート10110に接続、可能ならSSL/TLSにアップグレード。
 * ユーザ名に"user"、パスワードに"pass"を使用。 認証方式はAPOPを試行。
 */
var request2 = WebRequest.Create(new Uri("pop://user;AUTH=+APOP@pop.example.net:10110/")) as PopWebRequest;

request2.UseTlsIfAvailable = true;
request2.Credentials = new NetworkCredential("user", "pass");

/*
 * ホスト"pop.example.net"のポート10110にSSL/TLSを用いて接続。
 * ユーザ名に"user"、パスワードに"pass"を使用。 認証方式はDIGEST-MD5を試行。
 */
var request3 = WebRequest.Create(new Uri("pops://user;AUTH=DIGEST-MD5@pop.example.net:10110/")) as PopWebRequest;

request3.Credentials = new NetworkCredential("user", "pass");

/*
 * ホスト"pop.example.net"のPOPデフォルトポート(110)に接続、SSL/TLSは使用しない。
 * ユーザ名に"user"、パスワードに"pass"を使用。 認証方式はDIGEST-MD5, CRAM-MD5, APOP, USER/PASSの順に試行。
 */
var request4 = WebRequest.Create(new Uri("pop://;AUTH=*@pop.example.net/")) as PopWebRequest;

request4.UseTlsIfAvailable = false;
request4.UsingSaslMechanisms = new[] {"DIGEST-MD5", "CRAM-MD5", "+APOP"};
request4.Credentials = new NetworkCredential("user", "pass");

/*
 * ホスト"pop.example.net"のPOPデフォルトポート(110)に接続、可能ならSSL/TLSを使用する。
 * ユーザ名に"user"、パスワードに"pass"を使用。
 * 認証方式はCRAM-MD5, PLAIN, APOP, USER/PASSの順に試行。 ただしSSL/TLSが使用できない場合、平文による認証を許可しない。
 */
var request5 = WebRequest.Create(new Uri("pop://user@pop.example.net/")) as PopWebRequest;

request5.UseTlsIfAvailable = true;
request5.UsingSaslMechanisms = new[] {"CRAM-MD5", "PLAIN"};
request5.AllowInsecureLogin = false;
request5.Credentials = new NetworkCredential("user", "pass");

/*
 * GMailのアカウント"user"にPOPで接続。 パスワードは"pass"を使用。
 */
var request6 = WebRequest.Create(new Uri("pops://user@pop.gmail.com/"));

request6.Credentials = new NetworkCredential("user", "pass");

証明書の選択と検証

デフォルトでは、接続・認証時にPopSessionManagerクラスの以下のメンバを参照して証明書の選択と検証を行います。

SSL/TLS接続時に参照するメンバ(いずれもクラスプロパティ)
PopWebRequestクラスが参照するメンバ
X509Certificate2Collection PopSessionManager.ClientCertificates
RemoteCertificateValidationCallback PopSessionManager.ServerCertificateValidationCallback
RemoteCertificateValidationCallback PopSessionManager.ClientCertificateSelectionCallback

以下は証明書の検証を行う簡単な例です。

証明書の検証を行う例
using System;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using Smdn.Net.Pop3;
using Smdn.Net.Pop3.WebClients;

public class ValidateServerCerts {
  public static void Main(string[] args)
  {
    PopWebRequestCreator.RegisterPrefix();

    // 証明書の検証を行うコールバックメソッドを指定
    PopSessionManager.ServerCertificateValidationCallback += ValidateRemoteCertificate;

    using (var client = new WebClient()) {
      client.Credentials = new NetworkCredential("user", "pass");
      client.DownloadFile("pops://user@localhost/;MSG=1", "sample.eml");
    }
  }

  private static bool ValidateRemoteCertificate(object sender,
                                                X509Certificate certificate,
                                                X509Chain chain,
                                                SslPolicyErrors sslPolicyErrors)
  {
#if DEBUG
    // デバッグ時のみSslPolicyErrors.RemoteCertificateNameMismatchを無視
    if ((int)(sslPolicyErrors & SslPolicyErrors.RemoteCertificateNameMismatch) != 0)
      sslPolicyErrors &= ~SslPolicyErrors.RemoteCertificateNameMismatch;
#endif

    if (sslPolicyErrors == SslPolicyErrors.None) {
      return true;
    }
    else {
      // エラーがあれば標準エラーに表示
      Console.Error.WriteLine(sslPolicyErrors);
      return false;
    }
  }
}

SSL/TLS接続時の動作をデフォルトからカスタマイズしたい場合は、PopSessionManager.CreateSslStreamCallbackに適切なコールバックメソッドを指定してください。

リクエストURLとリクエスト・レスポンスの動作

POP URL(RFC 2384)ではPOPサーバ(メールボックス)を表す形式しか規定されていませんが、本ライブラリでは独自の拡張としてメッセージ番号を指定してリクエストできるようにしてあります。 リクエストURLにメッセージ番号(;MSG=x)が含まれているかどうかでリクエストの対象が変わります。

リクエストURLの形式とリクエストの対象
リクエストURLの形式 リクエストの対象
pop://pop.example.net/ URLで指定されたメールボックス
pop://pop.example.net/;MSG=1 URLで指定されたメールボックスにあるメッセージのうち、メッセージ番号に該当する特定のメッセージ

WebRequest.Createメソッドが返すWebRequestインスタンスのMethodプロパティにデフォルトで設定される値は、リクエストURLの形式によって決まります。

PopWebRequest.GetResponseの動作は、リクエストURLで表されるリクエストの対象と、WebRequest.Methodプロパティに設定する値によって変わります。 以下でリクエストURLの形式とMethodプロパティに指定可能な値、リクエストとレスポンスについて解説します。 PopWebRequestクラスのプロパティのうち、リクエストに影響するその他のプロパティについてはPopWebRequestのプロパティで説明します。

メールボックスに対するリクエスト

リクエストURLがメールボックスを表す場合(pop://pop.example.net/の形式)のリクエストとレスポンスの動作は次のとおりです。

Methodプロパティ リクエスト 解説 レスポンス
Methodプロパティ リクエスト 解説 レスポンス
PopWebRequestMethods.List, "LIST"
(デフォルト)
LISTコマンドを送信してメールボックスにあるメッセージの一覧を取得します。 - 取得したメッセージの一覧は、PopWebResponse.ScanListsプロパティに設定されます。
PopWebRequestMethods.Rset, "RSET" RSETコマンドを送信してDELEコマンドの操作をキャンセルします。 -
PopWebRequestMethods.Stat, "STAT" STATコマンドを送信してメールボックスにあるメッセージの数と総サイズを取得します。 - 取得したメッセージ数と総サイズは、PopWebResponse.DropListプロパティに設定されます。
PopWebRequestMethods.Uidl, "UIDL" UIDLコマンドを送信してメールボックスにあるメッセージのIDの一覧を取得します。 - 取得したIDの一覧は、PopWebResponse.UniqueIdListsプロパティに設定されます。

特定のメッセージに対するリクエスト

リクエストURLが特定のメッセージを表す場合(pop://pop.example.net/;MSG=1の形式)のリクエストとレスポンスの動作は次のとおりです。

Methodプロパティ リクエスト 解説 レスポンス
Methodプロパティ リクエスト 解説 レスポンス
PopWebRequestMethods.Retr, "RETR"
(デフォルト)
RETRコマンドを送信してメッセージの本文を取得します。 - メッセージ本文はWebResponse.GetResponseStreamメソッドが返すStreamから読み込めます。 取得したメッセージのサイズはWebResponse.ContentLengthプロパティに設定されます。
PopWebRequestMethods.Dele, "DELE" DELEコマンドを送信してメッセージを削除します。 - -
PopWebRequestMethods.List, "LIST" LISTコマンドを送信してメッセージの番号とサイズを取得します。 - 取得したメッセージ番号とサイズは、PopWebResponse.ScanListsプロパティに設定されます。
PopWebRequestMethods.Top, "TOP" TOPコマンドを送信してメッセージをヘッダ部分を取得します。 現時点ではTOPで取得するボディ部分の行数は指定できません(常に0を指定します)。 メッセージのヘッダ部分はWebResponse.GetResponseStreamメソッドが返すStreamから読み込めます。 取得したヘッダ部分のサイズはWebResponse.ContentLengthプロパティに設定されます。
PopWebRequestMethods.Uidl, "UIDL" UIDLコマンドを送信してメッセージの番号とIDを取得します。 - 取得したメッセージ番号とIDは、PopWebResponse.UniqueIdListsプロパティに設定されます。

リクエスト対象に制限されないリクエスト

リクエストURLの形式によらず使用可能なコマンドのリクエストとレスポンスの動作は次のとおりです。

Methodプロパティに指定可能な値 リクエスト 解説 レスポンス
Methodプロパティに指定可能な値 リクエスト 解説 レスポンス
PopWebRequestMethods.NoOp, "NOOP" NOOPコマンドを送信します。 このコマンドは何もしません。 自動ログアウトタイマの更新、セッションの接続性確認などに使います。 -

PopWebRequestクラスのプロパティ

PopWebRequestクラスにはMethodプロパティ以外にもコマンド送信時の動作を制御するためのプロパティを用意してあります。 プロパティの値によってリクエスト内容と動作が変わります。

PopWebRequestクラスのプロパティ
プロパティ デフォルト 対象となるコマンド 解説
KeepAlive true 全て リクエストが終了した後もセッションを維持するかどうかを指定します。 trueの場合はリクエストが終了してもセッションは維持し、falseの場合はリクエストの度にセッションを開きリクエスト終了と同時にセッションを閉じます。
UseTlsIfAvailable true 全て(認証時) サーバがサポートしている場合、認証を行う前にSSL/TLSを使用した接続にアップグレードします。
UsingSaslMechanisms {"DIGEST-MD5", "CRAM-MD5", "NTLM"} 全て(認証時) 認証メカニズムが指定されていない場合に、試行する認証メカニズムとその順番を配列で指定します。 サーバ・クライアントの両方がサポートするメカニズム以外が指定されている場合は無視します。
AllowInsecureLogin false 全て(認証時) 接続がSSL/TLSで保護されていない場合でも、平文またはAPOPによる認証を許可するかどうか指定します。
DeleteAfterRetrieve false RETR RETRコマンドでのメッセージの受信が成功した場合、同じメッセージに対して自動的にDELEコマンドを発行します。
ExpectedErrorResponseCodes null 全て サーバから返されることが予期されるエラーレスポンスコードの配列を指定します。 サーバからこれらのレスポンスコードが返された場合は、エラーレスポンスでもWebExceptionがスローされません。

WebRequestクラスから継承されるプロパティのうち、以下のプロパティは使用することができます。

WebRequestクラスから継承されるプロパティ
プロパティ デフォルト 対象となるコマンド 解説
Timeout -1 全て リクエストを開始してからレスポンスを取得するまでのタイムアウト時間を指定します。
ReadWriteTimeout 300000 全て ソケット送受信のタイムアウト時間を指定します。

PopWebRequestクラスのデフォルト値の変更

PopWebRequestDefaultsクラスのプロパティの値を変更することにより、PopWebRequestクラスのプロパティに設定されるデフォルト値を一括して指定することができます。 全てのリクエストでデフォルトの値を変更したい場合は、PopWebRequestDefaultsクラスのプロパティの値を変更してください。

PopWebRequestDefaultsクラスにはPopWebRequestプロパティと同名のプロパティを用意してあります。

アプリケーション構成ファイルでの設定

アプリケーション構成ファイルを記述することにより、pop, popsスキームの登録と、PopWebRequestDefaultsクラスのデフォルト値を変更することができます。 コード上での変更をしたくない場合、する必要がない場合などは、アプリケーション構成ファイルに設定を記述することができます。 以下はアプリケーション構成ファイルの記述例です。

アプリケーション構成ファイルの記述例
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <!-- smdn.net.pop3.client要素を処理するためのセクションハンドラクラスの設定 -->
    <sectionGroup name="smdn.net.pop3.client">
      <section name="webRequestDefaults" type="System.Configuration.DictionarySectionHandler, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
    </sectionGroup>
  </configSections>
  <system.net>
    <!-- pop, popsスキームの登録 -->
    <webRequestModules>
      <add prefix="pop"   type="Smdn.Net.Pop3.WebClients.PopWebRequestCreator, Smdn.Net.Pop3.WebClients" />
      <add prefix="pops"  type="Smdn.Net.Pop3.WebClients.PopWebRequestCreator, Smdn.Net.Pop3.WebClients" />
    </webRequestModules>
  </system.net>
  <smdn.net.pop3.client>
    <!-- PopWebRequestDefaultsクラスのデフォルト値の変更 -->
    <webRequestDefaults>
      <add key="timeout" value="-1"/>
      <add key="readWriteTimeout" value="-1"/>
      <add key="useTlsIfAvailable" value="true"/>
      <add key="deleteAfterRetrieve" value="false"/>
      <add key="keepAlive" value="true"/>
      <add key="usingSaslMechanisms" value="DIGEST-MD5, CRAM-MD5, NTLM"/>
      <add key="allowInsecureLogin" value="false"/>
      <add key="expectedErrorResponseCodes" value="SYS/TEMP"/>
    </webRequestDefaults>
  </smdn.net.pop3.client>
</configuration>


  
    
    
      
]]>

記述内容についてはネットワーク設定スキーマおよび構成セクション スキーマも合わせて参照してください。

例外

PopWebRequestからはSystem.Net.WebExceptionまたはSystem.Net.ProtocolViolationExceptionをスローします。 WebExceptionをスローする場合は、エラーの状況によりWebException.StatusプロパティとInnerExceptionプロパティに値が設定されます。 また、サーバがエラー応答を返した場合は、Responseプロパティも設定されます。
InnerExceptionプロパティに設定される例外クラスについては、例外を参照してください。

非同期操作・スレッドセーフティ

WebRequestから継承されるBeginGetResponse()/EndGetResponse()メソッドは実装済みのため、これらのメソッドを使って非同期操作を行えます。 ただし、.NET Framework上では、WebClient.OpenReadAsync()、WebClient.Download*Async()などのメソッドを使用した非同期操作は正しく動作しないようなので、現時点では使用できません。


サンプルコード

各クライアント実装を用いたサンプルコードを例示します。 証明書の検証を省略している箇所がありますが、実際に使用するコードでは必ず適切な検証を行うように書き換えてください。

PopClient

メッセージをダウンロードしてファイルに保存する

using System;

using Smdn.Net.Pop3;
using Smdn.Net.Pop3.Client;

class SaveToFile {
  public static void Main(string[] args)
  {
    using (var client = new PopClient(new Uri("pop://user;AUTH=DIGEST-MD5@localhost/"))) {
      client.Connect("pass");

      // メールボックスにある1件目のメッセージをダウンロード
      var message = client.GetFirstMessage();

      // ファイルsample.emlに保存
      message.Save("sample.eml");
    }
  }
}

同じ処理をPopWebRequestで記述した例

Gmailアカウントのメールボックスから、最新のメッセージをダウンロードする

using System;
using System.Net;
using System.Text;

using Smdn.Net.Pop3;
using Smdn.Net.Pop3.Client;
using Smdn.Net.Pop3.Protocol.Client;

class GpopRetrieveRecent {
  public static void Main(string[] args)
  {
    PopConnection.ServerCertificateValidationCallback += delegate {
      // 証明書の検証は省略 (エラーを無視してすべて受け入れる)
      return true;
    };

    using (var client = new PopClient(new Uri("pops://user@pop.gmail.com/"))) {
      client.Connect("pass");

      if (client.MessageCount == 0) {
        // メールボックスにメッセージがない
        Console.WriteLine("no messages");
        return;
      }

      // メールボックスにある最後のメッセージを取得
      var message = client.GetLastMessage();

      // メッセージ本文をダウンロードし、iso-2022-jpでデコードして表示
      Console.WriteLine(message.ReadAllText(Encoding.GetEncoding("iso-2022-jp")));
    }
  }
}

同じ処理をPopWebRequestで記述した例

メールボックスにあるすべてメッセージをダウンロードしてファイルに保存する

using System;

using Smdn.Net.Pop3;
using Smdn.Net.Pop3.Client;
using Smdn.Net.Pop3.Protocol.Client;

class SaveAndDelete {
  public static void Main(string[] args)
  {
    PopConnection.ServerCertificateValidationCallback += delegate {
      // 証明書の検証は省略 (エラーを無視してすべて受け入れる)
      return true;
    };

    using (var client = new PopClient(new Uri("pops://user;auth=ntlm@localhost/"))) {
      client.Connect("pass");

      // メッセージ本文を取得したら削除する
      client.DeleteAfterRetrieve = true;

      // メールボックスにあるすべてのメッセージとIDを取得
      foreach (var message in client.GetMessages(true)) {
        // IDをファイル名として保存 (IDがファイル名として妥当かどうかのチェックは省略)
        message.Save(message.UniqueId + ".eml");
      }

      // ログアウトしてメッセージを削除
      client.Logout();
    }
  }
}

上記のコードは、次のコードと等価です。

        :
      client.DeleteAfterRetrieve = false;

      // メールボックスにあるすべてのメッセージとIDを取得
      foreach (var message in client.GetMessages(true)) {
        // IDをファイル名として保存 (IDがファイル名として妥当かどうかのチェックは省略)
        message.Save(message.UniqueId + ".eml");
        // 保存できたら削除マークを付ける
        message.MarkAsDeleted();
      }
        :
        :

メッセージのIDを使って新着メッセージをチェックする

using System;
using System.Collections.Generic;
using System.Threading;

using Smdn.Net.Pop3;
using Smdn.Net.Pop3.Client;

class CheckRecent {
  public static void Main(string[] args)
  {
    // 既知のメッセージID
    var ids = new List<string>();

    using (var client = new PopClient(new Uri("pop://user;auth=ntlm@localhost/"))) {
      client.Profile.UseTlsIfAvailable = false;

      for (;;) {
        client.Connect("pass");

        if (!client.ServerCapabilities.Has(PopCapability.Uidl))
          // サーバがUIDLをサポートしていない
          throw new ApplicationException("UIDL incapable");

        // メールボックスにあるすべてのメッセージを取得
        foreach (var message in client.GetMessages(true)) {
          // 既知のメッセージIDかどうかチェック
          if (ids.Contains(message.UniqueId)) {
            // 既知の場合は何もしない
          }
          else {
            // 新着の場合は、メッセージ本文のヘッダのみを取得
            foreach (var line in message.ReadLines(0)) {
              // ヘッダのうち"Subject:"で始まる行のみを表示
              if (line.StartsWith("Subject:", StringComparison.OrdinalIgnoreCase))
                Console.WriteLine("{0}: {1}", message.UniqueId, line);
            }

            // 既知のメッセージIDとして追加
            ids.Add(message.UniqueId);
          }
        }

        // いったん切断
        client.Disconnect();

        // 5分後に再接続する
        Thread.Sleep(TimeSpan.FromMinutes(5.0));
      }
    }
  }
}
();

    using (var client = new PopClient(new Uri("pop://user;auth=ntlm@localhost/"))) {
      client.Profile.UseTlsIfAvailable = false;

      for (;;) {
        client.Connect("pass");

        if (!client.ServerCapabilities.Has(PopCapability.Uidl))
          // サーバがUIDLをサポートしていない
          throw new ApplicationException("UIDL incapable");

        // メールボックスにあるすべてのメッセージを取得
        foreach (var message in client.GetMessages(true)) {
          // 既知のメッセージIDかどうかチェック
          if (ids.Contains(message.UniqueId)) {
            // 既知の場合は何もしない
          }
          else {
            // 新着の場合は、メッセージ本文のヘッダのみを取得
            foreach (var line in message.ReadLines(0)) {
              // ヘッダのうち"Subject:"で始まる行のみを表示
              if (line.StartsWith("Subject:", StringComparison.OrdinalIgnoreCase))
                Console.WriteLine("{0}: {1}", message.UniqueId, line);
            }

            // 既知のメッセージIDとして追加
            ids.Add(message.UniqueId);
          }
        }

        // いったん切断
        client.Disconnect();

        // 5分後に再接続する
        Thread.Sleep(TimeSpan.FromMinutes(5.0));
      }
    }
  }
}]]>

この例において、初回の接続の時点ではidsは空なので、メールボックスにあるメッセージはすべて新着として扱われます。

メッセージをダウンロードしてパース・デコードする

メッセージをダウンロードした後、Smdn.Formats.Mimeを使ってパース・デコードする例。

using System;

using Smdn.Net.Pop3;
using Smdn.Net.Pop3.Client;
using Smdn.Formats.Mime;

class DecodeMime {
  public static void Main(string[] args)
  {
    using (var client = new PopClient(new Uri("pop://user;auth=ntlm@localhost/"))) {
      client.Profile.UseTlsIfAvailable = false;

      client.Connect("pass");

      // すべてのメッセージを取得
      foreach (var message in client.GetMessages()) {
        // メッセージの本文を取得し、MimeMessage.Loadメソッドでパース・デコードした結果を得る
        var decodedMessage = message.ReadAs<MimeMessage>(MimeMessage.Load);

        // デコードしたメッセージの件名を表示
        Console.WriteLine("====[{0}: {1}]{2}",
                          message.MessageNumber,
                          decodedMessage.Headers["Subject"].Value,
                          new string('=', 32));

        // デコードした本文を表示
        if (parsedMessage.MimeType.TypeEqualsIgnoreCase("text"))
          Console.WriteLine(decodedMessage.ReadContentAsText());
        else
          Console.WriteLine("非テキストメッセージです");
      }
    }
  }
}
(MimeMessage.Load);

        // デコードしたメッセージの件名を表示
        Console.WriteLine("====[{0}: {1}]{2}",
                          message.MessageNumber,
                          decodedMessage.Headers["Subject"].Value,
                          new string('=', 32));

        // デコードした本文を表示
        if (parsedMessage.MimeType.TypeEqualsIgnoreCase("text"))
          Console.WriteLine(decodedMessage.ReadContentAsText());
        else
          Console.WriteLine("非テキストメッセージです");
      }
    }
  }
}]]>

PopWebRequest/PopWebResponse

メッセージをダウンロードしてファイルに保存する

using System;
using System.Net;

using Smdn.Net.Pop3.WebClients;

class SaveToFile {
  public static void Main(string[] args)
  {
    PopWebRequestCreator.RegisterPrefix();

    using (var client = new WebClient()) {
      client.Credentials = new NetworkCredential("user", "pass");

      // メールボックスにある1件目のメッセージをダウンロードしてファイルsample.emlに保存
      client.DownloadFile("pop://user;AUTH=DIGEST-MD5@localhost/;MSG=1", "sample.eml");
    }
  }
}

同じ処理をPopClientで記述した例

Gmailアカウントのメールボックスから、最新のメッセージをダウンロードする

using System;
using System.IO;
using System.Text;
using System.Net;

using Smdn.Net.Pop3;
using Smdn.Net.Pop3.WebClients;

class GpopRetrieveRecent {
  public static void Main(string[] args)
  {
    PopWebRequestCreator.RegisterPrefix();
    PopSessionManager.ServerCertificateValidationCallback += delegate {
      // 証明書の検証は省略 (エラーを無視してすべて受け入れる)
      return true;
    };

    var baseUrl = new Uri("pops://user@pop.gmail.com/");
    var credential = new NetworkCredential("user", "pass");

    // STATコマンドでメールボックスにあるメッセージの数を取得
    var request = WebRequest.Create(baseUrl) as PopWebRequest;

    request.Credentials = credential;
    request.Method = "STAT";
    request.KeepAlive = true; // リクエストが完了しても接続したままにする

    long recentMessageNumber;

    using (var response = request.GetResponse() as PopWebResponse) {
      recentMessageNumber = response.DropList.MessageCount;
    }

    if (recentMessageNumber == 0) {
      // メールボックスにメッセージがない
      Console.WriteLine("no messages");
      return;
    }

    // 取得するメッセージ番号を含む相対URLを作成
    var recentMessageUrl = string.Format("./;MSG={0}", recentMessageNumber);

    // RETRコマンドでメッセージの本文を取得
    request = WebRequest.Create(new Uri(baseUrl, recentMessageUrl)) as PopWebRequest;

    request.Credentials = credential;
    request.Method = "RETR";
    request.KeepAlive = false; // リクエストが完了したら切断する

    using (var response = request.GetResponse()) {
      using (var stream = response.GetResponseStream()) {
        // メッセージ本文をiso-2022-jpでデコードして表示
        var reader = new StreamReader(stream, Encoding.GetEncoding("iso-2022-jp"));

        Console.WriteLine(reader.ReadToEnd());
      }
    }
  }
}

同じ処理をPopClientで記述した例

PopSession

このクラスを直接使用することはできますが推奨はしません。 また、ドキュメントを用意する予定はありません。 内部実装の参照や改変などの参考程度に掲載します。

メッセージをダウンロード・削除する

Gmailに接続し、最新のメッセージを取得し、表示したあと削除するサンプル。

using System;
using System.IO;
using System.Text;
using System.Net;

using Smdn.Net.Pop3;
using Smdn.Net.Pop3.Client.Session;
using Smdn.Net.Pop3.Protocol.Client;

class Sample {
  public static void Main(string[] args)
  {
    PopConnection.ServerCertificateValidationCallback += delegate {
      // 証明書の検証は省略 (エラーを無視してすべて受け入れる)
      return true;
    };

    using (var session = new PopSession("pop.gmail.com", 995, PopConnection.CreateSslStream)) {
      var cred = new NetworkCredential("user", "pass");

      // 利用可能ならAPOP、そうでなければUSER/PASSでログイン
      var authenticationResult = session.ApopAvailable
        ? session.Apop(cred)
        : session.Login(cred);

      if (authenticationResult.Failed) {
        Console.Error.WriteLine("authentication failed");
        return;
      }

      // STATコマンドを発行
      PopDropListing dropList;

      if (session.Stat(out dropList).Failed) {
        Console.Error.WriteLine("STAT failed");
        return;
      }

      if (dropList.MessageCount == 0L) {
        // メールボックスにメッセージがない
        Console.Error.WriteLine("no message exists");
        return;
      }

      // RETRコマンドでメッセージ本文を取得・表示
      PopMessage message;

      if (session.Retr(dropList.MessageCount, out message).Failed) {
        Console.Error.WriteLine("RETR failed");
        return;
      }

      var reader = new StreamReader(message.Body, Encoding.GetEncoding("iso-2022-jp"));

      Console.WriteLine(reader.ReadToEnd());

      message.Dispose();

      // DELEコマンドでメッセージを削除マーク済みにする
      if (session.Dele(message.Number).Failed) {
        Console.Error.WriteLine("DELE failed");
        return;
      }

      // QUITコマンドでメッセージを削除してログアウト
      if (session.Quit().Failed) {
        Console.Error.WriteLine("QUIT failed");
        return;
      }
    }
  }
}