PerlからGmailを送信する方法については前に調べたけど、今回はGmailを受信する方法を調べてみた。
Net::IMAP::Clientでメールの受信、Email::MIMEで本文のParseをする。
- Net::IMAP::Client - Not so simple IMAP client library - metacpan.org
- Email::MIME - easy MIME message handling - metacpan.org
まずは動くコードを。GmailのINBOXから最新10件のDate、From、Subject、本文を出力します。
use strict; use warnings; use utf8; use Encode; use IO::Socket::SSL; use Net::IMAP::Client; use Email::MIME; use HTML::FormatText; binmode STDOUT, ":utf8"; binmode STDERR, ":utf8"; my $imap = Net::IMAP::Client->new( server => 'imap.gmail.com', user => $USER_ID, pass => $PASSWORD, ssl => 1, port => 993, ) or die "Could not connect to IMAP server" unless $imap; $imap->login or die('Login failed: ' . $imap->last_error); $imap->select('INBOX'); my $messages = $imap->search('ALL'); for(my $i=0; $i<10; $i++){ my $idx = scalar @$messages - $i - 1; my $data = $imap->get_rfc822_body($$messages[$idx]); my $parsed = Email::MIME->new($data); printf("%s %s %s\n", $parsed->header('Date'), $parsed->header('From'), $parsed->header('Subject')); my $body = ''; $parsed->walk_parts(sub { my $part = shift; return if $part->subparts; if($part->content_type =~ m{text/html}i){ $body = HTML::FormatText->format_string($part->body_str); } elsif($part->content_type =~ m{text/plain}i){ $body = $part->body_str; } }); # CRLF -> LF $body =~ s/\r\n/\n/g; printf("%s\n", $body); } $imap->logout;
短いコードだけど意外とハマったのでまとめておく。
GmailのSEARCH
「最新n件」のメールを表示させたい。
PODを見ると、Net::IMAP::Client->search の第2引数に 'REVERSE DATE' や '^DATE' のようなソート条件を指定できるらしい。
が、なにか指定するとreturnが空になってしまう。
capabilityを調べてみるとSORTが無いようなので、それが原因なのかなあ。
### $capab: [ ### 'IMAP4rev1', ### 'UNSELECT', ### 'IDLE', ### 'NAMESPACE', ### 'QUOTA', ### 'ID', ### 'XLIST', ### 'CHILDREN', ### 'X-GM-EXT-1', ### 'UIDPLUS', ### 'COMPRESS=DEFLATE', ### 'ENABLE', ### 'MOVE', ### 'CONDSTORE', ### 'ESEARCH', ### 'UTF8=ACCEPT', ### 'LIST-EXTENDED', ### 'LIST-STATUS', ### 'LITERAL-', ### 'SPECIAL-USE', ### 'APPENDLIMIT=35651584' ### ]
第1引数も'NEW'とか'RECENT'とか指定してみると空のリストが帰ってくる。よくわからん。
とりあえず'ALL'で全件とって、後ろからn件見ていくという実装にした。
IMAPは難しいな。。時間があるときにちゃんと調べてみよう。
Headerのdecode
ちょっと調べてみると手動でdecodeしている強者もいるみたいですが、Email::MIMEに任せるのが楽。
Bodyのdecode
これがちょっとややこしい。bodyなんちゃらは若干バリエーションがあって
- body_raw : そのまま(base64エンコード、7bit ASCIIなど)のbody
- body : base64 decodeされたbody
- body_str : base64 decodeされたbodyをさらにcharsetでUnicode文字列にdecodeされたもの
基本的には body_str メソッドを使えばよい。
マルチパートの処理
普通のテキストメールならよいのだけど、HTMLメールにも対応する場合はマルチパートの処理が必要になる。
ということで walk_parts を使う。だいたいサンプルのパクリですが。。
普通に?partsメソッドをforループで回しても良さそうなんだけど、なんか不穏な解説があるので止めておく。
This is a stupid method. Don't use it.
Email::MIME - easy MIME message handling - metacpan.org
まぁ再帰的な構造だからコールバック関数で処理するというのが自然ではあるが、stupid methodとまで言わなくてもねぇ・・・。
CRLF -> LF
メールの改行はCRLFなのでLFに統一する。