サイト内で配布中のライブラリについてのスレッド。 バグ報告・質問など。 Atom 1.0

※ブラウザによっては新しい書き込みが表示されない場合があるようなので、返信が表示されていない場合はF5または更新ボタンでページを更新してください。

:ID:cipeco0y

配布中のライブラリについてのバグ報告・要望・質問用のスレッドです。
書き込みの際は再現用のコードやログなどもあわせて書き込んで頂けると助かります。

:ID:G9B2VPWg

Smdn.Protocols.Imap4 (v20) を利用させていただき、
Gmail から、C# で、IMAP4 受信を行おうと試みています。
受信には成功したのですが、同じメールを何度も受信して
しまうので、これを回避したいのですが、ドキュメントにある

request.Method = ImapWebRequestMethods.Expunge;

で試すも、うまく行きません。
IMAP URL を メールボックス形式で指定した場合には、
すべてのメールが受信できず、単一のメッセージを表すURL で
指定した場合には、すべてのメールを受信してしまいます。

プロパティをセットするタイミングが悪いのだと思いますが、
サンプルコードなどいただけないでしょうか。

あつかましいお願いで申し訳ございませんが、
よろしくお願いいたします。

:ID:cipeco0y

>>2
具体的にどういうコードを書かれたのかわからないので推測でしか言えませんが、
もし同じUIDを指定した(=単一のメッセージを表す)IMAP URLでリクエストしたのならば、
毎回同じメールが受信されるのはIMAP的には正しい動作です。

また、メールボックス形式のURLでリクエストした場合については、メールボックスに
あるすべてのメールのエンベロープだけが受信されます。 全メールの全文を
受信するわけではありません。 これはライブラリの仕様です。

EXPUNGEがうまくいかないのと、単一のメッセージを表すURLですべてのメールを
受信してしまうのはライブラリのバグかURLの形式が誤っているかどちらかだと
思うのですが、先も書いたように実際のコードまたはログがないとなんとも
言えません。

不都合のない範囲で構わないので、実際のコードまたはうまくいかなかったときの
ログを見せていただけますか?

動作ログは、Smdn.Protocols.Imap4のコンパイルオプションでシンボルTRACEを
有効にした上で、アプリケーション構成ファイルに以下の内容を書けばコンソールに
表示されるようになります。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.diagnostics>
    <sources>
      <source name="Smdn.Protocols.Imap4" switchName="switch" switchType="System.Diagnostics.SourceSwitch">
        <listeners>
          <add name="console" type="System.Diagnostics.ConsoleTraceListener"/>
          <remove name="Default"/>
        </listeners>
      </source>
    </sources>
    <switches>
      <add name="switch" value="All"/>
    </switches>
  </system.diagnostics>
</configuration>
:ID:cipeco0y

>>3の続きです。
とりあえず動作確認もかねて書いたサンプルを提示します。

どのようなサンプルコードが必要なのかはっきりとわからなかったのですが、
POP3のような動作を期待しているように思ったので、INBOXの未読メールを
検索・ダウンロードしたあと、削除するというサンプルを書いてみました。

具体的なご指定があれば、改めて書きます。

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;

using Smdn.Protocols.Imap4;
using Smdn.Protocols.Imap4.WebClient;

namespace imaptest {
  class MainClass {
    public static void Main(string[] args)
    {
      ImapSessionManager.RegisterWebRequestPrefix();
      ImapSessionManager.CertificateValidationCallback = delegate { return true; };

      var credential = new NetworkCredential("user", "pass");

      // INBOXにある未読メッセージを検索・取得する
      var searchUnseenMessageRequest = WebRequest.Create(new ImapUri("imaps://user@imap.gmail.com/INBOX/?UNSEEN"));

      searchUnseenMessageRequest.Credentials = credential;

      Console.WriteLine("unseen messages");

      var unseenMessageUrls = new List<ImapUri>();

      using (var response = searchUnseenMessageRequest.GetResponse() as ImapWebResponse) {
        foreach (var message in response.Messages) {
          unseenMessageUrls.Add(message.Url);

          Console.WriteLine("  {0} ({1})", message.Envelope.Subject, message.Url);
        }
      }

      // 取得したメッセージをダウンロードし、ファイルに保存する
      var downloadedMessageFiles = new List<string>();

      foreach (var messageUrl in unseenMessageUrls) {
        var fetchMessageRequest = WebRequest.Create(messageUrl);

        fetchMessageRequest.Credentials = credential;

        using (var response = fetchMessageRequest.GetResponse() as ImapWebResponse) {
          var responseStream = response.GetResponseStream();
          var downloadedMessageFile = string.Format("uid-{0}.eml", messageUrl.Uid);

          using (var downloadedFileStream = File.OpenWrite(downloadedMessageFile)) {
            var buffer = new byte[4096];

            for (;;) {
              var read = responseStream.Read(buffer, 0, 100);

              downloadedFileStream.Write(buffer, 0, read);

              if (read <= 0)
                break;
            }
          }

          downloadedMessageFiles.Add(downloadedMessageFile);

          Console.WriteLine("downloaded {0} as {1}", messageUrl, downloadedMessageFile);
        }
      }

      // 保存したファイルの内容を表示する
      foreach (var file in downloadedMessageFiles) {
        Console.WriteLine("==================== {0} ====================", file);
        Console.WriteLine(File.ReadAllText(file));
      }

      // 取得したメッセージを削除する(\Deletedフラグを付けたあとEXPUNGEコマンドを送信する)
      foreach (var messageUrl in unseenMessageUrls) {
        var storeDeletedAndExpungeMessageRequest = WebRequest.Create(messageUrl);

        storeDeletedAndExpungeMessageRequest.Credentials = credential;
        storeDeletedAndExpungeMessageRequest.Method = ImapWebRequestMethods.Expunge;

        using (var response = storeDeletedAndExpungeMessageRequest.GetResponse() as ImapWebResponse) {
          Console.WriteLine("expunged {0}", messageUrl);
        }
      }
    }
  }
}
:ID:G9B2VPWg

>>4
管理人さま。
とりあえず、自己解決してました。コードは↓です。
URL 単位でリクエストした後、GetResponse() を呼んでいなかったのが原因でした。初歩的なミスですみません。

  class MailService
  {
    private String host = "";
    private String port = "";
    private String user = "";
    private String password = "";
    public MailService(String host, String port, String user, String password)
    {
      this.host = host;
      this.port = port;
      this.user = user;
      this.password = password;
    }

    public Mail[] doImap()
    {
      List<Mail> mailList = new List<Mail>();
      ImapSessionManager.RegisterWebRequestPrefix();
      ImapSessionManager.CertificateValidationCallback = delegate { return true; };

      // とりあえず、ボックスにアクセスして
      String boxUrl = "imaps://" + user + "@" + host + "/INBOX/";
      var boxRequest = WebRequest.Create(boxUrl) as ImapWebRequest;
      boxRequest.Credentials = new NetworkCredential(user, password);

      using (var client = new WebClient())
      using (var response = boxRequest.GetResponse() as ImapWebResponse)
      {
        // ボックス内のメッセージ一覧を取得する
        foreach (var message in response.Messages)
        {
          // メッセージの情報から、メールの文字列をとってくる
          String mail = client.DownloadString(message.Url);
          // それを、メールクラスに格納して、リストに追加する
          mailList.Add(new Mail(mail));
          // で、今度は UID を特定して、メッセージにアクセスして、
          var urlRequest = WebRequest.Create(boxUrl + ";UID=" + message.Uid) as ImapWebRequest;
          urlRequest.Credentials = new NetworkCredential(user, password);
          // 削除フラグを立ててから (一度受信したやつは消す)
          urlRequest.Method = ImapWebRequestMethods.Expunge;
          // リクエストを送っておしまい
          urlRequest.GetResponse();
        }
      }
      return mailList.ToArray();
    }
  }

いただいたコードのおかげで、credentials の使い方もわかりましたので、拙い部分をさらに修正したいと思います。
コードのボリュームがすごかったので、追うのをあきらめ、サンプルだけでやろうとしていましたもので。

それにしても、こんなに速く、ご丁寧な回答をいただけるとは思っていませんでしたので、感激です。
本当にありがとうございました。

:ID:cipeco0y

>>5
解決したようで何よりです。

コードを見ましたが3つほど気になった箇所があったので書いておきます。
もしすでに修正済みだったすみません。

1つめに、GetResponseで取得したWebResponseについて、Close呼ぶかusingステートメントを
使うかしたほうがいいです。
2つめも1と同様で、ImapWebResponseのCloseが呼ばれる前にWebClient.DownloadStringを
呼んでいますが、この部分はusingの外に出したほうがいいです。

この2つは、ライブラリ内部の実装の一部はCloseを呼ばないとリクエストが完結せず、
内部状態がおかしくなる場合があるためです。

3つめは、単に改善案です。
メッセージごとにEXPUNGEのリクエストを送っている部分ですが、UIDが分かっているので
1回のリクエストでまとめてEXPUNGEできます。
Gimapサーバは遅いので、メッセージの数が多い場合、メッセージの数だけEXPUNGEの
リクエストを送るとだいぶ時間がかかるので、まとめて1回のリクエストにしたほうが
速く済みます。

実際のコードはこんな感じです。 テストはしてませんが、よろしければお使いください。

/*
 * (中略)
 */
List<long> uidList = new List<long>();

using (var client = new WebClient())
using (var response = boxRequest.GetResponse() as ImapWebResponse)
{
  // ボックス内のメッセージ一覧を取得する
  foreach (var message in response.Messages)
  {
    /*
     * (中略)
     */

    // メッセージのUIDをリストに追加する
    uidList.Add(message.Uid);
  }
}

// 削除フラグを立てるメッセージのuid setをクエリに含めたURLを作成する
String expungeUrl = boxUrl + "?UID " + ImapSequenceSet.CreateUidSet(uidList.ToArray()).ToString();

ImapWebRequest urlRequest = WebRequest.Create(expungeUrl) as ImapWebRequest;

urlRequest.Credentials = new NetworkCredential(user, password);
urlRequest.Method = ImapWebRequestMethods.Expunge;

// リクエストを送信、レスポンスを閉じて終了
urlRequest.GetResponse().Close();
:ID:X0pcAFaZ

こんにちは。
WindowsXPでC#で、GmailをPOPで受信しようとしています。
こちらのソースとサンプルが大変充実しているようなので、使わせていただこうとしています。
たいへん貴重なものを公開してくださっていて感謝します。
さて、ところが最初から躓いていて、質問したいと思います。
(1)Smdn.Net.Pop3.Client-091\Smdn.Net.Pop3.Client\Smdn.Net.Pop3.Client.csprojをVisualC#ExpressEdition2008で開いて、まずビルドしてみました。

すると、
参照アセンブリ "Smdn.Net.Pop3.Client-0.91\build\bin\Release\Smdn.Core.Standards.dll" が見つかりませんでした。このアセンブリが別のプロジェクトによって生成された場合、このプロジェクトをビルドする前に、その生成元のプロジェクトをビルドすることを確認してください。
とエラーになりました。

(2)Smdn.Net.Pop3.Client-0.91\build\bin\Release
は空だったので、Smdn.Net.Pop3.Client-091\Smdn.Net.Pop3.Client.clsを開いて、ビルドしてみました。

自動的に実装されたプロパティ 'Smdn.Net.Pop3.PopDropListing.SizeInOctets' のバッキング フィールドは、コントロールが呼び出し元に返される前に完全に割り当てられている必要があります。コンストラクタ初期化子から既定のコンストラクタを呼び出すことを検討してください。

これはどのように対処したらよいか、なにかドキュメントをご示唆いただけるとうれしいです。

(3)そもそも使用するのはどのディレクトリのどのファイル群なのでしょう?
Smdn.Net.Pop3.Client-0.91\Smdn.Net.Pop3.Client\Smdn.Net.Pop3.Client
2010/04/24 23:54 15,581 PopClient.cs
2010/04/24 23:54 6,571 PopClientProfile.cs
2010/04/24 23:54 2,407 PopMessageDeletedException.cs
2010/04/24 23:54 9,659 PopMessageInfo.cs
2010/04/24 23:54 2,520 PopMessageNotFoundException.cs
2010/04/24 23:54 3,617 Trace.cs

を使うとして、
「メールボックスにあるすべてのメッセージをダウンロードしてファイルに保存する」のソースを、
main.csとして保存して、このフォルダに保存すれば使えるのでしょうか?
.slnがないのでどうしたらよいかと悩んでいます。

(4)Gmailの受信サンプルで、1通のみ、またはメールボックスの全メールを受信するサンプルがありました。
このメールボックスは、GmailのIndoxにあるものを意味している、と考えてよいのでしょうか?
試せればよいのですが、まだ試せていないので、質問のみ先行します。
で、もしそうだとしたら、メールを受信したあと、IndoxからArchiveに移動したいのですが、それは可能でしょうか?

以上、初心者の質問で恐縮です。よろしくご回答いただけたらうれしいです。

:ID:gYO9uyz9

Smdn.Net.Pop3.Client-0.91を利用して
GmailからPop3でメール受信をしようとしています。

ドキュメント・サンプルの「Gmailアカウントのメールボックスから、最新のメッセージをダウンロードする」を例に試しているのですが、
new Uri("pops://user@pop.gmail.com/")の箇所で
「無効な URI: ホスト名を解析できませんでした」と出て失敗してしまいます。

Gmailの場合、userがxxx@gmail.comになるためUriクラスのコンストラクタに渡している文字列が
「pops://xxx@gmail.com@pop.gmail.com/」になっています。

これを回避する方法が分からないのでどうしたらいいのかご教授願えませんでしょうか。
よろしくお願いします。

:ID:gYO9uyz9

すいません。
自己解決しました。
userは@以前だけでよかったようで失礼致しました。

:ID:+iq/7O8y

>>7
こんにちは。 質問頂いた件について、順にお答えします。

(3)について
先ほどリリースした0.92より、実行可能なプロジェクトをソリューションに含めるようにしました。
PopClientSample.slnもしくはPopWebClientsSample.slnを開いて、Main.csを編集すれば
すぐに実行できるようにしてありますので、お使いください。

(1)と(2)についても(3)で解決すると思います。

(1)について。
Smdn.Net.Pop3.Client.csprojは他のプロジェクトを参照しているので、単体ではビルドできません。
ソースパッケージにあるslnファイルを開いてビルドするか、ソースパッケージの
各ディレクトリにあるcsprojを同一のソリューションに追加してからビルドしてください。

(2)について。
0.91に含まれるPopDropListing.csは、Visual C#ではビルドエラーになるコードになっていました。
0.92で修正しましたので、そちらを使ってください。

エラーが発生した原因と対処法の解説は省きますが、以下のドキュメントがそれに関連するものになります。
http://msdn.microsoft.com/ja-jp/library/bb384054.aspx 自動実装するプロパティ
http://msdn.microsoft.com/ja-jp/library/cc433530.aspx 構造体コンストラクタ

(4)について
「メールボックス」が指すのは、GmailではINBOXにあたります。
Gmailでは、POPでアクセスした場合は常にINBOXのメールを参照します。

Archiveへの移動についてですが、受信したメールはすべてINBOXとArchiveの両方に入っていたと思います。
POPでメールを受信する際、INBOXから削除してもArchiveには残ったままだと思うので、
わざわざArchiveに移動する必要はないと思います。

また、もし仮にメールボックスをまたぐ操作をしたいのであれば、POPではできないので
IMAPを使う必要があります。

:ID:b9UhlaRj

gmailのpop3受信非常にありがたく使わせていただきました。
まず、google appsで設定した独自ドメインでも使用可能であった事をご報告します。

appsはユーザー名がメールアドレス形式なので、サンプルであったgmailの例ではなく以下の方式で行ないました。

client = new PopClient("pop.gmail.com", 995, true, "user");
client.Connect("pass");

ここまではよかったのですが、私の場合、gmail以外にも使用するので、試しにyahooでも試したところ、どのような接続設定を行ってもう
まくいきません。

client = new PopClient("pop.mail.yahoo.co.jp", 110, false, "user", "PLAIN");
client.Connect("pass");

↓以下のエラー↓

authentication failed "popgate unknown command" (result code:Error)

この指定もだめでした

client = new PopClient(new Uri(@"pop://user@pop.mail.yahoo.co.jp/"));
client.Connect("pass");

↓以下のエラー↓

appropriate authentication mechanism not found

yahooは、SSLでもありませんし、特に特別な設定はしていないと思うのですが、自分の接続指定方法に誤りがあるのか、うまくいきませんでした。
ご教授頂けませんでしょうか。

:ID:3+urb4HG

まずgoogle appsでの動作報告ありがとうございます。 非常に参考になります。

yahooでの接続についてですが、以下のコードを試してみて下さい。

client = new PopClient("pop.mail.yahoo.co.jp", 110, false, "user");
//もしくは
//client = new PopClient(new Uri(@"pop://user@pop.mail.yahoo.co.jp/"));

client.Profile.AllowInsecureLogin = true;
client.Connect("pass");

デフォルトでは、SSL無しの場合は平文でのログインを行わないようにしていますが、
それが原因でエラーとなっていると思います。
AllowInsecureLoginをtrueにすれば、平文でのログインを許可するようになります。

:ID:b9UhlaRj

早いご回答ありがとうございました!
早速試してみたところうまくいきました。

また、yahooだけでなく、ぷららでもいきました。
ご教授ありがとうございました!

:ID:mvvRzrs+

Smdn.Formats.Mime について質問です

現在メールデコードにMimeクラスを使用し、ほとんどのケースは以下でデコードし、うまく稼働しています。

foreach (var message in client.GetMessages(true))
{
    MimeMessage m = message.ReadAs<MimeMessage>(MimeMessage.Load);
    body = m.ReadContentAsText();
}

しかし、一件文字化けが発生しました。bodyは次のようになりました。

Content-Type: text/plain; charset=ISO-2022-JP
Content-Transfer-Encoding: 7bit

$B$G$O!"$I$&$>$h$m$7$/$*4j$$$$$?$7$^$9!#&#x1B;(B

試しに以下で試したところきちんとデコードされました。

body = message.ReadAllText(Encoding.GetEncoding("iso-2022-jp"));

何かMimeクラスの使用方法に間違いがありますでしょうか。
また、暫定的に、文字化けが発生したら、次はエンコードタイプを指定して再取得したいと考えていますが、Mimeクラスでエンコードタイプを指定する事は可能でしょうか。

お手数ですがご教授頂けると幸いです。

:ID:3+urb4HG

>>14
bodyにContent-Typeヘッダが入ってるところを見ると、読もうとしているメールの書式に異常があり、
Content-Typeヘッダ以下が本文として扱われているため、文字化けしていると思われます。
おそらくヘッダの途中(Content-Typeヘッダの前)に余計な空行があるのが原因ではないかと思いますので、
読もうとしているメールのヘッダ部分を確認してみてください。
Content-Typeヘッダが正しく読まれている場合は、m.Charsetプロパティに適切なEncodingが
設定されているはずです。

次に、エンコードタイプを指定した読み込みついては、ReadContentAsText()メソッドを呼び出す前に
MimeMessage.Charsetプロパティの値を上書きすれば、その文字コードで読み込むようになっています。

MimeMessage m = message.ReadAs<MimeMessage>(MimeMessage.Load);

if (m.Charset.CodePage == Charsets.Latin1.CodePage) {
    // ヘッダでcharsetが指定されていない場合はデフォルトでLatin1が使用されるので、
    // Latin1の場合は文字化けすると判断し、代わりにiso-2022-jpを設定する
    m.Charset = Encoding.GetEncoding("iso-2022-jp");
}

body = m.ReadContentAsText();

なお、ヘッダでcharsetが設定されていない場合は上記のコードで対応できますが、不正なcharsetや
.NET Frameworkがサポートしていないcharsetが設定されている場合は、MimeMessage.Loadが
NotSupportedExceptionをスローします。 このあたりは今後のバージョンで改善するかもしれません。

:ID:lthiXA+R

バグ報告です。

MimeMessage.Load()でファイルからMimeMessageインスタンスを生成する際の
LineOrientedStreamのReadLineメソッドで \r\n が内部バッファで区切られると
次のReadLineメソッドの呼び出し時に \n が1文字目に来てしまいヘッダの解析
処理がストップしてしまいます。

:ID:lthiXA+R

バグ?報告です。

EncodingUtils.GetEncoding()の引数のエンコード名の前後に
半角空白が含まれているだけで戻り値がnullになります。

「charset=iso-2022-jp 」となっているメールが読み込めないので
ソースを書き換えて対応していますが、出来れば取り込んでいただきたいです。

以下、書き換えたメソッド

public static Encoding GetEncoding(string name)
{

 if (name == null)
   throw new ArgumentNullException("name");
 while (true)
 {
     try
     {
         string alias;
         if (encodingAliases.TryGetValue(name, out alias))
         {
             name = alias;
         }
         // Encodingを取得する
         return Encoding.GetEncoding(name);
     }
     catch (ArgumentException)
     {
         // エンコーディング名エラー
         if (name.StartsWith(" ") || name.EndsWith(" "))
         {
             // 前後の空白を取り除く
             name = name.Trim();
             continue;
         }
         if (name.StartsWith("\"") || name.EndsWith("\""))
         {
             // 前後のダブルクォーテーションを取り除く
             int start = (name.StartsWith("\"") ? 1 : 0);
             if (name.EndsWith("\"") && (name.Length > 2))
             {
                 name = name.Substring(start, name.Length - 2);
             }
             else
             {
                 name = name.Substring(start);
             }
             continue;
         }
         if (name.IndexOf('_') >= 0)
         {
             // 下線をハイフンに変換してリトライする
             name = name.Replace('_', '-');
             continue;
         }
         // Encoding取得失敗
         return null;
     }
 }

}

:ID:k/X6BCq+

ご報告ありがとうございます。
LineOrientedStreamの方は次のリリースまでに修正します。
EncodingUtilsの方については、とりあえず引数の前後の空白と、アンダーバーとハイフンの違いは無視するように修正しようと思います。
クオーテーションについては>>15の件と合わせて修正しようと思うので、どう扱うかは検討させてください。

:ID:Cnnw6BU2

VB.NET で使える POP クライアントを探してて、こちらに遭遇いたしました。早速軽く弄ってますが、下手な市販コンポーネントよりも構造が美しくて感激しています。(System.IO.FileInfoクラスに似たインターフェイスで~の辺)

使わせてもらってて早速で申し訳ないのですが、添付ファイルを取り出す場合には PopMessageInfo.Read ~を使って自力で取り出さないとダメなのでしょうか。
PopMessageInfo.Attachments みたいな風で添付ファイル群にアクセススできると滅茶苦茶うれしいです。

:ID:k/X6BCq+

ご意見ありがとうございます。

ライブラリ設計の方針上、PopMessageInfoではメール本文の解析は行わないようにしています。
本文の解析に関わる機能を追加するとPopMessageInfoのメソッド・プロパティが多くなってしまうので、
これらの機能はSmdn.Formats.Mimeで提供するようにしています。

ですので、多少面倒ではありますが、ReadXXX等のメソッドで本文をダウンロードした後、
添付ファイルの取り出しにはSmdn.Formats.Mimeや他のライブラリを使ってください。

なお、PopMessageInfo.Attachmentsのようなメンバを追加する予定はありませんが、
今後Smdn.Formats.Mimeの方で添付ファイルに関連する機能を追加するかもしれません。

:ID:k/X6BCq+

>>20の続き。

もしPOPの代わりにIMAPが使えるのであれば、IMAPの使用をおすすめします。
IMAPなら本文の解析をサーバ側でやってくれるので、ImapMessageInfo.Save等のメソッドだけで
添付ファイルの取り出しが出来ます。 この場合Smdn.Formats.Mimeは不要です。

サンプルを書くと以下のような感じです。

Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Net
Imports Smdn.Net.Imap4
Imports Smdn.Net.Imap4.Client

Public Class Sample
  Shared Sub Main
    Using client As New ImapClient(New Uri("imap://user;AUTH=CRAM-MD5@example.net/"))
      client.Connect(New NetworkCredential("user", "pass"))

      ' INBOXを開く
      Using mailbox As ImapOpenedMailboxInfo = client.OpenMailbox("INBOX")
        ' (とりあえず)UIDが1のメッセージの情報を取得
        Dim message As ImapMessageInfo = mailbox.GetMessageByUid(1)

        ' Content-Disposition: attachmentヘッダのあるメッセージパートを探す
        Dim attachments As New Dictionary(Of IImapBodyStructureExtension, String)

        For Each section As IImapBodyStructure In message.BodyStructure
          Dim s As IImapBodyStructureExtension = TryCast(section, IImapBodyStructureExtension)

          If s Is Nothing Then
            Continue For ' 拡張されたBODYSTRUCTUREではない
          Else If s.Disposition Is Nothing Then
            Continue For ' メッセージパートにContent-Dispositionヘッダがない
          Else If s.Disposition.IsAttachment
            ' メッセージパートにContent-Disposition: attachmentヘッダがある場合、
            ' filenameパラメータをディクショナリに追加
            attachments.Add(s, s.Disposition.Filename)
          End If
        Next

        ' 見つかった個々のメッセージパートについて、
        For Each attachment As KeyValuePair(Of IImapBodyStructureExtension, String) In attachments
          Dim section As IImapBodyStructureExtension = attachment.Key
          Dim filename As String = attachment.Value

          ' メッセージパートをダウンロード・デコードして、Content-Dispositionヘッダの
          ' filenameパラメータで指定されているファイル名で保存する
          message.Save(filename, section, ImapMessageFetchBodyOptions.DecodeContent)
        Next
      End Using
    End Using
  End Sub
End Class
:ID:k/X6BCq+

先ほどSmdn.Formats.Mime version 0.21ほかをリリースしました。
修正が遅くなってしまいすみません。

version 0.21で>>16の問題を修正しました。
>>14もしかしたら同じ原因なので、このバージョンでは正しく読み込めるかもしれません。

>>17については、前後の空白とエンコーディング名に含まれる空白、アンダーバー、ハイフンを無視してEncodingを取得するようにしてあります。
クオーテーションについては、MimeMessageクラスを使っている限りはエンコーディング名にクオーテーションが入ることはないので、修正はしていません。
もしクオーテーションが問題となるようでしたらお知らせください。

:ID:k/X6BCq+

続き。

また>>14>>17の件に関連して、MimeMessage.Loadメソッドで文字セットの取得に失敗した場合に使用するエンコーディングを
指定できるオーバーロードを二つ追加しました。 使い方は次のような感じです。

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

  foreach (var message in client.GetMessages()) {
    // エンコーディングの判別に失敗した場合はISO-2022-JPを使用する
    var m = message.ReadAs<Encoding, MimeMessage>(MimeMessage.Load,
                                                  Encoding.GetEncoding("iso-2022-jp"));

    Console.WriteLine(m.Headers["Content-Type"].GetParameter("charset"));
    Console.WriteLine(m.Charset);
    Console.WriteLine(m.ReadContentAsText());
  }
}

上記のコードは以下のコードと等価です。

foreach (var message in client.GetMessages()) {
  using (var stream = message.OpenRead()) {
    // エンコーディングの判別に失敗した場合はISO-2022-JPを使用する
    var m = MimeMessage.Load(stream, Encoding.GetEncoding("iso-2022-jp"));

    Console.WriteLine(m.Headers["Content-Type"].GetParameter("charset"));
    Console.WriteLine(m.Charset);
    Console.WriteLine(m.ReadContentAsText());
  }
}

コールバックメソッドを使うようにもできます。

foreach (var message in client.GetMessages()) {
  using (var stream = message.OpenRead()) {
    // エンコーディングの判別に失敗した場合はコールバックメソッドを呼ぶ
    var m = MimeMessage.Load(stream, SelectFallbackEncoding);

    Console.WriteLine(m.Headers["Content-Type"].GetParameter("charset"));
    Console.WriteLine(m.Charset);
    Console.WriteLine(m.ReadContentAsText());
  }
}

private static Encoding SelectFallbackEncoding(string name)
{
  if (string.Equals(name, "iso-2202-jp", StringComparison.OrdinalIgnoreCase)) {
    // 例として'iso-2022-jp'のつもりが'iso-2202-jp'になってる場合とか
    return Encoding.GetEncoding("iso-2022-jp");
  }
  else {
    Console.WriteLine("エンコーディングを取得できません");
    return null;
    // この後EncodingNotSupportedExceptionがスローされます
  }
}

また何かありましたらお知らせください。

:ID:6PHHJOWX

>>22

コールバック対応ありがとうございます。
理想的な対応をして頂けたので嬉しかったです。

現在数万件のメールを処理させて確認しているのですが、
以下のようなメールが来るとやはりダブルクォーテーションで
Encodingの判定処理が失敗していました。

To: test@example.jp
Sender: test@example.jp
MIME-Version: 1.0 
Subject: SAMPLE
From: <test@example.jp>
Content-Type: text/plain;charset="iso-2022-jp" 
Content-Transfer-Encoding: 7bit 
Message-Id: <20100826012503.EE3E11A72D8@example.jp>
Date: Thu, 26 Aug 2010 10:25:03 +0900 (JST)


MAIL BODY

# ちなみに楽天から送られてくるメールの一部がこんな感じです。

ただ、コールバック対応のおかげで処理を続行できているので
支障ありません。

対応ありがとうございました。

:ID:j/NwOewl

以前ご質問させて頂いたgonです。
その後非常に順調に動いていたのですが、メールサーバーの入れ替えを行ったところ急に「internal error」で接続出来なくなりました。平文で以下のような接続です。

var client = new PopClient("hogehoge.jp", "110", false, "userid");
client.Profile.UseTlsIfAvailable = true;
client.Profile.AllowInsecureLogin = true;
client.Connect("password");

Connect部分でエラーが出ます。

ちなみに、入れ替え時のMTA側は以前と全く同じ設定で、試しにBecky!2などのメーラーで該当MTAと接続すると問題なく接続できます。
逆に、上記でyahooやぷららなどのMTAには、sslを使ってないので、同様の接続ですが接続できます。

Connect以降のどの部分でエラーかまでは追いきれていませんが何か手掛かりはありますでしょうか。又は試してみた方がいい設定など?

:ID:k/X6BCq+

>>25
ありがとうございます。
はっきりとした原因が分からないので、とりあえず試せるのは以下の2点です。

まず、UseTlsIfAvailableがtrueになっているので、接続後にSSLへのアップグレードを行おうとしていて、
そこで何らかのエラーが発生し、internal errorとなっている可能性はあります。
falseにすることで状況が変わるかもしれません。

もう一つ、version 1.02でConnect()の内部実装が変わっているので、もし1.02を使っているのであれば
1.01に戻すことで接続できるかもしれません。

internal errorは予期せぬ例外の場合に起こるので、発生した例外のInnerExceptionも見てみないと
エラーの原因がバグなのかサーバの問題なのか分かりません。
ですので、スタックトレースも含めて発生した例外の詳細をお教えていただけますか?

