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)