ThunderbirdでSome messages could not be FETCHed (Failure)というエラーが出る

しばらくの間、Thunderbird(というか、Shredder)でGmail IMAPを使っていると、

Some messages could not be FETCHed (Failure)

というエラーが出るようになっていたんですが、いい加減、うざったくなってきたので、解決方法を探ってみました。
とりあえず、ググってみたんですが、解決方法がまとまっているところは無いようなので、まとめておきます。

Thunderbirdのプロトコルログを取る

最初に、そもそも、Thunderbirdがどのメッセージでつまずいているのかを知る必要があります。これには、Thunderbirdにプロトコルログを吐いてもらう必要があります。まとまっているところとしては、

MailNews:Logging

があります。要は環境変数を設定してから起動しろと。バッチファイルを書いてねとありますが、コマンドプロンプトから直打ちでもかまいません。適当なディレクトリで、

C:\work> set NSPR_LOG_MODULES=imap:5
C:\work> set NSPR_LOG_FILE=C:\work\imap.log
c:\work> "C:\Program Files\Mozilla Thunderbird\thunderbird.exe"

のようにやればいいです。Thunderbirdのパスは場合によっては修正が必要です。

ログがでかくなりすぎるのを防ぐために、エラーが出たら、さっさとThunderbirdは閉じてしまいましょう。

プロトコルログを調べる

さて、今作った、imap.logというプロトコルログですが、あっという間に数十MBとかになりまので、メモ帳だと微妙に荷が重いかもしれませんが、このファイルから、

Some messages could not be FETCHed (Failure)

を含む部分を検索します。エラーが一回しか出てなければ、引っかかる部分も一カ所しかないはずです。

僕の場合だと、

....<省略>....

4492[e70e440]: e5cf000:imap.gmail.com:S-INBOX:SendData: 10 UID fetch 67394 (UID RFC822.SIZE FLAGS BODY.PEEK[HEADER.FIELDS (From To Cc Bcc Subject Date Message-ID Priority X-Priority References Newsgroups In-Reply-To Content-Type)])

4492[e70e440]: ReadNextLine [stream=ea584c8 nb=52 needmore=0]
4492[e70e440]: e5cf000:imap.gmail.com:S-INBOX:CreateNewLineFromSocket: 10 NO Some messages could not be FETCHed (Failure)

....<省略>....

となっていました。IMAPは複数接続のプロトコルなので、エラーの前の行がエラーの原因とは限らないのですが、おおよそ、前の行がエラーの原因であると考えて良いでしょう。この部分のの前の行を見ると、

10 UID fetch 67394 (UID RFC822.SIZE FLAGS BODY.PEEK[HEADER.FIELDS (From To Cc Bcc Subject Date Message-ID Priority X-Priority References Newsgroups In-Reply-To Content-Type)])

というコマンドを発行しているのが分かります。これは、UID=67394のメールをくれというコマンドなので、結局、この、「UID=67394のメール」が悪さをしているらしいことが分かります。
ここでの目的は、とりあえず、この悪さをしているメールを削除することになります。

Python imaplib

さて、IMAPを駆使してメールを削除したいのですが、OpenSSLでセッションを張ってIMAPプロトコルを自由自在に操れる人なんて普通は居ませんから、なんかのライブラリとかプログラムを使わざるを得ないですが、ここでは、Pythonのimaplibを使うことにします。

Pythonは、

Download Python [www.python.org]

からダウンロードできますが、3.X系だと、imaplibがちゃんと動かないっぽいので、2.6をダウンロードして、インストールしてください。
そうすると、スタートメニューに「IDLE (Python GUI)」というのがインストールされるので、それを起動します。

f:id:espresso3389:20090830192335p:image

このウィンドウに対して、次の行を入力していきます。

import imaplib
s = imaplib.IMAP4_SSL('imap.gmail.com')
s.login('アカウント名@gmail.com', 'パスワード')

ここで、ちゃんとログインできれば、

('OK', ['アカウント名@gmail.com authenticated (Success)'])

のように表示されているはずです。

f:id:espresso3389:20090830192334p:image

さらに、INBOXに移動するために、次のように入力します。

s.select()

うまくいけば、次のように表示されます。OKの次にあるのはメールボックス内のメール総数です。

('OK', ['67469'])

メールの番号を調べる

さて、UID=67394のメールを消したいのですが、最初にこのメールが現在のメールボックス内で何番目のメールなのかを特定する必要があります。それには、次のように入力します。

s.search(None, 'UID', '67394')

うまく行けば、

('OK', ['55569 (UID 67394)'])

のように表示されます。これで、このメールのメールボックス内での番号が55569であることが特定できました。

じゃあ、ひと思いにこのメールを消してしまいたいところですが、このメールが何者なのか知ることが出来るなら知りたいものです。それには、

s.fetch('55569', '(BODY.PEEK[HEADER.FIELDS (From To Date Subject)])')

のようにやりますが、まぁ、おそらく、うまくいかないでしょう。Thunderbirdが同じようなコマンドを処理してうまくいかないわけですから。
ただ、このメールの前後のメール、ここでは、55568, 55570に関しては、情報が得られるでしょうから、それらについて、Dateなどを確認することによって、いつのメールが壊れたのかぐらいはうかがい知ることは出来ます。メールを消してしまうわけですから、覚悟のためにも、前後のメールぐらいは確認しておくべきでしょう。

さて、いよいよ、メールを削除しましょう。次のコマンドを入力してください。

s.store('55569', '+FLAGS', '\\Deleted')

うまくいけば、

('OK', ['55569 (FLAGS (\\Seen \\Deleted))'])

のように表示されます。このコマンドは、メールに\\Deletedというフラグを付与するだけです。実際に削除するには、この後に、

s.expunge()

というコマンドを実行します。うまくいけば、

('OK', ['55569'])

と表示されます。これで作業完了です。このままこのウィンドウを閉じてしまってもかまいませんが、我々のために働いてくれるGoogle様に失礼なので、

s.logout()

とやりましょう。