:ID:j/NwOewl

アドバイスありがとうございます
早速やってみました。
・UseTlsIfAvailableをfalse
→結果は同じでした。

・version 1.0、及びv1.01
→結果は同じでした。同様の現象が出ました。

・スタックトレース
→ドキュメントを確認してapp.configに書き加え「シンボルTRACEを有効にしてビルド」をしたつもりなのですが、ログファイルが生成されるものの何も記載されない状態です。やり方が間違っている可能性が高いので、もし宜しければ設定方法を教えて頂けませんでしょうか。初心者で申し訳ございません。

:ID:k/X6BCq+

以下のような感じでConnect()をtry-catchでくくって例外をキャッチして、Console.WriteLine()で
表示すればスタックトレースを含む例外の詳細が出力されるので、それを書き込んでください。

try {
  var client = new PopClient("localhost", 110, false, "userid");

  client.Profile.UseTlsIfAvailable = false;
  client.Profile.AllowInsecureLogin = true;

  client.Connect("password");
}
catch (Exception ex) {
  Console.WriteLine(ex);
}

ログが記載されない件はapp.configでAutoFlushをtrueにすれば出るようになると思いますが、
とりあえず↑のやり方でスタックトレースが見れれば状況はだいぶつかめると思いますのでよろしくお願いします。

参考、app.configでのAutoFlush設定例: http://msdn.microsoft.com/ja-jp/library/system.diagnostics.tracesource.aspx

:ID:j/NwOewl

ご教授ありがとうございます。スタックトレース取りました。

