Smdn.Net.Imap4.Clientのドキュメントとサンプルです。 ここに記載されているものはversion 1.14時点のものです。

認証・接続のサンプルコードは§.接続・認証のコードと動作の例、IMAP操作を行う具体例は§.IMAP操作と各クラスの使用例に記載しています。

このドキュメントは完全ではないため、ライブラリの使用方法に関する疑問やここに記載されていない事項については掲示板へどうぞ。


§1 用語と表記

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

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

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

§2 ライブラリの構成

§2.1 アセンブリ構成

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

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

本ライブラリを使用する場合は、上記アセンブリへの参照を追加してください。

§2.2 クライアント実装

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

クライアント実装の種類と概要
名前空間 クラス 概要
Smdn.Net.Imap4.Client ImapClient
ImapMailboxInfo
ImapMessageInfoなど
IMAPの操作を抽象化したクラスです。 メッセージに対する操作をSystem.IO.FileInfo, DirectoryInfoクラスに似たインターフェイスで行えるようにしてあります。 このドキュメントではこれらのクラスについて解説しています。
Smdn.Net.Imap4.Client.Session ImapSession IMAPコマンドと1対1に対応するメソッドを持つクラスです。 IMAPの操作は抽象化していません。 仕様と1対1で対応するような実装にしてあります。 このクラスは上記のクライアント実装で内部的に使用しています。
直接使用することもできますが、インターフェイスを変更することがあるので推奨はできません。

§3 概略

ImapClientクラスはIMAPサーバに接続してIMAPの操作を行うためのクラスです。 以下の例はImapClientクラスの基本的な機能を使ってIMAPの操作を行う例です。 ImapClientクラスの詳細については以下で個別に解説していきます。

using System;

using Smdn.Net.Imap4;
using Smdn.Net.Imap4.Client;

class Sample {
  static void Main(string[] args)
  {
    // ユーザ名'user'、CRAM-MD5による認証でimap.example.netに接続するクライアントを作成
    using (ImapClient client = new ImapClient(new Uri("imap://user;AUTH=CRAM-MD5@imap.example.net/"))) {
      // パスワード'pass'で接続
      client.Connect("pass");

      // INBOXを開く
      using (ImapOpenedMailboxInfo inbox = client.OpenInbox()) {
        // UIDが1のメッセージを取得
        ImapMessageInfo message = inbox.GetMessageByUid(1);

        // ダウンロードしてファイルsample.emlに保存
        message.Save("sample.eml");
      }
    }
  }
}

§4 接続・認証とSSL/TLS

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

§4.1 接続と認証

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

§4.1.1 接続・認証のコードと動作の例

以下は接続・認証処理の記述例です。 個々のパラメータの指定方法と動作の詳細についてはこの後順に解説していきます。 なお、実際にSSL/TLSを使用する場合は証明書の検証等が必要になります。

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

client1.Connect("pass");

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

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

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

client3.Connect("pass");

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

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

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

/*
 * ホスト"imap.example.net"のIMAPデフォルトポート(143)に接続、可能ならSSL/TLSを使用する。
 * ユーザ名に"user"を使用、パスワードはNetworkCredentialから取得。
 * 認証方式はCRAM-MD5, PLAIN, IMAP LOGINの順に試行。 ただしSSL/TLSが使用できない場合、平文による認証を許可しない。
 */
