Email::MIMEでのパースで本文が文字化けする場合

Email::MIMEでメールをパースした情報は各メソッドでデコードされた状態で値を取得できるのですが、メール本文がうまくデコードされない場合もあります。

メール処理をするCGIのプロトタイプを作成してる際にこのケースに遭遇しました。その時はメールのパース以降の処理で試してみたいことがあったので、ちゃんとした原因究明はしてないのですが、そういうレベルで問題ないのなら

  1. Email::MIMEのbody_rawメソッドで本文を取得
  2. Encode::Guessでメール本文の文字コードを解析
  3. Encodeを利用してデコード

という手順で応急処置が可能です。なお、Email::MIMEのbody_rawは対象となるメール本文をデコードしない状態で取得するメソッドです。サンプルを用意してみるとこんな感じになります。

use strict;
use warnings;
use utf8;
use Encode        qw(decode encode);
use Encode::Guess qw(euc-jp shiftjis 7bit-jis);
use Net::POP3;
use Email::MIME;

my $server   = 'mail.example.com';
my $account  = 'hoge';
my $password = 'bar';

my $pop3     = Net::POP3->new($server) or die 'can not open account.';
my $count    = $pop3->login($account, $password);
my $messages = $pop3->list;

my $layout = << 'TMPL';
[TITLE]
%s

[BODY]
%s

TMPL

for my $id ( sort { $a <=> $b } keys %{ $messages } ) {
    my $mail   = join q(), @{ $pop3->get($id) };
    my $parsed = Email::MIME->new($mail);

    print sprintf $layout,
                  encode( 'utf8', $parsed->header('Subject')         ),
                  encode( 'utf8', decode('Guess', $parsed->body_raw) )
    ;
}

$pop3->quit;
1;

Perl 5.8.x Unicode関連の情報によるとEnocdeの作者である小飼弾さん自身がEncode::Guessを利用する際の心得として、

  1. 出来れば使わない
  2. 使う場合は、データ全部をいっぺんに渡す
  3. Guessしなかった場合のエラー処理をきちんとする

とされてるそうなので、特に注意してください。