Smdn.Net.Pop3.PopException: internal error ---> System.IndexOutOfRangeException: Index was outside the bounds of the array.
   at Smdn.ByteString.get_Bytes(Int32 index) in D:\Smdn.Net.Pop3.Client-1.02\Smdn\Smdn\ByteString.cs:line 47
   at Smdn.Net.Pop3.Protocol.Client.PopResponseReceiver.ParseText(Byte[] line, Int32 posStatusEnd) in D:\Smdn.Net.Pop3.Client-1.02\Smdn.Net.Pop3.Client\Smdn.Net.Pop3.Protocol.Client\PopResponseReciever.cs:line 132
   at Smdn.Net.Pop3.Protocol.Client.PopResponseReceiver.ParseResponce(Byte[] line) in D:\Smdn.Net.Pop3.Client-1.02\Smdn.Net.Pop3.Client\Smdn.Net.Pop3.Protocol.Client\PopResponseReciever.cs:line 94
   at Smdn.Net.Pop3.Protocol.Client.PopResponseReceiver.ReceiveResponse() in D:\Smdn.Net.Pop3.Client-1.02\Smdn.Net.Pop3.Client\Smdn.Net.Pop3.Protocol.Client\PopResponseReciever.cs:line 67
   at Smdn.Net.Pop3.Protocol.Client.PopConnection.TryReceiveResponse() in D:\Smdn.Net.Pop3.Client-1.02\Smdn.Net.Pop3.Client\Smdn.Net.Pop3.Protocol.Client\PopConnection.cs:line 148
   at Smdn.Net.Pop3.Client.Transaction.PopTransactionBase`1.ProcessReceiveResponse() in D:\Smdn.Net.Pop3.Client-1.02\Smdn.Net.Pop3.Client\Smdn.Net.Pop3.Client.Transaction\PopTransactionBase.cs:line 163
   at Smdn.Net.Pop3.Client.Transaction.PopTransactionBase`1.Smdn.Net.Pop3.Client.Transaction.IPopTransaction.Process() in D:\Smdn.Net.Pop3.Client-1.02\Smdn.Net.Pop3.Client\Smdn.Net.Pop3.Client.Transaction\PopTransactionBase.cs:line 148
   --- End of inner exception stack trace ---
   at Smdn.Net.Pop3.Client.Session.PopSession.PostProcessTransaction(IPopTransaction t) in D:\Smdn.Net.Pop3.Client-1.02\Smdn.Net.Pop3.Client\Smdn.Net.Pop3.Client.Session\PopSession.TransactionProcessing.cs:line 163
   at Smdn.Net.Pop3.Client.Session.PopSession.ProcessTransaction(IPopTransaction t) in D:\Smdn.Net.Pop3.Client-1.02\Smdn.Net.Pop3.Client\Smdn.Net.Pop3.Client.Session\PopSession.TransactionProcessing.cs:line 75
   at Smdn.Net.Pop3.Client.Session.PopSession.Apop(ICredentialsByHost credentials, String username) in D:\Smdn.Net.Pop3.Client-1.02\Smdn.Net.Pop3.Client\Smdn.Net.Pop3.Client.Session\PopSession.CommandsAuthorizationState.cs:line 252
   at Smdn.Net.Pop3.Client.Session.PopSessionCreator.AuthenticateWithAppropriateMechanism(PopSession session, Boolean allowInsecureMechanism, ICredentialsByHost credentials, String username, IEnumerable`1 usingSaslMechanisms) in D:\Smdn.Net.Pop3.Client-1.02\Smdn.Net.Pop3.Client\Smdn.Net.Pop3.Client.Session\PopSessionCreator.cs:line 213
   at Smdn.Net.Pop3.Client.Session.PopSessionCreator.Authenticate(PopSession session, IPopSessionProfile profile) in D:\Smdn.Net.Pop3.Client-1.02\Smdn.Net.Pop3.Client\Smdn.Net.Pop3.Client.Session\PopSessionCreator.cs:line 153
   at Smdn.Net.Pop3.Client.Session.PopSessionCreator.CreateSession(IPopSessionProfile profile, SaslClientMechanism authMechanismSpecified, UpgradeConnectionStreamCallback createSslStreamCallback, PopSession& session) in D:\Smdn.Net.Pop3.Client-1.02\Smdn.Net.Pop3.Client\Smdn.Net.Pop3.Client.Session\PopSessionCreator.cs:line 97
   at Smdn.Net.Pop3.Client.Session.PopSessionCreator.CreateSession(IPopSessionProfile profile, SaslClientMechanism authMechanismSpecified, UpgradeConnectionStreamCallback createSslStreamCallback) in D:\Smdn.Net.Pop3.Client-1.02\Smdn.Net.Pop3.Client\Smdn.Net.Pop3.Client.Session\PopSessionCreator.cs:line 47
   at Smdn.Net.Pop3.Client.PopClient.ConnectCore(ConnectParams params) in D:\Smdn.Net.Pop3.Client-1.02\Smdn.Net.Pop3.Client\Smdn.Net.Pop3.Client\PopClient.cs:line 171
   at Smdn.Net.Pop3.Client.PopClient.Connect(String password) in D:\Smdn.Net.Pop3.Client-1.02\Smdn.Net.Pop3.Client\Smdn.Net.Pop3.Client\PopClient.cs:line 422
   at SeminarTools.MainForm.MainForm_Load(Object sender, EventArgs e) in D:\Smdn.Net.Pop3.Client-1.02\SeminarToolsLight\SeminarTools\MainForm.cs:line 175

尚、MRAConnectorもやってみましたが、想定した設定では予期せぬエラーになってしまいました。全組み合わせでやってみましたが接続できる組み合わせはありませんでした。

:ID:k/X6BCq+

先ほど問題を修正したSmdn.Net.Pop3 version 1.03をリリースしました。
頂いたスタックトレースのとおり、レスポンスの解析処理のバグが原因で例外がスローされていました。
>>25の件は今回の修正で直っていると思います。

MRAConnectorはSmdn.Net.Imap4の方と合わせて更新するつもりなので、今回はバージョンアップしません。
使う場合はSmdn.Net.Pop3を1.03に差し替えてください。

:ID:j/NwOewl

ありがとうございました!
1.03を反映してみたらうまくConnect通りました。
が、次の問題が・・・相手方のメールサーバーがUIDLに対応しているはずなのですが

if (!client.ServerCapabilities.Contains(PopCapability.Uidl))
{
Console.WriteLine("UIDL incapable");
}

で試すと非対応という判定となります。チェックをスキップして

foreach (var message in client.GetMessages(true))
{
var uidl = message.UniqueId;
}

で取ってみると取得できました。

ためしにBecky2のプロトコルログを取るとサーバーとの通信で

UIDL
+OK
1 123456780.1234.s77,S=1234
2 ・・・

のようにUIDLを取得出来ている事が確認できました。

:ID:j/NwOewl

ちなみに>>32の件も今回のMTAだけで発生しています。
coreserver.jpのメールサーバーで、MTAはqmailのようです。

:ID:k/X6BCq+

>>32-33
ServerCapabilitiesにはCAPAコマンドの結果が入りますが、サーバ側がCAPAをサポートしていない場合
(CAPAコマンドに対して-ERRとなった場合)は、ServerCapabilitiesが空になります。
そのため、仮にサーバ側がUIDLをサポートしていても、CAPAをサポートしていなければ
ServerCapabilities.Contains(PopCapability.Uidl)はfalseを返します。

なので、ServerCapabilities.Contains(PopCapability.Uidl)が

  • trueの場合は、確実にUIDLをサポートしている
  • falseの場合は、もしかしたらUIDLをサポートしているかもしれない

と判断したほうがいいです。

お使いのqmailでは単にCAPAをサポートしていないだけと思われるので、UIDLをサポートしていることが
明らかなのであれば、チェックをスキップしてしまっても構わないと思います。

:ID:k/X6BCq+

>>34続き
ちなみに、CAPAをサポートしていないサーバに対してUIDLをサポートしていないこと
確実に判断するには、実際にコマンドを送信してみないとわかりません。

次のようにすれば、UIDLをサポートしているかどうかを確実に判断できると思います。

if (client.MessageCount == 0)
  // メッセージが無い場合はUIDLコマンドを送信できないので中断
  return;

bool isUidlCapable = client.ServerCapabilities.Contains(PopCapability.Uidl);

// UIDLをサポートしていることが分かっている場合(trueの場合)は、ここでunique-idも取得する
// サポートしていないかもしれない場合(falseの場合)は、ここではunique-idを取得しない
PopMessageInfo first = client.GetFirstMessage(isUidlCapable);

try {
  // unique-idを表示
  // (GetFirstMessage()でunique-idを取得しなかった場合は、ここでUIDLコマンドが発行される)
  Console.WriteLine("UID = {0}", first.UniqueId);

  // 既にunique-idを取得済みか、UIDLコマンドを発行しても-ERRとならなかった場合は、
  // UIDLをサポートしていると判断する
  isUidlCapable = true;
}
catch (PopErrorResponseException) {
  // UIDLコマンドを発行した結果、サーバが-ERRを返した場合は
  // UIDLをサポートしていないと判断する
  isUidlCapable = false;
}

現在のPopMessageInfo.UniqueIdが例外をスローする実装は使いにくそうなので、
今後のバージョンではunique-idを取得できなかった場合はnullを返すように
変更するかもしれません。

また、上記のとおりサンプルコードのところで記載しているやり方はあまり確実ではないので、
これも近々書き直します。

:ID:j/NwOewl

ご説明ありがとうございます!今回はスキップして処理しました。
返答が丁寧で且つ早いので非常に感謝しています。

お騒がせしました。

:ID:k/X6BCq+

先ほどSmdn.Net.Pop3 version 1.10をリリースしました。
PopMessageInfo.UniqueIdの動作を>>35で書いたように変更したのでご注意ください。
詳しくはhttp://smdn.jp/works/libs/Smdn.Net.Pop3.Client/releases/をご覧ください。

:ID:3jxBJ9xC

素晴らしいサイトの運営、敬服いたします。
imaps でgmail の受信は順調に作動しています。
とても感謝しています。
popで、ロリポップや、IIS のメールの受信を試みています。

ロリポップで、
var client = new PopClient("pop3.lolipop.jp", 110, false, "user");
もしくは、
var client = new PopClient(new Uri(@"pop://userd@pop3.lolipop.jp/"));
client.Profile.AllowInsecureLogin = true;
client.Connect("pass");

で実験しているのですが、
以下のようなエラーが出てしまいます。

ファイルまたはアセンブリ 'Smdn, Version=0.40.0.0, Culture=neutral, PublicKeyToken=null'、
またはその依存関係の 1 つが読み込めませんでした。指定されたファイルが見つかりません。

勉強不足のは重々承知していますが、時間がないため、指定の誤りをご指摘いただけたら幸いです。
よろしくお願いします。

:ID:k/X6BCq+

ありがとうございます。 動作報告も非常に助かります。

件のエラーについてですが、プロジェクトの参照にSmdn-netfxX.Y.csprojを追加するか、
実行ファイルと同じ場所にSmdn.dllを置くことで解決すると思います。
プロジェクトの参照設定をご確認ください。

:ID:3jxBJ9xC

お忙しい中、御指示いただきまして、感謝しております。
Smdn.dll
Smdn.Core.Standards.dll
の二つを参照設定して置くのを忘れていました。
恥ずかしいかぎりです。
ロリポップも、IISも、無事作動いたしました。
ありがとうございました。
これからも、ご活躍を祈っております。

:ID:qdbB813W

有用なライブラリをありがとうございます。
Smdn.Net.Pop3.Clientについて、CoreServerに登録してある独自ドメインのメールを使用したいのですが、力量不足で接続できません。
アドバイスをいただけると幸いです。

現状では http://www.coreserver.jp/help/index.php/sharedssl/ を参考にして、

var client = new PopClient("s00-coreserver-jp.value-domain.com", 995, true, "user@domain");
client.Connect("pass");

として試しているのですが、connect failedになってしまいます。
サーバのメール設定ではPOPのアクセスは許可してあります。

知識不足ゆえの手詰まりで恥ずかしいのですが、ご指摘いただければ幸いです。

:ID:F+K0inzL

上記の件について、自己解決いたしました。
CoreServerでも使用できたことを報告いたします。
お騒がせしたことをお詫び申し上げます。

ソースコードは変更せず、プロキシ設定周りを変更したところ受信することができました。
基本的な部分について確認を怠っていてお恥ずかしい限りです。

:ID:qdbB813W

補足。
プロキシを使用しないネットワークでは受信できましたが、プロキシ使用環境下でこのライブラリを使用することは可能ですか?
IEプロキシですべてのプロトコルに使用する設定にしても接続できませんでした。

お手数ですがアドバイスいただけると助かります。

:ID:sisSL6UE

ありがとうございます。

プロキシを通した接続については、現時点では考慮していません。
ただ、テストはしていませんがプロキシを通したとしても動作するのではないかと思います。

connect failedになったのは、プロキシを通さず直接サーバに接続しようとしたからではないでしょうか?
プロキシを通すなら、コンストラクタに渡すホスト名とポート番号を、

const string popProxyHost = "localhost";
const int popProxyPort = 10995;

var client = new PopClient(popProxyHost, popProxyPort, true, "user");

のようにプロキシサーバのものに変えれば接続できるかもしれません。
それ以上はスローされた例外の詳細と動作ログを見てみないと原因は分かりません。

IEでのプロキシ設定についてですが、Smdn.Net.Pop3.Clientの動作には影響しないのではないかと思います。
この点についてはこちらでも調べてみます。

:ID:qdbB813W

お忙しい中返答いただきありがとうございました。
コンストラクタにプロキシのホスト名とポート番号を渡してみましたが、この場合はPOPのホスト名とポートを指定することはできない…ですよね。
教えていただいた方法では実現することができませんでした。

学内プロキシを越えてCoreServerにPOPでアクセスできれば理想なのですが…もう少し悩んでみます。

なるほど、IEのプロキシは影響しないのですね。
お手数をおかけしますが、もし有効な手段がありましたらご教示お願いいたします。
どうもありがとうございました。

:ID:sisSL6UE

トンネリングを想定しているのでしたら、本ライブラリにはその様な機能は無く、実装予定もありません。
学内ネットワークのセキュリティポリシーと照らし合わせながら、この辺りを参考にされるとよいかと思います。

http://tdtds.jp/tunnel/
http://ja.wikipedia.org/wiki/トンネリング

:ID:3jxBJ9xC

一度、お恥ずかしい質問で、ご迷惑をおかけした者です。
とても素晴らしいお仕事に、ただただ頭が下がる思いです。
その後のご報告です。
IMAP POP3 で、
ロリポップ、HETEML, Gmail, YahooMail, IIS
など、順調に動作しています。
ありがとうございます。
今後とも、よろしくお願いします。

:ID:sisSL6UE

問題なく動作しているようで何よりです。
動作報告もありがとうございます。

:ID:2yXqw/JC

不具合報告です。

MimeMessage.Load(string, EncodingSelectionCallback)メソッドが指定したコールバック関数を使ってくれません。

ソースを確認したところ MimeMessage.Load(Stream) メソッドが呼ばれていました。

報告まで。

:ID:sisSL6UE

ご報告ありがとうございます。

修正はコミットしたので次のバージョンでは修正されますがリリースがだいぶ先になりそうなので、
お手数をお掛けしてすみませんが、リリースまでは最新版をチェックアウトするかソースの以下の箇所を直接修正してください。

Index: Smdn.Formats.Mime/MimeMessage.cs
===================================================================
--- Smdn.Formats.Mime/MimeMessage.cs	(リビジョン 4369)
+++ Smdn.Formats.Mime/MimeMessage.cs	(作業コピー)
@@ -137,7 +137,7 @@
                                    EncodingSelectionCallback selectFallbackCharset)
     {
       using (var stream = File.OpenRead(file)) {
-        return Load(stream);
+        return Load(stream, selectFallbackCharset);
       }
     }
:ID:2yXqw/JC

対応有難う御座います。
とりあえず直接Load(Stream, EncodingSelectionCallback)メソッドを呼び出すように変更して対応しました。

:ID:zOQrVqB8

Smdn.Formats.Mime ライブラリを利用させて頂いております。

 Smdn.Formats.Mime-0.22 において EncodingSelectionCallback を利用してエンコーディングの
フォールバックを行っているのですが、ヘッダにおいてフォールバックが呼び出されない現象が
発生しています。

例外cp932 is an unsupported or invalid charset
   場所 Smdn.Formats.Mime.MimeEncoding.<>c__DisplayClass3.<Decode>b__1(Match m)
   場所 System.Text.RegularExpressions.RegexReplacement.Replace(MatchEvaluator evaluator, Regex regex, String input, Int32 count, Int32 startat)
   場所 System.Text.RegularExpressions.Regex.Replace(String input, MatchEvaluator evaluator, Int32 count, Int32 startat)
   場所 System.Text.RegularExpressions.Regex.Replace(String input, MatchEvaluator evaluator)
   場所 Smdn.Formats.Mime.MimeEncoding.Decode(String str, MimeEncodingMethod& encoding, Encoding& charset)
   場所 Smdn.Formats.Mime.Decode.Decoder.Decode(MimeHeaderCollection headers)
   場所 Smdn.Formats.Mime.Decode.Decoder.Decode(MimeMessage message)
   場所 Smdn.Formats.Mime.MimeMessage.Load(Stream stream, EncodingSelectionCallback selectFallbackCharset)

MimeMessage.Load ではコンテントのエンコーディングが解釈できない場合に EncodingSelectionCallback
を利用する様になっているのですが、Decode配下のヘッダ解釈に selectFallbackCharset が受け渡されて
いない様に見えます。

以下の修正にてヘッダでの B/Q エンコーディングのcharsetの解釈に selectFallbackCharset が利用され
る様になるのですが、よろしければ反映頂けないでしょうか。

Smdn.Formats.Mime.Decode クラス Decode( MimeMessage ) に EncodingSelectionCallback を受け入れる
オーバーライドを追加、同 Decode( MimeHeaderCollection ) にも追加

= internal static class Decoder {

=    internal static MimeMessage Decode(MimeMessage message)
=    {
+        return Decode(message, null);
+    }

+    internal static MimeMessage Decode(MimeMessage message,EncodingSelectionCallback encodingSelectionCallback)
+    {

-      var decodedHeaders = Decode(message.Headers);

+      var decodedHeaders = Decode(message.Headers,encodingSelectionCallback);

=      var decodedMessage = new MimeMessage(decodedHeaders, message.Content, message.SubParts.ConvertAll<MimeMessage>(Decode));

(snip)
=    private static MimeHeaderCollection Decode(MimeHeaderCollection headers)
=    {
+        return Decode(headers, null);
+    }
+    private static MimeHeaderCollection Decode(MimeHeaderCollection headers, EncodingSelectionCallback encodingSelectionCallback)

+    {

=      var decodedHeaders = new MimeHeaderCollection();

=
      foreach (var header in headers) {

-        decodedHeaders.Add(new MimeHeader(header.Name, MimeEncoding.Decode(header.Value)));

+        decodedHeaders.Add(new MimeHeader(header.Name, MimeEncoding.Decode(header.Value,encodingSelectionCallback)));

=      }

Smdn.Formats.Mime.MimeEncoding クラス Decode( string ) に EncodingSelectionCallback を受け取る
オーバーロードを追加、 同 Decode( string, out MimeEncoding, out Encoding ) にも追加

=    public static string Decode(string str)
=    {
+        return Decode(str, null);
+    }

+    public static string Decode(string str,EncodingSelectionCallback encodingSelectionCallback)

+    {

=      MimeEncodingMethod discard1;

=      Encoding discard2;
=
-      return Decode(str, out discard1, out discard2);

+      return Decode(str, encodingSelectionCallback, out discard1, out discard2);

=    }
(snip)

+    public static string Decode(string str, out MimeEncodingMethod encoding, out Encoding charset)
+    {
+        if( str==null )
+            throw new ArgumentNullException("str");
+        return Decode(str, null, out encoding, out charset);
+    }
+
+    public static string Decode(string str, EncodingSelectionCallback selectFallbackEncoding, out MimeEncodingMethod encoding, out Encoding charset)
    {
=    {
=      if (str == null)

=        throw new ArgumentNullException("str");

=
      charset = null;
=
      encoding = MimeEncodingMethod.None;

=
      Encoding lastCharset = null;

=      var lastEncoding = MimeEncodingMethod.None;

=
      lock (mimeEncodedWordRegex) {

=        var ret = mimeEncodedWordRegex.Replace(str, delegate(Match m) {

=          // charset

-          lastCharset = EncodingUtils.GetEncoding(m.Groups[1].Value);

+          lastCharset = EncodingUtils.GetEncoding(m.Groups[1].Value,selectFallbackEncoding);

Smdn.Formats.Mime.MimeMessage クラス Load( Stream, EncodingSelectionCallback ) において
Decoder.Decode の呼び出し時に EncodingSelectionCallback を引き渡す様にする。

=    public static MimeMessage Load(Stream stream,

=                                   EncodingSelectionCallback selectFallbackCharset)

=    {

=      var message = Decode.Parser.Parse(stream, selectFallbackCharset);


=#if false

=      // MIME-Version

=      if (message.Headers.Contains("MIME-Version") && message.Headers["MIME-Version"].Value.Trim() != "1.0")

=        throw new NotSupportedException("unsupported MIME version");

=#endif

=

-      return Decode.Decoder.Decode(message);

+      return Decode.Decoder.Decode(message,selectFallbackCharset);

=    }

(iPod のメーラーがSubjectにcp932のquated printableを投げてくるという酷い実装で
 びっくりしましたです。)

:ID:0r8C5mnA

>>52
ご要望・ご提案ありがとうございます。 早速ですが反映したバージョンSmdn.Formats.Mime 0.23をリリースいたしました。
もともとselectFallbackCharsetはContent-Typeの解釈(ボディ部のデコード用)のみに使用するものでしたが、
このバージョンでは、>>23と同じコードでB/Qエンコードされたヘッダのデコード時にもselectFallbackCharsetを呼び出すようにしました。
書き込んでいただいた内容をほとんどそのまま使用させて頂きました。 ありがとうございました。

なお、本バージョンには>>50の修正も含めて有ります。

また、本バージョンではMimeEncoding.Decodeメソッドで文字コードが見つからない場合にスローされる例外が
FormatExceptionからEncodingNotSupportedExceptionに変更になりましたので、直接使用されている場合はご注意ください。

:ID:B2WsISsu

>>53
素早い対応ありがとうございます。
修正リリースに反映されている事を確認させて頂きました。

:ID:vooMVcoB

こんにちは。初めまして。
未だに basp を使ってまして、そろそろネイティブ化したいなというところで貴ライブラリを発見致しました。

で早速の質問で恐縮ですが、new Smdn.Net.Pop3.Client.PopClient で uri を引き渡すかと思いますが、UserName もしくは Password の中に「@」が含まれている場合、System.UriBuilder がエラーになってしまい、Smdn.Net.Pop3.Client.PopClient に引き渡せません。

対象の POP3 アカウントが「xxx@yyy.com」の「xxx」のみでなく、フルに書かないといけない場合とか。
「@」を含まない場合は正常に動作するのですが、何か回避策はあるのでしょうか。

:ID:KDQyA187

>>55
こんにちは。 ご報告ありがとうございます。

現時点ではUserNameもしくはPasswordに「@」を含むURLをPopClientに渡すと例外エラーとなります。
この不具合については修正しますので、すみませんが次のリリースまでお待ちください。

なお、UriBuilderでは次のようにパーセントエンコードすれば「@」を含む文字列を設定することができます。

UriBuilder b = new UriBuilder();

b.Host = "yyy.com";
b.UserName = "xxx%40yyy.com"; // "xxx@yyy.com"
b.Password = "p%40ssword"; // "p@ssword"

もうひとつ念のために書き添えておくと、PopClientはURLにパスワードが含まれていてもパスワードとしては使用しません。
PopClient.Connectメソッドの引数で渡された文字列のみをパスワードとして扱います。
PopClient.Connectメソッドの方は、パスワードに「@」が含まれていてもパーセントエンコード等のエスケープを行う必要はありません。

:ID:KDQyA187

>>55
先ほどUserNameに「@」を含むURLを設定できない不具合を修正した
Smdn.Net.Pop3 version 1.14をリリースしました。

これで問題なく動作するようになりますので、どうぞご利用ください。

:ID:5Qqrcjdq

こんにちは。初めまして。
このライブラリーをありがたく使わせて頂いているものです。

私のWebサイトでは、GmailにImap経由でアクセスする機能があるのですが、ちゃんとWebサーバーにホスティングするとどうやらXOAUTH2の認証が必要になるようです。
SASLの仕組みを拡張してきっとできるはずと思い、色々、試行錯誤してみたのですが、行き詰まってしまいました。
何かアドバイス頂けますでしょうか?

https://developers.google.com/gmail/xoauth2_protocol

:ID:HMbHT7/s

>>58
こんにちは。 ご利用頂きましてありがとうございます。

OAuth認証を行う件についてですが、まず当方はOAuthについての知識があまりありませんので、
以下不十分な回答となるかもしれない点ご容赦ください。

早速ですが、現状Smdn.Net.Imap4.Clientが使用しているSASLの実装Smdn.Security.Authentication.Saslには
OAuth認証の実装は含まれていないので、まずSASLメカニズムに沿ってOAuth認証を行うクラスを作成します。

https://developers.google.com/gmail/xoauth2_protocolを読む限りでは、次のような実装でよさそうです。

using System;
using Smdn;
using Smdn.Security.Authentication.Sasl;
using Smdn.Security.Authentication.Sasl.Client;

// SASL XOAUTH2のクライアント側実装
[SaslMechanism("XOAUTH2", false)]
public class XOAuth2Mechanism : SaslClientMechanism {
  public override bool ClientFirst {
    // XOAUTH2のSASLハンドシェイクはクライアント側から
    get { return true; }
  }

  protected override SaslExchangeStatus Exchange(ByteString serverChallenge, out ByteString clientResponse)
  {
    // https://developers.google.com/gmail/xoauth2_protocol
    // The SASL XOAUTH2 Mechanism
    const string user = "someuser@example.com";
    const string accessToken = "vF9dft4qmTc2Nvb3RlckBhdHRhdmlzdGEuY29tCg==";

    // AUTHENTICATEコマンドに乗せるinitial client responseを組み立てる
    var builder = new ByteStringBuilder();

    builder.Append("user=");
    builder.Append(user);
    builder.Append("\u0001"); // ^A
    builder.Append("auth=Bearer ");
    builder.Append(accessToken);
    builder.Append("\u0001\u0001"); // ^A^A

    // Base64エンコードを行う前のinitial client responseをoutパラメータに設定する
    // (Smdn.Net.Imap4.ClientがAUTHENTICATEコマンドを送出する時点でBase64エンコードを行うため、
    // ここでBase64エンコードしてはならない)
    clientResponse = builder.ToByteString(false);

    return SaslExchangeStatus.Succeeded;
  }
}

Smdn.Net.Imap4.Clientは、IMAP AUTHENTICATEコマンドを送出する際にExchangeメソッドを呼び出し、
clientResponseに設定された内容をBase64でエンコードした上でサーバー側に送出します。

あとは、ImapClient.Connectメソッドを呼び出す際の引数に、上記のXOAuth2Mechanismのインスタンスを渡します。
これにより、IMAP AUTHENTICATEコマンドを送出する際にこのクラスが使われるようになります。

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

public class GimapOAuthSample {
  public static void Main(string[] args)
  {
    using (var oauthMechanism = new XOAuth2Mechanism()) {
      using (var client = new ImapClient("host", 993)) {
        client.Connect(oauthMechanism);

        Console.WriteLine(client.IsConnected);
      }
    }
  }
}

これで https://developers.google.com/gmail/xoauth2_protocol で示されているような動作となるはずです。

なお既存のSASL関連の実装はSmdn.Security.Authentication.Sasl/Smdn.Security.Authentication.Sasl.Clientフォルダにある各クラス、
AUTHENTICATEコマンドの処理はSmdn.Net.Imap4.Client/Smdn.Net.Imap4.Client.Transaction.BuiltIn/AuthenticateTransaction.csに
含まれています。 認証時の動作をトレースする場合はこれらのファイルをご覧ください。

:ID:5Qqrcjdq

おお・・・
早速ありがとうございます。
試してみて結果をご報告差し上げます!

:ID:5Qqrcjdq

早速試してみました。

XOAuth2Mechanismを渡した後のclient.Connect の後で exception が発生しました。
エラーメッセージは、"internal error"

下記のログを元にデバッグポイントを貼った所、下記の exchange already succeeded の exception が発生しているようでした。何かアドバイス頂けますでしょうか?必要であればこちらから可能な限り情報はお出しします。

   protected void CheckExchangeStatus()
   {
     switch (exchangeStatus) {
       case SaslExchangeStatus.Succeeded: throw new InvalidOperationException("exchange already succeeded");
       case SaslExchangeStatus.Failed: throw new InvalidOperationException("exchange already failed");
       default: return;
     }
   }

ここからログ

  場所 Smdn.Security.Authentication.Sasl.Client.SaslClientMechanism.CheckExchangeStatus() C:\Source\Smdn.Net.Imap4.Client-0.80\Smdn.Security.Authentication.Sasl\Smdn.Security.Authentication.Sasl.Client\SaslClientMechanism.cs:行 217
  場所 Smdn.Security.Authentication.Sasl.Client.SaslClientMechanism.Exchange(Byte[] serverChallenge, Byte[]& clientResponse) 場所 C:\Source\Smdn.Net.Imap4.Client-0.80\Smdn.Security.Authentication.Sasl\Smdn.Security.Authentication.Sasl.Client\SaslClientMechanism.cs:行 195
  場所 Smdn.Net.Imap4.Client.Transaction.BuiltIn.AuthenticateTransaction.OnCommandContinuationRequestReceived(ImapCommandContinuationRequest continuationRequest) 場所 C:\Source\Smdn.Net.Imap4.Client-0.80\Smdn.Net.Imap4.Client\Smdn.Net.Imap4.Client.Transaction.BuiltIn\AuthenticateTransaction.cs:行 173
  場所 Smdn.Net.Imap4.Client.Transaction.ImapTransactionBase`1.ProcessReceiveResponse() 場所 C:\Source\Smdn.Net.Imap4.Client-0.80\Smdn.Net.Imap4.Client\Smdn.Net.Imap4.Client.Transaction\ImapTransactionBase.cs:行 192
  場所 Smdn.Net.Imap4.Client.Transaction.ImapTransactionBase`1.Smdn.Net.Imap4.Client.Transaction.IImapTransaction.Process() 場所 C:\Source\Smdn.Net.Imap4.Client-0.80\Smdn.Net.Imap4.Client\Smdn.Net.Imap4.Client.Transaction\ImapTransactionBase.cs:行 156
:ID:5Qqrcjdq

最終的にうまくいきました。

  • こちらのいらない修正
  • access token の期限切れ

など、こちらがわのミスによるものだったようです。
頂いたコードで動きました。

以下、ImapConnection.cs の SetCommand(), TryReceiveResponse() から引き抜いた Authentication の一連の流れです。
ご参考までに。

{Condition=Ok, ResponseText={Code=, Arguments=[], Text=Gimap ready for requests from 121.102.95.209 c2if1415404pbm.166}}
{CommandString=CAPABILITY, Tag=0000, Arguments=}
{Type=CAPABILITY, Data={Text:IMAP4rev1}, {Text:UNSELECT}, {Text:IDLE}, {Text:NAMESPACE}, {Text:QUOTA}, {Text:ID}, {Text:XLIST}, {Text:CHILDREN}, {Text:X-GM-EXT-1}, {Text:XYZZY}, {Text:SASL-IR}, {Text:AUTH=XOAUTH}, {Text:AUTH=XOAUTH2}}
{Tag=0000, Condition=Ok, ResponseText={Code=, Arguments=[], Text=Thats all she wrote! c2if1415404pbm.166}}
{CommandString=AUTHENTICATE, Tag=0001, Arguments=XOAUTH2, {access_token}}
{Type=CAPABILITY, Data={Text:IMAP4rev1}, {Text:UNSELECT}, {Text:IDLE}, {Text:NAMESPACE}, {Text:QUOTA}, {Text:ID}, {Text:XLIST}, {Text:CHILDREN}, {Text:X-GM-EXT-1}, {Text:UIDPLUS}, {Text:COMPRESS=DEFLATE}, {Text:ENABLE}, {Text:MOVE}, {Text:CONDSTORE}, {Text:ESEARCH}}
{Tag=0001, Condition=Ok, ResponseText={Code=, Arguments=[], Text={userid} {username} authenticated (Success)}}
{CommandString=NAMESPACE, Tag=0002, Arguments=}
{Type=NAMESPACE, Data={List:{List:{Text:}, {Text:/}}}, {NIL}, {NIL}}
{Tag=0002, Condition=Ok, ResponseText={Code=, Arguments=[], Text=Success}}
{CommandString=ID, Tag=0003, Arguments=NIL}
{Type=ID, Data={List:{Text:name}, {Text:GImap}, {Text:vendor}, {Text:Google, Inc.}, {Text:support-url}, {Text:http://support.google.com/mail}, {Text:version}, {Text:gmail_imap_130623.00_p0}, {Text:remote-host}, {Text:121.102.95.209}}}
{Tag=0003, Condition=Ok, ResponseText={Code=, Arguments=[], Text=Success}}

:ID:HMbHT7/s

>>61 >>62

結果をご報告頂きありがとうございます。
動作結果を見る限り問題なく認証に成功しているようでなによりです。

以下は参考情報です。

internal errorが発生した理由は、XOAuth2Mechanismが、認証に失敗した際に返されるエラーメッセージを
予期しないレスポンスとして扱うようになっていたことが原因です。

XOAuth2Mechanismを次のように作り変えることにより、OAuth認証に失敗した場合には通常の認証失敗時と同様に
ImapAuthenticationExceptionをスローするようにできます。 また、必要に応じてserverChallengeを見ることにより、
Gmailサーバーが返送してくるJSON形式のエラーメッセージを読むことができます。

(最初からこのような実装にしておけば問題点の発見が容易だったと思います。 お手間を取らせてしまいすみません。)

// SASL XOAUTH2のクライアント側実装
[SaslMechanism("XOAUTH2", false)]
public class XOAuth2Mechanism : SaslClientMechanism {
  private bool initialResponseSent;

  public override bool ClientFirst {
    // XOAUTH2のSASLハンドシェイクはクライアント側から
    get { return true; }
  }

  public override void Initialize()
  {
    base.Initialize();

    initialResponseSent = false;
  }

  protected override SaslExchangeStatus Exchange(ByteString serverChallenge, out ByteString clientResponse)
  {
    const string user = "someuser@example.com";
    const string accessToken = "vF9dft4qmTc2Nvb3RlckBhdHRhdmlzdGEuY29tCg==";

    // https://developers.google.com/gmail/xoauth2_protocol
    // The SASL XOAUTH2 Mechanism
    if (initialResponseSent) {
      // initial client responseを生成した後にExchangeが呼び出された場合
      // (error responseが返ってきた場合)

      // serverChallengeにはサーバー側から返されてきたメッセージが格納されている
      Console.WriteLine(serverChallenge);

      clientResponse = null;

      // これ以上認証手続きを継続出来ないので、「認証手続きは失敗」とする
      return SaslExchangeStatus.Failed;
    }
    else {
      // AUTHENTICATEコマンドに乗せるinitial client responseを組み立てる
      var builder = new ByteStringBuilder();

      builder.Append("user=");
      builder.Append(user);
      builder.Append("\u0001"); // ^A
      builder.Append("auth=Bearer ");
      builder.Append(accessToken);
      builder.Append("\u0001\u0001"); // ^A^A

      // Base64エンコードを行う前のinitial client responseをoutパラメータに設定する
      // (Smdn.Net.Imap4.ClientがAUTHENTICATEコマンドを送出する時点でBase64エンコードを行うため、
      // ここでBase64エンコードしてはならない)
      clientResponse = builder.ToByteString(false);

      initialResponseSent = true;

      // initial client responseに対してエラーメッセージが返送される場合があるので、
      // 「認証手続きは継続中」とする
      return SaslExchangeStatus.Continuing;
    }
  }
}
:ID:5Qqrcjdq

さらなる改良コードありがとうございます。
早速試してみます。
Gmailのaccess tokenは通常1時間程度で期限が切れてしまい、認証に失敗した場合、取り直しが必要になるようなので、エラーメッセージが出るのはとても助かります!

:ID:HMbHT7/s

今後のバージョンでXOAuth2Mechanismをライブラリに組み込み、ユーザー側では必要なパラメータを指定して
インスタンスを作成するだけで済むようにしたいと考えています。

今のところ以下のような使い方ができるようにしようと考えていますが、もしよろしければ
どのような使い方をしたいか、実際に使用する上で不便な点などありましたらご意見をいただけると幸いです。

using (var client = new ImapClient("host", 993)) {
  // ユーザー名とaccess tokenをコンストラクタ(もしくはプロパティ)で指定できるようにする
  string oauthUser = "...";
  string oauthAccessToken = "...";

  using (var oauthMechanism = new XOAuth2Mechanism(oauthUser, oauthAccessToken)) {
    try {
      client.Connect(oauthMechanism);
    }
    catch (ImapAuthenticationException) {
      // 認証エラーとなった場合に返送されたエラーレスポンスをプロパティで参照できるようにする
      Console.WriteLine(oauthMechanism.LastErrorResponse);

      // access tokenが期限切れだったら取得しなおしたaccess tokenを設定して再度認証できるようにする
      oauthMechanism.AccessToken = TokenRequest(...);

      oauthMechanism.Initialize();

      try {
        client.Connect(oauthMechanism);
      }
      catch (...) {
        ...
      }
    }
  }

  Console.WriteLine(client.IsConnected);
}
:ID:5Qqrcjdq

お返事遅れました。

頂いたコード試してみました。

Authentication Command に対して、
{CommandString=AUTHENTICATE, Tag=0001, Arguments=XOAUTH2, {Credential}}

下記のレスポンス、
{"status":"400","schemes":"Bearer","scope":"https://mail.google.com/"}

以下、トレースログ

{CommandString=, Tag=, Arguments=*}
{CommandString=, Tag=, Arguments=*}
{Tag=0001, Condition=No, ResponseText={Code=ALERT, Arguments=[], Text=Invalid credentials (Failure)}}
{Tag=0001, Condition=No, ResponseText={Code=ALERT, Arguments=[], Text=Invalid credentials (Failure)}}
'Smdn.Net.Imap4.ImapAuthenticationException' の初回例外が Smdn.Net.Imap4.Client.DLL で発生しました。
'Smdn.Net.Imap4.ImapAuthenticationException' の初回例外が Smdn.Net.Imap4.Client.DLL で発生しました。

となり Invalid credential である所まで特定できるようになりました。

このライブラリにOAuth2が対応されるのは素晴らしいと思います。
GmailのOauth2であれば、expires_in をチェックして期限が切れていれば、access_tokenの取り直しといったフローがよさそうです。
(下記がaccess_token取得時に得られるパラメータ一覧です。)

{

 "access_token":"1/fFAGRNJru1FTz70BzhT3Zg",
 "expires_in":3920,
 "token_type":"Bearer"

}

従いまして、コンストラクタには、DateTimeの expires_in も入っているとなお取り回しがよさそうです。
(オプショナル扱いでもよいかもしれませんが・・・)
Gmailのexpires_inは、秒単位で来ますので、取得時に現在時刻に足して保存しておくと言った形で私は実装しました。

:ID:iVV7CnvI

>>66
XOAuth2Mechanismに関するご意見ありがとうございました。
先ほどXOAUTH2の実装を含んだSmdn.Net.Imap4.Client version 0.90をリリースしました。

使用方法については http://smdn.jp/works/libs/Smdn.Net.Imap4.Client/docs/#xoauth2
実装は http://svn.smdn.jp/anonsvn/libs/Smdn/trunk/Smdn.Security.Authentication.Sasl/Smdn.Security.Authentication.Sasl.Client/XOAuth2Mechanism.cs
その他このバージョンでの変更点については http://smdn.jp/works/libs/Smdn.Net.Imap4.Client/releases/#changes_v0.90 をご覧ください。

ご要望頂いたexpires_inについてですが、結論から言うとexpires_inはコンストラクタには含めず、
先に提示させていただいた例のままとさせて頂きました。 理由として、

  1. expires_inを絶対時刻(DateTime)ではなく相対時刻(TimeSpan)のまま扱いたい場合も考えられ、利便性を考えるとライブラリ側ではどちらか一方に決定できない
  2. expires_inをintのまま扱うとしても、.NET Frameworkではintの相対時刻はミリ秒単位と扱われる箇所が多い(Thread.Sleep, WebRequest.Timeout等)ため、単位が不一致となり一貫性が失われる
  3. access_tokenの有効期限に関して、expires_inをチェックして認証前にaccess_tokenを更新する方法と、expires_inのチェックは行わずに認証を試行しエラーが出たらaccess_tokenを更新する方法の二つが取りうるが、後者の場合ではexpires_inはそもそも不要となる

の三点からXOAuth2Mechanismではexpires_inは扱わず、userとaccess_tokenのみを扱うようにさせて頂きました。

expires_inをクラスに持たせたりaccess_tokenの有効期限を事前にチェックする場合は、XOAuth2Mechanismを継承したクラスを作成して頂き、
Initializeメソッドをオーバーライドして必要な処理を記述するようにしていただければと思います。
詳しくはドキュメント http://smdn.jp/works/libs/Smdn.Net.Imap4.Client/docs/#xoauth2 をご覧ください。

この他、例外処理関連で少し大きめの動作変更を加えているので、もし何かご不明な点が出てきましたらお聞きください。

:ID:cjgvHHaS

初めまして。ライブラリー使わせて頂いているものです。
タイムアウトの動作について質問させてください
タイムアウトの設定(*)を300秒にしているのですが、なぜか21秒ぐらいでタイムアウト例外が発生してしまいます。タイムアウトの発生方法はケーブル切断によるものです。
(*)以下のプロパティを全て300秒に設定
 Client.Profile.Timeout
 Client.Profile.SendTimeout
 Client.Profile.ReceiveTimeout

なお、20秒以下の設定は正しく反映されて動作します。例えば5秒に設定すれば、5秒でタイムアウト例外が発生します。
なぜ、300秒に到達せずタイムアウト例外が発生するのでしょうか?上記のプロパティ以外にタイムアウトの動作に影響を与えるようなプロパティがある等、考えられる理由があれば教えてもらえないでしょうか?

:ID:iVV7CnvI

>>68
ご利用頂きましてありがとうございます。

ケーブル切断でタイムアウト設定値に到達する前に例外となる原因についてははっきりしません。
スローされたTimeoutExceptionのMessageプロパティやInnerExceptionプロパティ、例外が発生した際のスタックトレースが
あれば何か状況がわかるかもしれません。
もしくはSocketクラスがそのような動作となっているのかもしれません。

タイムアウトのプロパティについてですが、Client.Profile.Send/ReceiveTimeoutに設定された値は
接続時にのみ使用され、以降のコマンド送受信時にはClient.Send/ReceiveTimeoutプロパティに設定された値を使用します。
(Client.Send/ReceiveTimeoutへの値の設定は常にClient.Profile.Send/ReceiveTimeoutに反映されます)

Client.Send/ReceiveTimeoutに設定した値は、そのままSystem.Net.Sockets.SocketクラスのSend/ReceiveTimeoutプロパティに設定されます。
そのため、SocketクラスがSend/ReceiveTimeoutの設定値未満でSocketError.TimedOutの例外をスローするような
ことがあれば、そのような事象が起こりえます。

Client.Timeoutは、コマンドの送信とレスポンスの受信を行うメソッドを非同期で呼び出した際の終了待ちに使用しています。
そのためClient.TimeoutにClient.SendTimeoutより小さい値を指定すれば、送信でのタイムアウトが起きる前に
TimeoutExceptionをスローする場合があります。

これ以外にはタイムアウト動作に影響するプロパティはありません。
引き続きこちらでも調べてみますが、ケーブル切断を行える環境をすぐに用意できないので調査に時間がかかりそうです。

:ID:cjgvHHaS

68のものです。
スタックトレース取得しました。(なお、以下のトレースはケーブル切断によって発生した例外ではなく、誤ったアドレス(POP3サーバではないマシン)に対してPOP3アクセスした場合のトレースです。こちらも同様に21秒ぐらいでタイムアウトが発生します。)

    • ここから --
      [Smdn.Net.Pop3.Protocol.PopConnectionException] {"connect failed"}

StackTrace:
場所 Smdn.Net.ConnectionBase.Connect(String host, Int32 port, Int32 millisecondsTimeout, UpgradeConnectionStreamCallback createAuthenticatedStreamCallback)
場所 Smdn.Net.Pop3.Protocol.Client.PopConnection..ctor(String host, Int32 port, Int32 millisecondsTimeout, UpgradeConnectionStreamCallback createAuthenticatedStreamCallback)
場所 Smdn.Net.Pop3.Client.Session.PopSession.Connect(String host, Int32 port, Int32 connectTimeout, Int32 sendTimeout, Int32 receiveTimeout, UpgradeConnectionStreamCallback createAuthenticatedStreamCallback)
場所 Smdn.Net.Pop3.Client.Session.PopSession..ctor(String host, Int32 port, Int32 transactionTimeout, Int32 sendTimeout, Int32 receiveTimeout, UpgradeConnectionStreamCallback createAuthenticatedStreamCallback)
場所 Smdn.Net.Pop3.Client.Session.PopSessionCreator.CreateSession(IPopSessionProfile profile, SaslClientMechanism authMechanismSpecified, UpgradeConnectionStreamCallback createSslStreamCallback, PopSession& session)
場所 Smdn.Net.Pop3.Client.Session.PopSessionCreator.CreateSession(IPopSessionProfile profile, SaslClientMechanism authMechanismSpecified, UpgradeConnectionStreamCallback createSslStreamCallback)
場所 Smdn.Net.Pop3.Client.PopClient.ConnectCore(ConnectParams params)
場所 Smdn.Net.Pop3.Client.PopClient.Connect(String password)
場所 jp.co.XXX.CheckMailThrd.ReceiveAndAdmit(MailAutoAdmitTask task, Boolean SendConnFailedMail) 場所 D:\\Work\\Develop\\src\\CheckMailThrd.cs:行 319"

InnerException
{"接続済みの呼び出し先が一定の時間を過ぎても正しく応答しなかったため、接続できませんでした。または接続済みのホストが応答しなかったため、確立された接続は失敗しました。 192.168.168.182:110"} System.Exception {System.Net.Sockets.SocketException}

InnerException StackTrace:
Server stack trace:
場所 System.Net.Sockets.Socket.Connect(IPAddress[] addresses, Int32 port)
場所 System.Net.Sockets.Socket.Connect(String host, Int32 port)
場所 System.Runtime.Remoting.Messaging.StackBuilderSink._PrivateProcessMessage(IntPtr md, Object[] args, Object server, Int32 methodPtr, Boolean fExecuteInContext, Object[]& outArgs)
場所 System.Runtime.Remoting.Messaging.StackBuilderSink.AsyncProcessMessage(IMessage msg, IMessageSink replySink)

Exception rethrown at [0]:
場所 System.Runtime.Remoting.Proxies.RealProxy.EndInvokeHelper(Message reqMsg, Boolean bProxyCase)
場所 System.Runtime.Remoting.Proxies.RemotingProxy.Invoke(Object NotUsed, MessageData& msgData)
場所 System.Action`2.EndInvoke(IAsyncResult result)
場所 Smdn.Net.ConnectionBase.Connector.Connect(String host, Int32 port, Int32 millisecondsTimeout)
場所 Smdn.Net.ConnectionBase.Connect(String host, Int32 port, Int32 millisecondsTimeout, UpgradeConnectionStreamCallback createAuthenticatedStreamCallback)"

    • ここまで --
    • 別のトレース --

場所 Smdn.Net.Pop3.Client.Session.PopSession.PostProcessTransaction(IPopTransaction t)\r\n
場所 Smdn.Net.Pop3.Client.Session.PopSession.EndProcessTransaction(IAsyncResult asyncResult)\r\n
場所 Smdn.Net.Pop3.Client.Session.PopSession.ProcessTransaction(IPopTransaction t)\r\n
場所 Smdn.Net.Pop3.Client.Session.PopSession.Connect(String host, Int32 port, Int32 connectTimeout, Int32 sendTimeout, Int32 receiveTimeout, UpgradeConnectionStreamCallback createAuthenticatedStreamCallback)\r\n
場所 Smdn.Net.Pop3.Client.Session.PopSession..ctor(String host, Int32 port, Int32 transactionTimeout, Int32 sendTimeout, Int32 receiveTimeout, UpgradeConnectionStreamCallback createAuthenticatedStreamCallback)\r\n
場所 Smdn.Net.Pop3.Client.Session.PopSessionCreator.CreateSession(IPopSessionProfile profile, SaslClientMechanism authMechanismSpecified, UpgradeConnectionStreamCallback createSslStreamCallback, PopSession& session)\r\n
場所 Smdn.Net.Pop3.Client.Session.PopSessionCreator.CreateSession(IPopSessionProfile profile, SaslClientMechanism authMechanismSpecified, UpgradeConnectionStreamCallback createSslStreamCallback)\r\n
場所 Smdn.Net.Pop3.Client.PopClient.ConnectCore(ConnectParams params)\r\n 場所 Smdn.Net.Pop3.Client.PopClient.Connect(String password)\r\n
場所 jp.co.XXX.CheckMailThrd.ReceiveAndAdmit(MailAutoAdmitTask task, Boolean SendConnFailedMail)
場所 D:\\Work\\Develop\\src\\CheckMailThrd.cs:行 322"

    • ここまで --
    • ソース --
       using (PopClient Client = new PopClient(task.MailPop3Server, task.MailPop3Port, false, task.MailPop3UserName))
       {
          int retry = task.MailPop3Retries;
       POP3_RETRY:
          try
          {
              Client.Profile.AllowInsecureLogin = true;
              Client.Profile.UseTlsIfAvailable = false;
              //Timeoutを設定
              Client.Profile.Timeout = task.MailPop3Timeout * 1000;
              Client.Profile.SendTimeout = task.MailPop3Timeout * 1000;
              Client.Profile.ReceiveTimeout = task.MailPop3Timeout * 1000;
              Client.Connect(task.MailPop3Password);
          }
          catch
          {
              //例外処理(リトライ処理含め)
              retry--;
              if (retry >= 0)
              {
                   goto POP3_RETRY;
              }
          }
       }
       ※taskオブジェクトの中には、設定画面で適用したPOP3サーバやリトライ回数の設定が入ります。
         task.MailPop3Timeoutは300が入っています。
    • ここまで --

なお、上記のソースをデバッガで確認したところ、例外が発生したときのClient.Send/ReceiveTimeout、Client.Profile.Send/ReceiveTimeoutは全て300秒になっていました。

上記の情報で原因を特定できないでしょうか?

:ID:cjgvHHaS

68のものです。

申し訳ありません。自己解決しました。タイムアウトの概念を誤解していました。
Send/ReceiveTimeoutはTCPコネクションが確立されてから用いられるものなので、110ポートが空いていないサーバーへのPOP3アクセスが300秒経過せず切断されるのは当然ですね。
試しに110ポートをListenするだけのテストプログラムを作成し、テストプログラムが動作するサーバに対して監視したところ、正しく300秒でタイムアウトが発生しました。

お手数をおかけして申し訳ありませんでした。

:ID:iVV7CnvI

>>70
>>71
スタックトレースありがとうございした。 また、すでに解決したようで何よりです。

質問を頂いた時点で「タイムアウト時間を満了する前にTimeoutExceptionがスローされた」と勘違いしていました。
そのため当を得ない回答となってしまったようです。 すみません。

以下蛇足ではありますが、21秒でTimeoutException以外の例外で接続に失敗する動作について調べた結果と、
接続時に300秒まで待ち合わせる方法について書き記しておきます。

Socket.Connectメソッドは、接続先が応答しない場合などでは最大21秒でSocketExceptionをスローするという
動作となっています。
一方、Socket.Connectメソッド自体にはタイムアウト時間を指定する引数がないので、21秒でSocketExceptionを
スローするという動作は変更することができません。

このため、ライブラリは接続開始から21秒後に発生したSocketExceptionをInnerExceptionに格納し、
PopConnectionExceptionをスローします(頂いたスタックトレースにあるとおりの動作)。

上記のようなSockect.Connectメソッドの動作により、本ライブラリでは、

  1. Timeoutプロパティの設定値を満了した場合はTimeoutException
  2. (Timeoutプロパティの設定値に関わらず)21秒が経過した場合はPopConnectionException

をスローするのが接続先無応答時の動作ということになります。

接続時に最大21秒でタイムアウトという動作を変更したい場合はレジストリの値(TcpMaxConnectRetransmissions)を
変更する必要があるようです。

従って、接続時に300秒まで待ち合わせるようにしたいのであれば、上記のレジストリの値を変更する必要があります。

:ID:iVV7CnvI

補足です。

ConnectメソッドはSendTimeout/ReceiveTimeoutプロパティの値を参照しません。
Timeoutプロパティに設定されている値でタイムアウトします。

:ID:SAW/u3DW

68のものです。

なぜ21秒でコネクションタイムアウトが発生するか理由が分からず、もやもやしていましたが、レジストリの値によって時間が決まっていることが分かり、疑問が解消しました。
丁寧な解説ありがとうございました。

:ID:DlQhl0ap

初歩的な質問となるのですが、POP3.Client(1.15.0.0)を利用させて頂いております。

自社のPOPサーバより取得するバッチを作ろうとしていて、一点御伺いさせて下さい。

MarkAsDeleted()メソッドでIsMarkedAsDeleted=trueに一旦なるのですが、
その後再取得させた際にはIsMarkedAsDeleted=falseとなります。

これは何に由来するなどはお分かりになりますでしょうか。

コードはこの様な記述です。

using System;
using System.Collections.Generic;
using System.Text;
using Smdn.Net.Pop3.Client;

public bool SaveMailList(string saveDir,string host,string user,string pass)
{
    try
    {
        bool result=false;
        using( client = new PopClient(host, -1, false, null, "*");
        client.Profile.UseTlsIfAvailable = false;
        client.Profile.UsingSaslMechanisms = new string[] { "DIGEST-MD5", "CRAM-MD5", "+APOP" };
        client.Profile.AllowInsecureLogin = true;
        client.Connect(new System.Net.NetworkCredential(user, pass));
        foreach (PopMessageInfo message in client.GetMessages(true))
        {
            if (!message.IsMarkedAsDeleted)
            {
                string id = message.UniqueId;
                string savePath = System.IO.Path.Combine(saveDir, id + ".eml");
                if (!System.IO.File.Exists(savePath))
                {
                    message.Save(savePath);
                    message.MarkAsDeleted();
                }
            }
        }
        client.Disconnect();
        result = true;
        return result;
    }
    catch (Exception ex)
    {
        throw new Exception(ex.ToString());
    }
    return result;
}
:ID:aen+5xOZ

>>75
ご利用頂きましてありがとうございます。

MarkAsDeleted()メソッドによって付けられる削除マークは、永続的に保持されるフラグではなく、
ログインしている間のみ保持されるフラグです。
そのため、MarkAsDeleted()メソッドで削除マークを付けた場合でも、実際に削除を行わないまま
切断すればフラグの状態は元に戻り、次回ログイン時に再取得すると削除マークが付けられていない状態、
つまりIsMarkedAsDeleted=falseとなります。

また、削除マークを付けたメッセージは、QUITコマンドを発行しない限り実際には削除されません。

Disconnect()メソッドでは、QUITコマンドを発行せずに切断します。 そのため、
削除マークが付けられているメッセージは削除されずサーバーに残ったままとなり、
次回ログイン時にも再取得されることとなります。

これが「再取得するとIsMarkedAsDeleted=falseとなる理由」になります。

一方、Logout()メソッドを使えば、QUITコマンドを発行した後に切断します。
この場合、削除マークを付けられているメッセージは切断前にサーバー上から削除され、
次回ログイン時には再取得されなくなります。

従って、コード中で呼び出しているDisconnect()メソッドをLogout()メソッドに書き換えれば、
「サーバー上のすべてのメッセージを取得・保存したのち、サーバー上から削除する」という動作にする
ことができます。

:ID:l+qaMhBM

Smdn.Net.Imap4.Clientライブラリを利用させていただき、IMAPメーラーを作成しておりますが、
以下の事象が発生しており開発が難航しております。
対処法等、ございましたらご教授いただけますと幸いです。

【発生事象】
SMDNライブラリを使用して社内のIMAPサーバへアクセスした際、
StartIdle()でIDLE開始でCPUが高負荷状態となる。
この時以下のエラーが出力され続けます。

【エラー内容】

型 'System.Net.Sockets.SocketException' の初回例外が System.dll で発生しました
型 'System.IO.IOException' の初回例外が System.dll で発生しました

【環境】
OS:Windows7/8
開発環境:VS2012 .NET 3.5
※ .NET 4.5 と .NET 3.5 をインストール済み
WPFアプリケーションにて作成

<参照ライブラリ/バージョン>
Smdn.Core.Standards.dll 0.40.0.0
Smdn.dll 0.40.0.0
Smdn.Net.Imap4.Client.dll 0.90.0.0(一部改変あり。下記【備考】参照)
Smdn.Net.Imap4.dll 0.90.0.0
Smdn.Net.MessageAccessProtocols.dll 0.90.0.0
Smdn.Security.Authentication.Sasl.dll 0.33.0.0

【テストアプリ実装機能】
①待機開始ボタン押下することでStartIdle()を呼び出しIDLE開始
②RecentMessageCountChangedイベントを補足してメッセージ表示

【発生事象】
①テストアプリを実行して「待機開始」ボタンを押下し、IMAP IDLEを開始すると一定時間後(2分以内)にCPUが高負荷状態となる

【テストアプリ(コードビハインド)】

using Smdn.Net.Imap4.Client;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Mail;
using System.Threading;
using System.Windows;
using System.Windows.Documents;

namespace IMAP_MailTest
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        List<ImapClient> _client = new List<ImapClient>();
        List<ImapOpenedMailboxInfo> _mailBox = new List<ImapOpenedMailboxInfo>();

        // 待機開始ボタン
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var client = new ImapClient(new Uri("imaps://username@imap.hostname.com/"));
            client.Profile.UseTlsIfAvailable = true;
            client.RecentMessageCountChanged += recentMessageCountChanged;

            // 接続
            client.Connect(new NetworkCredential("username", "password"));

            // メールボックスリスト追加
            var openMailBox = client.GetMailbox("inbox").Open();
            openMailBox.StartIdle(Timeout.Infinite);

            _client.Add(client);
            _mailBox.Add(openMailBox);

            MessageBox.Show("IDLE開始");
        }

        // 待機終了ボタン
        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            foreach(var mailBox in _mailBox)
            {
                mailBox.StopIdle();
                mailBox.Close();
            }
            _mailBox.Clear();

            foreach(var client in _client)
            {
                client.Logout();
            }
            _client.Clear();

            MessageBox.Show("IDLE終了");
        }

        // 画面表示ボタン
        private void Button_Click_2(object sender, RoutedEventArgs e)
        {
            (new Window1()).Show();
        }

        // RecentMessageCountChanged
        private void recentMessageCountChanged(object sender, ImapMailboxSizeChangedEventArgs e)
        {
            MessageBox.Show("新着メールあり");
        }
    }
}