var client5 = new ImapClient(new Uri("imap://user@imap.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"にIMAPで接続。 パスワードは"pass"を使用。
 */
var client6 = new ImapClient(new Uri("imaps://user@imap.gmail.com/"));

client6.Connect("pass");

§4.1.2 接続・認証時のパラメータ

接続・認証時のパラメータは、ImapClientクラスのコンストラクタまたはImapClient.Profileプロパティで指定します。 指定できるパラメータは次のとおりです。

接続・認証時のパラメータ
ImapClient.Profileのプロパティ 対応するImapClientコンストラクタの引数 意味と接続・認証時の動作 補足 詳細
接続に関するパラメータ Authority authority 接続先のURL (IMAP URL形式) Host, Port, SecurePort, UserName, AuthTypeに指定されているものと同等の値が反映されます §.接続に関するパラメータと動作
Host host 接続先のホスト名 Authorityのホスト名部分に指定されているものと同じ値が反映されます
Port port 接続先のポート番号 Authorityのポート番号部分に指定されているものと同じ値が反映されます
-1を指定した場合は標準のポート番号で接続します(imapsなら993、imapなら143)
SecurePort securePort 接続先のポートがSSL/TLSで保護されているかどうか
(SSL/TLSを使用して接続する必要があるかどうか)
Authorityに指定されているものと同じ設定が反映されます
スキーム部分がimapsならtrue、imapならfalseになります
UseTlsIfAvailable - 可能なら接続後にSSL/TLSへアップグレードするかどうか SecurePortがfalse(スキーム部分がimap)の場合のみ有効
デフォルト:true
認証に関するパラメータ UserName userName 認証時に使用するユーザー名 Authorityのユーザー名部分に指定されているものと同じ値が反映されます §.認証に関するパラメータと動作
AuthType authType 認証時に使用する方式 Authorityの認証方式部分に指定されているものと同じ値が反映されます
UsingSaslMechanisms - 認証時に試行するSASL認証方式 AuthTypeを指定しない(null)、あるいは"*"を指定した場合のみ有効
AllowInsecureLogin - 平文による安全でない認証を許可するかどうか 現在の接続がSSL/TLSで保護されていない場合のみ有効
保護されている場合は常に平文による認証を試行します
デフォルト:false
ImapClient.Profileのプロパティ 対応するImapClientコンストラクタの引数 意味と接続・認証時の動作 補足 詳細

なお、パスワードはImapClient.Connect()など接続用のメソッドの引数として指定します。 Profileプロパティやコンストラクタで指定することはできません。

ImapClientコンストラクタで指定した値はImapClient.Profileプロパティに反映されます。 また、ImapClient.Profileプロパティはシリアライズ可能なので設定ファイル等に保存されている値を読み込むこともできます。

ImapClientProfileを指定してImapClientのインスタンスを作成することもできます。 これにより同じ接続設定を使用して複数のインスタンスを作成することができます。

共通のImapClientProfileからインスタンスを作成する例
// ホスト"imap.example.net"のIMAPデフォルトポートに接続し、
// ユーザ名"user"で認証を行う設定を作成
var profile = new ImapClientProfile(new Uri("imap://user@imap.example.net/"));

// SSL/TLSが使用できない場合は、平文による認証を許可しない
profile.AllowInsecureLogin = false;

// 上記の設定でクライアントを2つ作成する
var c1 = new ImapClient(profile);
var c2 = new ImapClient(profile);

c1.Connect("pass");
c2.Connect("pass");

§4.1.3 接続・切断に関するメンバ

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

接続・切断に関するメソッド
クラス メソッド 解説 対応するIMAPコマンド
ImapClient Connect(string)
Connect(ICredentialsByHost)
ImapClient.Profileプロパティで指定された内容、および引数で指定されたパスワード(もしくはICredentialsByHost)を使って接続・認証を試みます。
接続・認証できた場合は、サーバの能力(サポートしている場合はID・名前空間も)を取得します。
LOGIN, AUTHENTICATE, STARTTLS, CAPABILITY
(サポートしている場合はID, NAMESPACEも)
BeginConnect(string)
BeginConnect(string, AsyncCallback, object)
BeginConnect(ICredentialsByHost)
BeginConnect(ICredentialsByHost, AsyncCallback, object)
Connect()メソッドと同じ処理を非同期的に実行します。 オプションでコールバックメソッドを指定できます。 LOGIN, AUTHENTICATE, STARTTLS, CAPABILITY
(サポートしている場合はID, NAMESPACEも)
EndConnect(IAsyncResult) BeginConnect()で開始した非同期接続を完了します。 BeginConnect()を呼び出した場合、EndConnect()で操作を完了する必要があります。 -
ConnectAsync(string)
ConnectAsync(ICredentialsByHost)
[.NET 4.0]
Connect()メソッドと同じ処理を非同期的に実行し、非同期操作を表すTaskを返します。
LOGIN, AUTHENTICATE, STARTTLS, CAPABILITY
(サポートしている場合はID, NAMESPACEも)
Logout() ログアウト処理を行った後に接続を切断します。 選択済みメールボックスの削除フラグが設定されているメッセージは削除されます。 CLOSE, LOGOUT
Disconnect()
IDisposable.Dispose()
ログアウト処理を行わずに接続を切断します。 削除フラグが設定されているメッセージがあっても削除されません。 -
Refresh() 自動ログアウトタイマの更新、セッションの接続性確認などに使います。 メールボックスを選択している場合は、メールボックスの状態も更新します。 サーバからメールボックスの状態に関する通知があった場合はイベントを発生させます。 NOOP
接続・切断に関するプロパティ
クラス プロパティ 解説
ImapClient Profile 接続先のホスト・ポート・ユーザ名などを取得/設定します。 コンストラクタで指定した内容はこのプロパティに反映されます。
Timeout 接続時およびコマンド処理のタイムアウト時間をミリ秒単位で取得/設定します。 Timeout.Infinite(-1)を指定した場合はタイムアウトしません。
SendTimeout ソケット送信時(コマンド送信)のタイムアウト時間をミリ秒単位で取得/設定します。 Timeout.Infinite(-1)を指定した場合はタイムアウトしません。
ReceiveTimeout ソケット受信時(レスポンス受信)のタイムアウト時間をミリ秒単位で取得/設定します。 Timeout.Infinite(-1)を指定した場合はタイムアウトしません。
IsConnected 接続中かどうかを表す値を取得します。
IsBusy 操作が進行中かどうかを表す値を取得します。 trueの場合、同一クライアントから他の操作を開始することはできません。
IsIdle IDLE中であるかどうかを表す値を取得します。 trueの場合、同一クライアントからIDLEの停止以外の操作を開始することはできません。
IDisposable.Dispose()
ImapClientクラスはIDisposableインターフェイスを実装しています。 IDisposable.Dispose()を呼び出した場合、削除マークが付けられているメッセージを削除せずに切断します。 usingステートメントを使って記述する場合でメッセージを削除したい場合は、切断する前にLogout()メソッド呼び出すようにしてください。
Logout(), Disconnect(), IDisposable.Dispose()
これらのメソッドのいずれかを呼び出した後でも、再度ImapClient.Connect()を呼び出すことで同じインスタンスを使って再度接続することはできます。
Timeout, SendTimeout, ReceiveTimeout
これらのプロパティはそれぞれImapClient.Profile.Timeout, ImapClient.Profile.SendTimeout, ImapClient.Profile.RecieveTimeoutを設定することと同じですが、接続後はImapClient.Profileの値を変更してもImapClientの動作には反映されません。 接続後はImapClient.Timeout, ImapClient.SendTimeout, ImapClient.ReceiveTimeoutプロパティを設定してください。
操作のタイムアウトと中断に関しての注意点は§.非同期操作の中断とタイムアウトを参照してください。

§4.1.4 接続に関するパラメータと動作

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

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

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

接続先URL*1
ImapClientクラスのコンストラクタで指定する引数authority
SecurePortプロパティ*2
ImapClientクラスのコンストラクタで指定する引数securePort、もしくはImapClient.Profile.SecurePortプロパティ
UseTlsIfAvailableプロパティ*3
ImapClient.Profile.UseTlsIfAvailableプロパティ

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

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

URLと接続動作の例
接続先URL 接続ポート SSL/TLS
imaps://user@imap.example.net/ 993 SSL/TLSを使用して接続
imaps://user@imap.example.net:10143/ 10143
imap://user@imap.example.net/ 143 SSL/TLSを使用しないで接続
もしくは可能なら接続後にSSL/TLSへアップグレード(UseTlsIfAvailable=trueの場合)
imap://user@imap.example.net:993/ 993

§4.1.5 認証に関するパラメータと動作

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

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

  1. AUTHENTICATEコマンド (サーバ・クライアントが対応している認証メカニズムを順に試行)
  2. LOGINコマンド (サーバが許可している場合のみ)

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

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

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

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

試行する認証メカニズムを制御するには、ImapClient.Profile.UsingSaslMechanismsプロパティの値を変更してください。 UsingSaslMechanismsプロパティにANONYMOUSが含まれていても匿名ログインは試行しません。

ImapClient.Profile.AllowInsecureLoginプロパティにfalseを指定した場合で、かつ現在の接続がSSL/TLSで保護されていない場合、平文による認証は試行されません(デフォルトはfalseです)。

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

§4.1.6 認証時に参照する資格情報

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

§4.2 SSL/TLSを使用した接続

§4.2.1 証明書の選択と検証

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

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

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

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

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

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

    using (var client = new ImapClient(new Uri("imaps://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接続時の動作をデフォルトからカスタマイズしたい場合は、ImapClient.Connect()メソッドの引数に適切なコールバックメソッドを指定してください。
(このドキュメントは作成中です)

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

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

§4.3 OAuth 2.0を使った認証

GMailがサポートするSASL XOAUTH2 Mechanismを使って認証を行う場合は、以下の例のようにImapClient.Connect()メソッドの引数にXOAuth2Mechanismクラス(Smdn.Security.Authentication.Sasl.Client名前空間)のインスタンスを指定します。

XOAUTH2による認証を行う例
using System;

using Smdn.Net.Imap4;
using Smdn.Net.Imap4.Client;
using Smdn.Security.Authentication.Sasl.Client;

public class XOAuth2Sample {
  static void Main(string[] args)
  {
    using (var client = new ImapClient(new Uri("imaps://user@localhost/"))) {
      // ユーザー名とアクセストークンを指定してXOAuth2Mechanismのインスタンスを作成
      var user = "someuser@example.com";
      var accessToken = "vF9dft4qmTc2Nvb3RlckBhdHRhdmlzdGEuY29tCg==";

      using (var oauthMechanism = new XOAuth2Mechanism(user, accessToken)) {
        try {
          // パスワードの代わりにXOAuth2Mechanismのインスタンスを指定して接続
          client.Connect(oauthMechanism);
        }
        catch (ImapAuthenticationException) {
          // 認証に失敗した場合は、サーバーが返送してきたエラーメッセージを取得・表示する
          Console.WriteLine(oauthMechanism.LastErrorResults);
        }
      }
    }
  }
}

XOAuth2Mechanismクラスは取得済みのアクセストークンを使用します。 XOAuth2Mechanismクラス自体にはアクセストークンの取得や更新を行う機能は実装していないため、必要に応じて他のライブラリを使用するなどしてください。

XOAuth2Mechanismクラスを継承してアクセストークンの取得や検証・更新の処理を組み込みたい場合は、以下のようにXOAuth2Mechanism.Initialize()メソッドをオーバーライドすることが出来ます。 XOAuth2Mechanism.Initialize()メソッドはImapClient.Connect()メソッドによる接続開始時(AUTHENTICATEコマンド送信の直前)に呼び出されます。

アクセストークンの取得処理を追加する例
using System;

using Smdn.Net.Imap4;
using Smdn.Net.Imap4.Client;
using Smdn.Security.Authentication.Sasl.Client;

class XOAuth2Client : XOAuth2Mechanism {
  // Initializeメソッドをオーバーライドしてアクセストークンの取得処理を記述する
  public override void Initialize()
  {
    base.Initialize();

    User = "someuser@example.com";
    AccessToken = "vF9dft4qmTc2Nvb3RlckBhdHRhdmlzdGEuY29tCg=="; // 何らかの手段で取得したアクセストークンを想定
  }
}

public class XOAuth2Sample {
  static void Main(string[] args)
  {
    using (var client = new ImapClient(new Uri("imaps://user@localhost/"))) {
      using (var oauthClient = new XOAuth2Client()) {
        client.Connect(oauthClient);
      }
    }
  }
}

§5 IMAPの操作

Smdn.Net.Imap4.Client名前空間のクラスを使ったIMAPの操作は、それぞれ以下のクラスのインスタンスを用いて行います。

クラスと機能
操作対象 クラス 機能
IMAPアカウント ImapClient クライアント本体のクラス。
メールボックス ImapMailboxInfo 単一のメールボックス(フォルダ)を表すクラス。
ImapOpenedMailboxInfo 選択済みメールボックスを表すクラス。 ImapMailboxInfoから派生。
メッセージ
(メール)
ImapMessageInfoBase 単一もしくは複数のメッセージに対する共通の操作を定義した抽象クラス。
ImapMessageInfo 単一のメッセージを表すクラス。 ImapMessageInfoBaseから派生。
ImapMessageInfoList 複数のメッセージを表すクラス。 ImapMessageInfoBaseから派生。 IEnumerable<ImapMessageInfo>を実装。
メッセージパート ImapMessagePartInfo メッセージ内のメッセージパートを表すクラス。

ImapClient以外のクラスのインスタンスは直接作成することはできません。 ImapMailboxInfo・ImapOpenedMailboxInfoはImapClientクラスのメソッドを呼び出すことで取得できます。 同様にImapMessageInfo・ImapMessageInfoListはImapOpenedMailboxInfoクラスのメソッド、ImapMessagePartInfoはImapMessageInfoクラスのメソッドを呼び出すことで取得できます。

例えば、IMAPサーバに接続してメールボックス内にあるメッセージをダウンロードするまでの流れは次のようになります。

  1. ImapClientを作成する (§.接続と認証)
  2. ImapClientのメソッドでImapOpenedMailboxInfoを取得する (§.メールボックスの選択)
  3. ImapOpenedMailboxInfoのメソッドでImapMessageInfoを取得する (§.メッセージの取得と通番・UID)
  4. ImapMessageInfoのメソッドを呼び出す (§.メッセージの本文のダウンロード)

次の例は上記のクラスを使ってメールボックス、メッセージの操作を行う一例です。

using System;

using Smdn.Net.Imap4;
using Smdn.Net.Imap4.Client;

class Sample {
  static void Main(string[] args)
  {
    using (ImapClient client = new ImapClient(new Uri("imap://user@localhost/"))) {
      client.Connect("pass");

      // INBOXを開く
      using (ImapOpenedMailboxInfo inbox = client.OpenInbox()) {
        // Thunderbirdの「後で」フラグ($label5)が付けられているメッセージを取得 (件名を含む静的属性も同時に取得する)
        ImapMessageInfoList messages = inbox.GetMessages(ImapSearchCriteria.Keyword("$label5"),
                                                         ImapMessageFetchAttributeOptions.StaticAttributes);

        foreach (ImapMessageInfo message in messages) {
          // 取得したメッセージの件名を表示
          Console.WriteLine(message.EnvelopeSubject);
        }

        // INBOXの下にあるメールボックス「後で」を取得または作成する
        ImapMailboxInfo pendingBox = inbox.GetOrCreateChild("後で");

        // 「後で」フラグが付けられているメッセージを取得したメールボックスに移動する
        messages.MoveTo(pendingBox);
      }
    }
  }
}

以下でImapClientと関連するクラスの使い方について解説します。

§5.1 IMAP操作と各クラスの使用例

ここではSmdn.Net.Imap4.Client名前空間の各クラスを使用してIMAPの操作を行う例を紹介します。 あわせてIMAPプロトコルによる操作を行う上での注意点についても解説します。

以下のサンプルコードでは接続・認証処理を省略しているので§.接続と認証を参照して必要なコードを追記してください。 また、各クラスに用意されているすべてのメソッド・プロパティの詳細については§.機能別リファレンスを参照してください。

§5.1.1 メールボックスの操作

§5.1.1.1 メールボックスの取得

メールクライアント上で「受信トレイ」などで表記される既定のメールボックスは、INBOXという固定の名称を持っています。 これはIMAPプロトコルで定められている名称で、INBOXは全てのアカウントで常に存在するメールボックスです。 既定のメールボックスINBOXを取得するには、ImapClient.GetInboxメソッドを使うか、GetMailboxメソッドに"INBOX"を指定して呼び出します。

「受信トレイ」を取得する例
using (ImapClient client = new ImapClient()) {
  ImapMailboxInfo inbox = client.GetInbox();

  // 以下のようにしても同じ
  //ImapMailboxInfo inbox = client.GetMailbox("INBOX");
}

これ以外にも、メールボックス(フォルダ)を取得するには既知のメールボックスを取得するImapClient.GetMailboxメソッド、すべてのメールボックスを取得するImapClient.GetMailboxesメソッドなどを使うことができます。

メールボックスを取得する例
using (ImapClient client = new ImapClient()) {
  // 既知のメールボックス"foo"を取得する
  ImapMailboxInfo mailbox = client.GetMailbox("foo");

  // すべてのメールボックスを取得する
  foreach (ImapMailboxInfo m in client.GetMailboxes()) {
    // メールボックス名を表示
    Console.WriteLine(m.FullName);
  }
}

§5.1.1.2 メールボックスのステータス

ImapMailboxInfo.ExistMessageCountなどメールボックスのステータスは、明示的に指定しない限り取得しません。 ImapClient.GetMailboxなどメールボックスを取得するメソッドでは、引数にImapMailboxListOptions.RequestStatusを指定することでメールボックスの取得と同時にステータスも取得します。 このほか、ImapMailboxInfo.Refreshメソッドを呼び出すことにより、メールボックスのステータスを取得しなおして最新の状態にすることができます。

メールボックスのステータスを取得する例
using (var client = new ImapClient()) {
  ImapMailboxInfo mailbox;

  // ImapMailboxListOptionsを指定せずにメールボックスを取得する
  mailbox = client.GetMailbox("foo");

  // ステータスは取得されていないので、0が出力される
  Console.WriteLine(mailbox.ExistMessageCount);

  // ステータスを更新する
  mailbox.Refresh();

  // メールボックスに存在するメッセージ数が出力される
  Console.WriteLine(mailbox.ExistMessageCount);

  // ImapMailboxListOptions.RequestStatusを指定してメールボックスを取得する
  mailbox = client.GetMailbox("bar", ImapMailboxListOptions.RequestStatus);

  // メールボックスに存在するメッセージ数が出力される
  Console.WriteLine(mailbox.ExistMessageCount);
}

§5.1.1.3 メールボックスと階層

IMAPではメールボックス(フォルダ)に階層を持たせることができますが、メールボックスの階層区切り文字はIMAPサーバーによって異なります。 一例として、GMailでは階層区切り文字として / (スラッシュ)、Dovecotでは . (ドット)が使用されます。 従って、メールボックスが次のような階層を持っている場合、それぞれ次のような名前を持つことになります。

メールボックスの階層
|--受信トレイ
|  |--フォルダ1
|  |  `--サブフォルダ1
|  `--フォルダ2
|--下書き
`--ごみ箱
   `--フォルダ1
GMailでのメールボックス名
INBOX
INBOX/フォルダ1
INBOX/フォルダ1/サブフォルダ1
INBOX/フォルダ2
下書き
ごみ箱
ごみ箱/フォルダ1
Dovecotでのメールボックス名
INBOX
INBOX.フォルダ1
INBOX.フォルダ1.サブフォルダ1
INBOX.フォルダ2
下書き
ごみ箱
ごみ箱.フォルダ1

(実際にはModified UTF-7でエンコードされたメールボックス名が使われますが、本ライブラリでは自動的にModified UTF-7への変換を行います)

ImapClient.GetMailboxメソッドなどで階層化されたメールボックスを取得する場合は、適切な階層区切り文字を指定したフルパスを指定する必要があります。 ImapMailboxInfoクラスのGetChildメソッドやCreateChildメソッドを使う場合、階層区切り文字を意識せずに子メールボックスの取得・作成ができます。

下位の階層のメールボックスを操作する例
using (ImapClient client = new ImapClient()) {
  // INBOXを取得
  ImapMailboxInfo inbox = client.GetInbox();

  // INBOXの下位に"test"という名前のメールボックスを作成する
  inbox.CreateChild("test");

  // INBOXの下位にあるメールボックス"test"を取得する
  ImapMailboxInfo test = inbox.GetChild("test");

  // INBOXの下位にあるすべてのメールボックスを取得する
  foreach (ImapMailboxInfo child in inbox.GetChildren()) {
    Console.WriteLine(child.FullName);
  }
}

メールボックスで使われる階層区切り文字を取得するにはImapMailboxInfo.MailboxSeparatorプロパティを参照してください。

メールボックスの階層区切り文字を取得する例
using (ImapClient client = new ImapClient()) {
  ImapMailboxInfo inbox = client.GetInbox();

  // INBOXの階層区切り文字を取得・表示
  Console.WriteLine(inbox.MailboxSeparator);
}

メールボックス名の結合・分割にはImapClient.CombineMailboxNameなどのメソッドを使うこともできます。

§5.1.1.4 特別なメールボックス

メールクライアント上で「受信トレイ」と表示されるメールボックスには、INBOXというIMAPプロトコルで定められた名前が付けられています。 これ以外にもよく使われる特別なメールボックスに「送信済みメール」「ごみ箱」「下書き」などがあります。 一般的にこれらのメールボックスには「Sent」「Trash」「Drafts」といった名前が付けられていることが多いですが、IMAPプロトコルではこれらのメールボックス名は定められていないため、クライアントやサーバーの実装によってはこれと異なる名前が付けられている場合があります。 また、これらのメールボックスがINBOXと同階層にある場合や下位にある場合もあり、まちまちです。

IMAPサーバーがSPECIAL-USE(RFC 6154 - IMAP LIST Extension for Special-Use Mailboxes)をサポートしている場合、具体的な名前が分からない場合でもこれらの特別なメールボックスを取得することが出来ます。 ImapClient.GetMailboxなどのメソッドでは、引数にImapSpecialMailbox列挙体の値を指定することで、特別なメールボックスを取得することが出来ます。

特別なメールボックスを取得する例
using (ImapClient client = new ImapClient()) {
  // 「送信済みメール」のメールボックスを取得する
  ImapMailboxInfo sent = client.GetMailbox(ImapSpecialMailbox.Sent);

  // 「下書き」のメールボックスを取得する
  ImapMailboxInfo drafts = client.GetMailbox(ImapSpecialMailbox.Drafts);

  // 「ごみ箱」のメールボックスを取得する
  ImapMailboxInfo trash = client.GetMailbox(ImapSpecialMailbox.Trash);
}

拡張機能を使った操作については、§.拡張機能の使用も合わせて参照してください。

§5.1.1.5 メールボックスの購読

IMAPではメールボックスを購読(SUBSCRIBE)するという機能があります。 メールクライアント上では購読中のメールボックスのみを表示するようにし、それ以外は見かけ上存在しないものとして扱うようにすることができます。 ImapClient.GetMailboxesなどのメールボックスを取得するメソッドでは引数にImapMailboxListOptionsを指定することができ、ImapMailboxListOptions.SubscribedOnlyを指定すれば購読中のメールボックスのみを取得することができます。

購読中のメールボックスを取得する例
using (var client = new ImapClient()) {
  // アカウントの全てのメールボックスを取得して表示
  Console.WriteLine("[all mailboxes]");
  foreach (ImapMailboxInfo mailbox in client.GetMailboxes()) {
    Console.WriteLine(mailbox.FullName);
  }

  // アカウントの購読中のメールボックスを取得して表示
  Console.WriteLine("[subscribed mailboxes]");
  foreach (ImapMailboxInfo mailbox in client.GetMailboxes(ImapMailboxListOptions.SubscribedOnly)) {
    Console.WriteLine(mailbox.FullName);
  }
}

例えばアカウントが次のようなメールボックスを持っていて、Xの付いたメールボックスのみを購読している場合、取得されるメールボックスは次のようになります。

アカウントのメールボックスと購読状況
|--[X] 受信トレイ
|  |--[X] フォルダ1
|  |  `--[ ] サブフォルダ1
|  `--[X] フォルダ2
|--[X] 下書き
`--[X] ごみ箱
   `--[ ] フォルダ1
ImapMailboxListOptionsの指定なし
INBOX
INBOX.フォルダ1
INBOX.フォルダ1.サブフォルダ1
INBOX.フォルダ2
下書き
ごみ箱
ごみ箱.フォルダ1
ImapMailboxListOptions.SubscribedOnlyを指定
INBOX
INBOX.フォルダ1
INBOX.フォルダ2
下書き
ごみ箱

メールボックスの購読状況はImapMailboxInfoクラスのSubscribeメソッドおよびUnsubscribeメソッドを使うことにより変更することができます。 またImapClient.CreateMailboxなどメールボックスを作成するメソッドでは、作成すると同時に購読状態にするかどうかも指定することができます。

§5.1.1.6 メールボックスの選択

IMAPでは、メールボックスにあるメッセージの取得や検索・削除・コピーなどの操作は、選択済みメールボックス(selected mailbox)に対してのみ行うことができます。 ImapClient.OpenMailboxメソッドやImapMailboxInfo.Openメソッドを呼び出すことでメールボックスの選択(SELECT)が行えます。 これらのメソッドではImapOpenedMailboxInfoクラスのインスタンスが返されます。 ImapOpenedMailboxInfoクラスはImapMailboxInfoクラスから派生したクラスで、メッセージの取得など選択済みメールボックスに対してのみ行える操作が追加されています。

メールボックスの選択を行う例
using (var client = new ImapClient()) {
  // INBOXを開く
  ImapOpenedMailboxInfo inbox = client.OpenInbox();

  // INBOX.Trashを開く
  ImapOpenedMailboxInfo trash = client.OpenMailbox("INBOX.Trash");
}

IMAPでは、選択することが出来るメールボックスは常に一つだけです。 あるメールボックスを選択している状態で別のメールボックスを選択しようとした場合、直前まで選択されていたメールボックスの選択は暗黙的に解除されます

メールボックスへのメッセージのアップロードは、メールボックスを選択しなくても行うことができます。

IMAPでは、メールボックスを読み取り専用で開くことができます。 読み取り専用で開いたメールボックスでは、メッセージに設定されている新着フラグ(\Recent)は消失せず、メッセージへのフラグの設定なども反映されません。 読み取り専用でメールボックスを開くには、ImapClient.OpenMailboxメソッドやImapMailboxInf.Openメソッドの引数asReadOnlyにtrueを指定します。

読み取り専用でメールボックスを開く例
using (var client = new ImapClient()) {
  // 読み取り専用でINBOXを開く
  ImapOpenedMailboxInfo inbox = client.OpenInbox(true);

  // 読み取り専用でINBOX.Trashを開く
  ImapOpenedMailboxInfo trash = client.OpenMailbox("INBOX.Trash", true);
}

§5.1.1.7 メールボックスのクローズ

IMAPでは選択したメールボックスを閉じる(CLOSE)際に削除予定のフラグ(\Deleted)を付けられているメッセージの削除が行われます。 ImapOpenedMailboxInfo.Closeメソッドを呼び出すことでメールボックスを閉じることができますが、usingステートメントとともにImapOpenedMailboxInfoを使うことによりブロックから抜ける時点で自動的にメールボックスを閉じるようにすることもできます。

開いたメールボックスを閉じる例
using (ImapClient client = new ImapClient()) {
  // INBOXを開く
  ImapOpenedMailboxInfo inbox = client.OpenInbox();

  // 送信日時が2013年7月1日以前のメールに削除フラグ(\Deleted)を追加する
  inbox.GetMessages(ImapSearchCriteria.SentBefore(new DateTime(2013, 7, 1))).AddFlags(ImapMessageFlag.Deleted);

  // INBOXを閉じる (同時に削除フラグが設定されたメッセージが削除される)
  inbox.Close();
}

次のコードは上記のコードと同じ動作となります。

開いたメールボックスを閉じる例
using (ImapClient client = new ImapClient()) {
  // INBOXを開く
  using (ImapOpenedMailboxInfo inbox = client.OpenInbox()) {

  // 送信日時が2013年7月1日以前のメールに削除フラグ(\Deleted)を追加する
    inbox.GetMessages(ImapSearchCriteria.SentBefore(new DateTime(2013, 7, 1))).AddFlags(ImapMessageFlag.Deleted);

  } // ブロックを抜ける時点でImapOpenedMailboxInfo.Closeメソッドが呼び出される
}

この例で使用しているImapSearchCriteriaについては§.メッセージの検索および§.メッセージの検索クエリ(ImapSearchCriteria)を参照してください。 また、メッセージを削除する際の注意点については§.メッセージの削除もご覧ください。

§5.1.2 メッセージの操作

§5.1.2.1 メッセージの取得と通番・UID

メールボックスにあるメッセージ(メール)の情報を取得するにはImapOpenedMailboxInfo.GetMessagesなどのメソッドを呼び出します。

IMAPではすべてのメッセージに1から始まる通し番号(sequence number, 通番)とユニークID(unique id, UID)が割り振られます。 目的のメッセージの通番またはUIDが分かっている場合は、ImapOpenedMailboxInfo.GetMessageByUidやImapOpenedMailboxInfo.GetMessageBySequenceなどのメソッドを使うことで該当するメッセージのImapMessageInfoを取得することができます。 取得したメッセージの通番・UIDを取得するには、ImapMessageInfo.SequenceおよびImapMessageInfo.Uidプロパティを参照してください。

メールボックスにあるメッセージを取得する例
using (ImapClient client = new ImapClient()) {
  // INBOXを開く
  using (ImapOpenedMailboxInfo inbox = client.OpenInbox()) {
    // INBOXにあるすべてのメッセージを列挙
    foreach (ImapMessageInfo message in inbox.GetMessages()) {
      // 通番とUIDを表示
      Console.WriteLine("#{0} UID={1}", message.Sequence, message.Uid);
    }

    // 通番1のメッセージを取得
    ImapMessageInfo m1 = inbox.GetMessageBySequence(1);

    // UIDが1234のメッセージを取得
    ImapMessageInfo m2 = inbox.GetMessageByUid(1234);
  }
}

通番・UIDのどちらも明らかでない場合はメッセージの検索を行うことにより目的のメッセージを取得することもできます。

通番は永続的でない値であるのに対し、UIDは永続的な値です。 各メールボックス内にあるメッセージには通番が割り振られますが、メッセージの削除や追加が行われるとこの番号は変わります。 従って通番1のメッセージは、次に操作を行おうとした時には異なるメッセージを表すものとなっている可能性があります。 一方、UIDはメールボックス内にメッセージが追加された時点で割り振られ、以降その値が変わることはありません。 従って、該当するメッセージが削除されない限り同一のメッセージを表すものとなります。

なお、サーバーの設定によってはUIDが永続的でないメールボックスが存在する場合もあります。 このようなメールボックスでは、ImapOpenedMailboxInfo.IsUidPersistentがfalseとなります。

§5.1.2.2 メッセージの本文のダウンロード

メッセージの本文を取得するにはImapMessageInfoクラスのOpenRead・Save・ReadAllTextなどのメソッドを使います。 取得される本文はヘッダ部分を含みます。

メッセージの本文をダウンロードする例
using (ImapClient client = new ImapClient()) {
  // INBOXを開く
  using (ImapOpenedMailboxInfo inbox = client.OpenInbox()) {
    // 通番1のメッセージを取得する
    ImapMessageInfo message = inbox.GetMessageBySequence(1);

    // メッセージ本文をダウンロードしてファイル"1.eml"に保存する
    message.Save("1.eml");
  }
}

IMAPでは本文のダウンロードと同時にメッセージを既読状態に(\Seenフラグをセット)することができます。 メッセージ本文を取得するメソッドの引数にImapMessageFetchBodyOptions.SetSeenを指定した場合、本文の取得が完了した時点でメッセージを既読にします。

メッセージ本文のダウンロードと同時に既読にする例
using (ImapClient client = new ImapClient()) {
  // INBOXを開く
  using (ImapOpenedMailboxInfo inbox = client.OpenInbox()) {
    // 通番1のメッセージを取得する
    ImapMessageInfo message = inbox.GetMessageBySequence(1);

    // メッセージ本文をダウンロードしてファイル"1.eml"に保存したあと、既読にする
    message.Save("1.eml", ImapMessageFetchBodyOptions.SetSeen);
  }
}

メッセージの本文全体をダウンロードする以外にも、オプションを指定することによりメッセージの一部分のみを取得することもできます。

なお、本ライブラリではダウンロードしたメッセージのヘッダパートを解析したり添付ファイルの抽出を行ったりする機能は用意していません。 そういったIMAPの仕様を超える機能はSmdn.Formats.Mimeで提供しているので、必要な場合はこれらのライブラリを組み合わせて使ってください。

参考までに、本ライブラリとSmdn.Formats.Mimeを組み合わせて使う場合の例を挙げます。

INBOX内のすべてのメッセージの本文を取得して、添付ファイルのみを取得する例
using System;

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

class DecodeMime {
  static void Main(string[] args)
  {
    using (var client = new ImapClient(new Uri("imap://user@localhost/"))) {
      client.Connect("pass");

      // INBOXを開く
      using (var inbox = client.OpenInbox()) {
        // すべてのメッセージを取得
        foreach (var message in inbox.GetMessages()) {
          // メッセージの本文を取得し、それをMail.Loadメソッド(Smdn.Formats.Mime)でパース・デコードした結果を得る
          var mail = message.ReadAs<Mail>(Mail.Load);

          // デコードしたメッセージの件名・受信日時と添付ファイル名を表示する
          Console.WriteLine("{0} {1}", mail.Date, mail.Subject);

          // すべての添付ファイルを列挙
          foreach (var attachedFile in mail.AttachedFiles) {
            Console.WriteLine("  file attached: {0}", attachedFile.FileName);

            // 添付ファイルを保存する場合
            //attachedFile.Save("filename");
          }
        }
      }
    }
  }
}

§5.1.2.3 メッセージの属性

IMAPサーバーで管理されるメッセージには、いくつかのプロパティ(message attribute, 属性)が与えられます。 各メッセージに与えられる属性には動的属性静的属性の二種類が存在します。

静的属性とは、メッセージがメールボックスに格納されて以降変更されることがない属性で、例えばメッセージのサイズ(RFC822.SIZE)や、受信したメッセージがメールボックスに追加された日時(INTERNALDATE)、メッセージのエンベロープ(ENVELOPE, ヘッダ情報)がそれに当たります。 一方の動的属性とは、ユーザによる変更が可能な属性で、例えばメッセージのフラグ(FLAGS)がそれにあたります。 このようなメッセージの属性はImapMessageInfoクラスのプロパティで取得・参照することができます。

なお、属性を未取得の状態でこれらのプロパティを参照しようとした場合、FETCHコマンドを発行して属性情報をダウンロードします。 ImapMessageInfoインスタンスの取得と同時に属性情報も取得したい場合は、ImapOpenedMailboxInfo.GetMessagesなどのメソッドでImapMessageFetchAttributeOptionsを指定します。

メッセージの属性を取得・参照する例
using (ImapClient client = new ImapClient()) {
  // INBOXを開く
  using (ImapOpenedMailboxInfo inbox = client.OpenInbox()) {
    // ImapMessageFetchAttributeOptionsを指定せずにINBOXにあるすべてのメッセージを列挙
    foreach (ImapMessageInfo message in inbox.GetMessages()) {
      // メッセージのサブジェクト(デコードされたSubjectヘッダ)を取得して表示
      // (ここで静的属性を取得するコマンドが発行される)
      Console.WriteLine("#{0} subject: {1}", message.Sequence, message.EnvelopeSubject);

      // メッセージが既読かどうかを取得して表示
      // (ここで動的属性を取得するコマンドが発行される)
      Console.WriteLine("#{0} is seen? {1}", message.Sequence, message.IsSeen);
    }

    // ImapMessageFetchAttributeOptions.AllAttributes(すべての属性を取得)を指定して
    // INBOXにあるすべてのメッセージを列挙
    foreach (ImapMessageInfo message in inbox.GetMessages(ImapMessageFetchAttributeOptions.AllAttributes)) {
      // メッセージのサブジェクト(デコードされたSubjectヘッダ)を取得して表示
      // (静的属性を取得するコマンドは発行されない)
      Console.WriteLine("#{0} subject: {1}", message.Sequence, message.EnvelopeSubject);

      // メッセージが既読かどうかを取得して表示
      // (動的属性を取得するコマンドは発行されない)
      Console.WriteLine("#{0} is seen? {1}", message.Sequence, message.IsSeen);
    }
  }
}

§5.1.2.4 メッセージのフラグ

IMAPではメッセージの状態を管理するために個々のメッセージにフラグを持たせることができます。 フラグには削除予定であることを表す\Deletedや、新着メッセージであることを表す\Recent、既読であることをあらわす\Seenなどがあります。 メッセージに設定されているフラグはImapMessageInfoクラスのIsMarkedAsDeleted, IsRecent, IsSeenなどのプロパティや、Flagsプロパティを参照することで取得することができます。

これらのフラグはユーザーが変更することもできます。 ImapMessageInfo.AddFlagsメソッドやStoreメソッドを使うことでこれらのフラグを変更することができます。 ただし、\Recentフラグはサーバーのみが変更することができ、ユーザーがこのフラグを変更することはできません。

IMAPでは\Deleted\Seenなど特別な意味を持つフラグ以外にも、ユーザー定義のフラグ(キーワード)を持たせることもできます。 よく使われるキーワードとして、迷惑メールであることを表すJunkや、Thunderbirdの「重要」タグ($label1)や「ToDo」タグ($label4)などがありますが、これ以外にも独自にキーワードを持たせて管理することができます。

メッセージのフラグ・キーワードを取得・設定する例
using (ImapClient client = new ImapClient()) {
    // INBOXを開く
    using (ImapOpenedMailboxInfo inbox = client.OpenInbox()) {
      // 動的属性(フラグを含む)とともに全てのメッセージを取得
      foreach (ImapMessageInfo message in inbox.GetMessages(ImapMessageFetchAttributeOptions.DynamicAttributes)) {
        // メッセージに付与されているすべてのフラグとキーワードを表示
        Console.WriteLine("#{0}: {1}", message.Sequence, string.Join(", ", message.Flags));
      }

      // 通番1のメッセージを取得してThunderbirdの「後で」タグ($label5)を付ける
      inbox.GetMessageBySequence(1).AddKeywords("$label5");

      // 通番2のメッセージから既読フラグを削除する(未読にする)
      inbox.GetMessageBySequence(2).RemoveFlags(ImapMessageFlag.Seen);

      // 通番3のメッセージの既読・未読状態を反転する
      inbox.GetMessageBySequence(3).ToggleFlags(ImapMessageFlag.Seen);
    }
  }
}

なお、キーワードは大文字小文字の違いが無視されます。 従って、メッセージにキーワードFOOfooの二つを設定しようとしても、サーバー上ではどちらも同じキーワードとして保持されます。 また、検索クエリにキーワードを指定する場合も、大文字小文字の違いが無視された上で検索されます。

サーバーの種類やメールボックスの設定によってはキーワードの設定や変更できるフラグに制限がある場合があります。 これらの設定はImapOpenedMailboxInfo.IsAllowedToCreateKeywordsなどのプロパティを参照することで確認できます。 また、読み取り専用で開いたメールボックスでは、フラグの変更は反映されません。

\Deletedフラグとメッセージの削除に関する動作・注意点については§.メッセージの削除を参照してください。

§5.1.2.6 複数のメッセージに対する操作 (ImapMessageInfoList)

ImapOpenedMailboxInfo.GetMessagesなどのメソッドで複数のメッセージの取得・検索を行った結果はImapMessageInfoListとして返されます。 ImapMessageInfoListクラスは検索結果として得られたメッセージの通番・UIDをカプセル化するクラスで、foreach文などにより列挙を行うまではImapMessageInfoを生成しないという点でList<ImapMessageInfo>などのコレクションとは異なります。

ImapMessageInfoListの動作
using (ImapClient client = new ImapClient()) {
  // INBOXを開く
  using (ImapOpenedMailboxInfo inbox = client.OpenInbox()) {
    // INBOX内にあるすべてのメッセージを取得する
    // (ここではまだコマンドは発行されない)
    ImapMessageInfoList messages = inbox.GetMessages();

    // ImapMessageInfoListを列挙してImapMessageInfoを取得する
    // (この時点でコマンドが発行される)
    foreach (ImapMessageInfo m in messages) {
      Console.WriteLine(m.Sequence);
    }

    // ImapMessageInfoListはIList<ImapMessageInfo>ではないので
    // Countなどのプロパティやインデクサ、Add/Removeなどのメソッドは持たない
    Console.WriteLine(messages.Count);
    Console.WriteLine(messages[0].Sequence);
  }
}

したがって、メールボックス内のメッセージを取得・検索した結果をImapMessageInfoのコレクションとして保持しておきたい場合は、ToList(), ToArray()などのメソッドを使ってImapMessageInfoListからList<ImapMessageInfo>などに変換する必要があります。

ImapMessageInfoListからList<ImapMessageInfo>への変換
using (ImapClient client = new ImapClient()) {
  // INBOXを開く
  using (ImapOpenedMailboxInfo inbox = client.OpenInbox()) {
    // INBOX内にあるすべてのメッセージを取得する
    // (ここではまだコマンドは発行されない)
    ImapMessageInfoList messages = inbox.GetMessages();

    // ImapMessageInfoListからList<ImapMessageInfo>を作成する
    // (この時点でコマンドが発行される)
    List<ImapMessageInfo> messageList = messages.ToList();

    // List<T>のメンバを使って操作することができる
    Console.WriteLine(messageList.Count);
    Console.WriteLine(messageList[0].Sequence);
  }
}

一方、ImapMessageInfoListクラスには複数のメッセージをまとめてコピー・削除したりすることができるように、CopyToメソッドやDeleteメソッドなどImapMessageInfoと共通のメソッドが複数用意されています。 ImapMessageInfoListクラスのメソッドを使うことにより、複数のメッセージに対する操作の場合でもメッセージの数に関わらず一度のコマンド発行で済ませることができます。

複数のメッセージを別のメールボックスにコピーする例
using (ImapClient client = new ImapClient()) {
  // INBOXを開く
  using (ImapOpenedMailboxInfo inbox = client.OpenInbox()) {
    // 返信済みのメッセージを取得する
    ImapMessageInfoList answerdMessages = inbox.GetMessages(ImapSearchCriteria.Answered);

    // メッセージをメールボックス「返信済み」にコピーする
    answerdMessages.CopyTo("返信済み");
  }
}

上記のコードでは、コピーされるメッセージの数に関わらず一度だけコマンド発行が行われます。 一方、以下のコードは上記のコードと結果は同じですが、コピーされるメッセージの数だけコマンド発行が行われる点で動作が異なります。

複数のメッセージを別のメールボックスにコピーする例
using (ImapClient client = new ImapClient()) {
  // INBOXを開く
  using (ImapOpenedMailboxInfo inbox = client.OpenInbox()) {
    // 返信済みのメッセージを取得する
    ImapMessageInfoList answerdMessages = inbox.GetMessages(ImapSearchCriteria.Answered);

    // 取得できたメッセージを列挙
    foreach (ImapMessageInfo m in answerdMessages) {
      // メッセージをメールボックス「返信済み」にコピーする
      m.CopyTo("返信済み");
    }
  }
}

ImapMessageInfoListクラスは、検索などのクエリは保持しますが、結果はキャッシュしません。 列挙を行う度に同じクエリでコマンドの再発行を行います。 そのため、同じImapMessageInfoListを何度も列挙する場合、一度目に列挙した際と同じ結果が得られるとは限りません

ImapMessageInfoListの再列挙とその動作
using (ImapClient client = new ImapClient()) {
  // INBOXを開く
  using (ImapOpenedMailboxInfo inbox = client.OpenInbox()) {
    // 未読メッセージを取得する
    ImapMessageInfoList unseenMessages = inbox.GetMessages(ImapSearchCriteria.Unseen);

    // 得られたImapMessageInfoListを列挙して、各未読メッセージの通番を表示する
    foreach (ImapMessageInfo m in unseenMessages) {
      Console.WriteLine(m.Sequence);
    }

    // 取得した未読メッセージを、すべて既読にする
    unseenMessages.MarkAsSeen();

    // 再び同じImapMessageInfoListを列挙する (未読メッセージはすべて既読に
    // なっているので、ここでは何も列挙されない)
    foreach (ImapMessageInfo m in unseenMessages) {
      Console.WriteLine(m.Sequence);
    }
  }
}

したがって、結果を保持しておきたい場合は、他の処理を行う前にImapMessageInfoListからList<ImapMessageInfo>などに変換する必要があります。

§5.1.2.7 メッセージの削除

IMAPでは、メッセージの削除を行う場合、まず\Deletedフラグを設定することにより削除予定であることをマークし(§.メッセージのフラグ)、その後EXPUNGEコマンドまたはCLOSEコマンドを発行することにより\Deletedフラグが設定されたメッセージの削除が行われます。

ImapMessageInfo.Deleteメソッド\Deletedフラグの設定とEXPUNGEコマンドの発行を行うことによりメッセージを削除します。 そのため、他に\Deletedフラグが設定されているメッセージがある場合、ImapMessageInfo.Deleteメソッドを呼び出すことによって他のメッセージも同時に削除される点に注意してください。

メッセージの削除を行う例
using (ImapClient client = new ImapClient()) {
  // INBOXを開く
  using (ImapOpenedMailboxInfo inbox = client.OpenInbox()) {
    ImapMessageInfo m1 = inbox.GetMessageBySequence(1);
    ImapMessageInfo m2 = inbox.GetMessageBySequence(2);
    ImapMessageInfo m3 = inbox.GetMessageBySequence(3);

    // 通番1のメッセージに\Deletedフラグを追加する
    m1.AddFlags(ImapMessageFlag.Deleted);

    // 通番2のメッセージに\Deletedフラグを追加する
    m2.AddFlags(ImapMessageFlag.Deleted);

    // 通番3のメッセージを削除する
    // (\Deletedフラグを設定した後にEXPUNGEコマンドを発行するが、
    // \Deletedフラグが設定されているm1, m2も同時に削除される)
    m3.Delete();

    // IsDeletedはすべてtrueとなる
    Console.WriteLine(m1.IsDeleted);
    Console.WriteLine(m2.IsDeleted);
    Console.WriteLine(m3.IsDeleted);
  }
}

なお、IMAP拡張コマンドであるUID EXPUNGEコマンド(RFC 4315 - Internet Message Access Protocol (IMAP) - UIDPLUS extension)をサポートしているサーバーの場合はこのような動作とはならず、ImapMessageInfo.DeleteメソッドはImapMessageInfoが表すメッセージのみを削除します。 (§.拡張機能の使用も合わせて参照してください。)

§5.1.2.8 メッセージの移動

IMAPにはメッセージの移動を行うコマンドが用意されていないため、ImapMessageInfo.MoveToなどのメソッドではコピーと削除の操作を組み合わせることで移動の操作をエミュレートします。 そのため、移動を行おうとしてコピーを行ったが、その後の削除に失敗した場合などには、コピー元とコピー先のメールボックスに同一のメッセージが存在することとなります。 現在の実装では、コピー後の削除に失敗してもそのままにします(コピー先のメッセージを削除してコピー前の状態にするような実装とはなっていません)。

なお、IMAP拡張コマンドであるMOVEコマンド(RFC 6851 - Internet Message Access Protocol (IMAP) - MOVE Extension)をサポートするサーバーの場合は、ImapMessageInfo.MoveToなどのメソッドはMOVEコマンドを使ってメッセージの移動を行います。 (§.拡張機能の使用も合わせて参照してください。)

§5.1.2.9 ImapMessageInfoの有効期限

ImapMessageInfoは、その取得元のメールボックス(ImapOpenedMailboxInfo)が開かれている間のみ有効です。 メールボックスを閉じた後にImapMessageInfoを使って操作を行おうとした場合はImapUnavailableExceptionをスローします。 また、同名のメールボックスを再度開いた場合でも有効にはならず、再度ImapMessageInfoを取得しなおす必要があります。

ImapMessageInfoの有効期限
using (ImapClient client = new ImapClient()) {
  // INBOXを開く
  ImapOpenedMailboxInfo inbox = client.OpenInbox();

  // INBOXから通番1のメッセージを取得する
  ImapMessageInfo m = inbox.GetMessageBySequence(1);

  // 新たに別のメールボックスを開く (INBOXは閉じられる)
  ImapOpenedMailboxInfo test = client.OpenMailbox("test");

  // INBOXから取得したメッセージは既に利用不可能なため、ImapUnavailableExceptionをスローする
  string body = m.ReadAllText();

  // INBOXを開きなおしてもメッセージは引き続き利用不可能
  inbox = client.OpenInbox();

  body = m.ReadAllText();
}

さらに、MoveToメソッドやDeleteメソッドによりメッセージを移動・削除した場合も、以降は無効となります。 ImapMessageInfoが有効かどうかを調べるにはImapMessageInfo.IsAvailableプロパティを参照してください。

この他にも、IMAPでは複数のクライアントが同時に操作を行うことが許可されているため、メッセージに対して操作を行おうとした時点ではメッセージが削除されている、といった場合もあります。 そういった場合には例外ImapMessageVanishedExceptionをスローします。

§5.1.2.10 メッセージのアップロード

IMAPではユーザーがメッセージをアップロードしてメールボックスへ追加(APPEND)することができます。 メッセージのアップロードにはImapMailboxInfo.AppendMessageなどのメソッドを使うことが出来ます。 アップロードの際、オプションでメールボックスへの追加日時(INTERNALDATE)とフラグを設定することもできます。

なお、選択されていないメールボックスへもメッセージのアップロードが出来るため、アップロードに際してメールボックスの選択を行う必要はありません。

メッセージのアップロードを行う例
using (ImapClient client = new ImapClient()) {
  // INBOXを取得する
  ImapMailboxInfo inbox = client.GetInbox();

  // ファイルtest1.emlを開いてINBOXにアップロードする
  using (Stream stream = File.OpenRead("test1.eml")) {
    inbox.AppendMessage(stream);
  }

  // ファイルtest2.emlを開いてINBOXにアップロードする
  // 合わせてINTERNALDATEとフラグも設定する
  DateTimeOffset internalDate = new DateTimeOffset(2013, 7, 1, 0, 0, 0, TimeSpan.FromHours(+9.0));
  ImapMessageFlagSet flags = new ImapMessageFlagSet();

  flags.Add(ImapMessageFlag.Seen); // 既読状態で追加する
  flags.Add(new ImapMessageFlag("junk")); // キーワード"junk"を設定する

  using (Stream stream = File.OpenRead("test2.eml")) {
    inbox.AppendMessage(stream, internalDate, flags);
  }
}

§5.1.2.11 メッセージのセクションとメッセージパート

IMAPでは、メールボックス内にあるMIMEメッセージの解析がサーバー側で行われます。 その結果、メッセージ内の各パートには1から始まるセクション番号が自動的に割り当てられます。 階層がネストしている場合は、親階層の番号の後にピリオド.で区切られたセクション番号が割り当てられます。 以下は、メッセージの構造と割り当てられるセクション番号の例です。

本文の後に画像が添付されているメールの例
multipart/mixed
|--[1] text/plain
`--[2] image/jpeg
本文の後に二つのメールがカプセル化されているメールの例
multipart/mixed
|--[1] text/plain
|--[2] message/rfc822
|   `--[2.1] text/plain
`--[3] message/rfc822
    `--[3.1] multipart/alternative
       |--[3.1.1] text/plain
       `--[3.1.2] text/html
構造がネストしているメールの例
multipart/mixed
|--[1] text/plain
|--[2] multipart/mixed
|   |--[2.1] text/plain
|   |--[2.2] image/jpeg
|   `--[2.3] image/jpeg
`--[3] multipart/mixed
    |--[3.1] text/plain
    `--[3.2] text/html

ImapMessageInfo.GetMessagePart()などのメソッドを使用することにより特定のメッセージパートを参照することができ、そのメッセージパートに関する情報を取得したり、メッセージ本文のうちメッセージパートの内容のみを取得したりすることができます。

例えば、次のような構造のメッセージから、セクション番号1(text/plainのパート)の本文だけを取得する場合は次のようにします。

メールボックスにあるメッセージの構造とセクション番号
multipart/mixed
|--[1] text/plain
`--[2] image/jpeg
上記のメッセージからtext/plainのパートだけを取得して表示する例
using (ImapClient client = new ImapClient()) {
  using (var mailbox = client.OpenInbox()) {
    // 目的のメッセージはINBOX内にあるUIDが1のメッセージと仮定
    var message = mailbox.GetMessageByUid(1L, ImapMessageFetchAttributeOptions.StaticAttributes);

    // メッセージのセクション番号1のメッセージパートを取得する
    ImapMessagePartInfo part1 = message.GetMessagePart(1);

    // セクション番号1のメッセージパートの本文のみを取得し、表示する
    Console.WriteLine(part1.ReadAllText());
  }
}

§5.1.2.12 メッセージの構造の取得とメッセージパートに対する操作

IMAPでは、サーバー側で解析されたMIMEメッセージの構造に関する情報(BODYSTRUCTURE)を取得することができます。 これにより、メッセージ全体を取得・パースしなくてもメッセージがどのような構造になっているかを本文の取得前に知ることができます。 取得したBODYSTRUCTUREの情報はImapMessagePartInfoクラスのプロパティで参照することができます。 また、メッセージ全体の構造はImapMessageInfo.MessagePartプロパティを参照することで取得することができます。

BODYSTRUCTUREの参照
using (ImapClient client = new ImapClient()) {
  using (var mailbox = client.OpenInbox()) {
    // 目的のメッセージはINBOX内にあるUIDが1のメッセージと仮定
    // ImapMessageFetchAttributeOptions.StaticAttributesを指定することにより
    // BODYSTRUCTUREを含む静的属性も同時に取得する
    var message = mailbox.GetMessageByUid(1L, ImapMessageFetchAttributeOptions.StaticAttributes);

    // BODYSTRUCTUREを参照し、メッセージがマルチパートかどうかを調べる
    // (単にmessage.IsMultiPartとしても同じ)
    if (message.MessagePart.IsMultiPart) {
      // NestedMessagePartsプロパティを参照することでネストしている
      // メッセージパートを取得することができる
      foreach (ImapMessagePartInfo nestedPart in message.MessagePart.NestedMessageParts) {
        // ...
      }
    }
    else {
      // ImapMessagePartInfoクラスでは個々のメッセージパートに関する情報も取得できる
      // (これらの情報はすべてIMAPサーバーより提供される)
      Console.WriteLine(message.MessagePart.Length);
      Console.WriteLine(message.MessagePart.ContentDisposition);
      Console.WriteLine(message.MessagePart.ContentTransferEncoding);
      Console.WriteLine(message.MessagePart.HasEncapsulatedMessage);
      //   :
      //   :
    }
  }
}

特定のメッセージパートを参照したい場合は、ImapMessageInfo.GetMessagePart()メソッドを使うことができます。 このメソッドでは、セクション番号を文字列または数値で指定することにより、そのセクションのメッセージパート情報を取得することができます。 また、FindMessagePart()またはFindAllMessageParts()メソッドを使うと、特定の条件(Predicate<ImapMessagePartInfo>)またはメディアタイプに該当するメッセージパートだけを取得することができます。

特定のメッセージパートを取得する例
using (ImapClient client = new ImapClient()) {
  using (var mailbox = client.OpenInbox()) {
    // 目的のメッセージはINBOX内にあるUIDが1のメッセージと仮定
    // ImapMessageFetchAttributeOptions.StaticAttributesを指定することにより
    // BODYSTRUCTUREを含む静的属性も同時に取得する
    var message = mailbox.GetMessageByUid(1L, ImapMessageFetchAttributeOptions.StaticAttributes);

    ImapMessagePartInfo p;

    // セクション番号"1.2.3"のメッセージパートを取得する
    p = message.GetMessagePart("1.2.3");

    // セクション番号"1.2.3"のメッセージパートを取得する (上記のコードと同等)
    p = message.GetMessagePart(1, 2, 3);

    // メディアタイプがimage/jpegのメッセージパートをすべて取得して列挙する
    foreach (var jpegImagePart in message.FindAllMessageParts(new Smdn.MimeType("image/jpeg"))) {
      // メッセージパートの内容を取得・デコードしてファイルに保存する
      jpegImagePart.Save(jpegImagePart.Specifier + ".jpg", ImapMessageFetchBodyOptions.DecodeContent);
    }
  }
}

この例で使用しているImapMessageFetchBodyOptions.DecodeContentについては§.メッセージのデコードで解説します。

FindAllMessageParts()メソッドを使った別の例として、メッセージから添付ファイルのメッセージパートを探してファイルに保存する例を挙げます。

メッセージに添付されているファイルを保存する例
using (ImapClient client = new ImapClient()) {
  using (var mailbox = client.OpenInbox()) {
    // 目的のメッセージはINBOX内にあるUIDが1のメッセージと仮定
    // ImapMessageFetchAttributeOptions.StaticAttributesを指定することにより
    // BODYSTRUCTUREを含む静的属性も同時に取得する
    var message = mailbox.GetMessageByUid(1L, ImapMessageFetchAttributeOptions.StaticAttributes);

    // 取得したメッセージについて、Content-Disposition: attachmentヘッダの
    // あるメッセージパートを探して列挙する
    foreach (var attachmentPart in message.FindAllMessageParts(ImapMessagePartInfo.Predicates.IsAttachmentPart)) {
      // Content-TypeまたはContent-Dispositionヘッダからファイル名を取得する
      var filename = attachmentPart.GetContentFileName();

      if (filename == null)
        // (取得できなかった場合は、適当なファイル名を与える必要がある)
        filename = "attachment-file.xxx";

      // セクションの内容を取得・デコードしてファイルに保存する
      attachmentPart.Save(filename, ImapMessageFetchBodyOptions.DecodeContent);
    }
  }
}

Traverse()メソッドを使うと、メッセージ内のすべてのメッセージパートを走査することができます。

メッセージ構造の走査を行う例
using (ImapClient client = new ImapClient()) {
  using (var mailbox = client.OpenInbox()) {
    var message = mailbox.GetMessageByUid(1L, ImapMessageFetchAttributeOptions.StaticAttributes);

    // すべてのメッセージパートを走査して表示する
    message.MessagePart.Traverse(true, delegate (ImapMessagePartInfo part) {
      Console.WriteLine("[{0}] {1}", part.Specifier, part.ContentType);
    });
  }
}
実行結果の一例
[] multipart/mixed
[1] text/plain
[2] message/rfc822
[2.1] text/plain
[3] message/rfc822
[3.1] text/plain
実行結果の一例
[] multipart/mixed
[1] text/plain
[2] image/jpeg
[3] image/jpeg
[4] image/jpeg

§5.1.2.13 メッセージの一部分の取得

IMAPではメッセージ全体の取得以外にも、特定のメッセージパートのみの取得や、ヘッダ部分やボディ部分のみの取得を行うことができるようになっています。 ImapMessageInfoクラスおよびImapMessagePartInfoクラスでは、メッセージの一部分のみを取得するためのメソッドやオプションを用意しています。

§5.1.2.13.1 ImapMessageInfoクラス

ImapMessageInfoクラスでは、OpenRead()やReadAllText()などのメソッドでメッセージ本文を取得することができます。 デフォルトではメッセージ全体を取得しますが、ImapMessageFetchBodyOptions.OmitHeaderを指定することによりヘッダ部分を省略したボディ部分のみを取得することもできます。 また、GetHeader()やGetRawHeader()などのメソッドを使用するとヘッダ部分のみを取得することができます。

取得対象のメッセージの構造例
Content-Type: text/plain   ^   ^
From: from@example.net     |   |
To: to@example.net         |   |
Subject: sample message   (3)  |
      :                    |   |
      :                    |  (1)
                           v   |
MIME message body          ^   |
line 1                     |   |
line 2                    (2)  |
   :                       |   |
   :                       v   v
取得したい部分 メソッド呼び出しの例
(1) メッセージ全体 ImapMessageInfo.OpenRead()
ImapMessageInfo.ReadAllText()
など
(2) ボディ部分のみ ImapMessageInfo.OpenRead(ImapMessageFetchBodyOptions.OmitHeader)
ImapMessageInfo.ReadAllText(ImapMessageFetchBodyOptions.OmitHeader)
など
(3) ヘッダ部分のみ ImapMessageInfo.GetHeader()
ImapMessageInfo.GetRawHeader()

§5.1.2.13.2 ImapMessagePartInfoクラス

ImapMessagePartInfoが特定のメッセージパートを表す場合では、OpenRead()やReadAllText()などのメソッドメッセージパートのボディ部分のみを取得します。 ImapMessageInfoクラスのメソッドとは異なり、取得される内容にはヘッダ部分が含まれない点に注意してください。 メッセージパートのヘッダ部分はGetHeader(), GetRawHeader()メソッドで取得することができます。

取得対象のメッセージの構造例
Content-Type: multipart/mixed;                    ^
  boundary=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx       |
From: from@example.net                            |
To: to@example.net                                |
Subject: sample message                           |
      :                                           |
      :                                           |
                                                  |
MIME message body                             ^   |
--xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx            |   |
Content-Type: text/plain                  ^   |   |
Content-Disposition: inline              (2)  |   |
Content-Description: message part 1       |   |  (4)
                                          v   |   |
message part 1                            ^   |   |
    :                                    (1)  |   |
    :                                     v   |   |
--xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx            |   |
Content-Type: text/plain                     (3)  |
Content-Disposition: inline                   |   |
Content-Description: message part 2           |   |
                                              |   |
  message part 2                              |   |
    :                                         |   |
    :                                         |   |
--xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx--          v   v
取得したい部分 メソッド呼び出しの例
(1) 特定メッセージパートのボディ部分のみ ImapMessageInfo.GetMessagePart(1).OpenRead()
ImapMessageInfo.GetMessagePart(1).ReadAllText()
など
(2) 特定メッセージパートのヘッダ部分のみ ImapMessageInfo.GetMessagePart(1).GetHeader()
ImapMessageInfo.GetMessagePart(1).GetRawHeader()
(3) メッセージ全体のボディ部分のみ ImapMessageInfo.OpenRead(ImapMessageFetchBodyOptions.OmitHeader)
ImapMessageInfo.ReadAllText(ImapMessageFetchBodyOptions.OmitHeader)
あるいは
ImapMessageInfo.MessagePart.OpenRead(ImapMessageFetchBodyOptions.OmitHeader)
ImapMessageInfo.MessagePart.ReadAllText(ImapMessageFetchBodyOptions.OmitHeader)
(4) メッセージ全体 ImapMessageInfo.OpenRead()
ImapMessageInfo.ReadAllText()
あるいは
ImapMessageInfo.MessagePart.OpenRead()
ImapMessageInfo.MessagePart.ReadAllText()

一方、ImapMessageInfo.MessagePartプロパティおよびIsRootプロパティがtrueのImapMessagePartInfoはメッセージ全体を表します。 この場合は、ImapMessageInfoクラスのメソッドを使用した場合と同じ動作となります。

取得対象のメッセージの構造例
Content-Type: text/plain   ^   ^
From: from@example.net     |   |
To: to@example.net         |   |
Subject: sample message   (3)  |
      :                    |   |
      :                    |  (1)
                           v   |
MIME message body          ^   |
line 1                     |   |
line 2                    (2)  |
   :                       |   |
   :                       v   v
取得したい部分 メソッド呼び出しの例
(1) メッセージ全体 ImapMessageInfo.MessagePart.OpenRead()
ImapMessageInfo.MessagePart.ReadAllText()
など
(2) ボディ部分のみ ImapMessageInfo.MessagePart.OpenRead(ImapMessageFetchBodyOptions.OmitHeader)
ImapMessageInfo.MessagePart.ReadAllText(ImapMessageFetchBodyOptions.OmitHeader)
など
(3) ヘッダ部分のみ ImapMessageInfo.MessagePart.GetHeader()
ImapMessageInfo.MessagePart.GetRawHeader()

§5.1.2.13.3 ImapPartialRange構造体

ImapMessageInfoクラス・ImapMessagePartInfoクラスのOpenReadメソッドでは、引数にImapPartialRange構造体を指定することにより取得する範囲をオクテット単位で指定することができます。 ImapMessageFetchBodyOptions.OmitHeaderと組み合わせて指定することにより、取得する範囲にヘッダ部分を含めるかどうかを指定することもできます。

オクテット単位で取得する範囲を指定する例
using (ImapClient client = new ImapClient()) {
  using (var mailbox = client.OpenInbox()) {
    var message = mailbox.GetMessageByUid(1L, ImapMessageFetchAttributeOptions.StaticAttributes);

    // メッセージの32オクテット目から256オクテット分を取得するStreamを開く
    message.OpenRead(new ImapPartialRange(32, 256));

    // メッセージのボディ部分の32オクテット目から256オクテット分を取得するStreamを開く
    message.OpenRead(new ImapPartialRange(32, 256), ImapMessageFetchBodyOptions.OmitHeader);

    var part = message.GetMessagePart(1);

    // 1番目のメッセージパートのボディ部分の100オクテット目以降を取得するStreamを開く
    part.OpenRead(new ImapPartialRange(100));
  }
}

なお、ImapPartialRangeを指定した場合はImapMessageFetchBodyOptions.DecodeContentを指定することはできません。 指定した場合はNotSupportedExceptionをスローします。

§5.1.2.14 メッセージのデコード

ImapMessagePartInfoクラスでは、メッセージパートの内容を取得すると同時に、取得した内容のデコードを行うことができます。 ImapMessageFetchBodyOptions.DecodeContentを指定した場合、OpenRead(), ReadAllBytes(), Download(), Save()などのメソッドは、取得した内容をContent-Transfer-Encodingヘッダの値(ImapMessagePartInfo.ContentTransferEncodingプロパティ)の値に従ってデコードします。

例えば、BASE64でエンコードされているメッセージパートのボディ部分をデコードした状態で取得するには次のようにします。

BASE64でエンコードされたバイナリデータを含むメッセージ
Content-Type: multipart/mixed;
  boundary=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

my photo
--xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: image/jpeg
Content-Transfer-Encoding: base64
Content-Disposition: inline

/9j/4AAQSkZJRgABAQEASABIAAD/2wBDA.....
CAgHCAgJCg0LCQoMCggICw8LDA0ODg8OC.....
Dg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4OD.....
--xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx--
using (ImapClient client = new ImapClient()) {
  using (var mailbox = client.OpenInbox()) {
    // 目的のメッセージはINBOX内にあるUIDが1のメッセージと仮定
    var message = mailbox.GetMessageByUid(1L, ImapMessageFetchAttributeOptions.StaticAttributes);

    // 1番目のメッセージパートを取得
    var part = message.GetMessagePart(1);

    // メッセージパートのボディ部分を取得すると同時にBASE64からデコードし、結果を保存する
    part.Save("photo.jpg", ImapMessageFetchBodyOptions.DecodeContent);
  }
}

また、OpenText(), ReadAllText(), ReadAllLines()などの取得した内容を文字列として扱うメソッドでは、Content-Transfer-Encodingヘッダの値に従ったデコードに加えて、Content-Typeヘッダのcharsetパラメータ(ImapMessagePartInfo.ContentTypeCharsetプロパティ)の値に従ってバイナリ→文字列のデコードも行います。

quoted-printableでエンコードされたテキストデータを含むメッセージ
Content-Type: multipart/mixed;
  boundary=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

html text
--xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline

=3Chtml=3E=0D=0A=20=3Ctitle=3E=E3=82=
=BF=E3=82=A4=E3=83=88=E3=83=AB=3C=2F=
title=3E=0D=0A=20=3Cbody=3E=E6=9C=AC=
=E6=96=87=3C=2Fbody=3E=0D=0A=3C=2Fht=
ml=3E
--xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx--
using (ImapClient client = new ImapClient()) {
  using (var mailbox = client.OpenInbox()) {
    // 目的のメッセージはINBOX内にあるUIDが1のメッセージと仮定
    var message = mailbox.GetMessageByUid(1L, ImapMessageFetchAttributeOptions.StaticAttributes);

    // 1番目のメッセージパートを取得
    var part = message.GetMessagePart(1);

    // メッセージパートのボディ部分を取得すると同時にquoted-printableからデコードする
    // 文字列化に際して、Content-Typeヘッダに指定されているUTF-8が使用される
    Console.WriteLine(part.ReadAllText(ImapMessageFetchBodyOptions.DecodeContent));
  }
}
実行結果
<html>
 <title>タイトル</title>
 <body>本文</body>
</html>

§5.1.2.15 メッセージのソート

IMAP拡張コマンドであるSORTコマンド(RFC 5256 - Internet Message Access Protocol - SORT and THREAD Extensions)をサポートしているサーバーでは、このコマンドを使うことによりメールボックス内のメッセージをソートした状態で取得することができます。 このソート処理はIMAPサーバー側で行われます。 ソート済みのメッセージの取得には、ImapOpenedMailboxInfo.GetSortedMessages()メソッドを使うことができます。 引数のImapSortCriteriaによってソート順序を指定できるほか、ImapSortCriteriaの値を組み合わせることにより比較結果が同値だった場合のソート条件を複数指定することができます。

ソート済みメッセージの取得
using (ImapClient client = new ImapClient()) {
  using (var mailbox = client.OpenInbox()) {
    // メールボックス内のすべてのメッセージを取得する (順不同で列挙される)
    foreach (var message in mailbox.GetMessages()) {
      Console.WriteLine(message.Sequence);
    }

    // メールボックス内のすべてのメッセージをソートして取得する
    // (送信日時順・昇順で列挙される)
    foreach (var message in mailbox.GetSortedMessages(ImapSortCriteria.Date)) {
      Console.WriteLine(message.Sequence);
    }

    // サブジェクト順・昇順で列挙される、サブジェクトが同じ場合は送信日時順・昇順で列挙される
    foreach (var message in mailbox.GetSortedMessages(ImapSortCriteria.Subject + ImapSortCriteria.Date)) {
      Console.WriteLine(message.Sequence);
    }

    // 送信日時・降順で列挙される、送信日時が同じ場合はサブジェクト順・昇順で列挙される
    foreach (var message in mailbox.GetSortedMessages(ImapSortCriteria.DateReverse + ImapSortCriteria.Subject)) {
      Console.WriteLine(message.Sequence);
    }
  }
}

ImapSortCriteriaで指定できるソート順序については§.メッセージのソート順序(ImapSortCriteria)を参照してください。

ImapOpenedMailboxInfo.GetSortedMessages()メソッドが返すImapMessageInfoListは、ソートされた順序で列挙される点を除けばGetMessages()メソッドなどの場合と同様に動作します。 さらに、GetSortedMessages()メソッドは§.メッセージの検索と同様にImapSearchCriteriaを指定することで、指定された条件にマッチするメッセージのみを取得してソートさせることもできます。

なお、サーバーがSORTコマンドをサポートしていない場合、このメソッドは例外ImapIncapableExceptionをスローします。 (§.拡張機能の使用も合わせて参照してください。)

§5.1.2.16 メッセージのスレッド化

IMAP拡張コマンドであるTHREADコマンド(RFC 5256 - Internet Message Access Protocol - SORT and THREAD Extensions)をサポートしているサーバーでは、このコマンドを使うことによりメールボックス内のメッセージをスレッド化した状態で取得することができます。 スレッド化されたメッセージツリーの取得には、ImapOpenedMailboxInfo.GetThreadedMessages()メソッドを使うことができます。

スレッド化したメッセージの取得
using (ImapClient client = new ImapClient()) {
  using (var mailbox = client.OpenInbox()) {
    // REFERENCES(Message-IDとIn-Reply-Toによるスレッド化アルゴリズム)を使って
    // スレッド化したメッセージを取得する
    ImapMessageInfoThread thread = mailbox.GetThreadedMessages(ImapThreadingAlgorithm.References);

    // スレッド内を走査して各メッセージの階層構造を表示
    thread.Traverse(delegate(ImapMessageInfoThread t) {
      Console.WriteLine("{0}{1}", new string(' ', (t.Depth - 1) * 2), t.Message.Sequence);
    });

    // スレッド内の最上位のメッセージを表示
    Console.WriteLine("[thread root]");

    foreach (ImapMessageInfoThread c in thread.Children) {
      Console.WriteLine(c.Message.Sequence);
    }
  }
}

例えばメールボックス内に次のような通番のメッセージが格納されている場合の実行結果は次のようになります。

メールボックス内のメッセージ
[#1] Thread1
 +--[#2] Re: Thread1
 |   +--[#3] Re:Re:Thread1
 |   |   +--[#4] Re:Re:Re:Thread1
 |   |   `--[#5] Re:Re:Re:Thread1
 |   `--[#6] Re:Re:Thread1
 +--[#7] Re:Thread1
 |   `--[#8] Re:Re:Thread1
 `--[#9] Re:Thread1
[#10] Thread2
 +--[#11] Re:Thread2
 `--[#12] Re:Thread2
上記サンプルの実行結果例
1
  2
    3
      4
      5
    6
  7
    8
  9
10
  11
  12
[thread root]
1
10

この例のようにImapMessageInfoThread.Traverse()メソッドを使うことでスレッド内を走査することができます。 このメソッドを使うことにより、スレッド内を再帰的に走査して子スレッドのImapMessageInfoThreadに対して操作を行うことができます。 また、ImapMessageInfoThread.Messageプロパティを参照することでスレッドに割り当てられているメッセージのImapMessageInfoを取得することができます。 このほか、ImapMessageInfoThread.Depthプロパティではスレッドの深さ、Childrenプロパティではスレッドから吊り下がっている子スレッドのImapMessageInfoThreadを取得することができます。

ImapMessageInfoThreadがスレッド全体を格納するコンテナとなっている場合(GetThreadedMessages()が直接返す値の場合)では、IsRootプロパティがtrueとなり、Messageプロパティを取得しようとした場合には例外NotSupportedExceptionをスローします。 なお、ImapMessageInfoThreadはImapMessageInfoListとは異なり遅延実行はされません。 ImapMessageInfoThreadのメソッドやプロパティを参照することで追加のコマンドが発行されることはありません。

ImapMessageInfoThread.Traverseメソッドには、スレッド内の走査の際に、スレッドに入る時とスレッドから出る時のそれぞれでコールバックさせることができるバージョンも用意しています。 以下は、スレッド化したメッセージをXML化する例です。

スレッド化したメッセージを取得してXML化する例
using (ImapClient client = new ImapClient()) {
  using (var mailbox = client.OpenInbox()) {
    // メッセージをスレッド化すると同時に、各メッセージの静的属性も取得する
    ImapMessageInfoThread thread = mailbox.GetThreadedMessages(ImapThreadingAlgorithm.References,
                                                               ImapMessageFetchAttributeOptions.StaticAttributes);

    // スレッドをXMLツリー化する
    XElement root = new XElement("root");
    XElement parent = root;

    // スレッド内を走査する
    thread.Traverse(
      // スレッドに入るときの処理
      delegate(ImapMessageInfoThread t) {
        // ノードを作成し、通番とサブジェクトの属性を追加する
        var node = new XElement("node");

        node.Add(new XAttribute("Sequence", t.Message.Sequence));
        node.Add(new XAttribute("Subject", t.Message.EnvelopeSubject));

        // 親ツリーに追加する
        parent.Add(node);

        // 現在のノードを次の親ツリーにする
        parent = node;
      },
      // スレッドから出るときの処理
      delegate(ImapMessageInfoThread t) {
        // 親ツリーに戻る
        parent = parent.Parent;
      }
    );

    Console.WriteLine(root);
  }
}
メールボックス内のメッセージ
[#1] Thread1
 +--[#2] Re: Thread1
 |   +--[#3] Re:Re:Thread1
 |   |   +--[#4] Re:Re:Re:Thread1
 |   |   `--[#5] Re:Re:Re:Thread1
 |   `--[#6] Re:Re:Thread1
 +--[#7] Re:Thread1
 |   `--[#8] Re:Re:Thread1
 `--[#9] Re:Thread1
[#10] Thread2
 +--[#11] Re:Thread2
 `--[#12] Re:Thread2
上記サンプルの実行結果例
<root>
  <node Sequence="1" Subject="Thread1">
    <node Sequence="2" Subject="Re: Thread1">
      <node Sequence="3" Subject="Re: Re: Thread1">
        <node Sequence="4" Subject="Re: Re: Re: Thread1" />
        <node Sequence="5" Subject="Re: Re: Re: Thread1" />
      </node>
      <node Sequence="6" Subject="Re: Re: Thread1" />
    </node>
    <node Sequence="7" Subject="Re: Thread1">
      <node Sequence="8" Subject="Re: Re: Thread1" />
    </node>
    <node Sequence="9" Subject="Re: Thread1" />
  </node>
  <node Sequence="10" Subject="Thread2">
    <node Sequence="11" Subject="Re: Thread2" />
    <node Sequence="12" Subject="Re: Thread2" />
  </node>
</root>

さらに、ImapOpenedMailboxInfo.GetThreadedMessages()メソッドは§.メッセージの検索と同様にImapSearchCriteriaを指定することで、指定された条件にマッチするメッセージのみをスレッド化して取得することもできます。

なお、サーバーがTHREADコマンドをサポートしていない場合、このメソッドは例外ImapIncapableExceptionをスローします。 (§.拡張機能の使用も合わせて参照してください。)

§5.1.3 その他の操作

§5.1.3.1 サーバーからの通知

IMAPでは複数のクライアントが同時に操作を行うことが許可されていて、他のクライアントによる変更があった場合はその変更が通知される場合があります。 例えば、他のクライアントがメッセージの追加・削除を行った場合には、メールボックスのステータス変更などが通知されます。 こういった通知は発行したコマンドに対するレスポンスに付随して送信されてきます。 新着メールの受信などもこれらの通知に含まれます。

これらのステータス変更はImapClientクラスのExistMessageCountChangedイベントなどによって検知することができます。 また、定期的にImapMailboxInfo.Refreshメソッドを呼び出してメールボックスのステータスを更新することでも他のクライアントによる変更を検知できます。

メールボックスのステータス変更イベントをハンドリングする例
using (ImapClient client = new ImapClient()) {
  // イベントハンドラを設定
  client.ExistMessageCountChanged += delegate(object sender, ImapMailboxSizeChangedEventArgs e) {
    Console.WriteLine("'{0}' 現在のメール総数: {1}", e.Mailbox.FullName, e.CurrentCount);
  };

  using (var inbox = client.OpenInbox()) {
    for (;;) {
      // 1分おきにメールボックスの状態を更新する
      inbox.Refresh();

      Thread.Sleep(TimeSpan.FromMinutes(1.0));
    }
  }
}

非同期メソッドを使って操作を開始した場合や、別スレッドで開始した操作の結果としてイベントが発生した場合は、そのスレッドからイベントハンドラが呼び出されるため、以下のような例ではInvokeRequiredがtrueとなります。

非同期処理とイベントハンドラ
public class Form1 : Form {
  ImapClient client;
  Label label1;

  public Form1()
  {
    client.ExistMessageCountChanged += delegate(object sender, ImapMailboxSizeChangedEventArgs e) {
      if (InvokeRequired) {
        // Invokeする必要がある場合
        Invoke(new Action(delegate {
          label1.Text = string.Format("'{0}' 現在のメール総数: {1}", e.Mailbox.FullName, e.CurrentCount);
        }));
      }
      else {
        // Invokeする必要がない場合
        label1.Text = string.Format("'{0}' 現在のメール総数: {1}", e.Mailbox.FullName, e.CurrentCount);
      }
    };
  }

    :
    :
}

イベントハンドラをUIスレッドで実行させたい場合は、ImapClient.SynchronizingObjectプロパティに同期オブジェクトとなるインスタンスを設定してください。 このようにすることでイベントハンドラでのInvokeが不要となります。

同期オブジェクトを指定する例
public class Form1 : Form {
  ImapClient client;
  Label label1;

  public Form1()
  {
    client.SynchronizingObject = this; // 同期オブジェクトとしてFormのインスタンスを指定する

    client.ExistMessageCountChanged += delegate(object sender, ImapMailboxSizeChangedEventArgs e) {
      // Invokeは不要
      label1.Text = string.Format("'{0}' 現在のメール総数: {1}", e.Mailbox.FullName, e.CurrentCount);
    };
  }

    :
    :
}

§5.1.3.2 ステータス変更の待機 (IDLE)

IMAP拡張コマンドであるIDLEコマンド(RFC 2177 - IMAP4 IDLE command)をサポートしているサーバーでは、このコマンドを使うことによりメールボックスのステータス変更などをプッシュ通知させることができます。 ステータス変更の待機にはImapOpenedMailboxInfo.Idleなどのメソッドを使うことが出来ます。 IDLE中に発生したメールボックスのステータス変更はImapClientクラスのExistMessageCountChangedイベントなどによって検知することができます。

IDLEによる通知の待機を行う例
using (ImapClient client = new ImapClient()) {
  // ステータス変更のイベントハンドラを設定
  client.RecentMessageCountChanged += delegate(object sender, ImapMailboxSizeChangedEventArgs e) {
    Console.WriteLine("'{0}'に{1}件の新着メッセージがあります", e.Mailbox.Name, e.CurrentCount);
  };

  // INBOXを開く
  using (ImapOpenedMailboxInfo inbox = client.OpenInbox()) {
    // 10分間待機する (この間に通知があれば通知内容に応じてイベントが発生する)
    inbox.Idle(TimeSpan.FromMinutes(10));
  }
}

ImapOpenedMailboxInfo.StartIdle, StopIdleメソッドを組み合わせて使うことにより、処理をブロックせずIDLE中に他の処理を行うようにすることもできます。

IDLEによる通知の待機を行い、その間に別の処理を行う例
using (ImapClient client = new ImapClient()) {
  // イベントハンドラを設定
  client.RecentMessageCountChanged += delegate(object sender, ImapMailboxSizeChangedEventArgs e) {
    Console.WriteLine("'{0}'に{1}件の新着メッセージがあります", e.Mailbox.Name, e.CurrentCount);
  };

  // INBOXを開く
  using (ImapOpenedMailboxInfo inbox = client.OpenInbox()) {
    // IDLEでの待機を開始する
    inbox.StartIdle();

    // ここで何らかの処理を行うことができる
    // 同時に、処理中に通知があればイベントが発生する
    Thread.Sleep(TimeSpan.FromMinutes(10));

    // 待機を終了する
    inbox.StopIdle();
  }
}

ImapOpenedMailboxInfo.WaitForMessageArrivalメソッドを使えば、新着メッセージの受信を待機と、受信したメッセージの取得を同時に行うことができます。

IDLEによる新着メッセージの待機と取得を行う例
using (ImapClient client = new ImapClient()) {
  // INBOXを開く
  using (var inbox = client.OpenInbox()) {
    // 新着メッセージの受信を待機する
    var newMessages = inbox.WaitForMessageArrival(Timeout.Infinite, ImapMessageFetchAttributeOptions.StaticAttributes);

    // 受信したメッセージの件名を表示する
    foreach (var message in newMessages) {
      Console.WriteLine(message.EnvelopeSubject);
    }
  }
}

なお、IDLEコマンドをサポートしていないサーバーに対してこれらのメソッドを使用した場合は、IDLEコマンドの代わりにNOOPコマンドを一定間隔おきに送信してステータスの変更を検知します。

IMAPでは、選択できるメールボックスは接続ごとに一つだけという制限があるため、IDLEコマンドでは複数のメールボックスのステータス変更を同時に待機することはできません。 したがって、複数のメールボックスのステータス変更を待機したい場合は、Refresh()メソッド(STATUSコマンド)を使って各メールボックスに対して定期的にポーリングを行うか、監視したいメールボックスの数だけ接続を用意してそのそれぞれでIDLEを行う必要があります。

§5.1.3.3 拡張機能の使用

Smdn.Net.Imap4.Clientでは、IMAP4rev1の基本仕様であるRFC 3501で定められているすべての機能のほかに、IDLE, QUOTA, SPECIAL-USE, SORT, MOVEなど、いくつかの拡張機能も実装しています。 これらの機能は必須の機能ではないため、サーバーによって実装している場合とそうでない場合があります。 IDLEやMOVEなどは、サポートされていなければ基本コマンドを使って動作のエミュレーションを行います。 一方、SPECIAL-USEやSORTなどエミュレーションを行うことが出来ない機能については、その機能を使用しようとした場合にはImapIncapableExceptionをスローします。

サーバーがある機能をサポートしているかを調べるには、ImapClient.ServerCapabilityプロパティを参照します。

サーバーがサポートしている機能を調べる例
using (ImapClient client = new ImapClient()) {
  // サーバーが各機能をサポートしているか調べる
  Console.WriteLine("IDLE: {0}", client.ServerCapabilities.Contains(ImapCapability.Idle));
  Console.WriteLine("SORT: {0}", client.ServerCapabilities.Contains(ImapCapability.Sort));
  Console.WriteLine("MOVE: {0}", client.ServerCapabilities.Contains(ImapCapability.Move));
  Console.WriteLine("SPECIAL-USE: {0}", client.ServerCapabilities.Contains(ImapCapability.SpecialUse));
  Console.WriteLine("QUOTA: {0}", client.ServerCapabilities.Contains(ImapCapability.Quota));

  // サポートしている場合はSPECIAL-USEを使い、そうでない場合は標準機能を使って
  // 「ごみ箱」のメールボックスを取得する
  ImapMailboxInfo trash;

  if (client.ServerCapabilities.Contains(ImapCapability.SpecialUse))
    trash = client.GetMailbox(ImapSpecialMailbox.Trash);
  else
    trash = client.GetMailbox("Trash");
}

また、サーバーがある機能を実装している場合でも、不具合があるなど何らかの理由でその機能を使用しないようにしたい場合は、ImapClient.DisabledCapabilityプロパティのコレクションにその機能を追加します。 例えば、IDLEをサポートしていたとしてもIDLEを使わないようにしたい場合は次のようにします。

サーバーがサポートしている機能を無効化する例
using (ImapClient client = new ImapClient()) {
  // サーバーがIDLEをサポートしている場合でも、IDLEの機能を
  // 無効化して使用しないようにする
  client.DisabledCapabilities.Add(ImapCapability.Idle);

  using (var inbox = client.OpenInbox()) {
    // 5分間待機する
    // (IDLEコマンドは使われず、NOOPコマンドによるポーリングが行われる)
    inbox.Idle(TimeSpan.FromMinutes(5));
  }
}

拡張機能を使用するメソッド、拡張機能が使用できない場合の動作等については§.拡張機能を使用しているメソッド・プロパティを参照してください。

§5.1.3.4 アカウントまたはサーバーをまたがるコピー・移動

ImapMessageInfoクラスおよびImapMessageInfoListクラスのCopyTo(), MoveTo()メソッドは、異なるアカウントまたはサーバーにあるメールボックスをコピー先・移動先として指定することもできます。 このメソッドでは、アカウント・サーバーをまたがったメッセージのコピー・移動を行うことができます。

アカウントまたはサーバーをまたがるメッセージのコピー
using (ImapClient clientSource = new ImapClient(new Uri("imap://user@source.imap.example.net/")),
                  clientDest   = new ImapClient(new Uri("imap://user@dest.imap.example.net/"))) {
  // コピー元アカウントのINBOXを開く
  var mailboxSource = clientSource.OpenInbox();
  // コピー元INBOXにあるUIDが1のメッセージを取得する
  var messageSource = mailboxSource.GetMessageByUid(1L);

  // コピー先アカウントのINBOXを取得する
  var mailboxDest = clientDest.GetInbox();

  // メッセージをコピー先アカウントのINBOXにコピーする
  messageSource.CopyTo(mailboxDest);
}

ImapMailboxInfo.MoveToメソッドは、コピー先として異なるアカウント・サーバーのメールボックスを表すImapMailboxInfoを指定することはできません。 そのようなImapMailboxInfoを指定した場合は例外をスローします。

一方ImapClient.CopyMailbox(), MoveMailbox()メソッドでは、アカウント・サーバーをまたがったメールボックスのコピー・移動を行うことができます。 このメソッドでは、メールボックスと、そのメールボックス内にあるすべてのメッセージをコピー・移動します。 なお、コピー先に全く同一内容のメッセージがある場合でも、上書きはされず別個のメッセージとしてコピーされます。

アカウントまたはサーバーをまたがるメールボックスのコピー
using (ImapClient clientSource = new ImapClient(new Uri("imap://user@source.imap.example.net/")),
                  clientDest   = new ImapClient(new Uri("imap://user@dest.imap.example.net/"))) {
  // コピー元アカウントのINBOXを取得
  var mailboxSource = clientSource.GetInbox();
  // コピー元メールボックスが階層を持つ場合、下位のメールボックスも再帰的にコピーするか
  bool recursive = true;
  // コピー先にメールボックスを作成する必要がある場合、作成と同時にメールボックスを購読するかどうか
  bool subscribe = true;

  // コピー元INBOXの内容を、コピー先アカウントのINBOXにコピーする
  ImapClient.CopyMailbox(mailboxSource, clientDest, "INBOX", recursive, subscribe);
}

上記のサンプルを実行した場合、メールボックスの構造は次のようになります。

コピー元アカウントのINBOX
INBOX
|--フォルダ1
|  `--サブフォルダ1
`--フォルダ2
コピー先アカウントのINBOX
INBOX
|--フォルダ1
|--フォルダ2
|  `--サブフォルダ2
`--フォルダ3
上記サンプル実行後のコピー先アカウントのINBOX
INBOX
|--フォルダ1
|  `--サブフォルダ1
|--フォルダ2
|  `--サブフォルダ2
`--フォルダ3

コピー先のメールボックス名を指定する代わりに、ImapMailboxInfoを指定することもできます。 この場合、コピー元メールボックスはコピー先メールボックスの下位のメールボックスとしてコピーされます。

アカウントまたはサーバーをまたがるメールボックスのコピー
using (ImapClient clientSource = new ImapClient(new Uri("imap://user@source.imap.example.net/")),
                  clientDest   = new ImapClient(new Uri("imap://user@dest.imap.example.net/"))) {
  // コピー元アカウントのINBOXを取得
  var mailboxSource = clientSource.GetInbox();
  // コピー先アカウントのINBOXを取得
  var mailboxDest = clientDest.GetInbox();
  // コピー元メールボックスが階層を持つ場合、下位のメールボックスも再帰的にコピーするか
  bool recursive = true;
  // コピー先にメールボックスを作成する必要がある場合、作成と同時にメールボックスを購読するかどうか
  bool subscribe = true;

  // コピー元INBOXの内容を、コピー先INBOXの"配下に"コピー
  ImapClient.CopyMailbox(mailboxSource, mailboxDest, recursive, subscribe);
}

上記のサンプルを実行した場合、コピー元INBOXはコピー先INBOXのサブフォルダとしてコピーされることになります。 上記サンプル実行後のメールボックスの構造は次のようになります。

コピー元アカウントのINBOX
INBOX
|--フォルダ1
|  `--サブフォルダ1
`--フォルダ2
コピー先アカウントのINBOX
INBOX
`--フォルダ3
上記サンプル実行後のコピー先アカウントのINBOX
INBOX
|--INBOX
|  |--フォルダ1
|  |  `--サブフォルダ1
|  `--フォルダ2
`--フォルダ3

ImapClient.CopyMailbox()メソッドの使用例として、アカウントのすべてのメールボックスとメッセージを、階層構造を維持したまま別のアカウントへコピーするには次のようにします。

すべてのメールボックスを別のアカウントへコピーする例
using (ImapClient clientSource = new ImapClient(new Uri("imap://user@source.imap.example.net/")),
                  clientDest   = new ImapClient(new Uri("imap://user@dest.imap.example.net/"))) {
  // コピー元アカウントの最上位のメールボックスをすべて取得して列挙
  foreach (var mailboxSource in clientSource.GetMailboxes(ImapMailboxListOptions.TopLevelOnly)) {
    // コピー元メールボックスを、コピー先アカウントに同名でコピー
    // (コピー元メールボックスが階層を持つ場合は下位のメールボックスもコピーする)
    ImapClient.CopyMailbox(mailboxSource, clientDest, mailboxSource.Name, true);
  }
}

なお、現在の実装では異なるImapClientから取得したImapMailboxInfoは、たとえそれが同一アカウントのメールボックスを表す場合でも異なるアカウントのメールボックスとして扱われます。 同一のImapClientから取得したImapMailboxInfoのみが同一アカウントのメールボックスとして扱われる点に注意してください。

§5.2 機能別リファレンス

以下はSmdn.Net.Imap4.Client名前空間の各クラスの機能別の簡易リファレンスです。 IMAP拡張機能を使用しているメソッド・プロパティについては、§.拡張機能を使用しているメソッド・プロパティにまとめているので適宜参照してください。

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

§5.2.1.1 メールボックスの取得と作成

以下はメールボックスの取得と作成に関するメソッドです。

メールボックスの取得と作成に関するメソッド
クラス メソッド 解説 対応するIMAPコマンド
ImapClient GetInbox()
GetInbox(ImapMailboxListOptions)
INBOXを取得します。 LIST/LSUB
GetInboxAsync()
GetInboxAsync(ImapMailboxListOptions)
GetMailbox(string)
GetMailbox(string, ImapMailboxListOptions)
指定した名前のメールボックスを取得します。
GetMailboxAsync(string)
GetMailboxAsync(string, ImapMailboxListOptions)
GetMailbox(ImapSpecialMailbox)
GetMailbox(ImapSpecialMailbox, ImapMailboxListOptions)
ImapSpecialMailboxで指定した種類のメールボックスを取得します。
GetMailboxAsync(ImapSpecialMailbox)
GetMailboxAsync(ImapSpecialMailbox, ImapMailboxListOptions)
GetMailboxes()
GetMailboxes(ImapMailboxListOptions)
すべてのメールボックスを取得します。
GetMailboxesAsync()
GetMailboxesAsync(ImapMailboxListOptions)
GetOrCreateMailbox(string)
GetOrCreateMailbox(string, ImapMailboxListOptions)
GetOrCreateMailbox(string, bool)
GetOrCreateMailbox(string, bool, ImapMailboxListOptions)
指定した名前のメールボックスを取得します。 存在しない場合は作成します。 オプションで、メールボックスを作成した場合に購読するかどうか指定できます。 LIST/LSUB, CREATE, SUBSCRIBE
GetOrCreateMailboxAsync(string)
GetOrCreateMailboxAsync(string, ImapMailboxListOptions)
GetOrCreateMailboxAsync(string, bool)
GetOrCreateMailboxAsync(string, bool, ImapMailboxListOptions)
CreateMailbox(string)
CreateMailbox(string, bool)
指定した名前のメールボックスを作成します。 CREATE, SUBSCRIBE
CreateMailboxAsync(string)
CreateMailboxAsync(string, bool)
ImapMailboxInfo GetChildren()
GetChildren(ImapMailboxListOptions)
現在のメールボックスの下位にあるすべてのメールボックスを取得します。 LIST/LSUB
GetChildrenAsync()
GetChildrenAsync(ImapMailboxListOptions)
GetChild(string)
GetChild(string, ImapMailboxListOptions)
現在のメールボックスの下位にある指定した名前のメールボックスを取得します。
GetChildAsync(string)
GetChildAsync(string, ImapMailboxListOptions)
GetParent()
GetParent(ImapMailboxListOptions)
現在のメールボックスの上位のメールボックスを取得します。
GetParentAsync()
GetParentAsync(ImapMailboxListOptions)
GetOrCreateParent()
GetOrCreateParent(ImapMailboxListOptions)
GetOrCreateParent(bool)
GetOrCreateParent(bool, ImapMailboxListOptions)
現在のメールボックスの上位のメールボックスを取得します。 存在しない場合は作成します。 オプションで、メールボックスを作成した場合に購読するかどうか指定できます。 LIST/LSUB, CREATE, SUBSCRIBE
GetOrCreateParentAsync()
GetOrCreateParentAsync(ImapMailboxListOptions)
GetOrCreateParentAsync(bool)
GetOrCreateParentAsync(bool, ImapMailboxListOptions)
GetOrCreateChild(string)
GetOrCreateChild(string, ImapMailboxListOptions)
GetOrCreateChild(string, bool)
GetOrCreateChild(string, bool, ImapMailboxListOptions)
現在のメールボックスの下位にある指定した名前のメールボックスを取得します。 存在しない場合は作成します。 オプションで、メールボックスを作成した場合に購読するかどうか指定できます。
GetOrCreateChildAsync(string)
GetOrCreateChildAsync(string, ImapMailboxListOptions)
GetOrCreateChildAsync(string, bool)
GetOrCreateChildAsync(string, bool, ImapMailboxListOptions)
CreateChild(string)
CreateChild(string, bool)
現在のメールボックスの下位に指定した名前でメールボックスを作成します。 オプションで、メールボックスを作成した場合に購読するかどうか指定できます。 CREATE, SUBSCRIBE
CreateChildAsync(string)
CreateChildAsync(string, bool)
クラス メソッド 解説 対応するIMAPコマンド
すべてのメソッド
これらのメソッドは、メールボックスを表すImapMailboxInfoクラスのインスタンス、もしくは読み取り専用のIList<ImapMailboxInfo>(.NET 4.5ではIReadOnlyList<ImapMailboxInfo>)を返します。
ImapMailboxInfo.GetParent(), GetOrCreateParent()
最上位のメールボックスに対して呼び出した場合、例外ImapProtocolViolationExceptionをスローします。
ImapMailboxInfo.GetChild(), GetOrCreateChild(), CreateChild()
下位に階層を作成できないメールボックス(CanHaveChildプロパティがfalse)の場合、ImapProtocolViolationExceptionをスローします。
メールボックスの取得オプション(ImapMailboxListOptions)
これらのメソッドには、ImapMailboxListOptionsを引数にとるバージョンのオーバーロードを用意してあります。 ImapMailboxListOptionsを指定することで、メールボックスの取得時の動作を変更することができます。 以下の値を組み合わせて指定できます。
SubscribedOnly
存在するメールボックスのうち、購読中のメールボックスのみを取得します。 このオプションを指定しない場合は、購読中かどうかを区別せずに取得します。
TopLevelOnly
存在するメールボックスのうち、直下の階層にあるメールボックスのみを取得します。 このオプションを指定しない場合は、すべての階層にあるメールボックスを取得します。
Remote
存在するメールボックスのうち、リモートサーバ上にあるメールボックスのみを取得します。 このオプションを指定しない場合は、リモートサーバ上のメールボックスかどうか区別せずに取得します。 サーバがMAILBOX REFERRALSに対応している場合のみ有効です。
RequestStatus
メールボックスの取得と同時に、メールボックスのメッセージ数などのステータスも取得します。 このオプションを指定しない場合は、ステータスは取得しません。
Default
オプションのデフォルト値です。 上記オプションのいずれも指定しない場合と同値です。
ImapSpecialMailboxを引数にとるメソッド
サーバがSPECIAL-USEをサポートしていない場合は、ImapIncapableExceptionをスローします。
ImapSpecialMailboxで指定できる値は次のとおりです。
ImapSpecialMailboxで指定できる値
解説 対応するメールボックスのフラグ
ImapSpecialMailbox.All すべてのメールが格納されているメールボックスを表します。 \All
ImapSpecialMailbox.Archive アーカイブされたメールが格納されているメールボックスを表します。 \Archive
ImapSpecialMailbox.Drafts 下書きのメール・未送信のメールが格納されているメールボックスを表します。 \Drafts
ImapSpecialMailbox.Flagged 何らかのフラグが設定されているメールが格納されているメールボックスを表します。 \Flagged
ImapSpecialMailbox.Junk スパムや迷惑メールなどが格納されているメールボックスを表します。 \Junk
ImapSpecialMailbox.Sent 送信済みのメールが格納されているメールボックスを表します。 \Sent
ImapSpecialMailbox.Trash 削除済みや削除予定のメールが格納されているメールボックスを表します。 \Trash

§5.2.1.2 メールボックス名の結合・分割

以下はメールボックスの結合・分割に関するメソッドです。

メールボックスの結合・分割に関するメソッド
クラス メソッド 解説 対応するIMAPコマンド
ImapClient CombineMailboxName(string)
CombineMailboxName(string, string)
CombineMailboxName(string, string, string, ...)
CombineMailboxName(IEnumerable<string>)
CombineMailboxName(string[])
分割されているメールボックス名を結合します。 結合に際して、階層区切り文字をサーバーに問い合わせます。 LIST
ImapMailboxInfo GetFullNameOf(string)
GetFullNameOf(string, string)
GetFullNameOf(string, string, string, ...)
GetFullNameOf(IEnumerable<string>)
GetFullNameOf(string[])
インスタンスのメールボックス名と指定されたメールボックス名を結合します。 結合に際して、MailboxSeparatorプロパティの値を階層区切り文字として使用します。 -
SplitMailboxHierarchy(string) 階層化されているメールボックス名を階層ごとに分割して配列として返します。 分割に際して、MailboxSeparatorプロパティの値を階層区切り文字として使用します。 -
クラス メソッド 解説 対応するIMAPコマンド
ImapClient.CombineMailboxNameメソッドの動作と結果
{"INBOX", "Archive", "Sent"}を結合する場合、まず最上位のメールボックスINBOXで使用される階層区切り文字の問い合わせを行います。 INBOXの階層区切り文字が"/"の場合、CombineMailboxName("INBOX", "Archive", "Sent")"INBOX/Archive/Sent"を返します。
ImapMailboxInfo.GetFullNameOfメソッドの動作と結果
FullNameプロパティが"INBOX/Archive"で、MailboxSeparatorプロパティが"/"の場合、GetFullNameOf("Sent", "2014")"INBOX/Archive/Sent/2014"を返します。
ImapMailboxInfo.SplitMailboxHierarchyメソッドの動作と結果
MailboxSeparatorプロパティが"/"の場合、SplitMailboxHierarchy("INBOX/Archive/Sent")は配列{"INBOX", "Archive", "Sent"}を返します。
メールボックスが階層を持てない場合
下位に階層を作成できないメールボックス(CanHaveChildプロパティがfalse)の場合、GetFullNameOf()メソッドおよびSplitMailboxHierarchy()メソッドはImapProtocolViolationExceptionをスローします。

§5.2.1.3 メールボックスの操作

以下はメールボックスの削除・作成・リネーム等の操作に関するメソッドです。

メールボックスの削除・作成・リネーム等の操作に関するメソッド
クラス メソッド 解説 対応するIMAPコマンド
ImapMailboxInfo Refresh()
Refresh(bool)
メールボックスのステータスを更新します。 オプションでメールボックスの属性を表すフラグも再取得して更新するかどうかを指定できます。 STATUS, LIST
RefreshAsync()
RefreshAsync(bool)
Delete()
Delete(bool)
メールボックスが存在する場合、削除します。 オプションで購読解除するかどうか指定できます。 DELETE, UNSUBSCRIBE
DeleteAsync()
DeleteAsync(bool)
Create()
Create(bool)
メールボックスが存在しない場合、作成します。 オプションで購読するかどうか指定できます。 CREATE, SUBSCRIBE
CreateAsync()
CreateAsync(bool)
MoveTo(ImapMailboxInfo)
MoveTo(ImapMailboxInfo, bool)
メールボックスを指定したメールボックスの下位に移動します。 RENAME, SUBSCRIBE, UNSUBSCRIBE
MoveToAsync(ImapMailboxInfo)
MoveToAsync(ImapMailboxInfo, bool)
MoveTo(string)
MoveTo(string, bool)
メールボックスを指定した名前に変更します。
MoveToAsync(string)
MoveToAsync(string, bool)
Subscribe()
Subscribe(bool)
メールボックスを購読します。 オプションで、下位にあるすべてのメールボックスも購読するかどうか指定できます。 SUBSCRIBE, LIST/LSUB
SubscribeAsync()
SubscribeAsync(bool)
Unsubscribe()
Unsubscribe(bool)
メールボックスを購読解除します。 オプションで、下位にあるすべてのメールボックスも購読解除するかどうか指定できます。 UNSUBSCRIBE, LIST/LSUB
UnsubscribeAsync()
UnsubscribeAsync(bool)
クラス メソッド 解説 対応するIMAPコマンド
移動先のメールボックス
移動先のメールボックスをImapMailboxInfoで指定する場合、ImapMailboxInfoが異なるクライアントから取得したものだった場合はNotSupportedExceptionをスローします。 アカウントもしくはサーバをまたがるメールボックスの移動はImapClient.MoveMailbox()を使用してください。
メールボックスの削除
Delete()メソッドでは、メールボックス内にメールが存在するかどうかに関わらずメールボックスを削除します。 削除したメールボックス内のメールは復元できないため注意してください。 「ごみ箱に入れる」の動作を実装する場合は、MoveTo()メソッドでメールボックスを移動するようにします。
INBOXの削除
プロトコル上INBOXを削除することは出来ません。 Delete()メソッドでINBOXを削除しようとした場合にはImapProtocolViolationExceptionをスローします。
INBOXの移動
MoveTo()メソッドでINBOXを移動しようとした場合にはImapProtocolViolationExceptionをスローします。

§5.2.1.4 アカウント・サーバーをまたがるメールボックスの移動・コピー

以下はアカウントもしくはサーバーをまたがった(異なるクライアント間での)メールボックスの移動・コピーを行うメソッドです。

異なるクライアント間でのメールボックスの移動・コピーを行うメソッド
クラス メソッド 解説
ImapClient MoveMailbox(ImapMailboxInfo, ImapClient, string)
MoveMailbox(ImapMailboxInfo, ImapClient, string, bool)
MoveMailbox(ImapMailboxInfo, ImapClient, string, bool, bool)
CopyMailbox(ImapMailboxInfo, ImapClient, string)
CopyMailbox(ImapMailboxInfo, ImapClient, string, bool)
CopyMailbox(ImapMailboxInfo, ImapClient, string, bool, bool)

(いずれも静的メソッド)
指定されたメールボックスを、別のクライアントが接続しているアカウントへ移動またはコピーします。 移動・コピー先は指定したメールボックス名を使用します。 オプションで移動・コピー先のメールボックスを購読するかどうか、再帰的に移動・コピーするかどうかを指定することができます。 戻り値として移動・コピー先となったメールボックスのImapMailboxInfoが返されます。
MoveMailbox(ImapMailboxInfo, ImapMailboxInfo)
MoveMailbox(ImapMailboxInfo, ImapMailboxInfo, bool)
MoveMailbox(ImapMailboxInfo, ImapMailboxInfo, bool, bool)
CopyMailbox(ImapMailboxInfo, ImapMailboxInfo)
CopyMailbox(ImapMailboxInfo, ImapMailboxInfo, bool)
CopyMailbox(ImapMailboxInfo, ImapMailboxInfo, bool, bool)

(いずれも静的メソッド)
指定されたメールボックスを、別のメールボックスの下位に移動またはコピーします。 オプションで移動・コピー先のメールボックスを購読するか、再帰的に移動・コピーするかどうかを指定することができます。
同一のクライアント間での移動・コピー
これらのメソッドは異なるクライアント間だけでなく、同一のクライアント間での移動・コピーもサポートしています。
現在開かれているメールボックス
これらのメソッド内では、移動・コピーに際して内部的にメールボックスを開いたり閉じたりします。 そのため、このメソッドを呼び出しの前後では、コピー元・コピー先で開かれているメールボックスは暗黙的に閉じられます。 進行中の処理がある場合は、先にメールボックスを閉じるようにしてください。
既に移動・コピー先が存在する場合
既に移動・コピー先のメールボックスが存在する場合は、その内容は上書きされずマージされます。 上書きしたい場合は、先に移動・コピー先のメールボックスを削除してください。

§5.2.1.5 メールボックスの状態の取得

以下はメールボックスの状態を表すプロパティです。

メールボックスの状態を表すプロパティ
クラス プロパティ 解説
ImapMailboxInfo FullName 階層を含むメールボックスの完全な名前を返します。
Name 階層を含まないメールボックスの名前を返します。
ParentMailboxName 上位のメールボックスを返します。 インスタンスが最上位のメールボックスを表す場合、空の文字列を返します。
MailboxSeparator メールボックスの階層区切り文字を返します。 メールボックスが階層構造を持たない場合、空の文字列を返します。
Url このメールボックスを表すIMAP URLを返します。
Exists メールボックスが存在するかどうかを表す値を返します。
Flags メールボックスの属性を表すフラグを返します。
IsUnselectable メールボックスが選択不可とされているかどうかを表す値を返します。 値がtrueの場合、Open()などのメソッドでメールボックスを選択することはできません。 値がfalseの場合でも、選択できないか選択に失敗する可能性があります。
IsOpen メールボックスが現在開かれているかどうかを表す値を返します。 ImapMailboxInfo.FullNameとImapClient.OpenedMailbox.FullNameが同名であれば、trueとなります。
IsInbox メールボックスがINBOXであるかどうかを表す値を返します。
CanHaveChild 下位のメールボックスを作成できるか(メールボックスが階層構造を持つことができるか)どうかを表す値を返します。 値がfalseの場合、CreateChild()などのメソッドで下位のメールボックスを作成することはできません。
メールボックスのサイズ UnseenMessageCount メールボックスにある未読のメッセージ数を返します。 ステータスを取得していない場合、0を返します。 なおサーバーによっては、未読のメッセージが存在する場合でも0を返す場合があります。
ExistMessageCount メールボックスに存在するすべてのメッセージ数を返します。 ステータスを取得していない場合、0を返します。
RecentMessageCount メールボックスにある新着のメッセージ数を返します。 ステータスを取得していない場合、0を返します。
UidValidity メールボックスのUIDVALIDITY値を返します。 UIDが永続的でない場合などは、0を返します。
NextUid メールボックスの次のメッセージに付与されるUIDを返します。 UIDが永続的でない場合などは、0を返します。 なおサーバーによっては、次のメッセージに付与されるUIDが決定している場合でも0を返す場合があります。
HighestModSeq メールボックス内のメッセージに設定されているmod-sequence値のうち最大の値を返します。
クラス プロパティ 解説
メールボックスのサイズに関するプロパティ
これらのプロパティは、ImapClient.GetMailbox()などのメソッドの引数でImapMailboxListOptions.RequestStatusを指定しなかった場合は0を返します。 また、ImapMailboxInfo.Refresh()などのメソッドを実行した後に値が更新されることがあります。

§5.2.1.6 メールボックスの選択

以下はメールボックスの選択に関するメソッドです。 メールボックス内のメッセージに対する操作は、現在選択しているメールボックスのものに対してのみ行えます。

メールボックスの選択に関するメソッド
クラス メソッド 解説 対応するIMAPコマンド
ImapClient OpenInbox()
OpenInbox(bool)
INBOXを開きます。 オプションで読み取り専用で開くかどうかを指定できます。 SELECT/EXAMINE
OpenInboxAsync()
OpenInboxAsync(bool)
OpenMailbox(ImapMailboxInfo)
OpenMailbox(ImapMailboxInfo, bool)
指定したインスタンスが表すメールボックスを開きます。 オプションで読み取り専用で開くかどうかを指定できます。
別のクライアントから取得したImapMailboxInfoを指定した場合は、ArgumentExceptionをスローします。
OpenMailboxAsync(ImapMailboxInfo)
OpenMailboxAsync(ImapMailboxInfo, bool)
OpenMailbox(string)
OpenMailbox(string, bool)
OpenMailbox(string, ImapMailboxListOptions)
OpenMailbox(string, ImapMailboxListOptions, bool)
指定した名前のメールボックスを開きます。 オプションで読み取り専用で開くかどうかを指定できます。
OpenMailboxAsync(string)
OpenMailboxAsync(string, bool)
OpenMailboxAsync(string, ImapMailboxListOptions)
OpenMailboxAsync(string, ImapMailboxListOptions, bool)
OpenMailbox(ImapSpecialMailbox)
OpenMailbox(ImapSpecialMailbox, bool)
OpenMailbox(ImapSpecialMailbox, ImapMailboxListOptions)
OpenMailbox(ImapSpecialMailbox, ImapMailboxListOptions, bool)
ImapSpecialMailboxで指定した種類のメールボックスを開きます。 オプションで読み取り専用で開くかどうかを指定できます。
OpenMailboxAsync(ImapSpecialMailbox)
OpenMailboxAsync(ImapSpecialMailbox, bool)
OpenMailboxAsync(ImapSpecialMailbox, ImapMailboxListOptions)
OpenMailboxAsync(ImapSpecialMailbox, ImapMailboxListOptions, bool)
OpenOrCreateMailbox(string)
OpenOrCreateMailbox(string, bool)
OpenOrCreateMailbox(string, ImapMailboxListOptions)
OpenOrCreateMailbox(string, bool, bool, ImapMailboxListOptions)
指定した名前のメールボックスを開きます。 存在しない場合は作成してから開きます。 オプションで読み取り専用で開くかどうかを指定できます。 SELECT/EXAMINE, CREATE, SUBSCRIBE
OpenOrCreateMailboxAsync(string)
OpenOrCreateMailboxAsync(string, bool)
OpenOrCreateMailboxAsync(string, ImapMailboxListOptions)
OpenOrCreateMailboxAsync(string, bool, bool, ImapMailboxListOptions)
ImapMailboxInfo Open()
Open(bool)
インスタンスが表すメールボックスを開きます。 オプションで読み取り専用で開くかどうかを指定できます。 SELECT/EXAMINE
OpenAsync()
OpenAsync(bool)
クラス メソッド 解説 対応するIMAPコマンド
すべてのメソッド
これらのメソッドは、選択済みメールボックスを表すImapOpenedMailboxInfoクラスのインスタンスを返します。
ImapSpecialMailboxを引数にとるメソッド
サーバがSPECIAL-USEをサポートしていない場合は、ImapIncapableExceptionをスローします。
メールボックスの選択に関するプロパティ
クラス プロパティ 解説
ImapClient OpenedMailbox 現在開いているメールボックスのインスタンス(ImapOpenedMailboxInfo)を取得します。 開いていない場合はnullを返します。

§5.2.2 選択済みメールボックスに対する操作

§5.2.2.1 選択済みメールボックスの操作

以下は選択済みメールボックスの操作に関するメソッドです。 これ以外にも、GetChild()MoveTo()などImapMailboxInfoから継承されるメソッドも使うことができます。

選択済みメールボックスの操作に関するメソッド
クラス メソッド 解説 対応するIMAPコマンド
ImapOpenedMailboxInfo Refresh()
Refresh(bool)
メールボックスの状態を更新します。 サーバからメールボックスの状態に関する通知があった場合はイベントを発生させます。 オプションでメールボックスの属性を表すフラグも再取得して更新するかどうかを指定できます。 NOOP, LIST
RefreshAsync()
RefreshAsync(bool)
Expunge() メールボックスにある削除フラグが設定されているメッセージをすべて削除します。 EXPUNGE
ExpungeAsync()
Close() メールボックスを閉じます。 CLOSE
CloseAsync()
IDisposable.Dispose()
ImapClient CloseMailbox() 選択済みのメールボックスがある場合、メールボックスを閉じます。 ない場合は何もしません。 CLOSE
CloseMailboxAsync()
クラス メソッド 解説 対応するIMAPコマンド

§5.2.2.2 選択済みメールボックスの状態

以下は選択済みメールボックスの状態を表すプロパティです。 これ以外にも、ImapMailboxInfoから継承されるプロパティも参照することができます。

選択済みメールボックスの状態を表すプロパティ
クラス プロパティ 解説
ImapOpenedMailboxInfo IsOpen メールボックスが現在開かれているかどうかを表す値を返します。 インスタンスがImapClient.OpenedMailboxと同一であれば、trueとなります。
IsReadOnly メールボックスを読み取り専用で選択したかどうかを表す値を返します。
IsUidPersistent メールボックス内の各メッセージに割り当てられているUIDが永続的かどうかを表す値を返します。
IsAllowedToCreateKeywords メールボックス内の各メッセージに、ユーザ定義のキーワードを設定できるかどうかを表す値を返します。
ApplicableFlags メールボックス内の各メッセージに設定できるフラグ・キーワードの一覧を返します。
PermanentFlags メールボックス内で永続的に保持されるフラグ・キーワードの一覧を返します。
FirstUnseenMessageNumber メールボックスを選択した時点で最初の未読メッセージの通番を返します。 未読メッセージがない場合は0を返します。
クラス プロパティ 解説

§5.2.2.3 メッセージの待機 (IDLE)

以下は選択済みメールボックスでのメッセージの待機、アイドル(IDLE)に関するプロパティおよびメソッドです。

選択済みメールボックスでのメッセージの待機に関するプロパティ
クラス プロパティ 解説
ImapOpenedMailboxInfo IsIdle 現在このメールボックスでメッセージの待機中(IDLE操作の進行中)かどうかを表す値を返します。
ImapClient IsIdle 現在このクライアントでメッセージの待機中(IDLE操作が進行中)かどうかを表す値を返します。 ImapClient.OpenedMailbox.IsIdleと同じ値を返します。
選択済みメールボックスでのメッセージの待機に関するメソッド
クラス メソッド 解説 対応するIMAPコマンド
ImapOpenedMailboxInfo Idle(int)
Idle(int, int)
Idle(TimeSpan)
Idle(TimeSpan, TimeSpan)
アイドル状態に入り、接続状態を維持します。 このメソッドは処理をブロックします。 引数timeoutで指定した時間が経過すると、メソッド呼び出しが終了します。 タイムアウトにTimeout.Infinite(-1)を指定することはできません。

オプションでNOOPの送信間隔を指定できます(サーバーがIDLEをサポートしていない場合のみ有効)。
NOOP/IDLE
StartIdle()
StartIdle(int)
StartIdle(TimeSpan)
アイドル状態に入り、接続状態を維持します。 このメソッドは処理をブロックせず、すぐにメソッド呼び出しが終了します。 StopIdle()メソッドを呼び出すことでアイドル状態から復帰します。

オプションでNOOPの送信間隔を指定できます(サーバーがIDLEをサポートしていない場合のみ有効)。
NOOP/IDLE
StopIdle() StartIdleで開始したアイドル状態から復帰します。 -
WaitForMessageArrival(int)
WaitForMessageArrival(int, int)
WaitForMessageArrival(int, ImapMessageFetchAttributeOptions)
WaitForMessageArrival(int, ImapMessageFetchAttributeOptions, int)
WaitForMessageArrival(TimeSpan)
WaitForMessageArrival(TimeSpan, TimeSpan)
WaitForMessageArrival(TimeSpan, ImapMessageFetchAttributeOptions)
WaitForMessageArrival(TimeSpan, ImapMessageFetchAttributeOptions, TimeSpan)
アイドル状態に入り、接続状態を維持しつつ、新着メッセージの受信を待機します。 このメソッドは処理をブロックします。 新着メッセージを受信するか、引数timeoutで指定した時間が経過すると、メソッド呼び出しが終了します。 タイムアウトにTimeout.Infinite(-1)を指定することもできます。 新着メッセージの受信により待機を終了した場合は、戻り値のImapMessageInfoListに新着メッセージが含まれます。

オプションでNOOPの送信間隔を指定できます(サーバーがIDLEをサポートしていない場合のみ有効)。
NOOP/IDLE
StartWaitForMessageArrival()
StartWaitForMessageArrival(ImapMessageFetchAttributeOptions)
StartWaitForMessageArrival(int)
StartWaitForMessageArrival(TimeSpan)
StartWaitForMessageArrival(ImapMessageFetchAttributeOptions, int)
StartWaitForMessageArrival(ImapMessageFetchAttributeOptions, TimeSpan)
アイドル状態に入り、接続状態を維持しつつ、新着メッセージの受信を待機します。 このメソッドは処理をブロックせず、すぐにメソッド呼び出しが終了します。 StopWaitForMessageArrival()メソッドを呼び出すことでアイドル状態から復帰します。

オプションでNOOPの送信間隔を指定できます(サーバーがIDLEをサポートしていない場合のみ有効)。
NOOP/IDLE
StopWaitForMessageArrival() StartWaitForMessageArrivalで開始したアイドル状態から復帰します。 待機中に新着メッセージを受信していた場合は、戻り値のImapMessageInfoListに新着メッセージが含まれます。 -
NOOPの送信間隔 (pollingInterval)
サーバがIDLEをサポートしていない場合、IDLEでの待機の代わりに、引数pollingIntervalで指定された間隔でNOOPを送信します。 指定しなかった場合はデフォルトの動作として10分おきにNOOPを送信します。 pollingIntervalには30分以上の値を指定することはできません。 IDLEをサポートしている場合は、引数pollingIntervalの指定は動作に影響しません。
メッセージ属性の取得オプション(ImapMessageFetchAttributeOptions)
ImapMessageFetchAttributeOptionsについては、ImapOpenedMailboxInfo.GetMessages()等のメソッドの解説を参照してください。
サーバー側からの自動ログアウト
IMAPサーバーでは、一定時間操作がないと自動的にログアウトが行われます。 サーバがIDLEをサポートしている場合は、一定間隔毎にIDLEしなおすことでサーバー側からの自動切断を抑止します。 そのため、IDLE中はRefresh()メソッドなどを使った自動ログアウトタイマの更新操作を行う不要はありません。 IDLEをサポートしていない場合でも、NOOPコマンドによるポーリングを行うため、同様にログアウトタイマの更新操作は不要です。
待機中の操作とイベント
IDLEを開始してから終了するまでの間は、他のコマンドを実行することはできません。 待機中に他の操作を行おうとした場合、例外をスローします。
また、待機中は受信したレスポンスに応じてImapClient.ExistMessageCountChangedなどのイベントが発生します。
そのため、これらのイベントハンドラで何らかの操作を行うようにしていると、例外がスローされてしまうため注意してください。
§.非同期操作の制限も合わせて参照してください。
IDLE中のReceiveTimeout
サーバからのレスポンスを待機する都合上、IDLE中はReceiveTimeoutの値がTimeout.Inifinite(-1)に設定されます。 例えばStartIdle()メソッドでIDLEを開始した直後にReceiveTimeoutはTimeout.Inifiniteに設定され、EndIdle()メソッドでIDLEを終了する際にReceiveTimeoutはIDLE前の値に戻ります。 なお、IDLE中にReceiveTimeoutの値を変更することはできず、変更しようとした場合には例外がスローされます。
ImapClient.IsBusy, ImapClient.IsIdle, ImapOpenedMailboxInfo.IsIdle
IDLE中、各プロパティはtrueを返します。 サーバがIDLEをサポートしていない場合、NOOPでの待機を行う場合でもIsBusy, IsIdleプロパティはtrueとなります。

§5.2.3 メールボックス内のメッセージに対する操作

§5.2.3.1 メッセージの取得と検索

以下はメールボックスにあるメッセージの取得と検索に関するメソッドです。

メールボックス内の単一のメッセージを取得するメソッド
クラス メソッド 解説 対応するIMAPコマンド
ImapOpenedMailboxInfo GetMessageByUid(long)
GetMessageByUid(long, ImapMessageFetchAttributeOptions)
メールボックスにあるメッセージのうち、指定したUIDを持つ1つのメッセージを取得します。 該当するUIDのメッセージが無い場合はImapMessageNotFoundExceptionをスローします。 FETCH
GetMessageByUidAsync(long)
GetMessageByUidAsync(long, ImapMessageFetchAttributeOptions)
GetMessageBySequence(long)
GetMessageBySequence(long, ImapMessageFetchAttributeOptions)
メールボックスにあるメッセージのうち、指定した通番を持つ1つのメッセージを取得します。 該当する通番のメッセージが無い場合はImapMessageNotFoundExceptionをスローします。
GetMessageBySequenceAsync(long)
GetMessageBySequenceAsync(long, ImapMessageFetchAttributeOptions)
GetMessageFirstUnseen()
GetMessageFirstUnseen(ImapMessageFetchAttributeOptions)
メールボックスにあるメッセージのうち、最初の未読メッセージを取得します。 未読メッセージがない場合はnullを返します。
GetMessageFirstUnseenAsync()
GetMessageFirstUnseenAsync(ImapMessageFetchAttributeOptions)
クラス メソッド 解説 対応するIMAPコマンド
メールボックス内の複数のメッセージの取得・検索に関するメソッド
クラス メソッド 解説 対応するIMAPコマンド
ImapOpenedMailboxInfo GetMessages()
GetMessages(ImapMessageFetchAttributeOptions)
メールボックスにあるすべてのメッセージを取得します。 (FETCH)
GetMessages(long, params long[])
GetMessages(ImapMessageFetchAttributeOptions, long, params long[])
メールボックスにあるメッセージのうち、指定したUIDを持つ1つ以上のメッセージを取得します。
GetMessagesRecent()
GetMessagesRecent(ImapMessageFetchAttributeOptions)
メールボックスにあるメッセージのうち、新着メッセージ(\Recentフラグが設定されているメッセージ)を取得します。 (SEARCH, FETCH)
GetMessagesUnseen()
GetMessagesUnseen(ImapMessageFetchAttributeOptions)
メールボックスにあるメッセージのうち、未読メッセージ(\Seenフラグが設定されていないメッセージ)を取得します。
GetMessagesNew()
GetMessagesNew(ImapMessageFetchAttributeOptions)
メールボックスにあるメッセージのうち、新しいメッセージ(新着あるいは未読のメッセージ)を取得します。
GetMessages(ImapSearchCriteria)
GetMessages(ImapSearchCriteria, ImapMessageFetchAttributeOptions)
GetMessages(ImapSearchCriteria, Encoding)
GetMessages(ImapSearchCriteria, Encoding, ImapMessageFetchAttributeOptions)
指定した検索条件にマッチするメッセージを取得します。 オプションでエンコーディングを指定できます。
GetMessagesGmailSearch(string)
GetMessagesGmailSearch(string, ImapMessageFetchAttributeOptions)
Gmail search syntax (Gmail詳細検索演算子)にマッチしたメッセージを取得します。 Gmailサーバー以外の場合(X-GM-RAWをサポートしていない場合)は、ImapIncapableExceptionをスローします。 (SEARCH X-GM-RAW, FETCH)
GetSortedMessages(ImapSortCriteria)
GetSortedMessages(ImapSortCriteria, ImapMessageFetchAttributeOptions)
メールボックスにあるメッセージを指定したソート条件でソート済みの状態で取得します。 オプションでエンコーディングを指定できます。
サーバがSORTコマンドをサポートしていない場合は、ImapIncapableExceptionをスローします。
(SORT, FETCH)
GetSortedMessages(ImapSortCriteria, ImapSearchCriteria)
GetSortedMessages(ImapSortCriteria, ImapSearchCriteria, ImapMessageFetchAttributeOptions)
GetSortedMessages(ImapSortCriteria, ImapSearchCriteria, Encoding)
GetSortedMessages(ImapSortCriteria, ImapSearchCriteria, Encoding, ImapMessageFetchAttributeOptions)
メールボックスにあるメッセージのうち、指定した検索条件にマッチするメッセージのみを指定したソート条件でソート済みの状態で取得します。 オプションでエンコーディングを指定できます。
サーバがSORTコマンドをサポートしていない場合は、ImapIncapableExceptionをスローします。
GetThreadedMessages(ImapThreadingAlgorithm)
GetThreadedMessages(ImapThreadingAlgorithm, ImapMessageFetchAttributeOptions)
メールボックスにあるメッセージを指定したアルゴリズムでスレッド化した状態で取得します。
サーバがTHREADコマンドをサポートしていない場合は、ImapIncapableExceptionをスローします。
THREAD
GetThreadedMessages(ImapThreadingAlgorithm, ImapSearchCriteria)
GetThreadedMessages(ImapThreadingAlgorithm, ImapSearchCriteria, Encoding)
GetThreadedMessages(ImapThreadingAlgorithm, ImapSearchCriteria, Encoding, ImapMessageFetchAttributeOptions)
メールボックスにあるメッセージのうち、指定した検索条件にマッチするメッセージのみを指定したアルゴリズムでスレッド化した状態で取得します。 オプションでエンコーディングを指定できます。
サーバがTHREADコマンドをサポートしていない場合は、ImapIncapableExceptionをスローします。
THREAD
ImapMessageInfoList GetEnumerator() ImapMessageInfoListインスタンスが表すクエリに該当するImapMessageInfoを取得し、列挙します。 (ImapMessageInfoListを取得したメソッドによる)
ToList() ImapMessageInfoListインスタンスが表すクエリに該当するImapMessageInfoを取得し、List<ImapMessageInfo>として返します。
ToListAsync()
ToArray() ImapMessageInfoListインスタンスが表すクエリに該当するImapMessageInfoを取得し、ImapMessageInfoの配列として返します。
ToArrayAsync()
クラス メソッド 解説 対応するIMAPコマンド
メッセージ属性の取得オプション(ImapMessageFetchAttributeOptions)
これらのメソッドには、ImapMessageFetchAttributeOptionsを引数にとるバージョンのオーバーロードを用意してあります。 ImapMessageFetchAttributeOptionsを指定することで、メッセージ取得時に取得する属性を指定することができます。 以下の値を組み合わせて指定できます。
StaticAttributes
メッセージに設定される静的な属性のみを取得します。
DynamicAttributes
メッセージに設定される動的な属性のみを取得します。
AllAttributes
メッセージに設定されるすべての属性を取得します。 StaticAttributesとDynamicAttributesの組み合わせと同値です。
Default, None
オプションのデフォルト値です。 上記オプションのいずれも指定しない場合と同値です。
ImapOpenedMailboxInfo.Get(Sorted)Messages(...)
これらのメソッドは戻り値としてImapMessageInfoListを返します。 メソッドの呼び出し時点ではコマンドの送受信は行われず、戻り値のImapMessageInfoListに対して列挙操作を開始する時点で初めてコマンドの送受信が行われます。
ImapMessageInfoList
このクラスはList<ImapMessageInfo>のようなコレクションクラスではなく、検索クエリを内包して結果を遅延取得するクラスです。 列挙操作を行うことで検索クエリに一致するメッセージの検索、ImapMessageInfoの生成が行われます。 このクラスから取得したImapMessageInfoを保持したい場合は、List<ImapMessageInfo>などに格納しなおすか、ToArray()、ToList()などのメソッドを使って生成した結果を使用してください。
エンコーディング
サーバーが指定されたエンコーディングをサポートしていないことを示すレスポンス([BADCHARSET])を返した場合は、例外Smdn.Formats.EncodingNotSupportedExceptionをスローします。

§5.2.3.1.1 メッセージの検索クエリ(ImapSearchCriteria)

ImapSearchCriteriaクラスはメッセージの検索クエリを表すクラスです。 各フィールドを単体で指定することができるほか、&演算子、|演算子やImapSearchCriteria.And(), Or()メソッドを使うことでImapSearchCriteria同士を結合し、複雑な検索クエリを組み立てることもできます。

検索クエリのフィールド・メソッド
クラス フィールド/メソッド 検索クエリのマッチ対象 補足
ImapSearchCriteria All すべてのメッセージ。
メッセージのフラグ Answered 返信済みフラグ(\Answered)が設定されているメッセージ。
Unanswered 返信済みフラグ(\Answered)が設定されていないメッセージ。
Deleted 削除予定フラグ(\Deleted)が設定されているメッセージ。
Undeleted 削除予定フラグ(\Deleted)が設定されていないメッセージ。
Draft 下書きフラグ(\Draft)が設定されているメッセージ。
Undraft 下書きフラグ(\Draft)が設定されていないメッセージ。
Flagged フラグ済みフラグ(\Flagged)が設定されているメッセージ。
Unflagged フラグ済みフラグ(\Flagged)が設定されていないメッセージ。
Recent 新着フラグ(\Recent)が設定されているメッセージ。
Seen 既読フラグ(\Seen)が設定されているメッセージ。
Unseen 既読フラグ(\Seen)が設定されていないメッセージ。
Keyword(ImapMessageFlag)
Keyword(string)
指定されたキーワードもしくはフラグが設定されているメッセージ。
Unkeyword(ImapMessageFlag)
Unkeyword(string)
指定されたキーワードもしくはフラグが設定されていないメッセージ。
New 新着フラグ(\Recent)が設定されているが、既読フラグ(\Seen)が設定が設定されていないメッセージ。
ImapSearchCriteria.Recent & ImapSearchCriteria.Unseenと同等。
Old 新着フラグ(\Recent)が設定されていないメッセージ。
!ImapSearchCriteria.Recentと同等。
メッセージのヘッダ・本文 Body(string) メッセージ本文に指定された文字列が含まれているメッセージ。
Text(string) メッセージ本文もしくはヘッダ部分に指定された文字列が含まれているメッセージ。
Header(string, string) 指定されたヘッダフィールドに指定された文字列が含まれているメッセージ。 空の検索文字列を指定した場合は、単にそのヘッダーフィールドが含まれているメッセージ。
Subject(string) Subjectヘッダに指定された文字列が含まれているメッセージ。
From(string) Fromヘッダに指定された文字列が含まれているメッセージ。
To(string) Toヘッダに指定された文字列が含まれているメッセージ。
Cc(string) CCヘッダに指定された文字列が含まれているメッセージ。
Bcc(string) BCCヘッダに指定された文字列が含まれているメッセージ。
メッセージのサイズ Larger(long) サイズが指定されたオクテット数より大きいメッセージ。
Smaller(long) サイズが指定されたオクテット数よりも小さいメッセージ。
メッセージの日付 On(DateTime) 指定された日付にメールボックスに追加されたメッセージ。 日付はINTERNALDATE(ImapMessageInfo.InternalDateの値)で判断されます。
指定したDateTimeの時刻部分は無視されます。
Before(DateTime) 指定された日付以前にメールボックスに追加されたメッセージ。
Since(DateTime) 指定された日付以降にメールボックスに追加されたメッセージ。
Older(TimeSpan)
Older(long)
現在時刻から指定された秒数以前にメールボックスに追加されたメッセージ。 時刻はINTERNALDATE(ImapMessageInfo.InternalDateの値)で判断されます。 時刻の解像度は1秒単位です。
サーバがRFC 5032 - WITHIN Search Extension to the IMAP Protocolをサポートしていない場合は、ImapIncapableExceptionをスローします。
Younger(TimeSpan)
Younger(long)
現在時刻から指定された秒数以内にメールボックスに追加されたメッセージ。
SentOn(DateTime) 指定された日付に送信されたメッセージ。 日付はDateヘッダの値で判断されます。
指定したDateTimeの時刻部分は無視されます。
SentBefore(DateTime) 指定された日付以前に送信されたメッセージ。
SentSince(DateTime) 指定された日付以降に送信されたメッセージ。
メッセージの通番・UID SequenceSet(ImapSequenceSet) 指定された通番を持つメッセージ。 一つ以上の通番・UIDを指定することができます。
Uid(ImapSequenceSet) 指定されたUIDを持つメッセージ。
SequenceOrUidSet(ImapSequenceSet) 指定された通番もしくはUIDを持つメッセージ。 通番とUIDどちらでの検索になるかは指定されるImapSequenceSet.IsUidSetプロパティの値で決まります。
検索クエリの結合 Not()
!演算子
検索クエリに一致しないメッセージ。 演算子とインスタンスメソッドの他、同名の静的メソッドを使うこともできます。
Or(ImapSearchCriteria)
|演算子
二つの検索クエリのどちらかに一致するメッセージ。
And(ImapSearchCriteria)
&演算子
二つの検索クエリの両方に一致するメッセージ。
クラス フィールド/メソッド 検索クエリのマッチ対象 補足

§5.2.3.1.2 メッセージのソート順序(ImapSortCriteria)

ImapSortCriteriaクラスはメッセージのソート順序を表すクラスです。 各フィールドを単体で指定することができるほか、+演算子やImapSortCriteria.Combine()メソッドを使うことでImapSortCriteria同士を結合し、一つ目のソート順が同値だった場合に使用する二つ目以降のソート順序を指定することもできます。

ソート順序のフィールド
クラス フィールド ソート順序 補足
ImapSortCriteria Arrival
ArrivalReverse
メールボックスに追加された日時に従って、昇順あるいは降順でソートします。 日時はINTERNALDATE(ImapMessageInfo.InternalDateの値)で判断されます。
Date
DateReverse
Dateヘッダに設定されている日時に従って、昇順あるいは降順でソートします。
Subject
SubjectReverse
Subjectヘッダの内容に従って、昇順あるいは降順でソートします。
Cc
CcReverse
CCヘッダに設定されている一つ目のアドレスに従って、昇順あるいは降順でソートします。
From
FromReverse
Fromヘッダに設定されている一つ目のアドレスに従って、昇順あるいは降順でソートします。
To
ToReverse
Toヘッダに設定されている一つ目のアドレスに従って、昇順あるいは降順でソートします。
Size
SizeReverse
メッセージのサイズ(オクテット数)に従って、昇順あるいは降順でソートします。
DisplayFrom Fromヘッダのdisplay-nameに従って、昇順でソートします。 サーバがRFC 5957 - Display-Based Address Sorting for the IMAP4 SORT Extensionをサポートしていない場合は、ImapIncapableExceptionをスローします。
DisplayTo Toヘッダのdisplay-nameに従って、昇順でソートします。

§5.2.3.2 メッセージの状態の取得

以下はメッセージの状態を表すプロパティです。

メッセージの状態を表すプロパティ
クラス プロパティ 解説
ImapMessageInfo Uid メッセージに割り当てられているUIDを返します。
Sequence メッセージに割り当てられている現在の通番を返します。 削除されている場合(IsDeletedがtrueの場合)は0を返します。
UidValidity メッセージを取得した時点でのメールボックスのUIDVALIDITY値を返します。 UIDが永続的でない場合(ImapOpenedMailboxInfo.IsUidPersistentがfalseの場合)などは、0を返します。
Url このメッセージを表すIMAP URLを返します。
Mailbox メッセージが属するメールボックスを表すインスタンスを返します。
IsAvailable メッセージが利用可能かどうかを表す値を返します。 インスタンスの取得元となったメールボックスが既に閉じられている場合、メッセージが移動または削除されている場合は、falseを返します。
IsDeleted メッセージが完全に削除されているかどうかを表す値を返します。
動的属性 IsAnswered メッセージが返信済みかどうかを表す値を返します。 \Answeredフラグが設定されている場合trueを返します。
IsDraft メッセージが下書きかどうかを表す値を返します。 \Draftフラグが設定されている場合trueを返します。
IsFlagged メッセージが「フラグ済み」かどうかを表す値を返します。 \Flaggedフラグが設定されている場合trueを返します。
IsMarkedAsDeleted メッセージが削除予定かどうかを表す値を返します。 \Deletedフラグが設定されている場合trueを返します。
IsRecent メッセージが新着かどうかを表す値を返します。 \Recentフラグが設定されている場合trueを返します。
IsSeen メッセージが既読かどうかを表す値を返します。 \Seenフラグが設定されている場合trueを返します。
Flags メッセージに設定されているフラグ・キーワードの一覧を返します。
ModSeq メッセージに設定されているmod-sequence値を返します。
静的属性 MessagePart メッセージに含まれるメッセージパートの操作・情報の取得を行うためのImapMessagePartInfoを返します。
IsMultiPart メッセージがマルチパート形式のメッセージかどうかを表す値を返します。 MessagePart.IsMultPartプロパティと同じ値を返します。
HasEncapsulatedMessage メッセージが他のメッセージを内包している(message/rfc822)かどうかを表す値を返します。 MessagePart.HasEncapsulatedMessageプロパティと同じ値を返します。
MediaType メッセージのメディアタイプを返します。 MessagePart.ContentTypeプロパティと同じ値を返します。
Length メッセージのサイズをバイト単位で返します。
Envelope メッセージのエンベロープを表すImapEnvelopeのインスタンスを返します。
EnvelopeDate メッセージのDateヘッダをパースした値をNullable<DateTimeOffset>で返します。 パースに失敗した場合・Dateヘッダがない場合などはnullを返します。 このプロパティは参照される度にヘッダのパースを行います。
EnvelopeSubject メッセージのSubjectヘッダをデコードした値をstringで返します。 デコードに失敗した場合は、デコードされていない状態の値を返します。 Subjectヘッダがない場合などは、nullを返します。 このプロパティは参照される度にヘッダのデコードを行います。
InternalDate メッセージがメールボックスに追加された(受信もしくはアップロードした)日時をDateTimeOffsetで返します。
クラス プロパティ 解説
動的属性を表すプロパティ
メッセージ取得時にImapMessageFetchAttributeOptionsで動的属性を取得するように指定しなかった場合、プロパティの値を参照する際、コマンドを発行してから結果を返します。
静的属性を表すプロパティ
メッセージ取得時にImapMessageFetchAttributeOptionsで静的属性を取得するように指定しなかった場合、プロパティの値を参照する際、コマンドを発行してから結果を返します。

§5.2.3.3 メッセージ本文の取得

以下はメッセージ本文の取得に関するメソッドです。

メッセージ本文の取得に関するメソッド
クラス メソッド 解説 対応するIMAPコマンド
ImapMessageInfo OpenRead()
OpenReadAsync()
および類似のオーバーロード
メッセージ本文を読み込むためのStreamを取得します。 FETCH
OpenText()
OpenTextAsync()
および類似のオーバーロード
メッセージ本文を読み込むためのStreamReaderを取得します。 FETCH
ReadAllBytes()
ReadAllBytesAsync()
および類似のオーバーロード
メッセージ本文をbyte[]で取得します。 FETCH
ReadAllLines()
ReadAllLinesAsync()
および類似のオーバーロード
メッセージ本文をstring[]で取得します。 FETCH
ReadAllText()
ReadAllTextAsync()
および類似のオーバーロード
メッセージ本文をstringで取得します。 FETCH
ReadLines()
および類似のオーバーロード
メッセージ本文をIEnumerable<string>で取得します。 FETCH
ReadAs<TResult>(Converter<Stream, TResult>)
ReadAsAsync<TResult>(Converter<Stream, TResult>)
および類似のオーバーロード
メッセージ本文を読み込むためのStreamを取得し、指定されたConverter<Stream, TResult>で変換された結果を取得します。 FETCH
ReadAs<T, TResult>
(Func<Stream, T, TResult>, T)
ReadAs<T1, T2, TResult>
(Func<Stream, T1, T2,TResult>, T1, T2)
ReadAs<T1, T2, T3, TResult>
(Func<Stream, T1, T2, T3, TResult>, T1, T2, T3)
ReadAs<T1, T2, T3, T4, TResult>
(Func<Stream, T1, T2, T3, T4, TResult>, T1, T2, T3, T4)
および類似のオーバーロード
メッセージ本文を読み込むためのStreamを取得し、指定されたFunc<Stream, T, TResult>~Func<Stream, T1, T2, T3, T4, TResult>で変換された結果を取得します。 FETCH
ReadAs<TResult>(Converter<StreamReader, TResult>)
ReadAsAsync<TResult>(Converter<StreamReader, TResult>)
および類似のオーバーロード
メッセージ本文を読み込むためのStreamReaderを取得し、指定されたConverter<StreamReader, TResult>で変換された結果を取得します。 FETCH
Save(string)
SaveAsync(string)
および類似のオーバーロード
メッセージ本文を指定されたファイルに保存します。 FETCH
WriteTo(Stream)
WriteToAsync(Stream)
および類似のオーバーロード
メッセージ本文を指定されたStreamに書き込みます。 FETCH
WriteTo(BinaryWriter)
WriteToAsync(BinaryWriter)
および類似のオーバーロード
メッセージ本文を指定されたBinaryWriterに書き込みます。 FETCH
Download()
DownloadAsync()
および類似のオーバーロード
メッセージ本文をメモリ上にダウンロードしたStreamを取得します。 FETCH
クラス メソッド 解説 対応するIMAPコマンド
すべてのメソッド
これらすべてのメソッドは、取得したメッセージ本文をキャッシュしません。 メソッド呼び出しの度にメッセージ本文のダウンロードを行います。 同じメッセージを何度も処理する必要がある場合は、Download()メソッドでメモリ上にダウンロードしたStreamを取得するか、Save()メソッドでファイルに保存するなどしてください。
OpenRead()/OpenText()とDownload()
OpenRead()/OpenText()メソッドはメッセージ本文を適宜ダウンロードしながら読み込むためのStream/StreamReaderを返します。 メソッド呼び出しが完了した時点ではメッセージ本文の先頭一部のみがダウンロードされていて、Readメソッドなどで読み込むことにより続きの部分が適宜ダウンロードされます。 OpenRead()/OpenText()が返すStream/StreamReaderはシーク操作および書き込み操作をサポートしません。
一方Download()メソッドはメッセージ全文をメモリ上にダウンロードしたStreamを返します。 メソッド呼び出しが完了した時点でメッセージ全文がダウンロードされています。 Download()メソッドが返すStreamはシーク操作および書き込み操作をサポートします。
OpenRead()/OpenText()が返すStream/StreamReaderのクローズ
OpenRead()/OpenText()メソッドによって取得したStream/StreamReaderに対してClose()メソッドを呼び出さない限り、ImapClient.IsBusyはtrueのままとなり、他の操作を開始しようとしてもInvalidOperationExceptionをスローします。 OpenRead()/OpenText()メソッドを使用する場合は、次の操作を開始するまでに取得したStream/StreamReaderのClose()メソッドを呼び出すようにしてください。 また、読み込み処理を行いながら他の操作を行いたい場合は、Download()メソッドで取得したStreamを使用するなどしてください。
ReadLines()が返すIEnumerable<string>
ReadLines()は遅延実行するIEnumerable<string>を返します。 列挙操作が完了するまではImapClient.IsBusyはtrueのままとなり、他の操作を開始しようとしてもInvalidOperationExceptionをスローします。 列挙中に他の操作を行いたい場合は、遅延実行を行わないReadAllLines()メソッドを使用してください。
非同期バージョンのメソッド
OpenReadAsync()など名前がAsyncで終わるメソッドは非同期バージョンのメソッドです。 非同期的に動作する以外は同期バージョンと同じです。 非同期バージョンのメソッドを使う際の注意点は§.非同期操作を参照してください。
メッセージ本文の取得オプション(ImapMessageFetchBodyOptions)
これらのメソッドには、ImapMessageFetchBodyOptionsを引数にとるバージョンのオーバーロードを用意してあります。 ImapMessageFetchBodyOptionsを指定することで、メッセージ本文取得時の動作を指定することができます。 以下の値を組み合わせて指定できます。
SetSeen
メッセージ本文の取得と同時に、メッセージを既読にします。 このオプションを指定しない場合は、メッセージの既読/未読の状態をそのままにします。
OmitHeader
メッセージ本文のうち、ヘッダ部分を省略したボディ部分のみを取得します。
DecodeContent
ImapMessageInfoクラスではサポートされません。 指定した場合はNotSupportedExceptionをスローします。
Default
オプションのデフォルト値です。 上記オプションのいずれも指定しない場合と同値です。

§5.2.3.4 メッセージパートの取得・検索

以下はメッセージ内のメッセージパートの取得を行うプロパティとメソッドです。

メッセージパートの取得を行うプロパティ
クラス プロパティ 解説
ImapMessageInfo MessagePart メッセージ全体のメッセージパート情報を表すImapMessagePartInfoを返します。
メッセージパートに関するメソッド
クラス プロパティ 解説
ImapMessageInfo GetMessagePart(string)
GetMessagePart(params int[])
GetMessagePart(IEnumerable<int>)
指定されたメッセージパートを表すImapMessagePartInfoを取得します。 空の文字列・空の配列・nullを指定した場合はImapMessageInfo.MessagePartと同じ値を返します。 該当するメッセージパートが無い場合はnullを返します。
FindAllMessageParts(Predicate<ImapMessagePartInfo>) 指定された条件に一致するすべてのメッセージパートを格納したList<ImapMessagePartInfo>を取得します。 該当するメッセージパートが無い場合は空のリストを返します。
FindMessagePart(Predicate<ImapMessagePartInfo>) 指定された条件に一致する最初のメッセージパートを取得します。 該当するメッセージパートが無い場合はnullを返します。
FindAllMessageParts(Smdn.MimeType) Content-Typeが指定されたMimeTypeと一致するすべてのメッセージパートを格納したList<ImapMessagePartInfo>を取得します。 該当するメッセージパートが無い場合は空のリストを返します。
FindMessagePart(Smdn.MimeType) Content-Typeが指定されたMimeTypeと一致する最初のメッセージパートを取得します。 該当するメッセージパートが無い場合はnullを返します。
上記すべてのプロパティ・メソッド
ImapMessageFetchAttributeOptions.StaticAttributesを指定してメッセージの動的属性をしていない場合は、コマンドを発行してから結果を返します。
ImapMessageInfo.GetMessagePart()
文字列"1.2.3"の指定と数値{1,2,3}の指定は等価です。 これらはどちらも"1.2.3"で表されるメッセージパートを表します。
ImapMessageInfo.FindMessagePart()およびImapMessageInfo.FindAllMessageParts()
引数としてImapMessagePartInfo.Predicatesクラスに定義済みのメソッドを指定することができます。

§5.2.3.5 メッセージヘッダの取得

以下はメッセージヘッダ(From, Subject, Content-Type等)の取得に関するメソッドです。

メッセージヘッダの取得に関するメソッド
クラス メソッド 解説 対応するIMAPコマンド
ImapMessageInfo GetHeader()
GetHeaderAsync()
およびオーバーロード
メッセージのヘッダ部分のみを取得し、ヘッダ名と値に分離したKeyValuePairの配列で返します。 オプションで取得するヘッダや、ヘッダの値をデコードするかどうかを指定できます。 FETCH
GetRawHeader()
GetRawHeaderAsync()
およびオーバーロード
メッセージのヘッダ部分のみを取得し、単一の文字列として返します。 オプションで取得するヘッダを指定できます。

§5.2.3.6 メッセージの操作

以下はメールボックスにあるメッセージの移動・コピー・削除・フラグの変更等の操作に関するメソッドです。

メールボックスにあるメッセージの移動・コピー等の操作に関するメソッド
クラス メソッド 解説 対応するIMAPコマンド
同一セッションの場合 異なるセッションの場合
ImapMessageInfo および ImapMessageInfoList (ImapMessageInfoBaseから継承) CopyTo(ImapMailboxInfo) 1つまたは複数のメッセージを、指定したメールボックスにコピーします。 COPY FETCH+APPEND
CopyToAsync(ImapMailboxInfo)
CopyTo(string)
CopyTo(string, bool)
1つまたは複数のメッセージを、指定した名前のメールボックスにコピーします。 オプションで、メールボックスが存在しない場合に作成するかどうかを指定できます。 COPY -
CopyToAsync(string)
CopyToAsync(string, bool)
MoveTo(ImapMailboxInfo) 1つまたは複数のメッセージを、指定したメールボックスに移動します。 COPY+STORE/MOVE FETCH+APPEND+STORE
MoveToAsync(ImapMailboxInfo)
MoveTo(string)
MoveTo(string, bool)
1つまたは複数のメッセージを、指定した名前のメールボックスに移動します。 オプションで、メールボックスが存在しない場合に作成するかどうかを指定できます。 COPY+STORE/MOVE -
MoveToAsync(string)
MoveToAsync(string, bool)
ImapOpenedMailboxInfo CopyMessagesTo(ImapMailboxInfo) メールボックスにあるすべてのメッセージを、指定したメールボックスにコピーします。 COPY FETCH+APPEND
CopyMessagesToAsync(ImapMailboxInfo)
CopyMessagesTo(ImapMailboxInfo, long, params long[]) メールボックスにあるメッセージのうち、指定したUIDを持つ1つ以上のメッセージを指定したメールボックスにコピーします。 COPY FETCH+APPEND
CopyMessagesToAsync(ImapMailboxInfo, long, params long[])
CopyMessagesTo(ImapSearchCriteria, ImapMailboxInfo)
CopyMessagesTo(ImapSearchCriteria, Encoding, ImapMailboxInfo)
メールボックスにあるメッセージのうち、指定した検索条件にマッチするメッセージを指定したメールボックスにコピーします。 SEARCH+COPY SEARCH+FETCH+APPEND
CopyMessagesToAsync(ImapSearchCriteria, ImapMailboxInfo)
CopyMessagesToAsync(ImapSearchCriteria, Encoding, ImapMailboxInfo)
MoveMessagesTo(ImapMailboxInfo) メールボックスにあるすべてのメッセージを、指定したメールボックスに移動します。 COPY+STORE / MOVE FETCH+APPEND+STORE
MoveMessagesToAsync(ImapMailboxInfo)
MoveMessagesTo(ImapMailboxInfo, long, params long[]) メールボックスにあるメッセージのうち、指定したUIDを持つ1つ以上のメッセージを指定したメールボックスに移動します。 COPY+STORE / MOVE FETCH+APPEND+STORE
MoveMessagesToAsync(ImapMailboxInfo, long, params long[])
MoveMessagesTo(ImapSearchCriteria, ImapMailboxInfo)
MoveMessagesTo(ImapSearchCriteria, Encoding, ImapMailboxInfo)
メールボックスにあるメッセージのうち、指定した検索条件にマッチするメッセージを指定したメールボックスに移動します。 SEARCH+COPY+STORE / SEARCH+MOVE SEARCH+FETCH+APPEND+STORE
MoveMessagesToAsync(ImapSearchCriteria, ImapMailboxInfo)
MoveMessagesToAsync(ImapSearchCriteria, Encoding, ImapMailboxInfo)
クラス メソッド 解説 同一セッションの場合 異なるセッションの場合
対応するIMAPコマンド
メッセージの移動
IMAPにはメッセージの移動を行うコマンドが存在しないため、代わりにコピーと削除フラグの設定を行うよう実装しています。 そのため、移動を行っても移動元のメッセージに削除フラグが設定されるのみですぐには削除されません。
これらのメッセージはImapOpenedMailboxInfo.Close()メソッドやImapClient.Logout()メソッドを呼び出した時点で完全に削除されますが、すぐに削除したい場合はDelete()メソッドやImapOpenedMailboxInfo.Expunge()メソッドを呼び出すようにしてください。
なお、サーバーがRFC 6851 - Internet Message Access Protocol (IMAP) - MOVE Extensionをサポートしている場合は、同一セッションでのメッセージの移動にMOVEコマンドを使用します。 この場合、移動元のメッセージは即座に削除されます。
コピー・移動先のメールボックス
コピー・移動先のメールボックスをImapMailboxInfoで指定する場合、コピー・移動元とコピー・移動先のImapMailboxInfoによって動作が異なります。
ImapMailboxInfoが同一セッションのものの場合は、サーバ上でコピー・移動が行われます。
ImapMailboxInfoが異なるセッションのものの場合は、一旦ダウンロードしてから追加先のメールボックスにアップロードします。
(現在の実装では、同一アカウントのメールボックスを表すImapMailboxInfoの場合でも、異なるImapClientから取得した場合は異なるセッションとみなされます。)
また、選択できないメールボックス(IsUnselectableがtrue)の場合や、既に削除されているメールボックス(Existがfalse)の場合は、ImapProtocolViolationExceptionをスローします。
メールボックスが存在しない場合(サーバがTRYCREATEレスポンスコードを返した場合)は、ImapMailboxNotFoundExceptionをスローします。
メールボックスにあるメッセージの削除・フラグの変更等の操作に関するメソッド
クラス メソッド 解説 対応するIMAPコマンド
ImapMessageInfo および ImapMessageInfoList (ImapMessageInfoBaseから継承) AddFlags(IImapMessageFlagSet)
AddFlags(ImapMessageFlag, params ImapMessageFlag[])
AddKeywords(string, params string[])
1つまたは複数のメッセージに対して、指定したフラグ・キーワードを追加します。 STORE
AddFlagsAsync(IImapMessageFlagSet)
AddFlagsAsync(ImapMessageFlag, params ImapMessageFlag[])
AddKeywordsAsync(string, params string[])
RemoveFlags(IImapMessageFlagSet)
RemoveFlags(ImapMessageFlag, params ImapMessageFlag[])
RemoveKeywords(string, params string[])
1つまたは複数のメッセージから、指定したフラグ・キーワードを削除します。
RemoveFlagsAsync(IImapMessageFlagSet)
RemoveFlagsAsync(ImapMessageFlag, params ImapMessageFlag[])
RemoveKeywordsAsync(string, params string[])}}
SetFlags(IImapMessageFlagSet)
SetFlags(ImapMessageFlag, params ImapMessageFlag[])
SetKeywords(string, params string[])
1つまたは複数のメッセージに対して、既に設定されているフラグ・キーワードを置き換えて指定した値に設定します。
SetFlagsAsync(IImapMessageFlagSet)
SetFlagsAsync(ImapMessageFlag, params ImapMessageFlag[])
SetKeywordsAsync(string, params string[])
Store(ImapStoreDataItem) 1つまたは複数のメッセージに対して、指定したフラグ・キーワードの追加/削除/設定を行います。
StoreAsync(ImapStoreDataItem)
MarkAsDeleted() 1つまたは複数のメッセージに削除フラグ(\Deleted)を設定します。
MarkAsDeletedAsync()
MarkAsSeen() 1つまたは複数のメッセージを既読フラグ(\Seen)を設定します。
MarkAsSeenAsync()
Delete() 1つまたは複数のメッセージを完全に削除します。
サーバがUID EXPUNGEをサポートしていない場合、既に削除フラグが設定されている他のメッセージも同時に削除されます
STORE+EXPUNGE/UID EXPUNGE
DeleteAsync()
ImapMessageInfo Refresh() メッセージの動的属性を更新します。 FETCH
RefreshAsync()
ToggleFlags(IImapMessageFlagSet)
ToggleFlags(ImapMessageFlag, params ImapMessageFlag[])
ToggleKeywords(string, params string[])
メッセージに設定されているフラグ・キーワードを反転します。
例として、メッセージに削除フラグが設定されている場合はフラグを削除し、設定されていない場合は追加します。
STORE
ToggleFlagsAsync(IImapMessageFlagSet)
ToggleFlagsAsync(ImapMessageFlag, params ImapMessageFlag[])
ToggleKeywordsAsync(string, params string[])
クラス メソッド 解説 対応するIMAPコマンド
ImapMessageFlag
以下のフラグを指定することができます。 これ以外のImapMessageFlagを指定した場合は無視されます。
ImapMessageFlag.Seen
メッセージが既読であることを表すフラグ(\Seen)
ImapMessageFlag.Answered
メッセージが返信済みであることを表すフラグ(\Answered)
ImapMessageFlag.Flagged
メッセージに何らかのマーク付け・フラグ付けがされていることを表すフラグ(\Flagged)
ImapMessageFlag.Deleted
メッセージが削除予定であることを表すフラグ(\Deleted)
ImapMessageFlag.Draft
メッセージが下書きであることを表すフラグ(\Draft)
キーワード
ImapOpenedMailboxInfo.IsAllowedToCreateKeywordstrueのメールボックスでは、メッセージにユーザー定義のキーワードを設定することができます。 それ以外のメールボックスでは、キーワードの設定に成功しても永続的には保持されません。
キーワードには\から始まる文字列を指定することはできません。 そのほか、制御文字や非ASCII文字、一部の記号など、使用できない文字をキーワードに含んでいる場合は例外ArgumentExceptionをスローします。
IImapMessageFlagSet
IImapMessageFlagSetを実装するImapMessageFlagSetなどのクラスを指定することができます。 ImapMessageFlagSetクラスは複数のフラグ・キーワードをまとめて扱いたい場合に使うことができます。

§5.2.3.7 メッセージのアップロード(メールボックスへの追加)

以下はメールボックスにメッセージをアップロード(追加)するためのメソッドです。

メールボックスにメッセージを追加するためのメソッド
クラス メソッド 解説 対応するIMAPコマンド
ImapMailboxInfo AppendMessage(Stream)
AppendMessage(Stream, DateTimeOffset)
AppendMessage(Stream, IImapMessageFlagSet)
AppendMessage(Stream, DateTimeOffset, IImapMessageFlagSet)
指定したStreamの内容をメールボックスにアップロードします。 オプションでメッセージに設定するINTERNALDATE, フラグ・キーワードを指定できます。 APPEND
AppendMessageAsync(Stream)
AppendMessageAsync(Stream, DateTimeOffset)
AppendMessageAsync(Stream, IImapMessageFlagSet)
AppendMessageAsync(Stream, DateTimeOffset, IImapMessageFlagSet)
AppendMessage(IImapAppendMessage message) 指定したIImapAppendMessageで表されるメッセージをメールボックスにアップロードします。
AppendMessageAsync(IImapAppendMessage message)
AppendMessages(IEnumerable<IImapAppendMessage>) 指定したIImapAppendMessageで表される複数のメッセージをメールボックスにアップロードします。 APPENDもしくはMULTIAPPEND
AppendMessagesAsync(IEnumerable<IImapAppendMessage>)
AppendMessage(Action<Stream>)
AppendMessage(DateTimeOffset, Action<Stream>)
AppendMessage(IImapMessageFlagSet, Action<Stream>)
AppendMessage(DateTimeOffset, IImapMessageFlagSet flags, Action<Stream>)
メッセージを書き込むためのStreamを取得し、書き込んだ内容をメールボックスにアップロードします。 オプションでメッセージに設定するINTERNALDATE, フラグ・キーワードを指定できます。 APPEND
AppendMessageAsync(Action<Stream>)
AppendMessageAsync(DateTimeOffset, Action<Stream>)
AppendMessageAsync(IImapMessageFlagSet, Action<Stream>)
AppendMessageAsync(DateTimeOffset, IImapMessageFlagSet flags, Action<Stream>)
AppendMessage(long, Action<Stream>)
AppendMessage(long, DateTimeOffset, Action<Stream>)
AppendMessage(long, IImapMessageFlagSet, Action<Stream>)
AppendMessage(long, DateTimeOffset, IImapMessageFlagSet, Action<Stream>)
指定したサイズのメッセージを書き込むためのStreamを取得し、書き込んだ内容をメールボックスにアップロードします。 オプションでメッセージに設定するINTERNALDATE, フラグ・キーワードを指定できます。
AppendMessageAsync(long, Action<Stream>)
AppendMessageAsync(long, DateTimeOffset, Action<Stream>)
AppendMessageAsync(long, IImapMessageFlagSet, Action<Stream>)
AppendMessageAsync(long, DateTimeOffset, IImapMessageFlagSet, Action<Stream>)
AppendMessage(ImapMessageInfo) 指定したImapMessageInfoの内容をメールボックスに追加します。

ImapMessageInfoが同一セッションのものの場合は、サーバ上でコピーが行われます。
ImapMessageInfoが異なるセッションのものの場合は、一旦ダウンロードしてから追加先のメールボックスにアップロードします。
COPY (同一セッションの場合)
APPEND (異なるセッションの場合)
AppendMessageAsync(ImapMessageInfo)
クラス メソッド 解説 対応するIMAPコマンド
追加先のメールボックス
選択できないメールボックス(IsUnselectableがtrue)の場合や、既に削除されているメールボックス(Existがfalse)の場合は、ImapProtocolViolationExceptionをスローします。
メールボックスが存在しない場合(サーバがTRYCREATEレスポンスコードを返した場合)は、ImapMailboxNotFoundExceptionをスローします。

§5.2.4 メッセージパートに対する操作

以下はメッセージ内の特定のメッセージパートに対する操作を行うメソッドです。

§5.2.4.1 メッセージパート情報の取得

以下はメッセージパート情報の取得を行うプロパティとメソッドです。

メッセージパート情報の取得を行うプロパティ
クラス プロパティ 解説
ImapMessagePartInfo Message このメッセージパートの取得元となるImapMessageInfoを返します。
Url このメッセージパートを表すIMAP URLを返します。
Specifier メッセージ内においてこのメッセージパートを表す文字列形式の指定子を返します。
メッセージパートの構造 ParentPart このメッセージパートを内包しているメッセージパートを返します。 最上位のメッセージパートの場合はnullを返します。
IsRootPart このメッセージパートが最上位のメッセージパートかどうかを表す値を返します。
IsMultiPart メッセージパートがマルチパート形式かどうかを表す値を返します。
NestedMessageParts メッセージパートがマルチパート形式の場合、内包している子メッセージパートを格納した読み取り専用のリストを返します。 マルチパート形式以外の場合は空のリストを返します。
HasEncapsulatedMessage メッセージパートがカプセル化されたメッセージを内包している(message/rfc822)かどうかを表す値を返します。
IsEncapsulatedMessage メッセージパートがカプセル化されたメッセージ(message/rfc822に内包されたメッセージ)かどうかを表す値を返します。
EncapsulatedMessagePart カプセル化されたメッセージを表すImapMessagePartInfoを返します。 カプセル化されたメッセージを持たない場合はnullを返します。
EncapsulatedMessageEnvelope カプセル化されたメッセージのエンベロープを表すImapEnvelopeを返します。 カプセル化されたメッセージを持たない場合はnullを返します。
NestLevel メッセージパートの入れ子深度を表す値を返します。 最上位のメッセージパートの場合は0、マルチパートの子メッセージパートやカプセル化されたメッセージパートの場合は、親パートのNestLevelに+1した値を返します。
Length メッセージパートのサイズをバイト単位で返します。 メッセージパートがマルチパート形式の場合は0を返します。
LineCount メッセージパート本文、またはカプセル化されたメッセージの行数を返します。 非テキストパートやマルチパート形式の場合は0を返します。
メッセージパートのヘッダ ContentType メッセージパートのContent-Typeヘッダの値を返します。
ContentTypeParameters メッセージパートのContent-Typeヘッダのパラメータを格納したディクショナリを返します。 パラメータが無い場合・取得できない場合は空のディクショナリを返します。
ContentTypeCharset メッセージパートのContent-Typeヘッダのcharsetパラメータの値を返します。 charsetパラメータが無い場合はnullを返します。
ContentTransferEncoding メッセージパートのContent-Transfer-Encodingヘッダの値を返します。 ヘッダが無い場合・取得できない場合はnullを返します。
ContentTransferEncodingMethod メッセージパートのContent-Transfer-Encodingヘッダの値を列挙体の値で返します。 ヘッダが無い場合・取得できない場合はContentTransferEncodingMethod.Unknownを返します。
ContentDisposition メッセージパートのContent-Dispositionヘッダの値を返します。 ヘッダが無い場合・取得できない場合はnullを返します。
ContentDispositionParameters メッセージパートのContent-Dispositionヘッダのパラメータを格納したディクショナリを返します。 パラメータが無い場合・取得できない場合は空のディクショナリを返します。
IsDispositionAttachment メッセージパートのContent-Dispositionヘッダの値がattachmentの場合にtrueを返します。 それ以外の値の場合・ヘッダが無い場合・取得できない場合はfalseを返します。
IsDispositionInline メッセージパートのContent-Dispositionヘッダの値がinlineの場合にtrueを返します。 それ以外の値の場合・ヘッダが無い場合・取得できない場合はfalseを返します。
ContentLanguages メッセージパートのContent-Languageヘッダの値を格納したリストを返します。 ヘッダが無い場合・取得できない場合は空のリストを返します。
ContentLocation メッセージパートのContent-Locationヘッダの値を返します。 ヘッダが無い場合・取得できない場合はnullを返します。
ContentID メッセージパートのContent-IDヘッダの値を返します。 ヘッダが無い場合・取得できない場合はnullを返します。
ContentDescription メッセージパートのContent-Descriptionヘッダの値を返します。 ヘッダが無い場合・取得できない場合はnullを返します。
ContentMD5 メッセージパートのContent-MD5ヘッダの値を返します。 ヘッダが無い場合・取得できない場合はnullを返します。
メッセージパート情報の取得を行うメソッド
クラス メソッド 解説
ImapMessagePartInfo GetContentFileName()
GetContentFileName(EncodingSelectionCallback, bool)
メッセージパートに与えられているファイル名を取得しデコードした状態で返します。 このメソッドは、Content-Dispositionヘッダのfilenameパラメータ、Content-Typeヘッダのnameパラメータを順に参照し、値が設定されている場合はその値をデコードした結果を返します。 ヘッダが無い場合・パラメータが指定されていない場合・取得できない場合はnullを返します。

§5.2.4.2 メッセージパート構造の走査・メッセージパートの取得

以下はメッセージパート内の構造の走査や入れ子になっているメッセージパートを取得するメソッドです。

メッセージパート構造の走査・取得に関するメソッド
クラス メソッド 解説
ImapMessagePartInfo Traverse(Action<ImapMessagePartInfo>)
Traverse(bool, Action<ImapMessagePartInfo>)
メッセージパート内の構造を走査し、各メッセージパートに対して指定されたコールバックメソッドを呼び出します。
オプションでメソッドを呼び出したImapMessagePartInfo自身も結果に含めるかどうかを指定できます。
Traverse(Action<ImapMessagePartInfo>, Action<ImapMessagePartInfo>)
Traverse(bool, Action<ImapMessagePartInfo>, Action<ImapMessagePartInfo>)
メッセージパート内の構造を走査し、各メッセージパートに対して、子構造内に入る際と出る際それぞれのタイミングで指定されたコールバックメソッドを呼び出します。
オプションでメソッドを呼び出したImapMessagePartInfo自身も結果に含めるかどうかを指定できます。
Flatten()
Flatten(bool)
メッセージパート内の構造を走査し、すべてのメッセージパートを格納したList<ImapMessagePartInfo>を取得します。
オプションでメソッドを呼び出したImapMessagePartInfo自身も結果に含めるかどうかを指定できます。
FindAll(Predicate<ImapMessagePartInfo> match) メッセージパート内の構造を走査し、指定された条件に一致するすべてのメッセージパートを格納したList<ImapMessagePartInfo>を取得します。 該当するメッセージパートが無い場合は空のリストを返します。
Find(Predicate<ImapMessagePartInfo> match) メッセージパート内の構造を走査し、指定された条件に一致する最初のメッセージパートを取得します。 該当するメッセージパートが無い場合はnullを返します。

Findメソッド・FindAllメソッドでは、以下に用意している定義済みのメソッドを引数として使用することができます。

メッセージパート構造の検索条件
メソッド 解説
ImapMessagePartInfo.Predicates.IsMultiPart マルチパート形式のメッセージパートの場合にtrueを返します
ImapMessagePartInfo.Predicates.HasEncapsulatedMessagePart カプセル化されたメッセージを内包する場合(Content-Typeがmessage/rfc822の場合)にtrueを返します
ImapMessagePartInfo.Predicates.IsEncapsulatedMessagePart カプセル化されたメッセージのパートの場合(Content-Typeがmessage/rfc822のメッセージパートに格納されているメッセージパートの場合)にtrueを返します
ImapMessagePartInfo.Predicates.IsTextPart Content-Typeがtext/*の場合にtrueを返します
ImapMessagePartInfo.Predicates.IsPlainTextPart Content-Typeがtext/plainの場合にtrueを返します
ImapMessagePartInfo.Predicates.IsImagePart Content-Typeがimage/*の場合にtrueを返します
ImapMessagePartInfo.Predicates.IsInlinePart Content-Dispositionがinlineの場合にtrueを返します
ImapMessagePartInfo.Predicates.IsAttachmentPart Content-Dispositionがattachmentの場合にtrueを返します
ImapMessagePartInfo.Predicates.SeemsToBeAttachmentPart Content-Dispositionがattachmentの場合、もしくは添付ファイルと推測されるメッセージパートの場合にtrueを返します

§5.2.4.3 メッセージパートの内容の取得

以下はメッセージパートの内容の取得に関するメソッドです。

メッセージパートの内容の取得に関するメソッド
クラス メソッド 解説 対応するIMAPコマンド
ImapMessagePartInfo OpenRead()
OpenReadAsync()
および類似のオーバーロード
メッセージ本文を読み込むためのStreamを取得します。 FETCH
OpenText()
OpenTextAsync()
および類似のオーバーロード
メッセージ本文を読み込むためのStreamReaderを取得します。 FETCH
ReadAllBytes()
ReadAllBytesAsync()
および類似のオーバーロード
メッセージ本文をbyte[]で取得します。 FETCH
ReadAllLines()
ReadAllLinesAsync()
および類似のオーバーロード
メッセージ本文をstring[]で取得します。 FETCH
ReadAllText()
ReadAllTextAsync()
および類似のオーバーロード
メッセージ本文をstringで取得します。 FETCH
ReadLines()
および類似のオーバーロード
メッセージ本文をIEnumerable<string>で取得します。 FETCH
ReadAs<TResult>(Converter<Stream, TResult>)
ReadAsAsync<TResult>(Converter<Stream, TResult>)
および類似のオーバーロード
メッセージ本文を読み込むためのStreamを取得し、指定されたConverter<Stream, TResult>で変換された結果を取得します。 FETCH
ReadAs<TResult>(Converter<StreamReader, TResult>)
ReadAsAsync<TResult>(Converter<StreamReader, TResult>)
および類似のオーバーロード
メッセージ本文を読み込むためのStreamReaderを取得し、指定されたConverter<StreamReader, TResult>で変換された結果を取得します。 FETCH
Save(string)
SaveAsync(string)
および類似のオーバーロード
メッセージ本文を指定されたファイルに保存します。 FETCH
WriteTo(Stream)
WriteToAsync(Stream)
および類似のオーバーロード
メッセージ本文を指定されたStreamに書き込みます。 FETCH
WriteTo(BinaryWriter)
WriteToAsync(BinaryWriter)
および類似のオーバーロード
メッセージ本文を指定されたBinaryWriterに書き込みます。 FETCH
Download()
DownloadAsync()
および類似のオーバーロード
メッセージ本文をメモリ上にダウンロードしたStreamを取得します。 FETCH
クラス メソッド 解説 対応するIMAPコマンド
すべてのメソッド
以下に記述する事項を除き、基本的な動作・使用上の注意点はImapMessageInfoクラスの同名メソッドと同様です。
特定のメッセージパート内容の取得
これらのメソッドでは、メッセージパートの内容のうちヘッダ部分を除いたボディ部分のみが取得されます。 ヘッダ部分が必要となる場合はImapMessagePartInfo.GetHeader()などのメソッドを使用してください。
メッセージ全体の内容の取得
ImapMessageInfo.MessagePartプロパティ、またはIsRootPartがtrueのImapMessagePartInfoからこれらのメソッドを呼び出した場合はヘッダ部分を含む内容が取得されます。 ヘッダ部分を除いた内容を取得したい場合はImapMessageFetchBodyOptions.OmitHeaderを指定してください。
メッセージパート内容の取得オプション(ImapMessageFetchBodyOptions)
これらのメソッドには、ImapMessageFetchBodyOptionsを引数にとるバージョンのオーバーロードを用意してあります。 ImapMessageFetchBodyOptionsを指定することで、メッセージ本文取得時の動作を指定することができます。 以下の値を組み合わせて指定できます。
SetSeen
メッセージパート内容の取得と同時に、メッセージ自体を既読にします。 このオプションを指定しない場合は、メッセージの既読/未読の状態をそのままにします。
OmitHeader
メッセージ本文のうち、ヘッダ部分を省略したボディ部分のみを取得します。 このオプションはImapMessageInfo.MessagePartプロパティ、またはIsRootPartがtrueのImapMessagePartInfoからメソッドを呼び出す場合のみ有効です。 それ以外の場合は指定しても取得される内容は変わりません。
DecodeContent
メッセージパートの転送エンコーディング(ContentTransferEncodingプロパティ)に従ってメッセージパートの内容をデコードします。 結果を文字列として取得するメソッドではメッセージパートの文字コード(ContentTypeCharsetプロパティ)に従ったデコードも行います。 このオプションを指定しない場合は、メッセージパートの内容をデコードせずに取得します。
サポートしているエンコーディング
7bit, 8bit, binary, base64, quoted-printable, uuencode(ただし、ファイルが複数格納されている場合は先頭のファイルのみ取得可能)
サポートされる文字コード
utf-8, shift_jis, iso-2022-jpほか、ランタイムのEncodingクラスがサポートする文字コード
Default
オプションのデフォルト値です。 上記オプションのいずれも指定しない場合と同値です。
文字コードのフォールバック
EncodingSelectionCallbackを指定できるオーバーロードでは、サポートしていない文字コードの場合にコールバックメソッドを呼び出し、代替となる文字コードを選択することができます。 文字コードのフォールバック用コールバックメソッドはImapMessageFetchBodyOptions.DecodeContentを指定した場合のみ使用されます。

§5.2.4.4 メッセージパートのヘッダの取得

以下はメッセージパートのヘッダ(From, Subject, Content-Type等)の取得に関するメソッドです。

メッセージパートのヘッダの取得に関するメソッド
クラス メソッド 解説 対応するIMAPコマンド
ImapMessagePartInfo GetHeader()
GetHeaderAsync()
およびオーバーロード
メッセージパートのヘッダ部分のみを取得し、ヘッダ名と値に分離したKeyValuePairの配列で返します。 オプションでヘッダの値をデコードするかどうかを指定できます。 FETCH
GetRawHeader()
GetRawHeaderAsync()
およびオーバーロード
メッセージパートのヘッダ部分のみを取得し、単一の文字列として返します。

§5.2.5 その他のIMAPの操作

§5.2.5.1 サーバ情報の取得

以下はサーバーがサポートしている機能などの情報を取得するためのプロパティです。

サーバー情報を取得するためのプロパティ
クラス プロパティ 解説
ImapClient ServerCapabilities サーバーがサポートする機能の一覧(CAPABILITYコマンドの結果)を取得します。
DisabledCapabilities 無効にする機能の一覧を取得します。 このコレクションを変更することにより、仮にサーバーがサポートしている場合でも使用しない機能を指定することができます。
ServerID サーバーがサポートしている場合、サーバーのID(IDコマンドの結果)を返します。 サポートしていない場合は、空のインスタンスを返します。
ServerNamespace サーバーがサポートしている場合、サーバーの名前空間(NAMESPACEコマンドの結果)を返します。 サポートしていない場合は、空のインスタンスを返します。
クラス プロパティ 解説
ServerCapabilities, ServerID, ServerNamespace
切断されている状態で取得しようとした場合(ImapClient.IsConnectedがfalseの場合)、例外をスローします。
DisabledCapabilities
このコレクションを変更することにより、サーバーがサポートしている拡張機能を使用せず、標準機能のみで処理を行うようにすることができます。 例えばDisabledCapabilities.Add(ImapCapability.Idle)のようにすれば、サーバーがIDLEをサポートしている場合でもIDLEを使わないようにすることができます。

§5.2.5.2 サーバーからの通知

以下はサーバーからの通知をハンドリングするためのイベントと関連するプロパティです。

サーバーからの通知に関するイベント
クラス イベント 解説
ImapClient ExistMessageCountChanged 現在選択しているメールボックスにあるすべてのメッセージの数が変わった場合に発生します。
イベント発生前後のメッセージ数と、現在選択しているメールボックスのImapOpenedMailboxInfoを含むImapMailboxSizeChangedEventArgsが送られます。
RecentMessageCountChanged 現在選択しているメールボックスにある新着メッセージの数が変わった場合に発生します。
イベント発生前後のメッセージ数と、現在選択しているメールボックスのImapOpenedMailboxInfoを含むImapMailboxSizeChangedEventArgsが送られます。
MessageDeleted 現在選択しているメールボックスにあるメッセージが削除(移動を含む)された場合に発生します。
削除されたメッセージのImapMessageInfo[]を含むImapMessageStatusChangedEventArgsが送られます。
MessageStatusChanged 現在選択しているメールボックスにあるメッセージの動的属性が変更された場合に発生します。
属性が変更されたメッセージのImapMessageInfo[]を含むImapMessageStatusChangedEventArgsが送られます。
AlertReceived サーバーからのアラートを受信した場合に発生します。
受信したアラートを含むImapAlertReceivedEventArgsが送られます。
クラス イベント 解説
サーバーからの通知に関するイベント引数とプロパティ
クラス プロパティ 解説
ImapMailboxSizeChangedEventArgs Mailbox 新着メッセージ数や総メッセージ数などのサイズが変化したメールボックスのインスタンス(ImapOpenedMailboxInfo)を返します。
CurrentCount 変化したサイズの現在の値を返します。
PrevCount 変化する前のサイズの値を返します。
ImapMessageStatusChangedEventArgs Messages 削除あるいは動的属性の変更など、状態が変更されたメッセージのインスタンス(ImapMessageInfo[])を返します。
ImapAlertReceivedEventArgs Alert サーバーから受信したアラートを文字列として返します。
Condition アラートとして受信したレスポンスの状態(OK, NG, BYE等)を返します。
サーバーからの通知に関するプロパティ
クラス プロパティ 解説
ImapClient SynchronizingObject イベントハンドラを呼び出す際に使用する同期オブジェクト(ISynchronizeInvoke)を取得・設定します。 非同期メソッドを呼び出した場合や別スレッドで操作を行った際に発生したイベントを、UIスレッドでハンドリングさせたい場合などに使用します。
クラス イベント 解説
イベントのsender
すべてのイベントについて、引数senderにはイベントの発生元となるImapClientのインスタンスが渡されます。
IDLE中のイベントと追加の操作
IDLE中にサーバー側から通知が行われた場合も、上記のイベントが発生します。 IDLE中はイベントハンドラから追加の操作(IDLEの停止を除く)を行なうことはできない点に注意してください。 IDLE中のImapClientはビジー状態(IsBusyがtrue)となっているため、追加の操作を行おうとした場合にはInvalidOperationExceptionをスローします。

§5.2.5.3 クォータ

以下はクォータの取得に関するメソッドです。

クォータの取得に関するメソッド
クラス メソッド 解説 対応するIMAPコマンド
ImapClient GetQuota(string) 指定した名前のクォータルートの状態を取得します。 サーバがQUOTAをサポートしていない場合はnullを返します。 GETQUOTA
GetQuotaAsync(string)
GetQuotaUsage(string, string) 指定した名前のクォータルートのうち、指定したリソースの使用率を0.0から1.0までのdouble値で返します。 サーバがQUOTAをサポートしていない場合、上限が0の場合は0.0を返します。 GETQUOTA
GetQuotaUsageAsync(string, string)
ImapMailboxInfo GetQuota() メールボックスに割り当てられているクォータの一覧をIEnumerable<ImapQuota>で返します。 サーバがQUOTAをサポートしていない場合は、空のIEnumerable<ImapQuota>を返します。 GETQUOTAROOT
GetQuotaAsync()
クラス メソッド 解説 対応するIMAPコマンド

§5.2.6 拡張機能を使用しているメソッド・プロパティ

拡張機能と、それを使っているメソッド・プロパティの一覧です。 これらのメソッドは、拡張機能が使用できない場合には標準コマンドによる不完全なエミュレーションを行うか、例外ImapIncapableExceptionをスローします。

拡張機能をサポートしていない場合は不完全なエミュレーションを行うメソッド
拡張機能 対応するImapCapability 動作が変わるメソッド・プロパティ サポートしていない場合の動作と注意事項
QUOTA ImapCapability.Quota ImapClient.GetQuota(string) 常にnullを返します。
ImapMailboxInfo.GetQuota() 常に空のIEnumerable<ImapQuota>を返します。
IDLE ImapCapability.Idle ImapOpenedMailboxInfo.Idle(...)
ImapOpenedMailboxInfo.StartIdle(...)
ImapOpenedMailboxInfo.WaitForMessageArrival(...)
ImapOpenedMailboxInfo.StartWaitForMessageArrival(...)
NOOPコマンドを使ったポーリングを行うため、サーバーからの通知は(即時ではなく)ポーリング間隔おきに通知されます。
MAILBOX-REFERRALS ImapCapability.MailboxReferrals ImapClient.GetMailboxes(ImapMailboxListOptions)などのメソッド
(ImapMailboxListOptions.Remoteを指定している場合)
LIST/LSUBコマンドを使用するため、ImapMailboxListOptions.Remoteを指定していない場合と同じ動作になります。
MULTIAPPEND ImapCapability.MultiAppend ImapMailboxInfo.AppendMessages(...) メッセージを一つずつアップロードするため、途中で失敗した場合は一部のメッセージのみアップロードされた状態になる場合があります。
UIDPLUS ImapCapability.UidPlus ImapMessageInfo.Delete()
ImapMessageInfoList.Delete()
EXPUNGEコマンドを使って削除するため、\Deletedが設定されている他のメッセージも同時に削除されます。
ImapMailboxInfo.IsUidPersistent 常にtrueを返します。
CONDSTORE ImapCapability.CondStore ImapMailboxInfo.IsModSequencesAvailable 常にfalseを返します。
ImapMessageInfo.ModSeq 常に0を返します。
MOVE ImapCapability.Move ImapMessageInfo.MoveTo(...)
ImapMessageInfoList.MoveTo(...)
CopyTo()+Delete()相当の動作となるため、UIDPLUSをサポートしていない場合は\Deletedが設定されている他のメッセージも同時に削除されます。
拡張機能 対応するImapCapability 動作が変わるメソッド・プロパティ サポートしていない場合の動作と注意事項
拡張機能をサポートしていない場合はImapIncapableExceptionをスローするメソッド
拡張機能 対応するImapCapability 動作が変わるメソッド
WITHIN ImapCapability.Within ImapOpenedMailboxInfo.GetMessages(...)などでImapSearchCriteria.YoungerもしくはImapSearchCriteria.Olderを指定した場合。
SORT ImapCapability.Sort ImapOpenedMailboxInfo.GetSortedMessages(...)
SORT=DISPLAY ImapCapability.SortDisplay ImapOpenedMailboxInfo.GetSortedMessages(ImapSortCriteria.DisplayFrom, ...)
ImapOpenedMailboxInfo.GetSortedMessages(ImapSortCriteria.DisplayTo, ...)
THREAD=REFERENCES ImapCapability.ThreadReferences ImapOpenedMailboxInfo.GetThreadedMessages(ImapThreadingAlgorithm.References, ...)
THREAD=ORDEREDSUBJECT ImapCapability.ThreadOrderedSubject ImapOpenedMailboxInfo.GetThreadedMessages(ImapThreadingAlgorithm.OrderedSubject, ...)
SORT=DISPLAY ImapCapability.SortDisplay ImapOpenedMailboxInfo.GetSortedMessages(...)などでImapSortCriteria.DisplayFromもしくはImapSortCriteria.DisplayToを指定した場合。
SPECIAL-USE ImapCapability.SpecialUse ImapClient.GetMailbox(ImapSpecialMailbox), ImapClient.OpenMailbox(ImapSpecialMailbox)などImapSpecialMailboxを引数にとるメソッド
X-GM-EXT-1 ImapCapability.GimapGmExt1 ImapOpenedMailboxInfo.GetMessagesGmailSearch(...)
拡張機能 対応するImapCapability 動作が変わるメソッド
特記事項
GMailでSPECIAL-USEを無効化する場合は、ImapClient.DisabledCapabilitiesにImapCapability.SpecialUseImapCapability.GimapXlistの両方を追加する必要があります。

§5.2.7 IMAPコマンドの引数およびレスポンスのデータ型

(このドキュメントは作成中です)

  • ImapStoreDataItem
  • IImapMailboxFlagSet

§6 非同期操作

本ライブラリではImapClient.BeginConnect()/EndConnect()、ImapClient.ConnectAsync()や、ImapMessageInfo.OpenReadAsync()、SaveAsync()など非同期バージョンのメソッドを用意しています。 また、ImapOpenedMailboxInfo.StartIdle()/EndIdle()などのメソッドでも非同期操作を行うことができます。

これらのうち、Asyncで終わる名前のメソッドは.NET Framework 4.0以降でのみ使用できるもので、いずれもTaskまたはTask<T>を返します。

§6.1 非同期操作の制限

ImapClientおよび関連クラスは並列操作をサポートしません。 常に1クライアントにつき同時に1操作のみ行えます。 この制限は非同期バージョンのメソッドを使った場合も同様です。 非同期バージョンのメソッドは非同期操作のみをサポートします。 同期操作・非同期操作に関わらず、何らかの操作が進行中の場合は同一のクライアントから別の操作を開始することはできません。 操作が進行中の場合、ImapClient.IsBusyプロパティはtrueを返します。 この状態で別の操作を開始しようとした場合にはInvalidOperationExceptionがスローされます。

例えば、次のような操作は正しく動作する保証はありません。

メッセージの取得を並列して行う
var client = new ImapClient(...);
var inbox = client.OpenInbox();

var m1 = inbox.GetMessageBySequence(1);
var m2 = inbox.GetMessageBySequence(2);

// 2つのメッセージの本文の取得を並列して開始する
var t1 = m1.ReadAllBytesAsync();
var t2 = m2.ReadAllBytesAsync(); // タスクが進行中の場合は他の処理を開始できず、例外をスローする場合がある

Task.WaitAll(t1, t2);
メッセージの取得中に他の処理を行う
var client = new ImapClient(...);
var inbox = client.OpenInbox();

var m1 = inbox.GetMessageBySequence(1);
var t1 = m1.ReadAllBytesAsync();

// 本文の取得が開始している状態で別の処理(メッセージの取得)を行う
var m2 = inbox.GetMessageBySequence(2); // タスクが進行中の場合は他の処理を開始できず、例外をスローする場合がある

t1.Wait();
IDLE中に他の処理を行う
var client = new ImapClient(...);
var inbox = client.OpenInbox();

// IDLEを開始する
inbox.StartIdle();

// IDLEの進行中に別の処理(メッセージの取得)を行う
var m = inbox.GetMessageBySequence(1); // IDLEが進行中の場合は他の処理を開始できず、例外をスローする場合がある

並列操作を行いたい場合は、ImapClientのインスタンスを並列数分だけ用意してください。 例えば、次のような操作は正しく動作します。

別々のクライアントを用意してメッセージ本文の取得を並列して行う
var client1 = new ImapClient(...);
var client2 = new ImapClient(...);
var inbox1 = client1.OpenInbox();
var inbox2 = client2.OpenInbox();

var m1 = inbox1.GetMessageBySequence(1);
var m2 = inbox2.GetMessageBySequence(2);

// 別々のクライアントから取得したメッセージを使い、そのそれぞれで並列して本文を取得する
var t1 = m1.ReadAllBytesAsync();
var t2 = m2.ReadAllBytesAsync();

Task.WaitAll(t1, t2);

なお、ImapMessageInfoクラスのうち、動的属性・静的属性を表すプロパティは、属性が未取得の場合は参照時にコマンドを発行します。 したがって、別の操作の進行中にこれらのプロパティを参照した場合も同様に例外をスローするので注意してください。 ImapMessageInfoの取得時にImapMessageFetchAttributeOptionsを指定し、事前に属性を取得しておくようにしておけば例外の発生を避けることができます。

ImapMessageInfo.OpenRead()およびOpenText()メソッドが返すStream/StreamReaderについても取り扱いに注意する必要があります。 これらのメソッドが返すStream/StreamReaderは、Closeメソッドが呼び出されるまで操作を完了しません(Closeメソッドが呼び出されるまではIsBusyがtrueとなる)。 そのため、Stream/StreamReaderがCloseされる前に他の処理を行おうとした場合にはInvalidOperationExceptionがスローされることになります。

従って、次のような操作も正しく動作しません。

OpenRead/OpenTextで取得したStream/StreamReaderの読み込み中に他の処理を行う
var client = new ImapClient(...);
var inbox = client.OpenInbox();
var m1 = inbox.GetMessageBySequence(1);

// メッセージ本文を読み込むStreamReaderを取得する
using (var reader = m1.OpenText()) {
  // ImapMessaageInfoから取得したStreamReaderがCloseされるまでは
  // 他の処理を開始できず、例外をスローする
  var m2 = inbox.GetMessageBySequence(2);
}

// 取得したStreamReaderをCloseした時点で別の処理を開始できるようになる
var m3 = inbox.GetMessageBySequence(3);

§6.2 非同期操作の中断とタイムアウト

非同期バージョンのメソッドでは、CancellationTokenを指定することにより非同期操作を中断することができるものもあります。 ただしCancellationTokenで中断要求を行なった場合でもプロトコル上の制限などにより即座には中断しない場合もあります。

Timeoutプロパティなどの設定によって強制的にタイムアウトさせることでも操作を中断させることができます。 非同期操作の場合も同期操作と同様にTimeoutプロパティなどの設定に従って操作がタイムアウトします。

なお、操作がタイムアウトしてTimeoutExceptionをスローする場合、ImapClientは内部的に接続を切断しImapClient.IsConnectedプロパティはfalseとなります。 タイムアウトした場合は処理を継続できないため、再接続する必要があります。 一方CancellationTokenによって中断を行った場合は接続は維持され、以後も処理を継続することができます。

§6.3 非同期操作の進捗通知

一部の非同期操作メソッドは進捗通知を行わせることができます。 進捗通知は.NET Framework 4.5以降でサポートしていて、IProgress<T>を指定することにより非同期操作の進捗を通知させることができます。 進捗の具体的な取得方法に関してはProgress<T>クラスを参照してください。

ImapMessageInfoクラスの非同期メソッドでは、IProgress<ImapMessageFetchProgressInfo>で進捗の通知を行います。 ImapMessageFetchProgressInfo構造体からは以下の情報を取得できます。

ImapMessageFetchProgressInfo構造体のプロパティ
プロパティ 解説
FetchedOctetCount 現在までに取得が完了したメッセージ本文のオクテット数
TotalOctetCount 取得されるメッセージ本文の総オクテット数
ProgressPercentage 現在までに取得が完了した割合(%)
百分率に100を掛けて0〜100までの整数で表した値

§6.4 スレッドセーフティ

ImapClientなどのクラスはスレッドセーフとなっているため、同時に複数のスレッドから参照することができます。 だだし、並列操作はサポートしていないため、同時に複数のスレッドから操作を行う場合、InvalidOperationExceptionがスローされる場合があります。

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


§7 例外とトレースログ

§7.1 例外

ライブラリからは主に以下の例外をスローします。 InnerExceptionプロパティに原因となった例外を設定した状態でスローする場合もあります。 また、ここに明記している以外の例外クラス・状況でスローされる場合があります。

スローする例外と状況
名前空間 例外クラス 状況 スローする可能性のあるメソッド
Smdn.Net.Imap4.Protocol ImapConnectionException 接続に失敗した場合、送受信中にソケットエラーが発生した場合など ImapClient.Connect()
Smdn.Net.Imap4.Protocol ImapSecureConnectionException SSL/TLSへのアップグレードに失敗した
証明書を検証した結果無効と判断した
ImapClient.Connect()
Smdn.Net.Imap4 ImapAuthenticationException 認証に失敗した ImapClient.Connect()
Smdn.Net.Imap4 ImapNoAppropriateAuthMechanismException 現在の設定で試行できる認証メカニズムが無い ImapClient.Connect()
Smdn.Net.Imap4 ImapErrorResponseException サーバがエラー応答を返した ImapClientおよび各クラスのメソッド・プロパティ
Smdn.Net.Imap4 ImapProtocolViolationException プロトコル上不正な操作を行おうとした ImapClientおよび各クラスのメソッド・プロパティ
Smdn.Net.Imap4 ImapIncapableException サーバがサポートしていない機能、あるいは無効化している機能を使った処理を要求しようとした ImapClientおよび各クラスのメソッド
(§.拡張機能を使用しているメソッド・プロパティ)
Smdn.Net.Imap4.Client ImapMailboxNotFoundException 指定された名前のメールボックスが見つからない ImapClient.GetMailbox()
ImapMessageInfo.CopyTo()など
Smdn.Net.Imap4.Client ImapMessageNotFoundException 指定されたUIDもしくは通番を持つメッセージが存在しない ImapOpenedMailboxInfo.GetMessageByUid()
ImapOpenedMailboxInfo.GetMessageBySequence()
Smdn.Net.Imap4.Client ImapUnavailableException 既に閉じたメールボックス、既に削除・移動したメッセージや取得元のメールボックスが既に閉じているメッセージに対して操作を行おうとした ImapOpenedMailboxInfoクラス・ImapMessageInfoクラスの各メソッド・プロパティ
Smdn.Net.Imap4.Client ImapMessageVanishedException 他のクライアントによって削除・移動されたことにより該当するメッセージが消滅した ImapMessageInfoクラスの各プロパティ、ImapMessageInfo.Open*(),ImapMessageInfo.Read*()などのメソッド
Smdn.Formats EncodingNotSupportedException サーバーは指定したエンコーディングをサポートしていない ImapOpenedMailboxInfo.GetMessages()などEncodingを指定するメソッド
System InvalidOperationException 切断された状態(ImapClient.IsConnectedがfalse)で操作を行おうとした
操作が進行している状態(ImapClient.IsBusyがtrue)で別の操作を開始しようとした
ImapClientおよび各クラスのメソッド・プロパティ
(§.非同期操作の制限)
System TimeoutException ImapClient.Timeout, SendTimeout, ReceiveTimeoutプロパティで指定されている時間内に操作が完了しなかった ImapClientおよび各クラスのメソッド・プロパティ
(§.非同期操作の中断とタイムアウト)
System ArgumentException
および派生クラス
nullや値域外の値など、メソッド呼び出し時の引数が不正な場合 ImapClientおよび各クラスのメソッド・プロパティ

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

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

  • System.SystemException
    • Smdn.Net.Imap4.ImapExcetion
      • Smdn.Net.Imap4.Protocol.ImapConnectionException
        • Smdn.Net.Imap4.Protocol.ImapUpgradeConnectionException
          • Smdn.Net.Imap4.Protocol.ImapSecureConnectionException
      • Smdn.Net.Imap4.ImapInvalidOperationException
        • Smdn.Net.Imap4.ImapErrorResponseException
          • Smdn.Net.Imap4.ImapAuthenticationException
        • Smdn.Net.Imap4.ImapIncapableException
        • Smdn.Net.Imap4.ImapProtocolViolationException
        • Smdn.Net.Imap4.Client.ImapMessageVanishedException
        • Smdn.Net.Imap4.Client.ImapUnavailableException
      • Smdn.Net.Imap4.ImapNoAppropriateAuthMechanismException
      • Smdn.Net.Imap4.Client.ImapMailboxNotFoundException
      • Smdn.Net.Imap4.Client.ImapMessageNotFoundException
    • System.NotSupportedException
      • Smdn.Formats.EncodingNotSupportedException

§7.2 ログ

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

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

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

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

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

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

トレースにログを出力する際、出力内容は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.Imap4.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]
  static void Main(string[] args) {
    // カスタマイズしたトレースリスナを追加
    ImapConnection.TraceSource.Listeners.Add(new CustomTraceListener("log"));
      :
      :
  }
}

§8 サンプルコード

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

§8.1 ImapClient

§8.1.1 メッセージをアップロードする

using System;
using System.IO;

using Smdn.Net.Imap4;
using Smdn.Net.Imap4.Client;

class Sample {
  static void Main(string[] args)
  {
    using (var client = new ImapClient("localhost", 143, "user", "DIGEST-MD5")) {
      client.Connect("pass");

      // INBOXを取得
      var inbox = client.GetInbox();

      // ファイルsample.emlを開く
      using (var messageStream = File.OpenRead("sample.eml")) {
        // Streamの内容をINBOXにアップロード
        inbox.AppendMessage(messageStream);
      }
    }
  }
}

§8.1.2 メッセージの検索