それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

telnet(openssl)でIMAPプロトコルを喋ってGmailに命令を出す。

IMAP のIDLEコマンドの通知タイミングについて調べるため、とりあえず生のIMAP4コマンドを使う必要があった。

imap コマンドを使ってGmailを読み出す。

gmailIMAPが有効になってればす、ターミナルから、IMAPプロトコルを使って対話できる。

imap.gmail.comに接続
openssl s_client -connect imap.gmail.com:993 -crlf 
ログイン
? login takuya@exmaple.com **PASSWORD**

 「?の意味」ここでは ? を使っているが、コマンド識別のための任意の文字列で何でも可能。ただし。*/+などはIMAPで意味を持つ記号なのでダメ

メールボックス一覧
?? LIST "" "*"
メールボックス選択
??? select inbox

 imap ではinboxは確実に存在する。

メールボックス内部のメール一覧
???? search all
メールボックスのメールの詳細表示
??? fetch 1 full

 「1」 は 「search all」 した結果の番号から選ぶ。IMAPではメールのMIME形式のネスト構造を重視している。

メールの本文取得
??? fetch 1 rfc822.text

 mimeのファイルそのままを取得する。「1」 は 「search all」 した結果の番号から選ぶ。

ログアウト
?? logout

別解

IMAPは同じ目的を別のコマンドで実現することも出来る

メールの一覧(search all 代替)
? fetch 1:* uid

fetch コマンドですべてのメールを対象にすることで、search all と同じ振る舞いをさせられる。

実際にGmailIMAPサーバーに接続して通信した例

以上を踏まえて、GmailIMAPで通信した例が以下になります。

赤の太字が実際にターミナルから入力したコマンド