【備考】
社内サーバーと通信ができないためライブラリを改修
ImapResponseReceiver.cs、67行目から抜粋
修正個所は // add startと // add end で囲っています。

    private readonly static ByteString continueReqMark = ByteString.CreateImmutable("+ ");

    // add start
    private readonly static ByteString continueReqMarkOnlyCRLF = ByteString.CreateImmutable("+\r\n");
    private readonly static ByteString continueReqMarkOnlyLF = ByteString.CreateImmutable("+\n");
    // add end

    private readonly static ByteString respCondMark = ByteString.CreateImmutable("* [");

    private ImapResponse ParseResponce(ByteString line, ref IParsingContext parsingContext)
    {
      // response        = *(continue-req / response-data) response-done

      // continue-req    = "+" SP (resp-text / base64) CRLF
      if (line.StartsWith(continueReqMark))
        return new ImapCommandContinuationRequest(line.ToString(2, line.Length - 4)); // remove leading "+" SP and trailing CRLF

      // add start
      if (line.Equals(continueReqMarkOnlyCRLF) || line.Equals(continueReqMarkOnlyLF))
        return new ImapCommandContinuationRequest("");
      // add end

      // greeting        = "*" SP (resp-cond-auth / resp-cond-bye) CRLF
      // response-done   = response-tagged / response-fatal
      // response-data   = "*" SP (resp-cond-state / resp-cond-bye /
      //                   mailbox-data / message-data / capability-data) CRLF
      // response-fatal  = "*" SP resp-cond-bye CRLF
      //                     ; Server closes connection immediately
      // response-tagged = tag SP resp-cond-state CRLF
:ID:DdfZu2HU

>>77
ご利用頂きましてありがとうございます。

こちらではStartIdle()で高負荷状態になる状況は再現できなかったのですが、
SSL接続中にIDLEを行った場合に意図していないデータを受信する問題が見つかりました。
高負荷状態になる問題はこれに関連するものと思われます。

この問題の調査と修正には時間が掛かりそうなので、ご不便をお掛けしますが
以下どちらかの方法をとりあえずの対処として頂ければと思います。

  1. (セキュリティ上問題なければ)SSLを使わずにサーバーへ接続する
  2. StartIdle()/IDLEコマンドは使わず、以下のようにImapOpenedMailboxInfo.Refresh()メソッド/NOOPコマンドを使用して新着メールをチェックする
        List<ImapClient> _client = new List<ImapClient>();
        List<ImapOpenedMailboxInfo> _mailBox = new List<ImapOpenedMailboxInfo>();
        System.Windows.Threading.DispatcherTimer _pollingTimer = new System.Windows.Threading.DispatcherTimer();

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var client = new ImapClient(new Uri("imaps://username@imap.hostname.com/"));
            client.Profile.UseTlsIfAvailable = true;
            client.RecentMessageCountChanged += recentMessageCountChanged;

            // 接続
            client.Connect(new NetworkCredential("username", "password"));

            // メールボックスリスト追加
            var openMailBox = client.GetMailbox("inbox").Open();

            _client.Add(client);
            _mailBox.Add(openMailBox);

            // ポーリング用のタイマーを設定
            _pollingTimer.Interval = TimeSpan.FromSeconds(30); // 30秒に一度各メールボックスの状態を更新
            _pollingTimer.Tick += refreshMailBox;
            _pollingTimer.Start();

            MessageBox.Show("チェック開始");
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            _pollingTimer.Stop();

            foreach (var mailBox in _mailBox)
            {
                mailBox.Close();
            }
            _mailBox.Clear();

            foreach (var client in _client)
            {
                client.Logout();
            }
            _client.Clear();

            MessageBox.Show("チェック終了");
        }

        private void refreshMailBox(object sender, EventArgs e)
        {
            foreach (var mailBox in _mailBox)
            {
                // NOOPコマンドを発行してメールボックスの状態を更新
                // (新着メールがあればRecentMessageCountChangedイベントが発生する)
                mailBox.Refresh();
            }
        }

        // RecentMessageCountChanged
        private void recentMessageCountChanged(object sender, ImapMailboxSizeChangedEventArgs e)
        {
            MessageBox.Show("新着メールあり");
        }

また、調査の参考として、以下の情報について差し支えない範囲で構いませんのでご提供いただけると助かります。

  1. お使いのIMAPサーバーの種類
  2. ImapResponseReceiver.csに修正が必要となった理由 (あるいは、IDLE中にどのようなレスポンスが返送されてきたか)
  3. 高負荷状態になった際の動作ログと送受信内容 (http://smdn.jp/works/libs/Smdn.Net.Imap4.Client/docs/#logging)

なお、IOExceptionとSocketExceptionについてですが、IDLE中にサーバーからのレスポンスが無い場合には
数十秒に一度程度発生します。 このエラーが出ていることに関しては(現時点では)意図した通りの動作です。

:ID:DdfZu2HU

>>77
調査したところ、現在こちらで把握している問題は以下の修正
(IDLE中のConnection.ReceiveTimeoutをTimeout.Infiniteにする)
を施すことにより対処できることがわかりました。

高負荷状態になる問題もおそらくこの修正で解決すると思われます。
恐れ入りますが以下の修正を施した上で再度アプリのテストをしていただければと思います。

Index: Smdn.Net.Imap4.Client/Smdn.Net.Imap4.Client.Transaction.BuiltIn/IdleTransaction.cs
===================================================================
--- Smdn.Net.Imap4.Client/Smdn.Net.Imap4.Client.Transaction.BuiltIn/IdleTransaction.cs	(リビジョン 4917)
+++ Smdn.Net.Imap4.Client/Smdn.Net.Imap4.Client.Transaction.BuiltIn/IdleTransaction.cs	(作業コピー)
@@ -87,9 +87,9 @@
     protected override void OnCommandContinuationRequestReceived(ImapCommandContinuationRequest continuationRequest)
     {
       prevReceiveTimeout = Connection.ReceiveTimeout;
 
-      Connection.ReceiveTimeout = 30 * 1000; // 30secs
+      Connection.ReceiveTimeout = Timeout.Infinite; // no timeout
       Connection.SetIsIdling(true);
 
       idleStateChangedEvent.Set();
     }

こちらでも引き続き検証を続けますが、この対処で問題なさそうなら
この修正を含めたバージョンをリリースしようと思います。

:ID:0abad20y

77のものです。
早々のご調査大変ありがとうございます。
79の修正については現在確認中です。追ってご連絡をさせていただきます。
77でご質問いただきました参考情報につきましては、
2.ImapResponseReceiver.csに修正が必要となった理由 (あるいは、IDLE中にどのようなレスポンスが返送されてきたか)
のみの回答となります。申し訳ありません。
IDLEコマンド発行後のレスポンスが"+"のみで後に文字列が続かないため修正が必要となりました。

+
:ID:0abad20y

>>77 のものです。
>>78 でお問い合わせいただいた
3.高負荷状態になった際の動作ログと送受信内容
のログが取得できました。
以下に掲載します。(掲示板の最大文字数に引っかかるため繰り返されるエラー部分は(中略)としています。)
管理人様で把握されている問題とログの内容が一致しているかご確認いただけないでしょうか?

'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\WINDOWS\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll' が読み込まれ、シンボルの読み込みがスキップされました。モジュールは最適化されていて、デバッグ オプションの [マイ コードのみ] 設定が有効になっています。
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\WINDOWS\assembly\GAC_MSIL\Microsoft.VisualStudio.HostingProcess.Utilities\11.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.HostingProcess.Utilities.dll' が読み込まれ、シンボルの読み込みがスキップされました。モジュールは最適化されていて、デバッグ オプションの [マイ コードのみ] 設定が有効になっています。
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\WINDOWS\assembly\GAC_MSIL\System.Windows.Forms\2.0.0.0__b77a5c561934e089\System.Windows.Forms.dll' が読み込まれ、シンボルの読み込みがスキップされました。モジュールは最適化されていて、デバッグ オプションの [マイ コードのみ] 設定が有効になっています。
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\WINDOWS\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll' が読み込まれ、シンボルの読み込みがスキップされました。モジュールは最適化されていて、デバッグ オプションの [マイ コードのみ] 設定が有効になっています。
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\WINDOWS\assembly\GAC_MSIL\System.Drawing\2.0.0.0__b03f5f7f11d50a3a\System.Drawing.dll' が読み込まれ、シンボルの読み込みがスキップされました。モジュールは最適化されていて、デバッグ オプションの [マイ コードのみ] 設定が有効になっています。
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\WINDOWS\assembly\GAC_MSIL\Microsoft.VisualStudio.HostingProcess.Utilities.Sync\11.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.HostingProcess.Utilities.Sync.dll' が読み込まれ、シンボルの読み込みがスキップされました。モジュールは最適化されていて、デバッグ オプションの [マイ コードのみ] 設定が有効になっています。
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\WINDOWS\assembly\GAC_MSIL\Microsoft.VisualStudio.Debugger.Runtime\11.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.Debugger.Runtime.dll' が読み込まれ、シンボルの読み込みがスキップされました。モジュールは最適化されていて、デバッグ オプションの [マイ コードのみ] 設定が有効になっています。
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\Users\XXXXXXXXX\Desktop\IMAP_MailTest(調査_ログあり)\IMAP_MailTest\bin\Debug\IMAP_MailTest.vshost.exe' が読み込まれ、シンボルの読み込みがスキップされました。モジュールは最適化されていて、デバッグ オプションの [マイ コードのみ] 設定が有効になっています。
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\WINDOWS\assembly\GAC_MSIL\mscorlib.resources\2.0.0.0_ja_b77a5c561934e089\mscorlib.resources.dll' が読み込まれ、シンボルの読み込みがスキップされました。モジュールは最適化されていて、デバッグ オプションの [マイ コードのみ] 設定が有効になっています。
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\WINDOWS\assembly\GAC_64\System.Data\2.0.0.0__b77a5c561934e089\System.Data.dll' が読み込まれ、シンボルの読み込みがスキップされました。モジュールは最適化されていて、デバッグ オプションの [マイ コードのみ] 設定が有効になっています。
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\WINDOWS\assembly\GAC_64\System.Web\2.0.0.0__b03f5f7f11d50a3a\System.Web.dll' が読み込まれ、シンボルの読み込みがスキップされました。モジュールは最適化されていて、デバッグ オプションの [マイ コードのみ] 設定が有効になっています。
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\WINDOWS\assembly\GAC_MSIL\System.Xml\2.0.0.0__b77a5c561934e089\System.Xml.dll' が読み込まれ、シンボルの読み込みがスキップされました。モジュールは最適化されていて、デバッグ オプションの [マイ コードのみ] 設定が有効になっています。
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\WINDOWS\assembly\GAC_MSIL\System.Core\3.5.0.0__b77a5c561934e089\System.Core.dll' が読み込まれ、シンボルの読み込みがスキップされました。モジュールは最適化されていて、デバッグ オプションの [マイ コードのみ] 設定が有効になっています。
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\WINDOWS\assembly\GAC_MSIL\System.Xml.Linq\3.5.0.0__b77a5c561934e089\System.Xml.Linq.dll' が読み込まれ、シンボルの読み込みがスキップされました。モジュールは最適化されていて、デバッグ オプションの [マイ コードのみ] 設定が有効になっています。
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\WINDOWS\assembly\GAC_MSIL\System.Data.DataSetExtensions\3.5.0.0__b77a5c561934e089\System.Data.DataSetExtensions.dll' が読み込まれ、シンボルの読み込みがスキップされました。モジュールは最適化されていて、デバッグ オプションの [マイ コードのみ] 設定が有効になっています。
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\WINDOWS\assembly\GAC_MSIL\WindowsBase\3.0.0.0__31bf3856ad364e35\WindowsBase.dll' が読み込まれ、シンボルの読み込みがスキップされました。モジュールは最適化されていて、デバッグ オプションの [マイ コードのみ] 設定が有効になっています。
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\WINDOWS\assembly\GAC_64\PresentationCore\3.0.0.0__31bf3856ad364e35\PresentationCore.dll' が読み込まれ、シンボルの読み込みがスキップされました。モジュールは最適化されていて、デバッグ オプションの [マイ コードのみ] 設定が有効になっています。
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\WINDOWS\assembly\GAC_MSIL\PresentationFramework\3.0.0.0__31bf3856ad364e35\PresentationFramework.dll' が読み込まれ、シンボルの読み込みがスキップされました。モジュールは最適化されていて、デバッグ オプションの [マイ コードのみ] 設定が有効になっています。
スレッド 'vshost.NotifyLoad' (0x1504) はコード 0 (0x0) で終了しました。
スレッド 'vshost.LoadReference' (0x1f28) はコード 0 (0x0) で終了しました。
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\Users\XXXXXXXXX\Desktop\IMAP_MailTest(調査_ログあり)\IMAP_MailTest\bin\Debug\IMAP_MailTest.exe が読み込まれました。シンボルが読み込まれました。
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\WINDOWS\assembly\GAC_MSIL\Accessibility\2.0.0.0__b03f5f7f11d50a3a\Accessibility.dll' が読み込まれました
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\WINDOWS\assembly\GAC_MSIL\System.Configuration\2.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll' が読み込まれました
ステップ イン: シンボルのないメソッド 'IMAP_MailTest.App.App' をステップ オーバーしています
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\Users\XXXXXXXXX\Desktop\IMAP_MailTest(調査_ログあり)\IMAP_MailTest\bin\Debug\Smdn.Net.Imap4.Client.dll' が読み込まれました
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\Users\XXXXXXXXX\Desktop\IMAP_MailTest(調査_ログあり)\IMAP_MailTest\bin\Debug\Smdn.Net.Imap4.dll' が読み込まれました
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\WINDOWS\assembly\GAC_MSIL\PresentationFramework.Aero\3.0.0.0__31bf3856ad364e35\PresentationFramework.Aero.dll' が読み込まれました
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\WINDOWS\assembly\GAC_MSIL\UIAutomationProvider\3.0.0.0__31bf3856ad364e35\UIAutomationProvider.dll' が読み込まれました
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\WINDOWS\assembly\GAC_MSIL\UIAutomationTypes\3.0.0.0__31bf3856ad364e35\UIAutomationTypes.dll' が読み込まれました
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\Users\XXXXXXXXX\Desktop\IMAP_MailTest(調査_ログあり)\IMAP_MailTest\bin\Debug\Smdn.Net.MessageAccessProtocols.dll' が読み込まれました
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\Users\XXXXXXXXX\Desktop\IMAP_MailTest(調査_ログあり)\IMAP_MailTest\bin\Debug\Smdn.dll' が読み込まれました
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\Users\XXXXXXXXX\Desktop\IMAP_MailTest(調査_ログあり)\IMAP_MailTest\bin\Debug\Smdn.Security.Authentication.Sasl.dll' が読み込まれました
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\Users\XXXXXXXXX\Desktop\IMAP_MailTest(調査_ログあり)\IMAP_MailTest\bin\Debug\Smdn.Core.Standards.dll' が読み込まれました
'IMAP_MailTest.vshost.exe' (マネージ (v2.0.50727)): 'C:\WINDOWS\assembly\GAC_MSIL\System.resources\2.0.0.0_ja_b77a5c561934e089\System.resources.dll' が読み込まれました
型 'System.InvalidOperationException' の初回例外が System.dll で発生しました
Smdn.Net.Imap4.Client Information: 10 : 2014-03-10T16:16:13.1439048+09:00 CID:- connecting
Smdn.Net.Imap4.Client Information: 10 : 2014-03-10T16:16:13.3590555+09:00 CID:1 connected
Smdn.Net.Imap4.Client Verbose: 10 : 2014-03-10T16:16:13.3820462+09:00 CID:1 Greeting: 
Smdn.Net.Imap4.Client Verbose: 10 : 2014-03-10T16:16:13.3850485+09:00 CID:1 Greeting: waiting for server greeting response
Smdn.Net.Imap4.Client Verbose: 10 : 2014-03-10T16:16:13.4310826+09:00 CID:1 Greeting: Ok "."
Smdn.Net.Imap4.Client Information: 10 : 2014-03-10T16:16:13.4361052+09:00 CID:1 authority: imaps://imap.XXXXXX.com:993/
Smdn.Net.Imap4.Client Information: 10 : 2014-03-10T16:16:13.4361052+09:00 CID:1 now in non-authenticated state
Smdn.Net.Imap4.Client Verbose: 10 : 2014-03-10T16:16:13.4391075+09:00 CID:1 Capability: 
Smdn.Net.Imap4.Client Verbose: 10 : 2014-03-10T16:16:13.5170274+09:00 CID:1 Capability: Ok "."
Smdn.Net.Imap4.Client Verbose: 10 : 2014-03-10T16:16:13.5390426+09:00 CID:1 Login: 
Smdn.Net.Imap4.Client Verbose: 10 : 2014-03-10T16:16:13.5914835+09:00 CID:1 Login: Ok "."
Smdn.Net.Imap4.Client Information: 10 : 2014-03-10T16:16:13.5914835+09:00 CID:1 authority: imaps://XXXXXXXXXXX@imap.XXXXXX.com:993/
Smdn.Net.Imap4.Client Information: 10 : 2014-03-10T16:16:13.5914835+09:00 CID:1 now in authenticated state
Smdn.Net.Imap4.Client Information: 10 : 2014-03-10T16:16:13.5914835+09:00 CID:1 logged in
Smdn.Net.Imap4.Client Verbose: 10 : 2014-03-10T16:16:13.5924840+09:00 CID:1 Capability: 
Smdn.Net.Imap4.Client Verbose: 10 : 2014-03-10T16:16:13.6278086+09:00 CID:1 Capability: Ok "."
Smdn.Net.Imap4.Client Verbose: 10 : 2014-03-10T16:16:13.6328432+09:00 CID:1 Namespace: 
Smdn.Net.Imap4.Client Verbose: 10 : 2014-03-10T16:16:13.7003133+09:00 CID:1 Namespace: Ok "."
Smdn.Net.Imap4.Client Verbose: 10 : 2014-03-10T16:16:13.7293533+09:00 CID:1 List: 'reference name'=>''; 'mailbox name'=>'inbox'; 
Smdn.Net.Imap4.Client Verbose: 10 : 2014-03-10T16:16:13.7913828+09:00 CID:1 List: Ok "."
Smdn.Net.Imap4.Client Verbose: 10 : 2014-03-10T16:16:13.8053709+09:00 CID:1 Select: 'mailbox name'=>'INBOX'; 
Smdn.Net.Imap4.Client Verbose: 10 : 2014-03-10T16:16:13.8807065+09:00 CID:1 Select: Ok "."
Smdn.Net.Imap4.Client Information: 10 : 2014-03-10T16:16:13.8817070+09:00 CID:1 now in selected state (selected 'INBOX')
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:13.9013705+09:00 CID:1 Idle: 
Smdn.Net.Imap4.Client Information: 14 : 2014-03-10T16:16:13.9013705+09:00 CID:1 idling
型 'System.Net.Sockets.SocketException' の初回例外が System.dll で発生しました
型 'System.IO.IOException' の初回例外が System.dll で発生しました
型 'System.IO.IOException' の初回例外が System.dll で発生しました
型 'System.Net.Sockets.SocketException' の初回例外が System.dll で発生しました
型 'System.IO.IOException' の初回例外が System.dll で発生しました
型 'System.IO.IOException' の初回例外が System.dll で発生しました
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:43.9567593+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:43.9785546+09:00 idling
型 'System.Net.Sockets.SocketException' の初回例外が System.dll で発生しました
型 'System.IO.IOException' の初回例外が System.dll で発生しました
(中略)
型 'System.IO.IOException' の初回例外が System.dll で発生しました
型 'System.IO.IOException' の初回例外が System.dll で発生しました
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:43.9981600+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.0201851+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.0341986+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.0492055+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.0592126+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.0672188+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.0732226+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.0802282+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.0862325+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.0922359+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.0982397+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.1052463+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.1112916+09:00 idling
Smdn.Net型 'System.Net.Sockets.SocketException' の初回例外が System.dll で発生しました
型 'System.IO.IOException' の初回例外が System.dll で発生しました
型 'System.IO.IOException' の初回例外が System.dll で発生しました
型 'System.Net.Sockets.SocketException' の初回例外が System.dll で発生しました
(中略)
型 'System.IO.IOException' の初回例外が System.dll で発生しました
型 'System.IO.IOException' の初回例外が System.dll で発生しました
.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.1172968+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.2012803+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.2206827+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.2376973+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.2530482+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.2651505+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.2761554+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.2881662+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.2971719+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.3086611+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.3156689+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.3216723+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.3276770+09:00 idling
Smdn.Net.Imap4.C型 'System.Net.Sockets.SocketException' の初回例外が System.dll で発生しました
型 'System.IO.IOException' の初回例外が System.dll で発生しました
型 'System.IO.IOException' の初回例外が System.dll で発生しました
型 'System.Net.Sockets.SocketException' の初回例外が System.dll で発生しました
型 'System.IO.IOException' の初回例外が System.dll で発生しました
型 'System.IO.IOException' の初回例外が System.dll で発生しました
型 'System.Net.Sockets.SocketException' の初回例外が System.dll で発生しました
型 'System.IO.IOException' の初回例外が System.dll で発生しました
(中略)
型 'System.IO.IOException' の初回例外が System.dll で発生しました
型 'System.IO.IOException' の初回例外が System.dll で発生しました
lient Verbose: 14 : 2014-03-10T16:16:44.3331485+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.4007748+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.4164747+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.4294842+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.4414896+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.4514998+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.4605034+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.4705105+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.4795167+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.4885237+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.4985322+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.5085402+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.5217304+09:00 idling
Smdn.Net.Imap4.Client Ve型 'System.Net.Sockets.SocketException' の初回例外が System.dll で発生しました
型 'System.IO.IOException' の初回例外が System.dll で発生しました
型 'System.IO.IOException' の初回例外が System.dll で発生しました
型 'System.Net.Sockets.SocketException' の初回例外が System.dll で発生しました
(中略)
型 'System.IO.IOException' の初回例外が System.dll で発生しました
型 'System.IO.IOException' の初回例外が System.dll で発生しました
rbose: 14 : 2014-03-10T16:16:44.5328599+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.5911515+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.5991809+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.6082491+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.6162552+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.6272628+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.6376403+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.6466492+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.6556567+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.6636633+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.6726677+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.6806734+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.6876795+09:00 idling
Smdn.Net.Imap4.Client Verbose: 1型 'System.Net.Sockets.SocketException' の初回例外が System.dll で発生しました
型 'System.IO.IOException' の初回例外が System.dll で発生しました
型 'System.IO.IOException' の初回例外が System.dll で発生しました
型 'System.Net.Sockets.SocketException' の初回例外が System.dll で発生しました
(中略)
型 'System.IO.IOException' の初回例外が System.dll で発生しました
型 'System.Net.Sockets.SocketException' の初回例外が System.dll で発生しました
型 'System.IO.IOException' の初回例外が System.dll で発生しました
型 'System.IO.IOException' の初回例外が System.dll で発生しました
4 : 2014-03-10T16:16:44.6956848+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.8022623+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.8214514+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.8406888+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.8591956+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.8741819+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.8891768+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.8991785+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.9091870+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.9191928+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.9271980+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.9342028+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014-03-10T16:16:44.9402071+09:00 idling
Smdn.Net.Imap4.Client Verbose: 14 : 2014型 'System.Net.Sockets.SocketException' の初回例外が System.dll で発生しました
型 'System.IO.IOException' の初回例外が System.dll で発生しました
型 'System.IO.IOException' の初回例外が System.dll で発生しました
型 'System.Net.Sockets.SocketException' の初回例外が System.dll で発生しました
(中略)
型 'System.IO.IOException' の初回例外が System.dll で発生しました
:ID:DdfZu2HU

>>81
回答が遅くなりましたが、動作ログのご提供ありがとうございました。

動作ログのみで実際の送受信内容が含まれていないのではっきりしない点は多いですが、
ログ内容から推測した結果としては、>>79の対処を行えばおそらく動作する、
あるいは好転するのではないか、という感触です。

ですので、こちらとしては>>79のテスト結果を待ちたいと思います。

以下は調査した結果と不明な点の整理です。

現在までで分かっているのは、「SSL接続でのIDLE時、タイムアウトする度にサーバーから
送信されるはずのない文字を含む文字列を読み取ってしまう」ということです。
これにより、IDLEが正しく動作しなくなっていました。

そこで、Timeout.Infiniteを設定してタイムアウトさせないようにしたところ、
非SSL接続時と同様問題なく動作するようになりました。
(そもそも、このタイムアウトの設定自体が不要なものでした。 また、
なぜSSL接続時のみ発生するかという点についてははっきりしません。)

>>80の"+"のみが返される問題はおそらくこれが原因(既に読み込んだ内容を
再度読み取っている?)と思われるので、>>79の修正を適用すれば不要に
なるのではないかと思います。

一方、こちらの環境では、タイムアウト間隔として設定していた30秒毎に
例外エラーが発生していたのに対し、そちらの環境では>>81の動作ログにあるとおり
一度タイムアウトすると30秒より短い間隔で継続的に発生するという、
その違いについては理由がわかりません。

サーバーの実装によるものか、これまで分かっている以外の問題によるものか
はっきりしないため、この点でもやはり>>79の修正でどう変わるかを
見させていただきたいと思います。

:ID:DdfZu2HU

>>81
追記。

http://smdn.jp/works/libs/Smdn.Net.Imap4.Client/docs/#loggingの内容どおり
アセンブリ構成ファイルを記述したのであれば、imap.logというファイルに送受信内容が
記録されていると思います。
もし>>79の修正でも動作しない場合は、その送受信内容も提供していただけると
助かります。
(長くなる場合は、こちらの掲示板ではなく smdn@smdn.jp 宛てにメールで
送って頂いても構いません)

:ID:l+qaMhBM

管理人様
>>77 のものです。
ご調査大変ありがとうございます。
回答が遅くなり、申し訳ありません。
>>79の修正で問題は発生しなくなっております。

:ID:DdfZu2HU

>>84
先ほど>>79の修正を含めたSmdn.Net.Imap4.Client version 0.91をリリースしました。
不具合報告・ログ提供ありがとうございました。

ImapResponseReceiver.csの修正は不要になっていると思われるため
このバージョンには含んでいませんのでご了承ください。
もしまだ挙動に問題があるようでしたらどうぞお知らせください。

その他このバージョンでの変更点については http://smdn.jp/works/libs/Smdn.Net.Imap4.Client/releases/#changes_v0.91 をご覧ください。

:ID:2nLcaFIf

管理人様

現在使っているアプリでBSMTP.DLLからSmdn.Net.Pop3.Clientへの乗り換えを検討しています。
受信するメールはgoogle appでのgmailで
単純な受信は>>11の方と同じように成功したのですが、
gmailに実装されているrecentモード( http://gmail.1o4.jp/recent.html )
で以下のように接続しようとすると接続エラーが発生します。

ソース

var client = new Smdn.Net.Pop3.Client.PopClient("pop.gmail.com", 995, true, "recent:username@hoge.jp");
client.Connect("password");

エラー

Smdn.Net.Pop3.PopAuthenticationException: authentication failed <credential not found for recent@pop.gmail.com:995> (result code:RequestError)

おそらく「:」によりユーザ名が内部で正しく取得できてないと思われるのですが
どうにか回避できる手段はございませんでしょうか。

それではよろしくお願いします。

:ID:DdfZu2HU

>>86
ご利用頂きましてありがとうございます。
ご指摘頂いたとおり、現在のバージョンではユーザー名に":"を含めることができません。

ソースコードを編集して以下の修正を施すことによりユーザー名に":"を含めることが
できるようになります。
(PopUriBuilder.csでencUserの"@"を"%40"に置き換えている次の行に、
":"を"%3A"に置き換える処理を加える)

--- Smdn.Net.Pop3/Smdn.Net.Pop3/PopUriBuilder.cs	(リビジョン 5005)
+++ Smdn.Net.Pop3/Smdn.Net.Pop3/PopUriBuilder.cs	(作業コピー)
@@ -248,8 +248,9 @@
       var encUser = PercentEncoding.GetEncodedString(userName,
                                                      ToPercentEncodedTransformMode.Rfc3986Uri);
 
       encUser = encUser.Replace("@", "%40"); // XXX
+      encUser = encUser.Replace(":", "%3A");
 
       sb.Append(encUser);
 
       if (authType != null) {

recentモードが使えるよう、今後のバージョンでユーザー名に":"を含められるように
しようと思いますが、少々検討が必要なのでリリースまでにはお時間を頂くと思います。

お手数をお掛けしますが、ソースコードを直接修正することで回避していただければと思います。

:ID:2nLcaFIf

管理人様

>>86の者です。
返事が遅れましたが、
修正後ビルドし無事受信することを確認しました。
ご返答有難うございました。

:ID:hHO83Hsp

はじめまして、くじら1号です。

http://smdn.jp/works/libs/Smdn.Formats.Mime/docs/
の「メールに添付されているファイル(Mail.AttachedFiles)を一つずつ保存する例」
を真似て、emlファイルから添付ファイルを取得しようとしていますが、

foreach (AttachedFile attachedFile in mail.AttachedFiles)

の部分で、「System.MissingMethodException はハンドルされませんでした。」
の例外が発生します。

 Message="メソッドが見つかりません: '!!0 Smdn.Threading.LazyInitializer.EnsureInitialized(!!0 ByRef, System.Func`1<!!0>)'"
 Source="Smdn.Formats.Mime"
 StackTrace: 場所 Smdn.Formats.Mime.Mail.get_AttachedFiles()

対処法をご教示いただければ幸いです。
よろしくお願いします。

:ID:DdfZu2HU

>>89
ご利用いただきありがとうございます。

Smdn.dllのメソッドを呼び出そうとしてMissingMethodExceptionに
なっていると思われます。

Smdn.dllおよびSmdn.Core.Standards.dllが実行ファイルと同じ場所に
配置されているかご確認ください。
(Smdn.Formats.Mime.dllだけでは動作しません)

:ID:RSyc9AqR

>>90
早速のご回答ありがとうございました。
使いやすいライブラリを提供していただき、感謝しています。

ご教示どおりSmdn関連のライブラリをすべて、実行ファイルと同じ場所に配置し、
参照設定をし直して、リビルドなど、いろいろ試したのですが、
やはり、同じエラーが発生してしまいます。

メールサーバはMicrosoft Exchange Serverです。
当初、Smdn.Net.Imap4.Clientを使って、ImapMessageInfoの
BodyStructureから添付ファイルを取得しようとしていたのですが、
Exchange Server 2007に不具合があり、サービスパック2を当てないと、
BodyStructureが壊れるとのMicrosoftの情報がありました。
(ExpressMailでは問題なく動いています)

「System.MissingMethodException はハンドルされませんでした。」
エラーもExchange Serverの不具合と関連している可能性はあるのでしょうか?
なんらかのヒントをいただければ幸いです。

よろしくお願いいたします。

:ID:DdfZu2HU

>>91
恐らくビルド済みパッケージをご利用のことかと思いますが、Smdn.Net.Imap4.Clientと
Smdn.Formats.Mimeのバージョンの組み合わせ、または対象のフレームワーク
(.NET 2.x or 3.5...)の組み合わせが一致しないためにMissingMethodExceptionが
発生しているかもしれません。

それぞれ最新のものを使っていれば発生しないと思いますが、参考までに
使用しているパッケージのファイル名をお教えいただけますでしょうか。

どうしても動作しないようなら、http://smdn.jp/works/tools/junk/SimpleMailer/
ソースコードからビルドしてDLLを作成することで解決すると思います。

なおMissingMethodExceptionはライブラリ内部の問題で発生しているものなので
Exchange Serverの不具合とは無関係のものです。

:ID:3E/r50lU

>>92
早速のご回答ありがとうございました。

Smdn関連のライブラリについて再度確認しましたところ、
.NET Framework 3.5のものと4.0のものとが混在しておりました。
すべて、3.5に統一することによって、正常に動作するようになりました。

お手数をおかけしましたが、これで解決しました。
ありがとうございました。

:ID:DdfZu2HU

>>88
先ほどSmdn.Net.Pop3.Client version 1.20をリリースしました。
対応が遅くなり申し訳ありません。
このバージョンではユーザ名に":"などの記号を含められるようにしています。
どうぞご利用ください。

その他このバージョンでの変更点については http://smdn.jp/works/libs/Smdn.Net.Pop3.Client/releases/#changes_v1.20 をご覧ください。

>>93
解決したようで何よりです。
恐らくもはや不要かと思いますが、BODYSTRUCTUREから添付ファイルを取得する例と
解説を以下に追記していますので、よろしければご覧ください。

http://smdn.jp/works/libs/Smdn.Net.Imap4.Client/docs/#operation_examples_message_bodystructure

:ID:l9gkygEU

>>94
いえいえ、大変参考になります。ありがとうございます。
早速で申し訳ありませんが、1つ教えてください。

「BODYSTRUCTUREの参照」のサンプルコードでは、
列挙することで、マルチパートのネストに対応しているようですね。

その下方にある「メッセージに添付されているファイルを保存する例」
のサンプルコードでは、FindAll()メソッドを使っていますが、
マルチパートがネストしている場合を意識した処理は不要なのでしょうか?
(ImapMessageFetchAttributeOptions.StaticAttributesを指定すれば大丈夫とか・・・)

お時間のあるときに、ご教示いただければ幸いです。
よろしくお願いいたします。

:ID:DdfZu2HU

>>95
FindAll()メソッドはネストした構造を持つBODYSTRUCTUREの場合でも、その構造内を再帰的に走査します。

そのためFindAll()メソッドを使うにあたっては、BODYSTRUCTUREがマルチパートかどうか、
構造がネストしているかどうかを意識する必要はありません。

例えば、次のように多重にネストしている場合でも、セクション番号の順にすべてのセクションを走査します。

[] multipart/mixed
[1] text/plain
[2] message/rfc822
[2.1] multipart/mixed
[2.1.1] text/plain
[2.1.2] image/jpeg
[2.1.3] image/jpeg
[2.1.4] image/jpeg

ImapMessageFetchAttributeOptions.StaticAttributesはImapMessageInfoの取得と同時に
BODYSTRUCTUREの取得(FETCH)も行うことを指定するためのものです。
この値の指定はFindAll()メソッドの動作には影響しません。

ImapMessageFetchAttributeOptions.StaticAttributesを指定しない場合は、BODYSTRUCTUREを
取得していない状態となるため、ImapMessageInfo.BodyStructureプロパティを参照しようとした時点で
別途FETCHコマンドを送信し、BODYSTRUCTUREを取得しようとします。

一方ImapMessageFetchAttributeOptions.StaticAttributesを指定しておけば、一度のFETCHコマンドで
BODYSTRUCTUREも同時に取得させるため、コマンドの送信回数を減らすことができます。

ImapMessageFetchAttributeOptions.StaticAttributesを指定した場合と、しない場合で
ImapMessageInfo.BodyStructureプロパティの内容が異なるということはありません。

:ID:07GEbc/j

>>96
いつも明確で丁寧なご教示、ありがとうございます。
FindAll()はとても便利なメソッドですね。

早速「メッセージに添付されているファイルを保存する例」
サンプルコードに従って、C#でコーディングしたのですが、
結果は添付ファイルを認識できませんでした。

メールサーバはMicrosoft Exchange Server 2010 SP2、
Visual Studio 2008 (.NET Framework 3.5使用)ですが、
実際、下記の部分で、s.Disposition が「null」値になっているようです。

// BODYSTRUCTUREが添付ファイルのセクションかどうかを調べる
static bool IsAttachmentSection(IImapBodyStructure section){
  var s = section as IImapBodyStructureExtension;
  if (s == null) return false;
  // Content-Dispositionヘッダがあり、かつ値がattachmentの場合
  return (s.Disposition != null && s.Disposition.IsAttachment);
}

Exchange Server 2010 SP1は、Update Rollup 3を充てないと
正しいBodyStructureが返されないようです。
http://support.microsoft.com/kb/2484862

Exchange Server 2010 SP2を使っているので、
問題ないと考えていますが、アドバイスをいただければ幸いです。

よろしくお願いいたします。

:ID:DdfZu2HU

>>97
BODYSTRUCTUREにはContent-Dispositionヘッダを解析した結果が含まれる場合があり、
それを参照するためのプロパティがIImapBodyStructureExtension.Dispositionです。
メッセージパート内にContent-Dispositionヘッダがなければ、この値はnullとなります。

サンプルのIsAttachmentSection()では、コメントにもあるように「Content-Dispositionヘッダがあり、
かつ値がattachment」のパートを添付ファイルのパートと判断しています。

したがって、サンプルのIsAttachmentSection()では「Content-Dispositionヘッダが設定されて
いない添付ファイル」は認識できません。

メールクライアントによってはContent-Dispositionヘッダが無くてもContent-Typeを見て
添付ファイルと判断するものもあるようですが、このサンプルではあくまでBODYSTRUCTUREの
解説ということでそういった複雑な判断は省略しています。

Content-DispositionとContent-Typeの両方から添付ファイルを認識する方法については、
Smdn.Formats.Mime.MailクラスのIsAttachedFile()メソッド(private)で実装していますので、
以下のファイルをご参照ください。

http://svn.smdn.jp/anonsvn/libs/Smdn/trunk/Smdn.Formats.Mime/Smdn.Formats.Mime/Mail.cs

また、仮にKBで指摘されている不具合に遭遇した場合は、ImapExceptionがスローされていると思います。
(サーバーがRFCに反する不正なレスポンスを返した場合は、InnerExeptionに
ImapMalformedResponseExceptionが設定されたImapExceptionがスローされます)
ですので、Exchange Serverから送信されるBODYSTRUCTUREには問題はないと思われます。

:ID:3E/r50lU

親切で丁寧なご回答ありがとうございました。

ご教示いただいたサンプルで試してみようと思います。

取り急ぎ、お礼のみにて失礼します。

:ID:DdfZu2HU

>>99
先ほどSmdn.Net.Imap4.Client version 1.01をリリースしたのと同時に、
添付ファイル保存のサンプルを若干修正しました。

添付ファイルを保存したい場合などにファイル名が必要になってくるかと思いますが、
version 1.01からはImapBodyStructureUtils.GetContentFileName()メソッドを使うことで
添付ファイルのファイル名を取得できるようになっています。 よろしければお使いください。

その他このバージョンでの変更点については http://smdn.jp/works/libs/Smdn.Net.Imap4.Client/releases/#changes_v1.01 をご覧ください。

:ID:SAW/u3DW

管理人様

以下のWebサービスに対して、POP3通信ができない現象が発生しています。

 ・WORKSPCE EMAIL (http://email.secureserver.net/)
   POPサーバ: pop.secureserver.net

こちらの呼び出し方法・設定に誤りがあるのでしょうか?

◎ 利用バージョン

  Smdn.Net.Pop3.Client 1.14
      最新の1.20でも同様な動作です。
  

◎ ソース

using (PopClient Client = new PopClient(pop3Server, pop3Port, false, pop3UserName))
{
    try
    {
        Client.Profile.AllowInsecureLogin = true;
        Client.Profile.UseTlsIfAvailable = false;
        Client.Connect(pop3Password);
    }
    catch (Exception e)
    {
        throw e;
    }
    finally
    {
        if (Client.IsConnected)
        {
            Client.Disconnect();
        }
    }
}

◎ 詳細
実際の通信を確認すると、以下の流れになりました。

 1) クライアント→サーバ    CAPA
 2) サーバ→クライアント    -ERR authorization first
 3) クライアント→サーバ    APOP <メールアドレス> <パスワード>
 4) サーバ→クライアント    -ERR authorization failed  Check your server settings.

