26 April 2009

(PHP) Pear Net_IMAPの日本語対応化

PearのNet_IMAPパッケージで日本語を含んだメールボックスが扱えないため、原因を探したところ、なんとNet_IMAPパッケージが英語(7ビットコード)前提で作られていることが判明…

検証用のサンプルスクリプトを作成して、メールボックス一覧の読み出し関数(getMailboxes)と、メールボックスの選択関数(selectMailbox)で問題が起こっていることがわかった。

検証用のサンプルスクリプト

test_imap.php

<?php

// 言語と文字コードの設定
mb_language('Japanese');
mb_internal_encoding('UTF-8');
mb_http_output('UTF-8');

// Net_IMAPパッケージのインクルード
require_once('Net/IMAP.php');
require_once('Net/IMAPProtocol.php'); // 日本語対応パッチ済み

$imap = new Net_IMAP('ssl://imap.gmail.com', 993);
$imap->setDebug(); // デバッグ表示
$imap->login('ユーザ名', 'パスワード');
$list = $imap->getMailboxes();
if (is_array($list)) {
foreach ($list as $key => $val) {
$val = mb_convert_encoding($val, 'UTF-8', 'auto');
printf("%02d : %s\n", $key, $val);
}
}
$imap->disconnect();

?>

コンソール上での実行結果。デバッグ出力の生データと、PHPでの出力結果を比べると(たとえば赤で着色した部分を比較すると)、明らかにPHPでの出力は情報が欠落している。

[user@~] php test_imap.php C: A0003 CAPABILITY S: * CAPABILITY IMAP4rev1 UNSELECT IDLE NAMESPACE QUOTA XLIST CHILDREN XYZZY S: A0003 OK Thats all she wrote! f21if12382354rvb.5 C: A0004 LOGIN "ユーザ名" "パスワード" S: A0004 OK ユーザ名@gmail.com authenticated (Success) C: A0005 SELECT "INBOX" S: * FLAGS (\Answered \Flagged \Draft \Deleted \Seen) S: * OK [PERMANENTFLAGS (\Answered \Flagged \Draft \Deleted \Seen \*)] S: * OK [UIDVALIDITY 614523676] S: * 1 EXISTS S: * 0 RECENT S: * OK [UNSEEN 1] S: * OK [UIDNEXT 93] S: A0005 OK [READ-WRITE] INBOX selected. (Success) C: A0006 LIST "" "*" S: * LIST (\HasNoChildren) "/" "Google&e6F0BmDFWDE-" S: * LIST (\HasNoChildren) "/" "INBOX" S: * LIST (\HasNoChildren) "/" "INBOX.Drafts" S: * LIST (\HasNoChildren) "/" "INBOX.Sent" S: * LIST (\HasNoChildren) "/" "INBOX.Trash" S: * LIST (\HasNoChildren) "/" "Linux&YMVYMQ-" S: * LIST (\HasNoChildren) "/" "ML_JMM&Z1FOCp+N-" S: * LIST (\HasNoChildren) "/" "ML_&MN4wpDCvMO0wvTDVMMg-" S: * LIST (\HasNoChildren) "/" "Sent" S: * LIST (\HasNoChildren) "/" "Trash" S: * LIST (\HasNoChildren) "/" "Windows&YMVYMQ-" S: A0006 OK Success 00 : Google???? 01 : INBOX 02 : INBOX.Drafts 03 : INBOX.Sent 04 : INBOX.Trash 05 : Linux?? 06 : ML_JMM??? 07 : ML_??????? 08 : Sent 09 : Trash 10 : Windows??

IMAP.php のソースコードを目で追っていったところ、ベースクラスであるIMAPProtocol.phpのUTF-7の変換部分で、ISO-8859-1をスクリプト内での想定文字コードとしていることが判明 orz

ということで、この部分を UTF-8 を標準想定することに修正してやれば問題解決である。

IMAPProtocol.php の修正箇所

pear/php/Net/IMAPProtocol.php の3500行目あたり
function utf7Encode($str) { if ($this->_useUTF_7 == false) { return $str; } if (function_exists('mb_convert_encoding')) { // return mb_convert_encoding($str, 'UTF7-IMAP', 'ISO-8859-1'); return mb_convert_encoding($str, 'UTF7-IMAP', 'auto'); } /* ~ 省略 ~*/ } function utf7Decode($str) { if ($this->_useUTF_7 == false) { return $str; } //return imap_utf7_decode($str); if (function_exists('mb_convert_encoding')) { // return mb_convert_encoding($str, 'ISO-8859-1', 'UTF7-IMAP'); return mb_convert_encoding($str, 'UTF-8', 'UTF7-IMAP'); } /* ~ 省略 ~*/ }

最初から自国語だけ考えて、プログラム作らないでくれよ…

(日本人なら、まさか日本語前提でプログラムを作成したり、コメントに日本語を使ったりはしないだろうけど、やはり英語圏の人は2バイト以上の文字コードを意識することなんて通常無いんだろうね。 それよりも、日本人が国際化して、標準言語を英語にすればよいだけなんだけどね w)