takuya@air:~/Desktop$ openssl s_client -connect imap.gmail.com:993 -crlf 
(中略)
    0060 - b7 09 37 38 30 3b 70 c0-27 60 c7 b3 e1 2b 63 97   ..780;p.'`...+c.
    0070 - 56 03 10 3c 5b a5 b8 48-b9 84 23 51 0e 24 1d 6a   V..<[..H..#Q.$.j
    0080 - 57 10 3b 2f 35 c0 f2 ff-38 69 4f e4 55 8a dd 10   W.;/5...8iO.U...
    0090 - a9 e1 73 78                                       ..sx

    Start Time: 1352575032
    Timeout   : 300 (sec)
    Verify return code: 20 (unable to get local issuer certificate)
    • -
* OK Gimap ready for requests from 182.167.127.229 vr10if12891101pbc.102 ? login takuya@example.com **my_password** * CAPABILITY IMAP4rev1 UNSELECT IDLE NAMESPACE QUOTA ID XLIST CHILDREN X-GM-EXT-1 UIDPLUS COMPRESS=DEFLATE ? OK takuya@example.com takuya authenticated (Success) ? list "" "*" * LIST (\HasNoChildren) "/" "AmazonGift" * LIST (\HasNoChildren) "/" "AmazonGift_Archive" * LIST (\HasNoChildren) "/" "INBOX" * LIST (\HasNoChildren) "/" "Notes" * LIST (\Noselect \HasChildren) "/" "[Gmail]" * LIST (\HasChildren \HasNoChildren \All) "/" "[Gmail]/All Mail" * LIST (\HasNoChildren \Drafts) "/" "[Gmail]/Drafts" * LIST (\HasNoChildren \Important) "/" "[Gmail]/Important" * LIST (\HasNoChildren \Sent) "/" "[Gmail]/Sent Mail" * LIST (\HasNoChildren \Flagged) "/" "[Gmail]/Starred" * LIST (\HasChildren \HasNoChildren \Trash) "/" "[Gmail]/Trash" * LIST (\HasNoChildren) "/" "auction" * LIST (\HasNoChildren) "/" "kof2012" ? OK Success ? select inbox * FLAGS (\Answered \Flagged \Draft \Deleted \Seen [Gmail]/Starred) * OK [PERMANENTFLAGS (\Answered \Flagged \Draft \Deleted \Seen [Gmail]/Starred \*)] Flags permitted. * OK [UIDVALIDITY 2] UIDs valid. * 65 EXISTS * 0 RECENT * OK [UIDNEXT 27459] Predicted next UID. ? OK [READ-WRITE] inbox selected. (Success) ? fetch 1:2 uid * 1 FETCH (UID 22561) * 2 FETCH (UID 22688) ? OK Success ? fetch 1 bodystructure * 1 FETCH (BODYSTRUCTURE ("TEXT" "PLAIN" ("CHARSET" "ISO-2022-JP") NIL NIL "7BIT" 4739 80 NIL NIL NIL)) ? OK Success

imapではメールはフラグ単位で管理される。

imap はフラグ単位でメールを管理します。これが特徴で重要。なぜこんなフラグがあるかというと、IMAPは非同期マルチスレッドを意識したプロトコルだからだそうです。つまり複数クライアントから同時に複数人がアクセスすることを想定するからだそうです。iphoneでメールを読むと既読状態がipad にも連携される。そういうことですね

主なフラグ
  • 未読(UNSEEN)
  • 返信済み(ANSWERED)
  • 削除(DELETED)

が挙げられます。他にも印をつけたマークなどもあるようです。

フラグの取得

1 fetch 1:* flags
 * 1 FETCH (FLAGS (\Seen))
 * 2 FETCH (FLAGS (\Seen))
 * 3 FETCH (FLAGS (\Seen))
 * 4 FETCH (FLAGS (\Seen))
 * 5 FETCH (FLAGS (\Seen))
 * 6 FETCH (FLAGS (\Seen))
 * 7 FETCH (FLAGS (\Seen))
 * 8 FETCH (FLAGS (\Seen))
 * 9 FETCH (FLAGS (\Seen))
 * 10 FETCH (FLAGS (\Seen))
 * 11 FETCH (FLAGS (\Seen))
 * 12 FETCH (FLAGS (\Seen))
 (中略
 * 54 FETCH (FLAGS (\Answered \Seen))
 * 61 FETCH (FLAGS (\Seen))
 * 62 FETCH (FLAGS (\Seen))
 * 63 FETCH (FLAGS (\Seen))
 * 64 FETCH (FLAGS ())
 * 65 FETCH (FLAGS ())

1 OK Success

最後の2件に注目

 * 65 FETCH (FLAGS ())

\seen (既読)フラグが付いていません。つまり未読です。

メールの件数とINBOXの表示について

GMAILのINBOXの件数と、"search all "の結果件数が合わないことがあります。それはスレッドにまとまっているからですね。スレッドを取得するにはthread コマンドを使うようなのですがGmailは未サポートでした。2012/11/11現在のところ、メールをスレッドで取得はどうやるかわかりませんでした。

IDLEについて

001 IDLE
001+ idling
 * 64 EXISTS
 * 65 EXISTS
 * 66 EXISTS

IDLEを使うと、サーバーがメールボックスの変化を通知してくれます。
通知されるタイミングは、新着メールが来てから約2秒後ですね。体感でプッシュ通知よりも遅い感じでした。さらに体感ではXMPPgoogle talk)よりもIDLEによる通知が後からくる感じでした。

メールが来る度に、通知が一つ増えます。変化があるたびに通知されます。

メールボックスの削除・作成

メールクライアントソフト(MUA)でも作ってない限りお世話になることはまず無いので、割愛。
同じ理由でsubscribeも使うことはないと思います。Gmailの管理画面から似たようなことできるしね。

メールの既読化

メールに既読フラグを立てるときは、storeをつかう。

?? store 65 +flags \seen
 * 65 FETCH (FLAGS (\Seen))

未読化

メールを未読にするときは、既読フラグを消す。

?? store 65 -flags \seen
 * 65 FETCH (FLAGS ())

フラグ操作

フラグ 意味    
FLAGS 指定フラグに置換える
+FLAGS 指定フラグを追加する
-FLAGS 指定フラグを削除する

フラグは、複数フラグをスペース区切りで並べてフラグリストにしてもいい

Gmailフォルダ(ラベル)を付ける

INBOXからAmazonGiftと書かれたラベルを付ける。ラベル付与はコピーをする。コピーをするとINBOXとAmazonGiftの両方のラベルがつく。

???? select inbox
(中略
?? copy 65 AmazonGift
 * 66 EXISTS
?? OK [COPYUID 57 27459 3677] (Success)

上記実行後にGmailにWEBにログインしてみると、INBOXとAmazonGiftの両方のラベルが付いてます。

メッセージの移動の場合。

INBOXのメッセージを別のラベルに移動するときは、コピー後にINBOXから消す(アーカイブする)
それには、コピー後に\deletedフラグ付与とExpungeを呼び出す。

?? copy 65 AmazonGift
 * 66 EXISTS
?? OK [COPYUID 57 27459 3677] (Success)
 ?? store 65 +flags \deleted
 * 65 FETCH (FLAGS (\Seen \Deleted))
 ?? OK Success
 ?? expunge
 * 65 EXPUNGE
 * 65 EXISTS
 ?? OK Success

とする。これで合ってるんだけど、削除関係については自信がない。moveはなくて、Copy+deletedフラグのはずなんだけどRFC調べるの面倒でちゃんと読んでないです。とくにArchive(All Mail)とGmail側の削除とdeletedフラグの関係が謎すぎてよくわからないんですよ

以上

これだけサンプルがあれば、手作業でIMAPプロトコルを叩いて、ある程度イメージが掴めると思います。いまさらIMAPを直接使うことは無いとは思いますが。imap4ライブラリやパッケージでメソッドの順番がイマイチピンと来ないときなどやっぱりプロトコル直接叩いたほうがあっさり理解でるんではないでしょうか。