上記のメールアドレス、パスワードはメールクライアントからは認証できることを確認しています。

:ID:DdfZu2HU

>>101
ご利用いただきありがとうございます。

発生している問題の原因がつかめませんので、catch句でキャッチした例外の型と
例外メッセージ・スタックトレースなどの詳細をお教えいただけますでしょうか。

また、合わせて以下の2点についてご確認ください。

1) 本ライブラリでは、APOPコマンドでの認証に失敗した場合はUSERコマンドでの認証を試みます。
通信内容からは、APOPでの認証に失敗した後にUSERコマンドを送信していないように見えます。
USERコマンドが送信されているかどうか確認していただけますか?

2) 可能なら、お使いのメールクライアントで接続に成功する場合の認証方式の種類を
確認していただけますか?

:ID:cjgvHHaS

>>101
早急なレスポンスありがとうございます。
最初の2つについて回答させていただきます。認証方式については確認させてください。

1) 例外メッセージは、以下になります。

Exception内容:
[Smdn.Net.Pop3.Protocol.PopConnectionException] = {"stream closed"}
Data = {System.Collections.ListDictionaryInternal}
StackTrace = "   at Smdn.Net.Pop3.Client.Session.PopSession.PostProcessTransaction(IPopTransaction t)\r\n   at Smdn.Net.Pop3.Client.Session.PopSession.ProcessTransaction(IPopTransaction t)\r\n   at Smdn.Net.Pop3.Client.Session.PopSession.User(String username)\r\n   at Smdn.Net.Pop3.Client.Session.PopSession.Login(String username, String password)\r\n   at Smdn.Net.Pop3.Client.Session.PopSession.Login(ICredentialsByHost credentials, String username)\r\n   at Smdn.Net.Pop3.Client.Session.PopSessionCreator.AuthenticateWithAppropriateMechanism(PopSession session, Boolean allowInsecureMechanism, ICredentialsByHost credentials, String username, IEnumerable`1 usingSaslMechanisms)\r\n   at Smdn.Net.Pop3.Client.Session.PopSessionCreator.Authenticate(PopSession session, IPopSessionProfile profile)\r\n   at Smdn.Net.Pop3.Client.Session.PopSessionCreator.CreateSession(IPopSessionProfile profile, SaslClientMechanism authMechanismSpecified, UpgradeConnectionStreamCallback createSslStreamCallback, PopSession& session)\r\n   at Smdn.Net.Pop3.Client.Session.PopSessionCreator.CreateSession(IPopSessionProfile profile, SaslClientMechanism authMechanismSpecified, UpgradeConnectionStreamCallback createSslStreamCallback)\r\n   at Smdn.Net.Pop3.Client.PopClient.ConnectCore(ConnectParams params)\r\n   at Smdn.Net.Pop3.Client.PopClient.Connect(String password)\r\n   at ……Pop3ConnectTest(String pop3Server, Int32 pop3Port, String pop3UserName, String pop3Password) ……"
TargetSite = {Smdn.Net.Pop3.Protocol.Client.PopCommandResult PostProcessTransaction(Smdn.Net.Pop3.Client.Transaction.IPopTransaction)}

2) USERコマンドの送信有無
送信していました。
その後、メールサーバからの応答はありませんでした。

3)

:ID:CYE0lc3Z

管理人様

過日は、いろいろとありがとうございました。
また、お世話になります。

【事象】

http://smdn.jp/works/libs/Smdn.Net.Imap4.Client/docs/#operation_examples_message_bodystructure

上記のような方法で、メッセージ全体の構造を取得し、添付ファイルを抽出する処理を組んでおりますが、
ImapMessageInfoのBodyStructureプロパティを参照するタイミングで、例外が発生してしまいます。
(通常は問題ありませんが、特定のメッセージに依存した問題のようです)

スタックトレースは以下のとおりです。

Message:internal error
Type   :Smdn.Net.Imap4.ImapException
StackTrace
at Smdn.Net.Imap4.Client.Session.ImapSession.PostProcessTransaction(IImapTransaction t)
at Smdn.Net.Imap4.Client.Session.ImapSession.FetchInternal[TmessageAttribute](ImapSequenceSet sequenceOrUidSet, ImapFetchDataItem message.DataItems, ImapParenthesizedString fetchModifiers, ImapCapability fetchModifiersCapabilityRequirement, Boolean retrieveMessages, TmessageAttribute[]& messages)
at Smdn.Net.Imap4.Client.Session.ImapSession.Fetch[TmessageAttribute](ImapSequenceSet sequenceOrUidSet, ImapFetchDataItem message.DataItems, TmessageAttribute[]& messages)
at Smdn.Net.Imap4.Client.ImapMessageInfo.EnsureStaticAttributesFetched()
at Smdn.Net.Imap4.Client.ImapMessageInfo.get_BodyStructure()

【質問1】
上記の事象が発生する原因と対策を教えてください。
(メッセージがいびつになっているので、添付ファイルは抽出できない?)

【質問2】
上記の事象が発生した後、INBOXにアクセスしようとすると以下の例外が発生します。

Message:not connected or already disconnected
Type   :System.InvalidOperationException
StackTrace
at Smdn.Net.Imap4.Client.Session.ImapClient.get_ServerCapabilities()
at Smdn.Net.Imap4.Client.Session.ImapClient.<GetMailBoxes>c__Iterator17.MoveNext()
at Smdn.Collections.Enumerable.FirstorDefault[Tsource](IEnumerable`1 source)
at Smdn.Net.Imap4.Client.Session.ImapClient.OpenMailBox(String mailboxName, Boolean as ReadOnly)

この事象が発生する原因と対策を教えてください。以下でよろしいでしょうか。
(原因:上記の事象により、接続が切れた。対策:IsConnectedプロパティがfalseなら再接続する。)

以上、ご教示いただければ幸いです。
よろしくお願いいたします。

:ID:DdfZu2HU

>>103
頂いた例外メッセージを見たところ、APOP認証でエラーとなった時点でサーバー側から切断され、
さらにその後USERコマンドを送信しようとしてPopConnectionExceptionとなっているようです。

この動作自体には問題はありませんが、APOPに失敗した時点で切断している点が
疑わしいので、サーバー側のAPOP認証に関する設定を改めてご確認いただければと
思います。

もう一点、>>101の通信内容から見てpop3UserNameに<メールアドレス>を
指定しているようですが、指定する値は<メールアドレス>であってますか?
サーバーの設定によりますが、認証時のユーザー名としてメールアドレスの@以前を
指定するものと、メールアドレス全体を指定するものがあるので、その点をご確認ください。

:ID:3E/r50lU

>>104

管理人様

いつもお世話になっております。
動作環境を記入し忘れましたので、追記します。

.NETFramework4.0

ライブラリバージョン
Smdn.Core.Standards.dll0.40.0.0
Smdn.dll0.40.0.0
Smdn.Net.Imap4.Client.dll0.90.0.0
Smdn.Net.Imap4.dll0.90.0.0
Smdn.Net.MessageAccessProtocols.dll0.90.0.0
Smdn.Security.Authentication.Sasl.dll0.33.0.0

以上、よろしくお願いします。

:ID:DdfZu2HU

>>104,106
こちらこそありがとうございます。
ユースケースからライブラリの不備や改善点を発見することが出来るので、
こちらとしても非常にありがたく思っています。

以下回答です。

【質問1の回答】
原因としてはサーバーから返送されるBODYSTRUCTUREの解析に失敗している可能性が考えられます。
ただ、返送されるBODYSTRUCTUREに問題があるのか、ライブラリ側の解析処理に問題があるのかは
判別できません。

発生した例外のInnerExceptionプロパティの内容と、BodyStructureプロパティを参照したときの
送受信ログを提示していただければと思います。

現状でのこの問題への対策は、例外が発生するメッセージの場合はBodyStructureプロパティを
参照しないようにするほかありません。
(0.90以降のSmdn.Net.Imap4.Client.dllでも同様の例外が発生すると思われます)

【質問2の回答】
原因・対策ともにご推察の通りです。

上記のような予期しない内部エラーが発生した場合は、正常に処理を継続できる保証がないため
内部的に接続を切断するようにしています。
そのため、内部エラーが発生したクライアントは切断状態となり、それ以降は操作を
行おうとすると例外InvalidOperationExceptionをスローするようになります。

このような場合は再接続して処理をやりなおす必要があります。

:ID:cjgvHHaS

>>105
回答ありがとうございます。

メールクライアントで接続に成功する場合の認証方式は「平文のUSER/PASS 認証」です。
その際のUSERには、メールアドレスが指定されています。
(メールクライアントは、Thunderbird)

成功する場合の流れは以下になります。

 サーバ→クライント    +OK <メールサーバの情報>
 クライアント→サーバ  AUTH
 サーバ→クライント    -ERR authorization first
 クライアント→サーバ  CAPA    
 サーバ→クライント    -ERR authorization first
 クライアント→サーバ  USER <メールアドレス>
 サーバ→クライント    +OK
 クライアント→サーバ  PASS <パスワード>
 サーバ→クライント    +OK

推測ですが、メールサーバ自体がAPOPを受け付けないようにみえます。
設定等でAPOPにアクセスさせずに、平文のUSER/PASS 認証をさせることはできるのでしょうか?

よろしくお願いします。

:ID:DdfZu2HU

>>108

サーバ→クライント    +OK <メールサーバの情報>

この部分について確認させていただきたいのですが、"+OK "に続く文字列は
"<process-ID.clock@hostname>"のように山カッコ<>でくくられた文字列でしょうか、
それとも山カッコを含まない文字列でしょうか。

もし山カッコでくくられた文字列(timestampと呼ばれます)であれば、
サーバーがAPOPをサポート・許可していることを表します。
(この場合、許可しているにも関わらず認証が失敗していることになります)

現在のバージョンでは、timestampを返送してきた場合は常にAPOPでのアクセスを
試行するようになっています。 また、APOPアクセスを行わないようにする設定も
今のところは用意していません。

そういった設定を追加する必要があるか判断したいので、差し支えなければ
サーバーが返送してくる具体的な文字列をお教えいただければと思います。

なお、ソースコードを修正して対応する場合は、以下のファイルにある
AuthenticateWithAppropriateMechanism()メソッドを修正することでAPOP認証を
行わないようにすることができます。

http://svn.smdn.jp/anonsvn/libs/Smdn/trunk/Smdn.Net.Pop3.Client/Smdn.Net.Pop3.Client.Session/PopSessionCreator.cs

/*
 * この部分をコメントアウトすることで、APOPによる認証を一切行わないようにする
      if ((result == null || result.Failed) && allowInsecureMechanism) {
        if (session.ApopAvailable)
          result = session.Apop(credentials, username);
      }
 */
:ID:SAW/u3DW

>>109
"+OK "に続く文字列は、山カッコ<>でくくられた文字列です。

サーバーが返送してくる具体的な文字列は以下です。

 +OK <32137.1400692822@p3plpop02-07.prod.phx3.secureserver.net>

現在の動作について分かりました。回答ありがとうございます。

:ID:DdfZu2HU

>>110
情報提供ありがとうございます。
やはりサーバー側のAPOP認証処理に問題があるようです。
(サーバーはAPOP認証を許可しているものの、実際に試行するとエラーとなる)
この問題はサーバー運営側にお問い合わせいただければと思います。

同時にSmdn.Net.Pop3.Clientの側でもAPOPを試行しないよう設定できるように
しようと思います。

次のバージョン(今週末か来週中にリリース予定)にて対応予定ですが、
それまでの間はお手数ですが>>109で提示した個所を修正することで
対応していただければと思います。

:ID:DdfZu2HU

>>110
先ほどSmdn.Net.Pop3.Client version 1.21をリリースしました。
このバージョンでは、PopClient.Profile.AllowApopLoginプロパティにfalseを
設定することで、APOPによる認証を行わないようにできるようになっています。
よろしければお使いください。

その他このバージョンでの変更点については http://smdn.jp/works/libs/Smdn.Net.Pop3.Client/releases/#changes_v1.21 をご覧ください。

:ID:9EK9fWfc

>>107

管理人様
いつもお世話になっております。

発生した例外のInnerExceptionプロパティの内容と、
BodyStructureプロパティを参照したときの送受信ログ
が採れましたので提示させていただきます。

Message  : internal error
Type       : Smdn.Net.Imap4.ImapException
StackTrace:
  at Smdn.Net.Imap4.Client.Session.ImapSession.PostProcessTransaction(IIMapTransaction t)
  at Smdn.Net.Imap4.Client.Session.ImapSession.FetchInternal[TmessageAttribute](ImapsequenceSet sequenceOrUidSet, ImapFetchDataItem message.DataItems
      , ImapParenthesizedString fetchModifiers, ImapCapability fetchModifiersCapabilityRequirement, Boolean retreiveMessages, TmessageAttribute[]& messages)
  at Smdn.Net.Imap4.Client.Session.DataItems, TmessageAttribute[]& messages)
  at Smdn.Net.Imap4.Client.ImapMessageInfo.EnsureStaticAttributesFetched()
  at Smdn.Net.Imap4.Client.ImapMessageInfo.get_BodyStructure()
Internal Exception:
Smdn.Net.Imap4.Protocol.Client.ImapMalformedResponseException : malformed literal (invalid number)
data:
  at Smdn.Net.Imap4.Protocol.Client.ImapResponseReceiver.ReceiverResponse()
  at Smdn.Net.Imap4.Protocol.Client.ImapConnection.TryReceiveResponse()
  at Smdn.Net.Imap4.Client.Transaction.ImapTransactionBase`1.ProcessReceiveResponse()
  at Smdn.Net.Imap4.Client.Transaction.ImapTransactionBase`1.Smdn.Net.Imap4.Client.Transaction.IImapTransaction.Process()"				
C: 0022 FETCH 3 (UID)
S: *3 FETCH (UID 3)
0022 OK fetch completed
C: 0023 UID FETCH 3 (RFC822.SIZE BODY.PEEK[HEADER.FIELDS] (Subject)]<0, 10240>)
S: *3 FETCH (RFC822.SIZE 250322 BODY.PEEK[HEADER.FIELDS(Subject)]<0> {128}
Subject: =?iso-2022-jp?b?~=
 =?iso-2022-jp?b?~

UID 3)
0023 OK fetch completed
C: 0024

送受信ログは、最終行が本来、
C: 0024 UID FETCH 3 (BODYSTRUCTURE ENVELOPE INTERNALDATE RFC822.SIZE)
となるべき所、上記のように尻切れトンボで終わってしまっています。

よろしくお願いいたします。

:ID:DdfZu2HU

>>113
ログありがとうございます。
調査と対応はこれから行いますが取り急ぎ。

ログはTextWriterTraceListenerで出力したものでしょうか?
C: 0024 UID FETCHのレスポンス部分のログが無いと恐らく問題を発見できないので、
恐れ入りますがソースの以下の個所を修正してログを再収集していただけますでしょうか。
尻切れトンボになっている部分はこれで出力されると思います。

もしくは、ConsoleTraceListenerでIDEのデバッグウィンドウに
出力させることができる場合はそちらから収集して頂いてもかまいません。

よろしくお願いいたします。

【修正するファイル】
http://svn.smdn.jp/anonsvn/libs/Smdn/trunk/Smdn.Net.MessageAccessProtocols/Smdn.Net.MessageAccessProtocols.Diagnostics/ConnectionTrace.cs

【修正内容】

    [Conditional("TRACE")]
    private static void LogReceived(ConnectionBase connection, byte[] received, int start, int count)
    {
#if TRACE
      connection.traceSource.TraceData(TraceEventType.Verbose,
                                       connection.Id,
                                       new ReceiveTraceData(new ArraySegment<byte>(received, start, count),
                                                            connection.RemoteEndPoint,
                                                            connection.LocalEndPoint,
                                                            connection.Id));
      connection.traceSource.Flush(); // <----この行を追加する
#endif
    }

    [Conditional("TRACE")]
    private static void LogSent(ConnectionBase connection, byte[] sent, int start, int count)
    {
#if TRACE
      connection.traceSource.TraceData(TraceEventType.Verbose,
                                       connection.Id,
                                       new SendTraceData(new ArraySegment<byte>(sent, start, count),
                                                         connection.RemoteEndPoint,
                                                         connection.LocalEndPoint,
                                                         connection.Id));
      connection.traceSource.Flush(); // <----この行を追加する
#endif
    }
}
:ID:DdfZu2HU

>>113
調査したところ、やはりUID FETCHに対するレスポンス部分のログを
見てみないことには問題個所の発見が難しいです。

なお、先ほどSmdn.Net.Imap4.Client version 1.04をリリースしました。
このバージョンではログが尻切れトンボになることはありませんので、
もしバージョンアップが可能でしたらこのバージョンでログを収集して
いただければと思います。

:ID:CBAR8jVx

>>115

さっそくのご回答ありがとうございます。
ログはTextWriterTraceListenerで出力したものです。
また、Smdn.Net.Imap4.Clientのバージョンアップありがとうございました。

バージョンアップしていただいたDLLで再度ログを取得しようと思います。
他のライブラリは>>106のままで問題ありませんでしょうか?

以上簡単ですが、今後ともよろしくお願いします。

:ID:DdfZu2HU

>>116

  • Smdn.Net.MessageAccessProtocols.dll
  • Smdn.Net.Imap4.dll
  • Smdn.Net.Imap4.Client.dll

上記3つのDLLだけバージョンアップしても動作するかもしれませんが、
確認はしていないので念の為すべて差し替えることをおすすめします。

:ID:CBAR8jVx

>>117

お世話になっております。

Smdn.Net.Imap4.Client.dllのみをVer1.4に置き換えたことにより、
IMAPログは最後まで出力されるようになりました。

以下2つのDLLでエラーになってしまうので、
FETCHのレスポンス部分のログが出力されれば、
ほかのDLLを最新にしなくても、解析は可能でしょうか?

1.Smdn.Net.Imap4.dllを1.4に置き換えたところ、以下の例外が発生しました。

Message   :メソッドが見つかりません: 'System.String Smdn.Net.Imap4.ImapBodyDisposition.get_Filename()'
Type      :System.MissingMethodException
StackTrace:
   場所 ImapOpenedMailboxInfo openedMailboxInfo, String uid)

2.Smdn.dllを2.0に置き換えると、以下の例外が発生します。

log4net:ERROR [EventLogAppender] ErrorCode: GenericFailure. Caught a SecurityException trying to access the EventLog.  Most likely the event source ExtractAttachedFile doesn't exist and must be created by a local administrator.  Will disable EventLogAppender.  See http://logging.apache.org/log4net/release/faq.html#trouble-EventLog
System.Security.SecurityException: ソースが見つかりませんでしたが、いくつかまたはすべてのログを検索できませんでした。アクセス不可能なログ: Security
   場所 System.Diagnostics.EventLog.FindSourceRegistration(String source, String machineName, Boolean readOnly, Boolean wantToCreate)
   場所 System.Diagnostics.EventLog.SourceExists(String source, String machineName, Boolean wantToCreate)
   場所 System.Diagnostics.EventLog.SourceExists(String source)
   場所 log4net.Appender.EventLogAppender.ActivateOptions()
失敗したアセンブリのゾーン:
MyComputer
ハンドルされていない例外: System.TypeLoadException: アセンブリ 'Smdn, Version=2.0.0.0, Culture=neutral, PublicKeyToken=null' から型 'Smdn.Collections.ISet`1' を読み込めませんでした。

お手数をおかけしますが、よろしくお願いします。

:ID:DdfZu2HU

>>118
ログが収集できているようでしたら、Smdn.Net.Imap4.Client.dllのみの
差し替えでもかまいませんのでよろしくお願いします。

以下、個々に発生した問題に関して。

1.Smdn.Net.Imap4.dllを1.4に置き換えたところ、以下の例外が発生しました。

version 1.01でImapBodyDisposition.FilenameプロパティをFileNameに改名しています。
(http://smdn.jp/works/libs/Smdn.Net.Imap4.Client/releases/#changes_v1.01)

恐れ入りますがImapBodyDisposition.Filenameを参照している個所のソースの修正を
お願いします。

2.Smdn.dllを2.0に置き換えると、以下の例外が発生します。

先日の>>91-93の同様の問題で、恐らくSmdn.Net.MessageAccessProtocols.dllが
.NET 3.5用のものになっているかと思われます。

.NET 4.0用と.NET 3.5用が混在していないか改めてご確認ください。

:ID:ucRfKAEb

>>119

早速のご回答ありがとうございます。

それでは取り急ぎ、Smdn.Net.Imap4.Client.dllのみの差し替えで
ログを収集し、結果は追ってご報告させていただきます。

個々の問題につきましては、ご教示のとおりでした。
1.FileNameに変更することで解決しました。
2.Smdn.Net.MessageAccessProtocols.dllが.NET 3.5用のものになっていました。

単純なミスでお手数をかけ、失礼しました。
今後ともよろしくお願いいたします。

:ID:5Q+S5EsZ

>>120
いつもお世話になっております。

Visual Studio でdllを参照する設定をしているのですが、
やはり、Smdn.Net.Imap4.dllもバージョンアップしないと、
内部エラーが発生することがあるようです。

全てのdllを最新(2014/6/2リリースの.NET4対応版)にした上で、
近々ログを採取する予定です。採取できましたら、
ご連絡しますので、もうしばらくお待ちください。

:ID:D00EWOwN

いつもお世話になっております。

SimplerMailerを使ってみたいと考えております。
Visual Studioでは???ということですので、
Mono(C#用のIDEという認識でよろしいでしょうか?)
のWindows版をインストールしてみようと思います。

Windows版のMonoでSimplerMailerを動かす場合の、
大まかな手順をご教示いただければ幸いです。

以上簡単ですが、よろしくお願いいたします。

:ID:D00EWOwN

>>122
いつもお世話になっております。

SimpleMailerを使うため、Ver1.02をダウンロードしました。
(環境が.NET Framework 4.0のため)
また、Gtk#-2.12.9とMonoDevelop-3.0.6を
ダウンロード、インストールしました。

MonoDevelopを起動して、SimpleMailerの.slnファイルを開き、
デバッグしようとしたところ、以下のエラーとなりました。

System.Runtime.InteropServices.COMException(0x80070032):
この要求はサポートされていません(HRESULTからの例外: 0x80070032)

考えられる原因と回避策がありましたら、ご教示いただければ幸いです。
よろしくお願いいたします。

:ID:DdfZu2HU

>>123
恐らく以下の件と同じ問題だと思うので、SimpleMailer.csprojのプロパティを開いて、
ターゲットアーキテクチャを32bitにすれば回避できるかもしれません。
http://forums.xamarin.com/discussion/12110/system-runtime-interopservices-comexception-0x80070032

もしくは、単に「デバッグなしで実行」することでも回避できるかもしれません。

これは恐らく違うと思いますが、以前、Mainメソッドに設定している[STAThread]が
原因でCOMExceptionが発生したことがあったので、上記の問題でなければ、
[STAThread]の削除を試してみてください。

:ID:9EK9fWfc

>>124
いつもお世話になっております。
さっそくのアドバイスありがとうございます。

【1】ターゲットアーキテクチャを32bitにする
【2】単に「デバッグなしで実行」する
【3】[STAThred]を削除する

【3】はご推察のとおり、関係ありませんでした。

【2】で動作はしますが、メールの仕組みを勉強したいので、デバッグはしたいです。
(ブレークポイントを設定したり、変数値を確認したり、といった操作)

【1】SimpleMailer.csprojがソリューションウィンドウに表示されなかったのですが、
「gui」-「Smdn.Applications.SimpleMailer」の右クリック-「オプション」で開く
「プロジェクトオプション」ダイアログの左ペインから、
「ビルド」-「カスタムコマンド」のプラットフォームには「AnyCPU」しか
表示されませんでした。

このため、SimpleMailer.csprojを直接テキストエディタで開き、
「AnyCPU」の部分を「x86」に置換して保存し、ソリューションを開きなおしましたが、
やはりデバッグの開始をしようとすると、同じエラーが表示されます。

ソリューションウィンドウの「gui」-「Smdn.Applications.SimpleMailer」が
「Smdn.Applicaions.SimpleMailer(not built in active configuration)」と
表示されているのも気になりますが、何度再ビルドしても表示は変わりません。

ターゲットアーキテクチャの変更方法に問題がありますか。

また、リンクでは、Monoのランタイムに置き換えればよい、といった記述もありますが、
それはどこにあって、何と置き換えればいいのか、わかりましたらご教示ください。

以上、ご多忙中恐縮ですが、よろしくお願いいたします。

:ID:ucRfKAEb

>>125
いつもお世話になっております。
追加で恐縮ですが、教えてください。

SimpleMailer.iniで、

 ServerURL = imap://<アカウント>@<ドメイン>/
 Password = <パスワード>

のように設定して実行したところ、下記のエラーになります。

Smdn.Net.Imap4.Client Information: 1 : CID:- connecting
<ドメイン>の接続に失敗しました
Exception: Smdn.Net.Imap4.Protocol.ImapConnectionException: connect failed ---> System.Net.Sockets.SocketException:
対象のコンピューターによって拒否されたため、接続できませんでした。

C#でメールを取得する処理では、

 client = new ImapClient(new Uri("imap://<アカウント>@<ドメイン>/"));
 client.Profile.AllowInsecureLogin = true;
 client.Connect("<パスワード>");

として、接続できています。

client.Profile.AllowInsecureLogin = true; の部分を、
SimpleMailer.iniで設定する方法をご教示いただければ幸いです。
(またはソースを修正する必要があれば、その方法)

以上、よろしくお願いいたします。

:ID:DdfZu2HU

>>125
[ビルド]→[コンパイラ]→[一般オプション]下にある[プラットフォームターゲット]はどうでしょうか?
これをx86にすればよいかと思います。

「置き換え」は使用するランタイムの切り替えを指すものと思いますが、
[編集](もしくは[ツール]?)→[設定]→[プロジェクト]→[.NETランタイム]で使用するランタイムを変えることができたと思います。

Windows環境で、かつVisual Studioが使用出来るのであればそちらを使ったほうが良いと思いますが、
MonoDevelopを使用される理由は何でしょうか?
Visual Studioではソリューションの修正が必要になる以外の問題はないと思っていますが、
他に何か問題がありましたでしょうか?

>>126
現状SimpleMailer.iniでAllowInsecureLogin相当の設定を記述することはできません。
ImapAccountクラスを編集して、コード上でAllowInsecureLoginを設定するようにしてください。

:ID:9EK9fWfc

>>127
いつもお世話になっております。
迅速なご回答ありがとうございます。

インストールしたMonoDevelopはVer.3.1.2で、
[ビルド]の配下に[コンパイラ]がなかったのですが、
「gui」-「Smdn.Applications.SimpleMailer」の右クリック-「オプション」
で開く「プロジェクトオプション」ダイアログの左ペインから、
「ビルド」-「コンパイル」にプラットフォームがありましたので、
これを「x86」に変更することで、デバッグができるようになりました。

なお、MonoDevelopに挑戦しようと思ったのは、Visual Studioでは、
ソリューション以外にも問題が出てきて、お手を煩わせたくなかっただけです。
問題がないのであれば、使い慣れたVisual Studioで行きたいと思います。

また、AllowInsecureLoginの設定については、ImapAccountクラスを編集して、
コード上で修正する必要がある旨、了解しました。

以上、簡単ですが今後ともよろしくお願いします。

:ID:ucRfKAEb

>>128
いつもお世話になっております。
SimpleMailer-1.02を試しています。
(Visual Studioではすんなり動いてくれないので、
 MonoDevelop 3.1.2 で動かしています)

    public ImapAccount(string name, Uri serverUri, ICredentialsByHost credential, int timeout, bool useTlsIfAvailable, bool readOnly)
      : base(name, serverUri)
    {
      this.client = new ImapClient(serverUri);
      this.credential = credential;

      this.client.Profile.Timeout = timeout;
      this.client.Profile.UseTlsIfAvailable = useTlsIfAvailable;
      this.client.Profile.AllowInsecureLogin = true; // ←この行を追加

      this.ReadOnly = readOnly;
    }

上記のように、ImapAccountクラスを修正しましたが、
実行すると、下記のエラーとなります。

localhostの接続に失敗しました
Exception: Smdn.Net.Imap4.Protocol.ImapConnectionException: connect failed ---> System.Net.Sockets.SocketException: 対象のコンピューターによって拒否されたため、接続できませんでした。 127.0.0.1:143
   場所 System.Net.Sockets.Socket.Connect(IPAddress[] addresses, Int32 port)
   場所 System.Net.Sockets.Socket.Connect(String host, Int32 port)
   場所 Smdn.Net.ConnectionBase.Connector.Connect(String host, Int32 port, Int32 millisecondsTimeout) 場所 ~\core\Smdn.Net.MessageAccessProtocols\Smdn.Net\ConnectionBase.cs:行 231
   場所 Smdn.Net.ConnectionBase.Connect(String host, Int32 port, Int32 millisecondsTimeout, UpgradeConnectionStreamCallback createAuthenticatedStreamCallback) 場所 ~\core\Smdn.Net.MessageAccessProtocols\Smdn.Net\ConnectionBase.cs:行 180
   --- 内部例外スタック トレースの終わり ---
   場所 Smdn.Net.ConnectionBase.Connect(String host, Int32 port, Int32 millisecondsTimeout, UpgradeConnectionStreamCallback createAuthenticatedStreamCallback) 場所 ~\core\Smdn.Net.MessageAccessProtocols\Smdn.Net\ConnectionBase.cs:行 204
   場所 Smdn.Net.Imap4.Protocol.ImapConnectionBase.Connect(String host, Int32 port, Int32 millisecondsTimeout, UpgradeConnectionStreamCallback createAuthenticatedStreamCallback) 場所 ~\core\Smdn.Net.Imap4\Smdn.Net.Imap4.Protocol\ImapConnectionBase.cs:行 58
   場所 Smdn.Net.Imap4.Protocol.Client.ImapConnection..ctor(String host, Int32 port, Int32 millisecondsTimeout, UpgradeConnectionStreamCallback createAuthenticatedStreamCallback) 場所 ~\core\Smdn.Net.Imap4.Client\Smdn.Net.Imap4.Protocol.Client\ImapConnection.cs:行 84
   場所 Smdn.Net.Imap4.Client.Session.ImapSession.Connect(String host, Int32 port, Int32 connectTimeout, Int32 sendTimeout, Int32 receiveTimeout, UpgradeConnectionStreamCallback createAuthenticatedStreamCallback) 場所 ~\core\Smdn.Net.Imap4.Client\Smdn.Net.Imap4.Client.Session\ImapSession.cs:行 368
   場所 Smdn.Net.Imap4.Client.Session.ImapSession..ctor(String host, Int32 port, Int32 transactionTimeout, Int32 sendTimeout, Int32 receiveTimeout, Boolean handlesReferralAsException, UpgradeConnectionStreamCallback createAuthenticatedStreamCallback) 場所 ~\core\Smdn.Net.Imap4.Client\Smdn.Net.Imap4.Client.Session\ImapSession.cs:行 233
   場所 Smdn.Net.Imap4.Client.Session.ImapSessionCreator.CreateSession(IImapSessionProfile profile, SaslClientMechanism authMechanismSpecified, UpgradeConnectionStreamCallback createSslStreamCallback, ImapSession& session) 場所 ~\core\Smdn.Net.Imap4.Client\Smdn.Net.Imap4.Client.Session\ImapSessionCreator.cs:行 68
   場所 Smdn.Net.Imap4.Client.Session.ImapSessionCreator.CreateSession(IImapSessionProfile profile, SaslClientMechanism authMechanismSpecified, UpgradeConnectionStreamCallback createSslStreamCallback) 場所 ~\core\Smdn.Net.Imap4.Client\Smdn.Net.Imap4.Client.Session\ImapSessionCreator.cs:行 43
   場所 Smdn.Net.Imap4.Client.ImapClient.ConnectCore(ConnectParams params) 場所 ~\core\Smdn.Net.Imap4.Client\Smdn.Net.Imap4.Client\ImapClient.cs:行 191
   場所 Smdn.Net.Imap4.Client.ImapClient.Connect(ICredentialsByHost credentials) 場所 ~\core\Smdn.Net.Imap4.Client\Smdn.Net.Imap4.Client\ImapClient.cs:行 501
   場所 Smdn.Applications.SimpleMailer.ImapAccount.EnsureConnected() 場所 ~\gui\Smdn.Applications.SimpleMailer\Smdn.Applications.SimpleMailer\ImapAccount.cs:行 37
   場所 Smdn.Applications.SimpleMailer.MainWindow.UpdateMailboxTree(ImapAccount account) 場所 ~\gui\Smdn.Applications.SimpleMailer\Smdn.Applications.Si
mpleMailer\MainWindow.cs:行 489

これだけでは足りないのでしょうか?
ご教示いただければ幸いです。

:ID:DdfZu2HU

>>129
AllowInsecureLoginの設定箇所自体は追加された個所で問題ありません。

しかし、>>126>>129のエラーメッセージを改めて見たところ、
AllowInsecureLoginの設定とエラーの原因は無関係で、
接続確立の段階(認証処理を開始する以前の段階)で失敗しています。

接続先のアドレスとポートは127.0.0.1:143で合っているか、
ServerURLの設定が複数記述されていないかなど、iniファイルの内容を再度ご確認ください。

また、Visual Studioで動かない理由について、もしサンプルや
ライブラリに問題があるなら修正したいと思いますので、
お教えいただけると幸いです。

:ID:5Q+S5EsZ

>>121
管理人様
いつもお世話になっております。

過日の例外の事象で、ログが不足なく採取できましたので、提示させていただきます。
つきましては、原因と対策につきまして、ご教示いただければ幸いです。

【事象】(再掲)
メッセージ全体の構造を取得し、添付ファイルを抽出する処理を組んでおりますが、
ImapMessageInfoのBodyStructureプロパティを参照するタイミングで、例外が発生。
(通常は問題なく処理できており、特定のメッセージに依存している模様)

【アプリケーションのログ】

:
Message   :internal error
Type      :Smdn.Net.Imap4.ImapException
StackTrace:   at Smdn.Net.Imap4.Client.Session.ImapSession.PostProcessTransaction(IImapTransaction t)
   at Smdn.Net.Imap4.Client.Session.ImapSession.FetchInternal[TMessageAttribute](ImapSequenceSet sequenceOrUidSet, ImapFetchDataItem messageDataItems
      , ImapParenthesizedString fetchModifiers, ImapCapability fetchModifiersCapabilityRequirement, Boolean retrieveMessages, TMessageAttribute[]& messages)
   at Smdn.Net.Imap4.Client.Session.ImapSession.Fetch[TMessageAttribute](ImapSequenceSet sequenceOrUidSet, ImapFetchDataItem messageDataItems, TMessageAttribute[]& messages)
   at Smdn.Net.Imap4.Client.ImapMessageInfo.EnsureStaticAttributesFetched()
   at Smdn.Net.Imap4.Client.ImapMessageInfo.get_BodyStructure()
InnerException:Smdn.Net.Imap4.Protocol.Client.ImapMalformedResponseException: malformed literal (invalid number)
data: 
   at Smdn.Net.Imap4.Protocol.Client.ImapResponseReceiver.ReceiveResponse()
   at Smdn.Net.Imap4.Protocol.Client.ImapConnection.TryReceiveResponse()
   at Smdn.Net.Imap4.Client.Transaction.ImapTransactionBase`1.ProcessReceiveResponse()
   at Smdn.Net.Imap4.Client.Transaction.ImapTransactionBase`1.Smdn.Net.Imap4.Client.Transaction.IImapTransaction.Process()

【IMAP送受信ログ】

:
C: 0024 UID FETCH 3 (BODYSTRUCTURE ENVELOPE INTERNALDATE RFC822.SIZE)
S: * 3 FETCH (BODYSTRUCTURE (("TEXT" "plain" ("charset" "ISO-2022-JP") NIL NIL "7bit" 579 21 NIL NIL NIL)
("application" "pdf" ("name" "Hoge Hoge.pdf") NIL NIL "base64" 247522 NIL ("attachment" ("filename" "Hoge Hoge.pdf")) NIL)
 "mixed" ("boundary" "------------060308060102060002080408") NIL NIL) ENVELOPE ("Mon, 21 Apr 2014 11:10:36 +0900" {113}
=?iso-2022-jp?b?~?= =?iso-2022-jp?b?~?= (({42}
=?ISO-2022-JP?B?~?= NIL "hanako-suzuki" "xxxx-xx.co.jp")) (({42}
=?ISO-2022-JP?B?~?= NIL "hanako-suzuki" "xxxx-xx.co.jp")) (({42}
=?ISO-2022-JP?B?~?= NIL "hanako-suzuki" "xxxx-xx.co.jp")) ((NIL NIL {-1}
 {14}
yy.zzzz.co.jp")) (({42}
=?ISO-2022-JP?B?~?= NIL "taro-yamada" "xxxx-xx.co.jp")) NIL NIL "<FFFFFFFF.9999999@xxxx-xx.co.jp>") INTERNALDATE "21-Apr-2014 02:10:41 +0000" RFC822.SIZE 250322 UID 3)
0024 OK fetch completed
CID:1 disconnected: 127.0.0.1:143 (localhost)

【動作環境】
.NET Framework4.0

ライブラリバージョン
Smdn.Core.Standards.dll1.0.0.0
Smdn.dll2.0.0.0
Smdn.Net.Imap4.Client.dll1.4.0.0
Smdn.Net.Imap4.dll1.4.0.0
Smdn.Net.MessageAccessProtocols.dll1.4.0.0
Smdn.Security.Authentication.Sasl.dll1.0.0.0

以上、ご多忙中恐縮ですが、よろしくお願いいたします。

:ID:DdfZu2HU

>>131
動作ログありがとうございます。

確認したところサーバーが返送するレスポンスに問題があります。
(送受信ログ内の{-1}の部分)

以下の不具合内容に送受信ログを添えて、サーバー開発元にお問い合わせ頂ければと思います。
なお、問題特定のために原因となったメールのヘッダ部分が必要とされる場合があるかもしれません。

・不具合概要
FETCHレスポンスにおいて、本来NILあるいは長さ0以上の文字列となるべき個所に、
長さが-1の文字列({-1})が設定される。

・不具合詳細
FETCHコマンドでBODYSTRUCTUREを取得する際、ENVELOPE内のaddr-mailbox(env-to部分)に
numberが-1のliteralが設定されたレスポンスが返される場合がある。
(ENVELOPE取得時、メールアドレスの解析に失敗した場合に発生?)

RFC3501ではliteralのnumberは0以上とされているため、すべてのIMAPクライアントで
このレスポンスを正しく解釈できない。

上記問題はサーバー側のRFC違反によるものなので、申し訳ありませんがライブラリ側としては
現状のまま対応なしとさせていただきたいと思います。

どうしてもクライアント側での対処が必要となるようでしたら、以下の個所を
修正することで暫定対応できるかもしれませんが、未検証かつ動作の正常性は保証はできません。

http://svn.smdn.jp/anonsvn/libs/Smdn/trunk/Smdn.Net.Imap4/Smdn.Net.Imap4.Protocol/ImapReceiver.cs
ParseLiteralメソッド

        else {
          //throw new ImapMalformedDataException("malformed literal (invalid number)");
          // 例外をスローする代わりに、numberを0と仮定して処理を続ける
          index += 2;
          number = 0L;
        }
:ID:DdfZu2HU

>>131,132
すみません、二箇所訂正です。

・不具合詳細
正) FETCHコマンドでBODYSTRUCTUREを取得する際
誤) FETCHコマンドでENVELOPEを取得する際

修正コード

        else {
          //throw new ImapMalformedDataException("malformed literal (invalid number)");
          // 例外をスローする代わりに、numberを0と仮定して処理を続ける
          index += 2;
          number = 0L;
          break;
        }
:ID:5Q+S5EsZ

>>133
いつもお世話になっております。
迅速なご回答、ありがとうございました!

早速、メールサーバ側に問い合わせを行いました。
取り急ぎ、以上ご連絡申し上げます。

:ID:DdfZu2HU

くじら1号様。 現在発生している問題とは別件です。

先ほどSmdn.Net.Imap4.Client version 1.05, 1.09をリリースしました。
1.05以降ではDLLファイルのプロパティにてアセンブリバージョンに
加えて対象フレームワークの情報を確認できるようになっています。

よろしければ構成管理の際などにご活用ください。

その他の変更点は以下をご覧ください。
http://smdn.jp/works/libs/Smdn.Net.Imap4.Client/releases/#changes_v1.05
http://smdn.jp/works/libs/Smdn.Net.Imap4.Client/releases/#changes_v1.09

:ID:QEeocG2o

>>135
管理人様。いつもお世話になっております。

ご連絡ありがとうございます。
対象フレームワークの情報が「製品名」で確認できるのですね。
(例:Smdn.Net.Imap4.Client-1.05-netfx4.0)

非常に助かります。今までは無理やりテキストエディタで開いて、
文字列「v4.0.30319」を検索していたものですから。

ついでの質問で恐縮ですが、教えてください。
現在利用中のVer.0.90を最新化(Ver.1.09)を検討中です。

その際、ソースの修正が必要なのは、
「ImapBodyDisposition.FilenameプロパティをFileNameに改名」
に対応する部分と考えており、リリース概要を見る限り、
他の修正は不要と考えておりますが、間違いないでしょうか?

以上、よろしくお願いいたします。

:ID:DdfZu2HU

>>136
Smdn.Net.Imap4.Client v0.90→v1.09のバージョンアップで修正が必要な個所
(インターフェイスが変更されている個所)は以下のとおりです。

【v0.91での変更】
以下の例外クラスをキャッチしている場合は、別の例外クラスに置き換えてください。

  • ImapMessageDeletedException→ImapMessageVanishedException
  • ImapMailboxClosedException→ImapUnavailableException

http://smdn.jp/works/libs/Smdn.Net.Imap4.Client/releases/#changes_v0.91

【v1.01での変更】
ImapBodyDisposition.Filenameプロパティを参照している場合は
ImapBodyDisposition.FileNameプロパティを参照するよう変更してください。

http://smdn.jp/works/libs/Smdn.Net.Imap4.Client/releases/#changes_v1.01

【v1.02での変更】
Lengthプロパティを参照している場合はCountプロパティを参照するよう変更してください。

  • ImapEnvelope.From.Length→ImapEnvelope.From.Countなど
  • この変更に該当するメンバーは以下の変更履歴を参照してください

http://smdn.jp/works/libs/Smdn.Net.Imap4.Client/releases/#changes_v1.02

上記の個所を変更すれば概ねこれまでどおり動作すると思いますが、これ以外にも
スローされる例外の種類が変わっているメソッド・動作が変わっているメソッドがあるため、
各クラスの使い方や例外処理の実装次第では追加の修正が必要になる可能性はあります。

もしバージョンアップで問題が生じた場合はお問い合わせください。

:ID:CBAR8jVx

>>137
丁寧なご回答、ありがとうございます。

ひととおり最新版に入れ替えて、ビルドしたところ、
T[]からILIST<T>への変更に伴って、
以下の変更が必要になりました。

【変更前】

string m_mailBodyFrom;
ImapMessageInfo message = openedMailbox.GetMessageByUid(int.Parse(uid));
ImapAddress[] fromAddr = message.Envelope.From;
 ̄ ̄ ̄ ̄ ̄ ̄ ̄
if (message.Envelope.From.Length > 0){
              ̄ ̄ ̄
  m_mailBodyFrom = fromAddr[0].Name + '<' + fromAddr[0].Mailbox + '@' + fromAddr[0].Host + '>';
}

【変更後】

string m_mailBodyFrom;
ImapMessageInfo message = openedMailbox.GetMessageByUid(int.Parse(uid));
IList<ImapAddress> fromAddr = message.Envelope.From;
 ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄
if (message.Envelope.From.Count > 0){
              ̄ ̄ ̄
  m_mailBodyFrom = fromAddr[0].Name + '<' + fromAddr[0].Mailbox + '@' + fromAddr[0].Host + '>';
}

この変更は正しいでしょうか?
fromAddr[0]の部分は変更していませんが、
同じ結果が得られるでしょうか?

C#の知識の問題かもしれませんが、
ご教示いただけると幸いです。

以上、よろしくお願いいたします。

:ID:U6R63jHy

>>138
その変更で問題ありません。

T[]からIList<T>に変更したことによりLengthの代わりにCountを
参照するよう変更する必要がありますが、リストに格納される内容と
その格納順序はこれまでどおりで変更はありません。

従って、fromAddr[0]で得られる結果もこれまでと同じとなります。

:ID:l9gkygEU

>>139
いつもお世話になっております。
迅速なご回答ありがとうございます。

その他は、問題なく正常に動作しています。
取り急ぎ、ご報告させていただきます。
ありがとうございました。

:ID:r0+Hb/Rv

お世話になります。
IMAPサーバーからIMAPサーバーへの移行を行うために、.netで移行ツールをこさえようとしています。
Smdn.Net.Imap4.Clientライブラリは、他にはないアカウント間のメールボックスの移行用メソッドがありますので、これを使いたい!とおもっています。

ドキュメントの「2.4 アカウント・サーバーをまたがるメールボックスの移動・コピー」のImapClientクラスに用意されているCopyMailboxメソッドの使用方法がわかりませんでしたので、ご相談させていただきたくて、投稿します。
VS2010でC#で開発中で、移行元をSrcClient、移行先をDstClientとしてIMAPClientを生成していますが、CopyMailboxメソッドをどのように使用すればよいかお教えください。

:ID:U6R63jHy

>>141
Smdn.Net.Imap4.Clientをご利用いただきありがとうございます。

ドキュメントの以下のセクションにて、CopyMailboxメソッドの使用方法・注意事項と
別のIMAPサーバーへのコピーを行うサンプルコードを追記しましたのでご覧ください。

§5.1.3.4 アカウントまたはサーバーをまたがるコピー・移動
http://smdn.jp/works/libs/Smdn.Net.Imap4.Client/docs/#operation_examples_misc_accrosssession

サンプルコードは接続処理等を省略しているため、そのままでは動作しない点に
ご注意ください。
また、もし他にご不明な点がありましたら、お気軽にご相談ください。

:ID:KEsErnr8

>>142

早速の回答ありがとうございます。できました。
これからもどうぞよろしくお願いします。

:ID:5Q+S5EsZ

>>98
いつもお世話になっております。
だいぶ前の話で恐縮です。

// BODYSTRUCTUREが添付ファイルのセクションかどうかを調べる
static bool IsAttachmentSection(IImapBodyStructure section){
  var s = section as IImapBodyStructureExtension;
  if (s == null) return false;
  // Content-Dispositionヘッダがあり、かつ値がattachmentの場合
  return (s.Disposition != null && s.Disposition.IsAttachment);
}

上記でs.Dispositionが「null」値になっている場合、
Content-Typeを見て、添付ファイルと判断する処理を追加しようと考えています。

Smdn.Formats.Mime.MailクラスのIsAttachedFile()メソッド(private)での
実装を拝見しましたが、MimeMessageクラスに対する内容になっていました。

IImapBodyStructureないしIImapBodyStructureExtensionに対して判断する場合、
これらのクラスとMimeMessageクラスとの関係はどうなっていますでしょうか?
アドバイスをいただければ幸いです。

以上簡単ですが、よろしくお願いいたします。

:ID:U6R63jHy

>>144
Content-DispositionとContent-Typeヘッダからどのように添付ファイルを判別するか
という実装の例として提示させて頂いたものですが、MimeMessageとIImapBodyStructureは
互いに関連の無いクラスなので、そのまま呼び出して使うことはできません。

ただ、Content-TypeヘッダはIImapBodyStructure.MediaTypeプロパティ、Content-Dispositionヘッダは
IImapBodyStructureExtension.Dispositionプロパティで参照できるので、このプロパティを
参照するように書き換えればこのメソッドの処理を流用できるかと思います。

参考までに、IsAttachedFileでは、Content-Dispositionが無い場合は以下のような判断を行なっています。

  1. Content-Dispositionが無い場合
    1. 最上位のmultipart/mixed配下にあるテキストパート(text/plain, text/html)は添付ファイルとみなさない (本文であるため)
    2. multipart/alternative配下にあるパートは添付ファイルとみなさない (代替コンテンツであるため)

本題とは離れますが、IImapBodyStructureに関して次のポストもご覧いただけると幸いです。

:ID:U6R63jHy

Smdn.Net.Imap4.Client.dllでは、この先のバージョンにてIImapBodyStructure関連のインターフェイスを
整理しなおそうと考えています。

これまで直接IImapBodyStructureを参照する必要があって分かりづらかったものを、以下のような
クラスを追加してより使いやすい形にしようと考えています。
(現行バージョンでIImapBodyStructureを直接参照しているメンバは、新しいクラスを追加する際に
非推奨とし、そのしばらく後に削除する予定です)

もし現行のIImapBodyStructure関連で不便な部分や必要な機能がありましたらお教えください。
新しいクラスを作成する際の参考にさせていただきたいと思います。

現行バージョンでのサンプル
ImapMessageInfo m;

IImapBodyStructure structure1 = m.GetStructureOf(1);
IImapBodyStructure structure2 = m.BodyStructure.Find(new Smdn.MimeType("image/jpeg"));

m.Save(structure1, "filename");
m.Save(structure2, structure2.GetContentFileName());
現在検討中のクラス案
ImapMessageInfo m;

ImapMessageSectionInfo section1 = m.GetSection(1);
ImapMessageSectionInfo section2 = m.FindSection(new Smdn.MimeType("image/jpeg"));

section1.Save("filename");
section2.Save();
:ID:D00EWOwN

>>145
いつもお世話になっております。
早速のご回答ありがとうございました。
参考にさせていただきます。

>>146
要望について記述します。

1.Content-Dispositionヘッダが無くても、
添付ファイルを取得できるような機能

2.添付ファイル名を取得できる機能

以上の機能があると嬉しいです。

私は、Smdnライブラリを利用した、添付ファイル抽出プログラム
の修正を担当しているのですが、現行のIImapBodyStructure関連では、
添付ファイル名の復元にかなり苦労しているようです。

具体的には、ファイル名部分が通常文字の場合と16進数の場合に分け、
16進数の場合はデコードし、ファイル名が復元できない場合は、
固定ファイル名で保存し、といった処理にかなりのステップ数を割いています。

以上簡単ですが、今後ともよろしくお願いいたします。

:ID:U6R63jHy

>>147
ご要望ありがとうございます。

1.Content-Dispositionヘッダが無くても、添付ファイルを取得できるような機能

どのようなインターフェイスにするかは検討中ですが、機能としては実装したいと思います。

2.添付ファイル名を取得できる機能

これに関しては、version 1.01 (>>100)にて提示させて頂いたImapBodyStructureUtils.GetContentFileName()メソッドを
使うことにより、現行バージョンでもヘッダからデコード済みのファイル名を取得できると思うのですが、
このメソッドでは機能に不足がありましたでしょうか?

GetContentFileName()相当の機能を新しいクラスで実装しようと考えているので、
このメソッドで対応できない場合、あるいは現在実装で苦労している部分(特に「16進数の場合」について)を
お教えいただけると幸いです。 可能なら対応したいと思います。

:ID:5Q+S5EsZ

>>148
早速のご回答ありがとうございます。

現在、稼働中のプログラムは、Ver0.90ベースで開発されていますので、
ImapBodyStructureUtils.GetContentFileName()メソッドが使えなかっただけ、
だと思いますが、開発者の意図を確認してみる必要があり、少し時間がかかるかもしれません。

取り急ぎ、以上中間報告させていただきます。

:ID:D00EWOwN

>>132
管理人様:
いつもお世話になっております。

過日のBODYSTRUCTURE取得時の例外の件についての後日談です。

ヘッダ情報が、以下のようになっている場合、

To: "@foo.bar.co.jp"

メールサーバから{-1}が返ってくることが判明しました。

ただし、このヘッダ情報は、RFC5321違反ということで、
メールサーバ側でも対応はしない、とのことでした。
>>133
の方法で回避できそうですが、ひとまずは例外を捕捉して、
当該メールはスキップする方向で対応する予定です。

以上、取り急ぎご報告いたします。

:ID:U6R63jHy

>>150
続報ありがとうございます。

私見ですが、せめてNILを返すようにするのが妥当かと思うので、対応しないというのは残念です。

先に書いたとおり、ライブラリ側としてもこの件は対応なしとさせていただきますので、
恐れ入りますが何らかの方法で回避して頂ければと思います。

:ID:5Q+S5EsZ

>>151
ご連絡ありがとうございます。

メールサーバ側で対応しない、と断言しているのでなく、
アプリ側で対応してもらえないか、という打診でした。
私の書き方が悪く、誤解を与えてしまい、失礼しました。

いずれにしても、せめてNILを返すようにしてほしいと、
メールサーバ側へは要望を出しておきました。

以上簡単ですが、今後ともよろしくお願いします。

:ID:D00EWOwN

>>148
いつもお世話になっております。
現在実装で苦労している部分(特に「16進数の場合」について)のロジックは以下の通りです。

メッセージのIImapBodyStructureExtensionにDispositionにFileNameプロパティが存在すれば、
それをそのまま添付ファイル名としていますが、存在しない(==null)場合、
以下のように添付ファイル名を取り出しています。

DispositionにFileNameプロパティが存在しない場合、すべてのkey-valueペアを探索し、
"filename"が含まれるkeyが見つかれば、対応するvalueからファイル名を以下のように組み立てる。
(見つからなければ、"name"が含まれるkeyが見つかれば、対応するvalueをファイル名とする。)

valueを'(シングルクォート)でsplitして格納した、string配列の第3要素(~[2])から
1文字ずつ取り出し、'%'か否かで、16進か通常文字かを判断した上、一旦byte配列に格納する。
byte配列をエンコードして、文字列としてのファイル名を復元する。
(ファイル名に使えない文字があった場合は、当該文字を削除する)

これ以外にも、CP1252だった場合の対応などがあります。

コードそのものはお客様情報になり、公開ができないこと、
私自身がロジックを理解しきっていないこと、などから
抽象的な説明となってしまい申し訳ありません。

不明な点がありましたら、ご教示いただければ、
可能な範囲でお答えする所存ですので、よろしくお願いします。

:ID:U6R63jHy

>>152
こちらこそ差し出がましいことを失礼しました。
また、要望として送っていただきありがとうございます。

:ID:U6R63jHy

先ほどSmdn.Net.Imap4.Client version 1.11をリリースしました。

>>146で予告したとおり、IImapBodyStructure関連の機能を
整理しなおしたクラスImapMessagePartInfoを導入しました。
詳細についてはドキュメントの以下のセクションをご覧ください。

メッセージのセクションとメッセージパート
http://smdn.jp/works/libs/Smdn.Net.Imap4.Client/docs/#operation_examples_message_section

メッセージの構造の取得とメッセージパートに対する操作
http://smdn.jp/works/libs/Smdn.Net.Imap4.Client/docs/#operation_examples_message_messagepart

メッセージパートの取得・検索
http://smdn.jp/works/libs/Smdn.Net.Imap4.Client/docs/#operation_reference_message_messagepart

メッセージパート構造の走査・メッセージパートの取得
http://smdn.jp/works/libs/Smdn.Net.Imap4.Client/docs/#operation_reference_messagepart

IImapBodyStructureインターフェイスを使用するメンバは廃止予定となっています。
お手数ですが、現在IImapBodyStructure使用している個所はImapMessagePartInfoを
使用するように変更してください。

また、引き続き要望は募集しておりますので、機能に不足があればお気軽にお申し付けください。

その他version1.11での変更点は以下をご覧ください。
http://smdn.jp/works/libs/Smdn.Net.Imap4.Client/releases/#changes_v1.11

:ID:U6R63jHy

くじら1号様。
以下はImapMessagePartInfoクラスを使って添付ファイルの抽出を行うサンプルです。

>>147で頂いたご要望を反映していますので、今後version 1.11を導入する際の
検討材料としてお使いいただければと思います。

また、>>153を読んだところ、ファイル名の取得以外にも現在ライブラリで実装されている
機能を使えば実現できる個所があったので、それも合わせて記載しています。
要求されている機能をすべて満たすには不十分かもしれませんが、一助となれば幸いです。

// メールボックスにあるすべてのメッセージから添付ファイルを抽出するサンプル
using (ImapClient client = new ImapClient()) {
  // INBOXを開く
  using (var mailbox = client.OpenInbox()) {
    // メールボックスにあるすべてのメッセージを取得
    // 同時にBODYSTRUCTUREも取得させるため、ImapMessageFetchAttributeOptions.StaticAttributesを指定
    foreach (var message in mailbox.GetMessages(ImapMessageFetchAttributeOptions.StaticAttributes)) {
      // メッセージ内を走査して添付ファイルと推測されるすべてのメッセージパートを取得
      var attachmentParts = message.FindAllMessageParts(ImapMessagePartInfo.Predicates.SeemsToBeAttachmentPart);

      // 以下のようにした場合は、Content-Disposition: attachmentが設定されている
      // メッセージパートのみが取得される
      //var attachmentParts = message.FindAllMessageParts(ImapMessagePartInfo.Predicates.IsAttachmentPart);

      foreach (var attachmentPart in attachmentParts) {
        // 添付ファイルに与えられているファイル名を取得する
        var filename = attachmentPart.GetContentFileName();

        if (filename == null) {
          // ファイル名が取得できなかった場合は、メールのサブジェクトと
          // メッセージパートのセクション番号を連結したものをとりあえずの
          // ファイル名として使用する
          filename = message.EnvelopeSubject + "." + attachmentPart.Specifier;

          // 拡張子はContent-Typeから推定したものを使用する
          // (ただし、常に適切な拡張子が使用されるとは限らない)
          filename += Smdn.MimeType.FindExtensionsByMimeType(attachmentPart.ContentType).FirstOrDefault() ?? string.Empty;
        }

        // ファイル名として使用できない文字をアンダースコア"_"に置換する
        filename = Smdn.IO.PathUtils.ReplaceInvalidFileNameChars(filename, "_");

        // 以下のようにした場合は、パスとして使用できない文字が置換される
        //filename = Smdn.IO.PathUtils.ReplaceInvalidPathChars(filename, "_");

        // 添付ファイルを取得し、デコードしてファイルに保存する
        attachmentPart.Save(filename, ImapMessageFetchBodyOptions.DecodeContent);
      }
    }
  }
}
:ID:U6R63jHy

>>153
詳細な内容を頂きありがとうございます。

(以下、ImapBodyStructureUtils.GetContentFileName()メソッドと
version 1.11に含まれるImapMessagePartInfo.GetContentFileName()メソッドに関して、
両方をまとめて述べます。 version 1.11時点ではどちらも同じ動作・同じ結果を返します。)

まず「16進数の場合」については、回答頂いた内容から察するにRFC2231形式の
パーセントエンコードされたファイル名のことと思われますが、だとすると
これについては既に対応しています。
(こちらで把握していない「16進数」の形式があるのかと思い、質問させて頂きました)

一方、「"name"が含まれるkeyが見つかれば―」の個所は、nameがContent-Typeの
パラメータか、それともContent-Dispositionのパラメータかで話が変わってきます。
現在のGetContentFileName()メソッドでは、Content-Typeのnameパラメータからの
ファイル名の取得には対応していますが、Content-Dispositionのnameパラメータには
非対応です。

ただ、Content-Dispositionのnameパラメータは添付ファイル名として扱うべき
値ではないので、その場合はGetContentFileName()で扱う必要はないと考えています。
このnameパラメータに関するロジックとは、どういったヘッダ内容を想定したものでしょうか?
よろしければお教えいただければと思います。

その他、いただいた内容を見る限りでは、すべて現在の実装でカバーでき、
新たに対応が必要になりそうな個所はなさそう、といった感触です。

最後に、現時点(version 1.11まで)のGetContentFileName()メソッドでファイル名として
取得できるヘッダのパターンを記載しておきます。
現状ではこれが添付ファイル名として広く使われているパターンと把握していますが、
これではカバーできないパターン、あるいは対応に苦慮しているパターンがあれば、
GetContentFileName()メソッドでの対応を検討したいと思います。

#1 Content-Dispositionヘッダ + 拡張されたfilenameパラメータ(RFC2231形式)
Content-Disposition: ...; filename*=iso-2022-jp'ja'%xx%xx%xx.jpg
Content-Disposition: ...; filename*=iso-2022-jp'ja'xxx.jpg

#2 Content-Dispositionヘッダ + 拡張されていないfilenameパラメータ
Content-Disposition: ...; filename=xxx.jpg

#3 Content-Typeヘッダ + 拡張されたnameパラメータ(RFC2231形式)
Content-Type: ...; name*=iso-2022-jp'ja'%xx%xx%xx.jpg
Content-Type: ...; name*=iso-2022-jp'ja'xxx.jpg

#4 Content-Typeヘッダ + nameパラメータ
Content-Type: ...; name="=?iso-2022-jp?B?XXXXXXXXX?="
Content-Type: ...; name="=?iso-2022-jp?Q?XXXXXXXXX?="
Content-Type: ...; name=xxx.jpg

(参考までに、GetContentFileName()メソッドは#1→#4の優先順位でファイル名を取得します。#1がなければ#2→#3→#4といった流れです。)

:ID:D00EWOwN

>>157
いつもお世話になっております。
ご丁寧な回答、ありがとうございます。

「"name"が含まれるkeyが見つかれば―」の個所は、Content-Dispositionのパラメータです。
ただ、こちらの"name"パラメータは、ファイル名に不適切とのこと、了解いたしました。

CP1252は、キリル文字などがファイル名に使われている場合の対応のようですが、
GetContentFileName()では、対応していますでしょうか?
また、ハングルなどのUnicode文字の場合など、いろいろな想定が必要かと考えています。

GetContentFileName()で対応しているファイル名の種類について、
ご教示いただけますでしょうか?

以上、よろしくお願いします。

:ID:5Q+S5EsZ

>>157
いつもお世話になっております。
ご丁寧な回答、ありがとうございます。

「"name"が含まれるkeyが見つかれば―」の個所は、Content-Dispositionのパラメータです。
ただ、こちらの"name"パラメータは、ファイル名に不適切とのこと、了解いたしました。

CP1252は、キリル文字などがファイル名に使われている場合の対応のようですが、
GetContentFileName()では、対応していますでしょうか?
また、ハングルなどのUnicode文字の場合など、いろいろな想定が必要かと考えています。

GetContentFileName()で対応しているファイル名の範囲(一覧)について、
また、想定外のファイル名であった場合、どのような例外を返すのかなど、
ご教示いただけますでしょうか?

以上、よろしくお願いします。

:ID:U6R63jHy

>>159
まず文字コードに関して。

GetContentFileName()ではSystem.Text.Encodingクラスを使ってデコードを行うので、
対応可能な文字コードはEncodingクラスと同じとなります。
.NET FrameworkのEncodingクラスはCP1252に対応しているようなので、
GetContentFileName()もCP1252でエンコードされた値を扱えると思います。

「対応しているファイル名の範囲(一覧)」については、失礼ながら「範囲」が
何の範囲を指すのか、お聞きしたいことがわかりませんでした。
カバーできる文字コードの範囲という意味であれば、先に述べたとおりEncodingクラスと
同じ範囲となります。
(ハングル・Unicode文字に関しても同様に、Encodingクラスが扱えるものなら対応しています)

次に例外等について。 version 1.11現在では以下の二つのオーバーロードを用意しています。

public string GetContentFileName()
public string GetContentFileName(EncodingSelectionCallback selectFallbackEncoding,
                                 bool throwIfMalformedOrUnsupported)

2つ目のオーバーロードでthrowIfMalformedOrUnsupportedにtrueを指定した場合は、
次の二つの例外をスローする可能性があります。

FormatException (System名前空間)
不正な形式の場合
EncodingNotSupportedException (Smdn.Formats名前空間, NotSupportedExceptionを継承)
対応していない文字コードの場合

一方1つ目のオーバーロード、あるいは2つ目でthrowIfMalformedOrUnsupportedにfalseを指定した場合は、
上記の例外をスローする代わりにデコード前の生の値をそのまま返します。

また、selectFallbackEncodingに適当なコールバックメソッドを指定すると、
対応していない文字コードだった場合に使用するフォールバック用のEncodingを
選択できるようになっています。
(コールバックメソッドに関しては>>23をご覧ください)

そのほか、>>157で挙げたヘッダとパラメータの組み合わせがない場合は、nullを返します。
(この場合は例外をスローしません)

GetContentFileName()はデコードした結果については一切検証を行いません。
例えば、戻り値がファイル名として適切かどうかは呼び出し側で判断する必要があります。
(ファイル名としての検証・無害化の例は>>156をご覧ください)

:ID:D00EWOwN

>>160
管理人様

大変丁寧なご回答ありがとうございました。
また、返事が遅くなって申し訳ありません。

「対応しているファイル名の範囲(一覧)」は、
ご想像通りカバーできる文字コードの範囲という意味です。
Encodingクラスと同じ範囲とのご回答、了解しました。

かなり複雑なコーディングで、迂闊に手を出せませんが、
状況が整えば、使い勝手の良い、最新のライブラリで
シンプルにコーディングし直したいと考えています。

その際には、またご厄介になるかもしれません。

取り急ぎ以上簡単ですが、今後ともよろしくお願いします。

:ID:5Q+S5EsZ

管理人様

ご無沙汰しております。
ライブラリの最新化は未だ実現できておりませんが、
ここ数週間でトラブルが繰り返されているため、ご教示ください。

メールサーバのIMAPサービスのCPU使用率が100%近くになってしまう事象が起きています。

この場合、プログラムから呼び出すSmdn.Net.Imap4.Clientライブラリによって
要求されるIMAP要求に対して、サーバがIMAP応答を返さなくなることが考えられますが、
ライブラリ側では、タイムアウトなどを検知して、プログラムに返す動きはありますでしょうか?

それとも、サーバがIMAP応答を返すまで、待ち続けることになるのでしょうか?

以上簡単ですが、ご教示いただければ幸いです。

:ID:89zanl/k

>>162
Smdn.Net.Imap4.Clientの要求が原因でサーバーのCPU使用率が上がるのではなく、
上がっている状態の時にSmdn.Net.Imap4.Clientで要求を行った場合について、
ということでしょうか。

その状況であればImapClient.ReceiveTimeoutプロパティを設定することで対応できるかと思います。
一定時間応答がなければTimeoutExceptionをスローさせることができます。

ただし、一度TimeoutExceptionとなったImapClientは継続して使用することはできないので
再度接続し直す必要があります。

詳しくはドキュメントの以下の項をご参照ください。

接続・切断に関するメンバ
http://smdn.jp/works/libs/Smdn.Net.Imap4.Client/docs/#connection_authentication_reference
非同期操作の中断とタイムアウト
http://smdn.jp/works/libs/Smdn.Net.Imap4.Client/docs/#async_operations_cancellation

:ID:5Q+S5EsZ

>>163
さっそくのご回答ありがとうございます。

Smdn.Net.Imap4.Clientの要求と、CPU使用率の上昇
(具体的には、IMAPサービスのCPU使用率が90%以上)
とは、ほぼ同時に起こっているので、どちらとも言い難い状況です。

サーバのCPU使用率の上昇中に、要求を行う場合、
ImapClient.ReceiveTimeoutプロパティを設定することで、
タイムアウトを検知できるとのこと、了解しました。

取り急ぎ、以上ありがとうございました。

:ID:5Q+S5EsZ

>>163
いつもお世話になっております。

ちなみに、ImapClient.ReceiveTimeoutプロパティに
設定する値について、推奨値などございますか?

現在メールサーバ(ExpressMail)のSMTPの
ソケット受信タイムアウトが180秒のため、
180,000(ミリ秒)を設定しようとしています。

アドバイスをいただければ幸いです。

:ID:D00EWOwN

いつもお世話になっております。
引き続きの質問で恐縮です。

繰り返し処理におけるメッセージのメッセージ番号、UIDの割り振りについて教えてください。
以下のようなコードで、"INBOX"のメッセージを"workFolder"にコピーする場合、

client = new ImapClient(new Uri("imap://user@localhost/")); 
client.Connect("password");
openedMailboxInfo = client.OpenInbox();
foreach (ImapMessageInfo message in openedMailboxInfo.GetMessages()) {
	message.CopyTo("workFolder");
}

1.「foreach」の中でメッセージは"INBOX"内の
  「メッセージ番号」「UID」どちらの
  「昇順」/「降順」に処理されますか?

2."workFolder"内のメッセージには、コピーされた順に
  どのような「メッセージ番号」と「UID」が振られますか?
  
以上、ご教示いただければ幸いです。

:ID:89zanl/k

>>165
ライブラリとしては特に推奨する値はありません。
サーバーの設定にあった値・負荷がかからない値であれば任意に設定いただけます。

>>166

1.GetMessages()の処理順について

処理される順序は不定です。

GetMessages()はサーバーからのレスポンスを受信した順でImapMessageInfoを列挙します。
レスポンスの順序は多くのサーバーではメッセージ番号順(昇順)になると思いますが、
IMAPの仕様としては明確な順序は定められてはいません。
そのため、確実にメッセージ番号順・UID順で処理したい場合はソートを行う必要があります。

// OrderByでメッセージ番号順に並べ替えてから列挙する
foreach (ImapMessageInfo message in openedMailboxInfo.GetMessages().OrderBy(m => m.Sequence))
  ;

// あるいは一旦Listに変換してからメッセージ番号順に並べ替えてから列挙する
var messages = openedMailboxInfo.GetMessages().ToList();

messages.Sort((mx, my) => mx.Sequence - my.Sequence);

foreach (ImapMessageInfo message in messages)
  ;

2.コピー時に割り振られる番号について

コピーを行う(=メールボックスにメールを追加する)場合、追加されるメールには、
メールボックス内のメールに割り振られている最も大きいメッセージ番号に+1した
メッセージ番号が割り振られます。
(メールがない場合は1が割り振られます)

一方UIDは+1した値になるとは限りませんが、メールボックス内で最後に
割り振られたUIDよりは大きい番号が割り振られます。

参考までに、ImapOpenedMailboxInfo.NextUidプロパティを参照すると、次回メールボックスに
メールが追加される際に割り振られるUIDを知ることができます。

5.2.1.5 メールボックスの状態の取得
http://smdn.jp/works/libs/Smdn.Net.Imap4.Client/docs#operation_reference_mailbox_status

:ID:D00EWOwN

>>167
いつもお世話になっております。
迅速で丁寧なご説明、ありがとうございました。

内容について了解しました。
メールの同一性の判断には、Message-IDを取得して
行うようにします。

ありがとうございました。

:ID:KgFU0J6k

管理人様、はじめまして。ぽこりんと申します。
数日前より Smdn.Net.Imap4.Client 最新版を利用させていただいております。
IMAPでのメール受信が簡単にでき、大変助かっております。

Gmail と Yahoo! メールで試していて、疑問と解決できない点がございますので、
お時間のあるときにご確認いただけましたら幸いです。

ドキュメントサンプルを参考に、Visual Studio 2012 で下記のコードで検証しています。

var client = new ImapClient(new Uri("imaps://" + userId + "@imap.mail.yahoo.co.jp/"));
client.Connect(password);
// INBOXを開く
using (ImapOpenedMailboxInfo inbox = client.OpenInbox())
{
    Console.WriteLine(inbox.UnseenMessageCount);  //(*1)
    // INBOXにあるすべての未読(unseen)メッセージを列挙
    foreach (ImapMessageInfo message in inbox.GetMessages(ImapSearchCriteria.Unseen))  //(*2)
    {
        // UIDを表示
        Console.WriteLine(message.Uid); //(*3)
    }
}

上記を Yahoo!メール、Gmail でテストすると、

1) 未読があっても(*1)では 0(件)と表示されますが、(*3)では正しく未読のUIDが表示されます。
2) Yahoo!メールに限り、(*2)の inbox.GetMessages(ImapSearchCriteria.Unseen) で例外エラーが発生します。
ImapSearchCriteria の他の指定でもすべて同じエラーになります。

型 'System.NullReferenceException' のハンドルされていない例外が Smdn.Net.Imap4.Client.dll で発生しました

追加情報:オブジェクト参照がオブジェクト インスタンスに設定されていません。

問題の解決方法をアドバイスいただければ幸いです。
何卒よろしくお願いいたします。

:ID:89zanl/k

>>169
Smdn.Net.Imap4.Clientをご利用頂きましてありがとうございます。
以下、ご質問について回答します。

1)UnseenMessageCountが0となる
サーバーによっては、メールボックスを開く際に未読メッセージ数を通知しないものがあります。
そのため、メールボックスに未読メッセージが存在する場合でもUnseenMessageCountが0となることがあります。
(このことはドキュメントに未記載だったので追記しておきました)

このようなサーバーでは、GetMessagesなどで実際に検索・列挙してみるまで未読メール数を
知ることができません。

2)GetMessages()でNullReferenceExceptionとなる
検証したところ、Yahoo!メールのサーバーが返送してくるレスポンスが不正な書式と
なっていることが原因であることがわかりました。
(具体的には、SEARCHコマンドのレスポンス末尾に余計な空白が含まれる)

ライブラリ側ではなくサーバー側の問題なので、ライブラリ側の修正対応は行いません。
先ほどYahoo!メール側には問題内容の報告を行っておきましたので、恐れ入りますが
Yahoo!メール側で問題が改善されるのをお待ちいただければと思います。

なお、ソースコードを修正することでも問題を回避することができます。
その場合は、以下の箇所を修正してください。
ただし、Yahoo!メールでは問題なく動作することは確認していますが、
他のメールサーバーでも正しく動作するかどうかは未検証です。

  • ファイル:Smdn.Net.Imap4.Client/Smdn.Net.Imap4.Protocol.Client/ImapDataResponseConverter.cs
  • メソッド:FromSearchOrSort
      else {
        // 修正前のコード
        //return new ImapMatchedSequenceSet(ImapSequenceSet.CreateSet(uidSet,
        //                                                            Array.ConvertAll<ImapData, long>(data.Data, ImapDataConverter.ToNonZeroNumber)));

        // 以下修正後のコード
        var texts = data.Data;

        if (texts[texts.Length - 1].GetTextLength() == 0)
          Array.Resize(ref texts, texts.Length - 1);

        return new ImapMatchedSequenceSet(ImapSequenceSet.CreateSet(uidSet,
                                                                    Array.ConvertAll<ImapData, long>(texts, ImapDataConverter.ToNonZeroNumber)));
      }
:ID:KgFU0J6k

管理人様

早々にご回答を頂戴し、誠にありがとうございます。

1)UnseenMessageCount の件、了解致しました。

2)ご提示いただいたソースコードでビルドしたDLLで、正しく動作することを確認いたしました。Yahoo!メールでの利用を考えていますので、当面こちらで検証してみます。

こんなに早く問題が解決出来て、感謝の気持ちで一杯です。

今後ともよろしくお願いいたします。

:ID:KgFU0J6k

管理人様

お世話になっております。先日はご回答いただきありがとうございました。

引き続き、YahooメールのIMAPで検証しております。

var client = new ImapClient(new Uri("imaps://" + userId + "@imap.mail.yahoo.co.jp/"));
client.Connect(password); //(*1)

(*1)の部分で、パスワードの先頭文字が{の時に ImapAuthenticationException をキャッチします。

authentication failed "Invalid command or arguments" (result code:Bad)

先頭文字を英数字に変更することでエラー回避できました。

先頭文字に記号を使うのはIMAPのルール違反か分かりませんが、一応、

事象をご報告させていただきます。

今後ともよろしくお願いいたします。

:ID:89zanl/k

>>172
ご報告頂きありがとうございます。

{はIMAPコマンドの引数では特別な意味を持つため、エスケープせずに送信すると
例外に含まれているメッセージにあるように、コマンド解析エラーとなります。
(平文でのログイン時にはエスケープせずにパスワードを送信します)

なお、IMAPの仕様では、(平文ログインの)パスワードに{を使うことは許可されていません。

:ID:7fF8n1/J

管理人様

はじめまして。サイパンと申します。
Smdn.Net.Imap4.Clientを利用させていただいております。
ありがとうございます。

現在、Yahoo!メールのIMAPに対し、メールボックスの名前と通数の取得を
以下のコードで検証をしておりますが、その中で質問があります。

using (ImapClient client = new ImapClient("imap.mail.yahoo.co.jp", 993, true, "hogehoge@localhost"))
            {
                client.Connect("fugafuga");
                foreach (ImapMailboxInfo mailboxInfo in client.GetMailboxes(ImapMailboxListOptions.RequestStatus))
                {
                    Console.WriteLine("MailboxName:{0}, MailCount:{1}", mailboxInfo.FullName, mailboxInfo.ExistMessageCount);
                }
            }

1)INBOXの名前がInboxからINBOXとなる
ログからIMAPのコマンド実行結果を確認すると、
Yahoo!メールの場合、INBOXの名前は、"Inbox"と返ってきてるようですが
FullNameで取得した結果は、全て大文字の"INBOX"と
なりますが、これは仕様でしょうか?
2)INBOXのメール通数が0となる
ログからIMAPコマンドの実行結果を確認すると、
正しいメール通数が返ってきているようですが、
ExistMessageCountは、常に0となってしまいますが、
解決策はありますでしょうか?
(他のメールボックスは正しい値が入っています。)

別件として、もう1点お教え願いたいのですが、1回のFETCH時の
サイズを決定していると思われる、
ImapFetchMessageBodyStreamクラスのDefaultFetchBlockSizeですが、
この値を変更する場合、ソースコードを直接変更するしか方法がないでしょうか?

以上、ご教示いただけると大変ありがたいです。

:ID:89zanl/k

>>174
Smdn.Net.Imap4.Clientをご利用頂きありがとうございます。

ご質問頂いた内容のうち、(2)に関してはライブラリの不具合だったため、
先ほど修正を施したversion 1.13をリリースいたしました。

変更点の詳細は以下をご確認ください。
http://smdn.jp/works/libs/Smdn.Net.Imap4.Client/releases/#changes_v1.13

また(1)についてですが、この動作は仕様です。
IMAPではINBOXに限りメールボックス名の大文字小文字の違いが無視されますが、
本ライブラリではINBOXを大文字に統一した上で管理しているため、
ImapMailboxInfo.FullNameなどのプロパティは常に大文字の"INBOX"を返す動作となっています。

最後に、ImapFetchMessageBodyStream.DefaultFetchBlockSizeはご推察の通り
1回のFETCHコマンドで取得する最大サイズとなっています。
現状この値を変更する場合はソースを直接編集し、コンパイルしなおす必要があります。
(コード上から実行時に変更することはできません。 また今のところそのような操作を
可能にする予定は考えていません。)

なお、この値はファイルが添付されているなど巨大なメールを受信する際に
LOH(Large Object Heap)の断片化を引き起こさないようにするために
設定している値です。 変更される際はその点にご留意ください。

:ID:+PcLrsR9

管理人様

早速のご回答と修正、LOHのアドバイスまでいただき、
ありがとうございます。

アドバイスを基に、使わせていただきます。

ご参考ですが、Yahoo!メールの場合、
SEARCHコマンドのクエリBEFOREとSINCEは、問題ないのですが、
SENTBEFOREとSENTSINCEは、
Yahoo!から正しいメールが返ってこないようです。
また、NAMESPACE拡張も、RFCとして正しいのか調べ切れておりませんが、
個人名前空間のセパレータにNILが入っており、
使わせていただいているライブラリが
エラーを出力しておりました。
Yahoo!メールのIMAPは、ちょっと特殊かもしれません。

今後とも、よろしくお願いいたします。

:ID:89zanl/k

>>176
ご報告ありがとうございます。
SENTBEFORE/SENTSINCEの動作は把握していないものでした。
NAMESPACEの問題と合わせて、ライブラリ側での対処が必要かどうか
今後検証します。

Yahoo!メールに関しては、SEARCHレスポンスでもRFC違反があることを
認識していますので、参考としてお知らせしておきます。
(詳細と対処法は>>170、この問題はYahoo!メール側に報告済みなのですが、
現在のところ修正されていないようです)

:ID:7fF8n1/J

管理人様

お世話になります。
早速version 1.13を使用させていただき、
INBOXのメール通数が取得できることを確認させていただきました。
ありがとうございます。

version1.12では、発生しなかったのですが、
ImapSearchCriteriaでSince又はBeforeを使用してメッセージを取得しようとすると、
ImapMessageInfoList.csの110行目でsequenceOrUidSetがNullとなり、
NullReferenceExceptionが発生するようになりました。

可能であれば、解決策をご教示いただけるとありがたいです。

よろしくお願いいたします。

:ID:89zanl/k

>>178
ImapMessageInfoList.csでNullReferenceExceptionが発生する件は
>>170の(2)と同じ事象と思われます。
恐れ入りますが、>>170の(2)にあるとおりにソースコードを修正してください。

以下は、先にご報告頂いた問題に関してです。
SENTBEFORE/SENTSINCEについてはサーバー側の問題のようなのでYahoo!メール側に報告しておきます。
NAMESPACE拡張についてはライブラリ側の不具合なので、今後のバージョンで修正します。

:ID:7fF8n1/J

管理人様

お世話になります。

>>170を見落としておりました。
お手数をおかけし、申し訳ありませんでした。

ご丁寧な対応、誠にありがとうございました。

:ID:89zanl/k

>>171 ぽこりん様
>>180 サイパン様

Yahoo!メールにて検索時にNullReferenceExceptionがスローされる問題(>>170の(2))について
問い合わせた結果、本日Yahoo!側より「現在の挙動は仕様であり、また修正の予定は無い」旨を
回答頂きました。

これを受け、暫定対処として>>170の(2)に相当する修正を盛り込んだ
version 1.14をリリースいたしました。

その他変更点の詳細は以下をご確認ください。
http://smdn.jp/works/libs/Smdn.Net.Imap4.Client/releases/#changes_v1.14

合わせて、Yahoo!メールでは一部モバイル端末を除いて、本ライブラリを含めた
各種メールソフトはサポート対象外となっていることをお知らせしておきます。
http://www.yahoo-help.jp/app/answers/detail/a_id/77496/p/622

すでにご報告頂いているSENTBEFORE/SENTSINCE(>>176)以外にも、本ライブラリで
提供している機能のすべてが正しくするとは限らない・IMAPの仕様通りの結果とならない
可能性がある点に留意した上でご利用頂ければと思います。

今後類似の不具合が発覚した場合、ライブラリ側で対応できるようであれば対応しようと思います。

:ID:PBQ1a2ws

管理人様

その後の状況をご報告いただき誠にありがとうございます。

Yahoo!の仕様を留意した上で利用させていただきたく思います。

今後ともよろしくお願いいたします。

:ID:7fF8n1/J

管理人様

お世話になります。

わざわざご報告いただき、ありがとうございます。

私も十分留意の上、使わさせていただきます。

今後とも、よろしくお願いいたいします。

:ID:7fF8n1/J

管理人様

お世話になります。
また、ご質問があり、投稿させていただきます。
よろしくお願いいたします。

foreachにて、ImapMessageInfoListからImapMessageInfoを取り出せる数について、
稀にですが、ImapMessageInfoList.ToArray().Lengthと比較し、少ない事象が
発生しております。
具体的には、以下のコードとなります。

List<long> uidList = ...// あらかじめ取得済みの10個のUID

ImapOpenedMailboxInfo mailbox = client.OpenMailbox(mailboxName, true);
ImapMessageInfoList messageinfoList 
    = mailbox.GetMessages(uidList, ImapMessageFetchAttributeOptions.StaticAttributes);

Console.WriteLine("ARRAY_LENGTH:{0}", messageInfoList.ToArray().Length); //10が表示

foreach (ImapMessageInfo message in messageInfoList)
{
    Console.WriteLine("Subject:{0}", message.EnvelopeSubject); //稀に10個表示されない場合がある
}

Subjectが10個表示されないときのimap.logには、
IMAP Verbose: 2 : 2015-02-27T22:17:07.9073699+09:00 CID:2 [183.79.79.172:993] S: * NO message error; can't fetch that message13
と記録されております。
メールボックス内には、メールが10通ありますので、
サーバ側にて、何かしらの不具合により、たまたま1通だけFETCHできず、
このような事象が発生しているようです。
この事象が発生した場合に、Exception等により検知する仕組みはありますでしょうか?

また、上記コードで、EnvelopeSubjectを取得しておりますが、
メールのサブジェクトが、Base64等でエンコードせずに、
ISO-2022-JP又はSHIFT_JIS等で直接日本語が記載されていた場合、
デコードは、失敗する認識でよろしいでしょうか?
この場合、EnveloepSubjectには、サブジェクトをUnicodeを前提として
変換格納しているのでしょうか?

メールによっては、サブジェクトがエンコードされていないため、
EnvelopeSubjectが文字化けしており、この文字化けを
元に戻すために、質問させていただきました。

よろしくお願いいたします。

:ID:QwCqbw4o

>>184
分けて回答します。

1)FETCHできる件数が少なくなる問題について
先に対処法ですが、ご報告頂いた事例の場合Exceptionとして検知することができません。
代わりに、以下のようにUIDごとにGetMessageByUidメソッドを呼び出すようにしてください。

List<long> uidList = ...//

ImapOpenedMailboxInfo mailbox = client.OpenMailbox(mailboxName, true);

foreach (var uid in uidList)
{
  try
  {
    // GetMessageByUidでUIDに対応するImapMessageInfoをひとつずつ取得する
    var message = mailbox.GetMessageByUid(uid, ImapMessageFetchAttributeOptions.StaticAttributes);

    Console.WriteLine("Subject:{0}", message.EnvelopeSubject);
  }
  catch (...)
  {
    ...
  }
}

以下は原因と対策について。
(問題の整理とメモを兼ねているので、不要なら読み飛ばして頂いて問題ありません)

頂いたコードとログを見る限り、foreachの際に送信されるFETCHコマンドに対して、
以下のようなレスポンスが返されていると思われます。

C: XXXX UID FETCH ( ... )
S: * XX FETCH ...
S: * XX FETCH ...
S: * NO message error; can't fetch that message13
S: * XX FETCH ...
     :
     :
S: XXXX OK FETCH completed

この場合の"* NO ..."のレスポンスはエラーではなくあくまで警告として
扱われるものであるため、現在の実装ではこのような警告のレスポンスに
対しては何も行いません(例外もスローしない)。

この事象のように要求したコマンドを完全に実行できない場合は、本来なら
"XXXX OK ..."ではなく"XXXX NO ..."が最終的に返されるべきであり、
これもYahoo!メール特有の動作と思われます。

現状では、このような要求を行わないようクライアント側で対処する必要があります。
再現する条件が判明したらYahoo!側に報告しようとは思いますが、
>>181の件と同様に修正はなされないかもしれません。

:ID:QwCqbw4o

>>184
続き。

2)BASE64等でエンコードされていないサブジェクトの復元について
SubjectヘッダがBASE64等でエンコードされていない場合、EnvelopeSubjectには
ヘッダの値のバイト表現をそのままstringに変換した生の値が格納されます。
(Encoding.GetStringではなくString(SByte*)コンストラクタで直接文字列化した
値が格納されているとイメージしてもらえればと思います)

BASE64等でエンコードされておらず、かつ、もとの文字コードが既知の場合は、
次のようなコードによって復元することができます。

ImapMessageInfo m;

// Subjectヘッダの値のバイト表現を取得する
var bytes = Smdn.Net.NetworkTransferEncoding.Transfer8Bit.GetBytes(m.EnvelopeSubject);

// 任意の文字コードで文字列化する
var subject = System.Text.Encoding.GetEncoding("SHIFT_JIS").GetString(bytes);

Console.WriteLine("Subject:{0}", subject);
:ID:7fF8n1/J

管理人様

お世話になります。

早急で詳細な回答、まことにありがとうございます。

事象が稀にしか発生せず、その条件も不明のため、
検証が思うようにいかず、困っておりましたので、
解説までいただき、大変助かります。

管理人様からお教えいただいた方法で、
試してみたいと思います。

私からも、気づいた点等あれば、
またご報告させていただきます。

ありがとうございました。

:ID:x85it0TA

管理人様

C# でのメール認証プログラムを検討していたところ、こちらのサイトにたどり着きました。
Smdn.Net.Pop3.Client や Smdn.Security.Authentication.Sasl の利用を考えているのですが、
こちらのライブラリは商用利用可能でしょうか?

:ID:x85it0TA

管理人様

丁寧で素早い返答ありがとうございます。
まだ検討段階ですがお世話になるかもしれません。
今後ともよろしくお願いします。

:ID:UzVd9h+O

管理人様
はじめまして

件名が euc-jp の Qエンコードだと文字化けしてしまします。
こちらのコードの問題なのか、対応外なのかご教授いただければ幸いです。
コードは下記のようにしています。

foreach (var message in client.GetMessages()) {
	var mail = message.ReadAs<Mail>(Mail.Load);
	Console.WriteLine(mail.Subject);
}        
:ID:/b5NIECk

>>191
ライブラリをご利用いただきありがとうございます。

euc-jp の Qエンコードには対応しています。
また使用されているコードにも問題はありません。

ヘッダの内容に問題があると思われますので、差し支えなければ
読み込もうとしているメールのSubjectヘッダの内容を書き込んで
頂けますでしょうか。

:ID:UzVd9h+O

>>192
管理人様
返信ありがとうございます。

ご指摘の通り、euc-jp の Qエンコードされた件名でも
文字化けするものとしないものとがありました。
文字化けした件名は下記になります。

Subject: =?euc-jp?Q?=B1=FE=B1=E7=B4=F3=C9=ED=B6=E2=BF=BD=B9=FE=A4=DF=A5?=
 =?euc-jp?Q?=D5=A5=A9=A1=BC=A5=E0?=

よろしくお願いします。

:ID:/b5NIECk

>>193
ありがとうございます。

ご提示頂いたSubjectヘッダの内容に問題があり、この場合、
文字化けしてしまうのが本ライブラリの動作となります。

具体的には、Subjectヘッダ中にあるQエンコードされた2つの文字列が、
マルチバイト文字の途中で分割されていることが原因です。
これはRFCの規定に反した正しくないエンコード方法で、本ライブラリでは
想定していないため必ず文字化けします。

原因についてはこちらの解説が詳しいです。
http://d.hatena.ne.jp/unarist/20150321/1426933207

現状では、このような場合に発生する文字化けをライブラリ側で検出したり、
回避したりする方法は用意していません。 今後、これをどのように扱うか
検討します。

恐れ入りますが、メールの送信側で正しくエンコードするように対処してもらうか、
このような場合に文字化けすることをご理解頂いた上でご利用いただければと
思います。

:ID:UzVd9h+O

>>192
管理人様
返信ありがとうございます。

エンコードが正しくなく、現在の動きが仕様ということで
承知いたしました。
ただ、Outlookでは正常に表示されるので気になった次第です。

今後のアップデートで対応して頂けると嬉しいです。

:ID:UiXMWVVZ

不具合あり

:ID:6971EjnS

>>196
100 Continue

:ID:hHO83Hsp

大変ご無沙汰しております。
先般は大変お世話になりました。

いつも、便利なライブラリを利用させていただき、
ありがとうございます。

今回は、メールの削除処理の効率化について教えてください。

現在、処理の終わったメールのMessage-IDを配列に保存しておき、
メールフォルダから、GetMessagesでメール一覧を取得したのち、
配列のMessage-IDに一致するものにDeletedフラグをつけています。

これでは、メール件数だけIMAP通信が発生してしまうため、
最初からMessage-ID配列に含まれるメール一覧を取得し、
まとめて、Deletedフラグを付けられたらいいのに、と考えています。

具体的には、
ImapMessageInfoList messages

= openedMailboxInfo.GetMessages
  (ImapSearchCriteria.Header("Message-ID", msgIdList[]);

messages.AddFlags(ImapMessageFlag.Deleted);
のようなことをしたいと考えています。

<質問1>
上記のように、msgIdList[]のように文字列の配列は指定できないようですが、
何か他の検索条件の設定方法はありますでしょうか?

<質問2>
Message-IDの配列による検索が難しいのであれば、
UIDの配列でも構わないと考えています。
実際、
ImapMessageInfoList messages

= openedMailboxInfo.GetMessages
  (ImapSearchCriteria.Uid(ImapSequenceSet uidSet);

messages.AddFlags(ImapMessageFlag.Deleted);
のような方法があるようですが、
処理が終わったメールのUIDをこのuidSetに、
追加登録していく具体的な方法を教えてください。

以上、よろしくお願いいたします。

:ID:QEeocG2o

>>198
追加情報です。
<質問1>について、ドキュメント・サンプルより、
以下の方法があることを見つけました。

ImapSearchCriteria isc = ImapSearchCriteria.New;
foreach (string msgId in m_msgIdList){

 isc |= ImapSearchCriteria.Header("Message-ID", msgId);

}
ImapMessageInfoList messages = openedMailboxInfo.GetMessages(isc);
messages.AddFlags(ImapMessageFlag.Deleted);

ただし、StackOverFlowExceptionがライブラリ内部で発生することがあるようです。
(条件が6000件だった場合)
回避策がありましたら、ご教示ください。

上記の方法では、問題があるようでしたら、
<質問2>の方法も考える必要がありますので、
こちらの質問についても引き続きよろしくお願いします。

:ID:5U/9Z0Js

>>198
>>199

引き続きライブラリをご利用いただいてありがとうございます。

先に目的を実現するための方法を書くと、<質問2>のようにUIDを用いる必要があります。

UIDを格納したlong型の配列をImapOpenedMailboxInfo.GetMessages()メソッドに渡すことで、
そのUIDに該当するメッセージを取得することができます。
その後、AddFlags(ImapMessageFlag.Deleted)することにより、まとめてDeleteフラグを付けることができます。

以下で個々の質問に対する回答と実装例を掲載します。

<質問1>への回答

質問にて指摘されているとおり、ImapSearchCriteria.Headerメソッドでは配列(=複数の値)を検索条件として
設定することはできません。 これはIMAPのSEARCHコマンドの仕様に基づく制限です。

従って、複数のMessage-IDからそれに該当するメッセージすべてを取得したい場合は、>>199
書かれているようにOR演算子によって条件式を結合する必要があります。
求められている動作を実現するコードも>>199のとおりとなります。

一方、StackOverFlowExceptionは発生するのはライブラリ側の問題で、まさに今回のように多数の条件式を
連結しようとする際に起こり得ます。 これは、コマンド送信の際に条件式の展開が再帰的に
行われることが原因です。 現時点での実装では、特に条件式が長大になるような場合において
StackOverFlowExceptionを回避することは難しいです。

なお、仮にStackOverFlowExceptionが起きない程度に処理するメール数を減らした場合でも、
サーバー側にかかる検索負荷を考慮するとこの方法は最善ではありません。
UIDが既知の状態であれば<質問2>の方法の方がよいでしょう。

<質問2>への回答

冒頭で書いたとおり、UIDを格納したlong型の配列(あるいはList<long>など)を、ImapOpenedMailboxInfo.GetMessages()メソッドに
渡すことにより、そのUIDに対応する全てのメールを取得することができます。
(ImapSearchCriteria.Uidおよび、ImapSequenceSetを使う必要はありません)

具体的なコードは次のようになります。 例示の簡単化のため配列ではなくList<long>を用いています。

// メールボックスを開く
ImapOpenedMailboxInfo openedMailboxInfo = client.OpenMailbox("MyMailbox");

// 処理が終わったメールのUIDを保持しておくためのList<long>を作成
List<long> deleteUidList = new List<long>();

// メールボックスから「処理」したいメールを取得する
foreach (ImapMessageInfo msg in openedMailboxInfo.GetMessages(ImapSearchCriteria.All)) {
  /*
   * msgに対する「処理」を行う
   */

  // 「処理」が終わった場合、そのメールのUIDをdeleteUidListに保持しておく
  if ( ... ) {
    deleteUidList.Add(msg.Uid);
  }
}

// 削除したいメール(処理済みのメール)の一覧を取得する
ImapMessageInfoList messages = openedMailboxInfo.GetMessages(deleteUidList);

// 取得した一覧に対して、まとめてDeletedフラグを付ける
messages.AddFlags(ImapMessageFlag.Deleted);
:ID:3E/r50lU

>>200

さっそくのご回答ありがとうございます。

UIDを格納したlong型の配列またはListを
ImapOpenedMailboxInfo.GetMessages()メソッドに
渡すことで、一括取得ができること、了解しました。

便利な機能ですね。
さっそく利用しようとしたのですが、
当方の処理の都合で問題があることがわかりました。

処理の流れは以下のとおりです。

1.INBOXフォルダのメールをすべて、作業フォルダにコピーする
  (ただし、削除フラグ有りのものを除く)
2.作業フォルダのメールについて、繰り返し処理を行う。
  処理済みのメールのMessage-IDを配列に追加する
3.INBOXフォルダのメールのうち、処理済のものを削除する

2の処理の部分で、取得できるUIDは、作業フォルダのものなので、
このUIDでは、3の処理で、INBOXフォルダのメールが削除できず、
メールを特定する情報として、Message-IDを使っています。

INBOXフォルダから作業フォルダにメールをコピーする際、
双方のフォルダのUIDを紐付けることができればいいのですが。
実際、1の処理は、以下のようにしています。

openedMailboxInfo = client.OpenInbox();
ImapMessageInfoList undeletedMessages = openedMailboxInfo.GetMessages(ImapSearchCriteria.Undeleted);
undeletedMessages.CopyTo(workFolderName);

この際のIMAPログを出力したところ、以下のようになっています。

C: 0009 SELECT INBOX
S: * FLAGS (\Answered \Flagged \Deleted \Seen \Draft $MDNSent)

1800 EXISTS

0 RECENT

OK [UIDVALIDITY 1470805234] UIDs valid

OK [UIDNEXT 19667] Predicted next UID

OK [UNSEEN 1] message 1 is first unseen

OK [PERMANENTFLAGS (\Answered \Flagged \Deleted \Seen \Draft $MDNSent)] limited

0009 OK [READ-WRITE] select completed
C: 000a SEARCH UNDELETED
S: * SEARCH 1 2 3 ・・・ 1800
000a OK search completed
C: 000b COPY 1,2,3,・・・,1800 201608101700 ←作業フォルダ名
S: 000b OK [COPYUID 1470816059 17867:19666 1:1800] copy completed

双方のフォルダのUIDが出力されているようです。

<質問3>

openedMailboxInfo = client.OpenInbox();
ImapMessageInfoList undeletedMessages = openedMailboxInfo.GetMessages(ImapSearchCriteria.Undeleted);
undeletedMessages.CopyTo(workFolderName);

上記のようにコピーを実施する際、
コピー元とコピー先のUIDを紐づいた形で
取得する方法がありますでしょうか?

以上、よろしくお願いします。

:ID:CBAR8jVx

>>201

追加情報です。

とりあえず、<質問3>の方法を使わずに、
処理を早くすることができましたので、ご報告します。

<質問3>につきましては、いったんご放念ください。

まず、INBOXフォルダのメールをすべて、作業フォルダにコピーする際に、
UIDとMessage-IDの関連を連想配列で紐付けておきます。

       ImapMessageInfoList undeletedMessages = openedMailboxInfo.GetMessages(ImapSearchCriteria.Undeleted);
       //INBOXのうち、削除フラグのないメールをすべて取得する
       foreach (ImapMessageInfo message in undeletedMessages) { //対象メールについて繰り返し
           KeyValuePair<String, String>[] val = message.GetHeader("Message-ID");
           string msgId = val[0].Value;
           long inboxUid = message.Uid;
           UidMsgID.Add(inboxUid, msgId); //INBOX上のUIDとMessage-IDのペアを連想配列に格納する
       }
       undeletedMessages.CopyTo(workFolderName); //メールをまとめてコピー【IMAPのCOPYコマンドは1回のみ】
       countCopy = undeletedMessages.Count();

削除する際には、Message-IDを元に、
連想配列か元となるUIDを見つけ、UIDの配列に追加して、
ご教示のあった方法で、まとめて削除フラグを付与します。

       foreach (long uid in UidMsgID.Keys) {
           string msgId = (string)UidMsgID[uid];
           if (m_msgIdList.Contains(msgId)){
               deleteUidList.Add(uid);
           }
       }
       // 削除したいメール(処理済みのメール)の一覧を取得する
       ImapMessageInfoList messages = openedMailboxInfo.GetMessages(deleteUidList);
       // 取得した一覧に対して、まとめてDeletedフラグを付ける
       messages.AddFlags(ImapMessageFlag.Deleted);

以上簡単ですが、今後ともよろしくお願いします。

:ID:5U/9Z0Js

>>201
>>202

目的とする処理に関してはすでに実装まで解決されておられるようですが、
コピー前後でのUIDの紐付けに関して、以下参考としてご覧ください。

作業フォルダへのコピー時のレスポンスは、ご推察のとおり「コピー元のフォルダにあるUIDが
17867〜19666のメールを、コピー先フォルダにコピーし、UIDとして1〜1800を割り当てた」という
意味になっています。

S: 000b OK [COPYUID 1470816059 17867:19666 1:1800] copy completed

従って、プロトコル上はこれを元にしてコピー前後のUIDを紐付けできるようになっています。

しかし、現状のCopyToメソッドではこの情報をもとにしたUIDの紐付けは行えません。
これはCOPYUIDがIMAPの基本機能ではなく拡張機能であり(=この情報を返送しないサーバーも
存在しうる)、ライブラリの機能としてもいかに実装するか決めかねているためです。

将来的にはコピー前後で紐付けできるようにするか、少なくとも可能ならコピー後のUIDを
取得できるように改善しようと考えていますが、現時点では、コピー前後のメールを紐付け
する場合はMessage-IDによって同定していただく必要があります。

:ID:F+6R0FE3

>>203
ご参考情報をいただき、ありがとうございました。
たしかに、ExpressMailでは、Move拡張コマンドには対応してないようです。

いずれにしましても、GetMessages(UID配列)により、
対象のメールをまとめて取得することができ、
処理時間が劇的に改善でき、嬉しく思っています。

以上簡単ですが、今後ともよろしくお願いします。

:ID:ucRfKAEb

>>204
いつもお世話になっております。

既知のUIDValidityとUIDから、メッセージを取得する方法について、
処理時間の短縮を検討しています。

現在は、GetMailBoxesメソッドにより、全フォルダを取得した上、
各フォルダについてループさせ、UIDValidityが一致した場合、
そのフォルダから、GetMessageByUidによりメッセージを取得しています。

 foreach (ImapMailboxInfo mailboxInfo in client.GetMailboxes()) {
   ImapOpenedMailboxInfo openedMailbox = client.OpenMailbox(mailboxInfo.FullName);
   if (openedMailbox.UidValidity.ToString().Equals(uidValidity)) {
     ImapMessageInfo message = openedMailbox.GetMessageByUid(int.Parse(uid));
     //メッセージに関する処理
     break;
   }
 }

上記の処理においてフォルダをループさせずに、
直接、UIDValidityから対象フォルダにアクセスする方法はないでしょうか?

 ImapMailboxInfo mailboxInfo in client.GetMailbox(uidValidity);
 

GetMailboxに指定できる引数は、フォルダ名なので、上記は失敗しますが、
このような形で、できないでしょうか?

以上、よろしくお願いします。

:ID:5U/9Z0Js

>>205

UidValidity値は、IDやエイリアスのようなメールボックスを特定するための値ではないため、
UidValidityから対応するメールボックスを取得するような目的には使えません。
ライブラリとしてもそういった機能は提供していません。

従って、挙げられたコードのようにメールボックスをひとつずつ検査してUidValidityを
チェックしていく必要があります。

(UidValidity値は、前回アクセスした時のUIDが現在も有効であるか、つまりメールとUIDの
対応関係が変化していないかどうかをチェックするための値です。 サーバー側の動作により
メールボックスのUidValidity値が変わる場合があります。)

ところで、コード中ではUidValidityの取得のために各メールボックスをOpenしているように
見えますが、GetMailboxesではオプションでメールボックスのステータスも同時に取得することができます。

これによりUidValidityの検証のためにひとつずつメールボックスをOpenする必要がなくなるため、
処理時間の短縮が図れるかもしれません。

 // すべてのメールボックスと、そのステータスを同時に取得する
 foreach (ImapMailboxInfo mailboxInfo in client.GetMailboxes(ImapMailboxListOptions.RequestStatus)) {
   // UidValidity値を検証する
   if (mailboxInfo.UidValidity.ToString().Equals(uidValidity)) {
     // 対象フォルダだった場合は、Openしてメッセージを取得する
     ImapOpenedMailboxInfo openedMailbox = client.OpenMailbox(mailboxInfo.FullName);
     ImapMessageInfo message = openedMailbox.GetMessageByUid(int.Parse(uid));
     //メッセージに関する処理
     break;
   }
 }
:ID:UALUQXu4

>>206

いつも迅速で的確なアドバイスをいただき、
ありがとうございます。

ご教示いただいた方法で、処理時間が半減しました。

また、UidValidity値について、私の認識が誤っていたことも
ご指摘いただき、ありがとうございました。

取り急ぎ、以上です。

:ID:hHO83Hsp

いつもお世話になっております。

今回は、Content-Dispositionヘッダに関して、
各メールソフトでの実装状況についての質問です。

添付ファイル名の判定にあたっては、
Content-Disposition: attachment; の
filenameパラメータを取得するのが基本だと考えています。

ただし、メールソフトによっては、filenameパラメータがなく、
他のパラメータを参考にする必要になる場合があります。

Content-Disposition: attachment; のパラメータには、
filenameパラメータの他に、下記のものがあるようです。

creation-date [RFC2183]
modification-date [RFC2183]
read-date [RFC2183]
size [RFC2183]
name [RFC7578]
voice [RFC2421]
handling [RFC3204]
preview-type [RFC7763]

調べた範囲では、以下のようでした。

【Mozilla Thunderbird】
filenameパラメータのみ

【Microsoft Outlook】
filenameパラメータ
cteation-dateパラメータ
modification-dateパラメータ
sizeパラメータ

他のメールソフトについて、
Content-Disposition: attachment; の実装状況を
ご教示いただければ幸いです。

あくまでも、現在ご存じの情報だけで構いません。

勝手なお願いで恐縮ですが、よろしくお願いします。

:ID:5U/9Z0Js

>>208
ご質問の「Content-Disposition: attachment; の実装状況」とは「filenameパラメータがない場合において、
添付ファイル名の代替として使用されるContent-Dispositionのヘッダパラメータにはどのようなものがあるか」という
趣旨かと推察いたしますが、各メールソフトの添付ファイル名の扱いがどうなっているか、
特にContent-Dispositionパラメータの利用例となると情報を持っていませんので、残念ながら
ご期待に添える回答ができません。

直接の回答にはなりませんが、ライブラリの機能で添付ファイル名を代替的に決定する場合、という観点で
述べると >>156 にてひとつ例示していますので、改めてご覧いただければと思います。

(この例ではサブジェクトをファイル名に使用していますが、ユニークであることの保証が必要なのであれば
Message-IDヘッダやContent-Typeヘッダのboundaryパラメータなどを組み合わせる、あるいは組み合わせた
値からハッシュ値を求めてそれを使う、などがよいかと思います。 ユーザーフレンドリな形式ではありませんが。)

以上、趣意と異なる回答となっていましたらすみません。

:ID:H1apoqUs

>>209

さっそくのご回答ありがとうございます。

各メールソフトの添付ファイル名の扱いについて、
情報をお持ちでないこと、了解しました。

また、根本的な改修の際には、ぜひとも、
>>156 のサンプルを参考にさせていただく所存です。

取り急ぎ以上簡単ですが、今後ともよろしくお願いします。

:ID:AQo2xKr3

こんにちは。
Gmailから、特定の条件に合致するメールを抽出するためのソフトを作りたく、ライブラリを利用させていただいています。
メールやC#の知識は乏しく、ドキュメントを見よう見まねでなんとか動かしている状況です。
本ライブラリを利用してやっていることは、メールのto,from,Body,subject,dateあたりをテキストでローカルDBに保存しておく、というだけの単純なものです。

GetMessagesGmailSearchで取得した
ImapMessageInfo message に対して
message.ReadAs<Mail>(Mail.Load)
を実行すると、一部メールで
'cp932' is an unsupported or invalid charset
という例外が発生します。
どうやらiPhoneから送られたメールの一部がこのような文字コードで送ってくるらしく、Web検索してみるとほかのメーラでも文字化けする現象がよくあるようです。

これは単純にcp932に対応していないため無理だということでしょうか?
それとも何か対応策があるのでしょうか?
>>52に「cp932」というキーワードがあるので関連があるかと思ったのですが、内容が高度で理解できませんでした…

内容的には

var m = inbox.GetMessagesGmailSearch(criteria).ToList();
var count = m.Count;
foreach (var message in m)
  {
    var mail = message.ReadAs<Mail>(Mail.Load);
    if (mail.MimeType.TypeEquals("text"))
    ~~~~~~

というようなコードです。
Smdn.Formats.Mime.dll 0.39.0.0
Smdn.Net.Imap4.Client.dll 1.14.0.0
Smdn.Core.Standards.dll 1.1.0.0
すべて.NET4.5版
を使用しています。
よろしくお願いいたします。

:ID:+LyRQS9z

>>211
ライブラリをご利用いただきありがとうございます。
事象としてはまさに >>52 の方と同様で、対処法は >>23 でも紹介していますが、改めて以下に記載します。

本ライブラリでサポートする文字コードは.NET Frameworkと同じで、.NET FrameworkのEncodingクラスが
サポートしていない場合は今回のように例外が発生します。

本ライブラリでは、このような場合でも代用する文字コード(Encoding)を選択できるようにしています。(>>23)

具体的なコードとしては、まず次のようなメソッドを用意します。

// 代用するEncodingを選択するメソッド
static Encoding SelectFallbackCharset(string encodingName)
{
  if (string.Equals(encodingName, "cp932", StringComparison.OrdinalIgnoreCase)))
  {
    // "cp932"の場合は、CP932の代替としてShift_JISを用いる
    return Encoding.GetEncoding("Shift_JIS");
  }
  else
  {
    // それ以外の非対応文字コードの場合は、nullを返す
    // (例外EncodingNotSupportedException: 'XXX' is an unsupported or invalid charsetをスローする)
    return null;
  }
}

次に、読み込み時にこのメソッドを使うようにMail.Loadの引数で指定します。
ご提示頂いたコードの場合では、ReadAsメソッドの代わりにOpenReadメソッドを使うように変更する必要があります。

var m = inbox.GetMessagesGmailSearch(criteria).ToList();
var count = m.Count;
foreach (var message in m)
{
  Mail mail = null;

  using (var stream = message.OpenRead()) {
    mail = Mail.Load(stream, SelectFallbackCharset);
  }

  if (mail.MimeType.TypeEquals("text"))
    ~~~~~~

このようにすることで、CP932の場合もShift_JISとしてデコードすることができるようになります。

ただこの方法では、CP932をShift_JISとしてデコードすることになるため、特殊文字等で文字化けが
発生する可能性があります。 その点はご留意ください。

もしほかにご不明な点がありましたらお気軽にお問い合わせください。

:ID:AQo2xKr3

>>212
ありがとうございます!
サンプルどころかコピペで済むレベルでコードまで書いていただいて……解説も非常にわかりやすかったです。
頂いたコードで問題なくデコードできることを確認しました。
即日お返事いただきありがとうございました。

:ID:v+9G1zYP

VB版DLLを使用してGmailからImapのメール取得を行っておりますが、
本文が文字化けてしまい上手く取得が出来ません。
以下の書き方に問題はありますでしょうか。

~Clientの定義~
For Each message As ImapMessageInfo In inbox.GetMessages(ImapSearchCriteria.Unseen)
  Dim part As ImapMessagePartInfo
    part = message.MessagePart

  body = part.ReadAllText(ImapMessageFetchBodyOptions.OmitHeader) ←ここで取得した本文が文字化けている
~本文取得後の処理~
:ID:+LyRQS9z

>>214
ライブラリをご利用いただきありがとうございます。

ご提示頂いたコードの場合、ImapMessagePartInfo.ReadAllTextは本文をデコードされていない(生の)状態で返します。

デコードされた状態の本文が必要な場合は、ImapMessageFetchBodyOptions.OmitHeader(ヘッダの省略)と同時に
ImapMessageFetchBodyOptions.DecodeContent(内容のデコード)を組み合わせて指定してください。

なお、デコード動作の詳細についてはドキュメント下記セクションの注釈をご覧ください。
http://smdn.jp/works/libs/Smdn.Net.Imap4.Client/docs/#operation_reference_messagepart_readbody

:ID:v+9G1zYP

>>215
早急な返信ありがとうございます!

デコードされた状態の本文が必要な場合は、ImapMessageFetchBodyOptions.OmitHeader(ヘッダの省略)と同時にImapMessageFetchBodyOptions.DecodeContent(内容のデコード)を組み合わせて指定してください。

とありますが、

ReadAllText(ImapMessageFetchBodyOptions.OmitHeader)

この部分で複数のオプションを組み合わせる方法があるのでしょうか?
複数のオプションを組み合わせる方法が見つけられず
度々の質問となってしまい申し訳ありませんが、ご教示頂けると幸いでございます。

:ID:v+9G1zYP

>>216

上記質問をさせて頂きましたが、
オプションを足し算して、それをImapMessageFetchBodyOptions型にCastすることにより、
複数のオプションが指定できました。

ありがとうございました。

  • >>1と入力すると1番へのアンカーになります。
  • 投稿内容はPukiWiki記法で整形されます。
    • 以下のPukiWiki記法が使えます。
      • 引用文
      • 番号付きリスト、番号なしリスト、定義リスト
      • 整形済みテキスト
        • 複数行のコードブロック・コマンド出力を書き込むには#code{{〜}}、#prompt{{〜}}と記入してください。
        • AAを書き込むには#aa{{〜}}と記入してください。
      • 表組み
      • 見出し
      • 強調・斜体、取り消し線・下線
      • 文字色(&color)、文字サイズ(&size)
      • 注釈
    • URL・メールアドレスは自動的にリンクになります。
    • 詳しくはPukiWikiのFormattingRulesを参照してください。
  • 書き込み後に投稿内容を編集することは出来ません。